<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <link href="https://friendlybit.com/feed/" rel="self" type="application/atom+xml" />
    <link href="https://friendlybit.com/" rel="alternate" type="text/html" />
    <updated>2026-06-21T12:00:00+02:00</updated>
    <id>https://friendlybit.com</id>
    <title type="html">Friendly Bit - Web development blog</title>
    <subtitle>Friendly Bit is a blog by Emil Stenström, a Swedish web developer that occasionally gets ideas of how to improve the internet.</subtitle>
    
        <entry>
            <title type="html">JustHTML 3.0.0: A new HTML5 parser architecture</title>
            <link href="http://friendlybit.com/python/justhtml-3-parser-architecture/" rel="alternate" type="text/html" title="JustHTML 3.0.0: A new HTML5 parser architecture" />
            <published>2026-06-21T12:00:00+02:00</published>
            <updated>2026-06-21T12:00:00+02:00</updated>
            <id>http://friendlybit.com/python/justhtml-3-parser-architecture/</id>
            <author>
                <name>Emil Stenström</name>
            </author>
            <summary type="text">JustHTML 3.0.0 is out, and the biggest change is not a new API. It&#39;s a new parser core. Up until now, JustHTML looked like most HTML5 parsers. First...</summary>
            <content type="html" xml:base="http://friendlybit.com/python/justhtml-3-parser-architecture/">
                &lt;p&gt;JustHTML 3.0.0 is out, and the biggest change is not a new API. It&#39;s a new parser core.&lt;/p&gt;
&lt;p&gt;Up until now, JustHTML looked like most HTML5 parsers. First tokenize the input, then feed those tokens into a tree builder, and only after that apply the default-safe cleanup that makes untrusted HTML usable in applications.&lt;/p&gt;
&lt;p&gt;That&#39;s the normal structure. The HTML5 spec itself is written that way. The tokenizer is one state machine, the tree builder is another, and the boundary between them is a stream of tokens: start tags, end tags, text, comments, doctypes, parse errors.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;html5lib&lt;/code&gt;, browser engines, and &lt;code&gt;html5ever&lt;/code&gt; all broadly follow that shape, even if the details differ a lot.&lt;/p&gt;
&lt;h2 id=&#34;what-changed-in-300&#34;&gt;What changed in 3.0.0&lt;a href=&#34;#what-changed-in-300&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;JustHTML 3.0.0 collapses that split into one plan-driven parser engine.&lt;/p&gt;
&lt;p&gt;So instead of scanning characters into token objects, handing those tokens to a second subsystem, and then applying sanitizer decisions as a later pass, the new engine does that work in one loop.&lt;/p&gt;
&lt;p&gt;It still implements the same HTML5 concepts: insertion modes, the open-element stack, active formatting elements, foster parenting, fragment parsing, RAWTEXT/RCDATA handling, foreign content rules, and all the other painful details that make browser parsing browser parsing.&lt;/p&gt;
&lt;p&gt;But the control flow is different now. The parser scans the source string directly, decides what the current tag means in context, mutates the DOM immediately, and can apply default-safe policy decisions while it is still in the hot path.&lt;/p&gt;
&lt;p&gt;This is a real architecture change, not just another round of optimization.&lt;/p&gt;
&lt;h2 id=&#34;how-it-works&#34;&gt;How it works&lt;a href=&#34;#how-it-works&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The key idea is the word &amp;quot;plan&amp;quot;.&lt;/p&gt;
&lt;p&gt;Before parsing starts, JustHTML compiles the requested behavior into an &lt;code&gt;EnginePlan&lt;/code&gt;. There are different plans for the common cases:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the default safe path&lt;/li&gt;
&lt;li&gt;custom sanitization policies that can be compiled into parser actions&lt;/li&gt;
&lt;li&gt;the raw path used by &lt;code&gt;sanitize=False&lt;/code&gt; and transform-heavy cases&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That plan contains the parser-time decisions that used to be scattered across later steps: tag actions, allowed tags, attribute handling, URL policy hooks, void-element knowledge, formatting-element behavior, and other mode-specific tables.&lt;/p&gt;
&lt;p&gt;So the hot path is no longer asking &amp;quot;what should I do with this node later?&amp;quot; It already knows.&lt;/p&gt;
&lt;p&gt;In practice the engine now looks more like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34; data-language=&#34;PYTHON&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;plan&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;compile_default_engine_plan&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fragment&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;False&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;n&#34;&gt;engine&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ParseEngine&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;html&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fragment&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;False&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;plan&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;plan&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;n&#34;&gt;root&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;engine&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;parse&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Inside &lt;code&gt;parse()&lt;/code&gt;, the engine sets up either a document shell or fragment root, then walks the input with a single range parser. On the fast path it uses specialized start-tag and end-tag parsers for compiled-safe mode, so it avoids building generic token objects and skips the tokenizer-to-treebuilder handoff completely.&lt;/p&gt;
&lt;p&gt;Attributes are handled differently too. In the old shape, a tokenizer typically parses all attributes into token payloads, and then the tree builder or sanitizer revisits them. In the new JustHTML engine, attribute scanning can be projected directly through the current plan: preserve what is needed, drop what is not, and keep only the state required for correct tree construction.&lt;/p&gt;
&lt;p&gt;That last part matters. HTML parsing is not just &amp;quot;keep the allowed attrs&amp;quot;. Some information is needed for parser state even if it will never survive serialization.&lt;/p&gt;
&lt;h2 id=&#34;why-this-is-faster&#34;&gt;Why this is faster&lt;a href=&#34;#why-this-is-faster&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The 3.0.0 changelog reports about a &lt;strong&gt;2x speedup&lt;/strong&gt;, and the reason is not very mysterious.&lt;/p&gt;
&lt;p&gt;Traditional parser structure pays several overhead costs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;token objects have to be allocated&lt;/li&gt;
&lt;li&gt;token payloads have to be normalized and handed off&lt;/li&gt;
&lt;li&gt;the tree builder has to re-interpret information the tokenizer already discovered&lt;/li&gt;
&lt;li&gt;default-safe behavior often becomes a separate tree walk or transform stage&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The fused engine removes a lot of that machinery from the common path.&lt;/p&gt;
&lt;p&gt;When JustHTML is used in its default mode, the parser can scan characters, recognize a tag, decide whether that tag is allowed, project the interesting attributes, and mutate the DOM immediately. Less indirection, fewer temporary objects, fewer full-tree passes.&lt;/p&gt;
&lt;p&gt;This is the kind of optimization that sounds boring until you remember it&#39;s happening in Python, where object churn and extra passes cost real time.&lt;/p&gt;
&lt;h2 id=&#34;the-comparison-to-other-parsers&#34;&gt;The comparison to other parsers&lt;a href=&#34;#the-comparison-to-other-parsers&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I still think the standard architecture is the safest place to start.&lt;/p&gt;
&lt;p&gt;If you are implementing HTML5 from scratch, tokenizer and tree builder as separate layers is easier to reason about, easier to debug, and closer to the specification. It is also friendlier to test harnesses that want to inspect intermediate token streams.&lt;/p&gt;
&lt;p&gt;So I don&#39;t think this proves everyone else wrong. &lt;code&gt;html5ever&lt;/code&gt; and browser parsers are structured the classic way because that structure maps well to the spec and to large codebases with many contributors.&lt;/p&gt;
&lt;p&gt;What JustHTML 3.0.0 changes is the tradeoff. It keeps the browser-style recovery model, but stops treating token emission as a required architectural boundary.&lt;/p&gt;
&lt;p&gt;That makes JustHTML a bit unusual among HTML parsers. It is still pure Python, still targets exact HTML5 behavior, and still does safe-by-default parsing for application use. But the parser core is now closer to a fused execution engine than a textbook tokenizer plus tree builder pipeline.&lt;/p&gt;
&lt;p&gt;I also think this is a better fit for what JustHTML actually is. Most users are not consuming a token stream. They want a correct DOM tree, and often they want it sanitized. If that is the real product, it makes more sense to optimize around that end-to-end job than around intermediate artifacts.&lt;/p&gt;
&lt;h2 id=&#34;what-did-not-change&#34;&gt;What did not change&lt;a href=&#34;#what-did-not-change&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;From the outside, this release is pleasantly boring.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;JustHTML(html)&lt;/code&gt; still gives you a DOM. Fragment parsing still works. Streaming, source-location tracking, strict mode, and safe-by-default behavior are still there.&lt;/p&gt;
&lt;p&gt;The main breaking change is diagnostics. Since the old tokenizer/tree-builder internals are gone, &lt;code&gt;collect_errors=True&lt;/code&gt; and &lt;code&gt;strict=True&lt;/code&gt; now report a smaller, more intentional built-in error set. If you depended on exact error codes, counts, or ordering, you will need to adjust.&lt;/p&gt;
&lt;p&gt;Everyone else should mostly experience 3.0.0 as &amp;quot;the same parser, only faster&amp;quot;.&lt;/p&gt;
&lt;p&gt;That&#39;s exactly what I wanted.&lt;/p&gt;

            </content>
        </entry>
    
        <entry>
            <title type="html">JustHTML is now safe-by-default</title>
            <link href="http://friendlybit.com/python/justhtml-sanitization/" rel="alternate" type="text/html" title="JustHTML is now safe-by-default" />
            <published>2025-12-28T12:00:00+01:00</published>
            <updated>2025-12-28T12:00:00+01:00</updated>
            <id>http://friendlybit.com/python/justhtml-sanitization/</id>
            <author>
                <name>Emil Stenström</name>
            </author>
            <summary type="text">If you accept HTML from users (comments, profiles, CMS fields), you eventually hit the same problem: You want to keep some markup. You really don’t want to...</summary>
            <content type="html" xml:base="http://friendlybit.com/python/justhtml-sanitization/">
                &lt;p&gt;If you accept HTML from users (comments, profiles, CMS fields), you eventually hit the same problem:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You want to keep &lt;em&gt;some&lt;/em&gt; markup.&lt;/li&gt;
&lt;li&gt;You really don’t want to ship an &lt;a href=&#34;https://learn.snyk.io/lesson/xss/?ecosystem=javascript&#34;&gt;XSS&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That’s why JustHTML now includes a built-in, policy-driven HTML sanitizer, and why serialization is &lt;strong&gt;safe-by-default&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&#34;safe-by-default-serialization&#34;&gt;Safe-by-default serialization&lt;a href=&#34;#safe-by-default-serialization&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;JustHTML sanitizes when you serialize to HTML or Markdown:&lt;/p&gt;
&lt;div class=&#34;highlight&#34; data-language=&#34;PYTHON&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;kn&#34;&gt;from&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;justhtml&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;JustHTML&lt;/span&gt;

&lt;span class=&#34;n&#34;&gt;user_html&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
    &lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;lt;p&amp;gt;Hello &amp;lt;b&amp;gt;world&amp;lt;/b&amp;gt; &amp;lt;script&amp;gt;alert(1)&amp;lt;/script&amp;gt; &amp;#39;&lt;/span&gt;
    &lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;lt;a href=&amp;quot;javascript:alert(1)&amp;quot;&amp;gt;bad&amp;lt;/a&amp;gt; &amp;#39;&lt;/span&gt;
    &lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;lt;a href=&amp;quot;https://example.com/?a=1&amp;amp;b=2&amp;quot;&amp;gt;ok&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&amp;#39;&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;

&lt;span class=&#34;n&#34;&gt;doc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;JustHTML&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;user_html&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fragment&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;True&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;

&lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;doc&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;to_html&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;
&lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;doc&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;to_markdown&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This drops &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; and strips dangerous URLs:&lt;/p&gt;
&lt;div class=&#34;highlight&#34; data-language=&#34;HTML&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;Hello &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;world&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;bad&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;href&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;quot;https://example.com/?a=1&amp;amp;amp;b=2&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;ok&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&#34;highlight&#34; data-language=&#34;MARKDOWN&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;Hello &lt;span class=&#34;gs&#34;&gt;**world**&lt;/span&gt; [bad] [&lt;span class=&#34;nt&#34;&gt;ok&lt;/span&gt;](&lt;span class=&#34;na&#34;&gt;https://example.com/?a=1&amp;amp;b=2&lt;/span&gt;)
&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&#34;turning-it-off-trusted-input-only&#34;&gt;Turning it off (trusted input only)&lt;a href=&#34;#turning-it-off-trusted-input-only&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If the input is trusted and you want raw output, you can opt out:&lt;/p&gt;
&lt;div class=&#34;highlight&#34; data-language=&#34;PYTHON&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;doc&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;to_html&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;safe&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;False&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
&lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;doc&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;to_markdown&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;safe&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;False&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&#34;custom-allowlist-policies&#34;&gt;Custom allowlist policies&lt;a href=&#34;#custom-allowlist-policies&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The default policy is intentionally conservative, but you can provide your own &lt;code&gt;SanitizationPolicy&lt;/code&gt;.
Here’s a small example that only allows &lt;code&gt;p&lt;/code&gt;, &lt;code&gt;b&lt;/code&gt;, and &lt;code&gt;a[href]&lt;/code&gt;, and only allows &lt;code&gt;https&lt;/code&gt; links:&lt;/p&gt;
&lt;div class=&#34;highlight&#34; data-language=&#34;PYTHON&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;kn&#34;&gt;from&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;justhtml&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;JustHTML&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SanitizationPolicy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;UrlRule&lt;/span&gt;

&lt;span class=&#34;n&#34;&gt;policy&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SanitizationPolicy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
    &lt;span class=&#34;n&#34;&gt;allowed_tags&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;p&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;quot;b&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;quot;a&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
    &lt;span class=&#34;n&#34;&gt;allowed_attributes&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;*&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[],&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;quot;a&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;href&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]},&lt;/span&gt;
    &lt;span class=&#34;n&#34;&gt;url_rules&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;a&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;quot;href&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;UrlRule&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;allowed_schemes&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;https&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]),&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;

