javascript-dock MDL-21329 Several major changes to the underlying navigation and...
authorSam Hemelryk <sam@moodle.com>
Thu, 14 Jan 2010 05:35:23 +0000 (05:35 +0000)
committerSam Hemelryk <sam@moodle.com>
Thu, 14 Jan 2010 05:35:23 +0000 (05:35 +0000)
The following changes have been made in this commit:
1. Renamed the side bar thing to dock, this is consistent through all code/css now.
2. Converted everything to YUI 3 except the panel which docked items are shown in, this iwll be converted once YUI 3 overlay is no longer beta.
3 Redisigned the dock, all blocks can now make use of it, and theme code within JS can override it so they can make it look as they want.
More changes are coming

19 files changed:
blocks/blocks.js [new file with mode: 0644]
blocks/global_navigation_tree/block_global_navigation_tree.php
blocks/global_navigation_tree/navigation.js [new file with mode: 0644]
blocks/moodleblock.class.php
blocks/settings_navigation_tree/block_settings_navigation_tree.php
lang/en_utf8/block.php
lang/en_utf8/block_global_navigation_tree.php
lang/en_utf8/block_settings_navigation_tree.php
lang/en_utf8/moodle.php
lib/ajax/ajaxlib.php
lib/navigationlib.php
pix/t/block_to_dock.png [new file with mode: 0644]
pix/t/dock_to_block.png [new file with mode: 0644]
theme/base/config.php
theme/base/javascript/navigation.js
theme/base/style/blocks.css
theme/standardold/config.php
theme/standardold/javascript/navigation.js
theme/standardold/style/styles_layout.css

