var toolbar = null;

function handleLoad()
{
    toolbar = new Bar();
    document.body.appendChild(toolbar.element);    
    
    var button1 = new BookmarkBarButton("0", false, "");
    var button2 = new BookmarkBarButton("1", false, "Yahoo");
    var button3 = new BookmarkBarButton("2", false, "Google");
    var button4 = new BookmarkBarButton("3", false, "New York Times");
    var button5 = new BookmarkBarButton("4", false, "Amazon: Online Shopping for Electronics, Apparel, etc");
    var button6 = new BookmarkBarButton("5", false, "Los Angeles Times");
    toolbar.appendButton(button1);
    toolbar.appendButton(button2);
    toolbar.appendButton(button3);
    toolbar.appendButton(button4);
    toolbar.appendButton(button5);
    toolbar.appendButton(button6);
}

String.prototype.escapeCharacters = function(chars)
{
    var foundChar = false;
    for (var i = 0; i < chars.length; ++i) {
        if (this.indexOf(chars.charAt(i)) !== -1) {
            foundChar = true;
            break;
        }
    }

    if (!foundChar)
        return this;

    var result = "";
    for (var i = 0; i < this.length; ++i) {
        if (chars.indexOf(this.charAt(i)) !== -1)
            result += "\\";
        result += this.charAt(i);
    }

    return result;
}

String.prototype.format = function()
{
    var stringParts = this.split("%@");
    for (var i = 0; i < arguments.length; ++i)
        stringParts.splice(i * 2 + 1, 0, arguments[i].toString());
    return stringParts.join("");
}

String.prototype.escapeForRegExp = function()
{
    return this.escapeCharacters("^[]{}()\\.$*+?|");
}

Element.prototype.removeStyleClass = function(className) 
{
    // Test for the simple case before using a RegExp.
    if (this.className === className) {
        this.className = "";
        return;
    }

    var regex = new RegExp("(^|\\s+)" + className.escapeForRegExp() + "($|\\s+)");
    if (regex.test(this.className))
        this.className = this.className.replace(regex, " ");
}

Element.prototype.addStyleClass = function(className) 
{
    if (className && !this.hasStyleClass(className))
        this.className += (this.className.length ? " " + className : className);
}

Element.prototype.hasStyleClass = function(className) 
{
    if (!className)
        return false;
    // Test for the simple case before using a RegExp.
    if (this.className === className)
        return true;
    var regex = new RegExp("(^|\\s)" + className.escapeForRegExp() + "($|\\s)");
    return regex.test(this.className);
}

Element.prototype.firstParentOrSelfWithStyleClass = function(className)
{
    var ele = this;
    while (ele && !ele.hasStyleClass(className))
        ele = ele.parentNode;
    return ele;
}

Element.prototype.removeChildren = function()
{
    while (this.firstChild) 
        this.removeChild(this.firstChild);        
}

Element.prototype.__defineGetter__("totalOffsetLeft", function()
{
    var total = 0;
    for (var element = this; element; element = element.offsetParent)
        total += element.offsetLeft;
    return total;
});

Element.prototype.__defineGetter__("totalOffsetTop", function()
{
    var total = 0;
    for (var element = this; element; element = element.offsetParent)
        total += element.offsetTop;
    return total;
});

Element.prototype.__defineGetter__("totalScrollTop", function()
{
    var total = 0;
    for (var element = this; element; element = element.offsetParent)
        total += element.scrollTop;
    return total;
});

// JS class inheritance
var JSClass = {
    inherit: function(subclass, baseClass) {
        function inheritance() { }
        inheritance.prototype = baseClass.prototype;

        subclass.prototype = new inheritance();
        subclass.prototype.constructor = subclass;
        subclass.baseConstructor = baseClass;
        subclass.superClass = baseClass.prototype;
    }
}

// Function binding
Function.prototype.bind = function(thisObject)
{
    var func = this;
    var args = Array.prototype.slice.call(arguments, 1);
    return function() { return func.apply(thisObject, args.concat(Array.prototype.slice.call(arguments, 0))) };
}

// Toolbar class

// We haven't handled many things yet.  The major ones inclue:
// - overflow (chevron)
// - dropdown buttons
// - the sliding animation when reordering button horizontally
function Bar()
{
    this.element = document.createElement("div");
    this.element.addStyleClass("bar");
    this.element.bar = this;
    this._buttons = [];
	this.element.addEventListener("mousedown", this.startButtonMove.bind(this), true);
}

