1/** 2 * DokuWiki Plugin imapmarkers (Javascript Component) 3 * 4 * @license MIT https://en.wikipedia.org/wiki/MIT_License 5 * @author Kai Thoene <k.git.thoene@gmx.net> 6 */ 7if (window.jQuery) { 8 globalThis[Symbol.for('imapmarkers_storage')] = (function () { 9 var defaults = { 10 'debug': false, // false = no debug on console 11 'marker_color': "#00AEEF", 12 'clicked-reference-css': { 'font-weight': 'bold', 'color': '#00AEEF' } 13 }; 14 var a_maps = []; 15 var a_areas = []; 16 var a_references = []; 17 var a_clicked_css_properties = []; 18 var a_cfg = []; 19 var resize_timeout = null; 20 var a_last_clicked_id = []; 21 var a_imap_div = []; 22 var imap_div_timeout = null; 23 var nr_startup_intervals = 0; 24 var mapster_decoration_obj_default = { 25 fillColor: 'ffffff', 26 fillOpacity: 0.3, 27 wrapClass: true, 28 wrapCss: true, 29 clickNavigate: true 30 }; 31 var is_imagemapster_existing = false; 32 33 function append_html_to_jquery_object(html, object) { 34 object[0].insertAdjacentHTML( 35 "beforeEnd", 36 html 37 ); 38 } // function append_html_to_jquery_object 39 40 function calc_marker_pos(coords) { 41 let split_coords = coords.split(","); 42 var x = 0; 43 var y = 0; 44 switch (split_coords.length) { 45 case 3: // circle 46 x = parseInt(split_coords[0]); 47 y = parseInt(split_coords[1]); 48 break; 49 case 4: // rectangle 50 x = Math.floor((parseInt(split_coords[0]) + parseInt(split_coords[2])) / 2); 51 y = Math.floor((parseInt(split_coords[1]) + parseInt(split_coords[3])) / 2); 52 break; 53 default: 54 if (split_coords.length >= 6) { 55 var num_coords = 0; 56 for (var i = 0; i < (split_coords.length - 1); i += 2) { 57 x += parseInt(split_coords[i]); 58 y += parseInt(split_coords[i + 1]); 59 num_coords++; 60 } 61 x = Math.floor(x / num_coords); 62 y = Math.floor(y / num_coords);; 63 } 64 } 65 return [x, y]; 66 } // function calc_marker_pos 67 68 function get_id_from_string(s) { 69 const a_ids = s.split(" "); 70 if (a_ids.length >= 1) { 71 if (a_ids[0].length > 0) { 72 return String(a_ids[0]); 73 } 74 } 75 return null; 76 } // function get_id_from_string 77 78 function get_index_from_string_end(s) { 79 return parseInt(s.replace(/^[^\d]*/, "")); 80 } // function get_index_from_string_end 81 82 function find_area_by_jquery_id(id, imap_index = null) { 83 try { 84 if (imap_index === null) { 85 for (var i = 0; i < a_areas.length; i++) { 86 if (a_areas[i] !== undefined) { 87 for (var j = 0; j < a_areas[i].length; j++) { 88 const area = a_areas[i][j]; 89 if (area['id'] == id) { 90 return [i, area['area'], j]; 91 } 92 } 93 } 94 } 95 } else { 96 if (a_areas[imap_index] !== undefined) { 97 for (var j = 0; j < a_areas[imap_index].length; j++) { 98 const area = a_areas[imap_index][j]; 99 if (area['id'] == id) { 100 return [imap_index, area['area'], j]; 101 } 102 } 103 } 104 } 105 } catch (e) { console.error("plugin:imapmarkers: EXCEPTION=" + e); } 106 return [-1, null, -1]; 107 } // function find_area_by_jquery_id 108 109 return { 110 // exported variables: 111 defaults: defaults, 112 a_maps: a_maps, 113 a_areas: a_areas, 114 a_references: a_references, 115 a_clicked_css_properties: a_clicked_css_properties, 116 a_cfg: a_cfg, 117 resize_timeout: resize_timeout, 118 a_last_clicked_id: a_last_clicked_id, 119 a_imap_div: a_imap_div, 120 imap_div_timeout: imap_div_timeout, 121 nr_startup_intervals: nr_startup_intervals, 122 mapster_decoration_obj_default: mapster_decoration_obj_default, 123 is_imagemapster_existing: is_imagemapster_existing, 124 // exported functions: 125 append_html_to_jquery_object: append_html_to_jquery_object, 126 calc_marker_pos: calc_marker_pos, 127 get_id_from_string: get_id_from_string, 128 get_index_from_string_end: get_index_from_string_end, 129 find_area_by_jquery_id: find_area_by_jquery_id, 130 }; 131 })(); 132 var _g = globalThis[Symbol.for('imapmarkers_storage')]; 133 134 _g.is_imagemapster_existing = jQuery.isFunction(jQuery.fn.mapster); 135 if (!_g.is_imagemapster_existing) { 136 // Load jQuery Imagemapster Library. SEE: http://www.outsharked.com/imagemapster/ 137 /* DOKUWIKI:include_once jquery.imagemapster.js */ 138 } 139 140 addEventListener("DOMContentLoaded", (event) => { 141 (function ($) { 142 //SEE:https://www.geeksforgeeks.org/how-to-get-all-css-styles-associated-with-an-element-using-jquery/ 143 $.fn.cssSpecific = function (str) { 144 var ob = {}; 145 if (this.length) { 146 var css = str.split(', '); 147 var prms = []; 148 for (var i = 0, ii = css.length; i < ii; i++) { 149 prms = css[i].split(':'); 150 ob[$.trim(prms[0])] = $(this).css($.trim(prms[1] || prms[0])); 151 } 152 } 153 return ob; 154 }; // $.fn.cssSpecific 155 156 function get_marker_width_and_height(id) { 157 return [$(id).width(), $(id).height()]; 158 } // function get_marker_width_and_height 159 160 function do_marker_if_resize() { 161 _g.a_imap_div.forEach((object, index) => { 162 if (_g.a_last_clicked_id[index] != undefined) { 163 let marker_id_jquery = "#imapmarkers-marker-" + index; 164 let id = _g.a_last_clicked_id[index]['id']; 165 if (_g.defaults['debug']) { console.log("RESIZE::[" + index + "] LAST CLICKED ID=" + id); } 166 if ((_g.a_last_clicked_id[index]['id'] !== undefined) && $(marker_id_jquery).is(":visible")) { 167 let area_index = _g.a_last_clicked_id[index]['area_index']; 168 if (_g.defaults['debug']) { console.log("RESIZE::[" + index + "] AREA-INDEX=" + area_index); } 169 try { 170 let found_area = _g.a_areas[index][area_index]['area']; 171 let coords = found_area.attr("coords"); 172 let xy = _g.calc_marker_pos(coords); 173 let wh = get_marker_width_and_height(marker_id_jquery); 174 $(marker_id_jquery).css({ top: xy[1] - wh[1] + 3, left: xy[0] - (wh[0] / 2) }); 175 } catch (e) { console.error("plugin:imapmarkers: EXCEPTION=" + e); } 176 } 177 } 178 }); 179 } // do_marker_if_resize 180 181 function do_resize() { 182 $('img[usemap]').each(function () { 183 let parent = $(this.offsetParent); 184 let parentparent = $(parent).parent(); 185 $(this).mapster('resize', ($(this)[0].naturalWidth < parentparent.width()) ? $(this)[0].naturalWidth : parentparent.width()); 186 }); 187 do_marker_if_resize(); 188 } // function do_resize 189 190 $(window).resize(function () { 191 if (_g.resize_timeout != null) { clearTimeout(_g.resize_timeout); } 192 _g.resize_timeout = setTimeout(do_resize, 100); 193 }); 194 195 let imap_do_main_function = function () { 196 if (_g.defaults['debug']) { console.log("imapmarkers::START IMAGEMAPPING MARKER"); } 197 _g.nr_startup_intervals++; 198 if (_g.nr_startup_intervals >= 10) { 199 // stop after 5 s searching: 200 if (_g.imap_div_timeout !== null) { clearTimeout(_g.imap_div_timeout); } 201 if (_g.defaults['debug']) { console.log("imapmarkers::GIVE UP IMAGEMAPPING SEARCH"); } 202 return; 203 } 204 // 205 // wait for another ImageMapster or not ...: 206 //setTimeout(function () { 207 if ($("img.mapster_el").length == 0) { 208 // initialize ImageMapster 209 $('img[usemap].imapmarkers').mapster(_g.mapster_decoration_obj_default); 210 if (_g.defaults['debug']) { console.log("NOT FOUND: img.mapster_el => INIT"); } 211 } else { 212 if (_g.defaults['debug']) { console.log("FOUND: img.mapster_el"); } 213 } 214 //}, ((_g.is_imagemapster_existing) ? 1000 : 0)); // 1000 : 0 215 // 216 // find container: 217 var imap_index = 0; 218 $(".imapmarkers-container").each(function (index, object) { 219 let imap_index = _g.get_index_from_string_end($(this).attr("id")); 220 if (_g.a_imap_div[imap_index] === undefined) { 221 if (_g.defaults['debug']) { console.log("[" + imap_index + "] CONTAINER FOUND ID='" + $(this).attr("id") + "'"); } 222 _g.a_imap_div[imap_index] = $(this); 223 } 224 }); 225 if ((_g.a_imap_div[0] !== undefined) && (_g.a_imap_div[0] !== null) && ($("img.mapster_el").length != 0)) { 226 // resize image: 227 do_resize(); 228 // find maps: 229 $(".imapmarkers-map").each(function (index, object) { 230 let imap_index = _g.get_index_from_string_end($(this).attr("name")); 231 if (_g.a_maps[imap_index] === undefined) { 232 _g.a_maps[imap_index] = $(this); 233 if (_g.defaults['debug']) { console.log("[" + imap_index + "] MAP FOUND NAME='" + $(this).attr("name") + "'"); } 234 } 235 }); 236 // set img z-index and collect areas: 237 _g.a_imap_div.forEach((object, index) => { 238 let imap_div_index = index; 239 if (_g.defaults['debug']) { console.log("[" + index + "] Z-INDEX THIS TAG=" + object.prop("tagName") + " ID=" + object.attr("id") + " NAME=" + object.attr("name")); } 240 // set z-index for images: 241 object.find("img").css("z-index", "0"); 242 // collect areas: 243 if (_g.a_maps[index] !== undefined) { 244 _g.a_maps[index].find("area").each(function (area_index, object) { 245 if (_g.a_areas[index] === undefined) { 246 _g.a_areas[index] = []; 247 } 248 const area_id = $(this).attr("location_id"); 249 _g.a_areas[index].push({ 'id': area_id, 'area': $(this) }); 250 if (_g.defaults['debug']) { console.log("[" + index + "] ADD AREA #" + area_index + " ID='" + area_id + "'"); } 251 }); 252 } 253 }); 254 // find configurations: 255 $(".imapmarkers-config").each(function (index, object) { 256 try { 257 let cfg_id = $(this).attr("id"); 258 let imap_index = _g.get_index_from_string_end(cfg_id); 259 let cfg_text = $(this).text(); 260 cfg_text = cfg_text.replaceAll('„', '"').replaceAll('“', '"'); 261 let cfg = JSON.parse(cfg_text); 262 if (imap_index >= 0) { 263 _g.a_cfg[imap_index] = cfg; 264 if (cfg['clicked-reference-css'] !== undefined) { 265 _g.a_clicked_css_properties[imap_index] = Object.keys(cfg['clicked-reference-css']).join(", "); 266 if (_g.defaults['debug']) { console.log("[" + imap_index + "] CLICKED CSS PROPERTIES='" + _g.a_clicked_css_properties[imap_index] + "'"); } 267 } 268 if (_g.defaults['debug']) { console.log("[" + imap_index + "] CFG FOUND ID=" + cfg_id + " CFG='" + cfg_text + "'"); } 269 var is_changed = false; 270 var mapster_decoration_obj = JSON.parse(JSON.stringify(_g.mapster_decoration_obj_default)); 271 if (cfg['area-fillColor'] != undefined) { 272 is_changed = true; 273 mapster_decoration_obj.fillColor = String(cfg['area-fillColor']); 274 } 275 if (cfg['area-fillOpacity'] != undefined) { 276 is_changed = true; 277 mapster_decoration_obj.fillOpacity = parseFloat(String(cfg['area-fillOpacity'])); 278 } 279 if (is_changed) { 280 let img_jquery_id = "#imapmarkers-img-" + imap_index; 281 if (_g.defaults['debug']) { console.log("[" + imap_index + "] APPLY LOOK TO IMG-JQUERY-ID='" + img_jquery_id + "'"); } 282 $(img_jquery_id).mapster('set_options', mapster_decoration_obj); 283 } 284 } else { 285 throw new Error("Id not found in any mapping! CONFIG-ID='" + cfg_id + "'"); 286 } 287 } catch (e) { 288 let msg = "EXCEPTION=" + e; 289 console.error("plugin:imapmarkers: " + msg); 290 $(this).css("background-color", "#FF0000"); 291 $(this).append(' <span style="color:white; background-color:red;">' + msg + '</span>'); 292 $(this).show(); 293 } 294 }); 295 // create marker: 296 _g.a_imap_div.forEach((object, index) => { 297 let imap_div_index = index; 298 let marker_id = "imapmarkers-marker-" + index; 299 let marker_id_jquery = "#" + marker_id; 300 let is_marker_internal = true; 301 if ((_g.a_cfg[imap_div_index] !== undefined) && (_g.a_cfg[imap_div_index]['marker'] !== undefined) && (String(_g.a_cfg[imap_div_index]['marker']).length > 0)) { 302 let marker_loc = String(_g.a_cfg[imap_div_index]['marker']); 303 if (marker_loc != "internal") { 304 var marker_src; 305 if (marker_loc.match(/^https{0,1}:\/\//) !== null) { 306 // http[s] URI: 307 marker_src = '<img id="' + marker_id + '" src="' + marker_loc + '">'; 308 } else { 309 // media link: 310 marker_src = '<img id="' + marker_id + '" src="/lib/exe/fetch.php?media=' + marker_loc + '">'; 311 } 312 let imagemapster_div = _g.a_imap_div[imap_div_index].find("div.imapmarkers\\-image"); 313 _g.append_html_to_jquery_object(marker_src, imagemapster_div); 314 if ((_g.a_cfg[imap_div_index] !== undefined) && (_g.a_cfg[imap_div_index]['marker-width'] !== undefined)) { 315 $(marker_id_jquery).css("width", parseInt(_g.a_cfg[imap_div_index]['marker-width'])); 316 } 317 if ((_g.a_cfg[imap_div_index] !== undefined) && (_g.a_cfg[imap_div_index]['marker-height'] !== undefined)) { 318 $(marker_id_jquery).css("height", parseInt(_g.a_cfg[imap_div_index]['marker-height'])); 319 } 320 is_marker_internal = false; 321 } 322 } 323 if (is_marker_internal) { 324 // create internal, default, marker: 325 let svg_marker = '<svg version="1.1" id="' + marker_id + '" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 365 560" enable-background="new 0 0 365 560" xml:space="preserve"> ' + 326 '<g> ' + 327 ' <path fill="#00AEEF" d="M182.9,551.7c0,0.1,0.2,0.3,0.2,0.3S358.3,283,358.3,194.6c0-130.1-88.8-186.7-175.4-186.9 ' + 328 ' C96.3,7.9,7.5,64.5,7.5,194.6c0,88.4,175.3,357.4,175.3,357.4S182.9,551.7,182.9,551.7z M122.2,187.2c0-33.6,27.2-60.8,60.8-60.8 ' + 329 ' c33.6,0,60.8,27.2,60.8,60.8S216.5,248,182.9,248C149.4,248,122.2,220.8,122.2,187.2z"/> ' + 330 '</g> ' + 331 '</svg>'; 332 let imagemapster_div = _g.a_imap_div[imap_div_index].find("div.imapmarkers\\-image"); 333 _g.append_html_to_jquery_object(svg_marker, imagemapster_div); 334 $(marker_id_jquery).css("width", 21); 335 $(marker_id_jquery).css("height", 32); 336 if ((_g.a_cfg[imap_div_index] !== undefined) && (_g.a_cfg[imap_div_index]['marker-width'] !== undefined)) { 337 $(marker_id_jquery).css("width", parseInt(_g.a_cfg[imap_div_index]['marker-width'])); 338 } 339 if ((_g.a_cfg[imap_div_index] !== undefined) && (_g.a_cfg[imap_div_index]['marker-height'] !== undefined)) { 340 $(marker_id_jquery).css("height", parseInt(_g.a_cfg[imap_div_index]['marker-height'])); 341 } 342 $(marker_id_jquery).css("top", -32); 343 $(marker_id_jquery).css("left", -32); 344 $("path", $(marker_id_jquery)).attr("style", "fill:" + _g.defaults['marker_color']); 345 if ((_g.a_cfg[imap_div_index] !== undefined) && (_g.a_cfg[imap_div_index]['marker-color'] !== undefined)) { 346 $("path", $(marker_id_jquery)).attr("style", "fill:" + _g.a_cfg[imap_div_index]['marker-color']); 347 } 348 } 349 $(marker_id_jquery).css("position", "absolute"); 350 $(marker_id_jquery).css("z-index", 1000); 351 $(marker_id_jquery).hide(); 352 if (_g.defaults['debug']) { console.log("[" + index + "] ADD MARKER ID='" + marker_id + "'"); } 353 }); 354 // setup clicked css style keywords: 355 _g.a_imap_div.forEach((object, index) => { 356 try { 357 if (_g.a_clicked_css_properties[index] === undefined) { 358 _g.a_clicked_css_properties[index] = Object.keys(_g.defaults['clicked-reference-css']).join(", "); 359 if (_g.defaults['debug']) { console.log("[" + index + "] CLICKED CSS PROPERTIES='" + _g.a_clicked_css_properties[index] + "'"); } 360 } 361 } catch (e) { console.error("plugin:imapmarkers: EXCEPTION=" + e); } 362 }); 363 // search for references: 364 $(".imapmarkers-location, .wrap_imapmloc").each(function (index, object) { 365 try { 366 var loc_id = null; 367 if ($(this).hasClass("imapmarkers-location")) { 368 loc_id = $(this).attr("location_id"); 369 if (_g.defaults['debug']) { console.log("FOUND IMAPMLOC NORMAL ID='" + loc_id + "' FONT-WEIGHT=" + $(this).css("font-weight") + " COLOR=" + $(this).css("color")); } 370 } 371 if ($(this).hasClass("wrap_imapmloc")) { 372 loc_id = _g.get_id_from_string($(this).text()); 373 if (_g.defaults['debug']) { console.log("FOUND IMAPMLOC WRAP ID='" + loc_id + "' FONT-WEIGHT=" + $(this).css("font-weight") + " COLOR=" + $(this).css("color")); } 374 } 375 if (loc_id !== null) { 376 // find corresponding area 377 let ia = _g.find_area_by_jquery_id(loc_id); 378 //if (_g.defaults['debug']) { console.log("SEARCH AREA -> '" + JSON.stringify(ia) + "'"); } 379 let imap_index = ia[0]; 380 let found_area = ia[1]; 381 let area_index = ia[2]; 382 if (found_area != null) { 383 if (_g.a_references[imap_index] === undefined) { 384 _g.a_references[imap_index] = []; 385 } 386 _g.a_references[imap_index].push({ 'id': loc_id, 'imap_index': imap_index, 'area_index': area_index, 'reference': $(this), 'css': $(this).cssSpecific(_g.a_clicked_css_properties[imap_index]) }); 387 let reference_index = _g.a_references[imap_index].length - 1; 388 if (_g.defaults['debug']) { console.log("FOUND AREA FOR IMAPMLOC ID='" + loc_id + "' AREA IMAP-INDEX=" + imap_index + " AREA-INDEX=" + area_index + " CSS='" + JSON.stringify($(this).cssSpecific(_g.a_clicked_css_properties[imap_index])) + "'"); } 389 $(this).on("click", { 'area': found_area, 'id': loc_id, 'imap_index': imap_index, 'area_index': area_index, 'reference_index': reference_index }, function (e) { 390 var data = e.data; 391 let imap_index = data['imap_index']; 392 let marker_id_jquery = "#imapmarkers-marker-" + imap_index; 393 let area_index = data['area_index']; 394 let id = data['id']; 395 let reference_index = data['reference_index']; 396 if (_g.defaults['debug']) { console.log("CLICK ID='" + id + "' AREA IMAP-INDEX=" + imap_index + " AREA-INDEX=" + area_index); } 397 if ((_g.a_last_clicked_id[imap_index] !== undefined) && (_g.a_last_clicked_id[imap_index]['id'] == data['id']) && $(marker_id_jquery).is(":visible")) { 398 // hide marker 399 $(marker_id_jquery).hide(); 400 $(marker_id_jquery).css('cursor', ''); 401 _g.a_references[imap_index].forEach((object, index) => { 402 if (object['id'] == id) { 403 object['reference'].css(object['css']); 404 } 405 }); 406 $(this).css(_g.a_references[imap_index][reference_index]['css']); 407 } else { 408 // show marker 409 let coords = data['area'].attr("coords"); 410 let xy = _g.calc_marker_pos(coords); 411 let wh = get_marker_width_and_height(marker_id_jquery); 412 $(marker_id_jquery).css({ top: xy[1] - wh[1] + 3, left: xy[0] - (wh[0] / 2) }); 413 let href = _g.a_areas[imap_index][area_index]['area'].attr('href'); 414 if (String(href).length > 0) { 415 $(marker_id_jquery).css('cursor', 'pointer'); 416 $(marker_id_jquery).on("click", { 'imap_index': imap_index, 'area_index': area_index }, function () { 417 let href = _g.a_areas[imap_index][area_index]['area'].attr('href');; 418 if (_g.defaults['debug']) { console.log("[" + imap_index + "] CLICK ON MARKER HREF='" + href + "'"); } 419 window.location.href = href; 420 }); 421 } 422 $(marker_id_jquery).show(); 423 _g.a_references[imap_index].forEach((object, index) => { 424 if (object['id'] == id) { 425 if ((_g.a_cfg[imap_index] !== undefined) && (_g.a_cfg[imap_index]['clicked-reference-css'] !== undefined)) { 426 object['reference'].css(_g.a_cfg[imap_index]['clicked-reference-css']); 427 } else { 428 object['reference'].css(_g.defaults['clicked-reference-css']); 429 } 430 } else { 431 object['reference'].css(object['css']); 432 } 433 }); 434 } 435 if (_g.defaults['debug']) { console.log("[" + imap_index + "] LAST CLICKED ID='" + data['id'] + " MARKER-ID='" + marker_id_jquery + "'"); } 436 _g.a_last_clicked_id[imap_index] = { 'imap_index': imap_index, 'area_index': area_index, 'id': data['id'] }; 437 }); 438 $(this).css('cursor', 'pointer'); 439 } 440 } 441 } catch (e) { console.error("plugin:imapmarkers: EXCEPTION=" + e); } 442 }); 443 } else { 444 if (_g.imap_div_timeout !== null) { clearTimeout(_g.imap_div_timeout); } 445 _g.imap_div_timeout = setTimeout(imap_do_main_function, 500); 446 } 447 }; 448 _g.imap_div_timeout = setTimeout(imap_do_main_function, 100); 449 })(jQuery); 450 }); 451} else { 452 console.error("plugin:imapmarkers: jQuery is undefined!"); 453} 454