1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
/**
* plugin.js
*
* Released under LGPL License.
* Copyright (c) 1999-2015 Ephox Corp. All rights reserved
*
* License: http://www.tinymce.com/license
* Contributing: http://www.tinymce.com/contributing
*/
/*global tinymce:true */
tinymce.PluginManager.add('visualchars', function(editor) {
var self = this, state;
function toggleVisualChars(addBookmark) {
var node, nodeList, i, body = editor.getBody(), nodeValue, selection = editor.selection, div, bookmark;
var charMap, visualCharsRegExp;
charMap = {
'\u00a0': 'nbsp',
'\u00ad': 'shy'
};
function wrapCharWithSpan(value) {
return '<span data-mce-bogus="1" class="mce-' + charMap[value] + '">' + value + '</span>';
}
function compileCharMapToRegExp() {
var key, regExp = '';
for (key in charMap) {
regExp += key;
}
return new RegExp('[' + regExp + ']', 'g');
}
function compileCharMapToCssSelector() {
var key, selector = '';
for (key in charMap) {
if (selector) {
selector += ',';
}
selector += 'span.mce-' + charMap[key];
}
return selector;
}
state = !state;
self.state = state;
editor.fire('VisualChars', {state: state});
visualCharsRegExp = compileCharMapToRegExp();
if (addBookmark) {
bookmark = selection.getBookmark();
}
if (state) {
nodeList = [];
tinymce.walk(body, function(n) {
if (n.nodeType == 3 && n.nodeValue && visualCharsRegExp.test(n.nodeValue)) {
nodeList.push(n);
}
}, 'childNodes');
for (i = 0; i < nodeList.length; i++) {
nodeValue = nodeList[i].nodeValue;
nodeValue = nodeValue.replace(visualCharsRegExp, wrapCharWithSpan);
div = editor.dom.create('div', null, nodeValue);
while ((node = div.lastChild)) {
editor.dom.insertAfter(node, nodeList[i]);
}
editor.dom.remove(nodeList[i]);
}
} else {
nodeList = editor.dom.select(compileCharMapToCssSelector(), body);
for (i = nodeList.length - 1; i >= 0; i--) {
editor.dom.remove(nodeList[i], 1);
}
}
selection.moveToBookmark(bookmark);
}
function toggleActiveState() {
var self = this;
editor.on('VisualChars', function(e) {
self.active(e.state);
});
}
editor.addCommand('mceVisualChars', toggleVisualChars);
editor.addButton('visualchars', {
title: 'Show invisible characters',
cmd: 'mceVisualChars',
onPostRender: toggleActiveState
});
editor.addMenuItem('visualchars', {
text: 'Show invisible characters',
cmd: 'mceVisualChars',
onPostRender: toggleActiveState,
selectable: true,
context: 'view',
prependToContext: true
});
});