├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── bower.json ├── dist ├── ng-tag-cloud-module.js └── ng-tag-cloud.min.js ├── examples ├── custom.css ├── index.html └── js │ └── app.js ├── package.json ├── src ├── css │ └── ng-tag-cloud.css └── ng-tag-cloud.js └── tests └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # angular-tag-cloud changelog 2 | 3 | ## v0.3.4 (19/12/2017) 4 | - Fix - not forcing to add click function 5 | - Add the ability to pass the weights into the component. 6 | (to make sure that words not overlapping because of custom css) 7 | 8 | ## v0.3.3 (24/01/2017) 9 | - Expose "afterCloudRender" function call to user (on-rendered attribute). 10 | - Fix - run afterCloudRender.call() only after cloud render .(instead on before and after) . [yonatan20](https://github.com/yonatan20). 11 | 12 | ## v0.3.2 (22/01/2017) 13 | - Expose the "delayed-mode" to user. 14 | - Removed redundant Second call for drawing the words. [yonatan20](https://github.com/yonatan20). 15 | 16 | ## v0.3.1 (06/11/2016) 17 | - Added the minified version. By [Ashok](https://github.com/ashokyadav006). 18 | 19 | ## v0.3.0 (25/04/2016) 20 | - Added `href` support to tags. Use `link` property on tag object to specify link value. 21 | 22 | ## v0.2.5 23 | - Added binding support. By [hazemhagrass](https://github.com/hazemhagrass). 24 | 25 | ## v0.2.0 26 | - Added `overflow` option. 27 | - Changed license to MIT 28 | - npm installation guide 29 | 30 | ## v0.1.0 31 | - Basic options: height, width. 32 | - Custom `css` styling support 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2016 Zeeshan Hyder 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # angular-tag-cloud 2 | Create simple and clean tag clouds in angular with this library. This library is jQuery-less, for-angular port of [Lucaong's jQCloud](https://github.com/lucaong/jQCloud) library. 3 | 4 | ## Installation 5 | 6 | ### General 7 | - Copy/move `ng-tag-cloud.js` from src dir in the package to your project dir 8 | - In your Angular app, add a dependency to your module as below: 9 | 10 | `angular.module('yourApp',['ngTagCloud',...]');` 11 | 12 | ### bower 13 | In your project dir, type the following command: 14 | 15 | ```sh 16 | $ bower install angular-tag-cloud 17 | ``` 18 | Then add a ` 22 | ``` 23 | And finally in your Angular app, add the dependency as: 24 | 25 | `angular.module('yourApp',['ngTagCloud',...]');` 26 | 27 | 28 | ### npm 29 | In your project dir, run the following command: 30 | 31 | ```sh 32 | $ npm install angular-tag-cloud 33 | ``` 34 | Then `require()` in your project source as: 35 | 36 | ```javascript 37 | require('angular-tag-cloud') 38 | ``` 39 | 40 | ### Styling 41 | 42 | I have included a default `css` file for default styling. Include it in your file: 43 | 44 | ```html 45 | 46 | ``` 47 | You can easily override it with your custom `css` class. 48 | 49 | ## Usage 50 | 51 | In your html file, use the component like this: 52 | 53 | ```html 54 | 55 | ``` 56 | or with your custom defined `css`. Please check [code example](https://github.com/zeeshanhyder/angular-tag-cloud/tree/master/examples) to see how to implement custom `css`. 57 | 58 | ```html 59 | 60 | ``` 61 | 62 | You can also pass the font-sizes to make sure that the words not overlapping: 63 | 64 | ```html 65 | 66 | ``` 67 | 68 | you can use 'px' or 'rem' as well. 69 | 70 | where your data is of `JSON` format as shown below. In your controller: 71 | 72 | ```javascript 73 | $scope.data = [ 74 | {text: "Lorem", weight: 15, link: "https://google.com"}, //if your tag has a link. 75 | {text: "Ipsum", weight: 9}, 76 | {text: "Dolor", weight: 6}, 77 | {text: "Sit", weight: 7}, 78 | {text: "Amet", weight: 5} 79 | // ...as many words as you want 80 | ]; 81 | ``` 82 | 83 | You can control whether there will be delay in word drawing like this: 84 | ```html 85 | 86 | ``` 87 | - True - 10 ms delay. 88 | - False - No delay. 89 | - Undefined - True only if there is more then 50 words. 90 | 91 | You can pass function that will invoke after word cloud is rendered: 92 | ```html 93 | 94 | ``` 95 | 96 | ## Examples 97 | 98 | Please check the examples directory to get the exact idea of what i am talking about. It's always better to check examples. 99 | 100 | Check code example [here](https://github.com/zeeshanhyder/angular-tag-cloud/tree/master/examples). 101 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-tag-cloud", 3 | "version": "0.3.4", 4 | "homepage": "https://github.com/zeeshanhyder/angular-tag-cloud", 5 | "authors": [ 6 | "Zeeshan " 7 | ], 8 | "description": "Create simple clean tag clouds in your Angular app. No other dependencies required.", 9 | "main": "src/ng-tag-cloud.js", 10 | "moduleType": [ 11 | "amd", 12 | "es6", 13 | "globals", 14 | "node" 15 | ], 16 | "keywords": [ 17 | "angular", 18 | "angular-tags", 19 | "angular-tag-cloud", 20 | "cloud-tags", 21 | "tag-cloud", 22 | "tags" 23 | ], 24 | "license": "MIT", 25 | "ignore": [ 26 | "**/.*", 27 | "node_modules", 28 | "bower_components", 29 | "test", 30 | "tests" 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /dist/ng-tag-cloud-module.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | 'use strict'; 3 | 4 | require('../src/ng-tag-cloud'); 5 | module.exports = 'ngTagCloud'; 6 | })(); -------------------------------------------------------------------------------- /dist/ng-tag-cloud.min.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | const WEIGHTS_LENGTH=10 3 | var ngTagCloud=angular.module("ngTagCloud",[]) 4 | ngTagCloud.directive("ngTagCloud",["$timeout","$log",function(e,t){return{restrict:"EA",scope:{cloudWidth:"=?",cloudHeight:"=?",cloudOverflow:"=?",cloudData:"=",cloudClick:"=",weights:"=",delayedMode:"=?",onRendered:"&"},template:"
",link:function(o,a,d){if(""===o.cloudData||void 0===o.cloudData)return void t.debug("ng-tag-cloud: No data passed. Please pass tags data as json. 50} 8 | i.afterCloudRender=function(){o.onRendered()} 9 | var n=angular.element(a)[0],l=(n.getAttribute("id")||Math.floor(1e6*Math.random()).toString(36),[]),r=function(){l=o.cloudData,n.style.width=i.width+"px",n.style.height=i.height+"px" 10 | var e={width:n.offsetWidth,height:n.offsetHeight,center:{x:i.width/2,y:i.height/2},delayedMode:i.delayedMode,shape:!1,encodeURI:!0,removeOverflowing:!o.cloudOverflow} 11 | o.weights&&(i.weights=o.weights),i=angular.extend(e,i||{})} 12 | r(),"static"!==n.style.position&&""!==n.style.position||(n.style.position="relative") 13 | var h=function(){a.empty() 14 | for(var t=function(e,t){var o=0 15 | for(o=0;ot.weight?-1:0}) 18 | var r="rectangular"===i.shape?18:2,h=[],f=i.width/i.height,c=function(e,a){var d,c=6.28*Math.random(),g=0,s=0,u=0,p=5 19 | l[0].weight>l[l.length-1].weight&&(p=Math.round((a.weight-l[l.length-1].weight)/(l[0].weight-l[l.length-1].weight)*9)+1),d=document.createElement("span"),d.className="w"+p 20 | var w=document.createTextNode(a.text) 21 | if(void 0!==a.link&&""!==a.link){if("string"==typeof a.link)var v=a.link 22 | i.encodeURI&&(v=encodeURI(v).replace(/'/g,"%27")) 23 | var y=document.createElement("a") 24 | y.href=v,y.appendChild(w),d.appendChild(y)}else d.appendChild(w) 25 | var m=angular.element 26 | o.cloudClick&&(m(d).click({text:a.text},o.cloudClick),m(d).addClass("cloud-word")),n.appendChild(d),i.weights&&(d.style.fontSize=i.weights[p-1]) 27 | var M=d.offsetWidth,C=d.offsetHeight,x=i.center.x-M/2,W=i.center.y-C/2,k=d.style 28 | for(k.position="absolute",k.left=x+"px",k.top=W+"px";t(d,h);){if("rectangular"===i.shape)switch(s++,s*r>(1+Math.floor(u/2))*r*(u%4%2==0?1:f)&&(s=0,u++),u%4){case 1:x+=r*f+2*Math.random() 29 | break 30 | case 2:W-=r+2*Math.random() 31 | break 32 | case 3:x-=r*f+2*Math.random() 33 | break 34 | case 0:W+=r+2*Math.random()}else g+=r,c+=(e%2==0?1:-1)*r,x=i.center.x-M/2+g*Math.cos(c)*f,W=i.center.y+g*Math.sin(c)-C/2 35 | k.left=x+"px",k.top=W+"px"}if(i.removeOverflowing&&(x<0||W<0||x+M>i.width||W+C>i.height))return void d.remove() 36 | h.push(d),"function"==typeof a.afterWordRender&&a.afterWordRender.call(d)},g=function(t){if(t=t||0,n.offsetWidth<=0&&n.offsetHeight<=0)return void e(function(){g(t)},10) 37 | t 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Angular Tag Cloud code example 11 | 12 | 13 | 14 | 15 |
16 |

Angular Tag Cloud (v0.3.0)

17 |
18 |
19 | 20 |

tag-cloud with default CSS:

21 | 22 |
23 | 24 | 25 | 26 | 27 | <ng-tag-cloud cloud-width="250" cloud-height="250" cloud-data="data"></ng-tag-cloud> //default width and height are 300px; 28 | 29 | 30 |
31 | 32 |

tag-cloud with custom CSS:

33 |
34 | 35 | 36 | 37 | <ng-tag-cloud class="custom-1" cloud-data="data"></ng-tag-cloud> 38 | 39 |
40 | 41 | 42 |

tag-cloud with overflow enabled:

43 | 44 |
45 | 46 | 47 | 48 | 49 | <ng-tag-cloud cloud-width="150" cloud-height="150" cloud-data="data" cloud-overflow="true"></ng-tag-cloud> 50 | 51 | 52 |
53 | 54 |
55 |

JSON Data Format:

56 | 57 | 58 | data = [ 59 | {text: "Lorem", weight: 15, link: "https://google.com"},
60 | {text: "Ipsum", weight: 9},
61 | {text: "Dolor", weight: 6},
62 | {text: "Sit", weight: 7},
63 | {text: "Amet", weight: 5}
64 | ]; 65 | 66 |
67 |
68 | 69 | 70 |
71 |

Custom CSS:

72 | 73 | 74 | div.custom-1 {
75 | font-family: "Helvetica", "Arial", sans-serif;
76 | font-size: 10px;
77 | line-height: normal;
78 | }
79 |
80 | div.custom-1 a {
81 | font-size: inherit;
82 | text-decoration: none;
83 | }
84 |
85 | div.custom-1 span.w10 { font-size: 550%; }
86 | div.custom-1 span.w9 { font-size: 500%; }
87 | div.custom-1 span.w8 { font-size: 450%; }
88 | div.custom-1 span.w7 { font-size: 400%; }
89 | div.custom-1 span.w6 { font-size: 350%; }
90 | div.custom-1 span.w5 { font-size: 300%; }
91 | div.custom-1 span.w4 { font-size: 250%; }
92 | div.custom-1 span.w3 { font-size: 200%; }
93 | div.custom-1 span.w2 { font-size: 150%; }
94 | div.custom-1 span.w1 { font-size: 100%; }
95 |
96 | /* colors */
97 |
98 | div.custom-1 { color: #09f; }
99 | div.custom-1 a { color: inherit; }
100 | div.custom-1 a:hover { color: #0df; }
101 | div.custom-1 a:hover { color: #0cf; }
102 | div.custom-1 span.w10 { color: grey; }
103 | div.custom-1 span.w9 { color: #0cf; }
104 | div.custom-1 span.w8 { color: #0cf; }
105 | div.custom-1 span.w7 { color: #39d; }
106 | div.custom-1 span.w6 { color: #90c5f0; }
107 | div.custom-1 span.w5 { color: #90a0dd; }
108 | div.custom-1 span.w4 { color: purple; }
109 | div.custom-1 span.w3 { color: green; }
110 | div.custom-1 span.w2 { color: blue; }
111 | div.custom-1 span.w1 { color: red; }
112 |
113 | /* layout */
114 |
115 | div.ng-tag-cloud {
116 | overflow: hidden;
117 | position: relative;
118 | }
119 |
120 | div.ng-tag-cloud span { padding: 0; }
121 |
122 |
123 |
124 | 125 |
126 | 127 | 130 | 131 | 132 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /examples/js/app.js: -------------------------------------------------------------------------------- 1 | var app = angular.module("tagcloudExample",["ngTagCloud"]); 2 | 3 | app.controller("MyController",function($scope){ 4 | 5 | 6 | $scope.data = [ 7 | {text: "Lorem", weight: 15, link: "https://google.com"}, 8 | {text: "Ipsum", weight: 9}, 9 | {text: "Dolor", weight: 6}, 10 | {text: "Sit", weight: 7}, 11 | {text: "Amet", weight: 5} 12 | // ...as many words as you want 13 | ]; 14 | }); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-tag-cloud", 3 | "version": "0.3.4", 4 | "description": "Create simple clean tag clouds in your Angular app. No other dependencies required.", 5 | "main": "dist/ng-tag-cloud-module.js", 6 | "directories": { 7 | "example": "examples", 8 | "test": "tests" 9 | }, 10 | "scripts": { 11 | "minify": "minify --output dist/ng-tag-cloud.min.js src/ng-tag-cloud.js", 12 | "test": "echo \"Error: no test specified\" && exit 1" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "git+https://github.com/zeeshanhyder/angular-tag-cloud.git" 17 | }, 18 | "keywords": [ 19 | "angular", 20 | "cloud-tags", 21 | "tag-cloud", 22 | "angular-tag-cloud", 23 | "tags" 24 | ], 25 | "author": "Zeeshan Hyder (https://github.com/zeeshanhyder)", 26 | "license": "MIT", 27 | "bugs": { 28 | "url": "https://github.com/zeeshanhyder/angular-tag-cloud/issues" 29 | }, 30 | "homepage": "https://github.com/zeeshanhyder/angular-tag-cloud#readme", 31 | "devDependencies": { 32 | "minifier": "^0.8.0" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/css/ng-tag-cloud.css: -------------------------------------------------------------------------------- 1 | /* fonts */ 2 | 3 | div.ng-tag-cloud { 4 | font-family: "Helvetica", "Arial", sans-serif; 5 | font-size: 10px; 6 | line-height: normal; 7 | } 8 | 9 | div.ng-tag-cloud a { 10 | font-size: inherit; 11 | text-decoration: none; 12 | } 13 | 14 | div.ng-tag-cloud span.w10 { font-size: 550%; } 15 | div.ng-tag-cloud span.w9 { font-size: 500%; } 16 | div.ng-tag-cloud span.w8 { font-size: 450%; } 17 | div.ng-tag-cloud span.w7 { font-size: 400%; } 18 | div.ng-tag-cloud span.w6 { font-size: 350%; } 19 | div.ng-tag-cloud span.w5 { font-size: 300%; } 20 | div.ng-tag-cloud span.w4 { font-size: 250%; } 21 | div.ng-tag-cloud span.w3 { font-size: 200%; } 22 | div.ng-tag-cloud span.w2 { font-size: 150%; } 23 | div.ng-tag-cloud span.w1 { font-size: 100%; } 24 | 25 | /* colors */ 26 | 27 | div.ng-tag-cloud { color: #09f; } 28 | div.ng-tag-cloud a { color: inherit; } 29 | div.ng-tag-cloud a:hover { color: #0df; } 30 | div.ng-tag-cloud a:hover { color: #0cf; } 31 | div.ng-tag-cloud span.w10 { color: #0cf; } 32 | div.ng-tag-cloud span.w9 { color: #0cf; } 33 | div.ng-tag-cloud span.w8 { color: #0cf; } 34 | div.ng-tag-cloud span.w7 { color: #39d; } 35 | div.ng-tag-cloud span.w6 { color: #90c5f0; } 36 | div.ng-tag-cloud span.w5 { color: #90a0dd; } 37 | div.ng-tag-cloud span.w4 { color: #90c5f0; } 38 | div.ng-tag-cloud span.w3 { color: #a0ddff; } 39 | div.ng-tag-cloud span.w2 { color: #99ccee; } 40 | div.ng-tag-cloud span.w1 { color: #aab5f0; } 41 | 42 | /* layout */ 43 | 44 | div.ng-tag-cloud { 45 | overflow: hidden; 46 | position: relative; 47 | } 48 | 49 | div.ng-tag-cloud span { padding: 0; } -------------------------------------------------------------------------------- /src/ng-tag-cloud.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | /** 3 | * Angular Tag Cloud (https://github.com/angular-tag-cloud) 4 | * Author: Zeeshan Hyder (https://github.com/zeeshanhyder) 5 | * Ported from: lucaong jQCloud (https://github.com/lucaong/jQCloud) 6 | * 7 | * Description: 8 | * Hello. This is the tag cloud library based in Angular and pure Javascript. It has no external dependencies. The library is ported from 9 | * lucaong's jQCloud(github link above). His library is based in jQuery and is a core dependency, where as this library is independent and needs * no external dependency. Please feel free to use this code and modify it according to your own wish. 10 | * 11 | * 12 | * Thankyou. 13 | */ 14 | const WEIGHTS_LENGTH = 10; 15 | 16 | var ngTagCloud = angular.module("ngTagCloud",[]); 17 | 18 | ngTagCloud.directive("ngTagCloud",["$timeout","$log",function($timeout,$log){ 19 | return { 20 | restrict: 'EA', 21 | scope: { 22 | cloudWidth: '=?', 23 | cloudHeight: '=?', 24 | cloudOverflow: '=?', 25 | cloudData: '=', 26 | cloudClick:'=', 27 | weights: '=', 28 | delayedMode:'=?', 29 | onRendered: '&' 30 | }, 31 | template: "
", 32 | link: function($scope,element,attrs){ 33 | 34 | if($scope.cloudData === "" || $scope.cloudData === undefined){ 35 | $log.debug("ng-tag-cloud: No data passed. Please pass tags data as json. 50) 54 | }; 55 | 56 | //Enable to execute function after cloud rendered 57 | options.afterCloudRender = function() { 58 | $scope.onRendered(); 59 | }; 60 | 61 | // Reference to the container element 62 | var $this = angular.element(element)[0]; 63 | // Namespace word ids to avoid collisions between multiple clouds 64 | var cloud_namespace = $this.getAttribute('id') || Math.floor((Math.random()*1000000)).toString(36); 65 | 66 | var word_array = []; 67 | var buildOptions = function () { 68 | word_array = $scope.cloudData; 69 | 70 | $this.style.width = options.width+"px"; 71 | $this.style.height = options.height+"px"; 72 | // Default options value 73 | var default_options = { 74 | width: $this.offsetWidth, 75 | height: $this.offsetHeight, 76 | center: { 77 | x: (options.width / 2.0), 78 | y: (options.height / 2.0) 79 | }, 80 | delayedMode: options.delayedMode, 81 | shape: false, // It defaults to elliptic shape 82 | encodeURI: true, 83 | removeOverflowing: $scope.cloudOverflow?false:true //TRUE by default. I know this is confusing, will be changed in next versions. 84 | }; 85 | if ($scope.weights){ 86 | options.weights = $scope.weights; 87 | } 88 | options = angular.extend(default_options, options || {}); 89 | }; 90 | 91 | buildOptions(); 92 | 93 | // Container's CSS position cannot be 'static' 94 | if ($this.style.position === "static" || $this.style.position === "") { 95 | $this.style.position = "relative"; 96 | } 97 | 98 | var drawWordCloud = function() { 99 | element.empty(); 100 | // Helper function to test if an element overlaps others 101 | var hitTest = function(elem, other_elems) { 102 | // Pairwise overlap detection 103 | var overlapping = function(a, b) { 104 | if (Math.abs(2.0*a.offsetLeft + a.offsetWidth - 2.0*b.offsetLeft - b.offsetWidth) < a.offsetWidth + b.offsetWidth) { 105 | if (Math.abs(2.0*a.offsetTop + a.offsetHeight - 2.0*b.offsetTop - b.offsetHeight) < a.offsetHeight + b.offsetHeight) { 106 | return true; 107 | } 108 | } 109 | return false; 110 | }; 111 | var i = 0; 112 | // Check elements for overlap one by one, stop and return false as soon as an overlap is found 113 | for(i = 0; i < other_elems.length; i++) { 114 | if (overlapping(elem, other_elems[i])) { 115 | return true; 116 | } 117 | } 118 | return false; 119 | }; 120 | 121 | // Make sure every weight is a number before sorting 122 | for (var i = 0; i < word_array.length; i++) { 123 | word_array[i].weight = parseFloat(word_array[i].weight, 10); 124 | } 125 | 126 | // Sort word_array from the word with the highest weight to the one with the lowest 127 | word_array.sort(function(a, b) { if (a.weight < b.weight) {return 1;} else if (a.weight > b.weight) {return -1;} else {return 0;} }); 128 | 129 | var step = (options.shape === "rectangular") ? 18.0 : 2.0, 130 | already_placed_words = [], 131 | aspect_ratio = options.width / options.height; 132 | 133 | // Function to draw a word, by moving it in spiral until it finds a suitable empty place. This will be iterated on each word. 134 | var drawOneWord = function(index, word) { 135 | // Define the ID attribute of the span that will wrap the word, and the associated jQuery selector string 136 | var word_id = cloud_namespace + "_word_" + index, 137 | word_selector = "#" + word_id, 138 | angle = 6.28 * Math.random(), 139 | radius = 0.0, 140 | 141 | // Only used if option.shape == 'rectangular' 142 | steps_in_direction = 0.0, 143 | quarter_turns = 0.0, 144 | 145 | weight = 5, 146 | custom_class = "", 147 | inner_html = "", 148 | word_span; 149 | 150 | // Leave out custom html for now. 151 | 152 | // Check if min(weight) > max(weight) otherwise use default 153 | if (word_array[0].weight > word_array[word_array.length - 1].weight) { 154 | // Linearly map the original weight to a discrete scale from 1 to 10 155 | weight = Math.round((word.weight - word_array[word_array.length - 1].weight) / 156 | (word_array[0].weight - word_array[word_array.length - 1].weight) * (WEIGHTS_LENGTH - 1)) + 1; 157 | } 158 | 159 | // Create a new span and insert node. 160 | word_span = document.createElement("span"); 161 | word_span.className = 'w' + weight; 162 | var textNode = document.createTextNode(word.text); 163 | 164 | // Append href if there's a link alongwith the tag 165 | if (word.link !== undefined && word.link !== "") { 166 | // If link is a string, then use it as the link href 167 | if (typeof word.link === "string") { 168 | var href = word.link; 169 | } 170 | 171 | // Extend link html options with defaults 172 | if ( options.encodeURI ) { 173 | href = encodeURI(href).replace(/'/g, "%27"); 174 | } 175 | 176 | var word_link = document.createElement("a"); 177 | word_link.href = href; 178 | word_link.appendChild(textNode); 179 | word_span.appendChild(word_link); 180 | } else { 181 | 182 | // If there's no link attribute 183 | word_span.appendChild(textNode); 184 | } 185 | 186 | 187 | // Bind handlers to words (though not really useful in this version!) 188 | // if (!!word.handlers) { 189 | // for (var prop in word.handlers) { 190 | // if (word.handlers.hasOwnProperty(prop) && typeof word.handlers[prop] === 'function') { 191 | // word_span.addEventListener(prop,word.handlers[prop]); 192 | // } 193 | // } 194 | // }$scope.cloudClick(word.text) 195 | var $ = angular.element; 196 | if($scope.cloudClick){ 197 | $(word_span).click({text:word.text},$scope.cloudClick); 198 | $(word_span).addClass('cloud-word'); 199 | } 200 | $this.appendChild(word_span); 201 | 202 | if(options.weights) { 203 | word_span.style.fontSize = options.weights[weight -1]; 204 | } 205 | 206 | var width = word_span.offsetWidth, 207 | height = word_span.offsetHeight, 208 | left = options.center.x - width / 2.0, 209 | top = options.center.y - height / 2.0; 210 | 211 | // Save a reference to the style property, for better performance 212 | var word_style = word_span.style; 213 | word_style.position = "absolute"; 214 | word_style.left = left + "px"; 215 | word_style.top = top + "px"; 216 | 217 | while(hitTest(word_span, already_placed_words)) { 218 | // option shape is 'rectangular' so move the word in a rectangular spiral 219 | if (options.shape === "rectangular") { //not enabled in this version. 220 | steps_in_direction++; 221 | if (steps_in_direction * step > (1 + Math.floor(quarter_turns / 2.0)) * step * ((quarter_turns % 4 % 2) === 0 ? 1 : aspect_ratio)) { 222 | steps_in_direction = 0.0; 223 | quarter_turns++; 224 | } 225 | switch(quarter_turns % 4) { 226 | case 1: 227 | left += step * aspect_ratio + Math.random() * 2.0; 228 | break; 229 | case 2: 230 | top -= step + Math.random() * 2.0; 231 | break; 232 | case 3: 233 | left -= step * aspect_ratio + Math.random() * 2.0; 234 | break; 235 | case 0: 236 | top += step + Math.random() * 2.0; 237 | break; 238 | } 239 | } else { // Default settings: elliptic spiral shape 240 | radius += step; 241 | angle += (index % 2 === 0 ? 1 : -1)*step; 242 | 243 | left = options.center.x - (width / 2.0) + (radius*Math.cos(angle)) * aspect_ratio; 244 | top = options.center.y + radius*Math.sin(angle) - (height / 2.0); 245 | } 246 | word_style.left = left + "px"; 247 | word_style.top = top + "px"; 248 | } 249 | 250 | // Don't render word if part of it would be outside the container 251 | if (options.removeOverflowing && (left < 0 || top < 0 || (left + width) > options.width || (top + height) > options.height)) { 252 | word_span.remove(); 253 | return; 254 | } 255 | 256 | 257 | already_placed_words.push(word_span); 258 | 259 | // Invoke callback if existing 260 | if (typeof(word.afterWordRender) === "function") { 261 | word.afterWordRender.call(word_span); 262 | } 263 | }; 264 | 265 | var drawOneWordDelayed = function(index) { 266 | index = index || 0; 267 | if ($this.offsetWidth<= 0 && $this.offsetHeight <= 0 ) { // if not visible then do not attempt to draw 268 | $timeout(function(){drawOneWordDelayed(index);},10); 269 | return; 270 | } 271 | if (index < word_array.length) { 272 | drawOneWord(index, word_array[index]); 273 | $timeout(function(){drawOneWordDelayed(index + 1);}, 10); 274 | } else if(index !== 0){ 275 | if (typeof(options.afterCloudRender) === "function") { 276 | options.afterCloudRender.call($this); 277 | } 278 | } 279 | }; 280 | 281 | // Iterate drawOneWord on every word. The way the iteration is done depends on the drawing mode (delayedMode is true or false) 282 | if (options.delayedMode){ 283 | drawOneWordDelayed(); 284 | } 285 | else { 286 | 287 | word_array.forEach( function(elem,index){ 288 | drawOneWord(index, elem); 289 | } ); 290 | if (typeof(options.afterCloudRender) === "function") { 291 | options.afterCloudRender.call($this); 292 | } 293 | } 294 | }; 295 | }, 296 | replace: true 297 | } 298 | }]); 299 | -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | # angular-tag-cloud tests 2 | 3 | There are no tests right now. --------------------------------------------------------------------------------