Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use existing add/removeEventListener for elements #146

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 61 additions & 49 deletions can-define.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,16 +83,9 @@ module.exports = define = ns.define = function(objPrototype, defines, baseDefine
return data;
});

// Mixin the event methods
define.mixinEvents(objPrototype);

// Add necessary event methods to this object.
for (var prop in eventsProto) {
Object.defineProperty(objPrototype, prop, {
enumerable: false,
value: eventsProto[prop],
configurable: true,
writable: true
});
}
// add so instance defs can be dynamically added
Object.defineProperty(objPrototype,"_define",{
enumerable: false,
Expand Down Expand Up @@ -177,7 +170,7 @@ define.property = function(objPrototype, prop, definition, dataInitializers, com
if ((definition.value !== undefined || definition.Value !== undefined)) {
getInitialValue = make.get.defaultValue(prop, definition, typeConvert, eventsSetter);
}

// If property has a getter, create the compute that stores its data.
if (definition.get) {
computedInitializers[prop] = make.compute(prop, definition.get, getInitialValue);
Expand Down Expand Up @@ -281,7 +274,7 @@ make = {
if (newVal !== current) {
setData.call(this, newVal);

canEvent.dispatch.call(this, {
canEvent.trigger.call(this, {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should make this .emit eventually to comply with event-emitter like apis.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This a problem, using .trigger makes computes not work (because domDispatch calls the handlers like DOM event handlers and canEvent.dispatch calls them like compute handler).

So I'm not sure how to fix this without checking to see if the event is for a property or not.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think you should also make this listen able via .on 👍

type: prop,
target: this
}, [newVal, current]);
Expand Down Expand Up @@ -643,47 +636,12 @@ replaceWith = function(obj, prop, cb, writable) {
eventsProto = assign({}, event);
assign(eventsProto, {
_eventSetup: function() {},
_eventTeardown: function() {},
addEventListener: function(eventName, handler) {

var computedBinding = this._computed && this._computed[eventName];
if (computedBinding && computedBinding.compute) {
if (!computedBinding.count) {
computedBinding.count = 1;
computedBinding.compute.addEventListener("change", computedBinding.handler);
} else {
computedBinding.count++;
}

}

return eventLifecycle.addAndSetup.apply(this, arguments);
},

// ### unbind
// Stops listening to an event.
// If this is the last listener of a computed property,
// stop forwarding events of the computed property to this map.
removeEventListener: function(eventName, handler) {
var computedBinding = this._computed && this._computed[eventName];
if (computedBinding) {
if (computedBinding.count === 1) {
computedBinding.count = 0;
computedBinding.compute.removeEventListener("change", computedBinding.handler);
} else {
computedBinding.count--;
}

}

return eventLifecycle.removeAndTeardown.apply(this, arguments);

}
_eventTeardown: function() {}
});
eventsProto.on = eventsProto.bind = eventsProto.addEventListener;
eventsProto.off = eventsProto.unbind = eventsProto.removeEventListener;

delete eventsProto.one;
delete eventsProto.addEventListener;
delete eventsProto.removeEventListener;

define.setup = function(props, sealed) {
defineConfigurableAndNotEnumerable(this, "_cid");
Expand Down Expand Up @@ -719,6 +677,60 @@ define.setup = function(props, sealed) {
};
define.replaceWith = replaceWith;
define.eventsProto = eventsProto;
define.mixinEvents = function(objPrototype, makeEnumerable){
// Add necessary event methods to this object.
for (var prop in eventsProto) {
Object.defineProperty(objPrototype, prop, {
enumerable: !!makeEnumerable,
value: eventsProto[prop],
configurable: true,
writable: true
});
}

var baseAddEventListener = objPrototype.addEventListener ||
canEvent.addEventListener;
var baseRemoveEventListener = objPrototype.removeEventListener ||
canEvent.removeEventListener;

objPrototype.addEventListener = function(eventName, handler) {
var computedBinding = this._computed && this._computed[eventName];
if (computedBinding && computedBinding.compute) {
if (!computedBinding.count) {
computedBinding.count = 1;
computedBinding.compute.addEventListener("change", computedBinding.handler);
} else {
computedBinding.count++;
}
}

return baseAddEventListener.apply(this, arguments);
};

// ### unbind
// Stops listening to an event.
// If this is the last listener of a computed property,
// stop forwarding events of the computed property to this map.
objPrototype.removeEventListener = function(eventName, handler) {
var computedBinding = this._computed && this._computed[eventName];
if (computedBinding) {
if (computedBinding.count === 1) {
computedBinding.count = 0;
computedBinding.compute.removeEventListener("change", computedBinding.handler);
} else {
computedBinding.count--;
}

}

return baseRemoveEventListener.apply(this, arguments);
};

eventLifecycle(objPrototype);

objPrototype.on = objPrototype.bind = objPrototype.addEventListener;
objPrototype.off = objPrototype.unbind = objPrototype.removeEventListener;
};
define.defineConfigurableAndNotEnumerable = defineConfigurableAndNotEnumerable;
define.make = make;
define.getDefinitionOrMethod = getDefinitionOrMethod;
Expand Down
32 changes: 32 additions & 0 deletions define-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ var define = require("can-define");
var stache = require("can-stache");
var CanList = require("can-list");
var canBatch = require("can-event/batch/batch");
var domDispatch = require("can-util/dom/dispatch/dispatch");
var isArray = require("can-util/js/is-array/is-array");
var each = require("can-util/js/each/each");
var types = require("can-types");
Expand Down Expand Up @@ -1504,3 +1505,34 @@ QUnit.test('setter with default value causes an infinite loop (#142)', function(
var a = new A();
QUnit.equal(a.val, 'hello', 'creating an instance should not cause an inifinte loop');
});

QUnit.test('Works with DOM elements', function(){
var el = document.createElement('div');
define(el, { foo: 'string' });

var fooCompute = compute(function(){
return el.foo;
});

var events = 0;
el.addEventListener('foo', function(){
events++;
});

el.addEventListener('some-event', function(){
events++;
});

fooCompute.on('change', function(){
QUnit.ok(true, "change was called");
QUnit.start();
});

QUnit.stop();

el.foo = 'bar';
QUnit.equal(events, 1, 'An event occurred');

domDispatch.call(el, "some-event");
QUnit.equal(events, 2, 'Another event');
});
10 changes: 2 additions & 8 deletions list/list.js
Original file line number Diff line number Diff line change
Expand Up @@ -1056,14 +1056,8 @@ assign(DefineList.prototype, {


// Add necessary event methods to this object.
for (var prop in define.eventsProto) {
DefineList[prop] = define.eventsProto[prop];
Object.defineProperty(DefineList.prototype, prop, {
enumerable: false,
value: define.eventsProto[prop],
writable: true
});
}
define.mixinEvents(DefineList, true);
define.mixinEvents(DefineList.prototype);

Object.defineProperty(DefineList.prototype, "length", {
get: function() {
Expand Down
13 changes: 4 additions & 9 deletions map/map.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ var setProps = function(props, remove) {
}
else if( ("replace" in curVal) && isArray(newVal)) {
curVal.replace(newVal);
}
}
else if( ("set" in curVal) && (isPlainObject(newVal) || isArray(newVal))) {
curVal.set(newVal, remove);
}
Expand Down Expand Up @@ -259,14 +259,9 @@ var DefineMap = Construct.extend("DefineMap",{
});

// Add necessary event methods to this object.
for(var prop in define.eventsProto) {
DefineMap[prop] = define.eventsProto[prop];
Object.defineProperty(DefineMap.prototype, prop, {
enumerable:false,
value: define.eventsProto[prop],
writable: true
});
}
define.mixinEvents(DefineMap, true);
define.mixinEvents(DefineMap.prototype);

types.DefineMap = DefineMap;
types.DefaultMap = DefineMap;

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"can-cid": "^1.0.0",
"can-compute": "^3.0.0",
"can-construct": "^3.0.6",
"can-event": "^3.0.1",
"can-event": "^3.1.0",
"can-namespace": "^1.0.0",
"can-observation": "^3.0.1",
"can-types": "^1.0.1",
Expand Down