~bac/juju-gui/1103207

« back to all changes in this revision

Viewing changes to app/views/topology/panzoom.js

  • Committer: Matthew Scott
  • Date: 2013-01-09 17:23:33 UTC
  • mfrom: (307 juju-gui)
  • mto: This revision was merged to the branch mainline in revision 312.
  • Revision ID: matthew.scott@canonical.com-20130109172333-ad6ndevmn5p44dz0
Merging with trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
6
6
      d3ns = Y.namespace('d3');
7
7
 
8
8
  /**
9
 
   * Handle PanZoom within the a Topology.
 
9
   * Handle PanZoom within a Topology.
10
10
   *
11
11
   * Emitted events:
12
12
   *
25
25
        '#zoom-in-btn': {click: 'zoom_in'}
26
26
      },
27
27
      yui: {
28
 
        zoom: {callback: 'zoomHandler'},
29
 
        rendered: {callback: 'renderedHandler'}
 
28
        zoom: 'zoomHandler',
 
29
        rendered: 'renderedHandler'
30
30
      }
31
31
    },
32
32
 
33
 
    initializer: function(options) {
34
 
      PanZoomModule.superclass.constructor.apply(this, arguments);
35
 
      this._translate = [0, 0];
36
 
      this._scale = 1.0;
37
 
    },
38
 
 
39
 
    // Handler for 'zoom' event.
40
 
    zoomHandler: function(evt) {
41
 
      var s = this.slider,
42
 
          vis = this.get('component').vis;
43
 
 
44
 
      s.set('value', Math.floor(evt.scale * 100));
45
 
      this.rescale(vis, evt);
 
33
    componentBound: function() {
 
34
      var topo = this.get('component'),
 
35
          options = topo.options;
 
36
 
 
37
      this.toScale = d3.scale.linear()
 
38
                            .domain([options.minZoom, options.maxZoom])
 
39
                            .range([0.25, 2])
 
40
                            .clamp(true);
 
41
      this.toSlider = d3.scale.linear()
 
42
                            .domain([0.25, 2])
 
43
                            .range([options.minZoom, options.maxZoom])
 
44
                            .clamp(true);
46
45
    },
47
46
 
48
47
    renderSlider: function() {
49
48
      var self = this,
50
49
          topo = this.get('component'),
51
 
          value = 100,
52
 
          currentScale = topo.get('scale');
 
50
          options = topo.options,
 
51
          currentScale = topo.get('scale'),
 
52
          slider;
53
53
 
54
54
      if (self.slider) {
55
55
        return;
56
56
      }
57
 
      // Build a slider to control zoom level
58
 
      if (currentScale) {
59
 
        value = currentScale * 100;
60
 
      }
61
 
      var slider = new Y.Slider({
62
 
        min: 25,
63
 
        max: 200,
64
 
        value: value
 
57
 
 
58
      slider = new Y.Slider({
 
59
        min: options.minZoom,
 
60
        max: options.maxZoom,
 
61
        value: this.toSlider(currentScale)
65
62
      });
 
63
      // XXX: selection to module option
66
64
      slider.render('#slider-parent');
67
65
      topo.recordSubscription(this,
68
66
                              slider.after('valueChange', function(evt) {
69
 
                                // Don't fire a zoom if there's a zoom event
70
 
                                // already in progress; that will run rescale
71
 
                                // for us.
72
67
                                if (d3.event && d3.event.scale &&
73
68
                                    d3.event.translate) {
74
69
                                  return;
75
70
                                }
76
 
                                self._fire_zoom((
77
 
                                    evt.newVal - evt.prevVal) / 100);
 
71
                                self._fire_zoom(self.toScale(evt.newVal));
78
72
                              }));
79
 
      self.slider = slider;
 
73
      this.slider = slider;
80
74
    },
81
75
 
82
 
    update: function() {
83
 
      PanZoomModule.superclass.update.apply(this, arguments);
84
 
      return this;
 
76
    // Handler for 'zoom' event.
 
77
    zoomHandler: function(evt) {
 
78
      var slider = this.slider,
 
79
          topo = this.get('component'),
 
80
          height = topo.get('height'),
 
81
          width = topo.get('width'),
 
82
          options = topo.options;
 
83
 
 
84
      if (!this.slider) {
 
85
        return;
 
86
      }
 
87
      slider.set('value', this.toSlider(evt.scale));
 
88
      this.rescale(d3.event);
85
89
    },
86
90
 
87
91
    /*
89
93
     */
90
94
    zoom_out: function(data, context) {
91
95
      var slider = context.slider,
92
 
              val = slider.get('value');
 
96
          val = slider.get('value');
93
97
      slider.set('value', val - 25);
94
98
    },
95
99
 
98
102
     */
