mirror of
https://github.com/Polymer/polymer.git
synced 2026-08-19 01:14:44 -05:00
Replace sources with modulizer output.
This commit is contained in:
+298
-306
@@ -1,4 +1,4 @@
|
||||
<!--
|
||||
/**
|
||||
@license
|
||||
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
|
||||
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
|
||||
@@ -6,311 +6,303 @@ The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
|
||||
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
|
||||
Code distributed by Google as part of the polymer project is also
|
||||
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
||||
-->
|
||||
<link rel="import" href="boot.html">
|
||||
<script>
|
||||
(function() {
|
||||
*/
|
||||
import './boot.js';
|
||||
|
||||
'use strict';
|
||||
|
||||
function newSplice(index, removed, addedCount) {
|
||||
return {
|
||||
index: index,
|
||||
removed: removed,
|
||||
addedCount: addedCount
|
||||
};
|
||||
}
|
||||
|
||||
const EDIT_LEAVE = 0;
|
||||
const EDIT_UPDATE = 1;
|
||||
const EDIT_ADD = 2;
|
||||
const EDIT_DELETE = 3;
|
||||
|
||||
// Note: This function is *based* on the computation of the Levenshtein
|
||||
// "edit" distance. The one change is that "updates" are treated as two
|
||||
// edits - not one. With Array splices, an update is really a delete
|
||||
// followed by an add. By retaining this, we optimize for "keeping" the
|
||||
// maximum array items in the original array. For example:
|
||||
//
|
||||
// 'xxxx123' -> '123yyyy'
|
||||
//
|
||||
// With 1-edit updates, the shortest path would be just to update all seven
|
||||
// characters. With 2-edit updates, we delete 4, leave 3, and add 4. This
|
||||
// leaves the substring '123' intact.
|
||||
function calcEditDistances(current, currentStart, currentEnd,
|
||||
old, oldStart, oldEnd) {
|
||||
// "Deletion" columns
|
||||
let rowCount = oldEnd - oldStart + 1;
|
||||
let columnCount = currentEnd - currentStart + 1;
|
||||
let distances = new Array(rowCount);
|
||||
|
||||
// "Addition" rows. Initialize null column.
|
||||
for (let i = 0; i < rowCount; i++) {
|
||||
distances[i] = new Array(columnCount);
|
||||
distances[i][0] = i;
|
||||
}
|
||||
|
||||
// Initialize null row
|
||||
for (let j = 0; j < columnCount; j++)
|
||||
distances[0][j] = j;
|
||||
|
||||
for (let i = 1; i < rowCount; i++) {
|
||||
for (let j = 1; j < columnCount; j++) {
|
||||
if (equals(current[currentStart + j - 1], old[oldStart + i - 1]))
|
||||
distances[i][j] = distances[i - 1][j - 1];
|
||||
else {
|
||||
let north = distances[i - 1][j] + 1;
|
||||
let west = distances[i][j - 1] + 1;
|
||||
distances[i][j] = north < west ? north : west;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return distances;
|
||||
}
|
||||
|
||||
// This starts at the final weight, and walks "backward" by finding
|
||||
// the minimum previous weight recursively until the origin of the weight
|
||||
// matrix.
|
||||
function spliceOperationsFromEditDistances(distances) {
|
||||
let i = distances.length - 1;
|
||||
let j = distances[0].length - 1;
|
||||
let current = distances[i][j];
|
||||
let edits = [];
|
||||
while (i > 0 || j > 0) {
|
||||
if (i == 0) {
|
||||
edits.push(EDIT_ADD);
|
||||
j--;
|
||||
continue;
|
||||
}
|
||||
if (j == 0) {
|
||||
edits.push(EDIT_DELETE);
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
let northWest = distances[i - 1][j - 1];
|
||||
let west = distances[i - 1][j];
|
||||
let north = distances[i][j - 1];
|
||||
|
||||
let min;
|
||||
if (west < north)
|
||||
min = west < northWest ? west : northWest;
|
||||
else
|
||||
min = north < northWest ? north : northWest;
|
||||
|
||||
if (min == northWest) {
|
||||
if (northWest == current) {
|
||||
edits.push(EDIT_LEAVE);
|
||||
} else {
|
||||
edits.push(EDIT_UPDATE);
|
||||
current = northWest;
|
||||
}
|
||||
i--;
|
||||
j--;
|
||||
} else if (min == west) {
|
||||
edits.push(EDIT_DELETE);
|
||||
i--;
|
||||
current = west;
|
||||
} else {
|
||||
edits.push(EDIT_ADD);
|
||||
j--;
|
||||
current = north;
|
||||
}
|
||||
}
|
||||
|
||||
edits.reverse();
|
||||
return edits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Splice Projection functions:
|
||||
*
|
||||
* A splice map is a representation of how a previous array of items
|
||||
* was transformed into a new array of items. Conceptually it is a list of
|
||||
* tuples of
|
||||
*
|
||||
* <index, removed, addedCount>
|
||||
*
|
||||
* which are kept in ascending index order of. The tuple represents that at
|
||||
* the |index|, |removed| sequence of items were removed, and counting forward
|
||||
* from |index|, |addedCount| items were added.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Lacking individual splice mutation information, the minimal set of
|
||||
* splices can be synthesized given the previous state and final state of an
|
||||
* array. The basic approach is to calculate the edit distance matrix and
|
||||
* choose the shortest path through it.
|
||||
*
|
||||
* Complexity: O(l * p)
|
||||
* l: The length of the current array
|
||||
* p: The length of the old array
|
||||
*
|
||||
* @param {!Array} current The current "changed" array for which to
|
||||
* calculate splices.
|
||||
* @param {number} currentStart Starting index in the `current` array for
|
||||
* which splices are calculated.
|
||||
* @param {number} currentEnd Ending index in the `current` array for
|
||||
* which splices are calculated.
|
||||
* @param {!Array} old The original "unchanged" array to compare `current`
|
||||
* against to determine splices.
|
||||
* @param {number} oldStart Starting index in the `old` array for
|
||||
* which splices are calculated.
|
||||
* @param {number} oldEnd Ending index in the `old` array for
|
||||
* which splices are calculated.
|
||||
* @return {!Array} Returns an array of splice record objects. Each of these
|
||||
* contains: `index` the location where the splice occurred; `removed`
|
||||
* the array of removed items from this location; `addedCount` the number
|
||||
* of items added at this location.
|
||||
*/
|
||||
function calcSplices(current, currentStart, currentEnd,
|
||||
old, oldStart, oldEnd) {
|
||||
let prefixCount = 0;
|
||||
let suffixCount = 0;
|
||||
let splice;
|
||||
|
||||
let minLength = Math.min(currentEnd - currentStart, oldEnd - oldStart);
|
||||
if (currentStart == 0 && oldStart == 0)
|
||||
prefixCount = sharedPrefix(current, old, minLength);
|
||||
|
||||
if (currentEnd == current.length && oldEnd == old.length)
|
||||
suffixCount = sharedSuffix(current, old, minLength - prefixCount);
|
||||
|
||||
currentStart += prefixCount;
|
||||
oldStart += prefixCount;
|
||||
currentEnd -= suffixCount;
|
||||
oldEnd -= suffixCount;
|
||||
|
||||
if (currentEnd - currentStart == 0 && oldEnd - oldStart == 0)
|
||||
return [];
|
||||
|
||||
if (currentStart == currentEnd) {
|
||||
splice = newSplice(currentStart, [], 0);
|
||||
while (oldStart < oldEnd)
|
||||
splice.removed.push(old[oldStart++]);
|
||||
|
||||
return [ splice ];
|
||||
} else if (oldStart == oldEnd)
|
||||
return [ newSplice(currentStart, [], currentEnd - currentStart) ];
|
||||
|
||||
let ops = spliceOperationsFromEditDistances(
|
||||
calcEditDistances(current, currentStart, currentEnd,
|
||||
old, oldStart, oldEnd));
|
||||
|
||||
splice = undefined;
|
||||
let splices = [];
|
||||
let index = currentStart;
|
||||
let oldIndex = oldStart;
|
||||
for (let i = 0; i < ops.length; i++) {
|
||||
switch(ops[i]) {
|
||||
case EDIT_LEAVE:
|
||||
if (splice) {
|
||||
splices.push(splice);
|
||||
splice = undefined;
|
||||
}
|
||||
|
||||
index++;
|
||||
oldIndex++;
|
||||
break;
|
||||
case EDIT_UPDATE:
|
||||
if (!splice)
|
||||
splice = newSplice(index, [], 0);
|
||||
|
||||
splice.addedCount++;
|
||||
index++;
|
||||
|
||||
splice.removed.push(old[oldIndex]);
|
||||
oldIndex++;
|
||||
break;
|
||||
case EDIT_ADD:
|
||||
if (!splice)
|
||||
splice = newSplice(index, [], 0);
|
||||
|
||||
splice.addedCount++;
|
||||
index++;
|
||||
break;
|
||||
case EDIT_DELETE:
|
||||
if (!splice)
|
||||
splice = newSplice(index, [], 0);
|
||||
|
||||
splice.removed.push(old[oldIndex]);
|
||||
oldIndex++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (splice) {
|
||||
splices.push(splice);
|
||||
}
|
||||
return splices;
|
||||
}
|
||||
|
||||
function sharedPrefix(current, old, searchLength) {
|
||||
for (let i = 0; i < searchLength; i++)
|
||||
if (!equals(current[i], old[i]))
|
||||
return i;
|
||||
return searchLength;
|
||||
}
|
||||
|
||||
function sharedSuffix(current, old, searchLength) {
|
||||
let index1 = current.length;
|
||||
let index2 = old.length;
|
||||
let count = 0;
|
||||
while (count < searchLength && equals(current[--index1], old[--index2]))
|
||||
count++;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
function calculateSplices(current, previous) {
|
||||
return calcSplices(current, 0, current.length, previous, 0,
|
||||
previous.length);
|
||||
}
|
||||
|
||||
function equals(currentValue, previousValue) {
|
||||
return currentValue === previousValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @namespace
|
||||
* @memberof Polymer
|
||||
* @summary Module that provides utilities for diffing arrays.
|
||||
*/
|
||||
Polymer.ArraySplice = {
|
||||
/**
|
||||
* Returns an array of splice records indicating the minimum edits required
|
||||
* to transform the `previous` array into the `current` array.
|
||||
*
|
||||
* Splice records are ordered by index and contain the following fields:
|
||||
* - `index`: index where edit started
|
||||
* - `removed`: array of removed items from this index
|
||||
* - `addedCount`: number of items added at this index
|
||||
*
|
||||
* This function is based on the Levenshtein "minimum edit distance"
|
||||
* algorithm. Note that updates are treated as removal followed by addition.
|
||||
*
|
||||
* The worst-case time complexity of this algorithm is `O(l * p)`
|
||||
* l: The length of the current array
|
||||
* p: The length of the previous array
|
||||
*
|
||||
* However, the worst-case complexity is reduced by an `O(n)` optimization
|
||||
* to detect any shared prefix & suffix between the two arrays and only
|
||||
* perform the more expensive minimum edit distance calculation over the
|
||||
* non-shared portions of the arrays.
|
||||
*
|
||||
* @function
|
||||
* @memberof Polymer.ArraySplice
|
||||
* @param {!Array} current The "changed" array for which splices will be
|
||||
* calculated.
|
||||
* @param {!Array} previous The "unchanged" original array to compare
|
||||
* `current` against to determine the splices.
|
||||
* @return {!Array} Returns an array of splice record objects. Each of these
|
||||
* contains: `index` the location where the splice occurred; `removed`
|
||||
* the array of removed items from this location; `addedCount` the number
|
||||
* of items added at this location.
|
||||
*/
|
||||
calculateSplices
|
||||
function newSplice(index, removed, addedCount) {
|
||||
return {
|
||||
index: index,
|
||||
removed: removed,
|
||||
addedCount: addedCount
|
||||
};
|
||||
}
|
||||
|
||||
})();
|
||||
</script>
|
||||
const EDIT_LEAVE = 0;
|
||||
const EDIT_UPDATE = 1;
|
||||
const EDIT_ADD = 2;
|
||||
const EDIT_DELETE = 3;
|
||||
|
||||
// Note: This function is *based* on the computation of the Levenshtein
|
||||
// "edit" distance. The one change is that "updates" are treated as two
|
||||
// edits - not one. With Array splices, an update is really a delete
|
||||
// followed by an add. By retaining this, we optimize for "keeping" the
|
||||
// maximum array items in the original array. For example:
|
||||
//
|
||||
// 'xxxx123' -> '123yyyy'
|
||||
//
|
||||
// With 1-edit updates, the shortest path would be just to update all seven
|
||||
// characters. With 2-edit updates, we delete 4, leave 3, and add 4. This
|
||||
// leaves the substring '123' intact.
|
||||
function calcEditDistances(current, currentStart, currentEnd,
|
||||
old, oldStart, oldEnd) {
|
||||
// "Deletion" columns
|
||||
let rowCount = oldEnd - oldStart + 1;
|
||||
let columnCount = currentEnd - currentStart + 1;
|
||||
let distances = new Array(rowCount);
|
||||
|
||||
// "Addition" rows. Initialize null column.
|
||||
for (let i = 0; i < rowCount; i++) {
|
||||
distances[i] = new Array(columnCount);
|
||||
distances[i][0] = i;
|
||||
}
|
||||
|
||||
// Initialize null row
|
||||
for (let j = 0; j < columnCount; j++)
|
||||
distances[0][j] = j;
|
||||
|
||||
for (let i = 1; i < rowCount; i++) {
|
||||
for (let j = 1; j < columnCount; j++) {
|
||||
if (equals(current[currentStart + j - 1], old[oldStart + i - 1]))
|
||||
distances[i][j] = distances[i - 1][j - 1];
|
||||
else {
|
||||
let north = distances[i - 1][j] + 1;
|
||||
let west = distances[i][j - 1] + 1;
|
||||
distances[i][j] = north < west ? north : west;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return distances;
|
||||
}
|
||||
|
||||
// This starts at the final weight, and walks "backward" by finding
|
||||
// the minimum previous weight recursively until the origin of the weight
|
||||
// matrix.
|
||||
function spliceOperationsFromEditDistances(distances) {
|
||||
let i = distances.length - 1;
|
||||
let j = distances[0].length - 1;
|
||||
let current = distances[i][j];
|
||||
let edits = [];
|
||||
while (i > 0 || j > 0) {
|
||||
if (i == 0) {
|
||||
edits.push(EDIT_ADD);
|
||||
j--;
|
||||
continue;
|
||||
}
|
||||
if (j == 0) {
|
||||
edits.push(EDIT_DELETE);
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
let northWest = distances[i - 1][j - 1];
|
||||
let west = distances[i - 1][j];
|
||||
let north = distances[i][j - 1];
|
||||
|
||||
let min;
|
||||
if (west < north)
|
||||
min = west < northWest ? west : northWest;
|
||||
else
|
||||
min = north < northWest ? north : northWest;
|
||||
|
||||
if (min == northWest) {
|
||||
if (northWest == current) {
|
||||
edits.push(EDIT_LEAVE);
|
||||
} else {
|
||||
edits.push(EDIT_UPDATE);
|
||||
current = northWest;
|
||||
}
|
||||
i--;
|
||||
j--;
|
||||
} else if (min == west) {
|
||||
edits.push(EDIT_DELETE);
|
||||
i--;
|
||||
current = west;
|
||||
} else {
|
||||
edits.push(EDIT_ADD);
|
||||
j--;
|
||||
current = north;
|
||||
}
|
||||
}
|
||||
|
||||
edits.reverse();
|
||||
return edits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Splice Projection functions:
|
||||
*
|
||||
* A splice map is a representation of how a previous array of items
|
||||
* was transformed into a new array of items. Conceptually it is a list of
|
||||
* tuples of
|
||||
*
|
||||
* <index, removed, addedCount>
|
||||
*
|
||||
* which are kept in ascending index order of. The tuple represents that at
|
||||
* the |index|, |removed| sequence of items were removed, and counting forward
|
||||
* from |index|, |addedCount| items were added.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Lacking individual splice mutation information, the minimal set of
|
||||
* splices can be synthesized given the previous state and final state of an
|
||||
* array. The basic approach is to calculate the edit distance matrix and
|
||||
* choose the shortest path through it.
|
||||
*
|
||||
* Complexity: O(l * p)
|
||||
* l: The length of the current array
|
||||
* p: The length of the old array
|
||||
*
|
||||
* @param {!Array} current The current "changed" array for which to
|
||||
* calculate splices.
|
||||
* @param {number} currentStart Starting index in the `current` array for
|
||||
* which splices are calculated.
|
||||
* @param {number} currentEnd Ending index in the `current` array for
|
||||
* which splices are calculated.
|
||||
* @param {!Array} old The original "unchanged" array to compare `current`
|
||||
* against to determine splices.
|
||||
* @param {number} oldStart Starting index in the `old` array for
|
||||
* which splices are calculated.
|
||||
* @param {number} oldEnd Ending index in the `old` array for
|
||||
* which splices are calculated.
|
||||
* @return {!Array} Returns an array of splice record objects. Each of these
|
||||
* contains: `index` the location where the splice occurred; `removed`
|
||||
* the array of removed items from this location; `addedCount` the number
|
||||
* of items added at this location.
|
||||
*/
|
||||
function calcSplices(current, currentStart, currentEnd,
|
||||
old, oldStart, oldEnd) {
|
||||
let prefixCount = 0;
|
||||
let suffixCount = 0;
|
||||
let splice;
|
||||
|
||||
let minLength = Math.min(currentEnd - currentStart, oldEnd - oldStart);
|
||||
if (currentStart == 0 && oldStart == 0)
|
||||
prefixCount = sharedPrefix(current, old, minLength);
|
||||
|
||||
if (currentEnd == current.length && oldEnd == old.length)
|
||||
suffixCount = sharedSuffix(current, old, minLength - prefixCount);
|
||||
|
||||
currentStart += prefixCount;
|
||||
oldStart += prefixCount;
|
||||
currentEnd -= suffixCount;
|
||||
oldEnd -= suffixCount;
|
||||
|
||||
if (currentEnd - currentStart == 0 && oldEnd - oldStart == 0)
|
||||
return [];
|
||||
|
||||
if (currentStart == currentEnd) {
|
||||
splice = newSplice(currentStart, [], 0);
|
||||
while (oldStart < oldEnd)
|
||||
splice.removed.push(old[oldStart++]);
|
||||
|
||||
return [ splice ];
|
||||
} else if (oldStart == oldEnd)
|
||||
return [ newSplice(currentStart, [], currentEnd - currentStart) ];
|
||||
|
||||
let ops = spliceOperationsFromEditDistances(
|
||||
calcEditDistances(current, currentStart, currentEnd,
|
||||
old, oldStart, oldEnd));
|
||||
|
||||
splice = undefined;
|
||||
let splices = [];
|
||||
let index = currentStart;
|
||||
let oldIndex = oldStart;
|
||||
for (let i = 0; i < ops.length; i++) {
|
||||
switch(ops[i]) {
|
||||
case EDIT_LEAVE:
|
||||
if (splice) {
|
||||
splices.push(splice);
|
||||
splice = undefined;
|
||||
}
|
||||
|
||||
index++;
|
||||
oldIndex++;
|
||||
break;
|
||||
case EDIT_UPDATE:
|
||||
if (!splice)
|
||||
splice = newSplice(index, [], 0);
|
||||
|
||||
splice.addedCount++;
|
||||
index++;
|
||||
|
||||
splice.removed.push(old[oldIndex]);
|
||||
oldIndex++;
|
||||
break;
|
||||
case EDIT_ADD:
|
||||
if (!splice)
|
||||
splice = newSplice(index, [], 0);
|
||||
|
||||
splice.addedCount++;
|
||||
index++;
|
||||
break;
|
||||
case EDIT_DELETE:
|
||||
if (!splice)
|
||||
splice = newSplice(index, [], 0);
|
||||
|
||||
splice.removed.push(old[oldIndex]);
|
||||
oldIndex++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (splice) {
|
||||
splices.push(splice);
|
||||
}
|
||||
return splices;
|
||||
}
|
||||
|
||||
function sharedPrefix(current, old, searchLength) {
|
||||
for (let i = 0; i < searchLength; i++)
|
||||
if (!equals(current[i], old[i]))
|
||||
return i;
|
||||
return searchLength;
|
||||
}
|
||||
|
||||
function sharedSuffix(current, old, searchLength) {
|
||||
let index1 = current.length;
|
||||
let index2 = old.length;
|
||||
let count = 0;
|
||||
while (count < searchLength && equals(current[--index1], old[--index2]))
|
||||
count++;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
function calculateSplices(current, previous) {
|
||||
return calcSplices(current, 0, current.length, previous, 0,
|
||||
previous.length);
|
||||
}
|
||||
|
||||
function equals(currentValue, previousValue) {
|
||||
return currentValue === previousValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Module that provides utilities for diffing arrays.
|
||||
*/
|
||||
`TODO(modulizer): A namespace named Polymer.ArraySplice was
|
||||
declared here. The surrounding comments should be reviewed,
|
||||
and this string can then be deleted`;
|
||||
|
||||
/**
|
||||
* Returns an array of splice records indicating the minimum edits required
|
||||
* to transform the `previous` array into the `current` array.
|
||||
*
|
||||
* Splice records are ordered by index and contain the following fields:
|
||||
* - `index`: index where edit started
|
||||
* - `removed`: array of removed items from this index
|
||||
* - `addedCount`: number of items added at this index
|
||||
*
|
||||
* This function is based on the Levenshtein "minimum edit distance"
|
||||
* algorithm. Note that updates are treated as removal followed by addition.
|
||||
*
|
||||
* The worst-case time complexity of this algorithm is `O(l * p)`
|
||||
* l: The length of the current array
|
||||
* p: The length of the previous array
|
||||
*
|
||||
* However, the worst-case complexity is reduced by an `O(n)` optimization
|
||||
* to detect any shared prefix & suffix between the two arrays and only
|
||||
* perform the more expensive minimum edit distance calculation over the
|
||||
* non-shared portions of the arrays.
|
||||
*
|
||||
* @function
|
||||
* @param {!Array} current The "changed" array for which splices will be
|
||||
* calculated.
|
||||
* @param {!Array} previous The "unchanged" original array to compare
|
||||
* `current` against to determine the splices.
|
||||
* @return {!Array} Returns an array of splice record objects. Each of these
|
||||
* contains: `index` the location where the splice occurred; `removed`
|
||||
* the array of removed items from this location; `addedCount` the number
|
||||
* of items added at this location.
|
||||
*/
|
||||
export { calculateSplices };
|
||||
|
||||
Reference in New Issue
Block a user