4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5 <title>The source code</title>
6 <link href="../prettify/prettify.css" type="text/css" rel="stylesheet" />
7 <script type="text/javascript" src="../prettify/prettify.js"></script>
8 <style type="text/css">
9 .highlight { display: block; background-color: #ddd; }
11 <script type="text/javascript">
12 function highlight() {
13 document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
17 <body onload="prettyPrint(); highlight();">
18 <pre class="prettyprint lang-js"><span id='Ext-draw-Surface'>/**
19 </span> * A Surface is an interface to render methods inside a draw {@link Ext.draw.Component}.
20 * A Surface contains methods to render sprites, get bounding boxes of sprites, add
21 * sprites to the canvas, initialize other graphic components, etc. One of the most used
22 * methods for this class is the `add` method, to add Sprites to the surface.
24 * Most of the Surface methods are abstract and they have a concrete implementation
25 * in VML or SVG engines.
27 * A Surface instance can be accessed as a property of a draw component. For example:
29 * drawComponent.surface.add({
37 * The configuration object passed in the `add` method is the same as described in the {@link Ext.draw.Sprite}
38 * class documentation.
42 * You can also add event listeners to the surface using the `Observable` listener syntax. Supported events are:
55 * drawComponent.surface.on({
56 * 'mousemove': function() {
57 * console.log('moving the mouse over the surface');
63 * var drawComponent = Ext.create('Ext.draw.Component', {
66 * renderTo: document.body
67 * }), surface = drawComponent.surface;
105 * group: 'rectangles'
113 * group: 'rectangles'
116 * // Get references to my groups
117 * circles = surface.getGroup('circles');
118 * rectangles = surface.getGroup('rectangles');
120 * // Animate the circles down
130 * // Animate the rectangles across
131 * rectangles.animate({
140 Ext.define('Ext.draw.Surface', {
142 /* Begin Definitions */
145 observable: 'Ext.util.Observable'
148 requires: ['Ext.draw.CompositeSprite'],
149 uses: ['Ext.draw.engine.Svg', 'Ext.draw.engine.Vml'],
151 separatorRe: /[, ]+/,
154 <span id='Ext-draw-Surface-method-create'> /**
155 </span> * Creates and returns a new concrete Surface instance appropriate for the current environment.
156 * @param {Object} config Initial configuration for the Surface instance
157 * @param {[String]} enginePriority Optional order of implementations to use; the first one that is
158 * available in the current environment will be used. Defaults to `['Svg', 'Vml']`.
159 * @return {Object} The created Surface or false.
162 create: function(config, enginePriority) {
163 enginePriority = enginePriority || ['Svg', 'Vml'];
166 len = enginePriority.length,
169 for (; i < len; i++) {
170 if (Ext.supports[enginePriority[i]]) {
171 return Ext.create('Ext.draw.engine.' + enginePriority[i], config);
178 /* End Definitions */
183 "clip-rect": "0 0 1e9 1e9",
184 cursor: "default",
187 'dominant-baseline': 'auto',
188 fill: "none",
189 "fill-opacity": 1,
190 font: '10px "Arial"',
191 "font-family": '"Arial"',
192 "font-size": "10",
193 "font-style": "normal",
194 "font-weight": 400,
195 gradient: "",
198 href: "http://sencha.com/",
200 path: "M0,0",
204 scale: "1 1",
206 stroke: "#000",
207 "stroke-dasharray": "",
208 "stroke-linecap": "butt",
209 "stroke-linejoin": "butt",
210 "stroke-miterlimit": 0,
211 "stroke-opacity": 1,
212 "stroke-width": 1,
213 target: "_blank",
215 "text-anchor": "middle",
216 title: "Ext Draw",
223 <span id='Ext-draw-Surface-cfg-height'> /**
224 </span> * @cfg {Number} height
225 * The height of this component in pixels (defaults to auto).
226 * **Note** to express this dimension as a percentage or offset see {@link Ext.Component#anchor}.
228 <span id='Ext-draw-Surface-cfg-width'> /**
229 </span> * @cfg {Number} width
230 * The width of this component in pixels (defaults to auto).
231 * **Note** to express this dimension as a percentage or offset see {@link Ext.Component#anchor}.
234 container: undefined,
240 <span id='Ext-draw-Surface-method-constructor'> /**
241 </span> * Creates new Surface.
242 * @param {Object} config (optional) Config object.
244 constructor: function(config) {
246 config = config || {};
247 Ext.apply(me, config);
249 me.domRef = Ext.getDoc().dom;
251 me.customAttributes = {};
264 me.mixins.observable.constructor.call(me);
270 me.render(me.renderTo);
273 me.initBackground(config.background);
276 // @private called to initialize components in the surface
277 // this is dependent on the underlying implementation.
278 initSurface: Ext.emptyFn,
280 // @private called to setup the surface to render an item
281 //this is dependent on the underlying implementation.
282 renderItem: Ext.emptyFn,
285 renderItems: Ext.emptyFn,
288 setViewBox: Ext.emptyFn,
290 <span id='Ext-draw-Surface-method-addCls'> /**
291 </span> * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
295 * drawComponent.surface.addCls(sprite, 'x-visible');
297 * @param {Object} sprite The sprite to add the class to.
298 * @param {String/[String]} className The CSS class to add, or an array of classes
303 <span id='Ext-draw-Surface-method-removeCls'> /**
304 </span> * Removes one or more CSS classes from the element.
308 * drawComponent.surface.removeCls(sprite, 'x-visible');
310 * @param {Object} sprite The sprite to remove the class from.
311 * @param {String/[String]} className The CSS class to remove, or an array of classes
314 removeCls: Ext.emptyFn,
316 <span id='Ext-draw-Surface-method-setStyle'> /**
317 </span> * Sets CSS style attributes to an element.
321 * drawComponent.surface.setStyle(sprite, {
322 * 'cursor': 'pointer'
325 * @param {Object} sprite The sprite to add, or an array of classes to
326 * @param {Object} styles An Object with CSS styles.
329 setStyle: Ext.emptyFn,
332 initGradients: function() {
333 var gradients = this.gradients;
335 Ext.each(gradients, this.addGradient, this);
340 initItems: function() {
341 var items = this.items;
342 this.items = Ext.create('Ext.draw.CompositeSprite');
343 this.groups = Ext.create('Ext.draw.CompositeSprite');
350 initBackground: function(config) {
354 gradientId, gradient, backgroundSprite;
356 if (config.gradient) {
357 gradient = config.gradient;
358 gradientId = gradient.id;
359 me.addGradient(gradient);
360 me.background = me.add({
366 fill: 'url(#' + gradientId + ')'
368 } else if (config.fill) {
369 me.background = me.add({
377 } else if (config.image) {
378 me.background = me.add({
390 <span id='Ext-draw-Surface-method-setSize'> /**
391 </span> * Sets the size of the surface. Accomodates the background (if any) to fit the new size too.
395 * drawComponent.surface.setSize(500, 500);
397 * This method is generally called when also setting the size of the draw Component.
399 * @param {Number} w The new width of the canvas.
400 * @param {Number} h The new height of the canvas.
402 setSize: function(w, h) {
403 if (this.background) {
404 this.background.setAttributes({
413 scrubAttrs: function(sprite) {
419 // Narrow down attributes to the main set
420 if (this.translateAttrs.hasOwnProperty(i)) {
422 attrs[this.translateAttrs[i]] = sattr[i];
423 exclude[this.translateAttrs[i]] = true;
425 else if (this.availableAttrs.hasOwnProperty(i) && !exclude[i]) {
434 onClick: function(e) {
435 this.processEvent('click', e);
439 onMouseUp: function(e) {
440 this.processEvent('mouseup', e);
444 onMouseDown: function(e) {
445 this.processEvent('mousedown', e);
449 onMouseOver: function(e) {
450 this.processEvent('mouseover', e);
454 onMouseOut: function(e) {
455 this.processEvent('mouseout', e);
459 onMouseMove: function(e) {
460 this.fireEvent('mousemove', e);
464 onMouseEnter: Ext.emptyFn,
467 onMouseLeave: Ext.emptyFn,
469 <span id='Ext-draw-Surface-method-addGradient'> /**
470 </span> * Adds a gradient definition to the Surface. Note that in some surface engines, adding
471 * a gradient via this method will not take effect if the surface has already been rendered.
472 * Therefore, it is preferred to pass the gradients as an item to the surface config, rather
473 * than calling this method, especially if the surface is rendered immediately (e.g. due to
474 * 'renderTo' in its config). For more information on how to create gradients in the Chart
475 * configuration object please refer to {@link Ext.chart.Chart}.
477 * The gradient object to be passed into this method is composed by:
479 * - **id** - string - The unique name of the gradient.
480 * - **angle** - number, optional - The angle of the gradient in degrees.
481 * - **stops** - object - An object with numbers as keys (from 0 to 100) and style objects as values.
485 * drawComponent.surface.addGradient({
500 addGradient: Ext.emptyFn,
502 <span id='Ext-draw-Surface-method-add'> /**
503 </span> * Adds a Sprite to the surface. See {@link Ext.draw.Sprite} for the configuration object to be
504 * passed into this method.
508 * drawComponent.surface.add({
518 var args = Array.prototype.slice.call(arguments),
522 var hasMultipleArgs = args.length > 1;
523 if (hasMultipleArgs || Ext.isArray(args[0])) {
524 var items = hasMultipleArgs ? args : args[0],
528 for (i = 0, ln = items.length; i < ln; i++) {
530 item = this.add(item);
536 sprite = this.prepareItems(args[0], true)[0];
537 this.normalizeSpriteCollection(sprite);
542 <span id='Ext-draw-Surface-method-normalizeSpriteCollection'> /**
544 * Inserts or moves a given sprite into the correct position in the items
545 * MixedCollection, according to its zIndex. Will be inserted at the end of
546 * an existing series of sprites with the same or lower zIndex. If the sprite
547 * is already positioned within an appropriate zIndex group, it will not be moved.
548 * This ordering can be used by subclasses to assist in rendering the sprites in
549 * the correct order for proper z-index stacking.
550 * @param {Ext.draw.Sprite} sprite
551 * @return {Number} the sprite's new index in the list
553 normalizeSpriteCollection: function(sprite) {
554 var items = this.items,
555 zIndex = sprite.attr.zIndex,
556 idx = items.indexOf(sprite);
558 if (idx < 0 || (idx > 0 && items.getAt(idx - 1).attr.zIndex > zIndex) ||
559 (idx < items.length - 1 && items.getAt(idx + 1).attr.zIndex < zIndex)) {
561 idx = items.findIndexBy(function(otherSprite) {
562 return otherSprite.attr.zIndex > zIndex;
567 items.insert(idx, sprite);
572 onAdd: function(sprite) {
573 var group = sprite.group,
574 draggable = sprite.draggable,
577 groups = [].concat(group);
579 for (i = 0; i < ln; i++) {
581 this.getGroup(group).add(sprite);
586 sprite.initDraggable();
590 <span id='Ext-draw-Surface-method-remove'> /**
591 </span> * Removes a given sprite from the surface, optionally destroying the sprite in the process.
592 * You can also call the sprite own `remove` method.
596 * drawComponent.surface.remove(sprite);
600 * @param {Ext.draw.Sprite} sprite
601 * @param {Boolean} destroySprite
602 * @return {Number} the sprite's new index in the list
604 remove: function(sprite, destroySprite) {
606 this.items.remove(sprite);
607 this.groups.each(function(item) {
611 if (destroySprite === true) {
617 <span id='Ext-draw-Surface-method-removeAll'> /**
618 </span> * Removes all sprites from the surface, optionally destroying the sprites in the process.
622 * drawComponent.surface.removeAll();
624 * @param {Boolean} destroySprites Whether to destroy all sprites when removing them.
625 * @return {Number} The sprite's new index in the list.
627 removeAll: function(destroySprites) {
628 var items = this.items.items,
631 for (i = ln - 1; i > -1; i--) {
632 this.remove(items[i], destroySprites);
636 onRemove: Ext.emptyFn,
638 onDestroy: Ext.emptyFn,
641 applyTransformations: function(sprite) {
642 sprite.bbox.transform = 0;
643 this.transform(sprite);
649 if (attr.translation.x != null || attr.translation.y != null) {
650 me.translate(sprite);
653 if (attr.scaling.x != null || attr.scaling.y != null) {
657 if (attr.rotation.degrees != null) {
662 sprite.bbox.transform = 0;
663 this.transform(sprite);
664 sprite.transformations = [];
669 rotate: function (sprite) {
671 deg = sprite.attr.rotation.degrees,
672 centerX = sprite.attr.rotation.x,
673 centerY = sprite.attr.rotation.y;
674 if (!Ext.isNumber(centerX) || !Ext.isNumber(centerY)) {
675 bbox = this.getBBox(sprite);
676 centerX = !Ext.isNumber(centerX) ? bbox.x + bbox.width / 2 : centerX;
677 centerY = !Ext.isNumber(centerY) ? bbox.y + bbox.height / 2 : centerY;
679 sprite.transformations.push({
680 type: "rotate",
688 translate: function(sprite) {
689 var x = sprite.attr.translation.x || 0,
690 y = sprite.attr.translation.y || 0;
691 sprite.transformations.push({
692 type: "translate",
699 scale: function(sprite) {
701 x = sprite.attr.scaling.x || 1,
702 y = sprite.attr.scaling.y || 1,
703 centerX = sprite.attr.scaling.centerX,
704 centerY = sprite.attr.scaling.centerY;
706 if (!Ext.isNumber(centerX) || !Ext.isNumber(centerY)) {
707 bbox = this.getBBox(sprite);
708 centerX = !Ext.isNumber(centerX) ? bbox.x + bbox.width / 2 : centerX;
709 centerY = !Ext.isNumber(centerY) ? bbox.y + bbox.height / 2 : centerY;
711 sprite.transformations.push({
712 type: "scale",
721 rectPath: function (x, y, w, h, r) {
723 return [["M", x + r, y], ["l", w - r * 2, 0], ["a", r, r, 0, 0, 1, r, r], ["l", 0, h - r * 2], ["a", r, r, 0, 0, 1, -r, r], ["l", r * 2 - w, 0], ["a", r, r, 0, 0, 1, -r, -r], ["l", 0, r * 2 - h], ["a", r, r, 0, 0, 1, r, -r], ["z"]];
725 return [["M", x, y], ["l", w, 0], ["l", 0, h], ["l", -w, 0], ["z"]];
729 ellipsePath: function (x, y, rx, ry) {
733 return [["M", x, y], ["m", 0, -ry], ["a", rx, ry, 0, 1, 1, 0, 2 * ry], ["a", rx, ry, 0, 1, 1, 0, -2 * ry], ["z"]];
737 getPathpath: function (el) {
742 getPathcircle: function (el) {
744 return this.ellipsePath(a.x, a.y, a.radius, a.radius);
748 getPathellipse: function (el) {
750 return this.ellipsePath(a.x, a.y,
751 a.radiusX || (a.width / 2) || 0,
752 a.radiusY || (a.height / 2) || 0);
756 getPathrect: function (el) {
758 return this.rectPath(a.x, a.y, a.width, a.height, a.r);
762 getPathimage: function (el) {
764 return this.rectPath(a.x || 0, a.y || 0, a.width, a.height);
768 getPathtext: function (el) {
769 var bbox = this.getBBoxText(el);
770 return this.rectPath(bbox.x, bbox.y, bbox.width, bbox.height);
773 createGroup: function(id) {
774 var group = this.groups.get(id);
776 group = Ext.create('Ext.draw.CompositeSprite', {
779 group.id = id || Ext.id(null, 'ext-surface-group-');
780 this.groups.add(group);
785 <span id='Ext-draw-Surface-method-getGroup'> /**
786 </span> * Returns a new group or an existent group associated with the current surface.
787 * The group returned is a {@link Ext.draw.CompositeSprite} group.
791 * var spriteGroup = drawComponent.surface.getGroup('someGroupId');
793 * @param {String} id The unique identifier of the group.
794 * @return {Object} The {@link Ext.draw.CompositeSprite}.
796 getGroup: function(id) {
797 if (typeof id == "string") {
798 var group = this.groups.get(id);
800 group = this.createGroup(id);
809 prepareItems: function(items, applyDefaults) {
810 items = [].concat(items);
811 // Make sure defaults are applied and item is initialized
813 for (i = 0, ln = items.length; i < ln; i++) {
815 if (!(item instanceof Ext.draw.Sprite)) {
816 // Temporary, just take in configs...
818 items[i] = this.createItem(item);
826 <span id='Ext-draw-Surface-method-setText'> /**
827 </span> * Changes the text in the sprite element. The sprite must be a `text` sprite.
828 * This method can also be called from {@link Ext.draw.Sprite}.
832 * var spriteGroup = drawComponent.surface.setText(sprite, 'my new text');
834 * @param {Object} sprite The Sprite to change the text.
835 * @param {String} text The new text to be set.
838 setText: Ext.emptyFn,
840 //@private Creates an item and appends it to the surface. Called
841 //as an internal method when calling `add`.
842 createItem: Ext.emptyFn,
844 <span id='Ext-draw-Surface-method-getId'> /**
845 </span> * Retrieves the id of this component.
846 * Will autogenerate an id if one has not already been set.
849 return this.id || (this.id = Ext.id(null, 'ext-surface-'));
852 <span id='Ext-draw-Surface-method-destroy'> /**
853 </span> * Destroys the surface. This is done by removing all components from it and
854 * also removing its reference to a DOM element.
858 * drawComponent.surface.destroy();
860 destroy: function() {