network_executive 0.0.4 → 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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,191 @@
1
+ describe NetworkExecutive::ProgramSchedule do
2
+
3
+ it 'should define an Occurrence structure' do
4
+ occ = described_class::Occurrence.new
5
+
6
+ occ.should respond_to(:start_time)
7
+ occ.should respond_to(:duration)
8
+ occ.should respond_to(:end_time)
9
+ end
10
+
11
+ describe '#initialize' do
12
+ context 'with an unknown program name' do
13
+ before do
14
+ NetworkExecutive::Program.stub( :find_by_name ).and_return nil
15
+ end
16
+
17
+ it 'should raise a ProgramNameError' do
18
+ expect{ described_class.new( 'null_prog' ) }.to raise_error NetworkExecutive::ProgramNameError
19
+ end
20
+ end
21
+
22
+ context 'with a known program name' do
23
+ let(:known_program) { double('program') }
24
+ let(:duration) { nil }
25
+
26
+ before do
27
+ NetworkExecutive::Program.stub( :find_by_name ).and_return known_program
28
+ end
29
+
30
+ subject { described_class.new( 'known', duration:duration ) }
31
+
32
+ it { should respond_to(:start_time) }
33
+ its(:program) { should == known_program }
34
+
35
+ context 'and no duration specified' do
36
+ its(:duration) { should == 24.hours }
37
+ end
38
+
39
+ context 'and a duration specified' do
40
+ let(:duration) { 35.minutes }
41
+
42
+ its(:duration) { should == 35.minutes }
43
+ end
44
+
45
+ end
46
+ end
47
+
48
+ describe '#proxy' do
49
+ before do
50
+ NetworkExecutive::Program.stub( :find_by_name ).and_return true
51
+ end
52
+
53
+ subject { described_class.new('p').proxy }
54
+
55
+ it 'should have a schedule proxy' do
56
+ subject.should be_a NetworkExecutive::ProgramScheduleProxy
57
+ end
58
+
59
+ it 'should execute the block' do
60
+ canary = 'alive'
61
+
62
+ described_class.new('p'){ canary = 'dead' }.proxy
63
+
64
+ subject
65
+
66
+ canary.should == 'dead'
67
+ end
68
+ end
69
+
70
+ describe '#start_time=' do
71
+ before do
72
+ NetworkExecutive::Program.stub( :find_by_name ).and_return true
73
+ end
74
+
75
+ it 'should reset the proxy' do
76
+ time = Time.now
77
+
78
+ NetworkExecutive::ProgramScheduleProxy.should_receive(:new).with( time, anything )
79
+
80
+ described_class.new('p').start_time = time
81
+ end
82
+ end
83
+
84
+ describe '#duration=' do
85
+ before do
86
+ NetworkExecutive::Program.stub( :find_by_name ).and_return true
87
+ end
88
+
89
+ it 'should reset the proxy' do
90
+ time = 10.minutes
91
+
92
+ NetworkExecutive::ProgramScheduleProxy.should_receive(:new).with( anything, time )
93
+
94
+ described_class.new('p').duration = time
95
+ end
96
+ end
97
+
98
+ describe '#occurs_at?' do
99
+ before do
100
+ NetworkExecutive::Program.stub( :find_by_name ).and_return true
101
+ end
102
+
103
+ subject { described_class.new('p').occurs_at? Time.now }
104
+
105
+ it 'should delegate to its proxy' do
106
+ NetworkExecutive::ProgramScheduleProxy.any_instance.should_receive( :occurs_at? )
107
+
108
+ subject
109
+ end
110
+ end
111
+
112
+ describe '#play' do
113
+ let(:program) { double('program') }
114
+
115
+ before do
116
+ NetworkExecutive::Program.stub( :find_by_name ).and_return program
117
+ end
118
+
119
+ subject { described_class.new('p').play }
120
+
121
+ it 'should delegate to its proxy' do
122
+ program.should_receive( :play )
123
+
124
+ subject do
125
+ # ...
126
+ end
127
+ end
128
+ end
129
+
130
+ describe '#update' do
131
+ let(:program) { double('program') }
132
+
133
+ before do
134
+ NetworkExecutive::Program.stub( :find_by_name ).and_return program
135
+ end
136
+
137
+ subject { described_class.new('p').update }
138
+
139
+ it 'should delegate to its proxy' do
140
+ program.should_receive( :update )
141
+
142
+ subject do
143
+ # ...
144
+ end
145
+ end
146
+ end
147
+
148
+ describe '#whats_on?' do
149
+ before do
150
+ Timecop.freeze
151
+
152
+ NetworkExecutive::Program.stub( :find_by_name ).and_return double('program')
153
+ end
154
+
155
+ subject { described_class.new( 'p').whats_on? }
156
+
157
+ it 'should indicate if the schedule has anything occuring at the given time' do
158
+ NetworkExecutive::ProgramScheduleProxy.any_instance.should_receive( :occurring_at? ).with Time.now
159
+
160
+ subject
161
+ end
162
+ end
163
+
164
+ describe '#occurrence_at' do
165
+ before do
166
+ NetworkExecutive::Program.stub( :find_by_name ).and_return true
167
+
168
+ NetworkExecutive::ProgramScheduleProxy
169
+ .any_instance
170
+ .stub(:occurrences_between)
171
+ .and_return [ Time.now.end_of_day ]
172
+ end
173
+
174
+ let(:time) { Time.now.end_of_day }
175
+
176
+ subject do
177
+ described_class.new('p', start_time:time, duration:1.minute).occurrence_at time
178
+ end
179
+
180
+ it 'should build an Occurrence' do
181
+ start_time = time.dup
182
+ duration = 1.minute - 1
183
+ end_time = start_time + duration
184
+
185
+ subject.start_time.should eq start_time
186
+ subject.duration.should eq duration
187
+ subject.end_time.should eq end_time
188
+ end
189
+ end
190
+
191
+ end
@@ -0,0 +1,57 @@
1
+ require 'ostruct'
2
+
3
+ describe NetworkExecutive::ScheduledProgram do
4
+
5
+ it { should respond_to(:program) }
6
+ it { should respond_to(:occurrence) }
7
+ it { should respond_to(:remainder) }
8
+ it { should respond_to(:portion) }
9
+
10
+ describe '#display_name' do
11
+ let(:program) { double('program', display_name:'foo') }
12
+
13
+ subject { described_class.new(program).display_name }
14
+
15
+ it { should == 'foo' }
16
+ end
17
+
18
+ describe '#+' do
19
+ context 'with a mismatched program' do
20
+ it 'should raise an ArgumentError' do
21
+ expect { subject + double('program') }.to raise_error ArgumentError
22
+ end
23
+ end
24
+
25
+ context 'with the same program type' do
26
+ let(:end_time) { Time.now }
27
+ let(:program_a) { OpenStruct.new( duration:59.seconds ) }
28
+ let(:occurrence) { OpenStruct.new( duration:59.seconds, end_time:end_time ) }
29
+ let(:program_b) { OpenStruct.new( duration:59.seconds ) }
30
+
31
+ subject do
32
+ described_class.new( program_a, occurrence ) + program_b
33
+ end
34
+
35
+ before do
36
+ program_a.stub(:occurrence).and_return double('occurrence').as_null_object
37
+ end
38
+
39
+ it 'should extend its duration with that of the other program' do
40
+ subject.program.duration.should == 119.seconds
41
+ end
42
+
43
+ it 'should extend its occurrences duration with that of the other program' do
44
+ subject.occurrence.duration.should == 119.seconds
45
+ end
46
+
47
+ it 'should extend its occurrences end time with that of the other program' do
48
+ subject.occurrence.end_time.should == end_time + 1.minute
49
+ end
50
+
51
+ it 'should return itself' do
52
+ subject.should be_a NetworkExecutive::ScheduledProgram
53
+ end
54
+ end
55
+ end
56
+
57
+ end
@@ -1,106 +1,95 @@
1
1
  describe NetworkExecutive::Scheduling do
