|
1
|
|
|
/** |
|
2
|
|
|
* @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. |
|
3
|
|
|
* For licensing, see LICENSE.md or http://ckeditor.com/license |
|
4
|
|
|
*/ |
|
5
|
|
|
|
|
6
|
|
|
/** |
|
7
|
|
|
* @fileOverview Justify commands. |
|
8
|
|
|
*/ |
|
9
|
|
|
|
|
10
|
|
|
(function () { |
|
11
|
|
|
function getAlignment(element, useComputedState) { |
|
12
|
|
|
useComputedState = useComputedState === undefined || useComputedState; |
|
13
|
|
|
|
|
14
|
|
|
var align; |
|
15
|
|
|
if (useComputedState) |
|
16
|
|
|
align = element.getComputedStyle('text-align'); |
|
17
|
|
|
else { |
|
18
|
|
|
while (!element.hasAttribute || !(element.hasAttribute('align') || element.getStyle('text-align'))) { |
|
19
|
|
|
var parent = element.getParent(); |
|
20
|
|
|
if (!parent) |
|
21
|
|
|
break; |
|
22
|
|
|
element = parent; |
|
23
|
|
|
} |
|
24
|
|
|
align = element.getStyle('text-align') || element.getAttribute('align') || ''; |
|
25
|
|
|
} |
|
26
|
|
|
|
|
27
|
|
|
// Sometimes computed values doesn't tell. |
|
28
|
|
|
align && (align = align.replace(/(?:-(?:moz|webkit)-)?(?:start|auto)/i, '')); |
|
29
|
|
|
|
|
30
|
|
|
!align && useComputedState && (align = element.getComputedStyle('direction') == 'rtl' ? 'right' : 'left'); |
|
31
|
|
|
|
|
32
|
|
|
return align; |
|
33
|
|
|
} |
|
34
|
|
|
|
|
35
|
|
|
function JustifyCommand(editor, name, value) { |
|
36
|
|
|
this.editor = editor; |
|
37
|
|
|
this.name = name; |
|
38
|
|
|
this.value = value; |
|
39
|
|
|
this.context = 'p'; |
|
40
|
|
|
|
|
41
|
|
|
var classes = editor.config.justifyClasses, |
|
42
|
|
|
blockTag = editor.config.enterMode == CKEDITOR.ENTER_P ? 'p' : 'div'; |
|
43
|
|
|
|
|
44
|
|
|
if (classes) { |
|
45
|
|
|
switch (value) { |
|
46
|
|
|
case 'center': |
|
47
|
|
|
this.cssClassName = classes[ 1 ]; |
|
48
|
|
|
break; |
|
49
|
|
|
case 'right': |
|
50
|
|
|
this.cssClassName = classes[ 2 ]; |
|
51
|
|
|
break; |
|
52
|
|
|
case 'justify': |
|
53
|
|
|
this.cssClassName = classes[ 3 ]; |
|
54
|
|
|
break; |
|
55
|
|
|
case 'left': |
|
56
|
|
|
default: |
|
57
|
|
|
this.cssClassName = classes[ 0 ]; |
|
58
|
|
|
|
|
59
|
|
|
} |
|
60
|
|
|
|
|
61
|
|
|
this.cssClassRegex = new RegExp('(?:^|\\s+)(?:' + classes.join('|') + ')(?=$|\\s)'); |
|
62
|
|
|
this.requiredContent = blockTag + '(' + this.cssClassName + ')'; |
|
63
|
|
|
} else { |
|
64
|
|
|
this.requiredContent = blockTag + '{text-align}'; |
|
65
|
|
|
} |
|
66
|
|
|
|
|
67
|
|
|
this.allowedContent = { |
|
68
|
|
|
'caption div h1 h2 h3 h4 h5 h6 p pre td th li': { |
|
69
|
|
|
// Do not add elements, but only text-align style if element is validated by other rule. |
|
70
|
|
|
propertiesOnly: true, |
|
71
|
|
|
styles: this.cssClassName ? null : 'text-align', |
|
72
|
|
|
classes: this.cssClassName || null |
|
73
|
|
|
} |
|
74
|
|
|
}; |
|
75
|
|
|
|
|
76
|
|
|
// In enter mode BR we need to allow here for div, because when non other |
|
77
|
|
|
// feature allows div justify is the only plugin that uses it. |
|
78
|
|
|
if (editor.config.enterMode == CKEDITOR.ENTER_BR) |
|
79
|
|
|
this.allowedContent.div = true; |
|
80
|
|
|
} |
|
81
|
|
|
|
|
82
|
|
|
function onDirChanged(e) { |
|
83
|
|
|
var editor = e.editor; |
|
84
|
|
|
|
|
85
|
|
|
var range = editor.createRange(); |
|
86
|
|
|
range.setStartBefore(e.data.node); |
|
87
|
|
|
range.setEndAfter(e.data.node); |
|
88
|
|
|
|
|
89
|
|
|
var walker = new CKEDITOR.dom.walker(range), |
|
90
|
|
|
node; |
|
91
|
|
|
|
|
92
|
|
|
while ((node = walker.next())) { |
|
93
|
|
|
if (node.type == CKEDITOR.NODE_ELEMENT) { |
|
94
|
|
|
// A child with the defined dir is to be ignored. |
|
95
|
|
|
if (!node.equals(e.data.node) && node.getDirection()) { |
|
96
|
|
|
range.setStartAfter(node); |
|
97
|
|
|
walker = new CKEDITOR.dom.walker(range); |
|
98
|
|
|
continue; |
|
99
|
|
|
} |
|
100
|
|
|
|
|
101
|
|
|
// Switch the alignment. |
|
102
|
|
|
var classes = editor.config.justifyClasses; |
|
103
|
|
|
if (classes) { |
|
104
|
|
|
// The left align class. |
|
105
|
|
|
if (node.hasClass(classes[ 0 ])) { |
|
106
|
|
|
node.removeClass(classes[ 0 ]); |
|
107
|
|
|
node.addClass(classes[ 2 ]); |
|
108
|
|
|
} |
|
109
|
|
|
// The right align class. |
|
110
|
|
|
else if (node.hasClass(classes[ 2 ])) { |
|
111
|
|
|
node.removeClass(classes[ 2 ]); |
|
112
|
|
|
node.addClass(classes[ 0 ]); |
|
113
|
|
|
} |
|
114
|
|
|
} |
|
115
|
|
|
|
|
116
|
|
|
// Always switch CSS margins. |
|
117
|
|
|
var style = 'text-align'; |
|
118
|
|
|
var align = node.getStyle(style); |
|
119
|
|
|
|
|
120
|
|
|
if (align == 'left') |
|
121
|
|
|
node.setStyle(style, 'right'); |
|
122
|
|
|
else if (align == 'right') |
|
123
|
|
|
node.setStyle(style, 'left'); |
|
124
|
|
|
} |
|
125
|
|
|
} |
|
126
|
|
|
} |
|
127
|
|
|
|
|
128
|
|
|
JustifyCommand.prototype = { |
|
129
|
|
|
exec: function (editor) { |
|
130
|
|
|
var selection = editor.getSelection(), |
|
131
|
|
|
enterMode = editor.config.enterMode; |
|
132
|
|
|
|
|
133
|
|
|
if (!selection) |
|
134
|
|
|
return; |
|
135
|
|
|
|
|
136
|
|
|
var bookmarks = selection.createBookmarks(), |
|
137
|
|
|
ranges = selection.getRanges(); |
|
138
|
|
|
|
|
139
|
|
|
var cssClassName = this.cssClassName, |
|
140
|
|
|
iterator, block; |
|
141
|
|
|
|
|
142
|
|
|
var useComputedState = editor.config.useComputedState; |
|
143
|
|
|
useComputedState = useComputedState === undefined || useComputedState; |
|
144
|
|
|
|
|
145
|
|
|
for (var i = ranges.length - 1; i >= 0; i--) { |
|
146
|
|
|
iterator = ranges[ i ].createIterator(); |
|
147
|
|
|
iterator.enlargeBr = enterMode != CKEDITOR.ENTER_BR; |
|
148
|
|
|
|
|
149
|
|
|
while ((block = iterator.getNextParagraph(enterMode == CKEDITOR.ENTER_P ? 'p' : 'div'))) { |
|
150
|
|
|
if (block.isReadOnly()) |
|
151
|
|
|
continue; |
|
152
|
|
|
|
|
153
|
|
|
block.removeAttribute('align'); |
|
154
|
|
|
block.removeStyle('text-align'); |
|
155
|
|
|
|
|
156
|
|
|
// Remove any of the alignment classes from the className. |
|
157
|
|
|
var className = cssClassName && (block.$.className = CKEDITOR.tools.ltrim(block.$.className.replace(this.cssClassRegex, ''))); |
|
158
|
|
|
|
|
159
|
|
|
var apply = (this.state == CKEDITOR.TRISTATE_OFF) && (!useComputedState || (getAlignment(block, true) != this.value)); |
|
160
|
|
|
|
|
161
|
|
|
if (cssClassName) { |
|
162
|
|
|
// Append the desired class name. |
|
163
|
|
|
if (apply) |
|
164
|
|
|
block.addClass(cssClassName); |
|
165
|
|
|
else if (!className) |
|
166
|
|
|
block.removeAttribute('class'); |
|
167
|
|
|
} else if (apply) { |
|
168
|
|
|
block.setStyle('text-align', this.value); |
|
169
|
|
|
} |
|
170
|
|
|
} |
|
171
|
|
|
|
|
172
|
|
|
} |
|
173
|
|
|
|
|
174
|
|
|
editor.focus(); |
|
175
|
|
|
editor.forceNextSelectionCheck(); |
|
176
|
|
|
selection.selectBookmarks(bookmarks); |
|
177
|
|
|
}, |
|
178
|
|
|
refresh: function (editor, path) { |
|
179
|
|
|
var firstBlock = path.block || path.blockLimit; |
|
180
|
|
|
|
|
181
|
|
|
this.setState(firstBlock.getName() != 'body' && getAlignment(firstBlock, this.editor.config.useComputedState) == this.value ? CKEDITOR.TRISTATE_ON : CKEDITOR.TRISTATE_OFF); |
|
182
|
|
|
} |
|
183
|
|
|
}; |
|
184
|
|
|
|
|
185
|
|
|
CKEDITOR.plugins.add('justify', { |
|
186
|
|
|
// jscs:disable maximumLineLength |
|
187
|
|
|
lang: 'af,ar,bg,bn,bs,ca,cs,cy,da,de,el,en,en-au,en-ca,en-gb,eo,es,et,eu,fa,fi,fo,fr,fr-ca,gl,gu,he,hi,hr,hu,id,is,it,ja,ka,km,ko,ku,lt,lv,mk,mn,ms,nb,nl,no,pl,pt,pt-br,ro,ru,si,sk,sl,sq,sr,sr-latn,sv,th,tr,tt,ug,uk,vi,zh,zh-cn', // %REMOVE_LINE_CORE% |
|
188
|
|
|
// jscs:enable maximumLineLength |
|
189
|
|
|
icons: 'justifyblock,justifycenter,justifyleft,justifyright', // %REMOVE_LINE_CORE% |
|
190
|
|
|
hidpi: true, // %REMOVE_LINE_CORE% |
|
191
|
|
|
init: function (editor) { |
|
192
|
|
|
if (editor.blockless) |
|
193
|
|
|
return; |
|
194
|
|
|
|
|
195
|
|
|
var left = new JustifyCommand(editor, 'justifyleft', 'left'), |
|
196
|
|
|
center = new JustifyCommand(editor, 'justifycenter', 'center'), |
|
197
|
|
|
right = new JustifyCommand(editor, 'justifyright', 'right'), |
|
198
|
|
|
justify = new JustifyCommand(editor, 'justifyblock', 'justify'); |
|
199
|
|
|
|
|
200
|
|
|
editor.addCommand('justifyleft', left); |
|
201
|
|
|
editor.addCommand('justifycenter', center); |
|
202
|
|
|
editor.addCommand('justifyright', right); |
|
203
|
|
|
editor.addCommand('justifyblock', justify); |
|
204
|
|
|
|
|
205
|
|
|
if (editor.ui.addButton) { |
|
206
|
|
|
editor.ui.addButton('JustifyLeft', { |
|
207
|
|
|
label: editor.lang.justify.left, |
|
208
|
|
|
command: 'justifyleft', |
|
209
|
|
|
toolbar: 'align,10' |
|
210
|
|
|
}); |
|
211
|
|
|
editor.ui.addButton('JustifyCenter', { |
|
212
|
|
|
label: editor.lang.justify.center, |
|
213
|
|
|
command: 'justifycenter', |
|
214
|
|
|
toolbar: 'align,20' |
|
215
|
|
|
}); |
|
216
|
|
|
editor.ui.addButton('JustifyRight', { |
|
217
|
|
|
label: editor.lang.justify.right, |
|
218
|
|
|
command: 'justifyright', |
|
219
|
|
|
toolbar: 'align,30' |
|
220
|
|
|
}); |
|
221
|
|
|
editor.ui.addButton('JustifyBlock', { |
|
222
|
|
|
label: editor.lang.justify.block, |
|
223
|
|
|
command: 'justifyblock', |
|
224
|
|
|
toolbar: 'align,40' |
|
225
|
|
|
}); |
|
226
|
|
|
} |
|
227
|
|
|
|
|
228
|
|
|
editor.on('dirChanged', onDirChanged); |
|
229
|
|
|
} |
|
230
|
|
|
}); |
|
231
|
|
|
})(); |
|
232
|
|
|
|
|
233
|
|
|
/** |
|
234
|
|
|
* List of classes to use for aligning the contents. If it's `null`, no classes will be used |
|
235
|
|
|
* and instead the corresponding CSS values will be used. |
|
236
|
|
|
* |
|
237
|
|
|
* The array should contain 4 members, in the following order: left, center, right, justify. |
|
238
|
|
|
* |
|
239
|
|
|
* // Use the classes 'AlignLeft', 'AlignCenter', 'AlignRight', 'AlignJustify' |
|
240
|
|
|
* config.justifyClasses = [ 'AlignLeft', 'AlignCenter', 'AlignRight', 'AlignJustify' ]; |
|
241
|
|
|
* |
|
242
|
|
|
* @cfg {Array} [justifyClasses=null] |
|
243
|
|
|
* @member CKEDITOR.config |
|
244
|
|
|
*/ |
|
245
|
|
|
|