4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5 <title>The source code</title>
6 <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
7 <script type="text/javascript" src="../resources/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-static-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).
227 <span id='Ext-draw-Surface-cfg-width'> /**
228 </span> * @cfg {Number} width
229 * The width of this component in pixels (defaults to auto).
232 container: undefined,
238 <span id='Ext-draw-Surface-property-orderSpritesByZIndex'> /**
239 </span> * @private Flag indicating that the surface implementation requires sprites to be maintained
240 * in order of their zIndex. Impls that don't require this can set it to false.
242 orderSpritesByZIndex: true,
245 <span id='Ext-draw-Surface-method-constructor'> /**
246 </span> * Creates new Surface.
247 * @param {Object} config (optional) Config object.
249 constructor: function(config) {
251 config = config || {};
252 Ext.apply(me, config);
254 me.domRef = Ext.getDoc().dom;
256 me.customAttributes = {};
269 me.mixins.observable.constructor.call(me);
275 me.render(me.renderTo);
278 me.initBackground(config.background);
281 // @private called to initialize components in the surface
282 // this is dependent on the underlying implementation.
283 initSurface: Ext.emptyFn,
285 // @private called to setup the surface to render an item
286 //this is dependent on the underlying implementation.
287 renderItem: Ext.emptyFn,
290 renderItems: Ext.emptyFn,
293 setViewBox: function (x, y, width, height) {
294 if (isFinite(x) && isFinite(y) && isFinite(width) && isFinite(height)) {
295 this.viewBox = {x: x, y: y, width: width, height: height};
300 <span id='Ext-draw-Surface-method-addCls'> /**
301 </span> * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
305 * drawComponent.surface.addCls(sprite, 'x-visible');
307 * @param {Object} sprite The sprite to add the class to.
308 * @param {String/String[]} className The CSS class to add, or an array of classes
313 <span id='Ext-draw-Surface-method-removeCls'> /**
314 </span> * Removes one or more CSS classes from the element.
318 * drawComponent.surface.removeCls(sprite, 'x-visible');
320 * @param {Object} sprite The sprite to remove the class from.
321 * @param {String/String[]} className The CSS class to remove, or an array of classes
324 removeCls: Ext.emptyFn,
326 <span id='Ext-draw-Surface-method-setStyle'> /**
327 </span> * Sets CSS style attributes to an element.
331 * drawComponent.surface.setStyle(sprite, {
332 * 'cursor': 'pointer'
335 * @param {Object} sprite The sprite to add, or an array of classes to
336 * @param {Object} styles An Object with CSS styles.
339 setStyle: Ext.emptyFn,
342 initGradients: function() {
343 var gradients = this.gradients;
345 Ext.each(gradients, this.addGradient, this);
350 initItems: function() {
351 var items = this.items;
352 this.items = Ext.create('Ext.draw.CompositeSprite');
353 this.groups = Ext.create('Ext.draw.CompositeSprite');
360 initBackground: function(config) {
364 gradientId, gradient, backgroundSprite;
366 if (config.gradient) {
367 gradient = config.gradient;
368 gradientId = gradient.id;
369 me.addGradient(gradient);
370 me.background = me.add({
376 fill: 'url(#' + gradientId + ')'
378 } else if (config.fill) {
379 me.background = me.add({
387 } else if (config.image) {
388 me.background = me.add({
400 <span id='Ext-draw-Surface-method-setSize'> /**
401 </span> * Sets the size of the surface. Accomodates the background (if any) to fit the new size too.
405 * drawComponent.surface.setSize(500, 500);
407 * This method is generally called when also setting the size of the draw Component.
409 * @param {Number} w The new width of the canvas.
410 * @param {Number} h The new height of the canvas.
412 setSize: function(w, h) {
413 if (this.background) {
414 this.background.setAttributes({
424 scrubAttrs: function(sprite) {
430 // Narrow down attributes to the main set
431 if (this.translateAttrs.hasOwnProperty(i)) {
433 attrs[this.translateAttrs[i]] = sattr[i];
434 exclude[this.translateAttrs[i]] = true;
436 else if (this.availableAttrs.hasOwnProperty(i) && !exclude[i]) {
445 onClick: function(e) {
446 this.processEvent('click', e);
450 onMouseUp: function(e) {
451 this.processEvent('mouseup', e);
455 onMouseDown: function(e) {
456 this.processEvent('mousedown', e);
460 onMouseOver: function(e) {
461 this.processEvent('mouseover', e);
465 onMouseOut: function(e) {
466 this.processEvent('mouseout', e);
470 onMouseMove: function(e) {
471 this.fireEvent('mousemove', e);
475 onMouseEnter: Ext.emptyFn,
478 onMouseLeave: Ext.emptyFn,
480 <span id='Ext-draw-Surface-method-addGradient'> /**
481 </span> * Adds a gradient definition to the Surface. Note that in some surface engines, adding
482 * a gradient via this method will not take effect if the surface has already been rendered.
483 * Therefore, it is preferred to pass the gradients as an item to the surface config, rather
484 * than calling this method, especially if the surface is rendered immediately (e.g. due to
485 * 'renderTo' in its config). For more information on how to create gradients in the Chart
486 * configuration object please refer to {@link Ext.chart.Chart}.
488 * The gradient object to be passed into this method is composed by:
490 * - **id** - string - The unique name of the gradient.
491 * - **angle** - number, optional - The angle of the gradient in degrees.
492 * - **stops** - object - An object with numbers as keys (from 0 to 100) and style objects as values.
496 * drawComponent.surface.addGradient({
511 addGradient: Ext.emptyFn,
513 <span id='Ext-draw-Surface-method-add'> /**
514 </span> * Adds a Sprite to the surface. See {@link Ext.draw.Sprite} for the configuration object to be
515 * passed into this method.
519 * drawComponent.surface.add({
529 var args = Array.prototype.slice.call(arguments),
533 var hasMultipleArgs = args.length > 1;
534 if (hasMultipleArgs || Ext.isArray(args[0])) {
535 var items = hasMultipleArgs ? args : args[0],
539 for (i = 0, ln = items.length; i < ln; i++) {
541 item = this.add(item);
547 sprite = this.prepareItems(args[0], true)[0];
548 this.insertByZIndex(sprite);
553 <span id='Ext-draw-Surface-method-insertByZIndex'> /**
555 * Inserts a given sprite into the correct position in the items collection, according to
556 * its zIndex. It will be inserted at the end of an existing series of sprites with the same or
557 * lower zIndex. By ensuring sprites are always ordered, this allows surface subclasses to render
558 * the sprites in the correct order for proper z-index stacking.
559 * @param {Ext.draw.Sprite} sprite
560 * @return {Number} the sprite's new index in the list
562 insertByZIndex: function(sprite) {
564 sprites = me.items.items,
565 len = sprites.length,
567 zIndex = sprite.attr.zIndex,
573 if (me.orderSpritesByZIndex && len && zIndex < sprites[high].attr.zIndex) {
574 // Find the target index via a binary search for speed
575 while (low <= high) {
576 idx = ceil((low + high) / 2);
577 otherZIndex = sprites[idx].attr.zIndex;
578 if (otherZIndex > zIndex) {
581 else if (otherZIndex < zIndex) {
588 // Step forward to the end of a sequence of the same or lower z-index
589 while (idx < len && sprites[idx].attr.zIndex <= zIndex) {
594 me.items.insert(idx, sprite);
598 onAdd: function(sprite) {
599 var group = sprite.group,
600 draggable = sprite.draggable,
603 groups = [].concat(group);
605 for (i = 0; i < ln; i++) {
607 this.getGroup(group).add(sprite);
612 sprite.initDraggable();
616 <span id='Ext-draw-Surface-method-remove'> /**
617 </span> * Removes a given sprite from the surface, optionally destroying the sprite in the process.
618 * You can also call the sprite own `remove` method.
622 * drawComponent.surface.remove(sprite);
626 * @param {Ext.draw.Sprite} sprite
627 * @param {Boolean} destroySprite
628 * @return {Number} the sprite's new index in the list
630 remove: function(sprite, destroySprite) {
632 this.items.remove(sprite);
633 this.groups.each(function(item) {
637 if (destroySprite === true) {
643 <span id='Ext-draw-Surface-method-removeAll'> /**
644 </span> * Removes all sprites from the surface, optionally destroying the sprites in the process.
648 * drawComponent.surface.removeAll();
650 * @param {Boolean} destroySprites Whether to destroy all sprites when removing them.
651 * @return {Number} The sprite's new index in the list.
653 removeAll: function(destroySprites) {
654 var items = this.items.items,
657 for (i = ln - 1; i > -1; i--) {
658 this.remove(items[i], destroySprites);
662 onRemove: Ext.emptyFn,
664 onDestroy: Ext.emptyFn,
666 <span id='Ext-draw-Surface-method-applyViewBox'> /**
667 </span> * @private Using the current viewBox property and the surface's width and height, calculate the
668 * appropriate viewBoxShift that will be applied as a persistent transform to all sprites.
670 applyViewBox: function() {
672 viewBox = me.viewBox,
675 viewBoxX, viewBoxY, viewBoxWidth, viewBoxHeight,
676 relativeHeight, relativeWidth, size;
678 if (viewBox && (width || height)) {
679 viewBoxX = viewBox.x;
680 viewBoxY = viewBox.y;
681 viewBoxWidth = viewBox.width;
682 viewBoxHeight = viewBox.height;
683 relativeHeight = height / viewBoxHeight;
684 relativeWidth = width / viewBoxWidth;
686 if (viewBoxWidth * relativeHeight < width) {
687 viewBoxX -= (width - viewBoxWidth * relativeHeight) / 2 / relativeHeight;
689 if (viewBoxHeight * relativeWidth < height) {
690 viewBoxY -= (height - viewBoxHeight * relativeWidth) / 2 / relativeWidth;
693 size = 1 / Math.min(viewBoxWidth, relativeHeight);
703 transformToViewBox: function (x, y) {
704 if (this.viewBoxShift) {
705 var me = this, shift = me.viewBoxShift;
706 return [x * shift.scale - shift.dx, y * shift.scale - shift.dy];
713 applyTransformations: function(sprite) {
714 sprite.bbox.transform = 0;
715 this.transform(sprite);
721 if (attr.translation.x != null || attr.translation.y != null) {
722 me.translate(sprite);
725 if (attr.scaling.x != null || attr.scaling.y != null) {
729 if (attr.rotation.degrees != null) {
734 sprite.bbox.transform = 0;
735 this.transform(sprite);
736 sprite.transformations = [];
741 rotate: function (sprite) {
743 deg = sprite.attr.rotation.degrees,
744 centerX = sprite.attr.rotation.x,
745 centerY = sprite.attr.rotation.y;
746 if (!Ext.isNumber(centerX) || !Ext.isNumber(centerY)) {
747 bbox = this.getBBox(sprite);
748 centerX = !Ext.isNumber(centerX) ? bbox.x + bbox.width / 2 : centerX;
749 centerY = !Ext.isNumber(centerY) ? bbox.y + bbox.height / 2 : centerY;
751 sprite.transformations.push({
752 type: "rotate",
760 translate: function(sprite) {
761 var x = sprite.attr.translation.x || 0,
762 y = sprite.attr.translation.y || 0;
763 sprite.transformations.push({
764 type: "translate",
771 scale: function(sprite) {
773 x = sprite.attr.scaling.x || 1,
774 y = sprite.attr.scaling.y || 1,
775 centerX = sprite.attr.scaling.centerX,
776 centerY = sprite.attr.scaling.centerY;
778 if (!Ext.isNumber(centerX) || !Ext.isNumber(centerY)) {
779 bbox = this.getBBox(sprite);
780 centerX = !Ext.isNumber(centerX) ? bbox.x + bbox.width / 2 : centerX;
781 centerY = !Ext.isNumber(centerY) ? bbox.y + bbox.height / 2 : centerY;
783 sprite.transformations.push({
784 type: "scale",
793 rectPath: function (x, y, w, h, r) {
795 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"]];
797 return [["M", x, y], ["l", w, 0], ["l", 0, h], ["l", -w, 0], ["z"]];
801 ellipsePath: function (x, y, rx, ry) {
805 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"]];
809 getPathpath: function (el) {
814 getPathcircle: function (el) {
816 return this.ellipsePath(a.x, a.y, a.radius, a.radius);
820 getPathellipse: function (el) {
822 return this.ellipsePath(a.x, a.y,
823 a.radiusX || (a.width / 2) || 0,
824 a.radiusY || (a.height / 2) || 0);
828 getPathrect: function (el) {
830 return this.rectPath(a.x, a.y, a.width, a.height, a.r);
834 getPathimage: function (el) {
836 return this.rectPath(a.x || 0, a.y || 0, a.width, a.height);
840 getPathtext: function (el) {
841 var bbox = this.getBBoxText(el);
842 return this.rectPath(bbox.x, bbox.y, bbox.width, bbox.height);
845 createGroup: function(id) {
846 var group = this.groups.get(id);
848 group = Ext.create('Ext.draw.CompositeSprite', {
851 group.id = id || Ext.id(null, 'ext-surface-group-');
852 this.groups.add(group);
857 <span id='Ext-draw-Surface-method-getGroup'> /**
858 </span> * Returns a new group or an existent group associated with the current surface.
859 * The group returned is a {@link Ext.draw.CompositeSprite} group.
863 * var spriteGroup = drawComponent.surface.getGroup('someGroupId');
865 * @param {String} id The unique identifier of the group.
866 * @return {Object} The {@link Ext.draw.CompositeSprite}.
868 getGroup: function(id) {
869 if (typeof id == "string") {
870 var group = this.groups.get(id);
872 group = this.createGroup(id);
881 prepareItems: function(items, applyDefaults) {
882 items = [].concat(items);
883 // Make sure defaults are applied and item is initialized
885 for (i = 0, ln = items.length; i < ln; i++) {
887 if (!(item instanceof Ext.draw.Sprite)) {
888 // Temporary, just take in configs...
890 items[i] = this.createItem(item);
898 <span id='Ext-draw-Surface-method-setText'> /**
899 </span> * Changes the text in the sprite element. The sprite must be a `text` sprite.
900 * This method can also be called from {@link Ext.draw.Sprite}.
904 * var spriteGroup = drawComponent.surface.setText(sprite, 'my new text');
906 * @param {Object} sprite The Sprite to change the text.
907 * @param {String} text The new text to be set.
910 setText: Ext.emptyFn,
912 //@private Creates an item and appends it to the surface. Called
913 //as an internal method when calling `add`.
914 createItem: Ext.emptyFn,
916 <span id='Ext-draw-Surface-method-getId'> /**
917 </span> * Retrieves the id of this component.
918 * Will autogenerate an id if one has not already been set.
921 return this.id || (this.id = Ext.id(null, 'ext-surface-'));
924 <span id='Ext-draw-Surface-method-destroy'> /**
925 </span> * Destroys the surface. This is done by removing all components from it and
926 * also removing its reference to a DOM element.
930 * drawComponent.surface.destroy();
932 destroy: function() {