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 import java.util.logging.Logger;
14
15 import com.google.gwt.core.client.Scheduler;
16 import com.google.gwt.core.client.Scheduler.ScheduledCommand;
17 import com.google.gwt.dom.client.DivElement;
18 import com.google.gwt.dom.client.Document;
19 import com.google.gwt.dom.client.Style.Overflow;
20 import com.google.gwt.dom.client.Style.Visibility;
21 import com.google.gwt.user.client.ui.Focusable;
22 import com.google.gwt.user.client.ui.Widget;
23 import com.vaadin.client.ApplicationConnection;
24 import com.vaadin.client.LayoutManager;
25 import com.vaadin.client.Paintable;
26 import com.vaadin.client.UIDL;
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 @SuppressWarnings("deprecation")
36 public class VCKEditorTextField extends Widget implements Paintable, CKEditorService.CKEditorListener, Focusable {
37
38
39 public static final String CLASSNAME = "v-ckeditortextfield";
40
41 public static final String ATTR_FOCUS = "focus";
42 public static final String ATTR_IMMEDIATE = "immediate";
43 public static final String ATTR_READONLY = "readonly";
44 public static final String ATTR_VIEW_WITHOUT_EDITOR = "viewWithoutEditor";
45 public static final String ATTR_INPAGECONFIG = "inPageConfig";
46 public static final String ATTR_PROTECTED_SOURCE = "protectedSource";
47 public static final String ATTR_WRITERRULES_TAGNAME = "writerRules.tagName";
48 public static final String ATTR_WRITERRULES_JSRULE = "writerRules.jsRule";
49 public static final String ATTR_WRITER_INDENTATIONCHARS = "writerIndentationChars";
50 public static final String ATTR_KEYSTROKES_KEYSTROKE = "keystrokes.keystroke";
51 public static final String ATTR_KEYSTROKES_COMMAND = "keystrokes.command";
52 public static final String ATTR_INSERT_HTML = "insert_html";
53 public static final String ATTR_INSERT_TEXT = "insert_text";
54 public static final String ATTR_PROTECTED_BODY = "protected_body";
55 public static final String VAR_TEXT = "text";
56 public static final String VAR_VAADIN_SAVE_BUTTON_PRESSED = "vaadinsave";
57 public static final String VAR_VERSION = "version";
58
59 public static final String EVENT_SELECTION_CHANGE = "selectionChange";
60
61 private static String ckeditorVersion;
62
63
64 protected String paintableId;
65 protected String ckeditorContainerId = null;
66
67
68 protected ApplicationConnection clientToServer;
69
70 private String inPageConfig = null;
71 private String dataBeforeEdit = null;
72 private boolean ignoreDataChangesUntilReady = false;
73
74 private boolean immediate;
75 private boolean readOnly;
76 private boolean viewWithoutEditor;
77 private boolean protectedBody;
78
79 private CKEditor ckEditor = null;
80 private boolean ckEditorIsBeingLoaded = false;
81 private boolean ckEditorIsReady = false;
82 private boolean resizeListenerInPlace = false;
83 private boolean notifyBlankSelection = false;
84
85 private LinkedList<String> protectedSourceList = null;
86 private HashMap<String,String> writerRules = null;
87 private String writerIndentationChars = null;
88 private HashMap<Integer,String> keystrokeMappings = null;
89
90 private int tabIndex;
91 private boolean setFocusAfterReady;
92 private boolean setTabIndexAfterReady;
93
94
95
96
97
98 public VCKEditorTextField() {
99
100
101 DivElement rootDiv = Document.get().createDivElement();
102 rootDiv.getStyle().setOverflow(Overflow.HIDDEN);
103 rootDiv.getStyle().setVisibility(Visibility.VISIBLE);
104 setElement(rootDiv);
105
106
107
108 setStyleName(CLASSNAME);
109 }
110
111
112
113
114 @Override
115 public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
116 clientToServer = client;
117 paintableId = uidl.getId();
118
119
120
121 boolean needsDataUpdate = false;
122 boolean needsProtectedBodyUpdate = false;
123 boolean readOnlyModeChanged = false;
124
125
126
127
128
129 if ( clientToServer.updateComponent(this, uidl, true) ) {
130 return;
131 }
132
133 if ( ! resizeListenerInPlace ) {
134
135 LayoutManager.get(client).addElementResizeListener(getElement(), new ElementResizeListener() {
136
137 @Override
138 public void onElementResize(ElementResizeEvent e) {
139 doResize();
140 }
141 });
142 resizeListenerInPlace = true;
143 }
144
145 if ( uidl.hasAttribute(ATTR_IMMEDIATE) ) {
146 immediate = uidl.getBooleanAttribute(ATTR_IMMEDIATE);
147 }
148 if ( uidl.hasAttribute(ATTR_READONLY) ) {
149 boolean newReadOnly = uidl.getBooleanAttribute(ATTR_READONLY);
150 readOnlyModeChanged = newReadOnly != readOnly;
151 readOnly = newReadOnly;
152 }
153 if ( uidl.hasAttribute(ATTR_VIEW_WITHOUT_EDITOR) ) {
154 viewWithoutEditor = uidl.getBooleanAttribute(ATTR_VIEW_WITHOUT_EDITOR);
155 }
156 if ( uidl.hasAttribute(ATTR_PROTECTED_BODY) ) {
157 boolean state = uidl.getBooleanAttribute(ATTR_PROTECTED_BODY);
158 if (protectedBody != state) {
159 protectedBody = state ;
160 needsProtectedBodyUpdate = true;
161 }
162 }
163 if ( uidl.hasVariable(VAR_TEXT) ) {
164 String data = uidl.getStringVariable(VAR_TEXT);
165 if ( ckEditor != null )
166 dataBeforeEdit = ckEditor.getData();
167 needsDataUpdate = ! data.equals(dataBeforeEdit);
168
169 dataBeforeEdit = data;
170 }
171
172
173 if ( ckeditorContainerId == null )
174 ckeditorContainerId = "VCKE_" + paintableId;
175 if ( ! ckeditorContainerId.equals(getElement().getId()) ) {
176 getElement().setId(ckeditorContainerId);
177 }
178
179 if ( viewWithoutEditor ) {
180 if ( ckEditor != null ) {
181
182 if ( ! needsDataUpdate ) {
183 dataBeforeEdit = ckEditor.getData();
184 }
185
186 unloadEditor();
187 }
188 getElement().setInnerHTML(dataBeforeEdit);
189 }
190 else if ( ckEditor == null ) {
191
192 getElement().setInnerHTML("");
193
194
195 if ( uidl.hasAttribute(ATTR_INPAGECONFIG) )
196 inPageConfig = uidl.getStringAttribute(ATTR_INPAGECONFIG);
197
198 if ( uidl.hasAttribute(ATTR_WRITER_INDENTATIONCHARS) )
199 writerIndentationChars = uidl.getStringAttribute(ATTR_WRITER_INDENTATIONCHARS);
200
201 if ( uidl.hasAttribute(ATTR_FOCUS) ) {
202 setFocus(uidl.getBooleanAttribute(ATTR_FOCUS));
203 }
204
205
206 int i = 0;
207 while( true ) {
208 if ( ! uidl.hasAttribute(ATTR_WRITERRULES_TAGNAME+i) ) {
209 break;
210 }
211 if ( i == 0 && writerRules != null )
212 writerRules.clear();
213
214 String tagName = uidl.getStringAttribute(ATTR_WRITERRULES_TAGNAME+i);
215 String jsRule = uidl.getStringAttribute(ATTR_WRITERRULES_JSRULE+i);
216 if ( writerRules == null ) {
217 writerRules = new HashMap<String,String>();
218 }
219 writerRules.put(tagName, jsRule);
220 ++i;
221 }
222
223
224 i = 0;
225 while( true ) {
226 if ( ! uidl.hasAttribute(ATTR_KEYSTROKES_KEYSTROKE+i) ) {
227 break;
228 }
229 if ( i == 0 && keystrokeMappings != null )
230 keystrokeMappings.clear();
231
232 int keystroke = uidl.getIntAttribute(ATTR_KEYSTROKES_KEYSTROKE+i);
233 String command = uidl.getStringAttribute(ATTR_KEYSTROKES_COMMAND+i);
234 if ( keystrokeMappings == null ) {
235 keystrokeMappings = new HashMap<Integer,String>();
236 }
237 keystrokeMappings.put(keystroke, command);
238 ++i;
239 }
240
241
242 i = 0;
243 while( true ) {
244 if ( ! uidl.hasAttribute(ATTR_PROTECTED_SOURCE+i) ) {
245 break;
246 }
247 if ( i == 0 && protectedSourceList != null )
248 protectedSourceList.clear();
249
250 String regex = uidl.getStringAttribute(ATTR_PROTECTED_SOURCE+i);
251 if ( protectedSourceList == null ) {
252 protectedSourceList = new LinkedList<String>();
253 }
254 protectedSourceList.add(regex);
255 ++i;
256 }
257
258
259 loadEditor();
260
261
262 } else if ( ckEditorIsReady ) {
263
264 if ( needsDataUpdate ) {
265 setEditorData(dataBeforeEdit);
266 }
267
268 if ( needsProtectedBodyUpdate ) {
269 ckEditor.protectBody(protectedBody);
270 }
271
272 if (uidl.hasAttribute(ATTR_INSERT_HTML)) {
273 ckEditor.insertHtml(uidl.getStringAttribute(ATTR_INSERT_HTML));
274 }
275
276 if (uidl.hasAttribute(ATTR_INSERT_TEXT)) {
277 ckEditor.insertText(uidl.getStringAttribute(ATTR_INSERT_TEXT));
278 }
279
280 if ( uidl.hasAttribute(ATTR_FOCUS) ) {
281 setFocus(uidl.getBooleanAttribute(ATTR_FOCUS));
282 }
283
284 if ( readOnlyModeChanged ) {
285 ckEditor.setReadOnly(readOnly);
286 }
287 }
288 }
289
290 void setEditorData(String html) {
291 if ( ckEditorIsReady ) {
292 dataBeforeEdit = html;
293 ignoreDataChangesUntilReady = true;
294 ckEditor.setData(dataBeforeEdit);
295 }
296 }
297
298 void unloadEditor() {
299 if ( ckEditor != null ) {
300
301 ckEditor.destroy(true);
302 ckEditor = null;
303 } else {
304
305 }
306 ignoreDataChangesUntilReady = false;
307 ckEditorIsReady = false;
308 ckEditorIsBeingLoaded = false;
309 setFocusAfterReady = false;
310 setTabIndexAfterReady = false;
311 }
312
313 protected void loadEditor() {
314 if ( ckEditor == null && inPageConfig != null && ! ckEditorIsBeingLoaded ) {
315 ckEditorIsBeingLoaded = true;
316
317 CKEditorService.loadLibrary(new ScheduledCommand() {
318 @Override
319 public void execute() {
320
321 ckEditor = loadEditor(inPageConfig);
322 ckEditorIsBeingLoaded = false;
323
324 }
325 });
326 } else {
327
328 }
329 }
330
331
332
333
334
335
336
337 protected CKEditor loadEditor(String inPageConfig) {
338 return (CKEditor) CKEditorService.loadEditor(ckeditorContainerId,
339 VCKEditorTextField.this,
340 inPageConfig,
341 VCKEditorTextField.super.getOffsetWidth(),
342 VCKEditorTextField.super.getOffsetHeight());
343 }
344
345
346
347 @Override
348 public void onSave() {
349
350 if ( ckEditorIsReady && ! readOnly ) {
351 ignoreDataChangesUntilReady = false;
352 String data = ckEditor.getData();
353 if ( ! data.equals(dataBeforeEdit) ) {
354
355 clientToServer.updateVariable(paintableId, VAR_TEXT, data, false);
356 dataBeforeEdit = data;
357 } else {
358
359 }
360 clientToServer.updateVariable(paintableId, VAR_VAADIN_SAVE_BUTTON_PRESSED,"",false);
361 logState("onSave() - sending pending changes to the server; rpc queue count: " + clientToServer.getServerRpcQueue().size() + "; isFlushPending: " + clientToServer.getServerRpcQueue().isFlushPending());
362 clientToServer.getServerRpcQueue().flush();
363 logState("onSave() - after sent pending changes to the server; rpc queue count: " + clientToServer.getServerRpcQueue().size() + "; isFlushPending: " + clientToServer.getServerRpcQueue().isFlushPending());
364 }
365 }
366
367
368 @Override
369 public void onBlur() {
370
371 if ( ckEditorIsReady ) {
372 boolean sendToServer = false;
373
374 if ( clientToServer.hasEventListeners(this, EventId.BLUR) ) {
375
376 sendToServer = true;
377 clientToServer.updateVariable(paintableId, EventId.BLUR, "", false);
378 }
379 else {
380
381 }
382
383
384
385 if ( ! readOnly && ! ignoreDataChangesUntilReady ) {
386 String data = ckEditor.getData();
387 if ( ! data.equals(dataBeforeEdit) ) {
388
389 clientToServer.updateVariable(paintableId, VAR_TEXT, data, false);
390 sendToServer = true;
391 dataBeforeEdit = data;
392 } else {
393
394 }
395 } else {
396
397 }
398
399 if (sendToServer) {
400 logState("onBlur() - flushing changes to server; rpc queue count: " + clientToServer.getServerRpcQueue().size() + "; isFlushPending: " + clientToServer.getServerRpcQueue().isFlushPending());
401 clientToServer.getServerRpcQueue().flush();
402 logState("onBlur() - after flushed changes to server; rpc queue count: " + clientToServer.getServerRpcQueue().size() + "; isFlushPending: " + clientToServer.getServerRpcQueue().isFlushPending());
403 }
404 }
405 }
406
407
408 @Override
409 public void onFocus() {
410 if ( ckEditorIsReady ) {
411 if ( clientToServer.hasEventListeners(this, EventId.FOCUS) ) {
412 clientToServer.updateVariable(paintableId, EventId.FOCUS, "", true);
413 }
414 }
415 }
416
417
418 @Override
419 public void onInstanceReady() {
420 ckEditorIsReady = true;
421
422
423 ckEditor.instanceReady(this);
424
425 if ( writerRules != null ) {
426
427 Set<String> tagNameSet = writerRules.keySet();
428 for( String tagName : tagNameSet ) {
429 ckEditor.setWriterRules(tagName, writerRules.get(tagName));
430 }
431 }
432
433 if ( writerIndentationChars != null ) {
434
435 ckEditor.setWriterIndentationChars(writerIndentationChars);
436 }
437
438 if ( keystrokeMappings != null ) {
439 Set<Integer> keystrokeSet = keystrokeMappings.keySet();
440
441 for( Integer keystroke : keystrokeSet ) {
442 ckEditor.setKeystroke(keystroke, keystrokeMappings.get(keystroke));
443 }
444 }
445
446 if ( protectedSourceList != null ) {
447
448 for( String regex : protectedSourceList ) {
449 ckEditor.pushProtectedSource(regex);
450 }
451 }
452
453 if ( dataBeforeEdit != null ) {
454
455 setEditorData(dataBeforeEdit);
456 }
457
458
459 if (setFocusAfterReady) {
460
461 setFocus(true);
462 }
463
464 if ( setTabIndexAfterReady ) {
465
466 setTabIndex(tabIndex);
467 }
468
469
470 doResize();
471
472 if (protectedBody) {
473
474 ckEditor.protectBody(protectedBody);
475 }
476
477
478 ckEditor.setReadOnly(readOnly);
479
480 ckeditorVersion = CKEditorService.version();
481
482 clientToServer.updateVariable(paintableId, VAR_VERSION, ckeditorVersion, true);
483 }
484
485
486 @Override
487 public void onChange() {
488 if ( ckEditor != null && ! readOnly && ! ignoreDataChangesUntilReady ) {
489
490 String data = ckEditor.getData();
491 if ( ! data.equals(dataBeforeEdit) ) {
492
493 clientToServer.updateVariable(paintableId, VAR_TEXT, data, immediate);
494 dataBeforeEdit = data;
495 } else {
496
497 }
498 } else {
499
500 }
501 }
502
503
504 @Override
505 public void onModeChange(String mode) {
506 if ( ckEditor != null ) {
507
508 if ( ! readOnly ) {
509 String data = ckEditor.getData();
510 if ( ! data.equals(dataBeforeEdit) ) {
511
512 clientToServer.updateVariable(paintableId, VAR_TEXT, data, immediate);
513 dataBeforeEdit = data;
514 ignoreDataChangesUntilReady = false;
515 } else {
516
517 }
518 } else {
519
520 }
521
522 if ("wysiwyg".equals(mode)) {
523 ckEditor.protectBody(protectedBody);
524 }
525 }
526 }
527
528
529 @Override
530 public void onSelectionChange() {
531 if ( ckEditorIsReady ) {
532 if ( clientToServer.hasEventListeners(this, EVENT_SELECTION_CHANGE) ) {
533
534 String html = ckEditor.getSelectedHtml();
535 if ( html == null )
536 html = "";
537
538 boolean isBlankSelection = "".equals(html);
539 if ( ! isBlankSelection || notifyBlankSelection ) {
540 clientToServer.updateVariable(paintableId, EVENT_SELECTION_CHANGE, html, true);
541 notifyBlankSelection = ! isBlankSelection;
542 }
543 }
544 }
545 }
546
547
548 @Override
549 public void onDataReady() {
550 if ( ckEditorIsReady ) {
551
552 String data = ckEditor.getData();
553 if ( ! data.equals(dataBeforeEdit) && ! ignoreDataChangesUntilReady ) {
554 clientToServer.updateVariable(paintableId, VAR_TEXT, data, immediate);
555 }
556 ignoreDataChangesUntilReady = false;
557 dataBeforeEdit = data;
558 ckEditor.protectBody(protectedBody);
559 } else {
560
561 }
562 }
563
564 @Override
565 public void setWidth(String width) {
566 super.setWidth(width);
567 doResize();
568 }
569
570 @Override
571 public void setHeight(String height) {
572 super.setHeight(height);
573 doResize();
574 }
575
576 protected void doResize() {
577 if (ckEditorIsReady) {
578
579 Scheduler.get().scheduleDeferred(new ScheduledCommand() {
580 @Override
581 public void execute() {
582
583 ckEditor.resize(VCKEditorTextField.super.getOffsetWidth(), VCKEditorTextField.super.getOffsetHeight());
584
585 }
586 });
587 } else {
588
589 }
590 }
591
592 private void logState(String methodMessage) {
593 Logger.getGlobal().info("VCKEditorTextField." + methodMessage + "; ckEditor: " + (ckEditor==null?"None":ckEditor.getId()) +
594 "; ckEditorIsReady: " + ckEditorIsReady + "; ckEditorIsLoading: " + ckEditorIsBeingLoaded + "; ignoreDataChangesUntilReady: " + ignoreDataChangesUntilReady + "; paintableId: " + paintableId);
595 }
596
597 @Override
598 protected void onLoad() {
599
600 if ( ! viewWithoutEditor ) {
601 loadEditor();
602 }
603 }
604
605 @Override
606 protected void onUnload() {
607
608 unloadEditor();
609 }
610
611 @Override
612 public int getTabIndex() {
613 if (ckEditorIsReady) {
614 return ckEditor.getTabIndex();
615 } else {
616 return tabIndex;
617 }
618 }
619
620 @Override
621 public void setTabIndex(int tabIndex) {
622 if (ckEditorIsReady) {
623 ckEditor.setTabIndex(tabIndex);
624 } else {
625 setTabIndexAfterReady = true;
626 }
627 this.tabIndex = tabIndex;
628 }
629
630 @Override
631 public void setAccessKey(char arg0) {
632 return;
633 }
634
635 @Override
636 public void setFocus(boolean arg0) {
637 if (arg0) {
638 if (ckEditorIsReady)
639 ckEditor.focus();
640 else
641 setFocusAfterReady = true;
642 } else {
643 setFocusAfterReady = false;
644 }
645 }
646
647 }