<?xml version="1.0"?>
<!DOCTYPE bindings SYSTEM "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" [
  <!ENTITY % pencilDTD SYSTEM "chrome://pencil/locale/pencil.dtd">
  %pencilDTD;
]>

<bindings xmlns="http://www.mozilla.org/xbl"
        xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
        xmlns:html="http://www.w3.org/1999/xhtml"
        xmlns:svg="http://www.w3.org/2000/svg"
        xmlns:xlink="http://www.w3.org/1999/xlink"
        xmlns:p="http://www.evolus.vn/Namespace/Pencil">
    <binding id="canvas">
        <implementation>
            <constructor>
            <![CDATA[
                this.xferHelpers = [];
                this.dragObservers = [];

                this.spaceHeld = false;
                this.painterPropertyMap = this.getPainterPropertyMap();

                //building the content as: box >> svg
                var thiz = this;

                var box = document.createElementNS(PencilNamespaces.xul, "xul:box");
                this.appendChild(box);
                var box2 = document.createElementNS(PencilNamespaces.xul, "xul:box");
                box2.setAttribute("focusable", true);
                this.firstChild.appendChild(box2);
                this.focusableBox = box2;

                this.addEventListener("mousedown", function (event) {
                    var inDrawing = Dom.findUpward(event.originalTarget, function (node) {
                        return (node == box2 || node == thiz.popup || node == thiz.svg);
                    });
                    if (!inDrawing) {
                        thiz.clearSelection();
                        thiz.selectNone();
                    }
                }, false);

                //create the context menu
                this.popup = document.getAnonymousElementByAttribute(this, "aid", "contextPopup");
                this.arrangementMenu = document.getAnonymousElementByAttribute(this, "aid", "arrangementMenu");
                this.linkMenu = document.getAnonymousElementByAttribute(this, "aid", "linkMenu");
                this.arrangementMenu._canvas = this;

                this.undoContextMenu = document.getAnonymousElementByAttribute(this, "aid", "contextUndoMenu");
                this.redoContextMenu = document.getAnonymousElementByAttribute(this, "aid", "contextRedoMenu");

                this.popupSeparator = document.getAnonymousElementByAttribute(this, "aid", "contextPopupSeparator");
                var id = "popup" + Math.round(1000 * Math.random());
                this.popup.setAttribute("id", id);
                box2.setAttribute("context", id);

                this.propertyMenuItem = document.getAnonymousElementByAttribute(this, "aid", "propertyMenuItem");
                this.lockingMenuItem = document.getAnonymousElementByAttribute(this, "aid", "lockingMenuItem");

                this.svg = document.createElementNS(PencilNamespaces.svg, "svg:svg");
                this.svg.setAttribute("version", "1.0");

                //FIXME: this will not be hard-coded
                this.width = this.hasAttribute("width") ? parseInt(this.getAttribute("width"), 10) : 780;
                this.height = this.hasAttribute("height") ? parseInt(this.getAttribute("height"), 10) : 600;
                box2.appendChild(this.svg);

                this.topGroup = document.createElementNS(PencilNamespaces.svg, "svg:g");

                var fixTarget = this.topGroup;
                var fixRef = this.svg;

                window.setTimeout(function () {
                    try {
                        var matrix = fixTarget.getScreenCTM();
                        var dx = matrix.e - Math.floor(matrix.e);
                        var dy = matrix.f - Math.floor(matrix.f);

                        if ((dx != 0 || dy != 0) && thiz.zoom == 1) {
                            matrix = fixRef.getScreenCTM();
                            dx = (matrix.e - Math.floor(matrix.e)) / thiz.zoom;
                            dy = (matrix.f - Math.floor(matrix.f)) / thiz.zoom;

                            var t = "translate(" + (0 - dx) + "," + (0 - dy) + ")";
                            fixTarget.setAttribute("transform", t);
                        }
                    } catch (e) { }
                    window.setTimeout(arguments.callee, 1000);
                }, 1000); 
                
                this.svg.appendChild(this.topGroup);

                //create the background layer
                this.bgLayer = document.createElementNS(PencilNamespaces.svg, "svg:g");
                this.bgLayer.setAttributeNS(PencilNamespaces.p, "p:name", "Background");
                var rect = document.createElementNS(PencilNamespaces.svg, "svg:rect");
                rect.setAttribute("x", "0");
                rect.setAttribute("y", "0");
                rect.setAttribute("width", "2000");
                rect.setAttribute("height", "2000");
                rect.setAttribute("style", "fill: #ffffff; stroke: none; fill-opacity: 0;");
                this.bgLayer.appendChild(rect);

                //background image
                this.backgroundImage = document.createElementNS(PencilNamespaces.svg, "svg:image");
                this.backgroundImage.setAttribute("x", "0");
                this.backgroundImage.setAttribute("y", "0");
                this.bgLayer.appendChild(this.backgroundImage);
                this.hasBackgroundImage = false;

                var rect = document.createElementNS(PencilNamespaces.svg, "svg:rect");
                rect.setAttribute("x", "0");
                rect.setAttribute("y", "0");
                rect.setAttribute("width", "2000");
                rect.setAttribute("height", "2000");
                rect.setAttributeNS(PencilNamespaces.p, "p:name", "BackgroundDimmer");
                this.bgLayer.appendChild(rect);

                this.topGroup.appendChild(this.bgLayer);

                //create the drawing layer
                this.drawingLayer = document.createElementNS(PencilNamespaces.svg, "svg:g");
                this.topGroup.appendChild(this.drawingLayer);

                //create the control layer
                this.controlLayer = document.createElementNS(PencilNamespaces.svg, "svg:g");
                this.controlLayer.setAttributeNS(PencilNamespaces.p, "p:type", "ControlLayer");

                this.topGroup.appendChild(this.controlLayer);

                this.selectionContainer = document.createElementNS(PencilNamespaces.svg, "svg:g");
                this.selectionContainer.setAttribute("id", "selectionContainer");
                this.installControlSVGElement(this.selectionContainer);

                this.rangeBoundRect = document.createElementNS(PencilNamespaces.svg, "svg:rect");
                this.installControlSVGElement(this.rangeBoundRect);
                Svg.setX(this.rangeBoundRect, 0.5);
                Svg.setY(this.rangeBoundRect, 0.5);
                Svg.setWidth(this.rangeBoundRect, 100);
                Svg.setHeight(this.rangeBoundRect, 100);
                this.rangeBoundRect.setAttributeNS(PencilNamespaces.p, "p:type", "RangeBound");

                this.snappingHelper = new SnappingHelper(this);

                this.idSeed = 1;

                this.onScreenEditors = [];
                

                //register event handler
                this.svg.addEventListener("click", function (event) {
                    thiz.handleClick(event);
                }, false);
                this.svg.addEventListener("mousedown", function (event) {
                    //document.commandDispatcher.advanceFocus();
                    thiz.focus();
                    thiz.handleMouseDown(event);
                }, false);
                this.svg.ownerDocument.addEventListener("mouseup", function (event) {
                    if (!thiz || !thiz.handleMouseUp) {
                        document.removeEventListener("mouseup", arguments.callee, false);
                        return;
                    }
                    thiz.handleMouseUp(event);
                }, false);
                this.svg.ownerDocument.addEventListener("mousemove", function (event) {
                    if (!thiz || !thiz.handleMouseMove) {
                        document.removeEventListener("mousemove", arguments.callee, false);
                        return;
                    }
                    thiz.handleMouseMove(event);
                }, false);
                this.focusableBox.addEventListener("keypress", function (event) {
                    if (thiz.handleKeyPress) thiz.handleKeyPress(event);
                }, false);

                this.svg.ownerDocument.addEventListener("keydown", function (event) {
                    if (event.keyCode == event.DOM_VK_SPACE && thiz.spaceHeld == false) {
                        thiz.spaceHeld = true;
                        thiz._lastPX = thiz._currentPX;
                        thiz._lastPY = thiz._currentPY;
                        thiz._lastScrollX = thiz.parentNode.scrollLeft;
                        thiz._lastScrollY = thiz.parentNode.scrollTop;
                        Dom.addClass(thiz, "PanDown");
                    }
                }, false);
                this.svg.ownerDocument.addEventListener("keyup", function (event) {
                    if (event.keyCode == event.DOM_VK_SPACE && thiz.spaceHeld == true) {
                        thiz.spaceHeld = false;
                        Dom.removeClass(thiz, "PanDown");
                    }
                }, false);

                this.propertyMenuItem.addEventListener("command", function(event) {
                    //thiz.handlePropertyMenuItemCommand(event);
                }, false);

                box2.addEventListener("contextmenu", function (event) {
                    thiz.handleContextMenuShow(event);
                }, false);

                this.svg.addEventListener("dblclick", function (event) {
                    stencilDebug("pCanvas, dblclick");
                    thiz.handleDblclick(event);
                }, true);

                this.careTaker = new CanvasCareTaker(this);

                /*
                this.dragObserver = {
                    getSupportedFlavours : function () {
                        var flavours = new FlavourSet();

                        flavours.appendFlavour("pencil/def");
                        flavours.appendFlavour("pencil/shortcut");
                        flavours.appendFlavour("text/unicode");
                        //flavours.appendFlavour("image/png");

                        return flavours;
                    },
                    onDragOver: function (evt, flavour, session){},
                    onDrop: function (evt, transferData, session) {

                        var defId = transferData.data;
                        var def = CollectionManager.shapeDefinition.locateDefinition(defId);

                        var loc = thiz.getEventLocation(evt);

                        if (loc.x <0 || loc.y < 0) return;

                        thiz.insertShape(def, new Bound(loc.x, loc.y, null, null));
                    }
                };
                */

                this.nVGridPainted = 0;
                this.nHGridPainted = 0;


                this.zoomTo(1);
            ]]>
            </constructor>
            <method name="getEventLocation">
                <parameter name="event"/>
                <body>
                <![CDATA[
                        var rect = this.svg.parentNode.getBoundingClientRect();
                        var x = Math.round(event.clientX - rect.left);
                        var y = Math.round(event.clientY - rect.top);

                        return {x: x, y: y};
                ]]>
                </body>
            </method>
            <method name="addToSelection">
                <parameter name="target"/>
                <body>
                <![CDATA[
                    var rect = document.createElementNS(PencilNamespaces.svg, "svg:rect");
                    this.selectionContainer.appendChild(rect);
                    rect._target = target;

                    this._invalidateOneSelection(rect);
                ]]>
                </body>
            </method>
            <method name="_invalidateOneSelection">
                <parameter name="rect"/>
                <body>
                <![CDATA[
                    var bbox = rect._target.getBoundingRect();
                    Svg.setX(rect, Math.round(bbox.x));
                    Svg.setY(rect, Math.round(bbox.y));
                    Svg.setWidth(rect, Math.round(bbox.width));
                    Svg.setHeight(rect, Math.round(bbox.height));
                ]]>
                </body>
            </method>
            <method name="_sayTargetChanged">
                <body>
                <![CDATA[
                    Dom.emitEvent("p:TargetChanged", this, {canvas: this});
                ]]>
                </body>
            </method>
            <method name="getSelectedTargets">
                <body>
                <![CDATA[
                    var targets = [];
                    for (var i = 0; i < this.selectionContainer.childNodes.length; i ++) {
                        var rect = this.selectionContainer.childNodes[i];
                        if (rect._target) targets.push(rect._target);
                    }

                    return targets;
                ]]>
                </body>
            </method>
            <method name="clearSelection">
                <body>
                <![CDATA[
                    while (this.selectionContainer.hasChildNodes()) {
                        this.selectionContainer.removeChild(this.selectionContainer.firstChild);
                    }
                ]]>
                </body>
            </method>
            <method name="selectSibling">
                <parameter name="next"/>
                <body>
                <![CDATA[
                    var targets = this.getSelectedTargets();
                    var node = null;

                    var sibling = null;

                    if (targets && targets[0] && targets[0].svg) {
                        node = targets[0].svg;

                        if (next) {
                            if (node.nextSibling) {
                                sibling = node.nextSibling;
                            } else {
                                sibling = this.drawingLayer.firstChild;
                            }
                        } else {
                            if (node.previousSibling) {
                                sibling = node.previousSibling;
                            } else {
                                sibling = this.drawingLayer.lastChild;
                            }
                        }

                    } else {
                        sibling = this.drawingLayer.firstChild;
                    }

                    if (!sibling) return;

                    this.selectShape(sibling);
                ]]>
                </body>
            </method>
            <method name="selectAll">
                <body>
                <![CDATA[
                    this.clearSelection();
                    var thiz = this;
                    Dom.workOn("./svg:g[@p:type]", this.drawingLayer, function (node) {
                        if (thiz.isShapeLocked(node)) return;
                        try {
                            var controller = thiz.createControllerFor(node);
                            thiz.addToSelection(controller);
                        } catch (e) {
                            //alert(e);
                        }
                    });

                    var targets = this.getSelectedTargets();
                    this.setAttributeNS(PencilNamespaces.p, "p:selection", targets.length);

                    var controller = null;
                    if (targets.length > 1) {
                        controller = new TargetSet(this, targets);
                    } else if (targets.length == 1 && controller == null) {
                        controller = targets[0];
                    }

                    this._detachEditors();
                    this.currentController = controller;

                    this._attachEditors(this.currentController);
                    this._sayTargetChanged();
                ]]>
                </body>
            </method>
            <method name="removeFromSelection">
                <parameter name="target"/>
                <body>
                <![CDATA[
                    var foundRect = null;
                    for (var i = 0; i < this.selectionContainer.childNodes.length; i ++) {
                        var rect = this.selectionContainer.childNodes[i];
                        if (rect._target == target) {
                            foundRect = rect;
                            break;
                        }
                    }
                    if (foundRect) this.selectionContainer.removeChild(foundRect);
                ]]>
                </body>
            </method>

            <method name="insertObject">
                <parameter name="obj"/>
                <body>
                <![CDATA[
                    this.drawingLayer.appendChild(obj);
                ]]>
                </body>
            </method>
            <method name="installControlSVGElement">
                <parameter name="obj"/>
                <body>
                <![CDATA[
                    this.controlLayer.appendChild(obj);
                ]]>
                </body>
            </method>
            <method name="zoomTo">
                <parameter name="factor"/>
                <body>
                <![CDATA[
                    this.zoom = factor;
                    this.drawingLayer.setAttribute("transform", "scale(" + [this.zoom, this.zoom] + ")");
                    this.svg.setAttribute("width", Math.ceil(this.width * this.zoom));
                    this.svg.setAttribute("height", Math.ceil(this.height * this.zoom));
                    this.backgroundImage.setAttribute("transform", "scale(" + [this.zoom, this.zoom] + ")");
                    CanvasImpl.setupGrid.apply(this);

                    this.invalidateEditors();
                ]]>
                </body>
            </method>
            <method name="getZoomedGeo">
                <parameter name="target"/>
                <body>
                <![CDATA[
                    if (!target) return null;
                    var geo = target.getGeometry();
                    geo = geo.clone(this.svg);
                    geo.ctm.e *= this.zoom;
                    geo.ctm.f *= this.zoom;
                    geo.dim.w *= this.zoom;
                    geo.dim.h *= this.zoom;

                    if (geo.loc) {
                        geo.loc.x *= this.zoom;
                        geo.loc.y *= this.zoom;
                    }

                    return geo;
                ]]>
                </body>
            </method>
            <method name="getSize">
                <body>
                <![CDATA[
                    return {
                        width: parseInt(this.getAttribute("width"), 10),
                        height: parseInt(this.getAttribute("height"), 10),
                    };
                ]]>
                </body>
            </method>

            <method name="getZoomedRect">
                <parameter name="rect"/>
                <body>
                <![CDATA[
                    rect.x *= this.zoom;
                    rect.y *= this.zoom;
                    rect.width *= this.zoom;
                    rect.height *= this.zoom;

                    return rect;
                ]]>
                </body>
            </method>
            <method name="setZoomedGeo">
                <parameter name="target"/>
                <parameter name="geo"/>
                <parameter name="setter"/>
                <body>
                <![CDATA[
                    geo = geo.clone(this.svg);
                    geo.ctm.e /= this.zoom;
                    geo.ctm.f /= this.zoom;
                    geo.dim.w /= this.zoom;
                    geo.dim.h /= this.zoom;

                    this.run(function() {
                        target.setGeometry(geo);
                    }, this, Util.getMessage("action.canvas.zoom"));

                    Dom.emitEvent("p:ShapeGeometryModified", this, {setter: setter ? setter : null});
                ]]>
                </body>
            </method>
            <method name="redraw">
                <body>
                <![CDATA[
                    this.drawingLayer.ownerSVGElement.forceRedraw();
                ]]>
                </body>
            </method>
            <method name="getType">
                <parameter name="svg"/>
                <body>
                <![CDATA[
                    return svg.getAttributeNS(PencilNamespaces.p, "def");
                ]]>
                </body>
            </method>
            <method name="insertShape">
                <parameter name="shapeDef"/>
                <parameter name="bound"/>
                <parameter name="overridingValueMap"/>
                <body>
                <![CDATA[
                    this.run(this.insertShapeImpl_, this, Util.getMessage("action.create.shape", shapeDef.displayName), [shapeDef, bound ? bound : null, overridingValueMap ? overridingValueMap : null]);
                ]]>
                </body>
            </method>
            <method name="insertShapeImpl_">
                <parameter name="shapeDef"/>
                <parameter name="bound"/>
                <parameter name="overridingValueMap"/>
                <body>
                <![CDATA[
                    // instantiate the shape using the shapedef
                    var shape = this.ownerDocument.createElementNS(PencilNamespaces.svg, "g");
                    shape.setAttributeNS(PencilNamespaces.p, "p:type", "Shape");
                    shape.setAttributeNS(PencilNamespaces.p, "p:def", shapeDef.id);
                    if (overridingValueMap && overridingValueMap._shortcut) {
                        shape.setAttributeNS(PencilNamespaces.p, "p:sc",
                                overridingValueMap._shortcut.displayName);
                    }

                    shape.appendChild(this.ownerDocument.createElementNS(PencilNamespaces.p, "p:metadata"));

                    for (var i = 0; i < shapeDef.contentNode.childNodes.length; i ++) {
                        shape.appendChild(this.ownerDocument.importNode(shapeDef.contentNode.childNodes[i], true));
                    }


                    //generate the ids
                    Dom.workOn(".//*[@p:name]", shape, function (node) {
                        var name = node.getAttributeNS(PencilNamespaces.p, "name");
                        var oldId = node.getAttribute("id");
                        if (oldId) return;

                        var uuid = Util.newUUID();
                        node.setAttribute("id", uuid);
                        node.id = uuid;

                        Dom.updateIdRef(shape, name, uuid);
                    });

                    Dom.renewId(shape);

                    // add the newly created shape into the drawing layer
                    this.drawingLayer.appendChild(shape);

                    // applying defined behaviors into the shape

                    var controller = new Shape(this, shape);
                    controller.setInitialPropertyValues(overridingValueMap);

                    if (bound) {
                        var bbox = controller.getBoundingRect();
                        controller.moveBy((bound.x - Math.round(bbox.width / 2)) / this.zoom, (bound.y - Math.round(bbox.height / 2)) / this.zoom, true);
                        controller.normalizePositionToGrid();
                    }

                    this.selectShape(shape);

                    this.snappingHelper.updateSnappingGuide(this.currentController);
                    DockingManager.enableDocking(this.currentController);

                ]]>
                </body>
            </method>
            <method name="selectShape">
                <parameter name="shape"/>
                <body>
                <![CDATA[
                    if (this.isShapeLocked(shape)) return;
                    var controller = this.createControllerFor(shape);

                    this.currentController = controller;
                    this.clearSelection();
                    this.addToSelection(this.currentController);
                    this.setAttributeNS(PencilNamespaces.p, "p:selection", 1);

                    this.lastTop = shape;
                    this.hasMoved = true;

                    this.focus();

                    this._attachEditors(controller);

                    this._sayTargetChanged();
                ]]>
                </body>
            </method>
            <method name="selectMultiple">
                <parameter name="shapes"/>
                <body>
                <![CDATA[
                    this.clearSelection();
                    for (i in shapes) {
                        if (this.isShapeLocked(shapes[i])) continue;
                        this.addToSelection(this.createControllerFor(shapes[i]));
                    }
                    this.setAttributeNS(PencilNamespaces.p, "p:selection", shapes.length);

                    var targets = this.getSelectedTargets();
                    if (targets.length > 1) {
                        controller = new TargetSet(this, targets);
                    } else if (targets.length == 1 && controller == null) {
                        controller = targets[0];
                    }


                    this.currentController = controller;

                    this.lastTop = null;
                    this.hasMoved = true;

                    this.focus();

                    this._attachEditors(controller);

                    this._sayTargetChanged();
                ]]>
                </body>
            </method>
            <method name="selectNone">
                <body>
                <![CDATA[
                    this.clearSelection();
                    this._detachEditors();
                    this.currentController = null;

                    this.lastTop = null;
                    this.hasMoved = true;

                    this.focus();
                    this._sayTargetChanged();
                ]]>
                </body>
            </method>
            <method name="createControllerFor">
                <parameter name="top"/>
                <body>
                <![CDATA[
                    var type = top.getAttributeNS(PencilNamespaces.p, "type");
                    if (type == "Shape") {
                        try {
                            return new Shape(this, top);
                        } catch (e) {}
                    } else if (type == "Group") {
                        return new Group(this, top);
                    }
                    return new Null(this, top);
                ]]>
                </body>
            </method>
            <method name="_detachEditors">
                <body>
                <![CDATA[
                    for (var editor in this.onScreenEditors) this.onScreenEditors[editor].dettach();
                    if (this.propertyPageEditor && this.propertyPageEditor.dettach) this.propertyPageEditor.dettach();
                    if (this.contextMenuEditor && this.contextMenuEditor.dettach) this.contextMenuEditor.dettach();
                ]]>
                </body>
            </method>
            <method name="passivateEditors">
                <body>
                <![CDATA[
                    for (editor in this.onScreenEditors) this.onScreenEditors[editor].passivated = true;
                    if (this.propertyPageEditor) this.propertyPageEditor.passivated = true;
                    if (this.contextMenuEditor) this.contextMenuEditor.passivated = true;
                ]]>
                </body>
            </method>
            <method name="_attachEditors">
                <parameter name="controller"/>
                <body>
                <![CDATA[
                    for (editor in this.onScreenEditors) this.onScreenEditors[editor].attach(controller);
                    if (this.propertyPageEditor && this.propertyPageEditor.attach) this.propertyPageEditor.attach(controller);
                    //if (this.contextMenuEditor && this.contextMenuEditor.attach) this.contextMenuEditor.attach(controller);
                ]]>
                </body>
            </method>
            <method name="handleMouseDown">
                <parameter name="event"/>
                <body>
                <![CDATA[
                    tick("begin");
                    Dom.emitEvent("p:CanvasMouseDown", this, {});

                    var canvasList = Pencil.getCanvasList();
                    for (var i = 0; i < canvasList.length; i++) {
                        if (canvasList[i] != this) {
                            canvasList[i].selectNone();
                        }
                    };

                    this.snappingHelper.clearSnappingGuide();

                    this._button = event.button;

                    var thiz = this;
                    var isInControlLayer = Dom.findUpward(event.originalTarget, function (node) {
                        return (node == thiz.controlLayer);
                    });
                    if (isInControlLayer) return;

                    var top = Dom.findTop(event.originalTarget, function (node) {
                        return node.hasAttributeNS && node.hasAttributeNS(PencilNamespaces.p, "type");
                    });
                    if (top && this.isShapeLocked(top)) top = null;

                    if (!top) {
                        this.lastTop = null;
                        //this.clearSelection();
                        //this._detachEditors();
                        this.currentController = null;
                        //this.setAttributeNS(PencilNamespaces.p, "p:selection", 0);

                        this.isSelectingRange = true;
                        this.lastMousePos = this.getEventLocation(event);
                        this.setRangeBoundStart(this.lastMousePos.x, this.lastMousePos.y);
                        this.setRangeBoundVisibility(true);
                        this.setRangeBoundSize(0, 0);
                        this.currentRange = {w: this.lastMousePos.x, y: this.lastMousePos.y, width: 0, height: 0};

                        this._sayTargetChanged();
                        this.endFormatPainter();

                        return;
                    }

                    var controller = null;

                    var targets = this.getSelectedTargets();
                    var foundTarget = null;
                    for (i in targets) {
                        var target = targets[i];
                        if (target.isFor(top)) {
                            foundTarget = target;
                            break;
                        }
                    }
                    if (event.ctrlKey) {
                        if (foundTarget) {
                            this.removeFromSelection(foundTarget);
                        } else {
                            var newController = this.createControllerFor(top);
                            this.addToSelection(newController);
                        }
                    } else {
                        if (!foundTarget || targets.length == 1) {
                            this.clearSelection();
                            controller = this.createControllerFor(top);
                            this.addToSelection(controller);
                        }
                    }

                    var targets = this.getSelectedTargets();
                    if (targets.length > 1) {
                        controller = new TargetSet(this, targets);
                    } else if (targets.length == 1 && controller == null) {
                        controller = targets[0];
                    }

                    this.setAttributeNS(PencilNamespaces.p, "p:selection", targets.length);


                    if (controller) {
                        try {
                            this.currentController = controller;
                            this.controllerHeld = true;

                            this.oX = Math.round(event.clientX / this.zoom);
                            this.oY = Math.round(event.clientY / this.zoom);

                            this._lastNewX = Math.round(event.clientX / this.zoom);
                            this._lastNewY = Math.round(event.clientY / this.zoom);

                            this.oldPos = this.currentController.getGeometry();

                            tick("before setPositionSnapshot");
                            this.currentController.setPositionSnapshot();
                            tick("after setPositionSnapshot");

                            if (event.button == 0) this.setAttributeNS(PencilNamespaces.p, "p:holding", "true");

                            if (top != this.lastTop || event.ctrlKey || event.button != 0) {
                                this.reClick = false;
                                this._attachEditors(controller);
                            } else {
                                if (event.detail != 2) this.reClick = true;
                            }

                            this.hasMoved = false;
                            this.lastTop = top;
                            this._sayTargetChanged();
                            tick("done");
                        } catch (e) {
                            Console.dumpError(e);
                        }
                    }
                ]]>
                </body>
            </method>
            <method name="finishMoving">
                <parameter name="event"/>
                <body>
                <![CDATA[
                    if (this.controllerHeld && this.currentController && this.currentController.markAsMoving) {
                        this.currentController.markAsMoving(false);
                        this.currentController.clearPositionSnapshot();
                        this.invalidateEditors();

                        this.snappingHelper.updateSnappingGuide(this.currentController);
                        this.snappingHelper.clearSnappingGuide();

                        Dom.emitEvent("p:ShapeGeometryModified", this, {setter: null});

                        if (this.hasMoved) {
                            //just to save state
                            this.run(function() {}, this, Util.getMessage("action.shape.finish.moving", this.currentController.getName()));
                        }
                    }

                    this.removeAttributeNS(PencilNamespaces.p, "holding");
                    this.controllerHeld = false;

                    if (Config.get("quick.editting", false) == true) {
                        Dom.emitEvent("p:ShapeInserted", this.currentController.svg, {controller: this.currentController, origTarget: event.originalTarget, clientX: event.clientX, clientY: event.clientY});
                    }
                ]]>
                </body>
            </method>
            <method name="handleMouseUp">
                <parameter name="event"/>
                <body>
                <![CDATA[
                    if (this.reClick && !this.hasMoved) {
                        for (editor in this.onScreenEditors) this.onScreenEditors[editor].nextTool();
                    }
                    if (this.controllerHeld && this.currentController && this.currentController.markAsMoving) {
                        this.currentController.markAsMoving(false);
                        this.currentController.clearPositionSnapshot();
                        this.invalidateEditors();

                        if (this.hasMoved) {
                            debug("current controller: " + this.currentController.constructor.name);
                            Dom.emitEvent("p:ShapeGeometryModified", this, {setter: null});
                        }
                        
                        if (this.currentController.invalidateOutboundConnections) {
                            this.currentController.invalidateOutboundConnections();
                        }
                        if (this.currentController.invalidateInboundConnections) {
                            this.currentController.invalidateInboundConnections();
                        }
                    }
                    if (this.controllerHeld && this.hasMoved) {
                        //just to save state
                        this.run(function() {}, this, Util.getMessage("action.move.shape"));
                    }

                    this.removeAttributeNS(PencilNamespaces.p, "holding");

                    this.reClick = false;
                    this.hasMoved = true;

                    this.controllerHeld = false;

                    if (this.isSelectingRange) {
                        this.setRangeBoundVisibility(false);
                        this.isSelectingRange = false;
                        //enum objects that are in range
                        if (!event.ctrlKey) {
                            this.clearSelection();
                        }
                        var thiz = this;
                        Dom.workOn("./svg:g[@p:type]", this.drawingLayer, function (node) {
                            if (thiz.isShapeLocked(node)) return;
                            var controller = thiz.createControllerFor(node);
                            var bbox = controller.getBoundingRect();

                            if (Svg.isInside(bbox, thiz.currentRange)) {
                                if (event.ctrlKey) {
                                    var targets = thiz.getSelectedTargets();
                                    var foundTarget = null;
                                    for (i in targets) {
                                        var target = targets[i];
                                        if (target.isFor(node)) {
                                            foundTarget = target;
                                            break;
                                        }
                                    }

                                    if (foundTarget) {
                                        thiz.removeFromSelection(foundTarget);
                                    } else {
                                        var target = thiz.createControllerFor(node);
                                        thiz.addToSelection(target);
                                    }
                                } else {
                                    var target = thiz.createControllerFor(node);
                                    thiz.addToSelection(target);
                                }
                            }
                        });
                        var controller = null;
                        var targets = this.getSelectedTargets();
                        this.setAttributeNS(PencilNamespaces.p, "p:selection", targets.length);

                        if (targets.length > 1) {
                            controller = new TargetSet(this, targets);
                        } else if (targets.length == 1 && controller == null) {
                            controller = targets[0];
                        }

                        this._detachEditors();
                        if (controller) {
                            this.currentController = controller;
                            this._attachEditors(this.currentController);
                        }
                        this.currentRange = null;
                        this._sayTargetChanged();
                    }

                    try {
                        this.snappingHelper.clearSnappingGuide();

                        if (this.currentController) {
                            if (this.currentController.constructor == TargetSet) {
                                for (var t in this.currentController.targets) {
                                    this.snappingHelper.updateSnappingGuide(this.currentController.targets[t]);
                                }
                            } else {
                                this.snappingHelper.updateSnappingGuide(this.currentController);
                            }
                        }

                        DockingManager.enableDocking(this.currentController);

                        var canvas = Dom.findUpward(event.originalTarget, function (node) {
                            return node.namespaceURI == PencilNamespaces.xul && node.localName == "pcanvas";
                        });
                        if (canvas && canvas == this && this.currentController) {
                            if (this.isFormatPainterAvailable() && Pencil._painterSourceTarget.svg != this.currentController.svg) {
                                var currentTargetProperties = this.currentController.getProperties();
                                for (var i = 0; i < this.painterPropertyMap.length; i++) {
                                    var name = this.painterPropertyMap[i];
                                    var p1 = Pencil._painterSourceProperties[name];
                                    var p2 = currentTargetProperties[name];
                                    if (p1 && p2) {
                                        this.currentController.setProperty(name, Pencil._painterSourceProperties[name]);
                                        this.invalidateEditors();
                                        this._sayTargetChanged();
                                    }
                                }
                                return;
                            }
                            this.endFormatPainter();
                        }
                    } catch (e) {
                        Console.dumpError(e);
                    }
                ]]>
                </body>
            </method>
            <method name="handleClick">
                <parameter name="event"/>
                <body>
                <![CDATA[
                    //is it from an html:a?
                    var a = Dom.findUpward(event.originalTarget, function (node) {
                        return (node && node.nodeType == 1
                                    && node.localName.toLowerCase() == "a"
                                    && node.namespaceURI == PencilNamespaces.html);
                    });

                    if (!a) return;

                    //is this html:a inside an svg:foreignObject?
                    var foreignObject = Dom.findUpward(a, function (node) {
                        return (node && node.nodeType == 1
                                    && node.localName == "foreignObject"
                                    && node.namespaceURI == PencilNamespaces.svg);
                    });

                    //ok, it is then prevent it
                    if (foreignObject) {
                        event.preventDefault();
                        event.cancelBubble = true;
                    }
                ]]>
                </body>
            </method>
            <method name="handleMouseMove">
                <parameter name="event"/>
                <parameter name="fake"/>
                <body>
                <![CDATA[
                try {
                    this._currentPX = event.clientX / this.zoom;
                    this._currentPY = event.clientY / this.zoom;

                    if (this.spaceHeld) {
                        var spanx = this._lastScrollX + (this._lastPX - Math.round(this._currentPX)) * this.zoom;
                        var spany = this._lastScrollY + (this._lastPY - Math.round(this._currentPY)) * this.zoom;

                        this.parentNode.scrollTop  = spany;
                        this.parentNode.scrollLeft = spanx;

                        return;
                    }

                    if (event.originalTarget && event.originalTarget.nodeName && "menu,menuitem,menuseparator,toolbar".indexOf(event.originalTarget.nodeName) == -1) {
                        var boxObject = this.svg.parentNode.boxObject;
                        var px = this._currentPX - boxObject.x;
                        var py = this._currentPY - boxObject.y;
                        // shadow 5
                        if (px >= 0 && px <= boxObject.width - 5 && py >= 0 && py <= boxObject.height - 5) {
                            //setTimeout(function () { Util.setPointerPosition(px, py); }, 100);
                        }
                    }

                    if (this._button != 0) return;

                    if (!fake) {
                        event.preventDefault();
                        event.stopPropagation();
                    }

                    if (this.controllerHeld && this.currentController) {
                        if (this.currentController.markAsMoving) this.currentController.markAsMoving(true);
                        var newX = Math.round(event.clientX / this.zoom);
                        var newY = Math.round(event.clientY / this.zoom);

                        var dx = newX - this.oX;
                        var dy = newY - this.oY;

                        var accX = Math.abs(newX - this._lastNewX) < 2;
                        var accY = Math.abs(newY - this._lastNewY) < 2;

                        this._lastNewX = newX;
                        this._lastNewY = newY;

                        this.currentX = newX;
                        this.currentY = newY;

                        //this.oX = newX;
                        //this.oY = newY;

                        this.hasMoved = true;

                        if (this.currentController.dockingManager) {
                            this.currentController.dockingManager.altKey = event.altKey;
                        }

                        var gridSize = Pencil.getGridSize();
                        var snap = null;
                        if (Config.get("object.snapping.enabled", false) == true) {
                            snap = this.snappingHelper.findSnapping(accX && !this.snappingHelper.snappedX, accY && !this.snappingHelper.snappedY, null, null, event.shiftKey);
                        }
                        if (Config.get("edit.snap.grid", false) == true) {
                            var snapGrid = this.snappingHelper.findSnapping(accX && !this.snappingHelper.snappedX, accY && !this.snappingHelper.snappedY, null, gridSize.w / 2, event.shiftKey, true);
                            if (snap && snapGrid) {
                                if (snap.dx == 0) {
                                    snap.dx = snapGrid.dx;
                                }
                                if (snap.dy == 0) {
                                    snap.dy = snapGrid.dy;
                                }
                            } else {
                                snap = snapGrid;
                            }
                            //debug("snap grid: " + [snapGrid.dx, snapGrid.dy]);
                        }
                        //debug("snap: " + [snap.dx, snap.dy, this.snappedX, this.snappedY]);
                        if (!event.shiftKey && snap && ((snap.dx != 0 && !this.snappingHelper.snappedX && accX) || (snap.dy != 0 && !this.snappingHelper.snappedY && accY))) {
                            if (snap.dx != 0 && !this.snappingHelper.snappedX) {
                                this.snappingHelper.snappedX = true;
                                this.snappingHelper.snapX = newX;
                                this.currentController._pSnapshot.lastDX += snap.dx;
                                //debug("snapX");
                            }
                            if (snap.dy != 0 && !this.snappingHelper.snappedY) {
                                this.snappingHelper.snappedY = true;
                                this.snappingHelper.snapY = newY;
                                this.currentController._pSnapshot.lastDY += snap.dy;
                                //debug("snapY");
                            }
                            this.currentController.moveBy(snap.dx, snap.dy);
                        } else {
                            var unsnapX = event.shiftKey || (this.snappingHelper.snapX != 0 && (Math.abs(this.snappingHelper.snapX - newX) > this.snappingHelper.unsnapX));
                            var unsnapY = event.shiftKey || (this.snappingHelper.snapY != 0 && (Math.abs(this.snappingHelper.snapY - newY) > this.snappingHelper.unsnapY));
                            //debug("unsnap: " + [unsnapX, unsnapY]);

                            if (!this.snappingHelper.snappedX && !this.snappingHelper.snappedY) {
                                this.currentController.moveFromSnapshot(dx, dy);
                            } else {
                                if (unsnapX || !this.snappingHelper.snappedX) {
                                    this.currentController.moveFromSnapshot(dx, this.snappingHelper.snappedY ? this.currentController._pSnapshot.lastDY : dy);
                                }
                                if (unsnapY || !this.snappingHelper.snappedY) {
                                    this.currentController.moveFromSnapshot(this.snappingHelper.snappedX ? this.currentController._pSnapshot.lastDX : dx, dy);
                                    this.snappingHelper.snapY = 0;
                                    this.snappingHelper.snappedY = false;
                                }
                                if (unsnapX || !this.snappingHelper.snappedX) {
                                    this.snappingHelper.snapX = 0;
                                    this.snappingHelper.snappedX = false;
                                }
                                if (unsnapX) {
                                    this.snappingHelper.clearSnappingGuideX();
                                }
                                if (unsnapY) {
                                    this.snappingHelper.clearSnappingGuideY();
                                }
                            }
                        }
                        
                        return;
                    }

                    if (this.isSelectingRange) {
                        var end = this.getEventLocation(event);
                        var x1 = Math.min(end.x, this.lastMousePos.x);
                        var x2 = Math.max(end.x, this.lastMousePos.x);
                        var y1 = Math.min(end.y, this.lastMousePos.y);
                        var y2 = Math.max(end.y, this.lastMousePos.y);

                        var w = x2 - x1;
                        var h = y2 - y1;

                        this.currentRange = {x: x1, y: y1, width: w, height: h};

                        this.setRangeBoundStart(x1, y1);
                        this.setRangeBoundSize(w, h);
                    }
                } catch (ex) {
                    error(ex);
                }

                ]]>
                </body>
            </method>
            <method name="setRangeBoundStart">
                <parameter name="x"/>
                <parameter name="y"/>
                <body>
                <![CDATA[
                    Svg.setX(this.rangeBoundRect, x + 0.5);
                    Svg.setY(this.rangeBoundRect, y + 0.5);
                ]]>
                </body>
            </method>
            <method name="setRangeBoundSize">
                <parameter name="w"/>
                <parameter name="h"/>
                <body>
                <![CDATA[
                    Svg.setWidth(this.rangeBoundRect, w);
                    Svg.setHeight(this.rangeBoundRect, h);
                ]]>
                </body>
            </method>
            <method name="setRangeBoundVisibility">
                <parameter name="visible"/>
                <body>
                <![CDATA[
                    this.rangeBoundRect.setAttributeNS(PencilNamespaces.p, "p:on", visible ? "true" : "false");
                ]]>
                </body>
            </method>
            <method name="handleKeyPress">
                <parameter name="event"/>
                <body>
                <![CDATA[
                    if (this != Pencil.activeCanvas) return;
                    if (OnScreenTextEditor.isEditing) {
                        debug("OnScreenTextEditor.isEditing = true");
                        return;
                    }
                    
                    for (editor in this.onScreenEditors) {
                        var e = this.onScreenEditors[editor];
                        if (e.handleKeyPressEvent) {
                            if (e.handleKeyPressEvent(event)) {
                                return;
                            }
                        }
                    }
                    
                    
                    if (event.keyCode == event.DOM_VK_UP ||
                        event.keyCode == event.DOM_VK_DOWN ||
                        event.keyCode == event.DOM_VK_LEFT ||
                        event.keyCode == event.DOM_VK_RIGHT
                        ) {
                        event.preventDefault();
                        if (!this.currentController) return;

                        var dx = 0;
                        var dy = 0;

                        if (event.keyCode == event.DOM_VK_UP) dy = -1;
                        if (event.keyCode == event.DOM_VK_DOWN) dy = 1;
                        if (event.keyCode == event.DOM_VK_LEFT) dx = -1;
                        if (event.keyCode == event.DOM_VK_RIGHT) dx = 1;

                        var gridSize = Pencil.getGridSize();
                        if (event.shiftKey) {
                            dx *= gridSize.w * 4;
                            dy *= gridSize.h * 4;
                        } else if (event.ctrlKey) {
                            dx *= gridSize.w;
                            dy *= gridSize.h;
                        }


                        this.run(function () {
                            //this.currentController.moveBy(dx, dy);
                            this.currentController.moveBy(dx, dy, false, true);
                            
                            if (this.currentController.invalidateOutboundConnections) {
                                this.currentController.invalidateOutboundConnections();
                            }
                            if (this.currentController.invalidateInboundConnections) {
                                this.currentController.invalidateInboundConnections();
                            }
                            
                        }, this, Util.getMessage("action.move.shape"));

                        if (this.currentController.constructor == TargetSet) {
                            for (var t in this.currentController.targets) {
                                this.snappingHelper.updateSnappingGuide(this.currentController.targets[t]);
                            }
                        } else {
                            this.snappingHelper.updateSnappingGuide(this.currentController);
                        }

                        this.invalidateEditors();
                        event.preventDefault();
                        Dom.emitEvent("p:ShapeGeometryModified", this, {setter: null});

                    } else if (event.keyCode == event.DOM_VK_DELETE) {
                        if (this.currentController) {
                            this.run(function () {
                                this.currentController.deleteTarget();
                            }, this, Util.getMessage("action.delete.shape", this.currentController.getName()));
                            this.currentController = null;
                            this._detachEditors();
                            this.clearSelection();
                            this._sayTargetChanged();
                            event.preventDefault();
                        }
                    } else if (event.keyCode == event.DOM_VK_F2) {
                        if (this.currentController) {
                            Dom.emitEvent("p:TextEditingRequested", event.originalTarget, {controller: this.currentController});
                        }
                    } else if (event.keyCode == event.DOM_VK_TAB) {
                        if (event.shiftKey) {
                            this.selectSibling(false);
                        } else {
                            this.selectSibling(true);
                        }

                        event.preventDefault();
                    } else if (event.charCode == " ".charCodeAt(0) && event.ctrlKey) {
                        event.preventDefault();
                        this._showPropertyDialog();
                        event.preventDefault();
                    } else if (event.charCode == "a".charCodeAt(0) && event.ctrlKey) {
                        event.preventDefault();
                        this.selectAll();
                        event.preventDefault();
                    } else if (event.keyCode == event.DOM_VK_ESCAPE) {
                        this.endFormatPainter();
                    } else if (event.keyCode == event.DOM_VK_ESCAPE) {
                        this.endFormatPainter();
                    }
                ]]>
                </body>
            </method>
            <method name="updateContextMenu">
                <parameter name="currentAction"/>
                <parameter name="prevAction"/>
                <body>
                <![CDATA[
                    this.undoContextMenu.setAttribute("label", Util.getMessage("menu.undo.label") + currentAction);
                    this.redoContextMenu.setAttribute("label", Util.getMessage("menu.redo.label") + prevAction);
                   ]]>
                </body>
            </method>
            <method name="handleContextMenuShow">
                <parameter name="event"/>
                <body>
                <![CDATA[
                    try {
                        this._lastEvent = event;
                        Dom.workOn("./xul:menuseparator", this.popup, function (sep) {
                            sep.style.display = "";
                        });
                        for (var i = 0; i < this.popup.childNodes.length; i ++) {
                            var child = this.popup.childNodes[i];
                            var forAtt = child.getAttributeNS(PencilNamespaces.p, "for");
                            child._canvas = this;
                            if (forAtt) {
                                forAtt = "," + forAtt + ",";
                                var visible = false;
                                if (this.currentController) {
                                    if (forAtt.indexOf("," + this.currentController.constructor.name + ",") >= 0) {
                                        visible = true;
                                    }
                                }

                                child.style.display = visible ? "" : "none";
                                child._controller = this.currentController;
                            }
                        }


                        this.lockingMenuItem.style.display = "none";
                        this.lockingStatus = null;

                        if (this.currentController) {
                            this.lockingStatus = {
                                controller: this.currentController
                            };
                            this.lockingMenuItem.style.display = "";
                            this.lockingMenuItem.setAttribute("checked", "false");
                        } else {
                            var top = Dom.findTop(event.originalTarget, function (node) {
                                return node.hasAttributeNS && node.hasAttributeNS(PencilNamespaces.p, "type");
                            });
                            if (top && this.isShapeLocked(top)) {
                                this.lockingStatus = {
                                    node: top
                                };
                                this.lockingMenuItem.style.display = "";
                                this.lockingMenuItem.setAttribute("checked", "true");
                            }
                        }

                        //remove all menu items previously created by the on-menu editor
                        var parent = this.popupSeparator.parentNode;
                        var len = parent.childNodes.length;
                        for (var i = len - 1; i >= 0; i --) {
                            var child = parent.childNodes[i];
                            if (child._isEditor) parent.removeChild(child);
                        }

                        if (this.currentController) {
                            //attach now
                            if (this.contextMenuEditor) {
                                this.contextMenuEditor.attach(this.currentController);
                            }
                        }

                        //this.buildAttachMenuItem();
                        //this.buildDetachMenuItem();

                        var childs = this.popup.childNodes;
                        var shouldHideNextSeparator = true;
                        var foundVisibleItem = false;

                        for (var i = 0; i < childs.length; i ++) {
                            var child = childs[i];
                            if (child.localName == "menuseparator") {
                                if (shouldHideNextSeparator) {
                                    child.style.display = "none";
                                } else {
                                    shouldHideNextSeparator = true;
                                }
                            } else {
                                if (child.style.display != "none") {
                                    shouldHideNextSeparator = false;
                                }
                            }

                            if (child.style.display != "none") {
                                foundVisibleItem = true;
                            }
                        }

                        for (var i = childs.length - 1; i >= 0; i --) {
                            var child = childs[i];
                            if (child.localName != "menuseparator" && child.style.display != "none") {
                                break;
                            }
                            child.style.display = "none";
                        }
                        if (!foundVisibleItem) {
                            this.popup.hidePopup();
                            return;
                        }
                    } catch (e) {
                        Console.dumpError(e);
                    }
                ]]>
                </body>
            </method>
            <method name="buildAttachMenuItem">
                <body>
                <![CDATA[
                    if (this.attachToMenu) {
                        this.popup.removeChild(this.attachToMenu);
                        this.attachToMenu = null;
                    }
                    if (!this.currentController) return;
                    if (this.currentController.supportAttach && this.currentController.supportAttach()) {
                        var thiz = this;
                        var container = this.currentController.getAttachContainer();
                        this.attachToMenu = document.createElementNS(PencilNamespaces.xul, "menu");
                        this.attachToMenu.setAttribute("label", Util.getMessage("canvas.merge.label"));
                        var attachSlots = this.currentController.getAttachSlots();
                        if (attachSlots.length == 1) {
                            this.attachToMenu = document.createElementNS(PencilNamespaces.xul, "menuitem");
                        }
                        var menu = this.popup.insertBefore(this.attachToMenu, document.getAnonymousElementByAttribute(this, "aid", "addToMyCollections"));
                        if (attachSlots.length > 1) {
                            menu.appendChild(document.createElementNS(PencilNamespaces.xul, "menupopup"));
                        }
                        for (var i = 0; i < attachSlots.length; i++) {
                            var displayName = attachSlots[i].displayName;
                            var tool = container.getProperty(attachSlots[i].name);
                            if (tool && tool != "") {
                                displayName += " (" + tool.targetName + ")";
                            }
                            if (attachSlots.length == 1) {
                                this.attachToMenu.setAttribute("label", Util.getMessage("canvas.merge.label.1", displayName));
                                this.attachToMenu._container = container;
                                this.attachToMenu._attachSlot = attachSlots[0];
                            } else {
                                var item = document.createElementNS(PencilNamespaces.xul, "menuitem");
                                menu.firstChild.appendChild(item);
                                item.setAttribute("label", displayName);
                                item._container = container;
                                item._attachSlot = attachSlots[i];
                            }
                        }
                        this.attachToMenu.addEventListener("command", function (event) {
                            try {
                                var containerNode = Dom.findUpward(event.originalTarget, function (node) { return node._container; });
                                var attachSlotNode = Dom.findUpward(event.originalTarget, function (node) { return node._attachSlot; });
                                if (containerNode && containerNode._container && attachSlotNode && attachSlotNode._attachSlot && containerNode._container._target) {
                                    var target = containerNode._container._target;
                                    var defId = target.def.id;
                                    var name = target.getName ? target.getName() : "";
                                    var metaNode = Dom.getSingle("./p:metadata", target.svg);
                                    var ctm = target.svg.getTransformToElement(thiz.drawingLayer);
                                    var cctm = containerNode._container.svg.getTransformToElement(thiz.drawingLayer);
                                    //var uuid = Util.newUUID();
                                    //target.svg.setAttribute("id", uuid);
                                    var uuid = target.svg.getAttribute("id");
                                    containerNode._container.setProperty(attachSlotNode._attachSlot.name, new Attachment(defId, uuid, name, metaNode, ctm, cctm));
                                    thiz.selectShape(containerNode._container.svg);
                                }
                            } catch (e) {
                                Console.dumpError(e);
                            }
                        }, false);
                    }
                 ]]>
                </body>
            </method>
            <method name="buildDetachMenuItem">
                <body>
                <![CDATA[
                    if (this.detachFromMenu) {
                        this.popup.removeChild(this.detachFromMenu);
                        this.detachFromMenu = null;
                    }
                    if (!this.currentController) return;
                    if (this.currentController.canDetach && this.currentController.canDetach()) {
                        var thiz = this;
                        var container = this.currentController;
                        this.detachFromMenu = document.createElementNS(PencilNamespaces.xul, "menu");
                        this.detachFromMenu.setAttribute("label", Util.getMessage("canvas.split.label"));
                        var attachedSlots = this.currentController.getAttachedSlots();
                        if (attachedSlots.length == 1) {
                            this.detachFromMenu = document.createElementNS(PencilNamespaces.xul, "menuitem");
                        }
                        var menu = this.popup.insertBefore(this.detachFromMenu, document.getAnonymousElementByAttribute(this, "aid", "addToMyCollections"));
                        if (attachedSlots.length > 1) {
                            menu.appendChild(document.createElementNS(PencilNamespaces.xul, "menupopup"));
                        }
                        for (var i = 0; i < attachedSlots.length; i++) {
                            var displayName = attachedSlots[i].displayName;
                            var tool = container.getProperty(attachedSlots[i].name);
                            if (tool && tool != "") {
                                displayName += " (" + tool.targetName + ")";
                            }
                            if (attachedSlots.length == 1) {
                                this.detachFromMenu.setAttribute("label", Util.getMessage("canvas.split.label.1", displayName));
                                this.detachFromMenu._container = container;
                                this.detachFromMenu._detachSlot = attachedSlots[0];
                            } else {
                                var item = document.createElementNS(PencilNamespaces.xul, "menuitem");
                                menu.firstChild.appendChild(item);
                                item.setAttribute("label", displayName);
                                item._container = container;
                                item._detachSlot = attachedSlots[i];
                            }
                        }
                        this.detachFromMenu.addEventListener("command", function (event) {
                            try {
                                var containerNode = Dom.findUpward(event.originalTarget, function (node) { return node._container; });
                                var detachSlotNode = Dom.findUpward(event.originalTarget, function (node) { return node._detachSlot; });
                                if (containerNode && containerNode._container && detachSlotNode && detachSlotNode._detachSlot) {
                                    var attachment = containerNode._container.getProperty(detachSlotNode._detachSlot.name);
                                    if (attachment && attachment.defId) {
                                        var shapeDef = CollectionManager.shapeDefinition.locateDefinition(attachment.defId);
                                        if (shapeDef) {
                                            thiz.insertShapeImpl_(shapeDef, containerNode._container.getBounding());
                                            var controller = thiz.currentController;
                                            if (controller) {
                                                Svg.ensureCTM(controller.svg, attachment.ctm);
                                                var metaNode = Dom.parseToNode(attachment.metaData);
                                                if (metaNode) {
                                                    Dom.workOn("./p:property", metaNode, function (node) {
                                                        var name = node.getAttribute("name");
                                                        var value = Dom.getText(node);
                                                        controller.setProperty(name, value);
                                                    });
                                                }
                                                //var g = document.getElementById(attachment.targetId);
                                                var octm = attachment.cctm;
                                                var cctm = containerNode._container.svg.getTransformToElement(thiz.drawingLayer);
                                                //var matrix = containerNode._container.svg.createSVGTransform().matrix;;
                                                //matrix = matrix.multiply(octm);
                                                //var ctm = g.getTransformToElement(thiz.drawingLayer);
                                                //Svg.ensureCTM(controller.svg, ctm);
                                                controller.moveBy(cctm.e - octm.e, cctm.f - octm.f);
                                            }
                                        }
                                        containerNode._container.setProperty(detachSlotNode._detachSlot.name, new Attachment(null, null, null));
                                        thiz.selectShape(containerNode._container.svg);
                                        thiz.invalidateEditors();
                                    }
                                }
                            } catch (e) {
                                Console.dumpError(e);
                            }
                        }, false);
                    }
                 ]]>
                </body>
            </method>
            <method name="insertEditorContextMenuItem">
                <parameter name="menuItem"/>
                <body>
                <![CDATA[
                    var parent = this.popupSeparator.parentNode;
                    menuItem._isEditor = true;

                    parent.insertBefore(menuItem, this.popupSeparator);
                ]]>
                </body>
            </method>
            <method name="invalidateEditors">
                <parameter name="source"/>
                <body>
                <![CDATA[
                    for (editor in this.onScreenEditors) {
                        var e = this.onScreenEditors[editor];
                        if (!source || source != e) e.invalidate();
                    }

                    //Pencil.invalidateSharedEditor();
                    //invalidates all selections
                    for (var i = 0; i < this.selectionContainer.childNodes.length; i ++) {
                        var rect = this.selectionContainer.childNodes[i];
                        this._invalidateOneSelection(rect);
                    }

                ]]>
                </body>
            </method>
            <method name="_showPropertyDialog">
                <body>
                <![CDATA[
                    if (this.propertyPageEditor && this.currentController) {
                        this.propertyPageEditor.showAndAttach(this.currentController);
                    }
                ]]>
                </body>
            </method>
            <method name="handlePropertyMenuItemCommand">
                <parameter name="event"/>
                <body>
                <![CDATA[
                    this._showPropertyDialog();
                ]]>
                </body>
            </method>
            <method name="handleDblclick">
                <parameter name="event"/>
                <body>
                <![CDATA[
                    stencilDebug("handleDblclick, start finding top");
                    
                    //find the top, get the def
                    var thiz = this;
                    var top = Dom.findTop(event.originalTarget, function (node) {
                        return node.hasAttributeNS && node.hasAttributeNS(PencilNamespaces.p, "type") && node.getAttributeNS(PencilNamespaces.p, "type") == "Shape" && !thiz.isShapeLocked(node);
                    });
                    
                    stencilDebug("handleDblclick, top = " + top);

                    if (!top) return;

                    var target = this.createControllerFor(top);
                    
                    stencilDebug("emitting event p:ShapeDoubleClicked");
                    Dom.emitEvent("p:ShapeDoubleClicked", top, {controller: target, origTarget: event.originalTarget, clientX: event.clientX, clientY: event.clientY});

                ]]>
                </body>
            </method>
            <method name="focus">
                <body>
                <![CDATA[
                    //document.getElementById("richTextEditorToolbar").focus();
                    //document.commandDispatcher.rewindFocus();
                    //document.commandDispatcher.advanceFocus();
                    this.focusableBox.focus("");
                ]]>
                </body>
            </method>
            <method name="doCopy">
                <body>
                <![CDATA[

                    if (!this.currentController) return;

                    var transferableData = this.currentController.createTransferableData();

                    transferableData.dataNode.removeAttribute("p:parentRef");
                    var metaNode = Dom.getSingle(".//p:metadata", transferableData.dataNode);
                    var childTargetsNode = Dom.getSingle("./p:childTargets", metaNode);
                    if (childTargetsNode) {
                        childTargetsNode.parentNode.removeChild(childTargetsNode);
                    }

                    //serialize to string
                    var textualData = new XMLSerializer().serializeToString(transferableData.dataNode);

                    var systemString   = Components.classes["@mozilla.org/supports-string;1"].
                                            createInstance(Components.interfaces.nsISupportsString);
                    if (!systemString) return false;
                    systemString.data  = textualData;

                    var trans = Components.classes["@mozilla.org/widget/transferable;1"].
                                            createInstance(Components.interfaces.nsITransferable);
                    if (!trans) return false;

                    trans.addDataFlavor("text/unicode");
                    trans.setTransferData("text/unicode", systemString, textualData.length * 2);

                    trans.addDataFlavor(transferableData.type);
                    trans.setTransferData(transferableData.type, systemString, textualData.length * 2);

                    try {
                        //generating text/xml+svg
                        if (transferableData.isSVG) {
                            var svg = this.svg.ownerDocument.createElementNS(PencilNamespaces.svg, "svg");

                            //svg.setAttribute("width", transferableData.svgDIM.w);
                            //svg.setAttribute("height", transferableData.svgDIM.h);
                            svg.appendChild(transferableData.dataNode);

                            var svgXML = new XMLSerializer().serializeToString(svg);
                            svgXML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + svgXML;

                            var svgXMLSS   = Components.classes["@mozilla.org/supports-string;1"].
                                                    createInstance(Components.interfaces.nsISupportsString);
                            if (!svgXMLSS) return false;
                            svgXMLSS.data = svgXML;

                            trans.addDataFlavor("image/svg+xml");
                            trans.setTransferData("image/svg+xml", svgXMLSS, svgXML.length * 2);
                        }
                    } catch (e) {
                        Console.dumpError(e);
                    }

                    var clipId = Components.interfaces.nsIClipboard;
                    var clip   = Components.classes["@mozilla.org/widget/clipboard;1"].getService(clipId);
                    if (!clip) return false;

                    clip.setData(trans, null, clipId.kGlobalClipboard);
                ]]>
                </body>
            </method>
            <method name="doPaste">
                <body>
                <![CDATA[

                    var clip  = Components.classes["@mozilla.org/widget/clipboard;1"].getService(Components.interfaces.nsIClipboard);
                    if (!clip) return false;

                    for (i in this.xferHelpers) {
                        var helper = this.xferHelpers[i];
                        var trans = Components.classes["@mozilla.org/widget/transferable;1"].createInstance(Components.interfaces.nsITransferable);
                        if (!trans) return;

                        trans.addDataFlavor(helper.type);
                        clip.getData(trans, clip.kGlobalClipboard);

                        var data = new Object();
                        var length = new Object();
                        var flavour = new Object();

                        try {
                            trans.getAnyTransferData(flavour, data, length);
                        } catch (e) {
                            continue;
                        }

                        if (data && length && data.value && length.value) {
                            helper.handleData(data.value, length.value);
                            break;
                        }
                    }

                ]]>
                </body>
            </method>
            <method name="doGroup">
                <body>
                <![CDATA[
                    this.run(this.doGroupImpl_, this, Util.getMessage("action.group.shapes"));
                ]]>
                </body>
            </method>
            <method name="doGroupImpl_">
                <body>
                <![CDATA[
                    var targets = this.getSelectedTargets();
                    if (targets.length <= 1) return;

                    //create an svg:g to box all the selected target
                    var g = this.ownerDocument.createElementNS(PencilNamespaces.svg, "g");
                    g.setAttributeNS(PencilNamespaces.p, "p:type", "Group");

                    var nodes = [];
                    var rect = null;
                    Dom.workOn("./svg:g[@p:type]", this.drawingLayer, function (node) {
                        for (t in targets) {
                            if (targets[t].isFor(node)) {
                                nodes.push(node);
                                var childRect = targets[t].getBounding();
                                rect = rect ? Svg.joinRect(rect, childRect) : childRect;
                            }
                        }
                    });

                    for (n in nodes) {
                        var node = nodes[n];
                        node.parentNode.removeChild(node);
                        g.appendChild(node);
                    }

                    this.drawingLayer.appendChild(g);

                    for (t in targets) {
                        targets[t].moveBy(0 - rect.x, 0 - rect.y, true);
                    }

                    g.setAttribute("transform", "translate(" + [rect.x, rect.y] + ")");

                    this.selectShape(g);
                    
                    this.currentController.processNewGroup();

                    this.snappingHelper.updateSnappingGuide(this.currentController);
                    for (var t in this.currentController.targets) {
                        this.snappingHelper.updateSnappingGuide(this.currentController.targets[t], true);
                    }
                ]]>
                </body>
            </method>
            <method name="ensureControllerInView">
                <body>
                <![CDATA[
                ]]>
                </body>
            </method>
            <method name="doUnGroup">
                <body>
                <![CDATA[
                    this.run(this.doUnGroupImpl_, this, Util.getMessage("action.ungroup.shapes"));
                ]]>
                </body>
            </method>
            <method name="doUnGroupImpl_">
                <body>
                <![CDATA[
                    if (!this.currentController) return;
                    if (this.currentController.constructor != Group) return;

                    // remove group
                    this.snappingHelper.updateSnappingGuide(this.currentController, true);

                    var nodes = this.currentController.ungroup();

                    this.selectMultiple(nodes);

                    if (this.currentController.constructor == TargetSet) {
                        for (var t in this.currentController.targets) {
                            this.snappingHelper.updateSnappingGuide(this.currentController.targets[t]);
                        }
                    }
                ]]>
                </body>
            </method>
            <method name="isShapeLocked">
                <parameter name="shape"/>
                <body>
                <![CDATA[
                    return shape.getAttributeNS(PencilNamespaces.p, "locked") == "true";
                ]]>
                </body>
            </method>
            <method name="toggleLocking">
                <body>
                <![CDATA[
                    this.run(this.toggleLockingImpl_, this, Util.getMessage("action.lock.shape"));
                ]]>
                </body>
            </method>
            <method name="toggleLockingImpl_">
                <body>
                <![CDATA[
                    if (!this.lockingStatus) return;
                    if (this.lockingStatus.controller && this.lockingStatus.controller.lock) {
                        this.lockingStatus.controller.lock();
                        this.selectNone();
                    } else if (this.lockingStatus.node) {
                        this.lockingStatus.node.removeAttributeNS(PencilNamespaces.p, "locked");
                        this.selectShape(this.lockingStatus.node);
                    }
                ]]>
                </body>
            </method>
            <method name="deleteSelected">
                <body>
                <![CDATA[
                    this.run(this.deleteSelectedImpl_, this, Util.getMessage("action.delete.shape"));
                ]]>
                </body>
            </method>
            <method name="deleteSelectedImpl_">
                <body>
                <![CDATA[
                    //this.snappingHelper.updateSnappingGuide(this.currentController, true);
                    this.currentController.deleteTarget();
                    this.currentController = null;
                    this._detachEditors();
                    this.clearSelection();
                ]]>
                </body>
            </method>
            <method name="_sayContentModified">
                <body>
                <![CDATA[
                    Dom.emitEvent("p:ContentModified", this);
                ]]>
                </body>
            </method>
            <method name="_saveMemento">
                <parameter name="actionName"/>
                <body>
                <![CDATA[
                    this.careTaker.save(actionName);
                ]]>
                </body>
            </method>
            <method name="getMemento">
                <parameter name="actionName"/>
                <body>
                <![CDATA[
                    return new CanvasMemento(this.drawingLayer.cloneNode(true), {}, actionName);
                ]]>
                </body>
            </method>
            <method name="setMemento">
                <parameter name="memento"/>
                <body>
                <![CDATA[
                    this.selectNone();
                    Dom.empty(this.drawingLayer);

                    //alert("copy back: " + memento.node.childNodes.length + " nodes");
                    var fragment = this.drawingLayer.ownerDocument.createDocumentFragment();
                    for (var i = 0; i < memento.node.childNodes.length; i ++) {
                        fragment.appendChild(this.drawingLayer.ownerDocument.importNode(memento.node.childNodes[i], true));
                    }
                    this.drawingLayer.appendChild(fragment);

                    this.focusableBox.style.visibility = "hidden";
                    this.focusableBox.style.visibility = "visible";

                    this._sayContentModified();
                ]]>
                </body>
            </method>
            <method name="run">
                <parameter name="job"/>
                <parameter name="targetObject"/>
                <parameter name="actionName"/>
                <parameter name="args"/>
                <body>
                <![CDATA[

                    try {
                        //console.log();
                        job.apply(targetObject, args);
                    } catch (e) {
                        Console.dumpError(e);
                    } finally {
                        this._saveMemento(actionName);
                        this._sayContentModified();
                    }
                ]]>
                </body>
            </method>
            <method name="setBackgroundColor">
                <parameter name="color"/>
                <body>
                <![CDATA[
                    if (color) {
                        this.focusableBox.style.backgroundColor = color.toRGBString();
                    }
                ]]>
                </body>
            </method>
            <method name="setSize">
                <parameter name="width"/>
                <parameter name="height"/>
                <body>
                <![CDATA[
                    this.width = width;
                    this.height = height;

                    var thiz = this;

                    this.svg.setAttribute("width", 10);
                    this.svg.setAttribute("height", 10);

                    window.setTimeout(function () {
                        thiz.svg.setAttribute("width", Math.ceil(thiz.width * thiz.zoom));
                        thiz.svg.setAttribute("height", Math.ceil(thiz.height * thiz.zoom));

                        CanvasImpl.setupGrid.apply(thiz);
                    }, 50);

                ]]>
                </body>
            </method>
            <method name="setBackgroundImageData">
                <parameter name="image"/>
                <parameter name="dimBackground"/>
                <body>
                <![CDATA[
                    if (!image) {
                        this.backgroundImage.setAttributeNS(PencilNamespaces.xlink, "xlink:href", "");
                        this.backgroundImage.style.display = "none";
                        this.hasBackgroundImage = false;
                        this.removeAttributeNS(PencilNamespaces.p, "with-background");
                        return;
                    }

                    this.backgroundImage.setAttributeNS(PencilNamespaces.xlink, "xlink:href", image.url);
                    this.backgroundImage.setAttribute("width", image.width);
                    this.backgroundImage.setAttribute("height", image.height);
                    this.backgroundImage.style.display = "";
                    this.hasBackgroundImage = true;
                    this.setAttributeNS(PencilNamespaces.p, "p:with-background", "true");
                    if (!dimBackground) {
                        this.removeAttributeNS(PencilNamespaces.p, "with-background-dimmer");
                    } else {
                        this.setAttributeNS(PencilNamespaces.p, "p:with-background-dimmer", "true");
                    }
                ]]>
                </body>
            </method>
            <method name="setDimBackground">
                <parameter name="dimBackground"/>
                <body>
                <![CDATA[
                    if (!dimBackground) {
                        this.removeAttributeNS(PencilNamespaces.p, "with-background-dimmer");
                    } else {
                        this.setAttributeNS(PencilNamespaces.p, "p:with-background-dimmer", "true");
                    }
                ]]>
                </body>
            </method>
            <method name="sizeToContent">
                <parameter name="hPadding"/>
                <parameter name="vPadding"/>
                <body>
                <![CDATA[
                    this.zoomTo(1.0);

                    var thiz = this;
                    var maxBox = null;
                    Dom.workOn("./svg:g[@p:type]", this.drawingLayer, function (node) {
                        try {
                            var controller = thiz.createControllerFor(node);
                            var bbox = controller.getBoundingRect();
                            var box = {
                                x1: bbox.x,
                                y1: bbox.y,
                                x2: bbox.x + bbox.width,
                                y2: bbox.y + bbox.height
                            };
                            if (maxBox == null) {
                                maxBox = box;
                            } else {
                                maxBox = {
                                    x1: Math.min(maxBox.x1, box.x1),
                                    y1: Math.min(maxBox.y1, box.y1),
                                    x2: Math.max(maxBox.x2, box.x2),
                                    y2: Math.max(maxBox.y2, box.y2)
                                };
                            }
                        } catch (e) {
                            Util.error(Util.getMessage("error.title"), e, Util.getMessage("button.cancel.close"));
                        }
                    });
                    if (maxBox == null) {
                        Util.info(Util.getMessage("error.title"), Util.getMessage("the.current.document.is.empty.sizing.is.failed"), Util.getMessage("button.cancel.close"));
                        return;
                    }

                    var width = maxBox.x2 - maxBox.x1 + 2 * hPadding;
                    var height = maxBox.y2 - maxBox.y1 + 2 * vPadding;

                    //moving
                    var dx = 0 - maxBox.x1 + hPadding;
                    var dy = 0 - maxBox.y1 + vPadding;

                    Dom.workOn("./svg:g[@p:type]", this.drawingLayer, function (node) {
                        try {
                            var controller = thiz.createControllerFor(node);
                            var bbox = controller.getBoundingRect();
                            controller.moveBy(dx, dy);
                        } catch (e) {
                            alert(e);
                        }
                    });

                    this.setSize(width, height);

                    this.selectNone();

                    return {width: width, height: height};
                ]]>
                </body>
            </method>
            <method name="addSelectedToMyCollection">
                <body>
                <![CDATA[
                    if (!this.currentController) return;
                    try {
                        var target = Pencil.getCurrentTarget();
                        //debug("adding selected shapes to my collection... ");

                        //generating text/xml+svg
                        var svg = target.svg.cloneNode(true);

                        var data = {
                            collections: PrivateCollectionManager.privateShapeDef.collections,
                            valueHolder: {}
                        };
                        var d = window.openDialog('chrome://pencil/content/privateCollectionWizard.xul', 'CreatePrivateCollectionWizard', 'chrome,centerscreen,modal', data);

                        var valueHolder = data.valueHolder;
                        if (!valueHolder.shapeName) return;

                        //debug("creating shape");

                        var shapeDef = new PrivateShapeDef();
                        shapeDef.displayName = valueHolder.shapeName;
                        shapeDef.content = svg;
                        shapeDef.id = shapeDef.displayName.replace(/\s+/g, "_").toLowerCase() + "_" + (new Date()).getTime();

                        //debug("shape name: " + shapeDef.displayName + ", id: " + shapeDef.id);

                        var collection = valueHolder.collection;
                        var isNewCollection = (collection == null || collection == -1);

                        if (isNewCollection) {
                            //debug("creating new collection...");
                            collection = new PrivateCollection();
                            collection.displayName = valueHolder.collectionName;
                            collection.description = valueHolder.collectionDescription;
                            collection.id = collection.displayName.replace(/\s+/g, "_").toLowerCase() + "_" + (new Date()).getTime();
                            //debug("collection name: " + collection.displayName + ", id: " + collection.id);

                            collection.shapeDefs.push(shapeDef);
                        }

                        debug("generating icon... ");
                        if (valueHolder.autoGenerateIcon) {
                            Util.generateIcon(target, 64, 64, 2, null, function (data) {
                                debug("\tdone generating icon.");
                                shapeDef.iconData = data;
                                if (isNewCollection) {
                                    PrivateCollectionManager.addShapeCollection(collection);
                                } else {
                                    PrivateCollectionManager.addShapeToCollection(collection, shapeDef);
                                }
                                return;
                            });
                        } else {
                            var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
                            file.initWithPath(valueHolder.shapeIcon);

                            var ios = Components.classes["@mozilla.org/network/io-service;1"].
                                            getService(Components.interfaces.nsIIOService);
                            var istream = Components.classes["@mozilla.org/network/file-input-stream;1"].
                                                    createInstance(Components.interfaces.nsIFileInputStream);
                            istream.init(file, -1, -1, false);

                            var bstream = Components.classes["@mozilla.org/binaryinputstream;1"].
                                                    createInstance(Components.interfaces.nsIBinaryInputStream);
                            bstream.setInputStream(istream);
                            var bytes = bstream.readBytes(bstream.available());
                            istream.close();
                            bstream.close();

                            var base64 = Base64.encode(bytes, true);
                            shapeDef.iconData = "data:image/png;base64," + base64;

                            if (isNewCollection) {
                                PrivateCollectionManager.addShapeCollection(collection);
                            } else {
                                PrivateCollectionManager.addShapeToCollection(collection, shapeDef);
                            }
                        }
                    } catch (e) {
                        Console.dumpError(e);
                    }
                ]]>
                </body>
            </method>
            <method name="insertPrivateShape">
                <parameter name="shapeDef"/>
                <parameter name="bound"/>
                <body>
                <![CDATA[
                    this.run(this.insertPrivateShapeImpl_, this, Util.getMessage("action.create.shape", shapeDef.displayName), [shapeDef, bound ? bound : null]);
                ]]>
                </body>
            </method>
            <method name="insertPrivateShapeImpl_">
                <parameter name="shapeDef"/>
                <parameter name="bound"/>
                <body>
                <![CDATA[
                    //debug("inserting private shape: " + shapeDef.displayName + ", id: " + shapeDef.id);
                    var g = this.ownerDocument.createElementNS(PencilNamespaces.svg, "g");
                    g.appendChild(shapeDef.content);

                    //validate
                    var shape = Dom.getSingle("/svg:g[@p:type='Shape' or @p:type='Group']", g);
                    if (!shape) {
                        return;
                    }

                    shape = this.ownerDocument.importNode(shape, true);

                    //generate the ids
                    Dom.workOn(".//*[@p:name]", shape, function (node) {
                        var name = node.getAttributeNS(PencilNamespaces.p, "name");
                        var oldId = node.getAttribute("id");
                        if (oldId) return;

                        var uuid = Util.newUUID();
                        node.setAttribute("id", uuid);
                        node.id = uuid;

                        Dom.updateIdRef(shape, name, uuid);
                    });

                    Dom.renewId(shape);

                    shape.style.visibility = "hidden";
                    this.setAttributeNS(PencilNamespaces.p, "p:holding", "true");

                    this.drawingLayer.appendChild(shape);
                    this.selectShape(shape);
                    if (bound) {
                        var bbox = this.currentController.getBounding();
                        this.currentController.moveBy((bound.x - bbox.x - Math.round(bbox.width/2)) / this.zoom, (bound.y - bbox.y - Math.round(bbox.height/2)) / this.zoom, true);
                    }
                    shape.style.visibility = "visible";
                    this.removeAttributeNS(PencilNamespaces.p, "holding");

                    this.ensureControllerInView();
                    this.snappingHelper.updateSnappingGuide(this.currentController);
                    DockingManager.enableDocking(this.currentController);

                    this.invalidateEditors();
                ]]>
                </body>
            </method>
            <method name="beginFormatPainter">
                <body>
                <![CDATA[
                    if (this.isFormatPainterAvailable()) {
                        return this.endFormatPainter();
                    }
                    if (!this.currentController) return;
                    try {
                        var target = this.currentController;
                        if (target && (target.constructor == Shape || target.constructor == Group)) {
                            Pencil._painterSourceTarget = target;
                            Pencil._painterSourceProperties = target.getProperties();
                            var canvasList = Pencil.getCanvasList();
                            for (var i = 0; i < canvasList.length; i++) {
                                Dom.addClass(canvasList[i], "Painter");
                            }
                        }
                    } catch (e) {
                        Console.dumpError(e);
                    }
                ]]>
                </body>
            </method>
            <method name="endFormatPainter">
                <body>
                <![CDATA[
                    Pencil._painterSourceTarget = null;
                    Pencil._painterSourceProperties = null;
                    Pencil.setPainterCommandChecked(false);
                    return true;
                ]]>
                </body>
            </method>
            <method name="isFormatPainterAvailable">
                <body>
                <![CDATA[
                    return Pencil._painterSourceTarget && Pencil._painterSourceProperties;
                ]]>
                </body>
            </method>
            <method name="getPainterPropertyMap">
                <body>
                <![CDATA[
                    var ppm = Config.get("painterPropertyMap");
                    if (ppm == null || ppm == "") {
                        ppm = "fillColor,foreColor,textColor,strokeColor,borderColor," +
                              "strokeStyle,textAlign,textFont,shadowStyle,shadowColor," +
                              "withBlur,withShadow,textPadding,startFillColor,endFillColor"
                        Config.set("painterPropertyMap", ppm);
                    }
                    return ppm.split(",");
                ]]>
                </body>
            </method>
            <method name="startFakeMove">
                <parameter name="event"/>
                <body>
                <![CDATA[
                    this._button = 0;
                    this.currentController = this.getSelectedTargets()[0];
                    this.controllerHeld = true;

                    this.oX = Math.round(event.clientX / this.zoom);
                    this.oY = Math.round(event.clientY / this.zoom);
                    this.oldPos = this.currentController.getGeometry();

                    this.currentController.setPositionSnapshot();

                    OnScreenTextEditor._hide();

                    this.setAttributeNS(PencilNamespaces.p, "p:holding", "true");
                    this.reClick = false;

                    this.hasMoved = false;
                    this.lastTop = top;
                    this._sayTargetChanged();
                ]]>
                </body>
            </method>
            <method name="checkDnDEventTimestamp">
                <parameter name="event"/>
                <body>
                <![CDATA[
                    var now = new Date().getTime();
                    if (!this.lastDragEnterExitEventTS) {
                        this.lastDragEnterExitEventTS = now;
                        return true;
                    }
                    var delta = now - this.lastDragEnterExitEventTS;

                    //debug("Even ts delta: " + delta);

                    this.lastDragEnterExitEventTS = now;

                    return delta > 500;
                ]]>
                </body>
            </method>
        </implementation>
        <handlers>
            <handler event="dragenter">
                <![CDATA[
                    if (!Dom.isChildOf(event.originalTarget, this.svg)) return;
                    //debug("PCanvas: drag enter, " + event.originalTarget.localName);

                    // find the first DO that accepts this drag session, use its for subsequence events
                    // in the same session

                    this.currentDragObserver = null;
                    for (var i in this.dragObservers) {
                        var observer = this.dragObservers[i];
                        try {
                            var accepted = nsDragAndDrop.dragEnter(event, observer);
                            if (accepted) {
                                this.currentDragObserver = observer;
                                break;
                            }
                        } catch (e) { Console.dumpError(e); }
                    }
                ]]>
            </handler>
            <handler event="dragexit">
                <![CDATA[
                    if (!Dom.isChildOf(event.originalTarget, this.svg)) return;
                    //debug("PCanvas: drag exit, " + event.originalTarget.localName);
                    if (!this.currentDragObserver) return;

                    try {
                        nsDragAndDrop.dragExit(event, this.currentDragObserver);
                    } catch (e) { Console.dumpError(e); }
                ]]>
            </handler>
            <handler event="dragend">
                <![CDATA[
                    //debug("drag end...");
                ]]>
            </handler>
            <handler event="dragover">
                <![CDATA[
                    tick("PCanvas: drag over");

                    if (!this.currentDragObserver) return;

                    try {
                        nsDragAndDrop.dragOver(event, this.currentDragObserver);
                    } catch (e) { Console.dumpError(e); }

                    /*


                    if (!this.currentDragProcessed) {
                        tick("drag over");
                        this.lastAcceptedDragObserver = null;
                        for (var i in this.dragObservers) {
                            var observer = this.dragObservers[i];
                            try {
                                var accepted = nsDragAndDrop.dragOver(event, observer);
                                if (accepted) {
                                    this.lastAcceptedDragObserver = observer;
                                    break;
                                }
                            } catch (e) { Console.dumpError(e); }
                        }

                        this.currentDragProcessed = true;
                    }

                    if (this.hasDrag) {
                        if (event.clientX != this._lastScreenX || event.clientY != this._lastScreenY) {
                            this.handleMouseMove(event, "fake");
                            this._lastScreenX = event.clientX;
                            this._lastScreenY = event.clientY;
                        }

                    }
                    */
                ]]>
            </handler>
            <handler event="dragdrop">
                <![CDATA[
                    //debug("PCanvas: drag drop");

                    if (!this.currentDragObserver) return;

                    try {
                        nsDragAndDrop.drop(event, this.currentDragObserver);
                    } catch (e) { Console.dumpError(e); }
                ]]>
            </handler>
        </handlers>
        <xbl:content xmlns:xbl="http://www.mozilla.org/xbl"
                xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
            <popupset>
                <menupopup aid="contextPopup">

                    <menuitem class="menuitem-iconic undo-menu" label="&menu.undo.label;" key="undoKey" command="undoCommand" aid="contextUndoMenu"/>
                    <menuitem class="menuitem-iconic redo-menu" label="&menu.redo.label;" key="redoKey" command="redoCommand" aid="contextRedoMenu"/>
                    <menuseparator/>

                    <menu label="&menu.arrangement.label;" aid="arrangementMenu" p:for="Shape,TargetSet,Group">
                        <menupopup>
                            <menuitem label="&menu.bring.to.front.label;" command="bringToFrontCommand" key="bringToFrontKey"/>
                            <menuitem label="&menu.bring.forward.label;" command="bringForwardCommand" key="bringForwardKey"/>
                            <menuitem label="&menu.send.backward.label;" command="sendBackwardCommand" key="sendBackwardKey"/>
                            <menuitem label="&menu.send.to.back.label;" command="sendToBackCommand" key="sendToBackKey"/>
                        </menupopup>
                    </menu>

                    <menuseparator/>
                    <menuseparator aid="contextPopupSeparator"/>

                    <menuitem class="menuitem-iconic locking-menu" aid="lockingMenuItem" label="&menu.locked.label;" type="checkbox" oncommand="this._canvas.toggleLocking();"/>
                    <menuitem class="menuitem-iconic group-menu" label="&menu.group.label;" command="groupCommand" p:for="TargetSet" key="groupKey"/>
                    <menuitem class="menuitem-iconic ungroup-menu" label="&menu.ungroup.label;" command="unGroupCommand" p:for="Group" key="unGroupKey"/>
                    <menuitem class="menuitem-iconic delete-menu" label="&menu.delete.label;" p:for="Shape,TargetSet,Group" oncommand="this._canvas.deleteSelected();"/>
                    <menuitem label="&menu.add.to.my.stuffs.label;" p:for="Shape,Group" oncommand="this._canvas.addSelectedToMyCollection();"/>

                    <menuseparator/>
                    <menuitem class="menuitem-iconic cut-menu" label="&menu.cut.label;" key="cutKey" command="cutCommand"/>
                    <menuitem class="menuitem-iconic copy-menu" label="&menu.copy.label;" key="copyKey" command="copyCommand"/>
                    <menuitem class="menuitem-iconic paste-menu" label="&menu.paste.label;" key="pasteKey" command="pasteCommand"/>
                    <menuitem class="menuitem-iconic select-all-menu" label="&menu.select.all.label;" key="selectAllKey" oncommand="Pencil.activeCanvas.selectAll();"/>

                    <menuseparator/>
                    <menu label="&menu.resize.canvas.label;" aid="resizeMenu">
                        <menupopup>
                            <menuitem label="&menu.fit.content.label;" oncommand="Pencil.controller.sizeToContent(null, false);"/>
                            <menuitem label="&menu.fit.content.padding.label;" oncommand="Pencil.controller.sizeToContent(null, true);"/>
                            <menuitem label="&menu.fit.bestfit.label;" oncommand="Pencil.controller.sizeToBestFit();"/>
                        </menupopup>
                    </menu>

                    <menuseparator/>
                    
                    <menuitem class="menuitem-iconic sizing-menu" aid="sizingPolicyMenuItem" label="Sizing Policy..." p:for="Shape,Group" oncommand="Group.openSizingPolicyDialog(Pencil.activeCanvas.currentController);"/>
                    
                    <menuseparator/>

                    <menuitem class="menuitem-iconic properties-menu" aid="propertyMenuItem" label="&menu.properties.label;" command="editPagePropertiesCommand"/>

                </menupopup>
            </popupset>
        </xbl:content>
    </binding>
</bindings>
