PageSourceSearch

https://www.landesdarmagnac.fr/extension/webpublic/design/webpublic/javascript/jquery.tagsphere.js

js landesdarmagnac.fr collected 2026-10-02 19:56:19 UTC 17,695 bytes, 419 lines download raw bytes

1/*
2Plugin: 3D Tag Sphere
3Version: 0.1
4Author: Ian George
5Website: http://www.iangeorge.net
6Tools: Emacs, js2-mode
7Tested on: IE6, IE7, IE8, Firefox 3.6 Linux, Firefox 3.5 Windows, Chrome Linux / Windows
8Requirements: Optional jquery.mousewheel for zooming
9
10Description: 3d tag cloud, rotates with the mouse and zooms in and out. Fixed camera position to simplify calculations
11
12Known issues:
13 Breaks badly on IE5.5
14 Looks a bit rough on page load
15
16TODO:
17 Performance is horrible when more than one instance is present in the page
18 Would be much quicker if instead of recalculating all values on mouse move a global phi/theta value pair is stored and the values calculated on-the-fly in Draw()
19
20Options:
21  Option            default            Comments
22  --------------------------------------------------
23  zoom              75                 Initial zoom level
24  min_zoom          25        
25  max_zoom          120      
26  zoom_factor       2                  Speed of zoom by the mouse wheel
27  rotate_factor     0.45               In degrees, the amount that the sphere rotates. Negative values reverse the direction.
28  fps               10                 Defines the (target) number of times the animation will be updated per second
29  centrex           250                Horizontal rotation centre in the container <div>
30  centrey           250                Vertical rotation centre in the container <div>
31  min_font_size     12
32  max_font_size     32
33  font_units        'px'
34  random_points     50                 Adds some random points on to the sphere to enhance the effect
35  foreground_colour #fff               Accepted colour formats are #333 #0A0A0A and rgb(n,n,n)
36  background_colour rgb(0,0,0)         Named colours may not work
37
38Usage:
39 Vanilla:
40    $('.tags').tagcloud();
41
42 Centered in a 200 x 200 container:
43    $('.tags').tagcloud({centrex:100,centrey:100});
44
45 With a different update speed
46    $('.selector').tagcloud({fps:24});
47
48Markup:
49 Must be an unordered list in a div with links in the list items. 
50 rel="[number]" is optional but necessary for ranking by font-size.
51 <div class="tags">
52 <ul>
53   <li><a href="#" rel="20">link 1</a></li>
54   <li><a href="#" rel="20">link 2</a></li>
55   <li><a href="#" rel="20">link 3</a></li>
56   <li><a href="#" rel="20">link 4</a></li>
57   <li><a href="#" rel="20">link 5</a></li>
58 </ul>
59 */
60
61(function($){
62
63     // jquery plugin hook
64     $.fn.tagcloud = function(options){
65        
66         // overwrite defaults with user-specified
67         var opts = $.extend($.fn.tagcloud.defaults, options);
68         opts.drawing_interval = 1/(opts.fps/1000);
69         
70         //create a new class for every matching element
71         $(this).each(function(){
72                          $('ul', $(this)).css('display', 'none');
73                          new TagCloudClass($(this), opts);
74                      });
75         return this;         
76     };
77
78
79     //default values for setup
80     $.fn.tagcloud.defaults = {
81         zoom: 90,
82         max_zoom: 120,
83         min_zoom: 25,
84         zoom_factor: 2, //multiplication factor for wheel delta
85         rotate_factor: -0.45, // multiplication factor for rotation
86         fps: 10, // frames per second
87         centrex: 250, // set centre of display
88         centrey: 250,
89         min_font_size: 12, //font limits and units
90         max_font_size: 32,
91         font_units: 'px',
92         random_points: 0         
93     };
94
95     var TagCloudClass = function(el, options){
96         $(el).css('position', 'relative');
97
98         // general values
99         var eyez = -500;
100
101         // set rotation (in this case, 5degrees)
102         var rad = Math.PI/180;
103         var global_cos = Math.cos(0);
104
105         var hex = new Array("0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f");
106
107         // per-instance values
108         var dirty = true;
109         var container = $(el);
110         var id_stub = 'tc_' + $(el).attr('id') + "_";
111         var opts = options;
112         var zoom = opts.zoom;
113         var depth;
114         var lastx = 0;
115         var lasty = 0;
116	 var points = [];
117         points['data'] = [];
118         var drawing_interval;
119         var cmx = options.centrex; 
120         var cmy = options.centrey;/*
121         var bg_colour, fg_colour;
122         if (options.background_colour){
123             bg_colour = parsecolour(options.background_colour);
124         }else{
125             bg_colour = parsecolour($(el).css('background-color'));
126         }
127         if (options.foreground_colour){
128             fg_colour = parsecolour(options.foreground_colour);
129         }else{
130             fg_colour = parsecolour($(el).css('color'));
131         }*/
132         
133         function parsecolour(colour){
134             function parse_rgb_colour(colour){
135                 rgb = colour.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
136                 if(rgb.length > 3){
137                     return {"r" : parseInt(rgb[1]), "g": parseInt(rgb[2]), "b" : parseInt(rgb[3])};                 
138                 }else{  
139                     return {"r":0, "g":0, "b":0};
140                 }
141             }
142             function parse_hex_colour(colour){
143                 var r = 0, g = 0, b = 0;
144                 if(colour.length > 4)
145                 {
146                     r = parseInt(colour.substr(1,2), 16);
147                     g = parseInt(colour.substr(3,2), 16);
148                     b = parseInt(colour.substr(5,2), 16);
149                 }
150                 else
151                 {
152                     r = parseInt(colour.substr(1,1)+colour.substr(1,1), 16);
153                     g = parseInt(colour.substr(2,1)+colour.substr(2,1), 16);
154                     b = parseInt(colour.substr(3,1)+colour.substr(3,1), 16);
155                 }
156                 return {"r" : r, "g" : g, "b" : b};
157             }
158
159             if(colour.substr(0, 1) === '#')
160             {
161                 return parse_hex_colour(colour);
162             }
163             else if (colour.substr(0,3) === 'rgb')
164             {
165                 return parse_rgb_colour(colour);
166             }
167             else{
168                 //somehow we've got a plain old string as a colour
169                 if(window.console != undefined)
170                     console.log("unable to parse:'" + colour + "' please ensure background and foreground colors for the container are set as hex values");
171                 return null;
172             }
173         }
174             /*
175         function getcolour(num){
176             if(num>256){num=256;}
177             if(num<0){num=0;}
178
179             var r = getshade(bg_colour.r, fg_colour.r, num);
180             var g = getshade(bg_colour.g, fg_colour.g, num);
181             var b = getshade(bg_colour.b, fg_colour.b, num);
182
183             var ret  = "rgb(" + r + ", "+ g + ", " + b + ")"; 
184             return ret;
185         }*/
186
187         function getshade(lbound, ubound, dist){
188             var scope = ubound - lbound;
189             var dist_percent = scope / 100;
190             var shade = Math.round(lbound + (dist * dist_percent));
191             return shade;
192         }
193
194         
195         //drawing and rotation...				
196         function rotx(s, c){
197             for(var p in points.data)
198             {
199	         var temp = s * points.data[p].y + c * points.data[p].z;
200	         points.data[p].y = c * points.data[p].y - s * points.data[p].z;
201	         points.data[p].z = temp;
202             }
203         }
204
205         function roty(s, c){
206             for(var p in points.data){
207	         var temp = - s * points.data[p].x + c * points.data[p].z;
208	         points.data[p].x = c * points.data[p].x + s * points.data[p].z;
209	         points.data[p].z = temp; 
210             }
211         }
212
213         function rotz(s, c){
214             for(var p in points.data) {
215	         var temp = s * points.data[p].x + c * points.data[p].y;
216	         points.data[p].x = c * points.data[p].x - s * points.data[p].y;
217	         points.data[p].y = temp;
218             }
219         }
220
221         function zoomed(by){
222             zoom += by * opts.zoom_factor;
223
224             if (zoom > opts.max_zoom) {
225	         zoom = opts.max_zoom;
226             }
227             if (zoom < opts.min_zoom) {
228	         zoom = opts.min_zoom;
229             }
230
231             depth = -(zoom * (eyez - opts.max_zoom) / 100) + eyez;
232         }
233
234         function moved(mx,my){
235             if(mx>lastx){
236                 var angle = mx - lastx;
237	         var sin = -Math.sin(angle*options.rotate_factor*rad);
238                 var cos = Math.cos(angle*options.rotate_factor*rad);
239	         roty(sin, cos);
240             }
241             if(mx<lastx){
242                 var angle = lastx - mx;
243	         var sin = Math.sin(angle*options.rotate_factor*rad);
244                 var cos = Math.cos(angle*options.rotate_factor*rad);
245	         roty(sin, cos);			
246             }
247             if(my>lasty){
248                 var angle = my - lasty;
249	         var sin = Math.sin(angle*options.rotate_factor*rad);
250                 var cos = Math.cos(angle*options.rotate_factor*rad);
251	         rotx(sin, cos);
252             }
253             if(my<lasty){
254                 var angle = lasty - my;
255	         var sin = -Math.sin(angle*options.rotate_factor*rad);
256                 var cos = Math.cos(angle*options.rotate_factor*rad);
257	         rotx(sin, cos);
258             }
259
260             lastx = mx;
261             lasty = my;
262             dirty = true;
263         }
264
265         function draw(){
266             // calculate 2D coordinates
267             if(dirty){
268                 var smallz = 10000; var bigz = -10000;
269                 for(var r_p in points.data){
270	             if(points.data[r_p].z < smallz){smallz = points.data[r_p].z;}
271	             if(points.data[r_p].z > bigz){bigz = points.data[r_p].z;}
272                 }
273                 var minz = Math.min(smallz, bigz);
274                 var maxz = Math.max(smallz, bigz);
275                 var diffz = maxz - minz;
276                 
277                 for(var s_p in points.data){ 
278                     //normalise depth
279	             var u = (depth - eyez)/(points.data[s_p].z - eyez);
280
281                     // calculate normalised grey value
282                     var dist = Math.round(((maxz - points.data[s_p].z)/diffz) * 100);
283                     //var dist_colour = getcolour(dist);
284                     //set new 2d positions for the data
285                     //$('#'+points.data[s_p].id + ' a', container).css('color',dist_colour);
286                     $('#'+points.data[s_p].id, container).css('z-index',dist);
287                     $('#'+points.data[s_p].id, container).css('left', u * points.data[s_p].x + cmx - points.data[s_p].cwidth);
288                     $('#'+points.data[s_p].id, container).css('top', u * points.data[s_p].y + cmy); 
289                 }			
290                 dirty = false;
291             }
292         }
293
294         // number of elements we're adding and placeholders for range values
295         points.count = $('li a', container).length;
296         points.largest = 1;
297         points.smallest = 0;
298
299
300         // Run through each li > a in the container and create an absolutely-positioned div in its place
301         // Also need to create a data structure to keep state between calls to draw()
302         // Data structure is as follows:
303         // 
304         // points{
305         //     'count':0,                      //Total number of points
306         //     'largest':0,                    //largest 'size' value
307         //     'smallest':0,                   //Smallest 'size' value
308         //     'data':{
309         //         'id':"",                    //HTML id for element
310         //         'size':0,                   //Size (from rel attribute on <a>)
311         //         'theta':0.0,                //Angle on sphere (used to calculate initial cartesian position)
312         //         'phi': 0.0,                 //Angle on sphere (used to calculate initial cartesian position)
313         //         'x':0.0,                    //Cartesian position in 3d space
314         //         'y':0.0,                    //Cartesian position in 3d space
315         //         'z':0.0,                    //Cartesian position in 3d space
316         //     }
317         // }
318         $('li a', container).each(function(idx, val){
319
320                                       var sz = parseInt($(this).attr('rel'));
321                                       if(sz == 0) 
322                                           sz = 1;
323
324                                       points.data[idx] = {
325                                           id:id_stub + idx,
326                                           size:sz
327                                       };
328
329                                       // plot the points on a sphere
330                                       // from: http://www.math.niu.edu/~rusin/known-math/97/spherefaq
331                                       // for k=1 to N do
332                                       //     h = -1 + 2*(k-1)/(N-1)
333                                       //     theta[k] = arccos(h)
334                                       //     if k=1 or k=N then phi[k] = 0
335                                       //     else phi[k] = (phi[k-1] + 3.6/sqrt(N*(1-h^2))) mod (2*pi)
336                                       // endfor
337
338                                       // In Cartesian coordinates the required point on a sphere of radius 1 is
339                                       // (cos(theta)*sin(phi), sin(theta)*sin(phi), cos(phi))
340                                       
341                                       var h = -1 + 2*(idx)/(points.count-1);
342                                       points.data[idx].theta = Math.acos(h);
343                                       if(idx == 0 || idx == points.count-1){
344                                           points.data[idx].phi = 0;
345                                       }
346                                       else{
347                                           points.data[idx].phi = (points.data[idx-1].phi + 3.6/Math.sqrt(points.count*(1-Math.pow(h,2)))) % (2 * Math.PI);
348                                       }
349
350                                       points.data[idx].x = Math.cos(points.data[idx].phi) * Math.sin(points.data[idx].theta) * (cmx/2);
351                                       points.data[idx].y = Math.sin(points.data[idx].phi) * Math.sin(points.data[idx].theta) * (cmy/2);
352                                       points.data[idx].z = Math.cos(points.data[idx].theta) * (cmx/2);
353
354                                       if(sz > points.largest) points.largest = sz;
355                                       if(sz < points.smallest) points.smallest = sz;
356
357                                       container.append('<div id="'+ id_stub + idx +'" class="point" style="position:absolute;display:none;"><a href=' + $(this).attr('href')  + '>' + $(this).html()  + '</a></div>');
358                                   });
359
360         //if required to do so (by opts.random_points being > 0) we need to generate some random points on the sphere
361         //bit cheezy, but can make more sparse data sets look a bit more believable
362         if(opts.random_points > 0){
363             for(b=0; b<opts.random_points; b++){
364                 points.count++;                                                  
365                 points.data[points.count] = {
366                     id:id_stub + points.count,
367                     size:1
368                 };
369                 points.data[points.count].theta = Math.random() * 2 * Math.PI;
370                 points.data[points.count].phi = Math.random() * 2 * Math.PI;
371                 points.data[points.count].x = Math.cos(points.data[points.count].phi) * Math.sin(points.data[points.count].theta) * (cmx/2);
372                 points.data[points.count].y = Math.sin(points.data[points.count].phi) * Math.sin(points.data[points.count].theta) * (cmy/2);
373                 points.data[points.count].z = Math.cos(points.data[points.count].theta) * (cmx/2);
374                 container.append('<div id="'+ id_stub + points.count +'" class="point" style="position:absolute;"><a>.</a></div>');
375             }
376         }
377         
378         //tag size and font size ranges 
379         var sz_range = points.largest - points.smallest + 1; 
380         var sz_n_range = opts.max_font_size - opts.min_font_size + 1;
381         
382         //set font size to normalised tag size
383         for(var p in points.data){
384             var sz = points.data[p].size;
385             var sz_n = parseInt((sz / sz_range) * sz_n_range) + opts.min_font_size;
386             if(!$('#' + points.data[p].id, container).hasClass('background')){
387                 $('#' + points.data[p].id, container).css('font-size', sz_n); 
388             }
389             //store element width / 2 so we can centre the text around the point later.
390             points.data[p].cwidth = $('#' + points.data[p].id, container).width()/2;
391         }
392         // bin original html
393         $('ul', container).remove();
394
395         //set up initial view
396         depth = -(zoom * (eyez - opts.max_zoom) / 100) + eyez;
397         moved(cmx, cmy);
398         draw();
399
400         //show all the points
401         $('.point',container).css('display','inherit');
402
403
404         //call draw every so often
405         drawing_interval = setInterval(draw, opts.drawing_interval);
406
407         //events to change position of items
408         container.mousemove(function(evt){
409                                 moved(evt.clientX, evt.clientY);
410                             });
411         container.mousewheel(function(evt, delta){
412                                  zoomed(delta);
413                                  evt.preventDefault();
414                                  return false;
415                              });
416         
417     };
418          
419 })(jQuery);

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.