<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>JavaScript &#8211; Matthew Petroff</title>
	<atom:link href="https://mpetroff.net/tag/javascript/feed/" rel="self" type="application/rss+xml" />
	<link>https://mpetroff.net</link>
	<description>mpetroff.net</description>
	<lastBuildDate>Sun, 21 Feb 2021 12:30:47 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	
	<item>
		<title>Space-efficient Embedding of WebAssembly in JavaScript</title>
		<link>https://mpetroff.net/2021/02/space-efficient-embedding-of-webassembly-in-javascript/</link>
					<comments>https://mpetroff.net/2021/02/space-efficient-embedding-of-webassembly-in-javascript/#comments</comments>
		
		<dc:creator><![CDATA[Matthew Petroff]]></dc:creator>
		<pubDate>Sat, 20 Feb 2021 15:01:07 +0000</pubDate>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[PNG]]></category>
		<category><![CDATA[WASM]]></category>
		<category><![CDATA[WebAssembly]]></category>
		<guid isPermaLink="false">https://mpetroff.net/?p=3336</guid>

					<description><![CDATA[Recently, I came across a blog post about converting parts of a JavaScript library into WebAssembly. The part that interested me the most was a section about efficiently embedding the WebAssembly binary into the JavaScript code such that the library &#8230; <a href="https://mpetroff.net/2021/02/space-efficient-embedding-of-webassembly-in-javascript/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
										<content:encoded><![CDATA[<p><span class="dropcap">R</span>ecently, I came across a blog post about <a href="https://engineering.q42.nl/webassembly/">converting parts of a JavaScript library into WebAssembly</a>. The part that interested me the most was a section about efficiently embedding the WebAssembly binary into the JavaScript code such that the library could be distributed as a single file, instead of the usual method of providing the WebAssembly binary as a separate file. This is accomplished by Base64-encoding the WebAssembly binary as a string and including the resulting string in the JavaScript file. Unfortunately, this significantly inflates the total file size, since the Base64-encoded string does not compress nearly as well as the original binary. To mitigate this issue, the blog post author had the clever idea of gzip-compressing the binary prior to Base64-encoding it and using the <a href="https://github.com/imaya/zlib.js">zlib.js</a> JavaScript library to decompress the binary client-side, after undoing the Base64-encoding. While this significantly reduced the size of the Base64-encoded WebAssembly binary, it required ~6.5&thinsp;kB for the decompression code, after gzip compression.<sup id="rf1-3336"><a href="https://mpetroff.net/2021/02/space-efficient-embedding-of-webassembly-in-javascript/#fn1-3336" title=" The author mentioned 12&thinsp;kB of decompression code, but that was without gzip-compression of the JavaScript code. " rel="footnote">1</a></sup></p>
<p>While I liked the idea of compressing the WebAssembly binary prior to Base64-encoding it, I thought there must be a way of decompressing it with a smaller decompression code. The simplest change would be to use a raw Deflate-compressed payload instead of one encapsulated with gzip, as the zlib.js library also provides a decompression function for this, which is only ~2.5&thinsp;kB after gzip-compression, saving ~4&thinsp;kB. However, this is still excessive, since it shouldn&#8217;t be necessary to provide a Deflate decompression function as web browsers include such functionality. Although such decompression functionality isn&#8217;t exposed directly to JavaScript, PNG images can be decoded from JavaScript, and PNG images use Deflate compression. Thus, I decided to encode the WebAssembly binary as a grayscale PNG image, Base64-encode the PNG as a data URI, and include the resulting string in the JavaScript file.<span id="more-3336"></span></p>
<p>To encode the binary as a PNG image, the dimensions of the image must first be decided on. For this, I decided to set the image width to the smallest power of two that allowed the image to have a landscape aspect ratio, although this decision was somewhat arbitrary. Each pixel in the grayscale image corresponds to one byte in the WebAssembly binary, starting in the top-left corner of the image and wrapping line-by-line. Any remaining pixels in the last row of the image were set to zero, but this presented a problem, since zero-padding a WebAssembly binary is not allowed. Thus, the first four pixels of the image are used to store the size of the WebAssembly binary as an unsigned 32-bit little-endian integer, which can then be used by the decoder to truncate the image data to the correct length. The resulting PNG image can then be optimized using tools such as <a href="https://github.com/shssoichiro/oxipng">OxiPNG</a> to reduce its file size further, after which the PNG image is Base64-encoded as a data URI.</p>
<p>To decode the Base64-encoded PNG image into WebAssembly from JavaScript, the <code>Image()</code> constructor is used to create an <code>&lt;image&gt;</code> element from the Base64-encoded string. Then, the <code>&lt;image&gt;</code> is drawn to a <code>&lt;canvas&gt;</code> element, and the <code>getImageData()</code> method is used to extract the image data as an array. The array is then filtered to keep only every fourth pixel, to convert the RGBA data to grayscale.<sup id="rf2-3336"><a href="https://mpetroff.net/2021/02/space-efficient-embedding-of-webassembly-in-javascript/#fn2-3336" title=" This step wouldn&#8217;t be necessary for a RGBA PNG, but these don&#8217;t seem to compress WebAssembly binaries quite as well as grayscale PNGs. " rel="footnote">2</a></sup> Next, the first four bytes containing the WebAssembly binary length are decoded, removed from the array, and used to truncate the array to just the WebAssembly binary data. Finally, these data are used to instantiate the WebAssembly code. The decoding routine is &lt;0.3&thinsp;kB after gzip compression.</p>
<p>I have made available a <a href="https://mpetroff.net/files/wasm-compression/">demo</a> that includes an encoding procedure written in Python, the JavaScript decoding procedure, and a live example using MDN&#8217;s <a href="https://github.com/mdn/webassembly-examples/blob/936ab39d0b2f83295966941f7a916c4df65f0f4e/js-api-examples/simple.wasm">simple WebAssembly example</a>. While this demonstrates the technique, it doesn&#8217;t provide a meaningful example of the bandwidth savings. Thus, I also applied the technique to the <a href="https://kripken.github.io/ammo.js/examples/webgl_demo/ammo.wasm.html">ammo.js WebAssembly demo</a>. For this example, the original WebAssembly binary is ~651&thinsp;kB uncompressed, ~252&thinsp;kB when compressed with <code>gzip -9</code>, and ~218&thinsp;kB when compressed with <code>brotli -9</code>. As a Base64-encoded string, it is ~880&thinsp;kB, which is reduced to ~358&thinsp;kB when compressed with <code>gzip -9</code> or ~316&thinsp;kB when compressed with <code>brotli -9</code>. When converted to a PNG image using the technique described in this blog post and optimized using OxiPNG, the resulting image is ~242&thinsp;kB. When converted to a Base64-encoded data URI, it is ~322&thinsp;kB, which is reduced to ~244&thinsp;kB when compressed with <code>gzip -9</code> or ~242&thinsp;kB when compressed with <code>brotli -9</code>. While not quite as small as the Brotli-compressed binary, the technique described in this blog post does better than the gzip-compressed binary and does much better than naively include a Base64-encoded binary in JavaScript.</p>
<hr class="footnotes"><ol class="footnotes" style="list-style-type:decimal"><li id="fn1-3336"><p > The author mentioned 12&thinsp;kB of decompression code, but that was without gzip-compression of the JavaScript code. &nbsp;<a href="https://mpetroff.net/2021/02/space-efficient-embedding-of-webassembly-in-javascript/#rf1-3336" class="backlink" title="Return to footnote 1.">&#8617;</a></p></li><li id="fn2-3336"><p > This step wouldn&#8217;t be necessary for a RGBA PNG, but these don&#8217;t seem to compress WebAssembly binaries quite as well as grayscale PNGs. &nbsp;<a href="https://mpetroff.net/2021/02/space-efficient-embedding-of-webassembly-in-javascript/#rf2-3336" class="backlink" title="Return to footnote 2.">&#8617;</a></p></li></ol>]]></content:encoded>
					
					<wfw:commentRss>https://mpetroff.net/2021/02/space-efficient-embedding-of-webassembly-in-javascript/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>Pannellum 2.5</title>
		<link>https://mpetroff.net/2019/07/pannellum-2-5/</link>
					<comments>https://mpetroff.net/2019/07/pannellum-2-5/#comments</comments>
		
		<dc:creator><![CDATA[Matthew Petroff]]></dc:creator>
		<pubDate>Sun, 14 Jul 2019 01:58:28 +0000</pubDate>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[library]]></category>
		<category><![CDATA[pannellum]]></category>
		<category><![CDATA[panorama]]></category>
		<guid isPermaLink="false">https://mpetroff.net/?p=2962</guid>

					<description><![CDATA[Pannellum 2.5 has now been released. As with Pannellum 2.4, this was a rather incremental release. The most noteworthy change is that equirectangular panoramas will now be automatically split into two textures if too big for a given device, which &#8230; <a href="https://mpetroff.net/2019/07/pannellum-2-5/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
										<content:encoded><![CDATA[<p><span class="dropcap">P</span>annellum 2.5 has now <a href="https://github.com/mpetroff/pannellum/releases/tag/2.5.2">been released</a>. As with <a href="https://mpetroff.net/2018/01/pannellum-2-4/">Pannellum 2.4</a>, this was a rather incremental release. The most noteworthy change is that equirectangular panoramas will now be automatically split into two textures if too big for a given device, which means images up to 8192&thinsp;px across, covering all consumer panoramic cameras, now have widespread support. There has also been a significant improvement of the rendering quality on certain mobile devices (the fragment shaders now use <code>highp</code> precision), and support for partial panoramas has improved. Finally, there are an assortment of more minor improvements and bug fixes. See the <a href="https://github.com/mpetroff/pannellum/blob/2.5.2/changelog.md">changelog</a> for full details. Pannellum also now has a <a href="https://doi.org/10.5281/zenodo.3334433">Zenodo DOI</a> (and a specific DOI for each new release).</p>
]]></content:encoded>
					
					<wfw:commentRss>https://mpetroff.net/2019/07/pannellum-2-5/feed/</wfw:commentRss>
			<slash:comments>21</slash:comments>
		
		
			</item>
		<item>
		<title>Pannellum 2.4</title>
		<link>https://mpetroff.net/2018/01/pannellum-2-4/</link>
					<comments>https://mpetroff.net/2018/01/pannellum-2-4/#comments</comments>
		
		<dc:creator><![CDATA[Matthew Petroff]]></dc:creator>
		<pubDate>Wed, 31 Jan 2018 21:14:03 +0000</pubDate>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[library]]></category>
		<category><![CDATA[pannellum]]></category>
		<category><![CDATA[panorama]]></category>
		<guid isPermaLink="false">https://mpetroff.net/?p=2597</guid>

					<description><![CDATA[Yesterday, I released Pannellum 2.4.0. It doesn&#8217;t contain any major new features, although it does finally include translation support, which was an often requested feature. Also included are numerous minor improvements, a few new API functions, and quite a few &#8230; <a href="https://mpetroff.net/2018/01/pannellum-2-4/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
										<content:encoded><![CDATA[<p><span class="dropcap">Y</span>esterday, I released <a href="https://github.com/mpetroff/pannellum/releases/tag/2.4.0">Pannellum 2.4.0</a>. It doesn&#8217;t contain any major new features, although it does finally include translation support, which was an often requested feature. Also included are numerous minor improvements, a few new API functions, and quite a few bug fixes; see the <a href="https://github.com/mpetroff/pannellum/blob/2.4.0/changelog.md">changelog</a> for full details. It had been more than a year since the last release&mdash;and I&#8217;ve been meaning to create a new release for a few months&mdash;so it was high time for a new release.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://mpetroff.net/2018/01/pannellum-2-4/feed/</wfw:commentRss>
			<slash:comments>10</slash:comments>
		
		
			</item>
		<item>
		<title>Bootstrap Navbar without jQuery</title>
		<link>https://mpetroff.net/2015/03/bootstrap-navbar-without-jquery/</link>
					<comments>https://mpetroff.net/2015/03/bootstrap-navbar-without-jquery/#comments</comments>
		
		<dc:creator><![CDATA[Matthew Petroff]]></dc:creator>
		<pubDate>Sat, 28 Mar 2015 02:47:00 +0000</pubDate>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Bootstrap]]></category>
		<category><![CDATA[html]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[jQuery]]></category>
		<guid isPermaLink="false">http://mpetroff.net/?p=1772</guid>

					<description><![CDATA[While I&#8217;m fond of Bootstrap in general, I dislike how much it relies on JavaScript and particularly dislike its dependency on jQuery. I have nothing against using JavaScript for creating interactive content, e.g. web applications, or progressive enhancement, e.g. lightboxes &#8230; <a href="https://mpetroff.net/2015/03/bootstrap-navbar-without-jquery/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
										<content:encoded><![CDATA[<p><span class="dropcap">W</span>hile I&#8217;m fond of <a href="http://getbootstrap.com/">Bootstrap</a> in general, I dislike how much it relies on JavaScript and particularly dislike its dependency on jQuery. I have nothing against using JavaScript for creating interactive content, e.g. web applications, or progressive enhancement, e.g. lightboxes or copying text to the clipboard, but I do take issue with requiring JavaScript for basic site functionality as is the case with Bootstrap&#8217;s navbar and dropdowns. What I particularly dislike is that not only does this require JavaScript, but it requires a 32kB JavaScript library, jQuery, to do what would otherwise take less than 1kB of JavaScript. To avoid this when <a href="/2014/01/camp-workcoeman-website-redesign/" title="Camp Workcoeman Website Redesign">redesigning campworkcoeman.org</a>, I made some modifications to open the navbar&#8217;s dropdowns on hover on larger devices and used a small piece of plain JavaScript to operate the menu on small, mobile devices. Unfortunately, this didn&#8217;t work properly under iOS, but I recently fixed this. I&#8217;m detailing the changes I made in case someone finds them useful.</p>
<p><span id="more-1772"></span></p>
<p>As far as HTML goes, the only change is using <code>&lt;div&gt;</code> elements instead of <code>&lt;a&gt;</code> elements for the <code>dropdown-toggle</code> class. In the CSS, a media query is used to open the dropdowns on hover on larger devices, and some code is added to restore the appearance of the dropdowns due to the HTML change.</p>
<div class="highlighted-source default css">
<pre><span></span><span class="c">/*</span>
<span class="c"> * Open dropdowns on hover instead of click.</span>
<span class="c"> */</span><span class="w"></span>
<span class="p">@</span><span class="k">media</span><span class="w"> </span><span class="o">(</span><span class="nt">min-width</span><span class="o">:</span><span class="w"> </span><span class="nt">768px</span><span class="o">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">  </span><span class="p">.</span><span class="nc">dropdown</span><span class="p">:</span><span class="nd">hover</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">background</span><span class="p">:</span><span class="w"> </span><span class="mh">#e7e7e7</span><span class="p">;</span><span class="w"></span>
<span class="w">  </span><span class="p">}</span><span class="w"></span>
<span class="w">  </span><span class="p">.</span><span class="nc">dropdown</span><span class="p">:</span><span class="nd">hover</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="p">.</span><span class="nc">dropdown-menu</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">display</span><span class="p">:</span><span class="w"> </span><span class="kc">block</span><span class="p">;</span><span class="w"></span>
<span class="w">  </span><span class="p">}</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="c">/*</span>
<span class="c"> * The following is needed since the dropdowns are &lt;div&gt;</span>
<span class="c"> * elements instead of &lt;a&gt; elements</span>
<span class="c"> */</span><span class="w"></span>
<span class="p">.</span><span class="nc">nav</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="nt">li</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="nt">div</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">  </span><span class="k">position</span><span class="p">:</span><span class="w"> </span><span class="kc">relative</span><span class="p">;</span><span class="w"></span>
<span class="w">  </span><span class="k">display</span><span class="p">:</span><span class="w"> </span><span class="kc">block</span><span class="p">;</span><span class="w"></span>
<span class="w">  </span><span class="k">padding</span><span class="p">:</span><span class="w"> </span><span class="mi">10</span><span class="kt">px</span><span class="w"> </span><span class="mi">15</span><span class="kt">px</span><span class="p">;</span><span class="w"></span>
<span class="w">  </span><span class="k">cursor</span><span class="p">:</span><span class="w"> </span><span class="kc">default</span><span class="p">;</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
<span class="p">.</span><span class="nc">navbar-nav</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="nt">li</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="nt">div</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">  </span><span class="k">padding-top</span><span class="p">:</span><span class="w"> </span><span class="mi">15</span><span class="kt">px</span><span class="p">;</span><span class="w"></span>
<span class="w">  </span><span class="k">padding-bottom</span><span class="p">:</span><span class="w"> </span><span class="mi">15</span><span class="kt">px</span><span class="p">;</span><span class="w"></span>
<span class="w">  </span><span class="k">line-height</span><span class="p">:</span><span class="w"> </span><span class="mi">20</span><span class="kt">px</span><span class="p">;</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
<span class="p">.</span><span class="nc">navbar-default</span><span class="w"> </span><span class="p">.</span><span class="nc">navbar-nav</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="nt">li</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="nt">div</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">  </span><span class="k">color</span><span class="p">:</span><span class="w"> </span><span class="nb">rgb</span><span class="p">(</span><span class="mi">119</span><span class="p">,</span><span class="w"> </span><span class="mi">119</span><span class="p">,</span><span class="w"> </span><span class="mi">119</span><span class="p">);</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
<span class="p">.</span><span class="nc">navbar-collapse</span><span class="p">.</span><span class="nc">collapse</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">  </span><span class="k">display</span><span class="p">:</span><span class="w"> </span><span class="kc">none</span><span class="p">;</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
<span class="p">.</span><span class="nc">dropdown-open</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">  </span><span class="k">background</span><span class="p">:</span><span class="w"> </span><span class="mh">#e7e7e7</span><span class="p">;</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</pre>
</div>
<p>Finally, a small script replaces the default Bootstrap <code>bootstrap.js</code> version.</p>
<div class="highlighted-source default javascript">
<pre><span></span><span class="c1">// Navbar and dropdowns</span>
<span class="kd">var</span> <span class="nx">toggle</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementsByClassName</span><span class="p">(</span><span class="s1">&#39;navbar-toggle&#39;</span><span class="p">)[</span><span class="mf">0</span><span class="p">],</span>
    <span class="nx">collapse</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementsByClassName</span><span class="p">(</span><span class="s1">&#39;navbar-collapse&#39;</span><span class="p">)[</span><span class="mf">0</span><span class="p">],</span>
    <span class="nx">dropdowns</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementsByClassName</span><span class="p">(</span><span class="s1">&#39;dropdown&#39;</span><span class="p">);;</span>

<span class="c1">// Toggle if navbar menu is open or closed</span>
<span class="kd">function</span> <span class="nx">toggleMenu</span><span class="p">()</span> <span class="p">{</span>
    <span class="nx">collapse</span><span class="p">.</span><span class="nx">classList</span><span class="p">.</span><span class="nx">toggle</span><span class="p">(</span><span class="s1">&#39;collapse&#39;</span><span class="p">);</span>
    <span class="nx">collapse</span><span class="p">.</span><span class="nx">classList</span><span class="p">.</span><span class="nx">toggle</span><span class="p">(</span><span class="s1">&#39;in&#39;</span><span class="p">);</span>
<span class="p">}</span>

<span class="c1">// Close all dropdown menus</span>
<span class="kd">function</span> <span class="nx">closeMenus</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">j</span> <span class="o">=</span> <span class="mf">0</span><span class="p">;</span> <span class="nx">j</span> <span class="o">&lt;</span> <span class="nx">dropdowns</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">j</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="nx">dropdowns</span><span class="p">[</span><span class="nx">j</span><span class="p">].</span><span class="nx">getElementsByClassName</span><span class="p">(</span><span class="s1">&#39;dropdown-toggle&#39;</span><span class="p">)[</span><span class="mf">0</span><span class="p">].</span><span class="nx">classList</span><span class="p">.</span><span class="nx">remove</span><span class="p">(</span><span class="s1">&#39;dropdown-open&#39;</span><span class="p">);</span>
        <span class="nx">dropdowns</span><span class="p">[</span><span class="nx">j</span><span class="p">].</span><span class="nx">classList</span><span class="p">.</span><span class="nx">remove</span><span class="p">(</span><span class="s1">&#39;open&#39;</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="c1">// Add click handling to dropdowns</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="mf">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="nx">dropdowns</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">dropdowns</span><span class="p">[</span><span class="nx">i</span><span class="p">].</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">&#39;click&#39;</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="nb">document</span><span class="p">.</span><span class="nx">body</span><span class="p">.</span><span class="nx">clientWidth</span> <span class="o">&lt;</span> <span class="mf">768</span><span class="p">)</span> <span class="p">{</span>
            <span class="kd">var</span> <span class="nx">open</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">classList</span><span class="p">.</span><span class="nx">contains</span><span class="p">(</span><span class="s1">&#39;open&#39;</span><span class="p">);</span>
            <span class="nx">closeMenus</span><span class="p">();</span>
            <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">open</span><span class="p">)</span> <span class="p">{</span>
                <span class="k">this</span><span class="p">.</span><span class="nx">getElementsByClassName</span><span class="p">(</span><span class="s1">&#39;dropdown-toggle&#39;</span><span class="p">)[</span><span class="mf">0</span><span class="p">].</span><span class="nx">classList</span><span class="p">.</span><span class="nx">toggle</span><span class="p">(</span><span class="s1">&#39;dropdown-open&#39;</span><span class="p">);</span>
                <span class="k">this</span><span class="p">.</span><span class="nx">classList</span><span class="p">.</span><span class="nx">toggle</span><span class="p">(</span><span class="s1">&#39;open&#39;</span><span class="p">);</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">});</span>
<span class="p">}</span>

<span class="c1">// Close dropdowns when screen becomes big enough to switch to open by hover</span>
<span class="kd">function</span> <span class="nx">closeMenusOnResize</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="nb">document</span><span class="p">.</span><span class="nx">body</span><span class="p">.</span><span class="nx">clientWidth</span> <span class="o">&gt;=</span> <span class="mf">768</span><span class="p">)</span> <span class="p">{</span>
        <span class="nx">closeMenus</span><span class="p">();</span>
        <span class="nx">collapse</span><span class="p">.</span><span class="nx">classList</span><span class="p">.</span><span class="nx">add</span><span class="p">(</span><span class="s1">&#39;collapse&#39;</span><span class="p">);</span>
        <span class="nx">collapse</span><span class="p">.</span><span class="nx">classList</span><span class="p">.</span><span class="nx">remove</span><span class="p">(</span><span class="s1">&#39;in&#39;</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="c1">// Event listeners</span>
<span class="nb">window</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">&#39;resize&#39;</span><span class="p">,</span> <span class="nx">closeMenusOnResize</span><span class="p">,</span> <span class="kc">false</span><span class="p">);</span>
<span class="nx">toggle</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">&#39;click&#39;</span><span class="p">,</span> <span class="nx">toggleMenu</span><span class="p">,</span> <span class="kc">false</span><span class="p">);</span>
</pre>
</div>
<p>I modified Bootstrap&#8217;s navbar example to <a href="http://bl.ocks.org/mpetroff/4666657beeb85754611f">demonstrate this</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://mpetroff.net/2015/03/bootstrap-navbar-without-jquery/feed/</wfw:commentRss>
			<slash:comments>8</slash:comments>
		
		
			</item>
		<item>
		<title>Print Maps</title>
		<link>https://mpetroff.net/2015/02/print-maps/</link>
					<comments>https://mpetroff.net/2015/02/print-maps/#comments</comments>
		
		<dc:creator><![CDATA[Matthew Petroff]]></dc:creator>
		<pubDate>Mon, 09 Feb 2015 00:19:35 +0000</pubDate>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[map]]></category>
		<category><![CDATA[OpenStreetMap]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[WebGL]]></category>
		<guid isPermaLink="false">http://mpetroff.net/?p=1760</guid>

					<description><![CDATA[Web maps, e.g. Google Maps, normally don&#8217;t print well, as their resolution is much lower than normal print resolution, not to mention the various other unwanted text and elements that print along with the map. While the unwanted elements can &#8230; <a href="https://mpetroff.net/2015/02/print-maps/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
										<content:encoded><![CDATA[<p><span class="dropcap">W</span>eb maps, e.g. Google Maps, normally don&#8217;t print well, as their resolution is much lower than normal print resolution, not to mention the various other unwanted text and elements that print along with the map. While the unwanted elements can be cropped out, the only fix for the low resolution is to render a higher resolution image (or use vectors). Formerly, this required installing GIS software, which also requires a suitable data source. <a href="https://printmaps.mpetroff.net/">Print Maps</a> changes that by leveraging <a href="https://github.com/mapbox/mapbox-gl-js">Mapbox GL JS</a> and <a href="https://openstreetmap.org/">OpenStreetMap</a> data to render print resolution maps in the browser. After the user selects the map size, zoom, location, style, resolution, and output format, PNG or PDF, Mapbox GL JS is configured as if it was being used on a very high pixel density display and used to render the map output. To use Print Maps, visit <a href="https://printmaps.mpetroff.net">printmaps.mpetroff.net</a>.</p>
<p><a href="https://cdn0.mpetroff.net/wp-content/uploads/2015/02/print-maps.png" title="Print Maps" data-sbox="1760"><img decoding="async" class="aligncenter wp-image-1761 size-large" title="Print Maps" src="https://cdn0.mpetroff.net/wp-content/uploads/2015/02/print-maps-640x736.png" alt="Print Maps" width="640" height="736" srcset="https://cdn0.mpetroff.net/wp-content/uploads/2015/02/print-maps-640x736.png 640w, https://cdn0.mpetroff.net/wp-content/uploads/2015/02/print-maps-261x300.png 261w, https://cdn0.mpetroff.net/wp-content/uploads/2015/02/print-maps.png 800w" sizes="(max-width: 640px) 100vw, 640px" /></a>The site&#8217;s source code is available on <a href="https://github.com/mpetroff/print-maps">GitHub</a>. Also, <a href="https://speakerdeck.com/mpetroff/hophacks-spring-2015">slides</a> from my HopHacks presentation on the project.</p>
<p>Edit (2017-01-01): Changed printmaps.org to printmaps.mpetroff.net as I&#8217;m going to let the printmaps.org domain registration expire.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://mpetroff.net/2015/02/print-maps/feed/</wfw:commentRss>
			<slash:comments>7</slash:comments>
		
		
			</item>
	</channel>
</rss>

<!--
Performance optimized by W3 Total Cache. Learn more: https://www.boldgrid.com/w3-total-cache/?utm_source=w3tc&utm_medium=footer_comment&utm_campaign=free_plugin

Page Caching using Disk: Enhanced 
Content Delivery Network via Amazon Web Services: CloudFront: cdn0.mpetroff.net
Minified using Disk

Served from: mpetroff.net @ 2026-03-28 17:59:48 by W3 Total Cache
-->