2
2
 
3
- describe '.every' do
4
- it 'should schedule the program' do
5
- channel = Class.new( NetworkExecutive::Channel ) do
6
- include NetworkExecutive::Scheduling
7
-
8
- every :day, play:'my_show'
9
- end
10
-
11
- channel.schedule.first.should be_a NetworkExecutive::ProgramSchedule
3
+ let(:klass) do
4
+ Class.new do
5
+ include NetworkExecutive::Scheduling
12
6
  end
13
7
  end
14
8
 
15
- describe 'schedule' do
16
- it 'should be a ChannelSchedule' do
17
- channel = Class.new( NetworkExecutive::Channel ) do
18
- include NetworkExecutive::Scheduling
19
- end
20
-
21
- channel.schedule.should be_a NetworkExecutive::ChannelSchedule
22
- end
9
+ before do
10
+ stub_const 'MyChannel', klass
23
11
  end
24
12
 
25
- describe '#whats_on?' do
26
- let(:channel) do
27
- Class.new( NetworkExecutive::Channel ) do
28
- include NetworkExecutive::Scheduling
13
+ subject { MyChannel.new }
14
+
15
+ describe '.schedule' do
16
+ context 'with a block' do
17
+ it 'should add a program' do
18
+ params = [
19
+ 'program',
20
+ kind_of(Hash)
21
+ ]
22
+
23
+ NetworkExecutive::ChannelSchedule.any_instance.should_receive( :add ).with( *params )
24
+
25
+ MyChannel.schedule( 'program' ) do
26
+ # ...
27
+ end
29
28
  end
