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