UNPKG

relu-core

Version:
296 lines (290 loc) 8.9 kB
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); };