Bar.prototype = {

    get buttons() {
        return this._buttons;
    },
    
    set buttons(x) {
		for (var i = 0; i < this._buttons.length; ++i)
			this.removeButton(this._buttons[i]);

		this._buttons = [];
		this.element.removeChildren();

		for (var i = 0; i < x.length; ++i)
			this.appendButton(x[i]);
    },

	indexForButton: function(button) {
		if ("index" in button && this._buttons[button.index] === button)
			return button.index;

		for (var i = 0; i < this._buttons.length; ++i) {
			if (this._buttons[i] === button) {
				button.index = i;
				return i;
			}
		}

		return -1;
	},

	appendButton: function(button) {
		if (!button)
			throw "button can't be undefined or null";

		button.index = this._buttons.length;

		this._buttons.push(button);
		this.element.appendChild(button.element);
	},
	
	insertButtonBefore: function(button, beforeIndex) {
		if (!button)
			throw "button can't be undefined or null";

        var buttonBefore = (beforeIndex >= 0 && beforeIndex < this._buttons.length) ? this._buttons[beforeIndex] : null;
        this._buttons.splice(beforeIndex, 0, button);
        this.element.insertBefore(button.element, buttonBefore ? buttonBefore.element : null);
	},

	removeButton: function(button) {
		if (!button)
			throw "button can't be undefined or null";

		var index = this.indexForButton(button);
		if (index !== -1)
			this._buttons.splice(index, 1);

		if (button.element.parentNode === this.element)
			this.element.removeChild(button.element);

		delete button.index;
	},

	startButtonMove: function(event) {
		if (this.buttonMoveEventListener || this.endButtonMoveEventListener)
			return this.endButtonMove(event);

		var buttonElement = event.target.firstParentOrSelfWithStyleClass("bar-button");
		if (!buttonElement || !buttonElement.button)
			return;

        // Cannot move fixed buttons.			
	    if (buttonElement.button.positionIsFixed)
	        return;

		this.movingButton = buttonElement.button;
		this.movingButtonOverBar = true;

		this.buttonMoveEventListener = this.buttonMove.bind(this);
		this.endButtonMoveEventListener = this.endButtonMove.bind(this);

		document.addEventListener("mousemove", this.buttonMoveEventListener, true);
		document.addEventListener("mouseup", this.endButtonMoveEventListener, true);

		event.preventDefault();
	},
	
	buttonMove: function(event) {
		this.movingButton.element.addStyleClass("dragging");

		if (!this.movingButtonPlaceholderElement) {
			this.movingButtonPlaceholderElement = document.createElement("div");
			this.movingButtonPlaceholderElement.className = "button-placeholder";
			var buttonWidth = this.movingButton.element.offsetWidth;
			this.movingButtonPlaceholderElement.style.width = buttonWidth + "px";
			this.element.insertBefore(this.movingButtonPlaceholderElement, this.movingButton.element);
			this.removeButton(this.movingButton);

    		this.buttonMovedOutOfBarEventListener = this.buttonOut.bind(this);
    		this.element.addEventListener("mouseout", this.buttonMovedOutOfBarEventListener, true);

            this.buttonMovedIntoBarEventListener = this.buttonOver.bind(this);
            this.element.addEventListener("mouseover", this.buttonMovedIntoBarEventListener, true);
		}
	},

	buttonOut: function(event) {
	    // We have dragged the button out of the bar
        this.movingButtonPlaceholderElement.addStyleClass("out-of-bar");

		this.movingButtonOverBar = false;
	},
	
	buttonOver: function(event) {
	    if (!this.movingButtonPlaceholderElement)
	        throw("We should have created a placeholder at this point");
	        
	    // We have dragged the button into the bar
	    if (!this.movingButtonOverBar) {
	        this.movingButtonPlaceholderElement.removeStyleClass("out-of-bar");
		    this.movingButtonOverBar = true;
		}

		// Figure out where to insert the placeholder
		if (event.target === this.movingButtonPlaceholderElement)
		    return;
		    
		var x = event.clientX;
		var y = event.clientY;
		
		for (var i = 0; i < this._buttons.length; ++i) {
		    if (this._buttons[i].positionIsFixed)
		        continue;
		        
		    if (x < this._buttons[i].element.offsetLeft + (this._buttons[i].element.offsetWidth / 2)) {
		        this.element.insertBefore(this.movingButtonPlaceholderElement, this._buttons[i].element);
		        return;
		    }
		}
		// Insert it in the end
		this.element.appendChild(this.movingButtonPlaceholderElement);
	},

	endButtonMove: function(event) {
		document.removeEventListener("mousemove", this.buttonMoveEventListener, true);
		document.removeEventListener("mouseup", this.endButtonMoveEventListener, true);
		this.element.removeEventListener("mouseout", this.buttonMovedOutOfBarEventListener, true);
        this.element.removeEventListener("mouseover", this.buttonMovedIntoBarEventListener, true);

		this.movingButton.element.removeStyleClass("dragging");

		if (this.movingButtonPlaceholderElement) {
		    if (this.movingButtonOverBar) {
                var elementAfterPlaceholder = this.movingButtonPlaceholderElement.nextSibling;

                var insertIndex = (elementAfterPlaceholder && elementAfterPlaceholder.button) ? this.indexForButton(elementAfterPlaceholder.button) : -1;
                this.insertButtonBefore(this.movingButton, insertIndex);
		    }
		    this.element.removeChild(this.movingButtonPlaceholderElement);
		    delete this.movingButtonPlaceholderElement;
		}
		    
		delete this.buttonMoveEventListener;
		delete this.endButtonMoveEventListener;
        delete this.buttonMovedOutOfBarEventListener;
        delete this.buttonMovedIntoBarEventListener;
        
        this.movingButton = null;

		event.preventDefault();
	},

	get overflow() {
		return this._overflow;
	},

}

// BarButton class
function BarButton(id, dropdown, text)
{
    this.id = id;
    this.dropdown = dropdown;
    this.text = text;
    
    this.element = document.createElement("div");
    this.element.addStyleClass("bar-button");
    this.element.button = this;
    this.element.innerText = text;
    if (dropdown)
        this.element.addStyleClass("dropdown");
}

// BookmarkBarButton - inherits BarButton
function BookmarkBarButton(id, dropdown, text)
{
    BookmarkBarButton.baseConstructor.call(this, id, dropdown, text);
    
    if (text == "") {
        this.element.id = "book-button";
        this.element.innerHTML = "&nbsp;";
        this.positionIsFixed = true;
    }
}

JSClass.inherit(BookmarkBarButton, BarButton);
