scheduled_resource 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +136 -0
- data/Rakefile +2 -0
- data/bin/schedulize +173 -0
- data/lib/assets/javascripts/blank.jpg +0 -0
- data/lib/assets/javascripts/justify_tweaks.js +186 -0
- data/lib/assets/javascripts/time_pix.js.coffee +103 -0
- data/lib/assets/javascripts/use_block.js.coffee +131 -0
- data/lib/assets/stylesheets/scheduled_resource.css.scss +250 -0
- data/lib/scheduled_resource.rb +262 -0
- data/lib/scheduled_resource/helper.rb +65 -0
- data/lib/scheduled_resource/resource_use_block.rb +33 -0
- data/lib/scheduled_resource/version.rb +3 -0
- data/lib/z_time_header.rb +20 -0
- data/lib/z_time_header_day.rb +16 -0
- data/lib/z_time_header_hour.rb +15 -0
- data/lib/z_time_label.rb +88 -0
- data/lib/z_time_label_day.rb +23 -0
- data/lib/z_time_label_hour.rb +26 -0
- data/scheduled_resource.gemspec +25 -0
- metadata +178 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 318a50d7695cf7ce846a78ccb22a41ef8aed0451
|
4
|
+
data.tar.gz: 4efe3c0200deefd46219b4fe1096478922493fbb
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 723748c0c600788fc3283f335a208db385a4343659d2d3a1e04a7fcc8bddf268b53c9ef5df30f19dc02bfb39161831a28c8feaa6bffb4f8ed2f2844833ae0c34
|
7
|
+
data.tar.gz: c08da4242b8d12d4d0e56f9fb2c7c7c82e021e381b73b9c6298c9d93d3660d2da8792b3239d257d0b7e46268b08ed8555f78b80db5b9854532a201df24886be0
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 emeyekayee (Mike Cannon)
|
2
|
+
|
3
|
+
The MIT License (MIT)
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
22
|
+
|
data/README.md
ADDED
@@ -0,0 +1,136 @@
|
|
1
|
+
#ScheduledResource
|
2
|
+
|
3
|
+
This gem is for displaying how things are used
|
4
|
+
over time -- a schedule for a set of resources. It
|
5
|
+
provides a way to describe what is being displayed,
|
6
|
+
along with utilities and protocols to connect them:
|
7
|
+
|
8
|
+
- Configuration (specification and management),
|
9
|
+
- Query interfaces (a REST-like API and internal protocols to query the models), and
|
10
|
+
- A basic Rails controller implementation.
|
11
|
+
|
12
|
+
So we have a way to configure the schedule, internal
|
13
|
+
methods to generate the data, and a way to retrieve
|
14
|
+
data from the client. But the gem is largely
|
15
|
+
view-framework agnostic. We could use a variety of
|
16
|
+
client-side packages or even more traditional Rails
|
17
|
+
view templates to generate HTML.
|
18
|
+
|
19
|
+
In any case, to get a the most from a display like this
|
20
|
+
we need some client-side code. The gem includes
|
21
|
+
client-side modules to:
|
22
|
+
|
23
|
+
- Manage <b>time and display geometries</b> with "infinite" scroll along the time axis.
|
24
|
+
- <b>Format display cells</b> in ways specific to the resource models.
|
25
|
+
- <b>Update text justification</b> as the display is scrolled horizontally.
|
26
|
+
|
27
|
+
|
28
|
+
## Configuration Management
|
29
|
+
|
30
|
+
A **scheduled resource** is something that can be
|
31
|
+
used for one thing at a time. Say "Rocky & Bullwinkle"
|
32
|
+
is on channel 3 from 10am to 11am on Saturday. Then
|
33
|
+
'channel 3' is the <u>resource</u> and that showing of
|
34
|
+
the is a <u>resource-use block</u>. Resources and
|
35
|
+
use-blocks are typically Rails models. Each resource
|
36
|
+
and its use-blocks get one row in the display. That
|
37
|
+
row has a label to the left with some timespan visible
|
38
|
+
on the rest of the row.
|
39
|
+
|
40
|
+
The <b>ScheduledResource</b> class manages resource and
|
41
|
+
use-block class names, id's and labels for a schedule.
|
42
|
+
A ScheduledResource instance ties together:
|
43
|
+
|
44
|
+
1. A resource class (eg TvStation),
|
45
|
+
2. An id (a channel number in this example), and
|
46
|
+
3. Strings and other assets that will go into the DOM.
|
47
|
+
|
48
|
+
The id is used to
|
49
|
+
- select a resource <em>instance</em> and
|
50
|
+
- select instances of the <em>resource use block</em> class (eg Program instances).
|
51
|
+
|
52
|
+
The id <em>could</em> be a database id but more
|
53
|
+
often is something a little more suited to human use
|
54
|
+
in the configuration. In any case it is used by model
|
55
|
+
class method
|
56
|
+
<tt>(resource_use_block_class).get_all_blocks()</tt>
|
57
|
+
to select the right use-blocks for the resource.
|
58
|
+
A resource class name and id are are joined with
|
59
|
+
a '_' to form a tag that also serves as an id for the DOM.
|
60
|
+
|
61
|
+
Something else you would expect see in a schedule
|
62
|
+
would be headers and labels -- perhaps one row with
|
63
|
+
the date and another row with the hour. Headers and
|
64
|
+
labels also fit the model of resources and use_blocks.
|
65
|
+
Basic timezone-aware classes (ZTime*) for those are
|
66
|
+
included in this gem.
|
67
|
+
|
68
|
+
|
69
|
+
### Configuration File
|
70
|
+
|
71
|
+
The schedule configuration comes from
|
72
|
+
<tt>config/resource_schedule.yml</tt> which has
|
73
|
+
three top-level sections:
|
74
|
+
|
75
|
+
- ResourceKinds: A hash where the key is a Resource and the value is a UseBlock. (Both are class names),
|
76
|
+
- Resources: A list where each item is a Resource Class followed by one or more resource ids, and
|
77
|
+
- visibleTime: The visible timespan of the schedule in seconds.
|
78
|
+
|
79
|
+
The example file <tt>config/resource_schedule.yml</tt>
|
80
|
+
(installed when you run <tt>schedulize</tt>) should be
|
81
|
+
enough to display a two-row schedule with just the date
|
82
|
+
above and the hour below. Of course you can monkey-patch
|
83
|
+
or subclass these classes for your own needs.
|
84
|
+
|
85
|
+
|
86
|
+
### The schedule API
|
87
|
+
|
88
|
+
The endpoint uses parameters <tt>t1</tt> and
|
89
|
+
<tt>t2</tt> to specify a time interval for the request.
|
90
|
+
A third parameter <tt>inc</tt> allows an initial time
|
91
|
+
window to be expanded without repeating blocks that
|
92
|
+
span those boundaries. The time parameters _plus the
|
93
|
+
configured resources_ define the data to be returned.
|
94
|
+
|
95
|
+
|
96
|
+
### More About Configuration Management
|
97
|
+
|
98
|
+
Once the configuration yaml is loaded that data is
|
99
|
+
maintained in the session structure. Of course having
|
100
|
+
a single configuration file limits the application's
|
101
|
+
usefulness. A more general approach would be to
|
102
|
+
have a user model with login and configuration would
|
103
|
+
be associated with the user.
|
104
|
+
|
105
|
+
|
106
|
+
|
107
|
+
|
108
|
+
|
109
|
+
|
110
|
+
## Installation
|
111
|
+
|
112
|
+
Add this line to your application's Gemfile:
|
113
|
+
|
114
|
+
```ruby
|
115
|
+
gem 'scheduled_resource'
|
116
|
+
```
|
117
|
+
|
118
|
+
And then execute:
|
119
|
+
|
120
|
+
$ bundle
|
121
|
+
|
122
|
+
Or install it yourself as:
|
123
|
+
|
124
|
+
$ gem install scheduled_resource
|
125
|
+
|
126
|
+
## Usage
|
127
|
+
|
128
|
+
To Do: Write usage instructions here
|
129
|
+
|
130
|
+
## Contributing
|
131
|
+
|
132
|
+
1. Fork it ( https://github.com/emeyekayee/scheduled_resource/fork )
|
133
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
134
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
135
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
136
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
data/bin/schedulize
ADDED
@@ -0,0 +1,173 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# This file is based heavily on Capistrano's `capify` command
|
4
|
+
|
5
|
+
require 'optparse'
|
6
|
+
require 'fileutils'
|
7
|
+
|
8
|
+
def place_file( base, file, content )
|
9
|
+
file = File.join(base, file)
|
10
|
+
if File.exists?(file)
|
11
|
+
warn "[skip] `#{file}' already exists"
|
12
|
+
elsif File.exists?(file.downcase)
|
13
|
+
warn "[skip] `#{file.downcase}' exists, which could conflict with `#{file}'"
|
14
|
+
elsif !File.exists?(File.dirname(file))
|
15
|
+
warn "[skip] directory `#{File.dirname(file)}' does not exist"
|
16
|
+
else
|
17
|
+
puts "[add] writing `#{file}'"
|
18
|
+
File.open(file, "w") { |f| f.write(content) }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def js_files_to_copy
|
23
|
+
dir = File.expand_path('../../lib/assets/javascripts' , __FILE__)
|
24
|
+
Dir[ dir + "/*" ]
|
25
|
+
end
|
26
|
+
|
27
|
+
def css_files_to_copy
|
28
|
+
dir = File.expand_path('../../lib/assets/stylesheets' , __FILE__)
|
29
|
+
Dir[ dir + "/*" ]
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_js_dir(base) File.join base, 'vendor/assets/javascripts' end
|
33
|
+
def to_css_dir(base) File.join base, 'vendor/assets/stylesheets' end
|
34
|
+
def js_manifest(base) File.join base, 'app/assets/javascripts/application.js' end
|
35
|
+
def css_manifest(base) File.join base, 'app/assets/stylesheets/application.css' end
|
36
|
+
|
37
|
+
def js_require_content
|
38
|
+
<<EOF
|
39
|
+
//= require use_block
|
40
|
+
//= require time_pix
|
41
|
+
//= require justify_tweaks
|
42
|
+
EOF
|
43
|
+
end
|
44
|
+
|
45
|
+
def insert_js_requires( file )
|
46
|
+
manifest_lines = IO.readlines( file )
|
47
|
+
ix = manifest_lines.rindex{|l| l =~ %r[^//= require.+(jquery|angular)] }
|
48
|
+
|
49
|
+
manifest_lines[ix] << js_require_content
|
50
|
+
puts "[modify] writing `#{file}'"
|
51
|
+
File.open(file, "w") { |f| f.write( manifest_lines.join ) }
|
52
|
+
rescue Exception => e
|
53
|
+
puts "Failed to edit file #{file};\n#{e}; continuing..."
|
54
|
+
puts "Be sure these js dependencies are handled:\n#{js_require_content}\n\n"
|
55
|
+
end
|
56
|
+
|
57
|
+
def css_require_content
|
58
|
+
<<EOF
|
59
|
+
*= require scheduled_resource
|
60
|
+
EOF
|
61
|
+
end
|
62
|
+
|
63
|
+
def insert_css_requires( file )
|
64
|
+
manifest_lines = IO.readlines( file )
|
65
|
+
ix = manifest_lines.index{|l| l =~ %r[^ *\*= require] }
|
66
|
+
|
67
|
+
manifest_lines[ix] = css_require_content + manifest_lines[ix]
|
68
|
+
puts "[modify] writing `#{file}'"
|
69
|
+
File.open(file, "w") { |f| f.write( manifest_lines.join ) }
|
70
|
+
rescue Exception => e
|
71
|
+
puts "Failed to edit file #{file};\n#{e}; continuing..."
|
72
|
+
puts "Be sure these css dependencies are handled:\n#{css_require_content}\n\n"
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
PROGRAM = File.basename($0)
|
77
|
+
|
78
|
+
OptionParser.new do |opts|
|
79
|
+
opts.banner = "Usage: #{PROGRAM} [path]"
|
80
|
+
|
81
|
+
begin
|
82
|
+
opts.parse!(ARGV)
|
83
|
+
rescue OptionParser::ParseError => e
|
84
|
+
warn e.message
|
85
|
+
puts opts
|
86
|
+
exit 1
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
unless ARGV.empty?
|
91
|
+
if !File.exists?(ARGV.first)
|
92
|
+
abort "`#{ARGV.first}' does not exist."
|
93
|
+
elsif !File.directory?(ARGV.first)
|
94
|
+
abort "`#{ARGV.first}' is not a directory."
|
95
|
+
elsif ARGV.length > 1
|
96
|
+
abort "Too many arguments; please specify only the directory to wheneverize."
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
base = ARGV.empty? ? '.' : ARGV.shift
|
101
|
+
|
102
|
+
FileUtils.cp js_files_to_copy, to_js_dir(base)
|
103
|
+
FileUtils.cd( to_js_dir(base) ) do
|
104
|
+
FileUtils.cp "blank.jpg", "Day.jpg"
|
105
|
+
FileUtils.cp "blank.jpg", "Hour.jpg"
|
106
|
+
end
|
107
|
+
|
108
|
+
FileUtils.cp css_files_to_copy, to_css_dir(base)
|
109
|
+
|
110
|
+
insert_js_requires js_manifest(base)
|
111
|
+
insert_css_requires css_manifest(base)
|
112
|
+
|
113
|
+
|
114
|
+
config_content = %q{
|
115
|
+
# Configuration file for ScheduledResource schedule widget.
|
116
|
+
#
|
117
|
+
|
118
|
+
# Classes that represent a resource and resource-use pair.
|
119
|
+
ResourceKinds:
|
120
|
+
# Resource: Resource Use
|
121
|
+
ZTimeHeaderDay: ZTimeLabelDay
|
122
|
+
ZTimeHeaderHour: ZTimeLabelHour
|
123
|
+
|
124
|
+
# Other examples:
|
125
|
+
# ConferenceRoom: Meeting
|
126
|
+
# TvStation: ProgramEvent
|
127
|
+
|
128
|
+
# The schedule display sizes itself to the page width. What span
|
129
|
+
# of time should this be in seconds? (Value string is eval'd)
|
130
|
+
visibleTime: 3.hours
|
131
|
+
|
132
|
+
# A row in the display is specified by a pair: (Resource_class, Resource_id)
|
133
|
+
# The Resource_id is given as a string to be interpreted by the specific
|
134
|
+
# classes. For the included ZTime* classes the Resource_id (rid) indicates
|
135
|
+
# the timezone, [-8 .. -5] represent Pacific .. Eastern US timezones.
|
136
|
+
Resources:
|
137
|
+
- ZTimeHeaderDay -8
|
138
|
+
- ZTimeHeaderHour -8
|
139
|
+
- ZTimeHeaderDay -5
|
140
|
+
- ZTimeHeaderHour -5
|
141
|
+
}
|
142
|
+
|
143
|
+
place_file( base, 'config/resource_schedule.yml', config_content)
|
144
|
+
|
145
|
+
|
146
|
+
|
147
|
+
controller_content = <<-CONTROLLER
|
148
|
+
# Needed this here to get the ZTime* classes in time for session.
|
149
|
+
require 'scheduled_resource'
|
150
|
+
|
151
|
+
class ScheduleController < ApplicationController
|
152
|
+
include ScheduledResource::Helper
|
153
|
+
|
154
|
+
before_action :ensure_schedule_config
|
155
|
+
|
156
|
+
|
157
|
+
# Returns angularjs page (template) which in turn fetches data.
|
158
|
+
def index
|
159
|
+
end
|
160
|
+
|
161
|
+
|
162
|
+
# Json data.
|
163
|
+
def schedule
|
164
|
+
get_data_for_time_span
|
165
|
+
|
166
|
+
render json: @blockss
|
167
|
+
end
|
168
|
+
|
169
|
+
end
|
170
|
+
CONTROLLER
|
171
|
+
|
172
|
+
place_file( base, 'app/controllers/schedule_controller.rb', controller_content)
|
173
|
+
|
Binary file
|
@@ -0,0 +1,186 @@
|
|
1
|
+
'use strict';
|
2
|
+
|
3
|
+
// ============================================================================
|
4
|
+
// Hi-lock: ((" ?[T]o ?Do:.*" (0 'accent10 t)))
|
5
|
+
// Hi-lock: (("\\(^\\|\\W\\)\\*\\(\\w.*\\w\\)\\*\\(\\W\\|$\\)" (2 'accent3 t)))
|
6
|
+
// Hi-lock: end
|
7
|
+
// ============================================================================
|
8
|
+
|
9
|
+
// The idea here is to have *smooth* horizontal scrolling for a mobile device,
|
10
|
+
// track-pad or touch-sensitive mouse like Apple's. It makes navigating a
|
11
|
+
// time-window feel much more natural.
|
12
|
+
//
|
13
|
+
// If you have text within the scrolled content you may want to re-justify that
|
14
|
+
// text for readability as it scrolls left or right.
|
15
|
+
//
|
16
|
+
// This is one approach that avoids the jerkiness associated with a normal
|
17
|
+
// scroll event-listener. The idea is just to wait for the scroll position to
|
18
|
+
// stabilize, recalculate the visible parts of each container, then animate
|
19
|
+
// changes to the '.text_locator' with a little randomness thrown in.
|
20
|
+
|
21
|
+
// ToDo: Convert this to an object w/ methods, for better encapsulation.
|
22
|
+
|
23
|
+
// Sole exported function:
|
24
|
+
function filter_justify_tweaks(sc) {
|
25
|
+
var scrollLeft = sc.scrollLeft()
|
26
|
+
|
27
|
+
// Don't repeat the calculation unnecessarily....
|
28
|
+
if ( filter_justify_tweaks.old_srcoll == scrollLeft ) return
|
29
|
+
filter_justify_tweaks.old_srcoll = scrollLeft
|
30
|
+
|
31
|
+
do_justify_tweaks( sc, scrollLeft )
|
32
|
+
}
|
33
|
+
|
34
|
+
|
35
|
+
function do_justify_tweaks (sc, scrollLeft) {
|
36
|
+
$('.ZTimeHeaderDayrow .timespan').each( function() { // Centered
|
37
|
+
justify( scrollLeft, $(this).children() )
|
38
|
+
});
|
39
|
+
|
40
|
+
$('.Stationrow .timespan').each( function() { // Left-aligned
|
41
|
+
justify_left( scrollLeft, $(this).children() )
|
42
|
+
});
|
43
|
+
|
44
|
+
}
|
45
|
+
|
46
|
+
|
47
|
+
function may_straddle (scrollLeft, scrollRight, blockdivs) {
|
48
|
+
var divs = [], bdiv, cd;
|
49
|
+
var len = blockdivs.length;
|
50
|
+
|
51
|
+
for (var i = 0;
|
52
|
+
(i<len) && (cd = common_data(blockdivs[i])) && cd.bdiv_right < scrollLeft;
|
53
|
+
i++) {}
|
54
|
+
|
55
|
+
for (;
|
56
|
+
(i<len) && (bdiv= blockdivs[i]) && parseInt(bdiv.style.left) < scrollRight;
|
57
|
+
i++) {
|
58
|
+
divs.push( bdiv )
|
59
|
+
}
|
60
|
+
return divs
|
61
|
+
}
|
62
|
+
|
63
|
+
// function sort_em (divs) {
|
64
|
+
// divs.sort( function(a, b) {
|
65
|
+
// return parseInt(a.style.left) - parseInt(b.style.left)
|
66
|
+
// })
|
67
|
+
// }
|
68
|
+
|
69
|
+
|
70
|
+
//////////////////////////////////////////////////////////////////////////////
|
71
|
+
// Re-align left-aligned block content (typ. Channelrows)
|
72
|
+
|
73
|
+
function justify_left (scrollLeft, blockdivs) {
|
74
|
+
// sort_em( blockdivs )
|
75
|
+
|
76
|
+
var scrollRight = scrollLeft + TimePix.pixWindow,
|
77
|
+
bdivs = may_straddle (scrollLeft, scrollRight, blockdivs);
|
78
|
+
if (! bdivs.length) {return}
|
79
|
+
|
80
|
+
var cd = common_data( bdivs[0] )
|
81
|
+
if ( cd.bdiv_left < scrollLeft ) {
|
82
|
+
bdivs.shift()
|
83
|
+
justify_left_1 ( scrollLeft, cd );
|
84
|
+
}
|
85
|
+
undo_any_justify_left( bdivs );
|
86
|
+
}
|
87
|
+
|
88
|
+
function justify_left_1 ( scrollLeft, cd ) {
|
89
|
+
if ( cd.bdiv_left + cd.bdiv_width > scrollLeft ) {
|
90
|
+
var jleft = scrollLeft - cd.bdiv_left
|
91
|
+
|
92
|
+
// cd.tl.css('left', jleft + 'px')
|
93
|
+
cd.tl.animate({left: jleft}, rand_speed())
|
94
|
+
}
|
95
|
+
}
|
96
|
+
|
97
|
+
function undo_any_justify_left (bdivs) {
|
98
|
+
bdivs.forEach( function( bdiv ) {
|
99
|
+
// $('.text_locator', bdiv).css( 'left', '')
|
100
|
+
var tl = $('.text_locator', bdiv)
|
101
|
+
if ( parseInt(tl.css('left')) == 2 ) {return}
|
102
|
+
tl.animate({ left: "2" }, rand_speed())
|
103
|
+
})
|
104
|
+
}
|
105
|
+
|
106
|
+
function rand_speed() { return { duration: 200 + Math.random() * 800 } }
|
107
|
+
|
108
|
+
//////////////////////////////////////////////////////////////////////////////
|
109
|
+
// Re-align center-aligned block content.
|
110
|
+
|
111
|
+
function justify (scrollLeft, blockdivs) {
|
112
|
+
// sort_em(blockdivs)
|
113
|
+
var scrollRight = scrollLeft + TimePix.pixWindow,
|
114
|
+
bdivs = may_straddle (scrollLeft, scrollRight, blockdivs);
|
115
|
+
if (! bdivs.length) {return}
|
116
|
+
|
117
|
+
|
118
|
+
if ( straddles(scrollLeft, bdivs[0]) && straddles(scrollRight, bdivs[0]) ) {
|
119
|
+
straddles_both( scrollLeft, scrollRight, common_data(bdivs[0]) )
|
120
|
+
} else {
|
121
|
+
|
122
|
+
while ( bdivs.length > 0 && straddles( scrollLeft, bdivs[0] ) ) {
|
123
|
+
straddles_left (scrollLeft, common_data(bdivs.shift()));
|
124
|
+
}
|
125
|
+
|
126
|
+
while ( bdivs.length > 0 && straddles( scrollRight, bdivs.slice(-1)[0] ) ) {
|
127
|
+
straddles_right( scrollRight, common_data(bdivs.pop()) );
|
128
|
+
}
|
129
|
+
|
130
|
+
bdivs.forEach( function(bdiv) {
|
131
|
+
straddles_none( common_data(bdiv));
|
132
|
+
})
|
133
|
+
}
|
134
|
+
}
|
135
|
+
|
136
|
+
function straddles(edge, bdiv) {
|
137
|
+
var cd = common_data( bdiv )
|
138
|
+
return ( cd.bdiv_left < edge && edge < cd.bdiv_right )
|
139
|
+
}
|
140
|
+
|
141
|
+
function common_data(bdiv) {
|
142
|
+
var left = parseInt(bdiv.style.left)
|
143
|
+
var width = parseInt(bdiv.style.width)
|
144
|
+
return { tl: $('.text_locator', bdiv),
|
145
|
+
bdiv_left: left,
|
146
|
+
bdiv_width: width,
|
147
|
+
bdiv_right: left + width }
|
148
|
+
}
|
149
|
+
|
150
|
+
function straddles_both (scrollLeft, scrollRight, cd) {
|
151
|
+
var nleft = scrollLeft - cd.bdiv_left
|
152
|
+
var nwidth = scrollRight - scrollLeft
|
153
|
+
relocate (cd.tl, nleft, nwidth)
|
154
|
+
}
|
155
|
+
|
156
|
+
function straddles_right (scrollRight, cd) {
|
157
|
+
if ( cd.bdiv_left + cd.bdiv_width > scrollRight ) {
|
158
|
+
var room = scrollRight - parseInt( cd.tl.parent().css('left') )
|
159
|
+
var jwidth = Math.max( room, 190 ) // 15 in vp
|
160
|
+
relocate (cd.tl, 0, jwidth)
|
161
|
+
}
|
162
|
+
}
|
163
|
+
|
164
|
+
function straddles_left (scrollLeft, cd) {
|
165
|
+
if ( cd.bdiv_left + cd.bdiv_width > scrollLeft ) {
|
166
|
+
var room = parseInt( cd.tl.parent().css('width') )
|
167
|
+
var jleft = Math.min (scrollLeft - cd.bdiv_left, room - 190 ) // 15 in vp
|
168
|
+
var jwidth = room - jleft // Should calculate ^^^ this Fix Me
|
169
|
+
relocate (cd.tl, jleft, jwidth)
|
170
|
+
}
|
171
|
+
}
|
172
|
+
|
173
|
+
function straddles_none(cd) {
|
174
|
+
var room = parseInt( cd.tl.parent().css('width') )
|
175
|
+
if ( parseInt(cd.tl.css('left')) != 0 ||
|
176
|
+
parseInt(cd.tl.css('width')) != room ) {
|
177
|
+
relocate (cd.tl, 0, room)
|
178
|
+
}
|
179
|
+
}
|
180
|
+
|
181
|
+
|
182
|
+
function relocate (tl, nleft, nwidth) {
|
183
|
+
tl.animate({opacity: 0}, {queue: true, duration: 200})
|
184
|
+
tl.animate({ left: nleft, width: nwidth}, {queue: true, duration: 0})
|
185
|
+
tl.animate({opacity: 1}, {queue: true, duration: 800})
|
186
|
+
}
|