This commit is contained in:
Leo Horie 2014-06-18 08:39:52 -04:00
parent 75b251adbf
commit 88bcb8a495
62 changed files with 8644 additions and 0 deletions

View file

@ -0,0 +1,139 @@
<!doctype html>
<html>
<head>
<title>Mithril</title>
<link href="lib/prism/prism.css" rel="stylesheet" />
<link href="style.css" rel="stylesheet" />
</head>
<body>
<header>
<nav class="container">
<a href="index.html" class="logo"><span>&#9675;</span> Mithril</a>
<a href="getting-started.html">Guide</a>
<a href="mithril.html">API</a>
<a href="community.html">Community</a>
<a href="mithril.min.zip">Download</a>
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
</nav>
</header>
<main>
<section class="content">
<div class="container">
<div class="row">
<div class="col(3,3,12)">
<h2 id="core-topics">Core Topics</h2>
<ul>
<li><a href="installation.html">Installation</a></li>
<li><a href="getting-started.html">Getting Started</a></li>
<li><a href="routing.html">Routing</a></li>
<li><a href="web-services.html">Web Services</a></li>
<li><a href="components.html">Components</a></li>
</ul>
<h2 id="advanced-topics.html">Advanced Topics</h2>
<ul>
<li><a href="compiling-templates.html">Compiling Templates</a></li>
<li><a href="auto-redrawing.html">The Auto-Redrawing System</a></li>
<li><a href="integration.html">Integrating with Other Libraries</a></li>
</ul>
<h2 id="misc">Misc</h2>
<ul>
<li><a href="comparison.html">Differences from Other MVC Frameworks</a></li>
<li><a href="benchmarks.html">Benchmarks</a></li>
<li><a href="practices.html">Good Practices</a></li>
<li><a href="tools.html">Useful Tools</a></li>
</ul>
</div>
<div class="col(9,9,12)">
<h2 id="integrating-with-the-auto-redrawing-system">Integrating with The Auto-Redrawing System</h2>
<p>If you need to do custom asynchronous calls without using Mithril&#39;s API, and find that your views are not redrawing, or that you&#39;re being forced to call <a href="mithril.redraw.html"><code>m.redraw</code></a> manually, you should consider using <code>m.startComputation</code> / <code>m.endComputation</code> so that Mithril can intelligently auto-redraw once your custom code finishes running.</p>
<p>In order to integrate asynchronous code to Mithril&#39;s autoredrawing system, you should call <code>m.startComputation</code> BEFORE making an asynchronous call, and <code>m.endComputation</code> after the asynchronous callback completes.</p>
<pre><code class="lang-javascript">//this service waits 1 second, logs &quot;hello&quot; and then notifies the view that
//it may start redrawing (if no other asynchronous operations are pending)
var doStuff = function() {
m.startComputation(); //call `startComputation` before the asynchronous `setTimeout`
setTimeout(function() {
console.log(&quot;hello&quot;);
m.endComputation(); //call `endComputation` at the end of the callback
}, 1000);
};</code></pre>
<p>To integrate synchronous code, call <code>m.startComputation</code> at the beginning of the method, and <code>m.endComputation</code> at the end.</p>
<pre><code class="lang-javascript">window.onfocus = function() {
m.startComputation(); //call before everything else in the event handler
doStuff();
m.endComputation(); //call after everything else in the event handler
}</code></pre>
<p>For each <code>m.startComputation</code> call a library makes, it MUST also make one and ONLY one corresponding <code>m.endComputation</code> call.</p>
<p>You should not use these methods if your code is intended to run repeatedly (e.g. by using <code>setInterval</code>). If you want to repeatedly redraw the view without necessarily waiting for user input, you should manually call <a href="mithril.redraw.html"><code>m.redraw</code></a> within the repeatable context.</p>
<hr>
<h3 id="integrating-multiple-execution-threads">Integrating multiple execution threads</h3>
<p>When <a href="integration.html">integrating with third party libraries</a>, you might find that you need to call asynchronous methods from outside of Mithril&#39;s API.</p>
<p>In order to integrate non-trivial asynchronous code with Mithril&#39;s auto-redrawing system, you need to ensure all execution threads call <code>m.startComputation</code> / <code>m.endComputation</code>.</p>
<p>An execution thread is basically any amount of code that runs before other asynchronous threads start to run.</p>
<p>Integrating multiple execution threads can be done in two different ways: in a layered fashion or in comprehensive fashion.</p>
<h4 id="layered-integration">Layered integration</h4>
<p>Layered integration is recommended for modular code where many different APIs may be put together at the application level.</p>
<p>Below is an example where various methods implemented with a third party library can be integrated in layered fashion: any of the methods can be used in isolation or in combination.</p>
<p>Notice how <code>doBoth</code> repeatedly calls <code>m.startComputation</code> since that method calls both <code>doSomething</code> and <code>doAnother</code>. This is perfectly valid: there are three asynchronous computations pending after the <code>jQuery.when</code> method is called, and therefore, three pairs of <code>m.startComputation</code> / <code>m.endComputation</code> in play.</p>
<pre><code class="lang-javascript">var doSomething = function(callback) {
m.startComputation(); //call `startComputation` before the asynchronous AJAX request
return jQuery.ajax(&quot;/something&quot;).done(function() {
if (callback) callback();
m.endComputation(); //call `endComputation` at the end of the callback
});
};
var doAnother = function(callback) {
m.startComputation(); //call `startComputation` before the asynchronous AJAX request
return jQuery.ajax(&quot;/another&quot;).done(function() {
if (callback) callback();
m.endComputation(); //call `endComputation` at the end of the callback
});
};
var doBoth = function(callback) {
m.startComputation(); //call `startComputation` before the asynchronous synchronization method
jQuery.when(doSomething(), doAnother()).then(function() {
if (callback) callback();
m.endComputation(); //call `endComputation` at the end of the callback
})
};</code></pre>
<h4 id="comprehensive-integration">Comprehensive integration</h4>
<p>Comprehensive integration is recommended if integrating a monolithic series of asynchronous operations. In contrast to layered integration, it minimizes the number of <code>m.startComputation</code> / <code>m.endComputation</code> calls to avoid clutter.</p>
<p>The example below shows a convoluted series of AJAX requests implemented with a third party library.</p>
<pre><code class="lang-javascript">var doSomething = function(callback) {
m.startComputation(); //call `startComputation` before everything else
jQuery.ajax(&quot;/something&quot;).done(function() {
doStuff();
jQuery.ajax(&quot;/another&quot;).done(function() {
doMoreStuff();
jQuery.ajax(&quot;/more&quot;).done(function() {
if (callback) callback();
m.endComputation(); //call `endComputation` at the end of everything
});
});
});
};</code></pre>
</div>
</div>
</div>
</section>
</main>
<footer>
<div class="container">
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
<br />&copy; 2014 Leo Horie
</div>
</footer>
<script src="lib/prism/prism.js"></script>
</body>
</html>

View file

@ -0,0 +1,92 @@
<!doctype html>
<html>
<head>
<title>Mithril</title>
<link href="lib/prism/prism.css" rel="stylesheet" />
<link href="style.css" rel="stylesheet" />
</head>
<body>
<header>
<nav class="container">
<a href="index.html" class="logo"><span>&#9675;</span> Mithril</a>
<a href="getting-started.html">Guide</a>
<a href="mithril.html">API</a>
<a href="community.html">Community</a>
<a href="mithril.min.zip">Download</a>
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
</nav>
</header>
<main>
<section class="content">
<div class="container">
<div class="row">
<div class="col(3,3,12)">
<h2 id="core-topics">Core Topics</h2>
<ul>
<li><a href="installation.html">Installation</a></li>
<li><a href="getting-started.html">Getting Started</a></li>
<li><a href="routing.html">Routing</a></li>
<li><a href="web-services.html">Web Services</a></li>
<li><a href="components.html">Components</a></li>
</ul>
<h2 id="advanced-topics.html">Advanced Topics</h2>
<ul>
<li><a href="compiling-templates.html">Compiling Templates</a></li>
<li><a href="auto-redrawing.html">The Auto-Redrawing System</a></li>
<li><a href="integration.html">Integrating with Other Libraries</a></li>
</ul>
<h2 id="misc">Misc</h2>
<ul>
<li><a href="comparison.html">Differences from Other MVC Frameworks</a></li>
<li><a href="benchmarks.html">Benchmarks</a></li>
<li><a href="practices.html">Good Practices</a></li>
<li><a href="tools.html">Useful Tools</a></li>
</ul>
</div>
<div class="col(9,9,12)">
<h2 id="benchmarks">Benchmarks</h2>
<p>These benchmarks were designed to measure Javascript running time for Mithril in comparison with other popular Javascript MVC frameworks. Javascript running time is significant because the gzipped size of a framework can be misleading in terms of how much code actually runs on page loads. In my experience, page loads happen far more commonly than one would expect in single page applications: power users open multiple tabs, and mobile users open and close the browser very frequently. And as far as templating engines go, the initial page load represents the worst case for the rendering algorithm since there is very little room for performance optimization tricks. It&#39;s arguably also <a href="http://blog.kissmetrics.com/loading-time/">one of the most important metrics when it comes to performance</a>.</p>
<p>The numbers shown here are best-run results for all frameworks, except for Mithril&#39;s case, for which I&#39;m taking the worst-run result. The numbers aren&#39;t statistically rigorous (e.g. I didn&#39;t bother to calculate standard deviation), but they should be enough to give a rough idea of what is faster than what.</p>
<p>Generally speaking, these tests are making a deliberate effort to be <strong>biased in favor of other frameworks:</strong> for example, I don&#39;t load &quot;optional-but-usually-used-in-real-life&quot; things like the router module for Angular, or Marionette in Backbone&#39;s case, and I load the entirety of Mithril. In addition, this test deliberately avoids triggering <code>requestAnimationFrame</code>-based performance optimizations for Mithril, since this optimization does not exist in many frameworks and <a href="http://jsperf.com/angular-vs-knockout-vs-ember/308"><em>severely</em> skews numbers in Mithril&#39;s favor</a> in CPU-intensive situations like parallax sites. I&#39;m also NOT using the <a href="compiling-templates.html">Mithril template compiler</a>, which would also skew the benchmark in Mithril&#39;s favor.</p>
<p>To run the execution time tests below, click on their respective links, run the profiler from your desired browser&#39;s developer tools and measure the running time of a page refresh (lower is better).</p>
<div class="performance" style="padding:0 0 0 10px;">
<div class="row">
<div class="col(4,4,6)">
<h3>Loading</h3>
<table>
<tr><td><a href="comparisons/mithril.parsing.html">Mithril</a></td><td><span class="bar" style="background:#161;width:1%;"></span> 0.28ms</td></tr>
<tr><td><a href="comparisons/jquery.parsing.html">jQuery</a></td><td><span class="bar" style="background:#66c;width:26%;"></span> 13.11ms</td></tr>
<tr><td><a href="comparisons/backbone.parsing.html">Backbone</a></td><td><span class="bar" style="background:#33c;width:37%;"></span> 18.54ms</td></tr>
<tr><td><a href="comparisons/angular.parsing.html">Angular</a></td><td><span class="bar" style="background:#c33;width:14%;"></span> 7.49ms</td></tr>
<tr><td><a href="comparisons/react.parsing.html">React</a></td><td><span class="bar" style="background:#6af;width:50%;"></span> 24.99ms</td></tr>
</table>
</div>
<div class="col(8,8,12)">
<h3>Rendering</h3>
<table>
<tr><td><a href="comparisons/mithril.rendering.html">Mithril</a></td><td><span class="bar" style="background:#161;width:4%;"></span> 9.44ms (uncompiled)</td></tr>
<tr><td><a href="comparisons/jquery.rendering.html">jQuery</a></td><td><span class="bar" style="background:#66c;width:17%;"></span> 40.27ms</td></tr>
<tr><td><a href="comparisons/backbone.rendering.html">Backbone</a></td><td><span class="bar" style="background:#33c;width:10%;"></span> 23.05ms</td></tr>
<tr><td><a href="comparisons/angular.rendering.html">Angular</a></td><td><span class="bar" style="background:#c33;width:50%;"></span> 118.63ms</td></tr>
<tr><td><a href="comparisons/react.rendering.html">React</a></td><td><span class="bar" style="background:#6af;width:33%;"></span> 79.65ms</td></tr>
</table>
</div>
</div>
</div>
<p>Feel free to implement versions of the tests above in other frameworks, if you wish. The code is very simple.</p>
</div>
</div>
</div>
</section>
</main>
<footer>
<div class="container">
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
<br />&copy; 2014 Leo Horie
</div>
</footer>
<script src="lib/prism/prism.js"></script>
</body>
</html>

View file

@ -0,0 +1,20 @@
{
"name": "mithril",
"version": "0.1.16",
"main": "mithril.min.js",
"description": "A Javascript Framework for building brilliant applications",
"authors": ["Leo Horie <lhorie@hotmail.com>"],
"license": "MIT",
"ignore": [
"*.html",
"*.css",
"*.zip",
"*.json",
"mithril-tests.js",
"archive",
"comparisons",
"lib",
"tools"
],
"keywords": ["mithril", "mvc", "framework"]
}

View file

@ -0,0 +1,287 @@
<!doctype html>
<html>
<head>
<title>Mithril</title>
<link href="lib/prism/prism.css" rel="stylesheet" />
<link href="style.css" rel="stylesheet" />
</head>
<body>
<header>
<nav class="container">
<a href="index.html" class="logo"><span>&#9675;</span> Mithril</a>
<a href="getting-started.html">Guide</a>
<a href="mithril.html">API</a>
<a href="community.html">Community</a>
<a href="mithril.min.zip">Download</a>
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
</nav>
</header>
<main>
<section class="content">
<div class="container">
<div class="row">
<div class="col(3,3,12)">
<h2 id="api">API (v0.1.16)</h2>
<h3 id="core">Core</h3>
<ul>
<li><a href="mithril.html">m</a></li>
<li><a href="mithril.prop.html">m.prop</a></li>
<li><a href="mithril.withAttr.html">m.withAttr</a></li>
<li><a href="mithril.module.html">m.module</a></li>
<li><a href="mithril.trust.html">m.trust</a></li>
<li><a href="mithril.render.html">m.render</a></li>
<li><a href="mithril.redraw.html">m.redraw</a></li>
</ul>
<h3 id="routing">Routing</h3>
<ul>
<li><a href="mithril.route.html">m.route</a>
<ul>
<li><a href="mithril.route.html#defining-routes">m.route(rootElement, defaultRoute, routes)</a></li>
<li><a href="mithril.route.html#redirecting">m.route(path, params)</a></li>
<li><a href="mithril.route.html#reading-current-route">m.route()</a></li>
<li><a href="mithril.route.html#mode-abstraction">m.route(element)</a></li>
<li><a href="mithril.route.html#mode">m.route.mode</a></li>
<li><a href="mithril.route.html#param">m.route.param</a></li>
</ul>
</li>
</ul>
<h3 id="data">Data</h3>
<ul>
<li><a href="mithril.request.html">m.request</a></li>
<li><a href="mithril.deferred.html">m.deferred</a></li>
<li><a href="mithril.sync.html">m.sync</a></li>
<li><a href="mithril.computation.html">m.startComputation / m.endComputation</a></li>
</ul>
<h2 id="archive">History</h2>
<ul>
<li><a href="roadmap.html">Roadmap</a></li>
<li><a href="change-log.html">Change log</a></li>
</ul>
</div>
<div class="col(9,9,12)">
<h2 id="change-log">Change Log</h2>
<p><a href="/mithril/archive/v0.1.6">v0.1.16</a> - maintenance</p>
<h3 id="news-">News:</h3>
<ul>
<li>controller::onunload now receives an event parameter so that the unloading can be aborted <a href="https://github.com/lhorie/mithril.js/issues/135">#135</a></li>
</ul>
<h3 id="bug-fixes-">Bug Fixes:</h3>
<ul>
<li>prevent route change when only hash changes in non-hash mode <a href="https://github.com/lhorie/mithril.js/issues/107">#107</a></li>
<li>config now always runs after template is attached to document <a href="https://github.com/lhorie/mithril.js/issues/109">#109</a></li>
<li>fix null reference exception with Browserify <a href="https://github.com/lhorie/mithril.js/issues/110">#110</a></li>
<li>fix nested array removal edge cases <a href="https://github.com/lhorie/mithril.js/issues/120">#120</a></li>
<li>ignore redraw calls when controller is not ready <a href="https://github.com/lhorie/mithril.js/issues/127">#127</a></li>
<li>fix null reference exception in nested array edge case <a href="https://github.com/lhorie/mithril.js/issues/129">#129</a></li>
<li>fix a contenteditable null reference error <a href="https://github.com/lhorie/mithril.js/issues/134">#134</a></li>
<li>fix textarea value diffing when value is a node inside an array <a href="https://github.com/lhorie/mithril.js/issues/136">#136</a></li>
<li>fix diff bug with trusted strings <a href="https://github.com/lhorie/mithril.js/issues/138">#138</a></li>
</ul>
<h3 id="breaking-changes-">Breaking changes:</h3>
<ul>
<li>Due to the poor level of compatibility between XDomainRequest and XHR2, XDomainRequest is no longer called internally by Mithril. If you need to use CORS in IE9 or lower, you will need to return an XDomainRequest instance from <code>m.request</code>&#39;s <code>config</code> method <a href="https://github.com/lhorie/mithril.js/issues/121">#121</a></li>
</ul>
<hr>
<p><a href="/mithril/archive/v0.1.15">v0.1.15</a> - maintenance</p>
<h3 id="bug-fixes-">Bug Fixes:</h3>
<ul>
<li><code>m.sync</code> now correctly passes arguments to resolver in same order as input arguments <a href="https://github.com/lhorie/mithril.js/issues/96">#96</a> </li>
<li>fixed diff deletion bug <a href="https://github.com/lhorie/mithril.js/issues/99">#99</a> </li>
<li>updating textarea attributes updates its value correctly <a href="https://github.com/lhorie/mithril.js/issues/100">#100</a></li>
</ul>
<hr>
<p><a href="/mithril/archive/v0.1.14">v0.1.14</a> - maintenance</p>
<h3 id="news-">News:</h3>
<ul>
<li>The signature of <code>m</code> now accepts virtual elements as the second parameter of the function.</li>
<li><code>m.route(path, params)</code> now accepts an argument that gets parsed as a querystring.</li>
<li>routes now ignore trailing slashes <a href="https://github.com/lhorie/mithril.js/issues/88">#88</a></li>
</ul>
<h3 id="bug-fixes-">Bug Fixes:</h3>
<ul>
<li>Resolving promises early without a value now works <a href="https://github.com/lhorie/mithril.js/issues/85">#85</a></li>
<li>Throwing exceptions within <code>m.request</code> now follow the same resolution procedure as <code>m.deferred</code> <a href="https://github.com/lhorie/mithril.js/issues/85">#86</a></li>
<li>Promises now always update their <code>m.prop</code> on success (and leave the m.prop alone on error)</li>
<li>Nested arrays no longer cause double removal of elements <a href="https://github.com/lhorie/mithril.js/issues/87">#87</a></li>
<li>HTTP error codes now correctly reject promises</li>
</ul>
<hr>
<p><a href="/mithril/archive/v0.1.13">v0.1.13</a> - maintenance</p>
<h3 id="news-">News:</h3>
<ul>
<li>m.module now runs clean-up code in root module controllers that implement an <code>onunload</code> instance method <a href="https://github.com/lhorie/mithril.js/issues/82">#82</a></li>
</ul>
<h3 id="bug-fixes-">Bug Fixes:</h3>
<ul>
<li>Removing CSS rules now diffs correctly <a href="https://github.com/lhorie/mithril.js/issues/79">#79</a></li>
</ul>
<hr>
<p><a href="/mithril/archive/v0.1.12">v0.1.12</a> - maintenance</p>
<h3 id="news-">News:</h3>
<ul>
<li>It&#39;s now possible to define <a href="mithril.route.html#variadic-routes">variadic routes</a> <a href="https://github.com/lhorie/mithril.js/issues/70">#70</a></li>
</ul>
<h3 id="bug-fixes-">Bug Fixes:</h3>
<ul>
<li>Fix link location in links using <code>config: m.route</code> after redraws <a href="https://github.com/lhorie/mithril.js/issues/74">#74</a></li>
<li>Fixed support for <code>list</code> attribute in inputs <a href="https://github.com/lhorie/mithril.js/issues/69">#69</a></li>
<li>Fixed URL decoding in route params <a href="https://github.com/lhorie/mithril.js/issues/75">#75</a></li>
</ul>
<hr>
<p><a href="/mithril/archive/v0.1.11">v0.1.11</a> - maintenance</p>
<h3 id="news-">News:</h3>
<ul>
<li>Added <code>m.route()</code> overload to allow reading of current route <a href="https://github.com/lhorie/mithril.js/issues/61">#61</a></li>
<li>Added <code>background</code> option to <code>m.request</code> to allow requests that don&#39;t affect rendering <a href="https://github.com/lhorie/mithril.js/issues/62">#62</a></li>
</ul>
<h3 id="bug-fixes-">Bug Fixes:</h3>
<ul>
<li>Links using <code>config: m.route</code> can now be opened in new tab correctly <a href="https://github.com/lhorie/mithril.js/issues/64">#64</a></li>
<li>Fixed diff within contenteditable areas <a href="https://github.com/lhorie/mithril.js/issues/65">#65</a></li>
</ul>
<hr>
<p><a href="/mithril/archive/v0.1.10">v0.1.10</a> - maintenance</p>
<h3 id="news-">News:</h3>
<ul>
<li>Added social buttons to homepage</li>
</ul>
<h3 id="bug-fixes-">Bug Fixes:</h3>
<ul>
<li>Bi-directional bindings no longer wipe out cursor position in Chrome <a href="https://github.com/lhorie/mithril.js/issues/58">#58</a></li>
</ul>
<hr>
<p><a href="/mithril/archive/v0.1.9">v0.1.9</a> - maintenance</p>
<h3 id="news-">News:</h3>
<ul>
<li>Added comparison with React to homepage</li>
<li>Added support for multi-island apps <a href="https://github.com/lhorie/mithril.js/issues/34">#34</a></li>
<li>m.prop is now JSON-serializable <a href="https://github.com/lhorie/mithril.js/issues/54">#54</a></li>
<li>Added <code>extract</code> option to <code>m.request</code> to allow access to response metadata <a href="https://github.com/lhorie/mithril.js/issues/53">#53</a></li>
</ul>
<h3 id="bug-fixes-">Bug Fixes:</h3>
<ul>
<li>Fixed node index displacement by null/undefined nodes <a href="https://github.com/lhorie/mithril.js/issues/56">#56</a></li>
<li>Fixed mock&#39;s insertBefore and appendChild when dealing w/ reattachments</li>
</ul>
<h3 id="breaking-changes-">Breaking changes:</h3>
<ul>
<li>changing an id in a virtual element now recreates the element, instead of recycling it <a href="https://github.com/lhorie/mithril.js/issues/55">#55</a></li>
</ul>
<hr>
<p><a href="/mithril/archive/v0.1.8">v0.1.8</a> - maintenance</p>
<h3 id="news-">News:</h3>
<ul>
<li>Mock now contains a basic <code>insertAdjacentHTML</code> implementation to enable better testing of <code>m.trust</code> / <code>m.render</code> interactions</li>
</ul>
<h3 id="bug-fixes-">Bug Fixes:</h3>
<ul>
<li>Fixed ordering bug in deep subchildren <a href="https://github.com/lhorie/mithril.js/issues/51">#51</a></li>
<li>Fixed ordering bug with trusted strings <a href="https://github.com/lhorie/mithril.js/issues/51">#51</a></li>
</ul>
<hr>
<p><a href="/mithril/archive/v0.1.7">v0.1.7</a> - maintenance</p>
<h3 id="news-">News:</h3>
<ul>
<li>Mithril will be on a accelerated release cycle for the rest of the v0.1.x series. This means CDNs may lag behind in versions, so it&#39;s recommended that you either use one of the supported NodeJS package managers or fork from the Github repo directly. More information can be found <a href="https://groups.google.com/forum/#!msg/mithriljs/mc0qTgFTlgs/OD7Mc7_2Wa4J">here</a></li>
</ul>
<h3 id="bug-fixes-">Bug Fixes:</h3>
<ul>
<li>Fixed ordering bug when virtual element is preceded by array <a href="https://github.com/lhorie/mithril.js/issues/50">#50</a></li>
</ul>
<hr>
<p><a href="/mithril/archive/v0.1.6">v0.1.6</a> - maintenance</p>
<h3 id="bug-fixes-">Bug Fixes:</h3>
<ul>
<li>Fixed serious bug when mixing cached text nodes with new virtual elements <a href="https://github.com/lhorie/mithril.js/issues/49">#49</a></li>
</ul>
<hr>
<p><a href="/mithril/archive/v0.1.5">v0.1.5</a> - maintenance</p>
<h3 id="news-">News:</h3>
<ul>
<li>Launched the <a href="http://lhorie.github.io/mithril-blog">Mithril Blog</a></li>
</ul>
<h3 id="bug-fixes-">Bug Fixes:</h3>
<ul>
<li>Fixed serious ordering problem when mixing arrays with virtual elements <a href="https://github.com/lhorie/mithril.js/issues/48">#48</a></li>
</ul>
<hr>
<p><a href="/mithril/archive/v0.1.4">v0.1.4</a> - maintenance</p>
<h3 id="news-">News:</h3>
<ul>
<li>added regression tests for reported bugs</li>
<li>added support for SVG</li>
</ul>
<h3 id="bug-fixes-">Bug Fixes:</h3>
<ul>
<li>URLs with port numbers are now handled correctly <a href="https://github.com/lhorie/mithril.js/issues/40">#40</a></li>
<li>NPM package now contains unminified version for map files <a href="https://github.com/lhorie/mithril.js/issues/39">#39</a></li>
<li>fixed ordering issue when mixing newly created virtual elements with elements from cache <a href="https://github.com/lhorie/mithril.js/issues/44">#44</a></li>
<li>fixed caching bug in links w/ config option attached <a href="https://github.com/lhorie/mithril.js/issues/43">#43</a></li>
<li>fixed attribute update bug when an element has both <code>oninput</code> and <code>onkeydown</code> handlers <a href="https://github.com/lhorie/mithril.js/issues/36">#36</a></li>
</ul>
<hr>
<p><a href="/mithril/archive/v0.1.3">v0.1.3</a> - maintenance</p>
<h3 id="news-">News:</h3>
<ul>
<li>Mithril is now available via <a href="http://component.io">Component</a></li>
<li>There&#39;s now an extra low-level optimization hook called a SubtreeDirective, which allows implementing plugins that only create virtual trees if necessary.</li>
</ul>
<h3 id="bug-fixes-">Bug Fixes:</h3>
<ul>
<li>diff no longer touch the DOM when processing <code>style</code> attributes and event handlers</li>
<li>returning a thennable to a resolution callback in <code>m.deferred().promise</code> now causes the promise to adopt its state </li>
<li>diff now correctly clears subtree if null or undefined is passed as a node</li>
</ul>
<hr>
<p><a href="/mithril/archive/v0.1.2">v0.1.2</a> - maintenance</p>
<h3 id="news-">News:</h3>
<ul>
<li>There&#39;s now a <a href="mailto:mithriljs@googlegroups.com">community mailing list</a>. There&#39;s also a <a href="https://groups.google.com/forum/#!forum/mithriljs">web interface</a></li>
<li>Mithril is now on Travis CI. The build status can be found in the <a href="https://github.com/lhorie/mithril.js">project homepage</a></li>
<li>Mithril is now available via the CommonJS and AMD API</li>
<li>Mithril can now <a href="installation.html">be installed via npm and bower</a></li>
</ul>
<h3 id="bug-fixes-">Bug Fixes:</h3>
<ul>
<li><code>m.render</code> now correctly reattaches reused DOM elements to replaced parent nodes <a href="https://github.com/lhorie/mithril.js/issues/31">#31</a></li>
<li>UI actions that can potentially de-synchronize the DOM from cache now force synchronization <a href="https://github.com/lhorie/mithril.js/issues/29">#29</a></li>
</ul>
<hr>
<p><a href="/mithril/archive/v0.1.1">v0.1.1</a> - maintenance</p>
<h3 id="news-">News:</h3>
<ul>
<li>Mithril is now available at <a href="http://cdnjs.com/libraries/mithril/">cdnjs</a> and <a href="http://www.jsdelivr.com/#!mithril">jsdelivr</a></li>
</ul>
<h3 id="bug-fixes-">Bug Fixes:</h3>
<ul>
<li><code>m.route.param</code> now resets on route change correctly <a href="https://github.com/lhorie/mithril.js/issues/15">#15</a></li>
<li><code>m.render</code> now correctly ignores undefined values in the virtual tree<a href="https://github.com/lhorie/mithril.js/issues/16">#16</a></li>
<li>errors thrown in promises now cause downstreams to be rejected <a href="https://github.com/lhorie/mithril.js/issues/1">#1</a></li>
</ul>
<h3 id="breaking-changes-">Breaking changes:</h3>
<ul>
<li><p>changed default value for <code>xhr.withCredentials</code> from <code>true</code> to <code>false</code> for <code>m.request</code>, since public APIs are more common than auth-walled ones. <a href="https://github.com/lhorie/mithril.js/issues/14">#14</a></p>
<p>In order to configure this flag, the following configuration should be used:</p>
<pre><code class="lang-javascript">var privateAPI = function(xhr) {xhr.withCredentials = true};
m.request({method: &quot;GET&quot;, url: &quot;http://foo.com/api&quot;, config: privateAPI});</code></pre>
</li>
</ul>
<hr>
<p><a href="/mithril/archive/v0.1">v0.1</a> - Initial release</p>
</div>
</div>
</div>
</section>
</main>
<footer>
<div class="container">
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
<br />&copy; 2014 Leo Horie
</div>
</footer>
<script src="lib/prism/prism.js"></script>
</body>
</html>

View file

@ -0,0 +1,76 @@
<!doctype html>
<html>
<head>
<title>Mithril</title>
<link href="lib/prism/prism.css" rel="stylesheet" />
<link href="style.css" rel="stylesheet" />
</head>
<body>
<header>
<nav class="container">
<a href="index.html" class="logo"><span>&#9675;</span> Mithril</a>
<a href="getting-started.html">Guide</a>
<a href="mithril.html">API</a>
<a href="community.html">Community</a>
<a href="mithril.min.zip">Download</a>
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
</nav>
</header>
<main>
<section class="content">
<div class="container">
<div class="row">
<div class="col(3,3,12)">
<h2 id="core-topics">Core Topics</h2>
<ul>
<li><a href="installation.html">Installation</a></li>
<li><a href="getting-started.html">Getting Started</a></li>
<li><a href="routing.html">Routing</a></li>
<li><a href="web-services.html">Web Services</a></li>
<li><a href="components.html">Components</a></li>
</ul>
<h2 id="advanced-topics.html">Advanced Topics</h2>
<ul>
<li><a href="compiling-templates.html">Compiling Templates</a></li>
<li><a href="auto-redrawing.html">The Auto-Redrawing System</a></li>
<li><a href="integration.html">Integrating with Other Libraries</a></li>
</ul>
<h2 id="misc">Misc</h2>
<ul>
<li><a href="comparison.html">Differences from Other MVC Frameworks</a></li>
<li><a href="benchmarks.html">Benchmarks</a></li>
<li><a href="practices.html">Good Practices</a></li>
<li><a href="tools.html">Useful Tools</a></li>
</ul>
</div>
<div class="col(9,9,12)">
<h2 id="community">Community</h2>
<h3 id="blog">Blog</h3>
<p>Read Mithril tutorials and articles about web app development. Join the discussion!</p>
<p>Go to the <a href="http://lhorie.github.io/mithril-blog">Mithril Blog</a></p>
<hr>
<h3 id="mailing-list">Mailing List</h3>
<p>Got questions about Mithril? Suggestions?</p>
<p>Feel free to post on the <a href="https://groups.google.com/forum/#!forum/mithriljs">mailing list</a></p>
<hr>
<h3 id="projects-and-snippets">Projects and Snippets</h3>
<p>A collection of projects and snippets created by Mithril users. A great place to find useful tools.</p>
<p>Go to the <a href="https://github.com/lhorie/mithril.js/wiki/Community-Projects-and-Snippets">Mithril wiki</a></p>
<hr>
<h3 id="bug-tracker">Bug Tracker</h3>
<p>You can file bugs in the <a href="https://github.com/lhorie/mithril.js/issues?state=open">issues page on Github</a></p>
</div>
</div>
</div>
</section>
</main>
<footer>
<div class="container">
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
<br />&copy; 2014 Leo Horie
</div>
</footer>
<script src="lib/prism/prism.js"></script>
</body>
</html>

View file

@ -0,0 +1,129 @@
<!doctype html>
<html>
<head>
<title>Mithril</title>
<link href="lib/prism/prism.css" rel="stylesheet" />
<link href="style.css" rel="stylesheet" />
</head>
<body>
<header>
<nav class="container">
<a href="index.html" class="logo"><span>&#9675;</span> Mithril</a>
<a href="getting-started.html">Guide</a>
<a href="mithril.html">API</a>
<a href="community.html">Community</a>
<a href="mithril.min.zip">Download</a>
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
</nav>
</header>
<main>
<section class="content">
<div class="container">
<div class="row">
<div class="col(3,3,12)">
<h2 id="core-topics">Core Topics</h2>
<ul>
<li><a href="installation.html">Installation</a></li>
<li><a href="getting-started.html">Getting Started</a></li>
<li><a href="routing.html">Routing</a></li>
<li><a href="web-services.html">Web Services</a></li>
<li><a href="components.html">Components</a></li>
</ul>
<h2 id="advanced-topics.html">Advanced Topics</h2>
<ul>
<li><a href="compiling-templates.html">Compiling Templates</a></li>
<li><a href="auto-redrawing.html">The Auto-Redrawing System</a></li>
<li><a href="integration.html">Integrating with Other Libraries</a></li>
</ul>
<h2 id="misc">Misc</h2>
<ul>
<li><a href="comparison.html">Differences from Other MVC Frameworks</a></li>
<li><a href="benchmarks.html">Benchmarks</a></li>
<li><a href="practices.html">Good Practices</a></li>
<li><a href="tools.html">Useful Tools</a></li>
</ul>
</div>
<div class="col(9,9,12)">
<h2 id="how-is-mithril-different-from-other-frameworks">How is Mithril Different from Other Frameworks</h2>
<p>There are a lot of different Javascript MVC frameworks and evaluating their merits and shortcomings can be a daunting task.</p>
<p>This page aims to provide a comparison between Mithril and some of the most widely used frameworks, as well as some of the younger, but relevant ones.</p>
<h3 id="code-size">Code Size</h3>
<p>One of the most obvious differences between Mithril and most frameworks is in file size: Mithril is around 4kb gzipped and has no dependencies on other libraries.</p>
<p>Note that while a small gzipped size can look appealing, that number is often used to &quot;hide the weight&quot; of the uncompressed code: remember that the decompressed Javascript still needs to be parsed and evaluated on every page load, and this cost (which can be in the dozens of milliseconds range for some frameworks in some browsers) cannot be cached.</p>
<p>This cost might be less of a concern in single page apps, but not necessarily if the app is typically opened simultaneously in multiple tabs, or run on less powerful devices.</p>
<p>The performance tests in the homepage show execution times for parsing and evaluation of Mithril&#39;s code, compared to some popular frameworks. As you can see, it paints a much less flattering picture for some frameworks than when we look at gzipped size alone.</p>
<h3 id="documentation">Documentation</h3>
<p>Another point of comparison is documentation. Most of the popular frameworks have at least a bare minimum amount of documentation nowadays, but many leave a bit to be desired: some lack usage examples, and some frameworks&#39; communities need to rely heavily on third party sites for explanations of more advanced topics, and sometimes even for learning the basics.</p>
<p>This is a problem particularly for frameworks that had breaking changes in the past: It&#39;s common to find answers in StackOverflow that are out-of-date and no longer work with the latest version of said frameworks.</p>
<p>Mithril has more documentation in its Github repo than source code, and none of the documentation is auto-generated.</p>
<p>All API points are explained in prose, and have code examples. Because the entire documentation is hand-crafted, you get the benefit of actually having explanations for things that documentation-generator tools don&#39;t support well (for example, interfaces and callback parameter documentation).</p>
<p>In addition, this guide section covers topics related to how to fit all the pieces together.</p>
<p>From the get-go, Mithril&#39;s build system produces archived versions of the code and documentation so that you&#39;ll never be stuck without docs for out-of-date versions.</p>
<p>Given how young Mithril is, hopefully you can appreciate the level of commitment for providing good documentation.</p>
<h3 id="architecture">Architecture</h3>
<p>In terms of architecture, one of Mithril&#39;s main differences is that it does not provide base classes to extend from.</p>
<p>It&#39;s often said that <em>frameworks</em>, in contrast to <em>libraries</em>, dictate how code should be written. In this sense, one could argue that Mithril isn&#39;t really a framework.</p>
<p>Instead of locking developers down to very specific implementations of design patterns, Mithril&#39;s approach is to provide an idiomatic pattern to follow, and tools to aid the developer when required. This approach means that developers can get discoverable codebases without necessarily getting locked into the framework.</p>
<p>One related difference is that other frameworks often have hard-coded base classes where every conceivable convenience method gets inherited by the developer&#39;s classes (remember, in Javascript, this can mean copying all of the utility methods over to the child class, regardless of whether they&#39;re going to be used or not).</p>
<p>Mithril&#39;s on-demand tooling approach means there are no hidden performance costs when implementing core MVC patterns, and there&#39;s also no extra learning curve for framework-specific syntax for those patterns.</p>
<h3 id="view-layer-paradigm">View Layer Paradigm</h3>
<p>Some of the older frameworks among the popular ones (out-of-the-box jQuery and Backbone, specifically) take a more procedural paradigm when it comes to the view layer; this means every action requires the developer to write custom view-level code to handle it.</p>
<p>This can get noticeably bulky when you look at thing like collections: you often need to implement insertion code and deletion code, in addition to a &quot;draw everything&quot; routine for performance. And this is for every list that needs to be displayed in some way.</p>
<p>Mithril&#39;s view layer paradigm is designed be <strong>declarative</strong>, much like HTML, such that the same code implicitly does everything it needs to. As it turns out, this design decision is actually a compromise: it offers the benefit of decreased application code complexity at the cost of some performance loss. However, as the performance tests in the homepage show, this does not necessarily hurt Mithril in a meaningful way.</p>
<hr>
<h2 id="specific-framework-comparisons">Specific Framework Comparisons</h2>
<p>Warning: this section is likely biased. Take it with a grain of salt.</p>
<h3 id="jquery">jQuery</h3>
<p>jQuery is ubiquitous and has a large ecosystem, but it&#39;s not an MVC framework.</p>
<p>There&#39;s no idiomatic way to organize jQuery code in an MVC pattern and many frameworks were created specifically to overcome that shortcoming.</p>
<p>As summarized above, Mithril differs from jQuery by allowing DOM-related code to be written largely in a declarative style (thereby decreasing code complexity), in addition to providing an idiomatic way to structure applications.</p>
<p>One other difference that is extremely clear is the treatment of data. In jQuery it&#39;s common to use the DOM as a data storage mechanism, whereas Mithril encourages data to exist in an isolated model layer.</p>
<h3 id="backbone">Backbone</h3>
<p>Backbone was originally designed as a way to structure jQuery-based applications. One of its selling points is that it allows developers to leverage their existing jQuery knowledge, while providing some &quot;walls&quot; to organize the code in a more structured manner.</p>
<p>As with jQuery, Mithril differs from Backbone by enforcing view code to be written in a declarative style.</p>
<p>Another marking difference is that Backbone is workflow agnostic, providing no idiomatic way to organize applications. This is good for framework adoption, but not necessarily ideal for team scalability and codebase discoverability.</p>
<p>In contrast, Mithril encourages you to develop applications using the patterns found throughout this guide, and discourages the use of &quot;bastardized&quot; MVC pattern variations.</p>
<p>One technical aspect that is also different is that Backbone is heavily event-oriented. Mithril, on the other hand, purposely avoids the observer pattern in an attempt to abolish &quot;come-from hell&quot;, a class of debugging problems where you don&#39;t know what triggers some code because of a long chain of events triggering other events.</p>
<p>A particularly nasty instance of this problem that sometimes occurs in &quot;real-time&quot; applications is when event triggering chains become circular due to a conditional statement bug, causing infinite loops and browser crashes.</p>
<p>Another significant difference between Backbone and Mithril is in their approach to familiarity: Backbone appeals to people familiar w/ jQuery; Mithril is designed to be familiar to people with server-side MVC framework experience.</p>
<h3 id="angular">Angular</h3>
<p>Angular is an MVC framework maintained by Google, and it provides a declarative view layer and an emphasis on testability. It leverages developer experience with server-side MVC frameworks, and in many ways, is very similar in scope to Mithril.</p>
<p>The main difference between Angular templates and Mithril templates is that Angular templates follow the tradition of being defined in HTML. This has the benefit of cleaner syntax for writing static text, but it comes with the disadvantage of features getting awkwardly tied to HTML syntax, as well as providing poor debugging support.</p>
<p>One thing you may have noticed on the <a href="http://lhorie.github.io/mithril/index.html#performance">Mithril homepage</a> is that, out of the box, Angular is not as performant as other frameworks. Steep performance degradation is a notoriously common issue in non-trivial Angular applications and there are several third party libraries which attempt to get around performance problems. Speaking from experience, it&#39;s generally difficult to reason about performance in Angular.</p>
<p>Mithril takes some learnings from that and implements a templating redrawing system that renders less aggressively, is less complex and is easier to profile.</p>
<p>A noteworthy difference between Angular and Mithril is in framework complexity: Angular implements several subsystems that would seem more logical in programming language implementations (e.g. a parser, a dynamic scoping mechanism, decorators, etc). Mithril, on the other hand, tries to provide only features that support a more classic MVC paradigm.</p>
<h3 id="ember">Ember</h3>
<p>Ember is a highly comprehensive MVC framework, providing a large API that covers not only traditional MVC patterns, but also a vast range of helper utilities as well.</p>
<p>The biggest difference between Ember and Mithril is summarized in the Architecture section above: Ember&#39;s comprehensiveness comes at the cost of a steep learning curve and a high degree of vendor lock-in.</p>
<p>Ember is also more opinionated in terms of how application architecture should look, and as a result, tends to be less transparent in terms of what is actually happening under the hood.</p>
<h3 id="react">React</h3>
<p>React is a templating engine developed by Facebook. It&#39;s relevant for comparison because it uses the same architecture as Mithril&#39;s templating engine: i.e. it acknowledges that DOM operations are the bottleneck of templating systems, and implements a virtual DOM tree which keeps track of changes and only applies diffs to the real DOM where needed.</p>
<p>The most visible difference between React and Mithril is that React&#39;s <em>JSX</em> syntax does not run natively in the browser, whereas Mithril&#39;s uncompiled templates do. Both can be compiled, but React&#39;s compiled code still has function calls for each virtual DOM element; Mithril templates compile into static Javascript data structures.</p>
<p>Another difference is that Mithril, being an MVC framework, rather than a templating engine, provides an auto-redrawing system that is aware of network asynchrony and that can render views efficiently without cluttering application code with redraw calls, and without letting the developer unintentionally bleed out of the MVC pattern.</p>
<p>Note also that, despite having a bigger scope, Mithril has a smaller file size than React.</p>
<h3 id="knockout">Knockout</h3>
<p>Knockout is a library focused on data binding. It is not an MVC framework in the traditional sense, but idiomatic Knockout code uses the similar concept of view models.</p>
<p>A Knockout view model is an amalgamation of model and controller layers in a single class. In contrast, Mithril separates the two layers more distinctly.</p>
<p>Generally speaking, Knockout applications tend to be more tightly coupled than Mithril since Knockout doesn&#39;t provide an equivalent to Mithril&#39;s modules and components.</p>
<p>As with Angular, Knockout templates are written in HTML, and therefore have the same pros and cons as Angular templates.</p>
<h3 id="vue">Vue</h3>
<p>Vue is a relatively new templating engine, but it boasts impressive results in its performance benchmark.</p>
<p>It is not a full MVC framework, but it is similar to Angular templates, and uses the same terminology for its features (e.g. directives and filters).</p>
<p>The most relevant difference is that Vue uses browser features that don&#39;t work (and cannot be made to work) in Internet Explorer 8. Mithril allows developers to support browsers all the way back to IE6 and Blackberry.</p>
<p>Vue&#39;s implementation cleverly hijacks array methods, but it should be noted that Javascript Arrays cannot be truly subclassed and as such, Vue suffers from abstraction leaks.</p>
<p>In contrast, Mithril avoids &quot;magic&quot; types.</p>
</div>
</div>
</div>
</section>
</main>
<footer>
<div class="container">
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
<br />&copy; 2014 Leo Horie
</div>
</footer>
<script src="lib/prism/prism.js"></script>
</body>
</html>

View file

@ -0,0 +1,2 @@
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.10/angular.min.js"></script>
To run an execution time test on this page, run the profiler from your browser's developer tools and measure the running time of a page refresh. (Lower is better)

View file

@ -0,0 +1,14 @@
<html ng-app>
<head><script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.10/angular.min.js"></script></head>
<body ng-controller="TestCtrl">
<p>To run an execution time test on this page, run the profiler from your browser's developer tools and measure the running time of a page refresh. (Lower is better)</p>
<div id="container">
<span ng-repeat="item in items"><input value="{{item.name}}"></span>
</div>
<script>
function TestCtrl($scope) {
$scope.items = [{name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}];
}
</script>
</body>
</html>

View file

@ -0,0 +1,13 @@
<html ng-app>
<head><script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.10/angular.min.js"></script></head>
<body ng-controller="TestCtrl">
<div id="container">
<span ng-repeat="item in items"><input value="{{item.name}}"></span>
</div>
<script>
function TestCtrl($scope) {
$scope.items = [{name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a\"><img src='javascript:;' onerror=\"alert('alert box should not appear')\">"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}];
}
</script>
</body>
</html>

View file

@ -0,0 +1,4 @@
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.5.2/underscore-min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/backbone.js/1.1.0/backbone-min.js"></script>
To run an execution time test on this page, run the profiler from your browser's developer tools and measure the running time of a page refresh. (Lower is better)

View file

@ -0,0 +1,30 @@
<html>
<head>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.5.2/underscore-min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/backbone.js/1.1.0/backbone-min.js"></script>
</head>
<body>
<p>To run an execution time test on this page, run the profiler from your browser's developer tools and measure the running time of a page refresh. (Lower is better)</p>
<div id="container"></div>
<script type="text/template" id="template">
<% _.each(items, function( item ){ %><input value="<%= item.name %>" /><% })%>
</script>
<script>
var View = Backbone.View.extend({
el: $("#container"),
template: _.template(document.getElementById("template").innerHTML),
initialize: function() {
this.render()
},
render: function() {
this.$el.html(this.template({items: this.model}))
}
})
new View({
model: [{name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}]
})
</script>
</body>
</html>

View file

@ -0,0 +1,29 @@
<html>
<head>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.5.2/underscore-min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/backbone.js/1.1.0/backbone-min.js"></script>
</head>
<body>
<div id="container"></div>
<script type="text/template" id="template">
<% _.each(items, function( item ){ %><input value="<%= item.name %>" /><% })%>
</script>
<script>
var View = Backbone.View.extend({
el: $("#container"),
template: _.template(document.getElementById("template").innerHTML),
initialize: function() {
this.render()
},
render: function() {
this.$el.html(this.template({items: this.model}))
}
})
new View({
model: [{name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a\"><img src='javascript:;' onerror=\"alert('alert box should not appear')\">"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}]
})
</script>
</body>
</html>

View file

@ -0,0 +1,2 @@
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
To run an execution time test on this page, run the profiler from your browser's developer tools and measure the running time of a page refresh. (Lower is better)

View file

@ -0,0 +1,17 @@
<html>
<head><script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script></head>
<body>
<p>To run an execution time test on this page, run the profiler from your browser's developer tools and measure the running time of a page refresh. (Lower is better)</p>
<div id="container"></div>
<script>
var items = [{name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}];
var view = function() {
var container = $("#container").html("");
for (var i = 0; i < items.length; i++) {
container.append("<input value=\"" + items[i].name + "\" />");
}
}
$(view);
</script>
</body>
</html>

View file

@ -0,0 +1,16 @@
<html>
<head><script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script></head>
<body>
<div id="container"></div>
<script>
var items = [{name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a\"><img src='javascript:;' onerror=\"alert('alert box should not appear')\">"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}];
var view = function() {
var container = $("#container").html("");
for (var i = 0; i < items.length; i++) {
container.append("<input value=\"" + items[i].name + "\" />");
}
}
$(view);
</script>
</body>
</html>

View file

@ -0,0 +1,2 @@
<script src="../mithril.min.js"></script>
To run an execution time test on this page, run the profiler from your browser's developer tools and measure the running time of a page refresh. (Lower is better)

View file

@ -0,0 +1,20 @@
<html>
<head><script src="../mithril.min.js"></script></head>
<body>
<p>To run an execution time test on this page, run the profiler from your browser's developer tools and measure the running time of a page refresh. (Lower is better)</p>
<div id="container"></div>
<script>
var app = {}
app.controller = function() {
this.items = [{name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}];
}
app.view = function(ctrl) {
return ctrl.items
.map(function(item) {
return m("input", {value: item.name})
});
}
m.module(document.getElementById("container"), app);
</script>
</body>
</html>

View file

@ -0,0 +1,19 @@
<html>
<head><script src="../mithril.min.js"></script></head>
<body>
<div id="container"></div>
<script>
var app = {}
app.controller = function() {
this.items = [{name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a\"><img src='javascript:;' onerror=\"alert('alert box should not appear')\">"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}];
}
app.view = function(ctrl) {
return ctrl.items
.map(function(item) {
return m("input", {value: item.name})
});
}
m.module(document.getElementById("container"), app);
</script>
</body>
</html>

View file

@ -0,0 +1,2 @@
<script src="http://cdnjs.cloudflare.com/ajax/libs/react/0.9.0/react.min.js"></script>
To run an execution time test on this page, run the profiler from your browser's developer tools and measure the running time of a page refresh. (Lower is better)

View file

@ -0,0 +1,24 @@
<html>
<head><script src="http://cdnjs.cloudflare.com/ajax/libs/react/0.9.0/react.min.js"></script></head>
<body>
<p>To run an execution time test on this page, run the profiler from your browser's developer tools and measure the running time of a page refresh. (Lower is better)</p>
<div id="container">
</div>
<script>
var items = [{name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}];
var d = React.DOM;
var Main = React.createClass({
render: function () {
return d.div({}, this.props.data.map(function (item) {
return d.input({value: item.name})
}));
}
});
React.renderComponent(Main({data: items}), document.getElementById('container'))
</script>
</body>
</html>

View file

@ -0,0 +1,24 @@
<html>
<head><script src="http://cdnjs.cloudflare.com/ajax/libs/react/0.9.0/react.min.js"></script></head>
<body>
<p>To run an execution time test on this page, run the profiler from your browser's developer tools and measure the running time of a page refresh. (Lower is better)</p>
<div id="container">
</div>
<script>
var items = [{name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a\"><img src='javascript:;' onerror=\"alert('alert box should not appear')\">"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}];
var d = React.DOM;
var Main = React.createClass({
render: function () {
return d.div({}, this.props.data.map(function (item) {
return d.input({value: item.name})
}));
}
});
React.renderComponent(Main({data: items}), document.getElementById('container'))
</script>
</body>
</html>

View file

@ -0,0 +1,112 @@
<!doctype html>
<html>
<head>
<title>Mithril</title>
<link href="lib/prism/prism.css" rel="stylesheet" />
<link href="style.css" rel="stylesheet" />
</head>
<body>
<header>
<nav class="container">
<a href="index.html" class="logo"><span>&#9675;</span> Mithril</a>
<a href="getting-started.html">Guide</a>
<a href="mithril.html">API</a>
<a href="community.html">Community</a>
<a href="mithril.min.zip">Download</a>
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
</nav>
</header>
<main>
<section class="content">
<div class="container">
<div class="row">
<div class="col(3,3,12)">
<h2 id="core-topics">Core Topics</h2>
<ul>
<li><a href="installation.html">Installation</a></li>
<li><a href="getting-started.html">Getting Started</a></li>
<li><a href="routing.html">Routing</a></li>
<li><a href="web-services.html">Web Services</a></li>
<li><a href="components.html">Components</a></li>
</ul>
<h2 id="advanced-topics.html">Advanced Topics</h2>
<ul>
<li><a href="compiling-templates.html">Compiling Templates</a></li>
<li><a href="auto-redrawing.html">The Auto-Redrawing System</a></li>
<li><a href="integration.html">Integrating with Other Libraries</a></li>
</ul>
<h2 id="misc">Misc</h2>
<ul>
<li><a href="comparison.html">Differences from Other MVC Frameworks</a></li>
<li><a href="benchmarks.html">Benchmarks</a></li>
<li><a href="practices.html">Good Practices</a></li>
<li><a href="tools.html">Useful Tools</a></li>
</ul>
</div>
<div class="col(9,9,12)">
<h2 id="compiling-templates">Compiling Templates</h2>
<p>You can optionally pre-compile templates that use <code>m()</code> by running the <a href="tools/template-compiler.sjs"><code>template-compiler.sjs</code></a> macro with <a href="https://github.com/mozilla/sweet.js">Sweet.js</a>. This step isn&#39;t required in order to use Mithril, but it&#39;s an easy way to squeeze a little bit more performance out of an application, without the need for code changes.</p>
<p>Compiling a template transforms the nested function calls of a template into a raw virtual DOM tree (which is merely a collection of native Javascript objects that is ready to be rendered via <a href="mithril.render.html"><code>m.render</code></a>). This means that compiled templates don&#39;t need to parse the string in <code>m(&quot;div#foo&quot;)</code> and they don&#39;t incur the cost of the function call.</p>
<p>It&#39;s worth mentioning that Mithril has built-in mechanisms elsewhere that take care of real bottlenecks like browser repaint management and DOM updating. This optional compilation tool is merely &quot;icing on the cake&quot; that speeds up the Javascript run-time of templates (which is already fast, even without compilation - see the <a href="http://lhorie.github.io/mithril/index.html#performance">performance section on the homepage</a>).</p>
<p>The macro takes regular Mithril templates like the one below:</p>
<pre><code class="lang-javascript">var view = function() {
return m(&quot;a&quot;, {href: &quot;http://google.com&quot;}, &quot;test&quot;);
}</code></pre>
<p>It pre-processes the <code>m()</code> call and replaces it with its output:</p>
<pre><code class="lang-javascript">var view = function() {
return {tag: &quot;a&quot;, attrs: {href: &quot;http://google.com&quot;}, children: &quot;test&quot;};
}</code></pre>
<p>Note that compiled templates are meant to be generated by an automated build process and are not meant to be human editable.</p>
<hr>
<h3 id="installing-nodejs-and-sweetjs-for-one-off-compilations">Installing NodeJS and SweetJS for one-off compilations</h3>
<p>SweetJS requires a <a href="http://nodejs.org">NodeJS</a> environment. To install it, go to its website and use the installer provided.</p>
<p>To install SweetJS, NodeJS provides a command-line package manager tool. In a command line, type:</p>
<pre><code>npm install -g sweet.js</code></pre>
<p>To compile a file, type:</p>
<pre><code>sjs --module /mithril.compile.sjs --output &lt;output-filename&gt;.js &lt;input-filename&gt;.js</code></pre>
<hr>
<h3 id="automating-compilation">Automating Compilation</h3>
<p>If you want to automate compilation, you can use <a href="http://gruntjs.com">GruntJS</a>, a task automation tool. If you&#39;re not familiar with GruntJS, you can find a tutorial on their website.</p>
<p>Assuming NodeJS is already installed, run the following command to install GruntJS:</p>
<pre><code>npm install -g grunt-cli</code></pre>
<p>Once installed, create two files in the root of your project, <code>package.json</code> and <code>Gruntfile.js</code></p>
<p><code>package.json</code></p>
<pre><code class="lang-javascript">{
&quot;name&quot;: &quot;project-name-goes-here&quot;,
&quot;version&quot;: &quot;0.0.0&quot;, //must follow this format
&quot;devDependencies&quot;: {
&quot;grunt-sweet.js&quot;: &quot;*&quot;
}
}</code></pre>
<p><code>Gruntfile.js</code></p>
<pre><code class="lang-javascript">module.exports = function(grunt) {
grunt.initConfig({
sweetjs: {
modules: [&quot;mithril.compile.sjs&quot;],
compile: {expand: true, cwd: &quot;.&quot;, src: &quot;**/*.js&quot;, dest: &quot;destination-folder-goes-here/&quot;}
}
});
grunt.loadNpmTasks(&#39;grunt-sweet.js&#39;);
grunt.registerTask(&#39;default&#39;, [&#39;sweetjs&#39;]);
}</code></pre>
<p>Make sure to replace the <code>project-name-goes-here</code> and <code>destination-folder-goes-here</code> placeholders with appropriate values.</p>
<p>To run the automation task, run the following command from the root folder of your project:</p>
<pre><code>grunt</code></pre>
<p>More documentation on the grunt-sweet.js task and its options <a href="https://github.com/natefaubion/grunt-sweet.js">can be found here</a></p>
</div>
</div>
</div>
</section>
</main>
<footer>
<div class="container">
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
<br />&copy; 2014 Leo Horie
</div>
</footer>
<script src="lib/prism/prism.js"></script>
</body>
</html>

View file

@ -0,0 +1,10 @@
{
"name": "mithril",
"description": "A Javascript framework for building brilliant applications",
"keywords": ["mvc", "framework"],
"repository": "lhorie/mithril",
"main": "mithril.min.js",
"scripts": ["mithril.min.js", "mithril.min.map", "mithril.js"],
"version": "0.1.16",
"license": "MIT"
}

View file

@ -0,0 +1,191 @@
<!doctype html>
<html>
<head>
<title>Mithril</title>
<link href="lib/prism/prism.css" rel="stylesheet" />
<link href="style.css" rel="stylesheet" />
</head>
<body>
<header>
<nav class="container">
<a href="index.html" class="logo"><span>&#9675;</span> Mithril</a>
<a href="getting-started.html">Guide</a>
<a href="mithril.html">API</a>
<a href="community.html">Community</a>
<a href="mithril.min.zip">Download</a>
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
</nav>
</header>
<main>
<section class="content">
<div class="container">
<div class="row">
<div class="col(3,3,12)">
<h2 id="core-topics">Core Topics</h2>
<ul>
<li><a href="installation.html">Installation</a></li>
<li><a href="getting-started.html">Getting Started</a></li>
<li><a href="routing.html">Routing</a></li>
<li><a href="web-services.html">Web Services</a></li>
<li><a href="components.html">Components</a></li>
</ul>
<h2 id="advanced-topics.html">Advanced Topics</h2>
<ul>
<li><a href="compiling-templates.html">Compiling Templates</a></li>
<li><a href="auto-redrawing.html">The Auto-Redrawing System</a></li>
<li><a href="integration.html">Integrating with Other Libraries</a></li>
</ul>
<h2 id="misc">Misc</h2>
<ul>
<li><a href="comparison.html">Differences from Other MVC Frameworks</a></li>
<li><a href="benchmarks.html">Benchmarks</a></li>
<li><a href="practices.html">Good Practices</a></li>
<li><a href="tools.html">Useful Tools</a></li>
</ul>
</div>
<div class="col(9,9,12)">
<h2 id="components">Components</h2>
<p>Components are Mithril&#39;s mechanism for <a href="http://en.wikipedia.org/wiki/Hierarchical_model%E2%80%93view%E2%80%93controller">hierarchical MVC</a>.</p>
<p>They allow complex, repeating logic to be abstracted into a logical unit of code, and they help modularize applications with widgets or multi-concern views (e.g. dashboards).</p>
<p>You can also use components for a number of other advanced techniques, like recursive templating (e.g. tree views) and partial template mixins (i.e. injecting part of a template into another).</p>
<hr>
<h3 id="nesting-components">Nesting components</h3>
<p>Here&#39;s an example of nested modules in a widgetization scenario:</p>
<pre><code class="lang-javascript">//root module
var dashboard = {};
dashboard.controller = function() {
this.userProfile = new userProfile.controller();
this.projectList = new projectList.controller();
}
dashboard.view = function(ctrl) {
return m(&quot;#example&quot;, [
m(&quot;.profile&quot;, [
userProfile.view(ctrl.userProfile)
]),
m(&quot;.projects&quot;, [
projectList.view(ctrl.projectList)
])
])
}
//components
//user profile component
var userProfile = {};
userProfile.controller = function() {
this.name = m.prop(&quot;John Doe&quot;);
};
userProfile.view = function(ctrl) {
return [
m(&quot;h1&quot;, &quot;Profile&quot;),
&quot;Name: &quot; + ctrl.name()
];
};
//project list component
var projectList = {};
projectList.controller = function() {};
projectList.view = function(ctrl) {
return &quot;There are no projects&quot;;
};
//initialize
m.module(document.body, dashboard);</code></pre>
<p>As you can see, components look exactly like regular modules - it&#39;s <a href="https://en.wikipedia.org/wiki/Turtles_all_the_way_down">turtles all the way down</a>! Remember that modules are simply dumb containers for <code>controller</code> and <code>view</code> classes.</p>
<p>This means components are decoupled both <em>horizontally</em> and <em>vertically</em>. It&#39;s possible to refactor each component as an isolated unit of logic (which itself follows the MVC pattern). And we can do so without touching the rest of the application (as long as the component API stays the same).</p>
<p>Similarly, it&#39;s possible to mix and match different classes to make mix-in anonymous components (e.g. it&#39;s straightforward to build several views - for, say, a mobile app - that use the same controller).</p>
<p>It&#39;s also possible to keep references to parent and even sibling components. This is useful, for example, when implementing notification badges in a navigation component, which are triggered and managed by other components in the system.</p>
<hr>
<h3 id="librarization">Librarization</h3>
<p>Applications often reuse rich UI controls that aren&#39;t provided out of the box by HTML. Below is a basic example of a component of that type: a minimalist autocompleter component.</p>
<p><em>Note: Be mindful that, for the sake of code clarity and brevity, the example below does not support keyboard navigation and other real world features.</em></p>
<pre><code class="lang-javascript">var autocompleter = {};
autocompleter.controller = function(data, getter) {
//binding for the text input
this.value = m.prop(&quot;&quot;);
//store for the list of items
this.data = m.prop([]);
//method to determine what property of a list item to compare the text input&#39;s value to
this.getter = getter;
//this method changes the relevance list depending on what&#39;s currently in the text input
this.change = function(value) {
this.value(value);
var list = value === &quot;&quot; ? [] : data.filter(function(item) {
return this.getter(item).toLowerCase().indexOf(value.toLowerCase()) &gt; -1;
}, this);
this.data(list);
};
//this method is called when an option is selected. It triggers an `onchange` event
this.select = function(value) {
this.value(value);
this.data([]);
if (this.onchange) this.onchange({currentTarget: {value: value}});
};
}
autocompleter.view = function(ctrl, options) {
if (options) ctrl.onchange = options.onchange;
return [
m(&quot;input&quot;, {oninput: m.withAttr(&quot;value&quot;, ctrl.change.bind(ctrl)), value: ctrl.value()}),
ctrl.data().map(function(item) {
return m(&quot;div&quot;, {data: ctrl.getter(item), onclick: m.withAttr(&quot;data&quot;, ctrl.select.bind(ctrl))}, ctrl.getter(item));
})
];
}
//here&#39;s an example of using the autocompleter
var dashboard = {}
dashboard.controller = function() {
this.names = m.prop([{id: 1, name: &quot;John&quot;}, {id: 2, name: &quot;Bob&quot;}, {id: 2, name: &quot;Mary&quot;}]);
this.autocompleter = new autocompleter.controller(this.names(), function(item) {
return item.name;
});
};
dashboard.view = function(ctrl) {
return m(&quot;#example&quot;, [
new autocompleter.view(ctrl.autocompleter, {onchange: m.withAttr(&quot;value&quot;, console.log)}),
]);
};
//initialize
m.module(document.body, dashboard);</code></pre>
<p>It&#39;s recommended that libraries that provide extra functionality to Mithril be implemented using this modular pattern, as opposed to trying to hide implementation in a <a href="mithril.html">virtual element&#39;s <code>config</code> attribute</a>.</p>
<p>You should only consider using <code>config</code>-based components when leveraging existing libraries.</p>
</div>
</div>
</div>
</section>
</main>
<footer>
<div class="container">
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
<br />&copy; 2014 Leo Horie
</div>
</footer>
<script src="lib/prism/prism.js"></script>
</body>
</html>

View file

@ -0,0 +1,459 @@
<!doctype html>
<html>
<head>
<title>Mithril</title>
<link href="lib/prism/prism.css" rel="stylesheet" />
<link href="style.css" rel="stylesheet" />
</head>
<body>
<header>
<nav class="container">
<a href="index.html" class="logo"><span>&#9675;</span> Mithril</a>
<a href="getting-started.html">Guide</a>
<a href="mithril.html">API</a>
<a href="community.html">Community</a>
<a href="mithril.min.zip">Download</a>
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
</nav>
</header>
<main>
<section class="content">
<div class="container">
<div class="row">
<div class="col(3,3,12)">
<h2 id="core-topics">Core Topics</h2>
<ul>
<li><a href="installation.html">Installation</a></li>
<li><a href="getting-started.html">Getting Started</a></li>
<li><a href="routing.html">Routing</a></li>
<li><a href="web-services.html">Web Services</a></li>
<li><a href="components.html">Components</a></li>
</ul>
<h2 id="advanced-topics.html">Advanced Topics</h2>
<ul>
<li><a href="compiling-templates.html">Compiling Templates</a></li>
<li><a href="auto-redrawing.html">The Auto-Redrawing System</a></li>
<li><a href="integration.html">Integrating with Other Libraries</a></li>
</ul>
<h2 id="misc">Misc</h2>
<ul>
<li><a href="comparison.html">Differences from Other MVC Frameworks</a></li>
<li><a href="benchmarks.html">Benchmarks</a></li>
<li><a href="practices.html">Good Practices</a></li>
<li><a href="tools.html">Useful Tools</a></li>
</ul>
</div>
<div class="col(9,9,12)">
<h2 id="getting-started">Getting Started</h2>
<h3 id="what-is-mithril-">What is Mithril?</h3>
<p>Mithril is a client-side Javascript MVC framework, i.e. it&#39;s a tool to make application code divided into a data layer (called <strong>M</strong>odel), a UI layer (called <strong>V</strong>iew), and a glue layer (called <strong>C</strong>ontroller)</p>
<p>Mithril is around 4kb gzipped thanks to its <a href="mithril.html">small, focused, API</a>. It provides a templating engine with a virtual DOM diff implementation for performant rendering, utilities for high-level modelling via functional composition, as well as support for routing and componentization.</p>
<p>The goal of the framework is to make application code discoverable, readable and maintainable, and hopefully help you become an even better developer.</p>
<p>Unlike some frameworks, Mithril tries very hard to avoid locking you into a web of dependencies: you can use as <em>little</em> of the framework as you need.</p>
<p>However, using its entire toolset idiomatically can bring lots of benefits: learning to use functional programming in real world scenarios and solidifying good coding practices for OOP and MVC are just some of them.</p>
<hr>
<h2 id="a-simple-application">A Simple Application</h2>
<p>Once you have a <a href="installation.html">copy of Mithril</a>, getting started is surprisingly boilerplate-free:</p>
<pre><code class="lang-markup">&lt;!doctype html&gt;
&lt;script src=&quot;mithril.js&quot;&gt;&lt;/script&gt;
&lt;script&gt;
//app goes here
&lt;/script&gt;</code></pre>
<p>Yes, this is valid HTML 5! According to the specs, the <code>&lt;html&gt;</code>, <code>&lt;head&gt;</code> and <code>&lt;body&gt;</code> tags can be omitted, but their respective DOM elements will still be there implicitly when a browser renders that markup.</p>
<hr>
<h3 id="model">Model</h3>
<p>In Mithril, an application typically lives in a namespace and contains modules. Modules are merely structures that represent a viewable &quot;page&quot; or component.</p>
<p>For simplicity, our application will have only one module, and we&#39;re going to use it as the namespace for our application:</p>
<pre><code class="lang-markup">&lt;script&gt;
//this application only has one module: todo
var todo = {};
&lt;/script&gt;</code></pre>
<p>This object will namespace our two Model classes:</p>
<pre><code class="lang-javascript">var todo = {};
//for simplicity, we use this module to namespace the model classes
//the Todo class has two properties
todo.Todo = function(data) {
this.description = m.prop(data.description);
this.done = m.prop(false);
};
//the TodoList class is a list of Todo&#39;s
todo.TodoList = Array;</code></pre>
<p><a href="mithril.prop.html"><code>m.prop</code></a> is simply a factory for a getter-setter function. Getter-setters work like this:</p>
<pre><code class="lang-javascript">//define a getter-setter with initial value `John`
var name = m.prop(&quot;John&quot;);
//read the value
var a = name(); //a == &quot;John&quot;
//set the value to `Mary`
name(&quot;Mary&quot;); //Mary
//read the value
var b = name(); //b == &quot;Mary&quot;</code></pre>
<p>Note that the <code>Todo</code> and <code>TodoList</code> classes we defined above are plain vanilla Javascript constructors. They can be initialized and used like this:</p>
<pre><code class="lang-javascript">var myTask = new todo.Todo({description: &quot;Write code&quot;});
//read the description
myTask.description(); //Write code
//is it done?
var isDone = myTask.done(); //isDone == false
//mark as done
myTask.done(true); //true
//now it&#39;s done
isDone = myTask.done(); //isDone == true</code></pre>
<p>The <code>TodoList</code> class is simply an alias of the native <code>Array</code> class.</p>
<pre><code class="lang-javascript">var list = new todo.TodoList();
list.length; //0</code></pre>
<hr>
<h3 id="controller">Controller</h3>
<p>Our next step is to write a controller that will use our model classes.</p>
<pre><code class="lang-javascript">//the controller uses three model-level entities, of which one is a custom defined class:
//`Todo` is the central class in this application
//`list` is merely a generic array, with standard array methods
//`description` is a temporary storage box that holds a string
//
//the `add` method simply adds a new todo to the list
todo.controller = function() {
this.list = new todo.TodoList();
this.description = m.prop(&quot;&quot;);
this.add = function(description) {
if (description()) {
this.list.push(new todo.Todo({description: description()}));
this.description(&quot;&quot;);
}
};
}</code></pre>
<p>The code above defines a controller class. It has three members: <code>list</code>, which is simply an array, <code>description</code>, which is an <code>m.prop</code> getter-setter function with an empty string as the initial value, and <code>add</code>, which is a method that adds a new Todo instance to <code>list</code> if an input description getter-setter is not an empty string. Later in this guide, we&#39;ll pass the <code>description</code> property as the parameter to this function. I&#39;ll explain why we&#39;re passing it as an argument when we get there.</p>
<p>You can use the controller like this:</p>
<pre><code class="lang-javascript">var ctrl = new todo.controller();
ctrl.description(); //[empty string]
//try adding a to-do
ctrl.add(ctrl.description);
ctrl.list.length; //0
//you can&#39;t add a to-do with an empty description
//add it properly
ctrl.description(&quot;Write code&quot;);
ctrl.add(ctrl.description);
ctrl.list.length; //1</code></pre>
<hr>
<h3 id="view">View</h3>
<p>The next step is to write a view so users can interact with the application</p>
<pre><code class="lang-javascript">todo.view = function(ctrl) {
return m(&quot;html&quot;, [
m(&quot;body&quot;, [
m(&quot;input&quot;),
m(&quot;button&quot;, &quot;Add&quot;),
m(&quot;table&quot;, [
m(&quot;tr&quot;, [
m(&quot;td&quot;, [
m(&quot;input[type=checkbox]&quot;)
]),
m(&quot;td&quot;, &quot;task description&quot;),
])
])
])
]);
};</code></pre>
<p>The utility method <code>m()</code> creates virtual DOM elements. As you can see, you can use CSS selectors to specify attributes. You can also use the <code>.</code> syntax to add CSS classes and the <code>#</code> to add an id.</p>
<p>The view can be rendered using the <code>m.render</code> method:</p>
<pre><code class="lang-javascript">//assuming the `ctrl` variable from earlier
m.render(document, todo.view(ctrl));</code></pre>
<p>Notice that we pass a root DOM element to attach our template to, as well as the template itself.</p>
<p>This renders the following markup:</p>
<pre><code class="lang-markup">&lt;html&gt;
&lt;body&gt;
&lt;input /&gt;
&lt;button&gt;Add&lt;/button&gt;
&lt;table&gt;
&lt;tr&gt;
&lt;td&gt;&lt;input type=&quot;checkbox&quot; /&gt;&lt;/td&gt;
&lt;td&gt;task description&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<hr>
<h4 id="data-bindings">Data Bindings</h4>
<p>Let&#39;s implement a <strong>data binding</strong> on the text input. Data bindings connect a DOM element to a Javascript variable so that updating one updates the other.</p>
<pre><code class="lang-javascript">m(&quot;input&quot;)
//becomes
m(&quot;input&quot;, {value: ctrl.description()})</code></pre>
<p>This binds the <code>description</code> getter-setter to the text input. Updating the value of the description updates the input when Mithril redraws.</p>
<pre><code class="lang-javascript">var ctrl = new todo.controller();
ctrl.description(); // empty string
m.render(document, todo.view(ctrl)); // input is empty
ctrl.description(&quot;Write code&quot;); //set the description in the controller
m.render(document, todo.view(ctrl)); // input now says &quot;Write code&quot;</code></pre>
<p>Note that calling the <code>todo.view</code> method multiple times does not re-render the entire template.</p>
<p>Internally, Mithril keeps a virtual representation of the DOM in cache, scans for changes, and then only modifies the minimum required to apply the change.</p>
<p>In this case, Mithril only touches the <code>value</code> attribute of the input.</p>
<hr>
<p>Bindings can also be <strong>bi-directional</strong>: that is, they can be made such that, in addition to what we saw just now, a user typing on the input updates the description getter-setter.</p>
<p>Here&#39;s the idiomatic way of implementing the view-to-controller part of the binding:</p>
<pre><code class="lang-javascript">m(&quot;input&quot;, {onchange: m.withAttr(&quot;value&quot;, ctrl.description), value: ctrl.description()})</code></pre>
<p>The code bound to the <code>onchange</code> can be read like this: &quot;with the attribute value, set ctrl.description&quot;.</p>
<p>Note that Mithril does not prescribe how the binding updates: you can bind it to <code>onchange</code>, <code>onkeypress</code>, <code>oninput</code>, <code>onblur</code> or any other event that you prefer.</p>
<p>You can also specify what attribute to bind. This means that just as you are able to bind the <code>value</code> attribute in an <code>&lt;select&gt;</code>, you are also able to bind the <code>selectedIndex</code> property, if needed for whatever reason.</p>
<p>The <code>m.withAttr</code> utility is a functional programming tool provided by Mithril to minimize the need for ugly anonymous functions in the view.</p>
<p>The <code>m.withAttr(&quot;value&quot;, ctrl.description)</code> call above returns a function that is the rough equivalent of this code:</p>
<pre><code class="lang-javascript">onchange: function(e) {
ctrl.description(e.target[&quot;value&quot;]);
}</code></pre>
<p>The difference, aside from the cosmetic avoidance of anonymous functions, is that the <code>m.withAttr</code> idiom also takes care of catching the correct event target and selecting the appropriate source of the data - i.e. whether it should come from a Javascript property or from <code>DOMElement::getAttribute()</code></p>
<hr>
<p>In addition to bi-directional data binding, we can also bind parameterized functions to events:</p>
<pre><code class="lang-javascript">m(&quot;button&quot;, {onclick: ctrl.add.bind(ctrl, ctrl.description)}, &quot;Add&quot;)</code></pre>
<p>In the code above, we are simply using the native Javascript <code>Function::bind</code> method. This creates a new function with the parameter already set. In functional programming, this is called <a href="http://en.wikipedia.org/wiki/Partial_application"><em>partial application</em></a>.</p>
<p>The <code>ctrl.add.bind(ctrl, ctrl.description)</code> expression above returns a function that is equivalent to this code:</p>
<pre><code class="lang-javascript">onclick: function(e) {
ctrl.add(ctrl.description)
}</code></pre>
<p>Note that when we construct the parameterized binding, we are passing the <code>description</code> getter-setter <em>by reference</em>, and not its value. We only evaluate the getter-setter to get its value in the controller method. This is a form of <em>lazy evaluation</em>: it allows us to say &quot;use this value later, when the event handler gets called&quot;.</p>
<p>Hopefully by now, you&#39;re starting to see why Mithril encourages the usage of <code>m.prop</code>: Because Mithril getter-setters are functions, they naturally compose well with functional programming tools, and allow for some very powerful idioms. In this case, we&#39;re using them in a way that resembles C pointers.</p>
<p>Mithril uses them in other interesting ways elsewhere.</p>
<p>As a side note, some readers have pointed out that we can refactor the <code>add</code> method like this:</p>
<pre><code class="lang-javascript">this.add = function() {
if (this.description()) {
this.list.push(new todo.Todo({description: this.description()}));
this.description(&quot;&quot;);
}
}.bind(this);</code></pre>
<p>The difference is that <code>add</code> no longer takes an argument, and we call <code>.bind(this)</code> at the end to lock the scoping of <code>this</code> inside of the <code>add</code> method</p>
<p>Then we can make the <code>onclick</code> binding on the template much simpler:</p>
<pre><code>m(&quot;button&quot;, {onclick: ctrl.add}, &quot;Add&quot;)</code></pre>
<p>The only reason I talked about partial application here was to make you aware of that technique, since it becomes useful when dealing with parameterized event handlers. In real life, given a choice, you should always pick the simplest idiom for your use case, as we just did here.</p>
<hr>
<p>To implement flow control in Mithril views, we simply use Javascript:</p>
<pre><code class="lang-javascript">//here&#39;s the view
m(&quot;table&quot;, [
ctrl.list.map(function(task, index) {
return m(&quot;tr&quot;, [
m(&quot;td&quot;, [
m(&quot;input[type=checkbox]&quot;)
]),
m(&quot;td&quot;, task.description()),
])
})
])</code></pre>
<p>In the code above, <code>ctrl.list</code> is an Array, and <code>map</code> is one of its native functional methods. It allows us to iterate over the list and merge transformed versions of the list items into an output array.</p>
<p>As you can see, we return a partial template with two <code>&lt;td&gt;</code>&#39;s. The second one has a data binding to the <code>description</code> getter-setter of the Todo class instance.</p>
<p>You&#39;re probably starting to notice that Javascript has strong support for functional programming and that it allows us to naturally do things that can be clunky in other frameworks (e.g. looping inside a <code>&lt;dl&gt;/&lt;dt&gt;/&lt;dd&gt;</code> construct).</p>
<hr>
<p>The rest of the code can be implemented using idioms we already covered. The complete view looks like this:</p>
<pre><code class="lang-javascript">todo.view = function(ctrl) {
return m(&quot;html&quot;, [
m(&quot;body&quot;, [
m(&quot;input&quot;, {onchange: m.withAttr(&quot;value&quot;, ctrl.description), value: ctrl.description()}),
m(&quot;button&quot;, {onclick: ctrl.add}, &quot;Add&quot;),
m(&quot;table&quot;, [
ctrl.list.map(function(task, index) {
return m(&quot;tr&quot;, [
m(&quot;td&quot;, [
m(&quot;input[type=checkbox]&quot;, {onclick: m.withAttr(&quot;checked&quot;, task.done), checked: task.done()})
]),
m(&quot;td&quot;, {style: {textDecoration: task.done() ? &quot;line-through&quot; : &quot;none&quot;}}, task.description()),
])
})
])
])
]);
};</code></pre>
<p>Here are the highlights of the template above:</p>
<ul>
<li>The template is rendered as a child of the implicit <code>&lt;html&gt;</code> element of the document.</li>
<li>The text input saves its value to the <code>ctrl.description</code> getter-setter we defined earlier.</li>
<li>The button calls the <code>ctrl.add</code> method when clicked.</li>
<li>The table lists all the existing to-dos, if any.</li>
<li>The checkboxes save their value to the <code>task.done</code> getter setter.</li>
<li>The description gets crossed out via CSS if the task is marked as done.</li>
<li>When updates happen, the template is not wholly re-rendered - only the changes are applied.</li>
</ul>
<hr>
<p>When running the classes in this application separately, you have full control and full responsibility for determining when to redraw the view.</p>
<p>However, Mithril does provide another utility to make this task automatic: <a href="http://lhorie.github.io/mithril/auto-redrawing.html">the Auto-Redrawing System</a>.</p>
<p>In order to enable Mithril&#39;s auto-redrawing system, we run the code as a Mithril module:</p>
<pre><code class="lang-javascript">m.module(document, todo);</code></pre>
<p>Mithril&#39;s auto-redrawing system keeps track of controller stability, and only redraws the view once it detects that the controller has finished running all of its code, including asynchronous AJAX payloads.</p>
<p>Also note that this mechanism itself is not asynchronous if it doesn&#39;t need to be: Mithril does not need to wait for the next browser repaint frame to redraw - it doesn&#39;t even need to wait for the document ready event on the first redraw - it will redraw immediately upon script completion, if able to.</p>
<hr>
<h3 id="summary">Summary</h3>
<p>Here&#39;s the application code in its entirety:</p>
<pre><code class="lang-markup">&lt;!doctype html&gt;
&lt;script src=&quot;mithril.js&quot;&gt;&lt;/script&gt;
&lt;script&gt;
//this application only has one module: todo
var todo = {};
//for simplicity, we use this module to namespace the model classes
//the Todo class has two properties
todo.Todo = function(data) {
this.description = m.prop(data.description);
this.done = m.prop(false);
};
//the TodoList class is a list of Todo&#39;s
todo.TodoList = Array;
//the controller uses three model-level entities, of which one is a custom defined class:
//`Todo` is the central class in this application
//`list` is merely a generic array, with standard array methods
//`description` is a temporary storage box that holds a string
//
//the `add` method simply adds a new todo to the list
todo.controller = function() {
this.list = new todo.TodoList();
this.description = m.prop(&quot;&quot;);
this.add = function() {
if (this.description()) {
this.list.push(new todo.Todo({description: this.description()}));
this.description(&quot;&quot;);
}
}.bind(this);
};
//here&#39;s the view
todo.view = function(ctrl) {
return m(&quot;html&quot;, [
m(&quot;body&quot;, [
m(&quot;input&quot;, {onchange: m.withAttr(&quot;value&quot;, ctrl.description), value: ctrl.description()}),
m(&quot;button&quot;, {onclick: ctrl.add}, &quot;Add&quot;),
m(&quot;table&quot;, [
ctrl.list.map(function(task, index) {
return m(&quot;tr&quot;, [
m(&quot;td&quot;, [
m(&quot;input[type=checkbox]&quot;, {onclick: m.withAttr(&quot;checked&quot;, task.done), checked: task.done()})
]),
m(&quot;td&quot;, {style: {textDecoration: task.done() ? &quot;line-through&quot; : &quot;none&quot;}}, task.description()),
])
})
])
])
]);
};
//initialize the application
m.module(document, todo);
&lt;/script&gt;</code></pre>
<hr>
<h2 id="notes-on-architecture">Notes on Architecture</h2>
<p>Let&#39;s look at each MVC layer in detail to illustrate some of Mithril&#39;s design principles and philosophies:</p>
<h3 id="model">Model</h3>
<p>Idiomatic Mithril code is meant to apply good programming conventions and be easy to refactor.</p>
<p>In the application above, notice how the Todo class can easily be moved to a different module if code re-organization is required.</p>
<p>Todos are self-contained and their data aren&#39;t tied to the DOM like in typical jQuery based code. The Todo class API is reusable and unit-test friendly, and in addition, it&#39;s a plain-vanilla Javascript class, and so has almost no framework-specific learning curve.</p>
<p><a href="mithril.prop.html"><code>m.prop</code></a> is a simple but surprisingly versatile tool: it&#39;s composable, it enables <a href="http://en.wikipedia.org/wiki/Uniform_data_access">uniform data access</a> and allows a higher degree of decoupling when major refactoring is required.</p>
<p>When refactoring is unavoidable, the developer can simply replace the <code>m.prop</code> call with an appropriate getter-setter implementation, instead of having to grep for API usage across the entire application.</p>
<p>For example, if todo descriptions needed to always be uppercased, one could simply change the <code>description</code> getter-setter:</p>
<pre><code class="lang-javascript">this.description = m.prop(data.description)</code></pre>
<p>becomes:</p>
<pre><code class="lang-javascript">//private store
var description = data.description;
//public getter-setter
this.description = function(value) {
if (arguments.length &gt; 0) description = value.toUpperCase();
return description;
}</code></pre>
<p>According to Mithril&#39;s philosophy, <code>list</code> and <code>description</code> are also considered model-level entities. This is a subtle but important point: model entities don&#39;t need to be full-blown custom classes.</p>
<p>Native Javascript classes are quite appropriate for storing primitive and structured data. Since in this case they are indeed being used to store data - even if temporarily - they are model entities!</p>
<p>Be aware that by using the native Array class for a list, we&#39;re making an implicit statement that we are going to support all of the standard Array methods as part of our API.</p>
<p>While this decision allows better API discoverability, the trade-off is that we&#39;re largely giving up on custom constraints and behavior. For example, if we wanted to change the application to make the list be persisted, a native Array would most certainly not be a suitable class to use.</p>
<p>In order to deal with that type of refactoring, one can explicitly decide to support only a subset of the Array API, and implement another class with the same interface as this subset API.</p>
<p>Given the code above, the replacement class would only need to implement the <code>.push()</code> and <code>.map()</code> methods. By freezing APIs and swapping implementations, the developer can completely avoid touching other layers in the application while refactoring.</p>
<pre><code class="lang-javascript">todo.TodoList = Array;</code></pre>
<p>becomes:</p>
<pre><code class="lang-javascript">todo.TodoList = function () {
this.push = function() { /*...*/ },
this.map = function() { /*...*/ }
};</code></pre>
<p>Hopefully these examples give you an idea of ways requirements can change over time and how Mithril&#39;s philosophy allows developers to use standard OOP techniques to refactor their codebases, rather than needing to modify large portions of the application.</p>
<hr>
<h3 id="controller">Controller</h3>
<p>Mithril follows a data binding paradigm that is familiar to developers that use server-side MVC frameworks like Rails and Django.</p>
<p>The difference, as mentioned earlier, is that Mithril philosophy considers any form of data storage as being a model entity - even data from a text input waiting to be saved!</p>
<p>In Mithril, controllers are not meant to progressively operate on model entities. Instead, model entities should expose methods that atomically act on themselves.</p>
<p>What this rule means is that controllers can have conditional logic, as is the case in the <code>add</code> method in the application above, but <strong>each action that touches a model entity should not leave it in an unstable state.</strong></p>
<p>This is in contrast to the ActiveRecord pattern of other frameworks, which allows entities to be in potentially invalid states (for example, a to-do with no description), so long as they are not &quot;saved&quot;.</p>
<p>The idea of disallowing unstable states hinges largely on the developer deciding what constitutes validity:</p>
<ul>
<li><p>An empty description in the context of the text input in the UI is a perfectly valid state, and a string is an appropriate type to express that.</p>
</li>
<li><p>A to-do with no description is not valid, therefore we avoid writing code that ever leaves the Todo class instance in a unstable state.</p>
</li>
</ul>
<p>Mithril doesn&#39;t programmatically define the scope of each model entity or in what states an entity is considered valid - validity is something the developer is responsible for defining.</p>
<p>Mithril&#39;s philosophical framework simply encourages that the developer map validity to static types. This is a key step in ensuring programs are robust and refactorable.</p>
<hr>
<h3 id="view">View</h3>
<p>The first and most obvious thing you may have noticed in the view layer is that the view is not written in HTML.</p>
<p>While superficially this may seem like an odd design, this actually has a lot of benefits:</p>
<ul>
<li><p>No flash-of-unbehaviored-content (FOUC). In fact, Mithril is able to render a fully functional application - with working event handlers - before the &quot;DOM ready&quot; event fires!</p>
</li>
<li><p>There&#39;s no need for a parse-and-compile pre-processing step to turn strings containing HTML + templating syntax into working DOM elements.</p>
</li>
<li><p>Mithril views can provide accurate and informative error reporting, with line numbers and meaningful stack traces.</p>
</li>
<li><p>You get the ability to automate linting, unit testing and minifying of the entire view layer - and you are even able to use Closure Compiler&#39;s Advanced Mode without needing extensive annotations.</p>
</li>
<li><p>It provides full Turing completeness: full control over evaluation eagerness/laziness and caching in templates. You can even build components that take other components as first-class-citizen parameters!</p>
</li>
<li><p><a href="https://en.wikipedia.org/wiki/Turtles_all_the_way_down">Turtles all the way down</a>: you don&#39;t need write custom data binding code in jQuery for every possible user interaction, and you don&#39;t need to support a complicated &quot;directive&quot; layer to be able to fit some types of components into the system.</p>
</li>
</ul>
<p>Views in Mithril use a virtual DOM diff implementation, which sidesteps performance problems related to opaque dirty-checking and excessive browser repaint that are present in some frameworks.</p>
<p>Another feature - the optional <code>m()</code> utility - allows writing terse templates in a declarative style using CSS shorthands, similar to popular HTML preprocessors from server-side MVC frameworks.</p>
<p>And because Mithril views are Javascript, the developer has full freedom to abstract common patterns - from bidirectional binding helpers to full blown components - using standard Javascript refactoring techniques.</p>
<p>Mithril templates are also more collision-proof than other component systems since there&#39;s no way to pollute the HTML tag namespace by defining ad-hoc tag names.</p>
<p>A more intellectually interesting aspect of the framework is that event handling is encouraged to be done via functional composition (i.e. by using tools like <a href="mithril.withAttr.html"><code>m.withAttr</code></a>, <a href="mithril.prop.html"><code>m.prop</code></a> and the native <code>.bind()</code> method for partial application).</p>
<p>If you&#39;ve been interested in learning or using Functional Programming in the real world, Mithril provides very pragmatic opportunities to get into it.</p>
<hr>
<h2 id="learn-more">Learn More</h2>
<p>Mithril provides a few more facilities that are not demonstrated in this page. The following topics are good places to start a deeper dive.</p>
<ul>
<li><a href="routing">Routing</a></li>
<li><a href="web-services">Web Services</a></li>
<li><a href="components">Components</a></li>
</ul>
<h2 id="advanced-topics">Advanced Topics</h2>
<ul>
<li><a href="compiling-templates">Compiling templates</a></li>
<li><a href="auto-redrawing">Integrating with the Auto-Redrawing System</a></li>
<li><a href="integration">Integrating with Other Libraries</a></li>
</ul>
<h2 id="misc">Misc</h2>
<ul>
<li><a href="comparison">Differences from Other MVC Frameworks</a></li>
<li><a href="benchmarks">Benchmarks</a></li>
<li><a href="practices">Good Practices</a></li>
<li><a href="tools">Useful Tools</a></li>
</ul>
</div>
</div>
</div>
</section>
</main>
<footer>
<div class="container">
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
<br />&copy; 2014 Leo Horie
</div>
</footer>
<script src="lib/prism/prism.js"></script>
</body>
</html>

View file

@ -0,0 +1,154 @@
<!doctype html>
<html>
<head>
<title>Mithril</title>
<link href="lib/prism/prism.css" rel="stylesheet" />
<link href="style.css" rel="stylesheet" />
</head>
<body>
<header>
<nav class="container">
<a href="index.html" class="logo"><span>&#9675;</span> Mithril</a>
<a href="getting-started.html">Guide</a>
<a href="mithril.html">API</a>
<a href="community.html">Community</a>
<a href="mithril.min.zip">Download</a>
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
</nav>
</header>
<main>
<section class="content">
<div class="container">
<div class="row">
<div class="col(3,3,12)">
<h2 id="api">API (v0.1.16)</h2>
<h3 id="core">Core</h3>
<ul>
<li><a href="mithril.html">m</a></li>
<li><a href="mithril.prop.html">m.prop</a></li>
<li><a href="mithril.withAttr.html">m.withAttr</a></li>
<li><a href="mithril.module.html">m.module</a></li>
<li><a href="mithril.trust.html">m.trust</a></li>
<li><a href="mithril.render.html">m.render</a></li>
<li><a href="mithril.redraw.html">m.redraw</a></li>
</ul>
<h3 id="routing">Routing</h3>
<ul>
<li><a href="mithril.route.html">m.route</a>
<ul>
<li><a href="mithril.route.html#defining-routes">m.route(rootElement, defaultRoute, routes)</a></li>
<li><a href="mithril.route.html#redirecting">m.route(path, params)</a></li>
<li><a href="mithril.route.html#reading-current-route">m.route()</a></li>
<li><a href="mithril.route.html#mode-abstraction">m.route(element)</a></li>
<li><a href="mithril.route.html#mode">m.route.mode</a></li>
<li><a href="mithril.route.html#param">m.route.param</a></li>
</ul>
</li>
</ul>
<h3 id="data">Data</h3>
<ul>
<li><a href="mithril.request.html">m.request</a></li>
<li><a href="mithril.deferred.html">m.deferred</a></li>
<li><a href="mithril.sync.html">m.sync</a></li>
<li><a href="mithril.computation.html">m.startComputation / m.endComputation</a></li>
</ul>
<h2 id="archive">History</h2>
<ul>
<li><a href="roadmap.html">Roadmap</a></li>
<li><a href="change-log.html">Change log</a></li>
</ul>
</div>
<div class="col(9,9,12)">
<h2 id="how-to-read-signatures">How to Read Signatures</h2>
<p>Rather than providing concrete classes like other frameworks, Mithril provides methods that operate on plain old Javascript objects (POJOs) that match given signatures.</p>
<p>A signature is a description of its static type. For functions, it shows the parameters of the function, its return value and their expected types. For objects and arrays, it shows the expected data structure and the expected types of their members.</p>
<p>Method signatures in this documentation follow a syntax similar to Java syntax, with some extra additions:</p>
<pre><code class="lang-clike">ReturnType methodName(ParameterType1 param1, ParameterType2 param2)</code></pre>
<h3 id="optional-parameters">Optional Parameters</h3>
<p>Square brackets denote optional parameters. In the example below, <code>param2</code> and <code>param3</code> can both be omitted, but passing a value to <code>param2</code> is required if also passing a value to <code>param3</code>:</p>
<pre><code class="lang-clike">String test(String arg1 [, String arg2 [, String arg3]])</code></pre>
<pre><code class="lang-javascript">//examples of valid function calls
test(&quot;first&quot;);
test(&quot;first&quot;, &quot;second&quot;);
test(&quot;first&quot;, &quot;second&quot;, &quot;third&quot;);</code></pre>
<h3 id="type-placeholders">Type Placeholders</h3>
<p>The word <code>void</code> is used as a type when a function does not return a value (i.e. undefined):</p>
<pre><code class="lang-clike">void test()</code></pre>
<pre><code class="lang-javascript">console.log(test()); // undefined</code></pre>
<p>The word <code>any</code> is used as a type if there are no type restrictions on a parameter:</p>
<pre><code class="lang-clike">void test(any value)</code></pre>
<pre><code class="lang-javascript">//examples of valid function calls
test(&quot;hello&quot;);
test(1);
test([&quot;hello&quot;, &quot;world&quot;]);</code></pre>
<h3 id="arrays">Arrays</h3>
<p>Arrays use Generics syntax to denote the expected type of array members:</p>
<pre><code class="lang-clike">void test(Array&lt;String&gt; values)</code></pre>
<pre><code class="lang-javascript">//example of a valid function call
test([&quot;first&quot;, &quot;second&quot;]);</code></pre>
<h3 id="objects-as-key-value-maps">Objects as Key-Value Maps</h3>
<p>Objects also use Generics syntax when they are meant to be used as a key-value map. Keys are always strings and, in key-value maps, can have any name.</p>
<pre><code class="lang-clike">void test(Object&lt;Number&gt; values)</code></pre>
<pre><code class="lang-javascript">//example of a valid function call
test({first: 1, second: 2});</code></pre>
<h3 id="objects-as-class-interfaces">Objects as Class Interfaces</h3>
<p>Objects that require specific keys are denoted using curly brace syntax:</p>
<pre><code class="lang-clike">void test(Object {String first, Number second} value)</code></pre>
<pre><code class="lang-javascript">//example of a valid function call
test({first: &quot;first&quot;, second: 2});</code></pre>
<h3 id="type-aliasing">Type Aliasing</h3>
<p>Some types are aliases of more complex types. For example, in the example below, we created an alias called <code>ComplexType</code> for the type from the previous example</p>
<pre><code class="lang-clike">void test(ComplexType value)
where:
ComplexType :: Object {String first, Number second}
//example of a valid function call
test({first: &quot;first&quot;, second: 2})</code></pre>
<h3 id="mixin-types">Mixin Types</h3>
<p>Curly brace syntax can also appear on other base types to denote that the value contains static members. For example, in the example below, a value of type <code>ComplexType</code> is a string, but it also has a boolean property called <code>flag</code>:</p>
<pre><code class="lang-clike">ComplexType :: String { Boolean flag }</code></pre>
<pre><code class="lang-javascript">//an example
var a = aComplexTypeValue
typeof a == &quot;string&quot; // true
&quot;flag&quot; in a // true
a.flag = true</code></pre>
<p>In the following example, a value of type <code>ComplexType</code> is a function, with a property called <code>label</code></p>
<pre><code class="lang-clike">ComplexType :: void test() { String label }</code></pre>
<pre><code class="lang-javascript">//an example
var a = aComplexTypeValue
typeof a == &quot;function&quot; // true
&quot;label&quot; in a // true
a.label = &quot;first&quot;</code></pre>
<h3 id="polymorphic-types">Polymorphic Types</h3>
<p>When multiple (but not all) types are accepted, the pipe <code>|</code> is used to delimit the list of valid types</p>
<pre><code class="lang-clike">void test(Children children, Value value)
where:
Children :: Array&lt;String text | Number number&gt;
Value :: String | Number</code></pre>
<pre><code class="lang-javascript">//examples of valid function calls
test([&quot;test&quot;, 2], &quot;second&quot;)
test([1, 2, 3], &quot;second&quot;)
test([1, &quot;test&quot;, 3], 2)</code></pre>
<p>Pipe syntax within Object curly brace syntax means that, for a specific key, name has specific type requirements.</p>
<p>In the example below, the <code>value</code> parameter should be a key-value map. This map may contain a key called <code>config</code>, whose value should be a function.</p>
<pre><code class="lang-clike">void test(Object { any | void config(DOMElement) } value)</code></pre>
<pre><code class="lang-javascript">//example of a valid function call
test({ first: &quot;first&quot;, config: function(element) { /*do stuff*/ } })</code></pre>
</div>
</div>
</div>
</section>
</main>
<footer>
<div class="container">
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
<br />&copy; 2014 Leo Horie
</div>
</footer>
<script src="lib/prism/prism.js"></script>
</body>
</html>

262
archive/v0.1.16/index.html Normal file
View file

@ -0,0 +1,262 @@
<!doctype html>
<html>
<head>
<title>Mithril</title>
<link href="lib/prism/prism.css" rel="stylesheet" />
<link href="style.css" rel="stylesheet" />
</head>
<body>
<header>
<nav class="container">
<a href="index.html" class="logo"><span>&#9675;</span> Mithril</a>
<a href="getting-started.html">Guide</a>
<a href="mithril.html">API</a>
<a href="community.html">Community</a>
<a href="mithril.min.zip">Download</a>
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
</nav>
</header>
<main>
<section class="cta">
<div class="container">
<h1 class="logo"><span>&#9675;</span> Mithril</h1>
<p>A Javascript Framework for Building Brilliant Applications</p>
<p>
<a class="button" href="getting-started.html">Guide</a>
<a class="button" href="mithril.min.zip">Download v0.1.16</a>
</p>
<iframe src="http://ghbtns.com/github-btn.html?user=lhorie&repo=mithril.js&type=watch&count=true" allowtransparency="true" frameborder="0" scrolling="0" width="100" height="20"></iframe>
<a href="https://twitter.com/share" class="twitter-share-button" data-url="http://lhorie.github.io/mithril" data-via="LeoHorie">Tweet</a>
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs');</script>
<a href="http://flattr.com/thing/2778375/lhoriemithril-js-on-GitHub" target="_blank"><img src="http://api.flattr.com/button/flattr-badge-large.png" alt="Flattr" title="Flattr" border="0" /></a>
</div>
</section>
<section class="features">
<div class="container">
<h2>What is Mithril?</h2>
<p>Mithril is a client-side MVC framework - a tool to organize code in a way that is easy to think about and to maintain.</p>
</div>
<div class="container row">
<div class="feature col(4,4,12)">
<h2>Light-weight</h2>
<ul>
<li>Only 4kb gzipped, no dependencies</li>
<li>Small API, small learning curve</li>
</ul>
</div>
<div class="feature col(4,4,12)">
<h2>Robust</h2>
<ul>
<li>Safe-by-default templates</li>
<li>Hierarchical MVC via components</li>
</ul>
</div>
<div class="feature col(4,4,12)">
<h2>Fast</h2>
<ul>
<li>Virtual DOM diffing and compilable templates</li>
<li>Intelligent auto-redrawing system</li>
</ul>
</div>
</div>
</section>
<section class="sample">
<div class="container">
<div class="col(8,8,12)">
<h2>Sample code</h2>
<pre><code class="language-javascript">//namespace
var app = {};
//model
app.PageList = function() {
return m.request({method: "GET", url: "pages.json"});
};
//controller
app.controller = function() {
this.pages = app.PageList();
this.rotate = function() {
this.pages.push(this.pages.shift())
}.bind(this)
};
//view
app.view = function(ctrl) {
return [
ctrl.pages().map(function(page) {
return m("a", {href: page.url}, page.title);
}),
m("a", {onclick: ctrl.rotate}, "Rotate links")
];
};
//initialize
m.module(document.getElementById("example"), app);</code></pre>
</div>
<div class="col(4,4,12)">
<h2>Output</h2>
<div id="example" class="example output">
<script src="mithril.min.js"></script>
<script>
//namespace
var app = {};
//model
app.PageList = function() {
return m.request({method: "GET", url: "pages.json"});
};
//controller
app.controller = function() {
this.pages = app.PageList();
this.rotate = function() {
this.pages().push(this.pages().shift())
}.bind(this)
};
//view
app.view = function(ctrl) {
return [
ctrl.pages().map(function(page) {
return m("a", {href: page.url}, page.title);
}),
m("button", {onclick: ctrl.rotate}, "Rotate links")
];
};
//initialize
m.module(document.getElementById("example"), app);
</script>
</div>
</div>
</div>
</section>
<section class="performance">
<div class="container">
<h2 id="performance">Performance</h2>
<p>To run the execution time tests below, click on their respective links, run the profiler from your desired browser's developer tools and measure the running time of a page refresh (Lower is better). <a href="benchmarks.html">Read more</a></p>
<div class="row">
<div class="col(4,4,6)">
<h3>Loading</h3>
<table>
<tr><td><a href="comparisons/mithril.parsing.html">Mithril</a></td><td><span class="bar" style="background:#161;width:1%;"></span> 0.28ms</td></tr>
<tr><td><a href="comparisons/jquery.parsing.html">jQuery</a></td><td><span class="bar" style="background:#66c;width:26%;"></span> 13.11ms</td></tr>
<tr><td><a href="comparisons/backbone.parsing.html">Backbone</a></td><td><span class="bar" style="background:#33c;width:37%;"></span> 18.54ms</td></tr>
<tr><td><a href="comparisons/angular.parsing.html">Angular</a></td><td><span class="bar" style="background:#c33;width:14%;"></span> 7.49ms</td></tr>
<tr><td><a href="comparisons/react.parsing.html">React</a></td><td><span class="bar" style="background:#6af;width:50%;"></span> 24.99ms</td></tr>
</table>
</div>
<div class="col(8,8,12)">
<h3>Rendering</h3>
<table>
<tr><td><a href="comparisons/mithril.rendering.html">Mithril</a></td><td><span class="bar" style="background:#161;width:4%;"></span> 9.44ms (uncompiled)</td></tr>
<tr><td><a href="comparisons/jquery.rendering.html">jQuery</a></td><td><span class="bar" style="background:#66c;width:17%;"></span> 40.27ms</td></tr>
<tr><td><a href="comparisons/backbone.rendering.html">Backbone</a></td><td><span class="bar" style="background:#33c;width:10%;"></span> 23.05ms</td></tr>
<tr><td><a href="comparisons/angular.rendering.html">Angular</a></td><td><span class="bar" style="background:#c33;width:50%;"></span> 118.63ms</td></tr>
<tr><td><a href="comparisons/react.rendering.html">React</a></td><td><span class="bar" style="background:#6af;width:33%;"></span> 79.65ms</td></tr>
</table>
</div>
</div>
</div>
</section>
<section class="security">
<div class="container">
<div class="row">
<div class="col(8,8,12)">
<h2>Safety</h2>
<p>Mithril templates are safe by default, i.e. you can't unintentionally create security holes.</p>
<p>To run the tests for each framework, click on the respective links. If you see an alert box, ensuring security with that framework is more work for you.</p>
</div>
<div class="col(4,4,12)">
<h3>Test Summary</h3>
<a href="comparisons/mithril.safety.html">Mithril</a> <em class="success">(pass) &#x2713;</em><br />
<a href="comparisons/jquery.safety.html">jQuery</a> <em class="error">(fail) &#x2717;</em><br />
<a href="comparisons/backbone.safety.html">Backbone</a> <em class="error">(fail) &#x2717;</em><br />
<a href="comparisons/angular.safety.html">Angular</a> <em class="success">(pass) &#x2713;</em><br />
<a href="comparisons/react.safety.html">React</a> <em class="success">(pass) &#x2713;</em><br />
</div>
</div>
</div>
</section>
<section class="more">
<div class="container row">
<div class="col(3,6,12)">
<div style="padding:0 2em 0 0;">
<h2>Guide</h2>
<p>Build a simple application. Learn the ropes</p>
<p><a href="getting-started.html">Read Guide</a></p>
</div>
</div>
<div class="col(3,6,12)">
<div style="padding:0 2em 0 0;">
<h2>API</h2>
<p>Docs and code samples for your reference</p>
<p><a href="mithril.html">Read Docs</a></p>
</div>
</div>
<div class="col(3,6,12)">
<div style="padding:0 2em 0 0;">
<h2>Blog</h2>
<p>Weekly articles on how to use Mithril to its full potential.</p>
<p><a href="http://lhorie.github.io/mithril-blog">Read blog</a></p>
</div>
</div>
<div class="col(3,6,12)">
<div style="padding:0 2em 0 0;">
<h2>Mailing List</h2>
<p>Post questions and discuss Mithril related topics.</p>
<p><a href="https://groups.google.com/forum/#!forum/mithriljs">Go to mailing list</a></p>
</div>
</div>
</div>
</section>
<section class="socialmedia">
<div class="container row">
<h2>Social Media</h2>
<div class="col(6,12,12)">
<div style="padding:0 1em 0 0;">
<blockquote class="twitter-tweet" lang="en"><p>Just converted the MELPA site from Angular to <a href="https://twitter.com/LeoHorie">@LeoHorie</a>&#39;s tiny mithril.js: very pleasant. <a href="http://t.co/R7JKfo9vJ1">http://t.co/R7JKfo9vJ1</a> <a href="https://t.co/7IrR3L84OY">https://t.co/7IrR3L84OY</a></p>&mdash; Steve Purcell (@sanityinc) <a href="https://twitter.com/sanityinc/statuses/471358494101504000">May 27, 2014</a></blockquote>
<blockquote class="twitter-tweet" lang="en"><p><a href="https://twitter.com/marijnjh">@marijnjh</a> Mithril is well worth checking out: <a href="http://t.co/RT61djMFxD">http://t.co/RT61djMFxD</a> Functional style, elegant design and powerful and only 3kb gzipped.</p>&mdash; Tom Atkins (@knitatoms) <a href="https://twitter.com/knitatoms/statuses/467061326859476992">May 15, 2014</a></blockquote>
</div>
</div>
<div class="col(6,12,12)">
<blockquote class="twitter-tweet" lang="en"><p>Mithril.js m.prop feature is plain brilliant. I spent years yearning for an approach that lightweight <a href="http://t.co/KgzOxoa2WR">http://t.co/KgzOxoa2WR</a></p>&mdash; Xavier Via (@xaviervia) <a href="https://twitter.com/xaviervia/statuses/454635992360554497">April 11, 2014</a></blockquote>
<blockquote class="twitter-tweet" lang="en"><p>Mithril: The newest JavaScript MVC library 3Kb. <a href="http://twitter.com/LeoHorie">@LeoHorie</a> got it right: It's all about good guides/docs: <a href="http://lhorie.github.io/mithril/comparison.html">lhorie.github.io/mithril/comparison.html</a></p> &mdash; David Corbacho (@dcorbacho) <a href="https://twitter.com/dcorbacho/status/446926407843991552">March 21, 2014</a></blockquote>
<script async src="http://platform.twitter.com/widgets.js"></script>
</div>
</div>
</section>
</main>
<footer>
<div class="container">
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a><br />
&copy; 2014 Leo Horie
</div>
</footer>
<script src="lib/prism/prism.js"></script>
</body>
</html>

View file

@ -0,0 +1,114 @@
<!doctype html>
<html>
<head>
<title>Mithril</title>
<link href="lib/prism/prism.css" rel="stylesheet" />
<link href="style.css" rel="stylesheet" />
</head>
<body>
<header>
<nav class="container">
<a href="index.html" class="logo"><span>&#9675;</span> Mithril</a>
<a href="getting-started.html">Guide</a>
<a href="mithril.html">API</a>
<a href="community.html">Community</a>
<a href="mithril.min.zip">Download</a>
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
</nav>
</header>
<main>
<section class="content">
<div class="container">
<div class="row">
<div class="col(3,3,12)">
<h2 id="core-topics">Core Topics</h2>
<ul>
<li><a href="installation.html">Installation</a></li>
<li><a href="getting-started.html">Getting Started</a></li>
<li><a href="routing.html">Routing</a></li>
<li><a href="web-services.html">Web Services</a></li>
<li><a href="components.html">Components</a></li>
</ul>
<h2 id="advanced-topics.html">Advanced Topics</h2>
<ul>
<li><a href="compiling-templates.html">Compiling Templates</a></li>
<li><a href="auto-redrawing.html">The Auto-Redrawing System</a></li>
<li><a href="integration.html">Integrating with Other Libraries</a></li>
</ul>
<h2 id="misc">Misc</h2>
<ul>
<li><a href="comparison.html">Differences from Other MVC Frameworks</a></li>
<li><a href="benchmarks.html">Benchmarks</a></li>
<li><a href="practices.html">Good Practices</a></li>
<li><a href="tools.html">Useful Tools</a></li>
</ul>
</div>
<div class="col(9,9,12)">
<h2 id="installation">Installation</h2>
<p>Mithril is available from a variety of sources:</p>
<hr>
<h3 id="direct-download">Direct download</h3>
<p>You can <a href="http://lhorie.github.io/mithril/mithril.min.zip">download a zip of the latest version version here</a>.</p>
<p>Links to older versions can be found in the <a href="change-log.html">change log</a>.</p>
<p>In order to use Mithril, extract it from the zip file and point a script tag to the <code>.js</code> file:</p>
<pre><code class="lang-markup">&lt;script src=&quot;mithril.min.js&quot;&gt;&lt;/script&gt;</code></pre>
<hr>
<h3 id="cdns-content-delivery-networks-">CDNs (Content Delivery Networks)</h3>
<p>You can also find Mithril in <a href="http://cdnjs.com/libraries/mithril/">cdnjs</a> and <a href="http://www.jsdelivr.com/#!mithril">jsDelivr</a>.</p>
<p>Content delivery networks allow the library to be cached across different websites that use the same version of the framework, and help reduce latency by serving the files from a server that is physically near the user&#39;s location.</p>
<h4 id="cdnjs">cdnjs</h4>
<pre><code class="lang-markup">&lt;script src=&quot;//cdnjs.cloudflare.com/ajax/libs/mithril/0.1.16/mithril.min.js&quot;&gt;&lt;/script&gt;</code></pre>
<h4 id="jsdelivr">jsDelivr</h4>
<pre><code class="lang-markup">&lt;script src=&quot;//cdn.jsdelivr.net/mithril/0.1.16/mithril.min.js&quot;&gt;&lt;/script&gt;</code></pre>
<hr>
<h3 id="npm">NPM</h3>
<p>NPM is the default package manager for <a href="http://nodejs.org/">NodeJS</a>. If you&#39;re using NodeJS already or planning on using <a href="http://gruntjs.com/">Grunt</a> to create a build system, you can use NPM to conveniently keep up-to-date with Mithril versions.</p>
<p>Assuming you have NodeJS installed, you can download Mithril by typing this:</p>
<pre><code>npm install mithril</code></pre>
<p>Then, to use Mithril, point a script tag to the downloaded file:</p>
<pre><code class="lang-markup">&lt;script src=&quot;/node_modules/mithril/mithril.min.js&quot;&gt;&lt;/script&gt;</code></pre>
<hr>
<h3 id="bower">Bower</h3>
<p><a href="http://bower.io">Bower</a> is a package manager for <a href="http://nodejs.org/">NodeJS</a>. If you&#39;re using NodeJS already or planning on using <a href="http://gruntjs.com/">Grunt</a> to create a build system, you can use Bower to conveniently keep up-to-date with Mithril versions.</p>
<p>Assuming you have NodeJS installed, you can install Bower by typing this in the command line:</p>
<pre><code>npm install -g bower</code></pre>
<p>And you can download Mithril by typing this:</p>
<pre><code>bower install mithril</code></pre>
<p>Then, to use Mithril, point a script tag to the downloaded file:</p>
<pre><code class="lang-markup">&lt;script src=&quot;/bower_components/mithril/mithril.min.js&quot;&gt;&lt;/script&gt;</code></pre>
<hr>
<h3 id="component">Component</h3>
<p><a href="http://component.io">Component</a> is another package manager for <a href="http://nodejs.org/">NodeJS</a>. If you&#39;re using NodeJS already or planning on using <a href="http://gruntjs.com/">Grunt</a> to create a build system, you can use Component to conveniently keep up-to-date with Mithril versions.</p>
<p>Assuming you have NodeJS installed, you can install Bower by typing this in the command line:</p>
<pre><code>npm install -g component</code></pre>
<p>And you can download Mithril by typing this:</p>
<pre><code>component install lhorie/mithril</code></pre>
<p>Then, to use Mithril, point a script tag to the downloaded file:</p>
<pre><code class="lang-markup">&lt;script src=&quot;/components/lhorie/mithril/master/mithril.min.js&quot;&gt;&lt;/script&gt;</code></pre>
<hr>
<h3 id="rails">Rails</h3>
<p>Jordan Humphreys created a gem to allow integration with Rails:</p>
<p><a href="Mithril-Rails"><a href="https://github.com/mrsweaters/mithril-rails">https://github.com/mrsweaters/mithril-rails</a></a></p>
<p>It includes support for the <a href="https://github.com/insin/msx">MSX</a> HTML templating syntax from Jonathan Buchanan.</p>
<hr>
<h3 id="github">Github</h3>
<p>You can also fork the latest stable project <a href="https://github.com/lhorie/mithril">directly from Github</a>.</p>
<p>If you want to use the bleeding edge version, you can <a href="https://github.com/lhorie/mithril">fork the development repository</a>.</p>
<p>Note that Mithril uses the <code>next</code> branch as the stable branch, instead of <code>master</code>, because contributors usually use <code>master</code> for pull requests. Therefore, the <code>master</code> branch should be considered unstable, and should not be used.</p>
<p>Be aware that even though Mithril has tests running in a continuous integration environment, the bleeding edge version might occasionally break. If you&#39;re interested in helping improve Mithril, you&#39;re welcome to use the bleeding edge version and report any bugs that you find.</p>
<p>In order to update a forked version of Mithril, <a href="https://help.github.com/articles/syncing-a-fork">follow the instructions on this page</a>.</p>
</div>
</div>
</div>
</section>
</main>
<footer>
<div class="container">
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
<br />&copy; 2014 Leo Horie
</div>
</footer>
<script src="lib/prism/prism.js"></script>
</body>
</html>

View file

@ -0,0 +1,159 @@
<!doctype html>
<html>
<head>
<title>Mithril</title>
<link href="lib/prism/prism.css" rel="stylesheet" />
<link href="style.css" rel="stylesheet" />
</head>
<body>
<header>
<nav class="container">
<a href="index.html" class="logo"><span>&#9675;</span> Mithril</a>
<a href="getting-started.html">Guide</a>
<a href="mithril.html">API</a>
<a href="community.html">Community</a>
<a href="mithril.min.zip">Download</a>
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
</nav>
</header>
<main>
<section class="content">
<div class="container">
<div class="row">
<div class="col(3,3,12)">
<h2 id="core-topics">Core Topics</h2>
<ul>
<li><a href="installation.html">Installation</a></li>
<li><a href="getting-started.html">Getting Started</a></li>
<li><a href="routing.html">Routing</a></li>
<li><a href="web-services.html">Web Services</a></li>
<li><a href="components.html">Components</a></li>
</ul>
<h2 id="advanced-topics.html">Advanced Topics</h2>
<ul>
<li><a href="compiling-templates.html">Compiling Templates</a></li>
<li><a href="auto-redrawing.html">The Auto-Redrawing System</a></li>
<li><a href="integration.html">Integrating with Other Libraries</a></li>
</ul>
<h2 id="misc">Misc</h2>
<ul>
<li><a href="comparison.html">Differences from Other MVC Frameworks</a></li>
<li><a href="benchmarks.html">Benchmarks</a></li>
<li><a href="practices.html">Good Practices</a></li>
<li><a href="tools.html">Useful Tools</a></li>
</ul>
</div>
<div class="col(9,9,12)">
<h2 id="integrating-with-other-libraries">Integrating with Other Libraries</h2>
<p>Integration with third party libraries can be achieved via the <code>config</code> attribute of virtual elements.</p>
<p>It&#39;s recommended that you encapsulate integration code in a component.</p>
<p>The example below shows a simple component that integrates with the <a href="http://ivaynberg.github.io/select2/">select2 library</a>.</p>
<pre><code class="lang-javascript">//Select2 component (assumes both jQuery and Select2 are included in the page)
/** @namespace */
var select2 = {};
/**
select2 config factory. The params in this doc refer to properties of the `ctrl` argument
@param {Object} data - the data with which to populate the &lt;option&gt; list
@param {number} value - the id of the item in `data` that we want to select
@param {function(Object id)} onchange - the event handler to call when the selection changes.
`id` is the the same as `value`
*/
select2.config = function(ctrl) {
return function(element, isInitialized) {
var el = $(element);
if (!isInitialized) {
//set up select2 (only if not initialized already)
el.select2()
//this event handler updates the controller when the view changes
.on(&quot;change&quot;, function(e) {
//integrate with the auto-redrawing system...
m.startComputation();
//...so that Mithril autoredraws the view after calling the controller callback
if (typeof ctrl.onchange == &quot;function&quot;) ctrl.onchange(el.select2(&quot;val&quot;));
m.endComputation();
//end integration
});
}
//update the view with the latest controller value
el.select2(&quot;val&quot;, ctrl.value);
}
}
//this view implements select2&#39;s `&lt;select&gt;` progressive enhancement mode
select2.view = function(ctrl) {
return m(&quot;select&quot;, {config: select2.config(ctrl)}, [
ctrl.data.map(function(item) {
return m(&quot;option&quot;, {value: item.id}, item.name)
})
]);
};
//end component
//usage
var dashboard = {};
dashboard.controller = function() {
//list of users to show
this.data = [{id: 1, name: &quot;John&quot;}, {id: 2, name: &quot;Mary&quot;}, {id: 3, name: &quot;Jane&quot;}];
//select Mary
this.currentUser = this.data[1];
this.changeUser = function(id) {
console.log(id)
};
}
dashboard.view = function(ctrl) {
return m(&quot;div&quot;, [
m(&quot;label&quot;, &quot;User:&quot;),
select2.view({data: ctrl.data, value: ctrl.currentUser.id, onchange: ctrl.changeUser})
]);
}
m.module(document.body, dashboard);</code></pre>
<p><code>select2.config</code> is a factory that creates a <code>config</code> function based on a given controller. We declare this outside of the <code>select2.view</code> function to avoid cluttering the template.</p>
<p>The <code>config</code> function created by our factory only runs the initialization code if it hasn&#39;t already. This <code>if</code> statement is important, because this function may be called multiple times by Mithril&#39;s auto-redrawing system and we don&#39;t want to re-initialize select2 at every redraw.</p>
<p>The initialization code defines a <code>change</code> event handler. Because this handler is not created using Mithril&#39;s templating engine (i.e. we&#39;re not defining an attribute in a virtual element), we must manually integrate it to the auto-redrawing system.</p>
<p>This can be done by simply calling <code>m.startComputation</code> at the beginning, and <code>m.endComputation</code> at the end of the function. You must add a pair of these calls for each asynchronous execution thread, unless the thread is already integrated.</p>
<p>For example, if you were to call a web service using <code>m.request</code>, you would not need to add more calls to <code>m.startComputation</code> / <code>m.endComputation</code> (you would still need the first pair in the event handler, though).</p>
<p>On the other hand, if you were to call a web service using jQuery, then you would be responsible for adding a <code>m.startComputation</code> call before the jQuery ajax call, and for adding a <code>m.endComputation</code> call at the end of the completion callback, in addition to the calls within the <code>change</code> event handler. Refer to the <a href="auto-redrawing.html"><code>auto-redrawing</code></a> guide for an example.</p>
<p>One important note about the <code>config</code> method is that you should avoid calling <code>m.redraw</code>, <code>m.startComputation</code> and <code>m.endComputation</code> in the <code>config</code> function&#39;s execution thread. (An execution thread is basically any amount of code that runs before other asynchronous threads start to run.)</p>
<p>While Mithril technically does support this use case, relying on multiple redraw passes degrades performance and makes it possible to code yourself into an infinite execution loop situation, which is extremely difficult to debug.</p>
<p>The <code>dashboard</code> module in the example shows how a developer would consume the select2 component.</p>
<p>You should always document integration components so that others can find out what attribute parameters can be used to initialize the component.</p>
<hr>
<h2 id="integrating-to-legacy-code">Integrating to legacy code</h2>
<p>If you need to add separate widgets to different places on a same page, you can simply initialize each widget as you would a regular Mithril application (i.e. use <code>m.render</code>, <code>m.module</code> or <code>m.route</code>).</p>
<p>There&#39;s just one caveat: while simply initializing multiple &quot;islands&quot; in this fashion works, their initialization calls are not aware of each other and can cause redraws too frequently. To optimize rendering, you should add a <code>m.startComputation</code> call before the first widget initialization call, and a <code>m.endComputation</code> after the last widget initialization call in each execution thread.</p>
<pre><code>m.startComputation()
m.module(document.getElementById(&quot;widget1-container&quot;), widget1)
m.module(document.getElementById(&quot;widget2-container&quot;), widget1)
m.endComputation()</code></pre>
</div>
</div>
</div>
</section>
</main>
<footer>
<div class="container">
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
<br />&copy; 2014 Leo Horie
</div>
</footer>
<script src="lib/prism/prism.js"></script>
</body>
</html>

View file

@ -0,0 +1,126 @@
/**
* prism.js default theme for JavaScript, CSS and HTML
* Based on dabblet (http://dabblet.com)
* @author Lea Verou
*/
code[class*="language-"],
pre[class*="language-"] {
color: black;
text-shadow: 0 1px white;
font-family: Consolas, Monaco, 'Andale Mono', monospace;
direction: ltr;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection,
code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection {
text-shadow: none;
background: #b3d4fc;
}
pre[class*="language-"]::selection, pre[class*="language-"] ::selection,
code[class*="language-"]::selection, code[class*="language-"] ::selection {
text-shadow: none;
background: #b3d4fc;
}
@media print {
code[class*="language-"],
pre[class*="language-"] {
text-shadow: none;
}
}
/* Code blocks */
pre[class*="language-"] {
padding: 1em;
margin: .5em 0;
overflow: auto;
}
:not(pre) > code[class*="language-"],
pre[class*="language-"] {
background: #f5f2f0;
}
/* Inline code */
:not(pre) > code[class*="language-"] {
padding: .1em;
border-radius: .3em;
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: slategray;
}
.token.punctuation {
color: #999;
}
.namespace {
opacity: .7;
}
.token.property,
.token.tag,
.token.boolean,
.token.number,
.token.constant,
.token.symbol {
color: #905;
}
.token.selector,
.token.attr-name,
.token.string,
.token.builtin {
color: #690;
}
.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string,
.token.variable {
color: #a67f59;
background: hsla(0,0%,100%,.5);
}
.token.atrule,
.token.attr-value,
.token.keyword {
color: #07a;
}
.token.regex,
.token.important {
color: #e90;
}
.token.important {
font-weight: bold;
}
.token.entity {
cursor: help;
}

View file

@ -0,0 +1,9 @@
/**
* Prism: Lightweight, robust, elegant syntax highlighting
* MIT license http://www.opensource.org/licenses/mit-license.php/
* @author Lea Verou http://lea.verou.me
*/(function(){var e=/\blang(?:uage)?-(?!\*)(\w+)\b/i,t=self.Prism={util:{type:function(e){return Object.prototype.toString.call(e).match(/\[object (\w+)\]/)[1]},clone:function(e){var n=t.util.type(e);switch(n){case"Object":var r={};for(var i in e)e.hasOwnProperty(i)&&(r[i]=t.util.clone(e[i]));return r;case"Array":return e.slice()}return e}},languages:{extend:function(e,n){var r=t.util.clone(t.languages[e]);for(var i in n)r[i]=n[i];return r},insertBefore:function(e,n,r,i){i=i||t.languages;var s=i[e],o={};for(var u in s)if(s.hasOwnProperty(u)){if(u==n)for(var a in r)r.hasOwnProperty(a)&&(o[a]=r[a]);o[u]=s[u]}return i[e]=o},DFS:function(e,n){for(var r in e){n.call(e,r,e[r]);t.util.type(e)==="Object"&&t.languages.DFS(e[r],n)}}},highlightAll:function(e,n){var r=document.querySelectorAll('code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code');for(var i=0,s;s=r[i++];)t.highlightElement(s,e===!0,n)},highlightElement:function(r,i,s){var o,u,a=r;while(a&&!e.test(a.className))a=a.parentNode;if(a){o=(a.className.match(e)||[,""])[1];u=t.languages[o]}if(!u)return;r.className=r.className.replace(e,"").replace(/\s+/g," ")+" language-"+o;a=r.parentNode;/pre/i.test(a.nodeName)&&(a.className=a.className.replace(e,"").replace(/\s+/g," ")+" language-"+o);var f=r.textContent;if(!f)return;f=f.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/\u00a0/g," ");var l={element:r,language:o,grammar:u,code:f};t.hooks.run("before-highlight",l);if(i&&self.Worker){var c=new Worker(t.filename);c.onmessage=function(e){l.highlightedCode=n.stringify(JSON.parse(e.data),o);t.hooks.run("before-insert",l);l.element.innerHTML=l.highlightedCode;s&&s.call(l.element);t.hooks.run("after-highlight",l)};c.postMessage(JSON.stringify({language:l.language,code:l.code}))}else{l.highlightedCode=t.highlight(l.code,l.grammar,l.language);t.hooks.run("before-insert",l);l.element.innerHTML=l.highlightedCode;s&&s.call(r);t.hooks.run("after-highlight",l)}},highlight:function(e,r,i){return n.stringify(t.tokenize(e,r),i)},tokenize:function(e,n,r){var i=t.Token,s=[e],o=n.rest;if(o){for(var u in o)n[u]=o[u];delete n.rest}e:for(var u in n){if(!n.hasOwnProperty(u)||!n[u])continue;var a=n[u],f=a.inside,l=!!a.lookbehind,c=0;a=a.pattern||a;for(var h=0;h<s.length;h++){var p=s[h];if(s.length>e.length)break e;if(p instanceof i)continue;a.lastIndex=0;var d=a.exec(p);if(d){l&&(c=d[1].length);var v=d.index-1+c,d=d[0].slice(c),m=d.length,g=v+m,y=p.slice(0,v+1),b=p.slice(g+1),w=[h,1];y&&w.push(y);var E=new i(u,f?t.tokenize(d,f):d);w.push(E);b&&w.push(b);Array.prototype.splice.apply(s,w)}}}return s},hooks:{all:{},add:function(e,n){var r=t.hooks.all;r[e]=r[e]||[];r[e].push(n)},run:function(e,n){var r=t.hooks.all[e];if(!r||!r.length)return;for(var i=0,s;s=r[i++];)s(n)}}},n=t.Token=function(e,t){this.type=e;this.content=t};n.stringify=function(e,r,i){if(typeof e=="string")return e;if(Object.prototype.toString.call(e)=="[object Array]")return e.map(function(t){return n.stringify(t,r,e)}).join("");var s={type:e.type,content:n.stringify(e.content,r,i),tag:"span",classes:["token",e.type],attributes:{},language:r,parent:i};s.type=="comment"&&(s.attributes.spellcheck="true");t.hooks.run("wrap",s);var o="";for(var u in s.attributes)o+=u+'="'+(s.attributes[u]||"")+'"';return"<"+s.tag+' class="'+s.classes.join(" ")+'" '+o+">"+s.content+"</"+s.tag+">"};if(!self.document){self.addEventListener("message",function(e){var n=JSON.parse(e.data),r=n.language,i=n.code;self.postMessage(JSON.stringify(t.tokenize(i,t.languages[r])));self.close()},!1);return}var r=document.getElementsByTagName("script");r=r[r.length-1];if(r){t.filename=r.src;document.addEventListener&&!r.hasAttribute("data-manual")&&document.addEventListener("DOMContentLoaded",t.highlightAll)}})();;
Prism.languages.markup={comment:/&lt;!--[\w\W]*?-->/g,prolog:/&lt;\?.+?\?>/,doctype:/&lt;!DOCTYPE.+?>/,cdata:/&lt;!\[CDATA\[[\w\W]*?]]>/i,tag:{pattern:/&lt;\/?[\w:-]+\s*(?:\s+[\w:-]+(?:=(?:("|')(\\?[\w\W])*?\1|\w+))?\s*)*\/?>/gi,inside:{tag:{pattern:/^&lt;\/?[\w:-]+/i,inside:{punctuation:/^&lt;\/?/,namespace:/^[\w-]+?:/}},"attr-value":{pattern:/=(?:('|")[\w\W]*?(\1)|[^\s>]+)/gi,inside:{punctuation:/=|>|"/g}},punctuation:/\/?>/g,"attr-name":{pattern:/[\w:-]+/g,inside:{namespace:/^[\w-]+?:/}}}},entity:/&amp;#?[\da-z]{1,8};/gi};Prism.hooks.add("wrap",function(e){e.type==="entity"&&(e.attributes.title=e.content.replace(/&amp;/,"&"))});;
Prism.languages.clike={comment:{pattern:/(^|[^\\])(\/\*[\w\W]*?\*\/|(^|[^:])\/\/.*?(\r?\n|$))/g,lookbehind:!0},string:/("|')(\\?.)*?\1/g,"class-name":{pattern:/((?:(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[a-z0-9_\.\\]+/ig,lookbehind:!0,inside:{punctuation:/(\.|\\)/}},keyword:/\b(if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/g,"boolean":/\b(true|false)\b/g,"function":{pattern:/[a-z0-9_]+\(/ig,inside:{punctuation:/\(/}}, number:/\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee]-?\d+)?)\b/g,operator:/[-+]{1,2}|!|&lt;=?|>=?|={1,3}|(&amp;){1,2}|\|?\||\?|\*|\/|\~|\^|\%/g,ignore:/&(lt|gt|amp);/gi,punctuation:/[{}[\];(),.:]/g};
;
Prism.languages.javascript=Prism.languages.extend("clike",{keyword:/\b(var|let|if|else|while|do|for|return|in|instanceof|function|get|set|new|with|typeof|try|throw|catch|finally|null|break|continue)\b/g,number:/\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee]-?\d+)?|NaN|-?Infinity)\b/g});Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:/(^|[^/])\/(?!\/)(\[.+?]|\\.|[^/\r\n])+\/[gim]{0,3}(?=\s*($|[\r\n,.;})]))/g,lookbehind:!0}});Prism.languages.markup&&Prism.languages.insertBefore("markup","tag",{script:{pattern:/(&lt;|<)script[\w\W]*?(>|&gt;)[\w\W]*?(&lt;|<)\/script(>|&gt;)/ig,inside:{tag:{pattern:/(&lt;|<)script[\w\W]*?(>|&gt;)|(&lt;|<)\/script(>|&gt;)/ig,inside:Prism.languages.markup.tag.inside},rest:Prism.languages.javascript}}});;

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,183 @@
<!doctype html>
<html>
<head>
<title>Mithril</title>
<link href="lib/prism/prism.css" rel="stylesheet" />
<link href="style.css" rel="stylesheet" />
</head>
<body>
<header>
<nav class="container">
<a href="index.html" class="logo"><span>&#9675;</span> Mithril</a>
<a href="getting-started.html">Guide</a>
<a href="mithril.html">API</a>
<a href="community.html">Community</a>
<a href="mithril.min.zip">Download</a>
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
</nav>
</header>
<main>
<section class="content">
<div class="container">
<div class="row">
<div class="col(3,3,12)">
<h2 id="api">API (v0.1.16)</h2>
<h3 id="core">Core</h3>
<ul>
<li><a href="mithril.html">m</a></li>
<li><a href="mithril.prop.html">m.prop</a></li>
<li><a href="mithril.withAttr.html">m.withAttr</a></li>
<li><a href="mithril.module.html">m.module</a></li>
<li><a href="mithril.trust.html">m.trust</a></li>
<li><a href="mithril.render.html">m.render</a></li>
<li><a href="mithril.redraw.html">m.redraw</a></li>
</ul>
<h3 id="routing">Routing</h3>
<ul>
<li><a href="mithril.route.html">m.route</a>
<ul>
<li><a href="mithril.route.html#defining-routes">m.route(rootElement, defaultRoute, routes)</a></li>
<li><a href="mithril.route.html#redirecting">m.route(path, params)</a></li>
<li><a href="mithril.route.html#reading-current-route">m.route()</a></li>
<li><a href="mithril.route.html#mode-abstraction">m.route(element)</a></li>
<li><a href="mithril.route.html#mode">m.route.mode</a></li>
<li><a href="mithril.route.html#param">m.route.param</a></li>
</ul>
</li>
</ul>
<h3 id="data">Data</h3>
<ul>
<li><a href="mithril.request.html">m.request</a></li>
<li><a href="mithril.deferred.html">m.deferred</a></li>
<li><a href="mithril.sync.html">m.sync</a></li>
<li><a href="mithril.computation.html">m.startComputation / m.endComputation</a></li>
</ul>
<h2 id="archive">History</h2>
<ul>
<li><a href="roadmap.html">Roadmap</a></li>
<li><a href="change-log.html">Change log</a></li>
</ul>
</div>
<div class="col(9,9,12)">
<h2 id="m-startcomputation-m-endcomputation">m.startComputation / m.endComputation</h2>
<p>Typically, <code>m.startComputation</code> / <code>m.endComputation</code> don&#39;t need to be called from application space. These methods are only intended to be used by people who are writing libraries that do things asynchronously.</p>
<p>If you need to do custom asynchronous calls without using Mithril&#39;s API, and find that your views are not redrawing, or that you&#39;re being forced to call <a href="mithril.redraw.html"><code>m.redraw</code></a> manually, you should consider using <code>m.startComputation</code> / <code>m.endComputation</code> so that Mithril can intelligently auto-redraw once your custom code finishes running.</p>
<p>In order to integrate an asynchronous code to Mithril&#39;s autoredrawing system, you should call <code>m.startComputation</code> BEFORE making an asynchronous call, and <code>m.endComputation</code> after the asynchronous callback completes.</p>
<pre><code class="lang-javascript">//this service waits 1 second, logs &quot;hello&quot; and then notifies the view that
//it may start redrawing (if no other asynchronous operations are pending)
var doStuff = function() {
m.startComputation(); //call `startComputation` before the asynchronous `setTimeout`
setTimeout(function() {
console.log(&quot;hello&quot;);
m.endComputation(); //call `endComputation` at the end of the callback
}, 1000);
};</code></pre>
<p>To integrate synchronous code, call <code>m.startComputation</code> at the beginning of the method, and <code>m.endComputation</code> at the end.</p>
<pre><code class="lang-javascript">window.onfocus = function() {
m.startComputation(); //call before everything else in the event handler
doStuff();
m.endComputation(); //call after everything else in the event handler
}</code></pre>
<p>For each <code>m.startComputation</code> call a library makes, it MUST also make one and ONLY one corresponding <code>m.endComputation</code> call.</p>
<p>You should not use these methods if your code is intended to run repeatedly (e.g. by using <code>setInterval</code>). If you want to repeatedly redraw the view without necessarily waiting for user input, you should manually call <a href="mithril.redraw.html"><code>m.redraw</code></a> within the repeatable context.</p>
<p>Note that failing to call <code>endComputation</code> after a respective <code>startComputation</code> call will halt the redrawing system. It&#39;s a good idea to wrap exception-prone code in a <code>try</code> block and call <code>m.endComputation</code> from within the respective <code>finally</code> block, in order to prevent rendering from halting.</p>
<pre><code class="lang-javascript">window.onfocus = function() {
m.startComputation();
try {
doStuff();
}
finally {
m.endComputation(); //redraw regardless of whether `doStuff` threw errors
}
}</code></pre>
<hr>
<h3 id="integrating-multiple-execution-threads">Integrating multiple execution threads</h3>
<p>When <a href="integration.html">integrating with third party libraries</a>, you might find that you need to call asynchronous methods from outside of Mithril&#39;s API.</p>
<p>In order to integrate non-trivial asynchronous code to Mithril&#39;s auto-redrawing system, you need to ensure all execution threads call <code>m.startComputation</code> / <code>m.endComputation</code>.</p>
<p>An execution thread is basically any amount of code that runs before other asynchronous threads start to run.</p>
<p>Integrating multiple execution threads can be done in a two different ways: in a layered fashion or in comprehensive fashion</p>
<h4 id="layered-integration">Layered integration</h4>
<p>Layered integration is recommended for modular code where many different APIs may be put together at the application level.</p>
<p>Below is an example where various methods implemented with a third party library can be integrated in layered fashion: any of the methods can be used in isolation or in combination.</p>
<p>Notice how <code>doBoth</code> repeatedly calls <code>m.startComputation</code> since that method calls both <code>doSomething</code> and <code>doAnother</code>. This is perfectly valid: there are three asynchronous computations pending after the <code>jQuery.when</code> method is called, and therefore, three pairs of <code>m.startComputation</code> / <code>m.endComputation</code> in play.</p>
<pre><code class="lang-javascript">var doSomething = function(callback) {
m.startComputation(); //call `startComputation` before the asynchronous AJAX request
return jQuery.ajax(&quot;/something&quot;).done(function() {
if (callback) callback();
m.endComputation(); //call `endComputation` at the end of the callback
});
};
var doAnother = function(callback) {
m.startComputation(); //call `startComputation` before the asynchronous AJAX request
return jQuery.ajax(&quot;/another&quot;).done(function() {
if (callback) callback();
m.endComputation(); //call `endComputation` at the end of the callback
});
};
var doBoth = function(callback) {
m.startComputation(); //call `startComputation` before the asynchronous synchronization method
jQuery.when(doSomething(), doAnother()).then(function() {
if (callback) callback();
m.endComputation(); //call `endComputation` at the end of the callback
})
};</code></pre>
<h4 id="comprehensive-integration">Comprehensive integration</h4>
<p>Comprehensive integration is recommended if integrating a monolithic series of asynchronous operations. In contrast to layered integration, it minimizes the number of <code>m.startComputation</code> / <code>m.endComputation</code> to avoid clutter.</p>
<p>The example below shows a convoluted series of AJAX requests implemented with a third party library.</p>
<pre><code class="lang-javascript">var doSomething = function(callback) {
m.startComputation(); //call `startComputation` before everything else
jQuery.ajax(&quot;/something&quot;).done(function() {
doStuff();
jQuery.ajax(&quot;/another&quot;).done(function() {
doMoreStuff();
jQuery.ajax(&quot;/more&quot;).done(function() {
if (callback) callback();
m.endComputation(); //call `endComputation` at the end of everything
});
});
});
};</code></pre>
<hr>
<h2 id="integrating-to-legacy-code">Integrating to legacy code</h2>
<p>If you need to add separate widgets to different places on a same page, you can simply initialize each widget as you would a regular Mithril application (i.e. use <code>m.render</code>, <code>m.module</code> or <code>m.route</code>).</p>
<p>There&#39;s just one caveat: while simply initializing multiple &quot;islands&quot; in this fashion works, their initialization calls are not aware of each other and can cause redraws too frequently. To optimize rendering, you should add a <code>m.startComputation</code> call before the first widget initialization call, and a <code>m.endComputation</code> after the last widget initialization call in each execution thread.</p>
<pre><code>m.startComputation()
m.module(document.getElementById(&quot;widget1-container&quot;), widget1)
m.module(document.getElementById(&quot;widget2-container&quot;), widget1)
m.endComputation()</code></pre>
<hr>
<h3 id="signature">Signature</h3>
<p><a href="how-to-read-signatures.html">How to read signatures</a></p>
<pre><code class="lang-clike">void startComputation()</code></pre>
<pre><code class="lang-clike">void endComputation()</code></pre>
</div>
</div>
</div>
</section>
</main>
<footer>
<div class="container">
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
<br />&copy; 2014 Leo Horie
</div>
</footer>
<script src="lib/prism/prism.js"></script>
</body>
</html>

64
archive/v0.1.16/mithril.d.ts vendored Normal file
View file

@ -0,0 +1,64 @@
//Mithril type definitions for Typescript
interface MithrilStatic {
(selector: string, attributes: Object, children?: any): MithrilVirtualElement;
(selector: string, children?: any): MithrilVirtualElement;
prop(value?: any): (value?: any) => any;
withAttr(property: string, callback: (value: any) => void): (e: Event) => any;
module(rootElement: Element, module: MithrilModule): void;
trust(html: string): String;
render(rootElement: Element, children?: any): void;
render(rootElement: HTMLDocument, children?: any): void;
redraw(): void;
route(rootElement: Element, defaultRoute: string, routes: { [key: string]: MithrilModule }): void;
route(rootElement: HTMLDocument, defaultRoute: string, routes: { [key: string]: MithrilModule }): void;
route(path: string, params?: any, shouldReplaceHistory?: boolean): void;
route(): string;
route(element: Element, isInitialized: boolean): void;
request(options: MithrilXHROptions): MithrilPromise;
deferred(): MithrilDeferred;
sync(promises: MithrilPromise[]): MithrilPromise;
startComputation(): void;
endComputation(): void;
}
interface MithrilVirtualElement {
tag: string;
attrs: Object;
children: any;
}
interface MithrilModule {
controller: Function;
view: Function;
}
interface MithrilDeferred {
resolve(value?: any): void;
reject(value?: any): void;
promise: MithrilPromise;
}
interface MithrilPromise {
(value?: any): any;
then(successCallback?: (value: any) => any, errorCallback?: (value: any) => any): MithrilPromise;
}
interface MithrilXHROptions {
method: string;
url: string;
user?: string;
password?: string;
data?: any;
background?: boolean;
unwrapSuccess?(data: any): any;
unwrapError?(data: any): any;
serialize?(dataToSerialize: any): string;
deserialize?(dataToDeserialize: string): any;
extract?(xhr: XMLHttpRequest, options: MithrilXHROptions);
type?(data: Object): void;
config?(xhr: XMLHttpRequest, options: MithrilXHROptions)
}
declare var Mithril: MithrilStatic;
declare var m: MithrilStatic;

View file

@ -0,0 +1,239 @@
<!doctype html>
<html>
<head>
<title>Mithril</title>
<link href="lib/prism/prism.css" rel="stylesheet" />
<link href="style.css" rel="stylesheet" />
</head>
<body>
<header>
<nav class="container">
<a href="index.html" class="logo"><span>&#9675;</span> Mithril</a>
<a href="getting-started.html">Guide</a>
<a href="mithril.html">API</a>
<a href="community.html">Community</a>
<a href="mithril.min.zip">Download</a>
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
</nav>
</header>
<main>
<section class="content">
<div class="container">
<div class="row">
<div class="col(3,3,12)">
<h2 id="api">API (v0.1.16)</h2>
<h3 id="core">Core</h3>
<ul>
<li><a href="mithril.html">m</a></li>
<li><a href="mithril.prop.html">m.prop</a></li>
<li><a href="mithril.withAttr.html">m.withAttr</a></li>
<li><a href="mithril.module.html">m.module</a></li>
<li><a href="mithril.trust.html">m.trust</a></li>
<li><a href="mithril.render.html">m.render</a></li>
<li><a href="mithril.redraw.html">m.redraw</a></li>
</ul>
<h3 id="routing">Routing</h3>
<ul>
<li><a href="mithril.route.html">m.route</a>
<ul>
<li><a href="mithril.route.html#defining-routes">m.route(rootElement, defaultRoute, routes)</a></li>
<li><a href="mithril.route.html#redirecting">m.route(path, params)</a></li>
<li><a href="mithril.route.html#reading-current-route">m.route()</a></li>
<li><a href="mithril.route.html#mode-abstraction">m.route(element)</a></li>
<li><a href="mithril.route.html#mode">m.route.mode</a></li>
<li><a href="mithril.route.html#param">m.route.param</a></li>
</ul>
</li>
</ul>
<h3 id="data">Data</h3>
<ul>
<li><a href="mithril.request.html">m.request</a></li>
<li><a href="mithril.deferred.html">m.deferred</a></li>
<li><a href="mithril.sync.html">m.sync</a></li>
<li><a href="mithril.computation.html">m.startComputation / m.endComputation</a></li>
</ul>
<h2 id="archive">History</h2>
<ul>
<li><a href="roadmap.html">Roadmap</a></li>
<li><a href="change-log.html">Change log</a></li>
</ul>
</div>
<div class="col(9,9,12)">
<h2 id="m-deferred">m.deferred</h2>
<p>This is a low-level method in Mithril. It&#39;s a modified version of the Thenable API.</p>
<p>A deferred is an asynchrony monad. It exposes a <code>promise</code> property which can <em>bind</em> callbacks to build a computation tree.</p>
<p>The deferred object can then <em>apply</em> a value by calling either <code>resolve</code> or <code>reject</code>, which then dispatches the value to be processed to the computation tree.</p>
<p>Each computation function takes a value as a parameter and is expected to return another value, which in turns is forwarded along to the next computation function (or functions) in the tree.</p>
<p>The deferred object returned by <code>m.deferred</code> has two methods: <code>resolve</code> and <code>reject</code>, and one property called <code>promise</code>. The methods can be called to dispatch a value to the promise tree. The <code>promise</code> property is the root of the promise tree. It has a method <code>then</code> which takes a <code>successCallback</code> and a <code>errorCallback</code> callbacks. Calling the <code>then</code> method attaches the computations represented by <code>successCallback</code> and <code>errorCallback</code> to the promise, which will be called when either <code>resolve</code> or <code>reject</code> is called. The <code>then</code> method returns a child promise, which, itself, can have more child promises, recursively.</p>
<p>The <code>promise</code> object is actually a function - specifically, it&#39;s an <a href="mithril.prop.html"><code>m.prop</code></a> getter-setter, which gets populated with the value returned by <code>successCallback</code> if the promise is resolved successfully.</p>
<p>Note that Mithril promises are not automatically integrated to its automatic redrawing system. If you wish to use third party asynchronous libraries (for example, <code>jQuery.ajax</code>), you should also consider using <a href="mithril.computation.html"><code>m.startComputation</code> / <code>m.endComputation</code></a> if you want views to redraw after requests complete.</p>
<hr>
<h3 id="usage">Usage</h3>
<pre><code class="lang-javascript">//standalone usage
var greetAsync = function() {
var deferred = m.deferred();
setTimeout(function() {
deferred.resolve(&quot;hello&quot;);
}, 1000);
return deferred.promise;
};
greetAsync()
.then(function(value) {return value + &quot; world&quot;})
.then(function(value) {console.log(value)}); //logs &quot;hello world&quot; after 1 second</code></pre>
<hr>
<h4 id="retrieving-a-value-via-the-getter-setter-api">Retrieving a value via the getter-setter API</h4>
<pre><code class="lang-javascript">//asynchronous service
var greetAsync = function() {
var deferred = m.deferred();
setTimeout(function() {
deferred.resolve(&quot;hello&quot;);
}, 1000);
return deferred.promise;
};
//asynchronous consumer
var greeting = greetAsync()
var processed = greeting.then(function(value) {return value + &quot; world&quot;})
console.log(greeting()) // undefined - because `deferred.resolve` has not been called yet
setTimeout(function() {
//now `deferred.resolve` has been called
console.log(greeting()) // &quot;hello&quot;
console.log(processed()) // &quot;hello world&quot;
}, 2000)</code></pre>
<hr>
<h4 id="integrating-to-the-mithril-redrawing-system">Integrating to the Mithril redrawing system</h4>
<pre><code class="lang-javascript">//asynchronous service
var greetAsync = function() {
m.startComputation();
var deferred = m.deferred();
setTimeout(function() {
deferred.resolve(&quot;hello&quot;);
m.endComputation();
}, 1000);
return deferred.promise;
};</code></pre>
<hr>
<h3 id="recoverable-vs-unrecoverable-errors">Recoverable vs Unrecoverable errors</h3>
<p>Recoverable errors are exceptions that are deliberately thrown by the application developer to signal an abnormal condition in the business logic. You can throw errors in this way if a computation needs to forward a failure condition to downstream promises.</p>
<p>In the example below we throw a recoverable error. It rejects the subsequent promise, and logs the string <code>&quot;error&quot;</code> to the console</p>
<pre><code class="lang-javascript">//standalone usage
var greetAsync = function() {
var deferred = m.deferred();
setTimeout(function() {
deferred.resolve(&quot;hello&quot;);
}, 1000);
return deferred.promise;
};
greetAsync()
.then(function(data) {
if (data != &quot;hi&quot;) throw new Error(&quot;wrong greeting&quot;)
})
.then(function() {
console.log(&quot;success&quot;)
}, function() {
console.log(&quot;error&quot;)
})</code></pre>
<p>Unrecoverable errors are exceptions that happen at runtime due to bugs in the code.</p>
<p>In the example below, calling the inexistent <code>foo.bar.baz</code> triggers a <code>ReferenceError</code>. Mithril does not handle this error in any way: it aborts execution and dumps the error information in the console.</p>
<pre><code class="lang-javascript">//standalone usage
var greetAsync = function() {
var deferred = m.deferred();
setTimeout(function() {
deferred.resolve(&quot;hello&quot;);
}, 1000);
return deferred.promise;
};
greetAsync().then(function() {
foo.bar.baz()
})</code></pre>
<hr>
<h3 id="differences-from-promises-a-">Differences from Promises/A+</h3>
<p>For the most part, Mithril promises behave as you&#39;d expect a <a href="http://promises-aplus.github.io/promises-spec/">Promise/A+</a> promise to behave, but with a few key differences:</p>
<p>Mithril promises forward a value downstream if a resolution callback returns <code>undefined</code>. This allows simpler debugging of promise chains:</p>
<pre><code class="lang-javascript">var data = m.request({method: &quot;GET&quot;, url: &quot;/data&quot;})
.then(console.log) //Mithril promises let us debug like this
.then(doStuff)
var data = m.request({method: &quot;GET&quot;, url: &quot;/data&quot;})
.then(function(value) { // Promises/A+ would require us to declare an anonymous function
console.log(value) // here&#39;s the debugging snippet
return value // and we need to remember to return the value as well
})
.then(doStuff) // or else `doStuff` will break</code></pre>
<p>Another subtle difference is that the Promises/A+ require a callback to run in a different execution context than its respective <code>then</code> method. This requirement exists to support an obscure edge cases and incurs <a href="http://thanpol.as/javascript/promises-a-performance-hits-you-should-be-aware-of/">a significant performance hit on each link of a promise chain</a>. To be more specific, the performance hit can come either in the form of a 4ms minimum delay (if the implementation uses <code>setTimeout</code>), or from having to load a <a href="https://raw.githubusercontent.com/NobleJS/setImmediate/master/setImmediate.js">bunch of hacky polyfill code</a> for a <a href="https://developer.mozilla.org/en-US/docs/Web/API/Window.setImmediate">feature that is not being considered for addition by some browser vendors</a>.</p>
<p>To illustrate the difference between Mithril and A+ promises, consider the code below:</p>
<pre><code class="lang-javascript">var deferred = m.deferred()
deferred.promise.then(function() {
console.log(1)
})
deferred.resolve(&quot;value&quot;)
console.log(2)</code></pre>
<p>In the example above, A+ promises are required to log <code>2</code> before logging <code>1</code>, whereas Mithril logs <code>1</code> before <code>2</code>. Typically <code>resolve</code>/<code>reject</code> are called asynchronously after the <code>then</code> method is called, so normally this difference does not matter.</p>
<p>One final difference is in how Mithril handles exceptions: if exceptions are thrown from within a success or error callbacks, A+ promises always reject the downstreams (and thus swallow the exception). Mithril does so only with errors that are subclasses of the Error class. Errors that are instances of the Error class itself are not caught by Mithril. This allows unrecoverable runtime errors to get thrown to the console w/ standard stack traces, while allowing developers to create application-space errors normally.</p>
<hr>
<h3 id="signature">Signature</h3>
<p><a href="how-to-read-signatures.html">How to read signatures</a></p>
<pre><code class="lang-clike">Deferred deferred()
where:
Deferred :: Object { Promise promise, void resolve(any value), void reject(any value) }
Promise :: GetterSetter { Promise then(any successCallback(any value), any errorCallback(any value)) }
GetterSetter :: any getterSetter([any value])</code></pre>
<ul>
<li><p><strong>GetterSetter { Promise then([any successCallback(any value) [, any errorCallback(any value)]]) } promise</strong></p>
<p>A promise has a method called <code>then</code> which takes two computation callbacks as parameters.</p>
<p>The <code>then</code> method returns another promise whose computations (if any) receive their inputs from the parent promise&#39;s computation.</p>
<p>A promise is also a getter-setter (see <a href="mithril.prop"><code>m.prop</code></a>). After a call to either <code>resolve</code> or <code>reject</code>, it holds the result of the parent&#39;s computation (or the <code>resolve</code>/<code>reject</code> value, if the promise has no parent promises)</p>
<ul>
<li><p><strong>Promise then([any successCallback(any value) [, any errorCallback(any value)]])</strong></p>
<p>This method accepts two callbacks which process a value passed to the <code>resolve</code> and <code>reject</code> methods, respectively, and pass the processed value to the returned promise</p>
<ul>
<li><p><strong>any successCallback(any value)</strong> (optional)</p>
<p>The <code>successCallback</code> is called if <code>resolve</code> is called in the root <code>deferred</code>.</p>
<p>The default value (if this parameter is falsy) is the identity function <code>function(value) {return value}</code></p>
<p>If this function returns undefined, then it passes the <code>value</code> argument to the next step in the thennable queue, if any</p>
</li>
<li><p><strong>any errorCallback(any value)</strong> (optional)</p>
<p>The <code>errorCallback</code> is called if <code>reject</code> is called in the root <code>deferred</code>.</p>
<p>The default value (if this parameter is falsy) is the identity function <code>function(value) {return value}</code></p>
<p>If this function returns undefined, then it passes the <code>value</code> argument to the next step in the thennable queue, if any</p>
</li>
<li><p><strong>returns Promise promise</strong></p>
</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>void resolve(any value)</strong></p>
<p>This method passes a value to the <code>successCallback</code> of the deferred object&#39;s child promise</p>
</li>
<li><p><strong>void reject(any value)</strong></p>
<p>This method passes a value to the <code>errorCallback</code> of the deferred object&#39;s child promise</p>
</li>
</ul>
</div>
</div>
</div>
</section>
</main>
<footer>
<div class="container">
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
<br />&copy; 2014 Leo Horie
</div>
</footer>
<script src="lib/prism/prism.js"></script>
</body>
</html>

View file

@ -0,0 +1,318 @@
<!doctype html>
<html>
<head>
<title>Mithril</title>
<link href="lib/prism/prism.css" rel="stylesheet" />
<link href="style.css" rel="stylesheet" />
</head>
<body>
<header>
<nav class="container">
<a href="index.html" class="logo"><span>&#9675;</span> Mithril</a>
<a href="getting-started.html">Guide</a>
<a href="mithril.html">API</a>
<a href="community.html">Community</a>
<a href="mithril.min.zip">Download</a>
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
</nav>
</header>
<main>
<section class="content">
<div class="container">
<div class="row">
<div class="col(3,3,12)">
<h2 id="api">API (v0.1.16)</h2>
<h3 id="core">Core</h3>
<ul>
<li><a href="mithril.html">m</a></li>
<li><a href="mithril.prop.html">m.prop</a></li>
<li><a href="mithril.withAttr.html">m.withAttr</a></li>
<li><a href="mithril.module.html">m.module</a></li>
<li><a href="mithril.trust.html">m.trust</a></li>
<li><a href="mithril.render.html">m.render</a></li>
<li><a href="mithril.redraw.html">m.redraw</a></li>
</ul>
<h3 id="routing">Routing</h3>
<ul>
<li><a href="mithril.route.html">m.route</a>
<ul>
<li><a href="mithril.route.html#defining-routes">m.route(rootElement, defaultRoute, routes)</a></li>
<li><a href="mithril.route.html#redirecting">m.route(path, params)</a></li>
<li><a href="mithril.route.html#reading-current-route">m.route()</a></li>
<li><a href="mithril.route.html#mode-abstraction">m.route(element)</a></li>
<li><a href="mithril.route.html#mode">m.route.mode</a></li>
<li><a href="mithril.route.html#param">m.route.param</a></li>
</ul>
</li>
</ul>
<h3 id="data">Data</h3>
<ul>
<li><a href="mithril.request.html">m.request</a></li>
<li><a href="mithril.deferred.html">m.deferred</a></li>
<li><a href="mithril.sync.html">m.sync</a></li>
<li><a href="mithril.computation.html">m.startComputation / m.endComputation</a></li>
</ul>
<h2 id="archive">History</h2>
<ul>
<li><a href="roadmap.html">Roadmap</a></li>
<li><a href="change-log.html">Change log</a></li>
</ul>
</div>
<div class="col(9,9,12)">
<h2 id="m">m</h2>
<p>This is a convenience method to compose virtual elements that can be rendered via <a href="mithril.render"><code>m.render()</code></a>.</p>
<p>You are encouraged to use CSS selectors to define virtual elements. See &quot;Signature&quot; section for details.</p>
<hr>
<h3 id="usage">Usage</h3>
<p>You can use simple tag selectors to make templates resemble HTML:</p>
<pre><code class="lang-javascript">m(&quot;br&quot;); //yields a virtual element that represents &lt;br&gt;
m(&quot;div&quot;, &quot;Hello&quot;); //yields &lt;div&gt;Hello&lt;/div&gt;
m(&quot;div&quot;, {class: &quot;container&quot;}, &quot;Hello&quot;); //yields &lt;div class=&quot;container&quot;&gt;Hello&lt;/div&gt;</code></pre>
<p>Note that the output value from <code>m()</code> is not an actual DOM element. In order to turn the virtual element into a real DOM element, you must call <a href="mithril.render"><code>m.render()</code></a>.</p>
<pre><code class="lang-javascript">m.render(document.body, m(&quot;br&quot;)); //puts a &lt;br&gt; in &lt;body&gt;</code></pre>
<p>You can also use more complex CSS selectors:</p>
<pre><code class="lang-javascript">m(&quot;.container&quot;); //yields &lt;div class=&quot;container&quot;&gt;&lt;/div&gt;
m(&quot;#layout&quot;); //yields &lt;div id=&quot;layout&quot;&gt;&lt;/div&gt;
m(&quot;a[name=top]&quot;); //yields &lt;a name=&quot;top&quot;&gt;&lt;/a&gt;
m(&quot;[contenteditable]&quot;); //yields &lt;div contenteditable&gt;&lt;/div&gt;
m(&quot;a#google.external[href=&#39;http://google.com&#39;]&quot;, &quot;Google&quot;); //yields &lt;a id=&quot;google&quot; class=&quot;external&quot; href=&quot;http://google.com&quot;&gt;Google&lt;/a&gt;</code></pre>
<p>Each <code>m()</code> call creates a virtual DOM element, that is, a Javascript object that represents a DOM element, and which is eventually converted into one.</p>
<p>You can, of course, nest virtual elements:</p>
<pre><code class="lang-javascript">m(&quot;ul&quot;, [
m(&quot;li&quot;, &quot;item 1&quot;),
m(&quot;li&quot;, &quot;item 2&quot;),
]);
/*
yields
&lt;ul&gt;
&lt;li&gt;item 1&lt;/li&gt;
&lt;li&gt;item 2&lt;/li&gt;
&lt;/ul&gt;
*/</code></pre>
<p>Be aware that when nesting virtual elements, the child elements must be in an Array.</p>
<hr>
<p>The CSS selector syntax (e.g. <code>a#google.external[href=&#39;http://google.com&#39;]</code>) is meant to be used for declaring static attributes in the element, i.e. attribute values that don&#39;t change dynamically when the user interacts with the app.</p>
<p>The <code>attributes</code> argument (i.e. the second parameter in the <code>m(&quot;div&quot;, {class: &quot;container&quot;}, &quot;Hello&quot;)</code> example) is meant to be used for attributes whose values we want to dynamically populate.</p>
<p>For example, let&#39;s say that you&#39;re generating a link from an entry that comes from a web service:</p>
<pre><code class="lang-javascript">//assume the variable `link` came from a web service
var link = {url: &quot;http://google.com&quot;, title: &quot;Google&quot;}
m(&quot;a&quot;, {href: link.url}, link.title); //yields &lt;a href=&quot;http://google.com&quot;&gt;Google&lt;/a&gt;</code></pre>
<p>Here&#39;s a less trivial example:</p>
<pre><code class="lang-javascript">var links = [
{title: &quot;item 1&quot;, url: &quot;/item1&quot;},
{title: &quot;item 2&quot;, url: &quot;/item2&quot;},
{title: &quot;item 3&quot;, url: &quot;/item3&quot;}
];
m.render(document.body, [
m(&quot;ul.nav&quot;, [
m(&quot;li&quot;, links.map(function(link) {
return m(&quot;a&quot;, {href: link.url}, link.title)
}))
])
]);</code></pre>
<p>yields:</p>
<pre><code class="lang-markup">&lt;body&gt;
&lt;ul class=&quot;nav&quot;&gt;
&lt;li&gt;
&lt;a href=&quot;/item1&quot;&gt;item 1&lt;/a&gt;
&lt;a href=&quot;/item2&quot;&gt;item 2&lt;/a&gt;
&lt;a href=&quot;/item3&quot;&gt;item 3&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/body&gt;</code></pre>
<p>As you can see, flow control is done with vanilla Javascript. This allows the developer to abstract away any aspect of the template at will.</p>
<hr>
<p>Note that you can use both Javascript property names and HTML attribute names to set values in the <code>attributes</code> argument, but you should pass a value of appropriate type. If an attribute has the same name in Javascript and in HTML, then Mithril assumes you&#39;re setting the Javascript property.</p>
<pre><code class="lang-javascript">m(&quot;div&quot;, {class: &quot;widget&quot;}); //yields &lt;div class=&quot;widget&quot;&gt;&lt;/div&gt;
m(&quot;div&quot;, {className: &quot;widget&quot;}); //yields &lt;div class=&quot;widget&quot;&gt;&lt;/div&gt;
m(&quot;input&quot;, {readonly: true}); //yields &lt;input readonly /&gt;
m(&quot;button&quot;, {onclick: alert}); //yields &lt;button&gt;&lt;/button&gt;, which alerts its event argument when clicked</code></pre>
<hr>
<p>Note that you can use JSON syntax if the attribute name you are setting has non-alphanumeric characters:</p>
<pre><code class="lang-javascript">m(&quot;div&quot;, {&quot;data-index&quot;: 1}); //yields &lt;div data-index=&quot;1&quot;&gt;&lt;/div&gt;</code></pre>
<p>You can set inline styles like this:</p>
<pre><code class="lang-javascript">m(&quot;div&quot;, {style: {border: &quot;1px solid red&quot;}}); //yields &lt;div style=&quot;border:1px solid red;&quot;&gt;&lt;/div&gt;</code></pre>
<p>Note that in order to keep the framework lean, Mithril does not auto-append units like <code>px</code> or <code>%</code> to any values. Typically, you should not even be using inline styles to begin with (unless you are dynamically changing them).</p>
<p>Mithril also does not auto-camel-case CSS properties on inline style attributes, so you should use the Javascript syntax when setting them via Javascript objects:</p>
<pre><code class="lang-javascript">m(&quot;div&quot;, {style: {textAlign: &quot;center&quot;}}); //yields &lt;div style=&quot;text-align:center;&quot;&gt;&lt;/div&gt;
//this does not work
m(&quot;div&quot;, {style: {&quot;text-align&quot;: &quot;center&quot;}});</code></pre>
<p>You can, however, use CSS syntax when defining style rules as inline strings:</p>
<pre><code class="lang-javascript">m(&quot;div[style=&#39;text-align:center&#39;]&quot;); //yields &lt;div style=&quot;text-align:center;&quot;&gt;&lt;/div&gt;</code></pre>
<p>One caveat of using the CSS syntax is that it clobbers the <code>style</code> attribute in the DOM element on redraws, so this syntax is not appropriate if you need to use it in conjunction with 3rd party tools that modify the element&#39;s style outside of Mithril&#39;s templates (e.g. via <code>config</code>, which is explained below)</p>
<hr>
<p>You can define a non-HTML-standard attribute called <code>config</code>. This special parameter allows you to call methods on the DOM element after it gets created.</p>
<p>This is useful, for example, if you declare a <code>canvas</code> element and want to use the Javascript API to draw:</p>
<pre><code class="lang-javascript">function draw(element, isInitialized, context) {
//don&#39;t redraw if we did once already
if (isInitialized) return;
var ctx = element.getContext(&quot;2d&quot;);
/* draws stuff */
}
var view = [
m(&quot;canvas&quot;, {config: draw})
]
//this creates the canvas element, and therefore, `isInitialized` is false
m.render(document.body, view);
//here, isInitialized is `true`
m.render(document.body, view);</code></pre>
<p>One common way of using <code>config</code> is in conjunction with <a href="mithril.route"><code>m.route</code></a>, which is an unobtrusive extension to links that allow Mithril&#39;s routing system to work transparently regardless of which routing mode is used.</p>
<pre><code class="lang-javascript">//this link can use any of Mithril&#39;s routing system modes
//(i.e. it can use either the hash, the querystring or the pathname as the router implementation)
//without needing to hard-code any syntax (`#` or `?`) in the `href` attribute.
m(&quot;a[href=&#39;/dashboard&#39;]&quot;, {config: m.route}, &quot;Dashboard&quot;);</code></pre>
<p>The <code>config</code> mechanism can also be used to put focus on form inputs, and call methods that would not be possible to execute via the regular attribute syntax.</p>
<p>It is only meant to be used to call methods on DOM elements that cannot be called otherwise.</p>
<p>It is NOT a &quot;free out-of-jail card&quot;. You should not use this method to modify element properties that could be modified via the <code>attributes</code> argument, nor values outside of the DOM element in question.</p>
<p>Also note that the <code>config</code> callback only runs after a rendering lifecycle is done. Therefore, you should not use <code>config</code> to modify controller and model values, if you expect these changes to render immediately. Changes to controller and model values in this fashion will only render on the next <code>m.render</code> or <code>m.module</code> call.</p>
<p>You can use this mechanism to attach custom event listeners to controller methods (for example, when integrating with third party libraries), but you are responsible for making sure the integration with Mithril&#39;s autoredrawing system is in place. See the <a href="integration.html">integration guide</a> for more information.</p>
<hr>
<p>The third argument for <code>config</code> allows you to map data to a virtual DOM element in a way that persists across redraws. This is useful when a <code>config</code> instantiates 3rd party classes and accesses the instance on redraws.</p>
<p>The example below shows a contrived redraw counter. In it, the count is stored in the context object and re-accessed on each redraw.</p>
<pre><code class="lang-javascript">function alertsRedrawCount(element, isInit, context) {
if (!isInit) context.count = 0
alert(++context.count)
}
m(&quot;div&quot;, {config: alertsRedrawCount})</code></pre>
<hr>
<p>You can use Mithril to create SVG documents (as long as you don&#39;t need to support browsers that don&#39;t support SVG natively).</p>
<p>Mithril automatically figures out the correct XML namespaces when it sees an SVG island in the virtual DOM tree.</p>
<pre><code class="lang-javascript">m(&quot;svg[height=&#39;200px&#39;][width=&#39;200px&#39;]&quot;, [
m(&quot;image[href=&#39;foo.jpg&#39;][height=&#39;200px&#39;][width=&#39;200px&#39;]&quot;)
])</code></pre>
<hr>
<h3 id="signature">Signature</h3>
<p><a href="how-to-read-signatures.html">How to read signatures</a></p>
<pre><code class="lang-clike">VirtualElement m(String selector [, Attributes attributes] [, Children children])
where:
VirtualElement :: Object { String tag, Attributes attributes, Children children }
Attributes :: Object&lt;any | void config(DOMElement element, Boolean isInitialized, Object context)&gt;
Children :: String text | VirtualElement virtualElement | SubtreeDirective directive | Array&lt;Children children&gt;
SubtreeDirective :: Object { String subtree }</code></pre>
<ul>
<li><p><strong>String selector</strong></p>
<p>This string should be a CSS rule that represents a DOM element.</p>
<p>Only tag, id, class and attribute selectors are supported.</p>
<p>If the tag selector is omitted, it defaults to <code>div</code>.</p>
<p>Note that if the same attribute is defined in the both <code>selector</code> and <code>attributes</code> parameters, the value in <code>attributes</code> is used.</p>
<p>For developer convenience, Mithril makes an exception for the <code>class</code> attribute: if there are classes defined in both parameters, they are concatenated as a space separated list. It does not, however, de-dupe classes if the same class is declared twice.</p>
<p><em>Examples:</em></p>
<p><code>&quot;div&quot;</code></p>
<p><code>&quot;#container&quot;</code></p>
<p><code>&quot;.active&quot;</code></p>
<p><code>&quot;[title=&#39;Application&#39;]&quot;</code></p>
<p><code>&quot;div#container.active[title=&#39;Application&#39;]&quot;</code></p>
<p><code>&quot;.active#container&quot;</code></p>
</li>
<li><p><strong>Attributes attributes</strong> (optional)</p>
<p>This key-value map should define a list of HTML attributes and their respective values.</p>
<p>You can use both HTML and Javascript attribute names. For example, both <code>class</code> and <code>className</code> are valid.</p>
<p>Values&#39; types should match the expected type for the respective attribute.</p>
<p>For example, the value for <code>className</code> should be a string.</p>
<p>When a attribute name expects different types for the value in HTML and Javascript, the Javascript type should be used. </p>
<p>For example, the value for the <code>onclick</code> attribute should be a function.</p>
<p>Similar, setting the value of attribute <code>readonly</code> to <code>false</code> is equivalent to removing the attribute in HTML.</p>
<p>It&#39;s also possible to set values to Javascript-only properties, such as <code>hash</code> in a <code>&lt;a&gt;</code> element.</p>
<p>Note that if the same attribute is defined in the both <code>selector</code> and <code>attributes</code> parameters, the value in <code>attributes</code> is used.</p>
<p>For developer convenience, Mithril makes an exception for the <code>class</code> attribute: if there are classes defined in both parameters, they are concatenated as a space separated list. It does not, however, de-dupe classes if the same class is declared twice.</p>
<p><em>Examples:</em></p>
<p><code>{ title: &quot;Application&quot; }</code></p>
<p><code>{ onclick: function(e) { /*do stuff*/ } }</code></p>
<p><code>{ style: {border: &quot;1px solid red&quot;} }</code></p>
</li>
<li><h4 id="the-config-attribute">The <code>config</code> attribute</h4>
<p><strong>void config(DOMElement element, Boolean isInitialized, Object context)</strong> (optional)</p>
<p>You can define a non-HTML-standard attribute called <code>config</code>. This special parameter allows you to call methods on the DOM element after it gets created.</p>
<p>This is useful, for example, if you declare a <code>canvas</code> element and want to use the Javascript API to draw:</p>
<pre><code class="lang-javascript">function draw(element, isInitialized) {
//don&#39;t redraw if we did once already
if (isInitialized) return;
var ctx = element.getContext(&quot;2d&quot;);
/* draws stuff */
}
var view = [
m(&quot;canvas&quot;, {config: draw})
]
//this creates the canvas element, and therefore, `isInitialized` is false
m.render(document.body, view);
//here, isInitialized is `true`
m.render(document.body, view);</code></pre>
<p>One common way of using <code>config</code> is in conjunction with <a href="mithril.route"><code>m.route</code></a>, which is an unobtrusive extension to links that allow Mithril&#39;s routing system to work transparently regardless of which routing mode is used.</p>
<pre><code class="lang-javascript">//this link can use any of Mithril&#39;s routing system modes
//(i.e. it can use either the hash, the querystring or the pathname as the router implementation)
//without needing to hard-code any syntax (`#` or `?`) in the `href` attribute.
m(&quot;a[href=&#39;/dashboard&#39;]&quot;, {config: m.route}, &quot;Dashboard&quot;);</code></pre>
<p>The <code>config</code> mechanism can also be used to put focus on form inputs, and call methods that would not be possible to execute via the regular attribute syntax.</p>
<p>It is only meant to be used to call methods on DOM elements that cannot be called otherwise.</p>
<p>It is NOT a &quot;free out-of-jail card&quot;. You should not use this method to modify element properties that could be modified via the <code>attributes</code> argument, nor values outside of the DOM element in question.</p>
<p>Also note that the <code>config</code> callback only runs after a rendering lifecycle is done. Therefore, you should not use <code>config</code> to modify controller and model values, if you expect these changes to render immediately. Changes to controller and model values in this fashion will only render on the next <code>m.render</code> or <code>m.module</code> call.</p>
<p>You can use this mechanism to attach custom event listeners to controller methods (for example, when integrating with third party libraries), but you are responsible for making sure the integration with Mithril&#39;s autoredrawing system is in place. See the <a href="integration.html">integration guide</a> for more information.</p>
<ul>
<li><strong>DOMElement element</strong></li>
</ul>
<p>The DOM element that corresponds to virtual element defined by the <code>m()</code> call.</p>
<ul>
<li><strong>Boolean isInitialized</strong></li>
</ul>
<p>Whether this is the first time we are running this function on this element. This flag is false the first time it runs on an element, and true on redraws that happen after the element has been created.</p>
<ul>
<li><strong>Object context</strong></li>
</ul>
<p>An object that retains its state across redraws. It can be used to store instances of 3rd party classes that need to be accessed more than one time throughout the lifecycle of a page.</p>
<p>The example below shows a contrived redraw counter. In it, the count is stored in the context object and re-accessed on each redraw.</p>
<pre><code class="lang-javascript">function alertsRedrawCount(element, isInit, context) {
if (!isInit) context.count = 0
alert(++context.count)
}
m(&quot;div&quot;, {config: alertsRedrawCount})</code></pre>
</li>
<li><p><strong>Children children</strong> (optional)</p>
<p>If this argument is a string, it will be rendered as a text node. To render a string as HTML, see <a href="mithril.trust"><code>m.trust</code></a></p>
<p>If it&#39;s a VirtualElement, it will be rendered as a DOM Element.</p>
<p>If it&#39;s a list, its contents will recursively be rendered as appropriate and appended as children of the element being created.</p>
<p>If it&#39;s a SubtreeDirective with the value &quot;retain&quot;, it will retain the existing DOM tree in place, if any. See <a href="mithril.render#subtree-directives">subtree directives</a> for more information.</p>
</li>
<li><p><strong>returns</strong> VirtualElement</p>
<p>The returned VirtualElement is a Javascript data structure that represents the DOM element to be rendered by <a href="mithril.render"><code>m.render</code></a></p>
</li>
</ul>
</div>
</div>
</div>
</section>
</main>
<footer>
<div class="container">
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
<br />&copy; 2014 Leo Horie
</div>
</footer>
<script src="lib/prism/prism.js"></script>
</body>
</html>

622
archive/v0.1.16/mithril.js Normal file
View file

@ -0,0 +1,622 @@
Mithril = m = new function app(window) {
var selectorCache = {}
var type = {}.toString
var parser = /(?:(^|#|\.)([^#\.\[\]]+))|(\[.+?\])/g, attrParser = /\[(.+?)(?:=("|'|)(.+?)\2)?\]/
function m() {
var args = arguments
var hasAttrs = type.call(args[1]) == "[object Object]" && !("tag" in args[1]) && !("subtree" in args[1])
var attrs = hasAttrs ? args[1] : {}
var classAttrName = "class" in attrs ? "class" : "className"
var cell = selectorCache[args[0]]
if (cell === undefined) {
selectorCache[args[0]] = cell = {tag: "div", attrs: {}}
var match, classes = []
while (match = parser.exec(args[0])) {
if (match[1] == "") cell.tag = match[2]
else if (match[1] == "#") cell.attrs.id = match[2]
else if (match[1] == ".") classes.push(match[2])
else if (match[3][0] == "[") {
var pair = attrParser.exec(match[3])
cell.attrs[pair[1]] = pair[3] || true
}
}
if (classes.length > 0) cell.attrs[classAttrName] = classes.join(" ")
}
cell = clone(cell)
cell.attrs = clone(cell.attrs)
cell.children = hasAttrs ? args[2] : args[1]
for (var attrName in attrs) {
if (attrName == classAttrName) cell.attrs[attrName] = (cell.attrs[attrName] || "") + " " + attrs[attrName]
else cell.attrs[attrName] = attrs[attrName]
}
return cell
}
function build(parentElement, parentTag, parentCache, parentIndex, data, cached, shouldReattach, index, editable, namespace, configs) {
if (data === null || data === undefined) data = ""
if (data.subtree === "retain") return
var cachedType = type.call(cached), dataType = type.call(data)
if (cachedType != dataType) {
if (cached !== null && cached !== undefined) {
if (parentCache && parentCache.nodes) {
var offset = index - parentIndex
clear(parentCache.nodes.slice(offset, offset + (dataType == "[object Array]" ? data : cached.nodes).length))
}
else clear(cached.nodes)
}
cached = new data.constructor
cached.nodes = []
}
if (dataType == "[object Array]") {
var nodes = [], intact = cached.length === data.length, subArrayCount = 0
var DELETION = 1, INSERTION = 2 , MOVE = 3
var existing = {}, shouldMaintainIdentities = false
for (var i = 0; i < cached.length; i++) {
if (cached[i] && cached[i].attrs && cached[i].attrs.key !== undefined) {
shouldMaintainIdentities = true
existing[cached[i].attrs.key] = {action: DELETION, index: i}
}
}
if (shouldMaintainIdentities) {
for (var i = 0; i < data.length; i++) {
if (data[i] && data[i].attrs && data[i].attrs.key !== undefined) {
var key = data[i].attrs.key
if (!existing[key]) existing[key] = {action: INSERTION, index: i}
else existing[key] = {action: MOVE, index: i, from: existing[key].index, element: parentElement.childNodes[existing[key].index]}
}
}
var actions = Object.keys(existing).map(function(key) {return existing[key]})
var changes = actions.sort(function(a, b) {return a.action - b.action || b.index - a.index})
var newCached = new Array(cached.length)
for (var i = 0, change; change = changes[i]; i++) {
if (change.action == DELETION) {
clear(cached[change.index].nodes)
newCached.splice(change.index, 1)
}
if (change.action == INSERTION) {
var dummy = window.document.createElement("div")
dummy.key = data[change.index].attrs.key.toString()
parentElement.insertBefore(dummy, parentElement.childNodes[change.index])
newCached.splice(change.index, 0, {attrs: {key: data[change.index].attrs.key}, nodes: [dummy]})
}
if (change.action == MOVE) {
if (parentElement.childNodes[change.index] !== change.element) {
parentElement.insertBefore(change.element, parentElement.childNodes[change.index])
}
newCached[change.index] = cached[change.from]
}
}
cached = newCached
cached.nodes = []
for (var i = 0, child; child = parentElement.childNodes[i]; i++) cached.nodes.push(child)
}
for (var i = 0, cacheCount = 0; i < data.length; i++) {
var item = build(parentElement, parentTag, cached, index, data[i], cached[cacheCount], shouldReattach, index + subArrayCount || subArrayCount, editable, namespace, configs)
if (item === undefined) continue
if (!item.nodes.intact) intact = false
subArrayCount += item instanceof Array ? item.length : 1
cached[cacheCount++] = item
}
if (!intact) {
for (var i = 0; i < data.length; i++) {
if (cached[i] !== undefined) nodes = nodes.concat(cached[i].nodes)
}
for (var i = nodes.length, node; node = cached.nodes[i]; i++) {
if (node.parentNode !== null && node.parentNode.childNodes.length != nodes.length) node.parentNode.removeChild(node)
}
for (var i = cached.nodes.length, node; node = nodes[i]; i++) {
if (node.parentNode === null) parentElement.appendChild(node)
}
if (data.length < cached.length) cached.length = data.length
cached.nodes = nodes
}
}
else if (dataType == "[object Object]") {
if (data.tag != cached.tag || Object.keys(data.attrs).join() != Object.keys(cached.attrs).join() || data.attrs.id != cached.attrs.id) clear(cached.nodes)
if (typeof data.tag != "string") return
var node, isNew = cached.nodes.length === 0
if (data.attrs.xmlns) namespace = data.attrs.xmlns
else if (data.tag === "svg") namespace = "http://www.w3.org/2000/svg"
if (isNew) {
node = namespace === undefined ? window.document.createElement(data.tag) : window.document.createElementNS(namespace, data.tag)
cached = {
tag: data.tag,
attrs: setAttributes(node, data.tag, data.attrs, {}, namespace),
children: data.children !== undefined ? build(node, data.tag, undefined, undefined, data.children, cached.children, true, 0, data.attrs.contenteditable ? node : editable, namespace, configs) : undefined,
nodes: [node]
}
parentElement.insertBefore(node, parentElement.childNodes[index] || null)
}
else {
node = cached.nodes[0]
setAttributes(node, data.tag, data.attrs, cached.attrs, namespace)
cached.children = build(node, data.tag, undefined, undefined, data.children, cached.children, false, 0, data.attrs.contenteditable ? node : editable, namespace, configs)
cached.nodes.intact = true
if (shouldReattach === true) parentElement.insertBefore(node, parentElement.childNodes[index] || null)
}
if (type.call(data.attrs["config"]) == "[object Function]") {
configs.push(data.attrs["config"].bind(window, node, !isNew, cached.configContext = cached.configContext || {}))
}
}
else {
var nodes
if (cached.nodes.length === 0) {
if (data.$trusted) {
nodes = injectHTML(parentElement, index, data)
}
else {
nodes = [window.document.createTextNode(data)]
parentElement.insertBefore(nodes[0], parentElement.childNodes[index] || null)
}
cached = "string number boolean".indexOf(typeof data) > -1 ? new data.constructor(data) : data
cached.nodes = nodes
}
else if (cached.valueOf() !== data.valueOf() || shouldReattach === true) {
nodes = cached.nodes
if (!editable || editable !== window.document.activeElement) {
if (data.$trusted) {
clear(nodes)
nodes = injectHTML(parentElement, index, data)
}
else {
if (parentTag === "textarea") parentElement.value = data
else if (editable) editable.innerHTML = data
else {
if (nodes[0].nodeType == 1 || nodes.length > 1) { //was a trusted string
clear(cached.nodes)
nodes = [window.document.createTextNode(data)]
}
parentElement.insertBefore(nodes[0], parentElement.childNodes[index] || null)
nodes[0].nodeValue = data
}
}
}
cached = new data.constructor(data)
cached.nodes = nodes
}
else cached.nodes.intact = true
}
return cached
}
function setAttributes(node, tag, dataAttrs, cachedAttrs, namespace) {
for (var attrName in dataAttrs) {
var dataAttr = dataAttrs[attrName]
var cachedAttr = cachedAttrs[attrName]
if (!(attrName in cachedAttrs) || (cachedAttr !== dataAttr) || node === window.document.activeElement) {
cachedAttrs[attrName] = dataAttr
if (attrName === "config") continue
else if (typeof dataAttr == "function" && attrName.indexOf("on") == 0) {
node[attrName] = autoredraw(dataAttr, node)
}
else if (attrName === "style" && typeof dataAttr == "object") {
for (var rule in dataAttr) {
if (cachedAttr === undefined || cachedAttr[rule] !== dataAttr[rule]) node.style[rule] = dataAttr[rule]
}
for (var rule in cachedAttr) {
if (!(rule in dataAttr)) node.style[rule] = ""
}
}
else if (namespace !== undefined) {
if (attrName === "href") node.setAttributeNS("http://www.w3.org/1999/xlink", "href", dataAttr)
else if (attrName === "className") node.setAttribute("class", dataAttr)
else node.setAttribute(attrName, dataAttr)
}
else if (attrName === "value" && tag === "input") {
if (node.value !== dataAttr) node.value = dataAttr
}
else if (attrName in node && !(attrName == "list" || attrName == "style")) {
node[attrName] = dataAttr
}
else node.setAttribute(attrName, dataAttr)
}
}
return cachedAttrs
}
function clear(nodes) {
for (var i = nodes.length - 1; i > -1; i--) if (nodes[i] && nodes[i].parentNode) nodes[i].parentNode.removeChild(nodes[i])
nodes.length = 0
}
function injectHTML(parentElement, index, data) {
var nextSibling = parentElement.childNodes[index]
if (nextSibling) {
var isElement = nextSibling.nodeType != 1
var placeholder = window.document.createElement("span")
if (isElement) {
parentElement.insertBefore(placeholder, nextSibling)
placeholder.insertAdjacentHTML("beforebegin", data)
parentElement.removeChild(placeholder)
}
else nextSibling.insertAdjacentHTML("beforebegin", data)
}
else parentElement.insertAdjacentHTML("beforeend", data)
var nodes = []
while (parentElement.childNodes[index] !== nextSibling) {
nodes.push(parentElement.childNodes[index])
index++
}
return nodes
}
function clone(object) {
var result = {}
for (var prop in object) result[prop] = object[prop]
return result
}
function autoredraw(callback, object) {
return function(e) {
e = e || event
m.startComputation()
try {return callback.call(object, e)}
finally {m.endComputation()}
}
}
var html
var documentNode = {
insertAdjacentHTML: function(_, data) {
window.document.write(data)
window.document.close()
},
appendChild: function(node) {
if (html === undefined) html = window.document.createElement("html")
if (node.nodeName == "HTML") html = node
else html.appendChild(node)
if (window.document.documentElement !== html) {
window.document.replaceChild(html, window.document.documentElement)
}
},
insertBefore: function(node) {
this.appendChild(node)
},
childNodes: []
}
var nodeCache = [], cellCache = {}
m.render = function(root, cell) {
var configs = []
if (!root) throw new Error("Please ensure the DOM element exists before rendering a template into it.")
var index = nodeCache.indexOf(root)
var id = index < 0 ? nodeCache.push(root) - 1 : index
var node = root == window.document || root == window.document.documentElement ? documentNode : root
cellCache[id] = build(node, null, undefined, undefined, cell, cellCache[id], false, 0, null, undefined, configs)
for (var i = 0; i < configs.length; i++) configs[i]()
}
m.trust = function(value) {
value = new String(value)
value.$trusted = true
return value
}
var roots = [], modules = [], controllers = [], now = 0, lastRedraw = 0, lastRedrawId = 0, computePostRedrawHook = null
m.module = function(root, module) {
var index = roots.indexOf(root)
if (index < 0) index = roots.length
var isPrevented = false
if (controllers[index] && typeof controllers[index].onunload == "function") {
var event = {
preventDefault: function() {isPrevented = true}
}
controllers[index].onunload(event)
}
if (!isPrevented) {
m.startComputation()
roots[index] = root
modules[index] = module
controllers[index] = new module.controller
m.endComputation()
}
}
m.redraw = function() {
now = window.performance && window.performance.now ? window.performance.now() : new window.Date().getTime()
if (now - lastRedraw > 16) redraw()
else {
var cancel = window.cancelAnimationFrame || window.clearTimeout
var defer = window.requestAnimationFrame || window.setTimeout
cancel(lastRedrawId)
lastRedrawId = defer(redraw, 0)
}
}
function redraw() {
for (var i = 0; i < roots.length; i++) {
if (controllers[i]) m.render(roots[i], modules[i].view(controllers[i]))
}
if (computePostRedrawHook) {
computePostRedrawHook()
computePostRedrawHook = null
}
lastRedraw = now
}
var pendingRequests = 0
m.startComputation = function() {pendingRequests++}
m.endComputation = function() {
pendingRequests = Math.max(pendingRequests - 1, 0)
if (pendingRequests == 0) m.redraw()
}
m.withAttr = function(prop, withAttrCallback) {
return function(e) {
e = e || event
withAttrCallback(prop in e.currentTarget ? e.currentTarget[prop] : e.currentTarget.getAttribute(prop))
}
}
//routing
var modes = {pathname: "", hash: "#", search: "?"}
var redirect = function() {}, routeParams = {}, currentRoute
m.route = function() {
if (arguments.length === 0) return currentRoute
else if (arguments.length === 3 && typeof arguments[1] == "string") {
var root = arguments[0], defaultRoute = arguments[1], router = arguments[2]
redirect = function(source) {
var path = currentRoute = normalizeRoute(source)
if (!routeByValue(root, router, path)) {
m.route(defaultRoute, true)
}
}
var listener = m.route.mode == "hash" ? "onhashchange" : "onpopstate"
window[listener] = function() {
if (currentRoute != normalizeRoute(window.location[m.route.mode])) {
redirect(window.location[m.route.mode])
}
}
computePostRedrawHook = setScroll
window[listener]()
currentRoute = normalizeRoute(window.location[m.route.mode])
}
else if (arguments[0].addEventListener) {
var element = arguments[0]
var isInitialized = arguments[1]
if (element.href.indexOf(modes[m.route.mode]) < 0) {
element.href = window.location.pathname + modes[m.route.mode] + element.pathname
}
if (!isInitialized) {
element.removeEventListener("click", routeUnobtrusive)
element.addEventListener("click", routeUnobtrusive)
}
}
else if (typeof arguments[0] == "string") {
currentRoute = arguments[0]
var querystring = typeof arguments[1] == "object" ? buildQueryString(arguments[1]) : null
if (querystring) currentRoute += (currentRoute.indexOf("?") === -1 ? "?" : "&") + querystring
var shouldReplaceHistoryEntry = (arguments.length == 3 ? arguments[2] : arguments[1]) === true
if (window.history.pushState) {
computePostRedrawHook = function() {
window.history[shouldReplaceHistoryEntry ? "replaceState" : "pushState"](null, window.document.title, modes[m.route.mode] + currentRoute)
setScroll()
}
redirect(modes[m.route.mode] + currentRoute)
}
else window.location[m.route.mode] = currentRoute
}
}
m.route.param = function(key) {return routeParams[key]}
m.route.mode = "search"
function normalizeRoute(route) {return route.slice(modes[m.route.mode].length)}
function routeByValue(root, router, path) {
routeParams = {}
var queryStart = path.indexOf("?")
if (queryStart !== -1) {
routeParams = parseQueryString(path.substr(queryStart + 1, path.length))
path = path.substr(0, queryStart)
}
for (var route in router) {
if (route == path) return !void m.module(root, router[route])
var matcher = new RegExp("^" + route.replace(/:[^\/]+?\.{3}/g, "(.*?)").replace(/:[^\/]+/g, "([^\\/]+)") + "\/?$")
if (matcher.test(path)) {
return !void path.replace(matcher, function() {
var keys = route.match(/:[^\/]+/g) || []
var values = [].slice.call(arguments, 1, -2)
for (var i = 0; i < keys.length; i++) routeParams[keys[i].replace(/:|\./g, "")] = decodeSpace(values[i])
m.module(root, router[route])
})
}
}
}
function routeUnobtrusive(e) {
e = e || event
if (e.ctrlKey || e.metaKey || e.which == 2) return
e.preventDefault()
m.route(e.currentTarget[m.route.mode].slice(modes[m.route.mode].length))
}
function setScroll() {
if (m.route.mode != "hash" && window.location.hash) window.location.hash = window.location.hash
else window.scrollTo(0, 0)
}
function buildQueryString(object, prefix) {
var str = []
for(var prop in object) {
var key = prefix ? prefix + "[" + prop + "]" : prop, value = object[prop]
str.push(typeof value == "object" ? buildQueryString(value, key) : encodeURIComponent(key) + "=" + encodeURIComponent(value))
}
return str.join("&")
}
function parseQueryString(str) {
var pairs = str.split("&"), params = {}
for (var i = 0; i < pairs.length; i++) {
var pair = pairs[i].split("=")
params[decodeSpace(pair[0])] = pair[1] ? decodeSpace(pair[1]) : (pair.length === 1 ? true : "")
}
return params
}
function decodeSpace(string) {
return decodeURIComponent(string.replace(/\+/g, " "))
}
//model
m.prop = function(store) {
var prop = function() {
if (arguments.length) store = arguments[0]
return store
}
prop.toJSON = function() {
return store
}
return prop
}
var none = {}
m.deferred = function() {
var resolvers = [], rejecters = [], resolved = none, rejected = none, promise = m.prop()
var object = {
resolve: function(value) {
if (resolved === none) promise(resolved = value)
for (var i = 0; i < resolvers.length; i++) resolvers[i](value)
resolvers.length = rejecters.length = 0
},
reject: function(value) {
if (rejected === none) rejected = value
for (var i = 0; i < rejecters.length; i++) rejecters[i](value)
resolvers.length = rejecters.length = 0
},
promise: promise
}
object.promise.resolvers = resolvers
object.promise.then = function(success, error) {
var next = m.deferred()
if (!success) success = identity
if (!error) error = identity
function callback(method, callback) {
return function(value) {
try {
var result = callback(value)
if (result && typeof result.then == "function") result.then(next[method], error)
else next[method](result !== undefined ? result : value)
}
catch (e) {
if (e instanceof Error && e.constructor !== Error) throw e
else next.reject(e)
}
}
}
if (resolved !== none) callback("resolve", success)(resolved)
else if (rejected !== none) callback("reject", error)(rejected)
else {
resolvers.push(callback("resolve", success))
rejecters.push(callback("reject", error))
}
return next.promise
}
return object
}
m.sync = function(args) {
var method = "resolve"
function synchronizer(pos, resolved) {
return function(value) {
results[pos] = value
if (!resolved) method = "reject"
if (--outstanding == 0) {
deferred.promise(results)
deferred[method](results)
}
return value
}
}
var deferred = m.deferred()
var outstanding = args.length
var results = new Array(outstanding)
for (var i = 0; i < args.length; i++) {
args[i].then(synchronizer(i, true), synchronizer(i, false))
}
return deferred.promise
}
function identity(value) {return value}
function ajax(options) {
var xhr = new window.XMLHttpRequest
xhr.open(options.method, options.url, true, options.user, options.password)
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) options.onload({type: "load", target: xhr})
else options.onerror({type: "error", target: xhr})
}
}
if (typeof options.config == "function") {
var maybeXhr = options.config(xhr, options)
if (maybeXhr !== undefined) xhr = maybeXhr
}
xhr.send(options.data)
return xhr
}
function bindData(xhrOptions, data, serialize) {
if (data && Object.keys(data).length > 0) {
if (xhrOptions.method == "GET") {
xhrOptions.url = xhrOptions.url + (xhrOptions.url.indexOf("?") < 0 ? "?" : "&") + buildQueryString(data)
}
else xhrOptions.data = serialize(data)
}
return xhrOptions
}
function parameterizeUrl(url, data) {
var tokens = url.match(/:[a-z]\w+/gi)
if (tokens && data) {
for (var i = 0; i < tokens.length; i++) {
var key = tokens[i].slice(1)
url = url.replace(tokens[i], data[key])
delete data[key]
}
}
return url
}
m.request = function(xhrOptions) {
if (xhrOptions.background !== true) m.startComputation()
var deferred = m.deferred()
var serialize = xhrOptions.serialize || JSON.stringify
var deserialize = xhrOptions.deserialize || JSON.parse
var extract = xhrOptions.extract || function(xhr) {
return xhr.responseText.length === 0 && deserialize === JSON.parse ? null : xhr.responseText
}
xhrOptions.url = parameterizeUrl(xhrOptions.url, xhrOptions.data)
xhrOptions = bindData(xhrOptions, xhrOptions.data, serialize)
xhrOptions.onload = xhrOptions.onerror = function(e) {
try {
e = e || event
var unwrap = (e.type == "load" ? xhrOptions.unwrapSuccess : xhrOptions.unwrapError) || identity
var response = unwrap(deserialize(extract(e.target, xhrOptions)))
if (e.type == "load") {
if (response instanceof Array && xhrOptions.type) {
for (var i = 0; i < response.length; i++) response[i] = new xhrOptions.type(response[i])
}
else if (xhrOptions.type) response = new xhrOptions.type(response)
}
deferred[e.type == "load" ? "resolve" : "reject"](response)
}
catch (e) {
if (e instanceof SyntaxError) throw new SyntaxError("Could not parse HTTP response. See http://lhorie.github.io/mithril/mithril.request.html#using-variable-data-formats")
else if (e instanceof Error && e.constructor !== Error) throw e
else deferred.reject(e)
}
if (xhrOptions.background !== true) m.endComputation()
}
ajax(xhrOptions)
return deferred.promise
}
//testing API
m.deps = function(mock) {return window = mock}
//for internal testing only, do not use `m.deps.factory`
m.deps.factory = app
return m
}(typeof window != "undefined" ? window : {})
if (typeof module != "undefined" && module !== null) module.exports = m
if (typeof define == "function" && define.amd) define(function() {return m})
;;;

8
archive/v0.1.16/mithril.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

View file

@ -0,0 +1,205 @@
<!doctype html>
<html>
<head>
<title>Mithril</title>
<link href="lib/prism/prism.css" rel="stylesheet" />
<link href="style.css" rel="stylesheet" />
</head>
<body>
<header>
<nav class="container">
<a href="index.html" class="logo"><span>&#9675;</span> Mithril</a>
<a href="getting-started.html">Guide</a>
<a href="mithril.html">API</a>
<a href="community.html">Community</a>
<a href="mithril.min.zip">Download</a>
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
</nav>
</header>
<main>
<section class="content">
<div class="container">
<div class="row">
<div class="col(3,3,12)">
<h2 id="api">API (v0.1.16)</h2>
<h3 id="core">Core</h3>
<ul>
<li><a href="mithril.html">m</a></li>
<li><a href="mithril.prop.html">m.prop</a></li>
<li><a href="mithril.withAttr.html">m.withAttr</a></li>
<li><a href="mithril.module.html">m.module</a></li>
<li><a href="mithril.trust.html">m.trust</a></li>
<li><a href="mithril.render.html">m.render</a></li>
<li><a href="mithril.redraw.html">m.redraw</a></li>
</ul>
<h3 id="routing">Routing</h3>
<ul>
<li><a href="mithril.route.html">m.route</a>
<ul>
<li><a href="mithril.route.html#defining-routes">m.route(rootElement, defaultRoute, routes)</a></li>
<li><a href="mithril.route.html#redirecting">m.route(path, params)</a></li>
<li><a href="mithril.route.html#reading-current-route">m.route()</a></li>
<li><a href="mithril.route.html#mode-abstraction">m.route(element)</a></li>
<li><a href="mithril.route.html#mode">m.route.mode</a></li>
<li><a href="mithril.route.html#param">m.route.param</a></li>
</ul>
</li>
</ul>
<h3 id="data">Data</h3>
<ul>
<li><a href="mithril.request.html">m.request</a></li>
<li><a href="mithril.deferred.html">m.deferred</a></li>
<li><a href="mithril.sync.html">m.sync</a></li>
<li><a href="mithril.computation.html">m.startComputation / m.endComputation</a></li>
</ul>
<h2 id="archive">History</h2>
<ul>
<li><a href="roadmap.html">Roadmap</a></li>
<li><a href="change-log.html">Change log</a></li>
</ul>
</div>
<div class="col(9,9,12)">
<h2 id="m-module">m.module</h2>
<p>A module is an Object with two keys: <code>controller</code> and <code>view</code>. Each of those should point to a Javascript class constructor function.</p>
<p>&#39;m.module&#39; activates a module by instantiating its controller, then instantiating its view and rendering it into a root DOM element.</p>
<p>Conceptually, the easiest way to think of a module is as a logical namespace with which to organize applications. For example, an app might have a dashboard module, a userEditForm module, an autocompleter module, a date formatting module, etc</p>
<p>In the context of single page applications (SPA), a module can often be thought of as the code for a single &quot;page&quot;, i.e. a visual state that is bookmarkable. Module can, however, also represent <em>parts</em> of pages.</p>
<p>Note that a module might have external dependencies and that the dependencies aren&#39;t considered part of the module.</p>
<p>In more complex applications, modules can be nested in a <a href="http://en.wikipedia.org/wiki/Hierarchical_model%E2%80%93view%E2%80%93controller">hierarchical MVC</a> pattern. Nested reusable modules that have views are called <strong>Components</strong>.</p>
<p>Modules and namespaces are often used interchangeably, but namespaces that do not implement the module interface (that is, objects that do not have a property called <code>controller</code> and a property called <code>view</code>) cannot be activated with <code>m.module</code>. For example, a namespace for date formatting utilities could be labeled a &quot;module&quot; (in the generic sense of the word) but it would not contain a view class, and therefore attempting to initialize it via <code>m.module</code> would result in undefined behavior.</p>
<hr>
<h3 id="usage">Usage</h3>
<p>You can make anonymous modules out of existing classes</p>
<pre><code class="lang-javascript">//controller class
var dashboardController = function() {
this.greeting = &quot;Hello&quot;;
};
//view class
var dashboardView = function(ctrl) {
return m(&quot;h1&quot;, ctrl.greeting);
};
//initialize an anonymous module
m.module(document.body, {controller: dashboardController, view: dashboardView});</code></pre>
<p>Typically, however, modules and namespaces are used interchangeably.</p>
<pre><code class="lang-javascript">//`dashboard` is both a namespace and a module
var dashboard = {}
//controller class
dashboard.controller = function() {
this.greeting = &quot;Hello&quot;;
};
//view class
dashboard.view = function(ctrl) {
return m(&quot;h1&quot;, ctrl.greeting);
};
//initialize it
m.module(document.body, dashboard);</code></pre>
<p>The example below shows a component module called <code>user</code> being included in a parent module <code>dashboard</code>.</p>
<pre><code class="lang-javascript">//this is a sample module
var dashboard = {
controller: function() {
this.greeting = &quot;Hello&quot;;
this.user = new user.controller();
},
view: function(controller) {
return [
m(&quot;h1&quot;, controller.greeting),
user.view(controller.user)
];
}
};
//this module is being included as a component
var user = {
//model
User: function(name) {
this.name = name;
},
//controller
controller: function() {
this.user = new user.User(&quot;John Doe&quot;);
},
//view
view: function(controller) {
return m(&quot;div&quot;, controller.user.name);
}
};
//activate the dashboard module
m.module(document.body, dashboard);</code></pre>
<p>yields:</p>
<pre><code class="lang-markup">&lt;body&gt;
&lt;h1&gt;Hello&lt;/h1&gt;
&lt;div&gt;John Doe&lt;/div&gt;
&lt;/body&gt;</code></pre>
<h3 id="unloading-modules">Unloading modules</h3>
<p>If a module&#39;s controller implements an instance method called <code>onunload</code>, this method will be called when a new <code>m.module</code> call updates the root DOM element tied to the module in question.</p>
<pre><code class="lang-javascript">var module1 = {};
module1.controller = function() {
this.onunload = function() {
console.log(&quot;unloading module 1&quot;);
};
};
module1.view = function() {};
m.module(document, module1);
var module2 = {};
module2.controller = function() {};
module1.view = function() {};
m.module(document, module2); // logs &quot;unloading module 1&quot;</code></pre>
<p>This mechanism is useful to clear timers and unsubscribe event handlers. If you have a hierarchy of components, you can recursively call <code>onunload</code> on all the components in the tree or use a <a href="http://microjs.com/#pubsub">pubsub</a> library to unload specific components on demand.</p>
<p>You can also use this event to prevent a module from being unloaded (e.g. to alert a user to save their changes before navigating away from a page)</p>
<pre><code class="lang-javascript">var module1 = {}
module1.controller = function() {
this.onunload = function(e) {
if (!confirm(&quot;are you sure you want to leave this page?&quot;)) e.preventDefault()
}
}</code></pre>
<hr>
<h3 id="signature">Signature</h3>
<p><a href="how-to-read-signatures.html">How to read signatures</a></p>
<pre><code class="lang-clike">void module(DOMElement rootElement, Module module)
where:
Module :: Object { Controller, void view(Object controllerInstance) }
Controller :: void controller() | void controller() { prototype: void unload(UnloadEvent e) }
UnloadEvent :: Object {void preventDefault()}</code></pre>
<ul>
<li><p><strong>DOMElement rootElement</strong></p>
<p>A DOM element which will contain the view&#39;s template.</p>
</li>
<li><p><strong>Module module</strong></p>
<p>A module is supposed to be an Object with two keys: <code>controller</code> and <code>view</code>. Each of those should point to a Javascript class constructor function</p>
<p>The controller class is instantiated immediately upon calling <code>m.module</code>.</p>
<p>Once the controller code finishes executing (and this may include waiting for AJAX requests to complete), the view class is instantiated, and the instance of the controller is passed as an argument to the view&#39;s constructor.</p>
<p>Note that controllers can manually instantiate child controllers (since they are simply Javascript constructors), and likewise, views can instantiate child views and manually pass the child controller instances down the the child view constructors.</p>
<p>This &quot;<a href="https://en.wikipedia.org/wiki/Turtles_all_the_way_down">turtles all the way down</a>&quot; approach is the heart of Mithril&#39;s component system.</p>
<p>Components are nothing more than decoupled classes that can be dynamically brought together as required. This permits the swapping of implementations at a routing level (for example, if implementing widgetized versions of existing components), and class dependency hierarchies can be structurally organized to provide uniform interfaces (for unit tests, for example).</p>
</li>
</ul>
</div>
</div>
</div>
</section>
</main>
<footer>
<div class="container">
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
<br />&copy; 2014 Leo Horie
</div>
</footer>
<script src="lib/prism/prism.js"></script>
</body>
</html>

View file

@ -0,0 +1,147 @@
<!doctype html>
<html>
<head>
<title>Mithril</title>
<link href="lib/prism/prism.css" rel="stylesheet" />
<link href="style.css" rel="stylesheet" />
</head>
<body>
<header>
<nav class="container">
<a href="index.html" class="logo"><span>&#9675;</span> Mithril</a>
<a href="getting-started.html">Guide</a>
<a href="mithril.html">API</a>
<a href="community.html">Community</a>
<a href="mithril.min.zip">Download</a>
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
</nav>
</header>
<main>
<section class="content">
<div class="container">
<div class="row">
<div class="col(3,3,12)">
<h2 id="api">API (v0.1.16)</h2>
<h3 id="core">Core</h3>
<ul>
<li><a href="mithril.html">m</a></li>
<li><a href="mithril.prop.html">m.prop</a></li>
<li><a href="mithril.withAttr.html">m.withAttr</a></li>
<li><a href="mithril.module.html">m.module</a></li>
<li><a href="mithril.trust.html">m.trust</a></li>
<li><a href="mithril.render.html">m.render</a></li>
<li><a href="mithril.redraw.html">m.redraw</a></li>
</ul>
<h3 id="routing">Routing</h3>
<ul>
<li><a href="mithril.route.html">m.route</a>
<ul>
<li><a href="mithril.route.html#defining-routes">m.route(rootElement, defaultRoute, routes)</a></li>
<li><a href="mithril.route.html#redirecting">m.route(path, params)</a></li>
<li><a href="mithril.route.html#reading-current-route">m.route()</a></li>
<li><a href="mithril.route.html#mode-abstraction">m.route(element)</a></li>
<li><a href="mithril.route.html#mode">m.route.mode</a></li>
<li><a href="mithril.route.html#param">m.route.param</a></li>
</ul>
</li>
</ul>
<h3 id="data">Data</h3>
<ul>
<li><a href="mithril.request.html">m.request</a></li>
<li><a href="mithril.deferred.html">m.deferred</a></li>
<li><a href="mithril.sync.html">m.sync</a></li>
<li><a href="mithril.computation.html">m.startComputation / m.endComputation</a></li>
</ul>
<h2 id="archive">History</h2>
<ul>
<li><a href="roadmap.html">Roadmap</a></li>
<li><a href="change-log.html">Change log</a></li>
</ul>
</div>
<div class="col(9,9,12)">
<h2 id="m-prop">m.prop</h2>
<p>This is a getter-setter factory utility. It returns a function that stores information.</p>
<hr>
<h3 id="usage">Usage</h3>
<pre><code class="lang-javascript">//define a getter-setter with initial value `John`
var name = m.prop(&quot;John&quot;);
//read the value
var a = name(); //a == &quot;John&quot;
//set the value to `Mary`
name(&quot;Mary&quot;); //Mary
//read the value
var b = name(); //b == &quot;Mary&quot;</code></pre>
<p>It can be used in conjunction with <a href="mithril.withattr"><code>m.withAttr</code></a> to implement data binding in the view-to-model direction and to provide uniform data access for model entity properties.</p>
<pre><code class="lang-javascript">//a contrived example of bi-directional data binding
var user = {
model: function(name) {
this.name = m.prop(name);
},
controller: function() {
this.user = new user.model(&quot;John Doe&quot;);
},
view: function(controller) {
m.render(&quot;body&quot;, [
m(&quot;input&quot;, {onchange: m.withAttr(&quot;value&quot;, controller.user.name), value: controller.user.name()})
]);
}
};</code></pre>
<p>In the example above, the usage of <code>m.prop</code> allows the developer to change the implementation of the user name getter/setter without the need for code changes in the controller and view.</p>
<p><code>m.prop</code> can also be used in conjunction with <a href="mithril.request"><code>m.request</code></a> and <a href="mithril.deferred"><code>m.deferred</code></a> to bind data on completion of an asynchronous operation.</p>
<pre><code class="lang-javascript">var users = m.prop([]);
var error = m.prop(&quot;&quot;);
m.request({method: &quot;GET&quot;, url: &quot;/users&quot;})
.then(users, error); //on success, `users` will be populated, otherwise `error` will be populated
//assuming the response contains the following data: `[{name: &quot;John&quot;}, {name: &quot;Mary&quot;}]`
//then when resolved (e.g. in a view), the `users` getter-setter will contain a list of User instances
//i.e. users()[0].name() == &quot;John&quot;</code></pre>
<hr>
<h3 id="serializing-getter-setters">Serializing getter-setters</h3>
<p>Getter-setters are JSON-serializable:</p>
<pre><code class="lang-javascript">var data = {foo: m.prop(&quot;bar&quot;)};
JSON.stringify(data); // &#39;{&quot;foo&quot;: &quot;bar&quot;}&#39;</code></pre>
<p>This allows getter-setters to be passed directly as parameters to <a href="mithril.request.html"><code>m.request</code></a>, for example.</p>
<hr>
<h3 id="signature">Signature</h3>
<p><a href="how-to-read-signatures.html">How to read signatures</a></p>
<pre><code class="lang-clike">GetterSetter prop([any initialValue])
where:
GetterSetter :: any getterSetter([any value])</code></pre>
<ul>
<li><p><strong>any initialValue</strong> (optional)</p>
<p>An initialization value. If not provided, the value of the getter-setter&#39;s internal store defaults to <code>undefined</code>.</p>
</li>
<li><p><strong>returns any getterSetter([any value])</strong></p>
<p>A getter-setter method.</p>
<ul>
<li><p><strong>any value</strong> (optional)</p>
<p>If provided, it updates the getter-setter&#39;s internal store to the provided value.</p>
<p>If not provided, return the current internally stored value.</p>
</li>
<li><p><strong>returns any value</strong></p>
<p>This method always returns the value of the internal store, regardless of whether it was updated or not.</p>
</li>
</ul>
</li>
</ul>
</div>
</div>
</div>
</section>
</main>
<footer>
<div class="container">
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
<br />&copy; 2014 Leo Horie
</div>
</footer>
<script src="lib/prism/prism.js"></script>
</body>
</html>

View file

@ -0,0 +1,90 @@
<!doctype html>
<html>
<head>
<title>Mithril</title>
<link href="lib/prism/prism.css" rel="stylesheet" />
<link href="style.css" rel="stylesheet" />
</head>
<body>
<header>
<nav class="container">
<a href="index.html" class="logo"><span>&#9675;</span> Mithril</a>
<a href="getting-started.html">Guide</a>
<a href="mithril.html">API</a>
<a href="community.html">Community</a>
<a href="mithril.min.zip">Download</a>
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
</nav>
</header>
<main>
<section class="content">
<div class="container">
<div class="row">
<div class="col(3,3,12)">
<h2 id="api">API (v0.1.16)</h2>
<h3 id="core">Core</h3>
<ul>
<li><a href="mithril.html">m</a></li>
<li><a href="mithril.prop.html">m.prop</a></li>
<li><a href="mithril.withAttr.html">m.withAttr</a></li>
<li><a href="mithril.module.html">m.module</a></li>
<li><a href="mithril.trust.html">m.trust</a></li>
<li><a href="mithril.render.html">m.render</a></li>
<li><a href="mithril.redraw.html">m.redraw</a></li>
</ul>
<h3 id="routing">Routing</h3>
<ul>
<li><a href="mithril.route.html">m.route</a>
<ul>
<li><a href="mithril.route.html#defining-routes">m.route(rootElement, defaultRoute, routes)</a></li>
<li><a href="mithril.route.html#redirecting">m.route(path, params)</a></li>
<li><a href="mithril.route.html#reading-current-route">m.route()</a></li>
<li><a href="mithril.route.html#mode-abstraction">m.route(element)</a></li>
<li><a href="mithril.route.html#mode">m.route.mode</a></li>
<li><a href="mithril.route.html#param">m.route.param</a></li>
</ul>
</li>
</ul>
<h3 id="data">Data</h3>
<ul>
<li><a href="mithril.request.html">m.request</a></li>
<li><a href="mithril.deferred.html">m.deferred</a></li>
<li><a href="mithril.sync.html">m.sync</a></li>
<li><a href="mithril.computation.html">m.startComputation / m.endComputation</a></li>
</ul>
<h2 id="archive">History</h2>
<ul>
<li><a href="roadmap.html">Roadmap</a></li>
<li><a href="change-log.html">Change log</a></li>
</ul>
</div>
<div class="col(9,9,12)">
<h2 id="m-redraw">m.redraw</h2>
<p>Redraws the view for the currently active module. Use <a href="mithril.module"><code>m.module()</code></a> to activate a module.</p>
<p>This method is called internally by Mithril&#39;s auto-redrawing system. Usually you don&#39;t need to call it manually unless you are doing recurring asynchronous operations (i.e. using <code>setInterval</code>) or if you want to decouple slow running background requests from the rendering context (see the <code>background</code> option in <a href="mithril.request.html"><code>m.request</code></a>.</p>
<p>By default, if you&#39;re using either <a href="mithril.route.html"><code>m.route</code></a> or <a href="mithril.module.html"><code>m.module</code></a>, <code>m.redraw()</code> is called automatically by Mithril&#39;s auto-redrawing system once the controller finishes executing.</p>
<p><code>m.redraw</code> is also called automatically on event handlers defined in virtual elements.</p>
<p>Note that calling this method will not do anything if a module was not activated via either <a href="mithril.module"><code>m.module()</code></a> or <a href="mithril.route"><code>m.route()</code></a>. This means that <code>m.redraw</code> doesn&#39;t do anything when instantiating controllers and rendering views via <code>m.render</code> manually.</p>
<p>If there are pending <a href="mithril.request.html"><code>m.request</code></a> calls in either a controller constructor or event handler, the auto-redrawing system waits for all the AJAX requests to complete before calling <code>m.redraw</code>.</p>
<p>This method may also be called manually from within a controller if more granular updates to the view are needed, however doing so is generally not recommended, as it may degrade performance. Model classes should never call this method. </p>
<p>If you are developing an asynchronous model-level service and finding that Mithril is not redrawing the view after your code runs, you should use <a href="mithril.computation.html"><code>m.startComputation</code> and <code>m.endComputation</code></a> to integrate with Mithril&#39;s auto-redrawing system instead.</p>
<hr>
<h3 id="signature">Signature</h3>
<p><a href="how-to-read-signatures.html">How to read signatures</a></p>
<pre><code class="lang-clike">void redraw()</code></pre>
</div>
</div>
</div>
</section>
</main>
<footer>
<div class="container">
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
<br />&copy; 2014 Leo Horie
</div>
</footer>
<script src="lib/prism/prism.js"></script>
</body>
</html>

View file

@ -0,0 +1,158 @@
<!doctype html>
<html>
<head>
<title>Mithril</title>
<link href="lib/prism/prism.css" rel="stylesheet" />
<link href="style.css" rel="stylesheet" />
</head>
<body>
<header>
<nav class="container">
<a href="index.html" class="logo"><span>&#9675;</span> Mithril</a>
<a href="getting-started.html">Guide</a>
<a href="mithril.html">API</a>
<a href="community.html">Community</a>
<a href="mithril.min.zip">Download</a>
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
</nav>
</header>
<main>
<section class="content">
<div class="container">
<div class="row">
<div class="col(3,3,12)">
<h2 id="api">API (v0.1.16)</h2>
<h3 id="core">Core</h3>
<ul>
<li><a href="mithril.html">m</a></li>
<li><a href="mithril.prop.html">m.prop</a></li>
<li><a href="mithril.withAttr.html">m.withAttr</a></li>
<li><a href="mithril.module.html">m.module</a></li>
<li><a href="mithril.trust.html">m.trust</a></li>
<li><a href="mithril.render.html">m.render</a></li>
<li><a href="mithril.redraw.html">m.redraw</a></li>
</ul>
<h3 id="routing">Routing</h3>
<ul>
<li><a href="mithril.route.html">m.route</a>
<ul>
<li><a href="mithril.route.html#defining-routes">m.route(rootElement, defaultRoute, routes)</a></li>
<li><a href="mithril.route.html#redirecting">m.route(path, params)</a></li>
<li><a href="mithril.route.html#reading-current-route">m.route()</a></li>
<li><a href="mithril.route.html#mode-abstraction">m.route(element)</a></li>
<li><a href="mithril.route.html#mode">m.route.mode</a></li>
<li><a href="mithril.route.html#param">m.route.param</a></li>
</ul>
</li>
</ul>
<h3 id="data">Data</h3>
<ul>
<li><a href="mithril.request.html">m.request</a></li>
<li><a href="mithril.deferred.html">m.deferred</a></li>
<li><a href="mithril.sync.html">m.sync</a></li>
<li><a href="mithril.computation.html">m.startComputation / m.endComputation</a></li>
</ul>
<h2 id="archive">History</h2>
<ul>
<li><a href="roadmap.html">Roadmap</a></li>
<li><a href="change-log.html">Change log</a></li>
</ul>
</div>
<div class="col(9,9,12)">
<h2 id="m-render">m.render</h2>
<p>This method generates a DOM tree inside of a given HTML element.</p>
<p>If the method is run more than once with the same root element, it diffs the new tree against the existing one and intelligently modifies only the portions that have changed.</p>
<p>Note that, unlike many templating engines, this &quot;smart diff&quot; feature does not affect things like cursor placement in inputs and focus, and is therefore safe to call during user interactions.</p>
<hr>
<h3 id="usage">Usage</h3>
<p>Assuming a document has an empty <code>&lt;body&gt;</code> element, the code below:</p>
<pre><code class="lang-javascript">var links = [
{title: &quot;item 1&quot;, url: &quot;/item1&quot;}
];
m.render(document.body, [
m(&quot;ul.nav&quot;, [
m(&quot;li&quot;, links.map(function(link) {
return m(&quot;a&quot;, {href: link.url, config: m.route}, link.title)
})
])
]);</code></pre>
<p>yields:</p>
<pre><code class="lang-markup">&lt;body&gt;
&lt;ul class=&quot;nav&quot;&gt;
&lt;li&gt;
&lt;a href=&quot;/item1&quot;&gt;item 1&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/body&gt;</code></pre>
<hr>
<h3 id="subtree-directives">Subtree Directives</h3>
<p><code>m.render</code> accepts a special low level SubtreeDirective object as a node in a virtual DOM tree: if a tree contains a node that looks exactly like the object below, Mithril will abort the diff algorithm for that node. This allows you to implement optimizations that avoid creating virtual DOM trees in favor of their cached counterparts, if you know they have not changed between redraws. Note that using this feature is discouraged if you don&#39;t have visible performance problems.</p>
<pre><code class="lang-javascript">{subtree: &quot;retain&quot;}</code></pre>
<p>This mechanism is only intended to be used as a last resort optimization tool. If you do use it, you are responsible for determining what constitutes a scenario where the virtual DOM tree is changed/unchanged.</p>
<p>The example below shows how to use a SubtreeDirective object to create a static header that doesn&#39;t incur diff costs once it has been rendered. This means that we are avoiding the creation of the header subtree (and therefore skipping the diff algorithm) altogether, but it also means that dynamic variables will NOT be updated within the header.</p>
<pre><code>var app = {}
//here&#39;s an example plugin that determines whether data has changes.
//in this case, it simply assume data has changed the first time, and never changes after that.
app.bindOnce = new function() {
var cache = {}
function(view) {
if (!cache[view.toString()]) {
cache[view.toString()] = true
return view()
}
else return {subtree: &quot;retain&quot;}
}
}
//here&#39;s the view
app.view = function(ctrl) {
m(&quot;.layout&quot;, [
app.bindOnce(function() {
//this only runs once in order to boost performance
//dynamic variables are not updated here
return m(&quot;header&quot;, [
m(&quot;h1&quot;, &quot;this never changes&quot;)
])
}),
//dynamic variables here still update on every redraw
m(&quot;main&quot;, &quot;rest of app goes here&quot;)
])
}</code></pre>
<hr>
<h3 id="signature">Signature</h3>
<p><a href="how-to-read-signatures.html">How to read signatures</a></p>
<pre><code class="lang-clike">void render(DOMElement rootElement, Children children)
where:
Children :: String text | VirtualElement virtualElement | SubtreeDirective directive | Array&lt;Children children&gt;
VirtualElement :: Object { String tag, Attributes attributes, Children children }
Attributes :: Object&lt;Any | void config(DOMElement element)&gt;
SubtreeDirective :: Object { String subtree }</code></pre>
<ul>
<li><p><strong>DOMElement rootElement</strong></p>
<p>A DOM element which will contain the template represented by <code>children</code>.</p>
</li>
<li><p><strong>Children children</strong></p>
<p>If this argument is a string, it will be rendered as a text node. To render a string as HTML, see <a href="mithril.trust"><code>m.trust</code></a></p>
<p>If it&#39;s a VirtualElement, it will be rendered as a DOM Element.</p>
<p>If it&#39;s a list, its contents will recursively be rendered as appropriate and appended as children of the <code>root</code> element.</p>
</li>
</ul>
</div>
</div>
</div>
</section>
</main>
<footer>
<div class="container">
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
<br />&copy; 2014 Leo Horie
</div>
</footer>
<script src="lib/prism/prism.js"></script>
</body>
</html>

View file

@ -0,0 +1,399 @@
<!doctype html>
<html>
<head>
<title>Mithril</title>
<link href="lib/prism/prism.css" rel="stylesheet" />
<link href="style.css" rel="stylesheet" />
</head>
<body>
<header>
<nav class="container">
<a href="index.html" class="logo"><span>&#9675;</span> Mithril</a>
<a href="getting-started.html">Guide</a>
<a href="mithril.html">API</a>
<a href="community.html">Community</a>
<a href="mithril.min.zip">Download</a>
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
</nav>
</header>
<main>
<section class="content">
<div class="container">
<div class="row">
<div class="col(3,3,12)">
<h2 id="api">API (v0.1.16)</h2>
<h3 id="core">Core</h3>
<ul>
<li><a href="mithril.html">m</a></li>
<li><a href="mithril.prop.html">m.prop</a></li>
<li><a href="mithril.withAttr.html">m.withAttr</a></li>
<li><a href="mithril.module.html">m.module</a></li>
<li><a href="mithril.trust.html">m.trust</a></li>
<li><a href="mithril.render.html">m.render</a></li>
<li><a href="mithril.redraw.html">m.redraw</a></li>
</ul>
<h3 id="routing">Routing</h3>
<ul>
<li><a href="mithril.route.html">m.route</a>
<ul>
<li><a href="mithril.route.html#defining-routes">m.route(rootElement, defaultRoute, routes)</a></li>
<li><a href="mithril.route.html#redirecting">m.route(path, params)</a></li>
<li><a href="mithril.route.html#reading-current-route">m.route()</a></li>
<li><a href="mithril.route.html#mode-abstraction">m.route(element)</a></li>
<li><a href="mithril.route.html#mode">m.route.mode</a></li>
<li><a href="mithril.route.html#param">m.route.param</a></li>
</ul>
</li>
</ul>
<h3 id="data">Data</h3>
<ul>
<li><a href="mithril.request.html">m.request</a></li>
<li><a href="mithril.deferred.html">m.deferred</a></li>
<li><a href="mithril.sync.html">m.sync</a></li>
<li><a href="mithril.computation.html">m.startComputation / m.endComputation</a></li>
</ul>
<h2 id="archive">History</h2>
<ul>
<li><a href="roadmap.html">Roadmap</a></li>
<li><a href="change-log.html">Change log</a></li>
</ul>
</div>
<div class="col(9,9,12)">
<h2 id="m-request">m.request</h2>
<p>This is a high-level utility for working with web services, which allows writing asynchronous code relatively procedurally.</p>
<p>By default, it assumes server responses are in JSON format and optionally instantiates a class with the response data.</p>
<p>It provides a number of useful features out of the box:</p>
<ul>
<li>The ability to get an early reference to a container that will hold the asynchronous response</li>
<li>The ability to queue operations to be performed after the asynchronous request completes</li>
<li>The ability to &quot;cast&quot; the response to a class of your choice</li>
<li>The ability to unwrap data in a response that includes metadata properties</li>
</ul>
<hr>
<h3 id="basic-usage">Basic usage</h3>
<p>The basic usage pattern for <code>m.request</code> returns an <a href="mithril.prop.html"><code>m.prop</code></a> getter-setter, which is populated when the AJAX request completes.</p>
<p>The returned getter-setter can be thought of as a box: you can pass this reference around cheaply, and you can &quot;unwrap&quot; its value when needed.</p>
<pre><code class="lang-javascript">var users = m.request({method: &quot;GET&quot;, url: &quot;/user&quot;});
//assuming the response contains the following data: `[{name: &quot;John&quot;}, {name: &quot;Mary&quot;}]`
//then when resolved (e.g. in a view), the `users` getter-setter will contain a list of users
//i.e. users() //[{name: &quot;John&quot;}, {name: &quot;Mary&quot;}]</code></pre>
<p>Note that this getter-setter holds an <em>undefined</em> value until the AJAX request completes. Attempting to unwrap its value early will likely result in errors.</p>
<p>The returned getter-setter also implements the <a href="mithril.deferred.html">promise</a> interface (also known as a <em>thennable</em>): this is the mechanism you should always use to queue operations to be performed on the data from the web service.</p>
<p>The simplest use case of this feature is to implement functional value assignment via <code>m.prop</code> (i.e. the same thing as above). You can bind a pre-existing getter-setter by passing it in as a parameter to a <code>.then</code> method:</p>
<pre><code class="lang-javascript">var users = m.prop([]); //default value
m.request({method: &quot;GET&quot;, url: &quot;/user&quot;}).then(users)
//assuming the response contains the following data: `[{name: &quot;John&quot;}, {name: &quot;Mary&quot;}]`
//then when resolved (e.g. in a view), the `users` getter-setter will contain a list of users
//i.e. users() //[{name: &quot;John&quot;}, {name: &quot;Mary&quot;}]</code></pre>
<p>This syntax allows you to bind intermediate results before piping them down for further processing, for example:</p>
<pre><code class="lang-javascript">var users = m.prop([]); //default value
var doSomething = function() { /*...*/ }
m.request({method: &quot;GET&quot;, url: &quot;/user&quot;}).then(users).then(doSomething)</code></pre>
<p>While both basic assignment syntax and thennable syntax can be used to the same effect, typically it&#39;s recommended that you use the assignment syntax whenever possible, as it&#39;s easier to read.</p>
<p>The thennable mechanism is intended to be used in three ways:</p>
<ul>
<li>in the model layer: to process web service data in transformative ways (e.g. filtering a list based on a parameter that the web service doesn&#39;t support)</li>
<li>in the controller layer: to bind redirection code upon a condition</li>
<li>in the controller layer: to bind error messages</li>
</ul>
<h4 id="processing-web-service-data">Processing web service data</h4>
<p>This step is meant to be done in the model layer. Doing it in the controller level is also possible, but philosophically not recommended, because by tying logic to a controller, the code becomes harder to reuse due to unrelated controller dependencies.</p>
<p>In the example below, the <code>listEven</code> method returns a getter-setter that resolves to a list of users containing only users whose id is even.</p>
<pre><code class="lang-javascript">//model
var User = {}
User.listEven = function() {
return m.request({method: &quot;GET&quot;, url: &quot;/user&quot;}).then(function(list) {
return list.filter(function(user) {return user.id % 2 == 0});
});
}
//controller
var controller = function() {
this.users = User.listEven()
}</code></pre>
<h4 id="bind-redirection-code">Bind redirection code</h4>
<p>This step is meant to be done in the controller layer. Doing it in the model level is also possible, but philosophically not recommended, because by tying redirection to the model, the code becomes harder to reuse due to overly tight coupling.</p>
<p>In the example below, we use the previously defined <code>listEven</code> model method and queue a controller-level function that redirects to another page if the user list is empty.</p>
<pre><code class="lang-javascript">//controller
var controller = function() {
this.users = User.listEven().then(function(users) {
if (users.length == 0) m.route(&quot;/add&quot;);
})
}</code></pre>
<h4 id="binding-errors">Binding errors</h4>
<p>Mithril thennables take two functions as optional parameters: the first parameter is called if the web service request completes successfully. The second one is called if it completes with an error.</p>
<p>Error binding is meant to be done in the controller layer. Doing it in the model level is also possible, but generally leads to more code in order to connect all the dots.</p>
<p>In the example below, we bind an error getter-setter to our previous controller so that the <code>error</code> variable gets populated if the server throws an error.</p>
<pre><code class="lang-javascript">//controller
var controller = function() {
this.error = m.prop(&quot;&quot;)
this.users = User.listEven().then(function(users) {
if (users.length == 0) m.route(&quot;/add&quot;);
}, this.error)
}</code></pre>
<p>If the controller doesn&#39;t already have a success callback to run after a request resolves, you can still bind errors like this:</p>
<pre><code class="lang-javascript">//controller
var controller = function() {
this.error = m.prop(&quot;&quot;)
this.users = User.listEven().then(null, this.error)
}</code></pre>
<hr>
<h3 id="queuing-operations">Queuing Operations</h3>
<p>As you saw, you can chain operations that act on the response data. Typically this is required in three situations:</p>
<ul>
<li>in model-level methods if client-side processing is needed to make the data useful for a controller or view.</li>
<li>in the controller, to redirect after a model service resolves.</li>
<li>in the controller, to bind error messages</li>
</ul>
<p>In the example below, we take advantage of queuing to debug the AJAX response data prior to doing further processing on the user list</p>
<pre><code class="lang-javascript">var users = m.request({method: &quot;GET&quot;, url: &quot;/user&quot;})
.then(console.log);
.then(function(users) {
//add one more user to the response
return users.concat({name: &quot;Jane&quot;})
})
//assuming the response contains the following data: `[{name: &quot;John&quot;}, {name: &quot;Mary&quot;}]`
//then when resolved (e.g. in a view), the `users` getter-setter will contain a list of users
//i.e. users() //[{name: &quot;John&quot;}, {name: &quot;Mary&quot;}, {name: &quot;Jane&quot;}]</code></pre>
<hr>
<h3 id="casting-the-response-data-to-a-class">Casting the Response Data to a Class</h3>
<p>It&#39;s possible to auto-cast a JSON response to a class. This is useful when we want to control access to certain properties in an object, as opposed to exposing all the fields in POJOs (plain old Javascript objects) for arbitrary processing.</p>
<p>In the example below, <code>User.list</code> returns a list of <code>User</code> instances.</p>
<pre><code class="lang-javascript">var User = function(data) {
this.name = m.prop(data.name);
}
User.list = function() {
return m.request({method: &quot;GET&quot;, url: &quot;/user&quot;, type: User});
}
var users = User.list();
//assuming the response contains the following data: `[{name: &quot;John&quot;}, {name: &quot;Mary&quot;}]`
//then when resolved (e.g. in a view), `users` will contain a list of User instances
//i.e. users()[0].name() == &quot;John&quot;</code></pre>
<hr>
<h3 id="unwrapping-response-data">Unwrapping Response Data</h3>
<p>Often, web services return the relevant data wrapped in objects that contain metadata.</p>
<p>Mithril allows you to unwrap the relevant data, by providing two callback hooks: <code>unwrapSuccess</code> and <code>unwrapError</code>.</p>
<p>These hooks allow you to unwrap different parts of the response data depending on whether it succeed or failed.</p>
<pre><code class="lang-javascript">var users = m.request({
method: &quot;GET&quot;,
url: &quot;/user&quot;,
unwrapSuccess: function(response) {
return response.data;
},
unwrapError: function(response) {
return response.error;
}
});
//assuming the response is: `{data: [{name: &quot;John&quot;}, {name: &quot;Mary&quot;}], count: 2}`
//then when resolved (e.g. in a view), the `users` getter-setter will contain a list of users
//i.e. users() //[{name: &quot;John&quot;}, {name: &quot;Mary&quot;}]</code></pre>
<hr>
<h3 id="using-different-data-transfer-formats">Using Different Data Transfer Formats</h3>
<p>By default, <code>m.request</code> uses JSON to send and receive data to web services. You can override this by providing <code>serialize</code> and <code>deserialize</code> options:</p>
<pre><code class="lang-javascript">var users = m.request({
method: &quot;GET&quot;,
url: &quot;/user&quot;,
serialize: mySerializer,
deserialize: myDeserializer
});</code></pre>
<p>One typical way to override this is to receive as-is responses. The example below shows how to receive a plain string from a txt file.</p>
<pre><code class="lang-javascript">var file = m.request({
method: &quot;GET&quot;,
url: &quot;myfile.txt&quot;,
deserialize: function(value) {return value;}
});</code></pre>
<hr>
<h3 id="using-variable-data-formats">Using variable data formats</h3>
<p>By default, Mithril assumes both success and error responses are in JSON format, but some servers may not return JSON responses when returning HTTP error codes (e.g. 404)</p>
<p>You can get around this issue by using <code>extract</code></p>
<pre><code class="lang-javascript">var nonJsonErrors = function(xhr) {
return xhr.status &gt; 200 ? JSON.stringify(xhr.responseText) : xhr.responseText
}
m.request({method: &quot;GET&quot;, url: &quot;/foo/bar.x&quot;, extract: nonJsonErrors})
.then(function(data) {}, function(error) {console.log(error)})</code></pre>
<hr>
<h3 id="extracting-metadata-from-the-response">Extracting Metadata from the Response</h3>
<p>The <code>extract</code> method can be used to read metadata from HTTP response headers or the status field of an XMLHttpRequest.</p>
<pre><code class="lang-javascript">var extract = function(xhr, xhrOptions) {
if (xhrOptions.method == &quot;HEAD&quot;) return xhr.getResponseHeader(&quot;x-item-count&quot;)
else return xhr.responseText
}
m.request({method: &quot;POST&quot;, url: &quot;/foo&quot;, extract: extract});</code></pre>
<hr>
<h3 id="configuring-the-underlying-xmlhttprequest">Configuring the underlying XMLHttpRequest</h3>
<p>The <code>config</code> option can be used to arbitrarily configure the native XMLHttpRequest instance and to access properties that would not be accessible otherwise.</p>
<p>The example below show how to configure a request where the server expects requests to have a <code>Content-Type: application/json</code> header</p>
<pre><code class="lang-javascript">var xhrConfig = function(xhr) {
xhr.setRequestHeader(&quot;Content-Type&quot;, &quot;application/json&quot;);
}
m.request({method: &quot;POST&quot;, url: &quot;/foo&quot;, config: xhrConfig});</code></pre>
<hr>
<h3 id="aborting-a-request">Aborting a request</h3>
<p>The <code>config</code> option can also be used to retrieve the <code>XMLHttpRequest</code> instance for aborting the request. This idiom can also be used to attach <code>onprogress</code> event handlers.</p>
<pre><code class="lang-javascript">var transport = m.prop();
m.request({method: &quot;POST&quot;, url: &quot;/foo&quot;, config: transport});
//the `transport` getter-setter contains an instance of XMLHttpRequest
transport().abort();</code></pre>
<hr>
<h3 id="signature">Signature</h3>
<p><a href="how-to-read-signatures.html">How to read signatures</a></p>
<pre><code class="lang-clike">Promise request(XHROptions options)
where:
Promise :: GetterSetter { Promise then(any successCallback(any value), any errorCallback(any value)) }
GetterSetter :: any getterSetter([any value])
XHROptions :: Object {
String method,
String url,
[String user,]
[String password,]
[Object&lt;any&gt; data,]
[Boolean background,]
[any unwrapSuccess(any data),]
[any unwrapError(any data),]
[String serialize(any dataToSerialize),]
[any deserialize(String dataToDeserialize),]
[any extract(XMLHttpRequest xhr, XHROptions options),]
[void type(Object&lt;any&gt; data),]
[XMLHttpRequest? config(XMLHttpRequest xhr, XHROptions options)]
}</code></pre>
<ul>
<li><p><strong>XHROptions options</strong></p>
<p>A map of options for the XMLHttpRequest</p>
<ul>
<li><p><strong>String method</strong></p>
<p>The HTTP method. Must be either <code>&quot;GET&quot;</code>, <code>&quot;POST&quot;</code>, <code>&quot;PUT&quot;</code>, <code>&quot;DELETE&quot;</code>, <code>&quot;HEAD&quot;</code> or <code>&quot;OPTIONS&quot;</code></p>
</li>
<li><p><strong>String url</strong></p>
<p>The URL to request. If the URL is not in the same domain as the application, the target server must be configured to accept cross-domain requests from the application&#39;s domain, i.e. its responses must include the header <code>Access-Control-Allow-Origin: *</code>.</p>
</li>
<li><p><strong>String user</strong> (optional)</p>
<p>A user for HTTP authentication. Defaults to <code>undefined</code></p>
</li>
<li><p><strong>String password</strong> (optional)</p>
<p>A password for HTTP authentication. Defaults to <code>undefined</code></p>
</li>
<li><p><strong>String password</strong> (optional)</p>
<p>A password for HTTP authentication. Defaults to <code>undefined</code></p>
</li>
<li><p><strong>Object<any> data</strong> (optional)</p>
<p>Data to be sent. It&#39;s automatically placed in the appropriate section of the request with the appropriate serialization based on <code>method</code></p>
</li>
<li><p><strong>Boolean background</strong> (optional)</p>
<p>Determines whether the <code>m.request</code> can affect template rendering. Defaults to false.</p>
<p>If this option is set to true, then the request does NOT call <a href="mithril.computation.html"><code>m.startComputation</code> / <code>m.endComputation</code></a>, and therefore the completion of the request does not trigger an update of the view, even if data has been changed. This option is useful for running operations in the background (i.e. without user intervention).</p>
<p>In order to force a redraw after a background request, use <a href="mithril.redraw.html"><code>m.redraw</code></a></p>
<pre><code class="lang-javascript">m.request({method: &quot;GET&quot;, url: &quot;/foo&quot;, background: true})
.then(m.redraw); //force redraw</code></pre>
</li>
<li><p><strong>any unwrapSuccess(any data)</strong> (optional)</p>
<p>A preprocessor function to unwrap the data from a success response in case the response contains metadata wrapping the data.</p>
<p>The default value (if this parameter is falsy) is the identity function <code>function(value) {return value}</code></p>
<p>For example, if the response is <code>{data: [{name: &quot;John&quot;}, {name: &quot;Mary&quot;}]}</code> and the unwrap function is <code>function(response) {return response.data}</code>, then the response will be considered to be <code>[{name: &quot;John&quot;}, {name: &quot;Mary&quot;}]</code> when processing the <code>type</code> parameter</p>
<ul>
<li><p><strong>Object<any> | Array<any> data</strong></p>
<p>The data to unwrap</p>
</li>
<li><p><strong>returns Object<any> | Array<any> unwrappedData</strong></p>
<p>The unwrapped data</p>
</li>
</ul>
</li>
<li><p><strong>any unwrapError(any data)</strong> (optional)</p>
<p>A preprocessor function to unwrap the data from an error response in case the response contains metadata wrapping the data.</p>
<p>The default value (if this parameter is falsy) is the identity function <code>function(value) {return value}</code></p>
<ul>
<li><p><strong>Object<any> | Array<any> data</strong></p>
<p>The data to unwrap</p>
</li>
<li><p><strong>returns Object<any> | Array<any> unwrappedData</strong></p>
<p>The unwrapped data</p>
</li>
</ul>
</li>
<li><p><strong>String serialize(any dataToSerialize)</strong> (optional)</p>
<p>Method to use to serialize the request data</p>
<p>The default value (if this parameter is falsy) is <code>JSON.stringify</code></p>
<ul>
<li><p><strong>any dataToSerialize</strong></p>
<p>Data to be serialized</p>
</li>
<li><p><strong>returns String serializedData</strong></p>
</li>
</ul>
</li>
<li><p><strong>any deserialize(String dataToDeserialize)</strong> (optional)</p>
<p>Method to use to deserialize the response data</p>
<p>The default value (if this parameter is falsy) is <code>JSON.parse</code></p>
<ul>
<li><p><strong>String dataToDeserialize</strong></p>
<p>Data to be deserialized</p>
</li>
<li><p><strong>returns any deserializedData</strong></p>
</li>
</ul>
</li>
<li><p><strong>any extract(XMLHttpRequest xhr, XHROptions options)</strong> (optional)</p>
<p>Method to use to extract the data from the raw XMLHttpRequest. This is useful when the relevant data is either in a response header or the status field.</p>
<p>If this parameter is falsy, the default value is a function that returns <code>xhr.responseText</code>.</p>
</li>
<li><p><strong>void type(Object<any> data)</strong> (optional)</p>
<p>The response object (or the child items if this object is an Array) will be passed as a parameter to the class constructor defined by <code>type</code></p>
<p>If this parameter is falsy, the deserialized data will not be wrapped.</p>
<p>For example, if <code>type</code> is the following class:</p>
<pre><code class="lang-javascript">var User = function(data) {
this.name = m.prop(data.name);
}</code></pre>
<p>And the data is <code>[{name: &quot;John&quot;}, {name: &quot;Mary&quot;}]</code>, then the response will contain an array of two User instances.</p>
</li>
<li><p><strong>XMLHttpRequest? config(XMLHttpRequest xhr, XHROptions options)</strong> (optional)</p>
<p>An initialization function that runs after <code>open</code> and before <code>send</code>. Useful for adding request headers and when using XHR2 features, such as the XMLHttpRequest&#39;s <code>upload</code> property.</p>
<ul>
<li><p><strong>XMLHttpRequest xhr</strong></p>
<p>The XMLHttpRequest instance.</p>
</li>
<li><p><strong>XHROptions options</strong></p>
<p>The <code>options</code> parameter that was passed into <code>m.request</code> call</p>
</li>
<li><p><strong>returns XMLHttpRequest? xhr</strong></p>
<p>You may return an XHR-like object (e.g. a XDomainRequest instance) to override the provided XHR instance altogether.</p>
</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>returns Promise promise</strong></p>
<p>returns a promise that can bind callbacks which get called on completion of the AJAX request.</p>
</li>
</ul>
</div>
</div>
</div>
</section>
</main>
<footer>
<div class="container">
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
<br />&copy; 2014 Leo Horie
</div>
</footer>
<script src="lib/prism/prism.js"></script>
</body>
</html>

View file

@ -0,0 +1,300 @@
<!doctype html>
<html>
<head>
<title>Mithril</title>
<link href="lib/prism/prism.css" rel="stylesheet" />
<link href="style.css" rel="stylesheet" />
</head>
<body>
<header>
<nav class="container">
<a href="index.html" class="logo"><span>&#9675;</span> Mithril</a>
<a href="getting-started.html">Guide</a>
<a href="mithril.html">API</a>
<a href="community.html">Community</a>
<a href="mithril.min.zip">Download</a>
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
</nav>
</header>
<main>
<section class="content">
<div class="container">
<div class="row">
<div class="col(3,3,12)">
<h2 id="api">API (v0.1.16)</h2>
<h3 id="core">Core</h3>
<ul>
<li><a href="mithril.html">m</a></li>
<li><a href="mithril.prop.html">m.prop</a></li>
<li><a href="mithril.withAttr.html">m.withAttr</a></li>
<li><a href="mithril.module.html">m.module</a></li>
<li><a href="mithril.trust.html">m.trust</a></li>
<li><a href="mithril.render.html">m.render</a></li>
<li><a href="mithril.redraw.html">m.redraw</a></li>
</ul>
<h3 id="routing">Routing</h3>
<ul>
<li><a href="mithril.route.html">m.route</a>
<ul>
<li><a href="mithril.route.html#defining-routes">m.route(rootElement, defaultRoute, routes)</a></li>
<li><a href="mithril.route.html#redirecting">m.route(path, params)</a></li>
<li><a href="mithril.route.html#reading-current-route">m.route()</a></li>
<li><a href="mithril.route.html#mode-abstraction">m.route(element)</a></li>
<li><a href="mithril.route.html#mode">m.route.mode</a></li>
<li><a href="mithril.route.html#param">m.route.param</a></li>
</ul>
</li>
</ul>
<h3 id="data">Data</h3>
<ul>
<li><a href="mithril.request.html">m.request</a></li>
<li><a href="mithril.deferred.html">m.deferred</a></li>
<li><a href="mithril.sync.html">m.sync</a></li>
<li><a href="mithril.computation.html">m.startComputation / m.endComputation</a></li>
</ul>
<h2 id="archive">History</h2>
<ul>
<li><a href="roadmap.html">Roadmap</a></li>
<li><a href="change-log.html">Change log</a></li>
</ul>
</div>
<div class="col(9,9,12)">
<h2 id="m-route">m.route</h2>
<p>Routing is a system that allows creating Single-Page-Applications (SPA), i.e. applications that can go from a page to another without causing a full browser refresh.</p>
<p>It enables seamless navigability while preserving the ability to bookmark each page individually, and the ability to navigate the application via the browser&#39;s history mechanism.</p>
<p>This method overloads four different units of functionality:</p>
<ul>
<li><p><code>m.route(rootElement, defaultRoute, routes)</code> - defines the available URLs in an application, and their respective modules</p>
</li>
<li><p><code>m.route(path)</code> - redirects to another route</p>
</li>
<li><p><code>m.route()</code> - returns the currently active route</p>
</li>
<li><p><code>m.route(element)</code> - an extension to link elements that unobtrusively abstracts away the routing mode</p>
</li>
</ul>
<p>Routing is single-page-application (SPA) friendly, and can be implemented using either <code>location.hash</code>, HTML5 URL rewriting or <code>location.querystring</code>. See <a href="#mode"><code>m.route.mode</code></a> for the caveats of each implementation.</p>
<hr>
<p><a name="defining-routes"></a></p>
<h3 id="defining-routes">Defining routes</h3>
<h4 id="usage">Usage</h4>
<p>To define a list of routes, you need to specify a host DOM element, a default route and a key-value map of possible routes and respective <a href="mithril.module.html">modules</a> to be rendered.</p>
<p>The example below defines three routes, to be rendered in <code>&lt;body&gt;</code>. <code>home</code>, <code>login</code> and <code>dashboard</code> are modules. We&#39;ll see how to define a module in a bit.</p>
<pre><code class="lang-javascript">m.route(document.body, &quot;/&quot;, {
&quot;/&quot;: home,
&quot;/login&quot;: login,
&quot;/dashboard&quot;: dashboard,
});</code></pre>
<p>Routes can take arguments, by prefixing words with a colon <code>:</code></p>
<p>The example below shows a route that takes an <code>userID</code> parameter</p>
<pre><code class="lang-javascript">//a sample module
var dashboard = {
controller: function() {
this.id = m.route.param(&quot;userID&quot;);
},
view: function(controller) {
return m(&quot;div&quot;, controller.id);
}
}
//define a route
m.route(document.body, &quot;/dashboard/johndoe&quot;, {
&quot;/dashboard/:userID&quot;: dashboard
});
//setup routes to start w/ the `#` symbol
m.route.mode = &quot;hash&quot;;</code></pre>
<p>This redirects to the URL <code>http://server/#/dashboard/johndoe</code> and yields:</p>
<pre><code class="lang-markup">&lt;body&gt;johndoe&lt;/body&gt;</code></pre>
<p>Above, <code>dashboard</code> is a module. It contains a <code>controller</code> and a <code>view</code> properties. When the URL matches a route, the respective module&#39;s controller is instantiated and passed as a parameter to the view.</p>
<p>In this case, since there&#39;s only route, the app redirects to the default route <code>&quot;/dashboard/johndoe&quot;</code>.</p>
<p>The string <code>johndoe</code> is bound to the <code>:userID</code> parameter, which can be retrieved programmatically in the controller via <code>m.route.param(&quot;userID&quot;)</code>.</p>
<p>The <code>m.route.mode</code> defines which part of the URL to use for routing.</p>
<hr>
<h4 id="variadic-routes">Variadic routes</h4>
<p>We can append an ellipsis (<code>...</code>) to the name of a route argument to allow it to match URL snippets that contain slashes:</p>
<pre><code class="lang-javascript">m.route(document.body, &quot;/files/pictures/pic1.jpg&quot;, {
&quot;/files/:file...&quot;: gallery
});
m.route.param(&quot;file&quot;) === &quot;pictures/pic1.jpg&quot;</code></pre>
<pre><code class="lang-javascript">m.route(document.body, &quot;/blog/2014/01/20/articles&quot;, {
&quot;/blog/:date.../articles&quot;: articleList
});
m.route.param(&quot;date&quot;) === &quot;2014/01/20&quot;</code></pre>
<p>Note that Mithril checks for route matches in the order the routes are defined, so you should put variadic routes at the bottom of the list to prevent them from matching other more specific routes.</p>
<pre><code>m.route(document.body, &quot;/blog/archive/2014&quot;, {
&quot;/blog/:date...&quot;: module1, //for the default path in the line above, this route matches first!
&quot;/blog/archive/:year&quot;: module2
});
m.route.param(&quot;date&quot;) === &quot;archive/2014&quot;
//the routes should be flipped around to get `m.route.param(&quot;year&quot;) == &quot;2014&quot;`</code></pre>
<hr>
<h4 id="routes-with-querystrings">Routes with querystrings</h4>
<p>In addition to route parameters, it&#39;s possible to pass arbitrary data to <code>m.route.param</code> using the querystring</p>
<pre><code class="lang-javascript">m.route(&quot;/grid?sortby=date&amp;dir=desc&quot;)
var sortBy = m.route.param(&quot;sortby&quot;) // &quot;date&quot;
var dir = m.route.param(&quot;dir&quot;) // &quot;desc&quot;</code></pre>
<hr>
<h4 id="running-clean-up-code-on-route-change">Running clean up code on route change</h4>
<p>If a module&#39;s controller implements an instance method called <code>onunload</code>, this method will be called when a route changes.</p>
<pre><code class="lang-javascript">var home = {};
home.controller = function() {
this.onunload = function() {
console.log(&quot;unloading home module&quot;);
};
};
var dashboard = {};
dashboard.controller = function() {};
dashboard.view = function() {};
//go to the default route (home)
m.route(document.body, &quot;/&quot;, {
&quot;/&quot;: home,
&quot;/dashboard&quot;: dashboard,
});
//re-route to dashboard
m.route(&quot;/dashboard&quot;); // logs &quot;unloading home&quot;</code></pre>
<p>This mechanism is useful to clear timers and unsubscribe event handlers. If you have a hierarchy of components, you can recursively call <code>unload</code> on all the components in the tree or use a <a href="http://microjs.com/#pubsub">pubsub</a> library to unload specific components on demand.</p>
<hr>
<h4 id="signature">Signature</h4>
<p><a href="how-to-read-signatures.html">How to read signatures</a></p>
<pre><code class="lang-clike">void route(DOMElement rootElement, String defaultRoute, Object&lt;Module&gt; routes) { String mode, String param(String key) }
where:
Module :: Object { void controller(), void view(Object controllerInstance) }</code></pre>
<ul>
<li><p><strong>DOMElement root</strong></p>
<p>A DOM element which will contain the view&#39;s template.</p>
</li>
<li><p><strong>String defaultRoute</strong></p>
<p>The route to redirect to if the current URL does not match any of the defined routes</p>
</li>
<li><p><strong>Object<Module> routes</strong></p>
<p>A key-value map of possible routes and their respective modules. Keys are expected to be absolute pathnames, but can include dynamic parameters. Dynamic parameters are words preceded by a colon <code>:</code></p>
<p><code>{&#39;/path/to/page/&#39;: pageModule}</code> - a route with a basic pathname</p>
<p><code>{&#39;/path/to/page/:id&#39;: pageModule}</code> - a route with a pathname that contains a dynamic parameter called <code>id</code>. This route would be selected if the URL was <code>/path/to/page/1</code>, <code>/path/to/page/test</code>, etc</p>
<p><code>{&#39;/user/:userId/book/:bookId&#39;: userBookModule}</code> - a route with a pathname that contains two parameters</p>
<p>Dynamic parameters are wild cards that allow selecting a module based on a URL pattern. The values that replace the dynamic parameters in a URL are available via <code>m.route.param()</code></p>
<p>Note that the URL component used to resolve routes is dependent on <code>m.route.mode</code>. By default, the querystring is considered the URL component to test against the routes collection</p>
<p>If the current page URL matches a route, its respective module is activated. See <code>m.module</code> for information on modules.</p>
</li>
<li><p><a name="mode"></a></p>
<h4 id="m-route-mode">m.route.mode</h4>
<p><strong>String mode</strong></p>
<p>The <code>m.route.mode</code> property defines which URL portion is used to implement the routing mechanism. Its value can be set to either &quot;search&quot;, &quot;hash&quot; or &quot;pathname&quot;. Default value is &quot;search&quot;. Note that if you&#39;re changing this configuration value, you should change it <strong>before</strong> calling <code>m.route</code>.</p>
<ul>
<li><p><code>search</code> mode uses the querystring (i.e. <code>?</code>). This allows named anchors (i.e. <code>&lt;a href=&quot;#top&quot;&gt;Back to top&lt;/a&gt;</code>, <code>&lt;a name=&quot;top&quot;&gt;&lt;/a&gt;</code>) to work on the page, but routing changes causes page refreshes in IE8, due to its lack of support for <code>history.pushState</code>.</p>
<p>Example URL: <code>http://server/?/path/to/page</code></p>
</li>
<li><p><code>hash</code> mode uses the hash (i.e. <code>#</code>). It&#39;s the only mode in which routing changes do not cause page refreshes in any browser. However, this mode does not support named anchors.</p>
<p>Example URL: <code>http://server/#/path/to/page</code></p>
</li>
<li><p><code>pathname</code> mode allows routing URLs that contains no special characters, however this mode requires server-side setup in order to support bookmarking and page refreshes. It also causes page refreshes in IE8.</p>
<p>Example URL: <code>http://server/path/to/page</code></p>
<p>The simplest server-side setup possible to support pathname mode is to serve the same content regardless of what URL is requested. In Apache, this URL rewriting can be achieved using ModRewrite.</p>
</li>
</ul>
</li>
<li><p><a name="param"></a></p>
<h4 id="m-route-param">m.route.param</h4>
<p><strong>String param(String key)</strong></p>
<p>Route parameters are dynamic values that can be extracted from the URL based on the signature of the currently active route.</p>
<p>A route without parameters looks like this:</p>
<p><code>&quot;/path/to/page/&quot;</code></p>
<p>A route with parameters might look like this:</p>
<p><code>&quot;/path/to/page/:id&quot;</code> - here, <code>id</code> is the name of the route parameter</p>
<p>If the currently active route is <code>/dashboard/:userID</code> and the current URL is <code>/dashboard/johndoe</code>, then calling <code>m.route.param(&quot;userID&quot;)</code> returns <code>&quot;johndoe&quot;</code></p>
<p>Querystring parameters in a route are also available in this collection automatically.</p>
<p><code>&quot;/grid?sortby=date&quot;</code> - here, <code>m.route.param(&quot;sortby&quot;)</code> returns <code>&quot;date&quot;</code></p>
<ul>
<li><p><strong>String key</strong></p>
<p>The name of a route parameter</p>
</li>
<li><p><strong>returns String value</strong></p>
<p>The value that maps to the parameter specified by <code>key</code></p>
</li>
</ul>
</li>
</ul>
<hr>
<p><a name="redirecting"></a></p>
<h3 id="redirecting">Redirecting</h3>
<h4 id="usage">Usage</h4>
<p>You can programmatically redirect to another page. Given the example in the &quot;Defining Routes&quot; section:</p>
<pre><code class="lang-javascript">m.route(&quot;/dashboard/marysue&quot;);</code></pre>
<p>redirects to <code>http://server/#/dashboard/marysue</code></p>
<hr>
<h4 id="signature">Signature</h4>
<p><a href="how-to-read-signatures.html">How to read signatures</a></p>
<pre><code class="lang-clike">void route(String path [, any params])</code></pre>
<ul>
<li><p><strong>String path</strong></p>
<p>The route to redirect to. Note that to redirect to a different page outside of the scope of Mithril&#39;s routing, you should use <code>window.location</code></p>
</li>
<li><p><strong>any params</strong></p>
<p>Parameters to pass as a querystring</p>
</li>
</ul>
<hr>
<p><a name="reading-current-route"></a></p>
<h3 id="reading-the-currently-active-route">Reading the currently active route</h3>
<h4 id="usage">Usage</h4>
<p>Mithril updates the native <code>location</code> object after rendering in order to allow the browser&#39;s <code>history.pushState</code> API to correctly show descriptive history entries (e.g. for Chrome&#39;s Ctrl+H page).</p>
<p>In order to retrieve the currently active route in a controller, you can use <code>m.route()</code>. This returns the portion of the URL determined by <code>m.route.mode</code> (minus the <code>?</code> or <code>#</code> symbols for the <code>search</code> and <code>hash</code> modes, respectively).</p>
<pre><code class="lang-javascript">//if the location bar is &quot;http://example.com/?/foo/bar&quot;
//and m.route.mode is `search`
//then `currentRoute == &quot;/foo/bar&quot;`
var currentRoute = m.route();</code></pre>
<hr>
<h4 id="signature">Signature</h4>
<p><a href="how-to-read-signatures.html">How to read signatures</a></p>
<pre><code class="lang-clike">String route()</code></pre>
<ul>
<li><p><strong>returns String route</strong></p>
<p>returns the currently active route</p>
</li>
</ul>
<hr>
<p><a name="mode-abstraction"></a></p>
<h3 id="mode-abstraction">Mode abstraction</h3>
<h4 id="usage">Usage</h4>
<p>This method is meant to be used with a virtual element&#39;s <code>config</code> attribute. For example:</p>
<pre><code class="lang-javascript">//Note that the &#39;#&#39; is not required in `href`, thanks to the `config` setting.
m(&quot;a[href=&#39;/dashboard/alicesmith&#39;]&quot;, {config: m.route});</code></pre>
<p>This makes the href behave correctly regardless of which <code>m.route.mode</code> is selected. It&#39;s a good practice to always use the idiom above, instead of hardcoding <code>?</code> or <code>#</code> in the href attribute.</p>
<p>See <a href="mithril.html"><code>m()</code></a> for more information on virtual elements.</p>
<hr>
<h4 id="signature">Signature</h4>
<p><a href="how-to-read-signatures.html">How to read signatures</a></p>
<pre><code class="lang-clike">void route(DOMElement element, Boolean isInitialized)</code></pre>
<ul>
<li><p><strong>DOMElement element</strong></p>
<p>an anchor element <code>&lt;a&gt;</code> with an <code>href</code> attribute that points to a route</p>
</li>
<li><p><strong>Boolean isInitialized</strong></p>
<p>the method does not run if this flag is set to true. This is to make the method compatible with virtual DOM elements&#39; <code>config</code> attribute (see <a href="mithril"><code>m()</code></a>)</p>
</li>
</ul>
</div>
</div>
</div>
</section>
</main>
<footer>
<div class="container">
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
<br />&copy; 2014 Leo Horie
</div>
</footer>
<script src="lib/prism/prism.js"></script>
</body>
</html>

View file

@ -0,0 +1,112 @@
<!doctype html>
<html>
<head>
<title>Mithril</title>
<link href="lib/prism/prism.css" rel="stylesheet" />
<link href="style.css" rel="stylesheet" />
</head>
<body>
<header>
<nav class="container">
<a href="index.html" class="logo"><span>&#9675;</span> Mithril</a>
<a href="getting-started.html">Guide</a>
<a href="mithril.html">API</a>
<a href="community.html">Community</a>
<a href="mithril.min.zip">Download</a>
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
</nav>
</header>
<main>
<section class="content">
<div class="container">
<div class="row">
<div class="col(3,3,12)">
<h2 id="api">API (v0.1.16)</h2>
<h3 id="core">Core</h3>
<ul>
<li><a href="mithril.html">m</a></li>
<li><a href="mithril.prop.html">m.prop</a></li>
<li><a href="mithril.withAttr.html">m.withAttr</a></li>
<li><a href="mithril.module.html">m.module</a></li>
<li><a href="mithril.trust.html">m.trust</a></li>
<li><a href="mithril.render.html">m.render</a></li>
<li><a href="mithril.redraw.html">m.redraw</a></li>
</ul>
<h3 id="routing">Routing</h3>
<ul>
<li><a href="mithril.route.html">m.route</a>
<ul>
<li><a href="mithril.route.html#defining-routes">m.route(rootElement, defaultRoute, routes)</a></li>
<li><a href="mithril.route.html#redirecting">m.route(path, params)</a></li>
<li><a href="mithril.route.html#reading-current-route">m.route()</a></li>
<li><a href="mithril.route.html#mode-abstraction">m.route(element)</a></li>
<li><a href="mithril.route.html#mode">m.route.mode</a></li>
<li><a href="mithril.route.html#param">m.route.param</a></li>
</ul>
</li>
</ul>
<h3 id="data">Data</h3>
<ul>
<li><a href="mithril.request.html">m.request</a></li>
<li><a href="mithril.deferred.html">m.deferred</a></li>
<li><a href="mithril.sync.html">m.sync</a></li>
<li><a href="mithril.computation.html">m.startComputation / m.endComputation</a></li>
</ul>
<h2 id="archive">History</h2>
<ul>
<li><a href="roadmap.html">Roadmap</a></li>
<li><a href="change-log.html">Change log</a></li>
</ul>
</div>
<div class="col(9,9,12)">
<h2 id="m-sync">m.sync</h2>
<p>This method takes a list of promises and returns a promise that resolves when all promises in the input list have resolved. See <a href="mithril.deferred"><code>m.deferred</code></a> for more information on promises.</p>
<hr>
<h3 id="usage">Usage</h3>
<pre><code class="lang-javascript">var greetAsync = function(delay) {
var deferred = m.deferred();
setTimeout(function() {
deferred.resolve(&quot;hello&quot;);
}, delay);
return deferred.promise;
};
m.sync([
greetAsync(1000),
greetAsync(1500)
]).then(function(args) {
console.log(args); // [&quot;hello&quot;, &quot;hello&quot;]
});</code></pre>
<hr>
<h3 id="signature">Signature</h3>
<p><a href="how-to-read-signatures.html">How to read signatures</a></p>
<pre><code class="lang-clike">Promise sync(Array&lt;Promise&gt; promises)
where:
Promise :: GetterSetter { Promise then(any successCallback(any value), any errorCallback(any value)) }
GetterSetter :: any getterSetter([any value])</code></pre>
<ul>
<li><p><strong>Array<Promise> promises</strong></p>
<p>A list of promises to synchronize</p>
</li>
<li><p><strong>return Promise promise</strong></p>
<p>The promise of the deferred object that is resolved when all input promises have been resolved</p>
<p>The callbacks for this promise receive as a parameter an Array containing the values of all the input promises</p>
</li>
</ul>
</div>
</div>
</div>
</section>
</main>
<footer>
<div class="container">
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
<br />&copy; 2014 Leo Horie
</div>
</footer>
<script src="lib/prism/prism.js"></script>
</body>
</html>

View file

@ -0,0 +1,121 @@
<!doctype html>
<html>
<head>
<title>Mithril</title>
<link href="lib/prism/prism.css" rel="stylesheet" />
<link href="style.css" rel="stylesheet" />
</head>
<body>
<header>
<nav class="container">
<a href="index.html" class="logo"><span>&#9675;</span> Mithril</a>
<a href="getting-started.html">Guide</a>
<a href="mithril.html">API</a>
<a href="community.html">Community</a>
<a href="mithril.min.zip">Download</a>
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
</nav>
</header>
<main>
<section class="content">
<div class="container">
<div class="row">
<div class="col(3,3,12)">
<h2 id="api">API (v0.1.16)</h2>
<h3 id="core">Core</h3>
<ul>
<li><a href="mithril.html">m</a></li>
<li><a href="mithril.prop.html">m.prop</a></li>
<li><a href="mithril.withAttr.html">m.withAttr</a></li>
<li><a href="mithril.module.html">m.module</a></li>
<li><a href="mithril.trust.html">m.trust</a></li>
<li><a href="mithril.render.html">m.render</a></li>
<li><a href="mithril.redraw.html">m.redraw</a></li>
</ul>
<h3 id="routing">Routing</h3>
<ul>
<li><a href="mithril.route.html">m.route</a>
<ul>
<li><a href="mithril.route.html#defining-routes">m.route(rootElement, defaultRoute, routes)</a></li>
<li><a href="mithril.route.html#redirecting">m.route(path, params)</a></li>
<li><a href="mithril.route.html#reading-current-route">m.route()</a></li>
<li><a href="mithril.route.html#mode-abstraction">m.route(element)</a></li>
<li><a href="mithril.route.html#mode">m.route.mode</a></li>
<li><a href="mithril.route.html#param">m.route.param</a></li>
</ul>
</li>
</ul>
<h3 id="data">Data</h3>
<ul>
<li><a href="mithril.request.html">m.request</a></li>
<li><a href="mithril.deferred.html">m.deferred</a></li>
<li><a href="mithril.sync.html">m.sync</a></li>
<li><a href="mithril.computation.html">m.startComputation / m.endComputation</a></li>
</ul>
<h2 id="archive">History</h2>
<ul>
<li><a href="roadmap.html">Roadmap</a></li>
<li><a href="change-log.html">Change log</a></li>
</ul>
</div>
<div class="col(9,9,12)">
<h2 id="m-trust">m.trust</h2>
<p>If you&#39;re writing a template for a view, use <code>m()</code> instead.</p>
<p>This method flags a string as trusted HTML.</p>
<p>Trusted HTML is allowed to render arbitrary, potentially invalid markup, as well as run arbitrary Javascript, and therefore the developer is responsible for either:</p>
<ul>
<li><p>sanitizing the markup contained in the string, or</p>
</li>
<li><p>acknowledging that the string is authorized to run any code that may be contained within it.</p>
</li>
</ul>
<p>Note that browsers ignore <code>&lt;script&gt;</code> tags that have been inserted into the DOM via innerHTML. They do this because once the element is ready (and thus, has an accessible <code>innerHTML</code> property), their rendering engines cannot backtrack to the parsing-stage if the script calls something like <code>document.write(&quot;&lt;/body&gt;&quot;)</code>.</p>
<p>For this reason, <code>m.trust</code> will not auto-run <code>&lt;script&gt;</code> tags from trusted strings.</p>
<p>Browsers do, however, allow scripts to be run asynchronously via a number of execution points, such as the <code>onload</code> or <code>onerror</code> attributes in <code>&lt;img&gt;</code> and <code>&lt;iframe&gt;</code>.</p>
<p>IE also allows running of Javascript via CSS behaviors in <code>&lt;link&gt;</code>/<code>&lt;style&gt;</code> tags and <code>style</code> attributes.</p>
<p>It&#39;s worth noting that the execution points listed above are commonly used for security attacks in combination with malformed markup, e.g. strings with mismatched attribute quotes like <code>&quot; onload=&quot;alert(1)</code>.</p>
<p>Mithril templates are defended against these attacks by default, except when markup is injected via <code>m.trust</code>.</p>
<p>It is the developer&#39;s responsibility to ensure the input to <code>m.trust</code> cannot be maliciously modified by user-entered data.</p>
<hr>
<h3 id="usage">Usage</h3>
<pre><code class="lang-javascript">//assume this content comes from the server
var content = &quot;&lt;h1&gt;Error: invalid user&lt;/h1&gt;&quot;;
m.render(&quot;body&quot;, [
m(&quot;div&quot;, m.trust(content))
]);</code></pre>
<p>yields:</p>
<pre><code class="lang-markup">&lt;body&gt;
&lt;div&gt;
&lt;h1&gt;Error: invalid user&lt;/h1&gt;
&lt;/div&gt;
&lt;/body&gt;</code></pre>
<hr>
<h3 id="signature">Signature</h3>
<p><a href="how-to-read-signatures.html">How to read signatures</a></p>
<pre><code class="lang-clike">String trust(String html)</code></pre>
<ul>
<li><p><strong>String html</strong></p>
<p>A string containing HTML markup</p>
</li>
<li><p><strong>returns String trustedHtml</strong></p>
<p>The returned string is a String object instance (as opposed to a string primitive) containing the same HTML content, and exposing a flag property for internal use within Mithril. Do not create or manipulate trust flags manually.</p>
<p>Also note that concatenating or splitting a trusted string removes the trust flag. If doing such operations, the final string needs to be flagged as trusted.</p>
</li>
</ul>
</div>
</div>
</div>
</section>
</main>
<footer>
<div class="container">
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
<br />&copy; 2014 Leo Horie
</div>
</footer>
<script src="lib/prism/prism.js"></script>
</body>
</html>

View file

@ -0,0 +1,126 @@
<!doctype html>
<html>
<head>
<title>Mithril</title>
<link href="lib/prism/prism.css" rel="stylesheet" />
<link href="style.css" rel="stylesheet" />
</head>
<body>
<header>
<nav class="container">
<a href="index.html" class="logo"><span>&#9675;</span> Mithril</a>
<a href="getting-started.html">Guide</a>
<a href="mithril.html">API</a>
<a href="community.html">Community</a>
<a href="mithril.min.zip">Download</a>
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
</nav>
</header>
<main>
<section class="content">
<div class="container">
<div class="row">
<div class="col(3,3,12)">
<h2 id="api">API (v0.1.16)</h2>
<h3 id="core">Core</h3>
<ul>
<li><a href="mithril.html">m</a></li>
<li><a href="mithril.prop.html">m.prop</a></li>
<li><a href="mithril.withAttr.html">m.withAttr</a></li>
<li><a href="mithril.module.html">m.module</a></li>
<li><a href="mithril.trust.html">m.trust</a></li>
<li><a href="mithril.render.html">m.render</a></li>
<li><a href="mithril.redraw.html">m.redraw</a></li>
</ul>
<h3 id="routing">Routing</h3>
<ul>
<li><a href="mithril.route.html">m.route</a>
<ul>
<li><a href="mithril.route.html#defining-routes">m.route(rootElement, defaultRoute, routes)</a></li>
<li><a href="mithril.route.html#redirecting">m.route(path, params)</a></li>
<li><a href="mithril.route.html#reading-current-route">m.route()</a></li>
<li><a href="mithril.route.html#mode-abstraction">m.route(element)</a></li>
<li><a href="mithril.route.html#mode">m.route.mode</a></li>
<li><a href="mithril.route.html#param">m.route.param</a></li>
</ul>
</li>
</ul>
<h3 id="data">Data</h3>
<ul>
<li><a href="mithril.request.html">m.request</a></li>
<li><a href="mithril.deferred.html">m.deferred</a></li>
<li><a href="mithril.sync.html">m.sync</a></li>
<li><a href="mithril.computation.html">m.startComputation / m.endComputation</a></li>
</ul>
<h2 id="archive">History</h2>
<ul>
<li><a href="roadmap.html">Roadmap</a></li>
<li><a href="change-log.html">Change log</a></li>
</ul>
</div>
<div class="col(9,9,12)">
<h2 id="m-withattr">m.withAttr</h2>
<p>This is an event handler factory. It returns a method that can be bound to a DOM element&#39;s event listener.</p>
<p>Typically, it&#39;s used in conjunction with <a href="mithril.prop"><code>m.prop</code></a> to implement data binding in the view-to-model direction.</p>
<p>This method is provided to decouple the browser&#39;s event model from the controller/logic model.</p>
<p>You should use this method and implement similar ones when extracting values from a browser&#39;s Event object, instead of hard-coding the extraction code into controllers (or model methods).</p>
<hr>
<h3 id="usage">Usage</h3>
<pre><code class="lang-javascript">//standalone usage
document.body.onclick = m.withAttr(&quot;title&quot;, function(value) {
//alerts the title of the body element when it&#39;s clicked
alert(value);
})</code></pre>
<p>A contrived example of bi-directional data binding</p>
<pre><code class="lang-javascript">var user = {
model: function(name) {
this.name = m.prop(name);
},
controller: function() {
this.user = new user.model(&quot;John Doe&quot;);
},
view: function(controller) {
m.render(&quot;body&quot;, [
m(&quot;input&quot;, {onchange: m.withAttr(&quot;value&quot;, controller.user.name), value: controller.user.name()})
]);
}
};</code></pre>
<hr>
<h3 id="signature">Signature</h3>
<p><a href="how-to-read-signatures.html">How to read signatures</a></p>
<pre><code class="lang-clike">EventHandler withAttr(String property, void callback(any value))
where:
EventHandler :: void handler(Event e)</code></pre>
<ul>
<li><p><strong>String property</strong></p>
<p>Defines the property of the DOM element whose value will be passed to the callback.</p>
</li>
<li><p><strong>void callback(any value)</strong></p>
<p>This function will be called with the value of the defined property as an argument.</p>
<ul>
<li><p><strong>any value</strong></p>
<p>This is the value of the defined DOM element&#39;s property.</p>
</li>
</ul>
</li>
<li><p><strong>returns EventHandler handler</strong></p>
<p>This handler method can be assigned to properties like <code>onclick</code>, or passed as callbacks to <code>addEventListener</code>.</p>
</li>
</ul>
</div>
</div>
</div>
</section>
</main>
<footer>
<div class="container">
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
<br />&copy; 2014 Leo Horie
</div>
</footer>
<script src="lib/prism/prism.js"></script>
</body>
</html>

View file

@ -0,0 +1,77 @@
<!doctype html>
<html>
<head>
<title>Mithril</title>
<link href="lib/prism/prism.css" rel="stylesheet" />
<link href="style.css" rel="stylesheet" />
</head>
<body>
<header>
<nav class="container">
<a href="index.html" class="logo"><span>&#9675;</span> Mithril</a>
<a href="getting-started.html">Guide</a>
<a href="mithril.html">API</a>
<a href="community.html">Community</a>
<a href="mithril.min.zip">Download</a>
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
</nav>
</header>
<main>
<section class="content">
<div class="container">
<div class="row">
<div class="col(3,3,12)">
<h2 id="api">API (v0.1.16)</h2>
<h3 id="core">Core</h3>
<ul>
<li><a href="mithril.html">m</a></li>
<li><a href="mithril.prop.html">m.prop</a></li>
<li><a href="mithril.withAttr.html">m.withAttr</a></li>
<li><a href="mithril.module.html">m.module</a></li>
<li><a href="mithril.trust.html">m.trust</a></li>
<li><a href="mithril.render.html">m.render</a></li>
<li><a href="mithril.redraw.html">m.redraw</a></li>
</ul>
<h3 id="routing">Routing</h3>
<ul>
<li><a href="mithril.route.html">m.route</a>
<ul>
<li><a href="mithril.route.html#defining-routes">m.route(rootElement, defaultRoute, routes)</a></li>
<li><a href="mithril.route.html#redirecting">m.route(path, params)</a></li>
<li><a href="mithril.route.html#reading-current-route">m.route()</a></li>
<li><a href="mithril.route.html#mode-abstraction">m.route(element)</a></li>
<li><a href="mithril.route.html#mode">m.route.mode</a></li>
<li><a href="mithril.route.html#param">m.route.param</a></li>
</ul>
</li>
</ul>
<h3 id="data">Data</h3>
<ul>
<li><a href="mithril.request.html">m.request</a></li>
<li><a href="mithril.deferred.html">m.deferred</a></li>
<li><a href="mithril.sync.html">m.sync</a></li>
<li><a href="mithril.computation.html">m.startComputation / m.endComputation</a></li>
</ul>
<h2 id="archive">History</h2>
<ul>
<li><a href="roadmap.html">Roadmap</a></li>
<li><a href="change-log.html">Change log</a></li>
</ul>
</div>
<div class="col(9,9,12)">
</div>
</div>
</div>
</section>
</main>
<footer>
<div class="container">
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
<br />&copy; 2014 Leo Horie
</div>
</footer>
<script src="lib/prism/prism.js"></script>
</body>
</html>

View file

@ -0,0 +1,11 @@
{
"name": "mithril",
"description": "A Javascript Framework for building brilliant applications",
"keywords": ["mvc", "framework"],
"version": "0.1.16",
"author": "Leo Horie <leohorie@hotmail.com>",
"repository": {"type": "git", "url": "https://github.com/lhorie/mithril"},
"main": "mithril.js",
"licenses": [{"type": "MIT", "url": "http://opensource.org/licenses/MIT"}],
"files": ["mithril.min.js", "mithril.min.map", "mithril.js"]
}

View file

@ -0,0 +1,6 @@
[
{"title": "Getting Started", "url": "getting-started.html"},
{"title": "Documentation", "url": "mithril.html"},
{"title": "Mithril Blog", "url": "http://lhorie.github.io/mithril-blog/"},
{"title": "Mailing List", "url": "https://groups.google.com/forum/#!forum/mithriljs"}
]

View file

@ -0,0 +1,119 @@
<!doctype html>
<html>
<head>
<title>Mithril</title>
<link href="lib/prism/prism.css" rel="stylesheet" />
<link href="style.css" rel="stylesheet" />
</head>
<body>
<header>
<nav class="container">
<a href="index.html" class="logo"><span>&#9675;</span> Mithril</a>
<a href="getting-started.html">Guide</a>
<a href="mithril.html">API</a>
<a href="community.html">Community</a>
<a href="mithril.min.zip">Download</a>
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
</nav>
</header>
<main>
<section class="content">
<div class="container">
<div class="row">
<div class="col(3,3,12)">
<h2 id="core-topics">Core Topics</h2>
<ul>
<li><a href="installation.html">Installation</a></li>
<li><a href="getting-started.html">Getting Started</a></li>
<li><a href="routing.html">Routing</a></li>
<li><a href="web-services.html">Web Services</a></li>
<li><a href="components.html">Components</a></li>
</ul>
<h2 id="advanced-topics.html">Advanced Topics</h2>
<ul>
<li><a href="compiling-templates.html">Compiling Templates</a></li>
<li><a href="auto-redrawing.html">The Auto-Redrawing System</a></li>
<li><a href="integration.html">Integrating with Other Libraries</a></li>
</ul>
<h2 id="misc">Misc</h2>
<ul>
<li><a href="comparison.html">Differences from Other MVC Frameworks</a></li>
<li><a href="benchmarks.html">Benchmarks</a></li>
<li><a href="practices.html">Good Practices</a></li>
<li><a href="tools.html">Useful Tools</a></li>
</ul>
</div>
<div class="col(9,9,12)">
<h2 id="how-should-code-be-organized">How Should Code Be Organized</h2>
<p>While Mithril doesn&#39;t dictate how to organize your code, it does provide some recommendations for structuring it.</p>
<p>As a rule of thumb, controllers should not change model entity properties on an individual basis.</p>
<p>Data manipulation should be done in model classes, such that controllers never have entities lying around in temporarily invalid states.</p>
<p>Mithril&#39;s design strongly encourages all entity logic to be handled in atomic model layer methods (in the sense of entity state stability).</p>
<p>In fact, unavoidable abstraction leaks (such as network-bound asynchrony) are laid out in such a way as to make idiomatic code organization elegant, and conversely, to make it so that the abstraction leak problems themselves discourage attempts to misplace entity logic in the controller.</p>
<p>This design decision comes from experience with <a href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself">DRY</a> and the <a href="http://en.wikipedia.org/wiki/Bus_factor">&quot;bus factor&quot;</a> of large, highly relational model layers.</p>
<p>This is in stark contrast to the ActiveRecord pattern of other frameworks, where model entities are largely object representations of database entities and these entities are manipulated in controllers in an ad-hoc field-by-field fashion, and then &quot;committed&quot; via a <code>save</code> method.</p>
<p>Because Mithril encourages all entity logic to be done in the model layer, it&#39;s idiomatic to create modules with model-level classes that deal specifically with relationships between entities, when there isn&#39;t already a model entity that can logically hold the relational business logic.</p>
<p>Models are also responsible for centralizing tasks such as filtering of entity lists and validation routines, so that access to these methods is available across the application.</p>
<p>DOM manipulation should be done in the view via <a href="mithril"><code>m()</code> and <code>config</code></a>. Controllers may explicitly call <a href="mithril.redraw"><code>m.redraw</code></a>, but, if possible, it&#39;s preferable to abstract this into a service which integrates with Mithril&#39;s auto-redrawing system (see <a href="mithril.computation"><code>m.startComputation</code> / <code>m.endComputation</code></a>).</p>
<hr>
<h2 id="file-separation">File Separation</h2>
<p>The examples in this site usually conflate different MVC layers together for the sake of readability, but normally it&#39;s recommended that each layer on a module be in different files. For example:</p>
<pre><code class="lang-javascript">//app.model.js
var app = {};
app.PageList = function() {
return m.request({method: &quot;GET&quot;, url: &quot;pages.json&quot;});
};</code></pre>
<pre><code class="lang-javascript">//app.controller.js
app.controller = function() {
this.pages = new app.PageList();
};</code></pre>
<pre><code class="lang-javascript">//app.view.js
app.view = function(ctrl) {
return ctrl.pages().map(function(page) {
return m(&quot;a&quot;, {href: page.url}, page.title);
});
};</code></pre>
<p>You can use task automation tools such as GruntJS to concatenate the files back together for production.</p>
<p>Typically, when separating MVC layers, it&#39;s common that the namespace declaration be in the model layer, since this is usually the most used dependency for the other layers.</p>
<p>You may choose to declare the namespace in a separate file or have the build system generate it on demand, instead.</p>
<p>You should avoid grouping classes by the MVC layer they belong to, i.e. don&#39;t create three files called model.js, controllers.js and views.js.</p>
<p>That organization pattern needlessly ties unrelated aspects of the application together and dilutes the clarity of modules.</p>
<hr>
<h2 id="global-namespace-hygiene">Global Namespace Hygiene</h2>
<p>For developer convenience, Mithril uses the global <code>m</code> variable as a namespace, much like jQuery uses <code>$</code>.</p>
<p>If you want to ensure global namespace hygiene, you can wrap your code in &quot;islands&quot; like this:</p>
<pre><code class="lang-javascript">new function(m) {
//your code goes here
}(Mithril);</code></pre>
<p>If you are creating components to be used by 3rd parties, it&#39;s recommended that you always use this idiom.</p>
<p>In the unlikely case that you have another global variable called <code>m</code> in your page, you should consider renaming it to make it more descriptive, or use the idiom below to keep it intact.</p>
<pre><code class="lang-markup">&lt;script&gt;_temp = m&lt;/script&gt;
&lt;script src=&quot;mithril.js&quot;&gt;&lt;/script&gt;
&lt;script&gt;m = _temp&lt;/script&gt;</code></pre>
<hr>
<h2 id="usage-of-m-redraw">Usage of m.redraw</h2>
<p><code>m.redraw</code> is a method that allows you to render a template outside the scope of Mithril&#39;s auto-redrawing system.</p>
<p>Calling this method while using <code>m.module</code> or <code>m.route</code> should only be done if you have recurring asynchronous view updates (i.e. something that uses setInterval).</p>
<p>If you&#39;re integrating other non-recurring services (e.g. calling setTimeout), you should use <a href="mithril.computation.html"><code>m.startComputation</code> / <code>m.emdComputation</code></a> instead.</p>
<p>This is the most potentially expensive method in Mithril and should not be used at a rate faster than the rate at which the native <code>requestAnimationFrame</code> method fires (i.e. the rate at which browsers are comfortable calling recurring rendering-intensive code). Typically, this rate is around 60 calls per second.</p>
<p>If you call this method more often than that, Mithril may ignore calls or defer them to the next browser repaint cycle.</p>
<p>If calls are more expensive than a repaint window, the browser may drop frames, resulting in choppy animations. It&#39;s your responsibility to make sure single iterations of animation rendering code don&#39;t take longer than 16ms (for a frequency of 60 frames-per-second).</p>
<p>In addition, note that template performance, both in Mithril templates as well as in general, is dependent on markup complexity. You are responsible for ensuring that templates aren&#39;t too big to render efficiently.</p>
</div>
</div>
</div>
</section>
</main>
<footer>
<div class="container">
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
<br />&copy; 2014 Leo Horie
</div>
</footer>
<script src="lib/prism/prism.js"></script>
</body>
</html>

View file

@ -0,0 +1,64 @@
<!doctype html>
<html>
<head>
<title>Mithril</title>
<link href="lib/prism/prism.css" rel="stylesheet" />
<link href="style.css" rel="stylesheet" />
</head>
<body>
<header>
<nav class="container">
<a href="index.html" class="logo"><span>&#9675;</span> Mithril</a>
<a href="getting-started.html">Guide</a>
<a href="mithril.html">API</a>
<a href="community.html">Community</a>
<a href="mithril.min.zip">Download</a>
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
</nav>
</header>
<main>
<section class="content">
<div class="container">
<div class="row">
<div class="col(3,3,12)">
<h2 id="core-topics">Core Topics</h2>
<ul>
<li><a href="installation.html">Installation</a></li>
<li><a href="getting-started.html">Getting Started</a></li>
<li><a href="routing.html">Routing</a></li>
<li><a href="web-services.html">Web Services</a></li>
<li><a href="components.html">Components</a></li>
</ul>
<h2 id="advanced-topics.html">Advanced Topics</h2>
<ul>
<li><a href="compiling-templates.html">Compiling Templates</a></li>
<li><a href="auto-redrawing.html">The Auto-Redrawing System</a></li>
<li><a href="integration.html">Integrating with Other Libraries</a></li>
</ul>
<h2 id="misc">Misc</h2>
<ul>
<li><a href="comparison.html">Differences from Other MVC Frameworks</a></li>
<li><a href="benchmarks.html">Benchmarks</a></li>
<li><a href="practices.html">Good Practices</a></li>
<li><a href="tools.html">Useful Tools</a></li>
</ul>
</div>
<div class="col(9,9,12)">
<h2 id="refactoring">Refactoring</h2>
<p>Below are some common refactoring patterns:</p>
<h3 id="porting-legacy-code-to-mithril">Porting legacy code to Mithril</h3>
</div>
</div>
</div>
</section>
</main>
<footer>
<div class="container">
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
<br />&copy; 2014 Leo Horie
</div>
</footer>
<script src="lib/prism/prism.js"></script>
</body>
</html>

View file

@ -0,0 +1,104 @@
<!doctype html>
<html>
<head>
<title>Mithril</title>
<link href="lib/prism/prism.css" rel="stylesheet" />
<link href="style.css" rel="stylesheet" />
</head>
<body>
<header>
<nav class="container">
<a href="index.html" class="logo"><span>&#9675;</span> Mithril</a>
<a href="getting-started.html">Guide</a>
<a href="mithril.html">API</a>
<a href="community.html">Community</a>
<a href="mithril.min.zip">Download</a>
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
</nav>
</header>
<main>
<section class="content">
<div class="container">
<div class="row">
<div class="col(3,3,12)">
<h2 id="api">API (v0.1.16)</h2>
<h3 id="core">Core</h3>
<ul>
<li><a href="mithril.html">m</a></li>
<li><a href="mithril.prop.html">m.prop</a></li>
<li><a href="mithril.withAttr.html">m.withAttr</a></li>
<li><a href="mithril.module.html">m.module</a></li>
<li><a href="mithril.trust.html">m.trust</a></li>
<li><a href="mithril.render.html">m.render</a></li>
<li><a href="mithril.redraw.html">m.redraw</a></li>
</ul>
<h3 id="routing">Routing</h3>
<ul>
<li><a href="mithril.route.html">m.route</a>
<ul>
<li><a href="mithril.route.html#defining-routes">m.route(rootElement, defaultRoute, routes)</a></li>
<li><a href="mithril.route.html#redirecting">m.route(path, params)</a></li>
<li><a href="mithril.route.html#reading-current-route">m.route()</a></li>
<li><a href="mithril.route.html#mode-abstraction">m.route(element)</a></li>
<li><a href="mithril.route.html#mode">m.route.mode</a></li>
<li><a href="mithril.route.html#param">m.route.param</a></li>
</ul>
</li>
</ul>
<h3 id="data">Data</h3>
<ul>
<li><a href="mithril.request.html">m.request</a></li>
<li><a href="mithril.deferred.html">m.deferred</a></li>
<li><a href="mithril.sync.html">m.sync</a></li>
<li><a href="mithril.computation.html">m.startComputation / m.endComputation</a></li>
</ul>
<h2 id="archive">History</h2>
<ul>
<li><a href="roadmap.html">Roadmap</a></li>
<li><a href="change-log.html">Change log</a></li>
</ul>
</div>
<div class="col(9,9,12)">
<h2 id="roadmap">Roadmap</h2>
<p>Things that would be useful to have (though likely not as part of Mithril core)</p>
<h3 id="utilities">Utilities</h3>
<ul>
<li>Formatters / parsers<ul>
<li>i18n</li>
<li>Date<ul>
<li>Absolute (e.g. Jan 1, 1970 12:00 AM)</li>
<li>Relative (e.g. 10 days ago)</li>
</ul>
</li>
<li>Number (e.g. 1,234.5)</li>
<li>Currency (e.g. $1,000.00)</li>
<li>Word wrap</li>
</ul>
</li>
<li>Dependency management</li>
<li>Functional / relational tools</li>
<li>Animation</li>
</ul>
<h3 id="components">Components</h3>
<ul>
<li>Autocompleter</li>
<li>Date/time picker</li>
<li>Swipe-to-show panel</li>
<li>Tree</li>
</ul>
</div>
</div>
</div>
</section>
</main>
<footer>
<div class="container">
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
<br />&copy; 2014 Leo Horie
</div>
</footer>
<script src="lib/prism/prism.js"></script>
</body>
</html>

View file

@ -0,0 +1,128 @@
<!doctype html>
<html>
<head>
<title>Mithril</title>
<link href="lib/prism/prism.css" rel="stylesheet" />
<link href="style.css" rel="stylesheet" />
</head>
<body>
<header>
<nav class="container">
<a href="index.html" class="logo"><span>&#9675;</span> Mithril</a>
<a href="getting-started.html">Guide</a>
<a href="mithril.html">API</a>
<a href="community.html">Community</a>
<a href="mithril.min.zip">Download</a>
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
</nav>
</header>
<main>
<section class="content">
<div class="container">
<div class="row">
<div class="col(3,3,12)">
<h2 id="core-topics">Core Topics</h2>
<ul>
<li><a href="installation.html">Installation</a></li>
<li><a href="getting-started.html">Getting Started</a></li>
<li><a href="routing.html">Routing</a></li>
<li><a href="web-services.html">Web Services</a></li>
<li><a href="components.html">Components</a></li>
</ul>
<h2 id="advanced-topics.html">Advanced Topics</h2>
<ul>
<li><a href="compiling-templates.html">Compiling Templates</a></li>
<li><a href="auto-redrawing.html">The Auto-Redrawing System</a></li>
<li><a href="integration.html">Integrating with Other Libraries</a></li>
</ul>
<h2 id="misc">Misc</h2>
<ul>
<li><a href="comparison.html">Differences from Other MVC Frameworks</a></li>
<li><a href="benchmarks.html">Benchmarks</a></li>
<li><a href="practices.html">Good Practices</a></li>
<li><a href="tools.html">Useful Tools</a></li>
</ul>
</div>
<div class="col(9,9,12)">
<h2 id="routing">Routing</h2>
<p>Routing is a system that allows creating Single-Page-Applications (SPA), i.e. applications that can go from one page to another without causing a full browser refresh.</p>
<p>It enables seamless navigability while preserving the ability to bookmark each page individually, and the ability to navigate the application via the browser&#39;s history mechanism.</p>
<p>Mithril provides utilities to handle three different aspect of routing:</p>
<ul>
<li>defining a list of routes</li>
<li>programmatically redirecting between routes</li>
<li>making links in templates routed transparently and unobtrusively</li>
</ul>
<hr>
<h3 id="defining-routes">Defining routes</h3>
<p>To define a list of routes, you need to specify a host DOM element, a default route and a key-value map of possible routes and respective <a href="mithril.module.html">modules</a> to be rendered.</p>
<p>The example below defines three routes, to be rendered in <code>&lt;body&gt;</code>. <code>home</code>, <code>login</code> and <code>dashboard</code> are modules. We&#39;ll see how to define a module in a bit.</p>
<pre><code class="lang-javascript">m.route(document.body, &quot;/&quot;, {
&quot;/&quot;: home,
&quot;/login&quot;: login,
&quot;/dashboard&quot;: dashboard,
});</code></pre>
<p>Routes can take arguments, by prefixing words with a colon <code>:</code>.</p>
<p>The example below shows a route that takes a <code>userID</code> parameter.</p>
<pre><code class="lang-javascript">//a sample module
var dashboard = {
controller: function() {
this.id = m.route.param(&quot;userID&quot;);
},
view: function(controller) {
return m(&quot;div&quot;, controller.id);
}
}
//define a route
m.route(document.body, &quot;/dashboard/johndoe&quot;, {
&quot;/dashboard/:userID&quot;: dashboard
});
//setup routes to start w/ the `#` symbol
m.route.mode = &quot;hash&quot;;</code></pre>
<p>This redirects to the URL <code>http://server/#/dashboard/johndoe</code> and yields:</p>
<pre><code class="lang-markup">&lt;body&gt;johndoe&lt;/body&gt;</code></pre>
<p>Above, <code>dashboard</code> is a module. It contains <code>controller</code> and <code>view</code> properties. When the URL matches a route, the respective module&#39;s controller is instantiated and passed as a parameter to the view.</p>
<p>In this case, since there&#39;s only one route, the app redirects to the default route <code>&quot;/dashboard/johndoe&quot;</code> and, under the hood, it calls <code>m.module(document.body, dashboard)</code>.</p>
<p>The string <code>johndoe</code> is bound to the <code>:userID</code> parameter, which can be retrieved programmatically in the controller via <code>m.route.param(&quot;userID&quot;)</code>.</p>
<p>The <code>m.route.mode</code> property defines which URL portion is used to implement the routing mechanism. Its value can be set to either &quot;search&quot;, &quot;hash&quot; or &quot;pathname&quot;. The default value is &quot;search&quot;.</p>
<ul>
<li><p><code>search</code> mode uses the querystring. This allows named anchors (i.e. <code>&lt;a href=&quot;#top&quot;&gt;Back to top&lt;/a&gt;</code>, <code>&lt;a name=&quot;top&quot;&gt;&lt;/a&gt;</code>) to work on the page, but routing changes causes page refreshes in IE8, due to its lack of support for <code>history.pushState</code>.</p>
<p>Example URL: <code>http://server/?/path/to/page</code></p>
</li>
<li><p><code>hash</code> mode uses the hash. It&#39;s the only mode in which routing changes do not cause page refreshes in any browser. However, this mode does not support named anchors and browser history lists.</p>
<p>Example URL: <code>http://server/#/path/to/page</code></p>
</li>
<li><p><code>pathname</code> mode allows routing URLs that contain no special characters, however this mode requires server-side setup in order to support bookmarking and page refreshes. It also causes page refreshes in IE8.</p>
<p>Example URL: <code>http://server/path/to/page</code></p>
<p>The simplest server-side setup possible to support pathname mode is to serve the same content regardless of what URL is requested. In Apache, this URL rewriting can be achieved using <a href="https://httpd.apache.org/docs/current/mod/mod_rewrite.html">mod_rewrite</a>.</p>
</li>
</ul>
<hr>
<h3 id="redirecting">Redirecting</h3>
<p>You can programmatically redirect to another page. Given the example in the &quot;Defining Routes&quot; section:</p>
<pre><code class="lang-javascript">m.route(&quot;/dashboard/marysue&quot;);</code></pre>
<p>redirects to <code>http://server/#/dashboard/marysue</code></p>
<hr>
<h3 id="mode-abstraction">Mode abstraction</h3>
<p>This method is meant to be used with a virtual element&#39;s <code>config</code> attribute. For example:</p>
<pre><code class="lang-javascript">//Note that the &#39;#&#39; is not required in `href`, thanks to the `config` setting.
m(&quot;a[href=&#39;/dashboard/alicesmith&#39;]&quot;, {config: m.route});</code></pre>
<p>This makes the href behave correctly regardless of which <code>m.route.mode</code> is selected. It&#39;s a good practice to always use the idiom above, instead of hardcoding <code>?</code> or <code>#</code> in the href attribute.</p>
<p>See <a href="mithril.html"><code>m()</code></a> for more information on virtual elements.</p>
</div>
</div>
</div>
</section>
</main>
<footer>
<div class="container">
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
<br />&copy; 2014 Leo Horie
</div>
</footer>
<script src="lib/prism/prism.js"></script>
</body>
</html>

92
archive/v0.1.16/style.css Normal file
View file

@ -0,0 +1,92 @@
.container {margin:auto;max-width:1000px;padding:0 20px;position:relative;}
.container:after,.row:after {content:"";display:table;clear:both;}
.container,.row,[class*='col('] {-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;}
[class*='col('] {float:left;}
[class*='(3,'] {width:25%;}
[class*='(4,'] {width:33.33333%;}
[class*='(6,'] {width:50%;}
[class*='(8,'] {width:66.66667%;}
[class*='(9,'] {width:75%;}
@media (max-width:1000px) {
[class*=',3,'] {width:25%;}
[class*=',4,'] {width:33.33333%;}
[class*=',6,'] {width:50%;}
[class*=',8,'] {width:66.66667%;}
[class*=',9,'] {width:75%;}
}
@media (max-width:750px) {
[class*=',6)'] {width:50%;}
[class*=',12)'] {width:100%;}
}
html {background:#999;color:#222;font:14px Helvetica;}
html,body {margin:0;padding:0;}
header,footer {background:#999;}
nav {text-align:right;}
nav a:first-child,nav a:first-child:visited {color:#fff;font-size:27px;float:left;line-height:1.3em;padding:0;text-decoration:none;}
nav a {color:#fff;display:inline-block;padding:10px;}
nav a:visited {color:#ddd;}
footer {text-align:center;padding:10px 0;}
footer,footer a,footer a:visited {color:#fff;}
h1,h2 {font-family:Verdana;margin:0 0 10px;}
h1 {font-size:3em;}
h1 span {animation:logo 2s;display:inline-block;-webkit-animation:logo 2s;}
h2 {color:#888;font-weight:normal;}
h3 {margin:10px 0;}
p {margin:15px 0;}
ul {margin:15px 0;padding:0 0 0 1em;}
li {margin:0 0 10px;}
a {color:#161;}
a:visited {color:#383;}
a:hover {text-decoration:none;}
pre {background:#ffe;border:1px solid #ddd;overflow:auto;margin:0 0 15px;padding:5px 10px;white-space:pre;-webkit-overflow-scrolling:touch;}
pre[class*="language-"],code {background:#ffe;font:12px/15px Lucida Console,Monaco,monospace;}
hr {border-top:1px solid #ccc;border-width:1px 0 0;margin:20px 0;}
table {margin:0 0 10px;width:100%;}
.cta {padding:30px 0 20px;text-align:center;}
.cta {
background:
linear-gradient(27deg, #e5e5e5 5px, rgba(255,255,255,0) 5px) 0 5px,
linear-gradient(207deg, #e5e5e5 5px, rgba(255,255,255,0) 5px) 10px 0px,
linear-gradient(27deg, #f2f2f2 5px, rgba(255,255,255,0) 5px) 0px 10px,
linear-gradient(207deg, #f2f2f2 5px, rgba(255,255,255,0) 5px) 10px 5px,
linear-gradient(90deg, #ebebeb 10px, rgba(255,255,255,0) 10px),
linear-gradient(#ededed 25%, #eaeaea 25%, #eaeaea 50%, rgba(255,255,255,0) 50%, rgba(255,255,255,0) 75%, #f4f4f4 75%, #f4f4f4);
background-color: #e3e3e3;
background-size: 20px 20px;
}
.logo {color:#5a5;font-family:Georgia;font-style:italic;}
.logo span {font-family:Arial;font-style:normal;}
.logo :before {content:"\25CB";position:absolute;margin:-0.17em 0 0 -0.10em;}
.logo :after {content:"\25CB";position:absolute;margin:-0.17em 0 0 -0.5em;}
.button,.button:visited {background:#5a5;color:#fff;display:inline-block;font:normal bold 16px Helvetica;margin:0 10px 10px;padding:10px 30px;text-decoration:none;}
.features {background:#fff;padding:30px 0 0;}
.feature {margin:0 0 30px;padding:0 20px 0 0;}
.sample {background:#f5f5f5;padding:30px 0 10px;}
.example {background:#ffe;border:1px solid #ddd;display:block;font:Courier New;margin-bottom:20px;padding:5px 10px;}
.example span {color:#383;font-weight:bold;}
.example small {color:#888;font-size:1em;}
.more {background:#ddd;padding:30px 0;}
.output a,.more a {display:block;margin:0 0 10px;}
.output a:after,.more a:after {content:" \bb";}
.performance,.socialmedia {background:#fff;padding:30px 0;}
.performance td:first-child {text-align:right;width:1%;}
.bar {background:red;height:4px;float:left;margin:0.5em 1em 0 0;}
.security {background:#f5f5f5;padding:30px 0;}
.success {color:#383;}
.callout {background:#84db84;padding:1em;}
.error {color:#f00;}
.content {background:#f5f5f5;padding:30px 0;}
@media (min-width:750px) {
.sample pre {margin-right:20px;}
}
@keyframes logo {
from {opacity:0;transform:scale(2) rotate(359deg);}
to {opacity:1;transform:scale(1) rotate(0deg);}
}
@-webkit-keyframes logo {
from {opacity:0;-webkit-transform:scale(2) rotate(359deg);}
to {opacity:1;-webkit-transform:scale(1) rotate(0deg);}
}

102
archive/v0.1.16/tools.html Normal file
View file

@ -0,0 +1,102 @@
<!doctype html>
<html>
<head>
<title>Mithril</title>
<link href="lib/prism/prism.css" rel="stylesheet" />
<link href="style.css" rel="stylesheet" />
</head>
<body>
<header>
<nav class="container">
<a href="index.html" class="logo"><span>&#9675;</span> Mithril</a>
<a href="getting-started.html">Guide</a>
<a href="mithril.html">API</a>
<a href="community.html">Community</a>
<a href="mithril.min.zip">Download</a>
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
</nav>
</header>
<main>
<section class="content">
<div class="container">
<div class="row">
<div class="col(3,3,12)">
<h2 id="core-topics">Core Topics</h2>
<ul>
<li><a href="installation.html">Installation</a></li>
<li><a href="getting-started.html">Getting Started</a></li>
<li><a href="routing.html">Routing</a></li>
<li><a href="web-services.html">Web Services</a></li>
<li><a href="components.html">Components</a></li>
</ul>
<h2 id="advanced-topics.html">Advanced Topics</h2>
<ul>
<li><a href="compiling-templates.html">Compiling Templates</a></li>
<li><a href="auto-redrawing.html">The Auto-Redrawing System</a></li>
<li><a href="integration.html">Integrating with Other Libraries</a></li>
</ul>
<h2 id="misc">Misc</h2>
<ul>
<li><a href="comparison.html">Differences from Other MVC Frameworks</a></li>
<li><a href="benchmarks.html">Benchmarks</a></li>
<li><a href="practices.html">Good Practices</a></li>
<li><a href="tools.html">Useful Tools</a></li>
</ul>
</div>
<div class="col(9,9,12)">
<h2 id="tools">Tools</h2>
<h3 id="html-to-mithril-template-converter">HTML-to-Mithril Template Converter</h3>
<p>If you already have your HTML written and want to convert it into a Mithril template, you can use the tool below for one-off manual conversion.</p>
<p><a href="tools/template-converter.html">Template Converter</a></p>
<hr>
<h3 id="automatic-html-to-mithril-template-converter">Automatic HTML-to-Mithril Template Converter</h3>
<p>There&#39;s a tool called <a href="https://github.com/insin/msx">MSX by Jonathan Buchanan</a> that allows you to write templates using HTML syntax, and then automatically compile them to Javascript when files change.</p>
<p>It is useful for teams where styling and functionality are done by different people, and for those who prefer to maintain templates in HTML syntax.</p>
<p>The tool allows you to write code like this:</p>
<pre><code class="lang-javascript">todo.view = function(ctrl) {
return &lt;html&gt;
&lt;body&gt;
&lt;input onchange={m.withAttr(&quot;value&quot;, ctrl.description)} value={ctrl.description()}/&gt;
&lt;button onclick={ctrl.add.bind(ctrl, ctrl.description)}&gt;Add&lt;/button&gt;
&lt;/body&gt;
&lt;/html&gt;
};</code></pre>
<p>Note, however, that since the code above is not valid Javascript, this syntax can only be used with a preprocessor build tool such as the provided <a href="http://gulpjs.com">Gulp.js</a> script.</p>
<p>This tool is also available as a <a href="https://github.com/mrsweaters/mithril-rails">Rails gem</a>, created by Jordan Humphreys.</p>
<hr>
<h3 id="mithril-template-compiler">Mithril Template Compiler</h3>
<p>You can pre-compile Mithril templates to make them run faster. For more information see this page:</p>
<p><a href="compiling-templates.html">Compiling Templates</a></p>
<hr>
<h3 id="typescript-support">Typescript Support</h3>
<p>There&#39;s a type definition file that you can use to add Mithril support to Typescript</p>
<p><a href="mithril.d.ts">mithril.d.ts</a></p>
<p>You can use it by adding a reference to your Typescript files. This will allow the compiler to type-check calls to the Mithril API.</p>
<pre><code class="lang-javascript">/// &lt;reference path=&quot;mithril.d.ts&quot; /&gt;</code></pre>
<hr>
<h3 id="internet-explorer-compatibility">Internet Explorer Compatibility</h3>
<p>Mithril relies on some Ecmascript 5 features, namely: <code>Array::indexOf</code> and <code>Object::keys</code>, as well as the <code>JSON</code> object.</p>
<p>You can use polyfill libraries to support these features in IE7.</p>
<ul>
<li><p><a href="https://github.com/es-shims/es5-shim">ES5 Shim</a> or Mozilla.org&#39;s <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf">Array::indexOf</a> and <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys">Object::keys</a> polyfills</p>
</li>
<li><p><a href="https://github.com/douglascrockford/JSON-js/blob/master/json2.js">JSON2.js</a></p>
</li>
</ul>
<p>Mithril also has a dependency on XMLHttpRequest. If you wish to support IE6, you&#39;ll need <a href="https://gist.github.com/Contra/2709462">a shim for it</a>. IE7 and lower do not support cross-domain AJAX requests.</p>
<p>In addition, note that most <code>m.route</code> modes rely on <code>history.pushState</code> in order to allow moving from one page to another without a browser refresh. <a href="http://caniuse.com/#search=pushstate">IE9 and lower</a> do not support this feature and will gracefully degrade to page refreshes instead.</p>
</div>
</div>
</div>
</section>
</main>
<footer>
<div class="container">
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
<br />&copy; 2014 Leo Horie
</div>
</footer>
<script src="lib/prism/prism.js"></script>
</body>
</html>

View file

@ -0,0 +1,64 @@
/*
Compiles Mithril templates
Requires sweet.js (https://github.com/mozilla/sweet.js)
Installation: npm install -g sweet.js
Usage: sjs --module /mithril.compile.sjs --output <output-filename>.js <input-filename>.js
*/
macro m {
case { _ ($selector) } => {
return #{m($selector, {}, [])};
}
case { _ ($selector, $partial) } => {
var partialSyntax = #{$partial};
var partial = unwrapSyntax(partialSyntax);
return partial.value == "{}" ? #{m($selector, $partial, [])} : #{m($selector, {}, partial)};
}
case { _ ($selector, $dynAttrs, $children) } => {
var selectorSyntax = #{$selector};
var selector = unwrapSyntax(selectorSyntax);
var dynAttrsSyntax = #{$dynAttrs};
var dynAttrs = unwrapSyntax(dynAttrsSyntax);
var parser = /(?:(^|#|\.)([^#\.\[\]]+))|(\[.+?\])/g;
var attrParser = /\[(.+?)=("|'|)(.+?)\2\]/;
var _match = null;
var classes = [];
var cell = {tag: "div", attrs: {}, children: []};
while (_match = parser.exec(selector)) {
if (_match[1] == "") cell.tag = _match[2];
else if (_match[1] == "#") cell.attrs.id = _match[2];
else if (_match[1] == ".") classes.push(_match[2]);
else if (_match[3][0] == "[") {
var pair = attrParser.exec(_match[3]);
cell.attrs[pair[1]] = pair[3];
}
}
if (classes.length > 0) cell.attrs["class"] = classes.join(" ");
var tag = makeValue(cell.tag, #{here});
var attrsBody = Object.keys(cell.attrs).reduce(function(memo, attrName) {
return memo.concat([
makeValue(attrName, #{here}),
makePunc(":", #{here}),
makeValue(cell.attrs[attrName], #{here}),
makePunc(",", #{here})
]);
}, []).concat(dynAttrs.inner);
var attrs = [makeDelim("{}", attrsBody, #{here})];
var children = cell.children.map(function(child) {
return makeValue(child, #{here});
})
letstx $tag = [tag], $attrs = attrs;
return #{ ({tag: $tag, attrs: $attrs , children: $children}) };
}
case { _ } => {
return #{Mithril};
}
}
export m;

View file

@ -0,0 +1,9 @@
<p>If you already have your HTML written and want to convert it into a Mithril template, paste the HTML below and press the "Convert" button.</p>
<div id="converter"></div>
<script src="../mithril.min.js"></script>
<script src="template-converter.js"></script>
<script>
m.module(document.getElementById("converter"), templateConverter);
</script>

View file

@ -0,0 +1,89 @@
var templateConverter = {};
templateConverter.DOMFragment = function(markup) {
if (markup.indexOf("<!doctype") > -1) return [new DOMParser().parseFromString(markup, "text/html").childNodes[1]]
var container = document.createElement("div");
container.insertAdjacentHTML("beforeend", markup);
return container.childNodes;
}
templateConverter.VirtualFragment = function recurse(domFragment) {
var virtualFragment = [];
for (var i = 0, el; el = domFragment[i]; i++) {
if (el.nodeType == 3) {
virtualFragment.push(el.nodeValue);
}
else {
var attrs = {};
for (var j = 0, attr; attr = el.attributes[j]; j++) {
attrs[attr.name] = attr.value;
}
virtualFragment.push({tag: el.tagName.toLowerCase(), attrs: attrs, children: recurse(el.childNodes)});
}
}
return virtualFragment;
}
templateConverter.Template = function recurse() {
if (Object.prototype.toString.call(arguments[0]) == "[object String]") {
return new recurse(new templateConverter.VirtualFragment(new templateConverter.DOMFragment(arguments[0])));
}
var virtualFragment = arguments[0], level = arguments[1]
if (!level) level = 1;
var tab = "\n" + new Array(level + 1).join("\t");
var virtuals = [];
for (var i = 0, el; el = virtualFragment[i]; i++) {
if (typeof el == "string") {
if (el.match(/\t| {2,}/g) && el.trim().length == 0) virtuals.indented = true;
else virtuals.push('"' + el.replace(/"/g, '\\"').replace(/\r/g, "\\r").replace(/\n/g, "\\n") + '"');
}
else {
var virtual = "";
if (el.tag != "div") virtual += el.tag;
if (el.attrs["class"]) {
virtual += "." + el.attrs["class"].replace(/\t+/g, " ").split(" ").join(".");
delete el.attrs["class"];
}
var attrNames = Object.keys(el.attrs).sort()
for (var j = 0, attrName; attrName = attrNames[j]; j++) {
if (attrName != "style") virtual += "[" + attrName + "='" + el.attrs[attrName].replace(/'/g, "\\'") + "']";
}
virtual = '"' + virtual + '"';
var style = ""
if (el.attrs.style) {
virtual += ", {style: " + ("{\"" + el.attrs.style.replace(/:/g, "\": \"").replace(/;/g, "\", \"") + "}").replace(/, "}|"}/, "}") + "}"
}
if (el.children.length > 0) {
virtual += ", " + recurse(el.children, level + 1);
}
virtual = "m(" + virtual + ")";
virtuals.push(virtual);
}
}
if (!virtuals.indented) tab = "";
var isInline = virtuals.length == 1 && virtuals[0].charAt(0) == '"';
var template = isInline ? virtuals.join(", ") : "[" + tab + virtuals.join("," + tab) + tab.slice(0, -1) + "]";
return new String(template);
}
templateConverter.controller = function() {
this.source = m.prop("");
this.output = m.prop("");
this.convert = function() {
return this.output(new templateConverter.Template(this.source()));
};
};
templateConverter.view = function(ctrl) {
return [
m("textarea", {autofocus: true, style: {width:"100%", height: "40%"}, onchange: m.withAttr("value", ctrl.source)}, ctrl.source()),
m("button", {onclick: ctrl.convert.bind(ctrl)}, "Convert"),
m("textarea", {style: {width:"100%", height: "40%"}}, ctrl.output())
];
};

View file

@ -0,0 +1,218 @@
<!doctype html>
<html>
<head>
<title>Mithril</title>
<link href="lib/prism/prism.css" rel="stylesheet" />
<link href="style.css" rel="stylesheet" />
</head>
<body>
<header>
<nav class="container">
<a href="index.html" class="logo"><span>&#9675;</span> Mithril</a>
<a href="getting-started.html">Guide</a>
<a href="mithril.html">API</a>
<a href="community.html">Community</a>
<a href="mithril.min.zip">Download</a>
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
</nav>
</header>
<main>
<section class="content">
<div class="container">
<div class="row">
<div class="col(3,3,12)">
<h2 id="core-topics">Core Topics</h2>
<ul>
<li><a href="installation.html">Installation</a></li>
<li><a href="getting-started.html">Getting Started</a></li>
<li><a href="routing.html">Routing</a></li>
<li><a href="web-services.html">Web Services</a></li>
<li><a href="components.html">Components</a></li>
</ul>
<h2 id="advanced-topics.html">Advanced Topics</h2>
<ul>
<li><a href="compiling-templates.html">Compiling Templates</a></li>
<li><a href="auto-redrawing.html">The Auto-Redrawing System</a></li>
<li><a href="integration.html">Integrating with Other Libraries</a></li>
</ul>
<h2 id="misc">Misc</h2>
<ul>
<li><a href="comparison.html">Differences from Other MVC Frameworks</a></li>
<li><a href="benchmarks.html">Benchmarks</a></li>
<li><a href="practices.html">Good Practices</a></li>
<li><a href="tools.html">Useful Tools</a></li>
</ul>
</div>
<div class="col(9,9,12)">
<h2 id="web-services">Web Services</h2>
<p>Mithril provides a high-level utility for working with web services, which allows writing asynchronous code relatively procedurally.</p>
<p>It provides a number of useful features out of the box:</p>
<ul>
<li>The ability to get an early reference to a container that will hold the asynchronous response</li>
<li>The ability to queue operations to be performed after the asynchronous request completes</li>
<li>The ability to &quot;cast&quot; the response to a class of your choice</li>
<li>The ability to unwrap data in a response that includes metadata properties</li>
</ul>
<hr>
<h3 id="basic-usage">Basic usage</h3>
<p>The basic usage pattern for <code>m.request</code> returns an <a href="mithril.prop.html"><code>m.prop</code></a> getter-setter, which is populated when the AJAX request completes.</p>
<p>The returned getter-setter can be thought of as a box: you can pass this reference around cheaply, and you can &quot;unwrap&quot; its value when needed.</p>
<pre><code class="lang-javascript">var users = m.request({method: &quot;GET&quot;, url: &quot;/user&quot;});
//assuming the response contains the following data: `[{name: &quot;John&quot;}, {name: &quot;Mary&quot;}]`
//then when resolved (e.g. in a view), the `users` getter-setter will contain a list of users
//i.e. users() //[{name: &quot;John&quot;}, {name: &quot;Mary&quot;}]</code></pre>
<p>Note that this getter-setter holds an <em>undefined</em> value until the AJAX request completes. Attempting to unwrap its value early will likely result in errors.</p>
<p>The returned getter-setter also implements the <a href="mithril.deferred.html">promise</a> interface (also known as a <em>thennable</em>): this is the mechanism you should always use to queue operations to be performed on the data from the web service.</p>
<p>The simplest use case of this feature is to implement functional value assignment via <code>m.prop</code> (i.e. the same thing as above). You can bind a pre-existing getter-setter by passing it in as a parameter to a <code>.then</code> method:</p>
<pre><code class="lang-javascript">var users = m.prop([]); //default value
m.request({method: &quot;GET&quot;, url: &quot;/user&quot;}).then(users)
//assuming the response contains the following data: `[{name: &quot;John&quot;}, {name: &quot;Mary&quot;}]`
//then when resolved (e.g. in a view), the `users` getter-setter will contain a list of users
//i.e. users() //[{name: &quot;John&quot;}, {name: &quot;Mary&quot;}]</code></pre>
<p>This syntax allows you to bind intermediate results before piping them down for further processing, for example:</p>
<pre><code class="lang-javascript">var users = m.prop([]); //default value
var doSomething = function() { /*...*/ }
m.request({method: &quot;GET&quot;, url: &quot;/user&quot;}).then(users).then(doSomething)</code></pre>
<p>While both basic assignment syntax and thennable syntax can be used to the same effect, typically it&#39;s recommended that you use the assignment syntax in the first example whenever possible, as it&#39;s easier to read.</p>
<p>The thennable mechanism is intended to be used in three ways:</p>
<ul>
<li>In the model layer: to process web service data in transformative ways (e.g. filtering a list based on a parameter that the web service doesn&#39;t support)</li>
<li>In the controller layer: to bind redirection code upon a condition</li>
<li>In the controller layer: to bind error messages</li>
</ul>
<h4 id="processing-web-service-data">Processing web service data</h4>
<p>This step is meant to be done in the model layer. Doing it in the controller level is also possible, but philosophically not recommended, because by tying logic to a controller, the code becomes harder to reuse due to unrelated controller dependencies.</p>
<p>In the example below, the <code>listEven</code> method returns a getter-setter that resolves to a list of users containing only users whose id is even.</p>
<pre><code class="lang-javascript">//model
var User = {}
User.listEven = function() {
return m.request({method: &quot;GET&quot;, url: &quot;/user&quot;}).then(function(list) {
return list.filter(function(user) {return user.id % 2 == 0});
});
}
//controller
var controller = function() {
this.users = User.listEven()
}</code></pre>
<h4 id="bind-redirection-code">Bind redirection code</h4>
<p>This step is meant to be done in the controller layer. Doing it in the model level is also possible, but philosophically not recommended, because by tying redirection to the model, the code becomes harder to reuse due to overly tight coupling.</p>
<p>In the example below, we use the previously defined <code>listEven</code> model method and queue a controller-level function that redirects to another page if the user list is empty.</p>
<pre><code class="lang-javascript">//controller
var controller = function() {
this.users = User.listEven().then(function(users) {
if (users.length == 0) m.route(&quot;/add&quot;);
})
}</code></pre>
<h4 id="binding-errors">Binding errors</h4>
<p>Mithril thennables take two functions as optional parameters: the first parameter is called if the web service request completes successfully. The second parameter is called if it completes with an error.</p>
<p>Error binding is meant to be done in the controller layer. Doing it in the model level is also possible, but generally leads to more code in order to connect all the dots.</p>
<p>In the example below, we bind an error getter-setter to our previous controller so that the <code>error</code> variable gets populated if the server throws an error.</p>
<pre><code class="lang-javascript">//controller
var controller = function() {
this.error = m.prop(&quot;&quot;)
this.users = User.listEven().then(function(users) {
if (users.length == 0) m.route(&quot;/add&quot;);
}, this.error)
}</code></pre>
<p>If the controller doesn&#39;t already have a success callback to run after a request resolves, you can still bind errors like this:</p>
<pre><code class="lang-javascript">//controller
var controller = function() {
this.error = m.prop(&quot;&quot;)
this.users = User.listEven().then(null, this.error)
}</code></pre>
<hr>
<h3 id="queuing-operations">Queuing Operations</h3>
<p>As you saw, you can chain operations that act on the response data. Typically this is required in three situations:</p>
<ul>
<li>In model-level methods if client-side processing is needed to make the data useful for a controller or view</li>
<li>In the controller, to redirect after a model service resolves</li>
<li>In the controller, to bind error messages</li>
</ul>
<p>In the example below, we take advantage of queuing to debug the AJAX response data prior to doing further processing on the user list:</p>
<pre><code class="lang-javascript">var users = m.request({method: &quot;GET&quot;, url: &quot;/user&quot;})
.then(log);
.then(function(users) {
//add one more user to the response
return users.concat({name: &quot;Jane&quot;})
})
function log(value) {
console.log(value)
return value
}
//assuming the response contains the following data: `[{name: &quot;John&quot;}, {name: &quot;Mary&quot;}]`
//then when resolved (e.g. in a view), the `users` getter-setter will contain a list of users
//i.e. users() //[{name: &quot;John&quot;}, {name: &quot;Mary&quot;}, {name: &quot;Jane&quot;}]</code></pre>
<hr>
<h3 id="casting-the-response-data-to-a-class">Casting the Response Data to a Class</h3>
<p>It&#39;s possible to auto-cast a JSON response to a class. This is useful when we want to control access to certain properties in an object, as opposed to exposing all the fields in POJOs (plain old Javascript objects) for arbitrary processing.</p>
<p>In the example below, <code>User.list</code> returns a list of <code>User</code> instances.</p>
<pre><code class="lang-javascript">var User = function(data) {
this.name = m.prop(data.name);
}
User.list = function() {
return m.request({method: &quot;GET&quot;, url: &quot;/user&quot;, type: User});
}
var users = User.list();
//assuming the response contains the following data: `[{name: &quot;John&quot;}, {name: &quot;Mary&quot;}]`
//then when resolved (e.g. in a view), `users` will contain a list of User instances
//i.e. users()[0].name() == &quot;John&quot;</code></pre>
<hr>
<h3 id="unwrapping-response-data">Unwrapping Response Data</h3>
<p>Often, web services return the relevant data wrapped in objects that contain metadata.</p>
<p>Mithril allows you to unwrap the relevant data, by providing two callback hooks: <code>unwrapSuccess</code> and <code>unwrapError</code>.</p>
<p>These hooks allow you to unwrap different parts of the response data depending on whether it succeed or failed.</p>
<pre><code class="lang-javascript">var users = m.request({
method: &quot;GET&quot;,
url: &quot;/user&quot;,
unwrapSuccess: function(response) {
return response.data;
},
unwrapError: function(response) {
return response.error;
}
});
//assuming the response is: `{data: [{name: &quot;John&quot;}, {name: &quot;Mary&quot;}], count: 2}`
//then when resolved (e.g. in a view), the `users` getter-setter will contain a list of users
//i.e. users() //[{name: &quot;John&quot;}, {name: &quot;Mary&quot;}]</code></pre>
<hr>
<h3 id="using-different-data-transfer-formats">Using Different Data Transfer Formats</h3>
<p>By default, <code>m.request</code> uses JSON to send and receive data to web services. You can override this by providing <code>serialize</code> and <code>deserialize</code> options:</p>
<pre><code class="lang-javascript">var users = m.request({
method: &quot;GET&quot;,
url: &quot;/user&quot;,
serialize: mySerializer,
deserialize: myDeserializer
});</code></pre>
<p>One typical way to override this is to receive as-is responses. The example below shows how to receive a plain string from a txt file.</p>
<pre><code class="lang-javascript">var file = m.request({
method: &quot;GET&quot;,
url: &quot;myfile.txt&quot;,
deserialize: function(value) {return value;}
});</code></pre>
</div>
</div>
</div>
</section>
</main>
<footer>
<div class="container">
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
<br />&copy; 2014 Leo Horie
</div>
</footer>
<script src="lib/prism/prism.js"></script>
</body>
</html>