[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/yui/build/moodle-core-dock/ -> moodle-core-dock-debug.js (source)

   1  YUI.add('moodle-core-dock', function (Y, NAME) {
   2  
   3  /**
   4   * Dock JS.
   5   *
   6   * This file contains the DOCK object and all dock related global namespace methods and properties.
   7   *
   8   * @module moodle-core-dock
   9   */
  10  
  11  var LOGNS = 'moodle-core-dock',
  12      BODY = Y.one(Y.config.doc.body),
  13      CSS = {
  14          dock: 'dock',                    // CSS Class applied to the dock box
  15          dockspacer: 'dockspacer',        // CSS class applied to the dockspacer
  16          controls: 'controls',            // CSS class applied to the controls box
  17          body: 'has_dock',                // CSS class added to the body when there is a dock
  18          buttonscontainer: 'buttons_container',
  19          dockeditem: 'dockeditem',        // CSS class added to each item in the dock
  20          dockeditemcontainer: 'dockeditem_container',
  21          dockedtitle: 'dockedtitle',      // CSS class added to the item's title in each dock
  22          activeitem: 'activeitem',        // CSS class added to the active item
  23          dockonload: 'dock_on_load'
  24      },
  25      SELECTOR = {
  26          dockableblock: '.block[data-instanceid][data-dockable]',
  27          blockmoveto: '.block[data-instanceid][data-dockable] .moveto',
  28          panelmoveto: '#dockeditempanel .commands a.moveto',
  29          dockonload: '.block.' + CSS.dockonload,
  30          blockregion: '[data-blockregion]'
  31      },
  32      DOCK,
  33      DOCKPANEL,
  34      TABHEIGHTMANAGER,
  35      BLOCK,
  36      DOCKEDITEM; // eslint-disable-line no-unused-vars
  37  
  38  M.core = M.core || {};
  39  M.core.dock = M.core.dock || {};
  40  
  41  /**
  42   * The dock - once initialised.
  43   *
  44   * @private
  45   * @property _dock
  46   * @type DOCK
  47   */
  48  M.core.dock._dock = null;
  49  
  50  /**
  51   * An associative array of dockable blocks.
  52   * @property _dockableblocks
  53   * @type {Array} An array of BLOCK objects organised by instanceid.
  54   * @private
  55   */
  56  M.core.dock._dockableblocks = {};
  57  
  58  /**
  59   * Initialises the dock.
  60   * This method registers dockable blocks, and creates delegations to dock them.
  61   * @static
  62   * @method init
  63   */
  64  M.core.dock.init = function() {
  65      Y.all(SELECTOR.dockableblock).each(M.core.dock.registerDockableBlock);
  66      Y.Global.on(M.core.globalEvents.BLOCK_CONTENT_UPDATED, function(e) {
  67          M.core.dock.notifyBlockChange(e.instanceid);
  68      }, this);
  69      BODY.delegate('click', M.core.dock.dockBlock, SELECTOR.blockmoveto);
  70      BODY.delegate('key', M.core.dock.dockBlock, SELECTOR.blockmoveto, 'enter');
  71  };
  72  
  73  /**
  74   * Returns an instance of the dock.
  75   * Initialises one if one hasn't already being initialised.
  76   *
  77   * @static
  78   * @method get
  79   * @return DOCK
  80   */
  81  M.core.dock.get = function() {
  82      if (this._dock === null) {
  83          this._dock = new DOCK();
  84      }
  85      return this._dock;
  86  };
  87  
  88  /**
  89   * Registers a dockable block with the dock.
  90   *
  91   * @static
  92   * @method registerDockableBlock
  93   * @param {int} id The block instance ID.
  94   * @return void
  95   */
  96  M.core.dock.registerDockableBlock = function(id) {
  97      if (typeof id === 'object' && typeof id.getData === 'function') {
  98          id = id.getData('instanceid');
  99      }
 100      M.core.dock._dockableblocks[id] = new BLOCK({id: id});
 101  };
 102  
 103  /**
 104   * Docks a block given either its instanceid, its node, or an event fired from within the block.
 105   * @static
 106   * @method dockBlockByInstanceID
 107   * @param id
 108   * @return void
 109   */
 110  M.core.dock.dockBlock = function(id) {
 111      if (typeof id === 'object' && id.target !== 'undefined') {
 112          id = id.target;
 113      }
 114      if (typeof id === "object") {
 115          if (!id.test(SELECTOR.dockableblock)) {
 116              id = id.ancestor(SELECTOR.dockableblock);
 117          }
 118          if (typeof id === 'object' && typeof id.getData === 'function' && !id.ancestor('.' + CSS.dock)) {
 119              id = id.getData('instanceid');
 120          } else {
 121              Y.log('Invalid instanceid given to dockBlockByInstanceID', 'warn', LOGNS);
 122              return;
 123          }
 124      }
 125      var block = M.core.dock._dockableblocks[id];
 126      if (block) {
 127          block.moveToDock();
 128      }
 129  };
 130  
 131  /**
 132   * Fixes the title orientation. Rotating it if required.
 133   *
 134   * @static
 135   * @method fixTitleOrientation
 136   * @param {Node} title The title node we are looking at.
 137   * @param {String} text The string to use as the title.
 138   * @return {Node} The title node to use.
 139   */
 140  M.core.dock.fixTitleOrientation = function(title, text) {
 141      var dock = M.core.dock.get(),
 142          fontsize = '11px',
 143          transform = 'rotate(270deg)',
 144          test,
 145          width,
 146          height,
 147          container,
 148          verticaldirection = M.util.get_string('thisdirectionvertical', 'langconfig');
 149      title = Y.one(title);
 150  
 151      if (dock.get('orientation') !== 'vertical') {
 152          // If the dock isn't vertical don't adjust it!
 153          title.set('innerHTML', text);
 154          return title;
 155      }
 156  
 157      if (Y.UA.ie > 0 && Y.UA.ie < 8) {
 158          // IE 6/7 can't rotate text so force ver
 159          verticaldirection = 'ver';
 160      }
 161  
 162      switch (verticaldirection) {
 163          case 'ver':
 164              // Stacked is easy
 165              return title.set('innerHTML', text.split('').join('<br />'));
 166          case 'ttb':
 167              transform = 'rotate(90deg)';
 168              break;
 169          case 'btt':
 170              // Nothing to do here. transform default is good.
 171              break;
 172      }
 173  
 174      if (Y.UA.ie === 8) {
 175          // IE8 can flip the text via CSS but not handle transform. IE9+ can handle the CSS3 transform attribute.
 176          title.set('innerHTML', text);
 177          title.setAttribute('style', 'writing-mode: tb-rl; filter: flipV flipH;display:inline;');
 178          title.addClass('filterrotate');
 179          return title;
 180      }
 181  
 182      // We need to fix a font-size - sorry theme designers.
 183      test = Y.Node.create('<h2 class="transform-test-heading"><span class="transform-test-node" style="font-size:' +
 184              fontsize + ';">' + text + '</span></h2>');
 185      BODY.insert(test, 0);
 186      width = test.one('span').get('offsetWidth') * 1.2;
 187      height = test.one('span').get('offsetHeight');
 188      test.remove();
 189  
 190      title.set('innerHTML', text);
 191      title.addClass('css3transform');
 192  
 193      // Move the title into position
 194      title.setStyles({
 195          'position': 'relative',
 196          'fontSize': fontsize,
 197          'width': width,
 198          'top': (width - height) / 2
 199      });
 200  
 201      // Positioning is different when in RTL mode.
 202      if (window.right_to_left()) {
 203          title.setStyle('left', width / 2 - height);
 204      } else {
 205          title.setStyle('right', width / 2 - height);
 206      }
 207  
 208      // Rotate the text
 209      title.setStyles({
 210          'transform': transform,
 211          '-ms-transform': transform,
 212          '-moz-transform': transform,
 213          '-webkit-transform': transform,
 214          '-o-transform': transform
 215      });
 216  
 217      container = Y.Node.create('<div></div>');
 218      container.append(title);
 219      container.setStyles({
 220          height: width + (width / 4),
 221          position: 'relative'
 222      });
 223      return container;
 224  };
 225  
 226  /**
 227   * Informs the dock that the content of the block has changed.
 228   * This should be called by the blocks JS code if its content has been updated dynamically.
 229   * This method ensure the dock resizes if need be.
 230   *
 231   * @static
 232   * @method notifyBlockChange
 233   * @param {Number} instanceid
 234   * @return void
 235   */
 236  M.core.dock.notifyBlockChange = function(instanceid) {
 237      if (this._dock !== null) {
 238          var dock = M.core.dock.get(),
 239              activeitem = dock.getActiveItem();
 240          if (activeitem && activeitem.get('blockinstanceid') === parseInt(instanceid, 10)) {
 241              dock.resizePanelIfRequired();
 242          }
 243      }
 244  };
 245  
 246  /**
 247   * The Dock.
 248   *
 249   * @namespace M.core.dock
 250   * @class Dock
 251   * @constructor
 252   * @extends Base
 253   * @uses EventTarget
 254   */
 255  DOCK = function() {
 256      DOCK.superclass.constructor.apply(this, arguments);
 257  };
 258  DOCK.prototype = {
 259      /**
 260       * Tab height manager used to ensure tabs are always visible.
 261       * @protected
 262       * @property tabheightmanager
 263       * @type TABHEIGHTMANAGER
 264       */
 265      tabheightmanager: null,
 266      /**
 267       * Will be an eventtype if there is an eventype to prevent.
 268       * @protected
 269       * @property preventevent
 270       * @type String
 271       */
 272      preventevent: null,
 273      /**
 274       * Will be an object if there is a delayed event in effect.
 275       * @protected
 276       * @property delayedevent
 277       * @type {Object}
 278       */
 279      delayedevent: null,
 280      /**
 281       * An array of currently docked items.
 282       * @protected
 283       * @property dockeditems
 284       * @type Array
 285       */
 286      dockeditems: [],
 287      /**
 288       * Set to true once the dock has been drawn.
 289       * @protected
 290       * @property dockdrawn
 291       * @type Boolean
 292       */
 293      dockdrawn: false,
 294      /**
 295       * The number of blocks that are currently docked.
 296       * @protected
 297       * @property count
 298       * @type Number
 299       */
 300      count: 0,
 301      /**
 302       * The total number of blocks that have been docked.
 303       * @protected
 304       * @property totalcount
 305       * @type Number
 306       */
 307      totalcount: 0,
 308      /**
 309       * A hidden node used as a holding area for DOM objects used by blocks that have been docked.
 310       * @protected
 311       * @property holdingareanode
 312       * @type Node
 313       */
 314      holdingareanode: null,
 315      /**
 316       * Called during the initialisation process of the object.
 317       * @method initializer
 318       */
 319      initializer: function() {
 320          Y.log('Dock initialising', 'debug', LOGNS);
 321  
 322          // Publish the events the dock has
 323          /**
 324           * Fired when the dock first starts initialising.
 325           * @event dock:starting
 326           */
 327          this.publish('dock:starting', {prefix: 'dock', broadcast:  2, emitFacade: true, fireOnce: true});
 328          /**
 329           * Fired after the dock is initialised for the first time.
 330           * @event dock:initialised
 331           */
 332          this.publish('dock:initialised', {prefix: 'dock', broadcast:  2, emitFacade: true, fireOnce: true});
 333          /**
 334           * Fired before the dock structure and content is first created.
 335           * @event dock:beforedraw
 336           */
 337          this.publish('dock:beforedraw', {prefix: 'dock', fireOnce: true});
 338          /**
 339           * Fired before the dock is changed from hidden to visible.
 340           * @event dock:beforeshow
 341           */
 342          this.publish('dock:beforeshow', {prefix: 'dock'});
 343          /**
 344           * Fires after the dock has been changed from hidden to visible.
 345           * @event dock:shown
 346           */
 347          this.publish('dock:shown', {prefix: 'dock', broadcast: 2});
 348          /**
 349           * Fired after the dock has been changed from visible to hidden.
 350           * @event dock:hidden
 351           */
 352          this.publish('dock:hidden', {prefix: 'dock', broadcast: 2});
 353          /**
 354           * Fires when an item is added to the dock.
 355           * @event dock:itemadded
 356           */
 357          this.publish('dock:itemadded', {prefix: 'dock'});
 358          /**
 359           * Fires when an item is removed from the dock.
 360           * @event dock:itemremoved
 361           */
 362          this.publish('dock:itemremoved', {prefix: 'dock'});
 363          /**
 364           * Fires when a block is added or removed from the dock.
 365           * This happens after the itemadded and itemremoved events have been called.
 366           * @event dock:itemschanged
 367           */
 368          this.publish('dock:itemschanged', {prefix: 'dock', broadcast: 2});
 369          /**
 370           * Fires once when the docks panel is first initialised.
 371           * @event dock:panelgenerated
 372           */
 373          this.publish('dock:panelgenerated', {prefix: 'dock', fireOnce: true});
 374          /**
 375           * Fires when the dock panel is about to be resized.
 376           * @event dock:panelresizestart
 377           */
 378          this.publish('dock:panelresizestart', {prefix: 'dock'});
 379          /**
 380           * Fires after the dock panel has been resized.
 381           * @event dock:resizepanelcomplete
 382           */
 383          this.publish('dock:resizepanelcomplete', {prefix: 'dock'});
 384  
 385          // Apply theme customisations here before we do any real work.
 386          this._applyThemeCustomisation();
 387          // Inform everyone we are now about to initialise.
 388          this.fire('dock:starting');
 389          this._ensureDockDrawn();
 390          // Inform everyone the dock has been initialised
 391          this.fire('dock:initialised');
 392      },
 393      /**
 394       * Ensures that the dock has been drawn.
 395       * @private
 396       * @method _ensureDockDrawn
 397       * @return {Boolean}
 398       */
 399      _ensureDockDrawn: function() {
 400          if (this.dockdrawn === true) {
 401              return true;
 402          }
 403          var dock = this._initialiseDockNode(),
 404              clickargs = {
 405                  cssselector: '.' + CSS.dockedtitle,
 406                  delay: 0
 407              },
 408              mouseenterargs = {
 409                  cssselector: '.' + CSS.dockedtitle,
 410                  delay: 0.5,
 411                  iscontained: true,
 412                  preventevent: 'click',
 413                  preventdelay: 3
 414              };
 415          if (Y.UA.ie > 0 && Y.UA.ie < 7) {
 416              // Adjust for IE 6 (can't handle fixed pos)
 417              dock.setStyle('height', dock.get('winHeight') + 'px');
 418          }
 419  
 420          this.fire('dock:beforedraw');
 421  
 422          this._initialiseDockControls();
 423  
 424          this.tabheightmanager = new TABHEIGHTMANAGER({dock: this});
 425  
 426          // Attach the required event listeners
 427          // We use delegate here as that way a handful of events are created for the dock
 428          // and all items rather than the same number for the dock AND every item individually
 429          Y.delegate('click', this.handleEvent, this.get('dockNode'), '.' + CSS.dockedtitle, this, clickargs);
 430          Y.delegate('mouseenter', this.handleEvent, this.get('dockNode'), '.' + CSS.dockedtitle, this, mouseenterargs);
 431          this.get('dockNode').on('mouseleave', this.handleEvent, this, {cssselector: '#dock', delay: 0.5, iscontained: false});
 432  
 433          Y.delegate('click', this.handleReturnToBlock, this.get('dockNode'), SELECTOR.panelmoveto, this);
 434          Y.delegate('dock:actionkey', this.handleDockedItemEvent, this.get('dockNode'), '.' + CSS.dockeditem, this);
 435  
 436          BODY.on('click', this.handleEvent, this, {cssselector: 'body', delay: 0});
 437          this.on('dock:itemschanged', this.resizeBlockSpace, this);
 438          this.on('dock:itemschanged', this.checkDockVisibility, this);
 439          this.on('dock:itemschanged', this.resetFirstItem, this);
 440          this.dockdrawn = true;
 441          return true;
 442      },
 443      /**
 444       * Handles an actionkey event on the dock.
 445       * @param {EventFacade} e
 446       * @method handleDockedItemEvent
 447       * @return {Boolean}
 448       */
 449      handleDockedItemEvent: function(e) {
 450          if (e.type !== 'dock:actionkey') {
 451              return false;
 452          }
 453          var target = e.target,
 454              dockeditem = '.' + CSS.dockeditem;
 455          if (!target.test(dockeditem)) {
 456              target = target.ancestor(dockeditem);
 457          }
 458          if (!target) {
 459              return false;
 460          }
 461          e.halt();
 462          this.dockeditems[target.getAttribute('rel')].toggle(e.action);
 463      },
 464      /**
 465       * Call the theme customisation method "customise_dock_for_theme" if it exists.
 466       * @private
 467       * @method _applyThemeCustomisation
 468       */
 469      _applyThemeCustomisation: function() {
 470          // Check if there is a customisation function
 471          if (typeof (customise_dock_for_theme) === 'function') {
 472              // First up pre the legacy object.
 473              M.core_dock = this;
 474              M.core_dock.cfg = {
 475                  buffer: null,
 476                  orientation: null,
 477                  position: null,
 478                  spacebeforefirstitem: null,
 479                  removeallicon: null
 480              };
 481              M.core_dock.css = {
 482                  dock: null,
 483                  dockspacer: null,
 484                  controls: null,
 485                  body: null,
 486                  buttonscontainer: null,
 487                  dockeditem: null,
 488                  dockeditemcontainer: null,
 489                  dockedtitle: null,
 490                  activeitem: null
 491              };
 492              try {
 493                  // Run the customisation function
 494                  window.customise_dock_for_theme(this);
 495              } catch (exception) {
 496                  // Do nothing at the moment.
 497                  Y.log('Exception while attempting to apply theme customisations.', 'error', LOGNS);
 498              }
 499              // Now to work out what they did.
 500              var key, value,
 501                  warned = false,
 502                  cfgmap = {
 503                      buffer: 'bufferPanel',
 504                      orientation: 'orientation',
 505                      position: 'position',
 506                      spacebeforefirstitem: 'bufferBeforeFirstItem',
 507                      removeallicon: 'undockAllIconUrl'
 508                  };
 509              // Check for and apply any legacy configuration.
 510              for (key in M.core_dock.cfg) {
 511                  if (Y.Lang.isString(key) && cfgmap[key]) {
 512                      value = M.core_dock.cfg[key];
 513                      if (value === null) {
 514                          continue;
 515                      }
 516                      if (!warned) {
 517                          Y.log('Warning: customise_dock_for_theme has changed. Please update your code.', 'warn', LOGNS);
 518                          warned = true;
 519                      }
 520                      // Damn, the've set something.
 521                      Y.log('Note for customise_dock_for_theme code: M.core_dock.cfg.' + key +
 522                              ' is now dock.set(\'' + key + '\', value)',
 523                              'debug', LOGNS);
 524                      this.set(cfgmap[key], value);
 525                  }
 526              }
 527              // Check for and apply any legacy CSS changes..
 528              for (key in M.core_dock.css) {
 529                  if (Y.Lang.isString(key)) {
 530                      value = M.core_dock.css[key];
 531                      if (value === null) {
 532                          continue;
 533                      }
 534                      if (!warned) {
 535                          Y.log('Warning: customise_dock_for_theme has changed. Please update your code.', 'warn', LOGNS);
 536                          warned = true;
 537                      }
 538                      // Damn, they've set something.
 539                      Y.log('Note for customise_dock_for_theme code: M.core_dock.css.' + key + ' is now CSS.' + key + ' = value',
 540                              'debug', LOGNS);
 541                      CSS[key] = value;
 542                  }
 543              }
 544          }
 545      },
 546      /**
 547       * Initialises the dock node, creating it and its content if required.
 548       *
 549       * @private
 550       * @method _initialiseDockNode
 551       * @return {Node} The dockNode
 552       */
 553      _initialiseDockNode: function() {
 554          var dock = this.get('dockNode'),
 555              positionorientationclass = CSS.dock + '_' + this.get('position') + '_' + this.get('orientation'),
 556              holdingarea = Y.Node.create('<div></div>').setStyles({display: 'none'}),
 557              buttons = this.get('buttonsNode'),
 558              container = this.get('itemContainerNode');
 559  
 560          if (!dock) {
 561              dock = Y.one('#' + CSS.dock);
 562          }
 563          if (!dock) {
 564              dock = Y.Node.create('<div id="' + CSS.dock + '"></div>');
 565              BODY.append(dock);
 566          }
 567          dock.setAttribute('role', 'menubar').addClass(positionorientationclass);
 568          if (Y.all(SELECTOR.dockonload).size() === 0) {
 569              // Nothing on the dock... hide it using CSS
 570              dock.addClass('nothingdocked');
 571          } else {
 572              positionorientationclass = CSS.body + '_' + this.get('position') + '_' + this.get('orientation');
 573              BODY.addClass(CSS.body).addClass();
 574          }
 575  
 576          if (!buttons) {
 577              buttons = dock.one('.' + CSS.buttonscontainer);
 578          }
 579          if (!buttons) {
 580              buttons = Y.Node.create('<div class="' + CSS.buttonscontainer + '"></div>');
 581              dock.append(buttons);
 582          }
 583  
 584          if (!container) {
 585              container = dock.one('.' + CSS.dockeditemcontainer);
 586          }
 587          if (!container) {
 588              container = Y.Node.create('<div class="' + CSS.dockeditemcontainer + '"></div>');
 589              buttons.append(container);
 590          }
 591  
 592          BODY.append(holdingarea);
 593          this.holdingareanode = holdingarea;
 594  
 595          this.set('dockNode', dock);
 596          this.set('buttonsNode', buttons);
 597          this.set('itemContainerNode', container);
 598  
 599          return dock;
 600      },
 601      /**
 602       * Initialises the dock controls.
 603       *
 604       * @private
 605       * @method _initialiseDockControls
 606       */
 607      _initialiseDockControls: function() {
 608          // Add a removeall button
 609          // Must set the image src seperatly of we get an error with XML strict headers
 610  
 611          var removeall = Y.Node.create('<img alt="' + M.util.get_string('undockall', 'block') + '" tabindex="0" />');
 612          removeall.setAttribute('src', this.get('undockAllIconUrl'));
 613          removeall.on('removeall|click', this.removeAll, this);
 614          removeall.on('dock:actionkey', this.removeAll, this, {actions: {enter: true}});
 615          this.get('buttonsNode').append(Y.Node.create('<div class="' + CSS.controls + '"></div>').append(removeall));
 616      },
 617      /**
 618       * Returns the dock panel. Initialising it if it hasn't already been initialised.
 619       * @method getPanel
 620       * @return {DOCKPANEL}
 621       */
 622      getPanel: function() {
 623          var panel = this.get('panel');
 624          if (!panel) {
 625              panel = new DOCKPANEL({dock: this});
 626              panel.on('panel:visiblechange', this.resize, this);
 627              Y.on('windowresize', this.resize, this);
 628              // Initialise the dockpanel .. should only happen once
 629              this.set('panel', panel);
 630              this.fire('dock:panelgenerated');
 631          }
 632          return panel;
 633      },
 634      /**
 635       * Resizes the dock panel if required.
 636       * @method resizePanelIfRequired
 637       */
 638      resizePanelIfRequired: function() {
 639          this.resize();
 640          var panel = this.get('panel');
 641          if (panel) {
 642              panel.correctWidth();
 643          }
 644      },
 645      /**
 646       * Handles a dock event sending it to the right place.
 647       *
 648       * @method handleEvent
 649       * @param {EventFacade} e
 650       * @param {Object} options
 651       * @return {Boolean}
 652       */
 653      handleEvent: function(e, options) {
 654          var item = this.getActiveItem(),
 655              target,
 656              targetid,
 657              regex = /^dock_item_(\d+)_title$/,
 658              self = this;
 659          if (options.cssselector === 'body') {
 660              if (!this.get('dockNode').contains(e.target)) {
 661                  if (item) {
 662                      item.hide();
 663                  }
 664              }
 665          } else {
 666              if (e.target.test(options.cssselector)) {
 667                  target = e.target;
 668              } else {
 669                  target = e.target.ancestor(options.cssselector);
 670              }
 671              if (!target) {
 672                  return true;
 673              }
 674              if (this.preventevent !== null && e.type === this.preventevent) {
 675                  return true;
 676              }
 677              if (options.preventevent) {
 678                  this.preventevent = options.preventevent;
 679                  if (options.preventdelay) {
 680                      setTimeout(function() {
 681                          self.preventevent = null;
 682                      }, options.preventdelay * 1000);
 683                  }
 684              }
 685              if (this.delayedevent && this.delayedevent.timeout) {
 686                  clearTimeout(this.delayedevent.timeout);
 687                  this.delayedevent.event.detach();
 688                  this.delayedevent = null;
 689              }
 690              if (options.delay > 0) {
 691                  return this.delayEvent(e, options, target);
 692              }
 693              targetid = target.get('id');
 694              if (targetid.match(regex)) {
 695                  item = this.dockeditems[targetid.replace(regex, '$1')];
 696                  if (item.active) {
 697                      item.hide();
 698                  } else {
 699                      item.show();
 700                  }
 701              } else if (item) {
 702                  item.hide();
 703              }
 704          }
 705          return true;
 706      },
 707      /**
 708       * Delays an event.
 709       *
 710       * @method delayEvent
 711       * @param {EventFacade} event
 712       * @param {Object} options
 713       * @param {Node} target
 714       * @return {Boolean}
 715       */
 716      delayEvent: function(event, options, target) {
 717          var self = this;
 718          self.delayedevent = (function() {
 719              return {
 720                  target: target,
 721                  event: BODY.on('mousemove', function(e) {
 722                      self.delayedevent.target = e.target;
 723                  }),
 724                  timeout: null
 725              };
 726          })(self);
 727          self.delayedevent.timeout = setTimeout(function() {
 728              self.delayedevent.timeout = null;
 729              self.delayedevent.event.detach();
 730              if (options.iscontained === self.get('dockNode').contains(self.delayedevent.target)) {
 731                  self.handleEvent(event, {cssselector: options.cssselector, delay: 0, iscontained: options.iscontained});
 732              }
 733          }, options.delay * 1000);
 734          return true;
 735      },
 736      /**
 737       * Resizes block spaces.
 738       * @method resizeBlockSpace
 739       */
 740      resizeBlockSpace: function() {
 741          if (Y.all(SELECTOR.dockonload).size() > 0) {
 742              // Do not resize during initial load
 743              return;
 744          }
 745  
 746          var populatedRegionCount = 0,
 747              populatedBlockRegions = [],
 748              unpopulatedBlockRegions = [],
 749              isMoving = false,
 750              populatedLegacyRegions = [],
 751              containsLegacyRegions = false,
 752              classesToAdd = [],
 753              classesToRemove = [];
 754  
 755          // First look for understood regions.
 756          Y.all(SELECTOR.blockregion).each(function(region) {
 757              var regionname = region.getData('blockregion');
 758              if (region.all('.block').size() > 0) {
 759                  populatedBlockRegions.push(regionname);
 760                  populatedRegionCount++;
 761              } else if (region.all('.block_dock_placeholder').size() > 0) {
 762                  unpopulatedBlockRegions.push(regionname);
 763              }
 764          });
 765  
 766          // Next check for legacy regions.
 767          Y.all('.block-region').each(function(region) {
 768              if (region.test(SELECTOR.blockregion)) {
 769                  // This is a new region, we've already processed it.
 770                  return;
 771              }
 772  
 773              // Sigh - there are legacy regions.
 774              containsLegacyRegions = true;
 775  
 776              var regionname = region.get('id').replace(/^region\-/, 'side-'),
 777                  hasblocks = (region.all('.block').size() > 0);
 778  
 779              if (hasblocks) {
 780                  populatedLegacyRegions.push(regionname);
 781                  populatedRegionCount++;
 782              } else {
 783                  // This legacy region has no blocks so cannot have the -only body tag.
 784                  classesToRemove.push(
 785                          regionname + '-only'
 786                      );
 787              }
 788          });
 789  
 790          if (BODY.hasClass('blocks-moving')) {
 791              // When we're moving blocks, we do not want to collapse.
 792              isMoving = true;
 793          }
 794  
 795          Y.each(unpopulatedBlockRegions, function(regionname) {
 796              classesToAdd.push(
 797                      // This block region is empty.
 798                      'empty-region-' + regionname,
 799  
 800                      // Which has the same effect as being docked.
 801                      'docked-region-' + regionname
 802                  );
 803              classesToRemove.push(
 804                      // It is no-longer used.
 805                      'used-region-' + regionname,
 806  
 807                      // It cannot be the only region on screen if it is empty.
 808                      regionname + '-only'
 809                  );
 810          }, this);
 811  
 812          Y.each(populatedBlockRegions, function(regionname) {
 813              classesToAdd.push(
 814                      // This block region is in use.
 815                      'used-region-' + regionname
 816                  );
 817              classesToRemove.push(
 818                      // It is not empty.
 819                      'empty-region-' + regionname,
 820  
 821                      // Is it not docked.
 822                      'docked-region-' + regionname
 823                  );
 824  
 825              if (populatedRegionCount === 1 && isMoving === false) {
 826                  // There was only one populated region, and we are not moving blocks.
 827                  classesToAdd.push(regionname + '-only');
 828              } else {
 829                  // There were multiple block regions visible - remove any 'only' classes.
 830                  classesToRemove.push(regionname + '-only');
 831              }
 832          }, this);
 833  
 834          if (containsLegacyRegions) {
 835              // Handle the classing for legacy blocks. These have slightly different class names for the body.
 836              if (isMoving || populatedRegionCount !== 1) {
 837                  Y.each(populatedLegacyRegions, function(regionname) {
 838                      classesToRemove.push(regionname + '-only');
 839                  });
 840              } else {
 841                  Y.each(populatedLegacyRegions, function(regionname) {
 842                      classesToAdd.push(regionname + '-only');
 843                  });
 844              }
 845          }
 846  
 847          if (!BODY.hasClass('has-region-content')) {
 848              // This page does not have a content region, therefore content-only is implied when all block regions are docked.
 849              if (populatedRegionCount === 0 && isMoving === false) {
 850                  // If all blocks are docked, ensure that the content-only class is added anyway.
 851                  classesToAdd.push('content-only');
 852              } else {
 853                  // Otherwise remove it.
 854                  classesToRemove.push('content-only');
 855              }
 856          }
 857  
 858          // Modify the body clases.
 859          Y.each(classesToRemove, function(className) {
 860              BODY.removeClass(className);
 861          });
 862          Y.each(classesToAdd, function(className) {
 863              BODY.addClass(className);
 864          });
 865      },
 866      /**
 867       * Adds an item to the dock.
 868       * @method add
 869       * @param {DOCKEDITEM} item
 870       */
 871      add: function(item) {
 872          // Set the dockitem id to the total count and then increment it.
 873          item.set('id', this.totalcount);
 874          Y.log('Adding block ' + item._getLogDescription() + ' to the dock.', 'debug', LOGNS);
 875          this.count++;
 876          this.totalcount++;
 877          this.dockeditems[item.get('id')] = item;
 878          this.dockeditems[item.get('id')].draw();
 879          this.fire('dock:itemadded', item);
 880          this.fire('dock:itemschanged', item);
 881      },
 882      /**
 883       * Appends an item to the dock (putting it in the item container.
 884       * @method append
 885       * @param {Node} docknode
 886       */
 887      append: function(docknode) {
 888          this.get('itemContainerNode').append(docknode);
 889      },
 890      /**
 891       * Handles events that require a docked block to be returned to the page./
 892       * @method handleReturnToBlock
 893       * @param {EventFacade} e
 894       */
 895      handleReturnToBlock: function(e) {
 896          e.halt();
 897          this.remove(this.getActiveItem().get('id'));
 898      },
 899      /**
 900       * Removes a docked item from the dock.
 901       * @method remove
 902       * @param {Number} id The docked item id.
 903       * @return {Boolean}
 904       */
 905      remove: function(id) {
 906          if (!this.dockeditems[id]) {
 907              return false;
 908          }
 909          Y.log('Removing block ' + this.dockeditems[id]._getLogDescription() + ' from the dock.', 'debug', LOGNS);
 910          this.dockeditems[id].remove();
 911          delete this.dockeditems[id];
 912          this.count--;
 913          this.fire('dock:itemremoved', id);
 914          this.fire('dock:itemschanged', id);
 915          return true;
 916      },
 917      /**
 918       * Ensures the the first item in the dock has the correct class.
 919       * @method resetFirstItem
 920       */
 921      resetFirstItem: function() {
 922          this.get('dockNode').all('.' + CSS.dockeditem + '.firstdockitem').removeClass('firstdockitem');
 923          if (this.get('dockNode').one('.' + CSS.dockeditem)) {
 924              this.get('dockNode').one('.' + CSS.dockeditem).addClass('firstdockitem');
 925          }
 926      },
 927      /**
 928       * Removes all docked blocks returning them to the page.
 929       * @method removeAll
 930       * @return {Boolean}
 931       */
 932      removeAll: function() {
 933          Y.log('Undocking all ' + this.dockeditems.length + ' blocks', 'debug', LOGNS);
 934          var i;
 935          for (i in this.dockeditems) {
 936              if (Y.Lang.isNumber(i) || Y.Lang.isString(i)) {
 937                  this.remove(i);
 938              }
 939          }
 940          return true;
 941      },
 942      /**
 943       * Hides the active item.
 944       * @method hideActive
 945       */
 946      hideActive: function() {
 947          var item = this.getActiveItem();
 948          if (item) {
 949              item.hide();
 950          }
 951      },
 952      /**
 953       * Checks wether the dock should be shown or hidden
 954       * @method checkDockVisibility
 955       */
 956      checkDockVisibility: function() {
 957          var bodyclass = CSS.body + '_' + this.get('position') + '_' + this.get('orientation');
 958          if (!this.count) {
 959              this.get('dockNode').addClass('nothingdocked');
 960              BODY.removeClass(CSS.body).removeClass();
 961              this.fire('dock:hidden');
 962          } else {
 963              this.fire('dock:beforeshow');
 964              this.get('dockNode').removeClass('nothingdocked');
 965              BODY.addClass(CSS.body).addClass(bodyclass);
 966              this.fire('dock:shown');
 967          }
 968      },
 969      /**
 970       * This function checks the size and position of the panel and moves/resizes if
 971       * required to keep it within the bounds of the window.
 972       * @method resize
 973       * @return {Boolean}
 974       */
 975      resize: function() {
 976          var panel = this.getPanel(),
 977              item = this.getActiveItem(),
 978              buffer,
 979              screenh,
 980              docky,
 981              titletop,
 982              containery,
 983              containerheight,
 984              scrolltop,
 985              panelheight,
 986              dockx,
 987              titleleft;
 988          if (!panel.get('visible') || !item) {
 989              return true;
 990          }
 991  
 992          this.fire('dock:panelresizestart');
 993          if (this.get('orientation') === 'vertical') {
 994              buffer = this.get('bufferPanel');
 995              screenh = parseInt(BODY.get('winHeight'), 10) - (buffer * 2);
 996              docky = this.get('dockNode').getY();
 997              titletop = item.get('dockTitleNode').getY() - docky - buffer;
 998              containery = this.get('itemContainerNode').getY();
 999              containerheight = containery - docky + this.get('buttonsNode').get('offsetHeight');
1000              scrolltop = panel.get('bodyNode').get('scrollTop');
1001              panel.get('bodyNode').setStyle('height', 'auto');
1002              panel.get('node').removeClass('oversized_content');
1003              panelheight = panel.get('node').get('offsetHeight');
1004  
1005              if (Y.UA.ie > 0 && Y.UA.ie < 7) {
1006                  panel.setTop(item.get('dockTitleNode').getY());
1007              } else if (panelheight > screenh) {
1008                  panel.setTop(buffer - containerheight);
1009                  panel.get('bodyNode').setStyle('height', (screenh - panel.get('headerNode').get('offsetHeight')) + 'px');
1010                  panel.get('node').addClass('oversized_content');
1011              } else if (panelheight > (screenh - (titletop - buffer))) {
1012                  panel.setTop(titletop - containerheight - (panelheight - (screenh - titletop)) + buffer);
1013              } else {
1014                  panel.setTop(titletop - containerheight + buffer);
1015              }
1016  
1017              if (scrolltop) {
1018                  panel.get('bodyNode').set('scrollTop', scrolltop);
1019              }
1020          }
1021  
1022          if (this.get('position') === 'right') {
1023              panel.get('node').setStyle('left', '-' + panel.get('node').get('offsetWidth') + 'px');
1024  
1025          } else if (this.get('position') === 'top') {
1026              dockx = this.get('dockNode').getX();
1027              titleleft = item.get('dockTitleNode').getX() - dockx;
1028              panel.get('node').setStyle('left', titleleft + 'px');
1029          }
1030  
1031          this.fire('dock:resizepanelcomplete');
1032          return true;
1033      },
1034      /**
1035       * Returns the currently active dock item or false
1036       * @method getActiveItem
1037       * @return {DOCKEDITEM}
1038       */
1039      getActiveItem: function() {
1040          var i;
1041          for (i in this.dockeditems) {
1042              if (this.dockeditems[i].active) {
1043                  return this.dockeditems[i];
1044              }
1045          }
1046          return false;
1047      },
1048      /**
1049       * Adds an item to the holding area.
1050       * @method addToHoldingArea
1051       * @param {Node} node
1052       */
1053      addToHoldingArea: function(node) {
1054          this.holdingareanode.append(node);
1055      }
1056  };
1057  
1058  Y.extend(DOCK, Y.Base, DOCK.prototype, {
1059      NAME: 'moodle-core-dock',
1060      ATTRS: {
1061          /**
1062           * The dock itself. #dock.
1063           * @attribute dockNode
1064           * @type Node
1065           * @writeOnce
1066           */
1067          dockNode: {
1068              writeOnce: true
1069          },
1070          /**
1071           * The docks panel.
1072           * @attribute panel
1073           * @type DOCKPANEL
1074           * @writeOnce
1075           */
1076          panel: {
1077              writeOnce: true
1078          },
1079          /**
1080           * A container within the dock used for buttons.
1081           * @attribute buttonsNode
1082           * @type Node
1083           * @writeOnce
1084           */
1085          buttonsNode: {
1086              writeOnce: true
1087          },
1088          /**
1089           * A container within the dock used for docked blocks.
1090           * @attribute itemContainerNode
1091           * @type Node
1092           * @writeOnce
1093           */
1094          itemContainerNode: {
1095              writeOnce: true
1096          },
1097  
1098          /**
1099           * Buffer used when containing a panel.
1100           * @attribute bufferPanel
1101           * @type Number
1102           * @default 10
1103           */
1104          bufferPanel: {
1105              value: 10,
1106              validator: Y.Lang.isNumber
1107          },
1108  
1109          /**
1110           * Position of the dock.
1111           * @attribute position
1112           * @type String
1113           * @default left
1114           */
1115          position: {
1116              value: 'left',
1117              validator: Y.Lang.isString
1118          },
1119  
1120          /**
1121           * vertical || horizontal determines if we change the title
1122           * @attribute orientation
1123           * @type String
1124           * @default vertical
1125           */
1126          orientation: {
1127              value: 'vertical',
1128              validator: Y.Lang.isString,
1129              setter: function(value) {
1130                  if (value.match(/^vertical$/i)) {
1131                      return 'vertical';
1132                  }
1133                  return 'horizontal';
1134              }
1135          },
1136  
1137          /**
1138           * Space between the top of the dock and the first item.
1139           * @attribute bufferBeforeFirstItem
1140           * @type Number
1141           * @default 10
1142           */
1143          bufferBeforeFirstItem: {
1144              value: 10,
1145              validator: Y.Lang.isNumber
1146          },
1147  
1148          /**
1149           * Icon URL for the icon to undock all blocks
1150           * @attribute undockAllIconUrl
1151           * @type String
1152           * @default t/dock_to_block
1153           */
1154          undockAllIconUrl: {
1155              value: M.util.image_url((window.right_to_left()) ? 't/dock_to_block_rtl' : 't/dock_to_block', 'moodle'),
1156              validator: Y.Lang.isString
1157          }
1158      }
1159  });
1160  Y.augment(DOCK, Y.EventTarget);
1161  /* global DOCKPANEL, LOGNS */
1162  
1163  /**
1164   * Dock JS.
1165   *
1166   * This file contains the panel class used by the dock to display the content of docked blocks.
1167   *
1168   * @module moodle-core-dock
1169   */
1170  
1171  /**
1172   * Panel.
1173   *
1174   * @namespace M.core.dock
1175   * @class Panel
1176   * @constructor
1177   * @extends Base
1178   * @uses EventTarget
1179   */
1180  DOCKPANEL = function() {
1181      DOCKPANEL.superclass.constructor.apply(this, arguments);
1182  };
1183  DOCKPANEL.prototype = {
1184      /**
1185       * True once the panel has been created.
1186       * @property created
1187       * @protected
1188       * @type {Boolean}
1189       */
1190      created: false,
1191      /**
1192       * Called during the initialisation process of the object.
1193       * @method initializer
1194       */
1195      initializer: function() {
1196          Y.log('Panel initialising', 'debug', LOGNS);
1197          /**
1198           * Fired before the panel is shown.
1199           * @event dockpane::beforeshow
1200           */
1201          this.publish('dockpanel:beforeshow', {prefix: 'dockpanel'});
1202          /**
1203           * Fired after the panel is shown.
1204           * @event dockpanel:shown
1205           */
1206          this.publish('dockpanel:shown', {prefix: 'dockpanel'});
1207          /**
1208           * Fired before the panel is hidden.
1209           * @event dockpane::beforehide
1210           */
1211          this.publish('dockpanel:beforehide', {prefix: 'dockpanel'});
1212          /**
1213           * Fired after the panel is hidden.
1214           * @event dockpanel:hidden
1215           */
1216          this.publish('dockpanel:hidden', {prefix: 'dockpanel'});
1217          /**
1218           * Fired when ever the dock panel is either hidden or shown.
1219           * Always fired after the shown or hidden events.
1220           * @event dockpanel:visiblechange
1221           */
1222          this.publish('dockpanel:visiblechange', {prefix: 'dockpanel'});
1223      },
1224      /**
1225       * Creates the Panel if it has not already been created.
1226       * @method create
1227       * @return {Boolean}
1228       */
1229      create: function() {
1230          if (this.created) {
1231              return true;
1232          }
1233          this.created = true;
1234          var dock = this.get('dock'),
1235              node = dock.get('dockNode');
1236          this.set('node', Y.Node.create('<div id="dockeditempanel" class="dockitempanel_hidden"></div>'));
1237          this.set('contentNode', Y.Node.create('<div class="dockeditempanel_content"></div>'));
1238          this.set('headerNode', Y.Node.create('<div class="dockeditempanel_hd"></div>'));
1239          this.set('bodyNode', Y.Node.create('<div class="dockeditempanel_bd"></div>'));
1240          node.append(
1241              this.get('node').append(this.get('contentNode').append(this.get('headerNode')).append(this.get('bodyNode')))
1242          );
1243      },
1244      /**
1245       * Displays the panel.
1246       * @method show
1247       */
1248      show: function() {
1249          this.create();
1250          this.fire('dockpanel:beforeshow');
1251          this.set('visible', true);
1252          this.get('node').removeClass('dockitempanel_hidden');
1253          this.fire('dockpanel:shown');
1254          this.fire('dockpanel:visiblechange');
1255      },
1256      /**
1257       * Hides the panel
1258       * @method hide
1259       */
1260      hide: function() {
1261          this.fire('dockpanel:beforehide');
1262          this.set('visible', false);
1263          this.get('node').addClass('dockitempanel_hidden');
1264          this.fire('dockpanel:hidden');
1265          this.fire('dockpanel:visiblechange');
1266      },
1267      /**
1268       * Sets the panel header.
1269       * @method setHeader
1270       * @param {Node|String} content
1271       */
1272      setHeader: function(content) {
1273          this.create();
1274          var header = this.get('headerNode'),
1275              i;
1276          header.setContent(content);
1277          if (arguments.length > 1) {
1278              for (i = 1; i < arguments.length; i++) {
1279                  if (Y.Lang.isNumber(i) || Y.Lang.isString(i)) {
1280                      header.append(arguments[i]);
1281                  }
1282              }
1283          }
1284      },
1285      /**
1286       * Sets the panel body.
1287       * @method setBody
1288       * @param {Node|String} content
1289       */
1290      setBody: function(content) {
1291          this.create();
1292          this.get('bodyNode').setContent(content);
1293      },
1294      /**
1295       * Sets the new top mark of the panel.
1296       *
1297       * @method setTop
1298       * @param {Number} newtop
1299       */
1300      setTop: function(newtop) {
1301          if (Y.UA.ie > 0 && Y.UA.ie < 7) {
1302              this.get('node').setY(newtop);
1303          } else {
1304              this.get('node').setStyle('top', newtop.toString() + 'px');
1305          }
1306      },
1307      /**
1308       * Corrects the width of the panel.
1309       * @method correctWidth
1310       */
1311      correctWidth: function() {
1312          var bodyNode = this.get('bodyNode'),
1313              // Width of content.
1314              width = bodyNode.get('clientWidth'),
1315              // Scrollable width of content.
1316              scroll = bodyNode.get('scrollWidth'),
1317              // Width of content container with overflow.
1318              offsetWidth = bodyNode.get('offsetWidth'),
1319              // The new width - defaults to the current width.
1320              newWidth = width,
1321              // The max width (80% of screen).
1322              maxWidth = Math.round(bodyNode.get('winWidth') * 0.8);
1323  
1324          // If the scrollable width is more than the visible width
1325          if (scroll > width) {
1326              //   Content width
1327              // + the difference
1328              // + any rendering difference (borders, padding)
1329              // + 10px to make it look nice.
1330              newWidth = width + (scroll - width) + ((offsetWidth - width) * 2) + 10;
1331          }
1332  
1333          // Make sure its not more then the maxwidth
1334          if (newWidth > maxWidth) {
1335              newWidth = maxWidth;
1336          }
1337  
1338          // Set the new width if its more than the old width.
1339          if (newWidth > offsetWidth) {
1340              this.get('node').setStyle('width', newWidth + 'px');
1341          }
1342      }
1343  };
1344  Y.extend(DOCKPANEL, Y.Base, DOCKPANEL.prototype, {
1345      NAME: 'moodle-core-dock-panel',
1346      ATTRS: {
1347          /**
1348           * The dock itself.
1349           * @attribute dock
1350           * @type DOCK
1351           * @writeonce
1352           */
1353          dock: {
1354              writeOnce: 'initOnly'
1355          },
1356          /**
1357           * The node that contains the whole panel.
1358           * @attribute node
1359           * @type Node
1360           */
1361          node: {
1362              value: null
1363          },
1364          /**
1365           * The node that contains the header, body and footer.
1366           * @attribute contentNode
1367           * @type Node
1368           */
1369          contentNode: {
1370              value: null
1371          },
1372          /**
1373           * The node that contains the header
1374           * @attribute headerNode
1375           * @type Node
1376           */
1377          headerNode: {
1378              value: null
1379          },
1380          /**
1381           * The node that contains the body
1382           * @attribute bodyNode
1383           * @type Node
1384           */
1385          bodyNode: {
1386              value: null
1387          },
1388          /**
1389           * True if the panel is currently visible.
1390           * @attribute visible
1391           * @type Boolean
1392           */
1393          visible: {
1394              value: false
1395          }
1396      }
1397  });
1398  Y.augment(DOCKPANEL, Y.EventTarget);
1399  /* global TABHEIGHTMANAGER, LOGNS */
1400  
1401  /**
1402   * Dock JS.
1403   *
1404   * This file contains the tab height manager.
1405   * The tab height manager is responsible for ensure all tabs are visible all the time.
1406   *
1407   * @module moodle-core-dock
1408   */
1409  
1410  /**
1411   * Tab height manager.
1412   *
1413   * @namespace M.core.dock
1414   * @class TabHeightManager
1415   * @constructor
1416   * @extends Base
1417   */
1418  TABHEIGHTMANAGER = function() {
1419      TABHEIGHTMANAGER.superclass.constructor.apply(this, arguments);
1420  };
1421  TABHEIGHTMANAGER.prototype = {
1422      /**
1423       * Initialises the dock sizer which then attaches itself to the required
1424       * events in order to monitor the dock
1425       * @method initializer
1426       */
1427      initializer: function() {
1428          var dock = this.get('dock');
1429          dock.on('dock:itemschanged', this.checkSizing, this);
1430          Y.on('windowresize', this.checkSizing, this);
1431      },
1432      /**
1433       * Check if the size dock items needs to be adjusted
1434       * @method checkSizing
1435       */
1436      checkSizing: function() {
1437          var dock = this.get('dock'),
1438              node = dock.get('dockNode'),
1439              items = dock.dockeditems,
1440              containermargin = parseInt(node.one('.dockeditem_container').getStyle('marginTop').replace('/[^0-9]+$/', ''), 10),
1441              dockheight = node.get('offsetHeight') - containermargin,
1442              controlheight = node.one('.controls').get('offsetHeight'),
1443              buffer = (dock.get('bufferPanel') * 3),
1444              possibleheight = dockheight - controlheight - buffer - (items.length * 2),
1445              totalheight = 0,
1446              id, dockedtitle;
1447          if (items.length > 0) {
1448              for (id in items) {
1449                  if (Y.Lang.isNumber(id) || Y.Lang.isString(id)) {
1450                      dockedtitle = Y.one(items[id].get('title')).ancestor('.' + CSS.dockedtitle);
1451                      if (dockedtitle) {
1452                          if (this.get('enabled')) {
1453                              dockedtitle.setStyle('height', 'auto');
1454                          }
1455                          totalheight += dockedtitle.get('offsetHeight') || 0;
1456                      }
1457                  }
1458              }
1459              if (totalheight > possibleheight) {
1460                  this.enable(possibleheight);
1461              }
1462          }
1463      },
1464      /**
1465       * Enables the dock sizer and resizes where required.
1466       * @method enable
1467       * @param {Number} possibleheight
1468       */
1469      enable: function(possibleheight) {
1470          var dock = this.get('dock'),
1471              items = dock.dockeditems,
1472              count = dock.count,
1473              runningcount = 0,
1474              usedheight = 0,
1475              id, itemtitle, itemheight, offsetheight;
1476          Y.log('Enabling the dock tab sizer.', 'debug', LOGNS);
1477          this.set('enabled', true);
1478          for (id in items) {
1479              if (Y.Lang.isNumber(id) || Y.Lang.isString(id)) {
1480                  itemtitle = Y.one(items[id].get('title')).ancestor('.' + CSS.dockedtitle);
1481                  if (!itemtitle) {
1482                      continue;
1483                  }
1484                  itemheight = Math.floor((possibleheight - usedheight) / (count - runningcount));
1485                  offsetheight = itemtitle.get('offsetHeight');
1486                  itemtitle.setStyle('overflow', 'hidden');
1487                  if (offsetheight > itemheight) {
1488                      itemtitle.setStyle('height', itemheight + 'px');
1489                      usedheight += itemheight;
1490                  } else {
1491                      usedheight += offsetheight;
1492                  }
1493                  runningcount++;
1494              }
1495          }
1496      }
1497  };
1498  Y.extend(TABHEIGHTMANAGER, Y.Base, TABHEIGHTMANAGER.prototype, {
1499      NAME: 'moodle-core-tabheightmanager',
1500      ATTRS: {
1501          /**
1502           * The dock.
1503           * @attribute dock
1504           * @type DOCK
1505           * @writeOnce
1506           */
1507          dock: {
1508              writeOnce: 'initOnly'
1509          },
1510          /**
1511           * True if the item_sizer is being used, false otherwise.
1512           * @attribute enabled
1513           * @type Bool
1514           */
1515          enabled: {
1516              value: false
1517          }
1518      }
1519  });
1520  /**
1521   * Dock JS.
1522   *
1523   * This file contains the action key event definition that is used for accessibility handling within the Dock.
1524   *
1525   * @module moodle-core-dock
1526   */
1527  
1528  /**
1529   * A 'dock:actionkey' Event.
1530   * The event consists of the left arrow, right arrow, enter and space keys.
1531   * More keys can be mapped to action meanings.
1532   * actions: collapse , expand, toggle, enter.
1533   *
1534   * This event is subscribed to by dockitems.
1535   * The on() method to subscribe allows specifying the desired trigger actions as JSON.
1536   *
1537   * This event can also be delegated if needed.
1538   *
1539   * @namespace M.core.dock
1540   * @class ActionKey
1541   */
1542  Y.Event.define("dock:actionkey", {
1543      // Webkit and IE repeat keydown when you hold down arrow keys.
1544      // Opera links keypress to page scroll; others keydown.
1545      // Firefox prevents page scroll via preventDefault() on either
1546      // keydown or keypress.
1547      _event: (Y.UA.webkit || Y.UA.ie) ? 'keydown' : 'keypress',
1548  
1549      /**
1550       * The keys to trigger on.
1551       * @property _keys
1552       */
1553      _keys: {
1554          // arrows
1555          '37': 'collapse',
1556          '39': 'expand',
1557          // (@todo: lrt/rtl/M.core_dock.cfg.orientation decision to assign arrow to meanings)
1558          '32': 'toggle',
1559          '13': 'enter'
1560      },
1561  
1562      /**
1563       * Handles key events
1564       * @method _keyHandler
1565       * @param {EventFacade} e
1566       * @param {SyntheticEvent.Notifier} notifier The notifier used to trigger the execution of subscribers
1567       * @param {Object} args
1568       */
1569      _keyHandler: function(e, notifier, args) {
1570          var actObj;
1571          if (!args.actions) {
1572              actObj = {collapse: true, expand: true, toggle: true, enter: true};
1573          } else {
1574              actObj = args.actions;
1575          }
1576          if (this._keys[e.keyCode] && actObj[this._keys[e.keyCode]]) {
1577              e.action = this._keys[e.keyCode];
1578              notifier.fire(e);
1579          }
1580      },
1581  
1582      /**
1583       * Subscribes to events.
1584       * @method on
1585       * @param {Node} node The node this subscription was applied to.
1586       * @param {Subscription} sub The object tracking this subscription.
1587       * @param {SyntheticEvent.Notifier} notifier The notifier used to trigger the execution of subscribers
1588       */
1589      on: function(node, sub, notifier) {
1590          // subscribe to _event and ask keyHandler to handle with given args[0] (the desired actions).
1591          if (sub.args === null) {
1592              // no actions given
1593              sub._detacher = node.on(this._event, this._keyHandler, this, notifier, {actions: false});
1594          } else {
1595              sub._detacher = node.on(this._event, this._keyHandler, this, notifier, sub.args[0]);
1596          }
1597      },
1598  
1599      /**
1600       * Detaches an event listener
1601       * @method detach
1602       * @param {Node} node The node this subscription was applied to.
1603       * @param {Subscription} sub The object tracking this subscription.
1604       * @param {SyntheticEvent.Notifier} notifier The notifier used to trigger the execution of subscribers
1605       */
1606      detach: function(node, sub) {
1607          // detach our _detacher handle of the subscription made in on()
1608          sub._detacher.detach();
1609      },
1610  
1611      /**
1612       * Creates a delegated event listener.
1613       * @method delegate
1614       * @param {Node} node The node this subscription was applied to.
1615       * @param {Subscription} sub The object tracking this subscription.
1616       * @param {SyntheticEvent.Notifier} notifier The notifier used to trigger the execution of subscribers
1617       * @param {String|function} filter Selector string or function that accpets an event object and returns null.
1618       */
1619      delegate: function(node, sub, notifier, filter) {
1620          // subscribe to _event and ask keyHandler to handle with given args[0] (the desired actions).
1621          if (sub.args === null) {
1622              // no actions given
1623              sub._delegateDetacher = node.delegate(this._event, this._keyHandler, filter, this, notifier, {actions: false});
1624          } else {
1625              sub._delegateDetacher = node.delegate(this._event, this._keyHandler, filter, this, notifier, sub.args[0]);
1626          }
1627      },
1628  
1629      /**
1630       * Detaches a delegated event listener.
1631       * @method detachDelegate
1632       * @param {Node} node The node this subscription was applied to.
1633       * @param {Subscription} sub The object tracking this subscription.
1634       * @param {SyntheticEvent.Notifier} notifier The notifier used to trigger the execution of subscribers
1635       * @param {String|function} filter Selector string or function that accpets an event object and returns null.
1636       */
1637      detachDelegate: function(node, sub) {
1638          sub._delegateDetacher.detach();
1639      }
1640  });
1641  /* global BLOCK, LOGNS, DOCKEDITEM */
1642  
1643  /**
1644   * Dock JS.
1645   *
1646   * This file contains the block class used to manage blocks (both docked and not) for the dock.
1647   *
1648   * @module moodle-core-dock
1649   */
1650  
1651  /**
1652   * Block.
1653   *
1654   * @namespace M.core.dock
1655   * @class Block
1656   * @constructor
1657   * @extends Base
1658   */
1659  BLOCK = function() {
1660      BLOCK.superclass.constructor.apply(this, arguments);
1661  };
1662  BLOCK.prototype = {
1663      /**
1664       * A content place holder used when the block has been docked.
1665       * @property contentplaceholder
1666       * @protected
1667       * @type Node
1668       */
1669      contentplaceholder: null,
1670      /**
1671       * The skip link associated with this block.
1672       * @property contentskipanchor
1673       * @protected
1674       * @type Node
1675       */
1676      contentskipanchor: null,
1677      /**
1678       * The cached content node for the actual block
1679       * @property cachedcontentnode
1680       * @protected
1681       * @type Node
1682       */
1683      cachedcontentnode: null,
1684      /**
1685       * If true the user preference isn't updated
1686       * @property skipsetposition
1687       * @protected
1688       * @type Boolean
1689       */
1690      skipsetposition: true,
1691      /**
1692       * The dock item associated with this block
1693       * @property dockitem
1694       * @protected
1695       * @type DOCKEDITEM
1696       */
1697      dockitem: null,
1698      /**
1699       * Called during the initialisation process of the object.
1700       * @method initializer
1701       */
1702      initializer: function() {
1703          var node = Y.one('#inst' + this.get('id'));
1704          if (!node) {
1705              return false;
1706          }
1707  
1708          Y.log('Initialised block with instance id:' + this.get('id'), 'debug', LOGNS);
1709  
1710          M.core.dock.ensureMoveToIconExists(node);
1711  
1712          // Move the block straight to the dock if required
1713          if (node.hasClass(CSS.dockonload)) {
1714              node.removeClass(CSS.dockonload);
1715              this.moveToDock();
1716          }
1717          this.skipsetposition = false;
1718          return true;
1719      },
1720      /**
1721       * Returns the class associated with this block.
1722       * @method _getBlockClass
1723       * @private
1724       * @param {Node} node
1725       * @return String
1726       */
1727      _getBlockClass: function(node) {
1728          var block = node.getData('block'),
1729              classes,
1730              matches;
1731          if (Y.Lang.isString(block) && block !== '') {
1732              return block;
1733          }
1734          classes = node.getAttribute('className').toString();
1735          matches = /(^| )block_([^ ]+)/.exec(classes);
1736          if (matches) {
1737              return matches[2];
1738          }
1739          return matches;
1740      },
1741  
1742      /**
1743       * This function is responsible for moving a block from the page structure onto the dock.
1744       * @method moveToDock
1745       * @param {EventFacade} e
1746       */
1747      moveToDock: function(e) {
1748          if (e) {
1749              e.halt(true);
1750          }
1751  
1752          var dock = M.core.dock.get(),
1753              id = this.get('id'),
1754              blockcontent = Y.one('#inst' + id).one('.content'),
1755              icon = (window.right_to_left()) ? 't/dock_to_block_rtl' : 't/dock_to_block',
1756              breakchar = (location.href.match(/\?/)) ? '&' : '?',
1757              blocktitle,
1758              blockcommands,
1759              movetoimg,
1760              moveto;
1761  
1762          if (!blockcontent) {
1763              return;
1764          }
1765  
1766          Y.log('Moving block to the dock:' + this.get('id'), 'debug', LOGNS);
1767  
1768          this.recordBlockState();
1769  
1770          blocktitle = this.cachedcontentnode.one('.title h2').cloneNode(true);
1771  
1772          // Build up the block commands.
1773          // These should not actually added to the DOM.
1774          blockcommands = this.cachedcontentnode.one('.title .commands');
1775          if (blockcommands) {
1776              blockcommands = blockcommands.cloneNode(true);
1777          } else {
1778              blockcommands = Y.Node.create('<div class="commands"></div>');
1779          }
1780          movetoimg = Y.Node.create('<img />').setAttrs({
1781              alt: Y.Escape.html(M.util.get_string('undockitem', 'block')),
1782              title: Y.Escape.html(M.util.get_string('undockblock', 'block', blocktitle.get('innerHTML'))),
1783              src: M.util.image_url(icon, 'moodle')
1784          });
1785          moveto = Y.Node.create('<a class="moveto customcommand requiresjs"></a>').setAttrs({
1786              href: Y.config.win.location.href + breakchar + 'dock=' + id
1787          });
1788          moveto.append(movetoimg);
1789          blockcommands.append(moveto.append(movetoimg));
1790  
1791          // Create a new dock item for the block
1792          this.dockitem = new DOCKEDITEM({
1793              block: this,
1794              dock: dock,
1795              blockinstanceid: id,
1796              title: blocktitle,
1797              contents: blockcontent,
1798              commands: blockcommands,
1799              blockclass: this._getBlockClass(Y.one('#inst' + id))
1800          });
1801          // Register an event so that when it is removed we can put it back as a block
1802          dock.add(this.dockitem);
1803  
1804          if (!this.skipsetposition) {
1805              // save the users preference
1806              M.util.set_user_preference('docked_block_instance_' + id, 1);
1807          }
1808  
1809          this.set('isDocked', true);
1810      },
1811      /**
1812       * Records the block state and adds it to the docks holding area.
1813       * @method recordBlockState
1814       */
1815      recordBlockState: function() {
1816          var id = this.get('id'),
1817              dock = M.core.dock.get(),
1818              node = Y.one('#inst' + id),
1819              skipanchor = node.previous();
1820          // Disable the skip anchor when docking
1821          if (skipanchor.hasClass('skip-block')) {
1822              this.contentskipanchor = skipanchor;
1823              this.contentskipanchor.hide();
1824          }
1825          this.cachedcontentnode = node;
1826          this.contentplaceholder = Y.Node.create('<div class="block_dock_placeholder"></div>');
1827          node.replace(this.contentplaceholder);
1828          dock.addToHoldingArea(node);
1829          node = null;
1830      },
1831  
1832      /**
1833       * This function removes a block from the dock and puts it back into the page structure.
1834       * @method returnToPage
1835       * @return {Boolean}
1836       */
1837      returnToPage: function() {
1838          var id = this.get('id');
1839  
1840          Y.log('Moving block out of the dock:' + this.get('id'), 'debug', LOGNS);
1841  
1842          // Enable the skip anchor when going back to block mode
1843          if (this.contentskipanchor) {
1844              this.contentskipanchor.show();
1845          }
1846  
1847          if (this.cachedcontentnode.one('.header')) {
1848              this.cachedcontentnode.one('.header').insert(this.dockitem.get('contents'), 'after');
1849          } else {
1850              this.cachedcontentnode.insert(this.dockitem.get('contents'));
1851          }
1852  
1853          this.contentplaceholder.replace(this.cachedcontentnode);
1854          this.cachedcontentnode = null;
1855  
1856          M.util.set_user_preference('docked_block_instance_' + id, 0);
1857          this.set('isDocked', false);
1858          return true;
1859      }
1860  };
1861  Y.extend(BLOCK, Y.Base, BLOCK.prototype, {
1862      NAME: 'moodle-core-dock-block',
1863      ATTRS: {
1864          /**
1865           * The block instance ID
1866           * @attribute id
1867           * @writeOnce
1868           * @type Number
1869           */
1870          id: {
1871              writeOnce: 'initOnly',
1872              setter: function(value) {
1873                  return parseInt(value, 10);
1874              }
1875          },
1876          /**
1877           * True if the block has been docked.
1878           * @attribute isDocked
1879           * @default false
1880           * @type Boolean
1881           */
1882          isDocked: {
1883              value: false
1884          }
1885      }
1886  });
1887  /* global LOGNS, DOCKEDITEM */
1888  
1889  /**
1890   * Dock JS.
1891   *
1892   * This file contains the docked item class.
1893   *
1894   * @module moodle-core-dock
1895   */
1896  
1897  /**
1898   * Docked item.
1899   *
1900   * @namespace M.core.dock
1901   * @class DockedItem
1902   * @constructor
1903   * @extends Base
1904   * @uses EventTarget
1905   */
1906  DOCKEDITEM = function() {
1907      DOCKEDITEM.superclass.constructor.apply(this, arguments);
1908  };
1909  DOCKEDITEM.prototype = {
1910      /**
1911       * Set to true if this item is currently being displayed.
1912       * @property active
1913       * @protected
1914       * @type Boolean
1915       */
1916      active: false,
1917      /**
1918       * Called during the initialisation process of the object.
1919       * @method initializer
1920       */
1921      initializer: function() {
1922          var title = this.get('title'),
1923              titlestring,
1924              type;
1925          /**
1926           * Fired before the docked item has been drawn.
1927           * @event dockeditem:drawstart
1928           */
1929          this.publish('dockeditem:drawstart', {prefix: 'dockeditem'});
1930          /**
1931           * Fired after the docked item has been drawn.
1932           * @event dockeditem:drawcomplete
1933           */
1934          this.publish('dockeditem:drawcomplete', {prefix: 'dockeditem'});
1935          /**
1936           * Fired before the docked item is to be shown.
1937           * @event dockeditem:showstart
1938           */
1939          this.publish('dockeditem:showstart', {prefix: 'dockeditem'});
1940          /**
1941           * Fired after the docked item has been shown.
1942           * @event dockeditem:showcomplete
1943           */
1944          this.publish('dockeditem:showcomplete', {prefix: 'dockeditem'});
1945          /**
1946           * Fired before the docked item has been hidden.
1947           * @event dockeditem:hidestart
1948           */
1949          this.publish('dockeditem:hidestart', {prefix: 'dockeditem'});
1950          /**
1951           * Fired after the docked item has been hidden.
1952           * @event dockeditem:hidecomplete
1953           */
1954          this.publish('dockeditem:hidecomplete', {prefix: 'dockeditem'});
1955          /**
1956           * Fired when the docked item is removed from the dock.
1957           * @event dockeditem:itemremoved
1958           */
1959          this.publish('dockeditem:itemremoved', {prefix: 'dockeditem'});
1960          if (title) {
1961              type = title.get('nodeName');
1962              titlestring = title.cloneNode(true);
1963              title = Y.Node.create('<' + type + '></' + type + '>');
1964              title = M.core.dock.fixTitleOrientation(title, titlestring.get('text'));
1965              this.set('title', title);
1966              this.set('titlestring', titlestring);
1967          }
1968          Y.log('Initialised dockeditem for block with title "' + this._getLogDescription(), 'debug', LOGNS);
1969      },
1970      /**
1971       * This function draws the item on the dock.
1972       * @method draw
1973       * @return Boolean
1974       */
1975      draw: function() {
1976          var create = Y.Node.create,
1977              dock = this.get('dock'),
1978              count = dock.count,
1979              docktitle,
1980              dockitem,
1981              closeicon,
1982              closeiconimg,
1983              id = this.get('id');
1984  
1985          this.fire('dockeditem:drawstart');
1986  
1987          docktitle = create('<div id="dock_item_' + id + '_title" role="menu" aria-haspopup="true" class="'
1988              + CSS.dockedtitle + '"></div>');
1989          docktitle.append(this.get('title'));
1990          dockitem = create('<div id="dock_item_' + id + '" class="' + CSS.dockeditem + '" tabindex="0" rel="' + id + '"></div>');
1991          if (count === 1) {
1992              dockitem.addClass('firstdockitem');
1993          }
1994          dockitem.append(docktitle);
1995          dock.append(dockitem);
1996  
1997          closeiconimg = create('<img alt="' + M.util.get_string('hidepanel', 'block') +
1998                  '" title="' + M.util.get_string('hidedockpanel', 'block') + '" />');
1999          closeiconimg.setAttribute('src', M.util.image_url('t/dockclose', 'moodle'));
2000          closeicon = create('<span class="hidepanelicon" tabindex="0"></span>').append(closeiconimg);
2001          closeicon.on('forceclose|click', this.hide, this);
2002          closeicon.on('dock:actionkey', this.hide, this, {actions: {enter: true, toggle: true}});
2003          this.get('commands').append(closeicon);
2004  
2005          this.set('dockTitleNode', docktitle);
2006          this.set('dockItemNode', dockitem);
2007  
2008          this.fire('dockeditem:drawcomplete');
2009          return true;
2010      },
2011      /**
2012       * This function toggles makes the item active and shows it.
2013       * @method show
2014       * @return Boolean
2015       */
2016      show: function() {
2017          var dock = this.get('dock'),
2018              panel = dock.getPanel(),
2019              docktitle = this.get('dockTitleNode');
2020  
2021          dock.hideActive();
2022          this.fire('dockeditem:showstart');
2023          Y.log('Showing ' + this._getLogDescription(), 'debug', LOGNS);
2024          panel.setHeader(this.get('titlestring'), this.get('commands'));
2025          panel.setBody(Y.Node.create('<div class="block_' + this.get('blockclass') + ' block_docked"></div>')
2026               .append(this.get('contents')));
2027          if (M.core.actionmenu !== undefined) {
2028              M.core.actionmenu.newDOMNode(panel.get('node'));
2029          }
2030          panel.show();
2031          panel.correctWidth();
2032  
2033          this.active = true;
2034          // Add active item class first up
2035          docktitle.addClass(CSS.activeitem);
2036          // Set aria-exapanded property to true.
2037          docktitle.set('aria-expanded', "true");
2038          this.fire('dockeditem:showcomplete');
2039          dock.resize();
2040          return true;
2041      },
2042      /**
2043       * This function hides the item and makes it inactive.
2044       * @method hide
2045       */
2046      hide: function() {
2047          this.fire('dockeditem:hidestart');
2048          Y.log('Hiding "' + this._getLogDescription(), 'debug', LOGNS);
2049          if (this.active) {
2050              // No longer active
2051              this.active = false;
2052              // Hide the panel
2053              this.get('dock').getPanel().hide();
2054          }
2055          // Remove the active class
2056          // Set aria-exapanded property to false
2057          this.get('dockTitleNode').removeClass(CSS.activeitem).set('aria-expanded', "false");
2058          this.fire('dockeditem:hidecomplete');
2059      },
2060      /**
2061       * A toggle between calling show and hide functions based on css.activeitem
2062       * Applies rules to key press events (dock:actionkey)
2063       * @method toggle
2064       * @param {String} action
2065       */
2066      toggle: function(action) {
2067          var docktitle = this.get('dockTitleNode');
2068          if (docktitle.hasClass(CSS.activeitem) && action !== 'expand') {
2069              this.hide();
2070          } else if (!docktitle.hasClass(CSS.activeitem) && action !== 'collapse') {
2071              this.show();
2072          }
2073      },
2074      /**
2075       * This function removes the node and destroys it's bits.
2076       * @method remove.
2077       */
2078      remove: function() {
2079          this.hide();
2080          // Return the block to its original position.
2081          this.get('block').returnToPage();
2082          // Remove the dock item node.
2083          this.get('dockItemNode').remove();
2084          this.fire('dockeditem:itemremoved');
2085      },
2086      /**
2087       * Returns the description of this item to use for log calls.
2088       * @method _getLogDescription
2089       * @private
2090       * @return {String}
2091       */
2092      _getLogDescription: function() {
2093          return this.get('titlestring').get('innerHTML') + ' (' + this.get('blockinstanceid') + ')';
2094      }
2095  };
2096  Y.extend(DOCKEDITEM, Y.Base, DOCKEDITEM.prototype, {
2097      NAME: 'moodle-core-dock-dockeditem',
2098      ATTRS: {
2099          /**
2100           * The block this docked item is associated with.
2101           * @attribute block
2102           * @type BLOCK
2103           * @writeOnce
2104           * @required
2105           */
2106          block: {
2107              writeOnce: 'initOnly'
2108          },
2109          /**
2110           * The dock itself.
2111           * @attribute dock
2112           * @type DOCK
2113           * @writeOnce
2114           * @required
2115           */
2116          dock: {
2117              writeOnce: 'initOnly'
2118          },
2119          /**
2120           * The docked item ID. This will be given by the dock.
2121           * @attribute id
2122           * @type Number
2123           */
2124          id: {},
2125          /**
2126           * Block instance id.Taken from the associated block.
2127           * @attribute blockinstanceid
2128           * @type Number
2129           * @writeOnce
2130           */
2131          blockinstanceid: {
2132              writeOnce: 'initOnly',
2133              setter: function(value) {
2134                  return parseInt(value, 10);
2135              }
2136          },
2137          /**
2138           * The title  nodeof the docked item.
2139           * @attribute title
2140           * @type Node
2141           * @default null
2142           */
2143          title: {
2144              value: null
2145          },
2146          /**
2147           * The title string.
2148           * @attribute titlestring
2149           * @type String
2150           */
2151          titlestring: {
2152              value: null
2153          },
2154          /**
2155           * The contents of the docked item
2156           * @attribute contents
2157           * @type Node
2158           * @writeOnce
2159           * @required
2160           */
2161          contents: {
2162              writeOnce: 'initOnly'
2163          },
2164          /**
2165           * Commands associated with the block.
2166           * @attribute commands
2167           * @type Node
2168           * @writeOnce
2169           * @required
2170           */
2171          commands: {
2172              writeOnce: 'initOnly'
2173          },
2174          /**
2175           * The block class.
2176           * @attribute blockclass
2177           * @type String
2178           * @writeOnce
2179           * @required
2180           */
2181          blockclass: {
2182              writeOnce: 'initOnly'
2183          },
2184          /**
2185           * The title node for the docked block.
2186           * @attribute dockTitleNode
2187           * @type Node
2188           */
2189          dockTitleNode: {
2190              value: null
2191          },
2192          /**
2193           * The item node for the docked block.
2194           * @attribute dockItemNode
2195           * @type Node
2196           */
2197          dockItemNode: {
2198              value: null
2199          },
2200          /**
2201           * The container for the docked item (will contain the block contents when visible)
2202           * @attribute dockcontainerNode
2203           * @type Node
2204           */
2205          dockcontainerNode: {
2206              value: null
2207          }
2208      }
2209  });
2210  Y.augment(DOCKEDITEM, Y.EventTarget);
2211  
2212  
2213  }, '@VERSION@', {
2214      "requires": [
2215          "base",
2216          "node",
2217          "event-custom",
2218          "event-mouseenter",
2219          "event-resize",
2220          "escape",
2221          "moodle-core-dock-loader",
2222          "moodle-core-event"
2223      ]
2224  });


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