guide.coffee 9.4 KB

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