- /**
- * <a href="http://www.w3.org/TR/NOTE-VML">VML</a> implementation of the <a href="Graphic.html">`Graphic`</a> class.
- * `VMLGraphic` is not intended to be used directly. Instead, use the <a href="Graphic.html">`Graphic`</a> class.
- * If the browser lacks <a href="http://www.w3.org/TR/SVG/">SVG</a> and <a href="http://www.w3.org/TR/html5/the-canvas-element.html">Canvas</a>
- * capabilities, the <a href="Graphic.html">`Graphic`</a> class will point to the `VMLGraphic` class.
- *
- * @module graphics
- * @class VMLGraphic
- * @constructor
- */
- VMLGraphic = function() {
- VMLGraphic.superclass.constructor.apply(this, arguments);
- };
-
- VMLGraphic.NAME = "vmlGraphic";
-
- VMLGraphic.ATTRS = {
- /**
- * Whether or not to render the `Graphic` automatically after to a specified parent node after init. This can be a Node
- * instance or a CSS selector string.
- *
- * @config render
- * @type Node | String
- */
- render: {},
-
- /**
- * Unique id for class instance.
- *
- * @config id
- * @type String
- */
- id: {
- valueFn: function()
- {
- return Y.guid();
- },
-
- setter: function(val)
- {
- var node = this._node;
- if(node)
- {
- node.setAttribute("id", val);
- }
- return val;
- }
- },
-
- /**
- * Key value pairs in which a shape instance is associated with its id.
- *
- * @config shapes
- * @type Object
- * @readOnly
- */
- shapes: {
- readOnly: true,
-
- getter: function()
- {
- return this._shapes;
- }
- },
-
- /**
- * Object containing size and coordinate data for the content of a Graphic in relation to the coordSpace node.
- *
- * @config contentBounds
- * @type Object
- */
- contentBounds: {
- readOnly: true,
-
- getter: function()
- {
- return this._contentBounds;
- }
- },
-
- /**
- * The html element that represents to coordinate system of the Graphic instance.
- *
- * @config node
- * @type HTMLElement
- */
- node: {
- readOnly: true,
-
- getter: function()
- {
- return this._node;
- }
- },
-
- /**
- * Indicates the width of the `Graphic`.
- *
- * @config width
- * @type Number
- */
- width: {
- setter: function(val)
- {
- if(this._node)
- {
- this._node.style.width = val + "px";
- }
- return val;
- }
- },
-
- /**
- * Indicates the height of the `Graphic`.
- *
- * @config height
- * @type Number
- */
- height: {
- setter: function(val)
- {
- if(this._node)
- {
- this._node.style.height = val + "px";
- }
- return val;
- }
- },
-
- /**
- * Determines the sizing of the Graphic.
- *
- * <dl>
- * <dt>sizeContentToGraphic</dt><dd>The Graphic's width and height attributes are, either explicitly set through the
- * <code>width</code> and <code>height</code> attributes or are determined by the dimensions of the parent element. The
- * content contained in the Graphic will be sized to fit with in the Graphic instance's dimensions. When using this
- * setting, the <code>preserveAspectRatio</code> attribute will determine how the contents are sized.</dd>
- * <dt>sizeGraphicToContent</dt><dd>(Also accepts a value of true) The Graphic's width and height are determined by the
- * size and positioning of the content.</dd>
- * <dt>false</dt><dd>The Graphic's width and height attributes are, either explicitly set through the <code>width</code>
- * and <code>height</code> attributes or are determined by the dimensions of the parent element. The contents of the
- * Graphic instance are not affected by this setting.</dd>
- * </dl>
- *
- *
- * @config autoSize
- * @type Boolean | String
- * @default false
- */
- autoSize: {
- value: false
- },
-
- /**
- * Determines how content is sized when <code>autoSize</code> is set to <code>sizeContentToGraphic</code>.
- *
- * <dl>
- * <dt>none<dt><dd>Do not force uniform scaling. Scale the graphic content of the given element non-uniformly if necessary
- * such that the element's bounding box exactly matches the viewport rectangle.</dd>
- * <dt>xMinYMin</dt><dd>Force uniform scaling position along the top left of the Graphic's node.</dd>
- * <dt>xMidYMin</dt><dd>Force uniform scaling horizontally centered and positioned at the top of the Graphic's node.<dd>
- * <dt>xMaxYMin</dt><dd>Force uniform scaling positioned horizontally from the right and vertically from the top.</dd>
- * <dt>xMinYMid</dt>Force uniform scaling positioned horizontally from the left and vertically centered.</dd>
- * <dt>xMidYMid (the default)</dt><dd>Force uniform scaling with the content centered.</dd>
- * <dt>xMaxYMid</dt><dd>Force uniform scaling positioned horizontally from the right and vertically centered.</dd>
- * <dt>xMinYMax</dt><dd>Force uniform scaling positioned horizontally from the left and vertically from the bottom.</dd>
- * <dt>xMidYMax</dt><dd>Force uniform scaling horizontally centered and position vertically from the bottom.</dd>
- * <dt>xMaxYMax</dt><dd>Force uniform scaling positioned horizontally from the right and vertically from the bottom.</dd>
- * </dl>
- *
- * @config preserveAspectRatio
- * @type String
- * @default xMidYMid
- */
- preserveAspectRatio: {
- value: "xMidYMid"
- },
-
- /**
- * The contentBounds will resize to greater values but not values. (for performance)
- * When resizing the contentBounds down is desirable, set the resizeDown value to true.
- *
- * @config resizeDown
- * @type Boolean
- */
- resizeDown: {
- resizeDown: false
- },
-
- /**
- * Indicates the x-coordinate for the instance.
- *
- * @config x
- * @type Number
- */
- x: {
- getter: function()
- {
- return this._x;
- },
-
- setter: function(val)
- {
- this._x = val;
- if(this._node)
- {
- this._node.style.left = val + "px";
- }
- return val;
- }
- },
-
- /**
- * Indicates the y-coordinate for the instance.
- *
- * @config y
- * @type Number
- */
- y: {
- getter: function()
- {
- return this._y;
- },
-
- setter: function(val)
- {
- this._y = val;
- if(this._node)
- {
- this._node.style.top = val + "px";
- }
- return val;
- }
- },
-
- /**
- * Indicates whether or not the instance will automatically redraw after a change is made to a shape.
- * This property will get set to false when batching operations.
- *
- * @config autoDraw
- * @type Boolean
- * @default true
- * @private
- */
- autoDraw: {
- value: true
- },
-
- visible: {
- value: true,
-
- setter: function(val)
- {
- this._toggleVisible(val);
- return val;
- }
- }
- };
-
- Y.extend(VMLGraphic, Y.GraphicBase, {
- /**
- * Sets the value of an attribute.
- *
- * @method set
- * @param {String|Object} name The name of the attribute. Alternatively, an object of key value pairs can
- * be passed in to set multiple attributes at once.
- * @param {Any} value The value to set the attribute to. This value is ignored if an object is received as
- * the name param.
- */
- set: function()
- {
- var host = this,
- attr = arguments[0],
- redrawAttrs = {
- autoDraw: true,
- autoSize: true,
- preserveAspectRatio: true,
- resizeDown: true
- },
- key,
- forceRedraw = false;
- AttributeLite.prototype.set.apply(host, arguments);
- if(host._state.autoDraw === true && Y.Object.size(this._shapes) > 0)
- {
- if(Y_LANG.isString && redrawAttrs[attr])
- {
- forceRedraw = true;
- }
- else if(Y_LANG.isObject(attr))
- {
- for(key in redrawAttrs)
- {
- if(redrawAttrs.hasOwnProperty(key) && attr[key])
- {
- forceRedraw = true;
- break;
- }
- }
- }
- }
- if(forceRedraw)
- {
- host._redraw();
- }
- },
-
- /**
- * Storage for `x` attribute.
- *
- * @property _x
- * @type Number
- * @private
- */
- _x: 0,
-
- /**
- * Storage for `y` attribute.
- *
- * @property _y
- * @type Number
- * @private
- */
- _y: 0,
-
- /**
- * Gets the current position of the graphic instance in page coordinates.
- *
- * @method getXY
- * @return Array The XY position of the shape.
- */
- getXY: function()
- {
- var node = this.parentNode,
- x = this.get("x"),
- y = this.get("y"),
- xy;
- if(node)
- {
- xy = Y.DOM.getXY(node);
- xy[0] += x;
- xy[1] += y;
- }
- else
- {
- xy = Y.DOM._getOffset(this._node);
- }
- return xy;
- },
-
- /**
- * Initializes the class.
- *
- * @method initializer
- * @private
- */
- initializer: function() {
- var render = this.get("render"),
- visibility = this.get("visible") ? "visible" : "hidden";
- this._shapes = {};
- this._contentBounds = {
- left: 0,
- top: 0,
- right: 0,
- bottom: 0
- };
- this._node = this._createGraphic();
- this._node.style.left = this.get("x") + "px";
- this._node.style.top = this.get("y") + "px";
- this._node.style.visibility = visibility;
- this._node.setAttribute("id", this.get("id"));
- if(render)
- {
- this.render(render);
- }
- },
-
- /**
- * Adds the graphics node to the dom.
- *
- * @method render
- * @param {HTMLElement} parentNode node in which to render the graphics node into.
- */
- render: function(render) {
- var parentNode = render || DOCUMENT.body,
- node = this._node,
- w,
- h;
- if(render instanceof Y.Node)
- {
- parentNode = render._node;
- }
- else if(Y.Lang.isString(render))
- {
- parentNode = Y.Selector.query(render, DOCUMENT.body, true);
- }
- w = this.get("width") || parseInt(Y.DOM.getComputedStyle(parentNode, "width"), 10);
- h = this.get("height") || parseInt(Y.DOM.getComputedStyle(parentNode, "height"), 10);
- parentNode.appendChild(node);
- this.parentNode = parentNode;
- this.set("width", w);
- this.set("height", h);
- return this;
- },
-
- /**
- * Removes all nodes.
- *
- * @method destroy
- */
- destroy: function()
- {
- this.removeAllShapes();
- if(this._node)
- {
- this._removeChildren(this._node);
- if(this._node.parentNode)
- {
- this._node.parentNode.removeChild(this._node);
- }
- this._node = null;
- }
- },
-
- /**
- * Generates a shape instance by type.
- *
- * @method addShape
- * @param {Object} cfg attributes for the shape
- * @return Shape
- */
- addShape: function(cfg)
- {
- cfg.graphic = this;
- if(!this.get("visible"))
- {
- cfg.visible = false;
- }
- var ShapeClass = this._getShapeClass(cfg.type),
- shape = new ShapeClass(cfg);
- this._appendShape(shape);
- shape._appendStrokeAndFill();
- return shape;
- },
-
- /**
- * Adds a shape instance to the graphic instance.
- *
- * @method _appendShape
- * @param {Shape} shape The shape instance to be added to the graphic.
- * @private
- */
- _appendShape: function(shape)
- {
- var node = shape.node,
- parentNode = this._frag || this._node;
- if(this.get("autoDraw") || this.get("autoSize") === "sizeContentToGraphic")
- {
- parentNode.appendChild(node);
- }
- else
- {
- this._getDocFrag().appendChild(node);
- }
- },
-
- /**
- * Removes a shape instance from from the graphic instance.
- *
- * @method removeShape
- * @param {Shape|String} shape The instance or id of the shape to be removed.
- */
- removeShape: function(shape)
- {
- if(!(shape instanceof VMLShape))
- {
- if(Y_LANG.isString(shape))
- {
- shape = this._shapes[shape];
- }
- }
- if(shape && (shape instanceof VMLShape))
- {
- shape._destroy();
- this._shapes[shape.get("id")] = null;
- delete this._shapes[shape.get("id")];
- }
- if(this.get("autoDraw"))
- {
- this._redraw();
- }
- },
-
- /**
- * Removes all shape instances from the dom.
- *
- * @method removeAllShapes
- */
- removeAllShapes: function()
- {
- var shapes = this._shapes,
- i;
- for(i in shapes)
- {
- if(shapes.hasOwnProperty(i))
- {
- shapes[i].destroy();
- }
- }
- this._shapes = {};
- },
-
- /**
- * Removes all child nodes.
- *
- * @method _removeChildren
- * @param node
- * @private
- */
- _removeChildren: function(node)
- {
- if(node.hasChildNodes())
- {
- var child;
- while(node.firstChild)
- {
- child = node.firstChild;
- this._removeChildren(child);
- node.removeChild(child);
- }
- }
- },
-
- /**
- * Clears the graphics object.
- *
- * @method clear
- */
- clear: function() {
- this.removeAllShapes();
- this._removeChildren(this._node);
- },
-
- /**
- * Toggles visibility
- *
- * @method _toggleVisible
- * @param {Boolean} val indicates visibilitye
- * @private
- */
- _toggleVisible: function(val)
- {
- var i,
- shapes = this._shapes,
- visibility = val ? "visible" : "hidden";
- if(shapes)
- {
- for(i in shapes)
- {
- if(shapes.hasOwnProperty(i))
- {
- shapes[i].set("visible", val);
- }
- }
- }
- if(this._node)
- {
- this._node.style.visibility = visibility;
- }
- if(this._node)
- {
- this._node.style.visibility = visibility;
- }
- },
-
- /**
- * Sets the size of the graphics object.
- *
- * @method setSize
- * @param w {Number} width to set for the instance.
- * @param h {Number} height to set for the instance.
- */
- setSize: function(w, h) {
- w = Math.round(w);
- h = Math.round(h);
- this._node.style.width = w + 'px';
- this._node.style.height = h + 'px';
- },
-
- /**
- * Sets the positon of the graphics object.
- *
- * @method setPosition
- * @param {Number} x x-coordinate for the object.
- * @param {Number} y y-coordinate for the object.
- */
- setPosition: function(x, y)
- {
- x = Math.round(x);
- y = Math.round(y);
- this._node.style.left = x + "px";
- this._node.style.top = y + "px";
- },
-
- /**
- * Creates a group element
- *
- * @method _createGraphic
- * @private
- */
- _createGraphic: function() {
- var group = DOCUMENT.createElement(
- '<group xmlns="urn:schemas-microsft.com:vml"' +
- ' style="behavior:url(#default#VML);padding:0px 0px 0px 0px;display:block;position:absolute;top:0px;left:0px;zoom:1;"' +
- '/>'
- );
- return group;
- },
-
- /**
- * Creates a graphic node
- *
- * @method _createGraphicNode
- * @param {String} type node type to create
- * @param {String} pe specified pointer-events value
- * @return HTMLElement
- * @private
- */
- _createGraphicNode: function(type)
- {
- return DOCUMENT.createElement(
- '<' +
- type +
- ' xmlns="urn:schemas-microsft.com:vml"' +
- ' style="behavior:url(#default#VML);display:inline-block;zoom:1;"' +
- '/>'
- );
-
- },
-
- /**
- * Returns a shape based on the id of its dom node.
- *
- * @method getShapeById
- * @param {String} id Dom id of the shape's node attribute.
- * @return Shape
- */
- getShapeById: function(id)
- {
- return this._shapes[id];
- },
-
- /**
- * Returns a shape class. Used by `addShape`.
- *
- * @method _getShapeClass
- * @param {Shape | String} val Indicates which shape class.
- * @return Function
- * @private
- */
- _getShapeClass: function(val)
- {
- var shape = this._shapeClass[val];
- if(shape)
- {
- return shape;
- }
- return val;
- },
-
- /**
- * Look up for shape classes. Used by `addShape` to retrieve a class for instantiation.
- *
- * @property _shapeClass
- * @type Object
- * @private
- */
- _shapeClass: {
- circle: Y.VMLCircle,
- rect: Y.VMLRect,
- path: Y.VMLPath,
- ellipse: Y.VMLEllipse,
- pieslice: Y.VMLPieSlice
- },
-
- /**
- * Allows for creating multiple shapes in order to batch appending and redraw operations.
- *
- * @method batch
- * @param {Function} method Method to execute.
- */
- batch: function(method)
- {
- var autoDraw = this.get("autoDraw");
- this.set("autoDraw", false);
- method.apply();
- this.set("autoDraw", autoDraw);
- },
-
- /**
- * Returns a document fragment to for attaching shapes.
- *
- * @method _getDocFrag
- * @return DocumentFragment
- * @private
- */
- _getDocFrag: function()
- {
- if(!this._frag)
- {
- this._frag = DOCUMENT.createDocumentFragment();
- }
- return this._frag;
- },
-
- /**
- * Adds a shape to the redraw queue and calculates the contentBounds.
- *
- * @method addToRedrawQueue
- * @param shape {VMLShape}
- * @protected
- */
- addToRedrawQueue: function(shape)
- {
- var shapeBox,
- box;
- this._shapes[shape.get("id")] = shape;
- if(!this.get("resizeDown"))
- {
- shapeBox = shape.getBounds();
- box = this._contentBounds;
- box.left = box.left < shapeBox.left ? box.left : shapeBox.left;
- box.top = box.top < shapeBox.top ? box.top : shapeBox.top;
- box.right = box.right > shapeBox.right ? box.right : shapeBox.right;
- box.bottom = box.bottom > shapeBox.bottom ? box.bottom : shapeBox.bottom;
- box.width = box.right - box.left;
- box.height = box.bottom - box.top;
- this._contentBounds = box;
- }
- if(this.get("autoDraw"))
- {
- this._redraw();
- }
- },
-
- /**
- * Redraws all shapes.
- *
- * @method _redraw
- * @private
- */
- _redraw: function()
- {
- var autoSize = this.get("autoSize"),
- preserveAspectRatio,
- node = this.parentNode,
- nodeWidth = parseFloat(Y.DOM.getComputedStyle(node, "width")),
- nodeHeight = parseFloat(Y.DOM.getComputedStyle(node, "height")),
- xCoordOrigin = 0,
- yCoordOrigin = 0,
- box = this.get("resizeDown") ? this._getUpdatedContentBounds() : this._contentBounds,
- left = box.left,
- right = box.right,
- top = box.top,
- bottom = box.bottom,
- contentWidth = right - left,
- contentHeight = bottom - top,
- aspectRatio,
- xCoordSize,
- yCoordSize,
- scaledWidth,
- scaledHeight,
- visible = this.get("visible");
- this._node.style.visibility = "hidden";
- if(autoSize)
- {
- if(autoSize === "sizeContentToGraphic")
- {
- preserveAspectRatio = this.get("preserveAspectRatio");
- if(preserveAspectRatio === "none" || contentWidth/contentHeight === nodeWidth/nodeHeight)
- {
- xCoordOrigin = left;
- yCoordOrigin = top;
- xCoordSize = contentWidth;
- yCoordSize = contentHeight;
- }
- else
- {
- if(contentWidth * nodeHeight/contentHeight > nodeWidth)
- {
- aspectRatio = nodeHeight/nodeWidth;
- xCoordSize = contentWidth;
- yCoordSize = contentWidth * aspectRatio;
- scaledHeight = (nodeWidth * (contentHeight/contentWidth)) * (yCoordSize/nodeHeight);
- yCoordOrigin = this._calculateCoordOrigin(preserveAspectRatio.slice(5).toLowerCase(), scaledHeight, yCoordSize);
- yCoordOrigin = top + yCoordOrigin;
- xCoordOrigin = left;
- }
- else
- {
- aspectRatio = nodeWidth/nodeHeight;
- xCoordSize = contentHeight * aspectRatio;
- yCoordSize = contentHeight;
- scaledWidth = (nodeHeight * (contentWidth/contentHeight)) * (xCoordSize/nodeWidth);
- xCoordOrigin = this._calculateCoordOrigin(preserveAspectRatio.slice(1, 4).toLowerCase(), scaledWidth, xCoordSize);
- xCoordOrigin = xCoordOrigin + left;
- yCoordOrigin = top;
- }
- }
- this._node.style.width = nodeWidth + "px";
- this._node.style.height = nodeHeight + "px";
- this._node.coordOrigin = xCoordOrigin + ", " + yCoordOrigin;
- }
- else
- {
- xCoordSize = contentWidth;
- yCoordSize = contentHeight;
- this._node.style.width = contentWidth + "px";
- this._node.style.height = contentHeight + "px";
- this._state.width = contentWidth;
- this._state.height = contentHeight;
-
- }
- this._node.coordSize = xCoordSize + ", " + yCoordSize;
- }
- else
- {
- this._node.style.width = nodeWidth + "px";
- this._node.style.height = nodeHeight + "px";
- this._node.coordSize = nodeWidth + ", " + nodeHeight;
- }
- if(this._frag)
- {
- this._node.appendChild(this._frag);
- this._frag = null;
- }
- if(visible)
- {
- this._node.style.visibility = "visible";
- }
- },
-
- /**
- * Determines the value for either an x or y coordinate to be used for the <code>coordOrigin</code> of the Graphic.
- *
- * @method _calculateCoordOrigin
- * @param {String} position The position for placement. Possible values are min, mid and max.
- * @param {Number} size The total scaled size of the content.
- * @param {Number} coordsSize The coordsSize for the Graphic.
- * @return Number
- * @private
- */
- _calculateCoordOrigin: function(position, size, coordsSize)
- {
- var coord;
- switch(position)
- {
- case "min" :
- coord = 0;
- break;
- case "mid" :
- coord = (size - coordsSize)/2;
- break;
- case "max" :
- coord = (size - coordsSize);
- break;
- }
- return coord;
- },
-
- /**
- * Recalculates and returns the `contentBounds` for the `Graphic` instance.
- *
- * @method _getUpdatedContentBounds
- * @return {Object}
- * @private
- */
- _getUpdatedContentBounds: function()
- {
- var bounds,
- i,
- shape,
- queue = this._shapes,
- box = {};
- for(i in queue)
- {
- if(queue.hasOwnProperty(i))
- {
- shape = queue[i];
- bounds = shape.getBounds();
- box.left = Y_LANG.isNumber(box.left) ? Math.min(box.left, bounds.left) : bounds.left;
- box.top = Y_LANG.isNumber(box.top) ? Math.min(box.top, bounds.top) : bounds.top;
- box.right = Y_LANG.isNumber(box.right) ? Math.max(box.right, bounds.right) : bounds.right;
- box.bottom = Y_LANG.isNumber(box.bottom) ? Math.max(box.bottom, bounds.bottom) : bounds.bottom;
- }
- }
- box.left = Y_LANG.isNumber(box.left) ? box.left : 0;
- box.top = Y_LANG.isNumber(box.top) ? box.top : 0;
- box.right = Y_LANG.isNumber(box.right) ? box.right : 0;
- box.bottom = Y_LANG.isNumber(box.bottom) ? box.bottom : 0;
- this._contentBounds = box;
- return box;
- },
-
- /**
- * Inserts shape on the top of the tree.
- *
- * @method _toFront
- * @param {VMLShape} Shape to add.
- * @private
- */
- _toFront: function(shape)
- {
- var contentNode = this._node;
- if(shape instanceof Y.VMLShape)
- {
- shape = shape.get("node");
- }
- if(contentNode && shape)
- {
- contentNode.appendChild(shape);
- }
- },
-
- /**
- * Inserts shape as the first child of the content node.
- *
- * @method _toBack
- * @param {VMLShape} Shape to add.
- * @private
- */
- _toBack: function(shape)
- {
- var contentNode = this._node,
- targetNode;
- if(shape instanceof Y.VMLShape)
- {
- shape = shape.get("node");
- }
- if(contentNode && shape)
- {
- targetNode = contentNode.firstChild;
- if(targetNode)
- {
- contentNode.insertBefore(shape, targetNode);
- }
- else
- {
- contentNode.appendChild(shape);
- }
- }
- }
- });
- Y.VMLGraphic = VMLGraphic;
-
-
-