Grille des programmes de Riff en HTML CSS JS.

main.js 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. (function() {
  2. // Schedule Template - by CodyHouse.co
  3. function ScheduleTemplate( element ) {
  4. this.element = element;
  5. this.timelineItems = this.element.getElementsByClassName('cd-schedule__timeline')[0].getElementsByTagName('li');
  6. this.timelineStart = getScheduleTimestamp(this.timelineItems[0].textContent);
  7. this.timelineUnitDuration = getScheduleTimestamp(this.timelineItems[1].textContent) - getScheduleTimestamp(this.timelineItems[0].textContent);
  8. this.topInfoElement = this.element.getElementsByClassName('cd-schedule__top-info')[0];
  9. this.singleEvents = this.element.getElementsByClassName('cd-schedule__event');
  10. this.modal = this.element.getElementsByClassName('cd-schedule-modal')[0];
  11. this.modalHeader = this.element.getElementsByClassName('cd-schedule-modal__header')[0];
  12. this.modalHeaderBg = this.element.getElementsByClassName('cd-schedule-modal__header-bg')[0];
  13. this.modalBody = this.element.getElementsByClassName('cd-schedule-modal__body')[0];
  14. this.modalBodyBg = this.element.getElementsByClassName('cd-schedule-modal__body-bg')[0];
  15. this.modalClose = this.modal.getElementsByClassName('cd-schedule-modal__close')[0];
  16. this.modalDate = this.modal.getElementsByClassName('cd-schedule-modal__date')[0];
  17. this.modalEventName = this.modal.getElementsByClassName('cd-schedule-modal__name')[0];
  18. this.coverLayer = this.element.getElementsByClassName('cd-schedule__cover-layer')[0];
  19. this.modalMaxWidth = 800;
  20. this.modalMaxHeight = 480;
  21. this.animating = false;
  22. this.supportAnimation = Util.cssSupports('transition');
  23. this.initSchedule();
  24. };
  25. ScheduleTemplate.prototype.initSchedule = function() {
  26. this.scheduleReset();
  27. this.initEvents();
  28. };
  29. ScheduleTemplate.prototype.scheduleReset = function() {
  30. // according to the mq value, init the style of the template
  31. var mq = this.mq(),
  32. loaded = Util.hasClass(this.element, 'js-schedule-loaded'),
  33. modalOpen = Util.hasClass(this.modal, 'cd-schedule-modal--open');
  34. if( mq == 'desktop' && !loaded ) {
  35. Util.addClass(this.element, 'js-schedule-loaded');
  36. this.placeEvents();
  37. modalOpen && this.checkEventModal(modalOpen);
  38. } else if( mq == 'mobile' && loaded) {
  39. //in this case you are on a mobile version (first load or resize from desktop)
  40. Util.removeClass(this.element, 'cd-schedule--loading js-schedule-loaded');
  41. this.resetEventsStyle();
  42. modalOpen && this.checkEventModal();
  43. } else if( mq == 'desktop' && modalOpen ) {
  44. //on a mobile version with modal open - need to resize/move modal window
  45. this.checkEventModal(modalOpen);
  46. Util.removeClass(this.element, 'cd-schedule--loading');
  47. } else {
  48. Util.removeClass(this.element, 'cd-schedule--loading');
  49. }
  50. };
  51. ScheduleTemplate.prototype.resetEventsStyle = function() {
  52. // remove js style applied to the single events
  53. for(var i = 0; i < this.singleEvents.length; i++) {
  54. this.singleEvents[i].removeAttribute('style');
  55. }
  56. };
  57. ScheduleTemplate.prototype.placeEvents = function() {
  58. // on big devices - place events in the template according to their time/day
  59. var self = this,
  60. slotHeight = this.topInfoElement.offsetHeight;
  61. for(var i = 0; i < this.singleEvents.length; i++) {
  62. var anchor = this.singleEvents[i].getElementsByTagName('a')[0];
  63. var start = getScheduleTimestamp(anchor.getAttribute('data-start')),
  64. duration = getScheduleTimestamp(anchor.getAttribute('data-end')) - start;
  65. var eventTop = slotHeight*(start - self.timelineStart)/self.timelineUnitDuration,
  66. eventHeight = slotHeight*duration/self.timelineUnitDuration;
  67. this.singleEvents[i].setAttribute('style', 'top: '+(eventTop-1)+'px; height: '+(eventHeight +1)+'px');
  68. }
  69. Util.removeClass(this.element, 'cd-schedule--loading');
  70. };
  71. ScheduleTemplate.prototype.initEvents = function() {
  72. var self = this;
  73. for(var i = 0; i < this.singleEvents.length; i++) {
  74. // open modal when user selects an event
  75. this.singleEvents[i].addEventListener('click', function(event){
  76. event.preventDefault();
  77. if(!self.animating) self.openModal(this.getElementsByTagName('a')[0]);
  78. });
  79. }
  80. //close modal window
  81. this.modalClose.addEventListener('click', function(event){
  82. event.preventDefault();
  83. if( !self.animating ) self.closeModal();
  84. });
  85. this.coverLayer.addEventListener('click', function(event){
  86. event.preventDefault();
  87. if( !self.animating ) self.closeModal();
  88. });
  89. };
  90. ScheduleTemplate.prototype.openModal = function(target) {
  91. var self = this;
  92. var mq = self.mq();
  93. this.animating = true;
  94. //update event name and time
  95. this.modalEventName.textContent = target.getElementsByTagName('em')[0].textContent;
  96. this.modalDate.textContent = target.getAttribute('data-start')+' - '+target.getAttribute('data-end');
  97. this.modal.setAttribute('data-event', target.getAttribute('data-event'));
  98. //update event content
  99. this.loadEventContent(target.getAttribute('data-content'));
  100. Util.addClass(this.modal, 'cd-schedule-modal--open');
  101. setTimeout(function(){
  102. //fixes a flash when an event is selected - desktop version only
  103. Util.addClass(target.closest('li'), 'cd-schedule__event--selected');
  104. }, 10);
  105. if( mq == 'mobile' ) {
  106. self.modal.addEventListener('transitionend', function cb(){
  107. self.animating = false;
  108. self.modal.removeEventListener('transitionend', cb);
  109. });
  110. } else {
  111. var eventPosition = target.getBoundingClientRect(),
  112. eventTop = eventPosition.top,
  113. eventLeft = eventPosition.left,
  114. eventHeight = target.offsetHeight,
  115. eventWidth = target.offsetWidth;
  116. var windowWidth = window.innerWidth,
  117. windowHeight = window.innerHeight;
  118. var modalWidth = ( windowWidth*.8 > self.modalMaxWidth ) ? self.modalMaxWidth : windowWidth*.8,
  119. modalHeight = ( windowHeight*.8 > self.modalMaxHeight ) ? self.modalMaxHeight : windowHeight*.8;
  120. var modalTranslateX = parseInt((windowWidth - modalWidth)/2 - eventLeft),
  121. modalTranslateY = parseInt((windowHeight - modalHeight)/2 - eventTop);
  122. var HeaderBgScaleY = modalHeight/eventHeight,
  123. BodyBgScaleX = (modalWidth - eventWidth);
  124. //change modal height/width and translate it
  125. self.modal.setAttribute('style', 'top:'+eventTop+'px;left:'+eventLeft+'px;height:'+modalHeight+'px;width:'+modalWidth+'px;transform: translateY('+modalTranslateY+'px) translateX('+modalTranslateX+'px)');
  126. //set modalHeader width
  127. self.modalHeader.setAttribute('style', 'width:'+eventWidth+'px');
  128. //set modalBody left margin
  129. self.modalBody.setAttribute('style', 'margin-left:'+eventWidth+'px');
  130. //change modalBodyBg height/width ans scale it
  131. self.modalBodyBg.setAttribute('style', 'height:'+eventHeight+'px; width: 1px; transform: scaleY('+HeaderBgScaleY+') scaleX('+BodyBgScaleX+')');
  132. //change modal modalHeaderBg height/width and scale it
  133. self.modalHeaderBg.setAttribute('style', 'height: '+eventHeight+'px; width: '+eventWidth+'px; transform: scaleY('+HeaderBgScaleY+')');
  134. self.modalHeaderBg.addEventListener('transitionend', function cb(){
  135. //wait for the end of the modalHeaderBg transformation and show the modal content
  136. self.animating = false;
  137. Util.addClass(self.modal, 'cd-schedule-modal--animation-completed');
  138. self.modalHeaderBg.removeEventListener('transitionend', cb);
  139. });
  140. }
  141. //if browser do not support transitions -> no need to wait for the end of it
  142. this.animationFallback();
  143. };
  144. ScheduleTemplate.prototype.closeModal = function() {
  145. var self = this;
  146. var mq = self.mq();
  147. var item = self.element.getElementsByClassName('cd-schedule__event--selected')[0],
  148. target = item.getElementsByTagName('a')[0];
  149. this.animating = true;
  150. if( mq == 'mobile' ) {
  151. Util.removeClass(this.modal, 'cd-schedule-modal--open');
  152. self.modal.addEventListener('transitionend', function cb(){
  153. Util.removeClass(self.modal, 'cd-schedule-modal--content-loaded');
  154. Util.removeClass(item, 'cd-schedule__event--selected');
  155. self.animating = false;
  156. self.modal.removeEventListener('transitionend', cb);
  157. });
  158. } else {
  159. var eventPosition = target.getBoundingClientRect(),
  160. eventTop = eventPosition.top,
  161. eventLeft = eventPosition.left,
  162. eventHeight = target.offsetHeight,
  163. eventWidth = target.offsetWidth;
  164. var modalStyle = window.getComputedStyle(self.modal),
  165. modalTop = Number(modalStyle.getPropertyValue('top').replace('px', '')),
  166. modalLeft = Number(modalStyle.getPropertyValue('left').replace('px', ''));
  167. var modalTranslateX = eventLeft - modalLeft,
  168. modalTranslateY = eventTop - modalTop;
  169. Util.removeClass(this.modal, 'cd-schedule-modal--open cd-schedule-modal--animation-completed');
  170. //change modal width/height and translate it
  171. self.modal.style.width = eventWidth+'px';self.modal.style.height = eventHeight+'px';self.modal.style.transform = 'translateX('+modalTranslateX+'px) translateY('+modalTranslateY+'px)';
  172. //scale down modalBodyBg element
  173. self.modalBodyBg.style.transform = 'scaleX(0) scaleY(1)';
  174. //scale down modalHeaderBg element
  175. // self.modalHeaderBg.setAttribute('style', 'transform: scaleY(1)');
  176. self.modalHeaderBg.style.transform = 'scaleY(1)';
  177. self.modalHeaderBg.addEventListener('transitionend', function cb(){
  178. //wait for the end of the modalHeaderBg transformation and reset modal style
  179. Util.addClass(self.modal, 'cd-schedule-modal--no-transition');
  180. setTimeout(function(){
  181. self.modal.removeAttribute('style');
  182. self.modalBody.removeAttribute('style');
  183. self.modalHeader.removeAttribute('style');
  184. self.modalHeaderBg.removeAttribute('style');
  185. self.modalBodyBg.removeAttribute('style');
  186. }, 10);
  187. setTimeout(function(){
  188. Util.removeClass(self.modal, 'cd-schedule-modal--no-transition');
  189. }, 20);
  190. self.animating = false;
  191. Util.removeClass(self.modal, 'cd-schedule-modal--content-loaded');
  192. Util.removeClass(item, 'cd-schedule__event--selected');
  193. self.modalHeaderBg.removeEventListener('transitionend', cb);
  194. });
  195. }
  196. //if browser do not support transitions -> no need to wait for the end of it
  197. this.animationFallback();
  198. };
  199. ScheduleTemplate.prototype.checkEventModal = function(modalOpen) {
  200. // this function is used on resize to reset events/modal style
  201. this.animating = true;
  202. var self = this;
  203. var mq = this.mq();
  204. if( mq == 'mobile' ) {
  205. //reset modal style on mobile
  206. self.modal.removeAttribute('style');
  207. self.modalBody.removeAttribute('style');
  208. self.modalHeader.removeAttribute('style');
  209. self.modalHeaderBg.removeAttribute('style');
  210. self.modalBodyBg.removeAttribute('style');
  211. Util.removeClass(self.modal, 'cd-schedule-modal--no-transition');
  212. self.animating = false;
  213. } else if( mq == 'desktop' && modalOpen) {
  214. Util.addClass(self.modal, 'cd-schedule-modal--no-transition cd-schedule-modal--animation-completed');
  215. var item = self.element.getElementsByClassName('cd-schedule__event--selected')[0],
  216. target = item.getElementsByTagName('a')[0];
  217. var eventPosition = target.getBoundingClientRect(),
  218. eventTop = eventPosition.top,
  219. eventLeft = eventPosition.left,
  220. eventHeight = target.offsetHeight,
  221. eventWidth = target.offsetWidth;
  222. var windowWidth = window.innerWidth,
  223. windowHeight = window.innerHeight;
  224. var modalWidth = ( windowWidth*.8 > self.modalMaxWidth ) ? self.modalMaxWidth : windowWidth*.8,
  225. modalHeight = ( windowHeight*.8 > self.modalMaxHeight ) ? self.modalMaxHeight : windowHeight*.8;
  226. var HeaderBgScaleY = modalHeight/eventHeight,
  227. BodyBgScaleX = (modalWidth - eventWidth);
  228. setTimeout(function(){
  229. self.modal.setAttribute('style', 'top:'+(windowHeight/2 - modalHeight/2)+'px;left:'+(windowWidth/2 - modalWidth/2)+'px;height:'+modalHeight+'px;width:'+modalWidth+'px;transform: translateY(0) translateX(0)');
  230. //change modal modalBodyBg height/width
  231. self.modalBodyBg.style.height = modalHeight+'px';self.modalBodyBg.style.transform = 'scaleY(1) scaleX('+BodyBgScaleX+')';self.modalBodyBg.style.width = '1px';
  232. //set modalHeader width
  233. self.modalHeader.setAttribute('style', 'width:'+eventWidth+'px');
  234. //set modalBody left margin
  235. self.modalBody.setAttribute('style', 'margin-left:'+eventWidth+'px');
  236. //change modal modalHeaderBg height/width and scale it
  237. self.modalHeaderBg.setAttribute('style', 'height: '+eventHeight+'px;width:'+eventWidth+'px; transform:scaleY('+HeaderBgScaleY+');');
  238. }, 10);
  239. setTimeout(function(){
  240. Util.removeClass(self.modal, 'cd-schedule-modal--no-transition');
  241. self.animating = false;
  242. }, 20);
  243. }
  244. };
  245. ScheduleTemplate.prototype.loadEventContent = function(content) {
  246. // load the content of an event when user selects it
  247. var self = this;
  248. httpRequest = new XMLHttpRequest();
  249. httpRequest.onreadystatechange = function() {
  250. if (httpRequest.readyState === XMLHttpRequest.DONE) {
  251. if (httpRequest.status === 200) {
  252. self.modal.getElementsByClassName('cd-schedule-modal__event-info')[0].innerHTML = self.getEventContent(httpRequest.responseText);
  253. Util.addClass(self.modal, 'cd-schedule-modal--content-loaded');
  254. }
  255. }
  256. };
  257. httpRequest.open('GET', content+'.html');
  258. httpRequest.send();
  259. };
  260. ScheduleTemplate.prototype.getEventContent = function(string) {
  261. // reset the loaded event content so that it can be inserted in the modal
  262. var div = document.createElement('div');
  263. div.innerHTML = string.trim();
  264. return div.getElementsByClassName('cd-schedule-modal__event-info')[0].innerHTML;
  265. };
  266. ScheduleTemplate.prototype.animationFallback = function() {
  267. if( !this.supportAnimation ) { // fallback for browsers not supporting transitions
  268. var event = new CustomEvent('transitionend');
  269. self.modal.dispatchEvent(event);
  270. self.modalHeaderBg.dispatchEvent(event);
  271. }
  272. };
  273. ScheduleTemplate.prototype.mq = function(){
  274. //get MQ value ('desktop' or 'mobile')
  275. var self = this;
  276. return window.getComputedStyle(this.element, '::before').getPropertyValue('content').replace(/'|"/g, "");
  277. };
  278. function getScheduleTimestamp(time) {
  279. //accepts hh:mm format - convert hh:mm to timestamp
  280. time = time.replace(/ /g,'');
  281. var timeArray = time.split(':');
  282. var timeStamp = parseInt(timeArray[0])*60 + parseInt(timeArray[1]);
  283. return timeStamp;
  284. };
  285. var scheduleTemplate = document.getElementsByClassName('js-cd-schedule'),
  286. scheduleTemplateArray = [],
  287. resizing = false;
  288. if( scheduleTemplate.length > 0 ) { // init ScheduleTemplate objects
  289. for( var i = 0; i < scheduleTemplate.length; i++) {
  290. (function(i){
  291. scheduleTemplateArray.push(new ScheduleTemplate(scheduleTemplate[i]));
  292. })(i);
  293. }
  294. window.addEventListener('resize', function(event) {
  295. // on resize - update events position and modal position (if open)
  296. if( !resizing ) {
  297. resizing = true;
  298. (!window.requestAnimationFrame) ? setTimeout(checkResize, 250) : window.requestAnimationFrame(checkResize);
  299. }
  300. });
  301. window.addEventListener('keyup', function(event){
  302. // close event modal when pressing escape key
  303. if( event.keyCode && event.keyCode == 27 || event.key && event.key.toLowerCase() == 'escape' ) {
  304. for(var i = 0; i < scheduleTemplateArray.length; i++) {
  305. scheduleTemplateArray[i].closeModal();
  306. }
  307. }
  308. });
  309. function checkResize(){
  310. for(var i = 0; i < scheduleTemplateArray.length; i++) {
  311. scheduleTemplateArray[i].scheduleReset();
  312. }
  313. resizing = false;
  314. };
  315. }
  316. }());