Merge pull request #88 from grosbouddha/clean-localstorage-service

Clean code
This commit is contained in:
grosbouddha 2012-09-16 06:37:37 -07:00
commit 1cd0e89207
10 changed files with 309 additions and 295 deletions

View File

@ -89,23 +89,26 @@
<!-- Application libraries-->
<script src="js/rendering/DrawingLoop.js"></script>
<!-- Models -->
<script src="js/model/Frame.js"></script>
<script src="js/model/FrameSheet.js"></script>
<script src="js/rendering/FrameRenderer.js"></script>
<script src="js/controller/DrawingController.js"></script>
<script src="js/controller/PreviewFilmController.js"></script>
<script src="js/controller/AnimatedPreviewController.js"></script>
<script src="js/controller/ToolController.js"></script>
<script src="js/LocalStorageService.js"></script>
<script src="js/HistoryManager.js"></script>
<script src="js/KeyManager.js"></script>
<script src="js/selection/SelectionManager.js"></script>
<script src="js/selection/BaseSelection.js"></script>
<script src="js/selection/RectangularSelection.js"></script>
<script src="js/selection/ShapeSelection.js"></script>
<script src="js/Palette.js"></script>
<script src="js/Notification.js"></script>
<!-- Rendering -->
<script src="js/rendering/FrameRenderer.js"></script>
<script src="js/controller/DrawingController.js"></script>
<!-- Controllers -->
<script src="js/controller/PreviewFilmController.js"></script>
<script src="js/controller/AnimatedPreviewController.js"></script>
<script src="js/controller/ToolController.js"></script>
<script src="js/controller/PaletteController.js"></script>
<script src="js/controller/NotificationController.js"></script>
<!-- Services -->
<script src="js/service/LocalStorageService.js"></script>
<script src="js/service/HistoryService.js"></script>
<script src="js/service/KeyboardEventService.js"></script>
<!-- Tools-->
<script src="js/drawingtools/BaseTool.js"></script>
<script src="js/drawingtools/SimplePen.js"></script>

View File

@ -1,96 +0,0 @@
/*
* @provide pskl.LocalStrageService
*
* @require Constants
* @require Events
*/
$.namespace("pskl");
pskl.LocalStorageService = (function() {
var frameSheet_;
/**
* @private
*/
var localStorageThrottler_ = null;
/**
* @private
*/
var persistToLocalStorageRequest_ = function() {
// Persist to localStorage when drawing. We throttle localStorage accesses
// for high frequency drawing (eg mousemove).
if(localStorageThrottler_ !== null) {
window.clearTimeout(localStorageThrottler_);
}
localStorageThrottler_ = window.setTimeout(function() {
persistToLocalStorage_();
localStorageThrottler_ = null;
}, 1000);
};
/**
* @private
*/
var persistToLocalStorage_ = function() {
console.log('[LocalStorage service]: Snapshot stored');
window.localStorage.snapShot = frameSheet_.serialize();
};
/**
* @private
* TODO(vince): Move that away from LocalStorageService
*/
var restoreFromLocalStorage_ = function() {
frameSheet_.deserialize(window.localStorage.snapShot);
frameSheet_.setCurrentFrameIndex(0);
};
/**
* @private
*/
var cleanLocalStorage_ = function() {
console.log('[LocalStorage service]: Snapshot removed');
delete window.localStorage.snapShot;
};
return {
init: function(frameSheet) {
if(frameSheet === undefined) {
throw "Bad LocalStorageService initialization: <undefined frameSheet>";
}
frameSheet_ = frameSheet;
$.subscribe(Events.LOCALSTORAGE_REQUEST, persistToLocalStorageRequest_);
},
// TODO(vincz): Find a good place to put this UI rendering, a service should not render UI.
displayRestoreNotification: function() {
if(window.localStorage && window.localStorage.snapShot) {
var reloadLink = "<a href='#' class='localstorage-restore onclick='piskel.restoreFromLocalStorage()'>reload</a>";
var discardLink = "<a href='#' class='localstorage-discard' onclick='piskel.cleanLocalStorage()'>discard</a>";
var content = "Non saved version found. " + reloadLink + " or " + discardLink;
$.publish(Events.SHOW_NOTIFICATION, [{
"content": content,
"behavior": function(rootNode) {
rootNode = $(rootNode);
rootNode.click(function(evt) {
var target = $(evt.target);
if(target.hasClass("localstorage-restore")) {
restoreFromLocalStorage_();
}
else if (target.hasClass("localstorage-discard")) {
cleanLocalStorage_();
}
$.publish(Events.HIDE_NOTIFICATION);
});
}
}]);
}
}
};
})();

