1
2
3
4
5
6
7
8 package org.vaadin.openesignforms.ckeditor.widgetset.client.ui;
9
10 import java.util.HashMap;
11 import java.util.LinkedList;
12 import java.util.Set;
13
14 import com.google.gwt.core.client.Scheduler;
15 import com.google.gwt.core.client.Scheduler.ScheduledCommand;
16 import com.google.gwt.dom.client.DivElement;
17 import com.google.gwt.dom.client.Document;
18 import com.google.gwt.dom.client.Style.Overflow;
19 import com.google.gwt.dom.client.Style.Visibility;
20 import com.google.gwt.user.client.ui.Focusable;
21 import com.google.gwt.user.client.ui.Widget;
22 import com.vaadin.client.ApplicationConnection;
23 import com.vaadin.client.LayoutManager;
24 import com.vaadin.client.Paintable;
25 import com.vaadin.client.UIDL;
26 import com.vaadin.client.VConsole;
27 import com.vaadin.client.ui.layout.ElementResizeEvent;
28 import com.vaadin.client.ui.layout.ElementResizeListener;
29 import com.vaadin.shared.EventId;
30
31
32
33
34
35 public class VCKEditorTextField extends Widget implements Paintable, CKEditorService.CKEditorListener, Focusable {
36
37
38 public static final String CLASSNAME = "v-ckeditortextfield";
39
40 public static final String ATTR_FOCUS = "focus";
41 public static final String ATTR_IMMEDIATE = "immediate";
42 public static final String ATTR_READONLY = "readonly";
43 public static final String ATTR_VIEW_WITHOUT_EDITOR = "viewWithoutEditor";
44 public static final String ATTR_INPAGECONFIG = "inPageConfig";
45 public static final String ATTR_PROTECTED_SOURCE = "protectedSource";
46 public static final String ATTR_WRITERRULES_TAGNAME = "writerRules.tagName";
47 public static final String ATTR_WRITERRULES_JSRULE = "writerRules.jsRule";
48 public static final String ATTR_WRITER_INDENTATIONCHARS = "writerIndentationChars";
49 public static final String ATTR_KEYSTROKES_KEYSTROKE = "keystrokes.keystroke";
50 public static final String ATTR_KEYSTROKES_COMMAND = "keystrokes.command";
51 public static final String ATTR_INSERT_HTML = "insert_html";
52 public static final String ATTR_INSERT_TEXT = "insert_text";
53 public static final String ATTR_PROTECTED_BODY = "protected_body";
54 public static final String VAR_TEXT = "text";
55 public static final String VAR_VAADIN_SAVE_BUTTON_PRESSED = "vaadinsave";
56 public static final String VAR_VERSION = "version";
57
58 public static final String EVENT_SELECTION_CHANGE = "selectionChange";
59
60 private static String ckeditorVersion;
61
62
63 protected String paintableId;
64
65
66 protected ApplicationConnection clientToServer;
67
68 private String dataBeforeEdit = null;
69
70 private boolean immediate;
71 private boolean readOnly;
72 private boolean viewWithoutEditor;
73 private boolean protectedBody;
74
75 private CKEditor ckEditor = null;
76 private boolean ckEditorIsReady = false;
77 private boolean resizeListenerInPlace = false;
78 private boolean notifyBlankSelection = false;
79
80 private LinkedList<String> protectedSourceList = null;
81 private HashMap<String,String> writerRules = null;
82 private String writerIndentationChars = null;
83 private HashMap<Integer,String> keystrokeMappings = null;
84
85 private int tabIndex;
86 private boolean setFocusAfterReady;
87 private boolean setTabIndexAfterReady;
88
89
90
91
92
93 public VCKEditorTextField() {
94
95
96 DivElement rootDiv = Document.get().createDivElement();
97 rootDiv.getStyle().setOverflow(Overflow.HIDDEN);
98 rootDiv.getStyle().setVisibility(Visibility.VISIBLE);
99 setElement(rootDiv);
100
101
102
103 setStyleName(CLASSNAME);
104 }
105
106
107
108
109 @Override
110 public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
111 clientToServer = client;
112 paintableId = uidl.getId();
113 boolean needsDataUpdate = false;
114 boolean needsProtectedBodyUpdate = false;
115 boolean readOnlyModeChanged = false;
116
117
118
119
120
121 if ( clientToServer.updateComponent(this, uidl, true) ) {
122 return;
123 }
124
125 if ( ! resizeListenerInPlace ) {
126 LayoutManager.get(client).addElementResizeListener(getElement(), new ElementResizeListener() {
127
128 @Override
129 public void onElementResize(ElementResizeEvent e) {
130 doResize();
131 }
132
133 });
134 resizeListenerInPlace = true;
135 }
136
137 if ( uidl.hasAttribute(ATTR_IMMEDIATE) ) {
138 immediate = uidl.getBooleanAttribute(ATTR_IMMEDIATE);
139 }
140 if ( uidl.hasAttribute(ATTR_READONLY) ) {
141 boolean newReadOnly = uidl.getBooleanAttribute(ATTR_READONLY);
142 readOnlyModeChanged = newReadOnly != readOnly;
143 readOnly = newReadOnly;
144 }
145 if ( uidl.hasAttribute(ATTR_VIEW_WITHOUT_EDITOR) ) {
146 viewWithoutEditor = uidl.getBooleanAttribute(ATTR_VIEW_WITHOUT_EDITOR);
147 }
148 if ( uidl.hasAttribute(ATTR_PROTECTED_BODY) ) {
149 boolean state = uidl.getBooleanAttribute(ATTR_PROTECTED_BODY);
150 if (protectedBody != state) {
151 protectedBody = state ;
152 needsProtectedBodyUpdate = true;
153 }
154 }
155 if ( uidl.hasVariable(VAR_TEXT) ) {
156 String data = uidl.getStringVariable(VAR_TEXT);
157 if ( ckEditor != null )
158 dataBeforeEdit = ckEditor.getData();
159 needsDataUpdate = ! data.equals(dataBeforeEdit);
160 dataBeforeEdit = data;
161 }
162
163
164 if ( ! paintableId.equals(getElement().getId()) ) {
165 getElement().setId(paintableId);
166 }
167
168 if ( viewWithoutEditor ) {
169 if ( ckEditor != null ) {
170
171 if ( ! needsDataUpdate ) {
172 dataBeforeEdit = ckEditor.getData();
173 }
174 ckEditor.destroy(true);
175 ckEditorIsReady = false;
176 ckEditor = null;
177 }
178 getElement().setInnerHTML(dataBeforeEdit);
179 }
180 else if ( ckEditor == null ) {
181 getElement().setInnerHTML("");
182
183 final String inPageConfig = uidl.hasAttribute(ATTR_INPAGECONFIG) ? uidl.getStringAttribute(ATTR_INPAGECONFIG) : null;
184
185 writerIndentationChars = uidl.hasAttribute(ATTR_WRITER_INDENTATIONCHARS) ? uidl.getStringAttribute(ATTR_WRITER_INDENTATIONCHARS) : null;
186
187 if ( uidl.hasAttribute(ATTR_FOCUS) ) {
188 setFocus(uidl.getBooleanAttribute(ATTR_FOCUS));
189 }
190
191
192 int i = 0;
193 while( true ) {
194 if ( ! uidl.hasAttribute(ATTR_WRITERRULES_TAGNAME+i) ) {
195 break;
196 }
197
198 String tagName = uidl.getStringAttribute(ATTR_WRITERRULES_TAGNAME+i);
199 String jsRule = uidl.getStringAttribute(ATTR_WRITERRULES_JSRULE+i);
200 if ( writerRules == null ) {
201 writerRules = new HashMap<String,String>();
202 }
203 writerRules.put(tagName, jsRule);
204 ++i;
205 }
206
207
208 i = 0;
209 while( true ) {
210 if ( ! uidl.hasAttribute(ATTR_KEYSTROKES_KEYSTROKE+i) ) {
211 break;
212 }
213
214 int keystroke = uidl.getIntAttribute(ATTR_KEYSTROKES_KEYSTROKE+i);
215 String command = uidl.getStringAttribute(ATTR_KEYSTROKES_COMMAND+i);
216 if ( keystrokeMappings == null ) {
217 keystrokeMappings = new HashMap<Integer,String>();
218 }
219 keystrokeMappings.put(keystroke, command);
220 ++i;
221 }
222
223
224 i = 0;
225 while( true ) {
226 if ( ! uidl.hasAttribute(ATTR_PROTECTED_SOURCE+i) ) {
227 break;
228 }
229
230 String regex = uidl.getStringAttribute(ATTR_PROTECTED_SOURCE+i);
231 if ( protectedSourceList == null ) {
232 protectedSourceList = new LinkedList<String>();
233 }
234 protectedSourceList.add(regex);
235 ++i;
236 }
237
238 ScheduledCommand scE = new ScheduledCommand() {
239 @Override
240 public void execute() {
241 ckEditor = loadEditor(inPageConfig);
242
243 }
244 };
245
246 CKEditorService.loadLibrary(scE);
247
248
249 } else if ( ckEditorIsReady ) {
250 if ( needsDataUpdate ) {
251 ckEditor.setData(dataBeforeEdit);
252 }
253
254 if ( needsProtectedBodyUpdate ) {
255 ckEditor.protectBody(protectedBody);
256 }
257
258 if (uidl.hasAttribute(ATTR_INSERT_HTML)) {
259 ckEditor.insertHtml(uidl.getStringAttribute(ATTR_INSERT_HTML));
260 }
261
262 if (uidl.hasAttribute(ATTR_INSERT_TEXT)) {
263 ckEditor.insertText(uidl.getStringAttribute(ATTR_INSERT_TEXT));
264 }
265
266 if ( uidl.hasAttribute(ATTR_FOCUS) ) {
267 setFocus(uidl.getBooleanAttribute(ATTR_FOCUS));
268 }
269
270 if ( readOnlyModeChanged ) {
271 ckEditor.setReadOnly(readOnly);
272 }
273 }
274
275 }
276
277
278
279
280
281
282
283 protected CKEditor loadEditor(String inPageConfig) {
284 return (CKEditor) CKEditorService.loadEditor(paintableId,
285 VCKEditorTextField.this,
286 inPageConfig,
287 VCKEditorTextField.super.getOffsetWidth(),
288 VCKEditorTextField.super.getOffsetHeight());
289 }
290
291
292 @Override
293 public void onSave() {
294 if ( ckEditorIsReady && ! readOnly ) {
295
296 String data = ckEditor.getData();
297 if ( ! data.equals(dataBeforeEdit) ) {
298 clientToServer.updateVariable(paintableId, VAR_TEXT, data, false);
299 dataBeforeEdit = data;
300 }
301 clientToServer.updateVariable(paintableId, VAR_VAADIN_SAVE_BUTTON_PRESSED,"",false);
302 clientToServer.sendPendingVariableChanges();
303 }
304 }
305
306
307 @Override
308 public void onBlur() {
309 if ( ckEditorIsReady ) {
310 boolean sendToServer = false;
311
312 if ( clientToServer.hasEventListeners(this, EventId.BLUR) ) {
313 sendToServer = true;
314 clientToServer.updateVariable(paintableId, EventId.BLUR, "", false);
315 }
316
317
318
319 if ( ! readOnly ) {
320 String data = ckEditor.getData();
321 if ( ! data.equals(dataBeforeEdit) ) {
322 clientToServer.updateVariable(paintableId, VAR_TEXT, data, false);
323 sendToServer = true;
324 dataBeforeEdit = data;
325 }
326 }
327
328 if (sendToServer) {
329 clientToServer.sendPendingVariableChanges();
330 }
331 }
332 }
333
334
335 @Override
336 public void onFocus() {
337 if ( ckEditorIsReady ) {
338 if ( clientToServer.hasEventListeners(this, EventId.FOCUS) ) {
339 clientToServer.updateVariable(paintableId, EventId.FOCUS, "", true);
340 }
341 }
342 }
343
344
345 @Override
346 public void onInstanceReady() {
347 ckEditor.instanceReady(this);
348
349 if ( writerRules != null ) {
350 Set<String> tagNameSet = writerRules.keySet();
351 for( String tagName : tagNameSet ) {
352 ckEditor.setWriterRules(tagName, writerRules.get(tagName));
353 }
354 writerRules = null;
355 }
356
357 if ( writerIndentationChars != null ) {
358 ckEditor.setWriterIndentationChars(writerIndentationChars);
359 writerIndentationChars = null;
360 }
361
362 if ( keystrokeMappings != null ) {
363 Set<Integer> keystrokeSet = keystrokeMappings.keySet();
364 for( Integer keystroke : keystrokeSet ) {
365 ckEditor.setKeystroke(keystroke, keystrokeMappings.get(keystroke));
366 }
367 keystrokeMappings = null;
368 }
369
370 if ( protectedSourceList != null ) {
371 for( String regex : protectedSourceList ) {
372 ckEditor.pushProtectedSource(regex);
373 }
374 protectedSourceList = null;
375 }
376
377 if ( dataBeforeEdit != null ) {
378 ckEditor.setData(dataBeforeEdit);
379 }
380
381 ckEditorIsReady = true;
382
383 if (setFocusAfterReady) {
384 setFocus(true);
385 }
386
387 if ( setTabIndexAfterReady ) {
388 setTabIndex(tabIndex);
389 }
390
391 doResize();
392
393 if (protectedBody) {
394 ckEditor.protectBody(protectedBody);
395 }
396
397 ckEditor.setReadOnly(readOnly);
398
399 ckeditorVersion = CKEditorService.version();
400 clientToServer.updateVariable(paintableId, VAR_VERSION, ckeditorVersion, true);
401 }
402
403
404 @Override
405 public void onChange() {
406 if ( ckEditor != null && ! readOnly ) {
407 String data = ckEditor.getData();
408 if ( ! data.equals(dataBeforeEdit) ) {
409 clientToServer.updateVariable(paintableId, VAR_TEXT, data, immediate);
410 dataBeforeEdit = data;
411 }
412 }
413 }
414
415
416 @Override
417 public void onModeChange(String mode) {
418 if ( ckEditor != null ) {
419 if ( ! readOnly ) {
420 String data = ckEditor.getData();
421 if ( ! data.equals(dataBeforeEdit) ) {
422 clientToServer.updateVariable(paintableId, VAR_TEXT, data, true);
423 dataBeforeEdit = data;
424 }
425 }
426
427 if ("wysiwyg".equals(mode)) {
428 ckEditor.protectBody(protectedBody);
429 }
430 }
431 }
432
433
434 @Override
435 public void onSelectionChange() {
436 if ( ckEditorIsReady ) {
437 if ( clientToServer.hasEventListeners(this, EVENT_SELECTION_CHANGE) ) {
438 String html = ckEditor.getSelectedHtml();
439 if ( html == null )
440 html = "";
441
442 boolean isBlankSelection = "".equals(html);
443 if ( ! isBlankSelection || notifyBlankSelection ) {
444 clientToServer.updateVariable(paintableId, EVENT_SELECTION_CHANGE, html, true);
445 notifyBlankSelection = ! isBlankSelection;
446 }
447 }
448 }
449 }
450
451
452 @Override
453 public void onDataReady() {
454 if ( ckEditor != null ) {
455 ckEditor.protectBody(protectedBody);
456 }
457 }
458
459 @Override
460 public void setWidth(String width) {
461 super.setWidth(width);
462 doResize();
463 }
464
465 @Override
466 public void setHeight(String height) {
467 super.setHeight(height);
468 doResize();
469 }
470
471 protected void doResize() {
472 if (ckEditorIsReady) {
473 Scheduler.get().scheduleDeferred(new ScheduledCommand() {
474 @Override
475 public void execute() {
476 ckEditor.resize(VCKEditorTextField.super.getOffsetWidth(), VCKEditorTextField.super.getOffsetHeight());
477 }
478 });
479 }
480 }
481
482 @Override
483 protected void onUnload() {
484 if ( ckEditor != null ) {
485 ckEditor.destroy();
486 ckEditor = null;
487 }
488 ckEditorIsReady = false;
489 }
490
491 @Override
492 public int getTabIndex() {
493 if (ckEditorIsReady) {
494 return ckEditor.getTabIndex();
495 } else {
496 return tabIndex;
497 }
498 }
499
500 @Override
501 public void setTabIndex(int tabIndex) {
502 if (ckEditorIsReady) {
503 ckEditor.setTabIndex(tabIndex);
504 } else {
505 setTabIndexAfterReady = true;
506 }
507 this.tabIndex = tabIndex;
508 }
509
510 @Override
511 public void setAccessKey(char arg0) {
512 return;
513 }
514
515 @Override
516 public void setFocus(boolean arg0) {
517 if (arg0) {
518 if (ckEditorIsReady)
519 ckEditor.focus();
520 else
521 setFocusAfterReady = true;
522 } else {
523 setFocusAfterReady = false;
524 }
525 }
526
527 }