network_executive 0.0.4 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. data/README.md +53 -24
  2. data/app/assets/fonts/network_executive/league_gothic.css +13 -0
  3. data/app/assets/fonts/network_executive/league_gothic.svg +148 -0
  4. data/app/assets/fonts/network_executive/league_gothic.ttf +0 -0
  5. data/app/assets/fonts/network_executive/league_gothic.woff +0 -0
  6. data/app/assets/javascripts/network_executive/components/iframe_notifier.coffee +14 -0
  7. data/app/assets/javascripts/network_executive/components/namespace.coffee +1 -0
  8. data/app/assets/javascripts/network_executive/components/osd.coffee +65 -0
  9. data/app/assets/javascripts/network_executive/components/photo_player.coffee +55 -0
  10. data/app/assets/javascripts/network_executive/components/postman.coffee +23 -0
  11. data/app/assets/javascripts/network_executive/components/set_top_box.coffee +81 -0
  12. data/app/assets/javascripts/network_executive/components/tweet_player.coffee +95 -0
  13. data/app/assets/javascripts/network_executive/components/you_tube_player.coffee +85 -0
  14. data/app/assets/javascripts/network_executive/slideshow.coffee +6 -0
  15. data/app/assets/javascripts/network_executive/twitter.coffee +7 -0
  16. data/app/assets/javascripts/network_executive/you_tube.coffee +8 -0
  17. data/app/assets/javascripts/network_executive.coffee +10 -0
  18. data/app/assets/stylesheets/network_executive/{gui_components.css → components/gui_components.css} +0 -0
  19. data/app/assets/stylesheets/network_executive/{normalize.css → components/normalize.css} +0 -0
  20. data/app/assets/stylesheets/network_executive/{osd.css → components/osd.css} +6 -34
  21. data/app/assets/stylesheets/network_executive/components/osd_guide.css +43 -0
  22. data/app/assets/stylesheets/network_executive/components/photo_player.css +61 -0
  23. data/app/assets/stylesheets/network_executive/{smpte.css → components/smpte.css} +0 -0
  24. data/app/assets/stylesheets/network_executive/components/tweet_player.css +60 -0
  25. data/app/assets/stylesheets/network_executive/components/you_tube_player.css +12 -0
  26. data/app/assets/stylesheets/network_executive/off_air.css +5 -0
  27. data/app/assets/stylesheets/network_executive/slideshow.css +4 -0
  28. data/app/assets/stylesheets/network_executive/twitter.css +5 -0
  29. data/app/assets/stylesheets/network_executive/you_tube.css +3 -0
  30. data/app/assets/stylesheets/{network_executive/application.css → network_executive.css} +5 -5
  31. data/app/controllers/network_executive/components_controller.rb +4 -0
  32. data/app/controllers/network_executive/guide_controller.rb +41 -0
  33. data/app/controllers/network_executive/programs_controller.rb +9 -0
  34. data/app/helpers/network_executive/network_helper.rb +6 -0
  35. data/app/models/network_executive/channel.rb +15 -14
  36. data/app/models/network_executive/guide.rb +65 -0
  37. data/app/models/network_executive/program.rb +69 -5
  38. data/app/models/network_executive/viewer.rb +4 -0
  39. data/app/programs/network_executive/off_air.rb +18 -0
  40. data/app/views/network_executive/components/slideshow.html.erb +20 -0
  41. data/app/views/network_executive/components/twitter.html.erb +16 -0
  42. data/app/views/network_executive/components/you_tube.html.erb +13 -0
  43. data/app/views/network_executive/guide/index.html.erb +30 -0
  44. data/app/views/network_executive/network/index.html.erb +3 -3
  45. data/app/views/network_executive/programs/off_air.html.erb +13 -0
  46. data/config/routes.rb +7 -1
  47. data/lib/network_executive/channel_schedule.rb +65 -0
  48. data/lib/network_executive/components/photo_player.rb +48 -0
  49. data/lib/network_executive/components/tweet_player.rb +48 -0
  50. data/lib/network_executive/components/you_tube_player.rb +15 -0
  51. data/lib/network_executive/components.rb +3 -0
  52. data/lib/network_executive/engine.rb +29 -3
  53. data/lib/network_executive/off_air_schedule.rb +18 -0
  54. data/lib/network_executive/producer.rb +3 -3
  55. data/lib/network_executive/program_schedule.rb +96 -0
  56. data/lib/network_executive/program_schedule_proxy.rb +55 -0
  57. data/lib/network_executive/scheduled_program.rb +26 -0
  58. data/lib/network_executive/scheduling.rb +15 -21
  59. data/lib/network_executive/time_calculations.rb +10 -0
  60. data/lib/network_executive/version.rb +1 -1
  61. data/lib/network_executive.rb +2 -1
  62. data/network_executive.gemspec +5 -0
  63. data/spec/models/channel_spec.rb +30 -12
  64. data/spec/models/{lineup_spec.rb → guide_spec.rb} +28 -6
  65. data/spec/models/program_spec.rb +46 -3
  66. data/spec/models/viewer_spec.rb +9 -1
  67. data/spec/programs/off_air_spec.rb +9 -0
  68. data/spec/spec_helper.rb +3 -2
  69. data/spec/unit/channel_schedule_spec.rb +67 -0
  70. data/spec/unit/components/photo_player_spec.rb +76 -0
  71. data/spec/unit/components/tweet_player_spec.rb +68 -0
  72. data/spec/unit/components/you_tube_player_spec.rb +17 -0
  73. data/spec/unit/off_air_schedule_spec.rb +28 -0
  74. data/spec/unit/producer_spec.rb +2 -4
  75. data/spec/unit/program_schedule_proxy_spec.rb +102 -0
  76. data/spec/unit/program_schedule_spec.rb +191 -0
  77. data/spec/unit/scheduled_program_spec.rb +57 -0
  78. data/spec/unit/scheduling_spec.rb +61 -72
  79. data/vendor/assets/javascripts/bigtext.js +264 -0
  80. metadata +185 -44
  81. data/app/assets/javascripts/application.js +0 -13
  82. data/app/assets/javascripts/network_executive/osd.js +0 -62
  83. data/app/assets/javascripts/network_executive/set_top_box.js +0 -45
  84. data/app/controllers/network_executive/lineup_controller.rb +0 -11
  85. data/app/models/network_executive/channel_schedule.rb +0 -13
  86. data/app/models/network_executive/lineup.rb +0 -74
  87. data/app/models/network_executive/lineup_range.rb +0 -34
  88. data/app/models/network_executive/program_schedule.rb +0 -148
  89. data/app/views/network_executive/lineup/index.html.erb +0 -26
  90. data/spec/models/channel_schedule_spec.rb +0 -29
  91. data/spec/models/lineup_range_spec.rb +0 -65
  92. data/spec/models/program_schedule_spec.rb +0 -399
