upfront-editable
Version:
Friendly contenteditable API
133 lines (107 loc) • 4.18 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.insertRangeBoundaryMarker = insertRangeBoundaryMarker;
exports.setRangeBoundary = setRangeBoundary;
exports.save = save;
exports.restore = restore;
var _rangy = require('rangy');
var _rangy2 = _interopRequireDefault(_rangy);
var _error = require('./util/error');
var _error2 = _interopRequireDefault(_error);
var _nodeType = require('./node-type');
var nodeType = _interopRequireWildcard(_nodeType);
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Inspired by the Selection save and restore module for Rangy by Tim Down
* Saves and restores ranges using invisible marker elements in the DOM.
*/
var boundaryMarkerId = 0;
// (U+FEFF) zero width no-break space
var markerTextChar = '\uFEFF';
function insertRangeBoundaryMarker(range, atStart) {
var container = range.commonAncestorContainer;
// If ownerDocument is null the commonAncestorContainer is window.document
if (container.ownerDocument === null || container.ownerDocument === undefined) {
(0, _error2.default)('Cannot save range: range is emtpy');
}
// Clone the Range and collapse to the appropriate boundary point
var boundaryRange = range.cloneRange();
boundaryRange.collapse(atStart);
// Create the marker element containing a single invisible character using DOM methods and insert it
var doc = container.ownerDocument.defaultView.document;
var markerEl = doc.createElement('span');
markerEl.id = 'editable-range-boundary-' + ++boundaryMarkerId;
markerEl.setAttribute('data-editable', 'remove');
markerEl.style.lineHeight = '0';
markerEl.style.display = 'none';
markerEl.appendChild(doc.createTextNode(markerTextChar));
boundaryRange.insertNode(markerEl);
return markerEl;
}
function setRangeBoundary(host, range, markerId, atStart) {
var markerEl = getMarker(host, markerId);
if (!markerEl) return console.log('Marker element has been removed. Cannot restore selection.');
range[atStart ? 'setStartBefore' : 'setEndBefore'](markerEl);
markerEl.parentNode.removeChild(markerEl);
}
function save(range) {
var rangeInfo = void 0,
startEl = void 0,
endEl = void 0;
// insert markers
if (range.collapsed) {
endEl = insertRangeBoundaryMarker(range, false);
rangeInfo = {
markerId: endEl.id,
collapsed: true
};
} else {
endEl = insertRangeBoundaryMarker(range, false);
startEl = insertRangeBoundaryMarker(range, true);
rangeInfo = {
startMarkerId: startEl.id,
endMarkerId: endEl.id,
collapsed: false
};
}
// Adjust each range's boundaries to lie between its markers
if (range.collapsed) {
range.collapseBefore(endEl);
} else {
range.setEndBefore(endEl);
range.setStartAfter(startEl);
}
return rangeInfo;
}
function restore(host, rangeInfo) {
if (rangeInfo.restored) return;
var range = _rangy2.default.createRange();
if (rangeInfo.collapsed) {
var markerEl = getMarker(host, rangeInfo.markerId);
if (markerEl) {
markerEl.style.display = 'inline';
var previousNode = markerEl.previousSibling;
// Workaround for rangy issue 17
if (previousNode && previousNode.nodeType === nodeType.textNode) {
markerEl.parentNode.removeChild(markerEl);
range.collapseToPoint(previousNode, previousNode.length);
} else {
range.collapseBefore(markerEl);
markerEl.parentNode.removeChild(markerEl);
}
} else {
console.log('Marker element has been removed. Cannot restore selection.');
}
} else {
this.setRangeBoundary(host, range, rangeInfo.startMarkerId, true);
this.setRangeBoundary(host, range, rangeInfo.endMarkerId, false);
}
range.normalizeBoundaries();
return range;
}
function getMarker(host, id) {
return host.querySelector('#' + id);
}