Schedule Template

HTML
<div class="cd-schedule loading"> <div class="timeline"> <ul> <li><span>09:00</span></li> <li><span>09:30</span></li> <li><span>10:00</span></li> <li><span>10:30</span></li> <li><span>11:00</span></li> <li><span>11:30</span></li> <li><span>12:00</span></li> <li><span>12:30</span></li> <li><span>13:00</span></li> <li><span>13:30</span></li> <li><span>14:00</span></li> <li><span>14:30</span></li> <li><span>15:00</span></li> <li><span>15:30</span></li> <li><span>16:00</span></li> <li><span>16:30</span></li> <li><span>17:00</span></li> <li><span>17:30</span></li> <li><span>18:00</span></li> </ul> </div> <!-- .timeline --> <div class="events"> <ul> <li class="events-group"> <div class="top-info"><span>Monday</span></div> <ul> <li class="single-event" data-start="09:30" data-end="10:30" data-content="event-abs-circuit" data-event="event-1"> <a href="#0"> <em class="event-name">Abs Circuit</em> </a> </li> <li class="single-event" data-start="11:00" data-end="12:30" data-content="event-rowing-workout" data-event="event-2"> <a href="#0"> <em class="event-name">Rowing Workout</em> </a> </li> <li class="single-event" data-start="14:00" data-end="15:15" data-content="event-yoga-1" data-event="event-3"> <a href="#0"> <em class="event-name">Yoga Level 1</em> </a> </li> </ul> </li> <li class="events-group"> <div class="top-info"><span>Tuesday</span></div> <ul> <li class="single-event" data-start="10:00" data-end="11:00" data-content="event-rowing-workout" data-event="event-2"> <a href="#0"> <em class="event-name">Rowing Workout</em> </a> </li> <li class="single-event" data-start="11:30" data-end="13:00" data-content="event-restorative-yoga" data-event="event-4"> <a href="#0"> <em class="event-name">Restorative Yoga</em> </a> </li> <li class="single-event" data-start="13:30" data-end="15:00" data-content="event-abs-circuit" data-event="event-1"> <a href="#0"> <em class="event-name">Abs Circuit</em> </a> </li> <li class="single-event" data-start="15:45" data-end="16:45" data-content="event-yoga-1" data-event="event-3"> <a href="#0"> <em class="event-name">Yoga Level 1</em> </a> </li> </ul> </li> <li class="events-group"> <div class="top-info"><span>Wednesday</span></div> <ul> <li class="single-event" data-start="09:00" data-end="10:15" data-content="event-restorative-yoga" data-event="event-4"> <a href="#0"> <em class="event-name">Restorative Yoga</em> </a> </li> <li class="single-event" data-start="10:45" data-end="11:45" data-content="event-yoga-1" data-event="event-3"> <a href="#0"> <em class="event-name">Yoga Level 1</em> </a> </li> <li class="single-event" data-start="12:00" data-end="13:45" data-content="event-rowing-workout" data-event="event-2"> <a href="#0"> <em class="event-name">Rowing Workout</em> </a> </li> <li class="single-event" data-start="13:45" data-end="15:00" data-content="event-yoga-1" data-event="event-3"> <a href="#0"> <em class="event-name">Yoga Level 1</em> </a> </li> </ul> </li> <li class="events-group"> <div class="top-info"><span>Thursday</span></div> <ul> <li class="single-event" data-start="09:30" data-end="10:30" data-content="event-abs-circuit" data-event="event-1"> <a href="#0"> <em class="event-name">Abs Circuit</em> </a> </li> <li class="single-event" data-start="12:00" data-end="13:45" data-content="event-restorative-yoga" data-event="event-4"> <a href="#0"> <em class="event-name">Restorative Yoga</em> </a> </li> <li class="single-event" data-start="15:30" data-end="16:30" data-content="event-abs-circuit" data-event="event-1"> <a href="#0"> <em class="event-name">Abs Circuit</em> </a> </li> <li class="single-event" data-start="17:00" data-end="18:30" data-content="event-rowing-workout" data-event="event-2"> <a href="#0"> <em class="event-name">Rowing Workout</em> </a> </li> </ul> </li> <li class="events-group"> <div class="top-info"><span>Friday</span></div> <ul> <li class="single-event" data-start="10:00" data-end="11:00" data-content="event-rowing-workout" data-event="event-2"> <a href="#0"> <em class="event-name">Rowing Workout</em> </a> </li> <li class="single-event" data-start="12:30" data-end="14:00" data-content="event-abs-circuit" data-event="event-1"> <a href="#0"> <em class="event-name">Abs Circuit</em> </a> </li> <li class="single-event" data-start="15:45" data-end="16:45" data-content="event-yoga-1" data-event="event-3"> <a href="#0"> <em class="event-name">Yoga Level 1</em> </a> </li> </ul> </li> </ul> </div> <div class="event-modal"> <header class="header"> <div class="content"> <span class="event-date"></span> <h3 class="event-name"></h3> </div> <div class="header-bg"></div> </header> <div class="body"> <div class="event-info"></div> <div class="body-bg"></div> </div> <a href="#0" class="close">Close</a> </div> <div class="cover-layer"></div> </div> <!-- .cd-schedule -->
CSS
/* -------------------------------- Primary style -------------------------------- */ *, *::after, *::before { box-sizing: border-box; } html { font-size: 62.5%; } body { font-size: 1.6rem; font-family: "Source Sans Pro", sans-serif; color: #222222; background-color: white; } a { color: #A2B9B2; text-decoration: none; } ul { list-style-type: none; } /* -------------------------------- Main Components -------------------------------- */ .cd-schedule { position: relative; margin: 2em 0; } .cd-schedule::before { /* never visible - this is used in js to check the current MQ */ content: 'mobile'; display: none; } @media only screen and (min-width: 800px) { .cd-schedule { width: 90%; max-width: 1400px; margin: 2em auto; } .cd-schedule::after { clear: both; content: ""; display: block; } .cd-schedule::before { content: 'desktop'; } } .cd-schedule .timeline { display: none; } @media only screen and (min-width: 800px) { .cd-schedule .timeline { display: block; position: absolute; top: 0; left: 0; height: 100%; width: 100%; padding-top: 50px; } .cd-schedule .timeline li { position: relative; height: 50px; } .cd-schedule .timeline li::after { /* this is used to create the table horizontal lines */ content: ''; position: absolute; bottom: 0; left: 0; width: 100%; height: 1px; background: #EAEAEA; } .cd-schedule .timeline li:last-of-type::after { display: none; } .cd-schedule .timeline li span { display: none; } } @media only screen and (min-width: 1000px) { .cd-schedule .timeline li::after { width: calc(100% - 60px); left: 60px; } .cd-schedule .timeline li span { display: inline-block; -webkit-transform: translateY(-50%); -ms-transform: translateY(-50%); transform: translateY(-50%); } .cd-schedule .timeline li:nth-of-type(2n) span { display: none; } } .cd-schedule .events { position: relative; z-index: 1; } .cd-schedule .events .events-group { margin-bottom: 30px; } .cd-schedule .events .top-info { width: 100%; padding: 0 5%; } .cd-schedule .events .top-info > span { display: inline-block; line-height: 1.2; margin-bottom: 10px; font-weight: bold; } .cd-schedule .events .events-group > ul { position: relative; padding: 0 5%; /* force its children to stay on one line */ display: -webkit-box; display: -ms-flexbox; display: flex; overflow-x: scroll; -webkit-overflow-scrolling: touch; } .cd-schedule .events .events-group > ul::after { /* never visible - used to add a right padding to .events-group > ul */ display: inline-block; content: '-'; width: 1px; height: 100%; opacity: 0; color: transparent; } .cd-schedule .events .single-event { /* force them to stay on one line */ -ms-flex-negative: 0; flex-shrink: 0; float: left; height: 150px; width: 70%; max-width: 300px; box-shadow: inset 0 -3px 0 rgba(0, 0, 0, 0.2); margin-right: 20px; -webkit-transition: opacity .2s, background .2s; transition: opacity .2s, background .2s; } .cd-schedule .events .single-event:last-of-type { margin-right: 5%; } .cd-schedule .events .single-event a { display: block; height: 100%; padding: .8em; } @media only screen and (min-width: 550px) { .cd-schedule .events .single-event { width: 40%; } } @media only screen and (min-width: 800px) { .cd-schedule .events { float: left; width: 100%; } .cd-schedule .events .events-group { width: 20%; float: left; border: 1px solid #EAEAEA; /* reset style */ margin-bottom: 0; } .cd-schedule .events .events-group:not(:first-of-type) { border-left-width: 0; } .cd-schedule .events .top-info { /* vertically center its content */ display: table; height: 50px; border-bottom: 1px solid #EAEAEA; /* reset style */ padding: 0; } .cd-schedule .events .top-info > span { /* vertically center inside its parent */ display: table-cell; vertical-align: middle; padding: 0 .5em; text-align: center; /* reset style */ font-weight: normal; margin-bottom: 0; } .cd-schedule .events .events-group > ul { height: 950px; /* reset style */ display: block; overflow: visible; padding: 0; } .cd-schedule .events .events-group > ul::after { clear: both; content: ""; display: block; } .cd-schedule .events .events-group > ul::after { /* reset style */ display: none; } .cd-schedule .events .single-event { position: absolute; z-index: 3; /* top position and height will be set using js */ width: calc(100% + 2px); left: -1px; box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1), inset 0 -3px 0 rgba(0, 0, 0, 0.2); /* reset style */ -ms-flex-negative: 1; flex-shrink: 1; height: auto; max-width: none; margin-right: 0; } .cd-schedule .events .single-event a { padding: 1.2em; } .cd-schedule .events .single-event:last-of-type { /* reset style */ margin-right: 0; } .cd-schedule .events .single-event.selected-event { /* the .selected-event class is added when an user select the event */ visibility: hidden; } } @media only screen and (min-width: 1000px) { .cd-schedule .events { /* 60px is the .timeline element width */ width: calc(100% - 60px); margin-left: 60px; } } .cd-schedule.loading .events .single-event { /* the class .loading is added by default to the .cd-schedule element it is removed as soon as the single events are placed in the schedule plan (using javascript) */ opacity: 0; } .cd-schedule .event-name, .cd-schedule .event-date { display: block; color: white; font-weight: bold; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } .cd-schedule .event-name { font-size: 2.4rem; } @media only screen and (min-width: 800px) { .cd-schedule .event-name { font-size: 2rem; } } .cd-schedule .event-date { /* they are not included in the the HTML but added using JavScript */ font-size: 1.4rem; opacity: .7; line-height: 1.2; margin-bottom: .2em; } .cd-schedule .single-event[data-event="event-1"], .cd-schedule [data-event="event-1"] .header-bg { /* this is used to set a background color for the event and the modal window */ background: #577F92; } .cd-schedule .single-event[data-event="event-1"]:hover { background: #618da1; } .cd-schedule .single-event[data-event="event-2"], .cd-schedule [data-event="event-2"] .header-bg { background: #443453; } .cd-schedule .single-event[data-event="event-2"]:hover { background: #513e63; } .cd-schedule .single-event[data-event="event-3"], .cd-schedule [data-event="event-3"] .header-bg { background: #A2B9B2; } .cd-schedule .single-event[data-event="event-3"]:hover { background: #b1c4be; } .cd-schedule .single-event[data-event="event-4"], .cd-schedule [data-event="event-4"] .header-bg { background: #f6b067; } .cd-schedule .single-event[data-event="event-4"]:hover { background: #f7bd7f; } .cd-schedule .event-modal { position: fixed; z-index: 3; top: 0; right: 0; height: 100%; width: 100%; visibility: hidden; /* Force Hardware acceleration */ -webkit-transform: translateZ(0); transform: translateZ(0); -webkit-transform: translateX(100%); -ms-transform: translateX(100%); transform: translateX(100%); -webkit-transition: visibility .4s, -webkit-transform .4s; transition: visibility .4s, -webkit-transform .4s; transition: transform .4s, visibility .4s; transition: transform .4s, visibility .4s, -webkit-transform .4s; -webkit-transition-timing-function: cubic-bezier(0.5, 0, 0.1, 1); transition-timing-function: cubic-bezier(0.5, 0, 0.1, 1); } .cd-schedule .event-modal .header { position: relative; height: 70px; /* vertically center its content */ display: table; width: 100%; } .cd-schedule .event-modal .header .content { position: relative; z-index: 3; /* vertically center inside its parent */ display: table-cell; vertical-align: middle; padding: .6em 5%; } .cd-schedule .event-modal .body { position: relative; width: 100%; /* 70px is the .header height */ height: calc(100% - 70px); } .cd-schedule .event-modal .event-info { position: relative; z-index: 2; line-height: 1.4; height: 100%; overflow: hidden; } .cd-schedule .event-modal .event-info > div { overflow: auto; height: 100%; padding: 1.4em 5%; } .cd-schedule .event-modal .header-bg, .cd-schedule .event-modal .body-bg { /* these are the morphing backgrounds - visible on desktop only */ position: absolute; top: 0; left: 0; height: 100%; width: 100%; } .cd-schedule .event-modal .body-bg { z-index: 1; background: white; -webkit-transform-origin: top left; -ms-transform-origin: top left; transform-origin: top left; } .cd-schedule .event-modal .header-bg { z-index: 2; -webkit-transform-origin: top center; -ms-transform-origin: top center; transform-origin: top center; } .cd-schedule .event-modal .close { /* this is the 'X' icon */ position: absolute; top: 0; right: 0; z-index: 3; background: rgba(0, 0, 0, 0.1); /* replace text with icon */ color: transparent; white-space: nowrap; text-indent: 100%; height: 70px; width: 70px; } .cd-schedule .event-modal .close::before, .cd-schedule .event-modal .close::after { /* these are the two lines of the 'X' icon */ content: ''; position: absolute; top: 50%; left: 50%; width: 2px; height: 22px; background: white; -webkit-backface-visibility: hidden; backface-visibility: hidden; } .cd-schedule .event-modal .close::before { -webkit-transform: translateX(-50%) translateY(-50%) rotate(45deg); -ms-transform: translateX(-50%) translateY(-50%) rotate(45deg); transform: translateX(-50%) translateY(-50%) rotate(45deg); } .cd-schedule .event-modal .close::after { -webkit-transform: translateX(-50%) translateY(-50%) rotate(-45deg); -ms-transform: translateX(-50%) translateY(-50%) rotate(-45deg); transform: translateX(-50%) translateY(-50%) rotate(-45deg); } .cd-schedule .event-modal .event-date { display: none; } .cd-schedule .event-modal.no-transition { -webkit-transition: none; transition: none; } .cd-schedule .event-modal.no-transition .header-bg, .cd-schedule .event-modal.no-transition .body-bg { -webkit-transition: none; transition: none; } @media only screen and (min-width: 800px) { .cd-schedule .event-modal { /* reset style */ right: auto; width: auto; height: auto; -webkit-transform: translateX(0); -ms-transform: translateX(0); transform: translateX(0); will-change: transform, width, height; -webkit-transition: height .4s, width .4s, visibility .4s, -webkit-transform .4s; transition: height .4s, width .4s, visibility .4s, -webkit-transform .4s; transition: height .4s, width .4s, transform .4s, visibility .4s; transition: height .4s, width .4s, transform .4s, visibility .4s, -webkit-transform .4s; -webkit-transition-timing-function: cubic-bezier(0.5, 0, 0.1, 1); transition-timing-function: cubic-bezier(0.5, 0, 0.1, 1); } .cd-schedule .event-modal .header { position: absolute; display: block; top: 0; left: 0; height: 100%; } .cd-schedule .event-modal .header .content { /* reset style */ display: block; padding: .8em; } .cd-schedule .event-modal .event-info > div { padding: 2em 3em 2em 2em; } .cd-schedule .event-modal .body { height: 100%; width: auto; } .cd-schedule .event-modal .header-bg, .cd-schedule .event-modal .body-bg { /* Force Hardware acceleration */ -webkit-transform: translateZ(0); transform: translateZ(0); will-change: transform; -webkit-backface-visibility: hidden; backface-visibility: hidden; } .cd-schedule .event-modal .header-bg { -webkit-transition: -webkit-transform .4s; transition: -webkit-transform .4s; transition: transform .4s; transition: transform .4s, -webkit-transform .4s; -webkit-transition-timing-function: cubic-bezier(0.5, 0, 0.1, 1); transition-timing-function: cubic-bezier(0.5, 0, 0.1, 1); } .cd-schedule .event-modal .body-bg { opacity: 0; -webkit-transform: none; -ms-transform: none; transform: none; } .cd-schedule .event-modal .event-date { display: block; } .cd-schedule .event-modal .close, .cd-schedule .event-modal .event-info { opacity: 0; } .cd-schedule .event-modal .close { width: 40px; height: 40px; background: transparent; } .cd-schedule .event-modal .close::after, .cd-schedule .event-modal .close::before { background: #222222; height: 16px; } } @media only screen and (min-width: 1000px) { .cd-schedule .event-modal .header .content { padding: 1.2em; } } .cd-schedule.modal-is-open .event-modal { /* .modal-is-open class is added as soon as an event is selected */ -webkit-transform: translateX(0); -ms-transform: translateX(0); transform: translateX(0); visibility: visible; } .cd-schedule.modal-is-open .event-modal .event-info > div { /* smooth scroll on iOS touch devices */ -webkit-overflow-scrolling: touch; } @media only screen and (min-width: 800px) { .cd-schedule.animation-completed .event-modal .close, .cd-schedule.content-loaded.animation-completed .event-modal .event-info { /* the .animation-completed class is added when the modal animation is completed the .content-loaded class is added when the modal content has been loaded (using ajax) */ opacity: 1; -webkit-transition: opacity .2s; transition: opacity .2s; } .cd-schedule.modal-is-open .body-bg { opacity: 1; -webkit-transition: -webkit-transform .4s; transition: -webkit-transform .4s; transition: transform .4s; transition: transform .4s, -webkit-transform .4s; -webkit-transition-timing-function: cubic-bezier(0.5, 0, 0.1, 1); transition-timing-function: cubic-bezier(0.5, 0, 0.1, 1); } } .cd-schedule .cover-layer { /* layer between the content and the modal window */ position: fixed; z-index: 2; top: 0; left: 0; height: 100%; width: 100%; background: rgba(0, 0, 0, 0.8); opacity: 0; visibility: hidden; -webkit-transition: opacity .4s, visibility .4s; transition: opacity .4s, visibility .4s; } .cd-schedule.modal-is-open .cover-layer { opacity: 1; visibility: visible; }
JAVASCRIPT
jQuery(document).ready(function($){ var transitionEnd = 'webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend'; var transitionsSupported = ( $('.csstransitions').length > 0 ); //if browser does not support transitions - use a different event to trigger them if( !transitionsSupported ) transitionEnd = 'noTransition'; //should add a loding while the events are organized function SchedulePlan( element ) { this.element = element; this.timeline = this.element.find('.timeline'); this.timelineItems = this.timeline.find('li'); this.timelineItemsNumber = this.timelineItems.length; this.timelineStart = getScheduleTimestamp(this.timelineItems.eq(0).text()); //need to store delta (in our case half hour) timestamp this.timelineUnitDuration = getScheduleTimestamp(this.timelineItems.eq(1).text()) - getScheduleTimestamp(this.timelineItems.eq(0).text()); this.eventsWrapper = this.element.find('.events'); this.eventsGroup = this.eventsWrapper.find('.events-group'); this.singleEvents = this.eventsGroup.find('.single-event'); this.eventSlotHeight = this.eventsGroup.eq(0).children('.top-info').outerHeight(); this.modal = this.element.find('.event-modal'); this.modalHeader = this.modal.find('.header'); this.modalHeaderBg = this.modal.find('.header-bg'); this.modalBody = this.modal.find('.body'); this.modalBodyBg = this.modal.find('.body-bg'); this.modalMaxWidth = 800; this.modalMaxHeight = 480; this.animating = false; this.initSchedule(); } SchedulePlan.prototype.initSchedule = function() { this.scheduleReset(); this.initEvents(); }; SchedulePlan.prototype.scheduleReset = function() { var mq = this.mq(); if( mq == 'desktop' && !this.element.hasClass('js-full') ) { //in this case you are on a desktop version (first load or resize from mobile) this.eventSlotHeight = this.eventsGroup.eq(0).children('.top-info').outerHeight(); this.element.addClass('js-full'); this.placeEvents(); this.element.hasClass('modal-is-open') && this.checkEventModal(); } else if( mq == 'mobile' && this.element.hasClass('js-full') ) { //in this case you are on a mobile version (first load or resize from desktop) this.element.removeClass('js-full loading'); this.eventsGroup.children('ul').add(this.singleEvents).removeAttr('style'); this.eventsWrapper.children('.grid-line').remove(); this.element.hasClass('modal-is-open') && this.checkEventModal(); } else if( mq == 'desktop' && this.element.hasClass('modal-is-open')){ //on a mobile version with modal open - need to resize/move modal window this.checkEventModal('desktop'); this.element.removeClass('loading'); } else { this.element.removeClass('loading'); } }; SchedulePlan.prototype.initEvents = function() { var self = this; this.singleEvents.each(function(){ //create the .event-date element for each event var durationLabel = '<span class="event-date">'+$(this).data('start')+' - '+$(this).data('end')+'</span>'; $(this).children('a').prepend($(durationLabel)); //detect click on the event and open the modal $(this).on('click', 'a', function(event){ event.preventDefault(); if( !self.animating ) self.openModal($(this)); }); }); //close modal window this.modal.on('click', '.close', function(event){ event.preventDefault(); if( !self.animating ) self.closeModal(self.eventsGroup.find('.selected-event')); }); this.element.on('click', '.cover-layer', function(event){ if( !self.animating && self.element.hasClass('modal-is-open') ) self.closeModal(self.eventsGroup.find('.selected-event')); }); }; SchedulePlan.prototype.placeEvents = function() { var self = this; this.singleEvents.each(function(){ //place each event in the grid -> need to set top position and height var start = getScheduleTimestamp($(this).attr('data-start')), duration = getScheduleTimestamp($(this).attr('data-end')) - start; var eventTop = self.eventSlotHeight*(start - self.timelineStart)/self.timelineUnitDuration, eventHeight = self.eventSlotHeight*duration/self.timelineUnitDuration; $(this).css({ top: (eventTop -1) +'px', height: (eventHeight+1)+'px' }); }); this.element.removeClass('loading'); }; SchedulePlan.prototype.openModal = function(event) { var self = this; var mq = self.mq(); this.animating = true; //update event name and time this.modalHeader.find('.event-name').text(event.find('.event-name').text()); this.modalHeader.find('.event-date').text(event.find('.event-date').text()); this.modal.attr('data-event', event.parent().attr('data-event')); //update event content this.modalBody.find('.event-info').load(event.parent().attr('data-content')+'.html .event-info > *', function(data){ //once the event content has been loaded self.element.addClass('content-loaded'); }); this.element.addClass('modal-is-open'); setTimeout(function(){ //fixes a flash when an event is selected - desktop version only event.parent('li').addClass('selected-event'); }, 10); if( mq == 'mobile' ) { self.modal.one(transitionEnd, function(){ self.modal.off(transitionEnd); self.animating = false; }); } else { var eventTop = event.offset().top - $(window).scrollTop(), eventLeft = event.offset().left, eventHeight = event.innerHeight(), eventWidth = event.innerWidth(); var windowWidth = $(window).width(), windowHeight = $(window).height(); var modalWidth = ( windowWidth*.8 > self.modalMaxWidth ) ? self.modalMaxWidth : windowWidth*.8, modalHeight = ( windowHeight*.8 > self.modalMaxHeight ) ? self.modalMaxHeight : windowHeight*.8; var modalTranslateX = parseInt((windowWidth - modalWidth)/2 - eventLeft), modalTranslateY = parseInt((windowHeight - modalHeight)/2 - eventTop); var HeaderBgScaleY = modalHeight/eventHeight, BodyBgScaleX = (modalWidth - eventWidth); //change modal height/width and translate it self.modal.css({ top: eventTop+'px', left: eventLeft+'px', height: modalHeight+'px', width: modalWidth+'px', }); transformElement(self.modal, 'translateY('+modalTranslateY+'px) translateX('+modalTranslateX+'px)'); //set modalHeader width self.modalHeader.css({ width: eventWidth+'px', }); //set modalBody left margin self.modalBody.css({ marginLeft: eventWidth+'px', }); //change modalBodyBg height/width ans scale it self.modalBodyBg.css({ height: eventHeight+'px', width: '1px', }); transformElement(self.modalBodyBg, 'scaleY('+HeaderBgScaleY+') scaleX('+BodyBgScaleX+')'); //change modal modalHeaderBg height/width and scale it self.modalHeaderBg.css({ height: eventHeight+'px', width: eventWidth+'px', }); transformElement(self.modalHeaderBg, 'scaleY('+HeaderBgScaleY+')'); self.modalHeaderBg.one(transitionEnd, function(){ //wait for the end of the modalHeaderBg transformation and show the modal content self.modalHeaderBg.off(transitionEnd); self.animating = false; self.element.addClass('animation-completed'); }); } //if browser do not support transitions -> no need to wait for the end of it if( !transitionsSupported ) self.modal.add(self.modalHeaderBg).trigger(transitionEnd); }; SchedulePlan.prototype.closeModal = function(event) { var self = this; var mq = self.mq(); this.animating = true; if( mq == 'mobile' ) { this.element.removeClass('modal-is-open'); this.modal.one(transitionEnd, function(){ self.modal.off(transitionEnd); self.animating = false; self.element.removeClass('content-loaded'); event.removeClass('selected-event'); }); } else { var eventTop = event.offset().top - $(window).scrollTop(), eventLeft = event.offset().left, eventHeight = event.innerHeight(), eventWidth = event.innerWidth(); var modalTop = Number(self.modal.css('top').replace('px', '')), modalLeft = Number(self.modal.css('left').replace('px', '')); var modalTranslateX = eventLeft - modalLeft, modalTranslateY = eventTop - modalTop; self.element.removeClass('animation-completed modal-is-open'); //change modal width/height and translate it this.modal.css({ width: eventWidth+'px', height: eventHeight+'px' }); transformElement(self.modal, 'translateX('+modalTranslateX+'px) translateY('+modalTranslateY+'px)'); //scale down modalBodyBg element transformElement(self.modalBodyBg, 'scaleX(0) scaleY(1)'); //scale down modalHeaderBg element transformElement(self.modalHeaderBg, 'scaleY(1)'); this.modalHeaderBg.one(transitionEnd, function(){ //wait for the end of the modalHeaderBg transformation and reset modal style self.modalHeaderBg.off(transitionEnd); self.modal.addClass('no-transition'); setTimeout(function(){ self.modal.add(self.modalHeader).add(self.modalBody).add(self.modalHeaderBg).add(self.modalBodyBg).attr('style', ''); }, 10); setTimeout(function(){ self.modal.removeClass('no-transition'); }, 20); self.animating = false; self.element.removeClass('content-loaded'); event.removeClass('selected-event'); }); } //browser do not support transitions -> no need to wait for the end of it if( !transitionsSupported ) self.modal.add(self.modalHeaderBg).trigger(transitionEnd); } SchedulePlan.prototype.mq = function(){ //get MQ value ('desktop' or 'mobile') var self = this; return window.getComputedStyle(this.element.get(0), '::before').getPropertyValue('content').replace(/["']/g, ''); }; SchedulePlan.prototype.checkEventModal = function(device) { this.animating = true; var self = this; var mq = this.mq(); if( mq == 'mobile' ) { //reset modal style on mobile self.modal.add(self.modalHeader).add(self.modalHeaderBg).add(self.modalBody).add(self.modalBodyBg).attr('style', ''); self.modal.removeClass('no-transition'); self.animating = false; } else if( mq == 'desktop' && self.element.hasClass('modal-is-open') ) { self.modal.addClass('no-transition'); self.element.addClass('animation-completed'); var event = self.eventsGroup.find('.selected-event'); var eventTop = event.offset().top - $(window).scrollTop(), eventLeft = event.offset().left, eventHeight = event.innerHeight(), eventWidth = event.innerWidth(); var windowWidth = $(window).width(), windowHeight = $(window).height(); var modalWidth = ( windowWidth*.8 > self.modalMaxWidth ) ? self.modalMaxWidth : windowWidth*.8, modalHeight = ( windowHeight*.8 > self.modalMaxHeight ) ? self.modalMaxHeight : windowHeight*.8; var HeaderBgScaleY = modalHeight/eventHeight, BodyBgScaleX = (modalWidth - eventWidth); setTimeout(function(){ self.modal.css({ width: modalWidth+'px', height: modalHeight+'px', top: (windowHeight/2 - modalHeight/2)+'px', left: (windowWidth/2 - modalWidth/2)+'px', }); transformElement(self.modal, 'translateY(0) translateX(0)'); //change modal modalBodyBg height/width self.modalBodyBg.css({ height: modalHeight+'px', width: '1px', }); transformElement(self.modalBodyBg, 'scaleX('+BodyBgScaleX+')'); //set modalHeader width self.modalHeader.css({ width: eventWidth+'px', }); //set modalBody left margin self.modalBody.css({ marginLeft: eventWidth+'px', }); //change modal modalHeaderBg height/width and scale it self.modalHeaderBg.css({ height: eventHeight+'px', width: eventWidth+'px', }); transformElement(self.modalHeaderBg, 'scaleY('+HeaderBgScaleY+')'); }, 10); setTimeout(function(){ self.modal.removeClass('no-transition'); self.animating = false; }, 20); } }; var schedules = $('.cd-schedule'); var objSchedulesPlan = [], windowResize = false; if( schedules.length > 0 ) { schedules.each(function(){ //create SchedulePlan objects objSchedulesPlan.push(new SchedulePlan($(this))); }); } $(window).on('resize', function(){ if( !windowResize ) { windowResize = true; (!window.requestAnimationFrame) ? setTimeout(checkResize) : window.requestAnimationFrame(checkResize); } }); $(window).keyup(function(event) { if (event.keyCode == 27) { objSchedulesPlan.forEach(function(element){ element.closeModal(element.eventsGroup.find('.selected-event')); }); } }); function checkResize(){ objSchedulesPlan.forEach(function(element){ element.scheduleReset(); }); windowResize = false; } function getScheduleTimestamp(time) { //accepts hh:mm format - convert hh:mm to timestamp time = time.replace(/ /g,''); var timeArray = time.split(':'); var timeStamp = parseInt(timeArray[0])*60 + parseInt(timeArray[1]); return timeStamp; } function transformElement(element, value) { element.css({ '-moz-transform': value, '-webkit-transform': value, '-ms-transform': value, '-o-transform': value, 'transform': value }); } });
Expand for more options Login