Skip to content

Commit 638dc02

Browse files
committed
Initial blog
0 parents  commit 638dc02

File tree

187 files changed

+6308
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

187 files changed

+6308
-0
lines changed

.gitignore

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
node_modules
2+
out
3+
public
4+
resources
5+
.DS_Store
6+
package-lock.json
7+
.now
8+
.env
9+
.lighthouseci
10+
11+
.vercel
12+
.hugo_build.lock

.prettierrc

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"tabWidth": 2,
3+
"useTabs": false
4+
}

LICENSE

+674
Large diffs are not rendered by default.

assets/js/scripts.js

+316
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,316 @@
1+
const $ = selector => document.querySelector(selector)
2+
const $$ = selector => document.querySelectorAll(selector)
3+
4+
const loadedScripts = []
5+
6+
function loadScript(src) {
7+
if (loadedScripts.includes(src)) return Promise.resolve()
8+
9+
return new Promise(function (resolve, reject) {
10+
const s = document.createElement('script')
11+
let r = false
12+
s.type = 'text/javascript'
13+
s.src = src
14+
s.async = true
15+
s.onerror = function (err) {
16+
reject(err, s)
17+
}
18+
s.onload = s.onreadystatechange = function () {
19+
// console.log(this.readyState); // uncomment this line to see which ready states are called.
20+
if (!r && (!this.readyState || this.readyState === 'complete')) {
21+
r = true
22+
loadedScripts.push(src)
23+
resolve()
24+
}
25+
}
26+
const t = document.getElementsByTagName('script')[0]
27+
t.parentElement.insertBefore(s, t)
28+
})
29+
}
30+
31+
// youtube functionality
32+
function createYoutubeFrame(id) {
33+
const html =
34+
"<div id='lightbox'><a href='#'><svg width='48' height='48' viewBox='0 0 24 24' fill='none' stroke='#fff' stroke-width='1' stroke-linecap='square' stroke-linejoin='arcs'><line x1='18' y1='6' x2='6' y2='18'></line><line x1='6' y1='6' x2='18' y2='18'></line></svg></a> <section> <div> <iframe src='https://www.youtube.com/embed/" +
35+
id +
36+
"?autoplay=1' width='560' height='315' frameborder='0' allow='accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture' allowfullscreen></iframe></div></section></div>"
37+
const fragment = document.createRange().createContextualFragment(html)
38+
document.body.appendChild(fragment)
39+
40+
$('#lightbox a').addEventListener(
41+
'click',
42+
function (e) {
43+
e.preventDefault()
44+
const lightbox = $('#lightbox')
45+
lightbox.parentNode.removeChild(lightbox)
46+
},
47+
{ once: true }
48+
)
49+
}
50+
51+
$$('.youtube-link').forEach(function (link) {
52+
link.addEventListener('click', function (e) {
53+
e.preventDefault()
54+
const id = this.getAttribute('data-id')
55+
createYoutubeFrame(id)
56+
})
57+
})
58+
59+
class LiteYTEmbed extends window.HTMLElement {
60+
async connectedCallback() {
61+
this.videoId = this.getAttribute('videoid')
62+
63+
let playBtnEl = this.querySelector('.lty-playbtn')
64+
// A label for the button takes priority over a [playlabel] attribute on the custom-element
65+
this.playLabel = (playBtnEl && playBtnEl.textContent.trim()) || this.getAttribute('playlabel') || 'Play'
66+
67+
const isWebpSupported = await LiteYTEmbed.checkWebPSupport()
68+
69+
this.posterUrl = isWebpSupported
70+
? `https://i.ytimg.com/vi_webp/${this.videoId}/hqdefault.webp`
71+
: `https://i.ytimg.com/vi/${this.videoId}/hqdefault.jpg`
72+
73+
// Warm the connection for the poster image
74+
LiteYTEmbed.addPrefetch('preload', this.posterUrl, 'image')
75+
76+
this.style.backgroundImage = `url("${this.posterUrl}")`
77+
78+
// Set up play button, and its visually hidden label
79+
if (!playBtnEl) {
80+
playBtnEl = document.createElement('button')
81+
playBtnEl.type = 'button'
82+
playBtnEl.classList.add('lty-playbtn')
83+
this.append(playBtnEl)
84+
}
85+
if (!playBtnEl.textContent) {
86+
const playBtnLabelEl = document.createElement('span')
87+
playBtnLabelEl.className = 'lyt-visually-hidden'
88+
playBtnLabelEl.textContent = this.playLabel
89+
playBtnEl.append(playBtnLabelEl)
90+
}
91+
92+
// On hover (or tap), warm up the TCP connections we're (likely) about to use.
93+
this.addEventListener('pointerover', LiteYTEmbed.warmConnections, { once: true })
94+
95+
// Once the user clicks, add the real iframe and drop our play button
96+
// TODO: In the future we could be like amp-youtube and silently swap in the iframe during idle time
97+
// We'd want to only do this for in-viewport or near-viewport ones: https://github.com/ampproject/amphtml/pull/5003
98+
this.addEventListener('click', e => this.addIframe())
99+
}
100+
101+
static addPrefetch(kind, url, as) {
102+
const linkEl = document.createElement('link')
103+
linkEl.rel = kind
104+
linkEl.href = url
105+
if (as) {
106+
linkEl.as = as
107+
}
108+
document.head.append(linkEl)
109+
}
110+
111+
/**
112+
* Check WebP support for the user
113+
*/
114+
static checkWebPSupport() {
115+
if (typeof LiteYTEmbed.hasWebPSupport !== 'undefined') { return Promise.resolve(LiteYTEmbed.hasWebPSupport) }
116+
117+
return new Promise(resolve => {
118+
const resolveAndSaveValue = value => {
119+
LiteYTEmbed.hasWebPSupport = value
120+
resolve(value)
121+
}
122+
123+
const img = new window.Image()
124+
img.onload = () => resolveAndSaveValue(true)
125+
img.onerror = () => resolveAndSaveValue(false)
126+
img.src = 'data:image/webp;base64,UklGRh4AAABXRUJQVlA4TBEAAAAvAAAAAAfQ//73v/+BiOh/AAA='
127+
})
128+
}
129+
130+
/**
131+
* Begin pre-connecting to warm up the iframe load
132+
* Since the embed's network requests load within its iframe,
133+
* preload/prefetch'ing them outside the iframe will only cause double-downloads.
134+
* So, the best we can do is warm up a few connections to origins that are in the critical path.
135+
*
136+
* Maybe `<link rel=preload as=document>` would work, but it's unsupported: http://crbug.com/593267
137+
* But TBH, I don't think it'll happen soon with Site Isolation and split caches adding serious complexity.
138+
*/
139+
static warmConnections() {
140+
if (LiteYTEmbed.preconnected) return
141+
142+
// The iframe document and most of its subresources come right off youtube.com
143+
LiteYTEmbed.addPrefetch('preconnect', 'https://www.youtube-nocookie.com')
144+
// The botguard script is fetched off from google.com
145+
LiteYTEmbed.addPrefetch('preconnect', 'https://www.google.com')
146+
147+
// Not certain if these ad related domains are in the critical path. Could verify with domain-specific throttling.
148+
LiteYTEmbed.addPrefetch('preconnect', 'https://googleads.g.doubleclick.net')
149+
LiteYTEmbed.addPrefetch('preconnect', 'https://static.doubleclick.net')
150+
151+
LiteYTEmbed.preconnected = true
152+
}
153+
154+
addIframe() {
155+
const params = new URLSearchParams(this.getAttribute('params') || [])
156+
params.append('autoplay', '1')
157+
158+
const iframeEl = document.createElement('iframe')
159+
iframeEl.width = 560
160+
iframeEl.height = 315
161+
// No encoding necessary as [title] is safe. https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html#:~:text=Safe%20HTML%20Attributes%20include
162+
iframeEl.title = this.playLabel
163+
iframeEl.allow = 'accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture'
164+
iframeEl.allowFullscreen = true
165+
// AFAIK, the encoding here isn't necessary for XSS, but we'll do it only because this is a URL
166+
// https://stackoverflow.com/q/64959723/89484
167+
iframeEl.src = `https://www.youtube-nocookie.com/embed/${encodeURIComponent(this.videoId)}?${params.toString()}`
168+
this.append(iframeEl)
169+
170+
this.classList.add('lyt-activated')
171+
172+
// Set focus for a11y
173+
this.querySelector('iframe').focus()
174+
}
175+
}
176+
// Register custome element
177+
window.customElements.define('lite-youtube', LiteYTEmbed)
178+
179+
// Show share only when needed
180+
const intersectionObserverOptions = {
181+
rootMargin: '0px',
182+
threshold: 1.0
183+
}
184+
185+
const $share = document.getElementById('share')
186+
187+
if ($share) {
188+
const $articlePagination = document.getElementById('article-pagination')
189+
const $footer = $('footer')
190+
const elementToObserve = $articlePagination || $footer
191+
192+
const onIntersect = function (entries) {
193+
const [entry] = entries
194+
const hide = entry.boundingClientRect.top <= 0 || entry.isIntersecting
195+
$share.classList.toggle('u-none', hide)
196+
}
197+
198+
const observer = new window.IntersectionObserver(
199+
onIntersect,
200+
intersectionObserverOptions
201+
)
202+
203+
observer.observe(elementToObserve)
204+
}
205+
206+
const ALGOLIA_APPLICATION_ID = 'QK9VV9YO5F'
207+
const ALGOLIA_SEARCH_ONLY_API_KEY = '247bb355c786b6e9f528bc382cab3039'
208+
let algoliaIndex
209+
210+
const $form = $('.ais-SearchBox-form')
211+
const $input = $('.ais-SearchBox-input')
212+
const $reset = $('.ais-SearchBox-reset')
213+
const $hits = $('#hits')
214+
215+
function getAlgoliaIndex() {
216+
if (algoliaIndex) return algoliaIndex
217+
console.log('🚀 ~ file: scripts.js ~ line 222 ~ getAlgoliaIndex ~ algoliaIndex', algoliaIndex)
218+
219+
const algoliaClient = window.algoliasearch(ALGOLIA_APPLICATION_ID, ALGOLIA_SEARCH_ONLY_API_KEY, {
220+
_useRequestCache: true
221+
})
222+
algoliaIndex = algoliaClient.initIndex('prod_blog_content')
223+
return algoliaIndex
224+
}
225+
226+
$form.addEventListener('submit', function (e) {
227+
e.preventDefault()
228+
})
229+
230+
$reset.addEventListener('click', function (e) {
231+
$input.value = ''
232+
$hits.innerHTML = ''
233+
})
234+
235+
$input.addEventListener('input', async function (e) {
236+
await loadScript('https://cdn.jsdelivr.net/npm/[email protected]/dist/algoliasearch-lite.umd.js')
237+
238+
const { value } = e.target
239+
if (value === '') {
240+
$hits.innerHTML = ''
241+
$reset.setAttribute('hidden')
242+
}
243+
244+
$reset.removeAttribute('hidden')
245+
$hits.removeAttribute('hidden')
246+
247+
const algoliaIndex = getAlgoliaIndex()
248+
algoliaIndex.search(value, {
249+
hitsPerPage: 3
250+
}).then(({ hits }) => {
251+
let hitsHtml = ''
252+
hits.forEach(hit => {
253+
const {
254+
link,
255+
_highlightResult: {
256+
title: { value: title },
257+
description: { value: description }
258+
}
259+
} = hit
260+
261+
hitsHtml += `
262+
<li class='ais-Hits-item'>
263+
<a href='${link}'>
264+
${title}
265+
<div>
266+
<small>${description}</small>
267+
</div>
268+
</a>
269+
</li>`
270+
})
271+
272+
$hits.innerHTML = hitsHtml
273+
})
274+
})
275+
276+
// Table Of Contents script
277+
function initTableOfContents() {
278+
const firstTableOfContentsElement = $('#TableOfContents-container li')
279+
if (!firstTableOfContentsElement) return null
280+
281+
// activate first element of table of contents
282+
firstTableOfContentsElement.classList.add('active')
283+
// get all links from table of contents
284+
const links = $$('#TableOfContents-container li a')
285+
286+
const changeBgLinks = entries => {
287+
entries.forEach(entry => {
288+
const { target, isIntersecting, intersectionRatio } = entry
289+
if (isIntersecting && intersectionRatio >= 0.5) {
290+
const id = target.getAttribute('id')
291+
$('#TableOfContents-container li.active').classList.remove('active')
292+
$(`nav li a[href="#${id}"]`).parentElement.classList.add('active')
293+
294+
links.forEach(link => {
295+
link.addEventListener('click', (e) => {
296+
$('#TableOfContents-container li.active').classList.remove('active')
297+
link.parentElement.classList.add('active')
298+
})
299+
})
300+
}
301+
})
302+
}
303+
304+
const options = {
305+
threshold: 0.5,
306+
rootMargin: '50px 0px -55% 0px'
307+
}
308+
309+
const observer = new window.IntersectionObserver(changeBgLinks, options)
310+
311+
const articleTitles = $$('#article-content h2')
312+
articleTitles.forEach(section => observer.observe(section))
313+
}
314+
315+
initTableOfContents()
316+

