1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2025-02-21 15:54:22 +01:00

update angular-ui-calendar and ajax load availabilites

This commit is contained in:
Peng DU 2016-07-13 19:09:07 +02:00
parent 1529e4b14e
commit 0af0cc7878
11 changed files with 235 additions and 171 deletions

View File

@ -4,8 +4,8 @@
# Controller used in the calendar management page # 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' Application.Controllers.controller "AdminCalendarController", ["$scope", "$state", "$uibModal", "moment", "Availability", 'Slot', 'Setting', 'growl', 'dialogs', 'bookingWindowStart', 'bookingWindowEnd', 'machinesPromise', '_t', 'uiCalendarConfig'
($scope, $state, $uibModal, moment, Availability, Slot, Setting, growl, dialogs, availabilitiesPromise, bookingWindowStart, bookingWindowEnd, machinesPromise, _t) -> ($scope, $state, $uibModal, moment, Availability, Slot, Setting, growl, dialogs, bookingWindowStart, bookingWindowEnd, machinesPromise, _t, uiCalendarConfig) ->
@ -36,12 +36,9 @@ Application.Controllers.controller "AdminCalendarController", ["$scope", "$state
## bind the availabilities slots with full-Calendar events ## bind the availabilities slots with full-Calendar events
$scope.eventSources = [] $scope.eventSources = []
$scope.eventSources.push $scope.eventSources.push
events: availabilitiesPromise url: '/api/availabilities'
textColor: 'black' textColor: 'black'
## after fullCalendar loads, provides access to its methods through $scope.calendar.fullCalendar()
$scope.calendar = null
## fullCalendar (v2) configuration ## fullCalendar (v2) configuration
$scope.calendarConfig = $scope.calendarConfig =
timezone: Fablab.timezone timezone: Fablab.timezone
@ -141,7 +138,7 @@ Application.Controllers.controller "AdminCalendarController", ["$scope", "$state
# update the machine_ids attribute # update the machine_ids attribute
$scope.availability.machine_ids = data.machine_ids $scope.availability.machine_ids = data.machine_ids
$scope.availability.title = data.title $scope.availability.title = data.title
$scope.calendar.fullCalendar 'rerenderEvents' uiCalendarConfig.calendars.calendar.fullCalendar 'rerenderEvents'
# notify the admin # notify the admin
growl.success(_t('the_machine_was_successfully_removed_from_the_slot')) growl.success(_t('the_machine_was_successfully_removed_from_the_slot'))
, (data, status) -> # failed , (data, status) -> # failed
@ -180,7 +177,7 @@ Application.Controllers.controller "AdminCalendarController", ["$scope", "$state
end: -> end end: -> end
# when the modal is closed, we send the slot to the server for saving # when the modal is closed, we send the slot to the server for saving
modalInstance.result.then (availability) -> modalInstance.result.then (availability) ->
$scope.calendar.fullCalendar 'renderEvent', uiCalendarConfig.calendars.calendar.fullCalendar 'renderEvent',
id: availability.id id: availability.id
title: availability.title, title: availability.title,
start: availability.start_at start: availability.start_at
@ -189,12 +186,13 @@ Application.Controllers.controller "AdminCalendarController", ["$scope", "$state
backgroundColor: availability.backgroundColor backgroundColor: availability.backgroundColor
borderColor: availability.borderColor borderColor: availability.borderColor
tag_ids: availability.tag_ids tag_ids: availability.tag_ids
tags: availability.tags
machine_ids: availability.machine_ids machine_ids: availability.machine_ids
, true , true
, -> , ->
$scope.calendar.fullCalendar('unselect') uiCalendarConfig.calendars.calendar.fullCalendar('unselect')
$scope.calendar.fullCalendar('unselect') uiCalendarConfig.calendars.calendar.fullCalendar('unselect')
@ -209,7 +207,7 @@ Application.Controllers.controller "AdminCalendarController", ["$scope", "$state
# if the user has clicked on the delete event button, delete the event # if the user has clicked on the delete event button, delete the event
if ($(jsEvent.target).hasClass('remove-event')) if ($(jsEvent.target).hasClass('remove-event'))
Availability.delete id: event.id, -> 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 for _event, i in $scope.eventSources[0].events
if _event.id == event.id if _event.id == event.id
$scope.eventSources[0].events.splice(i,1) $scope.eventSources[0].events.splice(i,1)
@ -231,12 +229,12 @@ Application.Controllers.controller "AdminCalendarController", ["$scope", "$state
# @see http://fullcalendar.io/docs/event_rendering/eventRender/ # @see http://fullcalendar.io/docs/event_rendering/eventRender/
## ##
eventRenderCb = (event, element) -> eventRenderCb = (event, element) ->
if event.tag_ids.length > 0 if event.tags.length > 0
Availability.get {id: event.id}, (avail) -> html = ''
html = '' for tag in event.tags
for tag in avail.tags html += "<span class='label label-success text-white'>#{tag.name}</span> "
html += "<span class='label label-success text-white'>#{tag.name}</span> " element.find('.fc-title').append("<br/>"+html)
element.find('.fc-title').append("<br/>"+html) return
] ]

View File

@ -268,8 +268,8 @@ Application.Controllers.controller "ShowMachineController", ['$scope', '$state',
# This controller workflow is pretty similar to the trainings reservation controller. # 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', 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',
($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, uiCalendarConfig) ->
@ -297,9 +297,6 @@ Application.Controllers.controller "ReserveMachineController", ["$scope", "$stat
### PUBLIC SCOPE ### ### 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 ## bind the machine availabilities with full-Calendar events
$scope.eventSources = [] $scope.eventSources = []
@ -412,7 +409,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.title = if $scope.currentUser.role isnt 'admin' then _t('i_ve_reserved') else _t('not_available')
$scope.slotToModify.backgroundColor = 'white' $scope.slotToModify.backgroundColor = 'white'
$scope.slotToModify = null $scope.slotToModify = null
$scope.calendar.fullCalendar 'rerenderEvents' uiCalendarConfig.calendars.calendar.fullCalendar 'rerenderEvents'
@ -425,7 +422,7 @@ Application.Controllers.controller "ReserveMachineController", ["$scope", "$stat
$scope.slotToPlace.backgroundColor = 'white' $scope.slotToPlace.backgroundColor = 'white'
$scope.slotToPlace.title = '' $scope.slotToPlace.title = ''
$scope.slotToPlace = null $scope.slotToPlace = null
$scope.calendar.fullCalendar 'rerenderEvents' uiCalendarConfig.calendars.calendar.fullCalendar 'rerenderEvents'
@ -458,7 +455,7 @@ Application.Controllers.controller "ReserveMachineController", ["$scope", "$stat
$scope.slotToModify.can_modify = false $scope.slotToModify.can_modify = false
$scope.slotToModify = null $scope.slotToModify = null
$scope.calendar.fullCalendar 'rerenderEvents' uiCalendarConfig.calendars.calendar.fullCalendar 'rerenderEvents'
, (err) -> # failure , (err) -> # failure
growl.error(_t('unable_to_change_the_reservation')) growl.error(_t('unable_to_change_the_reservation'))
console.error(err) console.error(err)
@ -476,7 +473,7 @@ Application.Controllers.controller "ReserveMachineController", ["$scope", "$stat
$scope.slotToModify.backgroundColor = 'white' $scope.slotToModify.backgroundColor = 'white'
$scope.slotToModify = null $scope.slotToModify = null
$scope.calendar.fullCalendar 'rerenderEvents' uiCalendarConfig.calendars.calendar.fullCalendar 'rerenderEvents'
@ -532,8 +529,8 @@ Application.Controllers.controller "ReserveMachineController", ["$scope", "$stat
$scope.plansAreShown = false $scope.plansAreShown = false
updateCartPrice() updateCartPrice()
$timeout -> $timeout ->
$scope.calendar.fullCalendar 'refetchEvents' uiCalendarConfig.calendars.calendar.fullCalendar 'refetchEvents'
$scope.calendar.fullCalendar 'rerenderEvents' uiCalendarConfig.calendars.calendar.fullCalendar 'rerenderEvents'
@ -732,7 +729,7 @@ Application.Controllers.controller "ReserveMachineController", ["$scope", "$stat
$scope.slotToModify = event $scope.slotToModify = event
event.backgroundColor = '#eee' event.backgroundColor = '#eee'
event.title = _t('i_change') event.title = _t('i_change')
$scope.calendar.fullCalendar 'rerenderEvents' uiCalendarConfig.calendars.calendar.fullCalendar 'rerenderEvents'
else if type == 'cancel' else if type == 'cancel'
dialogs.confirm dialogs.confirm
resolve: resolve:
@ -750,7 +747,7 @@ Application.Controllers.controller "ReserveMachineController", ["$scope", "$stat
$scope.canceledSlot.is_reserved = false $scope.canceledSlot.is_reserved = false
$scope.canceledSlot.can_modify = false $scope.canceledSlot.can_modify = false
$scope.canceledSlot = null $scope.canceledSlot = null
$scope.calendar.fullCalendar 'rerenderEvents' uiCalendarConfig.calendars.calendar.fullCalendar 'rerenderEvents'
, -> # error while canceling , -> # error while canceling
growl.error _t('cancellation_failed') growl.error _t('cancellation_failed')
, -> , ->
@ -758,7 +755,7 @@ Application.Controllers.controller "ReserveMachineController", ["$scope", "$stat
$scope.selectedPlan = null $scope.selectedPlan = null
$scope.modifiedSlots = null $scope.modifiedSlots = null
$scope.calendar.fullCalendar 'rerenderEvents' uiCalendarConfig.calendars.calendar.fullCalendar 'rerenderEvents'
updateCartPrice() updateCartPrice()
@ -930,8 +927,8 @@ Application.Controllers.controller "ReserveMachineController", ["$scope", "$stat
Auth._currentUser.subscribed_plan = angular.copy($scope.selectedPlan) Auth._currentUser.subscribed_plan = angular.copy($scope.selectedPlan)
$scope.plansAreShown = false $scope.plansAreShown = false
$scope.calendar.fullCalendar 'refetchEvents' uiCalendarConfig.calendars.calendar.fullCalendar 'refetchEvents'
$scope.calendar.fullCalendar 'rerenderEvents' uiCalendarConfig.calendars.calendar.fullCalendar 'rerenderEvents'

View File

@ -51,8 +51,8 @@ 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). # 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' 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'
, ($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, uiCalendarConfig) ->
@ -77,9 +77,6 @@ Application.Controllers.controller "ReserveTrainingController", ["$scope", "$sta
### PUBLIC SCOPE ### ### 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 ## bind the trainings availabilities with full-Calendar events
$scope.eventSources = [ { events: availabilityTrainingsPromise, textColor: 'black' } ] $scope.eventSources = [ { events: availabilityTrainingsPromise, textColor: 'black' } ]
@ -148,7 +145,7 @@ Application.Controllers.controller "ReserveTrainingController", ["$scope", "$sta
eventClick: (event, jsEvent, view) -> eventClick: (event, jsEvent, view) ->
calendarEventClickCb(event, jsEvent, view) calendarEventClickCb(event, jsEvent, view)
eventAfterAllRender: (view)-> eventAfterAllRender: (view)->
$scope.events = $scope.calendar.fullCalendar 'clientEvents' $scope.events = uiCalendarConfig.calendars.calendar.fullCalendar 'clientEvents'
eventRender: (event, element, view) -> eventRender: (event, element, view) ->
eventRenderCb(event, element, view) eventRenderCb(event, element, view)
@ -174,7 +171,7 @@ Application.Controllers.controller "ReserveTrainingController", ["$scope", "$sta
Member.get {id: $scope.ctrl.member.id}, (member) -> Member.get {id: $scope.ctrl.member.id}, (member) ->
$scope.ctrl.member = member $scope.ctrl.member = member
Availability.trainings {trainingId: $stateParams.id, member_id: $scope.ctrl.member.id}, (trainings) -> Availability.trainings {trainingId: $stateParams.id, member_id: $scope.ctrl.member.id}, (trainings) ->
$scope.calendar.fullCalendar 'removeEvents' uiCalendarConfig.calendars.calendar.fullCalendar 'removeEvents'
$scope.eventSources.push $scope.eventSources.push
events: trainings events: trainings
textColor: 'black' textColor: 'black'
@ -210,8 +207,8 @@ Application.Controllers.controller "ReserveTrainingController", ["$scope", "$sta
$scope.selectedPlan = null $scope.selectedPlan = null
$scope.trainingIsValid = false $scope.trainingIsValid = false
$timeout -> $timeout ->
$scope.calendar.fullCalendar 'refetchEvents' uiCalendarConfig.calendars.fullCalendar 'refetchEvents'
$scope.calendar.fullCalendar 'rerenderEvents' uiCalendarConfig.calendars.fullCalendar 'rerenderEvents'
@ -283,7 +280,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.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.backgroundColor = 'white'
$scope.slotToModify = null $scope.slotToModify = null
$scope.calendar.fullCalendar 'rerenderEvents' uiCalendarConfig.calendars.calendar.fullCalendar 'rerenderEvents'
@ -296,7 +293,7 @@ Application.Controllers.controller "ReserveTrainingController", ["$scope", "$sta
$scope.slotToPlace.backgroundColor = 'white' $scope.slotToPlace.backgroundColor = 'white'
$scope.slotToPlace.title = $scope.slotToPlace.training.name $scope.slotToPlace.title = $scope.slotToPlace.training.name
$scope.slotToPlace = null $scope.slotToPlace = null
$scope.calendar.fullCalendar 'rerenderEvents' uiCalendarConfig.calendars.calendar.fullCalendar 'rerenderEvents'
@ -329,7 +326,7 @@ Application.Controllers.controller "ReserveTrainingController", ["$scope", "$sta
$scope.slotToModify.can_modify = false $scope.slotToModify.can_modify = false
$scope.slotToModify.is_completed = false if $scope.slotToModify.is_completed $scope.slotToModify.is_completed = false if $scope.slotToModify.is_completed
$scope.slotToModify = null $scope.slotToModify = null
$scope.calendar.fullCalendar 'rerenderEvents' uiCalendarConfig.calendars.calendar.fullCalendar 'rerenderEvents'
, -> # failure , -> # failure
growl.error('an_error_occured_preventing_the_booked_slot_from_being_modified') growl.error('an_error_occured_preventing_the_booked_slot_from_being_modified')
@ -345,7 +342,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.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.backgroundColor = 'white'
$scope.slotToModify = null $scope.slotToModify = null
$scope.calendar.fullCalendar 'rerenderEvents' uiCalendarConfig.calendars.calendar.fullCalendar 'rerenderEvents'
@ -426,7 +423,7 @@ Application.Controllers.controller "ReserveTrainingController", ["$scope", "$sta
angular.forEach $scope.events, (e)-> angular.forEach $scope.events, (e)->
if event.id != e.id if event.id != e.id
e.backgroundColor = 'white' e.backgroundColor = 'white'
$scope.calendar.fullCalendar 'rerenderEvents' uiCalendarConfig.calendars.calendar.fullCalendar 'rerenderEvents'
# two if below for move training reserved # two if below for move training reserved
# if training isnt reserved and have a training to modify and same training and not complete # 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) else if !event.is_reserved && $scope.slotToModify && slotCanBePlaced(event)
@ -436,7 +433,7 @@ Application.Controllers.controller "ReserveTrainingController", ["$scope", "$sta
$scope.slotToPlace = event $scope.slotToPlace = event
event.backgroundColor = '#bbb' event.backgroundColor = '#bbb'
event.title = event.training.name + ' - ' + _t('i_shift') event.title = event.training.name + ' - ' + _t('i_shift')
$scope.calendar.fullCalendar 'rerenderEvents' uiCalendarConfig.calendars.calendar.fullCalendar 'rerenderEvents'
# if training reserved can modify # if training reserved can modify
else if event.is_reserved and (slotCanBeModified(event) or slotCanBeCanceled(event)) and !$scope.slotToModify and !$scope.selectedTraining else if event.is_reserved and (slotCanBeModified(event) or slotCanBeCanceled(event)) and !$scope.slotToModify and !$scope.selectedTraining
event.movable = slotCanBeModified(event) event.movable = slotCanBeModified(event)
@ -454,7 +451,7 @@ Application.Controllers.controller "ReserveTrainingController", ["$scope", "$sta
$scope.slotToModify = event $scope.slotToModify = event
event.backgroundColor = '#eee' event.backgroundColor = '#eee'
event.title = event.training.name + ' - ' + _t('i_change') event.title = event.training.name + ' - ' + _t('i_change')
$scope.calendar.fullCalendar 'rerenderEvents' uiCalendarConfig.calendars.calendar.fullCalendar 'rerenderEvents'
else if type == 'cancel' else if type == 'cancel'
dialogs.confirm dialogs.confirm
resolve: resolve:
@ -473,7 +470,7 @@ Application.Controllers.controller "ReserveTrainingController", ["$scope", "$sta
$scope.canceledSlot.can_modify = false $scope.canceledSlot.can_modify = false
$scope.canceledSlot.is_completed = false if event.is_completed $scope.canceledSlot.is_completed = false if event.is_completed
$scope.canceledSlot = null $scope.canceledSlot = null
$scope.calendar.fullCalendar 'rerenderEvents' uiCalendarConfig.calendars.calendar.fullCalendar 'rerenderEvents'
, -> # error while canceling , -> # error while canceling
growl.error _t('cancellation_failed') growl.error _t('cancellation_failed')
, -> # canceled , -> # canceled
@ -635,8 +632,8 @@ Application.Controllers.controller "ReserveTrainingController", ["$scope", "$sta
Auth._currentUser.training_credits = angular.copy(reservation.user.training_credits) Auth._currentUser.training_credits = angular.copy(reservation.user.training_credits)
Auth._currentUser.machine_credits = angular.copy(reservation.user.machine_credits) Auth._currentUser.machine_credits = angular.copy(reservation.user.machine_credits)
$scope.calendar.fullCalendar 'refetchEvents' uiCalendarConfig.calendars.calendar.fullCalendar 'refetchEvents'
$scope.calendar.fullCalendar 'rerenderEvents' uiCalendarConfig.calendars.calendar.fullCalendar 'rerenderEvents'

View File

@ -506,9 +506,6 @@ angular.module('application.router', ['ui.router']).
templateUrl: '<%= asset_path "admin/calendar/calendar.html" %>' templateUrl: '<%= asset_path "admin/calendar/calendar.html" %>'
controller: 'AdminCalendarController' controller: 'AdminCalendarController'
resolve: resolve:
availabilitiesPromise: ['Availability', (Availability)->
Availability.query().$promise
]
bookingWindowStart: ['Setting', (Setting)-> bookingWindowStart: ['Setting', (Setting)->
Setting.get(name: 'booking_window_start').$promise Setting.get(name: 'booking_window_start').$promise
] ]

View File

@ -8,7 +8,10 @@ class API::AvailabilitiesController < API::ApiController
def index def index
authorize Availability 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') @availabilities = Availability.includes(:machines,:tags,:trainings).where.not(available_type: 'event')
.where('start_at >= ? AND end_at <= ?', start_date, end_date)
end end
def show def show

View File

@ -9,4 +9,8 @@ json.array!(@availabilities) do |availability|
json.backgroundColor 'white' json.backgroundColor 'white'
json.borderColor availability.available_type == 'machines' ? '#e4cd78' : '#bd7ae9' json.borderColor availability.available_type == 'machines' ? '#e4cd78' : '#bd7ae9'
json.tag_ids availability.tag_ids json.tag_ids availability.tag_ids
json.tags availability.tags do |t|
json.id t.id
json.name t.name
end
end end

View File

@ -18,7 +18,7 @@
"angular-bootstrap": "~0.14.3", "angular-bootstrap": "~0.14.3",
"angular-ui-router": ">=0.2.15", "angular-ui-router": ">=0.2.15",
"fullcalendar": "=2.3.1", "fullcalendar": "=2.3.1",
"angular-ui-calendar": "0.9.0-beta.1", "angular-ui-calendar": "1.0.1",
"moment": "=2.10.6", "moment": "=2.10.6",
"angular-moment": ">=0.10.3", "angular-moment": ">=0.10.3",
"ngUpload": ">=0.5.11", "ngUpload": ">=0.5.11",

View File

@ -1,6 +1,6 @@
{ {
"name": "angular-ui-calendar", "name": "angular-ui-calendar",
"version": "0.9.0-beta.1", "version": "1.0.1",
"description": "A complete AngularJS directive for the Arshaw FullCalendar.", "description": "A complete AngularJS directive for the Arshaw FullCalendar.",
"author": "https://github.com/angular-ui/ui-calendar/graphs/contributors", "author": "https://github.com/angular-ui/ui-calendar/graphs/contributors",
"license": "MIT", "license": "MIT",
@ -16,21 +16,27 @@
"package.json" "package.json"
], ],
"dependencies": { "dependencies": {
"angular": "~1.2.x", "angular": "1.3.15",
"fullcalendar": "~2.x" "jquery": "2.x",
"fullcalendar": "2.3.1",
"moment": "2.*"
}, },
"devDependencies": { "devDependencies": {
"angular-mocks": "~1.x", "angular-mocks": "~1.x",
"bootstrap-css": "2.3.1", "bootstrap-css": "2.3.1"
"jquery-ui": "~1.10.3"
}, },
"_release": "0.9.0-beta.1", "resolutions": {
"jquery": "~2.x",
"angular": "1.3.15"
},
"_release": "1.0.1",
"_resolution": { "_resolution": {
"type": "version", "type": "version",
"tag": "0.9.0-beta.1", "tag": "1.0.1",
"commit": "80a32ee00a6a327ea1446451d725fdd30a4535e9" "commit": "9c658bcf574be738bbe24258992da263eafc3095"
}, },
"_source": "git://github.com/angular-ui/ui-calendar.git", "_source": "https://github.com/angular-ui/ui-calendar.git",
"_target": "0.9.0-beta.1", "_target": "1.0.1",
"_originalSource": "angular-ui-calendar" "_originalSource": "angular-ui-calendar",
"_direct": true
} }

View File

@ -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. A complete AngularJS directive for the Arshaw FullCalendar.
# Requirements # Requirements
- ([AngularJS](http://code.angularjs.org/1.2.1/angular.js)) - ([AngularJS](http://code.angularjs.org/1.2.1/angular.js))
- ([fullcalendar.js 2.0 and it's dependencies](http://arshaw.com/fullcalendar/download/)) - ([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)) - 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 # 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: { dependencies: {
"angular-ui-calendar": "latest" "angular-ui-calendar": "latest"
} }
To your `components.json` file. Then run And then run
bower install 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> <link rel="stylesheet" href="bower_components/fullcalendar/dist/fullcalendar.css"/>
<script type="text/javascript" src="bower_components/jquery-ui/ui/jquery-ui.js"></script> <!-- jquery, moment, and angular have to get included before fullcalendar -->
<script type="text/javascript" src="bower_components/angular/angular.js"></script> <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/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/dist/fullcalendar.min.js"></script>
<script type="text/javascript" src="bower_components/fullcalendar/gcal.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: 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> <div ui-calendar ng-model="eventSources"></div>
Define your model in a scope e.g.
$scope.eventSources = [];
## Options ## Options
All the Arshaw Fullcalendar options can be passed through the directive. This even means function objects that are declared on the scope. 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. 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/) 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. Note that all calendar options which are functions that are passed into the calendar are wrapped in an apply automatically.
## Accessing the calendar object ## 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"> <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. 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; returns "" + event.price;
} }
<ui-calendar calendar-watch-event="extraEventSignature" ... > <ui-calendar calendar-watch-event="extraEventSignature(event)" ... >
// will now watch for price // 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 ## 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 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 ## Documentation for the Calendar
The calendar works alongside of all the documentation represented [here](http://arshaw.com/fullcalendar/docs) The calendar works alongside of all the documentation represented [here](http://arshaw.com/fullcalendar/docs)
## PR's R always Welcome ## PR's R always Welcome
Make sure that if a new feature is added, that the proper tests are created. 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

View File

@ -1,6 +1,6 @@
{ {
"name": "angular-ui-calendar", "name": "angular-ui-calendar",
"version": "0.9.0-beta.1", "version": "1.0.1",
"description": "A complete AngularJS directive for the Arshaw FullCalendar.", "description": "A complete AngularJS directive for the Arshaw FullCalendar.",
"author": "https://github.com/angular-ui/ui-calendar/graphs/contributors", "author": "https://github.com/angular-ui/ui-calendar/graphs/contributors",
"license": "MIT", "license": "MIT",
@ -16,12 +16,17 @@
"package.json" "package.json"
], ],
"dependencies": { "dependencies": {
"angular": "~1.2.x", "angular": "1.3.15",
"fullcalendar": "~2.x" "jquery": "2.x",
"fullcalendar": "2.3.1",
"moment": "2.*"
}, },
"devDependencies": { "devDependencies": {
"angular-mocks": "~1.x", "angular-mocks": "~1.x",
"bootstrap-css": "2.3.1", "bootstrap-css": "2.3.1"
"jquery-ui": "~1.10.3" },
"resolutions": {
"jquery": "~2.x",
"angular": "1.3.15"
} }
} }

View File

@ -9,48 +9,56 @@
*/ */
angular.module('ui.calendar', []) angular.module('ui.calendar', [])
.constant('uiCalendarConfig', {}) .constant('uiCalendarConfig', {calendars: {}})
.controller('uiCalendarCtrl', ['$scope', '$timeout', function($scope, $timeout){ .controller('uiCalendarCtrl', ['$scope',
'$locale', function(
$scope,
$locale){
var sourceSerialId = 1, var sources = $scope.eventSources,
eventSerialId = 1,
sources = $scope.eventSources,
extraEventSignature = $scope.calendarWatchEvent ? $scope.calendarWatchEvent : angular.noop, extraEventSignature = $scope.calendarWatchEvent ? $scope.calendarWatchEvent : angular.noop,
wrapFunctionWithScopeApply = function(functionToWrap){ wrapFunctionWithScopeApply = function(functionToWrap){
var wrapper; return function(){
// This may happen outside of angular context, so create one if outside.
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.
if ($scope.$root.$$phase) {
return functionToWrap.apply(this, arguments);
} else {
var args = arguments; var args = arguments;
var _this = this; var self = this;
$timeout(function(){ return $scope.$root.$apply(function(){
functionToWrap.apply(_this, args); 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) { if (!e._id) {
e._id = eventSerialId++; e._id = eventSerialId++;
} }
// This extracts all the information we need from the event. http://jsperf.com/angular-calendar-events-fingerprint/3 // 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 || '') + 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) { var sourceSerialId = 1, sourceEventsSerialId = 1;
return source.__id || (source.__id = sourceSerialId++); // @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() { 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 = []; var arraySources = [];
for (var i = 0, srcLen = sources.length; i < srcLen; i++) { for (var i = 0, srcLen = sources.length; i < srcLen; i++) {
var source = sources[i]; var source = sources[i];
@ -61,7 +69,7 @@ angular.module('ui.calendar', [])
// event source as object, ie extended form // event source as object, ie extended form
var extEvent = {}; var extEvent = {};
for(var key in source){ for(var key in source){
if(key !== '_uiCalId' && key !== 'events'){ if(key !== '_id' && key !== 'events'){
extEvent[key] = source[key]; extEvent[key] = source[key];
} }
} }
@ -71,14 +79,17 @@ angular.module('ui.calendar', [])
arraySources.push(source.events); arraySources.push(source.events);
} }
} }
return Array.prototype.concat.apply([], arraySources); 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 // Track changes in array of objects by assigning id tokens to each element and watching the scope for changes in the tokens
// arguments: // @param {Array|Function} arraySource array of objects to watch
// arraySource array of function that returns array of objects to watch // @param tokenFn {Function} that returns the token for a given object
// tokenFn function(object) 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) { this.changeWatcher = function(arraySource, tokenFn) {
var self; var self;
var getTokens = function() { var getTokens = function() {
@ -92,8 +103,12 @@ angular.module('ui.calendar', [])
} }
return result; 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 subtractAsSets = function(a, b) {
var result = [], inB = {}, i, n; var result = [], inB = {}, i, n;
for (i = 0, n = b.length; i < n; i++) { for (i = 0, n = b.length; i < n; i++) {
@ -110,6 +125,7 @@ angular.module('ui.calendar', [])
// Map objects to tokens and vice-versa // Map objects to tokens and vice-versa
var map = {}; var map = {};
// Compare newTokens to oldTokens and call onAdded, onRemoved, and onChanged handlers for each affected event respectively.
var applyChanges = function(newTokens, oldTokens) { var applyChanges = function(newTokens, oldTokens) {
var i, n, el, token; var i, n, el, token;
var replacedTokens = {}; var replacedTokens = {};
@ -138,9 +154,10 @@ angular.module('ui.calendar', [])
} }
}; };
return self = { return self = {
subscribe: function(scope, onChanged) { subscribe: function(scope, onArrayChanged) {
scope.$watch(getTokens, function(newTokens, oldTokens) { scope.$watch(getTokens, function(newTokens, oldTokens) {
if (!onChanged || onChanged(newTokens, oldTokens) !== false) { var notify = !(onArrayChanged && onArrayChanged(newTokens, oldTokens) === false);
if (notify) {
applyChanges(newTokens, oldTokens); applyChanges(newTokens, oldTokens);
} }
}, true); }, true);
@ -165,26 +182,31 @@ angular.module('ui.calendar', [])
return config; 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 { return {
restrict: 'A', restrict: 'A',
scope: {eventSources:'=ngModel',calendarWatchEvent: '&'}, scope: {eventSources:'=ngModel',calendarWatchEvent: '&'},
@ -193,8 +215,9 @@ angular.module('ui.calendar', [])
var sources = scope.eventSources, var sources = scope.eventSources,
sourcesChanged = false, sourcesChanged = false,
eventSourcesWatcher = controller.changeWatcher(sources, controller.sourcesFingerprint), calendar,
eventsWatcher = controller.changeWatcher(controller.allEvents, controller.eventsFingerprint), eventSourcesWatcher = controller.changeWatcher(sources, controller.sourceFingerprint),
eventsWatcher = controller.changeWatcher(controller.allEvents, controller.eventFingerprint),
options = null; options = null;
function getOptions(){ function getOptions(){
@ -203,8 +226,12 @@ angular.module('ui.calendar', [])
fullCalendarConfig = controller.getFullCalendarConfig(calendarSettings, uiCalendarConfig); fullCalendarConfig = controller.getFullCalendarConfig(calendarSettings, uiCalendarConfig);
var localeFullCalendarConfig = controller.getLocaleConfig(fullCalendarConfig);
angular.extend(localeFullCalendarConfig, fullCalendarConfig);
options = { eventSources: sources }; options = { eventSources: sources };
angular.extend(options, fullCalendarConfig); angular.extend(options, localeFullCalendarConfig);
//remove calendars from options
options.calendars = null;
var options2 = {}; var options2 = {};
for(var o in options){ for(var o in options){
@ -216,51 +243,60 @@ angular.module('ui.calendar', [])
} }
scope.destroy = function(){ scope.destroy = function(){
if(scope.calendar && scope.calendar.fullCalendar){ if(calendar && calendar.fullCalendar){
scope.calendar.fullCalendar('destroy'); calendar.fullCalendar('destroy');
} }
if(attrs.calendar) { if(attrs.calendar) {
scope.calendar = scope.$parent[attrs.calendar] = $(elm).html(''); calendar = uiCalendarConfig.calendars[attrs.calendar] = $(elm).html('');
} else { } else {
scope.calendar = $(elm).html(''); calendar = $(elm).html('');
} }
}; };
scope.init = function(){ scope.init = function(){
scope.calendar.fullCalendar(options); calendar.fullCalendar(options);
if(attrs.calendar) {
uiCalendarConfig.calendars[attrs.calendar] = calendar;
}
}; };
eventSourcesWatcher.onAdded = function(source) { eventSourcesWatcher.onAdded = function(source) {
scope.calendar.fullCalendar('addEventSource', source); calendar.fullCalendar('addEventSource', source);
sourcesChanged = true; sourcesChanged = true;
}; };
eventSourcesWatcher.onRemoved = function(source) { eventSourcesWatcher.onRemoved = function(source) {
scope.calendar.fullCalendar('removeEventSource', source); calendar.fullCalendar('removeEventSource', source);
sourcesChanged = true;
};
eventSourcesWatcher.onChanged = function(source) {
calendar.fullCalendar('refetchEvents');
sourcesChanged = true; sourcesChanged = true;
}; };
eventsWatcher.onAdded = function(event) { eventsWatcher.onAdded = function(event) {
scope.calendar.fullCalendar('renderEvent', event); calendar.fullCalendar('renderEvent', event, (event.stick ? true : false));
}; };
eventsWatcher.onRemoved = function(event) { eventsWatcher.onRemoved = function(event) {
scope.calendar.fullCalendar('removeEvents', function(e) { calendar.fullCalendar('removeEvents', event._id);
return e._id === event._id;
});
}; };
eventsWatcher.onChanged = function(event) { eventsWatcher.onChanged = function(event) {
event._start = $.fullCalendar.moment(event.start); var clientEvents = calendar.fullCalendar('clientEvents', event._id);
event._end = $.fullCalendar.moment(event.end); for (var i = 0; i < clientEvents.length; i++) {
scope.calendar.fullCalendar('updateEvent', event); var clientEvent = clientEvents[i];
clientEvent = angular.extend(clientEvent, event);
calendar.fullCalendar('updateEvent', clientEvent);
}
}; };
eventSourcesWatcher.subscribe(scope); eventSourcesWatcher.subscribe(scope);
eventsWatcher.subscribe(scope, function(newTokens, oldTokens) { eventsWatcher.subscribe(scope, function() {
if (sourcesChanged === true) { if (sourcesChanged === true) {
sourcesChanged = false; sourcesChanged = false;
// prevent incremental updates in this case // return false to prevent onAdded/Removed/Changed handlers from firing in this case
return false; return false;
} }
}); });