From 4b50dfdb5b7d2141d03b183841ba28a68667d37b Mon Sep 17 00:00:00 2001 From: juliandescottes Date: Thu, 17 Nov 2016 10:03:48 +0100 Subject: [PATCH 1/3] issue #374 limit undo/redo to the last 500 states --- src/js/service/HistoryService.js | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/js/service/HistoryService.js b/src/js/service/HistoryService.js index debfd2a3..d1d65823 100644 --- a/src/js/service/HistoryService.js +++ b/src/js/service/HistoryService.js @@ -24,6 +24,9 @@ // Interval/buffer (in milliseconds) between two state load (ctrl+z/y spamming) ns.HistoryService.LOAD_STATE_INTERVAL = 50; + // Maximum number of states that can be recorded. + ns.HistoryService.MAX_SAVED_STATES = 500; + ns.HistoryService.prototype.init = function () { $.subscribe(Events.PISKEL_SAVE_STATE, this.onSaveStateEvent.bind(this)); @@ -41,7 +44,6 @@ }; ns.HistoryService.prototype.saveState = function (action) { - this.stateQueue = this.stateQueue.slice(0, this.currentIndex + 1); this.currentIndex = this.currentIndex + 1; var state = { @@ -58,6 +60,13 @@ state.piskel = this.serializer.serialize(piskel); } + // If the new state pushes over MAX_SAVED_STATES, erase all states between the first and + // second snapshot states. + if (this.stateQueue.length > ns.HistoryService.MAX_SAVED_STATES) { + var firstSnapshotIndex = this.getNextSnapshotIndex_(1); + this.stateQueue.splice(0, firstSnapshotIndex); + this.currentIndex = this.currentIndex - firstSnapshotIndex; + } this.stateQueue.push(state); $.publish(Events.HISTORY_STATE_SAVED); }; @@ -92,6 +101,13 @@ return index; }; + ns.HistoryService.prototype.getNextSnapshotIndex_ = function (index) { + while (this.stateQueue[index] && !this.stateQueue[index].piskel) { + index = index + 1; + } + return index; + }; + ns.HistoryService.prototype.loadState = function (index) { try { if (this.isLoadStateAllowed_(index)) { From d08c101b11963c0448c380fba875a34d8d007918 Mon Sep 17 00:00:00 2001 From: juliandescottes Date: Sun, 20 Nov 2016 01:10:05 +0100 Subject: [PATCH 2/3] issue #374 cap cached frames for cachedframeprocessor to 100 --- src/js/model/frame/CachedFrameProcessor.js | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/js/model/frame/CachedFrameProcessor.js b/src/js/model/frame/CachedFrameProcessor.js index 8a8ba610..eb88d1cf 100644 --- a/src/js/model/frame/CachedFrameProcessor.js +++ b/src/js/model/frame/CachedFrameProcessor.js @@ -1,8 +1,8 @@ (function () { var ns = $.namespace('pskl.model.frame'); - // 10 * 60 * 1000 = 10 minutes - var DEFAULT_CLEAR_INTERVAL = 10 * 60 * 1000; + // Maximum number of cache entries + var MAX_CACHE_ENTRIES = 100; var DEFAULT_FRAME_PROCESSOR = function (frame) { return pskl.utils.FrameUtils.toImage(frame); @@ -12,14 +12,16 @@ var DEFAULT_NAMESPACE = '__cache_default__'; - ns.CachedFrameProcessor = function (cacheResetInterval) { + ns.CachedFrameProcessor = function () { + // Cache object. this.cache_ = {}; - this.cacheResetInterval = cacheResetInterval || DEFAULT_CLEAR_INTERVAL; + + // Array of [namespace, key] for each cached frame. + this.cacheQueue_ = []; + this.frameProcessor = DEFAULT_FRAME_PROCESSOR; this.outputCloner = DEFAULT_OUTPUT_CLONER; this.defaultNamespace = DEFAULT_NAMESPACE; - - window.setInterval(this.clear.bind(this), this.cacheResetInterval); }; ns.CachedFrameProcessor.prototype.clear = function () { @@ -69,6 +71,11 @@ } else { processedFrame = this.frameProcessor(frame); cache[cacheKey] = processedFrame; + this.cacheQueue_.unshift([namespace, cacheKey]); + if (this.cacheQueue_.length > MAX_CACHE_ENTRIES) { + var oldestItem = this.cacheQueue_.pop(); + this.cache_[oldestItem[0]][oldestItem[1]] = null; + } } return processedFrame; From 136506da40229d4a4e52a36e3bb9361d6cc5d7da Mon Sep 17 00:00:00 2001 From: juliandescottes Date: Sun, 20 Nov 2016 12:08:31 +0100 Subject: [PATCH 3/3] issue #374 throttle calls to currentColorsService update function --- src/js/service/CurrentColorsService.js | 7 ++++++- src/js/utils/FunctionUtils.js | 24 ++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/js/service/CurrentColorsService.js b/src/js/service/CurrentColorsService.js index b58d251c..365bfb0b 100644 --- a/src/js/service/CurrentColorsService.js +++ b/src/js/service/CurrentColorsService.js @@ -10,11 +10,16 @@ this.cachedFrameProcessor = new pskl.model.frame.AsyncCachedFrameProcessor(); this.cachedFrameProcessor.setFrameProcessor(this.getFrameColors_.bind(this)); + this.throttledUpdateCurrentColors_ = pskl.utils.FunctionUtils.throttle( + this.updateCurrentColors_.bind(this), + 1000 + ); + this.paletteService = pskl.app.paletteService; }; ns.CurrentColorsService.prototype.init = function () { - $.subscribe(Events.HISTORY_STATE_SAVED, this.updateCurrentColors_.bind(this)); + $.subscribe(Events.HISTORY_STATE_SAVED, this.throttledUpdateCurrentColors_); $.subscribe(Events.HISTORY_STATE_LOADED, this.loadColorsFromCache_.bind(this)); }; diff --git a/src/js/utils/FunctionUtils.js b/src/js/utils/FunctionUtils.js index 71a05b1c..9c8233e1 100644 --- a/src/js/utils/FunctionUtils.js +++ b/src/js/utils/FunctionUtils.js @@ -2,6 +2,9 @@ var ns = $.namespace('pskl.utils'); ns.FunctionUtils = { + /** + * Returns a memoized version of the provided function. + */ memo : function (fn, cache, scope) { var memoized = function () { var key = Array.prototype.join.call(arguments, '-'); @@ -11,6 +14,27 @@ return cache[key]; }; return memoized; + }, + + /** + * Returns a throttled version of the provided method, that will be called at most + * every X milliseconds, where X is the provided interval. + */ + throttle : function (fn, interval) { + var last, timer; + return function () { + var now = Date.now(); + if (last && now < last + interval) { + clearTimeout(timer); + timer = setTimeout(function () { + last = now; + fn(); + }, interval); + } else { + last = now; + fn(); + } + }; } }; })();