diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ddaf90b --- /dev/null +++ b/.gitignore @@ -0,0 +1,34 @@ +# Created by .gitignore support plugin (hsz.mobi) +### Node template +# Logs +logs +*.log + +# Runtime data +pids +*.pid +*.seed + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directory +# Commenting this out is preferred by some people, see +# https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git +node_modules + +# Users Environment Variables +.lock-wscript + +.idea + +test/CSV/* \ No newline at end of file diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..05b55c1 --- /dev/null +++ b/.npmignore @@ -0,0 +1,8 @@ +test/ +.git* +npm-debug.log +.travis.yml +node_modules/ +node_modules/* +coverage/ +coverage/* \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 87f8cd9..f32eb0e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,7 @@ +sudo: false language: node_js node_js: - - "0.10" \ No newline at end of file + - "11" + - "10" + - "8" + - "6" \ No newline at end of file diff --git a/README.md b/README.md index e0cea24..42c1651 100755 --- a/README.md +++ b/README.md @@ -1,12 +1,13 @@ -# Node Tag Cloud HTML Generator +# tag-cloud - Node Tag Cloud HTML Generator +**This node module will take an array of tags and counts and generate a Tag/Word Cloud.** + +[![Dependencies](https://img.shields.io/david/mrodrig/tag-cloud.svg?style=flat-square)](https://www.npmjs.org/package/tag-cloud) [![Build Status](https://travis-ci.org/mrodrig/tag-cloud.svg?branch=master)](https://travis-ci.org/mrodrig/tag-cloud) -[![bitHound Dependencies](https://www.bithound.io/github/mrodrig/tag-cloud/badges/dependencies.svg)](https://www.bithound.io/github/mrodrig/tag-cloud/master/dependencies/npm) [![Downloads](http://img.shields.io/npm/dm/tag-cloud.svg)](https://www.npmjs.org/package/tag-cloud) [![NPM version](https://img.shields.io/npm/v/tag-cloud.svg)](https://www.npmjs.org/package/tag-cloud) -[![bitHound Score](https://www.bithound.io/github/mrodrig/tag-cloud/badges/score.svg)](https://www.bithound.io/github/mrodrig/tag-cloud) - -This node module will take an array of tags and counts and generate a Tag/Word Cloud. +[![Maintainability](https://api.codeclimate.com/v1/badges/7f915482db8fb7650731/maintainability)](https://codeclimate.com/github/mrodrig/tag-cloud/maintainability) +[![Known Vulnerabilities](https://snyk.io/test/npm/tag-cloud/badge.svg)](https://snyk.io/test/npm/tag-cloud) ## Installation @@ -17,12 +18,12 @@ $ npm install tag-cloud ## Usage ```javascript -var tagCloud = require('tag-cloud'); +let tagCloud = require('tag-cloud'); ``` ### API -#### tagCloud(array, callback, options) +#### `tagCloud(array, callback, options)` * `array` - An array of JSON documents of the form {tagName: , count: } * `callback` - A function of the form `function (err, html)`; This function will receive any errors and/or the HTML generated. @@ -44,9 +45,9 @@ var tagCloud = require('tag-cloud'); ```javascript -var tagCloud = require('tag-cloud'); +let tagCloud = require('tag-cloud'); -var tags = [ +let tags = [ {tagName: 'js', count: 5}, {tagName: 'css', count: 9}, {tagName: 'less', count: 13}, @@ -66,7 +67,7 @@ tagCloud.tagCloud(tags, function (err, data) { randomize: false }); -var promise = require('bluebird'); +let promise = require('bluebird'); promise.promisifyAll(tagCloud); /* Option 3 */ diff --git a/lib/tagCloud.js b/lib/tagCloud.js deleted file mode 100644 index 3caba7c..0000000 --- a/lib/tagCloud.js +++ /dev/null @@ -1,107 +0,0 @@ -'use strict'; - -var _ = require('underscore'); // Require underscore - -// Default options; By using a function this is essentially a 'static' variable -var defaultOptions = { - randomize: true, - classPrefix: 'bucket', - additionalAttributes: {}, - replacements: [], - numBuckets: 10, - htmlTag: 'span' -}; - -// Export the following functions that will be client accessible -module.exports = { - - /** - * Client Accessible Tag Cloud Function (Promisifiable as of v1.0.5) - * @param array Array of documents of form {tagName: String, count: Number} which will be used to generate the cloud - * @param callback Function of (err, data) which handles the error (if any) and data returned - * @param opts Document {optional} which contains any of the options from the API doc - * @returns {*} - */ - tagCloud: function (array, callback, opts) { - // If this was promisified (callback and opts are swapped) then fix the argument order. - if (_.isObject(callback) && !_.isFunction(callback)) { - var func = opts; - opts = callback; - callback = func; - } - - // Merge the options into the defaults - opts = _.defaults(opts || {}, defaultOptions); - // Shuffle the elements in the array to pseudo-randomize the tagCloud ordering - var min = _.min(array, function(value) { return value.count; }).count, - max = _.max(array, function(value) { return value.count; }).count, - diff = (max - min), - // Split the number of tags into the buckets as evenly as possible - numTagsPerBucket = ((diff || 1)/(opts.numBuckets - 1)); - array = _.map(array, function (tag) { - if (tag.count < 0) { return callback(new Error('All tag counts must be greater than zero.')); } - var attributes = _.defaults({ - class: opts.classPrefix + determineBucket(min, numTagsPerBucket, tag.count) - }, opts.additionalAttributes); - return generateHTMLTag(opts.htmlTag, attributes, opts.replacements, tag.tagName); - }); - if (opts.randomize) { array = _.shuffle(array); } - var html = array.join(''); - return callback(null, html); - } -}; - -/** - * Generates an HTML String with the given data - * @param tagType String tag type (ie. div, span, etc.) - * @param attributes Document {key : value} - * @param replacements Array [{find : , replace: }, ...] - * @param text String inner text of the HTML tag - * @returns {string} HTML String value - */ -var generateHTMLTag = function (tagType, attributes, replacements, tagText) { - var html = '<{tag}'.replace(/{tag}/, tagType); - var keys = _.keys(attributes); - - // For each additional attribute, add it into the HTML - _.each(keys, function (key) { - var value = attributes[key], - attrTag = tagText; - if (_.isObject(value)) { - // If encode is specified for this key, encode the text - attrTag = value.encode ? encodeURIComponent(tagText) : tagText; - value = value.value; - } - html += generateHTMLAttribute(key, value, attrTag, replacements); - }); - - html += '>{text}'.replace(/{text}/, tagText).replace(/{tag}/, tagType); - return html; -}; - -var generateHTMLAttribute = function (key, value, tagText, replacements) { - return performReplacements(' {key}="{value}"', - [ - { find: '{key}', replace: key }, - { find: '{value}', replace: value }, - { find: '{{tag}}', replace: tagText } - ].concat(replacements || [])); -}; - -var performReplacements = function (str, replacements) { - _.each(replacements, function (replacementDoc) { - str = str.replace(replacementDoc.find, replacementDoc.replace); - }); - return str; -}; - -/** - * Determines the appropriate bucket number for the tag - * @param min Number value of the minimum tag count - * @param numTagsPerBucket Number value of the number of tags per bucket - * @param count Number current tag's count value - * @returns {number} returns the bucket number for the tag - */ -var determineBucket = function (min, numTagsPerBucket, count) { - return Math.floor((count - min) / numTagsPerBucket); -}; diff --git a/package.json b/package.json index 233ce2e..bc1f87c 100755 --- a/package.json +++ b/package.json @@ -2,12 +2,12 @@ "author": "mrodrig", "name": "tag-cloud", "description": "Node Tag Cloud HTML Generator", - "version": "1.1.4", + "version": "1.2.0", "repository": { "type": "git", "url": "http://github.com/mrodrig/tag-cloud.git" }, - "main": "./lib/tagCloud.js", + "main": "./src/tagCloud.js", "scripts": { "test": "./node_modules/.bin/mocha --reporter spec" }, @@ -25,15 +25,15 @@ "text" ], "dependencies": { - "underscore": "1.8.3" + "underscore": "1.9.1" }, "devDependencies": { - "mocha": "3.3.0", - "should": "13.1.2", - "async": "2.5.0" + "mocha": "5.2.0", + "should": "13.2.3", + "async": "2.6.1" }, "engines": { - "node": "*" + "node": ">=6" }, "license": "MIT" -} \ No newline at end of file +} diff --git a/src/defaultOptions.json b/src/defaultOptions.json new file mode 100644 index 0000000..1b44f1f --- /dev/null +++ b/src/defaultOptions.json @@ -0,0 +1,8 @@ +{ + "randomize": true, + "classPrefix": "bucket", + "additionalAttributes": {}, + "replacements": [], + "numBuckets": 10, + "htmlTag": "span" +} \ No newline at end of file diff --git a/src/tagCloud.js b/src/tagCloud.js new file mode 100644 index 0000000..d0bbe4d --- /dev/null +++ b/src/tagCloud.js @@ -0,0 +1,108 @@ +'use strict'; + +let _ = require('underscore'); // Require underscore + +// Default options +let defaultOptions = require('./defaultOptions.json'); + +// Export the following functions that will be client accessible +module.exports = { + tagCloud: tagCloud +}; + +/** + * Client Accessible Tag Cloud Function (Promisifiable as of v1.0.5) + * @param array Array of documents of form {tagName: String, count: Number} which will be used to generate the cloud + * @param callback Function of (err, data) which handles the error (if any) and data returned + * @param opts Document {optional} which contains any of the options from the API doc + * @returns {*} + */ +function tagCloud(array, callback, opts) { + // If this was promisified (callback and opts are swapped) then fix the argument order. + if (_.isObject(callback) && !_.isFunction(callback)) { + let func = opts; + opts = callback; + callback = func; + } + + // Merge the options into the defaults + opts = _.defaults(opts || {}, defaultOptions); + // Shuffle the elements in the array to pseudo-randomize the tagCloud ordering + let min = _.min(array, function(value) { return value.count; }).count, + max = _.max(array, function(value) { return value.count; }).count, + diff = (max - min), + // Split the number of tags into the buckets as evenly as possible + numTagsPerBucket = ((diff || 1)/(opts.numBuckets - 1)); + array = _.map(array, function (tag) { + if (tag.count < 0) { return callback(new Error('All tag counts must be greater than zero.')); } + let attributes = _.defaults({ + class: opts.classPrefix + determineBucket(min, numTagsPerBucket, tag.count) + }, opts.additionalAttributes); + return generateHTMLTag(opts.htmlTag, attributes, opts.replacements, tag.tagName); + }); + if (opts.randomize) { array = _.shuffle(array); } + let html = array.join(''); + return callback(null, html); +} + +/** + * Generates an HTML String with the given data + * @param tagType String tag type (ie. div, span, etc.) + * @param attributes Document {key : value} + * @param replacements Array [{find : , replace: }, ...] + * @param tagText String inner text of the HTML tag + * @returns {string} HTML String value + */ +function generateHTMLTag(tagType, attributes, replacements, tagText) { + let html = '<{tag}'.replace(/{tag}/, tagType); + let keys = _.keys(attributes); + + // For each additional attribute, add it into the HTML + _.each(keys, function (key) { + let value = attributes[key], + attrTag = tagText; + if (_.isObject(value)) { + // If encode is specified for this key, encode the text + attrTag = value.encode ? encodeURIComponent(tagText) : tagText; + value = value.value; + } + html += generateHTMLAttribute(key, value, attrTag, replacements); + }); + + html += '>{text}'.replace(/{text}/, tagText).replace(/{tag}/, tagType); + return html; +} + +/** + * Generates html attributes + * @param key + * @param value + * @param tagText + * @param replacements + */ +function generateHTMLAttribute(key, value, tagText, replacements) { + return performReplacements(' {key}="{value}"', + [ + { find: '{key}', replace: key }, + { find: '{value}', replace: value }, + { find: '{{tag}}', replace: tagText } + ].concat(replacements || [])); +} + +function performReplacements(str, replacements) { + _.each(replacements, function (replacementDoc) { + str = str.replace(replacementDoc.find, replacementDoc.replace); + }); + return str; +} + +/** + * Determines the appropriate bucket number for the tag + * @param min Number value of the minimum tag count + * @param numTagsPerBucket Number value of the number of tags per bucket + * @param count Number current tag's count value + * @returns {number} returns the bucket number for the tag + */ +function determineBucket(min, numTagsPerBucket, count) { + return Math.floor((count - min) / numTagsPerBucket); +} diff --git a/test/tagCloudTests.js b/test/tagCloudTests.js index 98373ba..3cd8203 100644 --- a/test/tagCloudTests.js +++ b/test/tagCloudTests.js @@ -1,10 +1,10 @@ -var should = require('should'), - tagCloud = require('.././lib/tagCloud'), +let should = require('should'), + tagCloud = require('../src/tagCloud'), fs = require('fs'), _ = require('underscore'), async = require('async'); -var options = { +let options = { randomize: false, classPrefix: 'tagCloud', additionalAttributes: { @@ -18,7 +18,7 @@ var options = { htmlTag: 'span' }; -var json_emptyTags = require('./JSON/emptyTags'), +let json_emptyTags = require('./JSON/emptyTags'), json_singleTag = require('./JSON/singleTag'), json_tenTags = require('./JSON/tenTags'), json_twentyTags = require('./JSON/twentyTags'), @@ -30,7 +30,7 @@ var json_emptyTags = require('./JSON/emptyTags'), html_twentyTags = '', html_oneHundredTags = ''; -var tagCloudTests = function () { +let tagCloudTests = function () { // We can check the HTML is equivalent since we specify randomize to be false describe('/lib/tagCloud.js', function () { describe('Options Specified', function () { @@ -80,7 +80,7 @@ var tagCloudTests = function () { }); it('should convert no tags with custom replacements to blank html', function(done) { - var options = { + let options = { randomize: false, classPrefix: 'tagCloud', additionalAttributes: { @@ -102,7 +102,7 @@ var tagCloudTests = function () { }); it('should convert tags with custom replacements to html', function(done) { - var options = { + let options = { randomize: false, classPrefix: 'tagCloud', additionalAttributes: { diff --git a/test/tests.js b/test/tests.js index 2ce481f..1a5dd8e 100644 --- a/test/tests.js +++ b/test/tests.js @@ -1,4 +1,4 @@ -var tagCloudTests = require('./tagCloudTests'); +let tagCloudTests = require('./tagCloudTests'); describe('tag-cloud Module', function() { tagCloudTests.runTests();