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

Add data shim so jQuery's data functions also respect domData. #43

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
9 changes: 9 additions & 0 deletions can-jquery.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,15 @@ if ($) {
// when using domEvents.dispatch/domEvents.trigger.
var domDispatch = domEvents.dispatch;
domEvents.dispatch = function(event, args) {
if(typeof event === "object" && !Object.getOwnPropertyDescriptor(event, "type")) {
// Some native events break jQuery dispatch by having non-enumerable
// type properties.
Object.defineProperty(event, "type", {
configurable: true,
enumerable: true,
value: event.type
});
}
if (!specialEvents[event] && !nativeDispatchEvents[event]) {
$(this).trigger(event, args);
} else {
Expand Down
63 changes: 63 additions & 0 deletions can-jquery_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ var canEvent = require("can-event");
require("can-util/dom/events/inserted/inserted");
require("can-util/dom/events/removed/removed");
require("can-stache-bindings");
require("can-jquery/data");

QUnit.module("can-jquery/legacy - can-controls", {
setup: function() {
Expand Down Expand Up @@ -446,3 +447,65 @@ QUnit.test("should call correct `removed` handler when one is removed", function
fixture.append($el);
$el.remove();
});

QUnit.module("can-jquery/legacy - data functions", {
setup: function() {
enableLegacyMode($);
this.$div = $("<div />").appendTo("#qunit-fixture");
},
teardown: function() {
disableLegacyMode();
}
});

QUnit.test("data() compatibility with can-util/dom/data/", function() {
domData.set.call(this.$div[0], "foo", "bar");
QUnit.equal(this.$div.data("foo"), "bar");

this.$div.data("foo", "baz");
QUnit.equal(domData.get.call(this.$div[0], "foo"), "baz");
});

QUnit.test("data() returns full data object from domData.get() with no arguments", function() {
domData.set.call(this.$div[0], "foo", "bar");
QUnit.deepEqual(this.$div.data(), {"foo" : "bar"});

QUnit.deepEqual($.data(this.$div[0]), {"foo" : "bar"});
});

QUnit.test("data() destructures objects before passing to domData.set", function() {
this.$div.data({"foo": "baz"});
QUnit.equal(domData.get.call(this.$div[0], "foo"), "baz");
});

QUnit.test("hasData() checks both jQuery data and domData", function() {
domData.set.call(this.$div[0], "foo", "bar");
$.data(this.$div[0], "quux", "thud");
QUnit.ok($.hasData(this.$div[0], "foo"));
QUnit.ok($.hasData(this.$div[0], "quux"));
});

QUnit.test("removeData() also calls domData.clean", function() {
domData.set.call(this.$div[0], "foo", "bar");
domData.set.call(this.$div[0], "quux", "thud");

this.$div.removeData("foo");
QUnit.ok(!domData.get.call(this.$div[0], "foo"));
QUnit.equal(domData.get.call(this.$div[0], "quux"), "thud");

this.$div.removeData(); // remove all remaining data;
QUnit.ok(!domData.get.call(this.$div[0], "quux"));

// Repeat for static $.removeData
domData.set.call(this.$div[0], "foo", "bar");
domData.set.call(this.$div[0], "quux", "thud");

$.removeData(this.$div[0], "foo");
QUnit.ok(!domData.get.call(this.$div[0], "foo"));
QUnit.equal(domData.get.call(this.$div[0], "quux"), "thud");

$.removeData(this.$div[0]); // remove all remaining data;
QUnit.ok(!domData.get.call(this.$div[0], "quux"));
});


106 changes: 106 additions & 0 deletions data.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/* global module, require */
var $ = require("can-jquery");
var domData = require("can-util/dom/data/data");
var each = require("can-util/js/each/each");
var assign = require("can-util/js/assign/assign");

module.exports = $;

var oldData = $.data;
var oldFnData = $.fn.data;
$.fn.data = function() {
var args = arguments,
ret = oldFnData.apply(this, arguments);

if(arguments.length < 1) {
// get all (from the first element in the jQ)
assign(ret, domData.get.call(this[0]));
return ret;
} else if(arguments.length === 1 && typeof arguments[0] === "string") {
// get named property
if(ret != null) {
return ret;
} else {
return this.get().reduce(function(val, el) {
return val != null ? val : domData.get.apply(el, args);
}, null);
}
} else {
// set
this.each(function(i, el) {
if(typeof args[0] === "string") {
domData.set.apply(el, args);
} else {
each(args[0], function(val, key) {
domData.set.call(el, key, val);
});
}
});
return ret;
}
};

$.data = function() {
var elem = arguments[0],
args = [].slice.call(arguments, 1),
ret = oldData.apply(this, arguments);

if(arguments.length < 2) {
// get all
assign(ret, domData.get.call(elem));
return ret;
} else if(arguments.length === 2 && typeof arguments[1] === "string") {
// get named property
return ret != null ? ret : domData.get.apply(elem, args);
} else {
if(typeof args[0] === "string") {
domData.set.apply(elem, args);
} else {
each(args[0], function(val, key) {
domData.set.call(elem, key, val);
});
}
return ret;
}
};

var oldHasData = $.hasData;
$.hasData = function() {
var elem = arguments[0],
args = [].slice.call(arguments, 1);
return oldHasData.apply(this, arguments) || domData.get.call(elem).hasOwnProperty(args[0]);
};

var oldRemoveData = $.removeData;
var oldFnRemoveData = $.fn.removeData;

$.fn.removeData = function() {
var args = arguments,
ret = oldFnRemoveData.apply(this, arguments);

this.each(function(i, el) {
if(typeof args[0] === "string") {
domData.clean.apply(el, args);
} else {
each(domData.get.call(el), function(val, key) {
domData.clean.call(el, key);
});
}
});
return ret;
};

$.removeData = function() {
var elem = arguments[0],
args = [].slice.call(arguments, 1),
ret = oldRemoveData.apply(this, arguments);

if(typeof args[0] === "string") {
domData.clean.apply(elem, args);
} else {
each(domData.get.call(elem), function(val, key) {
domData.clean.call(elem, key);
});
}
return ret;
};
31 changes: 31 additions & 0 deletions docs/data.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
@module {jQuery} can-jquery/data can-jquery/data
@parent can-jquery.modules

@description Integrates the data functions of CanJS and jQuery.


Importing can-jquery/data will return the [jQuery object](http://api.jquery.com/jquery/). It will also import all non-legacy [can-jquery] features for event binding and dispatch.

```js
var $ = require("can-jquery/data");
```

@body

Importing `can-jquery/data` will also bring in [can-jquery], but also has the side effect of enabling jQuery data functions synchronizing with the [can-util/dom/data/data CanJS data store] and vice versa.

This means that data set via the [can-util/dom/data/data.set `domData.set`] function will be available when calling [`jQuery.data()`](https://api.jquery.com/jquery.data/) or [`jQuery.fn.data()`](https://api.jquery.com/data/) as a getter; conversely, using `jQuery.data()` or `jQuery.fn.data()` as a setter and calling [can-util/dom/data/data.get `domData.get`] will again make the set data available on get.

```js
var $ = require("can-jquery/data");
var domData = require("");

var $el = $("<div></div>");

$el.data("foo", "bar");
domData.get.call(el[0], "foo") // -> "bar"

domData.set.call(el[0], { baz: "quux" });
$el.data(); // -> { "foo": "bar", "baz": "quux" }
```