@lexriver/dome 2.0.2 → 2.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/out/index.cjs +802 -0
- package/out/index.mjs +1987 -0
- package/out/src/AnimatedArray.d.ts +29 -0
- package/out/src/AnimatedTable.d.mts +2 -1
- package/out/src/AnimatedTable.d.ts +39 -0
- package/out/src/AnimatedTable.mjs +2 -1
- package/out/src/AnimatedText.d.mts +1 -1
- package/out/src/AnimatedText.d.ts +13 -0
- package/out/src/AnimatedText.mjs +1 -1
- package/out/src/Animation.d.ts +4 -0
- package/out/src/Dome.d.ts +9 -0
- package/out/src/Dome.mjs +1 -1
- package/out/src/DomeComponent.d.ts +25 -0
- package/out/src/DomeManipulator.d.ts +52 -0
- package/out/src/DomeRouter.console-test.d.ts +1 -0
- package/out/src/DomeRouter.d.ts +32 -0
- package/out/src/LongestCommonSubsequence.d.ts +15 -0
- package/out/src/concurrent-updates.test.d.ts +1 -0
- package/out/src/index.d.ts +11 -0
- package/out/src/temp-test.d.ts +1 -0
- package/package.json +20 -3
- package/out/vitest.config.d.ts +0 -2
- package/out/vitest.config.js +0 -9
- package/src/AnimatedArray.mts +0 -74
- package/src/AnimatedTable.mts +0 -120
- package/src/AnimatedText.mts +0 -30
- package/src/Animation.mts +0 -4
- package/src/Dome.mts +0 -346
- package/src/DomeComponent.mts +0 -84
- package/src/DomeManipulator.mts +0 -391
- package/src/DomeRouter.console-test.mts +0 -52
- package/src/DomeRouter.mts +0 -287
- package/src/LongestCommonSubsequence.mts +0 -201
- package/src/concurrent-updates.test.mts +0 -252
- package/src/index.mts +0 -12
- package/src/temp-test.mts +0 -23
- package/tsconfig.json +0 -59
- package/vitest.config.ts +0 -10
package/src/DomeRouter.mts
DELETED
|
@@ -1,287 +0,0 @@
|
|
|
1
|
-
import { DomeManipulator } from "./DomeManipulator.mjs"
|
|
2
|
-
|
|
3
|
-
// const filename = '[DomeRouter]'
|
|
4
|
-
|
|
5
|
-
export type RouteAction = (
|
|
6
|
-
params:{[key:string]:string|number},
|
|
7
|
-
url:string,
|
|
8
|
-
scrollToPreviousPositionAsync:()=>Promise<void>,
|
|
9
|
-
query:{[key:string]:string}
|
|
10
|
-
) => void|Promise<void>
|
|
11
|
-
|
|
12
|
-
interface Route{
|
|
13
|
-
routeSlices:string[]
|
|
14
|
-
exactMatch:boolean
|
|
15
|
-
action:RouteAction
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
interface HistoryUrl{
|
|
19
|
-
url:string
|
|
20
|
-
scroll:number
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export namespace DomeRouter {
|
|
24
|
-
const historyUrls:HistoryUrl[] = []
|
|
25
|
-
const scrollPositionByUrl = new Map<string, number>()
|
|
26
|
-
export let maxHistoryUrlsCount:number = 20
|
|
27
|
-
const allRoutes:Route[] = []
|
|
28
|
-
let onNotFoundAction:(()=>void)|undefined = undefined
|
|
29
|
-
|
|
30
|
-
window.addEventListener('popstate', async (e) => {
|
|
31
|
-
// on go back/forward
|
|
32
|
-
//console.log(filename, 'window.popstate event', e, 'historyUrls=', historyUrls)
|
|
33
|
-
const url = window.location.pathname
|
|
34
|
-
await executeAsync(url, getScrollPositionForUrl(url) )
|
|
35
|
-
|
|
36
|
-
await DomeManipulator.scrollToAsync({
|
|
37
|
-
pxFromTop: getScrollPositionForUrl(window.location.pathname)
|
|
38
|
-
})
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
function getScrollPositionForUrl(url:string){
|
|
42
|
-
return scrollPositionByUrl.get(url) ?? 0
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function saveScrollPositionForUrl(url:string){
|
|
46
|
-
scrollPositionByUrl.set(url, DomeManipulator.getCurrentScrollPosition())
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function saveScrollPositionForCurrentUrl(){
|
|
50
|
-
scrollPositionByUrl.set(getCurrentUrl(), DomeManipulator.getCurrentScrollPosition())
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export function navigate(url:string){
|
|
54
|
-
addUrlToHistory(getCurrentUrl())
|
|
55
|
-
saveScrollPositionForCurrentUrl()
|
|
56
|
-
window.history.pushState(null, '', url)
|
|
57
|
-
executeAsync(url, 0)
|
|
58
|
-
DomeManipulator.scrollToTop()
|
|
59
|
-
// historyUrls.push()
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export function goBack(){
|
|
63
|
-
window.history.go(-1)
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export function goForward(){
|
|
67
|
-
window.history.go(+1)
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
export function changeUrl(url:string){
|
|
71
|
-
window.history.replaceState(null, '', url)
|
|
72
|
-
// if(historyUrls.length>0){
|
|
73
|
-
// historyUrls[historyUrls.length-1].url = url
|
|
74
|
-
// }
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function addUrlToHistory(url:string){
|
|
78
|
-
historyUrls.push({url, scroll: 0})
|
|
79
|
-
while(historyUrls.length>maxHistoryUrlsCount){
|
|
80
|
-
historyUrls.shift()
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// save scroll position to previous url
|
|
84
|
-
if(historyUrls.length>1){
|
|
85
|
-
historyUrls[historyUrls.length-2].scroll = DomeManipulator.getCurrentScrollPosition()
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// console.log('adding url to history', url)
|
|
89
|
-
// console.log('historyUrls=', historyUrls)
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
export function getCurrentUrl(){
|
|
95
|
-
return window.location.pathname
|
|
96
|
-
// return historyUrls.length>0?historyUrls[historyUrls.length-1]:window.location.pathname
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* get previous page url navigated by router
|
|
101
|
-
* @param previousPageIndex 0=previousPage, 1=previousPage-1, etc
|
|
102
|
-
*/
|
|
103
|
-
export function getPreviousPageUrl(previousPageIndex:number=0):string|undefined{
|
|
104
|
-
//if(historyUrls.length==0) return undefined
|
|
105
|
-
let index = historyUrls.length-2-previousPageIndex
|
|
106
|
-
if(index >= 0 && index < historyUrls.length) return historyUrls[index].url
|
|
107
|
-
return undefined
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// export function reloadCurrentPage(addToHistory:boolean = false){
|
|
111
|
-
// const url = window.location.pathname
|
|
112
|
-
// if(addToHistory) addUrlToHistory(url)
|
|
113
|
-
// executeAsync(url, getScrollPositionForUrl(url)) // TODO: check
|
|
114
|
-
// }
|
|
115
|
-
|
|
116
|
-
export function resolveUrl(url:string = window.location.pathname, addToHistory = true){
|
|
117
|
-
if(addToHistory) addUrlToHistory(window.location.pathname)
|
|
118
|
-
executeAsync(url, getScrollPositionForUrl(url))
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
export function onRoute(route:string, exactMatch:boolean, action:RouteAction){
|
|
122
|
-
if(route[0] !== '/') throw new Error('Please provide correct route. route='+route)
|
|
123
|
-
let routeSlices = getRouteSlices(route)
|
|
124
|
-
allRoutes.push({routeSlices, exactMatch, action})
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
function getRouteSlices(route:string){
|
|
128
|
-
//return route.split('/').filter(x => x.length>0).map(x => decodeURIComponent(x))
|
|
129
|
-
return route.split('/').map(x => decodeURIComponent(x))
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
export function onNotFound(action:()=>void){
|
|
133
|
-
onNotFoundAction = action
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Check if url matched route.
|
|
138
|
-
* example: /get-product/445345, /get-product/:productId, true ==> Map([productId:445345])
|
|
139
|
-
* example: /get-product/123, /another-route, true ==> undefined
|
|
140
|
-
* example: /articles, /articles, true ==> Map()
|
|
141
|
-
* example: /articles/some-article, /articles, false ==> Map()
|
|
142
|
-
* @param url
|
|
143
|
-
* @param route
|
|
144
|
-
* @param exactMatch
|
|
145
|
-
*/
|
|
146
|
-
export function checkUrlMatchRouteAndGetParameters(url:string, route:string, exactMatch:boolean = true):Object|undefined{
|
|
147
|
-
// this function is purely for export
|
|
148
|
-
|
|
149
|
-
const urlSlices = getRouteSlices(url)
|
|
150
|
-
const routeSlices = getRouteSlices(route)
|
|
151
|
-
if(exactMatch && routeSlices.length !== urlSlices.length) return undefined
|
|
152
|
-
// all routeSlices must match for !exactMatch
|
|
153
|
-
//const extractedParameters = new Map<string,string>()
|
|
154
|
-
const extractedParameters = {}
|
|
155
|
-
for(let i=0;i<routeSlices.length;i++){
|
|
156
|
-
let cRouteSlice = routeSlices[i]
|
|
157
|
-
let cUrlSlice = urlSlices[i] // could be undefined
|
|
158
|
-
//console.info('\t\t cRouteSlice=', cRouteSlice, 'cUrlSlice=', cUrlSlice)
|
|
159
|
-
|
|
160
|
-
if(cRouteSlice.startsWith(':')){
|
|
161
|
-
// we have a param name
|
|
162
|
-
if(cUrlSlice === undefined){
|
|
163
|
-
return undefined
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
const [name, value] = parseToNameAndValue(cRouteSlice, cUrlSlice)
|
|
167
|
-
extractedParameters[name] = value
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
} else {
|
|
171
|
-
// no param name
|
|
172
|
-
if(cRouteSlice !== cUrlSlice){
|
|
173
|
-
return undefined
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
return extractedParameters
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* parse name with type or without type
|
|
182
|
-
* :quantity
|
|
183
|
-
* :quantity<number>
|
|
184
|
-
* :quantity<float>
|
|
185
|
-
* :quantity<int>
|
|
186
|
-
*
|
|
187
|
-
* @param name string that starts with `:`
|
|
188
|
-
* @param value string that contains value
|
|
189
|
-
*/
|
|
190
|
-
function parseToNameAndValue(name:string, value:string):[string,string|number]{
|
|
191
|
-
if(!name) throw new Error('no name')
|
|
192
|
-
|
|
193
|
-
// parse name with type
|
|
194
|
-
// :quantity<number>
|
|
195
|
-
// :lat<float>
|
|
196
|
-
// :count<int>
|
|
197
|
-
const haveMatch = name.match(/:(.+)<(.+)>/)
|
|
198
|
-
if(haveMatch){
|
|
199
|
-
const paramName = haveMatch[1]
|
|
200
|
-
const paramType = haveMatch[2].toLowerCase()
|
|
201
|
-
if(paramType == 'int'){
|
|
202
|
-
return [paramName, parseInt(value)]
|
|
203
|
-
|
|
204
|
-
} else if(paramType == 'float'){
|
|
205
|
-
return [paramName, parseFloat(value)]
|
|
206
|
-
|
|
207
|
-
} else if(paramType == 'number'){
|
|
208
|
-
return [paramName, Number(value)]
|
|
209
|
-
|
|
210
|
-
} else {
|
|
211
|
-
return [paramName, value]
|
|
212
|
-
}
|
|
213
|
-
} else {
|
|
214
|
-
return [name.substring(1), value]
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
function parseQueryString(search:string):{[key:string]:string}{
|
|
220
|
-
const query:{[key:string]:string} = {}
|
|
221
|
-
if(!search || search[0] !== '?') return query
|
|
222
|
-
const params = new URLSearchParams(search.substring(1))
|
|
223
|
-
for(const [key, value] of params.entries()){
|
|
224
|
-
query[key] = value
|
|
225
|
-
}
|
|
226
|
-
return query
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
async function executeAsync(url:string = window.location.pathname, scrollToPosition:number){
|
|
230
|
-
const queryString = window.location.search
|
|
231
|
-
const query = parseQueryString(queryString)
|
|
232
|
-
url = url.split('?')[0] // Strip query params
|
|
233
|
-
//const url = window.location.pathname
|
|
234
|
-
const urlSlices = getRouteSlices(url)
|
|
235
|
-
// urlSlices = show, category, 345
|
|
236
|
-
let countOfFoundRoutes = 0
|
|
237
|
-
for(let route of allRoutes){
|
|
238
|
-
//console.info('\t route=', route.routeSlices, route.exactMatch)
|
|
239
|
-
// route.routeSlices = show, category, :categoryId
|
|
240
|
-
if(route.exactMatch && route.routeSlices.length != urlSlices.length) continue
|
|
241
|
-
|
|
242
|
-
// all routeSlices must match for !exactMatch
|
|
243
|
-
let matched:boolean = true
|
|
244
|
-
//let extractedParameters = new Map<string,string>()
|
|
245
|
-
const extractedParameters = {}
|
|
246
|
-
for(let i=0;i<route.routeSlices.length;i++){
|
|
247
|
-
let cRouteSlice = route.routeSlices[i]
|
|
248
|
-
let cUrlSlice = urlSlices[i] // could be undefined
|
|
249
|
-
//console.info('\t\t cRouteSlice=', cRouteSlice, 'cUrlSlice=', cUrlSlice)
|
|
250
|
-
|
|
251
|
-
if(cRouteSlice && typeof cRouteSlice === 'string' && cRouteSlice.startsWith(':')){
|
|
252
|
-
// we have a param name
|
|
253
|
-
if(cUrlSlice === undefined){
|
|
254
|
-
matched = false
|
|
255
|
-
continue
|
|
256
|
-
}
|
|
257
|
-
// we have some variable, save it
|
|
258
|
-
const [name, value] = parseToNameAndValue(cRouteSlice, cUrlSlice)
|
|
259
|
-
extractedParameters[name] = value
|
|
260
|
-
|
|
261
|
-
} else {
|
|
262
|
-
// no param name
|
|
263
|
-
if(cRouteSlice !== cUrlSlice){
|
|
264
|
-
matched = false
|
|
265
|
-
continue
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
if(matched){
|
|
270
|
-
countOfFoundRoutes++
|
|
271
|
-
//console.log('DomeRouter', 'executeAsync', url, route, extractedParameters)
|
|
272
|
-
await route.action(extractedParameters, url, async () => {
|
|
273
|
-
await DomeManipulator.scrollToAsync({
|
|
274
|
-
pxFromTop: scrollToPosition
|
|
275
|
-
})
|
|
276
|
-
}, query)
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
if(countOfFoundRoutes==0 && onNotFoundAction){
|
|
280
|
-
//console.log('DomeRouter', 'onNotFoundAction()')
|
|
281
|
-
onNotFoundAction()
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
export module LongestCommonSubsequence {
|
|
2
|
-
// https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sets/longest-common-subsequence
|
|
3
|
-
export function getLongestCommonSubsequence(set1: string[], set2: string[]) {
|
|
4
|
-
// Init LCS matrix.
|
|
5
|
-
const lcsMatrix = Array(set2.length + 1).fill(null).map(() => Array(set1.length + 1).fill(null));
|
|
6
|
-
|
|
7
|
-
// Fill first row with zeros.
|
|
8
|
-
for (let columnIndex = 0; columnIndex <= set1.length; columnIndex += 1) {
|
|
9
|
-
lcsMatrix[0][columnIndex] = 0;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
// Fill first column with zeros.
|
|
13
|
-
for (let rowIndex = 0; rowIndex <= set2.length; rowIndex += 1) {
|
|
14
|
-
lcsMatrix[rowIndex][0] = 0;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// Fill rest of the column that correspond to each of two strings.
|
|
18
|
-
for (let rowIndex = 1; rowIndex <= set2.length; rowIndex += 1) {
|
|
19
|
-
for (let columnIndex = 1; columnIndex <= set1.length; columnIndex += 1) {
|
|
20
|
-
if (set1[columnIndex - 1] === set2[rowIndex - 1]) {
|
|
21
|
-
lcsMatrix[rowIndex][columnIndex] = lcsMatrix[rowIndex - 1][columnIndex - 1] + 1;
|
|
22
|
-
} else {
|
|
23
|
-
lcsMatrix[rowIndex][columnIndex] = Math.max(
|
|
24
|
-
lcsMatrix[rowIndex - 1][columnIndex],
|
|
25
|
-
lcsMatrix[rowIndex][columnIndex - 1],
|
|
26
|
-
);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// Calculate LCS based on LCS matrix.
|
|
32
|
-
if (!lcsMatrix[set2.length][set1.length]) {
|
|
33
|
-
// If the length of largest common string is zero then return empty string.
|
|
34
|
-
return [''];
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const longestSequence:string[] = [];
|
|
38
|
-
let columnIndex = set1.length;
|
|
39
|
-
let rowIndex = set2.length;
|
|
40
|
-
|
|
41
|
-
while (columnIndex > 0 || rowIndex > 0) {
|
|
42
|
-
if (set1[columnIndex - 1] === set2[rowIndex - 1]) {
|
|
43
|
-
// Move by diagonal left-top.
|
|
44
|
-
longestSequence.unshift(set1[columnIndex - 1]);
|
|
45
|
-
columnIndex -= 1;
|
|
46
|
-
rowIndex -= 1;
|
|
47
|
-
} else if (lcsMatrix[rowIndex][columnIndex] === lcsMatrix[rowIndex][columnIndex - 1]) {
|
|
48
|
-
// Move left.
|
|
49
|
-
columnIndex -= 1;
|
|
50
|
-
} else {
|
|
51
|
-
// Move up.
|
|
52
|
-
rowIndex -= 1;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return longestSequence;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export function getPatch({ oldArray, newArray, onRemove, onAdd }: {
|
|
60
|
-
oldArray: string[]
|
|
61
|
-
newArray: string[]
|
|
62
|
-
onRemove: (index: number, item: string) => void
|
|
63
|
-
onAdd: (index: number, item: string) => void
|
|
64
|
-
}){
|
|
65
|
-
const lcsArray = getLongestCommonSubsequence(oldArray, newArray)
|
|
66
|
-
|
|
67
|
-
let countOfOperations = 0
|
|
68
|
-
|
|
69
|
-
// console.log('oldArray=', oldArray.join(' '))
|
|
70
|
-
// console.log('newArray=', newArray.join(' '))
|
|
71
|
-
// console.log('lcsArray=', lcsArray.join(' '))
|
|
72
|
-
|
|
73
|
-
// old: A B B B C
|
|
74
|
-
// new: X X B B B B C
|
|
75
|
-
// lcs: B B B C
|
|
76
|
-
|
|
77
|
-
//let lcsStartIndexForOld = 0
|
|
78
|
-
//let lcsStartIndexForNew = 0
|
|
79
|
-
let lcsIndex = 0
|
|
80
|
-
//let newIndex = 0
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
for(let oldIndex = 0; oldIndex < oldArray.length; oldIndex++){
|
|
84
|
-
let oldItem = oldArray[oldIndex]
|
|
85
|
-
//let oldItemInLcs = itemInArray(oldItem, lcsArray, lcsStartIndexForOld)
|
|
86
|
-
let oldItemInLcs = lcsArray[lcsIndex] == oldItem
|
|
87
|
-
//console.log('oldIndex=', oldIndex, 'oldItem=', oldItem, 'in LCS=', oldItemInLcs)
|
|
88
|
-
if(oldItemInLcs){
|
|
89
|
-
//lcsStartIndexForOld++
|
|
90
|
-
lcsIndex++
|
|
91
|
-
// so we must keep this element
|
|
92
|
-
//continue
|
|
93
|
-
} else {
|
|
94
|
-
onRemove(oldIndex-countOfOperations, oldItem)
|
|
95
|
-
//indexForOperation++
|
|
96
|
-
countOfOperations++
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
lcsIndex = 0
|
|
101
|
-
|
|
102
|
-
for(let newIndex = 0; newIndex < newArray.length; newIndex++){
|
|
103
|
-
let newItem = newArray[newIndex]
|
|
104
|
-
//let newItemInLcs = itemInArray(newItem, lcsArray, lcsStartIndexForNew)
|
|
105
|
-
let newItemInLcs = lcsArray[lcsIndex] == newItem
|
|
106
|
-
if(newItemInLcs){
|
|
107
|
-
lcsIndex++
|
|
108
|
-
//lcsStartIndexForNew++
|
|
109
|
-
//keep
|
|
110
|
-
} else {
|
|
111
|
-
onAdd(newIndex, newItem)
|
|
112
|
-
countOfOperations++
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
return countOfOperations
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
export function getPatchOrdered({ oldArray, newArray, onRemove, onAdd }: {
|
|
123
|
-
oldArray: string[]
|
|
124
|
-
newArray: string[]
|
|
125
|
-
onRemove: (index: number, item: string) => void
|
|
126
|
-
onAdd: (index: number, item: string) => void
|
|
127
|
-
}){
|
|
128
|
-
|
|
129
|
-
const lcsArray = getLongestCommonSubsequence(oldArray, newArray)
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
//let countOfOperations = 0
|
|
134
|
-
|
|
135
|
-
// console.log('oldArray=', oldArray.join(' '))
|
|
136
|
-
// console.log('newArray=', newArray.join(' '))
|
|
137
|
-
// console.log('lcsArray=', lcsArray.join(' '))
|
|
138
|
-
|
|
139
|
-
// old: A B B B C
|
|
140
|
-
// new: X X B B B B C
|
|
141
|
-
// lcs: B B B C
|
|
142
|
-
|
|
143
|
-
let lcsIndexForOld = 0
|
|
144
|
-
let lcsIndexForNew = 0
|
|
145
|
-
//let lcsIndex = 0
|
|
146
|
-
let oldIndex = 0
|
|
147
|
-
let newIndex = 0
|
|
148
|
-
let countOfRemoveOperations = 0
|
|
149
|
-
let countOfAddOperations = 0
|
|
150
|
-
let indexForOperation = 0
|
|
151
|
-
|
|
152
|
-
while(oldIndex < oldArray.length || newIndex < newArray.length){
|
|
153
|
-
while(oldIndex < oldArray.length){
|
|
154
|
-
let oldItem = oldArray[oldIndex]
|
|
155
|
-
//let oldItemInLcs = itemInArray(oldItem, lcsArray, lcsStartIndexForOld)
|
|
156
|
-
let oldItemInLcs = lcsArray[lcsIndexForOld] == oldItem
|
|
157
|
-
//console.log('oldItem', oldIndex, oldItem, oldItemInLcs)
|
|
158
|
-
if(oldItemInLcs){
|
|
159
|
-
|
|
160
|
-
//lcsStartIndexForNew++
|
|
161
|
-
//lcsStartIndexForOld++
|
|
162
|
-
lcsIndexForOld++
|
|
163
|
-
//indexForOperation++
|
|
164
|
-
oldIndex++
|
|
165
|
-
break
|
|
166
|
-
} else {
|
|
167
|
-
//onRemove(oldIndex-countOfRemoveOperations, oldItem)
|
|
168
|
-
onRemove(indexForOperation, oldItem)
|
|
169
|
-
countOfRemoveOperations++
|
|
170
|
-
oldIndex++
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
}
|
|
174
|
-
while(newIndex < newArray.length){
|
|
175
|
-
let newItem = newArray[newIndex]
|
|
176
|
-
//let newItemInLcs = itemInArray(newItem, lcsArray, lcsStartIndexForNew)
|
|
177
|
-
let newItemInLcs = lcsArray[lcsIndexForNew] == newItem
|
|
178
|
-
//console.log('newItem', oldIndex, newItem, newItemInLcs)
|
|
179
|
-
if(newItemInLcs){
|
|
180
|
-
//lcsStartIndexForNew++
|
|
181
|
-
lcsIndexForNew++
|
|
182
|
-
indexForOperation++
|
|
183
|
-
newIndex++
|
|
184
|
-
break
|
|
185
|
-
} else {
|
|
186
|
-
onAdd(indexForOperation, newItem)
|
|
187
|
-
indexForOperation++
|
|
188
|
-
countOfAddOperations++
|
|
189
|
-
newIndex++
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// console.log('countOf(+)operations', countOfAddOperations)
|
|
196
|
-
// console.log('countOf(-)operations', countOfRemoveOperations)
|
|
197
|
-
return countOfAddOperations+countOfRemoveOperations
|
|
198
|
-
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
}
|