summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWilson Page <wilsonpage@me.com>2013-09-17 03:03:19 -0700
committerWilson Page <wilsonpage@me.com>2013-09-17 03:03:19 -0700
commit70e0e69c155b00a41350ebd40e83e8b82a15f42a (patch)
treead799a772e84f5a14a6489d60b47736ec534cdb0
parentea5ec64119d5f8ac07180491b9043a361d66ceb0 (diff)
parent70e85478eb9df9b27f7a3f418b32c4f4f64ad157 (diff)
downloadfastdom-70e0e69c155b00a41350ebd40e83e8b82a15f42a.zip
fastdom-70e0e69c155b00a41350ebd40e83e8b82a15f42a.tar.gz
fastdom-70e0e69c155b00a41350ebd40e83e8b82a15f42a.tar.bz2
Merge pull request #8 from wilsonpage/dev
Change the way callback context is stored and scheduled jobs are cleared (closes #7)
-rw-r--r--bower.json2
-rw-r--r--component.json2
-rw-r--r--lib/fastdom.js132
-rw-r--r--package.json2
-rw-r--r--test/setup.js8
-rw-r--r--test/test.clear.js16
-rw-r--r--test/test.set.js37
7 files changed, 129 insertions, 70 deletions
diff --git a/bower.json b/bower.json
index a12556b..d9eb9c9 100644
--- a/bower.json
+++ b/bower.json
@@ -1,7 +1,7 @@
{
"name": "fastdom",
"description": "Eliminates layout thrashing by batching DOM read/write operations",
- "version": "0.4.2",
+ "version": "0.5.0",
"main": "lib/fastdom.js",
"scripts": [
"lib/fastdom.js"
diff --git a/component.json b/component.json
index 56ebe0f..c73a26f 100644
--- a/component.json
+++ b/component.json
@@ -1,7 +1,7 @@
{
"name": "fastdom",
"description": "Eliminates layout thrashing by batching DOM read/write operations",
- "version": "0.4.2",
+ "version": "0.5.0",
"main": "lib/fastdom.js",
"scripts": [
"lib/fastdom.js"
diff --git a/lib/fastdom.js b/lib/fastdom.js
index 6cba5c0..60b38d8 100644
--- a/lib/fastdom.js
+++ b/lib/fastdom.js
@@ -33,10 +33,12 @@
* @constructor
*/
function FastDom() {
- this.reads = [];
- this.writes = [];
+ this.lastId = 0;
+ this.jobs = {};
this.mode = null;
this.pending = false;
+ this.reads = [];
+ this.writes = [];
}
/**
@@ -47,8 +49,9 @@
* @api public
*/
FastDom.prototype.read = function(fn, ctx) {
- add(this.reads, fn, ctx);
- this.request('read');
+ var id = this._add(this.reads, fn, ctx);
+ this._request('read');
+ return id;
};
/**
@@ -59,30 +62,31 @@
* @api public
*/
FastDom.prototype.write = function(fn, ctx) {
- add(this.writes, fn, ctx);
- this.request('write');
+ var id = this._add(this.writes, fn, ctx);
+ this._request('write');
+ return id;
};
/**
* Removes a job from
* the 'reads' queue.
*
- * @param {Function} fn
+ * @param {Number} id
* @api public
*/
- FastDom.prototype.clearRead = function(fn) {
- remove(this.reads, fn);
+ FastDom.prototype.clearRead = function(id) {
+ this._remove(this.reads, id);
};
/**
* Removes a job from
* the 'writes' queue.
*
- * @param {Function} fn
+ * @param {Number} id
* @api public
*/
- FastDom.prototype.clearWrite = function(fn) {
- remove(this.writes, fn);
+ FastDom.prototype.clearWrite = function(id) {
+ this._remove(this.writes, id);
};
/**
@@ -93,31 +97,32 @@
* @param {String} type
* @api private
*/
- FastDom.prototype.request = function(type) {
+ FastDom.prototype._request = function(type) {
+ var mode = this.mode;
var self = this;
// If we are currently writing, we don't
// need to scedule a new frame as this
// job will be emptied from the write queue
- if (this.mode === 'writing' && type === 'write') return;
+ if (mode === 'writing' && type === 'write') return;
// If we are reading we don't need to schedule
// a new frame as this read will be emptied
// in the currently active read queue
- if (this.mode === 'reading' && type === 'read') return;
+ if (mode === 'reading' && type === 'read') return;
// If we are reading we don't need to schedule
// a new frame and this write job will be run
// after the read queue has been emptied in the
// currently active frame.
- if (this.mode === 'reading' && type === 'write') return;
+ if (mode === 'reading' && type === 'write') return;
// If there is already a frame
// scheduled, don't schedule another one
if (this.pending) return;
// Schedule frame (preserving context)
- raf(function() { self.frame(); });
+ raf(function() { self._frame(); });
// Set flag to indicate
// a frame has been scheduled
@@ -125,6 +130,16 @@
};
/**
+ * Generates a unique
+ * id for a job.
+ *
+ * @return {Number}
+ */
+ FastDom.prototype._uniqueId = function() {
+ return ++this.lastId;
+ };
+
+ /**
* Calls each job in
* the list passed.
*
@@ -136,18 +151,16 @@
* @param {Array} list
* @api private
*/
- FastDom.prototype.run = function(list) {
- var fn;
+ FastDom.prototype._run = function(list) {
var ctx;
+ var job;
+ var id;
- while (list.length) {
- fn = list.shift();
- ctx = fn._dbctx || this;
- try {
- fn.call(ctx);
- } catch (err) {
- // TODO: console.error if options.silent === false.
- }
+ while (id = list.shift()) {
+ job = this.jobs[id];
+ ctx = job.ctx || this;
+ delete this.jobs[id];
+ try { job.fn.call(ctx); } catch (e) {}
}
};
@@ -157,7 +170,7 @@
*
* @api private
*/
- FastDom.prototype.frame = function() {
+ FastDom.prototype._frame = function() {
// Set the pending flag to
// false so that any new requests
@@ -167,12 +180,12 @@
// Set the mode to 'reading',
// then empty all read jobs
this.mode = 'reading';
- this.run(this.reads);
+ this._run(this.reads);
// Set the mode to 'writing'
// then empty all write jobs
this.mode = 'writing';
- this.run(this.writes);
+ this._run(this.writes);
this.mode = null;
};
@@ -182,8 +195,6 @@
* by the number of frames
* specified.
*
- * TODO:
- *
* @param {Function} fn
* @param {Number} frames
* @api public
@@ -191,7 +202,7 @@
FastDom.prototype.defer = function(fn, frames) {
(function wrapped() {
if (frames-- === 0) {
- try { fn(); } catch (e){}
+ try { fn(); } catch (e) {}
} else {
raf(wrapped);
}
@@ -199,40 +210,45 @@
};
/**
- * Util
- */
-
- /**
- * Adds a function to
- * the given array.
+ * Adds a new job to
+ * the given queue.
*
- * If a context is given
- * it is stored on the
- * function object for
- * later.
- *
- * @param {Array} array
+ * @param {Array} list
* @param {Function} fn
* @param {Object} ctx
+ * @returns {Number} id
* @api private
*/
- function add(array, fn, ctx) {
- if (ctx) fn._dbctx = ctx;
- array.push(fn);
- }
+ FastDom.prototype._add = function(list, fn, ctx) {
+ var id = this._uniqueId();
+
+ // Store this job
+ this.jobs[id] = {
+ fn: fn,
+ ctx: ctx
+ };
+
+ // Push the id of
+ // this job into
+ // the given queue
+ list.push(id);
+
+ // Return the id
+ return id;
+ };
/**
- * Removes a function
- * from the given array.
- *
- * @param {Array} array
- * @param {Function} item
+ * Removes a job from
+ * the given queue.
+ * @param {Array} list
+ * @param {Number} id
* @api private
*/
- function remove(array, fn) {
- var index = array.indexOf(fn);
- if (~index) array.splice(index, 1);
- }
+ FastDom.prototype._remove = function(list, id) {
+ var index = list.indexOf(id);
+ if (~index) list.splice(index, 1);
+ delete this.jobs[id];
+ };
/**
* Expose 'FastDom'
diff --git a/package.json b/package.json
index f7a28a5..5022496 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "fastdom",
"description": "Eliminates layout thrashing by batching DOM read/write operations",
- "version": "0.4.2",
+ "version": "0.5.0",
"main": "lib/fastdom.js",
"scripts": {
"test": "./node_modules/.bin/mocha-phantomjs test/index.html"
diff --git a/test/setup.js b/test/setup.js
index bfe48f1..0844695 100644
--- a/test/setup.js
+++ b/test/setup.js
@@ -9,4 +9,10 @@ var raf = window.requestAnimationFrame
var FastDom = fastdom.constructor;
// Alias chai.assert
-var assert = chai.assert; \ No newline at end of file
+var assert = chai.assert;
+
+function objectLength(object) {
+ var l = 0;
+ for (var key in object) l++;
+ return l;
+} \ No newline at end of file
diff --git a/test/test.clear.js b/test/test.clear.js
index be273a4..6b274fe 100644
--- a/test/test.clear.js
+++ b/test/test.clear.js
@@ -5,8 +5,8 @@ suite('Clear', function(){
var fastdom = new FastDom();
var read = sinon.spy();
- fastdom.read(read);
- fastdom.clearRead(read);
+ var id = fastdom.read(read);
+ fastdom.clearRead(id);
raf(function() {
assert(!read.called);
@@ -19,8 +19,8 @@ suite('Clear', function(){
var read = sinon.spy();
var read2 = sinon.spy();
- fastdom.read(read);
- fastdom.clearRead(read2);
+ var id = fastdom.read(read);
+ fastdom.clearRead(id);
raf(function() {
assert(!read2.called);
@@ -33,9 +33,9 @@ suite('Clear', function(){
var read = sinon.spy();
var write = sinon.spy();
- fastdom.write(write);
+ var id = fastdom.write(write);
fastdom.read(function() {
- fastdom.clearWrite(write);
+ fastdom.clearWrite(id);
raf(function() {
assert(!read.called);
@@ -47,9 +47,9 @@ suite('Clear', function(){
test("Should not run 'write' job if cleared", function(done) {
var fastdom = new FastDom();
var write = sinon.spy();
+ var id = fastdom.write(write);
- fastdom.write(write);
- fastdom.clearWrite(write);
+ fastdom.clearWrite(id);
raf(function() {
assert(!write.called);
diff --git a/test/test.set.js b/test/test.set.js
index c8d9989..62f7e5f 100644
--- a/test/test.set.js
+++ b/test/test.set.js
@@ -124,4 +124,41 @@ suite('Set', function() {
done();
}, ctx);
});
+
+ test("Should have empty job hash when batch complete", function(done) {
+ var fastdom = new FastDom();
+
+ fastdom.read(function(){});
+ fastdom.read(function(){});
+ fastdom.write(function(){});
+ fastdom.write(function(){});
+
+ // Check there are four jobs stored
+ assert.equal(objectLength(fastdom.jobs), 4);
+
+ raf(function() {
+ assert.equal(objectLength(fastdom.jobs), 0);
+ done();
+ });
+ });
+
+ test("Should maintain correct context if single method is registered twice", function(done) {
+ var fastdom = new FastDom();
+ var ctx1 = { foo: 'bar' };
+ var ctx2 = { bar: 'baz' };
+
+ function shared(){}
+
+ var spy1 = sinon.spy(shared);
+ var spy2 = sinon.spy(shared);
+
+ fastdom.read(spy1, ctx1);
+ fastdom.read(spy2, ctx2);
+
+ raf(function() {
+ spy1.calledOn(ctx1);
+ spy2.calledOn(ctx2);
+ done();
+ });
+ });
}); \ No newline at end of file