99
103
    zoom_in: function(data, context) {
100
104
      var slider = context.slider,
101
 
              val = slider.get('value');
 
105
          val = slider.get('value');
102
106
      slider.set('value', val + 25);
103
107
    },
104
108
 
105
109
    /*
106
110
     * Wrapper around the actual rescale method for zoom buttons.
107
111
     */
108
 
    _fire_zoom: function(delta) {
 
112
    _fire_zoom: function(scale) {
109
113
      var topo = this.get('component'),
110
114
          vis = topo.vis,
111
115
          zoom = topo.zoom,
 
116
          rect = topo.zoomPlane,
 
117
          delta,
112
118
          evt = {};
113
119
 
 
120
      delta = scale - topo.get('scale');
 
121
 
114
122
      // Build a temporary event that rescale can use of a similar
115
123
      // construction to d3.event.
116
 
      evt.translate = zoom.translate();
117
 
      evt.scale = zoom.scale() + delta;
118
 
 
 
124
      evt.scale = scale;
119
125
      // Update the scale in our zoom behavior manager to maintain state.
120
 
      zoom.scale(evt.scale);
121
 
 
 
126
      zoom.scale(Math.floor(scale));
122
127
      // Update the translate so that we scale from the center
123
128
      // instead of the origin.
124
 
      var rect = vis.select('rect');
125
 
      evt.translate[0] -= parseInt(rect.attr('width'), 10) / 2 * delta;
126
 
      evt.translate[1] -= parseInt(rect.attr('height'), 10) / 2 * delta;
 
129
      evt.translate = zoom.translate();
 
130
      evt.translate[0] -= (parseInt(rect.attr('width'), 10) / 2) * delta;
 
131
      evt.translate[1] -= (parseInt(rect.attr('height'), 10) / 2) * delta;
127
132
      zoom.translate(evt.translate);
128
133
 
129
 
      this.rescale(vis, evt);
 
134
      this.rescale(evt);
130
135
    },
131
136
 
132
137
    /*
133
138
     * Rescale the visualization on a zoom/pan event.
134
139
     */
135
 
    rescale: function(vis, evt) {
 
140
    rescale: function(evt) {
136
141
      // Make sure we don't scale outside of our bounds.
137
142
      // This check is needed because we're messing with d3's zoom
138
143
      // behavior outside of mouse events (e.g.: with the slider),
139
144
      // and can't trust that zoomExtent will play well.
140
 
      var new_scale = Math.floor(evt.scale * 100),
141
 
          topo = this.get('component');
 
145
      var topo = this.get('component'),
 
146
          options = topo.options,
 
147
          vis = topo.vis;
142
148
 
143
 
      if (new_scale < 25 || new_scale > 200) {
144
 
        evt.scale = topo.get('scale');
 
149
      if (!vis) {
 
150
        return;
145
151
      }
 
152
 
 
153
      evt.scale = this.toSlider(evt.scale) / 100.0;
 
154
 
146
155
      // Store the current value of scale so that it can be restored later.
147
 
      this._scale = evt.scale;
 
156
      topo.set('scale', evt.scale);
148
157
      // Store the current value of translate as well, by copying the event
149
158
      // array in order to avoid reference sharing.
150
 
      this._translate = Y.mix(evt.translate);
151
 
      vis.attr('transform', 'translate(' + evt.translate + ')' +
152
 
              ' scale(' + evt.scale + ')');
 
159
      topo.set('translate', Y.mix(evt.translate));
 
160
      vis.attr('transform', 'translate(' + topo.get('translate') + ')' +
 
161
              ' scale(' + topo.get('scale') + ')');
153
162
      topo.fire('rescaled');
154
163
    },
155
164
 
157
166
      // Preserve zoom when the scene is updated.
158
167
      var topo = this.get('component'),
159
168
          changed = false,
160
 
          currentScale = this._scale,
161
 
          currentTranslate = this._translate;
 
169
          currentScale = topo.get('scale'),
 
170
          currentTranslate = topo.get('translate');
162
171
 
163
172
      this.renderSlider();
164
173
      if (currentTranslate && currentTranslate !== topo.get('translate')) {
170
179
        changed = true;
171
180
      }
172
181
      if (changed) {
173
 
        this._fire_zoom(0);
 
182
        this.rescale({scale: currentScale, translate: currentTranslate});
174
183
      }
175
184
    }
176
185
  }, {
180
189
  views.PanZoomModule = PanZoomModule;
181
190
}, '0.1.0', {
182
191
  requires: [
 
192
    'node',
 
193
    'event',
 
194
    'slider',
183
195
    'd3',
184
196
    'd3-components',
185
 
    'node',
186
 
    'event',
187
197
    'juju-models',
188
198
    'juju-env'
189
199
  ]