UNPKG

ckeditor5-image-upload-base64

Version:

The development environment of CKEditor 5 – the best browser-based rich text editor.

139 lines (112 loc) 4.1 kB
/** * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license */ /** * @module utils/diff */ import fastDiff from '../src/fastdiff'; // The following code is based on the "O(NP) Sequence Comparison Algorithm" // by Sun Wu, Udi Manber, Gene Myers, Webb Miller. /** * Calculates the difference between two arrays or strings producing an array containing a list of changes * necessary to transform input into output. * * diff( 'aba', 'acca' ); // [ 'equal', 'insert', 'insert', 'delete', 'equal' ] * * This function is based on the "O(NP) Sequence Comparison Algorithm" by Sun Wu, Udi Manber, Gene Myers, Webb Miller. * Unfortunately, while it gives the most precise results, its to complex for longer strings/arrow (above 200 items). * Therefore, `diff()` automatically switches to {@link module:utils/fastdiff~fastDiff `fastDiff()`} when detecting * such a scenario. The return formats of both functions are identical. * * @param {Array|String} a Input array or string. * @param {Array|String} b Output array or string. * @param {Function} [cmp] Optional function used to compare array values, by default === is used. * @returns {Array} Array of changes. */ export default function diff( a, b, cmp ) { // Set the comparator function. cmp = cmp || function( a, b ) { return a === b; }; const aLength = a.length; const bLength = b.length; // Perform `fastDiff` for longer strings/arrays (see #269). if ( aLength > 200 || bLength > 200 || aLength + bLength > 300 ) { return diff.fastDiff( a, b, cmp, true ); } // Temporary action type statics. let _insert, _delete; // Swapped the arrays to use the shorter one as the first one. if ( bLength < aLength ) { const tmp = a; a = b; b = tmp; // We swap the action types as well. _insert = 'delete'; _delete = 'insert'; } else { _insert = 'insert'; _delete = 'delete'; } const m = a.length; const n = b.length; const delta = n - m; // Edit scripts, for each diagonal. const es = {}; // Furthest points, the furthest y we can get on each diagonal. const fp = {}; function snake( k ) { // We use -1 as an alternative below to handle initial values ( instead of filling the fp with -1 first ). // Furthest points (y) on the diagonal below k. const y1 = ( fp[ k - 1 ] !== undefined ? fp[ k - 1 ] : -1 ) + 1; // Furthest points (y) on the diagonal above k. const y2 = fp[ k + 1 ] !== undefined ? fp[ k + 1 ] : -1; // The way we should go to get further. const dir = y1 > y2 ? -1 : 1; // Clone previous changes array (if any). if ( es[ k + dir ] ) { es[ k ] = es[ k + dir ].slice( 0 ); } // Create changes array. if ( !es[ k ] ) { es[ k ] = []; } // Push the action. es[ k ].push( y1 > y2 ? _insert : _delete ); // Set the beginning coordinates. let y = Math.max( y1, y2 ); let x = y - k; // Traverse the diagonal as long as the values match. while ( x < m && y < n && cmp( a[ x ], b[ y ] ) ) { x++; y++; // Push no change action. es[ k ].push( 'equal' ); } return y; } let p = 0; let k; // Traverse the graph until we reach the end of the longer string. do { // Updates furthest points and edit scripts for diagonals below delta. for ( k = -p; k < delta; k++ ) { fp[ k ] = snake( k ); } // Updates furthest points and edit scripts for diagonals above delta. for ( k = delta + p; k > delta; k-- ) { fp[ k ] = snake( k ); } // Updates furthest point and edit script for the delta diagonal. // note that the delta diagonal is the one which goes through the sink (m, n). fp[ delta ] = snake( delta ); p++; } while ( fp[ delta ] !== n ); // Return the final list of edit changes. // We remove the first item that represents the action for the injected nulls. return es[ delta ].slice( 1 ); } // Store the API in static property to easily overwrite it in tests. // Too bad dependency injection does not work in Webpack + ES 6 (const) + Babel. diff.fastDiff = fastDiff;