Skip to content
This repository has been archived by the owner on Apr 12, 2024. It is now read-only.

Commit

Permalink
fix(jqLite): have same expando format as jQuery
Browse files Browse the repository at this point in the history
  • Loading branch information
mhevery committed May 17, 2012
1 parent 301d8f2 commit acf095d
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 43 deletions.
72 changes: 42 additions & 30 deletions src/jqLite.js
Expand Up @@ -186,8 +186,8 @@ function JQLiteDealoc(element){
} }


function JQLiteUnbind(element, type, fn) { function JQLiteUnbind(element, type, fn) {
var events = JQLiteData(element, 'events'), var events = JQLiteExpandoStore(element, 'events'),
handle = JQLiteData(element, 'handle'); handle = JQLiteExpandoStore(element, 'handle');


if (!handle) return; //no listeners registered if (!handle) return; //no listeners registered


Expand All @@ -207,44 +207,56 @@ function JQLiteUnbind(element, type, fn) {
} }


function JQLiteRemoveData(element) { function JQLiteRemoveData(element) {
var cacheId = element[jqName], var expandoId = element[jqName],
cache = jqCache[cacheId]; expandoStore = jqCache[expandoId];


if (cache) { if (expandoStore) {
if (cache.handle) { if (expandoStore.handle) {
cache.events.$destroy && cache.handle({}, '$destroy'); expandoStore.events.$destroy && expandoStore.handle({}, '$destroy');
JQLiteUnbind(element); JQLiteUnbind(element);
} }
delete jqCache[cacheId]; delete jqCache[expandoId];
element[jqName] = undefined; // ie does not allow deletion of attributes on elements. element[jqName] = undefined; // ie does not allow deletion of attributes on elements.
} }
} }


function JQLiteData(element, key, value) { function JQLiteExpandoStore(element, key, value) {
var cacheId = element[jqName], var expandoId = element[jqName],
cache = jqCache[cacheId || -1]; expandoStore = jqCache[expandoId || -1];


if (isDefined(value)) { if (isDefined(value)) {
if (!cache) { if (!expandoStore) {
element[jqName] = cacheId = jqNextId(); element[jqName] = expandoId = jqNextId();
cache = jqCache[cacheId] = {}; expandoStore = jqCache[expandoId] = {};
} }
cache[key] = value; expandoStore[key] = value;
} else {
return expandoStore && expandoStore[key];
}
}

function JQLiteData(element, key, value) {
var data = JQLiteExpandoStore(element, 'data'),
isSetter = isDefined(value),
keyDefined = !isSetter && isDefined(key),
isSimpleGetter = keyDefined && !isObject(key);

if (!data && !isSimpleGetter) {
JQLiteExpandoStore(element, 'data', data = {});
}

if (isSetter) {
data[key] = value;
} else { } else {
if (isDefined(key)) { if (keyDefined) {
if (isObject(key)) { if (isSimpleGetter) {
if (!cacheId) element[jqName] = cacheId = jqNextId(); // don't create data in this case.
jqCache[cacheId] = cache = (jqCache[cacheId] || {}); return data && data[key];
extend(cache, key);
} else { } else {
return cache ? cache[key] : undefined; extend(data, key);
} }
} else { } else {
if (!cacheId) element[jqName] = cacheId = jqNextId(); return data;

return cache
? cache
: cache = jqCache[cacheId] = {};
} }
} }
} }
Expand Down Expand Up @@ -583,11 +595,11 @@ forEach({
dealoc: JQLiteDealoc, dealoc: JQLiteDealoc,


bind: function bindFn(element, type, fn){ bind: function bindFn(element, type, fn){
var events = JQLiteData(element, 'events'), var events = JQLiteExpandoStore(element, 'events'),
handle = JQLiteData(element, 'handle'); handle = JQLiteExpandoStore(element, 'handle');


if (!events) JQLiteData(element, 'events', events = {}); if (!events) JQLiteExpandoStore(element, 'events', events = {});
if (!handle) JQLiteData(element, 'handle', handle = createEventHandler(element, events)); if (!handle) JQLiteExpandoStore(element, 'handle', handle = createEventHandler(element, events));


forEach(type.split(' '), function(type){ forEach(type.split(' '), function(type){
var eventFns = events[type]; var eventFns = events[type];
Expand Down
36 changes: 30 additions & 6 deletions test/ngMock/angular-mocksSpec.js
Expand Up @@ -353,6 +353,18 @@ describe('ngMock', function() {
return keys.sort(); return keys.sort();
} }


function browserTrigger(element, eventType) {
element = element[0];
if (document.createEvent) {
var event = document.createEvent('MouseEvents');
event.initMouseEvent(eventType, true, true, window, 0, 0, 0, 0, 0, false, false,
false, false, 0, element);
element.dispatchEvent(event);
} else {
element.fireEvent('on' + eventType);
}
}

it('should remove data', function() { it('should remove data', function() {
expect(angular.element.cache).toEqual({}); expect(angular.element.cache).toEqual({});
var div = angular.element('<div></div>'); var div = angular.element('<div></div>');
Expand All @@ -364,17 +376,29 @@ describe('ngMock', function() {


it('should deregister event handlers', function() { it('should deregister event handlers', function() {
expect(keys(angular.element.cache)).toEqual([]); expect(keys(angular.element.cache)).toEqual([]);

var log = '';
var div = angular.element('<div></div>'); var div = angular.element('<div></div>');


div.bind('click', angular.noop); // crazy IE9 requires div to be connected to render DOM for click event to work
div.bind('mousemove', angular.noop); // mousemove works even when not connected. This is a heisen-bug since stepping
div.data('some', 'data'); // through the code makes the test pass. Viva IE!!!
expect(keys(angular.element.cache).length).toBe(1); angular.element(document.body).append(div)

div.bind('click', function() { log += 'click1;'});
div.bind('click', function() { log += 'click2;'});
div.bind('mousemove', function() { log += 'mousemove;'});

browserTrigger(div, 'click');
browserTrigger(div, 'mousemove');
expect(log).toEqual('click1;click2;mousemove;');
log = '';


angular.mock.clearDataCache(); angular.mock.clearDataCache();

browserTrigger(div, 'click');
browserTrigger(div, 'mousemove');
expect(log).toEqual('');
expect(keys(angular.element.cache)).toEqual([]); expect(keys(angular.element.cache)).toEqual([]);
expect(div.data('some')).toBeUndefined();


div.remove(); div.remove();
}); });
Expand Down
24 changes: 17 additions & 7 deletions test/testabilityPatch.js
Expand Up @@ -40,9 +40,14 @@ afterEach(function() {


// complain about uncleared jqCache references // complain about uncleared jqCache references
var count = 0; var count = 0;
forEachSorted(jqCache, function(value, key){
count ++; // This line should be enabled as soon as this bug is fixed: http://bugs.jquery.com/ticket/11775
forEach(value, function(value, key){ //var cache = jqLite.cache;
var cache = JQLite.cache;

forEachSorted(cache, function(expando, key){
forEach(expando.data, function(value, key){
count ++;
if (value.$element) { if (value.$element) {
dump('LEAK', key, value.$id, sortedHtml(value.$element)); dump('LEAK', key, value.$id, sortedHtml(value.$element));
} else { } else {
Expand All @@ -57,20 +62,25 @@ afterEach(function() {




function dealoc(obj) { function dealoc(obj) {
var jqCache = jqLite.cache;
if (obj) { if (obj) {
if (isElement(obj)) { if (isElement(obj)) {
var element = obj; cleanup(jqLite(obj));
if (element.nodeName) element = jqLite(element);
if (element.dealoc) element.dealoc();
} else { } else {
for(var key in jqCache) { for(var key in jqCache) {
var value = jqCache[key]; var value = jqCache[key];
if (value.$scope == obj) { if (value.data && value.data.$scope == obj) {
delete jqCache[key]; delete jqCache[key];
} }
} }
} }
}


function cleanup(element) {
element.unbind().removeData();
for ( var i = 0, children = element.children() || []; i < children.length; i++) {
cleanup(jqLite(children[i]));
}
} }
} }


Expand Down

0 comments on commit acf095d

Please sign in to comment.