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

Commit

Permalink
perf($mdMedia): use matchMedia#addListener instead of window resize l…
Browse files Browse the repository at this point in the history
…istener
  • Loading branch information
ajoslin committed Jan 27, 2015
1 parent 6333b72 commit 8a0411f
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 103 deletions.
36 changes: 15 additions & 21 deletions src/core/util/media.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,19 @@ angular.module('material.core')
* @example $mdMedia('(min-width: 1200px)') == true if device-width >= 1200px
* @example $mdMedia('max-width: 300px') == true if device-width <= 300px (sanitizes input, adding parens)
*/
function mdMediaFactory($mdConstant, $mdUtil, $rootScope, $window) {
var queriesCache = $mdUtil.cacheFactory('$mdMedia:queries', {capacity: 15});
var resultsCache = $mdUtil.cacheFactory('$mdMedia:results', {capacity: 15});

angular.element($window).on('resize', updateAll);
function mdMediaFactory($mdConstant, $rootScope, $window, $cacheFactory) {
var queries = {};
var results = {};

return $mdMedia;

function $mdMedia(query) {
var validated = queriesCache.get(query);
var validated = queries[query];
if (angular.isUndefined(validated)) {
validated = queriesCache.put(query, validate(query));
validated = queries[query] = validate(query);
}

var result = resultsCache.get(validated);
var result = results[validated];
if (angular.isUndefined(result)) {
result = add(validated);
}
Expand All @@ -38,20 +36,16 @@ function mdMediaFactory($mdConstant, $mdUtil, $rootScope, $window) {
}

function add(query) {
return resultsCache.put(query, !!$window.matchMedia(query).matches);
var result = $window.matchMedia(query);
result.addListener(onQueryChange);
return (results[result.media] = !!result.matches);
}

function updateAll() {
var keys = resultsCache.keys();
var len = keys.length;

if (len) {
for (var i = 0; i < len; i++) {
add(keys[i]);
}

// Trigger a $digest() if not already in progress
$rootScope.$evalAsync();
}
function onQueryChange() {
var query = this;
$rootScope.$evalAsync(function() {
results[query.media] = !!query.matches;
});
}

}
64 changes: 28 additions & 36 deletions src/core/util/media.spec.js
Original file line number Diff line number Diff line change
@@ -1,67 +1,59 @@
describe('$mdMedia', function() {
var matchMediaResult;
var queriesCache;
var resultsCache;
var listeners;

function runListeners() {
listeners.forEach(function(cb) {
cb.context.matches = matchMediaResult;
cb.call(cb.context, cb);
});
}

beforeEach(module('material.core'));

beforeEach(inject(function($cacheFactory, $mdMedia, $window) {
matchMediaResult = false;

queriesCache = $cacheFactory.get('$mdMedia:queries');
resultsCache = $cacheFactory.get('$mdMedia:results');

spyOn($window, 'matchMedia').andCallFake(function() {
return {matches: matchMediaResult};
listeners = [];

spyOn($window, 'matchMedia').andCallFake(function(media) {
return {
media: media,
matches: matchMediaResult,
addListener: function(listener) {
listener.context = this;
listeners.push(listener);
}
};
});
}));

afterEach(function() {
queriesCache.removeAll();
resultsCache.removeAll();
});


it('should look up queries in `$mdConstant.MEDIA`', inject(
function($mdConstant, $mdMedia, $window) {
$mdConstant.MEDIA.somePreset = 'someQuery';

$mdMedia('somePreset');
expect($window.matchMedia).toHaveBeenCalledWith('someQuery');

delete($mdConstant.MEDIA.somePreset);
delete $mdConstant.MEDIA.somePreset;
}
));

it('should look up validated queries in `queriesCache`', inject(function($mdMedia, $window) {
queriesCache.put('originalQuery', 'validatedQuery');

$mdMedia('originalQuery');
expect($window.matchMedia).toHaveBeenCalledWith('validatedQuery');
}));

it('should validate queries', inject(function($mdMedia, $window) {
$mdMedia('something');
expect($window.matchMedia).toHaveBeenCalledWith('(something)');
}));

it('should cache validated queries in `queriesCache`', inject(function($mdMedia) {
$mdMedia('query');
expect(queriesCache.get('query')).toBe('(query)');
}));
it('should return cached results if available', inject(function($mdMedia, $window) {
expect($window.matchMedia.callCount).toBe(0);

it('should return cached results if available', inject(function($mdMedia) {
resultsCache.put('(query)', 'result');
expect($mdMedia('(query)')).toBe('result');
}));
expect($mdMedia('query')).toBe(false);
expect($window.matchMedia.callCount).toBe(1);

it('should cache results in `resultsCache`', inject(function($mdMedia) {
$mdMedia('(query)');
expect(resultsCache.get('(query)')).toBe(false);
expect($mdMedia('query')).toBe(false);
expect($window.matchMedia.callCount).toBe(1);
}));

it('should recalculate on resize', inject(function($mdMedia, $window) {
it('should change result when listener is called', inject(function($mdMedia, $window, $timeout) {
matchMediaResult = true;
expect($mdMedia('query')).toBe(true);
expect($window.matchMedia.callCount).toBe(1);
Expand All @@ -73,9 +65,9 @@ describe('$mdMedia', function() {
expect($mdMedia('query')).toBe(true);
expect($window.matchMedia.callCount).toBe(1);

angular.element($window).triggerHandler('resize');
runListeners();
$timeout.flush();

expect($mdMedia('query')).toBe(false);
expect($window.matchMedia.callCount).toBe(2);
}));
});
47 changes: 1 addition & 46 deletions src/core/util/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
var nextUniqueId = ['0','0','0'];

angular.module('material.core')
.factory('$mdUtil', function($cacheFactory, $document, $timeout) {
.factory('$mdUtil', function($document, $timeout) {
var Util;

return Util = {
Expand Down Expand Up @@ -48,11 +48,6 @@ angular.module('material.core')
};
},

/**
* @see cacheFactory below
*/
cacheFactory: cacheFactory,

// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds.
Expand Down Expand Up @@ -183,46 +178,6 @@ angular.module('material.core')
}
};


/*
* Inject a 'keys()' method into Angular's $cacheFactory. Then
* head-hook all other methods
*
*/
function cacheFactory(id, options) {
var cache = $cacheFactory(id, options);
var keys = {};

cache._put = cache.put;
cache.put = function(k,v) {
keys[k] = true;
return cache._put(k, v);
};

cache._remove = cache.remove;
cache.remove = function(k) {
delete keys[k];
return cache._remove(k);
};

cache._removeAll = cache.removeAll;
cache.removeAll = function() {
keys = {};
return cache._removeAll();
};

cache._destroy = cache.destroy;
cache.destroy = function() {
keys = {};
return cache._destroy();
};

cache.keys = function() {
return Object.keys(keys);
};

return cache;
}
});

/*
Expand Down

0 comments on commit 8a0411f

Please sign in to comment.