guide.coffee 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. #
  2. # Config
  3. #
  4. FETCH_URL = 'programs.php'
  5. HOUR_WIDTH = 200
  6. CHANNEL_LABEL_WIDTH = 180
  7. STORAGE_CHANNELS = 'tvgids-channels'
  8. STORAGE_PROGRAMS = 'tvgids-programs'
  9. #SCROLL_MULTIPLIER = HOUR_WIDTH
  10. #
  11. # Utils
  12. #
  13. day_start = -> (new Date()).setHours(0, 0, 0, 0)
  14. day_offset = -> Settings.get('day') * 24 * 60 * 60 * 1000
  15. seconds_today = (time) -> (time - day_start() - day_offset()) / 1000
  16. time2px = (seconds) -> HOUR_WIDTH / 3600 * seconds
  17. zeropad = (digit) -> if digit < 10 then '0' + digit else String(digit)
  18. format_time = (time) ->
  19. date = new Date(time)
  20. zeropad(date.getHours()) + ':' + zeropad(date.getMinutes())
  21. store_list = (name, values) -> localStorage.setItem(name, values.join(';'))
  22. load_stored_list = (name, def) ->
  23. store_list(name, def) if not localStorage.hasOwnProperty(name)
  24. value = localStorage.getItem(name)
  25. if value.length > 0 then value.split(';') else []
  26. #
  27. # Models & collections
  28. #
  29. Channel = Backbone.Model.extend(
  30. defaults:
  31. id: null
  32. name: 'Some channel'
  33. visible: true
  34. programs: []
  35. )
  36. Program = Backbone.Model.extend(
  37. defaults:
  38. id: null
  39. title: 'Some program'
  40. genre: ''
  41. sort: ''
  42. start: 0
  43. end: 0
  44. article_id: null
  45. article_title: null
  46. )
  47. ChannelList = Backbone.Collection.extend(
  48. model: Channel
  49. #comparator: (a, b) -> parseInt(a.get('id')) - parseInt(b.get('id'))
  50. initialize: ->
  51. @listenTo(Settings, 'change:favourite_channels', @propagateVisible)
  52. fetch: ->
  53. @reset(CHANNELS)
  54. #$.getJSON('channels.php', (data) => @reset(data))
  55. @propagateVisible()
  56. propagateVisible: ->
  57. visible = Settings.get('favourite_channels')
  58. for id in visible
  59. @findWhere(id: id)?.set(visible: true)
  60. for id in _.difference(@pluck('id'), visible)
  61. @findWhere(id: id)?.set(visible: false)
  62. fetchPrograms: (day) ->
  63. $('#loading-screen').show()
  64. $.getJSON(
  65. FETCH_URL
  66. channels: Settings.get('favourite_channels').join(','), day: day
  67. (channels) ->
  68. _.each channels, (programs, id) ->
  69. channel = Channels.findWhere(id: id)
  70. channel.set(programs: (
  71. new Program(
  72. id: p.db_id
  73. title: p.titel
  74. genre: p.genre
  75. sort: p.soort
  76. start: Date.parse(p.datum_start)
  77. end: Date.parse(p.datum_end)
  78. article_id: p.artikel_id
  79. article_title: p.artikel_titel
  80. ) for p in programs
  81. ))
  82. $('#loading-screen').hide()
  83. )
  84. )
  85. #
  86. # Views
  87. #
  88. ChannelView = Backbone.View.extend(
  89. tagName: 'div'
  90. className: 'channel'
  91. initialize: ->
  92. @listenTo(@model, 'change:programs', @render)
  93. @listenTo(@model, 'change:visible', @toggleVisible)
  94. #@$el.text(@model.get('title'))
  95. render: ->
  96. @$el.empty()
  97. _.each @model.get('programs'), (program) =>
  98. view = new ProgramView(model: program)
  99. view.render()
  100. @$el.append(view.el)
  101. toggleVisible: ->
  102. @$el.toggle(@model.get('visible'))
  103. )
  104. ProgramView = Backbone.View.extend(
  105. tagName: 'div'
  106. className: 'program'
  107. events:
  108. 'click .favlink': 'toggleFavourite'
  109. 'click': -> Settings.set(selected_program: @model.get('id'))
  110. initialize: ->
  111. $('<span class="title"/>').text(@model.get('title')).appendTo(@el)
  112. from = format_time(@model.get('start'))
  113. to = format_time(@model.get('end'))
  114. @$el.attr('title', @model.get('title') + " (#{from} - #{to})")
  115. @$fav = $('<a class="favlink icon-heart"/>').appendTo(@el)
  116. @$fav.attr('title', 'Als favoriet instellen')
  117. @updateFavlink()
  118. left = time2px(Math.max(0, seconds_today(@model.get('start'))))
  119. width = time2px(seconds_today(@model.get('end'))) - left
  120. @$el.css(
  121. left: left + 'px'
  122. width: (width - 10) + 'px'
  123. )
  124. @listenTo(Settings, 'change:favourite_programs', @updateFavlink)
  125. toggleFavourite: (e) ->
  126. Settings.toggleFavouriteProgram(@model.get('title'))
  127. e.stopPropagation()
  128. updateFavlink: ->
  129. isfav = Settings.isFavouriteProgram(@model.get('title'))
  130. @$el.toggleClass('favourite', isfav)
  131. render: ->
  132. if @model.get('start') <= Date.now()
  133. if @model.get('end') < Date.now()
  134. @$el.removeClass('current').addClass('past')
  135. else
  136. @$el.addClass('current')
  137. )
  138. ChannelLabelsView = Backbone.View.extend(
  139. el: $('#guide .channel-labels')
  140. initialize: (options) ->
  141. @listenTo(Channels, 'reset', @addChannels)
  142. @listenTo(options.app, 'scroll', @moveTop)
  143. addChannels: ->
  144. @$el.empty()
  145. Channels.each((channel) ->
  146. elem = $('<div id="label-' + channel.get('id') + '" class="label"/>')
  147. elem.html(channel.get('name')).toggle(channel.get('visible')).appendTo(@el)
  148. @listenTo(channel, 'change:visible', -> @toggleVisible(channel))
  149. , @)
  150. moveTop: (delta) ->
  151. @$el.css('top', (@$el.position().top - delta) + 'px')
  152. toggleVisible: (channel) ->
  153. @$('#label-' + channel.get('id')).toggle(channel.get('visible'))
  154. )
  155. ProgramDetailsView = Backbone.View.extend(
  156. el: $('#program-details')
  157. template: _.template($('#details-template').html())
  158. events:
  159. 'click .bg': -> Settings.set(selected_program: null)
  160. initialize: (options) ->
  161. @listenTo(Settings, 'change:selected_program', @toggleDetails)
  162. toggleDetails: ->
  163. id = Settings.get('selected_program')
  164. if id
  165. $('#loading-screen').show()
  166. $.getJSON(
  167. 'details.php'
  168. id: id
  169. (data) =>
  170. $('#loading-screen').hide()
  171. @$el.show()
  172. @$('.content').html(@template(_.extend(id: id, data)))
  173. )
  174. else
  175. @$el.hide()
  176. @$('.content').empty()
  177. )
  178. AppView = Backbone.View.extend(
  179. el: $('#guide')
  180. events:
  181. # TODO: move to initialize
  182. 'scroll': 'moveTimeline'
  183. initialize: ->
  184. @prevScrollTop = null
  185. @listenTo(Channels, 'reset', @addChannels)
  186. @listenTo(Settings, 'change:day', @fetchPrograms)
  187. @labelview = new ChannelLabelsView(app: @)
  188. @detailsview = new ProgramDetailsView(app: @)
  189. $('#beforeyesterday').click(-> Settings.set(day: -2))
  190. $('#yesterday').click(-> Settings.set(day: -1))
  191. $('#today').click(-> Settings.set(day: 0))
  192. $('#tomorrow').click(-> Settings.set(day: 1))
  193. $('#overmorrow').click(-> Settings.set(day: 2))
  194. Channels.fetch()
  195. @centerIndicator()
  196. @updateIndicator()
  197. setInterval((=> @updateIndicator()), 60 * 60 * 1000 / HOUR_WIDTH)
  198. addChannels: ->
  199. @$('.channels > .channel').remove()
  200. Channels.each((channel) ->
  201. view = new ChannelView(model: channel)
  202. view.render()
  203. @$('.channels').append(view.el)
  204. , @)
  205. @fetchPrograms()
  206. updateIndicator: ->
  207. if Settings.get('day') == 0
  208. left = time2px(seconds_today(Date.now())) + CHANNEL_LABEL_WIDTH - 1
  209. @$('.indicator')
  210. .css('left', left + 'px')
  211. .height(@$('.channels').height() - 2)
  212. .show()
  213. else
  214. @$('.indicator').hide()
  215. centerIndicator: ->
  216. @$el.scrollLeft(@$('.indicator').position().left - @$el.width() / 2)
  217. fetchPrograms: ->
  218. day = Settings.get('day')
  219. Channels.fetchPrograms(day)
  220. @updateIndicator()
  221. $('.navbar .active').removeClass('active')
  222. $($('.navbar .navitem')[day + 2]).addClass('active')
  223. moveTimeline: ->
  224. if @$el.scrollTop() != @prevScrollTop
  225. @trigger('scroll', @$el.scrollTop() - @prevScrollTop)
  226. @prevScrollTop = @$el.scrollTop()
  227. @$('.timeline').css('top', (@$el.scrollTop() + 37) + 'px')
  228. )
  229. #
  230. # Main
  231. #
  232. Settings = new (Backbone.Model.extend(
  233. defaults:
  234. day: 0
  235. favourite_channels: load_stored_list(STORAGE_CHANNELS,
  236. _.pluck(CHANNELS, 'id'))
  237. favourite_programs: load_stored_list(STORAGE_PROGRAMS, [])
  238. selected_program: null
  239. toggleFavouriteProgram: (title) ->
  240. list = @get('favourite_programs')
  241. if @isFavouriteProgram(title)
  242. list.splice(list.indexOf(title), 1)
  243. else
  244. list.push(title)
  245. @attributes.favourite_programs = list
  246. @trigger('change:favourite_programs')
  247. store_list(STORAGE_PROGRAMS, list)
  248. isFavouriteProgram: (title) ->
  249. _.contains(@get('favourite_programs'), title)
  250. ))()
  251. Channels = new ChannelList()
  252. App = new AppView()