PageSourceSearch

https://localbitcoins.com/cached-static/main.3b9b9c0dbbee.js

js localbitcoins.com collected 2026-10-01 10:26:52 UTC 17,334 bytes, 508 lines download raw bytes

1/**
2 * LocalBitcoins.com site-wide Javascript
3 */
4
5// public utility functions
6localBitcoins = {};
7
8(function($) {
9
10    "use strict";
11
12    if (typeof(window.console) === 'undefined') {
13        console = {};
14        console.log = console.warn = console.error = function(a) {};
15    }
16
17    // http://stackoverflow.com/questions/3974827/detecting-touch-screen-devices-with-javascript
18    var hasTouch = 'ontouchstart' in document.documentElement;
19    window.hasTouch = hasTouch;
20
21    var isNarrow = window.screen.availWidth < 800;
22
23    // Export currently active language
24    var lang = $("html").attr("lang");
25    window.lang = lang;
26
27    // Helper for determining max element height of selected elements
28    $.fn.max = function(selector) {
29        return Math.max.apply(null, this.map(function(index, el) {
30            return selector.apply(el);
31        }).get());
32    };
33
34    function deleteCookie(name) {
35        document.cookie = name + '=;expires=Thu, 01 Jan 1970 00:00:01 GMT;';
36    };
37
38    // http://stackoverflow.com/questions/10730362/javascript-get-cookie-by-name
39    function readCookie(name) {
40        var nameEQ = name + "=";
41        var ca = document.cookie.split(';');
42        for (var i = 0; i < ca.length; i++) {
43            var c = ca[i];
44            while (c.charAt(0) === ' ') {
45                c = c.substring(1, c.length);
46            }
47            if (c.indexOf(nameEQ) === 0) {
48                return c.substring(nameEQ.length, c.length);
49            }
50        }
51        return null;
52    }
53
54    // Make sure cookie value is uri
55    function decodeCookieValue(val) {
56        if(val.substring("%") >= 0) {
57            return window.decodeURIComponent(val);
58        }
59    }
60    window.decodeCookieValue = decodeCookieValue;
61
62    // http://stackoverflow.com/a/13164238/315168
63    function writeCookie(key, value, days) {
64        var date = new Date();
65        // Get unix milliseconds at current time plus number of days
66        date.setTime(+date + (days * 86400000)); //24 * 60 * 60 * 1000
67        window.document.cookie = key + "=" + '"' + value + '"' + "; expires=" + date.toGMTString() + "; path=/";
68        return value;
69    }
70    window.writeCookie = writeCookie;
71
72    /** Pop-upify some links */
73    function popuify() {
74
75        //var psinet = getPSINETScore();
76        //console.log("Psinet:" + psinet);
77
78
79        var passwordIndicatorInitialized = false;
80
81        if (!$.support.cors) {
82            // IE9 and below
83            // CDN'd Bootstrap does not allow AJAX loading without setting CORS headers
84            // http://stackoverflow.com/questions/10232017/ie9-jquery-ajax-with-cors-returns-access-is-denied
85            return;
86        }
87
88        // Don't do modals on mobile
89        if (hasTouch && isNarrow) {
90            return;
91        }
92    }
93
94    // attach a very simple password strength checker to $selector
95    function passwordStrength($selector) {
96        var $response = $("<span></span>");
97        $response.addClass("pwstr");
98        $selector.after($response);
99        $selector.keyup(function() {
100            var $el = $(this);
101            var len = $el.val().length;
102            if (len < 8) {
103                $response.removeClass("pwstr-medium pwstr-good pwstr-better");
104                $response.addClass("pwstr-weak");
105                $response.html("Very weak");
106            } else if (len < 16) {
107                $response.removeClass("pwstr-weak pwstr-good pwstr-better");
108                $response.addClass("pwstr-medium");
109                $response.html("Medium");
110            } else if (len < 25) {
111                $response.removeClass("pwstr-medium pwstr-weak pwstr-better");
112                $response.addClass("pwstr-good");
113                $response.html("Good");
114            } else {
115                $response.removeClass("pwstr-medium pwstr-good pwstr-weak");
116                $response.addClass("pwstr-better");
117                $response.html("Better");
118            }
119        });
120    }
121    window.passwordStrength = passwordStrength;
122
123    // Equalize the height of front page boxes
124    function tuneAdPage() {
125
126        // http://stackoverflow.com/a/9189443/315168
127        $('#ad-tips .well').height(function() {
128            var maxHeight = $('#ad-tips .well').max(function() {
129                return $(this).height();
130            });
131            return maxHeight;
132        });
133    }
134
135    function tuneSplash() {
136
137        function fixSplashScreenHeight() {
138            $('.super-front-page .super-front-page-column').height(function() {
139                var maxHeight = $('.super-front-page .super-front-page-column').max(function() {
140                    return $(this).height();
141                });
142                return maxHeight;
143            });
144        }
145
146        fixSplashScreenHeight();
147    }
148
149    /** Migrated body <script> */
150    function oldFixes() {
151
152        if (!window.exchange) {
153            throw new Error("JavaScript dynamic options not correctly passed by HTML");
154        }
155
156        $('ul.nav > li > a[href="' + window.exchange.fullPath.split('?')[0] + '"]').parent().addClass("active");
157        $(".noscripthidden").show();
158        $(".noscriptvisible").hide();
159    }
160
161    /**
162     * In listing rows, make <tr>s clickable
163     *
164     */
165    function makeListingRowsClickable() {
166        $("tr.clickable").click(function(e) {
167
168            // Only do the magic on left mouse button
169            if (e.which !== 1) {
170                return;
171            }
172
173            var href = $(this).find('.megabutton, .primary-link').attr("href");
174
175            if (href) {
176                window.location = href;
177            }
178        });
179    }
180
181    /**
182     * IE8 and FF3.6 do not correctly handle navbar positioning.
183     *
184     * Fixed navbar overlays logged in nav bar because of CSS bugs.
185     *
186     */
187    function fixIE8NavBar() {
188
189        function checkNavBarHidden() {
190            var bottom = $(".navbar-fixed-top").offset().top + $(".navbar-fixed-top").height();
191
192            var top = $("#logged_in_bar").offset();
193
194            if (top) {
195                // No idea why happens.
196                top = top.top;
197            } else {
198                top = 999;
199            }
200
201            if (top < bottom) {
202                $("#logged_in_bar").css("padding-top", "50px");
203            }
204        }
205
206        if (window.OLD_IE) {
207            $(window).resize(function() {
208                checkNavBarHidden();
209            });
210
211            // Fix on load
212            checkNavBarHidden();
213        }
214
215    }
216
217    /**
218     * Toggle on/off arrow for Twitter Bootstrap collapsibles.
219     *
220     * Multi-collapsible-friendly; supports several collapsibles in the same group, on the same page.
221     */
222    function animateCollapsibles() {
223
224        $('.collapse').on('show', function() {
225            var $t = $(this);
226            var header = $("a[href='#" + $t.attr("id") + "']");
227            header.find(".fa-chevron-right").removeClass("fa-chevron-right").addClass("fa-chevron-down");
228        }).on('hide', function() {
229            var $t = $(this);
230            var header = $("a[href='#" + $t.attr("id") + "']");
231            header.find(".fa-chevron-down").removeClass("fa-chevron-down").addClass("fa-chevron-right");
232        });
233    }
234
235    function handleOnlineStatus() {
236        $(".online-status").tooltip();
237    }
238
239    // Set special marker class to hide the front page form on IE8
240    function handleFrontPageFormIE8() {
241        if (window.OLD_IE) {
242            $("body").addClass("msie8");
243        }
244    }
245
246    /**
247     * Handle notifications cross-tab friendly manner with visual and audiable feedback.
248     */
249    function handleNotifications() {
250
251        if (!$(".notifications_dropdown0").size()) {
252            // Not logged in
253            return;
254        }
255
256        // Set menu initial mute state
257        handleMuteState();
258
259        // For anonymous users and users without opened trades do not do notifications
260        // Also notifications may be disabled in the site settings.
261        var needNotifications = $(".notification-timestamp").attr("data-need-notifications") == "true";
262        if(!needNotifications) {
263            return false;
264        }
265
266        // In notifications menu, show mute or unmute
267        function handleMuteState() {
268            if (notifications.getAudible()) {
269                $(".mute-notifications").show();
270                $(".unmute-notifications").hide();
271            } else {
272                $(".mute-notifications").hide();
273                $(".unmute-notifications").show();
274            }
275
276            $(".mute-notifications").click(function() {
277                notifications.setAudible(false);
278                handleMuteState();
279            });
280            $(".unmute-notifications").click(function() {
281                notifications.setAudible(true);
282                handleMuteState();
283            });
284        }
285
286        // Simulate that we get changes from the server on 1/3 of polls
287        function fetchData(callback) {
288
289            var url = $(".notifications_dropdown0").attr("data-notification-url");
290            var timestamp = $(".notification-timestamp").attr("data-timestamp");
291
292            var buster = Math.random() * 10000;
293            $.ajax({
294                url: url,
295                data: {"last-timestamp": timestamp, "cache-buster": buster},
296                success: function(data) {
297
298                    // 304 not modified
299                    if(!data) {
300                        return;
301                    }
302
303                    data = data.trim();
304
305                    // A valid response, but does not actually
306                    // contain any notifications
307                    if(!data) {
308                        return;
309                    }
310
311                    var $data = $(data);
312                    var silent;
313
314                    if($data.find(".notification-menu-entry").length === 0) {
315                        // We got response all notifications seen
316                        silent = true;
317                    } else {
318                        // New notifications
319                        silent = false;
320                    }
321
322                    callback(data, silent, false);
323                },
324                error: function(xhr) {
325                    // Got error (usually 403 logged out) from the server,
326                    // time to stop polling
327                    callback("", false, true);
328                }
329            });
330        }
331
332        // Simulate changes HTML on the page with new notification list
333        function updatePage(data, source, action) {
334            $(".notifications_dropdown0").html(data);
335            handleMuteState();
336        }
337
338        function parseAndShowNotifications(oldValue, newValue) {
339            if (!window.exchange.webNotificationsEnabled) {
340                return;
341            }
342
343            // Use difference between old and new values to check what messages are new ones
344            var oldMessages = $(oldValue).find(".unread-msg");
345            var newMessages = $(newValue).find(".unread-msg");
346            var notifs = [];
347            for (var i = 0; i < newMessages.length; ++ i) {
348                var found = false;
349                var newMessage = newMessages[i];
350                for (var j = 0; j < oldMessages.length; ++ j) {
351                    var oldMessage = oldMessages[j];
352                    if (oldMessage.firstChild.href == newMessage.firstChild.href && oldMessage.firstChild.innerHTML == newMessage.firstChild.innerHTML) {
353                        found = true;
354                        break;
355                    }
356                }
357                if (!found) {
358                    var url = newMessage.firstChild.href;
359                    var msg = newMessage.firstChild.innerHTML;
360
361                    var notification = new Notification("LocalBitcoins.com", {
362                        icon: "/static/img/touch-icon-144.png",
363                        tag: url + msg,
364                        body: msg
365                    });
366                }
367            }
368        }
369
370        function showExternalNotifications(oldValue, newValue) {
371            if (Notification && Notification.permission === "granted") {
372                parseAndShowNotifications(oldValue, newValue);
373            } else if (Notification && Notification.permission !== "denied") {
374                Notification.requestPermission(function (status) {
375                    if (Notification.permission !== status) {
376                        Notification.permission = status;
377                    }
378                    if (status === "granted") {
379                        parseAndShowNotifications(oldValue, newValue);
380                    }
381                });
382            }
383        }
384
385        function detectWebNotificationEnabling() {
386            var checkbox = $('#id_enable_web_notifications');
387            checkbox.on("click", function() {
388                if($(this).is(":checked")) {
389                    if (Notification && Notification.permission !== "denied") {
390                        Notification.requestPermission(function (status) {
391                            if (Notification.permission !== status) {
392                                Notification.permission = status;
393                            }
394                        });
395                    }
396                }
397            });
398        }
399
400        // Start notification poller
401        notifications.init({
402            interval: 45 * 1000,
403            fetcher: fetchData,
404            updater: updatePage,
405            showExternalNotifications: showExternalNotifications,
406            debug: false,
407            hasChanged: function(oldValue, newValue) {
408
409                // Assume store notification value is the DOM string dump of the notification HTML
410
411                if (!oldValue) {
412                    return false;
413                }
414                // reparse DOM to get rid of all extra,
415                // so that we don't get alarms when the notification menu is source code changes,
416                // but actual DOM matches (whitespaces, settings style attribute, etc.)
417                oldValue = $(oldValue.trim()).find(".notification-menu-entry");
418                newValue = $(newValue.trim()).find(".notification-menu-entry");
419
420                // .html() gets the source code only for the first element of jQuery selection,
421                // thus if we have two messages from the same person,
422                // the second notification has same DOM and would not trigger change.
423                // We work around this by converting all menu body notifications to joined HTML string.
424                oldValue = oldValue.map(function() {
425                    return this.outerHTML;
426                }).get().join();
427                newValue = newValue.map(function() {
428                    return this.outerHTML;
429                }).get().join();
430
431                // TODO: Add a separate handler we actually have a notifications in the payload
432                // and e.g. not logged out response
433                return oldValue !== newValue;
434            }
435        });
436
437        // Pass the latest notification data, as received with the <body>
438        // to the other tabs.
439        var notificationData = $(".notifications_dropdown0").html();
440        notifications.trigger("notification-received", notificationData, true);
441
442        // When the page is opened, assume we get the notification payload
443        // in the HTML body and send a message that blinking should be stopped.
444        notifications.trigger("notification-noticed");
445
446        // This only matters when we are on settings page
447        detectWebNotificationEnabling();
448
449    }
450
451    // Intall JavaScript logic
452    $(document).ready(function() {
453        oldFixes();
454        popuify();
455        makeListingRowsClickable();
456        animateCollapsibles();
457        handleOnlineStatus();
458        handleFrontPageFormIE8();
459    });
460
461    // Install post-page render logic
462    $(window).load(function() {
463        tuneSplash();
464        tuneAdPage();
465        fixIE8NavBar();
466        handleNotifications();
467    });
468
469})(jQuery);
470
471
472// Returns a function, that, as long as it continues to be invoked, will not
473// be triggered. The function will be called after it stops being called for
474// N milliseconds. If `immediate` is passed, trigger the function on the
475// leading edge, instead of the trailing.
476function debounce(func, wait, immediate) {
477  var timeout;
478  return function() {
479    var context = this, args = arguments;
480    var later = function() {
481      timeout = null;
482      if (!immediate) func.apply(context, args);
483    };
484    var callNow = immediate && !timeout;
485    clearTimeout(timeout);
486    timeout = setTimeout(later, wait);
487    if (callNow) func.apply(context, args);
488  };
489};
490
491
492// https://docs.djangoproject.com/en/1.7/ref/contrib/csrf/#ajax
493// using jQuery
494function getCookie(name) {
495    var cookieValue = null;
496    if (document.cookie && document.cookie != '') {
497        var cookies = document.cookie.split(';');
498        for (var i = 0; i < cookies.length; i++) {
499            var cookie = jQuery.trim(cookies[i]);
500            // Does this cookie string begin with the name we want?
501            if (cookie.substring(0, name.length + 1) == (name + '=')) {
502                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
503                break;
504            }
505        }
506    }
507    return cookieValue;
508}

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.