theread.me
This commit is contained in:
@ -1,212 +0,0 @@
|
||||
---
|
||||
layout: post
|
||||
title: "CSS Filters are awesome!"
|
||||
date: 2015-03-28 17:13:46
|
||||
permalink: css-filters/
|
||||
categories: programming
|
||||
author: Mahdi
|
||||
---
|
||||
|
||||
I've been working on the [CSS Filter Editor
|
||||
widget](https://bugzilla.mozilla.org/show_bug.cgi?id=1055181) in Firefox
|
||||
Developer Tools for a couple of weeks, thanks to [Patrick
|
||||
Brosset](https://medium.com/@patrickbrosset) for mentoring me and [Tim
|
||||
Nguyen](https://github.com/nt1m) for his great contributions.
|
||||
|
||||
Here is an [online version](http://mdibaiee.github.io/CSS-Filter-Tooltip/) to
|
||||
use as a playground. This version is modified to be cross-browser and therefore
|
||||
is a little different from the original widget used in Firefox.
|
||||
|
||||
You can also use [David Walsh's
|
||||
demo](http://davidwalsh.name/demo/css-filters.php), although it doesn't have as
|
||||
much flexibility.
|
||||
|
||||
CSS Filters are supported by most modern browsers ([Can I Use CSS
|
||||
Filters](http://caniuse.com/#feat=css-filters)), if your browser doesn't
|
||||
support this, please change your browser (I recommend
|
||||
[Firefox](https://www.mozilla.org/en-US/firefox/new/)).
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
CSS Filters introduce a few useful effects and some image adjusting functions,
|
||||
namely blur, drop-shadow, contrast, brightness, [and a few
|
||||
others](https://developer.mozilla.org/en-US/docs/Web/CSS/filter) which can be
|
||||
really useful if used properly.
|
||||
|
||||
A simple demo showing blur, contrast and brightness combined (hover over image):
|
||||
|
||||
<iframe width="100%" height="300" src="//jsfiddle.net/mdibaiee/zLmyhe7t/embedded/result,css" allowfullscreen="allowfullscreen" frameborder="0"></iframe>
|
||||
|
||||
I group filters by the type of value they take, let's explain them briefly:
|
||||
|
||||
Length
|
||||
------
|
||||
These filters accept a length value (px, em, cm, [etc](http://www.w3.org/Style/Examples/007/units)). blur is the only member of this family.
|
||||
|
||||
Percentage
|
||||
----------
|
||||
These filters accept percentage values, but if you omit the percentage sign,
|
||||
the value is multiplied by 100, e.g. `contrast(2)` is another way of writing
|
||||
`contrast(200%)`. Negative values have the same effect as zero.
|
||||
|
||||
Most filters explain themselves:
|
||||
|
||||
* brightness
|
||||
* contrast
|
||||
* grayscale
|
||||
* invert
|
||||
* opacity
|
||||
* saturate
|
||||
* sepia
|
||||
|
||||
### invert
|
||||
|
||||
I first understood how cool this filter can be after I saw Tim Nguyen using
|
||||
this in theme switching. Yeah you can't invert everything and "Yay, I have a
|
||||
new theme", but you can use invert on some elements and it works flawlessly,
|
||||
believe me.
|
||||
|
||||
<iframe width="100%" height="100" src="//jsfiddle.net/mdibaiee/373dnby8/embedded/result,css" allowfullscreen="allowfullscreen" frameborder="0"></iframe>
|
||||
|
||||
### opacity
|
||||
You might wonder why do we have this function, as we already have an opacity
|
||||
property in CSS, that's because the opacity property is not hardware
|
||||
accelerated, but the filter property is hardware accelerated in most
|
||||
browsers, which includes this function.
|
||||
|
||||
Angle
|
||||
-----
|
||||
hue-rotate is the only function to accept an angle value (degree / radian).
|
||||
|
||||
###hue-rotate
|
||||
If you're familiar with [Hue](https://en.wikipedia.org/wiki/Hue) you probably
|
||||
know that it's measured by angles. The hue-rotate rotates the hue circle of
|
||||
an image relative to it's current hue value (360 and 0 have the same
|
||||
results).
|
||||
|
||||
<iframe width="100%" height="300" src="//jsfiddle.net/mdibaiee/smk922fh/embedded/result,css" allowfullscreen="allowfullscreen" frameborder="0"></iframe>
|
||||
|
||||
Special
|
||||
-------
|
||||
These filter's don't fit in any of the groups above, they have special/mixed values.
|
||||
|
||||
### drop-shadow
|
||||
The drop-shadow filter accepts a *shadow-list*, four length values, and one
|
||||
color. box-shadow and text-shadow also accept shadow lists.
|
||||
|
||||
You're probably familiar with shadow lists already: `drop-shadow(x y radius spread color)`.
|
||||
Unfortunaly spread doesn't work in either Chrome or Firefox as of this writing — It is treated as an error.
|
||||
|
||||
drop-shadow is pretty cool, as it doensn't have the limitations of box-shadow
|
||||
and text-shadow. box-shadow applies a shadow to the outer shape, but
|
||||
drop-shadow applies a shadow to elements independant to their shape, they
|
||||
might be triangles, PNG's with transparent background or just anything.
|
||||
|
||||
drop-shadow clones the element's image, moves it to the offset defined,
|
||||
applies blur and changes it's color, putting it under the original element.
|
||||
Couldn't do it better:
|
||||
|
||||

|
||||
|
||||
Here is an example, a PNG image with transparent background and a CSS triangle made using the border hack:
|
||||
|
||||
<iframe width="100%" height="150" src="//jsfiddle.net/mdibaiee/z077vbs0/embedded/result,css" allowfullscreen="allowfullscreen" frameborder="0"></iframe>
|
||||
|
||||
### url
|
||||
With the url function we have the power of CSS and SVG Filters in one place.
|
||||
You can reference an SVG element by linking to it with a hash of the filter
|
||||
element's ID:
|
||||
|
||||
{% highlight css %}
|
||||
filter: url(/example.svg#filter)
|
||||
{% endhighlight %}
|
||||
|
||||
If you want to know more about SVG Filters, I recommend [MDN's tutorial on SVG Filters](https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Filter_effects).
|
||||
|
||||
### Custom
|
||||
Now those filters are pretty cool, but what if I told you this is [going to be] done with CSS?
|
||||
|
||||

|
||||
|
||||
{% include caption.html text='Source: http://www.adobe.com/devnet/archive/html5/articles/css-shaders.html' %}
|
||||
|
||||
Custom Filters allows usage of vertex and fragment shaders which run directly
|
||||
in the GPU. Custom filters' specs is subject to change, so there's no
|
||||
implementation yet. For more info on this topic follow the links below:
|
||||
|
||||
* [Getting started with CSS custom filters](http://alteredqualia.com/css-shaders/article/#shaders)
|
||||
* [Introducing CSS shaders: Cinematic effects for the web](http://www.adobe.com/devnet/archive/html5/articles/css-shaders.html)
|
||||
* [CSS shaders specifications](http://dev.w3.org/fxtf/custom/)
|
||||
|
||||
Gotchas
|
||||
=======
|
||||
|
||||
You now have a basic understanding of filters, good. Here are a few gotchas
|
||||
you'd better know.
|
||||
|
||||
Order matters
|
||||
-------------
|
||||
The order in which filters are applied matters. Take this example:
|
||||
|
||||
{% highlight css %}
|
||||
filter: blur(10px) contrast(2);
|
||||
{% endhighlight %}
|
||||
|
||||
Hey, browser, please blur the element, then double the contrast of the blurred
|
||||
element. (blurred parts have their contrast affected)
|
||||
|
||||
{% highlight css %}
|
||||
filter: contrast(2) blur(10px);
|
||||
{% endhighlight %}
|
||||
|
||||
Hey browser, please double the contrast of my element, then blur it out. (high
|
||||
contrast image is blurred normally)
|
||||
|
||||
Here is the actual comparison:
|
||||
|
||||
<iframe width="100%" height="300" src="//jsfiddle.net/mdibaiee/608yoqrx/embedded/result,css" allowfullscreen="allowfullscreen" frameborder="0"></iframe>
|
||||
|
||||
Inheritance
|
||||
-----------
|
||||
|
||||
Okay, you now know the order of filters matters, the filter property is not
|
||||
actually *inherited*, but when you apply a filter on a parent element, of
|
||||
course it's children are affected too, but what if the children have their own
|
||||
css filters? Ah-ha! CSS properties are applied bottom-up, which means
|
||||
childrens' filters are applied first.
|
||||
|
||||
<iframe width="100%" height="300" src="//jsfiddle.net/mdibaiee/o40d7cs7/embedded/result,css" allowfullscreen="allowfullscreen" frameborder="0"></iframe>
|
||||
|
||||
Implementation
|
||||
--------------
|
||||
|
||||
I said using the url function we have "the power of CSS and SVG filters in one
|
||||
place", but the CSS filters are actually implemented using SVG filters! You
|
||||
know, the functions are actually referencing to an svg generated in the
|
||||
browser. Here is the list of [CSS Filter
|
||||
equivalents](http://www.w3.org/TR/filter-effects/#ShorthandEquivalents).
|
||||
|
||||
Go Wild
|
||||
=======
|
||||
|
||||
You can use CSS Filter on *any* element, experiment different things; `<video>`, `<canvas>`, `<iframe>`, GIF images, etc.
|
||||
|
||||
My try (although I couldn't get the GIF and CSS animation to be in sync, know a solution? Leave a comment please):
|
||||
|
||||
<iframe width="100%" height="300" src="//jsfiddle.net/mdibaiee/hjaL0ka3/embedded/result,css,js" allowfullscreen="allowfullscreen" frameborder="0"></iframe>
|
||||
|
||||
-----
|
||||
|
||||
That's it, you can subscribe to my [RSS]({{ "/feed.xml" | prepend: site.baseurl | prepend: site.url }}) or follow me on [Twitter](https://twitter.com/{{ site.twitter_username }}) to get more articles like this.
|
||||
|
||||
<style>
|
||||
#mahdi {
|
||||
transition: filter 1s ease;
|
||||
-webkit-transition: filter 1s ease;
|
||||
}
|
||||
#mahdi:hover {
|
||||
filter: blur(3px);
|
||||
-webkit-filter: blur(3px);
|
||||
}
|
||||
</style>
|
@ -4,7 +4,6 @@ title: "BroadcastChannel API"
|
||||
date: 2015-04-02 17:13:46
|
||||
permalink: broadcastchannel-api/
|
||||
categories: programming
|
||||
author: Mahdi
|
||||
---
|
||||
|
||||
[BroadcastChannel API](https://developer.mozilla.org/en-US/docs/Web/API/Broadcast_Channel_API)
|
||||
|
@ -1,178 +0,0 @@
|
||||
---
|
||||
layout: post
|
||||
title: "ES7 Array and Generator comprehensions"
|
||||
date: 2015-06-06 13:47:00
|
||||
permalink: es7-array-generator-comprehensions/
|
||||
categories: programming
|
||||
author: Mahdi
|
||||
---
|
||||
|
||||
Array comprehension is a new feature proposed for ES7, with a new syntax to
|
||||
create new arrays from existing
|
||||
[iterables](http://www.2ality.com/2015/02/es6-iteration.html), comprehensions
|
||||
can replace map and filter.
|
||||
|
||||
Generator comprehension brings the same feature to generators, this is a more
|
||||
useful feature as it removes the need to write new generators for simple
|
||||
map/filter operations.
|
||||
|
||||
Generator comprehensions allow us to easily write single-line generators, which
|
||||
can replace our arrays in some situations, you might ask why we might consider
|
||||
replacing arrays with generators, the most important reason is their
|
||||
[laziness](#laziness). I've explained laziness later in the article.
|
||||
|
||||
Comprehensions are currently only supported by Firefox, use Firefox 30+ or
|
||||
[Babel](https://babeljs.io/repl/) to run the examples. The Node.js version of
|
||||
examples using generator `function* ()`s is available at the
|
||||
[repository](https://github.com/mdibaiee/array-vs-generator) (doesn't require
|
||||
transpilation, use latest node).
|
||||
|
||||
Syntax
|
||||
======
|
||||
|
||||
The syntax is pretty simple, you can only use `for of` and `if` inside comprehensions.
|
||||
|
||||
Array comprehensions:
|
||||
{% highlight javascript %}
|
||||
let numbers = [1,2,3,4,5];
|
||||
|
||||
let even = [ for (n of numbers) if (n % 2 === 0) n ];
|
||||
// equivalent:
|
||||
// let even = numbers.filter(n => n % 2 === 0);
|
||||
|
||||
console.log(...even); // 2 4
|
||||
{% endhighlight %}
|
||||
|
||||
Generator comprehensions:
|
||||
{% highlight javascript %}
|
||||
// yield 0...5
|
||||
let generator = function* () {
|
||||
for (let i = 0; i < 6; i++) {
|
||||
yield i;
|
||||
}
|
||||
}
|
||||
|
||||
let squared = ( for (n of generator()) n * n );
|
||||
// equivalent:
|
||||
// let squared = Array.from(generator()).map(n => n * n);
|
||||
|
||||
console.log(...squared); // 0 1 4 9 16 25
|
||||
{% endhighlight %}
|
||||
|
||||
You can also nest comprehensions:
|
||||
|
||||
{% highlight javascript %}
|
||||
// yield 0...5
|
||||
let generator = function* () {
|
||||
for (let i = 0; i < 6; i++) {
|
||||
yield i;
|
||||
}
|
||||
}
|
||||
|
||||
// yield three numbers after number
|
||||
let after = function* (number) {
|
||||
for (let i = 1; i < 4; i++) {
|
||||
yield number + i;
|
||||
}
|
||||
}
|
||||
|
||||
// for each number 0...5, yield an array of 3 numbers after it
|
||||
let nested = ( for (n of generator())
|
||||
[ for (i of after(n)) i ]
|
||||
)
|
||||
|
||||
console.table(Array.from(nested));
|
||||
// 1, 2, 3
|
||||
// 2, 3, 4
|
||||
// 3, 4, 5
|
||||
// 4, 5, 6
|
||||
// 5, 6, 7
|
||||
// 6, 7, 8
|
||||
{% endhighlight %}
|
||||
|
||||
Laziness
|
||||
========
|
||||
This is one of the most important advantages of generators over arrays and
|
||||
things alike. The reason why I'm including this here is to give you a good
|
||||
reason to write generators instead of arrays while generator comprehensions make
|
||||
it extremely easy to write them — this is a proof of their usefulness.
|
||||
|
||||
In programming, laziness means doing nothing until the results are requested or
|
||||
in simpler terms, avoiding unnecessary work. For example, when you create an
|
||||
array and map it, the result will be evaluated no matter you need it now or not,
|
||||
you need the whole thing or a part of it, etc.
|
||||
|
||||
Take this example:
|
||||
|
||||
{% highlight javascript %}
|
||||
let bigArray = new Array(100000);
|
||||
for (let i = 0; i < 100000; i++) {
|
||||
bigArray[i] = i;
|
||||
}
|
||||
|
||||
let first = bigArray.map(n => n * n)[0];
|
||||
console.log(first);
|
||||
{% endhighlight %}
|
||||
|
||||
You know what happens here, first, map is evaluated, returning thousands of
|
||||
squared numbers, then the first element is returned. We must allocate and
|
||||
evaluate the whole squared array to be able to know about it's first or second
|
||||
element.
|
||||
|
||||
Think of optimizing it, is it possible to get the desired result without storing
|
||||
temporary arrays in memory? Can we get the first number directly without
|
||||
consuming a big chunk of memory?
|
||||
|
||||
Yes, using generators, Look at this:
|
||||
|
||||
{% highlight javascript %}
|
||||
let bigGenerator = function* () {
|
||||
for (let i = 0; i < 100000; i++) {
|
||||
yield i;
|
||||
}
|
||||
}
|
||||
|
||||
let squared = ( for (n of bigGenerator()) n * n );
|
||||
|
||||
console.log(squared.next());
|
||||
{% endhighlight %}
|
||||
|
||||
Let's see what happens in this case. Here, we create a generator which will
|
||||
yield numbers 0...100000, nothing is actually allocated or evaluated, we just
|
||||
have a generator which will return a new number every time we call `next()`.
|
||||
Then we use generator comprehension to create another generator which squares
|
||||
the numbers our `bigGenerator()` yields, again, we don't evaluate or allocate
|
||||
anything, we just create a generator which will call another generator's
|
||||
`next()` method, square the results, and yield it.
|
||||
|
||||
Now when we call `squared.next()`, the `squared` generator calls
|
||||
`bigArray().next()`, squares the results and yields it, it doesn't do any
|
||||
unnecessary work, it's lazy.
|
||||
|
||||
[
|
||||

|
||||
](/img/generator-diagram.png)
|
||||
{% include caption.html text='Calling squared.next() 4 times' %}
|
||||
|
||||
If you profile heap/memory usage and running time, you will see the difference.
|
||||
|
||||
I have prepared a Node.js version of the test case. With the help of [`process.memoryUsage()`](https://nodejs.org/api/process.html#process_process_memoryusage) and [`console.time`](https://developer.mozilla.org/en-US/docs/Web/API/Console/time) we can easily see the difference.
|
||||
It's a lot faster, with less space required, isn't that awesome?
|
||||
|
||||
[Repository: mdibaiee/array-vs-generator](https://github.com/mdibaiee/array-vs-generator)
|
||||
|
||||

|
||||
|
||||
If you want to know more about lazy iterators, I recommend raganwald's [Lazy Iterables in JavaScript](http://raganwald.com/2015/02/17/lazy-iteratables-in-javascript.html).
|
||||
|
||||
More:
|
||||
|
||||
[MDN: Array Comprehensions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Array_comprehensions)
|
||||
|
||||
[MDN: Generator Comprehensions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Generator_comprehensions)
|
||||
|
||||
[MDN: for...of](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of)
|
||||
|
||||
[MDN: Iterators and Generators](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators?redirectlocale=en-US&redirectslug=JavaScript%2FGuide%2FIterators_and_Generators)
|
||||
|
||||
[ES6 in Depth: Generators](https://hacks.mozilla.org/2015/05/es6-in-depth-generators/?utm_source=javascriptweekly&utm_medium=email)
|
@ -1,221 +0,0 @@
|
||||
---
|
||||
layout: post
|
||||
title: "Autocomplete using Tries"
|
||||
date: 2015-07-24 09:44:00
|
||||
permalink: autocomplete-predict-trie/
|
||||
categories: programming
|
||||
author: Mahdi
|
||||
---
|
||||
|
||||
In this article, I'm going over creating an autocompletion/prediction system
|
||||
using a data-structure called Trie, it's fast and easy to customize.
|
||||
|
||||
Trie
|
||||
====
|
||||
|
||||
[Trie](https://en.wikipedia.org/wiki/Trie) is a simple data-structure most commonly used as a dictionary, it looks like so:
|
||||
|
||||

|
||||
|
||||
As you see, it's just a *tree*, a set of nodes connected to other [child] nodes, but the nodes have a special relationship:
|
||||
|
||||
Each child node extends it's parent with one extra character.
|
||||
|
||||
{% highlight javascript %}
|
||||
// Something like this
|
||||
child.value = parent.value + 'c';
|
||||
{% endhighlight %}
|
||||
|
||||
It's pretty easy to traverse this tree and predict the next possible words.
|
||||
|
||||
Implementation
|
||||
--------------
|
||||
|
||||
We're going to use ES6 classes to create our `Trie` and `Node` classes.
|
||||
|
||||
Let's start with our simple Node class:
|
||||
{% highlight javascript %}
|
||||
class Node {
|
||||
constructor(value = '') {
|
||||
this.value = value;
|
||||
this.children = [];
|
||||
}
|
||||
}
|
||||
{% endhighlight %}
|
||||
|
||||
Unlike [binary trees](https://en.wikipedia.org/wiki/Binary_tree) where each node has a left and right child, Trie nodes don't necessarily have a limit on how many children they can have.
|
||||
|
||||
Trie class:
|
||||
{% highlight javascript %}
|
||||
class Trie {
|
||||
constructor() {
|
||||
this.root = new Node();
|
||||
}
|
||||
|
||||
add(value, parent = this.root) {
|
||||
for (let i = 0, len = value.length; i < len; i++) {
|
||||
let node = parent.children.find(child => child.value[i] === value[i]);
|
||||
|
||||
if (!node) {
|
||||
node = new Node(value.slice(0, i + 1));
|
||||
parent.children.push(node);
|
||||
}
|
||||
|
||||
parent = node;
|
||||
}
|
||||
|
||||
return parent;
|
||||
}
|
||||
|
||||
find(value, parent = this.root) {
|
||||
for (let i = 0, len = value.length; i < len; i++) {
|
||||
parent = parent.children.find(child => child.value[i] === value[i]);
|
||||
|
||||
if (!parent) return null;
|
||||
}
|
||||
return parent;
|
||||
}
|
||||
}
|
||||
{% endhighlight %}
|
||||
Every Trie must have a root node with empty value, that's how our
|
||||
single-character nodes follow the rule of Tries.
|
||||
|
||||
Ok, our first method, `add` handles adding a value to the trie, creating
|
||||
necessary parent nodes for our value. At each iteration, we compare the `i`th
|
||||
character of our value, with `i`th character of current node's children's value,
|
||||
if we find one, we continue to search the next branch, else, we create a node
|
||||
with `value.slice(0, i + 1)` and move onto the created node.
|
||||
|
||||
It might be a little hard to grasp at first, so I created a visualization of
|
||||
this method to help you understand it easier, take a look:
|
||||
[Trie Visualization](https://mdibaiee.github.io/autocomplete-trie/demo/add.html)
|
||||
|
||||
Then we have our find method, which searches for the given value in the trie.
|
||||
The algorithm for searching is the same, comparing by index and moving to the
|
||||
next branch.
|
||||
|
||||
# Example
|
||||
|
||||
That's it for our simple Trie class, now let's create an actual input with
|
||||
autocomplete functionality using our Trie.
|
||||
|
||||
{% highlight html %}
|
||||
<input>
|
||||
|
||||
<div class='results'>
|
||||
|
||||
</div>
|
||||
{% endhighlight %}
|
||||
|
||||
I put some random names and stuff into three categories, results: [data.json](https://mdibaiee.github.io/autocomplete-trie/demo/data.json)
|
||||
|
||||
Now we have to create a Trie of our data:
|
||||
{% highlight javascript %}
|
||||
const trie = new Trie();
|
||||
|
||||
let data = {...}; // read from data.json
|
||||
|
||||
for (let category in data) {
|
||||
for (let item of data[category]) {
|
||||
let node = trie.add(item);
|
||||
node.category = category;
|
||||
}
|
||||
}
|
||||
{% endhighlight %}
|
||||
As simple as that, our trie is made, it looks like this: [Data](https://mdibaiee.github.io/autocomplete-trie/demo/data.html)
|
||||
|
||||
Now, let's actually show results:
|
||||
{% highlight javascript %}
|
||||
const input = document.querySelector('input');
|
||||
const results = document.querySelector('#results');
|
||||
|
||||
input.addEventListener('keyup', () => {
|
||||
results.innerHTML = '';
|
||||
|
||||
const nodes = trie.find(input.value);
|
||||
|
||||
if (!nodes) return;
|
||||
|
||||
for (let node of nodes.children) {
|
||||
const category = node.category ? `- ${node.category}` : '';
|
||||
|
||||
results.innerHTML += `<li>${node.value} ${category}</li>`;
|
||||
}
|
||||
});
|
||||
{% endhighlight %}
|
||||
[Autocomplete 1](https://mdibaiee.github.io/autocomplete-trie/1.html)
|
||||

|
||||
This will only show the instant-childs of the word entered, but that's not what
|
||||
we want, we want to show *complete* words, how do we do that?
|
||||
|
||||
First, we need a way to detect complete words, we can have a flag to recognize
|
||||
complete words, we can modify our `add` method to automatically flag whole words
|
||||
or we can manually add the flag after adding the node, as we did by setting a
|
||||
category for our words, so we already have a flag to recognize whole words,
|
||||
that's our `category` property, now let's add a new method to our Trie class to
|
||||
find whole words.
|
||||
|
||||
{% highlight javascript %}
|
||||
...
|
||||
findWords(value, parent = this.root) {
|
||||
let top = this.find(value, parent);
|
||||
if (!top) return [];
|
||||
|
||||
let words = [];
|
||||
|
||||
top.children.forEach(function getWords(node) {
|
||||
if (node.category) words.push(node);
|
||||
node.children.forEach(getWords);
|
||||
});
|
||||
|
||||
return words;
|
||||
}
|
||||
...
|
||||
{% endhighlight %}
|
||||
|
||||
And change our event listener like so:
|
||||
{% highlight javascript %}
|
||||
const input = document.querySelector('input');
|
||||
const results = document.querySelector('#results');
|
||||
|
||||
input.addEventListener('keyup', () => {
|
||||
results.innerHTML = '';
|
||||
|
||||
const nodes = trie.findWords(input.value); // << Change
|
||||
|
||||
if (!nodes.length) return; // << Change
|
||||
|
||||
for (let node of nodes) { // << Change
|
||||
const category = node.category ? `- ${node.category}` : '';
|
||||
|
||||
results.innerHTML += `<li>${node.value} ${category}</li>`;
|
||||
}
|
||||
});
|
||||
{% endhighlight %}
|
||||
[Autocomplete 2](https://mdibaiee.github.io/autocomplete-trie/2.html)
|
||||

|
||||
Ta-daa!
|
||||
|
||||
We have our autocomplete working! Let's add zsh-like-tab-to-next-char functionality.
|
||||
|
||||
{% highlight javascript %}
|
||||
input.addEventListener('keydown', e => {
|
||||
// Tab Key
|
||||
if (e.keyCode === 9) {
|
||||
e.preventDefault();
|
||||
const current = trie.find(input.value);
|
||||
|
||||
if (!current.children.length) return;
|
||||
|
||||
input.value = current.children[0].value;
|
||||
}
|
||||
});
|
||||
{% endhighlight %}
|
||||
|
||||
That's it! We have an input with autocomplete and tab-to-next-char. Isn't it awesome?
|
||||
|
||||
[Final Result](https://mdibaiee.github.io/autocomplete-trie/2.html)
|
||||
|
||||
*Pst! I have a repository of algorithm implementations in ES6, you might want to
|
||||
take a look!
|
||||
[mdibaiee/harmony-algorithms](https://github.com/mdibaiee/harmony-algorithms)*
|
@ -1,116 +0,0 @@
|
||||
---
|
||||
layout: post
|
||||
title: "Open-source: The Good, The Bad and The Ugly"
|
||||
date: 2015-10-13 06:15:00
|
||||
permalink: open-source-good-bad-ugly/
|
||||
categories: programming
|
||||
author: Mahdi
|
||||
---
|
||||
|
||||
I have been doing Open-source for a while, I don't call myself an "expert" or
|
||||
something like that, but I'd like to share my opinion and experience on
|
||||
contributing to, and maintaining open-source code.
|
||||
|
||||
So, I've been following and contributing to open-source projects for quite a
|
||||
time, and I have had different experiences every time. There are always good and
|
||||
bad experiences along a road, it's never a heaven, never a hell. I've had
|
||||
contributions as small as fixing a typo in README, and as big as adding a new
|
||||
feature to Firefox Developer Tools or refactoring a whole repository!
|
||||
|
||||
Here I'm going to share my experiences and what I've learned along the way that
|
||||
you should consider if you want to take this road.
|
||||
|
||||
# The Good I love open-source, it's awesome how people share their efforts with
|
||||
others, and others give feedback to the maintainer to make the software better.
|
||||
It's an always-growing system, even if a maintainer stops maintaining, it's
|
||||
possible to _fork_ a repository and continue it, although not as easy, but
|
||||
possible.
|
||||
|
||||
The best part of doing open-source, in my opinion, is building connections and
|
||||
learning from others.
|
||||
|
||||
Whether you are maintaining or contributing to a project, you are going to learn
|
||||
new things, it just happens.
|
||||
|
||||
If you are a maintainer of a repository with a countable amount of users, you
|
||||
are going to constantly learn your mistakes from others, finding these mistakes
|
||||
by yourself is really hard, because you can't easily look at a subject _the
|
||||
other way_, but users have this potential to look at your code with their eyes,
|
||||
seeing mistakes you can't see.
|
||||
|
||||
If you are contributing, following or just exploring projects, you are
|
||||
definitely going to learn, the solutions people suggest to a problem, the way
|
||||
they communicate, etc. Usually, not always, the maintainer has a better
|
||||
knowledge over the subject of project than you, so you are going to learn from
|
||||
him and other contributors by reading their code or exploring the issues and how
|
||||
they've been solved. I personally learned a lot this way. I would volunteer to
|
||||
fix a bug, then the maintainer and other contributors would show up to give
|
||||
their suggestions and ideas on the issue, which I would then learn from. I also
|
||||
subscribe to interesting issues that I don't know how to fix to see how they get
|
||||
solved.
|
||||
|
||||
# The Bad First off, the most annoying thing about open-source contributions is
|
||||
that people (I'm looking at you, maintainers) think that contributors are
|
||||
jobless bored people who don't know how to spend their time and have come to
|
||||
waste some time on some random open-source project, NO, seriously.
|
||||
|
||||
I have a job, I totally care about my time and I'm not making a Pull-request
|
||||
because I'm bored.
|
||||
|
||||
Now, why is that important to know: it has happened to me a couple of times that
|
||||
I ask on an issue:
|
||||
|
||||
_"- Okay, I'm interested, what **exactly** has to be done?"_
|
||||
|
||||
_"- Great, please do x, y, z"_
|
||||
|
||||
_... some time later_
|
||||
|
||||
_"- Here is x, y and z, please review and merge"
|
||||
|
||||
_"- Oh, thank you very much, but you know, now that I think of it, I don't want
|
||||
x, y or even z. Closing."
|
||||
|
||||
and I'm like:
|
||||
|
||||
<!--  --> <img alt='Are you
|
||||
kidding me?' src='/img/are-you-kidding-me.jpg' />
|
||||
|
||||
{% include caption.html text='Are you kidding me?' %}
|
||||
|
||||
This is the worst thing that can happen to you, try to avoid it, you don't want
|
||||
your valuable time wasted.
|
||||
|
||||
How to avoid it you ask, there is a sign that I've found which leads to this
|
||||
problem most of the time and that's **lack of clear specification**, just like
|
||||
with clients, if the maintainer doesn't specify what should be done, you should
|
||||
stop.
|
||||
|
||||
It happened to me, just like the past discussion, except he didn't tell me
|
||||
_"Please do x, y, z"_, he made himself look too busy and said: _"The title says
|
||||
it all"_, no, it doesn't say it all. "x, y and z" can be implemented in `2^9`
|
||||
ways, and sadly, you are not going to accept the `192`th way, as you "don't like
|
||||
it". Do not get trapped in these time-wasting situations, I wish maintainers
|
||||
understood how valuable people's times are.
|
||||
|
||||
# The Ugly The sad part about open-source is, if the maintainer decides not to
|
||||
support the project anymore, people will _kind of_ suffer. If the maintainer
|
||||
abandons the project, the project is *almost* doomed, as forking and continuing
|
||||
is really hard, reading the code from bottom up and understanding it isn't easy,
|
||||
and as there is no outcome, people usually decide to abandon a project once they
|
||||
lose interest in the topic.
|
||||
|
||||
If I personally lose interest in a project I've made, I'll abandon it, I will
|
||||
try to guide new users through but I usually don't offer as much support, I have
|
||||
more important things to do, it's really sad, but true.
|
||||
|
||||
To prevent this from happening, you must be able to make money out of your
|
||||
project, or your project must be really interesting and challenging to keep you
|
||||
working on it.
|
||||
|
||||
------
|
||||
|
||||
That's it, please note that everything you read here is my opinion, it's not a
|
||||
rule, not a judgment, it's my opinion and experience. If you would like to
|
||||
discuss this further, put a comment below or reach me at
|
||||
[twitter](https://twitter.com/mdibaiee).
|
@ -4,7 +4,6 @@ title: "Immortals go extinct"
|
||||
permalink: immortals-go-extinct/
|
||||
categories: life
|
||||
excerpt_separator: <!--more-->
|
||||
author: Mahdi
|
||||
---
|
||||
|
||||
We are all going to die, we all know that well.<br /><br /> Now I want to take
|
||||
|
@ -4,7 +4,6 @@ title: "Stop High-Frequency Fuck-ups"
|
||||
permalink: stop-high-frequency-fuck-ups/
|
||||
categories: life
|
||||
excerpt_separator: <!--more-->
|
||||
author: Mahdi
|
||||
---
|
||||
|
||||
**High-Frequency Fuck-Ups**:
|
||||
|
@ -4,7 +4,6 @@ title: "Don't chase: Become the good one"
|
||||
permalink: dont-chase-become-the-good-one/
|
||||
categories: life
|
||||
excerpt_separator: <!--more-->
|
||||
author: Mahdi
|
||||
---
|
||||
|
||||
When it comes to relationships, most (unsuccessful) people are _chasing_ the
|
||||
|
@ -7,7 +7,6 @@ categories:
|
||||
- primitive-living
|
||||
- travel
|
||||
excerpt_separator: <!--more-->
|
||||
author: Mahdi
|
||||
---
|
||||
|
||||

|
||||
|
@ -4,7 +4,6 @@ title: "Difference between Travis CI tests: PR and Push"
|
||||
permalink: travis-ci-pr-push/
|
||||
categories: programming
|
||||
excerpt_separator: <!--more-->
|
||||
author: Mahdi
|
||||
---
|
||||
|
||||
I just want to leave this here as I often tend to look it up myself and the
|
||||
|
@ -6,7 +6,6 @@ categories:
|
||||
- life
|
||||
- diy
|
||||
excerpt_separator: <!--more-->
|
||||
author: Mahdi
|
||||
---
|
||||
|
||||

|
||||
|
@ -8,7 +8,6 @@ categories:
|
||||
- math
|
||||
math: true
|
||||
toc: true
|
||||
author: Mahdi
|
||||
---
|
||||
|
||||
I wanted to get proficient in Haskell so I decided to follow [An [Essential]
|
||||
|
@ -5,7 +5,6 @@ date: 2017-10-19
|
||||
permalink: mathematical-induction-proving-tiling-methods/
|
||||
categories: math
|
||||
math: true
|
||||
author: Mahdi
|
||||
---
|
||||
|
||||
On my way towards self-taught data science, I've stumbled upon the need to be
|
||||
|
@ -5,7 +5,6 @@ date: 2019-02-11
|
||||
permalink: self-hosted/
|
||||
categories: life
|
||||
excerpt_separator: <!--more-->
|
||||
author: Mahdi
|
||||
---
|
||||
|
||||
Since 3 years ago, I have always been eager to move away from Google and other
|
||||
|
@ -4,7 +4,6 @@ title: "Iran Sanctions: A Story of Discrimination and Isolation"
|
||||
date: 2019-07-27 17:13:46
|
||||
permalink: sanctions-discrimination-isolation-iran/
|
||||
categories: life, iran
|
||||
author: Mahdi
|
||||
---
|
||||
|
||||
Let me take you through a story on what it feels like to be isolated from the
|
||||
|
@ -4,7 +4,6 @@ title: "Depression as an Umwelt"
|
||||
date: 2021-02-14 00:00:00
|
||||
permalink: depression-as-an-umwelt/
|
||||
categories: personal, philosophy
|
||||
author: Mahdi
|
||||
---
|
||||
|
||||
[Von Uexküll’s A Stroll Through the Worlds of Animals and
|
||||
|
@ -4,7 +4,6 @@ title: "A Fear of Worlds Unknown"
|
||||
date: 2021-04-05 00:00:00
|
||||
permalink: a-fear-of-worlds-unknown/
|
||||
categories: personal, philosophy
|
||||
author: Mahdi
|
||||
---
|
||||
|
||||
When it comes to ethics, the impossibly hard question of ethics, we get dizzy when we try to think about relativity of it. The ideas of 4E cognition and imminence have been closer to my heart than the alternative approaches, however, I have always been dumbfounded by the question of ethics in such a framework.
|
||||
|
@ -5,7 +5,6 @@ subtitle: "An Alternative Definition of Objectivity for Rigid Scientists"
|
||||
date: 2021-10-30 12:57:46
|
||||
permalink: alternative-objectivity-and-inherent-subjectivity/
|
||||
categories: philosophy
|
||||
author: Mahdi
|
||||
---
|
||||
|
||||
As a computer scientist and someone who loves
|
||||
|
@ -4,7 +4,6 @@ title: "My first contribution to Linux Kernel: Step by step"
|
||||
date: 2021-11-06 00:00:00
|
||||
permalink: first-linux-contribution/
|
||||
categories: programming
|
||||
author: Mahdi
|
||||
published: false
|
||||
---
|
||||
|
||||
|
@ -5,7 +5,6 @@ subtitle: Using `rust-lldb` to understand rust memory internals
|
||||
date: 2022-06-16 00:00:00
|
||||
permalink: rust-box-str-vs-string/
|
||||
categories: programming
|
||||
author: Mahdi
|
||||
---
|
||||
|
||||
Today I and a friend went down a rabbit hole about Rust and how it manages the
|
||||
@ -28,10 +27,10 @@ bytes).
|
||||
I will be using `rust-lldb` throughout this post to understand what is going on
|
||||
in the rust programs we write and run. The source code for this blog post is
|
||||
available on
|
||||
[mdibaiee/rust-memory-playground](https://git.mahdi.blog/mahdi/rust-memory-playground).
|
||||
[rust-memory-playground](https://git.theread.me/thereadme/rust-memory-playground).
|
||||
|
||||
```bash
|
||||
git clone https://git.mahdi.blog/mahdi/rust-memory-playground
|
||||
git clone https://git.theread.me/thereadme/rust-memory-playground
|
||||
cd rust-memory-playground
|
||||
```
|
||||
|
||||
@ -63,7 +62,7 @@ $ cargo build && rust-lldb target/debug/stack-program
|
||||
Breakpoint 1: where = stack-program`stack_program::add_ten::h42edbf0bdcb04851 + 24 at main.rs:3:5, address = 0x0000000100001354
|
||||
|
||||
(lldb) run
|
||||
Process 65188 launched: '/Users/mahdi/workshop/rust-memory-playground/target/debug/stack-program' (arm64)
|
||||
Process 65188 launched: '/Users/workshop/rust-memory-playground/target/debug/stack-program' (arm64)
|
||||
Process 65188 stopped
|
||||
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
|
||||
frame #0: 0x0000000100001354 stack-program`stack_program::add_ten::h42edbf0bdcb04851(a=5) at main.rs:3:5
|
||||
@ -154,7 +153,7 @@ $ cargo build && rust-lldb target/debug/stack-and-heap-program
|
||||
Breakpoint 1: where = stack-and-heap-program`stack_and_heap_program::main::ha895783273646dc7 + 100 at main.rs:4:5, address = 0x0000000100005264
|
||||
|
||||
(lldb) run
|
||||
Process 67451 launched: '/Users/mahdi/workshop/rust-memory-playground/target/debug/stack-and-heap-program' (arm64)
|
||||
Process 67451 launched: '/Users/workshop/rust-memory-playground/target/debug/stack-and-heap-program' (arm64)
|
||||
Process 67451 stopped
|
||||
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
|
||||
frame #0: 0x0000000100005264 stack-and-heap-program`stack_and_heap_program::main::ha895783273646dc7 at main.rs:4:5
|
||||
@ -202,7 +201,7 @@ And here we go again:
|
||||
Breakpoint 1: where = string-program`string_program::main::h64ca96ee87b0ceaf + 44 at main.rs:3:5, address = 0x000000010000476c
|
||||
|
||||
(lldb) run
|
||||
Process 68317 launched: '/Users/mahdi/workshop/rust-memory-playground/target/debug/string-program' (arm64)
|
||||
Process 68317 launched: '/Users/workshop/rust-memory-playground/target/debug/string-program' (arm64)
|
||||
Process 68317 stopped
|
||||
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
|
||||
frame #0: 0x000000010000476c string-program`string_program::main::h64ca96ee87b0ceaf at main.rs:3:5
|
||||
|
@ -5,7 +5,6 @@ subtitle: "Videogames as an embodied activity"
|
||||
date: 2022-07-23 00:00:00
|
||||
permalink: embodying-the-avatar-videogames/
|
||||
categories: programming
|
||||
author: Mahdi
|
||||
---
|
||||
|
||||
Videogames are a pervasive part of lives of children and adults alike, with 73%
|
||||
|
@ -5,7 +5,6 @@ subtitle: "Publishing raw version of my blog posts"
|
||||
date: 2022-07-27 00:00:00
|
||||
permalink: raw-permalinks-for-accessibility/
|
||||
categories: programming
|
||||
author: Mahdi
|
||||
---
|
||||
|
||||
I realised that my blog and its content, even though very simple and
|
||||
@ -20,15 +19,15 @@ prepending `/raw` to the URL of my posts.
|
||||
Try it out:
|
||||
|
||||
```
|
||||
curl https://mahdi.blog/raw/raw-permalinks-for-accessibility/
|
||||
curl https://mahdi.blog/raw/embodying-the-avatar-videogames/
|
||||
curl https://mahdi.blog/raw/rust-box-str-vs-string/
|
||||
curl https://theread.me/raw/raw-permalinks-for-accessibility/
|
||||
curl https://theread.me/raw/embodying-the-avatar-videogames/
|
||||
curl https://theread.me/raw/rust-box-str-vs-string/
|
||||
```
|
||||
|
||||
You can get a raw listing of my blog posts at `/raw/`:
|
||||
|
||||
```
|
||||
curl https://mahdi.blog/raw/
|
||||
curl https://theread.me/raw/
|
||||
```
|
||||
|
||||
To make this easily readable on small screens and terminals, I used vim's
|
||||
@ -42,8 +41,8 @@ And to reformat my posts, I went over my text lines (I avoided breaking code
|
||||
sample lines), and used `gq` to reformat each section.
|
||||
|
||||
The source code for my blog is available on my
|
||||
[git](https://git.mahdi.blog/mahdi/mahdi.blog) server, you can find the source
|
||||
[git](https://git.theread.me/thereadme/theread.me) server, you can find the source
|
||||
for [the raw
|
||||
plugin](https://git.mahdi.blog/mahdi/mahdi.blog/src/branch/master/_plugins/raw.rb)
|
||||
plugin](https://git.theread.me/thereadme/theread.me/src/branch/master/_plugins/raw.rb)
|
||||
as well as the [layout
|
||||
file](https://git.mahdi.blog/mahdi/mahdi.blog/src/branch/master/_layouts/raw.html).
|
||||
file](https://git.theread.me/thereadme/theread.me/src/branch/master/_layouts/raw.html).
|
||||
|
@ -5,7 +5,6 @@ subtitle: "A tiny introduction to Rukiga"
|
||||
date: 2022-09-06 00:00:00
|
||||
permalink: rukiga-african-language/
|
||||
categories: travel, language
|
||||
author: Mahdi
|
||||
---
|
||||
|
||||
During my trip to south of Uganda I interacted with people who mainly spoke a
|
||||
@ -150,7 +149,7 @@ as Mushana.
|
||||
| Esati | Shirt |
|
||||
| Eshero | Hippo |
|
||||
| Emporogoma | Lion |
|
||||
| Eizina ryangye Mahdi | My name is Mahdi |
|
||||
| Eizina ryangye ... | My name is ... |
|
||||
|Ndaruga Iran| I’m from Iran|
|
||||
|Nashemererwa kubanimwe| I’m happy to be here|
|
||||
|Webare munonga| Thank you very much|
|
||||
|
@ -5,7 +5,6 @@ subtitle: "Travel journal "
|
||||
date: 2022-09-10 00:00:00
|
||||
permalink: uganda-trip/
|
||||
categories: travel
|
||||
author: Mahdi
|
||||
---
|
||||
|
||||
I and a friend of mine from Iran decided to go to Uganda to explore the country,
|
||||
@ -254,7 +253,7 @@ laughter! The preacher would crack jokes in between his preach and people would
|
||||
chuckle, which I found to be nice. Tutamuzongoza taught me how to introduce
|
||||
myself with these phrases:
|
||||
|
||||
Eizina ryangye Mahdi: My name is Mahdi
|
||||
Eizina ryangye ...: My name is ...
|
||||
|
||||
Ndaruga Iran: I’m from Iran
|
||||
|
||||
|
@ -5,7 +5,6 @@ subtitle: "A Tiny Introduction"
|
||||
date: 2022-09-15 00:00:00
|
||||
permalink: middle-persian-pahlavi-script/
|
||||
categories: language
|
||||
author: Mahdi
|
||||
custom_head: <link rel="stylesheet" href="/css/khusro.css"">
|
||||
---
|
||||
|
||||
|
@ -5,7 +5,6 @@ date: 2022-10-15 00:00:00
|
||||
permalink: parsi-minevisam/
|
||||
lang: fa
|
||||
categories: language
|
||||
author: Mahdi
|
||||
---
|
||||
|
||||
از روی کنجکاوی، بر آن که تا چه اندازه در زبانی که با آن گویش میکنیم، از واژههای عربی، فرانسوی، انگلیسی و یا دیگر زبانها بهره میبریم، در این نوشتار و نوشتارهای آیندهی خود کوشش میکنم تا از واژههای وام گرفته شده از زبانهای دیگر بهره نگیرم.
|
||||
|
@ -5,7 +5,6 @@ date: 2022-10-21 00:00:00
|
||||
permalink: parsi-khodemooni/
|
||||
lang: fa
|
||||
categories: language
|
||||
author: Mahdi
|
||||
---
|
||||
|
||||
|
||||
|
@ -4,7 +4,6 @@ title: "Why I don't recommend Ireland when asked"
|
||||
date: 2022-10-27 00:00:00
|
||||
permalink: i-dont-recommend-ireland/
|
||||
categories: personal, ireland
|
||||
author: Mahdi
|
||||
---
|
||||
|
||||
I have been living in Ireland for the past three years, and when asked about
|
||||
|
@ -6,7 +6,6 @@ date: 2022-11-04 00:00:00
|
||||
permalink: iran-1401-timeline/
|
||||
categories: iran, history
|
||||
published: false
|
||||
author: Mahdi
|
||||
custom_head: <link rel="stylesheet" href="/css/timeline.css"">
|
||||
---
|
||||
|
||||
|
@ -5,7 +5,6 @@ subtitle: "Readings on Iran's nationalisation of oil"
|
||||
date: 2022-11-13 00:00:00
|
||||
permalink: mosaddegh-iran-national-oil/
|
||||
categories: iran, history
|
||||
author: Mahdi
|
||||
---
|
||||
|
||||
I have been reading Mosaddegh's memoirs, specifically the two books _Brief
|
||||
|
@ -6,7 +6,6 @@ date: 2022-11-15 00:00:00
|
||||
permalink: animals-and-machines/
|
||||
categories: personal, science
|
||||
math: true
|
||||
author: Mahdi
|
||||
---
|
||||
|
||||
I find comparing animals and machines absurd, because of course, animals win!
|
||||
|
@ -5,7 +5,6 @@ subtitle: "...میخواهم دم بگیرم"
|
||||
date: 2022-11-27 00:00:00
|
||||
permalink: i-want-to-breathe/
|
||||
categories: personal
|
||||
author: Mahdi
|
||||
excerpt_separator: <!--more-->
|
||||
---
|
||||
|
||||
|
@ -5,7 +5,6 @@ subtitle: "آیا زندگی زیباست؟"
|
||||
date: 2023-01-06 00:00:00
|
||||
permalink: is-life-beautiful/
|
||||
categories: personal
|
||||
author: Mahdi
|
||||
published: false
|
||||
excerpt_separator: <!--more-->
|
||||
---
|
||||
|
@ -5,7 +5,6 @@ date: 2023-02-27 00:00:00
|
||||
permalink: zaban-va-farhang/
|
||||
lang: fa
|
||||
categories: language
|
||||
author: Mahdi
|
||||
---
|
||||
|
||||
هنگامی که از پارسیگویی با دوستانم سخن میگویم، پرسشی که بسیار آورده میشود، «چرا» است. این پرسش بجا است، برای همین در این نوشته میکوشم از دید خودم پاسخی به این پرسش بدهم.
|
||||
|
@ -5,7 +5,6 @@ subtitle: "Stillhet!"
|
||||
date: 2023-06-02 00:00:00
|
||||
permalink: norway/
|
||||
categories: personal
|
||||
author: Mahdi
|
||||
excerpt_separator: <!--more-->
|
||||
published: false
|
||||
---
|
||||
|
Reference in New Issue
Block a user