diff options
| author | yo mama <pepper@scannerjammer.com> | 2014-12-04 14:09:17 -0800 |
|---|---|---|
| committer | yo mama <pepper@scannerjammer.com> | 2014-12-04 14:09:17 -0800 |
| commit | 8fee508e5fbfdf35386ec82a5ec7860df20626d6 (patch) | |
| tree | 051075b6363dcc4446269bc45677fe138719541b | |
| parent | 77683e929b46504264c5459228f8eeace3811e20 (diff) | |
added minotaur, undo
| -rw-r--r-- | loader.js | 86 | ||||
| -rw-r--r-- | minotaur.js | 67 | ||||
| -rw-r--r-- | test.html | 2 | ||||
| -rw-r--r-- | undo/09-test-undo.js | 165 | ||||
| -rw-r--r-- | undo/undostack.js | 53 |
5 files changed, 373 insertions, 0 deletions
diff --git a/loader.js b/loader.js new file mode 100644 index 0000000..9de5cb3 --- /dev/null +++ b/loader.js @@ -0,0 +1,86 @@ +/***************************************************** + +// sample config + +var config = {}; +config.images = []; + +// sample app + +function app = {}; +app.init = function(){ + app.load(); +} +app.load = function(){ + app.loader = new Loader (); + app.loader.register("loading"); + + // register loaders here + + app.loader.ready("loading"); +} +app.ready = function(){ +} +******************************************************/ + +function Loader (readyCallback){ + this.assets = {}; + this.readyCallback = readyCallback || app.ready; +} + +// Register an asset as loading +Loader.prototype.register = function(s){ + this.assets[s] = false; +} + +// Signal that an asset has loaded +Loader.prototype.ready = function(s){ + console.log("ready >> " + s); + + this.assets[s] = true; + if (this.loaded) return; + if (! this.isReady()) return; + + this.loaded = true; + this.readyCallback(); +} + +// (boolean) Is the loader ready? +Loader.prototype.isReady = function(){ + for (var s in this.assets) { + if (this.assets.hasOwnProperty(s) && this.assets[s] != true) { + return false; + } + } + return true; +} + +// (int) Number of assets remaining +Loader.prototype.remainingAssets = function(){ + var n = 0; + for (var s in this.assets) { + if (this.assets.hasOwnProperty(s) && this.assets[s] != true) { + n++; + console.log('remaining: ' + s); + } + } + return n; +} + +// Preload the images in config.images +Loader.prototype.preloadImages = function(){ + for (var i = 0; i < config.images.length; i++) { + this.preloadImage(config.images[i]); + } +} +Loader.prototype.preloadImage = function(src){ + var _this = this; + this.register(src); + var img = new Image(); + img.onload = function(){ + _this.ready(src); + } + img.src = src; + if (img.complete) img.onload(); +} +
diff --git a/minotaur.js b/minotaur.js new file mode 100644 index 0000000..7a4b5f5 --- /dev/null +++ b/minotaur.js @@ -0,0 +1,67 @@ +//jules 5-May-2013 +//Minotaur is a thing I wrote for brbprint that defers model updates. +//The idea is you set some keys on the model, and the Minotaur defers the actual Ajax POST/PUT. +//Also updates a save button for you, letting you know when the changes finish posting. +// +//Expects that your model has a "save" method that accepts a callback, to inform the Minotaur that the save happened. +// +//The name is a pun on Monitor but is more badass!!!! 38c (minotaur emoticon) +$(function(){ + + var Monitor = function () { + var base = this; + base.$el = $("#save"); + base.timeout = null; + base.delay = 500; + base.objects = {}; + + base.init = function () { + base.$el.addClass('saved').html('Save'); + base.$el.click(base.save); + } + + base.watch = function (object) { + base.objects[object.type] = base.objects[object.type] || {}; + base.objects[object.type][object.id] = object; + base.clear(); + base.timeout = setTimeout(base.save, base.delay); + }; + + base.clear = function () { + if (base.timeout) clearTimeout(base.timeout); + base.timeout = false; + }; + + base.save = function () { + var saving = false; + base.clear(); + + for (var type in base.objects) { + for (var id in base.objects[type]) { + if (base.timeout) + return; + var obj = base.objects[type][id]; + if (obj) obj.save(function(){ + base.$el.removeClass('unsaved saving').addClass('saved').html('Saved'); + saving = true; + }); + base.objects[type][id] = false; + } + } + + if (saving) { + base.$el.removeClass('unsaved saved').addClass('saving').html('Saving'); + } + else { + base.$el.removeClass('unsaved saving').addClass('saved').html('Saved'); + } + + base.objects = {}; + }; + + base.init(); + }; + + window.Minotaur = new Monitor (); +}); +
@@ -47,6 +47,8 @@ <script type="text/javascript" src="jquery.js"></script> <script type="text/javascript" src="underscore.js"></script> +<script type="text/javascript" src="loader.js" ></script> +<script type="text/javascript" src="minotaur.js" ></script> <script type="text/javascript" src="View.js" ></script> <script type="text/javascript" src="ModalView.js" ></script> <script type="text/javascript" src="FormView.js" ></script> diff --git a/undo/09-test-undo.js b/undo/09-test-undo.js new file mode 100644 index 0000000..2cceb55 --- /dev/null +++ b/undo/09-test-undo.js @@ -0,0 +1,165 @@ +var assert = require("assert") +var UndoStack = require("./undo.js") +UndoStack.debug = false + +describe('undo', function(){ + + var state = "zero" + + describe('#register()', function(){ + + UndoStack.register({ + type: "demo", + undo: function(myState){ + state = myState + }, + redo: function(myState){ + state = myState + }, + }) + + it('registers undoable actions', function(){ + assert( UndoStack.types.hasOwnProperty("demo") ) + }) + }) + + describe('#push()', function(){ + + it('starts empty', function(){ + assert.equal(0, UndoStack.stack.length) + assert.equal(-1, UndoStack.pointer) + }) + + it('pushes some actions', function(){ + + UndoStack.push({ + type: "demo", + undo: state, + redo: "one" + }) + state = "one" + + UndoStack.push({ + type: "demo", + undo: state, + redo: "two" + }) + state = "two" + + UndoStack.push({ + type: "demo", + undo: state, + redo: "three" + }) + state = "three" + + assert.equal(3, UndoStack.stack.length) + assert.equal(2, UndoStack.pointer) + }) + }) + + describe('#undo()', function(){ + + it('retrieves old state', function(){ + assert.equal("three", state) + UndoStack.undo() + assert.equal("two", state) + assert.equal(1, UndoStack.pointer) + }) + + it('can only undo so far', function(){ + var canUndo + + canUndo = UndoStack.undo() + assert.equal("one", state) + assert.equal(0, UndoStack.pointer) + assert.equal(true, canUndo) + + canUndo = UndoStack.undo() + assert.equal("zero", state) + assert.equal(-1, UndoStack.pointer) + assert.equal(false, canUndo) + + canUndo = UndoStack.undo() + assert.equal("zero", state) + assert.equal(-1, UndoStack.pointer) + assert.equal(false, canUndo) + }) + + }) + + describe('#redo()', function(){ + + it('reassigns new state', function(){ + UndoStack.redo() + assert.equal("one", state) + }) + + it('can only redo so far', function(){ + var canRedo + + canRedo = UndoStack.redo() + assert.equal("two", state) + assert.equal(true, canRedo) + + canRedo = UndoStack.redo() + assert.equal("three", state) + assert.equal(false, canRedo) + + canRedo = UndoStack.redo() + assert.equal("three", state) + assert.equal(false, canRedo) + }) + + it('clobbers old state if we undo then do something else', function(){ + UndoStack.undo() + assert.equal("two", state) + assert.equal(1, UndoStack.pointer) + + UndoStack.undo() + assert.equal("one", state) + assert.equal(0, UndoStack.pointer) + assert.equal(3, UndoStack.stack.length) + + UndoStack.push({ + type: "demo", + undo: state, + redo: "four" + }) + state = "four" + + assert.equal(1, UndoStack.pointer) + assert.equal(2, UndoStack.stack.length) + + UndoStack.undo() + assert.equal("one", state) + assert.equal(0, UndoStack.pointer) + assert.equal(2, UndoStack.stack.length) + + UndoStack.redo() + assert.equal("four", state) + assert.equal(1, UndoStack.pointer) + assert.equal(2, UndoStack.stack.length) + }) + }) + +}) + +// 1) push action +// 2) push action +// 3) push action +// undo 3 +// undo 2 +// undo 1 +// undo * can't undo anymore +// redo 1 +// redo 2 +// redo 3 +// redo * can't redo anymore +// undo 3 +// 4) push action (clobbers action 3) +// undo 4 +// undo 2 +// redo 2 +// redo 4 +
diff --git a/undo/undostack.js b/undo/undostack.js new file mode 100644 index 0000000..5ba97e1 --- /dev/null +++ b/undo/undostack.js @@ -0,0 +1,53 @@ +(function(){ + + var UndoStack = function(){ + this.debug = true + this.stack = [] + this.types = {} + this.pointer = -1 + } + UndoStack.prototype.push = function(action){ + this.pointer++ + this.stack[this.pointer] = action + this.purge() + } + UndoStack.prototype.purge = function(){ + if (this.stack.length-1 == this.pointer) return + this.stack.length = this.pointer+1 + } + UndoStack.prototype.undo = function(){ + if (this.pointer == -1) return false + var action = this.stack[this.pointer] + this.debug && console.log("undo", action.type) + this.types[ action.type ].undo(action.undo) + this.pointer-- + return this.pointer > -1 + } + UndoStack.prototype.redo = function(){ + if (this.pointer == this.stack.length-1) return false + this.pointer++ + var action = this.stack[this.pointer] + this.debug && console.log("redo", action.type) + this.types[ action.type ].redo(action.redo) + return this.pointer < this.stack.length-1 + } + UndoStack.prototype.register = function(actionType){ + if (actionType.length) { + actionType.forEach(this.registerOne.bind(this)) + } + else { + this.registerOne(actionType) + } + } + UndoStack.prototype.registerOne = function(actionType){ + this.types[ actionType.type ] = actionType + } + if ('window' in this) { + window.UndoStack = new UndoStack + } + else { + module.exports = new UndoStack + } + +})() +
|