@@ -0,0 +1,85 @@
1
+ win = @
2
+
3
+ YT_BASE_URL = '//gdata.youtube.com/feeds/api/users/%USER%/uploads/%TAGS%?v=2&alt=json';
4
+
5
+ # A random sort function
6
+ randomizer = -> 0.5 - Math.random()
7
+
8
+ class @NE.YTPlayer
9
+ constructor : (id, options) ->
10
+ @id = id
11
+ @videos = []
12
+ @options = options || {}
13
+
14
+ win.addEventListener 'load:program', @onLoad, false
15
+
16
+ ready : =>
17
+ @__ready__ = true
18
+
19
+ @loadChannel()
20
+
21
+ url : ->
22
+ url = "#{YT_BASE_URL}"
23
+ user = @options.user
24
+ tags = @options.tags
25
+
26
+ url = url.replace( '%USER%', user )
27
+
28
+ if tags && tags.length > 0
29
+ tags.unshift '-'
30
+
31
+ url.replace '%TAGS%', tags.join('/')
32
+ else
33
+ url.replace '%TAGS%', ''
34
+
35
+ loadChannel : ->
36
+ return unless @__ready__ and @__loaded__
37
+
38
+ xhr = new XMLHttpRequest()
39
+ xhr.addEventListener 'readystatechange', (e) =>
40
+ if xhr.readyState == 4 && xhr.status == 200
41
+ data = JSON.parse( xhr.responseText )
42
+
43
+ @onChannelLoaded data
44
+
45
+ xhr.open 'GET', @url(), true
46
+ xhr.send()
47
+
48
+ onChannelLoaded : (data) ->
49
+ if data.feed.entry
50
+ data.feed.entry.forEach (v) =>
51
+ @videos.push v.media$group.yt$videoid.$t
52
+
53
+ @videos.sort randomizer
54
+
55
+ @createPlayer()
56
+
57
+ createPlayer : ->
58
+ @player = new YT.Player @id,
59
+ playerVars :
60
+ autohide: 1
61
+ events :
62
+ onReady : @onPlayerReady,
63
+ onStateChange : @onPlayerStateChange
64
+
65
+ onPlayerReady : (e) =>
66
+ @player.setLoop true
67
+ @player.setShuffle true
68
+
69
+ @player.loadPlaylist playlist:@videos
70
+
71
+ @player.playVideo()
72
+
73
+ onPlayerStateChange : (e) =>
74
+ if e.data == YT.PlayerState.ENDED
75
+ @player.nextVideo()
76
+
77
+ onLoad : (e) =>
78
+ @__loaded__ = true
79
+
80
+ payload = e.detail.data
81
+
82
+ Object.keys(payload).forEach (k) =>
83
+ @options[k] = payload[k]
84
+
85
+ @loadChannel()
@@ -0,0 +1,6 @@
1
+ #= require ./components/namespace
2
+ #= require ./components/iframe_notifier
3
+ #= require ./components/postman
4
+ #= require ./components/photo_player
5
+
6
+ new NE.PhotoPlayer( 'current_photo', 'title', 'description', 'photographer', 'location' )
@@ -0,0 +1,7 @@
1
+ #= require ./components/namespace
2
+ #= require ./components/iframe_notifier
3
+ #= require ./components/postman
4
+ #= require ./components/tweet_player
5
+ #= require bigtext
6
+
7
+ new NE.TweetPlayer( '#tweet .tweet-text' )
@@ -0,0 +1,8 @@
1
+ #= require ./components/namespace
2
+ #= require ./components/iframe_notifier
3
+ #= require ./components/postman
4
+ #= require ./components/you_tube_player
5
+
6
+ player = new NE.YTPlayer( 'player' )
7
+
8
+ @onYouTubeIframeAPIReady = player.ready
@@ -0,0 +1,10 @@
1
+ #= require ./network_executive/components/namespace
2
+ #= require ./network_executive/components/osd
3
+ #= require ./network_executive/components/set_top_box
4
+ #= require ./network_executive/components/postman
5
+
6
+ paths = @location.pathname.split '/'
7
+ channel = paths[ paths.length - 1]
8
+
9
+ new NE.SetTopBox( channel )
10
+ new NE.OSD()
@@ -9,54 +9,26 @@
9
9
  -moz-transition: opacity .25s ease-in;
