devexpress-richedit
Version:
DevExpress Rich Text Editor is an advanced word-processing tool designed for working with rich text documents.
114 lines (113 loc) • 6.39 kB
JavaScript
import { SearchUtils } from '@devexpress/utils/lib/utils/search';
import { LayoutBoxType } from '../../layout/main-structures/layout-boxes/layout-box';
import { TextBufferChangedSubDocumentChange } from '../changes/sub-document/text/text-buffer-changed';
import { HistoryItemIntervalState } from '../history/states/history-item-state';
import { HistoryItemTextBufferStateObject } from '../history/states/history-item-state-object';
import { SentenceStructureBuilder } from '../sentence-model-builder';
import { BaseManipulator } from './base-manipulator';
export class TextCaseManipulator extends BaseManipulator {
applyUpperCase(layoutFormatterManager, selection, subDocument, interval) {
return (new UpperCaseModifier(layoutFormatterManager, selection, subDocument, this.modelManipulator, interval)).modify();
}
applyLowerCase(layoutFormatterManager, selection, subDocument, interval) {
return (new LowerCaseModifier(layoutFormatterManager, selection, subDocument, this.modelManipulator, interval)).modify();
}
applyCapitalizeEachWordCase(layoutFormatterManager, selection, subDocument, interval) {
return (new CapitalizeEachWordCaseModifier(layoutFormatterManager, selection, subDocument, this.modelManipulator, interval)).modify();
}
applyToggleCase(layoutFormatterManager, selection, subDocument, interval) {
return (new ToggleCaseModifier(layoutFormatterManager, selection, subDocument, this.modelManipulator, interval)).modify();
}
applySentenceCase(layoutFormatterManager, selection, subDocument, interval) {
return (new SentenceCaseModifier(layoutFormatterManager, selection, subDocument, this.modelManipulator, interval)).modify();
}
applyBufferState(subDocument, oldState) {
var chunks = subDocument.chunks;
for (var i = 0, stateValue; stateValue = oldState.objects[i]; i++) {
var oldText = stateValue.value;
var oldTextPosition = stateValue.interval.start;
var chunkIndex = SearchUtils.normedInterpolationIndexOf(chunks, (c) => c.startLogPosition.value, oldTextPosition);
for (var chunk; oldText.length > 0 && (chunk = chunks[chunkIndex]); chunkIndex++) {
var currPosForInsertInThisChunk = oldTextPosition - chunk.startLogPosition.value;
var chunkTextBefore = chunk.textBuffer.substr(0, currPosForInsertInThisChunk);
var chunkTextAfter = chunk.textBuffer.substr(currPosForInsertInThisChunk + oldText.length);
var lengthInsertedText = chunk.textBuffer.length - currPosForInsertInThisChunk - chunkTextAfter.length;
chunk.textBuffer = [chunkTextBefore, oldText.substr(0, lengthInsertedText), chunkTextAfter].join("");
oldTextPosition += lengthInsertedText;
oldText = oldText.substr(lengthInsertedText);
}
}
if (!oldState.isEmpty())
this.modelManipulator.notifyModelChanged(new TextBufferChangedSubDocumentChange(subDocument.id, oldState));
}
}
class TextCaseModifierBase {
constructor(layoutFormatterManager, selection, subDocument, modelManipulator, interval) {
this.layoutFormatterManager = layoutFormatterManager;
this.selection = selection;
this.subDocument = subDocument;
this.interval = interval;
this.modelManipulator = modelManipulator;
}
modify() {
this.newState = new HistoryItemIntervalState();
this.oldState = new HistoryItemIntervalState();
var sentences = SentenceStructureBuilder.getBuilder(this.layoutFormatterManager, this.selection, this.subDocument, this.interval, true).sentences;
for (var sentenceIndex = 0, sentence; sentence = sentences[sentenceIndex]; sentenceIndex++)
this.modifyCore(sentence);
this.modelManipulator.textCase.applyBufferState(this.subDocument, this.newState);
if (!this.newState.isEmpty())
this.modelManipulator.notifyModelChanged(new TextBufferChangedSubDocumentChange(this.subDocument.id, this.newState));
return this.oldState;
}
}
class TextCaseSimpleModifier extends TextCaseModifierBase {
modifyCore(sentence) {
for (var wordIndex = 0, wordInfo; wordInfo = sentence.words[wordIndex]; wordIndex++) {
for (var wordPartIndex = 0, wordPart; wordPart = wordInfo.parts[wordPartIndex]; wordPartIndex++) {
if (wordPart.position < this.interval.start)
continue;
if (wordPart.position >= this.interval.end)
return;
switch (wordPart.type) {
case LayoutBoxType.Text:
this.oldState.register(new HistoryItemTextBufferStateObject(wordPart.position, wordPart.text));
var newText = this.applyModifier(wordIndex, wordPartIndex, wordPart.text);
this.newState.register(new HistoryItemTextBufferStateObject(wordPart.position, newText));
break;
}
}
}
}
}
class LowerCaseModifier extends TextCaseSimpleModifier {
applyModifier(_wordIndex, _wordPartIndex, text) {
return text.toLowerCase();
}
}
class UpperCaseModifier extends TextCaseSimpleModifier {
applyModifier(_wordIndex, _wordPartIndex, text) {
return text.toUpperCase();
}
}
class CapitalizeEachWordCaseModifier extends TextCaseSimpleModifier {
applyModifier(_wordIndex, wordPartIndex, text) {
return wordPartIndex == 0 ? text[0].toUpperCase() + text.substr(1).toLowerCase() : text.toLowerCase();
}
}
class ToggleCaseModifier extends TextCaseSimpleModifier {
applyModifier(_wordIndex, _wordPartIndex, text) {
var result = "";
for (var i = 0; i < text.length; i++) {
var char = text[i];
var lowerChar = char.toLowerCase();
result += lowerChar === char ? char.toUpperCase() : lowerChar;
}
return result;
}
}
class SentenceCaseModifier extends TextCaseSimpleModifier {
applyModifier(wordIndex, wordPartIndex, text) {
return wordIndex == 0 && wordPartIndex == 0 ? text[0].toUpperCase() + text.substr(1).toLowerCase() : text.toLowerCase();
}
}