forked from rooreynolds/jargone
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathjargone.js.post
155 lines (141 loc) · 5.65 KB
/
jargone.js.post
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
],
wordsLen = words.length,
idx;
function addEvent(elem, eventType, handler) {
if (elem.addEventListener) {
elem.addEventListener (eventType, handler, false);
} else if (elem.attachEvent) {
handler = function (e) {
var target = (typeof e.target === 'undefined') ? e.srcElement : e.target;
handler.call(target, { 'target' : target });
};
elem.attachEvent ('on' + eventType, handler);
} else {
return false;
}
};
var popup = {
add : function (element, notes, idx) {
var popup;
popup = document.createElement("div");
popup.id = "jargonepopup-" + (idx + 1);
popup.className = "jargonepopup";
document.body.appendChild(popup);
popup.innerHTML = notes;
popup.style.left = element.getBoundingClientRect().left + 'px';
popup.style.top = element.getBoundingClientRect().top + 20 + 'px';
popup.style.visibility = 'visible';
element.setAttribute('aria-describedby', popup.id);
this.current.idx = (idx + 1);
this.current.element = element;
},
remove : function () {
var popup = document.getElementById("jargonepopup-" + this.current.idx);
if (popup) {
document.body.removeChild(popup);
this.current.element.removeAttribute('aria-describedby');
this.current.idx = null;
this.current.element = null;
}
},
current : {
idx : null,
element : null
}
};
var popupEvt = (function () {
var openIdx = null,
focusedWord = null;
return (function (e) {
var element = e.target,
term;
if (!element.className || !element.className.match(/jargonehighlight/)) { return; }
if ((openIdx !== null) || (e.type === 'focusout')) {
popup.remove();
focusedElement = null;
} else {
term = element.firstChild.nodeValue.toLowerCase();
for (idx = 0; idx < wordsLen; idx++) {
if (term.match(new RegExp(words[idx][0])) && words[idx][1]) {
// clicks give focus so use it for capturing both events
// focus is retained by elements when scrolling clears their popup so use clicks as backup
if (e.type === 'click') {
if ((focusedWord === element) && (popup.current.element === null)) {
popup.add(element, words[idx][1], idx);
}
} else { // focusin
focusedWord = element;
popup.add(element, words[idx][1], idx);
}
}
}
}
if (event.stopPropagation) {
event.stopPropagation();
} else {
event.cancelBubble = true;
}
});
}());
// From http://james.padolsey.com/javascript/find-and-replace-text-with-javascript/
function findAndReplace(searchText, replacement, searchNode) {
if (!searchText || typeof replacement === 'undefined') {
// Throw error here if you want...
return;
}
var regex = typeof searchText === 'string' ?
new RegExp(searchText, 'g') : searchText,
childNodes = (searchNode || document.body).childNodes,
cnLength = childNodes.length,
excludes = 'html,head,style,title,link,meta,script,object,iframe';
while (cnLength--) {
var currentNode = childNodes[cnLength];
if (currentNode.nodeType === 1 &&
(excludes + ',').indexOf(currentNode.nodeName.toLowerCase() + ',') === -1) {
arguments.callee(searchText, replacement, currentNode);
}
if (currentNode.nodeType !== 3 || !regex.test(currentNode.data) ) {
continue;
}
var parent = currentNode.parentNode,
frag = (function(){
var html = currentNode.data.replace(regex, replacement),
wrap = document.createElement('div'),
frag = document.createDocumentFragment();
wrap.innerHTML = html;
while (wrap.firstChild) {
frag.appendChild(wrap.firstChild);
}
return frag;
})();
parent.insertBefore(frag, currentNode);
parent.removeChild(currentNode);
}
}
var css = document.createElement("style");
css.type = "text/css";
css.innerHTML = ".jargonehighlight { background-color: #FFFF88 !important; color: black; } .jargonehasnotes { cursor: help; border-bottom:1px dashed !important; } .jargonepopup { position: fixed; z-index: 1000 !important; visibility: hidden; background-color: #FFFFCC; color: black; border: solid silver 1px; margin: 5px; padding: 6px;} ";
document.getElementsByTagName("head")[0].appendChild(css);
for (idx = 0; idx < wordsLen; idx++) { // for each word
words[idx][0] = words[idx][0].replace(/([.*+?^=!:${}()|[\]\/\\])/g, "\\$1");
var pattern = '\\b' + words[idx][0];
if (pattern.slice(-6) == '\\.\\.\\.') { // don't include end word boundary check if word ended with '...'
pattern = pattern.slice(0, -6);
words[idx][0] = words[idx][0].slice(0, -6);
} else {
if (pattern.slice(-1) != '.') {
pattern = pattern + '\\b';
}
}
var regex = new RegExp('(' + pattern + ')', 'ig');
if (words[idx].length > 0 && words[idx][1] != undefined) {
findAndReplace( regex, '<span class="jargonehighlight jargonehasnotes" tabindex="0">$1<\/span>');
} else { // only use jargonehasnotes class if the entry has associated notes
findAndReplace( regex, '<span class="jargonehighlight" tabindex="0">$1<\/span>');
}
}
addEvent(document, 'focusin', popupEvt);
addEvent(document, 'focusout', popupEvt);
addEvent(document, 'click', popupEvt);
addEvent(document, 'scroll', function () { popup.remove(); });
})();