PageSourceSearch

https://beograd-kafana.ru/wp-content/plugins/redi-restaurant-reservation/js/redi-custom-fields.js

js beograd-kafana.ru collected 2026-10-04 21:47:19 UTC 7,199 bytes, 189 lines download raw bytes

1class ReDiCustomFields {
2    static instance = null;
3
4    static getInstance() {
5        if (!ReDiCustomFields.instance) {
6            ReDiCustomFields.instance = new ReDiCustomFields();
7        }
8        return ReDiCustomFields.instance;
9    }
10
11    constructor() {
12        this.container = jQuery('#custom_fields_container');
13        this.loadedFields = new Map(); // Track loaded fields by placeId
14    }
15
16    isLoaded(placeId) {
17        return this.loadedFields.has(placeId);
18    }
19
20    renderFields(customFields, placeId) {
21        // If fields are already loaded for this place, skip
22        if (this.isLoaded(placeId)) {
23            return;
24        }
25
26        // API can return an empty object or wrapped payload when there are no custom fields.
27        const normalizedFields = Array.isArray(customFields)
28            ? customFields
29            : Array.isArray(customFields?.CustomFields)
30                ? customFields.CustomFields
31                : [];
32
33        if (!normalizedFields.length) {
34            this.loadedFields.set(placeId, true);
35            return;
36        }
37
38        // Sort custom fields by DisplayOrder
39        normalizedFields.sort((a, b) => {
40            return (a.DisplayOrder || 0) - (b.DisplayOrder || 0);
41        });
42        
43        // Process each custom field
44        normalizedFields.forEach(field => {
45            // Generate HTML for this field
46            const fieldHtml = this.renderField(field);
47            
48            // Find all elements with data-display-order
49            const orderedElements = jQuery('[data-display-order]');
50            
51            // Find the element with the closest lower or equal display-order
52            let targetElement = null;
53            let targetOrder = -1;
54            
55            orderedElements.each(function() {
56                const elementOrder = parseInt(jQuery(this).data('display-order'));
57                if (elementOrder <= field.DisplayOrder && elementOrder > targetOrder) {
58                    targetElement = jQuery(this);
59                    targetOrder = elementOrder;
60                }
61            });
62            
63            if (targetElement) {
64                // Insert after the target element
65                targetElement.after(fieldHtml);
66            } else {
67                // If no target found, append to container
68                this.container.append(fieldHtml);
69            }
70        });
71
72        // Mark these fields as loaded for this place
73        this.loadedFields.set(placeId, true);
74    }
75
76    renderField(field) {
77        let html = `<div data-display-order="${field.DisplayOrder}">`;
78
79
80         if (
81            (field.Type === 'dropdown' || field.Type === 'options') &&
82            (!field.Values || field.Values.trim() === '')
83        ) {
84            console.warn(`Skipping field "${field.Text}" (ID: ${field.Id}) — no values defined. Defined values are required for dropdown and options types. Go to Settings > Custom Fields to define values.`);
85            return ''; // Don't render anything
86        }
87
88        switch (field.Type) {
89            case 'options':
90                html += `<label for="field_${field.Id}">${field.Text}`;
91                if (field.Required) {
92                    html += `<span class="redi_required"> *</span>
93                        <input type="hidden" 
94                            id="field_${field.Id}_message" 
95                            value="${field.Message || 'Custom field is required'}">`;
96                }
97                html += '</label>';
98                html += this.renderRadioField(field);
99                break;
100            case 'dropdown':
101                html += `<label for="field_${field.Id}">${field.Text}`;
102                if (field.Required) {
103                    html += `<span class="redi_required"> *</span>
104                        <input type="hidden" 
105                            id="field_${field.Id}_message" 
106                            value="${field.Message || 'Custom field is required'}">`;
107                }
108                html += '</label>';
109                html += this.renderDropdownField(field);
110                break;
111            case 'newsletter':
112            case 'reminder':
113            case 'allowsms':
114            case 'checkbox':
115            case 'allowwhatsapp':
116            case 'gdpr':
117                html += this.renderCheckboxField(field); // label will be inside this
118                break;
119            default:
120                html += `<label for="field_${field.Id}">${field.Text}`;
121                if (field.Required) {
122                    html += `<span class="redi_required"> *</span>
123                        <input type="hidden" 
124                            id="field_${field.Id}_message" 
125                            value="${field.Message || 'Custom field is required'}">`;
126                }
127                html += '</label>';
128                html += this.renderTextField(field);
129        }
130
131        html += '</div>';
132        return html;
133    }
134
135    renderTextField(field) {
136        return `<input type="text" 
137            value="" 
138            id="field_${field.Id}" 
139            name="field_${field.Id}"
140            ${field.Required ? 'class="field_required"' : ''}>`;
141    }
142
143    renderCheckboxField(field) {
144        return `
145            <div class="redi-custom-checkbox">
146                <label for="field_${field.Id}" style="display:block;">
147                    ${field.Text}
148                    ${field.Required ? '<span class="redi_required"> *</span>' : ''}
149                </label>
150                <input type="checkbox" 
151                    id="field_${field.Id}" 
152                    name="field_${field.Id}"
153                    ${field.Required ? 'class="field_required"' : ''}
154                    ${field.Default === 'True' ? 'checked' : ''}>
155                ${field.Required ? `<input type="hidden" id="field_${field.Id}_message" value="${field.Message || 'Custom field is required'}">` : ''}
156            </div>
157        `;
158    }
159
160    renderRadioField(field) {
161        return field.Values.split(',')
162            .filter(value => value)
163            .map(value => `
164                <input type="radio" 
165                    value="${value}" 
166                    name="field_${field.Id}" 
167                    id="field_${field.Id}_${value}" 
168                    class="redi-radiobutton${field.Required ? ' field_required' : ''}">
169                <label class="redi-radiobutton-label" 
170                    for="field_${field.Id}_${value}">${value}</label>
171                <br/>`)
172            .join('');
173    }
174
175    renderDropdownField(field) {
176        const options = field.Values.split(',')
177            .filter(value => value)
178            .map(value => `<option value="${value}">${value}</option>`)
179            .join('');
180
181        return `<select 
182            id="field_${field.Id}" 
183            name="field_${field.Id}"
184            ${field.Required ? 'class="field_required"' : ''}>
185            <option value="">Select</option>
186            ${options}
187        </select>`;
188    }
189} 

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.