diff --git a/.eslintignore b/.eslintignore
index ff583c3f..43a0cfc9 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -10,3 +10,4 @@ docs/layout/lib
# TODO: These are temporary, and need to be eventually enabled.
mithril.js
+tests
diff --git a/README.md b/README.md
index 6ed92eea..c04e8a82 100644
--- a/README.md
+++ b/README.md
@@ -18,7 +18,7 @@ Mithril is a client-side MVC framework - a tool to organize code in a way that i
### Light-weight
-- Only 7kb gzipped, no dependencies
+- Only 7.8 kB gzipped, no dependencies
- Small API, small learning curve
### Robust
diff --git a/archive/v0.2.2-rc.1/tools/template-converter.js b/archive/v0.2.2-rc.1/tools/template-converter.js
index 55217ba3..f08aa57c 100644
--- a/archive/v0.2.2-rc.1/tools/template-converter.js
+++ b/archive/v0.2.2-rc.1/tools/template-converter.js
@@ -73,6 +73,7 @@ window.templateConverter = (function () {
each(Object.keys(el.attrs).sort(), function (attrName) {
if (attrName === "style") return
+ if(el.attrs[attrName]===undefined) return;
virtual += "[" + attrName + "='"
virtual += el.attrs[attrName].replace(/'/g, "\\'") + "']"
})
diff --git a/docs/comparison.md b/docs/comparison.md
index fb24bcdd..649344f3 100644
--- a/docs/comparison.md
+++ b/docs/comparison.md
@@ -6,7 +6,7 @@ This page aims to provide a comparison between Mithril and some of the most wide
### Code Size
-One of the most obvious differences between Mithril and most frameworks is in file size: Mithril is around 7kb gzipped and has no dependencies on other libraries.
+One of the most obvious differences between Mithril and most frameworks is in file size: Mithril is around 7.8 kB gzipped and has no dependencies on other libraries.
Note that while a small gzipped size can look appealing, that number is often used to "hide the weight" 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.
diff --git a/docs/getting-started.md b/docs/getting-started.md
index 7ee6f431..aeb0b5c5 100644
--- a/docs/getting-started.md
+++ b/docs/getting-started.md
@@ -4,7 +4,7 @@
Mithril is a client-side Javascript MVC framework, i.e. it's a tool to make application code divided into a data layer (called **M**odel), a UI layer (called **V**iew), and a glue layer (called **C**ontroller)
-Mithril is around 7kb gzipped thanks to its [small, focused, API](mithril.md). 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.
+Mithril is around 7.8 kB gzipped thanks to its [small, focused, API](mithril.md). 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.
The goal of the framework is to make application code discoverable, readable and maintainable, and hopefully help you become an even better developer.
diff --git a/docs/layout/index.html b/docs/layout/index.html
index b91fedea..379a6bfb 100644
--- a/docs/layout/index.html
+++ b/docs/layout/index.html
@@ -50,7 +50,7 @@
Light-weight
- - Only 7kb gzipped, no dependencies
+ - Only 7.8 kB gzipped, no dependencies
- Small API, small learning curve
diff --git a/docs/mithril.component.md b/docs/mithril.component.md
index c89df6df..fa03042f 100644
--- a/docs/mithril.component.md
+++ b/docs/mithril.component.md
@@ -43,7 +43,7 @@ var MyComponent = {
m.mount(document.body, MyComponent) // renders Hello
into
```
-The optional `controller` function creates an object that may be used in the following recommended ways:
+The optional `controller` function creates an object that may be used in the following recommended ways:
- It can contain methods meant to be called by a `view`.
- It can call model methods directly or from methods inside the resulting object.
@@ -165,8 +165,8 @@ var MyComponent = {
m.render(document.body, [
//the two lines below are equivalent
- m(component, {data: "world"}),
- m.component(component, {data: "world"})
+ m(MyComponent, {data: "world"}),
+ m.component(MyComponent, {data: "world"})
])
```
@@ -211,7 +211,7 @@ var App = {
view: function() {
return m(".app", [
m("h1", "My App"),
-
+
//nested component
m.component(MyComponent, {message: "Hello"})
])
@@ -246,7 +246,7 @@ var App = {
return m(".app", [
//pressing the button reverses the list
m("button[type=button]", {onclick: function() {ctrl.data.reverse()}}, "My App"),
-
+
ctrl.data.map(function(item) {
//the key ensures the components aren't recreated from scratch, if they merely exchanged places
return m.component(MyComponent, {message: "Hello " + item, key: item})
@@ -327,12 +327,12 @@ var ctrl = new TemperatureConverter.controller();
assert(ctrl.kelvinToCelsius(273.15) == 0)
//test the template
-var tpl = TemperatureConverter.view(null, {value: 273.15})
+var tpl = TemperatureConverter.view(ctrl, {value: 273.15})
assert(tpl.children[1] == 0)
//test with real DOM
var testRoot = document.createElement("div")
-m.render(testRoot, TemperatureConverter.view(null, {value: 273.15}))
+m.render(testRoot, TemperatureConverter.view(ctrl, {value: 273.15}))
assert(testRoot.innerHTML.indexOf("celsius:0") > -1)
```
@@ -451,7 +451,7 @@ var component = {
var unsaved = m.prop(false)
return {
unsaved: unsaved,
-
+
onunload: function(e) {
if (unsaved()) {
e.preventDefault()
@@ -538,7 +538,7 @@ There are a few other technical caveats when nesting components:
2. Nested components cannot change `m.redraw.strategy` from the controller constructor (but they can from event handlers). It's recommended that you use the [`ctx.retain`](mithril.md#persisting-dom-elements-across-route-changes) flag instead of changing the redraw strategy in controller constructors.
3. The root DOM element in a component's view must not be changed during the lifecycle of the component, otherwise undefined behavior will occur. In other words, don't do this:
-
+
```javascript
var MyComponent = {
view: function() {
@@ -590,7 +590,7 @@ where:
- **Component component**
- A component is supposed to be an Object with two keys: `controller` and `view`. Each of these should point to a Javascript function. If a contoller is not specified, Mithril will automatically create an empty controller function.
+ A component is supposed to be an Object with two keys: `controller` and `view`. Each of these should point to a Javascript function. If a controller is not specified, Mithril will automatically create an empty controller function.
- **Object attributes**
@@ -603,4 +603,3 @@ where:
- **returns Component parameterizedComponent**
A component with arguments bound
-
diff --git a/mithril.d.ts b/mithril.d.ts
index 56d16901..111a9ebc 100644
--- a/mithril.d.ts
+++ b/mithril.d.ts
@@ -250,7 +250,7 @@ declare module _mithril {
(
rootElement: Element,
defaultRoute: string,
- routes: MithrilRoutes
+ routes: MithrilRoutes
): void;
/**
@@ -534,6 +534,12 @@ declare module _mithril {
* @see MithrilElementConfig
*/
config?: MithrilElementConfig;
+
+ /**
+ * Any other virtual element properties including attributes and
+ * event handlers
+ */
+ [property: string]: any;
}
/**
@@ -668,12 +674,12 @@ declare module _mithril {
/**
* This represents a key-value mapping linking routes to components.
*/
- interface MithrilRoutes {
+ interface MithrilRoutes {
/**
* The key represents the route. The value represents the corresponding
* component.
*/
- [key: string]: MithrilComponent;
+ [key: string]: MithrilComponent;
}
/**
diff --git a/mithril.js b/mithril.js
index 5ac0596b..f92faa45 100644
--- a/mithril.js
+++ b/mithril.js
@@ -553,7 +553,7 @@ var m = (function app(window, undefined) {
if (!(attrName in cachedAttrs) || (cachedAttr !== dataAttr)) {
cachedAttrs[attrName] = dataAttr;
try {
- //`config` isn't a real attributes, so ignore it
+ //`config` isn't a real attribute, so ignore it
if (attrName === "config" || attrName === "key") continue;
//hook event handlers to the auto-redrawing system
else if (isFunction(dataAttr) && attrName.slice(0, 2) === "on") {
@@ -578,7 +578,11 @@ var m = (function app(window, undefined) {
//- when using CSS selectors (e.g. `m("[style='']")`), style is used as a string, but it's an object in js
else if (attrName in node && attrName !== "list" && attrName !== "style" && attrName !== "form" && attrName !== "type" && attrName !== "width" && attrName !== "height") {
//#348 don't set the value if not needed otherwise cursor placement breaks in Chrome
- if (tag !== "input" || node[attrName] !== dataAttr) node[attrName] = dataAttr;
+ try {
+ if (tag !== "input" || node[attrName] !== dataAttr) node[attrName] = dataAttr;
+ } catch (e) {
+ node.setAttribute(attrName, dataAttr);
+ }
}
else node.setAttribute(attrName, dataAttr);
}
@@ -621,6 +625,13 @@ var m = (function app(window, undefined) {
else if (cached.children.tag) unload(cached.children);
}
}
+ function appendTextFragment(parentElement, data) {
+ try {
+ parentElement.appendChild($document.createRange().createContextualFragment(data));
+ } catch (e) {
+ parentElement.insertAdjacentHTML("beforeend", data);
+ }
+ }
function injectHTML(parentElement, index, data) {
var nextSibling = parentElement.childNodes[index];
if (nextSibling) {
@@ -634,10 +645,7 @@ var m = (function app(window, undefined) {
else nextSibling.insertAdjacentHTML("beforebegin", data);
}
else {
- if (window.Range && window.Range.prototype.createContextualFragment) {
- parentElement.appendChild($document.createRange().createContextualFragment(data));
- }
- else parentElement.insertAdjacentHTML("beforeend", data);
+ appendTextFragment(parentElement, data);
}
var nodes = [];
while (parentElement.childNodes[index] !== nextSibling) {
diff --git a/test/mithril.mount.js b/test/mithril.mount.js
index dadf0b9d..275e1c2b 100644
--- a/test/mithril.mount.js
+++ b/test/mithril.mount.js
@@ -333,12 +333,11 @@ describe("m.mount()", function () {
})
}))
- list.pop()
+ list = []
refresh(true)
- // TODO: These fail.
- // expect(spies[1]).to.have.been.called
- // expect(spies[2]).to.have.been.called
+ expect(spies[1]).to.have.been.called
+ expect(spies[2]).to.have.been.called
expect(spies[3]).to.have.been.called
})
@@ -374,17 +373,15 @@ describe("m.mount()", function () {
})
}))
- list.pop()
+ list = []
refresh(true)
- // TODO: These fail.
- // expect(spies1[1]).to.have.been.called
- // expect(spies1[2]).to.have.been.called
+ expect(spies1[1]).to.have.been.called
+ expect(spies1[2]).to.have.been.called
expect(spies1[3]).to.have.been.called
- // TODO: These fail.
- // expect(spies2[1]).to.have.been.called
- // expect(spies2[2]).to.have.been.called
+ expect(spies2[1]).to.have.been.called
+ expect(spies2[2]).to.have.been.called
expect(spies2[3]).to.have.been.called
})
diff --git a/test/mithril.trust.js b/test/mithril.trust.js
index e8679f29..c64adb4c 100644
--- a/test/mithril.trust.js
+++ b/test/mithril.trust.js
@@ -20,19 +20,20 @@ describe("m.trust()", function () {
// FIXME: implement document.createRange().createContextualFragment() in the
// mock window for these tests
dom(function () {
- xit("isn't escaped in m.render()", function () {
+
+ it("isn't escaped in m.render()", function () {
var root = document.createElement("div")
m.render(root, m("div", "a", m.trust("&"), "b"))
expect(root.childNodes[0].innerHTML).to.equal("a&b")
})
- xit("works with mixed trusted content in div", function () {
+ it("works with mixed trusted content in div", function () {
var root = document.createElement("div")
m.render(root, [m.trust("1
2
"), m("i", "foo")])
expect(root.childNodes[2].tagName).to.equal("I")
})
- xit("works with mixed trusted content in text nodes", function () {
+ it("works with mixed trusted content in text nodes", function () {
var root = document.createElement("div")
m.render(root, [
m.trust("1
1232
"),
@@ -41,9 +42,7 @@ describe("m.trust()", function () {
expect(root.childNodes[3].tagName).to.equal("I")
})
- // FIXME: this is a bug (trusted string's contents rendered as just
- // textual contents)
- xit("works with mixed trusted content in td", function () {
+ it("works with mixed trusted content in td", function () {
var root = document.createElement("table")
root.appendChild(root = document.createElement("tr"))
@@ -54,5 +53,16 @@ describe("m.trust()", function () {
expect(root.childNodes[2].tagName).to.equal("TD")
})
+
+ it("works with trusted content in div", function () {
+ var root = document.createElement("div")
+ m.render(root, m('div', [
+ m('p', '©'),
+ m('p', m.trust('©')),
+ m.trust('©'),
+ ]))
+ expect(root.innerHTML).to.equal("")
+ })
+
})
})
diff --git a/tests/trust-test.html b/tests/trust-test.html
new file mode 100644
index 00000000..e69de29b