Skip to content

Commit

Permalink
Start on transferrable events. Ref GH-2
Browse files Browse the repository at this point in the history
  • Loading branch information
shama committed Jun 22, 2016
1 parent da7aac8 commit f414207
Show file tree
Hide file tree
Showing 3 changed files with 183 additions and 23 deletions.
77 changes: 55 additions & 22 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,35 +1,68 @@
/* global MutationObserver */
var document = require('global/document')
var window = require('global/window')
var watch = []
var watch = Object.create(null)
var KEY_ID = 'onloadid'

if (window && window.MutationObserver) {
var observer = new MutationObserver(function (mutations) {
for (var i = 0; i < mutations.length; i++) {
var mutation = mutations[i]
var x, y
for (x = 0; x < mutation.addedNodes.length; x++) {
for (y = 0; y < watch.length; y++) {
if (watch[y][0] === mutation.addedNodes[x]) {
watch[y][1]()
}
}
}
for (x = 0; x < mutation.removedNodes.length; x++) {
for (y = 0; y < watch.length; y++) {
if (watch[y][0] === mutation.removedNodes[x]) {
watch[y][2]()
watch.splice(y, 1)
}
}
}
eachMutation(mutations[i].removedNodes, function (target) {
target[1]()
})
eachMutation(mutations[i].addedNodes, function (target) {
// TODO: Should queue functions to run, then run them all on setImmediate?
target[0]()
})
}
})
observer.observe(document.body, {childList: true, subtree: true})
}

module.exports = function onload (el, l, u) {
l = l || function () {}
u = u || function () {}
watch.push([el, l, u])
module.exports = function onload (el, on, off) {
on = on || function () {}
off = off || function () {}
var id
if (el.dataset && el.dataset[KEY_ID]) {
id = el.dataset[KEY_ID]
} else {
// TODO: Is there a better way to uniquely identify an element?
var caller = onload.caller.toString()
if (caller) {
id = hash(caller)
} else {
var err = new Error()
var lines = err.stack.split('\n')
for (var i = 0; i < lines.length; i++) {
if (lines[i].indexOf('onload') !== -1) {
id = lines[i + 1]
break
}
}
id = hash(id.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ''))
}
el.dataset[KEY_ID] = id
}
// TODO: Should we allow multiple set per element?
watch[id] = [on, off]
}

function eachMutation (nodes, fn) {
for (var i = 0; i < nodes.length; i++) {
if (nodes[i].dataset && nodes[i].dataset[KEY_ID] && watch[nodes[i].dataset[KEY_ID]]) {
fn(watch[nodes[i].dataset[KEY_ID]])
}
if (nodes[i].childNodes.length > 0) {
eachMutation(nodes[i].childNodes, fn)
}
}
}

function hash (str) {
var res = 5381
var i = str.length
while (i) {
res = (res * 33) ^ str.charCodeAt(--i)
}
return res >>> 0
}
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"standard": "^6.0.7",
"tape": "^4.5.0",
"testron": "^1.2.0",
"wzrd": "^1.3.1"
"wzrd": "^1.3.1",
"yo-yo": "^1.2.1"
}
}
126 changes: 126 additions & 0 deletions test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
var onload = require('./')
var test = require('tape')
var yo = require('yo-yo')

test('onload/onunload', function (t) {
t.plan(2)
Expand Down Expand Up @@ -34,3 +35,128 @@ test('nested', function (t) {
e2.appendChild(e3)
e2.removeChild(e3)
})

test('complex', function (t) {
t.plan(4)
var state = []

function button () {
var el = yo`<button>click</button>`
onload(el, function () {
state.push('on')
}, function () {
state.push('off')
})
return el
}

var root = yo`<div>
${button()}
</div>`
document.body.appendChild(root)

runops([
function () {
t.deepEqual(state, ['on'], 'turn on')
state = []
root = yo.update(root, yo`<p>${button()}</p>`)
},
function () {
t.deepEqual(state, ['off', 'on'], 'turn off/on')
state = []
root = yo.update(root, yo`<p>removed</p>`)
},
function () {
t.deepEqual(state, ['off'], 'turn off')
state = []
var btn = button()
root = yo.update(root, yo`<p><div>${btn}</div></p>`)
root = yo.update(root, yo`<p>
<div>Updated</div>
<div>${btn}</div>
</p>`)
},
function () {
t.deepEqual(state, ['off', 'on'], 'turn off/on')
}
], t.end)
})

test('complex nested', function (t) {
var state = []
function button () {
var el = yo`<button>click</button>`
onload(el, function () {
state.push('on')
}, function () {
state.push('off')
})
return el
}
function app (page) {
return yo`<div class="app">
<h1>Hello</h1>
${page}
</div>`
}

var root = app(yo`<div>Loading...</div>`)
document.body.appendChild(root)

runops([
function () {
t.deepEqual(state, [], 'did nothing')
state = []
root = yo.update(root, app(yo`<div class="page">
${button()}
</div>`))
},
function () {
t.deepEqual(state, ['on'], 'turn on')
state = []
root = yo.update(root, app(yo`<div class="page">
<h3>Another Page</h3>
</div>`))
},
function () {
t.deepEqual(state, ['off'], 'turn off')
state = []
root = yo.update(root, app(yo`<div class="page">
${button()}
${button()}
</div>`))
},
function () {
t.deepEqual(state, ['on', 'on'], 'turn 2 on')
state = []
root = yo.update(root, app(yo`<div class="page">
${button()}
${button()}
</div>`))
},
function () {
t.deepEqual(state, [], 'do nothing')
state = []
root = yo.update(root, app(yo`<div class="page">
${button()}
<p>removed</p>
</div>`))
},
function () {
t.deepEqual(state, ['off'], 'turn 1 off')
}
], t.end)
})

function runops (ops, done) {
function loop () {
var next = ops.shift()
if (next) {
next()
setTimeout(loop, 10)
} else {
done()
}
}
setTimeout(loop, 10)
}

0 comments on commit f414207

Please sign in to comment.