forked from STI/Astroport.ONE
265 lines
11 KiB
JavaScript
265 lines
11 KiB
JavaScript
|
/**
|
||
|
* Tag cloud plugin for jQuery, showing bigger tags in the center
|
||
|
* @version 1.2.0
|
||
|
* @release 2021-04-07
|
||
|
* @repository https://github.com/peterthoeny/jquery.tagcloud
|
||
|
* @author Peter Thoeny, https://twiki.org/ & https://github.com/peterthoeny
|
||
|
* @copyright 2021 Peter Thoeny, https://github.com/peterthoeny
|
||
|
* @license MIT, https://opensource.org/licenses/mit-license
|
||
|
*/
|
||
|
(function($) {
|
||
|
|
||
|
'use strict';
|
||
|
|
||
|
let debug = false;
|
||
|
|
||
|
function debugLog(msg) {
|
||
|
if(debug) {
|
||
|
console.log('- tagCloud: ' + msg);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function entityEncode(val) {
|
||
|
val = val.toString()
|
||
|
.replace(/&/g, '&')
|
||
|
.replace(/"/g, '"')
|
||
|
.replace(/'/g, ''')
|
||
|
.replace(/</g, '<')
|
||
|
.replace(/>/g, '>');
|
||
|
return val;
|
||
|
}
|
||
|
|
||
|
$.fn.tagCloud = function(options) {
|
||
|
let self = $(this);
|
||
|
let addLink = options && options.tag && options.tag.format ? false : true;
|
||
|
options = $.extend({}, $.fn.tagCloud.defaults, options);
|
||
|
if(options.debug != undefined) {
|
||
|
debug = options.debug;
|
||
|
}
|
||
|
debugLog('options: ' + JSON.stringify(options, null, ''));
|
||
|
let tagName = self.prop('tagName');
|
||
|
if(tagName === 'UL') {
|
||
|
if(!options.data) {
|
||
|
options.data = [];
|
||
|
}
|
||
|
self.find('li').each(function(idx, elem) {
|
||
|
let weight = $(elem).data('weight');
|
||
|
if(weight == undefined) {
|
||
|
weight = $(elem).find(':first-child').data('weight');
|
||
|
}
|
||
|
let href = $(elem).find('a').attr('href') || '';
|
||
|
let tag = $(elem).text() || '?';
|
||
|
debugLog(weight +', '+href+', '+tag+', '+$(elem).html());
|
||
|
if(options.data[idx]) {
|
||
|
if(weight != undefined) {
|
||
|
options.data[idx].weight = Number(weight);
|
||
|
}
|
||
|
if(href) {
|
||
|
options.data[idx].link = href;
|
||
|
}
|
||
|
options.data[idx].name = tag;
|
||
|
} else {
|
||
|
options.data.push({ name: tag, link: href, weight: Number(weight) })
|
||
|
}
|
||
|
});
|
||
|
self.hide();
|
||
|
if(self.next().hasClass('jqTcContainer')) {
|
||
|
self.next().remove();
|
||
|
}
|
||
|
self.after('<div></div>');
|
||
|
self = self.next();
|
||
|
}
|
||
|
let css = {};
|
||
|
Object.keys(options.container).forEach(function(key) {
|
||
|
css[key] = (options[key] != undefined) ? options[key] : options.container[key];
|
||
|
});
|
||
|
self.addClass('jqTcContainer').css(css);
|
||
|
let containerWidth = options.container.width;
|
||
|
let minWeight = 1000000000000;
|
||
|
let maxWeight = -1000000000000;
|
||
|
let minFontSize = options.tag.minFontSize || $.fn.tagCloud.defaults.tag.minFontSize;
|
||
|
let maxFontSize = options.tag.maxFontSize || $.fn.tagCloud.defaults.tag.maxFontSize;
|
||
|
let format = options.tag.format || $.fn.tagCloud.defaults.tag.format;
|
||
|
let sum = 0;
|
||
|
debugLog('minFontSize: '+minFontSize+', maxFontSize: '+maxFontSize);
|
||
|
options.data.forEach(function(item) {
|
||
|
if(item.weight < minWeight) {
|
||
|
minWeight = item.weight;
|
||
|
}
|
||
|
if(item.weight > maxWeight) {
|
||
|
maxWeight = item.weight;
|
||
|
}
|
||
|
sum += item.weight;
|
||
|
});
|
||
|
let a = (maxFontSize - minFontSize) / (maxWeight - minWeight);
|
||
|
let b = minFontSize - (minWeight * a);
|
||
|
debugLog('minWeight: '+minWeight+', maxWeight: '+maxWeight+', a: '+a+', b: '+b);
|
||
|
let tags = options.data.sort(function(a, b) {
|
||
|
if(a.weight > b.weight) {
|
||
|
return -1;
|
||
|
} else if(a.weight < b.weight) {
|
||
|
return 1;
|
||
|
}
|
||
|
return 0;
|
||
|
}).map(function(item, idx) {
|
||
|
let html = format
|
||
|
.replace(/\{tag\.name\}/g, item.name)
|
||
|
.replace(/\{tag\.link\}/g, item.link)
|
||
|
.replace(/\{tag\.weight(?:\.(\d))?\}/g, function(m, c1) {
|
||
|
let num = item.weight;
|
||
|
if(c1) {
|
||
|
let factor = 10 ** parseInt(c1);
|
||
|
num = Math.round(num * factor) / factor;
|
||
|
}
|
||
|
return num.toString();
|
||
|
})
|
||
|
.replace(/\{tag\.percent(?:\.(\d))?\}/g, function(m, c1) {
|
||
|
let num = 100 * item.weight / sum;
|
||
|
let factor = 1;
|
||
|
if(c1) {
|
||
|
factor = 10 ** parseInt(c1);
|
||
|
}
|
||
|
num = Math.round(num * factor) / factor;
|
||
|
return num.toString() + '%';
|
||
|
});
|
||
|
if(addLink && item.link) {
|
||
|
html = '<a href="' + item.link + '" target="_blank">' + html + '</a>';
|
||
|
}
|
||
|
let size = parseInt((a * item.weight + b) * 10, 10) / 10;
|
||
|
let attrs = [
|
||
|
'class="jqTcTag"',
|
||
|
'data-name="' + entityEncode(item.name) + '"',
|
||
|
'data-link="' + entityEncode(item.link || '') + '"',
|
||
|
'data-weight="' + item.weight + '"',
|
||
|
'data-size="' + size + '"'
|
||
|
];
|
||
|
let style = 'style="font-size: ' + size + 'px;';
|
||
|
let bgColor = item.bgColor || item.backgroundColor || options.tag.backgroundColor ||
|
||
|
$.fn.tagCloud.defaults.backgroundColors[idx] || $.fn.tagCloud.defaults.defaultTagBackgroundColor;
|
||
|
if(bgColor) {
|
||
|
style += ' background-color: ' + bgColor + ';';
|
||
|
}
|
||
|
let color = item.color || options.tag.color || $.fn.tagCloud.defaults.defaultTagColor;
|
||
|
if(color != 'auto') {
|
||
|
style += ' color: ' + color + ';';
|
||
|
}
|
||
|
bgColor
|
||
|
.replace(/^#(.)(.)(.)$/, '#$1$1$2$2$3$3')
|
||
|
.replace(/^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})?$/i, function(m, c1, c2, c3) {
|
||
|
let brightness = Math.round((
|
||
|
(Number('0x' + c1) * 299) +
|
||
|
(Number('0x' + c2) * 587) +
|
||
|
(Number('0x' + c3) * 114)
|
||
|
) / 1000);
|
||
|
if(brightness > 125) {
|
||
|
if(color === 'auto') {
|
||
|
style += ' color: black;';
|
||
|
}
|
||
|
if(options.tag.textShadow) {
|
||
|
style += ' text-shadow: 0px 0px 2px #dddddd;';
|
||
|
}
|
||
|
} else {
|
||
|
if(color === 'auto') {
|
||
|
style += ' color: white;';
|
||
|
}
|
||
|
if(options.tag.textShadow) {
|
||
|
style += ' text-shadow: 0px 0px 2px #222222;';
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
style += '"';
|
||
|
attrs.push(style);
|
||
|
if(item.tooltip) {
|
||
|
attrs.push('title="' + entityEncode(item.tooltip) + '"');
|
||
|
}
|
||
|
html = '<span ' + attrs.join(' ') + '>' + html + '</span>';
|
||
|
self.html(html); // set temporarily to get width and height
|
||
|
let tagElem = self.find('span');
|
||
|
item.width = tagElem.outerWidth();
|
||
|
item.height = tagElem.outerHeight();
|
||
|
item.html = html;
|
||
|
item.ttLength = tagElem.length;
|
||
|
item.ttHtml = self.text();
|
||
|
return item;
|
||
|
});
|
||
|
debugLog('tags: ' + JSON.stringify(tags, null, ' '))
|
||
|
let rows = [];
|
||
|
let cells = [];
|
||
|
let width = 0;
|
||
|
let addRight = true;
|
||
|
let addBottom = true;
|
||
|
let padding = 2 * 5 + 5;
|
||
|
let containerPadding = padding;
|
||
|
let tagMargin = 2 * 10 + 5;
|
||
|
let verticalAlign = 'middle';
|
||
|
tags.forEach(function(item) {
|
||
|
if(width + item.width + tagMargin >= containerWidth - containerPadding) {
|
||
|
let rowHtml = '<tr><td style="vertical-align: ' + verticalAlign + ';">' + cells.join('') + '</td></tr>';
|
||
|
if(addBottom) {
|
||
|
rows.push(rowHtml);
|
||
|
verticalAlign = 'bottom';
|
||
|
} else {
|
||
|
rows.unshift(rowHtml);
|
||
|
verticalAlign = 'top';
|
||
|
}
|
||
|
addBottom = !addBottom;
|
||
|
containerPadding += 1.5 * padding;
|
||
|
cells = [];
|
||
|
width = 0;
|
||
|
}
|
||
|
if(addRight) {
|
||
|
cells.push(item.html);
|
||
|
} else {
|
||
|
cells.unshift(item.html);
|
||
|
}
|
||
|
addRight = !addRight;
|
||
|
width = width + item.width + tagMargin;
|
||
|
});
|
||
|
let rowHtml = '<tr><td style="vertical-align: ' + verticalAlign + ';">' + cells.join('') + '</td></tr>';
|
||
|
if(addBottom) {
|
||
|
rows.push(rowHtml);
|
||
|
} else {
|
||
|
rows.unshift(rowHtml);
|
||
|
}
|
||
|
let html = '<table class="jqTcTable">' + rows.join('') + '</table>';
|
||
|
let tagStyle = {};
|
||
|
Object.keys(options.tag).forEach(key => {
|
||
|
if(!/^(minFontSize|maxFontSize|format|color|textShadow|backgroundColor)$/.test(key)) {
|
||
|
tagStyle[key] = options.tag[key];
|
||
|
}
|
||
|
});
|
||
|
self.html(html).find('.jqTcTag').css(tagStyle);
|
||
|
};
|
||
|
|
||
|
$.fn.tagCloud.defaults = {
|
||
|
container: {
|
||
|
width: 500,
|
||
|
height: 'auto',
|
||
|
backgroundColor: '#f0f0f0',
|
||
|
color: '#666666',
|
||
|
padding: '10px 5px',
|
||
|
fontFamily: '"Helvetica Neue",Helvetica,Arial,sans-serif'
|
||
|
},
|
||
|
tag: {
|
||
|
minFontSize: 10, // min font size in pixels
|
||
|
maxFontSize: 40, // max font size in pixels
|
||
|
format: '{tag.name}', // also '{tag.link}', '{tag.weight}', '{tag.percent}'
|
||
|
color: 'auto', // auto text color, black for light background, white for dark background
|
||
|
textShadow: false // text shadow, enable for better visibility
|
||
|
},
|
||
|
backgroundColors: [
|
||
|
'#db843d', '#92a8cd', '#a47d7c', '#058dc7', '#50b432', '#ed561b', '#24cbe5', '#64e572',
|
||
|
'#ff9655', '#d6cb54', '#6af9c4', '#b5ca92', '#2f7ed8', '#5c40de', '#8bbc21', '#910000',
|
||
|
'#1aadce', '#492970', '#f28f43', '#77a1e5', '#c42525', '#a6c96a', '#db843d', '#92a8cd',
|
||
|
'#a47d7c', '#058dc7', '#50b432', '#ed561b', '#24cbe5', '#64e572', '#ff9655', '#d6cb54',
|
||
|
'#6af9c4', '#b5ca92', '#2f7ed8', '#5c40de', '#8bbc21', '#910000', '#1aadce', '#492970',
|
||
|
'#f28f43', '#77a1e5', '#c42525', '#a6c96a', '#db843d', '#92a8cd', '#a47d7c', '#058dc7',
|
||
|
'#50b432', '#ed561b', '#24cbe5', '#64e572', '#ff9655', '#d6cb54', '#6af9c4', '#b5ca92',
|
||
|
'#2f7ed8', '#5c40de', '#8bbc21', '#910000', '#1aadce', '#492970', '#f28f43', '#77a1e5'
|
||
|
],
|
||
|
defaultTagColor: 'auto', // black or white, based on background color
|
||
|
defaultTagBackgroundColor: '#ff9655' // default background color
|
||
|
};
|
||
|
|
||
|
})(jQuery);
|