[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/yui/src/tooltip/js/ -> tooltip.js (source)

   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;


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1