web_interface/astpp/assets/js/excanvas.js   F
last analyzed

Complexity

Total Complexity 115
Complexity/F 2.35

Size

Lines of Code 890
Function Count 49

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 32769
dl 0
loc 890
rs 2.1818
c 0
b 0
f 0
wmc 115
mnd 5
bc 97
fnc 49
bpm 1.9795
cpm 2.3469
noi 17

47 Functions

Rating   Name   Duplication   Size   Complexity  
C 0 886 3
B excanvas.js ➔ CanvasRenderingContext2D_ 0 30 1
B G_vmlCanvasManager_.init_ 0 30 5
A contextPrototype.lineTo 0 7 1
A excanvas.js ➔ getContext 0 4 1
B excanvas.js ➔ matrixIsFinite 0 10 5
A excanvas.js ➔ setM 0 15 3
A excanvas.js ➔ matrixMultiply 0 16 4
A contextPrototype.createLinearGradient 0 8 1
A contextPrototype.strokeRect 0 13 1
A contextPrototype.translate 0 9 1
A excanvas.js ➔ bind 0 6 1
A contextPrototype.rotate 0 12 1
A contextPrototype.rect 0 7 1
A contextPrototype.save 0 7 1
B excanvas.js ➔ processStyle 0 23 5
F contextPrototype.stroke 0 182 25
B excanvas.js ➔ processLineCap 0 11 5
A CanvasGradient_.addColorStop 0 6 1
A contextPrototype.createRadialGradient 0 11 1
A contextPrototype.beginPath 0 5 1
A excanvas.js ➔ copyState 0 16 1
A contextPrototype.restore 0 4 1
A contextPrototype.clip 0 3 1
A contextPrototype.setTransform 0 9 1
A contextPrototype.createPattern 0 3 1
A contextPrototype.clearRect 0 3 1
A contextPrototype.moveTo 0 6 1
A contextPrototype.fill 0 3 1
A excanvas.js ➔ CanvasPattern_ 0 1 1
B contextPrototype.drawImage 0 107 6
B contextPrototype.arc 0 31 4
A contextPrototype.fillRect 0 13 1
A contextPrototype.scale 0 11 1
A contextPrototype.quadraticCurveTo 0 18 1
A G_vmlCanvasManager_.init 0 9 3
A excanvas.js ➔ onPropertyChange 0 14 3
A excanvas.js ➔ createMatrixIdentity 0 7 1
A excanvas.js ➔ onResize 0 7 2
A contextPrototype.closePath 0 3 1
A excanvas.js ➔ CanvasGradient_ 0 10 1
B G_vmlCanvasManager_.initElement 0 33 6
A excanvas.js ➔ bezierCurveTo 0 13 1
A contextPrototype.bezierCurveTo 0 8 1
A contextPrototype.arcTo 0 3 1
A contextPrototype.getCoords_ 0 7 1
A contextPrototype.transform 0 9 1

How to fix   Complexity   

Complexity

