1<?php 2/** 3 * Horde_Core_Ui_TagCloud:: for creating and displaying tag clouds. 4 * 5 * Based on a striped down version of Pear's HTML_TagCloud 6 * 7 * Copyright 2009-2017 Horde LLC (http://www.horde.org/) 8 * 9 * See the enclosed file COPYING for license information (LGPL). If you 10 * did not receive this file, see http://www.horde.org/licenses/lgpl21. 11 * 12 * @category Horde 13 * @license http://www.horde.org/licenses/lgpl21 LGPL 2.1 14 * @package Core 15 */ 16class Horde_Core_Ui_TagCloud 17{ 18 /** 19 * @var integer 20 */ 21 public $basefontsize; 22 23 /** 24 * @var integer 25 */ 26 public $fontsizerange; 27 28 /** 29 * @var string 30 */ 31 public $css_class = 'tagcloud'; 32 33 /** 34 * @var string 35 * mm,cm,in,pt,pc,px,em 36 */ 37 public $size_suffix = 'px'; 38 39 /** 40 * @var integer 41 */ 42 public $factor; 43 44 /** 45 * @var array 46 */ 47 public $epoc_level = array( 48 'earliest', 49 'earlier', 50 'later', 51 'latest' 52 ); 53 54 /** 55 * @var array 56 */ 57 protected $_elements = array(); 58 59 /** 60 * @var integer 61 */ 62 protected $_max = 0; 63 64 /** 65 * @var integer 66 */ 67 protected $_min = 0; 68 69 /** 70 * @var integer 71 */ 72 protected $_max_epoc; 73 74 /** 75 * @var integer 76 */ 77 protected $_min_epoc; 78 79 /** 80 * @var array 81 */ 82 protected $_map = array(); 83 84 /** 85 * Constructor 86 * 87 * @param integer $basefontsize Base font size of output tag (option). 88 * @param integer $fontsizerange Font size range. 89 */ 90 public function __construct($basefontsize = 24, $fontsizerange = 12) 91 { 92 $this->basefontsize = $basefontsize; 93 $this->fontsizerange = $fontsizerange; 94 95 $this->minfontsize = max($basefontsize - $fontsizerange, 0); 96 $this->maxfontsize = $basefontsize + $fontsizerange; 97 } 98 99 /** 100 * Add a Tag Element to build Tag Cloud. 101 * 102 * @param string $name TODO 103 * @param string $url TODO 104 * @param integer $count TODO 105 * @param integer $timestamp UNIX timestamp. 106 * @param string $onclick Javascript onclick event handler. 107 */ 108 public function addElement($name, $url ='', $count = 0, $timestamp = null, 109 $onclick = null) 110 { 111 112 if (isset($this->_map[$name])) { 113 $i = $this->_map[$name]; 114 // Increase the count 115 $this->_elements[$i]['count'] += $count; 116 117 // Keep the latest timestamp 118 if (!empty($timestamp) && 119 $timestamp > $this->_elements[$i]['timestamp']) { 120 $this->_elements[$i]['timestamp'] = $timestamp; 121 } 122 // For onclick and url we will simply overwrite the existing values 123 // instead of checking if they are empty, then overwriting. 124 $this->_elements[$i]['onclick'] = $onclick; 125 $this->elements[$i]['url'] = $url; 126 } else { 127 $i = count($this->_elements); 128 $this->_elements[$i]['name'] = $name; 129 $this->_elements[$i]['url'] = $url; 130 $this->_elements[$i]['count'] = $count; 131 $this->_elements[$i]['timestamp'] = $timestamp == null ? time() : $timestamp; 132 $this->_elements[$i]['onclick'] = $onclick; 133 $this->_map[$name] = $i; 134 } 135 } 136 137 /** 138 * Add a Tag Element to build Tag Cloud. 139 * 140 * @param array $tags Associative array to $this->_elements. 141 */ 142 public function addElements($tags) 143 { 144 $this->_elements = array_merge($this->_elements, $tags); 145 } 146 147 /** 148 * Clear Tag Elements. 149 */ 150 public function clearElements() 151 { 152 $this->_elements = array(); 153 } 154 155 /** 156 * Build HTML part. 157 * 158 * @param array $param 'limit' => int limit of generation tag num. 159 * 160 * @return string HTML 161 */ 162 public function buildHTML($param = array()) 163 { 164 return $this->_wrapDiv($this->_buidHTMLTags($param)); 165 } 166 167 /** 168 * Calc Tag level and create whole HTML of each Tags. 169 * 170 * @param array $param Limit of Tag Number. 171 * 172 * @return string HTML 173 */ 174 protected function _buidHTMLTags($param) 175 { 176 $this->total = count($this->_elements); 177 // no tags elements 178 if ($this->total == 0) { 179 return ''; 180 } elseif ($this->total == 1) { 181 $tag = $this->_elements[0]; 182 return $this->_createHTMLTag($tag, 'latest', $this->basefontsize); 183 } 184 185 $limit = array_key_exists('limit', $param) ? $param['limit'] : 0; 186 $this->_sortTags($limit); 187 $this->_calcMumCount(); 188 $this->_calcMumEpoc(); 189 190 $range = $this->maxfontsize - $this->minfontsize; 191 $this->factor = ($this->_max == $this->_min) 192 ? 1 193 : $range / (sqrt($this->_max) - sqrt($this->_min)); 194 $this->epoc_factor = ($this->_max_epoc == $this->_min_epoc) 195 ? 1 196 : count($this->epoc_level) / (sqrt($this->_max_epoc) - sqrt($this->_min_epoc)); 197 $rtn = array(); 198 foreach ($this->_elements as $tag){ 199 $count_lv = $this->_getCountLevel($tag['count']); 200 if (!isset($tag['timestamp']) || empty($tag['timestamp'])) { 201 $epoc_lv = count($this->epoc_level) - 1; 202 } else { 203 $epoc_lv = $this->_getEpocLevel($tag['timestamp']); 204 } 205 $color_type = $this->epoc_level[$epoc_lv]; 206 $font_size = $this->minfontsize + $count_lv; 207 $rtn[] = $this->_createHTMLTag($tag, $color_type, $font_size); 208 } 209 return implode('', $rtn); 210 } 211 212 /** 213 * Create a Element of HTML part. 214 * 215 * @param array $tag TODO 216 * @param string $type CSS class of time line param. 217 * @param integer $fontsize TODO 218 * 219 * @return string a Element of Tag HTML 220 */ 221 protected function _createHTMLTag($tag, $type, $fontsize) 222 { 223 return sprintf('<a style="font-size:%d%s" class="%s" href="%s"%s>%s</a>' . "\n", 224 $fontsize, 225 $this->size_suffix, 226 $type, 227 $tag['url'], 228 (empty($tag['onclick']) ? '' : ' onclick="' . $tag['onclick'] . '"'), 229 htmlspecialchars($tag['name'])); 230 } 231 232 /** 233 * Sort tags by name. 234 * 235 * @param integer $limit Limit element number of create TagCloud. 236 */ 237 protected function _sortTags($limit = 0) 238 { 239 usort($this->_elements, array($this, 'cmpElementsName')); 240 if ($limit != 0){ 241 $this->_elements = array_splice($this->_elements, 0, $limit); 242 } 243 } 244 245 /** 246 * Using for usort(). 247 * 248 * @return integer TODO 249 */ 250 public function cmpElementsName($a, $b) 251 { 252 return ($a['name'] == $b['name']) 253 ? 0 254 : (($a['name'] < $b['name']) ? -1 : 1); 255 } 256 257 /** 258 * Calc max and min tag count of use. 259 */ 260 protected function _calcMumCount() 261 { 262 foreach($this->_elements as $item){ 263 $array[] = $item['count']; 264 } 265 $this->_min = min($array); 266 $this->_max = max($array); 267 } 268 269 /** 270 * Calc max and min timestamp. 271 */ 272 protected function _calcMumEpoc() 273 { 274 foreach($this->_elements as $item){ 275 $array[] = $item['timestamp']; 276 } 277 $this->_min_epoc = min($array); 278 $this->_max_epoc = max($array); 279 } 280 281 /** 282 * Calc Tag Level of size. 283 * 284 * @param integer $count TODO 285 * 286 * @return integer Level. 287 */ 288 protected function _getCountLevel($count = 0) 289 { 290 return (int)((sqrt($count) - sqrt($this->_min)) * $this->factor); 291 } 292 293 /** 294 * Calc timeline level of Tag. 295 * 296 * @param integer $timestamp TODO 297 * 298 * @return integer Level of timeline. 299 */ 300 protected function _getEpocLevel($timestamp = 0) 301 { 302 return (int)((sqrt($timestamp) - sqrt($this->_min_epoc)) * $this->epoc_factor); 303 } 304 305 /** 306 * Wrap div tag. 307 * 308 * @param string $html TODO 309 * 310 * @return string TODO 311 */ 312 protected function _wrapDiv($html) 313 { 314 return ($html == '') 315 ? '' 316 : sprintf("<div class=\"%s\">\n%s</div>\n", $this->css_class, $html); 317 } 318 319} 320