30
29
  end
31
30
 
32
- context 'with nothing scheduled' do
33
- it 'should return nil' do
34
- channel.new.whats_on?.should be_nil
31
+ context 'without a block' do
32
+ it 'should be a ChannelSchedule' do
33
+ MyChannel.schedule.should be_a NetworkExecutive::ChannelSchedule
35
34
  end
36
35
  end
36
+ end
37
37
 
38
- it 'should return the most appropriate program for a given time' do
39
- program_c = double('program c', include?: true)
40
-
41
- channel.schedule.add double('program a', include?: false)
42
- channel.schedule.add double('program b', include?: true)
43
- channel.schedule.add program_c
44
- channel.schedule.add double('program d', include?: false)
45
-
46
- channel.new.whats_on?.should == program_c
38
+ describe '#schedule' do
39
+ it 'should be a ChannelSchedule' do
40
+ subject.schedule.should be_a NetworkExecutive::ChannelSchedule
47
41
  end
42
+ end
48
43
 
49
- it 'should return all programs scheduled for a range of times' do
50
- program_b = double 'program b'
51
- program_b.stub(:include?).and_return true, false
52
-
53
- program_c = double 'program c'
54
- program_c.stub(:include?).and_return false, true
55
-
56
- channel.schedule.add double('program a', include?: false)
57
- channel.schedule.add program_b
58
- channel.schedule.add program_c
59
- channel.schedule.add double('program d', include?: false)
44
+ describe '#whats_on?' do
45
+ before { Timecop.freeze }
60
46
 
61
- shows = channel.new.whats_on?( 1.hour.ago, 1.hour.from_now )
47
+ it 'should return what is on right now' do
48
+ subject.should_receive( :whats_on_at? ).with( Time.now.change(sec:0) )
62
49
 
63
- shows.first[:program].should be program_b
64
- shows.last[:program].should be program_c
50
+ subject.whats_on?
65
51
  end
52
+ end
66
53
 
67
- it 'should yield a block when provided a stop_time' do
68
- program_a = double('program a', include?: true)
69
- channel.schedule.add program_a
54
+ describe '#whats_on_at?' do
55
+ context 'with nothing scheduled' do
56
+ before do
57
+ NetworkExecutive::ChannelSchedule.any_instance.stub(:find).and_return nil
58
+ end
70
59
 
71
- channel.new.whats_on?( 1.hour.ago, 1.hour.from_now) do |program|
72
- program.should be program_a
60
+ it 'should return OffAir' do
61
+ subject.whats_on_at?( Time.now ).should be_a NetworkExecutive::OffAirSchedule
73
62
  end
74
63
  end
75
- end
76
64
 
