Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

Util.java 42KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243
  1. /*
  2. * Copyright 2000-2013 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.client;
  17. import java.util.ArrayList;
  18. import java.util.Arrays;
  19. import java.util.Collection;
  20. import java.util.Iterator;
  21. import java.util.List;
  22. import com.google.gwt.core.client.Scheduler;
  23. import com.google.gwt.core.client.Scheduler.ScheduledCommand;
  24. import com.google.gwt.dom.client.AnchorElement;
  25. import com.google.gwt.dom.client.DivElement;
  26. import com.google.gwt.dom.client.Document;
  27. import com.google.gwt.dom.client.NativeEvent;
  28. import com.google.gwt.dom.client.Node;
  29. import com.google.gwt.dom.client.NodeList;
  30. import com.google.gwt.dom.client.Style;
  31. import com.google.gwt.dom.client.Style.Display;
  32. import com.google.gwt.dom.client.Touch;
  33. import com.google.gwt.user.client.Command;
  34. import com.google.gwt.user.client.DOM;
  35. import com.google.gwt.user.client.Element;
  36. import com.google.gwt.user.client.Event;
  37. import com.google.gwt.user.client.EventListener;
  38. import com.google.gwt.user.client.Window;
  39. import com.google.gwt.user.client.ui.HasWidgets;
  40. import com.google.gwt.user.client.ui.RootPanel;
  41. import com.google.gwt.user.client.ui.Widget;
  42. import com.vaadin.client.RenderInformation.FloatSize;
  43. import com.vaadin.client.ui.VOverlay;
  44. import com.vaadin.shared.AbstractComponentState;
  45. import com.vaadin.shared.ApplicationConstants;
  46. import com.vaadin.shared.communication.MethodInvocation;
  47. import com.vaadin.shared.ui.ComponentStateUtil;
  48. public class Util {
  49. /**
  50. * Helper method for debugging purposes.
  51. *
  52. * Stops execution on firefox browsers on a breakpoint.
  53. *
  54. */
  55. public static native void browserDebugger()
  56. /*-{
  57. if($wnd.console)
  58. debugger;
  59. }-*/;
  60. /**
  61. *
  62. * Returns the topmost element of from given coordinates.
  63. *
  64. * TODO fix crossplat issues clientX vs pageX. See quircksmode. Not critical
  65. * for vaadin as we scroll div istead of page.
  66. *
  67. * @param x
  68. * @param y
  69. * @return the element at given coordinates
  70. */
  71. public static native Element getElementFromPoint(int clientX, int clientY)
  72. /*-{
  73. var el = $wnd.document.elementFromPoint(clientX, clientY);
  74. if(el != null && el.nodeType == 3) {
  75. el = el.parentNode;
  76. }
  77. return el;
  78. }-*/;
  79. /**
  80. * This helper method can be called if components size have been changed
  81. * outside rendering phase. It notifies components parent about the size
  82. * change so it can react.
  83. *
  84. * When using this method, developer should consider if size changes could
  85. * be notified lazily. If lazy flag is true, method will save widget and
  86. * wait for a moment until it notifies parents in chunks. This may vastly
  87. * optimize layout in various situation. Example: if component have a lot of
  88. * images their onload events may fire "layout phase" many times in a short
  89. * period.
  90. *
  91. * @param widget
  92. * @param lazy
  93. * run componentSizeUpdated lazyly
  94. *
  95. * @deprecated As of 7.0, use
  96. * {@link LayoutManager#setNeedsMeasure(ComponentConnector)}
  97. * instead
  98. */
  99. @Deprecated
  100. public static void notifyParentOfSizeChange(Widget widget, boolean lazy) {
  101. ComponentConnector connector = findConnectorFor(widget);
  102. if (connector != null) {
  103. connector.getLayoutManager().setNeedsMeasure(connector);
  104. if (!lazy) {
  105. connector.getLayoutManager().layoutNow();
  106. }
  107. }
  108. }
  109. public static ComponentConnector findConnectorFor(Widget widget) {
  110. List<ApplicationConnection> runningApplications = ApplicationConfiguration
  111. .getRunningApplications();
  112. for (ApplicationConnection applicationConnection : runningApplications) {
  113. ConnectorMap connectorMap = applicationConnection.getConnectorMap();
  114. ComponentConnector connector = connectorMap.getConnector(widget);
  115. if (connector == null) {
  116. continue;
  117. }
  118. if (connector.getConnection() == applicationConnection) {
  119. return connector;
  120. }
  121. }
  122. return null;
  123. }
  124. public static float parseRelativeSize(String size) {
  125. if (size == null || !size.endsWith("%")) {
  126. return -1;
  127. }
  128. try {
  129. return Float.parseFloat(size.substring(0, size.length() - 1));
  130. } catch (Exception e) {
  131. VConsole.log("Unable to parse relative size");
  132. return -1;
  133. }
  134. }
  135. private static final Element escapeHtmlHelper = DOM.createDiv();
  136. /**
  137. * Converts html entities to text.
  138. *
  139. * @param html
  140. * @return escaped string presentation of given html
  141. */
  142. public static String escapeHTML(String html) {
  143. DOM.setInnerText(escapeHtmlHelper, html);
  144. String escapedText = DOM.getInnerHTML(escapeHtmlHelper);
  145. if (BrowserInfo.get().isIE8()) {
  146. // #7478 IE8 "incorrectly" returns "<br>" for newlines set using
  147. // setInnerText. The same for " " which is converted to "&nbsp;"
  148. escapedText = escapedText.replaceAll("<(BR|br)>", "\n");
  149. escapedText = escapedText.replaceAll("&nbsp;", " ");
  150. }
  151. return escapedText;
  152. }
  153. /**
  154. * Escapes the string so it is safe to write inside an HTML attribute.
  155. *
  156. * @param attribute
  157. * The string to escape
  158. * @return An escaped version of <literal>attribute</literal>.
  159. */
  160. public static String escapeAttribute(String attribute) {
  161. if (attribute == null) {
  162. return "";
  163. }
  164. attribute = attribute.replace("\"", "&quot;");
  165. attribute = attribute.replace("'", "&#39;");
  166. attribute = attribute.replace(">", "&gt;");
  167. attribute = attribute.replace("<", "&lt;");
  168. attribute = attribute.replace("&", "&amp;");
  169. return attribute;
  170. }
  171. /**
  172. * Clones given element as in JavaScript.
  173. *
  174. * Deprecate this if there appears similar method into GWT someday.
  175. *
  176. * @param element
  177. * @param deep
  178. * clone child tree also
  179. * @return
  180. */
  181. public static native Element cloneNode(Element element, boolean deep)
  182. /*-{
  183. return element.cloneNode(deep);
  184. }-*/;
  185. public static int measureHorizontalPaddingAndBorder(Element element,
  186. int paddingGuess) {
  187. String originalWidth = DOM.getStyleAttribute(element, "width");
  188. int originalOffsetWidth = element.getOffsetWidth();
  189. int widthGuess = (originalOffsetWidth - paddingGuess);
  190. if (widthGuess < 1) {
  191. widthGuess = 1;
  192. }
  193. DOM.setStyleAttribute(element, "width", widthGuess + "px");
  194. int padding = element.getOffsetWidth() - widthGuess;
  195. DOM.setStyleAttribute(element, "width", originalWidth);
  196. return padding;
  197. }
  198. public static int measureVerticalPaddingAndBorder(Element element,
  199. int paddingGuess) {
  200. String originalHeight = DOM.getStyleAttribute(element, "height");
  201. int originalOffsetHeight = element.getOffsetHeight();
  202. int widthGuess = (originalOffsetHeight - paddingGuess);
  203. if (widthGuess < 1) {
  204. widthGuess = 1;
  205. }
  206. DOM.setStyleAttribute(element, "height", widthGuess + "px");
  207. int padding = element.getOffsetHeight() - widthGuess;
  208. DOM.setStyleAttribute(element, "height", originalHeight);
  209. return padding;
  210. }
  211. public static int measureHorizontalBorder(Element element) {
  212. int borders;
  213. if (BrowserInfo.get().isIE()) {
  214. String width = element.getStyle().getProperty("width");
  215. String height = element.getStyle().getProperty("height");
  216. int offsetWidth = element.getOffsetWidth();
  217. int offsetHeight = element.getOffsetHeight();
  218. if (offsetHeight < 1) {
  219. offsetHeight = 1;
  220. }
  221. if (offsetWidth < 1) {
  222. offsetWidth = 10;
  223. }
  224. element.getStyle().setPropertyPx("height", offsetHeight);
  225. element.getStyle().setPropertyPx("width", offsetWidth);
  226. borders = element.getOffsetWidth() - element.getClientWidth();
  227. element.getStyle().setProperty("width", width);
  228. element.getStyle().setProperty("height", height);
  229. } else {
  230. borders = element.getOffsetWidth()
  231. - element.getPropertyInt("clientWidth");
  232. }
  233. assert borders >= 0;
  234. return borders;
  235. }
  236. public static int measureVerticalBorder(Element element) {
  237. int borders;
  238. if (BrowserInfo.get().isIE()) {
  239. String width = element.getStyle().getProperty("width");
  240. String height = element.getStyle().getProperty("height");
  241. int offsetWidth = element.getOffsetWidth();
  242. int offsetHeight = element.getOffsetHeight();
  243. if (offsetHeight < 1) {
  244. offsetHeight = 1;
  245. }
  246. if (offsetWidth < 1) {
  247. offsetWidth = 10;
  248. }
  249. element.getStyle().setPropertyPx("width", offsetWidth);
  250. element.getStyle().setPropertyPx("height", offsetHeight);
  251. borders = element.getOffsetHeight()
  252. - element.getPropertyInt("clientHeight");
  253. element.getStyle().setProperty("height", height);
  254. element.getStyle().setProperty("width", width);
  255. } else {
  256. borders = element.getOffsetHeight()
  257. - element.getPropertyInt("clientHeight");
  258. }
  259. assert borders >= 0;
  260. return borders;
  261. }
  262. public static int measureMarginLeft(Element element) {
  263. return element.getAbsoluteLeft()
  264. - element.getParentElement().getAbsoluteLeft();
  265. }
  266. public static int setHeightExcludingPaddingAndBorder(Widget widget,
  267. String height, int paddingBorderGuess) {
  268. if (height.equals("")) {
  269. setHeight(widget, "");
  270. return paddingBorderGuess;
  271. } else if (height.endsWith("px")) {
  272. int pixelHeight = Integer.parseInt(height.substring(0,
  273. height.length() - 2));
  274. return setHeightExcludingPaddingAndBorder(widget.getElement(),
  275. pixelHeight, paddingBorderGuess, false);
  276. } else {
  277. // Set the height in unknown units
  278. setHeight(widget, height);
  279. // Use the offsetWidth
  280. return setHeightExcludingPaddingAndBorder(widget.getElement(),
  281. widget.getOffsetHeight(), paddingBorderGuess, true);
  282. }
  283. }
  284. private static void setWidth(Widget widget, String width) {
  285. DOM.setStyleAttribute(widget.getElement(), "width", width);
  286. }
  287. private static void setHeight(Widget widget, String height) {
  288. DOM.setStyleAttribute(widget.getElement(), "height", height);
  289. }
  290. public static int setWidthExcludingPaddingAndBorder(Widget widget,
  291. String width, int paddingBorderGuess) {
  292. if (width.equals("")) {
  293. setWidth(widget, "");
  294. return paddingBorderGuess;
  295. } else if (width.endsWith("px")) {
  296. int pixelWidth = Integer.parseInt(width.substring(0,
  297. width.length() - 2));
  298. return setWidthExcludingPaddingAndBorder(widget.getElement(),
  299. pixelWidth, paddingBorderGuess, false);
  300. } else {
  301. setWidth(widget, width);
  302. return setWidthExcludingPaddingAndBorder(widget.getElement(),
  303. widget.getOffsetWidth(), paddingBorderGuess, true);
  304. }
  305. }
  306. public static int setWidthExcludingPaddingAndBorder(Element element,
  307. int requestedWidth, int horizontalPaddingBorderGuess,
  308. boolean requestedWidthIncludesPaddingBorder) {
  309. int widthGuess = requestedWidth - horizontalPaddingBorderGuess;
  310. if (widthGuess < 0) {
  311. widthGuess = 0;
  312. }
  313. DOM.setStyleAttribute(element, "width", widthGuess + "px");
  314. int captionOffsetWidth = DOM.getElementPropertyInt(element,
  315. "offsetWidth");
  316. int actualPadding = captionOffsetWidth - widthGuess;
  317. if (requestedWidthIncludesPaddingBorder) {
  318. actualPadding += actualPadding;
  319. }
  320. if (actualPadding != horizontalPaddingBorderGuess) {
  321. int w = requestedWidth - actualPadding;
  322. if (w < 0) {
  323. // Cannot set negative width even if we would want to
  324. w = 0;
  325. }
  326. DOM.setStyleAttribute(element, "width", w + "px");
  327. }
  328. return actualPadding;
  329. }
  330. public static int setHeightExcludingPaddingAndBorder(Element element,
  331. int requestedHeight, int verticalPaddingBorderGuess,
  332. boolean requestedHeightIncludesPaddingBorder) {
  333. int heightGuess = requestedHeight - verticalPaddingBorderGuess;
  334. if (heightGuess < 0) {
  335. heightGuess = 0;
  336. }
  337. DOM.setStyleAttribute(element, "height", heightGuess + "px");
  338. int captionOffsetHeight = DOM.getElementPropertyInt(element,
  339. "offsetHeight");
  340. int actualPadding = captionOffsetHeight - heightGuess;
  341. if (requestedHeightIncludesPaddingBorder) {
  342. actualPadding += actualPadding;
  343. }
  344. if (actualPadding != verticalPaddingBorderGuess) {
  345. int h = requestedHeight - actualPadding;
  346. if (h < 0) {
  347. // Cannot set negative height even if we would want to
  348. h = 0;
  349. }
  350. DOM.setStyleAttribute(element, "height", h + "px");
  351. }
  352. return actualPadding;
  353. }
  354. public static String getSimpleName(Object widget) {
  355. if (widget == null) {
  356. return "(null)";
  357. }
  358. String name = widget.getClass().getName();
  359. return name.substring(name.lastIndexOf('.') + 1);
  360. }
  361. public static void setFloat(Element element, String value) {
  362. if (BrowserInfo.get().isIE()) {
  363. DOM.setStyleAttribute(element, "styleFloat", value);
  364. } else {
  365. DOM.setStyleAttribute(element, "cssFloat", value);
  366. }
  367. }
  368. private static int detectedScrollbarSize = -1;
  369. public static int getNativeScrollbarSize() {
  370. if (detectedScrollbarSize < 0) {
  371. Element scroller = DOM.createDiv();
  372. scroller.getStyle().setProperty("width", "50px");
  373. scroller.getStyle().setProperty("height", "50px");
  374. scroller.getStyle().setProperty("overflow", "scroll");
  375. scroller.getStyle().setProperty("position", "absolute");
  376. scroller.getStyle().setProperty("marginLeft", "-5000px");
  377. RootPanel.getBodyElement().appendChild(scroller);
  378. detectedScrollbarSize = scroller.getOffsetWidth()
  379. - scroller.getPropertyInt("clientWidth");
  380. RootPanel.getBodyElement().removeChild(scroller);
  381. }
  382. return detectedScrollbarSize;
  383. }
  384. /**
  385. * Run workaround for webkits overflow auto issue.
  386. *
  387. * See: our bug #2138 and https://bugs.webkit.org/show_bug.cgi?id=21462
  388. *
  389. * @param elem
  390. * with overflow auto
  391. */
  392. public static void runWebkitOverflowAutoFix(final Element elem) {
  393. // Add max version if fix lands sometime to Webkit
  394. // Starting from Opera 11.00, also a problem in Opera
  395. if (BrowserInfo.get().requiresOverflowAutoFix()) {
  396. final String originalOverflow = elem.getStyle().getProperty(
  397. "overflow");
  398. if ("hidden".equals(originalOverflow)) {
  399. return;
  400. }
  401. // check the scrolltop value before hiding the element
  402. final int scrolltop = elem.getScrollTop();
  403. final int scrollleft = elem.getScrollLeft();
  404. elem.getStyle().setProperty("overflow", "hidden");
  405. Scheduler.get().scheduleDeferred(new Command() {
  406. @Override
  407. public void execute() {
  408. // Dough, Safari scroll auto means actually just a moped
  409. elem.getStyle().setProperty("overflow", originalOverflow);
  410. if (scrolltop > 0 || elem.getScrollTop() > 0) {
  411. int scrollvalue = scrolltop;
  412. if (scrollvalue == 0) {
  413. // mysterious are the ways of webkits scrollbar
  414. // handling. In some cases webkit reports bad (0)
  415. // scrolltop before hiding the element temporary,
  416. // sometimes after.
  417. scrollvalue = elem.getScrollTop();
  418. }
  419. // fix another bug where scrollbar remains in wrong
  420. // position
  421. elem.setScrollTop(scrollvalue - 1);
  422. elem.setScrollTop(scrollvalue);
  423. }
  424. // fix for #6940 : Table horizontal scroll sometimes not
  425. // updated when collapsing/expanding columns
  426. // Also appeared in Safari 5.1 with webkit 534 (#7667)
  427. if ((BrowserInfo.get().isChrome() || (BrowserInfo.get()
  428. .isSafari() && BrowserInfo.get().getWebkitVersion() >= 534))
  429. && (scrollleft > 0 || elem.getScrollLeft() > 0)) {
  430. int scrollvalue = scrollleft;
  431. if (scrollvalue == 0) {
  432. // mysterious are the ways of webkits scrollbar
  433. // handling. In some cases webkit may report a bad
  434. // (0) scrollleft before hiding the element
  435. // temporary, sometimes after.
  436. scrollvalue = elem.getScrollLeft();
  437. }
  438. // fix another bug where scrollbar remains in wrong
  439. // position
  440. elem.setScrollLeft(scrollvalue - 1);
  441. elem.setScrollLeft(scrollvalue);
  442. }
  443. }
  444. });
  445. }
  446. }
  447. /**
  448. * Parses shared state and fetches the relative size of the component. If a
  449. * dimension is not specified as relative it will return -1. If the shared
  450. * state does not contain width or height specifications this will return
  451. * null.
  452. *
  453. * @param state
  454. * @return
  455. */
  456. public static FloatSize parseRelativeSize(AbstractComponentState state) {
  457. if (ComponentStateUtil.isUndefinedHeight(state)
  458. && ComponentStateUtil.isUndefinedWidth(state)) {
  459. return null;
  460. }
  461. float relativeWidth = Util.parseRelativeSize(state.width);
  462. float relativeHeight = Util.parseRelativeSize(state.height);
  463. FloatSize relativeSize = new FloatSize(relativeWidth, relativeHeight);
  464. return relativeSize;
  465. }
  466. @Deprecated
  467. public static boolean isCached(UIDL uidl) {
  468. return uidl.getBooleanAttribute("cached");
  469. }
  470. public static void alert(String string) {
  471. if (true) {
  472. Window.alert(string);
  473. }
  474. }
  475. public static boolean equals(Object a, Object b) {
  476. if (a == null) {
  477. return b == null;
  478. }
  479. return a.equals(b);
  480. }
  481. public static void updateRelativeChildrenAndSendSizeUpdateEvent(
  482. ApplicationConnection client, HasWidgets container, Widget widget) {
  483. notifyParentOfSizeChange(widget, false);
  484. }
  485. public static native int getRequiredWidth(
  486. com.google.gwt.dom.client.Element element)
  487. /*-{
  488. if (element.getBoundingClientRect) {
  489. var rect = element.getBoundingClientRect();
  490. return Math.ceil(rect.right - rect.left);
  491. } else {
  492. return element.offsetWidth;
  493. }
  494. }-*/;
  495. public static native int getRequiredHeight(
  496. com.google.gwt.dom.client.Element element)
  497. /*-{
  498. var height;
  499. if (element.getBoundingClientRect != null) {
  500. var rect = element.getBoundingClientRect();
  501. height = Math.ceil(rect.bottom - rect.top);
  502. } else {
  503. height = element.offsetHeight;
  504. }
  505. return height;
  506. }-*/;
  507. public static int getRequiredWidth(Widget widget) {
  508. return getRequiredWidth(widget.getElement());
  509. }
  510. public static int getRequiredHeight(Widget widget) {
  511. return getRequiredHeight(widget.getElement());
  512. }
  513. /**
  514. * Detects what is currently the overflow style attribute in given element.
  515. *
  516. * @param pe
  517. * the element to detect
  518. * @return true if auto or scroll
  519. */
  520. public static boolean mayHaveScrollBars(com.google.gwt.dom.client.Element pe) {
  521. String overflow = getComputedStyle(pe, "overflow");
  522. if (overflow != null) {
  523. if (overflow.equals("auto") || overflow.equals("scroll")) {
  524. return true;
  525. } else {
  526. return false;
  527. }
  528. } else {
  529. return false;
  530. }
  531. }
  532. /**
  533. * A simple helper method to detect "computed style" (aka style sheets +
  534. * element styles). Values returned differ a lot depending on browsers.
  535. * Always be very careful when using this.
  536. *
  537. * @param el
  538. * the element from which the style property is detected
  539. * @param p
  540. * the property to detect
  541. * @return String value of style property
  542. */
  543. private static native String getComputedStyle(
  544. com.google.gwt.dom.client.Element el, String p)
  545. /*-{
  546. try {
  547. if (el.currentStyle) {
  548. // IE
  549. return el.currentStyle[p];
  550. } else if (window.getComputedStyle) {
  551. // Sa, FF, Opera
  552. var view = el.ownerDocument.defaultView;
  553. return view.getComputedStyle(el,null).getPropertyValue(p);
  554. } else {
  555. // fall back for non IE, Sa, FF, Opera
  556. return "";
  557. }
  558. } catch (e) {
  559. return "";
  560. }
  561. }-*/;
  562. /**
  563. * Locates the nested child component of <literal>parent</literal> which
  564. * contains the element <literal>element</literal>. The child component is
  565. * also returned if "element" is part of its caption. If
  566. * <literal>element</literal> is not part of any child component, null is
  567. * returned.
  568. *
  569. * This method returns the deepest nested VPaintableWidget.
  570. *
  571. * @param client
  572. * A reference to ApplicationConnection
  573. * @param parent
  574. * The widget that contains <literal>element</literal>.
  575. * @param element
  576. * An element that is a sub element of the parent
  577. * @return The VPaintableWidget which the element is a part of. Null if the
  578. * element does not belong to a child.
  579. */
  580. public static ComponentConnector getConnectorForElement(
  581. ApplicationConnection client, Widget parent, Element element) {
  582. Element browseElement = element;
  583. Element rootElement = parent.getElement();
  584. while (browseElement != null && browseElement != rootElement) {
  585. ComponentConnector connector = ConnectorMap.get(client)
  586. .getConnector(browseElement);
  587. if (connector == null) {
  588. String ownerPid = VCaption.getCaptionOwnerPid(browseElement);
  589. if (ownerPid != null) {
  590. connector = (ComponentConnector) ConnectorMap.get(client)
  591. .getConnector(ownerPid);
  592. }
  593. }
  594. if (connector != null) {
  595. // check that inside the rootElement
  596. while (browseElement != null && browseElement != rootElement) {
  597. browseElement = (Element) browseElement.getParentElement();
  598. }
  599. if (browseElement != rootElement) {
  600. return null;
  601. } else {
  602. return connector;
  603. }
  604. }
  605. browseElement = (Element) browseElement.getParentElement();
  606. }
  607. // No connector found, element is possibly inside a VOverlay
  608. // If the overlay has an owner, try to find the owner's connector
  609. VOverlay overlay = findWidget(element, VOverlay.class);
  610. if (overlay != null && overlay.getOwner() != null) {
  611. return getConnectorForElement(client, client.getUIConnector()
  612. .getWidget(), overlay.getOwner().getElement());
  613. } else {
  614. return null;
  615. }
  616. }
  617. /**
  618. * Will (attempt) to focus the given DOM Element.
  619. *
  620. * @param el
  621. * the element to focus
  622. */
  623. public static native void focus(Element el)
  624. /*-{
  625. try {
  626. el.focus();
  627. } catch (e) {
  628. }
  629. }-*/;
  630. /**
  631. * Helper method to find the nearest parent paintable instance by traversing
  632. * the DOM upwards from given element.
  633. *
  634. * @param element
  635. * the element to start from
  636. */
  637. public static ComponentConnector findPaintable(
  638. ApplicationConnection client, Element element) {
  639. Widget widget = Util.findWidget(element, null);
  640. ConnectorMap vPaintableMap = ConnectorMap.get(client);
  641. while (widget != null && !vPaintableMap.isConnector(widget)) {
  642. widget = widget.getParent();
  643. }
  644. return vPaintableMap.getConnector(widget);
  645. }
  646. /**
  647. * Helper method to find first instance of given Widget type found by
  648. * traversing DOM upwards from given element.
  649. *
  650. * @param element
  651. * the element where to start seeking of Widget
  652. * @param class1
  653. * the Widget type to seek for
  654. */
  655. public static <T> T findWidget(Element element,
  656. Class<? extends Widget> class1) {
  657. if (element != null) {
  658. /* First seek for the first EventListener (~Widget) from dom */
  659. EventListener eventListener = null;
  660. while (eventListener == null && element != null) {
  661. eventListener = Event.getEventListener(element);
  662. if (eventListener == null) {
  663. element = (Element) element.getParentElement();
  664. }
  665. }
  666. if (eventListener != null) {
  667. /*
  668. * Then find the first widget of type class1 from widget
  669. * hierarchy
  670. */
  671. Widget w = (Widget) eventListener;
  672. while (w != null) {
  673. if (class1 == null || w.getClass() == class1) {
  674. return (T) w;
  675. }
  676. w = w.getParent();
  677. }
  678. }
  679. }
  680. return null;
  681. }
  682. /**
  683. * Force webkit to redraw an element
  684. *
  685. * @param element
  686. * The element that should be redrawn
  687. */
  688. public static void forceWebkitRedraw(Element element) {
  689. Style style = element.getStyle();
  690. String s = style.getProperty("webkitTransform");
  691. if (s == null || s.length() == 0) {
  692. style.setProperty("webkitTransform", "scale(1)");
  693. } else {
  694. style.setProperty("webkitTransform", "");
  695. }
  696. }
  697. /**
  698. * Detaches and re-attaches the element from its parent. The element is
  699. * reattached at the same position in the DOM as it was before.
  700. *
  701. * Does nothing if the element is not attached to the DOM.
  702. *
  703. * @param element
  704. * The element to detach and re-attach
  705. */
  706. public static void detachAttach(Element element) {
  707. if (element == null) {
  708. return;
  709. }
  710. Node nextSibling = element.getNextSibling();
  711. Node parent = element.getParentNode();
  712. if (parent == null) {
  713. return;
  714. }
  715. parent.removeChild(element);
  716. if (nextSibling == null) {
  717. parent.appendChild(element);
  718. } else {
  719. parent.insertBefore(element, nextSibling);
  720. }
  721. }
  722. public static void sinkOnloadForImages(Element element) {
  723. NodeList<com.google.gwt.dom.client.Element> imgElements = element
  724. .getElementsByTagName("img");
  725. for (int i = 0; i < imgElements.getLength(); i++) {
  726. DOM.sinkEvents((Element) imgElements.getItem(i), Event.ONLOAD);
  727. }
  728. }
  729. /**
  730. * Returns the index of the childElement within its parent.
  731. *
  732. * @param subElement
  733. * @return
  734. */
  735. public static int getChildElementIndex(Element childElement) {
  736. int idx = 0;
  737. Node n = childElement;
  738. while ((n = n.getPreviousSibling()) != null) {
  739. idx++;
  740. }
  741. return idx;
  742. }
  743. private static void printConnectorInvocations(
  744. ArrayList<MethodInvocation> invocations, String id,
  745. ApplicationConnection c) {
  746. ServerConnector connector = ConnectorMap.get(c).getConnector(id);
  747. if (connector != null) {
  748. VConsole.log("\t" + id + " (" + connector.getClass() + ") :");
  749. } else {
  750. VConsole.log("\t" + id
  751. + ": Warning: no corresponding connector for id " + id);
  752. }
  753. for (MethodInvocation invocation : invocations) {
  754. Object[] parameters = invocation.getParameters();
  755. String formattedParams = null;
  756. if (ApplicationConstants.UPDATE_VARIABLE_METHOD.equals(invocation
  757. .getMethodName()) && parameters.length == 2) {
  758. // name, value
  759. Object value = parameters[1];
  760. // TODO paintables inside lists/maps get rendered as
  761. // components in the debug console
  762. String formattedValue = value instanceof ServerConnector ? ((ServerConnector) value)
  763. .getConnectorId() : String.valueOf(value);
  764. formattedParams = parameters[0] + " : " + formattedValue;
  765. }
  766. if (null == formattedParams) {
  767. formattedParams = (null != parameters) ? Arrays
  768. .toString(parameters) : null;
  769. }
  770. VConsole.log("\t\t" + invocation.getInterfaceName() + "."
  771. + invocation.getMethodName() + "(" + formattedParams + ")");
  772. }
  773. }
  774. static void logVariableBurst(ApplicationConnection c,
  775. Collection<MethodInvocation> loggedBurst) {
  776. try {
  777. VConsole.log("Variable burst to be sent to server:");
  778. String curId = null;
  779. ArrayList<MethodInvocation> invocations = new ArrayList<MethodInvocation>();
  780. for (MethodInvocation methodInvocation : loggedBurst) {
  781. String id = methodInvocation.getConnectorId();
  782. if (curId == null) {
  783. curId = id;
  784. } else if (!curId.equals(id)) {
  785. printConnectorInvocations(invocations, curId, c);
  786. invocations.clear();
  787. curId = id;
  788. }
  789. invocations.add(methodInvocation);
  790. }
  791. if (!invocations.isEmpty()) {
  792. printConnectorInvocations(invocations, curId, c);
  793. }
  794. } catch (Exception e) {
  795. VConsole.error(e);
  796. }
  797. }
  798. /**
  799. * Temporarily sets the {@code styleProperty} to {@code tempValue} and then
  800. * resets it to its current value. Used mainly to work around rendering
  801. * issues in IE (and possibly in other browsers)
  802. *
  803. * @param element
  804. * The target element
  805. * @param styleProperty
  806. * The name of the property to set
  807. * @param tempValue
  808. * The temporary value
  809. */
  810. public static void setStyleTemporarily(Element element,
  811. final String styleProperty, String tempValue) {
  812. final Style style = element.getStyle();
  813. final String currentValue = style.getProperty(styleProperty);
  814. style.setProperty(styleProperty, tempValue);
  815. element.getOffsetWidth();
  816. style.setProperty(styleProperty, currentValue);
  817. }
  818. /**
  819. * A helper method to return the client position from an event. Returns
  820. * position from either first changed touch (if touch event) or from the
  821. * event itself.
  822. *
  823. * @param event
  824. * @return
  825. */
  826. public static int getTouchOrMouseClientX(Event event) {
  827. if (isTouchEvent(event)) {
  828. return event.getChangedTouches().get(0).getClientX();
  829. } else {
  830. return event.getClientX();
  831. }
  832. }
  833. /**
  834. * Find the element corresponding to the coordinates in the passed mouse
  835. * event. Please note that this is not always the same as the target of the
  836. * event e.g. if event capture is used.
  837. *
  838. * @param event
  839. * the mouse event to get coordinates from
  840. * @return the element at the coordinates of the event
  841. */
  842. public static Element getElementUnderMouse(NativeEvent event) {
  843. int pageX = getTouchOrMouseClientX(event);
  844. int pageY = getTouchOrMouseClientY(event);
  845. return getElementFromPoint(pageX, pageY);
  846. }
  847. /**
  848. * A helper method to return the client position from an event. Returns
  849. * position from either first changed touch (if touch event) or from the
  850. * event itself.
  851. *
  852. * @param event
  853. * @return
  854. */
  855. public static int getTouchOrMouseClientY(Event event) {
  856. if (isTouchEvent(event)) {
  857. return event.getChangedTouches().get(0).getClientY();
  858. } else {
  859. return event.getClientY();
  860. }
  861. }
  862. /**
  863. *
  864. * @see #getTouchOrMouseClientY(Event)
  865. * @param currentGwtEvent
  866. * @return
  867. */
  868. public static int getTouchOrMouseClientY(NativeEvent currentGwtEvent) {
  869. return getTouchOrMouseClientY(Event.as(currentGwtEvent));
  870. }
  871. /**
  872. * @see #getTouchOrMouseClientX(Event)
  873. *
  874. * @param event
  875. * @return
  876. */
  877. public static int getTouchOrMouseClientX(NativeEvent event) {
  878. return getTouchOrMouseClientX(Event.as(event));
  879. }
  880. public static boolean isTouchEvent(Event event) {
  881. return event.getType().contains("touch");
  882. }
  883. public static boolean isTouchEvent(NativeEvent event) {
  884. return isTouchEvent(Event.as(event));
  885. }
  886. public static void simulateClickFromTouchEvent(Event touchevent,
  887. Widget widget) {
  888. Touch touch = touchevent.getChangedTouches().get(0);
  889. final NativeEvent createMouseUpEvent = Document.get()
  890. .createMouseUpEvent(0, touch.getScreenX(), touch.getScreenY(),
  891. touch.getClientX(), touch.getClientY(), false, false,
  892. false, false, NativeEvent.BUTTON_LEFT);
  893. final NativeEvent createMouseDownEvent = Document.get()
  894. .createMouseDownEvent(0, touch.getScreenX(),
  895. touch.getScreenY(), touch.getClientX(),
  896. touch.getClientY(), false, false, false, false,
  897. NativeEvent.BUTTON_LEFT);
  898. final NativeEvent createMouseClickEvent = Document.get()
  899. .createClickEvent(0, touch.getScreenX(), touch.getScreenY(),
  900. touch.getClientX(), touch.getClientY(), false, false,
  901. false, false);
  902. /*
  903. * Get target with element from point as we want the actual element, not
  904. * the one that sunk the event.
  905. */
  906. final Element target = getElementFromPoint(touch.getClientX(),
  907. touch.getClientY());
  908. /*
  909. * Fixes infocusable form fields in Safari of iOS 5.x and some Android
  910. * browsers.
  911. */
  912. Widget targetWidget = findWidget(target, null);
  913. if (targetWidget instanceof com.google.gwt.user.client.ui.Focusable) {
  914. final com.google.gwt.user.client.ui.Focusable toBeFocusedWidget = (com.google.gwt.user.client.ui.Focusable) targetWidget;
  915. toBeFocusedWidget.setFocus(true);
  916. } else if (targetWidget instanceof Focusable) {
  917. ((Focusable) targetWidget).focus();
  918. }
  919. Scheduler.get().scheduleDeferred(new ScheduledCommand() {
  920. @Override
  921. public void execute() {
  922. try {
  923. target.dispatchEvent(createMouseDownEvent);
  924. target.dispatchEvent(createMouseUpEvent);
  925. target.dispatchEvent(createMouseClickEvent);
  926. } catch (Exception e) {
  927. }
  928. }
  929. });
  930. }
  931. /**
  932. * Gets the currently focused element.
  933. *
  934. * @return The active element or null if no active element could be found.
  935. */
  936. public native static Element getFocusedElement()
  937. /*-{
  938. if ($wnd.document.activeElement) {
  939. return $wnd.document.activeElement;
  940. }
  941. return null;
  942. }-*/
  943. ;
  944. /**
  945. * Gets the currently focused element for Internet Explorer.
  946. *
  947. * @return The currently focused element
  948. * @deprecated Use #getFocusedElement instead
  949. */
  950. @Deprecated
  951. public static Element getIEFocusedElement() {
  952. return getFocusedElement();
  953. }
  954. /**
  955. * Kind of stronger version of isAttached(). In addition to std isAttached,
  956. * this method checks that this widget nor any of its parents is hidden. Can
  957. * be e.g used to check whether component should react to some events or
  958. * not.
  959. *
  960. * @param widget
  961. * @return true if attached and displayed
  962. */
  963. public static boolean isAttachedAndDisplayed(Widget widget) {
  964. if (widget.isAttached()) {
  965. /*
  966. * Failfast using offset size, then by iterating the widget tree
  967. */
  968. boolean notZeroSized = widget.getOffsetHeight() > 0
  969. || widget.getOffsetWidth() > 0;
  970. return notZeroSized || checkVisibilityRecursively(widget);
  971. } else {
  972. return false;
  973. }
  974. }
  975. private static boolean checkVisibilityRecursively(Widget widget) {
  976. if (widget.isVisible()) {
  977. Widget parent = widget.getParent();
  978. if (parent == null) {
  979. return true; // root panel
  980. } else {
  981. return checkVisibilityRecursively(parent);
  982. }
  983. } else {
  984. return false;
  985. }
  986. }
  987. /**
  988. * Scrolls an element into view vertically only. Modified version of
  989. * Element.scrollIntoView.
  990. *
  991. * @param elem
  992. * The element to scroll into view
  993. */
  994. public static native void scrollIntoViewVertically(Element elem)
  995. /*-{
  996. var top = elem.offsetTop;
  997. var height = elem.offsetHeight;
  998. if (elem.parentNode != elem.offsetParent) {
  999. top -= elem.parentNode.offsetTop;
  1000. }
  1001. var cur = elem.parentNode;
  1002. while (cur && (cur.nodeType == 1)) {
  1003. if (top < cur.scrollTop) {
  1004. cur.scrollTop = top;
  1005. }
  1006. if (top + height > cur.scrollTop + cur.clientHeight) {
  1007. cur.scrollTop = (top + height) - cur.clientHeight;
  1008. }
  1009. var offsetTop = cur.offsetTop;
  1010. if (cur.parentNode != cur.offsetParent) {
  1011. offsetTop -= cur.parentNode.offsetTop;
  1012. }
  1013. top += offsetTop - cur.scrollTop;
  1014. cur = cur.parentNode;
  1015. }
  1016. }-*/;
  1017. /**
  1018. * Checks if the given event is either a touch event or caused by the left
  1019. * mouse button
  1020. *
  1021. * @param event
  1022. * @return true if the event is a touch event or caused by the left mouse
  1023. * button, false otherwise
  1024. */
  1025. public static boolean isTouchEventOrLeftMouseButton(Event event) {
  1026. boolean touchEvent = Util.isTouchEvent(event);
  1027. return touchEvent || event.getButton() == Event.BUTTON_LEFT;
  1028. }
  1029. /**
  1030. * Performs a shallow comparison of the collections.
  1031. *
  1032. * @param collection1
  1033. * The first collection
  1034. * @param collection2
  1035. * The second collection
  1036. * @return true if the collections contain the same elements in the same
  1037. * order, false otherwise
  1038. */
  1039. public static boolean collectionsEquals(Collection collection1,
  1040. Collection collection2) {
  1041. if (collection1 == null) {
  1042. return collection2 == null;
  1043. }
  1044. if (collection2 == null) {
  1045. return false;
  1046. }
  1047. Iterator<Object> collection1Iterator = collection1.iterator();
  1048. Iterator<Object> collection2Iterator = collection2.iterator();
  1049. while (collection1Iterator.hasNext()) {
  1050. if (!collection2Iterator.hasNext()) {
  1051. return false;
  1052. }
  1053. Object collection1Object = collection1Iterator.next();
  1054. Object collection2Object = collection2Iterator.next();
  1055. if (collection1Object != collection2Object) {
  1056. return false;
  1057. }
  1058. }
  1059. if (collection2Iterator.hasNext()) {
  1060. return false;
  1061. }
  1062. return true;
  1063. }
  1064. public static String getConnectorString(ServerConnector p) {
  1065. if (p == null) {
  1066. return "null";
  1067. }
  1068. return getSimpleName(p) + " (" + p.getConnectorId() + ")";
  1069. }
  1070. /**
  1071. * Resolve a relative URL to an absolute URL based on the current document's
  1072. * location.
  1073. *
  1074. * @param url
  1075. * a string with the relative URL to resolve
  1076. * @return the corresponding absolute URL as a string
  1077. */
  1078. public static String getAbsoluteUrl(String url) {
  1079. if (BrowserInfo.get().isIE8()) {
  1080. // The hard way - must use innerHTML and attach to DOM in IE8
  1081. DivElement divElement = Document.get().createDivElement();
  1082. divElement.getStyle().setDisplay(Display.NONE);
  1083. RootPanel.getBodyElement().appendChild(divElement);
  1084. divElement.setInnerHTML("<a href='" + escapeHTML(url) + "' ></a>");
  1085. AnchorElement a = divElement.getChild(0).cast();
  1086. String href = a.getHref();
  1087. RootPanel.getBodyElement().removeChild(divElement);
  1088. return href;
  1089. } else {
  1090. AnchorElement a = Document.get().createAnchorElement();
  1091. a.setHref(url);
  1092. return a.getHref();
  1093. }
  1094. }
  1095. }