<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Coding at 50]]></title><description><![CDATA[Join me as I attempt to turn myself into a full-stack developer at the ripe old age of 50+]]></description><link>https://wenchie.com/</link><image><url>https://wenchie.com/favicon.png</url><title>Coding at 50</title><link>https://wenchie.com/</link></image><generator>Ghost 5.88</generator><lastBuildDate>Mon, 11 May 2026 09:51:20 GMT</lastBuildDate><atom:link href="https://wenchie.com/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[TEST]]></title><description><![CDATA[<p>TEST</p>]]></description><link>https://wenchie.com/test/</link><guid isPermaLink="false">67b685a61cdfae1aa7308191</guid><dc:creator><![CDATA[Wenchie]]></dc:creator><pubDate>Thu, 20 Feb 2025 01:31:16 GMT</pubDate><content:encoded><![CDATA[<p>TEST</p>]]></content:encoded></item><item><title><![CDATA[Coming soon]]></title><description><![CDATA[<p>This is Wenchie.com, a brand new site by Wenchie that&apos;s just getting started. Things will be up and running here shortly, but you can <a href="#/portal/">subscribe</a> in the meantime if you&apos;d like to stay up to date and receive emails when new content is published!</p>]]></description><link>https://wenchie.com/coming-soon/</link><guid isPermaLink="false">67b680821cdfae1aa7307fac</guid><category><![CDATA[News]]></category><dc:creator><![CDATA[Wenchie]]></dc:creator><pubDate>Thu, 20 Feb 2025 01:08:18 GMT</pubDate><media:content url="https://static.ghost.org/v4.0.0/images/feature-image.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://static.ghost.org/v4.0.0/images/feature-image.jpg" alt="Coming soon"><p>This is Wenchie.com, a brand new site by Wenchie that&apos;s just getting started. Things will be up and running here shortly, but you can <a href="#/portal/">subscribe</a> in the meantime if you&apos;d like to stay up to date and receive emails when new content is published!</p>]]></content:encoded></item><item><title><![CDATA[Quick fix for scanning letters or documents written on that translucent "onion skin" or thin tissue paper stationery]]></title><description><![CDATA[To prevent bleed-through when you are scanning old documents, just do this]]></description><link>https://wenchie.com/quick-fix-for-scanning-documents-written-on-that-thin-see-through-onion-skin-type-of-paper/</link><guid isPermaLink="false">67b69f071cdfae1aa73081dc</guid><category><![CDATA[Hot Tips]]></category><dc:creator><![CDATA[Wenchie]]></dc:creator><pubDate>Wed, 06 Jun 2018 06:00:55 GMT</pubDate><media:content url="https://wenchie.com/content/images/2018/06/Rose_archives_0224-1.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://wenchie.com/content/images/2018/06/Rose_archives_0224-1.jpg" alt="Quick fix for scanning letters or documents written on that translucent &quot;onion skin&quot; or thin tissue paper stationery"><p>Do you remember &quot;Air Mail,&quot; and those handwritten letters that were written on the thinnest, lightest paper possible, to save on postage costs?</p>
<p>Well, they were hard to read back then, and they are even harder to read with a scanner nowadays. Why? Because the sender usually wrote on both sides of the paper, so the ink on the back side bleeds through to the front.</p>
<p>I was digitizing a lot of old family correspondence and I ran into this problem a lot. Yes, I know some scanners have a setting that lets you ignore &quot;bleed-through,&quot; but mine doesn&apos;t seem to have that feature. So a typical scanned letter looks like this:</p>
<p><img src="https://wenchie.com/content/images/2018/06/Rose_archives_0224.jpg" alt="Quick fix for scanning letters or documents written on that translucent &quot;onion skin&quot; or thin tissue paper stationery" loading="lazy"></p>
<p>Luckily there is a simple solution. All you need to do is find something black, like a sheet of black paper, a book with a black cover, a teflon oven liner, anything. I am using a black rubber mousepad on this example. Lay your document on the scanner then lay the black object on top of it. Ta da! The &quot;front&quot; text will be much easier to read.</p>
<p><img src="https://wenchie.com/content/images/2018/06/Rose_archives_0225.jpg" alt="Quick fix for scanning letters or documents written on that translucent &quot;onion skin&quot; or thin tissue paper stationery" loading="lazy"></p>
<p>Now that I think of it, it would work even better if the backing sheet was the exact same color as the pen ink, but not having any navy blue mousepads laying around, I haven&apos;t tested that theory.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Facebook is so biased in favor of verbal people...]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>Facebook is so biased in favor of &quot;verbal&quot; people. It&apos;s like a boisterous dinner conversation dominated by the comedians and debaters and yellers. Everyone else is left on the sidelines, shyly and anonymously &quot;sharing&quot; rants and raves and memes created by other people.</p>
<p>Maybe</p>]]></description><link>https://wenchie.com/facebook-is-so-biased-in-favor-of-verbal-people/</link><guid isPermaLink="false">67b69f071cdfae1aa73081d9</guid><category><![CDATA[Deep Thoughts]]></category><dc:creator><![CDATA[Wenchie]]></dc:creator><pubDate>Fri, 18 May 2018 13:03:02 GMT</pubDate><media:content url="https://wenchie.com/content/images/2018/05/telephone.png" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://wenchie.com/content/images/2018/05/telephone.png" alt="Facebook is so biased in favor of verbal people..."><p>Facebook is so biased in favor of &quot;verbal&quot; people. It&apos;s like a boisterous dinner conversation dominated by the comedians and debaters and yellers. Everyone else is left on the sidelines, shyly and anonymously &quot;sharing&quot; rants and raves and memes created by other people.</p>
<p>Maybe every verbal person should adopt a non-verbal person and volunteer to be their translator.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Easy JavaScript: change your CSS styles with no functions needed]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p class="drop-cap-round">After you have invested some time in learning HTML and CSS, you might be overwhelmed by the prospect of ALSO learning Javascript. Luckily, there are lots of things you can do with Javascript without actually knowing it. One easy win is manipulating the HTML and CSS you&apos;ve already</p>]]></description><link>https://wenchie.com/js-change-attributes/</link><guid isPermaLink="false">67cb46d71cdfae1aa730823f</guid><category><![CDATA[Javascript]]></category><category><![CDATA[HTML-CSS]]></category><dc:creator><![CDATA[Wenchie]]></dc:creator><pubDate>Tue, 27 Mar 2018 21:38:01 GMT</pubDate><media:content url="https://wenchie.com/content/images/2018/03/javascript-easy-psych.png" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://wenchie.com/content/images/2018/03/javascript-easy-psych.png" alt="Easy JavaScript: change your CSS styles with no functions needed"><p class="drop-cap-round">After you have invested some time in learning HTML and CSS, you might be overwhelmed by the prospect of ALSO learning Javascript. Luckily, there are lots of things you can do with Javascript without actually knowing it. One easy win is manipulating the HTML and CSS you&apos;ve already written. <u>Anything you can do with a style sheet, you can change with Javascript</u>. And you don&apos;t even have to write a function. You can copy and paste a short snippet of code into an anchor or button tag... it&apos;s not the &apos;right&apos; way, but it works. 
</p><p>For example, here is a style called &quot;#crazy&quot; with a font-color of red. Click the words underneath to change its color:</p>
<style>
#crazy {
  color:red;
  font-size:2em;
}
</style>
<pre><code class="language-css">#crazy {
  color:red;
  font-size:2em;
}
</code></pre>
<p id="crazy">Do You Think I&apos;m Psychedelic?</p>
<p><a href="javascript:crazy.setAttribute(&apos;style&apos;,&apos;color:green&apos;);">GREEN</a>&#xA0;&#xA0;&#xA0; <a href="javascript:crazy.setAttribute(&apos;style&apos;,&apos;color:purple;font-style:italic;font-weight:bold;text-shadow:2px 2px orange,-2px -2px lime&apos;);">PURPLE</a>&#xA0;&#xA0;&#xA0; <a href="javascript:crazy.setAttribute(&apos;style&apos;,&apos;color:red;font-weight:bold;font-variant:small-caps;letter-spacing:0.3em;text-align:center;text-shadow:2px 2px 0 orange, 4px 4px 0 yellow, 6px 6px 0 lime, 8px 8px 0 blue, 10px 10px 0 mediumpurple&apos;);">RAINBOW</a></p>
<p>This is what my GREEN anchor looks like:</p>
<pre><code class="language-javascript">&lt;a href=&quot;javascript:crazy.setAttribute(&apos;style&apos;,&apos;color:green&apos;);&quot;&gt;GREEN&lt;/a&gt;
</code></pre>
<p>Without knowing a lick of Javascript, you could modify that snippet to alter any style attribute in your entire document. Just be careful with your quotation marks -- singles and doubles are interchangeable, but still monogamous, so to speak.</p>
<p>Here is the PURPLE script. I&apos;ve added both a positive and a negative text-shadow, which gives it that &quot;Do I need glasses?&quot; effect.</p>
<pre><code class="language-javascript">&lt;a href=&quot;javascript:crazy.setAttribute(&apos;style&apos;,&apos;color:purple; font-style:italic; font-weight:bold; text-shadow:2px 2px orange,-2px -2px lime&apos;);&quot;&gt;PURPLE&lt;/a&gt;
</code></pre>
<p>And here is the RAINBOW text-shadow. It may be tacky, but it illustrates the fact that you can have multiple text-shadows, separated by commas. You have to make each one bigger than the last. They don&apos;t stack like pancakes, they expand like drops of food-coloring.</p>
<pre><code class="language-javascript">&lt;a href=&quot;javascript:crazy.setAttribute(&apos;style&apos;,&apos;color:red;font-weight:bold;font-variant:small-caps;letter-spacing:0.3em;text-align:center;text-shadow:2px 2px 0 orange, 4px 4px 0 yellow, 6px 6px 0 lime, 8px 8px 0 blue, 10px 10px 0 mediumpurple&apos;);&quot;&gt;RAINBOW&lt;/a&gt;
</code></pre>
<p>To be continued:</p>
<p>One of the most common uses of this technique would be to make something visible or invisible...</p>
<p>You can change the styles inside a class or ID, or you can change classes and IDs...</p>
<p>You can change background images...</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[SSH from the point of view of someone who doesn't give a damn]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p class="drop-cap-round">The worst part about managing your own blog is that you will be forced against your will to learn about web security. It&apos;s the biggest wall you will run into, not just technically, but emotionally. Problem after problem will come at you -- connecting to your server, transferring</p>]]></description><link>https://wenchie.com/understanding-ssh-from-the-point-of-view-of-someone-who-doesnt-give-a-damn/</link><guid isPermaLink="false">67cb46d71cdfae1aa730823b</guid><dc:creator><![CDATA[Wenchie]]></dc:creator><pubDate>Mon, 26 Mar 2018 14:45:32 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p class="drop-cap-round">The worst part about managing your own blog is that you will be forced against your will to learn about web security. It&apos;s the biggest wall you will run into, not just technically, but emotionally. Problem after problem will come at you -- connecting to your server, transferring stuff to your server, figuring out what a &quot;certificate&quot; is, trying to stop people from sneaking through your &quot;ports,&quot; being forced to create different &quot;users&quot; even though they are all just you, having your emails rejected as spam, the list goes on and on. </p>
<p>I vividly remember the existential despair I felt when I logged into my first &quot;droplet&quot; server, maybe a week after I had created it.</p>
<link href="//fonts.googleapis.com/css?family=Anonymous+Pro&amp;subset=latin" rel="stylesheet" type="text/css">
<div class="side-note-dotty" style="font-family: &apos;Anonymous Pro&apos;, sans-serif;">
<p>login as: root</p>
<p>Authenticating...</p>
<p>Welcome to Ubuntu... 60 packages can be updated.</p>
<p>There were 672,974 failed login attempts since the last successful login.</p>
</div>
<p>Wait, what?</p>
<p>Is that the Russians?</p>
<p>How do they even know that I exist? What will they do if they break in? Would I even know if they did? Why do they care about my collection of Zone diet recipes and scanned photos of my Dad&apos;s relatives? Am I supposed to DO something, or do I just calmly proceed with building my blog while surrounded by hordes of bots all trying to guess my password?</p>
<p>If you google &quot;Ubuntu failed login attempts&quot; you will find tons of advice, but it is hard to wrap your brain around, or to know what exactly you need to DO.</p>
<style>
.triangle-isosceles {margin: 1em 3em 0 3em;}
.triangle-isosceles.right {text-align:right;}   
    
.override-bootstrap {
  -webkit-box-sizing: content-box;
     -moz-box-sizing: content-box;
          box-sizing: content-box;
}
.override-boostrap:before,
.override-boostrap:after {
  -webkit-box-sizing: content-box;
     -moz-box-sizing: content-box;
          box-sizing: content-box;
}
</style>
<div style="margin-left:5em; margin-right:4em">
<p class="triangle-isosceles override-bootstrap left">Disable SSH login for root.</p>
<p class="triangle-isosceles override-bootstrap right">Disable password-based authentication altogether.</p>
<p class="triangle-isosceles override-bootstrap left">Passwords with high entropy are unlikely to be brute-forced.</p>
<p class="triangle-isosceles override-bootstrap right">Run the SSH server on another port than 22.</p>
<p class="triangle-isosceles override-bootstrap left">Bad idea for usability</p>
<p class="triangle-isosceles override-bootstrap right">Use port knocking, so the SSH port is only visible to hosts from which sequence of knocks is received.</p>
<p class="triangle-isosceles override-bootstrap left">But you need a dedicated client program to send the knock sequence. </p>
<p class="triangle-isosceles override-bootstrap right">And what if you&apos;re on some network which blocks outgoing traffic to some of the port numbers which you&apos;ve chosen in your port knock sequence?</p>
<p class="triangle-isosceles override-bootstrap left">Security through obscurity is the wrong approach. </p>
<p class="triangle-isosceles override-bootstrap right">The argument that port knocking is security by obscurity is sheer twaddle because it implies that port knocking is security in the first place, which it isn&apos;t.</p>
<p class="triangle-isosceles override-bootstrap left">Moving SSH to a random TCP port adds roughly 16 bits worth of entropy.</p>
<p class="triangle-isosceles override-bootstrap right">No, to get 16 bits of extra entropy, you would need to run 65,000 fake SSH servers alongside it.</p>
<p class="triangle-isosceles override-bootstrap left">And that&apos;s still security through obscurity.</p> 
<p class="triangle-isosceles override-bootstrap right">Be sure to block all IPs that have been reported to the blacklist in the last 48 hours, and report all attackers to the blacklist.</p>
<p class="triangle-isosceles override-bootstrap left">Require a keyfile to log in.</p>
<p class="triangle-isosceles override-bootstrap right">Allow SSH connections only from a whitelist (beware not to lock yourself out!).</p>
<p class="triangle-isosceles override-bootstrap left">Use Fail2ban to to keep the wolves at bay.</p>
<p class="triangle-isosceles override-bootstrap right">Use web knocking, a tiny application under a URL that only you know, such that if you browse to that URL and put some correct value into a form and submit it, it will open up the port.</p>
<p class="triangle-isosceles override-bootstrap left">For God&apos;s sake, I just wanted to build a website to share my breakfast noodle recipes.</p>
</div>
<p>You try to follow along, you really do. You think: &quot;OK, maybe I should look into getting a web knocker.&quot;</p>
<p>Meanwhile, your friends say, &quot;See? That&apos;s why I use Wordpress.&quot; Or: &quot;It only took me an hour to set up my website on Wix.&quot; Or: &quot;Why you don&apos;t just use Facebook to share your stuff?&quot;</p>
<p>And you try to remember WHY you wanted your own blog space that was totally under your control.</p>
<p>We&apos;ll talk about that some other time. For now, let&apos;s talk about SSH.</p>
<p>There are two key ideas buried in the conversation that I illustrated above, with help from this guy who put together a handy page of <a href="http://nicolasgallagher.com/pure-css-speech-bubbles/?ref=wenchie.com">pure CSS speech bubbles</a> for the rest of us to use and enjoy.</p>
<div class="oval-thought override-bootstrap">Actually, can I pause to say something about that?</div>
<p>(What I just typed to create that thought bubble:)</p>
<pre><code class="language-html">&lt;div class=&quot;oval-thought&quot;&gt;Actually, can I pause to say something about that?&lt;/div&gt;
</code></pre>
<p>That guy, Nicolas Gallagher, wrote that post in March of 2010. It is now March of 2018, eight years later. Yet it is still there, receiving visitors. Any time you search for the term, &quot;css speech bubble,&quot; (and who doesn&apos;t occassionally need a good speech bubble?) it will be at the top of the list. The page has no ads. Mr. Gallagher just decided to gift us with a beautifully organized presentation of different, easy ways to make speech bubbles.</p>
<p>It&apos;s a good example of what I&apos;m talking about when I complain that a huge portion of our population has become trapped in a web-world where the only things they see are the NEW the things that were published TODAY, the LATEST technology, etc. The real internet is a place where time stands still.</p>
<p>It also makes me laugh because of the way our old-fashioned vocabulary has been twisted to suit the current environment. Guys like Nicholas are referred to as &quot;software engineers.&quot; To people in my generation, &quot;engineer&quot; implies a guy a who designs jet engines or skyscrapers. To people in the next-oldest generation, it connotes a guy who drives a train. But today&apos;s &quot;engineer&quot; being a guy who designs speech bubbles and works out the calculations for having triangles point in various ways, or quotes floating in space nearby.</p>
<blockquote class="oval-quotes">
        <p>No, Donny, these men are nihilists, there&#x2019;s nothing to be afraid of.</p>
      </blockquote>
      <p>Walter Sobchak</p>
<p>keyfile<br>
disable</p>
<p>References:<br>
<a href="https://www.digitalocean.com/community/questions/login-attempts-is-this-usual?ref=wenchie.com">https://www.digitalocean.com/community/questions/login-attempts-is-this-usual</a><br>
<a href="https://security.stackexchange.com/questions/21027/invalid-users-trying-to-log-in-to-my-server?ref=wenchie.com">https://security.stackexchange.com/questions/21027/invalid-users-trying-to-log-in-to-my-server</a><br>
<a href="https://www.digitalocean.com/community/tutorials/how-to-monitor-system-authentication-logs-on-ubuntu?ref=wenchie.com">https://www.digitalocean.com/community/tutorials/how-to-monitor-system-authentication-logs-on-ubuntu</a><br>
<a href="https://unix.stackexchange.com/questions/16559/is-it-safe-to-give-out-my-ssh-public-key-for-work?ref=wenchie.com">https://unix.stackexchange.com/questions/16559/is-it-safe-to-give-out-my-ssh-public-key-for-work</a><br>
<a href="https://www.google.com/search?q=SSH+ON+MAC&amp;rlz=1C1CHBF_enUS694US694&amp;oq=SSH+ON+MAC&amp;aqs=chrome..69i57j0l5.1850j1j7&amp;sourceid=chrome&amp;ie=UTF-8&amp;ref=wenchie.com">https://www.google.com/search?q=SSH+ON+MAC&amp;rlz=1C1CHBF_enUS694US694&amp;oq=SSH+ON+MAC&amp;aqs=chrome..69i57j0l5.1850j1j7&amp;sourceid=chrome&amp;ie=UTF-8</a></p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[DAT.GUI: so useful...so ugly. I extracted the CSS and prettied it up]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>This exercise is a good demonstration of an important principle: you don&apos;t have to have any idea what you are doing to get in there and change things up.</p>
<p>When I was making my Manifesto page, I kept being annoyed by a mysterious, ugly control panel that clung</p>]]></description><link>https://wenchie.com/dat-gui-is-really-useful-but-ugly-as-heck-lets-pretty-it-up/</link><guid isPermaLink="false">67cb46d71cdfae1aa730823a</guid><category><![CDATA[Javascript]]></category><category><![CDATA[HTML-CSS]]></category><dc:creator><![CDATA[Wenchie]]></dc:creator><pubDate>Mon, 26 Mar 2018 13:26:00 GMT</pubDate><media:content url="https://wenchie.com/content/images/2018/03/gui-finalpurple_onlycontroller.png" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://wenchie.com/content/images/2018/03/gui-finalpurple_onlycontroller.png" alt="DAT.GUI: so useful...so ugly. I extracted the CSS and prettied it up"><p>This exercise is a good demonstration of an important principle: you don&apos;t have to have any idea what you are doing to get in there and change things up.</p>
<p>When I was making my Manifesto page, I kept being annoyed by a mysterious, ugly control panel that clung to the top-right of my screen and wouldn&apos;t be moved.</p>
<p>Finally, I noticed that it had a &quot;Close&quot; button, so I was able to make it retreat, but then it lurked like a cockroach at the corner of my page, and reappeared full-size every time I refreshed.</p>
<p><img src="https://wenchie.com/content/images/2018/03/gui-original-both-h300.png" alt="DAT.GUI: so useful...so ugly. I extracted the CSS and prettied it up" loading="lazy"></p>
<p>Gradually, I came to understand what that little box was doing, and I saw the light. How awesome is that? A little panel that lets you change the original programmer&apos;s variables on the fly!</p>
<p>For example, here is my Columbine flag with low gravity, then high gravity.</p>
<p><img src="https://wenchie.com/content/images/2018/03/gui-highgravity-h400.png" alt="DAT.GUI: so useful...so ugly. I extracted the CSS and prettied it up" loading="lazy"></p>
<p>Fun! If you haven&apos;t tried it yet, you should go to my Manifesto page and remove the pins that are holding up my flag. Isn&apos;t that a cool effect?</p>
<p>Until yesterday, I thought dat-GUI was part of Pixi.js. (GUI stands for &apos;graphical user interface,&apos; by the way.). It wouldn&apos;t be worth &quot;fixing up&quot; if I was never going to use it again. But now that I understand that it&apos;s something you can use with <em>any</em> javascript program, it seems like an incredibly useful tool to have around, if only to impress readers with my programmer-like skills.</p>
<p>See, I can&apos;t take any credit for programming my &quot;dangling cloth photo flag&quot; myself (check out <a href="https://codepen.io/dissimulate/pen/KrAwx?ref=wenchie.com">this guy</a> and <a href="https://codepen.io/shshaw/pen/JbrQrW?ref=wenchie.com">this guy</a>), but it would still be impressive if I could say, &quot;Hey, readers, while you&apos;re here, check out my mouseInfluence slider!&quot;</p>
<p>But egads, it really is so ugly. It totally doesn&apos;t match my purply-bluey color scheme. And it doesn&apos;t have any place to put instructions for the user. It&apos;s really just a basic HTML input form, though, isn&apos;t it? Buttons and checkboxes and drop-downs, those can&apos;t be too hard to stylize, right?</p>
<p>The tricky part is that the interface is created on the fly by a javascript program that loads when the page loads. So you can&apos;t just look at the raw HTML and target the various controls with CSS. If you right-click and look at the source code for the page, you won&apos;t see the dat-GUI at all. So how can we target it?</p>
<p>Luckily, I&apos;ve been mucking around a bit, so I have a head-start. I&apos;m going to write this blog post as I am actually doing it, so you can follow along and modify it to suit your own purposes.</p>
<h4 id="whatisdatguibriefly">What is dat-GUI, briefly?</h4>
<p>&quot;dat.GUI is a GUI widget for your demos.&quot;  The best place to see it in action is on this <a href="https://threejs.org/examples/?ref=wenchie.com#webgl_animation_skinning_morph">demo page</a> for threejs. And you can check out awesome examples of 3-D rendering while you&apos;re at it.</p>
<p><img src="https://wenchie.com/content/images/2018/03/gui-threejs.png" alt="DAT.GUI: so useful...so ugly. I extracted the CSS and prettied it up" loading="lazy"></p>
<p>It is a &quot;lightweight controller library for JavaScript,&quot; made by Google, and kept here on <a href="https://github.com/dataarts/dat.gui?ref=wenchie.com">GitHub</a></p>
<p>Here is a nice <a href="https://workshop.chromeexperiments.com/examples/gui/?ref=wenchie.com#1--Basic-Usage">tutorial</a> for using it, and <a href="https://cdnjs.com/libraries/dat-gui?ref=wenchie.com">here</a> is where you can find the latest CDN version.</p>
<p>Here is a sweet little example which shows how you would create a rectangle and then vary all its options with a dat-GUI: <a href="https://codepen.io/webhacck/pen/mVQJVj?ref=wenchie.com">https://codepen.io/webhacck/pen/mVQJVj</a></p>
<p>Here is an intense example showing a huge number of physics properties in one giant model: <a href="http://brm.io/matter-js/demo/?ref=wenchie.com#mixed">http://brm.io/matter-js/demo/#mixed</a></p>
<p>It consists of a single javascript file that you at the bottom of your HTML file in the usual way, such as:</p>
<pre><code class="language-html">&lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.1/dat.gui.min.js&quot;&gt;&lt;/script&gt;
</code></pre>
<p>But if you are going to edit some of the variables I mention below, you will probably want to use the un-minified version, which you can copy from here:</p>
<p><a href="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.1/dat.gui.js?ref=wenchie.com">https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.1/dat.gui.js</a></p>
<p>I am using the most recent version 0.7.1 even though my project originally used 0.6.1 because I haven&apos;t noticed any changes in terms of how my flag controller works.</p>
<p>In addition to the actual dat.gui.js file, you will need the code that actually creates the GUI in the first place. I am working with the code that was written on the Codepen that I am borrowing from. I am assuming you are here because you already have a dat-GUI that you want to pretty up. If not, go borrow some code from the demo place that I mentioned.</p>
<h4 id="thebruteforcemethodofcssextraction">The brute force method of CSS extraction</h4>
<p>Before it occurred to me to look into the actual javascript file, I was planning to simply right-click on the widget and use the &quot;inspector&quot; tools available in Chrome and Firefox to figure out the CSS that we needed. Often, this very effective. Here, for example, you can see that the class name we need to target is &quot;property-name&quot;.</p>
<p><img src="https://wenchie.com/content/images/2018/03/guibruteforce.png" alt="DAT.GUI: so useful...so ugly. I extracted the CSS and prettied it up" loading="lazy"></p>
<p>What I like to do is create a mini stylesheet that targets each class or ID I find in the most extreme way possible.</p>
<pre><code class="language-css">span.property-name{
  background-color: midnightblue;
  color: white;
  text-align: center;
  font-family: serif;
  font-size: 1.1em;
  border-radius: 15px;
  border: 2px purple ridge;
  display: inline-block;
  height: 30px;
  line-height: 2em;
}
</code></pre>
<p>It&apos;s a good exercise for your brain to spew out random CSS properties and colors as quickly as you can. The goal is to make every item completely different from the others, so you can see <em>exactly</em> what each class or ID is targeting. Soon, you will have converted that boring, black GUI utility into an exciting, colorful monstrosity like this:</p>
<p><img src="https://wenchie.com/content/images/2018/03/GUI-UGLYWAY-400.png" alt="DAT.GUI: so useful...so ugly. I extracted the CSS and prettied it up" loading="lazy"></p>
<p>It&apos;s especially important to place brightly colored borders around things, which can reveal surprises, like when certain borders extend beyond the edges of their container.</p>
<p>Once you have figured out what&apos;s what, it&apos;s a simple matter to go back and tone down and up-class the mini CSS sheet you have created.</p>
<p>But sometimes you will get frustrated, because things just don&apos;t respond to your commands. The height of the rows, for example, seem to be impervious to the charms of my CSS.</p>
<h3 id="diggingintothejavascript">Digging into the javascript</h3>
<p>I won&apos;t bore you with the details of my explorations because I want to finish this project before I fall asleep tonight. I will just tell you a few things I have learned so far.</p>
<h4 id="tofreetheguifromitsprisononthetoprightcornerdothis">To free the GUI from its prison on the top right corner, do this</h4>
<p>The first task is to rip that sticky beast off the top right side of your screen. And the way to do that, I have discovered, is to add this line to your code:</p>
<pre><code class="language-javascript">{ autoPlace: false }
</code></pre>
<p>More specifically, you want to add that to the place in your code where you actually create the new GUI, and you want to tell it where to put it instead. So you will see something like this:</p>
<pre><code class="language-javascript">let gui = new dat.GUI();
</code></pre>
<p>and you want to change it to something like this:</p>
<pre><code class="language-javascript">let gui = new dat.GUI({ autoPlace: false });
var GUIContainer = document.getElementById(&apos;my-gui-container&apos;);
GUIContainer.appendChild(gui.domElement);
</code></pre>
<p>Then, in your CSS you would want to position it where you want it to land. To do that, you need to use &quot;absolute&quot; positioning.&lt;a href=&quot;javascript:void(0)&quot; class=&quot;popover-dismiss&quot; data-toggle=&quot;popover&quot; data-trigger=&quot;hover&quot; title=&quot;&quot; data-content=&quot;If position:absolute doesn&apos;t work, remember that your container should be <a href="https://css-tricks.com/absolute-positioning-inside-relative-positioning/?ref=wenchie.com">relative</a>, not static.&quot;&gt;<span class="footnote"></span></p>
<pre><code class="language-css">#my-gui-container {
	position: absolute;
    left: 600px;   /* position inside relatively positioned parent */
	top: 150px;
    z-index: 0;   /* adjust as needed */
}
</code></pre>
<div class="side-note-dotty">Hot tip: If you ONLY want the gui to be LARGER (or smaller), here&apos;s an easy method. In your CSS for my-gui-container, just add the line: <b>transform: scale(1.25);</b> then adjust the number in parentheses to the proportion you want.</div>
<h3 id="changesyoucanmaketothejavascriptitselfifyoudoyoumustkeeptrackofitforever">Changes you can make to the javascript itself... if you do, you must keep track of it forever</h3>
<h4 id="adustthedefaultwidthvariable">Adust the default width variable</h4>
<p>In the JS, you can change the default width for the entire controller by changing this variable:</p>
<p>GUI.DEFAULT_WIDTH = 245;</p>
<h4 id="changethewordingontheopenandclosebuttons">Change the wording on the Open and Close buttons</h4>
<p>While you are there, you can change the text<br>
//GUI.TEXT_CLOSED = &apos;Close Controls&apos;;<br>
GUI.TEXT_CLOSED = &apos;Hide this control box&apos;;<br>
//GUI.TEXT_OPEN = &apos;Open Controls&apos;;<br>
GUI.TEXT_OPEN = &apos;Goof around with my Flag&apos;;</p>
<h4 id="togetridoftheuglyskinnyleftborders">To get rid of the ugly skinny left borders</h4>
<p><img src="https://wenchie.com/content/images/2018/03/gui-uglyleftborder.png" alt="DAT.GUI: so useful...so ugly. I extracted the CSS and prettied it up" loading="lazy"><br>
Comment out or change the colors on these:</p>
<pre><code class="language-css">/*
 * GETTING RID OF THE UGLY LEFT BORDERS

.dg .cr.boolean {
    border-left: 3px solid #806787
}

.dg .cr.color {
    border-left: 3px solid
}

.dg .cr.function {
    border-left: 3px solid #e61d5f
}

.dg .cr.number {
    border-left: 3px solid #2FA1D6
}
*/
</code></pre>
<h4 id="extractthecssfromthejsfile">Extract the CSS from the JS file</h4>
<p>Luckily, we don&apos;t have to &quot;figure out&quot; the CSS that we need, because there is a CSS style sheet buried in the javascript file. Just search for &quot;var styleSheet&quot;</p>
<p><img src="https://wenchie.com/content/images/2018/03/gui-stylesheet.png" alt="DAT.GUI: so useful...so ugly. I extracted the CSS and prettied it up" loading="lazy"></p>
<p>Yep, the entire, 400+ line stylesheet is just a single variable in javascript.</p>
<p>Now let&apos;s look at it!</p>
<h3 id="rawstylesheetextractedfrom071ofdatgui">Raw stylesheet extracted from 0.7.1 of dat.GUI</h3>
<pre><code class="language-css">.dg ul {
    list-style: none;
    margin: 0;
    padding: 0;
    width: 100%;
    clear: both
}

