2 * The dock namespace: Contains all things dock related
6 count : 0, // The number of dock items currently
7 totalcount : 0, // The number of dock items through the page life
8 items : [], // An array of dock items
9 earlybinds : [], // Events added before the dock was augmented to support events
10 Y : null, // The YUI instance to use with dock related code
11 initialised : false, // True once thedock has been initialised
12 delayedevent : null, // Will be an object if there is a delayed event in effect
13 preventevent : null, // Will be an eventtype if there is an eventyoe to prevent
17 * Namespace containing the nodes that relate to the dock
21 dock : null, // The dock itself
22 body : null, // The body of the page
23 panel : null // The docks panel
26 * Configuration parameters used during the initialisation and setup
27 * of dock and dock items.
28 * This is here specifically so that themers can override core parameters and
29 * design aspects without having to re-write navigation
33 buffer:10, // Buffer used when containing a panel
34 position:'left', // position of the dock
35 orientation:'vertical', // vertical || horizontal determines if we change the title
36 spacebeforefirstitem: 10, // Space between the top of the dock and the first item
37 removeallicon: M.util.image_url('t/dock_to_block', 'moodle')
40 * CSS classes to use with the dock
44 dock:'dock', // CSS Class applied to the dock box
45 dockspacer:'dockspacer', // CSS class applied to the dockspacer
46 controls:'controls', // CSS class applied to the controls box
47 body:'has_dock', // CSS class added to the body when there is a dock
48 dockeditem:'dockeditem', // CSS class added to each item in the dock
49 dockeditemcontainer:'dockeditem_container',
50 dockedtitle:'dockedtitle', // CSS class added to the item's title in each dock
51 activeitem:'activeitem' // CSS class added to the active item
54 * Augments the classes as required and processes early bindings
56 M.core_dock.init = function(Y) {
57 if (this.initialised) {
61 this.initialised = true;
63 this.nodes.body = Y.one(document.body);
65 // Give the dock item class the event properties/methods
66 Y.augment(this.item, Y.EventTarget);
67 Y.augment(this, Y.EventTarget, true);
69 // Publish the events the dock has
70 this.publish('dock:beforedraw', {prefix:'dock'});
71 this.publish('dock:beforeshow', {prefix:'dock'});
72 this.publish('dock:shown', {prefix:'dock'});
73 this.publish('dock:hidden', {prefix:'dock'});
74 this.publish('dock:initialised', {prefix:'dock'});
75 this.publish('dock:itemadded', {prefix:'dock'});
76 this.publish('dock:itemremoved', {prefix:'dock'});
77 this.publish('dock:itemschanged', {prefix:'dock'});
78 this.publish('dock:panelgenerated', {prefix:'dock'});
79 this.publish('dock:panelresizestart', {prefix:'dock'});
80 this.publish('dock:resizepanelcomplete', {prefix:'dock'});
81 this.publish('dock:starting', {prefix: 'dock',broadcast: 2,emitFacade: true});
82 this.fire('dock:starting');
83 // Re-apply early bindings properly now that we can
85 // Check if there is a customisation function
86 if (typeof(customise_dock_for_theme) === 'function') {
88 // Run the customisation function
89 customise_dock_for_theme();
91 // Do nothing at the moment
95 var dock = Y.one('#dock');
97 // Start the construction of the dock
98 dock = Y.Node.create('<div id="dock" class="'+css.dock+' '+css.dock+'_'+this.cfg.position+'_'+this.cfg.orientation+'"></div>')
99 .append(Y.Node.create('<div class="'+css.dockeditemcontainer+'"></div>'));
100 this.nodes.body.append(dock);
102 dock.addClass(css.dock+'_'+this.cfg.position+'_'+this.cfg.orientation);
104 this.holdingarea = Y.Node.create('<div></div>').setStyles({display:'none'});
105 this.nodes.body.append(this.holdingarea);
106 if (Y.UA.ie > 0 && Y.UA.ie < 7) {
107 // Adjust for IE 6 (can't handle fixed pos)
108 dock.setStyle('height', dock.get('winHeight')+'px');
111 this.nodes.dock = dock;
112 this.nodes.container = dock.one('.'+css.dockeditemcontainer);
114 if (Y.all('.block.dock_on_load').size() == 0) {
115 // Nothing on the dock... hide it using CSS
116 dock.addClass('nothingdocked');
118 this.nodes.body.addClass(this.css.body);
121 this.fire('dock:beforedraw');
123 // Add a removeall button
124 // Must set the image src seperatly of we get an error with XML strict headers
125 var removeall = Y.Node.create('<img alt="'+M.str.block.undockall+'" title="'+M.str.block.undockall+'" />');
126 removeall.setAttribute('src',this.cfg.removeallicon);
127 removeall.on('removeall|click', this.remove_all, this);
128 dock.appendChild(Y.Node.create('<div class="'+css.controls+'"></div>').append(removeall));
130 // Create a manager for the height of the tabs. Once set this can be forgotten about
133 enabled : false, // True if the item_sizer is being used, false otherwise
135 * Initialises the dock sizer which then attaches itself to the required
136 * events in order to monitor the dock
140 M.core_dock.on('dock:itemschanged', this.checkSizing, this);
141 Y.on('windowresize', this.checkSizing, this);
144 * Check if the size dock items needs to be adjusted
146 checkSizing : function() {
147 var dock = M.core_dock;
148 var possibleheight = dock.nodes.dock.get('offsetHeight') - dock.nodes.dock.one('.controls').get('offsetHeight') - (dock.cfg.buffer*3) - (dock.items.length*2);
150 for (var id in dock.items) {
151 var dockedtitle = Y.one(dock.items[id].title).ancestor('.'+dock.css.dockedtitle);
154 dockedtitle.setStyle('height', 'auto');
156 totalheight += dockedtitle.get('offsetHeight') || 0;
159 if (totalheight > possibleheight) {
160 this.enable(possibleheight);
164 * Enables the dock sizer and resizes where required.
166 enable : function(possibleheight) {
167 var dock = M.core_dock;
168 var runningcount = 0;
171 for (var id in dock.items) {
172 var itemtitle = Y.one(dock.items[id].title).ancestor('.'+dock.css.dockedtitle);
176 var itemheight = Math.floor((possibleheight-usedheight) / (dock.count - runningcount));
177 var offsetheight = itemtitle.get('offsetHeight');
178 itemtitle.setStyle('overflow', 'hidden');
179 if (offsetheight > itemheight) {
180 itemtitle.setStyle('height', itemheight+'px');
181 usedheight += itemheight;
183 usedheight += offsetheight;
191 // Attach the required event listeners
192 // We use delegate here as that way a handful of events are created for the dock
193 // and all items rather than the same number for the dock AND every item individually
194 Y.delegate('click', this.handleEvent, this.nodes.dock, '.'+this.css.dockedtitle, this, {cssselector:'.'+this.css.dockedtitle, delay:0});
195 Y.delegate('mouseenter', this.handleEvent, this.nodes.dock, '.'+this.css.dockedtitle, this, {cssselector:'.'+this.css.dockedtitle, delay:0.5, iscontained:true, preventevent:'click', preventdelay:3});
196 //Y.delegate('mouseleave', this.handleEvent, this.nodes.body, '#dock', this, {cssselector:'#dock', delay:0.5, iscontained:false});
197 this.nodes.dock.on('mouseleave', this.handleEvent, this, {cssselector:'#dock', delay:0.5, iscontained:false});
199 this.nodes.body.on('click', this.handleEvent, this, {cssselector:'body', delay:0});
200 this.on('dock:itemschanged', this.resizeBlockSpace, this);
201 this.on('dock:itemschanged', this.checkDockVisibility, this);
202 this.on('dock:itemschanged', this.resetFirstItem, this);
203 // Inform everyone the dock has been initialised
204 this.fire('dock:initialised');
208 * Get the panel docked blocks will be shown in and initialise it if we havn't already.
210 M.core_dock.getPanel = function() {
211 if (this.nodes.panel === null) {
212 // Initialise the dockpanel .. should only happen once
213 this.nodes.panel = (function(Y, parent){
214 var dockpanel = Y.Node.create('<div id="dockeditempanel" class="dockitempanel_hidden"><div class="dockeditempanel_content"><div class="dockeditempanel_hd"></div><div class="dockeditempanel_bd"></div></div></div>');
215 // Give the dockpanel event target properties and methods
216 Y.augment(dockpanel, Y.EventTarget);
217 // Publish events for the dock panel
218 dockpanel.publish('dockpanel:beforeshow', {prefix:'dockpanel'});
219 dockpanel.publish('dockpanel:shown', {prefix:'dockpanel'});
220 dockpanel.publish('dockpanel:beforehide', {prefix:'dockpanel'});
221 dockpanel.publish('dockpanel:hidden', {prefix:'dockpanel'});
222 dockpanel.publish('dockpanel:visiblechange', {prefix:'dockpanel'});
223 // Cache the content nodes
224 dockpanel.contentNode = dockpanel.one('.dockeditempanel_content');
225 dockpanel.contentHeader = dockpanel.contentNode.one('.dockeditempanel_hd');
226 dockpanel.contentBody = dockpanel.contentNode.one('.dockeditempanel_bd');
227 // Set the x position of the panel
228 //dockpanel.setX(parent.get('offsetWidth'));
229 dockpanel.visible = false;
231 dockpanel.show = function() {
232 this.fire('dockpanel:beforeshow');
234 this.removeClass('dockitempanel_hidden');
235 this.fire('dockpanel:shown');
236 this.fire('dockpanel:visiblechange');
239 dockpanel.hide = function() {
240 this.fire('dockpanel:beforehide');
241 this.visible = false;
242 this.addClass('dockitempanel_hidden');
243 this.fire('dockpanel:hidden');
244 this.fire('dockpanel:visiblechange');
246 // Add a method to set the header content
247 dockpanel.setHeader = function(content) {
248 this.contentHeader.setContent(content);
249 if (arguments.length > 1) {
250 for (var i=1;i < arguments.length;i++) {
251 this.contentHeader.append(arguments[i]);
255 // Add a method to set the body content
256 dockpanel.setBody = function(content) {
257 this.contentBody.setContent(content);
259 // Add a method to set the top of the panel position
260 dockpanel.setTop = function(newtop) {
267 this.setStyle('top', newtop+'px');
269 // Put the dockpanel in the body
270 parent.append(dockpanel);
273 })(this.Y, this.nodes.dock);
274 this.nodes.panel.on('panel:visiblechange', this.resize, this);
275 this.Y.on('windowresize', this.resize, this);
276 this.fire('dock:panelgenerated');
278 return this.nodes.panel;
281 * Handles a generic event within the dock
283 * @param {object} options Event configuration object
285 M.core_dock.handleEvent = function(e, options) {
286 var item = this.getActiveItem();
287 var target = (e.target.test(options.cssselector))?e.target:e.target.ancestor(options.cssselector);
288 if (options.cssselector == 'body') {
289 if (!this.nodes.dock.contains(e.target)) {
295 if (this.preventevent !== null && e.type === this.preventevent) {
298 if (options.preventevent) {
299 this.preventevent = options.preventevent;
300 if (options.preventdelay) {
301 setTimeout(function(){M.core_dock.preventevent = null;}, options.preventdelay*1000);
304 if (this.delayedevent && this.delayedevent.timeout) {
305 clearTimeout(this.delayedevent.timeout);
306 this.delayedevent.event.detach();
307 this.delayedevent = null;
309 if (options.delay > 0) {
310 return this.delayEvent(e, options, target);
312 var targetid = target.get('id');
313 if (targetid.match(/^dock_item_(\d+)_title$/)) {
314 item = this.items[targetid.replace(/^dock_item_(\d+)_title$/, '$1')];
327 * This function delays an event and then fires it providing the cursor if either
328 * within or outside of the original target (options.iscontained=true|false)
329 * @param {Y.Event} event
330 * @param {object} options
331 * @param {Y.Node} target
334 M.core_dock.delayEvent = function(event, options, target) {
336 self.delayedevent = (function(){
339 event : self.nodes.body.on('mousemove', function(e){
340 self.delayedevent.target = e.target;
345 self.delayedevent.timeout = setTimeout(function(){
346 self.delayedevent.timeout = null;
347 self.delayedevent.event.detach();
348 if (options.iscontained == self.nodes.dock.contains(self.delayedevent.target)) {
349 self.handleEvent(event, {cssselector:options.cssselector, delay:0, iscontained:options.iscontained});
351 }, options.delay*1000);
355 * Corrects the orientation of the title, which for the default
356 * dock just means making it vertical
357 * The orientation is determined by M.str.langconfig.thisdirectionvertical:
358 * ver : Letters are stacked rather than rotated
359 * ttb : Title is rotated clockwise so the first letter is at the top
360 * btt : Title is rotated counterclockwise so the first letter is at the bottom.
361 * @param {string} title
363 M.core_dock.fixTitleOrientation = function(item, title, text) {
366 var title = Y.one(title);
368 if (Y.UA.ie > 0 && Y.UA.ie < 8) {
369 // IE 6/7 can't rotate text so force ver
370 M.str.langconfig.thisdirectionvertical = 'ver';
373 var clockwise = false;
374 switch (M.str.langconfig.thisdirectionvertical) {
377 return title.setContent(text.split('').join('<br />'));
387 // IE8 can flip the text via CSS but not handle SVG
388 title.setContent(text);
389 title.setAttribute('style', 'writing-mode: tb-rl; filter: flipV flipH;display:inline;');
390 title.addClass('filterrotate');
394 // Cool, we can use SVG!
395 var test = Y.Node.create('<h2><span style="font-size:10px;">'+text+'</span></h2>');
396 this.nodes.body.append(test);
397 var height = test.one('span').get('offsetWidth')+4;
398 var width = test.one('span').get('offsetHeight')*2;
399 var qwidth = width/4;
402 // Create the text for the SVG
403 var txt = document.createElementNS('http://www.w3.org/2000/svg', 'text');
404 txt.setAttribute('font-size','10px');
406 txt.setAttribute('transform','rotate(90 '+(qwidth/2)+' '+qwidth+')');
408 txt.setAttribute('y', height);
409 txt.setAttribute('transform','rotate(270 '+qwidth+' '+(height-qwidth)+')');
411 txt.appendChild(document.createTextNode(text));
413 var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
414 svg.setAttribute('version', '1.1');
415 svg.setAttribute('height', height);
416 svg.setAttribute('width', width);
417 svg.appendChild(txt);
421 item.on('dockeditem:drawcomplete', function(txt, title){
422 txt.setAttribute('fill', Y.one(title).getStyle('color'));
423 }, item, txt, title);
428 * Resizes the space that contained blocks if there were no blocks left in
429 * it. e.g. if all blocks have been moved to the dock
430 * @param {Y.Node} node
432 M.core_dock.resizeBlockSpace = function(node) {
434 if (this.Y.all('.block.dock_on_load').size()>0) {
435 // Do not resize during initial load
438 var blockregions = [];
439 var populatedblockregions = 0;
440 this.Y.all('.block-region').each(function(region){
441 var hasblocks = (region.all('.block').size() > 0);
443 populatedblockregions++;
445 blockregions[region.get('id')] = {hasblocks: hasblocks, bodyclass: region.get('id').replace(/^region\-/, 'side-')+'-only'};
447 var bodynode = M.core_dock.nodes.body;
448 var noblocksbodyclass = 'content-only';
450 if (populatedblockregions==0) {
451 bodynode.addClass(noblocksbodyclass);
452 for (i in blockregions) {
453 bodynode.removeClass(blockregions[i].bodyclass);
455 } else if (populatedblockregions==1) {
456 bodynode.removeClass(noblocksbodyclass);
457 for (i in blockregions) {
458 if (!blockregions[i].hasblocks) {
459 bodynode.removeClass(blockregions[i].bodyclass);
461 bodynode.addClass(blockregions[i].bodyclass);
465 bodynode.removeClass(noblocksbodyclass);
466 for (i in blockregions) {
467 bodynode.removeClass(blockregions[i].bodyclass);
472 * Adds a dock item into the dock
474 * @param {M.core_dock.item} item
476 M.core_dock.add = function(item) {
477 item.id = this.totalcount;
480 this.items[item.id] = item;
481 this.items[item.id].draw();
482 this.fire('dock:itemadded', item);
483 this.fire('dock:itemschanged', item);
486 * Appends a dock item to the dock
487 * @param {YUI.Node} docknode
489 M.core_dock.append = function(docknode) {
490 this.nodes.container.append(docknode);
493 * Initialises a generic block object
497 M.core_dock.init_genericblock = function(Y, id) {
498 if (!this.initialised) {
501 new this.genericblock(id).init(Y, Y.one('#inst'+id));
504 * Removes the node at the given index and puts it back into conventional page sturcture
506 * @param {int} uid Unique identifier for the block
509 M.core_dock.remove = function(uid) {
510 if (!this.items[uid]) {
513 this.items[uid].remove();
514 delete this.items[uid];
516 this.fire('dock:itemremoved', uid);
517 this.fire('dock:itemschanged', uid);
521 * Ensures the the first item in the dock has the correct class
523 M.core_dock.resetFirstItem = function() {
524 this.nodes.dock.all('.'+this.css.dockeditem+'.firstdockitem').removeClass('firstdockeditem');
525 if (this.nodes.dock.one('.'+this.css.dockeditem)) {
526 this.nodes.dock.one('.'+this.css.dockeditem).addClass('firstdockitem');
530 * Removes all nodes and puts them back into conventional page sturcture
534 M.core_dock.remove_all = function() {
535 for (var i in this.items) {
541 * Hides the active item
543 M.core_dock.hideActive = function() {
544 var item = this.getActiveItem();
550 * Checks wether the dock should be shown or hidden
552 M.core_dock.checkDockVisibility = function() {
554 this.nodes.dock.addClass('nothingdocked');
555 this.nodes.body.removeClass(this.css.body);
556 this.fire('dock:hidden');
558 this.fire('dock:beforeshow');
559 this.nodes.dock.removeClass('nothingdocked');
560 this.nodes.body.addClass(this.css.body);
561 this.fire('dock:shown');
565 * This smart little function allows developers to attach event listeners before
566 * the dock has been augmented to allows event listeners.
567 * Once the augmentation is complete this function will be replaced with the proper
568 * on method for handling event listeners.
569 * Finally applyBinds needs to be called in order to properly bind events.
570 * @param {string} event
571 * @param {function} callback
573 M.core_dock.on = function(event, callback) {
574 this.earlybinds.push({event:event,callback:callback});
577 * This function takes all early binds and attaches them as listeners properly
578 * This should only be called once augmentation is complete.
580 M.core_dock.applyBinds = function() {
581 for (var i in this.earlybinds) {
582 var bind = this.earlybinds[i];
583 this.on(bind.event, bind.callback);
585 this.earlybinds = [];
588 * This function checks the size and position of the panel and moves/resizes if
589 * required to keep it within the bounds of the window.
591 M.core_dock.resize = function() {
592 this.fire('dock:panelresizestart');
593 var panel = this.getPanel();
594 var item = this.getActiveItem();
595 if (!panel.visible || !item) {
598 var buffer = this.cfg.buffer;
599 var screenheight = parseInt(this.nodes.body.get('winHeight'))-(buffer*2);
600 var docky = this.nodes.dock.getY();
601 var titletop = item.nodes.docktitle.getY()-docky-buffer;
602 var containery = this.nodes.container.getY();
603 var containerheight = containery-docky+this.nodes.container.get('offsetHeight');
604 panel.contentBody.setStyle('height', 'auto');
605 panel.removeClass('oversized_content');
606 var panelheight = panel.get('offsetHeight');
608 if (panelheight > screenheight) {
609 panel.setStyle('top', (buffer-containerheight)+'px');
610 panel.contentBody.setStyle('height', (screenheight-panel.contentHeader.get('offsetHeight'))+'px');
611 panel.addClass('oversized_content');
612 } else if (panelheight > (screenheight-(titletop-buffer))) {
613 var difference = panelheight - (screenheight-titletop);
614 panel.setStyle('top', (titletop-containerheight-difference+buffer)+'px');
616 panel.setStyle('top', (titletop-containerheight+buffer)+'px');
618 this.fire('dock:resizepanelcomplete');
622 * Returns the currently active dock item or false
624 M.core_dock.getActiveItem = function() {
625 for (var i in this.items) {
626 if (this.items[i].active) {
627 return this.items[i];
633 * This class represents a generic block
634 * @class M.core_dock.genericblock
637 M.core_dock.genericblock = function(id) {
638 // Nothing to actually do here but it needs a constructor!
643 M.core_dock.genericblock.prototype = {
644 Y : null, // A YUI instance to use with the block
645 id : null, // The block instance id
646 cachedcontentnode : null, // The cached content node for the actual block
647 blockspacewidth : null, // The width of the block's original container
648 skipsetposition : false, // If true the user preference isn't updated
649 isdocked : false, // True if it is docked
651 * This function should be called within the block's constructor and is used to
652 * set up the initial controls for swtiching block position as well as an initial
653 * moves that may be required.
656 * @param {YUI.Node} node The node that contains all of the block's content
657 * @return {M.core_dock.genericblock}
659 init : function(Y, node) {
667 var commands = node.one('.header .title .commands');
669 commands = this.Y.Node.create('<div class="commands"></div>');
670 if (node.one('.header .title')) {
671 node.one('.header .title').append(commands);
675 // Must set the image src seperatly of we get an error with XML strict headers
676 var moveto = Y.Node.create('<input type="image" class="moveto customcommand requiresjs" alt="'+M.str.block.addtodock+'" title="'+M.str.block.addtodock+'" />');
677 moveto.setAttribute('src', M.util.image_url('t/block_to_dock', 'moodle'));
678 moveto.on('movetodock|click', this.move_to_dock, this, commands);
680 var blockaction = node.one('.block_action');
682 blockaction.prepend(moveto);
684 commands.append(moveto);
687 // Move the block straight to the dock if required
688 if (node.hasClass('dock_on_load')) {
689 node.removeClass('dock_on_load');
690 this.skipsetposition = true;
691 this.move_to_dock(null, commands);
697 * This function is reponsible for moving a block from the page structure onto the
701 move_to_dock : function(e, commands) {
707 var dock = M.core_dock;
709 var node = Y.one('#inst'+this.id);
710 var blockcontent = node.one('.content');
715 var blockclass = (function(classes){
716 var r = /(^|\s)(block_[a-zA-Z0-9_]+)(\s|$)/;
717 var m = r.exec(classes);
719 })(node.getAttribute('className').toString());
721 this.cachedcontentnode = node;
723 node.replace(Y.Node.getDOMNode(Y.Node.create('<div id="content_placeholder_'+this.id+'" class="block_dock_placeholder"></div>')));
724 M.core_dock.holdingarea.append(node);
727 var blocktitle = Y.Node.getDOMNode(this.cachedcontentnode.one('.title h2')).cloneNode(true);
729 var blockcommands = this.cachedcontentnode.one('.title .commands');
730 if (!blockcommands) {
731 blockcommands = Y.Node.create('<div class="commands"></div>');
732 this.cachedcontentnode.one('.title').append(blockcommands);
735 // Must set the image src seperatly of we get an error with XML strict headers
736 var movetoimg = Y.Node.create('<img alt="'+M.str.block.undockitem+'" title="'+M.str.block.undockitem+'" />');
737 movetoimg.setAttribute('src', M.util.image_url('t/dock_to_block', 'moodle'));
738 var moveto = Y.Node.create('<a class="moveto customcommand requiresjs"></a>').append(movetoimg);
739 if (location.href.match(/\?/)) {
740 moveto.set('href', location.href+'&dock='+this.id);
742 moveto.set('href', location.href+'?dock='+this.id);
744 blockcommands.append(moveto);
746 // Create a new dock item for the block
747 var dockitem = new dock.item(Y, this.id, blocktitle, blockcontent, blockcommands, blockclass);
748 // Wire the draw events to register remove events
749 dockitem.on('dockeditem:drawcomplete', function(e){
750 // check the contents block [editing=off]
751 this.contents.all('.moveto').on('returntoblock|click', function(e){
753 dock.remove(this.id);
755 // check the commands block [editing=on]
756 this.commands.all('.moveto').on('returntoblock|click', function(e){
758 dock.remove(this.id);
761 // Must set the image src seperatly of we get an error with XML strict headers
762 var closeicon = Y.Node.create('<span class="hidepanelicon"><img alt="" style="width:11px;height:11px;cursor:pointer;" /></span>');
763 closeicon.one('img').setAttribute('src', M.util.image_url('t/dockclose', 'moodle'));
764 closeicon.on('forceclose|click', this.hide, this);
765 this.commands.append(closeicon);
767 // Register an event so that when it is removed we can put it back as a block
768 dockitem.on('dockeditem:itemremoved', this.return_to_block, this, dockitem);
771 if (!this.skipsetposition) {
772 // save the users preference
773 M.util.set_user_preference('docked_block_instance_'+this.id, 1);
775 this.skipsetposition = false;
778 this.isdocked = true;
781 * This function removes a block from the dock and puts it back into the page
783 * @param {M.core_dock.class.item}
785 return_to_block : function(dockitem) {
786 var placeholder = this.Y.one('#content_placeholder_'+this.id);
788 if (this.cachedcontentnode.one('.header')) {
789 this.cachedcontentnode.one('.header').insert(dockitem.contents, 'after');
791 this.cachedcontentnode.insert(dockitem.contents);
794 placeholder.replace(this.Y.Node.getDOMNode(this.cachedcontentnode));
795 this.cachedcontentnode = this.Y.one('#'+this.cachedcontentnode.get('id'));
797 var commands = this.cachedcontentnode.one('.title .commands');
799 commands.all('.hidepanelicon').remove();
800 commands.all('.moveto').remove();
803 this.cachedcontentnode.one('.title').append(commands);
804 this.cachedcontentnode = null;
805 M.util.set_user_preference('docked_block_instance_'+this.id, 0);
806 this.isdocked = false;
812 * This class represents an item in the dock
813 * @class M.core_dock.item
815 * @param {YUI} Y The YUI instance to use for this item
816 * @param {int} uid The unique ID for the item
817 * @param {this.Y.Node} title
818 * @param {this.Y.Node} contents
819 * @param {this.Y.Node} commands
820 * @param {string} blockclass
822 M.core_dock.item = function(Y, uid, title, contents, commands, blockclass){
824 this.publish('dockeditem:drawstart', {prefix:'dockeditem'});
825 this.publish('dockeditem:drawcomplete', {prefix:'dockeditem'});
826 this.publish('dockeditem:showstart', {prefix:'dockeditem'});
827 this.publish('dockeditem:showcomplete', {prefix:'dockeditem'});
828 this.publish('dockeditem:hidestart', {prefix:'dockeditem'});
829 this.publish('dockeditem:hidecomplete', {prefix:'dockeditem'});
830 this.publish('dockeditem:itemremoved', {prefix:'dockeditem'});
831 if (uid && this.id==null) {
834 if (title && this.title==null) {
835 this.titlestring = title.cloneNode(true);
836 this.title = document.createElement(title.nodeName);
837 M.core_dock.fixTitleOrientation(this, this.title, this.titlestring.firstChild.nodeValue);
839 if (contents && this.contents==null) {
840 this.contents = contents;
842 if (commands && this.commands==null) {
843 this.commands = commands;
845 if (blockclass && this.blockclass==null) {
846 this.blockclass = blockclass;
848 this.nodes = (function(){
849 return {docktitle : null, dockitem : null, container: null};
855 M.core_dock.item.prototype = {
856 Y : null, // The YUI instance to use with this dock item
857 id : null, // The unique id for the item
858 name : null, // The name of the item
859 title : null, // The title of the item
860 titlestring : null, // The title as a plain string
861 contents : null, // The content of the item
862 commands : null, // The commands for the item
863 active : false, // True if the item is being shown
864 blockclass : null, // The class of the block this item relates to
867 * This function draws the item on the dock
870 this.fire('dockeditem:drawstart');
873 var css = M.core_dock.css;
875 this.nodes.docktitle = Y.Node.create('<div id="dock_item_'+this.id+'_title" class="'+css.dockedtitle+'"></div>');
876 this.nodes.docktitle.append(this.title);
877 this.nodes.dockitem = Y.Node.create('<div id="dock_item_'+this.id+'" class="'+css.dockeditem+'"></div>');
878 if (M.core_dock.count === 1) {
879 this.nodes.dockitem.addClass('firstdockitem');
881 this.nodes.dockitem.append(this.nodes.docktitle);
882 M.core_dock.append(this.nodes.dockitem);
883 this.fire('dockeditem:drawcomplete');
887 * This function toggles makes the item active and shows it
890 M.core_dock.hideActive();
892 var css = M.core_dock.css;
893 var panel = M.core_dock.getPanel();
894 this.fire('dockeditem:showstart');
895 panel.setHeader(this.titlestring, this.commands);
896 panel.setBody(Y.Node.create('<div class="'+this.blockclass+' block_docked"></div>').append(this.contents));
900 // Add active item class first up
901 this.nodes.docktitle.addClass(css.activeitem);
902 this.fire('dockeditem:showcomplete');
903 M.core_dock.resize();
907 * This function hides the item and makes it inactive
910 var css = M.core_dock.css;
911 this.fire('dockeditem:hidestart');
914 // Remove the active class
915 this.nodes.docktitle.removeClass(css.activeitem);
917 M.core_dock.getPanel().hide();
918 this.fire('dockeditem:hidecomplete');
921 * This function removes the node and destroys it's bits
924 remove : function () {
926 this.nodes.dockitem.remove();
927 this.fire('dockeditem:itemremoved');