assets/js/t.js

Whitespace-only changes.

config.toml

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
baseURL = "https://blog.usarr.tech"
2+
languageCode = "es-ES"
3+
title = "Blog | Usarral"
4+
5+
disableHugoGeneratorInject = true
6+
defaultContentLanguage = "es"
7+
preserveTaxonomyNames = true
8+
paginate = 7
9+
10+
[permalinks]
11+
posts = "/:filename/"
12+
13+
[params]
14+
description = "Blog | Usarral"
15+
image = "/favicon/apple-touch-icon.png"
16+
17+
[markup]
18+
[markup.highlight]
19+
guessSyntax = true
20+
noClasses = false
21+
tabWidth = 2
22+
23+
[markup.goldmark.renderer]
24+
unsafe= true
25+
26+
[outputs]
27+
home = ["HTML", "RSS", "Algolia"]
28+
29+
[outputFormats]
30+
[outputFormats.Algolia]
31+
baseName = "algolia"
32+
mediaType = "application/json"
33+
isPlainText = true
34+
notAlternative = true
35+

content/posts/instalacion-hugo.md

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
---
2+
title: "Instalación de Hugo CMS"
3+
date: 2022-09-28
4+
topic: javascript
5+
toc: true
6+
tags:
7+
- terminal
8+
---
9+
## Instalación Hugo en Windows
10+
11+
Para instalar Hugo en Linux lo primero que vamos a hacer es comprobar si tenemos snap, para ello utilizaremos el siguiente comando:
12+
13+
```sh
14+
snap version
15+
16+
```
17+
## Instalación Hugo en Windows
18+
19+
Para instalar Hugo en Linux lo primero que vamos a hacer es comprobar si tenemos snap, para ello utilizaremos el siguiente comando:
20+
21+
```sh
22+
snap version
23+
24+
```
25+

0 commit comments

Comments
 (0)