4141 retrieve : function ( ) { return [ ] } ,
4242 remove : function ( ) { } ,
4343
44- // default insert_policy is simple - allow overlaps:
45- insert_policy : function ( event_list , evt ) { event_list [ evt . attr . id ] = evt ; } ,
44+ // default insert_policy is simple - do nothing, allow overlaps:
45+ insert_policy : function ( event_list , evt , end_of_day ) { } ,
4646
4747 min_time : '07:00' , // 7am
4848 max_time : '20:00' , // 8pm
131131function Calendar ( element , options )
132132{
133133 var t = this ;
134+ var global_timeformat ;
134135
135136//--- Exports ---
136137 t . element = element ;
137138 t . options = options ;
139+ t . options . baseminutes = convert_to_minutes ( t . options . min_time ) ; // used for event rendering
140+
141+ global_timeformat = t . options . min_time [ - 1 ] == 'm' ; // true if times in am/pm format
142+ // almost certainly a localization
143+ // problem here
138144
139145 //
140146 // look for <resource> elements within the calendar entity,
141147 // create a resource object for each, and bind it to the calendar
142148 //
143149 t . resources = [ ] ;
144150 element . find ( 'resource' ) . each ( function ( i , element ) {
145- t . resources [ $ ( this ) . attr ( 'id' ) ] = new rc_resource ( $ ( this ) , t . options . insert_policy ) ;
151+ t . resources [ $ ( this ) . attr ( 'id' ) ] = new rc_resource ( $ ( this ) , t . options . get_open_time ) ;
146152 } ) ;
147- t . resources [ 'unassigned_event_resource' ] = new rc_resource ( $ ( '#unassigned_event_resource' ) , function ( ) { } ) ;
153+ t . resources [ 'unassigned_event_resource' ] = new rc_resource ( $ ( '#unassigned_event_resource' ) , function ( ) { return [ t . options . min_time , t . options . max_time ] } ) ;
148154
149155 t . initialize_events = function ( ) {
150156 t . eventmanager = new rc_EventManager ( t . options . retrieve ,
151157 t . options . persist ,
152158 t . options . remove ,
159+ t . options . insert_policy ,
153160 t . resources ,
154161 t [ t . options . render_event ] ) ;
155- }
162+ }
156163
157164
158165//--- Public Methods ---
@@ -176,8 +183,8 @@ function Calendar( element, options )
176183 var dragged = $ ( ui . draggable [ 0 ] ) ;
177184
178185 // event.target gets confused if the viewport is smaller than the droppable,
179- // and the scrolled droppable 'virtually' overlaps another viewport containing
180- // droppables. This function call ensures that the visible droppable is used
186+ // and the scrolled droppable 'virtually' overlaps droppables from another viewport.
187+ // This function call ensures that the visible droppable is used
181188 var target_id = findDroppable ( ui ) ;
182189
183190 // the other half to this problem is that this function gets called twice,
@@ -272,16 +279,8 @@ function Calendar( element, options )
272279 } ) ;
273280 }
274281
275- var elapsed_mins = evt . attr . prep_time + evt . attr . duration + evt . attr . cleanup_time ;
276-
277- evt . attr . end = addMinutes_timeOfDay (
278- evt . attr . start ,
279- elapsed_mins ,
280- evt . attr . start ) . newtime ;
281-
282-
283- evt . attr . t_offset = ~ ~ ( diffMinutes_timeOfDay ( t . options . min_time , evt . attr . start ) / t . options . interval * t . options . intervalpixels ) ;
284- evt . attr . t_height = ~ ~ ( ( elapsed_mins / t . options . interval ) * t . options . intervalpixels ) - 2 ;
282+ evt . attr . t_offset = ~ ~ ( evt . attr . startmins - t . options . baseminutes ) / t . options . interval * t . options . intervalpixels ;
283+ evt . attr . t_height = ~ ~ ( ( ( evt . attr . endmins - evt . attr . startmins ) / t . options . interval ) * t . options . intervalpixels ) - 2 ;
285284 evt . attr . t_prepad = ~ ~ ( ( evt . attr . prep_time / t . options . interval ) * t . options . intervalpixels ) ;
286285 evt . attr . t_postpad = ~ ~ ( ( evt . attr . cleanup_time / t . options . interval ) * t . options . intervalpixels ) ;
287286
@@ -290,8 +289,13 @@ function Calendar( element, options )
290289
291290 newev . find ( '.deleteevent' ) . click ( function ( event ) {
292291 event . stopPropagation ( ) ;
293- confirm ( "You are about to delete this event; this action can not be undone! Confirm deletion?" ,
294- t . eventmanager . deleteEvent , evt . attr . id ) ;
292+
293+ if ( confirm ( "You are about to delete this event; this action can not be undone! Confirm deletion?" ) ) {
294+ t . eventmanager . deleteEvent ( evt . attr . id ) ;
295+ }
296+
297+
298+
295299 } ) ;
296300
297301 if ( ! evt . attr . locked ) {
@@ -319,7 +323,7 @@ function Calendar( element, options )
319323 var snapheight = ~ ~ ( ( ui . size . height + ( t . options . intervalpixels / 2 ) ) / t . options . intervalpixels ) ;
320324 var total_mins = snapheight * t . options . interval ;
321325
322- evt . attr . duration = total_mins - ( evt . attr . prep_time + evt . attr . cleanup_time ) ;
326+ evt . set_duration ( total_mins - ( evt . attr . prep_time + evt . attr . cleanup_time ) ) ;
323327 ui . helper . css ( 'height' , snapheight * t . options . intervalpixels - 2 + 'px' ) ;
324328 t . view_week_render_event ( evt ) ;
325329 t . options . persist ( evt ) ;
@@ -390,8 +394,6 @@ function Calendar( element, options )
390394 // Called during dragging and on drop, this function returns the id of a div beneath
391395 // the draggable object, if that div is visible and a droppable target.
392396 //
393- // [TODO - problem dragging new event onto scrolled Narwhal, ends up in Orca]
394- //
395397 function findDroppable ( ui )
396398 {
397399 retval = false ;
@@ -483,11 +485,36 @@ function Calendar( element, options )
483485// -----------
484486// Basic event characteristics that can be extended with application-specific code
485487//
486-
488+ // [REVIEW]
489+ // Ambivalent about global_timeformat; could be eliminated here and managed
490+ // only in convert_to_time( t, ampm ) helper function.
491+ //
487492function rc_Event ( options )
488493{
489494 var t = this ;
490495
496+ t . set_start_time = function ( s ) {
497+ if ( "number" == typeof ( s ) ) {
498+ t . attr . startmins = s ;
499+ t . attr . start = convert_to_time ( s , global_timeformat ) ;
500+ t . attr . ampm = global_timeformat ;
501+ }
502+ else {
503+ t . attr . start = s ;
504+ t . attr . startmins = convert_to_minutes ( s ) ;
505+ t . attr . ampm = ( 'm' == s [ - 1 ] ) ; // if true, show times in 12hr am/pm format
506+ }
507+
508+ t . attr . endmins = t . attr . startmins + t . attr . duration + t . attr . prep_time + t . attr . cleanup_time ;
509+ t . attr . end = convert_to_time ( t . attr . endmins , t . attr . ampm ) ;
510+ }
511+
512+ t . set_duration = function ( d ) {
513+ t . attr . endmins += ( d - t . attr . duration ) ;
514+ t . attr . duration = d ;
515+ t . attr . end = convert_to_time ( t . attr . endmins , t . attr . ampm ) ;
516+ }
517+
491518 var defaults = {
492519 id : new Date ( ) . getTime ( ) ,
493520 date : 1375416000000 ,
@@ -501,6 +528,11 @@ function rc_Event( options )
501528 } ;
502529
503530 t . attr = $ . extend ( true , { } , defaults , options ) ;
531+
532+ // prepare internal representation of times
533+ t . set_start_time ( t . attr . start ) ;
534+
535+ return t ;
504536}
505537
506538
@@ -511,51 +543,35 @@ function rc_Event( options )
511543// Manages updates to/from the server
512544//
513545
514- function rc_EventManager ( retrieve_events , save_event , delete_event , resources , display )
546+ function rc_EventManager ( retrieve_events , save_event , delete_event , insert_policy , resources , display )
515547{
516- var render = display ;
517- var persistEvent = save_event ;
518- var killEvent = delete_event ;
548+ var render = display ;
549+ var persistEvent = save_event ;
550+ var killEvent = delete_event ;
551+ var insert_policy = insert_policy ;
552+
519553 var t = this ;
520554
521- t . Events = retrieve_events ( ) ;
555+ t . Events = [ ] ;
556+ attrs = retrieve_events ( ) ;
522557
523- for ( var i in t . Events ) {
524- var evt = t . Events [ i ] ;
558+ for ( var i in attrs ) {
559+ var evt = new rc_Event ( attrs [ i ] . attr ) ;
560+ t . Events [ evt . attr . id ] = evt ;
525561 if ( undefined != resources [ evt . attr . resource ] ) {
526- resources [ evt . attr . resource ] . addEvent ( evt , 'no_confirm' ) ;
562+ resources [ evt . attr . resource ] . addEvent ( evt ) ;
527563 render ( evt ) ;
528564 }
529- else {
530- // [TODO] - remove from list??
531- }
532-
533- }
534-
535- t . createEvent = function ( parent , resource , options ) {
536- var new_event = new rc_Event ( options ) ;
537- var id = new_event . attr . id ;
538-
539- t . Events [ id ] = new_event ;
540-
541- if ( resource . addEvent ( new_event ) ) {
542- new_event . attr . resource = resource . id ;
543- new_event . attr . parent = parent . attr ( 'id' ) ; // div in which this event currently resides
544- render ( new_event ) ;
545- persistEvent ( new_event ) ;
546- }
547-
548- return new_event ;
549565 }
550566
551567 t . moveEvent = function ( evt , parent , old_resource , new_resource , options ) {
552568
553569 // have to update event attributes before attempting to move the event,
554570 // but have to be prepared to revert them if the new resource rejects the event
555571
556- var stash = evt . attr ;
572+ var stash = $ . extend ( { } , evt . attr ) ;
557573
558- evt . attr . start = options . start ;
574+ evt . set_start_time ( options . start ) ;
559575 evt . attr . date = options . date ;
560576 evt . attr . t_offset = options . t_offset ;
561577
@@ -565,20 +581,66 @@ function rc_EventManager( retrieve_events, save_event, delete_event, resources,
565581 // lazy-evaluation of boolean tests ensures 'removeEvent' happens only for inter-resource moves
566582 //
567583 if (
568- ( ( old_resource . attr . id == new_resource . attr . id ) && new_resource . addEvent ( evt ) )
569- || ( ( old_resource . attr . id != new_resource . attr . id ) && new_resource . addEvent ( evt ) && old_resource . removeEvent ( evt ) )
584+ ( ( old_resource . id == new_resource . id ) && new_resource . addEvent ( evt ) )
585+ || ( ( old_resource . id != new_resource . id ) && new_resource . addEvent ( evt ) && old_resource . removeEvent ( evt ) )
570586 ) {
571- // event accepted
572- evt . attr . resource = new_resource . id ;
573- evt . attr . parent = parent . attr ( 'id' ) ;
574- render ( evt ) ;
575- persistEvent ( evt ) ;
576- return true ;
587+
588+ // event accepted, but may yet fail the insertion-overlap criteria test
589+ // unassigned event resource always accepts drop-ins
590+ if ( 'unassigned_event_resource' == new_resource . id
591+ || ! insert_policy ( new_resource . listEvents ( evt . attr . date ) ,
592+ evt ,
593+ new_resource . get_open_time ( evt . attr . date ) ,
594+ render ,
595+ persistEvent ) ) {
596+
597+ evt . attr . resource = new_resource . id ;
598+ evt . attr . parent = parent . attr ( 'id' ) ;
599+ render ( evt ) ;
600+ persistEvent ( evt ) ;
601+
602+ if ( isNaN ( evt . attr . date ) ) {
603+ rc_notify ( 'Success' ,
604+ 'Added ' + evt . attr . ev_text + ' to Unassigned Events list' ,
605+ 'success' ) ;
606+ }
607+ else {
608+ var formatted_date = new moment ( evt . attr . date ) . format ( "dddd, Do MMM YYYY" ) ;
609+ rc_notify ( 'Success' ,
610+ 'Added ' + evt . attr . ev_text + ' to ' + new_resource . attr . title + ' at ' + evt . attr . start + ' on ' + formatted_date ,
611+ 'success' ) ;
612+ }
613+
614+ return false ;
615+
616+ }
617+ }
618+
619+ // failed to insert into resource, or failed to fit into calendar
620+ new_resource . removeEvent ( evt ) ;
621+ evt . attr = $ . extend ( { } , stash ) ;
622+
623+ if ( "none" == evt . attr . resource ) {
624+ killEvent ( evt . attr . id ) ; // Was freshly dragged from the new events palette
577625 }
578626 else {
579- evt . attr = stash ;
580- return false ;
627+ old_resource . addEvent ( evt ) ;
628+ render ( evt ) ;
581629 }
630+
631+ return true ;
632+
633+ }
634+
635+ t . createEvent = function ( parent , resource , options ) {
636+ var new_event = new rc_Event ( options ) ;
637+ var id = new_event . attr . id ;
638+
639+ t . Events [ id ] = new_event ;
640+
641+ t . moveEvent ( new_event , parent , resource , resource , options )
642+
643+ return new_event ;
582644 }
583645
584646 t . deleteEvent = function ( id ) {
@@ -606,16 +668,7 @@ function rc_EventManager( retrieve_events, save_event, delete_event, resources,
606668// 3: live Ajax + localStorage for offline working
607669
608670
609- //
610- // [TODO] - register per-day availability times for each resource
611- // This accommodates different open/close times on different days, or
612- // worker shifts, or holiday/vacation times.
613- //
614- // Implement regular hours function, and specific overrides
615- //
616-
617-
618- function rc_resource ( resource_element , insert_policy ) {
671+ function rc_resource ( resource_element , get_open_time ) {
619672 var t = this ;
620673
621674 //
@@ -625,7 +678,8 @@ function rc_resource( resource_element, insert_policy ) {
625678 t . eventpool = { } ;
626679
627680 t . id = resource_element . attr ( 'id' ) ;
628- t . insert_policy = insert_policy ;
681+ t . get_open_time = get_open_time ;
682+
629683 t . attr = $ . parseJSON ( resource_element . attr ( 'data-attr' ) ) ;
630684
631685 //
@@ -634,8 +688,6 @@ function rc_resource( resource_element, insert_policy ) {
634688 // perform basic sanity checking such as ensuring that (for example)
635689 // a room can accommodate all of the attendees for a meeting
636690
637- // [TODO - fix this so that Resources do not have to specify validation]
638-
639691 t . attr . validateFn = window [ resource_element . attr ( 'data-validate' ) ] ;
640692 t . attr . validateParams = $ . parseJSON ( resource_element . attr ( 'data-params' ) ) ;
641693
@@ -660,35 +712,27 @@ function rc_resource( resource_element, insert_policy ) {
660712 // true : could add the event
661713 // false: unable to add the event
662714 //
663- t . addEvent = function ( event , no_confirm ) {
664- if ( $ . isFunction ( t . attr . validateFn ) && ( reason = t . attr . validateFn ( event . attr , t . attr . validateParams ) ) ) {
715+ t . addEvent = function ( evt ) {
716+ if ( $ . isFunction ( t . attr . validateFn ) && ( reason = t . attr . validateFn ( evt . attr , t . attr . validateParams ) ) ) {
665717 rc_notify ( 'Unable to add the event' , 'The ' + reason + ' requirement was not met' , 'error' ) ;
666718 return false ;
667719 }
668720 else {
669721
670- if ( "undefined" == typeof ( t . eventpool [ event . attr . date ] ) ) {
671- t . eventpool [ event . attr . date ] = [ ] ;
722+ if ( "undefined" == typeof ( t . eventpool [ evt . attr . date ] ) ) {
723+ t . eventpool [ evt . attr . date ] = { } ;
672724 }
673725
674- //
675- // Policy code for overlapping events, locked events
676- //
677- t . insert_policy ( t . eventpool [ event . attr . date ] , event ) ;
678-
679- if ( undefined == no_confirm ) {
680- var formatted_date = new moment ( event . attr . date ) . format ( "dddd, Do MMM YYYY" ) ;
681-
682- rc_notify ( 'Success' ,
683- 'Added ' + event . attr . ev_text + ' to ' + t . attr . title + ' at ' + event . attr . start + ' on ' + formatted_date ,
684- 'success' ) ;
685- }
726+ t . eventpool [ evt . attr . date ] [ evt . attr . id ] = evt ;
686727 }
687728 return true ;
688729 }
689730
690731 t . removeEvent = function ( evt ) {
691- t . eventpool [ evt . attr . date ] [ evt . attr . id ] = undefined ;
732+ if ( ! isNaN ( evt . attr . date ) && "undefined" != typeof ( t . eventpool [ evt . attr . date ] ) ) {
733+ delete t . eventpool [ evt . attr . date ] [ evt . attr . id ] ;
734+ }
735+ return true ;
692736 }
693737
694738 t . listEvents = function ( date ) {
0 commit comments