10
10
  -transition: opacity .25s ease-in;
11
11
  }
12
+
12
13
  #osd.active {
13
14
  opacity: 1;
14
15
  }
16
+
15
17
  #osd header {
16
- overflow: auto;
18
+ overflow: hidden;
17
19
  }
20
+
18
21
  #osd .network-name {
19
22
  font-size: 5vmin;
20
23
  font-weight: normal;
21
24
  }
22
- #osd a {
23
- color: #fff;
24
- text-decoration: none;
25
- }
25
+
26
26
  .osd-now {
27
27
  float: right;
28
28
  padding-top: .35em;
29
29
  font-size: 2.5vmin;
30
30
  }
31
+
31
32
  .osd-now-date {
32
33
  display: block;
33
- }
34
-
35
- #osd table {
36
- width: 100%;
37
- border-spacing: .25em;
38
- border-collapse: separate;
39
- font-size: 1.5em;
40
- }
41
- #osd td,
42
- #osd th {
43
- height: 1em;
44
- padding: 0 .25em;
45
- overflow: hidden;
46
- text-overflow: ellipsis;
47
- white-space: nowrap;
48
- }
49
- .guide-time {
50
- text-align: left;
51
- }
52
- .guide-channel {
53
- text-align: right;
54
- text-transform: capitalize;
55
- }
56
- .guide-program {
57
- background: rgba(0,0,0,.33);
58
- }
59
- #osd a {
60
- color: #fff;
61
- text-decoration: none;
62
34
  }
