﻿// JScript File
Type.registerNamespace("Aurigma");

Aurigma.PsdEditor = function() {
	Aurigma.PsdEditor.initializeBase(this);
	this._canvasId = "";
	this._canvasViewerId = null;
	
	// Delegates
	this._currentVObjectChangedDelegate = null;
	
	this._layersListClickDelegate = null;
	this._layersListMouseDownDelegate = null;
	this._layersListMouseUpDelegate = null;
	this._layersListMouseMoveDelegate = null;
	
	this._startIndex = -1;
	this._endIndex = -1;
	this._mouseDownFlag = false;
	this._startLayerPoint = null;
	this._startMousePoint = null;
	
	this._designerInputChangingDelegate = null;
	//this._updateDelayedPropertiesDelegate = null;
	this._onAddingVImageDelegate = null;
	this._onAddingVTextDelegate = null;
	this._onApplyButtonClickDelegate = null;
	this._addVLineDelegate = null;
	this._addVRectangleDelegate = null;
	this._addVEllipseDelegate = null;
	
	this._showLayerMenuDelegate = null;
	this._hideLayerMenuDelegate = null;
	this._setHideMenuTimeoutDelegate = null;
	this._clearHideMenuTimeoutDelegate = null;
	
	this._applyButtonId = null;
	this._applyButtonPanelId = null;
	this._psdEditorPanelId = null;
	this._statusPanelId = null;
	
	this._statusChangedDelegate = null;
	
	this._textDesignerPanelId = null;
	this._commonDesignerPanelId = null;
	this._layersListId = null;
	
	//new layer menu button
	this._addLayerId = null;
	
	//new layer menu
	this._newLayerMenuId = null;
	
	//new layer menu elements
	this._addImageId = null;
	this._addTextId = null;	
	this._addLineId = null;
	this._addRectangleId = null;
	this._addEllipseId = null;
	
	// Text designer.
//	this._textNameId = null;
	this._textValueId = null;
	this._fontDropDownListId = null;
	//this._textFontNameId = null;
	this._textFontSizeId = null;
	this._textColorId = null;
	this._textFillColorId = null;
	this._textBorderWidthId = null;
	this._textBorderColorId = null;
	
	this._textValueEnteredDelegate = null;
	this._setTextValueFocusDelegate = null;
	
	// Common designer elements
//	this._commonNameId = null;
	this._commonBorderWidthId = null;
	this._commonFillColorId = null;
	this._commonBorderColorId = null;
	this._commonWidthId = null;
	this._commonColorId = null;
	
	// Zoom mode
	this._zoomModeSelectId = null;
	this._zoomChangedDelegate = null;
	this._manualZoomChanging = true;
	
	this._modalDialogPanelId = null;
	
	// Redo undo
	this._redoButtonId = null;
	this._undoButtonId = null;
	this._redoUndoChangedDelegate = null;
	
	// Navigators
	this._noneNavigatorButtonId = null;
	this._panNavigatorButtonId = null;
	this._zoomInNavigatorButtonId = null;
	this._zoomOutNavigatorButtonId = null;
	this._panNavigatorId = null;
	this._zoomInNavigatorId = null;
	this._zoomOutNavigatorId = null;
	this._navigators = [];
	this._navigatorButtonClickDelegate = null;
	
	// Bold italic underline
	this._textBoldId = null;
	this._textItalicId = null;
	this._textUnderlineId = null;
	
	// Original font
	this._textCustomFontId = null;
	this._textCustomFontClickDelegate = null;
	this._pulldownId = null;
	
	this._pulldownHeaderId = null;
	this._textBoldLabelId = null;
	this._textItalicLabelId = null;
	this._textUnderlineLabelId = null;
	
	this._alignmentPanelId = null;
	this._alignmentLeftId = null;
	this._alignmentCenterId = null;
	this._alignmentRightId = null;
	this._alignmentLabelId = null;
	
	this._objectAlignmentPanelId = null;
	this._leftAlignmentButtonId = null;
	this._rightAlignmentButtonId = null;
	this._topAlignmentButtonId = null;
	this._bottomAlignmentButtonId = null;
	this._horizontalAlignmentButtonId = null;
	this._verticalAlignmentButtonId = null;
	
	this._moveUpLayerButtonId = null;
	this._moveDownLayerButtonId = null;
	this._moveTopLayerButtonId = null;
	this._moveBottomLayerButtonId = null;
	this._moveLayerClickDelegate = null;
	
	this._props = [];
	
	this._delayedUpdate = [];
	this._updateDelay = 1000;
	this._timeout = null;
}

