mirror of
https://github.com/Brandon-Rozek/website.git
synced 2024-11-24 17:16:30 -05:00
Website snapshot
This commit is contained in:
parent
ee0ab66d73
commit
50ec3688a5
281 changed files with 21066 additions and 0 deletions
25
content/blog/2015-04-16-function-two-points-theory.md
Normal file
25
content/blog/2015-04-16-function-two-points-theory.md
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
---
|
||||||
|
id: 65
|
||||||
|
title: Math Theory for Function Between Two Points
|
||||||
|
date: 2015-04-16T22:15:21+00:00
|
||||||
|
author: Brandon Rozek
|
||||||
|
layout: post
|
||||||
|
guid: http://brandonrozek.com/?p=65
|
||||||
|
permalink: /2015/04/function-two-points-theory/
|
||||||
|
medium_post:
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";N;s:10:"author_url";N;s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";N;s:2:"id";N;s:21:"follower_notification";N;s:7:"license";N;s:14:"publication_id";N;s:6:"status";N;s:3:"url";N;}'
|
||||||
|
dsq_thread_id:
|
||||||
|
- ""
|
||||||
|
- ""
|
||||||
|
tumblr_post_id:
|
||||||
|
- "138122079174"
|
||||||
|
kind:
|
||||||
|
- note
|
||||||
|
---
|
||||||
|
<!--more-->
|
||||||
|
|
||||||
|
|
||||||
|
[<img src="https://brandonrozek.com/wp-content/uploads/2015/04/Part1.jpeg" alt="Part1" width="1700" height="2338" class="alignnone size-full wp-image-66" />](https://brandonrozek.com/wp-content/uploads/2015/04/Part1.jpeg){.broken_link}
|
||||||
|
[<img src="https://brandonrozek.com/wp-content/uploads/2015/04/Part2.jpeg" alt="Part2" width="1700" height="2338" class="alignnone size-full wp-image-67" />](https://brandonrozek.com/wp-content/uploads/2015/04/Part2.jpeg){.broken_link}
|
||||||
|
[<img src="https://brandonrozek.com/wp-content/uploads/2015/04/Part3.jpeg" alt="Part3" width="1700" height="2338" class="alignnone size-full wp-image-68" />](https://brandonrozek.com/wp-content/uploads/2015/04/Part3.jpeg){.broken_link}
|
||||||
|
[<img src="https://brandonrozek.com/wp-content/uploads/2015/04/Part4.jpeg" alt="Part4" width="1700" height="2338" class="alignnone size-full wp-image-69" />](https://brandonrozek.com/wp-content/uploads/2015/04/Part4.jpeg){.broken_link}
|
89
content/blog/2015-04-16-responsive-layout-and-animation.md
Normal file
89
content/blog/2015-04-16-responsive-layout-and-animation.md
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
---
|
||||||
|
id: 57
|
||||||
|
title: Responsive Layout and Animation
|
||||||
|
date: 2015-04-16T22:19:36+00:00
|
||||||
|
author: Brandon Rozek
|
||||||
|
layout: post
|
||||||
|
guid: http://brandonrozek.com/?p=57
|
||||||
|
permalink: /2015/04/responsive-layout-and-animation/
|
||||||
|
medium_post:
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";N;s:10:"author_url";N;s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";N;s:2:"id";N;s:21:"follower_notification";N;s:7:"license";N;s:14:"publication_id";N;s:6:"status";N;s:3:"url";N;}'
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";N;s:10:"author_url";N;s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";N;s:2:"id";N;s:21:"follower_notification";N;s:7:"license";N;s:14:"publication_id";N;s:6:"status";N;s:3:"url";N;}'
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";N;s:10:"author_url";N;s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";N;s:2:"id";N;s:21:"follower_notification";N;s:7:"license";N;s:14:"publication_id";N;s:6:"status";N;s:3:"url";N;}'
|
||||||
|
dsq_thread_id:
|
||||||
|
- "3688354609"
|
||||||
|
- "3688354609"
|
||||||
|
mf2_cite:
|
||||||
|
- 'a:1:{s:6:"author";a:0:{}}'
|
||||||
|
- 'a:1:{s:6:"author";a:0:{}}'
|
||||||
|
tumblr_post_id:
|
||||||
|
- "135656876494"
|
||||||
|
- "135656876494"
|
||||||
|
kind:
|
||||||
|
- article
|
||||||
|
---
|
||||||
|
I saw [Mike Riethmuller’s](http://madebymike.com.au/) precision typography [pen](http://codepen.io/MadeByMike/pen/YPJJYv){.broken_link}, and was highly impressed. I think the equation used has other purposes as well
|
||||||
|
|
||||||
|
<!--more-->
|
||||||
|
|
||||||
|
Side Note: I changed the form of the equation to something similar to y = mx + b so that I can more easily recognize how it functions
|
||||||
|
|
||||||
|
#### Responsive Layout
|
||||||
|
|
||||||
|
There are many occasions where I want an element on the page to move between two points. The navigation in the header of my site (at the time of writing) is a great example of this. So knowing the two points I want it to lie between and having the screen width as the variable, I can plug in… [<img class="aligncenter size-full wp-image-58" src="https://brandonrozek.com/wp-content/uploads/2015/04/responsivelayoutequation.gif" alt="f(x) = (100 * (b - a)/(d - c))X + (ad - bc) / (d - c)" width="219" height="36" />](https://brandonrozek.com/wp-content/uploads/2015/04/responsivelayoutequation.gif){.broken_link} where a is the start pixel b is the end pixel c is the start media query d is the end media query and X is the screen width out of 100 otherwise known as 1vw \*\*Don’t forget to keep track of your units!! Whether it’s px/rem/em/etc.\*\* Say I want to push a box towards the right a minimum of 5px, a maximum of 20px and for the push to vary between the widths 400-800px. Then I would write…
|
||||||
|
|
||||||
|
<pre><code class="language-css">
|
||||||
|
@media (min-width: 400px) and (max-width: 800px) {
|
||||||
|
|
||||||
|
.box {
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
left: calc(3.75vw - 10px) /*After simplifying the equation*/ }
|
||||||
|
|
||||||
|
}
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
That would only make it vary between 400-800px. Now we need to include what happens under 400px and over 800px.
|
||||||
|
|
||||||
|
<pre><code class="language-css">
|
||||||
|
@media (max-width: 400px) {
|
||||||
|
|
||||||
|
.box {
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
left: 5px; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 400px) and (max-width: 800px) {
|
||||||
|
|
||||||
|
.box {
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
left: calc(3.75vw - 10px); }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 800px) {
|
||||||
|
|
||||||
|
.box {
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
left: 20px; }
|
||||||
|
|
||||||
|
}
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
This is exactly like Mike’s pen, but instead he uses the equation to adjust the font-size between an upper and lower bound. You can apply this method to anything that accepts calc() and viewport units. Here is my [pen](http://codepen.io/brandonrozek/pen/JoQVEb){.broken_link} showing some use cases. To make your life easier, I made a quick little tool where you can input the variables and it will provide you with a simpler form of the equation to put into your calc() function [here](http://codepen.io/brandonrozek/pen/KpPwGL){.broken_link}.
|
||||||
|
|
||||||
|
#### Animation
|
||||||
|
|
||||||
|
This is where the majority of my research went towards. It’s not as practical as say positioning an element is but I find it interesting. Like, what if I can manipulate the acceleration of the function? [<img class="aligncenter size-full wp-image-62" src="https://brandonrozek.com/wp-content/uploads/2015/04/accelerationequation.gif" alt="f(x) = ((b - a) / (d^n - c^n))X^n + (ad^n - bc^n) / (d^n - c^n) " width="202" height="36" />](https://brandonrozek.com/wp-content/uploads/2015/04/accelerationequation.gif){.broken_link} Where a is the start unit b is the end unit c is the start time d is the end time n is the acceleration modifier and X is time The interesting part of the function here is the n. If I keep n at 1, then the acceleration is constant. If it’s less than one, then it’s fast in the beginning and slows down at the end. If it’s greater than one, then it’s the opposite. I also made a little pen [here](http://codepen.io/brandonrozek/pen/RNzdOV){.broken_link} to demo this for you.
|
||||||
|
|
||||||
|
#### Conclusion
|
||||||
|
|
||||||
|
Having a function that goes between two points is incredibly handy. Now when it comes to positioning, I don’t have to guess which values match the design. If i cant something to be between here and there fluidly, I can do it. What about animation? Chaining them together should have an interesting affect… P.S For those of you crazy people who like to see the theory behind the math, (like myself) I have my scanned work [here](https://brandonrozek.com/2015/04/function-two-points-theory/).
|
130
content/blog/2015-05-23-animatable-border.md
Normal file
130
content/blog/2015-05-23-animatable-border.md
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
---
|
||||||
|
id: 85
|
||||||
|
title: 'Animatable: Border'
|
||||||
|
date: 2015-05-23T19:59:25+00:00
|
||||||
|
author: Brandon Rozek
|
||||||
|
layout: post
|
||||||
|
guid: http://brandonrozek.com/?p=85
|
||||||
|
permalink: /2015/05/animatable-border/
|
||||||
|
medium_post:
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";N;s:10:"author_url";N;s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";N;s:2:"id";N;s:21:"follower_notification";N;s:7:"license";N;s:14:"publication_id";N;s:6:"status";N;s:3:"url";N;}'
|
||||||
|
dsq_thread_id:
|
||||||
|
- "3807156267"
|
||||||
|
- "3807156267"
|
||||||
|
tumblr_post_id:
|
||||||
|
- "135657818649"
|
||||||
|
- "135657818649"
|
||||||
|
mf2_cite:
|
||||||
|
- 'a:1:{s:6:"author";a:0:{}}'
|
||||||
|
- 'a:1:{s:6:"author";a:0:{}}'
|
||||||
|
kind:
|
||||||
|
- article
|
||||||
|
---
|
||||||
|
This is part 1 of an animation series I’m doing. It is inspired by Lea Verou’s talk called “[The Humble Border-Radius.](https://www.youtube.com/watch?v=JSaMl2OKjfQ)” I looked at her site and she has a good demo of a bunch of different animations [here](http://lea.verou.me/2011/10/animatable-a-css-transitions-gallery/). My goal here is to create a more comprehensive guide to these different animatable properties–mainly for future reference. Animations play a big part in adding interactivity to the web, so why not explore some possible options?
|
||||||
|
|
||||||
|
<!--more-->
|
||||||
|
|
||||||
|
This post follows well along with my Codepen [demo](http://codepen.io/brandonrozek/full/waWMWR/){.broken_link}, where I’ll state the box number that applies to what I’m talking about. \*\*Initial values must be explicitly stated, implicit initial values are generally ignored in animation\*\* \*\\*\*In English terms, you must have already stated a value for what you are animating before you animate it\*\**
|
||||||
|
|
||||||
|
### Border-color {#border-color}
|
||||||
|
|
||||||
|
* Accepts 1 to 4 color values
|
||||||
|
* Each value corresponds to each side of the border (starting from the top and going clockwise)
|
||||||
|
* Initial Value: currentColor
|
||||||
|
|
||||||
|
Border-color animates by splitting the colors to their red, green and blue components and raises/lowers them to it’s new value. ([Demo](http://codepen.io/brandonrozek/pen/PqzPMe){.broken_link}) ([Spec](http://www.w3.org/TR/css3-transitions/#animtype-color)) Example of animation corresponds to #1 in the pen, but I will rewrite the relevant code here.
|
||||||
|
|
||||||
|
<pre><code class="language-css">
|
||||||
|
@keyframes color {
|
||||||
|
|
||||||
|
to { border-color: purple red green blue; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.border-color {
|
||||||
|
|
||||||
|
border-color: white;
|
||||||
|
|
||||||
|
animation: color .4s ease-in .1s infinite alternate;
|
||||||
|
|
||||||
|
}
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Border-radius {#border-radius}
|
||||||
|
|
||||||
|
* Accepts 1 to 4 values for both horizontal and vertical radii
|
||||||
|
* Each value corresponds to a corner starting from the top left and going clockwise
|
||||||
|
* Initial Value: 0
|
||||||
|
|
||||||
|
<pre><code class="language-css">
|
||||||
|
.border-radius {
|
||||||
|
|
||||||
|
border-radius: 40% 30% 60% 50% / 20% 40% 60% 80%;
|
||||||
|
|
||||||
|
/** is the same as **/
|
||||||
|
|
||||||
|
border-top-left-radius: 40% 20%;
|
||||||
|
|
||||||
|
border-top-right-radius: 30% 40%;
|
||||||
|
|
||||||
|
border-bottom-right-radius: 60% 60%;
|
||||||
|
|
||||||
|
border-bottom-left-radius: 50% 80%;
|
||||||
|
|
||||||
|
/** where the first value is the horizontal radius and the second value the vertical radius**/
|
||||||
|
|
||||||
|
}
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
For animation, this corresponds to #2 in the pen I made at the top. I’ll repeat the relevant code here.
|
||||||
|
|
||||||
|
<pre><code class="language-css">
|
||||||
|
|
||||||
|
@keyframes radius {
|
||||||
|
|
||||||
|
to { border-radius: 20%; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.border-radius {
|
||||||
|
|
||||||
|
border-radius: 0;
|
||||||
|
|
||||||
|
animation: radius .5s ease-in .1s infinite alternate;
|
||||||
|
|
||||||
|
}
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Border-width {#border-width}
|
||||||
|
|
||||||
|
* Accepts 1 to 4 values
|
||||||
|
* Each value corresponds to a side of the border (starting from the top and going clockwise)
|
||||||
|
* Initial Value: medium
|
||||||
|
|
||||||
|
For animation, this corresponds to #3 in the pen I made at the top. I’ll repeat the relevant code here.
|
||||||
|
|
||||||
|
<pre><code class="language-css">
|
||||||
|
@keyframes width {
|
||||||
|
|
||||||
|
to { border-width: .15rem .25rem .15rem .25rem; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.border-width {
|
||||||
|
|
||||||
|
border-width: .7rem;
|
||||||
|
|
||||||
|
animation: width .5s ease-in .1s infinite alternate;
|
||||||
|
|
||||||
|
}
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Conclusion
|
||||||
|
|
||||||
|
Animations are quite enjoyable. The last box in my Codepen demo tries combining all three of those animations. (Super Wacky!) You don’t need to use keyframe animations to achieve this, you can also use the transition property. I used keyframes so you can better visualize what’s going on. There are plenty of other animatable properties to go through, so I’ll get started on those. In the meantime, if you want to look at some of the sites I used for research I’ll include the links below. <https://developer.mozilla.org/en-US/docs/Web/CSS/animation> <http://www.w3.org/TR/css3-transitions/#animatable-css> <https://developer.mozilla.org/en-US/docs/Web/CSS/border-color> <https://developer.mozilla.org/en-US/docs/Web/CSS/border-radius> <https://developer.mozilla.org/en-US/docs/Web/CSS/border-width> <https://docs.webplatform.org/wiki/css/properties/border-color>{.broken_link} <https://docs.webplatform.org/wiki/css/properties/border-radius>{.broken_link} <https://docs.webplatform.org/wiki/css/properties/border-width>{.broken_link}
|
194
content/blog/2015-09-14-animatable-box-model.md
Normal file
194
content/blog/2015-09-14-animatable-box-model.md
Normal file
|
@ -0,0 +1,194 @@
|
||||||
|
---
|
||||||
|
id: 155
|
||||||
|
title: 'Animatable: Box Model'
|
||||||
|
date: 2015-09-14T12:07:52+00:00
|
||||||
|
author: Brandon Rozek
|
||||||
|
layout: post
|
||||||
|
guid: https://brandonrozek.com/?p=155
|
||||||
|
permalink: /2015/09/animatable-box-model/
|
||||||
|
medium_post:
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";N;s:10:"author_url";N;s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";N;s:2:"id";N;s:21:"follower_notification";N;s:7:"license";N;s:14:"publication_id";N;s:6:"status";N;s:3:"url";N;}'
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";s:74:"https://cdn-images-1.medium.com/fit/c/200/200/1*dmbNkD5D-u45r44go_cf0g.png";s:10:"author_url";s:32:"https://medium.com/@brandonrozek";s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";s:2:"no";s:2:"id";s:12:"17293fa1c115";s:21:"follower_notification";s:2:"no";s:7:"license";s:19:"all-rights-reserved";s:14:"publication_id";s:2:"-1";s:6:"status";s:6:"public";s:3:"url";s:66:"https://medium.com/@brandonrozek/animatable-box-model-17293fa1c115";}'
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";s:74:"https://cdn-images-1.medium.com/fit/c/200/200/1*dmbNkD5D-u45r44go_cf0g.png";s:10:"author_url";s:32:"https://medium.com/@brandonrozek";s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";s:2:"no";s:2:"id";s:12:"17293fa1c115";s:21:"follower_notification";s:2:"no";s:7:"license";s:19:"all-rights-reserved";s:14:"publication_id";s:2:"-1";s:6:"status";s:6:"public";s:3:"url";s:66:"https://medium.com/@brandonrozek/animatable-box-model-17293fa1c115";}'
|
||||||
|
dsq_thread_id:
|
||||||
|
- "4130257548"
|
||||||
|
- "4130257548"
|
||||||
|
mf2_cite:
|
||||||
|
- 'a:1:{s:6:"author";a:0:{}}'
|
||||||
|
- 'a:1:{s:6:"author";a:0:{}}'
|
||||||
|
tumblr_post_id:
|
||||||
|
- "135657238924"
|
||||||
|
- "135657238924"
|
||||||
|
kind:
|
||||||
|
- article
|
||||||
|
---
|
||||||
|
This post is part 2 of an animation series I am doing; you can read part 1 [here](https://brandonrozek.com/2015/05/animatable-border/). In this post, we’ll look at the different parts of the box model (margin, padding, height, and width) and see how they can be animated.
|
||||||
|
|
||||||
|
<!--more-->
|
||||||
|
|
||||||
|
The W3C has a great starting reference for the [CSS Box Model](http://www.w3.org/TR/CSS2/box.html). It can be wordy at times, but has everything you need to know. I had never heard of _margin collapsing_ until I read that. Luckily there is a great [post](https://css-tricks.com/what-you-should-know-about-collapsing-margins/) on CSS-Tricks written by Geoff Graham explaining what it is. To see it all in action, take a look at this Codepen [demo](http://codepen.io/brandonrozek/full/RWPYgV/){.broken_link}– I reference this multiple times in this post. Now, on to the box model.
|
||||||
|
|
||||||
|
### Margin
|
||||||
|
|
||||||
|
* Accepts 1 to 4 numerical values (negative numbers are allowed)
|
||||||
|
* If you use 4 values, the first value is the top margin and the rest follow in a clockwise fashion
|
||||||
|
* Initial value: 0
|
||||||
|
|
||||||
|
Margins can be described as the space around an element. In the Codepen demo (#1), it shows 2 boxes. The first box has a margin-right that is increasing, making it seem as though it’s pushing the second box away.
|
||||||
|
|
||||||
|
<pre><code class="language-css">
|
||||||
|
@keyframes margin {
|
||||||
|
|
||||||
|
to { margin-right: 7rem; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.margin {
|
||||||
|
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
|
height: 5rem;
|
||||||
|
|
||||||
|
width: 5rem;
|
||||||
|
|
||||||
|
background-color: lightblue;
|
||||||
|
|
||||||
|
vertical-align: top;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.margin:first-of-type {
|
||||||
|
|
||||||
|
margin-right: 0;
|
||||||
|
|
||||||
|
animation: margin 4s ease 0s infinite;
|
||||||
|
|
||||||
|
}
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
### Padding
|
||||||
|
|
||||||
|
* Accepts 1 to 4 non-negative values
|
||||||
|
* If you use 4 values, the first value is the top margin and the rest follow in a clockwise fashion
|
||||||
|
* Initial value: 0
|
||||||
|
|
||||||
|
Padding is the space between the content and the border of an element. In the demo (#2), it shows a box in which its padding is increasing.
|
||||||
|
|
||||||
|
<pre><code class="language-css">
|
||||||
|
@keyframes padding {
|
||||||
|
|
||||||
|
to { padding: 2rem; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.padding {
|
||||||
|
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
background-color: lightgreen;
|
||||||
|
|
||||||
|
animation: padding 2.5s ease 0s infinite;
|
||||||
|
|
||||||
|
}
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
### Height
|
||||||
|
|
||||||
|
* Accepts a non-negative number, this number is overridden however by (min/max)-height
|
||||||
|
* Initial value: auto
|
||||||
|
|
||||||
|
“Height” is the height of an element without its <span><span> padding, border, or margin. In the demo (#3) you can see the boxes’ height shrink, and each box begins it’s animation at a different time. </span></span>
|
||||||
|
|
||||||
|
<pre><code class="language-css">
|
||||||
|
@keyframes height {
|
||||||
|
|
||||||
|
to { height: .01rem; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.height {
|
||||||
|
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
|
height: 4rem;
|
||||||
|
|
||||||
|
width: 3rem;
|
||||||
|
|
||||||
|
background-color: violet;
|
||||||
|
|
||||||
|
animation: height 1.25s ease 0s infinite;
|
||||||
|
|
||||||
|
vertical-align: top;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.height:nth-of-type(n) {
|
||||||
|
|
||||||
|
animation: height 1.25s ease .2s infinite alternate;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.height:nth-of-type(2n) {
|
||||||
|
|
||||||
|
animation: height 1.25s ease .4s infinite alternate;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.height:nth-of-type(3n) {
|
||||||
|
|
||||||
|
animation: height 1.25s ease .6s infinite alternate;
|
||||||
|
|
||||||
|
}
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
### Width
|
||||||
|
|
||||||
|
* Accepts a non-negative number, this number is overridden however by (min/max)-width
|
||||||
|
* Initial value: auto
|
||||||
|
|
||||||
|
“Width” is the width of an element without its <span><span> padding, border, or margin. In the demo (#4), it is similar to #3, however, it’s the width being affected as opposed to the height. </span></span>
|
||||||
|
|
||||||
|
<pre><code class="language-css">
|
||||||
|
@keyframes width {
|
||||||
|
|
||||||
|
to { width: .01rem; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.width {
|
||||||
|
|
||||||
|
margin-bottom: .2rem;
|
||||||
|
|
||||||
|
height: 3rem;
|
||||||
|
|
||||||
|
width: 6.5rem;
|
||||||
|
|
||||||
|
background-color: bisque;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.width:nth-of-type(n) {
|
||||||
|
|
||||||
|
animation: width 1.25s ease .2s infinite alternate;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.width:nth-of-type(2n) {
|
||||||
|
|
||||||
|
animation: width 1.25s ease .4s infinite alternate;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.width:nth-of-type(3n) {
|
||||||
|
|
||||||
|
animation: width 1.25s ease .6s infinite alternate;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
### Conclusion
|
||||||
|
|
||||||
|
And so with this we can add another collection of animations to our toolbelt! If you’re wondering why I left border out of this box-model post, it’s because I have already written a post dedicated to just the [border animation](https://brandonrozek.com/2015/05/animatable-border/). Here are some of the resources I looked at for this post. Hopefully I’ll come back with another animatable post soon! <https://developer.mozilla.org/en-US/docs/Web/CSS/margin> <https://docs.webplatform.org/wiki/css/properties/margin>{.broken_link} <https://developer.mozilla.org/en-US/docs/Web/CSS/padding> <https://docs.webplatform.org/wiki/css/properties/padding>{.broken_link} <https://developer.mozilla.org/en-US/docs/Web/CSS/height> <https://docs.webplatform.org/wiki/css/properties/height>{.broken_link} <https://developer.mozilla.org/en-US/docs/Web/CSS/width> <https://docs.webplatform.org/wiki/css/properties/width>{.broken_link}
|
231
content/blog/2015-10-03-animatable-location.md
Normal file
231
content/blog/2015-10-03-animatable-location.md
Normal file
|
@ -0,0 +1,231 @@
|
||||||
|
---
|
||||||
|
id: 190
|
||||||
|
title: 'Animatable: Location'
|
||||||
|
date: 2015-10-03T09:34:08+00:00
|
||||||
|
author: Brandon Rozek
|
||||||
|
layout: post
|
||||||
|
guid: https://brandonrozek.com/?p=190
|
||||||
|
permalink: /2015/10/animatable-location/
|
||||||
|
medium_post:
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";N;s:10:"author_url";N;s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";N;s:2:"id";N;s:21:"follower_notification";N;s:7:"license";N;s:14:"publication_id";N;s:6:"status";N;s:3:"url";N;}'
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";s:74:"https://cdn-images-1.medium.com/fit/c/200/200/1*dmbNkD5D-u45r44go_cf0g.png";s:10:"author_url";s:32:"https://medium.com/@brandonrozek";s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";s:2:"no";s:2:"id";s:12:"ad9e6109d5aa";s:21:"follower_notification";s:2:"no";s:7:"license";s:19:"all-rights-reserved";s:14:"publication_id";s:2:"-1";s:6:"status";s:6:"public";s:3:"url";s:65:"https://medium.com/@brandonrozek/animatable-location-ad9e6109d5aa";}'
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";s:74:"https://cdn-images-1.medium.com/fit/c/200/200/1*dmbNkD5D-u45r44go_cf0g.png";s:10:"author_url";s:32:"https://medium.com/@brandonrozek";s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";s:2:"no";s:2:"id";s:12:"ad9e6109d5aa";s:21:"follower_notification";s:2:"no";s:7:"license";s:19:"all-rights-reserved";s:14:"publication_id";s:2:"-1";s:6:"status";s:6:"public";s:3:"url";s:65:"https://medium.com/@brandonrozek/animatable-location-ad9e6109d5aa";}'
|
||||||
|
dsq_thread_id:
|
||||||
|
- "4194325357"
|
||||||
|
- "4194325357"
|
||||||
|
mf2_cite:
|
||||||
|
- 'a:1:{s:6:"author";a:0:{}}'
|
||||||
|
- 'a:1:{s:6:"author";a:0:{}}'
|
||||||
|
tumblr_post_id:
|
||||||
|
- "135657326384"
|
||||||
|
- "135657326384"
|
||||||
|
kind:
|
||||||
|
- article
|
||||||
|
---
|
||||||
|
Animating the location of objects is one of the most common types of animation. It’s interesting to animate this way because you need to keep in mind how each of the element’s interact with each other to constitute a page.
|
||||||
|
|
||||||
|
<!--more-->
|
||||||
|
|
||||||
|
This is part 4 of my series on animation. Check out the other parts of this series!
|
||||||
|
|
||||||
|
* Part 1 — [Animatable: Border](https://brandonrozek.com/2015/05/animatable-border/)
|
||||||
|
* Part 2 — [Animatable: Box Model](https://brandonrozek.com/2015/09/animatable-box-model/)
|
||||||
|
* Part 3 — [Animatable: Text](https://brandonrozek.com/2015/10/animatable-text/)
|
||||||
|
|
||||||
|
This post goes along well with this [Codepen demo](http://codepen.io/brandonrozek/full/NGbWzo/){.broken_link}, I’ll reference it multiple times in this post. Now onto animations!
|
||||||
|
|
||||||
|
### background-position
|
||||||
|
|
||||||
|
* Accepts keywords, percentages, or lengths
|
||||||
|
* Offset-x | Offset-Y
|
||||||
|
* Initial Value: 0% 0%
|
||||||
|
|
||||||
|
Background-position sets where the background is relative to it’s background-origin. In the Codepen demo (#1), you can see the background image scrolling horizontally.
|
||||||
|
|
||||||
|
<pre><code class="language-css">
|
||||||
|
@keyframes background-position {
|
||||||
|
|
||||||
|
to { background-position: 100% 0%; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.background-position {
|
||||||
|
background-image: url(https://upload.wikimedia.org/wikipedia/commons/d/d3/Tajik_mountains_edit.jpg);
|
||||||
|
|
||||||
|
height: 6rem;
|
||||||
|
|
||||||
|
width: 8rem;
|
||||||
|
|
||||||
|
background-size: 200% 100%;
|
||||||
|
|
||||||
|
background-position: 0% 0%;
|
||||||
|
|
||||||
|
animation: background-position 5s linear .1s infinite alternate;
|
||||||
|
|
||||||
|
}
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
### position with left, right, top, bottom
|
||||||
|
|
||||||
|
* Accepts length, percentage, or some keywords
|
||||||
|
* Initial value: auto
|
||||||
|
|
||||||
|
The left, right, top, and bottom properties require the position to be set to something other than static. When you add a value to any of these properties just imagine that value pushing the element on that side. For example: for “left: 2rem” imagine that the element is being pushed on the left side by 2rem, making it move to the right 2rem. In the demo (#2), the box is being pushed in a square path.
|
||||||
|
|
||||||
|
<pre><code class="language-css">
|
||||||
|
@keyframes position {
|
||||||
|
|
||||||
|
25% {
|
||||||
|
|
||||||
|
top: 0;
|
||||||
|
|
||||||
|
left: 3rem;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
|
||||||
|
top: 3rem;
|
||||||
|
|
||||||
|
left: 3rem;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
75% {
|
||||||
|
|
||||||
|
top: 3rem;
|
||||||
|
|
||||||
|
left: 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.position {
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
top: 0;
|
||||||
|
|
||||||
|
left: 0;
|
||||||
|
|
||||||
|
height: 3rem;
|
||||||
|
|
||||||
|
width: 3rem;
|
||||||
|
|
||||||
|
background-color: lightblue;
|
||||||
|
|
||||||
|
animation: position 1.5s ease .1s infinite;
|
||||||
|
|
||||||
|
}
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### vertical-align
|
||||||
|
|
||||||
|
* Accepts keywords, percentages, or lengths (Negative values allowed)
|
||||||
|
* Initial Value: baseline
|
||||||
|
|
||||||
|
Vertical-align sets how vertically the inline element or text is compared to the baseline. In the Codepen demo (#3), the question has its vertical align being manipulated which causes the “Maybe” to bounce up and down.
|
||||||
|
|
||||||
|
<pre><code class="language-css">
|
||||||
|
@keyframes vertical-align {
|
||||||
|
|
||||||
|
to { vertical-align: 1rem; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.vertical-align {
|
||||||
|
|
||||||
|
font-size: 1.5rem;
|
||||||
|
|
||||||
|
vertical-align: 0;
|
||||||
|
|
||||||
|
animation: vertical-align 1s ease-out .1s infinite alternate;
|
||||||
|
|
||||||
|
}
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
### z-index
|
||||||
|
|
||||||
|
* Accepts any integer value
|
||||||
|
* Initial Value: auto
|
||||||
|
|
||||||
|
If elements overlap, z-index determines which element appears on top. If the z-index is the same, then it is controlled by source order. In the demo (#4), The z-index of the biggest box changes, revealing what is under it.
|
||||||
|
|
||||||
|
<pre><code class="language-css">
|
||||||
|
@keyframes z-index {
|
||||||
|
|
||||||
|
to { z-index: 0; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.z-index {
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
|
||||||
|
left: 1rem;
|
||||||
|
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.z-index:nth-child(1) {
|
||||||
|
|
||||||
|
height: 1rem;
|
||||||
|
|
||||||
|
width: 1rem;
|
||||||
|
|
||||||
|
background-color: lightgreen;
|
||||||
|
|
||||||
|
z-index: 4;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.z-index:nth-child(2) {
|
||||||
|
|
||||||
|
height: 2rem;
|
||||||
|
|
||||||
|
width: 2rem;
|
||||||
|
|
||||||
|
background-color: #F4FFA4;
|
||||||
|
|
||||||
|
z-index: 3;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.z-index:nth-child(3) {
|
||||||
|
|
||||||
|
height: 3rem;
|
||||||
|
|
||||||
|
width: 3rem;
|
||||||
|
|
||||||
|
background-color: #DEB0ED;
|
||||||
|
|
||||||
|
z-index: 2;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.z-index:nth-child(4) {
|
||||||
|
|
||||||
|
height: 4rem;
|
||||||
|
|
||||||
|
width: 4rem;
|
||||||
|
|
||||||
|
background-color: #D8EADF;
|
||||||
|
|
||||||
|
z-index: 5;
|
||||||
|
|
||||||
|
animation: z-index 1s ease .1s infinite alternate;
|
||||||
|
|
||||||
|
}
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
### Conclusion
|
||||||
|
|
||||||
|
By animating the location of an element, it opens up a whole bunch of different opportunities. Using motion, you can signify that the user has indeed pressed the button, instead of what would otherwise leave them there clicking on the button multiple times thinking that nothing has happened. Using motion, you can bring a webpage that would’ve otherwise been boring to life. Use these animations to work, and I’ll be back with another animatable post soon!
|
||||||
|
|
||||||
|
#### The links
|
||||||
|
|
||||||
|
<https://developer.mozilla.org/en-US/docs/Web/CSS/background-position> <https://developer.mozilla.org/en-US/docs/Web/CSS/left> <https://developer.mozilla.org/en-US/docs/Web/CSS/vertical-align> <https://developer.mozilla.org/en-US/docs/Web/CSS/z-index> <https://docs.webplatform.org/wiki/css/properties/background-position>{.broken_link} <https://docs.webplatform.org/wiki/css/properties/left>{.broken_link} <https://docs.webplatform.org/wiki/css/properties/vertical-align>{.broken_link} <https://docs.webplatform.org/wiki/css/properties/z-index>{.broken_link}
|
208
content/blog/2015-10-03-animatable-text.md
Normal file
208
content/blog/2015-10-03-animatable-text.md
Normal file
|
@ -0,0 +1,208 @@
|
||||||
|
---
|
||||||
|
id: 148
|
||||||
|
title: 'Animatable: Text'
|
||||||
|
date: 2015-10-03T08:44:51+00:00
|
||||||
|
author: Brandon Rozek
|
||||||
|
layout: post
|
||||||
|
guid: https://brandonrozek.com/?p=148
|
||||||
|
permalink: /2015/10/animatable-text/
|
||||||
|
medium_post:
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";N;s:10:"author_url";N;s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";N;s:2:"id";N;s:21:"follower_notification";N;s:7:"license";N;s:14:"publication_id";N;s:6:"status";N;s:3:"url";N;}'
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";s:74:"https://cdn-images-1.medium.com/fit/c/200/200/1*dmbNkD5D-u45r44go_cf0g.png";s:10:"author_url";s:32:"https://medium.com/@brandonrozek";s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";s:2:"no";s:2:"id";s:12:"8ef239deb1ea";s:21:"follower_notification";s:2:"no";s:7:"license";s:19:"all-rights-reserved";s:14:"publication_id";s:2:"-1";s:6:"status";s:6:"public";s:3:"url";s:61:"https://medium.com/@brandonrozek/animatable-text-8ef239deb1ea";}'
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";s:74:"https://cdn-images-1.medium.com/fit/c/200/200/1*dmbNkD5D-u45r44go_cf0g.png";s:10:"author_url";s:32:"https://medium.com/@brandonrozek";s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";s:2:"no";s:2:"id";s:12:"8ef239deb1ea";s:21:"follower_notification";s:2:"no";s:7:"license";s:19:"all-rights-reserved";s:14:"publication_id";s:2:"-1";s:6:"status";s:6:"public";s:3:"url";s:61:"https://medium.com/@brandonrozek/animatable-text-8ef239deb1ea";}'
|
||||||
|
mf2_cite:
|
||||||
|
- 'a:1:{s:6:"author";a:0:{}}'
|
||||||
|
- 'a:1:{s:6:"author";a:0:{}}'
|
||||||
|
tumblr_post_id:
|
||||||
|
- "135657083469"
|
||||||
|
- "135657083469"
|
||||||
|
kind:
|
||||||
|
- article
|
||||||
|
---
|
||||||
|
This post is part 3 of my series on animation. In this post, I’ll show you different animations you can add onto text. If you haven’t already, you should check out [part 1](https://brandonrozek.com/2015/09/animatable-box-model/) and [part 2](https://brandonrozek.com/2015/05/animatable-border/) of this series. Animations on text can be used to bring attention, to add importance, or to convey a point. As with all animations, however, keep your user in mind and your text readable.
|
||||||
|
|
||||||
|
<!--more-->
|
||||||
|
|
||||||
|
This post follows along with a [Codepen demo](http://codepen.io/brandonrozek/full/dYGwbE/){.broken_link} I made.
|
||||||
|
|
||||||
|
### line-height
|
||||||
|
|
||||||
|
* Accepts certain keywords, or any positive number or length
|
||||||
|
* Initial value: normal
|
||||||
|
|
||||||
|
Line-height is the space between each line in a text block. It is commonly recommended that you use a unitless line-height because then it takes the font-size into consideration. When you use an unitless value, the browser determines the line-height by taking the unitless value and multiplying it by the element’s font-size. In the Codepen demo (#1), you can see the line-height decreasing while the opacity increases.
|
||||||
|
|
||||||
|
<pre><code class="language-css">
|
||||||
|
@keyframes line-height {
|
||||||
|
|
||||||
|
to {
|
||||||
|
|
||||||
|
opacity: 1;
|
||||||
|
|
||||||
|
line-height: 1.2;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.line-height {
|
||||||
|
|
||||||
|
opacity: 0;
|
||||||
|
|
||||||
|
line-height: 2.5;
|
||||||
|
|
||||||
|
animation: line-height .75s ease .2s infinite;
|
||||||
|
|
||||||
|
}
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
### font-weight
|
||||||
|
|
||||||
|
* Accepts certain keywords or 100, 200, 300, 400, 500, 600, 700, 800, 900 (the higher the number, the darker the font-weight)
|
||||||
|
* Initial value: normal
|
||||||
|
|
||||||
|
Font-weight specifies the boldness of the text. If the typeface doesn’t come with multiple weights, then the animation would only happen between the weights that it does have. In the demo (#2), the text will go from normal weight to bold.
|
||||||
|
|
||||||
|
<pre><code class="language-css">
|
||||||
|
@keyframes font-weight {
|
||||||
|
|
||||||
|
to { font-weight: 900;}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.font-weight {
|
||||||
|
|
||||||
|
font-weight: 100;
|
||||||
|
|
||||||
|
animation: font-weight 2s linear .2s infinite alternate;
|
||||||
|
|
||||||
|
}
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
### font-size
|
||||||
|
|
||||||
|
* Accepts any length
|
||||||
|
* Initial value: medium
|
||||||
|
|
||||||
|
It is important to note that changing the font-size could change the value of other text properties that are dependent upon it. (Like unitless line-heights) In the demo (#3), you can see the text’s font-size shrinking.
|
||||||
|
|
||||||
|
<pre><code class="language-css">
|
||||||
|
@keyframes font-size {
|
||||||
|
|
||||||
|
to { font-size: .1rem;}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.font-size {
|
||||||
|
|
||||||
|
font-size: 2rem;
|
||||||
|
|
||||||
|
animation: font-size 2s ease-out .1s infinite;
|
||||||
|
|
||||||
|
}
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
### text-shadow
|
||||||
|
|
||||||
|
* Accepts a color and 3 lengths
|
||||||
|
* Color | Offset-X | Offset-Y | Blur-radius
|
||||||
|
* Initial value: none
|
||||||
|
|
||||||
|
Text-shadow applies a shadow to both the text and it’s text-decoration. Multiple shadows can be added, and they are applied from front to back. In the animation (#4), you can see the text’s shadow move.
|
||||||
|
|
||||||
|
<pre><code class="language-css">
|
||||||
|
@keyframes text-shadow {
|
||||||
|
|
||||||
|
to { text-shadow: 25px 10px 5px rgba(0, 0, 0, .9);}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-shadow {
|
||||||
|
|
||||||
|
font-size: 1.5rem;
|
||||||
|
|
||||||
|
text-shadow: -10px 5px 3.5px rgba(0, 0, 0, .3);
|
||||||
|
|
||||||
|
animation: text-shadow 1s ease 0s infinite;
|
||||||
|
|
||||||
|
}
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
### text-decoration-color
|
||||||
|
|
||||||
|
* Accepts a color value
|
||||||
|
* Initial value: currentColor
|
||||||
|
|
||||||
|
This sets the color for [text-decoration-line](https://developer.mozilla.org/en-US/docs/Web/CSS/text-decoration-line) (underlines, overlines, or strike-throughs) In the demo (#5), the strike-through changes from red to black.
|
||||||
|
|
||||||
|
<pre><code class="language-css">
|
||||||
|
@keyframes text-decoration-color {
|
||||||
|
|
||||||
|
to { text-decoration-color: black;}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-decoration-color {
|
||||||
|
|
||||||
|
text-decoration-color: red;
|
||||||
|
|
||||||
|
text-decoration-line: line-through;
|
||||||
|
|
||||||
|
animation: text-decoration-color 2s linear 0s infinite alternate;
|
||||||
|
|
||||||
|
}
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
### word-spacing
|
||||||
|
|
||||||
|
* Accepts keywords or positive/negative length
|
||||||
|
* Initial value: normal
|
||||||
|
|
||||||
|
Word-spacing defines the space between tags and words. Negative values bring the words closer to each other. In the demo (#6), you can see the word-spacing increase with ‘good bye!’ where the word ‘bye!’ is moving away.
|
||||||
|
|
||||||
|
<pre><code class="language-css">
|
||||||
|
@keyframes word-spacing {
|
||||||
|
|
||||||
|
to { word-spacing: 5rem;}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.word-spacing {
|
||||||
|
|
||||||
|
word-spacing: normal;
|
||||||
|
|
||||||
|
animation: word-spacing 1s ease-in 0s infinite;
|
||||||
|
|
||||||
|
}
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
### letter-spacing
|
||||||
|
|
||||||
|
* Accepts keywords or positive/negative length
|
||||||
|
* Initial value: normal
|
||||||
|
|
||||||
|
Letter-spacing specifies the spacing between text characters. Negative values bring the letters closer together. In the demo (#7), each letter gets separated from one another.
|
||||||
|
|
||||||
|
<pre><code class="language-css">
|
||||||
|
@keyframes letter-spacing {
|
||||||
|
|
||||||
|
to { letter-spacing: 2rem;}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.letter-spacing {
|
||||||
|
|
||||||
|
letter-spacing: 0;
|
||||||
|
|
||||||
|
animation: letter-spacing .75s ease 0s infinite alternate;
|
||||||
|
|
||||||
|
}
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
### Conclusion
|
||||||
|
|
||||||
|
These animations show the different things you can do with text. Perhaps you’ll add a small animation to a heading to bring depth and attention, or you’ll add some to the text of a button to scream “call to action”. Whatever you decide, I hope this post helped. I’ll see you again next time with another animatable post! 🙂
|
||||||
|
|
||||||
|
#### The links
|
||||||
|
|
||||||
|
[https://docs.webplatform.org/wiki/css/properties/line-height](https://developer.mozilla.org/en-US/docs/Web/CSS/line-height) <https://developer.mozilla.org/en-US/docs/Web/CSS/line-height> <https://docs.webplatform.org/wiki/css/properties/font-weight>{.broken_link} <https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight> <https://docs.webplatform.org/wiki/css/properties/font-size>{.broken_link} <https://developer.mozilla.org/en-US/docs/Web/CSS/font-size> <https://docs.webplatform.org/wiki/css/properties/text-shadow>{.broken_link} <https://developer.mozilla.org/en-US/docs/Web/CSS/text-shadow> <https://docs.webplatform.org/wiki/css/properties/text-decoration-color>{.broken_link} <https://developer.mozilla.org/en-US/docs/Web/CSS/text-decoration-color> <https://docs.webplatform.org/wiki/css/properties/word-spacing>{.broken_link} <https://developer.mozilla.org/en-US/docs/Web/CSS/word-spacing> <https://docs.webplatform.org/wiki/css/properties/letter-spacing>{.broken_link} <https://developer.mozilla.org/en-US/docs/Web/CSS/letter-spacing>
|
129
content/blog/2015-10-04-html-css-javascript-link-together.md
Normal file
129
content/blog/2015-10-04-html-css-javascript-link-together.md
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
---
|
||||||
|
id: 210
|
||||||
|
title: HTML, CSS, Javascript, and how they all link together
|
||||||
|
date: 2015-10-04T17:50:50+00:00
|
||||||
|
author: Brandon Rozek
|
||||||
|
layout: post
|
||||||
|
guid: https://brandonrozek.com/?p=210
|
||||||
|
permalink: /2015/10/html-css-javascript-link-together/
|
||||||
|
medium_post:
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";N;s:10:"author_url";N;s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";N;s:2:"id";N;s:21:"follower_notification";N;s:7:"license";N;s:14:"publication_id";N;s:6:"status";N;s:3:"url";N;}'
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";s:74:"https://cdn-images-1.medium.com/fit/c/200/200/1*dmbNkD5D-u45r44go_cf0g.png";s:10:"author_url";s:32:"https://medium.com/@brandonrozek";s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";s:2:"no";s:2:"id";s:12:"9579f30ae529";s:21:"follower_notification";s:3:"yes";s:7:"license";s:19:"all-rights-reserved";s:14:"publication_id";s:2:"-1";s:6:"status";s:6:"public";s:3:"url";s:96:"https://medium.com/@brandonrozek/html-css-javascript-and-how-they-all-link-together-9579f30ae529";}'
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";s:74:"https://cdn-images-1.medium.com/fit/c/200/200/1*dmbNkD5D-u45r44go_cf0g.png";s:10:"author_url";s:32:"https://medium.com/@brandonrozek";s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";s:2:"no";s:2:"id";s:12:"9579f30ae529";s:21:"follower_notification";s:3:"yes";s:7:"license";s:19:"all-rights-reserved";s:14:"publication_id";s:2:"-1";s:6:"status";s:6:"public";s:3:"url";s:96:"https://medium.com/@brandonrozek/html-css-javascript-and-how-they-all-link-together-9579f30ae529";}'
|
||||||
|
dsq_thread_id:
|
||||||
|
- "4194222476"
|
||||||
|
- "4194222476"
|
||||||
|
mf2_cite:
|
||||||
|
- 'a:1:{s:6:"author";a:0:{}}'
|
||||||
|
- 'a:1:{s:6:"author";a:0:{}}'
|
||||||
|
tumblr_post_id:
|
||||||
|
- "135657370214"
|
||||||
|
- "135657370214"
|
||||||
|
kind:
|
||||||
|
- article
|
||||||
|
---
|
||||||
|
I’ve been teaching a small class on web development recently, and after my first lecture, I’ve gained a newfound respect for teachers. Teaching didn’t come as naturally to me as I would have imagined. I tried going in prepared: with a few outlines and a few code demos. Instead of letting my preparation go to waste, I decided to share them here with you on my site. It’s a nice break from the Animatable posts, so I hope you enjoy!
|
||||||
|
|
||||||
|
<!--more-->
|
||||||
|
|
||||||
|
I suggest a couple short readings to check out before the lecture:
|
||||||
|
|
||||||
|
* [What is CSS](http://www.sitepoint.com/web-foundations/css/) by Adam Roberts
|
||||||
|
* [Box Model](http://www.w3.org/TR/CSS2/box.html) by the W3C
|
||||||
|
|
||||||
|
Take a quick look if you wish. These are mostly to give an idea of what is going to happen over the next few lectures. What is HTML, CSS, and Javascript? We’ll look into the different parts that form a webpage and how they all interact with each other.
|
||||||
|
|
||||||
|
## HTML
|
||||||
|
|
||||||
|
HTML is where the content of your site lives. It’s also the file the server returns to no matter what, the bare-bones of a webpage. This file may contain text, pictures, and/or other media.
|
||||||
|
|
||||||
|
## CSS
|
||||||
|
|
||||||
|
This is where you style your content. Whether it’s through colors, layout, or typography, there are plenty of different ways for you to visually manipulate your content.
|
||||||
|
|
||||||
|
## Javascript
|
||||||
|
|
||||||
|
Javascript is where you add functionality of your site. Validating user input or having dynamic content are one of the many things you can do with Javascipt.
|
||||||
|
|
||||||
|
## How do they all link together?
|
||||||
|
|
||||||
|
HTML, CSS, and Javascript each do what they do the best. So how can you have them all play nicely on the same ball field?
|
||||||
|
|
||||||
|
### In HTML
|
||||||
|
|
||||||
|
Link the CSS file
|
||||||
|
|
||||||
|
<pre><code class="language-markup">
|
||||||
|
<link rel='stylesheet' href='style.css' />
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
Link the Javascript file
|
||||||
|
|
||||||
|
<pre><code class="language-markup">
|
||||||
|
<script src='script.js'></script>
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
Give the <p> tag a class of hello and id of world to use in CSS and Javascript
|
||||||
|
|
||||||
|
<pre><code class="language-markup">
|
||||||
|
<p class='hello' id='world'></p>
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
### In CSS
|
||||||
|
|
||||||
|
Refer to any element with class=’hello’ and change it’s text color to red.
|
||||||
|
|
||||||
|
<pre><code class="language-css">
|
||||||
|
.hello { color: red; }
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
Any element with an id=’world’ will have it’s font-size changed to 2rem
|
||||||
|
|
||||||
|
<pre><code class="language-css">
|
||||||
|
#world { font-size: 2rem; }
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
You don’t have to only use the class and id attributes in html, you can refer to any attribute. This code snippet grabs any canvas element with data=’sales’ and changes it’s border to be 5px thick, dashed, and the color blue.
|
||||||
|
|
||||||
|
<pre><code class="language-css">
|
||||||
|
canvas[data=sales] { border: 5px dashed blue; }
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
### In Javascript
|
||||||
|
|
||||||
|
Javascript has many properties and methods you can use to reference different HTML elements To grab (an) element(s)
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">
|
||||||
|
document.getElementById();
|
||||||
|
document.getElementsByClassName(); //Returns an array of element(s)
|
||||||
|
document.querySelector(); //Returns the first matching selector
|
||||||
|
document.querySelectorAll(); //Returns an array of element(s)
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
To add a class to an element
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">
|
||||||
|
element.className += “ random-class” //Note the space
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
To check whether a certain condition is true in the browser
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">
|
||||||
|
window.matchMedia('aspect-ratio: 12/8'); //Returns true if the aspect ratio is 12/8
|
||||||
|
window.innerHeight; //Is the height of the window
|
||||||
|
window.innerWidth; //Is the width of the window
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
_And a lot more_
|
||||||
|
|
||||||
|
## What should handle what?
|
||||||
|
|
||||||
|
HTML, CSS, and Javascript should all handle what each of them does best. HTML should handle the content, CSS should handle the styles/presentation, and Javascript should handle the behavior of the webpage. Why, you ask? One reason is that it demonstrates a “separation of concerns”. It would be a mess if you’re writing an article in the HTML and you put style attributes all over the place. That would make the markup confusing and hard to read/edit.
|
||||||
|
|
||||||
|
## Can’t I just put everything in a Javascript file?
|
||||||
|
|
||||||
|
Yes you can, but then you lose some of the great features of HTML and CSS. HTML and CSS ignore everything that it doesn’t understand. When Javascript encounters something it doesn’t understand, it stops running the code completely, regardless of what comes after. Please note that I am only speaking about what gets served to the user. When it comes to what’s actually on your server, then store your content however you wish.
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
To me personally, my first lecture was a mess. Some of my examples didn’t work properly in the first type-through and I was everywhere. Though, to be sharing the little knowledge I do have, comes with it’s own joy. It must not have gone as terrible as I imagined it in my head because they seemed enthusiastic for the next lecture. So I’m excited to share this outline with you, and hopefully I’ll have many more to come. Until next time. 🙂
|
311
content/blog/2015-10-10-javascript-data-types.md
Normal file
311
content/blog/2015-10-10-javascript-data-types.md
Normal file
|
@ -0,0 +1,311 @@
|
||||||
|
---
|
||||||
|
id: 238
|
||||||
|
title: Javascript Data Types
|
||||||
|
date: 2015-10-10T20:01:20+00:00
|
||||||
|
author: Brandon Rozek
|
||||||
|
layout: post
|
||||||
|
guid: https://brandonrozek.com/?p=238
|
||||||
|
permalink: /2015/10/javascript-data-types/
|
||||||
|
medium_post:
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";N;s:10:"author_url";N;s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";N;s:2:"id";N;s:21:"follower_notification";N;s:7:"license";N;s:14:"publication_id";N;s:6:"status";N;s:3:"url";N;}'
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";s:74:"https://cdn-images-1.medium.com/fit/c/200/200/1*dmbNkD5D-u45r44go_cf0g.png";s:10:"author_url";s:32:"https://medium.com/@brandonrozek";s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";s:2:"no";s:2:"id";s:12:"edcd4e2dcf42";s:21:"follower_notification";s:2:"no";s:7:"license";s:19:"all-rights-reserved";s:14:"publication_id";s:2:"-1";s:6:"status";s:6:"public";s:3:"url";s:67:"https://medium.com/@brandonrozek/javascript-data-types-edcd4e2dcf42";}'
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";s:74:"https://cdn-images-1.medium.com/fit/c/200/200/1*dmbNkD5D-u45r44go_cf0g.png";s:10:"author_url";s:32:"https://medium.com/@brandonrozek";s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";s:2:"no";s:2:"id";s:12:"edcd4e2dcf42";s:21:"follower_notification";s:2:"no";s:7:"license";s:19:"all-rights-reserved";s:14:"publication_id";s:2:"-1";s:6:"status";s:6:"public";s:3:"url";s:67:"https://medium.com/@brandonrozek/javascript-data-types-edcd4e2dcf42";}'
|
||||||
|
dsq_thread_id:
|
||||||
|
- "4215821079"
|
||||||
|
- "4215821079"
|
||||||
|
mf2_cite:
|
||||||
|
- 'a:1:{s:6:"author";a:0:{}}'
|
||||||
|
- 'a:1:{s:6:"author";a:0:{}}'
|
||||||
|
tumblr_post_id:
|
||||||
|
- "135657462089"
|
||||||
|
- "135657462089"
|
||||||
|
kind:
|
||||||
|
- article
|
||||||
|
---
|
||||||
|
Javascript has multiple ways you can store your data. Each of these different ways is called a data type, and they each carry different “methods” which are helpful commands. Today, I’ll show you the different data types and methods that I use and how they’re useful.
|
||||||
|
|
||||||
|
<!--more-->
|
||||||
|
|
||||||
|
This post is by far not a comprehenive list of all the Data types and their methods. If you want one of those, please check out [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects) and/or [WebPlatform](http://docs.webplatform.org/wiki/javascript/objects){.broken_link}. This is the second lecture of the web development class I’m teaching for the newer folks over at [Math I/O](http://math-io.com). Due to the nature of Math I/O (making math games and all), the next few posts will be Javascript centric. We’re getting ready to build a new game, so I want to prepare them as much as possible. \*Excited\* ^_^ Ilya Kantor does a good job of descibing Javascript data types and their many methods in [Mastering Data Types](http://javascript.info/tutorial/mastering-data-types) which I made the recommended reading for this lecture.
|
||||||
|
|
||||||
|
### <a href="#string" name="string"></a>String {#string}
|
||||||
|
|
||||||
|
A string is one or more characters.
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">var name = "Brandon";</code></pre>
|
||||||
|
|
||||||
|
You can access a character inside of a string by using [] notation. Inside the [] you put the index of the character you want. An index is the numeral location of the character starting from the left. It is important to note that Javascript starts counting from 0.
|
||||||
|
|
||||||
|
<table border="1">
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
B
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
r
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
a
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
n
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
d
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
o
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
n
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
1
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
2
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
3
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
4
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
5
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
6
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">var firstInitial = "Brandon"[0];</code></pre>
|
||||||
|
|
||||||
|
Now the value of firstInitial is the letter <code class="language-javascript">"B"</code>.
|
||||||
|
|
||||||
|
#### <a href="#some-useful-methods-for-strings" name="some-useful-methods-for-strings"></a>Some useful methods for strings {#some-useful-methods-for-strings}
|
||||||
|
|
||||||
|
##### <a href="#string.prototype.indexof();" name="string.prototype.indexof();"></a>String.prototype.indexOf(); {#string.prototype.indexof();}
|
||||||
|
|
||||||
|
This can be used to find the index of any character(s) in a string. I primarily use it for when I need to check if something exists in a string. Do I have a ‘z’ in my name?
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">"Brandon".indexOf('z');</code></pre>
|
||||||
|
|
||||||
|
Nope, so Javascript will return a <code class="language-javascript">-1</code>. How about a ‘d’?
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">"Brandon".indexOf('d');</code></pre>
|
||||||
|
|
||||||
|
Yes, Javascript will return <code class="language-javascript">5</code> which is the index of the letter ‘d’.
|
||||||
|
|
||||||
|
##### <a href="#string.prototype.replace();" name="string.prototype.replace();"></a>String.prototype.replace(); {#string.prototype.replace();}
|
||||||
|
|
||||||
|
The replace method can replace any character(s) with other character(s). For more complex replacing, look into [Regular Expression](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions) and how you can use them in .replace(). Replace the first ‘n’ in my name with an ‘m’.
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">
|
||||||
|
var modifiedName = "Brandon".replace('n', 'm');
|
||||||
|
|
||||||
|
console.log(modifiedName);
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
Logs <code class="language-javascript">"Bramdon"</code>.
|
||||||
|
|
||||||
|
##### <a href="#string.prototype.touppercase();" name="string.prototype.touppercase();"></a>String.prototype.toUpperCase(); {#string.prototype.touppercase();}
|
||||||
|
|
||||||
|
This method returns the string with all the lowercase characters converted to uppercase and can be useful for when you’re checking user input and you don’t want to worry about different cases.
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">"Brandon".toUpperCase();</code></pre>
|
||||||
|
|
||||||
|
Returns <code class="language-javascript">"BRANDON"</code>.
|
||||||
|
|
||||||
|
##### <a href="#string.prototype.tolowercase();" name="string.prototype.tolowercase();"></a>String.prototype.toLowerCase(); {#string.prototype.tolowercase();}
|
||||||
|
|
||||||
|
Same as above but instead of converting lowercase to uppercase, it converts uppercase to lowercase.
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">"Brandon".toLowerCase();</code></pre>
|
||||||
|
|
||||||
|
Returns <code class="language-javascript">"brandon"</code>.
|
||||||
|
|
||||||
|
#### <a href="#a-couple-useful-escape-secquences" name="a-couple-useful-escape-secquences"></a>A couple useful escape secquences {#a-couple-useful-escape-secquences}
|
||||||
|
|
||||||
|
* <code class="language-javascript">n</code> for newline.
|
||||||
|
* <code class="language-javascript">t</code> for tab character.
|
||||||
|
|
||||||
|
You can also use escape sequnces if you want to add “” or ‘’ to your strings.
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">
|
||||||
|
var greeting = "Hello "Brandon"";
|
||||||
|
|
||||||
|
console.log(greeting);
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
Returns <code class="language-javascript">"Hello "Brandon""</code>.
|
||||||
|
|
||||||
|
### <a href="#number" name="number"></a>Number {#number}
|
||||||
|
|
||||||
|
Any number between -(2<sup>53</sup> – 1) and (2<sup>53</sup> – 1).
|
||||||
|
|
||||||
|
#### <a href="#number-methods" name="number-methods"></a>Number Methods {#number-methods}
|
||||||
|
|
||||||
|
Number methods are useful when trying to represent complex numbers.
|
||||||
|
|
||||||
|
##### <a href="#number.prototype.toexponential();" name="number.prototype.toexponential();"></a>Number.prototype.toExponential(); {#number.prototype.toexponential();}
|
||||||
|
|
||||||
|
Returns a string representing a number in exponential notation.
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">77.1234.toExponential(2);</code></pre>
|
||||||
|
|
||||||
|
Returns <code class="language-javascript">"7.71e+1"</code>.
|
||||||
|
|
||||||
|
##### <a href="#number.prototype.tofixed();" name="number.prototype.tofixed();"></a>Number.prototype.toFixed(); {#number.prototype.tofixed();}
|
||||||
|
|
||||||
|
Returns a string representing a number fixed to x amount of decimal places.
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">12345.6789.toFixed(1);</code></pre>
|
||||||
|
|
||||||
|
Returns <code class="language-javascript">"12345.7"</code>.
|
||||||
|
|
||||||
|
##### <a href="#number.prototype.toprecision();" name="number.prototype.toprecision();"></a>Number.prototype.toPrecision(); {#number.prototype.toprecision();}
|
||||||
|
|
||||||
|
Returns a string representing a number using x amount of significant figures.
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">5.123456.toPrecision(2);</code></pre>
|
||||||
|
|
||||||
|
Returns <code class="language-javascript">"5.1"</code>.
|
||||||
|
|
||||||
|
#### <a href="#math-properties/methods" name="math-properties/methods"></a>Math properties/methods {#math-properties/methods}
|
||||||
|
|
||||||
|
In Javascript there is a Math object which contains many properties and methods which are useful for mathmatical calculations.
|
||||||
|
|
||||||
|
##### <a href="#return-euler's-constant" name="return-euler's-constant"></a>Return Euler’s constant {#return-euler's-constant}
|
||||||
|
|
||||||
|
<code class="language-javascript">Math.E</code> which returns ~2.718.
|
||||||
|
|
||||||
|
##### <a href="#return-the-natural-log-of-x" name="return-the-natural-log-of-x"></a>Return the natural log of x {#return-the-natural-log-of-x}
|
||||||
|
|
||||||
|
<code class="language-javascript">Math.log(x)</code>
|
||||||
|
|
||||||
|
##### <a href="#rise-x-to-the-y-power" name="rise-x-to-the-y-power"></a>Rise x to the y power {#rise-x-to-the-y-power}
|
||||||
|
|
||||||
|
<code class="language-javascript">Math.pow(x,y)</code>
|
||||||
|
|
||||||
|
##### <a href="#return-a-psuedo-random-number-[0,1)" name="return-a-psuedo-random-number-[0,1)"></a>Return a psuedo random number [0,1) {#return-a-psuedo-random-number-[0,1)}
|
||||||
|
|
||||||
|
<code class="language-javascript">Math.random()</code>
|
||||||
|
|
||||||
|
##### <a href="#round-x-to-the-nearest-integer" name="round-x-to-the-nearest-integer"></a>Round x to the nearest integer {#round-x-to-the-nearest-integer}
|
||||||
|
|
||||||
|
<code class="language-javascript">Math.round(x)</code>
|
||||||
|
|
||||||
|
### <a href="#boolean" name="boolean"></a>Boolean {#boolean}
|
||||||
|
|
||||||
|
Booleans are either <code class="language-javascript">true</code> or <code class="language-javascript">false and</code> are typically used in conditional statements. You can either create them explicitly
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">var alive = true;</code></pre>
|
||||||
|
|
||||||
|
or by evaluating a [comparison](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators).
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">
|
||||||
|
var dead = false;
|
||||||
|
var isAlive = !dead;
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
isAlive equals <code class="language-javascript">true</code>.
|
||||||
|
|
||||||
|
### <a href="#array" name="array"></a>Array {#array}
|
||||||
|
|
||||||
|
An array is a list of items. In Javascript these items can be any data type, even arrays themselves.
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">var mixedBag = ['sword', 24, true, [Math.PI, Math.E], 'shield'];</code></pre>
|
||||||
|
|
||||||
|
To access an item in an array use [] notation with an index as mentioned over at strings.
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">['sword', 'shield', 'helmet'][1];</code></pre>
|
||||||
|
|
||||||
|
Returns <code class="language-javascript">'shield'</code>. to figure out how many items are in the array.
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">
|
||||||
|
var inventory = ['boots', 'gloves', 'pants', 'shirt'];
|
||||||
|
var inventoryAmt = inventory.length;
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
inventoryAmt is <code class="language-javascript">4</code> since there are 4 items in inventory.
|
||||||
|
|
||||||
|
#### <a href="#array-methods" name="array-methods"></a>Array Methods {#array-methods}
|
||||||
|
|
||||||
|
##### <a href="#array.prototype.push();" name="array.prototype.push();"></a>Array.prototype.push(); {#array.prototype.push();}
|
||||||
|
|
||||||
|
Adds whatever is inside the parenthesis to the end of the array. Great for adding items to a list. For example, test scores.
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">[100,92,95].push(80);</code></pre>
|
||||||
|
|
||||||
|
Returns <code class="language-javascript">[100,92,95,80]</code>.
|
||||||
|
|
||||||
|
##### <a href="#array.prototype.reverse();" name="array.prototype.reverse();"></a>Array.prototype.reverse(); {#array.prototype.reverse();}
|
||||||
|
|
||||||
|
Reverses the order of all the items in the array.
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">[1,2,3].reverse();</code></pre>
|
||||||
|
|
||||||
|
Returns <code class="language-javascript">[3,2,1]</code>.
|
||||||
|
|
||||||
|
##### <a href="#array.prototype.concat();" name="array.prototype.concat();"></a>Array.prototype.concat(); {#array.prototype.concat();}
|
||||||
|
|
||||||
|
Combines two arrays, putting the items from the array in the parenthesis to the end of the main array. This method is a lot faster than grabbing each item by their index and adding them using the .push() method.
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">['a','b','c'].concat([1,2,3]);</code></pre>
|
||||||
|
|
||||||
|
Returns <code class="language-javascript">['a','b','c',1,2,3]</code>.
|
||||||
|
|
||||||
|
##### <a href="#array.prototype.join();" name="array.prototype.join();"></a>Array.prototype.join(); {#array.prototype.join();}
|
||||||
|
|
||||||
|
Converts the array into a string, with each item seperated by whatever is in the parenthesis. Useful for telling the user the items in their inventory, for example.
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">
|
||||||
|
var inventory = ['books','apples','pencils'];
|
||||||
|
console.log("You have " + inventory.join(", ") + " in your inventory.");
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
Logs <code class="language-javascript">"You have books, apples, pencils in your inventory."</code>
|
||||||
|
|
||||||
|
##### <a href="#array.prototype.indexof();" name="array.prototype.indexof();"></a>Array.prototype.indexOf(); {#array.prototype.indexof();}
|
||||||
|
|
||||||
|
Similar to String.prototype.indexOf(), it returns the index of the item inside the parenthesis.
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">['chicken','pizza','tacos'].indexOf('tacos');</code></pre>
|
||||||
|
|
||||||
|
Returns <code class="language-javascript">2</code>.
|
||||||
|
|
||||||
|
### <a href="#objects" name="objects"></a>Objects {#objects}
|
||||||
|
|
||||||
|
Objects are like arrays, however they’re easier for establishing the relationship between properties and their values. You can store any data type as a property of an object.
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">
|
||||||
|
var player = {};
|
||||||
|
player.name = "Brandon";
|
||||||
|
player.health = Number.POSITIVE_INFINITY;
|
||||||
|
console.log(player.name + " has " + player.health + " health.");
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
Logs <code class="language-javascript">"Brandon has Infinity health"</code> Yup that sounds about right.
|
||||||
|
|
||||||
|
### <a href="#conclusion" name="conclusion"></a>Conclusion {#conclusion}
|
||||||
|
|
||||||
|
All of the different data types in Javascript are tools for you to get the job done. When assigning a variable, think to yourself which tool you should use. I had fun doing this lecture. We finished earlier than expected, due to my extra preparations. (Still shuddering over my unpreparedness from last week). We had finished so early in fact, that I went ahead and started teaching next week’s material. Do not worry though, my lovely reader’s only get the most structured of lesson materials. So you’ll have to wait until next week to hear more. 🙂
|
119
content/blog/2015-10-11-animatable-visual.md
Normal file
119
content/blog/2015-10-11-animatable-visual.md
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
---
|
||||||
|
id: 297
|
||||||
|
title: 'Animatable: Visual'
|
||||||
|
date: 2015-10-11T16:52:36+00:00
|
||||||
|
author: Brandon Rozek
|
||||||
|
layout: post
|
||||||
|
guid: https://brandonrozek.com/?p=297
|
||||||
|
permalink: /2015/10/animatable-visual/
|
||||||
|
medium_post:
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";N;s:10:"author_url";N;s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";N;s:2:"id";N;s:21:"follower_notification";N;s:7:"license";N;s:14:"publication_id";N;s:6:"status";N;s:3:"url";N;}'
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";s:74:"https://cdn-images-1.medium.com/fit/c/200/200/1*dmbNkD5D-u45r44go_cf0g.png";s:10:"author_url";s:32:"https://medium.com/@brandonrozek";s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";s:2:"no";s:2:"id";s:12:"c1119f67e27a";s:21:"follower_notification";s:2:"no";s:7:"license";s:19:"all-rights-reserved";s:14:"publication_id";s:2:"-1";s:6:"status";s:6:"public";s:3:"url";s:63:"https://medium.com/@brandonrozek/animatable-visual-c1119f67e27a";}'
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";s:74:"https://cdn-images-1.medium.com/fit/c/200/200/1*dmbNkD5D-u45r44go_cf0g.png";s:10:"author_url";s:32:"https://medium.com/@brandonrozek";s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";s:2:"no";s:2:"id";s:12:"c1119f67e27a";s:21:"follower_notification";s:2:"no";s:7:"license";s:19:"all-rights-reserved";s:14:"publication_id";s:2:"-1";s:6:"status";s:6:"public";s:3:"url";s:63:"https://medium.com/@brandonrozek/animatable-visual-c1119f67e27a";}'
|
||||||
|
tumblr_post_id:
|
||||||
|
- "135657496179"
|
||||||
|
- "135657496179"
|
||||||
|
mf2_cite:
|
||||||
|
- 'a:1:{s:6:"author";a:0:{}}'
|
||||||
|
- 'a:1:{s:6:"author";a:0:{}}'
|
||||||
|
kind:
|
||||||
|
- article
|
||||||
|
---
|
||||||
|
Hello everyone! This is part 5 of my series on animation. Today’s post will be short, since we’re only going to talk about color and opacity.
|
||||||
|
|
||||||
|
<!--more-->
|
||||||
|
|
||||||
|
I’ll give a shout-out to <http://csstriggers.com>, if you are interested in [CSS Animation performance](https://blogs.adobe.com/webplatform/2014/03/18/css-animations-and-transitions-performance/), then check them out to see what triggers a repaint and/or reflow. Take a look at the other posts in this series!
|
||||||
|
|
||||||
|
* Part 1 — [Animatable: Border](https://brandonrozek.com/2015/05/animatable-border/)
|
||||||
|
* Part 2 — [Animatable: Box Model](https://brandonrozek.com/2015/09/animatable-box-model/)
|
||||||
|
* Part 3 — [Animatable: Text](https://brandonrozek.com/2015/10/animatable-text/)
|
||||||
|
* Part 4 — [Animatable: Location](https://brandonrozek.com/2015/10/animatable-location/)
|
||||||
|
|
||||||
|
This post goes with a [Codepen demo](http://codepen.io/brandonrozek/full/rOzeyO/){.broken_link} I made, I’ll reference it later in this post.
|
||||||
|
|
||||||
|
### <a href="#color" name="color"></a>color {#color}
|
||||||
|
|
||||||
|
* Accepts any [color](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value) value
|
||||||
|
* Typically inherits it’s color from the parent element
|
||||||
|
|
||||||
|
The color property sets the color of an element’s text content and its decoration. During the animation, the browser sees the colors in their red, green, and blue (rgb) components, then increments/decrements those values until it reaches the color it’s animating to. For example, in the Codepen demo (#1), the color of the text is changing from <code class="language-css">red</code> or <code class="language-css">rgb(255, 0, 0)</code> to <code class="language-css">green</code> or <code class="language-css">rgb(0, 255, 0)</code>. Meaning the red component is going from 255 to 0 and the green component is going from 0 to 255 during the animation.
|
||||||
|
|
||||||
|
<pre><code class="language-css">@keyframes color {
|
||||||
|
|
||||||
|
to { color: green; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.color {
|
||||||
|
|
||||||
|
font-size: 2rem;
|
||||||
|
|
||||||
|
color: red;
|
||||||
|
|
||||||
|
text-decoration: underline;
|
||||||
|
|
||||||
|
animation: color 1.5s ease-in 0s infinite alternate;
|
||||||
|
|
||||||
|
}
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
### <a href="#opacity" name="opacity"></a>opacity {#opacity}
|
||||||
|
|
||||||
|
* Accepts any number from [0, 1]
|
||||||
|
* Initial Value: 1
|
||||||
|
|
||||||
|
Opacity sets the transparency of an element and it’s decendants. Unlike <code class="language-css">display: none;</code>, when the element is <code class="language-css">opacity: 0;</code> the element still holds it’s space on the page. In the Codepen demo (#2), you can see the element and it’s children fading out.
|
||||||
|
|
||||||
|
<pre><code class="language-css">@keyframes opacity {
|
||||||
|
|
||||||
|
to { opacity: 0; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.opacity {
|
||||||
|
|
||||||
|
height: 5rem;
|
||||||
|
|
||||||
|
width: 5rem;
|
||||||
|
|
||||||
|
background-color: #cd86e4;
|
||||||
|
|
||||||
|
opacity: 1;
|
||||||
|
|
||||||
|
animation: opacity 2s linear 0s infinite alternate;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.opacity div {
|
||||||
|
|
||||||
|
margin-left: auto;
|
||||||
|
|
||||||
|
margin-right: auto;
|
||||||
|
|
||||||
|
height: 3rem;
|
||||||
|
|
||||||
|
width: 3rem;
|
||||||
|
|
||||||
|
background-color: lightblue;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.opacity div div {
|
||||||
|
|
||||||
|
margin-left: auto;
|
||||||
|
|
||||||
|
margin-right: auto;
|
||||||
|
|
||||||
|
height: 1rem;
|
||||||
|
|
||||||
|
width: 1rem;
|
||||||
|
|
||||||
|
background-color: #00b300;
|
||||||
|
|
||||||
|
}
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
### <a href="#conclusion" name="conclusion"></a>Conclusion {#conclusion}
|
||||||
|
|
||||||
|
I used to work on a chat application called Babbler. In it, I used opacity to fade in the messages as the user recieved them. With all these different types of animations, you can visually show the user what’s going on in your website/application. Doing this series helps me, (as well as you, I hope) recognize all the different properties and possibilities for animation. This is the second to last part of this series, meaning that the next part is the finale. I’m somewhat sad to see this series ending but excited at the same time. Until next time, have fun animating. 🙂
|
144
content/blog/2015-10-18-animatable-transform.md
Normal file
144
content/blog/2015-10-18-animatable-transform.md
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
---
|
||||||
|
id: 337
|
||||||
|
title: 'Animatable: Transform'
|
||||||
|
date: 2015-10-18T16:32:37+00:00
|
||||||
|
author: Brandon Rozek
|
||||||
|
layout: post
|
||||||
|
guid: https://brandonrozek.com/?p=337
|
||||||
|
permalink: /2015/10/animatable-transform/
|
||||||
|
medium_post:
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";N;s:10:"author_url";N;s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";N;s:2:"id";N;s:21:"follower_notification";N;s:7:"license";N;s:14:"publication_id";N;s:6:"status";N;s:3:"url";N;}'
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";s:74:"https://cdn-images-1.medium.com/fit/c/200/200/1*dmbNkD5D-u45r44go_cf0g.png";s:10:"author_url";s:32:"https://medium.com/@brandonrozek";s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";s:2:"no";s:2:"id";s:12:"1eb4fcf6a5df";s:21:"follower_notification";s:3:"yes";s:7:"license";s:19:"all-rights-reserved";s:14:"publication_id";s:2:"-1";s:6:"status";s:6:"public";s:3:"url";s:66:"https://medium.com/@brandonrozek/animatable-transform-1eb4fcf6a5df";}'
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";s:74:"https://cdn-images-1.medium.com/fit/c/200/200/1*dmbNkD5D-u45r44go_cf0g.png";s:10:"author_url";s:32:"https://medium.com/@brandonrozek";s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";s:2:"no";s:2:"id";s:12:"1eb4fcf6a5df";s:21:"follower_notification";s:3:"yes";s:7:"license";s:19:"all-rights-reserved";s:14:"publication_id";s:2:"-1";s:6:"status";s:6:"public";s:3:"url";s:66:"https://medium.com/@brandonrozek/animatable-transform-1eb4fcf6a5df";}'
|
||||||
|
mf2_cite:
|
||||||
|
- 'a:1:{s:6:"author";a:0:{}}'
|
||||||
|
- 'a:1:{s:6:"author";a:0:{}}'
|
||||||
|
tumblr_post_id:
|
||||||
|
- "135657540174"
|
||||||
|
- "135657540174"
|
||||||
|
kind:
|
||||||
|
- article
|
||||||
|
---
|
||||||
|
This is the last post of the animatable series. The grand finale. Here, we will talk about the transform property. It’s only one property but it comes with a lot of goodies in the form of transform-functions.
|
||||||
|
|
||||||
|
<!--more-->
|
||||||
|
|
||||||
|
This is not a comprehensize list. If you want one of those, please check out MDN. They have a great [resource](https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function). This is the last post of the animatable series, I recommend you check out the other parts too.
|
||||||
|
|
||||||
|
* Part 1 — [Animatable: Border](https://brandonrozek.com/2015/05/animatable-border/)
|
||||||
|
* Part 2 — [Animatable: Box Model](https://brandonrozek.com/2015/09/animatable-box-model/)
|
||||||
|
* Part 3 — [Animatable: Text](https://brandonrozek.com/2015/10/animatable-text/)
|
||||||
|
* Part 4 — [Animatable: Location](https://brandonrozek.com/2015/10/animatable-location/)
|
||||||
|
* Part 5 — [Animatable: Visual](https://brandonrozek.com/2015/10/animatable-visual/)
|
||||||
|
|
||||||
|
Animatable: Transform goes with a [CodePen demo](http://codepen.io/brandonrozek/full/ojoMyE){.broken_link} I made. Please check it out, as I will be referencing it later on in the post.
|
||||||
|
|
||||||
|
### <a href="#rotate()" name="rotate()"></a>rotate() {#rotate()}
|
||||||
|
|
||||||
|
The rotate() function moves the element around a fixed point as defined by the [transform-origin](https://developer.mozilla.org/en-US/docs/Web/CSS/transform-origin) property. In the Codepen demo (#1), the star continously rotates 360 degrees.
|
||||||
|
|
||||||
|
<pre><code class="language-css">@keyframes rotate {
|
||||||
|
|
||||||
|
to { transform: rotate(360deg);}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.rotate {
|
||||||
|
|
||||||
|
width: 6rem;
|
||||||
|
|
||||||
|
height: 6rem;
|
||||||
|
|
||||||
|
background-image: url("https://upload.wikimedia.org/wikipedia/commons/thumb/5/51/Star_full.svg/2000px-Star_full.svg.png");
|
||||||
|
|
||||||
|
background-size: cover;
|
||||||
|
|
||||||
|
transform: none;
|
||||||
|
|
||||||
|
animation: rotate 1s ease 0s infinite;
|
||||||
|
}
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
### <a href="#scale()" name="scale()"></a>scale() {#scale()}
|
||||||
|
|
||||||
|
Scale(sx, sy) modifies the size of the element by taking 2 arguments: sx and sy. Where sx is the multiplier that scales the element in the x-direction while sy is the multiplier that scales the element in the y-direction. In the CodePen demo (#2), the little ninja man scales to half his size and back to normal again.
|
||||||
|
|
||||||
|
<pre><code class="language-css">@keyframes scale {
|
||||||
|
|
||||||
|
to { transform: scale(.5); }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.scale {
|
||||||
|
|
||||||
|
width: 6rem;
|
||||||
|
|
||||||
|
height: 6rem;
|
||||||
|
|
||||||
|
background-image: url("https://upload.wikimedia.org/wikipedia/commons/7/71/Monocromaticman.JPG");
|
||||||
|
|
||||||
|
background-size: contain;
|
||||||
|
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
|
||||||
|
transform: none;
|
||||||
|
|
||||||
|
animation: scale 1s ease 0s infinite alternate;
|
||||||
|
}
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
### <a href="#skew()" name="skew()"></a>skew() {#skew()}
|
||||||
|
|
||||||
|
Skew distorts the element by moving each point to a certain angle determined by it’s distance from the origin. In the CodePen demo (#3), the square skews -20 degrees, making it appear as a parallelogram.
|
||||||
|
|
||||||
|
<pre><code class="language-css">@keyframes skew {
|
||||||
|
|
||||||
|
to { transform: skew(-20deg); }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.skew {
|
||||||
|
|
||||||
|
height: 6rem;
|
||||||
|
|
||||||
|
width: 6rem;
|
||||||
|
|
||||||
|
background-color: lightblue;
|
||||||
|
|
||||||
|
transform: none;
|
||||||
|
|
||||||
|
animation: skew .75s ease 0s infinite alternate;
|
||||||
|
}
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
### <a href="#translate()" name="translate()"></a>translate() {#translate()}
|
||||||
|
|
||||||
|
Transform(tx, ty) moves the element as specified by it’s two parameters tx and ty. Tx tells the browser how many units to move it in the x direction while ty tells the browser how many units to move the element in the y direction. In the CodePen demo (#4), the car moves forward 10rem and back again.
|
||||||
|
|
||||||
|
<pre><code class="language-css">@keyframes translate {
|
||||||
|
|
||||||
|
to { transform: translate(10rem);}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.translate {
|
||||||
|
|
||||||
|
height: 6rem;
|
||||||
|
|
||||||
|
width: 6rem;
|
||||||
|
|
||||||
|
background-image: url("http://res.freestockphotos.biz/pictures/15/15685-illustration-of-a-red-cartoon-car-pv.png");
|
||||||
|
|
||||||
|
background-size: contain;
|
||||||
|
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
|
||||||
|
transform: none;
|
||||||
|
|
||||||
|
animation: translate 1s ease 0s infinite alternate;
|
||||||
|
}
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
### <a href="#conclusion" name="conclusion"></a>Conclusion {#conclusion}
|
||||||
|
|
||||||
|
It is now the end of the Animatable series. I am sad to see this first completed series of mine go. It’s okay though, I’m bound to think of another series to write for you guys in no time. My goal in Animatable, is to show you (and me) what is possible using CSS animations. With this small reference completed, we have a good overview of the different tools available. Now with our new toolbelt, go out and craft meaningful experinces!
|
101
content/blog/2015-10-18-javascript-conditional-statements.md
Normal file
101
content/blog/2015-10-18-javascript-conditional-statements.md
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
---
|
||||||
|
id: 344
|
||||||
|
title: Javascript Conditional Statements
|
||||||
|
date: 2015-10-18T18:30:21+00:00
|
||||||
|
author: Brandon Rozek
|
||||||
|
layout: post
|
||||||
|
guid: https://brandonrozek.com/?p=344
|
||||||
|
permalink: /2015/10/javascript-conditional-statements/
|
||||||
|
medium_post:
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";N;s:10:"author_url";N;s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";N;s:2:"id";N;s:21:"follower_notification";N;s:7:"license";N;s:14:"publication_id";N;s:6:"status";N;s:3:"url";N;}'
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";s:74:"https://cdn-images-1.medium.com/fit/c/200/200/1*dmbNkD5D-u45r44go_cf0g.png";s:10:"author_url";s:32:"https://medium.com/@brandonrozek";s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";s:2:"no";s:2:"id";s:12:"72ac61ee8d04";s:21:"follower_notification";s:2:"no";s:7:"license";s:19:"all-rights-reserved";s:14:"publication_id";s:2:"-1";s:6:"status";s:6:"public";s:3:"url";s:79:"https://medium.com/@brandonrozek/javascript-conditional-statements-72ac61ee8d04";}'
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";s:74:"https://cdn-images-1.medium.com/fit/c/200/200/1*dmbNkD5D-u45r44go_cf0g.png";s:10:"author_url";s:32:"https://medium.com/@brandonrozek";s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";s:2:"no";s:2:"id";s:12:"72ac61ee8d04";s:21:"follower_notification";s:2:"no";s:7:"license";s:19:"all-rights-reserved";s:14:"publication_id";s:2:"-1";s:6:"status";s:6:"public";s:3:"url";s:79:"https://medium.com/@brandonrozek/javascript-conditional-statements-72ac61ee8d04";}'
|
||||||
|
dsq_thread_id:
|
||||||
|
- "4237729275"
|
||||||
|
- "4237729275"
|
||||||
|
mf2_cite:
|
||||||
|
- 'a:1:{s:6:"author";a:0:{}}'
|
||||||
|
- 'a:1:{s:6:"author";a:0:{}}'
|
||||||
|
tumblr_post_id:
|
||||||
|
- "135657634939"
|
||||||
|
- "135657634939"
|
||||||
|
kind:
|
||||||
|
- article
|
||||||
|
---
|
||||||
|
Javascript, like most other programming languages, include ways to run blocks of code when something meets a condition. Here, I will describe the most common ways to do so.
|
||||||
|
|
||||||
|
<!--more-->
|
||||||
|
|
||||||
|
This post is part of my lecture series for Math I/O. There is no pre-reading for this lecture.
|
||||||
|
|
||||||
|
### <a href="#if-statement" name="if-statement"></a>If Statement {#if-statement}
|
||||||
|
|
||||||
|
To run a block of code when a condition is true, use an <code class="language-javascript">if</code> statement.
|
||||||
|
|
||||||
|
<pre><code class="language-javascript"> if (condition) {
|
||||||
|
doSomething();
|
||||||
|
}
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
You can also run a block of code when a condition is false using the <code class="language-javascript">else</code> statement.
|
||||||
|
|
||||||
|
<pre><code class="language-javascript"> if (condition) {
|
||||||
|
doSomething();
|
||||||
|
} else {
|
||||||
|
doSomethingElse();
|
||||||
|
}
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
### <a href="#switch-statement" name="switch-statement"></a>Switch statement {#switch-statement}
|
||||||
|
|
||||||
|
If you want to check a variable for **equality** against multiple different cases, use a <code class="language-javascript">switch</code> statement.
|
||||||
|
|
||||||
|
<pre><code class="language-javascript"> switch (variable) {
|
||||||
|
case condition1:
|
||||||
|
doSomething();
|
||||||
|
break;
|
||||||
|
case condition2:
|
||||||
|
doSomethingElse();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
doSomethingCompletelyDifferent();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
The default statement runs when the variable doesn’t equal any of the cases.
|
||||||
|
|
||||||
|
### <a href="#while-loop" name="while-loop"></a>While loop {#while-loop}
|
||||||
|
|
||||||
|
To run a block of code over and over again until a condition is false, use a <code class="language-javascript">while</code> loop.
|
||||||
|
|
||||||
|
<pre><code class="language-javascript"> while (condition) {
|
||||||
|
doSomething();
|
||||||
|
}
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
Don’t forget to include something in the loop that will eventually make the condition <code class="language-javascript">false</code>, otherwise you run into an infinite loop. (Which is a loop that never stops repeating itself; most likely crashing your browser)
|
||||||
|
|
||||||
|
### <a href="#for-loop" name="for-loop"></a>For loop {#for-loop}
|
||||||
|
|
||||||
|
If you want to run something a certain amount of times, use a “<code class="language-javascript">for"</code> loop. For loops can be broken down into three components: an initiating statement, a condition, and a statement that runs after every loop.
|
||||||
|
|
||||||
|
<pre><code class="language-javascript"> for (var i = 0; i < 5; i++) {
|
||||||
|
doSomething();
|
||||||
|
}
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
Here you have the initiating statement of <code class="language-javascript">var i = 0</code>. From there you check, is <code class="language-javascript">i</code> less than 5? Yes, so then we <code class="language-javascript">doSomething();</code>. After we <code class="language-javascript">doSomething();</code>, we add 1 to <code class="language-javascript">i</code>. Now <code class="language-javascript">i</code> equals 2. Is <code class="language-javascript">i</code> still less than 5? Yes, so we <code class="language-javascript">doSomething();</code>. Then we add 1 to <code class="language-javascript">i</code> again. This loop will keep happening until <code class="language-javascript">i</code> is not less than 5.
|
||||||
|
|
||||||
|
### <a href="#conclusion" name="conclusion"></a>Conclusion {#conclusion}
|
||||||
|
|
||||||
|
Having different control/conditional statements helps keep the state of any application you’re making. Did the user say not to notify them? Then don’t, otherwise (else) notify them. That’s all I have to say for this week. Hope this post helps you get a little more used to this big world called programming.
|
||||||
|
|
||||||
|
<pre><code class="language-javascript"> if (youLikeThisPost) {
|
||||||
|
console.log("Come back next week! :)");
|
||||||
|
} else {
|
||||||
|
console.log("Aww that's okay, you should give me another chance next week :)");
|
||||||
|
}
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
I recommend that you look at different perspectives of the same concepts. WebCheatSheet.com has a similar post to mine, check out what they had to say [here](http://webcheatsheet.com/javascript/if_else_switch.php).
|
115
content/blog/2015-10-25-functions.md
Normal file
115
content/blog/2015-10-25-functions.md
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
---
|
||||||
|
id: 350
|
||||||
|
title: Functions
|
||||||
|
date: 2015-10-25T13:48:41+00:00
|
||||||
|
author: Brandon Rozek
|
||||||
|
layout: post
|
||||||
|
guid: https://brandonrozek.com/?p=350
|
||||||
|
permalink: /2015/10/functions/
|
||||||
|
medium_post:
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";N;s:10:"author_url";N;s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";N;s:2:"id";N;s:21:"follower_notification";N;s:7:"license";N;s:14:"publication_id";N;s:6:"status";N;s:3:"url";N;}'
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";s:74:"https://cdn-images-1.medium.com/fit/c/200/200/1*dmbNkD5D-u45r44go_cf0g.png";s:10:"author_url";s:32:"https://medium.com/@brandonrozek";s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";s:2:"no";s:2:"id";s:12:"44583f68d3fa";s:21:"follower_notification";s:2:"no";s:7:"license";s:19:"all-rights-reserved";s:14:"publication_id";s:2:"-1";s:6:"status";s:6:"public";s:3:"url";s:55:"https://medium.com/@brandonrozek/functions-44583f68d3fa";}'
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";s:74:"https://cdn-images-1.medium.com/fit/c/200/200/1*dmbNkD5D-u45r44go_cf0g.png";s:10:"author_url";s:32:"https://medium.com/@brandonrozek";s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";s:2:"no";s:2:"id";s:12:"44583f68d3fa";s:21:"follower_notification";s:2:"no";s:7:"license";s:19:"all-rights-reserved";s:14:"publication_id";s:2:"-1";s:6:"status";s:6:"public";s:3:"url";s:55:"https://medium.com/@brandonrozek/functions-44583f68d3fa";}'
|
||||||
|
mf2_cite:
|
||||||
|
- 'a:1:{s:6:"author";a:0:{}}'
|
||||||
|
- 'a:1:{s:6:"author";a:0:{}}'
|
||||||
|
tumblr_post_id:
|
||||||
|
- "135657661534"
|
||||||
|
- "135657661534"
|
||||||
|
kind:
|
||||||
|
- article
|
||||||
|
---
|
||||||
|
Ever had a snippet of code that appears multiple times in different places in your program? Whenever you had to change that snippet, you end up playing this game of search and replace. Functions can help. They exist to abstract your code, making it not only easier to change that little snippet, but to read and debug your code as well.
|
||||||
|
|
||||||
|
<!--more-->
|
||||||
|
|
||||||
|
### <a href="#how-to-create/execute-a-function" name="how-to-create/execute-a-function"></a>How to create/execute a function {#how-to-create/execute-a-function}
|
||||||
|
|
||||||
|
To make a function
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">
|
||||||
|
var doSomething = function() {
|
||||||
|
doStuff;
|
||||||
|
}
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
To call the above function to execute
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">
|
||||||
|
doSomething();
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
### <a href="#arguments" name="arguments"></a>Arguments {#arguments}
|
||||||
|
|
||||||
|
You can also add in arguments (parameters that go inside the paraenthesis next to the word function) for the functions to use.
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">
|
||||||
|
var add = function(number1, number2) {
|
||||||
|
return number1 + number2;
|
||||||
|
}
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
And when you use the `return` keyword, like the function above. You can store the value in a variable for future use.
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">
|
||||||
|
var total = add(1, 3);
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
<code class="language-javascript">total</code> here will equal `4`
|
||||||
|
|
||||||
|
### <a href="#scope" name="scope"></a>Scope {#scope}
|
||||||
|
|
||||||
|
Functions create their own scope, which means that variables created inside the function will only be able available within that function.
|
||||||
|
|
||||||
|
The snippet below will output an error like <code class="language-javascript">total is not defined</code>
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">
|
||||||
|
var add = function(number1, number2) {
|
||||||
|
var total = number1 + number2;
|
||||||
|
}
|
||||||
|
console.log(total);
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
Below is a correct example of the concept
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">
|
||||||
|
//Function below converts km/hr to m/s
|
||||||
|
var convert = function(speed) {
|
||||||
|
var metersPerHour = speed * 1000;
|
||||||
|
var metersPerSecound = metersPerHour / 3600;
|
||||||
|
return metersPerSecond;
|
||||||
|
}
|
||||||
|
var currentSpeed = convert(5);
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
It’s also important to note that functions can use variables outside of it; granted it resides in the same scope.
|
||||||
|
|
||||||
|
Here is an example of a variable that doesn’t reside in the same scope as the function. (The below code will fail)
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">
|
||||||
|
var something = function() {
|
||||||
|
var x = 5;
|
||||||
|
var y = 2;
|
||||||
|
}
|
||||||
|
something();
|
||||||
|
var addXandY = function() {
|
||||||
|
console.log(x + y);
|
||||||
|
}
|
||||||
|
addXandY();
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
Below, is an example of where the variable does reside in the same scope as the function. Which allows this snippet to execute properly.
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">
|
||||||
|
var x = 5;
|
||||||
|
var addX = function(a) {
|
||||||
|
return a + x;
|
||||||
|
}
|
||||||
|
var sum = addX(6);
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
<code class="language-javascript">sum</code> here will equal <code class="language-javascript">11</code>
|
||||||
|
|
||||||
|
### <a href="#conclusion" name="conclusion"></a>Conclusion {#conclusion}
|
||||||
|
|
||||||
|
As long as you name them appropriately, functions are useful for abstracting your code, making them easier to understand. This concludes another lecture made for the members over at Math I/O. Until next week 🙂
|
362
content/blog/2015-11-14-service-workers.md
Normal file
362
content/blog/2015-11-14-service-workers.md
Normal file
|
@ -0,0 +1,362 @@
|
||||||
|
---
|
||||||
|
id: 400
|
||||||
|
title: An Offline Experience with Service Workers
|
||||||
|
date: 2015-11-14T15:47:06+00:00
|
||||||
|
author: Brandon Rozek
|
||||||
|
layout: post
|
||||||
|
guid: https://brandonrozek.com/?p=400
|
||||||
|
permalink: /2015/11/service-workers/
|
||||||
|
medium_post:
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";N;s:10:"author_url";N;s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";N;s:2:"id";N;s:21:"follower_notification";N;s:7:"license";N;s:14:"publication_id";N;s:6:"status";N;s:3:"url";N;}'
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";s:74:"https://cdn-images-1.medium.com/fit/c/200/200/1*dmbNkD5D-u45r44go_cf0g.png";s:10:"author_url";s:32:"https://medium.com/@brandonrozek";s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";s:2:"no";s:2:"id";s:12:"125e50979ed8";s:21:"follower_notification";s:3:"yes";s:7:"license";s:19:"all-rights-reserved";s:14:"publication_id";s:2:"-1";s:6:"status";s:6:"public";s:3:"url";s:88:"https://medium.com/@brandonrozek/an-offline-experience-with-service-workers-125e50979ed8";}'
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";s:74:"https://cdn-images-1.medium.com/fit/c/200/200/1*dmbNkD5D-u45r44go_cf0g.png";s:10:"author_url";s:32:"https://medium.com/@brandonrozek";s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";s:2:"no";s:2:"id";s:12:"125e50979ed8";s:21:"follower_notification";s:3:"yes";s:7:"license";s:19:"all-rights-reserved";s:14:"publication_id";s:2:"-1";s:6:"status";s:6:"public";s:3:"url";s:88:"https://medium.com/@brandonrozek/an-offline-experience-with-service-workers-125e50979ed8";}'
|
||||||
|
mf2_cite:
|
||||||
|
- 'a:1:{s:6:"author";a:0:{}}'
|
||||||
|
- 'a:1:{s:6:"author";a:0:{}}'
|
||||||
|
tumblr_post_id:
|
||||||
|
- "135657690564"
|
||||||
|
- "135657690564"
|
||||||
|
kind:
|
||||||
|
- article
|
||||||
|
---
|
||||||
|
I’m excited to say that I’ve written my first service worker for brandonrozek.com. What is a service worker? A service worker provides an extra layer between the client and the server. The exciting part about this is that you can use service workers to deliver an offline experience. (Cached versions of your site, offline pages, etc.)
|
||||||
|
|
||||||
|
<!--more-->
|
||||||
|
|
||||||
|
Service workers are currently supported in Chrome, Opera, and Firefox nightly. You don’t have to worry too much about browser support because the Service Worker spec was written in a [progressively enchanced](http://alistapart.com/article/understandingprogressiveenhancement) way meaning it won’t break your existing site 🙂
|
||||||
|
|
||||||
|
## <a href="#caveats" name="caveats"></a>Caveats {#caveats}
|
||||||
|
|
||||||
|
You need HTTPS to be able to use service workers on your site. This is mainly for security reasons. Imagine if a third party can control all of the networking requests on your site? If you don’t want to go out and buy a SSL Certificate, there are a couple free ways to go about this. 1) [Cloudflare](https://www.cloudflare.com/) 2) [Let’s Encrypt](https://letsencrypt.org/) Service workers are promise heavy. Promises contain a then clause which runs code asynchronously. If you’re not accustomed to this idea please check out this [post](https://ponyfoo.com/articles/es6-promises-in-depth) by Nicolas Bevacqua. Now onto making the service worker! If you want to skip to the final code scroll down to the bottom. Unless you don’t like my syntax highlighting, then you can check out this [gist](https://gist.github.com/brandonrozek/0cf038df40a913fda655).
|
||||||
|
|
||||||
|
## <a href="#register-the-service-worker" name="register-the-service-worker"></a>Register the service worker {#register-the-service-worker}
|
||||||
|
|
||||||
|
Place `service-worker.js` on the root of your site. This is so the service worker can access all the files in the site. Then in your main javascript file, register the service worker.
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">
|
||||||
|
if (navigator.serviceWorker) {
|
||||||
|
navigator.serviceWorker.register('/serviceworker.js', {
|
||||||
|
scope: '/'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
## <a href="#install-the-service-worker" name="install-the-service-worker"></a>Install the service worker {#install-the-service-worker}
|
||||||
|
|
||||||
|
The first time the service worker runs, it emits the `install` event. At this time, we can load the visitor’s cache with some resources for when they’re offline. Every so often, I like to change up the theme of the site. So I have version numbers attached to my files. I would also like to invalidate my cache with this version number. So at the top of the file I added
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">
|
||||||
|
var version = 'v2.0.24:';
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
Now, to specify which files I want the service worker to cache for offline use. I thought my home page and my offline page would be good enough.
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">
|
||||||
|
var offlineFundamentals = [
|
||||||
|
'/',
|
||||||
|
'/offline/'
|
||||||
|
];
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
Since <code class="language-javascript">cache.addAll()</code> hasn’t been implemented yet in any of the browsers, and the polyfill implementation didn’t work for my needs. I pieced together my own.
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">
|
||||||
|
var updateStaticCache = function() {
|
||||||
|
return caches.open(version + 'fundamentals').then(function(cache) {
|
||||||
|
return Promise.all(offlineFundamentals.map(function(value) {
|
||||||
|
var request = new Request(value);
|
||||||
|
var url = new URL(request.url);
|
||||||
|
if (url.origin != location.origin) {
|
||||||
|
request = new Request(value, {mode: 'no-cors'});
|
||||||
|
}
|
||||||
|
return fetch(request).then(function(response) {
|
||||||
|
var cachedCopy = response.clone();
|
||||||
|
return cache.put(request, cachedCopy);
|
||||||
|
});
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
};
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
Let’s go through this chunk of code.
|
||||||
|
|
||||||
|
1. Open the cache called <code class="language-javascript">'v2.0.24:fundamentals'</code>
|
||||||
|
2. Go through all of the <code class="language-javascript">offlineFundamental</code>‘s URLs
|
||||||
|
* Does the file I ask for come from the same domain as my site?
|
||||||
|
* No. Then, make the request ‘no-cors’ (I had difficulty getting my asset files in cors mode. If the cors headers are included in the response, then you can take out this line)
|
||||||
|
* Fetch the file from the network and then cache it.
|
||||||
|
|
||||||
|
Now we call it when the `install` event is fired.
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">
|
||||||
|
self.addEventListener("install", function(event) {
|
||||||
|
event.waitUntil(updateStaticCache())
|
||||||
|
})
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
With this we now cached all the files in the offlineFundamentals array during the install step.
|
||||||
|
|
||||||
|
## <a href="#clear-out-the-old-cache" name="clear-out-the-old-cache"></a>Clear out the old cache {#clear-out-the-old-cache}
|
||||||
|
|
||||||
|
Since we’re caching everything. If you change one of the files, your visitor wouldn’t get the changed file. Wouldn’t it be nice to remove old files from the visitor’s cache? Every time the service worker finishes the install step, it releases an `activate` event. We can use this to look and see if there are any old cache containers on the visitor’s computer. From [Nicolas’ code](https://ponyfoo.com/articles/serviceworker-revolution). Thanks for sharing 🙂
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">
|
||||||
|
var clearOldCaches = function() {
|
||||||
|
return caches.keys().then(function(keys) {
|
||||||
|
return Promise.all(
|
||||||
|
keys
|
||||||
|
.filter(function (key) {
|
||||||
|
return key.indexOf(version) != 0;
|
||||||
|
})
|
||||||
|
.map(function (key) {
|
||||||
|
return caches.delete(key);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
1. Check the names of each of the cache containers
|
||||||
|
2. If they don’t start with the correct version number
|
||||||
|
* Delete that cache container
|
||||||
|
|
||||||
|
Call the function when the `activate` event fires.
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">
|
||||||
|
self.addEventListener("activate", function(event) {
|
||||||
|
event.waitUntil(clearOldCaches())
|
||||||
|
});
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
## <a href="#intercepting-fetch-requests" name="intercepting-fetch-requests"></a>Intercepting fetch requests {#intercepting-fetch-requests}
|
||||||
|
|
||||||
|
The cool thing about service worker’s is that it can handle file requests. We could cache all files requested for offline use, and if a fetch for a resource failed, then the service worker can look for it in the cache or provide an alternative. This is a large section, so I’m going to attempt to break it down as much as I can.
|
||||||
|
|
||||||
|
### <a href="#limit-the-cache" name="limit-the-cache"></a>Limit the cache {#limit-the-cache}
|
||||||
|
|
||||||
|
If the visitor started browsing all of the pages on my site, his or her cache would start to get bloated with files. To not burden my visitors, I decided to only keep the latest 25 pages and latest 10 images in the cache.
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">
|
||||||
|
var limitCache = function(cache, maxItems) {
|
||||||
|
cache.keys().then(function(items) {
|
||||||
|
if (items.length > maxItems) {
|
||||||
|
cache.delete(items[0]);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
We’ll call it later in the code.
|
||||||
|
|
||||||
|
### <a href="#fetch-from-network-and-cache" name="fetch-from-network-and-cache"></a>Fetch from network and cache {#fetch-from-network-and-cache}
|
||||||
|
|
||||||
|
Every time I fetch a file from the network I throw it into a specific cache container. <code class="language-javascript">'pages'</code> for HTML files, <code class="language-javascript">'images'</code> for CSS files, and <code class="language-javascript">'assets'</code> for any other file. This is so I can handle the cache limiting above easier. Defined within the `fetch` event.
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">
|
||||||
|
var fetchFromNetwork = function(response) {
|
||||||
|
var cacheCopy = response.clone();
|
||||||
|
if (event.request.headers.get('Accept').indexOf('text/html') != -1) {
|
||||||
|
caches.open(version + 'pages').then(function(cache) {
|
||||||
|
cache.put(event.request, cacheCopy).then(function() {
|
||||||
|
limitCache(cache, 25);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
} else if (event.request.headers.get('Accept').indexOf('image') != -1) {
|
||||||
|
caches.open(version + 'images').then(function(cache) {
|
||||||
|
cache.put(event.request, cacheCopy).then(function() {
|
||||||
|
limitCache(cache, 10);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
caches.open(version + 'assets').then(function add(cache) {
|
||||||
|
cache.put(event.request, cacheCopy);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
### <a href="#when-the-network-fails" name="when-the-network-fails"></a>When the network fails {#when-the-network-fails}
|
||||||
|
|
||||||
|
There are going to be times where the visitor cannot access the website. Maybe they went in a tunnel while they were riding a train? Or maybe your site went down. I thought it would be nice for my reader’s to be able to look over my blog posts again regardless of an internet connection. So I provide a fall-back. Defined within the `fetch` event.
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">
|
||||||
|
var fallback = function() {
|
||||||
|
if (event.request.headers.get('Accept').indexOf('text/html') != -1) {
|
||||||
|
return caches.match(event.request).then(function (response) {
|
||||||
|
return response || caches.match('/offline/');
|
||||||
|
})
|
||||||
|
} else if (event.request.headers.get('Accept').indexOf('image') != -1) {
|
||||||
|
return new Response('<svg width="400" height="300" role="img" aria-labelledby="offline-title" viewBox="0 0 400 300" xmlns="http://www.w3.org/2000/svg"><title id="offline-title">Offline</title><g fill="none" fill-rule="evenodd"><path fill="#D8D8D8" d="M0 0h400v300H0z"/><text fill="#9B9B9B" font-family="Helvetica Neue,Arial,Helvetica,sans-serif" font-size="72" font-weight="bold"><tspan x="93" y="172">offline</tspan></text></g></svg>', { headers: { 'Content-Type': 'image/svg+xml' }});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
1. Is the request for a HTML file?
|
||||||
|
* Show the [offline](https://brandonrozek.com/offline/) page.
|
||||||
|
2. Is the request for an image?
|
||||||
|
* Show a place-holder image (Courtesy of [Jeremy Keith](https://adactio.com/journal/9775))
|
||||||
|
|
||||||
|
### <a href="#handle-the-request" name="handle-the-request"></a>Handle the request {#handle-the-request}
|
||||||
|
|
||||||
|
First off, I’m only handling GET requests.
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">
|
||||||
|
if (event.request.method != 'GET') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
For HTML files, grab the file from the network. If that fails, then look for it in the cache. _Network then cache strategy_
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">
|
||||||
|
if (event.request.headers.get('Accept').indexOf('text/html') != -1) {
|
||||||
|
event.respondWith(fetch(event.request).then(fetchFromNetwork, fallback));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
For non-HTML files, follow this series of steps
|
||||||
|
|
||||||
|
1. Check the cache
|
||||||
|
2. Does a cache exist for this file?
|
||||||
|
* Yes. Then show it
|
||||||
|
* No. Then grab it from the network and cache it.
|
||||||
|
|
||||||
|
_Cache then network strategy_
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">
|
||||||
|
event.respondWith(
|
||||||
|
caches.match(event.request).then(function(cached) {
|
||||||
|
return cached || fetch(event.request).then(fetchFromNetwork, fallback);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
For different stategy’s, take a look at Jake Archibald’s [offline cookbook](https://jakearchibald.com/2014/offline-cookbook/).
|
||||||
|
|
||||||
|
## <a href="#conclusion" name="conclusion"></a>Conclusion {#conclusion}
|
||||||
|
|
||||||
|
With all of that, we now have a fully functioning offline-capable website! I wouldn’t be able to implement this myself if it wasn’t for some of the awesome people I mentioned earlier sharing their experience. So share, share, share! With that sentiment, I’ll now share the full code for `service-worker.js` **Update:** There is a new version of this code over at this [blog post](https://brandonrozek.com/2015/11/limiting-cache-service-workers-revisited/).
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">
|
||||||
|
var version = 'v2.0.24:';
|
||||||
|
|
||||||
|
var offlineFundamentals = [
|
||||||
|
'/',
|
||||||
|
'/offline/'
|
||||||
|
];
|
||||||
|
|
||||||
|
//Add core website files to cache during serviceworker installation
|
||||||
|
var updateStaticCache = function() {
|
||||||
|
return caches.open(version + 'fundamentals').then(function(cache) {
|
||||||
|
return Promise.all(offlineFundamentals.map(function(value) {
|
||||||
|
var request = new Request(value);
|
||||||
|
var url = new URL(request.url);
|
||||||
|
if (url.origin != location.origin) {
|
||||||
|
request = new Request(value, {mode: 'no-cors'});
|
||||||
|
}
|
||||||
|
return fetch(request).then(function(response) {
|
||||||
|
var cachedCopy = response.clone();
|
||||||
|
return cache.put(request, cachedCopy);
|
||||||
|
});
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
//Clear caches with a different version number
|
||||||
|
var clearOldCaches = function() {
|
||||||
|
return caches.keys().then(function(keys) {
|
||||||
|
return Promise.all(
|
||||||
|
keys
|
||||||
|
.filter(function (key) {
|
||||||
|
return key.indexOf(version) != 0;
|
||||||
|
})
|
||||||
|
.map(function (key) {
|
||||||
|
return caches.delete(key);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
limits the cache
|
||||||
|
If cache has more than maxItems then it removes the first item in the cache
|
||||||
|
*/
|
||||||
|
var limitCache = function(cache, maxItems) {
|
||||||
|
cache.keys().then(function(items) {
|
||||||
|
if (items.length > maxItems) {
|
||||||
|
cache.delete(items[0]);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//When the service worker is first added to a computer
|
||||||
|
self.addEventListener("install", function(event) {
|
||||||
|
event.waitUntil(updateStaticCache())
|
||||||
|
})
|
||||||
|
|
||||||
|
//Service worker handles networking
|
||||||
|
self.addEventListener("fetch", function(event) {
|
||||||
|
|
||||||
|
//Fetch from network and cache
|
||||||
|
var fetchFromNetwork = function(response) {
|
||||||
|
var cacheCopy = response.clone();
|
||||||
|
if (event.request.headers.get('Accept').indexOf('text/html') != -1) {
|
||||||
|
caches.open(version + 'pages').then(function(cache) {
|
||||||
|
cache.put(event.request, cacheCopy).then(function() {
|
||||||
|
limitCache(cache, 25);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
} else if (event.request.headers.get('Accept').indexOf('image') != -1) {
|
||||||
|
caches.open(version + 'images').then(function(cache) {
|
||||||
|
cache.put(event.request, cacheCopy).then(function() {
|
||||||
|
limitCache(cache, 10);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
caches.open(version + 'assets').then(function add(cache) {
|
||||||
|
cache.put(event.request, cacheCopy);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Fetch from network failed
|
||||||
|
var fallback = function() {
|
||||||
|
if (event.request.headers.get('Accept').indexOf('text/html') != -1) {
|
||||||
|
return caches.match(event.request).then(function (response) {
|
||||||
|
return response || caches.match('/offline/');
|
||||||
|
})
|
||||||
|
} else if (event.request.headers.get('Accept').indexOf('image') != -1) {
|
||||||
|
return new Response('<svg width="400" height="300" role="img" aria-labelledby="offline-title" viewBox="0 0 400 300" xmlns="http://www.w3.org/2000/svg"><title id="offline-title">Offline</title><g fill="none" fill-rule="evenodd"><path fill="#D8D8D8" d="M0 0h400v300H0z"/><text fill="#9B9B9B" font-family="Helvetica Neue,Arial,Helvetica,sans-serif" font-size="72" font-weight="bold"><tspan x="93" y="172">offline</tspan></text></g></svg>', { headers: { 'Content-Type': 'image/svg+xml' }});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//This service worker won't touch non-get requests
|
||||||
|
if (event.request.method != 'GET') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//For HTML requests, look for file in network, then cache if network fails.
|
||||||
|
if (event.request.headers.get('Accept').indexOf('text/html') != -1) {
|
||||||
|
event.respondWith(fetch(event.request).then(fetchFromNetwork, fallback));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//For non-HTML requests, look for file in cache, then network if no cache exists.
|
||||||
|
event.respondWith(
|
||||||
|
caches.match(event.request).then(function(cached) {
|
||||||
|
return cached || fetch(event.request).then(fetchFromNetwork, fallback);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
//After the install event
|
||||||
|
self.addEventListener("activate", function(event) {
|
||||||
|
event.waitUntil(clearOldCaches())
|
||||||
|
});
|
||||||
|
</code></pre>
|
126
content/blog/2015-11-15-fractions-js.md
Normal file
126
content/blog/2015-11-15-fractions-js.md
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
---
|
||||||
|
id: 398
|
||||||
|
title: Math with Fractions.js
|
||||||
|
date: 2015-11-15T12:46:14+00:00
|
||||||
|
author: Brandon Rozek
|
||||||
|
layout: post
|
||||||
|
guid: https://brandonrozek.com/?p=398
|
||||||
|
permalink: /2015/11/fractions-js/
|
||||||
|
medium_post:
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";N;s:10:"author_url";N;s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";N;s:2:"id";N;s:21:"follower_notification";N;s:7:"license";N;s:14:"publication_id";N;s:6:"status";N;s:3:"url";N;}'
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";s:74:"https://cdn-images-1.medium.com/fit/c/200/200/1*dmbNkD5D-u45r44go_cf0g.png";s:10:"author_url";s:32:"https://medium.com/@brandonrozek";s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";s:2:"no";s:2:"id";s:12:"dd3b15d9d3c9";s:21:"follower_notification";s:2:"no";s:7:"license";s:19:"all-rights-reserved";s:14:"publication_id";s:2:"-1";s:6:"status";s:6:"public";s:3:"url";s:68:"https://medium.com/@brandonrozek/math-with-fractions-js-dd3b15d9d3c9";}'
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";s:74:"https://cdn-images-1.medium.com/fit/c/200/200/1*dmbNkD5D-u45r44go_cf0g.png";s:10:"author_url";s:32:"https://medium.com/@brandonrozek";s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";s:2:"no";s:2:"id";s:12:"dd3b15d9d3c9";s:21:"follower_notification";s:2:"no";s:7:"license";s:19:"all-rights-reserved";s:14:"publication_id";s:2:"-1";s:6:"status";s:6:"public";s:3:"url";s:68:"https://medium.com/@brandonrozek/math-with-fractions-js-dd3b15d9d3c9";}'
|
||||||
|
mf2_cite:
|
||||||
|
- 'a:1:{s:6:"author";a:0:{}}'
|
||||||
|
- 'a:1:{s:6:"author";a:0:{}}'
|
||||||
|
tumblr_post_id:
|
||||||
|
- "135657739439"
|
||||||
|
- "135657739439"
|
||||||
|
kind:
|
||||||
|
- article
|
||||||
|
---
|
||||||
|
Last week I published my first library over on Github called [Fractions.js](https://github.com/brandonrozek/Fractions.js). Fractions.js is a library to help avoid the [mathmatetical errors](http://floating-point-gui.de/) in floating point arithmetic. What do you mean by floating point artihmetic errors? Here is an example: <code class="language-javascript">.1 * .2</code> outputs <code class="language-javascript">0.020000000000000004</code> even though the correct answer is <code class="language-javascript">.02</code>
|
||||||
|
|
||||||
|
<!--more-->
|
||||||
|
|
||||||
|
## <a href="#purpose" name="purpose"></a>Purpose {#purpose}
|
||||||
|
|
||||||
|
My team and I are currently working on a new game for Math I/O. One of the levels is testing whether the player can add, subtract, multiply, and divide fractions correctly. I didn’t want to implement a solution where we check if the input entered is within a range of the answer. So I asked the team, how do you add, subtract, multiply, and divide fractions in your head? As we were working through it together, I realized that this is a common problem over at Math I/O. So I decided to make a library dedicated to this.
|
||||||
|
|
||||||
|
## <a href="#how-it-works" name="how-it-works"></a>How it works {#how-it-works}
|
||||||
|
|
||||||
|
I broke up each fraction into two things, a numerator and a denominator. With these two numbers, I can figure out all of the most common operations.
|
||||||
|
|
||||||
|
### <a href="#addition" name="addition"></a>Addition {#addition}
|
||||||
|
|
||||||
|
For addition, if two fractions have the same denominator, then you just need to add the numerators.
|
||||||
|
|
||||||
|
1/3 + 1/3 = 2/3
|
||||||
|
|
||||||
|
|
||||||
|
If not, then you need to change it to have a common denominator. We can do this by multiply each fractions numerator and denominator by the other fractions denominator.
|
||||||
|
|
||||||
|
1/2 + 1/3 = (1 * 3)/ (2 * 3) + (1 * 2)/ (2 * 3) = 3/6 + 2/6 = 5/6
|
||||||
|
|
||||||
|
|
||||||
|
### <a href="#subtraction" name="subtraction"></a>Subtraction {#subtraction}
|
||||||
|
|
||||||
|
Works the same as addition, except the second fraction is subtracted (taken away) from the first.
|
||||||
|
|
||||||
|
### <a href="#multiplication" name="multiplication"></a>Multiplication {#multiplication}
|
||||||
|
|
||||||
|
To multiply two fractions, just multiply the numerators by each other, and the denominators by each other.
|
||||||
|
|
||||||
|
2/3 * 1/2 = 2/6
|
||||||
|
|
||||||
|
|
||||||
|
### <a href="#division" name="division"></a>Division {#division}
|
||||||
|
|
||||||
|
Treated similar to multiplication since dividing a number is the same thing as multiplying by it’s [reciprocal](https://www.mathsisfun.com/reciprocal.html).
|
||||||
|
|
||||||
|
1 / (1 / 2) = 1 * 2 = 2
|
||||||
|
|
||||||
|
|
||||||
|
### <a href="#simplification" name="simplification"></a>Simplification {#simplification}
|
||||||
|
|
||||||
|
Sometimes with the operations above, it’ll produce fractions in an unsimplified form. To avoid any confusion, I created a simplify function. It was challanging trying to figure out a way to code this. While I was browsing around for an implementation, I knocked into Euclider’s algorithm for finding the greatest common factor. Straight from the [Wikipedia article](https://en.wikipedia.org/wiki/Euclidean_algorithm) (where a is greater than b):
|
||||||
|
|
||||||
|
|
||||||
|
function gcd(a, b)
|
||||||
|
while b ≠ 0
|
||||||
|
t := b;
|
||||||
|
b := a mod b;
|
||||||
|
a := t;
|
||||||
|
return a;
|
||||||
|
|
||||||
|
|
||||||
|
I can then simplify the fraction by dividing the numerator and denominator by the greatest common factor.
|
||||||
|
|
||||||
|
## <a href="#the-api" name="the-api"></a>The API {#the-api}
|
||||||
|
|
||||||
|
I decided to provide as much flexibility as I can in the API. You have several ways to create a new Fraction.
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">
|
||||||
|
var oneHalf = new Fraction(1,2);
|
||||||
|
var oneHalf = new Fraction(.5);
|
||||||
|
var oneHalf = new Fraction("1/2");
|
||||||
|
var oneHalf = new Fraction("1", "2")
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
All of these results will return a Fraction with a numerator of <code class="language-javascript">1</code> and a denominator of <code class="language-javascript">2</code>. You also have two different ways to do the most common operations.
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">
|
||||||
|
var fiveThirds = Fraction.add("1/3", "4/3");
|
||||||
|
var fiveThirds = new Fraction("1/3").add("4/3");
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
The second style came from how jQuery implements it’s library. That way you can chain operations.
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">
|
||||||
|
new Fraction("1/2").add("2/3").divide("5/6").subtract("7/8").multiply("6/5").toString()
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
Outputs <code class="language-javascript">'63/100'</code> This is accomplished in the code through [prototypes](http://javascriptissexy.com/javascript-prototype-in-plain-detailed-language/).
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">
|
||||||
|
Fraction.add = function(frac1, frac2) {
|
||||||
|
Fraction.correctArgumentLength(2, arguments.length);
|
||||||
|
frac1 = Fraction.toFraction(frac1)
|
||||||
|
frac2 = Fraction.toFraction(frac2)
|
||||||
|
|
||||||
|
var newFrac = frac1;
|
||||||
|
newFrac.numerator = frac1.numerator * frac2.denominator + frac1.denominator * frac2.numerator;
|
||||||
|
newFrac.denominator = frac1.denominator * frac2.denominator;
|
||||||
|
return Fraction.simplify(newFrac);
|
||||||
|
}
|
||||||
|
Fraction.prototype.add = function(frac) {
|
||||||
|
Fraction.correctArgumentLength(1, arguments.length);
|
||||||
|
return Fraction.change(this, Fraction.add(this, frac));
|
||||||
|
}
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
In the code, the add prototype calls the Fraction.add function within it to avoid code duplication.
|
||||||
|
|
||||||
|
## <a href="#conclusion" name="conclusion"></a>Conclusion {#conclusion}
|
||||||
|
|
||||||
|
After I coded this up, I looked online for different implementations and found [fraction.js](https://github.com/ekg/fraction.js) by [Erik Garrison](http://hypervolu.me/~erik/). It’s important to look at different implementations and see which matches your needs better. This post isn’t meant to go fully into detail of the library. To know what else the library can do, visit the [readme page](https://github.com/brandonrozek/Fractions.js/blob/master/README.md). If you’re curious in how it’s implemented, check out the [code](https://github.com/brandonrozek/Fractions.js/blob/master/Fraction.js). [Email me](mailto:hello@brandonrozek.com) if you have any questions/criticisms 🙂
|
|
@ -0,0 +1,247 @@
|
||||||
|
---
|
||||||
|
id: 449
|
||||||
|
title: Limiting the Cache in Service Workers Revisited
|
||||||
|
date: 2015-11-30T00:34:15+00:00
|
||||||
|
author: Brandon Rozek
|
||||||
|
layout: post
|
||||||
|
guid: https://brandonrozek.com/?p=449
|
||||||
|
permalink: /2015/11/limiting-cache-service-workers-revisited3/
|
||||||
|
medium_post:
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";N;s:10:"author_url";N;s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";N;s:2:"id";N;s:21:"follower_notification";N;s:7:"license";N;s:14:"publication_id";N;s:6:"status";N;s:3:"url";N;}'
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";s:74:"https://cdn-images-1.medium.com/fit/c/200/200/1*dmbNkD5D-u45r44go_cf0g.png";s:10:"author_url";s:32:"https://medium.com/@brandonrozek";s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";s:2:"no";s:2:"id";s:12:"9cc502aae12e";s:21:"follower_notification";s:2:"no";s:7:"license";s:19:"all-rights-reserved";s:14:"publication_id";s:2:"-1";s:6:"status";s:6:"public";s:3:"url";s:93:"https://medium.com/@brandonrozek/limiting-the-cache-in-service-workers-revisited-9cc502aae12e";}'
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";s:74:"https://cdn-images-1.medium.com/fit/c/200/200/1*dmbNkD5D-u45r44go_cf0g.png";s:10:"author_url";s:32:"https://medium.com/@brandonrozek";s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";s:2:"no";s:2:"id";s:12:"9cc502aae12e";s:21:"follower_notification";s:2:"no";s:7:"license";s:19:"all-rights-reserved";s:14:"publication_id";s:2:"-1";s:6:"status";s:6:"public";s:3:"url";s:93:"https://medium.com/@brandonrozek/limiting-the-cache-in-service-workers-revisited-9cc502aae12e";}'
|
||||||
|
mf2_cite:
|
||||||
|
- 'a:1:{s:6:"author";a:0:{}}'
|
||||||
|
- 'a:1:{s:6:"author";a:0:{}}'
|
||||||
|
tumblr_post_id:
|
||||||
|
- "135657767639"
|
||||||
|
- "135657767639"
|
||||||
|
tumblr_crosspostr_crosspost:
|
||||||
|
- 'N'
|
||||||
|
kind:
|
||||||
|
- article
|
||||||
|
---
|
||||||
|
Summary: I rewrote how cache limiting works to address a few problems listed later in this post. Check out the [gist](https://gist.github.com/brandonrozek/0cf038df40a913fda655) for the updated code.
|
||||||
|
|
||||||
|
<!--more-->
|
||||||
|
|
||||||
|
I wrote a function in my [previous service worker post](https://brandonrozek.com/2015/11/service-workers/) to help limit the cache. Here’s a reminder of what it looked like.
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">
|
||||||
|
var limitCache = function(cache, maxItems) {
|
||||||
|
cache.keys().then(function(items) {
|
||||||
|
if (items.length > maxItems) {
|
||||||
|
cache.delete(items[0]);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
### The Problem
|
||||||
|
|
||||||
|
Jeremy Keith updated the service worker on his site and noticed that the images has blown past the amount he allocated for it ([post](https://adactio.com/journal/9844)). Looking back at my service worker, I noticed that mine has the same shortcoming as well. So what happened? Service workers function in an asynchronous manner. Meaning it can be processing not just one, but many fetch events at the same time. This comes into conflict when there are synchronous instructions such as deleting the first item from the cache which Jeremy describes in his follow up [post](https://adactio.com/journal/9888).
|
||||||
|
|
||||||
|
### A Solution
|
||||||
|
|
||||||
|
Jeremy wrote a function to help trim the cache and asked when it would be appropriate to apply it.
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">
|
||||||
|
var trimCache = function (cacheName, maxItems) {
|
||||||
|
caches.open(cacheName)
|
||||||
|
.then(function (cache) {
|
||||||
|
cache.keys()
|
||||||
|
.then(function (keys) {
|
||||||
|
if (keys.length > maxItems) {
|
||||||
|
cache.delete(keys[0])
|
||||||
|
.then(trimCache(cacheName, maxItems));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
And that got me thinking. In what situations is this problem more likely to occur? This particular problem happens when a lot of files are being called asynchronously. This problem doesn’t occur when only one file is being loaded. So when do we load a bunch of files? During page load. During page load, the browser might request css, javascript, images, etc. Which for most [websites](http://royal.pingdom.com/2011/11/21/web-pages-getting-bloated-here-is-why/), is a lot of files. Let’s now move our focus back to the humble script.js. Before, the only role it played with service workers was registering the script. However, if we can get the script to notify the service worker when the page is done loading, then the service worker will know when to trim the cache.
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">
|
||||||
|
if ('serviceWorker' in navigator) {
|
||||||
|
navigator.serviceWorker.register('https://yourwebsite.com/serviceworker.js', {scope: '/'});
|
||||||
|
}
|
||||||
|
window.addEventListener("load", function() {
|
||||||
|
if (navigator.serviceWorker.controller != null) {
|
||||||
|
navigator.serviceWorker.controller.postMessage({"command":"trimCache"});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
Why <code class="language-javascript">if (navigator.serviceWorker.controller != null)</code>? Service Workers don’t take control of the page immediately but on subsequent page loads, Jake Archibald [explains](https://jakearchibald.com/2014/using-serviceworker-today/). When the service worker does have control of the page however, we can use the [postMessage api](https://developer.mozilla.org/en-US/docs/Web/API/Worker/postMessage) to send a message to the service worker. Inside, I provided a json with a “command” to “trimCache”. Since we send the json to the service worker, we need to make sure that it can receive it.
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">
|
||||||
|
self.addEventListener("message", function(event) {
|
||||||
|
var data = event.data;
|
||||||
|
|
||||||
|
if (data.command == "trimCache") {
|
||||||
|
trimCache(version + "pages", 25);
|
||||||
|
trimCache(version + "images", 10);
|
||||||
|
trimCache(version + "assets", 30);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
Once it receives the command, it goes on to trim all of the caches.
|
||||||
|
|
||||||
|
### Conclusion
|
||||||
|
|
||||||
|
So whenever you download a bunch of files, make sure to run <code class="language-javascript">navigator.serviceWorker.controller.postMessage({"command":"trimCache"});</code> on the main javascript file to trim the cache. A downside to this method is that since Service Workers don’t take control during the first page load, the cache isn’t trimmed until the second page load. If you can find a way to make it so that this event happens in the first page load [tell me](mailto:hello@brandonrozek.com) about it/write a blog post. 🙂 **Update:** To get the service worker to take control of the page immediately call [self.skipWaiting()](https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerGlobalScope/skipWaiting) after the install event and [self.clients.claim()](https://developer.mozilla.org/en-US/docs/Web/API/Clients/claim) after the activate event. Current code for our humble service worker:
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">
|
||||||
|
var version = 'v2.0.24:';
|
||||||
|
|
||||||
|
var offlineFundamentals = [
|
||||||
|
'/',
|
||||||
|
'/offline/'
|
||||||
|
];
|
||||||
|
|
||||||
|
//Add core website files to cache during serviceworker installation
|
||||||
|
var updateStaticCache = function() {
|
||||||
|
return caches.open(version + 'fundamentals').then(function(cache) {
|
||||||
|
return Promise.all(offlineFundamentals.map(function(value) {
|
||||||
|
var request = new Request(value);
|
||||||
|
var url = new URL(request.url);
|
||||||
|
if (url.origin != location.origin) {
|
||||||
|
request = new Request(value, {mode: 'no-cors'});
|
||||||
|
}
|
||||||
|
return fetch(request).then(function(response) {
|
||||||
|
var cachedCopy = response.clone();
|
||||||
|
return cache.put(request, cachedCopy);
|
||||||
|
|
||||||
|
});
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
//Clear caches with a different version number
|
||||||
|
var clearOldCaches = function() {
|
||||||
|
return caches.keys().then(function(keys) {
|
||||||
|
return Promise.all(
|
||||||
|
keys
|
||||||
|
.filter(function (key) {
|
||||||
|
return key.indexOf(version) != 0;
|
||||||
|
})
|
||||||
|
.map(function (key) {
|
||||||
|
return caches.delete(key);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
trims the cache
|
||||||
|
If cache has more than maxItems then it removes the excess items starting from the beginning
|
||||||
|
*/
|
||||||
|
var trimCache = function (cacheName, maxItems) {
|
||||||
|
caches.open(cacheName)
|
||||||
|
.then(function (cache) {
|
||||||
|
cache.keys()
|
||||||
|
.then(function (keys) {
|
||||||
|
if (keys.length > maxItems) {
|
||||||
|
cache.delete(keys[0])
|
||||||
|
.then(trimCache(cacheName, maxItems));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//When the service worker is first added to a computer
|
||||||
|
self.addEventListener("install", function(event) {
|
||||||
|
event.waitUntil(updateStaticCache()
|
||||||
|
.then(function() {
|
||||||
|
return self.skipWaiting();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
})
|
||||||
|
|
||||||
|
self.addEventListener("message", function(event) {
|
||||||
|
var data = event.data;
|
||||||
|
|
||||||
|
//Send this command whenever many files are downloaded (ex: a page load)
|
||||||
|
if (data.command == "trimCache") {
|
||||||
|
trimCache(version + "pages", 25);
|
||||||
|
trimCache(version + "images", 10);
|
||||||
|
trimCache(version + "assets", 30);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//Service worker handles networking
|
||||||
|
self.addEventListener("fetch", function(event) {
|
||||||
|
|
||||||
|
//Fetch from network and cache
|
||||||
|
var fetchFromNetwork = function(response) {
|
||||||
|
var cacheCopy = response.clone();
|
||||||
|
if (event.request.headers.get('Accept').indexOf('text/html') != -1) {
|
||||||
|
caches.open(version + 'pages').then(function(cache) {
|
||||||
|
cache.put(event.request, cacheCopy);
|
||||||
|
});
|
||||||
|
} else if (event.request.headers.get('Accept').indexOf('image') != -1) {
|
||||||
|
caches.open(version + 'images').then(function(cache) {
|
||||||
|
cache.put(event.request, cacheCopy);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
caches.open(version + 'assets').then(function add(cache) {
|
||||||
|
cache.put(event.request, cacheCopy);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Fetch from network failed
|
||||||
|
var fallback = function() {
|
||||||
|
if (event.request.headers.get('Accept').indexOf('text/html') != -1) {
|
||||||
|
return caches.match(event.request).then(function (response) {
|
||||||
|
return response || caches.match('/offline/');
|
||||||
|
})
|
||||||
|
} else if (event.request.headers.get('Accept').indexOf('image') != -1) {
|
||||||
|
return new Response('Offlineoffline', { headers: { 'Content-Type': 'image/svg+xml' }});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//This service worker won't touch non-get requests
|
||||||
|
if (event.request.method != 'GET') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//For HTML requests, look for file in network, then cache if network fails.
|
||||||
|
if (event.request.headers.get('Accept').indexOf('text/html') != -1) {
|
||||||
|
event.respondWith(fetch(event.request).then(fetchFromNetwork, fallback));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//For non-HTML requests, look for file in cache, then network if no cache exists.
|
||||||
|
event.respondWith(
|
||||||
|
caches.match(event.request).then(function(cached) {
|
||||||
|
return cached || fetch(event.request).then(fetchFromNetwork, fallback);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
//After the install event
|
||||||
|
self.addEventListener("activate", function(event) {
|
||||||
|
event.waitUntil(clearOldCaches()
|
||||||
|
.then(function() {
|
||||||
|
return self.clients.claim();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">
|
||||||
|
if ('serviceWorker' in navigator) {
|
||||||
|
navigator.serviceWorker.register('https://brandonrozek.com/serviceworker.js', {scope: '/'});
|
||||||
|
}
|
||||||
|
window.addEventListener("load", function() {
|
||||||
|
if (navigator.serviceWorker.controller != null) {
|
||||||
|
navigator.serviceWorker.controller.postMessage({"command":"trimCache"});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</code></pre>
|
48
content/blog/2015-12-22-playing-with-qr-codes.md
Normal file
48
content/blog/2015-12-22-playing-with-qr-codes.md
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
---
|
||||||
|
id: 572
|
||||||
|
title: Playing with QR Codes
|
||||||
|
date: 2015-12-22T15:13:44+00:00
|
||||||
|
author: Brandon Rozek
|
||||||
|
layout: post
|
||||||
|
guid: https://brandonrozek.com/?p=572
|
||||||
|
permalink: /2015/12/playing-with-qr-codes/
|
||||||
|
medium_post:
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";N;s:10:"author_url";N;s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";N;s:2:"id";N;s:21:"follower_notification";N;s:7:"license";N;s:14:"publication_id";N;s:6:"status";N;s:3:"url";N;}'
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";N;s:10:"author_url";N;s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";N;s:2:"id";N;s:21:"follower_notification";N;s:7:"license";N;s:14:"publication_id";N;s:6:"status";N;s:3:"url";N;}'
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";N;s:10:"author_url";N;s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";N;s:2:"id";N;s:21:"follower_notification";N;s:7:"license";N;s:14:"publication_id";N;s:6:"status";N;s:3:"url";N;}'
|
||||||
|
mf2_cite:
|
||||||
|
- 'a:1:{s:6:"author";a:0:{}}'
|
||||||
|
- 'a:1:{s:6:"author";a:0:{}}'
|
||||||
|
tumblr_post_id:
|
||||||
|
- "135727537264"
|
||||||
|
- "135727537264"
|
||||||
|
bridgy_syndication:
|
||||||
|
- |
|
||||||
|
|
||||||
|
https://twitter.com/B_RozekJournal/status/790337750280970241
|
||||||
|
kind:
|
||||||
|
- article
|
||||||
|
---
|
||||||
|
Looking at Aaron Parecki’s [“Fun with QR Codes”](https://aaronparecki.com/articles/2015/10/05/1/fun-with-qr-codes) inspired me to play with QR Codes myself. Using the tool [QArt Coder](http://research.swtch.com/qr/draw) made by [Russ Cox](https://plus.google.com/116810148281701144465), I made a QR Code with my face in it!
|
||||||
|
|
||||||
|
<!--more-->
|
||||||
|
|
||||||
|
<img class="alignnone size-medium wp-image-579" src="https://brandonrozek.com/wp-content/uploads/2016/10/qrcode-large-1.png" alt="qrcode-large" width="300" height="300" />
|
||||||
|
|
||||||
|
### Why QR Codes?
|
||||||
|
|
||||||
|
QR Codes aren’t prevalent in the United States of America. In fact, they’re often viewed by technologists as a [joke](http://picturesofpeoplescanningqrcodes.tumblr.com/). But as [Christina Xu](http://www.christinaxu.org/) points out on her Medium post [“Pictures of Chinese People Scanning QR Codes”](https://medium.com/chrysaora-weekly/pictures-of-chinese-people-scanning-qr-codes-a564047ec58f), it’s not a joke everywhere. In fact, many people actually depend on this humble little QR code to get by in their day. Another story, my family and I go out to eat sometimes. Occasionally, we’ll knock into a Heinz Ketchup bottle.
|
||||||
|
|
||||||
|
<div id="attachment_573" style="width: 235px" class="wp-caption aligncenter">
|
||||||
|
<img aria-describedby="caption-attachment-573" class="size-medium wp-image-573" src="https://brandonrozek.com/wp-content/uploads/2016/10/heinz-2-768x1024.jpg" alt="Picture of Heinz ketchup bottle with QR Code." width="225" height="300" />
|
||||||
|
|
||||||
|
<p id="caption-attachment-573" class="wp-caption-text">
|
||||||
|
Picture by <a href="http://azadzahoory.com/2014/07/03/when-the-product-becomes-an-ad/">Azad Zahoory</a>
|
||||||
|
</p>
|
||||||
|
</div> My brother would get so excited whenever he saw this. This little ketchup bottle meant that we got to play a
|
||||||
|
|
||||||
|
<a href="http://www.heinztablegames.com/game_selector.html" rel="nofollow" class="broken_link">trivia game</a>. No matter what you think of QR Codes, vanity or not, there are people who use it every day. So have some fun and make yourself a QR Code of your own. You know, just in case 😉
|
||||||
|
|
||||||
|
### Setting up the QR Code
|
||||||
|
|
||||||
|
First, you need a picture. I used a picture of myself. Then for the QArt Coder site to work correctly, the image needs to be in high-contrast. Meaning, it needs to be mostly black and white. To accomplish this, fire up [Gimp](https://www.gimp.org/) (or your image editor of choice), and hit Color -> Threshold. That will give you a black and white version of the picture where you can then play around with the slider to get it just how you want it. After that, open up [QArt Coder](http://research.swtch.com/qr/draw) and upload the image onto the site. Once that’s accomplished, you might need to play with the QR Size, Image Size, Rotation, and other options. After that, hit “Save this QR Code” and viola! You got yourself three different sizes of QR codes to use. What should you do with the QR Code after you get it? First, brag to your friends about how cool it looks. Then maybe put it on your [business card](https://www.webdesignerdepot.com/2011/07/30-creative-qr-code-business-cards/). If you can think of any other <del>excuses</del> uses of QR Codes [contact me](mailto:hello@brandonrozek.com) 🙂
|
324
content/blog/2015-12-27-creating-vcards-from-h-cards.md
Normal file
324
content/blog/2015-12-27-creating-vcards-from-h-cards.md
Normal file
|
@ -0,0 +1,324 @@
|
||||||
|
---
|
||||||
|
id: 599
|
||||||
|
title: Creating vCards from h-cards
|
||||||
|
date: 2015-12-27T15:17:12+00:00
|
||||||
|
author: Brandon Rozek
|
||||||
|
layout: post
|
||||||
|
guid: https://brandonrozek.com/?p=599
|
||||||
|
permalink: /2015/12/creating-vcards-from-h-cards/
|
||||||
|
medium_post:
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";N;s:10:"author_url";N;s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";N;s:2:"id";N;s:21:"follower_notification";N;s:7:"license";N;s:14:"publication_id";N;s:6:"status";N;s:3:"url";N;}'
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";s:74:"https://cdn-images-1.medium.com/fit/c/200/200/1*dmbNkD5D-u45r44go_cf0g.png";s:10:"author_url";s:32:"https://medium.com/@brandonrozek";s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";s:2:"no";s:2:"id";s:12:"9eab6bd8e0e4";s:21:"follower_notification";s:3:"yes";s:7:"license";s:19:"all-rights-reserved";s:14:"publication_id";s:2:"-1";s:6:"status";s:6:"public";s:3:"url";s:74:"https://medium.com/@brandonrozek/creating-vcards-from-h-cards-9eab6bd8e0e4";}'
|
||||||
|
mf2_cite:
|
||||||
|
- 'a:1:{s:6:"author";a:0:{}}'
|
||||||
|
tumblr_post_id:
|
||||||
|
- "136059699334"
|
||||||
|
kind:
|
||||||
|
- article
|
||||||
|
---
|
||||||
|
Microformats is semantic HTML used to convey metadata. Using an userscript, I can generate a vCard from the representative h-card of the page. The code for this is on this gist [here.](https://gist.github.com/brandonrozek/e0153b2733e947fa9c87)
|
||||||
|
|
||||||
|
<!--more-->
|
||||||
|
|
||||||
|
### Terminology
|
||||||
|
|
||||||
|
[Microformats](http://microformats.org/wiki/Main_Page) allows search engines, browsers, websites, or people like me to consume content on a site.
|
||||||
|
|
||||||
|
[H-card](http://microformats.org/wiki/h-card) is a type of microformat that serves as a contact card for people and organizations.
|
||||||
|
|
||||||
|
[vCard](https://en.wikipedia.org/wiki/VCard) is the standard for electronic business cards. They’re most likely used in your phone to store contacts.
|
||||||
|
|
||||||
|
Userscript is essentially JavaScript that runs in the [Greasemonkey](http://www.greasespot.net/) extension.
|
||||||
|
|
||||||
|
### What I’ll need
|
||||||
|
|
||||||
|
* Microformat parser
|
||||||
|
* Way to find the representative h-card
|
||||||
|
* Way to generate a vCard from an h-card
|
||||||
|
* The userscript itself
|
||||||
|
|
||||||
|
### Implementation
|
||||||
|
|
||||||
|
To keep everything in small [reusable components](https://en.wikipedia.org/wiki/Modular_programming), I created four different sections. Thankfully, [Glenn Jones](http://glennjones.net/) already wrote a JavaScript microformats parser called [microformat-shiv.](https://github.com/glennjones/microformat-shiv) It’s licensed with [MIT](https://tldrlegal.com/license/mit-license), so we can use it, yay!
|
||||||
|
|
||||||
|
Next, I need to find the representative h-card of the page. Following the [instructions](http://microformats.org/wiki/representative-h-card-parsing) on the microformats wiki, I wrote the following code.
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">
|
||||||
|
/*
|
||||||
|
representative-h-card - v0.1.0
|
||||||
|
Copyright (c) 2015 Brandon Rozek
|
||||||
|
Licensed MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
Finds the representative h-card of the page
|
||||||
|
[http://microformats.org/wiki/representative-h-card-parsing]
|
||||||
|
@returns representative h-card if found, null otherwise
|
||||||
|
**/
|
||||||
|
var representativeHCard = function(hCards, url) {
|
||||||
|
if (hCards.items.length == 0) {
|
||||||
|
return null;
|
||||||
|
} else if (hCards.items.length == 1 && urlsMatchURL(hCards.items[0], url)) {
|
||||||
|
hCard = hCards;
|
||||||
|
hCard.items = [hCards.items[0]];
|
||||||
|
return hCard
|
||||||
|
} else {
|
||||||
|
for (var i = 0; i < hCards.items.length; i++) {
|
||||||
|
if (urlsMatchURL(hCards.items[i], url) && (uidsMatchURL(hCards.items[i], url) || relMeMatchURL(hCards, url))) {
|
||||||
|
hCard = hCards;
|
||||||
|
hCard.items = [hCards.items[i]];
|
||||||
|
return hCard
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var urlsMatchURL = function(hCard, url) {
|
||||||
|
var urls = hCard.properties.url;
|
||||||
|
if (typeof(urls) == "object") {
|
||||||
|
for (var i = 0; i < urls.length; i++) {
|
||||||
|
if (new URL(urls[i]).toString() == new URL(url).toString()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var uidsMatchURL = function(hCard, url) {
|
||||||
|
var uids = hCard.properties.uid;
|
||||||
|
if (typeof(uids) == "object") {
|
||||||
|
for (var i = 0; i < uids.length; i++) {
|
||||||
|
if (new URL(uids[i]).toString() == new URL(url).toString()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
var relMeMatchURL = function(microformats, url) {
|
||||||
|
var me = microformats.rels.me;
|
||||||
|
if (typeof(me) == "object") {
|
||||||
|
for (var i = 0; i < me.length; i++) {
|
||||||
|
if (new URL(me[i]).toString() == new URL(url).toString()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
Next up, is making the vCard. For this, I had to look at the [vCard 4.0 specification](https://tools.ietf.org/html/rfc6350) to figure out what the property names and values are. Then I browsed around different <a href="http://indieweb.thatmustbe.us/" rel="nofollow">sites</a> (takes you to a random [Indieweb](https://indiewebcamp.com/) site) to figure out which properties are the most common.
|
||||||
|
|
||||||
|
The properties I ended up adding to the vCard.
|
||||||
|
|
||||||
|
* name
|
||||||
|
* photo
|
||||||
|
* telephone numbers
|
||||||
|
* URLs
|
||||||
|
* [IMPPs](https://en.wikipedia.org/wiki/Instant_Messaging_and_Presence_Protocol) (Instant Messaging and Presence Protocol)
|
||||||
|
* emails
|
||||||
|
* roles
|
||||||
|
* categories
|
||||||
|
* notes
|
||||||
|
|
||||||
|
As I was browsing around, I noticed that a few people would have empty values for certain properties on their h-card. To avoid having this show up on the vCard, I added a filter that takes out empty strings.
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">
|
||||||
|
/*
|
||||||
|
vCard-from-h-card - v0.1.0
|
||||||
|
Copyright (c) 2015 Brandon Rozek
|
||||||
|
Licensed MIT
|
||||||
|
*/
|
||||||
|
var makeVCard = function(hCard) {
|
||||||
|
var vCard = "BEGIN:VCARDnVERSION:4.0n";
|
||||||
|
|
||||||
|
//Add full name
|
||||||
|
var name = hCard.items[0].properties.name;
|
||||||
|
if (typeof(name) == "object") {
|
||||||
|
name.removeEmptyStrings();
|
||||||
|
for (var i = 0; i < name.length; i++) {
|
||||||
|
vCard += "FN: " + name[i] + "n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Add photo
|
||||||
|
var photo = hCard.items[0].properties.photo;
|
||||||
|
if (typeof(photo) == "object") {
|
||||||
|
photo.removeEmptyStrings();
|
||||||
|
for (var i = 0; i < photo.length; i++) {
|
||||||
|
vCard += "PHOTO: " + photo[i] + "n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Add phone number
|
||||||
|
var tel = hCard.items[0].properties.tel;
|
||||||
|
if (typeof(tel) == "object") {
|
||||||
|
tel.removeEmptyStrings();
|
||||||
|
for (var i = 0; i < tel.length; i++) {
|
||||||
|
try {
|
||||||
|
if (new URL(tel[i]).schema == "sms:") {
|
||||||
|
vCard += "TEL;TYPE=text;VALUE=text: " + new URL(tel[i]).pathname + "n";
|
||||||
|
} else {
|
||||||
|
vCard += "TEL;TYPE=voice;VALUE=text: " + new URL(tel[i]).pathname + "n";
|
||||||
|
}
|
||||||
|
} catch(e) {
|
||||||
|
vCard += "TEL;TYPE=voice;VALUE=text: " + tel[i] + "n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Add URLs
|
||||||
|
var url = hCard.items[0].properties.url;
|
||||||
|
if (typeof(url) == "object") {
|
||||||
|
url.removeEmptyStrings();
|
||||||
|
for (var i = 0; i < url.length; i++) {
|
||||||
|
vCard += "URL: " + url[i] + "n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var impp = hCard.items[0].properties.impp;
|
||||||
|
//Add IMPP (Instant Messaging and Presence Protocol)
|
||||||
|
if (typeof(impp) == "object") {
|
||||||
|
impp.removeEmptyStrings();
|
||||||
|
for (var i = 0; i < impp.length; i++) {
|
||||||
|
vCard += "IMPP;PREF=" + (i + 1) + ": " + impp[i] + "n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Add emails
|
||||||
|
var email = hCard.items[0].properties.email;
|
||||||
|
if (typeof(email) == "object") {
|
||||||
|
email.removeEmptyStrings();
|
||||||
|
for (var i = 0; i < email.length; i++) {
|
||||||
|
try {
|
||||||
|
vCard += "EMAIL: " + new URL(email[i]).pathname + "n";
|
||||||
|
} catch (e) {
|
||||||
|
vCard += "EMAIL: " + email[i] + "n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Add roles
|
||||||
|
var role = hCard.items[0].properties.role;
|
||||||
|
if (typeof(role) == "object") {
|
||||||
|
role.removeEmptyStrings();
|
||||||
|
for (var i = 0; i < role.length; i++) {
|
||||||
|
vCard += "ROLE: " + role[i] + "n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Add Organizations
|
||||||
|
var org = hCard.items[0].properties.org;
|
||||||
|
if (typeof(org) == "object") {
|
||||||
|
org.removeEmptyStrings();
|
||||||
|
for (var i = 0; i < org.length; i++) {
|
||||||
|
vCard += "ORG: " + org[i] + "n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Add Categories
|
||||||
|
var category = hCard.items[0].properties.category;
|
||||||
|
if (typeof(category) == "object") {
|
||||||
|
vCard += "CATEGORIES: " + category.removeEmptyStrings().join(",") + "n";
|
||||||
|
}
|
||||||
|
|
||||||
|
//Add notes
|
||||||
|
var note = hCard.items[0].properties.note;
|
||||||
|
if (typeof(note) == "object") {
|
||||||
|
note.removeEmptyStrings();
|
||||||
|
for (var i = 0; i < note.length; i++) {
|
||||||
|
vCard += "NOTE: " + note[i] + "n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return vCard + "END:VCARD";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Array.prototype.removeEmptyStrings = function() {
|
||||||
|
return this.filter(function(i) { return i !== "" })
|
||||||
|
}
|
||||||
|
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
Now for the final part, making the userscript. Inspired by [Ryan Barret](https://snarfed.org/) and his userscript [Let’s Talk,](https://github.com/snarfed/misc/blob/master/userscripts/lets_talk.user.js) this userscript brings all of the above modules together. First it grabs the microformats from the page using microformat-shiv.
|
||||||
|
|
||||||
|
For some reason, when I tried filtering it by ‘h-card’ it froze my computer. So I wrote my own little filter instead.
|
||||||
|
|
||||||
|
After I grab the representative h-card from the page using the little module I wrote, I generated a vCard. With the vCard generated, I set up a little HTML and CSS to display the link in the top left corner of the screen.
|
||||||
|
|
||||||
|
The link is actually a [data uri](https://developer.mozilla.org/en-US/docs/Web/HTTP/data_URIs) that has all of the information of the vCard encoded in it. Depending on your browser, once you click the link you might have to hit CTRL-S to save.
|
||||||
|
|
||||||
|
<pre><code class="language-javascript">
|
||||||
|
/*
|
||||||
|
show-vCard - v0.1.0
|
||||||
|
Copyright (c) 2015 Brandon Rozek
|
||||||
|
Licensed MIT
|
||||||
|
*/
|
||||||
|
var filterMicroformats = function(items, filter) {
|
||||||
|
var newItems = [];
|
||||||
|
for (var i = 0; i < items.items.length; i++) {
|
||||||
|
for (var k = 0; k < items.items[i].type.length; k++) {
|
||||||
|
if (filter.indexOf(items.items[i].type[k]) != -1) {
|
||||||
|
newItems.push(items.items[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
items.items = newItems;
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
var render = function() {
|
||||||
|
var hCards = filterMicroformats(Microformats.get(), ['h-card']);
|
||||||
|
var person = representativeHCard(hCards, location.origin);
|
||||||
|
if (person == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var node = document.createElement("div");
|
||||||
|
node.setAttribute("class", "lt");
|
||||||
|
|
||||||
|
var link = "<a href="text/vcf;base64," + btoa(makeVCard(person))+ "" target="_blank">vCard</a>";
|
||||||
|
var style = "
|
||||||
|
.lt {
|
||||||
|
position: absolute;
|
||||||
|
left: 24px;
|
||||||
|
top: 0;
|
||||||
|
color: #DDD;
|
||||||
|
background-color: #FFD700;
|
||||||
|
z-index: 9999;
|
||||||
|
border-width: medium 1px 1px;
|
||||||
|
border-style: none solid solid;
|
||||||
|
border-color: #DDD #C7A900 #9E8600;
|
||||||
|
box-shadow: 0px 1px rgba(0, 0, 0, 0.1), 0px 1px 2px rgba(0, 0, 0, 0.1), 0px 1px rgba(255, 255, 255, 0.34) inset;
|
||||||
|
border-radius: 0px 0px 4px 4px;
|
||||||
|
}
|
||||||
|
.lt a {
|
||||||
|
padding: .5rem;
|
||||||
|
color: #8f6900;
|
||||||
|
text-shadow: 0px 1px #FFE770;
|
||||||
|
border: medium none;
|
||||||
|
}
|
||||||
|
";
|
||||||
|
|
||||||
|
node.innerHTML = link + style;
|
||||||
|
document.body.appendChild(node);
|
||||||
|
}
|
||||||
|
document.addEventListener("DOMContentLoaded", function() {
|
||||||
|
render();
|
||||||
|
});
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
Sadly, I have no way of controlling the file name when you save it so you’ll have to manually rename it to something more meaningful than a random string of characters. Also remember to add the extension ‘.vcf’ for it to be recognized by some devices.
|
||||||
|
|
||||||
|
### Conclusion
|
||||||
|
|
||||||
|
Fire up your favorite userscript handling tool and add the [script](https://gist.github.com/brandonrozek/e0153b2733e947fa9c87) in! Of course, since it’s pure JavaScript, you can also add it to your own site to serve the same purpose.
|
||||||
|
|
||||||
|
I ran into a small problem loading a contact onto my Android 5.0.2 phone. Apparently, they don’t support vCard 4.0 yet so I had to go into the file and change the line that says “VERSION 4.0” to “VERSION 3.0” which then allowed me to import the file into my contacts.
|
||||||
|
|
||||||
|
As with all the code I write, feel free to comment/criticize. I love hearing feedback so if you spot anything, [contact me](mailto:hello@brandonrozek.com) 🙂
|
||||||
|
|
||||||
|
Also posted on [IndieNews](http://news.indiewebcamp.com/en){.u-syndication}
|
94
content/blog/2016-08-16-pass-password-manager.md
Normal file
94
content/blog/2016-08-16-pass-password-manager.md
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
---
|
||||||
|
id: 919
|
||||||
|
title: Pass the password manager
|
||||||
|
date: 2016-08-16T23:37:09+00:00
|
||||||
|
author: Brandon Rozek
|
||||||
|
layout: post
|
||||||
|
guid: http://brandonrozek.com/?p=919
|
||||||
|
permalink: /2016/08/pass-password-manager/
|
||||||
|
medium_post:
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";N;s:10:"author_url";N;s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";s:2:"no";s:2:"id";N;s:21:"follower_notification";s:3:"yes";s:7:"license";s:19:"all-rights-reserved";s:14:"publication_id";s:2:"-1";s:6:"status";s:4:"none";s:3:"url";N;}'
|
||||||
|
mf2_cite:
|
||||||
|
- 'a:4:{s:9:"published";s:25:"0000-01-01T00:00:00+00:00";s:7:"updated";s:25:"0000-01-01T00:00:00+00:00";s:8:"category";a:1:{i:0;s:0:"";}s:6:"author";a:0:{}}'
|
||||||
|
tumblr_post_id:
|
||||||
|
- "149056904199"
|
||||||
|
bridgy_syndication:
|
||||||
|
- https://twitter.com/B_RozekJournal/status/790336614866100224
|
||||||
|
tumblr_crosspostr_crosspost:
|
||||||
|
- 'N'
|
||||||
|
mf2_syndicate-to:
|
||||||
|
- 'a:1:{i:0;s:4:"none";}'
|
||||||
|
kind:
|
||||||
|
- note
|
||||||
|
---
|
||||||
|
Looking over the list of programs installed on my computer, one of my all time favorites is a program called [Pass](https://www.passwordstore.org/). It’s a program available under most Linux distributions, Mac OS X (through [Brew](http://brew.sh/)), [Windows](https://github.com/mbos/Pass4Win), [Android](https://play.google.com/store/apps/details?id=com.zeapo.pwdstore), and [iOS](https://github.com/davidjb/pass-ios#readme). It stores all of the passwords using gpg encryption and stores them as actual files on the hard disk. Meaning if you wanted, you can sync them to all your devices!
|
||||||
|
|
||||||
|
This program assumes a light familiarity with the terminal
|
||||||
|
|
||||||
|
<!--more-->
|
||||||
|
|
||||||
|
## Setup {#setup}
|
||||||
|
|
||||||
|
First if you do not already have a gpg key, [create one](http://wooledge.org/~greg/crypto/node41.html).
|
||||||
|
|
||||||
|
Then in the terminal type in
|
||||||
|
|
||||||
|
pass init youremail@address
|
||||||
|
|
||||||
|
Substituting youremail@address with the email associated with your gpg key.
|
||||||
|
|
||||||
|
This will autmatically create an empty repository under your home folder in a folder labeled `.pass`
|
||||||
|
|
||||||
|
If you are switching from an existing password manager, check to see if on the [Pass homepage](https://www.passwordstore.org/), there doesn’t exist a script to help you out
|
||||||
|
|
||||||
|
## Basic Tasks {#basic-tasks}
|
||||||
|
|
||||||
|
To insert a password into pass
|
||||||
|
|
||||||
|
pass insert password-name
|
||||||
|
|
||||||
|
It will then prompt you to enter the password
|
||||||
|
|
||||||
|
To show passwords you have already inserted
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
To show an individual password
|
||||||
|
|
||||||
|
pass password-name
|
||||||
|
|
||||||
|
But generally I find it handy to have it automatically in my clipboard. You can do that with the -c option
|
||||||
|
|
||||||
|
pass -c password-name
|
||||||
|
|
||||||
|
You can generate new secure passwords using pass. (-c copies the result into your clipboard)
|
||||||
|
|
||||||
|
pass generate -c password-name password-length
|
||||||
|
|
||||||
|
If you don’t want it to output symbols, use the -n option to restrict it to alphanumericals
|
||||||
|
|
||||||
|
pass generate -n -c password-name password-length
|
||||||
|
|
||||||
|
Another command i find handy is the `find` command. Mainly because I have over a 100 passwords in this system and i tend to forget what I named some of them
|
||||||
|
|
||||||
|
pass find search-string
|
||||||
|
|
||||||
|
There are too many commands to list them all, but if you ever want to find out more, check out the manual entry page for `pass`
|
||||||
|
|
||||||
|
man pass
|
||||||
|
|
||||||
|
## Extra: Syncing {#sync}
|
||||||
|
|
||||||
|
I use a nextCloud instance on my server to sync my passwords, but I don’t see a reason why this wouldn’t work with other projects like dropbox, syncthing, or any other sync solution
|
||||||
|
|
||||||
|
Some sync solutions don’t like to sync folders that begin with a ‘.’, my solution around this is to create a symbolic link between that and a folder you wish to link it to
|
||||||
|
|
||||||
|
ln -s /path/to/home/folder/.password-store /path/to/sync/folder/password-store
|
||||||
|
|
||||||
|
Then you just need to make sure to make the same link to all your other computers
|
||||||
|
|
||||||
|
## Conclusion {#conclusion}
|
||||||
|
|
||||||
|
I like Pass for it’s ease of use and for the fact that I’m not tied into any one company for managing my passwords. It’s based on open source tools and the fact I didn’t have to configure a database is a huge plus for me
|
||||||
|
|
||||||
|
If you’re in a need of a password manager (I hope you have more than one password), then give pass a shot. It served me and my many passwords well.
|
29
content/blog/2017-03-07-knit-document-rstudio.md
Normal file
29
content/blog/2017-03-07-knit-document-rstudio.md
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
---
|
||||||
|
id: 2090
|
||||||
|
title: Knit a Document in RStudio
|
||||||
|
date: 2017-03-07T04:29:50+00:00
|
||||||
|
author: Brandon Rozek
|
||||||
|
layout: post
|
||||||
|
guid: https://brandonrozek.com/?p=2090
|
||||||
|
permalink: /2017/03/knit-document-rstudio/
|
||||||
|
medium_post:
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";N;s:10:"author_url";N;s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";N;s:2:"id";N;s:21:"follower_notification";N;s:7:"license";N;s:14:"publication_id";N;s:6:"status";N;s:3:"url";N;}'
|
||||||
|
mf2_syndicate-to:
|
||||||
|
- 'a:1:{i:0;s:4:"none";}'
|
||||||
|
mf2_cite:
|
||||||
|
- 'a:4:{s:9:"published";s:25:"0000-01-01T00:00:00+00:00";s:7:"updated";s:25:"0000-01-01T00:00:00+00:00";s:8:"category";a:1:{i:0;s:0:"";}s:6:"author";a:0:{}}'
|
||||||
|
tumblr_post_id:
|
||||||
|
- "158095811719"
|
||||||
|
format: aside
|
||||||
|
kind:
|
||||||
|
- note
|
||||||
|
---
|
||||||
|
In case you were wondering how I got all the R code and output for the [“Do females live longer than males?”](https://brandonrozek.com/portfolio/male-vs-female-life-expectancy/) page. There is actually a function in RStudio that allows one to compile a report based on code and it’s output.
|
||||||
|
|
||||||
|
<!--more-->
|
||||||
|
|
||||||
|
First go to File->Knit Document. If this is your first time, then it will install RMarkdown, a dependency this tool needs to compile the report.
|
||||||
|
|
||||||
|
Once that is downloaded, it will let you choose between three different file formats (HTML, PDF, MS Word). For the purposes of blog posts, I like to output it in HTML so I can copy and paste the code. But for personal use, I like using PDFs
|
||||||
|
|
||||||
|
After you select the file format, hit compile, and voila! A nice neat compiled report is created for you. Here is a [pdf](https://brandonrozek.com/wp-content/uploads/2017/03/LifeExpectancy.pdf) example of the report I made.
|
132
content/blog/2017-03-07-uniformity-math-random.md
Normal file
132
content/blog/2017-03-07-uniformity-math-random.md
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
---
|
||||||
|
id: 2095
|
||||||
|
title: Uniformity of Math.random()
|
||||||
|
date: 2017-03-07T21:50:52+00:00
|
||||||
|
author: Brandon Rozek
|
||||||
|
layout: post
|
||||||
|
guid: https://brandonrozek.com/?p=2095
|
||||||
|
permalink: /2017/03/uniformity-math-random/
|
||||||
|
medium_post:
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";N;s:10:"author_url";N;s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";N;s:2:"id";N;s:21:"follower_notification";N;s:7:"license";N;s:14:"publication_id";N;s:6:"status";N;s:3:"url";N;}'
|
||||||
|
mf2_syndicate-to:
|
||||||
|
- 'a:1:{i:0;s:4:"none";}'
|
||||||
|
mf2_cite:
|
||||||
|
- 'a:4:{s:9:"published";s:25:"0000-01-01T00:00:00+00:00";s:7:"updated";s:25:"0000-01-01T00:00:00+00:00";s:8:"category";a:1:{i:0;s:0:"";}s:6:"author";a:0:{}}'
|
||||||
|
tumblr_post_id:
|
||||||
|
- "158123669889"
|
||||||
|
format: aside
|
||||||
|
kind:
|
||||||
|
- note
|
||||||
|
---
|
||||||
|
There are many cases where websites use random number generators to influence some sort of page behavior. One test to ensure the quality of a random number generator is to see if after many cases, the numbers produced follow a uniform distribution.
|
||||||
|
|
||||||
|
<!--more-->
|
||||||
|
|
||||||
|
Today, I will compare Internet Explorer 11, Chrome, and Firefox on a Windows 7 machine and report my results.
|
||||||
|
|
||||||
|
## Hypothesis
|
||||||
|
|
||||||
|
H0: The random numbers outputted follow the uniform distribution
|
||||||
|
|
||||||
|
HA: The random numbers outputted do not follow the uniform distribution
|
||||||
|
|
||||||
|
## Gathering Data
|
||||||
|
|
||||||
|
I wrote a small [website](http://share.zeropointshift.com/files/2017/03/random.html) and obtained my data by getting the CSV outputted when I use IE11, Firefox, and Chrome.
|
||||||
|
|
||||||
|
The website works by producing a random number using <code class='language-javascript'>Math.random()</code> between 1 and 1000 inclusive and calls the function 1,000,000 times. Storing it’s results in a file
|
||||||
|
|
||||||
|
This website produces a file with all the numbers separated by a comma. We want these commas to be replaced by newlines. To do so, we can run a simple command in the terminal
|
||||||
|
|
||||||
|
<pre class='language-bash'><code class='language-bash'>
|
||||||
|
grep -oE '[0-9]+' Random.csv > Random_corrected.csv
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
Do this with all three files and make sure to keep track of which is which.
|
||||||
|
|
||||||
|
Here are a copy of my files for [Firefox](https://brandonrozek.com/wp-content/uploads/2017/03/Firefox_corrected.csv), [Chrome](https://brandonrozek.com/wp-content/uploads/2017/03/Chrome_corrected-1.csv), and [IE11](https://brandonrozek.com/wp-content/uploads/2017/03/IE11_corrected.csv)
|
||||||
|
|
||||||
|
## Check Conditions
|
||||||
|
|
||||||
|
Since we’re interested in if the random values occur uniformly, we need to perform a Chi-Square test for Goodness of Fit. With every test comes some assumptions
|
||||||
|
|
||||||
|
<u>Counted Data Condition:</u> The data can be converted from quantatative to count data.
|
||||||
|
|
||||||
|
<u>Independence Assumption:</u> One random value does not affect another.
|
||||||
|
|
||||||
|
<u>Expected Cell Frequency Condition:</u> The expected counts are going to be 10000
|
||||||
|
|
||||||
|
Since all of the conditions are met, we can use the Chi-square test of Goodness of Fit
|
||||||
|
|
||||||
|
## Descriptive Statistics
|
||||||
|
|
||||||
|
For the rest of the article, we will use R for analysis. Looking at the histograms for the three browsers below. The random numbers all appear to occur uniformly
|
||||||
|
|
||||||
|
<pre class="language-R"><code class='language-R'>rm(list=ls())
|
||||||
|
chrome = read.csv("~/Chrome_corrected.csv", header = F)
|
||||||
|
firefox = read.csv("~/Firefox_corrected.csv", header = F)
|
||||||
|
ie11 = read.csv("~/IE11_corrected.csv", header = F)
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
<pre class="language-R"><code class='language-R'>
|
||||||
|
hist(ie11$V1, main = "Distribution of Random Values for IE11", xlab = "Random Value")</code></pre>
|
||||||
|
|
||||||
|
![](https://brandonrozek.com/wp-content/uploads/2017/03/ie11hist.png)
|
||||||
|
|
||||||
|
<pre class="language-R"><code class='language-R'>hist(firefox$V1, main = "Distribution of Random Values for Firefox", xlab = "Random Value")</code></pre>
|
||||||
|
|
||||||
|
![](https://brandonrozek.com/wp-content/uploads/2017/03/firefoxhist.png)
|
||||||
|
|
||||||
|
<pre class="language-R"><code class='language-R'>hist(chrome$V1, main = "Distribution of Random Values for Chrome", xlab = "Random Value")</code></pre>
|
||||||
|
|
||||||
|
![](https://brandonrozek.com/wp-content/uploads/2017/03/chromehist.png)
|
||||||
|
|
||||||
|
## Chi-Square Test
|
||||||
|
|
||||||
|
Before we run our test, we need to convert the quantatative data to count data by using the plyr package
|
||||||
|
|
||||||
|
<pre class="language-R"><code class='language-R'>#Transform to count data
|
||||||
|
library(plyr)
|
||||||
|
chrome_count = count(chrome)
|
||||||
|
firefox_count = count(firefox)
|
||||||
|
ie11_count = count(ie11)
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
Run the tests
|
||||||
|
|
||||||
|
<pre class='language-R'><code class='language-R'>
|
||||||
|
# Chi-Square Test for Goodness-of-Fit
|
||||||
|
chrome_test = chisq.test(chrome_count$freq)
|
||||||
|
firefox_test = chisq.test(firefox_count$freq)
|
||||||
|
ie11_test = chisq.test(ie11_count$freq)
|
||||||
|
|
||||||
|
# Test results
|
||||||
|
chrome_test</code></pre>
|
||||||
|
|
||||||
|
As you can see in the test results below, we fail to reject the null hypothesis at a 5% significance level because all of the p-values are above 0.05.
|
||||||
|
|
||||||
|
##
|
||||||
|
## Chi-squared test for given probabilities
|
||||||
|
##
|
||||||
|
## data: chrome_count$freq
|
||||||
|
## X-squared = 101.67, df = 99, p-value = 0.4069
|
||||||
|
|
||||||
|
<pre class="r"><code>firefox_test</code></pre>
|
||||||
|
|
||||||
|
##
|
||||||
|
## Chi-squared test for given probabilities
|
||||||
|
##
|
||||||
|
## data: firefox_count$freq
|
||||||
|
## X-squared = 105.15, df = 99, p-value = 0.3172
|
||||||
|
|
||||||
|
<pre class="r"><code>ie11_test</code></pre>
|
||||||
|
|
||||||
|
##
|
||||||
|
## Chi-squared test for given probabilities
|
||||||
|
##
|
||||||
|
## data: ie11_count$freq
|
||||||
|
## X-squared = 78.285, df = 99, p-value = 0.9384
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
At a 5% significance level, we fail to obtain enough evidence to suggest that the distribution of random number is not uniform. This is a good thing since it shows us that our random number generators give all numbers an equal chance of being represented. We can use <code class='language-javascript'>Math.random()</code> with ease of mind.
|
59
content/blog/2017-03-09-simplifying-expressions-octave.md
Normal file
59
content/blog/2017-03-09-simplifying-expressions-octave.md
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
---
|
||||||
|
id: 2115
|
||||||
|
title: Simplifying Expressions with Octave
|
||||||
|
date: 2017-03-09T02:09:58+00:00
|
||||||
|
author: Brandon Rozek
|
||||||
|
layout: post
|
||||||
|
guid: https://brandonrozek.com/?p=2115
|
||||||
|
permalink: /2017/03/simplifying-expressions-octave/
|
||||||
|
medium_post:
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";N;s:10:"author_url";N;s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";N;s:2:"id";N;s:21:"follower_notification";N;s:7:"license";N;s:14:"publication_id";N;s:6:"status";N;s:3:"url";N;}'
|
||||||
|
mf2_syndicate-to:
|
||||||
|
- 'a:1:{i:0;s:22:"bridgy-publish_twitter";}'
|
||||||
|
mf2_cite:
|
||||||
|
- 'a:4:{s:9:"published";s:25:"0000-01-01T00:00:00+00:00";s:7:"updated";s:25:"0000-01-01T00:00:00+00:00";s:8:"category";a:1:{i:0;s:0:"";}s:6:"author";a:0:{}}'
|
||||||
|
tumblr_post_id:
|
||||||
|
- "158172999969"
|
||||||
|
mf2_syndication:
|
||||||
|
- 'a:1:{i:0;s:60:"https://twitter.com/B_RozekJournal/status/839659534146801665";}'
|
||||||
|
format: aside
|
||||||
|
kind:
|
||||||
|
- note
|
||||||
|
---
|
||||||
|
Octave is a high level programming language intended for numerical computations. One of the cool features of this is that with symbolic expressions, you can then simplify mathematical expressions.
|
||||||
|
|
||||||
|
<!--more-->
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
First install [Octave](https://www.gnu.org/software/octave/) and the [symbolic package](https://octave.sourceforge.io/symbolic/) using the website or your package manager of choice.
|
||||||
|
|
||||||
|
Then in octave type in the following code
|
||||||
|
|
||||||
|
|
||||||
|
pkg load symbolic
|
||||||
|
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
For every variable not defined earlier in your expression, make sure to declare it as a symbolic data type
|
||||||
|
|
||||||
|
<pre class='language-MATLAB'><code class='language-MATLAB'>syms x y</code></pre>
|
||||||
|
|
||||||
|
Then make an expression
|
||||||
|
|
||||||
|
<pre class='language-MATLAB'><code class='language-MATLAB'>
|
||||||
|
expr = y + sin(x)^2 + cos(x)^2
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
You can then ask Octave to simplify the expression for you
|
||||||
|
|
||||||
|
<pre class='language-MATLAB'><code class='language-MATLAB'>
|
||||||
|
simp_expr = simplify(expr)
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
Displaying it shows it as
|
||||||
|
|
||||||
|
<pre class='language-MATLAB'><code class='language-MATLAB'>(sym) y + 1</code></pre>
|
||||||
|
|
||||||
|
Which is indeed a simplification using a trig identity 🙂
|
125
content/blog/2017-03-14-monte-carlo-pi.md
Normal file
125
content/blog/2017-03-14-monte-carlo-pi.md
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
---
|
||||||
|
id: 2089
|
||||||
|
title: Monte Carlo Pi
|
||||||
|
date: 2017-03-14T05:31:21+00:00
|
||||||
|
author: Brandon Rozek
|
||||||
|
layout: post
|
||||||
|
guid: https://brandonrozek.com/?p=2089
|
||||||
|
permalink: /2017/03/monte-carlo-pi/
|
||||||
|
medium_post:
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";N;s:10:"author_url";N;s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";N;s:2:"id";N;s:21:"follower_notification";N;s:7:"license";N;s:14:"publication_id";N;s:6:"status";N;s:3:"url";N;}'
|
||||||
|
tumblr_post_id:
|
||||||
|
- "158383170019"
|
||||||
|
mf2_syndicate-to:
|
||||||
|
- 'a:1:{i:0;s:22:"bridgy-publish_twitter";}'
|
||||||
|
mf2_cite:
|
||||||
|
- 'a:4:{s:9:"published";s:25:"0000-01-01T00:00:00+00:00";s:7:"updated";s:25:"0000-01-01T00:00:00+00:00";s:8:"category";a:1:{i:0;s:0:"";}s:6:"author";a:0:{}}'
|
||||||
|
mf2_syndication:
|
||||||
|
- 'a:1:{i:0;s:60:"https://twitter.com/B_RozekJournal/status/841522141346570244";}'
|
||||||
|
format: aside
|
||||||
|
kind:
|
||||||
|
- note
|
||||||
|
---
|
||||||
|
Using Monte Carlo methods, we can create a simulation that approximates pi. In this post, we will go over the math behind the approximation and the code.
|
||||||
|
|
||||||
|
<!--more-->
|
||||||
|
|
||||||
|
## The Math
|
||||||
|
|
||||||
|
Pi is a mathematical constant consisting of the ratio between the circumfrence of a circle and it’s diameter.
|
||||||
|
|
||||||
|
The circumfrence of the circle is defined to be $$ C = 2\pi r$$ while the diameter of the circle is $$d = 2r$$
|
||||||
|
|
||||||
|
Take the ratio between the two and you get $$\frac{2\pi r}{2r} = \pi$$
|
||||||
|
|
||||||
|
Now let us consider the area of a circle. One can derive the area of a circle by taking the integral of the circumfrence with respect to it’s radius $$ A_{circle} = \int{(2\pi r) dr} = \pi r^2 $$
|
||||||
|
|
||||||
|
Let us simplify the formula more by setting the radius equal to one. $$A_{circle} = \pi$$
|
||||||
|
|
||||||
|
Now consider only the first quadrant of the circle. Since our circle is centered at the origin and all the points on the circumfrence is equidistant from the center, our area is now $$A_{circle} = \frac{1}{4} \pi$$
|
||||||
|
|
||||||
|
And bound the quarter-circle in a 1×1 box with an area of $$A_{square} = 1^2 = 1$$
|
||||||
|
|
||||||
|
Notice that the ratio between the circle and the square is a quarter of pi $$\frac{A\_{circle}}{A\_{square}} = \frac{\frac{1}{4} \pi}{1} = \frac{1}{4} \pi$$
|
||||||
|
|
||||||
|
## Simulation and Statisitcs
|
||||||
|
|
||||||
|
The formula for a circle centered at the origin with radius one is $$x^2 + y^2 = 1$$
|
||||||
|
|
||||||
|
Let us focus again on the first quadrent, and do a Monte Carlo simulation to find the area of the quarter-circle
|
||||||
|
|
||||||
|
![](https://brandonrozek.com/wp-content/uploads/2017/03/circlefilled.png)
|
||||||
|
|
||||||
|
We can do this by what is called the dart board method. We generate a random x and y between 0 and 1. If it satisfies the inequality $$x^2 + y^2 \leq 1$$ then it counts as being inside the circle, if not then it lies outside the circle.
|
||||||
|
|
||||||
|
That point will count as an really small area. The point will always be inside the square but may sometimes be inside the circle. Running the simulations a large number of times allows us to add up all the tiny little areas that make up the circle and the square.
|
||||||
|
|
||||||
|
To add up these small areas we need to make an assumption. The assumption is that the variance of all the little Monte Carlo trials are the same. Since we are using a psuedo-random number generator, it is safe to assume it is met.
|
||||||
|
|
||||||
|
This will allow us to perform a pooled empiricle probability on the simulations to sum up the areas.
|
||||||
|
|
||||||
|
Meaning the area of the circle will be the number of times that the inequality was satisfied $$A_{circle} = \# Successes$$
|
||||||
|
|
||||||
|
And the area of the square will be the number of times the simulation was run, since the random numbers generated will always be between 0 and 1 $$A_{square} = \# Trials$$
|
||||||
|
|
||||||
|
Recall that taking the ratio of the area of the circle and the area of the square is a fourth of pi. $$\frac{\frac{1}{4} \pi}{1} = \frac{1}{4} \pi$$
|
||||||
|
|
||||||
|
Multiply this number by 4 and you get the value for pi.
|
||||||
|
|
||||||
|
This tells us that four times the probability that the randomly generated point is in the circle is equal to pi.
|
||||||
|
|
||||||
|
$$\pi = 4 \* (Probability\ of\ being\ inside\ circle) = 4 \* \frac{\# Success}{\# Trials} = 4 * \frac{A\_{circle}}{A\_{square}}$$
|
||||||
|
|
||||||
|
## Implementation
|
||||||
|
|
||||||
|
For the Monte Carlo simulation I used Java. The BigDecimal implementation was used so that there wouldn’t be any issue with integer size limits
|
||||||
|
|
||||||
|
<pre class='language-java'><code class='language-java'>/** Calculates Pi
|
||||||
|
* @author Brandon Rozek
|
||||||
|
*/
|
||||||
|
// Big Integers are used so we don't run into the integer size limit
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
class MonteCarloPi {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
|
||||||
|
BigInteger successes = BigInteger.ZERO;
|
||||||
|
BigInteger trials = BigInteger.ZERO;
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
For this simulation, we will run 1,000,000,000 trials
|
||||||
|
|
||||||
|
<pre class='language-java'><code class='language-java'> BigInteger numTrials = new BigInteger("1000000000");
|
||||||
|
/*
|
||||||
|
Monte Carlo Simulation
|
||||||
|
Generate a random point 0 <= x < 1 and 0 <= y < 1
|
||||||
|
If the generated point satisfies x^2 + x^2 < 1
|
||||||
|
Count as a success
|
||||||
|
Keep track of the number of trials and successes
|
||||||
|
*/
|
||||||
|
for (; trials.compareTo(numTrials) < 0; trials = trials.add(BigInteger.ONE)) {
|
||||||
|
double randomX = Math.random();
|
||||||
|
double randomY = Math.random();
|
||||||
|
if (Math.pow(randomX, 2) + Math.pow(randomY, 2) < 1) {
|
||||||
|
successes = successes.add(BigInteger.ONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
And then we finalize it with a quick calculation of pi
|
||||||
|
|
||||||
|
<pre class='language=java'><code class='language-java'>// (Number of successes) / (Number of trials) * 4 gives the approximation for pi
|
||||||
|
BigDecimal pi = new BigDecimal(successes)
|
||||||
|
.divide(new BigDecimal(trials))
|
||||||
|
.multiply(new BigDecimal("4"));
|
||||||
|
System.out.println("The calculated value of pi is: " + pi);
|
||||||
|
}}
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
We found an approximation of pi using the Monte Carlo methods! I find that really awesome, however, there are some concerns I have with this approach.
|
||||||
|
|
||||||
|
1) We don’t keep track of double counting. One possible solution for this is increasing the radius and bounding box appropriately so that the probability of double counting is low.
|
||||||
|
|
||||||
|
2) Speed. The more trials you ask it to run, the longer it takes to perform all of the simulations. One possible way around this is to write a parrallel version of this code. That’s possible because of the equal variance that we spoke of earlier. Pooling the successses and trials will still result in a good approximation.
|
74
content/blog/2017-05-24-viewing-java-applets.md
Normal file
74
content/blog/2017-05-24-viewing-java-applets.md
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
---
|
||||||
|
id: 2174
|
||||||
|
title: Viewing Java Applets
|
||||||
|
date: 2017-05-24T15:59:45+00:00
|
||||||
|
author: Brandon Rozek
|
||||||
|
layout: post
|
||||||
|
guid: https://brandonrozek.com/?p=2174
|
||||||
|
permalink: /2017/05/viewing-java-applets/
|
||||||
|
medium_post:
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";N;s:10:"author_url";N;s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";N;s:2:"id";N;s:21:"follower_notification";N;s:7:"license";N;s:14:"publication_id";N;s:6:"status";N;s:3:"url";N;}'
|
||||||
|
mf2_syndicate-to:
|
||||||
|
- 'a:1:{i:0;s:22:"bridgy-publish_twitter";}'
|
||||||
|
mf2_cite:
|
||||||
|
- 'a:4:{s:9:"published";s:25:"0000-01-01T00:00:00+00:00";s:7:"updated";s:25:"0000-01-01T00:00:00+00:00";s:8:"category";a:1:{i:0;s:0:"";}s:6:"author";a:0:{}}'
|
||||||
|
tumblr_post_id:
|
||||||
|
- "161024360884"
|
||||||
|
mf2_syndication:
|
||||||
|
- 'a:1:{i:0;s:60:"https://twitter.com/B_RozekJournal/status/867409810932760576";}'
|
||||||
|
kind:
|
||||||
|
- article
|
||||||
|
---
|
||||||
|
When you use an IDE there are many things you can take for granted. A section of an intro level computer science course at my university uses [JGrasp](http://www.jgrasp.org/) to build Java Applets.
|
||||||
|
|
||||||
|
Following around using a normal text editor, I found that I couldn’t just compile and run the code like I have with my java programs in the past. To be able to help around and assist in the course, I need to be able to build and run these applications. The rest of this article describes the process I underwent to be able to use my existing setup to write and build java applets. Of course you can always install JGrasp and have that all built in, but it’s always nice to not have to change your workflow.
|
||||||
|
|
||||||
|
<!--more-->
|
||||||
|
|
||||||
|
When I tried following along, I would receive the following error
|
||||||
|
|
||||||
|
Main method not found in class HelloWorld, please define main method as...
|
||||||
|
|
||||||
|
Which makes sense since I have never defined a main method inside my source code. So how do I go about doing this?
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
Java Applets are meant to run on web pages, because of this one needs an html file to host the applet. The following code gives you the bare minimum for setting up the html file. I called it `HelloWorld.html`.
|
||||||
|
|
||||||
|
<pre class='language-html'><code class='language-html'>
|
||||||
|
<html>
|
||||||
|
<head><title>Applet Container<title>
|
||||||
|
<body>
|
||||||
|
<applet code='HelloWorld.class' width=400 height=400></applet>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
## Hello World Program
|
||||||
|
|
||||||
|
To get it up and running, I will show a “Hello World” like application for applets.
|
||||||
|
|
||||||
|
<pre class='language-java'><code class='language-java'>
|
||||||
|
import javax.swing.JApplet;
|
||||||
|
import java.awt.Graphics;
|
||||||
|
|
||||||
|
public class HelloWorld extends JApplet {
|
||||||
|
public void paint(Graphics g) {
|
||||||
|
g.drawString("Hello World", 30, 30);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
## Running the Applet
|
||||||
|
|
||||||
|
Now we need to compile the code
|
||||||
|
|
||||||
|
<pre class='langauge-bash'><code class='language-bash'>javac HelloWorld.java</code></pre>
|
||||||
|
|
||||||
|
Then run the appletviewer
|
||||||
|
|
||||||
|
<pre class='language-bash'><code class='language-bash'>appletviewer HelloWorld.html</code></pre>
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
This tutorial concludes the setup of running a simple Java applet. From here you can look at the different methods in the [Graphics library](https://docs.oracle.com/javase/7/docs/api/java/awt/Graphics.html) and play around 😀
|
205
content/blog/2017-06-05-java-swing-components.md
Normal file
205
content/blog/2017-06-05-java-swing-components.md
Normal file
|
@ -0,0 +1,205 @@
|
||||||
|
---
|
||||||
|
id: 2198
|
||||||
|
title: Java Swing Components
|
||||||
|
date: 2017-06-05T23:30:18+00:00
|
||||||
|
author: Brandon Rozek
|
||||||
|
layout: post
|
||||||
|
guid: https://brandonrozek.com/?p=2198
|
||||||
|
permalink: /2017/06/java-swing-components/
|
||||||
|
medium_post:
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";N;s:10:"author_url";N;s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";N;s:2:"id";N;s:21:"follower_notification";N;s:7:"license";N;s:14:"publication_id";N;s:6:"status";N;s:3:"url";N;}'
|
||||||
|
mf2_syndicate-to:
|
||||||
|
- 'a:1:{i:0;s:4:"none";}'
|
||||||
|
mf2_cite:
|
||||||
|
- 'a:4:{s:9:"published";s:25:"0000-01-01T00:00:00+00:00";s:7:"updated";s:25:"0000-01-01T00:00:00+00:00";s:8:"category";a:1:{i:0;s:0:"";}s:6:"author";a:0:{}}'
|
||||||
|
tumblr_post_id:
|
||||||
|
- "161484582559"
|
||||||
|
kind:
|
||||||
|
- article
|
||||||
|
---
|
||||||
|
This post, over time, will serve as a reference to myself and others of the different UI components available in the Swing library. This post assumes a general familiarity with setting up a basic Swing application and focuses only on the individual components.
|
||||||
|
|
||||||
|
<!--more-->
|
||||||
|
|
||||||
|
### Buttons
|
||||||
|
|
||||||
|
Buttons are created using the JButton component. The constructor takes the text placed inside the button.
|
||||||
|
|
||||||
|
<pre class='language-java'><code class='language-java'>
|
||||||
|
JButton stopBtn = new JButton("Stop");
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
<img src="https://brandonrozek.com/wp-content/uploads/2017/06/stopbutton.png" alt="" width="67" height="25" class="alignnone size-full wp-image-2211" />
|
||||||
|
|
||||||
|
You can also add images inside a button. To do that you need to get the image and make it into an icon. The following code grabs the image file “smallpanda.jpg” from the current working directory.
|
||||||
|
|
||||||
|
<pre class='langauge-java'><code class='language-java'>
|
||||||
|
Image img = this.getImage(this.getCodeBase(), "smallpanda.jpg");
|
||||||
|
ImageIcon imgIcon = new ImageIcon(img);
|
||||||
|
JButton feedBtn = new JButton("Feed", imgIcon);
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
Sometimes, you want to change the location of the text in the button. Like say, we want to place the text in the center horizontally and bottom vertically.
|
||||||
|
|
||||||
|
<pre class='language-java'><code class='language-java'>
|
||||||
|
feedBtn.setHorizontalTextPosition(JButton.CENTER);
|
||||||
|
feedBtn.setVerticalTextPosition(JButton.BOTTOM);
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
Don’t forget to add your buttons to the screen!
|
||||||
|
|
||||||
|
<pre class='language-java'><code class='language-java'>
|
||||||
|
this.add(stopBtn);
|
||||||
|
this.add(feedBtn);
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
<img src="https://brandonrozek.com/wp-content/uploads/2017/06/smallpandabutton.png" alt="" width="234" height="181" class="alignnone size-full wp-image-2210" />
|
||||||
|
|
||||||
|
### Labels and Textfields
|
||||||
|
|
||||||
|
One of the most common forms of input is a text field, usually distinguished with a label. Those components are called JTextField and JLabel respectively. The constructor for JTextArea can take just the width of the text field, or another common use is to have already inputed text and its width.
|
||||||
|
|
||||||
|
<pre class='language-java'><code class='language-java'>
|
||||||
|
JLabel nameLabel = new JLabel("Enter in your name: ");
|
||||||
|
|
||||||
|
// Create an input and set the width to be 10px wide
|
||||||
|
JTextField nameInput = new JTextField(10);
|
||||||
|
//Override nameInput with a field already contains the text "Brandon"
|
||||||
|
//And is 10px wide
|
||||||
|
nameInput = new JTextField("Brandon", 10);
|
||||||
|
|
||||||
|
this.add(nameLabel);
|
||||||
|
this.add(nameInput);
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
<img src="https://brandonrozek.com/wp-content/uploads/2017/06/labeltextfield.png" alt="" width="274" height="24" class="alignnone size-full wp-image-2209" />
|
||||||
|
|
||||||
|
### Checkboxes
|
||||||
|
|
||||||
|
Checkboxes are commonly used when giving the possibility for multiple answers. Such as, check all of the foods that you like.
|
||||||
|
|
||||||
|
<pre class='language-java'><code class='language-java'>
|
||||||
|
JCheckBox pizza = new JCheckBox("Pizza");
|
||||||
|
JCheckBox noodles = new JCheckBox("Noodles");
|
||||||
|
JCheckBox rice = new JCheckBox("Rice");
|
||||||
|
this.add(pizza);
|
||||||
|
this.add(noodles);
|
||||||
|
this.add(rice);
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
<img src="https://brandonrozek.com/wp-content/uploads/2017/06/checkboxes.png" alt="" width="206" height="40" class="alignnone size-full wp-image-2207" />
|
||||||
|
|
||||||
|
You can even replace the default look of the checkbox with an image. To do this, you need to make image icons for both when it’s checked and when it’s unchecked.
|
||||||
|
|
||||||
|
<pre class='language-java'><code class='language-java'>
|
||||||
|
Image checkedImage = this.getImage(this.getCodeBase(), "checked.png");
|
||||||
|
Image uncheckedImage = this.getImage(this.getCodeBase(), "unchecked.png");
|
||||||
|
|
||||||
|
ImageIcon checkedIcon = new ImageIcon(checkedImage);
|
||||||
|
ImageIcon uncheckedIcon = new ImageIcon(uncheckedImage);
|
||||||
|
|
||||||
|
JCheckBox checkbox = new JCheckBox("Check Me", uncheckedIcon);
|
||||||
|
checkbox.setSelectedIcon(checkedIcon);
|
||||||
|
|
||||||
|
this.add(checkbox);
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
<img src="https://brandonrozek.com/wp-content/uploads/2017/06/unchecked.png" alt="" width="187" height="123" class="alignnone size-full wp-image-2213" />
|
||||||
|
<img src="https://brandonrozek.com/wp-content/uploads/2017/06/checked.png" alt="" width="186" height="102" class="alignnone size-full wp-image-2208" />
|
||||||
|
|
||||||
|
### Text Areas
|
||||||
|
|
||||||
|
Text Areas are different from text fields in which it is made to be able to hold multiple lines of text. It’s called JTextArea and its construction takes a width and height as it’s arguments.
|
||||||
|
|
||||||
|
<pre class='language-java'><code class='language-java'>
|
||||||
|
JTextArea textarea = new JTextArea(10, 10);
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
By default, when the someone inputs more text than the size can hold, it will automatically grow with the text inputted. To override this behaviour and instead introuduce scroll bars. One must define a ScrollPane and put the TextArea inside of it by using it as the scroll pane’s argument for its constructor.
|
||||||
|
|
||||||
|
<pre class='language-java'><code class='language-java'>
|
||||||
|
JScrollPane scrollPane = new JScrollPane(textarea);
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
<img src="https://brandonrozek.com/wp-content/uploads/2017/06/textarea.png" alt="" width="119" height="149" class="alignnone size-full wp-image-2212" />
|
||||||
|
|
||||||
|
### Radio Buttons
|
||||||
|
|
||||||
|
Radio buttons are used for when you only want one out of many different options to be selected. For this, one needs to define a button group that houses the radio buttons for the user to choose from. This can be achieved with ButtonGroup and JRadioButton respectively.
|
||||||
|
|
||||||
|
<pre class='language-java'><code class='language-java'>
|
||||||
|
// Make the radio buttons
|
||||||
|
JRadioButton radio1 = new JRadioButton("Pies");
|
||||||
|
JRadioButton radio2 = new JRadioButton("Cakes");
|
||||||
|
JRadioButton radio3 = new JRadioButton("Cookies");
|
||||||
|
|
||||||
|
// Put the radio buttons in a group
|
||||||
|
Button Group desserts = new ButtonGroup();
|
||||||
|
desserts.add(radio1);
|
||||||
|
desserts.add(radio2);
|
||||||
|
desserts.add(radio3);
|
||||||
|
|
||||||
|
// Add the radio buttons to the screen
|
||||||
|
this.add(radio1);
|
||||||
|
this.add(radio2);
|
||||||
|
this.add(radio3);
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
<img src="https://brandonrozek.com/wp-content/uploads/2017/06/radiobuttons.png" alt="" width="211" height="34" class="alignnone size-full wp-image-2218" />
|
||||||
|
|
||||||
|
### JList
|
||||||
|
|
||||||
|
To display a list of items that are clickable by the user, you can use a `JList`. JLists require a model that stores the list implementation, we’ll use `DefaultListModel` to achieve this purpose.
|
||||||
|
|
||||||
|
<pre class='language-java'><code class='language-java'>
|
||||||
|
DefaultListModel model = new DefaultListModel();
|
||||||
|
JList list = new JList(model);
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
To add scrolling capabilities, remember to add it to a scroll pane
|
||||||
|
|
||||||
|
<pre class='language-java'><code class='language-java'>
|
||||||
|
JScollPane sp = new JScrollPane(list);
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
You can set the number of items you wish to see in the list. The example below, allows us to see three items in the list.
|
||||||
|
|
||||||
|
<pre class='language-java'><code class='language-java'>
|
||||||
|
list.setVisibleRowCount(3);
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
There are a variety of ways to add items to the list. If a number is specified that tells it to place it at the index specified. Starting from the top at zero, to the button.
|
||||||
|
|
||||||
|
<pre class='language-java'><code class='language-java'>
|
||||||
|
model.addElement("Apples")
|
||||||
|
model.addElement("Cherries");
|
||||||
|
model.addElement("Bananas");
|
||||||
|
// Adds 'Oranges' to the top
|
||||||
|
model.add(0, "Oranges");
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
Sometimes, you want to only let the user select one item. At the end, don’t forget to add the component to the screen!
|
||||||
|
|
||||||
|
<pre class='language-java'><code class='language-java'>
|
||||||
|
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||||
|
this.add(sp);
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
<img src="https://brandonrozek.com/wp-content/uploads/2017/06/JList.png" alt="" width="77" height="53" class="alignnone size-full wp-image-2224" />
|
||||||
|
|
||||||
|
### JComboBox
|
||||||
|
|
||||||
|
To create a dropdown list of different options, consider using a JComboBox.
|
||||||
|
|
||||||
|
<pre class='language-java'><code class='language-java'>
|
||||||
|
JComboBox cb = new JComboBox();
|
||||||
|
cb.addItem("Select Food Option");
|
||||||
|
cb.addItem("Pizza");
|
||||||
|
cb.addItem("Burger");
|
||||||
|
cb.addItem("Hot Dog");
|
||||||
|
cb.addItem("Steak");
|
||||||
|
// Add it to the screen
|
||||||
|
this.add(cb);
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
<img src="https://brandonrozek.com/wp-content/uploads/2017/06/JComboBox.png" alt="" width="153" height="24" class="alignnone size-full wp-image-2223" srcset="https://brandonrozek.com/wp-content/uploads/2017/06/JComboBox.png 153w, https://brandonrozek.com/wp-content/uploads/2017/06/JComboBox-150x24.png 150w" sizes="(max-width: 153px) 100vw, 153px" />
|
45
content/blog/2017-06-05-using-system-themes-java-swing.md
Normal file
45
content/blog/2017-06-05-using-system-themes-java-swing.md
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
---
|
||||||
|
id: 2192
|
||||||
|
title: Using System Themes In Java Swing
|
||||||
|
date: 2017-06-05T20:36:22+00:00
|
||||||
|
author: Brandon Rozek
|
||||||
|
layout: post
|
||||||
|
guid: https://brandonrozek.com/?p=2192
|
||||||
|
permalink: /2017/06/using-system-themes-java-swing/
|
||||||
|
medium_post:
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";N;s:10:"author_url";N;s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";N;s:2:"id";N;s:21:"follower_notification";N;s:7:"license";N;s:14:"publication_id";N;s:6:"status";N;s:3:"url";N;}'
|
||||||
|
mf2_syndicate-to:
|
||||||
|
- 'a:1:{i:0;s:4:"none";}'
|
||||||
|
mf2_cite:
|
||||||
|
- 'a:4:{s:9:"published";s:25:"0000-01-01T00:00:00+00:00";s:7:"updated";s:25:"0000-01-01T00:00:00+00:00";s:8:"category";a:1:{i:0;s:0:"";}s:6:"author";a:0:{}}'
|
||||||
|
tumblr_post_id:
|
||||||
|
- "161478693279"
|
||||||
|
mf2_syndication:
|
||||||
|
- 'a:1:{i:0;s:60:"https://twitter.com/B_RozekJournal/status/871828083459936257";}'
|
||||||
|
kind:
|
||||||
|
- article
|
||||||
|
---
|
||||||
|
The default theme for Java Swing components is a cross-platform theme called “Metal”. I use the Adapta theme for GTK on Linux and this theme does not match at all what my other GUI applications look like. So here, I will describe a simple way to utlize already existent system themes in Java Swing applications.
|
||||||
|
|
||||||
|
<!--more-->
|
||||||
|
|
||||||
|
### Solution
|
||||||
|
|
||||||
|
In the init method of your java application, place the following code.
|
||||||
|
|
||||||
|
<pre class='language-java'><code class='language-java'>
|
||||||
|
try {
|
||||||
|
UIManager.setLookAndFeel(UIManager
|
||||||
|
.getSystemLookAndFeelClassName());
|
||||||
|
} catch(Exception e) {}
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
Here the application will attempt to look up the system theme and set that as the default styles for the Swing components. If the lookup fails, then it will default back to the metal theme.
|
||||||
|
|
||||||
|
For more information, check out this page from [Oracle](http://docs.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html).
|
||||||
|
|
||||||
|
### Discussion
|
||||||
|
|
||||||
|
If it is so easy to set up applications that look native to each desktop environment, why not have that by default? With the cross platform metal theme, you can ensure that the style of your application is the same across all the operating systems. In this fashion, you don’t need to worry about spacing between components and have full control of the “look and feel” of your application.
|
||||||
|
|
||||||
|
Since I am used to development for the web, I don’t have strong motivation to have an application look the same on all platforms. I prefer the application to match the system theme and look like it was built for the platform that I am on. One loses partial control on the presentation of your application across different desktop environmnets, but with a strong layout, it is possible to make it look organized and integrated.
|
96
content/blog/2017-08-28-escape-sequences-java.md
Normal file
96
content/blog/2017-08-28-escape-sequences-java.md
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
---
|
||||||
|
id: 2236
|
||||||
|
title: Escape Sequences in Java
|
||||||
|
date: 2017-08-28T17:12:00+00:00
|
||||||
|
author: Brandon Rozek
|
||||||
|
layout: post
|
||||||
|
guid: https://brandonrozek.com/?p=2236
|
||||||
|
permalink: /2017/08/escape-sequences-java/
|
||||||
|
medium_post:
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";N;s:10:"author_url";N;s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";N;s:2:"id";N;s:21:"follower_notification";N;s:7:"license";N;s:14:"publication_id";N;s:6:"status";N;s:3:"url";N;}'
|
||||||
|
mf2_syndicate-to:
|
||||||
|
- 'a:1:{i:0;s:4:"none";}'
|
||||||
|
mf2_cite:
|
||||||
|
- 'a:4:{s:9:"published";s:25:"0000-01-01T00:00:00+00:00";s:7:"updated";s:25:"0000-01-01T00:00:00+00:00";s:8:"category";a:1:{i:0;s:0:"";}s:6:"author";a:0:{}}'
|
||||||
|
tumblr_post_id:
|
||||||
|
- "164716848139"
|
||||||
|
tumblr_crosspostr_crosspost:
|
||||||
|
- 'N'
|
||||||
|
kind:
|
||||||
|
- note
|
||||||
|
---
|
||||||
|
Sometimes you want to format your outputs. This is a quick cheatsheet containing the differerent escape sequences
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<table class="tg">
|
||||||
|
<tr>
|
||||||
|
<th class="tg-yw4l">
|
||||||
|
Character
|
||||||
|
</th>
|
||||||
|
|
||||||
|
<th class="tg-yw4l">
|
||||||
|
Escape Sequence
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td class="tg-yw4l">
|
||||||
|
Newline
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class="tg-yw4l">
|
||||||
|
\n
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td class="tg-yw4l">
|
||||||
|
Tab
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class="tg-yw4l">
|
||||||
|
\t
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td class="tg-yw4l">
|
||||||
|
Backspace
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class="tg-yw4l">
|
||||||
|
\b
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td class="tg-yw4l">
|
||||||
|
Double Quote
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class="tg-yw4l">
|
||||||
|
\”
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td class="tg-yw4l">
|
||||||
|
Single Quote
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class="tg-yw4l">
|
||||||
|
\’
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td class="tg-yw4l">
|
||||||
|
Backslash
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class="tg-yw4l">
|
||||||
|
\\
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
136
content/blog/2017-08-28-obtaining-command-line-input-java.md
Normal file
136
content/blog/2017-08-28-obtaining-command-line-input-java.md
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
---
|
||||||
|
id: 2241
|
||||||
|
title: Obtaining Command Line Input in Java
|
||||||
|
date: 2017-08-28T17:37:59+00:00
|
||||||
|
author: Brandon Rozek
|
||||||
|
layout: post
|
||||||
|
guid: https://brandonrozek.com/?p=2241
|
||||||
|
permalink: /2017/08/obtaining-command-line-input-java/
|
||||||
|
medium_post:
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";N;s:10:"author_url";N;s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";N;s:2:"id";N;s:21:"follower_notification";N;s:7:"license";N;s:14:"publication_id";N;s:6:"status";N;s:3:"url";N;}'
|
||||||
|
mf2_syndicate-to:
|
||||||
|
- 'a:1:{i:0;s:4:"none";}'
|
||||||
|
mf2_cite:
|
||||||
|
- 'a:4:{s:9:"published";s:25:"0000-01-01T00:00:00+00:00";s:7:"updated";s:25:"0000-01-01T00:00:00+00:00";s:8:"category";a:1:{i:0;s:0:"";}s:6:"author";a:0:{}}'
|
||||||
|
tumblr_post_id:
|
||||||
|
- "164717769554"
|
||||||
|
mf2_mp-syndicate-to:
|
||||||
|
- 'a:1:{i:0;s:4:"none";}'
|
||||||
|
kind:
|
||||||
|
- note
|
||||||
|
---
|
||||||
|
To obtain console input for your program you can use the <code class="language-java">Scanner</code> class
|
||||||
|
|
||||||
|
First import the relevant library
|
||||||
|
|
||||||
|
<pre class="language-java"><code class="language-java">
|
||||||
|
import java.util.Scanner;
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
Then create a variable to hold the <code class="language-java">Scanner</code> object
|
||||||
|
|
||||||
|
<pre class="language-java"><code class="language-java">
|
||||||
|
Scanner input;
|
||||||
|
input = new Scanner(System.in);
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
Inside the parenthesis, the <code class="language-java">Scanner</code> binds to the System input which is by default the console
|
||||||
|
|
||||||
|
The new varible <code class="language-java">input</code> now has the ability to obtain input from the console. To do so, use any of the following methods
|
||||||
|
|
||||||
|
<table class="tg">
|
||||||
|
<tr>
|
||||||
|
<th class="tg-yw4l">
|
||||||
|
Method
|
||||||
|
</th>
|
||||||
|
|
||||||
|
<th class="tg-yw4l">
|
||||||
|
What it Returns
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td class="tg-yw4l">
|
||||||
|
next()
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class="tg-yw4l">
|
||||||
|
The next space seperated string from the console
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td class="tg-yw4l">
|
||||||
|
nextInt()
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class="tg-yw4l">
|
||||||
|
An integer if it exists from the console
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td class="tg-yw4l">
|
||||||
|
nextDouble()
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class="tg-yw4l">
|
||||||
|
A double if it exists from the console
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td class="tg-yw4l">
|
||||||
|
nextFloat()
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class="tg-yw4l">
|
||||||
|
A float if it exists from the console
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td class="tg-yw4l">
|
||||||
|
nextLine()
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class="tg-yw4l">
|
||||||
|
A string up to the next newline character from the console
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td class="tg-yw4l">
|
||||||
|
hasNext()
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class="tg-yw4l">
|
||||||
|
Returns true if there is another token
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td class="tg-yw4l">
|
||||||
|
close()
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class="tg-yw4l">
|
||||||
|
Unbinds the Scanner from the console
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
Here is an example program where we get the user’s first name
|
||||||
|
|
||||||
|
<pre class="language-java"><code class="language-java">
|
||||||
|
import java.util.Scanner;
|
||||||
|
|
||||||
|
public class GetName {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
Scanner input = new Scanner(System.in);
|
||||||
|
System.out.print("Please enter your name: ");
|
||||||
|
String firstName = input.next();
|
||||||
|
System.out.println("Your first name is " + firstName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</code></pre>
|
|
@ -0,0 +1,177 @@
|
||||||
|
---
|
||||||
|
id: 2250
|
||||||
|
title: Identifying Misspelled Words in your Dataset with Hunspell
|
||||||
|
date: 2018-01-22T05:17:16+00:00
|
||||||
|
author: rozek_admin
|
||||||
|
layout: post
|
||||||
|
guid: https://brandonrozek.com/?p=2250
|
||||||
|
permalink: /2018/01/identifying-misspelled-words-dataset-hunspell/
|
||||||
|
medium_post:
|
||||||
|
- 'O:11:"Medium_Post":11:{s:16:"author_image_url";s:75:"https://cdn-images-1.medium.com/fit/c/200/200/1*06lotWcLMUnKZTN6-Th3IQ.jpeg";s:10:"author_url";s:32:"https://medium.com/@brandonrozek";s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";s:2:"no";s:2:"id";s:12:"c0ccd543b7e6";s:21:"follower_notification";s:3:"yes";s:7:"license";s:19:"all-rights-reserved";s:14:"publication_id";s:2:"-1";s:6:"status";s:6:"public";s:3:"url";s:104:"https://medium.com/@brandonrozek/identifying-misspelled-words-in-your-dataset-with-hunspell-c0ccd543b7e6";}'
|
||||||
|
mf2_mp-syndicate-to:
|
||||||
|
- 'a:1:{i:0;s:22:"bridgy-publish_twitter";}'
|
||||||
|
tumblr_post_id:
|
||||||
|
- "169988632939"
|
||||||
|
mf2_syndication:
|
||||||
|
- 'a:1:{i:0;s:60:"https://twitter.com/B_RozekJournal/status/955308388384235521";}'
|
||||||
|
kind:
|
||||||
|
- article
|
||||||
|
---
|
||||||
|
This article is based on one written by [Markus Konrad](https://datascience.blog.wzb.eu/author/markus_konrad/) at this link <a href='https://datascience.blog.wzb.eu/2016/07/13/autocorrecting-misspelled-words-in-python-using-hunspell/' target='_blank' >https://datascience.blog.wzb.eu/2016/07/13/autocorrecting-misspelled-words-in-python-using-hunspell/</a>
|
||||||
|
|
||||||
|
I assume in this article that you have hunspell and it's integration with python installed. If not, please refer to the article mention above and follow the prerequisite steps.
|
||||||
|
|
||||||
|
This article is inspired from the need to correct misspelled words in the [Dress Attributes Dataset](https://archive.ics.uci.edu/ml/datasets/Dresses_Attribute_Sales). I'll share with you my initial pitfall, and what I ended up doing instead.
|
||||||
|
|
||||||
|
### Background Information
|
||||||
|
|
||||||
|
Misspelled words are common when dealing with survey data or data where humans type in the responses manually. In the Dress Attributes Dataset this is apparent when looking at the sleeve lengths of the different dresses.
|
||||||
|
|
||||||
|
<pre><code class='language-python' lang='python'>dresses_data['SleeveLength'].value_counts()
|
||||||
|
</code></pre><figure>
|
||||||
|
|
||||||
|
| Word | Frequency |
|
||||||
|
| -------------- | --------- |
|
||||||
|
| sleevless | 223 |
|
||||||
|
| full | 97 |
|
||||||
|
| short | 96 |
|
||||||
|
| halfsleeve | 35 |
|
||||||
|
| threequarter | 17 |
|
||||||
|
| thressqatar | 10 |
|
||||||
|
| sleeveless | 5 |
|
||||||
|
| sleeevless | 3 |
|
||||||
|
| capsleeves | 3 |
|
||||||
|
| cap-sleeves | 2 |
|
||||||
|
| half | 1 |
|
||||||
|
| Petal | 1 |
|
||||||
|
| urndowncollor | 1 |
|
||||||
|
| turndowncollor | 1 |
|
||||||
|
| sleveless | 1 |
|
||||||
|
| butterfly | 1 |
|
||||||
|
| threequater | 1 |</figure>
|
||||||
|
|
||||||
|
Ouch, so many misspelled words. This is when my brain is racking up all the ways I can automate this problem away. Hence my stumbling upon Markus' post.
|
||||||
|
|
||||||
|
### Automagically Correcting Data
|
||||||
|
|
||||||
|
First, I decided to completely ignore what Markus warns in his post and automatically correct all the words in that column.
|
||||||
|
|
||||||
|
To begin the code, let's import and create an instance of the spellchecker:
|
||||||
|
|
||||||
|
<pre><code class='language-python' lang='python'>from hunspell import HunSpell
|
||||||
|
spellchecker = HunSpell('/usr/share/hunspell/en_US.dic', '/usr/share/hunspell/en_US.aff')
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
I modified his `correct_words` function so that it only corrects one word and so I can `apply` it along the `SleeveLength` column.
|
||||||
|
|
||||||
|
<pre><code class='language-python' lang='python'>def correct_word(checker, word, add_to_dict=[]):
|
||||||
|
"Takes in a hunspell object and a word and corrects the word if needed"
|
||||||
|
# Add custom words to the dictionary
|
||||||
|
for w in add_to_dict:
|
||||||
|
checker.add(w)
|
||||||
|
|
||||||
|
corrected = ""
|
||||||
|
# Check to see if it's a string
|
||||||
|
if isinstance(word, str):
|
||||||
|
# Check the spelling
|
||||||
|
ok = checker.spell(word)
|
||||||
|
if not ok:
|
||||||
|
# Grab suggestions for misspelled word
|
||||||
|
suggestions = checker.suggest(word)
|
||||||
|
if suggestions:
|
||||||
|
# Grab the best suggestion
|
||||||
|
best = suggestions[0]
|
||||||
|
corrected = best
|
||||||
|
else:
|
||||||
|
# There are no suggestions for misspelled word, return the original
|
||||||
|
corrected = word
|
||||||
|
else:
|
||||||
|
# Word is spelled correctly
|
||||||
|
corrected = word
|
||||||
|
else:
|
||||||
|
## Not a string. Return original
|
||||||
|
corrected = word
|
||||||
|
return corrected
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
Now let's apply the function over the `SleeveLength` column of the dataset:
|
||||||
|
|
||||||
|
<pre><code class='language-python' lang='python'>dresses_data['SleeveLength'] = dresses_data['SleeveLength'].apply(
|
||||||
|
lambda x: correct_word(spellchecker, x))
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
Doing so creates the following series:<figure>
|
||||||
|
|
||||||
|
| Word | Frequency |
|
||||||
|
| -------------- | --------- |
|
||||||
|
| sleeveless | 232 |
|
||||||
|
| full | 97 |
|
||||||
|
| short | 96 |
|
||||||
|
| half sleeve | 35 |
|
||||||
|
| three quarter | 17 |
|
||||||
|
| throatiness | 10 |
|
||||||
|
| cap sleeves | 3 |
|
||||||
|
| cap-sleeves | 2 |
|
||||||
|
| Petal | 1 |
|
||||||
|
| butterfly | 1 |
|
||||||
|
| turndowncollor | 1 |
|
||||||
|
| half | 1 |
|
||||||
|
| landownership | 1 |
|
||||||
|
| forequarter | 1 |</figure>
|
||||||
|
|
||||||
|
As you might be able to tell, this process didn't go as intended. `landownership` isn't even a length of a sleeve!
|
||||||
|
|
||||||
|
### Reporting Misspelled Items and Allowing User Intervention
|
||||||
|
|
||||||
|
This is when I have to remember, technology isn't perfect. Instead we should rely on ourselves to identify what the word should be correctly spelled as.
|
||||||
|
|
||||||
|
Keeping that in mind, I modified the function again to take in a list of the data, and return a dictionary that has the misspelled words as the keys and suggestions as the values represented as a list.
|
||||||
|
|
||||||
|
<pre><code class='language-python' lang='python'>def list_word_suggestions(checker, words, echo = True, add_to_dict=[]):
|
||||||
|
"Takes in a list of words and returns a dictionary with mispellt words as keys and suggestions as a list. Also prints it out"
|
||||||
|
# add custom words to the dictionary
|
||||||
|
for w in add_to_dict:
|
||||||
|
checker.add(w)
|
||||||
|
|
||||||
|
suggestions = {}
|
||||||
|
for word in words:
|
||||||
|
if isinstance(word, str):
|
||||||
|
ok = checker.spell(word)
|
||||||
|
if not ok and word not in suggestions:
|
||||||
|
suggestions[word] = checker.suggest(word)
|
||||||
|
if not suggestions[word] and echo:
|
||||||
|
print(word + ": No suggestions")
|
||||||
|
elif echo:
|
||||||
|
print(word + ": " + "[", ", ".join(repr(i) for i in suggestions[word]), "]")
|
||||||
|
return suggestions
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
With that, I can use the function on my data. To do so, I convert the pandas values to a list and pass it to the function:
|
||||||
|
|
||||||
|
<pre><code class='language-python' lang='python'>s = list_word_suggestions(spellchecker, dresses_data['SleeveLength'].values.tolist())
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
These are the suggestions it produces:
|
||||||
|
|
||||||
|
<pre><code class='language-python' lang='python'>sleevless: [ 'sleeveless', 'sleepless', 'sleeves', 'sleekness', 'sleeve', 'lossless' ]
|
||||||
|
threequarter: [ 'three quarter', 'three-quarter', 'forequarter' ]
|
||||||
|
halfsleeve: ['half sleeve', 'half-sleeve', 'sleeveless' ]
|
||||||
|
turndowncollor: No suggestions
|
||||||
|
threequater: [ 'forequarter' ]
|
||||||
|
capsleeves: [ 'cap sleeves', 'cap-sleeves', 'capsules' ]
|
||||||
|
sleeevless: [ 'sleeveless', 'sleepless', 'sleeves', 'sleekness', 'sleeve' ]
|
||||||
|
urndowncollor: [ 'landownership' ]
|
||||||
|
thressqatar: [ 'throatiness' ]
|
||||||
|
sleveless: [ 'sleeveless', 'levelness', 'valveless', 'loveless', 'sleepless' ]
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
From here, you can analyze the output and do the replacements yourself:
|
||||||
|
|
||||||
|
<pre><code class='language-python' lang='python'>dresses_data['SleeveLength'].replace('sleevless', 'sleeveless', inplace = True)
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
### What's the Benefit?
|
||||||
|
|
||||||
|
This is where you ask "What's the difference if it doesn't automatically fix my data?"
|
||||||
|
|
||||||
|
When you have large datasets, it can be hard to individually identify which items are misspelled. Using this method will allow you to have a list of all the items that are misspelled which can let you deal with it in a systematic way.
|
86
content/blog/DebuggingAndPerformance.md
Normal file
86
content/blog/DebuggingAndPerformance.md
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
---
|
||||||
|
title: "Debugging and Performance"
|
||||||
|
date: 2019-06-15T10:59:30-04:00
|
||||||
|
draft: false
|
||||||
|
---
|
||||||
|
|
||||||
|
I've come to like the GNU Debugger (GDB) and the `perf` tool recently. This post will be a short summary of the various interesting commands you can use.
|
||||||
|
|
||||||
|
## GNU Debugger
|
||||||
|
|
||||||
|
To attach gbd to an existing process do the following
|
||||||
|
|
||||||
|
```bash
|
||||||
|
gdb -p pid_of_process
|
||||||
|
```
|
||||||
|
|
||||||
|
Otherwise you can start a new application
|
||||||
|
|
||||||
|
```bash
|
||||||
|
gdb name_of_executable
|
||||||
|
```
|
||||||
|
|
||||||
|
Once it loads it will bring you into it's own `REPL` environment. Usually once this comes up I find it useful to add breakpoints to the program. You can either do it by function name or by line number.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
(gdb) break FunctionName
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
(gdb) break code.cpp:81
|
||||||
|
```
|
||||||
|
|
||||||
|
If you just started a new application you can begin running the program with whatever arguments you wish
|
||||||
|
|
||||||
|
```bash
|
||||||
|
(gdb) run -arg1 -arg2
|
||||||
|
```
|
||||||
|
|
||||||
|
If you have attached to a process then you can continue its execution.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
(gdb) continue
|
||||||
|
```
|
||||||
|
|
||||||
|
### Breakpoints
|
||||||
|
|
||||||
|
If you have set a breakpoint, it will stop the processes' execution when it lands on the breakpoint. From here, we can take a look at what's on the stack, print variables, and do whatever other debugging we wish.
|
||||||
|
|
||||||
|
**Print variables on stack:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
(gdb) info locals
|
||||||
|
```
|
||||||
|
|
||||||
|
**Print a specific variable:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
(gdb) print variable_name
|
||||||
|
```
|
||||||
|
|
||||||
|
**Show backtrace:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
(gdb) bt
|
||||||
|
```
|
||||||
|
|
||||||
|
**Continue on with program execution:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
(gdb) continue
|
||||||
|
```
|
||||||
|
|
||||||
|
## Perf
|
||||||
|
|
||||||
|
I haven't played with `perf` as much but one thing I found that was cool was the `perf top` command. This command gives you samples of which function calls keeps the program the most busy.
|
||||||
|
|
||||||
|
To attach to a process and show samples:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
perf top -p pid -K
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
3
content/blog/_index.md
Normal file
3
content/blog/_index.md
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
---
|
||||||
|
title: Blog
|
||||||
|
---
|
77
content/blog/abstractdef.md
Normal file
77
content/blog/abstractdef.md
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
---
|
||||||
|
title: "Useful Abstract Definitions"
|
||||||
|
date: 2019-10-27T23:49:57-04:00
|
||||||
|
draft: false
|
||||||
|
---
|
||||||
|
|
||||||
|
## Chapter 17
|
||||||
|
|
||||||
|
By a **ring** we mean a set $A$ with operations called addition and multiplication which satisfy the following axioms:
|
||||||
|
|
||||||
|
- $A$ with addition alone is an abelian group
|
||||||
|
- Multiplication is associative
|
||||||
|
- Multiplication is distributive over addition
|
||||||
|
|
||||||
|
Since $\langle A, + \rangle$ is an abelian group, there is in $A$ a neutral element for addition. This is called the **zero** element. Also, every element has an additive inverse called its **negative**.
|
||||||
|
|
||||||
|
If multiplication in a ring $A$ is commutative then we say that $A$ is a **commutative ring**.
|
||||||
|
|
||||||
|
If a ring $A$ has a neutral element for multiplication then we say that the neutral element is the **unity** of $A$.
|
||||||
|
|
||||||
|
If $A$ is a ring with unity, there may be elements in $A$ which have a multiplicative inverse. Such elements are said to be **invertible**.
|
||||||
|
|
||||||
|
If $A$ is a commutative ring with unity in which every nonzero element is invertible, then $A$ is called a **field**.
|
||||||
|
|
||||||
|
In any ring, a nonzero element $a$ is called a **divisor of zero** if there is a nonzero element $b$ in a ring such that the product $ab$ or $ba$ is equal to zero.
|
||||||
|
|
||||||
|
An **integral domain** is defined to be a commutative ring with unity that has the cancellation property. Another way of saying this is that an integral domain is a commutative ring with unity and has no zero divisors.
|
||||||
|
|
||||||
|
## Chapter 18
|
||||||
|
|
||||||
|
Let $A$ be a ring, and $B$ be a nonempty subset of $A$. $B$ is called a **subring** if it satisfies the following properties:
|
||||||
|
|
||||||
|
- Closed with respect to addition
|
||||||
|
- Closed with respect to negatives
|
||||||
|
- Closed with respect to multiplication
|
||||||
|
|
||||||
|
Let $B$ be a subring of $A$. We call $B$ an **ideal** of $A$ if $xb, bx \in B$ for all $b \in B$ and $x \in A$.
|
||||||
|
|
||||||
|
The **principle ideal generated by $a$**, denoted $\langle a \rangle$ is the subring defined by fixing an element $a$ in a subring $B$ of $A$ and multiplying all elements of $A$ by $a$.
|
||||||
|
$$
|
||||||
|
\langle a \rangle = \{ xa : x \in A \}
|
||||||
|
$$
|
||||||
|
A **homomorphism** from a ring $A$ to a ring $B$ is a function $f : A \to B$ satisfying the identities:
|
||||||
|
$$
|
||||||
|
f(x_1 + x_2 ) = f(x_1) + f(x_2) \\
|
||||||
|
f(x_1x_2) = f(x_1)f(x_2)
|
||||||
|
$$
|
||||||
|
If there is a homomorphism from $A$ onto $B$, we call $B$ a **homomorphic image** of $A$.
|
||||||
|
|
||||||
|
If $f$ is a homomorphism from a ring $A$ to a ring $B$, the **kernel** of $f$ is the set of all the elements of $A$ which are carried by $f$ onto the zero element of $B$. In symbols, the kernel of $f$ is the set
|
||||||
|
$$
|
||||||
|
K = \{x \in A: f(x) = 0\}
|
||||||
|
$$
|
||||||
|
If $A$ and $b$ are rings, an **isomorphism** from $A$ to $B$ is a homomorphism which is a one-to-one correspondence from $A$ to $B$. In other words, it is injective and surjective homomorphism.
|
||||||
|
|
||||||
|
If there is an isomorphism from $A$ to $B$ we say that $A$ is **isomorphic** to $B$, and this fact is expressed by writing $A \cong B$.
|
||||||
|
|
||||||
|
## Chapter 19
|
||||||
|
|
||||||
|
Let $A$ be a ring, and $J$ an ideal of $A$. For any element $a \in A$, the symbol $J + a$ denotes the set of all sums $j + a$ as $a$ remains fixed and $j$ ranges over $J$. That is,
|
||||||
|
$$
|
||||||
|
J + a = \{j + a : j \in J\}
|
||||||
|
$$
|
||||||
|
$J + a$ is called a **coset** of $J$ in $A$.
|
||||||
|
|
||||||
|
Now think of the set which contains all cosets of $J$ in $A$. This set is conventionally denoted by $A / J$ and reads $A$ mod $J$. Then, $A / J$ with coset addition and multiplication is a ring.
|
||||||
|
|
||||||
|
An ideal $J$ of a commutative ring is said to be a **prime ideal** if for any two elements $a$ and $b$ in the ring,
|
||||||
|
$$
|
||||||
|
ab \in J \implies a \in J \text{ or } b \in J
|
||||||
|
$$
|
||||||
|
<u>Theorem:</u> If $J$ is a prime ideal of a community ring with unity $A$, then the quotient ring $A / J$ is an integral domain.
|
||||||
|
|
||||||
|
An ideal $J$ of $A$ with $J \ne A$ is called a **maximal ideal** if there exists no proper ideal $K$ of $A$ such that $J \subseteq K$ with $J \ne K$.
|
||||||
|
|
||||||
|
<u>Theorem:</u> If $A$ is a commutative ring with unity, then $J$ is a maximal ideal of $A$ iff $A/J$ is a field.
|
||||||
|
|
13
content/blog/adding-fonts.md
Normal file
13
content/blog/adding-fonts.md
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
---
|
||||||
|
title: "Adding Fonts"
|
||||||
|
date: 2019-05-27T12:15:25-04:00
|
||||||
|
draft: false
|
||||||
|
---
|
||||||
|
|
||||||
|
**Warning: This blog post partially applies to Ubuntu-based operating systems**
|
||||||
|
|
||||||
|
I wanted to add a font to an image on GIMP. Now I wanted to make it so that the font is available system-wide in case I ever needed it again.
|
||||||
|
|
||||||
|
Following this [blog post from It's FOSS](https://itsfoss.com/install-fonts-ubuntu/) I decided to make a `.fonts` folder in my home directory. After that you can download various font's from [Font Squirrel](https://www.fontsquirrel.com/), [dafont](https://www.dafont.com), [Google Fonts](https://fonts.google.com/), and others and drop the `.otf` or `.ttf` files into the `.fonts` folder.
|
||||||
|
|
||||||
|
Then to finally achieve my goal, I made sure I added `/home/user/.fonts` folder to my fonts as outlined on the [GIMP documentation](https://docs.gimp.org/2.10/en/gimp-using-fonts.html)
|
105
content/blog/albuquerque.md
Normal file
105
content/blog/albuquerque.md
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
---
|
||||||
|
id: 2234
|
||||||
|
title: Albuquerque Real Estate Multiple Regression model
|
||||||
|
date: 2017-07-12T01:07:39+00:00
|
||||||
|
author: Brandon Rozek
|
||||||
|
layout: revision
|
||||||
|
guid: https://brandonrozek.com/2017/07/2227-revision-v1/
|
||||||
|
permalink: /2017/07/2227-revision-v1/
|
||||||
|
---
|
||||||
|
### Introduction
|
||||||
|
|
||||||
|
Albuquerque, New Mexico is a city of thriving culture. The population has grown drastically within the past several years and is a hotspot for New Mexico. Albuquerque’s one-of-a-kind southwestern culture is all around the city, from the quaint shops, Pueblo and Spanish inspired architecture and world famous cuisine, to the music and art of the city. Being the largest city in the state, it has a wide variety of homes across the entire city whether it be within the actual city of Albuquerque ore in the outskirts. Many people flock to the city for the weather. It is a mildly dry climate with roughly 310 days of solid sunshine each year, making it a literal hotspot for people who enjoy the outdoors and warm weather. The city has little to no natural disasters that threaten everyday life, make it a safe place to settle. In the city of Albuquerque, there is a wide variety of living communities throughout the area. As mentioned above, there are different diverse communities. The housing availability is vast as well. The housing market in this region is vast. Because there is little to no potential for natural disasters it causes the market to grow due to less of a liability for increased damage from weather. Albuquerque, has one of the lowest costs of living than anywhere else in the country which makes it a great place to invest in the housing market. The goal of this project is to figure out details of real estate, specifically in the city of Albuquerque, New Mexico. In the dataset given, the predictors available are square footage, age of home, number of features, amount in taxes, and several binary variables. Those binary variables being whether or not the home was of a custom design, is located in a corner location, or if it is located in the northeast quadrant of the city.
|
||||||
|
|
||||||
|
### Descriptive Statistics
|
||||||
|
|
||||||
|
Our dataset features a representative sample of 117 homes in Albuquerque, New Mexico. From this dataset, we can describe the key patterns that arise and build an intuition for what is to come in our inferential statistics section. It is important to build this intuition since it helps when generalizing to a population.
|
||||||
|
|
||||||
|
#### Distribution of Data
|
||||||
|
|
||||||
|
To understand the boxplot of interactions shown by Figure 1, we first need to establish what each of the indices or numbers under the x-axis represent. The first index discloses whether or not the home is located in the northeast quadrant of Albuquerque, New Mexico. The second 1index tells us whether or not the home had a custom design. And finally, the third index indicates whether or not the home is located in the corner of an intersection. From the boxplot of interactions, we can see that homes with a custom design tend to have a higher price than homes that don’t. There are no other patterns from this figure that we can readily pick up in terms of how the different combination of factors affect a home’s price. Figure 2 consists of multiple boxplots each comparing the price distribution of the different levels in all the categorical variables. From this we can reconfirm that having a custom design leads to a higher price distribution. We also see that the Northeast quadrant has a larger variation in price. There does not seem to be any significant difference in the price of homes depending on if it’s located on a corner. Finally, the more features a home has, the larger the variation in prices become. Figure 3 shows a scatterplot matrix of our data. From the matrix we can see the possibility of a linear correlation between price and square footage, as well as, price and tax. There is no evidence, however, of linear correlation between price and the age of the home. This suggests that perhaps age will not become a significant factor in our model. Figure 4 contains histograms of the different quantitative variables. As shown in the figure, the histograms are all right skewed. This shows that the majority of homes are in the lower price range while there are a few homes in the upper price region that might skew our model. Figure 5 shows the distribution of the data in terms of the categorical variables. It is interesting to note that the number of features falls in a gaussian like curve which is unimodal and symmetric at about 4 features. There are more homes that do not have a custom design and do not lie on a corner than ones that do. In this dataset, there are also more homes in the northeast quadrant than homes that are not. Figure 6 shows the distribution of taxes to be right skewed as well.
|
||||||
|
|
||||||
|
#### Effect/Interaction Plots
|
||||||
|
|
||||||
|
In the following section we will consider the effect of different categorical variables on the price of homes in Albuquerque and the other variables. The effect plots are used primarily to build an intuition on an individual variable’s effect on the price of a home. Interaction plots, however, analyze the influence of a variable on another’s effect on the price of a home. If the lines in an interaction plot intersect, it tells us that a categorical variable has a significant influence on another’s effect on the price of a home. The main effect plots in Figure 7 shows us the mean price of a home in relation to the different levels for each of the factors in the dataset. Similar to what was discovered in the boxplot figures, having a custom design increases the price of a home drastically compared to the increase of the price of a home by other factors. Having a home in a non-corner location slightly decreases the value of a home on average. Finally, it is shown in the Feature Effect plot that increases in the number of features increases the price of a home. An interesting thing to note is how in the case of 8 features, the average price of a home decreases. Figures 8, 9, and 10 show the interaction plots of different categorical variable combinations. In Figures 8 and 9, the lines do not intersect, therefore we can conclude that being in the northeast bears no influence on how custom design influences the price of the home and being in the northeast also bears no influence on how the corner location affect on the price. Figure 10, however, shows an interaction between corner location and custom design. This suggests that corner location has an influence on how custom design effects the price.
|
||||||
|
|
||||||
|
#### Missing Data
|
||||||
|
|
||||||
|
In the dataset provided, 44% of the homes are missing some data. Missing data, like outliers, are not to be ignored. To deal with missing data, one needs to know the implications of missing data on the analysis. From there, we can think of several techniques to deal with the absence of the data. One of the most important implications to check is to see if the data is missing at random. If the data is not missing at random, then it taints the representability of the sample, making it harder to generalize to a population. In the dataset provided there are two variables that contain missing data. Figure 11 shows how 42% of the data is missing the age variable and 9% of the data is missing the Tax variable. Looking at the right side of Figure 11, we can see the pattern of missing data in our set. 35% of the data in our set is only missing the age variable, 7% of our data is missing the age and tax variable, and only 2% of our data is missing the tax variable alone. Figure 12 shows us the missing data with respect to quantitative variables such as price, square footage, and tax. As seen in the figure, the missing data is spread equally throughout the distribution. Figure 13 shows us the missing data with respect to the categorical variables in our dataset. As seen in the figure, the proportion of missing data in the binary variables are about the same for each level. When looking at the number of features, however, there is a higher proportion of data missing in the homes with fewer features than in the homes with more features. Looking at the scatterplot matrix of missing values in Figure 14, we can see that the missing values in red are spread out evenly through the observed values in blue. Missing data with respect to age was not considered since the majority of the missing data comes from the age variable.
|
||||||
|
|
||||||
|
### Inferential Statistics
|
||||||
|
|
||||||
|
Now that we’ve described the sample, we can now look forward to generalizing our suspicions to the population. In this section, we define the hypotheses of interest in our model and then consider different techniques that lead up to the final model and analysis.
|
||||||
|
Hypotheses The main hypothesis of interest is if the model is significant.
|
||||||
|
➢ H 0 : There is no linear association between the price of homes in Albuquerque, New Mexico and any of the following: square footage, age of the home, tax, number of features, corner location, northeast location, or custom design.
|
||||||
|
➢ H A : There is a linear association between the price of homes in Albuquerque, New Mexico and at least one of the following predictors: square footage, age of the home, tax, number of features, corner location, northeast location, or custom design. Afterwards, we consider if square footage, age, tax, number of features, corner location, northeast location, or custom design are significant predictors in the model individually.
|
||||||
|
|
||||||
|
#### Ignore Missing Data Technique
|
||||||
|
|
||||||
|
One of the first regression models we considered is if we ignore all of the missing data. Since 44% of our data have missing values, this drops our degrees of freedom significantly to 61. This technique also doesn’t utilize all of the data, which is prefered so that it doesn’t affect the coefficients of our model. Running the stepwise algorithm to maximize the AIC, we obtain the following model
|
||||||
|
PRICE = 97.92321 + 0.33638(SQFT) + 0.52308(TAX) + 177.18519(CUST1) − 77.82234(COR1)
|
||||||
|
Where the p-value of the model is less than 2.2e-16. At a 5% significance level, there is a linear association between the price of the home and at least one of the following predictors: square footage, tax, custom design, or corner location. Looking at the individual t-tests for predictor significance, square footage, tax, and custom design all have a p-value around or less than 0.01 which rejects the null hypotheses of those predictors. This tells us that square footage, tax, and custom design are all significant predictors in our model. However, corner location only have a p-value of 0.09, which at a 5% significant level, fails to reject the null hypothesis. Corner location is not a significant predictor in our model. The residual standard error in this model is $155,400 which is about 15% of the mean price value of a home. The adjusted R 2 value is 0.8523, which means that about 85% of the variation in the prices are accounted for in the variation of the model. Not all of the conditions are met for this model and since we ignore a good chunk of the data, this model is not fully appropriate for our usage. In our next attempt, we will increase the number of observations that we include in our analysis.
|
||||||
|
|
||||||
|
#### Remove Age Column Technique
|
||||||
|
|
||||||
|
##### Justification of Removal of Age Variable
|
||||||
|
|
||||||
|
Most of the data missing is in the age variable. Age is also not a significant predictor in the model made in the previous section, therefore, our next idea was to remove the age variable from the dataset. It is important, however, to establish that the age data is truly missing at random and removing it won’t significantly affect the analysis. We do this by removing the only other variable that has missing data (tax) from the dataset. Looking at Figures 17 and 18, it did not change significantly the distribution of missing data with respect to the different variables. This lets us comfortably remove the age column from our dataset. From here, we then repeat the Ignore Missing Data technique. This time since only 9% of the data is missing, our degrees of freedom in this analysis increases from 61 to 102. Having a greater degrees of freedom allows us to have more statistical power in our analysis.
|
||||||
|
|
||||||
|
##### Variable Selection and Test Results
|
||||||
|
|
||||||
|
Running through the stepwise algorithm to maximize AIC, we get the following model
|
||||||
|
PRICE = 175.16603 + 0.67707(TAX) + 156.81481(CUST1) + 0.20760(SQFT) − 83.40126(COR1)
|
||||||
|
The p-value of this model is less than 2.2e-16, which at a 5% significance level, leads us to reject the null hypothesis and state that price has a linear association between at least one of the predictors in the model. Looking at the t-tests for predictor significance, all of the p-values are less than 0.05, which leads us to reject all the null hypotheses, and state that all of the predictors in this model: tax, custom design, square footage, and corner location are significant. The residual standard error in this model is $162,300 which is slightly higher from the $155,400 in the previous model. Though the proportion that this residual standard error is of the mean of the price set is the same at 15%. The adjusted R 2 value is 0.8213, which means that 82% of the variation in the prices are accounted for by the variation in the model. This dropped from the previous 85% value from the
|
||||||
|
previous model. The residual standard error and adjusted R 2 values indicate that this model is worse in terms of its predictive ability. This model, however, takes more of the data into account, which makes it more accurate when considering the data as a whole and not the subsetted data that arrives from removing the null entries. In this model, we still lose about 9% of our data. In our next model, we will consider a technique that allows us to use the entire dataset.
|
||||||
|
|
||||||
|
#### Multiple Imputation
|
||||||
|
|
||||||
|
To take the entire dataset into account we need to decide how to handle the missing values. Multiple imputation is a technique that randomly samples from the existing distribution to take place of the missing data multiple times. The benefits of multiple imputation is that it preserves the mean and variance of the data. For multiple imputation to work, the data must be missing at
|
||||||
|
random. In the missing data section, we described how the missing data is spread equally with respect to the different variables. This allows us to say that the Missing at Random condition is met.
|
||||||
|
|
||||||
|
##### Variable Selection and Test Results
|
||||||
|
|
||||||
|
Running the stepwise algorithm to maximize the AIC value, we get the following model
|
||||||
|
PRICE = 138.09421 + 0.65373(TAX) + 0.23709(SQFT) + 124.31776(CUST2) − 6 7.17475(COR2)
|
||||||
|
From the F-Test, we can reject the null hypothesis at a 5% significance level due to the fact that the p-value is less than 2.2e-16. This means that there is a linear association between price and one of the predictors: tax, square footage, custom design, and corner office. All of the T-Tests for predictor significance aside from COR2 have a p-value of less than 0.05. Meaning, we reject the appropriate null hypotheses and state that tax, square footage, and custom design are significant predictors. The residual standard error is $167,700 with 112 degrees of freedom. This value corresponds to 16% of the mean price of a home. The adjusted R 2 value is 0.8056, which means that about 81% of the variation in the prices is accounted for by this model. The residual standard error and adjusted R 2 values are again worse than the previous model. However, since this model uses the entire dataset, we can say that this is a more representative sample of the data than the previous models. To lower the amount of error in this model, we considered removing the outliers. In this model there are six outliers as shown in Figure 19. Since they are significantly far from the middle 50% of the price distribution, we removed them in the following section to better make a model that describes the main portion of the data.
|
||||||
|
|
||||||
|
#### Multiple Imputation without Outliers
|
||||||
|
|
||||||
|
Removing the outliers from the model and running the stepwise algorithm to maximize AIC, we obtain
|
||||||
|
PRICE = 76.47917 + 0.64130(TAX) + 0.27290(SQFT) + 77.58816(CUST2)
|
||||||
|
6For the F-Statistic, the p-value is less than 2.2e-16. At a 5% significance level, we reject the null hypothesis and state that there is a linear association between price and at least one of the following: tax, square footage, or custom design. The p-values of the T-Tests for predictor significance tells us that at a 5% significant level. all of the predictors are significant since all of the p-values are less than 0.05. The residual standard error of this model is $112,700 which is 11% of the mean of the dataset.
|
||||||
|
The adjusted R 2 value is 0.8901, which means that 89% of the variation in the prices is accounted for by the variation of the model. In this model, the residual standard error is lower and the adjusted R 2 value is higher than the previous models. This tells us that it is a better fit for the data without the outliers. Looking at the outlier effect on the coefficients, we can see that without the outliers the term relating to the corner location goes away from the model. That then significantly changes the
|
||||||
|
coefficients of CUST2 and the y-intercept.
|
||||||
|
|
||||||
|
##### Checking the Conditions for Inference
|
||||||
|
|
||||||
|
Before we conclude with the analysis, we must first check the conditions for inference to see if the technique is appropriate for our data.
|
||||||
|
<u>Independence Assumption:</u>
|
||||||
|
A house’s selling price can depend on another’s so this condition is not met.
|
||||||
|
<u>Randomization Condition:</u>
|
||||||
|
The dataset is comprised of a random sample of records of resale of homes which satisfies the
|
||||||
|
randomization condition.
|
||||||
|
<u>Straight Enough Condition:</u>
|
||||||
|
The scatterplot matrix in Figure 20 shows that for the predictors square footage and tax that the
|
||||||
|
scatterplot is straight enough and doesn’t have any bends or curves.
|
||||||
|
<u>Equal Variance Assumption:</u>
|
||||||
|
The residual analysis in Figure 21 shows that the outliers are not spread equally on the
|
||||||
|
scatterplot. Therefore, the equal variance assumption is not met.
|
||||||
|
<u>Nearly Normal Condition:</u>
|
||||||
|
The QQ-Plot in Figure 21 shows that the residuals follow a unimodal and symmetric distribution.
|
||||||
|
Taking out the outliers in the model also did not introduce any new outliers in the boxplot.
|
||||||
|
<u>Missing At Random Condition:</u>
|
||||||
|
7The discussion in the descriptive statistics section about the missing data tells us that the data
|
||||||
|
is missing evenly with respect to the different variables. Therefore, it is safe to assume that the
|
||||||
|
data is missing at random
|
||||||
|
<u>Multicollinearity Condition:</u>
|
||||||
|
All of the VIF values are lower than 10, therefore this condition is met.
|
||||||
|
|
||||||
|
The conditions for inference are not fully met due to the equal variance assumption. This means that our model will be more inaccurate for some price range of homes than others. Looking at our residual analysis, it appears that the inaccuracies happen when the price of the home is higher. There weren’t many outliers in the dataset (6 out of 117 or 5%) so removing these outliers makes the model more representative to the majority of the houses in the market. Since this model is intended to be used when analyzing prices of homes in the area, it is better not to include the outliers that most people don’t intend to buy. Since the error term is unimodal and symmetric, we can be at ease that there isn’t any other confounding factor in our model. Overall, this is a good model to use for inference and prediction as long as one doesn’t use it to describe the outliers.
|
||||||
|
|
||||||
|
### Conclusion
|
||||||
|
|
||||||
|
The multiple imputation model without outliers is the best model outlined in this paper for describing the price of housing in this region. The formula is re-expressed here
|
||||||
|
PRICE = 76.47917 + 0.64130(TAX) + 0.27290(SQFT) + 77.58816(CUST2)
|
||||||
|
This states that for every dollar of tax spent on the home, the home increases on average by $64 given the other parameters stay constant. The same concept applies to square footage and custom design. For every square foot added to the home, the value of it increases on average by $27. Having a home with a custom design increases the value of the home by $7700. This model is more reliable the lower the price of the home is. When it comes to high cost homes, the error produced by the model increases. From this model, we conclude that property tax, square footage, and whether or not a home is built from a custom design are the most significant factors in the price of a home in Albuquerque, New Mexico.
|
17
content/blog/archivingsites.md
Normal file
17
content/blog/archivingsites.md
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
---
|
||||||
|
title: "Archiving Sites"
|
||||||
|
date: 2019-08-02T22:42:16-04:00
|
||||||
|
draft: false
|
||||||
|
---
|
||||||
|
|
||||||
|
I have several old Wordpress sites that are now only alive for archival purposes. I've been trying to migrate off of Wordpress since I don't like having yet another system to manage. Luckily for archival type sites, I don't need the whole backend of Wordpress since I don't actually need any CRUD functionality. (Well I need the R part)
|
||||||
|
|
||||||
|
Solution... wget comes to the [rescue](https://stackoverflow.com/questions/538865/how-do-you-archive-an-entire-website-for-offline-viewing#538878)
|
||||||
|
|
||||||
|
Thanks to user [chuckg](https://stackoverflow.com/users/63193/chuckg), I now know you can run `wget -m -k -K -E https://url/of/web/site` to get a full offline copy of a website.
|
||||||
|
|
||||||
|
[Joel Gillman](https://stackoverflow.com/users/916604/jgillman) expanded out the command to `wget --mirror --convert-links --backup-converted --adjust-extension https://url/of/web/site`
|
||||||
|
|
||||||
|
There are other solutions in that stack overflow post, but something about the simplicity of `wget` appealed to me.
|
||||||
|
|
||||||
|
[Check out this now Wordpress free site of mine!](https://sentenceworthy.com)
|
8
content/blog/aspell.md
Normal file
8
content/blog/aspell.md
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
---
|
||||||
|
title: "aspell"
|
||||||
|
date: 2019-12-10T22:20:48-05:00
|
||||||
|
draft: false
|
||||||
|
images: []
|
||||||
|
---
|
||||||
|
|
||||||
|
When I was working on my honors thesis, I realized that I needed a way to spell check the file. After a quick search, I stumbled upon `aspell` which is a `curses` based tool to provide an interactive way for dealing with misspellings. Try it out when you have the chance!
|
32
content/blog/bashflags.md
Normal file
32
content/blog/bashflags.md
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
---
|
||||||
|
title: "Bash Flags"
|
||||||
|
date: 2019-08-06T16:55:47-04:00
|
||||||
|
draft: false
|
||||||
|
---
|
||||||
|
|
||||||
|
I was creating a bash script and was looking around for a solution for parsing command line arguments. [This StackOverflow post](https://stackoverflow.com/questions/192249/how-do-i-parse-command-line-arguments-in-bash) has a variety of different solutions available. I want to describe my favorite of these posts.
|
||||||
|
|
||||||
|
[Inanc Gumus](https://stackoverflow.com/users/115363/inanc-gumus) proposed the following:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
while [[ "$#" -gt 0 ]]; do case $1 in
|
||||||
|
-d|--deploy) deploy="$2"; shift;;
|
||||||
|
-u|--uglify) uglify=1;;
|
||||||
|
*) echo "Unknown parameter passed: $1"; exit 1;;
|
||||||
|
esac; shift; done
|
||||||
|
|
||||||
|
echo "Should deploy? $deploy"
|
||||||
|
echo "Should uglify? $uglify"
|
||||||
|
```
|
||||||
|
|
||||||
|
Let me quickly describe what it does. While the number of arguments left to process is greater than zero....
|
||||||
|
|
||||||
|
- Check to see if the argument matches any of the flags
|
||||||
|
- If it does...
|
||||||
|
- If the flag requires an additional argument grab it. Then discard an argument.
|
||||||
|
- If it doesn't. Error out.
|
||||||
|
- Then get rid of an argument.
|
||||||
|
|
||||||
|
At the end of the while loop, you would've evaluated all the arguments!
|
28
content/blog/bashprocesses.md
Normal file
28
content/blog/bashprocesses.md
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
---
|
||||||
|
title: "Handling Background Processes in Bash"
|
||||||
|
date: 2019-06-17T19:50:30-04:00
|
||||||
|
draft: false
|
||||||
|
---
|
||||||
|
|
||||||
|
For multi-process applications, I want to be able to start it up using the `bash` command processor and be able to stop all the processes just by hitting `CTRL-C`.
|
||||||
|
|
||||||
|
As a quick reminder, to have a task run in the background you need to add a `&` at the end of the line.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
execute_app &
|
||||||
|
```
|
||||||
|
|
||||||
|
Previously, I was grabbing the PID of this background process, trapping the interrupt signal and taking the time to send the interrupt signal to all of the background processes.
|
||||||
|
|
||||||
|
You can get the child pid by referencing the variable `$!` after sending a process to the background.
|
||||||
|
|
||||||
|
Now I just use `setsid` to set the process group of the background processes to be the same as the bash process itself. The following demo script here will show the capability.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
setsid sleep 5 &
|
||||||
|
setsid sleep 10 &
|
||||||
|
wait
|
||||||
|
```
|
||||||
|
|
||||||
|
This script will send two processes to the background and will wait until all the processes are finished. Hitting `CTRL-C` during execution will send the interrupt signal to all of the processes achieving my goal.
|
31
content/blog/blogworkflow.md
Normal file
31
content/blog/blogworkflow.md
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
---
|
||||||
|
title: "Blog Workflow"
|
||||||
|
date: 2019-10-28T00:16:44-04:00
|
||||||
|
draft: false
|
||||||
|
---
|
||||||
|
|
||||||
|
You may be curious on how I create content for this blog. There's pros and cons to my approach. One pro is that it's relatively easy for me to crank something out and upload it to my site. The downside is that it's not as easily customizable.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
I use Hugo as my static site generator. It works with Markdown files. I begin every blog post by typing
|
||||||
|
|
||||||
|
```bash
|
||||||
|
hugo new blog/title.md
|
||||||
|
```
|
||||||
|
|
||||||
|
This creates a file called `title.md` in `content/blog` with front-matter yaml information such as the title and the datetime that I created the post.
|
||||||
|
|
||||||
|
I then use my favorite Markdown editor `typora` to open up the file in the background.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
typora content/blog/title.md &
|
||||||
|
```
|
||||||
|
|
||||||
|
Write some great words down, compile the website, then sync it to my site!
|
||||||
|
|
||||||
|
```bash
|
||||||
|
hugo
|
||||||
|
rsync -Paz --delete public/ brandonrozek.com:/path/to/site/folder
|
||||||
|
```
|
||||||
|
|
55
content/blog/borgbackup.md
Normal file
55
content/blog/borgbackup.md
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
---
|
||||||
|
title: "Borg Backup"
|
||||||
|
date: 2019-05-21T22:35:31-04:00
|
||||||
|
draft: false
|
||||||
|
---
|
||||||
|
|
||||||
|
I started using [Borg Backup](https://www.borgbackup.org/) in order to efficiently and securely do my backups. I did some research before choosing this solution as I required three things:
|
||||||
|
|
||||||
|
- Compression
|
||||||
|
- Encryption
|
||||||
|
- Deduplication
|
||||||
|
|
||||||
|
Each point is important on their own. Ideally I would be able to put this onto a cloud solution. Due to this I would want compression and deduplication to keep my costs down and encryption in order to maintain privacy.
|
||||||
|
|
||||||
|
Luckily Borg does all of these things and more! It's also easily available on the AUR and the Ubuntu repositories.
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
This will be a short post just describing the basic usage of the tool. I haven't fully implemented this tool yet so forgive me if this doesn't match your exact use case. This is also a great way for me to document the basic commands for myself as well.
|
||||||
|
|
||||||
|
First to initialize a borg repo encrypted with a password at `repolocation`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
borg init --encryption=repokey repolocation
|
||||||
|
```
|
||||||
|
|
||||||
|
Then to create a backup in the repo with key `backup1`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
borg create --stats --progress --compression lzma repolocation::backup1 folderToBackup
|
||||||
|
```
|
||||||
|
|
||||||
|
You can actually replace the compression algorithm if wanted, here is a short description from their [website](https://borgbackup.readthedocs.io/en/stable/):
|
||||||
|
|
||||||
|
- lz4 (super fast, low compression)
|
||||||
|
- zstd (wide range from high speed and low compression to high compression and lower speed)
|
||||||
|
- zlib (medium speed and compression)
|
||||||
|
- lzma (low speed, high compression)
|
||||||
|
|
||||||
|
To list what backups you have in the repo:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
borg list repolocation
|
||||||
|
```
|
||||||
|
|
||||||
|
To mount and unmount the repository
|
||||||
|
|
||||||
|
```bash
|
||||||
|
borg mount repolocation mountlocation
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
borg umount mountlocation
|
||||||
|
```
|
||||||
|
|
28
content/blog/chirp.md
Normal file
28
content/blog/chirp.md
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
---
|
||||||
|
title: "Chirp"
|
||||||
|
date: 2019-09-27T22:46:52-04:00
|
||||||
|
draft: false
|
||||||
|
---
|
||||||
|
|
||||||
|
In the land of Ham Radio, you can program your radio with a very popular open source software called `chirp`. For Ubuntu users to install it, it is recommended you use the PPA to keep up to date with radio software...
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt-add-repository ppa:dansmith/chirp-snapshots
|
||||||
|
sudo apt update && sudo apt install chirp-daily
|
||||||
|
```
|
||||||
|
|
||||||
|
Once you're in, it is recommended that you download the configuration currently set on your radio and store it somewhere safe. You never know when you'll want the Stock configuration again...
|
||||||
|
|
||||||
|
The way the Software works is that you can store a certain amount of channels into your radio. For the Baofeng F8-HP, 128 channels can be stored. The software gives you a table to manage them. The software is nice since you can cut and paste entries around.
|
||||||
|
|
||||||
|
When you're importing frequencies from various sources, pay attention to what entry numbers you're importing to. Otherwise you might overwrite another import you've made. I recommend adding the following types of frequencies to your radio...
|
||||||
|
|
||||||
|
- Repeaters: Can be added by using RepeaterBook
|
||||||
|
- I used proximity search. It takes a zip code and the radius in miles of what you want to import.
|
||||||
|
- Local frequencies: You need a prenium account from Radio Reference in order to obtain these.
|
||||||
|
- Though honestly, a premium account is $15 for half a year. Not bad if you just go in once and download a bunch of frequencies for your area. Depending on where you live, your area might not have frequencies update all the time...
|
||||||
|
- Imagine being stuck in traffic and getting to know what's happening around you....
|
||||||
|
- National Calling Frequencies
|
||||||
|
- MURS/FRS/GMRS frequencies
|
||||||
|
- Marine/Railroad frequencies (if you have space)
|
||||||
|
|
15
content/blog/collabpandocbeamer.md
Normal file
15
content/blog/collabpandocbeamer.md
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
---
|
||||||
|
title: "Collaborating on Beamer Pandoc Slides"
|
||||||
|
date: 2019-11-22T14:49:19-05:00
|
||||||
|
draft: false
|
||||||
|
---
|
||||||
|
|
||||||
|
Recently I've been making slides using Pandoc with Beamer. However, I came across the issue where I needed to collaborate with someone using already existing slides written in Markdown.
|
||||||
|
|
||||||
|
Sadly, I don't think this person (and most people for that matter) wants to edit slides in Markdown and use Pandoc. So I needed to convert this into another source material. My original plan was to convert this into a Powerpoint and put it into Google Slides. When I went to convert it using Pandoc, it stripped my math typing and theme. Unfortunately, that wasn't going to work for me.
|
||||||
|
|
||||||
|
The solution I ultimately ended using was the following.
|
||||||
|
1. Convert the Markdown into Latex
|
||||||
|
2. Upload the latex document into Overleaf
|
||||||
|
3. Use the share feature in Overleaf
|
||||||
|
|
47
content/blog/colormanipulation.md
Normal file
47
content/blog/colormanipulation.md
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
---
|
||||||
|
title: "Color Manipulation with Sass"
|
||||||
|
date: 2019-05-21T23:10:06-04:00
|
||||||
|
draft: false
|
||||||
|
---
|
||||||
|
|
||||||
|
There are many times that I need to slightly mess with a color. The easiest way I found to do it is to use one of the many color functions in the program `Sass`. [Sass](https://sass-lang.com/) is a CSS preprocessor, meaning that it has it's own syntax and it compiles down to CSS. I remember using this before CSS variables became a thing and that was one of the main driving points of Sass.
|
||||||
|
|
||||||
|
Since I don't work in Web Development anymore, I don't actually have Sass installed on my computer but instead go to [SassMeister.com](https://www.sassmeister.com/) to do my color manipulations. ThoughtBot already wrote a really nice [post](https://thoughtbot.com/blog/controlling-color-with-sass-color-functions) describing all the different color functions, but I'll quickly describe what I do to mess with colors.
|
||||||
|
|
||||||
|
## Quick Color Manipulation
|
||||||
|
|
||||||
|
Now you do need to write special scss syntax in order for this to work, but luckily most of it you can just copy paste and change what you want. For example, to get Sass to make red 10% lighter you would do the following:
|
||||||
|
|
||||||
|
```scss
|
||||||
|
p {
|
||||||
|
color: lighten(red, 10%);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Sass will then output the following:
|
||||||
|
|
||||||
|
```css
|
||||||
|
p {
|
||||||
|
color: #ff3333;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
And BAM just like that you got the hex code for a lighter version of red. You can also get all crazy and nest statements into each other which is usually what I do when I play with these colors.
|
||||||
|
|
||||||
|
If you rather have the RGBA version of this you can do the following:
|
||||||
|
|
||||||
|
```scss
|
||||||
|
p {
|
||||||
|
color: rgba(lighten(red, 10%), 0.5);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
And it will output:
|
||||||
|
|
||||||
|
```css
|
||||||
|
p {
|
||||||
|
color: rgba(255, 51, 51, 0.5);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
So in summary, all you need is to replace the section after `color:` and you're all set for manipulating colors! Check out the [ThoughtBot Post above](https://thoughtbot.com/blog/controlling-color-with-sass-color-functions) in order to see all the variety of functions included with Sass such as saturating, hue-shifting, and much more.
|
46
content/blog/composesystemd.md
Normal file
46
content/blog/composesystemd.md
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
---
|
||||||
|
title: "Ensuring Docker Compose Startup with Systemd"
|
||||||
|
date: 2019-12-16T20:57:36-05:00
|
||||||
|
draft: false
|
||||||
|
images: []
|
||||||
|
---
|
||||||
|
|
||||||
|
I've been having trouble getting some docker containers such as `nginx` to start automatically on bootup, even with the `restart: always` flag.
|
||||||
|
|
||||||
|
To compensate, I wrote a small systemd script and enabled it on startup.
|
||||||
|
|
||||||
|
`/etc/systemd/system/docker-compose.service`
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[Unit]
|
||||||
|
Description=Docker Compose Application Service
|
||||||
|
Requires=docker.service
|
||||||
|
After=docker.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
User=brandonrozek
|
||||||
|
Group=brandonrozek
|
||||||
|
RemainAfterExit=yes
|
||||||
|
WorkingDirectory=/home/brandonrozek/docker/
|
||||||
|
ExecStart=/usr/bin/docker-compose up -d
|
||||||
|
ExecStop=/usr/bin/docker-compose down
|
||||||
|
TimeoutStartSec=0
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
To enable on startup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo systemctl enable docker-compose
|
||||||
|
```
|
||||||
|
|
||||||
|
To start now
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo systemctl start docker-compose
|
||||||
|
```
|
||||||
|
|
13
content/blog/copytoram.md
Normal file
13
content/blog/copytoram.md
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
---
|
||||||
|
title: "Copy to RAM Please"
|
||||||
|
date: 2019-08-02T22:37:12-04:00
|
||||||
|
draft: false
|
||||||
|
---
|
||||||
|
|
||||||
|
I bought a 1U server recently, and I forgot to purchase a SSD to actually hold data. Since I couldn't hold my excitement to play with the server, I decided to load an operating system entirely into RAM and never turn it off for the meantime.
|
||||||
|
|
||||||
|
I browsed [List of Linux Distributoins that run from RAM](https://en.wikipedia.org/wiki/List_of_Linux_distributions_that_run_from_RAM) and found [Slax](https://www.slax.org/) a [debian](https://www.debian.org/) derivative.
|
||||||
|
|
||||||
|
It's not what I would use for my daily driver but in terms of doing this task its perfect. It loads up quickly and lets me perform nice basic user functions. It's also based on debian so I have a whole repository of packages available I can install.
|
||||||
|
|
||||||
|
Now by default, if you're on a USB it'll try to run in a persistent mode, just press ESC in the boot splash to have the option to copy to ram.
|
6
content/blog/coqgroups.md
Normal file
6
content/blog/coqgroups.md
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
title: "Group Theory in Coq"
|
||||||
|
date: 2019-05-21T22:17:00-04:00
|
||||||
|
draft: true
|
||||||
|
---
|
||||||
|
|
90
content/blog/coredns.md
Normal file
90
content/blog/coredns.md
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
---
|
||||||
|
title: "coredns"
|
||||||
|
date: 2019-12-13T02:00:29-05:00
|
||||||
|
draft: false
|
||||||
|
images: []
|
||||||
|
---
|
||||||
|
|
||||||
|
Domain names are the easiest way for a reverse proxy to split up services in a homelab. Since I'm going full docker-compose in my homelab, I decided to use `coredns`.
|
||||||
|
|
||||||
|
The server I'm hosting my services on runs Ubuntu 18.04 at the time of writing. This verison of Ubuntu comes packaged with their own DNS resolver `systemd-resolved` which runs on port 53. Therefore in order to stop this, we need to edit `/etc/systemd/resolved.conf`:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
DNS=127.0.0.1
|
||||||
|
FallbackDNS=
|
||||||
|
Domains=~.
|
||||||
|
DNSStubListener=no
|
||||||
|
```
|
||||||
|
You might be wondering about the `~.` option in Domains. From the [Arch Wiki](https://wiki.archlinux.org/index.php/Systemd-resolved), "Without the `Domains=~.` option, systemd-resolved might use the per-link DNS servers."
|
||||||
|
|
||||||
|
You also need to remove the already existing `/etc/resolv.conf` file.
|
||||||
|
```bash
|
||||||
|
sudo rm /etc/resolv.conf
|
||||||
|
```
|
||||||
|
|
||||||
|
Now to setup `coredns` I used the following docker-compose snippet:
|
||||||
|
```yaml
|
||||||
|
coredns:
|
||||||
|
image: coredns/coredns
|
||||||
|
command: -conf coredns-config/Corefile
|
||||||
|
ports:
|
||||||
|
- 53:53/udp
|
||||||
|
volumes:
|
||||||
|
- /Volumes/coredns/coredns-config:/coredns-config/
|
||||||
|
```
|
||||||
|
Noticed that I mounted the configuration directory in a volume. `coredns` is managed by a configuration file. This would be located under `/Volumes/coredns/coredns-config/Corefile` in the host system.
|
||||||
|
|
||||||
|
Let's say we own the domain example.com and want subdomains of that to point to different to our server. To do that, we need to define a zonefile for that domain name. We will save that under `/Volumes/coredns/coredns-config/homelab.db` in our host system. Every other domain we will either pass to a public DNS server like Google's `8.8.8.8` or you can even use a PiHole.
|
||||||
|
|
||||||
|
Example Corefile:
|
||||||
|
```
|
||||||
|
example.com:53 {
|
||||||
|
file /coredns-config/homelab.db
|
||||||
|
log
|
||||||
|
errors
|
||||||
|
}
|
||||||
|
|
||||||
|
.:53 {
|
||||||
|
log
|
||||||
|
errors
|
||||||
|
forward . 8.8.8.8
|
||||||
|
cache
|
||||||
|
}
|
||||||
|
```
|
||||||
|
I do recommend that whatever domain you do use for your homelab is one you own. Otherwise you might name conflict with a public service that you would want to access in the future.
|
||||||
|
|
||||||
|
The `cache` directive will cache results from the other DNS provider for quicker delivery at a later point.
|
||||||
|
|
||||||
|
The zonefile `homelab.db` could look like the following:
|
||||||
|
```
|
||||||
|
$TTL 3600
|
||||||
|
$ORIGIN example.com.
|
||||||
|
@ IN SOA ns.example.com. contact.example.com. (
|
||||||
|
2019121301 ; serial
|
||||||
|
1d ; refresh
|
||||||
|
2h ; retry
|
||||||
|
4w ; expire
|
||||||
|
1h ; nxdomain ttl
|
||||||
|
)
|
||||||
|
IN NS ns.example.com.
|
||||||
|
@ IN A 192.168.1.10
|
||||||
|
* IN A 192.168.1.10
|
||||||
|
@ IN A 10.10.100.10
|
||||||
|
* IN A 10.10.100.10
|
||||||
|
```
|
||||||
|
|
||||||
|
In the zone file, `@` means whatever the value of `$ORIGIN` is and `*` is a wildcard meaning any subdomain. You can assign a local LAN address first and then a VPN ip address if you have one set up to access services remotely.
|
||||||
|
|
||||||
|
The serial number needs only to be unique across SOA entries, and is conventionally the date and hour in which the entry was modified.
|
||||||
|
|
||||||
|
I have this setup since all my services are on one physical machine. If you want to split it up through many different machines, specify it by adding the subdomains in place of the `*` or `@`.
|
||||||
|
|
||||||
|
After the `Corefile` and `homelab.db` are made, the DNS server is all ready to be up and running. Just execute `docker-compose up -d coredns` and the DNS server will start happily responding to requests.
|
||||||
|
|
||||||
|
A good way to verify it works is to request an `A` record from your site and an external site.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dig @serverip example.com A
|
||||||
|
dig @serverip google.com A
|
||||||
|
```
|
||||||
|
|
101
content/blog/cryptogames.md
Normal file
101
content/blog/cryptogames.md
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
---
|
||||||
|
title: "Cryptographic Games"
|
||||||
|
date: 2020-01-13T21:35:09-05:00
|
||||||
|
draft: false
|
||||||
|
images: []
|
||||||
|
---
|
||||||
|
|
||||||
|
When analyzing cryptographic algorithms, we characterize the strength of the crypto-system by analyzing what happens in various crypto games. Below are a couple examples of crypto games used in literature.
|
||||||
|
|
||||||
|
Actors typically involved in crypto games:
|
||||||
|
|
||||||
|
| Actor | Role |
|
||||||
|
| ---------- | ---------------------------------------------------------- |
|
||||||
|
| Oracle | Encrypts a given message. |
|
||||||
|
| Challenger | Sets up the game between the oracle and adversary. |
|
||||||
|
| Adversary | Sends messages to the oracle and makes a guess at the end. |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Left-Right Game
|
||||||
|
|
||||||
|
Here we will have two oracle's `left` and `right` both with a random key $k$.
|
||||||
|
|
||||||
|
The challenger will create a random bit $b$ which is either $0$ or $1$.
|
||||||
|
|
||||||
|
If the bit is $0$, then the adversary will send messages to `left`. Otherwise, the adversary will send messages to `right`.
|
||||||
|
|
||||||
|
After a certain number of interactions the adversary needs to guess which oracle it is talking to.
|
||||||
|
|
||||||
|
We define the adversary's advantage by the distance the probability is away from $\frac{1}{2}$.
|
||||||
|
$$
|
||||||
|
advantage = |p - \frac{1}{2}|
|
||||||
|
$$
|
||||||
|
Why the absolute value? Well if we only guess correctly $10\%$ of the time, then we just need to invert our guess to be correct $90\%$ of the time.
|
||||||
|
|
||||||
|
```
|
||||||
|
Generate key for both oracles
|
||||||
|
Generate random bit b
|
||||||
|
while game not done:
|
||||||
|
advesary generates message m
|
||||||
|
if b = 0:
|
||||||
|
send m to left oracle
|
||||||
|
receive c from left oracle
|
||||||
|
else:
|
||||||
|
send m to right oracle
|
||||||
|
receive c from right oracle
|
||||||
|
Adversary guesses whether b = 0 or 1
|
||||||
|
```
|
||||||
|
|
||||||
|
## Real-Random Game
|
||||||
|
|
||||||
|
This game is very similar to the last one except one of the oracle's only produces random bitstrings.
|
||||||
|
|
||||||
|
An oracle is initialized with a random key $k$ and the challenger creates a random bit $b$.
|
||||||
|
|
||||||
|
If $b = 0$, then the adversary will send messages to the oracle with the proper encryption function and will receive ciphertexts. Otherwise, the adversary will send messages to the random bitstring generator.
|
||||||
|
|
||||||
|
At the end of the game, the adversary needs to guess whether its talking to a proper oracle or a random bitstring generator.
|
||||||
|
|
||||||
|
The same metric `adversary's advantage` is used to characterize the strength of a crypto-system as the last one.
|
||||||
|
|
||||||
|
```
|
||||||
|
Generate key for both oracles
|
||||||
|
Generate random bit b
|
||||||
|
while game not done:
|
||||||
|
advesary generates message m
|
||||||
|
if b = 0:
|
||||||
|
send m to proper oracle
|
||||||
|
receive c from proper oracle
|
||||||
|
else:
|
||||||
|
send m to random oracle
|
||||||
|
receive random bitstring c from random oracle
|
||||||
|
Adversary guesses whether b = 0 or 1
|
||||||
|
```
|
||||||
|
|
||||||
|
## Random Function - Random Permutation Trick
|
||||||
|
|
||||||
|
One trick that we can use in proofs is to say that two procedures are the same up to a certain point when it diverges.
|
||||||
|
|
||||||
|
Let us define $f$ to be some function that given a bitstring $X$ produces a random bitstring $Y$.
|
||||||
|
|
||||||
|
Now, $image(f)$ is initially undefined since we don't know which bitstrings will be randomly generated.
|
||||||
|
|
||||||
|
However, as we calculate $Y$ we will store it to build up $image(f)$.
|
||||||
|
|
||||||
|
At some point, we'll generate $Y$ that already exists in $image(f)$. In that moment we will split based off of whether the procedure is a random function or random permutation.
|
||||||
|
|
||||||
|
If random function, then just return the same $Y$ and end the procedure. Otherwise, return a random bitstring outside of the $range(f)$ and end the procedure.
|
||||||
|
|
||||||
|
```
|
||||||
|
Initialize function f with range(f) empty
|
||||||
|
Generate different random bitstring X
|
||||||
|
While f(X) not in range(f):
|
||||||
|
Add f(X) to range(f)
|
||||||
|
Generate different random bitstring X
|
||||||
|
If random function:
|
||||||
|
return f(X)
|
||||||
|
Else:
|
||||||
|
return random bitstring Y not in range(f)
|
||||||
|
```
|
||||||
|
|
31
content/blog/ctf.md
Normal file
31
content/blog/ctf.md
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
---
|
||||||
|
title: "Capture The Flag"
|
||||||
|
date: 2019-05-22T21:25:22-04:00
|
||||||
|
draft: false
|
||||||
|
---
|
||||||
|
|
||||||
|
There is an event in Computer Security called "Capture The Flag". The purpose is to test the skills of security engineers and students through a variety of tasks in order to get a pass phrase which is called a flag. I noticed that Dr. Andrew Marshall wanted to set up this event at the University of Mary Washington for a while, so I and a few others decided to help make this a reality for him.
|
||||||
|
|
||||||
|
**I didn't know you studied security?** Well honestly I don't. But that doesn't stop me from learning and creating cool challenges.
|
||||||
|
|
||||||
|
## Challenges I Implemented
|
||||||
|
|
||||||
|
In this post, I'll describe the various challenges I've implemented. I won't give the full problem description or answer just in case the problems get recycled in later years.
|
||||||
|
|
||||||
|
### Web Archive Search
|
||||||
|
|
||||||
|
In this problem I describe a discussion that an alumni and the student has. The alumni has trouble remembering a certain detail of an event held on campus. Key words and descriptions are given about the event and it is the job of the student to find the detail that the alumni wants.
|
||||||
|
|
||||||
|
The juicy part of this problem is that the event is no longer on the current version of the university website. This means that ideally the student would go to the [Web Archive](http://web.archive.org/) to search and find for the information.
|
||||||
|
|
||||||
|
*Funny enough, one contestant found the information by reading a really old publication from the time period described.*
|
||||||
|
|
||||||
|
### Audio Recording
|
||||||
|
|
||||||
|
In this challenge, I recorded a flag in an audio recording and distorted it using various tools in audacity. The idea is that you would get the audio clip and not be able to hear what the flag is and then have to import it into an audio editor of choice and manipulate it until the flag is clear.
|
||||||
|
|
||||||
|
*Sadly no one was able to complete this challenge.* I think I made it too hard by voicing individual characters instead of doing a phrase. This would make ambiguous letters like "B" and "P" barely distinguishable from each other.
|
||||||
|
|
||||||
|
### Hidden Text
|
||||||
|
|
||||||
|
This challenge involves hiding text in the web page through some means. Hidden Text was meant to be one of the easiest problems to ease people onto the competition. You can easily solve this type of challenge by opening up the web inspector tool.
|
32
content/blog/custom-python-repl.md
Normal file
32
content/blog/custom-python-repl.md
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
---
|
||||||
|
title: "Custom Python REPL"
|
||||||
|
date: 2019-10-27T23:43:12-04:00
|
||||||
|
draft: false
|
||||||
|
---
|
||||||
|
|
||||||
|
Are you tired of importing the same libraries and setting up the same variables? Why not just create your own custom REPL? Now of course, we're not going to do it from scratch, but instead utilize what Python already gives us.
|
||||||
|
|
||||||
|
Key Ingridient:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python -i prompt.py
|
||||||
|
```
|
||||||
|
|
||||||
|
This tells Python to run prompt.py and then take you into an interactive prompt.
|
||||||
|
|
||||||
|
Now let's populate `prompt.py`
|
||||||
|
|
||||||
|
```python
|
||||||
|
#!/bin/env python
|
||||||
|
import favorite_libraries
|
||||||
|
|
||||||
|
print("Welcome to your own custom REPL!")
|
||||||
|
print("Type help('function_name') to get a more detailed description on some of your own custom functions!")
|
||||||
|
|
||||||
|
def help(function_name):
|
||||||
|
if function_name is "help":
|
||||||
|
print("Your very own help tool!")
|
||||||
|
else:
|
||||||
|
print("Sorry we don't have a help written for that yet :(")
|
||||||
|
```
|
||||||
|
|
78
content/blog/digialmodes.md
Normal file
78
content/blog/digialmodes.md
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
---
|
||||||
|
title: "Getting started with Digital Modes in Linux"
|
||||||
|
date: 2019-09-04T09:52:21-04:00
|
||||||
|
draft: false
|
||||||
|
---
|
||||||
|
|
||||||
|
This blog post is going to describe what steps I took to be able to decode signals using digital modes. Hardware wise, you will either need a RTL-SDR receiver or a transceiver radio with a cable plugging into the computer's soundcard.
|
||||||
|
|
||||||
|
## Software Required
|
||||||
|
|
||||||
|
- Gqrx (depends on GNU Radio)
|
||||||
|
|
||||||
|
- This is if you are trying to work with RTL-SDR
|
||||||
|
- It acts as a AM/FM/CW/etc demodulator for the data sent by the equipment
|
||||||
|
|
||||||
|
- Fldigi
|
||||||
|
|
||||||
|
- This is what decodes the protocol used to package the data
|
||||||
|
- It acts as a modem and provides capability to transmit data using a transceiver
|
||||||
|
|
||||||
|
- Pavucontrol
|
||||||
|
|
||||||
|
- This connects both Gqrx and Fldigi if using RTL-SDR
|
||||||
|
|
||||||
|
As you can see, if you are only using a transceiver then you can skip the parts mentioning Gqrx and Pavucontrol
|
||||||
|
|
||||||
|
## Process
|
||||||
|
|
||||||
|
### Gqrx
|
||||||
|
|
||||||
|
When you first open up Gqrx, it'll ask you to configure the device. I have the RTL2832U, so if you have the same tool...
|
||||||
|
|
||||||
|
Select the profile partially labeled RTL2832U. If you want to look at frequencies between 0 and 28MHz then add to the device string "direct_samp=3". Otherwise don't add that string.
|
||||||
|
|
||||||
|
You can keep the rest of the settings the same.
|
||||||
|
|
||||||
|
### Fldigi
|
||||||
|
|
||||||
|
When you first open up Fldigi, it'll ask you for our operator info.
|
||||||
|
|
||||||
|
| Field | description |
|
||||||
|
| ----------------- | ----------------------------------------- |
|
||||||
|
| Station Callsign | Your callsign |
|
||||||
|
| Station QTH | City, State |
|
||||||
|
| Station Locator | Your grid code. (Can look this up on QRZ) |
|
||||||
|
| Operator Callsign | Your callsign |
|
||||||
|
| Operator Name | Your name |
|
||||||
|
| Antenna | Your antenna type |
|
||||||
|
|
||||||
|
In Audio->Devices, make sure you select the physical device if you're using a transceiver, otherwise select PulseAudio.
|
||||||
|
|
||||||
|
You can keep the rest of the settings as the default.
|
||||||
|
|
||||||
|
### Pavucontrol
|
||||||
|
|
||||||
|
Finally to link the two, run the following lines in bash to create a sink in order to push the audio from Gqrx to Fldigi.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pacmd load-module module-null-sink sink_name=MyLoopback
|
||||||
|
pacmd update-sink-proplist MyLoopback device.description=MyLoopback
|
||||||
|
pacmd load-module module-loopback sink=MyLoopback
|
||||||
|
```
|
||||||
|
|
||||||
|
Now open up `pavucontrol`. On the Playback tab, change the output device of GQRX to be "MyLoopback"
|
||||||
|
|
||||||
|
On the Recording tab, change the input of fldigi to "Monitor of Null Output"
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
Now the software is sufficiently setup to be able to receive and decode signals. Best of luck finding things out there!
|
||||||
|
|
||||||
|
## Beginner Pitfalls
|
||||||
|
|
||||||
|
Since I'm also a beginner, I will share what I got stuck on and how you can avoid it.
|
||||||
|
|
||||||
|
Make sure that you know the appropriate mode to demodulate the signal. I've had luck getting information from https://www.sigidwiki.com/wiki/Signal_Identification_Guide
|
||||||
|
|
||||||
|
-> This needs to be configured on both Gqrx and Fldigi
|
13
content/blog/discoveringhamradio.md
Normal file
13
content/blog/discoveringhamradio.md
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
---
|
||||||
|
title: "Discovering Ham Radio"
|
||||||
|
date: 2019-07-01T22:06:23-04:00
|
||||||
|
draft: false
|
||||||
|
---
|
||||||
|
|
||||||
|
I feel like Ham Radio has been hiding in the background as I move through life. I knew someone that kept a Ham radio in his car, Noah talks about it frequently on his [podcast](http://www.asknoahshow.com/), and it seemed like the next step after [playing with](https://github.com/brandon-rozek/radiotuner) [software defined radio](https://www.amazon.com/RTL-SDR-Blog-RTL2832U-Software-Telescopic/dp/B011HVUEME/ref=sr_1_3).
|
||||||
|
|
||||||
|
One of the things that I think pushed me over to wanting to get my Technician's license is the discovery of [digital data modes](http://www.arrl.org/digital-data-modes). Hams even have their own [email](https://www.winlink.org/)!
|
||||||
|
|
||||||
|
Why is this more interesting than Internet? Well Ham Radio doesn't need any physical infrastructure setup (other than your radio and maybe some repeaters) to get you connected to other people. In fact, Hams often [volunteer](http://www.arrl.org/public-service) in natural disasters when the phone systems are down.
|
||||||
|
|
||||||
|
This convinced to buy a [study guide](https://www.amazon.com/ARRL-Radio-License-Manual-Spiral/dp/1625950829/ref=sr_1_6) and start preparing to take my technicians test this summer. If you're interested in Ham Radio, please contact me at my email on my home page :)
|
48
content/blog/docker-image-bash.md
Normal file
48
content/blog/docker-image-bash.md
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
---
|
||||||
|
title: "Advanced Docker Image Construction with Bash"
|
||||||
|
date: 2019-12-26T21:01:37-05:00
|
||||||
|
draft: false
|
||||||
|
images: []
|
||||||
|
---
|
||||||
|
|
||||||
|
On current versions of Docker, you can't mount volumes during image construction. This poses an issue for me as I don't want to replicate gigabytes of data already existing on my disk when it won't appear on the final build. Therefore, instead of building an image with a traditional Dockerfile, we're going to use a bash script on a running base image container and export the filesystem to create the image from.
|
||||||
|
|
||||||
|
So first run the base image with the mounts that you want
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run -v /mnt:/mnt -td --name containername baseimage /bin/bash
|
||||||
|
```
|
||||||
|
|
||||||
|
Then copy whatever `setup` script you have and execute it on the running container
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker cp setup containername:/setup
|
||||||
|
docker exec -it containername /setup
|
||||||
|
```
|
||||||
|
|
||||||
|
Once the setup script finalizes, we can export the container filesystem into a file called `image.tar`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker export --output="image.tar" containername
|
||||||
|
```
|
||||||
|
|
||||||
|
Once we've exported the filesystem, we can get rid of the existing container
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker stop containername && docker rm containername
|
||||||
|
```
|
||||||
|
|
||||||
|
Now create a `Dockerfile` with the following:
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
FROM scratch
|
||||||
|
ADD image.tar /
|
||||||
|
CMD ["bin/bash"]
|
||||||
|
```
|
||||||
|
|
||||||
|
Now you can create the image by building the Dockerfile
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker build -t finalimagename .
|
||||||
|
```
|
||||||
|
|
18
content/blog/externalmediaformats.md
Normal file
18
content/blog/externalmediaformats.md
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
---
|
||||||
|
title: "External Media Formats"
|
||||||
|
date: 2019-05-22T22:03:38-04:00
|
||||||
|
draft: false
|
||||||
|
---
|
||||||
|
|
||||||
|
I received an external SSD recently and I decided that it would be a great place to offload some of my backups. Before I got started, I became curious as to what filesystem to put on the SSD. After some research, it seems that if I want to be able to access it using Windows I am actually quite limited. In fact only three make sense:
|
||||||
|
|
||||||
|
- FAT32
|
||||||
|
- exFAT
|
||||||
|
- NTFS
|
||||||
|
|
||||||
|
Now FAT32 is a legacy filesystem, Windows 95 type of legacy. So let's rule that one out. exFAT is supposed to be based on FAT32 but increases the file size limits significantly. It is also known for being very slim and as such does not having journaling support. NTFS is the filesystem that Windows 10 uses for default for it's installation. That file system supports journaling.
|
||||||
|
|
||||||
|
I ended up going with NTFS due to its journaling support. Why is journaling important to me? Well this helps keep the file system internally consistent. The details are outside the scope of this post, but [Wikipedia has a nice entry](https://en.wikipedia.org/wiki/Journaling_file_system) about it.
|
||||||
|
|
||||||
|
Well as it turns out, the drive that I was going to use already was formatted as NTFS. Though for sake of completeness, I will say that you can format drives with tools like `parted`, `gparted`, and `partitionmanager`. Just make sure that the drive you're formatting is what you think it is!
|
||||||
|
|
26
content/blog/fmradiotuner.md
Normal file
26
content/blog/fmradiotuner.md
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
---
|
||||||
|
title: "FM Radio Tuner"
|
||||||
|
date: 2019-05-21T22:08:54-04:00
|
||||||
|
draft: true
|
||||||
|
---
|
||||||
|
|
||||||
|
I recently bought a [RTL-SDR Receiver](https://www.amazon.com/RTL-SDR-Blog-RTL2832U-Software-Telescopic/dp/B011HVUEME/ref=sr_1_3) off of Amazon to get started with [Software Defined Radio](https://en.wikipedia.org/wiki/Software-defined_radio). To start off, I thought that it would be great if I can write a fm receiver.
|
||||||
|
|
||||||
|
To begin you will need to install `gnuradio` and follow the following [instructions](https://osmocom.org/projects/rtl-sdr/wiki/Rtl-sdr).
|
||||||
|
|
||||||
|
## GNURadio
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## QT
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Modifying GNURadio Python Script
|
||||||
|
|
||||||
|
|
||||||
|
|
9
content/blog/gitlabcicd.md
Normal file
9
content/blog/gitlabcicd.md
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
---
|
||||||
|
title: "Gitlab CI/CD"
|
||||||
|
date: 2019-07-01T21:36:56-04:00
|
||||||
|
draft: false
|
||||||
|
---
|
||||||
|
|
||||||
|
One of the greatest benefits I believe of integrating Gitlab's CI/CD into your workload is code linting and building. This immediately eliminates the easy mistakes that developers make and wastes less people's time.
|
||||||
|
|
||||||
|
I'm working towards getting the build setup just right in one of the projects I'm currently working. However, in the meantime, Gitlab supports running it's CI/CD jobs under the `shell executor`. I would like to go to the default Docker route in the future once I figure out all of the dependencies of the project I'm building.
|
12
content/blog/gitlens.md
Normal file
12
content/blog/gitlens.md
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
---
|
||||||
|
|
||||||
|
title: "Git Lens"
|
||||||
|
date: 2019-10-27T23:38:45-04:00
|
||||||
|
draft: false
|
||||||
|
---
|
||||||
|
|
||||||
|
Git Lens is a great Visual Studio code extension. It annotates the line your cursor is on with the commit it came from. Extreemely useful for determining the reason that line of code is written or even who wrote it.
|
||||||
|
|
||||||
|
Images from the github page. (If they still exist when you read this)
|
||||||
|
|
||||||
|
![Current Line Blame](https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/images/docs/current-line-blame.png)![Git Code Lens](https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/images/docs/code-lens.png)
|
38
content/blog/groupssimplified.md
Normal file
38
content/blog/groupssimplified.md
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
---
|
||||||
|
title: "Groups Simplified"
|
||||||
|
date: 2019-12-10T21:40:00-05:00
|
||||||
|
draft: false
|
||||||
|
images: []
|
||||||
|
---
|
||||||
|
|
||||||
|
This post is inspired by the book "Term Rewriting & All That" by Franz Baader and Tobias Nipkow.
|
||||||
|
|
||||||
|
Let us have a set $G$ together with a binary operation $*$. We will use multiplicative notation throughout meaning $ab = a * b$. Let $x, y, z \in G$. If $\langle G , * \rangle$ has the following properties:
|
||||||
|
|
||||||
|
1. $(x y)z = x (y z)$
|
||||||
|
2. $ex = x$
|
||||||
|
3. $x^{-1} x = e$
|
||||||
|
|
||||||
|
for some fixed $e \in G$, then we say that $\langle G, * \rangle$ is a group. In class, we needed to show that $xe = x$ and $xx^{-1} = e$. However, these can be derived by the prior properties.
|
||||||
|
|
||||||
|
### Prove $xx^{-1} = e$
|
||||||
|
\begin{align*}
|
||||||
|
e &= (xx^{-1})^{-1}(x x^{-1}) \\\\
|
||||||
|
&= (xx^{-1})^{-1} (x (ex^{-1})) \\\\
|
||||||
|
&= (xx^{-1})^{-1} (x ((x^{-1} x) x^{-1})) \text{ ----- (A)} \\\\
|
||||||
|
&= (x x^{-1})^{-1} (x (x^{-1} x)x^{-1}) \\\\
|
||||||
|
&= (x x^{-1})^{-1}((x x^{-1})xx^{-1}) \\\\
|
||||||
|
&= (x x^{-1})^{-1} ((xx^{-1}) (x x^{-1})) \\\\
|
||||||
|
&= ((x x^{-1})^{-1}(x x^{-1})) (x x^{-1}) \\\\
|
||||||
|
&= e(xx^{-1}) \\\\
|
||||||
|
&= xx^{-1}
|
||||||
|
\end{align*}
|
||||||
|
### Prove $xe = x$
|
||||||
|
|
||||||
|
Once we showed $xx^{-1} = e$, the proof of $xe = e$ is simple.
|
||||||
|
\begin{align*}
|
||||||
|
x &= ex \\\\
|
||||||
|
&= (xx^{-1})x \\\\
|
||||||
|
&= x(x^{-1}x) \\\\
|
||||||
|
&= xe
|
||||||
|
\end{align*}
|
49
content/blog/haskellrealsequences.md
Normal file
49
content/blog/haskellrealsequences.md
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
---
|
||||||
|
title: "Real Analysis Sequences in Haskell"
|
||||||
|
date: 2019-05-21T22:18:21-04:00
|
||||||
|
draft: false
|
||||||
|
---
|
||||||
|
|
||||||
|
In Real Analysis it is useful to look at terms of a sequence. One of the best ways I've found to do this is in believe it or not Haskell. This is mainly for these two reasons
|
||||||
|
|
||||||
|
- Support for infinite data structures
|
||||||
|
|
||||||
|
- Built-in Data Type to keep fractional precision
|
||||||
|
|
||||||
|
## Code
|
||||||
|
|
||||||
|
Let's get started, first let us define a sequence by the following:
|
||||||
|
$$
|
||||||
|
f(1) = 1, f(2) = 2, f(n) = \frac{1}{2}(f(n - 2) + f(n - 1))
|
||||||
|
$$
|
||||||
|
That is equivalent to the following haskell code:
|
||||||
|
|
||||||
|
```haskell
|
||||||
|
f :: Integral a => a -> Ratio a
|
||||||
|
f 1 = 1
|
||||||
|
f 2 = 2
|
||||||
|
f n = 0.5 * (f (n - 2) + f (n - 1))
|
||||||
|
```
|
||||||
|
|
||||||
|
Now to generate the sequence we just need to map $f$ onto the natural numbers.
|
||||||
|
|
||||||
|
```haskell
|
||||||
|
nsequence = map f [1..]
|
||||||
|
```
|
||||||
|
|
||||||
|
If you want to look at specific subsequences, such as even or odd:
|
||||||
|
|
||||||
|
```haskell
|
||||||
|
odd_generator n = 2 * n - 1
|
||||||
|
odds = map odd_generator [1..]
|
||||||
|
|
||||||
|
even_generator n = 2 * n
|
||||||
|
evens = map odd_generator [1..]
|
||||||
|
```
|
||||||
|
|
||||||
|
To look at the differences between each term:
|
||||||
|
|
||||||
|
```haskell
|
||||||
|
diff x = map (\(a, b) -> a - b) $ zip (tail x) (init x)
|
||||||
|
```
|
||||||
|
|
37
content/blog/limitbandwidth.md
Normal file
37
content/blog/limitbandwidth.md
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
---
|
||||||
|
title: "Limit Bandwidth through Terminal"
|
||||||
|
date: 2020-01-15T19:51:45-05:00
|
||||||
|
draft: false
|
||||||
|
images: []
|
||||||
|
---
|
||||||
|
|
||||||
|
Have you ever wondered how an application or a system would operate under low bandwidth environments? Luckily `wondershaper` is a tool to help with just that!
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt install wondershaper
|
||||||
|
```
|
||||||
|
|
||||||
|
To get started, first find out the network `interface` that you want to throttle.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ip addr show
|
||||||
|
```
|
||||||
|
|
||||||
|
To show the state of the interface,
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo wondershaper [interface]
|
||||||
|
```
|
||||||
|
|
||||||
|
To set the bandwidth,
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo wondershaper [interface] [downlink] [uplink]
|
||||||
|
```
|
||||||
|
where downlink and uplink are defined in kilobits per second.
|
||||||
|
|
||||||
|
To clear the rules,
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo wondershaper clear [interface]
|
||||||
|
```
|
21
content/blog/linuxdesktopicons.md
Normal file
21
content/blog/linuxdesktopicons.md
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
---
|
||||||
|
title: "Linux Desktop Icons"
|
||||||
|
date: 2019-06-03T21:05:37-04:00
|
||||||
|
draft: false
|
||||||
|
---
|
||||||
|
|
||||||
|
I get asked a decent number of times how to add desktop icons on Linux. Luckily it's incredibly easy. [It's a `freedesktop` standard](http://standards.freedesktop.org/desktop-entry-spec/latest/).
|
||||||
|
|
||||||
|
In fact the simplest file would follow the format:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
[Desktop Entry]
|
||||||
|
Name=Application Name
|
||||||
|
Exec=/path/to/executable -randomFlag
|
||||||
|
Icon=/optional/path/to/icon
|
||||||
|
Terminal=false
|
||||||
|
Type=Application
|
||||||
|
```
|
||||||
|
|
||||||
|
Once you have this saved to `yourapplication.desktop` move it to either `/usr/share/applications` if you want it system-wide or `/home/user/.local/share/applications` if you want it just for your `user`.
|
||||||
|
|
21
content/blog/livedoc.md
Normal file
21
content/blog/livedoc.md
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
---
|
||||||
|
title: "Live Documentation"
|
||||||
|
date: 2019-09-27T23:07:19-04:00
|
||||||
|
draft: false
|
||||||
|
---
|
||||||
|
|
||||||
|
This blog post is mostly for one of my teams in which I use Jupyter Notebooks for documentation. Perhaps after reading this post, you the reader can understand why it might be beneficial to use Jupyter Notebooks as a form of documentation.
|
||||||
|
|
||||||
|
## Why?
|
||||||
|
|
||||||
|
So why Jupyter Notebooks?
|
||||||
|
|
||||||
|
- Follows the literate programming approach. You can write text explaining a feature and then immediately show code and it's result.
|
||||||
|
- It's modifiable. If your user wants to play around with the documentation, the environment is set up for them to do so.
|
||||||
|
- It's exportable. Let's say another user doesn't want to bother setting it up. Well it's super simple to just export the notebook as a PDF and send that to them instead.
|
||||||
|
|
||||||
|
## Setting up
|
||||||
|
|
||||||
|
Jupyter Notebooks are part of the [Project Jupyter](https://jupyter.org/) suite of products. You can install it via a `pip` package, but it is more commonly installed via the [Anaconda Distribution](https://www.anaconda.com/)
|
||||||
|
|
||||||
|
Once you have that installed, run `jupyter lab` in the directory that you wish to execute code from. You might need to be in the `bash` shell for this to work since the installer modifies those environmental variables.
|
29
content/blog/localrepoiso.md
Normal file
29
content/blog/localrepoiso.md
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
---
|
||||||
|
title: "Local Repo From Live Installer"
|
||||||
|
date: 2019-08-30T20:26:53-04:00
|
||||||
|
draft: false
|
||||||
|
---
|
||||||
|
|
||||||
|
I'm going to share my experience setting up a local repo from a CentOS live CD. These instructions should work similarly in REHL.
|
||||||
|
|
||||||
|
When working with off-network systems, it is often useful to have the base packages on the system after the installation process. Luckily the base packages are commonly included in the ISO installer.
|
||||||
|
|
||||||
|
First I'm going to assume that you've mounted the ISO at `/mnt`.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo mkdir -p /repos/local
|
||||||
|
sudo rsync -Paz /mnt/Packages /repos/local/Packages
|
||||||
|
sudo rsync -Paz /mnt/repodata /repos/local/repodata
|
||||||
|
```
|
||||||
|
|
||||||
|
Then create the file `/etc/yum.repos.d/local.repo`
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
[local]
|
||||||
|
name=Local Repo
|
||||||
|
baseurl=file:///repos/local/
|
||||||
|
gpgcheck=0
|
||||||
|
enabled=1
|
||||||
|
```
|
||||||
|
|
||||||
|
Now you're all set to go with the packages on the Live CD!
|
102
content/blog/lxdtmpfs.md
Normal file
102
content/blog/lxdtmpfs.md
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
---
|
||||||
|
title: "LXD on tmpfs"
|
||||||
|
date: 2019-12-31T22:35:21-05:00
|
||||||
|
draft: false
|
||||||
|
images: []
|
||||||
|
---
|
||||||
|
|
||||||
|
Container images are designed to be as small as possible. Wouldn't it be cool if we can hold entire containers in RAM? This post outlines how to accomplish this using LXD. It turns out that it is a lot easier to setup custom storage pools on LXD than with Docker.
|
||||||
|
|
||||||
|
## Setting up tmpfs
|
||||||
|
|
||||||
|
`tmpfs` is a temporary filesystem that resides in memory. To get started, first create a directory that you want to mount with `tmpfs`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir /tmp/ramdisk
|
||||||
|
```
|
||||||
|
|
||||||
|
To only do this temporarily, then you can run the following command
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo mount -t tmpfs -o size=4G myramdisk /tmp/ramdisk
|
||||||
|
```
|
||||||
|
|
||||||
|
You can replace `4G` with any size less than your current RAM size.
|
||||||
|
|
||||||
|
To set it up permanently, you will have to edit `/etc/fstab`
|
||||||
|
|
||||||
|
```
|
||||||
|
myramdisk /tmp/ramdisk tmpfs defaults,size=3G,x-gvfs-show 0 0
|
||||||
|
```
|
||||||
|
|
||||||
|
## Setting up LXD
|
||||||
|
|
||||||
|
LXD is a lightweight container hypervisor in Linux. To install and setup, please follow the beginning sections of https://linuxcontainers.org/lxd/getting-started-cli/
|
||||||
|
|
||||||
|
Once you've setup the initial configuration with `lxd init`, we can create a new storage pool using our newly created `tmpfs`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
lxc storage create tmplabel dir source=/tmp/ramdisk
|
||||||
|
```
|
||||||
|
|
||||||
|
We can then create a container that will use this storage pool
|
||||||
|
|
||||||
|
```bash
|
||||||
|
lxc launch ubuntu:18.04 mycontainer -s tmplabel
|
||||||
|
```
|
||||||
|
|
||||||
|
In the last command, the `-s` flag indicates which storage pool we want to use.
|
||||||
|
|
||||||
|
With this, our entire container filesystem lives in RAM!
|
||||||
|
|
||||||
|
## (Optional) Setting up mounts
|
||||||
|
|
||||||
|
One downside to putting the entire filesystem in RAM is that it isn't persistent across reboots. You can think of the container then as ephemeral and setup mountpoints to the host in order to save important configuration information.
|
||||||
|
|
||||||
|
If the mount point is configured to be world writable or you only need it to be readonly, then this is very simple to setup!
|
||||||
|
|
||||||
|
```bash
|
||||||
|
lxc config device add mycontainer dirlabel disk source=/path/on/host path=/path/on/container
|
||||||
|
```
|
||||||
|
|
||||||
|
This is because files in the container are marked as `nobody:nogroup`. If you want to be able to write to the mounted directory that's not setup to be world-writable then there's extra steps we need to take.
|
||||||
|
|
||||||
|
Most of the following information is taken from: https://tribaal.io/nicer-mounting-home-in-lxd.html
|
||||||
|
|
||||||
|
Let's say that you want LXD to be able to write to a folder that you own. First we need to allow LXD to remap your user ID.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
echo "root:$UID:1" | sudo tee -a /etc/subuid /etc/subgid
|
||||||
|
```
|
||||||
|
|
||||||
|
We only need to do this once per host system.
|
||||||
|
|
||||||
|
The Ubuntu container has a user called `ubuntu` with UID 1000. We can remap that userid, otherwise you would have to create another user on the container to remap.
|
||||||
|
|
||||||
|
If you create another user, make sure you get its id for the next command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# In the container
|
||||||
|
id -u username
|
||||||
|
```
|
||||||
|
|
||||||
|
For the rest of this tutorial, we will assume that you have a user named ubuntu with UID 1000.
|
||||||
|
|
||||||
|
Once LXD is able to remap your user id, you can tell it to do so for the container of interest.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
lxc config set mycontainer raw.idmap "both $UID 1000"
|
||||||
|
```
|
||||||
|
|
||||||
|
Now to setup the mount
|
||||||
|
|
||||||
|
```bash
|
||||||
|
lxc config device add myubuntu homedir disk source=$HOME path=/home/ubuntu
|
||||||
|
```
|
||||||
|
|
||||||
|
Restart the container and now the `ubuntu` user will be able to write to the mount!
|
||||||
|
|
||||||
|
```bash
|
||||||
|
lxc restart mycontainer
|
||||||
|
```
|
||||||
|
|
|
@ -0,0 +1,208 @@
|
||||||
|
---
|
||||||
|
id: 2169
|
||||||
|
title: Male vs Female Life Expectancy
|
||||||
|
date: 2017-03-16T14:12:40+00:00
|
||||||
|
author: rozek_admin
|
||||||
|
layout: revision
|
||||||
|
guid: https://brandonrozek.com/2017/03/2052-revision-v1/
|
||||||
|
permalink: /2017/03/2052-revision-v1/
|
||||||
|
---
|
||||||
|
![](https://brandonrozek.com/wp-content/uploads/2017/03/LifeExpectancyBoxplot.png)
|
||||||
|
|
||||||
|
## Do females live longer than males?
|
||||||
|
|
||||||
|
It is well known that females live longer than males, but does that statement hold statistically? Matthew Martinez and I set out to find out.
|
||||||
|
|
||||||
|
<!--more-->
|
||||||
|
|
||||||
|
## Population and the hypothesis
|
||||||
|
|
||||||
|
Our population of concern is citizens of the United States. We found a dataset on [WorldLifeExpectancy](http://www.worldlifeexpectancy.com/) listing by county the average life expectancy for both males and females. With this we form our null and alternative hypothesis
|
||||||
|
|
||||||
|
H0: The average life expectancy for both males and females are the same in the United States
|
||||||
|
|
||||||
|
HA: The average female life expectancy is higher than the average male life expectancy in the United States
|
||||||
|
|
||||||
|
## Data preparation
|
||||||
|
|
||||||
|
Since the website gives us an overlook at all of the counties in the United States we want to take a small sample of that so we can perform statistics. Using the entire dataset will result in looking at population parameters which doesn’t leave room for inference.
|
||||||
|
|
||||||
|
A random number was chosen to pick the state and then the county. This was done a total of 101 times. The CSV file is located [here](https://brandonrozek.com/wp-content/uploads/2017/03/LifeExpectancy.csv) for convenience.
|
||||||
|
|
||||||
|
## R Programming
|
||||||
|
|
||||||
|
For the rest of this article, we will use R for analysis. This article will focus more on the analysis, however, than the R code.
|
||||||
|
|
||||||
|
Read the CSV file into R
|
||||||
|
|
||||||
|
<pre class="language-R"><code class="language-R">rm(list=ls())
|
||||||
|
|
||||||
|
# Read in file
|
||||||
|
LifeExpectancy = read.csv("~/LifeExpectancy.csv")
|
||||||
|
maleExpectancy = LifeExpectancy$Life.Expectancy.Male
|
||||||
|
femaleExpectancy = LifeExpectancy$Life.Expectancy.Female
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
## Summary Statistics
|
||||||
|
|
||||||
|
Before we begin our inferential statistics, it is a good idea to look at what we have in our sample. It will give us a good feeling for what we’re working with and help us answer some questions involving the assumptions in parametric tests.
|
||||||
|
|
||||||
|
We’re interested in the minimum, mean, maximum, and interquartile range of the data
|
||||||
|
|
||||||
|
<pre class="language-R"><code class="language-R">
|
||||||
|
# Summary statistics
|
||||||
|
male_row = c(min(maleExpectancy), mean(maleExpectancy), max(maleExpectancy), IQR(maleExpectancy))
|
||||||
|
female_row = c(min(femaleExpectancy), mean(femaleExpectancy), max(femaleExpectancy), IQR(femaleExpectancy))
|
||||||
|
summary = rbind(male_row, female_row)
|
||||||
|
colnames(summary) = c("Min", "Mean", "Max", "IQR")
|
||||||
|
rownames(summary) = c("Male", "Female")
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
Looking at the table below, we can see that the average male lives to be around 69 years old in our sample while the average female lives to be about 71 years old. One interesting thing to note is how small the variation is between all the counties life expectancy that we sampled.
|
||||||
|
|
||||||
|
<pre class="language-R"><code class="language-R">summary
|
||||||
|
## Min Mean Max IQR
|
||||||
|
## Male 69.0 74.952 80.9 2.775
|
||||||
|
## Female 76.1 80.416 84.1 2.350</code></pre>
|
||||||
|
|
||||||
|
## Inferential Statistics
|
||||||
|
|
||||||
|
From here on out, we will perform a hypothesis test on the two hypothesis stated earlier in the text.
|
||||||
|
|
||||||
|
Since our data is quantitative in nature, we will attempt to perform a two sample t-test
|
||||||
|
|
||||||
|
### Check for Assumptions
|
||||||
|
|
||||||
|
Performing a t-test comes with several assumptions we need to check before confidently reporting our results.
|
||||||
|
|
||||||
|
<u>Independence Condition:</u> One county’s life span does not affect the lifespan of another.
|
||||||
|
|
||||||
|
<u>Independent groups assumption:</u> The lifespan of a male does not directly impact a lifespan of a female.
|
||||||
|
|
||||||
|
<u>Nearly Normal Condition:</u> We need to check the histograms to see if they’re unimodal and symmetric and check to see if any outliers exist
|
||||||
|
|
||||||
|
The male life expectancy distribution appears to be unimodal and symmetric.
|
||||||
|
|
||||||
|
<pre class="language-R"><code class='language-R'># Check for normality
|
||||||
|
hist(maleExpectancy, main = "Male Life Expectancy", xlab = "Age")</code></pre>
|
||||||
|
|
||||||
|
<img src="https://brandonrozek.com/wp-content/uploads/2017/03/maleLifeExpectancyHist.png" width="672" />
|
||||||
|
|
||||||
|
Same with the female life expectancy distribution
|
||||||
|
|
||||||
|
<pre class="language-R"><code class="language-R">hist(femaleExpectancy, main = "Female Life Expectancy", xlab = "Age")</code></pre>
|
||||||
|
|
||||||
|
<img src="https://brandonrozek.com/wp-content/uploads/2017/03/femaleLifeExpectancyHist.png" width="672" />
|
||||||
|
|
||||||
|
Looking at the boxplot, we can see that the IQR of the female life expectancy is higher than the one of the males. The hypothesis test will show us if this is of significant difference. On the male’s side there are two outliers. This violates the Nearly Normal Condition so we must proceed with caution in our test.
|
||||||
|
|
||||||
|
<pre class="language-R"><code class="language-R">boxplot(maleExpectancy, femaleExpectancy, names = c("Male Life Expectancy", "Female Life
|
||||||
|
Expectancy"), ylab = "Age")</code></pre>
|
||||||
|
|
||||||
|
<img src="https://brandonrozek.com/wp-content/uploads/2017/03/LifeExpectancyBoxplot.png" width="672" />
|
||||||
|
|
||||||
|
Since the nearly normal condition was not met, we do not meet the assumptions necessary to perform a t-test. However, since the condition was violated by an outlier, let us perform a t-test with the outlier and without the outlier and compare the results.
|
||||||
|
|
||||||
|
### Calculate the Test Statistic
|
||||||
|
|
||||||
|
Let us conduct a two sample t-test with the alternative hypothesis being that the female average life expectancy is greater than that of the males
|
||||||
|
|
||||||
|
Running the test below shoes us a p-value of less than 0.001. This tells us that the probability of obtaining a sample as extreme as the one obtained is close to zero. Therefore at a significance level of 5%, we reject the null hypothesis and state that there is strong evidence to suggest that females have a greater life expectancy that that of males.
|
||||||
|
|
||||||
|
<pre class="language-R"><code class="language-R"># Test alternative hypothesis
|
||||||
|
t.test(femaleExpectancy, maleExpectancy, alternative='g')</code></pre>
|
||||||
|
|
||||||
|
<pre class="language-R"><code class="language-R">##
|
||||||
|
## Welch Two Sample t-test
|
||||||
|
##
|
||||||
|
## data: femaleExpectancy and maleExpectancy
|
||||||
|
## t = 18.858, df = 182.48, p-value < 2.2e-16
|
||||||
|
## alternative hypothesis: true difference in means is greater than 0
|
||||||
|
## 95 percent confidence interval:
|
||||||
|
## 4.984992 Inf
|
||||||
|
## sample estimates:
|
||||||
|
## mean of x mean of y
|
||||||
|
## 80.416 74.952</code></pre>
|
||||||
|
|
||||||
|
In fact, we are 95% confident that the difference between the average female life expectancy and the average male life expectancy in the United States is between 5 and 6 years. Females live on average 5-6 years longer than males in the United States.
|
||||||
|
|
||||||
|
<code class="language-R"># Find confidence interval<br />
|
||||||
|
t.test(femaleExpectancy, maleExpectancy)</code>
|
||||||
|
|
||||||
|
<pre class="language-R"><code class="language-R">##
|
||||||
|
## Welch Two Sample t-test
|
||||||
|
##
|
||||||
|
## data: femaleExpectancy and maleExpectancy
|
||||||
|
## t = 18.858, df = 182.48, p-value < 2.2e-16
|
||||||
|
## alternative hypothesis: true difference in means is not equal to 0
|
||||||
|
## 95 percent confidence interval:
|
||||||
|
## 4.892333 6.035667
|
||||||
|
## sample estimates:
|
||||||
|
## mean of x mean of y
|
||||||
|
## 80.416 74.952</code></pre>
|
||||||
|
|
||||||
|
### Outlier Analysis
|
||||||
|
|
||||||
|
We cannot forget that we had outliers in our dataset. This might affect the results of our test. The point of outlier analysis is to see if such changes are significant.
|
||||||
|
|
||||||
|
First let us remove the outliers in R
|
||||||
|
|
||||||
|
<pre class="language-R"><code class="language-R"># Remove outliers
|
||||||
|
maleExpectancy2 = maleExpectancy[!maleExpectancy %in% boxplot.stats(maleExpectancy)$out]
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
Then let us check the histogram and boxplots to see if the nearly normal condition is now met.
|
||||||
|
|
||||||
|
Looking at the boxplot, there are no more outliers present
|
||||||
|
|
||||||
|
<pre class="language-R"><code class="language-R">
|
||||||
|
# Check graphs again
|
||||||
|
boxplot(maleExpectancy2, ylab = "Age", main = "Male Life Expectancy w/o Outliers")</code></pre>
|
||||||
|
|
||||||
|
<img src="https://brandonrozek.com/wp-content/uploads/2017/03/MLifeExpectBoxplotNoOutliers.png" width="672" />
|
||||||
|
|
||||||
|
The histogram still appears to be unimodal and symmetric
|
||||||
|
|
||||||
|
<pre class="language-R"><code class="language-R">hist(maleExpectancy2, xlab = "Age", main = "Male Life Expectancy w/o Outliers")</code></pre>
|
||||||
|
|
||||||
|
<img src="https://brandonrozek.com/wp-content/uploads/2017/03/MLifeExpectHistNoOutliers.png" width="672" />
|
||||||
|
|
||||||
|
Without the outliers present, the nearly normal condition is now met. We can perform the t-test.
|
||||||
|
|
||||||
|
We can see that the hypothesis test returns the same results as before, this tells us that the outliers did not have a significant impact on our test results
|
||||||
|
|
||||||
|
<pre class="language-R"><code class="language-R"># Test new alternative
|
||||||
|
t.test(femaleExpectancy, maleExpectancy2, alternative='g')</code></pre>
|
||||||
|
|
||||||
|
<pre class="language-R"><code class="language-R">##
|
||||||
|
## Welch Two Sample t-test
|
||||||
|
##
|
||||||
|
## data: femaleExpectancy and maleExpectancy2
|
||||||
|
## t = 19.471, df = 184.03, p-value < 2.2e-16
|
||||||
|
## alternative hypothesis: true difference in means is greater than 0
|
||||||
|
## 95 percent confidence interval:
|
||||||
|
## 5.000048 Inf
|
||||||
|
## sample estimates:
|
||||||
|
## mean of x mean of y
|
||||||
|
## 80.41600 74.95204</code></pre>
|
||||||
|
|
||||||
|
Redoing the confidence intervals, we can see that it did not change greatly
|
||||||
|
|
||||||
|
<pre class="language-R"><code class="language-R"># Find new confidence interval
|
||||||
|
t.test(femaleExpectancy, maleExpectancy2)</code></pre>
|
||||||
|
|
||||||
|
<pre class="language-R"><code class="language-R">##
|
||||||
|
## Welch Two Sample t-test
|
||||||
|
##
|
||||||
|
## data: femaleExpectancy and maleExpectancy2
|
||||||
|
## t = 19.471, df = 184.03, p-value < 2.2e-16
|
||||||
|
## alternative hypothesis: true difference in means is not equal to 0
|
||||||
|
## 95 percent confidence interval:
|
||||||
|
## 4.910317 6.017601
|
||||||
|
## sample estimates:
|
||||||
|
## mean of x mean of y
|
||||||
|
## 80.41600 74.95204</code></pre>
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
By running the tests and checking the effects of the outliers in the dataset and seeing that the results did not change, we can safely conclude that our interpretations stated before are correct. There is enough evidence to suggest that females in the United States live on average longer than males. We are 95% confident that they live longer than males by 5 to 6 years.
|
58
content/blog/manpandoc.md
Normal file
58
content/blog/manpandoc.md
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
---
|
||||||
|
title: "Man Pages with Pandoc"
|
||||||
|
date: 2019-11-07T21:42:08-05:00
|
||||||
|
draft: false
|
||||||
|
---
|
||||||
|
|
||||||
|
[Ethan Martin](https://emar10.dev) recently showed off the Pandoc tool to me. In case you don't know, Pandoc is a swiss-army knife of markup languages. It allows you to easily convert from one markup language to another.
|
||||||
|
|
||||||
|
This got me thinking my love for Markdown as the lowest common denominator for my notes. I can write markdown files for my website, for presentations, and output it into PDF and Word. Of course you don't get to have the fine control as you do with Latex, but with document classes, you can at least theme it any way you like!
|
||||||
|
|
||||||
|
Now to focus this blog post, I do want to share one really cool feature I found out about Pandoc. It can create man pages. I thought of this as a great way to create documentation for a software project. I'll give you an example to kick things off.
|
||||||
|
|
||||||
|
Note: The content is stolen from the `git` man page.
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
---
|
||||||
|
title: git
|
||||||
|
section: 1
|
||||||
|
header: Git Manual
|
||||||
|
---
|
||||||
|
|
||||||
|
# NAME
|
||||||
|
git - the stupid content tracker
|
||||||
|
|
||||||
|
# SYNOPSIS
|
||||||
|
git `[--version] [--help]`
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
Git is a fast, scalable, distributed revision control system with an unusually rich command set that provides both high-level operations and full access to internals.
|
||||||
|
|
||||||
|
See **gittutorial**(7) to get started, then see **giteveryday**(7) for a useful minimum set of commands. The **Git User’s Manual**[1] has a more in-depth introduction.
|
||||||
|
|
||||||
|
After you mastered the basic concepts, you can come back to this page to learn what commands Git offers. You can learn more about individual Git commands with "git help command". **gitcli**(7) manual page gives you an overview of the command-line command syntax.
|
||||||
|
|
||||||
|
A formatted and hyperlinked copy of the latest Git documentation can be viewed at **https://git.github.io/htmldocs/git.html**.
|
||||||
|
|
||||||
|
# OPTIONS
|
||||||
|
--version
|
||||||
|
Prints the Git suite version that the git program came from.
|
||||||
|
|
||||||
|
--help
|
||||||
|
Prints the synopsis and a list of the most commonly used commands. If the option --all or -a is given then all available commands are printed. If a Git command is named this option will bring up the manual page for that command.
|
||||||
|
|
||||||
|
Other options are available to control how the manual page is displayed. See git-help(1) for more information, because git --help ... is converted internally into git help ....
|
||||||
|
```
|
||||||
|
|
||||||
|
Then execute
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pandoc -s -t man -o git.1 git.md
|
||||||
|
```
|
||||||
|
|
||||||
|
Here's what the command means: "Run `pandoc` on standalone mode converting `git.md` into a man page called `git.1`"
|
||||||
|
|
||||||
|
To see the results run `man ./git.1`
|
||||||
|
|
||||||
|
![1573183066061](/files/images/1573183066061.png)
|
||||||
|
|
20
content/blog/memoryerrorsgo.md
Normal file
20
content/blog/memoryerrorsgo.md
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
---
|
||||||
|
title: "Memory Errors in Go"
|
||||||
|
date: 2019-08-02T22:35:53-04:00
|
||||||
|
draft: false
|
||||||
|
---
|
||||||
|
|
||||||
|
I enjoy playing with Valgrind. Sometimes I view it as a game to get rid of memory errors. When I wrote a go script, I noticed that I received a lot of memory errors. I decided to double check by writing a simple hello world program in Go.
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
import "fmt"
|
||||||
|
func main() {
|
||||||
|
fmt.Println("Hello World")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Running this then gives me a bunch of memory errors of the form `Invalid read of size 4`.
|
||||||
|
|
||||||
|
Now this doesn't mean that the program is leaky. Valgrind does confirm that no leaks are possible. It then begs the question, does this type of memory error even matter?
|
||||||
|
|
||||||
|
To avoid thinking about that I decided to play with [Rust](https://rust-lang.org) instead.
|
44
content/blog/mergerfs.md
Normal file
44
content/blog/mergerfs.md
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
---
|
||||||
|
title: "MergerFS"
|
||||||
|
date: 2020-01-14T23:10:17-05:00
|
||||||
|
draft: false
|
||||||
|
images: []
|
||||||
|
---
|
||||||
|
|
||||||
|
[MergerFS](https://github.com/trapexit/mergerfs) is a great filesystem for an expandable storage system in a homelab. Mostly since it allows you to add disks one at a time without having to, for example, resilver a ZFS pool. MergerFS won't be as efficient as a filesystem that stripes your data across disks, but in the case of a disk failure the disks unaffected will still have part of the data.
|
||||||
|
|
||||||
|
[Plenty](https://blog.linuxserver.io/2017/06/24/the-perfect-media-server-2017/) of other [people](https://www.teknophiles.com/2018/02/19/disk-pooling-in-linux-with-mergerfs/) described MergerFS, so I'll keep this post simple.
|
||||||
|
|
||||||
|
First install MergerFS,
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt install mergerfs
|
||||||
|
```
|
||||||
|
|
||||||
|
The way I have my drives in my homelab setup is to have `/mnt/data/N` where `N` is the number of the drive.
|
||||||
|
|
||||||
|
Examples: `/mnt/data/1`, `/mnt/data/2`, `/mnt/data/3`
|
||||||
|
|
||||||
|
This is mainly so that I can use wildcards to capture all the drives at once.
|
||||||
|
|
||||||
|
Temporary mounting solution:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo mergerfs -o defaults,allow_other,use_ino,fsname=data /mnt/data/\* $HOME/data
|
||||||
|
```
|
||||||
|
|
||||||
|
Permanent solution (Edit `/etc/fstab`)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
/mnt/data/* /home/user/data fuse.mergerfs defaults,allow_other,use_ino,fsname=data 0 0
|
||||||
|
```
|
||||||
|
|
||||||
|
Quick summary of options passed
|
||||||
|
|
||||||
|
| Option | Description |
|
||||||
|
| ----------- | ------------------------------------------------------------ |
|
||||||
|
| defaults | Shortcut for atomic_o_trunc, auto_cache, big_writes, default_permissions, splice_move, splice_read, splice_write |
|
||||||
|
| allow_other | Allows users beside the mergerfs owner to view the filesystem. |
|
||||||
|
| use_ino | MergerFS supplies inodes instead of libfuse |
|
||||||
|
| fsname | Name of the mount as shown in `df` and other tools |
|
||||||
|
|
14
content/blog/missingdata.md
Normal file
14
content/blog/missingdata.md
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
---
|
||||||
|
title: "Considerations with Missing Data"
|
||||||
|
date: 2019-05-22T23:18:45-04:00
|
||||||
|
draft: true
|
||||||
|
---
|
||||||
|
|
||||||
|
Write something about "Missing Completely at Random" (MCAR) and maybe talk about a couple R packages for visualizing this..
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Oh and also techniques for imputing
|
||||||
|
|
||||||
|
- multiple imputation with chained equations
|
||||||
|
- random forest
|
27
content/blog/networkthroughput.md
Normal file
27
content/blog/networkthroughput.md
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
---
|
||||||
|
title: "Network Throughput Testing"
|
||||||
|
date: 2019-08-30T20:11:26-04:00
|
||||||
|
draft: false
|
||||||
|
---
|
||||||
|
|
||||||
|
I ended up upgrading the wiring in my place to CAT7 recently and I wanted to see if there was a noticeable performance difference to my previous cabling. This blog post won't be a product comparison, but instead I'll show how you can do network throughput testing at your own location.
|
||||||
|
|
||||||
|
There is a great package called `iperf`. It's in most repositories under Linux, and binaries for Windows and macOS exist as well.
|
||||||
|
|
||||||
|
For a more in depth tutorial [check out this post from Linode](https://www.linode.com/docs/networking/diagnostics/install-iperf-to-diagnose-network-speed-in-linux/).
|
||||||
|
|
||||||
|
*For the 5 second spiel...*
|
||||||
|
|
||||||
|
One one machine, start the server
|
||||||
|
|
||||||
|
```bash
|
||||||
|
iperf -s
|
||||||
|
```
|
||||||
|
|
||||||
|
On another machine connect to the server and begin testing the connection
|
||||||
|
|
||||||
|
```bash
|
||||||
|
iperf -c 192.168.0.2
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also use the `-t` flag to specify the number of seconds you want to run the test for.
|
24
content/blog/ngrok.md
Normal file
24
content/blog/ngrok.md
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
---
|
||||||
|
title: "Ngrok"
|
||||||
|
date: 2019-11-20T20:56:19-05:00
|
||||||
|
draft: false
|
||||||
|
---
|
||||||
|
|
||||||
|
Let's say you want to spin up a quick demo for a client and you don't want to use a VPS, and they can't access your laptop through the network.
|
||||||
|
|
||||||
|
The easiest way I've known for the past few years to allow another person to access a specific port on your machine is through [ngrok](https://ngrok.com/). Ngrok is nice because not only do they offer a free plan, but they also offer paid plans. This means that you can trust that it'll at least be in business for a little while longer ;)
|
||||||
|
|
||||||
|
## The Process
|
||||||
|
|
||||||
|
You'll need to sign up for an account first. I was then going to write about some of the following steps, but `ngrok` has a really nice quad chart when you login
|
||||||
|
|
||||||
|
![steps](/files/images/0932485094325.png)
|
||||||
|
|
||||||
|
Just note that in step (3) you will actually have a random sequence of characters after `authtoken`.
|
||||||
|
|
||||||
|
Something else you might want to know is how to enable TLS support, luckily that's a simple command line argument.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ngrok http -bind-tls=true port
|
||||||
|
```
|
||||||
|
|
16
content/blog/notimplemented.md
Normal file
16
content/blog/notimplemented.md
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
---
|
||||||
|
title: "NotImplemented"
|
||||||
|
date: 2019-10-27T23:35:17-04:00
|
||||||
|
draft: false
|
||||||
|
---
|
||||||
|
|
||||||
|
Let's say you overwrite the `__mul__` operator in a class in Python, but you don't want the function to be called for all kinds of input. You can specify the type by just returning `NotImplemented` for types you don't want.
|
||||||
|
|
||||||
|
```python
|
||||||
|
class A:
|
||||||
|
def __mul__(self, x):
|
||||||
|
if not isinstance(x, A):
|
||||||
|
return NotImplemented
|
||||||
|
return someOperation()
|
||||||
|
```
|
||||||
|
|
45
content/blog/ohmyzsh.md
Normal file
45
content/blog/ohmyzsh.md
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
---
|
||||||
|
title: "Oh My Zsh"
|
||||||
|
date: 2019-07-21T08:45:03-04:00
|
||||||
|
draft: false
|
||||||
|
---
|
||||||
|
|
||||||
|
Zsh is an extension of the sh that contains a lot more features than the default bash shell installed on most systems.
|
||||||
|
|
||||||
|
For example, one of my new favorite features is being able to type in part of a command in your history and using the up-arrow to find it.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
vim test.hi
|
||||||
|
```
|
||||||
|
|
||||||
|
Then typing `vim` and up-arrow will expand it to `vim test.hi`. Super handy when you have to type of long commands frequently.
|
||||||
|
|
||||||
|
*Oh My Zsh* is a community project that superpowers zsh which allows you to easily add plugins and themes. The theme I'm currently using is [powerlevel10k](https://github.com/romkatv/powerlevel10k#oh-my-zsh) which is a custom theme.
|
||||||
|
|
||||||
|
The list of plugins I have is:
|
||||||
|
|
||||||
|
- git
|
||||||
|
- colored-man-pages
|
||||||
|
- colorize
|
||||||
|
- command-not-found
|
||||||
|
- cp
|
||||||
|
- rsync
|
||||||
|
- screen
|
||||||
|
- ssh-agent
|
||||||
|
- web-search
|
||||||
|
- docker
|
||||||
|
- node
|
||||||
|
- npm
|
||||||
|
- nvm
|
||||||
|
- gem
|
||||||
|
- ruby
|
||||||
|
- pip
|
||||||
|
- python
|
||||||
|
- pyenv
|
||||||
|
- virtualenv
|
||||||
|
- debian
|
||||||
|
- systemd
|
||||||
|
- sudo
|
||||||
|
- zsh-syntax-highlighting
|
17
content/blog/proofdef.md
Normal file
17
content/blog/proofdef.md
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
---
|
||||||
|
title: "Theorem Proving Definitions"
|
||||||
|
date: 2019-12-29T11:21:07-05:00
|
||||||
|
draft: false
|
||||||
|
images: []
|
||||||
|
---
|
||||||
|
|
||||||
|
When I look into a new field, sometimes I get confused by the whole new set of vocab terms I need to encounter. This post will serve to keep me straight with the terms involved in theorem proving.
|
||||||
|
|
||||||
|
| Word | Definition |
|
||||||
|
| ------------ | ------------------------------------------------------------ |
|
||||||
|
| Modus Ponens | If $P$ implies $Q$ and $P$ is asserted to be true, then $Q$ must be true. |
|
||||||
|
| Complete | If every formula having the property can be derived using the system. (i.e The system does not miss a result) |
|
||||||
|
| Decidable | An effective method exists for deriving the correct answer in a finite time. |
|
||||||
|
| Sound | Every formula that can be proved in the system is logically valid with respect to the semantics of the system. (i.e The system does not produce a wrong result) |
|
||||||
|
|
||||||
|
Hopefully, I'll come back and add more terms as I get confused.
|
31
content/blog/pythonpathhacks.md
Normal file
31
content/blog/pythonpathhacks.md
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
---
|
||||||
|
title: "Python Path Hacks"
|
||||||
|
date: 2020-01-13T22:26:16-05:00
|
||||||
|
draft: false
|
||||||
|
images: []
|
||||||
|
---
|
||||||
|
|
||||||
|
There are two quick ways to hack together custom imports in Python. One is by using the `PYTHONPATH` environmental variable, and the other way is by using the `sys` module in Python.
|
||||||
|
|
||||||
|
## Method 1: `PYTHONPATH`
|
||||||
|
|
||||||
|
Before you call `python` set the environmental variable.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
PYTHONPATH=$PYTHONPATH:/other/path
|
||||||
|
```
|
||||||
|
|
||||||
|
Then you can call `python`.
|
||||||
|
|
||||||
|
## Method 2: `sys`
|
||||||
|
|
||||||
|
In the beginning of your Python script add the following code.
|
||||||
|
|
||||||
|
```python
|
||||||
|
import sys
|
||||||
|
sys.path.append('/other/path')
|
||||||
|
```
|
||||||
|
|
||||||
|
## Remarks
|
||||||
|
|
||||||
|
Of course this isn't the best way to add custom libraries to your Python scripts. Ideally, we would use `pip` to manage our dependencies. Perhaps a future blog post will cover this!
|
109
content/blog/pythonsymmetricgroups.md
Normal file
109
content/blog/pythonsymmetricgroups.md
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
---
|
||||||
|
title: "Symmetric Groups in Python"
|
||||||
|
date: 2019-05-22T20:02:21-04:00
|
||||||
|
draft: false
|
||||||
|
---
|
||||||
|
|
||||||
|
**Warning:** This post is meant for someone whose familiar with concepts of Abstract Algebra.
|
||||||
|
|
||||||
|
## Refresher
|
||||||
|
|
||||||
|
### Definitions
|
||||||
|
|
||||||
|
An **operation** on a set is a calculation that maps one element in a set onto another element of the set.
|
||||||
|
|
||||||
|
A **group** in mathematics is a set and an operation that follows the three properties:
|
||||||
|
|
||||||
|
- There exists an identity element.
|
||||||
|
- The operation is associative.
|
||||||
|
- For every element, there exists an inverse of that element in the set.
|
||||||
|
|
||||||
|
**Symmetric Groups** are groups whose elements are all bijections from the set onto itself and operation which is composition of functions.
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
Let's look at the group $\mathbb{Z}_3$. Here is an example of an element of its symmetric group.
|
||||||
|
$$
|
||||||
|
\begin{pmatrix}
|
||||||
|
0 & 1 & 2 \\
|
||||||
|
1 & 2 & 0
|
||||||
|
\end{pmatrix}
|
||||||
|
$$
|
||||||
|
This element maps $0 \rightarrow 1$, $1 \rightarrow 2$, and $2 \rightarrow 0$.
|
||||||
|
|
||||||
|
A good way to check if something similar to the above is an element of a symmetric group is pay attention to the second row. Make sure that it only contains the elements of the set you care about (ex: $\mathbb{Z}_3$) and that there are no repeats.
|
||||||
|
|
||||||
|
Let's look at an example of composing two elements from this symmetric group.
|
||||||
|
$$
|
||||||
|
\begin{pmatrix}
|
||||||
|
0 & 1 & 2 \\
|
||||||
|
1 & 2 & 0
|
||||||
|
\end{pmatrix}
|
||||||
|
\circ
|
||||||
|
\begin{pmatrix}
|
||||||
|
0 & 1 & 2 \\
|
||||||
|
0 & 2 & 1 \\
|
||||||
|
\end{pmatrix}
|
||||||
|
=
|
||||||
|
\begin{pmatrix}
|
||||||
|
0 & 1 & 2 \\
|
||||||
|
1 & 0 & 2 \\
|
||||||
|
\end{pmatrix}
|
||||||
|
$$
|
||||||
|
The main thing to remember here is that you must compose from right to left.
|
||||||
|
|
||||||
|
$0 \rightarrow 0$ and then $0 \rightarrow 1$, so ultimately $0 \rightarrow 1$.
|
||||||
|
|
||||||
|
$1 \rightarrow 2$ and $2 \rightarrow 0$, so ultimately $1 \rightarrow 0$.
|
||||||
|
|
||||||
|
$2 \rightarrow 1$ and $1 \rightarrow 2$, so ultimately $2 \rightarrow 2$.
|
||||||
|
|
||||||
|
### Finding Inverses
|
||||||
|
|
||||||
|
Finding the inverse is simple, since all you need to do is flip the two rows and sort it again.
|
||||||
|
$$
|
||||||
|
\begin{pmatrix}
|
||||||
|
0 & 1 & 2 \\
|
||||||
|
1 & 2 & 0
|
||||||
|
\end{pmatrix}^{-1} =
|
||||||
|
\begin{pmatrix}
|
||||||
|
1 & 2 & 0 \\
|
||||||
|
0 & 1 & 2
|
||||||
|
\end{pmatrix} =
|
||||||
|
\begin{pmatrix}
|
||||||
|
0 & 1 & 2 \\
|
||||||
|
2 & 0 & 1
|
||||||
|
\end{pmatrix}
|
||||||
|
$$
|
||||||
|
|
||||||
|
### Code Implementation
|
||||||
|
|
||||||
|
For Abstract Algebra homework, there was a lot of compositions of these symmetric elements. Sadly, I get pretty lazy doing these by hand for many hours. So like any Computer Scientist, I created a simple script in Python to help me compute these.
|
||||||
|
|
||||||
|
The code is located in [this gist](https://gist.github.com/Brandon-Rozek/adf9e1e64e2fbfcd3f8d3bc5da9322bf).
|
||||||
|
|
||||||
|
#### Basic Usage
|
||||||
|
|
||||||
|
`SymmetricElement` takes in the second row of the matrices we were playing with. You can find the inverse with `element.inverse()` and you can compose two symmetric elements together with the `*` operation.
|
||||||
|
|
||||||
|
```python
|
||||||
|
SymmetricElement(1,2,3)
|
||||||
|
# array([[1., 2., 3.],
|
||||||
|
# [1., 2., 3.]])
|
||||||
|
```
|
||||||
|
|
||||||
|
```python
|
||||||
|
SymmetricElement(1,2,3) * SymmetricElement(2,1,3)
|
||||||
|
#array([[1., 2., 3.],
|
||||||
|
# [2., 1., 3.]])
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```python
|
||||||
|
SymmetricElement(1,2,3).inverse()
|
||||||
|
#array([[1., 2., 3.],
|
||||||
|
# [1., 2., 3.]])
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
14
content/blog/pythontyping.md
Normal file
14
content/blog/pythontyping.md
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
---
|
||||||
|
title: "Python Typing"
|
||||||
|
date: 2019-10-28T00:12:34-04:00
|
||||||
|
draft: false
|
||||||
|
---
|
||||||
|
|
||||||
|
There's a typing module built right into Python that you can use on your applications. Sobolevn write a great [blog post](https://sobolevn.me/2019/01/simple-dependent-types-in-python) about it. One thing that threw me off at first is that if you add type annotations and then run python like you normally would, it would act as if the annotations weren't there.
|
||||||
|
|
||||||
|
Why use type annotations then?
|
||||||
|
|
||||||
|
1. It enhances your internal docs. VS Code and other editors pick this up and show it to the user in their IDE.
|
||||||
|
2. You can use `mypy` to perform type checking for you.
|
||||||
|
|
||||||
|
I go back and forth with type checking in Python, but I do think that forcing yourself to follow type safety makes you a better programmer.
|
71
content/blog/quickstoragesetup.md
Normal file
71
content/blog/quickstoragesetup.md
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
---
|
||||||
|
title: "Quickly Setting up a Storage Device"
|
||||||
|
date: 2020-01-12T21:43:26-05:00
|
||||||
|
draft: false
|
||||||
|
images: []
|
||||||
|
---
|
||||||
|
|
||||||
|
This post exists mostly to aid myself for when I buy new drives for my home server. It's a quick and easy way to create an ext4 filesystem over the entire drive.
|
||||||
|
|
||||||
|
To go through this post, you'll need to know the name of your drive.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo fdisk -l
|
||||||
|
```
|
||||||
|
|
||||||
|
or
|
||||||
|
|
||||||
|
```bash
|
||||||
|
lsblk
|
||||||
|
```
|
||||||
|
|
||||||
|
The drive is most likely one of the larger devices with no partitions set. It'll likely be of the format `/dev/sdX`.
|
||||||
|
|
||||||
|
To begin, we'll have to set the label. Here we'll use `gpt`.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo parted /dev/sdX mklabel gpt
|
||||||
|
```
|
||||||
|
|
||||||
|
Then we can create a primary partition formatted with ext4 covering the entire device.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo parted -a opt /dev/sdX mkpart primary ext4 0% 100%
|
||||||
|
```
|
||||||
|
|
||||||
|
Now we can let `ext4` format the drive,
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo mkfs.ext4 /dev/sdX
|
||||||
|
```
|
||||||
|
|
||||||
|
I like to set up my mount points to be `/mnt/data/N` where N is the number of the drive I'm working with.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo mkdir /mnt/data/N
|
||||||
|
```
|
||||||
|
|
||||||
|
To temporarily mount it, just to make sure it works you can run
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo mount /dev/sdX /mnt/data/N
|
||||||
|
```
|
||||||
|
|
||||||
|
You can unmount it with `umount`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo umount /dev/sdX
|
||||||
|
```
|
||||||
|
|
||||||
|
When you're ready to make it permanent, we'll have to edit the `/etc/fstab` file. We should note the drive by its UUID so that it's not dependent on the slot the hard drive sits in. You can find it by running this command
|
||||||
|
|
||||||
|
```bash
|
||||||
|
lsblk -o UUID /dev/sdX1
|
||||||
|
```
|
||||||
|
|
||||||
|
Now you can append your `/etc/fstab` with the following:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
UUID=uuid-here /mnt/data/N ext4 defaults 0 0
|
||||||
|
```
|
||||||
|
|
33
content/blog/robustdd.md
Normal file
33
content/blog/robustdd.md
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
---
|
||||||
|
title: "Robustdd"
|
||||||
|
date: 2019-09-27T22:45:56-04:00
|
||||||
|
draft: false
|
||||||
|
---
|
||||||
|
|
||||||
|
This blog post is going to assume that we're writing to `/dev/sdX`. Please change this to whatever disk you're actually trying to write to. I bear no responsibility if you accidentally write to your OS drives.
|
||||||
|
|
||||||
|
Make sure the disk you wish to write to is unmounted.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo umount /dev/sdX
|
||||||
|
```
|
||||||
|
|
||||||
|
For good measure check `lsblk`....
|
||||||
|
|
||||||
|
Then perform the writing operation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo dd bs=4M if=/path/to/distro.iso of=/dev/sdX conv=fdatasync status=progress
|
||||||
|
```
|
||||||
|
|
||||||
|
`fdatasync` will wait until the data is physically written into the drive before finishing.
|
||||||
|
|
||||||
|
Once that operation is done we can check to see if it is performed properly. This step will only work for ISOs that have prepared a file similar to `md5sum.txt` that Ubuntu provides....
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo mount /dev/sdX /mnt
|
||||||
|
cd /mnt
|
||||||
|
md5sum -c md5sum.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
Hopefully you'll see all `OK` and we're good to go!
|
72
content/blog/rsa.md
Normal file
72
content/blog/rsa.md
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
---
|
||||||
|
title: RSA Cryptography
|
||||||
|
date: 2019-12-10T22:15:21-05:00
|
||||||
|
draft: false
|
||||||
|
images: []
|
||||||
|
---
|
||||||
|
|
||||||
|
# Introduction
|
||||||
|
|
||||||
|
Why do we need Cryptography? Well let's say Bob is purchasing things from Alice over the internet. He wants to give Alice his credit card details to complete his transaction, but he feels uncomfortable with the thought that others can possibly read it as well. RSA is a cryptosystem that underlies HTTPS, and this gives Bob a sense of security that only Alice can see his message.
|
||||||
|
|
||||||
|
The journey of RSA starts in 1976 when Whitfield Diffie and Martin Hellman published a framework describing an asymmetric public-private key cryptosystem through four key steps:
|
||||||
|
|
||||||
|
1. Key Generation
|
||||||
|
2. Key Distribution
|
||||||
|
3. Encryption with one-way function (using a public key)
|
||||||
|
4. Decryption (using a private key)
|
||||||
|
|
||||||
|
They left the one-way function undefined, making it an open problem for the community. Ron **R**ivest, Adi **S**hamir, and Leonard **A**dleman at MIT took up this challenge. Rivest and Shamir were Computer Scientists trying to come up with this function while Adleman was the mathematician tasked with finding flaws in their discoveries. After working together to develop the RSA cryptosystem in 1977, Rivest, Shamir, and Adleman went on to found the organization RSA Data Security in 1982.
|
||||||
|
|
||||||
|
# RSA Algorithm
|
||||||
|
|
||||||
|
In this section, we'll look at the RSA algorithm through the lens of the four steps listed in the previous section.
|
||||||
|
|
||||||
|
#### Key Generation
|
||||||
|
|
||||||
|
Alice needs to generate public and private keys through the following method:
|
||||||
|
|
||||||
|
1. Choose two distinct prime integers $p$ and $q$.
|
||||||
|
2. Compute $n = pq$. The integer $n$ will be later released as part of the *public key*.
|
||||||
|
3. Calculate $\phi(n)$ which by the Euler's Totient Product of Primes property is $(p - 1)(q - 1)$.
|
||||||
|
4. Choose $e \in \mathbb{Z}$ such that $e$ and $\phi(n)$ are relatively prime. The integer $e$ will also be released as part of the *public key*.
|
||||||
|
5. Calculate $d$, the multiplicative inverse of $e$ modulo $\phi(n)$. The integer $d$ will be kept secret as the *private key*.
|
||||||
|
|
||||||
|
#### Key Distribution
|
||||||
|
|
||||||
|
For Bob to send a message to Alice, he will need to know Alice's public key $n$ and $e$. Bob, however, does not need to know Alice's prime integers $p$ and $q$ that divide $n$. Nor does he need to know the private key $d$.
|
||||||
|
|
||||||
|
#### Encryption
|
||||||
|
|
||||||
|
Let $m \in \mathbb{Z}_n$ be the message that Bob wants to send. Bob calculates this number from his message using an agreed-upon reversible algorithm. The ciphertext or encrypted block $c$ is computed by, $c \equiv m^e$ (mod $n$). Bob then sends the ciphertext to Alice.
|
||||||
|
|
||||||
|
#### Decryption
|
||||||
|
|
||||||
|
Alice receives this ciphertext $c$ from Bob and then can recover the original message $m$ by computing $c^d$ modulo $n$. How is this possible? Well recall that $c^d \equiv (m^e)^d$. Then, $c^d \equiv m^{ed}$ by exponent rules. Recall that $e$ and $d$ are multiplicative inverses modulo $\phi(n)$. Therefore by definition, $ed = 1 + \phi(m)t$ for some $t \in \mathbb{Z}$. Then,
|
||||||
|
|
||||||
|
\begin{align*}
|
||||||
|
c^d &\equiv m^{1 + \phi(n)t} \\\\
|
||||||
|
&\equiv m(m^{\phi(n)})^t
|
||||||
|
\end{align*}
|
||||||
|
|
||||||
|
Well since $n$ is prime, by Euler's Theorem $m^{\phi(n)} \equiv 1$ modulo $n$. Thus, $c^d \equiv m \cdot 1^t \equiv m$ modulo $n$. Through this short proof, we can see that Alice successfully retrieves Bob's message $m$ just by raising the ciphertext $c$ to the power of the private key $d$.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
Let's run through a short example of this algorithm with small integers. Let $p = 3, q = 5$. Then $n = pq = 15$. We then calculate $\phi(n) = \phi(p)\phi(q) = 2(4) = 8$. An integer $e$ that is relatively prime to $\phi(n)$ is $11$. Finally we compute the inverse of $e$ modulo $\phi(n)$.
|
||||||
|
|
||||||
|
\begin{align*}
|
||||||
|
11d &\equiv 1 \text{ (mod $8$) } \\\\
|
||||||
|
11d &\equiv 33 \text{ (mod $8$)} \\\\
|
||||||
|
d &\equiv 3 \text{ (mod $8$)}
|
||||||
|
\end{align*}
|
||||||
|
|
||||||
|
|
||||||
|
Alice then gives Bob the integers $n = 15$ and $e = 11$. Bob can then encode his message (say $m = 12$) by raising $m$ to the power of $e$. Thus, $c = m^e = 12^{11} = 743008370688 \equiv 3$ modulo 15. Alice can then recover $m = 12$ by raising $c = 3$ to the power of her private key $d$. $m \equiv c^d \equiv 3^3 \equiv 27 \equiv 12$ modulo $15$.
|
||||||
|
|
||||||
|
|
||||||
|
# Discussion
|
||||||
|
|
||||||
|
Well if it's this simple to calculate, why is it secure? It is actually extremely difficult to factor a large prime number $n$. If $n$ can be factored into $p$ and $q$, then it is easy to compute $d \equiv e^{-1}$ modulo $\phi(pq)$. It is also really hard to efficiently solve for $m$ in the congruence $c \equiv m^e$ modulo $n$. RSA Laboratories on March 1991 issued a challenge tasking the public to calculate prime factors of $n$ (called RSA numbers) of increasing digits. They offered a cash prize of up to \$200,000 for solving RSA-2048 which is a 617 digit long number. As of the time of the presentation, the largest factored RSA number was RSA-768 (768 bits or 232 decimal digits) which was calculated over the span of two years. As of the time of this writing, towards the end of this year (2019) the largest known factored RSA number is actually RSA-240 (795 bits or 240 decimal digits). Currently the National Institute of Standards and Technology (NIST) recommends the use of 2048-bit keys for use in RSA. So for the time being, our communications are safe.
|
||||||
|
|
||||||
|
|
28
content/blog/rsynckey.md
Normal file
28
content/blog/rsynckey.md
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
---
|
||||||
|
title: "Rsync with a Different Key"
|
||||||
|
date: 2019-07-06T09:20:05-04:00
|
||||||
|
draft: false
|
||||||
|
---
|
||||||
|
|
||||||
|
To use Rsync with a different key, follow the command structure below.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rsync -e "ssh -i $HOME/.ssh/key" user@hostname:/from/dir/ /to/dir/
|
||||||
|
```
|
||||||
|
|
||||||
|
Though for syncing my local website to my VPS, I usually like having more options with my rsync command
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rsync -Paz --delete -e "ssh -i $HOME/.ssh/key" user@hostname:/from/dir/ /to/dir/
|
||||||
|
```
|
||||||
|
|
||||||
|
Quick option definitions (from man page)
|
||||||
|
|
||||||
|
| Option | Description |
|
||||||
|
| -------------- | ------------------------------------------------------------ |
|
||||||
|
| -e | Allows you to override the default shell used as the transport for rsync. Command line options are permitted after the command name. |
|
||||||
|
| -a, --archive | This is equivalent to -rlptgoD. It is a quick way of saying you want recursion and want to preserve almost everything (with -H being a notable omission). The only exception to the above equivalence is when --files-from is specified, in which case -r is not implied. <br />Note that -a does not preserve hardlinks, because finding multiply-linked files is expensive. You must separately specify -H. |
|
||||||
|
| -P | Equivalent to --partial --progress. Its purpose is to make it much easier to specify these two options for a long transfer that may be interrupted. |
|
||||||
|
| -z, --compress | Compress file data during the transfer |
|
||||||
|
| --delete | Delete extraneous files from dest dirs |
|
||||||
|
|
27
content/blog/scrcpy.md
Normal file
27
content/blog/scrcpy.md
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
---
|
||||||
|
title: "Scrcpy"
|
||||||
|
date: 2020-01-09T21:36:30-05:00
|
||||||
|
draft: false
|
||||||
|
images: []
|
||||||
|
---
|
||||||
|
|
||||||
|
With [Scrcpy](https://github.com/Genymobile/scrcpy) you can control an Android device remotely!
|
||||||
|
|
||||||
|
![1578623897330](/files/images/1578623897330.png)
|
||||||
|
|
||||||
|
The `README` on the Github page has all the information you need though it boils down to a few simple steps.
|
||||||
|
|
||||||
|
1. Install `scrcpy`.
|
||||||
|
```bash
|
||||||
|
sudo snap install scrcpy
|
||||||
|
```
|
||||||
|
2. [Enable adb debugging](https://developer.android.com/studio/command-line/adb.html#Enabling) on your device
|
||||||
|
3. (Optional for remote capability) Enable adb over TCP/IP on your device
|
||||||
|
```bash
|
||||||
|
adb tcpip 5555
|
||||||
|
```
|
||||||
|
4. (Optional Continued) Connect to the device
|
||||||
|
```bash
|
||||||
|
adb connect DEVICE_IP:5555
|
||||||
|
```
|
||||||
|
5. Run `scrcpy`
|
124
content/blog/screwuppresentations.md
Normal file
124
content/blog/screwuppresentations.md
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
---
|
||||||
|
title: "Notes on '13 Ways Designers Screw Up Client Presentations'"
|
||||||
|
date: 2019-05-21T22:20:14-04:00
|
||||||
|
draft: false
|
||||||
|
---
|
||||||
|
|
||||||
|
I watched a great talk by [Mike Monteiro](http://mikemonteiro.com/) recently called [13 Ways Designers Screw Up Client Presentations](https://www.youtube.com/watch?v=IXXKqwrEql4). You should really watch the talk but this post is just a quick summary of the points he got across in the presentation.
|
||||||
|
|
||||||
|
## Intro
|
||||||
|
|
||||||
|
You need to sell your work! Great work is worth nothing if you can't persuade the client it is good.
|
||||||
|
|
||||||
|
Don't leave the selling to other people. That is your job. Every decision you make is backed by some data or experience. If you're not there, then you lose context and won't be there for feedback.
|
||||||
|
|
||||||
|
On to the mistakes....
|
||||||
|
|
||||||
|
## 1. You are not there to be the client's friend
|
||||||
|
|
||||||
|
You are there to solve the client's goal. Happiness is the side-effect of good design, not a goal.
|
||||||
|
|
||||||
|
You are there to convince the client not to go down avenues that run counter to their goals. That's what they hired you for. You need to present yourself as the expert that they believed they hired.
|
||||||
|
|
||||||
|
For example, your doctor needs to tell you if you have a cracked tooth. Do you think that they would hide this information from you because it might hurt your feelings? No, because it is their job to tell you this information.
|
||||||
|
|
||||||
|
## 2. Not getting off your ass
|
||||||
|
|
||||||
|
It should be obvious when you enter the room, who is in charge of the presentation.
|
||||||
|
|
||||||
|
Confidence isn't about you feeling better, it is about your client feeling better.
|
||||||
|
|
||||||
|
## 3. Starting with an apology
|
||||||
|
|
||||||
|
Every time you apologize for something you are freaking the client out. No matter how much you hoped to present at the presentation, by the time you get to that room, you have the perfect amount of stuff.
|
||||||
|
|
||||||
|
If you don't feel like the work is up to spec, cancel the presentation. Do not have a presentation of work you cannot stand behind. It is better to cancel than waste people's time. You can get away with this exactly once during a project.
|
||||||
|
|
||||||
|
Your client probably works for somebody higher up, the minute you start apologizing to them, they imagine themselves apologizing to their boss!
|
||||||
|
|
||||||
|
## 4. Not Setting the Stage Properly
|
||||||
|
|
||||||
|
You have gathered busy people together, they probably have other things they can be doing, so let them know what this presentation is for.
|
||||||
|
|
||||||
|
Let them know why they are a necessary and important part of this conversation.
|
||||||
|
|
||||||
|
You need to address these two things at the beginning of the presentation
|
||||||
|
|
||||||
|
1. Why are we here?
|
||||||
|
1. Let them know what their roles are, why they are here, what you are going to show, and what you need from them.
|
||||||
|
2. When can we leave?
|
||||||
|
1. Let them know what it takes for the goal of the meeting to be met. The minute you get it, the meeting is over. Never give a client time to unmake a decision, especially a decision in your favor.
|
||||||
|
|
||||||
|
Once you have gotten what you need, keep quiet. Everything you do beyond that threatens to undo the victory you just got.
|
||||||
|
|
||||||
|
## 5. Giving the Real Estate Tour
|
||||||
|
|
||||||
|
You don't sell a house by talking about the sheetrock. You sell it by getting a buyer to picture themselves in the neighborhood, by picturing the kids in the playground, etc.
|
||||||
|
|
||||||
|
You sell the benefits of the work, and you sell how the work matches the project goals.
|
||||||
|
|
||||||
|
Every decision on that page should be made with the benefit of data and good research, but people make decisions based on stories.
|
||||||
|
|
||||||
|
## 6. Taking Notes
|
||||||
|
|
||||||
|
Find somebody else to take the notes. You are giving the presentation.
|
||||||
|
|
||||||
|
## 7. Reading a Script
|
||||||
|
|
||||||
|
You need to convince the client that you are excited about the product.
|
||||||
|
|
||||||
|
"Without promotion something terrible happens.... Nothing" - PT. Barnum
|
||||||
|
|
||||||
|
## 8. Getting Defensive
|
||||||
|
|
||||||
|
You are not your work, and your work is not you.
|
||||||
|
|
||||||
|
Your work is a product made to meet a client's goals. The client is free to criticize the work and is free to tell you whether they believe it has met the goals or not. You are free to present evidence to the contrary but not get hurt by the criticism.
|
||||||
|
|
||||||
|
When the client starts critiquing the work listen to what they're saying, don't feel like you have to defend everything they're saying right then and there. You also don't have to promise them anything then and there, sometimes it's best to sit on it for a while.
|
||||||
|
|
||||||
|
When a client is giving you feedback it is a great time to keep your mouth shut.
|
||||||
|
|
||||||
|
## 9. Mentioning Typefaces
|
||||||
|
|
||||||
|
Clients don't care about typefaces. You don't really want their input on this. Clients are uncomfortable in your field. The more you dive into these things, the more uncomfortable they're going to get. And it's going to look like you're inviting them to do your job for them.
|
||||||
|
|
||||||
|
Stop asking for permission to do a job you were hired for.
|
||||||
|
|
||||||
|
Their comfort zone is their business needs, which is great since you are never going to be an expert on their domain. Talk about your work in terms of their business and needs.
|
||||||
|
|
||||||
|
## 10. Talking about how hard you worked
|
||||||
|
|
||||||
|
You're not getting a grade on effort.
|
||||||
|
|
||||||
|
If you do the work right, it will look like it was effortless.
|
||||||
|
|
||||||
|
## 11. Reacting to questions as change requests
|
||||||
|
|
||||||
|
"Why is this blue?"
|
||||||
|
|
||||||
|
"Oh I can change it."
|
||||||
|
|
||||||
|
Sometimes the client just has a question! They're just looking for your reasoning.
|
||||||
|
|
||||||
|
## 12. Not guiding the feedback loop
|
||||||
|
|
||||||
|
Most clients have no idea what kind of feedback you are looking for. They are not trained in your work. Anything that helps you do your job is part of your job. Know what you want before calling the presentation.
|
||||||
|
|
||||||
|
Some suggestions:
|
||||||
|
|
||||||
|
1. How well does this reflect your brand?
|
||||||
|
2. How well does this reflect your users' needs as we discussed in the research?
|
||||||
|
3. How well does this reflect your ad strategy?
|
||||||
|
|
||||||
|
They're not going to give it to you unless you explicitly ask for it.
|
||||||
|
|
||||||
|
## 13. Asking "Do you like it?"
|
||||||
|
|
||||||
|
If it had a like button on it would you push it?
|
||||||
|
|
||||||
|
All the work you and your team did went down the drain! The client is no longer looking at you as an expert.
|
||||||
|
|
||||||
|
Every decision on the project has been made with the benefit of expertise and data and you just uttered the most subjective phrase.
|
||||||
|
|
||||||
|
If you want to make your client's happy, make them successful.
|
22
content/blog/shutdownafterjob.md
Normal file
22
content/blog/shutdownafterjob.md
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
---
|
||||||
|
title: "Shutdown After Job"
|
||||||
|
date: 2019-08-30T20:43:56-04:00
|
||||||
|
draft: false
|
||||||
|
---
|
||||||
|
|
||||||
|
I'm back to running longer jobs as part of my research. If I run a task overnight, I want to conserve energy and not keep it running after I finish. I suppose this would also apply to cloud billing, you want it to do the job and then stop.
|
||||||
|
|
||||||
|
This technique will require you to have sudo privileges on the machine.
|
||||||
|
|
||||||
|
1. Change user to root.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo su
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Run job as regular user, write output to file, and then poweroff.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
su -u user task > output.txt && chown user:user output.txt && poweroff
|
||||||
|
```
|
||||||
|
|
15
content/blog/simulators.md
Normal file
15
content/blog/simulators.md
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
---
|
||||||
|
title: "Simulators"
|
||||||
|
date: 2019-07-01T22:22:29-04:00
|
||||||
|
draft: false
|
||||||
|
---
|
||||||
|
|
||||||
|
Part of my job involves integrating multiple different sensors together to make a cohesive product. One thing that I appreciate about my current team, is the rich set of tooling built around the project. I am definitely learning a lot from this project. One thing I wanted to share was the use of *simulators*.
|
||||||
|
|
||||||
|
Now since we're working with sensor data, it doesn't always make sense to need to be connected to a whole system. There's also the additional hassle of dealing with safety and working outside. So in order to do some programming and still be at ease that we didn't break anything, we make heavy use of simulators.
|
||||||
|
|
||||||
|
<u>Example</u>: Have a GPS hooked up to your system? Well with a tool you can make waypoints and generate a file which you then feed in as a fake GPS device!
|
||||||
|
|
||||||
|
Now when I say fake device, I don't mean I'm actually emulating the device on the machine. It's easier to have your device interfacers publish to the local network and have your other parts of the application subscribe to it. So in this case, you will just need to produce those messages directly.
|
||||||
|
|
||||||
|
So in conclusion, I think this pattern will work well for my personal projects. It helps keep your code modular and easily testable!
|
16
content/blog/sshconfig.md
Normal file
16
content/blog/sshconfig.md
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
---
|
||||||
|
title: "SSH Config"
|
||||||
|
date: 2019-09-27T22:46:39-04:00
|
||||||
|
draft: false
|
||||||
|
---
|
||||||
|
|
||||||
|
Have you ever gone through the hassle of having multiple public/private keys for accessing your remote servers? Before recently, I used to specify the identity file in all my transactions `ssh -I ~/.ssh/private_key user@host` but no longer! I discovered you can add the following to your `~/.ssh/config` to specify which key you want to use!
|
||||||
|
|
||||||
|
```
|
||||||
|
Host someaddress
|
||||||
|
Hostname someaddress
|
||||||
|
user usernameonserver
|
||||||
|
IdentityFile ~/.ssh/private_key
|
||||||
|
```
|
||||||
|
|
||||||
|
Of course you can add multiple of these entries in that file so that you can `ssh` without having to worry about explicitly using keys.
|
19
content/blog/sshlocalportforwarding.md
Normal file
19
content/blog/sshlocalportforwarding.md
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
---
|
||||||
|
title: "SSH Local Port Forwarding"
|
||||||
|
date: 2019-08-06T16:50:00-04:00
|
||||||
|
draft: false
|
||||||
|
---
|
||||||
|
|
||||||
|
There are some services that I don't want to run all the time which makes me not want to open a port for it. One good example of this is [Jupyter Notebooks](https://jupyter.org/). Therefore, what I sometimes do is run it locally and forward the port so that another machine can access it.
|
||||||
|
|
||||||
|
Example command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh -L 8888:localhost:8888 -N 192.168.0.2
|
||||||
|
```
|
||||||
|
|
||||||
|
The `-L` flag allows you to specify the `localsocket:host:remotesocket`.
|
||||||
|
|
||||||
|
`-N` makes it so that it doesn't execute any additional commands
|
||||||
|
|
||||||
|
Then finally you put the address of the machine you wish to connect to.
|
23
content/blog/systemdstartup.md
Normal file
23
content/blog/systemdstartup.md
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
---
|
||||||
|
title: "Analyzing Startup Times with Systemd"
|
||||||
|
date: 2019-12-26T22:52:59-05:00
|
||||||
|
draft: false
|
||||||
|
images: []
|
||||||
|
---
|
||||||
|
|
||||||
|
Startup times feeling slow? Check to see if there are any uneeded services slowing you down!
|
||||||
|
|
||||||
|
To see how long it takes to bootup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
systemd-analyze
|
||||||
|
```
|
||||||
|
|
||||||
|
To see the length of time each service took to initialize
|
||||||
|
|
||||||
|
```bash
|
||||||
|
systemd-analyze blame
|
||||||
|
```
|
||||||
|
|
||||||
|
Then you can `disable` any services that you don't need.
|
||||||
|
|
29
content/blog/systemdwithpythonenvs.md
Normal file
29
content/blog/systemdwithpythonenvs.md
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
---
|
||||||
|
title: "Systemd with Python environments"
|
||||||
|
date: 2019-08-25T20:04:20-04:00
|
||||||
|
draft: false
|
||||||
|
---
|
||||||
|
|
||||||
|
It took me some time to realize why I couldn't start a project during startup. I then realized that it was because I was using a python virtual environment and didn't tell systemd about it.
|
||||||
|
|
||||||
|
Here's how you can do so...
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
[Unit]
|
||||||
|
Description=Start Service
|
||||||
|
# After=network.target # If you want a python webserver...
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=Simple
|
||||||
|
User=brandon
|
||||||
|
WorkingDirectory=/home/brandon/Development
|
||||||
|
ExecStart=/bin/sh -c ". /home/brandon/Development/pythonenv/bin/activate; /home/brandon/Development/start"
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
24
content/blog/tempresolve.md
Normal file
24
content/blog/tempresolve.md
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
---
|
||||||
|
title: "Temporarily Resolving Hostnames"
|
||||||
|
date: 2020-01-04T21:26:16-05:00
|
||||||
|
draft: false
|
||||||
|
images: []
|
||||||
|
---
|
||||||
|
|
||||||
|
Let's say that we're testing a webserver where the pages served depended on a domain that you don't own. The most common way I know to test this is to modify your `/etc/hosts` file to contain the hostname and ip address you want to map it to.
|
||||||
|
|
||||||
|
```
|
||||||
|
192.168.1.2 custom.domain
|
||||||
|
```
|
||||||
|
|
||||||
|
I've recently discovered that the command line utility `curl` has a quick and easy option to forge the hostname of a request.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl --resolve domain:port:ipaddr url
|
||||||
|
```
|
||||||
|
|
||||||
|
There are also browser extensions that you can use such as [LiveHosts](https://github.com/Aioros/livehosts) to get around this as well. This post isn't entirely useful when talking about permanent services.
|
||||||
|
|
||||||
|
If this is going to be a publicly facing service, then you should just set the records of your domain name to point to the server.
|
||||||
|
|
||||||
|
If it's a non-public routable service, then perhaps try looking into setting up your own private [dns server](https://brandonrozek.com/blog/coredns/).
|
6
content/blog/tensorboard.md
Normal file
6
content/blog/tensorboard.md
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
title: "Tensorboard"
|
||||||
|
date: 2019-05-26T19:04:47-04:00
|
||||||
|
draft: true
|
||||||
|
---
|
||||||
|
|
30
content/blog/togglingxinput.md
Normal file
30
content/blog/togglingxinput.md
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
---
|
||||||
|
title: "Toggling X Input"
|
||||||
|
date: 2020-01-07T20:46:32-05:00
|
||||||
|
draft: false
|
||||||
|
images: []
|
||||||
|
---
|
||||||
|
|
||||||
|
On X, we can easily enable or disable input devices using the `xinput` command. This is a great use case when you're tired of accidentally hitting the red Thinkpad nub or having your palm be recognized when drawing with a pen.
|
||||||
|
|
||||||
|
Running the `xinput` command performs the action temporarily. Your default settings will be restored upon a reboot.
|
||||||
|
|
||||||
|
To list `xinput` devices run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
xinput
|
||||||
|
```
|
||||||
|
|
||||||
|
To disable a device:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
xinput disable [id]
|
||||||
|
```
|
||||||
|
|
||||||
|
To enable a device:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
xinput enable [id]
|
||||||
|
```
|
||||||
|
|
||||||
|
The ids are listed when you list the devices.
|
70
content/blog/traefiknginx.md
Normal file
70
content/blog/traefiknginx.md
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
---
|
||||||
|
title: "Traefik & Nginx"
|
||||||
|
date: 2019-12-16T19:55:47-05:00
|
||||||
|
draft: false
|
||||||
|
images: []
|
||||||
|
---
|
||||||
|
|
||||||
|
I've been enjoying Traefik for its auto-discovery of containers. The only problem is that for a couple containers such as Plex and HomeAssistant I have host networking enabled. This usually results in Traefik failing to forward the traffic properly.
|
||||||
|
|
||||||
|
Having more fine grained control is exactly what Nginx is for! I don't want to switch my whole setup to Nginx since that would be a lot of configuration files for every docker container. But I think having configuration files for containers that use host networking is manageable.
|
||||||
|
|
||||||
|
In your docker-compose file first make sure that Traefik is disabled for containers that use host networking by adding the label `traefik.enable=false`
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
homeassistant:
|
||||||
|
image: homeassistant/home-assistant
|
||||||
|
container_name: homeassistant
|
||||||
|
hostname: homeassistant
|
||||||
|
network_mode: host
|
||||||
|
environment:
|
||||||
|
- PUID=1000
|
||||||
|
- PGID=1000
|
||||||
|
volumes:
|
||||||
|
- /Volumes/homeassistant/config:/config
|
||||||
|
restart: always
|
||||||
|
labels:
|
||||||
|
- traefik.enable=false
|
||||||
|
```
|
||||||
|
|
||||||
|
Then add a new section for `nginx` adding the domains that you wish it to manage in the labels
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
nginx:
|
||||||
|
image: linuxserver/nginx
|
||||||
|
container_name: nginx
|
||||||
|
environment:
|
||||||
|
- PUID=1000
|
||||||
|
- PGID=1000
|
||||||
|
volumes:
|
||||||
|
- /Volumes/nginx/config:/config
|
||||||
|
restart: always
|
||||||
|
labels:
|
||||||
|
- traefik.http.routers.my-container.rule=Host(`plex.example.com`,`homeassistant.example.com`)
|
||||||
|
```
|
||||||
|
|
||||||
|
Now on the host system add the configuration files for nginx to consume. Example:
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
listen [::]:80;
|
||||||
|
|
||||||
|
server_name plex.example.com;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://homelanip:32400;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection 'upgrade';
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_cache_bypass $http_upgrade;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Similarly add a configuration file for `homeassistant` or any other service you have.
|
||||||
|
|
||||||
|
Now for this example, the subdomains `plex.example.com` and `homeassistant.example.com` are managed by Nginx.
|
||||||
|
|
||||||
|
To finish it off, `docker-compose start nginx`.
|
11
content/blog/unattended-upgrades.md
Normal file
11
content/blog/unattended-upgrades.md
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
---
|
||||||
|
title: "Unattended Upgrades"
|
||||||
|
date: 2019-05-26T12:52:03-04:00
|
||||||
|
draft: false
|
||||||
|
---
|
||||||
|
|
||||||
|
I'm a big believer of reducing maintenance. One of the things that I didn't bother setting up before but should've is *unattended upgrades*. The benefits of this is that you don't have to repeatedly log into your sever to upgrade your system.
|
||||||
|
|
||||||
|
Why bother managing your own server in the first place? Well with a virtualized system you can set up any type of application that you like. You're not limited to only hosting static sites or whatever applications the C-Panel instance allows.
|
||||||
|
|
||||||
|
Now there's no need for me to reiterate what's been done before, so here's a [useful post from libre-software](https://libre-software.net/ubuntu-automatic-updates/) describing how to set it up so that a Ubuntu server updates itself automatically.
|
9
content/blog/usbredirection.md
Normal file
9
content/blog/usbredirection.md
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
---
|
||||||
|
title: "Wacom and USB Redirection in Virtual Machines"
|
||||||
|
date: 2019-05-24T22:15:56-04:00
|
||||||
|
draft: false
|
||||||
|
---
|
||||||
|
|
||||||
|
[Virt-Manager](https://virt-manager.org/) is a great tool for managing virtual machines under Linux. Today I learned of [Spice USB redirection](https://blog.wikichoon.com/2014/04/spice-usb-redirection-in-virt-manager.html). Essentially it allows you to switch USB devices from the host to the virtualized environment. This came in handy when I noticed that the graphics tablet device was not able to do pressure sensitivity on the Windows guest.
|
||||||
|
|
||||||
|
To achieve this goal, I removed the graphics tablet hardware device and manually redirected that USB device to the guest. I then remembered to install the [Wacom drivers](https://www.wacom.com/en-us/support/product-support/drivers) for the Windows VM.
|
22
content/blog/videosandgifs.md
Normal file
22
content/blog/videosandgifs.md
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
---
|
||||||
|
title: "Videos and GIFs"
|
||||||
|
date: 2019-12-04T10:13:35-05:00
|
||||||
|
draft: false
|
||||||
|
images: []
|
||||||
|
---
|
||||||
|
|
||||||
|
I like using GIFs in Google Slides because it doesn't require any interaction to start playing right away. Therefore, I looked at a couple resources to add a GIF from a video into my presentation. Of course this is all using one of my favorite terminal tools `ffmpeg`.
|
||||||
|
|
||||||
|
First of all, I wanted to include a video of a bot playing Pong. The video was a little long, so I decided speed it up for a more time-lapse type feel.
|
||||||
|
|
||||||
|
[From the FFMPEG website](https://trac.ffmpeg.org/wiki/How%20to%20speed%20up%20/%20slow%20down%20a%20video): To 10x the speed `ffmpeg -i input.mkv -filter:v "setpts=0.1*PTS" output.mkv`
|
||||||
|
|
||||||
|
You just need to replace the `0.1` with the appropriate decimal number to change the speed.
|
||||||
|
|
||||||
|
Giphy has a great [writeup](https://engineering.giphy.com/how-to-make-gifs-with-ffmpeg/) describing the commands to use in order to make your GIF look nice.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ffmpeg -i inputvideo.mp4 -filter_complex "[0:v] palettegen" palette.png
|
||||||
|
ffmpeg -i inputvideo.mp4 -i palette.png -filter_complex "[0:v][1:v] paletteuse" output.gif
|
||||||
|
```
|
||||||
|
|
84
content/blog/virtualdisks.md
Normal file
84
content/blog/virtualdisks.md
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
---
|
||||||
|
title: "Virtual Disks"
|
||||||
|
date: 2020-01-06T22:26:58-05:00
|
||||||
|
draft: false
|
||||||
|
images: []
|
||||||
|
---
|
||||||
|
|
||||||
|
Have you wanted to [play with ZFS](https://wiki.archlinux.org/index.php/ZFS/Virtual_disks) or any other filesystem without creating a dedicated partition or device? We can do this through virtual disks!
|
||||||
|
|
||||||
|
First, we need to create a [sparse file](https://en.wikipedia.org/wiki/Sparse_file) called `scratch.img` with some max capacity. Let's say 2G.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
truncate -s 2G $HOME/scratch.img
|
||||||
|
```
|
||||||
|
|
||||||
|
Now the file is only sparse, if your filesystem supports it. To see, run `du -sh $HOME/scratch.img`. If it says that the size is `0` then your filesystem supports sparse files. Otherwise it does not.
|
||||||
|
|
||||||
|
Then, we can format the file with any filesystem we will like. One popular tool is `mkfs` which depending on your operating system can support [`bfs`](https://en.wikipedia.org/wiki/Be_File_System), [`cramfs`](https://en.wikipedia.org/wiki/Cramfs), [`ext2`](https://en.wikipedia.org/wiki/Ext2), [`ext3`](https://en.wikipedia.org/wiki/Ext3), [`ext4`](https://en.wikipedia.org/wiki/Ext4), [`fat`](https://en.wikipedia.org/wiki/File_Allocation_Table), [`minix`](https://en.wikipedia.org/wiki/MINIX_file_system), `msdos`, [`ntfs`](https://en.wikipedia.org/wiki/NTFS), [`vfat`](https://en.wikipedia.org/wiki/File_Allocation_Table#VFAT), [`reiserfs`](https://en.wikipedia.org/wiki/ReiserFS), etc.
|
||||||
|
|
||||||
|
To format with `ext4`,
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkfs -t ext4 $HOME/scratch.img
|
||||||
|
```
|
||||||
|
|
||||||
|
Then we can create the mount-point `/mnt/scratch` and mount `scratch.img` to it.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo mkdir /mnt/scratch
|
||||||
|
sudo mount -t auto -o loop $HOME/scratch.img /mnt/scratch
|
||||||
|
```
|
||||||
|
|
||||||
|
With this, we now have a mounted `ext4` filesystem on `/mnt/scratch`. `cd` into it and have a blast!
|
||||||
|
|
||||||
|
## Resizing the Virtual Disk
|
||||||
|
|
||||||
|
To resize the virtual disk, we will first need to unmount the virtual disk so we don't create inconsistencies.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo umount /mnt/scratch
|
||||||
|
```
|
||||||
|
|
||||||
|
Now we can extend or shrink the drive with `truncate`.
|
||||||
|
|
||||||
|
Extend by 1G: `truncate -s +1G $HOME/scratch.img`
|
||||||
|
|
||||||
|
Shrink by 1G: `truncate -s -1G $HOME/scratch.img`
|
||||||
|
|
||||||
|
Check the filesystem to make sure that no inconsistencies occured. With `ext(2/3/4)` we can do this with the `e2fsck` command.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
e2fsck $HOME/scratch.img
|
||||||
|
```
|
||||||
|
|
||||||
|
Then we need to tell the filesystem to resize itself. For `ext(2/3/4)` you can do this with the `resize2fs` command.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
resize2fs $HOME/scratch.img
|
||||||
|
```
|
||||||
|
|
||||||
|
Now the virtual disk is successfully resized! We can mount it back with the previous mount command.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo mount -t auto -o loop $HOME/scratch.img /mnt/scratch
|
||||||
|
```
|
||||||
|
|
||||||
|
## Removing the Virtual Disk
|
||||||
|
|
||||||
|
To remove the virtual disk, we first need to unmount the virtual drive
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo umount /mnt/scratch
|
||||||
|
```
|
||||||
|
|
||||||
|
Then we can remove the mount-point and file.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo rm -r /mnt/scratch
|
||||||
|
rm $HOME/scratch.img
|
||||||
|
```
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
With virtual disks we can experiment with different types of filesystems and perhaps try out snapshotting in supported filesystems. If we create virtual disks on [`tmpfs` ](/blog/lxdtmpfs/), then we can have a super fast file system as well!
|
42
content/blog/virtualenv.md
Normal file
42
content/blog/virtualenv.md
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
---
|
||||||
|
title: "Python Virtual Environments"
|
||||||
|
date: 2019-05-21T23:04:54-04:00
|
||||||
|
draft: false
|
||||||
|
---
|
||||||
|
|
||||||
|
Dependency management is hard. Luckily with Python there is a program called `virtualenv` that can help isolate different projects and manage dependencies.
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
To create a new python 3.7 environment type in the following:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
virtualenv --python=python3.7 environment_name
|
||||||
|
```
|
||||||
|
|
||||||
|
Of course you can replace the python version with whichever version you like. Now to go into the environment do the following:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
source environment_name/bin/activate
|
||||||
|
```
|
||||||
|
|
||||||
|
This now sets up your python interpretor and other utilities to use the installation in the `environment_name` folder. You can now install python packages using `pip` and have it only reside in this environment.
|
||||||
|
|
||||||
|
To save all currently installed packages into `requirements.txt`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip freeze > requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
You can then install those packages in a different virtualenv session with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
You can leave the virtualenv session with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
deactivate
|
||||||
|
```
|
||||||
|
|
11
content/blog/virtualizingwithclonezilla.md
Normal file
11
content/blog/virtualizingwithclonezilla.md
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
---
|
||||||
|
title: "Virtualizing Environments with Clonezilla"
|
||||||
|
date: 2019-08-25T20:09:28-04:00
|
||||||
|
draft: false
|
||||||
|
---
|
||||||
|
|
||||||
|
[Clonezilla](https://clonezilla.org/) advertises itself as a disk cloning and backup utility. I've been starting to think of it as a little more than that. Let's say that you go to a client site and they have a machine in production. Instead of messing around with their machine, you make a Clonezilla copy of it. You can then take it back and place it on a computer you're not using to play around with it.
|
||||||
|
|
||||||
|
Now even better, instead of using bare metal, what if you virtualize it? It's essentially the same process as putting it on baremetal, except now you have a virtual copy that you can make clones of, backup, etc.
|
||||||
|
|
||||||
|
Now if the OS is looking for hardware that doesn't exist, that might make life a bit harder....
|
45
content/blog/virtuallivecd.md
Normal file
45
content/blog/virtuallivecd.md
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
---
|
||||||
|
title: "Playing with Live CDs"
|
||||||
|
date: 2020-01-12T22:45:06-05:00
|
||||||
|
draft: false
|
||||||
|
images: []
|
||||||
|
---
|
||||||
|
|
||||||
|
I was curious on how Lubuntu 19.10 looked but I didn't feel like rebooting my computer and loading into a new ISO. Luckily there is a nice easy way to play around with live CDs.
|
||||||
|
|
||||||
|
Here's the command
|
||||||
|
|
||||||
|
```bash
|
||||||
|
virt-install --name=Lubuntu \
|
||||||
|
--nodisks --livecd \
|
||||||
|
--graphics spice \
|
||||||
|
--vcpu=4 \
|
||||||
|
--ram=4096 \
|
||||||
|
--os-type=linux \
|
||||||
|
--sound \
|
||||||
|
--accelerate \
|
||||||
|
--cdrom=$HOME/Downloads/lubuntu-19.10-desktop-amd64.iso
|
||||||
|
```
|
||||||
|
|
||||||
|
The arguments mean the following
|
||||||
|
|
||||||
|
| Argument | Meaning |
|
||||||
|
| ---------- | ------------------------------------------------------------ |
|
||||||
|
| nodisks | No storage disks are created |
|
||||||
|
| livecd | Set the boot to the cdrom after installation |
|
||||||
|
| graphics | Sets the graphics mode |
|
||||||
|
| vcpu | Number of virtual CPUs |
|
||||||
|
| RAM | Size of RAM |
|
||||||
|
| os-type | Linux/Windows/etc. |
|
||||||
|
| sound | Attach a virtual audio device |
|
||||||
|
| accelerate | Make use of the KVM or KQEMU kernel acceleration capabilities if available. |
|
||||||
|
| cdrom | Location of ISO |
|
||||||
|
|
||||||
|
Once you run this command once, the image is "installed". This means that we can easily access it in the future with the following commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
virsh start Lubuntu
|
||||||
|
virt-viewer Lubuntu
|
||||||
|
```
|
||||||
|
|
||||||
|
![1578887790276](/files/images/1578887790276.png)
|
23
content/blog/vncsetup.md
Normal file
23
content/blog/vncsetup.md
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
---
|
||||||
|
title: "VNC Setup"
|
||||||
|
date: 2019-05-24T23:21:43-04:00
|
||||||
|
draft: false
|
||||||
|
---
|
||||||
|
|
||||||
|
I mostly following this [Digital Ocean Tutorial](https://www.digitalocean.com/community/tutorials/how-to-install-and-configure-vnc-on-ubuntu-18-04) on getting a working VNC setup on my desktop computer. There is one main difference on my end since I use KDE. I edited the `/home/$USER/.vnc/xstartup` file to appear as
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/sh
|
||||||
|
unset SESSION_MANAGER
|
||||||
|
unset DBUS_SESSION_BUS_ADDRESS
|
||||||
|
dbus-launch --exit-with-session startkde
|
||||||
|
```
|
||||||
|
|
||||||
|
And I added the following script to make my life easier
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
ssh -L 5901:127.0.0.1:5901 -C -N -l rozek 192.168.0.104 &
|
||||||
|
krdc vnc://127.0.0.1:5901
|
||||||
|
```
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue