diff --git a/examples/dbmonster/mithril-0.2.x/app.js b/examples/dbmonster/mithril-0.2.x/app.js
new file mode 100644
index 00000000..ebfa2253
--- /dev/null
+++ b/examples/dbmonster/mithril-0.2.x/app.js
@@ -0,0 +1,44 @@
+"use strict"
+
+var data = []
+
+var root = document.getElementById("app")
+update()
+
+function update() {
+ data = ENV.generateData().toArray()
+
+ Monitoring.renderRate.ping()
+
+ m.redraw();
+
+ setTimeout(update, ENV.timeout)
+}
+
+m.mount(root, {
+ view : function() {
+ return m("div", [
+ m("table", { className: "table table-striped latest-data" }, [
+ m("tbody",
+ data.map(function(db) {
+ return m("tr", {key: db.dbname}, [
+ m("td", { className: "dbname" }, db.dbname),
+ m("td", { className: "query-count" }, [
+ m("span", { className: db.lastSample.countClassName }, db.lastSample.nbQueries)
+ ]),
+ db.lastSample.topFiveQueries.map(function(query) {
+ return m("td", { className: query.elapsedClassName }, [
+ m("span", query.formatElapsed),
+ m("div", { className: "popover left" }, [
+ m("div", { className: "popover-content" }, query.query),
+ m("div", { className: "arrow" })
+ ])
+ ])
+ })
+ ])
+ })
+ )
+ ])
+ ])
+ }
+});
diff --git a/examples/dbmonster/mithril-0.2.x/index.html b/examples/dbmonster/mithril-0.2.x/index.html
new file mode 100644
index 00000000..d6000ee0
--- /dev/null
+++ b/examples/dbmonster/mithril-0.2.x/index.html
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+ dbmon (Mithril 0.2.x)
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/dbmonster/mithril-0.2.x/mithril-0.2.4.js b/examples/dbmonster/mithril-0.2.x/mithril-0.2.4.js
new file mode 100644
index 00000000..b98d933a
--- /dev/null
+++ b/examples/dbmonster/mithril-0.2.x/mithril-0.2.4.js
@@ -0,0 +1,2190 @@
+;(function (global, factory) { // eslint-disable-line
+ "use strict"
+ /* eslint-disable no-undef */
+ var m = factory(global)
+ if (typeof module === "object" && module != null && module.exports) {
+ module.exports = m
+ } else if (typeof define === "function" && define.amd) {
+ define(function () { return m })
+ } else {
+ global.m = m
+ }
+ /* eslint-enable no-undef */
+})(typeof window !== "undefined" ? window : this, function (global, undefined) { // eslint-disable-line
+ "use strict"
+
+ m.version = function () {
+ return "v0.2.3"
+ }
+
+ var hasOwn = {}.hasOwnProperty
+ var type = {}.toString
+
+ function isFunction(object) {
+ return typeof object === "function"
+ }
+
+ function isObject(object) {
+ return type.call(object) === "[object Object]"
+ }
+
+ function isString(object) {
+ return type.call(object) === "[object String]"
+ }
+
+ var isArray = Array.isArray || function (object) {
+ return type.call(object) === "[object Array]"
+ }
+
+ function noop() {}
+
+ var voidElements = {
+ AREA: 1,
+ BASE: 1,
+ BR: 1,
+ COL: 1,
+ COMMAND: 1,
+ EMBED: 1,
+ HR: 1,
+ IMG: 1,
+ INPUT: 1,
+ KEYGEN: 1,
+ LINK: 1,
+ META: 1,
+ PARAM: 1,
+ SOURCE: 1,
+ TRACK: 1,
+ WBR: 1
+ }
+
+ // caching commonly used variables
+ var $document, $location, $requestAnimationFrame, $cancelAnimationFrame
+
+ // self invoking function needed because of the way mocks work
+ function initialize(mock) {
+ $document = mock.document
+ $location = mock.location
+ $cancelAnimationFrame = mock.cancelAnimationFrame || mock.clearTimeout
+ $requestAnimationFrame = mock.requestAnimationFrame || mock.setTimeout
+ }
+
+ // testing API
+ m.deps = function (mock) {
+ initialize(global = mock || window)
+ return global
+ }
+
+ m.deps(global)
+
+ /**
+ * @typedef {String} Tag
+ * A string that looks like -> div.classname#id[param=one][param2=two]
+ * Which describes a DOM node
+ */
+
+ function parseTagAttrs(cell, tag) {
+ var classes = []
+ var parser = /(?:(^|#|\.)([^#\.\[\]]+))|(\[.+?\])/g
+ var match
+
+ while ((match = parser.exec(tag))) {
+ if (match[1] === "" && match[2]) {
+ 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 = /\[(.+?)(?:=("|'|)(.*?)\2)?\]/.exec(match[3])
+ cell.attrs[pair[1]] = pair[3] || ""
+ }
+ }
+
+ return classes
+ }
+
+ function getVirtualChildren(args, hasAttrs) {
+ var children = hasAttrs ? args.slice(1) : args
+
+ if (children.length === 1 && isArray(children[0])) {
+ return children[0]
+ } else {
+ return children
+ }
+ }
+
+ function assignAttrs(target, attrs, classes) {
+ var classAttr = "class" in attrs ? "class" : "className"
+
+ for (var attrName in attrs) {
+ if (hasOwn.call(attrs, attrName)) {
+ if (attrName === classAttr &&
+ attrs[attrName] != null &&
+ attrs[attrName] !== "") {
+ classes.push(attrs[attrName])
+ // create key in correct iteration order
+ target[attrName] = ""
+ } else {
+ target[attrName] = attrs[attrName]
+ }
+ }
+ }
+
+ if (classes.length) target[classAttr] = classes.join(" ")
+ }
+
+ /**
+ *
+ * @param {Tag} The DOM node tag
+ * @param {Object=[]} optional key-value pairs to be mapped to DOM attrs
+ * @param {...mNode=[]} Zero or more Mithril child nodes. Can be an array,
+ * or splat (optional)
+ */
+ function m(tag, pairs) {
+ var args = []
+
+ for (var i = 1, length = arguments.length; i < length; i++) {
+ args[i - 1] = arguments[i]
+ }
+
+ if (isObject(tag)) return parameterize(tag, args)
+
+ if (!isString(tag)) {
+ throw new Error("selector in m(selector, attrs, children) should " +
+ "be a string")
+ }
+
+ var hasAttrs = pairs != null && isObject(pairs) &&
+ !("tag" in pairs || "view" in pairs || "subtree" in pairs)
+
+ var attrs = hasAttrs ? pairs : {}
+ var cell = {
+ tag: "div",
+ attrs: {},
+ children: getVirtualChildren(args, hasAttrs)
+ }
+
+ assignAttrs(cell.attrs, attrs, parseTagAttrs(cell, tag))
+ return cell
+ }
+
+ function forEach(list, f) {
+ for (var i = 0; i < list.length && !f(list[i], i++);) {
+ // function called in condition
+ }
+ }
+
+ function forKeys(list, f) {
+ forEach(list, function (attrs, i) {
+ return (attrs = attrs && attrs.attrs) &&
+ attrs.key != null &&
+ f(attrs, i)
+ })
+ }
+ // This function was causing deopts in Chrome.
+ function dataToString(data) {
+ // data.toString() might throw or return null if data is the return
+ // value of Console.log in some versions of Firefox (behavior depends on
+ // version)
+ try {
+ if (data != null && data.toString() != null) return data
+ } catch (e) {
+ // silently ignore errors
+ }
+ return ""
+ }
+
+ // This function was causing deopts in Chrome.
+ function injectTextNode(parentElement, first, index, data) {
+ try {
+ insertNode(parentElement, first, index)
+ first.nodeValue = data
+ } catch (e) {
+ // IE erroneously throws error when appending an empty text node
+ // after a null
+ }
+ }
+
+ function flatten(list) {
+ // recursively flatten array
+ for (var i = 0; i < list.length; i++) {
+ if (isArray(list[i])) {
+ list = list.concat.apply([], list)
+ // check current index again and flatten until there are no more
+ // nested arrays at that index
+ i--
+ }
+ }
+ return list
+ }
+
+ function insertNode(parentElement, node, index) {
+ parentElement.insertBefore(node,
+ parentElement.childNodes[index] || null)
+ }
+
+ var DELETION = 1
+ var INSERTION = 2
+ var MOVE = 3
+
+ function handleKeysDiffer(data, existing, cached, parentElement) {
+ forKeys(data, function (key, i) {
+ existing[key = key.key] = existing[key] ? {
+ action: MOVE,
+ index: i,
+ from: existing[key].index,
+ element: cached.nodes[existing[key].index] ||
+ $document.createElement("div")
+ } : {action: INSERTION, index: i}
+ })
+
+ var actions = []
+ for (var prop in existing) {
+ if (hasOwn.call(existing, prop)) {
+ actions.push(existing[prop])
+ }
+ }
+
+ var changes = actions.sort(sortChanges)
+ var newCached = new Array(cached.length)
+
+ newCached.nodes = cached.nodes.slice()
+
+ forEach(changes, function (change) {
+ var index = change.index
+ if (change.action === DELETION) {
+ clear(cached[index].nodes, cached[index])
+ newCached.splice(index, 1)
+ }
+ if (change.action === INSERTION) {
+ var dummy = $document.createElement("div")
+ dummy.key = data[index].attrs.key
+ insertNode(parentElement, dummy, index)
+ newCached.splice(index, 0, {
+ attrs: {key: data[index].attrs.key},
+ nodes: [dummy]
+ })
+ newCached.nodes[index] = dummy
+ }
+
+ if (change.action === MOVE) {
+ var changeElement = change.element
+ var maybeChanged = parentElement.childNodes[index]
+ if (maybeChanged !== changeElement && changeElement !== null) {
+ parentElement.insertBefore(changeElement,
+ maybeChanged || null)
+ }
+ newCached[index] = cached[change.from]
+ newCached.nodes[index] = changeElement
+ }
+ })
+
+ return newCached
+ }
+
+ function diffKeys(data, cached, existing, parentElement) {
+ var keysDiffer = data.length !== cached.length
+
+ if (!keysDiffer) {
+ forKeys(data, function (attrs, i) {
+ var cachedCell = cached[i]
+ return keysDiffer = cachedCell &&
+ cachedCell.attrs &&
+ cachedCell.attrs.key !== attrs.key
+ })
+ }
+
+ if (keysDiffer) {
+ return handleKeysDiffer(data, existing, cached, parentElement)
+ } else {
+ return cached
+ }
+ }
+
+ function diffArray(data, cached, nodes) {
+ // diff the array itself
+
+ // update the list of DOM nodes by collecting the nodes from each item
+ forEach(data, function (_, i) {
+ if (cached[i] != null) nodes.push.apply(nodes, cached[i].nodes)
+ })
+ // remove items from the end of the array if the new array is shorter
+ // than the old one. if errors ever happen here, the issue is most
+ // likely a bug in the construction of the `cached` data structure
+ // somewhere earlier in the program
+ forEach(cached.nodes, function (node, i) {
+ if (node.parentNode != null && nodes.indexOf(node) < 0) {
+ clear([node], [cached[i]])
+ }
+ })
+
+ if (data.length < cached.length) cached.length = data.length
+ cached.nodes = nodes
+ }
+
+ function buildArrayKeys(data) {
+ var guid = 0
+ forKeys(data, function () {
+ forEach(data, function (attrs) {
+ if ((attrs = attrs && attrs.attrs) && attrs.key == null) {
+ attrs.key = "__mithril__" + guid++
+ }
+ })
+ return 1
+ })
+ }
+
+ function isDifferentEnough(data, cached, dataAttrKeys) {
+ if (data.tag !== cached.tag) return true
+
+ if (dataAttrKeys.sort().join() !==
+ Object.keys(cached.attrs).sort().join()) {
+ return true
+ }
+
+ if (data.attrs.id !== cached.attrs.id) {
+ return true
+ }
+
+ if (data.attrs.key !== cached.attrs.key) {
+ return true
+ }
+
+ if (m.redraw.strategy() === "all") {
+ return !cached.configContext || cached.configContext.retain !== true
+ }
+
+ if (m.redraw.strategy() === "diff") {
+ return cached.configContext && cached.configContext.retain === false
+ }
+
+ return false
+ }
+
+ function maybeRecreateObject(data, cached, dataAttrKeys) {
+ // if an element is different enough from the one in cache, recreate it
+ if (isDifferentEnough(data, cached, dataAttrKeys)) {
+ if (cached.nodes.length) clear(cached.nodes)
+
+ if (cached.configContext &&
+ isFunction(cached.configContext.onunload)) {
+ cached.configContext.onunload()
+ }
+
+ if (cached.controllers) {
+ forEach(cached.controllers, function (controller) {
+ if (controller.onunload) {
+ controller.onunload({preventDefault: noop})
+ }
+ })
+ }
+ }
+ }
+
+ function getObjectNamespace(data, namespace) {
+ if (data.attrs.xmlns) return data.attrs.xmlns
+ if (data.tag === "svg") return "http://www.w3.org/2000/svg"
+ if (data.tag === "math") return "http://www.w3.org/1998/Math/MathML"
+ return namespace
+ }
+
+ var pendingRequests = 0
+ m.startComputation = function () { pendingRequests++ }
+ m.endComputation = function () {
+ if (pendingRequests > 1) {
+ pendingRequests--
+ } else {
+ pendingRequests = 0
+ m.redraw()
+ }
+ }
+
+ function unloadCachedControllers(cached, views, controllers) {
+ if (controllers.length) {
+ cached.views = views
+ cached.controllers = controllers
+ forEach(controllers, function (controller) {
+ if (controller.onunload && controller.onunload.$old) {
+ controller.onunload = controller.onunload.$old
+ }
+
+ if (pendingRequests && controller.onunload) {
+ var onunload = controller.onunload
+ controller.onunload = noop
+ controller.onunload.$old = onunload
+ }
+ })
+ }
+ }
+
+ function scheduleConfigsToBeCalled(configs, data, node, isNew, cached) {
+ // schedule configs to be called. They are called after `build` finishes
+ // running
+ if (isFunction(data.attrs.config)) {
+ var context = cached.configContext = cached.configContext || {}
+
+ // bind
+ configs.push(function () {
+ return data.attrs.config.call(data, node, !isNew, context,
+ cached)
+ })
+ }
+ }
+
+ function buildUpdatedNode(
+ cached,
+ data,
+ editable,
+ hasKeys,
+ namespace,
+ views,
+ configs,
+ controllers
+ ) {
+ var node = cached.nodes[0]
+
+ if (hasKeys) {
+ 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 (controllers.length) {
+ cached.views = views
+ cached.controllers = controllers
+ }
+
+ return node
+ }
+
+ function handleNonexistentNodes(data, parentElement, index) {
+ var nodes
+ if (data.$trusted) {
+ nodes = injectHTML(parentElement, index, data)
+ } else {
+ nodes = [$document.createTextNode(data)]
+ if (!(parentElement.nodeName in voidElements)) {
+ insertNode(parentElement, nodes[0], index)
+ }
+ }
+
+ var cached
+
+ if (typeof data === "string" ||
+ typeof data === "number" ||
+ typeof data === "boolean") {
+ cached = new data.constructor(data)
+ } else {
+ cached = data
+ }
+
+ cached.nodes = nodes
+ return cached
+ }
+
+ function reattachNodes(
+ data,
+ cached,
+ parentElement,
+ editable,
+ index,
+ parentTag
+ ) {
+ var nodes = cached.nodes
+ if (!editable || editable !== $document.activeElement) {
+ if (data.$trusted) {
+ clear(nodes, cached)
+ nodes = injectHTML(parentElement, index, data)
+ } else if (parentTag === "textarea") {
+ //