| 1 |
import ChangeDetails from '../../core/change-details.js'; |
| 2 |
import { isString } from '../../core/utils.js'; |
| 3 |
import ContinuousTailDetails from '../../core/continuous-tail-details.js'; |
| 4 |
import IMask from '../../core/holder.js'; |
| 5 |
|
| 6 |
class ChunksTailDetails { |
| 7 |
/** */ |
| 8 |
|
| 9 |
constructor(chunks, from) { |
| 10 |
if (chunks === void 0) { |
| 11 |
chunks = []; |
| 12 |
} |
| 13 |
if (from === void 0) { |
| 14 |
from = 0; |
| 15 |
} |
| 16 |
this.chunks = chunks; |
| 17 |
this.from = from; |
| 18 |
} |
| 19 |
toString() { |
| 20 |
return this.chunks.map(String).join(''); |
| 21 |
} |
| 22 |
extend(tailChunk) { |
| 23 |
if (!String(tailChunk)) return; |
| 24 |
tailChunk = isString(tailChunk) ? new ContinuousTailDetails(String(tailChunk)) : tailChunk; |
| 25 |
const lastChunk = this.chunks[this.chunks.length - 1]; |
| 26 |
const extendLast = lastChunk && ( |
| 27 |
// if stops are same or tail has no stop |
| 28 |
lastChunk.stop === tailChunk.stop || tailChunk.stop == null) && |
| 29 |
// if tail chunk goes just after last chunk |
| 30 |
tailChunk.from === lastChunk.from + lastChunk.toString().length; |
| 31 |
if (tailChunk instanceof ContinuousTailDetails) { |
| 32 |
// check the ability to extend previous chunk |
| 33 |
if (extendLast) { |
| 34 |
// extend previous chunk |
| 35 |
lastChunk.extend(tailChunk.toString()); |
| 36 |
} else { |
| 37 |
// append new chunk |
| 38 |
this.chunks.push(tailChunk); |
| 39 |
} |
| 40 |
} else if (tailChunk instanceof ChunksTailDetails) { |
| 41 |
if (tailChunk.stop == null) { |
| 42 |
// unwrap floating chunks to parent, keeping `from` pos |
| 43 |
let firstTailChunk; |
| 44 |
while (tailChunk.chunks.length && tailChunk.chunks[0].stop == null) { |
| 45 |
firstTailChunk = tailChunk.chunks.shift(); // not possible to be `undefined` because length was checked above |
| 46 |
firstTailChunk.from += tailChunk.from; |
| 47 |
this.extend(firstTailChunk); |
| 48 |
} |
| 49 |
} |
| 50 |
|
| 51 |
// if tail chunk still has value |
| 52 |
if (tailChunk.toString()) { |
| 53 |
// if chunks contains stops, then popup stop to container |
| 54 |
tailChunk.stop = tailChunk.blockIndex; |
| 55 |
this.chunks.push(tailChunk); |
| 56 |
} |
| 57 |
} |
| 58 |
} |
| 59 |
appendTo(masked) { |
| 60 |
if (!(masked instanceof IMask.MaskedPattern)) { |
| 61 |
const tail = new ContinuousTailDetails(this.toString()); |
| 62 |
return tail.appendTo(masked); |
| 63 |
} |
| 64 |
const details = new ChangeDetails(); |
| 65 |
for (let ci = 0; ci < this.chunks.length; ++ci) { |
| 66 |
const chunk = this.chunks[ci]; |
| 67 |
const lastBlockIter = masked._mapPosToBlock(masked.displayValue.length); |
| 68 |
const stop = chunk.stop; |
| 69 |
let chunkBlock; |
| 70 |
if (stop != null && ( |
| 71 |
// if block not found or stop is behind lastBlock |
| 72 |
!lastBlockIter || lastBlockIter.index <= stop)) { |
| 73 |
if (chunk instanceof ChunksTailDetails || |
| 74 |
// for continuous block also check if stop is exist |
| 75 |
masked._stops.indexOf(stop) >= 0) { |
| 76 |
details.aggregate(masked._appendPlaceholder(stop)); |
| 77 |
} |
| 78 |
chunkBlock = chunk instanceof ChunksTailDetails && masked._blocks[stop]; |
| 79 |
} |
| 80 |
if (chunkBlock) { |
| 81 |
const tailDetails = chunkBlock.appendTail(chunk); |
| 82 |
details.aggregate(tailDetails); |
| 83 |
|
| 84 |
// get not inserted chars |
| 85 |
const remainChars = chunk.toString().slice(tailDetails.rawInserted.length); |
| 86 |
if (remainChars) details.aggregate(masked.append(remainChars, { |
| 87 |
tail: true |
| 88 |
})); |
| 89 |
} else { |
| 90 |
details.aggregate(masked.append(chunk.toString(), { |
| 91 |
tail: true |
| 92 |
})); |
| 93 |
} |
| 94 |
} |
| 95 |
return details; |
| 96 |
} |
| 97 |
get state() { |
| 98 |
return { |
| 99 |
chunks: this.chunks.map(c => c.state), |
| 100 |
from: this.from, |
| 101 |
stop: this.stop, |
| 102 |
blockIndex: this.blockIndex |
| 103 |
}; |
| 104 |
} |
| 105 |
set state(state) { |
| 106 |
const { |
| 107 |
chunks, |
| 108 |
...props |
| 109 |
} = state; |
| 110 |
Object.assign(this, props); |
| 111 |
this.chunks = chunks.map(cstate => { |
| 112 |
const chunk = "chunks" in cstate ? new ChunksTailDetails() : new ContinuousTailDetails(); |
| 113 |
chunk.state = cstate; |
| 114 |
return chunk; |
| 115 |
}); |
| 116 |
} |
| 117 |
unshift(beforePos) { |
| 118 |
if (!this.chunks.length || beforePos != null && this.from >= beforePos) return ''; |
| 119 |
const chunkShiftPos = beforePos != null ? beforePos - this.from : beforePos; |
| 120 |
let ci = 0; |
| 121 |
while (ci < this.chunks.length) { |
| 122 |
const chunk = this.chunks[ci]; |
| 123 |
const shiftChar = chunk.unshift(chunkShiftPos); |
| 124 |
if (chunk.toString()) { |
| 125 |
// chunk still contains value |
| 126 |
// but not shifted - means no more available chars to shift |
| 127 |
if (!shiftChar) break; |
| 128 |
++ci; |
| 129 |
} else { |
| 130 |
// clean if chunk has no value |
| 131 |
this.chunks.splice(ci, 1); |
| 132 |
} |
| 133 |
if (shiftChar) return shiftChar; |
| 134 |
} |
| 135 |
return ''; |
| 136 |
} |
| 137 |
shift() { |
| 138 |
if (!this.chunks.length) return ''; |
| 139 |
let ci = this.chunks.length - 1; |
| 140 |
while (0 <= ci) { |
| 141 |
const chunk = this.chunks[ci]; |
| 142 |
const shiftChar = chunk.shift(); |
| 143 |
if (chunk.toString()) { |
| 144 |
// chunk still contains value |
| 145 |
// but not shifted - means no more available chars to shift |
| 146 |
if (!shiftChar) break; |
| 147 |
--ci; |
| 148 |
} else { |
| 149 |
// clean if chunk has no value |
| 150 |
this.chunks.splice(ci, 1); |
| 151 |
} |
| 152 |
if (shiftChar) return shiftChar; |
| 153 |
} |
| 154 |
return ''; |
| 155 |
} |
| 156 |
} |
| 157 |
|
| 158 |
export { ChunksTailDetails as default }; |
| 159 |
|