Total Complexity | 510 |
Complexity/F | 2.98 |
Lines of Code | 2223 |
Function Count | 171 |
Duplicated Lines | 23 |
Ratio | 1.03 % |
Changes | 0 |
Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like lib/tagcanvas.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 | /** |
||
21 | (function(){ |
||
22 | "use strict"; |
||
23 | var 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," |
||
30 | }, Oproto, Tproto, TCproto, Mproto, Vproto, TSproto, TCVproto, |
||
31 | doc = document, ocanvas, handlers = {}; |
||
32 | for(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 | } |
||
38 | function Defined(d) { |
||
39 | return typeof d != 'undefined'; |
||
40 | } |
||
41 | function IsObject(o) { |
||
42 | return typeof o == 'object' && o != null; |
||
43 | } |
||
44 | function Clamp(v, mn, mx) { |
||
45 | return isNaN(v) ? mx : min(mx, max(mn, v)); |
||
46 | } |
||
47 | function Nop() { |
||
48 | return false; |
||
49 | } |
||
50 | function TimeNow() { |
||
51 | return new Date().valueOf(); |
||
52 | } |
||
53 | function 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 | } |
||
60 | function 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 | } |
||
70 | function Vector(x, y, z) { |
||
71 | this.x = x; |
||
72 | this.y = y; |
||
73 | this.z = z; |
||
74 | } |
||
75 | Vproto = Vector.prototype; |
||
76 | Vproto.length = function() { |
||
77 | return sqrt(this.x * this.x + this.y * this.y + this.z * this.z); |
||
78 | }; |
||
79 | Vproto.dot = function(v) { |
||
80 | return this.x * v.x + this.y * v.y + this.z * v.z; |
||
81 | }; |
||
82 | Vproto.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 | }; |
||
88 | Vproto.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 | }; |
||
99 | Vproto.unit = function() { |
||
100 | var l = this.length(); |
||
101 | return new Vector(this.x / l, this.y / l, this.z / l); |
||
102 | }; |
||
103 | function 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 | } |
||
109 | function 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 | } |
||
114 | Mproto = Matrix.prototype; |
||
115 | Matrix.Identity = function() { |
||
116 | return new Matrix([1,0,0, 0,1,0, 0,0,1]); |
||
117 | }; |
||
118 | Matrix.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 | } |
||
126 | Mproto.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 | }; |
||
139 | Mproto.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 | }; |
||
146 | View Code Duplication | function 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 | } |
||
157 | View Code Duplication | function 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 | } |
||
169 | function 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 | } |
||
179 | function PointsOnCylinderV(n,xr,yr,zr,m) { return Cylinder(n, 0, xr, yr, zr, m) } |
||
180 | function PointsOnCylinderH(n,xr,yr,zr,m) { return Cylinder(n, 1, xr, yr, zr, m) } |
||
181 | function PointsOnRingV(n, xr, yr, zr, offset) { |
||
182 | offset = isNaN(offset) ? 0 : offset * 1; |
||
183 | return Ring(0, n, xr, yr, zr, offset); |
||
184 | } |
||
185 | function PointsOnRingH(n, xr, yr, zr, offset) { |
||
186 | offset = isNaN(offset) ? 0 : offset * 1; |
||
187 | return Ring(1, n, xr, yr, zr, offset); |
||
188 | } |
||
189 | function 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 | } |
||
201 | function 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 | } |
||
219 | function 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... |
||
229 | function 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 | } |
||
244 | function 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 | } |
||
251 | function 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 | } |
||
272 | function TextSet(ctxt, font, colour, strings, padx, pady, shadowColour, |
||
273 | shadowBlur, shadowOffsets, maxWidth, widths, align) { |
||
274 | var xo = padx + (shadowBlur || 0) + |
||
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 | } |
||
298 | function 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 | } |
||
312 | function 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 | } |
||
324 | TCVproto = TextCanvas.prototype; |
||
325 | TCVproto.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 | }; |
||
336 | TCVproto.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 | }; |
||
344 | TCVproto.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 | }; |
||
428 | function 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 | } |
||
436 | function 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 | } |
||
444 | function 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 | */ |
||
489 | function 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 | */ |
||
521 | function 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 | } |
||
537 | function 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 | } |
||
577 | function FixFont(f) { |
||
578 | return "'" + f.replace(/(\'|\")/g,'').replace(/\s*,\s*/g, "', '") + "'"; |
||
579 | } |
||
580 | function 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 | } |
||
587 | function 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 | } |
||
594 | function 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 | } |
||
684 | function 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 | } |
||
689 | function 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 | } |
||
700 | function EventToCanvasId(e) { |
||
701 | return e.target && Defined(e.target.id) ? e.target.id : |
||
702 | e.srcElement.parentNode.id; |
||
703 | } |
||
704 | function 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 | } |
||
722 | function 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 | } |
||
730 | function 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 | } |
||
749 | function 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 | } |
||
756 | function 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 | } |
||
766 | function 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 | } |
||
788 | function 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 | } |
||
805 | function 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 | } |
||
832 | function 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 | } |
||
841 | function 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 | } |
||
854 | function DrawCanvas() { |
||
855 | DrawCanvasRAF(TimeNow()); |
||
856 | } |
||
857 | function 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 | } |
||
864 | function 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 | } |
||
873 | function 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 | */ |
||
886 | function TextSplitter(e) { |
||
887 | this.e = e; |
||
888 | this.br = 0; |
||
889 | this.line = []; |
||
890 | this.text = []; |
||
891 | this.original = e.innerText || e.textContent; |
||
892 | } |
||
893 | TSproto = TextSplitter.prototype; |
||
894 | TSproto.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 | }; |
||
900 | TSproto.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 | }; |
||
924 | TSproto.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 | */ |
||
945 | function 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 | } |
||
965 | Oproto = Outline.prototype; |
||
966 | Oproto.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 | }; |
||
982 | Oproto.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 | }; |
||
991 | Oproto.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 | } |
||
1016 | Oproto.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 | }; |
||
1022 | Oproto.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 | }; |
||
1042 | Oproto.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 | }; |
||
1056 | Oproto.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 | }; |
||
1064 | Oproto.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 | }; |
||
1098 | Oproto.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 | }; |
||
1103 | Oproto.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 | }; |
||
1112 | Oproto.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 | }; |
||
1119 | Oproto.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 | }; |
||
1129 | Oproto.PreDraw = Oproto.PostDraw = Oproto.LastDraw = Nop; |
||
1130 | /** |
||
1131 | * @constructor |
||
1132 | */ |
||
1133 | function 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 | } |
||
1157 | Tproto = Tag.prototype; |
||
1158 | Tproto.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 | }; |
||
1171 | Tproto.Draw = Nop; |
||
1172 | Tproto.HasText = function() { |
||
1173 | return this.text && this.text[0].length > 0; |
||
1174 | }; |
||
1175 | Tproto.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 | }; |
||
1183 | Tproto.SetImage = function(i) { |
||
1184 | this.image = this.fimage = i; |
||
1185 | }; |
||
1186 | Tproto.SetDraw = function(t) { |
||
1187 | this.Draw = this.fimage ? (t.ie > 7 ? this.DrawImageIE : this.DrawImage) : this.DrawText; |
||
1188 | t.noSelect && (this.CheckActive = Nop); |
||
1189 | }; |
||
1190 | Tproto.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 | }; |
||
1198 | Tproto.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 | }; |
||
1265 | Tproto.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 | }; |
||
1272 | Tproto.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 | }; |
||
1290 | Tproto.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 | }; |
||
1311 | Tproto.SetShadowColourFixed = function(c,s,a) { |
||
1312 | c.shadowColor = s; |
||
1313 | }; |
||
1314 | Tproto.SetShadowColourAlpha = function(c,s,a) { |
||
1315 | c.shadowColor = SetAlpha(s, a); |
||
1316 | }; |
||
1317 | Tproto.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 | }; |
||
1339 | Tproto.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 | }; |
||
1350 | Tproto.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 | }; |
||
1358 | Tproto.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 | }; |
||
1371 | Tproto.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 | }; |
||
1377 | Tproto.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 | }; |
||
1381 | Tproto.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 | */ |
||
1413 | function 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 | } |
||
1544 | TCproto = TagCanvas.prototype; |
||
1545 | TCproto.SourceElements = function() { |
||
1546 | if(doc.querySelectorAll) |
||
1547 | return doc.querySelectorAll('#' + this.source); |
||
1548 | return [doc.getElementById(this.source)]; |
||
1549 | }; |
||
1550 | TCproto.HideTags = function() { |
||
1551 | var el = this.SourceElements(), i; |
||
1552 | for(i = 0; i < el.length; ++i) |
||
1553 | el[i].style.display = 'none'; |
||
1554 | }; |
||
1555 | TCproto.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 | }; |
||
1567 | TCproto.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 | }; |
||
1585 | TCproto.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 | }; |
||
1630 | TCproto.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 | }; |
||
1642 | TCproto.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 | }; |
||
1667 | TCproto.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 | }; |
||
1716 | TCproto.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 | }; |
||
1788 | TCproto.SetShadow = function(c) { |
||
1789 | c.shadowBlur = this.shadowBlur; |
||
1790 | c.shadowOffsetX = this.shadowOffset[0]; |
||
1791 | c.shadowOffsetY = this.shadowOffset[1]; |
||
1792 | }; |
||
1793 | TCproto.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}); |
||
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 | }; |
||
1859 | TCproto.TooltipNone = function() { }; |
||
1860 | TCproto.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 | }; |
||
1866 | TCproto.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 | }; |
||
1882 | TCproto.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 | }; |
||
1891 | TCproto.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 | }; |
||
1899 | TCproto.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 | }; |
||
1929 | TCproto.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 | }; |
||
1944 | TCproto.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 | }; |
||
1956 | TCproto.Freeze = function() { |
||
1957 | if(!this.frozen) { |
||
1958 | this.preFreeze = [this.yaw, this.pitch]; |
||
1959 | this.frozen = 1; |
||
1960 | this.drawn = 0; |
||
1961 | } |
||
1962 | }; |
||
1963 | TCproto.UnFreeze = function() { |
||
1964 | if(this.frozen) { |
||
1965 | this.yaw = this.preFreeze[0]; |
||
1966 | this.pitch = this.preFreeze[1]; |
||
1967 | this.frozen = 0; |
||
1968 | } |
||
1969 | }; |
||
1970 | TCproto.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 | }; |
||
1977 | TCproto.Zoom = function(r) { |
||
1978 | this.z2 = this.z1 * (1/r); |
||
1979 | this.drawn = 0; |
||
1980 | }; |
||
1981 | TCproto.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 | }; |
||
1994 | TCproto.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 | }; |
||
1999 | TCproto.BeginDrag = function(e) { |
||
2000 | this.down = EventXY(e, this.canvas); |
||
2001 | e.cancelBubble = true; |
||
2002 | e.returnValue = false; |
||
2003 | e.preventDefault && e.preventDefault(); |
||
2004 | }; |
||
2005 | TCproto.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 | }; |
||
2018 | TCproto.EndDrag = function() { |
||
2019 | var res = this.dragging; |
||
2020 | this.dragging = this.down = null; |
||
2021 | return res; |
||
2022 | }; |
||
2023 | function 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 | } |
||
2027 | TCproto.BeginPinch = function(e) { |
||
2028 | this.pinched = [PinchDistance(e), this.zoom]; |
||
2029 | e.preventDefault && e.preventDefault(); |
||
2030 | }; |
||
2031 | TCproto.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 | }; |
||
2040 | TCproto.EndPinch = function(e) { |
||
2041 | this.pinched = null; |
||
2042 | }; |
||
2043 | TCproto.Pause = function() { this.paused = true; }; |
||
2044 | TCproto.Resume = function() { this.paused = false; }; |
||
2045 | TCproto.SetSpeed = function(i) { |
||
2046 | this.initial = i; |
||
2047 | this.yaw = i[0] * this.maxSpeed; |
||
2048 | this.pitch = i[1] * this.maxSpeed; |
||
2049 | }; |
||
2050 | TCproto.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 | }; |
||
2066 | TCproto.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 | }; |
||
2084 | TCproto.TagToFront = function(tag, time, callback, active) { |
||
2085 | this.RotateTag(tag, 0, 0, time, callback, active); |
||
2086 | }; |
||
2087 | TagCanvas.Start = function(id,l,o) { |
||
2088 | TagCanvas.Delete(id); |
||
2089 | TagCanvas.tc[id] = new TagCanvas(id,l,o); |
||
2090 | }; |
||
2091 | function tccall(f,id) { |
||
2092 | TagCanvas.tc[id] && TagCanvas.tc[id][f](); |
||
2093 | } |
||
2094 | TagCanvas.Linear = function(t, t0) { return t0 / t; } |
||
2095 | TagCanvas.Smooth = function(t, t0) { return 0.5 - cos(t0 * Math.PI / t) / 2; } |
||
2096 | TagCanvas.Pause = function(id) { tccall('Pause',id); }; |
||
2097 | TagCanvas.Resume = function(id) { tccall('Resume',id); }; |
||
2098 | TagCanvas.Reload = function(id) { tccall('Load',id); }; |
||
2099 | TagCanvas.Update = function(id) { tccall('Update',id); }; |
||
2100 | TagCanvas.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 | }; |
||
2108 | TagCanvas.TagToFront = function(id, options) { |
||
2109 | if(!IsObject(options)) |
||
2110 | return false; |
||
2111 | options.lat = options.lng = 0; |
||
2112 | return TagCanvas.RotateTag(id, options); |
||
2113 | }; |
||
2114 | TagCanvas.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 | }; |
||
2127 | TagCanvas.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 | }; |
||
2139 | TagCanvas.NextFrameRAF = function() { |
||
2140 | requestAnimationFrame(DrawCanvasRAF); |
||
2141 | }; |
||
2142 | TagCanvas.NextFrameTimeout = function(iv) { |
||
2143 | setTimeout(DrawCanvas, iv); |
||
2144 | }; |
||
2145 | TagCanvas.tc = {}; |
||
2146 | TagCanvas.options = { |
||
2147 | z1: 20000, |
||
2148 | z2: 20000, |
||
2149 | z0: 0.0002, |
||
2150 | freezeActive: true, |
||
2151 | freezeDecel: false, |
||
2152 | activeCursor: 'pointer', |
||
2153 | pulsateTo: 1, |
||
2154 | pulsateTime: 3, |
||
2155 | reverse: false, |
||
2156 | depth: 0.5, |
||
2157 | maxSpeed: 0.05, |
||
2158 | minSpeed: 0, |
||
2159 | decel: 0.95, |
||
2160 | interval: 20, |
||
2161 | minBrightness: 0.1, |
||
2162 | maxBrightness: 1, |
||
2163 | outlineColour: '', |
||
2164 | outlineThickness: 2, |
||
2165 | outlineOffset: 5, |
||
2166 | outlineMethod: 'outline', |
||
2167 | outlineRadius: 0, |
||
2168 | textColour: ['#222', '#000'], |
||
2169 | textHeight: 15, |
||
2170 | textFont: 'Helvetica, Arial, sans-serif', |
||
2171 | shadow: '#111', |
||
2172 | shadowBlur: 1, |
||
2173 | shadowOffset: [0.1,0.1], |
||
2174 | initial: null, |
||
2175 | hideTags: false, |
||
2176 | zoom: 0, |
||
2177 | weight: false, |
||
2178 | weightMode: 'size', |
||
2179 | weightFrom: null, |
||
2180 | weightSize: 1, |
||
2181 | weightSizeMin: null, |
||
2182 | weightSizeMax: null, |
||
2183 | weightGradient: {0:'#f00', 0.33:'#ff0', 0.66:'#0f0', 1:'#00f'}, |
||
2184 | txtOpt: true, |
||
2185 | txtScale: 2, |
||
2186 | frontSelect: false, |
||
2187 | wheelZoom: true, |
||
2188 | zoomMin: 0.8, |
||
2189 | zoomMax: 0.8, |
||
2190 | zoomStep: 0.05, |
||
2191 | shape: 'sphere', |
||
2192 | lock: null, |
||
2193 | tooltip: null, |
||
2194 | tooltipDelay: 300, |
||
2195 | tooltipClass: 'tctooltip', |
||
2196 | radiusX: 1, |
||
2197 | radiusY: 1, |
||
2198 | radiusZ: 1, |
||
2199 | stretchX: 1, |
||
2200 | stretchY: 1, |
||
2201 | offsetX: 0, |
||
2202 | offsetY: 0, |
||
2203 | shuffleTags: false, |
||
2204 | noSelect: false, |
||
2205 | noMouse: false, |
||
2206 | imageScale: 1, |
||
2207 | paused: false, |
||
2208 | dragControl: false, |
||
2209 | dragThreshold: 4, |
||
2210 | centreFunc: Nop, |
||
2211 | splitWidth: 0, |
||
2212 | animTiming: 'Smooth', |
||
2213 | clickToFront: false, |
||
2214 | fadeIn: 0, |
||
2215 | padding: 0, |
||
2216 | bgColour: null, |
||
2217 | bgRadius: 0, |
||
2218 | bgOutline: null, |
||
2219 | bgOutlineThickness: 0, |
||
2220 | outlineIncrease: 4, |
||
2221 | textAlign: 'centre', |
||
2222 | textVAlign: 'middle', |
||
2223 | imageMode: null, |
||
2224 | imagePosition: null, |
||
2225 | imagePadding: 2, |
||
2226 | imageAlign: 'centre', |
||
2227 | imageVAlign: 'middle', |
||
2228 | noTagsMessage: true, |
||
2229 | centreImage: null, |
||
2230 | pinchZoom: false, |
||
2231 | repeatTags: 0, |
||
2232 | minTags: 0, |
||
2233 | imageRadius: 0, |
||
2234 | scrollPause: false, |
||
2235 | outlineDash: 0, |
||
2236 | outlineDashSpace: 0, |
||
2237 | outlineDashSpeed: 1 |
||
2238 | }; |
||
2239 | for(i in TagCanvas.options) TagCanvas[i] = TagCanvas.options[i]; |
||
2240 | window.TagCanvas = TagCanvas; |
||
2241 | // set a flag for when the window has loaded |
||
2242 | AddHandler('load',function(){TagCanvas.loaded=1},window); |
||
2243 | })(); |
||
2244 |
Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.
Consider:
If you or someone else later decides to put another statement in, only the first statement will be executed.
In this case the statement
b = 42
will always be executed, while the logging statement will be executed conditionally.ensures that the proper code will be executed conditionally no matter how many statements are added or removed.