PageSourceSearch

https://www.furnituredealer.net/components/roomplanner/v2/init.js?…JnoqYX5TCcacykK-Yv__Aw8kotKqi7fOrCjAgMsETZQ

js furnituredealer.net collected 2026-09-24 18:28:27 UTC 14,623 bytes, 406 lines download raw bytes

1$(function () {
2    var roomPlannerConfig = window.__FDN_ROOM_PLANNER__ || {};
3    var V3_ENABLED = roomPlannerConfig.v3Enabled !== false;
4    var TRY_V3_BANNER_ENABLED = V3_ENABLED && !!roomPlannerConfig.tryV3BannerEnabled;
5
6    var REOPEN_V3_KEY = "fdn-room-planner-reopen-v3";
7    // In-memory only so dismiss sticks for this page load (timers won't re-show),
8    // but the prompt returns on the next navigation.
9    var tryV3BannerDismissed = false;
10    var tryV3BannerTimer = null;
11
12    function dismissTryV3Banner(banner) {
13        tryV3BannerDismissed = true;
14        if (tryV3BannerTimer) {
15            clearTimeout(tryV3BannerTimer);
16            tryV3BannerTimer = null;
17        }
18        if (banner && banner.parentNode) {
19            banner.remove();
20        }
21    }
22
23    // V3 is the default engine. A user who picks "Use classic planner" gets V2
24    // until they switch back, and `rpEngine` in the URL overrides both.
25    function resolveEngine() {
26        if (!V3_ENABLED) {
27            return "v2";
28        }
29
30        var requested = (getQueryParamIgnoreCase("rpEngine") || "").toLowerCase();
31
32        if (requested === "v2" || requested === "v3") {
33            return requested;
34        }
35
36        var preference = window.FdnRoomPlannerEngine && FdnRoomPlannerEngine.get();
37
38        return preference === "v2" ? "v2" : "v3";
39    }
40
41    function setEnginePreference(engine) {
42        if (window.FdnRoomPlannerEngine && typeof FdnRoomPlannerEngine.set === "function") {
43            FdnRoomPlannerEngine.set(engine);
44        }
45    }
46
47    function getQueryParamIgnoreCase(name) {
48        var target = String(name).toLowerCase();
49        var params = new URLSearchParams(window.location.search);
50        var matched = null;
51        params.forEach(function (value, key) {
52            if (matched === null && String(key).toLowerCase() === target) {
53                matched = value;
54            }
55        });
56        return matched;
57    }
58
59    function hasQueryParamIgnoreCase(name) {
60        return getQueryParamIgnoreCase(name) !== null;
61    }
62
63    function consumeReopenV3AfterLogin() {
64        try {
65            if (sessionStorage.getItem(REOPEN_V3_KEY) === "1") {
66                sessionStorage.removeItem(REOPEN_V3_KEY);
67                return true;
68            }
69        } catch (e) {
70            // ignore
71        }
72        return false;
73    }
74
75    function shouldOpenV3OnAutoOpen() {
76        if (!V3_ENABLED) {
77            consumeReopenV3AfterLogin();
78            return false;
79        }
80
81        var engine = (getQueryParamIgnoreCase("rpEngine") || "").toLowerCase();
82        if (engine === "v2") {
83            consumeReopenV3AfterLogin();
84            return false;
85        }
86        if (engine === "v3") {
87            return true;
88        }
89        if (consumeReopenV3AfterLogin()) {
90            return true;
91        }
92
93        return resolveEngine() === "v3";
94    }
95
96    function closeV2Planner() {
97        var $close = $(".room-planner-close--trigger");
98        if ($close.length) {
99            $close.first().trigger("click");
100            return;
101        }
102        $(".room-planner-v2").removeClass("__open");
103        $("body").css("overflow", "");
104    }
105
106    function openV3(data) {
107        if (!V3_ENABLED) {
108            openV2(data);
109            return;
110        }
111
112        if (window.RoomPlannerV3Launcher && typeof RoomPlannerV3Launcher.open === "function") {
113            RoomPlannerV3Launcher.open(data);
114        } else {
115            console.error("RoomPlannerV3Launcher is not available.");
116        }
117    }
118
119    function switchToV3(data) {
120        if (!V3_ENABLED) {
121            return;
122        }
123
124        setEnginePreference("v3");
125        closeV2Planner();
126        openV3(data);
127    }
128
129    function ensureTryV3BannerStyles() {
130        if (document.getElementById("fdn-try-v3-banner-styles")) {
131            return;
132        }
133        var style = document.createElement("style");
134        style.id = "fdn-try-v3-banner-styles";
135        style.textContent =
136            ".fdn-try-v3-banner{position:absolute;
136left:50%;bottom:12px;transform:translateX(-50%);z-index:200;" +
137            "display:flex;align-items:center;gap:.75rem;flex-wrap:wrap;max-width:min(560px,calc(100% - 24px));" +
138            "padding:.65rem .9rem;background:#1f2937;color:#f8fafc;border-radius:8px;box-shadow:0 8px 24px rgba(0,0,0,.28);" +
139            "font:500 13px/1.35 system-ui,-apple-system,Segoe UI,sans-serif}" +
140            ".fdn-try-v3-banner__text{flex:1 1 180px}" +
141            ".fdn-try-v3-banner__actions{display:flex;gap:.4rem;align-items:center}" +
142            ".fdn-try-v3-banner button{border:0;border-radius:6px;cursor:pointer;padding:.35rem .7rem;font:600 12px/1.2 inherit}" +
143            ".fdn-try-v3-banner__try{background:#4b89dc;color:#fff}" +
144            ".fdn-try-v3-banner__dismiss{background:transparent;color:#cbd5e1}";
145        document.head.appendChild(style);
146    }
147
148    function ensureTryV3Banner() {
149        if (!TRY_V3_BANNER_ENABLED || tryV3BannerDismissed) {
150            return;
151        }
152
153        var root = document.querySelector(".room-planner-v2");
154        if (!root || !root.classList.contains("__open")) {
155            return;
156        }
157
158        if (root.querySelector(".fdn-try-v3-banner")) {
159            return;
160        }
161
162        ensureTryV3BannerStyles();
163
164        var banner = document.createElement("div");
165        banner.className = "fdn-try-v3-banner";
166        banner.setAttribute("role", "status");
167        banner.innerHTML =
168            '<div class="fdn-try-v3-banner__text">You\u2019re using the classic room planner. The new one has faster editing and clearer measurements.</div>' +
169            '<div class="fdn-try-v3-banner__actions">' +
170            '<button type="button" class="fdn-try-v3-banner__try">Use new planner</button>' +
171            '<button type="button" class="fdn-try-v3-banner__dismiss">Not now</button>' +
172            "</div>";
173
174        banner.querySelector(".fdn-try-v3-banner__try").addEventListener("click", function () {
175            switchToV3(null);
176        });
177        banner.querySelector(".fdn-try-v3-banner__dismiss").addEventListener("click", function () {
178            dismissTryV3Banner(banner);
179        });
180
181        root.appendChild(banner);
182    }
183
184    function scheduleTryV3Banner(delayMs) {
185        if (tryV3BannerDismissed) {
186            return;
187        }
188
189        if (tryV3BannerTimer) {
190            clearTimeout(tryV3BannerTimer);
191        }
192
193        tryV3BannerTimer = setTimeout(function () {
194            tryV3BannerTimer = null;
195            ensureTryV3Banner();
196        }, typeof delayMs === "number" ? delayMs : 100);
197    }
198
199    // Site triggers route to the preferred engine (V3 unless the user opted into classic V2).
200    function openPlanner(data) {
201        var canUseV3 = V3_ENABLED
202            && !!(window.RoomPlannerV3Launcher && typeof RoomPlannerV3Launcher.open === "function");
203
204        if (resolveEngine() === "v2" || !canUseV3) {
205            openV2(data);
206            return;
207        }
208
209        // Snippets may have already kicked off classic open — close it before V3.
210        closeV2Planner();
211        openV3(data);
212    }
213
214    $(".room-planner--trigger").on("click", function () {
215        openPlanner();
216    });
217
218    $("body").on("click", ".js-add-to-room-plan", function () {
219        var data = $(this).data("room-plan-item");
220        openPlanner(data);
221        if (typeof laurentideTracker !== "undefined") {
222            Laurentide_AddToRoomplan(data);
223        }
224    });
225
226    var scriptsLoaded = false;
227    var isCurrentlyLoading = false;
228    var pendingInitData = null;
229    var roomPlannerInitCalled = false;
230
231    function openV2(data) {
232        init(data);
233    }
234
235    var init = function (data) {
236        pendingInitData = data;
237
238        if (!scriptsLoaded && !isCurrentlyLoading) {
239            console.log("init (load)");
240            loadRoomPlanner();
241        } else if (!isCurrentlyLoading && window.RoomPlannerV2) {
242            // Auto-open snippets poll open() every 150ms. After scripts load, the first
243            // init is async (templates + delayed loadCanvas). A second init before
244            // resizeCanvas flips false double-bootstraps the canvas.
245            if (roomPlannerInitCalled && !$(".room-planner-main .canvas-container").length) {
246                console.log("init (ignored — bootstrap in progress)");
247                return;
248            }
249            if ($(".room-planner-main .canvas-container").length && window.RoomPlannerV2.open) {
250                console.log("init (re-open existing)");
251                RoomPlannerV2.open(pendingInitData);
252                pendingInitData = null;
253                scheduleTryV3Banner(100);
254                return;
255            }
256            console.log("init (open)");
257            roomPlannerInitCalled = true;
258            RoomPlannerV2.init(pendingInitData);
259            pendingInitData = null;
260            scheduleTryV3Banner(250);
261        }
262    };
263
264    // Legacy name still used by dealer CodeSnippets / auto-open polls. Route through the
265    // engine preference so those callers respect V3 default / classic opt-in.
266    window.RoomPlannerV2Launcher = window.RoomPlannerV2Launcher || {};
267    window.RoomPlannerV2Launcher.open = function (data) {
268        openPlanner(data);
269    };
270    window.RoomPlannerV2Launcher.openClassic = function (data) {
271        openV2(data);
272    };
273
274    // Engine-aware entry point for host pages / legacy globals.
275    window.RoomPlannerLauncher = window.RoomPlannerLauncher || {};
276    window.RoomPlannerLauncher.open = function (data) {
277        openPlanner(data);
278    };
279    window.RoomPlannerLauncher.resolveEngine = resolveEngine;
280
281    function shouldAutoOpenRoomPlanner() {
282        if (!hasQueryParamIgnoreCase("autoOpenRoomPlanner")) {
283            return false;
284        }
285        var value = (getQueryParamIgnoreCase("autoOpenRoomPlanner") || "true").toLowerCase();
286        return value !== "false" && value !== "0";
287    }
288
289    if (shouldAutoOpenRoomPlanner()) {
290        window.setTimeout(function () {
291            if (shouldOpenV3OnAutoOpen() && window.RoomPlannerV3Launcher) {
292                openV3();
293                return;
294            }
295            openPlanner();
296        }, 0);
297    }
298
299    function loadScript(src) {
300        return new Promise((resolve, reject) => {
301            const script = document.createElement("script");
302            script.src = src;
303            script.async = false; // preserve execution order
304            script.onload = () => resolve(src);
305            script.onerror = () => reject(new Error(`Failed to load ${src}`));
306            document.head.appendChild(script);
307        });
308    }
309
310    function loadCss(href) {
311        return new Promise((resolve, reject) => {
312            // Prevent duplicate loads
313            if (document.querySelector(`link[href="${href}"]`)) {
314                resolve(href);
315                return;
316            }
317
318            const link = document.createElement("link");
319            link.rel = "stylesheet";
320            link.href = href;
321
322            link.onload = () => resolve(href);
323            link.onerror = () => reject(new Error(`Failed to load CSS ${href}`));
324
325            document.head.appendChild(link);
326        });
327    }
328
329    async function loadRoomPlanner() {
330        const startTime = performance.now();
331        isCurrentlyLoading = true;
332
333        try {
334            ensureRoomPlannerTemplatesContainer();
335
336            var baseUrl = (window.Utils && (Utils.baseUrl || Utils.BaseURL)) || "/";
337            // Bump when shipping v2 host fixes so browsers don't keep a stale Open-modal script.
338            var assetVersion = "20260917-v3-default";
339
340            // Load CSS first
341            await loadCss(baseUrl + "components/roomplanner/v2/room-planner-v2.css?v=" + assetVersion);
342
343            await loadScript(baseUrl + "components/roomplanner/v2/jquery-ui-1.11.1.js");
344            await loadScript(baseUrl + "components/roomplanner/v2/jquery-ui-touch-punch-0.2.2.js");
345            await loadScript(baseUrl + "components/roomplanner/v2/RoomPlannerFabric.js");
346            await loadScript(baseUrl + "components/roomplanner/v2/room-planner-v2.js?v=" + assetVersion);
347
348            const endTime = performance.now();
349            console.log(`loaded rp scripts in ${endTime - startTime}ms`);
350
351            if (!window.RoomPlannerV2 || typeof window.RoomPlannerV2.init !== "function") {
352                throw new Error("RoomPlannerV2 did not initialize after loading room-planner-v2.js");
353            }
354
355            scriptsLoaded = true;
356            roomPlannerInitCalled = true;
357            window.RoomPlannerV2.init(pendingInitData);
358            pendingInitData = null;
359            scheduleTryV3Banner(250);
360        } catch (err) {
361            console.error(err);
362        } finally {
363            isCurrentlyLoading = false;
364        }
365    }
366    function ensureRoomPlannerTemplatesContainer() {
367        if (!document.getElementById("room-planner-templates")) {
368            const div = document.createElement("div");
369            div.id = "room-planner-templates";
370            document.body.appendChild(div);
371        }
372        // Ensure main room planner container exists
373        if (!document.querySelector(".room-planner-v2")) {
374            const plannerDiv = document.createElement("div");
375            plannerDiv.className = "room-planner-v2";
376            document.body.appendChild(plannerDiv);
377        }
378    }
379
380    // Watch for V2 open state so banner can appear after delayed open().
381    if (TRY_V3_BANNER_ENABLED) {
382        var openObserver = new MutationObserver(function () {
383            ensureTryV3Banner();
384        });
385        openObserver.observe(document.body, {
386            subtree: true,
387            attributes: true,
388            attributeFilter: ["class"]
389        });
390    }
391});
392
393// for backward compatibility
394function AddToRoomPlan(data) {
395    if (window.RoomPlannerLauncher && typeof RoomPlannerLauncher.open === "function") {
396        RoomPlannerLauncher.open(data);
397        return;
398    }
399    if (window.RoomPlannerV2Launcher && typeof RoomPlannerV2Launcher.open === "function") {
400        RoomPlannerV2Launcher.open(data);
401        return;
402    }
403    if (window.RoomPlannerV2) {
404        RoomPlannerV2.AddToRoomPlan(data);
405    }
406}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.