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.
- data/README.md +53 -24
- data/app/assets/fonts/network_executive/league_gothic.css +13 -0
- data/app/assets/fonts/network_executive/league_gothic.svg +148 -0
- data/app/assets/fonts/network_executive/league_gothic.ttf +0 -0
- data/app/assets/fonts/network_executive/league_gothic.woff +0 -0
- data/app/assets/javascripts/network_executive/components/iframe_notifier.coffee +14 -0
- data/app/assets/javascripts/network_executive/components/namespace.coffee +1 -0
- data/app/assets/javascripts/network_executive/components/osd.coffee +65 -0
- data/app/assets/javascripts/network_executive/components/photo_player.coffee +55 -0
- data/app/assets/javascripts/network_executive/components/postman.coffee +23 -0
- data/app/assets/javascripts/network_executive/components/set_top_box.coffee +81 -0
- data/app/assets/javascripts/network_executive/components/tweet_player.coffee +95 -0
- data/app/assets/javascripts/network_executive/components/you_tube_player.coffee +85 -0
- data/app/assets/javascripts/network_executive/slideshow.coffee +6 -0
- data/app/assets/javascripts/network_executive/twitter.coffee +7 -0
- data/app/assets/javascripts/network_executive/you_tube.coffee +8 -0
- data/app/assets/javascripts/network_executive.coffee +10 -0
- data/app/assets/stylesheets/network_executive/{gui_components.css → components/gui_components.css} +0 -0
- data/app/assets/stylesheets/network_executive/{normalize.css → components/normalize.css} +0 -0
- data/app/assets/stylesheets/network_executive/{osd.css → components/osd.css} +6 -34
- data/app/assets/stylesheets/network_executive/components/osd_guide.css +43 -0
- data/app/assets/stylesheets/network_executive/components/photo_player.css +61 -0
- data/app/assets/stylesheets/network_executive/{smpte.css → components/smpte.css} +0 -0
- data/app/assets/stylesheets/network_executive/components/tweet_player.css +60 -0
- data/app/assets/stylesheets/network_executive/components/you_tube_player.css +12 -0
- data/app/assets/stylesheets/network_executive/off_air.css +5 -0
- data/app/assets/stylesheets/network_executive/slideshow.css +4 -0
- data/app/assets/stylesheets/network_executive/twitter.css +5 -0
- data/app/assets/stylesheets/network_executive/you_tube.css +3 -0
- data/app/assets/stylesheets/{network_executive/application.css → network_executive.css} +5 -5
- data/app/controllers/network_executive/components_controller.rb +4 -0
- data/app/controllers/network_executive/guide_controller.rb +41 -0
- data/app/controllers/network_executive/programs_controller.rb +9 -0
- data/app/helpers/network_executive/network_helper.rb +6 -0
- data/app/models/network_executive/channel.rb +15 -14
- data/app/models/network_executive/guide.rb +65 -0
- data/app/models/network_executive/program.rb +69 -5
- data/app/models/network_executive/viewer.rb +4 -0
- data/app/programs/network_executive/off_air.rb +18 -0
- data/app/views/network_executive/components/slideshow.html.erb +20 -0
- data/app/views/network_executive/components/twitter.html.erb +16 -0
- data/app/views/network_executive/components/you_tube.html.erb +13 -0
- data/app/views/network_executive/guide/index.html.erb +30 -0
- data/app/views/network_executive/network/index.html.erb +3 -3
- data/app/views/network_executive/programs/off_air.html.erb +13 -0
- data/config/routes.rb +7 -1
- data/lib/network_executive/channel_schedule.rb +65 -0
- data/lib/network_executive/components/photo_player.rb +48 -0
- data/lib/network_executive/components/tweet_player.rb +48 -0
- data/lib/network_executive/components/you_tube_player.rb +15 -0
- data/lib/network_executive/components.rb +3 -0
- data/lib/network_executive/engine.rb +29 -3
- data/lib/network_executive/off_air_schedule.rb +18 -0
- data/lib/network_executive/producer.rb +3 -3
- data/lib/network_executive/program_schedule.rb +96 -0
- data/lib/network_executive/program_schedule_proxy.rb +55 -0
- data/lib/network_executive/scheduled_program.rb +26 -0
- data/lib/network_executive/scheduling.rb +15 -21
- data/lib/network_executive/time_calculations.rb +10 -0
- data/lib/network_executive/version.rb +1 -1
- data/lib/network_executive.rb +2 -1
- data/network_executive.gemspec +5 -0
- data/spec/models/channel_spec.rb +30 -12
- data/spec/models/{lineup_spec.rb → guide_spec.rb} +28 -6
- data/spec/models/program_spec.rb +46 -3
- data/spec/models/viewer_spec.rb +9 -1
- data/spec/programs/off_air_spec.rb +9 -0
- data/spec/spec_helper.rb +3 -2
- data/spec/unit/channel_schedule_spec.rb +67 -0
- data/spec/unit/components/photo_player_spec.rb +76 -0
- data/spec/unit/components/tweet_player_spec.rb +68 -0
- data/spec/unit/components/you_tube_player_spec.rb +17 -0
- data/spec/unit/off_air_schedule_spec.rb +28 -0
- data/spec/unit/producer_spec.rb +2 -4
- data/spec/unit/program_schedule_proxy_spec.rb +102 -0
- data/spec/unit/program_schedule_spec.rb +191 -0
- data/spec/unit/scheduled_program_spec.rb +57 -0
- data/spec/unit/scheduling_spec.rb +61 -72
- data/vendor/assets/javascripts/bigtext.js +264 -0
- metadata +185 -44
- data/app/assets/javascripts/application.js +0 -13
- data/app/assets/javascripts/network_executive/osd.js +0 -62
- data/app/assets/javascripts/network_executive/set_top_box.js +0 -45
- data/app/controllers/network_executive/lineup_controller.rb +0 -11
- data/app/models/network_executive/channel_schedule.rb +0 -13
- data/app/models/network_executive/lineup.rb +0 -74
- data/app/models/network_executive/lineup_range.rb +0 -34
- data/app/models/network_executive/program_schedule.rb +0 -148
- data/app/views/network_executive/lineup/index.html.erb +0 -26
- data/spec/models/channel_schedule_spec.rb +0 -29
- data/spec/models/lineup_range_spec.rb +0 -65
- 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
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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
|
-
|
|
16
|
-
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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 '
|
|
33
|
-
it 'should
|
|
34
|
-
|
|
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
|
-
|
|
39
|
-
|
|
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
|
-
|
|
50
|
-
|
|
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
|
-
|
|
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
|
-
|
|
64
|
-
shows.last[:program].should be program_c
|
|
50
|
+
subject.whats_on?
|
|
65
51
|
end
|
|
52
|
+
end
|
|
66
53
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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
|
-
|
|
72
|
-
|
|
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
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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
|
-
|
|
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
|
-
|
|
89
|
-
|
|
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
|
-
|
|
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
|
-
|
|
87
|
+
args = [ date_a, date_b, nil ]
|
|
94
88
|
|
|
95
|
-
|
|
96
|
-
'block retval'
|
|
97
|
-
end
|
|
98
|
-
end
|
|
89
|
+
NetworkExecutive::ChannelSchedule.any_instance.should_receive(:whats_on_between?).with( *args )
|
|
99
90
|
|
|
100
|
-
|
|
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);
|