1+ {{/*
2+ Hugo Shortcode to solve for the vertex, focus, and directrix of a parabola.
3+ It takes the coefficients a, b, and c for the equation y = ax^2 + bx + c.
4+
5+ This version uses pure JavaScript for calculations, no external library needed.
6+
7+ Usage:
8+ {{< parabola _solver_js a ="1 " b ="-4 " c ="5 " > }}
9+ This solves y = x^2 - 4x + 5.
10+ */}}
11+
12+ < style >
13+ /* Scoped CSS for the parabola solver (same as before for consistency) */
14+ .parabola-solver-container {
15+ max-width : 650px ;
16+ margin : 2rem auto;
17+ padding : 2rem ;
18+ background-color : # fcfcfc ;
19+ border : 1px solid # e0e0e0 ;
20+ border-radius : 12px ;
21+ box-shadow : 0 6px 20px rgba (0 , 0 , 0 , 0.08 );
22+ font-family : 'Segoe UI' , Tahoma, Geneva, Verdana, sans-serif;
23+ color : # 333 ;
24+ }
25+
26+ .parabola-solver-container h3 {
27+ text-align : center;
28+ color : # 0056b3 ;
29+ margin-bottom : 1.5rem ;
30+ font-size : 1.8rem ;
31+ }
32+
33+ .equation-display {
34+ font-family : 'Times New Roman' , Times, serif;
35+ font-style : italic;
36+ font-size : 1.8rem ;
37+ text-align : center;
38+ margin-bottom : 2rem ;
39+ color : # 1a1a1a ;
40+ background-color : # f0f8ff ;
41+ padding : 1rem ;
42+ border-radius : 8px ;
43+ border : 1px dashed # cce5ff ;
44+ }
45+
46+ .input-grid {
47+ display : grid;
48+ grid-template-columns : repeat (3 , 1fr );
49+ gap : 1.5rem ;
50+ justify-content : center;
51+ margin-bottom : 2rem ;
52+ }
53+
54+ .input-group {
55+ display : flex;
56+ flex-direction : column;
57+ align-items : center;
58+ }
59+
60+ .input-group label {
61+ font-weight : 600 ;
62+ margin-bottom : 0.5rem ;
63+ color : # 555 ;
64+ }
65+
66+ .input-group input [type = "number" ] {
67+ width : 100% ;
68+ max-width : 120px ;
69+ padding : 0.8rem ;
70+ border : 1px solid # ccc ;
71+ border-radius : 6px ;
72+ font-size : 1.1rem ;
73+ text-align : center;
74+ transition : border-color 0.3s ease-in-out, box-shadow 0.3s ease-in-out;
75+ }
76+
77+ .input-group input : focus {
78+ outline : none;
79+ border-color : # 007bff ;
80+ box-shadow : 0 0 8px rgba (0 , 123 , 255 , 0.4 );
81+ }
82+
83+ .solver-button-container {
84+ text-align : center;
85+ }
86+
87+ .solver-button {
88+ padding : 1rem 2.5rem ;
89+ background-color : # 007bff ;
90+ color : # fff ;
91+ border : none;
92+ border-radius : 8px ;
93+ font-size : 1.1rem ;
94+ font-weight : bold;
95+ cursor : pointer;
96+ transition : background-color 0.3s ease-in-out, transform 0.1s ease-in-out;
97+ }
98+
99+ .solver-button : hover {
100+ background-color : # 0056b3 ;
101+ transform : translateY (-2px );
102+ }
103+
104+ .solver-result {
105+ margin-top : 2.5rem ;
106+ padding : 1.8rem ;
107+ background-color : # eaf6ff ;
108+ border-radius : 10px ;
109+ border : 1px solid # b3d9ff ;
110+ line-height : 1.8 ;
111+ font-size : 1.1rem ;
112+ color : # 1a2a3a ;
113+ }
114+
115+ .result-item {
116+ margin-bottom : 0.8rem ;
117+ }
118+
119+ .result-label {
120+ font-weight : 700 ;
121+ color : # 0056b3 ;
122+ }
123+
124+ .result-value {
125+ font-family : 'Courier New' , Courier, monospace;
126+ background-color : # fff ;
127+ padding : 0.2em 0.5em ;
128+ border-radius : 4px ;
129+ border : 1px solid # d4e3f1 ;
130+ }
131+
132+ .solver-error {
133+ color : # dc3545 ;
134+ font-weight : bold;
135+ }
136+
137+ /* Dark mode support */
138+ html : is (.dark ) {
139+ .parabola-solver-container {
140+ background-color : # 181c24 ;
141+ border-color : # 232a36 ;
142+ color : # e0e6ef ;
143+ box-shadow : 0 6px 20px rgba (0 , 0 , 0 , 0.32 );
144+ }
145+
146+ .parabola-solver-container h3 {
147+ color : # 7bb6ff ;
148+ }
149+
150+ .equation-display {
151+ background-color : # 232a36 ;
152+ color : # e0e6ef ;
153+ border-color : # 35507a ;
154+ }
155+
156+ .input-group label {
157+ color : # b3c7e6 ;
158+ }
159+
160+ .input-group input [type = "number" ] {
161+ background-color : # 232a36 ;
162+ color : # e0e6ef ;
163+ border-color : # 35507a ;
164+ }
165+
166+ .input-group input : focus {
167+ border-color : # 339cff ;
168+ box-shadow : 0 0 8px rgba (51 , 156 , 255 , 0.4 );
169+ }
170+
171+ .solver-button {
172+ background-color : # 339cff ;
173+ color : # fff ;
174+ }
175+
176+ .solver-button : hover {
177+ background-color : # 1976d2 ;
178+ }
179+
180+ .solver-result {
181+ background-color : # 1a273a ;
182+ border-color : # 35507a ;
183+ color : # e0e6ef ;
184+ }
185+
186+ .result-label {
187+ color : # 7bb6ff ;
188+ }
189+
190+ .result-value {
191+ background-color : # 232a36 ;
192+ color : # e0e6ef ;
193+ border-color : # 35507a ;
194+ }
195+
196+ .solver-error {
197+ color : # ff6b6b ;
198+ }
199+ }
200+
201+ @media (max-width : 600px ) {
202+ .parabola-solver-container {
203+ padding : 1rem ;
204+ max-width : 98vw ;
205+ }
206+ .equation-display {
207+ font-size : 1.2rem ;
208+ padding : 0.6rem ;
209+ }
210+ .input-grid {
211+ grid-template-columns : 1fr ;
212+ gap : 1rem ;
213+ }
214+ .solver-button {
215+ width : 100% ;
216+ padding : 0.8rem 0 ;
217+ font-size : 1rem ;
218+ }
219+ .solver-result {
220+ padding : 1rem ;
221+ font-size : 1rem ;
222+ }
223+ }
224+ </ style >
225+
226+ < div class ="parabola-solver-container ">
227+ < h3 > Parabola Properties Calculator (JS Only)</ h3 >
228+
229+ < div class ="equation-display ">
230+ y = < span id ="a-display-{{ .Ordinal }} "> a</ span > x² + < span id ="b-display-{{ .Ordinal }} "> b</ span > x + < span id ="c-display-{{ .Ordinal }} "> c</ span >
231+ </ div >
232+
233+ < div class ="input-grid ">
234+ < div class ="input-group ">
235+ < label for ="a-input-{{ .Ordinal }} "> a:</ label >
236+ < input type ="number " id ="a-input-{{ .Ordinal }} " value ="{{ .Get "a " | default "1" }}" oninput="updateParabolaDisplayJS('a', this.value, '{{ .Ordinal }}') ">
237+ </ div >
238+ < div class ="input-group ">
239+ < label for ="b-input-{{ .Ordinal }} "> b:</ label >
240+ < input type ="number " id ="b-input-{{ .Ordinal }} " value ="{{ .Get "b " | default "0" }}" oninput="updateParabolaDisplayJS('b', this.value, '{{ .Ordinal }}') ">
241+ </ div >
242+ < div class ="input-group ">
243+ < label for ="c-input-{{ .Ordinal }} "> c:</ label >
244+ < input type ="number " id ="c-input-{{ .Ordinal }} " value ="{{ .Get "c " | default "0" }}" oninput="updateParabolaDisplayJS('c', this.value, '{{ .Ordinal }}') ">
245+ </ div >
246+ </ div >
247+
248+ < div class ="solver-button-container ">
249+ < button class ="solver-button " onclick ="calculateParabolaPropertiesJS('{{ .Ordinal }}') "> Calculate Properties</ button >
250+ </ div >
251+
252+ < div class ="solver-result " id ="results-output-{{ .Ordinal }} ">
253+ Results will appear here.
254+ </ div >
255+ </ div >
256+
257+ < script >
258+ // Pure JavaScript functions for calculations and display updates.
259+ // Use `window.` to make them globally accessible from `onclick` and `oninput`.
260+
261+ /**
262+ * Updates the displayed coefficient in the equation.
263+ * @param {string } param The coefficient name ('a', 'b', or 'c').
264+ * @param {string } value The new value from the input.
265+ * @param {string } ordinal The unique ID part for the shortcode instance.
266+ */
267+ window . updateParabolaDisplayJS = function ( param , value , ordinal ) {
268+ const displayElement = document . getElementById ( param + '-display-' + ordinal ) ;
269+ if ( displayElement ) {
270+ displayElement . textContent = value ;
271+ }
272+ }
273+
274+ /**
275+ * Formats a number to a fixed number of decimal places.
276+ * @param {number } num The number to format.
277+ * @param {number } precision The number of decimal places.
278+ * @returns {string } The formatted number string.
279+ */
280+ function formatNumber ( num , precision = 6 ) {
281+ if ( typeof num !== 'number' || isNaN ( num ) ) {
282+ return 'N/A' ;
283+ }
284+ // Use toFixed to control decimal places.
285+ return num . toFixed ( precision ) ;
286+ }
287+
288+ /**
289+ * Calculates the parabola properties (vertex, focus, directrix).
290+ * @param {string } ordinal The unique ID part for the shortcode instance.
291+ */
292+ window . calculateParabolaPropertiesJS = function ( ordinal ) {
293+ const aInput = document . getElementById ( 'a-input-' + ordinal ) ;
294+ const bInput = document . getElementById ( 'b-input-' + ordinal ) ;
295+ const cInput = document . getElementById ( 'c-input-' + ordinal ) ;
296+ const resultsOutput = document . getElementById ( 'results-output-' + ordinal ) ;
297+
298+ const a = parseFloat ( aInput . value ) ;
299+ const b = parseFloat ( bInput . value ) ;
300+ const c = parseFloat ( cInput . value ) ;
301+
302+ try {
303+ if ( isNaN ( a ) || isNaN ( b ) || isNaN ( c ) ) {
304+ throw new Error ( "Please enter valid numbers for a, b, and c." ) ;
305+ }
306+
307+ if ( a === 0 ) {
308+ resultsOutput . innerHTML = '<span class="solver-error">Error: "a" cannot be 0. This is a linear equation, not a parabola.</span>' ;
309+ return ;
310+ }
311+
312+ // --- Calculation Formulas using standard JavaScript ---
313+ // Vertex x-coordinate: -b / 2a
314+ const vertexX = - b / ( 2 * a ) ;
315+
316+ // Vertex y-coordinate: plug vertexX back into the equation y = ax^2 + bx + c
317+ const vertexY = ( a * Math . pow ( vertexX , 2 ) ) + ( b * vertexX ) + c ;
318+
319+ // Focus y-coordinate: (4ac - b^2 + 1) / 4a
320+ const focusY = ( 4 * a * c - Math . pow ( b , 2 ) + 1 ) / ( 4 * a ) ;
321+
322+ // Directrix y-value: y = (4ac - b^2 - 1) / 4a
323+ const directrixY = ( 4 * a * c - Math . pow ( b , 2 ) - 1 ) / ( 4 * a ) ;
324+
325+ // Format the results for display
326+ const vertexXFormatted = formatNumber ( vertexX ) ;
327+ const vertexYFormatted = formatNumber ( vertexY ) ;
328+ const focusYFormatted = formatNumber ( focusY ) ;
329+ const directrixYFormatted = formatNumber ( directrixY ) ;
330+
331+ const resultHTML = `
332+ <div class="result-item"><span class="result-label">Vertex:</span> <span class="result-value">(${ vertexXFormatted } , ${ vertexYFormatted } )</span></div>
333+ <div class="result-item"><span class="result-label">Focus:</span> <span class="result-value">(${ vertexXFormatted } , ${ focusYFormatted } )</span></div>
334+ <div class="result-item"><span class="result-label">Directrix:</span> <span class="result-value">y = ${ directrixYFormatted } </span></div>
335+ ` ;
336+ resultsOutput . innerHTML = resultHTML ;
337+
338+ } catch ( error ) {
339+ resultsOutput . innerHTML = `<span class="solver-error">Error: ${ error . message } </span>` ;
340+ console . error ( "Parabola solver error:" , error ) ;
341+ }
342+ }
343+
344+ // Initial update of the display when the shortcode loads
345+ document . addEventListener ( 'DOMContentLoaded' , ( ) => {
346+ // Use a timeout to ensure all elements are loaded before updating.
347+ setTimeout ( ( ) => {
348+ const ordinal = '{{ .Ordinal }}' ;
349+ updateParabolaDisplayJS ( 'a' , document . getElementById ( 'a-input-' + ordinal ) . value , ordinal ) ;
350+ updateParabolaDisplayJS ( 'b' , document . getElementById ( 'b-input-' + ordinal ) . value , ordinal ) ;
351+ updateParabolaDisplayJS ( 'c' , document . getElementById ( 'c-input-' + ordinal ) . value , ordinal ) ;
352+ } , 100 ) ;
353+ } ) ;
354+ </ script >
0 commit comments