cl-editor
Version:
Lightweight text editor built with svelte + typescript
180 lines (152 loc) • 5.12 kB
JavaScript
let t = {};
export const exec = (command, value = null) => {
document.execCommand(command, false, value)
}
export const getTagsRecursive = (element, tags) => {
tags = tags || (element && element.tagName ? [element.tagName] : []);
if (element && element.parentNode) {
element = element.parentNode;
} else {
return tags;
}
const tag = element.tagName;
if (element.style && element.getAttribute) {
[element.style.textAlign || element.getAttribute('align'), element.style.color || tag === 'FONT' && 'forecolor', element.style.backgroundColor && 'backcolor']
.filter((item) => item)
.forEach((item) => tags.push(item));
}
if (tag === 'DIV') {
return tags;
}
tags.push(tag);
return getTagsRecursive(element, tags).filter((_tag) => _tag != null);
}
export const saveRange = (editor) => {
const documentSelection = document.getSelection();
t.range = null;
if (documentSelection.rangeCount) {
let savedRange = t.range = documentSelection.getRangeAt(0);
let range = document.createRange();
let rangeStart;
range.selectNodeContents(editor);
range.setEnd(savedRange.startContainer, savedRange.startOffset);
rangeStart = (range + '').length;
t.metaRange = {
start: rangeStart,
end: rangeStart + (savedRange + '').length
};
}
}
export const restoreRange = (editor) => {
let metaRange = t.metaRange;
let savedRange = t.range;
let documentSelection = document.getSelection();
let range;
if (!savedRange) {
return;
}
if (metaRange && metaRange.start !== metaRange.end) { // Algorithm from http://jsfiddle.net/WeWy7/3/
let charIndex = 0,
nodeStack = [editor],
node,
foundStart = false,
stop = false;
range = document.createRange();
while (!stop && (node = nodeStack.pop())) {
if (node.nodeType === 3) {
let nextCharIndex = charIndex + node.length;
if (!foundStart && metaRange.start >= charIndex && metaRange.start <= nextCharIndex) {
range.setStart(node, metaRange.start - charIndex);
foundStart = true;
}
if (foundStart && metaRange.end >= charIndex && metaRange.end <= nextCharIndex) {
range.setEnd(node, metaRange.end - charIndex);
stop = true;
}
charIndex = nextCharIndex;
} else {
let cn = node.childNodes;
let i = cn.length;
while (i > 0) {
i -= 1;
nodeStack.push(cn[i]);
}
}
}
}
documentSelection.removeAllRanges();
documentSelection.addRange(range || savedRange);
}
export const cleanHtml = (input) => {
const html = input.match(/<!--StartFragment-->(.*?)<!--EndFragment-->/);
let output = html && html[1] || input;
output = output
.replace(/\r?\n|\r/g, ' ')
.replace(/<!--(.*?)-->/g, '')
.replace(new RegExp('<(/)*(meta|link|span|\\?xml:|st1:|o:|font|w:sdt)(.*?)>', 'gi'), '')
.replace(/<!\[if !supportLists\]>(.*?)<!\[endif\]>/gi, '')
.replace(/style="[^"]*"/gi, '')
.replace(/style='[^']*'/gi, '')
.replace(/ /gi, ' ')
.replace(/>(\s+)</g, '><')
.replace(/class="[^"]*"/gi, '')
.replace(/class='[^']*'/gi, '')
.replace(/<[^/].*?>/g, i => i.split(/[ >]/g)[0] + '>')
.trim()
output = removeBadTags(output);
return output;
}
export const unwrap = (wrapper) => {
const docFrag = document.createDocumentFragment();
while (wrapper.firstChild) {
const child = wrapper.removeChild(wrapper.firstChild);
docFrag.appendChild(child);
}
// replace wrapper with document fragment
wrapper.parentNode.replaceChild(docFrag, wrapper);
}
export const removeBlockTagsRecursive = (elements, tagsToRemove) => {
Array.from(elements).forEach((item) => {
if (tagsToRemove.some((tag) => tag === item.tagName.toLowerCase())) {
if (item.children.length) {
removeBlockTagsRecursive(item.children, tagsToRemove);
}
unwrap(item);
}
});
}
export const getActionBtns = (actions) => {
return Object.keys(actions).map((action) => actions[action]);
}
export const getNewActionObj = (actions, userActions = []) => {
if (userActions && userActions.length) {
const newActions = {};
userActions.forEach((action) => {
if (typeof action === 'string') {
newActions[action] = Object.assign({}, actions[action]);
} else if (actions[action.name]) {
newActions[action.name] = Object.assign(actions[action.name], action);
} else {
newActions[action.name] = Object.assign({}, action);
}
});
return newActions;
} else {
return actions;
}
}
export const removeBadTags = (html) => {
['style', 'script', 'applet', 'embed', 'noframes', 'noscript'].forEach((badTag) => {
html = html.replace(new RegExp(`<${badTag}.*?${badTag}(.*?)>`, 'gi'), '')
});
return html;
}
export const isEditorClick = (target, editorWrapper) => {
if (target === editorWrapper) {
return true;
}
if (target.parentElement) {
return isEditorClick(target.parentElement, editorWrapper);
}
return false;
}