12ebb07b4SGreg Roach/** 22ebb07b4SGreg Roach * webtrees: online genealogy 32ebb07b4SGreg Roach * Copyright (C) 2019 webtrees development team 42ebb07b4SGreg Roach * This program is free software: you can redistribute it and/or modify 52ebb07b4SGreg Roach * it under the terms of the GNU General Public License as published by 62ebb07b4SGreg Roach * the Free Software Foundation, either version 3 of the License, or 72ebb07b4SGreg Roach * (at your option) any later version. 82ebb07b4SGreg Roach * This program is distributed in the hope that it will be useful, 92ebb07b4SGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of 102ebb07b4SGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 112ebb07b4SGreg Roach * GNU General Public License for more details. 122ebb07b4SGreg Roach * You should have received a copy of the GNU General Public License 132ebb07b4SGreg Roach * along with this program. If not, see <http://www.gnu.org/licenses/>. 142ebb07b4SGreg Roach */ 152ebb07b4SGreg Roach 162ebb07b4SGreg Roachfunction TreeViewHandler(treeview_instance, ged) { 172ebb07b4SGreg Roach var tv = this; // Store "this" for usage within jQuery functions where "this" is not this ;-) 182ebb07b4SGreg Roach 192ebb07b4SGreg Roach this.treeview = $("#" + treeview_instance + "_in"); 202ebb07b4SGreg Roach this.loadingImage = $("#" + treeview_instance + "_loading"); 212ebb07b4SGreg Roach this.toolbox = $("#tv_tools"); 222ebb07b4SGreg Roach this.buttons = $(".tv_button:first", this.toolbox); 232ebb07b4SGreg Roach this.zoom = 100; // in percent 242ebb07b4SGreg Roach this.boxWidth = 180; // default family box width 252ebb07b4SGreg Roach this.boxExpandedWidth = 250; // default expanded family box width 262ebb07b4SGreg Roach this.cookieDays = 3; // lifetime of preferences memory, in days 27*06f42609SGreg Roach this.ajaxDetails = document.getElementById(treeview_instance + "_out").dataset.urlDetails + "?instance=" + encodeURIComponent(treeview_instance); 28*06f42609SGreg Roach this.ajaxPersons = document.getElementById(treeview_instance + "_out").dataset.urlIndividuals + "?instance=" + encodeURIComponent(treeview_instance); 292ebb07b4SGreg Roach 302ebb07b4SGreg Roach this.container = this.treeview.parent(); // Store the container element ("#" + treeview_instance + "_out") 312ebb07b4SGreg Roach this.auto_box_width = false; 322ebb07b4SGreg Roach this.updating = false; 332ebb07b4SGreg Roach 342ebb07b4SGreg Roach // Restore user preferences 352ebb07b4SGreg Roach if (readCookie("compact") === "true") { 362ebb07b4SGreg Roach tv.compact(); 372ebb07b4SGreg Roach } 382ebb07b4SGreg Roach 392ebb07b4SGreg Roach /////////////////////////////////////////////// 402ebb07b4SGreg Roach // Based on https://codepen.io/chriscoyier/pen/zdsty 412ebb07b4SGreg Roach (function($) { 422ebb07b4SGreg Roach $.fn.drags = function(opt) { 432ebb07b4SGreg Roach var $el = this; 442ebb07b4SGreg Roach 452ebb07b4SGreg Roach return $el.css('cursor', 'move').on("mousedown", function(e) { 462ebb07b4SGreg Roach var $drag = $(this); 472ebb07b4SGreg Roach var drg_h = $drag.outerHeight(); 482ebb07b4SGreg Roach var drg_w = $drag.outerWidth(); 492ebb07b4SGreg Roach var pos_y = $drag.offset().top + drg_h - e.pageY; 502ebb07b4SGreg Roach var pos_x = $drag.offset().left + drg_w - e.pageX; 512ebb07b4SGreg Roach 522ebb07b4SGreg Roach $drag.addClass('draggable'); 532ebb07b4SGreg Roach 542ebb07b4SGreg Roach $(document) 552ebb07b4SGreg Roach .on("mousemove", function(e) { 562ebb07b4SGreg Roach $('.draggable').offset({ 572ebb07b4SGreg Roach top:e.pageY + pos_y - drg_h, 582ebb07b4SGreg Roach left:e.pageX + pos_x - drg_w 592ebb07b4SGreg Roach }).on("mouseup", function() { 602ebb07b4SGreg Roach $drag.removeClass('draggable'); 612ebb07b4SGreg Roach }); 622ebb07b4SGreg Roach }).on("mouseup", function() { 632ebb07b4SGreg Roach $drag.removeClass('draggable'); 642ebb07b4SGreg Roach tv.updateTree(); 652ebb07b4SGreg Roach }); 662ebb07b4SGreg Roach e.preventDefault(); 672ebb07b4SGreg Roach }); 682ebb07b4SGreg Roach 692ebb07b4SGreg Roach } 702ebb07b4SGreg Roach })(jQuery); 712ebb07b4SGreg Roach 722ebb07b4SGreg Roach tv.treeview.drags(); 732ebb07b4SGreg Roach /////////////////////////////////////////////// 742ebb07b4SGreg Roach 752ebb07b4SGreg Roach // Add click handlers to buttons 762ebb07b4SGreg Roach tv.toolbox.find("#tvbCompact").each(function (index, tvCompact) { 772ebb07b4SGreg Roach tvCompact.onclick = function () { 782ebb07b4SGreg Roach tv.compact(); 792ebb07b4SGreg Roach }; 802ebb07b4SGreg Roach }); 812ebb07b4SGreg Roach // If we click the "hide/show all partners" button, toggle the setting before reloading the page 822ebb07b4SGreg Roach tv.toolbox.find("#tvbAllPartners").each(function (index, tvAllPartners) { 832ebb07b4SGreg Roach tvAllPartners.onclick = function () { 842ebb07b4SGreg Roach createCookie("allPartners", readCookie("allPartners") === "true" ? "false" : "true", tv.cookieDays); 852ebb07b4SGreg Roach document.location = document.location; 862ebb07b4SGreg Roach }; 872ebb07b4SGreg Roach }); 882ebb07b4SGreg Roach tv.toolbox.find("#tvbOpen").each(function (index, tvbOpen) { 892ebb07b4SGreg Roach var b = $(tvbOpen, tv.toolbox); 902ebb07b4SGreg Roach tvbOpen.onclick = function () { 912ebb07b4SGreg Roach b.addClass("tvPressed"); 922ebb07b4SGreg Roach tv.setLoading(); 932ebb07b4SGreg Roach var e = jQuery.Event("click"); 942ebb07b4SGreg Roach tv.treeview.find(".tv_box:not(.boxExpanded)").each(function (index, box) { 952ebb07b4SGreg Roach var pos = $(box, tv.treeview).offset(); 962ebb07b4SGreg Roach if (pos.left >= tv.leftMin && pos.left <= tv.leftMax && pos.top >= tv.topMin && pos.top <= tv.topMax) { 972ebb07b4SGreg Roach tv.expandBox(box, e); 982ebb07b4SGreg Roach } 992ebb07b4SGreg Roach }); 1002ebb07b4SGreg Roach b.removeClass("tvPressed"); 1012ebb07b4SGreg Roach tv.setComplete(); 1022ebb07b4SGreg Roach }; 1032ebb07b4SGreg Roach }); 1042ebb07b4SGreg Roach tv.toolbox.find("#tvbClose").each(function (index, tvbClose) { 1052ebb07b4SGreg Roach var b = $(tvbClose, tv.toolbox); 1062ebb07b4SGreg Roach tvbClose.onclick = function () { 1072ebb07b4SGreg Roach b.addClass("tvPressed"); 1082ebb07b4SGreg Roach tv.setLoading(); 1092ebb07b4SGreg Roach tv.treeview.find(".tv_box.boxExpanded").each(function (index, box) { 1102ebb07b4SGreg Roach $(box).css("display", "none").removeClass("boxExpanded").parent().find(".tv_box.collapsedContent").css("display", "block"); 1112ebb07b4SGreg Roach }); 1122ebb07b4SGreg Roach b.removeClass("tvPressed"); 1132ebb07b4SGreg Roach tv.setComplete(); 1142ebb07b4SGreg Roach }; 1152ebb07b4SGreg Roach }); 1162ebb07b4SGreg Roach 1172ebb07b4SGreg Roach tv.centerOnRoot(); // fire ajax update if needed, which call setComplete() when all is loaded 1182ebb07b4SGreg Roach} 1192ebb07b4SGreg Roach/** 1202ebb07b4SGreg Roach * Class TreeView setLoading method 1212ebb07b4SGreg Roach */ 1222ebb07b4SGreg RoachTreeViewHandler.prototype.setLoading = function () { 1232ebb07b4SGreg Roach this.treeview.css("cursor", "wait"); 1242ebb07b4SGreg Roach this.loadingImage.css("display", "block"); 1252ebb07b4SGreg Roach}; 1262ebb07b4SGreg Roach/** 1272ebb07b4SGreg Roach * Class TreeView setComplete method 1282ebb07b4SGreg Roach */ 1292ebb07b4SGreg RoachTreeViewHandler.prototype.setComplete = function () { 1302ebb07b4SGreg Roach this.treeview.css("cursor", "move"); 1312ebb07b4SGreg Roach this.loadingImage.css("display", "none"); 1322ebb07b4SGreg Roach}; 1332ebb07b4SGreg Roach 1342ebb07b4SGreg Roach/** 1352ebb07b4SGreg Roach * Class TreeView getSize method 1362ebb07b4SGreg Roach * Store the viewport current size 1372ebb07b4SGreg Roach */ 1382ebb07b4SGreg RoachTreeViewHandler.prototype.getSize = function () { 1392ebb07b4SGreg Roach var tv = this; 1402ebb07b4SGreg Roach // retrieve the current container bounding box 1412ebb07b4SGreg Roach var container = tv.container.parent(); 1422ebb07b4SGreg Roach var offset = container.offset(); 1432ebb07b4SGreg Roach tv.leftMin = offset.left; 1442ebb07b4SGreg Roach tv.leftMax = tv.leftMin + container.innerWidth(); 1452ebb07b4SGreg Roach tv.topMin = offset.top; 1462ebb07b4SGreg Roach tv.topMax = tv.topMin + container.innerHeight(); 1472ebb07b4SGreg Roach /* 1482ebb07b4SGreg Roach var frm = $("#tvTreeBorder"); 1492ebb07b4SGreg Roach tv.treeview.css("width", frm.width()); 1502ebb07b4SGreg Roach tv.treeview.css("height", frm.height());*/ 1512ebb07b4SGreg Roach}; 1522ebb07b4SGreg Roach 1532ebb07b4SGreg Roach/** 1542ebb07b4SGreg Roach * Class TreeView updateTree method 1552ebb07b4SGreg Roach * Perform ajax requests to complete the tree after drag 1562ebb07b4SGreg Roach * param boolean @center center on root person when done 1572ebb07b4SGreg Roach */ 1582ebb07b4SGreg RoachTreeViewHandler.prototype.updateTree = function (center, button) { 1592ebb07b4SGreg Roach var tv = this; // Store "this" for usage within jQuery functions where "this" is not this ;-) 1602ebb07b4SGreg Roach var to_load = []; 1612ebb07b4SGreg Roach var elts = []; 1622ebb07b4SGreg Roach this.getSize(); 1632ebb07b4SGreg Roach 1642ebb07b4SGreg Roach // check which td with datafld attribute are within the container bounding box 1652ebb07b4SGreg Roach // and therefore need to be dynamically loaded 1662ebb07b4SGreg Roach tv.treeview.find("td[abbr]").each(function (index, el) { 1672ebb07b4SGreg Roach el = $(el, tv.treeview); 1682ebb07b4SGreg Roach var pos = el.offset(); 1692ebb07b4SGreg Roach if (pos.left >= tv.leftMin && pos.left <= tv.leftMax && pos.top >= tv.topMin && pos.top <= tv.topMax) { 1702ebb07b4SGreg Roach to_load.push(el.attr("abbr")); 1712ebb07b4SGreg Roach elts.push(el); 1722ebb07b4SGreg Roach } 1732ebb07b4SGreg Roach }); 1742ebb07b4SGreg Roach // if some boxes need update, we perform an ajax request 1752ebb07b4SGreg Roach if (to_load.length > 0) { 1762ebb07b4SGreg Roach tv.updating = true; 1772ebb07b4SGreg Roach tv.setLoading(); 1782ebb07b4SGreg Roach jQuery.ajax({ 179*06f42609SGreg Roach url: tv.ajaxPersons, 1802ebb07b4SGreg Roach dataType: "json", 1812ebb07b4SGreg Roach data: "q=" + to_load.join(";"), 1822ebb07b4SGreg Roach success: function (ret) { 1832ebb07b4SGreg Roach var nb = elts.length; 1842ebb07b4SGreg Roach var root_element = $(".rootPerson", this.treeview); 1852ebb07b4SGreg Roach var l = root_element.offset().left; 1862ebb07b4SGreg Roach for (var i = 0; i < nb; i++) { 1872ebb07b4SGreg Roach elts[i].removeAttr("abbr").html(ret[i]); 1882ebb07b4SGreg Roach } 1892ebb07b4SGreg Roach // we now ajust the draggable treeview size to its content size 1902ebb07b4SGreg Roach tv.getSize(); 1912ebb07b4SGreg Roach }, 1922ebb07b4SGreg Roach complete: function () { 1932ebb07b4SGreg Roach if (tv.treeview.find("td[abbr]").length) { 1942ebb07b4SGreg Roach tv.updateTree(center, button); // recursive call 1952ebb07b4SGreg Roach } 1962ebb07b4SGreg Roach // the added boxes need that in mode compact boxes 1972ebb07b4SGreg Roach if (tv.auto_box_width) { 1982ebb07b4SGreg Roach tv.treeview.find(".tv_box").css("width", "auto"); 1992ebb07b4SGreg Roach } 2002ebb07b4SGreg Roach tv.updating = true; // avoid an unuseful recursive call when all requested persons are loaded 2012ebb07b4SGreg Roach if (center) { 2022ebb07b4SGreg Roach tv.centerOnRoot(); 2032ebb07b4SGreg Roach } 2042ebb07b4SGreg Roach if (button) { 2052ebb07b4SGreg Roach button.removeClass("tvPressed"); 2062ebb07b4SGreg Roach } 2072ebb07b4SGreg Roach tv.setComplete(); 2082ebb07b4SGreg Roach tv.updating = false; 2092ebb07b4SGreg Roach }, 2102ebb07b4SGreg Roach timeout: function () { 2112ebb07b4SGreg Roach if (button) { 2122ebb07b4SGreg Roach button.removeClass("tvPressed"); 2132ebb07b4SGreg Roach } 2142ebb07b4SGreg Roach tv.updating = false; 2152ebb07b4SGreg Roach tv.setComplete(); 2162ebb07b4SGreg Roach } 2172ebb07b4SGreg Roach }); 2182ebb07b4SGreg Roach } else { 2192ebb07b4SGreg Roach if (button) { 2202ebb07b4SGreg Roach button.removeClass("tvPressed"); 2212ebb07b4SGreg Roach } 2222ebb07b4SGreg Roach tv.setComplete(); 2232ebb07b4SGreg Roach } 2242ebb07b4SGreg Roach return false; 2252ebb07b4SGreg Roach}; 2262ebb07b4SGreg Roach 2272ebb07b4SGreg Roach/** 2282ebb07b4SGreg Roach * Class TreeView compact method 2292ebb07b4SGreg Roach */ 2302ebb07b4SGreg RoachTreeViewHandler.prototype.compact = function () { 2312ebb07b4SGreg Roach var tv = this; 2322ebb07b4SGreg Roach var b = $("#tvbCompact", tv.toolbox); 2332ebb07b4SGreg Roach tv.setLoading(); 2342ebb07b4SGreg Roach if (tv.auto_box_width) { 2352ebb07b4SGreg Roach var w = tv.boxWidth * (tv.zoom / 100) + "px"; 2362ebb07b4SGreg Roach var ew = tv.boxExpandedWidth * (tv.zoom / 100) + "px"; 2372ebb07b4SGreg Roach tv.treeview.find(".tv_box:not(boxExpanded)", tv.treeview).css("width", w); 2382ebb07b4SGreg Roach tv.treeview.find(".boxExpanded", tv.treeview).css("width", ew); 2392ebb07b4SGreg Roach tv.auto_box_width = false; 2402ebb07b4SGreg Roach if (readCookie("compact")) { 2412ebb07b4SGreg Roach createCookie("compact", false, tv.cookieDays); 2422ebb07b4SGreg Roach } 2432ebb07b4SGreg Roach b.removeClass("tvPressed"); 2442ebb07b4SGreg Roach } else { 2452ebb07b4SGreg Roach tv.treeview.find(".tv_box").css("width", "auto"); 2462ebb07b4SGreg Roach tv.auto_box_width = true; 2472ebb07b4SGreg Roach if (!readCookie("compact")) { 2482ebb07b4SGreg Roach createCookie("compact", true, tv.cookieDays); 2492ebb07b4SGreg Roach } 2502ebb07b4SGreg Roach if (!tv.updating) { 2512ebb07b4SGreg Roach tv.updateTree(); 2522ebb07b4SGreg Roach } 2532ebb07b4SGreg Roach b.addClass("tvPressed"); 2542ebb07b4SGreg Roach } 2552ebb07b4SGreg Roach tv.setComplete(); 2562ebb07b4SGreg Roach return false; 2572ebb07b4SGreg Roach}; 2582ebb07b4SGreg Roach 2592ebb07b4SGreg Roach/** 2602ebb07b4SGreg Roach * Class TreeView centerOnRoot method 2612ebb07b4SGreg Roach */ 2622ebb07b4SGreg RoachTreeViewHandler.prototype.centerOnRoot = function () { 2632ebb07b4SGreg Roach this.loadingImage.css("display", "block"); 2642ebb07b4SGreg Roach var tv = this; 2652ebb07b4SGreg Roach var tvc = this.container; 2662ebb07b4SGreg Roach var tvc_width = tvc.innerWidth() / 2; 2672ebb07b4SGreg Roach if (isNaN(tvc_width)) { 2682ebb07b4SGreg Roach return false; 2692ebb07b4SGreg Roach } 2702ebb07b4SGreg Roach var tvc_height = tvc.innerHeight() / 2; 2712ebb07b4SGreg Roach var root_person = $(".rootPerson", this.treeview); 2722ebb07b4SGreg Roach 2732ebb07b4SGreg Roach if (!this.updating) { 2742ebb07b4SGreg Roach tv.setComplete(); 2752ebb07b4SGreg Roach } 2762ebb07b4SGreg Roach return false; 2772ebb07b4SGreg Roach}; 2782ebb07b4SGreg Roach 2792ebb07b4SGreg Roach/** 2802ebb07b4SGreg Roach * Class TreeView expandBox method 2812ebb07b4SGreg Roach * param string @box the person box element 2822ebb07b4SGreg Roach * param string @event the call event 2832ebb07b4SGreg Roach * param string @pid the person id 2842ebb07b4SGreg Roach * 2852ebb07b4SGreg Roach * called ONLY for elements which have NOT the class tv_link to avoid unuseful requests to the server 2862ebb07b4SGreg Roach */ 2872ebb07b4SGreg RoachTreeViewHandler.prototype.expandBox = function (box, event) { 2882ebb07b4SGreg Roach var t = $(event.target); 2892ebb07b4SGreg Roach if (t.hasClass("tv_link")) { 2902ebb07b4SGreg Roach return false; 2912ebb07b4SGreg Roach } 2922ebb07b4SGreg Roach 2932ebb07b4SGreg Roach var box = $(box, this.treeview); 2942ebb07b4SGreg Roach var bc = box.parent(); // bc is Box Container 2952ebb07b4SGreg Roach var pid = box.attr("abbr"); 2962ebb07b4SGreg Roach var tv = this; // Store "this" for usage within jQuery functions where "this" is not this ;-) 2972ebb07b4SGreg Roach var expanded; 2982ebb07b4SGreg Roach var collapsed; 2992ebb07b4SGreg Roach 3002ebb07b4SGreg Roach if (bc.hasClass("detailsLoaded")) { 3012ebb07b4SGreg Roach collapsed = bc.find(".collapsedContent"); 3022ebb07b4SGreg Roach expanded = bc.find(".tv_box:not(.collapsedContent)"); 3032ebb07b4SGreg Roach } else { 3042ebb07b4SGreg Roach // Cache the box content as an hidden person's box in the box's parent element 3052ebb07b4SGreg Roach expanded = box; 3062ebb07b4SGreg Roach collapsed = box.clone(); 3072ebb07b4SGreg Roach bc.append(collapsed.addClass("collapsedContent").css("display", "none")); 3082ebb07b4SGreg Roach // we add a waiting image at the right side of the box 3092ebb07b4SGreg Roach var loading_image = this.loadingImage.find("img").clone().addClass("tv_box_loading").css("display", "block"); 3102ebb07b4SGreg Roach box.prepend(loading_image); 3112ebb07b4SGreg Roach tv.updating = true; 3122ebb07b4SGreg Roach tv.setLoading(); 3132ebb07b4SGreg Roach // perform the Ajax request and load the result in the box 314*06f42609SGreg Roach box.load(tv.ajaxDetails + "&pid=" + encodeURIComponent(pid), function () { 3152ebb07b4SGreg Roach // If Lightbox module is active, we reinitialize it for the new links 3162ebb07b4SGreg Roach if (typeof CB_Init === "function") { 3172ebb07b4SGreg Roach CB_Init(); 3182ebb07b4SGreg Roach } 3192ebb07b4SGreg Roach box.css("width", tv.boxExpandedWidth * (tv.zoom / 100) + "px"); 3202ebb07b4SGreg Roach loading_image.remove(); 3212ebb07b4SGreg Roach bc.addClass("detailsLoaded"); 3222ebb07b4SGreg Roach tv.setComplete(); 3232ebb07b4SGreg Roach tv.updating = false; 3242ebb07b4SGreg Roach }); 3252ebb07b4SGreg Roach } 3262ebb07b4SGreg Roach if (box.hasClass("boxExpanded")) { 3272ebb07b4SGreg Roach expanded.css("display", "none"); 3282ebb07b4SGreg Roach collapsed.css("display", "block"); 3292ebb07b4SGreg Roach box.removeClass("boxExpanded"); 3302ebb07b4SGreg Roach } else { 3312ebb07b4SGreg Roach expanded.css("display", "block"); 3322ebb07b4SGreg Roach collapsed.css("display", "none"); 3332ebb07b4SGreg Roach expanded.addClass("boxExpanded"); 3342ebb07b4SGreg Roach } 3352ebb07b4SGreg Roach // we must ajust the draggable treeview size to its content size 3362ebb07b4SGreg Roach this.getSize(); 3372ebb07b4SGreg Roach return false; 3382ebb07b4SGreg Roach}; 3392ebb07b4SGreg Roach 3402ebb07b4SGreg Roachfunction createCookie(name, value, days) { 3412ebb07b4SGreg Roach if (days) { 3422ebb07b4SGreg Roach var date = new Date(); 3432ebb07b4SGreg Roach date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); 3442ebb07b4SGreg Roach document.cookie = name + "=" + value + "; expires=" + date.toGMTString() + "; path=/"; 3452ebb07b4SGreg Roach } else { 3462ebb07b4SGreg Roach document.cookie = name + "=" + value + "; path=/"; 3472ebb07b4SGreg Roach } 3482ebb07b4SGreg Roach} 3492ebb07b4SGreg Roach 3502ebb07b4SGreg Roachfunction readCookie(name) { 3512ebb07b4SGreg Roach var name_equals = name + "="; 3522ebb07b4SGreg Roach var ca = document.cookie.split(';'); 3532ebb07b4SGreg Roach for (var i = 0; i < ca.length; i++) { 3542ebb07b4SGreg Roach var c = ca[i]; 3552ebb07b4SGreg Roach while (c.charAt(0) === ' ') { 3562ebb07b4SGreg Roach c = c.substring(1, c.length); 3572ebb07b4SGreg Roach } 3582ebb07b4SGreg Roach if (c.indexOf(name_equals) === 0) { 3592ebb07b4SGreg Roach return c.substring(name_equals.length, c.length); 3602ebb07b4SGreg Roach } 3612ebb07b4SGreg Roach } 3622ebb07b4SGreg Roach return null; 3632ebb07b4SGreg Roach} 364