-
Notifications
You must be signed in to change notification settings - Fork 51
/
backbone.memento.js
159 lines (129 loc) · 4.42 KB
/
backbone.memento.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
// Backbone.Memento v0.4.1a
//
// Copyright (C)2011 Derick Bailey, Muted Solutions, LLC
// Distributed Under MIT License
//
// Documentation and Full License Available at:
// http://github.com/derickbailey/backbone.memento
Backbone.Memento = (function(Backbone, _){
'use strict';
// ----------------------------
// Memento: the public API
// ----------------------------
var Memento = function(structure, config){
this.version = "0.4.1a";
config = _.extend({ignore: []}, config);
var serializer = new Serializer(structure, config);
var mementoStack = new MementoStack(structure, config);
var restoreState = function (previousState, restoreConfig){
if (!previousState){ return; }
serializer.deserialize(previousState, restoreConfig);
};
this.store = function(){
var currentState = serializer.serialize();
mementoStack.push(currentState);
};
this.restore = function(restoreConfig){
var previousState = mementoStack.pop();
restoreState(previousState, restoreConfig);
};
this.restart = function(restoreConfig){
var previousState = mementoStack.rewind();
restoreState(previousState, restoreConfig);
};
};
// ----------------------------
// TypeHelper: a consistent API for removing attributes and
// restoring attributes, on models and collections
// ----------------------------
var TypeHelper = function(structure){
if (structure instanceof Backbone.Model) {
this.removeAttr = function(data){ structure.unset(data); };
this.restore = function(data){ structure.set(data); };
} else {
this.removeAttr = function(data){ structure.remove(data); };
this.restore = function(data){ structure.reset(data); };
}
};
// ----------------------------
// Serializer: serializer and deserialize model and collection state
// ----------------------------
var Serializer = function(structure, config){
var typeHelper = new TypeHelper(structure);
function dropIgnored(attrs, restoreConfig){
attrs = _.clone(attrs);
if (restoreConfig.hasOwnProperty("ignore") && restoreConfig.ignore.length > 0){
for(var index in restoreConfig.ignore){
var ignore = restoreConfig.ignore[index];
delete attrs[ignore];
}
}
return attrs;
}
function getAddedAttrDiff(newAttrs, oldAttrs){
var removedAttrs = [];
// guard clause to ensure we have attrs to compare
if (!newAttrs || !oldAttrs){
return removedAttrs;
}
// if the attr is found in the old set but not in
// the new set, then it was remove in the new set
for (var attr in oldAttrs){
if (oldAttrs.hasOwnProperty(attr)){
if (!newAttrs.hasOwnProperty(attr)){
removedAttrs.push(attr);
}
}
}
return removedAttrs;
}
function removeAttributes(structure, attrsToRemove){
for (var index in attrsToRemove){
var attr = attrsToRemove[index];
typeHelper.removeAttr(attr);
}
}
function restoreState(previousState, restoreConfig){
var oldAttrs = dropIgnored(previousState, restoreConfig);
//get the current state
var currentAttrs = structure.toJSON();
currentAttrs = dropIgnored(currentAttrs, restoreConfig);
//handle removing attributes that were added
var removedAttrs = getAddedAttrDiff(oldAttrs, currentAttrs);
removeAttributes(structure, removedAttrs);
typeHelper.restore(oldAttrs);
}
this.serialize = function(){
var attrs = structure.toJSON();
attrs = dropIgnored(attrs, config);
return attrs;
}
this.deserialize = function(previousState, restoreConfig){
restoreConfig = _.extend({}, config, restoreConfig);
restoreState(previousState, restoreConfig);
}
};
// ----------------------------
// MementoStack: push / pop model and collection states
// ----------------------------
var MementoStack = function(structure, config){
var attributeStack;
function initialize(){
attributeStack = [];
}
this.push = function(attrs){
attributeStack.push(attrs);
}
this.pop = function(restoreConfig){
var oldAttrs = attributeStack.pop();
return oldAttrs;
}
this.rewind = function(){
var oldAttrs = attributeStack[0];
initialize();
return oldAttrs;
}
initialize();
};
return Memento;
})(Backbone, _);