PageSourceSearch

https://www.stockholmshem.se/assets/javascripts/widgets.js

js stockholmshem.se collected 2026-10-02 12:00:52 UTC 41,148 bytes, 955 lines download raw bytes

1(function ($) {
2    var logcount = 0;
3
4    function getCookie(cname) {
5        var name = cname + "=";
6        var ca = document.cookie.split(';');
7        for (var i = 0; i < ca.length; i++) {
8            var c = ca[i];
9            while (c.charAt(0) === ' ') {
10                c = c.substring(1);
11            }
12            if (c.indexOf(name) === 0) {
13                return c.substring(name.length, c.length);
14            }
15        }
16        return "";
17    }
18
19    var useEnglish = getCookie("Fast2User_language") === 'en';
20
21    // Internal scope
22    var fast2 = {
23
24        wc: "[data-widget]"
25
26        , events: {
27            widgetLoad: "fast2-widget-load"
28            , widgetFail: "fast2-widget-fail"
29            , widgetAlways: "fast2-widget-always"
30            , widgetReady: "fast2-widget-ready"
31            , postbackBefore: "fast2-widget-postback-before"
32            , postbackEmptyRequiredFields: "fast2-widget-postback-empty-required-fields"
33            , postbackAfter: "fast2-widget-postback-after"
34            , postbackSuccess: "fast2-widget-postback-success"
35            , postbackFail: "fast2-widget-postback-error"
36            , postbackAlways: "fast2-widget-postback-always"
37            , onFileuploadComplete: "fast2-widget-fileupload-complete"
38            , onFileUploadFail: "fast2-widget-fileupload-fail"
39            , onFileUploadAlways: "fast2-widget-fileupload-always"
40        }
41
42        , scriptUrl: ""
43        , serviceUrl: "/widgets/"
44
45        , log: function () {
46            if (window.f2widgetdebug && typeof console != 'undefined') {
47                console.log("(" + (logcount++) + ") fast2 widget factory:: ", Array.prototype.slice.call(arguments));
48            }
49        }
50
51        /**
52         * @returns samtliga widget-ID på sidan som strängar
53         */
54        , widgetIds: function () {
55            var widgets = fast2.widgets();
56            var widgetIds = [];
57            for (var i = 0; i < widgets.length; i++) {
58                widgetIds.push(widgets[i].data('widget'));
59            }
60            return widgetIds;
61        }
62
63        /**
64         * @returns samtliga widgets på sidan om jquery-objekt
65         */
66        , widgets: function () {
67            var widgets = [];
68            $('[data-widget]').each(function () {
69                var widget = $(this);
70                fast2.log(widget);
71                widgets.push(widget);
72            });
73            return widgets;
74        }
75
76
77        /**
78         * Debug function used for adding all available mustache template variables
79         * to a page using {{#templatevariables}}{{/templatevariables}}
80         */
81        , templatevariables: function (text, render) {
82            var result = fast2.describe('', this);
83            return result;
84        }
85
86        /**
87         * toString() for mustache template variable objects
88         */
89        , describe: function (prefix, obj, indentation) {
90            indentation = indentation || '';
91            var result = '';
92
93            var shouldIterate = typeof obj === 'object';
94            if (!shouldIterate) {
95                result = result + (indentation + "&lbrace;&lbrace;.&rbrace;&rbrace; = " + obj + "<br />");
96            } else if ($.isArray(obj)) {
97                result = result + ("&lbrace;&lbrace;#.&rbrace;&rbrace;");
98                var s = obj.length;
99                result = result + (" (" + s + " elements)<br>");
100                result = result + (s === 0 ? "" : fast2.describe('', obj[0], "&nbsp;&nbsp;"));
101                result = result + ("&lbrace;&lbrace;/.&rbrace;&rbrace;<br>");
102            } else {
103                for (var item in obj) {
104                    var isObject = typeof obj[item] === 'object';
105                    var isFunction = typeof obj[item] === 'function';
106                    fast2.log(item, isObject, typeof obj[item]);
107                    if ($.isArray(obj[item])) {
108                        result = result + ("&lbrace;&lbrace;#" + prefix + item + "&rbrace;&rbrace;");
109                        var size = obj[item].length;
110                        result = result + (" (" + size + " elements)<br>");
111                        result = result + (size === 0 ? "" : fast2.describe('', obj[item][0], "&nbsp;&nbsp;"));
112                        result = result + ("&lbrace;&lbrace;/" + prefix + item + "&rbrace;&rbrace;<br>");
113                    } else if (isObject) {
114                        result = result + fast2.describe(item + '.', obj[item]);
115                    } else if (isFunction) {
116                        // skip
117                    } else {
118                        // "(" + typeof obj[item] + ")
119                        result = result + (indentation + "&lbrace;&lbrace;" + prefix + item + "&rbrace;&rbrace; = " + obj[item] + "<br />");
120                    }
121                }
122            }
123            return result;
124        }
125
126        , widgetsLoaded: function (data) {
127            $.ajaxSetup({
128                type: 'POST',
129                headers: {
130                    CSRF: data.CSRFtoken
131                }
132            });
133            fast2.log("Kör widgetsLoaded ", data);
134            var widgets = fast2.widgets();
135
136            if (typeof data !== 'undefined') {
137                // Check for GA-events (must be done before redirect
138                // Register GA-Events
139                if (typeof _gaq !== 'undefined' && data.events !== 'undefined' && data.events.length !== 0) {
140                    $.each(data.events, function (index, obj) {
141                        _gaq.push(['trackEvent', obj.category, obj.action, obj.label, obj.value, obj.nonInteraction]);
142                    });
143                }
144                // Is new window requested ?
145                if (typeof data.openWindow != 'undefined' && data.openWindow !== null &&
146                    typeof data.openWindow.url != 'undefined' && data.openWindow.url !== null) {
147                    fast2.log("Nytt fönster öppnas med URL till " + data.openWindow.url);
148                    window.open(data.openWindow.url, data.openWindow.windowName, data.openWindow.features);
149                    // Fortsätt exekvera - vi kan både öppna nytt fönster och ladda widgets på sidan
150                }
151                // Is replace ?
152                if (typeof data.replaceUrl != 'undefined' && data.replaceUrl !== null) {
153                    fast2.log("URL replace till " + data.replaceUrl);
154                    window.location.replace(data.replaceUrl);
155                    return;
156                }
157                // Is redirect ?
158                if (typeof data.redirectUrl != 'undefined' && data.redirectUrl !== null) {
159                    fast2.log("Redirect till " + data.redirectUrl);
160                    window.location = data.redirectUrl;
161                    return;
162                }
163                // Is Reload page ?
164                if (typeof data.reload != 'undefined' && data.reload !== null) {
165                    fast2.log("Reload page då det finns alert message");
166                    location.reload();
167                }
168
169                var widgetIdsToBindFormEventsOn = [];
170
171                for (var i = 0; i < widgets.length; i++) {
172                    var widgetId = widgets[i].data('widget')
173                        , html = '';
174                    if (typeof data.data[widgetId] === 'undefined' && typeof data.html[widgetId] === 'undefined') {
175                        fast2.log("Hoppar över  - ingen data för ", widgetId);
176                        continue;
177                    }
178                    fast2.log("Kör ", widgetId);
179                    if (widgets[i].html().indexOf("{{") >= 0 || widgets[i].data('tmpl')) {
180                        if (typeof data.data[widgetId] === 'undefined') {
181                            // Data not defined
182                            continue;
183                        }
184                        if (!widgets[i].data('tmpl')) {
185                            // Cache template
186                            widgets[i].data('tmpl', widgets[i].html());
187                        }
188                        var inlineTemplateData = data.data[widgetId];
189                        // Allow template variable output using {{#templatevariables}}{{/templatevariables}}
190                        inlineTemplateData.templatevariables = function () {
191                            return fast2.templatevariables;
192                        };
193                        fast2.log(data.data, widgetId, data.data[widgetId]);
194                        fast2.log("Inline template finns, använder inner html för widget " + widgetId, widgets[i].data('tmpl'), inlineTemplateData);
195                        if (typeof inlineTemplateData !== 'undefined') {
196                            html = Mustache.render(widgets[i].data('tmpl'), inlineTemplateData);
197                        } else if (fast2.debug) {
198                            if (useEnglish) {
199                                html = 'Error: Data from service is missing for ' + widgetId;
200                            } else {
201                                html = 'Fel: Data från tjänst saknas för ' + widgetId;
202                            }
203                        }
204                    } else if (typeof data.html[widgetId] !== 'undefined') {
205                        fast2.log("html finns för widget " + widgetId);
206                        html = data.html[widgetId];
207                    } else {
208                        fast2.log("template saknas, använder inner html för widget " + widgetId);
209                        html = Mustache.render(widgets[i].html(), data);
210                    }
211
212                    fast2.log("Append HTML, size: " + html.length);
213                    widgets[i].html(html);
214                    if (html === '') {
215                        widgets[i].addClass('NoContent');
216                    } else {
217                        widgets[i].removeClass('NoContent');
218                    }
219                    widgets[i].removeClass('Loading');
220                    // Add actionId field to identify button
221                    $('form:not(.standalone-form):has([data-action])', widgets[i]).append('<input type="hidden" name="actionId" />');
222                    // Add listener for toggle of help message on form elements
223                    $('.HelperIcon', widgets[i]).click(function (event) {
224                        $(event.target).parent('.controls').find('.HiddenHelperText').toggle(400);
225                    });
226
227
228                    // Markdown-konvertering
229                    if (typeof (Markdown) !== 'undefined') {
230
231                        if (widgets[i].attr('data-markdown') == 'yes') {
232
233                            $(widgets[i]).find('.Markdown').each(function () {
234
235                                // Plocka ut text detta id
236                                var message = $(this).text();
237
238                                // Kör markdown-plugin på texten
239                                var converter = new Markdown.Converter();
240                                var output = converter.makeHtml(message);
241
242                                // Ersätt text med den konverterade texten
243                                $(this).html(output);
244
245                            });
246
247                        }
248
249                    }
250
251
252                    var widgetIdAlreadyExistsInBindArray = widgetIdsToBindFormEventsOn.indexOf(widgetId) !== -1;
253                    if (!widgetIdAlreadyExistsInBindArray) {
254                        widgetIdsToBindFormEventsOn.push(widgetId);
255                    }
256
257                    // Trigger onload for widget
258                    fast2.onWidgetsLoaded(data, widgetId);
259                }
260
261                for (var prop in widgetIdsToBindFormEventsOn) {
262                    if (widgetIdsToBindFormEventsOn.hasOwnProperty(prop)) {
263                        fast2.bindFormEvents(widgetIdsToBindFormEventsOn[prop]);
264                    }
265                }
266
267                if (typeof data.html.timing !== 'undefined') {
268                    console.log(data.html.timing);
269                    $('body').append(data.html.timing);
270                }
271
272                $('span.btn.disabled i.icon-refresh').each(function () {
273                    var $laddar = $(this).parent();
274                    var $submit = $laddar.prev();
275                    $submit.data('handled', false);
276                    $submit.show();
277                    $laddar.remove();
278                });
279            }
280
281            $('[data-widget] form[method=post]').find('[type=submit]').click(function () {
282                var parentForm = $(this).parents('form');
283                if (this.attributes["data-no-validation"] && this.attributes["data-no-validation"].value === "true") {
284                    $(parentForm).attr('data-no-validation', 'true');
285                }
286                $('[type=submit]', parentForm).removeAttr('clicked');
287                $(this).attr('clicked', 'true');
288            });
289
290            // File uploads
291            if ($.isFunction($.fn.fileupload)) {
292                $('input.FileUpload').fileupload({
293                    limitMultiFileUploads: 1,
294                    singleFileUploads: true,
295                    sequentialUploads: true,
296                    dataType: 'json',
297                    xhrFields: {
298                        withCredentials: true
299                    },
300                    formData: function (form) {
301                        var flowId = $(form).data('flow');
302                        if (typeof flowId !== 'undefined') {
303                            form = $('form[data-flow="flow.skapafelanmalan"]');
304                        }
305                        return form.serializeArray();
306                    },
307                    always: function (e, data) {
308                        fast2.trigger(fast2.events.onFileuploadComplete);
309                        if (typeof data.result === 'undefined') {
310                            fast2.fileuploadIframeReload(); //fail
311                        } else {
312                            fast2.widgetsLoaded(data.result); //success
313                        }
314                    },
315                    progressall: function (e, data) {
316                        fast2.log(data.loaded, data.total);
317                    },
318                    add: function (e, data) {
319                        fast2.log("add file", data);
320                        // Fil vald - gör enable på knapp om sådan finns
321                        var submitButton = $(".FileUploadSubmit", data.form);
322                        var fileChooserButton = $(".fileinput-button", data.form);
323                        fast2.log("add", submitButton, fileChooserButton);
324                        if (submitButton.length === 0) {
325                            // Hittar ingen submit-knapp, antar auto-submit
326                            fast2.submitOnce(fileChooserButton);
327                            data.submit();
328                            return true;
329                        }
330                        submitButton.prop('disabled', false);
331                        fileChooserButton.prop('disabled', true);
332                        fileChooserButton.addClass('btn-info');
333                        fileChooserButton.removeClass('btn-primary');
334                        var filename = '';
335                        try {
336                            fast2.log(data.files);
337                            filename = data.files[0].name;
338                        } catch (err) {
339                            fast2.log('Saknar file-API?', err);
340                        }
341                        fileChooserButton.find("span").text(fileChooserButton.data('fil-vald') + " " + filename);
342                        submitButton.on('click', function () {
343                            fast2.submitOnce(fileChooserButton);
344                            data.submit();
345                            return false;
346                        });
347                    }
348                }).prop('disabled', !$.support.fileInput)
349                    .parent().addClass($.support.fileInput ? undefined : 'disabled');
350            }
351
352
353            // Hitta alla länkar som skall registrera GA-event och lägg till click
354            if (typeof _gaq !== 'undefined') {
355                $('a[data-gacategory],input[data-gacategory]').each(function () {
356                    var $elem = $(this);
357                    var category = $elem.data('gacategory');
358                    var action = $elem.data('gaaction');
359
360                    $elem.click(function () {
361                        _gaq.push(['trackEvent', category, action]);
362                    });
363                });
364            }
365
366            // Klicka på tabellrad i kompakta listor
367            $('.kompakt-lista__rad').click(function () {
368                href = $(this).attr('data-href');
369                if (href !== undefined) {
370                    window.location = href;
371                }
372            });
373
374            // Klicka på tabellrad i bostadsbyte kompakt, behövs?
375            $('.objektlistabosbytekompakt__post').click(function () {
376                href = $(this).attr('data-href');
377                window.location = href;
378            });
379
380            // Klicka på tabellrad i kompakta listor (bostadsbytemeddelande), behövs?
381            $('.bostadsbyte-meddelandelista.table').click(function () {
382                href = $('.table__row', this).attr('data-href');
383                window.location = href;
384            });
385
386            // Klicka på tabellrad i publiceringstatisik, behövs?
387            $('.Publiceringstatistik.tabell').click(function () {
388                href = $('.tabell__rad', this).attr('data-href');
389                window.location = href;
390            });
391
392            $('[class*="__fieldset"] select,[class*="__fieldset"] [type="text"], [class*="__fieldset"] [type="tel"], ' +
393                '[class*="__fieldset"] [type="email"], [class*="__fieldset"] [type="checkbox"], [class*="__fieldset"] [type="password"]').each(function () {
394                $(this).on('blur', function () {
395                    fast2.wcagClientValidation(this);
396                });
397            });
398        },
399        bindFormEvents: function (widgetId) {
400            var $selector = $('[data-widget="' + widgetId + '"] form[method=post]');
401            $selector.find('[type=submit]').click(fast2.updateFormsAttributes);
402            $selector.on('submit', fast2.onSubmitFormsEvent);
403        },
404        updateFormsAttributes: function () {
405            var parentForm = $(this).parents('form');
406            if (this.attributes["data-no-validation"] && this.attributes["data-no-validation"].value === "true") {
407                $(parentForm).attr('data-no-validation', 'true');
408            }
409            $('[type=submit]', parentForm).removeAttr('clicked');
410            $(this).attr('clicked', 'true');
411        },
412        onSubmitFormsEvent: function (e) {
413            // Filtrera bort formulär som har egen action, t.ex. eavtal
414            if ($(this).attr("action") !== undefined) {
415                return;
416            }
417
418            fast2.log("Onsubmit handler");
419            if (!fast2.trigger(fast2.events.postbackBefore)) {
420                fast2.log("Cancel Onsubmit handler by custom event");
421                // Cancel event
422                return false;
423            }
424
425            // I fall där flera formulär hänger ihop med data-flow
426            // så skall alla fält valideras tillsammans
427            var dataFlow = $(this).data('flow');
428            if (dataFlow) {
429                forms = $('form[data-flow="' + dataFlow + '"]');
430                fast2.log("data-flow-attribut finns på widget-formulär, postar flöde " + dataFlow + " tillsammans", forms);
431            } else {
432                forms = this;
433            }
434            //var $widget = $(this).closest('[data-widget]');
435            //var widget = $widget.data('widget');
436
437
438            var validateData = !(e.target && e.target.attributes['data-no-validation']);
439            if (validateData && !fast2.validateForms()) return false;
440
441            // Add eventId
442            var actionId = $('[type=submit][clicked=true]', this).data('action');
443            fast2.log("actionId = " + actionId);
444            $('[name="actionId"]', this).val(actionId);
445
446            fast2.log("Event skickat!");
447            fast2.log(fast2.serviceUrl);
448
449            var serializedForm = fast2.getSerializedForm(forms);
450            var widgetIds = fast2.getWidgetIdsFromForms(forms);
451
452            var $submitbutton = $('[type=submit][clicked=true]', this);
453            fast2.submitOnce($submitbutton, forms);
454
455            fast2.postFormsToBackend(serializedForm, widgetIds);
456            return false;
457        },
458        submitForms: function (forms, validateData, widgetsToUpdate) {
459            if (validateData && !fast2.validateForms()) return false;
460            var serializedForm = fast2.getSerializedForm(forms);
461            var widgetIds = fast2.getWidgetIdsFromForms(forms, widgetsToUpdate);
462            fast2.postFormsToBackend(serializedForm, widgetIds);
463            return true;
464        },
465        getSerializedForm: function (forms) {
466            var serializedForm = $(forms).serialize();
467            fast2.log("Formulär", serializedForm);
468            return serializedForm;
469        },
470        getWidgetIdsFromForms: function (forms, widgetsToUpdate) {
471            if (!widgetsToUpdate) widgetsToUpdate = [];
472            // Hämta samtliga widgets som har ett submittat form i sig
473            var widgetIds = {
474                'widgets[]': $(forms).parents('[data-widget]').map(function () {
475                    return $(this).data('widget');
476                }).get().concat(widgetsToUpdate)
477            };
478            fast2.log("Widget ID:s", widgetIds);
479            return widgetIds;
480        },
481        validateForms: function (forms) {
482            var wcag = $('[class*="__fieldset"]').length > 0;
483
484            // Required validering
485            // Check required fields
486            $(".required:visible", forms).removeClass("error");
487            $required = $(".required:visible input, .required:visible select[value='']", forms)
488                .filter(function () {
489                    return wcag || !this.value;
490                });
491            //$required = $.merge($required, $(".required:visible input[value=''][data-required-for-widget='" + widget + "'], .required:visible select[value=''][data-required-for-widget'" + widget + "']"));
492            // Checkbox and radio
493            $required_checkboxes = $(".required:visible input:checkbox:not(:checked)", forms);
494            fast2.log("Required checkbox", $required_checkboxes);
495            if ($required_checkboxes.length > 0) {
496                $required = $.merge($required, $required_checkboxes);
497            }
498
499            $required_radio = $(".required:visible input[type='radio']", forms);
500            fast2.log("Required radio", $required_radio);
501            if ($required_radio.length > 0) {
502                var _items = {};
503                $required_radio.each(function () {
504                    var $this = $(this);
505                    var name = $this.attr('name');
506                    fast2.log("Required radio", $this.attr('id'), name, _items);
507                    if (_items[name] !== true) {
508                        if ($this.is(':checked')) {
509                            _items[name] = true;
510                        } else if (_items[name] === undefined) {
511                            _items[name] = this;
512                        }
513                    }
514                });
515                for (var e in _items) {
516                    if (_items[e] !== true)
517                        $required.push(_items[e]);
518                }
519            }
520
521            $requiredMatching = $("input[requirematching]", forms);
522            if ($requiredMatching.length > 0) {
523                $requiredMatching.each(function (index, mainElement) {
524                    var verificationElement = $("#" + mainElement.attributes.requirematching.value);
525                    if (verificationElement.length > 0 && mainElement.value != verificationElement[0].value) {
526                        $required.push(mainElement);
527                        $required.push(verificationElement);
528                    }
529                });
530            }
531
532            if ($required.length > 0) {
533                var $focusFieldElement = null;
534                var setFocusFieldElement = function (el) {
535                    $focusFieldElement = $focusFieldElement || el;
536                    el.parents(".control-group").addClass("error");
537                };
538                if (wcag) {
539                    $required.each(function () {
540                        var $this = $(this);
541                        fast2.wcagClientValidation(this);
542                        if ($this.hasClass('validation-error')) {
543                            setFocusFieldElement($this);
544                        }
545                    });
546                } else {
547                    fast2.log("Not all required fields are filled out", $required);
548                    $required.each(function () {
549                        setFocusFieldElement($(this));
550                    });
551                }
552                if ($focusFieldElement !== null) {
553                    // Set focus to first required field
554                    if (!$focusFieldElement.is("[type=checkbox], [type=radio]")) {
555                        $focusFieldElement.focus();
556                    }
557                    $focusFieldElement = fast2.getElementLabelFromContext($focusFieldElement, forms) || $focusFieldElement;
558                    fast2.scrollTo($focusFieldElement, 0.3);
559                    fast2.trigger(fast2.events.postbackEmptyRequiredFields);
560                    return false;
561                }
562            }
563            return true;
564        },
565
566        wcagClientValidation: function (el) {
567            var $el = $(el);
568            if ($el.attr('readonly')) return;
569            var message = '';
570            var required = $el.parent().hasClass('required') || $el.attr('required');
571            if ($el.attr('type') === 'checkbox') {
572                fast2.removeErrorMessage(el);
573                if (required && !$el.is(':checked')) {
574                    fast2.addErrorMessage(el, message);
575                }
576                return;
577            }
578            var id = $el.attr('id');
579            var match = id.match(/^((huvudsokande|medsokande|hyresgast\d)_)?(\w+)_value$/);
580            if (!match) return;
581            var part = match[2];
582            var field = match[3].toLowerCase();
583            var value = $el.val();
584            if (value === '') {
585                if (required) {
586                    fast2.addErrorMessage(el, useEnglish ? 'The field must not be left blank' : 'Fältet får inte lämnas tomt');
587                }
588                return;
589            }
590            var pattern = $el.prop('pattern');
591            var regex = '';
592            var matchedRegex = '';
593            if (pattern) {
594                regex = new RegExp(pattern, 'gu');
595                matchedRegex = value.match(regex);
596            }
597            fast2.removeErrorMessage(el);
598
599            if (matchedRegex) {
600                fast2.patternMatched(el, matchedRegex);
601                return;
602            } else if (field === 'bekraftaepost') {
603                if (value === $('#' + part + '_epost_value').val()) {
604                    fast2.patternMatched(el, value);
605                    return;
606                }
607
608                value = undefined;
609            } else if (field === 'confirmnewpassword') {
610                if (value === $('#newPassword_value').val()) {
611                    fast2.patternMatched(el, value);
612                    return;
613                }
614
615                value = undefined;
616            } else {
617                switch (field) {
618                    case 'kundnummer':
619                    case 'kundnr':
620                        value = value.replace(/\D/g, '');
621                        break;
622
623                    case 'fnamn':
624                    case 'fornamn':
625                    case 'enamn':
626                    case 'efternamn':
627                        value = value.replace(/[0-9]/g, '');
628                        value = value.charAt(0).toUpperCase() + value.substring(1);
629                        break;
630
631                    case 'adress':
632                    case 'ort':
633                        value = value.charAt(0).toUpperCase() + value.substring(1);
634                        break;
635
636                    case 'postnr':
637                        value = value.replace(/[^\d-]/g, '');
638                        break;
639
640                    case 'telefonbostad':
641                    case 'telefondagtid':
642                    case 'telefonkvallstid':
643                    case 'telefonmobil':
644                    case 'mobilnr':
645                    case 'epost':
646                    case 'bekraftaepost':
647                    case 'newpassword':
648                        value = undefined;
649                        break;
650
651                    default:
652                        return;
653                }
654            }
655            if (value === undefined || !fast2.wcagFieldValidation(el, value, regex)) {
656                fast2.addErrorMessage(el, message);
657            }
658        },
659
660        wcagFieldValidation: function (el, value, regex) {
661            var $el = $(el);
662            if (regex === '') {
663                return true;
664            }
665            var matchedRegex = value.match(regex);
666            if (matchedRegex) {
667                $el.val(matchedRegex);
668                return true;
669            }
670            $el.val(value);
671            return false;
672        },
673
674        patternMatched: function (el, matchedRegex) {
675            fast2.removeErrorMessage(el);
676            $(el).val(matchedRegex);
677        },
678
679        addErrorMessage: function (el, message) {
680            var $el = $(el);
681            var $parent = $el.parent();
682            if (message === '') {
683                message = $el.attr('data-validationmessage') || (useEnglish ? 'The field must be filled out correctly' : 'Fältet behöver vara korrekt ifyllt');
684            }
685            $el.removeClass('validation-success').addClass('validation-error');
686            $parent.find('.validation-error__message').remove();
687            $parent.append('<span role="alert" class="validation-error__message">' + message + '</span>');
688        },
689        removeErrorMessage: function (el) {
690            var $el = $(el);
691            $el.removeClass('validation-error').addClass('validation-success');
692            $el.parent().find('.validation-error__message').remove();
693        },
694
695        scrollTo: function (el, position, speed) {
696            position = position === undefined || position < 0.0 ? 0.0 : (position > 1.0 ? 1.0 : position);
697            var windowHeight = $(window).height();
698            var offset = el.offset().top;
699            var elHeight = el.height();
700            if (elHeight < windowHeight) offset -= (windowHeight - elHeight) * position;
701            $('html, body').animate({scrollTop: offset}, speed === undefined ? 700 : speed);
702        },
703
704        getElementLabelFromContext: function (element, context) {
705            var elementId = element.attr('id');
706            var label;
707
708            if (!elementId) {
709                return null;
710            }
711
712            label = $("label[for='" + elementId + "']", context).first();
713
714            return label.length > 0 ? label : null;
715        },
716        postFormsToBackend: function (serializedForm, widgetIds) {
717            $.ajax({
718                url: fast2.serviceUrl + fast2.getWindowLocationSearch(),
719                type: 'POST',
720                dataType: 'jsonp',
721                data: serializedForm + "&" + $.param(widgetIds),
722                success: function (data, textStatus) {
723                    fast2.log('Success!', data, textStatus);
724                    fast2.trigger(fast2.events.postbackSuccess);
725                },
726                crossDomain: false,
727                xhrFields: {
728                    withCredentials: true
729                }
730            }).done(fast2.widgetsLoaded).fail(function () {
731                fast2.log("Widget fail", arguments);
732                fast2.trigger(fast2.events.postbackFail);
733            }).always(function () {
734                fast2.log("Widget always", arguments);
735                fast2.trigger(fast2.events.postbackAlways); // Cowboy firing
736            });
737            fast2.log("Formulär postat!");
738        },
739
740        submitOnce: function (element, forms) {
741            var submitOnceText = $(element).data("f2-once");
742            if (submitOnceText !== undefined && $(element).data('handled') !== true) { //  && (forms == undefined || $(".required:visible", forms).size() > 0)
743                $(element).data('handled', true);
744                // Get any pull-classes
745                var addClasses = $(element).attr('class') === undefined ? '' : fast2.findAndReturnMatchingClasses(element, ['pull-left', 'pull-right']);
746                $(element).after('<span class="btn disabled ' + addClasses + '"><i class="icon-refresh"></i> ' + (submitOnceText.length > 0 ? submitOnceText : useEnglish ? 'Loading...' : 'Laddar...') + '</span>');
747                $(element).hide();
748            }
749        },
750        findAndReturnMatchingClasses: function (element, searchClasses) {
751            var $elem = $(element);
752            var classes = "";
753
754            for (var i = 0; i < searchClasses.length; i++) {
755                if ($elem.attr('class').indexOf(searchClasses[i]) !== -1) {
756                    classes += " " + searchClasses[i];
757                }
758            }
759            return classes;
760        },
761
762        /**
763         * Plockar ut data-param-key=value från widgets och
764         * skickar med dessa i ajax-anropet som
765         * widget-ID.key=value
766         * Viss justering görs med lowercase/uppercase på param-namnet
767         * om detta innehåller flera -
768         */
769        widgetParamData: function (requestData, widgetIds) {
770            for (var i = 0; i < widgetIds.length; i++) {
771                var widgetId = widgetIds[i];
772                var widget = $('[data-widget="' + widgetId + '"]');
773                var attributes = widget.data();
774                if ($.isPlainObject(attributes)) {
775                    for (var key in attributes) {
776                        if (key.indexOf('param') === 0) {
777                            var paramName = widgetId + "." + key.replace('param', '').toLowerCase();
778                            fast2.log("Lägger till parameter " + paramName + "=" + attributes[key] + " för widget " + widgetId);
779                            requestData[paramName] = attributes[key];
780                        }
781                    }
782                }
783            }
784            return requestData;
785        },
786
787        loadWidgets: function (serviceUrl) {
788            fast2.log("Kör fast2.loadWidgets ", serviceUrl);
789            fast2.trigger(fast2.events.widgetLoad);
790            fast2.serviceUrl = serviceUrl;
791            var widgetIds = fast2.widgetIds();
792            var requestData = {
793                'widgets': widgetIds
794            };
795            requestData = fast2.widgetParamData(requestData, widgetIds);
796
797            $.ajax({
798                url: fast2.serviceUrl + fast2.getWindowLocationSearch(),
799                dataType: 'jsonp',
800                data: requestData,
801                crossDomain: false,
802                xhrFields: {
803                    withCredentials: true
804                }
805            }).done(function (data) {
806                fast2.log(data);
807
808                if (data.javascripts && data.javascripts.length > 0) {
809                    // Load logic scripts for widgets
810                    fast2.loadLogicScripts(data);
811                } else {
812                    fast2.widgetsLoaded(data);
813                }
814
815            }).fail(function () {
816                // TODO: Vad ska hända om inte widgeten hittas???
817                fast2.log("Widget failed", arguments);
818                fast2.trigger(fast2.events.widgetFail);
819            }).always(function () {
820                // Alla widgetts är klara och levde lyckliga alla sina dagar
821                fast2.log("Widgets ready", arguments);
822                fast2.trigger(fast2.events.widgetAlways);
823            });
824        }
825
826        /** Callback **/
827        // Borde heta onWidgetLoaded - anropas en gång för varje widget...
828        , onWidgetsLoaded: function (data, wid) {
829            fast2.log("onWidgetsLoaded", fast2.events.widgetReady);
830            fast2.trigger(fast2.events.widgetReady, {data: data}, wid); // Cowboy firing
831
832            // Captcha-stöd
833            $('div[data-captcha]').each(function () {
834                fast2.log('captcha', this);
835                Recaptcha.create($(this).data('captcha'),
836                    this,
837                    {
838                        // Options : https://developers.google.com/recaptcha/docs/customization
839                        theme: "white",
840                        callback: Recaptcha.focus_response_field
841                    }
842                );
843            });
844
845        }
846
847        , trigger: function (eventName, eventData, eventTarget) {
848            fast2.log("trigger " + eventName, eventData, eventTarget);
849            if (typeof eventTarget == "undefined") {
850                eventTarget = fast2.wc;
851            } else {
852                eventTarget = '[data-widget="' + eventTarget + '"]';
853            }
854            var defaultEventData = {ok: true, message: ""};
855            eventData = $.extend({}, defaultEventData, eventData);
856            var e = new jQuery.Event(eventName, [eventData]);
857            // Back reference to prop
858            e.f2 = function () {
859                return this[0];
860            };
861            fast2.log("trigger " + eventName, e);
862            $(eventTarget).trigger(e);
863            // Event response
864            fast2.log("trigger response " + eventName, eventData);
865            return eventData.ok;
866        }
867
868        , addPostBackBehavior: function () {
869
870        }
871
872        , loadLogicScripts: function (data) {
873            var callback = (function () {
874                var result = data.javascripts.slice();
875                return function (scriptUrl, ok) {
876                    result.pop();
877                    if (result.length === 0) {
878                        fast2.widgetsLoaded(data);
879                    }
880                };
881            })();
882
883            for (var item = 0; item < data.javascripts.length; item++) {
884                fast2.getSupportingScripts(callback, data.javascripts [item]);
885            }
886
887        }
888
889        , getSupportingScripts: function (callback, scriptUrl) {
890            if (scriptUrl.substring(0, 2) == '//') {
891                scriptUrl = window.location.protocol + scriptUrl;
892            } else {
893                scriptUrl = fast2.scriptUrl + scriptUrl;
894            }
895            if ($('[src="' + scriptUrl + '"]').length === 0) {
896                $.getScript(scriptUrl, function (data, textStatus, jqxhr) {
897                    callback(scriptUrl, true);
898                }).fail(function (jqxhr, settings, exception) {
899                    // Hitta alert
900                    callback(scriptUrl, false);
901                });
902            } else {
903                window.setTimeout(function () {
904                    callback(scriptUrl, true);
905                }, 1);
906            }
907        }
908
909        , fileuploadIframeReload: function () {
910            var url = location.href;
911            var separator = "";
912            if (url[url.length - 1] === '?') {
913                separator = "";
914            } else if (url.indexOf('?') > 0) {
915                separator = "&";
916            } else {
917                separator = "?";
918            }
919            location.replace(url + separator + "fileuploadiframereload=true");
920        }
921
922        , getWindowLocationSearch: function () {
923            var search = window.location.search;
924            if (search.indexOf('callback') != -1) {
925                search = search.replace(/([?&])callback=[^&]*&?/g, "$1");
926                if (search != window.location.search) {
927                    if (search === '?') search = '';
928                    var url = window.location.href.split('?')[0] + search;
929                    window.history.pushState('', document.title, url);
930                }
931            }
932            return search;
933        }
934
935        , stopEvent: function (e) {
936            if (e.preventDefault !== undefined) e.preventDefault();
937            if (e.stopPropagation !== undefined) e.stopPropagation();
938        }
939
940    };
941
942    window.fast2 = fast2;
943
944    $(function () {
945        // Check for service url on script elements
946        var $serviceScript = $('script[data-widget-service]');
947        if ($serviceScript && $serviceScript.attr("src") !== undefined && $serviceScript.attr("src").split('/').length > 3) {
948            fast2.scriptUrl = '//' + $serviceScript.attr("src").split('/')[2];
949            fast2.serviceUrl = '//' + $serviceScript.attr("src").split('/')[2] + fast2.serviceUrl;
950        }
951        fast2.log("fast2 loadWidgets" + fast2.serviceUrl);
952        fast2.loadWidgets(fast2.serviceUrl);
953    });
954
955})(jQuery);

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.