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