Aurigma.PsdEditor.prototype = {

    _getAlignment: function () {
        var ids = [this._alignmentLeftId, this._alignmentCenterId, this._alignmentRightId];
        var j = Aurigma.GraphicsMill.AjaxControls.VectorObjects.TextAlignment;
        var just = [j.Left, j.Center, j.Right];
        var classes = ["AlignmentLeftSelected", "AlignmentCenterSelected", "AlignmentRightSelected"];
        for (var i = 0; i < ids.length; i++) {
            if ($get(ids[i]).className == classes[i])
                return just[i];
        }
        return 0;
    },

    _setAlignment: function (alignment) {
        var ids = [this._alignmentLeftId, this._alignmentCenterId, this._alignmentRightId];
        var j = Aurigma.GraphicsMill.AjaxControls.VectorObjects.TextAlignment;
        var just = [j.Left, j.Center, j.Right];
        var classesSelected = ["AlignmentLeftSelected", "AlignmentCenterSelected", "AlignmentRightSelected"];
        var classes = ["AlignmentLeft", "AlignmentCenter", "AlignmentRight"];
        for (var i = 0; i < ids.length; i++) {
            if (just[i] == alignment) {
                $get(ids[i]).className = classesSelected[i];
            }
            else {
                $get(ids[i]).className = classes[i];
            }
        }
    },

    set_canvasId: function (v) {
        this._canvasId = v;
    },

    get_canvasId: function () {
        return this._canvasId;
    },

    get_canvas: function () {
        if (this.get_canvasId()) {
            var cv = $find(this.get_canvasId());
            if (cv) {
                this.get_canvas = function () { return cv; };
                return this.get_canvas();
            } else
                return null;
        } else
            return null;
    },

    get_canvasViewerId: function () {
        return this._canvasViewerId;
    },

    set_canvasViewerId: function (value) {
        this._canvasViewerId = value;
    },

    get_canvasViewer: function () {
        if (this.get_canvasViewerId()) {
            return $find(this.get_canvasViewerId());
        }
        return null;
    },

    set_zoom: function (v) {
        if (this.get_canvasViewer()) {
            this.get_canvasViewer().set_zoom(v);
            this._zoomChanged();
        }
    },

    get_zoom: function () {
        if (this.get_canvasViewer()) {
            return this.get_canvasViewer().get_zoom();
        }
    },

    _onVObjectChanged: function () {
        // Update Layers list.
        //run update async to avoid blink images on canvas
        var th = this;
        if (!this._updatePanelsDelegate)
            this._updatePanelsDelegate = Function.createDelegate(this, this._updatePanels);
        if (this._onVObjectChangedTimer)
            clearTimeout(this._onVObjectChangedTimer);
        this._onVObjectChangedTimer = setTimeout(this._updatePanelsDelegate, 75);
    },

    _updatePanels: function (psdEditor) {
        this._updateLayersList();
        this._updateDesigner();
    },

    _onDesignerInputChanging: function (e) {
        var id = e.target.id;
        var cv = this.get_canvas();
        if (!cv) return;
        var cur = cv.get_currentVObject();
        if (!cur) return;

        var i;
        var p = null;
        for (i = 0; i < this._props.length; i++) {
            if (e.target.id == this._props[i].id)
                p = this._props[i];
        }
        if (!p) return;

        // validate
        var valid = this._validate(e.target.value, p.validator);
        e.target.style.color = (valid) ? "#000000" : "#FF0000";

        // changed
        this._testProperties(false);
    },

    _hexToRgba: function (hex) {
        var r = parseInt(hex.substr(0, 2), 16);
        var g = parseInt(hex.substr(2, 2), 16);
        var b = parseInt(hex.substr(4, 2), 16);
        var a = parseInt(hex.substr(6, 2), 16);
        return "rgba(" + r + "," + g + "," + b + "," + a / 255 + ");";
    },

    // apply == true - update all parameters from designer
    // apply == false - just mark changed fields and apply button
    _testProperties: function (apply) {
        var i;
        var ns = Aurigma.GraphicsMill.AjaxControls.VectorObjects;
        var cv = this.get_canvas();
        var cvo = cv.get_currentVObject();
        if (!cvo) return;
        var isText = ns.TextVObject.isInstanceOfType(cvo);
        var isImage = ns.ImageVObject.isInstanceOfType(cvo);
        var isLine = ns.LineVObject.isInstanceOfType(cvo);
        var isRectangle = false;
        if (!isText && !isImage && !isLine)
            isRectangle =
				ns.RectangleVObject.isInstanceOfType(cvo) ||
				ns.EllipseVObject.isInstanceOfType(cvo);
        var changedFlag = false;
        var diffChanged = false;
        for (i = 0; i < this._props.length; i++) {
            if (((this._props[i].vObject == "RectangleVObject") && isImage) ||
				((this._props[i].vObject == "TextVObject") && isText) ||
				((this._props[i].vObject == "RectangleVObject") && isRectangle) ||
				((this._props[i].vObject == "LineVObject") && isLine)) {
                var input = $get(this._props[i].id);
                var diff = true;
                var valid = this._validate(input.value, this._props[i].validator);
                if (valid) {
                    if (cvo["get_" + this._props[i].propertyName]) {
                        var oldValue = cvo["get_" + this._props[i].propertyName]();

                        //bug 0010340: remove New Line symbols from text before compare
                        if (this._props[i].propertyName == "text") {
                            oldValue = oldValue.replace(/[\r\n]/mg, "");
                        }

                        if (this._props[i].validator == "color") {
                            var parsedOldValue = this._parseColor(oldValue);
                            diff = (input.value.toUpperCase() != parsedOldValue);
                            if (diff && apply) {
                                if (!changedFlag) {
                                    cv.get_history().addVObjectChanged(cvo);
                                    changedFlag = true;
                                }
                                cvo["set_" + this._props[i].propertyName](this._hexToRgba(input.value));
                            }
                        }
                        else if (input.className == "AlignmentPanel") {
                            diff = (this._getAlignment() != oldValue);
                            if (diff && apply) {
                                if (!changedFlag) {
                                    cv.get_history().addVObjectChanged(cvo);
                                    changedFlag = true;
                                }
                                cvo["set_" + this._props[i].propertyName](this._getAlignment());
                            }
                        }
                        else if (input.type == "checkbox") {
                            diff = (input.checked != oldValue);
                            if (diff && apply) {
                                if (!changedFlag) {
                                    cv.get_history().addVObjectChanged(cvo);
                                    changedFlag = true;
                                }
                                cvo["set_" + this._props[i].propertyName](input.checked);
                            }
                        }
                        else {
                            diff = (input.value != (oldValue + ""));
                            if (diff && apply) {
                                if (!changedFlag) {
                                    cv.get_history().addVObjectChanged(cvo);
                                    changedFlag = true;
                                }
                                //if poperty type is float then convert value to number
                                if (this._props[i].validator == "float")
                                    cvo["set_" + this._props[i].propertyName](new Number(input.value));
                                else
                                    cvo["set_" + this._props[i].propertyName](input.value);
                            }
                        }
                    }
                    else
                    //if no proprty then no difference 
                        diff = false;
                }
                diffChanged = diffChanged || diff;
                if (this._props[i].highlightType == "border") {
                    $get(this._props[i].changedHighlight).style.border = (diff && !apply) ? "1px solid red" : "1px solid gray";
                }
                else {
                    $get(this._props[i].changedHighlight).style.color = (diff && !apply) ? "red" : "gray";
                }
            }
        }
        if (apply) {
            cv.redraw();
        }

        $get(this._applyButtonId).className = (diffChanged && !apply) ? "ApplyButton" : "ApplyButtonDisabled";

        // update fontName
        //		if (isText) {
        //			var oldValue = cvo.get_fontName();
        //			var newValue = $find(this._fontDropDownListId).get_value();
        //			if (oldValue != newValue) {
        //				if (!changedFlag) {
        //					cv.get_history().addVObjectChanged(cvo);
        //					changedFlag = true;
        //				}
        //				cvo.set_fontName(newValue);
        //			}
        //		}
        //this._updateLayersList();
    },

    _onApplyButtonClick: function () {
        this._testProperties(true);
        this._updateLayersList();
    },

    _onStatusChanged: function () {
        var cv = this.get_canvas();
        if (cv.get_status() == Aurigma.GraphicsMill.AjaxControls.VectorObjects.UpdateStatus.busy) {
            this._showModalDialogPanel();
        }
        else {
            this._hideModalDialogPanel();
        }
    },

    _validate: function (str, validator) {
        if (validator == "float") {
            return this._validateFloat(str);
        }
        if (validator == "color") {
            return this._validateColor(str);
        }
        return true;
    },

    // return true if it is valid color
    _validateColor: function (color) {
        var std = /^\s*[0-9A-F]{8,8}\s*$/i;
        var c = std.exec(color);
        return (c != null);
    },

    _validateFloat: function (number) {
        // TODO: add validation for max/min values.
        var numberTemplate = /^\s*(\d{1,}(\.\d{1,})?)\s*$/i;
        var a = numberTemplate.exec(number);
        return (a != null);
    },

    _updateDesigner: function () {
        //Show appropriate designer.
        var ns = Aurigma.GraphicsMill.AjaxControls.VectorObjects;
        var cv = this.get_canvas();
        var obj = cv.get_currentVObject();
        var isText = false;
        var isImage = false;
        var isRectangle = false;
        var isLine = false;
        if (obj) {
            isText = ns.TextVObject.isInstanceOfType(obj);
            isImage = ns.ImageVObject.isInstanceOfType(obj);
            isLine = ns.LineVObject.isInstanceOfType(obj);
            if (!isText && !isImage)
                isRectangle =
					ns.RectangleVObject.isInstanceOfType(obj) ||
					ns.EllipseVObject.isInstanceOfType(obj);
        }
        var td = $get(this._textDesignerPanelId);
        var cd = $get(this._commonDesignerPanelId);
        var abp = $get(this._applyButtonPanelId);
        var oap = $get(this._objectAlignmentPanelId);
        var shown = "inherit";
        td.style.visibility = (isText) ? shown : "hidden";
        cd.style.visibility = (isImage || isRectangle || isLine) ? shown : "hidden";
        oap.style.visibility = (isText || isImage || isRectangle || isLine) ? shown : "hidden";
        abp.style.visibility = (isText || isImage || isRectangle || isLine) ? shown : "hidden";
        //$get(this._pulldownId).style.visibility = (isText) ? shown : "hidden";
        if (cd.style.visibility == shown) {
            var vObjectTypeName;
            if (isImage || isRectangle)
                vObjectTypeName = "RectangleVObject";
            else if (isLine)
                vObjectTypeName = "LineVObject";
            this._updateDesignerPanelLayout(cd, vObjectTypeName);
        }
        // Update parameters in designer.
        var i;
        for (i = 0; i < this._props.length; i++) {
            if (((this._props[i].vObject == "RectangleVObject") && (isImage)) ||
				((this._props[i].vObject == "TextVObject") && (isText)) ||
				(this._props[i].vObject == "RectangleVObject" && isRectangle) ||
				(this._props[i].vObject == "LineVObject" && isLine)) {
                if (obj["get_" + this._props[i].propertyName]) {
                    var input = $get(this._props[i].id);
                    if (this._props[i].validator == "color") {
                        var color = this._parseColor(obj["get_" + this._props[i].propertyName]());
                        input.value = color;
                        input.style.backgroundColor = "#" + color.substr(0, 6);
                    }
                    else if (input.className == "AlignmentPanel") {
                        this._setAlignment(obj["get_" + this._props[i].propertyName]());
                    }
                    else if (input.type == "checkbox") {
                        input.checked = obj["get_" + this._props[i].propertyName]();
                    }
                    //bug 0010340: remove New Line symbols from text
                    else if (this._props[i].propertyName == "text") {
                        input.value = obj["get_" + this._props[i].propertyName]().replace(/[\r\n]/mg, "");
                    }
                    else {
                        input.value = obj["get_" + this._props[i].propertyName]();
                    }
                    $get(this._props[i].id).style.color = "#000000";
                }
            }
        }
        if (isText) {
            // update fontName
            $find(this._fontDropDownListId).set_value(obj.get_fontName());
            this._textCustomFontClick();
            //this._setTextValueFocus();
        }
        this._testProperties(false);
    },

    //show / hide elements on designer panel base on selected object
    _updateDesignerPanelLayout: function (panelElement, vObjectTypeName) {
        var labels = panelElement.getElementsByTagName("LABEL");
        var hiddeElementClassName = "HiddenElement";
        if (vObjectTypeName == "LineVObject") {
            //hide borderwidth, bordercolor properties
            //show width, color properties
            for (var i = 0; i < labels.length; i++) {
                if (labels[i].attributes["for"].value == this._commonBorderWidthId ||
					labels[i].attributes["for"].value == this._commonBorderColorId ||
					labels[i].attributes["for"].value == this._commonFillColorId)
                    Sys.UI.DomElement.addCssClass(labels[i], hiddeElementClassName);
                if (labels[i].attributes["for"].value == this._commonWidthId ||
					labels[i].attributes["for"].value == this._commonColorId)
                    Sys.UI.DomElement.removeCssClass(labels[i], hiddeElementClassName);
            }
            Sys.UI.DomElement.addCssClass($get(this._commonBorderWidthId, panelElement), hiddeElementClassName);
            Sys.UI.DomElement.addCssClass($get(this._commonBorderColorId, panelElement), hiddeElementClassName);
            Sys.UI.DomElement.addCssClass($get(this._commonFillColorId, panelElement), hiddeElementClassName);

            Sys.UI.DomElement.removeCssClass($get(this._commonWidthId, panelElement), hiddeElementClassName);
            Sys.UI.DomElement.removeCssClass($get(this._commonColorId, panelElement), hiddeElementClassName);
        }
        else if (vObjectTypeName == "RectangleVObject") {
            //show borderwidth, bordercolor properties
            //hide width, color properties
            for (var i = 0; i < labels.length; i++) {
                if (labels[i].attributes["for"].value == this._commonBorderWidthId ||
					labels[i].attributes["for"].value == this._commonBorderColorId ||
					labels[i].attributes["for"].value == this._commonFillColorId)
                    Sys.UI.DomElement.removeCssClass(labels[i], hiddeElementClassName);
                if (labels[i].attributes["for"].value == this._commonWidthId ||
					labels[i].attributes["for"].value == this._commonColorId)
                    Sys.UI.DomElement.addCssClass(labels[i], hiddeElementClassName);
            }
            Sys.UI.DomElement.removeCssClass($get(this._commonBorderWidthId, panelElement), hiddeElementClassName);
            Sys.UI.DomElement.removeCssClass($get(this._commonBorderColorId, panelElement), hiddeElementClassName);
            Sys.UI.DomElement.removeCssClass($get(this._commonFillColorId, panelElement), hiddeElementClassName);

            Sys.UI.DomElement.addCssClass($get(this._commonWidthId, panelElement), hiddeElementClassName);
            Sys.UI.DomElement.addCssClass($get(this._commonColorId, panelElement), hiddeElementClassName);
        }
    },

    _toHex: function (color) {
        color = parseInt(color).toString(16).toUpperCase();
        return color.length < 2 ? "0" + color : color;
    },

    // returns string in hex RRGGBBAA
    _parseColor: function (color) {

        var rgba = /^\s*rgba\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\,\s*(\d{1,}(\.\d{1,})?)\s*\)\s*;{0,1}\s*$/i;
        var rgb = /^\s*rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)\s*;{0,1}\s*$/i;
        var std = /^\s*#[0-9A-F]{6,6}\s*$/i;

        var a = rgba.exec(color);
        var b = rgb.exec(color);
        var c = std.exec(color);
        var color = { r: 255, g: 255, b: 255, a: 255 };
        if (a) {
            color = { r: a[1], g: a[2], b: a[3], a: Math.round(a[4] * 255) };
        }
        if (b) {
            color = { r: b[1], g: b[2], b: b[3], a: 255 };
        }
        if (c) {
            color = { r: parseInt(c[0].substr(1, 2), 16), g: parseInt(c[0].substr(3, 2), 16), b: parseInt(c[0].substr(5, 2), 16), a: 255 };
        }
        return this._toHex(color.r) + this._toHex(color.g) + this._toHex(color.b) + this._toHex(color.a);
    },

    _getListCoordinate: function (pt) {
        var scroll;
        if (Sys.Browser.agent != Sys.Browser.InternetExplorer) {
            scroll = new Aurigma.GraphicsMill.AjaxControls.VectorObjects.Math.PointF(window.pageXOffset, window.pageYOffset);
        }
        else {
            scroll = new Aurigma.GraphicsMill.AjaxControls.VectorObjects.Math.PointF(document.documentElement.scrollLeft, document.documentElement.scrollTop);
        }
        var ll = $get(this._layersListId);
        var loc = Sys.UI.DomElement.getLocation(ll);
        var x = pt.X - loc.x + scroll.X + ll.scrollLeft;
        var y = pt.Y - loc.y + scroll.Y + ll.scrollTop;
        return new Aurigma.GraphicsMill.AjaxControls.VectorObjects.Math.PointF(x, y);
    },

    _isPointIntoRectangle: function (pt, bound) {
        return ((pt.X >= bound.x) && (pt.Y >= bound.y) && (pt.X < bound.x + bound.width) && (pt.Y < bound.y + bound.height));
    },

    _getBounds: function (element) {
        var bound = Sys.UI.DomElement.getBounds(element);
        var scroll;
        if (Sys.Browser.agent != Sys.Browser.InternetExplorer) {
            scroll = new Aurigma.GraphicsMill.AjaxControls.VectorObjects.Math.PointF(window.pageXOffset, window.pageYOffset);
        }
        else {
            scroll = new Aurigma.GraphicsMill.AjaxControls.VectorObjects.Math.PointF(document.documentElement.scrollLeft, document.documentElement.scrollTop);
        }
        bound.x = bound.x - scroll.X;
        bound.y = bound.y - scroll.Y;
        return bound;
    },

    _getCurrentLayerIndex: function (pt, isRectangle) {
        var ll = $get(this._layersListId);
        var i;
        for (i = 0; i < ll.childNodes.length; i++) {
            if ((ll.childNodes[i].id != "emptyLayer") && (ll.childNodes[i].className != "DragElement") && (ll.childNodes[i].tagName == "LI")) {
                var bound = this._getBounds(ll.childNodes[i]);
                var removeBound = this._getBounds(ll.childNodes[i].lastChild);
                if (!this._isPointIntoRectangle(pt, removeBound)) {
                    if ((pt.Y >= bound.y) && (pt.Y < bound.y + bound.height) && !isRectangle) {
                        return i;
                    }
                    if (this._isPointIntoRectangle(pt, bound) && isRectangle) {
                        return i;
                    }
                }
            }
        }
        return -1;
    },

    _showModalDialogPanel: function () {

        // blur all
        var i;
        for (i = 0; i < this._props.length; i++)
            $get(this._props[i].id).blur();

        var md = $get(this._modalDialogPanelId);
        var tb = $get(this._psdEditorPanelId);
        var bounds = Sys.UI.DomElement.getBounds(tb);
        md.style.width = bounds.width + "px";
        md.style.height = bounds.height + "px";
    },

    _hideModalDialogPanel: function () {
        var md = $get(this._modalDialogPanelId);
        md.style.width = "0px";
        md.style.height = "0px";
    },

    _onLayersListMouseDown: function (e) {
        if (e.target.className == "Delete") return;
        if (this._startIndex != -1) return;
        var ll = $get(this._layersListId);
        var cv = this.get_canvas();

        // Test which layer (index) we got.
        this._startIndex = this._getCurrentLayerIndex(new Aurigma.GraphicsMill.AjaxControls.VectorObjects.Math.PointF(e.clientX, e.clientY), true);

        if (this._startIndex < 0) return;

        this._mouseDownFlag = true;

        var mousePoint = new Aurigma.GraphicsMill.AjaxControls.VectorObjects.Math.PointF(e.clientX, e.clientY);
        var layerPoint = Sys.UI.DomElement.getLocation(ll.childNodes[this._startIndex]);
        this._startMousePoint = this._getListCoordinate(mousePoint);
        var bound = this._getBounds(ll.childNodes[this._startIndex]);
        this._startLayerPoint = this._getListCoordinate(new Aurigma.GraphicsMill.AjaxControls.VectorObjects.Math.PointF(bound.x, bound.y));

        //Replace this layer.

        var emptyLayer = document.createElement("li");
        emptyLayer.id = "emptyLayer";
        var el = ll.replaceChild(emptyLayer, ll.childNodes[this._startIndex]);
        el.style.position = "absolute";
        el.style.top = this._startLayerPoint.Y + "px";
        el.style.left = this._startLayerPoint.X + "px";
        el = ll.appendChild(el);
        el.className = "DragElement";
        this._floatElement = el;
        this._endIndex = this._startIndex;

        ll.setCapture(true, window);

        return true;
    },

    _onLayersListMouseUp: function (e) {
        var ll = $get(this._layersListId);
        if (!this._mouseDownFlag) return true;

        // apply drag

        var cv = this.get_canvas();
        var layers = cv.get_layers();
        var l = layers.get_item(0);
        var vObjects = l.get_vObjects();

        var ind1 = vObjects.get_count() - this._startIndex - 1;
        var ind2 = vObjects.get_count() - 1 - this._endIndex;
        if (ind1 != ind2) {
            var idx = vObjects.get_count() - this._startIndex - 1;
            var obj = vObjects.get_item(idx);
            vObjects.removeAt(idx);
            vObjects.insert(vObjects.get_count() - this._endIndex, obj);
        }

        cv.set_currentLayerIndex(0);
        cv.set_currentVObjectIndex(vObjects.get_count() - this._endIndex - 1);
        cv.redraw();

        // clear variables
        this._startIndex = -1;
        this._mouseDownFlag = false;

        ll.releaseCapture(window);

        // repaint Layers List
        this._onVObjectChanged();
        return true;
    },

    _onLayersListMouseMove: function (e) {
        var ll = $get(this._layersListId);
        if (!this._mouseDownFlag) return;

        var pt = new Aurigma.GraphicsMill.AjaxControls.VectorObjects.Math.PointF(e.clientX, e.clientY);

        // change current
        var currentIndex = this._getCurrentLayerIndex(pt, false);

        if (currentIndex != -1) {

            var insertIndex = (this._endIndex < currentIndex) ? currentIndex - 1 : currentIndex;

            var bound = this._getBounds(ll.childNodes[currentIndex]);
            if (pt.Y > bound.y + bound.height / 2)
                insertIndex += 1;

            if (insertIndex < ll.childNodes.length) {
                var empty = ll.removeChild(ll.childNodes[this._endIndex]);

                var element = ll.childNodes[insertIndex];
                ll.insertBefore(empty, element);
                this._endIndex = insertIndex;
            }
        }

        // change position of current float element
        pt = this._getListCoordinate(pt);
        this._floatElement.style.left = this._startLayerPoint.X + pt.X - this._startMousePoint.X + "px";
        this._floatElement.style.top = this._startLayerPoint.Y + pt.Y - this._startMousePoint.Y + "px";


        // change scroll if point near top or bottom edge.
        var bound = this._getBounds(ll);
        if (pt.Y - ll.scrollTop < 10)
            ll.scrollTop = ll.scrollTop - 10;
        if (pt.Y - ll.scrollTop > bound.height - 10)
            ll.scrollTop = ll.scrollTop + 10;
        return true;
    },

    _onLayersListClick: function (e) {
        var ll = $get(this._layersListId);
        var cv = this.get_canvas();
        if ((!ll) || (!cv)) return;
        var ls = cv.get_layers();
        if (!ls) return;
        var lr = ls.get_item(0);
        if (!lr) return;
        var vObjects = lr.get_vObjects();
        if (!vObjects) return;
        var i;
        for (i = 0; i < ll.childNodes.length; i++) {
            if ((e.target == ll.childNodes[i]) || (e.target == ll.childNodes[i].firstChild)) {
                cv.set_currentLayerIndex(0);
                cv.set_currentVObjectIndex(vObjects.get_count() - i - 1);
            }
            else if (e.target == ll.childNodes[i].lastChild) {
                if (confirm("Remove this layer?")) {
                    vObjects.removeAt(vObjects.get_count() - i - 1);
                    this._updateLayersList();
                    cv.redraw(true);
                    return;
                }
            }
        }
        return false;
    },

    _updateLayersList: function () {
        var ll = $get(this._layersListId);
        // clear
        while (ll.firstChild) {
            if ((ll.firstChild.lastChild) && (ll.firstChild.lastChild.className == "Delete")) {
                $removeHandler(ll.firstChild.lastChild, 'click', this._layersListClickDelegate);
            }
            ll.removeChild(ll.firstChild);
        }
        // add new
        var cv = this.get_canvas();
        if (!cv) return;
        var fl = cv.get_layers();
        if (!fl) return;
        var l = fl.get_item(0);
        if (!l) return;
        var vObjects = l.get_vObjects();
        if (!vObjects) return;
        var i;
        var currentVObject = cv.get_currentVObject();
        for (i = vObjects.get_count() - 1; i >= 0; i--) {
            var item = vObjects.get_item(i);
            var isText = Aurigma.GraphicsMill.AjaxControls.VectorObjects.TextVObject.isInstanceOfType(item);
            var isImage = Aurigma.GraphicsMill.AjaxControls.VectorObjects.ImageVObject.isInstanceOfType(item);
            var isEllipse = Aurigma.GraphicsMill.AjaxControls.VectorObjects.EllipseVObject.isInstanceOfType(item);
            var isRectangle = !isEllipse && Aurigma.GraphicsMill.AjaxControls.VectorObjects.RectangleVObject.isInstanceOfType(item);
            var isLine = Aurigma.GraphicsMill.AjaxControls.VectorObjects.LineVObject.isInstanceOfType(item);
            var isCurrent = (currentVObject == item);
            var el = document.createElement("li");

            if (isCurrent) el.id = "currentLayer";
            var textEl = document.createElement("h2");
            var removeEl = document.createElement("a");
            removeEl.href = "#remove";
            if (isText)
                textEl.className += "Text";
            else if (isImage)
                textEl.className += "Image";
            else if (isEllipse)
                textEl.className += "Ellipse";
            else if (isRectangle)
                textEl.className += "Rectangle";
            else if (isLine)
                textEl.className += "Line";
            textEl.innerHTML = vObjects.get_item(i).get_name();
            removeEl.className = "Delete";
            el.appendChild(textEl);
            removeEl = el.appendChild(removeEl);
            el = ll.appendChild(el);
            $addHandler(removeEl, 'click', this._layersListClickDelegate);
        }

        // Scroll ll to show current VObject.
        if (cv.get_currentVObjectIndex() != -1) {
            var current = ll.childNodes[vObjects.get_count() - cv.get_currentVObjectIndex() - 1];
            var llBound = this._getBounds(ll);
            var firstBound = this._getBounds(ll.firstChild);
            var currentBound = this._getBounds(current);
            var d = currentBound.height / 2;
            //if visible less than half of item then scroll list
            if (currentBound.y + currentBound.height < llBound.y + d || currentBound.y > llBound.y + llBound.height - d) {
                ll.scrollTop = currentBound.y - firstBound.y;
            }
        }
    },

    // redo undo

    _updateRedoUndoButtons: function () {
        var cv = this.get_canvas();
        var h = cv.get_history();
        $get(this._redoButtonId).className = h.get_canRedo() ? "RedoButton" : "RedoButtonDisabled";
        $get(this._undoButtonId).className = h.get_canUndo() ? "UndoButton" : "UndoButtonDisabled";
    },

    _redoButtonClick: function () {
        var cv = this.get_canvas();
        var h = cv.get_history();
        if (h.get_canRedo()) {
            h.redo();
            this._updateRedoUndoButtons();
            this._updateLayersList();
            this._updateDesigner();
        }
    },

    _undoButtonClick: function () {
        var cv = this.get_canvas();
        var h = cv.get_history();
        if (h.get_canUndo()) {
            h.undo();
            this._updateRedoUndoButtons();
            this._updateLayersList();
            this._updateDesigner();
        }
    },

    initialize: function () {

        // init color picker.
        var id = this.get_id();
        Sys.Application.add_load(function () {
            $.ColorPicker.init({ psdEditor: $find(id) });
        });

        // for drag and drop panel:
        // Add workaround for FF for setCapture/releaseCapture functionality enable.
        // http://www.activewidgets.com/javascript.forum.6087.21/firefox-1-0-5-resizing.html

        var wnd = window;
        if (wnd.HTMLElement) {
            var element = wnd.HTMLElement.prototype;

            var capture = ["click", "mousedown", "mouseup", "mousemove", "mouseover", "mouseout"];

            if (!element.setCapture) {
                element.setCapture = function (flag, wind) {
                    var wnd = (wind) ? wind : window;
                    var self = this;
                    var flag = false;
                    if (!this._capture) {
                        this._capture = function (e) {
                            if (flag) { return }
                            flag = true;
                            var event = document.createEvent("MouseEvents");
                            event.initMouseEvent(e.type,
								e.bubbles, e.cancelable, e.view, e.detail,
								e.screenX, e.screenY, e.clientX, e.clientY,
								e.ctrlKey, e.altKey, e.shiftKey, e.metaKey,
								e.button, e.relatedTarget);
                            self.dispatchEvent(event);
                            flag = false;
                        };
                        for (var i = 0; i < capture.length; i++) {
                            wnd.addEventListener(capture[i], this._capture, true);
                        }
                    }
                };

                element.releaseCapture = function (wind) {
                    var wnd = (wind) ? wind : window;
                    for (var i = 0; i < capture.length; i++) {
                        wnd.removeEventListener(capture[i], this._capture, true);
                    }
                    this._capture = null;
                };
            }
        }

        function Properties(id, vObject, update, propertyName, validator, changedHighlight, highlightType) {
            this.id = id;
            this.vObject = vObject;
            this.update = update;
            this.propertyName = propertyName;
            this.validator = validator;
            this.changedHighlight = changedHighlight;
            this.highlightType = highlightType;
        }
        this._props = [
			new Properties(this._textValueId, "TextVObject", "postpone", "text", "none", this._textValueId, "border"),
			new Properties($find(this._fontDropDownListId)._inputId, "TextVObject", "postpone", "fontName", "none", $find(this._fontDropDownListId)._inputId, "border"),
			new Properties(this._textFontSizeId, "TextVObject", "postpone", "fontSize", "float", this._textFontSizeId, "border"),
			new Properties(this._textColorId, "TextVObject", "postpone", "textColor", "color", this._textColorId, "border"),
			new Properties(this._textBoldId, "TextVObject", "postpone", "bold", "none", this._textBoldLabelId, "color"),
			new Properties(this._textItalicId, "TextVObject", "postpone", "italic", "none", this._textItalicLabelId, "color"),
			new Properties(this._textUnderlineId, "TextVObject", "postpone", "underline", "none", this._textUnderlineLabelId, "color"),
			new Properties(this._textCustomFontId, "TextVObject", "postpone", "customFont", "none", this._pulldownHeaderId, "border"),
			new Properties(this._alignmentPanelId, "TextVObject", "immediately", "alignment", "none", this._alignmentLabelId, "color"),
			new Properties(this._commonBorderWidthId, "RectangleVObject", "immediately", "borderWidth", "float", this._commonBorderWidthId, "border"),
			new Properties(this._commonFillColorId, "RectangleVObject", "immediately", "fillColor", "color", this._commonFillColorId, "border"),
			new Properties(this._commonBorderColorId, "RectangleVObject", "immediately", "borderColor", "color", this._commonBorderColorId, "border"),
			new Properties(this._commonColorId, "LineVObject", "immediately", "color", "color", this._commonColorId, "border"),
			new Properties(this._commonWidthId, "LineVObject", "immediately", "width", "float", this._commonWidthId, "border")
		];

        this._navigators = [
			[this._noneNavigatorButtonId, ""],
			[this._panNavigatorButtonId, this._panNavigatorId],
			[this._zoomInNavigatorButtonId, this._zoomInNavigatorId],
			[this._zoomOutNavigatorButtonId, this._zoomOutNavigatorId]
		];

        if (!this._onApplyButtonClickDelegate) {
            this._onApplyButtonClickDelegate = Function.createDelegate(this, this._onApplyButtonClick);
            $addHandler($get(this._applyButtonId), 'click', this._onApplyButtonClickDelegate);
        }
        if (!this._currentVObjectChangedDelegate) {
            this._currentVObjectChangedDelegate = Function.createDelegate(this, this._onVObjectChanged);
            var th = this;
            Sys.Application.add_load(function () {
                th.get_canvas().add_currentVObjectChanged(th._currentVObjectChangedDelegate);
                // TODO: remove from here
                th._updateLayersList();
                if (!th._onStatusChangedDelegate) {
                    th._onStatusChangedDelegate = Function.createDelegate(th, th._onStatusChanged);
                    th.get_canvas().add_statusChanged(th._onStatusChangedDelegate);
                }
            });
        }
        if (!this._layersListClickDelegate) {
            this._layersListClickDelegate = Function.createDelegate(this, this._onLayersListClick);
        }
        if (!this._layersListMouseUpDelegate) {
            this._layersListMouseUpDelegate = Function.createDelegate(this, this._onLayersListMouseUp);
        }
        if (!this._layersListMouseDownDelegate) {
            this._layersListMouseDownDelegate = Function.createDelegate(this, this._onLayersListMouseDown);
        }
        if (!this._layersListMouseMoveDelegate) {
            this._layersListMouseMoveDelegate = Function.createDelegate(this, this._onLayersListMouseMove);
        }
        var ll = $get(this._layersListId);
        $addHandler(ll, 'mousedown', this._layersListMouseDownDelegate);
        $addHandler(ll, 'mousemove', this._layersListMouseMoveDelegate);
        $addHandler(ll, 'mouseup', this._layersListMouseUpDelegate);

        if (!this._designerInputChangingDelegate) {
            this._designerInputChangingDelegate = Function.createDelegate(this, this._onDesignerInputChanging);
            var i;
            for (i = 0; i < this._props.length; i++) {
                $addHandler($get(this._props[i].id), 'keyup', this._designerInputChangingDelegate);
                $addHandler($get(this._props[i].id), 'blur', this._designerInputChangingDelegate);
                $addHandler($get(this._props[i].id), 'paste', this._designerInputChangingDelegate);
                $addHandler($get(this._props[i].id), 'click', this._designerInputChangingDelegate);
            }
        }

        if (!this._showLayerMenuDelegate) {
            this._showLayerMenuDelegate = Function.createDelegate(this,
				function () {
				    if (this._closeMenuTimeout) {
				        window.clearTimeout(this._closeMenuTimeout);
				        delete this._closeMenuTimeout;
				    }
				    Sys.UI.DomElement.removeCssClass($get(this._newLayerMenuId), "HiddenElement");
				});

            //show menu when click on "Add layer..." button
            $addHandler($get(this._addLayerId), 'mouseover', this._showLayerMenuDelegate);
        }

        if (!this._hideLayerMenuDelegate) {
            this._hideLayerMenuDelegate = Function.createDelegate(this,
				function () { Sys.UI.DomElement.addCssClass($get(this._newLayerMenuId), "HiddenElement"); });

            if (!this._setHideMenuTimeoutDelegate) {
                this._setHideMenuTimeoutDelegate = Function.createDelegate(this,
					function () { this._closeMenuTimeout = window.setTimeout(this._hideLayerMenuDelegate, 1000); })

                //hide menu after 1 sec when move mouse out from "Add layer..." button or menu itself
                $addHandler($get(this._addLayerId), "mouseout", this._setHideMenuTimeoutDelegate);
                $addHandler($get(this._newLayerMenuId), "mouseout", this._setHideMenuTimeoutDelegate);
            }

            if (!this._clearHideMenuTimeoutDelegate) {
                this._clearHideMenuTimeoutDelegate = Function.createDelegate(this,
					function () {
					    if (this._closeMenuTimeout) {
					        window.clearTimeout(this._closeMenuTimeout);
					        delete this._closeMenuTimeout;
					    }
					});

                //clear hide menu timeout when move mouse on menu
                $addHandler($get(this._newLayerMenuId), "mouseover", this._clearHideMenuTimeoutDelegate);
            }

            //hide menu after click on it
            $addHandler($get(this._newLayerMenuId), 'click', this._hideLayerMenuDelegate);
        }

        if (!this._onAddingVImageDelegate) {
            this._onAddingVImageDelegate = Function.createDelegate(this, this._onAddingVImage);
            $addHandler($get(this._addImageId), 'click', this._onAddingVImageDelegate);
        }
        if (!this._onAddingVTextDelegate) {
            this._onAddingVTextDelegate = Function.createDelegate(this, this._onAddingVText);
            $addHandler($get(this._addTextId), 'click', this._onAddingVTextDelegate);
        }

        if (!this._addVLineDelegate) {
            this._addVLineDelegate = Function.createDelegate(this, this._addVLine);
            $addHandler($get(this._addLineId), 'click', this._addVLineDelegate);
        }
        if (!this._addVRectangleDelegate) {
            this._addVRectangleDelegate = Function.createDelegate(this, this._addVRectangle);
            $addHandler($get(this._addRectangleId), 'click', this._addVRectangleDelegate);
        }
        if (!this._addVEllipseDelegate) {
            this._addVEllipseDelegate = Function.createDelegate(this, this._addVEllipse);
            $addHandler($get(this._addEllipseId), 'click', this._addVEllipseDelegate);
        }

        if ($get(this._modalDialogPanelId)) {
            var el = $get(this._modalDialogPanelId);
            $addHandler(el, 'click', this._falseHandler);
            $addHandler(el, 'mousedown', this._falseHandler);
            $addHandler(el, 'mouseup', this._falseHandler);
            $addHandler(el, 'mousemove', this._falseHandler);
        }

        // redo undo

        if (!this._redoUndoChangedDelegate) {
            this._redoUndoChangedDelegate = Function.createDelegate(this, this._updateRedoUndoButtons);
        }

        Sys.Application.add_load(Function.createDelegate(this, function () {

            var h = this.get_canvas().get_history();
            h.set_enable(true);
            h.add_changed(this._redoUndoChangedDelegate);
            this.get_canvas().add_statusChanged(this._redoUndoChangedDelegate);
            this._updateRedoUndoButtons();
            // initialize navigator
            this._updateNavigatorPanel();
        }));

        if (!this._redoButtonClickDelegate) {
            this._redoButtonClickDelegate = Function.createDelegate(this, this._redoButtonClick);
            $addHandler($get(this._redoButtonId), 'click', this._redoButtonClickDelegate);
        }

        if (!this._undoButtonClickDelegate) {
            this._undoButtonClickDelegate = Function.createDelegate(this, this._undoButtonClick);
            $addHandler($get(this._undoButtonId), 'click', this._undoButtonClickDelegate);
        }

        if (!this._navigatorButtonClickDelegate) {
            this._navigatorButtonClickDelegate = Function.createDelegate(this, this._navigatorButtonClick);
            $addHandler($get(this._noneNavigatorButtonId), 'click', this._navigatorButtonClickDelegate);
            $addHandler($get(this._panNavigatorButtonId), 'click', this._navigatorButtonClickDelegate);
            $addHandler($get(this._zoomInNavigatorButtonId), 'click', this._navigatorButtonClickDelegate);
            $addHandler($get(this._zoomOutNavigatorButtonId), 'click', this._navigatorButtonClickDelegate);
        }

        if (!this._zoomModeChangedDelegate) {
            this._zoomModeChangedDelegate = Function.createDelegate(this, this._zoomModeChanged);
            $addHandler($get(this._zoomModeSelectId), 'change', this._zoomModeChangedDelegate);
        }

        // initialize zoomMode
        if (!this._zoomChangedDelegate) {
            this._zoomChangedDelegate = Function.createDelegate(this, this._zoomChanged);
            var cv = this.get_canvasViewer();
            if (cv) {
                cv.add_zoomed(this._zoomChangedDelegate);
            }
        }
        this._zoomChanged();

        if (!this._textCustomFontClickDelegate) {
            this._textCustomFontClickDelegate = Function.createDelegate(this, this._textCustomFontClick);
            $addHandler($get(this._textCustomFontId), 'click', this._textCustomFontClickDelegate);
        }

        // sign for WorkspaceMouseUp event into CanvasViewer to set focus.
        // we do it here instead of currentVObjectChanged event because
        // currentVObjectChanged event calls after mousedown, and then when we set focus it loses
        // because mouseup event fires into Canvas.
        /*if (!this._setTextValueFocusDelegate) {
        this._setTextValueFocusDelegate = Function.createDelegate(this, this._setTextValueFocus);
        this.get_canvasViewer().add_workspaceMouseUp(this._setTextValueFocusDelegate);
        }*/

        // add apply on enter into text field functionality
        if (!this._textValueEnteredDelegate) {
            this._textValueEnteredDelegate = Function.createDelegate(this, this._textValueEntered);
            $addHandler(document, 'keydown', this._textValueEnteredDelegate);
        }

        if (!this._alignmentClickDelegate) {
            this._alignmentClickDelegate = Function.createDelegate(this, this._alignmentClick);
            $addHandler($get(this._alignmentLeftId), "click", this._alignmentClickDelegate);
            $addHandler($get(this._alignmentCenterId), "click", this._alignmentClickDelegate);
            $addHandler($get(this._alignmentRightId), "click", this._alignmentClickDelegate);
        }

        if (!this._objectAlignmentClickDelegate) {
            this._objectAlignmentClickDelegate = Function.createDelegate(this, this._objectAlignmentClick);
            $addHandler($get(this._leftAlignmentButtonId), "click", this._objectAlignmentClickDelegate);
            $addHandler($get(this._rightAlignmentButtonId), "click", this._objectAlignmentClickDelegate);
            $addHandler($get(this._topAlignmentButtonId), "click", this._objectAlignmentClickDelegate);
            $addHandler($get(this._bottomAlignmentButtonId), "click", this._objectAlignmentClickDelegate);
            $addHandler($get(this._horizontalAlignmentButtonId), "click", this._objectAlignmentClickDelegate);
            $addHandler($get(this._verticalAlignmentButtonId), "click", this._objectAlignmentClickDelegate);
        }

        if (!this._moveLayerClickDelegate) {
            this._moveLayerClickDelegate = Function.createDelegate(this, this._moveLayerClick);
            $addHandler($get(this._moveUpLayerButtonId), "click", this._moveLayerClickDelegate);
            $addHandler($get(this._moveDownLayerButtonId), "click", this._moveLayerClickDelegate);
            $addHandler($get(this._moveTopLayerButtonId), "click", this._moveLayerClickDelegate);
            $addHandler($get(this._moveBottomLayerButtonId), "click", this._moveLayerClickDelegate);
        }

        this._updateDesigner();
    },

    _moveLayerClick: function (e) {
        //actually we move objects but not layers
        //because allobjects are placed on one layer
        var cv = this.get_canvas();
        var layers = cv.get_layers();
        var l = layers.get_item(0);
        var vObjects = l.get_vObjects();

        var currentObjectIndex = cv.get_currentVObjectIndex();
        if (e.target && currentObjectIndex != -1) {
            if (e.target.id == this._moveUpLayerButtonId) {
                if (currentObjectIndex < vObjects.get_count() - 1) {
                    var newObjectIndex = currentObjectIndex + 1;
                    var obj = vObjects.get_item(currentObjectIndex);
                    vObjects.removeAt(currentObjectIndex);
                    vObjects.insert(newObjectIndex, obj);
                    cv.set_currentVObjectIndex(newObjectIndex);
                }
            } else if (e.target.id == this._moveDownLayerButtonId) {
                if (currentObjectIndex > 0) {
                    var newObjectIndex = currentObjectIndex - 1;
                    var obj = vObjects.get_item(currentObjectIndex);
                    vObjects.removeAt(currentObjectIndex);
                    vObjects.insert(newObjectIndex, obj);
                    cv.set_currentVObjectIndex(newObjectIndex);
                }
            } else if (e.target.id == this._moveTopLayerButtonId) {
                if (currentObjectIndex < vObjects.get_count() - 1) {
                    var newObjectIndex = vObjects.get_count() - 1;
                    var obj = vObjects.get_item(currentObjectIndex);
                    vObjects.removeAt(currentObjectIndex);
                    vObjects.insert(newObjectIndex, obj);
                    cv.set_currentVObjectIndex(newObjectIndex);
                }
            } else if (e.target.id == this._moveBottomLayerButtonId) {
                if (currentObjectIndex > 0) {
                    var newObjectIndex = 0;
                    var obj = vObjects.get_item(currentObjectIndex);
                    vObjects.removeAt(currentObjectIndex);
                    vObjects.insert(newObjectIndex, obj);
                    cv.set_currentVObjectIndex(newObjectIndex);
                }
            }
            cv.redraw();
        }
    },

    _objectAlignmentClick: function (e) {
        var cv = this.get_canvas();
        var cvo = cv.get_currentVObject();
        if ((e.target) && (cvo)) {
            if (e.target.id == this._leftAlignmentButtonId) {
                cv.alignVObject(cvo, "left");
            }
            if (e.target.id == this._rightAlignmentButtonId) {
                cv.alignVObject(cvo, "right");
            }
            if (e.target.id == this._topAlignmentButtonId) {
                cv.alignVObject(cvo, "top");
            }
            if (e.target.id == this._bottomAlignmentButtonId) {
                cv.alignVObject(cvo, "bottom");
            }
            if (e.target.id == this._horizontalAlignmentButtonId) {
                cv.alignVObject(cvo, "horizontalCenter");
            }
            if (e.target.id == this._verticalAlignmentButtonId) {
                cv.alignVObject(cvo, "verticalCenter");
            }
        }
    },

    _alignmentClick: function (e) {
        if (e.target) {
            var j = Aurigma.GraphicsMill.AjaxControls.VectorObjects.TextAlignment;
            if (e.target.id == this._alignmentLeftId) {
                this._setAlignment(j.Left);
            }
            else if (e.target.id == this._alignmentCenterId) {
                this._setAlignment(j.Center);
            }
            else if (e.target.id == this._alignmentRightId) {
                this._setAlignment(j.Right);
            }
            this._testProperties(false);
        }
    },

    _setTextValueFocus: function () {
        var cv = this.get_canvasViewer();
        var canvas = cv.get_canvas();
        var cvo = canvas.get_currentVObject();
        if ((cvo) && (Aurigma.GraphicsMill.AjaxControls.VectorObjects.TextVObject.isInstanceOfType(cvo))) {
            $get(this._textValueId).focus();
        }
    },

    _textValueEntered: function (e) {
        if ((e.target == $get(this._textValueId)) && (e.keyCode == 13)) {
            this._onApplyButtonClick();
        }
    },

    _zoomChanged: function () {
        var cv = this.get_canvasViewer();
        var select = $get(this._zoomModeSelectId);
        if (!cv) return;
        var zoom = cv.get_zoom();
        var zoomMode = cv.get_zoomMode();
        this._manualZoomChanging = false;
        if (zoomMode != GraphicsMill.ZoomMode.none) {
            var i;
            for (i = 0; i < select.childNodes.length; i++) {
                var el = select.childNodes[i];
                if ((el.tagName == "OPTION") && (el.value == zoomMode + "")) {
                    el.selected = true;
                }
            }
        }
        else {
            this._removeCustomZoomFromList();
            this._addCustomZoomToList(zoom * 100);
        }
        this._manualZoomChanging = true;
    },

    _removeCustomZoomFromList: function () {
        var select = $get(this._zoomModeSelectId);
        var i;
        for (i = 0; i < select.childNodes.length; i++) {
            if (select.childNodes[i].id == "customZoom") {
                select.removeChild(select.childNodes[i]);
            }
        }
    },

    _addCustomZoomToList: function (zoom) {
        var select = $get(this._zoomModeSelectId);
        var i;
        var exists = false;
        var index = -1;
        for (i = 0; i < select.childNodes.length; i++) {
            var el = select.childNodes[i];
            if (el.tagName == "OPTION") {
                if (el.value == Math.round(zoom) + "%") {
                    exists = true;
                    index = i;
                    break;
                }
                else if ((el.value.lastIndexOf("%") != -1) && (parseInt(el.value) > Math.round(zoom))) {
                    index = i;
                    break;
                }
            }
        }

        // insert and set as active.
        if (!exists) {
            var option = document.createElement("option");
            option.id = "customZoom";
            option.innerHTML = option.value = Math.round(zoom) + "%";
            if (index != -1) {
                select.insertBefore(option, select.childNodes[index]);
            }
            else {
                select.appendChild(option);
                index = select.childNodes.length - 1;
            }
        }
        select.childNodes[index].selected = true;
        return select.childNodes[index];
    },

    _zoomModeChanged: function () {
        if (this._manualZoomChanging) {
            var cv = this.get_canvasViewer();
            if (!cv) return;
            var value = $get(this._zoomModeSelectId).value;
            if (value.lastIndexOf("%") != -1) {
                cv.set_zoomMode(GraphicsMill.ZoomMode.none);
                cv.set_zoom(parseInt(value) / 100);
            }
            else {
                cv.set_zoomMode(parseInt(value));
            }
        }
    },

    _updateNavigatorPanel: function () {
        if (this.get_canvasViewer()) {
            var nid = this.get_canvasViewer().get_navigator();
            for (var i = 0; i < this._navigators.length; i++) {
                $get(this._navigators[i][0]).className = (nid == this._navigators[i][1]) ? "NavigatorButtonSelected" : "NavigatorButton";
            }
        }
    },

    _navigatorButtonClick: function (el) {
        var cv = this.get_canvasViewer();
        if (!cv) return;
        for (var i = 0; i < this._navigators.length; i++) {
            var btn = $get(this._navigators[i][0]);
            if (btn == el.target || btn == el.target.parentNode) {
                cv.set_navigator(this._navigators[i][1]);
            }
        }
        this._updateNavigatorPanel();
    },


    _falseHandler: function () {
        return false;
    },

    _onAddingVImage: function () {
        this._raiseEvent("AddingVImage");
    },

    _onAddingVText: function () {
        this._raiseEvent("AddingVText");
    },

    //add new VLine object
    _addVLine: function (e) {
        this._createVObject("LineVObject");
    },

    //add new VRectangle object
    _addVRectangle: function (e) {
        this._createVObject("RectangleVObject");
    },

    //add new VEllipse object
    _addVEllipse: function (e) {
        this._createVObject("EllipseVObject");
    },

    _createVObject: function (vObjevtTypeName) {
        var ns = Aurigma.GraphicsMill.AjaxControls.VectorObjects;
        if (ns[vObjevtTypeName]) {
            // create new VObject
            var cv = this.get_canvas();
            var vObject;
            var number;
            if (vObjevtTypeName == "EllipseVObject") {
                vObject = new Aurigma.GraphicsMill.AjaxControls.VectorObjects.EllipseVObject(25, 25, 30, 30);
                vObject.set_mode(ns.VObjectMode.creating);
                number = this.get_nextEllipseNumber();
                vObject.set_name("Ellipse" + number);
            }
            else if (vObjevtTypeName == "RectangleVObject") {
                vObject = new Aurigma.GraphicsMill.AjaxControls.VectorObjects.RectangleVObject(30, 25, 40, 30);
                vObject.set_mode(ns.VObjectMode.creating);
                number = this.get_nextRectangleNumber();
                vObject.set_name("Rectangle" + number);
            }
            else if (vObjevtTypeName == "LineVObject") {
                vObject = new Aurigma.GraphicsMill.AjaxControls.VectorObjects.LineVObject(10, 10, 50, 40);
                vObject.set_mode(ns.VObjectMode.creating);
                number = this.get_nextLineNumber();
                vObject.set_name("Line" + number);
                vObject.set_width(4);
                vObject.set_color("#707070");
            }
            else
                throw Error.argument("vObjevtTypeName");

            vObject.set_mode(ns.VObjectMode.created);
            cv.get_layers().get_item(0).get_vObjects().add(vObject);
            cv.set_currentLayerIndex(vObject.get_layer().get_index());
            cv.set_currentVObjectIndex(vObject.get_index());
            //need repaint canvas
            cv.redraw();
        }
    },

    dispose: function () {
        var cv = this.get_canvas();
        if (this._currentVObjectChangedDelegate != null) {
            if (cv) cv.remove_currentVObjectChanged(this._currentVObjectChangedDelegate);
            this._currentVObjectChangedDelegate = null;
        }
        if (this._onApplyButtonClickDelegate) {
            $removeHandler($get(this._applyButtonId), 'click', this._onApplyButtonClickDelegate);
            this._onApplyButtonClickDelegate = null;
        }
        if (this._layersListClickDelegate) {
            this._layersListClickDelegate = null;
        }
        delete this._updatePanelsDelegate;

        if (this._designerInputChnagingDelegate) {
            var i;
            for (i = 0; i < this._props.length; i++) {
                $removeHandler($get(this._props[i].id), 'keyup', this._designerInputChangingDelegate);
                $removeHandler($get(this._props[i].id), 'blur', this._designerInputChangingDelegate);
                $removeHandler($get(this._props[i].id), 'paste', this._designerInputChangingDelegate);
            }
            this._designerInputChangingDelegate = null;
        }
        if (this._layersListMouseUpDelegate) {
            this._layersListMouseUpDelegate = null;
        }
        if (this._layersListMouseDownDelegate) {
            this._layersListMouseDownDelegate = null;
        }
        if (this._layersListMouseMoveDelegate) {
            this._layersListMouseMoveDelegate = null;
        }
        if (!this._onStatusChangedDelegate) {
            this._onStatusChangedDelegate = Function.createDelegate(this, this._onStatusChanged);
            if (cv) cv.remove_statusChanged(this._onStatusChangedDelegate);
        }

        if ($get(this._modalDialogPanelId)) {
            var el = $get(this._modalDialogPanelId);
            $removeHandler(el, 'click', this._falseHandler);
            $removeHandler(el, 'mousedown', this._falseHandler);
            $removeHandler(el, 'mouseup', this._falseHandler);
            $removeHandler(el, 'mousemove', this._falseHandler);
        }

        // redo undo

        if (this._redoButtonClickDelegate) {
            $removeHandler($get(this._redoButtonId), 'click', this._redoButtonClickDelegate);
            this._redoButtonClickDelegate = null;
        }

        if (this._undoButtonClickDelegate) {
            $removeHandler($get(this._undoButtonId), 'click', this._undoButtonClickDelegate);
            this._undoButtonClickDelegate = null;
        }

        if (this._redoUndoChangedDelegate) {
            var cv = this.get_canvas();
            if (cv) {
                cv.get_history().remove_changed(this._redoUndoChangedDelegate);
            }
            this._redoUndoChangedDelegate = null;
        }

        if (this._setHideMenuTimeoutDelegate) {
            $removeHandler($get(this._addLayerId), "mouseout", this._setHideMenuTimeoutDelegate);
            $removeHandler($get(this._newLayerMenuId), "mouseout", this._setHideMenuTimeoutDelegate);
            delete this._setHideMenuTimeoutDelegate;
        }
        if (this._clearHideMenuTimeoutDelegate) {
            $removeHandler($get(this._newLayerMenuId), "mouseover", this._clearHideMenuTimeoutDelegate);
            delete this._clearHideMenuTimeoutDelegate;
        }

        if (this._showLayerMenuDelegate) {
            $removeHandler($get(this._addLayerId), "mouseover", this._showLayerMenuDelegate);
            delete this._showLayerMenuDelegate;
        }

        if (this._hideLayerMenuDelegate) {
            $removeHandler($get(this._newLayerMenuId), "click", this._hideLayerMenuDelegate);
            delete this._hideLayerMenuDelegate;
        }

        if (this._addVLineDelegate) {
            $removeHandler($get(this._addLineId), "click", this._addVLineDelegate);
            delete this._addVLineDelegate;
        }

        if (this._addVRectangleDelegate) {
            $removeHandler($get(this._addRectangleId), "click", this._addVRectangleDelegate);
            delete this._addVRectangleDelegate;
        }

        if (this._addVEllipseDelegate) {
            $removeHandler($get(this._addEllipseId), "click", this._addVEllipseDelegate);
            delete this._addVEllipseDelegate;
        }

        if (this._navigatorButtonClickDelegate) {
            $removeHandler($get(this._noneNavigatorButtonId), 'click', this._navigatorButtonClickDelegate);
            $removeHandler($get(this._panNavigatorButtonId), 'click', this._navigatorButtonClickDelegate);
            $removeHandler($get(this._zoomInNavigatorButtonId), 'click', this._navigatorButtonClickDelegate);
            $removeHandler($get(this._zoomOutNavigatorButtonId), 'click', this._navigatorButtonClickDelegate);
            this._navigatorButtonClickDelegate = null;
        }

        if (this._zoomModeChangedDelegate) {
            $removeHandler($get(this._zoomModeSelectId), 'change', this._zoomModeChangedDelegate);
            this._zoomModeChangedDelegate = null;
        }

        if (this._zoomChangedDelegate) {
            var cv = this.get_canvasViewer();
            if (cv) {
                cv.remove_zoomed(this._zoomChangedDelegate);
                this._zoomChangedDelegate = false;
            }
        }

        if (this._textCustomFontClickDelegate) {
            $removeHandler($get(this._textCustomFontId), 'click', this._textCustomFontClickDelegate);
            this._textCustomFontClickDelegate = null;
        }

        if (this._setTextValueFocusDelegate) {
            if (this.get_canvasViewer()) {
                this.get_canvasViewer().remove_workspaceMouseUp(this._setTextValueFocusDelegate);
            }
            this._setTextValueFocusDelegate = null;
        }

        if (this._textValueEnteredDelegate) {
            $removeHandler(document, 'keydown', this._textValueEnteredDelegate);
            this._textValueEnteredDelegate = null;
        }

        if (this._alignmentClickDelegate) {
            $removeHandler($get(this._alignmentLeftId), "click", this._alignmentClickDelegate);
            $removeHandler($get(this._alignmentCenterId), "click", this._alignmentClickDelegate);
            $removeHandler($get(this._alignmentRightId), "click", this._alignmentClickDelegate);
            this._alignmentClickDelegate = null;
        }

        if (this._objectAlignmentClickDelegate) {
            $removeHandler($get(this._leftAlignmentButtonId), "click", this._objectAlignmentClickDelegate);
            $removeHandler($get(this._rightAlignmentButtonId), "click", this._objectAlignmentClickDelegate);
            $removeHandler($get(this._topAlignmentButtonId), "click", this._objectAlignmentClickDelegate);
            $removeHandler($get(this._bottomAlignmentButtonId), "click", this._objectAlignmentClickDelegate);
            $removeHandler($get(this._horizontalAlignmentButtonId), "click", this._objectAlignmentClickDelegate);
            $removeHandler($get(this._verticalAlignmentButtonId), "click", this._objectAlignmentClickDelegate);
            this._objectAlignmentClickDelegate = null;
        }

        if (this._moveLayerClickDelegate) {
            $removeHandler($get(this._moveUpLayerButtonId), "click", this._moveLayerClickDelegate);
            $removeHandler($get(this._moveDownLayerButtonId), "click", this._moveLayerClickDelegate);
            $removeHandler($get(this._moveTopLayerButtonId), "click", this._moveLayerClickDelegate);
            $removeHandler($get(this._moveBottomLayerButtonId), "click", this._moveLayerClickDelegate);
            delete this._moveLayerClickDelegate;
        }
    },

    _textCustomFontClick: function () {
        var v = $get(this._textCustomFontId).checked;
        $get(this._pulldownId).style.visibility = (v) ? "inherit" : "hidden";
    },

    _raiseEvent: function (name, args) {
        /// <param name="name" type="String" />
        /// <param name="args" />
        var h = this.get_events().getHandler(name);
        if (h) {
            if (args == undefined) {
                args = Sys.EventArgs.Empty;
            }
            h(this, args);
        }
    },

    add_addingVImage: function (handle) {
        this.get_events().addHandler("AddingVImage", handle);
    },

    remove_addingVImage: function (handle) {
        this.get_events().removeHandler("AddingVImage", handle);
    },

    add_addingVText: function (handle) {
        this.get_events().addHandler("AddingVText", handle);
    },

    remove_addingVText: function (handle) {
        this.get_events().removeHandler("AddingVText", handle);
    },

    // BUGBUG [Dmitry] Свойства  get_nextXXXNumber вынести в отдельный класс в PFSProject. Данная функциональность здесь не очень логична.
    // BUGBUG [Andrey] вынес в PsdEditor. в принципе они тут и используются
    get_nextTextNumber: function () {
        return this.get_nextVObjectNumber("Text");
    },

    // BUGBUG [Dmitry] Свойства  get_nextXXXNumber вынести в отдельный класс в PFSProject. Данная функциональность здесь не очень логична.
    // BUGBUG [Andrey] вынес в PsdEditor
    get_nextImageNumber: function () {
        return this.get_nextVObjectNumber("Image");
    },

    // BUGBUG [Dmitry] Свойства  get_nextXXXNumber вынести в отдельный класс в PFSProject. Данная функциональность здесь не очень логична.
    // BUGBUG [Andrey] вынес в PsdEditor
    get_nextLineNumber: function () {
        return this.get_nextVObjectNumber("Line");
    },

    // BUGBUG [Dmitry] Свойства  get_nextXXXNumber вынести в отдельный класс в PFSProject. Данная функциональность здесь не очень логична.
    // BUGBUG [Andrey] вынес в PsdEditor
    get_nextRectangleNumber: function () {
        return this.get_nextVObjectNumber("Rectangle");
    },

    // BUGBUG [Dmitry] Свойства  get_nextXXXNumber вынести в отдельный класс в PFSProject. Данная функциональность здесь не очень логична.
    // BUGBUG [Andrey] вынес в PsdEditor
    get_nextEllipseNumber: function () {
        return this.get_nextVObjectNumber("Ellipse");
    },

    // BUGBUG [Dmitry] Свойства  get_nextXXXNumber вынести в отдельный класс в PFSProject. Данная функциональность здесь не очень логична.
    //   Реализовать так же через Canvas.Tags.
    // BUGBUG [Andrey] вынес в PsdEditor
    get_nextVObjectNumber: function (vObjectType) {
        var cv = this.get_canvas();
        var count = cv.get_tags()[vObjectType] ? cv.get_tags()[vObjectType] : 0;
        count++;
        cv.get_tags()[vObjectType] = count;
        return count;
    }
}

Aurigma.PsdEditor.registerClass("Aurigma.PsdEditor", Sys.Component);

