[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 /** 2 * Provides the base tooltip class. 3 * 4 * @module moodle-core-tooltip 5 */ 6 7 /** 8 * A base class for a tooltip. 9 * 10 * @param {Object} config Object literal specifying tooltip configuration properties. 11 * @class M.core.tooltip 12 * @constructor 13 * @extends M.core.dialogue 14 */ 15 function TOOLTIP(config) { 16 if (!config) { 17 config = {}; 18 } 19 20 // Override the default options provided by the parent class. 21 if (typeof config.draggable === 'undefined') { 22 config.draggable = true; 23 } 24 25 if (typeof config.constrain === 'undefined') { 26 config.constrain = true; 27 } 28 29 TOOLTIP.superclass.constructor.apply(this, [config]); 30 } 31 32 var SELECTORS = { 33 CLOSEBUTTON: '.closebutton' 34 }, 35 36 CSS = { 37 PANELTEXT: 'tooltiptext' 38 }, 39 RESOURCES = { 40 WAITICON: { 41 pix: 'i/loading_small', 42 component: 'moodle' 43 } 44 }, 45 ATTRS = {}; 46 47 /** 48 * Static property provides a string to identify the JavaScript class. 49 * 50 * @property NAME 51 * @type String 52 * @static 53 */ 54 TOOLTIP.NAME = 'moodle-core-tooltip'; 55 56 /** 57 * Static property used to define the CSS prefix applied to tooltip dialogues. 58 * 59 * @property CSS_PREFIX 60 * @type String 61 * @static 62 */ 63 TOOLTIP.CSS_PREFIX = 'moodle-dialogue'; 64 65 /** 66 * Static property used to define the default attribute configuration for the Tooltip. 67 * 68 * @property ATTRS 69 * @type String 70 * @static 71 */ 72 TOOLTIP.ATTRS = ATTRS; 73 74 /** 75 * The initial value of the header region before the content finishes loading. 76 * 77 * @attribute initialheadertext 78 * @type String 79 * @default '' 80 * @writeOnce 81 */ 82 ATTRS.initialheadertext = { 83 value: '' 84 }; 85 86 /** 87 * The initial value of the body region before the content finishes loading. 88 * 89 * The supplid string will be wrapped in a div with the CSS.PANELTEXT class and a standard Moodle spinner 90 * appended. 91 * 92 * @attribute initialbodytext 93 * @type String 94 * @default '' 95 * @writeOnce 96 */ 97 ATTRS.initialbodytext = { 98 value: '', 99 setter: function(content) { 100 var parentnode, 101 spinner; 102 parentnode = Y.Node.create('<div />') 103 .addClass(CSS.PANELTEXT); 104 105 spinner = Y.Node.create('<img />') 106 .setAttribute('src', M.util.image_url(RESOURCES.WAITICON.pix, RESOURCES.WAITICON.component)) 107 .addClass('spinner'); 108 109 if (content) { 110 // If we have been provided with content, add it to the parent and make 111 // the spinner appear correctly inline 112 parentnode.set('text', content); 113 spinner.addClass('iconsmall'); 114 } else { 115 // If there is no loading message, just make the parent node a lightbox 116 parentnode.addClass('content-lightbox'); 117 } 118 119 parentnode.append(spinner); 120 return parentnode; 121 } 122 }; 123 124 /** 125 * The initial value of the footer region before the content finishes loading. 126 * 127 * If a value is supplied, it will be wrapped in a <div> first. 128 * 129 * @attribute initialfootertext 130 * @type String 131 * @default '' 132 * @writeOnce 133 */ 134 ATTRS.initialfootertext = { 135 value: null, 136 setter: function(content) { 137 if (content) { 138 return Y.Node.create('<div />') 139 .set('text', content); 140 } 141 } 142 }; 143 144 /** 145 * The function which handles setting the content of the title region. 146 * The specified function will be called with a context of the tooltip instance. 147 * 148 * The default function will simply set the value of the title to object.heading as returned by the AJAX call. 149 * 150 * @attribute headerhandler 151 * @type Function|String|null 152 * @default set_header_content 153 */ 154 ATTRS.headerhandler = { 155 value: 'set_header_content' 156 }; 157 158 /** 159 * The function which handles setting the content of the body region. 160 * The specified function will be called with a context of the tooltip instance. 161 * 162 * The default function will simply set the value of the body area to a div containing object.text as returned 163 * by the AJAX call. 164 * 165 * @attribute bodyhandler 166 * @type Function|String|null 167 * @default set_body_content 168 */ 169 ATTRS.bodyhandler = { 170 value: 'set_body_content' 171 }; 172 173 /** 174 * The function which handles setting the content of the footer region. 175 * The specified function will be called with a context of the tooltip instance. 176 * 177 * By default, the footer is not set. 178 * 179 * @attribute footerhandler 180 * @type Function|String|null 181 * @default null 182 */ 183 ATTRS.footerhandler = { 184 value: null 185 }; 186 187 /** 188 * The function which handles modifying the URL that was clicked on. 189 * 190 * The default function rewrites '.php' to '_ajax.php'. 191 * 192 * @attribute urlmodifier 193 * @type Function|String|null 194 * @default null 195 */ 196 ATTRS.urlmodifier = { 197 value: null 198 }; 199 200 /** 201 * Set the Y.Cache object to use. 202 * 203 * By default a new Y.Cache object will be created for each instance of the tooltip. 204 * 205 * In certain situations, where multiple tooltips may share the same cache, it may be preferable to 206 * seed this cache from the calling method. 207 * 208 * @attribute textcache 209 * @type Y.Cache|null 210 * @default null 211 */ 212 ATTRS.textcache = { 213 value: null 214 }; 215 216 /** 217 * Set the default size of the Y.Cache object. 218 * 219 * This is only used if no textcache is specified. 220 * 221 * @attribute textcachesize 222 * @type Number 223 * @default 10 224 */ 225 ATTRS.textcachesize = { 226 value: 10 227 }; 228 229 Y.extend(TOOLTIP, M.core.dialogue, { 230 // The bounding box. 231 bb: null, 232 233 // Any event listeners we may need to cancel later. 234 listenevents: [], 235 236 // Cache of objects we've already retrieved. 237 textcache: null, 238 239 // The align position. This differs for RTL languages so we calculate once and store. 240 alignpoints: [ 241 Y.WidgetPositionAlign.TL, 242 Y.WidgetPositionAlign.RC 243 ], 244 245 initializer: function() { 246 // Set the initial values for the handlers. 247 // These cannot be set in the attributes section as context isn't present at that time. 248 if (!this.get('headerhandler')) { 249 this.set('headerhandler', this.set_header_content); 250 } 251 if (!this.get('bodyhandler')) { 252 this.set('bodyhandler', this.set_body_content); 253 } 254 if (!this.get('footerhandler')) { 255 this.set('footerhandler', function() {}); 256 } 257 if (!this.get('urlmodifier')) { 258 this.set('urlmodifier', this.modify_url); 259 } 260 261 // Set up the dialogue with initial content. 262 this.setAttrs({ 263 headerContent: this.get('initialheadertext'), 264 bodyContent: this.get('initialbodytext'), 265 footerContent: this.get('initialfootertext') 266 }); 267 268 // Hide and then render the dialogue. 269 this.hide(); 270 this.render(); 271 272 // Hook into a few useful areas. 273 this.bb = this.get('boundingBox'); 274 275 // Add an additional class to the boundingbox to allow tooltip-specific style to be 276 // set. 277 this.bb.addClass('moodle-dialogue-tooltip'); 278 279 // Change the alignment if this is an RTL language. 280 if (window.right_to_left()) { 281 this.alignpoints = [ 282 Y.WidgetPositionAlign.TR, 283 Y.WidgetPositionAlign.LC 284 ]; 285 } 286 287 // Set up the text cache if it's not set up already. 288 if (!this.get('textcache')) { 289 this.set('textcache', new Y.Cache({ 290 // Set a reasonable maximum cache size to prevent memory growth. 291 max: this.get('textcachesize') 292 })); 293 } 294 295 // Disable the textcache when in developerdebug. 296 if (M.cfg.developerdebug) { 297 this.get('textcache').set('max', 0); 298 } 299 300 return this; 301 }, 302 303 /** 304 * Display the tooltip for the clicked link. 305 * 306 * The anchor for the clicked link is used. 307 * 308 * @method display_panel 309 * @param {EventFacade} e The event from the clicked link. This is used to determine the clicked URL. 310 */ 311 display_panel: function(e) { 312 var clickedlink, thisevent, ajaxurl, config, cacheentry; 313 314 // Prevent the default click action and prevent the event triggering anything else. 315 e.preventDefault(); 316 317 // Cancel any existing listeners and close the panel if it's already open. 318 this.cancel_events(); 319 320 // Grab the clickedlink - this contains the URL we fetch and we align the panel to it. 321 clickedlink = e.target.ancestor('a', true); 322 323 // Reset the initial text to a spinner while we retrieve the text. 324 this.setAttrs({ 325 headerContent: this.get('initialheadertext'), 326 bodyContent: this.get('initialbodytext'), 327 footerContent: this.get('initialfootertext') 328 }); 329 330 // Now that initial setup has begun, show the panel. 331 this.show(e); 332 333 // Align with the link that was clicked. 334 this.align(clickedlink, this.alignpoints); 335 336 // Add some listen events to close on. 337 thisevent = this.bb.delegate('click', this.close_panel, SELECTORS.CLOSEBUTTON, this); 338 this.listenevents.push(thisevent); 339 340 thisevent = Y.one('body').on('key', this.close_panel, 'esc', this); 341 this.listenevents.push(thisevent); 342 343 // Listen for mousedownoutside events - clickoutside is broken on IE. 344 thisevent = this.bb.on('mousedownoutside', this.close_panel, this); 345 this.listenevents.push(thisevent); 346 347 // Modify the URL as required. 348 ajaxurl = Y.bind(this.get('urlmodifier'), this, clickedlink.get('href'))(); 349 350 cacheentry = this.get('textcache').retrieve(ajaxurl); 351 if (cacheentry) { 352 // The data from this help call was already cached so use that and avoid an AJAX call. 353 this._set_panel_contents(cacheentry.response); 354 } else { 355 // Retrieve the actual help text we should use. 356 config = { 357 method: 'get', 358 context: this, 359 sync: false, 360 on: { 361 complete: function(tid, response) { 362 this._set_panel_contents(response.responseText, ajaxurl); 363 } 364 } 365 }; 366 367 Y.io(ajaxurl, config); 368 } 369 }, 370 371 _set_panel_contents: function(response, ajaxurl) { 372 var responseobject; 373 374 // Attempt to parse the response into an object. 375 try { 376 responseobject = Y.JSON.parse(response); 377 if (responseobject.error) { 378 this.close_panel(); 379 Y.use('moodle-core-notification-ajaxexception', function() { 380 return new M.core.ajaxException(responseobject).show(); 381 }); 382 return this; 383 } 384 } catch (error) { 385 this.close_panel(); 386 Y.use('moodle-core-notification-exception', function() { 387 return new M.core.exception(error).show(); 388 }); 389 return this; 390 } 391 392 // Set the contents using various handlers. 393 // We must use Y.bind to ensure that the correct context is used when the default handlers are overridden. 394 Y.bind(this.get('headerhandler'), this, responseobject)(); 395 Y.bind(this.get('bodyhandler'), this, responseobject)(); 396 Y.bind(this.get('footerhandler'), this, responseobject)(); 397 398 if (ajaxurl) { 399 // Ensure that this data is added to the cache. 400 this.get('textcache').add(ajaxurl, response); 401 } 402 403 this.get('buttons').header[0].focus(); 404 }, 405 406 set_header_content: function(responseobject) { 407 this.set('headerContent', responseobject.heading); 408 }, 409 410 set_body_content: function(responseobject) { 411 var bodycontent = Y.Node.create('<div />') 412 .set('innerHTML', responseobject.text) 413 .setAttribute('role', 'alert') 414 .addClass(CSS.PANELTEXT); 415 this.set('bodyContent', bodycontent); 416 }, 417 418 modify_url: function(url) { 419 return url.replace(/\.php\?/, '_ajax.php?'); 420 }, 421 422 close_panel: function(e) { 423 // Hide the panel first. 424 this.hide(e); 425 426 // Cancel the listeners that we added in display_panel. 427 this.cancel_events(); 428 429 // Prevent any default click that the close button may have. 430 if (e) { 431 e.preventDefault(); 432 } 433 }, 434 435 cancel_events: function() { 436 // Detach all listen events to prevent duplicate triggers. 437 var thisevent; 438 while (this.listenevents.length) { 439 thisevent = this.listenevents.shift(); 440 thisevent.detach(); 441 } 442 } 443 }); 444 445 Y.Base.modifyAttrs(TOOLTIP, { 446 /** 447 * Whether the widget should be modal or not. 448 * 449 * Moodle override: We override this for tooltip to default it to false. 450 * 451 * @attribute Modal 452 * @type Boolean 453 * @default false 454 */ 455 modal: { 456 value: false 457 }, 458 459 focusOnPreviousTargetAfterHide: { 460 value: true 461 } 462 }); 463 464 M.core = M.core || {}; 465 M.core.tooltip = M.core.tooltip = TOOLTIP;
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |