suneditor
Version:
Pure JavaScript based WYSIWYG web editor
233 lines (193 loc) • 10 kB
JavaScript
/*
* wysiwyg web editor
*
* suneditor.js
* Copyright 2017 JiHong Lee.
* MIT license.
*/
'use strict';
export default {
name: 'list',
add: function (core, targetElement) {
const context = core.context;
context.list = {
_list: null,
currentList: ''
};
/** set submenu */
let listDiv = this.setSubmenu.call(core);
let listUl = listDiv.querySelector('ul');
/** add event listeners */
listUl.addEventListener('click', this.pickup.bind(core));
context.list._list = listUl.querySelectorAll('li button');
/** append html */
targetElement.parentNode.appendChild(listDiv);
/** empty memory */
listDiv = null, listUl = null;
},
setSubmenu: function () {
const lang = this.lang;
const listDiv = this.util.createElement('DIV');
listDiv.className = 'se-submenu se-list-layer';
listDiv.innerHTML = '' +
'<div class="se-list-inner">' +
'<ul class="se-list-basic">' +
'<li><button type="button" class="se-btn-list se-tooltip" data-command="OL">' +
'<i class="se-icon-list-number"></i>' +
'<span class="se-tooltip-inner"><span class="se-tooltip-text">' + lang.toolbar.orderList + '</span></span>' +
'</button></li>' +
'<li><button type="button" class="se-btn-list se-tooltip" data-command="UL">' +
'<i class="se-icon-list-bullets"></i>' +
'<span class="se-tooltip-inner"><span class="se-tooltip-text">' + lang.toolbar.unorderList + '</span></span>' +
'</button></li>' +
'</ul>' +
'</div>';
return listDiv;
},
on: function () {
const listContext = this.context.list;
const list = listContext._list;
const currentList = this.commandMap.LI.getAttribute('data-focus') || '';
if (currentList !== listContext.currentList) {
for (let i = 0, len = list.length; i < len; i++) {
if (currentList === list[i].getAttribute('data-command')) {
this.util.addClass(list[i], 'active');
} else {
this.util.removeClass(list[i], 'active');
}
}
listContext.currentList = currentList;
}
},
pickup: function (e) {
e.preventDefault();
e.stopPropagation();
let target = e.target;
let command = '';
while (!command && !/^UL$/i.test(target.tagName)) {
command = target.getAttribute('data-command');
target = target.parentNode;
}
if (!command) return;
const selectedFormsts = this.getSelectedElementsAndComponents();
if (!selectedFormsts || selectedFormsts.length === 0) return;
let isRemove = true;
let edgeFirst = null;
let edgeLast = null;
// merge
const firstSel = selectedFormsts[0];
const lastSel = selectedFormsts[selectedFormsts.length - 1];
let topEl = (this.util.isListCell(firstSel) || this.util.isComponent(firstSel)) && !firstSel.previousElementSibling ? firstSel.parentNode.previousElementSibling : firstSel.previousElementSibling;
let bottomEl = (this.util.isListCell(lastSel) || this.util.isComponent(lastSel)) && !lastSel.nextElementSibling ? lastSel.parentNode.nextElementSibling : lastSel.nextElementSibling;
for (let i = 0, len = selectedFormsts.length; i < len; i++) {
if (!this.util.isList(this.util.getRangeFormatElement(selectedFormsts[i], function (current) {
return this.getRangeFormatElement(current) && current !== selectedFormsts[i];
}.bind(this.util)))) {
isRemove = false;
break;
}
}
if (isRemove && (!topEl || command !== topEl.tagName) && (!bottomEl || command !== bottomEl.tagName)) {
const currentFormat = this.util.getRangeFormatElement(this.getSelectionNode());
const cancel = currentFormat && currentFormat.tagName === command;
let rangeArr, tempList;
const passComponent = function (current) {
return !this.isComponent(current);
}.bind(this.util);
if (!cancel) tempList = this.util.createElement(command);
for (let i = 0, len = selectedFormsts.length, r, o; i < len; i++) {
o = this.util.getRangeFormatElement(selectedFormsts[i], passComponent);
if (!o || !this.util.isList(o)) continue;
if (!r) {
r = o;
rangeArr = {r: r, f: [this.util.getParentElement(selectedFormsts[i], 'LI')]};
} else {
if (r !== o) {
const edge = this.detachRangeFormatElement(rangeArr.r, rangeArr.f, tempList, false, true);
if (!edgeFirst) edgeFirst = edge;
if (!cancel) tempList = this.util.createElement(command);
r = o;
rangeArr = {r: r, f: [this.util.getParentElement(selectedFormsts[i], 'LI')]};
} else {
rangeArr.f.push(this.util.getParentElement(selectedFormsts[i], 'LI'));
}
}
if (i === len - 1) {
edgeLast = this.detachRangeFormatElement(rangeArr.r, rangeArr.f, tempList, false, true);
if (!edgeFirst) edgeFirst = edgeLast;
}
}
} else {
const topElParent = topEl ? topEl.parentNode : topEl;
const bottomElParent = bottomEl ? bottomEl.parentNode : bottomEl;
topEl = topElParent && !this.util.isWysiwygDiv(topElParent) && topElParent.nodeName === command ? topElParent : topEl;
bottomEl = bottomElParent && !this.util.isWysiwygDiv(bottomElParent) && bottomElParent.nodeName === command ? bottomElParent : bottomEl;
const mergeTop = topEl && topEl.tagName === command;
const mergeBottom = bottomEl && bottomEl.tagName === command;
let list = mergeTop ? topEl : this.util.createElement(command);
let firstList = null;
let lastList = null;
let topNumber = null;
let bottomNumber = null;
const passComponent = function (current) {
return !this.isComponent(current) && !this.isList(current);
}.bind(this.util);
for (let i = 0, len = selectedFormsts.length, newCell, fTag, isCell, next, originParent, nextParent, parentTag, siblingTag, rangeTag; i < len; i++) {
fTag = selectedFormsts[i];
if (fTag.childNodes.length === 0 && !this.util.isIgnoreNodeChange(fTag)) {
this.util.removeItem(fTag);
continue;
}
next = selectedFormsts[i + 1];
originParent = fTag.parentNode;
nextParent = next ? next.parentNode : null;
isCell = this.util.isListCell(fTag);
rangeTag = this.util.isRangeFormatElement(originParent) ? originParent : null;
parentTag = isCell && !this.util.isWysiwygDiv(originParent) ? originParent.parentNode : originParent;
siblingTag = isCell && !this.util.isWysiwygDiv(originParent) ? !next ? originParent : originParent.nextSibling : fTag.nextSibling;
newCell = this.util.createElement('LI');
this.util.copyFormatAttributes(newCell, fTag);
if (this.util.isComponent(fTag)) {
const isHR = /^HR$/i.test(fTag.nodeName);
if (!isHR) newCell.innerHTML = '<br>';
newCell.innerHTML += fTag.outerHTML;
if (isHR) newCell.innerHTML += '<br>';
} else {
newCell.innerHTML = fTag.innerHTML;
}
list.appendChild(newCell);
if (!next) lastList = list;
if (!next || parentTag !== nextParent || this.util.isRangeFormatElement(siblingTag)) {
if (!firstList) firstList = list;
if ((!mergeTop || !next || parentTag !== nextParent) && !(next && this.util.isList(nextParent) && nextParent === originParent)) {
if (list.parentNode !== parentTag) parentTag.insertBefore(list, siblingTag);
}
}
this.util.removeItem(fTag);
if (mergeTop && topNumber === null) topNumber = list.children.length - 1;
if (next && this.util.getRangeFormatElement(nextParent, passComponent) !== this.util.getRangeFormatElement(originParent, passComponent)) {
list = this.util.createElement(command);
}
if (rangeTag && rangeTag.children.length === 0) this.util.removeItem(rangeTag);
}
if (topNumber) {
firstList = firstList.children[topNumber];
}
if (mergeBottom) {
bottomNumber = list.children.length - 1;
list.innerHTML += bottomEl.innerHTML;
lastList = list.children[bottomNumber];
this.util.removeItem(bottomEl);
}
edgeFirst = edgeLast = this.util.getEdgeChildNodes(firstList.firstChild, lastList.lastChild);
}
if (selectedFormsts.length > 1) {
this.setRange(edgeFirst.sc, 0, edgeLast.ec, edgeLast.ec.textContent.length);
} else {
this.setRange(edgeFirst.ec, edgeFirst.ec.textContent.length, edgeLast.ec, edgeLast.ec.textContent.length);
}
this.submenuOff();
// history stack
this.history.push(false);
}
};