PageSourceSearch

https://khasanredjaboev.com/modules/system/assets/js/framework.js

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