diff --git a/blocks/blocks.js b/blocks/blocks.js
new file mode 100644 (file)
index 0000000..22e2753
--- /dev/null
@@ -0,0 +1,591 @@
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * This file contains classes used to manage the navigation structures in Moodle
+ * and was introduced as part of the changes occuring in Moodle 2.0
+ *
+ * @since 2.0
+ * @package javascript
+ * @copyright 2010 Sam Hemelryk
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * This namespace will contain all of content (functions, classes, properties)
+ * for the block system
+ * @namespace
+ */
+var blocks = blocks || {};
+blocks.setup_generic_block = function(uid) {
+    Y.use('base','dom','io','node', 'event-custom', function() {
+        var block = new blocks.genericblock(uid);
+        block.init();
+    });
+}
+
+/**
+ * @namespace
+ */
+blocks.dock = {
+    count:0,        // The number of dock items through the page life
+    exists:false,   // True if the dock exists
+    items:[],       // An array of dock items
+    node:null,      // The YUI node for the dock itself
+    earlybinds:[],
+    strings:{
+        addtodock : '[[addtodock]]',
+        undockitem : '[[undockitem]]',
+        undockall : '[[undockall]]'
+    },
+    /**
+     * Configuration parameters used during the initialisation and setup
+     * of dock and dock items.
+     * This is here specifically so that themers can override core parameters and
+     * design aspects without having to re-write navigation
+     * @namespace
+     */
+    cfg:{
+        buffer:10,                          // Buffer used when containing a panel
+        position:'left',                    // position of the dock
+        orientation:'vertical',             // vertical || horizontal determines if we change the title
+        display:{
+            spacebeforefirstitem: 10         // Space between the top of the dock and the first item
+        },
+        css: {
+            dock:'dock',                  // CSS Class applied to the dock box
+            dockspacer:'dockspacer',      // CSS class applied to the dockspacer
+            controls:'controls',            // CSS class applied to the controls box
+            body:'has_dock',                // CSS class added to the body when there is a dock
+            dockeditem:'dockeditem',            // CSS class added to each item in the dock
+            dockedtitle:'dockedtitle',         // CSS class added to the item's title in each dock
+            activeitem:'activeitem'          // CSS class added to the active item
+        },
+        panel: {
+            close:false,                    // Show a close button on the panel
+            draggable:false,                // Make the panel draggable
+            underlay:"none",                // Use a special underlay
+            modal:false,                    // Throws a lightbox if set to true
+            keylisteners:null,              // An array of keylisterners to attach
+            visible:false,                  // Visible by default
+            effect: null,                   // An effect that should be used with the panel
+            monitorresize:false,            // Monitor the resize of the panel
+            context:null,                   // Sets up contexts for the panel
+            fixedcenter:false,              // Always displays the panel in the center of the screen
+            zIndex:null,                    // Sets a specific z index for the panel
+            constraintoviewport: false,     // Constrain the panel to the viewport
+            autofillheight:'body'           // Which container element should fill out empty space
+        }
+    },
+    /**
+     * Adds a dock item into the dock
+     * @function
+     */
+    add:function(item) {
+        item.id = this.count;
+        this.count++;
+        this.items[item.id] = item;
+        this.draw();
+        this.items[item.id].draw();
+        this.fire('dock:itemadded', item);
+    },
+    /**
+     * Draws the dock
+     * @function
+     */
+    draw:function() {
+        if (this.node !== null) {
+            return true;
+        }
+        this.fire('dock:drawstarted');
+        this.node = Y.Node.create('<div id="dock" class="'+blocks.dock.cfg.css.dock+'"></div>');
+        this.node.appendChild(Y.Node.create('<div class="'+blocks.dock.cfg.css.dockspacer+'" style="height:'+blocks.dock.cfg.display.spacebeforefirstitem+'px"></div>'));
+        if (Y.UA.ie > 0 && Y.UA.ie < 7) {
+            this.node.setStyle('height', this.node.get('winHeight')+'px');
+        }
+
+        var dockcontrol = Y.Node.create('<div class="'+blocks.dock.cfg.css.controls+'"></div>');
+        var removeall = Y.Node.create('<img src="'+get_image_url('t/dock_to_block', 'moodle')+'" alt="'+blocks.dock.strings.undockall+'" title="'+blocks.dock.strings.undockall+'" />');
+        removeall.on('removeall|click', this.remove_all, this);
+        dockcontrol.appendChild(removeall);
+        this.node.appendChild(dockcontrol);
+
+        Y.one(document.body).appendChild(this.node);
+        Y.one(document.body).addClass(blocks.dock.cfg.css.body);
+        this.fire('dock:drawcompleted');
+        return true;
+    },
+    /**
+     * Removes the node at the given index and puts it back into conventional page sturcture
+     * @function
+     */
+    remove:function(uid) {
+        this.items[uid].remove();
+        this.fire('dock:itemremoved', uid);
+        this.count--;
+        if (this.count===0) {
+            this.fire('dock:toberemoved');
+            this.items = [];
+            this.node.remove();
+            this.node = null;
+            this.fire('dock:removed');
+        }
+    },
+    /**
+     * Removes all nodes and puts them back into conventional page sturcture
+     * @function
+     */
+    remove_all:function() {
+        for (var i in this.items) {
+            this.items[i].remove();
+            this.items[i] = null;
+        }
+        Y.fire('dock:toberemoved');
+        this.items = [];
+        this.node.remove();
+        this.node = null;
+        Y.fire('dock:removed');
+    },
+    /**
+     * Resizes the active item
+     * @function
+     */
+    resize:function(e){
+        for (var i in this.items) {
+            if (this.items[i].active) {
+                this.items[i].resize_panel(e);
+            }
+        }
+    },
+    /**
+     * Hides all (the active) item
+     * @function
+     */
+    hide_all:function() {
+        for (var i in this.items) {
+            this.items[i].hide();
+        }
+    },
+    /**
+     * This smart little function allows developers to attach event listeners before
+     * the dock has been augmented to allows event listeners.
+     * Once the augmentation is complete this function will be replaced with the proper
+     * on method for handling event listeners.
+     * Finally apply_binds needs to be called in order to properly bind events.
+     * @param {string} event
+     * @param {function} callback
+     */
+    on : function(event, callback) {
+        this.earlybinds.push({event:event,callback:callback});
+    },
+    /**
+     * This function takes all early binds and attaches them as listeners properly
+     * This should only be called once augmentation is complete.
+     */
+    apply_binds : function() {
+        for (var i in this.earlybinds) {
+            var bind = this.earlybinds[i];
+            this.on(bind.event, bind.callback);
+        }
+        this.earlybinds = [];
+    },
+    /**
+     * Namespace containing methods and properties that will be prototyped
+     * to the generic block class and possibly overriden by themes
+     * @namespace
+     */
+    abstract_block_class : {
+
+        id : null,
+        cachedcontentnode : null,
+        blockspacewidth : null,
+        skipsetposition : false,
+
+        /**
+         * This function should be called within the block's constructor and is used to
+         * set up the initial controls for swtiching block position as well as an initial
+         * moves that may be required.
+         *
+         * @param {YUI.Node} The node that contains all of the block's content
+         */
+        init : function(node) {
+            if (!node) {
+                node = Y.one('#inst'+this.id);
+            }
+
+            var commands = node.one('.header .title .commands');
+            if (!commands) {
+                commands = Y.Node.create('<div class="commands"></div>');
+                if (node.one('.header .title')) {
+                    node.one('.header .title').append(commands);
+                }
+            }
+
+            var moveto = Y.Node.create('<a class="moveto customcommand requiresjs"></a>');
+            moveto.append(Y.Node.create('<img src="'+get_image_url('t/dock_to_block', 'moodle')+'" alt="'+blocks.dock.strings.undockitem+'" title="'+blocks.dock.strings.undockitem+'" />'));
+            if (location.href.match(/\?/)) {
+                moveto.set('href', location.href+'&dock='+this.id);
+            } else {
+                moveto.set('href', location.href+'?dock='+this.id);
+            }
+            commands.append(moveto);
+            commands.all('a.moveto').on('movetodock|click', this.move_to_dock, this);
+
+            var customcommands = node.all('.customcommand');
+            if (customcommands.size() > 0) {
+                customcommands.each(function(){
+                    this.remove();
+                    commands.appendChild(this);
+                });
+            }
+
+            if (node.hasClass('dock_on_load')) {
+                node.removeClass('dock_on_load')
+                this.skipsetposition = true;
+                this.move_to_dock();
+            }
+        },
+
+        /**
+         * This function is reponsible for moving a block from the page structure onto the
+         * dock
+         * @param {event}
+         */
+        move_to_dock : function(e) {
+            if (e) {
+                e.halt(true);
+            }
+
+            var node = Y.one('#inst'+this.id);
+            var blockcontent = node.one('.content');
+
+            this.cachedcontentnode = node;
+
+            node.all('a.moveto').each(function(moveto){
+                Y.Event.purgeElement(Y.Node.getDOMNode(moveto), false, 'click');
+                if (moveto.hasClass('customcommand')) {
+                    moveto.all('img').each(function(movetoimg){
+                        movetoimg.setAttribute('src', get_image_url('t/dock_to_block', 'moodle'));
+                        movetoimg.setAttribute('alt', blocks.dock.strings.undockitem);
+                        movetoimg.setAttribute('title', blocks.dock.strings.undockitem);
+                    }, this);
+                }
+            }, this);
+
+            var placeholder = Y.Node.create('<div id="content_placeholder_'+this.id+'"></div>');
+            node.replace(Y.Node.getDOMNode(placeholder));
+            node = null;
+
+            this.resize_block_space(placeholder);
+
+            var blocktitle = Y.Node.getDOMNode(this.cachedcontentnode.one('.title h2')).cloneNode(true);
+            blocktitle.innerHTML = blocktitle.innerHTML.replace(/([a-zA-Z0-9])/g, "$1<br />");
+
+            var commands = this.cachedcontentnode.all('.title .commands');
+            var blockcommands = Y.Node.create('<div class="commands"></div>');
+            if (commands.size() > 0) {
+                blockcommands = commands.item(0);
+            }
+
+            var dockitem = new blocks.dock.item(this.id, blocktitle, blockcontent, blockcommands);
+            dockitem.on('dockeditem:drawcomplete', function(e){
+                // check the contents block [editing=off]
+                this.contents.all('a.moveto').on('returntoblock|click', function(e){
+                    e.halt();
+                    blocks.dock.remove(this.id)
+                }, this);
+                // check the commands block [editing=on]
+                this.commands.all('a.moveto').on('returntoblock|click', function(e){
+                    e.halt();
+                    blocks.dock.remove(this.id)
+                }, this);
+            }, dockitem);
+            dockitem.on('dock:itemremoved', this.return_to_block, this, dockitem);
+            blocks.dock.add(dockitem);
+
+            if (!this.skipsetposition) {
+                set_user_preference('docked_block_instance_'+this.id, 1);
+            } else {
+                this.skipsetposition = false;
+            }
+        },
+
+        /**
+         * Resizes the space that contained blocks if there were no blocks left in
+         * it. e.g. if all blocks have been moved to the dock
+         */
+        resize_block_space : function(node) {
+            node = node.ancestor('.block-region');
+            if (node) {
+                if (node.all('.sideblock').size() === 0 && this.blockspacewidth === null) {
+                    this.blockspacewidth = node.getStyle('width');
+                    node.setStyle('width', '0px');
+                } else if (this.blockspacewidth !== null) {
+                    node.setStyle('width', this.blockspacewidth);
+                    this.blockspacewidth = null;
+                }
+            }
+        },
+
+        /**
+         * This function removes a block from the dock and puts it back into the page
+         * structure.
+         * @param {blocks.dock.class.item}
+         */
+        return_to_block : function(dockitem) {
+            var placeholder = Y.one('#content_placeholder_'+this.id);
+            this.cachedcontentnode.appendChild(dockitem.contents);
+            placeholder.replace(Y.Node.getDOMNode(this.cachedcontentnode));
+            this.cachedcontentnode = Y.one('#'+this.cachedcontentnode.get('id'));
+
+            this.resize_block_space(this.cachedcontentnode);
+
+            this.cachedcontentnode.all('a.moveto').each(function(moveto){
+                Y.Event.purgeElement(Y.Node.getDOMNode(moveto), false, 'click');
+                moveto.on('movetodock|click', this.move_to_dock, this);
+                if (moveto.hasClass('customcommand')) {
+                    moveto.all('img').each(function(movetoimg){
+                        movetoimg.setAttribute('src', get_image_url('t/block_to_dock', 'moodle'));
+                        movetoimg.setAttribute('alt', blocks.dock.strings.addtodock);
+                        movetoimg.setAttribute('title', blocks.dock.strings.addtodock);
+                    }, this);
+                }
+             }, this);
+
+            var commands = this.cachedcontentnode.all('.commands');
+            var blocktitle = this.cachedcontentnode.all('.title');
+
+            if (commands.size() === 1 && blocktitle.size() === 1) {
+                commands.item(0).remove();
+                blocktitle.item(0).append(commands.item(0));
+            }
+
+            this.cachedcontentnode = null;
+            set_user_preference('docked_block_instance_'+this.id, 0);
+            return true;
+        }
+    },
+
+    abstract_item_class : {
+        id : null,
+        name : null,
+        title : null,
+        contents : null,
+        commands : null,
+        events : null,
+        active : false,
+        panel : null,
+        preventhide : false,
+        cfg : null,
+
+        init_events : function() {
+            this.publish('dockeditem:drawstart', {prefix:'dockeditem'});
+            this.publish('dockeditem:drawcomplete', {prefix:'dockeditem'});
+            this.publish('dockeditem:showstart', {prefix:'dockeditem'});
+            this.publish('dockeditem:showcomplete', {prefix:'dockeditem'});
+            this.publish('dockeditem:hidestart', {prefix:'dockeditem'});
+            this.publish('dockeditem:hidecomplete', {prefix:'dockeditem'});
+            this.publish('dockeditem:resizestart', {prefix:'dockeditem'});
+            this.publish('dockeditem:resizecomplete', {prefix:'dockeditem'});
+            this.publish('dockeditem:itemremoved', {prefix:'dockeditem'});
+        },
+
+        /**
+         * This function draws the item on the dock
+         */
+        draw : function() {
+            this.fire('dockeditem:drawstart');
+            var dockitemtitle = Y.Node.create('<div id="dock_item_'+this.id+'_title" class="'+this.cfg.css.dockedtitle+'"></div>');
+            dockitemtitle.append(this.title);
+            var dockitem = Y.Node.create('<div id="dock_item_'+this.id+'" class="'+this.cfg.css.dockeditem+'"></div>');
+            if (blocks.dock.count === 1) {
+                dockitem.addClass('firstdockitem');
+            }
+            dockitem.append(dockitemtitle);
+            if (this.commands.hasChildNodes) {
+                this.contents.appendChild(this.commands);
+            }
+            blocks.dock.node.append(dockitem);
+
+            var position = dockitemtitle.getXY();
+            position[0] += parseInt(dockitemtitle.get('offsetWidth'));
+            if (YAHOO.env.ua.ie > 0 && YAHOO.env.ua.ie < 8) {
+                position[0] -= 2;
+            }
+            this.panel = new YAHOO.widget.Panel('dock_item_panel_'+this.id, {
+                close:this.cfg.panel.close,
+                draggable:this.cfg.panel.draggable,
+                underlay:this.cfg.panel.underlay,
+                modal: this.cfg.panel.modal,
+                keylisteners: this.cfg.panel.keylisteners,
+                visible:this.cfg.panel.visible,
+                effect:this.cfg.panel.effect,
+                monitorresize:this.cfg.panel.monitorresize,
+                context: this.cfg.panel.context,
+                fixedcenter: this.cfg.panel.fixedcenter,
+                zIndex: this.cfg.panel.zIndex,
+                constraintoviewport: this.cfg.panel.constraintoviewport,
+                xy:position,
+                autofillheight:this.cfg.panel.autofillheight});
+            this.panel.showEvent.subscribe(this.resize_panel, this, true);
+            this.panel.setBody(Y.Node.getDOMNode(this.contents));
+            this.panel.render(blocks.dock.node);
+            dockitem.on('showitem|mouseover', this.show, this);
+            this.fire('dockeditem:drawcomplete');
+        },
+        /**
+         * This function removes the node and destroys it's bits
+         */
+        remove : function (e) {
+            this.hide(e);
+            Y.one('#dock_item_'+this.id).remove();
+            this.panel.destroy();
+            this.fire('dock:itemremoved');
+        },
+        /**
+         * This function toggles makes the item active and shows it
+         * @param {event}
+         */
+        show : function(e) {
+            blocks.dock.hide_all();
+            this.fire('dockeditem:showstart');
+            this.panel.show(e, this);
+            this.active = true;
+            Y.one('#dock_item_'+this.id+'_title').addClass(this.cfg.css.activeitem);
+            Y.detach('mouseover', this.show, Y.one('#dock_item_'+this.id));
+            Y.one('#dock_item_panel_'+this.id).on('dockpreventhide|click', function(){this.preventhide=true;}, this);
+            Y.one('#dock_item_'+this.id).on('dockhide|click', this.hide, this);
+            Y.get(window).on('dockresize|resize', this.resize_panel, this);
+            Y.get(document.body).on('dockhide|click', this.hide, this);
+            this.fire('dockeditem:showcomplete');
+            return true;
+        },
+        /**
+         * This function hides the item and makes it inactive
+         * @param {event}
+         */
+        hide : function(e) {
+            if (this.preventhide===true) {
+                this.preventhide = false;
+            } else if (this.active) {
+                this.fire('dockeditem:hidestart');
+                this.active = false;
+                Y.one('#dock_item_'+this.id+'_title').removeClass(this.cfg.css.activeitem);
+                Y.one('#dock_item_'+this.id).on('showitem|mouseover', this.show, this);
+                Y.get(window).detach('dockresize|resize');
+                Y.get(document.body).detach('dockhide|click');
+                this.panel.hide(e, this);
+                this.fire('dockeditem:hidecomplete');
+            }
+        },
+        /**
+         * This function checks the size and position of the panel and moves/resizes if
+         * required to keep it within the bounds of the window.
+         */
+        resize_panel : function() {
+            this.fire('dockeditem:resizestart');
+            var panelbody = Y.one(this.panel.body);
+            var buffer = this.cfg.buffer;
+            var screenheight = parseInt(Y.get(document.body).get('winHeight'));
+            var panelheight = parseInt(panelbody.get('offsetHeight'));
+            var paneltop = parseInt(this.panel.cfg.getProperty('y'));
+            var titletop = parseInt(Y.one('#dock_item_'+this.id+'_title').getY());
+            var scrolltop = window.pageYOffset || document.body.scrollTop || 0;
+
+            // This makes sure that the panel is the same height as the dock title to
+            // begin with
+            if (paneltop > (buffer+scrolltop) && paneltop > (titletop+scrolltop)) {
+                this.panel.cfg.setProperty('y', titletop+scrolltop);
+            }
+
+            // This makes sure that if the panel is big it is moved up to ensure we don't
+            // have wasted space above the panel
+            if ((paneltop+panelheight)>(screenheight+scrolltop) && paneltop > buffer) {
+                paneltop = (screenheight-panelheight-buffer);
+                if (paneltop<buffer) {
+                    paneltop = buffer;
+                }
+                this.panel.cfg.setProperty('y', paneltop+scrolltop);
+            }
+
+            // This makes the panel constrain to the screen's height if the panel is big
+            if (paneltop <= buffer && ((panelheight+paneltop*2) > screenheight || panelbody.hasClass('oversized_content'))) {
+                this.panel.cfg.setProperty('height', screenheight-(buffer*2));
+                panelbody.setStyle('height', (screenheight-(buffer*3))+'px');
+                panelbody.addClass('oversized_content');
+            }
+            this.fire('dockeditem:resizecomplete');
+        }
+    }
+};
+
+/**
+ * This class represents a generic block
+ * @class genericblock
+ * @constructor
+ */
+blocks.genericblock = function(uid){
+    if (uid && this.id==null) {
+        this.id = uid;
+    }
+};
+/** Properties */
+blocks.genericblock.prototype.name =                    blocks.dock.abstract_block_class.name;
+blocks.genericblock.prototype.cachedcontentnode =       blocks.dock.abstract_block_class.cachedcontentnode;
+blocks.genericblock.prototype.blockspacewidth =         blocks.dock.abstract_block_class.blockspacewidth;
+blocks.genericblock.prototype.skipsetposition =         blocks.dock.abstract_block_class.skipsetposition;
+/** Methods **/
+blocks.genericblock.prototype.init =                    blocks.dock.abstract_block_class.init;
+blocks.genericblock.prototype.move_to_dock =            blocks.dock.abstract_block_class.move_to_dock;
+blocks.genericblock.prototype.resize_block_space =      blocks.dock.abstract_block_class.resize_block_space;
+blocks.genericblock.prototype.return_to_block =         blocks.dock.abstract_block_class.return_to_block;
+
+/**
+ * This class represents an item in the dock
+ * @class item
+ * @constructor
+ */
+blocks.dock.item = function(uid, title, contents, commands){
+    if (uid && this.id==null) this.id = uid;
+    if (title && this.title==null) this.title = title;
+    if (contents && this.contents==null) this.contents = contents;
+    if (commands && this.commands==null) this.commands = commands;
+    this.init_events();
+}
+/** Properties */
+blocks.dock.item.prototype.id =                 blocks.dock.abstract_item_class.id;
+blocks.dock.item.prototype.name =               blocks.dock.abstract_item_class.name;
+blocks.dock.item.prototype.title =              blocks.dock.abstract_item_class.title;
+blocks.dock.item.prototype.contents =           blocks.dock.abstract_item_class.contents;
+blocks.dock.item.prototype.commands =           blocks.dock.abstract_item_class.commands;
+blocks.dock.item.prototype.events =             blocks.dock.abstract_item_class.events;
+blocks.dock.item.prototype.active =             blocks.dock.abstract_item_class.active;
+blocks.dock.item.prototype.panel =              blocks.dock.abstract_item_class.panel;
+blocks.dock.item.prototype.preventhide =        blocks.dock.abstract_item_class.preventhide;
+blocks.dock.item.prototype.cfg =                blocks.dock.cfg;
+/** Methods **/
+blocks.dock.item.prototype.init_events =        blocks.dock.abstract_item_class.init_events;
+blocks.dock.item.prototype.draw =               blocks.dock.abstract_item_class.draw;
+blocks.dock.item.prototype.remove =             blocks.dock.abstract_item_class.remove;
+blocks.dock.item.prototype.show =               blocks.dock.abstract_item_class.show;
+blocks.dock.item.prototype.hide =               blocks.dock.abstract_item_class.hide;
+blocks.dock.item.prototype.resize_panel =       blocks.dock.abstract_item_class.resize_panel;
+
+YUI({base: moodle_cfg.yui3loaderBase}).use('event-custom','event', 'node', function(Y){
+    // Give the dock item class the event properties/methods
+    Y.augment(blocks.dock.item, Y.EventTarget);
+    Y.augment(blocks.dock, Y.EventTarget, true);
+    blocks.dock.apply_binds();
+});
\ No newline at end of file
index 61d5434..fc790b6 100644 (file)
@@ -79,6 +79,12 @@ class block_global_navigation_tree extends block_tree {
         return true;
     }
 
