rawfeed 0.1.4 → 0.2.0
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 +4 -4
- data/LICENSE.txt +20 -21
- data/README.md +12 -130
- data/_data/options.yml +270 -0
- data/_data/resume.yml +8 -8
- data/_includes/alert +3 -1
- data/_includes/chart +13 -32
- data/_includes/details +1 -57
- data/_includes/image +12 -4
- data/_includes/layout/blog_search.html +6 -4
- data/_includes/layout/data.liquid +21 -3
- data/_includes/layout/disqus.html +12 -26
- data/_includes/layout/footer.html +33 -17
- data/_includes/layout/giscus.html +27 -19
- data/_includes/layout/head.html +41 -41
- data/_includes/layout/header.html +127 -101
- data/_includes/layout/maintenance.html +6 -10
- data/_includes/layout/paginator.html +6 -4
- data/_includes/socials +7 -5
- data/_includes/tabs +1 -94
- data/_includes/toc +11 -194
- data/_includes/video +4 -1
- data/_layouts/blog.html +8 -7
- data/_layouts/contact.html +90 -196
- data/_layouts/default.html +42 -341
- data/_layouts/error.html +6 -4
- data/_layouts/home.html +45 -36
- data/_layouts/licenses.html +10 -0
- data/_layouts/page.html +4 -4
- data/_layouts/pixel.html +48 -0
- data/_layouts/pixels.html +71 -1
- data/_layouts/post.html +28 -29
- data/_layouts/resume.html +41 -34
- data/_layouts/tag.html +14 -3
- data/_layouts/tag_posts.html +3 -3
- data/_sass/base/_index.scss +39 -3
- data/_sass/components/_badges.scss +10 -0
- data/_sass/components/_markdown.scss +8 -5
- data/_sass/includes/_footer.scss +5 -2
- data/_sass/includes/_header.scss +23 -19
- data/_sass/includes/_highlight.scss +20 -7
- data/_sass/includes/_maintenance.scss +2 -3
- data/_sass/includes/_terminal.scss +35 -12
- data/_sass/layouts/_blog.scss +13 -9
- data/_sass/layouts/_contact.scss +6 -5
- data/_sass/layouts/_default.scss +5 -5
- data/_sass/layouts/_index.scss +3 -0
- data/_sass/layouts/_licenses.scss +7 -0
- data/_sass/layouts/_page.scss +1 -0
- data/_sass/layouts/_pixel.scss +61 -0
- data/_sass/layouts/_pixels.scss +86 -0
- data/_sass/layouts/_post.scss +4 -11
- data/_sass/layouts/_resume.scss +16 -3
- data/_sass/layouts/_tag-posts.scss +1 -2
- data/_sass/layouts/_tag.scss +12 -1
- data/_sass/main.scss +16 -1
- data/_sass/theme/_dark.scss +8 -1
- data/_sass/theme/_light.scss +8 -1
- data/assets/images/blog/.keep +0 -0
- data/assets/images/pixels/luffy.jpg +0 -0
- data/assets/js/blog.coffee +102 -0
- data/assets/js/contact.coffee +105 -0
- data/assets/js/default.coffee +172 -0
- data/assets/js/discus.coffee +30 -0
- data/assets/js/fallback/README.md +3 -0
- data/assets/js/fallback/blog.js +113 -0
- data/assets/js/fallback/contact.js +116 -0
- data/assets/js/{default.js → fallback/default.js} +50 -0
- data/assets/js/fallback/discus.js +32 -0
- data/{_includes/layout/google_analytics.html → assets/js/fallback/google_analytics.js} +7 -3
- data/assets/js/fallback/home.js +275 -0
- data/assets/js/fallback/no_inframe.js +4 -0
- data/assets/js/fallback/page.js +423 -0
- data/assets/js/fallback/pixels.js +1 -0
- data/assets/js/fallback/resume.js +13 -0
- data/assets/js/fallback/tags.js +1 -0
- data/{_includes/layout/capture_scripts.liquid → assets/js/fallback/theme_load.js} +0 -2
- data/assets/js/google_analytics.coffee +24 -0
- data/assets/js/home.coffee +250 -0
- data/assets/js/no_inframe.coffee +9 -0
- data/assets/js/page.coffee +379 -0
- data/assets/js/pixels.coffee +2 -0
- data/assets/js/resume.coffee +9 -0
- data/assets/js/tags.coffee +2 -0
- data/assets/js/theme_load.coffee +6 -0
- data/assets/json/blog_search.json +2 -2
- data/lib/rawfeed/author.rb +59 -0
- data/lib/rawfeed/csp_filters.rb +3 -0
- data/lib/rawfeed/draft.rb +1 -1
- data/lib/rawfeed/layout.rb +7 -0
- data/lib/rawfeed/page.rb +2 -2
- data/lib/rawfeed/pixel.rb +32 -0
- data/lib/rawfeed/post.rb +2 -2
- data/lib/rawfeed/resume.rb +1 -0
- data/lib/rawfeed/typescript_liquid.rb +172 -0
- data/lib/rawfeed/utils.rb +1 -0
- data/lib/rawfeed/version.rb +1 -1
- data/lib/rawfeed/with_class.rb +20 -0
- data/lib/rawfeed.rb +5 -1
- metadata +44 -12
- data/assets/js/avatar.js +0 -59
- data/assets/js/terminal.js +0 -18
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
---
|
|
2
|
+
---
|
|
3
|
+
|
|
4
|
+
{%- include layout/data.liquid -%}
|
|
5
|
+
|
|
6
|
+
document.addEventListener("DOMContentLoaded", () => {
|
|
7
|
+
|
|
8
|
+
const terminal = document.getElementById("terminal");
|
|
9
|
+
|
|
10
|
+
if (terminal) {
|
|
11
|
+
|
|
12
|
+
// effects terminal: maximize
|
|
13
|
+
// ----------------------------------------------------------------------------------------------
|
|
14
|
+
const btnMax = terminal.querySelector(".terminal-header__max");
|
|
15
|
+
|
|
16
|
+
let isFullscreen = false;
|
|
17
|
+
|
|
18
|
+
// maximize/restore
|
|
19
|
+
btnMax.addEventListener("click", () => {
|
|
20
|
+
isFullscreen = !isFullscreen;
|
|
21
|
+
terminal.classList.toggle("terminal-fullscreen", isFullscreen);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// populate the terminal
|
|
25
|
+
// ----------------------------------------------------------------------------------------------
|
|
26
|
+
const socialsEl = document.getElementById("terminal-screen--socials");
|
|
27
|
+
const screen = document.getElementById("screen");
|
|
28
|
+
|
|
29
|
+
const commands = {
|
|
30
|
+
// Multiple string is the same as using ` in Javascript
|
|
31
|
+
help: `{{ home_.terminal.help.menu }}`,
|
|
32
|
+
about: document.getElementById("home-content").innerHTML,
|
|
33
|
+
socials: socialsEl ? socialsEl.innerHTML : "{{ home_.terminal.no_socials }}"
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const createInputLine = () => {
|
|
37
|
+
const line = document.createElement("div");
|
|
38
|
+
line.className = "line";
|
|
39
|
+
|
|
40
|
+
const prompt = document.createElement("span");
|
|
41
|
+
prompt.className = "prompt";
|
|
42
|
+
prompt.textContent = "[{{ home_.terminal.user }}@{{ home_.terminal.hostname }} ~]$";
|
|
43
|
+
|
|
44
|
+
// wrapper para conter input, cursor e measure
|
|
45
|
+
const wrapper = document.createElement("span");
|
|
46
|
+
wrapper.className = "input-wrapper";
|
|
47
|
+
|
|
48
|
+
const input = document.createElement("input");
|
|
49
|
+
input.type = "text";
|
|
50
|
+
input.className = "input";
|
|
51
|
+
input.placeholder = `{{ home_.terminal.welcome }}`;
|
|
52
|
+
input.spellcheck = false;
|
|
53
|
+
input.autocomplete = "off";
|
|
54
|
+
input.autocorrect = "off";
|
|
55
|
+
input.autocapitalize = "off";
|
|
56
|
+
|
|
57
|
+
const cursor = document.createElement("span");
|
|
58
|
+
cursor.className = "cursor";
|
|
59
|
+
|
|
60
|
+
const measure = document.createElement("span");
|
|
61
|
+
measure.className = "measure";
|
|
62
|
+
|
|
63
|
+
wrapper.appendChild(input);
|
|
64
|
+
wrapper.appendChild(cursor);
|
|
65
|
+
wrapper.appendChild(measure);
|
|
66
|
+
|
|
67
|
+
line.appendChild(prompt);
|
|
68
|
+
line.appendChild(wrapper);
|
|
69
|
+
screen.appendChild(line);
|
|
70
|
+
|
|
71
|
+
input.focus();
|
|
72
|
+
screen.scrollTop = screen.scrollHeight;
|
|
73
|
+
|
|
74
|
+
// Updates the fake cursor position based on the input"s selectionStart
|
|
75
|
+
const updateCursor = () => {
|
|
76
|
+
const sel = input.selectionStart || 0;
|
|
77
|
+
// measure the text to the position of the caret
|
|
78
|
+
measure.textContent = input.value.slice(0, sel);
|
|
79
|
+
const textWidth = measure.offsetWidth; // largura do texto sem scroll
|
|
80
|
+
const visibleLeft = textWidth - input.scrollLeft;
|
|
81
|
+
cursor.style.left = `${visibleLeft}px`;
|
|
82
|
+
|
|
83
|
+
// ensure the caret is visible (for long texts): adjust input"s scrollLeft
|
|
84
|
+
const paddingRight = 10;
|
|
85
|
+
if (textWidth - input.scrollLeft > input.clientWidth - paddingRight) {
|
|
86
|
+
input.scrollLeft = textWidth - input.clientWidth + paddingRight;
|
|
87
|
+
cursor.style.left = `${textWidth - input.scrollLeft}px`;
|
|
88
|
+
} else if (textWidth < input.scrollLeft) {
|
|
89
|
+
input.scrollLeft = textWidth;
|
|
90
|
+
cursor.style.left = `${textWidth - input.scrollLeft}px`;
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
// show/hide cursor animation as focus changes
|
|
95
|
+
const onFocus = () => {
|
|
96
|
+
cursor.style.opacity = "1";
|
|
97
|
+
updateCursor();
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const onBlur = () => {
|
|
101
|
+
cursor.style.opacity = "0";
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
input.addEventListener("input", updateCursor);
|
|
105
|
+
|
|
106
|
+
input.addEventListener("keydown", (e) => {
|
|
107
|
+
// Update position on keys that do not trigger input immediately (arrows, delete, etc.)
|
|
108
|
+
setTimeout(updateCursor, 0);
|
|
109
|
+
|
|
110
|
+
if (e.key === "Enter") {
|
|
111
|
+
e.preventDefault();
|
|
112
|
+
const cmd = input.value.trim().toLowerCase();
|
|
113
|
+
if (cmd) {
|
|
114
|
+
// remove input/cursor/measure and place fixed text
|
|
115
|
+
wrapper.removeChild(input);
|
|
116
|
+
wrapper.removeChild(cursor);
|
|
117
|
+
wrapper.removeChild(measure);
|
|
118
|
+
const cmdText = document.createElement("span");
|
|
119
|
+
cmdText.textContent = cmd;
|
|
120
|
+
wrapper.appendChild(cmdText);
|
|
121
|
+
processCommand(cmd);
|
|
122
|
+
} else {
|
|
123
|
+
// if you enter without command, it just creates a new empty line (with prompt)
|
|
124
|
+
wrapper.removeChild(input);
|
|
125
|
+
wrapper.removeChild(cursor);
|
|
126
|
+
wrapper.removeChild(measure);
|
|
127
|
+
const blank = document.createElement("span");
|
|
128
|
+
blank.textContent = "";
|
|
129
|
+
wrapper.appendChild(blank);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// New line input
|
|
133
|
+
createInputLine();
|
|
134
|
+
|
|
135
|
+
} else if (e.key === "Escape") {
|
|
136
|
+
e.preventDefault();
|
|
137
|
+
screen.innerHTML = "";
|
|
138
|
+
createInputLine();
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
// arrows, mouse click, mouseup (position caret), etc.
|
|
143
|
+
input.addEventListener("keyup", updateCursor);
|
|
144
|
+
input.addEventListener("click", () => setTimeout(updateCursor, 0));
|
|
145
|
+
input.addEventListener("mouseup", () => setTimeout(updateCursor, 0));
|
|
146
|
+
input.addEventListener("focus", onFocus);
|
|
147
|
+
input.addEventListener("blur", onBlur);
|
|
148
|
+
|
|
149
|
+
updateCursor();
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
// function show date
|
|
153
|
+
function dateShow() {
|
|
154
|
+
const langBrowser = navigator.language || navigator.userLanguage;
|
|
155
|
+
|
|
156
|
+
const options = {
|
|
157
|
+
weekday: 'long',
|
|
158
|
+
year: 'numeric',
|
|
159
|
+
month: 'long',
|
|
160
|
+
day: 'numeric',
|
|
161
|
+
hour: '2-digit',
|
|
162
|
+
minute: '2-digit'
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
let brMessage = "";
|
|
166
|
+
|
|
167
|
+
if (langBrowser === "pt-BR" ) {
|
|
168
|
+
brMessage = " (Horário de Brasília)";
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
try {
|
|
172
|
+
return new Date().toLocaleString(langBrowser, options) + brMessage;
|
|
173
|
+
} catch (e) {
|
|
174
|
+
// Fallback for en-US
|
|
175
|
+
return new Date().toLocaleString('en-US', options) + brMessage;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// processes commands
|
|
180
|
+
const processCommand = (cmd) => {
|
|
181
|
+
if (cmd === "help") {
|
|
182
|
+
helpWriteHTML(commands.help, "html");
|
|
183
|
+
} else if (cmd === "date") {
|
|
184
|
+
commandsPrint(dateShow(), "text");
|
|
185
|
+
// else if (cmd.startsWith("echo "))
|
|
186
|
+
// commandsPrint(cmd.split(" ").slice(1).join(" "))
|
|
187
|
+
} else if (cmd === "about") {
|
|
188
|
+
aboutWriteHTML(commands.about);
|
|
189
|
+
} else if (cmd === "socials") {
|
|
190
|
+
socialsWriteHTML(commands.socials);
|
|
191
|
+
} else if (cmd === "clear") {
|
|
192
|
+
screen.innerHTML = "";
|
|
193
|
+
} else if (cmd) {
|
|
194
|
+
commandsPrint(cmd + `{{ home_.terminal.error }}`, "text");
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const helpWriteHTML = (text, mode = "html") => {
|
|
199
|
+
const wrapper = document.createElement("div");
|
|
200
|
+
wrapper.className = "line-wrapper";
|
|
201
|
+
const helpDescription = document.createElement("span");
|
|
202
|
+
helpDescription.className = "help-description";
|
|
203
|
+
helpDescription.innerHTML = `{{ home_.terminal.help.description | markdownify }}`;
|
|
204
|
+
const helpCommandTitle = document.createElement("span");
|
|
205
|
+
helpCommandTitle.className = "help-commands__title";
|
|
206
|
+
helpCommandTitle.textContent = "{{ home_.terminal.help.commands.title }}";
|
|
207
|
+
const helpCommandDesc = document.createElement("span");
|
|
208
|
+
helpCommandDesc.className = "help-commands__desc";
|
|
209
|
+
helpCommandDesc.textContent = `{{ home_.terminal.help.commands.description }}`;
|
|
210
|
+
|
|
211
|
+
wrapper.appendChild(helpDescription);
|
|
212
|
+
wrapper.appendChild(helpCommandTitle);
|
|
213
|
+
wrapper.appendChild(helpCommandDesc);
|
|
214
|
+
|
|
215
|
+
text.split("\n").forEach((t) => {
|
|
216
|
+
const line = document.createElement("div");
|
|
217
|
+
line.className = "line";
|
|
218
|
+
if (mode === "html") line.innerHTML = t; else line.textContent = t;
|
|
219
|
+
wrapper.appendChild(line);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
screen.appendChild(wrapper);
|
|
223
|
+
screen.scrollTop = screen.scrollHeight;
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
const socialsWriteHTML = (content, mode = "html") => {
|
|
227
|
+
const wrapper = document.createElement("div");
|
|
228
|
+
wrapper.className = "line-wrapper";
|
|
229
|
+
const socialsCommandDesc = document.createElement("span");
|
|
230
|
+
socialsCommandDesc.className = "socials-command__desc";
|
|
231
|
+
socialsCommandDesc.textContent = `{{ socials_.description.terminal.command.description }}`;
|
|
232
|
+
|
|
233
|
+
screen.appendChild(socialsCommandDesc);
|
|
234
|
+
|
|
235
|
+
if (mode === "html") wrapper.innerHTML = content; else wrapper.textContent = content;
|
|
236
|
+
|
|
237
|
+
screen.appendChild(wrapper);
|
|
238
|
+
screen.scrollTop = screen.scrollHeight;
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
const aboutWriteHTML = (content, mode = "html") => {
|
|
242
|
+
const wrapper = document.createElement("div");
|
|
243
|
+
wrapper.className = "line-wrapper";
|
|
244
|
+
if (mode === "html") wrapper.innerHTML = content; else wrapper.textContent = content;
|
|
245
|
+
screen.appendChild(wrapper);
|
|
246
|
+
screen.scrollTop = screen.scrollHeight;
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
const commandsPrint = (text, mode = "html") => {
|
|
250
|
+
// creates the wrapper to group all the lines
|
|
251
|
+
const wrapper = document.createElement("div");
|
|
252
|
+
wrapper.className = "line-wrapper";
|
|
253
|
+
|
|
254
|
+
text.split("\n").forEach((t) => {
|
|
255
|
+
const line = document.createElement("div");
|
|
256
|
+
line.className = "line";
|
|
257
|
+
if (mode === "html") line.innerHTML = t; else line.textContent = t;
|
|
258
|
+
wrapper.appendChild(line);
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
screen.appendChild(wrapper);
|
|
262
|
+
screen.scrollTop = screen.scrollHeight;
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
// start terminal
|
|
266
|
+
createInputLine();
|
|
267
|
+
|
|
268
|
+
// when clicking on the terminal, it always focuses on the last existing input
|
|
269
|
+
terminal.addEventListener("click", (e) => {
|
|
270
|
+
// avoids focusing when clicking a header button, etc.
|
|
271
|
+
const lastInput = screen.querySelector(".input:last-of-type");
|
|
272
|
+
if (lastInput) lastInput.focus();
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
});
|
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
---
|
|
2
|
+
---
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
document.addEventListener("DOMContentLoaded", () => {
|
|
6
|
+
|
|
7
|
+
/* details
|
|
8
|
+
# ------------------------------------------------------------------------------------------------
|
|
9
|
+
*/
|
|
10
|
+
const detailsStart = document.getElementById("details-start");
|
|
11
|
+
|
|
12
|
+
if (detailsStart) {
|
|
13
|
+
if (window.__jekyll_details_setup) return;
|
|
14
|
+
window.__jekyll_details_setup = true;
|
|
15
|
+
|
|
16
|
+
function initDetails(){
|
|
17
|
+
const starts = document.querySelectorAll('.details-start');
|
|
18
|
+
starts.forEach(start => {
|
|
19
|
+
const summary = start.getAttribute('data-summary') || 'Detalhes';
|
|
20
|
+
|
|
21
|
+
let end = start.nextSibling;
|
|
22
|
+
while(end && !(end.nodeType === 1 && end.classList.contains('details-end'))){
|
|
23
|
+
end = end.nextSibling;
|
|
24
|
+
}
|
|
25
|
+
if(!end) return;
|
|
26
|
+
|
|
27
|
+
let node = start.nextSibling;
|
|
28
|
+
const content = [];
|
|
29
|
+
while(node && node !== end){
|
|
30
|
+
const next = node.nextSibling;
|
|
31
|
+
if(node.nodeType === Node.ELEMENT_NODE || (node.nodeType === Node.TEXT_NODE && node.textContent.trim())){
|
|
32
|
+
content.push(node.cloneNode(true));
|
|
33
|
+
}
|
|
34
|
+
node = next;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const details = document.createElement('details');
|
|
38
|
+
const sum = document.createElement('summary');
|
|
39
|
+
sum.textContent = summary;
|
|
40
|
+
details.appendChild(sum);
|
|
41
|
+
|
|
42
|
+
const wrapper = document.createElement('div');
|
|
43
|
+
wrapper.className = 'details-content-wrapper';
|
|
44
|
+
|
|
45
|
+
content.forEach(el => wrapper.appendChild(el));
|
|
46
|
+
|
|
47
|
+
details.appendChild(wrapper);
|
|
48
|
+
|
|
49
|
+
start.parentNode.insertBefore(details, start);
|
|
50
|
+
let cur = start;
|
|
51
|
+
while(cur){
|
|
52
|
+
const next = cur.nextSibling;
|
|
53
|
+
cur.remove();
|
|
54
|
+
if(cur === end) break;
|
|
55
|
+
cur = next;
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if(document.readyState === 'loading')
|
|
61
|
+
document.addEventListener('DOMContentLoaded', initDetails);
|
|
62
|
+
else
|
|
63
|
+
initDetails();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/* tabs
|
|
67
|
+
# ------------------------------------------------------------------------------------------------
|
|
68
|
+
*/
|
|
69
|
+
const tabsStart = document.getElementById("tabs-start");
|
|
70
|
+
|
|
71
|
+
if (tabsStart) {
|
|
72
|
+
if (window.__simple_tabs_installed) return;
|
|
73
|
+
window.__simple_tabs_installed = true;
|
|
74
|
+
|
|
75
|
+
function processTabs() {
|
|
76
|
+
var starts = Array.from(document.querySelectorAll('.tabs-start'));
|
|
77
|
+
starts.forEach(function (start) {
|
|
78
|
+
var end = start.nextSibling;
|
|
79
|
+
while (end && !(end.nodeType === 1 && end.classList && end.classList.contains('tabs-end'))) {
|
|
80
|
+
end = end.nextSibling;
|
|
81
|
+
}
|
|
82
|
+
if (!end) return;
|
|
83
|
+
|
|
84
|
+
var node = start.nextSibling;
|
|
85
|
+
var tabs = [];
|
|
86
|
+
var currentTab = null;
|
|
87
|
+
while (node && node !== end) {
|
|
88
|
+
var next = node.nextSibling;
|
|
89
|
+
if (node.nodeType === Node.TEXT_NODE && !node.textContent.trim()) {
|
|
90
|
+
node = next; continue;
|
|
91
|
+
}
|
|
92
|
+
var text = (node.textContent || '').trim();
|
|
93
|
+
var m = text.match(/^\s*tab\d*\s*:\s*(.+)$/i);
|
|
94
|
+
if (m) {
|
|
95
|
+
currentTab = { title: m[1].trim(), nodes: [] };
|
|
96
|
+
tabs.push(currentTab);
|
|
97
|
+
if (node.parentNode) node.parentNode.removeChild(node);
|
|
98
|
+
} else if (currentTab) {
|
|
99
|
+
currentTab.nodes.push(node);
|
|
100
|
+
} else {
|
|
101
|
+
}
|
|
102
|
+
node = next;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (tabs.length === 0) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
var wrap = document.createElement('div');
|
|
110
|
+
wrap.className = 'tabs-wrap';
|
|
111
|
+
|
|
112
|
+
var nav = document.createElement('div');
|
|
113
|
+
nav.className = 'tabs-nav';
|
|
114
|
+
|
|
115
|
+
var panels = document.createElement('div');
|
|
116
|
+
panels.className = 'tabs-panels';
|
|
117
|
+
|
|
118
|
+
tabs.forEach(function (tab, i) {
|
|
119
|
+
var btn = document.createElement('button');
|
|
120
|
+
btn.type = 'button';
|
|
121
|
+
btn.className = 'tab-btn' + (i === 0 ? ' active' : '');
|
|
122
|
+
btn.setAttribute('data-idx', i);
|
|
123
|
+
btn.textContent = tab.title;
|
|
124
|
+
btn.addEventListener('click', function () {
|
|
125
|
+
var idx = +this.getAttribute('data-idx');
|
|
126
|
+
wrap.querySelectorAll('.tab-btn').forEach(function (b) {
|
|
127
|
+
b.classList.toggle('active', +b.getAttribute('data-idx') === idx);
|
|
128
|
+
});
|
|
129
|
+
wrap.querySelectorAll('.tab-panel').forEach(function (p, pi) {
|
|
130
|
+
p.classList.toggle('active', pi === idx);
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
nav.appendChild(btn);
|
|
134
|
+
|
|
135
|
+
var panel = document.createElement('div');
|
|
136
|
+
panel.className = 'tab-panel' + (i === 0 ? ' active' : '');
|
|
137
|
+
tab.nodes.forEach(function (n) {
|
|
138
|
+
panel.appendChild(n.cloneNode(true));
|
|
139
|
+
});
|
|
140
|
+
panels.appendChild(panel);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
wrap.appendChild(nav);
|
|
144
|
+
wrap.appendChild(panels);
|
|
145
|
+
|
|
146
|
+
start.parentNode.insertBefore(wrap, start);
|
|
147
|
+
|
|
148
|
+
var cur = start;
|
|
149
|
+
while (cur) {
|
|
150
|
+
var nx = cur.nextSibling;
|
|
151
|
+
if (cur.parentNode) cur.parentNode.removeChild(cur);
|
|
152
|
+
if (cur === end) break;
|
|
153
|
+
cur = nx;
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', processTabs);
|
|
159
|
+
else processTabs();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/* chart
|
|
163
|
+
# ------------------------------------------------------------------------------------------------
|
|
164
|
+
*/
|
|
165
|
+
|
|
166
|
+
const chart_elements = document.querySelectorAll('[id^="chart-"]');
|
|
167
|
+
|
|
168
|
+
for (const ctx of chart_elements) {
|
|
169
|
+
const data = ctx.dataset;
|
|
170
|
+
|
|
171
|
+
new Chart(ctx, {
|
|
172
|
+
type: data.type,
|
|
173
|
+
data: {
|
|
174
|
+
labels: data.labels.split(","),
|
|
175
|
+
datasets: [
|
|
176
|
+
{
|
|
177
|
+
label: data.label,
|
|
178
|
+
data: data.data.split(",").map(Number),
|
|
179
|
+
borderColor: data.color,
|
|
180
|
+
backgroundColor: `${data.color}33`,
|
|
181
|
+
fill: true,
|
|
182
|
+
tension: 0.3,
|
|
183
|
+
borderWidth: 2,
|
|
184
|
+
pointRadius: 4,
|
|
185
|
+
pointHoverRadius: 6
|
|
186
|
+
}
|
|
187
|
+
]
|
|
188
|
+
},
|
|
189
|
+
options: {
|
|
190
|
+
responsive: true,
|
|
191
|
+
plugins: {
|
|
192
|
+
legend: {
|
|
193
|
+
display: true,
|
|
194
|
+
labels: {
|
|
195
|
+
color: "#444444"
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
scales: {
|
|
200
|
+
x: {
|
|
201
|
+
ticks: {
|
|
202
|
+
color: "#131313"
|
|
203
|
+
},
|
|
204
|
+
grid: {
|
|
205
|
+
color: "#111111"
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
y: {
|
|
209
|
+
ticks: {
|
|
210
|
+
color: "#131313"
|
|
211
|
+
},
|
|
212
|
+
grid: {
|
|
213
|
+
color: "#111111"
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/* TOC
|
|
222
|
+
# ------------------------------------------------------------------------------------------------
|
|
223
|
+
*/
|
|
224
|
+
|
|
225
|
+
const toc = document.getElementById('toc');
|
|
226
|
+
if (toc) {
|
|
227
|
+
// Variável global de largura minima do TOC
|
|
228
|
+
const minLayoutWidth = 1830;
|
|
229
|
+
|
|
230
|
+
const sentinel = document.createElement('div');
|
|
231
|
+
toc.parentNode.insertBefore(sentinel, toc);
|
|
232
|
+
|
|
233
|
+
const shouldApplyFixed = () => window.innerWidth > minLayoutWidth;
|
|
234
|
+
|
|
235
|
+
const observer = new IntersectionObserver(([entry]) => {
|
|
236
|
+
if (shouldApplyFixed()) {
|
|
237
|
+
if (!entry.isIntersecting) {
|
|
238
|
+
toc.classList.add('toc-fixed');
|
|
239
|
+
} else {
|
|
240
|
+
toc.classList.remove('toc-fixed');
|
|
241
|
+
}
|
|
242
|
+
} else {
|
|
243
|
+
toc.classList.remove('toc-fixed');
|
|
244
|
+
}
|
|
245
|
+
}, { threshold: 0 });
|
|
246
|
+
|
|
247
|
+
observer.observe(sentinel);
|
|
248
|
+
|
|
249
|
+
window.addEventListener('resize', () => {
|
|
250
|
+
if (!shouldApplyFixed()) toc.classList.remove('toc-fixed');
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
const slugify = (text) => {
|
|
254
|
+
if (!text) return '';
|
|
255
|
+
return text.toString().toLowerCase().trim()
|
|
256
|
+
.normalize('NFKD').replace(/[\u0300-\u036f]/g, '')
|
|
257
|
+
.replace(/[^\w\s-]/g, '')
|
|
258
|
+
.replace(/\s+/g, '-')
|
|
259
|
+
.replace(/--+/g, '-');
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
const buildTOC = (tocEl) => {
|
|
263
|
+
const selector = tocEl.dataset.tocSelector || '.post-content' || '.page-content';
|
|
264
|
+
const maxLevel = parseInt(tocEl.dataset.tocMaxLevel || '3', 10);
|
|
265
|
+
const offset = parseInt(tocEl.dataset.tocScrollOffset || '20', 10);
|
|
266
|
+
|
|
267
|
+
const root = document.querySelector(selector);
|
|
268
|
+
|
|
269
|
+
if (!root) {
|
|
270
|
+
tocEl.querySelector('.toc-empty').textContent = `Content not found (${selector})`;
|
|
271
|
+
tocEl.querySelector('.toc-empty').style.display = 'block';
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const headings = Array.from(
|
|
276
|
+
root.querySelectorAll(Array(maxLevel).fill(0).map((_, i) => `h${i + 1}`).join(','))
|
|
277
|
+
)
|
|
278
|
+
.filter((h) => !tocEl.contains(h))
|
|
279
|
+
.filter((h) => parseInt(h.tagName.substring(1)) <= maxLevel);
|
|
280
|
+
|
|
281
|
+
if (headings.length === 0) return;
|
|
282
|
+
|
|
283
|
+
const tocRoot = tocEl.querySelector('.toc-list');
|
|
284
|
+
tocRoot.innerHTML = '';
|
|
285
|
+
|
|
286
|
+
const idCounts = {};
|
|
287
|
+
for (const h of headings) {
|
|
288
|
+
if (!h.id) {
|
|
289
|
+
let id = slugify(h.textContent);
|
|
290
|
+
if (!id) id = 'section';
|
|
291
|
+
|
|
292
|
+
if (idCounts[id]) {
|
|
293
|
+
idCounts[id] += 1;
|
|
294
|
+
id = `${id}-${idCounts[id]}`;
|
|
295
|
+
} else {
|
|
296
|
+
idCounts[id] = 1;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
h.id = id;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
const stack = [{ level: 0, ul: tocRoot }];
|
|
304
|
+
for (let i = 0; i < headings.length; i++) {
|
|
305
|
+
const h = headings[i];
|
|
306
|
+
const level = parseInt(h.tagName.substring(1));
|
|
307
|
+
const li = document.createElement('li');
|
|
308
|
+
const a = document.createElement('a');
|
|
309
|
+
a.href = `#${h.id}`;
|
|
310
|
+
a.textContent = h.textContent.trim();
|
|
311
|
+
|
|
312
|
+
a.addEventListener('click', (e) => {
|
|
313
|
+
e.preventDefault();
|
|
314
|
+
window.scrollTo({
|
|
315
|
+
top: h.getBoundingClientRect().top + window.scrollY - offset,
|
|
316
|
+
behavior: 'smooth'
|
|
317
|
+
});
|
|
318
|
+
history.replaceState(null, '', `#${h.id}`);
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
li.appendChild(a);
|
|
322
|
+
|
|
323
|
+
while (stack.length > 1 && level <= stack[stack.length - 1].level) {
|
|
324
|
+
stack.pop();
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const parent = stack[stack.length - 1].ul;
|
|
328
|
+
parent.appendChild(li);
|
|
329
|
+
|
|
330
|
+
const next = headings[i + 1];
|
|
331
|
+
if (next) {
|
|
332
|
+
const nextLevel = parseInt(next.tagName.substring(1));
|
|
333
|
+
if (nextLevel > level) {
|
|
334
|
+
const newUl = document.createElement('ul');
|
|
335
|
+
li.appendChild(newUl);
|
|
336
|
+
stack.push({ level, ul: newUl });
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
const links = tocRoot.querySelectorAll('a');
|
|
342
|
+
const onScroll = () => {
|
|
343
|
+
const fromTop = window.scrollY + offset + 1;
|
|
344
|
+
let current = headings[0];
|
|
345
|
+
|
|
346
|
+
for (const h of headings) {
|
|
347
|
+
if (h.offsetTop <= fromTop) current = h;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
for (const l of links) {
|
|
351
|
+
l.classList.toggle('active', l.getAttribute('href') === `#${current.id}`);
|
|
352
|
+
}
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
window.addEventListener('scroll', onScroll, { passive: true });
|
|
356
|
+
onScroll();
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
for (const tocEl of document.querySelectorAll('.toc')) {
|
|
360
|
+
// Obtém os textos dos botões do dataset (agora dinâmicos)
|
|
361
|
+
const btnShowText = tocEl.dataset.btnShow || 'Show';
|
|
362
|
+
const btnHiddenText = tocEl.dataset.btnHidden || 'Hide';
|
|
363
|
+
|
|
364
|
+
buildTOC(tocEl);
|
|
365
|
+
|
|
366
|
+
const toggle = tocEl.querySelector('.toc-toggle');
|
|
367
|
+
const wrapper = tocEl.querySelector('.toc-list-wrapper');
|
|
368
|
+
|
|
369
|
+
wrapper.style.display = 'none';
|
|
370
|
+
toggle.setAttribute('aria-expanded', 'false');
|
|
371
|
+
toggle.textContent = btnShowText;
|
|
372
|
+
|
|
373
|
+
toggle.addEventListener('click', () => {
|
|
374
|
+
const expanded = toggle.getAttribute('aria-expanded') === 'true';
|
|
375
|
+
wrapper.style.display = expanded ? 'none' : 'block';
|
|
376
|
+
toggle.setAttribute('aria-expanded', (!expanded).toString());
|
|
377
|
+
// Define o texto dinamicamente
|
|
378
|
+
toggle.textContent = expanded ? btnShowText : btnHiddenText;
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
const tocTop = tocEl.offsetTop;
|
|
382
|
+
|
|
383
|
+
const handleScrollFix = () => {
|
|
384
|
+
if (window.innerWidth <= minLayoutWidth) {
|
|
385
|
+
tocEl.classList.remove('fixed');
|
|
386
|
+
tocEl.style.position = '';
|
|
387
|
+
tocEl.style.top = '';
|
|
388
|
+
tocEl.style.zIndex = '';
|
|
389
|
+
tocEl.style.width = '';
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
const scrollTop = window.scrollY || document.documentElement.scrollTop;
|
|
394
|
+
|
|
395
|
+
if (scrollTop >= tocTop) {
|
|
396
|
+
tocEl.classList.add('fixed');
|
|
397
|
+
tocEl.style.position = 'fixed';
|
|
398
|
+
tocEl.style.top = '0';
|
|
399
|
+
tocEl.style.zIndex = '9999';
|
|
400
|
+
} else {
|
|
401
|
+
tocEl.classList.remove('fixed');
|
|
402
|
+
tocEl.style.position = '';
|
|
403
|
+
tocEl.style.top = '';
|
|
404
|
+
tocEl.style.width = '';
|
|
405
|
+
}
|
|
406
|
+
};
|
|
407
|
+
|
|
408
|
+
// fechar TOC ao pressionar 'Esc'
|
|
409
|
+
document.addEventListener('keydown', (e) => {
|
|
410
|
+
if (e.key === 'Escape') {
|
|
411
|
+
wrapper.style.display = 'none';
|
|
412
|
+
toggle.setAttribute('aria-expanded', 'false');
|
|
413
|
+
toggle.textContent = btnShowText;
|
|
414
|
+
}
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
window.addEventListener('scroll', handleScrollFix, { passive: true });
|
|
418
|
+
window.addEventListener('resize', handleScrollFix);
|
|
419
|
+
handleScrollFix();
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
document.addEventListener("DOMContentLoaded", () => {});
|