Skip to main content

Loading...

Let's create a calendar with JavaScript but without any external library. This project is based on my previous internship at Woowa Bros, a unicorn food-delivery startup in Seoul.

Show me the code first.

GitHub - anaclumos/calendar.js: Vanilla JS Calendar

Show me the demo first.

Goals

  • Use functional programming* instead of Object-oriented programming.
  • No DOM manipulation after initializing. This philosophy is based on the React framework (or any other Single Page Application libraries.) DOM manipulation can be highly confusing if 30 different codes are trying to edit the same thing. So instead, we will rerender the components if we need to edit something.

💡

Don't fix it. Buy a new one. — Rerendering in Front-end

Stack

  • JavaScript Date Object
  • CSS display: grid will be useful.

Basic Idea

  • There will be a global displayDate object that represents the displaying month.
  • navigator.js will change this displayDate object, and trigger renderCalendar() function with displayDate as an argument.
  • renderCalendar() will rerender with the calendar.

Before anything, prettier!

Prettier helps write clean and neat codes with automatic formatting.

// `.prettierrc`
{
"semi": false,
"singleQuote": true,
"arrowParens": "always",
"tabWidth": 2,
"useTabs": false,
"printWidth": 60,
"trailingComma": "es5",
"endOfLine": "lf",
"bracketSpacing": true
}

Now throw in some HTML.

<!-- `index.html` -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0"
/>
<title>JavaScript Calendar</title>
</head>
<body>
<div id="navigator"></div>
<div id="calendar"></div>
</body>
<script>
// code for rendering
</script>
</html>

I generated this boilerplate with VS Code.

Then trick VS Code to read JS String as HTML Tags.

Since we use Vanilla JavaScript, we don't have access to fancy JSX-style highlighting. Instead, our generated HTML codes will live inside JavaScript String, which doesn't have syntax highlighting or Intellisense. Therefore, let's create a function that tricks VS Code to recognize JavaScript String as HTML Tags.

// `util.js`
const html = (s, ...args) =>
s.map((ss, i) => `${ss}${args[i] || ''}`).join('')

to be added – screenshot of highlighting

calendar.js

Then we connect calendar.js and index.html.

<!-- `index.html` -->
<script src="calendar.js"></script>

Defining constants will help before writing renderCalendar().

// `calendar.js`
const NUMBER_OF_DAYS_IN_WEEK = 7
const NAME_OF_DAYS = [
'sun',
'mon',
'tue',
'wed',
'thu',
'fri',
'sat',
]
const LONG_NAME_OF_DAYS = [
'Sunday',
'Monday',
'Tuesday',
'Wednesday',
'Thursday',
'Friday',
'Saturday',
]
const ACTUAL_TODAY = new Date()

Note that we use NUMBER_OF_DAYS_IN_WEEK to remove magic numbers inside our code. It can be tough to decipher if we meet a random 7 during a code. Instead, using such constant increases the maintainability of the code.

for (let d = 0; d < NUMBER_OF_DAYS_IN_WEEK; d++) {
// do something
}

If there was a random 7, who knows if we are iterating through the number of Harry Potter Books?

This code block will be the baseline for our calendar generation. We will pass in the HTML target and day object. today represents the month being displayed. Thetoday object will come from navigator,js. Navigator will return the actual date for the current month and return on the first day of the month for other months.

// `calendar.js`
const renderCalendar = ($target, today) => {
let html = getCalendarHTML(today)
// minify html
html = html.replace(/\n/g, '')
// replace multiple spaces with single space
html = html.replace(/\s{2,}/g, ' ')
$target.innerHTML = html
}

Now, we need four different Date objects for displaying the calendar. We could've used fewer objects, but it is up to the implementation. I think reducing date objects here would cause a minimal performance increase but spike the understandability of the code, so using four objects seems like a fair middle ground.

Four Date objects we need

  • The last day of last month: needed to highlight last month's weekend and display the correct date for last month's row.
  • The first day of this month: needed to highlight this month's weekend and figure out how many days of last month we need to render.
  • The last day of this month: needed for rendering this month with iteration.
  • The first day of next month: needed to highlight the weekend of next month.

I made a function that would process these four dates when inputted a specific Date.

