PageSourceSearch

https://wizfort.com/assets/cslshoes_erp/js/sso_login_order.js?v=20260531b

js wizfort.com collected 2026-09-27 13:40:33 UTC 6,227 bytes, 155 lines download raw bytes

1/**
2 * sso_login_order.js — DOM-reorder social login buttons on /login (Google first).
3 *
4 * Fetches per-provider Display Order integers from the DB (via a guest endpoint)
5 * and PHYSICALLY RE-ORDERS the provider button wrappers in the DOM so the
6 * lower-order provider (Google=10) precedes the higher (Facebook=20).
7 *
8 * NATIVE LAYOUT PRESERVED: this script writes NO layout CSS. It does not set
9 * display:flex, flex-direction, or style.order on the container or any wrapper.
10 * Reordering is done by swapping the wrappers into their existing sibling slots
11 * (via comment-node placeholders), so Frappe's native spacing — including
12 * first-child / last-child / adjacent-sibling (.login-button-wrapper + ...)
13 * margin rules — is untouched. (Earlier revision forced flex `order`, which
14 * collapsed that native inter-button spacing.)
15 *
16 * Frappe serves /login server-rendered (no_cache=1 for ERP-097 safety).
17 * Buttons are present at DOMContentLoaded — no waiting for dynamic injection.
18 *
19 * ERP-090 lesson: no jQuery. Pure vanilla JS only.
20 */
21(function () {
22    "use strict";
23
24    // Only run on /login
25    if (window.location.pathname.replace(/\/+$/, "") !== "/login") {
26        return;
27    }
28
29    /** Fallback order constants if the DB fetch fails or a provider has no value. */
30    var FALLBACK = { google: 10, facebook: 20 };
31    var DEFAULT_ORDER = 100;
32
33    /**
34     * Determine the effective order for a provider.
35     * DB value wins if present and positive; then FALLBACK; then DEFAULT_ORDER.
36     */
37    function effectiveOrder(providerName, dbVal) {
38        if (dbVal && dbVal > 0) {
39            return dbVal;
40        }
41        if (FALLBACK[providerName] != null) {
42            return FALLBACK[providerName];
43        }
44        return DEFAULT_ORDER;
45    }
46
47    /**
48     * Given the anchor element's classList and the order map keys, return the
49     * matching provider key (e.g. "google", "facebook"), or null.
50     * Matches btn-<key> ONLY for keys present in orderMap to avoid false matches
51     * against btn-block, btn-sm, btn-default, btn-login-option, etc.
52     */
53    function detectProviderKey(anchor, mapKeys) {
54        var classes = Array.prototype.slice.call(anchor.classList);
55        for (var i = 0; i < mapKeys.length; i++) {
56            var key = mapKeys[i];
57            if (classes.indexOf("btn-" + key) !== -1) {
58                return key;
59            }
60        }
61        return null;
62    }
63
64    function applyOrder(orderMap) {
65        var mapKeys = Object.keys(orderMap);
66        if (!mapKeys.length) return;
67
68        // All .social-login-buttons containers (there may be multiple: OAuth + email-link)
69        var containers = document.querySelectorAll(".social-login-buttons");
70        for (var ci = 0; ci < containers.length; ci++) {
71            var container = containers[ci];
72            var children = Array.prototype.slice.call(container.children);
73
74            // Collect provider wrappers in current DOM order, with their order value.
75            var providers = [];
76            for (var i = 0; i < children.length; i++) {
77                var child = children[i];
78                if (!child.classList || !child.classList.contains("login-button-wrapper")) {
79                    continue;
80                }
81                var anchor = child.querySelector("a");
82                if (!anchor) continue;
83                var key = detectProviderKey(anchor, mapKeys);
84                if (key === null) {
85                    // Non-provider wrapper (e.g. email-link button) — leave untouched.
86                    continue;
87                }
88                providers.push({ el: child, order: effectiveOrder(key, orderMap[key]) });
89            }
90
91            // Single provider (or none) → nothing to reorder; native layout untouched.
92            if (providers.length < 2) continue;
93
94            // Desired order: ascending by order value (stable for equal values).
95            var sorted = providers.slice().sort(function (a, b) {
96                return a.order - b.order;
97            });
98
99            // Already in desired order → skip to avoid needless DOM churn.
100            var alreadySorted = true;
101            for (var s = 0; s < providers.length; s++) {
102                if (providers[s].el !== sorted[s].el) {
103                    alreadySorted = false;
104                    break;
105                }
106            }
107            if (alreadySorted) continue;
108
109            // Reorder by swapping providers into their OWN existing sibling slots,
110            // preserving the positions of any non-provider siblings. Place a comment
111            // placeholder at each provider slot, then drop the sorted providers in.
112            var placeholders = [];
113            for (var p = 0; p < providers.length; p++) {
114                var ph = document.createComment("sso-slot");
115                container.replaceChild(ph, providers[p].el); // detaches providers[p].el
116                placeholders.push(ph);
117            }
118            for (var k = 0; k < placeholders.length; k++) {
119                container.replaceChild(sorted[k].el, placeholders[k]);
120            }
121        }
122    }
123
124    function run() {
125        try {
126            fetch(
127                "/api/method/cslshoes_erp.api.social_login_order.get_display_order",
128                { headers: { Accept: "application/json" } }
129            )
130                .then(function (resp) {
131                    if (!resp.ok) return null;
132                    return resp.json();
133                })
134                .then(function (data) {
135                    if (data && data.message) {
136                        applyOrder(data.message);
137                    } else {
138                        // Fetch succeeded but no map — apply FALLBACK for known providers
139                        applyOrder(FALLBACK);
140                    }
141                })
142                .catch(function () {
143                    // Network error — fail silent, leave buttons in stock order
144                });
145        } catch (e) {
146            // Defensive top-level catch — never break the login page
147        }
148    }
149
150    if (document.readyState === "loading") {
151        document.addEventListener("DOMContentLoaded", run);
152    } else {
153        run();
154    }
155})();

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.