materialize-rails 0.97.5.custom1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/Gemfile +3 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +63 -0
  6. data/Rakefile +1 -0
  7. data/lib/materialize-rails.rb +25 -0
  8. data/lib/materialize-rails/engine.rb +14 -0
  9. data/lib/materialize-rails/version.rb +3 -0
  10. data/materialize-rails.gemspec +25 -0
  11. data/vendor/assets/fonts/materialize/material-design-icons/LICENSE.txt +428 -0
  12. data/vendor/assets/fonts/materialize/material-design-icons/Material-Design-Icons.eot +0 -0
  13. data/vendor/assets/fonts/materialize/material-design-icons/Material-Design-Icons.svg +769 -0
  14. data/vendor/assets/fonts/materialize/material-design-icons/Material-Design-Icons.ttf +0 -0
  15. data/vendor/assets/fonts/materialize/material-design-icons/Material-Design-Icons.woff +0 -0
  16. data/vendor/assets/fonts/materialize/material-design-icons/Material-Design-Icons.woff2 +0 -0
  17. data/vendor/assets/fonts/materialize/roboto/Roboto-Bold.eot +0 -0
  18. data/vendor/assets/fonts/materialize/roboto/Roboto-Bold.ttf +0 -0
  19. data/vendor/assets/fonts/materialize/roboto/Roboto-Bold.woff +0 -0
  20. data/vendor/assets/fonts/materialize/roboto/Roboto-Bold.woff2 +0 -0
  21. data/vendor/assets/fonts/materialize/roboto/Roboto-Light.eot +0 -0
  22. data/vendor/assets/fonts/materialize/roboto/Roboto-Light.ttf +0 -0
  23. data/vendor/assets/fonts/materialize/roboto/Roboto-Light.woff +0 -0
  24. data/vendor/assets/fonts/materialize/roboto/Roboto-Light.woff2 +0 -0
  25. data/vendor/assets/fonts/materialize/roboto/Roboto-Medium.eot +0 -0
  26. data/vendor/assets/fonts/materialize/roboto/Roboto-Medium.ttf +0 -0
  27. data/vendor/assets/fonts/materialize/roboto/Roboto-Medium.woff +0 -0
  28. data/vendor/assets/fonts/materialize/roboto/Roboto-Medium.woff2 +0 -0
  29. data/vendor/assets/fonts/materialize/roboto/Roboto-Regular.eot +0 -0
  30. data/vendor/assets/fonts/materialize/roboto/Roboto-Regular.ttf +0 -0
  31. data/vendor/assets/fonts/materialize/roboto/Roboto-Regular.woff +0 -0
  32. data/vendor/assets/fonts/materialize/roboto/Roboto-Regular.woff2 +0 -0
  33. data/vendor/assets/fonts/materialize/roboto/Roboto-Thin.eot +0 -0
  34. data/vendor/assets/fonts/materialize/roboto/Roboto-Thin.ttf +0 -0
  35. data/vendor/assets/fonts/materialize/roboto/Roboto-Thin.woff +0 -0
  36. data/vendor/assets/fonts/materialize/roboto/Roboto-Thin.woff2 +0 -0
  37. data/vendor/assets/javascripts/materialize.js +30 -0
  38. data/vendor/assets/javascripts/materialize/animation.js +9 -0
  39. data/vendor/assets/javascripts/materialize/buttons.js +91 -0
  40. data/vendor/assets/javascripts/materialize/cards.js +29 -0
  41. data/vendor/assets/javascripts/materialize/carousel.js +350 -0
  42. data/vendor/assets/javascripts/materialize/character_counter.js +59 -0
  43. data/vendor/assets/javascripts/materialize/chips.js +9 -0
  44. data/vendor/assets/javascripts/materialize/collapsible.js +137 -0
  45. data/vendor/assets/javascripts/materialize/date_picker/picker.date.js +1430 -0
  46. data/vendor/assets/javascripts/materialize/date_picker/picker.js +1123 -0
  47. data/vendor/assets/javascripts/materialize/dropdown.js +228 -0
  48. data/vendor/assets/javascripts/materialize/forms.js +581 -0
  49. data/vendor/assets/javascripts/materialize/global.js +45 -0
  50. data/vendor/assets/javascripts/materialize/hammer.min.js +1 -0
  51. data/vendor/assets/javascripts/materialize/initial.js +11 -0
  52. data/vendor/assets/javascripts/materialize/jquery.easing.1.3.js +205 -0
  53. data/vendor/assets/javascripts/materialize/jquery.hammer.js +33 -0
  54. data/vendor/assets/javascripts/materialize/leanModal.js +178 -0
  55. data/vendor/assets/javascripts/materialize/materialbox.js +269 -0
  56. data/vendor/assets/javascripts/materialize/parallax.js +58 -0
  57. data/vendor/assets/javascripts/materialize/pushpin.js +62 -0
  58. data/vendor/assets/javascripts/materialize/scrollFire.js +44 -0
  59. data/vendor/assets/javascripts/materialize/scrollspy.js +285 -0
  60. data/vendor/assets/javascripts/materialize/sideNav.js +315 -0
  61. data/vendor/assets/javascripts/materialize/slider.js +321 -0
  62. data/vendor/assets/javascripts/materialize/tabs.js +129 -0
  63. data/vendor/assets/javascripts/materialize/toasts.js +136 -0
  64. data/vendor/assets/javascripts/materialize/tooltip.js +203 -0
  65. data/vendor/assets/javascripts/materialize/transitions.js +154 -0
  66. data/vendor/assets/javascripts/materialize/velocity.min.js +5 -0
  67. data/vendor/assets/javascripts/materialize/waves.js +338 -0
  68. data/vendor/assets/stylesheets/components/_buttons.scss +181 -0
  69. data/vendor/assets/stylesheets/components/_cards.scss +134 -0
  70. data/vendor/assets/stylesheets/components/_carousel.scss +34 -0
  71. data/vendor/assets/stylesheets/components/_chips.scss +27 -0
  72. data/vendor/assets/stylesheets/components/_collapsible.scss +85 -0
  73. data/vendor/assets/stylesheets/components/_color.scss +412 -0
  74. data/vendor/assets/stylesheets/components/_dropdown.scss +57 -0
  75. data/vendor/assets/stylesheets/components/_form.scss +918 -0
  76. data/vendor/assets/stylesheets/components/_global.scss +766 -0
  77. data/vendor/assets/stylesheets/components/_grid.scss +146 -0
  78. data/vendor/assets/stylesheets/components/_icons-material-design.scss +3263 -0
  79. data/vendor/assets/stylesheets/components/_materialbox.scss +42 -0
  80. data/vendor/assets/stylesheets/components/_mixins.scss +5 -0
  81. data/vendor/assets/stylesheets/components/_modal.scss +90 -0
  82. data/vendor/assets/stylesheets/components/_navbar.scss +171 -0
  83. data/vendor/assets/stylesheets/components/_normalize.scss +427 -0
  84. data/vendor/assets/stylesheets/components/_prefixer.scss +384 -0
  85. data/vendor/assets/stylesheets/components/_preloader.scss +334 -0
  86. data/vendor/assets/stylesheets/components/_roboto.scss +49 -0
  87. data/vendor/assets/stylesheets/components/_sideNav.scss +112 -0
  88. data/vendor/assets/stylesheets/components/_slider.scss +92 -0
  89. data/vendor/assets/stylesheets/components/_table_of_contents.scss +33 -0
  90. data/vendor/assets/stylesheets/components/_tabs.scss +56 -0
  91. data/vendor/assets/stylesheets/components/_toast.scss +65 -0
  92. data/vendor/assets/stylesheets/components/_tooltip.scss +33 -0
  93. data/vendor/assets/stylesheets/components/_typography.scss +61 -0
  94. data/vendor/assets/stylesheets/components/_variables.scss +161 -0
  95. data/vendor/assets/stylesheets/components/_waves.scss +173 -0
  96. data/vendor/assets/stylesheets/components/date_picker/_default.date.scss +435 -0
  97. data/vendor/assets/stylesheets/components/date_picker/_default.scss +201 -0
  98. data/vendor/assets/stylesheets/components/date_picker/_default.time.scss +125 -0
  99. data/vendor/assets/stylesheets/materialize.scss +40 -0
  100. metadata +199 -0