View File

@ -1,39 +0,0 @@
/*
* @provide pskl.NotificationService
*
*/
$.namespace("pskl");
pskl.NotificationService = (function() {
/**
* @private
*/
var displayMessage_ = function (evt, messageInfo) {
var message = document.createElement('div');
message.id = "user-message";
message.className = "user-message";
message.innerHTML = messageInfo.content;
message.innerHTML = message.innerHTML + "<div title='Close message' class='close'>x</div>";
document.body.appendChild(message);
$(message).find(".close").click(removeMessage_);
if(messageInfo.behavior) messageInfo.behavior(message);
};
/**
* @private
*/
var removeMessage_ = function (evt) {
var message = $("#user-message");
if (message.length) {
message.remove();
}
};
return {
init: function() {
$.subscribe(Events.SHOW_NOTIFICATION, displayMessage_);
$.subscribe(Events.HIDE_NOTIFICATION, removeMessage_);
}
};
})();

View File

@ -1,116 +0,0 @@
/*
* @provide pskl.Palette
*
* @require Constants
* @require Events
*/
$.namespace("pskl");
pskl.Palette = (function() {
var paletteRoot,
paletteColors = [];
/**
* @private
*/
var onPickerChange_ = function(evt, isPrimary) {
var inputPicker = $(evt.target);
$.publish(Events.COLOR_SELECTED, [inputPicker.val(), evt.data.isPrimary]);
};
/**
* @private
*/
var createPalette_ = function (colors) {
// Always adding transparent color
paletteRoot.html('<span class="palette-color transparent-color" data-color="TRANSPARENT" rel="tooltip" data-placement="bottom" title="Transparent"></span>');
for(var color in colors) {
if(color != Constants.TRANSPARENT_COLOR) {
addColorToPalette_(color);
}
}
};
/**
* @private
*/
var addColorToPalette_ = function (color) {
if (paletteColors.indexOf(color) == -1 && color != Constants.TRANSPARENT_COLOR) {
var colorEl = document.createElement("li");
colorEl.className = "palette-color";
colorEl.setAttribute("rel", "tooltip");
colorEl.setAttribute("data-placement", "bottom");
colorEl.setAttribute("data-color", color);
colorEl.setAttribute("title", color);
colorEl.style.background = color;
paletteRoot[0].appendChild(colorEl);
paletteColors.push(color);
}
};
/**
* @private
*/
var onPaletteColorClick_ = function (event) {
var selectedColor = $(event.target).data("color");
if (event.which == 1) { // left button
updateColorPicker(selectedColor, $('#color-picker'));
$.publish(Events.COLOR_SELECTED, [selectedColor, true]);
} else if (event.which == 3) { // right button
updateColorPicker(selectedColor, $('#secondary-color-picker'));
$.publish(Events.COLOR_SELECTED, [selectedColor, false]);
}
};
var updateColorPicker = function (color, colorPicker) {
if (color == Constants.TRANSPARENT_COLOR) {
// We can set the current palette color to transparent.
// You can then combine this transparent color with an advanced
// tool for customized deletions.
// Eg: bucket + transparent: Delete a colored area
// Stroke + transparent: hollow out the equivalent of a stroke
// The colorpicker can't be set to a transparent state.
// We set its background to white and insert the
// string "TRANSPARENT" to mimic this state:
colorPicker[0].color.fromString("#fff");
colorPicker.val(Constants.TRANSPARENT_COLOR);
} else {
colorPicker[0].color.fromString(color);
}
};
return {
init: function(framesheet) {
paletteRoot = $("#palette");
// Initialize palette:
createPalette_(framesheet.getUsedColors());
$.subscribe(Events.FRAMESHEET_RESET, function(evt) {
createPalette_(framesheet.getUsedColors());
});
paletteRoot.mouseup(onPaletteColorClick_);
$.subscribe(Events.COLOR_SELECTED, function(evt, color) {
addColorToPalette_(color);
});
// Initialize colorpicker:
var colorPicker = $('#color-picker');
colorPicker.val(Constants.DEFAULT_PEN_COLOR);
colorPicker.change({isPrimary : true}, onPickerChange_);
var secondaryColorPicker = $('#secondary-color-picker');
secondaryColorPicker.val(Constants.TRANSPARENT_COLOR);
secondaryColorPicker.change({isPrimary : false}, onPickerChange_);
}
};
})();