+    function get_required_javascript() {
+        $this->_initialise_dock();
+        $this->page->requires->js('blocks/global_navigation_tree/navigation.js');
+        user_preference_allow_ajax_update('docked_block_instance_'.$this->instance->id, PARAM_INT);
+    }
+
     /**
      * Gets the content for this block by grabbing it from $this->page
      */
@@ -142,21 +148,14 @@ class block_global_navigation_tree extends block_tree {
             $this->showmyhistory();
         }
 
-        $togglesidetabdisplay = get_string('togglesidetabdisplay', $this->blockname);
-        $toggleblockdisplay = get_string('toggleblockdisplay', $this->blockname);
-
-
         // Get the expandable items so we can pass them to JS
         $expandable = array();
         $this->page->navigation->find_expandable($expandable);
-        $args = array('expansions'=>$expandable,'instance'=>$this->instance->id);
-        $args['togglesidetabdisplay'] = $togglesidetabdisplay;
-        $args['toggleblockdisplay'] = $toggleblockdisplay;
-        // Give JS some information we will use within the JS tree object
-        $this->page->requires->data_for_js('globalnav'.block_global_navigation_tree::$navcount, $args);
+        
         // Initialise the JS tree object
-        $this->id = 'globalnav'.block_global_navigation_tree::$navcount;
-        $this->page->requires->js_function_call('setup_new_navtree', array($this->id))->on_dom_ready();
+        $args = array($this->instance->id,array('expansions'=>$expandable,'instance'=>$this->instance->id));
+        $this->page->requires->js_function_call('blocks.navigation.setup_new_tree',  $args)->on_dom_ready();
+        
         // Grab the items to display
         $this->content->items = array($this->page->navigation);
 