// `calendar.js`
const processDate = (day) => {
const month = day.getMonth()
const year = day.getFullYear()
return {
lastMonthLastDate: new Date(year, month, 0),
thisMonthFirstDate: new Date(year, month, 1),
thisMonthLastDate: new Date(year, month + 1, 0),
nextMonthFirstDate: new Date(year, month + 1, 1),
}
}
💬Work in Progress

Work in Progress. Check back later.

Loading...

Recently I came across The Noun Project's API. With the combination of the download function I created in the past, you could download hundreds of icons within seconds.

Beware

Do not use this tool to pirate others' intellectual property. Beware of what you are doing with this code and The Noun Project's API. Read the license and API documents thoroughly. Unauthorized use cases are listed here. This entire post & codes are MIT licensed.

Importing libraries

import requests
import os
from tqdm import tqdm
from requests_oauthlib import OAuth1

You will need to pip3 download if you do not have these libraries.

The download function

def download(url, pathname):
if not os.path.isdir(pathname):
os.makedirs(pathname)
response = requests.get(url, stream=True)
file_size = int(response.headers.get("Content-Length", 0))
filename = os.path.join(pathname, url.split("/")[-1])
if filename.find("?") > 0:
filename = filename.split("?")[0]
progress = tqdm(
response.iter_content(256),
f"Downloading {filename}",
total=file_size,
unit="B",
unit_scale=True,
unit_divisor=1024,
)
with open(filename, "wb") as f:
for data in progress:
f.write(data)
progress.update(len(data))

This code fetches the URL and saves it as a file at pathname.

The Noun Project API

# ---

DOWNLOAD_ITERATION = 3
# Returns 50 icons per iteration.
# Three iteration equals 150 icons.

SEARCH_KEY = "tree" # Search Term
SAVE_LOCATION = "./icons"
auth = OAuth1("API_KEY", "API_SECRET")

# ---

for iteration in range(DOWNLOAD_ITERATION):
endpoint = (
"http://api.thenounproject.com/icons/"
+ SEARCH_KEY
+ "?offset="
+ str(iteration * 50)
)
response = requests.get(endpoint, auth=auth).json()
for icon in response["icons"]:
download(icon["preview_url"], SAVE_LOCATION)

For more advanced uses, please visit this docs page. In addition, you can get your API Key and API secret by registering your app here.

Result

I have run some benchmarks and found that downloading ~5k icons shouldn&#39;t be a problem.

I have run some benchmarks and found that downloading ~5k icons shouldn't be a problem.
However, The Noun Project's API has a call limit so beware of that.

Loading...

Primary Objectives

  • Implement the Karatsuba Method
  • Do not use any * operator (like — not at all!)

First, let's import the math library.

import math

Let's add some util functions for adding zeros. The following operation is super-expensive, and I did this for the sake of removing *s.

def addZeros(number: int, zeros: int) -> int:
s = str(number)
for _ in range(zeros):
s += "0"
return int(s)

If you do not care about not using *s, you can go with:

def addZeros(number: int, zeros: int) -> int:
return number * (10 ** zeros)

Let's say the standard input provides the value in string, with , in between the two numbers. I wrote a wrapper class that parses the standard input and feeds the value into the core method.

def karatsuba(input: str) -> str:
inputList = list(map(str.strip, input.split(',')))
return str(karatsubaCore(int(inputList[0]), int(inputList[1])))

Then we need to finish the actual calculation. For the base calculation (the line after if min(v1, v2) <= 100:) you could go with v1 * v2 if you don't need to remove *s.

def karatsubaCore(v1: int, v2: int) -> int:
if min(v1, v2) <= 100:
minv = min(v1, v2)
maxv = max(v1, v2)
ans = 0
for _ in range(minv):
ans += maxv
return ans