.dg.ac {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    height: 0;
    z-index: 0
}

.dg:not(.ac) .main {
    overflow: hidden
}

.dg.main {
    -webkit-transition: opacity .1s linear;
    -o-transition: opacity .1s linear;
    -moz-transition: opacity .1s linear;
    transition: opacity .1s linear
}

.dg.main.taller-than-window {
    overflow-y: auto
}

.dg.main.taller-than-window .close-button {
    opacity: 1;
    margin-top: -1px;
    border-top: 1px solid #2c2c2c
}

.dg.main ul.closed .close-button {
    opacity: 1 !important
}

.dg.main:hover .close-button,
.dg.main .close-button.drag {
    opacity: 1
}

.dg.main .close-button {
    -webkit-transition: opacity .1s linear;
    -o-transition: opacity .1s linear;
    -moz-transition: opacity .1s linear;
    transition: opacity .1s linear;
    border: 0;
    line-height: 19px;
    height: 20px;
    cursor: pointer;
    text-align: center;
    background-color: #000
}

.dg.main .close-button.close-top {
    position: relative
}

.dg.main .close-button.close-bottom {
    position: absolute
}

.dg.main .close-button:hover {
    background-color: #111
}

.dg.a {
    float: right;
    margin-right: 15px;
    overflow-y: visible
}

.dg.a.has-save&gt;ul.close-top {
    margin-top: 0
}

.dg.a.has-save&gt;ul.close-bottom {
    margin-top: 27px
}

.dg.a.has-save&gt;ul.closed {
    margin-top: 0
}

.dg.a .save-row {
    top: 0;
    z-index: 1002
}

.dg.a .save-row.close-top {
    position: relative
}

.dg.a .save-row.close-bottom {
    position: fixed
}

.dg li {
    -webkit-transition: height .1s ease-out;
    -o-transition: height .1s ease-out;
    -moz-transition: height .1s ease-out;
    transition: height .1s ease-out;
    -webkit-transition: overflow .1s linear;
    -o-transition: overflow .1s linear;
    -moz-transition: overflow .1s linear;
    transition: overflow .1s linear
}

.dg li:not(.folder) {
    cursor: auto;
    height: 27px;
    line-height: 27px;
    padding: 0 4px 0 5px
}

.dg li.folder {
    padding: 0;
    border-left: 4px solid transparent
}

.dg li.title {
    cursor: pointer;
    margin-left: -4px
}

.dg .closed li:not(.title),
.dg .closed ul li,
.dg .closed ul li&gt;* {
    height: 0;
    overflow: hidden;
    border: 0
}

.dg .cr {
    clear: both;
    padding-left: 3px;
    height: 27px;
    overflow: hidden
}

.dg .property-name {
    cursor: default;
    float: left;
    clear: left;
    width: 40%;
    overflow: hidden;
    text-overflow: ellipsis
}

.dg .c {
    float: left;
    width: 60%;
    position: relative
}

.dg .c input[type=text] {
    border: 0;
    margin-top: 4px;
    padding: 3px;
    width: 100%;
    float: right
}

.dg .has-slider input[type=text] {
    width: 30%;
    margin-left: 0
}

.dg .slider {
    float: left;
    width: 66%;
    margin-left: -5px;
    margin-right: 0;
    height: 19px;
    margin-top: 4px
}

