267 lines
21 KiB
JavaScript
267 lines
21 KiB
JavaScript
//////////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// This is a generated file. You can view the original //
|
|
// source in your browser if your browser supports source maps. //
|
|
// //
|
|
// If you are using Chrome, open the Developer Tools and click the gear //
|
|
// icon in its lower right corner. In the General Settings panel, turn //
|
|
// on 'Enable source maps'. //
|
|
// //
|
|
// If you are using Firefox 23, go to `about:config` and set the //
|
|
// `devtools.debugger.source-maps-enabled` preference to true. //
|
|
// (The preference should be on by default in Firefox 24; versions //
|
|
// older than 23 do not support source maps.) //
|
|
// //
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
(function () {
|
|
|
|
/* Imports */
|
|
var Meteor = Package.meteor.Meteor;
|
|
var _ = Package.underscore._;
|
|
var Tracker = Package.tracker.Tracker;
|
|
var Deps = Package.tracker.Deps;
|
|
var EJSON = Package.ejson.EJSON;
|
|
|
|
/* Package-scope variables */
|
|
var ReactiveDict;
|
|
|
|
(function () {
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// packages/reactive-dict/reactive-dict.js //
|
|
// //
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// XXX come up with a serialization method which canonicalizes object key // 1
|
|
// order, which would allow us to use objects as values for equals. // 2
|
|
var stringify = function (value) { // 3
|
|
if (value === undefined) // 4
|
|
return 'undefined'; // 5
|
|
return EJSON.stringify(value); // 6
|
|
}; // 7
|
|
var parse = function (serialized) { // 8
|
|
if (serialized === undefined || serialized === 'undefined') // 9
|
|
return undefined; // 10
|
|
return EJSON.parse(serialized); // 11
|
|
}; // 12
|
|
// 13
|
|
// XXX COMPAT WITH 0.9.1 : accept migrationData instead of dictName // 14
|
|
ReactiveDict = function (dictName) { // 15
|
|
// this.keys: key -> value // 16
|
|
if (dictName) { // 17
|
|
if (typeof dictName === 'string') { // 18
|
|
// the normal case, argument is a string name. // 19
|
|
// _registerDictForMigrate will throw an error on duplicate name. // 20
|
|
ReactiveDict._registerDictForMigrate(dictName, this); // 21
|
|
this.keys = ReactiveDict._loadMigratedDict(dictName) || {}; // 22
|
|
} else if (typeof dictName === 'object') { // 23
|
|
// back-compat case: dictName is actually migrationData // 24
|
|
this.keys = dictName; // 25
|
|
} else { // 26
|
|
throw new Error("Invalid ReactiveDict argument: " + dictName); // 27
|
|
} // 28
|
|
} else { // 29
|
|
// no name given; no migration will be performed // 30
|
|
this.keys = {}; // 31
|
|
} // 32
|
|
// 33
|
|
this.keyDeps = {}; // key -> Dependency // 34
|
|
this.keyValueDeps = {}; // key -> Dependency // 35
|
|
}; // 36
|
|
// 37
|
|
_.extend(ReactiveDict.prototype, { // 38
|
|
// set() began as a key/value method, but we are now overloading it // 39
|
|
// to take an object of key/value pairs, similar to backbone // 40
|
|
// http://backbonejs.org/#Model-set // 41
|
|
// 42
|
|
set: function (keyOrObject, value) { // 43
|
|
var self = this; // 44
|
|
// 45
|
|
if ((typeof keyOrObject === 'object') && (value === undefined)) { // 46
|
|
self._setObject(keyOrObject); // 47
|
|
return; // 48
|
|
} // 49
|
|
// the input isn't an object, so it must be a key // 50
|
|
// and we resume with the rest of the function // 51
|
|
var key = keyOrObject; // 52
|
|
// 53
|
|
value = stringify(value); // 54
|
|
// 55
|
|
var oldSerializedValue = 'undefined'; // 56
|
|
if (_.has(self.keys, key)) oldSerializedValue = self.keys[key]; // 57
|
|
if (value === oldSerializedValue) // 58
|
|
return; // 59
|
|
self.keys[key] = value; // 60
|
|
// 61
|
|
var changed = function (v) { // 62
|
|
v && v.changed(); // 63
|
|
}; // 64
|
|
// 65
|
|
changed(self.keyDeps[key]); // 66
|
|
if (self.keyValueDeps[key]) { // 67
|
|
changed(self.keyValueDeps[key][oldSerializedValue]); // 68
|
|
changed(self.keyValueDeps[key][value]); // 69
|
|
} // 70
|
|
}, // 71
|
|
// 72
|
|
setDefault: function (key, value) { // 73
|
|
var self = this; // 74
|
|
// for now, explicitly check for undefined, since there is no // 75
|
|
// ReactiveDict.clear(). Later we might have a ReactiveDict.clear(), in which case // 76
|
|
// we should check if it has the key. // 77
|
|
if (self.keys[key] === undefined) { // 78
|
|
self.set(key, value); // 79
|
|
} // 80
|
|
}, // 81
|
|
// 82
|
|
get: function (key) { // 83
|
|
var self = this; // 84
|
|
self._ensureKey(key); // 85
|
|
self.keyDeps[key].depend(); // 86
|
|
return parse(self.keys[key]); // 87
|
|
}, // 88
|
|
// 89
|
|
equals: function (key, value) { // 90
|
|
var self = this; // 91
|
|
// 92
|
|
// Mongo.ObjectID is in the 'mongo' package // 93
|
|
var ObjectID = null; // 94
|
|
if (typeof Mongo !== 'undefined') { // 95
|
|
ObjectID = Mongo.ObjectID; // 96
|
|
} // 97
|
|
// 98
|
|
// We don't allow objects (or arrays that might include objects) for // 99
|
|
// .equals, because JSON.stringify doesn't canonicalize object key // 100
|
|
// order. (We can make equals have the right return value by parsing the // 101
|
|
// current value and using EJSON.equals, but we won't have a canonical // 102
|
|
// element of keyValueDeps[key] to store the dependency.) You can still use // 103
|
|
// "EJSON.equals(reactiveDict.get(key), value)". // 104
|
|
// // 105
|
|
// XXX we could allow arrays as long as we recursively check that there // 106
|
|
// are no objects // 107
|
|
if (typeof value !== 'string' && // 108
|
|
typeof value !== 'number' && // 109
|
|
typeof value !== 'boolean' && // 110
|
|
typeof value !== 'undefined' && // 111
|
|
!(value instanceof Date) && // 112
|
|
!(ObjectID && value instanceof ObjectID) && // 113
|
|
value !== null) // 114
|
|
throw new Error("ReactiveDict.equals: value must be scalar"); // 115
|
|
var serializedValue = stringify(value); // 116
|
|
// 117
|
|
if (Tracker.active) { // 118
|
|
self._ensureKey(key); // 119
|
|
// 120
|
|
if (! _.has(self.keyValueDeps[key], serializedValue)) // 121
|
|
self.keyValueDeps[key][serializedValue] = new Tracker.Dependency; // 122
|
|
// 123
|
|
var isNew = self.keyValueDeps[key][serializedValue].depend(); // 124
|
|
if (isNew) { // 125
|
|
Tracker.onInvalidate(function () { // 126
|
|
// clean up [key][serializedValue] if it's now empty, so we don't // 127
|
|
// use O(n) memory for n = values seen ever // 128
|
|
if (! self.keyValueDeps[key][serializedValue].hasDependents()) // 129
|
|
delete self.keyValueDeps[key][serializedValue]; // 130
|
|
}); // 131
|
|
} // 132
|
|
} // 133
|
|
// 134
|
|
var oldValue = undefined; // 135
|
|
if (_.has(self.keys, key)) oldValue = parse(self.keys[key]); // 136
|
|
return EJSON.equals(oldValue, value); // 137
|
|
}, // 138
|
|
// 139
|
|
_setObject: function (object) { // 140
|
|
var self = this; // 141
|
|
// 142
|
|
_.each(object, function (value, key){ // 143
|
|
self.set(key, value); // 144
|
|
}); // 145
|
|
}, // 146
|
|
// 147
|
|
_ensureKey: function (key) { // 148
|
|
var self = this; // 149
|
|
if (!(key in self.keyDeps)) { // 150
|
|
self.keyDeps[key] = new Tracker.Dependency; // 151
|
|
self.keyValueDeps[key] = {}; // 152
|
|
} // 153
|
|
}, // 154
|
|
// 155
|
|
// Get a JSON value that can be passed to the constructor to // 156
|
|
// create a new ReactiveDict with the same contents as this one // 157
|
|
_getMigrationData: function () { // 158
|
|
// XXX sanitize and make sure it's JSONible? // 159
|
|
return this.keys; // 160
|
|
} // 161
|
|
}); // 162
|
|
// 163
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
}).call(this);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(function () {
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// packages/reactive-dict/migration.js //
|
|
// //
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
ReactiveDict._migratedDictData = {}; // name -> data // 1
|
|
ReactiveDict._dictsToMigrate = {}; // name -> ReactiveDict // 2
|
|
// 3
|
|
ReactiveDict._loadMigratedDict = function (dictName) { // 4
|
|
if (_.has(ReactiveDict._migratedDictData, dictName)) // 5
|
|
return ReactiveDict._migratedDictData[dictName]; // 6
|
|
// 7
|
|
return null; // 8
|
|
}; // 9
|
|
// 10
|
|
ReactiveDict._registerDictForMigrate = function (dictName, dict) { // 11
|
|
if (_.has(ReactiveDict._dictsToMigrate, dictName)) // 12
|
|
throw new Error("Duplicate ReactiveDict name: " + dictName); // 13
|
|
// 14
|
|
ReactiveDict._dictsToMigrate[dictName] = dict; // 15
|
|
}; // 16
|
|
// 17
|
|
if (Meteor.isClient && Package.reload) { // 18
|
|
// Put old migrated data into ReactiveDict._migratedDictData, // 19
|
|
// where it can be accessed by ReactiveDict._loadMigratedDict. // 20
|
|
var migrationData = Package.reload.Reload._migrationData('reactive-dict'); // 21
|
|
if (migrationData && migrationData.dicts) // 22
|
|
ReactiveDict._migratedDictData = migrationData.dicts; // 23
|
|
// 24
|
|
// On migration, assemble the data from all the dicts that have been // 25
|
|
// registered. // 26
|
|
Package.reload.Reload._onMigrate('reactive-dict', function () { // 27
|
|
var dictsToMigrate = ReactiveDict._dictsToMigrate; // 28
|
|
var dataToMigrate = {}; // 29
|
|
// 30
|
|
for (var dictName in dictsToMigrate) // 31
|
|
dataToMigrate[dictName] = dictsToMigrate[dictName]._getMigrationData(); // 32
|
|
// 33
|
|
return [true, {dicts: dataToMigrate}]; // 34
|
|
}); // 35
|
|
} // 36
|
|
// 37
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
}).call(this);
|
|
|
|
|
|
/* Exports */
|
|
if (typeof Package === 'undefined') Package = {};
|
|
Package['reactive-dict'] = {
|
|
ReactiveDict: ReactiveDict
|
|
};
|
|
|
|
})();
|