import { contains, isArrayEqual } from 'mobiledoc-kit/utils/array-utils';
import Range from 'mobiledoc-kit/utils/cursor/range';
/**
* Used by {@link Editor} to manage its current state (cursor, active markups
* and active sections).
* @private
*/
class EditState {
constructor(editor) {
this.editor = editor;
let defaultState = {
range: Range.blankRange(),
activeMarkups: [],
activeSections: [],
activeSectionTagNames: []
};
this.prevState = this.state = defaultState;
}
updateRange(newRange) {
this.prevState = this.state;
this.state = this._readState(newRange);
}
destroy() {
this.editor = null;
this.prevState = this.state = null;
}
/**
* @return {Boolean}
*/
rangeDidChange() {
let { state: { range } , prevState: {range: prevRange} } = this;
return !prevRange.isEqual(range);
}
/**
* @return {Boolean} Whether the input mode (active markups or active section tag names)
* has changed.
*/
inputModeDidChange() {
let { state, prevState } = this;
return (!isArrayEqual(state.activeMarkups, prevState.activeMarkups) ||
!isArrayEqual(state.activeSectionTagNames, prevState.activeSectionTagNames));
}
/**
* @return {Range}
*/
get range() {
return this.state.range;
}
/**
* @return {Section[]}
*/
get activeSections() {
return this.state.activeSections;
}
/**
* @return {Markup[]}
*/
get activeMarkups() {
return this.state.activeMarkups;
}
/**
* Update the editor's markup state. This is used when, e.g.,
* a user types meta+B when the editor has a cursor but no selected text;
* in this case the editor needs to track that it has an active "b" markup
* and apply it to the next text the user types.
*/
toggleMarkupState(markup) {
if (contains(this.activeMarkups, markup)) {
this._removeActiveMarkup(markup);
} else {
this._addActiveMarkup(markup);
}
}
_readState(range) {
let state = {
range,
activeMarkups: this._readActiveMarkups(range),
activeSections: this._readActiveSections(range)
};
// Section objects are 'live', so to check that they changed, we
// need to map their tagNames now (and compare to mapped tagNames later).
// In addition, to catch changes from ul -> ol, we keep track of the
// un-nested tag names (otherwise we'd only see li -> li change)
state.activeSectionTagNames = state.activeSections.map(s => {
return s.isNested ? s.parent.tagName : s.tagName;
});
return state;
}
_readActiveSections(range) {
let { head, tail } = range;
let { editor: { post } } = this;
if (range.isBlank) {
return [];
} else {
return post.sections.readRange(head.section, tail.section);
}
}
_readActiveMarkups(range) {
let { editor: { post } } = this;
return post.markupsInRange(range);
}
_removeActiveMarkup(markup) {
let index = this.state.activeMarkups.indexOf(markup);
this.state.activeMarkups.splice(index, 1);
}
_addActiveMarkup(markup) {
this.state.activeMarkups.push(markup);
}
}
export default EditState;