.dg .slider-fg {
    height: 100%
}

.dg .c input[type=checkbox] {
    margin-top: 7px
}

.dg .c select {
    margin-top: 5px
}

.dg .cr.function,
.dg .cr.function .property-name,
.dg .cr.function *,
.dg .cr.boolean,
.dg .cr.boolean * {
    cursor: pointer
}

.dg .cr.color {
    overflow: visible
}

.dg .selector {
    display: none;
    position: absolute;
    margin-left: -9px;
    margin-top: 23px;
    z-index: 10
}

.dg .c:hover .selector,
.dg .selector.drag {
    display: block
}

.dg li.save-row {
    padding: 0
}

.dg li.save-row .button {
    display: inline-block;
    padding: 0px 6px
}

.dg.dialogue {
    background-color: #222;
    width: 460px;
    padding: 15px;
    font-size: 13px;
    line-height: 15px
}

#dg-new-constructor {
    padding: 10px;
    color: #222;
    font-family: Monaco, monospace;
    font-size: 10px;
    border: 0;
    resize: none;
    box-shadow: inset 1px 1px 1px #888;
    word-wrap: break-word;
    margin: 12px 0;
    display: block;
    width: 440px;
    overflow-y: scroll;
    height: 100px;
    position: relative
}

#dg-local-explain {
    display: none;
    font-size: 11px;
    line-height: 17px;
    border-radius: 3px;
    background-color: #333;
    padding: 8px;
    margin-top: 10px
}

#dg-local-explain code {
    font-size: 10px
}

#dat-gui-save-locally {
    display: none
}

.dg {
    color: #eee;
    font: 11px &apos;Lucida Grande&apos;, sans-serif;
    text-shadow: 0 -1px 0 #111
}

.dg.main::-webkit-scrollbar {
    width: 5px;
    background: #1a1a1a
}

.dg.main::-webkit-scrollbar-corner {
    height: 0;
    display: none
}

.dg.main::-webkit-scrollbar-thumb {
    border-radius: 5px;
    background: #676767
}

.dg li:not(.folder) {
    background: #1a1a1a;
    border-bottom: 1px solid #2c2c2c
}

.dg li.save-row {
    line-height: 25px;
    background: #dad5cb;
    border: 0
}

.dg li.save-row select {
    margin-left: 5px;
    width: 108px
}

.dg li.save-row .button {
    margin-left: 5px;
    margin-top: 1px;
    border-radius: 2px;
    font-size: 9px;
    line-height: 7px;
    padding: 4px 4px 5px 4px;
    background: #c5bdad;
    color: #fff;
    text-shadow: 0 1px 0 #b0a58f;
    box-shadow: 0 -1px 0 #b0a58f;
    cursor: pointer
}

.dg li.save-row .button.gears {
    background: #c5bdad url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAANCAYAAAB/9ZQ7AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQJJREFUeNpiYKAU/P//PwGIC/ApCABiBSAW+I8AClAcgKxQ4T9hoMAEUrxx2QSGN6+egDX+/vWT4e7N82AMYoPAx/evwWoYoSYbACX2s7KxCxzcsezDh3evFoDEBYTEEqycggWAzA9AuUSQQgeYPa9fPv6/YWm/Acx5IPb7ty/fw+QZblw67vDs8R0YHyQhgObx+yAJkBqmG5dPPDh1aPOGR/eugW0G4vlIoTIfyFcA+QekhhHJhPdQxbiAIguMBTQZrPD7108M6roWYDFQiIAAv6Aow/1bFwXgis+f2LUAynwoIaNcz8XNx3Dl7MEJUDGQpx9gtQ8YCueB+D26OECAAQDadt7e46D42QAAAABJRU5ErkJggg==) 2px 1px no-repeat;
    height: 7px;
    width: 8px
}

.dg li.save-row .button:hover {
    background-color: #bab19e;
    box-shadow: 0 -1px 0 #b0a58f
}

.dg li.folder {
    border-bottom: 0
}

.dg li.title {
    padding-left: 16px;
    background: #000 url(data:image/gif;base64,R0lGODlhBQAFAJEAAP////Pz8////////yH5BAEAAAIALAAAAAAFAAUAAAIIlI+hKgFxoCgAOw==) 6px 10px no-repeat;
    cursor: pointer;
    border-bottom: 1px solid rgba(255, 255, 255, 0.2)
}

.dg .closed li.title {
    background-image: url(data:image/gif;base64,R0lGODlhBQAFAJEAAP////Pz8////////yH5BAEAAAIALAAAAAAFAAUAAAIIlGIWqMCbWAEAOw==)
}

.dg .cr.boolean {
    border-left: 3px solid #806787
}

.dg .cr.color {
    border-left: 3px solid
}

.dg .cr.function {
    border-left: 3px solid #e61d5f
}

.dg .cr.number {
    border-left: 3px solid #2FA1D6
}

.dg .cr.number input[type=text] {
    color: #2FA1D6
}

.dg .cr.string {
    border-left: 3px solid #1ed36f
}

.dg .cr.string input[type=text] {
    color: #1ed36f
}

.dg .cr.function:hover,
.dg .cr.boolean:hover {
    background: #111
}

.dg .c input[type=text] {
    background: #303030;
    outline: none
}

.dg .c input[type=text]:hover {
    background: #3c3c3c
}

.dg .c input[type=text]:focus {
    background: #494949;
    color: #fff
}

.dg .c .slider {
    background: #303030;
    cursor: ew-resize
}

.dg .c .slider-fg {
    background: #2FA1D6;
    max-width: 100%
}

.dg .c .slider:hover {
    background: #3c3c3c
}

.dg .c .slider:hover .slider-fg {
    background: #44abda
}

</code></pre>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[My journey through layering hell with PIXI.js and HTML (hey, that rhymes)]]></title><description><![CDATA[How I made my "dangling cloth with a photo on it" ]]></description><link>https://wenchie.com/my-journey-through-layering-hell-with-pixi-and-html-hey-that-rhymes/</link><guid isPermaLink="false">67cb46d71cdfae1aa7308239</guid><dc:creator><![CDATA[Wenchie]]></dc:creator><pubDate>Fri, 16 Mar 2018 18:34:52 GMT</pubDate><media:content url="https://wenchie.com/content/images/2018/03/pixi-me.png" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://wenchie.com/content/images/2018/03/pixi-me.png" alt="My journey through layering hell with PIXI.js and HTML (hey, that rhymes)"><p><b><u>Subtitle</u>: &quot;How I made that &quot;dangling cloth-like mesh with a photo on it&quot; that you see on my &quot;Manifesto&quot; page&quot;</b></p>
<p>This goes out to anyone who might be struggling to integrate PIXI graphics into a regular web page. I will address two main issues: (1.) getting your graphic into your DOM, and (2.) figuring out the transparency and z-index stuff. I will also include all the code for making my version of the &quot;un-tearable photo cloth.&quot; It&apos;s not anything I can take credit for, please give a shout-at to the original makers.</p>
<p>According to Wikipedia, &quot;Pixi.js is an open source, cross browser JavaScript 2D WebGL graphics library with canvas fallback.&quot;</p>
<p>Translation: &quot;PIXI is a Javascript thing that helps you make online video games that other people can play right in their web browsers.&quot;</p>
<p>Making video games is NOT anything I find even remotely interesting. I&apos;m too busy gnashing my teeth at the memory of all the money I spent on my kids&apos; Gameboy, Gameboy Advanced, Nintendo 64, GameCube, Nintendo DS, Xbox 360, Wii, PS3, and PS4 gaming systems. I had to call one of them to get that list -- it was even longer than I remembered, and doesn&apos;t include the PC games like Winnie the Pooh, PlaySkool, Myst, Barbie Detective, Lemonade Tycoon, and all that. Every game between $30 and $60 a pop, in &apos;aught&apos; dollars.</p>
<p>All right, I&apos;m not interested in games, but I am interested in becoming a programmer, so there I was, admiring this <a href="https://codepen.io/dissimulate/pen/KrAwx?ref=wenchie.com">masterpiece of Javascript programming</a> on Codepen. If you haven&apos;t see it, you should, even though it is five years old. It has over 3 million views. To see the best part, drag over it with your RIGHT mouse button. <a href="javascript:void(0)" class="popover-dismiss" data-toggle="popover" data-trigger="hover" title data-content="He recently made an updated version, but I prefer the original -- more organic."><span class="footnote"></span></a></p>
<figure><a href="https://codepen.io/dissimulate/pen/KrAwx?ref=wenchie.com"><img src="https://wenchie.com/content/images/2018/03/PIXI-ORIGINAL-TEARABLE-CLOTH-1.png" alt="My journey through layering hell with PIXI.js and HTML (hey, that rhymes)"></a><figcaption>The original TEARABLE CLOTH</figcaption></figure>
<p>My main learning method is to copy cool things that other people made and try to get it running myself, so I copied his code and made a little demo on my own web page. But, when I tried to show it to someone, I discovered that it didn&apos;t work on &quot;finger touch&quot; devices, a.k.a. phones. So I went back to Codepen to read the comments, to see if there was a solution, and sure enough, I found <a href="https://codepen.io/shshaw/pen/JbrQrW?ref=wenchie.com">this guy</a>, who had not only adapted it for touch devices, but also figured out how to overlay a photo on the fabric. Try it out yourself.</p>
<figure><a href="https://codepen.io/shshaw/pen/JbrQrW?ref=wenchie.com"><img src="https://wenchie.com/content/images/2018/03/pixi-other-guy.png" alt="My journey through layering hell with PIXI.js and HTML (hey, that rhymes)"></a><figcaption>Put your face on PIXI fabric</figcaption></figure>
<p>Isn&apos;t that coolio? Of course, his version is not &quot;tearable.&quot; The physics of ripping fabric apart is quite challenging, apparently, which is why the original has 3.5M views. But this guy&apos;s addition of a PHOTO made my future-blogger senses tingle. I mean, that&apos;s actually useful. You have to display photos anyway, so why not show them dangling on fabric?</p>
<p>Of course, I assumed I could just copy it, change the photo, and give credit to both brilliant guys, but, as always, nothing is every easy, especially when you have no idea what you are doing.</p>
<h3>From full screen object to wee little node</h3>
<p>PIXI is for game creators, and games usually take over the whole screen. A game is literally a giant canvas, with little objects that go around doing things. All I wanted was the dangling photo mesh, so I could include it in a blog post. To pop it into a div, so to speak.</p>
<p>I took my one Javascript class two years ago, so it took me two full days of trying to understand PIXI and &quot;renderers&quot; and &quot;textures&quot; and &quot;nodes&quot; to get it to work. Basically, I just created a div in my HTML document called &quot;canvas-parent,&quot; and I figured out how to get the renderer to plop the new canvas inside of that div. Here is where my code deviates from the master:</p>
<pre><code class="language-javascript">let canvas = document.createElement(&apos;canvas&apos;);
let ctx = canvas.getContext(&apos;2d&apos;);
var canvasContainer = document.getElementById(&apos;canvas-parent&apos;);
canvasContainer.appendChild(canvas);

...THEN LATER ...

let stage = new PIXI.Container();
let renderer = PIXI.autoDetectRenderer(350, 500, {transparent: true }); 
canvasContainer.insertBefore(renderer.view, canvas);
renderer.render(stage);
</code></pre>
<p>Two things are different:<br>
(1.) instead of appending the canvas to the body element with (<code>document.body.appendChild(canvas);</code>, which puts the fabric-photo at the way bottom of your page, I appended it to my &quot;canvas-parent&quot; div, and<br>
(2.) instead of <code>let renderer = PIXI.autoDetectRenderer(window.innerWidth, window.innerHeight...</code> which creates a full-screen container, I forced it to create a smaller 350x500 rectangle, similar to the size of a typical photo on a blog post.</p>
<p>When that finally worked (oh, the glory!), I thought I was scott free, but it turns out the hardest part was actually managing the CSS.  If you look closely at the demo, you will see that he is actually creating TWO canvases. One seems to have the &quot;mesh&quot; and the other seems to have the photo -- also known as the &quot;texture&quot; -- and one lays on top of the other and they move in perfect synchronicity, like pair skaters.</p>
<p>That leads to two issues: positioning, and transparency.</p>
<p>To precisely position something in CSS, you have to use the dreaded  <code>position:absolute</code>, which means you have to re-learn the lesson you have undoubtedly learned before, but forgotten about:</p>
<div class="side-note-dotty">
REPEAT AFTER ME: YOU CAN&apos;T <b>POSITION:ABSOLUTE</b> <u>ANYTHING</u> UNLESS IT IS <u>INSIDE A PARENT</u> THAT HAS <b>POSITION:RELATIVE</b> SET ON IT!
<p>But let&apos;s say you manage to get them to land in the exact same spot, instead of side-by-side, like mine was doing. That&apos;s the 2-D positioning. But then you also have to worry about the 3-D positioning. We have two canvases, one of top of the other, and I wanted to layer them on top of a sky and a mountain range, so there&apos;s no way around it, we have to learn about z-index.</p>
<p>The basic concept is not too hard. First of all...</p>
<div class="side-note-dotty">REPEAT AFTER ME: <b>Z-INDEX</b> has NO EFFECT unless you apply it to an object that is POSITIONED. By default, HTML positions things in a &quot;static&quot; way, which means &quot;one thing after the other,&quot; and you have to change that to a <i>different</i> positioning method before your browser will care about your z-indexes! </div>
<p>So, you have this thing called a &quot;stacking order&quot; or &quot;stacking context&quot;. You put a LOW z-index on the things that should go in the &quot;back,&quot; and a HIGH z-index on things that should go in the &quot;front.&quot;</p>
<p>I get it!</p>
<p>But did I mention that my PIXI canvases are partially <i>transparent</i>? They have to be, for you to see both the photo and the mesh... and the background.</p>
<p>So what?</p>
<p>Well, it turns out that there is an incestuous relationship between Z-INDEX and OPACITY. It will make your brain hurt (Read <a href="https://philipwalton.com/articles/what-no-one-told-you-about-z-index/?ref=wenchie.com">this article</a> for an explanation, or you can just think about it.</p>
<p>There&apos;s no such thing as a transparent background, because then it wouldn&apos;t be a background, would it? It would be revealing something else that is more backer.</p>
<p>And, if something in the foreground is transparent, that means it reveals things that are in the back. So the things that are &quot;in back&quot; are actually, from the point of view of the browser doing the rendering, &quot;in front.&quot;</p>
<p>And it&apos;s not just visibility that is the problem. Obviously, I don&apos;t want my mountains to appear in front of my PIXI canvas, and I don&apos;t want the backgrounds of my pixi frames to cover up my cool little meshy thing. And I don&apos;t want my mountains to show through my photo. But there&apos;s another issue. I need you, the reader, to be able to &quot;grab&quot; my mesh with your mouse and yank it around. Once I got things layered and looking OK, I discovered that my cloth was no longer grabbable. It needs to be -- technically speaking -- WAY WAY on top. But with two canvases that interact, one on top of the other, which one should be <u>more</u> way-way on top, and how do you get it there?</p>
<p>In other words, can z-index counteract the effect of things being ordered in a particular way in your HTML document? Can you use it to bring &quot;forward&quot; something that &quot;comes after&quot;? And where do transparent things fit into this equation?</p>
<p>Answer: I HAVE NO IDEA. Read the article. I just tried and tried different numbers until suddenly it worked. Then I saved the file as PIXI_v978_ohmygoditactuallyworkedFINALLY.html.</p>
<p>And then you have the question: if I build a scene inside of a ROW, and I am forced to set a low z-index on that row so that the stuff will be &quot;in back,&quot; and so that I can use absolute positioning, can I then override the z-index of the parent for a child that resides in that very row?</p>
<p>In other words, is z-index inherited?</p>
<p>Luckily, the answer is No, it not inherited, and yes, you can override it. So, without further ado, here is how I got this stupid thing working.</p>
<p>Therefore, z-index is not technically inherited, but z-index of ancestors does affect z-position.</p>
<p>19<br>
down vote<br>
z-index only applies to elements that have been given an explicit position.</p>
<p>Those two canvases have to land on your page exactly on top of each other.</p>
<p>Once you get it looking right, you will discover that your mesh is no longer grabbable.</p>
<p>I put everything inside of a Bootstrap ROW (which is just a div) and that ROW has:</p>
<p>style=&quot;height:550px; background-color:#83C4DB;position:relative;&quot;</p>
<p>Inside the row, I created a container</p>
<p>In the container I put my background mountains image which has z-index: -9<br>
There, I abolute:positioned my stuff<br>
A sun which has -8</p>
<p>Left space for more junk</p>
<p>My canvas-parent div has z-index -5</p>
<p>As you can see, we are working our way from back to front. This is where it gets complicate:</p>
<p>On my CSS for the canvas element, I put z-index: -2 !important<br>
It must be LOWER than the next thing<br>
I have NO background... if you do, it covers up the fabric</p>
<p>But then on my canvas+canvas css, I put z-index: -1 !important<br>
and I put a floralwhite background with opacity: 0.8<br>
Opacity of 1 will cover up the photo, but less and the mountains show through<br>
MORE IMPORTANTLY:<br>
a positive z-index puts the background in front of the photo!  So leave it at -1</p>
<p>I have notes to myself:</p>
<p>OK, this is where</p>
<p>IN THE END I COULD NOT POSITION MY DAT-GUI CONTROLLER INSIDE THE ROW WITH THE MOUNTAINS. RATHER I HAD TO PUT OUTSIDE THE ROW AND THEN MANUALLY POSITION IT. I WANTED IT TO WRAP IN SECOND SET OF COLUMSN AND IT WAS BEAUTIFUL EXCEPT THE Z-CONTENT MADE IT UNREACHABLE. DAMMIT!</p>
<p>how does opacity and z-index relate to interactivity with the mouse? i can put the flagpole in front, which is actually a giant transparent png that you think might cover up the photo flag, but it still works if i set its z-index at 5 to bring in front of the flag which has -1.</p>
<p>and setting flag to anything positive at all puts it in BACK of the of the background. ... i think</p>
<p>THE STACKING CONTEXT:<br>
<a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context?ref=wenchie.com">https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context</a></p>
<p>THE ORIGINAL TEARABLE CLOTH WITH 3.5 MILLION VIEWS<br>
<a href="https://codepen.io/dissimulate/pen/KrAwx?ref=wenchie.com">https://codepen.io/dissimulate/pen/KrAwx</a></p>
<p>THE GUY WHO FIGURED OUT TO PUT A PHOTO ON A MESH... BUT NO MORE &apos;TEARABLE&quot;-NESS<br>
<a href="https://codepen.io/shshaw/pen/JbrQrW?ref=wenchie.com">https://codepen.io/shshaw/pen/JbrQrW</a></p>
<p><img src="https://wenchie.com/content/images/2018/03/pixi-needhelp.png" alt="My journey through layering hell with PIXI.js and HTML (hey, that rhymes)" loading="lazy"></p>
<!--kg-card-end: markdown--></div>]]></content:encoded></item><item><title><![CDATA[Is your Windows 10 laptop overheating and the CPU racing full speed? Instant fix.]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>For now apparent reason, my laptop was heating up, and the CPU was racing.</p>
<p>I had two browsers open, but they were not doing anything strenuous. So I googled around and found this:</p>
<p><a href="http://news.softpedia.com/news/windows-10-high-cpu-usage-fix-490908.shtml?ref=wenchie.com">http://news.softpedia.com/news/windows-10-high-cpu-usage-fix-490908.shtml</a></p>
<p>I applied &quot;Method 1&quot; and it worked instantly.</p>]]></description><link>https://wenchie.com/is/</link><guid isPermaLink="false">67cb46d71cdfae1aa7308238</guid><dc:creator><![CDATA[Wenchie]]></dc:creator><pubDate>Thu, 15 Mar 2018 20:40:29 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>For now apparent reason, my laptop was heating up, and the CPU was racing.</p>
<p>I had two browsers open, but they were not doing anything strenuous. So I googled around and found this:</p>
<p><a href="http://news.softpedia.com/news/windows-10-high-cpu-usage-fix-490908.shtml?ref=wenchie.com">http://news.softpedia.com/news/windows-10-high-cpu-usage-fix-490908.shtml</a></p>
<p>I applied &quot;Method 1&quot; and it worked instantly. Like a wild horse that suddenly became tame and friendly.</p>
<p>If you use &quot;Cortana&quot; to set reminders for yourself, you should NOT apply this fix, because that&apos;s what gets affected. I don&apos;t use Cortana for anything, so I&apos;m a happy camper.</p>
<div class="side-note-dotty">
    <h3>How to fix the high CPU usage</h3>
