arclight 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (146) hide show
  1. checksums.yaml +4 -4
  2. data/.eslintrc +12 -0
  3. data/.gitignore +5 -0
  4. data/.rubocop.yml +66 -0
  5. data/.solr_wrapper +5 -0
  6. data/.travis.yml +30 -2
  7. data/CONTRIBUTING.md +43 -0
  8. data/Gemfile +36 -0
  9. data/LICENSE.txt +1 -0
  10. data/README.md +85 -12
  11. data/Rakefile +14 -3
  12. data/app/assets/images/blacklight/compact.svg +25 -0
  13. data/app/assets/images/blacklight/logo.png +0 -0
  14. data/app/assets/javascripts/arclight/arclight.js +9 -0
  15. data/app/assets/javascripts/arclight/collection_context.js +18 -0
  16. data/app/assets/javascripts/arclight/collection_navigation.js +114 -0
  17. data/app/assets/javascripts/arclight/collection_scrollspy.js +6 -0
  18. data/app/assets/javascripts/arclight/component_ancestors.js +56 -0
  19. data/app/assets/javascripts/arclight/oembed_viewer.js +39 -0
  20. data/app/assets/javascripts/arclight/truncator.js.erb +23 -0
  21. data/app/assets/stylesheets/arclight/application.scss +13 -0
  22. data/app/assets/stylesheets/arclight/bootstrap_overrides.scss +3 -0
  23. data/app/assets/stylesheets/arclight/modules/collection_search.scss +3 -0
  24. data/app/assets/stylesheets/arclight/modules/hierarchy_and_online_contents.scss +174 -0
  25. data/app/assets/stylesheets/arclight/modules/highlights.scss +10 -0
  26. data/app/assets/stylesheets/arclight/modules/layout.scss +33 -0
  27. data/app/assets/stylesheets/arclight/modules/mastheads.scss +60 -0
  28. data/app/assets/stylesheets/arclight/modules/repositories.scss +29 -0
  29. data/app/assets/stylesheets/arclight/modules/repository_card.scss +54 -0
  30. data/app/assets/stylesheets/arclight/modules/search_results.scss +75 -0
  31. data/app/assets/stylesheets/arclight/modules/show_collection.scss +78 -0
  32. data/app/assets/stylesheets/arclight/modules/sidebar.scss +16 -0
  33. data/app/assets/stylesheets/arclight/variables.scss +2 -0
  34. data/app/controllers/arclight/repositories_controller.rb +40 -0
  35. data/app/controllers/concerns/arclight/field_config_helpers.rb +86 -0
  36. data/app/factories/blacklight_field_configuration_factory.rb +29 -0
  37. data/app/helpers/arclight_helper.rb +173 -0
  38. data/app/models/arclight/parent.rb +24 -0
  39. data/app/models/arclight/parents.rb +34 -0
  40. data/app/models/arclight/requests/google_form.rb +44 -0
  41. data/app/models/concerns/arclight/catalog.rb +22 -0
  42. data/app/models/concerns/arclight/search_behavior.rb +46 -0
  43. data/app/models/concerns/arclight/solr_document.rb +131 -0
  44. data/app/presenters/arclight/index_presenter.rb +10 -0
  45. data/app/presenters/arclight/show_presenter.rb +27 -0
  46. data/app/views/arclight/.keep +0 -0
  47. data/app/views/arclight/repositories/_in_person_repository.html.erb +19 -0
  48. data/app/views/arclight/repositories/_repository.html.erb +62 -0
  49. data/app/views/arclight/repositories/index.html.erb +4 -0
  50. data/app/views/arclight/repositories/show.html.erb +38 -0
  51. data/app/views/arclight/requests/_google_form.html.erb +11 -0
  52. data/app/views/arclight/viewers/_oembed.html.erb +7 -0
  53. data/app/views/catalog/_arclight_document_index_header.html.erb +13 -0
  54. data/app/views/catalog/_arclight_document_index_header_hierarchy_default.html.erb +0 -0
  55. data/app/views/catalog/_arclight_document_index_header_online_contents_default.html.erb +0 -0
  56. data/app/views/catalog/_arclight_document_show_header.html.erb +15 -0
  57. data/app/views/catalog/_arclight_document_show_header_collection.html.erb +12 -0
  58. data/app/views/catalog/_arclight_index_compact_default.html.erb +15 -0
  59. data/app/views/catalog/_arclight_online_content_indicator.html.erb +5 -0
  60. data/app/views/catalog/_arclight_rangelimit.html.erb +24 -0
  61. data/app/views/catalog/_arclight_viewer_default.html.erb +1 -0
  62. data/app/views/catalog/_collection_contents.html.erb +12 -0
  63. data/app/views/catalog/_collection_count.html.erb +7 -0
  64. data/app/views/catalog/_collection_downloads.html.erb +17 -0
  65. data/app/views/catalog/_collection_online_contents.html.erb +17 -0
  66. data/app/views/catalog/_collection_overview.html.erb +7 -0
  67. data/app/views/catalog/_component_overview.html.erb +46 -0
  68. data/app/views/catalog/_context_card.html.erb +27 -0
  69. data/app/views/catalog/_context_sidebar.html.erb +8 -0
  70. data/app/views/catalog/_custom_metadata.html.erb +16 -0
  71. data/app/views/catalog/_home.html.erb +1 -0
  72. data/app/views/catalog/_index_breadcrumb_default.html.erb +3 -0
  73. data/app/views/catalog/_index_default.html.erb +17 -0
  74. data/app/views/catalog/_index_header.html.erb +7 -0
  75. data/app/views/catalog/_index_header_hierarchy_default.html.erb +42 -0
  76. data/app/views/catalog/_index_header_online_contents_default.html.erb +1 -0
  77. data/app/views/catalog/_index_hierarchy_default.html.erb +28 -0
  78. data/app/views/catalog/_index_online_contents_default.html.erb +6 -0
  79. data/app/views/catalog/_results_histogram.html.erb +10 -0
  80. data/app/views/catalog/_search_results.html.erb +31 -0
  81. data/app/views/catalog/_search_results_repository.html.erb +6 -0
  82. data/app/views/catalog/_search_within_form.html.erb +16 -0
  83. data/app/views/catalog/_show_breadcrumbs_default.html.erb +7 -0
  84. data/app/views/catalog/_show_collection.html.erb +40 -0
  85. data/app/views/catalog/_show_component_sidebar.html.erb +12 -0
  86. data/app/views/catalog/_show_default.html.erb +32 -0
  87. data/app/views/catalog/_show_header.html.erb +5 -0
  88. data/app/views/catalog/_show_sidebar.html.erb +30 -0
  89. data/app/views/catalog/index.html.erb +8 -0
  90. data/app/views/layouts/catalog_result.html.erb +7 -0
  91. data/app/views/shared/_breadcrumbs.html.erb +15 -0
  92. data/app/views/shared/_context_sidebar.html.erb +8 -0
  93. data/app/views/shared/_header_navbar.html.erb +54 -0
  94. data/app/views/shared/_main_menu_links.html.erb +6 -0
  95. data/arclight.gemspec +17 -4
  96. data/bin/rails +13 -0
  97. data/config/locales/arclight.en.yml +65 -0
  98. data/config/routes.rb +6 -0
  99. data/lib/arclight.rb +5 -0
  100. data/lib/arclight/custom_component.rb +98 -0
  101. data/lib/arclight/custom_document.rb +93 -0
  102. data/lib/arclight/digital_object.rb +26 -0
  103. data/lib/arclight/engine.rb +55 -0
  104. data/lib/arclight/exceptions.rb +18 -0
  105. data/lib/arclight/indexer.rb +9 -0
  106. data/lib/arclight/normalized_date.rb +45 -0
  107. data/lib/arclight/normalized_id.rb +25 -0
  108. data/lib/arclight/normalized_title.rb +30 -0
  109. data/lib/arclight/repository.rb +91 -0
  110. data/lib/arclight/shared_indexing_behavior.rb +97 -0
  111. data/lib/arclight/shared_terminology_behavior.rb +65 -0
  112. data/lib/arclight/solr_ead_indexer_ext.rb +159 -0
  113. data/lib/arclight/version.rb +3 -1
  114. data/lib/arclight/viewer.rb +45 -0
  115. data/lib/arclight/viewers/oembed.rb +56 -0
  116. data/lib/arclight/year_range.rb +102 -0
  117. data/lib/generators/arclight/install_generator.rb +63 -0
  118. data/lib/generators/arclight/templates/arclight.js +2 -0
  119. data/lib/generators/arclight/templates/arclight.scss +3 -0
  120. data/lib/generators/arclight/templates/catalog_controller.rb +347 -0
  121. data/lib/generators/arclight/templates/config/downloads.yml +13 -0
  122. data/lib/generators/arclight/templates/config/repositories.yml +42 -0
  123. data/lib/generators/arclight/update_generator.rb +22 -0
  124. data/lib/tasks/index.rake +87 -0
  125. data/package.json +24 -0
  126. data/solr/conf/_rest_managed.json +3 -0
  127. data/solr/conf/admin-extra.html +31 -0
  128. data/solr/conf/elevate.xml +36 -0
  129. data/solr/conf/mapping-ISOLatin1Accent.txt +246 -0
  130. data/solr/conf/protwords.txt +21 -0
  131. data/solr/conf/schema.xml +631 -0
  132. data/solr/conf/scripts.conf +24 -0
  133. data/solr/conf/solrconfig.xml +393 -0
  134. data/solr/conf/spellings.txt +2 -0
  135. data/solr/conf/stopwords.txt +58 -0
  136. data/solr/conf/stopwords_en.txt +58 -0
  137. data/solr/conf/synonyms.txt +31 -0
  138. data/solr/conf/xslt/example.xsl +132 -0
  139. data/solr/conf/xslt/example_atom.xsl +67 -0
  140. data/solr/conf/xslt/example_rss.xsl +66 -0
  141. data/solr/conf/xslt/luke.xsl +337 -0
  142. data/tasks/arclight.rake +68 -0
  143. data/template.rb +15 -0
  144. data/vendor/assets/javascripts/responsiveTruncator.js +69 -0
  145. data/vendor/assets/javascripts/stickyfill.js +480 -0
  146. metadata +301 -6
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'solr_wrapper'
4
+ require 'engine_cart/rake_task'
5
+ require 'rspec/core/rake_task'
6
+ require 'arclight'
7
+
8
+ EngineCart.fingerprint_proc = EngineCart.rails_fingerprint_proc
9
+
10
+ desc 'Run test suite'
11
+ task ci: %w[arclight:generate] do
12
+ SolrWrapper.wrap do |solr|
13
+ solr.with_collection do
14
+ Rake::Task['arclight:seed'].invoke
15
+ within_test_app do
16
+ ## Do stuff inside arclight app here
17
+ end
18
+ Rake::Task['spec'].invoke
19
+ end
20
+ end
21
+ end
22
+
23
+ desc 'Run Eslint'
24
+ task :eslint do
25
+ system './node_modules/.bin/eslint app/assets/javascripts'
26
+ end
27
+
28
+ namespace :arclight do
29
+ desc 'Generate a test application'
30
+ task generate: %w[engine_cart:generate]
31
+
32
+ desc 'Run Solr and Blacklight for interactive development'
33
+ task :server, %i[rails_server_args] do |_t, args|
34
+ if File.exist? EngineCart.destination
35
+ within_test_app do
36
+ system 'bundle update'
37
+ end
38
+ else
39
+ Rake::Task['engine_cart:generate'].invoke
40
+ end
41
+
42
+ print 'Starting Solr...'
43
+ SolrWrapper.wrap do |solr|
44
+ puts 'done.'
45
+ solr.with_collection do
46
+ Rake::Task['arclight:seed'].invoke
47
+ within_test_app do
48
+ system "bundle exec rails s #{args[:rails_server_args]}"
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ desc 'Seed fixture data to Solr'
55
+ task :seed do
56
+ puts 'Seeding index with data from spec/fixtures/ead...'
57
+ Dir.glob('spec/fixtures/ead/*.xml').each do |file|
58
+ system("FILE=#{file} rake arclight:index") # no REPOSITORY_ID
59
+ end
60
+ Dir.glob('spec/fixtures/ead/*').each do |dir|
61
+ next unless File.directory?(dir)
62
+ system("REPOSITORY_ID=#{File.basename(dir)} " \
63
+ 'REPOSITORY_FILE=spec/fixtures/config/repositories.yml ' \
64
+ "DIR=#{dir} " \
65
+ 'rake arclight:index_dir')
66
+ end
67
+ end
68
+ end
data/template.rb ADDED
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ # We can remove `blacklight` and `blacklight_range_limit` once they are released
4
+ # since they are already dependents of Arclight (but we need to pin to particular branches)
5
+ gem 'blacklight', github: 'projectblacklight/blacklight'
6
+ gem 'blacklight_range_limit', github: 'projectblacklight/blacklight_range_limit', branch: 'blacklight-7'
7
+
8
+ gem 'arclight', github: 'sul-dlss/arclight'
9
+
10
+ run 'bundle install'
11
+
12
+ generate 'blacklight:install', '--devise'
13
+ generate 'arclight:install', '-f'
14
+
15
+ rake 'db:migrate'
@@ -0,0 +1,69 @@
1
+ /*
2
+ * jQuery Responsive Truncator Plugin
3
+ *
4
+ * https://github.com/jkeck/responsiveTruncator
5
+ *
6
+ * VERSION 0.0.2
7
+ *
8
+ **/
9
+ (function( $ ){
10
+ $.fn.responsiveTruncate = function(options){
11
+ var $this = this;
12
+ $(window).bind("resize", function(){
13
+ removeTruncation($this);
14
+ addTruncation($this);
15
+ });
16
+
17
+ addTruncation($this);
18
+
19
+ function addTruncation(el){
20
+ el.each(function(){
21
+ if($(".responsiveTruncate", $(this)).length == 0){
22
+ var parent = $(this);
23
+ var fontSize = $(this).css('font-size');
24
+ var lineHeight = $(this).css("line-height") ? $(this).css("line-height").replace('px','') : Math.floor(parseInt(fontSize.replace('px','')) * 1.5);
25
+ var total_lines = Math.ceil(parent.height() / lineHeight);
26
+ var settings = $.extend({
27
+ 'lines' : 3,
28
+ 'height' : null,
29
+ 'more' : 'more',
30
+ 'less' : 'less'
31
+ }, options);
32
+ var truncate_height;
33
+ if(settings.height){
34
+ truncate_height = settings.height;
35
+ }else{
36
+ truncate_height = (lineHeight * settings.lines);
37
+ }
38
+ if(parent.height() > truncate_height) {
39
+ var orig_content = parent.html();
40
+ parent.html("<div style='height: " + truncate_height + "px; overflow: hidden;' class='responsiveTruncate'></div>");
41
+ var truncate = $(".responsiveTruncate", parent);
42
+ truncate.html(orig_content);
43
+ truncate.after("<a class='responsiveTruncatorToggle' href='#'>" + settings.more + "</a>");
44
+ var toggle_link = $(".responsiveTruncatorToggle", parent);
45
+ toggle_link.click(function(){
46
+ var text = toggle_link.text() == settings.more ? settings.less : settings.more;
47
+ toggle_link.text(text);
48
+ if(truncate.height() == truncate_height){
49
+ truncate.css({height: '100%'})
50
+ }else{
51
+ truncate.css({height: truncate_height})
52
+ }
53
+ return false;
54
+ });
55
+ }
56
+ }
57
+ });
58
+ }
59
+
60
+ function removeTruncation(el){
61
+ el.each(function(){
62
+ if($(".responsiveTruncate", $(this)).length > 0){
63
+ $(this).html($(".responsiveTruncate", $(this)).html());
64
+ $(".responsiveTruncatorToggle", $(this)).remove();
65
+ }
66
+ });
67
+ }
68
+ };
69
+ })( jQuery );
@@ -0,0 +1,480 @@
1
+ /*!
2
+ * Stickyfill -- `position: sticky` polyfill
3
+ * v. 1.1.4 | https://github.com/wilddeer/stickyfill
4
+ * Copyright Oleg Korsunsky | http://wd.dizaina.net/
5
+ *
6
+ * MIT License
7
+ */
8
+ (function(doc, win) {
9
+ var watchArray = [],
10
+ scroll,
11
+ initialized = false,
12
+ html = doc.documentElement,
13
+ noop = function() {},
14
+ checkTimer,
15
+
16
+ //visibility API strings
17
+ hiddenPropertyName = 'hidden',
18
+ visibilityChangeEventName = 'visibilitychange';
19
+
20
+ //fallback to prefixed names in old webkit browsers
21
+ if (doc.webkitHidden !== undefined) {
22
+ hiddenPropertyName = 'webkitHidden';
23
+ visibilityChangeEventName = 'webkitvisibilitychange';
24
+ }
25
+
26
+ //test getComputedStyle
27
+ if (!win.getComputedStyle) {
28
+ seppuku();
29
+ }
30
+
31
+ //test for native support
32
+ var prefixes = ['', '-webkit-', '-moz-', '-ms-'],
33
+ block = document.createElement('div');
34
+
35
+ for (var i = prefixes.length - 1; i >= 0; i--) {
36
+ try {
37
+ block.style.position = prefixes[i] + 'sticky';
38
+ }
39
+ catch(e) {}
40
+ if (block.style.position != '') {
41
+ seppuku();
42
+ }
43
+ }
44
+
45
+ updateScrollPos();
46
+
47
+ //commit seppuku!
48
+ function seppuku() {
49
+ init = add = rebuild = pause = stop = kill = noop;
50
+ }
51
+
52
+ function mergeObjects(targetObj, sourceObject) {
53
+ for (var key in sourceObject) {
54
+ if (sourceObject.hasOwnProperty(key)) {
55
+ targetObj[key] = sourceObject[key];
56
+ }
57
+ }
58
+ }
59
+
60
+ function parseNumeric(val) {
61
+ return parseFloat(val) || 0;
62
+ }
63
+
64
+ function updateScrollPos() {
65
+ scroll = {
66
+ top: win.pageYOffset,
67
+ left: win.pageXOffset
68
+ };
69
+ }
70
+
71
+ function onScroll() {
72
+ if (win.pageXOffset != scroll.left) {
73
+ updateScrollPos();
74
+ rebuild();
75
+ return;
76
+ }
77
+
78
+ if (win.pageYOffset != scroll.top) {
79
+ updateScrollPos();
80
+ recalcAllPos();
81
+ }
82
+ }
83
+
84
+ //fixes flickering
85
+ function onWheel(event) {
86
+ setTimeout(function() {
87
+ if (win.pageYOffset != scroll.top) {
88
+ scroll.top = win.pageYOffset;
89
+ recalcAllPos();
90
+ }
91
+ }, 0);
92
+ }
93
+
94
+ function recalcAllPos() {
95
+ for (var i = watchArray.length - 1; i >= 0; i--) {
96
+ recalcElementPos(watchArray[i]);
97
+ }
98
+ }
99
+
100
+ function recalcElementPos(el) {
101
+ if (!el.inited) return;
102
+
103
+ var currentMode = (scroll.top <= el.limit.start? 0: scroll.top >= el.limit.end? 2: 1);
104
+
105
+ if (el.mode != currentMode) {
106
+ switchElementMode(el, currentMode);
107
+ }
108
+ }
109
+
110
+ //checks whether stickies start or stop positions have changed
111
+ function fastCheck() {
112
+ for (var i = watchArray.length - 1; i >= 0; i--) {
113
+ if (!watchArray[i].inited) continue;
114
+
115
+ var deltaTop = Math.abs(getDocOffsetTop(watchArray[i].clone) - watchArray[i].docOffsetTop),
116
+ deltaHeight = Math.abs(watchArray[i].parent.node.offsetHeight - watchArray[i].parent.height);
117
+
118
+ if (deltaTop >= 2 || deltaHeight >= 2) return false;
119
+ }
120
+ return true;
121
+ }
122
+
123
+ function initElement(el) {
124
+ if (isNaN(parseFloat(el.computed.top)) || el.isCell || el.computed.display == 'none') return;
125
+
126
+ el.inited = true;
127
+
128
+ if (!el.clone) clone(el);
129
+ if (el.parent.computed.position != 'absolute' &&
130
+ el.parent.computed.position != 'relative') el.parent.node.style.position = 'relative';
131
+
132
+ recalcElementPos(el);
133
+
134
+ el.parent.height = el.parent.node.offsetHeight;
135
+ el.docOffsetTop = getDocOffsetTop(el.clone);
136
+ }
137
+
138
+ function deinitElement(el) {
139
+ var deinitParent = true;
140
+
141
+ el.clone && killClone(el);
142
+ mergeObjects(el.node.style, el.css);
143
+
144
+ //check whether element's parent is used by other stickies
145
+ for (var i = watchArray.length - 1; i >= 0; i--) {
146
+ if (watchArray[i].node !== el.node && watchArray[i].parent.node === el.parent.node) {
147
+ deinitParent = false;
148
+ break;
149
+ }
150
+ };
151
+
152
+ if (deinitParent) el.parent.node.style.position = el.parent.css.position;
153
+ el.mode = -1;
154
+ }
155
+
156
+ function initAll() {
157
+ for (var i = watchArray.length - 1; i >= 0; i--) {
158
+ initElement(watchArray[i]);
159
+ }
160
+ }
161
+
162
+ function deinitAll() {
163
+ for (var i = watchArray.length - 1; i >= 0; i--) {
164
+ deinitElement(watchArray[i]);
165
+ }
166
+ }
167
+
168
+ function switchElementMode(el, mode) {
169
+ var nodeStyle = el.node.style;
170
+
171
+ switch (mode) {
172
+ case 0:
173
+ nodeStyle.position = 'absolute';
174
+ nodeStyle.left = el.offset.left + 'px';
175
+ nodeStyle.right = el.offset.right + 'px';
176
+ nodeStyle.top = el.offset.top + 'px';
177
+ nodeStyle.bottom = 'auto';
178
+ nodeStyle.width = 'auto';
179
+ nodeStyle.marginLeft = 0;
180
+ nodeStyle.marginRight = 0;
181
+ nodeStyle.marginTop = 0;
182
+ break;
183
+
184
+ case 1:
185
+ nodeStyle.position = 'fixed';
186
+ nodeStyle.left = el.box.left + 'px';
187
+ nodeStyle.right = el.box.right + 'px';
188
+ nodeStyle.top = el.css.top;
189
+ nodeStyle.bottom = 'auto';
190
+ nodeStyle.width = 'auto';
191
+ nodeStyle.marginLeft = 0;
192
+ nodeStyle.marginRight = 0;
193
+ nodeStyle.marginTop = 0;
194
+ break;
195
+
196
+ case 2:
197
+ nodeStyle.position = 'absolute';
198
+ nodeStyle.left = el.offset.left + 'px';
199
+ nodeStyle.right = el.offset.right + 'px';
200
+ nodeStyle.top = 'auto';
201
+ nodeStyle.bottom = 0;
202
+ nodeStyle.width = 'auto';
203
+ nodeStyle.marginLeft = 0;
204
+ nodeStyle.marginRight = 0;
205
+ break;
206
+ }
207
+
208
+ el.mode = mode;
209
+ }
210
+
211
+ function clone(el) {
212
+ el.clone = document.createElement('div');
213
+
214
+ var refElement = el.node.nextSibling || el.node,
215
+ cloneStyle = el.clone.style;
216
+
217
+ cloneStyle.height = el.height + 'px';
218
+ cloneStyle.width = el.width + 'px';
219
+ cloneStyle.marginTop = el.computed.marginTop;
220
+ cloneStyle.marginBottom = el.computed.marginBottom;
221
+ cloneStyle.marginLeft = el.computed.marginLeft;
222
+ cloneStyle.marginRight = el.computed.marginRight;
223
+ cloneStyle.padding = cloneStyle.border = cloneStyle.borderSpacing = 0;
224
+ cloneStyle.fontSize = '1em';
225
+ cloneStyle.position = 'static';
226
+ cloneStyle.cssFloat = el.computed.cssFloat;
227
+
228
+ el.node.parentNode.insertBefore(el.clone, refElement);
229
+ }
230
+
231
+ function killClone(el) {
232
+ el.clone.parentNode.removeChild(el.clone);
233
+ el.clone = undefined;
234
+ }
235
+
236
+ function getElementParams(node) {
237
+ var computedStyle = getComputedStyle(node),
238
+ parentNode = node.parentNode,
239
+ parentComputedStyle = getComputedStyle(parentNode),
240
+ cachedPosition = node.style.position;
241
+
242
+ node.style.position = 'relative';
243
+
244
+ var computed = {
245
+ top: computedStyle.top,
246
+ marginTop: computedStyle.marginTop,
247
+ marginBottom: computedStyle.marginBottom,
248
+ marginLeft: computedStyle.marginLeft,
249
+ marginRight: computedStyle.marginRight,
250
+ cssFloat: computedStyle.cssFloat,
251
+ display: computedStyle.display
252
+ },
253
+ numeric = {
254
+ top: parseNumeric(computedStyle.top),
255
+ marginBottom: parseNumeric(computedStyle.marginBottom),
256
+ paddingLeft: parseNumeric(computedStyle.paddingLeft),
257
+ paddingRight: parseNumeric(computedStyle.paddingRight),
258
+ borderLeftWidth: parseNumeric(computedStyle.borderLeftWidth),
259
+ borderRightWidth: parseNumeric(computedStyle.borderRightWidth)
260
+ };
261
+
262
+ node.style.position = cachedPosition;
263
+
264
+ var css = {
265
+ position: node.style.position,
266
+ top: node.style.top,
267
+ bottom: node.style.bottom,
268
+ left: node.style.left,
269
+ right: node.style.right,
270
+ width: node.style.width,
271
+ marginTop: node.style.marginTop,
272
+ marginLeft: node.style.marginLeft,
273
+ marginRight: node.style.marginRight
274
+ },
275
+ nodeOffset = getElementOffset(node),
276
+ parentOffset = getElementOffset(parentNode),
277
+
278
+ parent = {
279
+ node: parentNode,
280
+ css: {
281
+ position: parentNode.style.position
282
+ },
283
+ computed: {
284
+ position: parentComputedStyle.position
285
+ },
286
+ numeric: {
287
+ borderLeftWidth: parseNumeric(parentComputedStyle.borderLeftWidth),
288
+ borderRightWidth: parseNumeric(parentComputedStyle.borderRightWidth),
289
+ borderTopWidth: parseNumeric(parentComputedStyle.borderTopWidth),
290
+ borderBottomWidth: parseNumeric(parentComputedStyle.borderBottomWidth)
291
+ }
292
+ },
293
+
294
+ el = {
295
+ node: node,
296
+ box: {
297
+ left: nodeOffset.win.left,
298
+ right: html.clientWidth - nodeOffset.win.right
299
+ },
300
+ offset: {
301
+ top: nodeOffset.win.top - parentOffset.win.top - parent.numeric.borderTopWidth,
302
+ left: nodeOffset.win.left - parentOffset.win.left - parent.numeric.borderLeftWidth,
303
+ right: -nodeOffset.win.right + parentOffset.win.right - parent.numeric.borderRightWidth
304
+ },
305
+ css: css,
306
+ isCell: computedStyle.display == 'table-cell',
307
+ computed: computed,
308
+ numeric: numeric,
309
+ width: nodeOffset.win.right - nodeOffset.win.left,
310
+ height: nodeOffset.win.bottom - nodeOffset.win.top,
311
+ mode: -1,
312
+ inited: false,
313
+ parent: parent,
314
+ limit: {
315
+ start: nodeOffset.doc.top - numeric.top,
316
+ end: parentOffset.doc.top + parentNode.offsetHeight - parent.numeric.borderBottomWidth -
317
+ node.offsetHeight - numeric.top - numeric.marginBottom
318
+ }
319
+ };
320
+
321
+ return el;
322
+ }
323
+
324
+ function getDocOffsetTop(node) {
325
+ var docOffsetTop = 0;
326
+
327
+ while (node) {
328
+ docOffsetTop += node.offsetTop;
329
+ node = node.offsetParent;
330
+ }
331
+
332
+ return docOffsetTop;
333
+ }
334
+
335
+ function getElementOffset(node) {
336
+ var box = node.getBoundingClientRect();
337
+
338
+ return {
339
+ doc: {
340
+ top: box.top + win.pageYOffset,
341
+ left: box.left + win.pageXOffset
342
+ },
343
+ win: box
344
+ };
345
+ }
346
+
347
+ function startFastCheckTimer() {
348
+ checkTimer = setInterval(function() {
349
+ !fastCheck() && rebuild();
350
+ }, 500);
351
+ }
352
+
353
+ function stopFastCheckTimer() {
354
+ clearInterval(checkTimer);
355
+ }
356
+
357
+ function handlePageVisibilityChange() {
358
+ if (!initialized) return;
359
+
360
+ if (document[hiddenPropertyName]) {
361
+ stopFastCheckTimer();
362
+ }
363
+ else {
364
+ startFastCheckTimer();
365
+ }
366
+ }
367
+
368
+ function init() {
369
+ if (initialized) return;
370
+
371
+ updateScrollPos();
372
+ initAll();
373
+
374
+ win.addEventListener('scroll', onScroll);
375
+ win.addEventListener('wheel', onWheel);
376
+
377
+ //watch for width changes
378
+ win.addEventListener('resize', rebuild);
379
+ win.addEventListener('orientationchange', rebuild);
380
+
381
+ //watch for page visibility
382
+ doc.addEventListener(visibilityChangeEventName, handlePageVisibilityChange);
383
+
384
+ startFastCheckTimer();
385
+
386
+ initialized = true;
387
+ }
388
+
389
+ function rebuild() {
390
+ if (!initialized) return;
391
+
392
+ deinitAll();
393
+
394
+ for (var i = watchArray.length - 1; i >= 0; i--) {
395
+ watchArray[i] = getElementParams(watchArray[i].node);
396
+ }
397
+
398
+ initAll();
399
+ }
400
+
401
+ function pause() {
402
+ win.removeEventListener('scroll', onScroll);
403
+ win.removeEventListener('wheel', onWheel);
404
+ win.removeEventListener('resize', rebuild);
405
+ win.removeEventListener('orientationchange', rebuild);
406
+ doc.removeEventListener(visibilityChangeEventName, handlePageVisibilityChange);
407
+
408
+ stopFastCheckTimer();
409
+
410
+ initialized = false;
411
+ }
412
+
413
+ function stop() {
414
+ pause();
415
+ deinitAll();
416
+ }
417
+
418
+ function kill() {
419
+ stop();
420
+
421
+ //empty the array without loosing the references,
422
+ //the most performant method according to http://jsperf.com/empty-javascript-array
423
+ while (watchArray.length) {
424
+ watchArray.pop();
425
+ }
426
+ }
427
+
428
+ function add(node) {
429
+ //check if Stickyfill is already applied to the node
430
+ for (var i = watchArray.length - 1; i >= 0; i--) {
431
+ if (watchArray[i].node === node) return;
432
+ };
433
+
434
+ var el = getElementParams(node);
435
+
436
+ watchArray.push(el);
437
+
438
+ if (!initialized) {
439
+ init();
440
+ }
441
+ else {
442
+ initElement(el);
443
+ }
444
+ }
445
+
446
+ function remove(node) {
447
+ for (var i = watchArray.length - 1; i >= 0; i--) {
448
+ if (watchArray[i].node === node) {
449
+ deinitElement(watchArray[i]);
450
+ watchArray.splice(i, 1);
451
+ }
452
+ };
453
+ }
454
+
455
+ //expose Stickyfill
456
+ win.Stickyfill = {
457
+ stickies: watchArray,
458
+ add: add,
459
+ remove: remove,
460
+ init: init,
461
+ rebuild: rebuild,
462
+ pause: pause,
463
+ stop: stop,
464
+ kill: kill
465
+ };
466
+ })(document, window);
467
+
468
+
469
+ //if jQuery is available -- create a plugin
470
+ if (window.jQuery) {
471
+ (function($) {
472
+ $.fn.Stickyfill = function(options) {
473
+ this.each(function() {
474
+ Stickyfill.add(this);
475
+ });
476
+
477
+ return this;
478
+ };
479
+ })(window.jQuery);
480
+ }