// ########## // An Item is an object that adheres to an interface consisting of these methods: // reloadBounds: function() // getBounds: function(), inherited from Item // setBounds: function(rect, immediately) // setPosition: function(left, top, immediately), inherited from Item // setSize: function(width, height, immediately), inherited from Item // getZ: function(), inherited from Item // setZ: function(value) // close: function() // addOnClose: function(referenceObject, callback) // removeOnClose: function(referenceObject) // // In addition, it must have these properties: // isAnItem, set to true (set by Item) // defaultSize, a Point // bounds, a Rect (set by Item in _init() via reloadBounds()) // debug (set by Item) // $debug (set by Item in _init()) // container, a DOM element (set by Item in _init()) // // Its container must also have a jQuery data named 'item' that points to the item. // This is set by Item in _init(). // Class: Item // Superclass for all visible objects (tabs and groups). window.Item = function() { // Variable: isAnItem // Always true for Items this.isAnItem = true; // Variable: bounds // The position and size of this Item, represented as a . this.bounds = null; // Variable: debug // When set to true, displays a rectangle on the screen that corresponds with bounds. // May be used for additional debugging features in the future. this.debug = false; // Variable: $debug // If is true, this will be the jQuery object for the visible rectangle. this.$debug = null; // Variable: container // The outermost DOM element that describes this item on screen. this.container = null; }; window.Item.prototype = { // ---------- // Function: _init // Initializes the object. To be called from the subclass's intialization function. // // Parameters: // container - the outermost DOM element that describes this item onscreen. _init: function(container) { this.container = container; if(this.debug) { this.$debug = $('
') .css({ border: '2px solid green', zIndex: -10, position: 'absolute' }) .appendTo($('body')); } this.reloadBounds(); $(this.container).data('item', this); }, // ---------- // Function: getBounds // Returns a copy of the Item's bounds as a . getBounds: function() { return new Rect(this.bounds); }, // ---------- // Function: setPosition // Moves the Item to the specified location. // // Parameters: // left - the new left coordinate relative to the window // top - the new top coordinate relative to the window // immediately - if false or omitted, animates to the new position; // otherwise goes there immediately setPosition: function(left, top, immediately) { this.setBounds(new Rect(left, top, this.bounds.width, this.bounds.height), immediately); }, // ---------- // Function: setSize // Resizes the Item to the specified size. // // Parameters: // width - the new width in pixels // height - the new height in pixels // immediately - if false or omitted, animates to the new size; // otherwise resizes immediately setSize: function(width, height, immediately) { this.setBounds(new Rect(this.bounds.left, this.bounds.top, width, height), immediately); }, // ---------- // Function: getZ // Returns the zIndex of the Item. getZ: function() { return parseInt($(this.container).css('zIndex')); }, // ---------- // Function: pushAway // Pushes all other items away so none overlap this Item. pushAway: function() { var buffer = 10; var items = Items.getTopLevelItems(); $.each(items, function(index, item) { var data = {}; data.bounds = item.getBounds(); data.startBounds = new Rect(data.bounds); data.generation = Infinity; item.pushAwayData = data; }); var itemsToPush = [this]; this.pushAwayData.generation = 0; var pushOne = function(baseItem) { var baseData = baseItem.pushAwayData; var bb = new Rect(baseData.bounds); bb.inset(-buffer, -buffer); var bbc = bb.center(); $.each(items, function(index, item) { if(item == baseItem) return; var data = item.pushAwayData; if(data.generation <= baseData.generation) return; var bounds = data.bounds; var box = new Rect(bounds); box.inset(-buffer, -buffer); if(box.intersects(bb)) { var offset = new Point(); var center = box.center(); if(Math.abs(center.x - bbc.x) < Math.abs(center.y - bbc.y)) { if(center.y > bbc.y) offset.y = bb.bottom - box.top; else offset.y = bb.top - box.bottom; } else { if(center.x > bbc.x) offset.x = bb.right - box.left; else offset.x = bb.left - box.right; } bounds.offset(offset); data.generation = baseData.generation + 1; itemsToPush.push(item); } }); }; while(itemsToPush.length) pushOne(itemsToPush.shift()); $.each(items, function(index, item) { var data = item.pushAwayData; if(!data.bounds.equals(data.startBounds)) item.setPosition(data.bounds.left, data.bounds.top); }); }, // ---------- // Function: _updateDebugBounds // Called by a subclass when its bounds change, to update the debugging rectangles on screen. // This functionality is enabled only by the debug property. _updateDebugBounds: function() { if(this.$debug) { this.$debug.css({ left: this.bounds.left, top: this.bounds.top, width: this.bounds.width, height: this.bounds.height }); } } }; // ########## // Class: Items // Keeps track of all Items. window.Items = { // ---------- // Function: item // Given a DOM element representing an Item, returns the Item. item: function(el) { return $(el).data('item'); }, // ---------- // Function: getTopLevelItems // Returns an array of all Items not grouped into groups. getTopLevelItems: function() { var items = []; $('.tab, .group').each(function() { $this = $(this); if(!$this.data('group')) items.push($this.data('item')); }); return items; } };