diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 54c752a0..fc21cd27 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -12,6 +12,7 @@ // //= require jquery //= require jquery_ujs +//= require jquery.mobile.custom.min //= require waypoints/jquery.waypoints //= require dataTables/jquery.dataTables //= require dataTables/bootstrap/3/jquery.dataTables.bootstrap diff --git a/app/assets/javascripts/jquery.mobile.custom.min.js b/app/assets/javascripts/jquery.mobile.custom.min.js new file mode 100644 index 00000000..058c3453 --- /dev/null +++ b/app/assets/javascripts/jquery.mobile.custom.min.js @@ -0,0 +1,3 @@ +/*! jQuery Mobile v1.4.5 | Copyright 2010, 2014 jQuery Foundation, Inc. | jquery.org/license */ + +(function(e,t,n){typeof define=="function"&&define.amd?define(["jquery"],function(r){return n(r,e,t),r.mobile}):n(e.jQuery,e,t)})(this,document,function(e,t,n,r){(function(e,t,n,r){function T(e){while(e&&typeof e.originalEvent!="undefined")e=e.originalEvent;return e}function N(t,n){var i=t.type,s,o,a,l,c,h,p,d,v;t=e.Event(t),t.type=n,s=t.originalEvent,o=e.event.props,i.search(/^(mouse|click)/)>-1&&(o=f);if(s)for(p=o.length,l;p;)l=o[--p],t[l]=s[l];i.search(/mouse(down|up)|click/)>-1&&!t.which&&(t.which=1);if(i.search(/^touch/)!==-1){a=T(s),i=a.touches,c=a.changedTouches,h=i&&i.length?i[0]:c&&c.length?c[0]:r;if(h)for(d=0,v=u.length;di||Math.abs(n.pageY-p)>i,d&&!r&&P("vmousecancel",t,s),P("vmousemove",t,s),_()}function I(e){if(g)return;A();var t=C(e.target),n,r;P("vmouseup",e,t),d||(n=P("vclick",e,t),n&&n.isDefaultPrevented()&&(r=T(e).changedTouches[0],v.push({touchID:E,x:r.clientX,y:r.clientY}),m=!0)),P("vmouseout",e,t),d=!1,_()}function q(t){var n=e.data(t,i),r;if(n)for(r in n)if(n[r])return!0;return!1}function R(){}function U(t){var n=t.substr(1);return{setup:function(){q(this)||e.data(this,i,{});var r=e.data(this,i);r[t]=!0,l[t]=(l[t]||0)+1,l[t]===1&&b.bind(n,H),e(this).bind(n,R),y&&(l.touchstart=(l.touchstart||0)+1,l.touchstart===1&&b.bind("touchstart",B).bind("touchend",I).bind("touchmove",F).bind("scroll",j))},teardown:function(){--l[t],l[t]||b.unbind(n,H),y&&(--l.touchstart,l.touchstart||b.unbind("touchstart",B).unbind("touchmove",F).unbind("touchend",I).unbind("scroll",j));var r=e(this),s=e.data(this,i);s&&(s[t]=!1),r.unbind(n,R),q(this)||r.removeData(i)}}}var i="virtualMouseBindings",s="virtualTouchID",o="vmouseover vmousedown vmousemove vmouseup vclick vmouseout vmousecancel".split(" "),u="clientX clientY pageX pageY screenX screenY".split(" "),a=e.event.mouseHooks?e.event.mouseHooks.props:[],f=e.event.props.concat(a),l={},c=0,h=0,p=0,d=!1,v=[],m=!1,g=!1,y="addEventListener"in n,b=e(n),w=1,E=0,S,x;e.vmouse={moveDistanceThreshold:10,clickDistanceThreshold:10,resetTimerDuration:1500};for(x=0;xMath.floor(e.pageY)||e.pageX===0&&Math.floor(i)>Math.floor(e.pageX))i-=n,s-=r;else if(se.event.special.swipe.horizontalDistanceThreshold&&Math.abs(t.coords[1]-n.coords[1])n.coords[0]?"swipeleft":"swiperight";return l(r,"swipe",e.Event("swipe",{target:i,swipestart:t,swipestop:n}),!0),l(r,s,e.Event(s,{target:i,swipestart:t,swipestop:n}),!0),!0}return!1},eventInProgress:!1,setup:function(){var t,n=this,r=e(n),s={};t=e.data(this,"mobile-events"),t||(t={length:0},e.data(this,"mobile-events",t)),t.length++,t.swipe=s,s.start=function(t){if(e.event.special.swipe.eventInProgress)return;e.event.special.swipe.eventInProgress=!0;var r,o=e.event.special.swipe.start(t),u=t.target,l=!1;s.move=function(t){if(!o||t.isDefaultPrevented())return;r=e.event.special.swipe.stop(t),l||(l=e.event.special.swipe.handleSwipe(o,r,n,u),l&&(e.event.special.swipe.eventInProgress=!1)),Math.abs(o.coords[0]-r.coords[0])>e.event.special.swipe.scrollSupressionThreshold&&t.preventDefault()},s.stop=function(){l=!0,e.event.special.swipe.eventInProgress=!1,i.off(f,s.move),s.move=null},i.on(f,s.move).one(a,s.stop)},r.on(u,s.start)},teardown:function(){var t,n;t=e.data(this,"mobile-events"),t&&(n=t.swipe,delete t.swipe,t.length--,t.length===0&&e.removeData(this,"mobile-events")),n&&(n.start&&e(this).off(u,n.start),n.move&&i.off(f,n.move),n.stop&&i.off(a,n.stop))}},e.each({scrollstop:"scrollstart",taphold:"tap",swipeleft:"swipe.left",swiperight:"swipe.right"},function(t,n){e.event.special[t]={setup:function(){e(this).bind(n,e.noop)},teardown:function(){e(this).unbind(n)}}})}(e,this)}); \ No newline at end of file diff --git a/app/assets/stylesheets/osem-schedule.css.scss b/app/assets/stylesheets/osem-schedule.css.scss index 80511f33..cc48da5d 100644 --- a/app/assets/stylesheets/osem-schedule.css.scss +++ b/app/assets/stylesheets/osem-schedule.css.scss @@ -13,6 +13,9 @@ background-image: -webkit-gradient( color-stop(0.97, rgb(194,232,190)), color-stop(1, rgb(194,232,190)) ); + +cursor: pointer; +padding: 3px 2px; } #schedule td.event:hover @@ -75,3 +78,129 @@ a.unstyled-link { background-image: linear-gradient(to top, #f7faf2 24%, #53ab4a 97%, #53ab4a 100%); } + +.schedule-dropdown{ + margin-left: 20%; + margin-right: 20%; + margin-top: 20px; + margin-bottom: 20px; +} + +.schedule-dropdown > button{ + width: 100%; +} + +.schedule-dropdown > .dropdown-menu{ + width: 100%; +} + +.elipsis{ + text-overflow: ellipsis; + display: -webkit-box; + -webkit-box-orient: vertical; +} + +.break-words{ + /* These are technically the same, but use both */ + overflow-wrap: break-word; + word-wrap: break-word; + + /* Warning: Needed for oldIE support, but words are broken up letter-by-letter */ + -ms-word-break: break-all; + word-break: break-all; + + /* Non standard for webkit */ + word-break: break-word; + + -ms-hyphens: auto; + -moz-hyphens: auto; + -webkit-hyphens: auto; + hyphens: auto; +} + +.room, .event-title{ + font-size: 11px; + line-height: 17px; + overflow: hidden; +} + +td.room{ + width: 15%; + padding: 3px !important; + max-width:15%; +} + +th.date{ + font-size: 12px; + padding: 8px 0px 8px 1% !important; +} + +.speaker-pic{ + padding: 4px 2px; +} + +.schedule-table{ + width: 100%; + margin:0px; + padding:0px; +} + +td.no-padding{ + padding:0px !important; +} + +/* Small devices (tablets, 768px and up) */ +@media (min-width: 768px) { + .room, .event-title{ + font-size: 12px; + } + + td.room{ + width: 12%; + max-width:12%; + } + + th.date{ + font-size: 13px; + } + + td.event{ + padding: 3px 4px !important; + } +} + +/* Medium devices (desktops, 992px and up) */ +@media (min-width: 992px) { + .room, .event-title{ + font-size: 13px; + } + + td.room{ + width: 10%; + max-width: 10%; + } + + td.event{ + padding: 3px 5px !important; + } +} + +/* Large devices (large desktops, 1200px and up) */ +@media (min-width: 1200px) { + .room, .event-title{ + font-size: 14px; + } + + td.room{ + width: 9%; + max-width: 9%; + } + + td.event{ + padding: 3px 6px !important; + } + + th.date{ + font-size: 14px; + } +} diff --git a/app/controllers/conference_controller.rb b/app/controllers/conference_controller.rb index 25d55175..da98b944 100644 --- a/app/controllers/conference_controller.rb +++ b/app/controllers/conference_controller.rb @@ -16,6 +16,10 @@ class ConferenceController < ApplicationController @events = @conference.program.events @events_xml = @events.scheduled.order(start_time: :asc).group_by{ |event| event.start_time.to_date } @dates = @conference.start_date..@conference.end_date + @step_minutes = EventType::LENGTH_STEP.minutes + @conf_start = 9 + conf_end = 20 + @conf_period = conf_end - @conf_start if @dates == Date.current @today = Date.current.strftime('%Y-%m-%d') diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index ad88edec..5004347c 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -298,4 +298,41 @@ module ApplicationHelper yiq = ((r * 299) + (g * 587) + (b * 114)) / 1000 (yiq >= 128) ? 'black' : 'white' end + + def td_height(rooms) + td_height = 500 / rooms.length + # we want all least 3 lines in events and td padding = 3px, speaker picture height >= 25px + # and line-height = 17px => (17 * 3) + 6 + 25 = 82 + td_height < 82 ? 82 : td_height + end + + def room_height(rooms) + room_lines(rooms) * 17 + end + + def room_lines(rooms) + # line-height = 17px, td padding = 3px + (td_height(rooms) - 6) / 17 + end + + def event_height(rooms) + event_lines(rooms) * 17 + end + + def event_lines(rooms) + # line-height = 17px, td padding = 3px, speaker picture height >= 25px + (td_height(rooms) - 31) / 17 + end + + def speaker_height(rooms) + # td padding = 3px + speaker_height = td_height(rooms) - 6 - event_height(rooms) + # The speaker picture is a circle and the width must be <= 37 to avoid making the cell widther + speaker_height >= 37 ? 37 : speaker_height + end + + def speaker_width(rooms) + # speaker picture padding: 4px 2px; and we want the picture to be a circle + speaker_height(rooms) - 4 + end end diff --git a/app/models/event.rb b/app/models/event.rb index 1e118095..fe605131 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -223,6 +223,13 @@ class Event < ActiveRecord::Base (100 * result.values.count(true) / result.values.compact.count).to_s end + ## + # Returns end of the event + # + def end_time + self.start_time + self.event_type.length.minutes + end + private ## diff --git a/app/models/event_type.rb b/app/models/event_type.rb index 9a5c4c84..96d988cc 100644 --- a/app/models/event_type.rb +++ b/app/models/event_type.rb @@ -10,6 +10,7 @@ class EventType < ActiveRecord::Base alias_attribute :name, :title + # If LENGTH_STEP must be divisor of 60, otherwise the schedule wont be displayed properly LENGTH_STEP = 15 private diff --git a/app/views/conference/_carousel.html.erb b/app/views/conference/_carousel.html.erb new file mode 100644 index 00000000..fbdc8fa1 --- /dev/null +++ b/app/views/conference/_carousel.html.erb @@ -0,0 +1,63 @@ +<% intervals = number_columns * 60 / EventType::LENGTH_STEP + 1 %> +<% width = 85 / intervals %> +<% carousel_number = (@conf_period / number_columns.to_f).ceil %> + diff --git a/app/views/conference/_schedule.html.erb b/app/views/conference/_schedule.html.erb index 1d036378..991529cc 100644 --- a/app/views/conference/_schedule.html.erb +++ b/app/views/conference/_schedule.html.erb @@ -1,88 +1,82 @@
-