View File

@ -0,0 +1,39 @@
(function () {
var ns = $.namespace("pskl.controller");
ns.NotificationController = function () {};
/**
* @private
*/
ns.NotificationController.prototype.displayMessage_ = function (evt, messageInfo) {
var message = document.createElement('div');
message.id = "user-message";
message.className = "user-message";
message.innerHTML = messageInfo.content;
message.innerHTML = message.innerHTML + "<div title='Close message' class='close'>x</div>";
document.body.appendChild(message);
$(message).find(".close").click($.proxy(this.removeMessage_, this));
if(messageInfo.behavior) {
messageInfo.behavior(message);
}
};
/**
* @private
*/
ns.NotificationController.prototype.removeMessage_ = function (evt) {
var message = $("#user-message");
if (message.length) {
message.remove();
}
};
/**
* @public
*/
ns.NotificationController.prototype.init = function() {
$.subscribe(Events.SHOW_NOTIFICATION, $.proxy(this.displayMessage_, this));
$.subscribe(Events.HIDE_NOTIFICATION, $.proxy(this.removeMessage_, this));
};
})();

View File

@ -0,0 +1,115 @@
(function () {
var ns = $.namespace("pskl.controller");
ns.PaletteController = function () {
this.paletteRoot = null;
this.paletteColors = [];
};
/**
* @private
*/
ns.PaletteController.prototype.onPickerChange_ = function(evt, isPrimary) {
var inputPicker = $(evt.target);
$.publish(Events.COLOR_SELECTED, [inputPicker.val(), evt.data.isPrimary]);
};
/**
* @private
*/
ns.PaletteController.prototype.createPalette_ = function (colors) {
// Always adding transparent color
this.paletteRoot.html('<span class="palette-color transparent-color" data-color="TRANSPARENT" title="Transparent"></span>');
for(var color in colors) {
if(color != Constants.TRANSPARENT_COLOR) {
this.addColorToPalette_(color);
}
}
};
/**
* @private
*/
ns.PaletteController.prototype.addColorToPalette_ = function (color) {
if (this.paletteColors.indexOf(color) == -1 && color != Constants.TRANSPARENT_COLOR) {
var colorEl = document.createElement("li");
colorEl.className = "palette-color";
colorEl.setAttribute("data-color", color);
colorEl.setAttribute("title", color);
colorEl.style.background = color;
this.paletteRoot.append(colorEl);
this.paletteColors.push(color);
}
};
/**
* @private
*/
ns.PaletteController.prototype.onPaletteColorClick_ = function (event) {
var selectedColor = $(event.target).data("color");
if (event.which == 1) { // left button
this.updateColorPicker_(selectedColor, $('#color-picker'));
$.publish(Events.COLOR_SELECTED, [selectedColor, true]);
} else if (event.which == 3) { // right button
this.updateColorPicker_(selectedColor, $('#secondary-color-picker'));
$.publish(Events.COLOR_SELECTED, [selectedColor, false]);
}
};
/**
* @private
*/
ns.PaletteController.prototype.updateColorPicker_ = function (color, colorPicker) {
if (color == Constants.TRANSPARENT_COLOR) {
// We can set the current palette color to transparent.
// You can then combine this transparent color with an advanced
// tool for customized deletions.
// Eg: bucket + transparent: Delete a colored area
// Stroke + transparent: hollow out the equivalent of a stroke
// The colorpicker can't be set to a transparent state.
// We set its background to white and insert the
// string "TRANSPARENT" to mimic this state:
colorPicker[0].color.fromString("#fff");
colorPicker.val(Constants.TRANSPARENT_COLOR);
} else {
colorPicker[0].color.fromString(color);
}
};
/**
* @public
*/
ns.PaletteController.prototype.init = function(framesheet) {
this.paletteRoot = $("#palette");
this.framesheet = framesheet;
// Initialize palette:
this.createPalette_(this.framesheet.getUsedColors());
$.subscribe(Events.FRAMESHEET_RESET, $.proxy(function(evt) {
this.createPalette_(this.framesheet.getUsedColors());
}, this));
this.paletteRoot.mouseup($.proxy(this.onPaletteColorClick_, this));
$.subscribe(Events.COLOR_SELECTED, $.proxy(function(evt, color) {
this.addColorToPalette_(color);
}, this));
// Initialize colorpickers:
var colorPicker = $('#color-picker');
colorPicker.val(Constants.DEFAULT_PEN_COLOR);
colorPicker.change({isPrimary : true}, $.proxy(this.onPickerChange_, this));
var secondaryColorPicker = $('#secondary-color-picker');
secondaryColorPicker.val(Constants.TRANSPARENT_COLOR);
secondaryColorPicker.change({isPrimary : false}, $.proxy(this.onPickerChange_, this));
};
})();

