Index: arms/html/analysisResource/content-container.html =================================================================== diff -u -rac1734be0ebbb10fedf0b74cdb4bfccea36f2902 -rcadcfbea605edcf95ecdc2e0046ac64051670c0e --- arms/html/analysisResource/content-container.html (.../content-container.html) (revision ac1734be0ebbb10fedf0b74cdb4bfccea36f2902) +++ arms/html/analysisResource/content-container.html (.../content-container.html) (revision cadcfbea605edcf95ecdc2e0046ac64051670c0e) @@ -1039,7 +1039,7 @@
-
+
Index: arms/js/analysisResource.js =================================================================== diff -u -rac1734be0ebbb10fedf0b74cdb4bfccea36f2902 -rcadcfbea605edcf95ecdc2e0046ac64051670c0e --- arms/js/analysisResource.js (.../analysisResource.js) (revision ac1734be0ebbb10fedf0b74cdb4bfccea36f2902) +++ arms/js/analysisResource.js (.../analysisResource.js) (revision cadcfbea605edcf95ecdc2e0046ac64051670c0e) @@ -73,8 +73,10 @@ "../reference/jquery-plugins/dataTables-1.10.16/extensions/Buttons/js/buttons.print.js", "../reference/jquery-plugins/dataTables-1.10.16/extensions/Buttons/js/jszip.min.js", "../reference/jquery-plugins/dataTables-1.10.16/extensions/Buttons/js/pdfmake.min.js", + "../reference/jquery-plugins/jQCloud/dist/jqcloud.js", + "../reference/jquery-plugins/jQCloud/dist/jqcloud.css", "../arms/css/analysis/analysis.css", - "../arms/js/analysis/resource/sankey.js", + "../arms/js/analysis/resource/sankey.js" ] // 추가적인 플러그인 그룹들을 이곳에 추가하면 됩니다. ]; @@ -339,10 +341,48 @@ drawProductToManSankeyChart($("#selected_pdService").val(), selectedVersionId); drawManRequirementTreeMapChart($("#selected_pdService").val(), selectedVersionId); stackedHorizontalBar(); + wordCloud(); } }); } +function wordCloud() { + $('#tag-cloud').jQCloud('destroy'); + + const url = new UrlBuilder() + .setBaseUrl(`/auth-user/api/arms/dashboard/aggregation/flat`) + .addQueryParam('pdServiceLink', selectedPdServiceId) + .addQueryParam('pdServiceVersionLinks', selectedVersionId) + .addQueryParam('메인그룹필드', "assignee.assignee_accountId.keyword") + .addQueryParam('하위그룹필드들', "assignee.assignee_displayName.keyword") + .addQueryParam('크기', 1000) + .addQueryParam('하위크기', 1000) + .addQueryParam('컨텐츠보기여부', true) + .addQueryParam("isReqType", "ISSUE") + .build(); + + $.ajax({ + url: url, + type: "GET", + contentType: "application/json;charset=UTF-8", + dataType: "json", + progress: true, + statusCode: { + 200: function (data) { + let words = data['검색결과']["group_by_assignee.assignee_accountId.keyword"].map(item => ({ + text: item["하위검색결과"]["group_by_assignee.assignee_displayName.keyword"][0]["필드명"], + weight: item["하위검색결과"]["group_by_assignee.assignee_displayName.keyword"][0]["개수"] + })); + + $('#tag-cloud').jQCloud(words); + } + } + }); + + + +} + function bind_VersionData_By_PdService() { $(".multiple-select option").remove(); $.ajax({ @@ -370,6 +410,7 @@ drawProductToManSankeyChart(selectedPdServiceId, selectedVersionId); drawManRequirementTreeMapChart(selectedPdServiceId, selectedVersionId); stackedHorizontalBar(); + wordCloud(); if (data.length > 0) { Index: reference/jquery-plugins/jQCloud/.gitignore =================================================================== diff -u --- reference/jquery-plugins/jQCloud/.gitignore (revision 0) +++ reference/jquery-plugins/jQCloud/.gitignore (revision cadcfbea605edcf95ecdc2e0046ac64051670c0e) @@ -0,0 +1,6 @@ +/.idea +/*.iml +/.sass-cache +/bower_components +/node_modules +/npm-debug.log \ No newline at end of file Index: reference/jquery-plugins/jQCloud/.jshintrc =================================================================== diff -u --- reference/jquery-plugins/jQCloud/.jshintrc (revision 0) +++ reference/jquery-plugins/jQCloud/.jshintrc (revision cadcfbea605edcf95ecdc2e0046ac64051670c0e) @@ -0,0 +1,3 @@ +{ + "-W055": true // non standard constructor name +} \ No newline at end of file Index: reference/jquery-plugins/jQCloud/.scss-lint.yml =================================================================== diff -u --- reference/jquery-plugins/jQCloud/.scss-lint.yml (revision 0) +++ reference/jquery-plugins/jQCloud/.scss-lint.yml (revision cadcfbea605edcf95ecdc2e0046ac64051670c0e) @@ -0,0 +1,6 @@ +linters: + ColorVariable: + enabled: false + + HexLength: + enabled: false \ No newline at end of file Index: reference/jquery-plugins/jQCloud/Gruntfile.js =================================================================== diff -u --- reference/jquery-plugins/jQCloud/Gruntfile.js (revision 0) +++ reference/jquery-plugins/jQCloud/Gruntfile.js (revision cadcfbea605edcf95ecdc2e0046ac64051670c0e) @@ -0,0 +1,140 @@ +'use strict'; + +module.exports = function(grunt) { + require('time-grunt')(grunt); + require('jit-grunt')(grunt, { + scsslint: 'grunt-scss-lint' + }); + + grunt.initConfig({ + pkg: grunt.file.readJSON('package.json'), + + banner: '/*!\n' + + ' * jQCloud <%= pkg.version %>\n' + + ' * Copyright 2011 Luca Ongaro (http://www.lucaongaro.eu)\n' + + ' * Copyright 2013 Daniel White (http://www.developerdan.com)\n' + + ' * Copyright 2014-<%= grunt.template.today("yyyy") %> Damien "Mistic" Sorel (http://www.strangeplanet.fr)\n' + + ' * Licensed under MIT (http://opensource.org/licenses/MIT)\n' + + ' */', + + // add UMD + wrap: { + js: { + src: 'src/jqcloud.js', + dest: 'dist/jqcloud.js', + options: { + separator: '', + wrapper: function() { + return grunt.file.read('src/.wrapper.js').replace(/\r\n/g, '\n').split(/@@js\n/); + } + } + } + }, + + // add banner + concat: { + options: { + banner: '<%= banner %>\n', + stripBanners: false + }, + js: { + src: 'dist/jqcloud.js', + dest: 'dist/jqcloud.js' + }, + css: { + src: 'dist/jqcloud.css', + dest: 'dist/jqcloud.css' + } + }, + + // compress js + uglify: { + options: { + banner: '<%= banner %>\n' + }, + dist: { + src: 'dist/jqcloud.js', + dest: 'dist/jqcloud.min.js' + } + }, + + // parse scss + sass: { + options: { + sourcemap: 'none', + style: 'expanded' + }, + dist: { + src: 'src/jqcloud.scss', + dest: 'dist/jqcloud.css' + } + }, + + // compress css + cssmin: { + options: { + banner: '<%= banner %>', + keepSpecialComments: 0 + }, + dist: { + src: 'dist/jqcloud.css', + dest: 'dist/jqcloud.min.css' + } + }, + + // jshint tests + jshint: { + lib: { + options: { + jshintrc: '.jshintrc' + }, + src: 'src/jqcloud.js' + } + }, + + // scss tests + scsslint: { + lib: { + options: { + config: '.scss-lint.yml' + }, + src: 'src/jqcloud.scss' + } + }, + + // qunit test suite + qunit: { + all: { + options: { + urls: ['test/index.html'], + noGlobals: true + } + } + } + }); + + + grunt.registerTask('build_js', [ + 'wrap', + 'concat:js', + 'uglify' + ]); + + grunt.registerTask('build_css', [ + 'sass', + 'concat:css', + 'cssmin' + ]); + + grunt.registerTask('default', [ + 'build_js', + 'build_css' + ]); + + grunt.registerTask('test', [ + 'jshint', + 'scsslint', + 'default', + 'qunit' + ]); +}; \ No newline at end of file Index: reference/jquery-plugins/jQCloud/LICENSE.txt =================================================================== diff -u --- reference/jquery-plugins/jQCloud/LICENSE.txt (revision 0) +++ reference/jquery-plugins/jQCloud/LICENSE.txt (revision cadcfbea605edcf95ecdc2e0046ac64051670c0e) @@ -0,0 +1,24 @@ +The MIT License (MIT) + +Copyright (c) 2011 Luca Ongaro +Copyright (c) 2013 Daniel White +Copyright (c) 2014-2016 Damien Sorel + +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: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +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. \ No newline at end of file Index: reference/jquery-plugins/jQCloud/README.md =================================================================== diff -u --- reference/jquery-plugins/jQCloud/README.md (revision 0) +++ reference/jquery-plugins/jQCloud/README.md (revision cadcfbea605edcf95ecdc2e0046ac64051670c0e) @@ -0,0 +1,19 @@ +# jQCloud + +[![Bower version](https://img.shields.io/bower/v/jqcloud2.svg?style=flat-square)](http://mistic100.github.io/jQCloud) +[![NPM version](https://img.shields.io/npm/v/jqcloud2.svg?style=flat-square)](https://www.npmjs.com/package/jqcloud2) + +> ## _Long Term Support_ : I will not develop any more features for this library but contributions are more than welcomed and I will be happy to merge and publish new versions. + +## Beautiful word clouds with jQuery + +jQCloud is a jQuery plugin that builds neat and pure HTML + CSS word clouds and tag clouds that are actually shaped like a cloud (otherwise, why would we call them 'word clouds'?). + +Also available as [AngularJS directive](https://github.com/mistic100/angular-jqcloud) + +## Documentation + +http://mistic100.github.io/jQCloud + +## License +This library is available under the MIT license. Index: reference/jquery-plugins/jQCloud/bower.json =================================================================== diff -u --- reference/jquery-plugins/jQCloud/bower.json (revision 0) +++ reference/jquery-plugins/jQCloud/bower.json (revision cadcfbea605edcf95ecdc2e0046ac64051670c0e) @@ -0,0 +1,43 @@ +{ + "name": "jqcloud2", + "homepage": "https://github.com/mistic100/jQCloud", + "description": "jQuery plugin for drawing neat word clouds that actually look like clouds", + "authors": [{ + "name": "Luca Ongaro", + "homepage": "http://www.lucaongaro.eu" + }, { + "name": "Daniel White", + "homepage": "http://www.developerdan.com" + }, { + "name": "Damien \"Mistic\" Sorel", + "email": "contact@git.strangeplanet.fr", + "homepage": "http://www.strangeplanet.fr" + }], + "main": [ + "dist/jqcloud.js", + "dist/jqcloud.css" + ], + "dependencies" : { + "jquery": ">= 1.9.0" + }, + "keywords": [ + "cloud", + "jquery", + "keyword", + "tag" + ], + "license": "MIT", + "repository": { + "type": "git", + "url": "git://github.com/mistic100/jQCloud.git" + }, + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests", + "package.json", + "Gruntfile.js" + ] +} Index: reference/jquery-plugins/jQCloud/dist/jqcloud.css =================================================================== diff -u --- reference/jquery-plugins/jQCloud/dist/jqcloud.css (revision 0) +++ reference/jquery-plugins/jQCloud/dist/jqcloud.css (revision cadcfbea605edcf95ecdc2e0046ac64051670c0e) @@ -0,0 +1,66 @@ +/*! + * jQCloud 2.0.3 + * Copyright 2011 Luca Ongaro (http://www.lucaongaro.eu) + * Copyright 2013 Daniel White (http://www.developerdan.com) + * Copyright 2014-2017 Damien "Mistic" Sorel (http://www.strangeplanet.fr) + * Licensed under MIT (http://opensource.org/licenses/MIT) + */ +.jqcloud { + font: 10px "Helvetica", "Arial", sans-serif; + line-height: normal; + overflow: hidden; + position: relative; +} + +.jqcloud-word { + margin: 0; + padding: 0; +} +.jqcloud-word.w1 { + color: #aab5f0; + font-size: 100%; +} +.jqcloud-word.w2 { + color: #99ccee; + font-size: 150%; +} +.jqcloud-word.w3 { + color: #a0ddff; + font-size: 200%; +} +.jqcloud-word.w4 { + color: #90c5f0; + font-size: 250%; +} +.jqcloud-word.w5 { + color: #90a0dd; + font-size: 300%; +} +.jqcloud-word.w6 { + color: #90c5f0; + font-size: 350%; +} +.jqcloud-word.w7 { + color: #3399dd; + font-size: 400%; +} +.jqcloud-word.w8 { + color: #00ccff; + font-size: 450%; +} +.jqcloud-word.w9 { + color: #00ccff; + font-size: 500%; +} +.jqcloud-word.w10 { + color: #00ccff; + font-size: 550%; +} +.jqcloud-word a { + color: inherit; + font-size: inherit; + text-decoration: none; +} +.jqcloud-word a:hover { + color: #00ccff; +} Index: reference/jquery-plugins/jQCloud/dist/jqcloud.js =================================================================== diff -u --- reference/jquery-plugins/jQCloud/dist/jqcloud.js (revision 0) +++ reference/jquery-plugins/jQCloud/dist/jqcloud.js (revision cadcfbea605edcf95ecdc2e0046ac64051670c0e) @@ -0,0 +1,546 @@ +/*! + * jQCloud 2.0.3 + * Copyright 2011 Luca Ongaro (http://www.lucaongaro.eu) + * Copyright 2013 Daniel White (http://www.developerdan.com) + * Copyright 2014-2017 Damien "Mistic" Sorel (http://www.strangeplanet.fr) + * Licensed under MIT (http://opensource.org/licenses/MIT) + */ +(function(root, factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } + else if (typeof module === 'object' && module.exports) { + module.exports = factory(require('jquery')); + } + else { + factory(root.jQuery); + } +}(this, function($) { +'use strict'; + +/* + * Plugin class + */ +var jQCloud = function(element, word_array, options) { + this.$element = $(element); + + this.word_array = word_array || []; + this.options = options; + + this.sizeGenerator = null; + this.colorGenerator = null; + + // Data used internally + this.data = { + placed_words: [], + timeouts: {}, + namespace: null, + step: null, + angle: null, + aspect_ratio: null, + max_weight: null, + min_weight: null, + sizes: [], + colors: [] + }; + + this.initialize(); +}; + +jQCloud.DEFAULTS = { + width: 100, + height: 100, + center: { x: 0.5, y: 0.5 }, + steps: 10, + delay: null, + shape: 'elliptic', + classPattern: 'w{n}', + encodeURI: true, + removeOverflowing: true, + afterCloudRender: null, + autoResize: false, + colors: null, + fontSize: null, + template: null +}; + +jQCloud.prototype = { + initialize: function() { + // Set/Get dimensions + if (this.options.width) { + this.$element.width(this.options.width); + } + else { + this.options.width = this.$element.width(); + } + if (this.options.height) { + this.$element.height(this.options.height); + } + else { + this.options.height = this.$element.height(); + } + + // Default options value + this.options = $.extend(true, {}, jQCloud.DEFAULTS, this.options); + + // Ensure delay + if (this.options.delay === null) { + this.options.delay = this.word_array.length > 50 ? 10 : 0; + } + + // Backward compatibility + if (this.options.center.x > 1) { + this.options.center.x = this.options.center.x / this.options.width; + this.options.center.y = this.options.center.y / this.options.height; + } + + // Create colorGenerator function from options + // Direct function + if (typeof this.options.colors == 'function') { + this.colorGenerator = this.options.colors; + } + // Array of sizes + else if ($.isArray(this.options.colors)) { + var cl = this.options.colors.length; + if (cl > 0) { + // Fill the sizes array to X items + if (cl < this.options.steps) { + for (var i = cl; i < this.options.steps; i++) { + this.options.colors[i] = this.options.colors[cl - 1]; + } + } + + this.colorGenerator = function(weight) { + return this.options.colors[this.options.steps - weight]; + }; + } + } + + // Create sizeGenerator function from options + // Direct function + if (typeof this.options.fontSize == 'function') { + this.sizeGenerator = this.options.fontSize; + } + // Object with 'from' and 'to' + else if ($.isPlainObject(this.options.fontSize)) { + this.sizeGenerator = function(width, height, weight) { + var max = width * this.options.fontSize.from, + min = width * this.options.fontSize.to; + return Math.round(min + (max - min) * 1.0 / (this.options.steps - 1) * (weight - 1)) + 'px'; + }; + } + // Array of sizes + else if ($.isArray(this.options.fontSize)) { + var sl = this.options.fontSize.length; + if (sl > 0) { + // Fill the sizes array to X items + if (sl < this.options.steps) { + for (var j = sl; j < this.options.steps; j++) { + this.options.fontSize[j] = this.options.fontSize[sl - 1]; + } + } + + this.sizeGenerator = function(width, height, weight) { + return this.options.fontSize[this.options.steps - weight]; + }; + } + } + + this.data.angle = Math.random() * 6.28; + this.data.step = (this.options.shape === 'rectangular') ? 18.0 : 2.0; + this.data.aspect_ratio = this.options.width / this.options.height; + this.clearTimeouts(); + + // Namespace word ids to avoid collisions between multiple clouds + this.data.namespace = (this.$element.attr('id') || Math.floor((Math.random() * 1000000)).toString(36)) + '_word_'; + + this.$element.addClass('jqcloud'); + + // Container's CSS position cannot be 'static' + if (this.$element.css('position') === 'static') { + this.$element.css('position', 'relative'); + } + + // Delay execution so that the browser can render the page before the computatively intensive word cloud drawing + this.createTimeout($.proxy(this.drawWordCloud, this), 10); + + // Attach window resize event + if (this.options.autoResize) { + $(window).on('resize.' + this.data.namespace, throttle(this.resize, 50, this)); + } + }, + + // Helper function to keep track of timeouts so they can be destroyed + createTimeout: function(callback, time) { + var timeout = setTimeout($.proxy(function() { + delete this.data.timeouts[timeout]; + callback(); + }, this), time); + this.data.timeouts[timeout] = true; + }, + + // Destroy all timeouts + clearTimeouts: function() { + $.each(this.data.timeouts, function(key) { + clearTimeout(key); + }); + this.data.timeouts = {}; + }, + + // Pairwise overlap detection + overlapping: function(a, b) { + if (Math.abs(2.0 * a.left + a.width - 2.0 * b.left - b.width) < a.width + b.width) { + if (Math.abs(2.0 * a.top + a.height - 2.0 * b.top - b.height) < a.height + b.height) { + return true; + } + } + return false; + }, + + // Helper function to test if an element overlaps others + hitTest: function(elem) { + // Check elements for overlap one by one, stop and return false as soon as an overlap is found + for (var i = 0, l = this.data.placed_words.length; i < l; i++) { + if (this.overlapping(elem, this.data.placed_words[i])) { + return true; + } + } + return false; + }, + + // Initialize the drawing of the whole cloud + drawWordCloud: function() { + var i, l; + + this.$element.children('[id^="' + this.data.namespace + '"]').remove(); + + if (this.word_array.length === 0) { + return; + } + + // Make sure every weight is a number before sorting + for (i = 0, l = this.word_array.length; i < l; i++) { + this.word_array[i].weight = parseFloat(this.word_array[i].weight, 10); + } + + // Sort word_array from the word with the highest weight to the one with the lowest + this.word_array.sort(function(a, b) { + return b.weight - a.weight; + }); + + // Kepp trace of bounds + this.data.max_weight = this.word_array[0].weight; + this.data.min_weight = this.word_array[this.word_array.length - 1].weight; + + // Generate colors + this.data.colors = []; + if (this.colorGenerator) { + for (i = 0; i < this.options.steps; i++) { + this.data.colors.push(this.colorGenerator(i + 1)); + } + } + + // Generate font sizes + this.data.sizes = []; + if (this.sizeGenerator) { + for (i = 0; i < this.options.steps; i++) { + this.data.sizes.push(this.sizeGenerator(this.options.width, this.options.height, i + 1)); + } + } + + // Iterate drawOneWord on every word, immediately or with delay + if (this.options.delay > 0) { + this.drawOneWordDelayed(); + } + else { + for (i = 0, l = this.word_array.length; i < l; i++) { + this.drawOneWord(i, this.word_array[i]); + } + + if (typeof this.options.afterCloudRender === 'function') { + this.options.afterCloudRender.call(this.$element); + } + } + }, + + // Function to draw a word, by moving it in spiral until it finds a suitable empty place + drawOneWord: function(index, word) { + var word_id = this.data.namespace + index, + word_selector = '#' + word_id, + + // option.shape == 'elliptic' + angle = this.data.angle, + radius = 0.0, + + // option.shape == 'rectangular' + steps_in_direction = 0.0, + quarter_turns = 0.0, + + weight = Math.floor(this.options.steps / 2), + word_span, + word_size, + word_style; + + // Create word attr object + word.attr = $.extend({}, word.html, { id: word_id }); + + // Linearly map the original weight to a discrete scale from 1 to 10 + // Only if weights are different + if (this.data.max_weight != this.data.min_weight) { + weight = Math.round((word.weight - this.data.min_weight) * 1.0 * (this.options.steps - 1) / (this.data.max_weight - this.data.min_weight)) + 1; + } + word_span = $('').attr(word.attr); + + word_span.addClass('jqcloud-word'); + + // Apply class + if (this.options.classPattern) { + word_span.addClass(this.options.classPattern.replace('{n}', weight)); + } + + // Apply color + if (this.data.colors.length) { + word_span.css('color', this.data.colors[weight - 1]); + } + + // Apply color from word property + if (word.color) { + word_span.css('color', word.color); + } + + // Apply size + if (this.data.sizes.length) { + word_span.css('font-size', this.data.sizes[weight - 1]); + } + + //Render using template function if provided. + if (this.options.template) { + word_span.html(this.options.template(word)); + } else if (word.link) { + // Append link if word.link attribute was set + // If link is a string, then use it as the link href + if (typeof word.link === 'string') { + word.link = { href: word.link }; + } + + if (this.options.encodeURI) { + word.link.href = encodeURI(word.link.href).replace(/'/g, '%27'); + } + + word_span.append($('').attr(word.link).text(word.text)); + } + else { + word_span.text(word.text); + } + + // Bind handlers to words + if (word.handlers) { + word_span.on(word.handlers); + } + + this.$element.append(word_span); + + word_size = { + width: word_span.outerWidth(), + height: word_span.outerHeight() + }; + word_size.left = this.options.center.x * this.options.width - word_size.width / 2.0; + word_size.top = this.options.center.y * this.options.height - word_size.height / 2.0; + + // Save a reference to the style property, for better performance + word_style = word_span[0].style; + word_style.position = 'absolute'; + word_style.left = word_size.left + 'px'; + word_style.top = word_size.top + 'px'; + + while (this.hitTest(word_size)) { + // option shape is 'rectangular' so move the word in a rectangular spiral + if (this.options.shape === 'rectangular') { + steps_in_direction++; + + if (steps_in_direction * this.data.step > (1 + Math.floor(quarter_turns / 2.0)) * this.data.step * ((quarter_turns % 4 % 2) === 0 ? 1 : this.data.aspect_ratio)) { + steps_in_direction = 0.0; + quarter_turns++; + } + + switch (quarter_turns % 4) { + case 1: + word_size.left += this.data.step * this.data.aspect_ratio + Math.random() * 2.0; + break; + case 2: + word_size.top -= this.data.step + Math.random() * 2.0; + break; + case 3: + word_size.left -= this.data.step * this.data.aspect_ratio + Math.random() * 2.0; + break; + case 0: + word_size.top += this.data.step + Math.random() * 2.0; + break; + } + } + // Default settings: elliptic spiral shape + else { + radius += this.data.step; + angle += (index % 2 === 0 ? 1 : -1) * this.data.step; + + word_size.left = this.options.center.x * this.options.width - (word_size.width / 2.0) + (radius * Math.cos(angle)) * this.data.aspect_ratio; + word_size.top = this.options.center.y * this.options.height + radius * Math.sin(angle) - (word_size.height / 2.0); + } + word_style.left = word_size.left + 'px'; + word_style.top = word_size.top + 'px'; + } + + // Don't render word if part of it would be outside the container + if (this.options.removeOverflowing && ( + word_size.left < 0 || word_size.top < 0 || + (word_size.left + word_size.width) > this.options.width || + (word_size.top + word_size.height) > this.options.height + ) + ) { + word_span.remove(); + return; + } + + // Save position for further usage + this.data.placed_words.push(word_size); + + if (typeof word.afterWordRender === 'function') { + word.afterWordRender.call(word_span); + } + }, + + // Draw one word then recall the function after a delay + drawOneWordDelayed: function(index) { + index = index || 0; + + // if not visible then do not attempt to draw + if (!this.$element.is(':visible')) { + this.createTimeout($.proxy(function() { + this.drawOneWordDelayed(index); + }, this), 10); + + return; + } + + if (index < this.word_array.length) { + this.drawOneWord(index, this.word_array[index]); + + this.createTimeout($.proxy(function() { + this.drawOneWordDelayed(index + 1); + }, this), this.options.delay); + } + else { + if (typeof this.options.afterCloudRender == 'function') { + this.options.afterCloudRender.call(this.$element); + } + } + }, + + // Destroy any data and objects added by the plugin + destroy: function() { + if (this.options.autoResize) { + $(window).off('resize.' + this.data.namespace); + } + + this.clearTimeouts(); + this.$element.removeClass('jqcloud'); + this.$element.removeData('jqcloud'); + this.$element.children('[id^="' + this.data.namespace + '"]').remove(); + }, + + // Update the list of words + update: function(word_array) { + this.word_array = word_array; + this.data.placed_words = []; + + this.clearTimeouts(); + this.drawWordCloud(); + }, + + resize: function() { + var new_size = { + width: this.$element.width(), + height: this.$element.height() + }; + + if (new_size.width != this.options.width || new_size.height != this.options.height) { + this.options.width = new_size.width; + this.options.height = new_size.height; + this.data.aspect_ratio = this.options.width / this.options.height; + + this.update(this.word_array); + } + }, +}; + +/* + * Apply throttling to a callback + * @param callback {function} + * @param delay {int} milliseconds + * @param context {object|null} + * @return {function} + */ +function throttle(callback, delay, context) { + var state = { + pid: null, + last: 0 + }; + + return function() { + var elapsed = new Date().getTime() - state.last, + args = arguments, + that = this; + + function exec() { + state.last = new Date().getTime(); + return callback.apply(context || that, Array.prototype.slice.call(args)); + } + + if (elapsed > delay) { + return exec(); + } + else { + clearTimeout(state.pid); + state.pid = setTimeout(exec, delay - elapsed); + } + }; +} + +/* + * jQuery plugin + */ +$.fn.jQCloud = function(word_array, option) { + var args = arguments; + + return this.each(function() { + var $this = $(this), + data = $this.data('jqcloud'); + + if (!data && word_array === 'destroy') { + // Don't even try to initialize when called with 'destroy' + return; + } + if (!data) { + var options = typeof option === 'object' ? option : {}; + $this.data('jqcloud', (data = new jQCloud(this, word_array, options))); + } + else if (typeof word_array === 'string') { + data[word_array].apply(data, Array.prototype.slice.call(args, 1)); + } + }); +}; + +$.fn.jQCloud.defaults = { + set: function(options) { + $.extend(true, jQCloud.DEFAULTS, options); + }, + get: function(key) { + var options = jQCloud.DEFAULTS; + if (key) { + options = options[key]; + } + return $.extend(true, {}, options); + } +}; + +})); \ No newline at end of file Index: reference/jquery-plugins/jQCloud/dist/jqcloud.min.css =================================================================== diff -u --- reference/jquery-plugins/jQCloud/dist/jqcloud.min.css (revision 0) +++ reference/jquery-plugins/jQCloud/dist/jqcloud.min.css (revision cadcfbea605edcf95ecdc2e0046ac64051670c0e) @@ -0,0 +1 @@ +.jqcloud{font:10px Helvetica,Arial,sans-serif;line-height:normal;overflow:hidden;position:relative}.jqcloud-word{margin:0;padding:0}.jqcloud-word.w1{color:#aab5f0;font-size:100%}.jqcloud-word.w2{color:#9ce;font-size:150%}.jqcloud-word.w3{color:#a0ddff;font-size:200%}.jqcloud-word.w4{color:#90c5f0;font-size:250%}.jqcloud-word.w5{color:#90a0dd;font-size:300%}.jqcloud-word.w6{color:#90c5f0;font-size:350%}.jqcloud-word.w7{color:#39d;font-size:400%}.jqcloud-word.w8{color:#0cf;font-size:450%}.jqcloud-word.w9{color:#0cf;font-size:500%}.jqcloud-word.w10{color:#0cf;font-size:550%}.jqcloud-word a{color:inherit;font-size:inherit;text-decoration:none}.jqcloud-word a:hover{color:#0cf} \ No newline at end of file Index: reference/jquery-plugins/jQCloud/dist/jqcloud.min.js =================================================================== diff -u --- reference/jquery-plugins/jQCloud/dist/jqcloud.min.js (revision 0) +++ reference/jquery-plugins/jQCloud/dist/jqcloud.min.js (revision cadcfbea605edcf95ecdc2e0046ac64051670c0e) @@ -0,0 +1,8 @@ +/*! + * jQCloud 2.0.3 + * Copyright 2011 Luca Ongaro (http://www.lucaongaro.eu) + * Copyright 2013 Daniel White (http://www.developerdan.com) + * Copyright 2014-2017 Damien "Mistic" Sorel (http://www.strangeplanet.fr) + * Licensed under MIT (http://opensource.org/licenses/MIT) + */ +!function(a,b){"function"==typeof define&&define.amd?define(["jquery"],b):"object"==typeof module&&module.exports?module.exports=b(require("jquery")):b(a.jQuery)}(this,function(a){"use strict";function b(a,b,c){var d={pid:null,last:0};return function(){function e(){return d.last=(new Date).getTime(),a.apply(c||h,Array.prototype.slice.call(g))}var f=(new Date).getTime()-d.last,g=arguments,h=this;return f>b?e():(clearTimeout(d.pid),void(d.pid=setTimeout(e,b-f)))}}var c=function(b,c,d){this.$element=a(b),this.word_array=c||[],this.options=d,this.sizeGenerator=null,this.colorGenerator=null,this.data={placed_words:[],timeouts:{},namespace:null,step:null,angle:null,aspect_ratio:null,max_weight:null,min_weight:null,sizes:[],colors:[]},this.initialize()};c.DEFAULTS={width:100,height:100,center:{x:.5,y:.5},steps:10,delay:null,shape:"elliptic",classPattern:"w{n}",encodeURI:!0,removeOverflowing:!0,afterCloudRender:null,autoResize:!1,colors:null,fontSize:null,template:null},c.prototype={initialize:function(){if(this.options.width?this.$element.width(this.options.width):this.options.width=this.$element.width(),this.options.height?this.$element.height(this.options.height):this.options.height=this.$element.height(),this.options=a.extend(!0,{},c.DEFAULTS,this.options),null===this.options.delay&&(this.options.delay=this.word_array.length>50?10:0),this.options.center.x>1&&(this.options.center.x=this.options.center.x/this.options.width,this.options.center.y=this.options.center.y/this.options.height),"function"==typeof this.options.colors)this.colorGenerator=this.options.colors;else if(a.isArray(this.options.colors)){var d=this.options.colors.length;if(d>0){if(d0){if(f0)this.drawOneWordDelayed();else{for(a=0,b=this.word_array.length;a").attr(c.attr),d.addClass("jqcloud-word"),this.options.classPattern&&d.addClass(this.options.classPattern.replace("{n}",l)),this.data.colors.length&&d.css("color",this.data.colors[l-1]),c.color&&d.css("color",c.color),this.data.sizes.length&&d.css("font-size",this.data.sizes[l-1]),this.options.template?d.html(this.options.template(c)):c.link?("string"==typeof c.link&&(c.link={href:c.link}),this.options.encodeURI&&(c.link.href=encodeURI(c.link.href).replace(/'/g,"%27")),d.append(a("").attr(c.link).text(c.text))):d.text(c.text),c.handlers&&d.on(c.handlers),this.$element.append(d),e={width:d.outerWidth(),height:d.outerHeight()},e.left=this.options.center.x*this.options.width-e.width/2,e.top=this.options.center.y*this.options.height-e.height/2,f=d[0].style,f.position="absolute",f.left=e.left+"px",f.top=e.top+"px";this.hitTest(e);){if("rectangular"===this.options.shape)switch(j++,j*this.data.step>(1+Math.floor(k/2))*this.data.step*(k%4%2===0?1:this.data.aspect_ratio)&&(j=0,k++),k%4){case 1:e.left+=this.data.step*this.data.aspect_ratio+2*Math.random();break;case 2:e.top-=this.data.step+2*Math.random();break;case 3:e.left-=this.data.step*this.data.aspect_ratio+2*Math.random();break;case 0:e.top+=this.data.step+2*Math.random()}else i+=this.data.step,h+=(b%2===0?1:-1)*this.data.step,e.left=this.options.center.x*this.options.width-e.width/2+i*Math.cos(h)*this.data.aspect_ratio,e.top=this.options.center.y*this.options.height+i*Math.sin(h)-e.height/2;f.left=e.left+"px",f.top=e.top+"px"}return this.options.removeOverflowing&&(e.left<0||e.top<0||e.left+e.width>this.options.width||e.top+e.height>this.options.height)?void d.remove():(this.data.placed_words.push(e),void("function"==typeof c.afterWordRender&&c.afterWordRender.call(d)))},drawOneWordDelayed:function(b){return b=b||0,this.$element.is(":visible")?void(b= 1.9.0" + }, + "devDependencies": { + "grunt": "^1.0.0", + "grunt-contrib-concat": "^1.0.0", + "grunt-contrib-cssmin": "^1.0.0", + "grunt-contrib-jshint": "^1.0.0", + "grunt-contrib-qunit": "^0.7.0", + "grunt-contrib-sass": "^1.0.0", + "grunt-contrib-uglify": "^1.0.0", + "grunt-scss-lint": "^0.3.8", + "grunt-wrap": "^0.3.0", + "jit-grunt": "^0.10.0", + "qunitjs": "^1.23.0", + "time-grunt": "^1.3.0" + }, + "keywords": [ + "cloud", + "jquery", + "keyword", + "tag" + ], + "license": "MIT", + "repository": { + "type": "git", + "url": "git://github.com/mistic100/jQCloud.git" + }, + "bugs": { + "url": "https://github.com/mistic100/jQCloud/issues" + }, + "scripts": { + "test": "grunt test" + } +} Index: reference/jquery-plugins/jQCloud/src/.wrapper.js =================================================================== diff -u --- reference/jquery-plugins/jQCloud/src/.wrapper.js (revision 0) +++ reference/jquery-plugins/jQCloud/src/.wrapper.js (revision cadcfbea605edcf95ecdc2e0046ac64051670c0e) @@ -0,0 +1,16 @@ +(function(root, factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } + else if (typeof module === 'object' && module.exports) { + module.exports = factory(require('jquery')); + } + else { + factory(root.jQuery); + } +}(this, function($) { +'use strict'; + +@@js + +})); \ No newline at end of file Index: reference/jquery-plugins/jQCloud/src/jqcloud.js =================================================================== diff -u --- reference/jquery-plugins/jQCloud/src/jqcloud.js (revision 0) +++ reference/jquery-plugins/jQCloud/src/jqcloud.js (revision cadcfbea605edcf95ecdc2e0046ac64051670c0e) @@ -0,0 +1,524 @@ +/* + * Plugin class + */ +var jQCloud = function(element, word_array, options) { + this.$element = $(element); + + this.word_array = word_array || []; + this.options = options; + + this.sizeGenerator = null; + this.colorGenerator = null; + + // Data used internally + this.data = { + placed_words: [], + timeouts: {}, + namespace: null, + step: null, + angle: null, + aspect_ratio: null, + max_weight: null, + min_weight: null, + sizes: [], + colors: [] + }; + + this.initialize(); +}; + +jQCloud.DEFAULTS = { + width: 100, + height: 100, + center: { x: 0.5, y: 0.5 }, + steps: 10, + delay: null, + shape: 'elliptic', + classPattern: 'w{n}', + encodeURI: true, + removeOverflowing: true, + afterCloudRender: null, + autoResize: false, + colors: null, + fontSize: null, + template: null +}; + +jQCloud.prototype = { + initialize: function() { + // Set/Get dimensions + if (this.options.width) { + this.$element.width(this.options.width); + } + else { + this.options.width = this.$element.width(); + } + if (this.options.height) { + this.$element.height(this.options.height); + } + else { + this.options.height = this.$element.height(); + } + + // Default options value + this.options = $.extend(true, {}, jQCloud.DEFAULTS, this.options); + + // Ensure delay + if (this.options.delay === null) { + this.options.delay = this.word_array.length > 50 ? 10 : 0; + } + + // Backward compatibility + if (this.options.center.x > 1) { + this.options.center.x = this.options.center.x / this.options.width; + this.options.center.y = this.options.center.y / this.options.height; + } + + // Create colorGenerator function from options + // Direct function + if (typeof this.options.colors == 'function') { + this.colorGenerator = this.options.colors; + } + // Array of sizes + else if ($.isArray(this.options.colors)) { + var cl = this.options.colors.length; + if (cl > 0) { + // Fill the sizes array to X items + if (cl < this.options.steps) { + for (var i = cl; i < this.options.steps; i++) { + this.options.colors[i] = this.options.colors[cl - 1]; + } + } + + this.colorGenerator = function(weight) { + return this.options.colors[this.options.steps - weight]; + }; + } + } + + // Create sizeGenerator function from options + // Direct function + if (typeof this.options.fontSize == 'function') { + this.sizeGenerator = this.options.fontSize; + } + // Object with 'from' and 'to' + else if ($.isPlainObject(this.options.fontSize)) { + this.sizeGenerator = function(width, height, weight) { + var max = width * this.options.fontSize.from, + min = width * this.options.fontSize.to; + return Math.round(min + (max - min) * 1.0 / (this.options.steps - 1) * (weight - 1)) + 'px'; + }; + } + // Array of sizes + else if ($.isArray(this.options.fontSize)) { + var sl = this.options.fontSize.length; + if (sl > 0) { + // Fill the sizes array to X items + if (sl < this.options.steps) { + for (var j = sl; j < this.options.steps; j++) { + this.options.fontSize[j] = this.options.fontSize[sl - 1]; + } + } + + this.sizeGenerator = function(width, height, weight) { + return this.options.fontSize[this.options.steps - weight]; + }; + } + } + + this.data.angle = Math.random() * 6.28; + this.data.step = (this.options.shape === 'rectangular') ? 18.0 : 2.0; + this.data.aspect_ratio = this.options.width / this.options.height; + this.clearTimeouts(); + + // Namespace word ids to avoid collisions between multiple clouds + this.data.namespace = (this.$element.attr('id') || Math.floor((Math.random() * 1000000)).toString(36)) + '_word_'; + + this.$element.addClass('jqcloud'); + + // Container's CSS position cannot be 'static' + if (this.$element.css('position') === 'static') { + this.$element.css('position', 'relative'); + } + + // Delay execution so that the browser can render the page before the computatively intensive word cloud drawing + this.createTimeout($.proxy(this.drawWordCloud, this), 10); + + // Attach window resize event + if (this.options.autoResize) { + $(window).on('resize.' + this.data.namespace, throttle(this.resize, 50, this)); + } + }, + + // Helper function to keep track of timeouts so they can be destroyed + createTimeout: function(callback, time) { + var timeout = setTimeout($.proxy(function() { + delete this.data.timeouts[timeout]; + callback(); + }, this), time); + this.data.timeouts[timeout] = true; + }, + + // Destroy all timeouts + clearTimeouts: function() { + $.each(this.data.timeouts, function(key) { + clearTimeout(key); + }); + this.data.timeouts = {}; + }, + + // Pairwise overlap detection + overlapping: function(a, b) { + if (Math.abs(2.0 * a.left + a.width - 2.0 * b.left - b.width) < a.width + b.width) { + if (Math.abs(2.0 * a.top + a.height - 2.0 * b.top - b.height) < a.height + b.height) { + return true; + } + } + return false; + }, + + // Helper function to test if an element overlaps others + hitTest: function(elem) { + // Check elements for overlap one by one, stop and return false as soon as an overlap is found + for (var i = 0, l = this.data.placed_words.length; i < l; i++) { + if (this.overlapping(elem, this.data.placed_words[i])) { + return true; + } + } + return false; + }, + + // Initialize the drawing of the whole cloud + drawWordCloud: function() { + var i, l; + + this.$element.children('[id^="' + this.data.namespace + '"]').remove(); + + if (this.word_array.length === 0) { + return; + } + + // Make sure every weight is a number before sorting + for (i = 0, l = this.word_array.length; i < l; i++) { + this.word_array[i].weight = parseFloat(this.word_array[i].weight, 10); + } + + // Sort word_array from the word with the highest weight to the one with the lowest + this.word_array.sort(function(a, b) { + return b.weight - a.weight; + }); + + // Kepp trace of bounds + this.data.max_weight = this.word_array[0].weight; + this.data.min_weight = this.word_array[this.word_array.length - 1].weight; + + // Generate colors + this.data.colors = []; + if (this.colorGenerator) { + for (i = 0; i < this.options.steps; i++) { + this.data.colors.push(this.colorGenerator(i + 1)); + } + } + + // Generate font sizes + this.data.sizes = []; + if (this.sizeGenerator) { + for (i = 0; i < this.options.steps; i++) { + this.data.sizes.push(this.sizeGenerator(this.options.width, this.options.height, i + 1)); + } + } + + // Iterate drawOneWord on every word, immediately or with delay + if (this.options.delay > 0) { + this.drawOneWordDelayed(); + } + else { + for (i = 0, l = this.word_array.length; i < l; i++) { + this.drawOneWord(i, this.word_array[i]); + } + + if (typeof this.options.afterCloudRender === 'function') { + this.options.afterCloudRender.call(this.$element); + } + } + }, + + // Function to draw a word, by moving it in spiral until it finds a suitable empty place + drawOneWord: function(index, word) { + var word_id = this.data.namespace + index, + word_selector = '#' + word_id, + + // option.shape == 'elliptic' + angle = this.data.angle, + radius = 0.0, + + // option.shape == 'rectangular' + steps_in_direction = 0.0, + quarter_turns = 0.0, + + weight = Math.floor(this.options.steps / 2), + word_span, + word_size, + word_style; + + // Create word attr object + word.attr = $.extend({}, word.html, { id: word_id }); + + // Linearly map the original weight to a discrete scale from 1 to 10 + // Only if weights are different + if (this.data.max_weight != this.data.min_weight) { + weight = Math.round((word.weight - this.data.min_weight) * 1.0 * (this.options.steps - 1) / (this.data.max_weight - this.data.min_weight)) + 1; + } + word_span = $('').attr(word.attr); + + word_span.addClass('jqcloud-word'); + + // Apply class + if (this.options.classPattern) { + word_span.addClass(this.options.classPattern.replace('{n}', weight)); + } + + // Apply color + if (this.data.colors.length) { + word_span.css('color', this.data.colors[weight - 1]); + } + + // Apply color from word property + if (word.color) { + word_span.css('color', word.color); + } + + // Apply size + if (this.data.sizes.length) { + word_span.css('font-size', this.data.sizes[weight - 1]); + } + + //Render using template function if provided. + if (this.options.template) { + word_span.html(this.options.template(word)); + } else if (word.link) { + // Append link if word.link attribute was set + // If link is a string, then use it as the link href + if (typeof word.link === 'string') { + word.link = { href: word.link }; + } + + if (this.options.encodeURI) { + word.link.href = encodeURI(word.link.href).replace(/'/g, '%27'); + } + + word_span.append($('').attr(word.link).text(word.text)); + } + else { + word_span.text(word.text); + } + + // Bind handlers to words + if (word.handlers) { + word_span.on(word.handlers); + } + + this.$element.append(word_span); + + word_size = { + width: word_span.outerWidth(), + height: word_span.outerHeight() + }; + word_size.left = this.options.center.x * this.options.width - word_size.width / 2.0; + word_size.top = this.options.center.y * this.options.height - word_size.height / 2.0; + + // Save a reference to the style property, for better performance + word_style = word_span[0].style; + word_style.position = 'absolute'; + word_style.left = word_size.left + 'px'; + word_style.top = word_size.top + 'px'; + + while (this.hitTest(word_size)) { + // option shape is 'rectangular' so move the word in a rectangular spiral + if (this.options.shape === 'rectangular') { + steps_in_direction++; + + if (steps_in_direction * this.data.step > (1 + Math.floor(quarter_turns / 2.0)) * this.data.step * ((quarter_turns % 4 % 2) === 0 ? 1 : this.data.aspect_ratio)) { + steps_in_direction = 0.0; + quarter_turns++; + } + + switch (quarter_turns % 4) { + case 1: + word_size.left += this.data.step * this.data.aspect_ratio + Math.random() * 2.0; + break; + case 2: + word_size.top -= this.data.step + Math.random() * 2.0; + break; + case 3: + word_size.left -= this.data.step * this.data.aspect_ratio + Math.random() * 2.0; + break; + case 0: + word_size.top += this.data.step + Math.random() * 2.0; + break; + } + } + // Default settings: elliptic spiral shape + else { + radius += this.data.step; + angle += (index % 2 === 0 ? 1 : -1) * this.data.step; + + word_size.left = this.options.center.x * this.options.width - (word_size.width / 2.0) + (radius * Math.cos(angle)) * this.data.aspect_ratio; + word_size.top = this.options.center.y * this.options.height + radius * Math.sin(angle) - (word_size.height / 2.0); + } + word_style.left = word_size.left + 'px'; + word_style.top = word_size.top + 'px'; + } + + // Don't render word if part of it would be outside the container + if (this.options.removeOverflowing && ( + word_size.left < 0 || word_size.top < 0 || + (word_size.left + word_size.width) > this.options.width || + (word_size.top + word_size.height) > this.options.height + ) + ) { + word_span.remove(); + return; + } + + // Save position for further usage + this.data.placed_words.push(word_size); + + if (typeof word.afterWordRender === 'function') { + word.afterWordRender.call(word_span); + } + }, + + // Draw one word then recall the function after a delay + drawOneWordDelayed: function(index) { + index = index || 0; + + // if not visible then do not attempt to draw + if (!this.$element.is(':visible')) { + this.createTimeout($.proxy(function() { + this.drawOneWordDelayed(index); + }, this), 10); + + return; + } + + if (index < this.word_array.length) { + this.drawOneWord(index, this.word_array[index]); + + this.createTimeout($.proxy(function() { + this.drawOneWordDelayed(index + 1); + }, this), this.options.delay); + } + else { + if (typeof this.options.afterCloudRender == 'function') { + this.options.afterCloudRender.call(this.$element); + } + } + }, + + // Destroy any data and objects added by the plugin + destroy: function() { + if (this.options.autoResize) { + $(window).off('resize.' + this.data.namespace); + } + + this.clearTimeouts(); + this.$element.removeClass('jqcloud'); + this.$element.removeData('jqcloud'); + this.$element.children('[id^="' + this.data.namespace + '"]').remove(); + }, + + // Update the list of words + update: function(word_array) { + this.word_array = word_array; + this.data.placed_words = []; + + this.clearTimeouts(); + this.drawWordCloud(); + }, + + resize: function() { + var new_size = { + width: this.$element.width(), + height: this.$element.height() + }; + + if (new_size.width != this.options.width || new_size.height != this.options.height) { + this.options.width = new_size.width; + this.options.height = new_size.height; + this.data.aspect_ratio = this.options.width / this.options.height; + + this.update(this.word_array); + } + }, +}; + +/* + * Apply throttling to a callback + * @param callback {function} + * @param delay {int} milliseconds + * @param context {object|null} + * @return {function} + */ +function throttle(callback, delay, context) { + var state = { + pid: null, + last: 0 + }; + + return function() { + var elapsed = new Date().getTime() - state.last, + args = arguments, + that = this; + + function exec() { + state.last = new Date().getTime(); + return callback.apply(context || that, Array.prototype.slice.call(args)); + } + + if (elapsed > delay) { + return exec(); + } + else { + clearTimeout(state.pid); + state.pid = setTimeout(exec, delay - elapsed); + } + }; +} + +/* + * jQuery plugin + */ +$.fn.jQCloud = function(word_array, option) { + var args = arguments; + + return this.each(function() { + var $this = $(this), + data = $this.data('jqcloud'); + + if (!data && word_array === 'destroy') { + // Don't even try to initialize when called with 'destroy' + return; + } + if (!data) { + var options = typeof option === 'object' ? option : {}; + $this.data('jqcloud', (data = new jQCloud(this, word_array, options))); + } + else if (typeof word_array === 'string') { + data[word_array].apply(data, Array.prototype.slice.call(args, 1)); + } + }); +}; + +$.fn.jQCloud.defaults = { + set: function(options) { + $.extend(true, jQCloud.DEFAULTS, options); + }, + get: function(key) { + var options = jQCloud.DEFAULTS; + if (key) { + options = options[key]; + } + return $.extend(true, {}, options); + } +}; Index: reference/jquery-plugins/jQCloud/src/jqcloud.scss =================================================================== diff -u --- reference/jquery-plugins/jQCloud/src/jqcloud.scss (revision 0) +++ reference/jquery-plugins/jQCloud/src/jqcloud.scss (revision cadcfbea605edcf95ecdc2e0046ac64051670c0e) @@ -0,0 +1,45 @@ +$jqcloud-font: 10px "Helvetica", "Arial", sans-serif; + +$jqcloud-link-hover-color: #00ccff; + +$jqcloud-words: ( + w1: (100%, #aab5f0), + w2: (150%, #99ccee), + w3: (200%, #a0ddff), + w4: (250%, #90c5f0), + w5: (300%, #90a0dd), + w6: (350%, #90c5f0), + w7: (400%, #3399dd), + w8: (450%, #00ccff), + w9: (500%, #00ccff), + w10: (550%, #00ccff) +) !default; + +.jqcloud { + font: $jqcloud-font; + line-height: normal; + overflow: hidden; + position: relative; +} + +.jqcloud-word { + margin: 0; + padding: 0; + + @each $word, $config in $jqcloud-words { + &.#{$word} { + color: nth($config, 2); + font-size: nth($config, 1); + } + } + + a { + color: inherit; + font-size: inherit; + text-decoration: none; + + &:hover { + color: $jqcloud-link-hover-color; + } + } +} Index: reference/jquery-plugins/jQCloud/test/core_tests.js =================================================================== diff -u --- reference/jquery-plugins/jQCloud/test/core_tests.js (revision 0) +++ reference/jquery-plugins/jQCloud/test/core_tests.js (revision cadcfbea605edcf95ecdc2e0046ac64051670c0e) @@ -0,0 +1,261 @@ +$(function() { + var $c = $('#cloud'); + + QUnit.module('core', { + afterEach: function() { + $c.jQCloud('destroy'); + } + }); + + var some_words = [ + { + text: 'Zero', + weight: 0, + html: { 'test': 'just testing' } + }, + { + text: 'Minus three', + weight: -3, + link: '#' + }, + { + text: 'Minus point fiftyfive', + weight: -0.55 + }, + { + text: 'Two', + weight: '2.0', + link: { href: '#', test: "testing" }, + handlers: { + click: function() { + $(this).data("testHandler", "Handler works!"); + } + }, + afterWordRender: function() { + this.data("testCallback", "Callback works!"); + }, + html: { "class": "mycustomclass" } + } + ]; + + var some_other_words = [ + { text: 'Abc', weight: 1 }, + { text: 'Def', weight: 2 }, + { text: 'Ghi', weight: 3 } + ]; + + var words_with_same_weight = [ + { text: 'Abc', weight: 1 }, + { text: 'Def', weight: 1 }, + { text: 'Ghi', weight: 1 } + ]; + + var encoded_words = [ + { text: "John's Bday", weight: 1, link: "/posts?tag=John%27s+Bday" } + ]; + + var words_with_color = [ + { text: 'Abc', weight: 0, color: '#000' }, + { text: 'Def', weight: 1, color: '#ccc' }, + { text: 'Ghi', weight: 2, color: '#eee' } + ]; + + + QUnit.test('Layout', function(assert) { + var done = assert.async(); + + $c.jQCloud(some_words, { + afterCloudRender: function() { + var text = $c.text(); + assert.ok(text.search(/Zero/) >= 0, "'Zero' is in the cloud"); + assert.ok(text.search(/Minus three/) >= 0, "'Minus three' is in the cloud"); + assert.ok(text.search(/Minus point fiftyfive/) >= 0, "'Minus point five' is in the cloud"); + assert.ok(text.search(/Two/) >= 0, "'Two' is in the cloud, even if the weight was a string"); + + var biggest = $("#cloud_word_0"); + assert.equal(some_words[0].text, "Two", "'Two', having the biggest weight, becomes the first element in the array"); + assert.equal(biggest.text(), "Two", "'Two', having the biggest weight, gets wrapped in an element of id cloud_word_0"); + assert.ok(biggest.hasClass("w10"), "the element with the biggest weight gets wrapped in an element of class w10"); + + var smallest = $("#cloud_word_" + (some_words.length - 1)); + assert.equal(some_words[(some_words.length - 1)].text, "Minus three", "'Minus three', having the smallest weight, becomes the last element in the array"); + assert.equal(smallest.text(), "Minus three", "'Minus three', having the smallest weight, gets wrapped in an element of id cloud_word_" + (some_words.length - 1)); + assert.ok(smallest.hasClass("w1"), "the element with the smallest weight gets wrapped in an element of class w1"); + + var middle = $("#cloud_word_2"); + assert.equal(middle.text(), "Minus point fiftyfive", "'Minus point fiftyfive' should get wrapped in an element of id cloud_word_2"); + assert.ok(middle.hasClass("w5") && middle.text() == "Minus point fiftyfive", "'Minus zero point fiftyfive', having a weight in the middle of the range, should get wrapped in an element of class w5"); + + done(); + } + }); + }); + + QUnit.test('Links', function(assert) { + var done = assert.async(); + + $c.jQCloud(some_words, { + afterCloudRender: function() { + assert.ok($c.find("span:contains('Minus three') a[href='#']").length == 1, "If 'link' option is specified and is a string, an html anchor pointing to that URL is created."); + assert.ok($c.find("span:contains('Two') a[href='#']").length == 1, "If 'link' option is specified and is an object, an html anchor pointing to link.href is created."); + assert.equal($c.find("span:contains('Two') a").attr("test"), "testing", "If 'link' option is specified and is an object, custom attributes should be set."); + + done(); + } + }); + }); + + QUnit.test('Event handlers', function(assert) { + var done = assert.async(); + + $c.jQCloud(some_words, { + afterCloudRender: function() { + $c.find("span:contains('Two')").trigger("click"); + assert.equal($c.find("span:contains('Two')").data("testHandler"), "Handler works!", "Event handlers should be triggered."); + + done(); + } + }); + }); + + QUnit.test('Callbacks', function(assert) { + var done = assert.async(); + + $c.jQCloud(some_words, { + afterCloudRender: function() { + assert.equal($c.find("span:contains('Two')").data("testCallback"), "Callback works!", "afterWordRender callback should be called, and 'this' should be the word element."); + + done(); + } + }); + }); + + QUnit.test('Attributes', function(assert) { + var done = assert.async(); + + $c.jQCloud(some_words, { + afterCloudRender: function() { + assert.ok($c.find("span:contains('Two')").hasClass("mycustomclass"), "Custom classes should be set via html.class attribute"); + assert.equal($c.find("span:contains('Zero')").attr("test"), "just testing", "Custom attributes should be set via the html option"); + + done(); + } + }); + }); + + QUnit.test('Cloud rendering with with delay > 0', function(assert) { + var done = assert.async(); + + $c.jQCloud(some_other_words, { + delay: 10, + afterCloudRender: function() { + var text = $c.text(); + assert.ok(text.search(/Abc/) >= 0, "'Abc' is in the second cloud"); + assert.ok(text.search(/Def/) >= 0, "'Def' is in the second cloud"); + assert.ok(text.search(/Ghi/) >= 0, "'Ghi' is in the second cloud"); + assert.ok(text.search(/Zero/) < 0, "'Zero' is not in the second cloud"); + + done(); + } + }); + }); + + QUnit.test('Words with equal weight', function(assert) { + var done = assert.async(); + + $c.jQCloud(words_with_same_weight, { + afterCloudRender: function() { + assert.ok($c.find(".w5").length == 3, "There should be three words with equal weight."); + + done(); + } + }); + }); + + QUnit.test('Words render when delay is positive and container is visible', function(assert) { + assert.expect(4); + var done1 = assert.async(); + var done2 = assert.async(); + + $c.hide(); + + $c.jQCloud(some_words, { + delay: 10, + afterCloudRender: function() { + assert.ok($c.is(':visible'), "Container is visible"); + assert.ok($c.find("span").length, "Words are rendered"); + + done1(); + } + }); + + setTimeout(function() { + assert.ok(!$c.is(':visible'), "Container is not visible"); + assert.ok($c.find("span").length === 0, "There should be no spans in the container"); + + // now set container4 to visible so that the corresponding visibility test executes + $c.show(); + + done2(); + }, 20); + }); + + + QUnit.test('Links encoding', function(assert) { + assert.expect(2); + var done1 = assert.async(); + var done2 = assert.async(); + + $c.jQCloud($.extend(true, [], encoded_words), { + encodeURI: false, + afterCloudRender: function() { + assert.equal($c.find("span a").attr('href'), '/posts?tag=John%27s+Bday', 'encodeURI is turned off'); + + done1(); + + $c.jQCloud('destroy'); + + $c.jQCloud($.extend(true, [], encoded_words), { + encodeURI: true, + afterCloudRender: function() { + assert.equal($c.find("span a").attr('href'), '/posts?tag=John%2527s+Bday', 'encodeURI is turned on'); + + done2(); + } + }); + } + }); + }); + + QUnit.test('Custom class, colors and fontSize', function(assert) { + var done = assert.async(); + + $c.jQCloud(some_words, { + classPattern: 'word-{n}', + colors: ["#800026", "#bd0026", "#e31a1c", "#fc4e2a", "#fd8d3c", "#feb24c", "#fed976", "#ffeda0", "#ffffcc"], + fontSize: ['50px', '40px', '20px'], + afterCloudRender: function() { + var first = $('#cloud_word_0'); + assert.ok(first.hasClass('word-10')); + assert.equal(first.css('color'), "rgb(128, 0, 38)"); + assert.equal(first.css('font-size'), "50px"); + + done(); + } + }); + }); + + QUnit.test('Word specific color', function(assert) { + var done = assert.async(); + + $c.jQCloud(words_with_color, { + afterCloudRender: function() { + assert.equal($('#cloud_word_2').css('color'), "rgb(0, 0, 0)"); + assert.equal($('#cloud_word_1').css('color'), "rgb(204, 204, 204)"); + assert.equal($('#cloud_word_0').css('color'), "rgb(238, 238, 238)"); + + done(); + } + }); + }); +}); \ No newline at end of file Index: reference/jquery-plugins/jQCloud/test/index.html =================================================================== diff -u --- reference/jquery-plugins/jQCloud/test/index.html (revision 0) +++ reference/jquery-plugins/jQCloud/test/index.html (revision cadcfbea605edcf95ecdc2e0046ac64051670c0e) @@ -0,0 +1,32 @@ + + + + jQCloud + + + + + + + + + + + + + + + + +
+
+ +
+ + +