Schedule for <%= @conference.title %>

-
<%= javascript_tag do %> +// change of active tab and the button title when a date clicked $(function() { - // add a hash to the URL when the user clicks on a tab - $('a[data-toggle="tab"]').on('click', function(e) { - history.pushState(null, null, $(this).attr('href')); - }); - // navigate to a tab when the history changes - window.addEventListener("popstate", function(e) { - var activeTab = $('[href=' + location.hash + ']'); - if (activeTab.length) { - activeTab.tab('show'); - } else { - $('.nav-tabs a[href=#<%= j @today %>]').tab('show'); - } + $('.date-tab').on('click', function(e) { + $('.li-dropdown-schedule').removeClass('active'); + $('.schedule-dropdown').find('button').text($(this).text()); }); }); +// use the date tag in the url to select a tab and the title of the button $(function() { var hash = window.location.hash; - hash && $('ul.nav a[href="' + hash + '"]').tab('show'); + if(hash && !(hash === '#schedule')){ + hash && $('ul a[href="' + hash + '"]').tab('show'); + $('button.dropdown-toggle').text(hash.substr(1)); + } }); +// hide the right and left controls when neccesary after moving the carousel +$('.carousel').on('slid.bs.carousel', '', +function(){ + $(this).children('.left.carousel-control').show(); + $(this).children('.right.carousel-control').show(); + if($(this).find('.first').hasClass('active')) { + $(this).children('.left.carousel-control').hide(); + } else if($(this).find('.last').hasClass('active')) { + $(this).children('.right.carousel-control').hide(); + } +}); + +$(document).ready(function(){ + // hide the left control when the page is ready + $('.carousel').children('.left.carousel-control').hide(); + + // carousel swipe + $(".carousel-inner").swiperight(function() { + $(this).parent().carousel('prev'); + }); + $(".carousel-inner").swipeleft(function() { + $(this).parent().carousel('next'); + }); +}); <% end %> diff --git a/app/views/conference/_schedule_item.html.haml b/app/views/conference/_schedule_item.html.haml index 743095ca..d4d34ac7 100644 --- a/app/views/conference/_schedule_item.html.haml +++ b/app/views/conference/_schedule_item.html.haml @@ -1,24 +1,12 @@ -%td.event{ style: "width: #{95 / @rooms.length}%; cursor:pointer", | - rowspan: span[room.id], | +%td.event{ width: "#{ width * span }%" , | + colspan: span, | role: "button" } | - %a.unstyled-link{href: url_for(conference_program_proposal_path(@conference.short_title, event[0].id))} - - if speaker = event[0].speakers.first - = image_tag speaker.gravatar_url, :class => "img-circle pull-right", | - :alt => speaker.name, | - :title => speaker.name, | - :style => "padding:8px;" | - %h5 - %span.fa.fa-comment - = event[0].title - - unless event[0].subtitle.blank? - %span.schedule-subtitle - = event[0].subtitle - %span.schedule-speaker - %span.fa.fa-user - = speaker.name - - if event[0].track - %span.schedule-track - %span.fa.fa-road - %span.label{ style: "background-color: #{event[0].track.color}" } - = event[0].track.name - + %a.unstyled-link{href: url_for(conference_program_proposal_path(@conference.short_title, event.id))} + %div{ class: "elipsis break-words event-title", | + style: "-webkit-line-clamp: #{ event_lines(@rooms) }; height: #{ event_height(@rooms) }px;"} | + = event.title + - if speaker = event.speakers.first + = image_tag speaker.gravatar_url, :class => "img-circle pull-right speaker-pic", | + :alt => speaker.name, | + :title => speaker.name, | + :style => "height: #{ speaker_height(@rooms) }px; width: #{ speaker_width(@rooms) }px;" diff --git a/app/views/conference/schedule.html.erb b/app/views/conference/schedule.html.erb index 9390cf5d..f39b0cc2 100644 --- a/app/views/conference/schedule.html.erb +++ b/app/views/conference/schedule.html.erb @@ -1,5 +1,5 @@
- <% if @conference.program.events.scheduled.any? %> + <% if @rooms.length > 1 and @conference.program.events.scheduled.any? %>