View File

@ -66,13 +66,17 @@ $.namespace("pskl");
this.animationController.init();
this.previewsController.init();
this.historyManager = new pskl.HistoryManager(frameSheet);
this.historyManager.init();
this.historyService = new pskl.service.HistoryService(frameSheet);
this.historyService.init();
this.keyManager = new pskl.KeyManager();
this.keyboardEventService = new pskl.service.KeyboardEventService();
this.keyboardEventService.init();
pskl.NotificationService.init();
pskl.LocalStorageService.init(frameSheet);
this.notificationController = new pskl.controller.NotificationController();
this.notificationController.init();
this.localStorageService = new pskl.service.LocalStorageService(frameSheet);
this.localStorageService.init();
// TODO: Add comments
var framesheetId = this.getFramesheetIdFromUrl();
@ -81,7 +85,7 @@ $.namespace("pskl");
this.loadFramesheetFromService(framesheetId);
} else {
this.finishInit();
pskl.LocalStorageService.displayRestoreNotification();
this.localStorageService.displayRestoreNotification();
}
var drawingLoop = new pskl.rendering.DrawingLoop();
@ -155,7 +159,8 @@ $.namespace("pskl");
var toolController = new pskl.controller.ToolController();
toolController.init();
pskl.Palette.init(frameSheet);
var paletteController = new pskl.controller.PaletteController();
paletteController.init(frameSheet);
},
getFramesheetIdFromUrl : function() {

View File

@ -1,26 +1,26 @@
(function () {
var ns = $.namespace("pskl");
ns.HistoryManager = function (framesheet) {
var ns = $.namespace("pskl.service");
ns.HistoryService = function (framesheet) {
this.framesheet = framesheet;
};
ns.HistoryManager.prototype.init = function () {
ns.HistoryService.prototype.init = function () {
$.subscribe(Events.TOOL_RELEASED, this.saveState.bind(this));
$.subscribe(Events.UNDO, this.undo.bind(this));
$.subscribe(Events.REDO, this.redo.bind(this));
};
ns.HistoryManager.prototype.saveState = function () {
ns.HistoryService.prototype.saveState = function () {
this.framesheet.getCurrentFrame().saveState();
};
ns.HistoryManager.prototype.undo = function () {
ns.HistoryService.prototype.undo = function () {
this.framesheet.getCurrentFrame().loadPreviousState();
$.publish(Events.FRAMESHEET_RESET);
};
ns.HistoryManager.prototype.redo = function () {
ns.HistoryService.prototype.redo = function () {
this.framesheet.getCurrentFrame().loadNextState();
$.publish(Events.FRAMESHEET_RESET);
};

View File

@ -1,21 +1,12 @@
(function () {
var ns = $.namespace("pskl");
var ns = $.namespace("pskl.service");
ns.KeyManager = function () {
$(document.body).keydown($.proxy(this.onKeyUp_, this));
};
ns.KeyboardEventService = function () {};
// Kind of object that make you want to stop front-end _engineering_:
ns.KeyManager.prototype.CharCodeToKeyCodeMap = {
90 : "z",
89 : "y",
88 : "x",
67 : "c",
86 : "v"
};
ns.KeyManager.prototype.KeyboardActions = {
/**
* @private
*/
ns.KeyboardEventService.prototype.KeyboardActions_ = {
"ctrl" : {
"z" : Events.UNDO,
@ -26,9 +17,22 @@
}
};
/**
* @private
*/
ns.KeyboardEventService.prototype.CharCodeToKeyCodeMap_ = {
ns.KeyManager.prototype.onKeyUp_ = function(evt) {
90 : "z",
89 : "y",
88 : "x",
67 : "c",
86 : "v"
};
/**
* @private
*/
ns.KeyboardEventService.prototype.onKeyUp_ = function(evt) {
var isMac = false;
if (navigator.appVersion.indexOf("Mac")!=-1) {
// Welcome in mac world where vowels are consons and meta used instead of ctrl:
@ -37,14 +41,24 @@
if (isMac ? evt.metaKey : evt.ctrlKey) {
// Get key pressed:
var letter = this.CharCodeToKeyCodeMap[evt.which];
var letter = this.CharCodeToKeyCodeMap_[evt.which];
if(letter) {
var eventToTrigger = this.KeyboardActions.ctrl[letter];
var eventToTrigger = this.KeyboardActions_.ctrl[letter];
if(eventToTrigger) {
$.publish(eventToTrigger);
evt.preventDefault();
return false;
}
}
}
};
/**
* @public
*/
ns.KeyboardEventService.prototype.init = function() {
$(document.body).keydown($.proxy(this.onKeyUp_, this));
};
})();

View File

@ -0,0 +1,89 @@
(function () {
var ns = $.namespace("pskl.service");
ns.LocalStorageService = function (framesheet_) {
if(framesheet_ === undefined) {
throw "Bad LocalStorageService initialization: <undefined frameSheet>";
}
this.framesheet = framesheet_;
this.localStorageThrottler_ = null;
};
/**
* @private
*/
ns.LocalStorageService.prototype.persistToLocalStorageRequest_ = function () {
// Persist to localStorage when drawing. We throttle localStorage accesses
// for high frequency drawing (eg mousemove).
if(this.localStorageThrottler_ !== null) {
window.clearTimeout(this.localStorageThrottler_);
}
this.localStorageThrottler_ = window.setTimeout($.proxy(function() {
this.persistToLocalStorage_();
this.localStorageThrottler_ = null;
}, this), 1000);
};
/**
* @private
*/
ns.LocalStorageService.prototype.persistToLocalStorage_ = function() {
console.log('[LocalStorage service]: Snapshot stored');
window.localStorage.snapShot = this.framesheet.serialize();
};
/**
* @private
*/
ns.LocalStorageService.prototype.restoreFromLocalStorage_ = function() {
this.framesheet.deserialize(window.localStorage.snapShot);
this.framesheet.setCurrentFrameIndex(0);
};
/**
* @private
*/
ns.LocalStorageService.prototype.cleanLocalStorage_ = function() {
console.log('[LocalStorage service]: Snapshot removed');
delete window.localStorage.snapShot;
};
/**
* @public
*/
ns.LocalStorageService.prototype.init = function(framesheet_) {
$.subscribe(Events.LOCALSTORAGE_REQUEST, $.proxy(this.persistToLocalStorageRequest_, this));
};
/**
* @public
*/
ns.LocalStorageService.prototype.displayRestoreNotification = function() {
if(window.localStorage && window.localStorage.snapShot) {
var reloadLink = "<a href='#' class='localstorage-restore onclick='piskel.restoreFromLocalStorage()'>reload</a>";
var discardLink = "<a href='#' class='localstorage-discard' onclick='piskel.cleanLocalStorage()'>discard</a>";
var content = "Non saved version found. " + reloadLink + " or " + discardLink;
$.publish(Events.SHOW_NOTIFICATION, [{
"content": content,
"behavior": $.proxy(function(rootNode) {
rootNode = $(rootNode);
rootNode.click($.proxy(function(evt) {
var target = $(evt.target);
if(target.hasClass("localstorage-restore")) {
this.restoreFromLocalStorage_();
}
else if (target.hasClass("localstorage-discard")) {
this.cleanLocalStorage_();
}
$.publish(Events.HIDE_NOTIFICATION);
}, this));
}, this)
}]);
}
};
})();