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 23KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716
  1. /*
  2. @ITMillApache2LicenseForJavaFiles@
  3. */
  4. package com.vaadin.terminal.gwt.client;
  5. import java.util.HashMap;
  6. import java.util.HashSet;
  7. import java.util.Iterator;
  8. import java.util.Map;
  9. import java.util.Set;
  10. import com.google.gwt.user.client.Command;
  11. import com.google.gwt.user.client.DOM;
  12. import com.google.gwt.user.client.DeferredCommand;
  13. import com.google.gwt.user.client.Element;
  14. import com.google.gwt.user.client.Timer;
  15. import com.google.gwt.user.client.Window;
  16. import com.google.gwt.user.client.ui.HasWidgets;
  17. import com.google.gwt.user.client.ui.RootPanel;
  18. import com.google.gwt.user.client.ui.Widget;
  19. import com.vaadin.terminal.gwt.client.RenderInformation.FloatSize;
  20. public class Util {
  21. /**
  22. * Helper method for debugging purposes.
  23. *
  24. * Stops execution on firefox browsers on a breakpoint.
  25. *
  26. */
  27. public static native void browserDebugger()
  28. /*-{
  29. if($wnd.console)
  30. debugger;
  31. }-*/;
  32. private static final int LAZY_SIZE_CHANGE_TIMEOUT = 400;
  33. private static Set<Paintable> latelyChangedWidgets = new HashSet<Paintable>();
  34. private static Timer lazySizeChangeTimer = new Timer() {
  35. private boolean lazySizeChangeTimerScheduled = false;
  36. @Override
  37. public void run() {
  38. componentSizeUpdated(latelyChangedWidgets);
  39. latelyChangedWidgets.clear();
  40. lazySizeChangeTimerScheduled = false;
  41. }
  42. @Override
  43. public void schedule(int delayMillis) {
  44. if (lazySizeChangeTimerScheduled) {
  45. cancel();
  46. } else {
  47. lazySizeChangeTimerScheduled = true;
  48. }
  49. super.schedule(delayMillis);
  50. }
  51. };
  52. /**
  53. * This helper method can be called if components size have been changed
  54. * outside rendering phase. It notifies components parent about the size
  55. * change so it can react.
  56. *
  57. * When using this method, developer should consider if size changes could
  58. * be notified lazily. If lazy flag is true, method will save widget and
  59. * wait for a moment until it notifies parents in chunks. This may vastly
  60. * optimize layout in various situation. Example: if component have a lot of
  61. * images their onload events may fire "layout phase" many times in a short
  62. * period.
  63. *
  64. * @param widget
  65. * @param lazy
  66. * run componentSizeUpdated lazyly
  67. */
  68. public static void notifyParentOfSizeChange(Paintable widget, boolean lazy) {
  69. if (lazy) {
  70. latelyChangedWidgets.add(widget);
  71. lazySizeChangeTimer.schedule(LAZY_SIZE_CHANGE_TIMEOUT);
  72. } else {
  73. Set<Paintable> widgets = new HashSet<Paintable>();
  74. widgets.add(widget);
  75. Util.componentSizeUpdated(widgets);
  76. }
  77. }
  78. /**
  79. * Called when the size of one or more widgets have changed during
  80. * rendering. Finds parent container and notifies them of the size change.
  81. *
  82. * @param widgets
  83. */
  84. public static void componentSizeUpdated(Set<Paintable> widgets) {
  85. if (widgets.isEmpty()) {
  86. return;
  87. }
  88. Map<Container, Set<Paintable>> childWidgets = new HashMap<Container, Set<Paintable>>();
  89. for (Paintable widget : widgets) {
  90. // ApplicationConnection.getConsole().log(
  91. // "Widget " + Util.getSimpleName(widget) + " size updated");
  92. Widget parent = ((Widget) widget).getParent();
  93. while (parent != null && !(parent instanceof Container)) {
  94. parent = parent.getParent();
  95. }
  96. if (parent != null) {
  97. Set<Paintable> set = childWidgets.get(parent);
  98. if (set == null) {
  99. set = new HashSet<Paintable>();
  100. childWidgets.put((Container) parent, set);
  101. }
  102. set.add(widget);
  103. }
  104. }
  105. Set<Paintable> parentChanges = new HashSet<Paintable>();
  106. for (Container parent : childWidgets.keySet()) {
  107. if (!parent.requestLayout(childWidgets.get(parent))) {
  108. parentChanges.add(parent);
  109. }
  110. }
  111. componentSizeUpdated(parentChanges);
  112. }
  113. public static float parseRelativeSize(String size) {
  114. if (size == null || !size.endsWith("%")) {
  115. return -1;
  116. }
  117. try {
  118. return Float.parseFloat(size.substring(0, size.length() - 1));
  119. } catch (Exception e) {
  120. ClientExceptionHandler.displayError(
  121. "Unable to parse relative size", e);
  122. }
  123. return -1;
  124. }
  125. /**
  126. * Returns closest parent Widget in hierarchy that implements Container
  127. * interface
  128. *
  129. * @param component
  130. * @return closest parent Container
  131. */
  132. public static Container getLayout(Widget component) {
  133. Widget parent = component.getParent();
  134. while (parent != null && !(parent instanceof Container)) {
  135. parent = parent.getParent();
  136. }
  137. if (parent != null) {
  138. assert ((Container) parent).hasChildComponent(component);
  139. return (Container) parent;
  140. }
  141. return null;
  142. }
  143. /**
  144. * Detects if current browser is IE.
  145. *
  146. * @deprecated use BrowserInfo class instead
  147. *
  148. * @return true if IE
  149. */
  150. @Deprecated
  151. public static boolean isIE() {
  152. return BrowserInfo.get().isIE();
  153. }
  154. /**
  155. * Detects if current browser is IE6.
  156. *
  157. * @deprecated use BrowserInfo class instead
  158. *
  159. * @return true if IE6
  160. */
  161. @Deprecated
  162. public static boolean isIE6() {
  163. return BrowserInfo.get().isIE6();
  164. }
  165. /**
  166. * @deprecated use BrowserInfo class instead
  167. * @return
  168. */
  169. @Deprecated
  170. public static boolean isIE7() {
  171. return BrowserInfo.get().isIE7();
  172. }
  173. /**
  174. * @deprecated use BrowserInfo class instead
  175. * @return
  176. */
  177. @Deprecated
  178. public static boolean isFF2() {
  179. return BrowserInfo.get().isFF2();
  180. }
  181. private static final Element escapeHtmlHelper = DOM.createDiv();
  182. /**
  183. * Converts html entities to text.
  184. *
  185. * @param html
  186. * @return escaped string presentation of given html
  187. */
  188. public static String escapeHTML(String html) {
  189. DOM.setInnerText(escapeHtmlHelper, html);
  190. return DOM.getInnerHTML(escapeHtmlHelper);
  191. }
  192. /**
  193. * Adds transparent PNG fix to image element; only use for IE6.
  194. *
  195. * @param el
  196. * IMG element
  197. * @param blankImageUrl
  198. * URL to transparent one-pixel gif
  199. */
  200. public native static void addPngFix(Element el, String blankImageUrl)
  201. /*-{
  202. el.attachEvent("onload", function() {
  203. var src = el.src;
  204. if (src.indexOf(".png")<1) return;
  205. var w = el.width||16;
  206. var h = el.height||16;
  207. el.src =blankImageUrl;
  208. el.style.height = h+"px";
  209. el.style.width = w+"px";
  210. el.style.padding = "0px";
  211. el.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+src+"', sizingMethod='scale')";
  212. },false);
  213. }-*/;
  214. /**
  215. * Clones given element as in JavaScript.
  216. *
  217. * Deprecate this if there appears similar method into GWT someday.
  218. *
  219. * @param element
  220. * @param deep
  221. * clone child tree also
  222. * @return
  223. */
  224. public static native Element cloneNode(Element element, boolean deep)
  225. /*-{
  226. return element.cloneNode(deep);
  227. }-*/;
  228. public static int measureHorizontalPaddingAndBorder(Element element,
  229. int paddingGuess) {
  230. String originalWidth = DOM.getStyleAttribute(element, "width");
  231. String originalOverflow = "";
  232. if (BrowserInfo.get().isIE6()) {
  233. originalOverflow = DOM.getStyleAttribute(element, "overflow");
  234. DOM.setStyleAttribute(element, "overflow", "hidden");
  235. }
  236. int originalOffsetWidth = element.getOffsetWidth();
  237. int widthGuess = (originalOffsetWidth - paddingGuess);
  238. if (widthGuess < 1) {
  239. widthGuess = 1;
  240. }
  241. DOM.setStyleAttribute(element, "width", widthGuess + "px");
  242. int padding = element.getOffsetWidth() - widthGuess;
  243. DOM.setStyleAttribute(element, "width", originalWidth);
  244. if (BrowserInfo.get().isIE6()) {
  245. DOM.setStyleAttribute(element, "overflow", originalOverflow);
  246. }
  247. return padding;
  248. }
  249. public static int measureVerticalPaddingAndBorder(Element element,
  250. int paddingGuess) {
  251. String originalHeight = DOM.getStyleAttribute(element, "height");
  252. int originalOffsetHeight = element.getOffsetHeight();
  253. int widthGuess = (originalOffsetHeight - paddingGuess);
  254. if (widthGuess < 1) {
  255. widthGuess = 1;
  256. }
  257. DOM.setStyleAttribute(element, "height", widthGuess + "px");
  258. int padding = element.getOffsetHeight() - widthGuess;
  259. DOM.setStyleAttribute(element, "height", originalHeight);
  260. return padding;
  261. }
  262. public static int measureHorizontalBorder(Element element) {
  263. int borders;
  264. int cw = element.getClientWidth();
  265. if (BrowserInfo.get().isIE()) {
  266. String width = element.getStyle().getProperty("width");
  267. String height = element.getStyle().getProperty("height");
  268. int offsetWidth = element.getOffsetWidth();
  269. int offsetHeight = element.getOffsetHeight();
  270. if (BrowserInfo.get().isIE6() || BrowserInfo.get().isIE8()) {
  271. if (offsetHeight < 1) {
  272. offsetHeight = 1;
  273. }
  274. if (offsetWidth < 1) {
  275. offsetWidth = 10;
  276. }
  277. element.getStyle().setPropertyPx("height", offsetHeight);
  278. }
  279. element.getStyle().setPropertyPx("width", offsetWidth);
  280. borders = element.getOffsetWidth() - element.getClientWidth();
  281. element.getStyle().setProperty("width", width);
  282. if (BrowserInfo.get().isIE6() || BrowserInfo.get().isIE8()) {
  283. element.getStyle().setProperty("height", height);
  284. }
  285. } else {
  286. borders = element.getOffsetWidth()
  287. - element.getPropertyInt("clientWidth");
  288. }
  289. assert borders >= 0;
  290. return borders;
  291. }
  292. public static int measureVerticalBorder(Element element) {
  293. int borders;
  294. if (BrowserInfo.get().isIE()) {
  295. String width = element.getStyle().getProperty("width");
  296. String height = element.getStyle().getProperty("height");
  297. int offsetWidth = element.getOffsetWidth();
  298. int offsetHeight = element.getOffsetHeight();
  299. // if (BrowserInfo.get().isIE6()) {
  300. if (offsetHeight < 1) {
  301. offsetHeight = 1;
  302. }
  303. if (offsetWidth < 1) {
  304. offsetWidth = 10;
  305. }
  306. element.getStyle().setPropertyPx("width", offsetWidth);
  307. // }
  308. element.getStyle().setPropertyPx("height", offsetHeight);
  309. borders = element.getOffsetHeight()
  310. - element.getPropertyInt("clientHeight");
  311. element.getStyle().setProperty("height", height);
  312. // if (BrowserInfo.get().isIE6()) {
  313. element.getStyle().setProperty("width", width);
  314. // }
  315. } else {
  316. borders = element.getOffsetHeight()
  317. - element.getPropertyInt("clientHeight");
  318. }
  319. assert borders >= 0;
  320. return borders;
  321. }
  322. public static int measureMarginLeft(Element element) {
  323. return element.getAbsoluteLeft()
  324. - element.getParentElement().getAbsoluteLeft();
  325. }
  326. public static int setHeightExcludingPaddingAndBorder(Widget widget,
  327. String height, int paddingBorderGuess) {
  328. if (height.equals("")) {
  329. setHeight(widget, "");
  330. return paddingBorderGuess;
  331. } else if (height.endsWith("px")) {
  332. int pixelHeight = Integer.parseInt(height.substring(0, height
  333. .length() - 2));
  334. return setHeightExcludingPaddingAndBorder(widget.getElement(),
  335. pixelHeight, paddingBorderGuess, false);
  336. } else {
  337. // Set the height in unknown units
  338. setHeight(widget, height);
  339. // Use the offsetWidth
  340. return setHeightExcludingPaddingAndBorder(widget.getElement(),
  341. widget.getOffsetHeight(), paddingBorderGuess, true);
  342. }
  343. }
  344. private static void setWidth(Widget widget, String width) {
  345. DOM.setStyleAttribute(widget.getElement(), "width", width);
  346. }
  347. private static void setHeight(Widget widget, String height) {
  348. DOM.setStyleAttribute(widget.getElement(), "height", height);
  349. }
  350. public static int setWidthExcludingPaddingAndBorder(Widget widget,
  351. String width, int paddingBorderGuess) {
  352. if (width.equals("")) {
  353. setWidth(widget, "");
  354. return paddingBorderGuess;
  355. } else if (width.endsWith("px")) {
  356. int pixelWidth = Integer.parseInt(width.substring(0,
  357. width.length() - 2));
  358. return setWidthExcludingPaddingAndBorder(widget.getElement(),
  359. pixelWidth, paddingBorderGuess, false);
  360. } else {
  361. setWidth(widget, width);
  362. return setWidthExcludingPaddingAndBorder(widget.getElement(),
  363. widget.getOffsetWidth(), paddingBorderGuess, true);
  364. }
  365. }
  366. public static int setWidthExcludingPaddingAndBorder(Element element,
  367. int requestedWidth, int horizontalPaddingBorderGuess,
  368. boolean requestedWidthIncludesPaddingBorder) {
  369. int widthGuess = requestedWidth - horizontalPaddingBorderGuess;
  370. if (widthGuess < 0) {
  371. widthGuess = 0;
  372. }
  373. DOM.setStyleAttribute(element, "width", widthGuess + "px");
  374. int captionOffsetWidth = DOM.getElementPropertyInt(element,
  375. "offsetWidth");
  376. int actualPadding = captionOffsetWidth - widthGuess;
  377. if (requestedWidthIncludesPaddingBorder) {
  378. actualPadding += actualPadding;
  379. }
  380. if (actualPadding != horizontalPaddingBorderGuess) {
  381. int w = requestedWidth - actualPadding;
  382. if (w < 0) {
  383. // Cannot set negative width even if we would want to
  384. w = 0;
  385. }
  386. DOM.setStyleAttribute(element, "width", w + "px");
  387. }
  388. return actualPadding;
  389. }
  390. public static int setHeightExcludingPaddingAndBorder(Element element,
  391. int requestedHeight, int verticalPaddingBorderGuess,
  392. boolean requestedHeightIncludesPaddingBorder) {
  393. int heightGuess = requestedHeight - verticalPaddingBorderGuess;
  394. if (heightGuess < 0) {
  395. heightGuess = 0;
  396. }
  397. DOM.setStyleAttribute(element, "height", heightGuess + "px");
  398. int captionOffsetHeight = DOM.getElementPropertyInt(element,
  399. "offsetHeight");
  400. int actualPadding = captionOffsetHeight - heightGuess;
  401. if (requestedHeightIncludesPaddingBorder) {
  402. actualPadding += actualPadding;
  403. }
  404. if (actualPadding != verticalPaddingBorderGuess) {
  405. int h = requestedHeight - actualPadding;
  406. if (h < 0) {
  407. // Cannot set negative height even if we would want to
  408. h = 0;
  409. }
  410. DOM.setStyleAttribute(element, "height", h + "px");
  411. }
  412. return actualPadding;
  413. }
  414. public static String getSimpleName(Object widget) {
  415. if (widget == null) {
  416. return "(null)";
  417. }
  418. String name = widget.getClass().getName();
  419. return name.substring(name.lastIndexOf('.') + 1);
  420. }
  421. public static void setFloat(Element element, String value) {
  422. if (BrowserInfo.get().isIE()) {
  423. DOM.setStyleAttribute(element, "styleFloat", value);
  424. } else {
  425. DOM.setStyleAttribute(element, "cssFloat", value);
  426. }
  427. }
  428. private static int detectedScrollbarSize = -1;
  429. public static int getNativeScrollbarSize() {
  430. if (detectedScrollbarSize < 0) {
  431. Element scroller = DOM.createDiv();
  432. scroller.getStyle().setProperty("width", "50px");
  433. scroller.getStyle().setProperty("height", "50px");
  434. scroller.getStyle().setProperty("overflow", "scroll");
  435. scroller.getStyle().setProperty("position", "absolute");
  436. scroller.getStyle().setProperty("marginLeft", "-5000px");
  437. RootPanel.getBodyElement().appendChild(scroller);
  438. detectedScrollbarSize = scroller.getOffsetWidth()
  439. - scroller.getPropertyInt("clientWidth");
  440. // Asserting the detected value causes a problem
  441. // at least in Hosted Mode Browser/Linux/GWT-1.5.3, so
  442. // use a default if detection fails.
  443. if (detectedScrollbarSize == 0) {
  444. detectedScrollbarSize = 20;
  445. }
  446. RootPanel.getBodyElement().removeChild(scroller);
  447. }
  448. return detectedScrollbarSize;
  449. }
  450. /**
  451. * Run workaround for webkits overflow auto issue.
  452. *
  453. * See: our bug #2138 and https://bugs.webkit.org/show_bug.cgi?id=21462
  454. *
  455. * @param elem
  456. * with overflow auto
  457. */
  458. public static void runWebkitOverflowAutoFix(final Element elem) {
  459. // add max version if fix landes sometime to webkit
  460. if (BrowserInfo.get().getWebkitVersion() > 0) {
  461. elem.getStyle().setProperty("overflow", "hidden");
  462. DeferredCommand.addCommand(new Command() {
  463. public void execute() {
  464. // Dough, safari scoll auto means actually just a moped
  465. elem.getStyle().setProperty("overflow", "auto");
  466. if (elem.getScrollTop() > 0) {
  467. // fix another bug where scrollbar remains in wrong
  468. // position
  469. int scrolltop = elem.getScrollTop();
  470. elem.setScrollTop(scrolltop - 1);
  471. elem.setScrollTop(scrolltop);
  472. }
  473. }
  474. });
  475. }
  476. }
  477. /**
  478. * Parses the UIDL parameter and fetches the relative size of the component.
  479. * If a dimension is not specified as relative it will return -1. If the
  480. * UIDL does not contain width or height specifications this will return
  481. * null.
  482. *
  483. * @param uidl
  484. * @return
  485. */
  486. public static FloatSize parseRelativeSize(UIDL uidl) {
  487. boolean hasAttribute = false;
  488. String w = "";
  489. String h = "";
  490. if (uidl.hasAttribute("width")) {
  491. hasAttribute = true;
  492. w = uidl.getStringAttribute("width");
  493. }
  494. if (uidl.hasAttribute("height")) {
  495. hasAttribute = true;
  496. h = uidl.getStringAttribute("height");
  497. }
  498. if (!hasAttribute) {
  499. return null;
  500. }
  501. float relativeWidth = Util.parseRelativeSize(w);
  502. float relativeHeight = Util.parseRelativeSize(h);
  503. FloatSize relativeSize = new FloatSize(relativeWidth, relativeHeight);
  504. return relativeSize;
  505. }
  506. public static boolean isCached(UIDL uidl) {
  507. return uidl.getBooleanAttribute("cached");
  508. }
  509. public static void alert(String string) {
  510. if (true) {
  511. Window.alert(string);
  512. }
  513. }
  514. public static boolean equals(Object a, Object b) {
  515. if (a == null) {
  516. return b == null;
  517. }
  518. return a.equals(b);
  519. }
  520. public static void updateRelativeChildrenAndSendSizeUpdateEvent(
  521. ApplicationConnection client, HasWidgets container) {
  522. updateRelativeChildrenAndSendSizeUpdateEvent(client, container,
  523. (Paintable) container);
  524. }
  525. public static void updateRelativeChildrenAndSendSizeUpdateEvent(
  526. ApplicationConnection client, HasWidgets container, Paintable widget) {
  527. /*
  528. * Relative sized children must be updated first so the component has
  529. * the correct outer dimensions when signaling a size change to the
  530. * parent.
  531. */
  532. Iterator<Widget> childIterator = container.iterator();
  533. while (childIterator.hasNext()) {
  534. Widget w = childIterator.next();
  535. client.handleComponentRelativeSize(w);
  536. }
  537. HashSet<Paintable> widgets = new HashSet<Paintable>();
  538. widgets.add(widget);
  539. Util.componentSizeUpdated(widgets);
  540. }
  541. public static native int getRequiredWidth(
  542. com.google.gwt.dom.client.Element element)
  543. /*-{
  544. if (element.getBoundingClientRect) {
  545. var rect = element.getBoundingClientRect();
  546. return Math.ceil(rect.right - rect.left);
  547. } else {
  548. return element.offsetWidth;
  549. }
  550. }-*/;
  551. public static native int getRequiredHeight(
  552. com.google.gwt.dom.client.Element element)
  553. /*-{
  554. var height;
  555. if (element.getBoundingClientRect != null) {
  556. var rect = element.getBoundingClientRect();
  557. height = Math.ceil(rect.bottom - rect.top);
  558. } else {
  559. height = element.offsetHeight;
  560. }
  561. return height;
  562. }-*/;
  563. public static int getRequiredWidth(Widget widget) {
  564. return getRequiredWidth(widget.getElement());
  565. }
  566. public static int getRequiredHeight(Widget widget) {
  567. return getRequiredHeight(widget.getElement());
  568. }
  569. /**
  570. * Detects what is currently the overflow style attribute in given element.
  571. *
  572. * @param pe
  573. * the element to detect
  574. * @return true if auto or scroll
  575. */
  576. public static boolean mayHaveScrollBars(com.google.gwt.dom.client.Element pe) {
  577. String overflow = getComputedStyle(pe, "overflow");
  578. if (overflow != null) {
  579. if (overflow.equals("auto") || overflow.equals("scroll")) {
  580. return true;
  581. } else {
  582. return false;
  583. }
  584. } else {
  585. return false;
  586. }
  587. }
  588. /**
  589. * A simple helper method to detect "computed style" (aka style sheets +
  590. * element styles). Values returned differ a lot depending on browsers.
  591. * Always be very careful when using this.
  592. *
  593. * @param el
  594. * the element from which the style property is detected
  595. * @param p
  596. * the property to detect
  597. * @return String value of style property
  598. */
  599. private static native String getComputedStyle(
  600. com.google.gwt.dom.client.Element el, String p)
  601. /*-{
  602. try {
  603. if (el.currentStyle) {
  604. // IE
  605. return el.currentStyle[p];
  606. } else if (window.getComputedStyle) {
  607. // Sa, FF, Opera
  608. var view = el.ownerDocument.defaultView;
  609. return view.getComputedStyle(el,null).getPropertyValue(p);
  610. } else {
  611. // fall back for non IE, Sa, FF, Opera
  612. return "";
  613. }
  614. } catch (e) {
  615. return "";
  616. }
  617. }-*/;
  618. }