Comparing version 0.7.1 to 0.8.0
{ | ||
"name": "fastdom", | ||
"description": "Eliminates layout thrashing by batching DOM read/write operations", | ||
"version": "0.7.1", | ||
"version": "0.8.0", | ||
"main": "index.js", | ||
@@ -14,3 +14,4 @@ "scripts": [ | ||
], | ||
"license": "MIT" | ||
"license": "MIT", | ||
"_source": "git@github.com/wilsonpage/fastdom.git" | ||
} |
{ | ||
"name": "fastdom", | ||
"description": "Eliminates layout thrashing by batching DOM read/write operations", | ||
"version": "0.7.1", | ||
"version": "0.8.0", | ||
"main": "index.js", | ||
@@ -15,2 +15,2 @@ "scripts": [ | ||
"license": "MIT" | ||
} | ||
} |
0.8.0 / 2013-10-14 | ||
================== | ||
* change to a rAF loop technique of emtying frame queue to prevent frame conflicts | ||
* add ability to call `FastDom#defer` with no frame argument to schedule job for next free frame | ||
0.7.1 / 2013-10-05 | ||
@@ -14,2 +20,2 @@ ================== | ||
* remove `FastDom#clearWrite` | ||
* change directory structure by removing `/lib` | ||
* change directory structure by removing `/lib` |
279
index.js
/** | ||
* DOM-Batch | ||
* FastDom | ||
* | ||
@@ -41,7 +41,7 @@ * Eliminates layout thrashing | ||
function FastDom() { | ||
this.frames = []; | ||
this.lastId = 0; | ||
this.jobs = {}; | ||
this.mode = null; | ||
this.pending = false; | ||
this.queue = { | ||
this.batch = { | ||
hash: {}, | ||
read: [], | ||
@@ -53,4 +53,5 @@ write: [] | ||
/** | ||
* Adds a job to | ||
* the read queue. | ||
* Adds a job to the | ||
* write batch and schedules | ||
* a new frame if need be. | ||
* | ||
@@ -62,4 +63,12 @@ * @param {Function} fn | ||
var job = this.add('read', fn, ctx); | ||
this.queue.read.push(job.id); | ||
this.request('read'); | ||
this.batch.read.push(job.id); | ||
// If we're writing and a 'read' job | ||
// comes in, we do have to schedule a new frame | ||
var needsFrame = !this.batchPending || this.mode === 'writing'; | ||
// Schedule a new frame if need be | ||
if (needsFrame) this.scheduleBatch(); | ||
return job.id; | ||
@@ -69,4 +78,5 @@ }; | ||
/** | ||
* Adds a job to | ||
* the write queue. | ||
* Adds a job to the | ||
* write batch and schedules | ||
* a new frame if need be. | ||
* | ||
@@ -78,4 +88,16 @@ * @param {Function} fn | ||
var job = this.add('write', fn, ctx); | ||
this.queue.write.push(job.id); | ||
this.request('write'); | ||
this.batch.write.push(job.id); | ||
// If we're emptying the read | ||
// batch and a write comes in, | ||
// we don't need to schedule a | ||
// new frame. If we're writing | ||
// and write comes in we don't | ||
// need to schedule a new frame | ||
var needsFrame = !this.batchPending; | ||
// Schedule a new frame if need be | ||
if (needsFrame) this.scheduleBatch(); | ||
return job.id; | ||
@@ -85,5 +107,38 @@ }; | ||
/** | ||
* Removes a job from | ||
* the 'reads' queue. | ||
* Defers the given job | ||
* by the number of frames | ||
* specified. | ||
* | ||
* If no frames are given | ||
* then the job is run in | ||
* the next free frame. | ||
* | ||
* @param {Number} frame | ||
* @param {Function} fn | ||
* @api public | ||
*/ | ||
FastDom.prototype.defer = function(frame, fn, ctx) { | ||
// Accepts two arguments | ||
if (typeof frame === 'function') { | ||
ctx = fn; | ||
fn = frame; | ||
frame = 1; | ||
} | ||
var self = this; | ||
var index = frame - 1; | ||
return this.schedule(index, function() { | ||
self.run({ | ||
fn: fn, | ||
ctx: ctx | ||
}); | ||
}); | ||
}; | ||
/** | ||
* Clears a scheduled 'read', | ||
* 'write' or 'defer' job. | ||
* | ||
* @param {Number} id | ||
@@ -93,16 +148,16 @@ * @api public | ||
FastDom.prototype.clear = function(id) { | ||
var job = this.jobs[id]; | ||
if (!job) return; | ||
// Clear reference | ||
delete this.jobs[id]; | ||
// Defer jobs are cleared differently | ||
if (job.type === 'defer') { | ||
caf(job.timer); | ||
return; | ||
if (typeof id === 'function') { | ||
return this.clearFrame(id); | ||
} | ||
var list = this.queue[job.type]; | ||
var job = this.batch.hash[id]; | ||
if (!job) return; | ||
var list = this.batch[job.type]; | ||
var index = list.indexOf(id); | ||
// Clear references | ||
delete this.batch.hash[id]; | ||
if (~index) list.splice(index, 1); | ||
@@ -112,39 +167,30 @@ }; | ||
/** | ||
* Makes the decision as to | ||
* whether a the frame needs | ||
* to be scheduled. | ||
* Clears a scheduled frame. | ||
* | ||
* @param {String} type | ||
* @param {Function} frame | ||
* @api private | ||
*/ | ||
FastDom.prototype.request = function(type) { | ||
var mode = this.mode; | ||
FastDom.prototype.clearFrame = function(frame) { | ||
var index = this.frames.indexOf(frame); | ||
if (~index) this.frames.splice(index, 1); | ||
}; | ||
/** | ||
* Schedules a new read/write | ||
* batch if one isn't pending. | ||
* | ||
* @api private | ||
*/ | ||
FastDom.prototype.scheduleBatch = function() { | ||
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 (mode === 'writing' && type === 'write') return; | ||
// Schedule batch for next frame | ||
this.schedule(0, function() { | ||
self.runBatch(); | ||
self.batchPending = false; | ||
}); | ||
// 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 (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 (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(); }); | ||
// Set flag to indicate | ||
// a frame has been scheduled | ||
this.pending = true; | ||
this.batchPending = true; | ||
}; | ||
@@ -178,3 +224,3 @@ | ||
while (id = list.shift()) { | ||
this.run(this.jobs[id]); | ||
this.run(this.batch.hash[id]); | ||
} | ||
@@ -189,13 +235,8 @@ }; | ||
*/ | ||
FastDom.prototype.frame = function() { | ||
FastDom.prototype.runBatch = function() { | ||
// Set the pending flag to | ||
// false so that any new requests | ||
// that come in will schedule a new frame | ||
this.pending = false; | ||
// Set the mode to 'reading', | ||
// then empty all read jobs | ||
this.mode = 'reading'; | ||
this.flush(this.queue.read); | ||
this.flush(this.batch.read); | ||
@@ -205,3 +246,3 @@ // Set the mode to 'writing' | ||
this.mode = 'writing'; | ||
this.flush(this.queue.write); | ||
this.flush(this.batch.write); | ||
@@ -212,30 +253,4 @@ this.mode = null; | ||
/** | ||
* Defers the given job | ||
* by the number of frames | ||
* specified. | ||
* | ||
* @param {Number} frames | ||
* @param {Function} fn | ||
* @api public | ||
*/ | ||
FastDom.prototype.defer = function(frames, fn, ctx) { | ||
if (frames < 0) return; | ||
var job = this.add('defer', fn, ctx); | ||
var self = this; | ||
(function wrapped() { | ||
if (!(frames--)) { | ||
self.run(job); | ||
return; | ||
} | ||
job.timer = raf(wrapped); | ||
})(); | ||
return job.id; | ||
}; | ||
/** | ||
* Adds a new job to | ||
* the given queue. | ||
* the given batch. | ||
* | ||
@@ -250,3 +265,3 @@ * @param {Array} list | ||
var id = this.uniqueId(); | ||
return this.jobs[id] = { | ||
return this.batch.hash[id] = { | ||
id: id, | ||
@@ -260,13 +275,4 @@ fn: fn, | ||
/** | ||
* Called when a callback errors. | ||
* Overwrite this if you don't | ||
* want errors inside your jobs | ||
* to fail silently. | ||
* Runs a given job. | ||
* | ||
* @param {Error} | ||
*/ | ||
FastDom.prototype.onError = function(){}; | ||
/** | ||
* Runs a given job. | ||
* @param {Object} job | ||
@@ -279,10 +285,71 @@ * @api private | ||
// Clear reference to the job | ||
delete this.jobs[job.id]; | ||
delete this.batch.hash[job.id]; | ||
// Call the job in | ||
try { job.fn.call(ctx); } catch(e) { | ||
this.onError(e); | ||
if (this.quiet) { | ||
try { job.fn.call(ctx); } catch (e) {} | ||
} else { | ||
job.fn.call(ctx); | ||
} | ||
}; | ||
/** | ||
* Starts of a rAF loop | ||
* to empty the frame queue. | ||
* | ||
* @api private | ||
*/ | ||
FastDom.prototype.loop = function() { | ||
var self = this; | ||
// Don't start more than one loop | ||
if (this.looping) return; | ||
raf(function frame() { | ||
var fn = self.frames.shift(); | ||
// Run the frame | ||
if (fn) fn(); | ||
// If no more frames, | ||
// stop looping | ||
if (!self.frames.length) { | ||
self.looping = false; | ||
return; | ||
} | ||
raf(frame); | ||
}); | ||
this.looping = true; | ||
}; | ||
/** | ||
* Adds a function to | ||
* a specified index | ||
* of the frame queue. | ||
* | ||
* @param {Number} index | ||
* @param {Function} fn | ||
* @return {Function} | ||
*/ | ||
FastDom.prototype.schedule = function(index, fn) { | ||
// Make sure this slot | ||
// hasn't already been | ||
// taken. If it has, try | ||
// re-scheduling for the next slot | ||
if (this.frames[index]) { | ||
return this.schedule(index + 1, fn); | ||
} | ||
// Start the rAF | ||
// loop to empty | ||
// the frame queue | ||
this.loop(); | ||
// Insert this function into | ||
// the frames queue and return | ||
return this.frames[index] = fn; | ||
}; | ||
// We only ever want there to be | ||
@@ -296,5 +363,5 @@ // one instance of FastDom in an app | ||
if (typeof exports === "object") { | ||
if (typeof module !== 'undefined' && module.exports) { | ||
module.exports = fastdom; | ||
} else if (typeof define === "function" && define.amd) { | ||
} else if (typeof define === 'function' && define.amd) { | ||
define(function(){ return fastdom; }); | ||
@@ -305,2 +372,2 @@ } else { | ||
})(window.fastdom); | ||
})(window.fastdom); |
{ | ||
"name": "fastdom", | ||
"description": "Eliminates layout thrashing by batching DOM read/write operations", | ||
"version": "0.7.1", | ||
"version": "0.8.0", | ||
"main": "index.js", | ||
"scripts": { | ||
"test": "./node_modules/.bin/mocha-phantomjs test/index.html" | ||
"test": "jshint . && mocha-phantomjs test/index.html" | ||
}, | ||
@@ -21,2 +21,3 @@ "homepage": "https://github.com/wilsonpage/fastdom", | ||
"mocha": "~1.12.0", | ||
"jshint": "~2.1.0", | ||
"sinon": "~1.7.3", | ||
@@ -23,0 +24,0 @@ "chai": "~1.7.2", |
@@ -6,4 +6,2 @@ # fastdom [![Build Status](https://travis-ci.org/wilsonpage/fastdom.png?branch=master)](https://travis-ci.org/wilsonpage/fastdom) | ||
```js | ||
var fastdom = new FastDom(); | ||
fastdom.read(function() { | ||
@@ -37,2 +35,3 @@ console.log('read'); | ||
- [Animation example](http://wilsonpage.github.io/fastdom/examples/animation.html) | ||
- [Aspect ratio example](http://wilsonpage.github.io/fastdom/examples/aspect-ratio.html) | ||
@@ -44,9 +43,9 @@ | ||
``` | ||
``` sh | ||
$ npm install fastdom | ||
``` | ||
``` | ||
``` sh | ||
$ bower install fastdom | ||
``` | ||
``` | ||
``` sh | ||
$ component install wilsonpage/fastdom | ||
@@ -64,3 +63,3 @@ ``` | ||
Potentially a third-party library could depend on FastDom, and better intrgrate within an app that itself uses it. | ||
Potentially a third-party library could depend on FastDom, and better integrate within an app that itself uses it. | ||
@@ -89,3 +88,3 @@ ## API | ||
### FastDom#defer(frames, callback[, context]) | ||
### FastDom#defer([frames,] callback[, context]) | ||
@@ -100,2 +99,15 @@ Defers a job for the number of frames specified. This is useful is you have a particualrly expensive piece of work to do, and don't want it to be done with all the other work. | ||
`FastDom#defer` can also be called with no `frames` argument to push work onto next avaiable frame. | ||
```js | ||
// Runs in frame 1 | ||
fastdom.defer(expensiveStuff1); | ||
// Runs in frame 2 | ||
fastdom.defer(expensiveStuff2); | ||
// Runs in frame 3 | ||
fastdom.defer(expensiveStuff3); | ||
``` | ||
### FastDom#clear(id) | ||
@@ -119,3 +131,3 @@ | ||
``` | ||
``` sh | ||
$ npm install | ||
@@ -148,2 +160,2 @@ $ npm test | ||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
14031
10
333
154
5