scheduled_resource 0.0.1
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.
- 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
|
+
}
|