Method 1
<p>Launch the Registry Editor by typing regedit.exe in the Start menu and navigate to the following path:</p>
<p>HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\TimeBroker</p>
<p>Look for the Start entry and change its value from dword:00000003 to dword:00000004 (simply replace the 3 at the end of the value with 4).</p>
<p>Changing this value could have an impact on Cortana&#x2019;s performance, but it appears to be closely connected to the high CPU usage in Windows 10. In case you&#x2019;re not using Cortana at all, you&#x2019;re on the good side. Otherwise, setting reminders won&#x2019;t work.</p>
</div><!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Do you end up every day with a gazillion browser tabs open? Try Session Buddy.]]></title><description><![CDATA[I've tried several browser-tab savers and this is the one I like. ]]></description><link>https://wenchie.com/do-you-end-up-every-day-with-a-gazillion-browser-tabs-open/</link><guid isPermaLink="false">67cb46d71cdfae1aa7308236</guid><category><![CDATA[Hot-Tips-for-Regular-People]]></category><category><![CDATA[Programmer-Tools]]></category><dc:creator><![CDATA[Wenchie]]></dc:creator><pubDate>Mon, 12 Mar 2018 03:35:30 GMT</pubDate><media:content url="https://wenchie.com/content/images/2018/03/sessionbuddylogo-1.png" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://wenchie.com/content/images/2018/03/SessionBuddyLong2.jpg" alt="Do you end up every day with a gazillion browser tabs open? Try Session Buddy.">
<img src="https://wenchie.com/content/images/2018/03/sessionbuddylogo-1.png" alt="Do you end up every day with a gazillion browser tabs open? Try Session Buddy."><p class="drop-cap-round">Whether you&apos;re working, studying, keeping up with the news, or just entertaining yourself, you will inevitably end up with a large number of tabs open on your browser. </p>
<p>For many of us, browser tabs have replaced To Do lists.</p>
<p>We keep tabs open on Google, Amazon, Facebook, Youtube, Craigslist, our bank, and our local government -- to remind ourselves to find the words to that song, send a happy birthday message, buy coffee filters, watch last night&apos;s monologue, look for a better desk, pay a bill, apply for a building permit.</p>
<p>Sometimes our browser windows get so crowded that the tabs become triangular specks, and when we try to click on one, we accidentally close it.</p>
<p><span class="sparkley-teal">POOF!</span> <span style="font-size:smaller">(&lt;== hover over me!)</span> There goes that thing we really wanted to remember.</p>
<p><img src="https://wenchie.com/content/images/2018/03/sessionbuddytiny.png" alt="Do you end up every day with a gazillion browser tabs open? Try Session Buddy." loading="lazy"></p>
<p>Then, at the end of the day, we review all the tabs we still have open and exclaim, &quot;Oh, no, I forgot to buy a battery charger, and I still want to read this essay, and none of these sites have the right guitar chords for that song!&quot;</p>
<p>We don&apos;t want to turn off our laptops -- literally -- because we don&apos;t want to lose our browser tabs.</p>
<p>So instead of shutting them down, we put them to &quot;sleep&quot; -- again, just to preserve our browser tabs. But what does that accomplish? The next day, instead starting fresh, we start out with all that clutter. That&apos;s no good.</p>
<p>&quot;That&apos;s what bookmarks are for,&quot; you say, &quot;to save the sites you want to come back to.&quot; Yes, maybe they work for some people. But, honestly, the last time I tried to use browser bookmarks was a decade ago. They just grow so quickly out of control, and most of mine were labeled &quot;Things to look at,&quot; &quot;Show to kids,&quot; or &quot;Save this.&quot;</p>
<figure>
    <img src="https://wenchie.com/content/images/2018/03/sessionbuddy-bookmarks.png" alt="Do you end up every day with a gazillion browser tabs open? Try Session Buddy.">
    <figcaption>I&apos;m sure there&apos;s good stuff in there</figcaption>
    </figure>
<p>It&apos;s even worse when your browser gets &quot;smart&quot; and decides to sync your ancient bookmarks with your new phone, so you&apos;re suddenly confronted with a page of &quot;Favorites&quot; labeled &quot;To read,&quot; &quot;Xmas &apos;08&quot; and &quot;Mexico trip.&quot;</p>
<p>&quot;Well, what about the browser history? Isn&apos;t everything already saved there, if you really needed to go back and find something?&quot;</p>
<p>We know that browsers keep good records, because that&apos;s how people are always getting busted by the cops and their spouses. But we rarely look at it. Try clicking and holding on your browser&apos;s &quot;Back&quot; button until a menu pops up, then choose &quot;Full History&quot;... yikes, the level of detail is kind of creepy.</p>
<p>The problem with using your browser history&quot; to remember the past is that it&apos;s way too detailed. For example, lately I&apos;ve been working on the design of this very blog, so my History looks like this.</p>
<figure>
    <img src="https://wenchie.com/content/images/2018/03/sessionbuddy-history.png" alt="Do you end up every day with a gazillion browser tabs open? Try Session Buddy.">
    <figcaption>Not-very-useful history</figcaption>
    </figure>
<p>That&apos;s not very helpful.</p>
<p>So, what&apos;s the solution? How do we keep track of where we&apos;ve been without having to create formal bookmarks for all fifty sites we visit every day? And how can we start fresh every morning without losing all the stuff we were interested in yesterday?</p>
<p>There are a lot of solutions out there, and I have tried several. TabCloud and OneTab come to mind. But the one that I am happiest with is a Chrome browser extension called &quot;Session Buddy.&quot; Why? Because it&apos;s clean and simple, it&apos;s free, it doesn&apos;t make you look at ads, and it doesn&apos;t do anything unexpected. If you use Chrome as your primary browser, I highly recommend it.</p>
<figure>
    <a href="https://sessionbuddy.com/?ref=wenchie.com"> <img src="https://wenchie.com/content/images/2018/03/sessionbuddylogo.png" alt="Do you end up every day with a gazillion browser tabs open? Try Session Buddy."></a>
    <figcaption>Session Buddy to the rescue</figcaption>
    </figure>
<h3 id="installingsessionbuddy">Installing Session Buddy</h3>
<p>Just go to their website, <a href="https://sessionbuddy.com/?ref=wenchie.com">SessionBuddy.com</a> and press the &quot;Install&quot; button.</p>
<p>That&apos;s it. Now look at the top right of your browser window, you should see a little blue folder icon. Click on that, and ta-da, your current collection of browser tabs has been saved.</p>
<p>You can now close your browser, turn off your computer, and go on with your life.</p>
<figure>
    <img src="https://wenchie.com/content/images/2018/03/sessionbuddyarrow.png" alt="Do you end up every day with a gazillion browser tabs open? Try Session Buddy.">
    <figcaption>Click the folder to save your tabs</figcaption>
    </figure>
<p>You have the option of naming your collections, or not. I usually don&apos;t, because I know they will be saved in chronological order, and that&apos;s good enough for me. But if most of my tabs are related to a particular topic, I might give it a name, to make it easier to come back to some day.</p>
<figure>
    <img src="https://wenchie.com/content/images/2018/03/SessionBuddyFeature-1.png" alt="Do you end up every day with a gazillion browser tabs open? Try Session Buddy.">
    <figcaption>My web browsing history</figcaption>
    </figure>
<p>Session Buddy saves your links in lists, which you can glance through without having to restore the actual windows. It also saves your most recent session in case the power goes out or your browser crashes. That&apos;s all I want. I don&apos;t want it to close tabs for me or take over my bookmarks or replicate itself to the cloud.</p>
<p>I like going back and reviewing the browser tabs I&apos;ve had open in recent days, to see if there are any interesting little nuggets I had forgotten about.</p>
<p>Hey, there&apos;s one right there.</p>
<figure>
    <img src="https://wenchie.com/content/images/2018/03/sessionbuddymyhistory.png" alt="Do you end up every day with a gazillion browser tabs open? Try Session Buddy.">
    <figcaption>Oh! I was going to copy those</figcaption>
    </figure>
<p>Aren&apos;t these just the coolest glass buttons, from <a href="https://codepen.io/Sextant/pen/gAFoL?ref=wenchie.com">CodePen</a>? The pictures behind the buttons are something you can change on the fly. For example, I went over to <a href="https://unsplash.com/?ref=wenchie.com">UnSplash.com</a> and found a photo of a red rose, and pasted in the URL, and <span class="sparkley-red">POOF!</span> -- the rose showed up inside the red button. So you could make buttons of your kids faces. They also have hover effects. I might use them for something.</p>
<figure>
    <img src="https://wenchie.com/content/images/2018/03/sessionbuddy-glass.jpg" alt="Do you end up every day with a gazillion browser tabs open? Try Session Buddy.">
    <figcaption>CSS glass buttons made by <a href="https://codepen.io/Sextant/pen/gAFoL?ref=wenchie.com">Sextant Shepherd</a></figcaption>
    </figure>
<p>I would not have remembered those glass buttons if Session Buddy hadn&apos;t hung onto them for me.</p>
<p>If I really want to hold onto a site permanently, what I do is paste the URL into <a href="https://evernote.com/?ref=wenchie.com">Evernote</a> and jot down as many descriptive words as I can can think of so it will turn up when I search for it years later, like &quot;24-hour late night restaurants coffee shop insomnia all night wifi.&quot;</p>
<p>I hope you like Session Buddy, and, as always, consider throwing the developers a few bucks if you find it useful. And if you DON&apos;T like it, just click the Chrome &quot;menu&quot; (3 tiny dots) at top right, choose More Tools &gt; Extensions &gt; Session Buddy &gt; Trash Can, and it will go away (<span class="sparkley-rainbow">...poof).</span></p>
<p>Happy browsing!</p>
<img src="https://wenchie.com/content/images/2018/03/SessionBuddyLong2.jpg" alt="Do you end up every day with a gazillion browser tabs open? Try Session Buddy."><!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[A demonstration of simple ways to align images in a Ghost blog post.]]></title><description><![CDATA[Getting an image to center or right-align should not be cause for despair. Just copy and paste my examples, and walk toward the light. ]]></description><link>https://wenchie.com/ghost-align-images/</link><guid isPermaLink="false">67cb46d71cdfae1aa7308235</guid><category><![CDATA[Ghost-Blog-How-To]]></category><dc:creator><![CDATA[Wenchie]]></dc:creator><pubDate>Fri, 09 Mar 2018 04:04:55 GMT</pubDate><media:content url="https://wenchie.com/content/images/2018/03/apple_sm.png" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://wenchie.com/content/images/2018/03/apple_sm.png" alt="A demonstration of simple ways to align images in a Ghost blog post."><p class="drop-cap-round">We can put a man on the moon, but we can&apos;t seem to make it easy to align images in blog posts. Any time we get something that actually works, like the friendly <b>&lt;center&gt;</b> tag, it is snatched away by the purists who say it&apos;s unsemantic. Have they even <i>looked</i> at Bootstrap? Talk about unsemantic. But I digress. </p>
<p>The purpose of this post is to demonstrate various ways you can align your images in a Ghost blog post. Nothing fancy, just good, old-fashioned HTML, which Ghost handles beautifully. Personally, I don&apos;t see any good reason to learn the &quot;markdown&quot; way when HTML works fine, and will be around much longer than markdown.</p>
<p>When using the HTML &lt;img&gt; tag, what I do is upload my photo by clicking the little icon with the moutains and moon at the bottom of the Ghost editor. (See my first example, it was created that way.) That gives you a basic image aligned to the left, which nobody likes. So I copy the URL out of the markdown into an &lt;img&gt; tag, and delete the detritus. Not sayin&apos; it&apos;s right, that&apos;s just how I do it.</p>
<p>So without further ado, here are some simple ways to align your images. I will put the code first, in the same order as the apples... you can figure it out.</p>
<hr>
<pre><code class="language-html">![apple-1](/content/images/2018/03/apple.png)

&lt;img src=&quot;/content/images/2018/03/apple.png&quot; alt=&quot;apple&quot; style=&quot;float:right; margin:1em;&quot;&gt;

&lt;img src=&quot;/content/images/2018/03/apple.png&quot; alt=&quot;apple&quot; style=&quot;float:left; margin:1em;&quot;&gt;

&lt;div style=&quot;text-align:center&quot;&gt;&lt;img src=&quot;/content/images/2018/03/apple.png&quot; alt=&quot;apple&quot; style=&quot;margin-bottom:1em; border:4px dotted lime&quot;&gt;&lt;/div&gt;

&lt;figure style=&quot;text-align:center; margin:1em&quot;&gt;&lt;img src=&quot;/content/images/2018/03/apple.png&quot; alt=&quot;apple&quot;&gt;&lt;figcaption style=&quot;font-size:smaller;&quot;&gt;I am an apple with a caption!&lt;/figcaption&gt;&lt;/figure&gt;

&lt;img src=&quot;/content/images/2018/03/apple.png&quot; alt=&quot;apple&quot; style=&quot;position:absolute&quot;&gt;

&lt;img src=&quot;/content/images/2018/03/apple.png&quot; alt=&quot;apple&quot; width=&quot;20%&quot; style=&quot;float:right&quot;&gt;&lt;img src=&quot;/content/images/2018/03/apple.png&quot; alt=&quot;apple&quot; width=&quot;15%&quot; style=&quot;float:right&quot;&gt;&lt;img src=&quot;/content/images/2018/03/apple.png&quot; alt=&quot;apple&quot; width=&quot;10%&quot; style=&quot;float:right&quot;&gt;&lt;img src=&quot;/content/images/2018/03/apple.png&quot; alt=&quot;apple&quot; width=&quot;5%&quot; style=&quot;float:right&quot;&gt;
</code></pre>
<hr>
<p>Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla.</p>
<p><img src="https://wenchie.com/content/images/2018/03/apple.png" alt="A demonstration of simple ways to align images in a Ghost blog post." loading="lazy"></p>
<p>Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla.</p>
<p>Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla.</p>
<img src="https://wenchie.com/content/images/2018/03/apple.png" alt="A demonstration of simple ways to align images in a Ghost blog post." style="float:right; margin:1em;">  
<p>Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla.</p>
<p>Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla.</p>
<p>Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla.</p>
<img src="https://wenchie.com/content/images/2018/03/apple.png" alt="A demonstration of simple ways to align images in a Ghost blog post." style="float:left; margin:1em;"> 
<p>Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla.</p>
<p>Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla.</p>
<p>Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla.</p>
<div style="text-align:center"><img src="https://wenchie.com/content/images/2018/03/apple.png" alt="A demonstration of simple ways to align images in a Ghost blog post." style="margin-bottom:1em; border:4px dotted lime"></div>
Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. 
<figure style="text-align:center; margin:1em"><img src="https://wenchie.com/content/images/2018/03/apple.png" alt="A demonstration of simple ways to align images in a Ghost blog post."><figcaption style="font-size:smaller;">I am an apple with a caption!</figcaption></figure>
<p>Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla.</p>
<p>Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla.<br>
<img src="https://wenchie.com/content/images/2018/03/apple.png" alt="A demonstration of simple ways to align images in a Ghost blog post." style="position:absolute"><br>
Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla.</p>
<p>Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla.</p>
<p>Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla.</p>
<p>Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla.<br>
<img src="https://wenchie.com/content/images/2018/03/apple.png" alt="A demonstration of simple ways to align images in a Ghost blog post." width="20%" style="float:right"><img src="https://wenchie.com/content/images/2018/03/apple.png" alt="A demonstration of simple ways to align images in a Ghost blog post." width="15%" style="float:right"><img src="https://wenchie.com/content/images/2018/03/apple.png" alt="A demonstration of simple ways to align images in a Ghost blog post." width="10%" style="float:right"><img src="https://wenchie.com/content/images/2018/03/apple.png" alt="A demonstration of simple ways to align images in a Ghost blog post." width="5%" style="float:right"><br>
Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla.</p>
<p>Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla. Bla bla bla bla bla bla.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Play my Ruby blackjack game, and learn how to use PRISM.JS  to pretty up your code]]></title><description><![CDATA[Attempting to show off  my blackjack game forced me to learn about syntax highlighting in blog posts.]]></description><link>https://wenchie.com/blackjack-and-prism/</link><guid isPermaLink="false">67cb46d71cdfae1aa730821e</guid><category><![CDATA[Ruby]]></category><category><![CDATA[Ghost-Blog-How-To]]></category><dc:creator><![CDATA[Wenchie]]></dc:creator><pubDate>Tue, 06 Mar 2018 21:49:00 GMT</pubDate><media:content url="https://wenchie.com/content/images/2018/03/blackjack_feature.png" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><figure><a href="https://marydean-blackjack.herokuapp.com/?ref=wenchie.com" target="_blank"><img src="https://wenchie.com/content/images/2018/03/blackjack300.png" alt="Play my Ruby blackjack game, and learn how to use PRISM.JS  to pretty up your code"></a><figcaption>The author at the casino</figcaption></figure>
<img src="https://wenchie.com/content/images/2018/03/blackjack_feature.png" alt="Play my Ruby blackjack game, and learn how to use PRISM.JS  to pretty up your code"><p class="drop-cap-round">My first programming class was in the Ruby language. At the time, Ruby on Rails was a big player in the &quot;full stack&quot; world, so it seemed like a good idea to climb onto that bandwagon. The class was challenging and fun. If anyone reading this is interested in learning Ruby, you can get a huge head start just by reading the free introductory materials and doing the pre-class exercises at <a href="https://launchschool.com/?ref=wenchie.com">Launchschool.com</a>.</p>
<p>Anyway, one of my assignments was to write a blackjack game. That was four years ago, mind you, so I was recently surprised to discover that my game is still up and running on trusty <a href="https://marydean-blackjack.herokuapp.com/?ref=wenchie.com" target="_blank">Heroku.com</a>, so you can play it, too. Woo hoo! There might be a five second delay before the page loads -- that&apos;s how Horoku punishes you for not being a paying customer, which is fair enough.</p>
<div style="text-align:center">
<a href="https://marydean-blackjack.herokuapp.com/?ref=wenchie.com" target="_blank">https://marydean-blackjack.herokuapp.com</a>
</div><br>
<p>It&apos;s strange to find something that you made and forgot about still &quot;living&quot; perpetually on the web, like reading a diary entry about events you don&apos;t remember, or coming across a video of yourself speaking a language you don&apos;t speak anymore.</p>
<p>I love my tiny playing cards, how they look grimy and bent. No, I didn&apos;t make them. I found them on this useful website that everyone should know, <a href="https://openclipart.org/?ref=wenchie.com">openclipart.org</a>. It has free, public-domain clip-art that you can help yourself to, and use for any purpose. And it&apos;s well organized, too.</p>
<figure>
<a href="https://marydean-blackjack.herokuapp.com/?ref=wenchie.com" target="_blank"><img src="https://wenchie.com/content/images/2018/03/blackjackgame350_border.png" alt="Play my Ruby blackjack game, and learn how to use PRISM.JS  to pretty up your code" style="margin-top:1em"></a> 
<figcaption>Get free clip-art from <a href="https://openclipart.org/?ref=wenchie.com">openclipart.org</a></figcaption>
</figure>
<p>After I found those cards, I was faced with the prospect of downloading 52 of them to make my deck, and that motivated me to write a &quot;web scraper&quot;. That&apos;s a program that searches the web for something and downloads it for you. It was the first time I felt like a real programmer, because -- surprise! -- it actually worked. That&apos;s a pretty rare occurrence when you&apos;re a beginner.</p>
<p>My scraper looked like this:</p>
<div class="codeblock"><pre>
require &apos;mechanize&apos;
a = Mechanize.new
a.agent.http.verify_mode = OpenSSL::SSL::VERIFY_NONE
page = a.get &apos;https://openclipart.org/search/?query=ornamental+deck&amp;page=2&apos;
url_list = []
page.links_with(:href =&gt; /ornamental-deck/).each do |link|
  url_list &lt;&lt; link.href
