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 if (attachEvent.isAttached()) {
137 initFormView();
138 initDialog();
139 checkOverlay();
140 if (!isRichTextEditor) {
141 appendStretcher(textWidget.getElement());
142 stretchControl.addClassName(SIMPLE_STYLE_NAME);
143 } else {
144 Scheduler.get().scheduleFixedDelay(new Scheduler.RepeatingCommand() {
145 private int repeats = 0;
146
147 @Override
148 public boolean execute() {
149 repeats++;
150 isRichTextEditor = true;
151 final Element toolbox = JQueryWrapper.select(textWidget).find(CKEDITOR_TOOLBOX).get(0);
152 if (toolbox != null) {
153 appendStretcher(toolbox);
154 stretchControl.addClassName(RICH_TEXT_STYLE_NAME);
155 }
156 return toolbox == null && repeats < 5;
157 }
158 }, DELAY_MS);
159 }
160 } else {
161 clearTraces();
162 }
163 }
164 });
165 }
166
167 @Override
168 public TextAreaStretcherState getState() {
169 return (TextAreaStretcherState) super.getState();
170 }
171
172 private void appendStretcher(Element rootElement) {
173 rootElement.getStyle().setPosition(Style.Position.RELATIVE);
174 rootElement.getParentElement().insertAfter(stretchControl, rootElement);
175 Widget parent = textWidget.getParent();
176 TouchDelegate touchDelegate = new TouchDelegate(parent);
177 touchDelegate.addTouchEndHandler(new TouchEndHandler() {
178 @Override
179 public void onTouchEnd(TouchEndEvent event) {
180 Element target = event.getNativeEvent().getEventTarget().cast();
181 if (stretchControl.isOrHasChild(target)) {
182 if (!getState().isCollapsed) {
183 unregisterSizeChangeListeners();
184 }
185 getRpcProxy(TextAreaStretcherServerRpc.class).toggle(textWidget.getOffsetWidth(), textWidget.getOffsetHeight());
186
187 }
188 }
189 });
190 }
191
192 private void registerSizeChangeListeners() {
193 final LayoutManager lm = getParent().getLayoutManager();
194 final UIConnector ui = getConnection().getUIConnector();
195 getParent().addStateChangeHandler(textAreaSizeHandler);
196 lm.addElementResizeListener(ui.getWidget().getElement(), windowResizeListener);
197
198 final ComponentConnector formConnector = Util.findConnectorFor(this.form);
199 if (formConnector != null) {
200 formConnector.getLayoutManager().addElementResizeListener(this.form.getElement(), formResizeListener);
201 }
202 }
203
204 private void updateSize() {
205 if (!getState().isCollapsed) {
206 stretchControl.replaceClassName("icon-open-fullscreen-2", "icon-close-fullscreen-2");
207 stretchControl.replaceClassName(COLLAPSED, STRETCHED);
208 form.asWidget().addStyleName("textarea-stretched");
209
210 Style style = textWidget.getElement().getStyle();
211 style.setPosition(Style.Position.ABSOLUTE);
212 Element header = getDialogHeaderElement();
213 ComputedStyle headerCS = new ComputedStyle(header);
214
215 int top = form.getAbsoluteTop() - dialog.getAbsoluteTop();
216 top = isOverlay ? top : top + headerCS.getPadding()[0] + headerCS.getPadding()[2];
217
218 int left = isOverlay ? 0 : form.getAbsoluteLeft();
219
220 style.setLeft(left, Style.Unit.PX);
221 style.setTop(top, Style.Unit.PX);
222
223 stretchTextArea(style);
224 style.setZIndex(5);
225
226 if (!isOverlay && !isRichTextEditor) {
227 stretchControl.getStyle().setTop(top + 5, Style.Unit.PX);
228 stretchControl.getStyle().setLeft(left + textWidget.getOffsetWidth() - stretchControl.getOffsetWidth() - 5, Style.Unit.PX);
229
230 }
231
232 hideOtherStretchers();
233 } else {
234 stretchControl.replaceClassName(STRETCHED, COLLAPSED);
235 stretchControl.replaceClassName("icon-close-fullscreen-2", "icon-open-fullscreen-2");
236 form.asWidget().removeStyleName(TEXTAREA_STRETCHED);
237 clearTraces();
238 }
239 }
240
241 @Override
242 public void onUnregister() {
243 super.onUnregister();
244 clearTraces();
245 }
246
247 private void hideOtherStretchers() {
248 JQueryWrapper.select("." + STRETCHER_BASE).setCss("display", "none");
249 this.stretchControl.getStyle().setDisplay(Style.Display.BLOCK);
250 }
251
252 private void clearTraces() {
253 Style style = textWidget.getElement().getStyle();
254 style.clearLeft();
255 style.clearTop();
256 style.clearPosition();
257 style.clearZIndex();
258
259 stretchControl.getStyle().clearTop();
260 stretchControl.getStyle().clearLeft();
261
262 JQueryWrapper.select("." + STRETCHER_BASE).setCss("display", "");
263 }
264
265 private void stretchTextArea(Style style) {
266 style.setWidth(form.getOffsetWidth(), Style.Unit.PX);
267 adjustTextAreaHeightToScreen(getConnection().getUIConnector().getWidget().getOffsetHeight());
268 }
269
270 private void unregisterSizeChangeListeners() {
271 final LayoutManager lm = getParent().getLayoutManager();
272 final UIConnector ui = getConnection().getUIConnector();
273 if (ui != null) {
274 getParent().removeStateChangeHandler(textAreaSizeHandler);
275 lm.removeElementResizeListener(ui.getWidget().getElement(), windowResizeListener);
276 }
277
278 final ComponentConnector formConnector = Util.findConnectorFor(this.form);
279 if (formConnector != null) {
280 formConnector.getLayoutManager().removeElementResizeListener(this.form.getElement(), formResizeListener);
281 }
282 }
283
284 private Element getDialogHeaderElement() {
285 return JQueryWrapper.select(dialog.asWidget()).find(".dialog-header").get(0);
286 }
287
288 private void checkOverlay() {
289 Widget it = this.dialog.asWidget();
290 while (it != null && !isOverlay) {
291 it = it.getParent();
292 this.isOverlay = it instanceof OverlayWidget;
293 }
294 }
295
296 private void initDialog() {
297 this.dialog = form.getParent();
298 }
299
300 private void initFormView() {
301 Widget it = textWidget;
302 while (it != null && !(it instanceof FormView)) {
303 it = it.getParent();
304 }
305 this.form = (it instanceof FormView) ? it : null;
306 }
307
308 private void adjustTextAreaHeightToScreen(int uiHeight) {
309 int formTop = form.getAbsoluteTop();
310 textWidget.setHeight((uiHeight - formTop) + "px");
311 }
312
313 private class WindowResizeListener implements ElementResizeListener {
314 @Override
315 public void onElementResize(ElementResizeEvent e) {
316 adjustTextAreaHeightToScreen(e.getLayoutManager().getOuterHeight(e.getElement()));
317 }
318 }
319
320 }