From 84c12373fc411e16eaaf89ba422efd98619e33f8 Mon Sep 17 00:00:00 2001 From: Aaron Rosenzweig Date: Thu, 7 Feb 2013 19:06:55 -0500 Subject: [PATCH] New Tool Tip Widget You hover your mouse and a message appears. Like a tool tip. Signed-off-by: Aaron Rosenzweig --- .../Ajax/Ajax/Components/AjaxHoverable.api | 16 ++ .../AjaxHoverable.wo/AjaxHoverable.html | 43 ++++ .../AjaxHoverable.wo/AjaxHoverable.wod | 76 ++++++ .../AjaxHoverable.wo/AjaxHoverable.woo | 4 + .../Ajax/Sources/er/ajax/AjaxHoverable.java | 236 ++++++++++++++++++ .../Ajax/WebServerResources/ajaxHoverable.css | 56 +++++ .../Ajax/WebServerResources/ajaxHoverable.js | 104 ++++++++ 7 files changed, 535 insertions(+) create mode 100644 Frameworks/Ajax/Ajax/Components/AjaxHoverable.api create mode 100644 Frameworks/Ajax/Ajax/Components/AjaxHoverable.wo/AjaxHoverable.html create mode 100644 Frameworks/Ajax/Ajax/Components/AjaxHoverable.wo/AjaxHoverable.wod create mode 100644 Frameworks/Ajax/Ajax/Components/AjaxHoverable.wo/AjaxHoverable.woo create mode 100644 Frameworks/Ajax/Ajax/Sources/er/ajax/AjaxHoverable.java create mode 100644 Frameworks/Ajax/Ajax/WebServerResources/ajaxHoverable.css create mode 100644 Frameworks/Ajax/Ajax/WebServerResources/ajaxHoverable.js diff --git a/Frameworks/Ajax/Ajax/Components/AjaxHoverable.api b/Frameworks/Ajax/Ajax/Components/AjaxHoverable.api new file mode 100644 index 00000000000..6028e233723 --- /dev/null +++ b/Frameworks/Ajax/Ajax/Components/AjaxHoverable.api @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/Frameworks/Ajax/Ajax/Components/AjaxHoverable.wo/AjaxHoverable.html b/Frameworks/Ajax/Ajax/Components/AjaxHoverable.wo/AjaxHoverable.html new file mode 100644 index 00000000000..769ea08b7e8 --- /dev/null +++ b/Frameworks/Ajax/Ajax/Components/AjaxHoverable.wo/AjaxHoverable.html @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + diff --git a/Frameworks/Ajax/Ajax/Components/AjaxHoverable.wo/AjaxHoverable.wod b/Frameworks/Ajax/Ajax/Components/AjaxHoverable.wo/AjaxHoverable.wod new file mode 100644 index 00000000000..badf1948f3c --- /dev/null +++ b/Frameworks/Ajax/Ajax/Components/AjaxHoverable.wo/AjaxHoverable.wod @@ -0,0 +1,76 @@ +ComponentContent : WOComponentContent { + +} + +HasHoverTextMessageConditional: WOConditional { + condition = ^toolTipMessage.toString; +} + +HasImageResourceConditional: WOConditional { + condition = ^toolTipImageFilename.toString; +} + +HasNSDataConditional: WOConditional { + condition = ^toolTipImageData.toString; +} + +HoverAreaContainer: WOGenericContainer { + class = hoverAreaClasses; + elementName = "div"; + style = hoverAreaInlineStyle; +} + +HoverImgResource: WOImage { + filename = ^toolTipImageFilename; + framework = ^toolTipImageFramework; +} + +NSDataImage: WOImage { + data = ^toolTipImageData; + mimeType = ^toolTipImageMimeType; +} + +ToolTipContainer: WOGenericContainer { + id = idStr; + class = toolTipClasses; + elementName = "div"; + style = toolTipInlineStyle; +} + +ToolTipMessageText: WOString { + escapeHTML = false; + value = ^toolTipMessage; +} + +showHoverableConditional: WOConditional { + condition = showHoverable; +} + +useJavascriptHoverEffectConditional: WOConditional { + condition = useJavascriptForHoverEffect; +} + +toolTipIDString: WOString { + value = idStr; +} + +toolTipWidthString: WOString { + value = toolTipWidth; +} + +useJavascriptOffsetX: WOString { + value = useJavascriptOffsetX; +} + +useJavascriptOffsetY: WOString { + value = useJavascriptOffsetY; +} + +isAjaxRedrawConditional : WOConditional { + condition = isAjaxRequest; +} + +isNotAjaxRedrawConditional : WOConditional { + condition = isAjaxRequest; + negate = true; +} \ No newline at end of file diff --git a/Frameworks/Ajax/Ajax/Components/AjaxHoverable.wo/AjaxHoverable.woo b/Frameworks/Ajax/Ajax/Components/AjaxHoverable.wo/AjaxHoverable.woo new file mode 100644 index 00000000000..a076a051a7e --- /dev/null +++ b/Frameworks/Ajax/Ajax/Components/AjaxHoverable.wo/AjaxHoverable.woo @@ -0,0 +1,4 @@ +{ + "WebObjects Release" = "WebObjects 5.0"; + encoding = "UTF-8"; +} \ No newline at end of file diff --git a/Frameworks/Ajax/Ajax/Sources/er/ajax/AjaxHoverable.java b/Frameworks/Ajax/Ajax/Sources/er/ajax/AjaxHoverable.java new file mode 100644 index 00000000000..d5edbba6066 --- /dev/null +++ b/Frameworks/Ajax/Ajax/Sources/er/ajax/AjaxHoverable.java @@ -0,0 +1,236 @@ +package er.ajax; + +/* + * Aaron Rosenzweig Sept. 18, 2012 + */ + +import com.webobjects.appserver.WOComponent; +import com.webobjects.appserver.WOContext; +import com.webobjects.appserver.WOResponse; +import com.webobjects.foundation.NSArray; + +import er.extensions.appserver.ERXResponseRewriter; +import er.extensions.foundation.ERXStringUtilities; + +/** + * @binding hoverableWidth num of pixels for hoverable width (200 as default) (String) + * @binding additionalClassHoverArea + * @binding additionalClassToolTip + * @binding showHoverable + * @binding additionalStyleHoverArea + * @binding useJavascriptForHoverEffect + * @binding useJavascriptOffsetX num of pixels without a 'px' on the end. Just the raw number. + * @binding useJavascriptOffsetY num of pixels without a 'px' on the end. Just the raw number. + * @binding toolTipWidth + * @binding additionalStyleToolTip + * @binding toolTipHeight + * @binding toolTipAutoScroll + * @binding toolTipDropBelow + * @binding toolTipDropAbove + * @binding toolTipDropTopRight + * @binding toolTipDropTopLeft + * @binding toolTipDropBottomRight + * @binding toolTipDropBottomLeft + * @binding advancedToolTipLeft + * @binding advancedToolTipRight + * @binding advancedToolTipTop + * @binding advancedToolTipBottom + */ + +public class AjaxHoverable extends WOComponent { + protected static final String AJAX_FRAMEWORK_NAME = "Ajax"; + protected static final String LOCAL_CSS_FILE = "ajaxHoverable.css"; + protected static final String LOCAL_JS_FILE = "ajaxHoverable.js"; + + protected String _idStr = null; + + public AjaxHoverable(WOContext context) { + super(context); + } + + public boolean synchronizesVariablesWithBindings() { + return false; + } + + public static void addWebResourcesInHead(WOResponse response, WOContext context) { + AjaxUtils.addStylesheetResourceInHead(context, response, AJAX_FRAMEWORK_NAME, LOCAL_CSS_FILE); + AjaxUtils.addScriptResourceInHead(context, response, AJAX_FRAMEWORK_NAME, LOCAL_JS_FILE); + } + + public void appendToResponse(WOResponse response, WOContext context) { + addWebResourcesInHead(response, context); + super.appendToResponse(response, context); + } + + public String hoverAreaClasses() { + String classes = "erxHoverArea"; + String userDefined = (String) valueForBinding("additionalClassHoverArea"); + if (ERXStringUtilities.isNotBlank(userDefined)) { + classes += " " + userDefined; + } + return classes; + } + + public String toolTipClasses() { + String classes = "erxToolTip"; + if (useJavascriptForHoverEffect()) { + classes = "erxToolTipJS"; + } + String userDefined = (String) valueForBinding("additionalClassToolTip"); + if (ERXStringUtilities.isNotBlank(userDefined)) { + classes += " " + userDefined; + } + return classes; + } + + public boolean showHoverable() { + boolean showHoverable = true; + if (valueForBinding("showHoverable") != null) { + Object showHoverableBinding = valueForBinding("showHoverable"); + + if (showHoverableBinding instanceof Boolean) { + showHoverable = ((Boolean)showHoverableBinding).booleanValue(); + } else if (showHoverableBinding instanceof NSArray) { + NSArray tempArray = (NSArray)showHoverableBinding; + if (tempArray.count() == 0) { + showHoverable = false; + } else { + showHoverable = true; + } + } else if (showHoverableBinding instanceof Number) { + Number tempNumber = (Number)showHoverableBinding; + if (tempNumber.intValue() == 0) { + showHoverable = false; + } else { + showHoverable = true; + } + } + + } + return showHoverable; + } + + public String hoverAreaInlineStyle() { + String userDefined = (String) valueForBinding("additionalStyleHoverArea"); + + return userDefined; + } + + public boolean useJavascriptForHoverEffect() { + Boolean useJavascriptForHoverEffect = (Boolean) valueForBinding("useJavascriptForHoverEffect"); + + if (useJavascriptForHoverEffect == null) { + useJavascriptForHoverEffect = Boolean.TRUE; + } + + return useJavascriptForHoverEffect.booleanValue(); + } + + public String toolTipWidth() { + String toolTipWidth = (String) valueForBinding("toolTipWidth"); + return toolTipWidth; + } + + public Number useJavascriptOffsetX() { + Number returnVal = Integer.valueOf(0); + Number offsetValue = (Number) valueForBinding("useJavascriptOffsetX"); + if (offsetValue != null) { + returnVal = offsetValue; + } + + return returnVal; + } + + public Number useJavascriptOffsetY() { + Number returnVal = Integer.valueOf(0); + Number offsetValue = (Number) valueForBinding("useJavascriptOffsetY"); + if (offsetValue != null) { + returnVal = offsetValue; + } + + return returnVal; + } + + public String toolTipInlineStyle() { + String userDefined = (String) valueForBinding("additionalStyleToolTip"); + String inlineStyle = "width: " + toolTipWidth() + "; "; + + String toolTipHeight = (String) valueForBinding("toolTipHeight"); + if (ERXStringUtilities.isNotBlank(toolTipHeight)) { + inlineStyle += "height: " + toolTipHeight + "; "; + } + + boolean toolTipAutoScroll = valueForBinding("toolTipAutoScroll") != null && ((Boolean) valueForBinding("toolTipAutoScroll")).booleanValue(); + if (toolTipAutoScroll) { + inlineStyle += "overflow: auto; "; + } + + /* + * The following bindings are only "guesses". They make assumptions on how big the hoverArea container is. + * If they do not work correctly, you'll have to forget about them and use the advanced options. + */ + boolean toolTipDropBelow = valueForBinding("toolTipDropBelow") != null && ((Boolean) valueForBinding("toolTipDropBelow")).booleanValue(); + boolean toolTipDropAbove = valueForBinding("toolTipDropAbove") != null && ((Boolean) valueForBinding("toolTipDropAbove")).booleanValue(); + boolean toolTipDropTopRight = valueForBinding("toolTipDropTopRight") != null && ((Boolean) valueForBinding("toolTipDropTopRight")).booleanValue(); + boolean toolTipDropTopLeft = valueForBinding("toolTipDropTopLeft") != null && ((Boolean) valueForBinding("toolTipDropTopLeft")).booleanValue(); + boolean toolTipDropBottomRight = valueForBinding("toolTipDropBottomRight") != null && ((Boolean) valueForBinding("toolTipDropBottomRight")).booleanValue(); + boolean toolTipDropBottomLeft = valueForBinding("toolTipDropBottomLeft") != null && ((Boolean) valueForBinding("toolTipDropBottomLeft")).booleanValue(); + + /* + * You should only use these bindings if the "drop" bindings don't suit your needs. These set the top/bot/left/right properties + * of the toolTip div that shows on hover. The "drop" bindings make guesses for these values on your behalf. + */ + String advancedToolTipLeft = (String) valueForBinding("advancedToolTipLeft"); + String advancedToolTipRight = (String) valueForBinding("advancedToolTipRight"); + String advancedToolTipTop = (String) valueForBinding("advancedToolTipTop"); + String advancedToolTipBottom = (String) valueForBinding("advancedToolTipBottom"); + + if (ERXStringUtilities.isBlank(advancedToolTipLeft) && ERXStringUtilities.isBlank(advancedToolTipRight) && + ERXStringUtilities.isBlank(advancedToolTipTop) && ERXStringUtilities.isBlank(advancedToolTipBottom)) { + if (toolTipDropAbove) { + inlineStyle += "bottom: 1.5em; "; + } else if (toolTipDropTopRight) { + inlineStyle += "bottom: 1.5em; left: 30px; "; + } else if (toolTipDropTopLeft) { + inlineStyle += "bottom: 1.5em; right: 30px; "; + } else if (toolTipDropBottomRight) { + inlineStyle += "left: 30px; "; + } else if (toolTipDropBottomLeft) { + inlineStyle += "right: 30px; "; + } + } else { + if (ERXStringUtilities.isNotBlank(advancedToolTipLeft)) { + inlineStyle += "left: " + advancedToolTipLeft + "; "; + } + + if (ERXStringUtilities.isNotBlank(advancedToolTipRight)) { + inlineStyle += "right: " + advancedToolTipRight + "; "; + } + + if (ERXStringUtilities.isNotBlank(advancedToolTipTop)) { + inlineStyle += "top: " + advancedToolTipTop + "; "; + } + + if (ERXStringUtilities.isNotBlank(advancedToolTipBottom)) { + inlineStyle += "bottom: " + advancedToolTipBottom + "; "; + } + } + + if (userDefined == null) { + userDefined = ""; + } + return inlineStyle + userDefined; + } + + public String idStr() { + if (ERXStringUtilities.isBlank(_idStr) && useJavascriptForHoverEffect()) { + _idStr = "hoverable_" + ERXStringUtilities.safeIdentifierName(context().elementID()); + } + return _idStr; + } + + public boolean isAjaxRequest() { + return AjaxUtils.isAjaxRequest(context().request()); + } + +} \ No newline at end of file diff --git a/Frameworks/Ajax/Ajax/WebServerResources/ajaxHoverable.css b/Frameworks/Ajax/Ajax/WebServerResources/ajaxHoverable.css new file mode 100644 index 00000000000..e2cc28ea7d8 --- /dev/null +++ b/Frameworks/Ajax/Ajax/WebServerResources/ajaxHoverable.css @@ -0,0 +1,56 @@ +.erxHoverArea { + /*padding:3px 0;*/ + /* border: solid black thin; */ + /*text-align:center;*/ + /*font-size: 70%;*/ + position: relative; + display: -moz-inline-box; + display: inline-block; +} + +/* target IE7 and below. These two definitions must be done seperately. Together they create a true inline block for IE. */ +*:first-child+html .erxHoverArea, +* html .erxHoverArea { + /*IE hack, http://www.brunildo.org/test/InlineBlockLayout.html*/ + display: inline-block; +} + +*:first-child+html .erxHoverArea, +* html .erxHoverArea { + /*IE hack, http://www.brunildo.org/test/InlineBlockLayout.html*/ + display: inline; +} + +.erxHoverArea div.erxToolTip, +.erxHoverArea div.erxToolTipJS, +#erxToolTipContents { + padding: .8em; + /*margin-top: 3px;*/ + border: 1px solid #000; + background-color: #EEE; + color: #000; + /*font-size: 50%;*/ + font-weight: normal; + position: absolute; + text-align: left; + z-index: 2000000; + white-space: normal; +} + +.erxHoverArea div.erxToolTip, +.erxHoverArea div.erxToolTipJS { + display: none; +} + +/* target IE7 and below. This fixes the z index stack order bug*/ +*:first-child+html div.erxHoverArea:hover, +* html div.erxHoverArea:hover { + border: 0px solid white; + /* width: 99%; */ + z-index: 1; +} + +div.erxHoverArea:hover div.erxToolTip { + display: block; +} + diff --git a/Frameworks/Ajax/Ajax/WebServerResources/ajaxHoverable.js b/Frameworks/Ajax/Ajax/WebServerResources/ajaxHoverable.js new file mode 100644 index 00000000000..3ed21629e52 --- /dev/null +++ b/Frameworks/Ajax/Ajax/WebServerResources/ajaxHoverable.js @@ -0,0 +1,104 @@ +// Inspired by Michael Leigeber in a tutorial post here: +// http://sixrevisions.com/tutorials/javascript_tutorial/create_lightweight_javascript_tooltip/ +var erxToolTip=function(){ + var id = 'erxToolTip'; + var top = 3; + var left = 3; + var maxw = 300; + var speed = 10; + var timer = 20; + var endalpha = 95; + var alpha = 0; + var tt,t,c,b,h; + var ie = document.all ? true : false; + var hoverActionElem = null; + var offsetX = 0; + var offsetY = 0; + return{ + show:function(hoverActionElement, v, w, offsetXValue, offsetYValue){ + if (offsetXValue) { + offsetX = offsetXValue; + } + + if (offsetYValue) { + offsetY = offsetYValue; + } + + if(tt == null){ + tt = document.createElement('div'); + tt.setAttribute('id',id); + t = document.createElement('div'); + t.setAttribute('id',id + 'Top'); + c = document.createElement('div'); + c.setAttribute('id',id + 'Contents'); + c.setAttribute('class', 'erxDefaultFont'); + b = document.createElement('div'); + b.setAttribute('id',id + 'Bottom'); + tt.appendChild(t); + tt.appendChild(c); + tt.appendChild(b); + document.body.appendChild(tt); + tt.style.opacity = 0; + tt.style.filter = 'alpha(opacity=0)'; + tt.style.position = 'absolute'; + tt.style.zIndex = 2000000; + } + tt.style.display = 'block'; + c.innerHTML = v; + tt.style.width = w ? w : 'auto'; + if(!w && ie){ + t.style.display = 'none'; + b.style.display = 'none'; + tt.style.width = tt.offsetWidth; + t.style.display = 'block'; + b.style.display = 'block'; + } + if(tt.offsetWidth > maxw){tt.style.width = maxw + 'px'} + + // (Aaron) Use Prototype and the erxHoverArea action element + hoverActionElem = hoverActionElement; + if ( ! hoverActionElem.erxHoverAreaMouseMoveRegistered) { + hoverActionElem.erxHoverAreaMouseMoveRegistered = true; + Element.observe(hoverActionElem, 'mousemove', erxToolTip.pos); + } + + h = parseInt(tt.offsetHeight) + top; + clearInterval(tt.timer); + tt.timer = setInterval(function(){erxToolTip.fade(1)},timer); + }, + pos:function(e){ + var u = ie ? event.clientY + document.documentElement.scrollTop : e.pageY; + var l = ie ? event.clientX + document.documentElement.scrollLeft : e.pageX; + + // this raises the hoverable to the top + //tt.style.top = (u - h) + 'px'; + + // this lowers the hoverable to the bottom + tt.style.top = (u + offsetY) + 'px'; + + // this places the hoverable on the right + tt.style.left = (l + left + offsetX) + 'px'; + }, + fade:function(d){ + var a = alpha; + if((a != endalpha && d == 1) || (a != 0 && d == -1)){ + var i = speed; + if(endalpha - a < speed && d == 1){ + i = endalpha - a; + }else if(alpha < speed && d == -1){ + i = a; + } + alpha = a + (i * d); + tt.style.opacity = alpha * .01; + tt.style.filter = 'alpha(opacity=' + alpha + ')'; + }else{ + clearInterval(tt.timer); + if(d == -1){tt.style.display = 'none'} + } + }, + hide:function(){ + clearInterval(tt.timer); + tt.timer = setInterval(function(){erxToolTip.fade(-1)},timer); + } + }; +}(); \ No newline at end of file