@@ -0,0 +1,43 @@
1
+ .guide {
2
+ overflow: hidden;
3
+ }
4
+
5
+ .guide table {
6
+ width: 100%;
7
+ border-spacing: .25em;
8
+ border-collapse: separate;
9
+ font-size: 1.5em;
10
+ }
11
+
12
+ .guide th,
13
+ .guide-program {
14
+ height: 1.2em;
15
+ overflow: hidden;
16
+ text-overflow: ellipsis;
17
+ white-space: nowrap;
18
+ }
19
+
20
+ .guide-time {
21
+ text-align: left;
22
+ }
23
+
24
+ .guide-channel {
25
+ text-align: right;
26
+ text-transform: capitalize;
27
+ }
28
+
29
+ .guide-program-ct {
30
+ float: left;
31
+ }
32
+
33
+ .guide-program {
34
+ display: block;
35
+ margin: 0 .25em;
36
+ padding: 0 .25em;
37
+ background: rgba(0,0,0,.33);
38
+ }
39
+
40
+ .guide a {
41
+ color: #fff;
42
+ text-decoration: none;
43
+ }
@@ -0,0 +1,61 @@
1
+ html,
2
+ body {
3
+ margin: 0;
4
+ padding: 0;
5
+ height: 100%;
6
+ background: #000;
7
+ overflow: hidden;
8
+ }
9
+
10
+ .photo-player {
11
+ margin: 0;
12
+ text-align: center;
13
+ }
14
+
15
+ .current-photo {
16
+ height: 100%;
17
+ max-height: 100%;
18
+ }
19
+
20
+ .caption {
21
+ position: absolute;
22
+ right: 0;
23
+ bottom: 0;
24
+ left: 0;
25
+ padding: 1em;
26
+ background: rgba(0,0,0,0.7);
27
+ color: #fff;
28
+ text-align: left;
29
+ font-family: 'Lato', sans-serif;
30
+ }
31
+
32
+ .caption a {
33
+ display: none;
34
+ }
35
+
36
+ .title {
37
+ margin-bottom: .25em;
38
+ font-size: 1.75em;
39
+ font-weight: bold;
40
+ }
41
+
42
+ .desc {
43
+ font-size: 1.5em;
44
+ max-height: 1.8em;
45
+ overflow: hidden;
46
+ text-overflow: ellipsis;
47
+ white-space: nowrap;
48
+ }
49
+ .desc:not(:empty) {
50
+ padding: .5em 0 .75em;
51
+ }
52
+
53
+ .photog,
54
+ .location {
55
+ display: inline-block;
56
+ font-size: 1.2em;
57
+ }
58
+
59
+ .photog:not(:empty) + .location:not(:empty):before {
60
+ content: ' of ';
61
+ }
@@ -0,0 +1,60 @@
1
+ html,
2
+ body {
3
+ width: 100%;
4
+ height: 100%;
5
+ margin: 0;
6
+ padding: 0;
7
+ overflow: hidden;
8
+ font-family: 'LeagueGothicRegular';
9
+ text-transform: uppercase;
10
+ line-height: .95;
11
+ }
12
+
13
+ .background {
14
+ position: absolute;
15
+ top: 0;
16
+ right: 0;
17
+ bottom: 0;
18
+ left: 0;
19
+ opacity: .3;
20
+ width: 100%;
21
+ height: 100%;
22
+ z-index: -1;
23
+ }
24
+
25
+ .tweet {
26
+ display: -webkit-box;
27
+ display: -moz-box;
28
+ display: -ms-box;
29
+ display: box;
30
+ -webkit-box-pack: center;
31
+ -moz-box-pack: center;
32
+ -ms-box-pack: center;
33
+ box-pack: center;
34
+ -webkit-box-align: center;
35
+ -moz-box-align: center;
36
+ -ms-box-align: center;
37
+ box-align: center;
38
+ -webkit-box-orient: vertical;
39
+ -moz-box-orient: vertical;
40
+ -ms-box-orient: vertical;
41
+ box-orient: vertical;
42
+ height: 100%;
43
+ }
44
+
45
+ .tweet-text {
46
+ width: 430px;
47
+ }
48
+
49
+ .author {
50
+ width: 430px;
51
+ margin-top: .5em;
52
+ text-align: right;
53
+ font-size: 1.75em;
54
+ }
55
+
56
+ .author:before {
57
+ padding-right: .25em;
58
+ content: '--';
59
+ white-space: nowrap;
60
+ }
@@ -0,0 +1,12 @@
1
+ body {
2
+ height: 100%;
3
+ margin: 0;
4
+ padding: 0;
5
+ overflow: hidden;
6
+ background: #000;
7
+ }
8
+
9
+ #player {
10
+ width: 100%;
11
+ height: 100%;
12
+ }
@@ -0,0 +1,5 @@
1
+ /*
2
+ *= require ./components/normalize
3
+ *= require ./components/gui_components
4
+ *= require ./components/smpte
5
+ */
@@ -0,0 +1,4 @@
1
+ /*
2
+ *= require ./components/normalize
3
+ *= require ./components/photo_player
4
+ */
@@ -0,0 +1,5 @@
1
+ /*
2
+ *= require ./components/normalize
3
+ *= require ./components/tweet_player
4
+ *= require network_executive/league_gothic
5
+ */
@@ -0,0 +1,3 @@
1
+ /*
2
+ *= require ./components/you_tube_player
3
+ */
@@ -8,9 +8,9 @@
8
8
  * You're free to add application-wide styles to this file and they'll appear at the top of the