77
- describe '#with_showtimes_between' do
78
- let(:klass) do
79
- Class.new( NetworkExecutive::Channel ) do
80
- include NetworkExecutive::Scheduling
65
+ context 'with something scheduled' do
66
+ let(:prog_1) { double('unscheduled', whats_on?: false) }
67
+ let(:prog_2) { double('scheduled', whats_on?: true) }
68
+ let(:prog_3) { double('unscheduled', whats_on?: false) }
81
69
 
82
- every :day, play:'my_show'
70
+ before do
71
+ subject.schedule << prog_1
72
+ subject.schedule << prog_2
73
+ subject.schedule << prog_3
83
74
  end
84
- end
85
-
86
- let(:channel) { klass.new }
87
75
 
88
- subject do
89
- program_a = double 'program_a', include?: true
76
+ it 'should return the scheduled program' do
77
+ subject.whats_on_at?( Time.now ).should == prog_2
78
+ end
79
+ end
80
+ end
90
81
 
91
- channel.schedule.add program_a
82
+ describe '#whats_on_between?' do
83
+ it 'should ask the ChannelSchedule what is on between two dates' do
84
+ date_a = Time.now
85
+ date_b = date_a + 1.5.hours
92
86
 
93
- range = [ 1.hour.ago, 1.hour.from_now ]
87
+ args = [ date_a, date_b, nil ]
94
88
 
95
- channel.with_showtimes_between( *range ) do |showtime, program|
96
- 'block retval'
97
- end
98
- end
89
+ NetworkExecutive::ChannelSchedule.any_instance.should_receive(:whats_on_between?).with( *args )
99
90
 
100
- it 'should contain the blocks return value' do
101
- subject.all?{ |x| x == 'block retval' }.should be_true
91
+ subject.whats_on_between? date_a, date_b
102
92
  end
103
-
104
93
  end
105
94
 
106
95
  end
