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 package info.magnolia.ui.vaadin.gwt.client.richtext;
35
36 import info.magnolia.ui.vaadin.gwt.client.dialog.widget.OverlayWidget;
37 import info.magnolia.ui.vaadin.gwt.client.form.widget.FormView;
38 import info.magnolia.ui.vaadin.gwt.client.jquerywrapper.JQueryWrapper;
39 import info.magnolia.ui.vaadin.richtext.TextAreaStretcher;
40
41 import com.google.gwt.core.client.Scheduler;
42 import com.google.gwt.dom.client.Element;
43 import com.google.gwt.dom.client.Style;
44 import com.google.gwt.event.logical.shared.AttachEvent;
45 import com.google.gwt.user.client.DOM;
46 import com.google.gwt.user.client.ui.Widget;
47 import com.googlecode.mgwt.dom.client.event.touch.TouchEndEvent;
48 import com.googlecode.mgwt.dom.client.event.touch.TouchEndHandler;
49 import com.googlecode.mgwt.ui.client.widget.touch.TouchDelegate;
50 import com.vaadin.client.ComponentConnector;
51 import com.vaadin.client.ComputedStyle;
52 import com.vaadin.client.LayoutManager;
53 import com.vaadin.client.ServerConnector;
54 import com.vaadin.client.Util;
55 import com.vaadin.client.communication.StateChangeEvent;
56 import com.vaadin.client.extensions.AbstractExtensionConnector;
57 import com.vaadin.client.ui.layout.ElementResizeEvent;
58 import com.vaadin.client.ui.layout.ElementResizeListener;
59 import com.vaadin.client.ui.ui.UIConnector;
60 import com.vaadin.shared.ui.Connect;
61
62
63
64
65 @Connect(TextAreaStretcher.class)
66 public class TextAreaStretcherConnector extends AbstractExtensionConnector {
67
68 public static final String STRETCHER_BASE = "textarea-stretcher";
69 public static final String STRETCHED = "stretched";
70 public static final String COLLAPSED = "collapsed";
71 public static final String CKEDITOR_TOOLBOX = ".cke_top";
72 public static final String TEXTAREA_STRETCHED = "textarea-stretched";
73
74 public static final String RICH_TEXT_STYLE_NAME = "rich-text";
75 public static final String SIMPLE_STYLE_NAME = "simple";
76
77 public static final int DELAY_MS = 500;
78
79 private Widget form;
80 private Widget dialog;
81 private Widget textWidget;
82
83 private Element stretchControl = DOM.createDiv();
84 private WindowResizeListener windowResizeListener = new WindowResizeListener();
85
86 private boolean isOverlay = false;
87 private boolean isRichTextEditor = false;
88
89 private StateChangeEvent.StateChangeHandler textAreaSizeHandler = new StateChangeEvent.StateChangeHandler() {
90 @Override
91 public void onStateChanged(StateChangeEvent stateChangeEvent) {
92 stretchTextArea(textWidget.getElement().getStyle());
93 }
94 };
95
96 private ElementResizeListener formResizeListener = new ElementResizeListener() {
97 @Override
98 public void onElementResize(ElementResizeEvent e) {
99 if (isRichTextEditor) {
100 Scheduler.get().scheduleDeferred(new Scheduler.ScheduledCommand() {
101 @Override
102 public void execute() {
103 updateSize();
104 }
105 });
106 } else {
107 updateSize();
108 }
109 }
110 };;
111
112 @Override
113 public void onStateChanged(StateChangeEvent stateChangeEvent) {
114 super.onStateChanged(stateChangeEvent);
115 if (stateChangeEvent.hasPropertyChanged("isCollapsed")) {
116 updateSize();
117 if (!getState().isCollapsed) {
118 registerSizeChangeListeners();
119 }
120 }
121 }
122
123 @Override
124 public ComponentConnector getParent() {
125 return (ComponentConnector) super.getParent();
126 }
127
128 @Override
129 protected void extend(ServerConnector target) {
130 this.textWidget = ((ComponentConnector)target).getWidget();
131 this.isRichTextEditor = target instanceof RichTextConnector;
132 this.stretchControl.setClassName(STRETCHER_BASE);
133 textWidget.addAttachHandler(new AttachEvent.Handler() {
134 @Override
135 public void onAttachOrDetach(AttachEvent attachEvent) {
136 initFormView();
137 initDialog();
138 checkOverlay();
139 if (!isRichTextEditor) {
140 appendStretcher(textWidget.getElement());
141 stretchControl.addClassName(SIMPLE_STYLE_NAME);
142 } else {
143 Scheduler.get().scheduleFixedDelay(new Scheduler.RepeatingCommand() {
144 private int repeats = 0;
145
146 @Override
147 public boolean execute() {
148 repeats++;
149 isRichTextEditor = true;
150 final Element toolbox = JQueryWrapper.select(textWidget).find(CKEDITOR_TOOLBOX).get(0);
151 if (toolbox != null) {
152 appendStretcher(toolbox);
153 stretchControl.addClassName(RICH_TEXT_STYLE_NAME);
154 }
155 return toolbox == null && repeats < 5;
156 }
157 }, DELAY_MS);
158 }
159 }
160 });
161 }
162
163 @Override
164 public TextAreaStretcherState getState() {
165 return (TextAreaStretcherState) super.getState();
166 }
167
168 private void appendStretcher(Element rootElement) {
169 rootElement.getStyle().setPosition(Style.Position.RELATIVE);
170 rootElement.getParentElement().insertAfter(stretchControl, rootElement);
171 Widget parent = textWidget.getParent();
172 TouchDelegate touchDelegate = new TouchDelegate(parent);
173 touchDelegate.addTouchEndHandler(new TouchEndHandler() {
174 @Override
175 public void onTouchEnd(TouchEndEvent event) {
176 Element target = event.getNativeEvent().getEventTarget().cast();
177 if (stretchControl.isOrHasChild(target)) {
178 if (!getState().isCollapsed) {
179 unregisterSizeChangeListeners();
180 }
181 getRpcProxy(TextAreaStretcherServerRpc.class).toggle(textWidget.getOffsetWidth(), textWidget.getOffsetHeight());
182
183 }
184 }
185 });
186 }
187
188 private void registerSizeChangeListeners() {
189 final LayoutManager lm = getParent().getLayoutManager();
190 final UIConnector ui = getConnection().getUIConnector();
191 getParent().addStateChangeHandler(textAreaSizeHandler);
192 lm.addElementResizeListener(ui.getWidget().getElement(), windowResizeListener);
193
194 final ComponentConnector formConnector = Util.findConnectorFor(this.form);
195 if (formConnector != null) {
196 formConnector.getLayoutManager().addElementResizeListener(this.form.getElement(), formResizeListener);
197 }
198 }
199
200 private void updateSize() {
201 if (!getState().isCollapsed) {
202 stretchControl.replaceClassName("icon-open-fullscreen-2", "icon-close-fullscreen-2");
203 stretchControl.replaceClassName(COLLAPSED, STRETCHED);
204 form.asWidget().addStyleName("textarea-stretched");
205
206 Style style = textWidget.getElement().getStyle();
207 style.setPosition(Style.Position.ABSOLUTE);
208 Element header = getDialogHeaderElement();
209 ComputedStyle headerCS = new ComputedStyle(header);
210
211 int top = form.getAbsoluteTop() - dialog.getAbsoluteTop();
212 top = isOverlay ? top : top + headerCS.getPadding()[0] + headerCS.getPadding()[2];
213
214 int left = isOverlay ? 0 : form.getAbsoluteLeft();
215
216 style.setLeft(left, Style.Unit.PX);
217 style.setTop(top, Style.Unit.PX);
218
219 stretchTextArea(style);
220 style.setZIndex(5);
221
222 if (!isOverlay && !isRichTextEditor) {
223 stretchControl.getStyle().setTop(top + 5, Style.Unit.PX);
224 stretchControl.getStyle().setLeft(left + textWidget.getOffsetWidth() - stretchControl.getOffsetWidth() - 5, Style.Unit.PX);
225
226 }
227
228 hideOtherStretchers();
229 } else {
230 stretchControl.replaceClassName(STRETCHED, COLLAPSED);
231 stretchControl.replaceClassName("icon-close-fullscreen-2", "icon-open-fullscreen-2");
232 form.asWidget().removeStyleName(TEXTAREA_STRETCHED);
233 clearTraces();
234 }
235 }
236
237 @Override
238 public void onUnregister() {
239 super.onUnregister();
240 clearTraces();
241 }
242
243 private void hideOtherStretchers() {
244 JQueryWrapper.select("." + STRETCHER_BASE).setCss("display", "none");
245 this.stretchControl.getStyle().setDisplay(Style.Display.BLOCK);
246 }
247
248 private void clearTraces() {
249 Style style = textWidget.getElement().getStyle();
250 style.clearLeft();
251 style.clearTop();
252 style.clearPosition();
253 style.clearZIndex();
254
255 stretchControl.getStyle().clearTop();
256 stretchControl.getStyle().clearLeft();
257
258 JQueryWrapper.select("." + STRETCHER_BASE).setCss("display", "");
259 }
260
261 private void stretchTextArea(Style style) {
262 style.setWidth(form.getOffsetWidth(), Style.Unit.PX);
263 adjustTextAreaHeightToScreen(getConnection().getUIConnector().getWidget().getOffsetHeight());
264 }
265
266 private void unregisterSizeChangeListeners() {
267 final LayoutManager lm = getParent().getLayoutManager();
268 final UIConnector ui = getConnection().getUIConnector();
269 if (ui != null) {
270 getParent().removeStateChangeHandler(textAreaSizeHandler);
271 lm.removeElementResizeListener(ui.getWidget().getElement(), windowResizeListener);
272 }
273
274 final ComponentConnector formConnector = Util.findConnectorFor(this.form);
275 if (formConnector != null) {
276 formConnector.getLayoutManager().removeElementResizeListener(this.form.getElement(), formResizeListener);
277 }
278 }
279
280 private Element getDialogHeaderElement() {
281 return JQueryWrapper.select(dialog.asWidget()).find(".dialog-header").get(0);
282 }
283
284 private void checkOverlay() {
285 Widget it = this.dialog.asWidget();
286 while (it != null && !isOverlay) {
287 it = it.getParent();
288 this.isOverlay = it instanceof OverlayWidget;
289 }
290 }
291
292 private void initDialog() {
293 this.dialog = form.getParent();
294 }
295
296 private void initFormView() {
297 Widget it = textWidget;
298 while (it != null && !(it instanceof FormView)) {
299 it = it.getParent();
300 }
301 this.form = (it instanceof FormView) ? it : null;
302 }
303
304 private void adjustTextAreaHeightToScreen(int uiHeight) {
305 int formTop = form.getAbsoluteTop();
306 textWidget.setHeight((uiHeight - formTop) + "px");
307 }
308
309 private class WindowResizeListener implements ElementResizeListener {
310 @Override
311 public void onElementResize(ElementResizeEvent e) {
312 adjustTextAreaHeightToScreen(e.getLayoutManager().getOuterHeight(e.getElement()));
313 }
314 }
315
316 }