From 7f9fad3db7d690c39a61a931b569c53e12df0d9c Mon Sep 17 00:00:00 2001 From: Fabio Neves Date: Tue, 23 Oct 2012 16:49:33 -0400 Subject: [PATCH 1/5] Brought back the customClass option --- README.md | 3 ++- examples/rectangular.html | 2 +- jqcloud/jqcloud-1.0.2.js | 7 ++++--- jqcloud/jqcloud-1.0.2.min.js | 4 ++-- src/README.md.erb | 3 ++- src/examples/rectangular.html.erb | 2 +- src/jqcloud/jqcloud.js.erb | 5 +++-- 7 files changed, 15 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index d796caf..572b98d 100644 --- a/README.md +++ b/README.md @@ -96,6 +96,7 @@ All cloud-wide configurations are optional, and the full list of available optio * **afterCloudRender** (function): A callback function to be called after the whole cloud is fully rendered. * **delayedMode** (boolean): If true, words are rendered one after another with a tiny delay between each one. This prevents freezing of the browser when there are many words to be rendered. If false, the cloud will be rendered in one single shot. By default, delayedMode is true when there are more than 50 words. * **shape** (string): the shape of the cloud. By default it is elliptic, but it can be set to `"rectangular"` to draw a rectangular-shaped cloud. +* **customClass** (string): custom CSS class to be added to all words. ## Custom CSS guidelines @@ -114,7 +115,7 @@ Please note that version 1.0 is a redesign of the API that does not maintain bac This is a quick list of what changed in the new 1.0 API: - * in the word object, you can now specify any html attribute for the word using the `html` option (e.g.: `{title: "A Title", "class": "custom-class", data-custom: "custom data attribute"}`). Since this allows for more flexibility, `title`, `customClass` and `dataAttributes` options are superfluous and dropped in v1.0. + * in the word object, you can now specify any html attribute for the word using the `html` option (e.g.: `{title: "A Title", "class": "custom-class", data-custom: "custom data attribute"}`). Since this allows for more flexibility, `title` and `dataAttributes` options are superfluous and dropped in v1.0. * the `url` option was renamed `link` in v1.0, and can now be a URL string or an object. In the latter case, any html attribute for the `` tag can be specified (e.g.: `{href: "http://myurl.com", title: "A Title"}`). * the cloud options `randomClasses` and `nofollow` are dropped in v1.0. They were indended for purposes which are better achieved using the new `html` and `link` word options. * `width` and `height` cloud options now set the width and height of the cloud container element, other than determining the aspect ratio of the cloud. diff --git a/examples/rectangular.html b/examples/rectangular.html index 09be1b6..df68a46 100644 --- a/examples/rectangular.html +++ b/examples/rectangular.html @@ -51,7 +51,7 @@ {text: "Sed", weight: 1} ]; $(function() { - $("#my_favorite_latin_words").jQCloud(word_list, {shape: "rectangular"}); + $("#my_favorite_latin_words").jQCloud(word_list, {shape: "rectangular", customClass: "tag"}); }); diff --git a/jqcloud/jqcloud-1.0.2.js b/jqcloud/jqcloud-1.0.2.js index 22eba92..b8fa27a 100644 --- a/jqcloud/jqcloud-1.0.2.js +++ b/jqcloud/jqcloud-1.0.2.js @@ -6,7 +6,7 @@ * Copyright 2011, Luca Ongaro * Licensed under the MIT license. * - * Date: Tue Oct 09 22:08:53 +0200 2012 + * Date: 2012-10-23 16:47:45 -0400 */ (function( $ ) { @@ -27,7 +27,8 @@ }, delayedMode: word_array.length > 50, shape: false, // It defaults to elliptic shape - encodeURI: true + encodeURI: true, + customClass: '' }; options = $.extend(default_options, options || {}); @@ -87,7 +88,7 @@ quarter_turns = 0.0, weight = 5, - custom_class = "", + custom_class = options.customClass, inner_html = "", word_span = ""; diff --git a/jqcloud/jqcloud-1.0.2.min.js b/jqcloud/jqcloud-1.0.2.min.js index 2ef9347..b1e2db4 100644 --- a/jqcloud/jqcloud-1.0.2.min.js +++ b/jqcloud/jqcloud-1.0.2.min.js @@ -6,6 +6,6 @@ * Copyright 2011, Luca Ongaro * Licensed under the MIT license. * - * Date: Tue Oct 09 22:08:53 +0200 2012 + * Date: 2012-10-23 16:47:45 -0400 */ -(function(a){"use strict",a.fn.jQCloud=function(b,c){var d=this,e=d.attr("id")||Math.floor(Math.random()*1e6).toString(36),f={width:d.width(),height:d.height(),center:{x:(c&&c.width?c.width:d.width())/2,y:(c&&c.height?c.height:d.height())/2},delayedMode:b.length>50,shape:!1,encodeURI:!0};c=a.extend(f,c||{}),d.addClass("jqcloud").width(c.width).height(c.height),d.css("position")==="static"&&d.css("position","relative");var g=function(){var f=function(a,b){var c=function(a,b){return Math.abs(2*a.offsetLeft+a.offsetWidth-2*b.offsetLeft-b.offsetWidth)b.weight?-1:0});var h=c.shape==="rectangular"?18:2,i=[],j=c.width/c.height,k=function(g,k){var l=e+"_word_"+g,m="#"+l,n=6.28*Math.random(),o=0,p=0,q=0,r=5,s="",t="",u="";k.html=a.extend(k.html,{id:l}),k.html&&k.html["class"]&&(s=k.html["class"],delete k.html["class"]),b[0].weight>b[b.length-1].weight&&(r=Math.round((k.weight-b[b.length-1].weight)/(b[0].weight-b[b.length-1].weight)*9)+1),u=a("").attr(k.html).addClass("w"+r+" "+s),k.link?(typeof k.link=="string"&&(k.link={href:k.link}),c.encodeURI&&(k.link=a.extend(k.link,{href:encodeURI(k.link.href).replace(/'/g,"%27")})),t=a("").attr(k.link).text(k.text)):t=k.text,u.append(t);if(!!k.handlers)for(var v in k.handlers)k.handlers.hasOwnProperty(v)&&typeof k.handlers[v]=="function"&&a(u).bind(v,k.handlers[v]);d.append(u);var w=u.width(),x=u.height(),y=c.center.x-w/2,z=c.center.y-x/2,A=u[0].style;A.position="absolute",A.left=y+"px",A.top=z+"px";while(f(document.getElementById(l),i)){if(c.shape==="rectangular"){p++,p*h>(1+Math.floor(q/2))*h*(q%4%2===0?1:j)&&(p=0,q++);switch(q%4){case 1:y+=h*j+Math.random()*2;break;case 2:z-=h+Math.random()*2;break;case 3:y-=h*j+Math.random()*2;break;case 0:z+=h+Math.random()*2}}else o+=h,n+=(g%2===0?1:-1)*h,y=c.center.x-w/2+o*Math.cos(n)*j,z=c.center.y+o*Math.sin(n)-x/2;A.left=y+"px",A.top=z+"px"}i.push(document.getElementById(l)),a.isFunction(k.afterWordRender)&&k.afterWordRender.call(u)},l=function(e){e=e||0;if(!d.is(":visible")){setTimeout(function(){l(e)},10);return}e50,shape:!1,encodeURI:!0,customClass:""};n=e.extend(s,n||{}),r.addClass("jqcloud").width(n.width).height(n.height),r.css("position")==="static"&&r.css("position","relative");var o=function(){var s=function(e,t){var n=function(e,t){return Math.abs(2*e.offsetLeft+e.offsetWidth-2*t.offsetLeft-t.offsetWidth)t.weight?-1:0});var u=n.shape==="rectangular"?18:2,a=[],f=n.width/n.height,l=function(o,l){var c=i+"_word_"+o,h="#"+c,p=6.28*Math.random(),d=0,v=0,m=0,g=5,y=n.customClass,b="",w="";l.html=e.extend(l.html,{id:c}),l.html&&l.html["class"]&&(y=l.html["class"],delete l.html["class"]),t[0].weight>t[t.length-1].weight&&(g=Math.round((l.weight-t[t.length-1].weight)/(t[0].weight-t[t.length-1].weight)*9)+1),w=e("").attr(l.html).addClass("w"+g+" "+y),l.link?(typeof l.link=="string"&&(l.link={href:l.link}),n.encodeURI&&(l.link=e.extend(l.link,{href:encodeURI(l.link.href).replace(/'/g,"%27")})),b=e("").attr(l.link).text(l.text)):b=l.text,w.append(b);if(!!l.handlers)for(var E in l.handlers)l.handlers.hasOwnProperty(E)&&typeof l.handlers[E]=="function"&&e(w).bind(E,l.handlers[E]);r.append(w);var S=w.width(),x=w.height(),T=n.center.x-S/2,N=n.center.y-x/2,C=w[0].style;C.position="absolute",C.left=T+"px",C.top=N+"px";while(s(document.getElementById(c),a)){if(n.shape==="rectangular"){v++,v*u>(1+Math.floor(m/2))*u*(m%4%2===0?1:f)&&(v=0,m++);switch(m%4){case 1:T+=u*f+Math.random()*2;break;case 2:N-=u+Math.random()*2;break;case 3:T-=u*f+Math.random()*2;break;case 0:N+=u+Math.random()*2}}else d+=u,p+=(o%2===0?1:-1)*u,T=n.center.x-S/2+d*Math.cos(p)*f,N=n.center.y+d*Math.sin(p)-x/2;C.left=T+"px",C.top=N+"px"}a.push(document.getElementById(c)),e.isFunction(l.afterWordRender)&&l.afterWordRender.call(w)},c=function(i){i=i||0;if(!r.is(":visible")){setTimeout(function(){c(i)},10);return}i using the `html` option (e.g.: `{title: "A Title", "class": "custom-class", data-custom: "custom data attribute"}`). Since this allows for more flexibility, `title`, `customClass` and `dataAttributes` options are superfluous and dropped in v1.0. + * in the word object, you can now specify any html attribute for the word using the `html` option (e.g.: `{title: "A Title", "class": "custom-class", data-custom: "custom data attribute"}`). Since this allows for more flexibility, `title` and `dataAttributes` options are superfluous and dropped in v1.0. * the `url` option was renamed `link` in v1.0, and can now be a URL string or an object. In the latter case, any html attribute for the `` tag can be specified (e.g.: `{href: "http://myurl.com", title: "A Title"}`). * the cloud options `randomClasses` and `nofollow` are dropped in v1.0. They were indended for purposes which are better achieved using the new `html` and `link` word options. * `width` and `height` cloud options now set the width and height of the cloud container element, other than determining the aspect ratio of the cloud. diff --git a/src/examples/rectangular.html.erb b/src/examples/rectangular.html.erb index 8e4b5aa..7c216a2 100644 --- a/src/examples/rectangular.html.erb +++ b/src/examples/rectangular.html.erb @@ -51,7 +51,7 @@ {text: "Sed", weight: 1} ]; $(function() { - $("#my_favorite_latin_words").jQCloud(word_list, {shape: "rectangular"}); + $("#my_favorite_latin_words").jQCloud(word_list, {shape: "rectangular", customClass: "tag"}); }); diff --git a/src/jqcloud/jqcloud.js.erb b/src/jqcloud/jqcloud.js.erb index 79e3c45..325a679 100644 --- a/src/jqcloud/jqcloud.js.erb +++ b/src/jqcloud/jqcloud.js.erb @@ -27,7 +27,8 @@ }, delayedMode: word_array.length > 50, shape: false, // It defaults to elliptic shape - encodeURI: true + encodeURI: true, + customClass: '' }; options = $.extend(default_options, options || {}); @@ -87,7 +88,7 @@ quarter_turns = 0.0, weight = 5, - custom_class = "", + custom_class = options.customClass, inner_html = "", word_span = ""; From d8e87d0eeb6ea73805e5c2a99c91a4071cbc4868 Mon Sep 17 00:00:00 2001 From: Fabio Neves Date: Tue, 23 Oct 2012 16:54:14 -0400 Subject: [PATCH 2/5] Updated changelog --- README.md | 2 ++ jqcloud/jqcloud-1.0.2.js | 2 +- jqcloud/jqcloud-1.0.2.min.js | 2 +- src/README.md.erb | 2 ++ 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 572b98d..1f398ad 100644 --- a/README.md +++ b/README.md @@ -151,6 +151,8 @@ If you make changes to the JavaScript source, to the README, to examples or to t ## Changelog +1.0.3 Brought back the `customClass` convenience option ([fzero](https://github.com/fzero)) + 1.0.2 Relative font sizes and easier to customize CSS (kudos to [daniel-toman](https://github.com/daniel-toman)) 1.0.1 Option to turn off URL encoding for links (thanks to [bboughton](https://github.com/bboughton)) diff --git a/jqcloud/jqcloud-1.0.2.js b/jqcloud/jqcloud-1.0.2.js index b8fa27a..61b70e1 100644 --- a/jqcloud/jqcloud-1.0.2.js +++ b/jqcloud/jqcloud-1.0.2.js @@ -6,7 +6,7 @@ * Copyright 2011, Luca Ongaro * Licensed under the MIT license. * - * Date: 2012-10-23 16:47:45 -0400 + * Date: 2012-10-23 16:53:11 -0400 */ (function( $ ) { diff --git a/jqcloud/jqcloud-1.0.2.min.js b/jqcloud/jqcloud-1.0.2.min.js index b1e2db4..65f1a96 100644 --- a/jqcloud/jqcloud-1.0.2.min.js +++ b/jqcloud/jqcloud-1.0.2.min.js @@ -6,6 +6,6 @@ * Copyright 2011, Luca Ongaro * Licensed under the MIT license. * - * Date: 2012-10-23 16:47:45 -0400 + * Date: 2012-10-23 16:53:11 -0400 */ (function(e){"use strict";e.fn.jQCloud=function(t,n){var r=this,i=r.attr("id")||Math.floor(Math.random()*1e6).toString(36),s={width:r.width(),height:r.height(),center:{x:(n&&n.width?n.width:r.width())/2,y:(n&&n.height?n.height:r.height())/2},delayedMode:t.length>50,shape:!1,encodeURI:!0,customClass:""};n=e.extend(s,n||{}),r.addClass("jqcloud").width(n.width).height(n.height),r.css("position")==="static"&&r.css("position","relative");var o=function(){var s=function(e,t){var n=function(e,t){return Math.abs(2*e.offsetLeft+e.offsetWidth-2*t.offsetLeft-t.offsetWidth)t.weight?-1:0});var u=n.shape==="rectangular"?18:2,a=[],f=n.width/n.height,l=function(o,l){var c=i+"_word_"+o,h="#"+c,p=6.28*Math.random(),d=0,v=0,m=0,g=5,y=n.customClass,b="",w="";l.html=e.extend(l.html,{id:c}),l.html&&l.html["class"]&&(y=l.html["class"],delete l.html["class"]),t[0].weight>t[t.length-1].weight&&(g=Math.round((l.weight-t[t.length-1].weight)/(t[0].weight-t[t.length-1].weight)*9)+1),w=e("").attr(l.html).addClass("w"+g+" "+y),l.link?(typeof l.link=="string"&&(l.link={href:l.link}),n.encodeURI&&(l.link=e.extend(l.link,{href:encodeURI(l.link.href).replace(/'/g,"%27")})),b=e("").attr(l.link).text(l.text)):b=l.text,w.append(b);if(!!l.handlers)for(var E in l.handlers)l.handlers.hasOwnProperty(E)&&typeof l.handlers[E]=="function"&&e(w).bind(E,l.handlers[E]);r.append(w);var S=w.width(),x=w.height(),T=n.center.x-S/2,N=n.center.y-x/2,C=w[0].style;C.position="absolute",C.left=T+"px",C.top=N+"px";while(s(document.getElementById(c),a)){if(n.shape==="rectangular"){v++,v*u>(1+Math.floor(m/2))*u*(m%4%2===0?1:f)&&(v=0,m++);switch(m%4){case 1:T+=u*f+Math.random()*2;break;case 2:N-=u+Math.random()*2;break;case 3:T-=u*f+Math.random()*2;break;case 0:N+=u+Math.random()*2}}else d+=u,p+=(o%2===0?1:-1)*u,T=n.center.x-S/2+d*Math.cos(p)*f,N=n.center.y+d*Math.sin(p)-x/2;C.left=T+"px",C.top=N+"px"}a.push(document.getElementById(c)),e.isFunction(l.afterWordRender)&&l.afterWordRender.call(w)},c=function(i){i=i||0;if(!r.is(":visible")){setTimeout(function(){c(i)},10);return}i Date: Mon, 3 Dec 2012 14:57:46 -0500 Subject: [PATCH 3/5] Added new features --- README.md | 14 +++-- jqcloud/jqcloud-1.0.2.js | 40 +++++++++++--- jqcloud/jqcloud-1.0.2.min.js | 11 ---- jqcloud/jqcloud.css | 104 ++++++++++++++++++++++++++--------- src/README.md.erb | 14 +++-- src/jqcloud/jqcloud.js.erb | 38 ++++++++++--- 6 files changed, 158 insertions(+), 63 deletions(-) delete mode 100644 jqcloud/jqcloud-1.0.2.min.js diff --git a/README.md b/README.md index 1f398ad..58306e1 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ The container element must be visible and have non-zero dimensions when you call ### Word Options For each word object in your word array, you need to specify the following mandatory attributes: - + * **text** (string): the word(s) text * **weight** (number): a number (integer or float) defining the relative importance of the word (such as the number of occurrencies, etc.). The range of values is arbitrary, and they will be linearly mapped to a discrete scale from 1 to 10. @@ -95,8 +95,12 @@ All cloud-wide configurations are optional, and the full list of available optio * **center** (object): The x and y coordinates of the center of the word cloud, relative to the container element (e.g.: {x: 300, y: 150}). Defaults to the center of the container element. * **afterCloudRender** (function): A callback function to be called after the whole cloud is fully rendered. * **delayedMode** (boolean): If true, words are rendered one after another with a tiny delay between each one. This prevents freezing of the browser when there are many words to be rendered. If false, the cloud will be rendered in one single shot. By default, delayedMode is true when there are more than 50 words. -* **shape** (string): the shape of the cloud. By default it is elliptic, but it can be set to `"rectangular"` to draw a rectangular-shaped cloud. -* **customClass** (string): custom CSS class to be added to all words. +* **shape** (string): The shape of the cloud. By default it is elliptic, but it can be set to `"rectangular"` to draw a rectangular-shaped cloud. +* **customClass** (string): Custom CSS class to be added to all words. +* **toggleClass** (string): Option to have more than one CSS class for each word weight (i.e. alternates between `wa1` and `wb1` instead of using just `w1`). +* **overlapFactor** (integer): How much the words can overlap each other (default: 0). +* **testBoundaries** (boolean): If true, jQCloud will make sure the words don't go past the container's boundaries (useful for long words). + ## Custom CSS guidelines @@ -104,7 +108,7 @@ The word cloud produced by jQCloud is made of pure HTML, so you can style it usi * Always specify the dimensions of the container element (div.jqcloud in jqcloud.css). * The CSS attribute 'position' of the container element must be explicitly declared and different from 'static' (if it is 'statis', jQCloud overwrites it to 'relative'). -* Specifying the style of the words (color, font, dimension, etc.) is super easy: words are wrapped in `` tags with ten levels of importance corresponding to the following classes (in descending order of importance): w10, w9, w8, w7, w6, w5, w4, w3, w2, w1. +* Specifying the style of the words (color, font, dimension, etc.) is super easy: words are wrapped in `` tags with ten levels of importance corresponding to the following classes (in descending order of importance): w10, w9, w8, w7, w6, w5, w4, w3, w2, w1 (or wa/wb + number if using toggleClasses). ## v1.0 and backward compatibility @@ -151,6 +155,8 @@ If you make changes to the JavaScript source, to the README, to examples or to t ## Changelog +1.0.4 Added `toggleClass`, `overlapFactor` and `testBoundaries` ([fzero](https://github.com/fzero)) + 1.0.3 Brought back the `customClass` convenience option ([fzero](https://github.com/fzero)) 1.0.2 Relative font sizes and easier to customize CSS (kudos to [daniel-toman](https://github.com/daniel-toman)) diff --git a/jqcloud/jqcloud-1.0.2.js b/jqcloud/jqcloud-1.0.2.js index 61b70e1..63beeff 100644 --- a/jqcloud/jqcloud-1.0.2.js +++ b/jqcloud/jqcloud-1.0.2.js @@ -3,12 +3,11 @@ * * Version 1.0.2 * - * Copyright 2011, Luca Ongaro + * Copyright 2012, Luca Ongaro, Fabio Neves * Licensed under the MIT license. * - * Date: 2012-10-23 16:53:11 -0400 + * Date: Mon Dec 03 14:55:56 -0500 2012 */ - (function( $ ) { "use strict"; $.fn.jQCloud = function(word_array, options) { @@ -22,13 +21,16 @@ width: $this.width(), height: $this.height(), center: { - x: ((options && options.width) ? options.width : $this.width()) / 2.0, - y: ((options && options.height) ? options.height : $this.height()) / 2.0 + x: ((options && options.width) ? options.width : $this.width() / 2.0), + y: ((options && options.height) ? options.height : $this.height() / 2.0) }, delayedMode: word_array.length > 50, shape: false, // It defaults to elliptic shape encodeURI: true, - customClass: '' + customClass: '', + toggleClasses: false, + overlapFactor: 0, + testBoundaries: false }; options = $.extend(default_options, options || {}); @@ -46,11 +48,20 @@ var hitTest = function(elem, other_elems){ // Pairwise overlap detection var overlapping = function(a, b){ - if (Math.abs(2.0*a.offsetLeft + a.offsetWidth - 2.0*b.offsetLeft - b.offsetWidth) < a.offsetWidth + b.offsetWidth) { - if (Math.abs(2.0*a.offsetTop + a.offsetHeight - 2.0*b.offsetTop - b.offsetHeight) < a.offsetHeight + b.offsetHeight) { + // First test against canvas boundaries (depending on options) + if (options.testBoundaries) { + if (a.offsetLeft < 0 || a.offsetTop < 0 || + a.offsetTop + a.offsetHeight > options.height || + a.offsetLeft + a.offsetWidth > options.width) { return true; } } + // Then test a/b overlap + var ov = 2.0 + Math.abs(options.overlapFactor / 10); + if ( (Math.abs(ov * a.offsetLeft + a.offsetWidth - ov * b.offsetLeft - b.offsetWidth) < a.offsetWidth + b.offsetWidth) && + (Math.abs(ov * a.offsetTop + a.offsetHeight - ov * b.offsetTop - b.offsetHeight) < a.offsetHeight + b.offsetHeight) ) { + return true; + } return false; }; var i = 0; @@ -75,6 +86,9 @@ already_placed_words = [], aspect_ratio = options.width / options.height; + // Toggles between 'wa' and 'wb' classes for color variation. + var color_toggle = 'a'; + // Function to draw a word, by moving it in spiral until it finds a suitable empty place. This will be iterated on each word. var drawOneWord = function(index, word) { // Define the ID attribute of the span that will wrap the word, and the associated jQuery selector string @@ -107,7 +121,15 @@ weight = Math.round((word.weight - word_array[word_array.length - 1].weight) / (word_array[0].weight - word_array[word_array.length - 1].weight) * 9.0) + 1; } - word_span = $('').attr(word.html).addClass('w' + weight + " " + custom_class); + + var class_prefix = 'w' + + if (options.toggleClasses) { + class_prefix = 'w' + color_toggle; + color_toggle = (color_toggle === 'a') ? 'b' : 'a'; + } + + word_span = $('').attr(word.html).addClass(class_prefix + weight + " " + custom_class); // Append link if word.url attribute was set if (word.link) { diff --git a/jqcloud/jqcloud-1.0.2.min.js b/jqcloud/jqcloud-1.0.2.min.js deleted file mode 100644 index 65f1a96..0000000 --- a/jqcloud/jqcloud-1.0.2.min.js +++ /dev/null @@ -1,11 +0,0 @@ -/*! - * jQCloud Plugin for jQuery - * - * Version 1.0.2 - * - * Copyright 2011, Luca Ongaro - * Licensed under the MIT license. - * - * Date: 2012-10-23 16:53:11 -0400 -*/ -(function(e){"use strict";e.fn.jQCloud=function(t,n){var r=this,i=r.attr("id")||Math.floor(Math.random()*1e6).toString(36),s={width:r.width(),height:r.height(),center:{x:(n&&n.width?n.width:r.width())/2,y:(n&&n.height?n.height:r.height())/2},delayedMode:t.length>50,shape:!1,encodeURI:!0,customClass:""};n=e.extend(s,n||{}),r.addClass("jqcloud").width(n.width).height(n.height),r.css("position")==="static"&&r.css("position","relative");var o=function(){var s=function(e,t){var n=function(e,t){return Math.abs(2*e.offsetLeft+e.offsetWidth-2*t.offsetLeft-t.offsetWidth)t.weight?-1:0});var u=n.shape==="rectangular"?18:2,a=[],f=n.width/n.height,l=function(o,l){var c=i+"_word_"+o,h="#"+c,p=6.28*Math.random(),d=0,v=0,m=0,g=5,y=n.customClass,b="",w="";l.html=e.extend(l.html,{id:c}),l.html&&l.html["class"]&&(y=l.html["class"],delete l.html["class"]),t[0].weight>t[t.length-1].weight&&(g=Math.round((l.weight-t[t.length-1].weight)/(t[0].weight-t[t.length-1].weight)*9)+1),w=e("").attr(l.html).addClass("w"+g+" "+y),l.link?(typeof l.link=="string"&&(l.link={href:l.link}),n.encodeURI&&(l.link=e.extend(l.link,{href:encodeURI(l.link.href).replace(/'/g,"%27")})),b=e("").attr(l.link).text(l.text)):b=l.text,w.append(b);if(!!l.handlers)for(var E in l.handlers)l.handlers.hasOwnProperty(E)&&typeof l.handlers[E]=="function"&&e(w).bind(E,l.handlers[E]);r.append(w);var S=w.width(),x=w.height(),T=n.center.x-S/2,N=n.center.y-x/2,C=w[0].style;C.position="absolute",C.left=T+"px",C.top=N+"px";while(s(document.getElementById(c),a)){if(n.shape==="rectangular"){v++,v*u>(1+Math.floor(m/2))*u*(m%4%2===0?1:f)&&(v=0,m++);switch(m%4){case 1:T+=u*f+Math.random()*2;break;case 2:N-=u+Math.random()*2;break;case 3:T-=u*f+Math.random()*2;break;case 0:N+=u+Math.random()*2}}else d+=u,p+=(o%2===0?1:-1)*u,T=n.center.x-S/2+d*Math.cos(p)*f,N=n.center.y+d*Math.sin(p)-x/2;C.left=T+"px",C.top=N+"px"}a.push(document.getElementById(c)),e.isFunction(l.afterWordRender)&&l.afterWordRender.call(w)},c=function(i){i=i||0;if(!r.is(":visible")){setTimeout(function(){c(i)},10);return}i` tags with ten levels of importance corresponding to the following classes (in descending order of importance): w10, w9, w8, w7, w6, w5, w4, w3, w2, w1. +* Specifying the style of the words (color, font, dimension, etc.) is super easy: words are wrapped in `` tags with ten levels of importance corresponding to the following classes (in descending order of importance): w10, w9, w8, w7, w6, w5, w4, w3, w2, w1 (or wa/wb + number if using toggleClasses). ## v1.0 and backward compatibility @@ -151,6 +155,8 @@ If you make changes to the JavaScript source, to the README, to examples or to t ## Changelog +1.0.4 Added `toggleClass`, `overlapFactor` and `testBoundaries` ([fzero](https://github.com/fzero)) + 1.0.3 Brought back the `customClass` convenience option ([fzero](https://github.com/fzero)) 1.0.2 Relative font sizes and easier to customize CSS (kudos to [daniel-toman](https://github.com/daniel-toman)) diff --git a/src/jqcloud/jqcloud.js.erb b/src/jqcloud/jqcloud.js.erb index 325a679..dc5e9c3 100644 --- a/src/jqcloud/jqcloud.js.erb +++ b/src/jqcloud/jqcloud.js.erb @@ -3,12 +3,11 @@ * * Version <%= version %> * - * Copyright 2011, Luca Ongaro + * Copyright 2012, Luca Ongaro, Fabio Neves * Licensed under the MIT license. * * Date: <%= Time.new.to_s %> */ - (function( $ ) { "use strict"; $.fn.jQCloud = function(word_array, options) { @@ -22,13 +21,16 @@ width: $this.width(), height: $this.height(), center: { - x: ((options && options.width) ? options.width : $this.width()) / 2.0, - y: ((options && options.height) ? options.height : $this.height()) / 2.0 + x: ((options && options.width) ? options.width : $this.width() / 2.0), + y: ((options && options.height) ? options.height : $this.height() / 2.0) }, delayedMode: word_array.length > 50, shape: false, // It defaults to elliptic shape encodeURI: true, - customClass: '' + customClass: '', + toggleClasses: false, + overlapFactor: 0, + testBoundaries: false }; options = $.extend(default_options, options || {}); @@ -46,11 +48,20 @@ var hitTest = function(elem, other_elems){ // Pairwise overlap detection var overlapping = function(a, b){ - if (Math.abs(2.0*a.offsetLeft + a.offsetWidth - 2.0*b.offsetLeft - b.offsetWidth) < a.offsetWidth + b.offsetWidth) { - if (Math.abs(2.0*a.offsetTop + a.offsetHeight - 2.0*b.offsetTop - b.offsetHeight) < a.offsetHeight + b.offsetHeight) { + // First test against canvas boundaries (depending on options) + if (options.testBoundaries) { + if (a.offsetLeft < 0 || a.offsetTop < 0 || + a.offsetTop + a.offsetHeight > options.height || + a.offsetLeft + a.offsetWidth > options.width) { return true; } } + // Then test a/b overlap + var ov = 2.0 + Math.abs(options.overlapFactor / 10); + if ( (Math.abs(ov * a.offsetLeft + a.offsetWidth - ov * b.offsetLeft - b.offsetWidth) < a.offsetWidth + b.offsetWidth) && + (Math.abs(ov * a.offsetTop + a.offsetHeight - ov * b.offsetTop - b.offsetHeight) < a.offsetHeight + b.offsetHeight) ) { + return true; + } return false; }; var i = 0; @@ -75,6 +86,9 @@ already_placed_words = [], aspect_ratio = options.width / options.height; + // Toggles between 'wa' and 'wb' classes for color variation. + var color_toggle = 'a'; + // Function to draw a word, by moving it in spiral until it finds a suitable empty place. This will be iterated on each word. var drawOneWord = function(index, word) { // Define the ID attribute of the span that will wrap the word, and the associated jQuery selector string @@ -107,7 +121,15 @@ weight = Math.round((word.weight - word_array[word_array.length - 1].weight) / (word_array[0].weight - word_array[word_array.length - 1].weight) * 9.0) + 1; } - word_span = $('').attr(word.html).addClass('w' + weight + " " + custom_class); + + var class_prefix = 'w' + + if (options.toggleClasses) { + class_prefix = 'w' + color_toggle; + color_toggle = (color_toggle === 'a') ? 'b' : 'a'; + } + + word_span = $('').attr(word.html).addClass(class_prefix + weight + " " + custom_class); // Append link if word.url attribute was set if (word.link) { From 8ee9f945fdadf323a6bfc3a699c0dacb3905166a Mon Sep 17 00:00:00 2001 From: Fabio Neves Date: Mon, 3 Dec 2012 15:01:39 -0500 Subject: [PATCH 4/5] Bumped version to 1.0.4 --- README.md | 4 ++-- examples/index.html | 2 +- examples/rectangular.html | 2 +- examples/vertical_words.html | 2 +- jqcloud/{jqcloud-1.0.2.js => jqcloud-1.0.4.js} | 4 ++-- jqcloud/jqcloud-1.0.4.min.js | 11 +++++++++++ test/index.html | 2 +- version.txt | 2 +- 8 files changed, 20 insertions(+), 9 deletions(-) rename jqcloud/{jqcloud-1.0.2.js => jqcloud-1.0.4.js} (99%) create mode 100644 jqcloud/jqcloud-1.0.4.min.js diff --git a/README.md b/README.md index 58306e1..999e353 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ You can see a demo here: http://www.lucaongaro.eu/demos/jqcloud/ Installing jQCloud is extremely simple: 1. Make sure to import jQuery in your project. -2. Download the jQCloud files. Place [jqcloud-1.0.2.js](https://raw.github.com/DukeLeNoir/jQCloud/master/jqcloud/jqcloud-1.0.2.js) (or the minified version [jqcloud-1.0.2.min.js](https://raw.github.com/DukeLeNoir/jQCloud/master/jqcloud/jqcloud-1.0.2.min.js)) and [jqcloud.css](https://raw.github.com/DukeLeNoir/jQCloud/master/jqcloud/jqcloud.css) somewhere in your project and import both of them in your HTML code. +2. Download the jQCloud files. Place [jqcloud-1.0.4.js](https://raw.github.com/DukeLeNoir/jQCloud/master/jqcloud/jqcloud-1.0.4.js) (or the minified version [jqcloud-1.0.4.min.js](https://raw.github.com/DukeLeNoir/jQCloud/master/jqcloud/jqcloud-1.0.4.min.js)) and [jqcloud.css](https://raw.github.com/DukeLeNoir/jQCloud/master/jqcloud/jqcloud.css) somewhere in your project and import both of them in your HTML code. You can easily substitute jqcloud.css with a custom CSS stylesheet following the guidelines explained later. @@ -31,7 +31,7 @@ Here is more detailed example: jQCloud Example - + - + - + - + - + diff --git a/version.txt b/version.txt index e6d5cb8..ee90284 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.0.2 \ No newline at end of file +1.0.4 From ced8aa4638149c4ba6ed90f0f8d5789fdc1ddea9 Mon Sep 17 00:00:00 2001 From: Fabio Neves Date: Mon, 3 Dec 2012 15:05:03 -0500 Subject: [PATCH 5/5] Correcting URLs --- README.md | 2 +- jqcloud/jqcloud-1.0.4.js | 2 +- jqcloud/jqcloud-1.0.4.min.js | 2 +- src/README.md.erb | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 999e353..c099591 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ You can see a demo here: http://www.lucaongaro.eu/demos/jqcloud/ Installing jQCloud is extremely simple: 1. Make sure to import jQuery in your project. -2. Download the jQCloud files. Place [jqcloud-1.0.4.js](https://raw.github.com/DukeLeNoir/jQCloud/master/jqcloud/jqcloud-1.0.4.js) (or the minified version [jqcloud-1.0.4.min.js](https://raw.github.com/DukeLeNoir/jQCloud/master/jqcloud/jqcloud-1.0.4.min.js)) and [jqcloud.css](https://raw.github.com/DukeLeNoir/jQCloud/master/jqcloud/jqcloud.css) somewhere in your project and import both of them in your HTML code. +2. Download the jQCloud files. Place [jqcloud-1.0.4.js](https://raw.github.com/fzero/jQCloud/master/jqcloud/jqcloud-1.0.4.js) (or the minified version [jqcloud-1.0.4.min.js](https://raw.github.com/fzero/jQCloud/master/jqcloud/jqcloud-1.0.4.min.js)) and [jqcloud.css](https://raw.github.com/fzero/jQCloud/master/jqcloud/jqcloud.css) somewhere in your project and import both of them in your HTML code. You can easily substitute jqcloud.css with a custom CSS stylesheet following the guidelines explained later. diff --git a/jqcloud/jqcloud-1.0.4.js b/jqcloud/jqcloud-1.0.4.js index 1acf373..c996e90 100644 --- a/jqcloud/jqcloud-1.0.4.js +++ b/jqcloud/jqcloud-1.0.4.js @@ -6,7 +6,7 @@ * Copyright 2012, Luca Ongaro, Fabio Neves * Licensed under the MIT license. * - * Date: 2012-12-03 15:01:12 -0500 + * Date: 2012-12-03 15:04:52 -0500 */ (function( $ ) { "use strict"; diff --git a/jqcloud/jqcloud-1.0.4.min.js b/jqcloud/jqcloud-1.0.4.min.js index 908ed8e..5065dc4 100644 --- a/jqcloud/jqcloud-1.0.4.min.js +++ b/jqcloud/jqcloud-1.0.4.min.js @@ -6,6 +6,6 @@ * Copyright 2012, Luca Ongaro, Fabio Neves * Licensed under the MIT license. * - * Date: 2012-12-03 15:01:12 -0500 + * Date: 2012-12-03 15:04:52 -0500 */ (function(e){"use strict";e.fn.jQCloud=function(t,n){var r=this,i=r.attr("id")||Math.floor(Math.random()*1e6).toString(36),s={width:r.width(),height:r.height(),center:{x:n&&n.width?n.width:r.width()/2,y:n&&n.height?n.height:r.height()/2},delayedMode:t.length>50,shape:!1,encodeURI:!0,customClass:"",toggleClasses:!1,overlapFactor:0,testBoundaries:!1};n=e.extend(s,n||{}),r.addClass("jqcloud").width(n.width).height(n.height),r.css("position")==="static"&&r.css("position","relative");var o=function(){var s=function(e,t){var r=function(e,t){if(n.testBoundaries)if(e.offsetLeft<0||e.offsetTop<0||e.offsetTop+e.offsetHeight>n.height||e.offsetLeft+e.offsetWidth>n.width)return!0;var r=2+Math.abs(n.overlapFactor/10);return Math.abs(r*e.offsetLeft+e.offsetWidth-r*t.offsetLeft-t.offsetWidth)t.weight?-1:0});var u=n.shape==="rectangular"?18:2,a=[],f=n.width/n.height,l="a",c=function(o,c){var h=i+"_word_"+o,p="#"+h,d=6.28*Math.random(),v=0,m=0,g=0,y=5,b=n.customClass,w="",E="";c.html=e.extend(c.html,{id:h}),c.html&&c.html["class"]&&(b=c.html["class"],delete c.html["class"]),t[0].weight>t[t.length-1].weight&&(y=Math.round((c.weight-t[t.length-1].weight)/(t[0].weight-t[t.length-1].weight)*9)+1);var S="w";n.toggleClasses&&(S="w"+l,l=l==="a"?"b":"a"),E=e("").attr(c.html).addClass(S+y+" "+b),c.link?(typeof c.link=="string"&&(c.link={href:c.link}),n.encodeURI&&(c.link=e.extend(c.link,{href:encodeURI(c.link.href).replace(/'/g,"%27")})),w=e("").attr(c.link).text(c.text)):w=c.text,E.append(w);if(!!c.handlers)for(var x in c.handlers)c.handlers.hasOwnProperty(x)&&typeof c.handlers[x]=="function"&&e(E).bind(x,c.handlers[x]);r.append(E);var T=E.width(),N=E.height(),C=n.center.x-T/2,k=n.center.y-N/2,L=E[0].style;L.position="absolute",L.left=C+"px",L.top=k+"px";while(s(document.getElementById(h),a)){if(n.shape==="rectangular"){m++,m*u>(1+Math.floor(g/2))*u*(g%4%2===0?1:f)&&(m=0,g++);switch(g%4){case 1:C+=u*f+Math.random()*2;break;case 2:k-=u+Math.random()*2;break;case 3:C-=u*f+Math.random()*2;break;case 0:k+=u+Math.random()*2}}else v+=u,d+=(o%2===0?1:-1)*u,C=n.center.x-T/2+v*Math.cos(d)*f,k=n.center.y+v*Math.sin(d)-N/2;L.left=C+"px",L.top=k+"px"}a.push(document.getElementById(h)),e.isFunction(c.afterWordRender)&&c.afterWordRender.call(E)},h=function(i){i=i||0;if(!r.is(":visible")){setTimeout(function(){h(i)},10);return}i.js](https://raw.github.com/DukeLeNoir/jQCloud/master/jqcloud/jqcloud-<%= version %>.js) (or the minified version [jqcloud-<%= version %>.min.js](https://raw.github.com/DukeLeNoir/jQCloud/master/jqcloud/jqcloud-<%= version %>.min.js)) and [jqcloud.css](https://raw.github.com/DukeLeNoir/jQCloud/master/jqcloud/jqcloud.css) somewhere in your project and import both of them in your HTML code. +2. Download the jQCloud files. Place [jqcloud-<%= version %>.js](https://raw.github.com/fzero/jQCloud/master/jqcloud/jqcloud-<%= version %>.js) (or the minified version [jqcloud-<%= version %>.min.js](https://raw.github.com/fzero/jQCloud/master/jqcloud/jqcloud-<%= version %>.min.js)) and [jqcloud.css](https://raw.github.com/fzero/jQCloud/master/jqcloud/jqcloud.css) somewhere in your project and import both of them in your HTML code. You can easily substitute jqcloud.css with a custom CSS stylesheet following the guidelines explained later.