001package com.hfg.javascript;
002
003import com.hfg.html.HTMLTag;
004import com.hfg.html.HTML;
005import com.hfg.graphics.ColorUtil;
006
007import java.awt.*;
008
009//------------------------------------------------------------------------------
010/**
011  Generic hoverboard generation javascript.
012  You will also need to add generateJS() to the javascript in the page's <head> tag.
013  <div>
014  See <a href='doc-files/tooltipTest.html' target='_parent'>this page</a> for tooltip examples.
015  </div>
016
017  <div>Simple code example:
018  <pre>
019       HoverboardMaker hoverboardMaker = new HoverboardMaker();
020
021       HTMLDoc htmlDoc = new HTMLDoc();
022
023       // Keep IE from going into quirks mode.
024       htmlDoc.setDoctype(Doctype.HTML_4_01_TRANSITIONAL_NO_URL);
025
026       HTML html = new HTML();
027       htmlDoc.setRootTag(html);
028       html.getHead().addSubtag(Meta.CONTENT_TYPE_ISO_8859_1);
029
030       // Add the necessary javascript to the page.
031       html.getHead().addJavascript(hoverboardMaker.generateJS());
032       html.getHead().addJavascript(generateJS());
033
034       // Add CSS
035       html.getHead().addStyle(hoverboardMaker.generateCSS());
036
037       Body body = html.getBody();
038       body.br(2);
039
040       // Create the target tag (in this case a span in a span) that the hoverboard will function on.
041       Span span = body.addSpan("The quick ");
042       hoverboardMaker.addHoverboard(span, "generateHoverboardContent");
043       Span targetSpan = span.addSpan("brown").setId("hoverTarget");
044
045       span.addContent(" fox jumped over the lazy dog");
046
047       ...
048
049       private String generateJS()
050       {
051          Pre content = new Pre();
052          content.addSpan("Choose a door to open:\n").setStyle(CSS.BOLD + CSS.fontSize(24));
053          content.addSpan("1. ").addLink("javascript:void(0);", "One");
054          content.addSpan("\n2. ").addLink("javascript:void(0);", "Two");
055          content.addSpan("\n3. ").addLink("javascript:void(0);", "Three");
056
057          String contentString = content.toHTML();
058
059          String safeString = StringUtil.replaceAll(contentString, "'", "\\'");
060          safeString = StringUtil.replaceAll(safeString, "\n", "\\n");
061          safeString = StringUtil.replaceAll(safeString, "\r", "\\r");
062
063          StringBuffer js = new StringBuffer();
064
065          js.append("//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n"
066                    + "function generateHoverboardContent(inTargetElement)\n"
067                    + "{\n"
068                    + "  if (inTargetElement.id &amp;&amp; inTargetElement.id == 'hoverTarget') {"
069                    + "    return '" + safeString + "';\n"
070                    + "  }\n"
071                    + "}\n");
072
073          return js.toString();
074       }
075
076  </pre>
077  </div>
078
079  @author J. Alex Taylor, hairyfatguy.com
080 */
081//------------------------------------------------------------------------------
082// com.hfg XML/HTML Coding Library
083//
084// This library is free software; you can redistribute it and/or
085// modify it under the terms of the GNU Lesser General Public
086// License as published by the Free Software Foundation; either
087// version 2.1 of the License, or (at your option) any later version.
088//
089// This library is distributed in the hope that it will be useful,
090// but WITHOUT ANY WARRANTY; without even the implied warranty of
091// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
092// Lesser General Public License for more details.
093//
094// You should have received a copy of the GNU Lesser General Public
095// License along with this library; if not, write to the Free Software
096// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
097//
098// J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com
099// jataylor@hairyfatguy.com
100//------------------------------------------------------------------------------
101
102public class HoverboardMaker
103{
104   //##########################################################################
105   // PRIVATE FIELDS
106   //##########################################################################
107
108   private int     mDelay          = 500;
109   private int     mXOffset        = 10;
110   private int     mYOffset        = 20;
111   private int     mWidth          = 300;
112   private String  mBorder         = "1px ridge #cccccc";
113   private String  mFont           = "arial,sans-serif";
114   private String  mFontSize       = "11px";
115   private String  mFontColor      = "#006600";
116   private Color   mBgColor        = Color.decode("#cccccc");
117   private int     mPadding        = 3;
118   private String  mShadowColor    = "#000000";
119   private int     mShadowWidth    = 0;//2;
120   private boolean mViaOnClick     = true;
121
122   //##########################################################################
123   // CONSTRUCTORS
124   //##########################################################################
125
126   //--------------------------------------------------------------------------
127   public HoverboardMaker()
128   {
129   }
130
131   //##########################################################################
132   // PUBLIC METHODS
133   //##########################################################################
134
135
136   //--------------------------------------------------------------------------
137   /**
138    Sets whether to trigger the hoverboard via an onclick() or an onmouseover().
139    */
140   public void activateViaOnClick(boolean inValue)
141   {
142      mViaOnClick = inValue;
143   }
144
145   //--------------------------------------------------------------------------
146   public void setDelay(int inValue)
147   {
148      mDelay = inValue;
149   }
150
151   //--------------------------------------------------------------------------
152   public void setXOffset(int inValue)
153   {
154      mXOffset = inValue;
155   }
156
157   //--------------------------------------------------------------------------
158   public void setYOffset(int inValue)
159   {
160      mYOffset = inValue;
161   }
162
163   //--------------------------------------------------------------------------
164   public void setWidth(int inValue)
165   {
166      mWidth = inValue;
167   }
168
169   //--------------------------------------------------------------------------
170   public void setBorder(String inValue)
171   {
172      mBorder = inValue;
173   }
174
175   //--------------------------------------------------------------------------
176   public void setFont(String inValue)
177   {
178      mFont = inValue;
179   }
180
181   //--------------------------------------------------------------------------
182   public void setFontSize(String inValue)
183   {
184      mFontSize = inValue;
185   }
186
187   //--------------------------------------------------------------------------
188   public void setFontColor(String inValue)
189   {
190      mFontColor = inValue;
191   }
192
193   //--------------------------------------------------------------------------
194   public void setBackgroundColor(Color inValue)
195   {
196      mBgColor = inValue;
197   }
198
199   //--------------------------------------------------------------------------
200   public void setPadding(int inValue)
201   {
202      mPadding = inValue;
203   }
204
205   //--------------------------------------------------------------------------
206   public void setShadowColor(String inValue)
207   {
208      mShadowColor = inValue;
209   }
210
211   //--------------------------------------------------------------------------
212   public void setShadowWidth(int inValue)
213   {
214      mShadowWidth = inValue;
215   }
216
217   //--------------------------------------------------------------------------
218   /**
219    * Hoverboards can be added to any tag which supports onmouseover and onmouseout.
220    * You will also need to add generateJS() to the javascript in the page's &lt;head&gt; tag.
221    * @param inTag the tag to which the onclick() or onmouseover() attribute will
222    *              be set to display a hoverboard.
223    * @param inJSMethodForContent the javascript method to call for content generation.
224    */
225   public void addHoverboard(HTMLTag inTag, String inJSMethodForContent)
226   {
227      String currentValue = inTag.getAttributeValue(mViaOnClick ? HTML.ONCLICK : HTML.ONMOUSEOVER);
228      inTag.setAttribute(mViaOnClick ? HTML.ONCLICK : HTML.ONMOUSEOVER,
229                         (currentValue != null ? currentValue + ";" : "") + "showHoverboard(event, " + inJSMethodForContent + ")");
230
231      currentValue = inTag.getAttributeValue(HTML.CLASS);
232      inTag.setAttribute(HTML.CLASS, (currentValue != null ? currentValue + " " : "") + "hoverboard");
233   }
234
235
236   //--------------------------------------------------------------------------
237   public String generateCSS()
238   {
239      StringBuffer css = new StringBuffer();
240
241
242      css.append(".hoverboard:hover {\n"
243                 + "   background-color: #" + ColorUtil.colorToHex(mBgColor) + ";\n"
244                 + "   }\n\n");
245
246
247      return css.toString();
248   }
249
250   //--------------------------------------------------------------------------
251   public String generateJS()
252   {
253      StringBuffer js = new StringBuffer();
254
255      line(js, "var hoverboard_delay       = " + (mViaOnClick ? 0 : mDelay) + ";");
256      line(js, "var hoverboard_border      = '" + mBorder + "';");
257      if (mShadowWidth > 0)
258      {
259         line(js, "var hoverboard_shadowColor = '" + mShadowColor + "';");
260         line(js, "var hoverboard_shadowWidth = " + mShadowWidth + ";");
261      }
262      line(js, "");
263      line(js, "");
264      line(js, "var _hoverboard;");
265      line(js, "var _hoverboard_timeout;");
266      line(js, "var _hoverboard_text;");
267      line(js, "var _hoverboard_eventX;");
268      line(js, "var _hoverboard_eventY;");
269      line(js, "var _hoverboard_width;");
270      line(js, "var _hoverboard_height;");
271      line(js, "var _hoverboard_parent;");
272      line(js, "var _hoverboard_content;");
273      line(js, "var _hoverboard_content_method;");
274      line(js, "");
275      line(js, "//----------------------------------");
276      line(js, "function isSupported() {");
277      line(js, "  return (Boolean(document.getElementsByTagName)");
278      line(js, "          && Boolean(document.getElementById));");
279      line(js, "}");
280      line(js, "//----------------------------------");
281      line(js, "function showHoverboard(event, content_method) {");
282      line(js, "  if (!isSupported() || _hoverboard_parent) return;");
283      line(js, "");
284      line(js, "  event = event || window.event || window.Event;");
285      line(js, "  _hoverboard_parent = event.originalTarget || event.srcElement;");
286      line(js, "  _hoverboard_parent._bgColor_cache = _hoverboard_parent.style.backgroundColor;");
287      line(js, "  _hoverboard_content_method = content_method;");
288      line(js, "  _hoverboard_timeout = setTimeout('initHoverboard()', hoverboard_delay);");
289      line(js, "}");
290      line(js, "//----------------------------------");
291      line(js, "function initHoverboard() {");
292      line(js, "  // Check to see if there is content for this srcElement");
293      line(js, "  _hoverboard_content = _hoverboard_content_method(_hoverboard_parent)");
294      line(js, "  if (!_hoverboard_content) {");
295      line(js, "    destroyHoverboard();");
296      line(js, "    return;");
297      line(js, "  }");
298      line(js, "");
299      line(js, "  // Hoverboard");
300      line(js, "  _hoverboard = document.createElement('div');");
301      line(js, "  _hoverboard.style.visibility='hidden';");
302      line(js, "  _hoverboard.style.position='absolute';");
303      line(js, "");
304      line(js, "  _hoverboard_parent.appendChild(_hoverboard);");
305      line(js, "");
306      if (mShadowWidth > 0)
307      {
308         line(js, "  // Shadow Div");
309         line(js, "  var shadowDiv = document.createElement('div');");
310         line(js, "  shadowDiv.style.position = 'absolute';");
311         line(js, "  shadowDiv.style.display  = 'block';");
312         line(js, "  shadowDiv.style.backgroundColor = hoverboard_shadowColor;");
313         line(js, "  shadowDiv.style.width  = '100%';");
314         line(js, "  shadowDiv.style.height = '100%';");
315         line(js, "  shadowDiv.style.left   = hoverboard_shadowWidth + 'px';");
316         line(js, "  shadowDiv.style.top    = hoverboard_shadowWidth + 'px';");
317         line(js, "");
318         line(js, "  // Opacity");
319         line(js, "  if (shadowDiv.style.MozOpacity) shadowDiv.style.MozOpacity=0.75;");
320         line(js, "  else if (shadowDiv.style.filter) shadowDiv.style.filter='alpha(opacity=85)';");
321         line(js, "");
322         line(js, "  _hoverboard.appendChild(shadowDiv);");
323         line(js, "");
324      }
325      line(js, "  // Content Div");
326      line(js, "  var contentDiv = document.createElement('div');");
327      line(js, "  contentDiv.style.position        = 'relative';");
328      line(js, "  contentDiv.style.display         = 'block';");
329      line(js, "  contentDiv.style.border          = hoverboard_border;");
330      line(js, "  _hoverboard.appendChild(contentDiv);");
331      line(js, "");
332      line(js, "  if (document.createRange) {");
333      line(js, "    // Gecko & KHTML flavors should be able to handle this.");
334      line(js, "    var range = document.createRange();");
335      line(js, "    range.setStartBefore(contentDiv);");
336      line(js, "    var domfrag = range.createContextualFragment(_hoverboard_content);");
337      line(js, "    contentDiv.appendChild(domfrag);");
338      line(js, "  }");
339      line(js, "  else {");
340      line(js, "    contentDiv.innerHTML = '<table><tr><td>' ");
341      line(js, "                           + _hoverboard_content");
342      line(js, "                           + '<\\/td><\\/tr><\\/table>';");
343      line(js, "  }");
344      line(js, "");
345      line(js, "  _hoverboard_height = (_hoverboard.pixelHeight ? _hoverboard.pixelHeight :");
346      line(js, "                                            _hoverboard.offsetHeight ? _hoverboard.offsetHeight : 0);");
347      line(js, "  _hoverboard_width  = (_hoverboard.pixelWidth ? _hoverboard.pixelWidth :");
348      line(js, "                                           _hoverboard.offsetWidth ? _hoverboard.offsetWidth : 0);");
349      line(js, "");
350      line(js, "  if (_hoverboard.style.width) {");
351      line(js, "     _hoverboard.style.width = _hoverboard_width + 'px';");
352      line(js, "  }");
353      line(js, "");
354      line(js, "  positionHoverboard();");
355      line(js, "  _hoverboard_parent.style.backgroundColor = '#" + ColorUtil.colorToHex(mBgColor) + "';");
356      line(js, "  _hoverboard.style.visibility='visible';");
357      line(js, "");
358      line(js, "  _hoverboard_parent._onmouseout_cache = _hoverboard_parent.onmouseout || function() {};");
359      line(js, "  _hoverboard_parent.onmouseout = function(event) {");
360      line(js, "    if (_hoverboard) {");
361      line(js, "      event = event || window.event || window.Event;");
362      line(js, "      var x = event.pageX || event.clientX || 0;");
363      line(js, "      var y = event.pageY || event.clientY || 0;");
364
365      line(js, "      // Safari has a problem with onmouseout() being called while still within the parent's rectangle.");
366      line(js, "      var offsetTrail = _hoverboard_parent;");
367      line(js, "      var offsetLeft = 0;");
368      line(js, "      var offsetTop = 0;");
369      line(js, "      while (offsetTrail) {");
370      line(js, "        offsetLeft += offsetTrail.offsetLeft;");
371      line(js, "        offsetTop += offsetTrail.offsetTop;");
372      line(js, "        offsetTrail = offsetTrail.offsetParent;");
373      line(js, "      }");
374      line(js, "      if (x >= offsetLeft && x <= offsetLeft + _hoverboard_parent.offsetWidth");
375      line(js, "          && y >= offsetTop && y <= offsetTop + _hoverboard_parent.offsetHeight)");
376      line(js, "      {");
377      line(js, "        // Still inside the parent.");
378      line(js, "        return true;");
379      line(js, "      }");
380
381      line(js, "      offsetTrail = _hoverboard;");
382      line(js, "      offsetLeft = 0;");
383      line(js, "      offsetTop = 0;");
384      line(js, "      while (offsetTrail) {");
385      line(js, "        offsetLeft += offsetTrail.offsetLeft;");
386      line(js, "        offsetTop += offsetTrail.offsetTop;");
387      line(js, "        offsetTrail = offsetTrail.offsetParent;");
388      line(js, "      }");
389//      line(js, "  alert('y:' + y + ' ' + offsetTop + ' ' + _hoverboard_height);");////////////
390      line(js, "      if (x <= offsetLeft || x >= offsetLeft + _hoverboard_width");
391      line(js, "          || y <= offsetTop || y >= offsetTop + _hoverboard_height)");
392
393//      line(js, "      if (x < parseInt(_hoverboard.style.left) || x > parseInt(_hoverboard.style.left) + _hoverboard_width");
394//      line(js, "          || y < parseInt(_hoverboard.style.top) || y > parseInt(_hoverboard.style.top) + _hoverboard_height)");
395      line(js, "      {");
396      line(js, "         destroyHoverboard(event);");
397      line(js, "      }");
398
399
400      line(js, "    }");
401      line(js, "    else {");
402      line(js, "      destroyHoverboard(event);");
403      line(js, "    }");
404      line(js, "    return true;");
405      line(js, "  }");
406
407
408      line(js, "}");
409      line(js, "//----------------------------------");
410      line(js, "function destroyHoverboard(event) {");
411      line(js, "  if (!isSupported()) return;");
412      line(js, "");
413      line(js, "  if (_hoverboard_timeout) {");
414      line(js, "    clearTimeout(_hoverboard_timeout);");
415      line(js, "  }");
416      line(js, "");
417      line(js, "  if (_hoverboard) {");
418      line(js, "    _hoverboard_parent.removeChild(_hoverboard);");
419      line(js, "    _hoverboard = null;");
420      line(js, "  }");
421      line(js, "");
422      line(js, "  if (_hoverboard_parent) {");
423      line(js, "    _hoverboard_parent.style.backgroundColor = _hoverboard_parent._bgColor_cache;");
424      line(js, "    if (_hoverboard_parent._onmouseout_cache) {");
425      line(js, "       _hoverboard_parent.onmouseout = _hoverboard_parent._onmouseout_cache;");
426      line(js, "       _hoverboard_parent.onmouseout(event);");
427      line(js, "     }");
428      line(js, "    _hoverboard_parent = null;");
429      line(js, "  }");
430      line(js, "");
431      line(js, "}");
432      line(js, "//----------------------------------");
433      line(js, "function positionHoverboard() {");
434      line(js, "  var offsetTrail = _hoverboard_parent;");
435      line(js, "  var offsetLeft = 0;");
436      line(js, "  var offsetTop = 0;");
437      line(js, "  while (offsetTrail) {");
438      line(js, "    offsetLeft += offsetTrail.offsetLeft;");
439      line(js, "    offsetTop += offsetTrail.offsetTop;");
440      line(js, "    offsetTrail = offsetTrail.offsetParent;");
441      line(js, "  }");
442      line(js, "  var x = offsetLeft;");
443      line(js, "  var y = offsetTop + _hoverboard_parent.offsetHeight;");
444      line(js, "  var body = document.compatMode && document.compatMode != 'BackCompat' ? document.documentElement : document.body? document.body : null;");
445      line(js, "");
446      line(js, "  if (_hoverboard) {");
447      line(js, "    var windowWidth = 0;");
448      line(js, "    if (body.clientWidth) {");
449      line(js, "      if (window.innerWidth) {");
450      line(js, "        windowWidth = Math.max(body.clientWidth, window.innerWidth);");
451      line(js, "      }");
452      line(js, "      else {");
453      line(js, "        windowWidth = body.clientWidth;");
454      line(js, "      }");
455      line(js, "    }");
456      line(js, "    else {");
457      line(js, "      windowWidth = window.innerWidth;");
458      line(js, "    }");
459      line(js, "");
460      line(js, "    var windowHeight = 0;");
461      line(js, "    if (body.clientHeight) {");
462      line(js, "      if (window.innerHeight) {");
463      line(js, "        windowHeight = Math.max(body.clientHeight, window.innerHeight);");
464      line(js, "      }");
465      line(js, "      else {");
466      line(js, "        windowHeight = body.clientHeight;");
467      line(js, "      }");
468      line(js, "    }");
469      line(js, "    else {");
470      line(js, "      windowHeight = window.innerHeight;");
471      line(js, "    }");
472      line(js, "");
473      line(js, "    var xLimit = windowWidth");
474      line(js, "                 + (window.pageXOffset ? window.pageXOffset : body.scrollLeft ? body.scrollLeft : 0)");
475      line(js, "                 - _hoverboard_width - 10;");
476      line(js, "");
477      line(js, "    var yLimit = windowHeight");
478      line(js, "                 + (window.pageYOffset ? window.pageYOffset : body.scrollTop ? body.scrollTop : 0)");
479      line(js, "                 - _hoverboard_height;");
480      line(js, "");
481      line(js, "    if (x > xLimit) x = xLimit;");
482      line(js, "");
483      line(js, "    if (y > yLimit) {");
484      line(js, "      if (hoverboard_yOffset > 0) {");
485      line(js, "        // Flip display to above the cursor");
486      line(js, "        y = y - hoverboard_yOffset - hoverboard_yOffset - _hoverboard_height;");
487      line(js, "      }");
488      line(js, "      if (y > yLimit) y = yLimit;");
489      line(js, "    }");
490      line(js, "");
491      line(js, "    _hoverboard.style.left = x + 'px';");
492      line(js, "    _hoverboard.style.top  = y + 'px';");
493      line(js, "  }");
494      line(js, "}");
495
496      return js.toString();
497   }
498
499   //--------------------------------------------------------------------------
500   private static void line(StringBuffer inBuffer, String inLine)
501   {
502      inBuffer.append(inLine + "\n");
503   }
504
505}