forked from xsokev/Dojo-Bootstrap
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathScrollSpy.js
executable file
·126 lines (116 loc) · 4.67 KB
/
ScrollSpy.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
/* ==========================================================
* ScrollSpy.js v2.0.0
* ==========================================================
* Copyright 2012 xsokev
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ========================================================== */
define([
"./Support",
"./_BootstrapWidget",
"dojo/_base/declare",
"dojo/query",
"dojo/on",
"dojo/_base/lang",
"dojo/_base/window",
"dojo/dom-attr",
"dojo/dom-geometry",
"dojo/NodeList-traverse"
], function (support, _BootstrapWidget, declare, query, on, lang, win, domAttr, domGeom) {
"use strict";
// module:
// ScrollSpy
var _offsets, _targets;
// summary:
// Bootstrap template for creating a widget that uses a template
return declare("ScrollSpy", [_BootstrapWidget], {
// offset: Integer
// distance from scroll top which triggers selection of corresponding anchor
offset: 10,
// target: String
// the navigation controls to activate
target: "",
_setTargetAttr: function (val) {
if (val) {
this._set("target", val);
} else {
this._set("target", support.hrefValue(this.domNode));
}
this.target += ' li > a';
},
postCreate: function () {
// summary:
//
// tags:
// private
this.scrollNode = this.domNode.tagName === 'BODY' ? win.global : this.domNode;
this.own(on(this.scrollNode, 'scroll', lang.hitch(this, 'process')));
this.refresh();
this.process();
},
process: function () {
var domPos = domGeom.position(this.domNode, false);
var scrollTop = this.domNode.scrollTop + parseInt(this.offset, 10);
var scrollHeight = this.domNode.scrollHeight || win.body().scrollHeight;
//TODO: Test across all browsers
var domHeight = (this.domNode.tagName === "BODY") ? this.scrollNode.innerHeight : domPos.h;
var maxScroll = scrollHeight - domHeight;
var activeTarget = this.activeTarget;
var i;
if (scrollTop >= maxScroll) {
return activeTarget !== (i = _targets[_targets.length - 1]) && this.activate(i);
}
for (i = _offsets.length; i--;) {
if (activeTarget !== _targets[i] && scrollTop >= _offsets[i] && (!_offsets[i + 1] || scrollTop <= _offsets[i + 1])) {
this.activate(_targets[i]);
}
}
},
refresh: function () {
_offsets = [];
_targets = [];
var container_offset = domGeom.position(this.domNode, false).y;
query(this.target).map(function (node) {
var href = support.getData(node, 'target') || domAttr.get(node, 'href');
var target = /^#\w/.test(href) && query(href);
var pos;
if (target[0]) {
pos = domGeom.position(target[0], false);
}
return (target && target.length && [ pos.y - container_offset, href ] ) || null;
})
.filter(function (item) {
return item;
})
.sort(function (a, b) {
return a[0] - b[0];
})
.forEach(function (item) {
_offsets.push(item[0]);
_targets.push(item[1]);
}, this);
},
activate: function (elm) {
this.activeTarget = elm;
query(this.target).parent('.active').removeClass('active');
var selector = this.target + '[data-target="' + elm + '"],' + this.target + '[href="' + elm + '"]';
var active = query(selector).parent('li').addClass('active');
if (active.parent('.dropdown-menu').length) {
active = active.closest('li.dropdown').addClass('active');
}
if(active && active[0]){
this.emit('activate', lang.mixin(support.eventObject, {relatedTarget: query(elm)[0], target: active[0]}));
}
}
});
});