roosterjs
Version:
A simple facade for all roosterjs code
1,135 lines (1,037 loc) • 687 kB
JavaScript
var roosterjs =
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ }
/******/ };
/******/
/******/ // define __esModule on exports
/******/ __webpack_require__.r = function(exports) {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/
/******/ // create a fake namespace object
/******/ // mode & 1: value is a module id, require it
/******/ // mode & 2: merge all properties of value into the ns
/******/ // mode & 4: return value when already ns object
/******/ // mode & 8|1: behave like require
/******/ __webpack_require__.t = function(value, mode) {
/******/ if(mode & 1) value = __webpack_require__(value);
/******/ if(mode & 8) return value;
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ return ns;
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = "./packages/roosterjs/lib/index.ts");
/******/ })
/************************************************************************/
/******/ ({
/***/ "./packages/roosterjs-editor-api/lib/experiment/experimentSetIndentation.ts":
/*!**********************************************************************************!*\
!*** ./packages/roosterjs-editor-api/lib/experiment/experimentSetIndentation.ts ***!
\**********************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var blockFormat_1 = __webpack_require__(/*! ../utils/blockFormat */ "./packages/roosterjs-editor-api/lib/utils/blockFormat.ts");
var roosterjs_editor_dom_1 = __webpack_require__(/*! roosterjs-editor-dom */ "./packages/roosterjs-editor-dom/lib/index.ts");
var roosterjs_editor_dom_2 = __webpack_require__(/*! roosterjs-editor-dom */ "./packages/roosterjs-editor-dom/lib/index.ts");
var BlockWrapper = '<blockquote style="margin-top:0;margin-bottom:0"></blockquote>';
/**
* @internal
*/
function experimentSetIndentation(editor, indentation) {
var handler = indentation == 0 /* Increase */ ? indent : outdent;
blockFormat_1.default(editor, function (region, start, end) {
var blocks = roosterjs_editor_dom_1.getSelectedBlockElementsInRegion(region);
var blockGroups = [[]];
for (var i = 0; i < blocks.length; i++) {
var startNode = blocks[i].getStartNode();
var vList = roosterjs_editor_dom_2.createVListFromRegion(region, true /*includeSiblingLists*/, startNode);
if (vList) {
blockGroups.push([]);
while (blocks[i + 1] && vList.contains(blocks[i + 1].getStartNode())) {
i++;
}
vList.setIndentation(start, end, indentation);
vList.writeBack();
}
else {
blockGroups[blockGroups.length - 1].push(blocks[i]);
}
}
blockGroups.forEach(function (group) { return handler(region, group); });
});
}
exports.default = experimentSetIndentation;
function indent(region, blocks) {
if (blocks.length > 0) {
var startNode = blocks[0].getStartNode();
var endNode = blocks[blocks.length - 1].getEndNode();
var nodes = roosterjs_editor_dom_2.collapseNodesInRegion(region, [startNode, endNode]);
roosterjs_editor_dom_2.wrap(nodes, BlockWrapper);
}
}
function outdent(region, blocks) {
blocks.forEach(function (blockElement) {
var node = blockElement.collapseToSingleElement();
var quote = roosterjs_editor_dom_2.findClosestElementAncestor(node, region.rootNode, 'blockquote');
if (quote) {
if (node == quote) {
node = roosterjs_editor_dom_2.wrap(roosterjs_editor_dom_2.toArray(node.childNodes));
}
while (roosterjs_editor_dom_2.isNodeInRegion(region, node) && roosterjs_editor_dom_2.getTagOfNode(node) != 'BLOCKQUOTE') {
node = roosterjs_editor_dom_2.splitBalancedNodeRange(node);
}
if (roosterjs_editor_dom_2.isNodeInRegion(region, node)) {
roosterjs_editor_dom_2.unwrap(node);
}
}
});
}
/***/ }),
/***/ "./packages/roosterjs-editor-api/lib/experiment/experimentToggleListType.ts":
/*!**********************************************************************************!*\
!*** ./packages/roosterjs-editor-api/lib/experiment/experimentToggleListType.ts ***!
\**********************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var blockFormat_1 = __webpack_require__(/*! ../utils/blockFormat */ "./packages/roosterjs-editor-api/lib/utils/blockFormat.ts");
var roosterjs_editor_dom_1 = __webpack_require__(/*! roosterjs-editor-dom */ "./packages/roosterjs-editor-dom/lib/index.ts");
/**
* @internal
*/
function experimentToggleListType(editor, listType) {
blockFormat_1.default(editor, function (region, start, end) {
var vList = roosterjs_editor_dom_1.createVListFromRegion(region, true /*includeSiblingLists*/);
if (vList) {
vList.changeListType(start, end, listType);
vList.writeBack();
}
});
}
exports.default = experimentToggleListType;
/***/ }),
/***/ "./packages/roosterjs-editor-api/lib/format/changeFontSize.ts":
/*!********************************************************************!*\
!*** ./packages/roosterjs-editor-api/lib/format/changeFontSize.ts ***!
\********************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var applyInlineStyle_1 = __webpack_require__(/*! ../utils/applyInlineStyle */ "./packages/roosterjs-editor-api/lib/utils/applyInlineStyle.ts");
var roosterjs_editor_dom_1 = __webpack_require__(/*! roosterjs-editor-dom */ "./packages/roosterjs-editor-dom/lib/index.ts");
/**
* Default font size sequence, in pt. Suggest editor UI use this sequence as your font size list,
* So that when increase/decrease font size, the font size can match the sequence of your font size picker
*/
exports.FONT_SIZES = [8, 9, 10, 11, 12, 14, 16, 18, 20, 22, 24, 26, 28, 36, 48, 72];
var MIN_FONT_SIZE = 1;
var MAX_FONT_SIZE = 1000;
/**
* Increase or decrease font size in selection
* @param editor The editor instance
* @param change Whether increase or decrease font size
* @param fontSizes A sorted font size array, in pt. Default value is FONT_SIZES
*/
function changeFontSize(editor, change, fontSizes) {
if (fontSizes === void 0) { fontSizes = exports.FONT_SIZES; }
var changeBase = change == 0 /* Increase */ ? 1 : -1;
applyInlineStyle_1.default(editor, function (element) {
var pt = parseFloat(roosterjs_editor_dom_1.getComputedStyle(element, 'font-size'));
element.style.fontSize = getNewFontSize(pt, changeBase, fontSizes) + 'pt';
var lineHeight = roosterjs_editor_dom_1.getComputedStyle(element, 'line-height');
if (lineHeight != 'normal') {
element.style.lineHeight = 'normal';
}
});
}
exports.default = changeFontSize;
function getNewFontSize(pt, changeBase, fontSizes) {
pt = changeBase == 1 ? Math.floor(pt) : Math.ceil(pt);
var last = fontSizes[fontSizes.length - 1];
if (pt <= fontSizes[0]) {
pt = Math.max(pt + changeBase, MIN_FONT_SIZE);
}
else if (pt > last || (pt == last && changeBase == 1)) {
pt = pt / 10;
pt = changeBase == 1 ? Math.floor(pt) : Math.ceil(pt);
pt = Math.min(Math.max((pt + changeBase) * 10, last), MAX_FONT_SIZE);
}
else if (changeBase == 1) {
for (var i = 0; i < fontSizes.length; i++) {
if (pt < fontSizes[i]) {
pt = fontSizes[i];
break;
}
}
}
else {
for (var i = fontSizes.length - 1; i >= 0; i--) {
if (pt > fontSizes[i]) {
pt = fontSizes[i];
break;
}
}
}
return pt;
}
exports.getNewFontSize = getNewFontSize;
/***/ }),
/***/ "./packages/roosterjs-editor-api/lib/format/clearBlockFormat.ts":
/*!**********************************************************************!*\
!*** ./packages/roosterjs-editor-api/lib/format/clearBlockFormat.ts ***!
\**********************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var collapseSelectedBlocks_1 = __webpack_require__(/*! ../utils/collapseSelectedBlocks */ "./packages/roosterjs-editor-api/lib/utils/collapseSelectedBlocks.ts");
var roosterjs_editor_dom_1 = __webpack_require__(/*! roosterjs-editor-dom */ "./packages/roosterjs-editor-dom/lib/index.ts");
exports.TAGS_TO_UNWRAP = 'B,I,U,STRONG,EM,SUB,SUP,STRIKE,FONT,CENTER,H1,H2,H3,H4,H5,H6,UL,OL,LI,SPAN,P,BLOCKQUOTE,CODE,S,PRE'.split(',');
exports.TAGS_TO_STOP_UNWRAP = ['TD', 'TH', 'TR', 'TABLE', 'TBODY', 'THEAD'];
exports.ATTRIBUTES_TO_PRESERVE = ['href'];
/**
* Clear all formats of selected blocks.
* When selection is collapsed, only clear format of current block.
* @param editor The editor instance
* @param tagsToUnwrap Optional. A string array contains HTML tags in upper case which we will unwrap when clear format
* @param tagsToStopUnwrap Optional. A string array contains HTML tags in upper case which we will stop unwrap if these tags are hit
*/
function clearBlockFormat(editor, tagsToUnwrap, tagsToStopUnwrap, attributesToPreserve) {
if (tagsToUnwrap === void 0) { tagsToUnwrap = exports.TAGS_TO_UNWRAP; }
if (tagsToStopUnwrap === void 0) { tagsToStopUnwrap = exports.TAGS_TO_STOP_UNWRAP; }
if (attributesToPreserve === void 0) { attributesToPreserve = exports.ATTRIBUTES_TO_PRESERVE; }
editor.focus();
editor.addUndoSnapshot(function (start, end) {
var groups = [{}];
var stopUnwrapSelector = tagsToStopUnwrap.join(',');
// 1. Collapse the selected blocks and get first and last element
collapseSelectedBlocks_1.default(editor, function (element) {
var group = groups[groups.length - 1];
var td = editor.getElementAtCursor(stopUnwrapSelector, element);
if (td != group.td && group.first) {
groups.push((group = {}));
}
group.td = td;
group.first = group.first || element;
group.last = element;
});
groups
.filter(function (group) { return group.first; })
.forEach(function (group) {
// 2. Collapse with first and last element to make them under same parent
var nodes = editor.collapseNodes(group.first, group.last, true /*canSplitParent*/);
// 3. Continue collapse until we can't collapse any more (hit root node, or a table)
if (canCollapse(tagsToStopUnwrap, nodes[0])) {
while (editor.contains(nodes[0].parentNode) &&
canCollapse(tagsToStopUnwrap, nodes[0].parentNode)) {
nodes = [roosterjs_editor_dom_1.splitBalancedNodeRange(nodes)];
}
}
// 4. Clear formats of the nodes
nodes.forEach(function (node) {
return clearNodeFormat(node, tagsToUnwrap, tagsToStopUnwrap, attributesToPreserve);
});
// 5. Clear CSS of container TD if exist
if (group.td) {
var styles = group.td.getAttribute('style') || '';
var styleArray = styles.split(';');
styleArray = styleArray.filter(function (style) { return style.trim().toLowerCase().indexOf('border') == 0; });
styles = styleArray.join(';');
if (styles) {
group.td.setAttribute('style', styles);
}
else {
group.td.removeAttribute('style');
}
}
});
editor.select(start, end);
}, "Format" /* Format */);
}
exports.default = clearBlockFormat;
function clearNodeFormat(node, tagsToUnwrap, tagsToStopUnwrap, attributesToPreserve) {
if (node.nodeType != 1 /* Element */ || roosterjs_editor_dom_1.getTagOfNode(node) == 'BR') {
return false;
}
// 1. Recursively clear format of all its child nodes
var allChildrenAreBlock = roosterjs_editor_dom_1.toArray(node.childNodes)
.map(function (n) { return clearNodeFormat(n, tagsToUnwrap, tagsToStopUnwrap, attributesToPreserve); })
.reduce(function (previousValue, value) { return previousValue && value; }, true);
if (!canCollapse(tagsToStopUnwrap, node)) {
return false;
}
var returnBlockElement = roosterjs_editor_dom_1.isBlockElement(node);
// 2. If we should unwrap this tag, put it into an array and unwrap it later
if (tagsToUnwrap.indexOf(roosterjs_editor_dom_1.getTagOfNode(node)) >= 0 || allChildrenAreBlock) {
if (returnBlockElement && !allChildrenAreBlock) {
roosterjs_editor_dom_1.wrap(node);
}
roosterjs_editor_dom_1.unwrap(node);
}
else {
// 3. Otherwise, remove all attributes
clearAttribute(node, attributesToPreserve);
}
return returnBlockElement;
}
function clearAttribute(element, attributesToPreserve) {
for (var _i = 0, _a = roosterjs_editor_dom_1.toArray(element.attributes); _i < _a.length; _i++) {
var attr = _a[_i];
if (attributesToPreserve.indexOf(attr.name.toLowerCase()) < 0 &&
attr.name.indexOf('data-') != 0) {
element.removeAttribute(attr.name);
}
}
}
function canCollapse(tagsToStopUnwrap, node) {
return tagsToStopUnwrap.indexOf(roosterjs_editor_dom_1.getTagOfNode(node)) < 0;
}
/***/ }),
/***/ "./packages/roosterjs-editor-api/lib/format/clearFormat.ts":
/*!*****************************************************************!*\
!*** ./packages/roosterjs-editor-api/lib/format/clearFormat.ts ***!
\*****************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var execCommand_1 = __webpack_require__(/*! ../utils/execCommand */ "./packages/roosterjs-editor-api/lib/utils/execCommand.ts");
var setBackgroundColor_1 = __webpack_require__(/*! ./setBackgroundColor */ "./packages/roosterjs-editor-api/lib/format/setBackgroundColor.ts");
var setFontName_1 = __webpack_require__(/*! ./setFontName */ "./packages/roosterjs-editor-api/lib/format/setFontName.ts");
var setFontSize_1 = __webpack_require__(/*! ./setFontSize */ "./packages/roosterjs-editor-api/lib/format/setFontSize.ts");
var setTextColor_1 = __webpack_require__(/*! ./setTextColor */ "./packages/roosterjs-editor-api/lib/format/setTextColor.ts");
var toggleBold_1 = __webpack_require__(/*! ./toggleBold */ "./packages/roosterjs-editor-api/lib/format/toggleBold.ts");
var toggleItalic_1 = __webpack_require__(/*! ./toggleItalic */ "./packages/roosterjs-editor-api/lib/format/toggleItalic.ts");
var toggleUnderline_1 = __webpack_require__(/*! ./toggleUnderline */ "./packages/roosterjs-editor-api/lib/format/toggleUnderline.ts");
var STYLES_TO_REMOVE = ['font', 'text-decoration', 'color', 'background'];
/**
* Clear the format in current selection, after cleaning, the format will be
* changed to default format. The format that get cleaned include B/I/U/font name/
* font size/text color/background color/align left/align right/align center/superscript/subscript
* @param editor The editor instance
*/
function clearFormat(editor) {
editor.focus();
editor.addUndoSnapshot(function () {
execCommand_1.default(editor, "removeFormat" /* RemoveFormat */);
editor.queryElements('[class]', 1 /* OnSelection */, function (node) {
return node.removeAttribute('class');
});
var defaultFormat = editor.getDefaultFormat();
var isDefaultFormatEmpty = Object.keys(defaultFormat).length === 0;
editor.queryElements('[style]', 2 /* InSelection */, function (node) {
STYLES_TO_REMOVE.forEach(function (style) { return node.style.removeProperty(style); });
// when default format is empty, keep the HTML minimum by removing style attribute if there's no style
// (note: because default format is empty, we're not adding style back in)
if (isDefaultFormatEmpty && node.getAttribute('style') === '') {
node.removeAttribute('style');
}
});
if (!isDefaultFormatEmpty) {
if (defaultFormat.fontFamily) {
setFontName_1.default(editor, defaultFormat.fontFamily);
}
if (defaultFormat.fontSize) {
setFontSize_1.default(editor, defaultFormat.fontSize);
}
if (defaultFormat.textColor) {
if (defaultFormat.textColors) {
setTextColor_1.default(editor, defaultFormat.textColors);
}
else {
setTextColor_1.default(editor, defaultFormat.textColor);
}
}
if (defaultFormat.backgroundColor) {
if (defaultFormat.backgroundColors) {
setBackgroundColor_1.default(editor, defaultFormat.backgroundColors);
}
else {
setBackgroundColor_1.default(editor, defaultFormat.backgroundColor);
}
}
if (defaultFormat.bold) {
toggleBold_1.default(editor);
}
if (defaultFormat.italic) {
toggleItalic_1.default(editor);
}
if (defaultFormat.underline) {
toggleUnderline_1.default(editor);
}
}
}, "Format" /* Format */);
}
exports.default = clearFormat;
/***/ }),
/***/ "./packages/roosterjs-editor-api/lib/format/createLink.ts":
/*!****************************************************************!*\
!*** ./packages/roosterjs-editor-api/lib/format/createLink.ts ***!
\****************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var roosterjs_editor_dom_1 = __webpack_require__(/*! roosterjs-editor-dom */ "./packages/roosterjs-editor-dom/lib/index.ts");
// Regex matching Uri scheme
var URI_REGEX = /^[a-zA-Z]+:/i;
// Regex matching begin of email address
var MAILTO_REGEX = /^[\w.%+-]+@/i;
// Regex matching begin of ftp, i.e. ftp.microsoft.com
var FTP_REGEX = /^ftp\./i;
var TEMP_TITLE = 'istemptitle';
function applyLinkPrefix(url) {
if (!url) {
return url;
}
// Add link prefix per rule:
// (a) if the url always starts with a URI scheme, leave it as it is
// (b) if the url is an email address, xxx@... add mailto: prefix
// (c) if the url starts with ftp., add ftp:// prefix
// (d) rest, add http:// prefix
var prefix = '';
if (url.search(URI_REGEX) < 0) {
if (url.search(MAILTO_REGEX) == 0) {
prefix = 'mailto:';
}
else if (url.search(FTP_REGEX) == 0) {
prefix = 'ftp://';
}
else {
// fallback to http://
prefix = 'http://';
}
}
return prefix + url;
}
/**
* Insert a hyperlink at cursor.
* When there is a selection, hyperlink will be applied to the selection,
* otherwise a hyperlink will be inserted to the cursor position.
* @param editor Editor object
* @param link Link address, can be http(s), mailto, notes, file, unc, ftp, news, telnet, gopher, wais.
* When protocol is not specified, a best matched protocol will be predicted.
* @param altText Optional alt text of the link, will be shown when hover on the link
* @param displayText Optional display text for the link.
* If specified, the display text of link will be replaced with this text.
* If not specified and there wasn't a link, the link url will be used as display text.
*/
function createLink(editor, link, altText, displayText) {
editor.focus();
var url = (checkXss(link) || '').trim();
if (url) {
var linkData = roosterjs_editor_dom_1.matchLink(url);
// matchLink can match most links, but not all, i.e. if you pass link a link as "abc", it won't match
// we know in that case, users will want to insert a link like http://abc
// so we have separate logic in applyLinkPrefix to add link prefix depending on the format of the link
// i.e. if the link starts with something like abc@xxx, we will add mailto: prefix
// if the link starts with ftp.xxx, we will add ftp:// link. For more, see applyLinkPrefix
var normalizedUrl_1 = linkData ? linkData.normalizedUrl : applyLinkPrefix(url);
var originalUrl_1 = linkData ? linkData.originalUrl : url;
editor.addUndoSnapshot(function () {
var range = editor.getSelectionRange();
var anchor = null;
if (range && range.collapsed) {
anchor = getAnchorNodeAtCursor(editor);
// If there is already a link, just change its href
if (anchor) {
anchor.href = normalizedUrl_1;
// Change text content if it is specified
updateAnchorDisplayText(anchor, displayText);
}
else {
anchor = editor.getDocument().createElement('A');
anchor.textContent = displayText || originalUrl_1;
anchor.href = normalizedUrl_1;
editor.insertNode(anchor);
}
}
else {
// the selection is not collapsed, use browser execCommand
editor.getDocument().execCommand("createLink" /* CreateLink */, false, normalizedUrl_1);
anchor = getAnchorNodeAtCursor(editor);
updateAnchorDisplayText(anchor, displayText);
}
if (altText && anchor) {
// Hack: Ideally this should be done by HyperLink plugin.
// We make a hack here since we don't have an event to notify HyperLink plugin
// before we apply the link.
anchor.removeAttribute(TEMP_TITLE);
anchor.title = altText;
}
return anchor;
}, "CreateLink" /* CreateLink */);
}
}
exports.default = createLink;
function getAnchorNodeAtCursor(editor) {
return editor.queryElements('a[href]', 1 /* OnSelection */)[0];
}
function updateAnchorDisplayText(anchor, displayText) {
if (displayText && anchor.textContent != displayText) {
anchor.textContent = displayText;
}
}
function checkXss(link) {
var santizer = new roosterjs_editor_dom_1.HtmlSanitizer();
var doc = new DOMParser().parseFromString('<a></a>', 'text/html');
var a = doc.body.firstChild;
a.href = link || '';
santizer.sanitize(doc.body);
// We use getAttribute because some browsers will try to make the href property a valid link.
// This has unintended side effects when the link lacks a protocol.
return a.getAttribute('href');
}
/***/ }),
/***/ "./packages/roosterjs-editor-api/lib/format/getFormatState.ts":
/*!********************************************************************!*\
!*** ./packages/roosterjs-editor-api/lib/format/getFormatState.ts ***!
\********************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
Object.defineProperty(exports, "__esModule", { value: true });
var roosterjs_editor_core_1 = __webpack_require__(/*! roosterjs-editor-core */ "./packages/roosterjs-editor-core/lib/index.ts");
var roosterjs_editor_dom_1 = __webpack_require__(/*! roosterjs-editor-dom */ "./packages/roosterjs-editor-dom/lib/index.ts");
var roosterjs_editor_dom_2 = __webpack_require__(/*! roosterjs-editor-dom */ "./packages/roosterjs-editor-dom/lib/index.ts");
/**
* Get element based Format State at cursor
* @param editor The editor instance
* @param event (Optional) The plugin event, it stores the event cached data for looking up.
* In this function the event cache is used to get list state and header level. If not passed,
* it will query the node within selection to get the info
* @returns An ElementBasedFormatState object
*/
function getElementBasedFormatState(editor, event) {
var listTag = roosterjs_editor_dom_1.getTagOfNode(roosterjs_editor_core_1.cacheGetElementAtCursor(editor, event, 'OL,UL'));
var headerTag = roosterjs_editor_dom_1.getTagOfNode(roosterjs_editor_core_1.cacheGetElementAtCursor(editor, event, 'H1,H2,H3,H4,H5,H6'));
return {
isBullet: listTag == 'UL',
isNumbering: listTag == 'OL',
headerLevel: (headerTag && parseInt(headerTag[1])) || 0,
canUnlink: !!editor.queryElements('a[href]', 1 /* OnSelection */)[0],
canAddImageAltText: !!editor.queryElements('img', 1 /* OnSelection */)[0],
isBlockQuote: !!editor.queryElements('blockquote', 1 /* OnSelection */)[0],
};
}
exports.getElementBasedFormatState = getElementBasedFormatState;
/**
* Get style based Format State at cursor
* @param editor The editor instance
* @returns A StyleBasedFormatState object
*/
function getStyleBasedFormatState(editor) {
var range = editor.getSelectionRange();
var node = range && roosterjs_editor_dom_1.Position.getStart(range).normalize().node;
var styles = node ? roosterjs_editor_dom_1.getComputedStyles(node) : [];
return {
fontName: styles[0],
fontSize: styles[1],
textColor: styles[2],
backgroundColor: styles[3],
};
}
exports.getStyleBasedFormatState = getStyleBasedFormatState;
/**
* Get format state at cursor
* A format state is a collection of all format related states, e.g.,
* bold, italic, underline, font name, font size, etc.
* @param editor The editor instance
* @param event (Optional) The plugin event, it stores the event cached data for looking up.
* In this function the event cache is used to get list state and header level. If not passed,
* it will query the node within selection to get the info
* @returns The format state at cursor
*/
function getFormatState(editor, event) {
return __assign(__assign(__assign(__assign({}, roosterjs_editor_dom_2.getPendableFormatState(editor.getDocument())), getElementBasedFormatState(editor, event)), getStyleBasedFormatState(editor)), { canUndo: editor.canUndo(), canRedo: editor.canRedo() });
}
exports.default = getFormatState;
/***/ }),
/***/ "./packages/roosterjs-editor-api/lib/format/insertImage.ts":
/*!*****************************************************************!*\
!*** ./packages/roosterjs-editor-api/lib/format/insertImage.ts ***!
\*****************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
function insertImage(editor, imageFile) {
if (typeof imageFile == 'string') {
insertImageWithSrc(editor, imageFile);
}
else {
var reader = new FileReader();
reader.onload = function (event) {
if (!editor.isDisposed()) {
insertImageWithSrc(editor, event.target.result);
}
};
reader.readAsDataURL(imageFile);
}
}
exports.default = insertImage;
function insertImageWithSrc(editor, src) {
editor.addUndoSnapshot(function () {
var image = editor.getDocument().createElement('img');
image.src = src;
image.style.maxWidth = '100%';
editor.insertNode(image);
}, "Format" /* Format */);
}
/***/ }),
/***/ "./packages/roosterjs-editor-api/lib/format/removeLink.ts":
/*!****************************************************************!*\
!*** ./packages/roosterjs-editor-api/lib/format/removeLink.ts ***!
\****************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var roosterjs_editor_dom_1 = __webpack_require__(/*! roosterjs-editor-dom */ "./packages/roosterjs-editor-dom/lib/index.ts");
/**
* Remove link at selection. If no links at selection, do nothing.
* If selection contains multiple links, all of the link styles will be removed.
* If only part of a link is selected, the whole link style will be removed.
* @param editor The editor instance
*/
function removeLink(editor) {
editor.focus();
editor.addUndoSnapshot(function (start, end) {
editor.queryElements('a[href]', 1 /* OnSelection */, roosterjs_editor_dom_1.unwrap);
editor.select(start, end);
}, "Format" /* Format */);
}
exports.default = removeLink;
/***/ }),
/***/ "./packages/roosterjs-editor-api/lib/format/replaceWithNode.ts":
/*!*********************************************************************!*\
!*** ./packages/roosterjs-editor-api/lib/format/replaceWithNode.ts ***!
\*********************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
function replaceWithNode(editor, textOrRange, node, exactMatch, searcher) {
// Make sure the text and node is valid
if (!textOrRange || !node) {
return false;
}
var range;
if (typeof textOrRange == 'string') {
searcher = searcher || editor.getContentSearcherOfCursor();
range = searcher && searcher.getRangeFromText(textOrRange, exactMatch);
}
else {
range = textOrRange;
}
if (range) {
var backupRange = editor.getSelectionRange();
// If the range to replace is right before current cursor, it is actually an exact match
if (backupRange.collapsed &&
range.endContainer == backupRange.startContainer &&
range.endOffset == backupRange.startOffset) {
exactMatch = true;
}
editor.insertNode(node, {
position: 5 /* Range */,
updateCursor: exactMatch,
replaceSelection: true,
insertOnNewLine: false,
range: range,
});
return true;
}
return false;
}
exports.default = replaceWithNode;
/***/ }),
/***/ "./packages/roosterjs-editor-api/lib/format/setAlignment.ts":
/*!******************************************************************!*\
!*** ./packages/roosterjs-editor-api/lib/format/setAlignment.ts ***!
\******************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var execCommand_1 = __webpack_require__(/*! ../utils/execCommand */ "./packages/roosterjs-editor-api/lib/utils/execCommand.ts");
/**
* Set content alignment
* @param editor The editor instance
* @param alignment The alignment option:
* Alignment.Center, Alignment.Left, Alignment.Right
*/
function setAlignment(editor, alignment) {
var command = "justifyLeft" /* JustifyLeft */;
var align = 'left';
if (alignment == 1 /* Center */) {
command = "justifyCenter" /* JustifyCenter */;
align = 'center';
}
else if (alignment == 2 /* Right */) {
command = "justifyRight" /* JustifyRight */;
align = 'right';
}
editor.addUndoSnapshot(function () {
execCommand_1.default(editor, command);
editor.queryElements('[align]', 1 /* OnSelection */, function (node) { return (node.style.textAlign = align); });
}, "Format" /* Format */);
}
exports.default = setAlignment;
/***/ }),
/***/ "./packages/roosterjs-editor-api/lib/format/setBackgroundColor.ts":
/*!************************************************************************!*\
!*** ./packages/roosterjs-editor-api/lib/format/setBackgroundColor.ts ***!
\************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var applyInlineStyle_1 = __webpack_require__(/*! ../utils/applyInlineStyle */ "./packages/roosterjs-editor-api/lib/utils/applyInlineStyle.ts");
/**
* Set background color at current selection
* @param editor The editor instance
* @param color One of two options:
* The color string, can be any of the predefined color names (e.g, 'red')
* or hexadecimal color string (e.g, '#FF0000') or rgb value (e.g, 'rgb(255, 0, 0)') supported by browser.
* Currently there's no validation to the string, if the passed string is invalid, it won't take affect
* Alternatively, you can pass a @typedef ModeIndepenentColor. If in light mode, the lightModeColor property will be used.
* If in dark mode, the darkModeColor will be used and the lightModeColor will be used when converting back to light mode.
**/
function setBackgroundColor(editor, color) {
if (typeof color === 'string') {
var trimmedColor_1 = color.trim();
applyInlineStyle_1.default(editor, function (element, isInnerNode) {
element.style.backgroundColor = isInnerNode ? '' : trimmedColor_1;
});
}
else {
var darkMode_1 = editor.isDarkMode();
var appliedColor_1 = darkMode_1 ? color.darkModeColor : color.lightModeColor;
applyInlineStyle_1.default(editor, function (element, isInnerNode) {
element.style.backgroundColor = isInnerNode ? '' : appliedColor_1;
if (darkMode_1) {
element.dataset.ogsb = color.lightModeColor;
}
});
}
}
exports.default = setBackgroundColor;
/***/ }),
/***/ "./packages/roosterjs-editor-api/lib/format/setDirection.ts":
/*!******************************************************************!*\
!*** ./packages/roosterjs-editor-api/lib/format/setDirection.ts ***!
\******************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var collapseSelectedBlocks_1 = __webpack_require__(/*! ../utils/collapseSelectedBlocks */ "./packages/roosterjs-editor-api/lib/utils/collapseSelectedBlocks.ts");
/**
* Change direction for the blocks/paragraph at selection
* @param editor The editor instance
* @param direction The direction option:
* Direction.LeftToRight refers to 'ltr', Direction.RightToLeft refers to 'rtl'
*/
function setDirection(editor, direction) {
editor.focus();
editor.addUndoSnapshot(function (start, end) {
collapseSelectedBlocks_1.default(editor, function (element) {
element.setAttribute('dir', direction == 0 /* LeftToRight */ ? 'ltr' : 'rtl');
element.style.textAlign = direction == 0 /* LeftToRight */ ? 'left' : 'right';
});
editor.select(start, end);
}, "Format" /* Format */);
}
exports.default = setDirection;
/***/ }),
/***/ "./packages/roosterjs-editor-api/lib/format/setFontName.ts":
/*!*****************************************************************!*\
!*** ./packages/roosterjs-editor-api/lib/format/setFontName.ts ***!
\*****************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var applyInlineStyle_1 = __webpack_require__(/*! ../utils/applyInlineStyle */ "./packages/roosterjs-editor-api/lib/utils/applyInlineStyle.ts");
/**
* Set font name at selection
* @param editor The editor instance
* @param fontName The fontName string, should be a valid CSS font-family style.
* Currently there's no validation to the string, if the passed string is invalid, it won't take affect
*/
function setFontName(editor, fontName) {
fontName = fontName.trim();
// The browser provided execCommand creates a HTML <font> tag with face attribute. <font> is not HTML5 standard
// (http://www.w3schools.com/tags/tag_font.asp). Use applyInlineStyle which gives flexibility on applying inline style
// for here, we use CSS font-family style
applyInlineStyle_1.default(editor, function (element, isInnerNode) {
element.style.fontFamily = isInnerNode ? '' : fontName;
});
}
exports.default = setFontName;
/***/ }),
/***/ "./packages/roosterjs-editor-api/lib/format/setFontSize.ts":
/*!*****************************************************************!*\
!*** ./packages/roosterjs-editor-api/lib/format/setFontSize.ts ***!
\*****************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var applyInlineStyle_1 = __webpack_require__(/*! ../utils/applyInlineStyle */ "./packages/roosterjs-editor-api/lib/utils/applyInlineStyle.ts");
var roosterjs_editor_dom_1 = __webpack_require__(/*! roosterjs-editor-dom */ "./packages/roosterjs-editor-dom/lib/index.ts");
/**
* Set font size at selection
* @param editor The editor instance
* @param fontSize The fontSize string, should be a valid CSS font-size style.
* Currently there's no validation to the string, if the passed string is invalid, it won't take affect
*/
function setFontSize(editor, fontSize) {
fontSize = fontSize.trim();
// The browser provided execCommand only accepts 1-7 point value. In addition, it uses HTML <font> tag with size attribute.
// <font> is not HTML5 standard (http://www.w3schools.com/tags/tag_font.asp). Use applyInlineStyle which gives flexibility on applying inline style
// for here, we use CSS font-size style
applyInlineStyle_1.default(editor, function (element, isInnerNode) {
element.style.fontSize = isInnerNode ? '' : fontSize;
var lineHeight = roosterjs_editor_dom_1.getComputedStyle(element, 'line-height');
if (lineHeight != 'normal') {
element.style.lineHeight = 'normal';
}
});
}
exports.default = setFontSize;
/***/ }),
/***/ "./packages/roosterjs-editor-api/lib/format/setImageAltText.ts":
/*!*********************************************************************!*\
!*** ./packages/roosterjs-editor-api/lib/format/setImageAltText.ts ***!
\*********************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
/**
* Set image alt text for all selected images at selection. If no images is contained
* in selection, do nothing.
* The alt attribute provides alternative information for an image if a user for some reason
* cannot view it (because of slow connection, an error in the src attribute, or if the user
* uses a screen reader). See https://www.w3schools.com/tags/att_img_alt.asp
* @param editor The editor instance
* @param altText The image alt text
*/
function setImageAltText(editor, altText) {
editor.focus();
editor.addUndoSnapshot(function () {
editor.queryElements('img', 1 /* OnSelection */, function (node) {
return node.setAttribute('alt', altText);
});
}, "Format" /* Format */);
}
exports.default = setImageAltText;
/***/ }),
/***/ "./packages/roosterjs-editor-api/lib/format/setIndentation.ts":
/*!********************************************************************!*\
!*** ./packages/roosterjs-editor-api/lib/format/setIndentation.ts ***!
\********************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var experimentSetIndentation_1 = __webpack_require__(/*! ../experiment/experimentSetIndentation */ "./packages/roosterjs-editor-api/lib/experiment/experimentSetIndentation.ts");
var processList_1 = __webpack_require__(/*! ../utils/processList */ "./packages/roosterjs-editor-api/lib/utils/processList.ts");
/**
* Set indentation at selection
* If selection contains bullet/numbering list, increase/decrease indentation will
* increase/decrease the list level by one.
* @param editor The editor instance
* @param indentation The indentation option:
* Indentation.Increase to increase indentation or Indentation.Decrease to decrease indentation
*/
function setIndentation(editor, indentation) {
if (editor.useExperimentFeatures()) {
experimentSetIndentation_1.default(editor, indentation);
}
else {
var command_1 = indentation == 0 /* Increase */ ? "indent" /* Indent */ : "outdent" /* Outdent */;
editor.addUndoSnapshot(function () {
editor.focus();
var listNode = editor.getElementAtCursor('OL,UL');
var newNode;
if (listNode) {
// There is already list node, setIndentation() will increase/decrease the list level,
// so we need to process the list when change indentation
newNode = processList_1.default(editor, command_1);
}
else {
// No existing list node, browser will create <Blockquote> node for indentation.
// We need to set top and bottom margin to 0 to avoid unnecessary spaces
editor.getDocument().execCommand(command_1, false, null);
editor.queryElements('BLOCKQUOTE', 1 /* OnSelection */, function (node) {
newNode = newNode || node;
node.style.marginTop = '0px';
node.style.marginBottom = '0px';
});
}
return newNode;
}, "Format" /* Format */);
}
}
exports.default = setIndentation;
/***/ }),
/***/ "./packages/roosterjs-editor-api/lib/format/setTextColor.ts":
/*!******************************************************************!*\
!*** ./packages/roosterjs-editor-api/lib/format/setTextColor.ts ***!
\******************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var applyInlineStyle_1 = __webpack_require__(/*! ../utils/applyInlineStyle */ "./packages/roosterjs-editor-api/lib/utils/applyInlineStyle.ts");
/**
* Set text color at selection
* @param editor The editor instance
* @param color One of two options:
* The color string, can be any of the predefined color names (e.g, 'red')
* or hexadecimal color string (e.g, '#FF0000') or rgb value (e.g, 'rgb(255, 0, 0)') supported by browser.
* Currently there's no validation to the string, if the passed string is invalid, it won't take affect
* Alternatively, you can pass a @typedef ModeIndepenentColor. If in light mode, the lightModeColor property will be used.
* If in dark mode, the darkModeColor will be used and the lightModeColor will be used when converting back to light mode.
*/
function setTextColor(editor, color) {
if (typeof color === 'string') {
var trimmedColor_1 = color.trim();
applyInlineStyle_1.default(editor, function (element, isInnerNode) {
element.style.color = isInnerNode ? '' : trimmedColor_1;
});
}
else {
var darkMode_1 = editor.isDarkMode();
var appliedColor_1 = darkMode_1 ? color.darkModeColor : color.lightModeColor;
applyInlineStyle_1.default(editor, function (element, isInnerNode) {
element.style.color = isInnerNode ? '' : appliedColor_1;
if (darkMode_1) {
element.dataset.ogsc = color.lightModeColor;
}
});
}
}
exports.default = setTextColor;
/***/ }),
/***/ "./packages/roosterjs-editor-api/lib/format/toggleBlockQuote.ts":
/*!**********************************************************************!*\
!*** ./packages/roosterjs-editor-api/lib/format/toggleBlockQuote.ts ***!
\**********************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var toggleTagCore_1 = __webpack_require__(/*! ../utils/toggleTagCore */ "./packages/roosterjs-editor-api/lib/utils/toggleTagCore.ts");
var BLOCKQUOTE_TAG = 'blockquote';
var DEFAULT_STYLER = function (element) {
element.style.borderLeft = '3px solid';
element.style.borderColor = '#C8C8C8';
element.style.paddingLeft = '10px';
element.style.color = '#666666';
};
/**
* Toggle blockquote at selection, if selection already contains any blockquoted elements,
* the blockquoted elements will be unblockquoted and other elements will take no affect
* @param editor The editor instance
* @param styler (Optional) The custom styler for setting the style for the blockquote element
*/
function toggleBlockQuote(editor, styler) {
toggleTagCore_1.default(editor, BLOCKQUOTE_TAG, styler || DEFAULT_STYLER);
}
exports.default = toggleBlockQuote;
/***/ }),
/***/ "./packages/roosterjs-editor-api/lib/format/toggleBold.ts":
/*!****************************************************************!*\
!*** ./packages/roosterjs-editor-api/lib/format/toggleBold.ts ***!
\****************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var execCommand_1 = __webpack_require__(/*! ../utils/execCommand */ "./packages/roosterjs-editor-api/lib/utils/execCommand.ts");
/**
* Toggle bold at selection
* If selection is collapsed, it will only affect the following input after caret
* If selection contains only bold text, the bold style will be removed
* If selection contains only normal text, bold style will be added to the whole selected text
* If selection contains both bold and normal text, bold stle wil