angular-history-rails 0.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +14 -0
- data/Rakefile +1 -0
- data/angular-history-rails.gemspec +22 -0
- data/lib/angular/history/rails.rb +9 -0
- data/lib/angular/history/rails/version.rb +7 -0
- data/vendor/assets/javascripts/angular-history.js +1517 -0
- data/vendor/assets/javascripts/angular-lazy-bind.js +91 -0
- metadata +82 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b0fc62aebce63961823cf3a20af88bf8e2075ece
|
4
|
+
data.tar.gz: 051505ba4d9a0b6b23e9dba1b3535ba253bcd0f6
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d64bcdeecedcfa6b02fd5eb52b6abef3069aaca658482668b5c9131c33c030f31b83ed0a20f3238d8df805a1b282deea2277f629ecbf9b27d67018e1fe60be7a
|
7
|
+
data.tar.gz: 513d8fb43a1267d84a69337c6edc21a8cfc34e3f250934aee3428cd91b87f67167bf607087b47f32c47332cbd0bda804c83a709a276c4c219e5d18380c7cf6ed
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Paul Vonderscher
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
Rails 3.1 asset-pipeline gem to provide [angular-history.js](https://github.com/decipherinc/angular-history).
|
2
|
+
|
3
|
+
Also provides the optional [ngLazyBind service](https://github.com/Ticore/ngLazyBind).
|
4
|
+
|
5
|
+
# Setup
|
6
|
+
|
7
|
+
In your Gemfile:
|
8
|
+
|
9
|
+
gem 'angular-history-rails'
|
10
|
+
|
11
|
+
In your application.js manifest:
|
12
|
+
|
13
|
+
//= require angular-lazy-bind
|
14
|
+
//= require angular-history
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'angular/history/rails/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "angular-history-rails"
|
8
|
+
spec.version = Angular::History::Rails::VERSION
|
9
|
+
spec.authors = ["Paul Vonderscher"]
|
10
|
+
spec.email = ["paul.vonderscher@gmail.com"]
|
11
|
+
spec.summary = %q{Adds the angular-history module to the asset pipeline}
|
12
|
+
spec.homepage = "https://github.com/VonD/angular-history-rails/"
|
13
|
+
spec.license = "MIT"
|
14
|
+
|
15
|
+
spec.files = `git ls-files`.split($/)
|
16
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
|
+
spec.require_paths = ["lib"]
|
19
|
+
|
20
|
+
spec.add_development_dependency "bundler", "~> 1.5"
|
21
|
+
spec.add_development_dependency "rake"
|
22
|
+
end
|
@@ -0,0 +1,1517 @@
|
|
1
|
+
/*global angular*/
|
2
|
+
|
3
|
+
/**
|
4
|
+
* @ngdoc overview
|
5
|
+
* @name decipher.history
|
6
|
+
* @description
|
7
|
+
* A history service for AngularJS. Undo/redo, that sort of thing. Has nothing to do with the "back" button, unless you want it to.
|
8
|
+
*
|
9
|
+
*/
|
10
|
+
(function () {
|
11
|
+
'use strict';
|
12
|
+
|
13
|
+
var DEEPWATCH_EXP = /^\s*(.*?)\s+for\s+(?:([\$\w][\$\w\d]*)|(?:\(\s*([\$\w][\$\w\d]*)\s*,\s*([\$\w][\$\w\d]*)\s*\)))\s+in\s+(.*?)$/,
|
14
|
+
DEFAULT_TIMEOUT = 1000,
|
15
|
+
lazyBindFound = false,
|
16
|
+
isDefined = angular.isDefined,
|
17
|
+
isUndefined = angular.isUndefined,
|
18
|
+
isFunction = angular.isFunction,
|
19
|
+
isArray = angular.isArray,
|
20
|
+
isString = angular.isString,
|
21
|
+
isObject = angular.isObject,
|
22
|
+
forEach = angular.forEach,
|
23
|
+
copy = angular.copy,
|
24
|
+
bind = angular.bind;
|
25
|
+
|
26
|
+
/**
|
27
|
+
* Polyfill for Object.keys
|
28
|
+
*
|
29
|
+
* @see: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/keys
|
30
|
+
*/
|
31
|
+
if (!Object.keys) {
|
32
|
+
Object.keys = (function () {
|
33
|
+
var hasOwnProperty = Object.prototype.hasOwnProperty,
|
34
|
+
hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'),
|
35
|
+
dontEnums = [
|
36
|
+
'toString',
|
37
|
+
'toLocaleString',
|
38
|
+
'valueOf',
|
39
|
+
'hasOwnProperty',
|
40
|
+
'isPrototypeOf',
|
41
|
+
'propertyIsEnumerable',
|
42
|
+
'constructor'
|
43
|
+
],
|
44
|
+
dontEnumsLength = dontEnums.length;
|
45
|
+
|
46
|
+
return function (obj) {
|
47
|
+
if (typeof obj !== 'object' && typeof obj !== 'function' ||
|
48
|
+
obj === null) {
|
49
|
+
throw new TypeError('Object.keys called on non-object');
|
50
|
+
}
|
51
|
+
|
52
|
+
var result = [];
|
53
|
+
|
54
|
+
for (var prop in obj) {
|
55
|
+
if (hasOwnProperty.call(obj, prop)) {
|
56
|
+
result.push(prop);
|
57
|
+
}
|
58
|
+
}
|
59
|
+
|
60
|
+
if (hasDontEnumBug) {
|
61
|
+
for (var i = 0; i < dontEnumsLength; i++) {
|
62
|
+
if (hasOwnProperty.call(obj,
|
63
|
+
dontEnums[i])) {
|
64
|
+
result.push(dontEnums[i]);
|
65
|
+
}
|
66
|
+
}
|
67
|
+
}
|
68
|
+
return result;
|
69
|
+
};
|
70
|
+
})();
|
71
|
+
}
|
72
|
+
|
73
|
+
// stub out lazyBind if we don't have it.
|
74
|
+
try {
|
75
|
+
angular.module('lazyBind');
|
76
|
+
lazyBindFound = true;
|
77
|
+
}
|
78
|
+
catch (e) {
|
79
|
+
angular.module('lazyBind', []).factory('$lazyBind', angular.noop);
|
80
|
+
}
|
81
|
+
|
82
|
+
/**
|
83
|
+
* @ngdoc service
|
84
|
+
* @name decipher.history.service:History
|
85
|
+
* @description
|
86
|
+
* Provides an API for keeping a history of model values.
|
87
|
+
*/
|
88
|
+
angular.module('decipher.history', ['lazyBind']).service('History',
|
89
|
+
function ($parse, $rootScope, $interpolate, $lazyBind, $timeout, $log,
|
90
|
+
$injector) {
|
91
|
+
var service = this,
|
92
|
+
history = {},
|
93
|
+
pointers = {},
|
94
|
+
watches = {},
|
95
|
+
watchObjs = {},
|
96
|
+
lazyWatches = {},
|
97
|
+
descriptions = {},
|
98
|
+
// TODO: async safe?
|
99
|
+
batching = false, // whether or not we are currently in a batch
|
100
|
+
deepWatchId = 0; // incrementing ID of deep {@link decipher.history.object:Watch Watch instance}s
|
101
|
+
|
102
|
+
/**
|
103
|
+
* @ngdoc object
|
104
|
+
* @name decipher.history.object:Watch
|
105
|
+
* @overview
|
106
|
+
* @constructor
|
107
|
+
* @description
|
108
|
+
* An object instance that provides several methods for executing handlers after
|
109
|
+
* certain changes have been made.
|
110
|
+
*
|
111
|
+
* Each function return the `Watch` instance, so you can chain the calls.
|
112
|
+
*
|
113
|
+
* See the docs for {@link decipher.history.service:History#deepWatch History.deepWatch()} for an example of using these functions.
|
114
|
+
*
|
115
|
+
* @todo ability to remove all handlers at once, or all handlers of a certain type
|
116
|
+
*/
|
117
|
+
var Watch = function Watch(exp, scope) {
|
118
|
+
this.exp = exp;
|
119
|
+
this.scope = scope || $rootScope;
|
120
|
+
|
121
|
+
this.$handlers = {
|
122
|
+
$change : {},
|
123
|
+
$undo : {},
|
124
|
+
$rollback : {},
|
125
|
+
$redo : {},
|
126
|
+
$revert : {},
|
127
|
+
};
|
128
|
+
|
129
|
+
this.$ignores = {};
|
130
|
+
};
|
131
|
+
|
132
|
+
/**
|
133
|
+
* @description
|
134
|
+
* Helper method for the add*Handler functions.
|
135
|
+
* @param {string} where Type of handler, corresponds to object defined in constructor
|
136
|
+
* @param {string} name Name of handler to be supplied by user
|
137
|
+
* @param {Function} fn Handler function to execute
|
138
|
+
* @param {Object} resolve Mapping of function parameters to values
|
139
|
+
* @private
|
140
|
+
* @returns {Watch} This {@link decipher.history.object:Watch Watch instance}
|
141
|
+
*/
|
142
|
+
Watch.prototype._addHandler =
|
143
|
+
function _addHandler(where, name, fn, resolve) {
|
144
|
+
if (!where || !name || !fn) {
|
145
|
+
throw new Error('invalid parameters to _addHandler()');
|
146
|
+
}
|
147
|
+
this.$handlers[where][name] = {
|
148
|
+
fn: fn,
|
149
|
+
resolve: resolve || {}
|
150
|
+
};
|
151
|
+
return this;
|
152
|
+
};
|
153
|
+
|
154
|
+
/**
|
155
|
+
* @description
|
156
|
+
* Helper method for remove*Handler functions.
|
157
|
+
* @param {string} where Type of handler, corresponds to object defined in constructor
|
158
|
+
* @param {string} name Name of handler to be supplied by user
|
159
|
+
* @private
|
160
|
+
* @returns {Watch} This {@link decipher.history.object:Watch Watch instance}
|
161
|
+
*/
|
162
|
+
Watch.prototype._removeHandler = function (where, name) {
|
163
|
+
if (!name) {
|
164
|
+
throw new Error('invalid parameters to _removeHandler()');
|
165
|
+
}
|
166
|
+
delete this.$handlers[where][name];
|
167
|
+
return this;
|
168
|
+
};
|
169
|
+
|
170
|
+
/**
|
171
|
+
* @ngdoc function
|
172
|
+
* @name decipher.history.object:Watch#addChangeHandler
|
173
|
+
* @methodOf decipher.history.object:Watch
|
174
|
+
* @method
|
175
|
+
* @param {string} name Unique name of handler
|
176
|
+
* @param {Function} fn Function to execute upon change
|
177
|
+
* @param {object} resolve Mapping of function parameters to values
|
178
|
+
* @description
|
179
|
+
* Adds a change handler function with name `name` to be executed
|
180
|
+
* whenever a value matching this watch's expression changes (is archived).
|
181
|
+
* @returns {Watch} This {@link decipher.history.object:Watch Watch instance}
|
182
|
+
*/
|
183
|
+
Watch.prototype.addChangeHandler =
|
184
|
+
function addChangeHandler(name, fn, resolve) {
|
185
|
+
if (!name || !fn) {
|
186
|
+
throw new Error('invalid parameters');
|
187
|
+
}
|
188
|
+
return this._addHandler('$change', name, fn, resolve);
|
189
|
+
};
|
190
|
+
/**
|
191
|
+
* @ngdoc function
|
192
|
+
* @name decipher.history.object:Watch#addUndoHandler
|
193
|
+
* @methodOf decipher.history.object:Watch
|
194
|
+
* @method
|
195
|
+
* @param {string} name Unique name of handler
|
196
|
+
* @param {Function} fn Function to execute upon change
|
197
|
+
* @param {object} resolve Mapping of function parameters to values
|
198
|
+
* @description
|
199
|
+
* Adds an undo handler function with name `name` to be executed
|
200
|
+
* whenever a value matching this watch's expression is undone.
|
201
|
+
* @returns {Watch} This {@link decipher.history.object:Watch Watch instance}
|
202
|
+
*/
|
203
|
+
Watch.prototype.addUndoHandler =
|
204
|
+
function addUndoHandler(name, fn, resolve) {
|
205
|
+
if (!name || !fn) {
|
206
|
+
throw new Error('invalid parameters');
|
207
|
+
}
|
208
|
+
return this._addHandler('$undo', name, fn, resolve);
|
209
|
+
};
|
210
|
+
/**
|
211
|
+
* @ngdoc function
|
212
|
+
* @name decipher.history.object:Watch#addRedoHandler
|
213
|
+
* @methodOf decipher.history.object:Watch
|
214
|
+
* @method
|
215
|
+
* @param {string} name Unique name of handler
|
216
|
+
* @param {Function} fn Function to execute upon change
|
217
|
+
* @param {object} resolve Mapping of function parameters to values
|
218
|
+
* @description
|
219
|
+
* Adds a redo handler function with name `name` to be executed
|
220
|
+
* whenever a value matching this watch's expression is redone.
|
221
|
+
* @returns {Watch} This {@link decipher.history.object:Watch Watch instance}
|
222
|
+
*/
|
223
|
+
Watch.prototype.addRedoHandler =
|
224
|
+
function addRedoHandler(name, fn, resolve) {
|
225
|
+
if (!name || !fn) {
|
226
|
+
throw new Error('invalid parameters');
|
227
|
+
}
|
228
|
+
return this._addHandler('$redo', name, fn, resolve);
|
229
|
+
};
|
230
|
+
/**
|
231
|
+
* @ngdoc function
|
232
|
+
* @name decipher.history.object:Watch#addRevertHandler
|
233
|
+
* @methodOf decipher.history.object:Watch
|
234
|
+
* @method
|
235
|
+
* @param {string} name Unique name of handler
|
236
|
+
* @param {Function} fn Function to execute upon change
|
237
|
+
* @param {object} resolve Mapping of function parameters to values
|
238
|
+
* @description
|
239
|
+
* Adds a revert handler function with name `name` to be executed
|
240
|
+
* whenever a value matching this watch's expression is reverted.
|
241
|
+
* @returns {Watch} This {@link decipher.history.object:Watch Watch instance}
|
242
|
+
*/
|
243
|
+
Watch.prototype.addRevertHandler =
|
244
|
+
function addRevertHandler(name, fn, resolve) {
|
245
|
+
if (!name || !fn) {
|
246
|
+
throw new Error('invalid parameters');
|
247
|
+
}
|
248
|
+
return this._addHandler('$revert', name, fn, resolve);
|
249
|
+
};
|
250
|
+
/**
|
251
|
+
* @ngdoc function
|
252
|
+
* @name decipher.history.object:Watch#addRollbackHandler
|
253
|
+
* @methodOf decipher.history.object:Watch
|
254
|
+
* @method
|
255
|
+
* @param {string} name Unique name of handler
|
256
|
+
* @param {Function} fn Function to execute upon change
|
257
|
+
* @param {object} resolve Mapping of function parameters to values
|
258
|
+
* @description
|
259
|
+
* Adds a rollback handler function with name `name` to be executed
|
260
|
+
* whenever the batch tied to this watch is rolled back.
|
261
|
+
* @returns {Watch} This {@link decipher.history.object:Watch Watch instance}
|
262
|
+
*/
|
263
|
+
Watch.prototype.addRollbackHandler =
|
264
|
+
function addRollbackHandler(name, fn, resolve) {
|
265
|
+
if (!name || !fn) {
|
266
|
+
throw new Error('invalid parameters');
|
267
|
+
}
|
268
|
+
return this._addHandler('$rollback', name, fn, resolve);
|
269
|
+
};
|
270
|
+
|
271
|
+
/**
|
272
|
+
* @ngdoc function
|
273
|
+
* @name decipher.history.object:Watch#removeRevertHandler
|
274
|
+
* @methodOf decipher.history.object:Watch
|
275
|
+
* @method
|
276
|
+
* @param {string} name Name of handler to remove
|
277
|
+
* @returns {Watch} This {@link decipher.history.object:Watch Watch instance}
|
278
|
+
* @description
|
279
|
+
* Removes a revert handler with name `name`.
|
280
|
+
*/
|
281
|
+
Watch.prototype.removeRevertHandler = function removeRevertHandler(name) {
|
282
|
+
if (!name) {
|
283
|
+
throw new Error('invalid parameters');
|
284
|
+
}
|
285
|
+
return this._removeHandler('$revert', name);
|
286
|
+
};
|
287
|
+
/**
|
288
|
+
* @ngdoc function
|
289
|
+
* @name decipher.history.object:Watch#removeChangeHandler
|
290
|
+
* @methodOf decipher.history.object:Watch
|
291
|
+
* @method
|
292
|
+
* @param {string} name Name of handler to remove
|
293
|
+
* @returns {Watch} This {@link decipher.history.object:Watch Watch instance}
|
294
|
+
* @description
|
295
|
+
* Removes a change handler with name `name`.
|
296
|
+
*/
|
297
|
+
Watch.prototype.removeChangeHandler = function removeChangeHandler(name) {
|
298
|
+
if (!name) {
|
299
|
+
throw new Error('invalid parameters');
|
300
|
+
}
|
301
|
+
return this._removeHandler('$change', name);
|
302
|
+
};
|
303
|
+
/**
|
304
|
+
* @ngdoc function
|
305
|
+
* @name decipher.history.object:Watch#removeUndoHandler
|
306
|
+
* @methodOf decipher.history.object:Watch
|
307
|
+
* @method
|
308
|
+
* @param {string} name Name of handler to remove
|
309
|
+
* @returns {Watch} This {@link decipher.history.object:Watch Watch instance}
|
310
|
+
* @description
|
311
|
+
* Removes a undo handler with name `name`.
|
312
|
+
*/
|
313
|
+
Watch.prototype.removeUndoHandler = function removeUndoHandler(name) {
|
314
|
+
if (!name) {
|
315
|
+
throw new Error('invalid parameters');
|
316
|
+
}
|
317
|
+
return this._removeHandler('$undo', name);
|
318
|
+
};
|
319
|
+
|
320
|
+
/**
|
321
|
+
* @ngdoc function
|
322
|
+
* @name decipher.history.object:Watch#removeRollbackHandler
|
323
|
+
* @methodOf decipher.history.object:Watch
|
324
|
+
* @method
|
325
|
+
* @param {string} name Name of handler to remove
|
326
|
+
* @returns {Watch} This {@link decipher.history.object:Watch Watch instance}
|
327
|
+
* @description
|
328
|
+
* Removes a rollback handler with name `name`.
|
329
|
+
*/
|
330
|
+
Watch.prototype.removeRollbackHandler =
|
331
|
+
function removeRollbackHandler(name) {
|
332
|
+
return this._removeHandler('$rollback', name);
|
333
|
+
};
|
334
|
+
|
335
|
+
/**
|
336
|
+
* @ngdoc function
|
337
|
+
* @name decipher.history.object:Watch#removeRedoHandler
|
338
|
+
* @methodOf decipher.history.object:Watch
|
339
|
+
* @method
|
340
|
+
* @param {string} name Name of handler to remove
|
341
|
+
* @returns {Watch} This {@link decipher.history.object:Watch Watch instance}
|
342
|
+
* @description
|
343
|
+
* Removes a redo handler with name `name`.
|
344
|
+
*/
|
345
|
+
Watch.prototype.removeRedoHandler =
|
346
|
+
function removeRedoHandler(name) {
|
347
|
+
if (!name) {
|
348
|
+
throw new Error('invalid parameters');
|
349
|
+
}
|
350
|
+
return this._removeHandler('$redo', name);
|
351
|
+
};
|
352
|
+
|
353
|
+
/**
|
354
|
+
* Fires all handlers for a particular type, optionally w/ a scope.
|
355
|
+
* @param {string} where Watch type
|
356
|
+
* @param {string} exp Expression
|
357
|
+
* @param {Scope} [scope] Optional Scope
|
358
|
+
* @private
|
359
|
+
*/
|
360
|
+
Watch.prototype._fireHandlers =
|
361
|
+
function _fireHandlers(where, exp, scope) {
|
362
|
+
var hasScope = isDefined(scope),
|
363
|
+
localScope = this.scope, that = this;
|
364
|
+
forEach(this.$handlers[where], function (handler) {
|
365
|
+
var locals = {
|
366
|
+
$locals: localScope
|
367
|
+
};
|
368
|
+
if (isDefined(scope)) {
|
369
|
+
locals.$locals = scope;
|
370
|
+
}
|
371
|
+
if (isDefined(exp)) {
|
372
|
+
locals.$expression = exp;
|
373
|
+
}
|
374
|
+
forEach(handler.resolve, function (value, key) {
|
375
|
+
if (hasScope) {
|
376
|
+
locals[key] = $parse(value)(scope);
|
377
|
+
} else {
|
378
|
+
locals[key] = value;
|
379
|
+
}
|
380
|
+
});
|
381
|
+
$injector.invoke(handler.fn, scope || that, locals);
|
382
|
+
});
|
383
|
+
};
|
384
|
+
|
385
|
+
/**
|
386
|
+
* Fires the change handlers
|
387
|
+
* @param {Scope} scope Scope
|
388
|
+
* @param {string} exp Expression
|
389
|
+
* @private
|
390
|
+
*/
|
391
|
+
Watch.prototype._fireChangeHandlers =
|
392
|
+
function _fireChangeHandlers(exp, scope) {
|
393
|
+
this._fireHandlers('$change', exp, scope);
|
394
|
+
};
|
395
|
+
|
396
|
+
/**
|
397
|
+
* Fires the undo handlers
|
398
|
+
* @param {Scope} scope Scope
|
399
|
+
* @param {string} exp Expression
|
400
|
+
* @private
|
401
|
+
*/
|
402
|
+
Watch.prototype._fireUndoHandlers =
|
403
|
+
function _fireUndoHandlers(exp, scope) {
|
404
|
+
this._fireHandlers('$undo', exp, scope);
|
405
|
+
};
|
406
|
+
|
407
|
+
/**
|
408
|
+
* Fires the redo handlers
|
409
|
+
* @param {Scope} scope Scope
|
410
|
+
* @param {string} exp Expression
|
411
|
+
* @private
|
412
|
+
*/
|
413
|
+
Watch.prototype._fireRedoHandlers =
|
414
|
+
function _fireRedoHandlers(exp, scope) {
|
415
|
+
this._fireHandlers('$redo', exp, scope);
|
416
|
+
};
|
417
|
+
|
418
|
+
/**
|
419
|
+
* Fires the revert handlers
|
420
|
+
* @param {Scope} scope Scope
|
421
|
+
* @param {string} exp Expression
|
422
|
+
* @private
|
423
|
+
*/
|
424
|
+
Watch.prototype._fireRevertHandlers =
|
425
|
+
function _fireRevertHandlers(exp, scope) {
|
426
|
+
this._fireHandlers('$revert', exp, scope);
|
427
|
+
};
|
428
|
+
|
429
|
+
/**
|
430
|
+
* Fires the rollback handlers (note lack of scope and expression)
|
431
|
+
* @private
|
432
|
+
*/
|
433
|
+
Watch.prototype._fireRollbackHandlers =
|
434
|
+
function _fireRollbackHandlers() {
|
435
|
+
this._fireHandlers('$rollback');
|
436
|
+
};
|
437
|
+
|
438
|
+
/**
|
439
|
+
* Decline to broadcast an event for this Watch.
|
440
|
+
* @param {string} eventName Name of event to avoid. i.e. "History.archived"
|
441
|
+
* @param {Function=} callback Optional callback
|
442
|
+
* @param {Object=} resolve Optional mapping of parameters to invoke
|
443
|
+
* the callback with.
|
444
|
+
* @returns {Watch} this Watch object
|
445
|
+
*/
|
446
|
+
Watch.prototype.ignoreEvent =
|
447
|
+
function ignoreEvent(eventName, callback, resolve) {
|
448
|
+
// special case; we cannot ignore History.archived within a Watch obj
|
449
|
+
// created from a batch. there may be a way around this.
|
450
|
+
if (this.exp === null && eventName === 'History.archived') {
|
451
|
+
$log.warn('cannot ignore History.archived event for batch');
|
452
|
+
return this;
|
453
|
+
}
|
454
|
+
resolve = resolve || {};
|
455
|
+
if (isFunction(callback)) {
|
456
|
+
this.$ignores[eventName] = {
|
457
|
+
callback: callback,
|
458
|
+
resolve: resolve
|
459
|
+
};
|
460
|
+
} else if (isDefined(callback)) {
|
461
|
+
this.$ignores[eventName] = {
|
462
|
+
callback: function cb() {
|
463
|
+
return callback;
|
464
|
+
},
|
465
|
+
resolve: resolve
|
466
|
+
};
|
467
|
+
}
|
468
|
+
return this;
|
469
|
+
};
|
470
|
+
|
471
|
+
/**
|
472
|
+
* Broadcasts an event, taking ignored events into account.
|
473
|
+
* @param {string} eventName Event to broadcast
|
474
|
+
* @param {*} data Some data to pass
|
475
|
+
* @private
|
476
|
+
*/
|
477
|
+
Watch.prototype._broadcast = function _broadcast(eventName, data) {
|
478
|
+
var ignore = this.$ignores[eventName];
|
479
|
+
if (!ignore ||
|
480
|
+
(isFunction(ignore.callback) &&
|
481
|
+
!$injector.invoke(ignore.callback, this.scope, ignore.resolve))) {
|
482
|
+
$rootScope.$broadcast(eventName, data);
|
483
|
+
}
|
484
|
+
};
|
485
|
+
|
486
|
+
/**
|
487
|
+
* Undoes last change against this watch object's target.
|
488
|
+
*/
|
489
|
+
Watch.prototype.undo = function undo() {
|
490
|
+
if (this.exp === null) {
|
491
|
+
$log.warn("attempt to undo a batch; use rollback() instead");
|
492
|
+
return;
|
493
|
+
}
|
494
|
+
service.undo(this.exp, this.scope);
|
495
|
+
};
|
496
|
+
|
497
|
+
/**
|
498
|
+
* Redoes last undo against this watch object's target.
|
499
|
+
*/
|
500
|
+
Watch.prototype.redo = function redo() {
|
501
|
+
if (this.exp === null) {
|
502
|
+
$log.warn("attempt to redo a batch; just execute the batch callback again");
|
503
|
+
}
|
504
|
+
service.redo(this.exp, this.scope);
|
505
|
+
};
|
506
|
+
|
507
|
+
/**
|
508
|
+
* Reverts this target's watch object.
|
509
|
+
* @param {number=0} pointer Pointer to revert to
|
510
|
+
*/
|
511
|
+
Watch.prototype.revert = function revert(pointer) {
|
512
|
+
if (this.exp === null) {
|
513
|
+
$log.warn("attempt to revert a batch; use rollback() instead");
|
514
|
+
}
|
515
|
+
service.revert(this.exp, this.scope, pointer);
|
516
|
+
};
|
517
|
+
|
518
|
+
/**
|
519
|
+
* Whether or not you may undo this watch object's target
|
520
|
+
* @returns {boolean}
|
521
|
+
*/
|
522
|
+
Watch.prototype.canUndo = function canUndo() {
|
523
|
+
return this.exp === null ? false :
|
524
|
+
service.canUndo(this.exp, this.scope);
|
525
|
+
};
|
526
|
+
|
527
|
+
/**
|
528
|
+
* Whether or not you may redo this watch object's target
|
529
|
+
* @returns {boolean}
|
530
|
+
*/
|
531
|
+
Watch.prototype.canRedo = function canRedo() {
|
532
|
+
return this.exp === null ? false :
|
533
|
+
service.canRedo(this.exp, this.scope);
|
534
|
+
};
|
535
|
+
|
536
|
+
/**
|
537
|
+
* Evaluates an expression on the scope lazily. That means it will return
|
538
|
+
* a new value every DEFAULT_TIMEOUT ms at maximum, even if you change it between
|
539
|
+
* now and then. This allows us to $broadcast at an interval instead of after
|
540
|
+
* every scope change.
|
541
|
+
* @param {Object} scope AngularJS Scope
|
542
|
+
* @param {string} exp AngularJS expression to evaluate
|
543
|
+
* @param {number} [timeout=DEFAULT_TIMEOUT] How often to change the value
|
544
|
+
* @returns {Function}
|
545
|
+
*/
|
546
|
+
var lazyWatch = function lazyWatch(scope, exp, timeout) {
|
547
|
+
var bind = $lazyBind(scope);
|
548
|
+
bind.cacheTime(timeout || DEFAULT_TIMEOUT);
|
549
|
+
|
550
|
+
/**
|
551
|
+
* This is the "expression function" we use to $watch with. You normally
|
552
|
+
* $watch a string, but you can also watch a function, and this is one of
|
553
|
+
* those functions. This is where the actual lazy evaluation happens.
|
554
|
+
*/
|
555
|
+
return function () {
|
556
|
+
return bind.call(scope, exp);
|
557
|
+
};
|
558
|
+
};
|
559
|
+
|
560
|
+
/**
|
561
|
+
* Initializes object stores for a Scope id
|
562
|
+
* @param {string} id Sccope id
|
563
|
+
* @private
|
564
|
+
*/
|
565
|
+
this._initStores = function _initStores(id) {
|
566
|
+
if (isUndefined(watches[id])) {
|
567
|
+
watches[id] = {};
|
568
|
+
}
|
569
|
+
if (isUndefined(lazyWatches[id])) {
|
570
|
+
lazyWatches[id] = {};
|
571
|
+
}
|
572
|
+
if (isUndefined(descriptions[id])) {
|
573
|
+
descriptions[id] = {};
|
574
|
+
}
|
575
|
+
if (isUndefined(history[id])) {
|
576
|
+
history[id] = {};
|
577
|
+
}
|
578
|
+
if (isUndefined(watchObjs[id])) {
|
579
|
+
watchObjs[id] = {};
|
580
|
+
}
|
581
|
+
if (isUndefined(pointers[id])) {
|
582
|
+
pointers[id] = {};
|
583
|
+
}
|
584
|
+
};
|
585
|
+
|
586
|
+
/**
|
587
|
+
* When an expression changes, store the information about it
|
588
|
+
* and increment a pointer.
|
589
|
+
* @param {string|Function} exp Expression
|
590
|
+
* @param {string} id Scope $id
|
591
|
+
* @param {Scope} locals AngularJS scope
|
592
|
+
* @param {boolean} pass Whether or not to pass on the first call
|
593
|
+
* @param {string} description AngularJS string to interpolate
|
594
|
+
* @return {Function} Watch function
|
595
|
+
* @private
|
596
|
+
*/
|
597
|
+
this._archive = function (exp, id, locals, pass, description) {
|
598
|
+
var _initStores = this._initStores;
|
599
|
+
return function (newVal, oldVal) {
|
600
|
+
var watchObj;
|
601
|
+
_initStores(id);
|
602
|
+
if (description) {
|
603
|
+
descriptions[id][exp] = $interpolate(description)(locals);
|
604
|
+
}
|
605
|
+
if (pass) {
|
606
|
+
pass = false;
|
607
|
+
return;
|
608
|
+
}
|
609
|
+
if (isUndefined(history[id][exp])) {
|
610
|
+
history[id][exp] = [];
|
611
|
+
}
|
612
|
+
if (isUndefined(pointers[id][exp])) {
|
613
|
+
pointers[id][exp] = 0;
|
614
|
+
}
|
615
|
+
history[id][exp].splice(pointers[id][exp] + 1);
|
616
|
+
history[id][exp].push(copy(newVal));
|
617
|
+
pointers[id][exp] = history[id][exp].length - 1;
|
618
|
+
if (pointers[id][exp] > 0 && isDefined(watchObjs[id]) &&
|
619
|
+
isDefined(watchObj = watchObjs[id][exp])) {
|
620
|
+
if (!batching) {
|
621
|
+
watchObj._fireChangeHandlers(exp, locals);
|
622
|
+
}
|
623
|
+
watchObj._broadcast('History.archived', {
|
624
|
+
expression: exp,
|
625
|
+
newValue: newVal,
|
626
|
+
oldValue: oldVal,
|
627
|
+
description: descriptions[id][exp],
|
628
|
+
locals: locals
|
629
|
+
});
|
630
|
+
}
|
631
|
+
};
|
632
|
+
};
|
633
|
+
|
634
|
+
/**
|
635
|
+
* @ngdoc function
|
636
|
+
* @name decipher.history.service:History#watch
|
637
|
+
* @method
|
638
|
+
* @methodOf decipher.history.service:History
|
639
|
+
* @description
|
640
|
+
* Register some expression(s) for watching.
|
641
|
+
* @param {string|string[]} exps Array of expressions or one expression as a string
|
642
|
+
* @param {Scope=} scope Scope; defaults to `$rootScope`
|
643
|
+
* @param {string=} description Description of this change
|
644
|
+
* @param {Object=} lazyOptions Options for lazy loading. Only valid
|
645
|
+
* property is `timeout` at this point
|
646
|
+
* @returns {Watch|Array} {@link decipher.history.object:Watch Watch instance} or array of them
|
647
|
+
*
|
648
|
+
* @example
|
649
|
+
* <example module="decipher.history">
|
650
|
+
<file name="script.js">
|
651
|
+
|
652
|
+
angular.module('decipher.history')
|
653
|
+
.run(function(History, $rootScope) {
|
654
|
+
$rootScope.foo = 'foo';
|
655
|
+
|
656
|
+
$rootScope.$on('History.archived', function(evt, data) {
|
657
|
+
$rootScope.message = data.description;
|
658
|
+
});
|
659
|
+
|
660
|
+
History.watch('foo', $rootScope, 'you changed the foo');
|
661
|
+
});
|
662
|
+
</file>
|
663
|
+
<file name="index.html">
|
664
|
+
<input type="text" ng-model="foo"/> {{foo}}<br/>
|
665
|
+
<span ng-show="message">{{message}}</span><br/>
|
666
|
+
</file>
|
667
|
+
</example>
|
668
|
+
*/
|
669
|
+
this.watch = function watch(exps, scope, description, lazyOptions) {
|
670
|
+
if (isUndefined(exps)) {
|
671
|
+
throw new Error('expression required');
|
672
|
+
}
|
673
|
+
scope = scope || $rootScope;
|
674
|
+
description = description || '';
|
675
|
+
var i,
|
676
|
+
id = scope.$id,
|
677
|
+
exp,
|
678
|
+
objs = [],
|
679
|
+
watchObj,
|
680
|
+
model;
|
681
|
+
|
682
|
+
if (!isArray(exps)) {
|
683
|
+
exps = [exps];
|
684
|
+
}
|
685
|
+
|
686
|
+
this._initStores(id);
|
687
|
+
|
688
|
+
i = exps.length;
|
689
|
+
while (i--) {
|
690
|
+
exp = exps[i];
|
691
|
+
|
692
|
+
// assert we have an assignable model
|
693
|
+
model = $parse(exp);
|
694
|
+
if (isUndefined(model.assign)) {
|
695
|
+
throw 'expression "' + exp +
|
696
|
+
'" is not an assignable expression';
|
697
|
+
}
|
698
|
+
|
699
|
+
// blast any old watches
|
700
|
+
if (isFunction(watches[id][exp])) {
|
701
|
+
watches[id][exp]();
|
702
|
+
}
|
703
|
+
|
704
|
+
descriptions[id][exp] = $interpolate(description)(scope);
|
705
|
+
|
706
|
+
this._watch(exp, scope, false, lazyOptions);
|
707
|
+
watchObjs[id][exp] = watchObj = new Watch(exp, scope);
|
708
|
+
objs.push(watchObj);
|
709
|
+
}
|
710
|
+
|
711
|
+
return objs.length > 1 ? objs : objs[0];
|
712
|
+
};
|
713
|
+
|
714
|
+
/**
|
715
|
+
* @ngdoc function
|
716
|
+
* @name decipher.history.service:History#deepWatch
|
717
|
+
* @method
|
718
|
+
* @methodOf decipher.history.service:History
|
719
|
+
* @description
|
720
|
+
* Allows you to watch an entire array/object full of objects, but only watch
|
721
|
+
* a certain property of each object.
|
722
|
+
*
|
723
|
+
* @example
|
724
|
+
* <example module="decipher.history">
|
725
|
+
<file name="script.js">
|
726
|
+
angular.module('decipher.history')
|
727
|
+
.run(function(History, $rootScope) {
|
728
|
+
var exp, locals;
|
729
|
+
|
730
|
+
$rootScope.foos = [
|
731
|
+
{id: 1, name: 'herp'},
|
732
|
+
{id: 2, name: 'derp'}
|
733
|
+
];
|
734
|
+
|
735
|
+
$rootScope.$on('History.archived', function(evt, data) {
|
736
|
+
$rootScope.message = data.description;
|
737
|
+
exp = data.expression;
|
738
|
+
locals = data.locals;
|
739
|
+
})
|
740
|
+
|
741
|
+
History.deepWatch('foo.name for foo in foos', $rootScope,
|
742
|
+
'Changed {{foo.id}} to name "{{foo.name}}"')
|
743
|
+
.addChangeHandler('myChangeHandler', function($expression,
|
744
|
+
$locals, foo) {
|
745
|
+
console.log(foo);
|
746
|
+
console.log("(totally hit the server and update the model)");
|
747
|
+
$rootScope.undo = function() {
|
748
|
+
History.undo($expression, $locals);
|
749
|
+
};
|
750
|
+
$rootScope.canUndo = function() {
|
751
|
+
return History.canUndo($expression, $locals);
|
752
|
+
};
|
753
|
+
}, {foo: 'foo'});
|
754
|
+
});
|
755
|
+
</file>
|
756
|
+
<file name="index.html">
|
757
|
+
<input type="text" ng-model="foos[0].name"/> {{foos[0].name}}<br/>
|
758
|
+
<span ng-show="message">{{message}}</span><br/>
|
759
|
+
<button ng-disabled="!canUndo()" ng-click="undo()">Undo!</button>
|
760
|
+
</file>
|
761
|
+
</example>
|
762
|
+
* @param {(string|string[])} exp Expression or array of expressions to watch
|
763
|
+
* @param {Scope=} scope Scope; defaults to `$rootScope`
|
764
|
+
* @param {string=} description Description of this change
|
765
|
+
* @param {Object=} lazyOptions Options for lazy loading. Only valid
|
766
|
+
* property is `timeout` at this point
|
767
|
+
* @return {Watch} {@link decipher.history.object:Watch Watch instance}
|
768
|
+
*/
|
769
|
+
this.deepWatch =
|
770
|
+
function deepWatch(exp, scope, description, lazyOptions) {
|
771
|
+
var match,
|
772
|
+
targetName,
|
773
|
+
valueFn,
|
774
|
+
keyName,
|
775
|
+
value,
|
776
|
+
valueName,
|
777
|
+
valuesName,
|
778
|
+
watchObj,
|
779
|
+
id = scope.$id,
|
780
|
+
_clear = bind(this, this._clear),
|
781
|
+
_initStores = this._initStores,
|
782
|
+
_archive = bind(this, this._archive),
|
783
|
+
createDeepWatch = function createDeepWatch(targetName, valueName,
|
784
|
+
keyName, watchObj) {
|
785
|
+
return function (values) {
|
786
|
+
forEach(values, function (v, k) {
|
787
|
+
|
788
|
+
var locals = scope.$new(),
|
789
|
+
id = locals.$id;
|
790
|
+
locals.$$deepWatchId = scope.$$deepWatch[targetName];
|
791
|
+
locals.$$deepWatchTargetName = targetName;
|
792
|
+
locals[valueName] = v;
|
793
|
+
if (keyName) {
|
794
|
+
locals[keyName] = k;
|
795
|
+
}
|
796
|
+
value = valueFn(scope, locals);
|
797
|
+
|
798
|
+
_initStores(id);
|
799
|
+
|
800
|
+
descriptions[id][exp] = $interpolate(description)(locals);
|
801
|
+
|
802
|
+
if (isFunction(watches[id][targetName])) {
|
803
|
+
watches[id][targetName]();
|
804
|
+
}
|
805
|
+
|
806
|
+
if (lazyBindFound && isObject(lazyOptions)) {
|
807
|
+
watches[id][targetName] =
|
808
|
+
locals.$watch(lazyWatch(locals, targetName,
|
809
|
+
lazyOptions.timeout || 500),
|
810
|
+
_archive(targetName, id, locals, false, description),
|
811
|
+
true);
|
812
|
+
lazyWatches[id][targetName] = true;
|
813
|
+
}
|
814
|
+
else {
|
815
|
+
watches[id][targetName] = locals.$watch(targetName,
|
816
|
+
_archive(targetName, id, locals, false, description),
|
817
|
+
true);
|
818
|
+
lazyWatches[id][targetName] = false;
|
819
|
+
}
|
820
|
+
|
821
|
+
watchObjs[id][targetName] = watchObj;
|
822
|
+
|
823
|
+
locals.$on('$destroy', function () {
|
824
|
+
_clear(scope);
|
825
|
+
});
|
826
|
+
|
827
|
+
});
|
828
|
+
|
829
|
+
};
|
830
|
+
};
|
831
|
+
|
832
|
+
description = description || '';
|
833
|
+
if (!(match = exp.match(DEEPWATCH_EXP))) {
|
834
|
+
throw 'expected expression in form of "_select_ for (_key_,)? _value_ in _collection_" but got "' +
|
835
|
+
exp + '"';
|
836
|
+
}
|
837
|
+
targetName = match[1];
|
838
|
+
valueName = match[4] || match[2];
|
839
|
+
valueFn = $parse(valueName);
|
840
|
+
keyName = match[3];
|
841
|
+
valuesName = match[5];
|
842
|
+
|
843
|
+
if (isUndefined(scope.$$deepWatch)) {
|
844
|
+
scope.$$deepWatch = {};
|
845
|
+
}
|
846
|
+
|
847
|
+
// if we already have a deepWatch on this value, we
|
848
|
+
// need to kill all the child scopes. because reasons
|
849
|
+
if (isDefined(scope.$$deepWatch[targetName])) {
|
850
|
+
_clear(scope, targetName);
|
851
|
+
}
|
852
|
+
scope.$$deepWatch[targetName] = ++deepWatchId;
|
853
|
+
|
854
|
+
_initStores(id);
|
855
|
+
watchObjs[id][targetName] = watchObj = new Watch(targetName, scope);
|
856
|
+
|
857
|
+
// TODO: assert this doesn't leak memory like crazy. it might if
|
858
|
+
// we remove things from the values context.
|
859
|
+
watches[id][targetName] = scope.$watchCollection(valuesName,
|
860
|
+
createDeepWatch(targetName, valueName, keyName,
|
861
|
+
watchObj));
|
862
|
+
|
863
|
+
return watchObj;
|
864
|
+
};
|
865
|
+
|
866
|
+
/**
|
867
|
+
* Clears a bunch of information for a scope and optionally an array of expressions.
|
868
|
+
* Lacking an expression, this will eliminate an entire scopesworth of data.
|
869
|
+
* It will recognize deep watches and clear them out completely.
|
870
|
+
* @param {Scope} scope Scope obj
|
871
|
+
* @param {(string|string[])} exps Expression or array of expressions
|
872
|
+
* @private
|
873
|
+
*/
|
874
|
+
this._clear = function _clear(scope, exps) {
|
875
|
+
var id = scope.$id,
|
876
|
+
i,
|
877
|
+
nextSibling,
|
878
|
+
exp,
|
879
|
+
clear = function clear(id, key) {
|
880
|
+
var zap = function zap(what) {
|
881
|
+
if (isDefined(what[id][key])) {
|
882
|
+
delete what[id][key];
|
883
|
+
if (Object.keys(what[id]).length === 0) {
|
884
|
+
delete what[id];
|
885
|
+
}
|
886
|
+
}
|
887
|
+
};
|
888
|
+
|
889
|
+
if (isDefined(watches[id]) &&
|
890
|
+
isFunction(watches[id][key])) {
|
891
|
+
watches[id][key]();
|
892
|
+
}
|
893
|
+
if (isDefined(watches[id])) {
|
894
|
+
zap(watches);
|
895
|
+
}
|
896
|
+
if (isDefined(watchObjs[id])) {
|
897
|
+
zap(watchObjs);
|
898
|
+
}
|
899
|
+
if (isDefined(history[id])) {
|
900
|
+
zap(history);
|
901
|
+
}
|
902
|
+
if (isDefined(pointers[id])) {
|
903
|
+
zap(pointers);
|
904
|
+
}
|
905
|
+
if (isDefined(lazyWatches[id])) {
|
906
|
+
zap(lazyWatches);
|
907
|
+
}
|
908
|
+
},
|
909
|
+
|
910
|
+
clearAll = function clearAll(id) {
|
911
|
+
forEach(watches[id], function (watch) {
|
912
|
+
return isFunction(watch) && watch();
|
913
|
+
});
|
914
|
+
delete watches[id];
|
915
|
+
delete history[id];
|
916
|
+
delete pointers[id];
|
917
|
+
delete lazyWatches[id];
|
918
|
+
delete watchObjs[id];
|
919
|
+
};
|
920
|
+
|
921
|
+
if (isString(exps)) {
|
922
|
+
exps = [exps];
|
923
|
+
}
|
924
|
+
else if (isUndefined(exps) && isDefined(watches[id])) {
|
925
|
+
exps = Object.keys(watches[id]);
|
926
|
+
}
|
927
|
+
|
928
|
+
if (isDefined(exps)) {
|
929
|
+
i = exps.length;
|
930
|
+
while (i--) {
|
931
|
+
exp = exps[i];
|
932
|
+
clear(id, exp);
|
933
|
+
}
|
934
|
+
} else {
|
935
|
+
clearAll(id);
|
936
|
+
}
|
937
|
+
nextSibling = scope.$$childHead;
|
938
|
+
while (nextSibling) {
|
939
|
+
this._clear(nextSibling, exp);
|
940
|
+
nextSibling = nextSibling.$$nextSibling;
|
941
|
+
}
|
942
|
+
};
|
943
|
+
|
944
|
+
|
945
|
+
/**
|
946
|
+
* @ngdoc function
|
947
|
+
* @name decipher.history.service:History#forget
|
948
|
+
* @method
|
949
|
+
* @methodOf decipher.history.service:History
|
950
|
+
* @description
|
951
|
+
* Unregister some watched expression(s).
|
952
|
+
* @param {(string|string[])} exps Array of expressions or one expression as a string
|
953
|
+
* @param {Scope=} scope Scope object; defaults to $rootScope
|
954
|
+
*/
|
955
|
+
this.forget = function forget(scope, exps) {
|
956
|
+
scope = scope || $rootScope;
|
957
|
+
if (isDefined(exps) && isString(exps)) {
|
958
|
+
exps = [exps];
|
959
|
+
}
|
960
|
+
this._clear(scope, exps);
|
961
|
+
};
|
962
|
+
|
963
|
+
/**
|
964
|
+
* Internal function to change some value in the stack to another.
|
965
|
+
* Kills the watch and then calls `_watch()` to restore it.
|
966
|
+
* @param {Scope} scope Scope object
|
967
|
+
* @param {string} exp AngularJS expression
|
968
|
+
* @param {array} stack History stack; see `History.history`
|
969
|
+
* @param {number} pointer Pointer
|
970
|
+
* @returns {{oldValue: {*}, newValue: {*}}} The old value and the new value
|
971
|
+
* @private
|
972
|
+
*/
|
973
|
+
this._do = function _do(scope, exp, stack, pointer) {
|
974
|
+
var model,
|
975
|
+
oldValue,
|
976
|
+
id = scope.$id;
|
977
|
+
if (isFunction(watches[id][exp])) {
|
978
|
+
watches[id][exp]();
|
979
|
+
delete watches[id][exp];
|
980
|
+
}
|
981
|
+
model = $parse(exp);
|
982
|
+
oldValue = model(scope);
|
983
|
+
// todo: assert there's no bug here with unassignable expressions
|
984
|
+
model.assign(scope, stack[pointer]);
|
985
|
+
this._watch(exp, scope, true);
|
986
|
+
return {
|
987
|
+
oldValue: oldValue,
|
988
|
+
newValue: model(scope)
|
989
|
+
};
|
990
|
+
};
|
991
|
+
|
992
|
+
/**
|
993
|
+
* @ngdoc function
|
994
|
+
* @name decipher.history.service:History#undo
|
995
|
+
* @method
|
996
|
+
* @methodOf decipher.history.service:History
|
997
|
+
* @description
|
998
|
+
* Undos an expression to last known value.
|
999
|
+
* @param {string} exp Expression to undo
|
1000
|
+
* @param {Scope=} scope Scope; defaults to `$rootScope`
|
1001
|
+
*/
|
1002
|
+
this.undo = function undo(exp, scope) {
|
1003
|
+
scope = scope || $rootScope;
|
1004
|
+
if (isUndefined(exp)) {
|
1005
|
+
throw new Error('expression required');
|
1006
|
+
}
|
1007
|
+
var id = scope.$id,
|
1008
|
+
scopeHistory = history[id],
|
1009
|
+
stack,
|
1010
|
+
values,
|
1011
|
+
pointer,
|
1012
|
+
watchObj;
|
1013
|
+
|
1014
|
+
if (isUndefined(scopeHistory)) {
|
1015
|
+
throw 'could not find history for scope ' + id;
|
1016
|
+
}
|
1017
|
+
|
1018
|
+
stack = scopeHistory[exp];
|
1019
|
+
if (isUndefined(stack)) {
|
1020
|
+
throw 'could not find history in scope "' + id +
|
1021
|
+
' against expression "' + exp + '"';
|
1022
|
+
}
|
1023
|
+
pointer = --pointers[id][exp];
|
1024
|
+
if (pointer < 0) {
|
1025
|
+
$log.warn('attempt to undo past history');
|
1026
|
+
pointers[id][exp]++;
|
1027
|
+
return;
|
1028
|
+
}
|
1029
|
+
values = this._do(scope, exp, stack, pointer);
|
1030
|
+
if (isDefined(watchObjs[id]) &&
|
1031
|
+
isDefined(watchObjs[id][exp])) {
|
1032
|
+
watchObj = watchObjs[id][exp];
|
1033
|
+
watchObj._fireUndoHandlers(exp, scope);
|
1034
|
+
watchObj._broadcast('History.undone', {
|
1035
|
+
expression: exp,
|
1036
|
+
newValue: values.newValue,
|
1037
|
+
oldValue: values.oldValue,
|
1038
|
+
description: descriptions[id][exp],
|
1039
|
+
scope: scope
|
1040
|
+
});
|
1041
|
+
}
|
1042
|
+
};
|
1043
|
+
|
1044
|
+
/**
|
1045
|
+
* Actually issues the appropriate scope.$watch
|
1046
|
+
* @param {string} exp Expression
|
1047
|
+
* @param {Scope=} scope Scope; defaults to $rootScope
|
1048
|
+
* @param {boolean=} pass Whether or not to skip the first watch execution. Defaults to false
|
1049
|
+
* @param {Object} lazyOptions Options to send the lazy module
|
1050
|
+
* @private
|
1051
|
+
*/
|
1052
|
+
this._watch = function _watch(exp, scope, pass, lazyOptions) {
|
1053
|
+
var id;
|
1054
|
+
scope = scope || $rootScope;
|
1055
|
+
pass = pass || false;
|
1056
|
+
id = scope.$id;
|
1057
|
+
|
1058
|
+
// do we have an array or object?
|
1059
|
+
if (lazyBindFound && (isObject(lazyOptions) ||
|
1060
|
+
(lazyWatches[id] && !!lazyWatches[id][exp]))) {
|
1061
|
+
watches[id][exp] =
|
1062
|
+
scope.$watch(lazyWatch(scope, exp, lazyOptions.timeout),
|
1063
|
+
bind(this, this._archive(exp, id, scope, pass)), true);
|
1064
|
+
lazyWatches[id][exp] = true;
|
1065
|
+
}
|
1066
|
+
else {
|
1067
|
+
watches[id][exp] =
|
1068
|
+
scope.$watch(exp, bind(this, this._archive(exp, id, scope, pass)),
|
1069
|
+
true);
|
1070
|
+
lazyWatches[id][exp] = false;
|
1071
|
+
}
|
1072
|
+
|
1073
|
+
};
|
1074
|
+
|
1075
|
+
/**
|
1076
|
+
* @ngdoc function
|
1077
|
+
* @name decipher.history.service:History#redo
|
1078
|
+
* @method
|
1079
|
+
* @methodOf decipher.history.service:History
|
1080
|
+
* @description
|
1081
|
+
* Redoes (?) the last undo.
|
1082
|
+
* @param {string} exp Expression to redo
|
1083
|
+
* @param {Scope=} scope Scope; defaults to `$rootScope`
|
1084
|
+
*/
|
1085
|
+
this.redo = function redo(exp, scope) {
|
1086
|
+
scope = scope || $rootScope;
|
1087
|
+
var id = scope.$id,
|
1088
|
+
stack = history[id][exp],
|
1089
|
+
values,
|
1090
|
+
pointer,
|
1091
|
+
watchObj;
|
1092
|
+
|
1093
|
+
if (isUndefined(stack)) {
|
1094
|
+
throw 'could not find history in scope "' + id +
|
1095
|
+
' against expression "' + exp + '"';
|
1096
|
+
}
|
1097
|
+
pointer = ++pointers[id][exp];
|
1098
|
+
if (pointer === stack.length) {
|
1099
|
+
$log.warn('attempt to redo past history');
|
1100
|
+
pointers[id][exp]--;
|
1101
|
+
return;
|
1102
|
+
}
|
1103
|
+
|
1104
|
+
values = this._do(scope, exp, stack, pointer);
|
1105
|
+
|
1106
|
+
if (isDefined(watchObjs[id]) &&
|
1107
|
+
isDefined(watchObjs[id][exp])) {
|
1108
|
+
watchObj = watchObjs[id][exp];
|
1109
|
+
watchObj._fireRedoHandlers(exp, scope);
|
1110
|
+
watchObj._broadcast('History.redone', {
|
1111
|
+
expression: exp,
|
1112
|
+
oldValue: copy(values.newValue),
|
1113
|
+
newValue: copy(values.oldValue),
|
1114
|
+
description: descriptions[id][exp],
|
1115
|
+
scope: scope
|
1116
|
+
});
|
1117
|
+
}
|
1118
|
+
};
|
1119
|
+
|
1120
|
+
/**
|
1121
|
+
* @ngdoc function
|
1122
|
+
* @name decipher.history.service:History#canUndo
|
1123
|
+
* @method
|
1124
|
+
* @methodOf decipher.history.service:History
|
1125
|
+
* @description
|
1126
|
+
* Whether or not we have accumulated any history for a particular expression.
|
1127
|
+
* @param {string} exp Expression
|
1128
|
+
* @param {Scope=} scope Scope; defaults to $rootScope
|
1129
|
+
* @return {boolean} Whether or not you can issue an `undo()`
|
1130
|
+
* @example
|
1131
|
+
* <example module="decipher.history">
|
1132
|
+
<file name="script.js">
|
1133
|
+
angular.module('decipher.history').run(function(History, $rootScope) {
|
1134
|
+
$rootScope.foo = 'bar';
|
1135
|
+
History.watch('foo');
|
1136
|
+
$rootScope.canUndo = History.canUndo;
|
1137
|
+
});
|
1138
|
+
</file>
|
1139
|
+
<file name="index.html">
|
1140
|
+
<input type="text" ng-model="foo"/> Can undo? {{canUndo('foo')}}
|
1141
|
+
</file>
|
1142
|
+
</example>
|
1143
|
+
*/
|
1144
|
+
this.canUndo = function canUndo(exp, scope) {
|
1145
|
+
var id;
|
1146
|
+
scope = scope || $rootScope;
|
1147
|
+
id = scope.$id;
|
1148
|
+
return isDefined(pointers[id]) &&
|
1149
|
+
isDefined(pointers[id][exp]) &&
|
1150
|
+
pointers[id][exp] > 0;
|
1151
|
+
};
|
1152
|
+
|
1153
|
+
/**
|
1154
|
+
* @ngdoc function
|
1155
|
+
* @name decipher.history.service:History#canRedo
|
1156
|
+
* @method
|
1157
|
+
* @methodOf decipher.history.service:History
|
1158
|
+
* @description
|
1159
|
+
* Whether or not we can redo an expression's value.
|
1160
|
+
* @param {string} exp Expression
|
1161
|
+
* @param {Scope=} scope Scope; defaults to $rootScope
|
1162
|
+
* @return {Boolean} Whether or not you can issue a `redo()`
|
1163
|
+
* @example
|
1164
|
+
* <example module="decipher.history">
|
1165
|
+
<file name="script.js">
|
1166
|
+
angular.module('decipher.history').run(function(History, $rootScope) {
|
1167
|
+
$rootScope.foo = 'bar';
|
1168
|
+
History.watch('foo');
|
1169
|
+
$rootScope.canRedo = History.canRedo;
|
1170
|
+
$rootScope.canUndo = History.canUndo;
|
1171
|
+
$rootScope.undo = History.undo;
|
1172
|
+
});
|
1173
|
+
</file>
|
1174
|
+
<file name="index.html">
|
1175
|
+
<input type="text" ng-model="foo"/> <br/>
|
1176
|
+
<button ng-show="canUndo('foo')" ng-click="undo('foo')">Undo</button><br/>
|
1177
|
+
Can redo? {{canRedo('foo')}}
|
1178
|
+
</file>
|
1179
|
+
</example>
|
1180
|
+
*/
|
1181
|
+
this.canRedo = function canRedo(exp, scope) {
|
1182
|
+
var id;
|
1183
|
+
scope = scope || $rootScope;
|
1184
|
+
id = scope.$id;
|
1185
|
+
return isDefined(pointers[id]) &&
|
1186
|
+
isDefined(pointers[id][exp]) &&
|
1187
|
+
pointers[id][exp] < history[id][exp].length - 1;
|
1188
|
+
};
|
1189
|
+
|
1190
|
+
/**
|
1191
|
+
* @ngdoc function
|
1192
|
+
* @method
|
1193
|
+
* @methodOf decipher.history.service:History
|
1194
|
+
* @name decipher.history.service:History#revert
|
1195
|
+
* @description
|
1196
|
+
* Reverts to earliest known value of some expression, or at a particular
|
1197
|
+
* pointer if you please.
|
1198
|
+
* @param {string} exp Expression
|
1199
|
+
* @param {Scope=} scope Scope; defaults to $rootScope
|
1200
|
+
* @param {number=} pointer Optional; defaults to 0
|
1201
|
+
*/
|
1202
|
+
this.revert = function (exp, scope, pointer) {
|
1203
|
+
scope = scope || $rootScope;
|
1204
|
+
pointer = pointer || 0;
|
1205
|
+
var id = scope.$id,
|
1206
|
+
stack = history[id][exp],
|
1207
|
+
values,
|
1208
|
+
watchObj;
|
1209
|
+
|
1210
|
+
if (isUndefined(stack)) {
|
1211
|
+
$log.warn('nothing to revert');
|
1212
|
+
return;
|
1213
|
+
}
|
1214
|
+
values = this._do(scope, exp, stack, pointer);
|
1215
|
+
|
1216
|
+
// wait; what is this?
|
1217
|
+
history[id][exp].splice();
|
1218
|
+
pointers[id][exp] = pointer;
|
1219
|
+
|
1220
|
+
if (isDefined(watchObjs[id]) &&
|
1221
|
+
isDefined(watchObjs[id][exp])) {
|
1222
|
+
watchObj = watchObjs[id][exp];
|
1223
|
+
watchObj._fireRevertHandlers(exp, scope);
|
1224
|
+
watchObj._broadcast('History.reverted', {
|
1225
|
+
expression: exp,
|
1226
|
+
oldValue: copy(values.newValue),
|
1227
|
+
newValue: copy(values.oldValue),
|
1228
|
+
description: descriptions[id][exp],
|
1229
|
+
scope: scope,
|
1230
|
+
pointer: pointer
|
1231
|
+
});
|
1232
|
+
}
|
1233
|
+
};
|
1234
|
+
|
1235
|
+
/**
|
1236
|
+
* @ngdoc function
|
1237
|
+
* @name decipher.history.service:History#batch
|
1238
|
+
* @method
|
1239
|
+
* @methodOf decipher.history.service:History
|
1240
|
+
* @description
|
1241
|
+
* Executes a function within a batch context which can then be rolled back.
|
1242
|
+
* @param {function} fn Function to execute
|
1243
|
+
* @param {Scope=} scope Scope object; defaults to `$rootScope`
|
1244
|
+
* @param {string=} description Description of this change
|
1245
|
+
* @returns {Watch} {@link decipher.history.object:Watch Watch instance}
|
1246
|
+
* @example
|
1247
|
+
<example module="decipher.history">
|
1248
|
+
<file name="script.js">
|
1249
|
+
angular.module('decipher.history').run(function(History, $rootScope) {
|
1250
|
+
var t;
|
1251
|
+
|
1252
|
+
$rootScope.herp = 'derp';
|
1253
|
+
$rootScope.bar = 'baz';
|
1254
|
+
$rootScope.frick = 'frack';
|
1255
|
+
|
1256
|
+
$rootScope.$on('History.batchEnded', function(evt, data) {
|
1257
|
+
t = data.transaction;
|
1258
|
+
});
|
1259
|
+
|
1260
|
+
History.watch('herp');
|
1261
|
+
History.watch('bar');
|
1262
|
+
History.watch('frick');
|
1263
|
+
|
1264
|
+
$rootScope.batch = function() {
|
1265
|
+
History.batch(function() {
|
1266
|
+
$rootScope.herp = 'derp2';
|
1267
|
+
$rootScope.bar = 'baz2';
|
1268
|
+
$rootScope.frick = 'frack2';
|
1269
|
+
})
|
1270
|
+
.addRollbackHandler('myRollbackHandler', function() {
|
1271
|
+
$rootScope.message = 'rolled a bunch of stuff back';
|
1272
|
+
});
|
1273
|
+
$rootScope.message = "batch complete";
|
1274
|
+
};
|
1275
|
+
|
1276
|
+
$rootScope.rollback = function() {
|
1277
|
+
if (isDefined(t)) {
|
1278
|
+
History.rollback(t);
|
1279
|
+
}
|
1280
|
+
};
|
1281
|
+
});
|
1282
|
+
</file>
|
1283
|
+
<file name="index.html">
|
1284
|
+
<ul>
|
1285
|
+
<li>herp: {{herp}}</li>
|
1286
|
+
<li>bar: {{bar}}</li>
|
1287
|
+
<li>frick: {{frick}}</li>
|
1288
|
+
</ul>
|
1289
|
+
<button ng-click="batch()">Batch</button>
|
1290
|
+
<button ng-click="rollback()">Rollback</button><br/>
|
1291
|
+
{{message}}
|
1292
|
+
</file>
|
1293
|
+
</example>
|
1294
|
+
*/
|
1295
|
+
this.batch = function batch(fn, scope, description) {
|
1296
|
+
var _clear = bind(this, this._clear),
|
1297
|
+
_initStores = this._initStores,
|
1298
|
+
listener,
|
1299
|
+
watchObj,
|
1300
|
+
child;
|
1301
|
+
scope = scope || $rootScope;
|
1302
|
+
if (!isFunction(fn)) {
|
1303
|
+
throw new Error('transaction requires a function');
|
1304
|
+
}
|
1305
|
+
|
1306
|
+
child = scope.$new();
|
1307
|
+
child.$on('$destroy', function () {
|
1308
|
+
_clear(child);
|
1309
|
+
});
|
1310
|
+
|
1311
|
+
listener = scope.$on('History.archived', function (evt, data) {
|
1312
|
+
var deepChild,
|
1313
|
+
exp = data.expression,
|
1314
|
+
id;
|
1315
|
+
if (data.locals.$id !== child.$id) {
|
1316
|
+
deepChild = child.$new();
|
1317
|
+
deepChild.$on('$destroy', function () {
|
1318
|
+
_clear(deepChild);
|
1319
|
+
});
|
1320
|
+
deepChild.$$locals = data.locals;
|
1321
|
+
id = deepChild.$id;
|
1322
|
+
_initStores(id);
|
1323
|
+
history[id][exp] =
|
1324
|
+
copy(history[data.locals.$id][exp]);
|
1325
|
+
pointers[id][exp] = pointers[data.locals.$id][exp] - 1;
|
1326
|
+
}
|
1327
|
+
});
|
1328
|
+
|
1329
|
+
watchObjs[child.$id] = watchObj = new Watch(null, child);
|
1330
|
+
watchObj._broadcast('History.batchBegan', {
|
1331
|
+
transaction: child,
|
1332
|
+
description: description
|
1333
|
+
});
|
1334
|
+
|
1335
|
+
// we need to put this into a timeout and apply manually
|
1336
|
+
// since it's not clear when the watchers will get fired,
|
1337
|
+
// and we must ensure that any existing watchers on the archived
|
1338
|
+
// event can be skipped before the batchEnd occurs.
|
1339
|
+
batching = true;
|
1340
|
+
$timeout(function () {
|
1341
|
+
fn(child);
|
1342
|
+
scope.$apply();
|
1343
|
+
})
|
1344
|
+
.then(function () {
|
1345
|
+
listener();
|
1346
|
+
batching = false;
|
1347
|
+
watchObj._broadcast('History.batchEnded', {
|
1348
|
+
transaction: child,
|
1349
|
+
description: description
|
1350
|
+
});
|
1351
|
+
});
|
1352
|
+
|
1353
|
+
|
1354
|
+
return watchObj;
|
1355
|
+
};
|
1356
|
+
|
1357
|
+
/**
|
1358
|
+
* @ngdoc function
|
1359
|
+
* @name decipher.history.service:History#rollback
|
1360
|
+
* @method
|
1361
|
+
* @methodOf decipher.history.service:History
|
1362
|
+
* @description
|
1363
|
+
* Rolls a transaction back that was executed via {@link decipher.history.service:History#batch batch()}.
|
1364
|
+
*
|
1365
|
+
* For an example, see {@link decipher.history.service:History#batch batch()}.
|
1366
|
+
* @param {Scope} t Scope object in which the transaction was executed.
|
1367
|
+
*/
|
1368
|
+
this.rollback = function rollback(t) {
|
1369
|
+
|
1370
|
+
var _do = bind(this, this._do),
|
1371
|
+
parent = t.$parent,
|
1372
|
+
packets = {},
|
1373
|
+
nextSibling,
|
1374
|
+
watchObj,
|
1375
|
+
nextSiblingLocals;
|
1376
|
+
if (!t || !isObject(t)) {
|
1377
|
+
throw new Error('must pass a scope to rollback');
|
1378
|
+
}
|
1379
|
+
|
1380
|
+
function _rollback(scope, comparisonScope) {
|
1381
|
+
var id = scope.$id,
|
1382
|
+
comparisonScopeId = comparisonScope.$id,
|
1383
|
+
stack = history[id],
|
1384
|
+
pointer,
|
1385
|
+
descs,
|
1386
|
+
exp,
|
1387
|
+
values,
|
1388
|
+
exps,
|
1389
|
+
rolledback,
|
1390
|
+
i;
|
1391
|
+
if (stack) {
|
1392
|
+
exps = Object.keys(stack);
|
1393
|
+
i = exps.length;
|
1394
|
+
} else {
|
1395
|
+
// might not actually have history, it's ok
|
1396
|
+
return;
|
1397
|
+
}
|
1398
|
+
while (i--) {
|
1399
|
+
exp = exps[i];
|
1400
|
+
values = [];
|
1401
|
+
descs = [];
|
1402
|
+
pointer = pointers[comparisonScopeId][exp];
|
1403
|
+
rolledback = false;
|
1404
|
+
while (pointer > pointers[id][exp]) {
|
1405
|
+
pointer--;
|
1406
|
+
values.push(_do(comparisonScope,
|
1407
|
+
exp, history[comparisonScopeId][exp], pointer));
|
1408
|
+
pointers[comparisonScopeId][exp] = pointer;
|
1409
|
+
descs.push(descriptions[comparisonScopeId][exp]);
|
1410
|
+
// throw this off the history stack so
|
1411
|
+
// we don't end up with it in the stack while we
|
1412
|
+
// do normal undo() calls later against the same
|
1413
|
+
// expression and scope
|
1414
|
+
history[comparisonScopeId][exp].pop();
|
1415
|
+
rolledback = true;
|
1416
|
+
}
|
1417
|
+
if (rolledback) {
|
1418
|
+
packets[exp] = {
|
1419
|
+
values: values,
|
1420
|
+
scope: scope,
|
1421
|
+
comparisonScope: comparisonScope,
|
1422
|
+
descriptions: descs
|
1423
|
+
};
|
1424
|
+
}
|
1425
|
+
}
|
1426
|
+
}
|
1427
|
+
|
1428
|
+
watchObj = watchObjs[t.$id];
|
1429
|
+
|
1430
|
+
if (isDefined(parent) &&
|
1431
|
+
isDefined(history[parent.$id])) {
|
1432
|
+
_rollback(t, parent);
|
1433
|
+
}
|
1434
|
+
nextSibling = t.$$childHead;
|
1435
|
+
while (nextSibling) {
|
1436
|
+
nextSiblingLocals = nextSibling.$$locals;
|
1437
|
+
if (nextSiblingLocals) {
|
1438
|
+
_rollback(nextSibling, nextSiblingLocals);
|
1439
|
+
}
|
1440
|
+
nextSibling = nextSibling.$$nextSibling;
|
1441
|
+
}
|
1442
|
+
watchObj._fireRollbackHandlers();
|
1443
|
+
watchObj._broadcast('History.rolledback', packets);
|
1444
|
+
|
1445
|
+
};
|
1446
|
+
|
1447
|
+
/**
|
1448
|
+
* @ngdoc property
|
1449
|
+
* @name decipher.history.service:History#history
|
1450
|
+
* @propertyOf decipher.history.service:History
|
1451
|
+
* @description
|
1452
|
+
* The complete history stack, keyed by Scope `$id` and then expression.
|
1453
|
+
* @type {{}}
|
1454
|
+
*/
|
1455
|
+
this.history = history;
|
1456
|
+
|
1457
|
+
/**
|
1458
|
+
* @ngdoc property
|
1459
|
+
* @name decipher.history.service:History#descriptions
|
1460
|
+
* @propertyOf decipher.history.service:History
|
1461
|
+
* @description
|
1462
|
+
* The complete map of change descriptions, keyed by Scope `$id` and then expression.
|
1463
|
+
* @type {{}}
|
1464
|
+
*/
|
1465
|
+
this.descriptions = descriptions;
|
1466
|
+
|
1467
|
+
/**
|
1468
|
+
* @ngdoc property
|
1469
|
+
* @name decipher.history.service:History#pointers
|
1470
|
+
* @propertyOf decipher.history.service:History
|
1471
|
+
* @description
|
1472
|
+
* The complete pointer map, keyed by Scope `$id` and then expression.
|
1473
|
+
* @type {{}}
|
1474
|
+
*/
|
1475
|
+
this.pointers = pointers;
|
1476
|
+
|
1477
|
+
/**
|
1478
|
+
* @ngdoc property
|
1479
|
+
* @name decipher.history.service:History#watches
|
1480
|
+
* @propertyOf decipher.history.service:History
|
1481
|
+
* @description
|
1482
|
+
* The complete index of all AngularJS `$watch`es, keyed by Scope `$id` and then expression.
|
1483
|
+
* @type {{}}
|
1484
|
+
*/
|
1485
|
+
this.watches = watches;
|
1486
|
+
|
1487
|
+
/**
|
1488
|
+
* @ngdoc property
|
1489
|
+
* @name decipher.history.service:History#lazyWatches
|
1490
|
+
* @propertyOf decipher.history.service:History
|
1491
|
+
* @description
|
1492
|
+
* The complete index of all AngularJS `$watch`es designated to be "lazy", keyed by Scope `$id` and then expression.
|
1493
|
+
* @type {{}}
|
1494
|
+
*/
|
1495
|
+
this.lazyWatches = lazyWatches;
|
1496
|
+
|
1497
|
+
/**
|
1498
|
+
* @ngdoc property
|
1499
|
+
* @name decipher.history.service:History#watchObjs
|
1500
|
+
* @propertyOf decipher.history.service:History
|
1501
|
+
* @description
|
1502
|
+
* The complete index of all {@link decipher.history.object:Watch Watch} objects registered, keyed by Scope `$id` and then (optionally) expression.
|
1503
|
+
* @type {{}}
|
1504
|
+
*/
|
1505
|
+
this.watchObjs = watchObjs;
|
1506
|
+
|
1507
|
+
/**
|
1508
|
+
* @ngdoc property
|
1509
|
+
* @name decipher.history.service:History#Watch
|
1510
|
+
* @propertyOf decipher.history.service:History
|
1511
|
+
* @description
|
1512
|
+
* Here's the Watch prototype for you to play with.
|
1513
|
+
* @type {Watch}
|
1514
|
+
*/
|
1515
|
+
this.Watch = Watch;
|
1516
|
+
});
|
1517
|
+
})();
|