end
url_list.uniq!
url_list.each do |card_url|
  card_page = a.get card_url
  card_page.links_with(:href =&gt; /\/2400px\//).each do |link|
    fullname = link.href.split(&apos;/&apos;).last
    shortname = fullname.sub(&apos;nicubunu_Ornamental_deck_&apos;,&apos;&apos;).downcase
    download_page = link.click
    a.get(download_page.uri).save_as &quot;images/cards2/#{shortname}&quot;
  end
end
</pre></div>
<p>LOL, when I first pasted that code into this post, it made a big mess. You see, this blog uses a Markdown editor, and Markdown uses &quot;#&quot; pound signs for formatting your headings, while the Ruby language uses &quot;#&quot; pound signs for making comments.</p>
<p>&quot;No problem,&quot; I thought, &quot;I will simply wrap my code in <code>&lt;code&gt;</code> and <code>&lt;pre&gt;</code> tags. That should neutralize those pesky octothorpes.&quot; It <em>looked</em> like it was working, so I continued on my way. But later, when I &quot;published,&quot; eek, it looked like this:</p>
<figure>
<img src="https://wenchie.com/content/images/2018/03/rubycodegonebad-1.png" alt="Play my Ruby blackjack game, and learn how to use PRISM.JS  to pretty up your code"> 
<figcaption>Ruby code gone bad</figcaption>
</figure>
<p>Sigh.</p>
<p>Why is nothing ever easy?</p>
<p>Just escape it, you say.</p>
<h3 id="abriefdetourintotheworldofhtmlcharacterentities">A brief detour into the world of HTML character entities.</h3>
<p>I am writing this blog for self-taught coders who often have giant gaps in their education, like me, so feel free to skip this section if you know what I am talking about.</p>
<p>HTML character entities (a.k.a. &quot;named character references,&quot; &quot;reserved symbols,&quot; &quot;numeric character references,&quot; &quot;escape codes&quot;) are special codes that <u>you</u> can use to represent certain symbols that might have different meanings in different contexts.</p>
<p>The most common example is the less-than and greater-than signs that HTML uses for everything. For example, take a look at this boring sentence.</p>
<p><b><larger>The &lt;center&gt; tag, along with &lt;menu&gt; and &lt;strike&gt; have been deprecated.</larger></b></p>
<p>What I actually typed was:</p>
<p><img src="https://wenchie.com/content/images/2018/03/char_entities2.png" alt="Play my Ruby blackjack game, and learn how to use PRISM.JS  to pretty up your code" loading="lazy"></p>
<p>I had to type it that way, because otherwise, it would have looked like this:</p>
<figure>
<img src="https://wenchie.com/content/images/2018/03/char_entities.png" alt="Play my Ruby blackjack game, and learn how to use PRISM.JS  to pretty up your code">
<figcaption>This is what happens when you don&apos;t escape!</figcaption>
</figure>
<p>Do you see why? The browser interprets HTML code as HTML code! Who knew?</p>
<p>The escape from this problem (ha ha) is to &quot;escape&quot; your symbols, which means replace them with secret-code equivalents that tell the browser, &quot;make it LOOK like a less-than sign but don&apos;t INTERPRET it as a less-than sign.&quot;</p>
<div class="side-note-dotty">
Side note: <a href="https://www.freeformatter.com/html-escape.html">FreeFormatter.com</a> is a handy website for escaping things. 
</div>
<p>So, I thought, I simply need to look up the escape code for the &quot;#&quot; sign, and replace all my #&apos;s with that code. And I was right about that. But I was wrong about something else. I&apos;d never really deep-dived into escape codes before, so I had a vague and incorrect, suspicion that replacing &quot;#&quot; with <code>&quot;&amp;#35;&quot;</code> in my code would mean that anyone who copied my code would end up with a bunch of <code>&quot;&amp;#35;&quot;</code>&apos;s instead of &quot;#&quot; signs in their code.</p>
<p>Nobody would want my scraper code, of course, but someone who happens to be taking a Ruby course and has to write a blackjack game <i>might</i> actually want to copy my blackjack code (for learning purposes, of course). And how ugly would my comments look if they were all preceded with <code>&quot;&amp;#35;&quot;</code> ?</p>
<p>Like I said, I was WRONG about that suspicion. If you copy text that contains character codes from a browser, you actually get the real thing, not the code! Kind of weird, huh?</p>
<p>But I didn&apos;t go in that direction because, in addition to the above misconception, I was thrown off by <a href="https://stackoverflow.com/questions/3025171/whats-the-html-character-entity-for-the-sign?ref=wenchie.com">this post</a> on Stack Overflow, in which three people responded to someone with my exact question by saying, basically, &quot;don&apos;t be stupid, the pound sign <u>isn&apos;t</u> a reserved character in HTML so there is <u>no</u> character entity for it.&quot;</p>
<p>Now, if I had kept reading, I would have seen a later comment that said, &quot;Markdown interprets # as &apos;headers&apos;, so you need to use <code>&amp;#35;1</code>&quot; and that would have saved my day, but I didn&apos;t.</p>
<p>Instead, I thought, &quot;@#*&amp;%! I CAN&apos;T BE THE ONLY PERSON IN THE HISTORY OF THE WORLD WHO WANTS TO PASTE SOME RUBY CODE INTO A MARKDOWN EDITOR! WHERE DO I GET A SYNTAX HIGHLIGHTER AND HOW DO I INSTALL IT?&quot;</p>
<h3 id="whichleadsustothetopicathandsyntaxhighlighting">Which leads us to the topic at hand: SYNTAX HIGHLIGHTING</h3>
<p>How DO other people get their code to look so nice?</p>
<p>If you start out programming in Notepad, as many of us do, there will come a moment when your mind is blown by a professional code editor, like <a href="https://www.sublimetext.com/?ref=wenchie.com">Sublime Text</a>, for example.</p>
<figure>
<img src="https://wenchie.com/content/images/2018/03/beercode-1.png" alt="Play my Ruby blackjack game, and learn how to use PRISM.JS  to pretty up your code"> 
    <figcaption><a href="https://www.sublimetext.com/?ref=wenchie.com">Sublime text</a>, a sublime code editor<a href="javascript:void(0)" class="popover-dismiss" data-toggle="popover" data-trigger="hover" title="Hot tip" data-content="Sublime Text costs $80 but you can use it for free. It will pop up a &apos;BUY ME!&apos; after every-so-many saves, but it doesn&apos;t do anything else nefarious. Obviously, you should buy it if you&apos;re going to use it regularly."><span class="footnote"></span></a></figcaption>
</figure>
<p>Wowzer! Look how everything is neatly lined up and color coded. Even a person who knows nothing about computers could change the words to that song, or make the beer disappear by twos or by fives. Seriously, how does it know what&apos;s a variable and what&apos;s a function and what&apos;s an operator?</p>
<p>Notepad++ is another great code editor, and it&apos;s free. Notice the lines connecting each opening <code>&lt;div&gt;</code> to its closing <code>&lt;/div&gt;</code>. When I click on one element, it highlights its partner, even if it is on the other side of the room. How sweet is that?</p>
<figure>
    <img src="https://wenchie.com/content/images/2018/03/notepad---1.png" alt="Play my Ruby blackjack game, and learn how to use PRISM.JS  to pretty up your code"><figcaption>&quot;I&apos;m a closing div, and I belong to YOU!&quot;</figcaption>
</figure>
<p>Alas, when you copy that colorful code out of your editor and paste it into a web page, or into Microsoft Word, or wherever you keep your notes, it looks terrible, or at least boring, like my &quot;scraper&quot; code did above.</p>
<div class="side-note-dotty">
    <b>NOTE</b>: One <u>free cure for ugly notes</u> in MS Word or wherever is a Notepad++ plugin called &quot;NppExport.&quot; It gives you a &quot;Copy to RTF&quot; menu option which preserves all your pretty colors for pasting into other programs.
</div>
<p>The solution, for web pages, is to use a syntax highlighter. And after snooping around a bit, it looks like <a href="https://prismjs.com/?ref=wenchie.com">Prism.js</a> is the code formatter du jour. Why? Because it&apos;s easy, free, and it speaks dozens of <a href="http://prismjs.com/index.html?ref=wenchie.com#languages-list">languages</a>. Oh, and you can link to it via a CDN so you don&apos;t even have to download it.</p>
<h3 id="howtouseprismjs">How to use Prism.js</h3>
<p>OK, we want the pretty colors, so how do we get them into our blog pages?</p>
<p>Here&apos;s the easiest way:</p>
<p>Paste this line of code BEFORE the ugly code you want to format. It will load the Prism.js CSS style sheet.</p>
<pre><code class="language-html">
&lt;link href=&quot;https://cdnjs.cloudflare.com/ajax/libs/prism/1.12.2/themes/prism.min.css&quot; rel=&quot;stylesheet&quot; /&gt;  
</code></pre>
<p>Then, paste this line AFTER the ugly code you want to format. It will load the actual Prism.js javascript file:</p>
<pre><code class="language-html">
 &lt;script type=&quot;text/javascript&quot; src=&quot;https://cdnjs.cloudflare.com/ajax/libs/prism/1.12.2/prism.min.js&quot;&gt; &lt;/script&gt;
</code></pre>
<p>That&apos;s it!</p>
<p>Now you just need to learn how to tell Prism.js what to format, and what language to format it in. By default, Prism can format HTML, CSS, and Javascript. So let&apos;s do a little test.</p>
<pre><code class="language-html">&lt;!-- I am HTML --&gt;
&lt;h4&gt;I am a heading&lt;/h4&gt;
&lt;p&gt;I am a paragraph with &lt;em&gt;emphasized&lt;/em&gt;text&lt;/p&gt;
</code></pre>
<pre><code class="language-css">/* I am CSS */
h2, h3, h4 {
    color:blanchedalmond;
    background-color:cornflowerblue
    }
</code></pre>
<pre><code class="language-javascript">//I am javascript
$(&apos;body&apos;).on(&apos;mouseleave&apos;, &apos;.popover&apos;, function () {
				$(&apos;.popover&apos;).popover(&apos;hide&apos;);
			})
</code></pre>
<pre><code class="language-ruby">#I am ruby, but I am not being formatted :-(
array_of_cards.each do |c|
        #cards are in string format, e.g. &quot;jack_of_diamonds&quot;
        face = c.split(&apos;_&apos;,0)[0]
</code></pre>
<p>You should be seeing some different colors. And you might have noticed how it correctly identifies how comments are formatted in each language.</p>
<p>Wait? How did Prism know what language I was speaking in each of those examples?</p>
<p>This is how.</p>
<p>You should wrap your code in <code>&lt;pre&gt;</code> and <code>&lt;code&gt;</code> tags, in that order, and then add a class called &quot;language&quot; to the <code>&lt;code&gt;</code> tag, like this:</p>
<pre><code class="language-html">
&lt;pre&gt;
&lt;code class=&quot;language-javascript&quot;&gt;
//This is my javascript code 
&lt;/code&gt;
&lt;/pre&gt;
</code></pre>
<p>Obviously, you would change the &quot;language-javascript&quot; to &quot;language-css&quot; or &quot;language-html&quot; or &quot;language-fortran&quot;.</p>
<p>Even simpler, if you are using a Markdown editor, you can use back ticks, like this:</p>
<pre><code class="language-css">
```css
#pretty-code {
  color: pink; 
  font-style: &apos;gum drop&apos;;
  }
```
</code></pre>
<p>Get it?</p>
<p>As you have probably noticed, I am playing around with the colors right now. I&apos;ll explain how to do that, too.</p>
<p>But first, what about poor Ruby? She needs some formatting, too.</p>
<h3 id="addingotherlanguagestoprism">Adding other languages to Prism</h3>
<p>By default, Prism will format Javascript, CSS, and HTML. If you want other languages, you&apos;ll have to load them yourself. But it&apos;s easy.</p>
<p>Here&apos;s how to add syntax highlighting for Ruby.</p>
<p>Copy the following line, and paste it AFTER the javascript line that we pasted before, at the bottom of your file.</p>
<pre><code class="language-html">
&lt;script type=&quot;text/javascript&quot; src=&quot;https://cdnjs.cloudflare.com/ajax/libs/prism/1.12.2/components/prism-ruby.min.js&quot;&gt;&lt;/script&gt; 
</code></pre>
<p>Now we can add <b>class=&quot;language-ruby&quot;</b> to our code tags. See my blackjack game below for an example.</p>
<p>But what if you want Java or Python or LOLCode?<a href="javascript:void(0)" class="popover-dismiss" data-toggle="popover" data-trigger="hover" title data-content="Apparently that&apos;s a &lt;a href=&apos;https://en.wikipedia.org/wiki/LOLCODEreal&apos;&gt;real thing&lt;/a&gt;."><span class="footnote"></span></a></p>
<p>Here&apos;s the lazy way to add more languages. Examine that string I just gave you and look for the word &quot;ruby&quot;. All you need to do is change the word &quot;ruby&quot; to any of the <a href="http://prismjs.com/index.html?ref=wenchie.com#languages-list">other languages</a> available.</p>
<p>Once you have it working, you should ask yourself: will I frequently need to format code? If so, you should add the lines I gave you to your <b>default.hbs</b> file (that&apos;s the file in your ghost theme that holds the stuff you always want to load). Either edit the file directly, or go into your admin module in Ghost and add them via the &quot;Code Injection&quot; section of the Admin panel. Obviously, you want to put the .css file in the header, and the .js files in the footer.</p>
<p>If the answer is &quot;no,&quot; you could actually do it the way we discussed, by simply pasting the lines immediately above and below your code, <u>right inside your blog post</u>.</p>
<p>(Note: if you have trouble getting that to work in Ghost, make sure your script tags are aligned to the left with NO spaces or tabs in front of them, and make sure that all URLs start with https:// not plain // or http:// without the s.  Also: I am working on a self-hosting Ghost blog. I have no idea if they block scripts on a hosted version of Ghost... let me know in the comments).</p>
<p>This strategy has the added benefit that you only load the syntax highlighter when someone is actually viewing a post that contains code, instead of everywhere and all the time.</p>
<p>If you are one of those people who likes to collect everything, yes, you could go to the Prism website and create a massive file that will format all possible languages. But we should resist that impulse. Just load the modules you actually need when you need them. That&apos;s the beauty of the Prism approach to code formatting.</p>
<p>On the other hand, if you are one of those people who likes to try out different color schemes and create your own color combinations, I give you permission to do that, but ONLY if you promise not to spend more than an hour or two on it. That sort of thing can be addictive. But here&apos;s how you would do that.</p>
<figure>
    <img src="https://wenchie.com/content/images/2018/03/colorschemes.png" alt="Play my Ruby blackjack game, and learn how to use PRISM.JS  to pretty up your code">
    <figcaption>Prism color schemes</figcaption>
</figure>
<h3 id="howtocustomizeyourcolorscheme">How to customize your color scheme</h3>
<p>First, get Prism working with the default styles. Then look in these two places. The colors they demonstrate on their <a href="http://prismjs.com/index.html?ref=wenchie.com">website</a> can be found in the &quot;themes&quot; folder in their <a href="https://github.com/PrismJS?ref=wenchie.com">GitHub site</a>. And if you want more, take a look at this <a href="https://github.com/PrismJS/prism-themes/tree/master/themes?ref=wenchie.com">prism-themes</a> repository. They are just CSS files. Choose one that you like and download it to your assets/css folder, then link to it in your default.hbs file, right AFTER the default Prism css file. Remember that in Ghost, you need to use the &quot;asset helper.&quot; My link looks like this:</p>
<pre><code class="language-html">&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;{{asset &apos;css/prism-ghcolors.css&apos;}}&quot;/&gt;
</code></pre>
<p>Once you have that working, open it in a code editor and go to town changing the colors around. I love the <a href="https://www.w3schools.com/colors/colors_names.asp?ref=wenchie.com">CSS color names</a>, like PapayaWhip and DodgerBlue. I&apos;m using a creamy &quot;FloralWhite&quot; for my background, and isn&apos;t it so much nicer than regular white? You don&apos;t have to limit yourself to named colors. Here&apos;s a nicely arranged list of <a href="https://cloford.com/resources/colours/500col.htm?ref=wenchie.com">500 colors</a>. If a color doesn&apos;t have an official css name, you can use the hex code.</p>
<h3 id="finalconsiderations">Final considerations</h3>
<p>Here are a few more technical notes before I actually paste my Ruby blackjack code below.</p>
<ul>
<li class="li-pretty">Whenever you are loading javascript files, it&apos;s a good idea to press F12 on your Chrome or Firefox browser and look at the Console. You might see errors about the files being blocked. Chrome will reject them, for example, if your links don&apos;t start start with <b>https://</b>. Pay attention to the &quot;s&quot; which stands for &quot;super secure.&quot; </li>
    <li class="li-pretty">If you are reading this in the future (Hi, Future!), you will want to change the version number in all those links I gave you to the current version. The version I have installed is 1.12.2. Just during the week I was messing with this stuff, it went from 1.11.0 to 1.12.0 to 1.12.2. The way to find the most current version is go to <a href="https://cdnjs.com/libraries/prism/1.12.2?ref=wenchie.com">this page</a> and click the version dropdown at the top of the page. Luckily, they keep the old versions around, so if everything is working nicely, there&apos;s no need to upgrade. You will see the version number in the URL of all the links I gave you. Just make sure they are all the same. </li>
<li class="li-pretty">We are using the CDN (&quot;content delivery network&quot;) version of the PRISM files. That means we are calling them from a distant web server rather than serving them up from our own web server. Personally, I have grown to prefer that method because: </li>
    <ol>
        <li class="li-pretty&gt;It leaves your own server uncluttered.&lt;/li&gt;
      &lt;li class=" li-pretty">It makes it easier to update to newer versions of the program (just edit the links)</li>
        <li class="li-pretty">It is fast because your browser only makes the call once, and then caches the files. Also because CDN owners buy faster servers than you do.</li>
        <li class="li-pretty">You can easily change your mind and try out different things. A better syntax highlighter might show up tomorrow, and we might want to check it out</li>
         <li class="li-pretty">Most importantly, it doesn&apos;t really MATTER to me if the CDN link were to fail one day and I suddenly didn&apos;t have syntax highlighting on my blog. It&apos;s not of vital importance. If it were, then I would definitely want to download the files and serve them up from my own server. It&apos;s all just text files, after all.</li> 
</ol></ul>
<h3 id="usefullinks">Useful links</h3>
<p>Here are some useful links:</p>
<ol>
    <li class="li-pretty">Prismjs website: https://prismjs.com/</li>
    <li class="li-pretty">List of all the languages available: https://prismjs.com/#languages-list</li>
    <li class="li-pretty">Prism Github site where you can play with color schemes: https://github.com/PrismJS.</li>
    <li class="li-pretty">CDN links. This is where you will find all of the links you might need to load Prism and its many accessoiries. https://cdnjs.com/libraries/prism </li>
</ol>

<p>OK, without further ado, here is the hopefully prettified code to my brilliant blackjack game, followed by a decorative rooster.</p>
<pre><code class="language-ruby">

#BLACKJACK by Mary Dean
#working game located at 
#https://marydean-blackjack.herokuapp.com

#main.rb
require &apos;rubygems&apos;
require &apos;sinatra&apos;
require &apos;pry&apos;

#removed this line due to possible conflict with chrome
#set :sessions, true
#and added this line. 
use Rack::Session::Cookie, :key =&gt; &apos;rack.session&apos;,
                            :path =&gt; &apos;/&apos;,
                            :secret =&gt; &apos;some_random_string&apos;

#these lines are for me to test over home network
set :environment, :production
#set :bind, &apos;IP ADDRESS&apos;

helpers do

    def value_of_hand(array_of_cards)
      faces = {&quot;ace&quot; =&gt;11,
               &quot;2&quot; =&gt; 2,
               &quot;3&quot; =&gt; 3,
               &quot;4&quot; =&gt; 4,
               &quot;5&quot; =&gt; 5,
               &quot;6&quot; =&gt; 6,
               &quot;7&quot; =&gt; 7,
               &quot;8&quot; =&gt; 8,
               &quot;9&quot; =&gt; 9,
               &quot;10&quot; =&gt; 10,
               &quot;jack&quot; =&gt; 10,
               &quot;queen&quot; =&gt; 10,
               &quot;king&quot; =&gt; 10
      }
      total_value = 0
      num_aces = 0
      array_of_cards.each do |c|
        #cards are in string format, e.g. &quot;jack_of_diamonds&quot;
        face = c.split(&apos;_&apos;,0)[0]

        value = faces[face]

        total_value += value

        num_aces += 1 if face == &apos;ace&apos;

      end
      #correct for Aces -- BORROWED THIS LOGIC FROM TEACHER&apos;S CODE
      num_aces.times do
        total_value -= 10 if total_value &gt; 21
      end

      return total_value
    end #end value_of_hand

    def decrease_kitty_by(amount)
      old_kitty = session[:kitty].to_i
      new_kitty = old_kitty - amount
      session[:kitty] = new_kitty.to_s
      if new_kitty <= 0 @error="You have run out of money, please put more money in your kitty" end def increase_kitty_by(amount) old_kitty="session[:kitty].to_i" new_kitty="old_kitty" + amount session[:kitty]="new_kitty.to_s" check_kitty kitty="session[:kitty].to_i" bet="session[:current_bet].to_i" if < redirect " game overdraft" set_helpful_message(type) #after extracting all these i think it was a mistake because now the #lookups will be done even they aren't needed player_value="value_of_hand(session[:player_cards]).to_s" dealer_value="value_of_hand(session[:dealer_cards]).to_s" bet_value="session[:current_bet]" name_value="session[:player_name]" message="case" type when 'continue_player_hand' "<h3>Your cards add up to:<strong> #{player_value} </strong>What would you like to do?&quot;
        when &apos;player_went_over&apos;
          &quot;<h3>Drat!</h3>Too bad! With #{player_value} you have busted, #{name_value}. I hope you will play again&quot;
        when &apos;player_stays&apos;
          &quot;<h3>Player stays</h3>You have chosen to stay with #{player_value}. Now it&apos;s the dealer&apos;s turn.&quot;
        when &apos;dealer_must_hit&apos;
          &quot;The dealer must hit...&quot;
        when &apos;dealer_went_over&apos;
          &quot;<h3>Yahoo!</h3> The dealer went over! You win #{bet_value} chips!&quot;
        when &apos;player_blackjack&apos;
          &quot;<h3>Awesome! #{name_value} got a BLACKJACK!</h3> You win #{(session[:current_bet].to_i * 2).to_s} chips!&quot;
        when &apos;dealer_blackjack&apos;
          &quot;<h3>Dealer Blackjack :-( </h3>Too bad, #{name_value}, the dealer got a blackjack. You lose #{bet_value} chips&quot;
        when &apos;dealer_wins&apos;
          &quot;<h3>Darn!</h3> The dealer&apos;s #{dealer_value} beats your #{player_value}. You lose #{bet_value} chips&quot;
        when &apos;player_wins&apos;
          &quot;<h3>You win!</h3> Congratulations, #{name_value}, you won #{bet_value} chips!&quot;
        when &apos;its_a_tie&apos;
          &quot;<h3>Tie game</h3>You both have #{dealer_value}, so looks like we have a TIE! Let&apos;s play again.&quot;
      end #end of case
    session[:helpful_message]=message
    message
  end #of def

end #of helpers

#WELCOME
#I wanted to serve a static page but couldn&apos;t figure out how
get &apos;/&apos; do
  erb :index, :layout =&gt; :layout_plain
end

#GET and SUBMIT USER NAME
get &apos;/set_name&apos; do
  erb :set_name, :layout =&gt; :layout_plain
end

#SET NAME, KITTY, AND INITIAL BET
post &apos;/set_name&apos; do
  session[:player_name] = params[:player_name]
  session[:kitty] = params[:kitty]
  session[:current_bet] = params[:current_bet]

  check_kitty
  redirect &quot;/game&quot;
end

##CREATE DECK OF CARDS AND DEAL 2 CARDS TO EACH
#ALSO SET SHOW/HIDE VARIABLES
get &apos;/game&apos; do
  @show_dealer_first_card = false
  @show_hit_stay_buttons = true
  @show_dealer_turn_button = false
  @dealer_blackjack = false
  @player_blackjack = false
  session[:dealer_cards] = []
  session[:player_cards] = []
  #I WANT TO RE-USE DECK FOR CARD COUNTING SO ONLY CREATE NEW DECK IF NEEDED
  if session[:deck].nil? || session[:deck].size &lt; 10
   suits = [&quot;hearts&quot;, &quot;clubs&quot;, &quot;spades&quot;, &quot;diamonds&quot;]
      faces = [&quot;ace&quot;, &quot;2&quot;, &quot;3&quot;, &quot;4&quot;, &quot;5&quot;, &quot;6&quot;, &quot;7&quot;, &quot;8&quot;, &quot;9&quot;, &quot;10&quot;,&quot;jack&quot;,&quot;queen&quot;,&quot;king&quot;]
      deck = []
      cards= faces.each do |s|
        suits.each do |f|
          card = &quot;#{s}_of_#{f}&quot;
          deck.push(card)
        end
      end
   deck.shuffle!
   session[:deck] = deck
   end
  session[:player_cards] = [session[:deck].pop, session[:deck].pop]
  session[:dealer_cards] = [session[:deck].pop, session[:deck].pop]
  if value_of_hand(session[:player_cards])== 21
    @player_blackjack = true
    @show_hit_stay_buttons = false
    @show_play_again_button = true
    increase_kitty_by(session[:current_bet].to_i * 2)
    set_helpful_message(&apos;player_blackjack&apos;)
    else
    set_helpful_message(&apos;continue_player_hand&apos;)
    end
  erb :game
end

post &apos;/game/player/hit&apos; do
  session[:player_cards] &lt;&lt; session[:deck].pop
  if value_of_hand(session[:player_cards])&gt;21
    set_helpful_message(&apos;player_went_over&apos;)
    decrease_kitty_by(session[:current_bet].to_i)
    @show_hit_stay_buttons = false
    @show_dealer_turn_button = false
    @show_play_again_button = true
    @show_dealer_first_card = false
    erb :game
  else
    @show_hit_stay_buttons = true
    @show_dealer_turn_button = false
    @show_play_again_button = false
    @show_dealer_first_card = false
    set_helpful_message(&apos;continue_player_hand&apos;)
  end
  erb :game
end

post &apos;/game/player/stay&apos; do
  set_helpful_message(&apos;player_stays&apos;)
  #@success = &quot;You have chosen to stay with #{value_of_hand(session[:player_cards])}&quot;
  @show_hit_stay_buttons = false
  @show_dealer_turn_button = true
  @show_dealer_first_card = false
  erb :game
end

get &apos;/game/dealer&apos; do
  @show_hit_stay_buttons = false
  dealer_value = value_of_hand(session[:dealer_cards])
  player_value = value_of_hand(session[:player_cards])
  if dealer_value == 21 &amp;&amp; session[:dealer_cards].size == 2
    set_helpful_message(&apos;dealer_blackjack&apos;)
    decrease_kitty_by(session[:current_bet].to_i)
    @show_play_again_button = true
  elsif dealer_value &gt; 21
    set_helpful_message(&apos;dealer_went_over&apos;)
    @show_play_again_button = true
    increase_kitty_by(session[:current_bet].to_i)
  elsif dealer_value &lt; 17
    set_helpful_message(&apos;dealer_must_hit&apos;)
    redirect &apos;/game/dealer/hit&apos;
  elsif dealer_value &gt;= 17
    if dealer_value &gt; player_value
      set_helpful_message(&apos;dealer_wins&apos;)
      @show_play_again_button = true
      decrease_kitty_by(session[:current_bet].to_i)
    elsif dealer_value &lt; player_value
      set_helpful_message(&apos;player_wins&apos;)
      increase_kitty_by(session[:current_bet].to_i)
      @show_play_again_button = true
    elsif dealer_value == player_value
      set_helpful_message(&apos;its_a_tie&apos;)
      @show_play_again_button = true
    end
  end
  @show_dealer_first_card = true
  erb :game
end

get &apos;/game/dealer/hit&apos; do
  session[:dealer_cards] &lt;&lt; session[:deck].pop
  redirect &apos;/game/dealer&apos;
end

get &apos;/game/overdraft&apos; do
  erb :overdraft
end

get &apos;/game/player/set_new_bet&apos; do
  erb :new_bet
end

post &apos;/game/player/set_new_bet&apos; do
  current_kitty = session[:kitty].to_i
  new_amount = params[:add_to_kitty].to_i
  total = current_kitty + new_amount
  session[:kitty] = total.to_s
  session[:current_bet] = params[:current_bet]
  check_kitty
  redirect &apos;/game&apos;
end

get &apos;/profile&apos; do
  &quot;Edit your profile here&quot;
end

get &apos;/cash_out&apos; do
  &quot;Here are your big winnings!&quot;
end

</=></code></pre>
<figure>
<img src="https://openclipart.org/download/297280/Abstract-Rooster.svg" alt="Play my Ruby blackjack game, and learn how to use PRISM.JS  to pretty up your code" style="width:20vw">
    <figcaption>An example of free clipart from <a href="https://openclipart.org/?ref=wenchie.com">openclipart.org</a></figcaption>
</figure>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[The trick to using multiple counters in css... and how to number your figures]]></title><description><![CDATA[Once I figured out how to use them, I fell in love with counters. You can add incrementing numbers to anything.]]></description><link>https://wenchie.com/the-trick-to-using-counters-in-css/</link><guid isPermaLink="false">67cb46d71cdfae1aa7308231</guid><category><![CDATA[Easter-Eggs]]></category><category><![CDATA[HTML-CSS]]></category><dc:creator><![CDATA[Wenchie]]></dc:creator><pubDate>Mon, 05 Mar 2018 08:27:41 GMT</pubDate><media:content url="https://wenchie.com/content/images/2018/03/sketch_featureimage-1.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><div class="side-note-dotty">
Note: The actual &quot;trick&quot; I will eventually discuss here applies to when you are using multiple counters that are reset by the same element. In that case, you must pack all your counter-resets into ONE SINGLE reset statement.
</div>
<img src="https://wenchie.com/content/images/2018/03/sketch_featureimage-1.jpg" alt="The trick to using multiple counters in css... and how to number your figures"><p>Once I figured out how to use them, I fell in love with counters.</p>
<p>You can add incrementing numbers to anything. They make you look more polished and organized than you actually are. It&apos;s cool how they just work.</p>
<p>My first counter was a figure counter. I wanted to learn how to add captions to my photos, so I learned about the <code>&lt;figure&gt;</code> tag, which happens to come equipped with the handy <code>&lt;figcaption&gt;</code> tag. That means your photos and their captions can always stay together. Like this:</p>
<div class="codeblock"><pre>
&lt;figure&gt;
&lt;img src=&quot;/content/images/2018/03/WorstPoliceSketchEver.jpg&quot;&gt;
    &lt;figcaption&gt;The Worst Police Sketch Ever&lt;/figcaption&gt;
&lt;/figure&gt;
</pre></div>
<img style="width: 234px; display:block;margin:0 auto 1em auto" alt="The trick to using multiple counters in css... and how to number your figures" src="https://wenchie.com/content/images/2018/03/worst_sketch_sm_caption.jpg">
<p>(Using <code>&lt;figure&gt;</code> is also a good way to center your photos, which is often harder than it should be. We will do that in our CSS below. Also... you DO know that you can enter HTML directly into a post in your Ghost blog, don&apos;t you? You can&apos;t do that in most other blogging platforms.)</p>
<p>Naturally, adding a beautiful caption like that made me wonder, &quot;Hmm, could I also <em>number</em> my captions, like they do in school textbooks? I want it to say, &quot;Figure 1: The Worst Police Sketch Ever&quot;<a href="javascript:void(0)" class="popover-dismiss" data-toggle="popover" data-trigger="hover" title data-content="That&apos;s real, by the way, an actual police sketch that led to an arrest in Bolivia."><span class="footnote"></span></a>. Then the next one should say, &quot;Figure 2...&quot; and so on.</p>
<p>Obviously, I could just write the words, &quot;Figure 1,&quot; and &quot;Figure 2,&quot; into my figcaptions, but why not let the computer do that work for me? After all, it is inevitable that I will eventually want to squeeze &quot;Figure 1b&quot; between figures 1 and 2, and then I will realize I need to re-number everything, and you know how that goes.</p>
<p>Luckily, it&apos;s pretty easy. My HTML will stay exactly the same. What I am going to do is add a &quot;counter&quot; to my CSS style sheet, and then take that counter and prefix it to every <code>&lt;figcaption&gt;</code> that happens to come along. If you want the same effect on your blog, you can simply copy and paste my CSS code, shown below. The end result will look like this:</p>
<figure>
<img src="https://wenchie.com/content/images/2018/03/WorstPoliceSketchEver_sm.jpg" alt="The trick to using multiple counters in css... and how to number your figures">
    <figcaption>The Worst Police Sketch Ever</figcaption>
</figure>
<p>The only real mental work you need to do is to think: &quot;<u>When</u> do I want my counter to start back at 1 again?&quot;</p>
<p>In my case, that&apos;s not a hard question. Each one of my blog posts is a separate document. And I don&apos;t forsee a time when I would have a single post that contained multiple &quot;chapters,&quot; so to speak, so I am fine with my figures starting at &quot;Fig 1&quot; and continuing until... whenever. When I create a new post, the numbering should start back at 1 again. Perfect.</p>
<p>What does every HTML document have exactly one of? A <code>&lt;body&gt;</code> tag, of course. So I can simply tell my counter to start from zero every time it encounters a <code>&lt;body&gt;</code> tag.</p>
<p>Later, when I create footnotes, I will do exactly the same thing. The first footnote in any post should be footnote number 1, right?<a href="javascript:void(0)" class="popover-dismiss" data-toggle="popover" data-trigger="hover" title data-content="Hee-hee, I am actually footnote number 2."><span class="footnote"></span></a> And they should continue incrementing from there. So, conveniently, the <code>&lt;body&gt;</code> tag can serve as the clue that tells BOTH counters to start over.</p>
<p>Just to be clear... we could use <em>any</em> tag. If you were a lawyer, for example, you could make every paragraph numbered, and reset the numbering at every section. Or if you are writing a research paper, you could attach outline-style numbering to your <code>&lt;h1&gt;</code> and <code>&lt;h2&gt;</code> tags, so that your <code>&lt;h2&gt;</code> counter is reset every time there&apos;s a new <code>&lt;h1&gt;</code>, and so forth. Google &quot;css counter&quot; if you want to get fancy.</p>
<p>Here&apos;s my CSS for a really basic figure which centers the photo (or table, or graph, or whatever), then centers a caption under it, then numbers the captions, starting at 1. Copy this into your CSS (or simply include it between <code>&lt;style&gt;</code> tags in your HTML), and every time you use the <code>&lt;figcaption&gt;</code> tag you will get automatic numbering.</p>
<div class="codeblock"><pre>
/* -----------------------------------------------*/
/* MARY&apos;S CENTERED FIGURES WITH NUMBERED CAPTIONS */
/* -----------------------------------------------*/
body {
	counter-reset: my-fig-counter 0;
}
figure {
	text-align: center;
}
figcaption {
	font-size:smaller;
}
figcaption:before {
	counter-increment: my-fig-counter 1; 
	content: &quot;Fig. &quot; counter(my-fig-counter, decimal) &quot; - &quot;; 
}
</pre></div>
<p>I&apos;m sure you can figure out what everything means, but here&apos;s the rundown:</p>
<ul class="ul-pretty">
    <li class="li-pretty"><b>counter-reset</b> means &quot;set the counter back to zero&quot; when you encounter a new body tag. 
    </li><li class="li-pretty"><b>counter-increment</b> means &quot;increment the counter by this much&quot;. 
    </li><li class="li-pretty"><b>:before</b> means, &quot;prefix any figcaption with the following <b>content</b>&quot;. 
    </li><li class="li-pretty"><b>decimal</b> means use regular numbers, not Roman numerals or any of the other format choices (which you can google yourself).  
    </li><li class="li-pretty">And, obviously, you can change the word &quot;<b>Fig</b>&quot; to &quot;Figure&quot; or &quot;Illustration&quot; or &quot;Example&quot; or anything else you want.
</li></ul>
<p>Ta-da, an automatically incrementally numbered figure.</p>
<figure>
<img src="https://wenchie.com/content/images/2018/03/best_sketch.jpg" alt="The trick to using multiple counters in css... and how to number your figures">
    <figcaption>The Best Police Sketch Ever?</figcaption>
</figure>
<h3 id="sowhatsthetrickyousaidtherewasatrick">So what&apos;s the trick? You said there was a trick!</h3>
<p>Now, pay attention.</p>
<p>As you have hopefully discovered, I later decided to create some awesome, hoverable footnotes<a href="javascript:void(0)" class="popover-dismiss" data-toggle="popover" data-trigger="hover" title="Here&apos;s a fancier version" data-content="A hoverable footnote can have a title, too, but I actually prefer the smaller, cleaner one without the title."><span class="footnote"></span></a>, which <u>also</u> use counters. I will show you how I did that in another post, but basically, I used the same method of resetting the numbering for every new HTML &quot;body.&quot;</p>
<p>A week later, I realized that my figures were no longer incrementing -- every single one was labeled &quot;Figure 1&quot; -- and I spent a good two hours trying to figure out what I had broken.</p>
<p>This is what I had done WRONGLY.</p>
<div class="codeblock"><pre>
body {
	counter-reset: my-fig-counter 0;
	counter-reset: my-footnote-counter 0;
}
</pre></div>
<p>Which is the same as doing this:</p>
<div class="codeblock"><pre>
body {counter-reset: my-fig-counter 0;}
body {counter-reset: my-footnote-counter 0;}
</pre></div>
<p>The problem with doing things that way -- and I did not find this documented anywhere that was easy to find -- is that, apparently, you can only have <b>ONE counter-reset</b> statement attached to a particular tag. (Or maybe only one counter-reset period. I did not test that.)</p>
<p>In any event, the RIGHT, CORRECT way to reset your counters is like this:</p>
<div class="codeblock" style="font-size:larger; border:4px dotted lime;"><pre>
body {
    counter-reset: my-fig-counter 0 my-footnote-counter 0;
}
</pre></div>
<p>Notice that there are only spaces separating those items, no punctuation.</p>
<p>That&apos;s a little bit annoying because I like to keep my css code grouped together. Anything that applies to figures should be in one place, in my opinion, and anything related to footnotes should be in another, but when it comes to counter-resets, they need to stay together in one single statement.</p>
<p>Got it?</p>
<p>I hope this helps someone who has run into the same issue, when they find out that all 20 of their figures are labeled &quot;Figure 1.&quot; When you think your CSS counter increment is not working, it might NOT be your counter-increment, but rather your counter-reset being wiped out by another counter-reset.</p>
<p>Cheers! Now go forth and number your figures.</p>
<figure>
<img src="https://wenchie.com/content/images/2018/03/sketch_malefemale.jpg" alt="The trick to using multiple counters in css... and how to number your figures">
    <figcaption>Is this suspect male<a href="javascript:void(0)" class="popover-dismiss" data-toggle="popover" data-trigger="hover" title data-content="A footnote inside of a caption... has the world gone mad?"><span class="footnote"></span></a> or female?</figcaption>
</figure>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Create your own vanity gravatar...  then hash it]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>As you wander the web, you might notice that many people have a cute little photo associated with their name. For example, when I login to GitHub, Codepen, DigitalOcean, or many other sites, a mini-me magically appears.</p>
<figure>
<img alt="How a gravatar appears on Codepen" src="https://wenchie.com/content/images/2018/03/gravatar_codepen.jpg">
    <figcaption>A gravatar on Codepen</figcaption>
</figure>
<p>If I write a comment, there I am again.</p>]]></description><link>https://wenchie.com/create-your-own-vanity-gravatar/</link><guid isPermaLink="false">67cb46d71cdfae1aa730822f</guid><dc:creator><![CDATA[Wenchie]]></dc:creator><pubDate>Sat, 03 Mar 2018 21:39:09 GMT</pubDate><media:content url="https://wenchie.com/content/images/2018/03/gravatar_codepen-2.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://wenchie.com/content/images/2018/03/gravatar_codepen-2.jpg" alt="Create your own vanity gravatar...  then hash it"><p>As you wander the web, you might notice that many people have a cute little photo associated with their name. For example, when I login to GitHub, Codepen, DigitalOcean, or many other sites, a mini-me magically appears.</p>
<figure>
<img alt="Create your own vanity gravatar...  then hash it" src="https://wenchie.com/content/images/2018/03/gravatar_codepen.jpg">
    <figcaption>A gravatar on Codepen</figcaption>
