upfront-editable
Version:
Friendly contenteditable API
266 lines (212 loc) • 8.63 kB
JavaScript
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
var _createClass2 = require('babel-runtime/helpers/createClass');
var _createClass3 = _interopRequireDefault(_createClass2);
var _jquery = require('jquery');
var _jquery2 = _interopRequireDefault(_jquery);
var _nodeType = require('./node-type');
var nodeType = _interopRequireWildcard(_nodeType);
var _content = require('./content');
var content = _interopRequireWildcard(_content);
var _highlightText = require('./highlight-text');
var _highlightText2 = _interopRequireDefault(_highlightText);
var _spellcheckService = require('./plugins/highlighting/spellcheck-service');
var _spellcheckService2 = _interopRequireDefault(_spellcheckService);
var _whitespaceHighlighting = require('./plugins/highlighting/whitespace-highlighting');
var _whitespaceHighlighting2 = _interopRequireDefault(_whitespaceHighlighting);
var _textHighlighting = require('./plugins/highlighting/text-highlighting');
var _textHighlighting2 = _interopRequireDefault(_textHighlighting);
var _matchCollection = require('./plugins/highlighting/match-collection');
var _matchCollection2 = _interopRequireDefault(_matchCollection);
var _highlightSupport = require('./highlight-support');
var _highlightSupport2 = _interopRequireDefault(_highlightSupport);
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 }; }
var Highlighting = function () {
function Highlighting(editable, configuration, spellcheckConfig) {
(0, _classCallCheck3.default)(this, Highlighting);
this.editable = editable;
this.win = editable.win;
this.focusedEditableHost = undefined;
this.currentlyCheckedEditableHost = undefined;
this.timeout = {};
var defaultConfig = {
checkOnInit: false,
checkOnFocus: false,
checkOnChange: true,
// unbounce rate in ms before calling the spellcheck service after changes
throttle: 1000,
// remove highlights after a change if the cursor is inside a highlight
removeOnCorrection: true,
spellcheck: {
marker: '<span class="highlight-spellcheck"></span>',
throttle: 1000,
spellcheckService: function spellcheckService() {}
},
whitespace: {
marker: '<span class="highlight-whitespace"></span>'
}
};
this.config = _jquery2.default.extend(true, defaultConfig, configuration);
var spellcheckService = this.config.spellcheck.spellcheckService;
var spellcheckMarker = this.config.spellcheck.marker;
var whitespaceMarker = this.config.whitespace.marker;
var spellcheckMarkerNode = _highlightSupport2.default.createMarkerNode(spellcheckMarker, 'spellcheck', this.win);
var whitespaceMarkerNode = _highlightSupport2.default.createMarkerNode(whitespaceMarker, 'spellcheck', this.win);
this.spellcheckService = new _spellcheckService2.default(spellcheckService);
this.spellcheck = new _textHighlighting2.default(spellcheckMarkerNode);
this.whitespace = new _whitespaceHighlighting2.default(whitespaceMarkerNode);
this.setupListeners();
}
// Events
// ------
(0, _createClass3.default)(Highlighting, [{
key: 'setupListeners',
value: function setupListeners() {
if (this.config.checkOnFocus) {
this.editable.on('focus', _jquery2.default.proxy(this, 'onFocus'));
this.editable.on('blur', _jquery2.default.proxy(this, 'onBlur'));
}
if (this.config.checkOnChange || this.config.removeOnCorrection) {
this.editable.on('change', _jquery2.default.proxy(this, 'onChange'));
}
if (this.config.checkOnInit) {
this.editable.on('init', _jquery2.default.proxy(this, 'onInit'));
}
}
}, {
key: 'onInit',
value: function onInit(editableHost) {
this.highlight(editableHost);
}
}, {
key: 'onFocus',
value: function onFocus(editableHost) {
if (this.focusedEditableHost !== editableHost) {
this.focusedEditableHost = editableHost;
this.editableHasChanged(editableHost);
}
}
}, {
key: 'onBlur',
value: function onBlur(editableHost) {
if (this.focusedEditableHost === editableHost) {
this.focusedEditableHost = undefined;
}
}
}, {
key: 'onChange',
value: function onChange(editableHost) {
if (this.config.checkOnChange) {
this.editableHasChanged(editableHost, this.config.throttle);
}
if (this.config.removeOnCorrection) {
this.removeHighlightsAtCursor(editableHost);
}
}
// Manage Highlights
// -----------------
}, {
key: 'editableHasChanged',
value: function editableHasChanged(editableHost, throttle) {
var _this = this;
if (this.timeout.id && this.timeout.editableHost === editableHost) {
clearTimeout(this.timeout.id);
}
var timeoutId = setTimeout(function () {
_this.highlight(editableHost);
_this.timeout = {};
}, throttle || 0);
this.timeout = {
id: timeoutId,
editableHost: editableHost
};
}
}, {
key: 'highlight',
value: function highlight(editableHost) {
var _this2 = this;
var text = _highlightText2.default.extractText(editableHost);
// getSpellcheck
this.spellcheckService.check(text, function (err, misspelledWords) {
if (err) {
return;
}
// refresh the text
text = _highlightText2.default.extractText(editableHost);
var matchCollection = new _matchCollection2.default();
var matches = _this2.spellcheck.findMatches(text, misspelledWords);
matchCollection.addMatches('spellcheck', matches);
matches = _this2.whitespace.findMatches(text);
matchCollection.addMatches('whitespace', matches);
_this2.safeHighlightMatches(editableHost, matchCollection.matches);
});
}
// Calls highlightMatches internally but ensures
// that the selection stays the same
}, {
key: 'safeHighlightMatches',
value: function safeHighlightMatches(editableHost, matches) {
var _this3 = this;
var selection = this.editable.getSelection(editableHost);
if (selection) {
selection.retainVisibleSelection(function () {
_this3.highlightMatches(editableHost, matches);
});
} else {
this.highlightMatches(editableHost, matches);
}
}
}, {
key: 'highlightMatches',
value: function highlightMatches(editableHost, matches) {
// Remove old highlights
this.removeHighlights(editableHost);
// Create new highlights
if (matches && matches.length > 0) {
// const span = this.createMarkerNode()
_highlightText2.default.highlightMatches(editableHost, matches);
}
}
}, {
key: 'removeHighlights',
value: function removeHighlights(editableHost) {
(0, _jquery2.default)(editableHost).find('[data-highlight="spellcheck"]').each(function (index, elem) {
content.unwrap(elem);
});
}
}, {
key: 'removeHighlightsAtCursor',
value: function removeHighlightsAtCursor(editableHost) {
var selection = this.editable.getSelection(editableHost);
if (selection && selection.isCursor) {
var elementAtCursor = selection.range.startContainer;
if (elementAtCursor.nodeType === nodeType.textNode) {
elementAtCursor = elementAtCursor.parentNode;
}
var wordId = void 0;
do {
if (elementAtCursor === editableHost) return;
var highlightType = elementAtCursor.getAttribute('data-highlight');
if (highlightType === 'spellcheck') {
wordId = elementAtCursor.getAttribute('data-word-id');
break;
}
} while (elementAtCursor = elementAtCursor.parentNode);
if (wordId) {
selection.retainVisibleSelection(function () {
(0, _jquery2.default)(editableHost).find('[data-word-id=' + wordId + ']').each(function (index, elem) {
content.unwrap(elem);
});
});
}
}
}
}]);
return Highlighting;
}();
exports.default = Highlighting;