@@ -0,0 +1,264 @@
1
+ ;(function(window, $)
2
+ {
3
+ var counter = 0,
4
+ $headCache = $('head'),
5
+ oldBigText = window.BigText,
6
+ oldjQueryMethod = $.fn.bigtext,
7
+ BigText = {
8
+ DEFAULT_MIN_FONT_SIZE_PX: null,
9
+ DEFAULT_MAX_FONT_SIZE_PX: 528,
10
+ GLOBAL_STYLE_ID: 'bigtext-style',
11
+ STYLE_ID: 'bigtext-id',
12
+ LINE_CLASS_PREFIX: 'bigtext-line',
13
+ EXEMPT_CLASS: 'bigtext-exempt',
14
+ DEFAULT_CHILD_SELECTOR: '> div',
15
+ childSelectors: {
16
+ div: '> div',
17
+ ol: '> li',
18
+ ul: '> li'
19
+ },
20
+ noConflict: function(restore)
21
+ {
22
+ if(restore) {
23
+ $.fn.bigtext = oldjQueryMethod;
24
+ window.BigText = oldBigText;
25
+ }
26
+ return BigText;
27
+ },
28
+ init: function()
29
+ {
30
+ if(!$('#'+BigText.GLOBAL_STYLE_ID).length) {
31
+ $headCache.append(BigText.generateStyleTag(BigText.GLOBAL_STYLE_ID, ['.bigtext * { white-space: nowrap; }',
32
+ '.bigtext .' + BigText.EXEMPT_CLASS + ', .bigtext .' + BigText.EXEMPT_CLASS + ' * { white-space: normal; }']));
33
+ }
34
+ },
35
+ bindResize: function(eventName, resizeFunction)
36
+ {
37
+ if($.throttle) {
38
+ // https://github.com/cowboy/jquery-throttle-debounce
39
+ $(window).unbind(eventName).bind(eventName, $.throttle(100, resizeFunction));
40
+ } else {
41
+ if($.fn.smartresize) {
42
+ // https://github.com/lrbabe/jquery-smartresize/
43
+ eventName = 'smartresize.' + eventName;
44
+ }
45
+ $(window).unbind(eventName).bind(eventName, resizeFunction);
46
+ }
47
+ },
48
+ getStyleId: function(id)
49
+ {
50
+ return BigText.STYLE_ID + '-' + id;
51
+ },
52
+ generateStyleTag: function(id, css)
53
+ {
54
+ return $('<style>' + css.join('\n') + '</style>').attr('id', id);
55
+ },
56
+ clearCss: function(id)
57
+ {
58
+ var styleId = BigText.getStyleId(id);
59
+ $('#' + styleId).remove();
60
+ },
61
+ generateCss: function(id, linesFontSizes, lineWordSpacings, minFontSizes)
62
+ {
63
+ var css = [];
64
+
65
+ BigText.clearCss(id);
66
+
67
+ for(var j=0, k=linesFontSizes.length; j<k; j++) {
68
+ css.push('#' + id + ' .' + BigText.LINE_CLASS_PREFIX + j + ' {' +
69
+ (minFontSizes[j] ? ' white-space: normal;' : '') +
70
+ (linesFontSizes[j] ? ' font-size: ' + linesFontSizes[j] + 'px;' : '') +
71
+ (lineWordSpacings[j] ? ' word-spacing: ' + lineWordSpacings[j] + 'px;' : '') +
72
+ '}');
73
+ }
74
+
75
+ return BigText.generateStyleTag(BigText.getStyleId(id), css);
76
+ },
77
+ jQueryMethod: function(options)
78
+ {
79
+ BigText.init();
80
+
81
+ options = $.extend({
82
+ minfontsize: BigText.DEFAULT_MIN_FONT_SIZE_PX,
83
+ maxfontsize: BigText.DEFAULT_MAX_FONT_SIZE_PX,
84
+ childSelector: '',
85
+ resize: true
86
+ }, options || {});
87
+
88
+ return this.each(function()
89
+ {
90
+ var $t = $(this).addClass('bigtext'),
91
+ childSelector = options.childSelector ||
92
+ BigText.childSelectors[this.tagName.toLowerCase()] ||
93
+ BigText.DEFAULT_CHILD_SELECTOR,
94
+ maxWidth = $t.width(),
95
+ id = $t.attr('id');
96
+
97
+ if(!id) {
98
+ id = 'bigtext-id' + (counter++);
99
+ $t.attr('id', id);
100
+ }
101
+
102
+ if(options.resize) {
103
+ BigText.bindResize('resize.bigtext-event-' + id, function()
104
+ {
105
+ BigText.jQueryMethod.call($('#' + id), options);
106
+ });
107
+ }
108
+
109
+ BigText.clearCss(id);
110
+
111
+ $t.find(childSelector).addClass(function(lineNumber, className)
112
+ {
113
+ // remove existing line classes.
114
+ return [className.replace(new RegExp('\\b' + BigText.LINE_CLASS_PREFIX + '\\d+\\b'), ''),
115
+ BigText.LINE_CLASS_PREFIX + lineNumber].join(' ');
116
+ });
117
+
118
+ var sizes = calculateSizes($t, childSelector, maxWidth, options.maxfontsize, options.minfontsize);
119
+ $headCache.append(BigText.generateCss(id, sizes.fontSizes, sizes.wordSpacings, sizes.minFontSizes));
120
+ });
121
+ }
122
+ };
123
+
124
+ function testLineDimensions($line, maxWidth, property, size, interval, units)
125
+ {
126
+ var width;
127
+ $line.css(property, size + units);
128
+
129
+ width = $line.width();
130
+
131
+ if(width >= maxWidth) {
132
+ $line.css(property, '');
133
+
134
+ if(width == maxWidth) {
135
+ return {
136
+ match: 'exact',
137
+ size: parseFloat((parseFloat(size) - 0.1).toFixed(3))
138
+ };
139
+ }
140
+
141
+ return {
142
+ match: 'estimate',
143
+ size: parseFloat((parseFloat(size) - interval).toFixed(3))
144
+ };
145
+ }
146
+
147
+ return false;
148
+ }
149
+
150
+ function calculateSizes($t, childSelector, maxWidth, maxFontSize, minFontSize)
151
+ {
152
+ var $c = $t.clone(true)
153
+ .addClass('bigtext-cloned')
154
+ .css({
155
+ fontFamily: $t.css('font-family'),
156
+ 'min-width': parseInt(maxWidth, 10),
157
+ width: 'auto',
158
+ position: 'absolute',
159
+ left: -9999,
160
+ top: -9999
161
+ }).appendTo(document.body);
162
+
163
+ // font-size isn't the only thing we can modify, we can also mess with:
164
+ // word-spacing and letter-spacing.
165
+ // Note: webkit does not respect subpixel letter-spacing or word-spacing,
166
+ // nor does it respect hundredths of a font-size em.
167
+ var fontSizes = [],
168
+ wordSpacings = [],
169
+ minFontSizes = [],
170
+ ratios = [];
171
+
172
+ $c.find(childSelector).css({
173
+ 'float': 'left',
174
+ 'clear': 'left'
175
+ }).each(function(lineNumber) {
176
+ var $line = $(this),
177
+ intervals = [4,1,.4,.1],
178
+ lineMax;
179
+
180
+ if($line.hasClass(BigText.EXEMPT_CLASS)) {
181
+ fontSizes.push(null);
182
+ ratios.push(null);
183
+ minFontSizes.push(false);
184
+ return;
185
+ }
186
+
187
+ // TODO we can cache this ratio?
188
+ var autoGuessSubtraction = 20, // px
189
+ currentFontSize = parseFloat($line.css('font-size')),
190
+ lineWidth = $line.width(),
191
+ ratio = (lineWidth / currentFontSize).toFixed(6),
192
+ newFontSize = parseFloat(((maxWidth - autoGuessSubtraction) / ratio).toFixed(3));
193
+
194
+ outer: for(var m=0, n=intervals.length; m<n; m++) {
195
+ inner: for(var j=1, k=4; j<=k; j++) {
196
+ if(newFontSize + j*intervals[m] > maxFontSize) {
197
+ newFontSize = maxFontSize;
198
+ break outer;
199
+ }
200
+
201
+ lineMax = testLineDimensions($line, maxWidth, 'font-size', newFontSize + j*intervals[m], intervals[m], 'px');
202
+ if(lineMax !== false) {
203
+ newFontSize = lineMax.size;
204
+
205
+ if(lineMax.match == 'exact') {
206
+ break outer;
207
+ }
208
+ break inner;
209
+ }
210
+ }
211
+ }
212
+
213
+ ratios.push(maxWidth / newFontSize);
214
+
215
+ if(newFontSize > maxFontSize) {
216
+ fontSizes.push(maxFontSize);
217
+ minFontSizes.push(false);
218
+ } else if(!!minFontSize && newFontSize < minFontSize) {
219
+ fontSizes.push(minFontSize);
220
+ minFontSizes.push(true);
221
+ } else {
222
+ fontSizes.push(newFontSize);
223
+ minFontSizes.push(false);
224
+ }
225
+ }).each(function(lineNumber) {
226
+ var $line = $(this),
227
+ wordSpacing = 0,
228
+ interval = 1,
229
+ maxWordSpacing;
230
+
231
+ if($line.hasClass(BigText.EXEMPT_CLASS)) {
232
+ wordSpacings.push(null);
233
+ return;
234
+ }
235
+
236
+ // must re-use font-size, even though it was removed above.
237
+ $line.css('font-size', fontSizes[lineNumber] + 'px');
238
+
239
+ for(var m=1, n=5; m<n; m+=interval) {
240
+ maxWordSpacing = testLineDimensions($line, maxWidth, 'word-spacing', m, interval, 'px');
241
+ if(maxWordSpacing !== false) {
242
+ wordSpacing = maxWordSpacing.size;
243
+ break;
244
+ }
245
+ }
246
+
247
+ $line.css('font-size', '');
248
+ wordSpacings.push(wordSpacing);
249
+ }).removeAttr('style');
250
+
251
+ $c.remove();
252
+
253
+ return {
254
+ fontSizes: fontSizes,
255
+ wordSpacings: wordSpacings,
256
+ ratios: ratios,
257
+ minFontSizes: minFontSizes
258
+ };
259
+ }
260
+
261
+ $.fn.bigtext = BigText.jQueryMethod;
262
+ window.BigText = BigText;
263
+
264
+ })(this, jQuery);