PageSourceSearch

https://gisersqdai.top/js/tagcanvas.js

js gisersqdai.top collected 2026-10-05 22:12:05 UTC 65,047 bytes, 2,243 lines download raw bytes

1/**
2 * Copyright (C) 2010-2015 Graham Breach
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 * GNU Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 */
17/**
18 * TagCanvas 2.9
19 * For more information, please contact <[email protected]>
20 */
21(function(){
22"use strict";
23var i, j, abs = Math.abs, sin = Math.sin, cos = Math.cos, max = Math.max,
24  min = Math.min, ceil = Math.ceil, sqrt = Math.sqrt, pow = Math.pow,
25  hexlookup3 = {}, hexlookup2 = {}, hexlookup1 = {
26  0:"0,",   1:"17,",  2:"34,",  3:"51,",  4:"68,",  5:"85,",
27  6:"102,", 7:"119,", 8:"136,", 9:"153,", a:"170,", A:"170,",
28  b:"187,", B:"187,", c:"204,", C:"204,", d:"221,", D:"221,",
29  e:"238,", E:"238,", f:"255,", F:"255,"
vendor: 7,062 bytes, lines 29-274
29
30  }, Oproto, Tproto, TCproto, Mproto, Vproto, TSproto, TCVproto,
31  doc = document, ocanvas, handlers = {};
32for(i = 0; i < 256; ++i) {
33  j = i.toString(16);
34  if(i < 16)
35    j = '0' + j;
36  hexlookup2[j] = hexlookup2[j.toUpperCase()] = i.toString() + ',';
37}
38function Defined(d) {
39  return typeof d != 'undefined';
40}
41function IsObject(o) {
42  return typeof o == 'object' && o != null;
43}
44function Clamp(v, mn, mx) {
45  return isNaN(v) ? mx : min(mx, max(mn, v));
46}
47function Nop() {
48  return false;
49}
50function TimeNow() {
51  return new Date().valueOf();
52}
53function SortList(l, f) {
54  var nl = [], tl = l.length, i;
55  for(i = 0; i < tl; ++i)
56    nl.push(l[i]);
57  nl.sort(f);
58  return nl;
59}
60function Shuffle(a) {
61  var i = a.length-1, t, p;
62  while(i) {
63    p = ~~(Math.random()*i);
64    t = a[i];
65    a[i] = a[p];
66    a[p] = t;
67    --i;
68  }
69}
70function Vector(x, y, z) {
71  this.x = x;
72  this.y = y;
73  this.z = z;
74}
75Vproto = Vector.prototype;
76Vproto.length = function() {
77  return sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
78};
79Vproto.dot = function(v) {
80  return this.x * v.x + this.y * v.y + this.z * v.z;
81};
82Vproto.cross = function(v) {
83  var x = this.y * v.z - this.z * v.y,
84    y = this.z * v.x - this.x * v.z,
85    z = this.x * v.y - this.y * v.x;
86  return new Vector(x, y, z);
87};
88Vproto.angle = function(v) {
89  var dot = this.dot(v), ac;
90  if(dot == 0)
91    return Math.PI / 2.0;
92  ac = dot / (this.length() * v.length());
93  if(ac >= 1)
94    return 0;
95  if(ac <= -1)
96    return Math.PI;
97  return Math.acos(ac);
98};
99Vproto.unit = function() {
100  var l = this.length();
101  return new Vector(this.x / l, this.y / l, this.z / l);
102};
103function MakeVector(lg, lt) {
104  lt = lt * Math.PI / 180;
105  lg = lg * Math.PI / 180;
106  var x = sin(lg) * cos(lt), y = -sin(lt), z = -cos(lg) * cos(lt);
107  return new Vector(x, y, z);
108}
109function Matrix(a) {
110  this[1] = {1: a[0],  2: a[1],  3: a[2]};
111  this[2] = {1: a[3],  2: a[4],  3: a[5]};
112  this[3] = {1: a[6],  2: a[7],  3: a[8]};
113}
114Mproto = Matrix.prototype;
115Matrix.Identity = function() {
116  return new Matrix([1,0,0, 0,1,0, 0,0,1]);
117};
118Matrix.Rotation = function(angle, u) {
119  var sina = sin(angle), cosa = cos(angle), mcos = 1 - cosa;
120  return new Matrix([
121    cosa + pow(u.x, 2) * mcos, u.x * u.y * mcos - u.z * sina, u.x * u.z * mcos + u.y * sina,
122    u.y * u.x * mcos + u.z * sina, cosa + pow(u.y, 2) * mcos, u.y * u.z * mcos - u.x * sina,
123    u.z * u.x * mcos - u.y * sina, u.z * u.y * mcos + u.x * sina, cosa + pow(u.z, 2) * mcos
124  ]);
125}
126Mproto.mul = function(m) {
127  var a = [], i, j, mmatrix = (m.xform ? 1 : 0);
128  for(i = 1; i <= 3; ++i)
129    for(j = 1; j <= 3; ++j) {
130      if(mmatrix)
131        a.push(this[i][1] * m[1][j] +
132          this[i][2] * m[2][j] +
133          this[i][3] * m[3][j]);
134      else
135        a.push(this[i][j] * m);
136    }
137  return new Matrix(a);
138};
139Mproto.xform = function(p) {
140  var a = {}, x = p.x, y = p.y, z = p.z;
141  a.x = x * this[1][1] + y * this[2][1] + z * this[3][1];
142  a.y = x * this[1][2] + y * this[2][2] + z * this[3][2];
143  a.z = x * this[1][3] + y * this[2][3] + z * this[3][3];
144  return a;
145};
146function PointsOnSphere(n,xr,yr,zr,magic) {
147  var i, y, r, phi, pts = [], off = 2/n, inc;
148  inc = Math.PI * (3 - sqrt(5) + (parseFloat(magic) ? parseFloat(magic) : 0));
149  for(i = 0; i < n; ++i) {
150    y = i * off - 1 + (off / 2);
151    r = sqrt(1 - y*y);
152    phi = i * inc;
153    pts.push([cos(phi) * r * xr, y * yr, sin(phi) * r * zr]);
154  }
155  return pts;
156}
157function Cylinder(n,o,xr,yr,zr,magic) {
158  var phi, pts = [], off = 2/n, inc, i, j, k, l;
159  inc = Math.PI * (3 - sqrt(5) + (parseFloat(magic) ? parseFloat(magic) : 0));
160  for(i = 0; i < n; ++i) {
161    j = i * off - 1 + (off / 2);
162    phi = i * inc;
163    k = cos(phi);
164    l = sin(phi);
165    pts.push(o ? [j * xr, k * yr, l * zr] : [k * xr, j * yr, l * zr]);
166  }
167  return pts;
168}
169function Ring(o, n, xr, yr, zr, j) {
170  var phi, pts = [], inc = Math.PI * 2 / n, i, k, l;
171  for(i = 0; i < n; ++i) {
172    phi = i * inc;
173    k = cos(phi);
174    l = sin(phi);
175    pts.push(o ? [j * xr, k * yr, l * zr] : [k * xr, j * yr, l * zr]);
176  }
177  return pts;
178}
179function PointsOnCylinderV(n,xr,yr,zr,m) { return Cylinder(n, 0, xr, yr, zr, m) }
180function PointsOnCylinderH(n,xr,yr,zr,m) { return Cylinder(n, 1, xr, yr, zr, m) }
181function PointsOnRingV(n, xr, yr, zr, offset) {
182  offset = isNaN(offset) ? 0 : offset * 1;
183  return Ring(0, n, xr, yr, zr, offset);
184}
185function PointsOnRingH(n, xr, yr, zr, offset) {
186  offset = isNaN(offset) ? 0 : offset * 1;
187  return Ring(1, n, xr, yr, zr, offset);
188}
189function CentreImage(t) {
190  var i = new Image;
191  i.onload = function() {
192    var dx = i.width / 2, dy = i.height / 2;
193    t.centreFunc = function(c, w, h, cx, cy) {
194      c.setTransform(1, 0, 0, 1, 0, 0);
195      c.globalAlpha = 1;
196      c.drawImage(i, cx - dx, cy - dy);
197    };
198  };
199  i.src = t.centreImage;
200}
201function SetAlpha(c,a) {
202  var d = c, p1, p2, ae = (a*1).toPrecision(3) + ')';
203  if(c[0] === '#') {
204    if(!hexlookup3[c])
205      if(c.length === 4)
206        hexlookup3[c] = 'rgba(' + hexlookup1[c[1]] + hexlookup1[c[2]] + hexlookup1[c[3]];
207      else
208        hexlookup3[c] = 'rgba(' + hexlookup2[c.substr(1,2)] + hexlookup2[c.substr(3,2)] + hexlookup2[c.substr(5,2)];
209    d = hexlookup3[c] + ae;
210  } else if(c.substr(0,4) === 'rgb(' || c.substr(0,4) === 'hsl(') {
211    d = (c.replace('(','a(').replace(')', ',' + ae));
212  } else if(c.substr(0,5) === 'rgba(' || c.substr(0,5) === 'hsla(') {
213    p1 = c.lastIndexOf(',') + 1, p2 = c.indexOf(')');
214    a *= parseFloat(c.substring(p1,p2));
215    d = c.substr(0,p1) + a.toPrecision(3) + ')';
216  }
217  return d;
218}
219function NewCanvas(w,h) {
220  // if using excanvas, give up now
221  if(window.G_vmlCanvasManager)
222    return null;
223  var c = doc.createElement('canvas');
224  c.width = w;
225  c.height = h;
226  return c;
227}
228// I think all browsers pass this test now...
229function ShadowAlphaBroken() {
230  var cv = NewCanvas(3,3), c, i;
231  if(!cv)
232    return false;
233  c = cv.getContext('2d');
234  c.strokeStyle = '#000';
235  c.shadowColor = '#fff';
236  c.shadowBlur = 3;
237  c.globalAlpha = 0;
238  c.strokeRect(2,2,2,2);
239  c.globalAlpha = 1;
240  i = c.getImageData(2,2,1,1);
241  cv = null;
242  return (i.data[0] > 0);
243}
244function SetGradient(c, l, o, g) {
245  var gd = c.createLinearGradient(0, 0, l, 0), i;
246  for(i in g)
247    gd.addColorStop(1 - i, g[i]);
248  c.fillStyle = gd;
249  c.fillRect(0, o, l, 1);
250}
251function FindGradientColour(tc, p, r) {
252  var l = 1024, h = 1, gl = tc.weightGradient, cv, c, i, d;
253  if(tc.gCanvas) {
254    c = tc.gCanvas.getContext('2d');
255    h = tc.gCanvas.height;
256  } else {
257    if(IsObject(gl[0]))
258      h = gl.length;
259    else
260      gl = [gl];
261    tc.gCanvas = cv = NewCanvas(l, h);
262    if(!cv)
263      return null;
264    c = cv.getContext('2d');
265    for(i = 0; i < h; ++i)
266      SetGradient(c, l, i, gl[i]);
267  }
268  r = max(min(r || 0, h - 1), 0);
269  d = c.getImageData(~~((l - 1) * p), r, 1, 1).data;
270  return 'rgba(' + d[0] + ',' + d[1] + ',' + d[2] + ',' + (d[3]/255) + ')';
271}
272function TextSet(ctxt, font, colour, strings, padx, pady, shadowColour,
273  shadowBlur, shadowOffsets, maxWidth, widths, align) {
274  var xo = padx + (shadowBlur || 0) +
vendor: 45,234 bytes, lines 274-1808
274
275    (shadowOffsets.length && shadowOffsets[0] < 0 ? abs(shadowOffsets[0]) : 0),
276    yo = pady + (shadowBlur || 0) +
277    (shadowOffsets.length && shadowOffsets[1] < 0 ? abs(shadowOffsets[1]) : 0), i, xc;
278  ctxt.font = font;
279  ctxt.textBaseline = 'top';
280  ctxt.fillStyle = colour;
281  shadowColour && (ctxt.shadowColor = shadowColour);
282  shadowBlur && (ctxt.shadowBlur = shadowBlur);
283  shadowOffsets.length && (ctxt.shadowOffsetX = shadowOffsets[0],
284    ctxt.shadowOffsetY = shadowOffsets[1]);
285  for(i = 0; i < strings.length; ++i) {
286    xc = 0;
287    if(widths) {
288      if('right' == align) {
289        xc = maxWidth - widths[i];
290      } else if('centre' == align) {
291        xc = (maxWidth - widths[i]) / 2;
292      }
293    }
294    ctxt.fillText(strings[i], xo + xc, yo);
295    yo += parseInt(font);
296  }
297}
298function RRect(c, x, y, w, h, r, s) {
299  if(r) {
300    c.beginPath();
301    c.moveTo(x, y + h - r);
302    c.arcTo(x, y, x + r, y, r);
303    c.arcTo(x + w, y, x + w, y + r, r);
304    c.arcTo(x + w, y + h, x + w - r, y + h, r);
305    c.arcTo(x, y + h, x, y + h - r, r);
306    c.closePath();
307    c[s ? 'stroke' : 'fill']();
308  } else {
309    c[s ? 'strokeRect' : 'fillRect'](x, y, w, h);
310  }
311}
312function TextCanvas(strings, font, w, h, maxWidth, stringWidths, align, valign,
313  scale) {
314  this.strings = strings;
315  this.font = font;
316  this.width = w;
317  this.height = h;
318  this.maxWidth = maxWidth;
319  this.stringWidths = stringWidths;
320  this.align = align;
321  this.valign = valign;
322  this.scale = scale;
323}
324TCVproto = TextCanvas.prototype;
325TCVproto.SetImage = function(image, w, h, position, padding, align, valign,
326  scale) {
327  this.image = image;
328  this.iwidth = w * this.scale;
329  this.iheight = h * this.scale;
330  this.ipos = position;
331  this.ipad = padding * this.scale;
332  this.iscale = scale;
333  this.ialign = align;
334  this.ivalign = valign;
335};
336TCVproto.Align = function(size, space, a) {
337  var pos = 0;
338  if(a == 'right' || a == 'bottom')
339    pos = space - size;
340  else if(a != 'left' && a != 'top')
341    pos = (space - size) / 2;
342  return pos;
343};
344TCVproto.Create = function(colour, bgColour, bgOutline, bgOutlineThickness,
345  shadowColour, shadowBlur, shadowOffsets, padding, radius) {
346  var cv, cw, ch, c, x1, x2, y1, y2, offx, offy, ix, iy, iw, ih, rr,
347    sox = abs(shadowOffsets[0]), soy = abs(shadowOffsets[1]), shadowcv, shadowc;
348  padding = max(padding, sox + shadowBlur, soy + shadowBlur);
349  x1 = 2 * (padding + bgOutlineThickness);
350  y1 = 2 * (padding + bgOutlineThickness);
351  cw = this.width + x1;
352  ch = this.height + y1;
353  offx = offy = padding + bgOutlineThickness;
354
355  if(this.image) {
356    ix = iy = padding + bgOutlineThickness;
357    iw = this.iwidth;
358    ih = this.iheight;
359    if(this.ipos == 'top' || this.ipos == 'bottom') {
360      if(iw < this.width)
361        ix += this.Align(iw, this.width, this.ialign);
362      else
363        offx += this.Align(this.width, iw, this.align);
364      if(this.ipos == 'top')
365        offy += ih + this.ipad;
366      else
367        iy += this.height + this.ipad;
368      cw = max(cw, iw + x1);
369      ch += ih + this.ipad;
370    } else {
371      if(ih < this.height)
372        iy += this.Align(ih, this.height, this.ivalign);
373      else
374        offy += this.Align(this.height, ih, this.valign);
375      if(this.ipos == 'right')
376        ix += this.width + this.ipad;
377      else
378        offx += iw + this.ipad;
379      cw += iw + this.ipad;
380      ch = max(ch, ih + y1);
381    }
382  }
383
384  cv = NewCanvas(cw, ch);
385  if(!cv)
386    return null;
387  x1 = y1 = bgOutlineThickness / 2;
388  x2 = cw - bgOutlineThickness;
389  y2 = ch - bgOutlineThickness;
390  rr = min(radius, x2 / 2, y2 / 2);
391  c = cv.getContext('2d');
392  if(bgColour) {
393    c.fillStyle = bgColour;
394    RRect(c, x1, y1, x2, y2, rr);
395  }
396  if(bgOutlineThickness) {
397    c.strokeStyle = bgOutline;
398    c.lineWidth = bgOutlineThickness;
399    RRect(c, x1, y1, x2, y2, rr, true);
400  }
401  if(shadowBlur || sox || soy) {
402    // use a transparent canvas to draw on
403    shadowcv = NewCanvas(cw, ch);
404    if(shadowcv) {
405      shadowc = c;
406      c = shadowcv.getContext('2d');
407    }
408  }
409
410  // don't use TextSet shadow support because it adds space for shadow
411  TextSet(c, this.font, colour, this.strings, offx, offy, 0, 0, [],
412    this.maxWidth, this.stringWidths, this.align);
413
414  if(this.image)
415    c.drawImage(this.image, ix, iy, iw, ih);
416
417  if(shadowc) {
418    // draw the text and image with the added shadow
419    c = shadowc;
420    shadowColour && (c.shadowColor = shadowColour);
421    shadowBlur && (c.shadowBlur = shadowBlur);
422    c.shadowOffsetX = shadowOffsets[0];
423    c.shadowOffsetY = shadowOffsets[1];
424    c.drawImage(shadowcv, 0, 0);
425  }
426  return cv;
427};
428function ExpandImage(i, w, h) {
429  var cv = NewCanvas(w, h), c;
430  if(!cv)
431    return null;
432  c = cv.getContext('2d');
433  c.drawImage(i, (w - i.width) / 2, (h - i.height) / 2);
434  return cv;
435}
436function ScaleImage(i, w, h) {
437  var cv = NewCanvas(w, h), c;
438  if(!cv)
439    return null;
440  c = cv.getContext('2d');
441  c.drawImage(i, 0, 0, w, h);
442  return cv;
443}
444function AddBackgroundToImage(i, w, h, scale, colour, othickness, ocolour,
445  padding, radius, ofill) {
446  var cw = w + ((2 * padding) + othickness) * scale,
447    ch = h + ((2 * padding) + othickness) * scale,
448    cv = NewCanvas(cw, ch), c, x1, y1, x2, y2, ocanvas, cc, rr;
449  if(!cv)
450    return null;
451  othickness *= scale;
452  radius *= scale;
453  x1 = y1 = othickness / 2;
454  x2 = cw - othickness;
455  y2 = ch - othickness;
456  padding = (padding * scale) + x1; // add space for outline
457  c = cv.getContext('2d');
458  rr = min(radius, x2 / 2, y2 / 2);
459  if(colour) {
460    c.fillStyle = colour;
461    RRect(c, x1, y1, x2, y2, rr);
462  }
463  if(othickness) {
464    c.strokeStyle = ocolour;
465    c.lineWidth = othickness;
466    RRect(c, x1, y1, x2, y2, rr, true);
467  }
468
469  if(ofill) {
470    // use compositing to colour in the image and border
471    ocanvas = NewCanvas(cw, ch);
472    cc = ocanvas.getContext('2d');
473    cc.drawImage(i, padding, padding, w, h);
474    cc.globalCompositeOperation = 'source-in';
475    cc.fillStyle = ocolour;
476    cc.fillRect(0, 0, cw, ch);
477    cc.globalCompositeOperation = 'destination-over';
478    cc.drawImage(cv, 0, 0);
479    cc.globalCompositeOperation = 'source-over';
480    c.drawImage(ocanvas, 0, 0);
481  } else {
482    c.drawImage(i, padding, padding, i.width, i.height);
483  }
484  return {image: cv, width: cw / scale, height: ch / scale};
485}
486/**
487 * Rounds off the corners of an image
488 */
489function RoundImage(i, r, iw, ih, s) {
490  var cv, c, r1 = parseFloat(r), l = max(iw, ih);
491  cv = NewCanvas(iw, ih);
492  if(!cv)
493    return null;
494  if(r.indexOf('%') > 0)
495    r1 = l * r1 / 100;
496  else
497    r1 = r1 * s;
498  c = cv.getContext('2d');
499  c.globalCompositeOperation = 'source-over';
500  c.fillStyle = '#fff';
501  if(r1 >= l/2) {
502    r1 = min(iw,ih) / 2;
503    c.beginPath();
504    c.moveTo(iw/2,ih/2);
505    c.arc(iw/2,ih/2,r1,0,2*Math.PI,false);
506    c.fill();
507    c.closePath();
508  } else {
509    r1 = min(iw/2,ih/2,r1);
510    RRect(c, 0, 0, iw, ih, r1, true);
511    c.fill();
512  }
513  c.globalCompositeOperation = 'source-in';
514  c.drawImage(i, 0, 0, iw, ih);
515  return cv;
516}
517/**
518 * Creates a new canvas containing the image and its shadow
519 * Returns an object containing the image and its dimensions at z=0
520 */
521function AddShadowToImage(i, w, h, scale, sc, sb, so) {
522  var sw = abs(so[0]), sh = abs(so[1]),
523    cw = w + (sw > sb ? sw + sb : sb * 2) * scale,
524    ch = h + (sh > sb ? sh + sb : sb * 2) * scale,
525    xo = scale * ((sb || 0) + (so[0] < 0 ? sw : 0)),
526    yo = scale * ((sb || 0) + (so[1] < 0 ? sh : 0)), cv, c;
527  cv = NewCanvas(cw, ch);
528  if(!cv)
529    return null;
530  c = cv.getContext('2d');
531  sc && (c.shadowColor = sc);
532  sb && (c.shadowBlur = sb * scale);
533  so && (c.shadowOffsetX = so[0] * scale, c.shadowOffsetY = so[1] * scale);
534  c.drawImage(i, xo, yo, w, h);
535  return {image: cv, width: cw / scale, height: ch / scale};
536}
537function FindTextBoundingBox(s,f,ht) {
538  var w = parseInt(s.toString().length * ht), h = parseInt(ht * 2 * s.length),
539    cv = NewCanvas(w,h), c, idata, w1, h1, x, y, i, ex;
540  if(!cv)
541    return null;
542  c = cv.getContext('2d');
543  c.fillStyle = '#000';
544  c.fillRect(0,0,w,h);
545  TextSet(c,ht + 'px ' + f,'#fff',s,0,0,0,0,[],'centre')
546
547  idata = c.getImageData(0,0,w,h);
548  w1 = idata.width; h1 = idata.height;
549  ex = {
550    min: { x: w1, y: h1 },
551    max: { x: -1, y: -1 }
552  };
553  for(y = 0; y < h1; ++y) {
554    for(x = 0; x < w1; ++x) {
555      i = (y * w1 + x) * 4;
556      if(idata.data[i+1] > 0) {
557        if(x < ex.min.x) ex.min.x = x;
558        if(x > ex.max.x) ex.max.x = x;
559        if(y < ex.min.y) ex.min.y = y;
560        if(y > ex.max.y) ex.max.y = y;
561      }
562    }
563  }
564  // device pixels might not be css pixels
565  if(w1 != w) {
566    ex.min.x *= (w / w1);
567    ex.max.x *= (w / w1);
568  }
569  if(h1 != h) {
570    ex.min.y *= (w / h1);
571    ex.max.y *= (w / h1);
572  }
573
574  cv = null;
575  return ex;
576}
577function FixFont(f) {
578  return "'" + f.replace(/(\'|\")/g,'').replace(/\s*,\s*/g, "', '") + "'";
579}
580function AddHandler(h,f,e) {
581  e = e || doc;
582  if(e.addEventListener)
583    e.addEventListener(h,f,false);
584  else
585    e.attachEvent('on' + h, f);
586}
587function RemoveHandler(h,f,e) {
588  e = e || doc;
589  if(e.removeEventListener)
590    e.removeEventListener(h, f);
591  else
592    e.detachEvent('on' + h, f);
593}
594function AddImage(i, o, t, tc) {
595  var s = tc.imageScale, mscale, ic, bc, oc, iw, ih;
596  // image not loaded, wait for image onload
597  if(!o.complete)
598    return AddHandler('load',function() { AddImage(i,o,t,tc); }, o);
599  if(!i.complete)
600    return AddHandler('load',function() { AddImage(i,o,t,tc); }, i);
601
602  // Yes, this does look like nonsense, but it makes sure that both the
603  // width and height are actually set and not just calculated. This is
604  // required to keep proportional sizes when the images are hidden, so
605  // the images can be used again for another cloud.
606  o.width = o.width;
607  o.height = o.height;
608
609  if(s) {
610    i.width = o.width * s;
611    i.height = o.height * s;
612  }
613  // the standard width of the image, with imageScale applied
614  t.iw = i.width;
615  t.ih = i.height;
616  if(tc.txtOpt) {
617    ic = i;
618    mscale = tc.zoomMax * tc.txtScale;
619    iw = t.iw * mscale;
620    ih = t.ih * mscale;
621    if(iw < o.naturalWidth || ih < o.naturalHeight) {
622      ic = ScaleImage(i, iw, ih);
623      if(ic)
624        t.fimage = ic;
625    } else {
626      iw = t.iw;
627      ih = t.ih;
628      mscale = 1;
629    }
630    if(parseFloat(tc.imageRadius))
631      t.image = t.fimage = i = RoundImage(t.image, tc.imageRadius, iw, ih, mscale);
632    if(!t.HasText()) {
633      if(tc.shadow) {
634        ic = AddShadowToImage(t.image, iw, ih, mscale, tc.shadow, tc.shadowBlur,
635          tc.shadowOffset);
636        if(ic) {
637          t.fimage = ic.image;
638          t.w = ic.width;
639          t.h = ic.height;
640        }
641      }
642      if(tc.bgColour || tc.bgOutlineThickness) {
643        bc = tc.bgColour == 'tag' ? GetProperty(t.a, 'background-color') :
644          tc.bgColour;
645        oc = tc.bgOutline == 'tag' ? GetProperty(t.a, 'color') :
646          (tc.bgOutline || tc.textColour);
647        iw = t.fimage.width;
648        ih = t.fimage.height;
649        if(tc.outlineMethod == 'colour') {
650          // create the outline version first, using the current image state
651          ic = AddBackgroundToImage(t.fimage, iw, ih, mscale, bc,
652            tc.bgOutlineThickness, t.outline.colour, tc.padding, tc.bgRadius, 1);
653          if(ic)
654            t.oimage = ic.image;
655        }
656        ic = AddBackgroundToImage(t.fimage, iw, ih, mscale, bc,
657          tc.bgOutlineThickness, oc, tc.padding, tc.bgRadius);
658        if(ic) {
659          t.fimage = ic.image;
660          t.w = ic.width;
661          t.h = ic.height;
662        }
663      }
664      if(tc.outlineMethod == 'size') {
665        if(tc.outlineIncrease > 0) {
666          t.iw += 2 * tc.outlineIncrease;
667          t.ih += 2 * tc.outlineIncrease;
668          iw = mscale * t.iw;
669          ih = mscale * t.ih;
670          ic = ScaleImage(t.fimage, iw, ih);
671          t.oimage = ic;
672          t.fimage = ExpandImage(t.fimage, t.oimage.width, t.oimage.height);
673        } else {
674          iw = mscale * (t.iw + (2 * tc.outlineIncrease));
675          ih = mscale * (t.ih + (2 * tc.outlineIncrease));
676          ic = ScaleImage(t.fimage, iw, ih);
677          t.oimage = ExpandImage(ic, t.fimage.width, t.fimage.height);
678        }
679      }
680    }
681  }
682  t.Init();
683}
684function GetProperty(e,p) {
685  var dv = doc.defaultView, pc = p.replace(/\-([a-z])/g,function(a){return a.charAt(1).toUpperCase()});
686  return (dv && dv.getComputedStyle && dv.getComputedStyle(e,null).getPropertyValue(p)) ||
687    (e.currentStyle && e.currentStyle[pc]);
688}
689function FindWeight(a, wFrom, tHeight) {
690  var w = 1, p;
691  if(wFrom) {
692    w = 1 * (a.getAttribute(wFrom) || tHeight);
693  } else if(p = GetProperty(a,'font-size')) {
694    w = (p.indexOf('px') > -1 && p.replace('px','') * 1) ||
695      (p.indexOf('pt') > -1 && p.replace('pt','') * 1.25) ||
696      p * 3.3;
697  }
698  return w;
699}
700function EventToCanvasId(e) {
701  return e.target && Defined(e.target.id) ? e.target.id :
702    e.srcElement.parentNode.id;
703}
704function EventXY(e, c) {
705  var xy, p, xmul = parseInt(GetProperty(c, 'width')) / c.width,
706    ymul = parseInt(GetProperty(c, 'height')) / c.height;
707  if(Defined(e.offsetX)) {
708    xy = {x: e.offsetX, y: e.offsetY};
709  } else {
710    p = AbsPos(c.id);
711    if(Defined(e.changedTouches))
712      e = e.changedTouches[0];
713    if(e.pageX)
714      xy = {x: e.pageX - p.x, y: e.pageY - p.y};
715  }
716  if(xy && xmul && ymul) {
717    xy.x /= xmul;
718    xy.y /= ymul;
719  }
720  return xy;
721}
722function MouseOut(e) {
723  var cv = e.target || e.fromElement.parentNode, tc = TagCanvas.tc[cv.id];
724  if(tc) {
725   tc.mx = tc.my = -1;
726   tc.UnFreeze();
727   tc.EndDrag();
728  }
729}
730function MouseMove(e) {
731  var i, t = TagCanvas, tc, p, tg = EventToCanvasId(e);
732  for(i in t.tc) {
733    tc = t.tc[i];
734    if(tc.tttimer) {
735      clearTimeout(tc.tttimer);
736      tc.tttimer = null;
737    }
738  }
739  if(tg && t.tc[tg]) {
740    tc = t.tc[tg];
741    if(p = EventXY(e, tc.canvas)) {
742      tc.mx = p.x;
743      tc.my = p.y;
744      tc.Drag(e, p);
745    }
746    tc.drawn = 0;
747  }
748}
749function MouseDown(e) {
750  var t = TagCanvas, cb = doc.addEventListener ? 0 : 1,
751    tg = EventToCanvasId(e);
752  if(tg && e.button == cb && t.tc[tg]) {
753    t.tc[tg].BeginDrag(e);
754  }
755}
756function MouseUp(e) {
757  var t = TagCanvas, cb = doc.addEventListener ? 0 : 1,
758    tg = EventToCanvasId(e), tc;
759  if(tg && e.button == cb && t.tc[tg]) {
760    tc = t.tc[tg];
761    MouseMove(e);
762    if(!tc.EndDrag() && !tc.touchState)
763      tc.Clicked(e);
764  }
765}
766function TouchDown(e) {
767  var tg = EventToCanvasId(e), tc = (tg && TagCanvas.tc[tg]), p;
768  if(tc && e.changedTouches) {
769    if(e.touches.length == 1 && tc.touchState == 0) {
770      tc.touchState = 1;
771      tc.BeginDrag(e);
772      if(p = EventXY(e, tc.canvas)) {
773        tc.mx = p.x;
774        tc.my = p.y;
775        tc.drawn = 0;
776      }
777    } else if(e.targetTouches.length == 2 && tc.pinchZoom) {
778      tc.touchState = 3;
779      tc.EndDrag();
780      tc.BeginPinch(e);
781    } else {
782      tc.EndDrag();
783      tc.EndPinch();
784      tc.touchState = 0;
785    }
786  }
787}
788function TouchUp(e) {
789  var tg = EventToCanvasId(e), tc = (tg && TagCanvas.tc[tg]);
790  if(tc && e.changedTouches) {
791    switch(tc.touchState) {
792    case 1:
793      tc.Draw();
794      tc.Clicked();
795      break;
796    case 2:
797      tc.EndDrag();
798      break;
799    case 3:
800      tc.EndPinch();
801    }
802    tc.touchState = 0;
803  }
804}
805function TouchMove(e) {
806  var i, t = TagCanvas, tc, p, tg = EventToCanvasId(e);
807  for(i in t.tc) {
808    tc = t.tc[i];
809    if(tc.tttimer) {
810      clearTimeout(tc.tttimer);
811      tc.tttimer = null;
812    }
813  }
814  tc = (tg && t.tc[tg]);
815  if(tc && e.changedTouches && tc.touchState) {
816    switch(tc.touchState) {
817    case 1:
818    case 2:
819      if(p = EventXY(e, tc.canvas)) {
820        tc.mx = p.x;
821        tc.my = p.y;
822        if(tc.Drag(e, p))
823          tc.touchState = 2;
824      }
825      break;
826    case 3:
827      tc.Pinch(e);
828    }
829    tc.drawn = 0;
830  }
831}
832function MouseWheel(e) {
833  var t = TagCanvas, tg = EventToCanvasId(e);
834  if(tg && t.tc[tg]) {
835    e.cancelBubble = true;
836    e.returnValue = false;
837    e.preventDefault && e.preventDefault();
838    t.tc[tg].Wheel((e.wheelDelta || e.detail) > 0);
839  }
840}
841function Scroll(e) {
842  var i, t = TagCanvas;
843  clearTimeout(t.scrollTimer);
844  for(i in t.tc) {
845    t.tc[i].Pause();
846  }
847  t.scrollTimer = setTimeout(function() {
848    var i, t = TagCanvas;
849    for(i in t.tc) {
850      t.tc[i].Resume();
851    }
852  }, t.scrollPause);
853}
854function DrawCanvas() {
855  DrawCanvasRAF(TimeNow());
856}
857function DrawCanvasRAF(t) {
858  var tc = TagCanvas.tc, i;
859  TagCanvas.NextFrame(TagCanvas.interval);
860  t = t || TimeNow();
861  for(i in tc)
862    tc[i].Draw(t);
863}
864function AbsPos(id) {
865  var e = doc.getElementById(id), r = e.getBoundingClientRect(),
866    dd = doc.documentElement, b = doc.body, w = window,
867    xs = w.pageXOffset || dd.scrollLeft,
868    ys = w.pageYOffset || dd.scrollTop,
869    xo = dd.clientLeft || b.clientLeft,
870    yo = dd.clientTop || b.clientTop;
871  return { x: r.left + xs - xo, y: r.top + ys - yo };
872}
873function Project(tc,p1,sx,sy) {
874  var m = tc.radius * tc.z1 / (tc.z1 + tc.z2 + p1.z);
875  return {
876    x: p1.x * m * sx,
877    y: p1.y * m * sy,
878    z: p1.z,
879    w: (tc.z1 - p1.z) / tc.z2
880  };
881}
882/**
883 * @constructor
884 * for recursively splitting tag contents on <br> tags
885 */
886function TextSplitter(e) {
887  this.e = e;
888  this.br = 0;
889  this.line = [];
890  this.text = [];
891  this.original = e.innerText || e.textContent;
892}
893TSproto = TextSplitter.prototype;
894TSproto.Empty = function() {
895  for(var i = 0; i < this.text.length; ++i)
896    if(this.text[i].length)
897      return false;
898  return true;
899};
900TSproto.Lines = function(e) {
901  var r = e ? 1 : 0, cn, cl, i;
902  e = e || this.e;
903  cn = e.childNodes;
904  cl = cn.length;
905
906  for(i = 0; i < cl; ++i) {
907    if(cn[i].nodeName == 'BR') {
908      this.text.push(this.line.join(' '));
909      this.br = 1;
910    } else if(cn[i].nodeType == 3) {
911      if(this.br) {
912        this.line = [cn[i].nodeValue];
913        this.br = 0;
914      } else {
915        this.line.push(cn[i].nodeValue);
916      }
917    } else {
918      this.Lines(cn[i]);
919    }
920  }
921  r || this.br || this.text.push(this.line.join(' '));
922  return this.text;
923};
924TSproto.SplitWidth = function(w, c, f, h) {
925  var i, j, words, text = [];
926  c.font = h + 'px ' + f;
927  for(i = 0; i < this.text.length; ++i) {
928    words = this.text[i].split(/\s+/);
929    this.line = [words[0]];
930    for(j = 1; j < words.length; ++j) {
931      if(c.measureText(this.line.join(' ') + ' ' + words[j]).width > w) {
932        text.push(this.line.join(' '));
933        this.line = [words[j]];
934      } else {
935        this.line.push(words[j]);
936      }
937    }
938    text.push(this.line.join(' '));
939  }
940  return this.text = text;
941};
942/**
943 * @constructor
944 */
945function Outline(tc,t) {
946  this.ts = null;
947  this.tc = tc;
948  this.tag = t;
949  this.x = this.y = this.w = this.h = this.sc = 1;
950  this.z = 0;
951  this.pulse = 1;
952  this.pulsate = tc.pulsateTo < 1;
953  this.colour = tc.outlineColour;
954  this.adash = ~~tc.outlineDash;
955  this.agap = ~~tc.outlineDashSpace || this.adash;
956  this.aspeed = tc.outlineDashSpeed * 1;
957  if(this.colour == 'tag')
958    this.colour = GetProperty(t.a, 'color');
959  else if(this.colour == 'tagbg')
960    this.colour = GetProperty(t.a, 'background-color');
961  this.Draw = this.pulsate ? this.DrawPulsate : this.DrawSimple;
962  this.radius = tc.outlineRadius | 0;
963  this.SetMethod(tc.outlineMethod);
964}
965Oproto = Outline.prototype;
966Oproto.SetMethod = function(om) {
967  var methods = {
968    block: ['PreDraw','DrawBlock'],
969    colour: ['PreDraw','DrawColour'],
970    outline: ['PostDraw','DrawOutline'],
971    classic: ['LastDraw','DrawOutline'],
972    size: ['PreDraw','DrawSize'],
973    none: ['LastDraw']
974  }, funcs = methods[om] || methods.outline;
975  if(om == 'none') {
976    this.Draw = function() { return 1; }
977  } else {
978    this.drawFunc = this[funcs[1]];
979  }
980  this[funcs[0]] = this.Draw;
981};
982Oproto.Update = function(x,y,w,h,sc,z,xo,yo) {
983  var o = this.tc.outlineOffset, o2 = 2 * o;
984  this.x = sc * x + xo - o;
985  this.y = sc * y + yo - o;
986  this.w = sc * w + o2;
987  this.h = sc * h + o2;
988  this.sc = sc; // used to determine frontmost
989  this.z = z;
990};
991Oproto.Ants = function(c) {
992  if(!this.adash)
993    return;
994  var l = this.adash, g = this.agap, s = this.aspeed, length = l + g,
995    l1 = 0, l2 = l, g1 = g, g2 = 0, seq = 0, ants;
996  if(s) {
997    seq = abs(s) * (TimeNow() - this.ts) / 50;
998    if(s < 0)
999      seq = 8.64e6 - seq;
1000    s = ~~seq % length;
1001  }
1002  if(s) {
1003    if(l >= s) {
1004      l1 = l - s;
1005      l2 = s;
1006    } else {
1007      g1 = length - s;
1008      g2 = g - g1;
1009    }
1010    ants = [l1, g1, l2, g2];
1011  } else {
1012    ants = [l,g];
1013  }
1014  c.setLineDash(ants);
1015}
1016Oproto.DrawOutline = function(c,x,y,w,h,colour) {
1017  var r = min(this.radius, h/2, w/2);
1018  c.strokeStyle = colour;
1019  this.Ants(c);
1020  RRect(c, x, y, w, h, r, true);
1021};
1022Oproto.DrawSize = function(c,x,y,w,h,colour,tag,x1,y1) {
1023  var tw = tag.w, th = tag.h, m, i, sc;
1024  if(this.pulsate) {
1025    if(tag.image)
1026      sc = (tag.image.height + this.tc.outlineIncrease) / tag.image.height;
1027    else
1028      sc = tag.oscale;
1029    i = tag.fimage || tag.image;
1030    m = 1 + ((sc - 1) * (1-this.pulse));
1031    tag.h *= m;
1032    tag.w *= m;
1033  } else {
1034    i = tag.oimage;
1035  }
1036  tag.alpha = 1;
1037  tag.Draw(c, x1, y1, i);
1038  tag.h = th;
1039  tag.w = tw;
1040  return 1;
1041};
1042Oproto.DrawColour = function(c,x,y,w,h,colour,tag,x1,y1) {
1043  if(tag.oimage) {
1044    if(this.pulse < 1) {
1045      tag.alpha = 1 - pow(this.pulse, 2);
1046      tag.Draw(c, x1, y1, tag.fimage);
1047      tag.alpha = this.pulse;
1048    } else {
1049      tag.alpha = 1;
1050    }
1051    tag.Draw(c, x1, y1, tag.oimage);
1052    return 1;
1053  }
1054  return this[tag.image ? 'DrawColourImage' : 'DrawColourText'](c,x,y,w,h,colour,tag,x1,y1);
1055};
1056Oproto.DrawColourText = function(c,x,y,w,h,colour,tag,x1,y1) {
1057  var normal = tag.colour;
1058  tag.colour = colour;
1059  tag.alpha = 1;
1060  tag.Draw(c,x1,y1);
1061  tag.colour = normal;
1062  return 1;
1063};
1064Oproto.DrawColourImage = function(c,x,y,w,h,colour,tag,x1,y1) {
1065  var ccanvas = c.canvas, fx = ~~max(x,0), fy = ~~max(y,0),
1066    fw = min(ccanvas.width - fx, w) + .5|0, fh = min(ccanvas.height - fy,h) + .5|0, cc;
1067  if(ocanvas)
1068    ocanvas.width = fw, ocanvas.height = fh;
1069  else
1070    ocanvas = NewCanvas(fw, fh);
1071  if(!ocanvas)
1072    return this.SetMethod('outline'); // if using IE and images, give up!
1073  cc = ocanvas.getContext('2d');
1074
1075  cc.drawImage(ccanvas,fx,fy,fw,fh,0,0,fw,fh);
1076  c.clearRect(fx,fy,fw,fh);
1077  if(this.pulsate) {
1078    tag.alpha = 1 - pow(this.pulse, 2);
1079  } else {
1080    tag.alpha = 1;
1081  }
1082  tag.Draw(c,x1,y1);
1083  c.setTransform(1,0,0,1,0,0);
1084  c.save();
1085  c.beginPath();
1086  c.rect(fx,fy,fw,fh);
1087  c.clip();
1088  c.globalCompositeOperation = 'source-in';
1089  c.fillStyle = colour;
1090  c.fillRect(fx,fy,fw,fh);
1091  c.restore();
1092  c.globalAlpha = 1;
1093  c.globalCompositeOperation = 'destination-over';
1094  c.drawImage(ocanvas,0,0,fw,fh,fx,fy,fw,fh);
1095  c.globalCompositeOperation = 'source-over';
1096  return 1;
1097};
1098Oproto.DrawBlock = function(c,x,y,w,h,colour) {
1099  var r = min(this.radius, h/2, w/2);
1100  c.fillStyle = colour;
1101  RRect(c, x, y, w, h, r);
1102};
1103Oproto.DrawSimple = function(c, tag, x1, y1, ga, useGa) {
1104  var t = this.tc;
1105  c.setTransform(1,0,0,1,0,0);
1106  c.strokeStyle = this.colour;
1107  c.lineWidth = t.outlineThickness;
1108  c.shadowBlur = c.shadowOffsetX = c.shadowOffsetY = 0;
1109  c.globalAlpha = useGa ? ga : 1;
1110  return this.drawFunc(c,this.x,this.y,this.w,this.h,this.colour,tag,x1,y1);
1111};
1112Oproto.DrawPulsate = function(c, tag, x1, y1) {
1113  var diff = TimeNow() - this.ts, t = this.tc,
1114    ga = t.pulsateTo + ((1 - t.pulsateTo) *
1115    (0.5 + (cos(2 * Math.PI * diff / (1000 * t.pulsateTime)) / 2)));
1116  this.pulse = ga = TagCanvas.Smooth(1,ga);
1117  return this.DrawSimple(c, tag, x1, y1, ga, 1);
1118};
1119Oproto.Active = function(c,x,y) {
1120  var a = (x >= this.x && y >= this.y &&
1121    x <= this.x + this.w && y <= this.y + this.h);
1122  if(a) {
1123    this.ts = this.ts || TimeNow();
1124  } else {
1125    this.ts = null;
1126  }
1127  return a;
1128};
1129Oproto.PreDraw = Oproto.PostDraw = Oproto.LastDraw = Nop;
1130/**
1131 * @constructor
1132 */
1133function Tag(tc, text, a, v, w, h, col, bcol, bradius, boutline, bothickness,
1134  font, padding, original) {
1135  this.tc = tc;
1136  this.image = null;
1137  this.text = text;
1138  this.text_original = original;
1139  this.line_widths = [];
1140  this.title = a.title || null;
1141  this.a = a;
1142  this.position = new Vector(v[0], v[1], v[2]);
1143  this.x = this.y = this.z = 0;
1144  this.w = w;
1145  this.h = h;
1146  this.colour = col || tc.textColour;
1147  this.bgColour = bcol || tc.bgColour;
1148  this.bgRadius = bradius | 0;
1149  this.bgOutline = boutline || this.colour;
1150  this.bgOutlineThickness = bothickness | 0;
1151  this.textFont = font || tc.textFont;
1152  this.padding = padding | 0;
1153  this.sc = this.alpha = 1;
1154  this.weighted = !tc.weight;
1155  this.outline = new Outline(tc,this);
1156}
1157Tproto = Tag.prototype;
1158Tproto.Init = function(e) {
1159  var tc = this.tc;
1160  this.textHeight = tc.textHeight;
1161  if(this.HasText()) {
1162    this.Measure(tc.ctxt,tc);
1163  } else {
1164    this.w = this.iw;
1165    this.h = this.ih;
1166  }
1167
1168  this.SetShadowColour = tc.shadowAlpha ? this.SetShadowColourAlpha : this.SetShadowColourFixed;
1169  this.SetDraw(tc);
1170};
1171Tproto.Draw = Nop;
1172Tproto.HasText = function() {
1173  return this.text && this.text[0].length > 0;
1174};
1175Tproto.EqualTo = function(e) {
1176  var i = e.getElementsByTagName('img');
1177  if(this.a.href != e.href)
1178    return 0;
1179  if(i.length)
1180    return this.image.src == i[0].src;
1181  return (e.innerText || e.textContent) == this.text_original;
1182};
1183Tproto.SetImage = function(i) {
1184  this.image = this.fimage = i;
1185};
1186Tproto.SetDraw = function(t) {
1187  this.Draw = this.fimage ? (t.ie > 7 ? this.DrawImageIE : this.DrawImage) : this.DrawText;
1188  t.noSelect && (this.CheckActive = Nop);
1189};
1190Tproto.MeasureText = function(c) {
1191  var i, l = this.text.length, w = 0, wl;
1192  for(i = 0; i < l; ++i) {
1193    this.line_widths[i] = wl = c.measureText(this.text[i]).width;
1194    w = max(w, wl);
1195  }
1196  return w;
1197};
1198Tproto.Measure = function(c,t) {
1199  var extents = FindTextBoundingBox(this.text, this.textFont, this.textHeight),
1200    s, th, f, soff, cw, twidth, theight, img, tcv;
1201  // add the gap at the top to the height to make equal gap at bottom
1202  theight = extents ? extents.max.y + extents.min.y : this.textHeight;
1203  c.font = this.font = this.textHeight + 'px ' + this.textFont;
1204  twidth = this.MeasureText(c);
1205  if(t.txtOpt) {
1206    s = t.txtScale;
1207    th = s * this.textHeight;
1208    f = th + 'px ' + this.textFont;
1209    soff = [s * t.shadowOffset[0], s * t.shadowOffset[1]];
1210    c.font = f;
1211    cw = this.MeasureText(c);
1212    tcv = new TextCanvas(this.text, f, cw + s, (s * theight) + s, cw,
1213      this.line_widths, t.textAlign, t.textVAlign, s);
1214
1215    if(this.image)
1216      tcv.SetImage(this.image, this.iw, this.ih, t.imagePosition, t.imagePadding,
1217        t.imageAlign, t.imageVAlign, t.imageScale);
1218
1219    img = tcv.Create(this.colour, this.bgColour, this.bgOutline,
1220      s * this.bgOutlineThickness, t.shadow, s * t.shadowBlur, soff,
1221      s * this.padding, s * this.bgRadius);
1222
1223    // add outline image using highlight colour
1224    if(t.outlineMethod == 'colour') {
1225      this.oimage = tcv.Create(this.outline.colour, this.bgColour, this.outline.colour,
1226        s * this.bgOutlineThickness, t.shadow, s * t.shadowBlur, soff,
1227        s * this.padding, s * this.bgRadius);
1228
1229    } else if(t.outlineMethod == 'size') {
1230      extents = FindTextBoundingBox(this.text, this.textFont,
1231        this.textHeight + t.outlineIncrease);
1232      th = extents.max.y + extents.min.y;
1233      f = (s * (this.textHeight + t.outlineIncrease)) + 'px ' + this.textFont;
1234      c.font = f;
1235      cw = this.MeasureText(c);
1236
1237      tcv = new TextCanvas(this.text, f, cw + s, (s * th) + s, cw,
1238        this.line_widths, t.textAlign, t.textVAlign, s);
1239      if(this.image)
1240        tcv.SetImage(this.image, this.iw + t.outlineIncrease,
1241          this.ih + t.outlineIncrease, t.imagePosition, t.imagePadding,
1242          t.imageAlign, t.imageVAlign, t.imageScale);
1243
1244      this.oimage = tcv.Create(this.colour, this.bgColour, this.bgOutline,
1245        s * this.bgOutlineThickness, t.shadow, s * t.shadowBlur, soff,
1246        s * this.padding, s * this.bgRadius);
1247
1248      this.oscale = this.oimage.width / img.width;
1249      if(t.outlineIncrease > 0)
1250        img = ExpandImage(img, this.oimage.width, this.oimage.height);
1251      else
1252        this.oimage = ExpandImage(this.oimage, img.width, img.height);
1253    }
1254    if(img) {
1255      this.fimage = img;
1256      twidth = this.fimage.width / s;
1257      theight = this.fimage.height / s;
1258    }
1259    this.SetDraw(t);
1260    t.txtOpt = !!this.fimage;
1261  }
1262  this.h = theight;
1263  this.w = twidth;
1264};
1265Tproto.SetFont = function(f, c, bc, boc) {
1266  this.textFont = f;
1267  this.colour = c;
1268  this.bgColour = bc;
1269  this.bgOutline = boc;
1270  this.Measure(this.tc.ctxt, this.tc);
1271};
1272Tproto.SetWeight = function(w) {
1273  var tc = this.tc, modes = tc.weightMode.split(/[, ]/), m, s, wl = w.length;
1274  if(!this.HasText())
1275    return;
1276  this.weighted = true;
1277  for(s = 0; s < wl; ++s) {
1278    m = modes[s] || 'size';
1279    if('both' == m) {
1280      this.Weight(w[s], tc.ctxt, tc, 'size', tc.min_weight[s],
1281        tc.max_weight[s], s);
1282      this.Weight(w[s], tc.ctxt, tc, 'colour', tc.min_weight[s],
1283        tc.max_weight[s], s);
1284    } else {
1285      this.Weight(w[s], tc.ctxt, tc, m, tc.min_weight[s], tc.max_weight[s], s);
1286    }
1287  }
1288  this.Measure(tc.ctxt, tc);
1289};
1290Tproto.Weight = function(w, c, t, m, wmin, wmax, wnum) {
1291  w = isNaN(w) ? 1 : w;
1292  var nweight = (w - wmin) / (wmax - wmin);
1293  if('colour' == m)
1294    this.colour = FindGradientColour(t, nweight, wnum);
1295  else if('bgcolour' == m)
1296    this.bgColour = FindGradientColour(t, nweight, wnum);
1297  else if('bgoutline' == m)
1298    this.bgOutline = FindGradientColour(t, nweight, wnum);
1299  else if('outline' == m)
1300    this.outline.colour = FindGradientColour(t, nweight, wnum);
1301  else if('size' == m) {
1302    if(t.weightSizeMin > 0 && t.weightSizeMax > t.weightSizeMin) {
1303      this.textHeight = t.weightSize *
1304        (t.weightSizeMin + (t.weightSizeMax - t.weightSizeMin) * nweight);
1305    } else {
1306      // min textHeight of 1
1307      this.textHeight = max(1, w * t.weightSize);
1308    }
1309  }
1310};
1311Tproto.SetShadowColourFixed = function(c,s,a) {
1312  c.shadowColor = s;
1313};
1314Tproto.SetShadowColourAlpha = function(c,s,a) {
1315  c.shadowColor = SetAlpha(s, a);
1316};
1317Tproto.DrawText = function(c,xoff,yoff) {
1318  var t = this.tc, x = this.x, y = this.y, s = this.sc, i, xl;
1319  c.globalAlpha = this.alpha;
1320  c.fillStyle = this.colour;
1321  t.shadow && this.SetShadowColour(c,t.shadow,this.alpha);
1322  c.font = this.font;
1323  x += xoff / s;
1324  y += (yoff / s) - (this.h / 2);
1325  for(i = 0; i < this.text.length; ++i) {
1326    xl = x;
1327    if('right' == t.textAlign) {
1328      xl += this.w / 2 - this.line_widths[i];
1329    } else if('centre' == t.textAlign) {
1330      xl -= this.line_widths[i] / 2;
1331    } else {
1332      xl -= this.w / 2;
1333    }
1334    c.setTransform(s, 0, 0, s, s * xl, s * y);
1335    c.fillText(this.text[i], 0, 0);
1336    y += this.textHeight;
1337  }
1338};
1339Tproto.DrawImage = function(c,xoff,yoff,im) {
1340  var x = this.x, y = this.y, s = this.sc,
1341    i = im || this.fimage, w = this.w, h = this.h, a = this.alpha,
1342    shadow = this.shadow;
1343  c.globalAlpha = a;
1344  shadow && this.SetShadowColour(c,shadow,a);
1345  x += (xoff / s) - (w / 2);
1346  y += (yoff / s) - (h / 2);
1347  c.setTransform(s, 0, 0, s, s * x, s * y);
1348  c.drawImage(i, 0, 0, w, h);
1349};
1350Tproto.DrawImageIE = function(c,xoff,yoff) {
1351  var i = this.fimage, s = this.sc,
1352    w = i.width = this.w*s, h = i.height = this.h * s,
1353    x = (this.x*s) + xoff - (w/2), y = (this.y*s) + yoff - (h/2);
1354  c.setTransform(1,0,0,1,0,0);
1355  c.globalAlpha = this.alpha;
1356  c.drawImage(i, x, y);
1357};
1358Tproto.Calc = function(m,a) {
1359  var pp, t = this.tc, mnb = t.minBrightness,
1360    mxb = t.maxBrightness, r = t.max_radius;
1361  pp = m.xform(this.position);
1362  this.xformed = pp;
1363  pp = Project(t, pp, t.stretchX, t.stretchY);
1364  this.x = pp.x;
1365  this.y = pp.y;
1366  this.z = pp.z;
1367  this.sc = pp.w;
1368  this.alpha = a * Clamp(mnb + (mxb - mnb) * (r - this.z) / (2 * r), 0, 1);
1369  return this.xformed;
1370};
1371Tproto.UpdateActive = function(c, xoff, yoff) {
1372  var o = this.outline, w = this.w, h = this.h,
1373    x = this.x - w/2, y = this.y - h/2;
1374  o.Update(x, y, w, h, this.sc, this.z, xoff, yoff);
1375  return o;
1376};
1377Tproto.CheckActive = function(c,xoff,yoff) {
1378  var t = this.tc, o = this.UpdateActive(c, xoff, yoff);
1379  return o.Active(c, t.mx, t.my) ? o : null;
1380};
1381Tproto.Clicked = function(e) {
1382  var a = this.a, t = a.target, h = a.href, evt;
1383  if(t != '' && t != '_self') {
1384    if(self.frames[t]) {
1385      self.frames[t].document.location = h;
1386    } else{
1387      try {
1388        if(top.frames[t]) {
1389          top.frames[t].document.location = h;
1390          return;
1391        }
1392      } catch(err) {
1393        // different domain/port/protocol?
1394      }
1395      window.open(h, t);
1396    }
1397    return;
1398  }
1399  if(doc.createEvent) {
1400    evt = doc.createEvent('MouseEvents');
1401    evt.initMouseEvent('click', 1, 1, window, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, null);
1402    if(!a.dispatchEvent(evt))
1403      return;
1404  } else if(a.fireEvent) {
1405    if(!a.fireEvent('onclick'))
1406      return;
1407  }
1408  doc.location = h;
1409};
1410/**
1411 * @constructor
1412 */
1413function TagCanvas(cid,lctr,opt) {
1414  var i, p, c = doc.getElementById(cid), cp = ['id','class','innerHTML'], raf;
1415
1416  if(!c) throw 0;
1417  if(Defined(window.G_vmlCanvasManager)) {
1418    c = window.G_vmlCanvasManager.initElement(c);
1419    this.ie = parseFloat(navigator.appVersion.split('MSIE')[1]);
1420  }
1421  if(c && (!c.getContext || !c.getContext('2d').fillText)) {
1422    p = doc.createElement('DIV');
1423    for(i = 0; i < cp.length; ++i)
1424      p[cp[i]] = c[cp[i]];
1425    c.parentNode.insertBefore(p,c);
1426    c.parentNode.removeChild(c);
1427    throw 0;
1428  }
1429  for(i in TagCanvas.options)
1430    this[i] = opt && Defined(opt[i]) ? opt[i] :
1431      (Defined(TagCanvas[i]) ? TagCanvas[i] : TagCanvas.options[i]);
1432
1433  this.canvas = c;
1434  this.ctxt = c.getContext('2d');
1435  this.z1 = 250 / max(this.depth, 0.001);
1436  this.z2 = this.z1 / this.zoom;
1437  this.radius = min(c.height, c.width) * 0.0075; // fits radius of 100 in canvas
1438  this.max_radius = 100;
1439  this.max_weight = [];
1440  this.min_weight = [];
1441  this.textFont = this.textFont && FixFont(this.textFont);
1442  this.textHeight *= 1;
1443  this.imageRadius = this.imageRadius.toString();
1444  this.pulsateTo = Clamp(this.pulsateTo, 0, 1);
1445  this.minBrightness = Clamp(this.minBrightness, 0, 1);
1446  this.maxBrightness = Clamp(this.maxBrightness, this.minBrightness, 1);
1447  this.ctxt.textBaseline = 'top';
1448  this.lx = (this.lock + '').indexOf('x') + 1;
1449  this.ly = (this.lock + '').indexOf('y') + 1;
1450  this.frozen = this.dx = this.dy = this.fixedAnim = this.touchState = 0;
1451  this.fixedAlpha = 1;
1452  this.source = lctr || cid;
1453  this.repeatTags = min(64, ~~this.repeatTags);
1454  this.minTags = min(200, ~~this.minTags);
1455  if(~~this.scrollPause > 0)
1456    TagCanvas.scrollPause = ~~this.scrollPause;
1457  else
1458    this.scrollPause = 0;
1459  if(this.minTags > 0 && this.repeatTags < 1 && (i = this.GetTags().length))
1460    this.repeatTags = ceil(this.minTags / i) - 1;
1461  this.transform = Matrix.Identity();
1462  this.startTime = this.time = TimeNow();
1463  this.mx = this.my = -1;
1464  this.centreImage && CentreImage(this);
1465  this.Animate = this.dragControl ? this.AnimateDrag : this.AnimatePosition;
1466  this.animTiming = (typeof TagCanvas[this.animTiming] == 'function' ?
1467    TagCanvas[this.animTiming] : TagCanvas.Smooth);
1468  if(this.shadowBlur || this.shadowOffset[0] || this.shadowOffset[1]) {
1469    // let the browser translate "red" into "#ff0000"
1470    this.ctxt.shadowColor = this.shadow;
1471    this.shadow = this.ctxt.shadowColor;
1472    this.shadowAlpha = ShadowAlphaBroken();
1473  } else {
1474    delete this.shadow;
1475  }
1476  this.Load();
1477  if(lctr && this.hideTags) {
1478    (function(t) {
1479    if(TagCanvas.loaded)
1480      t.HideTags();
1481    else
1482      AddHandler('load', function() { t.HideTags(); }, window);
1483    })(this);
1484  }
1485
1486  this.yaw = this.initial ? this.initial[0] * this.maxSpeed : 0;
1487  this.pitch = this.initial ? this.initial[1] * this.maxSpeed : 0;
1488  if(this.tooltip) {
1489    this.ctitle = c.title;
1490    c.title = '';
1491    if(this.tooltip == 'native') {
1492      this.Tooltip = this.TooltipNative;
1493    } else {
1494      this.Tooltip = this.TooltipDiv;
1495      if(!this.ttdiv) {
1496        this.ttdiv = doc.createElement('div');
1497        this.ttdiv.className = this.tooltipClass;
1498        this.ttdiv.style.position = 'absolute';
1499        this.ttdiv.style.zIndex = c.style.zIndex + 1;
1500        AddHandler('mouseover',function(e){e.target.style.display='none';},this.ttdiv);
1501        doc.body.appendChild(this.ttdiv);
1502      }
1503    }
1504  } else {
1505    this.Tooltip = this.TooltipNone;
1506  }
1507  if(!this.noMouse && !handlers[cid]) {
1508    handlers[cid] = [
1509      ['mousemove', MouseMove],
1510      ['mouseout', MouseOut],
1511      ['mouseup', MouseUp],
1512      ['touchstart', TouchDown],
1513      ['touchend', TouchUp],
1514      ['touchcancel', TouchUp],
1515      ['touchmove', TouchMove]
1516    ];
1517    if(this.dragControl) {
1518      handlers[cid].push(['mousedown', MouseDown]);
1519      handlers[cid].push(['selectstart', Nop]);
1520    }
1521    if(this.wheelZoom) {
1522      handlers[cid].push(['mousewheel', MouseWheel]);
1523      handlers[cid].push(['DOMMouseScroll', MouseWheel]);
1524    }
1525    if(this.scrollPause) {
1526      handlers[cid].push(['scroll', Scroll, window]);
1527    }
1528    for(i = 0; i < handlers[cid].length; ++i) {
1529      p = handlers[cid][i];
1530      AddHandler(p[0], p[1], p[2] ? p[2] : c);
1531    }
1532  }
1533  if(!TagCanvas.started) {
1534    raf = window.requestAnimationFrame = window.requestAnimationFrame ||
1535      window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame ||
1536      window.msRequestAnimationFrame;
1537    TagCanvas.NextFrame = raf ? TagCanvas.NextFrameRAF :
1538      TagCanvas.NextFrameTimeout;
1539    TagCanvas.interval = this.interval;
1540    TagCanvas.NextFrame(this.interval);
1541    TagCanvas.started = 1;
1542  }
1543}
1544TCproto = TagCanvas.prototype;
1545TCproto.SourceElements = function() {
1546  if(doc.querySelectorAll)
1547    return doc.querySelectorAll('#' + this.source);
1548  return [doc.getElementById(this.source)];
1549};
1550TCproto.HideTags = function() {
1551  var el = this.SourceElements(), i;
1552  for(i = 0; i < el.length; ++i)
1553    el[i].style.display = 'none';
1554};
1555TCproto.GetTags = function() {
1556  var el = this.SourceElements(), etl, tl = [], i, j, k;
1557  for(k = 0; k <= this.repeatTags; ++k) {
1558    for(i = 0; i < el.length; ++i) {
1559      etl = el[i].getElementsByTagName('a');
1560      for(j = 0; j < etl.length; ++j) {
1561        tl.push(etl[j]);
1562      }
1563    }
1564  }
1565  return tl;
1566};
1567TCproto.Message = function(text) {
1568  var tl = [], i, p, tc = text.split(''), a, t, x, z;
1569  for(i = 0; i < tc.length; ++i) {
1570    if(tc[i] != ' ') {
1571      p = i - tc.length / 2;
1572      a = doc.createElement('A');
1573      a.href = '#';
1574      a.innerText = tc[i];
1575      x = 100 * sin(p / 9);
1576      z = -100 * cos(p / 9);
1577      t = new Tag(this, tc[i], a, [x,0,z], 2, 18, '#000', '#fff', 0, 0, 0,
1578        'monospace', 2, tc[i]);
1579      t.Init();
1580      tl.push(t);
1581    }
1582  }
1583  return tl;
1584};
1585TCproto.CreateTag = function(e) {
1586  var im, i, t, txt, ts, font, bc, boc, p = [0, 0, 0];
1587  if('text' != this.imageMode) {
1588    im = e.getElementsByTagName('img');
1589    if(im.length) {
1590      i = new Image;
1591      i.src = im[0].src;
1592
1593      if(!this.imageMode) {
1594        t = new Tag(this, "", e, p, 0, 0);
1595        t.SetImage(i);
1596        //t.Init();
1597        AddImage(i, im[0], t, this);
1598        return t;
1599      }
1600    }
1601  }
1602  if('image' != this.imageMode) {
1603    ts = new TextSplitter(e);
1604    txt = ts.Lines();
1605    if(!ts.Empty()) {
1606      font = this.textFont || FixFont(GetProperty(e,'font-family'));
1607      if(this.splitWidth)
1608        txt = ts.SplitWidth(this.splitWidth, this.ctxt, font, this.textHeight);
1609
1610      bc = this.bgColour == 'tag' ? GetProperty(e, 'background-color') :
1611        this.bgColour;
1612      boc = this.bgOutline == 'tag' ? GetProperty(e, 'color') : this.bgOutline;
1613    } else {
1614      ts = null;
1615    }
1616  }
1617  if(ts || i) {
1618    t = new Tag(this, txt, e, p, 2, this.textHeight + 2,
1619      this.textColour || GetProperty(e,'color'), bc, this.bgRadius,
1620      boc, this.bgOutlineThickness, font, this.padding, ts && ts.original);
1621    if(i) {
1622      t.SetImage(i);
1623      AddImage(i, im[0], t, this);
1624    } else {
1625      t.Init();
1626    }
1627    return t;
1628  }
1629};
1630TCproto.UpdateTag = function(t, a) {
1631  var colour = this.textColour || GetProperty(a, 'color'),
1632    font = this.textFont || FixFont(GetProperty(a, 'font-family')),
1633    bc = this.bgColour == 'tag' ? GetProperty(a, 'background-color') :
1634      this.bgColour, boc = this.bgOutline == 'tag' ? GetProperty(a, 'color') :
1635      this.bgOutline;
1636  t.a = a;
1637  t.title = a.title;
1638  if(t.colour != colour || t.textFont != font || t.bgColour != bc ||
1639    t.bgOutline != boc)
1640    t.SetFont(font, colour, bc, boc);
1641};
1642TCproto.Weight = function(tl) {
1643  var ll = tl.length, w, i, s, weights = [], valid,
1644    wfrom = this.weightFrom ? this.weightFrom.split(/[, ]/) : [null],
1645    wl = wfrom.length;
1646  for(i = 0; i < ll; ++i) {
1647    weights[i] = [];
1648    for(s = 0; s < wl; ++s) {
1649      w = FindWeight(tl[i].a, wfrom[s], this.textHeight);
1650      if(!this.max_weight[s] || w > this.max_weight[s])
1651        this.max_weight[s] = w;
1652      if(!this.min_weight[s] || w < this.min_weight[s])
1653        this.min_weight[s] = w;
1654      weights[i][s] = w;
1655    }
1656  }
1657  for(s = 0; s < wl; ++s) {
1658    if(this.max_weight[s] > this.min_weight[s])
1659      valid = 1;
1660  }
1661  if(valid) {
1662    for(i = 0; i < ll; ++i) {
1663      tl[i].SetWeight(weights[i]);
1664    }
1665  }
1666};
1667TCproto.Load = function() {
1668  var tl = this.GetTags(), taglist = [], shape, t,
1669    shapeArgs, rx, ry, rz, vl, i, tagmap = [], pfuncs = {
1670      sphere: PointsOnSphere,
1671      vcylinder: PointsOnCylinderV,
1672      hcylinder: PointsOnCylinderH,
1673      vring: PointsOnRingV,
1674      hring: PointsOnRingH
1675    };
1676
1677  if(tl.length) {
1678    tagmap.length = tl.length;
1679    for(i = 0; i < tl.length; ++i)
1680      tagmap[i] = i;
1681    this.shuffleTags && Shuffle(tagmap);
1682    rx = 100 * this.radiusX;
1683    ry = 100 * this.radiusY;
1684    rz = 100 * this.radiusZ;
1685    this.max_radius = max(rx, max(ry, rz));
1686
1687    for(i = 0; i < tl.length; ++i) {
1688      t = this.CreateTag(tl[tagmap[i]]);
1689      if(t)
1690        taglist.push(t);
1691    }
1692    this.weight && this.Weight(taglist, true);
1693
1694    if(this.shapeArgs) {
1695      this.shapeArgs[0] = taglist.length;
1696    } else {
1697      shapeArgs = this.shape.toString().split(/[(),]/);
1698      shape = shapeArgs.shift();
1699      if(typeof window[shape] === 'function')
1700        this.shape = window[shape];
1701      else
1702        this.shape = pfuncs[shape] || pfuncs.sphere;
1703      this.shapeArgs = [taglist.length, rx, ry, rz].concat(shapeArgs);
1704    }
1705    vl = this.shape.apply(this, this.shapeArgs);
1706    this.listLength = taglist.length;
1707    for(i = 0; i < taglist.length; ++i)
1708      taglist[i].position = new Vector(vl[i][0], vl[i][1], vl[i][2]);
1709  }
1710  if(this.noTagsMessage && !taglist.length) {
1711    i = (this.imageMode && this.imageMode != 'both' ? this.imageMode + ' ': '');
1712    taglist = this.Message('No ' + i + 'tags');
1713  }
1714  this.taglist = taglist;
1715};
1716TCproto.Update = function() {
1717  var tl = this.GetTags(), newlist = [],
1718    taglist = this.taglist, found,
1719    added = [], removed = [], vl, ol, nl, i, j;
1720
1721  if(!this.shapeArgs)
1722    return this.Load();
1723
1724  if(tl.length) {
1725    nl = this.listLength = tl.length;
1726    ol = taglist.length;
1727
1728    // copy existing list, populate "removed"
1729    for(i = 0; i < ol; ++i) {
1730      newlist.push(taglist[i]);
1731      removed.push(i);
1732    }
1733
1734    // find added and removed tags
1735    for(i = 0; i < nl; ++i) {
1736      for(j = 0, found = 0; j < ol; ++j) {
1737        if(taglist[j].EqualTo(tl[i])) {
1738          this.UpdateTag(newlist[j], tl[i]);
1739          found = removed[j] = -1;
1740        }
1741      }
1742      if(!found)
1743        added.push(i);
1744    }
1745
1746    // clean out found tags from removed list
1747    for(i = 0, j = 0; i < ol; ++i) {
1748      if(removed[j] == -1)
1749        removed.splice(j,1);
1750      else
1751        ++j;
1752    }
1753
1754    // insert new tags in gaps where old tags removed
1755    if(removed.length) {
1756      Shuffle(removed);
1757      while(removed.length && added.length) {
1758        i = removed.shift();
1759        j = added.shift();
1760        newlist[i] = this.CreateTag(tl[j]);
1761      }
1762
1763      // remove any more (in reverse order)
1764      removed.sort(function(a,b) {return a-b});
1765      while(removed.length) {
1766        newlist.splice(removed.pop(), 1);
1767      }
1768    }
1769
1770    // add any extra tags
1771    j = newlist.length / (added.length + 1);
1772    i = 0;
1773    while(added.length) {
1774      newlist.splice(ceil(++i * j), 0, this.CreateTag(tl[added.shift()]));
1775    }
1776
1777    // assign correct positions to tags
1778    this.shapeArgs[0] = nl = newlist.length;
1779    vl = this.shape.apply(this, this.shapeArgs);
1780    for(i = 0; i < nl; ++i)
1781      newlist[i].position = new Vector(vl[i][0], vl[i][1], vl[i][2]);
1782
1783    // reweight tags
1784    this.weight && this.Weight(newlist);
1785  }
1786  this.taglist = newlist;
1787};
1788TCproto.SetShadow = function(c) {
1789  c.shadowBlur = this.shadowBlur;
1790  c.shadowOffsetX = this.shadowOffset[0];
1791  c.shadowOffsetY = this.shadowOffset[1];
1792};
1793TCproto.Draw = function(t) {
1794  if(this.paused)
1795    return;
1796  var cv = this.canvas, cw = cv.width, ch = cv.height, max_sc = 0,
1797    tdelta = (t - this.time) * TagCanvas.interval / 1000,
1798    x = cw / 2 + this.offsetX, y = ch / 2 + this.offsetY, c = this.ctxt,
1799    active, a, i, aindex = -1, tl = this.taglist, l = tl.length,
1800    frontsel = this.frontSelect, centreDrawn = (this.centreFunc == Nop), fixed;
1801  this.time = t;
1802  if(this.frozen && this.drawn)
1803    return this.Animate(cw,ch,tdelta);
1804  fixed = this.AnimateFixed();
1805  c.setTransform(1,0,0,1,0,0);
1806  for(i = 0; i < l; ++i)
1807    tl[i].Calc(this.transform, this.fixedAlpha);
1808  tl = SortList(tl, function(a,b) {return b.z-a.z});
vendor: 9,714 bytes, lines 1809-2150
1809
1810  if(fixed && this.fixedAnim.active) {
1811    active = this.fixedAnim.tag.UpdateActive(c, x, y);
1812  } else {
1813    this.active = null;
1814    for(i = 0; i < l; ++i) {
1815      a = this.mx >= 0 && this.my >= 0 && this.taglist[i].CheckActive(c, x, y);
1816      if(a && a.sc > max_sc && (!frontsel || a.z <= 0)) {
1817        active = a;
1818        aindex = i;
1819        active.tag = this.taglist[i];
1820        max_sc = a.sc;
1821      }
1822    }
1823    this.active = active;
1824  }
1825
1826  this.txtOpt || (this.shadow && this.SetShadow(c));
1827  c.clearRect(0,0,cw,ch);
1828  for(i = 0; i < l; ++i) {
1829    if(!centreDrawn && tl[i].z <= 0) {
1830      // run the centreFunc if the next tag is at the front
1831      try { this.centreFunc(c, cw, ch, x, y); }
1832      catch(e) {
1833        alert(e);
1834        // don't run it again
1835        this.centreFunc = Nop;
1836      }
1837      centreDrawn = true;
1838    }
1839
1840    if(!(active && active.tag == tl[i] && active.PreDraw(c, tl[i], x, y)))
1841      tl[i].Draw(c, x, y);
1842    active && active.tag == tl[i] && active.PostDraw(c);
1843  }
1844  if(this.freezeActive && active) {
1845    this.Freeze();
1846  } else {
1847    this.UnFreeze();
1848    this.drawn = (l == this.listLength);
1849  }
1850  if(this.fixedCallback) {
1851    this.fixedCallback(this,this.fixedCallbackTag);
1852    this.fixedCallback = null;
1853  }
1854  fixed || this.Animate(cw, ch, tdelta);
1855  active && active.LastDraw(c);
1856  cv.style.cursor = active ? this.activeCursor : '';
1857  this.Tooltip(active,this.taglist[aindex]);
1858};
1859TCproto.TooltipNone = function() { };
1860TCproto.TooltipNative = function(active,tag) {
1861  if(active)
1862    this.canvas.title = tag && tag.title ? tag.title : '';
1863  else
1864    this.canvas.title = this.ctitle;
1865};
1866TCproto.SetTTDiv = function(title, tag) {
1867  var tc = this, s = tc.ttdiv.style;
1868  if(title != tc.ttdiv.innerHTML)
1869    s.display = 'none';
1870  tc.ttdiv.innerHTML = title;
1871  tag && (tag.title = tc.ttdiv.innerHTML);
1872  if(s.display == 'none' && ! tc.tttimer) {
1873    tc.tttimer = setTimeout(function() {
1874      var p = AbsPos(tc.canvas.id);
1875      s.display = 'block';
1876      s.left = p.x + tc.mx + 'px';
1877      s.top = p.y + tc.my + 24 + 'px';
1878      tc.tttimer = null;
1879    }, tc.tooltipDelay);
1880  }
1881};
1882TCproto.TooltipDiv = function(active,tag) {
1883  if(active && tag && tag.title) {
1884    this.SetTTDiv(tag.title, tag);
1885  } else if(!active && this.mx != -1 && this.my != -1 && this.ctitle.length) {
1886    this.SetTTDiv(this.ctitle);
1887  } else {
1888    this.ttdiv.style.display = 'none';
1889  }
1890};
1891TCproto.Transform = function(tc, p, y) {
1892  if(p || y) {
1893    var sp = sin(p), cp = cos(p), sy = sin(y), cy = cos(y),
1894      ym = new Matrix([cy,0,sy, 0,1,0, -sy,0,cy]),
1895      pm = new Matrix([1,0,0, 0,cp,-sp, 0,sp,cp]);
1896    tc.transform = tc.transform.mul(ym.mul(pm));
1897  }
1898};
1899TCproto.AnimateFixed = function() {
1900  var fa, t1, angle, m, d;
1901  if(this.fadeIn) {
1902    t1 = TimeNow() - this.startTime;
1903    if(t1 >= this.fadeIn) {
1904      this.fadeIn = 0;
1905      this.fixedAlpha = 1;
1906    } else {
1907      this.fixedAlpha = t1 / this.fadeIn;
1908    }
1909  }
1910  if(this.fixedAnim) {
1911    if(!this.fixedAnim.transform)
1912      this.fixedAnim.transform = this.transform;
1913    fa = this.fixedAnim, t1 = TimeNow() - fa.t0, angle = fa.angle,
1914      m, d = this.animTiming(fa.t, t1);
1915    this.transform = fa.transform;
1916    if(t1 >= fa.t) {
1917      this.fixedCallbackTag = fa.tag;
1918      this.fixedCallback = fa.cb;
1919      this.fixedAnim = this.yaw = this.pitch = 0;
1920    } else {
1921      angle *= d;
1922    }
1923    m = Matrix.Rotation(angle, fa.axis);
1924    this.transform = this.transform.mul(m);
1925    return (this.fixedAnim != 0);
1926  }
1927  return false;
1928};
1929TCproto.AnimatePosition = function(w, h, t) {
1930  var tc = this, x = tc.mx, y = tc.my, s, r;
1931  if(!tc.frozen && x >= 0 && y >= 0 && x < w && y < h) {
1932    s = tc.maxSpeed, r = tc.reverse ? -1 : 1;
1933    tc.lx || (tc.yaw = ((x * 2 * s / w) - s) * r * t);
1934    tc.ly || (tc.pitch = ((y * 2 * s / h) - s) * -r * t);
1935    tc.initial = null;
1936  } else if(!tc.initial) {
1937    if(tc.frozen && !tc.freezeDecel)
1938      tc.yaw = tc.pitch = 0;
1939    else
1940      tc.Decel(tc);
1941  }
1942  this.Transform(tc, tc.pitch, tc.yaw);
1943};
1944TCproto.AnimateDrag = function(w, h, t) {
1945  var tc = this, rs = 100 * t * tc.maxSpeed / tc.max_radius / tc.zoom;
1946  if(tc.dx || tc.dy) {
1947    tc.lx || (tc.yaw = tc.dx * rs / tc.stretchX);
1948    tc.ly || (tc.pitch = tc.dy * -rs / tc.stretchY);
1949    tc.dx = tc.dy = 0;
1950    tc.initial = null;
1951  } else if(!tc.initial) {
1952    tc.Decel(tc);
1953  }
1954  this.Transform(tc, tc.pitch, tc.yaw);
1955};
1956TCproto.Freeze = function() {
1957  if(!this.frozen) {
1958    this.preFreeze = [this.yaw, this.pitch];
1959    this.frozen = 1;
1960    this.drawn = 0;
1961  }
1962};
1963TCproto.UnFreeze = function() {
1964  if(this.frozen) {
1965    this.yaw = this.preFreeze[0];
1966    this.pitch = this.preFreeze[1];
1967    this.frozen = 0;
1968  }
1969};
1970TCproto.Decel = function(tc) {
1971  var s = tc.minSpeed, ay = abs(tc.yaw), ap = abs(tc.pitch);
1972  if(!tc.lx && ay > s)
1973    tc.yaw = ay > tc.z0 ? tc.yaw * tc.decel : 0;
1974  if(!tc.ly && ap > s)
1975    tc.pitch = ap > tc.z0 ? tc.pitch * tc.decel : 0;
1976};
1977TCproto.Zoom = function(r) {
1978  this.z2 = this.z1 * (1/r);
1979  this.drawn = 0;
1980};
1981TCproto.Clicked = function(e) {
1982  var a = this.active;
1983  try {
1984    if(a && a.tag)
1985      if(this.clickToFront === false || this.clickToFront === null)
1986        a.tag.Clicked(e);
1987      else
1988        this.TagToFront(a.tag, this.clickToFront, function() {
1989          a.tag.Clicked(e);
1990        }, true);
1991  } catch(ex) {
1992  }
1993};
1994TCproto.Wheel = function(i) {
1995  var z = this.zoom + this.zoomStep * (i ? 1 : -1);
1996  this.zoom = min(this.zoomMax,max(this.zoomMin,z));
1997  this.Zoom(this.zoom);
1998};
1999TCproto.BeginDrag = function(e) {
2000  this.down = EventXY(e, this.canvas);
2001  e.cancelBubble = true;
2002  e.returnValue = false;
2003  e.preventDefault && e.preventDefault();
2004};
2005TCproto.Drag = function(e, p) {
2006  if(this.dragControl && this.down) {
2007    var t2 = this.dragThreshold * this.dragThreshold,
2008      dx = p.x - this.down.x, dy = p.y - this.down.y;
2009    if(this.dragging || dx * dx + dy * dy > t2) {
2010      this.dx = dx;
2011      this.dy = dy;
2012      this.dragging = 1;
2013      this.down = p;
2014    }
2015  }
2016  return this.dragging;
2017};
2018TCproto.EndDrag = function() {
2019  var res = this.dragging;
2020  this.dragging = this.down = null;
2021  return res;
2022};
2023function PinchDistance(e) {
2024  var t1 = e.targetTouches[0], t2 = e.targetTouches[1];
2025  return sqrt(pow(t2.pageX - t1.pageX, 2) + pow(t2.pageY - t1.pageY, 2));
2026}
2027TCproto.BeginPinch = function(e) {
2028  this.pinched = [PinchDistance(e), this.zoom];
2029  e.preventDefault && e.preventDefault();
2030};
2031TCproto.Pinch = function(e) {
2032  var z, d, p = this.pinched;
2033  if(!p)
2034    return;
2035  d = PinchDistance(e);
2036  z = p[1] * d / p[0];
2037  this.zoom = min(this.zoomMax,max(this.zoomMin,z));
2038  this.Zoom(this.zoom);
2039};
2040TCproto.EndPinch = function(e) {
2041  this.pinched = null;
2042};
2043TCproto.Pause = function() { this.paused = true; };
2044TCproto.Resume = function() { this.paused = false; };
2045TCproto.SetSpeed = function(i) {
2046  this.initial = i;
2047  this.yaw = i[0] * this.maxSpeed;
2048  this.pitch = i[1] * this.maxSpeed;
2049};
2050TCproto.FindTag = function(t) {
2051  if(!Defined(t))
2052    return null;
2053  Defined(t.index) && (t = t.index);
2054  if(!IsObject(t))
2055    return this.taglist[t];
2056  var srch, tgt, i;
2057  if(Defined(t.id))
2058    srch = 'id', tgt = t.id;
2059  else if(Defined(t.text))
2060    srch = 'innerText', tgt = t.text;
2061
2062  for(i = 0; i < this.taglist.length; ++i)
2063    if(this.taglist[i].a[srch] == tgt)
2064      return this.taglist[i];
2065};
2066TCproto.RotateTag = function(tag, lt, lg, time, callback, active) {
2067  var t = tag.Calc(this.transform, 1), v1 = new Vector(t.x, t.y, t.z),
2068    v2 = MakeVector(lg, lt), angle = v1.angle(v2), u = v1.cross(v2).unit();
2069  if(angle == 0) {
2070    this.fixedCallbackTag = tag;
2071    this.fixedCallback = callback;
2072  } else {
2073    this.fixedAnim = {
2074      angle: -angle,
2075      axis: u,
2076      t: time,
2077      t0: TimeNow(),
2078      cb: callback,
2079      tag: tag,
2080      active: active
2081    };
2082  }
2083};
2084TCproto.TagToFront = function(tag, time, callback, active) {
2085  this.RotateTag(tag, 0, 0, time, callback, active);
2086};
2087TagCanvas.Start = function(id,l,o) {
2088  TagCanvas.Delete(id);
2089  TagCanvas.tc[id] = new TagCanvas(id,l,o);
2090};
2091function tccall(f,id) {
2092  TagCanvas.tc[id] && TagCanvas.tc[id][f]();
2093}
2094TagCanvas.Linear = function(t, t0) { return t0 / t; }
2095TagCanvas.Smooth = function(t, t0) { return 0.5 - cos(t0 * Math.PI / t) / 2; }
2096TagCanvas.Pause = function(id) { tccall('Pause',id); };
2097TagCanvas.Resume = function(id) { tccall('Resume',id); };
2098TagCanvas.Reload = function(id) { tccall('Load',id); };
2099TagCanvas.Update = function(id) { tccall('Update',id); };
2100TagCanvas.SetSpeed = function(id, speed) {
2101  if(IsObject(speed) && TagCanvas.tc[id] &&
2102    !isNaN(speed[0]) && !isNaN(speed[1])) {
2103    TagCanvas.tc[id].SetSpeed(speed);
2104    return true;
2105  }
2106  return false;
2107};
2108TagCanvas.TagToFront = function(id, options) {
2109  if(!IsObject(options))
2110    return false;
2111  options.lat = options.lng = 0;
2112  return TagCanvas.RotateTag(id, options);
2113};
2114TagCanvas.RotateTag = function(id, options) {
2115  if(IsObject(options) && TagCanvas.tc[id]) {
2116    if(isNaN(options.time))
2117      options.time = 500;
2118    var tt = TagCanvas.tc[id].FindTag(options);
2119    if(tt) {
2120      TagCanvas.tc[id].RotateTag(tt, options.lat, options.lng,
2121        options.time, options.callback, options.active);
2122      return true;
2123    }
2124  }
2125  return false;
2126};
2127TagCanvas.Delete = function(id) {
2128  var i, c;
2129  if(handlers[id]) {
2130    c = doc.getElementById(id);
2131    if(c) {
2132      for(i = 0; i < handlers[id].length; ++i)
2133        RemoveHandler(handlers[id][i][0], handlers[id][i][1], c);
2134    }
2135  }
2136  delete handlers[id];
2137  delete TagCanvas.tc[id];
2138};
2139TagCanvas.NextFrameRAF = function() {
2140  requestAnimationFrame(DrawCanvasRAF);
2141};
2142TagCanvas.NextFrameTimeout = function(iv) {
2143  setTimeout(DrawCanvas, iv);
2144};
2145TagCanvas.tc = {};
2146TagCanvas.options = {
2147z1: 20000,
2148z2: 20000,
2149z0: 0.0002,
2150freezeActive: 
vendor: 1,784 bytes, lines 2150-2243
2150true,
2151freezeDecel: false,
2152activeCursor: 'pointer',
2153pulsateTo: 1,
2154pulsateTime: 3,
2155reverse: false,
2156depth: 0.5,
2157maxSpeed: 0.05,
2158minSpeed: 0,
2159decel: 0.95,
2160interval: 20,
2161minBrightness: 0.1,
2162maxBrightness: 1,
2163outlineColour: '',
2164outlineThickness: 2,
2165outlineOffset: 5,
2166outlineMethod: 'outline',
2167outlineRadius: 0,
2168textColour: ['#222', '#000'],
2169textHeight: 15,
2170textFont: 'Helvetica, Arial, sans-serif',
2171shadow: '#111',
2172shadowBlur: 1,
2173shadowOffset: [0.1,0.1],
2174initial: null,
2175hideTags: false,
2176zoom: 0,
2177weight: false,
2178weightMode: 'size',
2179weightFrom: null,
2180weightSize: 1,
2181weightSizeMin: null,
2182weightSizeMax: null,
2183weightGradient: {0:'#f00', 0.33:'#ff0', 0.66:'#0f0', 1:'#00f'},
2184txtOpt: true,
2185txtScale: 2,
2186frontSelect: false,
2187wheelZoom: true,
2188zoomMin: 0.8,
2189zoomMax: 0.8,
2190zoomStep: 0.05,
2191shape: 'sphere',
2192lock: null,
2193tooltip: null,
2194tooltipDelay: 300,
2195tooltipClass: 'tctooltip',
2196radiusX: 1,
2197radiusY: 1,
2198radiusZ: 1,
2199stretchX: 1,
2200stretchY: 1,
2201offsetX: 0,
2202offsetY: 0,
2203shuffleTags: false,
2204noSelect: false,
2205noMouse: false,
2206imageScale: 1,
2207paused: false,
2208dragControl: false,
2209dragThreshold: 4,
2210centreFunc: Nop,
2211splitWidth: 0,
2212animTiming: 'Smooth',
2213clickToFront: false,
2214fadeIn: 0,
2215padding: 0,
2216bgColour: null,
2217bgRadius: 0,
2218bgOutline: null,
2219bgOutlineThickness: 0,
2220outlineIncrease: 4,
2221textAlign: 'centre',
2222textVAlign: 'middle',
2223imageMode: null,
2224imagePosition: null,
2225imagePadding: 2,
2226imageAlign: 'centre',
2227imageVAlign: 'middle',
2228noTagsMessage: true,
2229centreImage: null,
2230pinchZoom: false,
2231repeatTags: 0,
2232minTags: 0,
2233imageRadius: 0,
2234scrollPause: false,
2235outlineDash: 0,
2236outlineDashSpace: 0,
2237outlineDashSpeed: 1
2238};
2239for(i in TagCanvas.options) TagCanvas[i] = TagCanvas.options[i];
2240window.TagCanvas = TagCanvas;
2241// set a flag for when the window has loaded
2242AddHandler('load',function(){TagCanvas.loaded=1},window);
2243})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.