| @@ -7,8 +7,22 @@ | ||
| 7 | 7 | function ($scope) { |
| 8 | 8 | elementorFrontend.elementsHandler.addHandler( |
| 9 | 9 | elementorModules.frontend.handlers.Base.extend({ |
| 10 | 10 | onInit() { |
| 11 | + const normalizeSafeUrl = (rawUrl) => { | |
| 12 | + if (!rawUrl || typeof rawUrl !== "string") return null; | |
| 13 | + try { | |
| 14 | + const url = new URL(rawUrl, window.location.href); | |
| 15 | + const allowedProtocols = ["http:", "https:", "mailto:", "tel:"]; | |
| 16 | + if (allowedProtocols.includes(url.protocol)) { | |
| 17 | + return url.href; | |
| 18 | + } | |
| 19 | + } catch (e) { | |
| 20 | + // ignore | |
| 21 | + } | |
| 22 | + return null; | |
| 23 | + }; | |
| 24 | + | |
| 11 | 25 | const $elem = this.$element, |
| 12 | 26 | $wrap = $elem.find(".king-addons-image-accordion"), |
| 13 | 27 | $wrapContainer = $elem.find(".king-addons-image-accordion-wrap"), |
| 14 | 28 | settings = JSON.parse( |
| @@ -19,8 +33,56 @@ | ||
| 19 | 33 | lightboxAttr = $wrap.attr("lightbox"), |
| 20 | 34 | lightboxSettings = lightboxAttr ? JSON.parse(lightboxAttr) : "", |
| 21 | 35 | $accordionItems = $elem.find(".king-addons-image-accordion-item"); |
| 22 | 36 | |
| 37 | + // Content stays at the open-item size for the whole animation. | |
| 38 | + // Clearing that size when the transition ends was an instant | |
| 39 | + // resize: the text wrapped and the block jumped. | |
| 40 | + const contentAxis = () => | |
| 41 | + $wrap.css("flex-direction") === "column" ? "height" : "width"; | |
| 42 | + | |
| 43 | + const setOpenContentSize = () => { | |
| 44 | + const axis = contentAxis(); | |
| 45 | + const margin = axis === "height" ? "margin-bottom" : "margin-right"; | |
| 46 | + const count = $accordionItems.length; | |
| 47 | + let gaps = 0; | |
| 48 | + | |
| 49 | + $accordionItems.each(function (index) { | |
| 50 | + if (index < count - 1) { | |
| 51 | + gaps += parseFloat($(this).css(margin)) || 0; | |
| 52 | + } | |
| 53 | + }); | |
| 54 | + | |
| 55 | + const total = | |
| 56 | + ($wrap[axis === "height" ? "innerHeight" : "innerWidth"]() || 0) - gaps; | |
| 57 | + const activeGrow = parseFloat(settings.activeItem.activeWidth) || 1; | |
| 58 | + let inactiveGrow = 1; | |
| 59 | + | |
| 60 | + $accordionItems.each(function () { | |
| 61 | + if (!$(this).hasClass("king-addons-image-accordion-item-grow")) { | |
| 62 | + inactiveGrow = parseFloat(window.getComputedStyle(this).flexGrow) || 1; | |
| 63 | + return false; | |
| 64 | + } | |
| 65 | + }); | |
| 66 | + | |
| 67 | + const sum = activeGrow + inactiveGrow * Math.max(count - 1, 0); | |
| 68 | + if (total <= 0 || sum <= 0) { | |
| 69 | + return; | |
| 70 | + } | |
| 71 | + | |
| 72 | + $wrap[0].style.setProperty( | |
| 73 | + "--king-acc-open-size", | |
| 74 | + Math.round((total * activeGrow) / sum) + "px" | |
| 75 | + ); | |
| 76 | + }; | |
| 77 | + | |
| 78 | + setOpenContentSize(); | |
| 79 | + const resizeEvent = "resize.kingAddonsImageAccordion" + ($elem.data("id") || "item"); | |
| 80 | + $(window).off(resizeEvent).on(resizeEvent, setOpenContentSize); | |
| 81 | + this.onDestroy = () => { | |
| 82 | + $(window).off(resizeEvent); | |
| 83 | + }; | |
| 84 | + | |
| 23 | 85 | // Adjust row/column layout if necessary |
| 24 | 86 | if ($wrapContainer.hasClass("king-addons-acc-no-column")) { |
| 25 | 87 | if (!$elem.hasClass("king-addons-image-accordion-row")) { |
| 26 | 88 | $elem |
| @@ -132,16 +194,20 @@ | ||
| 132 | 194 | ".king-addons-img-accordion-item-lightbox" |
| 133 | 195 | ).length |
| 134 | 196 | ) { |
| 135 | 197 | const itemUrl = thisSettings.activeItem.overlayLink; |
| 136 | - if (itemUrl) { | |
| 198 | + const safeUrl = normalizeSafeUrl(itemUrl); | |
| 199 | + if (safeUrl) { | |
| 137 | 200 | if ( |
| 138 | 201 | thisSettings.activeItem.overlayLinkTarget === |
| 139 | 202 | "_blank" |
| 140 | 203 | ) { |
| 141 | - window.open(itemUrl, "_blank").focus(); | |
| 204 | + const w = window.open(safeUrl, "_blank"); | |
| 205 | + if (w && typeof w.focus === "function") { | |
| 206 | + w.focus(); | |
| 207 | + } | |
| 142 | 208 | } else { |
| 143 | - window.location.href = itemUrl; | |
| 209 | + window.location.href = safeUrl; | |
| 144 | 210 | } |
| 145 | 211 | } |
| 146 | 212 | } |
| 147 | 213 | }); |
| @@ -151,28 +217,32 @@ | ||
| 151 | 217 | // Interaction type: "hover" |
| 152 | 218 | if (settings.activeItem.interaction === "hover") { |
| 153 | 219 | mediaHoverLink(); |
| 154 | 220 | |
| 155 | - $accordionItems | |
| 156 | - .on("mouseenter", function () { | |
| 157 | - $accordionItems.removeClass( | |
| 158 | - "king-addons-image-accordion-item-grow" | |
| 159 | - ); | |
| 160 | - $accordionItems | |
| 161 | - .find(".king-addons-animation-wrap") | |
| 162 | - .removeClass("king-addons-animation-wrap-active"); | |
| 221 | + $accordionItems.on("mouseenter", function () { | |
| 222 | + if ($(this).hasClass("king-addons-image-accordion-item-grow")) { | |
| 223 | + return; | |
| 224 | + } | |
| 225 | + $accordionItems.removeClass( | |
| 226 | + "king-addons-image-accordion-item-grow" | |
| 227 | + ); | |
| 228 | + $accordionItems | |
| 229 | + .find(".king-addons-animation-wrap") | |
| 230 | + .removeClass("king-addons-animation-wrap-active"); | |
| 163 | 231 | |
| 164 | - $(this).addClass("king-addons-image-accordion-item-grow"); | |
| 165 | - $(this) | |
| 166 | - .find(".king-addons-animation-wrap") | |
| 167 | - .addClass("king-addons-animation-wrap-active"); | |
| 168 | - }) | |
| 169 | - .on("mouseleave", function () { | |
| 170 | - $(this).removeClass("king-addons-image-accordion-item-grow"); | |
| 171 | - $(this) | |
| 172 | - .find(".king-addons-animation-wrap") | |
| 173 | - .removeClass("king-addons-animation-wrap-active"); | |
| 174 | - }); | |
| 232 | + $(this).addClass("king-addons-image-accordion-item-grow"); | |
| 233 | + $(this) | |
| 234 | + .find(".king-addons-animation-wrap") | |
| 235 | + .addClass("king-addons-animation-wrap-active"); | |
| 236 | + }); | |
| 237 | + $wrap.on("mouseleave", function () { | |
| 238 | + $accordionItems.removeClass( | |
| 239 | + "king-addons-image-accordion-item-grow" | |
| 240 | + ); | |
| 241 | + $accordionItems | |
| 242 | + .find(".king-addons-animation-wrap") | |
| 243 | + .removeClass("king-addons-animation-wrap-active"); | |
| 244 | + }); | |
| 175 | 245 | |
| 176 | 246 | // Interaction type: "click" |
| 177 | 247 | } else if (settings.activeItem.interaction === "click") { |
| 178 | 248 | $elem |
| @@ -215,16 +285,20 @@ | ||
| 215 | 285 | ".king-addons-img-accordion-item-lightbox" |
| 216 | 286 | ).length |
| 217 | 287 | ) { |
| 218 | 288 | const itemUrl = thisSettings.activeItem.overlayLink; |
| 219 | - if (itemUrl) { | |
| 289 | + const safeUrl = normalizeSafeUrl(itemUrl); | |
| 290 | + if (safeUrl) { | |
| 220 | 291 | if ( |
| 221 | 292 | thisSettings.activeItem.overlayLinkTarget === |
| 222 | 293 | "_blank" |
| 223 | 294 | ) { |
| 224 | - window.open(itemUrl, "_blank").focus(); | |
| 295 | + const w = window.open(safeUrl, "_blank"); | |
| 296 | + if (w && typeof w.focus === "function") { | |
| 297 | + w.focus(); | |
| 298 | + } | |
| 225 | 299 | } else { |
| 226 | - window.location.href = itemUrl; | |
| 300 | + window.location.href = safeUrl; | |
| 227 | 301 | } |
| 228 | 302 | } |
| 229 | 303 | } |
| 230 | 304 | } else { |