123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454 |
- /*
- * @license Copyright (c) CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.html or http://ckeditor.com/license
- */
- /**
- * Represent plain text selection range.
- */
- CKEDITOR.plugins.add('textselection',
- {
- version: "1.08.0",
- init: function (editor) {
- if (editor.config.fullPage) {
- return;
- }
- // Corresponding text range of WYSIWYG bookmark.
- var wysiwygBookmark;
- // Auto sync text selection with 'wysiwyg' mode selection range.
- if (editor.config.syncSelection
- && CKEDITOR.plugins.sourcearea) {
- editor.on('beforeModeUnload', function (evt) {
- if (editor.mode === 'source') {
- if (editor.mode === 'source' && !editor.plugins.codemirror) {
- var range = editor.getTextSelection();
- // Fly the range when create bookmark.
- delete range.element;
- range.createBookmark(editor);
- sourceBookmark = true;
- evt.data = range.content;
- }
- }
- });
- editor.on('mode', function () {
- if (editor.mode === 'wysiwyg' && sourceBookmark) {
- editor.focus();
- var doc = editor.document,
- range = new CKEDITOR.dom.range(editor.document),
- startNode,
- endNode,
- isTextNode = false;
- range.setStartAt(doc.getBody(), CKEDITOR.POSITION_AFTER_START);
- range.setEndAt(doc.getBody(), CKEDITOR.POSITION_BEFORE_END);
- var walker = new CKEDITOR.dom.walker(range);
- // walker.type = CKEDITOR.NODE_COMMENT;
- walker.evaluator = function (node) {
- //
- var match = /cke_bookmark_\d+(\w)/.exec(node.$.nodeValue);
- if (match) {
- if (unescape(node.$.nodeValue)
- .match(/<!--cke_bookmark_[0-9]+S-->.*<!--cke_bookmark_[0-9]+E-->/)){
- isTextNode = true;
- startNode = endNode = node;
- return false;
- } else {
- if (match[1] === 'S') {
- startNode = node;
- } else if (match[1] === 'E') {
- endNode = node;
- return false;
- }
- }
- }
- return false;
- };
- walker.lastForward();
- try {
- range.setStartAfter(startNode);
- range.setEndBefore(endNode);
- range.select();
- // Scroll into view for non-IE.
- if (!CKEDITOR.env.ie || (CKEDITOR.env.ie && CKEDITOR.env.version === 9)) {
- editor.getSelection().getStartElement().scrollIntoView(true);
- } // Remove the comments node which are out of range.
- if (isTextNode) {
- //remove all of our bookmarks from the text node
- //then remove all of the cke_protected bits that added because we had a comment
- //whatever code is supposed to clean these cke_protected up doesn't work
- //when there's two comments in a row like: <!--{cke_protected}{C}--><!--{cke_protected}{C}-->
- startNode.$.nodeValue = unescape(startNode.$.nodeValue).
- replace(/<!--cke_bookmark_[0-9]+[SE]-->/g, '').
- replace(/<!--[\s]*\{cke_protected}[\s]*\{C}[\s]*-->/g, '');
- } else {
- //just remove the comment nodes
- startNode.remove();
- endNode.remove();
- }
- } catch (excec) {
- }
-
- }
- }, null, null, 10);
- editor.on('beforeGetModeData', function () {
- if (editor.mode === 'wysiwyg' && editor.getData()) {
- if (CKEDITOR.env.gecko && !editor.focusManager.hasFocus) {
- return;
-
- }
- var sel = editor.getSelection(), range;
- if (sel && (range = sel.getRanges()[0])) {
- wysiwygBookmark = range.createBookmark(editor);
- }
- }
- });
- // Build text range right after WYSIWYG has unloaded.
- editor.on('afterModeUnload', function (evt) {
- if (editor.mode === 'wysiwyg' && wysiwygBookmark) {
- textRange = new CKEDITOR.dom.textRange(evt.data);
- textRange.moveToBookmark(wysiwygBookmark, editor);
- evt.data = textRange.content;
- }
- });
- editor.on('mode', function () {
- if (editor.mode === 'source' && textRange && !editor.plugins.codemirror) {
- textRange.element = new CKEDITOR.dom.element(editor._.editable.$);
- textRange.select();
- }
- });
- editor.on('destroy', function () {
- textRange = null;
- sourceBookmark = null;
- });
- }
- }
- });
- /**
- * Gets the current text selection from the editing area when in Source mode.
- * @returns {CKEDITOR.dom.textRange} Text range represent the caret positions.
- * @example
- * var textSelection = CKEDITOR.instances.editor1.<b>getTextSelection()</b>;
- * alert( textSelection.startOffset );
- * alert( textSelection.endOffset );
- */
- CKEDITOR.editor.prototype.getTextSelection = function () {
- return this._.editable && getTextSelection(this._.editable.$) || null;
- };
- /**
- * Returns the caret position of the specified textfield/textarea.
- * @param {HTMLTextArea|HTMLTextInput} element
- */
- function getTextSelection(element) {
- var startOffset, endOffset;
- if (!CKEDITOR.env.ie) {
- startOffset = element.selectionStart;
- endOffset = element.selectionEnd;
- } else {
- element.focus();
-
- // The current selection
- if (window.getSelection) {
- startOffset = element.selectionStart;
- endOffset = element.selectionEnd;
- } else {
- var range = document.selection.createRange(),
- textLength = range.text.length;
-
- // Create a 'measuring' range to help calculate the start offset by
- // stretching it from start to current position.
- var measureRange = range.duplicate();
- measureRange.moveToElementText(element);
- measureRange.setEndPoint('EndToEnd', range);
- endOffset = measureRange.text.length;
- startOffset = endOffset - textLength;
- }
- }
- return new CKEDITOR.dom.textRange(
- new CKEDITOR.dom.element(element), startOffset, endOffset);
- }
- /**
- * Represent the selection range within a HTML textfield/textarea element,
- * or even a flyweight string content represent the text content.
- * @constructor
- * @param {CKEDITOR.dom.element|String} element
- * @param {Number} start
- * @param {Number} end
- */
- CKEDITOR.dom.textRange = function (element, start, end) {
- if (element instanceof CKEDITOR.dom.element
- && (element.is('textarea')
- || element.is('input') && element.getAttribute('type') == 'text')) {
- this.element = element;
- this.content = element.$.value;
- } else if (typeof element == 'string')
- this.content = element;
- else
- throw 'Unknown "element" type.';
- this.startOffset = start || 0;
- this.endOffset = end || 0;
- };
-
- /**
- * Changes the editing mode of this editor instance. (Override of the original function)
- *
- * **Note:** The mode switch could be asynchronous depending on the mode provider.
- * Use the `callback` to hook subsequent code.
- *
- * // Switch to "source" view.
- * CKEDITOR.instances.editor1.setMode( 'source' );
- * // Switch to "WYSIWYG" view and be notified on completion.
- * CKEDITOR.instances.editor1.setMode( 'wysiwyg', function() { alert( 'WYSIWYG mode loaded!' ); } );
- *
- * @param {String} [newMode] If not specified, the {@link CKEDITOR.config#startupMode} will be used.
- * @param {Function} [callback] Optional callback function which is invoked once the mode switch has succeeded.
- */
- CKEDITOR.editor.prototype.setMode = function (newMode, callback) {
- var editor = this;
- var modes = this._.modes;
- // Mode loading quickly fails.
- if (newMode == editor.mode || !modes || !modes[newMode])
- return;
- editor.fire('beforeSetMode', newMode);
- if (editor.mode) {
- var isDirty = editor.checkDirty();
- editor._.previousMode = editor.mode;
- // Get cached data, which was set while detaching editable.
- editor._.previousModeData = editor.getData();
- editor.fire('beforeModeUnload');
- editor.fire('beforeGetModeData');
- var data = editor.getData();
- data = editor.fire('beforeModeUnload', data);
- data = editor.fire('afterModeUnload', data);
- // Detach the current editable.
- editor.editable(0);
- editor._.data = data;
- // Clear up the mode space.
- editor.ui.space('contents').setHtml('');
- editor.mode = '';
- }
- // Fire the mode handler.
- this._.modes[newMode](function () {
- // Set the current mode.
- editor.mode = newMode;
- if (isDirty !== undefined) {
- !isDirty && editor.resetDirty();
- }
- // Delay to avoid race conditions (setMode inside setMode).
- setTimeout(function () {
- editor.fire('mode');
- callback && callback.call(editor);
- }, 0);
- });
- };
- CKEDITOR.dom.textRange.prototype =
- {
- /**
- * Sets the text selection of the specified textfield/textarea.
- * @param {HTMLTextArea|HTMLTextInput} element
- * @param {CKEDITOR.dom.textRange} range
- */
- select: function() {
- var startOffset = this.startOffset,
- endOffset = this.endOffset,
- element = this.element.$;
- if (endOffset == undefined) {
- endOffset = startOffset;
- }
- if (CKEDITOR.env.ie && CKEDITOR.env.version == 9) {
- element.focus();
- element.selectionStart = startOffset;
- element.selectionEnd = startOffset;
- setTimeout(function() {
- element.selectionStart = startOffset;
- element.selectionEnd = endOffset;
- }, 20);
- } else {
- if (element.setSelectionRange) {
- if (CKEDITOR.env.ie) {
- element.focus();
- }
- element.setSelectionRange(startOffset, endOffset);
- if (!CKEDITOR.env.ie) {
- element.focus();
- }
- } else if (element.createTextRange) {
- element.focus();
- var range = element.createTextRange();
- range.collapse(true);
- range.moveStart('character', startOffset);
- range.moveEnd('character', endOffset - startOffset);
- range.select();
- }
- }
- },
- /**
- * Select the range included within the bookmark text with the bookmark
- * text removed.
- * @param {Object} bookmark Exactly the one created by CKEDITOR.dom.range.createBookmark( true ).
- */
- moveToBookmark: function(bookmark, editor) {
- var content = this.content;
- function removeBookmarkText(bookmarkId) {
- var bookmarkRegex = new RegExp('<span[^<]*?' + bookmarkId + '.*?/span>'),
- offset;
- content = content.replace(bookmarkRegex, function(str, index) {
- offset = index;
- return '';
- });
- return offset;
- }
- this.startOffset = removeBookmarkText(bookmark.startNode);
- this.endOffset = removeBookmarkText(bookmark.endNode);
- var savedContent = editor._.previousModeData;
- if (savedContent.length > 0) {
- var diff = content.length - savedContent.length;
- this.startOffset = this.startOffset - diff;
- if (diff > 0) {
- //this.endOffset = this.endOffset - diff;
- }
- content = editor._.previousModeData;
- this.content = content;
- this.updateElement();
- if (editor.undoManager) {
- editor.undoManager.unlock();
- }
- }
- },
- /**
- * If startOffset/endOffset anchor inside element tag, start the range before/after the element
- */
- enlarge: function() {
- var htmlOpenTagRegexp = /<[a-zA-Z]+(>|.*?[^?]>)/g;
- var htmlCloseTagRegexp = /<\/[^>]+>/g;
- var content = this.content,
- start = this.startOffset,
- end = this.endOffset,
- match,
- tagStartIndex,
- tagEndIndex;
- while (match = htmlCloseTagRegexp.exec(content)) {
- tagStartIndex = match.index;
- tagEndIndex = tagStartIndex + match[0].length;
- if (tagEndIndex < start) {
- continue;
- }
- if (tagStartIndex <= start) {
- start = tagStartIndex;
- end = tagStartIndex;
- break;
- }
- }
- while (match = htmlOpenTagRegexp.exec(content)) {
- tagStartIndex = match.index;
- tagEndIndex = tagStartIndex + match[0].length;
- if (tagEndIndex < start) {
- continue;
- }
- if (tagStartIndex <= start) {
- start = tagEndIndex;
- end = tagEndIndex;
- break;
- }
- }
- this.startOffset = start;
- this.endOffset = end;
- },
- createBookmark: function (editor) {
- // Enlarge the range to avoid tag partial selection.
- this.enlarge();
- var content = this.content,
- start = this.startOffset,
- end = this.endOffset,
- id = CKEDITOR.tools.getNextNumber(),
- bookmarkTemplate = '<!--cke_bookmark_%1-->';
- content = content.substring(0, start) + bookmarkTemplate.replace('%1', id + 'S')
- + content.substring(start, end) + bookmarkTemplate.replace('%1', id + 'E')
- + content.substring(end);
- if (editor.undoManager) {
- editor.undoManager.lock();
- }
- this.content = content;
- this.updateElement();
- },
- updateElement: function() {
- if (this.element)
- this.element.$.value = this.content;
- }
- };
- var Browser = {
- Version: function() {
- var version = 999;
- if (navigator.appVersion.indexOf("MSIE") != -1)
- version = parseFloat(navigator.appVersion.split("MSIE")[1]);
- return version;
- }
- };
- // Seamless selection range across different modes.
- CKEDITOR.config.syncSelection = true;
- var textRange,sourceBookmark;
|