7 |
8 | / /
9 |
10 |
11 | The Devs is a community on Telegram that tries to gather developers around the world together and help them to chat and discuss about things they love.
12 |
13 |
126 |
127 | Be the first to receive our new groups and articles.
128 |
129 |
130 | {subscription}
131 |
132 |
133 | );
134 | }
135 | }
136 |
137 | export default withStyles(styles)(Subscription);
--------------------------------------------------------------------------------
/posts/the-forth-programming-language.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: The Forth Programming Language
3 | img: the-forth-programming-language
4 | date: 2017, 12, 02
5 | author: Trevor Payne
6 | cats: programming
7 | tags: oop, functional, programming
8 | ---
9 |
10 | Forth is an imperative procedural stack oriented extensive meta reflective
11 | [concatenative programming language written by Charles H. Moore for the IBM 1330 Operating System in 1970.](https://en.wikipedia.org/wiki/Concatenative_programming_language)
12 | The name was going to be Fourth because the language is a Fourth Generation
13 | Programming Language, but due to restrictions on file names, the language has
14 | since been named Forth. Forth works by manipulating the parameter stack, and
15 | procedurally carrying out doing so. It is a self modifying language much like
16 | Lisp and Elixir.
17 |
18 | ### Now, Why Forth?
19 |
20 | Creating programs in Forth is simple, yet different. For any programmer Forth
21 | will seem uncomfortable at first, due to it's obscure ways of programming. Such
22 | as the Reverse Polish Notation for maths, how it is compiled and interpreted,
23 | and how it overall looks compared to other languages. The four most basic
24 | integer-arithmetic operators are +, -, _, and /. Much like most programming
25 | languages, + adds, - subtracts, _ multiplies, and / divides. Huh, this doesn't
26 | sound so different! Although it is. Forth does math in what is called The
27 | Reverse Polish Notation, also known as Postfix. Which means before actually
28 | calculating the equation, numbers are added to the stack first. For instance,
29 |
30 | Many programmers find this to be very confusing. Although eventually it will
31 | come easy. Forth is also considered strange and confusing because of its strange
32 | syntax. Writing code is done by defining sub-sequences, better known as Words in
33 | the Forth language. These are then added to the parameter stack for later use.
34 | This doesn't sound too complicated, right? It reminds some of Object Oriented
35 | and such. Until you see this:
36 |
37 | 
38 |
39 | Now suddenly Forth looks a lot more difficult than it actually is.
40 |
41 | No worries, much like any other spoken language, you're not going to understand
42 | it unless you know it. Let's get on that!
43 |
44 | ### Forth Programming
45 |
46 | Notes:
47 |
48 | "TOS" = Top of Stack,
49 |
50 | "NOS" = Next on Stack
51 |
52 | "BOS" = Bottom of Stack
53 |
54 | Word = Sub-sequence
55 |
56 | Most of Forth programming consists of modifying the stack using the basic Words
57 | dup, swap, rot, drop, and nip. Dup duplicates the TOS, swap swaps the TOS with
58 | the second element, rot rotates the top three elements, drop removes the TOS,
59 | and nip removes the second item. This sounds cool and all, but how can you
60 | create entire programs with just numbers being manipulated? Well, you don't
61 | actually. Forth also has a compile mode, where you can compile sub-sequences to
62 | be called back on later. To enter the compile mode, you can use the :. To exit,
63 | use a ;. Rather than doing 5 dup _ to get the square of a number, you can create
64 | a word for getting the square of every number you want! To do this, first enter
65 | compile mode, name word, then tell it dup _, and finally, exit compile mode by
66 | typing ;. It should look like this:
67 |
68 | _: sqr dup \* ;_
69 |
70 | Now everything is starting to make a lot more sense! Forth also has a whole
71 | dictionary of words that you can use, in most Forths you can find this by typing
72 | the word words. Much a real dictionary, the words have meaning as well! To get
73 | the meaning of a word in Forth, type the word see, then the word you want the
74 | definition of. In example, see dup. This will help you get a better
75 | understanding of some of the more advanced Forth words. You can find many
76 | resources online for learning more Forth programming.
77 |
78 | ### Is Forth used?
79 |
80 | Yes! By many companies in fact. The language is often described as dead, unused,
81 | and forgotten, but it's one of the only programming languages that has visited
82 | Saturn, space, yet can run on some of the smallest hardware! It is used very
83 | much by NASA, Lockheed, Boeing, and many other Aviation companies. Forth only is
84 | around 500 Lines of Code in languages like x86 and ARM, which is only around
85 | 3KB. Although there are many Forth implementations that are much smaller. This
86 | is great when working on extremely small hardware and small storage devices.
87 | Forth does not have much overhead, and can run on baremetal. I'd not be
88 | surprised if SpoonForth becomes a thing one day. Forth is an entire world itself
89 | in the universe of programming, I just hope to shed some light in that beautiful
90 | yet different world.
91 |
92 | ### In Conclusion
93 |
94 | Forth is a small language that can do extremely large things, such as wrap C,
95 | interpret Lisp, create operating systems, etc. It is not very popular due to
96 | it's strange and different syntax, which scares away most programmers. I'm one
97 | of many Forth programmers who wish it was more popular, and hopes to see it
98 | grow. With the fall of OOP, maybe that will be soon.
99 |
--------------------------------------------------------------------------------
/posts/best-ui-libraries-and-frameworks-for-reactjs.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Best UI libraries and frameworks for ReactJS
3 | img: best-ui-libraries-and-frameworks-for-reactjs
4 | date: 2017, 09, 01
5 | author: Pouria Ezzati
6 | cats: javascript
7 | tags: javascript, react, ui
8 | ---
9 | Since React has been introduced dozens of UI libraries and frameworks started to come out one by one and it's continuing. All of these tools, boilerplates and UI components are here to save your time and help you focus on building apps.
10 |
11 | With these tools, you no longer need to worry about designing elements and components and trying to keep them consistent at the same time, which is a **time-consuming** process, even if you have a designing background.
12 |
13 | In this list, I try to share the best **UI libraries and frameworks** based on my personal experience and **opinion**.
14 |
15 | ## Material UI
16 |
17 | [](https://material-ui-1dab0.firebaseapp.com/)
18 |
19 | There are several material design UI frameworks for React, but this is my **favorite** one because of the implementation of components which looks better than similar frameworks.
20 |
21 | Version 1 is still in *beta* and has much more stuff but it's inconsistent and API changes a lot. However, I still recommend you to try the **latest version**, as we did in The Devs.
22 |
23 | - **Well implemented** Material design
24 | - Suitable for **every** website
25 | - [**create-react-app**](https://github.com/facebookincubator/create-react-app) and [**Next.js**](https://github.com/zeit/next.js/) examples
26 | - v1 beta is a lot better, but **API changes** a lot
27 | - [**JSS**](https://github.com/cssinjs/react-jss) makes it a little **harder** and time-consuming to start with
28 | - **Customizable** theme with JSS
29 |
30 | [**Material UI**](https://material-ui-1dab0.firebaseapp.com/)
31 |
32 | ## Blueprint
33 |
34 | [](http://blueprintjs.com/)
35 |
36 | Blueprint has a clean design similar to bootstrap, but **better**. It has its own **design system** which helps you to use components in their correct form and place.
37 |
38 | - **Lots of components** with great designs
39 | - Has APIs for **both CSS and JS**
40 | - Built with **Typescript**, examples are also in Typescript
41 | - **Flexible** and customizable styles built with **Sass**
42 | - Extensive **Documentation**
43 |
44 | [**Blueprint**](http://blueprintjs.com/)
45 |
46 | ## Ant Design
47 |
48 | [](https://ant.design/)
49 |
50 | Yet another **design system** that its **professional** design makes it perfect to use it on **enterprise** projects. It's easy to start and it also supports s**erver-side rendering** and **Electron**.
51 |
52 | - **Enterprise** design
53 | - **Huge** number of components
54 | - [**create-react-app**](https://github.com/facebookincubator/create-react-app) example
55 | - Works on Browser, server-side rendering and [**Electron**](https://electron.atom.io/)
56 | - **Customizable** theme
57 | - **Straightforward** documentation
58 | - Available for **React Native**
59 |
60 | [**Ant Design**](https://ant.design/)
61 |
62 | ## Semantic UI
63 |
64 | [](https://react.semantic-ui.com/)
65 |
66 | Semantic UI is a beautiful user interface that is super easy and **enjoyable** to work with. I've built several apps with it before and will do again.
67 |
68 | - Beautiful design
69 | - **Super easy** to start with
70 | - Simple **declarative** component APIs
71 | - Well documented
72 |
73 | [**Semantic UI**](https://react.semantic-ui.com/)
74 |
75 | ## React-Bootstrap
76 |
77 | [](https://react-bootstrap.github.io/)
78 |
79 | Currently the **best implementation** of Bootstrap for React. It has built upon **Bootstrap 3** and as they mention in their website, it's under active development and APIs will change.
80 |
81 | - Almost every **Bootstrap 3** components available for React
82 | - **Easy** to setup and use
83 | - Under active development, **unstable APIs**
84 |
85 | [**React-Bootstrap**](https://react-bootstrap.github.io/)
86 |
87 | ## Fabric
88 |
89 | [](https://dev.office.com/fabric#/)
90 |
91 | Use the whole Microsoft Office styles, components and icons with React. **Maintained officially** by Office.
92 |
93 | - **Offical Office UI** framework for React
94 | - Includes Office **fonts and icons**
95 | - Microsoft’s **palette**
96 | - Extensive APIs for components
97 | - **Best practices** guides for each component
98 |
99 | [**Fabric**](https://dev.office.com/fabric#/)
100 |
101 | ## AtlasKit
102 |
103 | [](https://atlaskit.atlassian.com/)
104 |
105 | AtlasKit is Atlassian's official UI library, built according to the Atlassian **design guidelines**.
106 |
107 | - **atlaskit-starter** creates a React app with AtlasKit components
108 | - Lots of well crafted components
109 | - Has it's own **design guidelines**
110 |
111 | [**AtlasKit**](https://atlaskit.atlassian.com/)
112 |
113 | ## React Desktop
114 |
115 | [](http://reactdesktop.js.org/)
116 |
117 | React Desktop aims to bring a **native desktop** experience to the web, featuring many macOS Sierra and Windows 10 components.
118 |
119 | - Native-like **macOS** and **Windows 10** components
120 | - Works perfectly with [**NW.js**](https://nwjs.io/) and [**Electron.js**](https://electron.atom.io/)
121 |
122 | [**React Desktop**](http://reactdesktop.js.org/)
123 |
124 | ## Cloudflare UI
125 |
126 | [](https://cloudflare.github.io/cf-ui/)
127 |
128 | Cloudflares UI framework has **+50 packages** and it uses CSS in JS for its components. The design looks suitable for **corporate** websites.
129 |
130 | - Corporate design style
131 | - **CSS in JS** solution
132 | - **Huge** number of components
133 |
134 | [**Cloudflare UI**](https://cloudflare.github.io/cf-ui/)
135 |
136 | ## React Toolbox
137 |
138 | [](http://react-toolbox.com/)
139 |
140 | Another UI framework that implements Google's **Material design** system. It uses CSS Modules to import stylesheets.
141 |
142 | - Good implementation of **Material design**
143 | - Powered by **CSS Modules**
144 | - Needs **Webpack** or another module bundler
145 |
146 | [**React Toolbox**](http://react-toolbox.com/)
147 |
--------------------------------------------------------------------------------
/posts/you-have-been-using-console-wrong-the-whole-time.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: You have been using 'console' wrong the whole time
3 | img: you-have-been-using-console-wrong-the-whole-time
4 | date: 2018, 06, 18
5 | author: Sparkenstein
6 | cats: javascript
7 | tags: javascript, console
8 | ---
9 |
10 | As a newcomer to any language, the first thing you ever try out is the "Hello World!" program of the respective laguage. JavaScript is not an exception. When I started learning JavaScript, my first line of code was
11 |
12 | ```JavaScript
13 | console.log("Hello World!");
14 | ```
15 |
16 | And that's it! After learning `console.log` can print anything from the script to the console, I've used it in almost every project.
17 |
18 | It can print strings, numbers, variables, objects, arrays, execute and print the output of functions, concatenate multiple outputs and what not. But I recently learned that this is not the only power of the console object. It can do many more things that we are unaware of. And that is what we are going to take a look today.
19 |
20 | ### Differenciate output message as Error, Warning, Debug and Info
21 |
22 | `.log()` method of the console object is a very basic method which takes the input and prints it directly to the browser console. To differenciate the output message in the form of Error, Warning, Info, Debug, console has the methods `.error()`, `.warn()`, `.info()`, and `.debug()` respectively.
23 |
24 | Let's take an example of the `.error()` method. This is very helpful when you are outputting an error message to the console. It not only prints the message in bright red so the user can look for it specifically, but also provides a nice stack trace of the function calls as well. On the browser, you can filter errors/warnings from the console toolbar if required, and in nodejs, they are outputed to stderr.
25 |
26 | Try running the following code in your console:
27 |
28 | ```JavaScript
29 | const getEmployeeSalaryByName = function(name){
30 | if(typeof name === "string") {
31 | return "Salary is $15000";
32 | } else console.error(new TypeError("Name should be a string"));
33 | };
34 |
35 | getEmployeeSalaryByName(1322); //-> TypeError: "Name should be a string"
36 | ```
37 |
38 | ### Format objects in a tabular format
39 |
40 | Sometimes it's a mess to see the structure of the objects printed by `console.log()`. console provides a nice way of outputting objects to the console via `console.table()` method. It takes two arguments—`data` and `columns`, which is optional.
41 |
42 | The `data` parameter can be any enumerable object (Array, Objects, Set, Map, everyone is welcome). Believe it or not, it even supports nested objects like array of arrays `[[1,'Luffy'],[2,'Ichigo'],[3,'Goku'],[4,'Naruto']]` and complex objects like `{'rank' : [1,2,3,4], 'names': ['Luffy','Ichigo','Goku','Naruto']};` and you have to do literally nothing except pass this data to `console.table()` method and the browser will handle everything on its own. Example:
43 |
44 | 
45 |
46 | 
47 |
48 | Go ahead try it on your own.
49 |
50 | > Note: `console.table() is not available in node, for fairly obvious reasons.`
51 |
52 | ### Benchmarking with console.time
53 |
54 | Although this method is not meant for benchmarking, and we now have much better alternatives like `performance.now()`, it works fine and is simpler to use than `console.time()`.
55 |
56 | Sometimes we need to measure the time spent by a function or loop to check the performance of a webpage. I used to use `Date.now()` method to calculate the execution time of a code snippet. The strategy was simple. Before execution, measure current time, store it in a variable `t1`. After code execution, measure current time, store it in another variable `t2`, and at the end, `t2-t1` is the execution time.
57 |
58 | What can go wrong with this one? Although Paul Irish's [When milliseconds are not enough](https://developers.google.com/web/updates/2012/08/When-milliseconds-are-not-enough-performance-now?hl=en) is a post about `performance.now()`, it clearly explains why `Date.now()` is not a very good idea. And that's where `console.time()` method comes in handy. And the best part is you don't even have to do any calculation here to measure performance. The browser does everything automatically and prints the output to the console.
59 |
60 | Take an example of following `for` loop:
61 |
62 | ```JavaScript
63 | var m = 0;
64 | for (let i=0; i<=10000; i++){
65 | m = m + (Math.random() * 100) * i*i;
66 | }
67 | ```
68 |
69 | And you want to calculate the time taken by the for loop. You just prepend the loop with `console.time("benchmarkName")` before its execution and append `console.timeEnd("benchmarkName")` post exection. `benchmarkName` is nothing but a string which helps the browser identify which start point to correlate with `.timeEnd()` where multiple `console.time()` are involved. If you only have one `.time()` call, the nearest `.timeEnd()` will be associated with `default` as label.
70 |
71 | ```JavaScript
72 | var m = 0;
73 | console.time("for10k");
74 | for (let i=0; i<=10000; i++){
75 | m = m + (Math.random() * 100) * i*i;
76 | }
77 | console.timeEnd("for10k"); // -> outputs 2ms
78 | ```
79 |
80 | And when the code executes, the browser will automatically start measuring time with label "for10k" and stop with the `.timeEnd()` method with the same name, calculate the difference between both timestamps and print it with label "for10k" on the console.
81 |
82 | ### Show message only when something is not correct with console.assert
83 |
84 | This is yet another small but very handy method. It takes two parameters—the predicate or expression which evaluates to true/false, and the message which is to be printed when predicate evaluates to `false`. Note that in case the assertion is true, it doesn't print anything.
85 |
86 | ```JavaScript
87 | const timeTravellersArray = ["Okabe"];
88 |
89 | function isTimeTraveller(name){
90 | if(timeTravellersArray.indexOf(name) !== -1)
91 | return true;
92 | return false;
93 | }
94 |
95 | const isOkabeATimeTraveller = isTimeTraveller("Okabe");
96 | if(!isOkabeATimeTraveller){
97 | console.error("Okabe is not a time traveller")
98 | }
99 | ```
100 |
101 | With console.assert, this can be slimmed down to:
102 |
103 | ```JavaScript
104 | console.assert(isTimeTraveller("Okabe"), "Okabe is not a time traveller");
105 | ```
106 |
107 | Amazing isn't it?
108 |
109 | ### See all the properties of an object with console.dir
110 |
111 | This is a very helpful method when you want to list down all the properties of an object in a 'collapsible tree' format. Although it does not look completly different from console.log(), it treats it's parameters different from `console.log`. Try executing this example below yourself to see the different outputs:
112 |
113 | ```JavaScript
114 | console.log(document.body);
115 | console.dir(document.body);
116 | ```
117 |
118 | ### Last but not least, console.group
119 |
120 | consider a scenario where you have to output multiple messages, while keeping the hierarchy of the functions from which the messages are coming from. Normally I'd have just assigned it to some `message` object and printed that object. But here we are outputting another object and not the message itself. To overcome this, console provides a nice method called `.group()` which groups output in folders. Consider the following example:
121 |
122 | ```JavaScript
123 | console.group("first");
124 | console.log("inside first 1");
125 | console.group("first1");
126 | console.log("inside first1");
127 | console.groupEnd("first1");
128 | console.group("first2");
129 | console.log("inside first2 1");
130 | console.log("inside first2 2");
131 | console.groupEnd("first2");
132 | console.log("outside first2 1");
133 | console.groupEnd("first");
134 | console.log("outside first");
135 | console.group("second");
136 | console.log("inside second");
137 | console.groupEnd("second");
138 | ```
139 |
140 | Even though the utility of this function may be debatable, you'll be amazed how well formatted the output messages are.
141 |
142 | > Note: `console.group` is not available in node as well.
143 |
144 | To check and learn more about the `console` object please visit [MDN Console API](https://developer.mozilla.org/en-US/docs/Web/API/console)
145 |
--------------------------------------------------------------------------------
/posts/type-coercion-in-javascript.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Type coercion in JavaScript (and why everyone gets it wrong)
3 | img: type-coercion-javascript
4 | date: 2018, 06, 07
5 | author: Muthu Kumar
6 | cats: javascript
7 | tags: javascript, types, coercion
8 | ---
9 |
10 | Weak dynamic typing is arguably one of those things everybody likes to pick at about JavaScript. For an elegant dynamic language, JavaScript does some very silly things.
11 |
12 | Or does it? _(Queue Vsauce intro)_
13 |
14 | This is my little attempt to explain JavaScript's type coercion in a very simple manner that all JavaScript developers, regardless of experience, can simply understand. I hope you'll leave this with an enriched knowledge of how to take hold of the language's quirks and use it to your advantage.
15 |
16 | ## What is it doing wrong?
17 |
18 | **Example 1**
19 |
20 | ```JavaScript
21 | const a = [ 1, 2, 3 ];
22 | const b = [ 1, 2, 3 ];
23 |
24 | console.log(a + b); //-> 1,2,31,2,3 <- Why?
25 | ```
26 |
27 | **Example 2**
28 |
29 | ```JavaScript
30 | const nope = Array(10).join("nope" - 1) + " Batman!";
31 |
32 | console.log(nope); //-> NaNNaNNaNNaNNaNNaNNaNNaNNaN Batman! <- Why?
33 | ```
34 |
35 | **Example 3**
36 |
37 | ```JavaScript
38 | const x = [];
39 | const y = {};
40 |
41 | console.log(x + y); //-> [object Object] <- Wait what?
42 | ```
43 |
44 | ## Why is it doing it wrong?
45 |
46 | The simple reason is history. JavaScript has a long, weird, winding history from when Brendan Eich originally wrote the first prototype in 10 days at Netscape in 1995.
47 |
48 | Since then, anything that hasn't been "fixed" has only one reason. JavaScript has one simple rule -- don't break the web. This is why strange things like `typeof null === 'object'` exist. This is outside the scope of this article, so more on why that is [here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof#null).
49 |
50 | ## How do I know what's happening?
51 |
52 | It's really very simple. When you ask a value to work with an operator it does not respect (like adding an object to a number), or when you ask two values that don't work with each other to work with each other (adding a number and a string), JavaScript tries to convert either or both types to make it work.
53 |
54 | But there must be rules for this. After all, if a computer makes a mistake, it's at least consistent about it.
55 |
56 | JavaScript simply hooks into appropriate methods found in that object or primitive's prototype. The two that matter are `.toString()` and `.valueOf()`. When you try to concat an array and a string using the "+" operator, the `Array.prototype.toString()` method is called to convert the Array to a string.
57 |
58 | Here's a cheatsheet on what coerces to what by default:
59 |
60 |
61 |
62 |
63 |
Origin
64 |
Target
65 |
Result
66 |
67 |
68 |
69 |
70 |
Number
71 |
Boolean
72 |
True, except if it's 0, or NaN
73 |
74 |
75 |
String
76 |
Boolean
77 |
True, except if it's an empty string ""
78 |
79 |
80 |
undefined or null
81 |
Boolean
82 |
Always false
83 |
84 |
85 |
Object, Array, Symbol, Function
86 |
Boolean
87 |
Always true
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
Number, Undefined, Null, NaN
96 |
String
97 |
Value as string (5 --> "5")
98 |
99 |
100 |
Boolean, Function, Symbol
101 |
String
102 |
Value as string (true --> "true")
103 |
104 |
105 |
Array
106 |
String
107 |
String of array values separated by commas
108 |
109 |
110 |
111 |
112 |
Empty string if empty array
113 |
114 |
115 |
Object
116 |
String
117 |
'[object Object]' (yes, yes I know...)
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
String
126 |
Number
127 |
NaN, except if string represents a number
128 |
129 |
130 |
131 |
132 |
"5" --> 5, "0.001" --> 0.001
133 |
134 |
135 |
Array
136 |
Number
137 |
0 if empty array, number if the only
138 |
139 |
140 |
141 |
142 |
element is a number, NaN in any other case
143 |
144 |
145 |
Object, Function, undefined
146 |
Number
147 |
NaN
148 |
149 |
150 |
null
151 |
Number
152 |
0
153 |
154 |
155 |
Symbol
156 |
Number
157 |
THROWS!
158 |
159 |
160 |
161 |
162 |
163 | > Side note: I won't go too deep into operators, but a common misunderstanding among amateurs is that the "+" operator behaves unexpectedly. In fact, it does not. It's simply that the same operator acts as one of "summation" (when dealing with numbers), "concatenation" (when dealing with strings), or the "unary plus" operators depending on context. Anything else you attempt to pass to it will result in coercion to either a string or number. We'll come back to this.
164 |
165 | It's important to consider that the Arrays are in fact objects. The array prototype is syntax sugar. You can make your own Array type in pure JavaScript by leveraging objects. In fact this is very evident from the fact that you can assign any property to an array, even negative indices and strings.
166 |
167 | ```JavaScript
168 | var arr = [ ];
169 |
170 | arr[0] = "Zero";
171 | // All good
172 |
173 | arr[-1] = "Minus One";
174 | // Wait what
175 |
176 | arr['prop'] = true;
177 | // Really?
178 |
179 | arr.says = "Hello";
180 | // Mr. JavaScript, I'm not feeling so good...
181 |
182 | console.log(arr); //-> [ "Zero", -1: "Minus One", prop: true, says: "Hello" ]
183 | ```
184 |
185 | Do you see what's happening here? Yes, arrays are plain JavaScript objects. But why do they coerce differently? Because they have different builtin `.toString()` and `.valueOf()` methods.
186 |
187 | You can even hook into these defaults, or even override them.
188 |
189 | ```JavaScript
190 | Object.prototype.toString = function() {
191 | return JSON.stringify(this);
192 | // "this" refers to the call-site, here the object that calls toString()
193 | };
194 | const obj = { "foo" : "bar" };
195 | const obj2 = { "baz" : "foobar" };
196 |
197 | console.log( obj + obj2 ); //-> {"foo":"bar"}{"baz":"foobar"}
198 | ```
199 |
200 | As you can see, we've overriden the default `.toString()` property to our advantage. We no longer get `[object Object][object Object]` as our value.
201 |
202 | > As a side note, I would highly recommend that you never do this in production to builtin datatypes. You can make your own classes of course with custom `.toString()` and `.valueOf()`.
203 |
204 | So when does `.toString()` get called, and when does `.valueOf()` get called? Whenever possible, `toString()` will always be called first. Failing that, `.valueOf()` will be called. Constructors like `String()` and `Number()` will respect your methods as well.
205 |
206 | When leveraging these methods to your purpose, you have no obligation to return a String type from your prototype's `.toString()` method at all! We'll take JavaScript's inbuild Date as an example.
207 |
208 | ```JavaScript
209 | const now = new Date();
210 |
211 | console.log(now + " is the current time.");
212 | //-> Thu Jun 07 2018 11:17:10 GMT+0530 (IST) is the current time.
213 |
214 | Date.prototype.toString = function() {
215 | return this.valueOf();
216 | };
217 | console.log(now + 10000);
218 | //-> 1528350440978
219 | ```
220 |
221 | Again, you should never do this to JavaScript builtins, but if you do this to datastructures you may author, consider the implications of what you do.
222 |
223 | We still have one loose end to cover. We know JavaScript always coerces to String, Number, or Boolean. And we've learnt how to leverage coercion to the first two types. What about the third?
224 |
225 | Coercion to Boolean calls the native `ToBoolean` method. At JavaScript's level, we cannot modify this behaviour. `ToBoolean` is simple. It depends on what JavaScript considers "truthy" and "falsy".
226 |
227 | - Objects, Arrays, and Symbols are always truthy (this includes any class you may create).
228 | - null and undefined are always falsy.
229 | - `0`, `-0`, and `+0` are falsy, and all other Numbers are truthy.
230 | - Empty string is falsy, and all other strings are truthy.
231 |
232 | But consider this seemingly logic defying coercion:
233 |
234 | ```JavaScript
235 | console.log( Boolean(-[]) ) //-> false <- but arrays are always truthy?
236 | ```
237 |
238 | Here, because you're coercing the array to a value first by using the unary minus operator, `-[]` becomes `-0` before it's even passed to the Boolean. Hence what gets called is `Boolean(-0)` which correctly returns `false`.
239 |
240 | Now that you have this information, good luck JavaScripting!
241 |
242 | ---
243 |
244 | Hey, I'm Muthu Kumar, and I'm on [Twitter](https://twitter.com/MKRhere), [Telegram](https://t.me/MKRhere), and [Github](https://github.com/codefeathers). Or check my personal website: [https://mkr.pw](https://mkr.pw).
245 |
--------------------------------------------------------------------------------
/posts/build-a-telegram-bot-with-node.js.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Build a simple Telegram Bot with Node.js
3 | img: simple-telegram-bot-nodejs
4 | date: 2017, 08, 25
5 | author: Pouria Ezzati
6 | cats: javascript
7 | tags: javascript, telegram, bot, node
8 | ---
9 |
10 | Ever since Telegram introduced bots in 2015, other messengers have added bots to
11 | their own platforms. But Telegram's extensive bot API and the freedom it offers
12 | the developer keep it at an edge ahead of its competitors.
13 |
14 | Lately, businesses have begun to adopt Telegram's bot platform to build
15 | completely separate apps that can do virtually anything within the bot
16 | framework.
17 |
18 | ## How bots work
19 |
20 | In order to be able to get updates from Telegram, you need a **token**. All the
21 | updates and the interactions with your bot gets stored in Telegram and you can
22 | get them by sending a request to this URL with that token.
23 |
24 | ```javascript
25 | https://api.telegram.org/bot/METHOD_NAME
26 | ```
27 |
28 | > The Bot API is an HTTP-based interface created for developers keen on building
29 | > bots for Telegram.
30 |
31 | ## Getting started
32 |
33 | First of all, go ahead and create your bot with
34 | [BotFather](https://telegram.me/botfather) - which is a bot by itself. Now you
35 | have the token and can get updates from Telegram. Let's get some info from
36 | Telegram so we can make sure our bot works.
37 |
38 | Replace the URL above with your token and a method from
39 | [Telegram's Bot API](https://core.telegram.org/bots/api). Let's use **getMe**
40 | method.
41 |
42 | ```javascript
43 | https://api.telegram.org/bot/getMe
44 |
45 | // --> {"ok":true,"result":{"id":437852999,"is_bot":true,"first_name":"Reddit Bot","username":"SimpleReddit_Bot"}}
46 | ```
47 |
48 | Well done. But how do we do this in NodeJS? It's basically the same. Every time
49 | we need an update we send a request to that URL with our desired method.
50 |
51 | But the whole process of doing this would be frustrating, so we have handy
52 | frameworks for this. They handle everything and let us focus on what's
53 | important. There are some good frameworks
54 | [available for NodeJS](https://core.telegram.org/bots/samples), in this tutorial
55 | we're going to use [Telegraf](https://github.com/telegraf/telegraf).
56 |
57 | ## Start coding
58 |
59 | Initialize the project and install **Telegraf**:
60 |
61 | ```javascript
62 | npm init
63 | ```
64 |
65 | ```javascript
66 | npm install telegraf --save
67 | ```
68 |
69 | Now let's add it to our script and make a simple bot:
70 |
71 | ```javascript
72 | const Telegraf = require('telegraf');
73 | const app = new Telegraf(YOUR_TOKEN_HERE);
74 |
75 | app.hears('hi', ctx => {
76 | return ctx.reply('Hey!');
77 | });
78 |
79 | app.startPolling();
80 | ```
81 |
82 | What's going on? Telegraf has it's own methods to handle most of the work for
83 | us. We can use this method to respond to a user's message:
84 |
85 | 
86 |
87 | ## Reddit bot
88 |
89 | Let's take an example. We're going to send the top post of the subreddit that a
90 | user asks for. Install [axios](https://github.com/mzabriskie/axios) library to
91 | simplify sending **GET** requests and grabbing data from
92 | [Reddit](https://www.reddit.com/dev/api/).
93 |
94 | ```javascript
95 | npm install axios --save
96 | ```
97 |
98 | ```javascript
99 | const axios = require('axios'); // add axios
100 |
101 | // handle the reaction everytime user sends a text message
102 | app.on('text', ctx => {
103 | // ctx object holds the Update object from Telegram API
104 | // So you can use everything you see there
105 |
106 | // get the text message sent by user
107 | const subreddit = ctx.message.text;
108 |
109 | // GET the data from Reddit API
110 | axios
111 | .get(`https://reddit.com/r/${subreddit}/top.json?limit=10`)
112 | .then(res => {
113 | // data recieved from Reddit
114 | const data = res.data.data;
115 |
116 | // if subbreddit does not exist
117 | if (data.children.length < 1)
118 | return ctx.reply("The subreddit couldn't be found.");
119 |
120 | // send the first top post link to the user
121 | const link = `https://reddit.com/${data.children[0].data.permalink}`;
122 | return ctx.reply(link);
123 | })
124 |
125 | // if there's any error in request
126 | .catch(err => console.log(err));
127 | });
128 | ```
129 |
130 | 
131 |
132 | When a user sends a subreddit name, we're going to grab the top post of that
133 | subreddit and send its link to them. Simple, huh?
134 |
135 | ## Saving state
136 |
137 | Imagine users want other options such as top, hot and new. We need to store the
138 | latest command they used to be able to respond correctly. Note that we use
139 | **command** method instead of **on**.
140 |
141 | You can create commands on a Telegram bot. Commands start with **'/'** and are
142 | clickable. To add commands to your bot, message
143 | [BotFather](https://telegram.me/botfather).
144 |
145 | ```javascript
146 | let state = {};
147 |
148 | app.command('top', ctx => {
149 | const userId = ctx.message.from.id;
150 |
151 | // if user id does not exist create one
152 | if (!state[userId]) state[userId] = { id: userId };
153 |
154 | // save/update user last command
155 | state[userId].command = 'top';
156 | return ctx.replyWithMarkdown(`Enter a subreddit name to get *top* posts.`);
157 | });
158 |
159 | app.command('hot', ctx => {
160 | const userId = ctx.message.from.id;
161 | if (!state[userId]) state[userId] = { id: userId };
162 | state[userId].command = 'hot';
163 | return ctx.replyWithMarkdown('Enter a subreddit name to get *hot* posts.');
164 | });
165 | ```
166 |
167 | Now we can send the proper post based on filter. In our **text** response:
168 |
169 | ```javascript
170 | const userId = ctx.message.from.id;
171 | // check if state and command exists and set defaults
172 | const type = !state[userId]
173 | ? 'top'
174 | : state[userId].command ? state[userId].command : 'top';
175 | axios
176 | .get(`https://reddit.com/r/${subreddit}/${type}.json?limit=10`)
177 | .then(res => [
178 | // do stuff
179 | ]);
180 | ```
181 |
182 | 
183 |
184 | ## Inline buttons
185 |
186 | Telegram bots have interactive buttons called **InlineKeyboardMarkup**. We're
187 | going to add a **next** button, so the user can get the next post in that
188 | category.
189 |
190 | We need to extract the specific methods for buttons from Telegraf in order to
191 | work with them:
192 |
193 | ```javascript
194 | const { Markup } = require('telegraf');
195 | ```
196 |
197 | First, let's add the current post number to the state. Every time a user asks
198 | for a subreddit we need to set index to 0. In our **text** method:
199 |
200 | ```javascript
201 | if (!state[userId]) state[userId] = {};
202 | state[userId].index = 0;
203 | ```
204 |
205 | Instead of sending plain text, we send it with an inline button in the **axios
206 | response**:
207 |
208 | ```javascript
209 | // old response, only text
210 | return ctx.reply(link);
211 |
212 | // new response, with inline buttons
213 | return ctx.reply(
214 | link,
215 | Markup.inlineKeyboard([
216 | // first argument is button's text
217 | // second argument is callback text
218 | Markup.callbackButton('➡️ Next', subreddit),
219 | ]).extra(),
220 | );
221 | ```
222 |
223 | We can handle the callback with **on** method, but this time, the update method
224 | is **callback_query**:
225 |
226 | ```javascript
227 | app.on('callback_query', ctx => {
228 | // get info from callback_query object
229 | const subreddit = ctx.update.callback_query.data;
230 | const userId = ctx.update.callback_query.from.id;
231 |
232 | // check if user state and its properties exist
233 | let type;
234 | let index;
235 | try {
236 | type = state[userId].command ? state[userId].command : 'top';
237 | index = state[userId].index;
238 | } catch (err) {
239 | return ctx.reply('Send a subreddit name.');
240 | }
241 |
242 | // reply with a popup to callback
243 | ctx.answerCallbackQuery('Wait...');
244 |
245 | axios
246 | .get(`https://reddit.com/r/${subreddit}/${type}.json?limit=10`)
247 | .then(res => {
248 | const data = res.data.data;
249 |
250 | // check if next one exists
251 | if (!data.children[index + 1]) return ctx.reply('No more posts!');
252 |
253 | // send next link and update the user state with new index
254 | const link = `https://reddit.com/${
255 | data.children[index + 1].data.permalink
256 | }`;
257 | state[userId].index = state[userId].index + 1;
258 | return ctx.reply(
259 | link,
260 | Markup.inlineKeyboard([
261 | Markup.callbackButton('➡️ Next', subreddit),
262 | ]).extra(),
263 | );
264 | })
265 | .catch(err => console.log(err));
266 | });
267 | ```
268 |
269 | 
270 |
271 | ## Wrapping up
272 |
273 | As you can see, we've created a simple Telegram bot in minutes. Creating bots in
274 | Telegram easy, but it doesn't stop here. There are a lot of more stuff you can
275 | do with them—such as sending photos, videos, documents etc.
276 |
277 | Imagine all the things you can do with Telegram's huge API which continuously
278 | gets better with each update.
279 |
280 | You can find the source code of this bot on
281 | [GitHub](https://github.com/poeti8/simple-reddit-bot).
282 | [Here is a more complex Reddit bot](https://github.com/poeti8/reddbot) I've
283 | created, which uses more bot features such as sending photos and inline buttons.
284 |
--------------------------------------------------------------------------------
/static/css/flexboxgrid.min.scss:
--------------------------------------------------------------------------------
1 | .container-fluid{margin-right:auto;margin-left:auto;padding-right:2rem;padding-left:2rem}.row{box-sizing:border-box;display:-ms-flexbox;display:-webkit-box;display:flex;-ms-flex:0 1 auto;-webkit-box-flex:0;flex:0 1 auto;-ms-flex-direction:row;-webkit-box-orient:horizontal;-webkit-box-direction:normal;flex-direction:row;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-1rem;margin-left:-1rem}.row.reverse{-ms-flex-direction:row-reverse;-webkit-box-orient:horizontal;-webkit-box-direction:reverse;flex-direction:row-reverse}.col.reverse{-ms-flex-direction:column-reverse;-webkit-box-orient:vertical;-webkit-box-direction:reverse;flex-direction:column-reverse}.col-xs,.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{box-sizing:border-box;-ms-flex:0 0 auto;-webkit-box-flex:0;flex:0 0 auto;padding-right:1rem;padding-left:1rem}.col-xs{-webkit-flex-grow:1;-ms-flex-positive:1;-webkit-box-flex:1;flex-grow:1;-ms-flex-preferred-size:0;flex-basis:0;max-width:100%}.col-xs-1{-ms-flex-preferred-size:8.333%;flex-basis:8.333%;max-width:8.333%}.col-xs-2{-ms-flex-preferred-size:16.667%;flex-basis:16.667%;max-width:16.667%}.col-xs-3{-ms-flex-preferred-size:25%;flex-basis:25%;max-width:25%}.col-xs-4{-ms-flex-preferred-size:33.333%;flex-basis:33.333%;max-width:33.333%}.col-xs-5{-ms-flex-preferred-size:41.667%;flex-basis:41.667%;max-width:41.667%}.col-xs-6{-ms-flex-preferred-size:50%;flex-basis:50%;max-width:50%}.col-xs-7{-ms-flex-preferred-size:58.333%;flex-basis:58.333%;max-width:58.333%}.col-xs-8{-ms-flex-preferred-size:66.667%;flex-basis:66.667%;max-width:66.667%}.col-xs-9{-ms-flex-preferred-size:75%;flex-basis:75%;max-width:75%}.col-xs-10{-ms-flex-preferred-size:83.333%;flex-basis:83.333%;max-width:83.333%}.col-xs-11{-ms-flex-preferred-size:91.667%;flex-basis:91.667%;max-width:91.667%}.col-xs-12{-ms-flex-preferred-size:100%;flex-basis:100%;max-width:100%}.col-xs-offset-1{margin-left:8.333%}.col-xs-offset-2{margin-left:16.667%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-4{margin-left:33.333%}.col-xs-offset-5{margin-left:41.667%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-7{margin-left:58.333%}.col-xs-offset-8{margin-left:66.667%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-10{margin-left:83.333%}.col-xs-offset-11{margin-left:91.667%}.start-xs{-ms-flex-pack:start;-webkit-box-pack:start;justify-content:flex-start;text-align:start}.center-xs{-ms-flex-pack:center;-webkit-box-pack:center;justify-content:center;text-align:center}.end-xs{-ms-flex-pack:end;-webkit-box-pack:end;justify-content:flex-end;text-align:end}.top-xs{-ms-flex-align:start;-webkit-box-align:start;align-items:flex-start}.middle-xs{-ms-flex-align:center;-webkit-box-align:center;align-items:center}.bottom-xs{-ms-flex-align:end;-webkit-box-align:end;align-items:flex-end}.around-xs{-ms-flex-pack:distribute;justify-content:space-around}.between-xs{-ms-flex-pack:justify;-webkit-box-pack:justify;justify-content:space-between}.first-xs{-ms-flex-order:-1;-webkit-box-ordinal-group:0;order:-1}.last-xs{-ms-flex-order:1;-webkit-box-ordinal-group:2;order:1}@media only screen and (min-width:48em){.container{width:46rem}.col-sm,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9{box-sizing:border-box;-ms-flex:0 0 auto;-webkit-box-flex:0;flex:0 0 auto;padding-right:1rem;padding-left:1rem}.col-sm{-webkit-flex-grow:1;-ms-flex-positive:1;-webkit-box-flex:1;flex-grow:1;-ms-flex-preferred-size:0;flex-basis:0;max-width:100%}.col-sm-1{-ms-flex-preferred-size:8.333%;flex-basis:8.333%;max-width:8.333%}.col-sm-2{-ms-flex-preferred-size:16.667%;flex-basis:16.667%;max-width:16.667%}.col-sm-3{-ms-flex-preferred-size:25%;flex-basis:25%;max-width:25%}.col-sm-4{-ms-flex-preferred-size:33.333%;flex-basis:33.333%;max-width:33.333%}.col-sm-5{-ms-flex-preferred-size:41.667%;flex-basis:41.667%;max-width:41.667%}.col-sm-6{-ms-flex-preferred-size:50%;flex-basis:50%;max-width:50%}.col-sm-7{-ms-flex-preferred-size:58.333%;flex-basis:58.333%;max-width:58.333%}.col-sm-8{-ms-flex-preferred-size:66.667%;flex-basis:66.667%;max-width:66.667%}.col-sm-9{-ms-flex-preferred-size:75%;flex-basis:75%;max-width:75%}.col-sm-10{-ms-flex-preferred-size:83.333%;flex-basis:83.333%;max-width:83.333%}.col-sm-11{-ms-flex-preferred-size:91.667%;flex-basis:91.667%;max-width:91.667%}.col-sm-12{-ms-flex-preferred-size:100%;flex-basis:100%;max-width:100%}.col-sm-offset-1{margin-left:8.333%}.col-sm-offset-2{margin-left:16.667%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-4{margin-left:33.333%}.col-sm-offset-5{margin-left:41.667%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-7{margin-left:58.333%}.col-sm-offset-8{margin-left:66.667%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-10{margin-left:83.333%}.col-sm-offset-11{margin-left:91.667%}.start-sm{-ms-flex-pack:start;-webkit-box-pack:start;justify-content:flex-start;text-align:start}.center-sm{-ms-flex-pack:center;-webkit-box-pack:center;justify-content:center;text-align:center}.end-sm{-ms-flex-pack:end;-webkit-box-pack:end;justify-content:flex-end;text-align:end}.top-sm{-ms-flex-align:start;-webkit-box-align:start;align-items:flex-start}.middle-sm{-ms-flex-align:center;-webkit-box-align:center;align-items:center}.bottom-sm{-ms-flex-align:end;-webkit-box-align:end;align-items:flex-end}.around-sm{-ms-flex-pack:distribute;justify-content:space-around}.between-sm{-ms-flex-pack:justify;-webkit-box-pack:justify;justify-content:space-between}.first-sm{-ms-flex-order:-1;-webkit-box-ordinal-group:0;order:-1}.last-sm{-ms-flex-order:1;-webkit-box-ordinal-group:2;order:1}}@media only screen and (min-width:62em){.container{width:61rem}.col-md,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9{box-sizing:border-box;-ms-flex:0 0 auto;-webkit-box-flex:0;flex:0 0 auto;padding-right:1rem;padding-left:1rem}.col-md{-webkit-flex-grow:1;-ms-flex-positive:1;-webkit-box-flex:1;flex-grow:1;-ms-flex-preferred-size:0;flex-basis:0;max-width:100%}.col-md-1{-ms-flex-preferred-size:8.333%;flex-basis:8.333%;max-width:8.333%}.col-md-2{-ms-flex-preferred-size:16.667%;flex-basis:16.667%;max-width:16.667%}.col-md-3{-ms-flex-preferred-size:25%;flex-basis:25%;max-width:25%}.col-md-4{-ms-flex-preferred-size:33.333%;flex-basis:33.333%;max-width:33.333%}.col-md-5{-ms-flex-preferred-size:41.667%;flex-basis:41.667%;max-width:41.667%}.col-md-6{-ms-flex-preferred-size:50%;flex-basis:50%;max-width:50%}.col-md-7{-ms-flex-preferred-size:58.333%;flex-basis:58.333%;max-width:58.333%}.col-md-8{-ms-flex-preferred-size:66.667%;flex-basis:66.667%;max-width:66.667%}.col-md-9{-ms-flex-preferred-size:75%;flex-basis:75%;max-width:75%}.col-md-10{-ms-flex-preferred-size:83.333%;flex-basis:83.333%;max-width:83.333%}.col-md-11{-ms-flex-preferred-size:91.667%;flex-basis:91.667%;max-width:91.667%}.col-md-12{-ms-flex-preferred-size:100%;flex-basis:100%;max-width:100%}.col-md-offset-1{margin-left:8.333%}.col-md-offset-2{margin-left:16.667%}.col-md-offset-3{margin-left:25%}.col-md-offset-4{margin-left:33.333%}.col-md-offset-5{margin-left:41.667%}.col-md-offset-6{margin-left:50%}.col-md-offset-7{margin-left:58.333%}.col-md-offset-8{margin-left:66.667%}.col-md-offset-9{margin-left:75%}.col-md-offset-10{margin-left:83.333%}.col-md-offset-11{margin-left:91.667%}.start-md{-ms-flex-pack:start;-webkit-box-pack:start;justify-content:flex-start;text-align:start}.center-md{-ms-flex-pack:center;-webkit-box-pack:center;justify-content:center;text-align:center}.end-md{-ms-flex-pack:end;-webkit-box-pack:end;justify-content:flex-end;text-align:end}.top-md{-ms-flex-align:start;-webkit-box-align:start;align-items:flex-start}.middle-md{-ms-flex-align:center;-webkit-box-align:center;align-items:center}.bottom-md{-ms-flex-align:end;-webkit-box-align:end;align-items:flex-end}.around-md{-ms-flex-pack:distribute;justify-content:space-around}.between-md{-ms-flex-pack:justify;-webkit-box-pack:justify;justify-content:space-between}.first-md{-ms-flex-order:-1;-webkit-box-ordinal-group:0;order:-1}.last-md{-ms-flex-order:1;-webkit-box-ordinal-group:2;order:1}}@media only screen and (min-width:75em){.container{width:71rem}.col-lg,.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9{box-sizing:border-box;-ms-flex:0 0 auto;-webkit-box-flex:0;flex:0 0 auto;padding-right:1rem;padding-left:1rem}.col-lg{-webkit-flex-grow:1;-ms-flex-positive:1;-webkit-box-flex:1;flex-grow:1;-ms-flex-preferred-size:0;flex-basis:0;max-width:100%}.col-lg-1{-ms-flex-preferred-size:8.333%;flex-basis:8.333%;max-width:8.333%}.col-lg-2{-ms-flex-preferred-size:16.667%;flex-basis:16.667%;max-width:16.667%}.col-lg-3{-ms-flex-preferred-size:25%;flex-basis:25%;max-width:25%}.col-lg-4{-ms-flex-preferred-size:33.333%;flex-basis:33.333%;max-width:33.333%}.col-lg-5{-ms-flex-preferred-size:41.667%;flex-basis:41.667%;max-width:41.667%}.col-lg-6{-ms-flex-preferred-size:50%;flex-basis:50%;max-width:50%}.col-lg-7{-ms-flex-preferred-size:58.333%;flex-basis:58.333%;max-width:58.333%}.col-lg-8{-ms-flex-preferred-size:66.667%;flex-basis:66.667%;max-width:66.667%}.col-lg-9{-ms-flex-preferred-size:75%;flex-basis:75%;max-width:75%}.col-lg-10{-ms-flex-preferred-size:83.333%;flex-basis:83.333%;max-width:83.333%}.col-lg-11{-ms-flex-preferred-size:91.667%;flex-basis:91.667%;max-width:91.667%}.col-lg-12{-ms-flex-preferred-size:100%;flex-basis:100%;max-width:100%}.col-lg-offset-1{margin-left:8.333%}.col-lg-offset-2{margin-left:16.667%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-4{margin-left:33.333%}.col-lg-offset-5{margin-left:41.667%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-7{margin-left:58.333%}.col-lg-offset-8{margin-left:66.667%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-10{margin-left:83.333%}.col-lg-offset-11{margin-left:91.667%}.start-lg{-ms-flex-pack:start;-webkit-box-pack:start;justify-content:flex-start;text-align:start}.center-lg{-ms-flex-pack:center;-webkit-box-pack:center;justify-content:center;text-align:center}.end-lg{-ms-flex-pack:end;-webkit-box-pack:end;justify-content:flex-end;text-align:end}.top-lg{-ms-flex-align:start;-webkit-box-align:start;align-items:flex-start}.middle-lg{-ms-flex-align:center;-webkit-box-align:center;align-items:center}.bottom-lg{-ms-flex-align:end;-webkit-box-align:end;align-items:flex-end}.around-lg{-ms-flex-pack:distribute;justify-content:space-around}.between-lg{-ms-flex-pack:justify;-webkit-box-pack:justify;justify-content:space-between}.first-lg{-ms-flex-order:-1;-webkit-box-ordinal-group:0;order:-1}.last-lg{-ms-flex-order:1;-webkit-box-ordinal-group:2;order:1}}
--------------------------------------------------------------------------------
/static/css/normalize.scss:
--------------------------------------------------------------------------------
1 | /*! normalize.css v7.0.0 | MIT License | github.com/necolas/normalize.css */
2 |
3 | /* Document
4 | ========================================================================== */
5 |
6 | /**
7 | * 1. Correct the line height in all browsers.
8 | * 2. Prevent adjustments of font size after orientation changes in
9 | * IE on Windows Phone and in iOS.
10 | */
11 |
12 | html {
13 | line-height: 1.15; /* 1 */
14 | -ms-text-size-adjust: 100%; /* 2 */
15 | -webkit-text-size-adjust: 100%; /* 2 */
16 | }
17 |
18 | /* Sections
19 | ========================================================================== */
20 |
21 | /**
22 | * Remove the margin in all browsers (opinionated).
23 | */
24 |
25 | body {
26 | margin: 0;
27 | }
28 |
29 | /**
30 | * Add the correct display in IE 9-.
31 | */
32 |
33 | article,
34 | aside,
35 | footer,
36 | header,
37 | nav,
38 | section {
39 | display: block;
40 | }
41 |
42 | /**
43 | * Correct the font size and margin on `h1` elements within `section` and
44 | * `article` contexts in Chrome, Firefox, and Safari.
45 | */
46 |
47 | h1 {
48 | font-size: 2em;
49 | margin: 0.67em 0;
50 | }
51 |
52 | /* Grouping content
53 | ========================================================================== */
54 |
55 | /**
56 | * Add the correct display in IE 9-.
57 | * 1. Add the correct display in IE.
58 | */
59 |
60 | figcaption,
61 | figure,
62 | main { /* 1 */
63 | display: block;
64 | }
65 |
66 | /**
67 | * Add the correct margin in IE 8.
68 | */
69 |
70 | figure {
71 | margin: 1em 40px;
72 | }
73 |
74 | /**
75 | * 1. Add the correct box sizing in Firefox.
76 | * 2. Show the overflow in Edge and IE.
77 | */
78 |
79 | hr {
80 | box-sizing: content-box; /* 1 */
81 | height: 0; /* 1 */
82 | overflow: visible; /* 2 */
83 | }
84 |
85 | /**
86 | * 1. Correct the inheritance and scaling of font size in all browsers.
87 | * 2. Correct the odd `em` font sizing in all browsers.
88 | */
89 |
90 | pre {
91 | font-family: monospace, monospace; /* 1 */
92 | font-size: 1em; /* 2 */
93 | }
94 |
95 | /* Text-level semantics
96 | ========================================================================== */
97 |
98 | /**
99 | * 1. Remove the gray background on active links in IE 10.
100 | * 2. Remove gaps in links underline in iOS 8+ and Safari 8+.
101 | */
102 |
103 | a {
104 | background-color: transparent; /* 1 */
105 | -webkit-text-decoration-skip: objects; /* 2 */
106 | }
107 |
108 | /**
109 | * 1. Remove the bottom border in Chrome 57- and Firefox 39-.
110 | * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
111 | */
112 |
113 | abbr[title] {
114 | border-bottom: none; /* 1 */
115 | text-decoration: underline; /* 2 */
116 | text-decoration: underline dotted; /* 2 */
117 | }
118 |
119 | /**
120 | * Prevent the duplicate application of `bolder` by the next rule in Safari 6.
121 | */
122 |
123 | b,
124 | strong {
125 | font-weight: inherit;
126 | }
127 |
128 | /**
129 | * Add the correct font weight in Chrome, Edge, and Safari.
130 | */
131 |
132 | b,
133 | strong {
134 | font-weight: bolder;
135 | }
136 |
137 | /**
138 | * 1. Correct the inheritance and scaling of font size in all browsers.
139 | * 2. Correct the odd `em` font sizing in all browsers.
140 | */
141 |
142 | code,
143 | kbd,
144 | samp {
145 | font-family: monospace, monospace; /* 1 */
146 | font-size: 1em; /* 2 */
147 | }
148 |
149 | /**
150 | * Add the correct font style in Android 4.3-.
151 | */
152 |
153 | dfn {
154 | font-style: italic;
155 | }
156 |
157 | /**
158 | * Add the correct background and color in IE 9-.
159 | */
160 |
161 | mark {
162 | background-color: #ff0;
163 | color: #000;
164 | }
165 |
166 | /**
167 | * Add the correct font size in all browsers.
168 | */
169 |
170 | small {
171 | font-size: 80%;
172 | }
173 |
174 | /**
175 | * Prevent `sub` and `sup` elements from affecting the line height in
176 | * all browsers.
177 | */
178 |
179 | sub,
180 | sup {
181 | font-size: 75%;
182 | line-height: 0;
183 | position: relative;
184 | vertical-align: baseline;
185 | }
186 |
187 | sub {
188 | bottom: -0.25em;
189 | }
190 |
191 | sup {
192 | top: -0.5em;
193 | }
194 |
195 | /* Embedded content
196 | ========================================================================== */
197 |
198 | /**
199 | * Add the correct display in IE 9-.
200 | */
201 |
202 | audio,
203 | video {
204 | display: inline-block;
205 | }
206 |
207 | /**
208 | * Add the correct display in iOS 4-7.
209 | */
210 |
211 | audio:not([controls]) {
212 | display: none;
213 | height: 0;
214 | }
215 |
216 | /**
217 | * Remove the border on images inside links in IE 10-.
218 | */
219 |
220 | img {
221 | border-style: none;
222 | }
223 |
224 | /**
225 | * Hide the overflow in IE.
226 | */
227 |
228 | svg:not(:root) {
229 | overflow: hidden;
230 | }
231 |
232 | /* Forms
233 | ========================================================================== */
234 |
235 | /**
236 | * 1. Change the font styles in all browsers (opinionated).
237 | * 2. Remove the margin in Firefox and Safari.
238 | */
239 |
240 | button,
241 | input,
242 | optgroup,
243 | select,
244 | textarea {
245 | font-family: sans-serif; /* 1 */
246 | font-size: 100%; /* 1 */
247 | line-height: 1.15; /* 1 */
248 | margin: 0; /* 2 */
249 | }
250 |
251 | /**
252 | * Show the overflow in IE.
253 | * 1. Show the overflow in Edge.
254 | */
255 |
256 | button,
257 | input { /* 1 */
258 | overflow: visible;
259 | }
260 |
261 | /**
262 | * Remove the inheritance of text transform in Edge, Firefox, and IE.
263 | * 1. Remove the inheritance of text transform in Firefox.
264 | */
265 |
266 | button,
267 | select { /* 1 */
268 | text-transform: none;
269 | }
270 |
271 | /**
272 | * 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`
273 | * controls in Android 4.
274 | * 2. Correct the inability to style clickable types in iOS and Safari.
275 | */
276 |
277 | button,
278 | html [type="button"], /* 1 */
279 | [type="reset"],
280 | [type="submit"] {
281 | -webkit-appearance: button; /* 2 */
282 | }
283 |
284 | /**
285 | * Remove the inner border and padding in Firefox.
286 | */
287 |
288 | button::-moz-focus-inner,
289 | [type="button"]::-moz-focus-inner,
290 | [type="reset"]::-moz-focus-inner,
291 | [type="submit"]::-moz-focus-inner {
292 | border-style: none;
293 | padding: 0;
294 | }
295 |
296 | /**
297 | * Restore the focus styles unset by the previous rule.
298 | */
299 |
300 | button:-moz-focusring,
301 | [type="button"]:-moz-focusring,
302 | [type="reset"]:-moz-focusring,
303 | [type="submit"]:-moz-focusring {
304 | outline: 1px dotted ButtonText;
305 | }
306 |
307 | /**
308 | * Correct the padding in Firefox.
309 | */
310 |
311 | fieldset {
312 | padding: 0.35em 0.75em 0.625em;
313 | }
314 |
315 | /**
316 | * 1. Correct the text wrapping in Edge and IE.
317 | * 2. Correct the color inheritance from `fieldset` elements in IE.
318 | * 3. Remove the padding so developers are not caught out when they zero out
319 | * `fieldset` elements in all browsers.
320 | */
321 |
322 | legend {
323 | box-sizing: border-box; /* 1 */
324 | color: inherit; /* 2 */
325 | display: table; /* 1 */
326 | max-width: 100%; /* 1 */
327 | padding: 0; /* 3 */
328 | white-space: normal; /* 1 */
329 | }
330 |
331 | /**
332 | * 1. Add the correct display in IE 9-.
333 | * 2. Add the correct vertical alignment in Chrome, Firefox, and Opera.
334 | */
335 |
336 | progress {
337 | display: inline-block; /* 1 */
338 | vertical-align: baseline; /* 2 */
339 | }
340 |
341 | /**
342 | * Remove the default vertical scrollbar in IE.
343 | */
344 |
345 | textarea {
346 | overflow: auto;
347 | }
348 |
349 | /**
350 | * 1. Add the correct box sizing in IE 10-.
351 | * 2. Remove the padding in IE 10-.
352 | */
353 |
354 | [type="checkbox"],
355 | [type="radio"] {
356 | box-sizing: border-box; /* 1 */
357 | padding: 0; /* 2 */
358 | }
359 |
360 | /**
361 | * Correct the cursor style of increment and decrement buttons in Chrome.
362 | */
363 |
364 | [type="number"]::-webkit-inner-spin-button,
365 | [type="number"]::-webkit-outer-spin-button {
366 | height: auto;
367 | }
368 |
369 | /**
370 | * 1. Correct the odd appearance in Chrome and Safari.
371 | * 2. Correct the outline style in Safari.
372 | */
373 |
374 | [type="search"] {
375 | -webkit-appearance: textfield; /* 1 */
376 | outline-offset: -2px; /* 2 */
377 | }
378 |
379 | /**
380 | * Remove the inner padding and cancel buttons in Chrome and Safari on macOS.
381 | */
382 |
383 | [type="search"]::-webkit-search-cancel-button,
384 | [type="search"]::-webkit-search-decoration {
385 | -webkit-appearance: none;
386 | }
387 |
388 | /**
389 | * 1. Correct the inability to style clickable types in iOS and Safari.
390 | * 2. Change font properties to `inherit` in Safari.
391 | */
392 |
393 | ::-webkit-file-upload-button {
394 | -webkit-appearance: button; /* 1 */
395 | font: inherit; /* 2 */
396 | }
397 |
398 | /* Interactive
399 | ========================================================================== */
400 |
401 | /*
402 | * Add the correct display in IE 9-.
403 | * 1. Add the correct display in Edge, IE, and Firefox.
404 | */
405 |
406 | details, /* 1 */
407 | menu {
408 | display: block;
409 | }
410 |
411 | /*
412 | * Add the correct display in all browsers.
413 | */
414 |
415 | summary {
416 | display: list-item;
417 | }
418 |
419 | /* Scripting
420 | ========================================================================== */
421 |
422 | /**
423 | * Add the correct display in IE 9-.
424 | */
425 |
426 | canvas {
427 | display: inline-block;
428 | }
429 |
430 | /**
431 | * Add the correct display in IE.
432 | */
433 |
434 | template {
435 | display: none;
436 | }
437 |
438 | /* Hidden
439 | ========================================================================== */
440 |
441 | /**
442 | * Add the correct display in IE 10-.
443 | */
444 |
445 | [hidden] {
446 | display: none;
447 | }
448 |
--------------------------------------------------------------------------------
/static/js/prism.js:
--------------------------------------------------------------------------------
1 | /* http://prismjs.com/download.html?themes=prism&languages=markup+css+clike+javascript+git+go+graphql+json+python+jsx+scss */
2 | var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(){var e=/\blang(?:uage)?-(\w+)\b/i,t=0,n=_self.Prism={manual:_self.Prism&&_self.Prism.manual,util:{encode:function(e){return e instanceof a?new a(e.type,n.util.encode(e.content),e.alias):"Array"===n.util.type(e)?e.map(n.util.encode):e.replace(/&/g,"&").replace(/e.length)return;if(!(w instanceof s)){h.lastIndex=0;var _=h.exec(w),P=1;if(!_&&m&&b!=t.length-1){if(h.lastIndex=k,_=h.exec(e),!_)break;for(var A=_.index+(d?_[1].length:0),j=_.index+_[0].length,x=b,O=k,S=t.length;S>x&&(j>O||!t[x].type&&!t[x-1].greedy);++x)O+=t[x].length,A>=O&&(++b,k=O);if(t[b]instanceof s||t[x-1].greedy)continue;P=x-b,w=e.slice(k,O),_.index-=k}if(_){d&&(p=_[1].length);var A=_.index+p,_=_[0].slice(p),j=A+_.length,N=w.slice(0,A),C=w.slice(j),E=[b,P];N&&(++b,k+=N.length,E.push(N));var L=new s(u,f?n.tokenize(_,f):_,y,_,m);if(E.push(L),C&&E.push(C),Array.prototype.splice.apply(t,E),1!=P&&n.matchGrammar(e,t,a,b,k,!0,u),l)break}else if(l)break}}}}},tokenize:function(e,t){var a=[e],r=t.rest;if(r){for(var i in r)t[i]=r[i];delete t.rest}return n.matchGrammar(e,a,t,0,0,!1),a},hooks:{all:{},add:function(e,t){var a=n.hooks.all;a[e]=a[e]||[],a[e].push(t)},run:function(e,t){var a=n.hooks.all[e];if(a&&a.length)for(var r,i=0;r=a[i++];)r(t)}}},a=n.Token=function(e,t,n,a,r){this.type=e,this.content=t,this.alias=n,this.length=0|(a||"").length,this.greedy=!!r};if(a.stringify=function(e,t,r){if("string"==typeof e)return e;if("Array"===n.util.type(e))return e.map(function(n){return a.stringify(n,t,e)}).join("");var i={type:e.type,content:a.stringify(e.content,t,r),tag:"span",classes:["token",e.type],attributes:{},language:t,parent:r};if("comment"==i.type&&(i.attributes.spellcheck="true"),e.alias){var l="Array"===n.util.type(e.alias)?e.alias:[e.alias];Array.prototype.push.apply(i.classes,l)}n.hooks.run("wrap",i);var o=Object.keys(i.attributes).map(function(e){return e+'="'+(i.attributes[e]||"").replace(/"/g,""")+'"'}).join(" ");return"<"+i.tag+' class="'+i.classes.join(" ")+'"'+(o?" "+o:"")+">"+i.content+""+i.tag+">"},!_self.document)return _self.addEventListener?(_self.addEventListener("message",function(e){var t=JSON.parse(e.data),a=t.language,r=t.code,i=t.immediateClose;_self.postMessage(n.highlight(r,n.languages[a],a)),i&&_self.close()},!1),_self.Prism):_self.Prism;var r=document.currentScript||[].slice.call(document.getElementsByTagName("script")).pop();return r&&(n.filename=r.src,!document.addEventListener||n.manual||r.hasAttribute("data-manual")||("loading"!==document.readyState?window.requestAnimationFrame?window.requestAnimationFrame(n.highlightAll):window.setTimeout(n.highlightAll,16):document.addEventListener("DOMContentLoaded",n.highlightAll))),_self.Prism}();"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism);
3 | Prism.languages.markup={comment://,prolog:/<\?[\s\S]+?\?>/,doctype://i,cdata://i,tag:{pattern:/<\/?(?!\d)[^\s>\/=$<]+(?:\s+[^\s>\/=]+(?:=(?:("|')(?:\\\1|\\?(?!\1)[\s\S])*\1|[^\s'">=]+))?)*\s*\/?>/i,inside:{tag:{pattern:/^<\/?[^\s>\/]+/i,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"attr-value":{pattern:/=(?:('|")[\s\S]*?(\1)|[^\s>]+)/i,inside:{punctuation:/[=>"']/}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:/?[\da-z]{1,8};/i},Prism.languages.markup.tag.inside["attr-value"].inside.entity=Prism.languages.markup.entity,Prism.hooks.add("wrap",function(a){"entity"===a.type&&(a.attributes.title=a.content.replace(/&/,"&"))}),Prism.languages.xml=Prism.languages.markup,Prism.languages.html=Prism.languages.markup,Prism.languages.mathml=Prism.languages.markup,Prism.languages.svg=Prism.languages.markup;
4 | Prism.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:/@[\w-]+?.*?(;|(?=\s*\{))/i,inside:{rule:/@[\w-]+/}},url:/url\((?:(["'])(\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1|.*?)\)/i,selector:/[^\{\}\s][^\{\};]*?(?=\s*\{)/,string:{pattern:/("|')(\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},property:/(\b|\B)[\w-]+(?=\s*:)/i,important:/\B!important\b/i,"function":/[-a-z0-9]+(?=\()/i,punctuation:/[(){};:]/},Prism.languages.css.atrule.inside.rest=Prism.util.clone(Prism.languages.css),Prism.languages.markup&&(Prism.languages.insertBefore("markup","tag",{style:{pattern:/(