@@ -0,0 +1,269 @@
1
+ (function ($) {
2
+
3
+ $.fn.materialbox = function () {
4
+
5
+ return this.each(function() {
6
+
7
+ if ($(this).hasClass('initialized')) {
8
+ return;
9
+ }
10
+
11
+ $(this).addClass('initialized');
12
+
13
+ var overlayActive = false;
14
+ var doneAnimating = true;
15
+ var inDuration = 275;
16
+ var outDuration = 200;
17
+ var origin = $(this);
18
+ var placeholder = $('<div></div>').addClass('material-placeholder');
19
+ var originalWidth = 0;
20
+ var originalHeight = 0;
21
+ var ancestorsChanged;
22
+ var ancestor;
23
+ origin.wrap(placeholder);
24
+
25
+
26
+ origin.on('click', function(){
27
+ var placeholder = origin.parent('.material-placeholder');
28
+ var windowWidth = window.innerWidth;
29
+ var windowHeight = window.innerHeight;
30
+ var originalWidth = origin.width();
31
+ var originalHeight = origin.height();
32
+
33
+
34
+ // If already modal, return to original
35
+ if (doneAnimating === false) {
36
+ returnToOriginal();
37
+ return false;
38
+ }
39
+ else if (overlayActive && doneAnimating===true) {
40
+ returnToOriginal();
41
+ return false;
42
+ }
43
+
44
+
45
+ // Set states
46
+ doneAnimating = false;
47
+ origin.addClass('active');
48
+ overlayActive = true;
49
+
50
+ // Set positioning for placeholder
51
+ placeholder.css({
52
+ width: placeholder[0].getBoundingClientRect().width,
53
+ height: placeholder[0].getBoundingClientRect().height,
54
+ position: 'relative',
55
+ top: 0,
56
+ left: 0
57
+ });
58
+
59
+ // Find ancestor with overflow: hidden; and remove it
60
+ ancestorsChanged = undefined;
61
+ ancestor = placeholder[0].parentNode;
62
+ var count = 0;
63
+ while (ancestor !== null && !$(ancestor).is(document)) {
64
+ var curr = $(ancestor);
65
+ if (curr.css('overflow') === 'hidden') {
66
+ curr.css('overflow', 'visible');
67
+ if (ancestorsChanged === undefined) {
68
+ ancestorsChanged = curr;
69
+ }
70
+ else {
71
+ ancestorsChanged = ancestorsChanged.add(curr);
72
+ }
73
+ }
74
+ ancestor = ancestor.parentNode;
75
+ }
76
+
77
+ // Set css on origin
78
+ origin.css({position: 'absolute', 'z-index': 1000})
79
+ .data('width', originalWidth)
80
+ .data('height', originalHeight);
81
+
82
+ // Add overlay
83
+ var overlay = $('<div id="materialbox-overlay"></div>')
84
+ .css({
85
+ opacity: 0
86
+ })
87
+ .click(function(){
88
+ if (doneAnimating === true)
89
+ returnToOriginal();
90
+ });
91
+ // Animate Overlay
92
+ $('body').append(overlay);
93
+ overlay.velocity({opacity: 1}, {duration: inDuration, queue: false, easing: 'easeOutQuad'}
94
+ );
95
+
96
+
97
+ // Add and animate caption if it exists
98
+ if (origin.data('caption') !== "") {
99
+ var $photo_caption = $('<div class="materialbox-caption"></div>');
100
+ $photo_caption.text(origin.data('caption'));
101
+ $('body').append($photo_caption);
102
+ $photo_caption.css({ "display": "inline" });
103
+ $photo_caption.velocity({opacity: 1}, {duration: inDuration, queue: false, easing: 'easeOutQuad'});
104
+ }
105
+
106
+
107
+
108
+ // Resize Image
109
+ var ratio = 0;
110
+ var widthPercent = originalWidth / windowWidth;
111
+ var heightPercent = originalHeight / windowHeight;
112
+ var newWidth = 0;
113
+ var newHeight = 0;
114
+
115
+ if (widthPercent > heightPercent) {
116
+ ratio = originalHeight / originalWidth;
117
+ newWidth = windowWidth * 0.9;
118
+ newHeight = windowWidth * 0.9 * ratio;
119
+ }
120
+ else {
121
+ ratio = originalWidth / originalHeight;
122
+ newWidth = (windowHeight * 0.9) * ratio;
123
+ newHeight = windowHeight * 0.9;
124
+ }
125
+
126
+ // Animate image + set z-index
127
+ if(origin.hasClass('responsive-img')) {
128
+ origin.velocity({'max-width': newWidth, 'width': originalWidth}, {duration: 0, queue: false,
129
+ complete: function(){
130
+ origin.css({left: 0, top: 0})
131
+ .velocity(
132
+ {
133
+ height: newHeight,
134
+ width: newWidth,
135
+ left: $(document).scrollLeft() + windowWidth/2 - origin.parent('.material-placeholder').offset().left - newWidth/2,
136
+ top: $(document).scrollTop() + windowHeight/2 - origin.parent('.material-placeholder').offset().top - newHeight/ 2
137
+ },
138
+ {
139
+ duration: inDuration,
140
+ queue: false,
141
+ easing: 'easeOutQuad',
142
+ complete: function(){doneAnimating = true;}
143
+ }
144
+ );
145
+ } // End Complete
146
+ }); // End Velocity
147
+ }
148
+ else {
149
+ origin.css('left', 0)
150
+ .css('top', 0)
151
+ .velocity(
152
+ {
153
+ height: newHeight,
154
+ width: newWidth,
155
+ left: $(document).scrollLeft() + windowWidth/2 - origin.parent('.material-placeholder').offset().left - newWidth/2,
156
+ top: $(document).scrollTop() + windowHeight/2 - origin.parent('.material-placeholder').offset().top - newHeight/ 2
157
+ },
158
+ {
159
+ duration: inDuration,
160
+ queue: false,
161
+ easing: 'easeOutQuad',
162
+ complete: function(){doneAnimating = true;}
163
+ }
164
+ ); // End Velocity
165
+ }
166
+
167
+ }); // End origin on click
168
+
169
+
170
+ // Return on scroll
171
+ $(window).scroll(function() {
172
+ if (overlayActive ) {
173
+ returnToOriginal();
174
+ }
175
+ });
176
+
177
+ // Return on ESC
178
+ $(document).keyup(function(e) {
179
+
180
+ if (e.keyCode === 27 && doneAnimating === true) { // ESC key
181
+ if (overlayActive) {
182
+ returnToOriginal();
183
+ }
184
+ }
185
+ });
186
+
187
+
188
+ // This function returns the modaled image to the original spot
189
+ function returnToOriginal() {
190
+
191
+ doneAnimating = false;
192
+
193
+ var placeholder = origin.parent('.material-placeholder');
194
+ var windowWidth = window.innerWidth;
195
+ var windowHeight = window.innerHeight;
196
+ var originalWidth = origin.data('width');
197
+ var originalHeight = origin.data('height');
198
+
199
+ origin.velocity("stop", true);
200
+ $('#materialbox-overlay').velocity("stop", true);
201
+ $('.materialbox-caption').velocity("stop", true);
202
+
203
+
204
+ $('#materialbox-overlay').velocity({opacity: 0}, {
205
+ duration: outDuration, // Delay prevents animation overlapping
206
+ queue: false, easing: 'easeOutQuad',
207
+ complete: function(){
208
+ // Remove Overlay
209
+ overlayActive = false;
210
+ $(this).remove();
211
+ }
212
+ });
213
+
214
+ // Resize Image
215
+ origin.velocity(
216
+ {
217
+ width: originalWidth,
218
+ height: originalHeight,
219
+ left: 0,
220
+ top: 0
221
+ },
222
+ {
223
+ duration: outDuration,
224
+ queue: false, easing: 'easeOutQuad'
225
+ }
226
+ );
227
+
228
+ // Remove Caption + reset css settings on image
229
+ $('.materialbox-caption').velocity({opacity: 0}, {
230
+ duration: outDuration, // Delay prevents animation overlapping
231
+ queue: false, easing: 'easeOutQuad',
232
+ complete: function(){
233
+ placeholder.css({
234
+ height: '',
235
+ width: '',
236
+ position: '',
237
+ top: '',
238
+ left: ''
239
+ });
240
+
241
+ origin.css({
242
+ height: '',
243
+ top: '',
244
+ left: '',
245
+ width: '',
246
+ 'max-width': '',
247
+ position: '',
248
+ 'z-index': ''
249
+ });
250
+
251
+ // Remove class
252
+ origin.removeClass('active');
253
+ doneAnimating = true;
254
+ $(this).remove();
255
+
256
+ // Remove overflow overrides on ancestors
257
+ ancestorsChanged.css('overflow', '');
258
+ }
259
+ });
260
+
261
+ }
262
+ });
263
+ };
264
+
265
+ $(document).ready(function(){
266
+ $('.materialboxed').materialbox();
267
+ });
268
+
269
+ }( jQuery ));
@@ -0,0 +1,58 @@
1
+ (function ($) {
2
+
3
+ $.fn.parallax = function () {
4
+ var window_width = $(window).width();
5
+ // Parallax Scripts
6
+ return this.each(function(i) {
7
+ var $this = $(this);
8
+ $this.addClass('parallax');
9
+
10
+ function updateParallax(initial) {
11
+ var container_height;
12
+ if (window_width < 601) {
13
+ container_height = ($this.height() > 0) ? $this.height() : $this.children("img").height();
14
+ }
15
+ else {
16
+ container_height = ($this.height() > 0) ? $this.height() : 500;
17
+ }
18
+ var $img = $this.children("img").first();
19
+ var img_height = $img.height();
20
+ var parallax_dist = img_height - container_height;
21
+ var bottom = $this.offset().top + container_height;
22
+ var top = $this.offset().top;
23
+ var scrollTop = $(window).scrollTop();
24
+ var windowHeight = window.innerHeight;
25
+ var windowBottom = scrollTop + windowHeight;
26
+ var percentScrolled = (windowBottom - top) / (container_height + windowHeight);
27
+ var parallax = Math.round((parallax_dist * percentScrolled));
28
+
29
+ if (initial) {
30
+ $img.css('display', 'block');
31
+ }
32
+ if ((bottom > scrollTop) && (top < (scrollTop + windowHeight))) {
33
+ $img.css('transform', "translate3D(-50%," + parallax + "px, 0)");
34
+ }
35
+
36
+ }
37
+
38
+ // Wait for image load
39
+ $this.children("img").one("load", function() {
40
+ updateParallax(true);
41
+ }).each(function() {
42
+ if(this.complete) $(this).load();
43
+ });
44
+
45
+ $(window).scroll(function() {
46
+ window_width = $(window).width();
47
+ updateParallax(false);
48
+ });
49
+
50
+ $(window).resize(function() {
51
+ window_width = $(window).width();
52
+ updateParallax(false);
53
+ });
54
+
55
+ });
56
+
57
+ };
58
+ }( jQuery ));
@@ -0,0 +1,62 @@
1
+ (function ($) {
2
+ $(document).ready(function() {
3
+
4
+ $.fn.pushpin = function (options) {
5
+
6
+ var defaults = {
7
+ top: 0,
8
+ bottom: Infinity,
9
+ offset: 0
10
+ }
11
+ options = $.extend(defaults, options);
12
+
13
+ $index = 0;
14
+ return this.each(function() {
15
+ var $uniqueId = Materialize.guid(),
16
+ $this = $(this),
17
+ $original_offset = $(this).offset().top;
18
+
19
+ function removePinClasses(object) {
20
+ object.removeClass('pin-top');
21
+ object.removeClass('pinned');
22
+ object.removeClass('pin-bottom');
23
+ }
24
+
25
+ function updateElements(objects, scrolled) {
26
+ objects.each(function () {
27
+ // Add position fixed (because its between top and bottom)
28
+ if (options.top <= scrolled && options.bottom >= scrolled && !$(this).hasClass('pinned')) {
29
+ removePinClasses($(this));
30
+ $(this).css('top', options.offset);
31
+ $(this).addClass('pinned');
32
+ }
33
+
34
+ // Add pin-top (when scrolled position is above top)
35
+ if (scrolled < options.top && !$(this).hasClass('pin-top')) {
36
+ removePinClasses($(this));
37
+ $(this).css('top', 0);
38
+ $(this).addClass('pin-top');
39
+ }
40
+
41
+ // Add pin-bottom (when scrolled position is below bottom)
42
+ if (scrolled > options.bottom && !$(this).hasClass('pin-bottom')) {
43
+ removePinClasses($(this));
44
+ $(this).addClass('pin-bottom');
45
+ $(this).css('top', options.bottom - $original_offset);
46
+ }
47
+ });
48
+ }
49
+
50
+ updateElements($this, $(window).scrollTop());
51
+ $(window).on('scroll.' + $uniqueId, function () {
52
+ var $scrolled = $(window).scrollTop() + options.offset;
53
+ updateElements($this, $scrolled);
54
+ });
55
+
56
+ });
57
+
58
+ };
59
+
60
+
61
+ });
62
+ }( jQuery ));
@@ -0,0 +1,44 @@
1
+ (function($) {
2
+
3
+ // Input: Array of JSON objects {selector, offset, callback}
4
+
5
+ Materialize.scrollFire = function(options) {
6
+
7
+ var didScroll = false;
8
+
9
+ window.addEventListener("scroll", function() {
10
+ didScroll = true;
11
+ });
12
+
13
+ // Rate limit to 100ms
14
+ setInterval(function() {
15
+ if(didScroll) {
16
+ didScroll = false;
17
+
18
+ var windowScroll = window.pageYOffset + window.innerHeight;
19
+
20
+ for (var i = 0 ; i < options.length; i++) {
21
+ // Get options from each line
22
+ var value = options[i];
23
+ var selector = value.selector,
24
+ offset = value.offset,
25
+ callback = value.callback;
26
+
27
+ var currentElement = document.querySelector(selector);
28
+ if ( currentElement !== null) {
29
+ var elementOffset = currentElement.getBoundingClientRect().top + window.pageYOffset;
30
+
31
+ if (windowScroll > (elementOffset + offset)) {
32
+ if (value.done !== true) {
33
+ var callbackFunc = new Function(callback);
34
+ callbackFunc();
35
+ value.done = true;
36
+ }
37
+ }
38
+ }
39
+ }
40
+ }
41
+ }, 100);
42
+ };
43
+
44
+ })(jQuery);
@@ -0,0 +1,285 @@
1
+ /**
2
+ * Extend jquery with a scrollspy plugin.
3
+ * This watches the window scroll and fires events when elements are scrolled into viewport.
4
+ *
5
+ * throttle() and getTime() taken from Underscore.js
6
+ * https://github.com/jashkenas/underscore
7
+ *
8
+ * @author Copyright 2013 John Smart
9
+ * @license https://raw.github.com/thesmart/jquery-scrollspy/master/LICENSE
10
+ * @see https://github.com/thesmart
11
+ * @version 0.1.2
12
+ */
13
+ (function($) {
14
+
15
+ var jWindow = $(window);
16
+ var elements = [];
17
+ var elementsInView = [];
18
+ var isSpying = false;
19
+ var ticks = 0;
20
+ var unique_id = 1;
21
+ var offset = {
22
+ top : 0,
23
+ right : 0,
24
+ bottom : 0,
25
+ left : 0,
26
+ }
27
+
28
+ /**
29
+ * Find elements that are within the boundary
30
+ * @param {number} top
31
+ * @param {number} right
32
+ * @param {number} bottom
33
+ * @param {number} left
34
+ * @return {jQuery} A collection of elements
35
+ */
36
+ function findElements(top, right, bottom, left) {
37
+ var hits = $();
38
+ $.each(elements, function(i, element) {
39
+ if (element.height() > 0) {
40
+ var elTop = element.offset().top,
41
+ elLeft = element.offset().left,
42
+ elRight = elLeft + element.width(),
43
+ elBottom = elTop + element.height();
44
+
45
+ var isIntersect = !(elLeft > right ||
46
+ elRight < left ||
47
+ elTop > bottom ||
48
+ elBottom < top);
49
+
50
+ if (isIntersect) {
51
+ hits.push(element);
52
+ }
53
+ }
54
+ });
55
+
56
+ return hits;
57
+ }
58
+
59
+
60
+ /**
61
+ * Called when the user scrolls the window
62
+ */
63
+ function onScroll() {
64
+ // unique tick id
65
+ ++ticks;
66
+
67
+ // viewport rectangle
68
+ var top = jWindow.scrollTop(),
69
+ left = jWindow.scrollLeft(),
70
+ right = left + jWindow.width(),
71
+ bottom = top + jWindow.height();
72
+
73
+ // determine which elements are in view
74
+ // + 60 accounts for fixed nav
75
+ var intersections = findElements(top+offset.top + 200, right+offset.right, bottom+offset.bottom, left+offset.left);
76
+ $.each(intersections, function(i, element) {
77
+
78
+ var lastTick = element.data('scrollSpy:ticks');
79
+ if (typeof lastTick != 'number') {
80
+ // entered into view
81
+ element.triggerHandler('scrollSpy:enter');
82
+ }
83
+
84
+ // update tick id
85
+ element.data('scrollSpy:ticks', ticks);
86
+ });
87
+
88
+ // determine which elements are no longer in view
89
+ $.each(elementsInView, function(i, element) {
90
+ var lastTick = element.data('scrollSpy:ticks');
91
+ if (typeof lastTick == 'number' && lastTick !== ticks) {
92
+ // exited from view
93
+ element.triggerHandler('scrollSpy:exit');
94
+ element.data('scrollSpy:ticks', null);
95
+ }
96
+ });
97
+
98
+ // remember elements in view for next tick
99
+ elementsInView = intersections;
100
+ }
101
+
102
+ /**
103
+ * Called when window is resized
104
+ */
105
+ function onWinSize() {
106
+ jWindow.trigger('scrollSpy:winSize');
107
+ }
108
+
109
+ /**
110
+ * Get time in ms
111
+ * @license https://raw.github.com/jashkenas/underscore/master/LICENSE
112
+ * @type {function}
113
+ * @return {number}
114
+ */
115
+ var getTime = (Date.now || function () {
116
+ return new Date().getTime();
117
+ });
118
+
119
+ /**
120
+ * Returns a function, that, when invoked, will only be triggered at most once
121
+ * during a given window of time. Normally, the throttled function will run
122
+ * as much as it can, without ever going more than once per `wait` duration;
123
+ * but if you'd like to disable the execution on the leading edge, pass
124
+ * `{leading: false}`. To disable execution on the trailing edge, ditto.
125
+ * @license https://raw.github.com/jashkenas/underscore/master/LICENSE
126
+ * @param {function} func
127
+ * @param {number} wait
128
+ * @param {Object=} options
129
+ * @returns {Function}
130
+ */
131
+ function throttle(func, wait, options) {
132
+ var context, args, result;
133
+ var timeout = null;
134
+ var previous = 0;
135
+ options || (options = {});
136
+ var later = function () {
137
+ previous = options.leading === false ? 0 : getTime();
138
+ timeout = null;
139
+ result = func.apply(context, args);
140
+ context = args = null;
141
+ };
142
+ return function () {
143
+ var now = getTime();
144
+ if (!previous && options.leading === false) previous = now;
145
+ var remaining = wait - (now - previous);
146
+ context = this;
147
+ args = arguments;
148
+ if (remaining <= 0) {
149
+ clearTimeout(timeout);
150
+ timeout = null;
151
+ previous = now;
152
+ result = func.apply(context, args);
153
+ context = args = null;
154
+ } else if (!timeout && options.trailing !== false) {
155
+ timeout = setTimeout(later, remaining);
156
+ }
157
+ return result;
158
+ };
159
+ };
160
+
161
+ /**
162
+ * Enables ScrollSpy using a selector
163
+ * @param {jQuery|string} selector The elements collection, or a selector
164
+ * @param {Object=} options Optional.
165
+ throttle : number -> scrollspy throttling. Default: 100 ms
166
+ offsetTop : number -> offset from top. Default: 0
167
+ offsetRight : number -> offset from right. Default: 0
168
+ offsetBottom : number -> offset from bottom. Default: 0
169
+ offsetLeft : number -> offset from left. Default: 0
170
+ * @returns {jQuery}
171
+ */
172
+ $.scrollSpy = function(selector, options) {
173
+ var visible = [];
174
+ selector = $(selector);
175
+ selector.each(function(i, element) {
176
+ elements.push($(element));
177
+ $(element).data("scrollSpy:id", i);
178
+ // Smooth scroll to section
179
+ $('a[href="#' + $(element).attr('id') + '"]').click(function(e) {
180
+ e.preventDefault();
181
+ var scrollOffset = options.offsetScroll || 0;
182
+ var offset = $(this.hash).offset().top + 1;
183
+
184
+ // offset - 200 allows elements near bottom of page to scroll
185
+
186
+ $('html, body').animate({ scrollTop: offset - scrollOffset - 200 }, {duration: 400, queue: false, easing: 'easeOutCubic'});
187
+
188
+ });
189
+ });
190
+ options = options || {
191
+ throttle: 100
192
+ };
193
+
194
+ offset.top = options.offsetTop || 0;
195
+ offset.right = options.offsetRight || 0;
196
+ offset.bottom = options.offsetBottom || 0;
197
+ offset.left = options.offsetLeft || 0;
198
+
199
+ var throttledScroll = throttle(onScroll, options.throttle || 100);
200
+ var readyScroll = function(){
201
+ $(document).ready(throttledScroll);
202
+ };
203
+
204
+ if (!isSpying) {
205
+ jWindow.on('scroll', readyScroll);
206
+ jWindow.on('resize', readyScroll);
207
+ isSpying = true;
208
+ }
209
+
210
+ // perform a scan once, after current execution context, and after dom is ready
211
+ setTimeout(readyScroll, 0);
212
+
213
+
214
+ selector.on('scrollSpy:enter', function() {
215
+ visible = $.grep(visible, function(value) {
216
+ return value.height() != 0;
217
+ });
218
+
219
+ var $this = $(this);
220
+
221
+ if (visible[0]) {
222
+ $('a[href="#' + visible[0].attr('id') + '"]').removeClass('active');
223
+ if ($this.data('scrollSpy:id') < visible[0].data('scrollSpy:id')) {
224
+ visible.unshift($(this));
225
+ }
226
+ else {
227
+ visible.push($(this));
228
+ }
229
+ }
230
+ else {
231
+ visible.push($(this));
232
+ }
233
+
234
+
235
+ $('a[href="#' + visible[0].attr('id') + '"]').addClass('active');
236
+ });
237
+ selector.on('scrollSpy:exit', function() {
238
+ visible = $.grep(visible, function(value) {
239
+ return value.height() != 0;
240
+ });
241
+
242
+ if (visible[0]) {
243
+ $('a[href="#' + visible[0].attr('id') + '"]').removeClass('active');
244
+ var $this = $(this);
245
+ visible = $.grep(visible, function(value) {
246
+ return value.attr('id') != $this.attr('id');
247
+ });
248
+ if (visible[0]) { // Check if empty
249
+ $('a[href="#' + visible[0].attr('id') + '"]').addClass('active');
250
+ }
251
+ }
252
+ });
253
+
254
+ return selector;
255
+ };
256
+
257
+ /**
258
+ * Listen for window resize events
259
+ * @param {Object=} options Optional. Set { throttle: number } to change throttling. Default: 100 ms
260
+ * @returns {jQuery} $(window)
261
+ */
262
+ $.winSizeSpy = function(options) {
263
+ $.winSizeSpy = function() { return jWindow; }; // lock from multiple calls
264
+ options = options || {
265
+ throttle: 100
266
+ };
267
+ return jWindow.on('resize', throttle(onWinSize, options.throttle || 100));
268
+ };
269
+
270
+ /**
271
+ * Enables ScrollSpy on a collection of elements
272
+ * e.g. $('.scrollSpy').scrollSpy()
273
+ * @param {Object=} options Optional.
274
+ throttle : number -> scrollspy throttling. Default: 100 ms
275
+ offsetTop : number -> offset from top. Default: 0
276
+ offsetRight : number -> offset from right. Default: 0
277
+ offsetBottom : number -> offset from bottom. Default: 0
278
+ offsetLeft : number -> offset from left. Default: 0
279
+ * @returns {jQuery}
280
+ */
281
+ $.fn.scrollSpy = function(options) {
282
+ return $.scrollSpy($(this), options);
283
+ };
284
+
285
+ })(jQuery);