</figure>
<p>If I write a comment, there I am again.</p>
<figure>
<img alt="Create your own vanity gravatar...  then hash it" src="https://wenchie.com/content/images/2018/03/gravatar_comment.jpg">
<figcaption>Same gravatar... on Github!</figcaption>
</figure>
<p>Where does mini-me come from? How could multiple companies all possess the same image of me? Are they secretly owned by one giant mega-corporation that tracks me everywhere I go?</p>
<p>Luckily, no. A gravatar is a &quot;globally recognized avatar&quot; and you create it yourself. You register it at <a href="https://en.gravatar.com/?ref=wenchie.com">Gravatar.com</a>, associate it with an email address, and voila. If you don&apos;t have one yet, let&apos;s make one, and learn some stuff along the way.</p>
<h3 id="makeyourowngravatar">Make your own gravatar</h3>
<p>Find a nice picture of yourself. How nice? As nice as 2048-pixels square, that&apos;s how. They recently increased the allowable image size so people can inspect your nose hairs on their IPhones, but since your gravatar will usually be seen as an 80-pixel square, you don&apos;t need anything that huge.</p>
<p>Striking the right attitude is what counts. We don&apos;t want to see a blurry face cut out of your Thanksgiving dinner snapshot. Put some thought into it, Mon!</p>
<p>Here are some random examples of gravatars.</p>
<figure>
<img alt="Create your own vanity gravatar...  then hash it" src="https://wenchie.com/content/images/2018/03/GravatarExamples.png">
<figcaption>Other peoples&apos; gravatars</figcaption>
</figure>
<p>Personally, I like to see real people rather than cartoons or animals. My first programming class was an online program that lasted several months. I spent a lot of time interacting with the instructor -- hearing his voice during the live sessions, getting his feedback on my homework, &quot;chatting&quot; in the forums -- and it always felt weird and alienating that I never once saw what he looked like. I didn&apos;t want to stalk him, mind you, just to see if his face matched his voice... just see what he looked like, in a normal, human way for goodness sake.</p>
<p>(It was called Tealeaf Academy, now called <a href="https://launchschool.com/?ref=wenchie.com"> LaunchSchool</a>. Excellent program if you&apos;re interested in learning Ruby. I notice they still have no names or photos of the instructors. Maybe they&apos;re hideously deformed creatures, or FBI most wanteds. See what happens when you don&apos;t include a real photo in your gravatar? People think things.)</p>
<p>For my own gravatar, I chose to put a picture of me on the back of a pony when I was three. Why? Because I don&apos;t have any good pictures of my adult self, but more importantly, so that when people read my posts and comments, they will think, &quot;Wow, what a smart 3-year old!&quot; and hopefully won&apos;t say anything mean.</p>
<p>As you have probably surmised, your photo must be SQUARE. So crop it into a square shape. If I showed you how to do that, I would have to start ranting about how Windows Paint has been stripped of basic features like &quot;constrain a selection to a square shape&quot; that it had 20 years ago, so I won&apos;t. You&apos;ll just have to open Paint, use the selection tool, eyeball the little pixel counters at the bottom of the screen, and try to stop your jittery fingers precisely when the width is the same as the height.</p>
<figure>
<img alt="Create your own vanity gravatar...  then hash it" src="https://wenchie.com/content/images/2018/03/lincolnsized.jpg">
    <figcaption>Abe succeeds at making a square</figcaption>
