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

Commit acf095d

Browse files
committed
fix(jqLite): have same expando format as jQuery
1 parent 301d8f2 commit acf095d

File tree

3 files changed

+89
-43
lines changed

3 files changed

+89
-43
lines changed

src/jqLite.js

+42-30
Original file line numberDiff line numberDiff line change
@@ -186,8 +186,8 @@ function JQLiteDealoc(element){
186186
}
187187

188188
function JQLiteUnbind(element, type, fn) {
189-
var events = JQLiteData(element, 'events'),
190-
handle = JQLiteData(element, 'handle');
189+
var events = JQLiteExpandoStore(element, 'events'),
190+
handle = JQLiteExpandoStore(element, 'handle');
191191

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

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

209209
function JQLiteRemoveData(element) {
210-
var cacheId = element[jqName],
211-
cache = jqCache[cacheId];
210+
var expandoId = element[jqName],
211+
expandoStore = jqCache[expandoId];
212212

213-
if (cache) {
214-
if (cache.handle) {
215-
cache.events.$destroy && cache.handle({}, '$destroy');
213+
if (expandoStore) {
214+
if (expandoStore.handle) {
215+
expandoStore.events.$destroy && expandoStore.handle({}, '$destroy');
216216
JQLiteUnbind(element);
217217
}
218-
delete jqCache[cacheId];
218+
delete jqCache[expandoId];
219219
element[jqName] = undefined; // ie does not allow deletion of attributes on elements.
220220
}
221221
}
222222

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

227227
if (isDefined(value)) {
228-
if (!cache) {
229-
element[jqName] = cacheId = jqNextId();
230-
cache = jqCache[cacheId] = {};
228+
if (!expandoStore) {
229+
element[jqName] = expandoId = jqNextId();
230+
expandoStore = jqCache[expandoId] = {};
231231
}
232-
cache[key] = value;
232+
expandoStore[key] = value;
233+
} else {
234+
return expandoStore && expandoStore[key];
235+
}
236+
}
237+
238+
function JQLiteData(element, key, value) {
239+
var data = JQLiteExpandoStore(element, 'data'),
240+
isSetter = isDefined(value),
241+
keyDefined = !isSetter && isDefined(key),
242+
isSimpleGetter = keyDefined && !isObject(key);
243+
244+
if (!data && !isSimpleGetter) {
245+
JQLiteExpandoStore(element, 'data', data = {});
246+
}
247+
248+
if (isSetter) {
249+
data[key] = value;
233250
} else {
234-
if (isDefined(key)) {
235-
if (isObject(key)) {
236-
if (!cacheId) element[jqName] = cacheId = jqNextId();
237-
jqCache[cacheId] = cache = (jqCache[cacheId] || {});
238-
extend(cache, key);
251+
if (keyDefined) {
252+
if (isSimpleGetter) {
253+
// don't create data in this case.
254+
return data && data[key];
239255
} else {
240-
return cache ? cache[key] : undefined;
256+
extend(data, key);
241257
}
242258
} else {
243-
if (!cacheId) element[jqName] = cacheId = jqNextId();
244-
245-
return cache
246-
? cache
247-
: cache = jqCache[cacheId] = {};
259+
return data;
248260
}
249261
}
250262
}
@@ -583,11 +595,11 @@ forEach({
583595
dealoc: JQLiteDealoc,
584596

585597
bind: function bindFn(element, type, fn){
586-
var events = JQLiteData(element, 'events'),
587-
handle = JQLiteData(element, 'handle');
598+
var events = JQLiteExpandoStore(element, 'events'),
599+
handle = JQLiteExpandoStore(element, 'handle');
588600

589-
if (!events) JQLiteData(element, 'events', events = {});
590-
if (!handle) JQLiteData(element, 'handle', handle = createEventHandler(element, events));
601+
if (!events) JQLiteExpandoStore(element, 'events', events = {});
602+
if (!handle) JQLiteExpandoStore(element, 'handle', handle = createEventHandler(element, events));
591603

592604
forEach(type.split(' '), function(type){
593605
var eventFns = events[type];

test/ngMock/angular-mocksSpec.js

+30-6
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,18 @@ describe('ngMock', function() {
353353
return keys.sort();
354354
}
355355

356+
function browserTrigger(element, eventType) {
357+
element = element[0];
358+
if (document.createEvent) {
359+
var event = document.createEvent('MouseEvents');
360+
event.initMouseEvent(eventType, true, true, window, 0, 0, 0, 0, 0, false, false,
361+
false, false, 0, element);
362+
element.dispatchEvent(event);
363+
} else {
364+
element.fireEvent('on' + eventType);
365+
}
366+
}
367+
356368
it('should remove data', function() {
357369
expect(angular.element.cache).toEqual({});
358370
var div = angular.element('<div></div>');
@@ -364,17 +376,29 @@ describe('ngMock', function() {
364376

365377
it('should deregister event handlers', function() {
366378
expect(keys(angular.element.cache)).toEqual([]);
367-
379+
var log = '';
368380
var div = angular.element('<div></div>');
369381

370-
div.bind('click', angular.noop);
371-
div.bind('mousemove', angular.noop);
372-
div.data('some', 'data');
373-
expect(keys(angular.element.cache).length).toBe(1);
382+
// crazy IE9 requires div to be connected to render DOM for click event to work
383+
// mousemove works even when not connected. This is a heisen-bug since stepping
384+
// through the code makes the test pass. Viva IE!!!
385+
angular.element(document.body).append(div)
386+
387+
div.bind('click', function() { log += 'click1;'});
388+
div.bind('click', function() { log += 'click2;'});
389+
div.bind('mousemove', function() { log += 'mousemove;'});
390+
391+
browserTrigger(div, 'click');
392+
browserTrigger(div, 'mousemove');
393+
expect(log).toEqual('click1;click2;mousemove;');
394+
log = '';
374395

375396
angular.mock.clearDataCache();
397+
398+
browserTrigger(div, 'click');
399+
browserTrigger(div, 'mousemove');
400+
expect(log).toEqual('');
376401
expect(keys(angular.element.cache)).toEqual([]);
377-
expect(div.data('some')).toBeUndefined();
378402

379403
div.remove();
380404
});

test/testabilityPatch.js

+17-7
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,14 @@ afterEach(function() {
4040

4141
// complain about uncleared jqCache references
4242
var count = 0;
43-
forEachSorted(jqCache, function(value, key){
44-
count ++;
45-
forEach(value, function(value, key){
43+
44+
// This line should be enabled as soon as this bug is fixed: http://bugs.jquery.com/ticket/11775
45+
//var cache = jqLite.cache;
46+
var cache = JQLite.cache;
47+
48+
forEachSorted(cache, function(expando, key){
49+
forEach(expando.data, function(value, key){
50+
count ++;
4651
if (value.$element) {
4752
dump('LEAK', key, value.$id, sortedHtml(value.$element));
4853
} else {
@@ -57,20 +62,25 @@ afterEach(function() {
5762

5863

5964
function dealoc(obj) {
65+
var jqCache = jqLite.cache;
6066
if (obj) {
6167
if (isElement(obj)) {
62-
var element = obj;
63-
if (element.nodeName) element = jqLite(element);
64-
if (element.dealoc) element.dealoc();
68+
cleanup(jqLite(obj));
6569
} else {
6670
for(var key in jqCache) {
6771
var value = jqCache[key];
68-
if (value.$scope == obj) {
72+
if (value.data && value.data.$scope == obj) {
6973
delete jqCache[key];
7074
}
7175
}
7276
}
77+
}
7378

79+
function cleanup(element) {
80+
element.unbind().removeData();
81+
for ( var i = 0, children = element.children() || []; i < children.length; i++) {
82+
cleanup(jqLite(children[i]));
83+
}
7484
}
7585
}
7686

0 commit comments

Comments
 (0)