9
9
  * compiled file, but it's generally better to create a new file per style scope.
10
10
  *
11
- *= require ./normalize
12
- *= require ./gui_components
13
- *= require ./smpte
14
- *= require ./osd
15
- *= require_self
11
+ *= require ./network_executive/components/normalize
12
+ *= require ./network_executive/components/gui_components
13
+ *= require ./network_executive/components/smpte
14
+ *= require ./network_executive/components/osd
15
+ *= require ./network_executive/components/osd_guide
16
16
  */
@@ -0,0 +1,4 @@
1
+ module NetworkExecutive
2
+ class ComponentsController < NetworkExecutive::ApplicationController
3
+ end
4
+ end
@@ -0,0 +1,41 @@
1
+ module NetworkExecutive
2
+ class GuideController < NetworkExecutive::ApplicationController
3
+
4
+ respond_to :html, :json
5
+
6
+ cattr_accessor :index_cached
7
+
8
+ def index
9
+ if stale_cache?
10
+ self.class.index_cached = true
11
+
12
+ @guide = Guide.new
13
+ end
14
+ end
15
+
16
+ private
17
+
18
+ # Only 304 if the cache is fresh *OR* we haven't cached the page yet.
19
+ # This allows server reboots to regenerate the content in the case
20
+ # of an error.
21
+ #
22
+ # NOTE: It is important to note the order of the arguments. The call to
23
+ # Rails` `stale?` method does magical things to the response, so its
24
+ # important that it comes last. If the custom logic indicates that the
25
+ # page should not be cached, then we don't want the Rails method to be
26
+ # called and magically send an unwanted 304.
27
+ def stale_cache?
28
+ !self.class.index_cached || stale?( caching_params )
29
+ end
30
+
31
+ def caching_params
32
+ current_time_slot = Time.now.floor( Guide::Interval.minutes )
33
+
34
+ {
35
+ last_modified: current_time_slot,
36
+ etag: current_time_slot.to_i
37
+ }
38
+ end
39
+
40
+ end
41
+ end
@@ -0,0 +1,9 @@
1
+ module NetworkExecutive
2
+ class ProgramsController < NetworkExecutive::ApplicationController
3
+
4
+ def show
5
+ render "network_executive/programs/#{params[:program_name]}"
6
+ end
7
+
8
+ end
9
+ end
@@ -1,7 +1,13 @@
1
1
  module NetworkExecutive