</figure>
<p>Got it! Now click &quot;Crop,&quot; and save your image. Then go to this website and create a gravatar account:</p>
<p style="text-align:center; font-size:1.2em"><a href="https://en.gravatar.com/?ref=wenchie.com">https://en.gravatar.com/</a></p>
<p>&quot;But wait!&quot; you say. &quot;It says I have to login into my Wordpress account! I neither have nor want a Wordpress account.&quot;</p>
<p>I know, neither did I. It seems that Gravatar is a subsidiary of Wordpress. Don&apos;t worry, though, they won&apos;t force you to create a virus-laden, ad-ridden blog. They will just ask you to enter your email address and to upload an image, and then you&apos;ll never have to go back unless you want to change one of those two things.</p>
<p>As I was writing this, I was happy to discover that you can add multiple email addresses and images to your Gravatar account. So I decided to create a separate gravatar for my (er, future) business name, Casa 14 Studios. I entered my business email address and uploaded my tortuously-produced logo (it&apos;s a <em>casa</em> with a 14 in it, in case you can&apos;t tell). At first I thought it looked fine:</p>
<figure>
<img alt="Create your own vanity gravatar...  then hash it" src="https://wenchie.com/content/images/2018/03/gravatar_business.jpg">
    <figcaption>How my logo appears on Codepen...nice!</figcaption>
</figure>   
<p>But then I ventured over to a couple of other websites where I use my business email, and, oh no! Ugly gravatar alert!</p>
<figure>
<img alt="Create your own vanity gravatar...  then hash it" src="https://wenchie.com/content/images/2018/03/gravatar_business_ugly-1.jpg">
    <figcaption>How my logo appears elsewhere... ick! <i class="fa fa-frown-o"></i></figcaption>
</figure>
<p>Yuk. It seems that some websites roundify your gravatar. And where is that orange dot coming from? And why is the other one all squished to the top? I don&apos;t know. But now I do know the second rule of gravatars:</p>
<p><em>Not only must your gravatar be SQUARE, it must also look good as a CIRCLE.</em></p>
<p>Back to the logo drawing board.</p>
<h3 id="howdogravatarswork">How do gravatars work?</h3>
<p>You might be wondering, how does some random website &quot;fetch&quot; my gravatar? I was wondering that, too, so I took a look at their  <a href="https://en.gravatar.com/site/implement/?ref=wenchie.com">Developer Resources</a>. Here&apos;s how they do it.</p>
<p>You go to a website and create an account, using your email address. They take that email address and &quot;hash&quot; it -- which means &quot;turn it into secret code&quot; -- using an algorithm called &quot;MD5,&quot; which, since my name is Mary Dean, I assume was named after me.</p>
<p>To see how easy it is to turn your email address into an MD5 hash, hop over to this website, type your email address into the box, and press the button. While you are there, copy that hash into your clipboard.</p>
<p><a href="https://www.md5hashgenerator.com/?ref=wenchie.com">https://www.md5hashgenerator.com/</a></p>
<p>It will look something like this:</p>
<p>6fdbb18ef3ab9304260e516530d57754</p>
<p>With that long string of text, you, or anyone, can fetch your gravatar. Just type the following into a web browser, replacing my hash with your hash.</p>
<p><a href="https://gravatar.com/avatar/6fdbb18ef3ab9304260e516530d57754?ref=wenchie.com">https://gravatar.com/avatar/6fdbb18ef3ab9304260e516530d57754</a></p>
<p>Press enter, and instantly you should see your 80-pixel face... or your dog&apos;s face, or whatever image you used for your gravatar.</p>
<figure>
<img alt="Create your own vanity gravatar...  then hash it" src="https://wenchie.com/content/images/2018/03/gravatar_it_worked.jpg">
    <figcaption>Making an HTTP call to fetch a gravatar</figcaption>
