PageSourceSearch

https://coastalcameranetwork.com/wp-content/plugins/ccnai-coastal-…/js/board-widget.js?ver=1.0.15.1791107953

js coastalcameranetwork.com collected 2026-10-04 09:59:38 UTC 7,050 bytes, 201 lines download raw bytes

1/**
2 * Board Recommendation Widget
3 * Displays surf board recommendations with quality rating
4 */
5
6class BoardWidget {
7    constructor(containerId, options = {}) {
8        this.container = document.getElementById(containerId);
9        if (!this.container) {
10            console.error(`Container ${containerId} not found`);
11            return;
12        }
13
14        this.options = {
15            recommendation: options.recommendation || null,
16            ...options
17        };
18
19        this.render();
20    }
21
22    /**
23     * Render the board recommendation widget
24     */
25    render() {
26        const { recommendation } = this.options;
27
28        if (!recommendation) {
29            this.container.innerHTML = '<p class="no-recommendation">No recommendation available</p>';
30            return;
31        }
32
33        const {
34            board_type,
35            board_length_display,
36            skill_level,
37            wave_quality,
38            quality_stars
39        } = recommendation;
40
41        const icon = this.getBoardIcon(board_type);
42        const qualityClass = `quality-${wave_quality.toLowerCase()}`;
43        const stars = this.renderStars(quality_stars);
44
45        this.container.innerHTML = `
46            <div class="ccnai-board-widget ${qualityClass}">
47                <div class="board-header">
48                    <h3>Recommended Board</h3>
49                </div>
50
51                <div class="board-content">
52                    <div class="board-icon">
53                        ${icon}
54                    </div>
55
56                    <div class="board-details">
57                        <div class="board-type">${this.formatBoardType(board_type)}</div>
58                        <div class="board-length">${board_length_display}</div>
59                        <div class="skill-level">
60                            <span class="label">Skill:</span>
61                            <span class="value">${this.formatSkillLevel(skill_level)}</span>
62                        </div>
63                    </div>
64                </div>
65
66                <div class="board-footer">
67                    <div class="quality-rating">
68                        <span class="quality-label">${this.formatQuality(wave_quality)}</span>
69                        <div class="stars">${stars}</div>
70                    </div>
71                </div>
72            </div>
73        `;
74    }
75
76    /**
77     * Get SVG icon for board type
78     */
79    getBoardIcon(boardType) {
80        const icons = {
81            'shortboard': `
82                <svg viewBox="0 0 60 200" class="board-svg shortboard">
83                    <path d="M 30 10 Q 20 20, 25 50 L 25 150 Q 25 180, 30 190 Q 35 180, 35 150 L 35 50 Q 40 20, 30 10 Z"
84                        fill="currentColor" stroke="#fff" stroke-width="2"/>
85                    <line x1="30" y1="100" x2="30" y2="120" stroke="#fff" stroke-width="1.5"/>
86                </svg>
87            `,
88            'longboard': `
89                <svg viewBox="0 0 80 200" class="board-svg longboard">
90                    <path d="M 40 5 Q 25 10, 30 40 L 30 160 Q 30 190, 40 195 Q 50 190, 50 160 L 50 40 Q 55 10, 40 5 Z"
91                        fill="currentColor" stroke="#fff" stroke-width="2"/>
92                    <line x1="40" y1="80" x2="40" y2="100" stroke="#fff" stroke-width="1.5"/>
93                    <line x1="40" y1="110" x2="40" y2="130" stroke="#fff" stroke-width="1.5"/>
94                </svg>
95            `,
96            'fish': `
97                <svg viewBox="0 0 70 180" class="board-svg fish">
98                    <path d="M 35 10 Q 20 15, 25 45 L 25 140 Q 20 165, 30 175 Q 35 170, 35 165 Q 35 170, 40 175 Q 50 165, 45 140 L 45 45 Q 50 15, 35 10 Z"
99                        fill="currentColor" stroke="#fff" stroke-width="2"/>
100                </svg>
101            `,
102            'funboard': `
103                <svg viewBox="0 0 75 190" class="board-svg funboard">
104                    <path d="M 37.5 8 Q 23 12, 28 42 L 28 150 Q 28 180, 37.5 188 Q 47 180, 47 150 L 47 42 Q 52 12, 37.5 8 Z"
105                        fill="currentColor" stroke="#fff" stroke-width="2"/>
106                    <line x1="37.5" y1="95" x2="37.5" y2="115" stroke="#fff" stroke-width="1.5"/>
107                </svg>
108            `,
109            'gun': `
110                <svg viewBox="0 0 50 220" class="board-svg gun">
111                    <path d="M 25 5 Q 15 8, 20 35 L 20 180 Q 20 210, 25 215 Q 30 210, 30 180 L 30 35 Q 35 8, 25 5 Z"
112                        fill="currentColor" stroke="#fff" stroke-width="2"/>
113                    <line x1="25" y1="110" x2="25" y2="130" stroke="#fff" stroke-width="1.5"/>
114                </svg>
115            `,
116            'bodyboard': `
117                <svg viewBox="0 0 90 120" class="board-svg bodyboard">
118                    <rect x="15" y="10" width="60" height="100" rx="8"
119                        fill="currentColor" stroke="#fff" stroke-width="2"/>
120                    <line x1="45" y1="50" x2="45" y2="70" stroke="#fff" stroke-width="1.5"/>
121                </svg>
122            `
123        };
124
125        return icons[boardType] || icons['shortboard'];
126    }
127
128    /**
129     * Render star rating
130     */
131    renderStars(count) {
132        const stars = [];
133        for (let i = 0; i < 5; i++) {
134            const filled = i < count;
135            stars.push(`
136                <svg class="star ${filled ? 'filled' : ''}" width="20" height="20" viewBox="0 0 20 20">
137                    <path d="M10 1 L12.5 7.5 L19 8.5 L14.5 13 L16 19.5 L10 16 L4 19.5 L5.5 13 L1 8.5 L7.5 7.5 Z"
138                        fill="${filled ? '#fbbf24' : '#e5e7eb'}" stroke="#d1d5db" stroke-width="0.5"/>
139                </svg>
140            `);
141        }
142        return stars.join('');
143    }
144
145    /**
146     * Format board type for display
147     */
148    formatBoardType(type) {
149        const names = {
150            'shortboard': 'Shortboard',
151            'longboard': 'Longboard',
152            'fish': 'Fish',
153            'funboard': 'Funboard',
154            'gun': 'Gun',
155            'bodyboard': 'Bodyboard'
156        };
157        return names[type] || type;
158    }
159
160    /**
161     * Format skill level for display
162     */
163    formatSkillLevel(level) {
164        const names = {
165            'beginner': 'Beginner',
166            'beginner-intermediate': 'Beginner-Intermediate',
167            'intermediate': 'Intermediate',
168            'intermediate-advanced': 'Intermediate-Advanced',
169            'advanced': 'Advanced',
170            'expert': 'Expert'
171        };
172        return names[level] || level;
173    }
174
175    /**
176     * Format quality for display
177     */
178    formatQuality(quality) {
179        const names = {
180            'flat': 'Flat',
181            'poor': 'Poor',
182            'fair': 'Fair',
183            'good': 'Good',
184            'epic': 'Epic'
185        };
186        return names[quality] || quality;
187    }
188
189    /**
190     * Update widget with new recommendation
191     */
192    update(newRecommendation) {
193        this.options.recommendation = newRecommendation;
194        this.render();
195    }
196}
197
198// Export for WordPress
199if (typeof window !== 'undefined') {
200    window.BoardWidget = BoardWidget;
201}

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.