Complex classes like web_interface/astpp/assets/js/excanvas.js often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
// Copyright 2006 Google Inc.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//   http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
16
// Known Issues:
17
//
18
// * Patterns are not implemented.
19
// * Radial gradient are not implemented. The VML version of these look very
20
//   different from the canvas one.
21
// * Clipping paths are not implemented.
22
// * Coordsize. The width and height attribute have higher priority than the
23
//   width and height style values which isn't correct.
24
// * Painting mode isn't implemented.
25
// * Canvas width/height should is using content-box by default. IE in
26
//   Quirks mode will draw the canvas using border-box. Either change your
27
//   doctype to HTML5
28
//   (http://www.whatwg.org/specs/web-apps/current-work/#the-doctype)
29
//   or use Box Sizing Behavior from WebFX
30
//   (http://webfx.eae.net/dhtml/boxsizing/boxsizing.html)
31
// * Non uniform scaling does not correctly scale strokes.
32
// * Optimize. There is always room for speed improvements.
33
34
// Only add this code if we do not already have a canvas implementation
35
if (!document.createElement('canvas').getContext) {
36
37
(function() {
38
39
  // alias some functions to make (compiled) code shorter
40
  var m = Math;
0 ignored issues
show
Comprehensibility Best Practice introduced by
You seem to be aliasing the built-in name Math as m. This makes your code very difficult to follow, consider using the built-in name directly.
Loading history...
41
  var mr = m.round;
42
  var ms = m.sin;
43
  var mc = m.cos;
44
  var abs = m.abs;
45
  var sqrt = m.sqrt;
46
47
  // this is used for sub pixel precision
48
  var Z = 10;
49
  var Z2 = Z / 2;
50
51
  /**
52
   * This funtion is assigned to the <canvas> elements as element.getContext().
53
   * @this {HTMLElement}
54
   * @return {CanvasRenderingContext2D_}
55
   */
56
  function getContext() {
57
    return this.context_ ||
58
        (this.context_ = new CanvasRenderingContext2D_(this));
59
  }
60
61
  var slice = Array.prototype.slice;
62
63
  /**
64
   * Binds a function to an object. The returned function will always use the
65
   * passed in {@code obj} as {@code this}.
66
   *
67
   * Example:
68
   *
69
   *   g = bind(f, obj, a, b)
70
   *   g(c, d) // will do f.call(obj, a, b, c, d)
71
   *
72
   * @param {Function} f The function to bind the object to
73
   * @param {Object} obj The object that should act as this when the function
74
   *     is called
75
   * @param {*} var_args Rest arguments that will be used as the initial
76
   *     arguments when the function is called
77
   * @return {Function} A new function that has bound this
78
   */
79
  function bind(f, obj, var_args) {
0 ignored issues
show
Unused Code introduced by
The parameter var_args is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
80
    var a = slice.call(arguments, 2);
81
    return function() {
82
      return f.apply(obj, a.concat(slice.call(arguments)));
83
    };
84
  }
85
86
  var G_vmlCanvasManager_ = {
87
    init: function(opt_doc) {
88
      if (/MSIE/.test(navigator.userAgent) && !window.opera) {
0 ignored issues
show
Bug introduced by
The variable navigator seems to be never declared. If this is a global, consider adding a /** global: navigator */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
89
        var doc = opt_doc || document;
90
        // Create a dummy element so that IE will allow canvas elements to be
91
        // recognized.
92
        doc.createElement('canvas');
93
        doc.attachEvent('onreadystatechange', bind(this.init_, this, doc));
94
      }
95
    },
96
97
    init_: function(doc) {
98
      // create xmlns
99
      if (!doc.namespaces['g_vml_']) {
100
        doc.namespaces.add('g_vml_', 'urn:schemas-microsoft-com:vml',
101
                           '#default#VML');
102
103
      }
104
      if (!doc.namespaces['g_o_']) {
105
        doc.namespaces.add('g_o_', 'urn:schemas-microsoft-com:office:office',
106
                           '#default#VML');
107
      }
108
109
      // Setup default CSS.  Only add one style sheet per document
110
      if (!doc.styleSheets['ex_canvas_']) {
111
        var ss = doc.createStyleSheet();
112
        ss.owningElement.id = 'ex_canvas_';
113
        ss.cssText = 'canvas{display:inline-block;overflow:hidden;' +
114
            // default size is 300x150 in Gecko and Opera
115
            'text-align:left;width:300px;height:150px}' +
116
            'g_vml_\\:*{behavior:url(#default#VML)}' +
117
            'g_o_\\:*{behavior:url(#default#VML)}';
118
119
      }
120
121
      // find all canvas elements
122
      var els = doc.getElementsByTagName('canvas');
123
      for (var i = 0; i < els.length; i++) {
124
        this.initElement(els[i]);
125
      }
126
    },
127
128
    /**
129
     * Public initializes a canvas element so that it can be used as canvas
130
     * element from now on. This is called automatically before the page is
131
     * loaded but if you are creating elements using createElement you need to
132
     * make sure this is called on the element.
133
     * @param {HTMLElement} el The canvas element to initialize.
134
     * @return {HTMLElement} the element that was created.
135
     */
136
    initElement: function(el) {
137
      if (!el.getContext) {
138
139
        el.getContext = getContext;
140
141
        // Remove fallback content. There is no way to hide text nodes so we
142
        // just remove all childNodes. We could hide all elements and remove
143
        // text nodes but who really cares about the fallback content.
144
        el.innerHTML = '';
145
146
        // do not use inline function because that will leak memory
147
        el.attachEvent('onpropertychange', onPropertyChange);
148
        el.attachEvent('onresize', onResize);
149
150
        var attrs = el.attributes;
151
        if (attrs.width && attrs.width.specified) {
152
          // TODO: use runtimeStyle and coordsize
153
          // el.getContext().setWidth_(attrs.width.nodeValue);
154
          el.style.width = attrs.width.nodeValue + 'px';
155
        } else {
156
          el.width = el.clientWidth;
157
        }
158
        if (attrs.height && attrs.height.specified) {
159
          // TODO: use runtimeStyle and coordsize
160
          // el.getContext().setHeight_(attrs.height.nodeValue);
161
          el.style.height = attrs.height.nodeValue + 'px';
162
        } else {
163
          el.height = el.clientHeight;
164
        }
165
        //el.getContext().setCoordsize_()
166
      }
167
      return el;
168
    }
169
  };
170
171
  function onPropertyChange(e) {
172
    var el = e.srcElement;
173
174
    switch (e.propertyName) {
0 ignored issues
show
Coding Style introduced by
As per coding-style, switch statements should have a default case.
Loading history...
175
      case 'width':
176
        el.style.width = el.attributes.width.nodeValue + 'px';
177
        el.getContext().clearRect();
178
        break;
179
      case 'height':
180
        el.style.height = el.attributes.height.nodeValue + 'px';
181
        el.getContext().clearRect();
182
        break;
183
    }
184
  }
185
186
  function onResize(e) {
187
    var el = e.srcElement;
188
    if (el.firstChild) {
189
      el.firstChild.style.width =  el.clientWidth + 'px';
190
      el.firstChild.style.height = el.clientHeight + 'px';
191
    }
192
  }
193
194
  G_vmlCanvasManager_.init();
195
196
  // precompute "00" to "FF"
197
  var dec2hex = [];
198
  for (var i = 0; i < 16; i++) {
199
    for (var j = 0; j < 16; j++) {
200
      dec2hex[i * 16 + j] = i.toString(16) + j.toString(16);
201
    }
202
  }
203
204
  function createMatrixIdentity() {
205
    return [
206
      [1, 0, 0],
207
      [0, 1, 0],
208
      [0, 0, 1]
209
    ];
210
  }
211
212
  function matrixMultiply(m1, m2) {
213
    var result = createMatrixIdentity();
214
215
    for (var x = 0; x < 3; x++) {
216
      for (var y = 0; y < 3; y++) {
217
        var sum = 0;
218
219
        for (var z = 0; z < 3; z++) {
220
          sum += m1[x][z] * m2[z][y];
221
        }
222
223
        result[x][y] = sum;
224
      }
225
    }
226
    return result;
227
  }
228
229
  function copyState(o1, o2) {
230
    o2.fillStyle     = o1.fillStyle;
231
    o2.lineCap       = o1.lineCap;
232
    o2.lineJoin      = o1.lineJoin;
233
    o2.lineWidth     = o1.lineWidth;
234
    o2.miterLimit    = o1.miterLimit;
235
    o2.shadowBlur    = o1.shadowBlur;
236
    o2.shadowColor   = o1.shadowColor;
237
    o2.shadowOffsetX = o1.shadowOffsetX;
238
    o2.shadowOffsetY = o1.shadowOffsetY;
239
    o2.strokeStyle   = o1.strokeStyle;
240
    o2.globalAlpha   = o1.globalAlpha;
241
    o2.arcScaleX_    = o1.arcScaleX_;
242
    o2.arcScaleY_    = o1.arcScaleY_;
243
    o2.lineScale_    = o1.lineScale_;
244
  }
245
246
  function processStyle(styleString) {
247
    var str, alpha = 1;
248
249
    styleString = String(styleString);
250
    if (styleString.substring(0, 3) == 'rgb') {
251
      var start = styleString.indexOf('(', 3);
252
      var end = styleString.indexOf(')', start + 1);
253
      var guts = styleString.substring(start + 1, end).split(',');
254
255
      str = '#';
256
      for (var i = 0; i < 3; i++) {
257
        str += dec2hex[Number(guts[i])];
258
      }
259
260
      if (guts.length == 4 && styleString.substr(3, 1) == 'a') {
261
        alpha = guts[3];
262
      }
263
    } else {
264
      str = styleString;
265
    }
266
267
    return {color: str, alpha: alpha};
268
  }
269
270
  function processLineCap(lineCap) {
271
    switch (lineCap) {
272
      case 'butt':
273
        return 'flat';
274
      case 'round':
275
        return 'round';
276
      case 'square':
277
      default:
278
        return 'square';
279
    }
280
  }
281
282
  /**
283
   * This class implements CanvasRenderingContext2D interface as described by
284
   * the WHATWG.
285
   * @param {HTMLElement} surfaceElement The element that the 2D context should
286
   * be associated with
287
   */
288
  function CanvasRenderingContext2D_(surfaceElement) {
289
    this.m_ = createMatrixIdentity();
290
291
    this.mStack_ = [];
292
    this.aStack_ = [];
293
    this.currentPath_ = [];
294
295
    // Canvas context properties
296
    this.strokeStyle = '#000';
297
    this.fillStyle = '#000';
298
299
    this.lineWidth = 1;
300
    this.lineJoin = 'miter';
301
    this.lineCap = 'butt';
302
    this.miterLimit = Z * 1;
303
    this.globalAlpha = 1;
304
    this.canvas = surfaceElement;
305
306
    var el = surfaceElement.ownerDocument.createElement('div');
307
    el.style.width =  surfaceElement.clientWidth + 'px';
308
    el.style.height = surfaceElement.clientHeight + 'px';
309
    el.style.overflow = 'hidden';
310
    el.style.position = 'absolute';
311
    surfaceElement.appendChild(el);
312
313
    this.element_ = el;
314
    this.arcScaleX_ = 1;
315
    this.arcScaleY_ = 1;
316
    this.lineScale_ = 1;
317
  }
318
319
  var contextPrototype = CanvasRenderingContext2D_.prototype;
320
  contextPrototype.clearRect = function() {
321
    this.element_.innerHTML = '';
322
  };
323
324
  contextPrototype.beginPath = function() {
325
    // TODO: Branch current matrix so that save/restore has no effect
326
    //       as per safari docs.
327
    this.currentPath_ = [];
328
  };
329
330
  contextPrototype.moveTo = function(aX, aY) {
331
    var p = this.getCoords_(aX, aY);
332
    this.currentPath_.push({type: 'moveTo', x: p.x, y: p.y});
333
    this.currentX_ = p.x;
334
    this.currentY_ = p.y;
335
  };
336
337
  contextPrototype.lineTo = function(aX, aY) {
338
    var p = this.getCoords_(aX, aY);
339
    this.currentPath_.push({type: 'lineTo', x: p.x, y: p.y});
340
341
    this.currentX_ = p.x;
342
    this.currentY_ = p.y;
343
  };
344
345
  contextPrototype.bezierCurveTo = function(aCP1x, aCP1y,
346
                                            aCP2x, aCP2y,
347
                                            aX, aY) {
348
    var p = this.getCoords_(aX, aY);
349
    var cp1 = this.getCoords_(aCP1x, aCP1y);
350
    var cp2 = this.getCoords_(aCP2x, aCP2y);
351
    bezierCurveTo(this, cp1, cp2, p);
352
  };
353
354
  // Helper function that takes the already fixed cordinates.
355
  function bezierCurveTo(self, cp1, cp2, p) {
356
    self.currentPath_.push({
357
      type: 'bezierCurveTo',
358
      cp1x: cp1.x,
359
      cp1y: cp1.y,
360
      cp2x: cp2.x,
361
      cp2y: cp2.y,
362
      x: p.x,
363
      y: p.y
364
    });
365
    self.currentX_ = p.x;
366
    self.currentY_ = p.y;
367
  }
368
369
  contextPrototype.quadraticCurveTo = function(aCPx, aCPy, aX, aY) {
370
    // the following is lifted almost directly from
371
    // http://developer.mozilla.org/en/docs/Canvas_tutorial:Drawing_shapes
372
373
    var cp = this.getCoords_(aCPx, aCPy);
374
    var p = this.getCoords_(aX, aY);
375
376
    var cp1 = {
377
      x: this.currentX_ + 2.0 / 3.0 * (cp.x - this.currentX_),
378
      y: this.currentY_ + 2.0 / 3.0 * (cp.y - this.currentY_)
379
    };
380
    var cp2 = {
381
      x: cp1.x + (p.x - this.currentX_) / 3.0,
382
      y: cp1.y + (p.y - this.currentY_) / 3.0
383
    };
384
385
    bezierCurveTo(this, cp1, cp2, p);
386
  };
387
388
  contextPrototype.arc = function(aX, aY, aRadius,
389
                                  aStartAngle, aEndAngle, aClockwise) {
390
    aRadius *= Z;
391
    var arcType = aClockwise ? 'at' : 'wa';
392
393
    var xStart = aX + mc(aStartAngle) * aRadius - Z2;
394
    var yStart = aY + ms(aStartAngle) * aRadius - Z2;
395
396
    var xEnd = aX + mc(aEndAngle) * aRadius - Z2;
397
    var yEnd = aY + ms(aEndAngle) * aRadius - Z2;
398
399
    // IE won't render arches drawn counter clockwise if xStart == xEnd.
400
    if (xStart == xEnd && !aClockwise) {
401
      xStart += 0.125; // Offset xStart by 1/80 of a pixel. Use something
402
                       // that can be represented in binary
403
    }
404
405
    var p = this.getCoords_(aX, aY);
406
    var pStart = this.getCoords_(xStart, yStart);
407
    var pEnd = this.getCoords_(xEnd, yEnd);
408
409
    this.currentPath_.push({type: arcType,
410
                           x: p.x,
411
                           y: p.y,
412
                           radius: aRadius,
413
                           xStart: pStart.x,
414
                           yStart: pStart.y,
415
                           xEnd: pEnd.x,
416
                           yEnd: pEnd.y});
417
418
  };
419
420
  contextPrototype.rect = function(aX, aY, aWidth, aHeight) {
421
    this.moveTo(aX, aY);
422
    this.lineTo(aX + aWidth, aY);
423
    this.lineTo(aX + aWidth, aY + aHeight);
424
    this.lineTo(aX, aY + aHeight);
425
    this.closePath();
426
  };
427
428
  contextPrototype.strokeRect = function(aX, aY, aWidth, aHeight) {
429
    var oldPath = this.currentPath_;
430
    this.beginPath();
431
432
    this.moveTo(aX, aY);
433
    this.lineTo(aX + aWidth, aY);
434
    this.lineTo(aX + aWidth, aY + aHeight);
435
    this.lineTo(aX, aY + aHeight);
436
    this.closePath();
437
    this.stroke();
438
439
    this.currentPath_ = oldPath;
440
  };
441
442
  contextPrototype.fillRect = function(aX, aY, aWidth, aHeight) {
443
    var oldPath = this.currentPath_;
444
    this.beginPath();
445
446
    this.moveTo(aX, aY);
447
    this.lineTo(aX + aWidth, aY);
448
    this.lineTo(aX + aWidth, aY + aHeight);
449
    this.lineTo(aX, aY + aHeight);
450
    this.closePath();
451
    this.fill();
452
453
    this.currentPath_ = oldPath;
454
  };
455
456
  contextPrototype.createLinearGradient = function(aX0, aY0, aX1, aY1) {
457
    var gradient = new CanvasGradient_('gradient');
458
    gradient.x0_ = aX0;
459
    gradient.y0_ = aY0;
460
    gradient.x1_ = aX1;
461
    gradient.y1_ = aY1;
462
    return gradient;
463
  };
464
465
  contextPrototype.createRadialGradient = function(aX0, aY0, aR0,
466
                                                   aX1, aY1, aR1) {
467
    var gradient = new CanvasGradient_('gradientradial');
468
    gradient.x0_ = aX0;
469
    gradient.y0_ = aY0;
470
    gradient.r0_ = aR0;
471
    gradient.x1_ = aX1;
472
    gradient.y1_ = aY1;
473
    gradient.r1_ = aR1;
474
    return gradient;
475
  };
476
477
  contextPrototype.drawImage = function(image, var_args) {
0 ignored issues
show
Unused Code introduced by
The parameter var_args is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
478
    var dx, dy, dw, dh, sx, sy, sw, sh;
479
480
    // to find the original width we overide the width and height
481
    var oldRuntimeWidth = image.runtimeStyle.width;
482
    var oldRuntimeHeight = image.runtimeStyle.height;
483
    image.runtimeStyle.width = 'auto';
484
    image.runtimeStyle.height = 'auto';
485
486
    // get the original size
487
    var w = image.width;
488
    var h = image.height;
489
490
    // and remove overides
491
    image.runtimeStyle.width = oldRuntimeWidth;
492
    image.runtimeStyle.height = oldRuntimeHeight;
493
494
    if (arguments.length == 3) {
495
      dx = arguments[1];
496
      dy = arguments[2];
497
      sx = sy = 0;
498
      sw = dw = w;
499
      sh = dh = h;
500
    } else if (arguments.length == 5) {
501
      dx = arguments[1];
502
      dy = arguments[2];
503
      dw = arguments[3];
504
      dh = arguments[4];
505
      sx = sy = 0;
506
      sw = w;
507
      sh = h;
508
    } else if (arguments.length == 9) {
509
      sx = arguments[1];
510
      sy = arguments[2];
511
      sw = arguments[3];
512
      sh = arguments[4];
513
      dx = arguments[5];
514
      dy = arguments[6];
515
      dw = arguments[7];
516
      dh = arguments[8];
517
    } else {
518
      throw Error('Invalid number of arguments');
519
    }
520
521
    var d = this.getCoords_(dx, dy);
522
523
    var w2 = sw / 2;
0 ignored issues
show
Unused Code introduced by
The variable w2 seems to be never used. Consider removing it.
Loading history...
524
    var h2 = sh / 2;
0 ignored issues
show
Unused Code introduced by
The variable h2 seems to be never used. Consider removing it.
Loading history...
525
526
    var vmlStr = [];
527
528
    var W = 10;
529
    var H = 10;
530
531
    // For some reason that I've now forgotten, using divs didn't work
532
    vmlStr.push(' <g_vml_:group',
533
                ' coordsize="', Z * W, ',', Z * H, '"',
534
                ' coordorigin="0,0"' ,
535
                ' style="width:', W, 'px;height:', H, 'px;position:absolute;');
536
537
    // If filters are necessary (rotation exists), create them
538
    // filters are bog-slow, so only create them if abbsolutely necessary
539
    // The following check doesn't account for skews (which don't exist
540
    // in the canvas spec (yet) anyway.
541
542
    if (this.m_[0][0] != 1 || this.m_[0][1]) {
543
      var filter = [];
544
545
      // Note the 12/21 reversal
546
      filter.push('M11=', this.m_[0][0], ',',
547
                  'M12=', this.m_[1][0], ',',
548
                  'M21=', this.m_[0][1], ',',
549
                  'M22=', this.m_[1][1], ',',
550
                  'Dx=', mr(d.x / Z), ',',
551
                  'Dy=', mr(d.y / Z), '');
552
553
      // Bounding box calculation (need to minimize displayed area so that
554
      // filters don't waste time on unused pixels.
555
      var max = d;
556
      var c2 = this.getCoords_(dx + dw, dy);
557
      var c3 = this.getCoords_(dx, dy + dh);
558
      var c4 = this.getCoords_(dx + dw, dy + dh);
559
560
      max.x = m.max(max.x, c2.x, c3.x, c4.x);
561
      max.y = m.max(max.y, c2.y, c3.y, c4.y);
562
563
      vmlStr.push('padding:0 ', mr(max.x / Z), 'px ', mr(max.y / Z),
564
                  'px 0;filter:progid:DXImageTransform.Microsoft.Matrix(',
565
                  filter.join(''), ", sizingmethod='clip');")
566
    } else {
567
      vmlStr.push('top:', mr(d.y / Z), 'px;left:', mr(d.x / Z), 'px;');
568
    }
569
570
    vmlStr.push(' ">' ,
571
                '<g_vml_:image src="', image.src, '"',
572
                ' style="width:', Z * dw, 'px;',
573
                ' height:', Z * dh, 'px;"',
574
                ' cropleft="', sx / w, '"',
575
                ' croptop="', sy / h, '"',
576
                ' cropright="', (w - sx - sw) / w, '"',
577
                ' cropbottom="', (h - sy - sh) / h, '"',
578
                ' />',
579
                '</g_vml_:group>');
580
581
    this.element_.insertAdjacentHTML('BeforeEnd',
582
                                    vmlStr.join(''));
583
  };
584
585
  contextPrototype.stroke = function(aFill) {
586
    var lineStr = [];
587
    var lineOpen = false;
0 ignored issues
show
Unused Code introduced by
The variable lineOpen seems to be never used. Consider removing it.
Loading history...
588
    var a = processStyle(aFill ? this.fillStyle : this.strokeStyle);
589
    var color = a.color;
590
    var opacity = a.alpha * this.globalAlpha;
591
592
    var W = 10;
593
    var H = 10;
594
595
    lineStr.push('<g_vml_:shape',
596
                 ' filled="', !!aFill, '"',
597
                 ' style="position:absolute;width:', W, 'px;height:', H, 'px;"',
598
                 ' coordorigin="0 0" coordsize="', Z * W, ' ', Z * H, '"',
599
                 ' stroked="', !aFill, '"',
600
                 ' path="');
601
602
    var newSeq = false;
0 ignored issues
show
Unused Code introduced by
The variable newSeq seems to be never used. Consider removing it.
Loading history...
603
    var min = {x: null, y: null};
604
    var max = {x: null, y: null};
605
606
    for (var i = 0; i < this.currentPath_.length; i++) {
607
      var p = this.currentPath_[i];
608
      var c;
609
610
      switch (p.type) {
0 ignored issues
show
Coding Style introduced by
As per coding-style, switch statements should have a default case.
Loading history...
611
        case 'moveTo':
612
          c = p;
0 ignored issues
show
Unused Code introduced by
The variable c seems to be never used. Consider removing it.
Loading history...
613
          lineStr.push(' m ', mr(p.x), ',', mr(p.y));
614
          break;
615
        case 'lineTo':
616
          lineStr.push(' l ', mr(p.x), ',', mr(p.y));
617
          break;
618
        case 'close':
619
          lineStr.push(' x ');
620
          p = null;
621
          break;
622
        case 'bezierCurveTo':
623
          lineStr.push(' c ',
624
                       mr(p.cp1x), ',', mr(p.cp1y), ',',
625
                       mr(p.cp2x), ',', mr(p.cp2y), ',',
626
                       mr(p.x), ',', mr(p.y));
627
          break;
628
        case 'at':
629
        case 'wa':
630
          lineStr.push(' ', p.type, ' ',
631
                       mr(p.x - this.arcScaleX_ * p.radius), ',',
632
                       mr(p.y - this.arcScaleY_ * p.radius), ' ',
633
                       mr(p.x + this.arcScaleX_ * p.radius), ',',
634
                       mr(p.y + this.arcScaleY_ * p.radius), ' ',
635
                       mr(p.xStart), ',', mr(p.yStart), ' ',
636
                       mr(p.xEnd), ',', mr(p.yEnd));
637
          break;
638
      }
639
640
641
      // TODO: Following is broken for curves due to
642
      //       move to proper paths.
643
644
      // Figure out dimensions so we can do gradient fills
645
      // properly
646
      if (p) {
647
        if (min.x == null || p.x < min.x) {
648
          min.x = p.x;
649
        }
650
        if (max.x == null || p.x > max.x) {
651
          max.x = p.x;
652
        }
653
        if (min.y == null || p.y < min.y) {
654
          min.y = p.y;
655
        }
656
        if (max.y == null || p.y > max.y) {
657
          max.y = p.y;
658
        }
659
      }
660
    }
661
    lineStr.push(' ">');
662
663
    if (!aFill) {
664
      var lineWidth = this.lineScale_ * this.lineWidth;
665
666
      // VML cannot correctly render a line if the width is less than 1px.
667
      // In that case, we dilute the color to make the line look thinner.
668
      if (lineWidth < 1) {
669
        opacity *= lineWidth;
670
      }
671
672
      lineStr.push(
673
        '<g_vml_:stroke',
674
        ' opacity="', opacity, '"',
675
        ' joinstyle="', this.lineJoin, '"',
676
        ' miterlimit="', this.miterLimit, '"',
677
        ' endcap="', processLineCap(this.lineCap), '"',
678
        ' weight="', lineWidth, 'px"',
679
        ' color="', color, '" />'
680
      );
681
    } else if (typeof this.fillStyle == 'object') {
682
      var fillStyle = this.fillStyle;
683
      var angle = 0;
684
      var focus = {x: 0, y: 0};
685
686
      // additional offset
687
      var shift = 0;
688
      // scale factor for offset
689
      var expansion = 1;
690
691
      if (fillStyle.type_ == 'gradient') {
692
        var x0 = fillStyle.x0_ / this.arcScaleX_;
693
        var y0 = fillStyle.y0_ / this.arcScaleY_;
694
        var x1 = fillStyle.x1_ / this.arcScaleX_;
695
        var y1 = fillStyle.y1_ / this.arcScaleY_;
696
        var p0 = this.getCoords_(x0, y0);
697
        var p1 = this.getCoords_(x1, y1);
698
        var dx = p1.x - p0.x;
699
        var dy = p1.y - p0.y;
700
        angle = Math.atan2(dx, dy) * 180 / Math.PI;
701
702
        // The angle should be a non-negative number.
703
        if (angle < 0) {
704
          angle += 360;
705
        }
706
707
        // Very small angles produce an unexpected result because they are
708
        // converted to a scientific notation string.
709
        if (angle < 1e-6) {
710
          angle = 0;
711
        }
712
      } else {
713
        var p0 = this.getCoords_(fillStyle.x0_, fillStyle.y0_);
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable p0 already seems to be declared on line 696. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
714
        var width  = max.x - min.x;
715
        var height = max.y - min.y;
716
        focus = {
717
          x: (p0.x - min.x) / width,
718
          y: (p0.y - min.y) / height
719
        };
720
721
        width  /= this.arcScaleX_ * Z;
722
        height /= this.arcScaleY_ * Z;
723
        var dimension = m.max(width, height);
724
        shift = 2 * fillStyle.r0_ / dimension;
725
        expansion = 2 * fillStyle.r1_ / dimension - shift;
726
      }
727
728
      // We need to sort the color stops in ascending order by offset,
729
      // otherwise IE won't interpret it correctly.
730
      var stops = fillStyle.colors_;
731
      stops.sort(function(cs1, cs2) {
732
        return cs1.offset - cs2.offset;
733
      });
734
735
      var length = stops.length;
736
      var color1 = stops[0].color;
737
      var color2 = stops[length - 1].color;
738
      var opacity1 = stops[0].alpha * this.globalAlpha;
739
      var opacity2 = stops[length - 1].alpha * this.globalAlpha;
740
741
      var colors = [];
742
      for (var i = 0; i < length; i++) {
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable i already seems to be declared on line 606. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
743
        var stop = stops[i];
744
        colors.push(stop.offset * expansion + shift + ' ' + stop.color);
745
      }
746
747
      // When colors attribute is used, the meanings of opacity and o:opacity2
748
      // are reversed.
749
      lineStr.push('<g_vml_:fill type="', fillStyle.type_, '"',
750
                   ' method="none" focus="100%"',
751
                   ' color="', color1, '"',
752
                   ' color2="', color2, '"',
753
                   ' colors="', colors.join(','), '"',
754
                   ' opacity="', opacity2, '"',
755
                   ' g_o_:opacity2="', opacity1, '"',
756
                   ' angle="', angle, '"',
757
                   ' focusposition="', focus.x, ',', focus.y, '" />');
758
    } else {
759
      lineStr.push('<g_vml_:fill color="', color, '" opacity="', opacity,
760
                   '" />');
761
    }
762
763
    lineStr.push('</g_vml_:shape>');
764
765
    this.element_.insertAdjacentHTML('beforeEnd', lineStr.join(''));
766
  };
767
768
  contextPrototype.fill = function() {
769
    this.stroke(true);
770
  }
771
772
  contextPrototype.closePath = function() {
773
    this.currentPath_.push({type: 'close'});
774
  };
775
776
  /**
777
   * @private
778
   */
779
  contextPrototype.getCoords_ = function(aX, aY) {
780
    var m = this.m_;
781
    return {
782
      x: Z * (aX * m[0][0] + aY * m[1][0] + m[2][0]) - Z2,
783
      y: Z * (aX * m[0][1] + aY * m[1][1] + m[2][1]) - Z2
784
    }
785
  };
786
787
  contextPrototype.save = function() {
788
    var o = {};
789
    copyState(this, o);
790
    this.aStack_.push(o);
791
    this.mStack_.push(this.m_);
792
    this.m_ = matrixMultiply(createMatrixIdentity(), this.m_);
793
  };
794
795
  contextPrototype.restore = function() {
796
    copyState(this.aStack_.pop(), this);
797
    this.m_ = this.mStack_.pop();
798
  };
799
800
  function matrixIsFinite(m) {
801
    for (var j = 0; j < 3; j++) {
802
      for (var k = 0; k < 2; k++) {
803
        if (!isFinite(m[j][k]) || isNaN(m[j][k])) {
804
          return false;
805
        }
806
      }
807
    }
808
    return true;
809
  }
810
811
  function setM(ctx, m, updateLineScale) {
812
    if (!matrixIsFinite(m)) {
813
      return;
814
    }
815
    ctx.m_ = m;
816
817
    if (updateLineScale) {
818
      // Get the line scale.
819
      // Determinant of this.m_ means how much the area is enlarged by the
820
      // transformation. So its square root can be used as a scale factor
821
      // for width.
822
      var det = m[0][0] * m[1][1] - m[0][1] * m[1][0];
823
      ctx.lineScale_ = sqrt(abs(det));
824
    }
825
  }
826
827
  contextPrototype.translate = function(aX, aY) {
828
    var m1 = [
829
      [1,  0,  0],
830
      [0,  1,  0],
831
      [aX, aY, 1]
832
    ];
833
834
    setM(this, matrixMultiply(m1, this.m_), false);
835
  };
836
837
  contextPrototype.rotate = function(aRot) {
838
    var c = mc(aRot);
839
    var s = ms(aRot);
840
841
    var m1 = [
842
      [c,  s, 0],
843
      [-s, c, 0],
844
      [0,  0, 1]
845
    ];
846
847
    setM(this, matrixMultiply(m1, this.m_), false);
848
  };
849
850
  contextPrototype.scale = function(aX, aY) {
851
    this.arcScaleX_ *= aX;
852
    this.arcScaleY_ *= aY;
853
    var m1 = [
854
      [aX, 0,  0],
855
      [0,  aY, 0],
856
      [0,  0,  1]
857
    ];
858
859
    setM(this, matrixMultiply(m1, this.m_), true);
860
  };
861
862
  contextPrototype.transform = function(m11, m12, m21, m22, dx, dy) {
863
    var m1 = [
864
      [m11, m12, 0],
865
      [m21, m22, 0],
866
      [dx,  dy,  1]
867
    ];
868
869
    setM(this, matrixMultiply(m1, this.m_), true);
870
  };
871
872
  contextPrototype.setTransform = function(m11, m12, m21, m22, dx, dy) {
873
    var m = [
874
      [m11, m12, 0],
875
      [m21, m22, 0],
876
      [dx,  dy,  1]
877
    ];
878
879
    setM(this, m, true);
880
  };
881
882
  /******** STUBS ********/
883
  contextPrototype.clip = function() {
884
    // TODO: Implement
885
  };
886
887
  contextPrototype.arcTo = function() {
888
    // TODO: Implement
889
  };
890
891
  contextPrototype.createPattern = function() {
892
    return new CanvasPattern_;
893
  };
894
895
  // Gradient / Pattern Stubs
896
  function CanvasGradient_(aType) {
897
    this.type_ = aType;
898
    this.x0_ = 0;
899
    this.y0_ = 0;
900
    this.r0_ = 0;
901
    this.x1_ = 0;
902
    this.y1_ = 0;
903
    this.r1_ = 0;
904
    this.colors_ = [];
905
  }
906
907
  CanvasGradient_.prototype.addColorStop = function(aOffset, aColor) {
908
    aColor = processStyle(aColor);
909
    this.colors_.push({offset: aOffset,
910
                       color: aColor.color,
911
                       alpha: aColor.alpha});
912
  };
913
914
  function CanvasPattern_() {}
915
916
  // set up externs
917
  G_vmlCanvasManager = G_vmlCanvasManager_;
0 ignored issues
show
Bug introduced by
The variable G_vmlCanvasManager seems to be never declared. Assigning variables without defining them first makes them global. If this was intended, consider making it explicit like using window.G_vmlCanvasManager.
Loading history...
918
  CanvasRenderingContext2D = CanvasRenderingContext2D_;
0 ignored issues
show
Bug introduced by
The variable CanvasRenderingContext2D seems to be never declared. Assigning variables without defining them first makes them global. If this was intended, consider making it explicit like using window.CanvasRenderingContext2D.
Loading history...
919
  CanvasGradient = CanvasGradient_;
0 ignored issues
show
Bug introduced by
The variable CanvasGradient seems to be never declared. Assigning variables without defining them first makes them global. If this was intended, consider making it explicit like using window.CanvasGradient.
Loading history...
920
  CanvasPattern = CanvasPattern_;
0 ignored issues
show
Bug introduced by
The variable CanvasPattern seems to be never declared. Assigning variables without defining them first makes them global. If this was intended, consider making it explicit like using window.CanvasPattern.
Loading history...
921
922
})();
923
924
} // if
925