PageSourceSearch

https://sparkletour.ca/js/photo-features.js?v=1790737962

js sparkletour.ca collected 2026-09-30 03:12:57 UTC 9,849 bytes, 267 lines download raw bytes

1/**
2 * Photo and Features Handler for SparkleTour
3 * Handles photo preview and feature checkbox functionality
4 */
5
6(function($) {
7    'use strict';
8    
9    var features = {
10        popular: [],
11        categories: {
12            lighting: [],
13            elements: [],
14            special: [],
15            accessibility: []
16        }
17    };
18    
19    /**
20     * Initialize photo and features functionality
21     */
22    function init() {
23        loadFeatures();
24        setupPhotoPreview();
25        setupFeatureToggle();
26        setupFeatureChipInteraction();
27    }
28    
29    /**
30     * Load features from API
31     */
32    function loadFeatures() {
33        $.ajax({
34            url: '/api/features.php',
35            type: 'GET',
36            dataType: 'json',
37            success: function(response) {
38                if (response.success) {
39                    features = response.data;
40                    renderFeatures();
41                }
42            },
43            error: function(xhr, status, error) {
44                console.error('[PhotoFeatures] Error loading features:', error);
45            }
46        });
47    }
48    
49    /**
50     * Render feature checkboxes
51     */
52    function renderFeatures() {
53        // Render popular features
54        var popularHtml = '';
55        features.popular.forEach(function(feature) {
56            popularHtml += createFeatureCheckbox(feature);
57        });
58        $('#popular-features').html(popularHtml);
59        
60        // Render category features
61        $.each(features.categories, function(category, categoryFeatures) {
62            var categoryHtml = '';
63            categoryFeatures.forEach(function(feature) {
64                categoryHtml += createFeatureCheckbox(feature);
65            });
66            $('#' + category + '-features .feature-checkboxes').html(categoryHtml);
67        });
68    }
69    
70    /**
71     * Create feature checkbox HTML
72     * Uses chip-style buttons for better mobile experience
73     */
74    function createFeatureCheckbox(feature) {
75        var icon = feature.icon ? '<i class="fa ' + feature.icon + ' me-1"></i>' : '';
76        return '<div class="feature-chip-wrapper">' +
77               '<input type="checkbox" name="features[]" value="' + feature.slug + '" class="feature-checkbox d-none" id="feature-' + feature.slug + '">' +
78               '<label for="feature-' + feature.slug + '" class="feature-chip-label">' +
79               icon + feature.name +
80               '</label>' +
81               '</div>';
82    }
83    
84    /**
85     * Setup photo preview functionality
86     */
87    function setupPhotoPreview() {
88        // Use event delegation to handle dynamically added buttons
89        $(document).on('click', '#photo-upload-btn', function() {
90            var $fileInput = $('#display_photo');
91            // Remove capture attribute to allow gallery selection
92            $fileInput.removeAttr('capture');
93            $fileInput.click();
94        });
95        
96        // Take Photo button - opens camera directly (mobile only)
97        $(document).on('click', '#photo-camera-btn', function() {
98            var $fileInput = $('#display_photo');
99            // Add capture attribute to force camera
100            $fileInput.attr('capture', 'environment');
101            $fileInput.click();
102        });
103        
104        $('#display_photo').on('change', function(e) {
105            var file = e.target.files[0];
106            
107            if (!file) {
108                $('#photo-preview').html('');
109                $('#photo-filename').html('');
110                $('#photo-upload-btn').html('<i class="bi bi-image me-2"></i>Choose Photo');
111                $('#photo-camera-btn').html('<i class="bi bi-camera-fill me-2"></i>Take Photo');
112                return;
113            }
114            
115            // Validate file type
116            var validTypes = ['image/jpeg', 'image/png', 'image/webp'];
117            if (validTypes.indexOf(file.type) === -1) {
118                $('#photo-preview').html('<div class="alert alert-danger">Invalid file type. Please select a JPEG, PNG, or WebP image.</div>');
119                $('#photo-filename').html('');
120                $('#photo-upload-btn').html('<i class="bi bi-image me-2"></i>Choose Photo');
121                $('#photo-camera-btn').html('<i class="bi bi-camera-fill me-2"></i>Take Photo');
122                $(this).val('');
123                return;
124            }
125            
126            // Validate file size (5MB)
127            if (file.size > 5242880) {
128                $('#photo-preview').html('<div class="alert alert-danger">File size exceeds 5MB limit.</div>');
129                $('#photo-filename').html('');
130                $('#photo-upload-btn').html('<i class="bi bi-image me-2"></i>Choose Photo');
131                $('#photo-camera-btn').html('<i class="bi bi-camera-fill me-2"></i>Take Photo');
132                $(this).val('');
133                return;
134            }
135            
136            // Update button text and show filename
137            $('#photo-upload-btn').html('<i class="bi bi-check-circle-fill me-2"></i>Photo Selected');
138            $('#photo-filename').html('<i class="bi bi-file-image me-1"></i>' + file.name + ' (' + formatFileSize(file.size) + ')');
139            
140            // Show preview
141            var reader = new FileReader();
142            reader.onload = function(e) {
143                var previewHtml = '<div class="photo-preview-container text-center">' +
144                                '<img src="' + e.target.result + '" class="img-thumbnail" style="max-width: 100%; max-height: 120px; border-radius: 6px;">' +
145                                '<button type="button" class="btn btn-sm btn-outline-danger mt-1" id="remove-photo">' +
146                                '<i class="bi bi-trash me-1"></i>Remove' +
147                                '</button>' +
148                                '</div>';
149                $('#photo-preview').html(previewHtml);
150                
151                // Setup remove button
152                $('#remove-photo').on('click', function() {
153                    $('#display_photo').val('');
154                    $('#photo-preview').html('');
155                    $('#photo-filename').html('');
156                    $('#photo-upload-btn').html('<i class="bi bi-image me-2"></i>Choose Photo');
157                    $('#photo-camera-btn').html('<i class="bi bi-camera-fill me-2"></i>Take Photo');
158                });
159            };
160            reader.readAsDataURL(file);
161        });
162    }
163    
164    /**
165     * Setup feature toggle functionality
166     */
167    function setupFeatureToggle() {
168        $('#toggle-more-features').on('click', function(e) {
169            e.preventDefault();
170            var $additionalFeatures = $('#additional-features');
171            var $icon = $(this).find('i');
172            
173            if ($additionalFeatures.is(':visible')) {
174                $additionalFeatures.slideUp();
175                $icon.removeClass('fa-chevron-up').addClass('fa-chevron-down');
176                $(this).html('Show More Features <i class="fa fa-chevron-down"></i>');
177            } else {
178                $additionalFeatures.slideDown();
179                $icon.removeClass('fa-chevron-down').addClass('fa-chevron-up');
180                $(this).html('Show Fewer Features <i class="fa fa-chevron-up"></i>');
181            }
182        });
183    }
184    
185    /**
186     * Setup feature chip interaction
187     * Handles visual state changes for chip-style feature buttons
188     */
189    function setupFeatureChipInteraction() {
190        // Use event delegation for dynamically added chips
191        $(document).on('click', '.feature-chip-label', function() {
192            var $label = $(this);
193            var $checkbox = $label.prev('.feature-checkbox');
194            
195            // Toggle the checkbox state
196            $checkbox.prop('checked', !$checkbox.prop('checked'));
197            
198            // Update visual state
199            updateChipVisualState($label, $checkbox.prop('checked'));
200        });
201        
202        // Initialize visual state for any pre-checked boxes
203        $('.feature-checkbox').each(function() {
204            var $checkbox = $(this);
205            var $label = $checkbox.next('.feature-chip-label');
206            if ($checkbox.prop('checked')) {
207                updateChipVisualState($label, true);
208            }
209        });
210    }
211    
212    /**
213     * Update chip visual state
214     */
215    function updateChipVisualState($label, isChecked) {
216        if (isChecked) {
217            $label.css({
218                'background': '#c41e3a',
219                'border': '2px solid #c41e3a',
220                'color': 'white',
221                'font-weight': '600'
222            });
223        } else {
224            $label.css({
225                'background': 'white',
226                'border': '2px solid #dee2e6',
227                'color': '#212529',
228                'font-weight': 'normal'
229            });
230        }
231    }
232    
233    /**
234     * Format file size for display
235     */
236    function formatFileSize(bytes) {
237        if (bytes === 0) return '0 Bytes';
238        var k = 1024;
239        var sizes = ['Bytes', 'KB', 'MB'];
240        var i = Math.floor(Math.log(bytes) / Math.log(k));
241        return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i];
242    }
243    
244    /**
245     * Get selected features
246     */
247    function getSelectedFeatures() {
248        var selected = [];
249        $('.feature-checkbox:checked').each(function() {
250            selected.push($(this).val());
251        });
252        return selected;
253    }
254    
255    // Initialize on document ready
256    $(document).ready(function() {
257        init();
258    });
259    
260    // Expose public methods
261    window.PhotoFeatures = {
262        getSelectedFeatures: getSelectedFeatures,
263        init: init,  // Expose init so it can be called when flyup opens
264        renderFeatures: renderFeatures  // Expose render so features can be re-rendered
265    };
266    
267})(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.