@@ -165,21 +164,6 @@ class block_global_navigation_tree extends block_tree {
 
         $this->content->footer .= $OUTPUT->action_icon($reloadlink, get_string('reload'), 't/reload');
 
-        if (empty($this->config->enablesidebarpopout) || $this->config->enablesidebarpopout == 'yes') {
-            user_preference_allow_ajax_update('nav_in_tab_panel_globalnav'.block_global_navigation_tree::$navcount, PARAM_INT);
-
-            $movelink = new html_link($this->page->url);
-            $movelink->add_classes('moveto customcommand requiresjs');
-            if ($this->docked) {
-                $movelink->url->param('undock', $this->instance->id);
-                $moveicon = $OUTPUT->action_icon($movelink, $toggleblockdisplay, 't/movetoblock');
-            } else {
-                $movelink->url->param('dock', $this->instance->id);
-                $moveicon = $OUTPUT->action_icon($movelink, $toggleblockdisplay, 't/movetosidetab');
-            }
-            $this->content->footer .= $moveicon;
-        }
-
         // Set content generated to true so that we know it has been done
         $this->contentgenerated = true;
         return true;
@@ -200,14 +184,14 @@ class block_global_navigation_tree extends block_tree {
         $attributes = parent::html_attributes();
 
         if ($this->docked===null) {
-            $this->docked = get_user_preferences('nav_in_tab_panel_globalnav'.block_global_navigation_tree::$navcount, 0);
+            $this->docked = get_user_preferences('docked_block_instance_'.$this->instance->id, 0);
         }
 
         if (!empty($this->config->enablehoverexpansion) && $this->config->enablehoverexpansion == 'yes') {
-            $attributes['class'] .= ' sideblock_js_expansion';
+            $attributes['class'] .= ' block_js_expansion';
         }
         if ($this->docked) {
-            $attributes['class'] .= ' sideblock_js_sidebarpopout';
+            $attributes['class'] .= ' dock_on_load';
         }
         return $attributes;
     }
diff --git a/blocks/global_navigation_tree/navigation.js b/blocks/global_navigation_tree/navigation.js
new file mode 100644 (file)
index 0000000..39bd0f2
--- /dev/null
@@ -0,0 +1,298 @@
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * This file contains classes used to manage the navigation structures in Moodle
+ * and was introduced as part of the changes occuring in Moodle 2.0
+ *
+ * @since 2.0
+ * @package javascript
+ * @copyright 2009 Sam Hemelryk
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * @namespace
+ */
+var blocks = blocks || {};
+
+/**
+ * This namespace will contain all of the contents of the navigation blocks
+ * global navigation and settings.
+ * @namespace
+ */
+blocks.navigation = {
+    /** The number of expandable branches in existence */
+    expandablebranchcount:0,
+    /** An array of initialised trees */
+    treecollection:[],
+    /**
+     * @namespace
+     */
+    classes:{},
+    /**
+     * @function
+     * @static
+     * @param {int} uid The id of the block within the page
+     * @param {object} properties
+     */
+    setup_new_tree:function(uid, properties) {
+        Y.use('base','dom','io','node', function() {
+            properties = properties || {'instance':uid};
+            blocks.navigation.treecollection[uid] = new blocks.navigation.classes.tree(uid, uid, properties);
+        });
+    }
+};
+
+/**
+ * @class tree
+ * @constructor
+ * @base blocks.dock.abstractblock
+ * @param {string} id The name of the tree
+ * @param {int} key The internal id within the tree store
+ * @param {object} properties Object containing tree properties
+ */
+blocks.navigation.classes.tree = function(id, key, properties) {
+    this.id = id;
+    this.key = key;
+    this.type = 'blocks.navigation.classes.tree';
+    this.errorlog = [];
+    this.ajaxbranches = 0;
+    this.expansions = [];
+    this.instance = null;
+    this.cachedcontentnode = null;
+    this.cachedfooter = null;
+    this.position = 'block';
+    this.skipsetposition = false;
+    if (properties) {
+        if (properties.expansions) {
+            this.expansions = properties.expansions;
+        }
+        if (properties.instance) {
+            this.instance = properties.instance;
+        }
+    }
+
+    if (Y.one('#inst'+this.id) === null) {
+        return;
+    }
+
+    for (var i in this.expansions) {
+        Y.one('#'+this.expansions[i].id).on('ajaxload|click', this.init_load_ajax, this, this.expansions[i]);
+        blocks.navigation.expandablebranchcount++;
+    }
+
+    var node = Y.one('#inst'+this.id);
+    node.all('.tree_item.branch').on('click', this.toggleexpansion , this);
+
+    this.init(node);
+
+    if (node.hasClass('block_js_expansion')) {
+        node.on('mouseover', function(e){this.toggleClass('mouseover');}, node);
+        node.on('mouseout', function(e){this.toggleClass('mouseover');}, node);
+    }
+}
+
+/**
+ * Loads a branch via AJAX
+ * @param {event} The event object
+ * @param {object} A branch to load via ajax
+ */
+blocks.navigation.classes.tree.prototype.init_load_ajax = function(e, branch) {
+    e.stopPropagation();
+    if (e.target.get('nodeName').toUpperCase() != 'P') {
+        return true;
+    }
+    var cfginstance = (this.instance != null)?'&instance='+this.instance:'';
+    Y.io(moodle_cfg.wwwroot+'/lib/ajax/getnavbranch.php', {
+        method:'POST',
+        data:'elementid='+branch.id+'&id='+branch.branchid+'&type='+branch.type+'&sesskey='+moodle_cfg.sesskey+cfginstance,
+        on: {
+            complete:this.load_ajax,
+            success:function() {Y.detach('click', this.init_load_ajax, e.target);}
+        },
+        context:this,
+        arguments:{
+            target:e.target
+        }
+    });
+    return true;
+}
+
+/**
+ * Takes an branch provided through ajax and loads it into the tree
+ */
+blocks.navigation.classes.tree.prototype.load_ajax = function(tid, outcome, args) {
+    // Check the status
+    if (outcome.status!=0 && outcome.responseXML!=null) {
+        var branch = outcome.responseXML.documentElement;
+        if (branch!=null && this.add_branch(branch, args.target.ancestor('LI') ,1)) {
+            // If we get here everything worked perfectly
+            blocks.dock.resize();
+            return true;
+        }
+    }
+    args.target.replaceClass('branch', 'emptybranch');
+    return true;
+}
+
+/**
+ * Adds a branch into the tree provided with some XML
+ */
+blocks.navigation.classes.tree.prototype.add_branch = function(branchxml, target, depth) {
+
+    var branch = new blocks.navigation.classes.branch(this);
+    branch.construct_from_xml(branchxml);
+
+    var childrenul = false;
+    if (depth === 1) {
+        if (!branch.haschildren) {
+            return false;
+        }
+        childrenul = Y.Node.create('<ul></ul>');
+        target.appendChild(childrenul);
+    } else {
+        childrenul = branch.inject_into_dom(target);
+    }
+
+    if (childrenul) {
+        for (var i=0;i<branch.children.childNodes.length;i++) {
+            this.add_branch(branch.children.childNodes[i], childrenul, depth+1);
+        }
+    }
+    return true;
+}
+/**
+ * Toggle a branch as expanded or collapsed
+ */
+blocks.navigation.classes.tree.prototype.toggleexpansion = function(e) {
+    e.target.ancestor('LI').toggleClass('collapsed');
+    blocks.dock.resize();
+}
+
+/**
+ * This class represents a branch for a tree
+ * @class tree
+ * @constructor
+ */
+blocks.navigation.classes.branch = function(tree) {
+    this.tree = tree;
+    this.name = null;
+    this.title = null;
+    this.classname = null;
+    this.id = null;
+    this.key = null;
+    this.type = null;
+    this.link = null;
+    this.icon = null;
+    this.expandable = null;
+    this.expansionceiling = null;
+    this.hidden = false;
+    this.haschildren = false;
+    this.children = false;
+}
+/**
+ * Constructs a branch from XML
+ */
+blocks.navigation.classes.branch.prototype.construct_from_xml = function(xml) {
+    this.title = xml.getAttribute('title');
+    this.classname = xml.getAttribute('class');
+    this.id = xml.getAttribute('id');
+    this.link = xml.getAttribute('link');
+    this.icon = xml.getAttribute('icon');
+    this.key = xml.getAttribute('key');
+    this.type = xml.getAttribute('type');
+    this.expandable = xml.getAttribute('expandable');
+    this.expansionceiling = xml.getAttribute('expansionceiling');
+    this.hidden = (xml.getAttribute('hidden')=='true');
+    this.haschildren = (xml.getAttribute('haschildren')=='true');
+
+    if (this.id && this.id.match(/^expandable_branch_\d+$/)) {
+        blocks.navigation.expandablebranchcount++;
+        this.id = 'expandable_branch_'+blocks.navigation.expandablebranchcount;
+    }
+
+    for (var i=0; i<xml.childNodes.length;i++) {
+        var node = xml.childNodes[i];
+        switch (node.nodeName.toLowerCase()) {
+            case 'name':
+                this.name = node.firstChild.nodeValue;
+                break;
+            case 'children':
+                this.children = node;
+        }
+    }
+}
+/**
+ * Injects a branch into the tree at the given location
+ */
+blocks.navigation.classes.branch.prototype.inject_into_dom = function(element) {
+
+    var branchli = Y.Node.create('<li></li>');
+    var branchp = Y.Node.create('<p class="tree_item"></p>');
+
+    if ((this.expandable !== null || this.haschildren) && this.expansionceiling===null) {
+        branchli.addClass('collapsed');
+        branchp.addClass('branch');
+        branchp.on('click', this.tree.toggleexpansion, this.tree);
+        if (this.expandable) {
+            branchp.on('ajaxload|click', this.tree.init_load_ajax, this.tree, {branchid:this.key,id:this.id,type:this.type});
+        }
+    }
+
+    if (this.myclass !== null) {
+        branchp.addClass(this.myclass);
+    }
+    if (this.id !== null) {
+        branchp.setAttribute('id', this.id);
+    }
+
+    var branchicon = false;
+    if (this.icon != null) {
+        branchicon = Y.Node.create('<img src="'+this.icon+'" alt="" />');
+        this.name = ' '+this.name;
+    }
+    if (this.link === null) {
+        if (branchicon) {
+            branchp.appendChild(branchicon);
+        }
+        branchp.append(this.name.replace(/\n/g, '<br />'));
+    } else {
+        var branchlink = Y.Node.create('<a title="'+this.title+'" href="'+this.link+'">'+this.name.replace(/\n/g, '<br />')+'</a>');
+        if (branchicon) {
+            branchlink.appendChild(branchicon);
+        }
+        if (this.hidden) {
+            branchlink.addClass('dimmed');
+        }
+        branchp.appendChild(branchlink);
+    }
+
+    branchli.appendChild(branchp);
+    if (this.haschildren) {
+        var childrenul = Y.Node.create('<ul></ul>');
+        branchli.appendChild(childrenul);
+        element.appendChild(branchli);
+        return childrenul
+    } else {
+        element.appendChild(branchli);
+        return false;
+    }
+}
+
+YUI({base: moodle_cfg.yui3loaderBase}).use('event-custom', 'node', function(Y){
+    // Give the tree class the dock block properties
+    Y.augment(blocks.navigation.classes.tree, blocks.genericblock);
+});
\ No newline at end of file
index 03db833..595405c 100644 (file)
@@ -113,6 +113,8 @@ class block_base {
 
     var $cron          = NULL;
 
+    static $dockinitialised = false;
+
 /// Class Functions
 
     /**
@@ -541,10 +543,14 @@ class block_base {
      * @return array attribute name => value.
      */
     function html_attributes() {
-        return array(
+        $attributes = array(
             'id' => 'inst' . $this->instance->id,
             'class' => 'block_' . $this->name()
         );
+        if (get_user_preferences('docked_block_instance_'.$this->instance->id, 0)) {
+            $attributes['class'] .= ' dock_on_load';
+        }
+        return $attributes;
     }
 
     /**
@@ -566,6 +572,15 @@ class block_base {
         }
         $this->page = $page;
         $this->specialization();
+        $this->get_required_javascript();
+    }
+
+    function get_required_javascript() {
+        if ($this->instance_can_dock_with_dock()) {
+            $this->_initialise_dock();
+            $this->page->requires->js_function_call('blocks.setup_generic_block', array($this->instance->id))->on_dom_ready();
+            user_preference_allow_ajax_update('docked_block_instance_'.$this->instance->id, PARAM_INT);
+        }
     }
 
     /**
@@ -728,6 +743,22 @@ class block_base {
     function config_print() {
         throw new coding_exception('config_print() can no longer be used. Blocks should use a settings.php file.');
     }
+
+    public function instance_can_dock_with_dock() {
+        return true;
+    }
+
+    public function _initialise_dock() {
+        if (!self::$dockinitialised) {
+            $this->page->requires->js('blocks/blocks.js');
+            $this->page->requires->data_for_js('blocks.dock.strings.addtodock', get_string('addtodock', 'block'));
+            $this->page->requires->data_for_js('blocks.dock.strings.undockitem', get_string('undockitem', 'block'));
+            $this->page->requires->data_for_js('blocks.dock.strings.undockall', get_string('undockall', 'block'));
+            self::$dockinitialised = true;
+        }
+        
+    }
+
     /** @callback callback functions for comments api */
     public static function comment_template($options) {
         $ret = <<<EOD
index 3f9b82e..75fee8b 100644 (file)
@@ -39,7 +39,6 @@ class block_settings_navigation_tree extends block_tree {
     /** @var string */
     public static $navcount;
     public $blockname = null;
-    public $id = null;
     /** @var bool */
     protected $contentgenerated = false;
     /** @var bool|null */
@@ -78,6 +77,13 @@ class block_settings_navigation_tree extends block_tree {
         return true;
     }
 
+    function get_required_javascript() {
+        $this->_initialise_dock();
+        $this->page->requires->js('blocks/global_navigation_tree/navigation.js');
+        $this->page->requires->js_function_call('blocks.navigation.setup_new_tree', array($this->instance->id))->on_dom_ready();
+        user_preference_allow_ajax_update('docked_block_instance_'.$this->instance->id, PARAM_INT);
+    }
+
     /**
      * Gets the content for this block by grabbing it from $this->page
      */
@@ -110,17 +116,6 @@ class block_settings_navigation_tree extends block_tree {
             redirect($url);
         }
 
-        $togglesidetabdisplay = get_string('togglesidetabdisplay', $this->blockname);
-        $toggleblockdisplay = get_string('toggleblockdisplay', $this->blockname);
-        $args = array('instance'=>$this->instance->id);
-        $args['togglesidetabdisplay'] = $togglesidetabdisplay;
-        $args['toggleblockdisplay'] = $toggleblockdisplay;
-        // Give JS some information we will use within the JS tree object
-        $this->page->requires->data_for_js('settingsnav'.block_settings_navigation_tree::$navcount, $args);
-
-
-        $this->id = 'settingsnav'.block_settings_navigation_tree::$navcount;
-        $this->page->requires->js_function_call('setup_new_navtree', array($this->id))->on_dom_ready();
         // Grab the children from settings nav, we have more than one root node
         // and we dont want to show the site node
         $this->content->items = $this->page->settingsnav->children;
@@ -147,17 +142,6 @@ class block_settings_navigation_tree extends block_tree {
 
             if (!empty($this->config->enablesidebarpopout) && $this->config->enablesidebarpopout == 'yes') {
                 user_preference_allow_ajax_update('nav_in_tab_panel_settingsnav'.block_settings_navigation_tree::$navcount, PARAM_INT);
-
-                $movelink = new html_link($this->page->url);
-                $movelink->add_classes('moveto customcommand requiresjs');
-                if ($this->docked) {
-                    $movelink->url->param('undock', $this->instance->id);
-                    $moveicon = $OUTPUT->action_icon($movelink, $toggleblockdisplay, 't/movetoblock');
-                } else {
-                    $movelink->url->param('dock', $this->instance->id);
-                    $moveicon = $OUTPUT->action_icon($movelink, $toggleblockdisplay, 't/movetosidetab');
-                }
-                $this->content->footer .= $moveicon;
             }
         }
 
@@ -170,14 +154,14 @@ class block_settings_navigation_tree extends block_tree {
 
         // Check if this block has been docked
         if ($this->docked === null) {
-            $this->docked = get_user_preferences('nav_in_tab_panel_settingsnav'.block_settings_navigation_tree::$navcount, 0);
+            $this->docked = get_user_preferences('docked_block_instance_'.$this->instance->id, 0);
         }
 
         if (!empty($this->config->enablehoverexpansion) && $this->config->enablehoverexpansion == 'yes') {
-            $attributes['class'] .= ' sideblock_js_expansion';
+            $attributes['class'] .= ' block_js_expansion';
         }
         if ($this->docked) {
-            $attributes['class'] .= ' sideblock_js_sidebarpopout';
+            $attributes['class'] .= ' dock_on_load';
         }
         return $attributes;
     }
index 70b576f..e06c51d 100644 (file)
@@ -1,6 +1,7 @@
 <?php // $Id$ 
       // block.php - created with Moodle 2.0 dev
 
+$string['addtodock'] = 'Move this to the dock';
 $string['appearsinsubcontexts'] = 'Appears in sub-contexts';
 $string['anypagematchingtheabove'] = 'Any page matching the above';
 $string['blocksettings'] = 'Block settings';
@@ -19,3 +20,5 @@ $string['thisspecificpage'] = 'This specific page (page $a)';
 $string['visible'] = 'Visible';
 $string['weight'] = 'Weight';
 $string['wherethisblockappears'] = 'Where this block appears';
+$string['undockall'] = 'Undock all';
+$string['undockitem'] = 'Undock this item';
index 4de6075..2f8fb00 100644 (file)
@@ -36,5 +36,3 @@ $string['enablehoverexpansiondesc'] = 'Enable mouseover expansion of this block'
 $string['enablesidebarpopoutdesc'] = 'Allow the user to switch the block to a sidbar popout';
 $string['showmyhistorydesc'] = 'Show my history as a branch in the navigation';
 $string['showmyhistorytitle'] = 'My history';
-$string['toggleblockdisplay'] = 'Move to block position';
-$string['togglesidetabdisplay'] = 'Move to side panel tab';
index 813815f..cead9f6 100644 (file)
@@ -27,5 +27,3 @@
 $string['blockname'] = 'Settings';
 $string['enablehoverexpansion'] = 'Enable mouseover expansion of this block';
 $string['enablesidebarpopout'] = 'Allow the user to switch the block to a sidbar popout';
-$string['toggleblockdisplay'] = 'Move to block position';
-$string['togglesidetabdisplay'] = 'Move to side panel tab';
index 47bc55c..8e67ad2 100644 (file)
@@ -1047,7 +1047,6 @@ $string['moreinformation'] = 'More information about this error';
 $string['moreprofileinfoneeded'] = 'Please tell us more about yourself';
 $string['mostrecently'] = 'most recently';
 $string['move'] = 'Move';
-$string['moveallsidetabstoblock'] = 'Move all tabs back to their block position';
 $string['movecategoryto'] = 'Move category to:';
 $string['movecategorycontentto'] = 'Move into';
 $string['movecontentstoanothercategory'] = 'Move contents to another category';
index 94ac28f..d5a3865 100644 (file)
@@ -243,7 +243,7 @@ class page_requirements_manager {
      */
     public function yui3_lib($libname) {
         if ($this->headdone) {
-            throw new coding_exception('YUI3 libraries can be preloaded by PHP only from HEAD, please use YUI autoloading instead: ', $stylesheet);
+            throw new coding_exception('YUI3 libraries can be preloaded by PHP only from HEAD, please use YUI autoloading instead: ', $libname);
         }
         $libnames = (array)$libname;
         foreach ($libnames as $lib) {
@@ -591,6 +591,7 @@ class page_requirements_manager {
      * @return string the HTML code to to at the end of the page.
      */
     public function get_end_code() {
+        global $CFG;
         $output = $this->get_yui2lib_code();
         $output .= $this->get_linked_resources_code(self::WHEN_AT_END);
 
@@ -601,9 +602,16 @@ class page_requirements_manager {
         $js = $this->get_javascript_code(self::WHEN_AT_END);
 
         $ondomreadyjs = $this->get_javascript_code(self::WHEN_ON_DOM_READY, '    ');
-        if ($ondomreadyjs) {
-            $js .= "YAHOO.util.Event.onDOMReady(function() {\n" . $ondomreadyjs . "});\n";
-        }
+
+        $js .= <<<EOD
+Y = YUI({
+    base: moodle_cfg.yui3loaderBase
+}).use('node-base', function(Y) {
+    Y.on('domready', function() {
+    $ondomreadyjs
+    });
+});
+EOD;
 
         $output .= ajax_generate_script_tag($js);
 
index 0d75c05..41e035e 100644 (file)
@@ -884,7 +884,6 @@ class global_navigation extends navigation_node {
         $this->forceopen = true;
         $this->action = new moodle_url($CFG->wwwroot);
         $this->cache = new navigation_cache(NAVIGATION_CACHE_NAME);
-        $PAGE->requires->string_for_js('moveallsidetabstoblock','moodle');
         $regenerate = optional_param('regenerate', null, PARAM_TEXT);
         if ($regenerate==='navigation') {
             $this->cache->clear();
diff --git a/pix/t/block_to_dock.png b/pix/t/block_to_dock.png
new file mode 100644 (file)
index 0000000..d72690c
Binary files /dev/null and b/pix/t/block_to_dock.png differ
diff --git a/pix/t/dock_to_block.png b/pix/t/dock_to_block.png
new file mode 100644 (file)
index 0000000..5ace5af
Binary files /dev/null and b/pix/t/dock_to_block.png differ
index 4b0ef7c..03070d7 100644 (file)
@@ -150,5 +150,5 @@ $THEME->layouts = array(
 );
 
 /** List of javascript files that need to included on each page */
-$THEME->javascripts = array('navigation');
-//$THEME->javascripts_footer = array();
\ No newline at end of file
+$THEME->javascripts = array();
+$THEME->javascripts_footer = array('navigation');
\ No newline at end of file
index e8b872f..7e0fbff 100644 (file)
@@ -1 +1,116 @@
-/* base: javascript needed for navbar manipulations */
+/**
+ * So you want to override the navigation huh ??
+ * Make it look like your own and totally customise it to be way cool !!
+ *
+ * Well now you can by following the instructions in this file.
+ *
+ * It will be essential to have a clear idea about what it is you want to acheive,
+ * whilst it is possible to override nearly all of the navbar settings/methods it's
+ * not nesecarily going to be an easy task.
+ *
+ * To begin you must understand the structure of the blocks and particually the navbar
+ * object. The following outlines the basic structure:
+ *
+ *      - Namespace: blocks
+ *          - Func: setup_generic_block         Creates a new generic block instance
+ *          - Class: genericblock                   Generic block class
+ *          - Namespace: navbar
+ *              - Var: count                        The # of items that have EVER existed on the navbar
+ *              - Var: exists                       True if the navbar exists
+ *              - Var: items                        An array of items on the navbar
+ *              - Var: node                         The node that is the navbar
+ *              - Var: strings                      An object containing strings for the navbar
+ *              - Namespace: cfg
+ *                  - Var: buffer                   The space buffer around panels
+ *                  - Var: position                 The position of the navbar
+ *                  - Var: orientation              The orientation of the navbar
+ *                  - Namespace: display
+ *                      ............                A series of display parameters
+ *                  - Namespace: css
+ *                      ............                A series of CSS class names
+ *                  - Namespace: panel
+ *                      ............                A series of conf options for YUI panels
+ *              - Func: add                         Adds an item to the navbar
+ *              - Func: draw                        Creates the navbar and adds it to the page
+ *              - Func: remove                      Removes an item from the navbar
+ *              - Func: remove_all                  Removes all items from the navbar
+ *              - Func: resize                      Calls the navbar to resize its active item
+ *              - Func: hide_all                    Calls the navbar to hide all active items
+ *              - Class: item                       A navbar item class
+ *              - Namespace: abstract_block_class   A namespace containing all of the properties
+ *                      .............               and methods that will be used as the default
+ *                      .............               methods for the generic block class.
+ *              - Namespace: abstract_item_class    A namespace containing all of the properties
+ *                      .............               and methods for the navbar item class
+ *              
+ * From the structure above you are able to immediatly override any of the vars
+ * that are associated with the navigation by simply assigning them a value as
+ * shown below:
+ * 
+ *      blocks.navbar.cfg.buffer = 20; // or
+ *
+ * You are also able to override all of the properties and methods of the two
+ * abstract classes that manage all of the interaction for the blocks and navbar
+ * items thanks to the prototyping method that is being used to build the classes.
+ *
+ * To override a method simply copy the following style of coding:
+ *
+ *      blocks.genericblock.prototype.init = function(uid) {
+ *          // The code for the new init method which will be executed in the
+ *          // objects scope and override the old init method.
+ *      }
+ *
+ *      // OR if the following is easier for you to understand
+ *
+ *      function new_init_method(uid) {
+ *          // The code for the new init method which will be executed in the
+ *          // objects scope and override the old init method.
+ *      }
+ *      blocks.genericblock.prototype.init = new_init_method()
+ *
+ * Alternativily for the navbar items class the there are a series of actions that
+ * get fired that you may want to listen to. The events defined are as follows:
+ *
+ *      navbaritem:drawstart        draw is called
+ *      navbaritem:drawcomplete     draw is complete
+ *      navbaritem:showstart        show is called
+ *      navbaritem:showcomplete     show is complete
+ *      navbaritem:hidestart        hide is called
+ *      navbaritem:hidecomplete     hide is complete
+ *      navbaritem:resizestart      resize is called
+ *      navbaritem:resizecomplete   resize is complete
+ *      navbaritem:itemremoved      item is removed from the navbar
+ *
+ * You can listen to any of these events by first finding the appropriate item within
+ * the navbar.items array and then calling the following on it:
+ *
+ *      var uid = x;
+ *      blocks.navbar.items[uid].on('navbaritem:showstart', callback, scope);
+ *      function callback(navbaritem) {
+ *          // What ever you want to do can go here
+ *      }
+ *
+ */
+
+// If this isn't set we don't need an override at all
+if (blocks.genericblock) {
+
+    /**
+     * Override the default resize_block_space method so that we can ensure
+     * it works for this template
+     * @param {Y.Node} blocknode
+     */
+    blocks.genericblock.prototype.resize_block_space = function(blocknode) {
+        var blockregion = blocknode.ancestor('#block-region');
+        if (blockregion) {
+            if (blockregion.all('.sideblock').size() === 0 && this.blockspacewidth === null) {
+                // Some spiffy code to reduce the template sideblock to 0 width
+                this.blockspacewidth = blockregion.getStyle('width');
+            } else if (this.blockspacewidth !== null) {
+                // Some spiffy code to set the sideblock width back to the original width
+                this.blockspacewidth = null;
+            }
+        }
+    }
+    
+}
\ No newline at end of file
index f37f39b..7a2646c 100644 (file)
   font-weight:bold;
 }
 
-.jsenabled .sideblock_js_sidebarpopout,
+.jsenabled .dock_on_load,
 .jsenabled .block_tree .collapsed ul {
   display: none;
 }
index 884fc9d..80cf7a4 100644 (file)
@@ -177,5 +177,5 @@ $THEME->layouts = array(
 );
 
 /** List of javascript files that need to included on each page */
-$THEME->javascripts = array('navigation');
-
+$THEME->javascripts = array();
+$THEME->javascripts_footer = array('navigation');
\ No newline at end of file
index b23832b..e9ee0e4 100644 (file)
-/* legacy standard: javascript needed for navbar manipulations */
-
-// content of this file was originally in lib/javascript-naigation.php,
-// it was moved here for two reasons - testing of themes JS and second it may need
-// to use different tricks in other themes with CSS column layouts
-
-
-
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * This file contains classes used to manage the navigation structures in Moodle
- * and was introduced as part of the changes occuring in Moodle 2.0
- *
- * @since 2.0
- * @package javascript
- * @copyright 2009 Sam Hemelryk
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-/**
- * Some very important general namespaces to act as containers for the general
- * objects required to manage the navigation.
- *
- * For anyone looking to improve this javascript taking a little time to turn
- * the classes into namespaced classes, and giving the class structure in this file
- * a similar structure to YUI on a moodle namespace would be AWESOME
- */
-YAHOO.namespace('moodle.navigation');
-YAHOO.namespace('moodle.navigation.sideblockwidth');
-YAHOO.namespace('moodle.navigation.tabpanel');
-YAHOO.namespace('moodle.navigation.treecollection');
-
-/**
- * Instatiate some very important variables that allow us to manage the navigaiton
- * objects without having to hit my arch enemy `undefined`
- */
-YAHOO.moodle.navigation.sideblockwidth = null;
-YAHOO.moodle.navigation.tabpanel = null;
-YAHOO.moodle.navigation.treecollection = Array();
-YAHOO.moodle.navigation.expandablebranchcount = 0;
-
-/**
- * Navigation Tree object (function) used to control a global navigation tree
- * handling things such as collapse, expand, and AJAX requests for more branches
- *
- * You should never call this directly.. you should use {@link start_new_navtree()}
- * which will create the class and make it accessible in a smart way
- *
- * @class navigation_tree
- * @constructor
- * @param {string} treename
- * @param {string} key
- */
-function navigation_tree (treename, key) {
-    this.name = treename;
-    this.key = key;
-    this.errorlog = '';
-    this.ajaxbranches = 0;
-    this.expansions = Array();
-    this.instance = null
-    this.cachedcontent = null;
-    this.cachedfooter = null;
-    this.position = 'block';
-    this.skipsetposition = false;
-    this.togglesidetabdisplay = '[[togglesidetabdisplay]]';
-    this.toggleblockdisplay = '[[toggleblockdisplay]]';
-    this.sideblockwidth = null;
-    if (window[this.name]) {
-        if (window[this.name].expansions) {
-            this.expansions = window[this.name].expansions;
-        }
-        if (window[this.name].instance) {
-            this.instance = window[this.name].instance;
-        }
-        if (window[this.name].togglesidetabdisplay) {
-            this.togglesidetabdisplay = window[this.name].togglesidetabdisplay;
-        }
-        if (window[this.name].toggleblockdisplay) {
-            this.toggleblockdisplay = window[this.name].toggleblockdisplay;
-        }
-    }
-}
-/**
- * Initialise function used to attach the initial events to the navigation tree
- * This function attachs toggles and ajax calls
- */
-navigation_tree.prototype.initialise = function() {
-    if (!document.getElementById(this.name)) {
-        return;
-    }
-    var e = document.getElementById(this.name);
-    var i = 0;
-    while (!YAHOO.util.Dom.hasClass(e, 'sideblock') && e.nodeName.toUpperCase()!='BODY') {
-        e = e.parentNode;
-    }
-    var movetos = YAHOO.util.Dom.getElementsByClassName('moveto', 'a', e);
-    if (movetos !== null && movetos.length > 0) {
-        for (i = 0;i<movetos.length;i++) {
-            YAHOO.util.Event.addListener(movetos[i], 'click', this.toggle_block_display, this, true);
-        }
-    }
-    for (i = 0; i<this.expansions.length; i++) {
-        try {
-            this.expansions[i].element = document.getElementById(this.expansions[i].id);
-            YAHOO.util.Event.addListener(this.expansions[i].id, 'click', this.init_load_ajax, this.expansions[i], this);
-            YAHOO.moodle.navigation.expandablebranchcount++;
-        } catch (err) {
-            this.errorlog += "attaching ajax load events: \t"+err+"\n";
-        }
-    }
-    var items = YAHOO.util.Dom.getElementsByClassName('tree_item branch', '', document.getElementById(this.name));
-    if (items != null && items.length>0) {
-        for (i = 0; i<items.length; i++) {
-            try {
-                YAHOO.util.Event.addListener(items[i], 'click', this.toggleexpansion, this, true);
-            } catch (err) {
-                this.errorlog += "attaching toggleexpansion events: \t"+err+"\n";
-            }
-        }
-    }
-
-    var customcommands = YAHOO.util.Dom.getElementsByClassName('customcommand', 'a', e);
-    var commands = YAHOO.util.Dom.getElementsByClassName('commands', 'div', e);
-    if (commands.length === 1 && customcommands.length > 0) {
-        for (i = 0; i < customcommands.length; i++) {
-            customcommands[i].parentNode.removeChild(customcommands[i]);
-            commands[0].appendChild(customcommands[i]);
-        }
-    }
-
-    if (YAHOO.util.Dom.hasClass(e, 'sideblock_js_sidebarpopout')) {
-        YAHOO.util.Dom.removeClass(e, 'sideblock_js_sidebarpopout');
-        this.skipsetposition = true;
-        this.toggle_block_display(e, this);
-    } else if (YAHOO.util.Dom.hasClass(e, 'sideblock_js_expansion')) {
-        YAHOO.util.Event.addListener(e, 'mouseover', this.togglesize, e, this);
-        YAHOO.util.Event.addListener(e, 'mouseout', this.togglesize, e, this);
-    }
-}
-/**
- * Toogle a branch either collapsed or expanded... CSS styled
- * @param {object} e Event object
- */
-navigation_tree.prototype.toggleexpansion = function(e) {
-    YAHOO.util.Event.stopPropagation(e);
-    var target = YAHOO.util.Event.getTarget(e);
-    var parent = target.parentNode;
-    while (parent.nodeName.toUpperCase()!='LI') {
-        parent = parent.parentNode;
-    }
-    if (YAHOO.util.Dom.hasClass(parent, 'collapsed')) {
-        YAHOO.util.Dom.removeClass(parent, 'collapsed');
-    } else {
-        YAHOO.util.Dom.addClass(parent, 'collapsed');
-    }
-    if (this.position === 'sidebar') {
-        YAHOO.moodle.navigation.tabpanel.resize_tab();
-    }
-}
-/**
- * Toggles the size on an element by adding/removing the mouseover class
- * @param {object} e Event object
- * @param {element} element The element to add/remove the class from
- */
-navigation_tree.prototype.togglesize = function(e, element) {
-    if (e.type == 'mouseout') {
-        var mp = YAHOO.util.Event.getXY(e);
-        if (mp[0] == -1) {
-            return true;
-        }
-        var ep = YAHOO.util.Dom.getXY(element);
-        ep[2] = ep[0]+element.offsetWidth;
-        ep[3] = ep[1]+element.offsetHeight;
-        var withinrealm = (mp[0] > ep[0] && mp[0] < ep[2] && mp[1] > ep[1] && mp[1] < ep[3]);
-        if (!withinrealm) {
-            YAHOO.util.Event.stopEvent(e);
-            YAHOO.util.Dom.removeClass(element, 'mouseover');
-        }
-    } else {
-        YAHOO.util.Event.stopEvent(e);
-        element.style.width = element.offsetWidth +'px';
-        YAHOO.util.Dom.addClass(element, 'mouseover');
-    }
-    return true;
-}
-/**
- * This function makes the initial call to load a branch of the navigation
- * tree by AJAX
- * @param {object} e Event object
- * @param {object} branch The branch object from navigation_tree::expansions
- * @return {bool}
- */
-navigation_tree.prototype.init_load_ajax = function(e, branch) {
-    YAHOO.util.Event.stopPropagation(e);
-    if (YAHOO.util.Event.getTarget(e).nodeName.toUpperCase() != 'P') {
-        return true;
-    }
-    var postargs = 'elementid='+branch.id+'&id='+branch.branchid+'&type='+branch.type+'&sesskey='+moodle_cfg.sesskey;
-    if (this.instance != null) {
-        postargs += '&instance='+this.instance;
-    }
-    YAHOO.util.Connect.asyncRequest('POST', moodle_cfg.wwwroot+'/lib/ajax/getnavbranch.php', callback={
-        success:function(o) {this.load_ajax(o);},
-        failure:function(o) {this.load_ajax(o);},
-        argument: {gntinstance:this,branch:branch,event:e, target:YAHOO.util.Event.getTarget(e)},
-        scope: this
-    }, postargs);
-    return true;
-}
-/**
- * This function loads a branch returned by AJAX into the XHTML tree structure
- * @param {object} outcome The AJAX response
- * @return {bool}
- */
-navigation_tree.prototype.load_ajax = function(outcome) {
-    // Check the status
-    if (outcome.status!=0 && outcome.responseXML!=null) {
-        var branch = outcome.responseXML.documentElement;
-        if (branch!=null && this.add_branch(branch,outcome.argument.target ,1)) {
-            // If we get here everything worked perfectly
-            YAHOO.util.Event.removeListener(outcome.argument.branch.element, 'click', navigation_tree.prototype.init_load_ajax);
-            if (this.position === 'sidebar') {
-                YAHOO.moodle.navigation.tabpanel.resize_tab();
-            }
-            return true;
-        }
-    }
-    // Something went wrong or there simply wasn't anything more to display
-    // add the emptybranch css class so we can flag it
-    YAHOO.util.Dom.replaceClass(outcome.argument.target, 'branch', 'emptybranch');
-    return false;
-}
-/**
- * This recursive function takes an XML branch and includes it in the tree
- * @param {xmlnode} branchxml The XML node for the branch
- * @param {element} target The target node to add to
- * @param {int} depth The depth we have delved (recusive counter)
- * @return {bool}
- */
-navigation_tree.prototype.add_branch = function(branchxml, target, depth) {
-    var branch = new navigation_tree_branch(this.name);
-    branch.load_from_xml_node(branchxml);
-    if (depth>1) {
-        target = branch.inject_into_dom(target,this);
-    }
-    var dropcount = 5;
-    while (target.nodeName.toUpperCase() !== 'LI') {
-        target = target.parentNode;
-        if (dropcount==0 && moodle_cfg.developerdebug) {
-            return alert("dropped because of exceeding dropcount");
-        }
-        dropcount--;
-    }
-    if (branch.haschildren && branch.mychildren && branch.mychildren.childNodes) {
-        for (var i=0;i<branch.mychildren.childNodes.length;i++) {
-            if (branch.haschildren) {
-                var ul = document.createElement('ul');
-                target.appendChild(ul);
-            }
-            var child = branch.mychildren.childNodes[i];
-            this.add_branch(child, ul, depth+1);
-        }
-    } else if(depth==1) {
-        // If we are here then we got a valid response however there are no children
-        // to display for the branch that we are expanding, thus we will return false
-        // so we can add the emptybranch class
-        return false;
-    }
-    return true;
-}
-/**
- * This switches a navigation block between its block position and the sidebar
- *
- * @param {element} e Event object
- */
-navigation_tree.prototype.toggle_block_display = function(e) {
-    if (e !== null) {
-        YAHOO.util.Event.stopPropagation(e);
-    }
-    if (this.position === 'block') {
-        this.move_to_sidebar_popout(e);
-        this.position = 'sidebar';
-    } else {
-        this.move_to_block_position(e);
-        this.position = 'block';
-    }
-}
-/**
- * This function gets called from {@link navigation_tree.toggle_block_display()}
- * and is responsible for moving the block from the block position to the sidebar
- * @return {bool}
- */
-navigation_tree.prototype.move_to_sidebar_popout = function(e) {
-
-    YAHOO.util.Event.stopEvent(e);
-
-    var element = document.getElementById(this.name).parentNode;
-    if (element == null) {
-        return false;
-    }
-    var tabcontent = document.getElementById(this.name).parentNode;
-    while (!YAHOO.util.Dom.hasClass(element, 'sideblock')) {
-        element = element.parentNode;
-    }
-    this.cachedcontent = element;
-
-    var sideblocknode = element;
-    while (sideblocknode && !YAHOO.util.Dom.hasClass(sideblocknode, 'block-region')) {
-        sideblocknode = sideblocknode.parentNode;
-    }
-
-    var moveto = YAHOO.util.Dom.getElementsByClassName('moveto customcommand', 'a', this.cachedcontent);
-    if (moveto.length > 0) {
-        for (var i=0;i<moveto.length;i++) {
-            var moveicon = moveto[i].getElementsByTagName('img');
-            if (moveicon.length>0) {
-                for (var j=0;j<moveicon.length;j++) {
-                    moveicon[j].src = get_image_url('t/movetoblock', 'moodle');
-                    moveicon[j].setAttribute('alt', this.toggleblockdisplay);
-                    moveicon[j].setAttribute('title', this.toggleblockdisplay);
-                }
-            }
-        }
-    }
-
-    var placeholder = document.createElement('div');
-    placeholder.setAttribute('id', this.name+'_content_placeholder');
-    element.parentNode.replaceChild(placeholder, element);
-    element = null;
-    var tabtitle = this.cachedcontent.getElementsByTagName('h2')[0].cloneNode(true);
-    tabtitle.innerHTML = tabtitle.innerHTML.replace(/([a-zA-Z0-9])/g, "$1<br />");
-    var commands = YAHOO.util.Dom.getElementsByClassName('commands', 'div', this.cachedcontent);
-    var tabcommands = null;
-    if (commands.length > 0) {
-        tabcommands = commands[0];
-    } else {
-        tabcommands = document.createElement('div');
-        YAHOO.util.Dom.addClass(tabcommands, 'commands');
-    }
-
-    if (YAHOO.util.Dom.hasClass(sideblocknode, 'block-region')) {
-        var blocks = YAHOO.util.Dom.getElementsByClassName('sideblock', 'div', sideblocknode);
-        if (blocks.length === 0) {
-            YAHOO.moodle.navigation.sideblockwidth = YAHOO.util.Dom.getStyle(sideblocknode, 'width');
-            YAHOO.util.Dom.setStyle(sideblocknode, 'width', '0px');
-        }
-    }
-
-    if (YAHOO.moodle.navigation.tabpanel === null) {
-        YAHOO.moodle.navigation.tabpanel = new navigation_tab_panel();
-    }
-    YAHOO.moodle.navigation.tabpanel.add_to_tab_panel(this.name, tabtitle, tabcontent, tabcommands);
-    if (!this.skipsetposition) {
-        set_user_preference('nav_in_tab_panel_'+this.name, 1);
-    } else {
-        this.skipsetposition = false;
-    }
-    return true;
-}
-/**
- * This function gets called from {@link navigation_tree.toggle_block_display()}
- * and is responsible for moving the block from the sidebar to the block position
- * @return {bool}
- */
-navigation_tree.prototype.move_to_block_position = function(e) {
-
-    YAHOO.util.Event.stopEvent(e);
-
-    if (this.sideblockwidth !== null) {
-        YAHOO.util.Dom.setStyle(sideblocknode, 'width', this.sideblockwidth);
-        this.sideblockwidth = null;
-    }
-
-    var placeholder = document.getElementById(this.name+'_content_placeholder');
-    if (!placeholder || YAHOO.moodle.navigation.tabpanel == null) {
-        return false;
-    }
-
-    if (YAHOO.moodle.navigation.tabpanel.showntab !== null) {
-        YAHOO.moodle.navigation.tabpanel.hide_tab(e, YAHOO.moodle.navigation.tabpanel.showntab.tabname);
-    }
-
-    var tabcontent = YAHOO.moodle.navigation.tabpanel.get_tab_panel_contents(this.name);
-    this.cachedcontent.appendChild(tabcontent);
-    placeholder.parentNode.replaceChild(this.cachedcontent, placeholder);
-
-    if (YAHOO.moodle.navigation.sideblockwidth !== null) {
-        var sideblocknode = this.cachedcontent;
-        while (sideblocknode && !YAHOO.util.Dom.hasClass(sideblocknode, 'block-region')) {
-            sideblocknode = sideblocknode.parentNode;
-        }
-        if (YAHOO.util.Dom.hasClass(sideblocknode, 'block-region')) {
-            YAHOO.util.Dom.setStyle(sideblocknode, 'width', YAHOO.moodle.navigation.sideblockwidth);
-        }
-    }
-
-    var moveto = YAHOO.util.Dom.getElementsByClassName('moveto customcommand', 'a', this.cachedcontent);
-    if (moveto.length > 0) {
-        for (var i=0;i<moveto.length;i++) {
-            var moveicon = moveto[i].getElementsByTagName('img');
-            if (moveicon.length>0) {
-                for (var j=0;j<moveicon.length;j++) {
-                    moveicon[j].src = get_image_url('t/movetosidetab', 'moodle');
-                    moveicon[j].setAttribute('alt', this.togglesidetabdisplay);
-                    moveicon[j].setAttribute('title', this.togglesidetabdisplay);
-                }
-            }
-        }
-    }
-
-    var commands = YAHOO.util.Dom.getElementsByClassName('commands', 'div', this.cachedcontent);
-    var blocktitle = YAHOO.util.Dom.getElementsByClassName('title', 'div', this.cachedcontent);
-    if (commands.length === 1 && blocktitle.length === 1) {
-        commands[0].parentNode.removeChild(commands[0]);
-        blocktitle[0].appendChild(commands[0]);
-    }
-
-    YAHOO.moodle.navigation.tabpanel.remove_from_tab_panel(this.name);
-
-    var block = this.cachedcontent;
-    while (!YAHOO.util.Dom.hasClass(block, 'sideblock')) {
-        block = block.parentNode;
-    }
-    set_user_preference('nav_in_tab_panel_'+this.name, 0);
-    return true;
-}
-
-/**
- * This class is used to manage the navigation tab panel
- *
- * Through this class you can add, remove, and manage items from the navigation
- * tab panel.
- * Note you only EVER need one of these
- * @constructor
- * @class navigation_tab_panel
- */
-function navigation_tab_panel() {
-    this.tabpanelexists = false;
-    this.tabpanelelementnames = Array();
-    this.tabpanelelementcontents = Array();
-    this.navigationpanel = null;
-    this.tabpanel = null;
-    this.tabpanels = Array();
-    this.tabcount = 0;
-    this.preventhide = false;
-    this.showntab = null;
-}
-/**
- * This creates a tab panel element and injects it into the DOM
- * @method create_tab_panel
- * @return {bool}
- */
-navigation_tab_panel.prototype.create_tab_panel = function () {
-    var navbar  = document.createElement('div');
-    navbar.style.display = 'none';
-    navbar.setAttribute('id', 'sidebarpopup');
-    var navbarspacer = document.createElement('div');
-    navbarspacer.style.height = '10px';
-    navbar.appendChild(navbarspacer);
-    YAHOO.util.Dom.addClass(navbar, 'navigation_bar');
-    if (YAHOO.env.ua.ie > 0 && YAHOO.env.ua.ie < 7) {
-        YAHOO.util.Dom.setStyle(navbar, 'height', YAHOO.util.Dom.getViewportHeight()+'px');
-    }
-
-    var navbarcontrol = document.createElement('div');
-    YAHOO.util.Dom.addClass(navbarcontrol, 'controls');
-    var removeall = document.createElement('img');
-    removeall.setAttribute('src', get_image_url('t/movetoblock', 'moodle'));
-    removeall.setAttribute('title', mstr.moodle.moveallsidetabstoblock);
-    removeall.setAttribute('alt', mstr.moodle.moveallsidetabstoblock);
-    navbarcontrol.appendChild(removeall);
-    navbar.appendChild(navbarcontrol);
-
-    document.getElementsByTagName('body')[0].appendChild(navbar);
-    navbar.appendChild(create_shadow(false, true, true, false));
-    YAHOO.util.Dom.addClass(document.getElementsByTagName('body')[0], 'has_navigation_bar');
-    this.navigationpanel = navbar;
-    this.tabpanelexists = true;
-    navbar.style.display = 'block';
-
-    YAHOO.util.Event.addListener(removeall, 'click', move_all_sidetabs_to_block_position);
-
-    return true;
-}
-/**
- * This removes the tab panel element from the page
- * @method remove_tab_panel
- * @return {bool}
- */
-navigation_tab_panel.prototype.remove_tab_panel = function () {
-    var panel = document.getElementById('sidebarpopup');
-    if (!panel) {
-        return false;
-    }
-    this.tabpanel = null;
-    panel.parentNode.removeChild(panel);
-    this.tabpanelexists = false;
-    this.navigationpanel = null;
-    if (YAHOO.util.Dom.hasClass(document.getElementsByTagName('body')[0], 'has_navigation_bar')) {
-        YAHOO.util.Dom.removeClass(document.getElementsByTagName('body')[0], 'has_navigation_bar')
-    }
-    return true;
-}
-/**
- * This function retrieves the content of a tab in the navigation tab panel
- * @method get_tab_panel_contents
- * @param {string} tabname The name of the tab
- * @return {element} The content element
- */
-navigation_tab_panel.prototype.get_tab_panel_contents = function(tabname) {
-    remove_shadow(this.tabpanelelementcontents[tabname]);
-    return this.tabpanelelementcontents[tabname];
-}
-/**
- * This function adds a tab to the navigation tab panel
- *
- * If you find that it takes a long time to make the initial transaction then I
- * would first check the time that set_user_preference is taking, during development
- * the code needed to be re-jigged because it was taking a very long time to execute
- *
- * @method add_to_tab_panel
- * @param {string} tabname The string name of the tab
- * @param {element} tabtitle The title of the tab
- * @param {element} tabcontent The content for the tab
- * @param {element} tabcommands The commands for the tab
- */
-navigation_tab_panel.prototype.add_to_tab_panel = function (tabname, tabtitle, tabcontent, tabcommands) {
-    if (!this.tabpanelexists) {
-        this.create_tab_panel();
-    }
-
-    var firsttab = (this.tabcount==0);
-
-    var sidetab = document.createElement('div');
-    sidetab.setAttribute('id', tabname+'_sidebarpopup');
-    YAHOO.util.Dom.addClass(sidetab, 'sideblock_tab');
-
-    if (firsttab) {
-        YAHOO.util.Dom.addClass(sidetab, 'firsttab');
-    }
-    var sidetabtitle = document.createElement('div');
-    sidetabtitle.appendChild(tabtitle);
-    sidetabtitle.setAttribute('id', tabname+'_title');
-    YAHOO.util.Dom.addClass(sidetabtitle, 'title');
-    tabcontent.appendChild(create_shadow(true, true, true, false));
-    sidetab.appendChild(sidetabtitle);
-
-    if (tabcommands.childNodes.length>0) {
-        tabcontent.appendChild(tabcommands);
-    }
-
-    this.navigationpanel.appendChild(sidetab);
-
-    var position = YAHOO.util.Dom.getXY(sidetabtitle);
-    position[0] += sidetabtitle.offsetWidth;
-    if (YAHOO.env.ua.ie > 0 && YAHOO.env.ua.ie < 8) {
-        position[0] -= 2;
-    }
-
-    this.tabpanels[tabname] = new YAHOO.widget.Panel('navigation_tab_panel_'+tabname, {
-        close:false,
-        draggable:false,
-        constraintoviewport: false,
-        underlay:"none",
-        visible:false,
-        monitorresize:false,
-        /*context:[tabname+'_title','tl','tr',['configChanged','beforeShow','changeBody']],*/
-        xy:position,
-        autofillheight:'body'});
-    this.tabpanels[tabname].showEvent.subscribe(this.resize_tab, this, true);
-    this.tabpanels[tabname].setBody(tabcontent);
-    this.tabpanels[tabname].render(this.navigationpanel);
-
-    this.tabpanelelementnames[this.tabpanelelementnames.length] = tabname;
-    this.tabpanelelementcontents[tabname] = tabcontent;
-    this.tabcount++;
-
-    YAHOO.util.Event.addListener(sidetab, "mouseover", this.show_tab, tabname, this);
-}
-/**
- * This function handles checking the size, and positioning of the navigaiton
- * panel when expansion events occur, or when the panel is shown, or if the window
- * is resized
- *
- * There are undoubtably some bugs in this little bit of code. For one it relies
- * on the padding set in CSS by the YUI:sam skin, if you are hitting a problem
- * whereby the navigation extends beyond its border, or doesn't fill to its own
- * border check the value assigned to padding for the panel body `.yui_panel .bd`
- *
- * @return {bool}
- */
-navigation_tab_panel.prototype.resize_tab = function () {
-    var screenheight = YAHOO.util.Dom.getViewportHeight();
-    var tabheight = parseInt(this.tabpanels[this.showntab.tabname].body.offsetHeight);
-    var tabtop = parseInt(this.tabpanels[this.showntab.tabname].cfg.getProperty('y'));
-    var titletop = YAHOO.util.Dom.getY(this.showntab.tabname+'_title');
-    var scrolltop = (document.all)?document.body.scrollTop:window.pageYOffset;
-    // This makes sure that the panel is the same height as the tab title to
-    // begin with
-    if (tabtop > (10+scrolltop) && tabtop > (titletop+scrolltop)) {
-        this.tabpanels[this.showntab.tabname].cfg.setProperty('y', titletop+scrolltop);
-    }
-
-    // This makes sure that if the panel is big it is moved up to ensure we don't
-    // have wasted space above the panel
-    if ((tabtop+tabheight)>(screenheight+scrolltop) && tabtop > 10) {
-        tabtop = (screenheight-tabheight-10);
-        if (tabtop<10) {
-            tabtop = 10;
-        }
-        this.tabpanels[this.showntab.tabname].cfg.setProperty('y', tabtop+scrolltop);
-    }
-
-    // This makes the panel constrain to the screen's height if the panel is big
-    if (tabtop <= 10 && ((tabheight+tabtop*2) > screenheight || YAHOO.util.Dom.hasClass(this.tabpanels[this.showntab.tabname].body, 'oversized_content'))) {
-        this.tabpanels[this.showntab.tabname].cfg.setProperty('height', (screenheight-39));
-        YAHOO.util.Dom.setStyle(this.tabpanels[this.showntab.tabname].body, 'height', (screenheight-59)+'px');
-        YAHOO.util.Dom.addClass(this.tabpanels[this.showntab.tabname].body, 'oversized_content');
-    }
-}
-/**
- * This function sets everything up for the show even and then calls the panel's
- * show event once we are happy.
- *
- * This function is responsible for closing any open panels, removing show events
- * so we don't refresh unnessecarily and adding events to trap closing, and resizing
- * events
- *
- * @param {event} e The event that fired to get us here
- * @param {string} tabname The tabname to open
- * @return {bool}
- */
-navigation_tab_panel.prototype.show_tab = function (e, tabname) {
-    if (this.showntab !== null) {
-        this.hide_tab(e, this.showntab.tabname);
-    }
-    this.showntab = {event:e, tabname:tabname};
-    this.tabpanels[tabname].show(e, this.tabpanel);
-    YAHOO.util.Dom.addClass(tabname+'_title', 'active_tab');
-    YAHOO.util.Event.removeListener(tabname+'_sidebarpopup', "mouseover", this.show_tab);
-    YAHOO.util.Event.addListener('navigation_tab_panel_'+tabname, "click", function (e){this.preventhide = true}, this, true);
-    YAHOO.util.Event.addListener(tabname+'_sidebarpopup', "click", this.hide_tab, tabname, this);
-    YAHOO.util.Event.addListener(window, 'resize', this.resize_tab, this, true);
-    YAHOO.util.Event.addListener(document.body, "click", this.hide_tab, tabname, this);
-    return true;
-}
-/**
- * This function closes the open tab and sets the listeners up to handle the show
- * event again
- *
- * @param {event} e The event that fired to get us here
- * @param {string} tabname The tabname to close
- * @return {bool}
- */
-navigation_tab_panel.prototype.hide_tab = function(e, tabname) {
-    if (this.preventhide===true) {
-        this.preventhide = false;
-    } else {
-        this.showntab = null;
-        YAHOO.util.Event.addListener(tabname+'_sidebarpopup', "mouseover", this.show_tab, tabname, this);
-        YAHOO.util.Event.removeListener(window, 'resize', this.resize_tab);
-        YAHOO.util.Event.removeListener(document.body, "click", this.hide_tab);
-        YAHOO.util.Dom.removeClass(tabname+'_title', 'active_tab');
-        this.tabpanels[tabname].hide(e, this.tabpanel);
-    }
-}
-/**
- * This function removes a tab from the navigation tab panel
- * @param {string} tabname
- * @return {bool}
- */
-navigation_tab_panel.prototype.remove_from_tab_panel = function(tabname) {
-    var tab = document.getElementById(tabname+'_sidebarpopup');
-    if (!tab) {
-        return false;
-    }
-    tab.parentNode.removeChild(tab);
-    this.tabpanels[tabname].destroy();
-    this.tabpanels[tabname] = null;
-    this.tabcount--;
-    if (this.tabcount === 0) {
-        this.remove_tab_panel();
-    }
-    return true;
-}
-
-/**
- * Global navigation tree branch object used to parse an XML branch
- * into a usable object, and then to inject it into the DOM
- * @class navigation_tree_branch
- * @constructor
- */
-function navigation_tree_branch(treename) {
-    this.treename = treename;
-    this.myname = null;
-    this.mytitle = null;
-    this.myclass = null;
-    this.myid = null;
-    this.mykey = null;
-    this.mytype = null;
-    this.mylink = null;
-    this.myicon = null;
-    this.myexpandable = null;
-    this.expansionceiling = null;
-    this.myhidden = false;
-    this.haschildren = false;
-    this.mychildren = false;
-}
-/**
- * This function populates the object from an XML branch
- * @param {xmlnode} branch The XML branch to turn into an object
- */
-navigation_tree_branch.prototype.load_from_xml_node = function (branch) {
-    this.myname = null;
-    this.mytitle = branch.getAttribute('title');
-    this.myclass = branch.getAttribute('class');
-    this.myid = branch.getAttribute('id');
-    this.mylink = branch.getAttribute('link');
-    this.myicon = branch.getAttribute('icon');
-    this.mykey = branch.getAttribute('key');
-    this.mytype = branch.getAttribute('type');
-    this.myexpandable = branch.getAttribute('expandable');
-    this.expansionceiling = branch.getAttribute('expansionceiling');
-    this.myhidden = (branch.getAttribute('hidden')=='true');
-    this.haschildren = (branch.getAttribute('haschildren')=='true');
-
-    if (this.myid && this.myid.match(/^expandable_branch_\d+$/)) {
-        YAHOO.moodle.navigation.expandablebranchcount++;
-        this.myid = 'expandable_branch_'+YAHOO.moodle.navigation.expandablebranchcount;
-    }
-
-    for (var i=0; i<branch.childNodes.length;i++) {
-        var node = branch.childNodes[i];
-        switch (node.nodeName.toLowerCase()) {
-            case 'name':
-                this.myname = node.firstChild.nodeValue;
-                break;
-            case 'children':
-                this.mychildren = node;
-        }
-    }
-}
-/**
- * This function injects the node into the navigation tree
- * @param {element} element The branch to inject into {element}
- * @param {navigation_tree} gntinstance The instance of the navigaiton_tree that this branch
- *         is associated with
- * @return {element} The now added node
- */
-navigation_tree_branch.prototype.inject_into_dom = function (element, gntinstance) {
-    var branchli = document.createElement('li');
-    var branchp = document.createElement('p');
-    YAHOO.util.Dom.addClass(branchp, 'tree_item');
-    if ((this.myexpandable !==null || this.haschildren) && this.expansionceiling===null) {
-        YAHOO.util.Dom.addClass(branchp, 'branch');
-        YAHOO.util.Dom.addClass(branchli, 'collapsed');
-        YAHOO.util.Event.addListener(branchp, 'click', gntinstance.toggleexpansion, this, gntinstance);
-        if (this.myexpandable) {
-            YAHOO.util.Event.addListener(branchp, 'click', gntinstance.init_load_ajax, {branchid:this.mykey,id:this.myid,type:this.mytype,element:branchp}, gntinstance);
-        }
-    }
-    if (this.myclass != null) {
-        YAHOO.util.Dom.addClass(branchp, this.myclass);
-    }
-    if (this.myid != null) {
-        branchp.setAttribute('id',this.myid);
-    }
-    var branchicon = false;
-    if (this.myicon != null) {
-        branchicon = document.createElement('img');
-        branchicon.setAttribute('src',this.myicon);
-        branchicon.setAttribute('alt','');
-        this.myname = ' '+this.myname;
-    }
-    if (this.mylink === null) {
-        if (branchicon !== false) {
-            branchp.appendChild(branchicon);
-        }
-        branchp.appendChild(document.createTextNode(this.myname.replace(/\n/g, '<br />')));
-    } else {
-        var branchlink = document.createElement('a');
-        branchlink.setAttribute('title', this.mytitle);
-        branchlink.setAttribute('href', this.mylink);
-        if (branchicon !== false) {
-            branchlink.appendChild(branchicon);
-        }
-        branchlink.appendChild(document.createTextNode(this.myname.replace(/\n/g, '<br />')));
-        if (this.myhidden) {
-            YAHOO.util.Dom.addClass(branchlink, 'dimmed');
-        }
-        branchp.appendChild(branchlink);
-    }
-    branchli.appendChild(branchp);
-    element.appendChild(branchli);
-    return branchli;
-}
-
-/**
- * Creates a new JS instance of a global navigation tree and kicks it into gear
- * @param {string} treename The name of the tree
- */
-function setup_new_navtree(treename) {
-    var key = YAHOO.moodle.navigation.treecollection.length;
-    YAHOO.moodle.navigation.treecollection[key] = new navigation_tree(treename, key);
-    YAHOO.moodle.navigation.treecollection[key].initialise();
-}
-
-/**
- * This function moves all navigation tree instances that are currently
- * displayed in the sidebar back into their block positions
- */
-function move_all_sidetabs_to_block_position(e) {
-    for (var i=0; i<YAHOO.moodle.navigation.treecollection.length;i++) {
-        var navtree = YAHOO.moodle.navigation.treecollection[i];
-        if (navtree.position != 'block') {
-            navtree.move_to_block_position(e);
-        }
-    }
-}
-
-/**
- * This function create a series of DIV's appended to an element to give it a
- * shadow
- * @param {bool} top Displays a top shadow if true
- * @param {bool} right Displays a right shadow if true
- * @param {bool} bottom Displays a bottom shadow if true
- * @param {bool} left Displays a left shadow if true
- * @return {element}
- */
-function create_shadow(top, right, bottom, left) {
-    var shadow = document.createElement('div');
-    YAHOO.util.Dom.addClass(shadow, 'divshadow');
-    if (YAHOO.env.ua.ie > 0 && YAHOO.env.ua.ie < 7) {
-        // IE6 just doest like my shadow...
+// This attaches a shadow to the dock when it has been drawn (added to the page)
+blocks.dock.on('dock:drawcompleted', function() {
+    blocks.dock.node.append(shadow.create(true, true, true, true));
+});
+blocks.dock.on('dock:itemadded', function(item) {
+    item.on('dockeditem:showcomplete', function() {
+        Y.one('#dock_item_panel_'+this.id).append(shadow.create(true, true, true, false));
+    });
+    item.on('dockeditem:hidestart', function() {
+        shadow.remove(Y.one('#dock_item_panel_'+this.id));
+    });
+});
+
+var shadow = {
+    /**
+     * This function create a series of DIV's appended to an element to give it a
+     * shadow
+     * @param {bool} top Displays a top shadow if true
+     * @param {bool} right Displays a right shadow if true
+     * @param {bool} bottom Displays a bottom shadow if true
+     * @param {bool} left Displays a left shadow if true
+     * @return {Y.Node}
+     */
+    create : function(top, right, bottom, left) {
+        var shadow = Y.Node.create('<div class="divshadow"></div>');
+        if (Y.UA.ie > 0 && Y.UA.ie < 7) {
+            // IE 6 doesn't like this shadow method
+            return shadow;
+        }
+        if (top) shadow.append(Y.Node.create('<div class="shadow_top"></div>'));
+        if (right) shadow.append(Y.Node.create('<div class="shadow_right"></div>'));
+        if (bottom) shadow.append(Y.Node.create('<div class="shadow_bottom"></div>'));
+        if (left) shadow.append(Y.Node.create('<div class="shadow_left"></div>'));
+        if (top && left) shadow.append(Y.Node.create('<div class="shadow_top_left"></div>'));
+        if (top && right) shadow.append(Y.Node.create('<div class="shadow_top_right"></div>'));
+        if (bottom && left) shadow.append(Y.Node.create('<div class="shadow_bottom_left"></div>'));
+        if (bottom && right) shadow.append(Y.Node.create('<div class="shadow_bottom_right"></div>'));
         return shadow;
-    }
-    var createShadowDiv = function(cname) {
-        var shadowdiv = document.createElement('div');
-        YAHOO.util.Dom.addClass(shadowdiv, cname);
-        if (YAHOO.env.ua.ie > 0 && YAHOO.env.ua.ie < 7) {
-            // IE version less than 7 doesnt support alpha
-            YAHOO.util.Dom.setStyle(shadowdiv, 'opacity', 0.3);
-        }
-        return shadowdiv;
-    }
-    if (top) shadow.appendChild(createShadowDiv('shadow_top'));
-    if (right) shadow.appendChild(createShadowDiv('shadow_right'));
-    if (bottom) shadow.appendChild(createShadowDiv('shadow_bottom'));
-    if (left) shadow.appendChild(createShadowDiv('shadow_left'));
-    if (top && left) shadow.appendChild(createShadowDiv('shadow_top_left'));
-    if (bottom && left) shadow.appendChild(createShadowDiv('shadow_bottom_left'));
-    if (top && right) shadow.appendChild(createShadowDiv('shadow_top_right'));
-    if (bottom && right) shadow.appendChild(createShadowDiv('shadow_bottom_right'));
-    return shadow;
-}
-/**
- * This function removes any shadows that a node and its children may have
- * @param {element} el The element to remove the shadow from
- * @return {bool}
- */
-function remove_shadow(el) {
-    var shadows = YAHOO.util.Dom.getElementsByClassName('divshadow', 'div', el);
-    if (shadows == null || shadows.length == 0) return true;
-    for (var i=0;i<shadows.length;i++) {
-        shadows[i].parentNode.removeChild(shadows[i]);
-    }
-    return true;
-}
+    },
+    /**
+     * This function removes any shadows that a node and its children may have
+     * @param {Y.Node} node The element to remove the shadow from
+     * @return {bool}
+     */
+    remove : function(node) {
+        node.all('.divshadow').remove();
+    }
+}
\ No newline at end of file
index eddddff..c4b3bae 100644 (file)
@@ -1848,7 +1848,7 @@ a.skip:focus, a.skip:active {
 .block_tree .root_node.leaf {
   padding-left:0px;
 }
-.jsenabled .sideblock_js_sidebarpopout,
+.jsenabled .dock_on_load,
 .jsenabled .block_tree .collapsed ul {
   display: none;
 }
@@ -1871,10 +1871,10 @@ a.skip:focus, a.skip:active {
 }
 /* Navigation and settings block */
 /* This CSS is for the side panel */
-body.has_navigation_bar {
+body.has_dock {
   margin-left:3em;
 }
-.navigation_bar {
+.dock {
   width:30px;
   position:fixed;
   top:0px;
@@ -1886,89 +1886,89 @@ body.has_navigation_bar {
   background-position:100% 0%;
   background-repeat:repeat-y;
 }
-.ie6 .navigation_bar {
+.ie6 .dock {
   position:absolute;
 }
-.ie6 .navigation_bar hr {
+.ie6 .dock hr {
   display:none;
   margin:0px;
   height:0px;
   padding:0px;
 }
-.ie6 .navigation_bar li p {
+.ie6 .dock li p {
     background-color:inherit;
 }
-.navigation_bar .sideblock_tab {
+.dock .dockeditem {
 
 }
-.navigation_bar .sideblock_tab .firsttab {
+.dock .dockeditem .firstdockitem {
   margin-top:1em;
 }
-.navigation_bar .sideblock_tab .title {
+.dock .dockeditem .dockedtitle {
   border-bottom:1px solid #eee;
   border-top:1px solid #ddd;
   cursor:pointer;
 }
-.navigation_bar .bd.oversized_content {
+.dock .bd.oversized_content {
   overflow-y:auto;
   overflow-x:visible;
   height:inherit;
 }
-.navigation_bar .block_tree .current_branch {
+.dock .block_tree .current_branch {
   background-color:#ddd;
 }
-.ie6 .navigation_bar .bd.oversized_content {
+.ie6 .dock .bd.oversized_content {
   width:100%;
 }
-.ie7 .navigation_bar .bd.oversized_content {
+.ie7 .dock .bd.oversized_content {
   width:400px;
 }
-.navigation_bar .bd.oversized_content .content {
+.dock .bd.oversized_content .content {
   margin:6px 6px 6px 0px;
   padding-bottom:6px;
 }
-.ie6 .navigation_bar .bd.oversized_content .content,
-.ie7 .navigation_bar .bd.oversized_content .content {
+.ie6 .dock .bd.oversized_content .content,
+.ie7 .dock .bd.oversized_content .content {
   padding-bottom:0px;
 }
-.navigation_bar .sideblock_tab .title.active_tab {
+.dock .dockeditem .dockedtitle.activeitem {
   background-color:#e9e9e9;
   border-bottom-color:#ccc;
   border-top-color:#ddd;
 }
-.navigation_bar .sideblock_tab .title h2 {
+.dock .dockeditem .dockedtitle h2 {
   font-size:0.8em;
   line-height:100%;
   text-transform:uppercase;
   text-align:center;
 }
-.navigation_bar .controls {
+.dock .controls {
   position:absolute;
   bottom:1em;
   width:100%;
   text-align:center;
 }
-.navigation_bar .controls img {
+.dock .controls img {
   cursor:pointer;
 }
 
 /* Navigation and settings block */
 /* Sideblock expansion code */
-.sideblock_js_expansion .block_tree {
+.block_js_expansion .block_tree {
   overflow-x:scroll;
 }
-.sideblock_js_expansion.mouseover .content {
+.block_js_expansion.mouseover .content {
   width:200%;
   z-index:1000;
   position:relative;
 }
-.sideblock_js_expansion.mouseover .content .block_tree {
+.block_js_expansion.mouseover .content .block_tree {
   width:100%;
   background-color:#fcfcfc;
   padding-bottom:0px;
 }
-.ie6 .sideblock_js_expansion.mouseover .content,
-.ie7 .sideblock_js_expansion.mouseover .content{
+.ie6 .block_js_expansion.mouseover .content,
+.ie7 .block_js_expansion.mouseover .content{
   padding-bottom:2px;
 }