<p>OK, so at first I was thinking, &quot;I shouldn&apos;t show my actual email &quot;hash&quot; to my readers. That&apos;s private!&quot; But then I realized... anyone who sees anyone&apos;s gravatar on any page can simply look at the page source (right-click, View Page Source) and see that person&apos;s hashed email address.</p>
<p>Indeed, this is what I am seeing under the hood:</p>
<code>
<img class="profile-avatar" src="https://gravatar.com/avatar/6fdbb18ef3ab9304260e516530d57754?s=512" alt="Create your own vanity gravatar...  then hash it" id="profile-image">
</code>
<p>If you click on that URL you will see a larger mini-me. Why is it larger? Note the &#xA0; <code>?s=512</code> &#xA0; at the end of the URL. That means &quot;give it to me in a size of 512 pixels.&quot;</p>
<p><img style="display:block;margin:0 auto 1em auto" alt="Create your own vanity gravatar...  then hash it" src="https://wenchie.com/content/images/2018/03/gravatar_larger.jpg"><br></p>
<p>What we just did is called &quot;making an HTTP request to the gravatar.com API&quot; or simply &quot;making a call to the gravatar API.&quot;</p>
<p>API (&quot;application programming interface&quot;) is one of those scary terms that you can be totally intimidated by until you realize it just means, &quot;the format in which you must send your request in order to get something back.&quot; It&apos;s like those complicated rebate forms we used to have to fill out to get $20 back from the purchase of a laser printer. &quot;Complete and sign the enclosed form. Send it along with with the UPC symbol cut from the bottom of the box, and the original register receipt with the printer purchase highlight</p>
<p>Cool, huh?</p>
<p>Here is a Codepen that will accept your email address, hash it, and return your gravatar, just like we did. You can use it right here on my page. You could also examine the javascript to see how he generates the hash.</p>
<p data-height="519" data-theme-id="0" data-slug-hash="eZjpVW" data-default-tab="result" data-user="SamEureka" data-embed-version="2" data-pen-title="Gravatar Checker (MD5.js Test)" class="codepen">See the Pen <a href="https://codepen.io/SamEureka/pen/eZjpVW/?ref=wenchie.com">Gravatar Checker (MD5.js Test)</a> by Sam Dennon (<a href="https://codepen.io/SamEureka?ref=wenchie.com">@SamEureka</a>) on <a href="https://codepen.io/?ref=wenchie.com">CodePen</a>.</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>
<h3 id="isitsafetohaveagravatar">Is it &quot;safe&quot; to have a gravatar?</h3>
<p>The thing that surprised me on this little excursion to hash-land is that I always thought &quot;security&quot; meant that secret codes were produced on the fly. If anyone ends up with the same hash from the same email address, it can&apos;t be that hard to reverse engineer. That&apos;s called a handshake.</p>
<p>Your email address is a unique identifier, meaning nobody else on the internet has that same email address. The &quot;hashed&quot; version of your email address is likewise unique. Try googling your hash. When I google mine, it turns up a comment I made on a Code Academy website back in 2014.</p>
<p>Any website that uses gravatars <em>already stores your email address in the hashed format</em>. So, not using a gravatar doesn&apos;t protect your hash from being associated with you in the event a website is hacked, or, just as likely, sloppily designed. It would be possible for someone to consolidate all the brilliant things I have said on Stack Overflow, Code Academy, Codepen, Github, Wordpress, and wherever, by tracking my hash, so to speak.</p>
<p>But (a.) so what? Most of my participation in these sites has been related to trying to learn how to solve various programming issues. Not highly sensitive stuff.</p>
<p>And (b.) you could easily prevent that kind of tracking by using a different email address and different gravatar at each different site that you participate in. But the whole point of having a &quot;globally recognized avatar&quot; is to unify, rather than fracture, your existence, isn&apos;t it?</p>
<p>So here&apos;s the recommendation:</p>
<ol>
<li>
<p>At a bare minimum, you should have two different email addresses for your personal and public stuff. Your gravatar is your public face. Don&apos;t use your personal email address for it.</p>
</li>
<li>
<p>It&apos;s also a good idea to have a third email address for any use that is likely to put you on a mailing list. That way, your discount coupon from Kohls and your daily news briefing from the New York Times don&apos;t get mixed in with your sister&apos;s happy birthday message, or news about your cousin&apos;s chemotherapy.</p>
</li>
<li>
<p>If you register on a &quot;highly sensitive&quot; website, where you would be really horrified to have anything you say tracked back to you, you should use an email address that you ONLY use for that site.</p>
</li>
</ol>
<p>And here&apos;s a good trick for that. When you have a gmail account, you actually have an infinite number of email addresses. Why? Because you can <u>add a period</u> to the first part of your email address:</p>
<pre><code>fuzzywuzzy@gmail.com = fuzzy.wuzzy@gmail.com
</code></pre>
<p>And/or you can <u>add a plus sign</u> and anything else to the <u>end</u> of your email address:</p>
<pre><code>fuzzywuzzy+todo@gmail.com
fuzzywuzzy+business@gmail.com
fuzzywuzzy+twitter@gmail.com
fuzzywuzzy+junkmail@gmail.com
</code></pre>
<p>All of those &quot;different&quot; emails will (a.) produce different hashes (b.) still be delivered to your gmail account. So you could set up filters, causing all of your +todo items go into a folder and everything from +junkmail to go into the trash. Here&apos;s an article about doing that sort of thing: <a href="https://fieldguide.gizmodo.com/how-to-use-the-infinite-number-of-email-addresses-gmail-1609458192?ref=wenchie.com">https://fieldguide.gizmodo.com/how-to-use-the-infinite-number-of-email-addresses-gmail-1609458192</a></p>
<p>I think that&apos;s a very good idea whenever you sign up for something &quot;iffy.&quot; Add a tag to the end of your email address when you sign up for something, like:</p>
<pre><code>fuzzywuzzy+visatraveloffer@gmail.com
</code></pre>
<p>Later, when you are flooded with spam, you will know exactly where it came from and you can use that unique address to filter it right into the trash.</p>
<p>(None of that addresses the issue that Google scans all free email accounts to mine marketing information, so there goes your privacy anyway. It&apos;s still a good idea for junk mailing lists, but I&apos;m a big believer in paying for your personal/private email accounts.</p>
<p>If you want to read an in-depth discussion of gravatars and privacy, see <a href="https://www.wordfence.com/blog/2016/12/gravatar-advisory-protect-email-address-identity/?ref=wenchie.com">here</a>.</p>
<p>And if you want to look into using and filtering multiple gmail addresses for different purposes, see <a href="https://www.wordfence.com/blog/2016/12/gravatar-advisory-protect-email-address-identity/?ref=wenchie.com">here</a>.</p>
<p>Meanwhile, I hope you enjoy seeing your gravatar pop up in unexpected places, and I urge you to participate, not just lurk, in the various communities that are out there to help you learn and create.</p>
<!--kg-card-end: markdown--></figure>]]></content:encoded></item><item><title><![CDATA[Pixi to make "cloth" photos]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>Using PIXI to make cloth flag photos</p>
<style>
    canvas { position: absolute; top: 0; left: 0; z-index: -1; }
canvas + canvas { opacity: 0.3; z-index: -2; }

.desc { position: absolute; z-index: 10; left: 0; right: 0; bottom: 0; padding: 1em; font-size: 10px; color: #aaa; text-align: center; }

.loading:before {
  position: absolute;
  top: 0;
  left: 0;</style>]]></description><link>https://wenchie.com/pixi-to-make-cloth-photos/</link><guid isPermaLink="false">67cb46d71cdfae1aa730822a</guid><category><![CDATA[Easter-Eggs]]></category><dc:creator><![CDATA[Wenchie]]></dc:creator><pubDate>Fri, 23 Feb 2018 08:14:33 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>Using PIXI to make cloth flag photos</p>
<style>
    canvas { position: absolute; top: 0; left: 0; z-index: -1; }
canvas + canvas { opacity: 0.3; z-index: -2; }

.desc { position: absolute; z-index: 10; left: 0; right: 0; bottom: 0; padding: 1em; font-size: 10px; color: #aaa; text-align: center; }

.loading:before {
  position: absolute;
  top: 0;
  left: 0;
  padding: 1em;
  content: 'Loading...';
  color: #aaa;
  font-family: monospace;
}
    </style>
<p class="desc">Cloth code adapted from <a href="https://codepen.io/dissimulate/details/eZxEBO/?ref=wenchie.com">Tearable Cloth v2</a> by <a href="https://codepen.io/dissimulate/?ref=wenchie.com" target="_blank">dissumulate</a>. Most images from <a href="http://unsplash.it/?ref=wenchie.com" target="_blank">unsplash.it</a>.</p>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.1/dat.gui.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/pixi.js/4.4.2/pixi.min.js"></script>
<script>
    

console.clear();

let mesh;
let cloth;
let spacingX = 5;
let spacingY = 5;
let accuracy = 1;

let opts = {
  image: 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/39255/face.png',
  gravity: 200,
  friction: 0.99,
  bounce: 0.3,
  pointsX: 20,
  pointsY: 22,
  renderCloth: true,
  mouseInfluence: 35,
  pinCorners: true,
  randomImage(){
    this.image = 'https://unsplash.it/400/400?image=' + Math.floor(Math.random() * 1100);
    loadTexture();
  }
};


let gui = new dat.GUI();
gui.closed = window.innerWidth < 600;
let renderCloth = gui.add(opts, 'renderCloth');

let image = gui.add(opts, 'image', { 
  Face: 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/39255/face.png', 
  Water: 'https://unsplash.it/400/400?image=1053', 
  YellowCurtain: 'https://unsplash.it/400/400?image=855', 
  Tunnel: 'https://unsplash.it/400/400?image=137'
});
image.onChange(loadTexture);

let random = gui.add(opts, 'randomImage');
let influence = gui.add(opts, 'mouseInfluence', 0, 200).step(1);
let gravity = gui.add(opts, 'gravity', 0, 1000).step(20);
let friction = gui.add(opts, 'friction', 0.5, 1).step(0.005);
let bounce = gui.add(opts, 'bounce', 0, 2).step(0.1);


let pin = gui.add(opts, 'pinCorners');
pin.onChange(loadTexture);

let canvas = document.createElement('canvas');
let ctx = canvas.getContext('2d');
document.body.appendChild(canvas);

ctx.strokeStyle = '#555';

let mouse = {
  
  down: false,
  x: 0,
  y: 0,
  px: 0,
  py: 1
}

/*////////////////////////////////////////*/

let stage = new PIXI.Container();
let renderer = PIXI.autoDetectRenderer(window.innerWidth, window.innerHeight, { transparent: true });

document.body.insertBefore(renderer.view, canvas);
renderer.render(stage);

canvas.width = renderer.width;
canvas.height = renderer.height;


/*////////////////////////////////////////*/

function loadTexture() {
  
  console.log('loading texture', opts.image);
  
  document.body.className = 'loading';

  let texture = new PIXI.Texture.fromImage(opts.image);
  if ( !texture.requiresUpdate ) { texture.update(); }
  
  texture.on('error', function(){ console.error('AGH!'); });

  texture.on('update',function(){
  document.body.className = '';
    
    console.log('texture loaded');

    if ( mesh ) { stage.removeChild(mesh); }
    
    mesh = new PIXI.mesh.Plane( this, opts.pointsX, opts.pointsY);
    mesh.width = this.width;
    mesh.height = this.height;

    spacingX = mesh.width / (opts.pointsX-1);
    spacingY = mesh.height / (opts.pointsY-1);

    cloth = new Cloth(opts.pointsX-1, opts.pointsY-1, !opts.pinCorners);

    stage.addChild(mesh);
  });
}

loadTexture(opts.image);

;(function update() {
  requestAnimationFrame(update);
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  if ( cloth ) { cloth.update(0.016) }
  renderer.render(stage);
})(0)

/*////////////////////////////////////////*/

class Point {
  constructor (x, y) {
    this.x = x
    this.y = y
    this.px = x
    this.py = y
    this.vx = 0
    this.vy = 0
    this.pinX = null
    this.pinY = null

    this.constraints = []
  }

  update (delta) {
    if (this.pinX && this.pinY) return this

    if (mouse.down) {
      let dx = this.x - mouse.x
      let dy = this.y - mouse.y
      let dist = Math.sqrt(dx * dx + dy * dy)

      if (mouse.button === 1 && dist < opts.mouseInfluence) {
        this.px = this.x - (mouse.x - mouse.px)
        this.py = this.y - (mouse.y - mouse.py)
      } else if (dist < mouse.cut) {
        this.constraints = []
      }
    }

    this.addForce(0, opts.gravity)

    let nx = this.x + (this.x - this.px) * opts.friction + this.vx * delta
    let ny = this.y + (this.y - this.py) * opts.friction + this.vy * delta

    this.px = this.x
    this.py = this.y

    this.x = nx
    this.y = ny

    this.vy = this.vx = 0

    if (this.x >= canvas.width) {
      this.px = canvas.width + (canvas.width - this.px) * opts.bounce
      this.x = canvas.width
    } else if (this.x <= 0) {
      this.px *= -1 * opts.bounce
      this.x = 0
    }

    if (this.y >= canvas.height) {
      this.py = canvas.height + (canvas.height - this.py) * opts.bounce
      this.y = canvas.height
    } else if (this.y <= 0) {
      this.py *= -1 * opts.bounce
      this.y = 0
    }

    return this
  }

  draw () {
    let i = this.constraints.length
    while (i--) this.constraints[i].draw()
  }

  resolve () {
    if (this.pinX && this.pinY) {
      this.x = this.pinX
      this.y = this.pinY
      return
    }

    this.constraints.forEach((constraint) => constraint.resolve())
  }

  attach (point) {
    this.constraints.push(new Constraint(this, point))
  }

  free (constraint) {
    this.constraints.splice(this.constraints.indexOf(constraint), 1)
  }

  addForce (x, y) {
    this.vx += x
    this.vy += y
  }

  pin (pinx, piny) {
    this.pinX = pinx
    this.pinY = piny
  }
  
  unpin(){
    this.pinX = null;
    this.pinY = null;
  }
}

/*////////////////////////////////////////*/

class Constraint {
  constructor (p1, p2, length) {
    this.p1 = p1
    this.p2 = p2
    this.length = length || spacingX
  }

  resolve () {
    let dx = this.p1.x - this.p2.x
    let dy = this.p1.y - this.p2.y
    let dist = Math.sqrt(dx * dx + dy * dy)

    if (dist < this.length) return

    let diff = (this.length - dist) / dist

    //if (dist > tearDist) this.p1.free(this)

    let mul = diff * 0.5 * (1 - this.length / dist)

    let px = dx * mul
    let py = dy * mul

    !this.p1.pinX && (this.p1.x += px)
    !this.p1.pinY && (this.p1.y += py)
    !this.p2.pinX && (this.p2.x -= px)
    !this.p2.pinY && (this.p2.y -= py)

    return this
  }

  draw () {
    ctx.moveTo(this.p1.x, this.p1.y)
    ctx.lineTo(this.p2.x, this.p2.y)
  }
}

/*////////////////////////////////////////*/

let count = 0;

class Cloth {
  constructor (clothX, clothY, free) {
    this.points = []

    let startX = canvas.width / 2 - clothX * spacingX / 2;
    let startY = canvas.height * 0.2;

    for (let y = 0; y <= clothY; y++) {
      for (let x = 0; x <= clothX; x++) {
        let point = new Point(
          startX + x * spacingX - (spacingX * Math.sin(y) ), 
          y * spacingY + startY + ( y !== 0 ? 5 * Math.cos(x) : 0 )
        )
        !free && y === 0 && point.pin(point.x, point.y)
        x !== 0 && point.attach(this.points[this.points.length - 1])
        y !== 0 && point.attach(this.points[x + (y - 1) * (clothX + 1)])

        this.points.push(point)
      }
    }
    
  }

  update (delta) {
    let i = accuracy

    while (i--) {
      this.points.forEach((point) => {
        point.resolve()
      })
    }

    ctx.beginPath();

    this.points.forEach((point,i) => {
      point.update(delta * delta)
        
      if ( opts.renderCloth ) { point.draw(); }
      
      if ( mesh ) {
        i *= 2;
        mesh.vertices[i] = point.x;
        mesh.vertices[i+1] = point.y;
      }
    });
    
    ctx.stroke()
  }
}

function pointerMove(e) {
  let pointer = e.touches ? e.touches[0] : e;
  mouse.px = mouse.x || pointer.clientX
  mouse.py = mouse.y || pointer.clientY
  mouse.x = pointer.clientX
  mouse.y = pointer.clientY
}

function pointerDown(e){
  mouse.down = true
  mouse.button = 1
  pointerMove(e);
}

function pointerUp(e){
  mouse.down = false;
  mouse.px = null;
  mouse.py = null;
  console.log('pointer up');
}

document.body.addEventListener('mousedown', pointerDown);
document.body.addEventListener('touchstart', pointerDown);

document.body.addEventListener('mousemove',pointerMove);
document.body.addEventListener('touchmove', pointerMove);

document.body.addEventListener('mouseup', pointerUp);
document.body.addEventListener('touchend', pointerUp);
document.body.addEventListener('mouseleave', pointerUp);
</script><!--kg-card-end: markdown-->]]></content:encoded></item></channel></rss>