You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Util.java 35KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076
  1. /*
  2. @ITMillApache2LicenseForJavaFiles@
  3. */
  4. package com.vaadin.terminal.gwt.client;
  5. import java.util.ArrayList;
  6. import java.util.HashMap;
  7. import java.util.HashSet;
  8. import java.util.Iterator;
  9. import java.util.Map;
  10. import java.util.Set;
  11. import com.google.gwt.core.client.GWT;
  12. import com.google.gwt.core.client.Scheduler;
  13. import com.google.gwt.dom.client.DivElement;
  14. import com.google.gwt.dom.client.Document;
  15. import com.google.gwt.dom.client.Node;
  16. import com.google.gwt.dom.client.NodeList;
  17. import com.google.gwt.dom.client.Style;
  18. import com.google.gwt.user.client.Command;
  19. import com.google.gwt.user.client.DOM;
  20. import com.google.gwt.user.client.Element;
  21. import com.google.gwt.user.client.Event;
  22. import com.google.gwt.user.client.EventListener;
  23. import com.google.gwt.user.client.Timer;
  24. import com.google.gwt.user.client.Window;
  25. import com.google.gwt.user.client.ui.HasWidgets;
  26. import com.google.gwt.user.client.ui.RootPanel;
  27. import com.google.gwt.user.client.ui.Widget;
  28. import com.vaadin.terminal.gwt.client.RenderInformation.FloatSize;
  29. public class Util {
  30. /**
  31. * Helper method for debugging purposes.
  32. *
  33. * Stops execution on firefox browsers on a breakpoint.
  34. *
  35. */
  36. public static native void browserDebugger()
  37. /*-{
  38. if($wnd.console)
  39. debugger;
  40. }-*/;
  41. /**
  42. *
  43. * Returns the topmost element of from given coordinates.
  44. *
  45. * TODO fix crossplat issues clientX vs pageX. See quircksmode. Not critical
  46. * for vaadin as we scroll div istead of page.
  47. *
  48. * @param x
  49. * @param y
  50. * @return the element at given coordinates
  51. */
  52. public static native Element getElementFromPoint(int clientX, int clientY)
  53. /*-{
  54. var el = $wnd.document.elementFromPoint(clientX, clientY);
  55. if(el != null && el.nodeType == 3) {
  56. el = el.parentNode;
  57. }
  58. return el;
  59. }-*/;
  60. private static final int LAZY_SIZE_CHANGE_TIMEOUT = 400;
  61. private static Set<Paintable> latelyChangedWidgets = new HashSet<Paintable>();
  62. private static Timer lazySizeChangeTimer = new Timer() {
  63. private boolean lazySizeChangeTimerScheduled = false;
  64. @Override
  65. public void run() {
  66. componentSizeUpdated(latelyChangedWidgets);
  67. latelyChangedWidgets.clear();
  68. lazySizeChangeTimerScheduled = false;
  69. }
  70. @Override
  71. public void schedule(int delayMillis) {
  72. if (lazySizeChangeTimerScheduled) {
  73. cancel();
  74. } else {
  75. lazySizeChangeTimerScheduled = true;
  76. }
  77. super.schedule(delayMillis);
  78. }
  79. };
  80. /**
  81. * This helper method can be called if components size have been changed
  82. * outside rendering phase. It notifies components parent about the size
  83. * change so it can react.
  84. *
  85. * When using this method, developer should consider if size changes could
  86. * be notified lazily. If lazy flag is true, method will save widget and
  87. * wait for a moment until it notifies parents in chunks. This may vastly
  88. * optimize layout in various situation. Example: if component have a lot of
  89. * images their onload events may fire "layout phase" many times in a short
  90. * period.
  91. *
  92. * @param widget
  93. * @param lazy
  94. * run componentSizeUpdated lazyly
  95. */
  96. public static void notifyParentOfSizeChange(Paintable widget, boolean lazy) {
  97. if (lazy) {
  98. latelyChangedWidgets.add(widget);
  99. lazySizeChangeTimer.schedule(LAZY_SIZE_CHANGE_TIMEOUT);
  100. } else {
  101. Set<Paintable> widgets = new HashSet<Paintable>();
  102. widgets.add(widget);
  103. Util.componentSizeUpdated(widgets);
  104. }
  105. }
  106. /**
  107. * Called when the size of one or more widgets have changed during
  108. * rendering. Finds parent container and notifies them of the size change.
  109. *
  110. * @param paintables
  111. */
  112. public static void componentSizeUpdated(Set<Paintable> paintables) {
  113. if (paintables.isEmpty()) {
  114. return;
  115. }
  116. Map<Container, Set<Paintable>> childWidgets = new HashMap<Container, Set<Paintable>>();
  117. for (Paintable paintable : paintables) {
  118. Widget widget = (Widget) paintable;
  119. if (!widget.isAttached()) {
  120. continue;
  121. }
  122. // ApplicationConnection.getConsole().log(
  123. // "Widget " + Util.getSimpleName(widget) + " size updated");
  124. Widget parent = widget.getParent();
  125. while (parent != null && !(parent instanceof Container)) {
  126. parent = parent.getParent();
  127. }
  128. if (parent != null) {
  129. Set<Paintable> set = childWidgets.get(parent);
  130. if (set == null) {
  131. set = new HashSet<Paintable>();
  132. childWidgets.put((Container) parent, set);
  133. }
  134. set.add(paintable);
  135. }
  136. }
  137. Set<Paintable> parentChanges = new HashSet<Paintable>();
  138. for (Container parent : childWidgets.keySet()) {
  139. if (!parent.requestLayout(childWidgets.get(parent))) {
  140. parentChanges.add(parent);
  141. }
  142. }
  143. componentSizeUpdated(parentChanges);
  144. }
  145. public static float parseRelativeSize(String size) {
  146. if (size == null || !size.endsWith("%")) {
  147. return -1;
  148. }
  149. try {
  150. return Float.parseFloat(size.substring(0, size.length() - 1));
  151. } catch (Exception e) {
  152. VConsole.log("Unable to parse relative size");
  153. return -1;
  154. }
  155. }
  156. /**
  157. * Returns closest parent Widget in hierarchy that implements Container
  158. * interface
  159. *
  160. * @param component
  161. * @return closest parent Container
  162. */
  163. public static Container getLayout(Widget component) {
  164. Widget parent = component.getParent();
  165. while (parent != null && !(parent instanceof Container)) {
  166. parent = parent.getParent();
  167. }
  168. if (parent != null) {
  169. assert ((Container) parent).hasChildComponent(component);
  170. return (Container) parent;
  171. }
  172. return null;
  173. }
  174. /**
  175. * Detects if current browser is IE.
  176. *
  177. * @deprecated use BrowserInfo class instead
  178. *
  179. * @return true if IE
  180. */
  181. @Deprecated
  182. public static boolean isIE() {
  183. return BrowserInfo.get().isIE();
  184. }
  185. /**
  186. * Detects if current browser is IE6.
  187. *
  188. * @deprecated use BrowserInfo class instead
  189. *
  190. * @return true if IE6
  191. */
  192. @Deprecated
  193. public static boolean isIE6() {
  194. return BrowserInfo.get().isIE6();
  195. }
  196. /**
  197. * @deprecated use BrowserInfo class instead
  198. * @return
  199. */
  200. @Deprecated
  201. public static boolean isIE7() {
  202. return BrowserInfo.get().isIE7();
  203. }
  204. /**
  205. * @deprecated use BrowserInfo class instead
  206. * @return
  207. */
  208. @Deprecated
  209. public static boolean isFF2() {
  210. return BrowserInfo.get().isFF2();
  211. }
  212. private static final Element escapeHtmlHelper = DOM.createDiv();
  213. /**
  214. * Converts html entities to text.
  215. *
  216. * @param html
  217. * @return escaped string presentation of given html
  218. */
  219. public static String escapeHTML(String html) {
  220. DOM.setInnerText(escapeHtmlHelper, html);
  221. return DOM.getInnerHTML(escapeHtmlHelper);
  222. }
  223. /**
  224. * Adds transparent PNG fix to image element; only use for IE6.
  225. *
  226. * @param el
  227. * IMG element
  228. */
  229. public native static void addPngFix(Element el)
  230. /*-{
  231. el.attachEvent("onload", function() {
  232. @com.vaadin.terminal.gwt.client.Util::doIE6PngFix(Lcom/google/gwt/user/client/Element;)(el);
  233. },false);
  234. }-*/;
  235. private native static void doPngFix(Element el, String blankImageUrl)
  236. /*-{
  237. var src = el.src;
  238. if (src.indexOf(".png") < 1) return;
  239. var w = el.width || 16;
  240. var h = el.height || 16;
  241. if(h==30 || w==28) {
  242. setTimeout(function(){
  243. el.style.height = el.height + "px";
  244. el.style.width = el.width + "px";
  245. el.src = blankImageUrl;
  246. },10);
  247. } else {
  248. el.src = blankImageUrl;
  249. el.style.height = h + "px";
  250. el.style.width = w + "px";
  251. }
  252. el.style.padding = "0";
  253. el.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+src+"', sizingMethod='crop')";
  254. }-*/;
  255. public static void doIE6PngFix(Element el) {
  256. String blankImageUrl = GWT.getModuleBaseURL() + "ie6pngfix/blank.gif";
  257. String src = el.getAttribute("src");
  258. if (src != null && !src.equals(blankImageUrl)) {
  259. doPngFix(el, blankImageUrl);
  260. }
  261. }
  262. /**
  263. * Clones given element as in JavaScript.
  264. *
  265. * Deprecate this if there appears similar method into GWT someday.
  266. *
  267. * @param element
  268. * @param deep
  269. * clone child tree also
  270. * @return
  271. */
  272. public static native Element cloneNode(Element element, boolean deep)
  273. /*-{
  274. return element.cloneNode(deep);
  275. }-*/;
  276. public static int measureHorizontalPaddingAndBorder(Element element,
  277. int paddingGuess) {
  278. String originalWidth = DOM.getStyleAttribute(element, "width");
  279. String originalOverflow = "";
  280. if (BrowserInfo.get().isIE6()) {
  281. originalOverflow = DOM.getStyleAttribute(element, "overflow");
  282. DOM.setStyleAttribute(element, "overflow", "hidden");
  283. }
  284. int originalOffsetWidth = element.getOffsetWidth();
  285. int widthGuess = (originalOffsetWidth - paddingGuess);
  286. if (widthGuess < 1) {
  287. widthGuess = 1;
  288. }
  289. DOM.setStyleAttribute(element, "width", widthGuess + "px");
  290. int padding = element.getOffsetWidth() - widthGuess;
  291. DOM.setStyleAttribute(element, "width", originalWidth);
  292. if (BrowserInfo.get().isIE6()) {
  293. DOM.setStyleAttribute(element, "overflow", originalOverflow);
  294. }
  295. return padding;
  296. }
  297. public static int measureVerticalPaddingAndBorder(Element element,
  298. int paddingGuess) {
  299. String originalHeight = DOM.getStyleAttribute(element, "height");
  300. int originalOffsetHeight = element.getOffsetHeight();
  301. int widthGuess = (originalOffsetHeight - paddingGuess);
  302. if (widthGuess < 1) {
  303. widthGuess = 1;
  304. }
  305. DOM.setStyleAttribute(element, "height", widthGuess + "px");
  306. int padding = element.getOffsetHeight() - widthGuess;
  307. DOM.setStyleAttribute(element, "height", originalHeight);
  308. return padding;
  309. }
  310. public static int measureHorizontalBorder(Element element) {
  311. int borders;
  312. // element.getClientWidth();
  313. if (BrowserInfo.get().isIE()) {
  314. String width = element.getStyle().getProperty("width");
  315. String height = element.getStyle().getProperty("height");
  316. int offsetWidth = element.getOffsetWidth();
  317. int offsetHeight = element.getOffsetHeight();
  318. if (BrowserInfo.get().isIE6() || BrowserInfo.get().isIE8()) {
  319. if (offsetHeight < 1) {
  320. offsetHeight = 1;
  321. }
  322. if (offsetWidth < 1) {
  323. offsetWidth = 10;
  324. }
  325. element.getStyle().setPropertyPx("height", offsetHeight);
  326. }
  327. element.getStyle().setPropertyPx("width", offsetWidth);
  328. borders = element.getOffsetWidth() - element.getClientWidth();
  329. element.getStyle().setProperty("width", width);
  330. if (BrowserInfo.get().isIE6() || BrowserInfo.get().isIE8()) {
  331. element.getStyle().setProperty("height", height);
  332. }
  333. } else {
  334. borders = element.getOffsetWidth()
  335. - element.getPropertyInt("clientWidth");
  336. }
  337. assert borders >= 0;
  338. return borders;
  339. }
  340. public static int measureVerticalBorder(Element element) {
  341. int borders;
  342. if (BrowserInfo.get().isIE()) {
  343. String width = element.getStyle().getProperty("width");
  344. String height = element.getStyle().getProperty("height");
  345. int offsetWidth = element.getOffsetWidth();
  346. int offsetHeight = element.getOffsetHeight();
  347. // if (BrowserInfo.get().isIE6()) {
  348. if (offsetHeight < 1) {
  349. offsetHeight = 1;
  350. }
  351. if (offsetWidth < 1) {
  352. offsetWidth = 10;
  353. }
  354. element.getStyle().setPropertyPx("width", offsetWidth);
  355. // }
  356. element.getStyle().setPropertyPx("height", offsetHeight);
  357. borders = element.getOffsetHeight()
  358. - element.getPropertyInt("clientHeight");
  359. element.getStyle().setProperty("height", height);
  360. // if (BrowserInfo.get().isIE6()) {
  361. element.getStyle().setProperty("width", width);
  362. // }
  363. } else {
  364. borders = element.getOffsetHeight()
  365. - element.getPropertyInt("clientHeight");
  366. }
  367. assert borders >= 0;
  368. return borders;
  369. }
  370. public static int measureMarginLeft(Element element) {
  371. return element.getAbsoluteLeft()
  372. - element.getParentElement().getAbsoluteLeft();
  373. }
  374. public static int setHeightExcludingPaddingAndBorder(Widget widget,
  375. String height, int paddingBorderGuess) {
  376. if (height.equals("")) {
  377. setHeight(widget, "");
  378. return paddingBorderGuess;
  379. } else if (height.endsWith("px")) {
  380. int pixelHeight = Integer.parseInt(height.substring(0,
  381. height.length() - 2));
  382. return setHeightExcludingPaddingAndBorder(widget.getElement(),
  383. pixelHeight, paddingBorderGuess, false);
  384. } else {
  385. // Set the height in unknown units
  386. setHeight(widget, height);
  387. // Use the offsetWidth
  388. return setHeightExcludingPaddingAndBorder(widget.getElement(),
  389. widget.getOffsetHeight(), paddingBorderGuess, true);
  390. }
  391. }
  392. private static void setWidth(Widget widget, String width) {
  393. DOM.setStyleAttribute(widget.getElement(), "width", width);
  394. }
  395. private static void setHeight(Widget widget, String height) {
  396. DOM.setStyleAttribute(widget.getElement(), "height", height);
  397. }
  398. public static int setWidthExcludingPaddingAndBorder(Widget widget,
  399. String width, int paddingBorderGuess) {
  400. if (width.equals("")) {
  401. setWidth(widget, "");
  402. return paddingBorderGuess;
  403. } else if (width.endsWith("px")) {
  404. int pixelWidth = Integer.parseInt(width.substring(0,
  405. width.length() - 2));
  406. return setWidthExcludingPaddingAndBorder(widget.getElement(),
  407. pixelWidth, paddingBorderGuess, false);
  408. } else {
  409. setWidth(widget, width);
  410. return setWidthExcludingPaddingAndBorder(widget.getElement(),
  411. widget.getOffsetWidth(), paddingBorderGuess, true);
  412. }
  413. }
  414. public static int setWidthExcludingPaddingAndBorder(Element element,
  415. int requestedWidth, int horizontalPaddingBorderGuess,
  416. boolean requestedWidthIncludesPaddingBorder) {
  417. int widthGuess = requestedWidth - horizontalPaddingBorderGuess;
  418. if (widthGuess < 0) {
  419. widthGuess = 0;
  420. }
  421. DOM.setStyleAttribute(element, "width", widthGuess + "px");
  422. int captionOffsetWidth = DOM.getElementPropertyInt(element,
  423. "offsetWidth");
  424. int actualPadding = captionOffsetWidth - widthGuess;
  425. if (requestedWidthIncludesPaddingBorder) {
  426. actualPadding += actualPadding;
  427. }
  428. if (actualPadding != horizontalPaddingBorderGuess) {
  429. int w = requestedWidth - actualPadding;
  430. if (w < 0) {
  431. // Cannot set negative width even if we would want to
  432. w = 0;
  433. }
  434. DOM.setStyleAttribute(element, "width", w + "px");
  435. }
  436. return actualPadding;
  437. }
  438. public static int setHeightExcludingPaddingAndBorder(Element element,
  439. int requestedHeight, int verticalPaddingBorderGuess,
  440. boolean requestedHeightIncludesPaddingBorder) {
  441. int heightGuess = requestedHeight - verticalPaddingBorderGuess;
  442. if (heightGuess < 0) {
  443. heightGuess = 0;
  444. }
  445. DOM.setStyleAttribute(element, "height", heightGuess + "px");
  446. int captionOffsetHeight = DOM.getElementPropertyInt(element,
  447. "offsetHeight");
  448. int actualPadding = captionOffsetHeight - heightGuess;
  449. if (requestedHeightIncludesPaddingBorder) {
  450. actualPadding += actualPadding;
  451. }
  452. if (actualPadding != verticalPaddingBorderGuess) {
  453. int h = requestedHeight - actualPadding;
  454. if (h < 0) {
  455. // Cannot set negative height even if we would want to
  456. h = 0;
  457. }
  458. DOM.setStyleAttribute(element, "height", h + "px");
  459. }
  460. return actualPadding;
  461. }
  462. public static String getSimpleName(Object widget) {
  463. if (widget == null) {
  464. return "(null)";
  465. }
  466. String name = widget.getClass().getName();
  467. return name.substring(name.lastIndexOf('.') + 1);
  468. }
  469. public static void setFloat(Element element, String value) {
  470. if (BrowserInfo.get().isIE()) {
  471. DOM.setStyleAttribute(element, "styleFloat", value);
  472. } else {
  473. DOM.setStyleAttribute(element, "cssFloat", value);
  474. }
  475. }
  476. private static int detectedScrollbarSize = -1;
  477. public static int getNativeScrollbarSize() {
  478. if (detectedScrollbarSize < 0) {
  479. Element scroller = DOM.createDiv();
  480. scroller.getStyle().setProperty("width", "50px");
  481. scroller.getStyle().setProperty("height", "50px");
  482. scroller.getStyle().setProperty("overflow", "scroll");
  483. scroller.getStyle().setProperty("position", "absolute");
  484. scroller.getStyle().setProperty("marginLeft", "-5000px");
  485. RootPanel.getBodyElement().appendChild(scroller);
  486. detectedScrollbarSize = scroller.getOffsetWidth()
  487. - scroller.getPropertyInt("clientWidth");
  488. // Asserting the detected value causes a problem
  489. // at least in Hosted Mode Browser/Linux/GWT-1.5.3, so
  490. // use a default if detection fails.
  491. if (detectedScrollbarSize == 0) {
  492. detectedScrollbarSize = 20;
  493. }
  494. RootPanel.getBodyElement().removeChild(scroller);
  495. }
  496. return detectedScrollbarSize;
  497. }
  498. /**
  499. * Run workaround for webkits overflow auto issue.
  500. *
  501. * See: our bug #2138 and https://bugs.webkit.org/show_bug.cgi?id=21462
  502. *
  503. * @param elem
  504. * with overflow auto
  505. */
  506. public static void runWebkitOverflowAutoFix(final Element elem) {
  507. // Add max version if fix lands sometime to Webkit
  508. // Starting from Opera 11.00, also a problem in Opera
  509. if (BrowserInfo.get().getWebkitVersion() > 0
  510. || BrowserInfo.get().getOperaVersion() >= 11) {
  511. final String originalOverflow = elem.getStyle().getProperty(
  512. "overflow");
  513. if ("hidden".equals(originalOverflow)) {
  514. return;
  515. }
  516. // check the scrolltop value before hiding the element
  517. final int scrolltop = elem.getScrollTop();
  518. elem.getStyle().setProperty("overflow", "hidden");
  519. Scheduler.get().scheduleDeferred(new Command() {
  520. public void execute() {
  521. // Dough, Safari scroll auto means actually just a moped
  522. elem.getStyle().setProperty("overflow", originalOverflow);
  523. if (scrolltop > 0 || elem.getScrollTop() > 0) {
  524. int scrollvalue = scrolltop;
  525. if (scrolltop == 0) {
  526. // mysterious are the ways of webkits scrollbar
  527. // handling. In some cases webkit reports bad (0)
  528. // scrolltop before hiding the element temporary,
  529. // sometimes after.
  530. scrollvalue = elem.getScrollTop();
  531. }
  532. // fix another bug where scrollbar remains in wrong
  533. // position
  534. elem.setScrollTop(scrollvalue - 1);
  535. elem.setScrollTop(scrollvalue);
  536. }
  537. }
  538. });
  539. }
  540. }
  541. /**
  542. * Parses the UIDL parameter and fetches the relative size of the component.
  543. * If a dimension is not specified as relative it will return -1. If the
  544. * UIDL does not contain width or height specifications this will return
  545. * null.
  546. *
  547. * @param uidl
  548. * @return
  549. */
  550. public static FloatSize parseRelativeSize(UIDL uidl) {
  551. boolean hasAttribute = false;
  552. String w = "";
  553. String h = "";
  554. if (uidl.hasAttribute("width")) {
  555. hasAttribute = true;
  556. w = uidl.getStringAttribute("width");
  557. }
  558. if (uidl.hasAttribute("height")) {
  559. hasAttribute = true;
  560. h = uidl.getStringAttribute("height");
  561. }
  562. if (!hasAttribute) {
  563. return null;
  564. }
  565. float relativeWidth = Util.parseRelativeSize(w);
  566. float relativeHeight = Util.parseRelativeSize(h);
  567. FloatSize relativeSize = new FloatSize(relativeWidth, relativeHeight);
  568. return relativeSize;
  569. }
  570. public static boolean isCached(UIDL uidl) {
  571. return uidl.getBooleanAttribute("cached");
  572. }
  573. public static void alert(String string) {
  574. if (true) {
  575. Window.alert(string);
  576. }
  577. }
  578. public static boolean equals(Object a, Object b) {
  579. if (a == null) {
  580. return b == null;
  581. }
  582. return a.equals(b);
  583. }
  584. public static void updateRelativeChildrenAndSendSizeUpdateEvent(
  585. ApplicationConnection client, HasWidgets container) {
  586. updateRelativeChildrenAndSendSizeUpdateEvent(client, container,
  587. (Paintable) container);
  588. }
  589. public static void updateRelativeChildrenAndSendSizeUpdateEvent(
  590. ApplicationConnection client, HasWidgets container, Paintable widget) {
  591. /*
  592. * Relative sized children must be updated first so the component has
  593. * the correct outer dimensions when signaling a size change to the
  594. * parent.
  595. */
  596. Iterator<Widget> childIterator = container.iterator();
  597. while (childIterator.hasNext()) {
  598. Widget w = childIterator.next();
  599. client.handleComponentRelativeSize(w);
  600. }
  601. HashSet<Paintable> widgets = new HashSet<Paintable>();
  602. widgets.add(widget);
  603. Util.componentSizeUpdated(widgets);
  604. }
  605. public static native int getRequiredWidth(
  606. com.google.gwt.dom.client.Element element)
  607. /*-{
  608. if (element.getBoundingClientRect) {
  609. var rect = element.getBoundingClientRect();
  610. return Math.ceil(rect.right - rect.left);
  611. } else {
  612. return element.offsetWidth;
  613. }
  614. }-*/;
  615. public static native int getRequiredHeight(
  616. com.google.gwt.dom.client.Element element)
  617. /*-{
  618. var height;
  619. if (element.getBoundingClientRect != null) {
  620. var rect = element.getBoundingClientRect();
  621. height = Math.ceil(rect.bottom - rect.top);
  622. } else {
  623. height = element.offsetHeight;
  624. }
  625. return height;
  626. }-*/;
  627. public static int getRequiredWidth(Widget widget) {
  628. return getRequiredWidth(widget.getElement());
  629. }
  630. public static int getRequiredHeight(Widget widget) {
  631. return getRequiredHeight(widget.getElement());
  632. }
  633. /**
  634. * Detects what is currently the overflow style attribute in given element.
  635. *
  636. * @param pe
  637. * the element to detect
  638. * @return true if auto or scroll
  639. */
  640. public static boolean mayHaveScrollBars(com.google.gwt.dom.client.Element pe) {
  641. String overflow = getComputedStyle(pe, "overflow");
  642. if (overflow != null) {
  643. if (overflow.equals("auto") || overflow.equals("scroll")) {
  644. return true;
  645. } else {
  646. return false;
  647. }
  648. } else {
  649. return false;
  650. }
  651. }
  652. /**
  653. * A simple helper method to detect "computed style" (aka style sheets +
  654. * element styles). Values returned differ a lot depending on browsers.
  655. * Always be very careful when using this.
  656. *
  657. * @param el
  658. * the element from which the style property is detected
  659. * @param p
  660. * the property to detect
  661. * @return String value of style property
  662. */
  663. private static native String getComputedStyle(
  664. com.google.gwt.dom.client.Element el, String p)
  665. /*-{
  666. try {
  667. if (el.currentStyle) {
  668. // IE
  669. return el.currentStyle[p];
  670. } else if (window.getComputedStyle) {
  671. // Sa, FF, Opera
  672. var view = el.ownerDocument.defaultView;
  673. return view.getComputedStyle(el,null).getPropertyValue(p);
  674. } else {
  675. // fall back for non IE, Sa, FF, Opera
  676. return "";
  677. }
  678. } catch (e) {
  679. return "";
  680. }
  681. }-*/;
  682. /**
  683. * IE7 sometimes "forgets" to render content. This function runs a hack to
  684. * workaround the bug if needed. This happens easily in framset. See #3295.
  685. */
  686. public static void runIE7ZeroSizedBodyFix() {
  687. if (BrowserInfo.get().isIE7()) {
  688. int offsetWidth = RootPanel.getBodyElement().getOffsetWidth();
  689. if (offsetWidth == 0) {
  690. shakeBodyElement();
  691. }
  692. }
  693. }
  694. /**
  695. * Does some very small adjustments to body element. We need this just to
  696. * overcome some IE bugs.
  697. */
  698. public static void shakeBodyElement() {
  699. final DivElement shaker = Document.get().createDivElement();
  700. RootPanel.getBodyElement().insertBefore(shaker,
  701. RootPanel.getBodyElement().getFirstChildElement());
  702. shaker.getStyle().setPropertyPx("height", 0);
  703. shaker.setInnerHTML("&nbsp;");
  704. RootPanel.getBodyElement().removeChild(shaker);
  705. }
  706. /**
  707. * Locates the child component of <literal>parent</literal> which contains
  708. * the element <literal>element</literal>. The child component is also
  709. * returned if "element" is part of its caption. If
  710. * <literal>element</literal> is not part of any child component, null is
  711. * returned.
  712. *
  713. * @param client
  714. * A reference to ApplicationConnection
  715. * @param parent
  716. * The widget that contains <literal>element</literal>.
  717. * @param element
  718. * An element that is a sub element of the parent
  719. * @return The Paintable which the element is a part of. Null if the element
  720. * does not belong to a child.
  721. */
  722. public static Paintable getChildPaintableForElement(
  723. ApplicationConnection client, Container parent, Element element) {
  724. Element rootElement = ((Widget) parent).getElement();
  725. while (element != null && element != rootElement) {
  726. Paintable paintable = client.getPaintable(element);
  727. if (paintable == null) {
  728. String ownerPid = VCaption.getCaptionOwnerPid(element);
  729. if (ownerPid != null) {
  730. paintable = client.getPaintable(ownerPid);
  731. }
  732. }
  733. if (paintable != null) {
  734. try {
  735. if (parent.hasChildComponent((Widget) paintable)) {
  736. return paintable;
  737. }
  738. } catch (ClassCastException e) {
  739. // We assume everything is a widget however there is no need
  740. // to crash everything if there is a paintable that is not.
  741. }
  742. }
  743. element = (Element) element.getParentElement();
  744. }
  745. return null;
  746. }
  747. /**
  748. * Will (attempt) to focus the given DOM Element.
  749. *
  750. * @param el
  751. * the element to focus
  752. */
  753. public static native void focus(Element el)
  754. /*-{
  755. try {
  756. el.focus();
  757. } catch (e) {
  758. }
  759. }-*/;
  760. /**
  761. * Helper method to find first instance of given Widget type found by
  762. * traversing DOM upwards from given element.
  763. *
  764. * @param element
  765. * the element where to start seeking of Widget
  766. * @param class1
  767. * the Widget type to seek for
  768. */
  769. public static <T> T findWidget(Element element,
  770. Class<? extends Widget> class1) {
  771. if (element != null) {
  772. /* First seek for the first EventListener (~Widget) from dom */
  773. EventListener eventListener = null;
  774. while (eventListener == null && element != null) {
  775. eventListener = Event.getEventListener(element);
  776. if (eventListener == null) {
  777. element = (Element) element.getParentElement();
  778. }
  779. }
  780. if (eventListener != null) {
  781. /*
  782. * Then find the first widget of type class1 from widget
  783. * hierarchy
  784. */
  785. Widget w = (Widget) eventListener;
  786. while (w != null) {
  787. if (class1 == null || w.getClass() == class1) {
  788. return (T) w;
  789. }
  790. w = w.getParent();
  791. }
  792. }
  793. }
  794. return null;
  795. }
  796. /**
  797. * Force webkit to redraw an element
  798. *
  799. * @param element
  800. * The element that should be redrawn
  801. */
  802. public static void forceWebkitRedraw(Element element) {
  803. Style style = element.getStyle();
  804. String s = style.getProperty("webkitTransform");
  805. if (s == null || s.length() == 0) {
  806. style.setProperty("webkitTransform", "scale(1)");
  807. } else {
  808. style.setProperty("webkitTransform", "");
  809. }
  810. }
  811. /**
  812. * Detaches and re-attaches the element from its parent. The element is
  813. * reattached at the same position in the DOM as it was before.
  814. *
  815. * Does nothing if the element is not attached to the DOM.
  816. *
  817. * @param element
  818. * The element to detach and re-attach
  819. */
  820. public static void detachAttach(Element element) {
  821. if (element == null) {
  822. return;
  823. }
  824. Node nextSibling = element.getNextSibling();
  825. Node parent = element.getParentNode();
  826. if (parent == null) {
  827. return;
  828. }
  829. parent.removeChild(element);
  830. if (nextSibling == null) {
  831. parent.appendChild(element);
  832. } else {
  833. parent.insertBefore(element, nextSibling);
  834. }
  835. }
  836. public static void sinkOnloadForImages(Element element) {
  837. NodeList<com.google.gwt.dom.client.Element> imgElements = element
  838. .getElementsByTagName("img");
  839. for (int i = 0; i < imgElements.getLength(); i++) {
  840. DOM.sinkEvents((Element) imgElements.getItem(i), Event.ONLOAD);
  841. }
  842. }
  843. /**
  844. * Returns the index of the childElement within its parent.
  845. *
  846. * @param subElement
  847. * @return
  848. */
  849. public static int getChildElementIndex(Element childElement) {
  850. int idx = 0;
  851. Node n = childElement;
  852. while ((n = n.getPreviousSibling()) != null) {
  853. idx++;
  854. }
  855. return idx;
  856. }
  857. private static void printPaintablesVariables(ArrayList<String[]> vars,
  858. String id, ApplicationConnection c) {
  859. Paintable paintable = c.getPaintable(id);
  860. if (paintable != null) {
  861. VConsole.log("\t" + id + " (" + paintable.getClass() + ") :");
  862. for (String[] var : vars) {
  863. VConsole.log("\t\t" + var[1] + " (" + var[2] + ")" + " : "
  864. + var[0]);
  865. }
  866. }
  867. }
  868. static void logVariableBurst(ApplicationConnection c,
  869. ArrayList<String> loggedBurst) {
  870. try {
  871. VConsole.log("Variable burst to be sent to server:");
  872. String curId = null;
  873. ArrayList<String[]> vars = new ArrayList<String[]>();
  874. for (int i = 0; i < loggedBurst.size(); i++) {
  875. String value = loggedBurst.get(i++);
  876. String[] split = loggedBurst.get(i).split(
  877. ApplicationConnection.VAR_FIELD_SEPARATOR);
  878. String id = split[0];
  879. if (curId == null) {
  880. curId = id;
  881. } else if (!curId.equals(id)) {
  882. printPaintablesVariables(vars, curId, c);
  883. vars.clear();
  884. curId = id;
  885. }
  886. split[0] = value;
  887. vars.add(split);
  888. }
  889. if (!vars.isEmpty()) {
  890. printPaintablesVariables(vars, curId, c);
  891. }
  892. } catch (Exception e) {
  893. VConsole.error(e);
  894. }
  895. }
  896. /**
  897. * Temporarily sets the {@code styleProperty} to {@code tempValue} and then
  898. * resets it to its current value. Used mainly to work around rendering
  899. * issues in IE (and possibly in other browsers)
  900. *
  901. * @param element
  902. * The target element
  903. * @param styleProperty
  904. * The name of the property to set
  905. * @param tempValue
  906. * The temporary value
  907. */
  908. public static void setStyleTemporarily(Element element,
  909. final String styleProperty, String tempValue) {
  910. final Style style = element.getStyle();
  911. final String currentValue = style.getProperty(styleProperty);
  912. style.setProperty(styleProperty, tempValue);
  913. element.getOffsetWidth();
  914. style.setProperty(styleProperty, currentValue);
  915. }
  916. /**
  917. * A helper method to return the client position from an event. Returns
  918. * position from either first changed touch (if touch event) or from the
  919. * event itself.
  920. *
  921. * @param event
  922. * @return
  923. */
  924. public static int getTouchOrMouseClientX(Event event) {
  925. if ((event.getTypeInt() & Event.TOUCHEVENTS) > 0) {
  926. return event.getChangedTouches().get(0).getClientX();
  927. } else {
  928. return event.getClientX();
  929. }
  930. }
  931. /**
  932. * A helper method to return the client position from an event. Returns
  933. * position from either first changed touch (if touch event) or from the
  934. * event itself.
  935. *
  936. * @param event
  937. * @return
  938. */
  939. public static int getTouchOrMouseClientY(Event event) {
  940. if ((event.getTypeInt() & Event.TOUCHEVENTS) > 0) {
  941. return event.getChangedTouches().get(0).getClientY();
  942. } else {
  943. return event.getClientY();
  944. }
  945. }
  946. }