2
2
  module NetworkHelper
3
+
3
4
  def network_name
4
5
  NetworkExecutive.config.name
5
6
  end
7
+
8
+ def guide_header_width
9
+ '%.3f' % (1 / (@guide.times.size.to_f + 1) * 100)
10
+ end
11
+
6
12
  end
7
13
  end
@@ -3,30 +3,31 @@ require 'network_executive/scheduling'
3
3
 
4
4
  module NetworkExecutive
5
5
  class Channel < EventMachine::Channel
6
- include Scheduling
7
-
8
- # Example
9
- # every :monday, play:'morning_show'
10
- # every :day, play:'stoplight', for_the_first:'15mins', of:'each hour', ending_at:'11am'
11
- # every :day, play:'tweets', for_the_last: '15mins', of:'every hour'
12
- # every :day, play:'sales', for_the_first:'15mins', of:'each hour', between:'2pm and 5pm'
13
- # every :friday, play:'lunch_menu', between:'11am and 13pm', commercial_free:true
6
+ include NetworkExecutive::Scheduling
14
7
 
15
8
  def name
16
9
  self.class.name.demodulize.underscore
17
10
  end
11
+ alias_method :to_s, :name
18
12
 
19
13
  def display_name
20
14
  name.gsub %r{_}, ' '
21
15
  end
22
- alias_method :to_s, :display_name
23
-
24
- def show( scheduled_program )
25
- program = Program.find_by_name scheduled_program.program_name
26
16
 
27
- raise ProgramNotFoundError.new("#{scheduled_program.program_name} is not a registered channel") unless program
17
+ def play( program )
18
+ if program.occurs_at?( Time.now.change(sec:0) )
19
+ program.play do |msg|
20
+ push msg
21
+ end
22
+ else
23
+ program.update do |msg|
24
+ push msg
25
+ end
26
+ end
27
+ end
28
28
 
29
- push program.play
29
+ def play_whats_on( &block )
30
+ whats_on?.play( &block )
30
31
  end
31
32
 
32
33
  class << self
