can-dom-mutate
Advanced tools
Comparing version 1.3.7 to 2.0.0
15
-util.js
@@ -32,11 +32,2 @@ "use strict"; | ||
function isInDocument (node) { | ||
var root = getDocument(); | ||
if (root === node) { | ||
return true; | ||
} | ||
return contains(root, node); | ||
} | ||
function isDocumentElement (node) { | ||
@@ -128,3 +119,3 @@ return getDocument().documentElement === node; | ||
rootNode, | ||
NodeFilter.SHOW_TEXT | NodeFilter.SHOW_ELEMENT, | ||
NodeFilter.SHOW_TEXT | NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT, | ||
treeWalkerFilter, | ||
@@ -167,3 +158,2 @@ false | ||
eliminate: eliminate, | ||
isInDocument: isInDocument, | ||
getDocument: getDocument, | ||
@@ -176,3 +166,4 @@ isDocumentElement: isDocumentElement, | ||
subscription: subscription, | ||
addToSet: addToSet | ||
addToSet: addToSet, | ||
contains: contains | ||
}; |
@@ -16,5 +16,3 @@ 'use strict'; | ||
var slice = Array.prototype.slice; | ||
var domMutate, dispatchInsertion, dispatchRemoval; | ||
var domMutate, dispatchInsertion, dispatchRemoval, dispatchAttributeChange; | ||
var dataStore = new WeakMap(); | ||
@@ -53,4 +51,4 @@ | ||
return function batchAdd(items, callback) { | ||
processBatchItems(items); | ||
return function batchAdd(items, callback, dispatchConnected, flush) { | ||
processBatchItems(items, dispatchConnected, flush); | ||
if(callback){ | ||
@@ -114,21 +112,69 @@ callback(); | ||
function fire (callbacks, arg) { | ||
var safeCallbacks = slice.call(callbacks, 0); | ||
var safeCallbackCount = safeCallbacks.length; | ||
for (var i = 0; i < safeCallbackCount; i++) { | ||
safeCallbacks[i](arg); | ||
var promise = Promise.resolve(); | ||
function nextTick(handler) { | ||
promise.then(handler); | ||
} | ||
var recordsAndCallbacks = null; | ||
function flushCallbacks(callbacks, arg){ | ||
var callbacksCount = callbacks.length; | ||
var safeCallbacks = callbacks.slice(0); | ||
for(var c = 0; c < callbacksCount; c++){ | ||
safeCallbacks[c](arg); | ||
} | ||
} | ||
function dispatch(listenerKey, documentDataKey) { | ||
return function dispatchEvents(events) { | ||
function flushRecords(){ | ||
if(recordsAndCallbacks === null) { | ||
return; | ||
} | ||
var safeBatch = recordsAndCallbacks; | ||
recordsAndCallbacks = null; | ||
var batchCount = safeBatch.length; | ||
for (var i = 0; i < batchCount; i++) { | ||
var batchData = safeBatch[i]; | ||
flushCallbacks(batchData.callbacks, batchData.arg); | ||
} | ||
} | ||
function flushAsync(callbacks, arg) { | ||
if(recordsAndCallbacks === null) { | ||
recordsAndCallbacks = [{arg: arg, callbacks: callbacks}]; | ||
nextTick(flushRecords); | ||
} else { | ||
recordsAndCallbacks.push({arg: arg, callbacks: callbacks}); | ||
} | ||
} | ||
function dispatch(targetKey, connectedKey, documentDataKey) { | ||
return function dispatchEvents(events, dispatchConnected, flush) { | ||
// we could check the first element and see if it's in the document | ||
// if it is conditionally fire "connected" events | ||
for (var e = 0; e < events.length; e++) { | ||
var event = events[e]; | ||
var target = event.target; | ||
// we need to check | ||
// | ||
var targetListeners = getTargetListeners(target, targetKey); | ||
var targetListeners = getTargetListeners(target, listenerKey); | ||
if (targetListeners) { | ||
fire(targetListeners, event); | ||
flush(targetListeners, event); | ||
} | ||
if(!dispatchConnected) { | ||
continue; | ||
} | ||
var connectedListeners; | ||
if(connectedKey){ | ||
connectedListeners = getTargetListeners(target, connectedKey); | ||
} | ||
if (connectedListeners) { | ||
flush(connectedListeners, event); | ||
} | ||
if (!documentDataKey) { | ||
@@ -140,3 +186,3 @@ continue; | ||
if (documentListeners) { | ||
fire(documentListeners, event); | ||
flush(documentListeners, event); | ||
} | ||
@@ -224,4 +270,4 @@ } | ||
dispatchRemoval( toMutationEvents( canReflect.toArray(removed) ) ); | ||
dispatchInsertion( toMutationEvents( canReflect.toArray(added) ) ); | ||
dispatchRemoval( toMutationEvents( canReflect.toArray(removed) ), null, true, flushCallbacks ); | ||
dispatchInsertion( toMutationEvents( canReflect.toArray(added) ), null, true, flushCallbacks ); | ||
} | ||
@@ -237,3 +283,7 @@ | ||
var oldValue = mutation.oldValue; | ||
domMutate.dispatchNodeAttributeChange(node, attributeName, oldValue); | ||
dispatchAttributeChange([{ | ||
target: node, | ||
attributeName: attributeName, | ||
oldValue: oldValue | ||
}], null, true, flushCallbacks); | ||
} | ||
@@ -273,3 +323,6 @@ } | ||
return function removeNodeListener() { | ||
stopObserving(); | ||
if(stopObserving) { | ||
stopObserving(); | ||
} | ||
removeTargetListener(target, listenerKey, listener); | ||
@@ -322,9 +375,11 @@ }; | ||
// target listener keys | ||
var insertionDataKey = domMutationPrefix + 'InsertionData'; | ||
var removalDataKey = domMutationPrefix + 'RemovalData'; | ||
var connectedDataKey = domMutationPrefix + 'ConnectedData'; | ||
var disconnectedDataKey = domMutationPrefix + 'DisconnectedData'; | ||
var insertedDataKey = domMutationPrefix + 'InsertedData'; | ||
var removedDataKey = domMutationPrefix + 'RemovedData'; | ||
var attributeChangeDataKey = domMutationPrefix + 'AttributeChangeData'; | ||
// document listener keys | ||
var documentInsertionDataKey = domMutationPrefix + 'DocumentInsertionData'; | ||
var documentRemovalDataKey = domMutationPrefix + 'DocumentRemovalData'; | ||
var documentConnectedDataKey = domMutationPrefix + 'DocumentConnectedData'; | ||
var documentDisconnectedDataKey = domMutationPrefix + 'DocumentDisconnectedData'; | ||
var documentAttributeChangeDataKey = domMutationPrefix + 'DocumentAttributeChangeData'; | ||
@@ -336,19 +391,21 @@ | ||
dispatchInsertion = batch(dispatch(insertionDataKey, documentInsertionDataKey)); | ||
dispatchRemoval = batch(dispatch(removalDataKey, documentRemovalDataKey)); | ||
var dispatchAttributeChange = batch(dispatch(attributeChangeDataKey, documentAttributeChangeDataKey)); | ||
dispatchInsertion = batch(dispatch(insertedDataKey, connectedDataKey, documentConnectedDataKey)); | ||
dispatchRemoval = batch(dispatch(removedDataKey, disconnectedDataKey, documentDisconnectedDataKey)); | ||
dispatchAttributeChange = batch(dispatch(attributeChangeDataKey, null , documentAttributeChangeDataKey)); | ||
// node listeners | ||
var addNodeInsertionListener = addNodeListener(insertionDataKey, treeDataKey); | ||
var addNodeRemovalListener = addNodeListener(removalDataKey, treeDataKey); | ||
var addNodeConnectedListener = addNodeListener(connectedDataKey, treeDataKey); | ||
var addNodeDisconnectedListener = addNodeListener(disconnectedDataKey, treeDataKey); | ||
var addNodeInsertedListener = addNodeListener(insertedDataKey, treeDataKey); | ||
var addNodeRemovedListener = addNodeListener(removedDataKey, treeDataKey); | ||
var addNodeAttributeChangeListener = addNodeListener(attributeChangeDataKey, attributeDataKey, true); | ||
// global listeners | ||
var addInsertionListener = addGlobalListener( | ||
documentInsertionDataKey, | ||
addNodeInsertionListener | ||
var addConnectedListener = addGlobalListener( | ||
documentConnectedDataKey, | ||
addNodeConnectedListener | ||
); | ||
var addRemovalListener = addGlobalListener( | ||
documentRemovalDataKey, | ||
addNodeRemovalListener | ||
var addDisconnectedListener = addGlobalListener( | ||
documentDisconnectedDataKey, | ||
addNodeDisconnectedListener | ||
); | ||
@@ -373,7 +430,8 @@ var addAttributeChangeListener = addGlobalListener( | ||
*/ | ||
dispatchNodeInsertion: function (node, callback) { | ||
dispatchNodeInsertion: function (node, callback, dispatchConnected) { | ||
var nodes = new Set(); | ||
util.addToSet( getAllNodes(node), nodes); | ||
var events = toMutationEvents( canReflect.toArray(nodes) ); | ||
dispatchInsertion(events, callback); | ||
// this is basically an array of every single child of node including node | ||
dispatchInsertion(events, callback, dispatchConnected, flushAsync); | ||
}, | ||
@@ -392,7 +450,7 @@ | ||
*/ | ||
dispatchNodeRemoval: function (node, callback) { | ||
dispatchNodeRemoval: function (node, callback, dispatchConnected) { | ||
var nodes = new Set(); | ||
util.addToSet( getAllNodes(node), nodes); | ||
var events = toMutationEvents( canReflect.toArray(nodes) ); | ||
dispatchRemoval(events, callback); | ||
dispatchRemoval(events, callback, dispatchConnected, flushAsync); | ||
}, | ||
@@ -425,11 +483,11 @@ | ||
oldValue: oldValue | ||
}], callback); | ||
}], callback, true, flushAsync); | ||
}, | ||
/** | ||
* @function can-dom-mutate.onNodeInsertion onNodeInsertion | ||
* @function can-dom-mutate.onNodeConnected onNodeConnected | ||
* | ||
* Listen for insertion mutations on the given node. | ||
* | ||
* @signature `onNodeInsertion( node, callback )` | ||
* @signature `onNodeConnected( node, callback )` | ||
* @parent can-dom-mutate.static | ||
@@ -440,10 +498,14 @@ * @param {Node} node The node on which to listen for insertion mutations. | ||
*/ | ||
onNodeInsertion: addNodeInsertionListener, | ||
onNodeConnected: addNodeConnectedListener, | ||
onNodeInsertion: function(){ | ||
// TODO: remove in prod | ||
console.warn("can-dom-mutate: Use onNodeConnected instead of onNodeInsertion"); | ||
return addNodeConnectedListener.apply(this, arguments); | ||
}, | ||
/** | ||
* @function can-dom-mutate.onNodeRemoval onNodeRemoval | ||
* @function can-dom-mutate.onNodeDisconnected onNodeDisconnected | ||
* | ||
* Listen for removal mutations on the given node. | ||
* | ||
* @signature `onNodeRemoval( node, callback )` | ||
* @signature `onNodeDisconnected( node, callback )` | ||
* @parent can-dom-mutate.static | ||
@@ -454,4 +516,8 @@ * @param {Node} node The node on which to listen for removal mutations. | ||
*/ | ||
onNodeRemoval: addNodeRemovalListener, | ||
onNodeDisconnected: addNodeDisconnectedListener, | ||
onNodeRemoval: function(){ | ||
// TODO: remove in prod | ||
console.warn("can-dom-mutate: Use onNodeDisconnected instead of onNodeRemoval"); | ||
return addNodeDisconnectedListener.apply(this, arguments); | ||
}, | ||
/** | ||
@@ -471,7 +537,7 @@ * @function can-dom-mutate.onNodeAttributeChange onNodeAttributeChange | ||
/** | ||
* @function can-dom-mutate.onRemoval onRemoval | ||
* @function can-dom-mutate.onDisconnected onDisconnected | ||
* | ||
* Listen for removal mutations on any node within the documentElement. | ||
* | ||
* @signature `onRemoval( documentElement, callback )` | ||
* @signature `onDisconnected( documentElement, callback )` | ||
* @parent can-dom-mutate.static | ||
@@ -482,10 +548,14 @@ * @param {Node} documentElement The documentElement on which to listen for removal mutations. | ||
*/ | ||
onRemoval: addRemovalListener, | ||
onDisconnected: addDisconnectedListener, | ||
onRemoval: function(){ | ||
// TODO: remove in prod | ||
console.warn("can-dom-mutate: Use onDisconnected instead of onRemoval"); | ||
return addDisconnectedListener.apply(this, arguments); | ||
}, | ||
/** | ||
* @function can-dom-mutate.onInsertion onInsertion | ||
* @function can-dom-mutate.onConnected onConnected | ||
* | ||
* Listen for insertion mutations on any node within the documentElement. | ||
* | ||
* @signature `onInsertion( documentElement, callback )` | ||
* @signature `onConnected( documentElement, callback )` | ||
* @parent can-dom-mutate.static | ||
@@ -496,4 +566,8 @@ * @param {Node} documentElement The documentElement on which to listen for removal mutations. | ||
*/ | ||
onInsertion: addInsertionListener, | ||
onConnected: addConnectedListener, | ||
onInsertion: function(){ | ||
// TODO: remove in prod | ||
console.warn("can-dom-mutate: Use onConnected instead of onInsertion"); | ||
return addConnectedListener.apply(this, arguments); | ||
}, | ||
/** | ||
@@ -510,5 +584,28 @@ * @function can-dom-mutate.onAttributeChange onAttributeChange | ||
*/ | ||
onAttributeChange: addAttributeChangeListener | ||
onAttributeChange: addAttributeChangeListener, | ||
flushRecords: function(doc){ | ||
doc = doc || DOCUMENT(); | ||
var data = dataStore.get(doc); | ||
if(data) { | ||
if(data.domMutationTreeData && data.domMutationTreeData.observer) { | ||
var records = data.domMutationTreeData.observer.takeRecords(); | ||
handleTreeMutations(records); | ||
} | ||
// flush any synthetic records | ||
flushRecords(); | ||
} | ||
}, | ||
onNodeInserted: addNodeInsertedListener, | ||
onNodeRemoved: addNodeRemovedListener | ||
}; | ||
//!steal-remove-start | ||
if(process.env.NODE_ENV !== "production") { | ||
domMutate.dataStore = dataStore; | ||
} | ||
//!steal-remove-end | ||
module.exports = namespace.domMutate = domMutate; |
@@ -21,7 +21,7 @@ @module {{}} can-dom-mutate | ||
// onAttributeChange( documentElement, callback ), | ||
// onInsertion( documentElement, callback ), | ||
// onRemoval( documentElement, callback ), | ||
// onConnected( documentElement, callback ), | ||
// onDisconnected( documentElement, callback ), | ||
// onNodeAttributeChange( node, callback ), | ||
// onNodeInsertion( node, callback ), | ||
// onNodeRemoval( node, callback ) | ||
// onNodeConnected( node, callback ), | ||
// onNodeDisconnected( node, callback ) | ||
// } | ||
@@ -57,3 +57,3 @@ | ||
var teardown = domMutate.onNodeInsertion(element, ()=>{ | ||
var teardown = domMutate.onNodeConnected(element, ()=>{ | ||
console.log("element inserted!"); | ||
@@ -60,0 +60,0 @@ }); |
@@ -12,2 +12,4 @@ var unit = require('steal-qunit'); | ||
QUnit.test('inserted', function (assert) { | ||
// make sure QUnit writing out the page is fully finished. | ||
domMutate.flushRecords(); | ||
var done = assert.async(); | ||
@@ -19,3 +21,3 @@ var fixture = testUtils.getFixture(); | ||
childInsertedHandler, | ||
removeOnInsertionHandler; | ||
removeonConnectedHandler; | ||
@@ -30,4 +32,4 @@ // add inserted event to registry | ||
// clean up handler added by onInsertion | ||
removeOnInsertionHandler (); | ||
// clean up handler added by onConnected | ||
removeonConnectedHandler (); | ||
@@ -52,5 +54,5 @@ // remove inserted event from registry | ||
// listen for any element being inserted and run appropriate test | ||
var onNodeInsertionCount = 0; | ||
removeOnInsertionHandler = domMutate.onInsertion(document.documentElement, function () { | ||
switch(onNodeInsertionCount) { | ||
var onNodeConnectedCount = 0; | ||
removeonConnectedHandler = domMutate.onConnected(document.documentElement, function (arg) { | ||
switch(onNodeConnectedCount) { | ||
case 0: | ||
@@ -62,6 +64,6 @@ assert.equal(insertedEventCount, 1, 'inserted event should trigger for event.currentTarget'); | ||
assert.equal(insertedEventCount, 1, 'inserted event should NOT trigger for child of event.currentTarget'); | ||
setTimeout(cleanup, 50); | ||
setTimeout(cleanup, 100); | ||
break; | ||
} | ||
onNodeInsertionCount++; | ||
onNodeConnectedCount++; | ||
}); | ||
@@ -68,0 +70,0 @@ |
@@ -80,4 +80,4 @@ 'use strict'; | ||
attributes: makeMutationEvent('attributes', domMutate.onNodeAttributeChange), | ||
inserted: makeMutationEvent('inserted', domMutate.onNodeInsertion, false), | ||
removed: makeMutationEvent('removed', domMutate.onNodeRemoval) | ||
inserted: makeMutationEvent('inserted', domMutate.onNodeConnected, false), | ||
removed: makeMutationEvent('removed', domMutate.onNodeDisconnected) | ||
}; |
@@ -6,2 +6,3 @@ var unit = require('steal-qunit'); | ||
var testUtils = require('../test/test-utils'); | ||
var makeSimpleDocument = require("can-vdom/make-document/make-document"); | ||
@@ -11,2 +12,3 @@ var test = unit.test; | ||
var moduleWithoutMutationObserver = testUtils.moduleWithoutMutationObserver; | ||
var moduleMutationObserver = testUtils.moduleMutationObserver; | ||
var mock = testUtils.mock; | ||
@@ -20,2 +22,48 @@ | ||
unit.module("can-dom-mutate/node document selector"); | ||
test("isConnected() uses isConnected where available", function(assert) { | ||
assert.expect(4); | ||
var doc = getDocument(); | ||
var fakenode = { | ||
get isConnected() { | ||
assert.strictEqual(doc.constructor, getDocument().constructor, "with real document") | ||
return true; | ||
}, | ||
get ownerDocument() { | ||
if ('isConnected' in doc) { | ||
assert.notStrictEqual(doc.constructor, getDocument().constructor, "with SimpleDocument") | ||
} else { | ||
// IE 11 doesn't support isConnected, so both isConnected() calls will go through here | ||
assert.ok(true, "Native Node.prototype does not support isConnected"); | ||
} | ||
return null; | ||
} | ||
}; | ||
assert.ok(node.isConnected(fakenode), "Real document connected"); | ||
getDocument(makeSimpleDocument()); | ||
assert.ok(node.isConnected(fakenode), "SimpleDocument connected"); | ||
getDocument(doc); | ||
}); | ||
function onNodeRemovedTest(){ | ||
test('removeChild dispatches onNodeRemoved', function (assert) { | ||
var done = assert.async(); | ||
var parent = testUtils.getFixture(); | ||
var child = document.createElement('div'); | ||
parent.appendChild(child); | ||
var undo = domMutate.onNodeRemoved(child, function() { | ||
assert.ok(true, 'this was called'); | ||
undo(); | ||
done(); | ||
}); | ||
node.removeChild.call(parent, child); | ||
}); | ||
} | ||
moduleMutationObserver('can-dom-mutate/node with real document', getDocument(), onNodeRemovedTest); | ||
moduleMutationObserver('can-dom-mutate/node with SimpleDocument', makeSimpleDocument(), onNodeRemovedTest); | ||
moduleWithMutationObserver('can-dom-mutate/node', function () { | ||
@@ -75,4 +123,6 @@ QUnit.test('appendChild should not call domMutate.dispatchNodeInsertion', function (assert) { | ||
QUnit.test('setAttribute should not call domMutate.dispatchNodeAttributeChange', function (assert) { | ||
var parent = testUtils.getFixture(); | ||
var element = document.createElement('div'); | ||
var undo = neverCall(assert, domMutate, 'dispatchNodeAttributeChange'); | ||
parent.appendChild(element); | ||
@@ -86,4 +136,6 @@ node.setAttribute.call(element, 'data-foo', 'bar'); | ||
QUnit.test('removeAttribute should not call domMutate.dispatchNodeAttributeChange', function (assert) { | ||
var parent = testUtils.getFixture(); | ||
var element = document.createElement('div'); | ||
var undo = neverCall(assert, domMutate, 'dispatchNodeAttributeChange'); | ||
parent.appendChild(element); | ||
@@ -99,7 +151,8 @@ node.removeAttribute.call(element, 'data-foo'); | ||
moduleWithoutMutationObserver('can-dom-mutate/node', function () { | ||
function withoutMutationObserverTests () { | ||
QUnit.test('appendChild should call domMutate.dispatchNodeInsertion', function (assert) { | ||
var doc = getDocument(); | ||
var done = assert.async(); | ||
var parent = testUtils.getFixture(); | ||
var child = document.createElement('div'); | ||
var child = doc.createElement('div'); | ||
@@ -118,6 +171,7 @@ var undo = mock(domMutate, 'dispatchNodeInsertion', function (node, callback) { | ||
function getFragmentInsertionTest () { | ||
var fragment = document.createDocumentFragment(); | ||
var child1 = document.createElement('div'); | ||
var child2 = document.createElement('div'); | ||
var grandchild = document.createElement('div'); | ||
var doc = getDocument(); | ||
var fragment = doc.createDocumentFragment(); | ||
var child1 = doc.createElement('div'); | ||
var child2 = doc.createElement('div'); | ||
var grandchild = doc.createElement('div'); | ||
fragment.appendChild(child1); | ||
@@ -153,5 +207,6 @@ fragment.appendChild(child2); | ||
var done = assert.async(); | ||
var doc = getDocument(); | ||
var parent = testUtils.getFixture(); | ||
var sibling = document.createElement('span'); | ||
var child = document.createElement('div'); | ||
var sibling = doc.createElement('span'); | ||
var child = doc.createElement('div'); | ||
@@ -172,4 +227,5 @@ var undo = mock(domMutate, 'dispatchNodeInsertion', function (node, callback) { | ||
assert.expect(2); | ||
var doc = getDocument(); | ||
var parent = testUtils.getFixture(); | ||
var sibling = document.createElement('div'); | ||
var sibling = doc.createElement('div'); | ||
parent.appendChild(sibling); | ||
@@ -184,4 +240,5 @@ | ||
var done = assert.async(); | ||
var doc = getDocument(); | ||
var parent = testUtils.getFixture(); | ||
var child = document.createElement('div'); | ||
var child = doc.createElement('div'); | ||
@@ -202,5 +259,6 @@ var undo = mock(domMutate, 'dispatchNodeRemoval', function (node, callback) { | ||
var done = assert.async(); | ||
var doc = getDocument(); | ||
var parent = testUtils.getFixture(); | ||
var sibling = document.createElement('span'); | ||
var child = document.createElement('div'); | ||
var sibling = doc.createElement('span'); | ||
var child = doc.createElement('div'); | ||
var isSiblingRemoved = false; | ||
@@ -231,4 +289,5 @@ | ||
assert.expect(3); | ||
var doc = getDocument(); | ||
var parent = testUtils.getFixture(); | ||
var sibling = document.createElement('div'); | ||
var sibling = doc.createElement('div'); | ||
parent.appendChild(sibling); | ||
@@ -249,3 +308,4 @@ | ||
var done = assert.async(); | ||
var element = document.createElement('div'); | ||
var doc = getDocument(); | ||
var element = doc.createElement('div'); | ||
element.setAttribute('data-foo', 'bar'); | ||
@@ -268,3 +328,4 @@ | ||
var done = assert.async(); | ||
var element = document.createElement('div'); | ||
var doc = getDocument(); | ||
var element = doc.createElement('div'); | ||
element.setAttribute('data-foo', 'bar'); | ||
@@ -284,5 +345,8 @@ | ||
}); | ||
}); | ||
}; | ||
moduleWithoutMutationObserver('can-dom-mutate/node with real document', getDocument(), withoutMutationObserverTests); | ||
moduleWithoutMutationObserver('can-dom-mutate/node with SimpleDocument', makeSimpleDocument(), withoutMutationObserverTests); | ||
moduleWithoutMutationObserver('can-dom-mutate/node (not in document)', function () { | ||
function notInDocumentTests() { | ||
/* | ||
@@ -302,7 +366,8 @@ We do not want insertion events to dispatched when a node | ||
QUnit.test('appendChild should not call dispatchNodeInsertion', function (assert) { | ||
assert.expect(0); | ||
var fragment = document.createDocumentFragment(); | ||
var child = document.createElement('div'); | ||
var undo = mock(domMutate, 'dispatchNodeInsertion', function () { | ||
assert.ok(false, 'This should never be called'); | ||
assert.expect(1); | ||
var doc = getDocument(); | ||
var fragment = doc.createDocumentFragment(); | ||
var child = doc.createElement('div'); | ||
var undo = mock(domMutate, 'dispatchNodeInsertion', function (el, callback, dispatchConnected) { | ||
assert.equal(dispatchConnected, false, 'We should not dispatch connected things'); | ||
}); | ||
@@ -315,10 +380,11 @@ | ||
QUnit.test('insertBefore should not call dispatchNodeInsertion', function (assert) { | ||
assert.expect(0); | ||
var fragment = document.createDocumentFragment(); | ||
var child = document.createElement('div'); | ||
var reference = document.createElement('span'); | ||
assert.expect(1); | ||
var doc = getDocument(); | ||
var fragment = doc.createDocumentFragment(); | ||
var child = doc.createElement('div'); | ||
var reference = doc.createElement('span'); | ||
fragment.appendChild(reference); | ||
var undo = mock(domMutate, 'dispatchNodeInsertion', function () { | ||
assert.ok(false, 'This should never be called'); | ||
var undo = mock(domMutate, 'dispatchNodeInsertion', function (el, callback, dispatchConnected) { | ||
assert.equal(dispatchConnected, false, 'We should not dispatch connected things'); | ||
}); | ||
@@ -331,9 +397,10 @@ | ||
QUnit.test('removeChild should not call dispatchNodeRemoval', function (assert) { | ||
assert.expect(0); | ||
var fragment = document.createDocumentFragment(); | ||
var child = document.createElement('div'); | ||
assert.expect(1); | ||
var doc = getDocument(); | ||
var fragment = doc.createDocumentFragment(); | ||
var child = doc.createElement('div'); | ||
fragment.appendChild(child); | ||
var undo = mock(domMutate, 'dispatchNodeRemoval', function () { | ||
assert.ok(false, 'This should never be called'); | ||
var undo = mock(domMutate, 'dispatchNodeRemoval', function (el, callback, dispatchConnected) { | ||
assert.equal(dispatchConnected, false, 'We should not dispatch connected things'); | ||
}); | ||
@@ -346,13 +413,14 @@ | ||
QUnit.test('replaceChild should not call dispatchNodeRemoval+Insertion', function (assert) { | ||
assert.expect(0); | ||
var fragment = document.createDocumentFragment(); | ||
var child = document.createElement('div'); | ||
var oldChild = document.createElement('span'); | ||
assert.expect(2); | ||
var doc = getDocument(); | ||
var fragment = doc.createDocumentFragment(); | ||
var child = doc.createElement('div'); | ||
var oldChild = doc.createElement('span'); | ||
fragment.appendChild(oldChild); | ||
var undoRemoval = mock(domMutate, 'dispatchNodeRemoval', function () { | ||
assert.ok(false, 'This should never be called'); | ||
var undoRemoval = mock(domMutate, 'dispatchNodeRemoval', function (el, callback, dispatchConnected) { | ||
assert.equal(dispatchConnected, false, 'We should not dispatch connected things'); | ||
}); | ||
var undoInsertion = mock(domMutate, 'dispatchNodeInsertion', function () { | ||
assert.ok(false, 'This should never be called'); | ||
var undoInsertion = mock(domMutate, 'dispatchNodeInsertion', function (el, callback, dispatchConnected) { | ||
assert.equal(dispatchConnected, false, 'We should not dispatch connected things'); | ||
}); | ||
@@ -367,5 +435,6 @@ | ||
var done = assert.async(); | ||
var doc1 = document.implementation.createHTMLDocument('doc1'); | ||
var doc = getDocument(); | ||
var doc1 = doc.implementation.createHTMLDocument('doc1'); | ||
getDocument(doc1); | ||
var undo = domMutate.onNodeRemoval(doc1.documentElement, function() { | ||
var undo = domMutate.onNodeDisconnected(doc1.documentElement, function() { | ||
assert.ok(true, 'this was called'); | ||
@@ -378,4 +447,6 @@ undo(); | ||
node.removeChild.call(doc1, doc1.documentElement); | ||
getDocument(document); | ||
getDocument(doc); | ||
}); | ||
}); | ||
} | ||
moduleWithoutMutationObserver('can-dom-mutate/node (not in real document)', getDocument(), notInDocumentTests); | ||
moduleWithoutMutationObserver('can-dom-mutate/node (not in SimpleDocument)', makeSimpleDocument(), notInDocumentTests); |
@@ -8,17 +8,28 @@ 'use strict'; | ||
var isInDocument = util.isInDocument; | ||
var getParents = util.getParents; | ||
var contains = util.contains; | ||
var synthetic = { | ||
dispatchNodeInsertion: function (container, node) { | ||
if (isInDocument(node)) { | ||
domMutate.dispatchNodeInsertion(node); | ||
} | ||
}, | ||
dispatchNodeRemoval: function (container, node) { | ||
if (isInDocument(container) && !isInDocument(node)) { | ||
domMutate.dispatchNodeRemoval(node); | ||
} | ||
var isConnected; | ||
function getIsConnectedFromNode(node) { | ||
return node.isConnected; | ||
} | ||
function getIsConnectedFromDocument(node) { | ||
var doc = node.ownerDocument; | ||
// if node *is* the document, ownerDocument is null | ||
// However, CanSimpleDom implements this incorrectly, and a document's ownerDocument is itself, | ||
// so make both checks | ||
return doc === null || doc === node || contains(doc, node); | ||
} | ||
function setIsConnected(doc) { | ||
var node = doc.createTextNode(""); | ||
isConnected = 'isConnected' in node.constructor.prototype ? | ||
getIsConnectedFromNode : | ||
getIsConnectedFromDocument; | ||
if(mutate) { | ||
mutate.isConnected = isConnected; | ||
} | ||
}; | ||
} | ||
setIsConnected(globals.getKeyValue("document")); | ||
globals.onKeyValue("document", setIsConnected); | ||
@@ -29,5 +40,5 @@ var compat = { | ||
var result = this.replaceChild(newChild, oldChild); | ||
synthetic.dispatchNodeRemoval(this, oldChild); | ||
domMutate.dispatchNodeRemoval(oldChild, null, isConnected(this) && !isConnected(oldChild)); | ||
for (var i = 0; i < newChildren.length; i++) { | ||
synthetic.dispatchNodeInsertion(this, newChildren[i]); | ||
domMutate.dispatchNodeInsertion(newChildren[i], null, isConnected(this)); | ||
} | ||
@@ -67,3 +78,3 @@ return result; | ||
for (var i = 0; i < nodes.length; i++) { | ||
synthetic[dispatchMethod](this, nodes[i]); | ||
domMutate[dispatchMethod](nodes[i], null, isConnected(this) && (pair[1] === 'Removal' ? !isConnected(nodes[i]) : true)); | ||
} | ||
@@ -78,3 +89,7 @@ return result; | ||
normal[methodName] = function () { | ||
return this[methodName].apply(this, arguments); | ||
if(isConnected(this)) { | ||
return this[methodName].apply(this, arguments); | ||
} else { | ||
return compat[methodName].apply(this, arguments); | ||
} | ||
}; | ||
@@ -183,2 +198,3 @@ }); | ||
var strategy = observer ? normal : compat; | ||
for (var key in strategy) { | ||
@@ -193,2 +209,4 @@ mutate[key] = strategy[key]; | ||
mutate.isConnected = isConnected; | ||
module.exports = namespace.domMutateNode = domMutate.node = mutate; |
{ | ||
"name": "can-dom-mutate", | ||
"description": "Dispatch and listen for DOM mutations", | ||
"version": "1.3.7", | ||
"version": "2.0.0", | ||
"author": { | ||
@@ -16,6 +16,9 @@ "name": "DoneJS Team", | ||
"can-namespace": "1.0.0", | ||
"can-reflect": "^1.17.6" | ||
"can-reflect": "^1.17.6", | ||
"can-symbol": "^1.6.4" | ||
}, | ||
"devDependencies": { | ||
"can-dom-events": "^1.3.0", | ||
"can-simple-dom": "^1.6.2", | ||
"can-vdom": "^4.4.1", | ||
"fixpack": "^2.3.1", | ||
@@ -22,0 +25,0 @@ "jshint": "^2.9.1", |
@@ -8,2 +8,3 @@ var unit = require('steal-qunit'); | ||
var MUTATION_OBSERVER = require('can-globals/mutation-observer/mutation-observer'); | ||
var makeSimpleDocument = require("can-vdom/make-document/make-document"); | ||
@@ -13,9 +14,10 @@ var test = unit.test; | ||
moduleMutationObserver('can-dom-mutate', function () { | ||
test('onNodeInsertion should be called when that node is inserted', function (assert) { | ||
function mutationObserverTests() { | ||
test('onNodeConnected should be called when that node is inserted', function (assert) { | ||
var done = assert.async(); | ||
var doc = globals.getKeyValue('document'); | ||
var parent = testUtils.getFixture(); | ||
var child = document.createElement('div'); | ||
var child = doc.createElement('div'); | ||
var undo = domMutate.onNodeInsertion(child, function (mutation) { | ||
var undo = domMutate.onNodeConnected(child, function (mutation) { | ||
var node = mutation.target; | ||
@@ -32,8 +34,9 @@ assert.equal(node, child, 'Node should be the inserted child'); | ||
test('onNodeRemoval should be called when that node is removed', function (assert) { | ||
test('onNodeDisconnected should be called when that node is removed', function (assert) { | ||
var done = assert.async(); | ||
var doc = globals.getKeyValue('document'); | ||
var parent = testUtils.getFixture(); | ||
var child = document.createElement('div'); | ||
var child = doc.createElement('div'); | ||
var undo = domMutate.onNodeRemoval(child, function (mutation) { | ||
var undo = domMutate.onNodeDisconnected(child, function (mutation) { | ||
var node = mutation.target; | ||
@@ -52,3 +55,4 @@ assert.equal(node, child, 'Node should be the removed child'); | ||
var done = assert.async(); | ||
var child = document.createElement('div'); | ||
var doc = globals.getKeyValue('document'); | ||
var child = doc.createElement('div'); | ||
var attributeName = 'foo'; | ||
@@ -69,12 +73,15 @@ child.setAttribute(attributeName, 'bar'); | ||
test('onInserted should be called when any node is inserted', function (assert) { | ||
test('onConnected should be called when any node is inserted', function (assert) { | ||
var done = assert.async(); | ||
var parent = testUtils.getFixture(); | ||
var child = document.createElement('div'); | ||
var doc = globals.getKeyValue('document'); | ||
var child = doc.createElement('div'); | ||
var undo = domMutate.onInsertion(document.documentElement, function (mutation) { | ||
assert.equal(mutation.target, child, 'Node should be the inserted child'); | ||
var undo = domMutate.onConnected(doc.documentElement, function (mutation) { | ||
if(mutation.target === child) { | ||
assert.ok(true, 'Node should be the inserted child'); | ||
undo(); | ||
done(); | ||
undo(); | ||
done(); | ||
} | ||
}); | ||
@@ -89,8 +96,9 @@ | ||
var parent = testUtils.getFixture(); | ||
var fragment = document.createDocumentFragment(); | ||
var child1 = document.createElement('div'); | ||
var doc = globals.getKeyValue('document'); | ||
var fragment = doc.createDocumentFragment(); | ||
var child1 = doc.createElement('div'); | ||
child1.id = 'child1'; | ||
var child2 = document.createElement('div'); | ||
var child2 = doc.createElement('div'); | ||
child2.id = 'child2'; | ||
var grandchild = document.createElement('div'); | ||
var grandchild = doc.createElement('div'); | ||
grandchild.id = 'grandchild'; | ||
@@ -103,3 +111,3 @@ fragment.appendChild(child1); | ||
var nodes = [child1, child2, grandchild]; | ||
var undo = domMutate.onInsertion(document.documentElement, function (mutation) { | ||
var undo = domMutate.onConnected(doc.documentElement, function (mutation) { | ||
var target = mutation.target; | ||
@@ -127,12 +135,15 @@ if (nodes.indexOf(target) !== -1) { | ||
test('onRemoval should be called when any node is removed', function (assert) { | ||
test('onDisconnected should be called when any node is removed', function (assert) { | ||
var done = assert.async(); | ||
var doc = globals.getKeyValue('document'); | ||
var parent = testUtils.getFixture(); | ||
var child = document.createElement('div'); | ||
var child = doc.createElement('div'); | ||
var undo = domMutate.onRemoval(document.documentElement, function (mutation) { | ||
assert.equal(mutation.target, child, 'Node should be the removed child'); | ||
var undo = domMutate.onDisconnected(doc.documentElement, function (mutation) { | ||
if(mutation.target === child) { | ||
assert.ok(true, 'Node should be the removed child'); | ||
undo(); | ||
done(); | ||
undo(); | ||
done(); | ||
} | ||
}); | ||
@@ -144,10 +155,11 @@ | ||
test('onNodeInsertion should be called when that node is inserted into a different document', function(assert){ | ||
test('onNodeConnected should be called when that node is inserted into a different document', function(assert){ | ||
var done = assert.async(); | ||
var doc = globals.getKeyValue('document'); | ||
var parent = testUtils.getFixture(); | ||
var doc1 = document.implementation.createHTMLDocument('doc1'); | ||
var doc1 = doc.implementation.createHTMLDocument('doc1'); | ||
var child = doc1.createElement('div'); | ||
var undo = domMutate.onNodeInsertion(child, function (mutation) { | ||
var undo = domMutate.onNodeConnected(child, function (mutation) { | ||
var node = mutation.target; | ||
@@ -163,4 +175,5 @@ assert.equal(node, child, 'Node should be the inserted child'); | ||
test('onNodeRemoval does not leak when given a document fragment', function(assert){ | ||
var doc1 = document.implementation.createHTMLDocument('doc1'); | ||
test('onNodeDisconnected does not leak when given a document fragment', function(assert){ | ||
var doc = globals.getKeyValue('document'); | ||
var doc1 = doc.implementation.createHTMLDocument('doc1'); | ||
var frag = doc1.createDocumentFragment(); | ||
@@ -174,4 +187,4 @@ frag.appendChild(doc1.createElement('div')); | ||
DOCUMENT(doc1); | ||
domMutate.onNodeRemoval(frag, function() {}); | ||
DOCUMENT(document); | ||
domMutate.onNodeDisconnected(frag, function() {}); | ||
DOCUMENT(doc); | ||
@@ -181,10 +194,11 @@ assert.equal(getListenerCount(), previousListenerCount, "No new listeners added for this fragment"); | ||
test('onNodeInsertion should be called when textNode is inserted within a parent', function (assert) { | ||
test('onNodeConnected should be called when textNode is inserted within a parent', function (assert) { | ||
var done = assert.async(); | ||
var doc = globals.getKeyValue('document'); | ||
var parent = testUtils.getFixture(); | ||
var child = document.createTextNode("Hello World"); | ||
var wrapper = document.createElement("div"); | ||
var child = doc.createTextNode("Hello World"); | ||
var wrapper = doc.createElement("div"); | ||
wrapper.appendChild(child); | ||
var undo = domMutate.onNodeInsertion(child, function (mutation) { | ||
var undo = domMutate.onNodeConnected(child, function (mutation) { | ||
var node = mutation.target; | ||
@@ -200,9 +214,32 @@ assert.equal(node, child, 'Node should be the inserted child'); | ||
test('flushRecords works', function(assert){ | ||
var done = assert.async(); | ||
var doc = globals.getKeyValue('document'); | ||
var parent = testUtils.getFixture(); | ||
var wrapper = doc.createElement("div"); | ||
var called = false; | ||
domMutate.onNodeConnected(wrapper, function () { | ||
called = true; | ||
}); | ||
node.appendChild.call(parent, wrapper); | ||
domMutate.flushRecords(); | ||
assert.ok(called, "insertion run immediately"); | ||
setTimeout(done, 1); | ||
}); | ||
} | ||
moduleMutationObserver('can-dom-mutate with real document', DOCUMENT(), mutationObserverTests); | ||
testUtils.moduleWithMutationObserver('can-dom-mutate with real document', function() { | ||
test('changing the MutationObserver tears down the mutation observer', function (assert) { | ||
assert.expect(2); | ||
var doc = globals.getKeyValue('document'); | ||
var done = assert.async(); | ||
var parent = testUtils.getFixture(); | ||
var wrapper = document.createElement("div"); | ||
var wrapper = doc.createElement("div"); | ||
var undoA = domMutate.onNodeInsertion(wrapper, function () { | ||
var undoA = domMutate.onNodeConnected(wrapper, function () { | ||
assert.ok(true, "this will still be called b/c it's on the document"); | ||
@@ -212,3 +249,3 @@ undoA(); | ||
MUTATION_OBSERVER(MUTATION_OBSERVER()); | ||
var undoB = domMutate.onNodeInsertion(wrapper, function (mutation) { | ||
var undoB = domMutate.onNodeConnected(wrapper, function (mutation) { | ||
var node = mutation.target; | ||
@@ -224,1 +261,3 @@ assert.equal(node, wrapper, 'Node should be the inserted child'); | ||
}); | ||
moduleMutationObserver('can-dom-mutate with SimpleDocument', makeSimpleDocument(), mutationObserverTests); | ||
@@ -14,9 +14,36 @@ var unit = require('steal-qunit'); | ||
function moduleWithoutMutationObserver (title, tests) { | ||
function moduleWithoutMutationObserver (title, doc, tests) { | ||
var hooks = { | ||
beforeEach: function () { | ||
globals.setKeyValue(mutationObserverKey, null); | ||
globals.setKeyValue('document', doc); | ||
if(doc === document) { | ||
this.fixture = document.getElementById("qunit-fixture"); | ||
} else { | ||
this.fixture = doc.createElement("div"); | ||
this.fixture.setAttribute("id", "qunit-fixture"); | ||
doc.body.appendChild(this.fixture); | ||
} | ||
}, | ||
afterEach: function () { | ||
afterEach: function (assert) { | ||
globals.deleteKeyValue(mutationObserverKey); | ||
if(doc !== document) { | ||
doc.body.removeChild(this.fixture); | ||
} | ||
var done = assert.async(); | ||
setTimeout(function() { | ||
globals.deleteKeyValue('document'); | ||
var fixture = document.getElementById("qunit-fixture"); | ||
while (fixture && fixture.hasChildNodes()) { | ||
fixture.removeChild(fixture.lastChild); | ||
} | ||
done(); | ||
}, 10); | ||
} | ||
@@ -28,5 +55,7 @@ }; | ||
function moduleMutationObserver (title, tests) { | ||
moduleWithMutationObserver(title, tests); | ||
moduleWithoutMutationObserver(title, tests); | ||
function moduleMutationObserver (title, doc, tests) { | ||
if (doc === document) { | ||
moduleWithMutationObserver(title, tests); | ||
} | ||
moduleWithoutMutationObserver(title, doc, tests); | ||
} | ||
@@ -43,3 +72,3 @@ | ||
function getFixture () { | ||
return document.getElementById('qunit-fixture'); | ||
return globals.getKeyValue('document').getElementById('qunit-fixture'); | ||
} | ||
@@ -46,0 +75,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 2 instances in 1 package
Debug access
Supply chain riskUses debug, reflection and dynamic code execution features.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
792547
23
14275
4
9
1
12
9
+ Addedcan-symbol@^1.6.4