[ Index ]

PHP Cross Reference of moodle-2.8

title

Body

[close]

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

   1  /**
   2   * Dock JS.
   3   *
   4   * This file contains the DOCK object and all dock related global namespace methods and properties.
   5   *
   6   * @module moodle-core-dock
   7   */
   8  
   9  
  10  var LOGNS = 'moodle-core-dock',
  11      BODY = Y.one(Y.config.doc.body),
  12      CSS = {
  13          dock: 'dock',                    // CSS Class applied to the dock box
  14          dockspacer: 'dockspacer',        // CSS class applied to the dockspacer
  15          controls: 'controls',            // CSS class applied to the controls box
  16          body: 'has_dock',                // CSS class added to the body when there is a dock
  17          buttonscontainer: 'buttons_container',
  18          dockeditem: 'dockeditem',        // CSS class added to each item in the dock
  19          dockeditemcontainer: 'dockeditem_container',
  20          dockedtitle: 'dockedtitle',      // CSS class added to the item's title in each dock
  21          activeitem: 'activeitem',        // CSS class added to the active item
  22          contentonly: 'content-only',
  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;
  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      BODY.delegate('click', M.core.dock.dockBlock, SELECTOR.blockmoveto);
  67      BODY.delegate('key', M.core.dock.dockBlock, SELECTOR.blockmoveto, 'enter');
  68  };
  69  
  70  /**
  71   * Returns an instance of the dock.
  72   * Initialises one if one hasn't already being initialised.
  73   *
  74   * @static
  75   * @method get
  76   * @return DOCK
  77   */
  78  M.core.dock.get = function() {
  79      if (this._dock === null) {
  80          this._dock = new DOCK();
  81      }
  82      return this._dock;
  83  };
  84  
  85  /**
  86   * Registers a dockable block with the dock.
  87   *
  88   * @static
  89   * @method registerDockableBlock
  90   * @param {int} id The block instance ID.
  91   * @return void
  92   */
  93  M.core.dock.registerDockableBlock = function(id) {
  94      if (typeof id === 'object' && typeof id.getData === 'function') {
  95          id = id.getData('instanceid');
  96      }
  97      M.core.dock._dockableblocks[id] = new BLOCK({id : id});
  98  };
  99  
 100  /**
 101   * Docks a block given either its instanceid, its node, or an event fired from within the block.
 102   * @static
 103   * @method dockBlockByInstanceID
 104   * @param id
 105   * @return void
 106   */
 107  M.core.dock.dockBlock = function(id) {
 108      if (typeof id === 'object' && id.target !== 'undefined') {
 109          id = id.target;
 110      }
 111      if (typeof id === "object") {
 112          if (!id.test(SELECTOR.dockableblock)) {
 113              id = id.ancestor(SELECTOR.dockableblock);
 114          }
 115          if (typeof id === 'object' && typeof id.getData === 'function' && !id.ancestor('.'+CSS.dock)) {
 116              id = id.getData('instanceid');
 117          } else {
 118              Y.log('Invalid instanceid given to dockBlockByInstanceID', 'warn', LOADERNAME);
 119              return;
 120          }
 121      }
 122      var block = M.core.dock._dockableblocks[id];
 123      if (block) {
 124          block.moveToDock();
 125      }
 126  };
 127  
 128  /**
 129   * Fixes the title orientation. Rotating it if required.
 130   *
 131   * @static
 132   * @method fixTitleOrientation
 133   * @param {Node} title The title node we are looking at.
 134   * @param {String} text The string to use as the title.
 135   * @return {Node} The title node to use.
 136   */
 137  M.core.dock.fixTitleOrientation = function(title, text) {
 138      var dock = M.core.dock.get(),
 139          fontsize = '11px',
 140          transform = 'rotate(270deg)',
 141          test,
 142          width,
 143          height,
 144          container;
 145      title = Y.one(title);
 146  
 147      if (dock.get('orientation') !== 'vertical') {
 148          // If the dock isn't vertical don't adjust it!
 149          title.set('innerHTML', text);
 150          return title;
 151      }
 152  
 153      if (Y.UA.ie > 0 && Y.UA.ie < 8) {
 154          // IE 6/7 can't rotate text so force ver
 155          M.str.langconfig.thisdirectionvertical = 'ver';
 156      }
 157  
 158      switch (M.str.langconfig.thisdirectionvertical) {
 159          case 'ver':
 160              // Stacked is easy
 161              return title.set('innerHTML', text.split('').join('<br />'));
 162          case 'ttb':
 163              transform = 'rotate(90deg)';
 164              break;
 165          case 'btt':
 166              // Nothing to do here. transform default is good.
 167              break;
 168      }
 169  
 170      if (Y.UA.ie === 8) {
 171          // IE8 can flip the text via CSS but not handle transform. IE9+ can handle the CSS3 transform attribute.
 172          title.set('innerHTML', text);
 173          title.setAttribute('style', 'writing-mode: tb-rl; filter: flipV flipH;display:inline;');
 174          title.addClass('filterrotate');
 175          return title;
 176      }
 177  
 178      // We need to fix a font-size - sorry theme designers.
 179      test = Y.Node.create('<h2 class="transform-test-heading"><span class="transform-test-node" style="font-size:'+fontsize+';">'+text+'</span></h2>');
 180      BODY.insert(test, 0);
 181      width = test.one('span').get('offsetWidth') * 1.2;
 182      height = test.one('span').get('offsetHeight');
 183      test.remove();
 184  
 185      title.set('innerHTML', text);
 186      title.addClass('css3transform');
 187  
 188      // Move the title into position
 189      title.setStyles({
 190          'position' : 'relative',
 191          'fontSize' : fontsize,
 192          'width' : width,
 193          'top' : (width - height)/2
 194      });
 195  
 196      // Positioning is different when in RTL mode.
 197      if (right_to_left()) {
 198          title.setStyle('left', width/2 - height);
 199      } else {
 200          title.setStyle('right', width/2 - height);
 201      }
 202  
 203      // Rotate the text
 204      title.setStyles({
 205          'transform' : transform,
 206          '-ms-transform' : transform,
 207          '-moz-transform' : transform,
 208          '-webkit-transform' : transform,
 209          '-o-transform' : transform
 210      });
 211  
 212      container = Y.Node.create('<div></div>');
 213      container.append(title);
 214      container.setStyles({
 215          height : width + (width / 4),
 216          position : 'relative'
 217      });
 218      return container;
 219  };
 220  
 221  /**
 222   * Informs the dock that the content of the block has changed.
 223   * This should be called by the blocks JS code if its content has been updated dynamically.
 224   * This method ensure the dock resizes if need be.
 225   *
 226   * @static
 227   * @method notifyBlockChange
 228   * @param {Number} instanceid
 229   * @return void
 230   */
 231  M.core.dock.notifyBlockChange = function(instanceid) {
 232      if (this._dock !== null) {
 233          var dock = M.core.dock.get(),
 234              activeitem = dock.getActiveItem();
 235          if (activeitem && activeitem.get('blockinstanceid') === parseInt(instanceid, 10)) {
 236              dock.resizePanelIfRequired();
 237          }
 238      }
 239  };
 240  
 241  /**
 242   * The Dock.
 243   *
 244   * @namespace M.core.dock
 245   * @class Dock
 246   * @constructor
 247   * @extends Base
 248   * @uses EventTarget
 249   */
 250  DOCK = function() {
 251      DOCK.superclass.constructor.apply(this, arguments);
 252  };
 253  DOCK.prototype = {
 254      /**
 255       * Tab height manager used to ensure tabs are always visible.
 256       * @protected
 257       * @property tabheightmanager
 258       * @type TABHEIGHTMANAGER
 259       */
 260      tabheightmanager : null,
 261      /**
 262       * Will be an eventtype if there is an eventype to prevent.
 263       * @protected
 264       * @property preventevent
 265       * @type String
 266       */
 267      preventevent : null,
 268      /**
 269       * Will be an object if there is a delayed event in effect.
 270       * @protected
 271       * @property delayedevent
 272       * @type {Object}
 273       */
 274      delayedevent : null,
 275      /**
 276       * An array of currently docked items.
 277       * @protected
 278       * @property dockeditems
 279       * @type Array
 280       */
 281      dockeditems : [],
 282      /**
 283       * Set to true once the dock has been drawn.
 284       * @protected
 285       * @property dockdrawn
 286       * @type Boolean
 287       */
 288      dockdrawn : false,
 289      /**
 290       * The number of blocks that are currently docked.
 291       * @protected
 292       * @property count
 293       * @type Number
 294       */
 295      count : 0,
 296      /**
 297       * The total number of blocks that have been docked.
 298       * @protected
 299       * @property totalcount
 300       * @type Number
 301       */
 302      totalcount : 0,
 303      /**
 304       * A hidden node used as a holding area for DOM objects used by blocks that have been docked.
 305       * @protected
 306       * @property holdingareanode
 307       * @type Node
 308       */
 309      holdingareanode : null,
 310      /**
 311       * Called during the initialisation process of the object.
 312       * @method initializer
 313       */
 314      initializer : function() {
 315          Y.log('Dock initialising', 'debug', LOGNS);
 316  
 317          // Publish the events the dock has
 318          /**
 319           * Fired when the dock first starts initialising.
 320           * @event dock:starting
 321           */
 322          this.publish('dock:starting', {prefix: 'dock',broadcast:  2,emitFacade: true, fireOnce:true});
 323          /**
 324           * Fired after the dock is initialised for the first time.
 325           * @event dock:initialised
 326           */
 327          this.publish('dock:initialised', {prefix: 'dock',broadcast:  2,emitFacade: true, fireOnce:true});
 328          /**
 329           * Fired before the dock structure and content is first created.
 330           * @event dock:beforedraw
 331           */
 332          this.publish('dock:beforedraw', {prefix:'dock', fireOnce:true});
 333          /**
 334           * Fired before the dock is changed from hidden to visible.
 335           * @event dock:beforeshow
 336           */
 337          this.publish('dock:beforeshow', {prefix:'dock'});
 338          /**
 339           * Fires after the dock has been changed from hidden to visible.
 340           * @event dock:shown
 341           */
 342          this.publish('dock:shown', {prefix:'dock', broadcast: 2});
 343          /**
 344           * Fired after the dock has been changed from visible to hidden.
 345           * @event dock:hidden
 346           */
 347          this.publish('dock:hidden', {prefix:'dock', broadcast: 2});
 348          /**
 349           * Fires when an item is added to the dock.
 350           * @event dock:itemadded
 351           */
 352          this.publish('dock:itemadded', {prefix:'dock'});
 353          /**
 354           * Fires when an item is removed from the dock.
 355           * @event dock:itemremoved
 356           */
 357          this.publish('dock:itemremoved', {prefix:'dock'});
 358          /**
 359           * Fires when a block is added or removed from the dock.
 360           * This happens after the itemadded and itemremoved events have been called.
 361           * @event dock:itemschanged
 362           */
 363          this.publish('dock:itemschanged', {prefix:'dock', broadcast: 2});
 364          /**
 365           * Fires once when the docks panel is first initialised.
 366           * @event dock:panelgenerated
 367           */
 368          this.publish('dock:panelgenerated', {prefix:'dock', fireOnce:true});
 369          /**
 370           * Fires when the dock panel is about to be resized.
 371           * @event dock:panelresizestart
 372           */
 373          this.publish('dock:panelresizestart', {prefix:'dock'});
 374          /**
 375           * Fires after the dock panel has been resized.
 376           * @event dock:resizepanelcomplete
 377           */
 378          this.publish('dock:resizepanelcomplete', {prefix:'dock'});
 379  
 380          // Apply theme customisations here before we do any real work.
 381          this._applyThemeCustomisation();
 382          // Inform everyone we are now about to initialise.
 383          this.fire('dock:starting');
 384          this._ensureDockDrawn();
 385          // Inform everyone the dock has been initialised
 386          this.fire('dock:initialised');
 387      },
 388      /**
 389       * Ensures that the dock has been drawn.
 390       * @private
 391       * @method _ensureDockDrawn
 392       * @return {Boolean}
 393       */
 394      _ensureDockDrawn : function() {
 395          if (this.dockdrawn === true) {
 396              return true;
 397          }
 398          var dock = this._initialiseDockNode(),
 399              clickargs = {
 400                  cssselector:'.'+CSS.dockedtitle,
 401                  delay:0
 402              },
 403              mouseenterargs = {
 404                  cssselector:'.'+CSS.dockedtitle,
 405                  delay:0.5,
 406                  iscontained:true,
 407                  preventevent:'click',
 408                  preventdelay:3
 409              };
 410          if (Y.UA.ie > 0 && Y.UA.ie < 7) {
 411              // Adjust for IE 6 (can't handle fixed pos)
 412              dock.setStyle('height', dock.get('winHeight')+'px');
 413          }
 414  
 415          this.fire('dock:beforedraw');
 416  
 417          this._initialiseDockControls();
 418  
 419          this.tabheightmanager = new TABHEIGHTMANAGER({dock:this});
 420  
 421          // Attach the required event listeners
 422          // We use delegate here as that way a handful of events are created for the dock
 423          // and all items rather than the same number for the dock AND every item individually
 424          Y.delegate('click', this.handleEvent, this.get('dockNode'), '.'+CSS.dockedtitle, this, clickargs);
 425          Y.delegate('mouseenter', this.handleEvent, this.get('dockNode'), '.'+CSS.dockedtitle, this, mouseenterargs);
 426          this.get('dockNode').on('mouseleave', this.handleEvent, this, {cssselector:'#dock', delay:0.5, iscontained:false});
 427  
 428          Y.delegate('click', this.handleReturnToBlock, this.get('dockNode'), SELECTOR.panelmoveto, this);
 429          Y.delegate('click', this.handleReturnToBlock, this.get('dockNode'), SELECTOR.panelmoveto, this);
 430          Y.delegate('dock:actionkey', this.handleDockedItemEvent, this.get('dockNode'), '.'+CSS.dockeditem, this);
 431  
 432          BODY.on('click', this.handleEvent, this,  {cssselector:'body', delay:0});
 433          this.on('dock:itemschanged', this.resizeBlockSpace, this);
 434          this.on('dock:itemschanged', this.checkDockVisibility, this);
 435          this.on('dock:itemschanged', this.resetFirstItem, this);
 436          this.dockdrawn = true;
 437          return true;
 438      },
 439      /**
 440       * Handles an actionkey event on the dock.
 441       * @param {EventFacade} e
 442       * @method handleDockedItemEvent
 443       * @return {Boolean}
 444       */
 445      handleDockedItemEvent : function(e) {
 446          if (e.type !== 'dock:actionkey') {
 447              return false;
 448          }
 449          var target = e.target,
 450              dockeditem = '.'+CSS.dockeditem;
 451          if (!target.test(dockeditem)) {
 452              target = target.ancestor(dockeditem);
 453          }
 454          if (!target) {
 455              return false;
 456          }
 457          e.halt();
 458          this.dockeditems[target.getAttribute('rel')].toggle(e.action);
 459      },
 460      /**
 461       * Call the theme customisation method "customise_dock_for_theme" if it exists.
 462       * @private
 463       * @method _applyThemeCustomisation
 464       */
 465      _applyThemeCustomisation : function() {
 466          // Check if there is a customisation function
 467          if (typeof(customise_dock_for_theme) === 'function') {
 468              // First up pre the legacy object.
 469              M.core_dock = this;
 470              M.core_dock.cfg = {
 471                  buffer : null,
 472                  orientation : null,
 473                  position : null,
 474                  spacebeforefirstitem : null,
 475                  removeallicon : null
 476              };
 477              M.core_dock.css = {
 478                  dock : null,
 479                  dockspacer : null,
 480                  controls : null,
 481                  body : null,
 482                  buttonscontainer : null,
 483                  dockeditem : null,
 484                  dockeditemcontainer : null,
 485                  dockedtitle : null,
 486                  activeitem : null
 487              };
 488              try {
 489                  // Run the customisation function
 490                  customise_dock_for_theme(this);
 491              } catch (exception) {
 492                  // Do nothing at the moment.
 493                  Y.log('Exception while attempting to apply theme customisations.', 'error', LOGNS);
 494              }
 495              // Now to work out what they did.
 496              var key, value,
 497                  warned = false,
 498                  cfgmap = {
 499                      buffer : 'bufferPanel',
 500                      orientation : 'orientation',
 501                      position : 'position',
 502                      spacebeforefirstitem : 'bufferBeforeFirstItem',
 503                      removeallicon : 'undockAllIconUrl'
 504                  };
 505              // Check for and apply any legacy configuration.
 506              for (key in M.core_dock.cfg) {
 507                  if (Y.Lang.isString(key) && cfgmap[key]) {
 508                      value = M.core_dock.cfg[key];
 509                      if (value === null) {
 510                          continue;
 511                      }
 512                      if (!warned) {
 513                          Y.log('Warning: customise_dock_for_theme has changed. Please update your code.', 'warn', LOGNS);
 514                          warned = true;
 515                      }
 516                      // Damn, the've set something.
 517                      Y.log('Note for customise_dock_for_theme code: M.core_dock.cfg.'+key+' is now dock.set(\''+key+'\', value)', 'debug', LOGNS);
 518                      this.set(cfgmap[key], value);
 519                  }
 520              }
 521              // Check for and apply any legacy CSS changes..
 522              for (key in M.core_dock.css) {
 523                  if (Y.Lang.isString(key)) {
 524                      value = M.core_dock.css[key];
 525                      if (value === null) {
 526                          continue;
 527                      }
 528                      if (!warned) {
 529                          Y.log('Warning: customise_dock_for_theme has changed. Please update your code.', 'warn', LOGNS);
 530                          warned = true;
 531                      }
 532                      // Damn, they've set something.
 533                      Y.log('Note for customise_dock_for_theme code: M.core_dock.css.'+key+' is now CSS.'+key+' = value', 'debug', LOGNS);
 534                      CSS[key] = value;
 535                  }
 536              }
 537          }
 538      },
 539      /**
 540       * Initialises the dock node, creating it and its content if required.
 541       *
 542       * @private
 543       * @method _initialiseDockNode
 544       * @return {Node} The dockNode
 545       */
 546      _initialiseDockNode : function() {
 547          var dock = this.get('dockNode'),
 548              positionorientationclass = CSS.dock+'_'+this.get('position')+'_'+this.get('orientation'),
 549              holdingarea = Y.Node.create('<div></div>').setStyles({display:'none'}),
 550              buttons = this.get('buttonsNode'),
 551              container = this.get('itemContainerNode');
 552  
 553          if (!dock) {
 554              dock = Y.one('#'+CSS.dock);
 555          }
 556          if (!dock) {
 557              dock = Y.Node.create('<div id="'+CSS.dock+'"></div>');
 558              BODY.append(dock);
 559          }
 560          dock.setAttribute('role', 'menubar').addClass(positionorientationclass);
 561          if (Y.all(SELECTOR.dockonload).size() === 0) {
 562              // Nothing on the dock... hide it using CSS
 563              dock.addClass('nothingdocked');
 564          } else {
 565              positionorientationclass = CSS.body+'_'+this.get('position')+'_'+this.get('orientation');
 566              BODY.addClass(CSS.body).addClass();
 567          }
 568  
 569          if (!buttons) {
 570              buttons = dock.one('.'+CSS.buttonscontainer);
 571          }
 572          if (!buttons) {
 573              buttons = Y.Node.create('<div class="'+CSS.buttonscontainer+'"></div>');
 574              dock.append(buttons);
 575          }
 576  
 577          if (!container) {
 578              container = dock.one('.'+CSS.dockeditemcontainer);
 579          }
 580          if (!container) {
 581              container = Y.Node.create('<div class="'+CSS.dockeditemcontainer+'"></div>');
 582              buttons.append(container);
 583          }
 584  
 585          BODY.append(holdingarea);
 586          this.holdingareanode = holdingarea;
 587  
 588          this.set('dockNode', dock);
 589          this.set('buttonsNode', buttons);
 590          this.set('itemContainerNode', container);
 591  
 592          return dock;
 593      },
 594      /**
 595       * Initialises the dock controls.
 596       *
 597       * @private
 598       * @method _initialiseDockControls
 599       */
 600      _initialiseDockControls : function() {
 601          // Add a removeall button
 602          // Must set the image src seperatly of we get an error with XML strict headers
 603  
 604          var removeall = Y.Node.create('<img alt="'+M.util.get_string('undockall', 'block')+'" tabindex="0" />');
 605          removeall.setAttribute('src',this.get('undockAllIconUrl'));
 606          removeall.on('removeall|click', this.removeAll, this);
 607          removeall.on('dock:actionkey', this.removeAll, this, {actions:{enter:true}});
 608          this.get('buttonsNode').append(Y.Node.create('<div class="'+CSS.controls+'"></div>').append(removeall));
 609      },
 610      /**
 611       * Returns the dock panel. Initialising it if it hasn't already been initialised.
 612       * @method getPanel
 613       * @return {DOCKPANEL}
 614       */
 615      getPanel : function() {
 616          var panel = this.get('panel');
 617          if (!panel) {
 618              panel = new DOCKPANEL({dock:this});
 619              panel.on('panel:visiblechange', this.resize, this);
 620              Y.on('windowresize', this.resize, this);
 621              // Initialise the dockpanel .. should only happen once
 622              this.set('panel', panel);
 623              this.fire('dock:panelgenerated');
 624          }
 625          return panel;
 626      },
 627      /**
 628       * Resizes the dock panel if required.
 629       * @method resizePanelIfRequired
 630       */
 631      resizePanelIfRequired : function() {
 632          this.resize();
 633          var panel = this.get('panel');
 634          if (panel) {
 635              panel.correctWidth();
 636          }
 637      },
 638      /**
 639       * Handles a dock event sending it to the right place.
 640       *
 641       * @method handleEvent
 642       * @param {EventFacade} e
 643       * @param {Object} options
 644       * @return {Boolean}
 645       */
 646      handleEvent : function(e, options) {
 647          var item = this.getActiveItem(),
 648              target,
 649              targetid,
 650              regex = /^dock_item_(\d+)_title$/,
 651              self = this;
 652          if (options.cssselector === 'body') {
 653              if (!this.get('dockNode').contains(e.target)) {
 654                  if (item) {
 655                      item.hide();
 656                  }
 657              }
 658          } else {
 659              if (e.target.test(options.cssselector)) {
 660                  target = e.target;
 661              } else {
 662                  target = e.target.ancestor(options.cssselector);
 663              }
 664              if (!target) {
 665                  return true;
 666              }
 667              if (this.preventevent !== null && e.type === this.preventevent) {
 668                  return true;
 669              }
 670              if (options.preventevent) {
 671                  this.preventevent = options.preventevent;
 672                  if (options.preventdelay) {
 673                      setTimeout(function(){
 674                          self.preventevent = null;
 675                      }, options.preventdelay * 1000);
 676                  }
 677              }
 678              if (this.delayedevent && this.delayedevent.timeout) {
 679                  clearTimeout(this.delayedevent.timeout);
 680                  this.delayedevent.event.detach();
 681                  this.delayedevent = null;
 682              }
 683              if (options.delay > 0) {
 684                  return this.delayEvent(e, options, target);
 685              }
 686              targetid = target.get('id');
 687              if (targetid.match(regex)) {
 688                  item = this.dockeditems[targetid.replace(regex, '$1')];
 689                  if (item.active) {
 690                      item.hide();
 691                  } else {
 692                      item.show();
 693                  }
 694              } else if (item) {
 695                  item.hide();
 696              }
 697          }
 698          return true;
 699      },
 700      /**
 701       * Delays an event.
 702       *
 703       * @method delayEvent
 704       * @param {EventFacade} event
 705       * @param {Object} options
 706       * @param {Node} target
 707       * @return {Boolean}
 708       */
 709      delayEvent : function(event, options, target) {
 710          var self = this;
 711          self.delayedevent = (function(){
 712              return {
 713                  target : target,
 714                  event : BODY.on('mousemove', function(e){
 715                      self.delayedevent.target = e.target;
 716                  }),
 717                  timeout : null
 718              };
 719          })(self);
 720          self.delayedevent.timeout = setTimeout(function(){
 721              self.delayedevent.timeout = null;
 722              self.delayedevent.event.detach();
 723              if (options.iscontained === self.get('dockNode').contains(self.delayedevent.target)) {
 724                  self.handleEvent(event, {cssselector:options.cssselector, delay:0, iscontained:options.iscontained});
 725              }
 726          }, options.delay*1000);
 727          return true;
 728      },
 729      /**
 730       * Resizes block spaces.
 731       * @method resizeBlockSpace
 732       */
 733      resizeBlockSpace : function() {
 734          if (Y.all(SELECTOR.dockonload).size() > 0) {
 735              // Do not resize during initial load
 736              return;
 737          }
 738          var blockregions = [],
 739              populatedblockregions = 0,
 740              allnewregions = true,
 741              showregions = false,
 742              i;
 743          // First look for understood regions.
 744          Y.all(SELECTOR.blockregion).each(function(region){
 745              var regionname = region.getData('blockregion');
 746              if (region.all('.block').size() > 0) {
 747                  populatedblockregions++;
 748                  BODY.addClass('used-region-'+regionname);
 749                  BODY.removeClass('empty-region-'+regionname);
 750                  BODY.removeClass('docked-region-'+regionname);
 751              } else if (region.all('.block_dock_placeholder').size() > 0) {
 752                  // There are no blocks in the region but there are placeholders.
 753                  // All blocks in this region have been docked.
 754                  BODY.addClass('empty-region-'+regionname);
 755                  BODY.addClass('docked-region-'+regionname);
 756                  BODY.removeClass('used-region-'+regionname);
 757              }
 758          });
 759          // Next check for legacy regions.
 760          Y.all('.block-region').each(function(region){
 761              if (region.test(SELECTOR.blockregion)) {
 762                  // This is a new region, we've already processed it.
 763                  return;
 764              }
 765              var hasblocks = (region.all('.block').size() > 0);
 766              if (hasblocks) {
 767                  populatedblockregions++;
 768              }
 769              allnewregions = false;
 770              blockregions[region.get('id')] = {
 771                  hasblocks : hasblocks,
 772                  bodyclass : region.get('id').replace(/^region\-/, 'side-')+'-only'
 773              };
 774          });
 775          if (BODY.hasClass('blocks-moving')) {
 776              // open up blocks during blocks positioning
 777              showregions = true;
 778          }
 779          if (populatedblockregions === 0 && showregions === false) {
 780              BODY.addClass(CSS.contentonly);
 781          } else {
 782              BODY.removeClass(CSS.contentonly);
 783          }
 784  
 785          if (!allnewregions) {
 786              if (populatedblockregions === 0 && showregions === false) {
 787                  for (i in blockregions) {
 788                      if (blockregions[i].bodyclass) {
 789                          BODY.removeClass(blockregions[i].bodyclass);
 790                      }
 791                  }
 792              } else if (populatedblockregions === 1 && showregions === false) {
 793                  for (i in blockregions) {
 794                      if (blockregions[i].bodyclass) {
 795                          if (!blockregions[i].hasblocks) {
 796                              BODY.removeClass(blockregions[i].bodyclass);
 797                          } else {
 798                              BODY.addClass(blockregions[i].bodyclass);
 799                          }
 800                      }
 801                  }
 802              } else {
 803                  for (i in blockregions) {
 804                      if (blockregions[i].bodyclass) {
 805                          BODY.removeClass(blockregions[i].bodyclass);
 806                      }
 807                  }
 808              }
 809          }
 810      },
 811      /**
 812       * Adds an item to the dock.
 813       * @method add
 814       * @param {DOCKEDITEM} item
 815       */
 816      add : function(item) {
 817          // Set the dockitem id to the total count and then increment it.
 818          item.set('id', this.totalcount);
 819          Y.log('Adding block '+item._getLogDescription()+' to the dock.', 'debug', LOGNS);
 820          this.count++;
 821          this.totalcount++;
 822          this.dockeditems[item.get('id')] = item;
 823          this.dockeditems[item.get('id')].draw();
 824          this.fire('dock:itemadded', item);
 825          this.fire('dock:itemschanged', item);
 826      },
 827      /**
 828       * Appends an item to the dock (putting it in the item container.
 829       * @method append
 830       * @param {Node} docknode
 831       */
 832      append : function(docknode) {
 833          this.get('itemContainerNode').append(docknode);
 834      },
 835      /**
 836       * Handles events that require a docked block to be returned to the page./
 837       * @method handleReturnToBlock
 838       * @param {EventFacade} e
 839       */
 840      handleReturnToBlock : function(e) {
 841          e.halt();
 842          this.remove(this.getActiveItem().get('id'));
 843      },
 844      /**
 845       * Removes a docked item from the dock.
 846       * @method remove
 847       * @param {Number} id The docked item id.
 848       * @return {Boolean}
 849       */
 850      remove : function(id) {
 851          if (!this.dockeditems[id]) {
 852              return false;
 853          }
 854          Y.log('Removing block '+this.dockeditems[id]._getLogDescription()+' from the dock.', 'debug', LOGNS);
 855          this.dockeditems[id].remove();
 856          delete this.dockeditems[id];
 857          this.count--;
 858          this.fire('dock:itemremoved', id);
 859          this.fire('dock:itemschanged', id);
 860          return true;
 861      },
 862      /**
 863       * Ensures the the first item in the dock has the correct class.
 864       * @method resetFirstItem
 865       */
 866      resetFirstItem : function() {
 867          this.get('dockNode').all('.'+CSS.dockeditem+'.firstdockitem').removeClass('firstdockitem');
 868          if (this.get('dockNode').one('.'+CSS.dockeditem)) {
 869              this.get('dockNode').one('.'+CSS.dockeditem).addClass('firstdockitem');
 870          }
 871      },
 872      /**
 873       * Removes all docked blocks returning them to the page.
 874       * @method removeAll
 875       * @return {Boolean}
 876       */
 877      removeAll : function() {
 878          Y.log('Undocking all '+this.dockeditems.length+' blocks', 'debug', LOGNS);
 879          var i;
 880          for (i in this.dockeditems) {
 881              if (Y.Lang.isNumber(i) || Y.Lang.isString(i)) {
 882                  this.remove(i);
 883              }
 884          }
 885          return true;
 886      },
 887      /**
 888       * Hides the active item.
 889       * @method hideActive
 890       */
 891      hideActive : function() {
 892          var item = this.getActiveItem();
 893          if (item) {
 894              item.hide();
 895          }
 896      },
 897      /**
 898       * Checks wether the dock should be shown or hidden
 899       * @method checkDockVisibility
 900       */
 901      checkDockVisibility : function() {
 902          var bodyclass = CSS.body+'_'+this.get('position')+'_'+this.get('orientation');
 903          if (!this.count) {
 904              this.get('dockNode').addClass('nothingdocked');
 905              BODY.removeClass(CSS.body).removeClass();
 906              this.fire('dock:hidden');
 907          } else {
 908              this.fire('dock:beforeshow');
 909              this.get('dockNode').removeClass('nothingdocked');
 910              BODY.addClass(CSS.body).addClass(bodyclass);
 911              this.fire('dock:shown');
 912          }
 913      },
 914      /**
 915       * This function checks the size and position of the panel and moves/resizes if
 916       * required to keep it within the bounds of the window.
 917       * @method resize
 918       * @return {Boolean}
 919       */
 920      resize : function() {
 921          var panel = this.getPanel(),
 922              item = this.getActiveItem(),
 923              buffer,
 924              screenh,
 925              docky,
 926              titletop,
 927              containery,
 928              containerheight,
 929              scrolltop,
 930              panelheight,
 931              dockx,
 932              titleleft;
 933          if (!panel.get('visible') || !item) {
 934              return true;
 935          }
 936  
 937          this.fire('dock:panelresizestart');
 938          if (this.get('orientation') === 'vertical') {
 939              buffer = this.get('bufferPanel');
 940              screenh = parseInt(BODY.get('winHeight'), 10)-(buffer*2);
 941              docky = this.get('dockNode').getY();
 942              titletop = item.get('dockTitleNode').getY()-docky-buffer;
 943              containery = this.get('itemContainerNode').getY();
 944              containerheight = containery-docky+this.get('buttonsNode').get('offsetHeight');
 945              scrolltop = panel.get('bodyNode').get('scrollTop');
 946              panel.get('bodyNode').setStyle('height', 'auto');
 947              panel.get('node').removeClass('oversized_content');
 948              panelheight = panel.get('node').get('offsetHeight');
 949  
 950              if (Y.UA.ie > 0 && Y.UA.ie < 7) {
 951                  panel.setTop(item.get('dockTitleNode').getY());
 952              } else if (panelheight > screenh) {
 953                  panel.setTop(buffer-containerheight);
 954                  panel.get('bodyNode').setStyle('height', (screenh-panel.get('headerNode').get('offsetHeight'))+'px');
 955                  panel.get('node').addClass('oversized_content');
 956              } else if (panelheight > (screenh-(titletop-buffer))) {
 957                  panel.setTop(titletop-containerheight-(panelheight - (screenh-titletop))+buffer);
 958              } else {
 959                  panel.setTop(titletop-containerheight+buffer);
 960              }
 961  
 962              if (scrolltop) {
 963                  panel.get('bodyNode').set('scrollTop', scrolltop);
 964              }
 965          }
 966  
 967          if (this.get('position') === 'right') {
 968              panel.get('node').setStyle('left', '-' + panel.get('node').get('offsetWidth') + 'px');
 969  
 970          } else if (this.get('position') === 'top') {
 971              dockx = this.get('dockNode').getX();
 972              titleleft = item.get('dockTitleNode').getX()-dockx;
 973              panel.get('node').setStyle('left', titleleft+'px');
 974          }
 975  
 976          this.fire('dock:resizepanelcomplete');
 977          return true;
 978      },
 979      /**
 980       * Returns the currently active dock item or false
 981       * @method getActiveItem
 982       * @return {DOCKEDITEM}
 983       */
 984      getActiveItem : function() {
 985          var i;
 986          for (i in this.dockeditems) {
 987              if (this.dockeditems[i].active) {
 988                  return this.dockeditems[i];
 989              }
 990          }
 991          return false;
 992      },
 993      /**
 994       * Adds an item to the holding area.
 995       * @method addToHoldingArea
 996       * @param {Node} node
 997       */
 998      addToHoldingArea : function(node) {
 999          this.holdingareanode.append(node);
1000      }
1001  };
1002  
1003  Y.extend(DOCK, Y.Base, DOCK.prototype, {
1004      NAME : 'moodle-core-dock',
1005      ATTRS : {
1006          /**
1007           * The dock itself. #dock.
1008           * @attribute dockNode
1009           * @type Node
1010           * @writeOnce
1011           */
1012          dockNode : {
1013              writeOnce : true
1014          },
1015          /**
1016           * The docks panel.
1017           * @attribute panel
1018           * @type DOCKPANEL
1019           * @writeOnce
1020           */
1021          panel : {
1022              writeOnce : true
1023          },
1024          /**
1025           * A container within the dock used for buttons.
1026           * @attribute buttonsNode
1027           * @type Node
1028           * @writeOnce
1029           */
1030          buttonsNode : {
1031              writeOnce : true
1032          },
1033          /**
1034           * A container within the dock used for docked blocks.
1035           * @attribute itemContainerNode
1036           * @type Node
1037           * @writeOnce
1038           */
1039          itemContainerNode : {
1040              writeOnce : true
1041          },
1042  
1043          /**
1044           * Buffer used when containing a panel.
1045           * @attribute bufferPanel
1046           * @type Number
1047           * @default 10
1048           */
1049          bufferPanel : {
1050              value : 10,
1051              validator : Y.Lang.isNumber
1052          },
1053  
1054          /**
1055           * Position of the dock.
1056           * @attribute position
1057           * @type String
1058           * @default left
1059           */
1060          position : {
1061              value : 'left',
1062              validator : Y.Lang.isString
1063          },
1064  
1065          /**
1066           * vertical || horizontal determines if we change the title
1067           * @attribute orientation
1068           * @type String
1069           * @default vertical
1070           */
1071          orientation : {
1072              value : 'vertical',
1073              validator : Y.Lang.isString,
1074              setter : function(value) {
1075                  if (value.match(/^vertical$/i)) {
1076                      return 'vertical';
1077                  }
1078                  return 'horizontal';
1079              }
1080          },
1081  
1082          /**
1083           * Space between the top of the dock and the first item.
1084           * @attribute bufferBeforeFirstItem
1085           * @type Number
1086           * @default 10
1087           */
1088          bufferBeforeFirstItem : {
1089              value : 10,
1090              validator : Y.Lang.isNumber
1091          },
1092  
1093          /**
1094           * Icon URL for the icon to undock all blocks
1095           * @attribute undockAllIconUrl
1096           * @type String
1097           * @default t/dock_to_block
1098           */
1099          undockAllIconUrl : {
1100              value : M.util.image_url((right_to_left()) ? 't/dock_to_block_rtl' : 't/dock_to_block', 'moodle'),
1101              validator : Y.Lang.isString
1102          }
1103      }
1104  });
1105  Y.augment(DOCK, Y.EventTarget);


Generated: Fri Nov 28 20:29:05 2014 Cross-referenced by PHPXref 0.7.1