View Javadoc
1   /**
2    * This file Copyright (c) 2013-2014 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
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   * Client-side connector for {@link info.magnolia.ui.vaadin.richtext.TextAreaStretcher}.
64   */
65  @Connect(TextAreaStretcher.class)
66  public class TextAreaStretcherConnector extends AbstractExtensionConnector {
67  
68      public static final int DELAY_MS = 500;
69      private Widget form;
70      private Widget dialog;
71      private Widget textWidget;
72  
73      private Element stretchControl = DOM.createDiv();
74      private WindowResizeListener windowResizeListener = new WindowResizeListener();
75  
76      private boolean isOverlay = false;
77      private boolean isRichTextEditor = false;
78  
79      private StateChangeEvent.StateChangeHandler textAreaSizeHandler = new StateChangeEvent.StateChangeHandler() {
80          @Override
81          public void onStateChanged(StateChangeEvent stateChangeEvent) {
82              stretchTextArea(textWidget.getElement().getStyle());
83          }
84      };
85  
86      private ElementResizeListener formResizeListener = new ElementResizeListener() {
87          @Override
88          public void onElementResize(ElementResizeEvent e) {
89              if (isRichTextEditor) {
90                  Scheduler.get().scheduleDeferred(new Scheduler.ScheduledCommand() {
91                      @Override
92                      public void execute() {
93                          updateSize();
94                      }
95                  });
96              } else {
97                  updateSize();
98              }
99          }
100     };;
101 
102     @Override
103     public void onStateChanged(StateChangeEvent stateChangeEvent) {
104         super.onStateChanged(stateChangeEvent);
105         if (stateChangeEvent.hasPropertyChanged("isCollapsed")) {
106             updateSize();
107             if (!getState().isCollapsed) {
108                 registerSizeChangeListeners();
109             }
110         }
111     }
112 
113     @Override
114     public ComponentConnector getParent() {
115         return (ComponentConnector) super.getParent();
116     }
117 
118     @Override
119     protected void extend(ServerConnector target) {
120         this.textWidget = ((ComponentConnector)target).getWidget();
121         this.isRichTextEditor = target instanceof RichTextConnector;
122         this.stretchControl.setClassName("textarea-stretcher");
123         textWidget.addAttachHandler(new AttachEvent.Handler() {
124             @Override
125             public void onAttachOrDetach(AttachEvent attachEvent) {
126                 initFormView();
127                 initDialog();
128                 checkOverlay();
129                 if (!isRichTextEditor) {
130                     appendStretcher(textWidget.getElement());
131                 } else {
132                     Scheduler.get().scheduleFixedDelay(new Scheduler.RepeatingCommand() {
133                         private int repeats = 0;
134 
135                         @Override
136                         public boolean execute() {
137                             repeats++;
138                             isRichTextEditor = true;
139                             Element iframe = JQueryWrapper.select(textWidget).find("iframe").get(0);
140                             if (iframe != null) {
141                                 appendStretcher(iframe);
142                                 stretchControl.addClassName("rich-text");
143                             }
144                             return iframe == null && repeats < 5;
145                         }
146                     }, DELAY_MS);
147                 }
148             }
149         });
150     }
151 
152     @Override
153     public TextAreaStretcherState getState() {
154         return (TextAreaStretcherState) super.getState();
155     }
156 
157     private void appendStretcher(Element rootElement) {
158         rootElement.getParentElement().insertAfter(stretchControl, rootElement);
159         Widget parent = textWidget.getParent();
160         TouchDelegate touchDelegate = new TouchDelegate(parent);
161         touchDelegate.addTouchEndHandler(new TouchEndHandler() {
162             @Override
163             public void onTouchEnd(TouchEndEvent event) {
164                 Element target = event.getNativeEvent().getEventTarget().cast();
165                 if (stretchControl.isOrHasChild(target)) {
166                     if (!getState().isCollapsed) {
167                         unregisterSizeChangeListeners();
168                     }
169                     getRpcProxy(TextAreaStretcherServerRpc.class).toggle(textWidget.getOffsetWidth(), textWidget.getOffsetHeight());
170 
171                 }
172             }
173         });
174     }
175 
176     private void registerSizeChangeListeners() {
177         final LayoutManager lm = getParent().getLayoutManager();
178         final UIConnector ui = getConnection().getUIConnector();
179         getParent().addStateChangeHandler(textAreaSizeHandler);
180         lm.addElementResizeListener(ui.getWidget().getElement(), windowResizeListener);
181 
182         final ComponentConnector formConnector = Util.findConnectorFor(this.form);
183         if (formConnector != null) {
184             formConnector.getLayoutManager().addElementResizeListener(this.form.getElement(), formResizeListener);
185         }
186     }
187 
188     private void updateSize() {
189         if (!getState().isCollapsed) {
190             stretchControl.replaceClassName("icon-open-fullscreen-2", "icon-close-fullscreen-2");
191             stretchControl.replaceClassName("collapsed", "stretched");
192             form.asWidget().addStyleName("textarea-stretched");
193 
194             Style style = textWidget.getElement().getStyle();
195             style.setPosition(Style.Position.ABSOLUTE);
196             Element header = getDialogHeaderElement();
197             ComputedStyle headerCS = new ComputedStyle(header);
198 
199             int top = form.getAbsoluteTop() - dialog.getAbsoluteTop();
200             top = isOverlay ? top : top + headerCS.getPadding()[0] + headerCS.getPadding()[2];
201 
202             int left = isOverlay ? 0 : form.getAbsoluteLeft();
203 
204             style.setLeft(left, Style.Unit.PX);
205             style.setTop(top, Style.Unit.PX);
206 
207             stretchTextArea(style);
208             style.setZIndex(5);
209 
210             if (!isOverlay && !isRichTextEditor) {
211                 stretchControl.getStyle().setTop(top + 5, Style.Unit.PX);
212                 stretchControl.getStyle().setLeft(left + textWidget.getOffsetWidth() - stretchControl.getOffsetWidth() - 5, Style.Unit.PX);
213 
214             }
215 
216         } else {
217             stretchControl.replaceClassName("stretched", "collapsed");
218             stretchControl.replaceClassName("icon-close-fullscreen-2", "icon-open-fullscreen-2");
219             form.asWidget().removeStyleName("textarea-stretched");
220             clearTraces();
221         }
222     }
223 
224     private void clearTraces() {
225         Style style = textWidget.getElement().getStyle();
226         style.clearLeft();
227         style.clearTop();
228         style.clearPosition();
229         style.clearZIndex();
230 
231         stretchControl.getStyle().clearTop();
232         stretchControl.getStyle().clearLeft();
233     }
234 
235     private void stretchTextArea(Style style) {
236         style.setWidth(form.getOffsetWidth(), Style.Unit.PX);
237         adjustTextAreaHeightToScreen(getConnection().getUIConnector().getWidget().getOffsetHeight());
238     }
239 
240     private void unregisterSizeChangeListeners() {
241         final LayoutManager lm = getParent().getLayoutManager();
242         final UIConnector ui = getConnection().getUIConnector();
243         if (ui != null) {
244             getParent().removeStateChangeHandler(textAreaSizeHandler);
245             lm.removeElementResizeListener(ui.getWidget().getElement(), windowResizeListener);
246         }
247 
248         final ComponentConnector formConnector = Util.findConnectorFor(this.form);
249         if (formConnector != null) {
250             formConnector.getLayoutManager().removeElementResizeListener(this.form.getElement(), formResizeListener);
251         }
252     }
253 
254     private Element getDialogHeaderElement() {
255         return JQueryWrapper.select(dialog.asWidget()).find(".dialog-header").get(0);
256     }
257 
258     private void checkOverlay() {
259         Widget it = this.dialog.asWidget();
260         while (it != null && !isOverlay) {
261             it = it.getParent();
262             this.isOverlay = it instanceof OverlayWidget;
263         }
264     }
265 
266     private void initDialog() {
267         this.dialog = form.getParent();
268     }
269 
270     private void initFormView() {
271         Widget it = textWidget;
272         while (it != null && !(it instanceof FormView)) {
273             it = it.getParent();
274         }
275         this.form = (it instanceof FormView) ? it : null;
276     }
277 
278     private void adjustTextAreaHeightToScreen(int uiHeight) {
279         int formTop = form.getAbsoluteTop();
280         textWidget.setHeight((uiHeight - formTop) + "px");
281     }
282 
283     private class WindowResizeListener implements ElementResizeListener {
284         @Override
285         public void onElementResize(ElementResizeEvent e) {
286             adjustTextAreaHeightToScreen(e.getLayoutManager().getOuterHeight(e.getElement()));
287         }
288     }
289 
290 }