Skip to content

Commit d106208

Browse files
committed
Bumps minor release: v1.4.0
1 parent 9917a9a commit d106208

File tree

3 files changed

+194
-35
lines changed

3 files changed

+194
-35
lines changed

dist/immstruct.js

Lines changed: 191 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,82 @@
11
/**
2-
* immstruct v1.3.1
2+
* immstruct v1.4.0
33
* Authors: Mikael Brevik, @torgeir
44
***************************************/
55
!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.immstruct=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
66
var Structure = _dereq_('./src/structure');
77

8-
module.exports = function (key, data) {
9-
return getInstance({
8+
function Immstruct () {
9+
if (!(this instanceof Immstruct)) {
10+
return new Immstruct();
11+
}
12+
13+
this.instances = {};
14+
}
15+
16+
Immstruct.prototype.get = function (key, data) {
17+
return getInstance(this, {
1018
key: key,
1119
data: data
1220
});
1321
};
1422

15-
module.exports.instances = {};
23+
Immstruct.prototype.clear = function () {
24+
this.instances = {};
25+
};
26+
27+
Immstruct.prototype.remove = function (key) {
28+
return delete this.instances[key];
29+
};
30+
31+
Immstruct.prototype.withHistory = function (key, data) {
32+
return getInstance(this, {
33+
key: key,
34+
data: data,
35+
history: true
36+
});
37+
};
38+
39+
var inst = new Immstruct();
40+
41+
module.exports = function (key, data) {
42+
return getInstance(inst, {
43+
key: key,
44+
data: data
45+
});
46+
};
1647

1748
module.exports.withHistory = function (key, data) {
18-
return getInstance({
49+
return getInstance(inst, {
1950
key: key,
2051
data: data,
2152
history: true
2253
});
2354
};
2455

2556
module.exports.Structure = Structure;
26-
27-
function getInstance (options) {
57+
module.exports.Immstruct = Immstruct;
58+
module.exports.clear = inst.clear.bind(inst);
59+
module.exports.remove = inst.remove.bind(inst);
60+
Object.defineProperty(module.exports, 'instances', {
61+
get: function() { return inst.instances; },
62+
enumerable: true,
63+
configurable: true
64+
});
65+
66+
function getInstance (obj, options) {
2867
if (typeof options.key === 'object') {
2968
options.data = options.key;
3069
options.key = void 0;
3170
}
3271

33-
if (options.key && module.exports.instances[options.key]) {
34-
return module.exports.instances[options.key];
72+
if (options.key && obj.instances[options.key]) {
73+
return obj.instances[options.key];
3574
}
3675

3776
var newInstance = new Structure(options);
38-
module.exports.instances[newInstance.key] = newInstance;
77+
obj.instances[newInstance.key] = newInstance;
3978
return newInstance;
4079
}
41-
42-
module.exports.clear = function () {
43-
module.exports.instances = {};
44-
};
45-
46-
module.exports.remove = function (key) {
47-
return delete module.exports.instances[key];
48-
};
49-
5080
},{"./src/structure":5}],2:[function(_dereq_,module,exports){
5181
'use strict';
5282

@@ -596,13 +626,16 @@ var utils = _dereq_('./utils');
596626
* ## Public API.
597627
* Constructor({ history: bool, key: string, data: structure|object })
598628
* .cursor(path)
629+
* .reference(path)
599630
* .forceHasSwapped(newData, oldData, keyPath)
600631
* .undo(steps)
601632
* .redo(steps)
602633
* .undoUntil(structure)
603634
*
604635
************************************/
605636
function Structure (options) {
637+
var self = this;
638+
606639
options = options || {};
607640
if (!(this instanceof Structure)) {
608641
return new Structure(options);
@@ -620,6 +653,16 @@ function Structure (options) {
620653
this._currentRevision = 0;
621654
}
622655

656+
this._pathListeners = [];
657+
this.on('swap', function (newData, oldData, keyPath) {
658+
listListenerMatching(self._pathListeners, pathString(keyPath)).forEach(function (fns) {
659+
fns.forEach(function (fn) {
660+
if (typeof fn !== 'function') return;
661+
fn(newData, oldData, keyPath);
662+
});
663+
});
664+
});
665+
623666
EventEmitter.call(this, arguments);
624667
}
625668
inherits(Structure, EventEmitter);
@@ -634,7 +677,6 @@ Structure.prototype.cursor = function (path) {
634677
}
635678

636679
var changeListener = function (newRoot, oldRoot, path) {
637-
638680
if(self.current === oldRoot) {
639681
return self.current = newRoot;
640682
}
@@ -655,6 +697,67 @@ Structure.prototype.cursor = function (path) {
655697
return Cursor.from(self.current, path, changeListener);
656698
};
657699

700+
Structure.prototype.reference = function (path) {
701+
if (isCursor(path) && path._keyPath) {
702+
path = path._keyPath;
703+
}
704+
var self = this, pathId = pathString(path);
705+
var listenerNs = self._pathListeners[pathId];
706+
var cursor = this.cursor(path);
707+
708+
var changeListener = function (newRoot, oldRoot, changedPath) { cursor = self.cursor(path); };
709+
var referenceListeners = [changeListener];
710+
this._pathListeners[pathId] = !listenerNs ? referenceListeners : listenerNs.concat(changeListener);
711+
712+
return {
713+
observe: function (eventName, newFn) {
714+
if (typeof eventName === 'function') {
715+
newFn = eventName;
716+
eventName = void 0;
717+
}
718+
if (this._dead || typeof newFn !== 'function') return;
719+
if (eventName && eventName !== 'swap') {
720+
newFn = onlyOnEvent(eventName, newFn);
721+
}
722+
723+
self._pathListeners[pathId] = self._pathListeners[pathId].concat(newFn);
724+
referenceListeners = referenceListeners.concat(newFn);
725+
726+
return function unobserve () {
727+
var fnIndex = self._pathListeners[pathId].indexOf(newFn);
728+
var localListenerIndex = referenceListeners.indexOf(newFn);
729+
730+
if (referenceListeners[localListenerIndex] === newFn) {
731+
referenceListeners.splice(localListenerIndex, 1);
732+
}
733+
734+
if (!self._pathListeners[pathId]) return;
735+
if (self._pathListeners[pathId][fnIndex] !== newFn) return;
736+
self._pathListeners[pathId].splice(fnIndex, 1);
737+
};
738+
},
739+
cursor: function (subPath) {
740+
if (subPath) return cursor.cursor(subPath);
741+
return cursor;
742+
},
743+
unobserveAll: function () {
744+
removeAllListenersBut(self, pathId, referenceListeners, changeListener);
745+
referenceListeners = [changeListener];
746+
},
747+
destroy: function () {
748+
removeAllListenersBut(self, pathId, referenceListeners);
749+
referenceListeners = void 0;
750+
cursor = void 0;
751+
752+
this._dead = true;
753+
this.observe = void 0;
754+
this.unobserveAll = void 0;
755+
this.cursor = void 0;
756+
this.destroy = void 0;
757+
}
758+
};
759+
};
760+
658761
Structure.prototype.forceHasSwapped = function (newData, oldData, keyPath) {
659762
this.emit('swap', newData || this.current, oldData, keyPath);
660763
possiblyEmitAnimationFrameEvent(this, newData || this.current, oldData, keyPath)
@@ -742,19 +845,10 @@ function handlePersisting (emitter, fn) {
742845
return function (newData, oldData, path) {
743846
var newStructure = fn.apply(fn, arguments);
744847
if(newData === oldData) return newStructure;
848+
var info = analyze(newData, oldData, path);
745849

746-
var oldObject = oldData && oldData.getIn(path);
747-
var newObject = newStructure && newStructure.getIn(path);
748-
749-
var inOld = oldData && hasIn(oldData, path);
750-
var inNew = newStructure && hasIn(newStructure, path);
751-
752-
if (inOld && !inNew) {
753-
emitter.emit('delete', path, oldObject);
754-
} else if (inOld && inNew) {
755-
emitter.emit('change', path, newObject, oldObject);
756-
} else if (!inOld && inNew) {
757-
emitter.emit('add', path, newObject);
850+
if (info.eventName) {
851+
emitter.emit.apply(emitter, [info.eventName].concat(info.arguments));
758852
}
759853
return newStructure;
760854
};
@@ -764,13 +858,78 @@ function handlePersisting (emitter, fn) {
764858
* Private helpers.
765859
***********************************/
766860

861+
function removeAllListenersBut(self, pathId, listeners, except) {
862+
if (!listeners) return;
863+
listeners.forEach(function (fn) {
864+
if (except && fn === except) return;
865+
var index = self._pathListeners[pathId].indexOf(fn);
866+
self._pathListeners[pathId].splice(index, 1);
867+
});
868+
}
869+
870+
function analyze (newData, oldData, path) {
871+
var oldObject = oldData && oldData.getIn(path);
872+
var newObject = newData && newData.getIn(path);
873+
874+
var inOld = oldData && hasIn(oldData, path);
875+
var inNew = newData && hasIn(newData, path);
876+
877+
var arguments, eventName;
878+
879+
if (inOld && !inNew) {
880+
eventName = 'delete';
881+
arguments = [path, oldObject];
882+
} else if (inOld && inNew) {
883+
eventName = 'change';
884+
arguments = [path, newObject, oldObject];
885+
} else if (!inOld && inNew) {
886+
eventName = 'add';
887+
arguments = [path, newObject];
888+
}
889+
890+
return {
891+
eventName: eventName,
892+
arguments: arguments
893+
};
894+
}
895+
896+
767897
// Check if path exists.
768898
var NOT_SET = {};
769899
function hasIn(cursor, path) {
770900
if(cursor.hasIn) return cursor.hasIn(path);
771901
return cursor.getIn(path, NOT_SET) !== NOT_SET;
772902
}
773903

904+
function pathString(path) {
905+
var topLevel = 'global';
906+
if (!path || !path.length) return topLevel;
907+
return [topLevel].concat(path).join('|');
908+
}
909+
910+
function listListenerMatching (listeners, basePath) {
911+
var newListeners = [];
912+
for (var key in listeners) {
913+
if (!listeners.hasOwnProperty(key)) return;
914+
if (basePath.indexOf(key) !== 0) continue;
915+
newListeners.push(listeners[key]);
916+
}
917+
918+
return newListeners;
919+
}
920+
921+
function onlyOnEvent(eventName, fn) {
922+
return function (newData, oldData, keyPath) {
923+
var info = analyze(newData, oldData, keyPath);
924+
if (info.eventName !== eventName) return;
925+
return fn(newData, oldData, keyPath);
926+
};
927+
}
928+
929+
function isCursor (potential) {
930+
return potential && typeof potential.deref === 'function';
931+
}
932+
774933
// Check if passed structure is existing immutable structure.
775934
// From https://github.com/facebook/immutable-js/wiki/Upgrading-to-Immutable-v3#additional-changes
776935
function isImmutableStructure (data) {

0 commit comments

Comments
 (0)