upfront-editable
Version:
Friendly contenteditable API
358 lines (309 loc) • 13.2 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _typeof = require("@babel/runtime/helpers/typeof");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = void 0;
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));
var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));
var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));
require("rangy/lib/rangy-textrange");
var _jquery = _interopRequireDefault(require("jquery"));
var _cursor = _interopRequireDefault(require("./cursor"));
var content = _interopRequireWildcard(require("./content"));
var parser = _interopRequireWildcard(require("./parser"));
var _config = _interopRequireDefault(require("./config"));
var _highlightSupport = _interopRequireDefault(require("./highlight-support"));
var _highlightText = _interopRequireDefault(require("./highlight-text"));
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = (0, _getPrototypeOf2["default"])(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = (0, _getPrototypeOf2["default"])(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return (0, _possibleConstructorReturn2["default"])(this, result); }; }
function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }
/**
* The Selection module provides a cross-browser abstraction layer for range
* and selection.
*
* @module core
* @submodule selection
*/
/**
* Class that represents a selection and provides functionality to access or
* modify the selection.
*
* @class Selection
* @constructor
*/
var Selection = /*#__PURE__*/function (_Cursor) {
(0, _inherits2["default"])(Selection, _Cursor);
var _super = _createSuper(Selection);
function Selection() {
var _this;
(0, _classCallCheck2["default"])(this, Selection);
_this = _super.apply(this, arguments);
delete _this.isCursor;
_this.isSelection = true;
return _this;
} // Get the text inside the selection.
(0, _createClass2["default"])(Selection, [{
key: "text",
value: function text() {
return this.range.toString();
} // Get the html inside the selection.
}, {
key: "html",
value: function html() {
return this.range.toHtml();
}
}, {
key: "isAllSelected",
value: function isAllSelected() {
return parser.isBeginningOfHost(this.host, this.range.startContainer, this.range.startOffset) && parser.isTextEndOfHost(this.host, this.range.endContainer, this.range.endOffset);
}
}, {
key: "getTextRange",
value: function getTextRange() {
return this.range.toCharacterRange(this.host);
} // Get the ClientRects of this selection.
// Use this if you want more precision than getBoundingClientRect can give.
}, {
key: "getRects",
value: function getRects() {
// consider: translate into absolute positions
// just like Cursor#getCoordinates()
return this.range.nativeRange.getClientRects();
}
}, {
key: "link",
value: function link(href) {
var attrs = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var $link = (0, _jquery["default"])(this.createElement(_config["default"].linkMarkup.name, _config["default"].linkMarkup.attribs));
if (href) attrs.href = href;
$link.attr(attrs);
this.forceWrap($link[0]);
}
}, {
key: "unlink",
value: function unlink() {
this.removeFormatting(_config["default"].linkMarkup.name);
}
}, {
key: "toggleLink",
value: function toggleLink(href, attrs) {
var links = this.getTagsByName(_config["default"].linkMarkup.name);
if (links.length >= 1) {
var firstLink = links[0];
if (this.isExactSelection(firstLink, 'visible')) {
this.unlink();
} else {
this.expandTo(firstLink);
}
} else {
this.link(href, attrs);
}
} // Manually add a highlight
// Note: the current code does not work with newlines (LP)
}, {
key: "highlight",
value: function highlight(_ref) {
var highlightId = _ref.highlightId;
var textBefore = this.textBefore();
var currentTextContent = this.text();
var marker = '<span class="highlight-comment"></span>';
var markerNode = _highlightSupport["default"].createMarkerNode(marker, this.win);
markerNode.setAttribute('data-match', currentTextContent);
var match = {
startIndex: textBefore.length,
endIndex: textBefore.length + currentTextContent.length,
match: currentTextContent,
marker: markerNode
}; // Note: highlighting won't retain the selection
_highlightText["default"].highlightMatches(this.host, [match]);
} // toggle('<em>')
}, {
key: "toggle",
value: function toggle(elem) {
elem = this.adoptElement(elem);
this.range = content.toggleTag(this.host, this.range, elem);
this.setSelection();
}
}, {
key: "toggleCustom",
value: function toggleCustom(_ref2) {
var tagName = _ref2.tagName,
attributes = _ref2.attributes;
var customElem = this.createElement(tagName, attributes);
this.toggle(customElem);
}
}, {
key: "makeCustom",
value: function makeCustom(_ref3) {
var tagName = _ref3.tagName,
attributes = _ref3.attributes;
var customElem = this.createElement(tagName, attributes);
this.forceWrap(customElem);
}
}, {
key: "makeBold",
value: function makeBold() {
var bold = this.createElement(_config["default"].boldMarkup.name, _config["default"].boldMarkup.attribs);
this.forceWrap(bold);
}
}, {
key: "toggleBold",
value: function toggleBold() {
var bold = this.createElement(_config["default"].boldMarkup.name, _config["default"].boldMarkup.attribs);
this.toggle(bold);
}
}, {
key: "giveEmphasis",
value: function giveEmphasis() {
var em = this.createElement(_config["default"].italicMarkup.name, _config["default"].italicMarkup.attribs);
this.forceWrap(em);
}
}, {
key: "toggleEmphasis",
value: function toggleEmphasis() {
var em = this.createElement(_config["default"].italicMarkup.name, _config["default"].italicMarkup.attribs);
this.toggle(em);
}
}, {
key: "makeUnderline",
value: function makeUnderline() {
var u = this.createElement(_config["default"].underlineMarkup.name, _config["default"].underlineMarkup.attribs);
this.forceWrap(u);
}
}, {
key: "toggleUnderline",
value: function toggleUnderline() {
var u = this.createElement(_config["default"].underlineMarkup.name, _config["default"].underlineMarkup.attribs);
this.toggle(u);
}
}, {
key: "insertCharacter",
value: function insertCharacter(character) {
var cursor = this.deleteContent();
var textNode = cursor.createTextNode(character);
cursor.insertBefore(textNode);
cursor.setSelection();
return cursor;
} // Surround the selection with characters like quotes.
//
// @param {String} E.g. '«'
// @param {String} E.g. '»'
}, {
key: "surround",
value: function surround(startCharacter, endCharacter) {
this.range = content.surround(this.host, this.range, startCharacter, endCharacter);
this.setSelection();
}
}, {
key: "removeSurround",
value: function removeSurround(startCharacter, endCharacter) {
this.range = content.deleteCharacter(this.host, this.range, startCharacter);
this.range = content.deleteCharacter(this.host, this.range, endCharacter);
this.setSelection();
}
}, {
key: "removeChars",
value: function removeChars() {
var chars = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
for (var i = 0; i < chars.length; i++) {
var _char = chars[i];
this.range = content.deleteCharacter(this.host, this.range, _char);
}
this.setSelection();
}
}, {
key: "toggleSurround",
value: function toggleSurround(startCharacter, endCharacter) {
if (this.containsString(startCharacter) && this.containsString(endCharacter)) {
this.removeSurround(startCharacter, endCharacter);
} else {
this.surround(startCharacter, endCharacter);
}
} // @param {String} tagName. E.g. 'a' to remove all links; if undefined, remove all.
}, {
key: "removeFormatting",
value: function removeFormatting(tagName) {
this.range = content.removeFormatting(this.host, this.range, tagName);
this.setSelection();
} // Delete the contents inside the range. After that the selection will be a
// cursor.
//
// @return Cursor instance
}, {
key: "deleteContent",
value: function deleteContent() {
this.range.deleteContents();
return new _cursor["default"](this.host, this.range);
} // Expand the current selection.
//
// @param {DOM Node}
}, {
key: "expandTo",
value: function expandTo(elem) {
this.range = content.expandTo(this.host, this.range, elem);
this.setSelection();
} // Collapse the selection at the beginning of the selection
//
// @return Cursor instance
}, {
key: "collapseAtBeginning",
value: function collapseAtBeginning(elem) {
this.range.collapse(true);
this.setSelection();
return new _cursor["default"](this.host, this.range);
} // Collapse the selection at the end of the selection
//
// @return Cursor instance
}, {
key: "collapseAtEnd",
value: function collapseAtEnd(elem) {
this.range.collapse(false);
this.setSelection();
return new _cursor["default"](this.host, this.range);
} // Wrap the selection with the specified tag. If any other tag with
// the same tagName is affecting the selection this tag will be
// remove first.
}, {
key: "forceWrap",
value: function forceWrap(elem) {
elem = this.adoptElement(elem);
this.range = content.forceWrap(this.host, this.range, elem);
this.setSelection();
} // Check if the selection is the same as the elements contents.
//
// @method isExactSelection
// @param {DOM Node}
// @param {flag: undefined or 'visible'} if 'visible' is passed
// whitespaces at the beginning or end of the selection will
// be ignored.
// @return {Boolean}
}, {
key: "isExactSelection",
value: function isExactSelection(elem, onlyVisible) {
return content.isExactSelection(this.range, elem, onlyVisible);
} // Check if the selection contains the passed string.
//
// @method containsString
// @return {Boolean}
}, {
key: "containsString",
value: function containsString(str) {
return content.containsString(this.range, str);
} // Delete all occurrences of the specified character from the
// selection.
}, {
key: "deleteCharacter",
value: function deleteCharacter(character) {
this.range = content.deleteCharacter(this.host, this.range, character);
this.setSelection();
}
}]);
return Selection;
}(_cursor["default"]);
exports["default"] = Selection;
module.exports = exports.default;