1 /** 2 * @fileOverview 3 * @author <a href="https://www.labkey.org">LabKey</a> (<a href="mailto:info@labkey.com">info@labkey.com</a>) 4 * @license Copyright (c) 2012-2019 LabKey Corporation 5 * <p/> 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * <p/> 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * <p/> 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 * <p/> 18 */ 19 20 /** 21 * @private 22 * @namespace LabKey ToolTip Class. 23 * Unlike Ext.ToolTip, this ToolTip class won't be dismissed when 24 * then mouse is hovering over the ToolTip's popup element. 25 */ 26 Ext.ns('LABKEY.ext'); 27 28 LABKEY.ext.PersistentToolTip = Ext.extend(Ext.ToolTip,{ 29 initComponent: function() { 30 Ext.apply(this, { 31 showDelay: 500, 32 hideDelay: 1500, 33 dismissDelay: 0, 34 autoHide: false 35 //floating:{shadow:true,shim:true,useDisplay:true,constrain:true} 36 }); 37 LABKEY.ext.PersistentToolTip.superclass.initComponent.call(this); 38 }, 39 40 afterRender: function() { 41 LABKEY.ext.PersistentToolTip.superclass.afterRender.call(this); 42 this.el.on('mouseout', this.onTargetOut, this); 43 this.el.on('mouseover', this.onElOver, this); 44 }, 45 46 checkWithin: function(e) { 47 if (this.el && e.within(this.el.dom)) { 48 return true; 49 } 50 if (this.disabled || e.within(this.target.dom, true)) { 51 return true; 52 } 53 return false; 54 }, 55 56 onElOver: function(e) { 57 if (this.checkWithin(e)) { 58 this.clearTimer('hide'); 59 } 60 }, 61 62 // same as private method of ToolTip, passing 'e' to delayShow 63 onTargetOver : function(e) 64 { 65 if (this.disabled || e.within(this.target.dom, true)) { 66 return; 67 } 68 var t = e.getTarget(this.delegate); 69 if (t) { 70 this.triggerElement = t; 71 this.clearTimer('hide'); 72 this.targetXY = e.getXY(); 73 this.delayShow(e); 74 } 75 }, 76 77 // override private method of ToolTip, passing 'e' to doShow 78 delayShow : function(e) { 79 this.showTimer = this.doShow.defer(this.showDelay, this, [e]); 80 }, 81 82 doShow: function(e) { 83 var xy = e.getXY(); 84 var within = this.target.getRegion().contains({left: xy[0], right: xy[0], top: xy[1], bottom: xy[1]}); 85 if (within) { 86 this.show(); 87 } 88 }, 89 90 onTargetOut: function(e) { 91 if (this.checkWithin(e)) { 92 this.clearTimer('hide'); 93 } 94 else if (this.hideTimer) { 95 this.hide(); 96 } 97 else { 98 this.delayHide(); 99 } 100 }, 101 102 //NOTE: Ext tooltips should support constrain: true, which would probably accomplish the same thing as ensureBoxVisible() 103 show: function () { 104 LABKEY.ext.PersistentToolTip.superclass.show.call(this); 105 if (this.el) 106 LABKEY.ext.Utils.ensureBoxVisible(this); 107 }, 108 109 // private 110 onDocMouseDown : function(e) { 111 if (this.autoHide !== true /*&& !this.closable*/ && !this.checkWithin(e)) { 112 this.disable(); 113 this.doEnable.defer(100, this); 114 } 115 } 116 117 }); 118 Ext.reg('persistenttip', LABKEY.ext.PersistentToolTip); 119 120 /** 121 * @private 122 * Adds a callout icon after the 'config.target' element. 123 * If configued to autoLoad and a 'config.tpl' template is set, 124 * the template will be used to render the contents of the tooltip. 125 * 126 * @example 127 <div id='my-div'>hello</div> 128 <script type="text/javascript> 129 var queryUrl = LABKEY.ActionURL.buildURL("query", "getQuery"); 130 var tip = new LABKEY.ext.CalloutTip({ 131 target: "my-div", 132 autoLoad: { 133 url: queryUrl, 134 params: { 135 schemaName: "flow", 136 "query.queryName": "Runs", 137 "query.FCSFileCount~neq": 0, 138 "query.columns": encodeURI("Name,Flag/Comment,FCSFileCount"), 139 apiVersion: 9.1 140 } 141 }, 142 tpl: new Ext.XTemplate( 143 '<table boder=0>', 144 '<tpl for="rows">', 145 ' <tr>', 146 ' <!-- use a subtemplate on the "Name" field to get url and value properties ... -->', 147 ' <tpl for="Name">', 148 ' <td><a href="{url}">{value}</a>', 149 ' </tpl>', 150 ' <!-- ... or use a template expression -->', 151 ' <td align="right">({[values.FCSFileCount.value]} files)', 152 ' </tr>', 153 '</tpl>', 154 '</table>') 155 }); 156 * </script> 157 */ 158 LABKEY.ext.CalloutTip = Ext.extend(LABKEY.ext.PersistentToolTip, { 159 overTargetCls : 'labkey-callout-tip-over', 160 161 initComponent: function() 162 { 163 Ext.apply(this, { 164 mouseOffset: [0, 0] 165 }); 166 167 if (!this.targetAutoEl) { 168 this.targetAutoEl = 169 '<span class="labkey-callout-tip' + (this.leftPlacement ? ' labkey-callout-tip-left' : '' ) + '">' + 170 '<img src="' + LABKEY.contextPath + '/_.gif">' + 171 '</span>'; 172 } 173 174 LABKEY.ext.CalloutTip.superclass.initComponent.call(this); 175 }, 176 177 initTarget : function (target) 178 { 179 if (this.target) 180 { 181 var origTarget = Ext.get(this.target); 182 this.target = Ext.DomHelper.append(origTarget, this.targetAutoEl, true); 183 } 184 LABKEY.ext.CalloutTip.superclass.initTarget.call(this, this.target); 185 }, 186 187 onTargetOver : function (e) 188 { 189 if (!this.disabled && this.overTargetCls) 190 { 191 this.target.addClass(this.overTargetCls); 192 } 193 LABKEY.ext.CalloutTip.superclass.onTargetOver.call(this, e); 194 }, 195 196 onTargetOut : function (e) 197 { 198 if (!this.disabled && this.overTargetCls) 199 { 200 this.target.removeClass(this.overTargetCls); 201 } 202 LABKEY.ext.CalloutTip.superclass.onTargetOut.call(this, e); 203 }, 204 205 doAutoLoad : function () 206 { 207 if (this.initialConfig.tpl || this.initialConfig.renderer) 208 { 209 var tpl = this.initialConfig.tpl; 210 var renderer = this.initialConfig.renderer; 211 var self = this; 212 var updaterRenderer = { 213 render: function (el, response, updateManager, callback) 214 { 215 var json = null; 216 if (response && response.getResponseHeader && response.getResponseHeader('Content-Type') 217 && response.getResponseHeader('Content-Type').indexOf('application/json') >= 0) 218 json = Ext.util.JSON.decode(response.responseText); 219 220 // XXX: error handling 221 222 if (json) 223 { 224 self.hide(); 225 if (tpl) 226 tpl.overwrite(el, json); 227 else if (renderer) 228 renderer(el, json); 229 self.show(); 230 } 231 } 232 }; 233 this.renderer = updaterRenderer; 234 } 235 LABKEY.ext.CalloutTip.superclass.doAutoLoad.call(this); 236 } 237 }); 238 239 240