&lt;span class=&#34;n&#34;&gt;doc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;JustHTML&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;user_html&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fragment&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;True&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;doc&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;to_html&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;policy&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;policy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you’re sanitizing a full document, safe serialization keeps &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;, and &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; wrappers.
For snippets, pass &lt;code&gt;fragment=True&lt;/code&gt; to avoid implicit document wrappers.&lt;/p&gt;
&lt;p&gt;There are also a couple of knobs that tend to show up in real systems:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;URL proxying (for example, rewriting &lt;code&gt;https://example.com/…&lt;/code&gt; to &lt;code&gt;/proxy?url=…&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Optional inline styles, with an allowlist of CSS properties and conservative value checks&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;why-i-built-codejusthtml-xss-benchcode&#34;&gt;Why I built &lt;code&gt;justhtml-xss-bench&lt;/code&gt;&lt;a href=&#34;#why-i-built-codejusthtml-xss-benchcode&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you’ve worked on sanitizers before, you know the hard part isn’t writing a policy — it’s knowing what the browser will actually do with the result.&lt;/p&gt;
&lt;p&gt;So I built a tiny benchmark harness: &lt;code&gt;[justhtml-xss-bench](https://github.com/EmilStenstrom/justhtml-xss-bench/)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;What it does:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Takes a payload vector and a sanitizer.&lt;/li&gt;
&lt;li&gt;Sanitizes the payload.&lt;/li&gt;
&lt;li&gt;Embeds the sanitized output into the initial HTML page (&amp;quot;server-side&amp;quot; style).&lt;/li&gt;
&lt;li&gt;Loads it in a real Playwright browser engine.&lt;/li&gt;
&lt;li&gt;Fails the case if JavaScript executes (including signals like dialogs or attempted external script fetches).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It ships with &lt;strong&gt;7,000+ real-world XSS vectors&lt;/strong&gt; and can be used to compare JustHTML’s output with other sanitizers.&lt;/p&gt;
&lt;p&gt;If you want to explore it locally, the CLI looks like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34; data-language=&#34;BASH&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# Run all vector files in ./vectors against the default sanitizer set&lt;/span&gt;
xssbench

&lt;span class=&#34;c1&#34;&gt;# Limit to one engine&lt;/span&gt;
xssbench&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--browser&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;chromium

&lt;span class=&#34;c1&#34;&gt;# List available sanitizers&lt;/span&gt;
xssbench&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--list-sanitizers

&lt;span class=&#34;c1&#34;&gt;# Run a subset&lt;/span&gt;
xssbench&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--vectors&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;vectors/bleach.json&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--sanitizers&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;noop
&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&#34;threat-model-what-safe-means&#34;&gt;Threat model (what “safe” means)&lt;a href=&#34;#threat-model-what-safe-means&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;JustHTML’s sanitizer aims to prevent script execution when you sanitize untrusted HTML and embed the result into an HTML document as markup.&lt;/p&gt;
&lt;p&gt;It does &lt;em&gt;not&lt;/em&gt; make it safe to drop the output into JavaScript string contexts, CSS contexts, URL contexts, or other non-HTML contexts — those need their own escaping/handling.&lt;/p&gt;
&lt;p&gt;If you want the details, see the JustHTML sanitization documentation:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;https://github.com/EmilStenstrom/justhtml/blob/master/docs/sanitization.md&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And the benchmark harness repo:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;https://github.com/EmilStenstrom/justhtml-xss-bench&lt;/li&gt;
&lt;/ul&gt;

            </content>
        </entry>
    
        <entry>
            <title type="html">JustHTML: Addressing some questions</title>
            <link href="http://friendlybit.com/python/justhtml-faq/" rel="alternate" type="text/html" title="JustHTML: Addressing some questions" />
            <published>2025-12-19T12:00:00+01:00</published>
            <updated>2025-12-19T12:00:00+01:00</updated>
            <id>http://friendlybit.com/python/justhtml-faq/</id>
            <author>
                <name>Emil Stenström</name>
            </author>
            <summary type="text">When Simon Willison wrote about JustHTML [1] [2], suddenly everyone was interested in giving their view. After reading through (what I think is) all of...</summary>
            <content type="html" xml:base="http://friendlybit.com/python/justhtml-faq/">
                &lt;p&gt;When Simon Willison wrote about JustHTML &lt;a href=&#34;https://simonwillison.net/2025/Dec/14/justhtml/&#34;&gt;[1]&lt;/a&gt; &lt;a href=&#34;https://simonwillison.net/2025/Dec/15/porting-justhtml/&#34;&gt;[2]&lt;/a&gt;, suddenly everyone was interested in giving their view. After reading through (what I think is) all of them, I thought I&#39;d address some questions that have arisen.&lt;/p&gt;
&lt;h2 id=&#34;quotthis-is-a-copy-this-is-derived-workquot&#34;&gt;&amp;quot;This is a copy / this is derived work!&amp;quot;&lt;a href=&#34;#quotthis-is-a-copy-this-is-derived-workquot&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It is very unclear if JustHTML is a derived work. About halfway in I did tell the LLM to &amp;quot;port html5ever&amp;quot;, but I don&#39;t think that&#39;s what the LLM did. It started from the code structure of html5ever, but much of the code was trial and error against the html5lib-tests suite. In later versions I asked it to refactor much of the code, so I don&#39;t think even the structure is there any more.&lt;/p&gt;
&lt;p&gt;I asked an agent to try to find cross references between the two projects that still remain, but all it found were things that were also in the WHATWG HTML5 specification. This doesn&#39;t say it&#39;s &lt;strong&gt;not&lt;/strong&gt; derivative work, but highlights that it&#39;s far from clear.&lt;/p&gt;
&lt;p&gt;If you can find cases where the code is very similar (and not specifically required by spec), I would be happy to see it!&lt;/p&gt;
&lt;h2 id=&#34;quothe-stripped-the-authorship-laundered-the-l&#34;&gt;&amp;quot;He stripped the authorship / laundered the license!&amp;quot;&lt;a href=&#34;#quothe-stripped-the-authorship-laundered-the-l&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This wording assumes an active attempt to strip it, which is opposite of me adding the html5ever &lt;a href=&#34;https://github.com/EmilStenstrom/justhtml/blob/master/README.md#acknowledgments&#34;&gt;acknowledgement&lt;/a&gt; to the JustHTML README. Just stop that nonsense. I have nothing but love for the html5ever developers. To put that to rest, I&#39;ve decided to 1) add their copyright block to my license anyway and 2) &lt;a href=&#34;https://github.com/servo/html5ever/issues/701&#34;&gt;ask them specifically for guidance&lt;/a&gt;. I&#39;m looking forward to hearing their view.&lt;/p&gt;
&lt;p&gt;For reference, this is the current &lt;code&gt;LICENSE&lt;/code&gt; file in JustHTML:&lt;/p&gt;
&lt;pre&gt;MIT License

Copyright (c) 2025 Emil Stenström
Copyright (c) 2014-2017, The html5ever Project Developers

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the &amp;quot;Software&amp;quot;), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
...
&lt;/pre&gt;&lt;h2 id=&#34;quothe-doesnt-understand-the-codequot&#34;&gt;&amp;quot;He doesn&#39;t understand the code&amp;quot;&lt;a href=&#34;#quothe-doesnt-understand-the-codequot&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Many commenters were angry that I didn&#39;t &lt;em&gt;understand the code&lt;/em&gt;. This is such an interesting take, especially in a society where not understanding is seen as weakness. We should be certain! We should know everything! But we don&#39;t. We&#39;re all fallible and walk around trying to figure things out.&lt;/p&gt;
&lt;p&gt;In the specific case of the HTML5 specification, there are very few people—in the world—who understand it. HTML5 is an intricate web of algorithms, that interact in difficult-to-understand ways (see &lt;a href=&#34;https://htmlparser.info/&#34;&gt;htmlparser.info&lt;/a&gt; for a great guided tour). Did you know that the tokenizer and treebuilder affect each other?&lt;/p&gt;
&lt;p&gt;Luckily for us, the authors decided to &lt;s&gt;shame&lt;/s&gt; help browsers interoperate by publishing the fantastic html5lib-tests suite. It&#39;s an incredible feat of engineering, with thousands of integration tests that (almost) completely test the specification.&lt;/p&gt;
&lt;p&gt;Here is a representative example of the kind of input those tests test for:&lt;/p&gt;
&lt;div class=&#34;highlight&#34; data-language=&#34;HTML&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;One &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;two &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;three&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt; four&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt; five
&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;table&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;tr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;td&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;cell&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;table&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;svg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;foreignObject&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;inside svg&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;foreignObject&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;svg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;small&gt;That single snippet touches multiple &amp;quot;special&amp;quot; parts of the spec: mis-nested formatting elements (the adoption agency algorithm), implied end tags, table insertion mode oddities, and foreign content integration points.&lt;/small&gt;&lt;/p&gt;
&lt;p&gt;What&#39;s fantastic about html5lib-tests is that it gives us a way to look at our code from the outside and see if it works or not, &lt;strong&gt;without us having to understand it&lt;/strong&gt;. If this feels extreme, think of low-level code—assembler—if you will. Do you understand how it flips the transistors in your computer? I don&#39;t. And that&#39;s fine, because you have other ways to know that your code works. You don&#39;t have to go into the details.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;LLMs are quickly becoming a new layer on top of the code we write. If we can find a way to prove that it works, we don&#39;t need to understand it. That opens up whole new possibilities!&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&#34;quotits-not-high-quality-code-because-it-wont&#34;&gt;&amp;quot;It&#39;s not high quality code, because it won&#39;t be maintained&amp;quot;&lt;a href=&#34;#quotits-not-high-quality-code-because-it-wont&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I&#39;m very sure that this code is maintainable, because I have been maintaining it for a while already. As I was approaching 100% test coverage, a new HTML5 feature was added to the test suite: &lt;code&gt;&amp;lt;selectedcontent&amp;gt;&lt;/code&gt;. This was easily supported with a couple of queries to the LLM.&lt;/p&gt;
&lt;p&gt;I am planning to maintain it. The PRs are rolling in, and I have quite a clear image of where I want to take it. I think the API I&#39;ve put on top of the parser is really attractive, with a very low learning curve. That&#39;s worth something, and it&#39;s missing from all the other libraries.&lt;/p&gt;
&lt;p&gt;The first versions of the library were very hard to maintain, even with LLM help. When I looked under the hood there were messy nested if blocks, that mirrored some of the test data exactly. The LLM was cheating! I have not seen signs of this in the later versions of the code, and especially since LLM models got better.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;I&#39;m happy that my little experiment triggered so many discussions. Overall, I think (and you are free to disagree) that having this library is a big net positive for the Python community.&lt;/p&gt;

            </content>
        </entry>
    
        <entry>
            <title type="html">How I wrote JustHTML using coding agents</title>
            <link href="http://friendlybit.com/python/writing-justhtml-with-coding-agents/" rel="alternate" type="text/html" title="How I wrote JustHTML using coding agents" />
            <published>2025-12-03T12:00:00+01:00</published>
            <updated>2025-12-03T12:00:00+01:00</updated>
            <id>http://friendlybit.com/python/writing-justhtml-with-coding-agents/</id>
            <author>
                <name>Emil Stenström</name>
            </author>
            <summary type="text">I recently released JustHTML, a python-based HTML5 parser. It passes 100% of the html5lib test suite, has zero dependencies, and includes a CSS selector...</summary>
            <content type="html" xml:base="http://friendlybit.com/python/writing-justhtml-with-coding-agents/">
                &lt;p&gt;I recently released &lt;a href=&#34;https://github.com/EmilStenstrom/justhtml&#34;&gt;JustHTML&lt;/a&gt;, a python-based HTML5 parser. It passes 100% of the html5lib test suite, has zero dependencies, and includes a CSS selector query API. Writing it taught me a lot about how to work with coding agents effectively.&lt;/p&gt;
&lt;p&gt;I thought I knew HTML going into this project, but it turns out I know nothing when it comes to parsing broken HTML5 code. That&#39;s the majority of the algorithm.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://hsivonen.fi/&#34;&gt;Henri Sivonen&lt;/a&gt;, who implemented the HTML5 parser for Firefox, called the &amp;quot;&lt;a href=&#34;https://html.spec.whatwg.org/multipage/parsing.html#adoption-agency-algorithm&#34;&gt;adoption agency algorithm&lt;/a&gt;&amp;quot; (which handles misnested formatting elements) &amp;quot;the most complicated part of the tree builder&amp;quot;. It involves a &amp;quot;&lt;a href=&#34;https://html.spec.whatwg.org/multipage/parsing.html#list-of-active-formatting-elements&#34;&gt;Noah&#39;s Ark&lt;/a&gt;&amp;quot; clause (limiting identical elements to 3) and complex stack manipulation that breaks the standard stack model.&lt;/p&gt;
&lt;p&gt;I still don&#39;t know how to solve those problems. But I still have a parser that solves those problems better than the reference implementation &lt;a href=&#34;https://github.com/html5lib/html5lib-python&#34;&gt;html5lib&lt;/a&gt;. Power of AI! :)&lt;/p&gt;
&lt;h2 id=&#34;why-html5&#34;&gt;Why HTML5?&lt;a href=&#34;#why-html5&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When picking a project to build with coding agents, choosing one that already has a lot of tests is a great idea. HTML5 is extremely well-specified, with a long specification and thousands of treebuilder and tokenizer tests available in the &lt;a href=&#34;https://github.com/html5lib/html5lib-tests&#34;&gt;&lt;code&gt;html5lib-tests&lt;/code&gt;&lt;/a&gt; repository.&lt;/p&gt;
&lt;p&gt;When using coding agents autonomously, you need a way for them to understand their own progress. A complete test suite is perfect for that. The agent can run the tests, see what failed, and iterate until they pass.&lt;/p&gt;
&lt;h2 id=&#34;building-the-parser-iterations-restarts-and-per&#34;&gt;Building the parser (iterations, restarts, and performance work)&lt;a href=&#34;#building-the-parser-iterations-restarts-and-per&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Writing a full HTML5 parser is not a short one-shot problem. I have been working on this project for a couple of months on off-hours.&lt;/p&gt;
&lt;p&gt;Tooling: I used plain VS Code with Github Copilot in Agent mode. I enabled automatic approval of all commands, and then added a blacklist of commands that I always wanted to approve manually. I wrote an &lt;a href=&#34;https://github.com/EmilStenstrom/justhtml/blob/main/.github/copilot-instructions.md&#34;&gt;agent instruction&lt;/a&gt; that told it to keep working, and don&#39;t stop to ask questions. Worked well!&lt;/p&gt;
&lt;p&gt;Here is the process it took to get here:&lt;/p&gt;
&lt;h3 id=&#34;a-one-shot-html5-parser-as-a-baseline&#34;&gt;A one-shot HTML5 parser (as a baseline)&lt;a href=&#34;#a-one-shot-html5-parser-as-a-baseline&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To begin, I asked the agent to write a super-basic one-shot HTML5 parser. It didn&#39;t work very well, but it was a start.&lt;/p&gt;
&lt;h3 id=&#34;wiring-up-codehtml5lib-testscode-lt1-pass&#34;&gt;Wiring up &lt;code&gt;html5lib-tests&lt;/code&gt; (&amp;lt;1% pass rate)&lt;a href=&#34;#wiring-up-codehtml5lib-testscode-lt1-pass&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Next, I wired up the &lt;code&gt;html5lib-tests&lt;/code&gt; and saw that we had a &amp;lt;1% pass rate. Yes, those tests are hard. They are the gold standard for HTML5 parsing, containing thousands of edge cases like:&lt;/p&gt;
&lt;div class=&#34;highlight&#34; data-language=&#34;HTML&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&#34;iterating-to-30-coverage-refactors-and-bugfixes&#34;&gt;Iterating to ~30% coverage (refactors and bugfixes)&lt;a href=&#34;#iterating-to-30-coverage-refactors-and-bugfixes&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;After that, we started iterating, slowly climbing to about 30% pass rate. This involved a lot of refactoring and fixing small bugs.&lt;/p&gt;
&lt;h3 id=&#34;refactoring-into-per-tag-handlers&#34;&gt;Refactoring into per-tag handlers&lt;a href=&#34;#refactoring-into-per-tag-handlers&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Once I could see the shape of the problem, I decided I liked a handler-based structure, where each tag gets its own handler. Modular structure ftw! I asked the agent to refactor and it did.&lt;/p&gt;
&lt;div class=&#34;highlight&#34; data-language=&#34;PYTHON&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nc&#34;&gt;TagHandler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;&amp;quot;&amp;quot;&amp;quot;Base class for all tag handlers.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;handle_start&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;context&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;token&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;pass&lt;/span&gt;

&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nc&#34;&gt;UnifiedCommentHandler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;TagHandler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;&amp;quot;&amp;quot;&amp;quot;Handles comments in all states.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;handle_start&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;context&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;token&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
        &lt;span class=&#34;n&#34;&gt;context&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insert_comment&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;token&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&#34;reaching-100-test-coverage-with-better-models&#34;&gt;Reaching 100% test coverage (with better models)&lt;a href=&#34;#reaching-100-test-coverage-with-better-models&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;From there, we continued iterating to 100% test coverage. This took a long time, and the &lt;a href=&#34;https://www.anthropic.com/news/claude-3-7-sonnet&#34;&gt;Claude Sonnet 3.7&lt;/a&gt; release was the reason we got anywhere at all.&lt;/p&gt;
&lt;h3 id=&#34;benchmarking-and-discovering-we-were-3x-slower&#34;&gt;Benchmarking and discovering we were 3x slower&lt;a href=&#34;#benchmarking-and-discovering-we-were-3x-slower&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;With correctness handled, I set up a &lt;a href=&#34;https://github.com/EmilStenstrom/justhtml/blob/master/benchmarks/performance.py&#34;&gt;benchmark&lt;/a&gt; to test how fast my parser was. I saw that I was 3x slower than &lt;code&gt;html5lib&lt;/code&gt;, which is already considered slow.&lt;/p&gt;
&lt;h3 id=&#34;rewriting-the-tokenizer-in-rust-and-barely-matchi&#34;&gt;Rewriting the tokenizer in Rust (and barely matching &lt;code&gt;html5lib&lt;/code&gt;)&lt;a href=&#34;#rewriting-the-tokenizer-in-rust-and-barely-matchi&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;So I tried the obvious next move: I let an agent rewrite the tokenizer in Rust to speed things up (note: I don&#39;t know Rust). It worked, and the speed barely passed &lt;code&gt;html5lib&lt;/code&gt;. It created a whole &lt;code&gt;rust_tokenizer&lt;/code&gt; crate with 690 lines of Rust code in &lt;code&gt;lib.rs&lt;/code&gt; that I couldn&#39;t read, but it passed the tests.&lt;/p&gt;
&lt;h3 id=&#34;discovering-codehtml5evercode-fast-correct&#34;&gt;Discovering &lt;code&gt;html5ever&lt;/code&gt; (fast, correct, Rust)&lt;a href=&#34;#discovering-codehtml5evercode-fast-correct&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;While looking for alternatives, I found &lt;a href=&#34;https://github.com/servo/html5ever&#34;&gt;&lt;code&gt;html5ever&lt;/code&gt;&lt;/a&gt;, &lt;a href=&#34;https://servo.org/&#34;&gt;Servo&lt;/a&gt;&#39;s parsing engine. It is very correct and written from scratch in Rust to be fast.&lt;/p&gt;
&lt;h3 id=&#34;asking-why-build-this-at-all&#34;&gt;Asking: why build this at all?&lt;a href=&#34;#asking-why-build-this-at-all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;At that point I had the uncomfortable thought: why would the world need a slower version of &lt;code&gt;html5ever&lt;/code&gt; in partial Python? What is the meaning of it all?! I almost just deleted the whole project.&lt;/p&gt;
&lt;h3 id=&#34;pivoting-to-porting-codehtml5evercode-logic-t&#34;&gt;Pivoting to porting &lt;code&gt;html5ever&lt;/code&gt; logic to Python&lt;a href=&#34;#pivoting-to-porting-codehtml5evercode-logic-t&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Instead of quitting, I considered writing a Python interface against &lt;code&gt;html5ever&lt;/code&gt;, but decided I didn&#39;t like the hassle of a library requiring installing binary files. So I went pure Python again, but with a faster approach: what if I port the &lt;code&gt;html5ever&lt;/code&gt; logic to Python? Shouldn&#39;t that be faster than the existing Python libraries? I decided to throw all previous work away.&lt;/p&gt;
&lt;h3 id=&#34;restarting-from-scratch-again&#34;&gt;Restarting from scratch (again)&lt;a href=&#34;#restarting-from-scratch-again&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;So I started over from &amp;lt;1% test coverage and iterated with the same set of tests all the way up to 100%. This time I asked it to cross reference the Rust codebase in the beginning. It was tedious work, doing the same thing over again.&lt;/p&gt;
&lt;h3 id=&#34;still-slower-than-codehtml5libcode&#34;&gt;Still slower than &lt;code&gt;html5lib&lt;/code&gt;&lt;a href=&#34;#still-slower-than-codehtml5libcode&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Unfortunately, I ran the benchmark on the new codebase and found that it was &lt;em&gt;still&lt;/em&gt; slower than &lt;code&gt;html5lib&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;profiling-real-world-benchmarks-and-micro-optimi&#34;&gt;Profiling, real-world benchmarks, and micro-optimizations&lt;a href=&#34;#profiling-real-world-benchmarks-and-micro-optimi&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;So I switched to performance work: I wrote some new tools for the agents to use, a simple profiler and a scraper that built a dataset of 100k popular webpages for real-world benchmarking. I managed to get the speed down below the target with Python micro-optimizations, but only when using the just-released Gemini 3 Pro (which is incredible) to run the benchmark and profiler iteratively. No other model made any progress on the benchmarks.&lt;/p&gt;
&lt;div class=&#34;highlight&#34; data-language=&#34;PYTHON&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;_append_text_chunk&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;chunk&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ends_with_cr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;False&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;not&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;chunk&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ignore_lf&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ends_with_cr&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ignore_lf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;chunk&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\n&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
            &lt;span class=&#34;n&#34;&gt;chunk&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;chunk&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:]&lt;/span&gt;
            &lt;span class=&#34;c1&#34;&gt;# ...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&#34;deleting-untested-code-coverage-as-a-scalpel&#34;&gt;Deleting untested code (coverage as a scalpel)&lt;a href=&#34;#deleting-untested-code-coverage-as-a-scalpel&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Later, on a whim I ran &lt;a href=&#34;https://coverage.readthedocs.io/&#34;&gt;&lt;code&gt;coverage&lt;/code&gt;&lt;/a&gt; on the codebase and found that large parts of the code were &amp;quot;untested&amp;quot;. But this was backwards, because I already knew that the tests were covering everything important. So lines with no test coverage could be removed! I told the agent to start removing code to reach 100% test coverage, which was an interesting reversal of roles. These removals actually sped up the code as much as the microoptimizations.&lt;/p&gt;
&lt;div class=&#34;highlight&#34; data-language=&#34;PYTHON&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# Before: 786 lines of treebuilder code&lt;/span&gt;
&lt;span class=&#34;c1&#34;&gt;# After: 453 lines of treebuilder code&lt;/span&gt;
&lt;span class=&#34;c1&#34;&gt;# Result: Faster and cleaner&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&#34;fuzzing-to-find-crashes-and-harden-the-parser&#34;&gt;Fuzzing to find crashes and harden the parser&lt;a href=&#34;#fuzzing-to-find-crashes-and-harden-the-parser&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;After removing code, I got worried that I had removed too much and missed corner cases. So I asked the agent to write a &lt;a href=&#34;https://github.com/EmilStenstrom/justhtml/blob/master/benchmarks/fuzz.py&#34;&gt;html5 fuzzer&lt;/a&gt; that tried really hard to generate HTML that broke the parser.&lt;/p&gt;
&lt;div class=&#34;highlight&#34; data-language=&#34;PYTHON&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;generate_fuzzed_html&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;():&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;&amp;quot;&amp;quot;&amp;quot;Generate a complete fuzzed HTML document.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class=&#34;n&#34;&gt;parts&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;random&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;random&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;0.5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
        &lt;span class=&#34;n&#34;&gt;parts&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fuzz_doctype&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;
    &lt;span class=&#34;c1&#34;&gt;# Generate random mix of elements&lt;/span&gt;
    &lt;span class=&#34;n&#34;&gt;num_elements&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;random&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;randint&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;c1&#34;&gt;# ...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It did break the parser, and for each breaking case I asked it to fix it, and write a new test for the test suite. Passed 3 million generated webpages without any crashes, and hardened the codebase again.&lt;/p&gt;
&lt;h3 id=&#34;comparing-against-other-parsers-how-rare-100-is&#34;&gt;Comparing against other parsers (how rare 100% is)&lt;a href=&#34;#comparing-against-other-parsers-how-rare-100-is&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To sanity-check where 100% landed, I ran the &lt;code&gt;html5lib&lt;/code&gt; tests against the other parsers. I found that &lt;strong&gt;no other parser passes 90% coverage&lt;/strong&gt;, and that &lt;code&gt;lxml&lt;/code&gt;, one of the most popular Python parsers, is at &lt;strong&gt;1%&lt;/strong&gt;. The reference implementation, html5lib itself, is at 88%. Maybe this is a hard problem after all?&lt;/p&gt;
&lt;h3 id=&#34;shipping-it-as-a-library-ci-releases-selector-a&#34;&gt;Shipping it as a library (CI, releases, selector API)&lt;a href=&#34;#shipping-it-as-a-library-ci-releases-selector-a&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Finally, to make this a good library I asked the agent to set up CI, releases via GitHub, a &lt;a href=&#34;https://github.com/EmilStenstrom/justhtml/blob/master/src/justhtml/selector.py&#34;&gt;query API&lt;/a&gt;, write READMEs, and so on.&lt;/p&gt;
&lt;div class=&#34;highlight&#34; data-language=&#34;PYTHON&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;kn&#34;&gt;from&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;justhtml&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;JustHTML&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;query&lt;/span&gt;

&lt;span class=&#34;n&#34;&gt;doc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;JustHTML&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&amp;lt;div&amp;gt;&amp;lt;p&amp;gt;Hello&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;n&#34;&gt;elements&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;query&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;doc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;quot;div &amp;gt; p&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Decided to rename the library from turbohtml to justhtml, to not fool anyone that it&#39;s the fastest library, and instead focus on the feeling of everything just working.&lt;/p&gt;
&lt;h2 id=&#34;what-the-agent-did-vs-what-i-did&#34;&gt;What the agent did vs. what I did&lt;a href=&#34;#what-the-agent-did-vs-what-i-did&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;After writing the parser, I still don&#39;t know HTML5 properly. The agent wrote it for me. I guided it when it came to API design and corrected bad decisions at the high level, but it did ALL of the gruntwork and wrote all of the code.&lt;/p&gt;
&lt;p&gt;I handled all git commits myself, reviewing code as it went in. I didn&#39;t understand all the algorithmic choices, but I understood when it didn&#39;t do the right thing.&lt;/p&gt;
&lt;p&gt;As models have gotten better, I&#39;ve seen steady increases in test coverage. &lt;strong&gt;Gemini is the smartest model from a one-shot perspective, while Claude Opus is best at iterating its way to a good solution.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&#34;practical-tips-for-working-with-coding-agents&#34;&gt;Practical tips for working with coding agents&lt;a href=&#34;#practical-tips-for-working-with-coding-agents&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Start with a clear, measurable goal.&lt;/strong&gt; &amp;quot;Make the tests pass&amp;quot; is better than &amp;quot;improve the code.&amp;quot;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Review the changes.&lt;/strong&gt; The agent writes a lot of code. Read it. You&#39;ll catch issues and learn things.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Push back.&lt;/strong&gt; If something feels wrong, say so. &amp;quot;I don&#39;t like that&amp;quot; is a valid response.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use version control.&lt;/strong&gt; If the agent goes in the wrong direction, you can always revert.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Let it fail.&lt;/strong&gt; Running a command that fails teaches the agent something. Don&#39;t try to prevent all errors upfront.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;was-it-worth-it-and-what-quickly-meant&#34;&gt;Was it worth it (and what “quickly” meant)?&lt;a href=&#34;#was-it-worth-it-and-what-quickly-meant&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Yes. &lt;a href=&#34;https://github.com/EmilStenstrom/justhtml&#34;&gt;JustHTML&lt;/a&gt; is about 3,000 lines of Python with 8,500+ tests passing. I couldn&#39;t have written it this quickly without the agent.&lt;/p&gt;
&lt;p&gt;But &amp;quot;quickly&amp;quot; doesn&#39;t mean &amp;quot;without thinking.&amp;quot; I spent a lot of time reviewing code, making design decisions, and steering the agent in the right direction. The agent did the typing; I did the thinking.&lt;/p&gt;
&lt;p&gt;That&#39;s probably the right division of labor.&lt;/p&gt;

            </content>
        </entry>
    
        <entry>
            <title type="html">3 tips for agile roadmaps</title>
            <link href="http://friendlybit.com/product/agile-roadmaps/" rel="alternate" type="text/html" title="3 tips for agile roadmaps" />
            <published>2023-05-18T18:17:00+02:00</published>
            <updated>2023-05-18T18:17:00+02:00</updated>
            <id>http://friendlybit.com/product/agile-roadmaps/</id>
            <author>
                <name>Emil Stenström</name>
            </author>
            <summary type="text">&#34;Roadmapping is anti-agile. Instead, we should only have priorities.&#34;This was a response to my product roadmaps article - discuss! Are roadmaps anti-agile?—...</summary>
            <content type="html" xml:base="http://friendlybit.com/product/agile-roadmaps/">
                &lt;blockquote class=&#34;twitter-tweet&#34; data-dnt=&#34;true&#34;&gt;&lt;p lang=&#34;en&#34; dir=&#34;ltr&#34;&gt;&amp;quot;Roadmapping is anti-agile. Instead, we should only have priorities.&amp;quot;&lt;br&gt;&lt;br&gt;This was a response to my product roadmaps article - discuss! &lt;br&gt;&lt;br&gt;Are roadmaps anti-agile?&lt;/p&gt;&amp;mdash; Ant Murphy (@ant_murphy) &lt;a href=&#34;https://twitter.com/ant_murphy/status/1658955594177421312?ref_src=twsrc%5Etfw&#34;&gt;May 17, 2023&lt;/a&gt;&lt;/blockquote&gt;

&lt;p&gt;My response: Product Roadmaps can be agile or not, it depends on how you use them.&lt;/p&gt;
&lt;p&gt;The idea behind a roadmap is to deliver predictability to the business, but how does that work with also being able to respond to changes?&lt;/p&gt;
&lt;h2 id=&#34;1-update-it-regularly&#34;&gt;1. Update it regularly&lt;a href=&#34;#1-update-it-regularly&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Once a year is too slow, and leaves no wiggle room for new insights or priorities throughout the year. But updating it too often makes it hard to use instead. I&#39;ve seen quarterly updates work well for a 50 people company. Shorter for a smaller or younger company, longer for a bigger or more slow moving one.&lt;/p&gt;
&lt;h2 id=&#34;2-schedule-broad-areasproblems-not-features&#34;&gt;2. Schedule broad areas/problems, not features&lt;a href=&#34;#2-schedule-broad-areasproblems-not-features&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Features change more often than problems, so putting problem areas in the roadmap works better for both delivering some predictability and leaving space for learning. The clearer you can be with what effect you are looking for, the more empowered the team can be in solving the problem.&lt;/p&gt;
&lt;h2 id=&#34;3-dont-make-everything-time-bound&#34;&gt;3. Don’t make everything time bound&lt;a href=&#34;#3-dont-make-everything-time-bound&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If every item in the roadmap is time bound, then updating it regularly will be though. Put a time frame on the nearest items, that you know more about, so the business can plan. But don&#39;t worry about the ones longer in the future.&lt;/p&gt;
&lt;p&gt;That&#39;s it, three quick tips to work with agile roadmaps. Sounds easy? It&#39;s not.&lt;/p&gt;

            </content>
        </entry>
    
        <entry>
            <title type="html">How to prioritize a feature list, the hard way</title>
            <link href="http://friendlybit.com/product/customer-feedback/" rel="alternate" type="text/html" title="How to prioritize a feature list, the hard way" />
            <published>2023-06-08T07:53:00+02:00</published>
            <updated>2023-06-08T07:53:00+02:00</updated>
            <id>http://friendlybit.com/product/customer-feedback/</id>
            <author>
                <name>Emil Stenström</name>
            </author>
            <summary type="text">What % of the top 10 [features that customers ask for] do you end up implementing typically?— Jason Cohen (@asmartbear) June 7, 2022 Raw customer feedback...</summary>
            <content type="html" xml:base="http://friendlybit.com/product/customer-feedback/">
                &lt;blockquote class=&#34;twitter-tweet&#34;&gt;&lt;p lang=&#34;en&#34; dir=&#34;ltr&#34;&gt;What % of the top 10 [features that customers ask for] do you end up implementing typically?&lt;/p&gt;&amp;mdash; Jason Cohen (@asmartbear) &lt;a href=&#34;https://twitter.com/asmartbear/status/1534193998843330568?ref_src=twsrc%5Etfw&#34;&gt;June 7, 2022&lt;/a&gt;&lt;/blockquote&gt;

&lt;p&gt;Raw customer feedback is always too much about features, and too little about the problems customers are trying to solve.&lt;/p&gt;
&lt;p&gt;So you need to rework the feedback to map to the problems customers have instead, merging and splitting feedback as you go. Hard work. Now you have transformed your list of features to a list of problems.&lt;/p&gt;
&lt;p&gt;Can’t you just build things in this list in “most common problems” order? No. Because how often a problem occurs is not the same as how important it is.&lt;/p&gt;
&lt;p&gt;Also, the &lt;em&gt;type of user&lt;/em&gt; that has that problem is also very important.&lt;/p&gt;
&lt;p&gt;In a B2B context, the buyer is much more important than the user. In B2C there are different kinds of users, which have wildly different priority.&lt;/p&gt;
&lt;p&gt;And there are likely five other important factors too, all specific to your specific problem area, that we haven&#39;t even talked about yet.&lt;/p&gt;
&lt;p&gt;Could you build a ranking system weighing these things together, and get a bottom up priority list?&lt;/p&gt;
&lt;p&gt;You could, but it wouldn’t be at all as exact as it looks to be. It’s easy to fool yourself. Remember that the raw customer was very biased, is this something to build a solid mathematical model based on? I think not.&lt;/p&gt;
&lt;p&gt;A better way is to not build a ranking model, and work from the list of common customer problems.&lt;/p&gt;
&lt;p&gt;Instead, look at your company’s business goals, and start mapping customer problems to business goals.&lt;/p&gt;
&lt;p&gt;Say some users have problems with the checkout flow. This maps directly to a revenue target.&lt;/p&gt;
&lt;p&gt;Someone didn’t like the design of the site. Talking to them, you find that that leads to them not trusting you. Which indirectly leads to lost sales.&lt;/p&gt;
&lt;p&gt;So maybe you put up “Increase customer trust” as a node in a tree and continue mapping. As you add more feedback, and more nodes, you get a much better picture of how things fit together.&lt;/p&gt;
&lt;p&gt;Looking at the whole tree, you can now prioritize which customer problems to work on, based on business goal priority. This will make a lot more sense.&lt;/p&gt;
&lt;p&gt;Because your job is not to build features, or even to solve customer problems. It’s to drive business outcomes.&lt;/p&gt;
&lt;p&gt;And yes, you do that by solving customer problems, and building features. But that’s not where you start.&lt;/p&gt;
&lt;p&gt;That’s how you prioritize a feature list, the hard way.&lt;/p&gt;

            </content>
        </entry>
    
        <entry>
            <title type="html">The case for national digital currencies, not Bitcoin</title>
            <link href="http://friendlybit.com/future/the-case-for-national-digital-currencies-not-bitcoin/" rel="alternate" type="text/html" title="The case for national digital currencies, not Bitcoin" />
            <published>2021-06-09T10:06:00+02:00</published>
            <updated>2021-06-09T10:06:00+02:00</updated>
            <id>http://friendlybit.com/future/the-case-for-national-digital-currencies-not-bitcoin/</id>
            <author>
                <name>Emil Stenström</name>
            </author>
            <summary type="text">Bitcoin is now accepted as legal tender in El Salvador. Many believe all countries should do the same, and that the time is right for Bitcoin to take over...</summary>
            <content type="html" xml:base="http://friendlybit.com/future/the-case-for-national-digital-currencies-not-bitcoin/">
                &lt;p&gt;Bitcoin is now &lt;a href=&#34;https://www.bbc.com/news/world-latin-america-57373058&#34;&gt;accepted as legal tender in El Salvador&lt;/a&gt;. Many believe all countries should do the same, and that the time is right for Bitcoin to take over the world as a &lt;strong&gt;global digital currency&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&#34;decentralization-is-not-a-good-idea-for-a-currency&#34;&gt;Decentralization is not a good idea for a currency&lt;a href=&#34;#decentralization-is-not-a-good-idea-for-a-currency&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The core tenet of most cryptocurrencies is &lt;em&gt;decentralization&lt;/em&gt;, the idea that no single entity or individual should have absolute control over it. Instead it&#39;s protected by the fact that controlling 51% of the network is hard when lots of diverse entities are involved. The push for decentralization in cryptocurrencies is the root of most of its problems.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;For the 51% protection to work you need a large network of independent server operators&lt;/li&gt;
&lt;li&gt;To get that you need to give them some incentive to host their servers&lt;/li&gt;
&lt;li&gt;It&#39;s natural then, to give them some of the new currency in return&lt;/li&gt;
&lt;li&gt;But the currency is worth zero when no one is using it...&lt;/li&gt;
&lt;li&gt;So everyone who joins early, bets that the value will &lt;em&gt;not&lt;/em&gt; be stable, but grow exponentially over time&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This is not the recipe for how to construct something that should be stable, and be used as a digital currency.&lt;/p&gt;
&lt;p&gt;Instead we should &lt;strong&gt;skip the decentralization part&lt;/strong&gt;, and have a central bank guarantee the value. This makes it stable, and stable is something we definitely need for a currency.&lt;/p&gt;
&lt;h2 id=&#34;proof-of-work-makes-things-worse&#34;&gt;Proof of work makes things worse&lt;a href=&#34;#proof-of-work-makes-things-worse&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To get any value from a cryptocurrency you need to limit supply somehow, if there was infinite money, it wouldn&#39;t be worth anything. For Bitcoin, this is done by requiring each server to randomly try solutions to a tricky algorithmic problem. Solving it gives you coins.&lt;/p&gt;
&lt;p&gt;Proof of work for Bitcoin is a colossal waste of electricity. Especially in countries where electricity is generated by &lt;a href=&#34;https://medium.com/crypto-lucid/enough-with-bitcoins-greenwashing-bitcoin-uses-mainly-fossil-energy-b83256d693bc&#34;&gt;non-clean energy sources&lt;/a&gt;. But even for renewables, is this really the best way to use our limited energy? Let&#39;s charge car batteries, construct fossil free steel, and capture CO&lt;sub&gt;2&lt;/sub&gt; from the atmosphere with that electricity instead.&lt;/p&gt;
&lt;p&gt;There are other ways to limit supply in other cryptocurrencies, like proof of stake, and I agree that these would be better for a currency. But the easiest solution is to &lt;strong&gt;let a central bank guarantee the value&lt;/strong&gt;, and avoid the need for a &amp;quot;proof of X&amp;quot; system at all.&lt;/p&gt;
&lt;h2 id=&#34;ease-of-use-for-crime-is-not-a-good-feature&#34;&gt;Ease of use for crime is not a good feature&lt;a href=&#34;#ease-of-use-for-crime-is-not-a-good-feature&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There is a reason crime groups love Bitcoin: Transactions are anonymous. All you need is a Bitcoin address to send money to. This means no taxation, as transactions are not tied to any individual. It also means that cyber criminals just need to transfer all proceeds from a hack to their own accounts, no need to hide things further.&lt;/p&gt;
&lt;p&gt;When crime groups get the same access to capital as everyone else they can do more damage. I hope you agree that this is something we should try to limit. But this is not something that cryptocurrencies can do, because it requires a centralized authority that can act on suspicion of wrongdoing. We already have that system in place in our governments, it&#39;s just that cryptocurrencies are not built with stopping crime in mind. &lt;strong&gt;A new digital currency should take crime into consideration&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Of course, accessing your transactions should require reasonable cause as decided by a judge, and be protected by strict privacy laws. &lt;a href=&#34;https://www.courthousenews.com/banks-profit-from-selling-your-spending-data/&#34;&gt;Probably better protected than your bank statement is today&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;another-look-at-the-el-salvador-use-of-bitcoin&#34;&gt;Another look at the El Salvador use of Bitcoin&lt;a href=&#34;#another-look-at-the-el-salvador-use-of-bitcoin&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When you &lt;a href=&#34;https://www.coindesk.com/its-official-el-salvadors-legislature-votes-to-adopt-bitcoin-as-legal-tender&#34;&gt;look closer at how El Salvador plans to use Bitcoin&lt;/a&gt; a couple of things stand out:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;20% of El Salvador&#39;s GDP is people sending money to relatives, and they want to encourage that&lt;/li&gt;
&lt;li&gt;All merchants will be mandated to accept Bitcoin as payment&lt;/li&gt;
&lt;li&gt;The government will accept the monetary risk of merchants by directly converting Bitcoin to US dollars (their native currency)&lt;/li&gt;
&lt;li&gt;There&#39;s a trust fund with $150 million to balance the risk&lt;/li&gt;
&lt;li&gt;They will continuously sell off Bitcoin to balance the trust fund&lt;/li&gt;
&lt;li&gt;There is talk of the state promoting bitcoin mining&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is not something other countries should try to emulate:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Tying a country&#39;s finances to a volatile security like Bitcoin is a very high risk move. If the Bitcoin price where to crash, all the Bitcoin the government holds would be worthless. It could also rise, but this is nothing but reckless.&lt;/li&gt;
&lt;li&gt;Receiving money from relatives requires their relatives to accept the risk of holding Bitcoin. If you buy Bitcoin one day, it might be worth half what you bought it for the day after. The government will not mitigate that risk.&lt;/li&gt;
&lt;li&gt;The tech to accept Bitcoin might not be readily available to everyone, especially a poor country like El Salvador.&lt;/li&gt;
&lt;li&gt;As previously stated, betting on Bitcoin is not environmentally sound.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;requirements-for-an-ideal-digital-currency&#34;&gt;Requirements for an ideal digital currency&lt;a href=&#34;#requirements-for-an-ideal-digital-currency&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;So let&#39;s sum up what I&#39;ve talked about so far, in terms of what I believe are requirements for a digital currency.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It should be stable, so it can be used for day-to-day transactions&lt;/li&gt;
&lt;li&gt;Environmental impact of running the system should be low&lt;/li&gt;
&lt;li&gt;Law enforcement should be able to access to transaction data when warranted&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&#39;s add some things that Bitcoin is doing well:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It should be available to anyone&lt;/li&gt;
&lt;li&gt;There should be a digital wallet that it&#39;s easy to send money to&lt;/li&gt;
&lt;li&gt;Transaction costs should be low, or even zero&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&#39;s see if we can build a system that would satisfy all six requirements above, fully or partially.&lt;/p&gt;
&lt;h2 id=&#34;a-national-digital-currency&#34;&gt;A national digital currency&lt;a href=&#34;#a-national-digital-currency&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Each country should have their own digital currency, backed by that country&#39;s central bank. The digital money would be considered the same as paper money, and the central bank would be in control of minting new digital money, just like they are with paper money today.&lt;/p&gt;
&lt;p&gt;They don&#39;t need a blockchain system for this, as there &lt;em&gt;is&lt;/em&gt; trust in this system. So no proof of work, and no server farms. A simple database with a transaction ledger would be fine. Of course it needs to be protected like a bank&#39;s IT system would. This makes sure we&#39;re operating with low environmental impact.&lt;/p&gt;
&lt;p&gt;A new government agency would be created whose job it would be to safeguard the transaction ledger in a privacy sensitive way. The agency should have strict rules for when transaction data can be disclosed to law enforcement and not. I believe separating responsibility like this would be an effective way to make sure there is better balance between the needs of law enforcement and privacy.&lt;/p&gt;
&lt;p&gt;Every citizen should get a digital wallet from the government based on their social security number (or equivalent) or registered company ID. This account could be used for government payouts instead of other bank accounts. Ideally there should be a way to easily add accounts for people that live outside the system (paperless, immigrants living in the country, ...), so that these digital accounts really could be used by everyone.&lt;/p&gt;
&lt;p&gt;There should be a basic interface supplied by the government for making payments, but also an API so private organizations could build other payment solutions using these digital wallets. Transaction costs should be low or zero when done through the government supplied interface, but third parties could add fees when doing transactions through the API. Allowing fees and an API ensures that there are ways for commercial companies to improve the payment flow in interesting ways.&lt;/p&gt;
&lt;h2 id=&#34;in-summary&#34;&gt;In summary&lt;a href=&#34;#in-summary&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I think a setup like this would be great for simplifying digital commerce in a country. It would expand access to digital services, open plentiful of new use-cases, and decrease the cost of making digital transactions.&lt;/p&gt;
&lt;p&gt;Luckily, it seems at least &lt;a href=&#34;https://twitter.com/lagarde/status/1382991097849376768&#34;&gt;80 central banks are exploring this use-case&lt;/a&gt;. Let&#39;s hope we see more action, and less talk.&lt;/p&gt;

            </content>
        </entry>
    
        <entry>
            <title type="html">[The Listserve] How randomness affects your life</title>
            <link href="http://friendlybit.com/life/the-listserve-how-randomness-affects-your-life/" rel="alternate" type="text/html" title="[The Listserve] How randomness affects your life" />
            <published>2017-12-02T12:04:00+01:00</published>
            <updated>2017-12-02T12:04:00+01:00</updated>
            <id>http://friendlybit.com/life/the-listserve-how-randomness-affects-your-life/</id>
            <author>
                <name>Emil Stenström</name>
            </author>
            <summary type="text">It&#39;s a mistake not to think about randomness, for randomness is what you get if you don&#39;t make choices. Do you really want randomness controlling the most...</summary>
            <content type="html" xml:base="http://friendlybit.com/life/the-listserve-how-randomness-affects-your-life/">
                &lt;p&gt;It&#39;s a mistake not to think about randomness, for randomness is what you get if you don&#39;t make choices. Do you really want randomness controlling the most important events in your life?&lt;/p&gt;
&lt;p&gt;There are two kinds of randomness: The &amp;quot;true&amp;quot; kind, where it&#39;s impossible, using the science we know, to know the outcome. Maybe you&#39;ve heard about the &lt;a href=&#34;https://en.wikipedia.org/wiki/Uncertainty_principle&#34;&gt;Heisenberg&#39;s uncertainty principle&lt;/a&gt;. It says that, in some circumstances, if we try to measure, we destroy the thing we wanted to measure. It&#39;s not measurable, we don&#39;t know, therefor it&#39;s random. This is a basic thing in nature, a randomness we can&#39;t change no matter how hard we try.&lt;/p&gt;
&lt;p&gt;Then there&#39;s the more common kind. The kind that SEEMS random to us. If we throw a die, and three comes up, was that randomness? Not really, because if we could throw the die in exactly the same way, the same number would come up. That&#39;s not how real randomness works, but it&#39;s the kind of random we surround ourself with every day. Let&#39;s focus on this kind of every-day randomness.&lt;/p&gt;
&lt;p&gt;Is there any way to control randomness? Yes! You can reduce uncertainty. Example: Say you want to meet a partner, something that is seemingly random (we&#39;ve all hear the stories of how people &amp;quot;randomly&amp;quot; bumped into each other). Is there something you can do to make that more likely (i.e. less random)? Maybe you could hang out in places where a good partner is likely to hang out? Probably not in your livingroom right? At a local bar? At an evening course in pottery? Think about it. This is you consciously reducing the amount of randomness around meeting someone.&lt;/p&gt;
&lt;p&gt;So while randomness is something that affects us all, you can affect it by just knowing more, and adapting. Now, knowing this, is there some area of your life that you find extra important? Is there something that you really would miss if it got lost on you? Maybe it&#39;s time to reduce the uncertainty and randomness around that thing? Is there something you could do right now?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;My point: Don&#39;t let randomness control what happens to the most important parts of your life. If you don&#39;t do anything, it will.&lt;/strong&gt;&lt;/p&gt;

            </content>
        </entry>
    
        <entry>
            <title type="html">How to make e-mail encrypted for everyone</title>
            <link href="http://friendlybit.com/security/how-to-make-e-mail-encrypted-for-everyone/" rel="alternate" type="text/html" title="How to make e-mail encrypted for everyone" />
            <published>2016-10-28T23:26:00+02:00</published>
            <updated>2016-10-28T23:26:00+02:00</updated>
            <id>http://friendlybit.com/security/how-to-make-e-mail-encrypted-for-everyone/</id>
            <author>
                <name>Emil Stenström</name>
            </author>
            <summary type="text">Update: For a wholesome look at e-mail security this video from the CTO of ProtonMail: https://vimeo.com/216747532 When you send an e-mail today it&#39;s sent...</summary>
            <content type="html" xml:base="http://friendlybit.com/security/how-to-make-e-mail-encrypted-for-everyone/">
                &lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt;: For a wholesome look at e-mail security this video from the CTO of ProtonMail: &lt;a href=&#34;https://vimeo.com/216747532&#34;&gt;https://vimeo.com/216747532&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;When you send an e-mail today it&#39;s sent in plaintext. This means that when you connect to your local coffee shop&#39;s WiFi they can intercept all e-mail that is sent through their router. This is probably not the relationship you have with your barista…&lt;/p&gt;
&lt;p&gt;E-mail is way more important than this, and should receive the same privacy protection as the web has with HTTPS. I haven&#39;t seen any meaningful progress on e-mail encryption in a long time, so this article is a suggestion of how to get things moving.&lt;/p&gt;
&lt;p&gt;The base idea is this: &lt;strong&gt;We should make e-mail work just like the web in terms on encryption&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Encryption should be added gradually, without breaking backwards compatibility (interoperability)&lt;/li&gt;
&lt;li&gt;Intermediate servers shouldn&#39;t be able to read the message (privacy)&lt;/li&gt;
&lt;li&gt;Users should be able to keep sending and receiving e-mail like they always have (usability)&lt;/li&gt;
&lt;li&gt;E-mail clients and e-mail providers should be able to easily implement the scheme (simplicity)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;encrypt-as-much-as-possible-of-the-e-mail-message&#34;&gt;Encrypt as much as possible of the e-mail message&lt;a href=&#34;#encrypt-as-much-as-possible-of-the-e-mail-message&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For web traffic, we&#39;ve encrypting everything &lt;a href=&#34;https://idea.popcount.org/2012-06-16-dissecting-ssl-handshake/&#34;&gt;except the domain name&lt;/a&gt; of the site we&#39;re connecting to, this is how e-mail should work too. Since we need to know who to send the e-mail to, we should encrypt everything except the recipient address.&lt;/p&gt;
&lt;p&gt;Some of you might have read about &lt;a href=&#34;https://en.wikipedia.org/wiki/Opportunistic_TLS&#34;&gt;STARTTLS&lt;/a&gt;, a way to make SMTP encrypted. Unfortunately STARTTLS has &lt;a href=&#34;https://blog.filippo.io/the-sad-state-of-smtp-encryption/&#34;&gt;several problems&lt;/a&gt; that makes it unfit to solve the privacy problem:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It only encrypts the message &lt;strong&gt;between&lt;/strong&gt; the many servers your e-mail passes through. Your e-mail is decrypted and exists in plaintext on each of the intermediate servers. If any of the intermediate servers is hacked they get full access to all e-mails passing through. You likely don&#39;t have that kind of relationship with your ISP network administrator either…&lt;/li&gt;
&lt;li&gt;If any of the intermediate servers doesn&#39;t support STARTTLS, the connection is downgraded to plaintext instead. One old server and the whole chain breaks.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We should apply encryption on the client when sending, and decrypt at the recipient. No intermediate servers should get access to the message.&lt;/p&gt;
&lt;h2 id=&#34;encrypt-with-public-key-cryptography-without-the&#34;&gt;Encrypt with public-key cryptography (without the hassle)&lt;a href=&#34;#encrypt-with-public-key-cryptography-without-the&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The best way to handle this encryption is with &lt;a href=&#34;https://en.wikipedia.org/wiki/Public-key_cryptography&#34;&gt;public-key cryptography&lt;/a&gt;, where the sender first fetches the public key of the receiver, sends the message encrypted with that key, and then that the receiver decrypts it using their private key.&lt;/p&gt;
&lt;p&gt;People are already sending e-mail this way with &lt;a href=&#34;https://en.wikipedia.org/wiki/Pretty_Good_Privacy&#34;&gt;PGP&lt;/a&gt;, so way not just use this everywhere? It has several big usability problems:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;You need to do key management yourself&lt;/strong&gt;. On the web the browser handles all cryptography for you. You  type the address in the browser and everything just works. This is not the case with PGP. Look at this guide in the eyes of a normal person: &lt;a href=&#34;http://zacharyvoase.com/2009/08/20/openpgp/&#34;&gt;OpenPGP for a complete beginner&lt;/a&gt; - it quickly goes into downloading zipfiles, package managers and using the command line to generate keys.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;You need to install software on your computer&lt;/strong&gt;. Most e-mail providers are moving to the web, having to install a browser plugin just to send e-mail is not the experience we should strive for. I understand that this is means your e-mail provider can read your e-mail, so it won&#39;t work for everyone. But I think most people will accept that the same way they accept that their bank can read their bank statement. If we can trade this for usability it&#39;s still a HUGE step up for privacy.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here&#39;s how this could be done differently:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Encryption:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;For each recipient in the To, Cc, and Bcc fields:&lt;ol&gt;
&lt;li&gt;Do a lookup against their domain to see if it supports encryption. This should be a new DNS record that points to an address that the e-mail client can use to fetch the public key for a specific user of that domain.&lt;/li&gt;
&lt;li&gt;Fetch the public key for each of the users of the domain&lt;/li&gt;
&lt;li&gt;If both the domain has encryption support and the public key can be fetched, show a lock symbol beside that e-mail address to show that the message to that recipient will be encrypted. Clicking the lock can show information about the encryption that will be used, just like browsers do today.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;When the message is sent, split it into as many parts as the number of recipients and encrypt each part with that recipient&#39;s public key. Send it off to each recipient using the normal protocols. Users on domains that don&#39;t support encryption will get messages sent in plaintext, just like today. This ensures support can be added gradually.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Note that for the user nothing changes compared to how they send e-mail today, except that the lock shows up. Web users are slowly learning about the importance of the lock symbol when browsing the web, transfering that knowledge to e-mail too will create a push for e-mail server operators to support encryption.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Decryption:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When the encrypted e-mail reaches the intended recipients e-mail server, that server uses the private key for that user to decrypt the message and (securely) deliver it the the users inbox. The user reads their e-mail like they&#39;ve always have.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Some may frown reading that I suggest leaving the private key in the hands of your e-mail provider. I agree that this means you lose some privacy to your e-mail provider. The upside here is that we gain a lot of usability. An e-mail provider (such as Gmail) could and an e-mail client (such as Thunderbird) could update all their software tomorrow and &lt;strong&gt;all e-mail sent from Thunderbird to Gmail would be instantly encrypted&lt;/strong&gt;. This removes the burden of normal users to learn about cryptography to be safe on the web.&lt;/p&gt;
&lt;p&gt;In summary, these are the changes that needs to happen for e-mail move towards being encrypted by default:&lt;/p&gt;
&lt;h2 id=&#34;consequences-for-e-mail-clients&#34;&gt;Consequences for e-mail clients&lt;a href=&#34;#consequences-for-e-mail-clients&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;New features needed:&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;Fetch a DNS record for each receiving domain they want to send an e-mail to&lt;/li&gt;
&lt;li&gt;Fetch each receiving user&#39;s public key in the background&lt;/li&gt;
&lt;li&gt;And encrypt as much as possible of the outgoing e-mail&lt;/li&gt;
&lt;li&gt;Send the e-mail using the normal protocols&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ways to pitch this to users:&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;All e-mail sent to e-mail providers that support this scheme will be encrypted by default, with no effort on your part&lt;/li&gt;
&lt;li&gt;You can easily see which addresses get encrypted messages with the lock icon that you know from browsers&lt;/li&gt;
&lt;li&gt;Increased privacy from prying eyes&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;consequences-for-e-mail-providers&#34;&gt;Consequences for e-mail providers&lt;a href=&#34;#consequences-for-e-mail-providers&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;New features needed:&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;Add a DNS record that points to their public keys&lt;/li&gt;
&lt;li&gt;Serve the public key of a specific user that the e-mail clients ask for&lt;/li&gt;
&lt;li&gt;Decrypt incoming e-mails using the private key of that user&lt;/li&gt;
&lt;li&gt;Deliver the e-mail using the normal protocols&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ways to pitch this to users:&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;All e-mails sent from clients that support this scheme will be encrypted by default, with no effort on your part&lt;/li&gt;
&lt;li&gt;Increased privacy from prying eyes, do you really trust your local coffee shop as much as your e-mail provider?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Business reasons:&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;Support privacy without letting go of the chance to serve ads based on the content of the users e-mails. This is a very important point for big players like Gmail that serve ads. Users or course still have the option to use another e-mail provider or add PGP if they want.&lt;/li&gt;
&lt;li&gt;Nothing in this scheme blocks smaller players from making their users safe using the same methods as the big players will.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;in-summary&#34;&gt;In summary&lt;a href=&#34;#in-summary&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I think this is a very straightforward way to make all e-mail encrypted. If anyone has a better suggestion that balances the needs of users, e-mail clients and e-mail providers differently I&#39;m all ears. But let&#39;s not let this privacy travesty go on much longer…&lt;/p&gt;

            </content>
        </entry>
    
        <entry>
            <title type="html">Hur säkra är svenska banker? (Swedish)</title>
            <link href="http://friendlybit.com/security/hur-sakra-ar-svenska-banker/" rel="alternate" type="text/html" title="Hur säkra är svenska banker? (Swedish)" />
            <published>2015-03-01T19:22:05+01:00</published>
            <updated>2015-03-01T19:22:05+01:00</updated>
            <id>http://friendlybit.com/security/hur-sakra-ar-svenska-banker/</id>
            <author>
                <name>Emil Stenström</name>
            </author>
            <summary type="text">Säkerheten varierar stort bland svenska banker. Bankens storlek hänger inte ihop med hur väl dom hanterar säkerheten. Ingen av de fyra storbankerna kom med...</summary>
            <content type="html" xml:base="http://friendlybit.com/security/hur-sakra-ar-svenska-banker/">
                &lt;p&gt;&lt;em&gt;Säkerheten varierar stort bland svenska banker. Bankens storlek hänger inte ihop med hur väl dom hanterar säkerheten. Ingen av de fyra storbankerna kom med på topplistan. Några små banker finns med i toppen, några riktigt stora finns i botten.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;För att testa bankernas säkerhet gav jag varje bank två olika betyg:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Kryptering: Krypteras trafiken mellan dig och banken ordentligt?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Utan kryptering kan andra se de uppgifter som du skickar till banken, personnummer, lösenord, och koder. Dessa relativt tekniska tester görs med hjälp av &lt;a href=&#34;https://www.ssllabs.com/ssltest/&#34;&gt;SSL Labs&lt;/a&gt;. Betygsskalan kommer direkt från SSL Labs och går från A-F, där A är bäst. Notera att jag testar inloggningssidan, inte startsidan.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Visuellt: Ser allt ok ut i webbläsaren?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Det finns gott om guider som lär ut webbsäkerhet till ovana användare. Här tittar jag på om banksajterna är konfigurerade så att allt ser OK ut för en vanlig användare. Har sajten grönt hänglås? Jag har använt samma betygsskala: A-F, där A är bäst.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Bankerna kommer från Finansinspektionens lista över &lt;a href=&#34;http://www.fi.se/Templates/InstitutcategoriesPage.aspx?id=2466?la=sv&#34;&gt;bankaktiebolag och medlemsbanker&lt;/a&gt;. De lokala sparbankerna har inte tagits med, och inte heller de som främst riktar sig till företag. Testet gjordes 2015-02-28. &lt;a href=&#34;https://docs.google.com/spreadsheets/d/1KBwhyUt9Afo7_4p0izS8eSudGyHr89Ckwym--pNe1S4/edit?usp=sharing&#34;&gt;Rådata för testet finns här&lt;/a&gt;, inklusive länkarna till SSL Labs där du kan se testresultatet själv.&lt;/p&gt;
&lt;h3 id=&#34;uppdaterat&#34;&gt;Uppdaterat:&lt;a href=&#34;#uppdaterat&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Eftersom denna artikel fått väldigt bra spridning har flera av bankerna hört av sig. Ofta med konkreta förbättringar de har genomfört på grund av min artikel. Här hittar du en tidslinje över hur artikeln har ändrats över tid.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;2015-03-03: &lt;strong&gt;Inga fler uppdateringar görs från detta datum och framåt...&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;2015-03-02: &lt;strong&gt;Danske Bank&lt;/strong&gt; tillagd. Dom får tyvärr bara ett C.&lt;/li&gt;
&lt;li&gt;2015-03-02: Kontrollerade stöd för &lt;a href=&#34;https://en.wikipedia.org/wiki/Domain_Name_System_Security_Extensions&#34;&gt;&lt;strong&gt;DNSSEC&lt;/strong&gt;&lt;/a&gt;: Landshypotek, Marginalen, Danske Bank, och Länsförsäkringar är de enda som stödjer det.&lt;/li&gt;
&lt;li&gt;2015-03-03: Rättade felaktig klassificering av SBAB. Jag hade testat startsidans certifikat, inte inloggningssidans.&lt;/li&gt;
&lt;li&gt;2015-03-04: &lt;strong&gt;SBAB Bank&lt;/strong&gt; har förbättrat sitt certifikat på startsidan.&lt;/li&gt;
&lt;li&gt;2015-03-04: &lt;strong&gt;Länsförsäkringar&lt;/strong&gt; har uppdaterat sitt certifik och får nu ett B.&lt;/li&gt;
&lt;li&gt;2015-03-05: &lt;strong&gt;Ekobanken&lt;/strong&gt; har uppdaterat sitt certifikat och får nu ett A.&lt;/li&gt;
&lt;li&gt;2015-03-05: &lt;strong&gt;Forex bank&lt;/strong&gt; har uppdaterat sitt certifikat och får nu ett A.&lt;/li&gt;
&lt;li&gt;2015-03-05: &lt;strong&gt;Santander Consumer&lt;/strong&gt; har släppt en ny internetbank och hamnar nu på B.&lt;/li&gt;
&lt;li&gt;2015-03-06: &lt;strong&gt;MedMera Bank&lt;/strong&gt; har uppdaterat sitt certifikat och får nu ett B.&lt;/li&gt;
&lt;li&gt;2015-03-07: &lt;strong&gt;Carnegie Bank&lt;/strong&gt; har uppdaterat sitt certifikat och får nu ett A&lt;/li&gt;
&lt;li&gt;2015-03-08: &lt;strong&gt;SBAB Bank&lt;/strong&gt; har gjort ytterligare uppdateringar och blir första bank med A/A. Full pott!&lt;/li&gt;
&lt;li&gt;2015-03-12: &lt;strong&gt;JAK Medlemsbank&lt;/strong&gt; har uppdaterat sitt certifikat och får nu ett B.&lt;/li&gt;
&lt;li&gt;2015-03-12: &lt;strong&gt;Länsförsäkringar&lt;/strong&gt; har uppdaterat sitt certifikat och får nu ett A-.&lt;/li&gt;
&lt;li&gt;2015-03-18: &lt;strong&gt;Swedbank&lt;/strong&gt; kör nu HTTPS på sin startsida.&lt;/li&gt;
&lt;li&gt;2015-04-11: &lt;strong&gt;Resurs Bank&lt;/strong&gt; har uppdaterat certifikatet på sin startsida, och kräver nu även https där. Inloggningscertifikatet kommer att uppdateras också, men är inte klart ännu.&lt;/li&gt;
&lt;li&gt;2015-04-24: &lt;strong&gt;Skandiabanken&lt;/strong&gt; kör nu HTTPS på sin startsida.&lt;/li&gt;
&lt;li&gt;2015-10-28: &lt;strong&gt;Resurs Bank&lt;/strong&gt; har flyttat sin inloggningssida till egen domän och certifikat.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;bankerna-med-bast-sakerhet-a&#34;&gt;Bankerna med bäst säkerhet (A):&lt;a href=&#34;#bankerna-med-bast-sakerhet-a&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Fantastiska resultat för alla dessa fyra banker. Krypteringen uppfyller best practices och du kan sova gott med vissheten att din bank är bland Sveriges bästa.&lt;/p&gt;
&lt;table&gt;
  &lt;tr&gt;
    &lt;th rowspan=&#34;2&#34;&gt;
    &lt;/th&gt;
    &lt;th colspan=&#34;2&#34;&gt;
      Kryptering
    &lt;/th&gt;
    &lt;th colspan=&#34;2&#34;&gt;
      Visuellt
    &lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;
      Betyg
    &lt;/th&gt;
    &lt;th&gt;
      Not
    &lt;/th&gt;
    &lt;th&gt;
      Betyg
    &lt;/th&gt;
    &lt;th&gt;
      Not
    &lt;/th&gt;
  &lt;/tr&gt;

  &lt;tr&gt;
    &lt;td&gt;
      &lt;strong&gt;SBAB&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
      &lt;strong&gt;A&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
      1
    &lt;/td&gt;
    &lt;td&gt;
      &lt;strong&gt;A&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
    &lt;/td&gt;
  &lt;/tr&gt;

  &lt;tr&gt;
    &lt;td&gt;
      &lt;strong&gt;Skandiabanken&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
      &lt;strong&gt;A&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
      1
    &lt;/td&gt;
    &lt;td&gt;
      &lt;strong&gt;A&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
    &lt;/td&gt;
  &lt;/tr&gt;

  &lt;tr&gt;
    &lt;td&gt;
      &lt;strong&gt;Resurs Bank&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
      &lt;strong&gt;A&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
      1
    &lt;/td&gt;
    &lt;td&gt;
      &lt;strong&gt;A&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
    &lt;/td&gt;
  &lt;/tr&gt;

  &lt;tr&gt;
    &lt;td&gt;
      &lt;strong&gt;Landshypotek&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
      &lt;strong&gt;A&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
      2
    &lt;/td&gt;
    &lt;td&gt;
      &lt;strong&gt;B&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
      4
    &lt;/td&gt;
  &lt;/tr&gt;

  &lt;tr&gt;
    &lt;td&gt;
      &lt;strong&gt;Carnegie Investment Bank&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
      &lt;strong&gt;A&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
    &lt;/td&gt;
    &lt;td&gt;
      &lt;strong&gt;B&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
      4
    &lt;/td&gt;
  &lt;/tr&gt;

  &lt;tr&gt;
    &lt;td&gt;
      &lt;strong&gt;Ekobanken&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
      &lt;strong&gt;A&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
    &lt;/td&gt;
    &lt;td&gt;
      &lt;strong&gt;B&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
      4
    &lt;/td&gt;
  &lt;/tr&gt;

  &lt;tr&gt;
    &lt;td&gt;
      &lt;strong&gt;Forex Bank&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
      &lt;strong&gt;A&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
    &lt;/td&gt;
    &lt;td&gt;
      &lt;strong&gt;B&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
      4
    &lt;/td&gt;
  &lt;/tr&gt;

  &lt;tr&gt;
    &lt;td&gt;
      &lt;strong&gt;Länsförsäkringar Bank&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
      &lt;strong&gt;A-&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
      3
    &lt;/td&gt;
    &lt;td&gt;
      &lt;strong&gt;A&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
    &lt;/td&gt;
  &lt;/tr&gt;

  &lt;tr&gt;
    &lt;td&gt;
      &lt;strong&gt;Marginalen Bank&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
      &lt;strong&gt;A-&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
      3
    &lt;/td&gt;
    &lt;td&gt;
      &lt;strong&gt;A&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
    &lt;/td&gt;
  &lt;/tr&gt;

  &lt;tr&gt;
    &lt;td&gt;
      &lt;strong&gt;Nordnet&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
      &lt;strong&gt;A-&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
      3
    &lt;/td&gt;
    &lt;td&gt;
      &lt;strong&gt;A&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
    &lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;

&lt;p&gt;&lt;strong&gt;Noter&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Grattis till &lt;strong&gt;SBAB Bank, Skandiabanken&lt;/strong&gt; och &lt;strong&gt;Resurs Bank&lt;/strong&gt; som får A i båda kategorier.&lt;/li&gt;
&lt;li&gt;En extra eloge till &lt;strong&gt;Landshypotek&lt;/strong&gt; som får noll varningar från SSL Labs.&lt;/li&gt;
&lt;li&gt;Flera banker får &lt;strong&gt;A minus&lt;/strong&gt; eftersom de saknar stöd för &lt;a href=&#34;https://en.wikipedia.org/wiki/Forward_secrecy&#34;&gt;Forward Secrecy&lt;/a&gt;. Förenklat innebär det att krypteringsmetoden ändras över tid, så att någon som avlyssnat trafik till en sajt för länge sedan inte kan dekrypteras den om sajten hackas senare. Totalt har endast tre svenska banker har stöd för detta (Landshypotek, Skandiabanken och ICA Banken (se nedan)). Bra jobbat!&lt;/li&gt;
&lt;li&gt;Flera bankers &lt;strong&gt;startsidor saknar kryptering&lt;/strong&gt;. Eftersom inloggningssidorna är krypterade är detta inte katastrof, bara olyckligt. Att ha kryptering på hela sin webbplats är en bra idé eftersom kunden vänjer sig vid att se det &lt;a href=&#34;https://support.google.com/chromebook/answer/95617&#34;&gt;gröna hänglåset&lt;/a&gt; så länge dom har med banken att göra.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;bankerna-som-nastan-kom-med-pa-topplistan-b&#34;&gt;Bankerna som nästan kom med på topplistan (B):&lt;a href=&#34;#bankerna-som-nastan-kom-med-pa-topplistan-b&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Även här får bankerna bra betyg. Anledningen att dom missar topplistan är att samtliga banker i denna lista har problem med vilka krypteringsalgoritmer/protokoll dom stödjer. Det är enkelt egentligen: Om en krypteringsmetod är bevisat osäker (t.ex. RC4 eller SSL3) ska den plockas bort. Att ha den kvar innebär att kunderna luras att tro att deras uppkoppling mot banken är säker trots att den inte är det.&lt;/p&gt;
&lt;table&gt;
  &lt;tr&gt;
    &lt;th rowspan=&#34;2&#34;&gt;
    &lt;/th&gt;
    &lt;th colspan=&#34;2&#34;&gt;
      Kryptering
    &lt;/th&gt;
    &lt;th colspan=&#34;2&#34;&gt;
      Visuellt
    &lt;/th&gt;
  &lt;/tr&gt;

  &lt;tr&gt;
    &lt;th&gt;
      Betyg
    &lt;/th&gt;
    &lt;th&gt;
      Not
    &lt;/th&gt;
    &lt;th&gt;
      Betyg
    &lt;/th&gt;
    &lt;th&gt;
      Not
    &lt;/th&gt;
  &lt;/tr&gt;

  &lt;tr&gt;
    &lt;td&gt;
      &lt;strong&gt;ICA Banken&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
      &lt;strong&gt;B&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
      1
    &lt;/td&gt;
    &lt;td&gt;
      &lt;strong&gt;A&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
    &lt;/td&gt;
  &lt;/tr&gt;

  &lt;tr&gt;
    &lt;td&gt;
      &lt;strong&gt;Avanza&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
      &lt;strong&gt;B&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
    &lt;/td&gt;
    &lt;td&gt;
      &lt;strong&gt;A&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
    &lt;/td&gt;
  &lt;/tr&gt;

  &lt;tr&gt;
    &lt;td&gt;
      &lt;strong&gt;MedMera Bank&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
      &lt;strong&gt;B&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
    &lt;/td&gt;
    &lt;td&gt;
      &lt;strong&gt;A&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
    &lt;/td&gt;
  &lt;/tr&gt;

  &lt;tr&gt;
    &lt;td&gt;
      &lt;strong&gt;Santander Consumer&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
      &lt;strong&gt;B&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
    &lt;/td&gt;
    &lt;td&gt;
      &lt;strong&gt;A&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
    &lt;/td&gt;
  &lt;/tr&gt;

  &lt;tr&gt;
    &lt;td&gt;
      &lt;strong&gt;JAK Medlemsbank&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
      &lt;strong&gt;B&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
    &lt;/td&gt;
    &lt;td&gt;
      &lt;strong&gt;A&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
    &lt;/td&gt;
  &lt;/tr&gt;

  &lt;tr&gt;
    &lt;td&gt;
      &lt;strong&gt;Swedbank&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
      &lt;strong&gt;B&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
    &lt;/td&gt;
    &lt;td&gt;
      &lt;strong&gt;A&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
    &lt;/td&gt;
  &lt;/tr&gt;

  &lt;tr&gt;
    &lt;td&gt;
      &lt;strong&gt;SEB&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
      &lt;strong&gt;B&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
    &lt;/td&gt;
    &lt;td&gt;
      &lt;strong&gt;B&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
      2
    &lt;/td&gt;
  &lt;/tr&gt;

  &lt;tr&gt;
    &lt;td&gt;
      &lt;strong&gt;Volvofinans Bank&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
      &lt;strong&gt;B&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
    &lt;/td&gt;
    &lt;td&gt;
      &lt;strong&gt;B&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
      2
    &lt;/td&gt;
  &lt;/tr&gt;

  &lt;tr&gt;
    &lt;td&gt;
      &lt;strong&gt;Ikanobanken&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
      &lt;strong&gt;B&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
    &lt;/td&gt;
    &lt;td&gt;
      &lt;strong&gt;B&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
      2
    &lt;/td&gt;
  &lt;/tr&gt;

  &lt;tr&gt;
    &lt;td&gt;
      &lt;strong&gt;Nordea Bank AB&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
      &lt;strong&gt;B&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
    &lt;/td&gt;
    &lt;td&gt;
      &lt;strong&gt;B&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
      2
    &lt;/td&gt;
  &lt;/tr&gt;

  &lt;tr&gt;
    &lt;td&gt;
      &lt;strong&gt;Handelsbanken&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
      &lt;strong&gt;B&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
    &lt;/td&gt;
    &lt;td&gt;
      &lt;strong&gt;B&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
      2
    &lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;

&lt;p&gt;&lt;strong&gt;Noter&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;ICA Banken&lt;/strong&gt; är nästan med i topplistan ovan. Dom krypterar sin startsida, dom stödjer &lt;a href=&#34;https://en.wikipedia.org/wiki/Forward_secrecy&#34;&gt;Forward Secrecy&lt;/a&gt;, och är dessutom den enda bank som stödjer &lt;a href=&#34;https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security&#34;&gt;HTTP Strict Transfer Security&lt;/a&gt;. Tyvärr används krypteringsalgoritmen &lt;a href=&#34;https://en.wikipedia.org/wiki/RC4#Security&#34;&gt;RC4&lt;/a&gt;, en algoritm som inte är säker alls. Har du en lite äldre webbläsare kan du alltså bli lurad att allt ser bra ut, trots att trafiken mellan dig och banken relativt enkelt kan avlyssnas. ICA Banken borde helt enkelt stänga av stöd för RC4, be sina användare att uppgradera sin webbläsare, och inta toppositionen i denna lista.&lt;/li&gt;
&lt;li&gt;Flera bankers &lt;strong&gt;startsidor saknar kryptering&lt;/strong&gt;. Eftersom inloggningssidorna är krypterade är detta inte katastrof, bara olyckligt. Att ha kryptering på hela sin webbplats är en bra idé eftersom kunden vänjer sig vid att se det &lt;a href=&#34;https://support.google.com/chromebook/answer/95617&#34;&gt;gröna hänglåset&lt;/a&gt; så länge dom har med banken att göra.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;bankerna-som-du-borde-vara-orolig-for-c&#34;&gt;Bankerna som du borde vara orolig för (C):&lt;a href=&#34;#bankerna-som-du-borde-vara-orolig-for-c&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Någonstans här är det dags att börja bli orolig. Dessa banker har gemensamt att dom inte hanterar sin kryptering på rätt sätt. Extra olyckligt är det ställt för OK-Q8 Bank som använder en &lt;a href=&#34;https://nettbank.edb.com/&#34;&gt;färdig produkt från EDB&lt;/a&gt; och ändå får sämst betyg av C-bankerna.&lt;/p&gt;
&lt;table&gt;
  &lt;tr&gt;
    &lt;th rowspan=&#34;2&#34;&gt;
    &lt;/th&gt;
    &lt;th colspan=&#34;2&#34;&gt;
      Kryptering
    &lt;/th&gt;
    &lt;th colspan=&#34;2&#34;&gt;
      Visuellt
    &lt;/th&gt;
  &lt;/tr&gt;

  &lt;tr&gt;
    &lt;th&gt;
      Betyg
    &lt;/th&gt;
    &lt;th&gt;
      Not
    &lt;/th&gt;
    &lt;th&gt;
      Betyg
    &lt;/th&gt;
    &lt;th&gt;
      Not
    &lt;/th&gt;
  &lt;/tr&gt;

  &lt;tr&gt;
    &lt;td&gt;
      &lt;strong&gt;Danske Bank&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
      &lt;strong&gt;B&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
    &lt;/td&gt;
    &lt;td&gt;
      &lt;strong&gt;F&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
      2
    &lt;/td&gt;
  &lt;/tr&gt;

  &lt;tr&gt;
    &lt;td&gt;
      &lt;strong&gt;Erik Penser&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
      &lt;strong&gt;C&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
      3
    &lt;/td&gt;
    &lt;td&gt;
      &lt;strong&gt;F&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
      4, 5
    &lt;/td&gt;
  &lt;/tr&gt;

  &lt;tr&gt;
    &lt;td&gt;
      &lt;strong&gt;OK-Q8 Bank&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
      &lt;strong&gt;C&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
      3
    &lt;/td&gt;
    &lt;td&gt;
      &lt;strong&gt;F&lt;/strong&gt;
    &lt;/td&gt;
    &lt;td&gt;
      1, 4, 6
    &lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;

&lt;p&gt;&lt;strong&gt;Noter:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;OK-Q8 Bank lyckas vända något bra - det gröna hänglåset - till något mindre bra. Ett klick på det gröna hänglåset visar vem som garanterar säkerheten på webbplatsen. Bakom OK-Q8 Bank står EVRY AS, &lt;strong&gt;ett norskt bolag, som garant&lt;/strong&gt;.Hur ska en kund veta att det är banken som står bakom det certifikatet, att dom har kommit till rätt sajt? Underkänt.&lt;/li&gt;
&lt;li&gt;Danske Bank har bara har satsat på en &lt;strong&gt;grått hänglås&lt;/strong&gt;. Den gråa hänglåset betyder att uppkopplingen är krypterad, men inte vem som står bakom den (så kallad &lt;a href=&#34;https://en.wikipedia.org/wiki/Extended_Validation_Certificate&#34;&gt;Extended Validation&lt;/a&gt;). För något så känsligt som banktjänster ska ett riktigt EV-certifikat användas.&lt;/li&gt;
&lt;li&gt;Flera banker verkar inte ha skyddat sig från ett &lt;strong&gt;säkerhetshål som döpts till &lt;a href=&#34;https://community.qualys.com/blogs/securitylabs/2014/10/15/ssl-3-is-dead-killed-by-the-poodle-attack&#34;&gt;POODLE&lt;/a&gt;&lt;/strong&gt;. Google publicerade detaljer om hur man använder sig av hacket redan i oktober 2014. Att det fortfarande finns banker som inte skyddat sig är anmärkningsvärt.&lt;/li&gt;
&lt;li&gt;Flera bankers &lt;strong&gt;startsidor saknar kryptering&lt;/strong&gt;. Eftersom inloggningssidorna är krypterade är detta inte katastrof, bara olyckligt. Att ha kryptering på hela sin webbplats är en bra idé eftersom kunden vänjer sig vid att se det &lt;a href=&#34;https://support.google.com/chromebook/answer/95617&#34;&gt;gröna hänglåset&lt;/a&gt; så länge dom har med banken att göra.&lt;/li&gt;
&lt;li&gt;Erik Penser gör kapitalfel på sin inloggningssida, den &lt;strong&gt;ser inte ut att vara krypterad&lt;/strong&gt;. Som kund ska man aldrig skriva in känsliga inloggningsuppgifter på en webbsida som saknar hänglås. Erik Penser gör visserligen rätt bakom kulisserna (inloggningssidan ÄR krypterad) men det visas inte upp för kunden alls. Gör om, gör rätt.&lt;/li&gt;
&lt;li&gt;OK-Q8 Bank och Danske Bank visar inte något som helst hänglås i webbläsaren. Anledningen är att inloggningssidan &lt;strong&gt;hämtar bilder över en helt okrypterad uppkoppling&lt;/strong&gt;. Det gör att man väldigt lätt kan se att du loggar in på banken. Danske Bank döljer dessutom denna miss genom att visa inloggningssidan i en popupruta. Riktigt illa.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;bankerna-du-borde-undvika-f&#34;&gt;Bankerna du borde undvika (F):&lt;a href=&#34;#bankerna-du-borde-undvika-f&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Dags för bottennoteringarna. Det gemensamma för dessa banker är att dom har allvarliga problem med sin kryptering. Du kan därför inte kan lita på att trafiken till deras sajter är ordentligt krypterad.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Inga banker ligger på den här positionen längre. Bra jobbat av alla fem!&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&#34;slutligen&#34;&gt;Slutligen&lt;a href=&#34;#slutligen&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Det finns väldigt många andra kriterier att sätta betyg på, men jag nöjer mig med detta denna gång. Kanske finns det någon annan som vill fortsätta jämförelsen på sin egen blogg?&lt;/p&gt;
&lt;p&gt;Vill du diskutera resultaten vidare finns jag på Twitter: @&lt;a href=&#34;http://twitter.com/EmilStenstrom&#34;&gt;EmilStenstrom&lt;/a&gt;&lt;/p&gt;

            </content>
        </entry>
    
        <entry>
            <title type="html">8 insights from using the Narrative clip</title>
            <link href="http://friendlybit.com/lifelogging/8-insights-from-using-the-narrative-clip/" rel="alternate" type="text/html" title="8 insights from using the Narrative clip" />
            <published>2014-02-22T16:42:03+01:00</published>
            <updated>2014-02-22T16:42:03+01:00</updated>
            <id>http://friendlybit.com/lifelogging/8-insights-from-using-the-narrative-clip/</id>
            <author>
                <name>Emil Stenström</name>
            </author>
            <summary type="text">For about three weeks, I have been a beta tester and then a regular user of Narrative; a wearable camera that is always on, and that takes two pictures a...</summary>
            <content type="html" xml:base="http://friendlybit.com/lifelogging/8-insights-from-using-the-narrative-clip/">
                &lt;p&gt;For about three weeks, I have been a beta tester and then a regular user of &lt;a href=&#34;http://getnarrative.com/&#34;&gt;Narrative&lt;/a&gt;; a wearable camera that is always on, and that takes two pictures a minute. I thought I&#39;d take some time and share my insights from that experience, and the reason why decided to stop using it.&lt;/p&gt;
&lt;p&gt;2014 will be a year of wearable tech and Narrative is clearly a front-runner. It&#39;s always on, and nicely captures things around you with iPhone photo quality. At the end of the day you connect it to your computer, which uploads the images to private, encrypted, image storage, that you can access through the Narrative app on your phone. A clever clustering algorithm, based on both GPS data and image similarity, clusters the images to make them easier to browse. It works great, and lives up to the promise of capturing your day as it is.&lt;/p&gt;
&lt;img class=&#34;aligncenter size-full wp-image-922&#34; src=&#34;/files/post-media/narrative-medium.png&#34; alt=&#34;narrative-medium&#34; width=&#34;539&#34; height=&#34;310&#34;&gt;

&lt;p&gt;So, insights, what did I learn from this experiment?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Asking for permission&lt;/strong&gt;. Narrative takes asking for permission to its limits, since it&#39;s always taking pictures. Because of that, I think it follows the same rules as taking a photo with your phone (people tend to think of them as two different things, I don&#39;t). Just like you ask permission before taking photos with your phone, you need permission before wearing the Narrative clip on around someone. This was my stance from the very beginning. Not everyone agrees with this, more on that below.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Taking pictures vs. Sharing pictures&lt;/strong&gt;. Giving someone permission to take pictures, does not mean you automatically get permission to share them. Narrative takes pictures all the time, but they are are not meant to be shared. They are put in the personal diary that is the Narrative app, and are only viewable by one person. Getting permission to wear it while meeting your friends, does not mean you can share the pictures with other people, without getting their permission again. While this might be obvious for the Narrative camera, I think it applies to mobile photos too. &amp;quot;Is it OK if I take a picture of you?&amp;quot; is not the same as &amp;quot;Is it OK if I share a picture of you with my friends?&amp;quot;. If you miss this distinction, you will offend people.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Everyday life&lt;/strong&gt;. If you are an active Facebook or Instagram user and look at your profile, you will see a very &amp;quot;directed&amp;quot; and prettified version of your life. This &amp;quot;fixed version&amp;quot; might match your experience well (doing new things means more vivid memories), but it does NOT match how your life looks when viewed through Narrative&#39;s stream of photos. I do programming, and ~6-8 hours of every workday I look at a computer screen. The photos from Narrative makes this painstakingly clear. This is a very different image from the one I get from looking at Facebook or Instagram, where the only picture shared might be the great theatre performance I watched later that night. This is of course obvious when you think about it, but my guess is that most people don&#39;t think about it. I definitely did during these weeks. Facebook and Instagram fundamentally change how we remember things, by reminding us of all the cool stuff we do, and forgetting everyday life.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Everyday life of your friends&lt;/strong&gt;. The difference between your social profiles and your Narrative photo stream of course applies to other people as well. When you look at your friends&#39; social profiles, know that what you see very different from their everyday life. We live in a time where we focus too &lt;a href=&#34;http://waitbutwhy.com/2013/09/why-generation-y-yuppies-are-unhappy.html&#34;&gt;much of our attention on other peoples experiences&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Asking for permission is uncomfortable&lt;/strong&gt;. No matter how strict you are in your policy about sharing the pictures you take, there will be a couple of uncomfortable minutes of explaining how the camera works to new people. People assume that the photos are shared automatically. They assume that you just asked for permission on a whim, and could just as easily forgotten about it. This of course goes away with some explaining, but it&#39;s a very bad place to be with people you don&#39;t know that well. As someone that takes pride in trying to meet new people often, this is a severe blow to the experience using Narrative. A blow hard enough that I decided the camera wasn&#39;t for me.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Not everyone will ask for permission&lt;/strong&gt;. Not everyone using Narrative will ask for permission first. I&#39;ve met people that decided not to, arguing that if you have showed something to them, it makes sense that you allow them to see it again. I understand this line of reasoning, but I think the problem really is with allowing a digital file is where the problem lies. &lt;a href=&#34;http://www.snapchat.com/&#34;&gt;Snapchat&lt;/a&gt; raised the bar on what kind of pictures you could send to your friends, but deleting them after a set amount of seconds. The Narrative images are permanent files. Even though they are not shared, there&#39;s the potential of them being shared, and that&#39;s bad enough.&lt;/p&gt;
&lt;p&gt;So I don&#39;t agree with this line of reasoning, but there will be no way of stopping these people from taking pictures of you anyway, and potentially sharing them with their friends. Personally, I think I&#39;m fine with the &amp;quot;taking pictures&amp;quot; part, especially if it&#39;s in a public setting: a subway station, a mingle event, or a large party. If it&#39;s a private setting with fewer people, I would like to be asked. If I trust the person, I would most often be OK with having pictures of me in a private setting too, but again, I would like to be asked when that person wanted to share the picture with other people. These are my own thoughts, and I don&#39;t assume yours to be the same.&lt;/p&gt;
&lt;p&gt;My thinking around this means I won&#39;t have a problem with other people not asking for permission, since this will happen mostly in public settings. In more private settings I will have to talk to them, and understand their policy around sharing the pictures with friends.&lt;/p&gt;
&lt;p&gt;Again: No matter what set of rules you think people should follow when taking pictures of you, your policy when &lt;em&gt;taking pictures yourself&lt;/em&gt; should be &lt;em&gt;at least&lt;/em&gt; as strict. If you don&#39;t expect people to take photos of you using a Narrative camera, don&#39;t take photos of them using your mobile camera without asking first. And ask again before sharing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Different forms of sharing&lt;/strong&gt;. Sharing does not necessarily mean sharing on Facebook or Instagram. It could just as easily mean showing someone your phone when having the Narrative app open. It could mean having the file on your computer and then flipping past it when looking for other stuff to show your friends. It could mean inadvertent sharing when you get hacked, get your computer or phone held as evidence in a police investigation, or your government spying on you (NSA).&lt;/p&gt;
&lt;p&gt;Sharing is tricky, and I&#39;ve heard people reasoning around it in terms of what group you share to. Some people are OK with government spying since only a few analysts will see their stuff. Other people are OK with you sharing a picture on Facebook if they know you have reasonable privacy settings. Your mileage may vary, but I wouldn&#39;t be surprised if the answer to &amp;quot;Can I share this?&amp;quot; will be highly dependent on the group of people that you share to. Now, the people you share to, and the people that are likely to see something you share, are a much smaller group (EdgeRank), and this makes it even more complicated to value how much privacy you are giving away by allowing someone to share something.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Sound&lt;/strong&gt; &lt;strong&gt;and video&lt;/strong&gt;. Another common question I&#39;ve gotten was if I&#39;m recording sound and video. The answer is no, and I think this is a good thing. I hadn&#39;t thought of this before, but sound is much more intimate than pictures are. Sound is closer to your thoughts, and sharing someone saying something stupid is much more revealing than a picture of them looking stupid. Pictures rarely portray thoughts. Sound raises the bar considerably, and would make all the interaction around the clip even more difficult. I&#39;m not sure people asking for sound and video support to be added to Narrative have considered the social aspects properly.&lt;/p&gt;
&lt;p&gt;***&lt;/p&gt;
&lt;p&gt;And that&#39;s it, my thoughts around the Narrative clip and what I&#39;ve learnt from it. I hope you have learnt something too. Don&#39;t take this as a reason not to try the camera, it&#39;s very opinionated, and you might handle the social aspects better than I did.&lt;/p&gt;
&lt;p&gt;Feel free to leave a comment if something needs clarification or if I can answer any questions about my experience.&lt;/p&gt;

            </content>
        </entry>
    
        <entry>
            <title type="html">My text editor: Sublime Text</title>
            <link href="http://friendlybit.com/tools/my-text-editor-sublime-text/" rel="alternate" type="text/html" title="My text editor: Sublime Text" />
            <published>2012-07-07T23:53:03+02:00</published>
            <updated>2012-07-07T23:53:03+02:00</updated>
            <id>http://friendlybit.com/tools/my-text-editor-sublime-text/</id>
            <author>
                <name>Emil Stenström</name>
            </author>
            <summary type="text">What text editor you use when coding is a very serious subject. I&#39;ll hardly be able to talk about what I prefer, without some people (other than you, of...</summary>
            <content type="html" xml:base="http://friendlybit.com/tools/my-text-editor-sublime-text/">
                &lt;p&gt;What text editor you use when coding is a very serious subject. I&#39;ll hardly be able to talk about what I prefer, without some people (other than you, of course ;) taking it as me trying to steal their editor from them. I&#39;m not going to. I&#39;m just going to tell you what I think, and you can just decide of you agree or not. My hope is that it helps you be more certain about your own choice of editor.&lt;/p&gt;
&lt;p&gt;What I look for in a text editor:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Support both Mac and PC&lt;/strong&gt;. I use a Mac for work, and a PC at home, and I want to be able to use the editor skills I&#39;ve learned at work for my hobby projects, and vice versa.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Less than 3 seconds starting time&lt;/strong&gt;. Big projects means lots of little files to open. So while programming I will open files and close files all the time. Each delay waiting for the editor to open, or a file to load, means hundreds of seconds over time.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Syntax highlighting for web languages&lt;/strong&gt;. I work with web stuff, both front end and back end, and I need colors to be able to scan files quickly. I don&#39;t really care about the exact ones, light or dark, I just want it to look decent. What &amp;quot;web languages&amp;quot;? HTML, CSS, Javascript, JSON, Python (Django), Ruby (Rails), PHP, SQL.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Handle big files&lt;/strong&gt; &lt;strong&gt;(&amp;lt; 1 Gb)&lt;/strong&gt;. From time to time I work with big files. Can be database dumps or large text files. I&#39;m not expecting &amp;lt; 3 sec to open them, but at least &amp;lt; 15 sec, and then be able to work with the file without the editor lagging.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Some kind of file browsing/project support&lt;/strong&gt;. There&#39;s never just one file and there&#39;s needs to be a way to overview all the files in a certain directory structure, and pick from them. I&#39;d like there both to be file searching support and a way to click to a file through the directory structure.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Code auto-completion for web languages&lt;/strong&gt;. Do I use font-variant, font-style, or font-decoration to set italic? I want my editor to show me a list of options, and let me pick. This is harder to do well for the dynamic server-side languages, but I still want it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Look good&lt;/strong&gt;. I don&#39;t want something that looks like the terminal, and want something beautiful. For me this means gradients, nice rounded tabs to click on, a nice font, a color scheme that matches.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;With that list of priorities in mind, let&#39;s look at a couple of popular editors:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Editors that are not available both for Mac and PC: &lt;strong&gt;Textmate&lt;/strong&gt;, &lt;strong&gt;Coda,&lt;/strong&gt; &lt;strong&gt;Notepad++&lt;/strong&gt;, and &lt;strong&gt;Visual Studio&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Editors that are slow: &lt;strong&gt;XCode, Eclipse&lt;/strong&gt;, &lt;strong&gt;NetBeans&lt;/strong&gt;, &lt;strong&gt;Dreamweaver&lt;/strong&gt;, and most other editors built with Java.&lt;/li&gt;
&lt;li&gt;Editors that don&#39;t handle syntax highlighting and code completion: &lt;strong&gt;Notepad&lt;/strong&gt;, &lt;strong&gt;Wordpad&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Editors that don&#39;t handle big files well: &lt;strong&gt;Komodo Edit&lt;/strong&gt; (what I used til recently).&lt;/li&gt;
&lt;li&gt;Editors that look like shit: &lt;strong&gt;Emacs&lt;/strong&gt;, &lt;strong&gt;Vim&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And then there&#39;s &lt;a href=&#34;http://www.sublimetext.com/&#34;&gt;&lt;strong&gt;Sublime Text&lt;/strong&gt;&lt;/a&gt;. Which does all of the above, perfectly. You should try it.&lt;/p&gt;

            </content>
        </entry>
    
        <entry>
            <title type="html">[The Listserve] I’m sorry to break this to you</title>
            <link href="http://friendlybit.com/other/the-listserve-im-sorry-to-break-this-to-you/" rel="alternate" type="text/html" title="[The Listserve] I’m sorry to break this to you" />
            <published>2012-04-19T13:02:33+02:00</published>
            <updated>2012-04-19T13:02:33+02:00</updated>
            <id>http://friendlybit.com/other/the-listserve-im-sorry-to-break-this-to-you/</id>
            <author>
                <name>Emil Stenström</name>
            </author>
            <summary type="text">I just got the fantastic chance to send an e-mail to ~10.000 people. What would you write if you got that chance? This is what I wrote: &#34;I&#39;m sorry to break...</summary>
            <content type="html" xml:base="http://friendlybit.com/other/the-listserve-im-sorry-to-break-this-to-you/">
                &lt;p&gt;I just got the fantastic chance to send an e-mail to ~10.000 people. What would you write if &lt;a href=&#34;http://thelistserve.com/&#34;&gt;you got that chance&lt;/a&gt;? This is what I wrote:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;quot;I&#39;m sorry to break this to you, in an e-mail from a random stranger like this, but it needs to be said: Most of your life won&#39;t be fantastic. I&#39;m not joking. The adventures you&#39;ll tell your children about will be a minuscule part of it. So if you want to avoid the feeling of utter disappointment as you grow older, you need to accept that fact. Sorry.&lt;/p&gt;
&lt;p&gt;This leads us to the insight: You should focus more on the non-fantastic parts. The parts where you eat breakfast, walk to the bus, have a boring day at work, eat your ordinary lunch, shop groceries, and brush your teeth. After all, this is the major part of your life, and neglecting it is a wasted opportunity.&lt;/p&gt;
&lt;p&gt;Here&#39;s the thing: Most of the boring stuff in your life is so dull, that even the tiniest thing can make it seem fun. The tiniest thing. This means the you could make it better with extremely simple means.&lt;/p&gt;
&lt;p&gt;What exactly am I talking about here? Little things. Like these:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Challenges: When brushing your teeth tonight, use your left hand.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Mind games: When you enter work (or school!), imagine the sound &amp;quot;Kabaaaam!&amp;quot; as you enter, as if your presence changed the whole room.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Action: Jump down from the side-walk, instead of just stepping down.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Changes: Buy some fancy tomato sauce tonight, instead of your usual brand.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;More action: Count the number of pink things on your way to work, as if your life depended on it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;See? Easy stuff. I really try to live by this &amp;quot;Everyday Action&amp;quot; idea, and I think it works for making the boring parts of life more fun. Because that&#39;s the thing: just because the fantastic moments are few, there&#39;s no reason to just sit there, waiting for the next big thing to swipe you off your feet. Have some fun meanwhile, it&#39;s easy…&amp;quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&#34;http://thelistserve.com/&#34;&gt;Sign up for Thelistserve&lt;/a&gt;. I&#39;m guessing there&#39;s more inspiration to come…&lt;/p&gt;

            </content>
        </entry>
    
        <entry>
            <title type="html">What movies on Piratebay will you like the most?</title>
            <link href="http://friendlybit.com/python/what-movies-on-piratebay-will-you-like-the-most/" rel="alternate" type="text/html" title="What movies on Piratebay will you like the most?" />
            <published>2012-01-08T22:07:29+01:00</published>
            <updated>2012-01-08T22:07:29+01:00</updated>
            <id>http://friendlybit.com/python/what-movies-on-piratebay-will-you-like-the-most/</id>
            <author>
                <name>Emil Stenström</name>
            </author>
            <summary type="text">Christmas, and the weeks thereafter, are times for coding. And I&#39;ve been playing around with piratebay and filmtipset (a Swedish movie recommendation) a...</summary>
            <content type="html" xml:base="http://friendlybit.com/python/what-movies-on-piratebay-will-you-like-the-most/">
                &lt;p&gt;Christmas, and the weeks thereafter, are times for coding. And I&#39;ve been playing around with &lt;a href=&#34;http://thepiratebay.org&#34;&gt;piratebay&lt;/a&gt; and &lt;a href=&#34;http://filmtipset.se&#34;&gt;filmtipset&lt;/a&gt; (a Swedish movie recommendation) a little bit. I just pushed it to the &lt;a href=&#34;https://github.com/EmilStenstrom/filmtipset-piratebay&#34;&gt;filmtipset-piratebay project on GitHub&lt;/a&gt;, if you want to take a look.&lt;/p&gt;
&lt;h2 id=&#34;css-for-screen-scraping&#34;&gt;CSS for screen scraping&lt;a href=&#34;#css-for-screen-scraping&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The script is using &lt;strong&gt;CSS for screen scraping&lt;/strong&gt;; something that works extremely well:&lt;/p&gt;
&lt;div class=&#34;highlight&#34; data-language=&#34;PYTHON&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;kn&#34;&gt;from&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;lxml&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;html&lt;/span&gt;
&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;requests&lt;/span&gt;
&lt;span class=&#34;n&#34;&gt;response&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;requests&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;http://thepiratebay.org/browse/207/0/7&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;n&#34;&gt;document&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;html&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;document_fromstring&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;response&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;content&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;n&#34;&gt;links&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;document&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;cssselect&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;.detLink&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;link&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;text_content&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;link&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;links&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Note: You need &lt;a href=&#34;http://lxml.de/&#34;&gt;lxml&lt;/a&gt; and &lt;a href=&#34;http://docs.python-requests.org&#34;&gt;requests&lt;/a&gt; to run the above example.&lt;/p&gt;
&lt;p&gt;Saving the above snippet to a py-file and running it will give you a list of all torrents on the given url. Play around with the CSS selector to get some other data from the page.&lt;/p&gt;
&lt;h2 id=&#34;extracting-movie-titles-from-torrent-names&#34;&gt;Extracting movie titles from torrent names&lt;a href=&#34;#extracting-movie-titles-from-torrent-names&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It&#39;s surprisingly easy to convert torrent names to movie titles. Just follow this simple algorithm:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Split the torrent name into words by treating all non-alphanumeric characters as space.&lt;/li&gt;
&lt;li&gt;Loop over the remaining words, and look for a predefined set of &amp;quot;torrent endings&amp;quot;.&lt;/li&gt;
&lt;li&gt;When you find an ending, cut the name from there&lt;/li&gt;
&lt;li&gt;(Optional) Remove the year if there is one at the end of the remaining string&lt;/li&gt;
&lt;li&gt;(Optional) Remove all movies which really are bundles of movies, and not single movies. This is easily done by looking for a set of common strongs such as &amp;quot;trilogy&amp;quot; and &amp;quot;series&amp;quot;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Result: &amp;quot;Real.Steel.2011.720p.BluRay.x264-REFiNED&amp;quot; -&amp;gt; &amp;quot;Real Steel&amp;quot;&lt;/p&gt;
&lt;p&gt;You can find my movie title finder implementation in parse.py on GitHub.&lt;/p&gt;
&lt;h2 id=&#34;cache-all-http-request&#34;&gt;Cache all HTTP Request&lt;a href=&#34;#cache-all-http-request&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To both save time, and be nice to the services we&#39;re querying, the script caches all HTTP requests for a number of days. I do this by simply saving the returned HTML/JSON to a file, and checking the file system for that file before making a new request. Saving the HTML/JSON, and not the processed result, makes it possible to experiment with the parsing, without having to wait for new requests from the server.&lt;/p&gt;
&lt;p&gt;My caching implementation is of course also on GitHub.&lt;/p&gt;
&lt;p&gt;***&lt;/p&gt;
&lt;p&gt;All and all, this has been a fun little project, and I&#39;ve learned a lot. But I&#39;m sure we can make this even better. Feel free to send pull requests!&lt;/p&gt;

            </content>
        </entry>
    
        <entry>
            <title type="html">Partial XMLHttpRequest responses?</title>
            <link href="http://friendlybit.com/js/partial-xmlhttprequest-responses/" rel="alternate" type="text/html" title="Partial XMLHttpRequest responses?" />
            <published>2012-01-08T15:28:49+01:00</published>
            <updated>2012-01-08T15:28:49+01:00</updated>
            <id>http://friendlybit.com/js/partial-xmlhttprequest-responses/</id>
            <author>
                <name>Emil Stenström</name>
            </author>
            <summary type="text">We all know how to make an AJAX request, and fetch some data. But as soon as you need to fetch data incrementally, have the server push data to you, you...</summary>
            <content type="html" xml:base="http://friendlybit.com/js/partial-xmlhttprequest-responses/">
                &lt;p&gt;We all know how to make an AJAX request, and fetch some data. But as soon as you need to fetch data incrementally, have the server push data to you, you have to resort to all sorts of complicated stuff. Websockets; with all their different versions and shady support, different kinds of polling, hidden iframes, ActiveX for IE?&lt;/p&gt;
&lt;p&gt;The simplest way, that almost workds is partial XMLHttpRequest responses. I first read about them as &lt;a href=&#34;http://www.kylescholz.com/blog/2010/01/progressive_xmlhttprequest_1.html&#34;&gt;progressive xmlhttprequests on Kyle Schulz blog&lt;/a&gt;, but really think that method should get more recognition.&lt;/p&gt;
&lt;p&gt;Note: I&#39;ve only tested this with Webkit, against Twitter&#39;s Streaming API, with a XMLHttpRequest that allows cross-domain requests. I think it works with Firefox too, but it will definitely not work in IE. Sorry.&lt;/p&gt;
&lt;div class=&#34;highlight&#34; data-language=&#34;JS&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;xhr&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;ow&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;XMLHttpRequest&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&amp;lt;streaming-url-on-you-own-domain-or-CORS&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;nx&#34;&gt;xhr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;open&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;GET&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;span class=&#34;nx&#34;&gt;xhr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;send&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;span class=&#34;c1&#34;&gt;// Define a method to parse the partial response chunk by chunk&lt;/span&gt;
&lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;last_index&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;parse&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;curr_index&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;xhr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;responseText&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;length&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;last_index&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;==&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;curr_index&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// No new data&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;xhr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;responseText&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;substring&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;last_index&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;curr_index&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;last_index&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;curr_index&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;c1&#34;&gt;// Check for new content every 5 seconds&lt;/span&gt;
&lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;interval&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;setInterval&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;parse&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;5000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;span class=&#34;c1&#34;&gt;// Abort after 25 seconds&lt;/span&gt;
&lt;span class=&#34;nx&#34;&gt;setTimeout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;clearInterval&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;interval&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;parse&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;xhr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;abort&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;25000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The biggest problem with this method is that the responseText property keeps filling up with data. The longer you receive data, the bigger the data in memory will be. The only way I can see this fixed (today) is to simply kill the connection after a certain amount of data has been received, and open it up again.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;I would love to see a better way to do this&lt;/strong&gt;, from native javascript, without all the numerous hacks that are out there. If you know of a way that fills these requirements, please let me know:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Easy to implement on the &lt;strong&gt;client side&lt;/strong&gt;. Ideally I would like to use XMLHttpRequest, and just get a callback each time the client sends data, with the NEW data specified as a callback parameter.&lt;/li&gt;
&lt;li&gt;Easy to implement on the &lt;strong&gt;server-side&lt;/strong&gt;. I can set some headers if you make me, but ideally I would like to use this against existing Streaming APIs (like Twitter&#39;s), without adding custom stuff.&lt;/li&gt;
&lt;li&gt;As cross-browser, cross-platform as possible.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Is there a way to get this working? It&#39;s so annoying to see something that&#39;s a curl one-liner, be 100s of lines of code with web technologies…&lt;/p&gt;
&lt;div class=&#34;highlight&#34; data-language=&#34;BASH&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;curl&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;https://stream.twitter.com/1/statuses/filter.json?track&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&amp;lt;your-keyword&amp;gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-u&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&amp;lt;your-twitter-nick&amp;gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Is the web really that far behind?&lt;/strong&gt;&lt;/p&gt;

            </content>
        </entry>
    
        <entry>
            <title type="html">Suggestions for TweetDeck</title>
            <link href="http://friendlybit.com/ui/suggestions-for-tweetdeck/" rel="alternate" type="text/html" title="Suggestions for TweetDeck" />
            <published>2011-07-22T12:35:04+02:00</published>
            <updated>2011-07-22T12:35:04+02:00</updated>
            <id>http://friendlybit.com/ui/suggestions-for-tweetdeck/</id>
            <author>
                <name>Emil Stenström</name>
            </author>
            <summary type="text">To be an effective twitter user you really need a permanent window on your desktop, that shows you what&#39;s happening right now. I&#39;ve been using TweetDeck for...</summary>
            <content type="html" xml:base="http://friendlybit.com/ui/suggestions-for-tweetdeck/">
                &lt;p&gt;To be an effective twitter user you really need a permanent window on your desktop, that shows you what&#39;s happening right now. I&#39;ve been using &lt;a class=&#34;zem_slink&#34; title=&#34;TweetDeck&#34; href=&#34;http://www.tweetdeck.com&#34; rel=&#34;homepage&#34;&gt;TweetDeck&lt;/a&gt; for a long time now, with short detours into &lt;a class=&#34;zem_slink&#34; title=&#34;seesmic&#34; href=&#34;http://seesmic.com&#34; rel=&#34;homepage&#34;&gt;Seesmic&lt;/a&gt; and various smaller clients. TweetDeck is the best client I&#39;ve found for my needs, but its interface has lots of little bugs and inconsistencies; small things that together add up to a bad user experience.&lt;/p&gt;
&lt;p&gt;I hope someone from TweetDeck reads this, and can start working on these (simple) issues. I really think they would improve the general user experience a lot.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Compose pane should close after a tweet as been sent&lt;/strong&gt;. I&#39;ve looked over the shoulder of lots of people using TweetDeck, and the most common pattern is to click the reply button on someone&#39;s avatar to write a new message. If writing multiple messages directly after eachother was more common than writing them one by one, then it should stay open, but that&#39;s not the case.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tweets should be as compact height-wise as they can be&lt;/strong&gt;. All tweets in TweetDeck are displayed as if they where full 140 chars. This means that valuable screen space is lost, something TweetDeck pushes one of its advantages over the official Twitter client or the web. &amp;quot;More Tweets per screen!&amp;quot;. What&#39;s worse, having tweets aligned horizontally over several columns makes it look like there&#39;s some connection between then. Why do they align my third timeline tweet with my third mention? No idea.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;All retweets should be shown, not only the last one&lt;/strong&gt;. Retweets are a very important part of Twitter. By seeing who retweets you, you learn who your active followers are, and maybe even find people that are interesting for you to follow. But TweetDeck does not follow the lead of the official client that shows &amp;quot;Retweeted by [user] and 3 others&amp;quot;, instead they show just the last one.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The buttons at the bottom of each column should be hidden&lt;/strong&gt;. The bottom buttons of each column are for things that people rarely use, but they still are allowed to permanently take of screen estate. Instead they should be hidden behind one button, and put at the top of the window with the column title. This would leave even more space for tweets to be shown.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The &amp;quot;Quick jump to column&amp;quot; buttons at the bottom of the window should be removed&lt;/strong&gt;. Having clickable icons that set the scroll offset is a very strange interface concept. I understand the need to jump between columns when you have 10-15 columns, but most people don&#39;t have that. Letting an interface element that is used seldom by most people take up that much space is just a waste. It users with that many columns is an important part of the target audience, use tabbed workspaces instead. Each workspace could show up as a tab at the top of the window, and each workspace could have its own set of columns. Until that&#39;s done, remove the clunky icons at the bottom and free more space for tweets.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Here&#39;s a quick sketch of how TweetDeck looks &lt;em&gt;&lt;strong&gt;before&lt;/strong&gt;&lt;/em&gt; any changes:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/files/post-media/tweetdeck_original.png&#34; data-no-instant&gt;&lt;img class=&#34;alignnone size-full wp-image-824&#34; title=&#34;tweetdeck_original&#34; src=&#34;/files/post-media/tweetdeck_original.png&#34; alt=&#34;&#34; width=&#34;730&#34; height=&#34;612&#34;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;… and here&#39;s the same view &lt;strong&gt;*after*&lt;/strong&gt; the five interface improvements have been implemented:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/files/post-media/tweetdeck_redesign1.png&#34; data-no-instant&gt;&lt;img class=&#34;alignnone size-full wp-image-826&#34; title=&#34;tweetdeck_redesign&#34; src=&#34;/files/post-media/tweetdeck_redesign1.png&#34; alt=&#34;&#34; width=&#34;730&#34; height=&#34;612&#34;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;These are changes I really would like to see in TweetDeck, what do you think?&lt;/p&gt;

            </content>
        </entry>
    
        <entry>
            <title type="html">Fixing Microsoft’s bad reputation</title>
            <link href="http://friendlybit.com/strategy/fixing-microsofts-bad-reputation/" rel="alternate" type="text/html" title="Fixing Microsoft’s bad reputation" />
            <published>2011-06-19T14:07:32+02:00</published>
            <updated>2011-06-19T14:07:32+02:00</updated>
            <id>http://friendlybit.com/strategy/fixing-microsofts-bad-reputation/</id>
            <author>
                <name>Emil Stenström</name>
            </author>
            <summary type="text">Microsoft has continuously failed at getting people in the tech crowd to like them. This is a growing problem for them, and something they need to start...</summary>
            <content type="html" xml:base="http://friendlybit.com/strategy/fixing-microsofts-bad-reputation/">
                &lt;figure class=&#34;wp-caption alignright&#34;&gt;&lt;a href=&#34;http://www.flickr.com/photos/88442983@N00/2228633614&#34;&gt;&lt;img title=&#34;Blue Screen of Death&#34; src=&#34;/files/post-media/2228633614_e26ea98fbe_m.jpg&#34; alt=&#34;Blue Screen of Death&#34; width=&#34;240&#34; height=&#34;187&#34; /&gt;&lt;/a&gt;
&lt;/figure&gt;

&lt;p&gt;Microsoft has continuously failed at getting people in the tech crowd to like them. This is a growing problem for them, and something they need to start taking seriously. To understand how to turn this around, let&#39;s start a decade ago, with Slashdot.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&#34;http://slashdot.org/&#34;&gt;Slashdot&lt;/a&gt;&lt;/strong&gt; has always been one of the pillars of Microsoft-negative news. They have a whole &lt;a href=&#34;http://slashdot.org/index2.pl?fhfilter=microsoft&#34;&gt;category on Microsoft&lt;/a&gt; (And the others have lots of articles too: &lt;a href=&#34;http://digg.com/search?q=microsoft&#34;&gt;&amp;quot;Microsoft&amp;quot; articles on Digg&lt;/a&gt;, &lt;a href=&#34;http://www.reddit.com/search?q=microsoft&#34;&gt;&amp;quot;Microsoft&amp;quot; articles on Reddit&lt;/a&gt;, &amp;quot;Microsoft&amp;quot; articles on Hacker News). With a few exceptions, articles (and comments) are about Microsoft using their monopoly to crush smaller businesses, how their technology is inferior to what the open source world creates, on them creating data lock-in where users are unable to switch away from them, and so on. Slashdot, Digg, Reddit, Hacker News together have millions of daily users, who place as much faith in them as others do in their morning newspaper.&lt;/p&gt;
&lt;p&gt;But these sites are only read by disgruntled teens right? No. Hacker news users are ~26, &lt;a href=&#34;http://www.reddit.com/r/reddit.com/comments/7oaxh/experiment_how_old_is_the_average_redditor_vote/?sort=top&#34;&gt;Reddit users are ~24&lt;/a&gt;, &lt;a href=&#34;http://www.ignitesocialmedia.com/social-media-stats/2011-social-network-analysis-report/#Digg&#34;&gt;Digg users are 35-44&lt;/a&gt;… When asked: &amp;quot;What computer do you want?&amp;quot; These users answer: &amp;quot;&lt;strong&gt;A Mac (or Linux) laptop&lt;/strong&gt;&amp;quot;. When asked: &amp;quot;What software? They answer: &amp;quot;&lt;strong&gt;Google Docs (or LibreOffice/OpenOffice)&lt;/strong&gt;&amp;quot;. All those individual choices are starting to pile up, and have far-reaching consequences for Microsoft: Windows and Office licenses are the &lt;a href=&#34;http://www.businessinsider.com/chart-of-the-day-microsoft-operating-income-by-division-2010-2&#34;&gt;major part of Microsoft&#39;s income&lt;/a&gt;. Choosing a Mac or Linux laptop means no Microsoft Windows, choosing Google Docs or LibreOffice/OpenOffice means no Microsoft Office.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tech users are a major force&lt;/strong&gt; inside real companies, and not something Microsoft can just ignore. What about beginners and business users? They are both heavily influenced by by tech users. Beginners because they ask others for advise before buying their computers and software. Business users are instead controlled by their IT departments, which in turn try to find the most knowledgeable tech people to work for them. The small hold Microsoft still have on some IT departments is slowly shifted away from them when employees get empowered to choose their own equipment, moving them from business users to beginners in their purchase patterns. The tech crowd has more influence than you&#39;d think.&lt;/p&gt;
&lt;p&gt;Question is: &lt;strong&gt;where do you start&lt;/strong&gt; fixing all this distrust? There&#39;s only one way: you start talking to tech users on their own terms. Here&#39;s how I would do it:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Hire some good of &lt;strong&gt;community managers&lt;/strong&gt;. Their job will not be to market Microsoft, but to be the communication channel that can aggregate community opinions to Microsoft. The more rooted they already are in their respective communities the better; that makes it easier to start get the discussion started at a respectful level. You don&#39;t yell &amp;quot;Microsoft sucks!&amp;quot; at someone you respect.&lt;/li&gt;
&lt;li&gt;Start by &lt;strong&gt;monitoring news&lt;/strong&gt; about Microsoft and send monthly reports to managers inside Microsoft. Couple each news item with an approximate number of users that read them. This will paint an image of how tech users see the Microsoft brand. Pretty soon they will want to change that image.&lt;/li&gt;
&lt;li&gt;Use community managers to ask for &lt;strong&gt;opinions on what to do next&lt;/strong&gt;. Ask for small things that are likely easy to get done, and make sure to manage expectations right away. Windows 8 won&#39;t be open sourced :)&lt;/li&gt;
&lt;li&gt;Have a &lt;strong&gt;small engineering team&lt;/strong&gt; whose sole purpose is to make the suggestions from the tech crowd happen. They need to be cross-disciplinary, be well connected across the company, and have mandate from high up the organization. Employing this team is a small cost compared to other forms of marketing. Feed back any progress they make back as articles where they fit.&lt;/li&gt;
&lt;li&gt;Lastly: Use insights from community managers to create &lt;strong&gt;marketing campaigns directly aimed at the tech crowd&lt;/strong&gt;. A good community manager can have a pretty good guess at what work and what will. Many of the things will not only be marketing, but also require engineering, but there already is a team for that.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Working through that list will make Microsoft slowly earn their trust back from the tech crowd. It won&#39;t happen overnight; you don’t reverse 10 years of silence that fast. But I think this is doable, reasonable, and something that really could work.&lt;/p&gt;
&lt;p&gt;Would you be willing to be a community manager for Microsoft? What would you suggestion Microsoft did to please the tech crowd? I&#39;d love to hear your opinions.&lt;/p&gt;

            </content>
        </entry>
    
        <entry>
            <title type="html">Geolocation and Google maps</title>
            <link href="http://friendlybit.com/js/geolocation-and-google-maps/" rel="alternate" type="text/html" title="Geolocation and Google maps" />
            <published>2011-06-18T14:11:11+02:00</published>
            <updated>2011-06-18T14:11:11+02:00</updated>
            <id>http://friendlybit.com/js/geolocation-and-google-maps/</id>
            <author>
                <name>Emil Stenström</name>
            </author>
            <summary type="text">Image by heiwa4126 via Flickr Google Maps has has geolocation support for a long time, but I still find people surprised of how it all works. So here&#39;s a...</summary>
            <content type="html" xml:base="http://friendlybit.com/js/geolocation-and-google-maps/">
                &lt;div class=&#34;zemanta-img&#34;&gt;
  &lt;figure style=&#34;width: 240px&#34; class=&#34;wp-caption alignright&#34;&gt;&lt;a href=&#34;http://www.flickr.com/photos/57552634@N00/3791431635&#34;&gt;&lt;img title=&#34;Google Maps Marker in Tokyo&#34; src=&#34;/files/post-media/3791431635_c722c1d51a_m.jpg&#34; alt=&#34;Google Maps Marker in Tokyo&#34; width=&#34;240&#34; height=&#34;120&#34; /&gt;&lt;/a&gt;&lt;figcaption class=&#34;wp-caption-text&#34;&gt;Image by heiwa4126 via Flickr&lt;/figcaption&gt;&lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;Google Maps has has geolocation support for a long time, but I still find people surprised of how it all works. So here&#39;s a short writeup, skip it if you already know all about geolocation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Lets start&lt;/strong&gt; at the &lt;a title=&#34;Google Maps&#34; rel=&#34;homepage&#34; href=&#34;http://maps.google.com/&#34;&gt;Google Maps&lt;/a&gt; frontpage. Among the zoom controls, above the little old man, there&#39;s a button in the form of a small circle. It does not look like much, but what it&#39;s doing is incredibly cool. Click it and the map moves to where you are, with an accuracy of ~20 meters. Sci-fi crazy!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How does it work?&lt;/strong&gt; In the newer browsers (all but IE6, IE7, or IE8) may ask you for your positioning information from the browser. It usually shows up as a bar at the top of the browser. The browser then gathers two specific forms of positioning information from your computer: your &lt;strong&gt;IP address&lt;/strong&gt; and the &lt;strong&gt;signal strength of any wireless network&lt;/strong&gt; near you. That information is then sent, if you approve it, to Google, which returns the coordinates you are at the moment.&lt;/p&gt;
&lt;p&gt;Google can do this because they&#39;ve been driving around with their street view cars all over the world, and measured signal strength of WiFi networks (public and private) at each specific location. Now they can use that information, combined with your local signal strength conditions, to triangulate their way to where you are. And it&#39;s both accurate and fast.&lt;/p&gt;
&lt;p&gt;If your wireless reciever is turned off, or you&#39;re at a stationary computer, all calculations are based on the IP number. These kind of lookups are quite arbitrary and inaccurate, I just get to the nearest big city when trying to use it over a non-wireless line. But mobile connections are slowly taking over landlines, so I guess this problem will solve itself automatically.&lt;/p&gt;
&lt;p&gt;The best kind of technology throws you forward in time, and make your realize that we&#39;re actually making progress. Geolocation has feel to it.&lt;/p&gt;
&lt;p&gt;Looking for the techy explaination of geolocation? Have a look at &lt;a href=&#34;http://robertnyman.com/2010/03/15/geolocation-in-web-browsers-to-find-location-google-maps-examples/&#34;&gt;Robert Nyman&#39;s writeup&lt;/a&gt;.&lt;/p&gt;

            </content>
        </entry>
    
        <entry>
            <title type="html">Animate from one element to another (jQuery plugin)</title>
            <link href="http://friendlybit.com/js/animate-from-one-element-to-another-jquery-plugin/" rel="alternate" type="text/html" title="Animate from one element to another (jQuery plugin)" />
            <published>2011-05-05T22:52:14+02:00</published>
            <updated>2011-05-05T22:52:14+02:00</updated>
            <id>http://friendlybit.com/js/animate-from-one-element-to-another-jquery-plugin/</id>
            <author>
                <name>Emil Stenström</name>
            </author>
            <summary type="text">Image via Wikipedia Have you even tried clicking an &#34;Add to cart&#34;-button and not understood what happened? I have. An although I understand the idea of...</summary>
            <content type="html" xml:base="http://friendlybit.com/js/animate-from-one-element-to-another-jquery-plugin/">
                &lt;div class=&#34;zemanta-img&#34;&gt;
  &lt;figure style=&#34;width: 215px&#34; class=&#34;wp-caption alignright&#34;&gt;&lt;a href=&#34;http://commons.wikipedia.org/wiki/File:Stencil_shopping_cart.jpg&#34;&gt;&lt;img title=&#34;Stencil of a shopping cart with the head of th...&#34; src=&#34;/files/post-media/300px-Stencil_shopping_cart31.jpg&#34; alt=&#34;Stencil of a shopping cart with the head of th...&#34; width=&#34;215&#34; height=&#34;162&#34; /&gt;&lt;/a&gt;&lt;figcaption class=&#34;wp-caption-text&#34;&gt;Image via Wikipedia&lt;/figcaption&gt;&lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;Have you even tried clicking an &amp;quot;Add to cart&amp;quot;-button and not understood what happened? I have. An although I understand the idea of adding a product to the cart, and then letting the user continue browsing from where he is, I still get stumped when &amp;quot;nothing happens&amp;quot; when I click the button. So what to do?&lt;/p&gt;
&lt;p&gt;Simple: Add a &lt;strong&gt;animation from the add button to the cart&lt;/strong&gt;. That way you communicate what just happened. &amp;quot;The product moved in there, and by clicking the cart you&#39;ll find it again. Now continue buying stuff!&amp;quot;.&lt;/p&gt;
&lt;p&gt;Somebody must have done this before, so I started looking for a &lt;a class=&#34;zem_slink&#34; title=&#34;JQuery&#34; rel=&#34;homepage&#34; href=&#34;http://jquery.com/&#34;&gt;jQuery&lt;/a&gt; plugin to do this (jQuery was already on the page, so why not?). I found add2cart - A plugin that looks good, but that misses a few features I wanted:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;animation duration&lt;/strong&gt; is set in seconds, meaning products further down the page will move faster than those further up. I wanted constant speed.&lt;/li&gt;
&lt;li&gt;It didn&#39;t allow me to &lt;strong&gt;customize the look&lt;/strong&gt; of the animated element.&lt;/li&gt;
&lt;li&gt;The code rely on concatenating strings of CSS and generally could use &lt;strong&gt;lots of improvement&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So I did what anyone would do: rewrote the code from scratch, and posted it on GitHub.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Get the code: &lt;a href=&#34;https://github.com/EmilStenstrom/jQuery-animate_from_to&#34;&gt;Animate From To 1.0&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;It&#39;s my first jQuery plugin ever, and my first public GitHub project, so let me know if I&#39;ve done something wrong. Is this something that could be useful in one of your projects?&lt;/p&gt;

            </content>
        </entry>
    
        <entry>
            <title type="html">Will newspapers die?</title>
            <link href="http://friendlybit.com/modern-web/will-newspapers-die/" rel="alternate" type="text/html" title="Will newspapers die?" />
            <published>2011-02-09T00:41:17+01:00</published>
            <updated>2011-02-09T00:41:17+01:00</updated>
            <id>http://friendlybit.com/modern-web/will-newspapers-die/</id>
            <author>
                <name>Emil Stenström</name>
            </author>
            <summary type="text">A journalism student in Toronto, Canada, asked me some questions via e-mail about my old article about newspapers and online reading. Instead of just...</summary>
            <content type="html" xml:base="http://friendlybit.com/modern-web/will-newspapers-die/">
                &lt;p&gt;A journalism student in Toronto, Canada, asked me some questions via e-mail about my old article about &lt;a href=&#34;/modern-web/why-people-skip-newspapers-and-read-news-on-the-web-instead/&#34;&gt;newspapers and online reading&lt;/a&gt;. Instead of just sending an e-mail out in the void I thought I&#39;d persist my answers here:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;Do you think it would be best for newspaper publications to get rid of their print and go strictly online only? Why or why not?&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;I think they should keep their print papers for a while longer. It&#39;s a simple question of profit and catering to what users want. If you have thousands of users that want their paper, and it&#39;s economically justifiable, just keep it.&lt;/p&gt;
&lt;p&gt;The tricky part is determining how much loss in paper subscribers you should tolerate before shutting down the huge printing presses. I have no good answer to that other than to know your numbers. Paper profit needs to cover paper printing and distribution. Journalism costs can be split between channels. Do the math.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;Many people believe newspapers are dying and eventually will no longer exist. What are your thoughts on this?&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;I think they will persist, just not their paper versions. Many of them are looking at dwindling subscription numbers, year after year, and are trying to figure out what to do. How do you move to the digital worlds while keeping quality, relevance, and profit.&lt;/p&gt;
&lt;p&gt;The iPad is one way to get people to pay for news again, and some newspapers are seeing success doing that. Over a longer haul just the iPad won&#39;t be enough, you need to be able to deliver your news to wherever your customers want them. Generally, this means being able to get your content on all kinds of devices, both mobile and desktop. The only format that can really do that, and still deliver a somewhat consistent design across devices, is HTML and CSS. Building one native app for each platform is just not worth it.&lt;/p&gt;
&lt;p&gt;So it&#39;s simple really: newspapers that figure out how to transition to digital (while still getting paid enough to afford quality journalism) will persist, all others will (slowly) die. This means an end to both paper-only newspapers, and those that fail to find the right payment model.&lt;/p&gt;

            </content>
        </entry>
    
        <entry>
            <title type="html">HTML5 is not an option</title>
            <link href="http://friendlybit.com/html/html5-is-not-an-option/" rel="alternate" type="text/html" title="HTML5 is not an option" />
            <published>2010-11-03T20:20:42+01:00</published>
            <updated>2010-11-03T20:20:42+01:00</updated>
            <id>http://friendlybit.com/html/html5-is-not-an-option/</id>
            <author>
                <name>Emil Stenström</name>
            </author>
            <summary type="text">HTML5 is HOT! Developers all over the world are adapting their sites, browsers are catching up, and new fallback solutions are released every day. But many...</summary>
            <content type="html" xml:base="http://friendlybit.com/html/html5-is-not-an-option/">
                &lt;p&gt;HTML5 is HOT! Developers all over the world are adapting their sites, browsers are catching up, and new fallback solutions are released every day. But many developers misunderstand one thing:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;You can&#39;t choose to use HTML5 or not, your site will be parsed as HTML5 no matter what.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img class=&#34;alignnone size-full wp-image-656&#34; style=&#34;max-width: 100%;&#34; title=&#34;no-choice&#34; src=&#34;/files/post-media/no-choice-e1288811809935.jpg&#34; alt=&#34;Voting is pointless. I am your leader now. It is useless to resist me.&#34; /&gt;&lt;/p&gt;
&lt;p&gt;The reason is simple, HTML5 is made to be backwards compatible with the current web, so browsers don&#39;t need to keep their current parsers. All of them have soon switched to HTML5 parsers. You want to continue using HTML4? Not possible.&lt;/p&gt;
&lt;p&gt;Now. You &lt;strong&gt;can&lt;/strong&gt; choose whether you want to use the new features or not: New semantic tags, Microdata attributes, new form field types, accessibility features, 10-15 new JavaScript API:s (depending on how you count). Lots of new interesting stuff to learn.&lt;/p&gt;
&lt;p&gt;So, go read up on HTML5 if you haven&#39;t already, but don&#39;t think you can keep using HTML4. Your site is being switched over as we speak.&lt;/p&gt;

            </content>
        </entry>
    
        <entry>
            <title type="html">Lazy Loading Asyncronous Javascript</title>
            <link href="http://friendlybit.com/js/lazy-loading-asyncronous-javascript/" rel="alternate" type="text/html" title="Lazy Loading Asyncronous Javascript" />
            <published>2010-05-08T00:15:06+02:00</published>
            <updated>2010-05-08T00:15:06+02:00</updated>
            <id>http://friendlybit.com/js/lazy-loading-asyncronous-javascript/</id>
            <author>
                <name>Emil Stenström</name>
            </author>
            <summary type="text">Update: This is no longer the best way to load scripts. Use a script tag with async and defer set instead. Like many of you might know, I&#39;m working on a...</summary>
            <content type="html" xml:base="http://friendlybit.com/js/lazy-loading-asyncronous-javascript/">
                &lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; This is no longer the best way to load scripts. Use a &lt;a href=&#34;https://www.igvita.com/2014/05/20/script-injected-async-scripts-considered-harmful/&#34;&gt;script tag with async and defer set&lt;/a&gt; instead.&lt;/p&gt;
&lt;p&gt;Like many of you might know, I&#39;m working on a site called &lt;a href=&#34;http://kundo.se&#34;&gt;Kundo&lt;/a&gt; with a couple of friends. It&#39;s kinda like a Swedish version of &lt;a href=&#34;http://getsatisfaction.com/&#34;&gt;Getsatisfaction&lt;/a&gt;, which means we have a javascript snippet that people add to their site to get feedback functionality. Cut-and-paste instead of writing the code yourself. Simple.&lt;/p&gt;
&lt;p&gt;The problem is, how do you load an external javascript with minimal impact on your customer&#39;s sites? Here are my requirements:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Small&lt;/strong&gt;. I don&#39;t want a big mess for them to include on their sites. 10-15 lines, tops.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stand-alone&lt;/strong&gt;. The environment is unknown, so we can&#39;t rely on any external dependencies, like javascript libraries.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cross-browser&lt;/strong&gt;. I have no idea what browsers my customer&#39;s customers have, so I can&#39;t do anything modern or fancy that isn&#39;t backwards compatible. I assume at least IE6 and up though.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Asynchronous download&lt;/strong&gt;. The download of my script should not block the download of any script on their sites.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lazy Loading&lt;/strong&gt;. If my site is temporarily slow, I don&#39;t want to block the onload event from triggering until after our site responds.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Preserve events&lt;/strong&gt;. Any events used should not override any events on the customer&#39;s site. Minimal impact, like I said.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Don&#39;t pollute namespace&lt;/strong&gt;. Global variables should be avoided, since they could conflict with existing javascript.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Note: I did not make all of this up myself. Lots of people did, I&#39;m just writing it down for you. Thanks: Jonatan, &lt;a href=&#34;http://stevenbenner.com/&#34;&gt;Steven&lt;/a&gt;, &lt;a href=&#34;http://fleecelabs.se/&#34;&gt;Peter&lt;/a&gt;, and &lt;a href=&#34;http://hanssonlarsson.se/&#34;&gt;Linus&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;script-tag&#34;&gt;Script tag&lt;a href=&#34;#script-tag&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&#34;highlight&#34; data-language=&#34;HTML&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;script&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;src&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;quot;http://yourdomain.com/script.js&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;script&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;While being the stand-alone, cross-browser, and the shortest piece of code possible; it doesn&#39;t download asynchronously and doesn&#39;t lazy load. &lt;strong&gt;Fail&lt;/strong&gt;.&lt;/p&gt;
&lt;div style=&#34;overflow: auto;&#34;&gt;
  &lt;img class=&#34;alignnone size-full wp-image-619&#34; title=&#34;Firebug screenshoot with script tag&#34; src=&#34;/files/post-media/script11.png&#34; alt=&#34;&#34; width=&#34;725&#34; height=&#34;70&#34; /&gt;
&lt;/div&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Screenshot from Firebug&#39;s net console:&lt;/strong&gt; The script (set to load in 2 seconds) blocks the download of the big image (added after the above script tag, and used throughout this article as a test). Onload event (the red line) triggers after 2.46 seconds.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&#34;async-pattern-a-script-tag-written-with-javascrip&#34;&gt;Async pattern (A script tag written with javascript)&lt;a href=&#34;#async-pattern-a-script-tag-written-with-javascrip&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;http://stevesouders.com&#34;&gt;Steve Souders&lt;/a&gt;, the web performance guru, has compiled a decision tree over &lt;a href=&#34;http://stevesouders.com/efws/images/0405-load-scripts-decision-tree-04.gif&#34;&gt;different ways to achieve non-blocking downloads&lt;/a&gt;. Have a look at that graph.&lt;/p&gt;
&lt;p&gt;Since we&#39;re on a different domain, and only have one script (order doesn&#39;t matter), the solution is given: We should create a script tag with inline javascript, and append it to the document. Voila! Non-blocking download!&lt;/p&gt;
&lt;div class=&#34;highlight&#34; data-language=&#34;JS&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;document&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;createElement&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;script&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;text/javascript&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;async&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;src&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;http://yourdomain.com/script.js&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;document&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;getElementsByTagName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;script&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)[&lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;parentNode&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;insertBefore&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;})();&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: &lt;code&gt;async&lt;/code&gt; is a &lt;a href=&#34;http://www.whatwg.org/specs/web-apps/current-work/#attr-script-async&#34;&gt;HTML5 attribute&lt;/a&gt;, doing exactly what we&#39;re trying to simulate with our hack, so it&#39;s added for good measure. Also, wrapping the code in an anonymous function prevents any variables to leak out to the rest of the document.&lt;/p&gt;
&lt;p&gt;This is a pattern that is getting more and more popular nowadays, especially since &lt;a href=&#34;http://code.google.com/apis/analytics/docs/tracking/asyncTracking.html&#34;&gt;Google Analytics uses it&lt;/a&gt;. But there&#39;s an important distinction here: The above snipped &lt;strong&gt;blocks onload from triggering&lt;/strong&gt; until the referenced script is fully loaded. &lt;strong&gt;Fail&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update 2010-09-01&lt;/strong&gt;: &lt;a href=&#34;#comment-34047&#34;&gt;Steve Souders&lt;/a&gt; adds that the above is only true for Firefox, Chrome, and Safari, but not IE and Opera. So for a IE-only site, this might be the best method.&lt;/p&gt;
&lt;div style=&#34;overflow: auto;&#34;&gt;
  &lt;img class=&#34;alignnone size-full wp-image-617&#34; title=&#34;Firefox screenshoot with the async pattern&#34; src=&#34;/files/post-media/asyncload11.png&#34; alt=&#34;&#34; width=&#34;726&#34; height=&#34;69&#34; /&gt;
&lt;/div&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Screenshot from Firebug&#39;s net console:&lt;/strong&gt; The script (set to load in 2 seconds) downloads in parallell with the big image. Onload event (the red line) triggers after 2.02 seconds.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&#34;lazy-load-pattern-async-pattern-triggered-onload&#34;&gt;Lazy load pattern (Async pattern triggered onload)&lt;a href=&#34;#lazy-load-pattern-async-pattern-triggered-onload&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;So, how to you make sure you don&#39;t block onload? Well, you wrap your code inside a function that&#39;s called on load. When the onload event triggers, you know you haven&#39;t blocked it.&lt;/p&gt;
&lt;div class=&#34;highlight&#34; data-language=&#34;JS&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;window&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;onload&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;document&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;createElement&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;script&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;text/javascript&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;async&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;src&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;http://yourdomain.com/script.js&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;document&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;getElementsByTagName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;script&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)[&lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;parentNode&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;insertBefore&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This works, but it overrides the onload event of the site that uses the script. This could be OK in some cases, where you have control over the site referencing the script, but I need to cater for that. &lt;strong&gt;Fail&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&#34;unobtrusive-lazy-load-pattern&#34;&gt;Unobtrusive lazy load pattern&lt;a href=&#34;#unobtrusive-lazy-load-pattern&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The logical solution to the above problem is to use an incarnation of addEvent. addEvent is simply a common name for an cross browser way to take the current function tied to onload, add it to a queue, add your function to the queue, and tie the queue to the onload event. So which version of addEvent should we use?&lt;/p&gt;
&lt;p&gt;There&#39;s been competitions for writing a &lt;a href=&#34;http://www.quirksmode.org/blog/archives/2005/10/_and_the_winner_1.html&#34;&gt;short and compact version of addEvent&lt;/a&gt;, and the winner of that competition was John Resig, with this little beauty:&lt;/p&gt;
&lt;div class=&#34;highlight&#34; data-language=&#34;JS&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;addEvent&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;attachEvent&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;e&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(){&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;e&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;](&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;window&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;event&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);}&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;attachEvent&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;on&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]);&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;else&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;addEventListener&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: This is unsafe code, since it relies on serializing a function to a string, something that &lt;a href=&#34;http://my.opera.com/hallvors/blog/2007/03/28/a-problem-with-john-resigs-addevent&#34;&gt;Opera mobile browsers have disabled&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Thing is, we don&#39;t need all that generic event stuff, we&#39;re only dealing with onload here. So if we first replace the type attribute with hardcoded &#39;load&#39;, replace obj with &#39;window&#39;, and remove the fix for making &#39;this&#39; work in IE, we&#39;ve got four lines of code left. Let&#39;s combine this with the above lazy load pattern:&lt;/p&gt;
&lt;div class=&#34;highlight&#34; data-language=&#34;JS&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;async_load&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(){&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;document&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;createElement&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;script&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;text/javascript&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;async&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;src&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;http://yourdomain.com/script.js&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;document&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;getElementsByTagName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;script&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)[&lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;parentNode&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;insertBefore&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;window&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;attachEvent&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;window&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;attachEvent&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;onload&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;async_load&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;else&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;window&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;addEventListener&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;load&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;async_load&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;})();&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is exactly what we&#39;re looking for here. &lt;strong&gt;Finally!&lt;/strong&gt;&lt;/p&gt;
&lt;div style=&#34;overflow: auto;&#34;&gt;
  &lt;img class=&#34;alignnone size-full wp-image-618&#34; title=&#34;Firebug screenshoot with the lazy load pattern&#34; src=&#34;/files/post-media/lazyload11.png&#34; alt=&#34;&#34; width=&#34;726&#34; height=&#34;71&#34; /&gt;
&lt;/div&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Screenshot from Firebug&#39;s net console:&lt;/strong&gt; The script (set to load in 2 seconds) downloads after the onload event has triggered. Onload event (the red line) triggers after 0.41 seconds.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;And that wraps up this article: Different tactics works for different scenarios, and only understanding them all makes you capable of picking the right one for your problem. As always, I&#39;m waiting for your feedback in the comments. Thanks for reading!&lt;/p&gt;

            </content>
        </entry>
    
        <entry>
            <title type="html">My iPad – a short review</title>
            <link href="http://friendlybit.com/modern-web/my-ipad-a-short-review/" rel="alternate" type="text/html" title="My iPad – a short review" />
            <published>2010-04-25T20:33:32+02:00</published>
            <updated>2010-04-25T20:33:32+02:00</updated>
            <id>http://friendlybit.com/modern-web/my-ipad-a-short-review/</id>
            <author>
                <name>Emil Stenström</name>
            </author>
            <summary type="text">About two weeks ago, I got my hands on an iPad. For those of you who have been living under a rock for the last months, and iPad is something that looks...</summary>
            <content type="html" xml:base="http://friendlybit.com/modern-web/my-ipad-a-short-review/">
                &lt;p&gt;About two weeks ago, I got my hands on an iPad. For those of you who have been living under a rock for the last months, and &lt;a href=&#34;http://www.apple.com/ipad/&#34;&gt;iPad&lt;/a&gt; is something that looks like a big iPhone, but behaves much like a small laptop.&lt;/p&gt;
&lt;p&gt;Since people who just spent over $500 for a toy, are very subjective in their judging (buying something bad would make you look stupid!), you have to take this review for what it is: Me trying to justify buying an expensive toy. To help my mind balance things a bit, I&#39;m going to talk about two good things, and two bad things about the iPad.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;#ipad_ui&#34;&gt;&lt;strong&gt;Good&lt;/strong&gt;: Looks, UI and surfing the web&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#ipad_developer&#34;&gt;&lt;strong&gt;Bad&lt;/strong&gt;: A horrible Developer Platform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#ipad_games&#34;&gt;&lt;strong&gt;Good&lt;/strong&gt;: Games that use the touch interface&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#ipad_itunes&#34;&gt;&lt;strong&gt;Bad&lt;/strong&gt;: Downloading apps through iTunes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&#34;#ipad_summary&#34;&gt;Summary&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;ipad_ui&#34;&gt;Good: Looks, UI and surfing the web&lt;a href=&#34;#ipad_ui&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;figure id=&#34;attachment_597&#34; style=&#34;width: 300px&#34; class=&#34;wp-caption alignleft&#34;&gt;
  [&lt;img class=&#34;size-medium wp-image-597 &#34; title=&#34;iPad and Banana&#34; src=&#34;/files/post-media/ipad_and_banana-300x225.jpg&#34; alt=&#34;&#34; width=&#34;300&#34; height=&#34;225&#34;&gt;](/files/post-media/ipad_and_banana.jpg)
  &lt;figcaption class=&#34;wp-caption-text&#34;&gt;An iPad, here shown next to a old banana for comparison. Click for bigger image.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;The area where the iPad really shines is when it comes to the look and feel. It&#39;s simply gorgeous, feels good in your hand, and makes all your friends instantly drool (like you expect them to). The screen is as sharp (or sharper) than a computer monitor, and its brightness could light up a dark room.&lt;/p&gt;
&lt;p&gt;Working with it is just as pleasant. You click, swipe, pinch and type, like you&#39;ve done nothing nothing else in your life. As someone who haven&#39;t used an iPhone before, I&#39;m impressed. Those that have, say that the iPad is even snappier than the iPhone. Some application are of course better fit for a touch interface than others. Google Maps is one of them, when a large display combined with panning and zooming makes the old arrow and zoom buttons on the traditional Google Maps interface feel like stone-age.&lt;/p&gt;
&lt;p&gt;They keyboard surprised me by being even better than I expected. In landscape mode it&#39;s almost as large as a laptop keyboard, and in some cases even faster than a traditional keyboard since you don&#39;t have to push, just touch the buttons. The biggest keyboard problem though is of course that you usually only have one hand available. When you have two hands, it&#39;s great.&lt;/p&gt;
&lt;p&gt;Another area where the iPad really works is as &lt;strong&gt;a window to the web&lt;/strong&gt;. Safari comes pre-installed (Microsoft monopoly anyone?) but works like a charm. Pages load quickly, and swiping to scroll and pinch to zoom works just like it should.&lt;/p&gt;
&lt;p&gt;Web standards are well supported, like you expect from Safari. But some new problems emerge when looking at HTML5 demos though:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The Safari inside the iPad isn&#39;t as performant as a desktop browser. So there&#39;s lots of lagging when trying to rotate things in advanced ways.&lt;/li&gt;
&lt;li&gt;Even though Apple has decided to ban Flash and pick a fight with Adobe, many new apps are built to take advantage of the mouse moving around. On the iPad, there&#39;s no mouse pointer, so you have to simulate one by touching the screen and moving the finger around.&lt;/li&gt;
&lt;li&gt;Keyboard navigation is strange, because most of the time, you don&#39;t have a keyboard. The keyboard only shows up only when you focus a text field, at other times, you&#39;re lost.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These things sound like annoyances, and they are, but they are also quite minor. For all the surfing you usually do, the iPad works just like it should.&lt;/p&gt;
&lt;h2 id=&#34;ipad_developer&#34;&gt;Bad: A horrible Developer Platform&lt;a href=&#34;#ipad_developer&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;As you&#39;ve probably heard during the last few weeks, Apple have no clue whatsoever about how to cultivate a vibrant developer community. Let&#39;s look at the facts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;To distribute your app you need to join the iPhone Developer Program, that costs you $99-$299 per year.&lt;/li&gt;
&lt;li&gt;To download the SDK you need to fill out a huge multi-step form (where almost all fields are required), and where you&#39;re asked to give away lots of personal information.&lt;/li&gt;
&lt;li&gt;When you &amp;quot;publish&amp;quot; your app it has to go through a manual approval process, which could take months if you are unlucky.&lt;/li&gt;
&lt;li&gt;You need to approve to a ridiculous legal statement preventing you from using some programming languages, and making calls to some APIs.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To quote:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;quot;3.3.1 — Applications may only use Documented APIs in the manner prescribed by Apple and must not use or call any private APIs. Applications must be originally written in Objective-C, C, C++, or JavaScript as executed by the iPhone OS WebKit engine, and only code written in C, C++, and Objective-C may compile and directly link against the Documented APIs (e.g., Applications that link to Documented APIs through an intermediary translation or compatibility layer or tool are prohibited).&amp;quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Needless to say, &lt;strong&gt;I&#39;m never going to be develop anything for the Apple Platform&lt;/strong&gt;. Apple and its developers is much like one big company (with strict rules). The only difference is that instead of a salary the developers get to pay to work for them. Good for Apple, horrible for developers.&lt;/p&gt;
&lt;h2 id=&#34;ipad_games&#34;&gt;Good: Games that use the touch interface&lt;a href=&#34;#ipad_games&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There are lots of good games that have started to take advantage of the big touch surface to make fun interaction styles possible.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;http://itunes.apple.com/us/app/tap-tap-radiation/id364160328?mt=8&#34;&gt;Tap Tap Radiation&lt;/a&gt; builds on you tapping the screen in pace with the music playing. The place to hit move around, just like the markers that indicate where to tap.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://itunes.apple.com/us/app/id363998989?mt=8&#34;&gt;Asphalt 5&lt;/a&gt; is a beautiful racing game where you use the iPad as a steering wheel and tilt it back and fourth to steel. Since the accelerometer is so sensitive it works remarkably well.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://itunes.apple.com/se/app/n-o-v-a-near-orbit-vanguard/id343596730?mt=8&#34;&gt;N.O.V.A. - Near Orbit Vanguard Alliance&lt;/a&gt; is a first person shooter that uses gestures to control who you shoot and how. Advanced gestures makes it possible to throw grenades that fly around corners, and targeting multiple enemies at once.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Simply put, there are lots of fun games for the iPad, and the reason is experimentation with the touch interface. Combine this with several days of battery power, and you have many hours of fun ahead of you.&lt;/p&gt;
&lt;h2 id=&#34;ipad_itunes&#34;&gt;Bad: Downloading apps through iTunes&lt;a href=&#34;#ipad_itunes&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;figure id=&#34;attachment_601&#34; style=&#34;width: 152px&#34; class=&#34;wp-caption alignleft&#34;&gt;
  [&lt;img class=&#34;size-medium wp-image-601&#34; title=&#34;iPad closeup&#34; src=&#34;/files/post-media/ipad_closeup-225x300.jpg&#34; alt=&#34;&#34; width=&#34;152&#34; height=&#34;203&#34;&gt;](/files/post-media/ipad_closeup.jpg)
  &lt;figcaption class=&#34;wp-caption-text&#34;&gt;A closer look at the iPad, still with the banana present. Click for bigger image.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Sadly, the only way to get apps legally into the iPad is through iTunes. Since my iPad was bought in the US, the App Store icon leads to a &amp;quot;Your request could not be completed&amp;quot; with no further info. So it seems that the App Store is only launched in the US. Silly me, that thought the web was an international thing.&lt;/p&gt;
&lt;p&gt;Anyway, it is possible to get apps into the iPad without getting a fake american App Store account. You just use iTunes to download the apps to a desktop computer, and then sync your app to the iPad. It&#39;s easy.&lt;/p&gt;
&lt;p&gt;Well, strike that, using the iTunes store is not easy. Not being a previous iPhone using I had thought that downloading apps from iTunes was like shopping on Amazon. &lt;strong&gt;IT&#39;S NOT EVEN CLOSE&lt;/strong&gt;. This is how Apple built iTunes:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Take one  fully functional e-commerce web site.&lt;/li&gt;
&lt;li&gt;Make it slow so that people like me that surf on a 100 Mbit/s fibre connection have to wait several seconds after each click.&lt;/li&gt;
&lt;li&gt;Make sure you can only access the store through a special downloadable app, that requires you sign several legal agreements, and leave away personal information, before you even bought anything.&lt;/li&gt;
&lt;li&gt;Remove the possibility to sort through listings, remove any information what the list is currently sorted by, and hide options to only show apps for the device they know you just connected (the iPad).&lt;/li&gt;
&lt;li&gt;Tada! You&#39;ve built iTunes!&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Needless to say, each visit to the iTunes App Store is a bad experience, and no something you want to do often.&lt;/p&gt;
&lt;h2 id=&#34;ipad_summary&#34;&gt;Summary&lt;a href=&#34;#ipad_summary&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Situations where I see myself using my iPad:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In bed, watching public television through Svtplay. They have a great app, that is mainly adapted for iPhone, but scales up when used on a bigger screen.&lt;/li&gt;
&lt;li&gt;Casually surfing the web from the couch. This includes checking my e-mail (which is IMAP over SSL, which works fine), checking my feeds through Google Reader, checking Facebook through the iPhone app (which can be pixel-doubled to fit the screen nicely), and twitter through twitterific.&lt;/li&gt;
&lt;li&gt;Surfing all kinds of maps, and wikipedia in some social context. If I ever get into an argument about where a country is exactly, I&#39;m pull up my iPad instantly and check it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And that concludes this iPad review.&lt;/p&gt;
&lt;figure id=&#34;attachment_607&#34; style=&#34;width: 300px&#34; class=&#34;wp-caption alignnone&#34;&gt;
  [&lt;img class=&#34;size-medium wp-image-607&#34; title=&#34;iPad turned off&#34; src=&#34;/files/post-media/ipad_off_and_banana-300x225.jpg&#34; alt=&#34;&#34; width=&#34;300&#34; height=&#34;225&#34;&gt;](/files/post-media/ipad_off_and_banana.jpg)
  &lt;figcaption class=&#34;wp-caption-text&#34;&gt;iPad rests here, next to banana.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;As usual I&#39;m looking forward to &lt;strong&gt;your thoughts in the comments&lt;/strong&gt;. Thanks for reading!&lt;/p&gt;

            </content>
        </entry>
    
        <entry>
            <title type="html">Rendering a web page – step by step</title>
            <link href="http://friendlybit.com/css/rendering-a-web-page-step-by-step/" rel="alternate" type="text/html" title="Rendering a web page – step by step" />
            <published>2010-01-11T01:04:33+01:00</published>
            <updated>2010-01-11T01:04:33+01:00</updated>
            <id>http://friendlybit.com/css/rendering-a-web-page-step-by-step/</id>
            <author>
                <name>Emil Stenström</name>
            </author>
            <summary type="text">Have you ever thought about what happens when you surf the web? It&#39;s not as simple as it seems: You type an URL into address bar in your preferred browser....</summary>
            <content type="html" xml:base="http://friendlybit.com/css/rendering-a-web-page-step-by-step/">
                &lt;p&gt;Have you ever thought about what happens when you surf the web? It&#39;s not as simple as it seems:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;You &lt;strong&gt;type an URL&lt;/strong&gt; into address bar in your preferred browser.&lt;/li&gt;
&lt;li&gt;The browser &lt;strong&gt;parses the URL&lt;/strong&gt; to find the protocol, host, port, and path.&lt;/li&gt;
&lt;li&gt;It &lt;strong&gt;forms a HTTP request&lt;/strong&gt; (that was most likely the protocol)&lt;/li&gt;
&lt;li&gt;To reach the host, it first needs to &lt;strong&gt;translate&lt;/strong&gt; the human readable host &lt;strong&gt;into an IP number&lt;/strong&gt;, and it does this by doing a DNS lookup on the host&lt;/li&gt;
&lt;li&gt;Then a &lt;strong&gt;socket needs to be opened&lt;/strong&gt; from the user&#39;s computer to that IP number, on the port specified (most often port 80)&lt;/li&gt;
&lt;li&gt;When a connection is open, the &lt;strong&gt;HTTP request is sent&lt;/strong&gt; to the host&lt;/li&gt;
&lt;li&gt;The host &lt;strong&gt;forwards the request&lt;/strong&gt; to the server software (most often Apache) configured to listen on the specified port&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;server inspects the request&lt;/strong&gt; (most often only the path), and &lt;strong&gt;launches the server plugin needed&lt;/strong&gt; to handle the request (corresponding to the server language you use, PHP, Java, .NET, Python?)&lt;/li&gt;
&lt;li&gt;The plugin gets access to the full request, and starts to prepare a HTTP response.&lt;/li&gt;
&lt;li&gt;To construct the response a &lt;strong&gt;database&lt;/strong&gt; is (most likely) &lt;strong&gt;accessed&lt;/strong&gt;. A database search is made, based on parameters in the path (or data) of the request&lt;/li&gt;
&lt;li&gt;Data from the database, together with other information the plugin decides to add, is &lt;strong&gt;combined into a long string&lt;/strong&gt; of text (probably HTML).&lt;/li&gt;
&lt;li&gt;The plugin &lt;strong&gt;combines&lt;/strong&gt; that data with some meta data (in the form of HTTP headers), and &lt;strong&gt;sends the HTTP response&lt;/strong&gt; back to the browser.&lt;/li&gt;
&lt;li&gt;The browser receives the response, and &lt;strong&gt;parses the HTML&lt;/strong&gt; (which with 95% probability is broken) in the response&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;DOM tree is built&lt;/strong&gt; out of the broken HTML&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;New requests are made&lt;/strong&gt; to the server for each new resource that is found in the HTML source (typically images, style sheets, and JavaScript files). Go back to step 3 and repeat for each resource.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stylesheets are parsed&lt;/strong&gt;, and the rendering information in each gets attached to the matching node in the DOM tree&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Javascript is parsed and executed&lt;/strong&gt;, and DOM nodes are moved and style information is updated accordingly&lt;/li&gt;
&lt;li&gt;The browser &lt;strong&gt;renders the page&lt;/strong&gt; on the screen according to the DOM tree and the style information for each node&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;You&lt;/strong&gt; &lt;strong&gt;see&lt;/strong&gt; the page on the screen&lt;/li&gt;
&lt;li&gt;You get annoyed the whole process was too slow.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I, too, get annoyed when the above steps take longer than one tenth of a second. But now at least I have some documentation to look at, while waiting the remaining fractions of a second before the page renders.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;http://www.flickr.com/photos/amboo213/115034446/sizes/s/&#34;&gt;&lt;img class=&#34;alignnone&#34; title=&#34;Spoiled dog - by amboo213 on Flickr&#34; src=&#34;http://farm1.static.flickr.com/45/115034446_8bf43c2273_m.jpg&#34; alt=&#34;Spoiled dog&#34; width=&#34;240&#34; height=&#34;180&#34; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Spoiled we are, all of us.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(Feel free to add more steps, through the comments…)&lt;/em&gt;&lt;/p&gt;

            </content>
        </entry>
    
        <entry>
            <title type="html">Position: fixed CSS templates</title>
            <link href="http://friendlybit.com/css/position-fixed-css-templates/" rel="alternate" type="text/html" title="Position: fixed CSS templates" />
            <published>2009-12-31T15:14:26+01:00</published>
            <updated>2009-12-31T15:14:26+01:00</updated>
            <id>http://friendlybit.com/css/position-fixed-css-templates/</id>
            <author>
                <name>Emil Stenström</name>
            </author>
            <summary type="text">In 2006 I wrote an article about simulating Frames and Iframes and from time to time, I get questions of how to make modifications to the templates...</summary>
            <content type="html" xml:base="http://friendlybit.com/css/position-fixed-css-templates/">
                &lt;p&gt;In 2006 I wrote an article about &lt;a href=&#34;/css/frames-or-iframes-with-css/&#34;&gt;simulating Frames and Iframes&lt;/a&gt; and from time to time, I get questions of how to make modifications to the templates presented. But one big thing has changed since 2006: &lt;strong&gt;Perfect support for IE6 is no longer mandatory&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;So yesterday, when Brandon Cobb of &lt;a href=&#34;http://superfighter.com/&#34;&gt;Super Fighter Team&lt;/a&gt; asked about a design with a fixed header, fixed left column, and scrolling right column, I thought I&#39;d renew my take on &lt;a href=&#34;/css/frames-or-iframes-with-css/&#34;&gt;simulating frames&lt;/a&gt; with CSS (The original article is still good for background information, so I still recommend reading it).&lt;/p&gt;
&lt;h2 id=&#34;position-fixed-css-templates&#34;&gt;Position: fixed CSS templates&lt;a href=&#34;#position-fixed-css-templates&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The idea is this: Instead of specifying what parts of the page should scroll, we can &lt;strong&gt;specify what parts should stay fixed when scrolling&lt;/strong&gt;. This makes it easier to deal with several fixed parts of the same layout, but also allows us do to things frames cannot do.&lt;/p&gt;
&lt;p&gt;So what&#39;s the trick? Well, &lt;code&gt;position: fixed&lt;/code&gt; does exactly what we want. It works just like &lt;code&gt;position: absolute&lt;/code&gt;, but it stays still when the page is scrolled. So it&#39;s really just a matter of making sure things don&#39;t overlap.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;/files/fixed/fixedtopandleft.html&#34; data-no-instant&gt;Demo: Fixed top, fixed left, scrolling right&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/files/fixed/fixedtop.html&#34; data-no-instant&gt;Demo: Fixed top, scrolling bottom&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/files/fixed/fixedleft.html&#34; data-no-instant&gt;Demo: Fixed left, scrolling right&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Try resizing the page, and see how the scrolling works&lt;/strong&gt;. The templates have been tested in: Fx 3.5, IE8, IE7, IE6 (see note below), Opera 10, Safari 4; all on Windows. Let me know if you can test it on more browsers, or find bugs.&lt;/p&gt;
&lt;p&gt;You&#39;re of course free to use this templates as you see fit, with or without a link back, commercially or not. Consider it public domain.&lt;/p&gt;
&lt;h2 id=&#34;fixes-for-ie6&#34;&gt;Fixes for IE6&lt;a href=&#34;#fixes-for-ie6&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;As I&#39;ve said, these designs don&#39;t act the same in IE6. Instead of some parts being fixed, the whole page scrolls in IE6. The good thing about this is that  &lt;strong&gt;IE6 users won&#39;t see that your site is &amp;quot;broken&amp;quot;&lt;/strong&gt;, they will just get another design. And as the number of IE6 users goes towards zero, your design will be more and more consistent :). The fallback is very simple, IE6 gets &lt;code&gt;position: absolute&lt;/code&gt; instead of fixed, using the &lt;a href=&#34;http://www.webdevout.net/css-hacks#in_css-important&#34;&gt;!important hack&lt;/a&gt; (you should probably use &lt;a href=&#34;http://www.quirksmode.org/css/condcom.html&#34;&gt;conditional comments&lt;/a&gt; instead).&lt;/p&gt;
&lt;p&gt;Hope these templates are useful for some of you!&lt;/p&gt;

            </content>
        </entry>
    
        <entry>
            <title type="html">Google support gone wrong</title>
            <link href="http://friendlybit.com/other/google-support-gone-wrong/" rel="alternate" type="text/html" title="Google support gone wrong" />
            <published>2009-10-24T15:00:28+02:00</published>
            <updated>2009-10-24T15:00:28+02:00</updated>
            <id>http://friendlybit.com/other/google-support-gone-wrong/</id>
            <author>
                <name>Emil Stenström</name>
            </author>
            <summary type="text">Google really produces great software. I use many of them: Web Search, Picasa, Reader, Feedburner, Analytics, Images, Groups, Docs, Translate, Code, Chrome,...</summary>
            <content type="html" xml:base="http://friendlybit.com/other/google-support-gone-wrong/">
                &lt;p&gt;Google really produces great software. I use many of them: Web Search, Picasa, Reader, Feedburner, Analytics, Images, Groups, Docs, Translate, Code, Chrome, Maps, Video, Blog Search, Youtube, AJAX API, Webmaster Central, and Site Search.&lt;/p&gt;
&lt;p&gt;Just to name a few :)&lt;/p&gt;
&lt;p&gt;Problem is, with many of the above, if something breaks you&#39;re out of luck. Because it&#39;s &lt;strong&gt;god damn impossible&lt;/strong&gt; to get a hold of someone that you can talk to. Do you reach anyone at Google with your e-mails? Does anyone from Google read your forum posts? Highly unlikely.&lt;/p&gt;
&lt;p&gt;Google is a great example of support gone wrong. I think the explanation is easy: Few of the programmers I know want to deal with support. They want to deal with coding! And since Google is a company of programmers, it doesn&#39;t want to do support.&lt;/p&gt;
&lt;p&gt;Let me tell two stories:&lt;/p&gt;
&lt;h2 id=&#34;feedburner&#34;&gt;Feedburner&lt;a href=&#34;#feedburner&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Feedburner once was a great company, with a simple but thought out service. They gave you subscriber statistics of your RSS feed. You just redirected your feed to them, and made all your subscribers sign up to their generated feed, and they did all the tricky calculations. Simple, efficient.&lt;/p&gt;
&lt;p&gt;As a company Feedburner understood the value of being personal. Messages throughout the site stated &amp;quot;My feeds like a little namespace to call their own&amp;quot;, &amp;quot;Sometimes your feed just wants to look good. Spruce it up in the following ways&amp;quot;, &amp;quot;Oh dear, what kind of trouble?&amp;quot;. Nice personal touch :) Feedburner developers where easy to get in touch with, an e-mail and you had a friendly, knowledgeable person to talk to.&lt;/p&gt;
&lt;p&gt;Then Google came along and bought the whole thing. Everything was rewritten to Google&#39;s platform, domains where switched, and chaos ensued. Of course, during that time, Feedburner&#39;s previous support people totally vanished, and everyone was directed to the Feedburner Status Blog, when you could confirm they where &lt;strong&gt;not&lt;/strong&gt; working on your problem. Just great.&lt;/p&gt;
&lt;p&gt;For three weeks now, my subscriber stats have jumped from 800 to 3000, back an forth, several times per week. Nice, isn&#39;t it?&lt;/p&gt;
&lt;img class=&#34;alignnone size-full wp-image-567&#34; title=&#34;feedburner_stats&#34; src=&#34;/files/post-media/feedburner_stats.PNG&#34; alt=&#34;feedburner_stats&#34; width=&#34;519&#34; height=&#34;165&#34;&gt;

&lt;p&gt;Feedburner is apparently broken, and the simple service of delivering feed statistics doesn&#39;t work.&lt;/p&gt;
&lt;p&gt;So I &lt;a href=&#34;http://groups.google.com/group/feedburner-statistics/browse_thread/thread/3989f82b9efc3b26/e0990145155dca15?lnk=gst&amp;amp;q=subscriber&amp;amp;pli=1&#34;&gt;post to their Google Group&lt;/a&gt;, the place where you should ask questions about the service. Three months later, several other users have reported having the same problem, but no reply from a Feedburner developer.&lt;/p&gt;
&lt;p&gt;Now I&#39;m thinking of switching. Anyone have a good alternative ready?&lt;/p&gt;
&lt;h2 id=&#34;google-wave&#34;&gt;Google Wave&lt;a href=&#34;#google-wave&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Services that start at Google are even less likely, compared to acquired services like Feedburner, to have proper support. It&#39;s just not a priority, and there&#39;s far too many interesting programming challenges to deal with users.&lt;/p&gt;
&lt;p&gt;So when Wave gives me an old picasa name as my username (&lt;code&gt;[company name].|place of one of our conferences]@googlewave.com&lt;/code&gt;), I know there&#39;s very little chance that I&#39;ll get help from anywhere. The official channel to ask for help is their support forum, but it doesn&#39;t seem that they reply to issues there. &lt;a href=&#34;http://www.google.com/support/forum/p/wave/thread?tid=6cb8ca45d22453e5&amp;amp;hl=en&#34;&gt;My message there from two weeks ago&lt;/a&gt; stands on it&#39;s own, with no replies.&lt;/p&gt;
&lt;p&gt;It&#39;s the proposed revolution that will replace e-mail, and I can&#39;t use it :(&lt;/p&gt;
&lt;h2 id=&#34;bad-trend&#34;&gt;Bad trend&lt;a href=&#34;#bad-trend&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This trend of Google not offering even basic support from dedicated people is a unfortunate development, that should be taken seriously by executives at Google, and dealt with at a very high level.&lt;/p&gt;
&lt;p&gt;Launching new web services is easy, improving a services based on feedback is much harder. Google has not yet managed to crack that nut.&lt;/p&gt;

            </content>
        </entry>
    
        <entry>
            <title type="html">Google Chrome as an Internet Explorer plugin</title>
            <link href="http://friendlybit.com/browsers/google-chrome-as-an-internet-explorer-plugin/" rel="alternate" type="text/html" title="Google Chrome as an Internet Explorer plugin" />
            <published>2009-09-23T16:15:49+02:00</published>
            <updated>2009-09-23T16:15:49+02:00</updated>
            <id>http://friendlybit.com/browsers/google-chrome-as-an-internet-explorer-plugin/</id>
            <author>
                <name>Emil Stenström</name>
            </author>
            <summary type="text">I just encountered a quite mindboggling piece of news on the Google Wave blog. It seems they have decided not to support Internet Explorer at all. Not IE6,...</summary>
            <content type="html" xml:base="http://friendlybit.com/browsers/google-chrome-as-an-internet-explorer-plugin/">
                &lt;p&gt;I just encountered a quite mindboggling piece of news on the Google Wave blog. It seems they have decided &lt;strong&gt;not to support Internet Explorer at all&lt;/strong&gt;. Not IE6, IE7 or IE8. Surprised? I sure was. What&#39;s even better is that to still give these users the opportunity to use Google Wave, they&#39;ve built a plugin for Internet Explorer, that loads the &lt;a href=&#34;http://googlewavedev.blogspot.com/2009/09/google-wave-in-internet-explorer.html&#34;&gt;Chrome browser with Google Wave inside it&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What?&lt;/strong&gt; Load a browser as a plugin inside another one? I guess this is a real war after all? Who will build the first browser to load Firefox into Chrome? Internet Explorer inside Firefox?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why?&lt;/strong&gt; Well, now they don&#39;t have to care about Internet Explorer support at all. Felt that heavy weight lift from your shoulders? Suddenly large parts of HTML5, CSS3, and modern javascript, is available to them, with no backporting and performance tuning needed. Must be fabulous.&lt;/p&gt;
&lt;p&gt;Now. Combine that with the possibility for the developer to &lt;a href=&#34;http://blog.chromium.org/2009/09/introducing-google-chrome-frame.html&#34;&gt;choose when to trigger the different browsers&lt;/a&gt;. We&#39;re up for a whole new ride. You&#39;re invited.&lt;/p&gt;

            </content>
        </entry>
    
        <entry>
            <title type="html">SpriteMe – Combine images and get fewer HTTP requests</title>
            <link href="http://friendlybit.com/css/spriteme-combine-images-and-get-fewer-http-requests/" rel="alternate" type="text/html" title="SpriteMe – Combine images and get fewer HTTP requests" />
            <published>2009-09-19T17:09:02+02:00</published>
            <updated>2009-09-19T17:09:02+02:00</updated>
            <id>http://friendlybit.com/css/spriteme-combine-images-and-get-fewer-http-requests/</id>
            <author>
                <name>Emil Stenström</name>
            </author>
            <summary type="text">Those of you that care about website performance have probably read about combining images, something that&#39;s called &#34;CSS sprites&#34;. The idea is that by using...</summary>
            <content type="html" xml:base="http://friendlybit.com/css/spriteme-combine-images-and-get-fewer-http-requests/">
                &lt;p&gt;Those of you that care about website performance have probably read about combining images, something that&#39;s called &amp;quot;CSS sprites&amp;quot;. The idea is that by using the same (somewhat larger) image several times, you get fewer HTTP requests to your site, and therefore speed it up. Problem is, most of your images are CSS background images, that are positioned using clever background-positions and repeats.&lt;/p&gt;
&lt;p&gt;Now, this makes to tricky to combine images. Something that repeats horizontally can&#39;t be combined with something that repeats vertically (unless there&#39;s transparency involved), and wide images can&#39;t be combined with narrow ones. So combining is usually tedious, manual work, both to combine the images and then calculate the new background-positions required.&lt;/p&gt;
&lt;p&gt;Lucky for us then, that there is a tool called &lt;a href=&#34;http://spriteme.org/&#34;&gt;SpriteMe&lt;/a&gt;, available as a bookmarklet (a bookmark containing javascript), and as an excellent &lt;a href=&#34;http://spriteme.org/demo.php&#34;&gt;online demo&lt;/a&gt; that walks you through how it works. SpriteMe does all the hard work for you, you just have to copy and paste both the combined images, and the new background rules, to your site.&lt;/p&gt;
&lt;p&gt;Decreasing the number of HTTP requests have never been simpler. Well done &lt;a href=&#34;http://www.stevesouders.com/&#34;&gt;Steve Souders&lt;/a&gt;!&lt;/p&gt;

            </content>
        </entry>
    
        <entry>
            <title type="html">Voddler – movie streaming for the masses?</title>
            <link href="http://friendlybit.com/other/voddler-movie-streaming-for-the-masses/" rel="alternate" type="text/html" title="Voddler – movie streaming for the masses?" />
            <published>2009-08-31T00:12:46+02:00</published>
            <updated>2009-08-31T00:12:46+02:00</updated>
            <id>http://friendlybit.com/other/voddler-movie-streaming-for-the-masses/</id>
            <author>
                <name>Emil Stenström</name>
            </author>
            <summary type="text">I&#39;ve recently managed to get a hold of a beta invite to Voddler, and thought I should tell you a little about my experience of it. But first, big thanks to...</summary>
            <content type="html" xml:base="http://friendlybit.com/other/voddler-movie-streaming-for-the-masses/">
                &lt;p&gt;I&#39;ve recently managed to get a hold of a beta invite to &lt;a href=&#34;http://www.voddler.com/&#34;&gt;Voddler&lt;/a&gt;, and thought I should tell you a little about my experience of it. But first, big thanks to &lt;a href=&#34;http://twitter.com/GunnarBark&#34;&gt;Gunnar Bark&lt;/a&gt; (Tweets in Swedish, follow him!) who tipped me off about a Voddler invitation competition. It was run by Christian Rudolf at &lt;a href=&#34;http://www.mjukvara.se/blogg/&#34;&gt;mjukvara.se&lt;/a&gt; (In Swedish, but definitely something to add to your feeds) who had 15 invites to give away.&lt;/p&gt;
&lt;p&gt;Anyway.&lt;/p&gt;
&lt;p&gt;Two years ago, &lt;a href=&#34;/other/spotify-is-a-lot-like/&#34;&gt;I got to test Spotify&lt;/a&gt;, a music streaming that will soon launch in the US and is heavily anticipated. It completely changed how I listened to music, I deleted all my mp3:s and is now streaming all my music. &lt;a href=&#34;http://www.voddler.com/&#34;&gt;Voddler&lt;/a&gt; is a similar streaming service, but for movies.&lt;/p&gt;
&lt;p&gt;So, what do I think of it?&lt;/p&gt;
&lt;p&gt;First of all, Voddler is in a very early phase of testing. It&#39;s what I would call a &amp;quot;&lt;strong&gt;real beta&lt;/strong&gt;&amp;quot;, where the client crashes frequently, subtitles sometimes lag behind, the interface is tricky to use, and several features just don&#39;t work.&lt;/p&gt;
&lt;p&gt;Thing is, I&#39;m quite certain Voddler will &lt;strong&gt;revolutionize how I watch movies&lt;/strong&gt;, anyway. Because at the core of Voddler is HD-quality streaming, that just works. Believe me, I normally don&#39;t watch that many movies, but have seen &lt;strong&gt;four&lt;/strong&gt;, ****in the last 48 hours. They are just a couple of clicks away, and start in 10 seconds (during which you&#39;ll watch a commercial). Here&#39;s some &lt;a href=&#34;http://pappmaskin.no/2009/07/voddler-screenshots-and-details/&#34;&gt;screenshots&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Now. I&#39;m saying that the client is a beta, and that there are serious bugs left, what kind of bugs do I mean?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;About half of the &lt;strong&gt;menu options don&#39;t work&lt;/strong&gt;. &amp;quot;Tv series&amp;quot; lead nowhere, I can&#39;t browse movies by actor, director, studio, and several genres are empty. In my opinion, they would gain a lot by just &lt;strong&gt;removing those options until they lead somewhere&lt;/strong&gt;! This is a no-brainer.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;client freezes and crashes&lt;/strong&gt; regularly, and I have never been able to watch two movies in a row without restarting in between. This of course requires squashing bugs that are left in the code, and &lt;strong&gt;improve the testing procedure&lt;/strong&gt;, so that such bugs never reach production code. Using mozilla-style reporting that allow people to &lt;strong&gt;send in crash reports&lt;/strong&gt; is probably a good idea at this stage. The code is based on &lt;a href=&#34;http://en.wikipedia.org/wiki/XBMC&#34;&gt;XBMC&lt;/a&gt;, and people say stuff like &amp;quot;how could they add that many bugs to a product that good?!&amp;quot;. Voddler, don&#39;t make it that easy to dismiss your hard work!&lt;/li&gt;
&lt;li&gt;Sometimes the &lt;strong&gt;subtitles skip some lines&lt;/strong&gt;. I watched &lt;a href=&#34;http://www.imdb.com/title/tt0325005/&#34;&gt;Antikiller&lt;/a&gt;, a russian action movie, five times important lines were shown only half a second on the screen, making me miss important pieces of the story. I&#39;m not sure if this had something to do with the subtitles for that exact movie, or lag because of Voddler, but I know that it was a crappy first impression of the client. It just has to be fixed.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;support forum is so slow it&#39;s unusable&lt;/strong&gt;. My guess is that it&#39;s on the same network as the movie streaming service, and therefore goes down together with that one. Those two just &lt;strong&gt;have to&lt;/strong&gt; be independent, as you need the forum most when the other is down.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;UI doesn&#39;t work usability-wise&lt;/strong&gt;. First of all, it&#39;s built for television sets, which means that only the keyboard works (arrow keys, space and esc), no mouse support. Several of the menues cycle (when you&#39;re on the end the first item appears again), so you&#39;ll easily loose track of where you are. There are small errors all over the place, things that makes finding and starting movies difficult.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So, in summary, the core of Voddler is good, but the interface needs quite a lot of work before it can be released to the public. No matter what, I&#39;ll keep following the development of this product!&lt;/p&gt;
&lt;p&gt;In general, it seems that when you Americans stop us from using your servies (ie. &lt;a href=&#34;http://www.voddler.com&#34;&gt;Hulu&lt;/a&gt;), we Swedes build our own services. First Spotify, now Voddler.&lt;/p&gt;

            </content>
        </entry>
    
        <entry>
            <title type="html">Techniques to use when IE6 dies</title>
            <link href="http://friendlybit.com/css/techniques-to-use-when-ie6-dies/" rel="alternate" type="text/html" title="Techniques to use when IE6 dies" />
            <published>2009-08-26T23:54:51+02:00</published>
            <updated>2009-08-26T23:54:51+02:00</updated>
            <id>http://friendlybit.com/css/techniques-to-use-when-ie6-dies/</id>
            <author>
                <name>Emil Stenström</name>
            </author>
            <summary type="text">Everyone except Microsoft themselves are talking about the death of IE6. I&#39;ve tried motivating people to drop support, arguing that you at least can show...</summary>
            <content type="html" xml:base="http://friendlybit.com/css/techniques-to-use-when-ie6-dies/">
                &lt;p&gt;Everyone &lt;a href=&#34;http://www.eweek.com/c/a/Windows/Microsoft-Internet-Explorer-6-Support-Continues-Despite-Calls-for-PhaseOut-307122/&#34;&gt;except Microsoft themselves&lt;/a&gt; are talking about the death of IE6. I&#39;ve tried &lt;a href=&#34;/browsers/motivation-for-building-for-ie6/&#34;&gt;motivating people to drop support&lt;/a&gt;, arguing that you at least can show IE6 users a message. Many have replied with &amp;quot;but our IT department doesn&#39;t let us…&amp;quot;, and I can say nothing more than that the IT department is filled with humans. Which means they are lazy, and upgrade when people whine enough about it. It&#39;s a shame it has to be that way, that we have to &lt;a href=&#34;http://www.ie6nomore.com/corporate-users.html&#34;&gt;punish people for their IT departments&lt;/a&gt;, but that&#39;s how it has to be.&lt;/p&gt;
&lt;p&gt;As &lt;strong&gt;web developers&lt;/strong&gt;, we should be the ones complaining the loudest. We have so much to win on IE6:s death it&#39;s silly. Just look at the below list of things IE6 can&#39;t do, but IE7 can (the next worst browser):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Native PNG alpha transparency without silly hacks.&lt;/li&gt;
&lt;li&gt;Several selectors: Child selector (&amp;quot;&amp;gt;&amp;quot;), Adjacent sibling selector (&amp;quot;+&amp;quot;), Attribute selectors ([attr=value]), and &lt;a href=&#34;http://www.w3.org/TR/css3-selectors/#general-sibling-combinators&#34;&gt;General sibling selector&lt;/a&gt; (&amp;quot;~&amp;quot;, CSS3)&lt;/li&gt;
&lt;li&gt;Min-height, Max-height, Min-width, Max-width&lt;/li&gt;
&lt;li&gt;Multiple class/id selectors in the same ruleset (ie. .class1.class2 { … })&lt;/li&gt;
&lt;li&gt;:hover on all elements, not just links&lt;/li&gt;
&lt;li&gt;position: fixed&lt;/li&gt;
&lt;li&gt;Native XMLHTTP (Without ActiveX)&lt;/li&gt;
&lt;li&gt;International Domain Names (IDN), the ability to use UTF-8 chars in domains&lt;/li&gt;
&lt;li&gt;Page zoom that zooms the whole page (don&#39;t worry about zoom)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is a huge step forward for us web developers. Huge! Bigger then when CSS3 comes out, because we won&#39;t be able to use that one for years. Someone is pushing the front of what&#39;s possible, I&#39;m pushing for the front of what&#39;s &lt;strong&gt;usable&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;What can you do to help me get everything in the above list? And, did I forget something?&lt;/p&gt;
&lt;p&gt;Sources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;http://www.quirksmode.org/css/contents.html&#34;&gt;CSS Compatibility charts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://blogulate.com/content/new-features-of-internet-explorer-7/&#34;&gt;IE6 vs IE7 - New features in Internet Explorer 7&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

            </content>
        </entry>
    
</feed>