Upgrade to ExtJS 3.3.0 - Released 10/06/2010
[extjs.git] / examples / docs / source / Reorderer.html
diff --git a/examples/docs/source/Reorderer.html b/examples/docs/source/Reorderer.html
new file mode 100644 (file)
index 0000000..f2f55c0
--- /dev/null
@@ -0,0 +1,435 @@
+<html>
+<head>
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />    
+  <title>The source code</title>
+    <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
+    <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
+</head>
+<body  onload="prettyPrint();">
+    <pre class="prettyprint lang-js">/*!
+ * Ext JS Library 3.3.0
+ * Copyright(c) 2006-2010 Ext JS, Inc.
+ * licensing@extjs.com
+ * http://www.extjs.com/license
+ */
+<div id="cls-Ext.ux.Reorderer"></div>/**
+ * @class Ext.ux.Reorderer
+ * @extends Object
+ * Generic base class for handling reordering of items. This base class must be extended to provide the
+ * actual reordering functionality - the base class just sets up events and abstract logic functions.
+ * It will fire events and set defaults, deferring the actual reordering to a doReorder implementation.
+ * See Ext.ux.TabReorderer for an example.
+ */
+Ext.ux.Reorderer = Ext.extend(Object, {
+    <div id="prop-Ext.ux.Reorderer-defaults"></div>/**
+     * @property defaults
+     * @type Object
+     * Object containing default values for plugin configuration details. These can be overridden when
+     * constructing the plugin
+     */
+    defaults: {
+        <div id="cfg-Ext.ux.Reorderer-null"></div>/**
+         * @cfg animate
+         * @type Boolean
+         * If set to true, the rearranging of the toolbar items is animated
+         */
+        animate: true,
+        
+        <div id="cfg-Ext.ux.Reorderer-null"></div>/**
+         * @cfg animationDuration
+         * @type Number
+         * The duration of the animation used to move other toolbar items out of the way
+         */
+        animationDuration: 0.2,
+        
+        <div id="cfg-Ext.ux.Reorderer-null"></div>/**
+         * @cfg defaultReorderable
+         * @type Boolean
+         * True to make every toolbar draggable unless reorderable is specifically set to false.
+         * This defaults to false
+         */
+        defaultReorderable: false
+    },
+    
+    <div id="method-Ext.ux.Reorderer-constructor"></div>/**
+     * Creates the plugin instance, applies defaults
+     * @constructor
+     * @param {Object} config Optional config object
+     */
+    constructor: function(config) {
+        Ext.apply(this, config || {}, this.defaults);
+    },
+    
+    <div id="method-Ext.ux.Reorderer-init"></div>/**
+     * Initializes the plugin, stores a reference to the target 
+     * @param {Mixed} target The target component which contains the reorderable items
+     */
+    init: function(target) {
+        <div id="prop-Ext.ux.Reorderer-target"></div>/**
+         * @property target
+         * @type Ext.Component
+         * Reference to the target component which contains the reorderable items
+         */
+        this.target = target;
+        
+        this.initEvents();
+        
+        var items  = this.getItems(),
+            length = items.length,
+            i;
+        
+        for (i = 0; i < length; i++) {
+            this.createIfReorderable(items[i]);
+        }
+    },
+    
+    <div id="method-Ext.ux.Reorderer-reorder"></div>/**
+     * Reorders the items in the target component according to the given mapping object. Example:
+     * this.reorder({
+     *     1: 5,
+     *     3: 2
+     * });
+     * Would move the item at index 1 to index 5, and the item at index 3 to index 2
+     * @param {Object} mappings Object containing current item index as key and new index as property
+     */
+    reorder: function(mappings) {
+        var target = this.target;
+        
+        if (target.fireEvent('before-reorder', mappings, target, this) !== false) {
+            this.doReorder(mappings);
+            
+            target.fireEvent('reorder', mappings, target, this);
+        }
+    },
+    
+    <div id="method-Ext.ux.Reorderer-doReorder"></div>/**
+     * Abstract function to perform the actual reordering. This MUST be overridden in a subclass
+     * @param {Object} mappings Mappings of the old item indexes to new item indexes
+     */
+    doReorder: function(paramName) {
+        throw new Error("doReorder must be implemented in the Ext.ux.Reorderer subclass");
+    },
+    
+    /**
+     * Should create and return an Ext.dd.DD for the given item. This MUST be overridden in a subclass
+     * @param {Mixed} item The item to create a DD for. This could be a TabPanel tab, a Toolbar button, etc
+     * @return {Ext.dd.DD} The DD for the given item
+     */
+    createItemDD: function(item) {
+        throw new Error("createItemDD must be implemented in the Ext.ux.Reorderer subclass");
+    },
+    
+    <div id="method-Ext.ux.Reorderer-createItemDD"></div>/**
+     * Sets up the given Toolbar item as a draggable
+     * @param {Mixed} button The item to make draggable (usually an Ext.Button instance)
+     */
+    createItemDD: function(button) {
+        var el   = button.getEl(),
+            id   = el.id,
+            tbar = this.target,
+            me   = this;
+        
+        button.dd = new Ext.dd.DD(el, undefined, {
+            isTarget: false
+        });
+        
+        button.dd.constrainTo(tbar.getEl());
+        button.dd.setYConstraint(0, 0, 0);
+        
+        Ext.apply(button.dd, {
+            b4StartDrag: function() {       
+                this.startPosition = el.getXY();
+                
+                //bump up the z index of the button being dragged but keep a reference to the original
+                this.startZIndex = el.getStyle('zIndex');
+                el.setStyle('zIndex', 10000);
+                
+                button.suspendEvents();
+            },
+            
+            onDrag: function(e) {
+                //calculate the button's index within the toolbar and its current midpoint
+                var buttonX  = el.getXY()[0],
+                    deltaX   = buttonX - this.startPosition[0],
+                    items    = tbar.items.items,
+                    oldIndex = items.indexOf(button),
+                    newIndex;
+                
+                //find which item in the toolbar the midpoint is currently over
+                for (var index = 0; index < items.length; index++) {
+                    var item = items[index];
+                    
+                    if (item.reorderable && item.id != button.id) {
+                        //find the midpoint of the button
+                        var box        = item.getEl().getBox(),
+                            midpoint   = (me.buttonXCache[item.id] || box.x) + (box.width / 2),
+                            movedLeft  = oldIndex > index && deltaX < 0 && buttonX < midpoint,
+                            movedRight = oldIndex < index && deltaX > 0 && (buttonX + el.getWidth()) > midpoint;
+                        
+                        if (movedLeft || movedRight) {
+                            me[movedLeft ? 'onMovedLeft' : 'onMovedRight'](button, index, oldIndex);
+                            break;
+                        }                        
+                    }
+                }
+            },
+            
+            <div id="method-Ext.ux.Reorderer-endDrag"></div>/**
+             * After the drag has been completed, make sure the button being dragged makes it back to
+             * the correct location and resets its z index
+             */
+            endDrag: function() {
+                //we need to update the cache here for cases where the button was dragged but its
+                //position in the toolbar did not change
+                me.updateButtonXCache();
+                
+                el.moveTo(me.buttonXCache[button.id], undefined, {
+                    duration: me.animationDuration,
+                    scope   : this,
+                    callback: function() {
+                        button.resumeEvents();
+                        
+                        tbar.fireEvent('reordered', button, tbar);
+                    }
+                });
+                
+                el.setStyle('zIndex', this.startZIndex);
+            }
+        });
+    },
+    
+    /**
+     * @private
+     * Creates a DD instance for a given item if it is reorderable
+     * @param {Mixed} item The item
+     */
+    createIfReorderable: function(item) {
+        if (this.defaultReorderable && item.reorderable == undefined) {
+            item.reorderable = true;
+        }
+        
+        if (item.reorderable && !item.dd) {
+            if (item.rendered) {
+                this.createItemDD(item);                
+            } else {
+                item.on('render', this.createItemDD.createDelegate(this, [item]), this, {single: true});
+            }
+        }
+    },
+    
+    <div id="method-Ext.ux.Reorderer-getItems"></div>/**
+     * Returns an array of items which will be made draggable. This defaults to the contents of this.target.items,
+     * but can be overridden - e.g. for TabPanels
+     * @return {Array} The array of items which will be made draggable
+     */
+    getItems: function() {
+        return this.target.items.items;
+    },
+    
+    <div id="method-Ext.ux.Reorderer-initEvents"></div>/**
+     * Adds before-reorder and reorder events to the target component
+     */
+    initEvents: function() {
+        this.target.addEvents(
+          <div id="event-Ext.ux.Reorderer-before-reorder"></div>/**
+           * @event before-reorder
+           * Fires before a reorder occurs. Return false to cancel
+           * @param {Object} mappings Mappings of the old item indexes to new item indexes
+           * @param {Mixed} component The target component
+           * @param {Ext.ux.TabReorderer} this The plugin instance
+           */
+          'before-reorder',
+          
+          <div id="event-Ext.ux.Reorderer-reorder"></div>/**
+           * @event reorder
+           * Fires after a reorder has occured.
+           * @param {Object} mappings Mappings of the old item indexes to the new item indexes
+           * @param {Mixed} component The target component
+           * @param {Ext.ux.TabReorderer} this The plugin instance
+           */
+          'reorder'
+        );
+    }
+});
+
+<div id="cls-Ext.ux.HBoxReorderer"></div>/**
+ * @class Ext.ux.HBoxReorderer
+ * @extends Ext.ux.Reorderer
+ * Description
+ */
+Ext.ux.HBoxReorderer = Ext.extend(Ext.ux.Reorderer, {
+    <div id="method-Ext.ux.HBoxReorderer-init"></div>/**
+     * Initializes the plugin, decorates the container with additional functionality
+     */
+    init: function(container) {
+        <div id="prop-Ext.ux.HBoxReorderer-buttonXCache"></div>/**
+         * This is used to store the correct x value of each button in the array. We need to use this
+         * instead of the button's reported x co-ordinate because the buttons are animated when they move -
+         * if another onDrag is fired while the button is still moving, the comparison x value will be incorrect
+         */
+        this.buttonXCache = {};
+        
+        container.on({
+            scope: this,
+            add  : function(container, item) {
+                this.createIfReorderable(item);
+            }
+        });
+        
+        //super sets a reference to the toolbar in this.target
+        Ext.ux.HBoxReorderer.superclass.init.apply(this, arguments);
+    },
+    
+    <div id="method-Ext.ux.HBoxReorderer-createItemDD"></div>/**
+     * Sets up the given Toolbar item as a draggable
+     * @param {Mixed} button The item to make draggable (usually an Ext.Button instance)
+     */
+    createItemDD: function(button) {
+        if (button.dd != undefined) {
+            return;
+        }
+        
+        var el   = button.getEl(),
+            id   = el.id,
+            me   = this,
+            tbar = me.target;
+        
+        button.dd = new Ext.dd.DD(el, undefined, {
+            isTarget: false
+        });
+        
+        el.applyStyles({
+            position: 'absolute'
+        });
+        
+        //if a button has a menu, it is disabled while dragging with this function
+        var menuDisabler = function() {
+            return false;
+        };
+        
+        Ext.apply(button.dd, {
+            b4StartDrag: function() {       
+                this.startPosition = el.getXY();
+                
+                //bump up the z index of the button being dragged but keep a reference to the original
+                this.startZIndex = el.getStyle('zIndex');
+                el.setStyle('zIndex', 10000);
+                
+                button.suspendEvents();
+                if (button.menu) {
+                    button.menu.on('beforeshow', menuDisabler, me);
+                }
+            },
+            
+            startDrag: function() {
+                this.constrainTo(tbar.getEl());
+                this.setYConstraint(0, 0, 0);
+            },
+            
+            onDrag: function(e) {
+                //calculate the button's index within the toolbar and its current midpoint
+                var buttonX  = el.getXY()[0],
+                    deltaX   = buttonX - this.startPosition[0],
+                    items    = tbar.items.items,
+                    length   = items.length,
+                    oldIndex = items.indexOf(button),
+                    newIndex, index, item;
+                
+                //find which item in the toolbar the midpoint is currently over
+                for (index = 0; index < length; index++) {
+                    item = items[index];
+                    
+                    if (item.reorderable && item.id != button.id) {
+                        //find the midpoint of the button
+                        var box        = item.getEl().getBox(),
+                            midpoint   = (me.buttonXCache[item.id] || box.x) + (box.width / 2),
+                            movedLeft  = oldIndex > index && deltaX < 0 && buttonX < midpoint,
+                            movedRight = oldIndex < index && deltaX > 0 && (buttonX + el.getWidth()) > midpoint;
+                        
+                        if (movedLeft || movedRight) {
+                            me[movedLeft ? 'onMovedLeft' : 'onMovedRight'](button, index, oldIndex);
+                            break;
+                        }                        
+                    }
+                }
+            },
+            
+            <div id="method-Ext.ux.HBoxReorderer-endDrag"></div>/**
+             * After the drag has been completed, make sure the button being dragged makes it back to
+             * the correct location and resets its z index
+             */
+            endDrag: function() {
+                //we need to update the cache here for cases where the button was dragged but its
+                //position in the toolbar did not change
+                me.updateButtonXCache();
+                
+                el.moveTo(me.buttonXCache[button.id], el.getY(), {
+                    duration: me.animationDuration,
+                    scope   : this,
+                    callback: function() {
+                        button.resumeEvents();
+                        if (button.menu) {
+                            button.menu.un('beforeshow', menuDisabler, me);
+                        }
+                        
+                        tbar.fireEvent('reordered', button, tbar);
+                    }
+                });
+                
+                el.setStyle('zIndex', this.startZIndex);
+            }
+        });
+    },
+    
+    onMovedLeft: function(item, newIndex, oldIndex) {
+        var tbar   = this.target,
+            items  = tbar.items.items,
+            length = items.length,
+            index;
+        
+        if (newIndex != undefined && newIndex != oldIndex) {
+            //move the button currently under drag to its new location
+            tbar.remove(item, false);
+            tbar.insert(newIndex, item);
+            
+            //set the correct x location of each item in the toolbar
+            this.updateButtonXCache();
+            for (index = 0; index < length; index++) {
+                var obj  = items[index],
+                    newX = this.buttonXCache[obj.id];
+                
+                if (item == obj) {
+                    item.dd.startPosition[0] = newX;
+                } else {
+                    var el = obj.getEl();
+                    
+                    el.moveTo(newX, el.getY(), {
+                        duration: this.animationDuration
+                    });
+                }
+            }
+        }
+    },
+    
+    onMovedRight: function(item, newIndex, oldIndex) {
+        this.onMovedLeft.apply(this, arguments);
+    },
+    
+    /**
+     * @private
+     * Updates the internal cache of button X locations. 
+     */
+    updateButtonXCache: function() {
+        var tbar   = this.target,
+            items  = tbar.items,
+            totalX = tbar.getEl().getBox(true).x;
+            
+        items.each(function(item) {
+            this.buttonXCache[item.id] = totalX;
+
+            totalX += item.getEl().getWidth();
+        }, this);
+    }
+});</pre>    
+</body>
+</html>
\ No newline at end of file