else:
n = int(math.log10(max(v1, v2))//2)
a = int(v1 // pow(10, n))
b = int(v1 % pow(10, n))
c = int(v2 // pow(10, n))
d = int(v2 % pow(10, n))

val1 = karatsubaCore(a, c)
val2 = karatsubaCore(b, d)
val3 = karatsubaCore(a+b, c+d) - val1 - val2

return addZeros(val1, n+n) + addZeros(val3, n) + val2

It is always a good idea to have some validation. Unfortunately, I did not use any testing library; this short script will suffice the purpose of validating the answer.

def karatCheck(input: str) -> str:
i = list(map(str.strip, input.split(',')))

# my calculation
karat: int = karatsubaCore(int(i[0]), int(i[1]))

# the correct calculation
correct: int = int(i[0]) * int(i[1])

print("Correct!" if karat == correct else "Itz... Wrong...")


karatCheck("342345,123943")
karatCheck("342345,0")
karatCheck("00342345 , 123943129893493")
karatCheck("12030912342345,1239431000192837812")
karatCheck("2,1239431000192837812")
karatCheck("249302570293475092384,0")
karatCheck(" 100, 100 ")

If you run this, you will get:

Correct!
Correct!
Correct!

Loading...

Recently I came across the idea of publishing a React App on GitHub Pages. I can distribute my React App using GitHub, further saving server bandwidth and simplifying the API server structure. I have created a boilerplate for this structure.

Key points

  • GitHub has a feature that automatically posts the docs folder into a small landing page.
  • Create-React-App builds its results into a build folder.
  • So if I can automatically move files from /build to /docs whenever I build the app, it would work as if I have set up a CI/CD structure.

Implementation

"scripts": {
"start": "react-scripts start",
"build": "react-scripts build && rm -rf docs && mv build docs",
"test": "react-scripts test --verbose",
"eject": "react-scripts eject"
},

The yarn build command will replace the docs folder with a newer build of the app.

Result

📜Heads up!
  • I wrote this post more than 2 years ago.
  • That's enough time for things to change.
  • I might not agree with this post anymore.
Google Latest Articles Instead
📜Heads up!
  • I wrote this post more than 2 years ago.
  • That's enough time for things to change.
  • I might not agree with this post anymore.
Google Latest Articles Instead
📜Heads up!
  • I wrote this post more than 2 years ago.
  • That's enough time for things to change.
  • I might not agree with this post anymore.
Google Latest Articles Instead
📜Heads up!
  • I wrote this post more than 2 years ago.
  • That's enough time for things to change.
  • I might not agree with this post anymore.
Google Latest Articles Instead

Loading...

So, this blog runs on Ghost. At the footer of this website, I wanted to keep the message "Ghost ${version} self-hosted on DigitalOcean distributed by Cloudflare." But that meant every time I updated Ghost, I had to manually update that string in my theme package and re-upload those. While I automated theme deployment with GitHub Actions (you can find the post here), it was a hassle to ① clone the theme package ② fix the string ③ commit and push it back. Then I thought it would be great to automatically insert the current Ghost version so that I don't have to update it every time manually. At first, I investigated the Ghost engine side to make the Node.js include the value before responding to the client browser, but I figured that there was a much simpler way after a while.

Extracting the Ghost version on client-side

Every Ghost blog includes a tag like the following for SEO and statistical reasons unless you manually disabled it.

<meta name="generator" content="Ghost 3.13" />

That content thing was what I wanted to use. Extract that value with JS.

document.getElementsByName("generator")[0].content;

Of course, if you made some other HTML tag with a name generator before this generator, this wouldn't work. But you really shouldn't do that – generator tags should only be used by automatic software and aren't supposed to be edited. So either leave this tag as-is or remove it altogether.

Displaying the extracted Ghost version

The footer's HTML is generated with a handlebars file.

{
{
{
t "{ghostlink} self-hosted on {cloudlink} distributed by {CDN}"
ghostlink = "<a href = \"https://github.com/TryGhost/Ghost\">Ghost</a>"
cloudlink = "<a href = \"https://www.digitalocean.com/\">DigitalOcean</a>"
CDN="<a href=\"https://www.cloudflare.com/\">Cloudflare</a>"
}
}
}.

I added an id property to ghostlink.

ghostlink="<a id = \"ghost-version\" href=\"https://github.com/TryGhost/Ghost\">Ghost</a>"

Then input the string to the corresponding tag with JS.

<script>
document.getElementById("ghost-version").innerText =
document.getElementsByName("generator")[0].content;
</script>

Paste this to Admin Panel → Code Injections → Site Footer.

You are good to go. See this in action down at the footer. ↓

One less hard-coded magic number!

📜Heads up!
  • I wrote this post more than 2 years ago.
  • That's enough time for things to change.
  • I might not agree with this post anymore.
Google Latest Articles Instead