relu-core
Version:
296 lines (290 loc) • 8.9 kB
JavaScript
var base = require("./base");
var arrayOperation = require("./arrayOperation");
var computed = require("./computed");
var delegated = require("./delegated");
module.exports = function rpOrdered(array, comparator) {
if(!comparator) comparator = function(a,b) {
a = a();
b = b();
if(a < b) return -1; if(a > b) return 1; return 0;
}
var internal;
var arrayInstance;
var connections = [];
var updatedConnections;
var newItems = []
var indieces = [];
var reverseIndieces = [];
var refHolder = base().ref();
refHolder.constructor = function rpOrderedRefHolder() {};
var p = arrayOperation(array, function(newArray) {
internal = this;
arrayInstance = newArray;
updatedConnections = null;
newItems.length = 0;
base.atAtomicEnd(checkUpdate);
}, function(idx, item) {
if(updatedConnections) {
newItems.push(idx);
for(var i = 0; i < reverseIndieces.length; i++) {
if(reverseIndieces[i] >= idx)
reverseIndieces[i]++;
}
}
base.atAtomicEnd(checkUpdate);
}, function(idx, item) {
if(updatedConnections) {
for(var i = 0; i < reverseIndieces.length; i++) {
if(reverseIndieces[i] >= idx)
reverseIndieces[i]--;
}
var orderedIdx = indieces[idx];
if(orderedIdx === undefined) throw new Error("removed a item not in array");
this.splice(orderedIdx, 1);
indieces.splice(idx, 1);
reverseIndieces.splice(orderedIdx, 1);
var a = reverseIndieces[orderedIdx - 1];
var b = reverseIndieces[orderedIdx];
var c;
var arrayLength = array._getUndependend();
arrayLength = Array.isArray(arrayLength) ? arrayLength.length : -1;
if(a !== undefined && b !== undefined && b < arrayLength && a < arrayLength) {
a = array(a);
b = array(b);
c = computed(function() {
if(!a || !b) return 0;
return comparator(a, b);
}).ref(refHolder);
c.__changeListener = function() {
onChanged(c);
};
c.onceChanged(c.__changeListener);
connections.splice(orderedIdx - 1, 2, c).forEach(function(c) {
c.removeChangedListener(c.__changeListener);
c.unref(refHolder);
});
} else {
connections.splice(orderedIdx === 0 ? 0 : orderedIdx - 1, 1).forEach(function(c) {
c.removeChangedListener(c.__changeListener);
c.unref(refHolder);
});
}
}
});
p.constructor = rpOrdered;
array.ref(p);
p.onceDisposed(function() {
disconnect();
refHolder.unref();
array.unref(p);
});
function disconnect() {
if(!connections) return;
connections.forEach(function(c) {
c.removeChangedListener(c.__changeListener);
c.unref(refHolder);
});
connections.length = 0;
}
function checkUpdate() {
if(!updatedConnections) {
// Updated the full array
// 1. Sort the array (indieces)
indieces = [];
indieces.length = arrayInstance.length;
reverseIndieces = [];
base.uncaptured(function() {
for(var i = 0; i < arrayInstance.length; i++) {
for(var j = reverseIndieces.length - 1; j >= 0; j--) {
var a = array(reverseIndieces[j]);
var b = array(i);
var result = (!a || !b) ? 0 : comparator(a, b);
if(result <= 0) break;
}
if(j < 0)
reverseIndieces.unshift(i);
else
reverseIndieces.splice(j+1, 0, i);
}
});
// 2. Make indieces
for(var i = 0; i < reverseIndieces.length; i++)
indieces[reverseIndieces[i]] = i;
// 3. Establish connections
if(connections) disconnect();
for(var i = 1; i < reverseIndieces.length; i++) {
(function(i) {
var a = array(reverseIndieces[i-1]);
var b = array(reverseIndieces[i]);
var c = computed(function() {
if(!a || !b) return 0;
return comparator(a, b);
}).ref(refHolder);
c.__changeListener = function() {
onChanged(c);
};
c.onceChanged(c.__changeListener);
connections.push(c);
}(i));
}
// 4. Set new array result
var result = [];
for(var i = 0; i < reverseIndieces.length; i++) {
result.push(array(reverseIndieces[i]));
}
internal.set(result);
// 5. reeanble part updating
updatedConnections = [];
} else {
// Updated only a part
var currentSortedPart = {
reverseIndieces: [],
connections: []
};
var sortedParts = [currentSortedPart];
// 1. Extrace remaining sorted parts
for(var i = 0; i < connections.length; i++) {
var brockenConnection = updatedConnections.indexOf(connections[i]) >= 0;
currentSortedPart.reverseIndieces.push(reverseIndieces[i]);
if(brockenConnection) {
connections[i].removeChangedListener(connections[i].__changeListener);
connections[i].unref(refHolder);
currentSortedPart = {
reverseIndieces: [],
connections: []
};
sortedParts.push(currentSortedPart);
} else {
currentSortedPart.connections.push(connections[i]);
}
}
if(connections.length >= 1) {
currentSortedPart.reverseIndieces.push(reverseIndieces[connections.length]);
} else {
sortedParts.length = 0;
}
base.uncaptured(function() {
// 2. Summarize new items
newItems.sort(function(a, b) {
a = array(a);
b = array(b);
if(!a || !b) return 0;
return comparator(a, b);
});
var _newItems = newItems.slice();
// 3. Merge sort them
var resultReverseIndices = [];
connections = [];
var availibleConnection = null;
var availibleConnectionForPart;
for(;;) {
var next = -1, part;
if(sortedParts.length > 0) {
next = sortedParts[0].reverseIndieces[0];
part = 0;
for(var i = 1; i < sortedParts.length; i++) {
var idx = sortedParts[i].reverseIndieces[0];
var a = array(next);
var b = array(idx);
if(a && b && comparator(a, b) > 0) {
next = idx;
part = i;
}
}
if(newItems.length > 0) {
var idx = newItems[0];
var a = array(next);
var b = array(idx);
if(a && b && comparator(a, b) > 0) {
next = idx;
part = -1;
}
}
} else if(newItems.length > 0) {
next = newItems[0];
part = -1;
} else break;
resultReverseIndices.push(next);
if(part === -1) {
newItems.shift();
if(availibleConnection) {
availibleConnection.removeChangedListener(availibleConnection.__changeListener);
availibleConnection.unref(refHolder);
availibleConnection = null;
}
makeNewConnection();
} else {
if(availibleConnection) {
if(availibleConnectionForPart === part) {
connections.push(availibleConnection);
availibleConnection = null;
} else {
availibleConnection.removeChangedListener(availibleConnection.__changeListener);
availibleConnection.unref(refHolder);
makeNewConnection();
availibleConnection = null;
}
} else {
makeNewConnection();
}
availibleConnection = sortedParts[part].connections.shift();
sortedParts[part].reverseIndieces.shift();
if(!availibleConnection)
sortedParts.splice(part, 1);
else
availibleConnectionForPart = part;
}
};
function makeNewConnection() {
if(resultReverseIndices.length > 1) {
var a = array(resultReverseIndices[resultReverseIndices.length-2]);
var b = array(next);
var c = computed(function() {
if(!a || !b) return 0;
return comparator(a, b);
}).ref(refHolder);
c.__changeListener = function() {
onChanged(c);
};
c.onceChanged(c.__changeListener);
connections.push(c);
}
};
// 4. Execute the movement
var movedIndices = _newItems;
var movedItems = _newItems.map(function(x) { return array(x) });
var result = internal._getUndependend().slice();
for(var i = 0, j = 0; i < resultReverseIndices.length;) {
var n = resultReverseIndices[i];
var o = result[j];
var idx = movedIndices.indexOf(n);
if(idx >= 0) {
internal._splice(i, 0, movedItems[idx]);
movedIndices.splice(idx, 1);
movedItems.splice(idx, 1);
i++;
} else if(!o) {
throw new Error("Something weird");
} else if(n !== reverseIndieces[j]) {
movedIndices.push(reverseIndieces[j]);
movedItems.push(o);
internal._splice(i, 1);
j++;
} else {
i++;
j++;
}
}
reverseIndieces = resultReverseIndices;
indieces = [];
for(var i = 0; i < reverseIndieces.length; i++)
indieces[reverseIndieces[i]] = i;
});
}
}
function onChanged(c) {
if(updatedConnections) updatedConnections.push(c);
base.atAtomicEnd(checkUpdate);
}
return delegated(p);
};