javascript-dock MDL-21329 Added $CFG->allowblockstodock(true by default) and $THEME...
authorSam Hemelryk <sam@moodle.com>
Thu, 14 Jan 2010 06:56:12 +0000 (06:56 +0000)
committerSam Hemelryk <sam@moodle.com>
Thu, 14 Jan 2010 06:56:12 +0000 (06:56 +0000)
Also moved blocks/blocks.js to javascript-static,js so there is one less file to load

admin/settings/appearance.php
blocks/blocks.js [deleted file]
blocks/global_navigation_tree/block_global_navigation_tree.php
blocks/global_navigation_tree/navigation.js
blocks/moodleblock.class.php
blocks/settings_navigation_tree/block_settings_navigation_tree.php
lang/en_utf8/admin.php
lib/javascript-static.js
lib/outputlib.php
theme/base/javascript/navigation.js
theme/standardold/config.php

index 4cf9db0..941122e 100644 (file)
@@ -16,6 +16,7 @@ if ($hassiteconfig) { // speedup for non-admins, add all caps used on this page
     $temp->add(new admin_setting_configcheckbox('allowcategorythemes',  get_string('allowcategorythemes', 'admin'), get_string('configallowcategorythemes', 'admin'), 0));
     $temp->add(new admin_setting_configcheckbox('allowthemechangeonurl',  get_string('allowthemechangeonurl', 'admin'), get_string('configallowthemechangeonurl', 'admin'), 0));
     $temp->add(new admin_setting_configcheckbox('allowuserblockhiding', get_string('allowuserblockhiding', 'admin'), get_string('configallowuserblockhiding', 'admin'), 1));
+    $temp->add(new admin_setting_configcheckbox('allowblockstodock', get_string('allowblockstodock', 'admin'), get_string('configallowblockstodock', 'admin'), 1));
     $temp->add(new admin_setting_configcheckbox('showblocksonmodpages', get_string('showblocksonmodpages', 'admin'), get_string('configshowblocksonmodpages', 'admin'), 0));
     $temp->add(new admin_setting_configselect('hideactivitytypenavlink', get_string('hideactivitytypenavlink', 'admin'), get_string('confighideactivitytypenavlink', 'admin'), 0,
     array(
diff --git a/blocks/blocks.js b/blocks/blocks.js
deleted file mode 100644 (file)
index 22e2753..0000000
+++ /dev/null
@@ -1,591 +0,0 @@
-// 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 fc790b6..9bb2e48 100644 (file)
@@ -151,9 +151,9 @@ class block_global_navigation_tree extends block_tree {
         // Get the expandable items so we can pass them to JS
         $expandable = array();
         $this->page->navigation->find_expandable($expandable);
-        
+
         // Initialise the JS tree object
-        $args = array($this->instance->id,array('expansions'=>$expandable,'instance'=>$this->instance->id));
+        $args = array($this->instance->id,array('expansions'=>$expandable,'instance'=>$this->instance->id, 'candock'=>$this->instance_can_be_docked()));
         $this->page->requires->js_function_call('blocks.navigation.setup_new_tree',  $args)->on_dom_ready();
         
         // Grab the items to display
@@ -182,17 +182,9 @@ class block_global_navigation_tree extends block_tree {
      */
     public function html_attributes() {
         $attributes = parent::html_attributes();
-
-        if ($this->docked===null) {
-            $this->docked = get_user_preferences('docked_block_instance_'.$this->instance->id, 0);
-        }
-
         if (!empty($this->config->enablehoverexpansion) && $this->config->enablehoverexpansion == 'yes') {
             $attributes['class'] .= ' block_js_expansion';
         }
-        if ($this->docked) {
-            $attributes['class'] .= ' dock_on_load';
-        }
         return $attributes;
     }
 
index 39bd0f2..18cd6e6 100644 (file)
@@ -76,6 +76,7 @@ blocks.navigation.classes.tree = function(id, key, properties) {
     this.cachedfooter = null;
     this.position = 'block';
     this.skipsetposition = false;
+    this.candock = false;
     if (properties) {
         if (properties.expansions) {
             this.expansions = properties.expansions;
@@ -83,6 +84,9 @@ blocks.navigation.classes.tree = function(id, key, properties) {
         if (properties.instance) {
             this.instance = properties.instance;
         }
+        if (properties.candock) {
+            this.candock = true;
+        }
     }
 
     if (Y.one('#inst'+this.id) === null) {
@@ -97,7 +101,9 @@ blocks.navigation.classes.tree = function(id, key, properties) {
     var node = Y.one('#inst'+this.id);
     node.all('.tree_item.branch').on('click', this.toggleexpansion , this);
 
-    this.init(node);
+    if (this.candock) {
+        this.init(node);
+    }
 
     if (node.hasClass('block_js_expansion')) {
         node.on('mouseover', function(e){this.toggleClass('mouseover');}, node);
@@ -140,7 +146,9 @@ blocks.navigation.classes.tree.prototype.load_ajax = function(tid, outcome, args
         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();
+            if (this.candock) {
+                blocks.dock.resize();
+            }
             return true;
         }
     }
@@ -179,7 +187,9 @@ blocks.navigation.classes.tree.prototype.add_branch = function(branchxml, target
  */
 blocks.navigation.classes.tree.prototype.toggleexpansion = function(e) {
     e.target.ancestor('LI').toggleClass('collapsed');
-    blocks.dock.resize();
+    if (this.candock) {
+        blocks.dock.resize();
+    }
 }
 
 /**
@@ -293,6 +303,8 @@ blocks.navigation.classes.branch.prototype.inject_into_dom = function(element) {
 }
 
 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);
+    if (blocks.genericblock) {
+        // Give the tree class the dock block properties
+        Y.augment(blocks.navigation.classes.tree, blocks.genericblock);
+    }
 });
\ No newline at end of file
index 595405c..501fa66 100644 (file)
@@ -547,7 +547,7 @@ class block_base {
             'id' => 'inst' . $this->instance->id,
             'class' => 'block_' . $this->name()
         );
-        if (get_user_preferences('docked_block_instance_'.$this->instance->id, 0)) {
+        if ($this->instance_can_be_docked() && get_user_preferences('docked_block_instance_'.$this->instance->id, 0)) {
             $attributes['class'] .= ' dock_on_load';
         }
         return $attributes;
@@ -576,8 +576,8 @@ class block_base {
     }
 
     function get_required_javascript() {
-        if ($this->instance_can_dock_with_dock()) {
-            $this->_initialise_dock();
+        $this->_initialise_dock();
+        if ($this->instance_can_be_docked()) {
             $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);
         }
@@ -744,19 +744,19 @@ class block_base {
         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 instance_can_be_docked() {
+        global $CFG;
+        return ($CFG->allowblockstodock && $this->page->theme->enable_dock);
     }
 
     public function _initialise_dock() {
         if (!self::$dockinitialised) {
-            $this->page->requires->js('blocks/blocks.js');
+            $this->page->requires->js_function_call('blocks.dock.init')->on_dom_ready();
             $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 */
index 75fee8b..ef51316 100644 (file)
@@ -80,7 +80,8 @@ class block_settings_navigation_tree extends block_tree {
     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();
+        $args = array($this->instance->id, array('instance'=>$this->instance->id, 'candock'=>$this->instance_can_be_docked()));
+        $this->page->requires->js_function_call('blocks.navigation.setup_new_tree', $args)->on_dom_ready();
         user_preference_allow_ajax_update('docked_block_instance_'.$this->instance->id, PARAM_INT);
     }
 
@@ -151,18 +152,9 @@ class block_settings_navigation_tree extends block_tree {
 
     function html_attributes() {
         $attributes = parent::html_attributes();
-
-        // Check if this block has been docked
-        if ($this->docked === null) {
-            $this->docked = get_user_preferences('docked_block_instance_'.$this->instance->id, 0);
-        }
-
         if (!empty($this->config->enablehoverexpansion) && $this->config->enablehoverexpansion == 'yes') {
             $attributes['class'] .= ' block_js_expansion';
         }
-        if ($this->docked) {
-            $attributes['class'] .= ' dock_on_load';
-        }
         return $attributes;
     }
 }
index 60bc3cc..aa1648d 100644 (file)
@@ -6,6 +6,7 @@ $string['adminseesallevents'] = 'Administrators see all events';
 $string['adminseesownevents'] = 'Administrators are just like other users';
 $string['advancedfeatures'] = 'Advanced features';
 $string['allcountrycodes'] = 'All country codes';
+$string['allowblockstodock'] = 'Allow blocks to use the dock';
 $string['allowcategorythemes'] = 'Allow category themes';
 $string['allowcoursethemes'] = 'Allow course themes';
 $string['allowdeletes'] = 'Allow deletes';
@@ -87,6 +88,7 @@ $string['computedfromlogs'] = 'Computed from logs since $a.';
 $string['confeditorhidebuttons'] = 'Select the buttons that should be hidden in the HTML editor.';
 $string['configallcountrycodes'] = 'This is the list of countries that may be selected in various places, for example in a user\'s profile. If blank (the default) the list in countries.php in the standard English language pack is used. That is the list from ISO 3166-1. Otherwise, you can specify a comma-separated list of codes, for example \'GB,FR,ES\'. If you add new, non-standard codes here, you will need to add them to countries.php in your language pack.';
 $string['configallowassign'] = 'You can allow people who have the roles on the left side to assign some of the column roles to other people';
+$string['configallowblockstodock'] = 'If enabled and supported by the selected theme users can choose to moved blocks to a special dock.';
 $string['configallowcategorythemes'] = 'If you enable this, then themes can be set at the category level. This will affect all child categories and courses unless they have specifically set their own theme. WARNING: Enabling category themes may affect performance.';
 $string['configallowcoursethemes'] = 'If you enable this, then courses will be allowed to set their own themes.  Course themes override all other theme choices (site, user, or session themes)';
 $string['configallowemailaddresses'] = 'If you want to restrict all new email addresses to particular domains, then list them here separated by spaces.  All other domains will be rejected.  To allow subdomains add the domain with a preceding \'.\'. eg <strong>ourcollege.edu.au .gov.au</strong>';
index 02f1645..3db6a65 100644 (file)
@@ -1412,3 +1412,588 @@ function get_image_url(imagename, component) {
 function submitFormById(id) {
     submit_form_by_id(null, {id: id});
 }
+
+
+/**
+ * START OF BLOCKS CODE
+ * This code can be included in the footer instead of the header if we ever
+ * have a static JS file that will be loaded in the footer.
+ * Once this is done we will then also be able to remove the blocks.dock.init
+ * function and call
+ */
+
+/**
+ * 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
+        }
+    },
+    /**
+     * Augments the classes as required and processes early bindings
+     */
+    init:function() {
+        Y.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();
+        });
+    },
+    /**
+     * 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);
+                if (!node) {
+                    return;
+                }
+            }
+
+            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;
+
+///////////////// END OF BLOCKS CODE \\\\\\\\\\\\\\\\\\\\\\
\ No newline at end of file
index 1238292..49b3e50 100644 (file)
@@ -316,6 +316,13 @@ class theme_config {
      */
     public $setting = null;
 
+    /**
+     * If set to true and the theme enables the dock then  blocks will be able
+     * to be moved to the special dock
+     * @var bool
+     */
+    public $enable_dock = false;
+
     /**
      * Instance of the renderer_factory implementation
      * we are using. Implementation detail.
@@ -392,7 +399,7 @@ class theme_config {
         }
 
         $configurable = array('parents', 'sheets', 'parents_exclude_sheets', 'plugins_exclude_sheets', 'javascripts', 'javascripts_footer',
-                              'parents_exclude_javascripts', 'layouts', 'resource_mp3player_colors',
+                              'parents_exclude_javascripts', 'layouts', 'resource_mp3player_colors', 'enable_dock',
                               'filter_mediaplugin_colors', 'rendererfactory', 'csspostprocess', 'editor_sheets', 'rarrow', 'larrow');
 
         foreach ($config as $key=>$value) {
index 7e0fbff..9bd934d 100644 (file)
@@ -93,7 +93,7 @@
  */
 
 // If this isn't set we don't need an override at all
-if (blocks.genericblock) {
+if (typeof(blocks)=='object' && blocks.genericblock) {
 
     /**
      * Override the default resize_block_space method so that we can ensure
index 80cf7a4..0577847 100644 (file)
@@ -178,4 +178,6 @@ $THEME->layouts = array(
 
 /** List of javascript files that need to included on each page */
 $THEME->javascripts = array();
-$THEME->javascripts_footer = array('navigation');
\ No newline at end of file
+$THEME->javascripts_footer = array('navigation');
+
+$THEME->enable_dock = true;
\ No newline at end of file