View Javadoc

1   /**
2    * This file Copyright (c) 2012-2013 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.editor.jsni;
35  
36  import info.magnolia.ui.vaadin.gwt.client.widget.PageEditorView;
37  
38  import com.google.gwt.core.client.GWT;
39  import com.google.gwt.dom.client.Document;
40  import com.google.gwt.dom.client.Element;
41  import com.google.gwt.dom.client.NativeEvent;
42  import com.google.gwt.event.dom.client.DomEvent;
43  import com.google.gwt.event.dom.client.LoadEvent;
44  import com.google.gwt.event.dom.client.LoadHandler;
45  import com.google.gwt.user.client.Timer;
46  import com.google.gwt.user.client.ui.Frame;
47  
48  /**
49   * WebkitFrameEventHandler. Provides separated implementations to overcome different bugs in the
50   * handling of iframes on webkit browsers including the iPad.
51   * See SCRUM-1593 for details.
52   */
53  public class WebkitFrameEventHandler extends AbstractFrameEventHandler {
54  
55      private MyTimer timer = new MyTimer();
56      private boolean loaded = false;
57      private int touchStartY = 0;
58  
59      @Override
60      public void init() {
61          registerLoadHandler();
62      }
63  
64      @Override
65      public void onFrameReady() {
66          super.onFrameReady();
67          timer.cancel();
68          registerUnloadHandler(getView().getFrame().getElement(), timer);
69      }
70  
71      /**
72       * Registers two separated implementations of an onload event.
73       * In case the onload is not triggered it's supposed to fallback on a readystate poller.
74       * We have to make sure, that they don't interfere by using a loaded boolean.
75       */
76      public void registerLoadHandler() {
77          Frame frame = getView().getFrame();
78  
79          frame.addLoadHandler(new LoadHandler() {
80              @Override
81              public void onLoad(LoadEvent event) {
82                  if (loaded) {
83                      return;
84                  }
85                  loaded = true;
86                  onFrameReady();
87              }
88          });
89          timer.setIframe(frame);
90          timer.scheduleRepeating(100);
91  
92      }
93  
94      @Override
95      public void notifyUrlChange() {
96          loaded = false;
97          timer.scheduleRepeating(100);
98      }
99  
100     /**
101      * This function is supposed to trigger an unload event, when the page inside the Iframe is changed.
102      * Doesn't work, that's why browsing inside the iframe is broken for webkit.
103      */
104     public native void registerUnloadHandler(Element element, Timer timer) /*-{
105         var that = timer;
106         var poll = function(){
107             that.@com.google.gwt.user.client.Timer::scheduleRepeating(I)(100);
108         };
109 
110         element.contentWindow.addEventListener('unload', poll, false);
111 
112     }-*/;
113 
114     /**
115      * Custom implementation for iPads of the touch end handling. Suppresses the selection, when scrolling.
116      */
117     @Override
118     public native void initNativeTouchSelectionListener(Element element, PageEditorView.Listener listener) /*-{
119         if (element != 'undefined') {
120             var ref = this;
121             var that = listener;
122             var wndRef = $wnd;
123             element.ontouchend = function(event) {
124                 var touchEndY = event.changedTouches[0].pageY;
125                 var touchStartY = ref.@info.magnolia.ui.vaadin.gwt.client.editor.jsni.WebkitFrameEventHandler::touchStartY;
126                 if (Math.abs(touchEndY - touchStartY) < 5) {
127                     that.@info.magnolia.ui.vaadin.gwt.client.widget.PageEditorView.Listener::selectElement(Lcom/google/gwt/dom/client/Element;)(event.target);
128                 }
129             }
130 
131             element.ontouchstart = function(event) {
132                 ref.@info.magnolia.ui.vaadin.gwt.client.editor.jsni.WebkitFrameEventHandler::touchStartY = event.targetTouches[0].pageY;
133             }
134 
135         }
136     }-*/;
137 
138     @Override
139     public native void initNativeKeyListener(Element element) /*-{
140         if (element != 'undefined') {
141             element.onkeydown = function(event) {
142                 var eventObj = document.createEventObject ?
143                     document.createEventObject() : document.createEvent("Events");
144 
145                 if(eventObj.initEvent){
146                     eventObj.initEvent("keydown", true, true);
147                 }
148 
149                 eventObj.keyCode = event.keyCode;
150                 eventObj.which = event.keyCode;
151 
152                 element.dispatchEvent ? element.dispatchEvent(eventObj) : element.fireEvent("onkeydown", eventObj);
153 
154                 element.dispatchEvent(event);
155             }
156         }
157     }-*/;
158 
159     /**
160      * Poller to check the readystate of the contentdocument in the iframe.
161      */
162     private class MyTimer extends Timer {
163 
164         Frame iframe;
165 
166         @Override
167         public void run() {
168             GWT.log("timer started");
169             String readyState = getReadyState(iframe.getElement());
170 
171             if (readyState != null && !readyState.isEmpty()) {
172                 GWT.log("doc.readyState" + readyState);
173             }
174 
175             if ("interactive".equals(readyState)) {
176                 NativeEvent event = Document.get().createLoadEvent();
177                 DomEvent.fireNativeEvent(event, iframe);
178             }
179         }
180 
181         public void setIframe(Frame iframe) {
182             this.iframe = iframe;
183         }
184 
185         public final native String getReadyState(Element element) /*-{
186             if (element.contentWindow != null) {
187                 return element.contentWindow.document.readyState;
188             }
189             return '';
190         }-*/;
191     }
192 }