PageSourceSearch

https://zkvh.org.rs/modules/system/assets/js/framework.js

js zkvh.org.rs collected 2026-10-02 12:18:11 UTC 33,264 bytes, 931 lines download raw bytes

1/* ========================================================================
2 * October CMS: Frontend JavaScript Framework
3 * https://octobercms.com
4 * ========================================================================
5 * Copyright 2013-2021 Alexey Bobkov, Samuel Georges
6 * ======================================================================== */
7
8if (window.jQuery === undefined) {
9    throw new Error('The jQuery library is not loaded! The October CMS framework cannot be initialized.');
10}
11if (window.jQuery.request !== undefined) {
12    throw new Error('The October CMS framework is already loaded!');
13}
14
15+function ($) { "use strict";
16
17    var Request = function (element, handler, options) {
18        var $el = this.$el = $(element);
19        this.options = options || {};
20
21        /*
22         * Validate handler name
23         */
24        if (handler === undefined) {
25            throw new Error('The request handler name is not specified.');
26        }
27
28        if (!handler.match(/^(?:\w+\:{2})?on*/)) {
29            throw new Error('Invalid handler name. The correct handler name format is: "onEvent".');
30        }
31
32        /*
33         * Prepare the options and execute the request
34         */
35        var $form = options.form ? $(options.form) : $el.closest('form'),
36            $triggerEl = !!$form.length ? $form : $el,
37            context = { handler: handler, options: options };
38
39        /*
40         * Validate the form client-side
41         */
42        if (
43            (options.browserValidate !== undefined) &&
44            typeof document.createElement('input').reportValidity == 'function' &&
45            $form &&
46            $form[0] &&
47            !$form[0].checkValidity()
48        ) {
49            $form[0].reportValidity();
50            return false;
51        }
52
53        /*
54         * Execute the request
55         */
56        $el.trigger('ajaxSetup', [context]);
57        var _event = jQuery.Event('oc.beforeRequest');
58        $triggerEl.trigger(_event, context);
59        if (_event.isDefaultPrevented()) {
60            return;
61        }
62
63        var loading = options.loading !== undefined ? options.loading : null,
64            url = options.url !== undefined ? options.url : window.location.href,
65            isRedirect = options.redirect !== undefined && options.redirect.length,
66            useFlash = options.flash !== undefined,
67            useFiles = options.files !== undefined;
68
69        if (useFiles && typeof FormData === 'undefined') {
70            console.warn('This browser does not support file uploads via FormData');
71            useFiles = false;
72        }
73
74        if ($.type(loading) == 'string') {
75            loading = $(loading);
76        }
77
78        /*
79         * Request headers
80         */
81        var requestHeaders = {
82            'X-OCTOBER-REQUEST-HANDLER': handler,
83            'X-OCTOBER-REQUEST-PARTIALS': this.extractPartials(options.update)
84        };
85
86        if (useFlash) {
87            requestHeaders['X-OCTOBER-REQUEST-FLASH'] = 1;
88        }
89
90        var csrfToken = getXSRFToken();
91        if (csrfToken) {
92            requestHeaders['X-XSRF-TOKEN'] = csrfToken;
93        }
94
95        /*
96         * Request data
97         */
98        var requestData,
99            inputName,
100            data = {};
101
102        $.each($el.parents('[data-request-data]').toArray().reverse(), function extendRequest() {
103            $.extend(data, paramToObj('data-request-data', $(this).data('request-data')));
104        });
105
106        if ($el.is(':input') && !$form.length) {
107            inputName = $el.attr('name');
108            if (inputName !== undefined && options.data[inputName] === undefined) {
109                options.data[inputName] = $el.val();
110            }
111        }
112
113        if (options.data !== undefined && !$.isEmptyObject(options.data)) {
114            $.extend(data, options.data);
115        }
116
117        if (useFiles) {
118            requestData = new FormData($form.length ? $form.get(0) : undefined);
119
120            if ($el.is(':file') && inputName) {
121                $.each($el.prop('files'), function() {
122                    requestData.append(inputName, this);
123                });
124
125                delete data[inputName];
126            }
127
128            $.each(data, function(key) {
129                requestData.append(key, this);
130            });
131        }
132        else {
133            requestData = [$form.serialize(), $.param(data)].filter(
133Boolean).join('&');
134        }
135
136        /*
137         * Request options
138         */
139        var requestOptions = {
140            url: url,
141            crossDomain: false,
142            global: options.ajaxGlobal,
143            context: context,
144            headers: requestHeaders,
145            success: function(data, textStatus, jqXHR) {
146                /*
147                 * Halt here if beforeUpdate() or data-request-before-update returns false
148                 */
149                if (this.options.beforeUpdate.apply(this, [data, textStatus, jqXHR]) === false) {
150                    return;
151                }
152
153                if (options.evalBeforeUpdate && $.proxy(new Function('data', options.evalBeforeUpdate), $el.get(0))(data) === false) {
154                    return;
155                }
156
157                /*
158                 * Trigger 'ajaxBeforeUpdate' on the form, halt if event.preventDefault() is called
159                 */
160                var _event = jQuery.Event('ajaxBeforeUpdate');
161                $triggerEl.trigger(_event, [context, data, textStatus, jqXHR]);
162                if (_event.isDefaultPrevented()) return;
163
164                if (useFlash && data['X_OCTOBER_FLASH_MESSAGES']) {
165                    $.each(data['X_OCTOBER_FLASH_MESSAGES'], function(type, message) {
166                        requestOptions.handleFlashMessage(message, type);
167                    })
168                }
169
170                /*
171                 * Proceed with the update process
172                 */
173                var updatePromise = requestOptions.handleUpdateResponse(data, textStatus, jqXHR);
174
175                updatePromise.done(function() {
176                    $triggerEl.trigger('ajaxSuccess', [context, data, textStatus, jqXHR]);
177                    options.evalSuccess && $.proxy(new Function('data', options.evalSuccess), $el.get(0))(data);
178                })
179
180                return updatePromise;
181            },
182            error: function(jqXHR, textStatus, errorThrown) {
183                var errorMsg,
184                    updatePromise = $.Deferred();
185
186                if ((window.ocUnloading !== undefined && window.ocUnloading) || errorThrown == 'abort') {
187                    return;
188                }
189
190                /*
191                 * Disable redirects
192                 */
193                isRedirect = false;
194                options.redirect = null;
195
196                /*
197                 * Error 406 is a "smart error" that returns response object that is
198                 * processed in the same fashion as a successful response.
199                 */
200                if (jqXHR.status == 406 && jqXHR.responseJSON) {
201                    errorMsg = jqXHR.responseJSON['X_OCTOBER_ERROR_MESSAGE'];
202                    updatePromise = requestOptions.handleUpdateResponse(jqXHR.responseJSON, textStatus, jqXHR);
203                }
204                /*
205                 * Standard error with standard response text
206                 */
207                else {
208                    errorMsg = jqXHR.responseText ? jqXHR.responseText : jqXHR.statusText;
209                    updatePromise.resolve();
210                }
211
212                updatePromise.done(function() {
213                    $el.data('error-message', errorMsg);
214
215                    /*
216                     * Trigger 'ajaxError' on the form, halt if event.preventDefault() is called
217                     */
218                    var _event = jQuery.Event('ajaxError');
219                    $triggerEl.trigger(_event, [context, errorMsg, textStatus, jqXHR]);
220                    if (_event.isDefaultPrevented()) return;
221
222                    /*
223                     * Halt here if the data-request-error attribute returns false
224                     */
225                    if (options.evalError && $.proxy(new Function(options.evalError), $el.get(0))() === false) {
226                        return;
227                    }
228
229                    requestOptions.handleErrorMessage(errorMsg);
230                });
231
232                return updatePromise;
233            },
234            complete: function(data, textStatus, jqXHR) {
235                $triggerEl.trigger('ajaxComplete', [context, data, textStatus, jqXHR]);
236                options.evalComplete && $.proxy(new Function('data', options.evalComplete), $el.get(0))(data);
237            },
238
239            /*
240             * Custom function, requests confirmation from the user
241             */
242            handleConfirmMessage: function(message) {
243                var _event = jQuery.Event('ajaxConfirmMessage');
244
245                _event.promise = $.Deferred();
246                if ($(window).triggerHandler(_event, [message]) !== undefined) {
247                    _event.promise.done(function() {
248                        options.confirm = null;
249                        new Request(element, handler, options);
250                    })
251                    return false;
252                }
253
254                if (_event.isDefaultPrevented()) return;
255                if (message) return confirm(message);
256            },
257
258            /*
259             * Custom function, display an error message to the user
260             */
261            handleErrorMessage: function(message) {
262                var _event = jQuery.Event('ajaxErrorMessage');
263                $(window).trigger(_event, [message]);
264                if (_event.isDefaultPrevented()) return;
265                if (message) alert(message);
266            },
267
268            /*
269             * Custom function, focus fields with errors
270             */
271            handleValidationMessage: function(message, fields) {
272                $triggerEl.trigger('ajaxValidation', [context, message, fields]);
273
274                var isFirstInvalidField = true;
275                $.each(fields, function focusErrorField(fieldName, fieldMessages) {
276                    fieldName = fieldName.replace(/\.(\w+)/g, '[$1]');
277
278                    var fieldElement = $form.find('[name="'+fieldName+'"], [name="'+fieldName+'[]"], [name$="['+fieldName+']"], [name$="['+fieldName+'][]"]').filter(':enabled').first();
279                    if (fieldElement.length > 0) {
280
281                        var _event = jQuery.Event('ajaxInvalidField');
282                        $(window).trigger(_event, [fieldElement.get(0), fieldName, fieldMessages, isFirstInvalidField]);
283
284                        if (isFirstInvalidField) {
285                            if (!_event.isDefaultPrevented()) fieldElement.focus();
286                            isFirstInvalidField = false;
287                        }
288                    }
289                });
290            },
291
292            /*
293             * Custom function, display a flash message to the user
294             */
295            handleFlashMessage: function(message, type) {},
296
297            /*
298             * Custom function, redirect the browser to another location
299             */
300            handleRedirectResponse: function(url) {
301                window.location.assign(url);
302                $el.trigger('ajaxRedirect');
303            },
304
305            /*
306             * Custom function, handle any application specific response values
307             * Using a promisary object here in case injected assets need time to load
308             */
309            handleUpdateResponse: function(data, textStatus, jqXHR) {
310
311                /*
312                 * Update partials and finish request
313                 */
314                var updatePromise = $.Deferred().done(function() {
315                    for (var partial in data) {
316                        /*
317                         * If a partial has been supplied on the client side that matches the server supplied key, look up
318                         * it's selector and use that. If not, we assume it is an explicit selector reference.
319                         */
320                        var selector = (options.update[partial]) ? options.update[partial] : partial;
321                        if ($.type(selector) == 'string' && selector.charAt(0) == '!') {
322                            $(selector.substring(1)).replaceWith(data[partial]).trigger('ajaxUpdate', [context, data, textStatus, jqXHR]);
323                        }
324                        else if ($.type(selector) == 'string' && selector.charAt(0) == '@') {
325                            $(selector.substring(1)).append(data[partial]).trigger('aja
325xUpdate', [context, data, textStatus, jqXHR]);
326                        }
327                        else if ($.type(selector) == 'string' && selector.charAt(0) == '^') {
328                            $(selector.substring(1)).prepend(data[partial]).trigger('ajaxUpdate', [context, data, textStatus, jqXHR]);
329                        }
330                        else {
331                            $(selector).trigger('ajaxBeforeReplace');
332                            $(selector).html(data[partial]).trigger('ajaxUpdate', [context, data, textStatus, jqXHR]);
333                        }
334                    }
335
336                    /*
337                     * Wait for .html() method to finish rendering from partial updates
338                     */
339                    setTimeout(function() {
340                        $(window)
341                            .trigger('ajaxUpdateComplete', [context, data, textStatus, jqXHR])
342                            .trigger('resize');
343                    }, 0);
344                })
345
346                /*
347                 * Handle redirect
348                 */
349                if (data['X_OCTOBER_REDIRECT']) {
350                    options.redirect = data['X_OCTOBER_REDIRECT'];
351                    isRedirect = true;
352                }
353
354                if (isRedirect) {
355                    requestOptions.handleRedirectResponse(options.redirect);
356                }
357
358                /*
359                 * Handle validation
360                 */
361                if (data['X_OCTOBER_ERROR_FIELDS']) {
362                    requestOptions.handleValidationMessage(data['X_OCTOBER_ERROR_MESSAGE'], data['X_OCTOBER_ERROR_FIELDS']);
363                }
364
365                /*
366                 * Handle asset injection
367                 */
368                 if (data['X_OCTOBER_ASSETS']) {
369                    assetManager.load(data['X_OCTOBER_ASSETS'], $.proxy(updatePromise.resolve, updatePromise));
370                 }
371                 else {
372                    updatePromise.resolve();
373                }
374
375                return updatePromise;
376            }
377        }
378
379        if (useFiles) {
380            requestOptions.processData = requestOptions.contentType = false;
381        }
382
383        /*
384         * Allow default business logic to be called from user functions
385         */
386        context.success = requestOptions.success;
387        context.error = requestOptions.error;
388        context.complete = requestOptions.complete;
389        requestOptions = $.extend(requestOptions, options);
390        requestOptions.data = requestData;
391
392        /*
393         * Initiate request
394         */
395        if (options.confirm && !requestOptions.handleConfirmMessage(options.confirm)) {
396            return;
397        }
398
399        if (loading) loading.show();
400        $(window).trigger('ajaxBeforeSend', [context]);
401        $el.trigger('ajaxPromise', [context]);
402
403        return $.ajax(requestOptions)
404            .fail(function(jqXHR, textStatus, errorThrown) {
405                if (!isRedirect) {
406                    $el.trigger('ajaxFail', [context, textStatus, jqXHR]);
407                }
408                if (loading) loading.hide();
409            })
410            .done(function(data, textStatus, jqXHR) {
411                if (!isRedirect) {
412                    $el.trigger('ajaxDone', [context, data, textStatus, jqXHR]);
413                }
414                if (loading) loading.hide();
415            })
416            .always(function(dataOrXhr, textStatus, xhrOrError) {
417                $el.trigger('ajaxAlways', [context, dataOrXhr, textStatus, xhrOrError]);
418            });
419    }
420
421    Request.DEFAULTS = {
422        update: {},
423        type : 'POST',
424        beforeUpdate: function(data, textStatus, jqXHR) {},
425        evalBeforeUpdate: null,
426        evalSuccess: null,
427        evalError: null,
428        evalComplete: null,
429        ajaxGlobal: false
430    }
431
432    /*
433     * Internal function, build a string of partials and their update elements.
434     */
435    Request.prototype.extractPartials = function(update) {
436        var result = [];
437
438        for (var partial in update) {
439            result.push(partial);
440        }
441
442        return result.join('&');
443    }
444
445    // REQUEST PLUGIN DEFINITION
446    // ============================
447
448    var old = $.fn.request;
449
450    $.fn.request = function(handler, option) {
451        var args = arguments;
452
453        var $this = $(this).first();
454        var data  = {
455            evalBeforeUpdate: $this.data('request-before-update'),
456            evalSuccess: $this.data('request-success'),
457            evalError: $this.data('request-error'),
458            evalComplete: $this.data('request-complete'),
459            ajaxGlobal: $this.data('request-ajax-global'),
460            confirm: $this.data('request-confirm'),
461            redirect: $this.data('request-redirect'),
462            loading: $this.data('request-loading'),
463            flash: $this.data('request-flash'),
464            files: $this.data('request-files'),
465            browserValidate: $this.data('browser-validate'),
466            form: $this.data('request-form'),
467            url: $this.data('request-url'),
468            update: paramToObj('data-request-update', $this.data('request-update')),
469            data: paramToObj('data-request-data', $this.data('request-data'))
470        };
471        if (!handler) handler = $this.data('request');
472        var options = $.extend(true, {}, Request.DEFAULTS, data, typeof option == 'object' && option);
473        return new Request($this, handler, options);
474    }
475
476    $.fn.request.Constructor = Request;
477
478    $.request = function(handler, option) {
479        return $(document).request(handler, option);
480    }
481
482    // REQUEST NO CONFLICT
483    // =================
484
485    $.fn.request.noConflict = function() {
486        $.fn.request = old;
487        return this;
488    }
489
490    // REQUEST DATA-API
491    // ==============
492
493    function paramToObj(name, value) {
494        if (value === undefined) value = '';
495        if (typeof value == 'object') return value;
496
497        try {
498            return ocJSON("{" + value + "}");
499        }
500        catch (e) {
501            throw new Error('Error parsing the '+name+' attribute value. '+e);
502        }
503    }
504
505    function getXSRFToken() {
506        var cookieValue = null;
507        if (document.cookie && document.cookie != '') {
508            var cookies = document.cookie.split(';');
509            for (var i = 0; i < cookies.length; i++) {
510                var cookie = jQuery.trim(cookies[i]);
511                if (cookie.substring(0, 11) == ('XSRF-TOKEN' + '=')) {
512                    cookieValue = decodeURIComponent(cookie.substring(11));
513                    break;
514                }
515            }
516        }
517        return cookieValue;
518    }
519
520    $(document).on('change', 'select[data-request], input[type=radio][data-request], input[type=checkbox][data-request], input[type=file][data-request]', function documentOnChange() {
521        $(this).request();
522    });
523
524    $(document).on('click', 'a[data-request], button[data-request], input[type=button][data-request], input[type=submit][data-request]', function documentOnClick(e) {
525        e.preventDefault();
526
527        $(this).request();
528
529        if ($(this).is('[type=submit]')) {
530            return false;
531        }
532    });
533
534    $(document).on('keydown', 'input[type=text][data-request], input[type=submit][data-request], input[type=password][data-request]', function documentOnKeydown(e) {
535        if (e.key === 'Enter') {
536            if (this.dataTrackInputTimer !== undefined) {
537                window.clearTimeout(this.dataTrackInputTimer);
538            }
539
540            $(this).request();
541            return false;
542        }
543    });
544
545    $(document).on('input', 'input[data-request][data-track-input]', function documentOnKeyup(e) {
546        var
547            $el = $(this),
548            lastValue = $el.data('oc.lastvalue');
549
550        if (!$el.is('[type=email],[type=number],[type=password],[type=search],[type=text]')) {
551            return;
552        }
553
554        if (lastValue !== undefined && lastValue == this.value) {
555            return;
556        }
557
558        $el.data('oc.lastvalue', this.value);
559
560        if (this.dataTrackInputTimer !== undefined) {
561            window.clearTimeout(this.dataTrackInputTimer);
562        }
563
564        var interval = $(this).data('track-input');
565        if (!interval) {
566            interval = 300;
567        }
568
569        var self = this;
570        this.dataTrackInputTimer = window.setTimeout(function() {
571            if (self.lastDataTrackInputRequest) {
572                self.lastDataTrackInputRequest.abort();
573            }
574            self.lastDataTrackInputRequest = $(self).request();
575        }, interval);
576    });
577
578    $(document).on('submit', '[data-request]', function documentOnSubmit() {
579        $(this).request();
580        return false;
581    });
582
583    window.onbeforeunload = function documentOnBeforeUnload() {
584        window.ocUnloading = true;
585    }
586
587    /*
588     * Invent our own event that unifies document.ready with window.ajaxUpdateComplete
589     *
590     * $(document).render(function() { })
591     * $(document).on('render', function() { })
592     */
593
594    $(document).ready(function triggerRenderOnReady() {
595        $(document).trigger('render');
596    });
597
598    $(window).on('ajaxUpdateComplete', function triggerRenderOnAjaxUpdateComplete() {
599        $(document).trigger('render');
600    });
601
602    $.fn.render = function(callback) {
603        $(document).on('render', callback);
604    };
605
606}(window.jQuery);
607
608/*
609 * October CMS JSON Parser
610 */
611+function(window) { "use strict";
612    function parseKey(str, pos, quote) {
613        var key = "";
614        for (var i = pos; i < str.length; i++) {
615            if (quote && quote === str[i]) {
616                return key;
617            } else if (!quote && (str[i] === " " || str[i] === ":")) {
618                return key;
619            }
620
621            key += str[i];
622
623            if (str[i] === "\\" && i + 1 < str.length) {
624                key += str[i + 1];
625                i++;
626            }
627        }
628        throw new Error("Broken JSON syntax near " + key);
629    }
630
631    function getBody(str, pos) {
632        // parse string body
633        if (str[pos] === "\"" || str[pos] === "'") {
634            var body = str[pos];
635            for (var i = pos + 1; i < str.length; i++) {
636                if (str[i] === "\\") {
637                    body += str[i];
638                    if (i + 1 < str.length) body += str[i + 1];
639                    i++;
640                } else if (str[i] === str[pos]) {
641                    body += str[pos];
642                    return {
643                        originLength: body.length,
644                        body: body
645                    };
646                } else body += str[i];
647            }
648            throw new Error("Broken JSON string body near " + body);
649        }
650
651        // parse true / false
652        if (str[pos] === "t") {
653            if (str.indexOf("true", pos) === pos) {
654                return {
655                    originLength: "true".length,
656                    body: "true"
657                };
658            }
659            throw new Error("Broken JSON boolean body near " + str.substr(0, pos + 10));
660        }
661        if (str[pos] === "f") {
662            if (str.indexOf("f", pos) === pos) {
663                return {
664                    originLength: "false".length,
665                    body: "false"
666                };
667            }
668            throw new Error("Broken JSON boolean body near " + str.substr(0, pos + 10));
669        }
670
671        // parse null
672        if (str[pos] === "n") {
673            if (str.indexOf("null", pos) === pos) {
674                return {
675                    originLength: "null".length,
676                    body: "null"
677                };
678            }
679            throw new Error("Broken JSON boolean body near " + str.substr(0, pos + 10));
680        }
681
682        // parse number
683        if (str[pos] === "-" || str[pos] === "+" || str[pos] === "." || (str[pos] >= "0" && str[pos] <= "9")) {
684            var body = "";
685            for (var i = pos; i < str.length; i++) {
686                if (str[i] === "-" || str[i] === "+" || str[i] === "." || (str[i] >= "0" && str[i] <= "9")) {
687                    body += str[i];
688                } else {
689                    return {
690                        originLength: body.length,
691                        body: body
692                    };
693                }
694            }
695            throw new Error("Broken JSON number body near " + body);
696        }
697
698        // parse object
699        if (str[pos] === "{" || str[pos] === "[") {
700            var stack = [str[pos]];
701            var body = str[pos];
702            for (var i = pos + 1; i < str.length; i++) {
703                body += str[i];
704                if (str[i] === "\\") {
705                    if (i + 1 < str.length) body += str[i + 1];
706                    i++;
707                } else if (str[i] === "\"") {
708                    if (stack[stack.length - 1] === "\"") {
709                        stack.pop();
710                    } else if (stack[stack.length - 1] !== "'") {
711                        stack.push(str[i]);
712                    }
713                } else if (str[i] === "'") {
714                    if (stack[stack.length - 1] === "'") {
715                        stack.pop();
716                    } else if (stack[stack.length - 1] !== "\"") {
717                        stack.push(str[i]);
718                    }
719                } else if (stack[stack.length - 1] !== "\"" && stack[stack.length - 1] !== "'") {
720                    if (str[i] === "{") {
721                        stack.push("{");
722                    } else if (str[i] === "}") {
723                        if (stack[stack.length - 1] === "{") {
724                            stack.pop();
725                        } else {
726                            throw new Error("Broken JSON " + (str[pos] === "{" ? "object" : "array") + " body near " + body);
727                        }
728                    } else if (str[i] === "[") {
729                        stack.push("[");
730                    } else if (str[i] === "]") {
731                        if (stack[stack.length - 1] === "[") {
732                            stack.pop();
733                        } else {
734                            throw new Error("Broken JSON " + (str[pos] === "{" ? "object" : "array") + " body near " + body);
735                        }
736                    }
737                }
738                if (!stack.length) {
739                    return {
740                        originLength: i - pos,
741                        body: body
742                    };
743                }
744            }
745            throw new Error("Broken JSON " + (str[pos] === "{" ? "object" : "array") + " body near " + body);
746        }
747        throw new Error("Broken JSON body near " + str.substr((pos - 5 >= 0) ? pos - 5 : 0, 50));
748    }
749
750    function canBeKeyHead(ch) {
751        if (ch[0] === "\\") return false;
752        if ((ch[0] >= 'a' && ch[0] <= 'z') || (ch[0] >= 'A' && ch[0] <= 'Z') || ch[0] === '_') return true;
753        if (ch[0] >= '0' && ch[0] <= '9') return true;
754        if (ch[0] === '$') return true;
755        if (ch.charCodeAt(0) > 255) return true;
756        return false;
757    }
758
759    function isBlankChar(ch) {
760        return ch === " " || ch === "\n" || ch === "\t";
761    }
762
763    function parse(str) {
764        str = str.trim();
765        if (!str.length) throw new Error("Broken JSON object.");
766        var result = "";
767
768        /*
769         * the mistake ','
770         */
771        while (str && str[0] === ",") {
772            str = str.substr(1);
773        }
774
775        /*
776         * string
777         */
778        if (str[0] === "\"" || str[0] === "'") {
779            if (str[str.length - 1] !== str[0]) {
780                throw new Error("Invalid string JSON object.");
781            }
782
783            var body = "\"";
784            for (var i = 1; i < str.length; i++) {
785                if (str[i] === "\\") {
786                    if (str[i + 1] === "'") {
787                        body += str[i + 1]
788                    } else {
789                        body += str[i];
790                        body += str[i + 1];
791                    }
792                    i++;
793                } else if (str[i] === str[0]) {
794                    body += "\"";
795                    return body
796                } else if (str[i] === "\"") {
797                    body += "\\\""
798                } else body += str[i];
799            }
800            throw new Error("Invalid string JSON object.");
801        }
802
803        /*
804         * boolean
805         */
806        if (str === "true" || str === "false") {
807            return str;
808        }
809
810        /*
811         * null
812         */
813        if (str === "null") {
814            return "null";
815        }
816
817        /*
818         * number
819         */
820        var num = parseFloat(str);
821        if (!isNaN(num)) {
822            return num.toString();
823        }
824
825        /*
826         * object
827         */
828        if (str[0] === "{") {
829            var type = "needKey";
830            var result = "{";
831
832            for (var i = 1; i < str.length; i++) {
833                if (isBlankChar(str[i])) {
834                    continue;
835                } else if (type === "needKey" && (str[i] === "\"" || str[i] === "'")) {
836                    var key = parseKey(str, i + 1, str[i]);
837                    result += "\"" + key + "\"";
838                    i += key.length;
839                    i += 1;
840                    type = "afterKey";
841                } else if (type === "needKey" && canBeKeyHead(str[i])) {
842                    var key = parseKey(str, i);
843                    result += "\"";
844                    result += key;
845                    result += "\"";
846                    i += key.length - 1;
847                    type = "afterKey";
848                } else if (type === "afterKey" && str[i] === ":") {
849                    result += ":";
850                    type = ":";
851                } else if (type === ":") {
852                    var body = getBody(str, i);
853
854                    i = i + body.originLength - 1;
855                    result += parse(body.body);
856
857                    type = "afterBody";
858                } else if (type === "afterBody" || type === "needKey") {
859                    var last = i;
860                    while (str[last] === "," || isBlankChar(str[last])) {
861                        last++;
862                    }
863                    if (str[last] === "}" && last === str.length - 1) {
864                        while (result[result.length - 1] === ",") {
865                            result = result.substr(0, result.length - 1);
866                        }
867                        result += "}";
868                        return result;
869                    } else if (last !== i && result !== "{") {
870                        result += ",";
871                        type = "needKey";
872                        i = last - 1;
873                    }
874                }
875            }
876            throw new Error("Broken JSON object near " + result);
877        }
878
879        /*
880         * array
881         */
882        if (str[0] === "[") {
883            var result = "[";
884            var type = "needBody";
885            for (var i = 1; i < str.length; i++) {
886                if (" " === str[i] || "\n" === str[i] || "\t" === str[i]) {
887                    continue;
888                } else if (type === "needBody") {
889                    if (str[i] === ",") {
890                        result += "null,";
891                        continue;
892                    }
893                    if (str[i] === "]" && i === str.length - 1) {
894                        if (result[result.length - 1] === ",") result = result.substr(0, result.length - 1);
895                        result += "]";
896                        return result;
897                    }
898
899                    var body = getBody(str, i);
900
901                    i = i + body.originLength - 1;
902                    result += parse(body.body);
903
904                    type = "afterBody";
905                } else if (type === "afterBody") {
906                    if (str[i] === ",") {
907                        result += ",";
908                        type = "needBody";
909
910                        // deal with mistake ","
911                        while (str[i + 1] === "," || isBlankChar(str[i + 1])) {
912                            if (str[i + 1] === ",") result += "null,";
913                            i++;
914                        }
915                    } else if (str[i] === "]" && i === str.length - 1) {
916                        result += "]";
917                        return result;
918                    }
919                }
920            }
921            throw new Error("Broken JSON array near " + result);
922        }
923    }
924
925    // Global function
926    window.ocJSON = function(json) {
927        var jsonString = parse(json);
928        return JSON.parse(jsonString);
929    };
930
931}(window);

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.