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