@@ -0,0 +1,65 @@
1
+ # Represents the currently scheduled programs for all channels in a Network
2
+ # for a given time range.
3
+ module NetworkExecutive
4
+ # TODO: Why does this subclass Hash?
5
+ # TODO: This is an ugly data structure. Refactor.
6
+ class Guide < Hash
7
+
8
+ Interval = 15 # In minutes.
9
+ Range = 1.5 # In hours.
10
+
11
+ attr_accessor :start_time, :stop_time
12
+
13
+ def initialize( start = nil, stop = nil )
14
+ self.start_time = start || Time.now
15
+ self.stop_time = stop || Time.now + Range.hours
16
+
17
+ self[:channels] = generate
18
+ end
19
+
20
+ def generate
21
+ with_each_channel do |channel, guide|
22
+ programs = channel.whats_on_between? start_time, stop_time, Interval.minutes
23
+
24
+ guide << {
25
+ channel: channel,
26
+ programs: programs
27
+ }
28
+ end
29
+ end
30
+
31
+ def start_time=( time )
32
+ @start_time = time.floor( Interval.minutes )
33
+ end
34
+
35
+ def stop_time=( time )
36
+ @stop_time = time.floor( Interval.minutes )
37
+ end
38
+
39
+ # TODO: Add test
40
+ def times
41
+ @times ||= begin
42
+ cursor = start_time.dup
43
+
44
+ times = []
45
+
46
+ while cursor < stop_time do
47
+ times << cursor
48
+
49
+ cursor += Interval.minutes
50
+ end
51
+
52
+ times
53
+ end
54
+ end
55
+
56
+ private
57
+
58
+ # TODO: Decouple
59
+ def with_each_channel
60
+ Network.channels.each_with_object([]) do |channel, lu|
61
+ yield channel, lu
62
+ end
63
+ end
64
+ end
65
+ end
@@ -1,5 +1,7 @@
1
1
  require 'multi_json'
2
2
 
3
+ require 'network_executive/network'
4
+
3
5
  module NetworkExecutive
4
6
  class Program
5
7
 
@@ -7,19 +9,81 @@ module NetworkExecutive
7
9
  self.class.name.demodulize.underscore
8
10
  end
9
11
 
12
+ def display_name
13
+ name.titleize
14
+ end
15
+
10
16
  def url
11
17
  ''
12
18
  end
13
19
 
14
- def as_json
20
+ # Will the embedded page be responsible for keeping the content fresh?
21
+ #
22
+ # * `:auto` - The page will be reloaded the page every minute.
23
+ # * `FALSE` - The page will not be reloaded
24
+ #
25
+ # Defaults to `:auto`
26
+ def refresh
27
+ :auto
28
+ end
29
+
30
+ # A Hash containing data to pass to the embedded page via postMessage
31
+ # once the IFRAME has indicated that it has loaded.
32
+ def onload
33
+ {}
34
+ end
35
+
36
+ # A Hash containing data to pass to the embedded page via postMessage
37
+ # whenever a program update is issued.
38
+ def onupdate
39
+ {}
40
+ end
41
+
42
+ # A Hash containing data to pass to the embedded page via postMessage
43
+ # whenever a program is initially shown
44
+ def onshow
15
45
  {
16
- name: name,
17
- url: url
46
+ name: name,
47
+ url: url,
48
+ onLoad: onload,
49
+ refresh: refresh
18
50
  }
19
51
  end
20
52
 
21
- def play
22
- MultiJson.encode( as_json )
53
+ def play( &block )
54
+ Rails.logger.info %Q[Started playing "#{display_name}" at #{Time.now} (refresh: #{refresh})]
55
+
56
+ defer = EM::DefaultDeferrable.new
57
+
58
+ defer.callback( &block )
59
+
60
+ EM.defer do
61
+ payload = onshow.merge( event:'show:program' )
62
+ json = MultiJson.encode( payload )
63
+
64
+ defer.succeed json
65
+ end
66
+
67
+ defer
68
+ end
69
+
70
+ def update( &block )
71
+ return play( &block ) if refresh == :auto
72
+
73
+ Rails.logger.info %Q[Started updating "#{display_name}" at #{Time.now}]
74
+
75
+ defer = EM::DefaultDeferrable.new
76
+
77
+ defer.callback( &block )
78
+
79
+ EM.defer do
80
+ payload = onupdate.merge( event:'update:program' )
81
+ json = MultiJson.encode( payload )
82
+
83
+ defer.succeed json
84
+ end
85
+
86
+ defer
23
87
  end
24
88
 
25
89
  class << self