1// ===== checkout_wasm.go =====
2package main
3
4import (
5 "encoding/json"
6 "fmt"
7 "log"
8 "strconv"
9 "strings"
10 "syscall/js"
11)
12
13// set client pk on compile
14var stripePK string
15
16type item struct {
17 ID string `json:"id"`
18 Amount int `json:"amount"`
19 Qty int `json:"quantity"`
20}
21
22var (
23 wasmName string
24 doc = js.Global().Get("document")
25 body = doc.Call("querySelector", "body")
26 bodystring = body.Get("innerHTML").String()
27 cart []item
28)
29
30func main() {
31 ready := make(chan struct{})
32
33 document := js.Global().Get("document")
34 readyState := document.Get("readyState").String()
35 if readyState == "interactive" || readyState == "complete" {
36 log.Println(wasmName+":", "WASM: DOM already fully loaded")
37 close(ready)
38 } else {
39 cb := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
40 log.Println(wasmName+":", "WASM: DOM fully loaded and parsed")
41 close(ready)
42 return nil
43 })
44 defer cb.Release()
45
46 document.Call("addEventListener", "DOMContentLoaded", cb)
47 log.Println(wasmName+":", "WASM: waiting for DOM to load")
48 }
49
50 <-ready
51
52 c := make(chan struct{}, 0)
53 if stripePK == "" {
54 log.Fatal("Stripe PK not found!")
55 }
56 window := js.Global().Get("window")
57 location := window.Get("location")
58 pathname := location.Get("pathname").String()
59
60 switch pathname {
61 case "/complete":
62 completeLogic()
63 default:
64 defaultLogic()
65 }
66 <-c
67}
68
69func defaultLogic() {
70 js.Global().Set("addToCart", js.FuncOf(addUnToCart))
71 js.Global().Set("clearStorage", js.FuncOf(clearAll))
72 js.Global().Set("emptyCart", js.FuncOf(emptyCart))
73 js.Global().Set("updateItemQuantity", js.FuncOf(updateItemQuantity))
74 js.Global().Set("removeFromCart", js.FuncOf(removeFromCart))
75 js.Global().Set("addShippingInfo", js.FuncOf(addShippingInfo))
76 js.Global().Set("goToCheckout", js.FuncOf(goToCheckout))
77 js.Global().Set("cancelCheckout", js.FuncOf(cancelCheckout))
78 js.Global().Set("callUpdateCartDisplay", js.FuncOf(updateCartDisplayWrapper))
79
80 loadCart()
81 updateCartDisplay()
82}
83
84func updateCartDisplayWrapper(this js.Value, args []js.Value) interface{} {
85 updateCartDisplay()
86 return nil
87}
88
89func saveCart() {
90 cartJSON, err := json.Marshal(cart)
91 if err != nil {
92 log.Println(wasmName+":", "Error saving cart:", err)
93 return
94 }
95 js.Global().Get("localStorage").Call("setItem", "cartItems", string(cartJSON))
96 updateCartDisplay()
97}
98
99func addToCart(this js.Value, args []js.Value) any {
100 if len(args) < 2 {
101 return "Error: Missing arguments"
102 }
103 var cartItem item
104 index := -1
105 id := args[0].String()
106 qty := args[2].Int()
107 if qty == 0 {
108 qty = 1
109 }
110 amount := int(args[1].Float()) * qty
111 for i, _ := range cart {
112 if strings.Split(cart[i].ID, "|")[0] == strings.Split(id, "|")[0] {
113 index = i
114 }
115 }
116 if index > -1 {
117 // update shipping
118 if strings.Split(cart[index].ID, "|")[0] == "shipping-to" {
119 cart[index].ID = id
120 cart[index].Qty = 1
121 cart[index].Amount = amount
122 } else {
123 cart[index].Qty = cart[index].Qty + qty
124 cart[index].Amount = cart[index].Amount + amount
125 }
126 } else {
127 cartItem = item{
128 ID: id,
129 Amount: amount,
130 Qty: qty,
131 }
132 cart = append(cart, cartItem)
133 }
134 saveCart()
135 return nil
136}
137
138func addUnToCart(this js.Value, args []js.Value) interface{} {
139 if len(args) < 2 {
140 return "Error: Missing arguments"
141 }
142 id := args[0].String()
143 price := args[1].Float()
144 quantityInput := doc.Call("getElementById", fmt.Sprintf("qty-%s", id))
145 if !quantityInput.Truthy() {
146 log.Println(wasmName+":", "Error: Quantity input not found for item", id)
147 return nil
148 }
149 quantity, err := strconv.Atoi(quantityInput.Get("value").String())
150 if err != nil || quantity < 1 {
151 quantity = 1
152 }
153
154 addToCart(js.Value{}, []js.Value{
155 js.ValueOf(id),
156 js.ValueOf(int(price * 100)),
157 js.ValueOf(quantity),
158 })
159 return nil
160}
161
162func removeFromCart(this js.Value, inputs []js.Value) interface{} {
163 id := inputs[0].String()
164 newCart := []item{}
165 for _, m := range cart {
166 if m.ID != id {
167 newCart = append(newCart, m)
168 }
169 }
170 cart = newCart
171 saveCart()
172 return nil
173}
174
175func loadCart() {
176 storedCart := js.Global().Get("localStorage").Call("getItem", "cartItems")
177 if !storedCart.IsUndefined() && !storedCart.IsNull() {
178 err := json.Unmarshal([]byte(storedCart.String()), &cart)
179 if err != nil {
180 log.Println(`can't unmarshal cart from local storage`)
181 cart = []item{}
182 }
183 }
184}
185
186func emptyCart(this js.Value, inputs []js.Value) interface{} {
187 js.Global().Get("localStorage").Call("removeItem", "cartItems")
188 cart = []item{}
189 updateCartDisplay()
190 return nil
191}
192
193func clearAll(this js.Value, inputs []js.Value) interface{} {
194 js.Global().Get("localStorage").Call("clear")
195 cart = []item{}
196 updateCartDisplay()
197 return nil
198}
199
200func updateCartDisplay() {
201 cartContainer := doc.Call("getElementById", "cart-items")
202 totalPriceElement := doc.Call("getElementById", "total-price")
203 table := cartContainer.Call("querySelector", "table")
204 if table.IsNull() {
205 table = doc.Call("createElement", "table")
206 thead := doc.Call("createElement", "thead")
207 thead.Set("innerHTML", `<tr><th>Item</th><th>Price</th><th>Quantity</th><th>Actions</th></tr>`)
208 table.Call("appendChild", thead)
209 tbody := doc.Call("createElement", "tbody")
210 tbody.Set("id", "cart-tbody")
211 table.Call("appendChild", tbody)
212 cartContainer.Call("appendChild", table)
213 }
214 tbody := doc.Call("getElementById", "cart-tbody")
215 tbody.Set("innerHTML", "")
216
217 total := 0
218 hasShipping := false
219 for _, m := range cart {
220 total += m.Amount
221 row := doc.Call("createElement", "tr")
222
223 row.Set("innerHTML", fmt.Sprintf(`<td>%s</td><td>$%.2f</td><td>%s</td><td><button onclick='removeFromCart("%s")'>Remove</button></td>`,
224 func() string {
225 parts := strings.Split(m.ID, "|")
226 if len(parts) < 8 {
227 return m.ID
228 }
229 hasShipping = true
230 return fmt.Sprintf("%s:<br>%s<br>%s<br>%s, %s %s<br>%s<br>%s", parts[0], parts[1], parts[2], parts[3], parts[4], parts[5], parts[6], parts[7])
231 }(),
232 float64(m.Amount)/100,
233 func() string {
234 if len(strings.Split(m.ID, "|")) == 8 {
235 return ""
236 }
237 return fmt.Sprintf(`<input type='number' value='%d' min='1' onchange='updateItemQuantity("%s", this.value)'>`, m.Qty, m.ID)
238 }(),
239 m.ID,
240 ))
241 tbody.Call("appendChild", row)
242 }
243 totalPriceElement.Set("textContent", fmt.Sprintf("Total: $%.2f", float64(total)/100))
244
245 checkoutbutton := doc.Call("getElementById", "checkout-button")
246 if !checkoutbutton.Truthy() {
247 return
248 }
249
250 if len(cart) > 1 && hasShipping {
251 checkoutbutton.Call("removeAttribute", "disabled")
252 } else {
253 checkoutbutton.Call("setAttribute", "disabled", "true")
254 }
255}
256
257func updateItemQuantity(this js.Value, args []js.Value) interface{} {
258 id := args[0].String()
259 qty, err := strconv.Atoi(args[1].String())
260 if err != nil {
261 log.Println(err)
262 }
263 for i, _ := range cart {
264 if cart[i].ID == id {
265 unitPrice := cart[i].Amount / cart[i].Qty
266 cart[i].Qty = qty
267 cart[i].Amount = unitPrice * qty
268 break
269 }
270 }
271 saveCart()
272 return nil
273}
274
275func addShippingInfo(this js.Value, args []js.Value) interface{} {
276 event := args[0]
277 form := args[1]
278 event.Call("preventDefault")
279 getFormValue := func(name string) string {
280 return form.Call("querySelector", fmt.Sprintf("[name='%s']", name)).Get("value").String()
281 }
282 shippingInfo := fmt.Sprintf("shipping-to|%s|%s|%s|%s|%s|%s|%s",
283 getFormValue("shipping-name"),
284 getFormValue("shipping-address"),
285 getFormValue("shipping-city"),
286 getFormValue("shipping-state"),
287 getFormValue("shipping-zip"),
288 getFormValue("shipping-country"),
289 getFormValue("shipping-phone"),
290 )
291 priceStr := getFormValue("shipping-price")
292 price, err := strconv.ParseFloat(priceStr, 64)
293 if err != nil {
294 log.Println(wasmName+":", "Error: Failed to parse shipping price")
295 price = 0.0
296 }
297
298 addToCart(js.Value{}, []js.Value{
299 js.ValueOf(shippingInfo),
300 js.ValueOf(int(price * 100)),
301 js.ValueOf(1),
302 })
303 return false
304}
305
306var (
307 elements js.Value
308 stripeValue js.Value
309 stripe js.Value
310 checkoutButton = doc.Call("getElementById", "checkout-button")
311 checkoutDiv = doc.Call("getElementById", "checkout-container")
312 checkoutStripe = doc.Call("getElementById", "stripecheckout")
313)
314
315func goToCheckout(this js.Value, args []js.Value) any {
316 if stripeValue.IsUndefined() {
317 log.Println(`js.Global().Get("Stripe")`)
318 stripeValue = js.Global().Get("Stripe")
319 if stripeValue.IsUndefined() {
320 log.Println(`Stripe is undefined, attempting to load Stripe.js`)
321
322 doc := js.Global().Get("document")
323 head := doc.Call("querySelector", "head")
324 script := doc.Call("createElement", "script")
325 script.Set("src", "https://js.stripe.com/v3/")
326 script.Set("defer", true)
327
328 done := make(chan bool)
329 script.Call("addEventListener", "load", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
330 log.Println(wasmName+":", "Stripe.js script has been loaded")
331 done <- true
332 return nil
333 }))
334
335 head.Call("appendChild", script)
336
337 <-done
338
339 stripeValue = js.Global().Get("Stripe")
340 if stripeValue.IsUndefined() {
341 log.Println(wasmName+":", "Failed to load Stripe.js")
342 return nil
343 }
344 }
345 }
346
347 log.Println(wasmName+":", "Stripe.js loaded successfully")
348
349 if stripe.IsUndefined() {
350 log.Println(wasmName+":", "Invoking Stripe")
351 stripe = stripeValue.Invoke(stripePK)
352 if stripe.IsUndefined() {
353 log.Println(wasmName+":", "Failed to invoke Stripe")
354 return nil
355 }
356 }
357
358 log.Println(wasmName+":", "Stripe initialized")
359 checkoutStripe = doc.Call("getElementById", "stripecheckout")
360 if checkoutStripe.IsUndefined() {
361 log.Println(wasmName+":", "element with ID stripecheckout not found")
362 }
363
364 checkoutStripe.Call("showModal")
365 log.Println(wasmName+":", "initializePayment()")
366 initializePayment()
367
368 return nil
369}
370
371func cancelCheckout(this js.Value, args []js.Value) any {
372 log.Println(wasmName+":", "Cancelling checkout ; closing dialog")
373 checkoutStripe.Call("close")
374 updateCartDisplay()
375 return nil
376}
377
378func initializePayment() {
379 type cItem struct {
380 ID string `json:"id"`
381 Amount int `json:"amount"`
382 }
383 type checkout struct {
384 Items []cItem `json:"items"`
385 }
386 payload := checkout{
387 Items: func() []cItem {
388 var items []cItem
389 for _, it := range cart {
390 items = append(items, cItem{ID: it.ID + " X " + strconv.Itoa(it.Qty), Amount: it.Amount})
391 }
392 return items
393 }(),
394 }
395 payloadJSON, err := json.Marshal(payload)
396 if err != nil {
397 log.Println(wasmName+":", "Error marshaling JSON:", err)
398 return
399 }
400 fetchInit := map[string]interface{}{
401 "method": "POST",
402 "headers": map[string]interface{}{
403 "Content-Type": "application/json",
404 },
405 "body": string(payloadJSON),
406 }
407
408 log.Println(wasmName+":", "fetch /create-payment-intent")
409 js.Global().Call("fetch", "/create-payment-intent", js.ValueOf(fetchInit)).
410 Call("then", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
411 response := args[0]
412 log.Println(wasmName+":", "got response from fetch /create-payment-intent")
413 if !response.Get("ok").Bool() {
414 log.Println(wasmName+":", "Fetch request failed with status:", response.Get("status").Int())
415 showMessage("Failed to create payment intent: " + response.Get("status").String())
416 return nil
417 }
418 response.Call("json").Call("then", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
419 clientSecret := args[0].Get("clientSecret").String()
420 log.Println(wasmName+":", "Client secret received:", clientSecret)
421 setupStripeElements(clientSecret)
422 return nil
423 })).Call("catch", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
424 log.Println(wasmName+":", "Error parsing JSON response:", args[0])
425 showMessage("Failed to parse payment intent response.")
426 return nil
427 }))
428 return nil
429 })).
430 Call("catch", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
431 log.Println(wasmName+":", "Error in fetch request:", args[0])
432 showMessage("Failed to communicate with the server.")
433 return nil
434 }))
435}
436
437func setupStripeElements(clientSecret string) {
438 elements = stripe.Call("elements", map[string]interface{}{
439 "clientSecret": clientSecret,
440 })
441 if elements.IsUndefined() {
442 log.Println(wasmName+":", "Failed to initialize Stripe Elements")
443 showMessage("Failed to initialize payment elements.")
444 return
445 }
446 paymentElement := elements.Call("create", "payment", map[string]interface{}{
447 "layout": "tabs",
448 })
449 if paymentElement.IsUndefined() {
450 log.Println(wasmName+":", "Failed to create payment element")
451 showMessage("Failed to create payment element.")
452 return
453 }
454 paymentElement.Call("mount", "#payment-element")
455 submitButton := doc.Call("getElementById", "submit")
456 submitButton.Call("addEventListener", "click", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
457 args[0].Call("preventDefault")
458 showSpinner(true)
459 confirmPayment(clientSecret)
460 return nil
461 }))
462}
463
464func confirmPayment(clientSecret string) {
465
466 windowLocation := js.Global().Get("window").Get("location")
467 protocol := windowLocation.Get("protocol").String()
468 hostname := windowLocation.Get("hostname").String()
469 port := windowLocation.Get("port").String()
470
471 baseURL := protocol + "//" + hostname
472 if port != "" {
473 baseURL += ":" + port
474 }
475 // path := windowLocation.Get("pathname").String()
476 // baseURL += strings.Split(path, "?")[0]
477 // log.Println(wasmName+":","return url ", baseURL)
478
479 returnURL := baseURL + "/complete"
480 returnURL += "?payment_intent=" + clientSecret // + "#complete"
481 log.Println(wasmName+":", "Return URL for payment:", returnURL)
482
483 stripe.Call("confirmPayment", map[string]interface{}{
484 "elements": elements,
485 "confirmParams": map[string]interface{}{
486 "return_url": returnURL,
487 },
488 }).Call("then", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
489 result := args[0]
490 if result.Get("error").IsUndefined() {
491 log.Println(wasmName+":", "Payment successful:", result)
492 showMessage("Payment successful! Thank you for your order.")
493 } else {
494 log.Println(wasmName+":", "Payment error:", result.Get("error").Get("message").String())
495 showMessage("Payment failed: " + result.Get("error").Get("message").String())
496 }
497
498 showSpinner(false)
499 return nil
500 }))
501}
502
503func showMessage(message string) {
504 messageElement := doc.Call("getElementById", "payment-message")
505 messageElement.Set("innerText", message)
506 messageElement.Set("className", "")
507}
508
509func showSpinner(isLoading bool) {
510 spinner := doc.Call("getElementById", "spinner")
511 buttonText := doc.Call("getElementById", "button-text")
512
513 if isLoading {
514 spinner.Set("className", "")
515 buttonText.Set("className", "hidden")
516 } else {
517 spinner.Set("className", "hidden")
518 buttonText.Set("className", "")
519 }
520}
521
522// /complete
523
524func completeLogic() {
525 initializeStripe()
526}
527
528func initializeStripe() {
529 if stripeValue.IsUndefined() {
530 log.Println(`js.Global().Get("Stripe")`)
531 stripeValue = js.Global().Get("Stripe")
532 if stripeValue.IsUndefined() {
533 log.Println(`Stripe is undefined, attempting to load Stripe.js`)
534
535 doc := js.Global().Get("document")
536 head := doc.Call("querySelector", "head")
537 script := doc.Call("createElement", "script")
538 script.Set("src", "https://js.stripe.com/v3/")
539 script.Set("defer", true)
540
541 done := make(chan bool)
542 script.Call("addEventListener", "load", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
543 log.Println(wasmName+":", "Stripe.js script has been loaded")
544 done <- true
545 return nil
546 }))
547
548 head.Call("appendChild", script)
549
550 <-done
551
552 stripeValue = js.Global().Get("Stripe")
553 if stripeValue.IsUndefined() {
554 log.Println(wasmName+":", "Failed to load Stripe.js")
555 return
556 }
557 }
558 }
559
560 log.Println(wasmName+":", "Stripe.js loaded successfully")
561
562 if stripe.IsUndefined() {
563 log.Println(wasmName+":", "Invoking Stripe")
564 stripe = stripeValue.Invoke(stripePK)
565 if stripe.IsUndefined() {
566 log.Println(wasmName+":", "Failed to invoke Stripe")
567 return
568 }
569 }
570
571 log.Println(wasmName+":", "Stripe initialized")
572 checkStatus()
573}
574
575var (
576 successIcon = `<svg width="16" height="14" viewBox="0 0 16 14" fill="none" xmlns="http://www.w3.org/2000/svg">
577 <path fill-rule="evenodd" clip-rule="evenodd" d="M15.4695 0.232963C15.8241 0.561287 15.8454 1.1149 15.5171 1.46949L6.14206 11.5945C5.97228 11.7778 5.73221 11.8799 5.48237 11.8748C5.23253 11.8698 4.99677 11.7582 4.83452 11.5681L0.459523 6.44311C0.145767 6.07557 0.18937 5.52327 0.556912 5.20951C0.924454 4.89575 1.47676 4.93936 1.79051 5.3069L5.52658 9.68343L14.233 0.280522C14.5613 -0.0740672 15.1149 -0.0953599 15.4695 0.232963Z" fill="white"/>
578 </svg>`
579
580 errorIcon = `<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
581 <path fill-rule="evenodd" clip-rule="evenodd" d="M1.25628 1.25628C1.59799 0.914573 2.15201 0.914573 2.49372 1.25628L8 6.76256L13.5063 1.25628C13.848 0.914573 14.402 0.914573 14.7437 1.25628C15.0854 1.59799 15.0854 2.15201 14.7437 2.49372L9.23744 8L14.7437 13.5063C15.0854 13.848 15.0854 14.402 14.7437 14.7437C14.402 15.0854 13.848 15.0854 13.5063 14.7437L8 9.23744L2.49372 14.7437C2.15201 15.0854 1.59799 15.0854 1.25628 14.7437C0.914573 14.402 0.914573 13.848 1.25628 13.5063L6.76256 8L1.25628 2.49372C0.914573 2.15201 0.914573 1.59799 1.25628 1.25628Z" fill="white"/>
582 </svg>`
583
584 infoIcon = `<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
585 <path fill-rule="evenodd" clip-rule="evenodd" d="M10 1.5H4C2.61929 1.5 1.5 2.61929 1.5 4V10C1.5 11.3807 2.61929 12.5 4 12.5H10C11.3807 12.5 12.5 11.3807 12.5 10V4C12.5 2.61929 11.3807 1.5 10 1.5ZM4 0C1.79086 0 0 1.79086 0 4V10C0 12.2091 1.79086 14 4 14H10C12.2091 14 14 12.2091 14 10V4C14 1.79086 12.2091 0 10 0H4Z" fill="white"/>
586 <path fill-rule="evenodd" clip-rule="evenodd" d="M5.25 7C5.25 6.58579 5.58579 6.25 6 6.25H7.25C7.66421 6.25 8 6.58579 8 7V10.5C8 10.9142 7.66421 11.25 7.25 11.25C6.83579 11.25 6.5 10.9142 6.5 10.5V7.75H6C5.58579 7.75 5.25 7.41421 5.25 7Z" fill="white"/>
587 <path d="M5.75 4C5.75 3.31075 6.31075 2.75 7 2.75C7.68925 2.75 8.25 3.31075 8.25 4C8.25 4.68925 7.68925 5.25 7 5.25C6.31075 5.25 5.75 4.68925 5.75 4Z" fill="white"/>
588 </svg>`
589)
590
591func setErrorState() {
592 js.Global().Get("document").Call("querySelector", "#status-icon").Set("style", map[string]interface{}{"backgroundColor": "#DF1B41"})
593 js.Global().Get("document").Call("querySelector", "#status-icon").Set("innerHTML", errorIcon)
594 js.Global().Get("document").Call("querySelector", "#status-text").Set("textContent", "Something went wrong, please try again.")
595 js.Global().Get("document").Call("querySelector", "#details-table").Call("classList").Call("add", "hidden")
596 js.Global().Get("document").Call("querySelector", "#view-details").Call("classList").Call("add", "hidden")
597}
598
599func checkStatus() {
600 clientSecret := js.Global().Get("URLSearchParams").New(js.Global().Get("window").Get("location").Get("search")).Call("get", "payment_intent_client_secret").String()
601
602 if clientSecret == "" {
603 setErrorState()
604 return
605 }
606
607 if stripe.IsUndefined() {
608 log.Println(wasmName+":", "Stripe is not initialized")
609 setErrorState()
610 return
611 }
612
613 stripe.Call("retrievePaymentIntent", clientSecret).Call("then", js.FuncOf(func(this js.Value, p []js.Value) interface{} {
614 paymentIntent := p[0].Get("paymentIntent")
615 setPaymentDetails(paymentIntent)
616 return nil
617 })).Call("catch", js.FuncOf(func(this js.Value, p []js.Value) interface{} {
618 setErrorState()
619 return nil
620 }))
621}
622
623func getAllLocalStorageData() map[string]interface{} {
624 localStorage := js.Global().Get("localStorage")
625 keys := js.Global().Get("Object").Call("keys", localStorage)
626 data := make(map[string]interface{})
627
628 for i := 0; i < keys.Length(); i++ {
629 key := keys.Index(i).String()
630 value := localStorage.Call("getItem", key).String()
631 var parsedValue interface{}
632 err := json.Unmarshal([]byte(value), &parsedValue)
633 if err != nil {
634 parsedValue = value // If not JSON, store raw value
635 }
636 data[key] = parsedValue
637 }
638 return data
639}
640
641func submitOrder(localStorageData map[string]interface{}, paymentIntentId string) {
642 orderData := map[string]interface{}{
643 "localStorageData": localStorageData,
644 "paymentIntentId": paymentIntentId,
645 }
646
647 body, err := json.Marshal(orderData)
648 if err != nil {
649 log.Println(wasmName+":", "Error marshalling order data:", err)
650 return
651 }
652
653 fetch := js.Global().Get("fetch")
654 if fetch.IsUndefined() {
655 log.Println(wasmName+":", "Fetch API is not available")
656 return
657 }
658
659 options := map[string]interface{}{
660 "method": "POST",
661 "headers": map[string]interface{}{
662 "Content-Type": "application/json",
663 },
664 "body": string(body),
665 }
666
667 fetch.Invoke("/submit-order", js.ValueOf(options)).Call("then", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
668 response := args[0]
669 response.Call("json").Call("then", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
670 data := args[0]
671 log.Println(wasmName+":", "Order submitted successfully:", data)
672 return nil
673 })).Call("catch", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
674 err := args[0]
675 js.Global().Call("alert", fmt.Sprintf("Error submitting order: %s\n Please reload the page.\nReach out to us on Telegram regarding this incident:\nhttps://t.me/magnetosphere", err))
676 log.Println(wasmName+":", "Error parsing order response:", err)
677 return nil
678 }))
679 return nil
680 })).Call("catch", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
681 err := args[0]
682 js.Global().Call("alert", fmt.Sprintf("Error submitting order: %s\n Please reload the page.\nReach out to us on Telegram regarding this incident:\nhttps://t.me/magnetosphere", err))
683 log.Println(wasmName+":", "Error submitting order:", err)
684 return nil
685 }))
686}
687
688func setPaymentDetails(intent js.Value) {
689 var statusText, iconColor, icon string
690 statusText = "Something went wrong, please try again."
691 iconColor = "#DF1B41"
692 icon = errorIcon
693
694 if !intent.IsUndefined() {
695 intentStatus := intent.Get("status").String()
696 intentID := intent.Get("id").String()
697
698 allLocalStorageData := getAllLocalStorageData()
699
700 switch intentStatus {
701 case "succeeded":
702 statusText = "Payment succeeded"
703 iconColor = "#30B130"
704 icon = successIcon
705 if len(allLocalStorageData) > 0 {
706 submitOrder(allLocalStorageData, intentID)
707 } else {
708 log.Println(wasmName+":", "No data found in localStorage; order not submitted.")
709 }
710 case "processing":
711 statusText = "Your payment is processing."
712 iconColor = "#6D6E78"
713 icon = infoIcon
714 if len(allLocalStorageData) > 0 {
715 submitOrder(allLocalStorageData, intentID)
716 } else {
717 log.Println(wasmName+":", "No data found in localStorage; order not submitted.")
718 }
719 case "requires_payment_method":
720 statusText = "Your payment was not successful, please try again."
721 default:
722 statusText = "Unknown payment status."
723 }
724
725 // Update the status icon, text, and links
726 js.Global().Get("document").Call("querySelector", "#status-icon").Set("style", map[string]interface{}{"backgroundColor": iconColor})
727 js.Global().Get("document").Call("querySelector", "#status-icon").Set("innerHTML", icon)
728 js.Global().Get("document").Call("querySelector", "#status-text").Set("textContent", statusText)
729 js.Global().Get("document").Call("querySelector", "#intent-id").Set("textContent", intentID)
730 js.Global().Get("document").Call("querySelector", "#intent-status").Set("textContent", intentStatus)
731 js.Global().Get("document").Call("querySelector", "#view-details").Set("href", "https://dashboard.stripe.com/payments/"+intentID)
732
733 // Update the "Order Details" link with the paymentIntent ID
734 orderDetailsLink := js.Global().Get("document").Call("querySelector", "#order-details-link")
735 orderDetailsLink.Set("href", "/order/"+intentID)
736 orderDetailsLink.Set("onclick", nil) // Allow default behavior (navigation)
737
738 } else {
739 setErrorState()
740 }
741}
742
743
744// ===== stl2.go =====
745// Package main stl2.go
746package main
747
748import (
749 "bytes"
750 "crypto/rand"
751 "encoding/base64"
752 "encoding/binary"
753 "log"
754 m "math"
755 r "reflect"
756 "runtime"
757 "strconv"
758 "strings"
759 "syscall/js"
760 u "unsafe"
761
762 "github.com/go-gl/mathgl/mgl32"
763 "gitlab.com/russoj88/stl/stl"
764)
765
766var (
767 wasmName string
768 running = true
769 done chan struct{}
770 stlFileName, originalHTML string
771 rr Renderer
772 existingFooter, body, footer, sXV, sYV, sZV, sZoomV, cEl, gl js.Value
773 currentZoom float32 = 3
774)
775
776const gebi = "getElementById"
777const ael = "addEventListener"
778const ih = "innerHTML"
779
780func main() {
781 ready := make(chan struct{})
782
783 document := js.Global().Get("document")
784 readyState := document.Get("readyState").String()
785 if readyState == "interactive" || readyState == "complete" {
786 log.Println(wasmName+":", "WASM: DOM already fully loaded")
787 close(ready)
788 } else {
789 cb := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
790 log.Println(wasmName+":", "WASM: DOM fully loaded and parsed")
791 close(ready)
792 return nil
793 })
794 defer cb.Release()
795
796 document.Call(ael, "DOMContentLoaded", cb)
797 log.Println(wasmName+":", "WASM: waiting for DOM to load")
798 }
799
800 // Wait until the DOM is ready
801 <-ready
802
803 cEl = document.Call(gebi, "gocanvas")
804 if cEl.IsUndefined() || cEl.IsNull() {
805 return
806 }
807
808 window := js.Global().Get("window")
809 location := window.Get("location")
810 pathname := location.Get("pathname").String()
811 parts := strings.SplitN(pathname, "/", 3)
812 if len(parts) > 1 {
813 parts[1] = "/" + parts[1]
814 } else {
815 parts = append(parts, "/")
816 }
817 switch parts[1] {
818 case "/cat":
819 log.Println(wasmName+":", "nothing to animate ; return")
820 return
821 case "/p":
822 log.Println(wasmName+":", "fetching stereolithograph")
823 preElement := js.Global().Get("document").Call("getElementById", "package-type")
824 if preElement.IsUndefined() || preElement.IsNull() {
825 log.Println(wasmName+":", "Element with ID 'package-type' not found. Exiting.")
826 return // Exit the WebAssembly here
827 }
828
829 packageText := preElement.Get("textContent").String()
830 packageType := packageText[len("Package Type: "):]
831 stlFileName = packageType + ".stl"
832
833 // Fetch the STL file
834 response := js.Global().Call("fetch", "/i/stl/base64/"+stlFileName)
835 promise := response.Call("then", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
836 resp := args[0]
837 if !resp.Get("ok").Bool() {
838 log.Printf("HTTP Error: %s (status: %d)", resp.Get("statusText").String(), resp.Get("status").Int())
839 log.Println(wasmName+":", "Failed to fetch the STL file "+stlFileName+". Exiting.")
840 return nil // Exit the WebAssembly here
841 }
842 return resp.Call("text")
843 }))
844
845 // Process the fetched data
846 promise.Call("then", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
847 if len(args) > 0 {
848 result := args[0].String()
849 uploadedFile, err1 := parseBase64File(result)
850 if !err1.IsNull() {
851 log.Println(wasmName+":", "Error parsing the base64 file:", err1)
852 return nil
853 }
854
855 stlSolid, err2 := NewSTL(uploadedFile)
856 if err2 != nil {
857 log.Println(wasmName+":", "Error creating STL object:", err2)
858 return nil
859 }
860
861 vert, colors, indices := stlSolid.GetModel()
862 modelSize := getMaxScalar(vert)
863 currentZoom := modelSize * 3
864 rr.SetZoom(currentZoom)
865 rr.SetModel(colors, vert, indices)
866 }
867 return nil
868 }))
869
870 // Ensure this function does not execute until the fetch of the STL file succeeds
871 promise.Call("catch", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
872 log.Println(wasmName+":", "An error occurred during the fetch or processing. Exiting.")
873 return nil
874 }))
875 niam()
876 default:
877 niam()
878 }
879}
880func niam() {
881 mgl32.DisableMemoryPooling()
882
883 //stlFileName = "TO-247.stl"
884 tdata := struct {
885 XRange, ZMin, ZMax string
886 XStep, ZoomStep string
887 }{
888 XRange: "1", ZMin: "0", ZMax: "50",
889 XStep: "0.01", ZoomStep: "0.1",
890 }
891 if stlFileName != ".stl" && stlFileName != "" {
892 tdata.ZMin = "10"
893 tdata.ZMax = "1000"
894 }
895
896 var controlsHTML = `
897 <datalist id="speeds">
898 <option>-` + tdata.XRange + `</option><option>0</option><option>` + tdata.XRange + `</option></datalist>
899 <table class="🌐">
900 <tr><td><p><button type="button" id="stop">Stop Rendering</button></p></td><td><p>X<input id="X" type="range" min="-` + tdata.XRange + `" max="` + tdata.XRange + `" step="` + tdata.XStep + `" list="speeds"><text id="XV">00.00</text></p></td>
901 <td><p>Y<input id="Y" type="range" min="-` + tdata.XRange + `" max="` + tdata.XRange + `" step="` + tdata.XStep + `" list="speeds"><text id="YV">00.00</text></p></td>
902 <td><p>Z<input id="Z" type="range" min="-` + tdata.XRange + `" max="` + tdata.XRange + `" step="` + tdata.XStep + `" list="speeds"><text id="ZV">00.00</text></p></td>
903 <td><p>Zoom<input id="Zoom" type="range" min="` + tdata.ZMin + `" max="` + tdata.ZMax + `" step="` + tdata.ZoomStep + `" list="speeds"><text id="ZoomV">0000.00</text></p></td>
904 </table>
905 `
906 doc := js.Global().Get("document")
907 body = doc.Get("body")
908 existingFooter = doc.Call("getElementsByTagName", "footer").Index(0)
909 if existingFooter.Truthy() {
910 originalHTML = existingFooter.Get(ih).String()
911 footer = doc.Call("createElement", "footer")
912 footer.Set(ih, originalHTML+controlsHTML)
913 body.Call("replaceChild", footer, existingFooter)
914 } else {
915 footer = doc.Call("createElement", "footer")
916 footer.Set(ih, controlsHTML)
917 body.Call("appendChild", footer)
918 }
919
920 cEl = doc.Call(gebi, "gocanvas")
921 width := doc.Get("body").Get("clientWidth").Int()
922 height := doc.Get("body").Get("clientHeight").Int()
923
924 cEl.Set("width", width)
925 cEl.Set("height", height)
926 sXc := js.FuncOf(sCX)
927 sX := doc.Call(gebi, "X")
928 sX.Call(ael, "input", sXc)
929 sXV = doc.Call(gebi, "XV")
930
931 sYc := js.FuncOf(sCY)
932 sY := doc.Call(gebi, "Y")
933 sY.Call(ael, "input", sYc)
934 sYV = doc.Call(gebi, "YV")
935
936 sZc := js.FuncOf(sCZ)
937 sZ := doc.Call(gebi, "Z")
938 sZ.Call(ael, "input", sZc)
939 sZV = doc.Call(gebi, "ZV")
940
941 sZoomc := js.FuncOf(sCZoom)
942 sZoom := doc.Call(gebi, "Zoom")
943 sZoom.Call(ael, "input", sZoomc)
944 sZoomV = doc.Call(gebi, "ZoomV")
945
946 sBc := js.FuncOf(stopApplication)
947 sB := doc.Call(gebi, "stop")
948 sB.Call(ael, "click", sBc)
949 defer sBc.Release()
950 /*
951 if stlFileName != ".stl" && stlFileName != "" {
952
953 response := js.Global().Call("fetch", "/stl/base64/"+stlFileName)
954 promise := response.Call("then", js.FuncOf(func(this js.Value, p []js.Value) interface{} {
955 if p[0].Get("ok").Bool() {
956 return p[0].Call("text")
957 }
958 return "Error fetching stereolithograph"
959 }))
960 promise.Call("then", js.FuncOf(func(this js.Value, p []js.Value) interface{} {
961 result := p[0].String()
962 uploadedFile, _ := parseBase64File(result) // nolint
963 stlSolid, _ := NewSTL(uploadedFile) // nolint
964 vert, colors, indices := stlSolid.GetModel()
965 modelSize := getMaxScalar(vert)
966 currentZoom := modelSize * 3
967 rr.SetZoom(currentZoom)
968 rr.SetModel(colors, vert, indices)
969 return nil
970 }))
971 }
972 */
973 gl = cEl.Call("getContext", "webgl")
974 if gl.IsUndefined() {
975 gl = cEl.Call("getContext", "experimental-webgl")
976 }
977 if gl.IsUndefined() {
978 js.Global().Call("alert", "WASM: browser might not support webgl")
979 return
980 }
981
982 config := InitialConfig{
983 W: width,
984 H: height,
985 X: 0,
986 Y: 0,
987 Z: 0,
988 Vertices: verticesNative,
989 Indices: indicesNative,
990 Colors: colorsNative,
991 FSC: fragShaderCode,
992 VSC: vertShaderCode,
993 }
994
995 config.X = cryptoRandFloat32() / 20
996 config.Y = cryptoRandFloat32() / 20
997 config.Z = cryptoRandFloat32() / 20
998 config.Vertices, config.Indices = generateSphereVertices(float32(1.0), 30, 30)
999 if stlFileName == ".stl" || stlFileName == "" {
1000 config.FSC, config.VSC = fragShaderCode1, vertShaderCode1
1001 }
1002 var jsErr js.Value
1003 rr, jsErr = NewRenderer(gl, config)
1004 if !jsErr.IsNull() {
1005 js.Global().Call("alert", "WASM: Cannot load webgl ")
1006 return
1007 }
1008 rr.SetZoom(currentZoom)
1009 defer rr.Release()
1010
1011 x, y, z := rr.GetSpeed()
1012 sX.Set("value", f32(x, 'f', -1, 64))
1013 if x > 0 {
1014 sXV.Set(ih, "+"+f32(x, 'f', 2, 64))
1015 }
1016 if x == 0 {
1017 sXV.Set(ih, " "+f32(x, 'f', 2, 64))
1018 }
1019 if x < 0 {
1020 sXV.Set(ih, f32(x, 'f', 2, 64))
1021 }
1022 sY.Set("value", f32(y, 'f', -1, 64))
1023 if y > 0 {
1024 sYV.Set(ih, "+"+f32(y, 'f', 2, 64))
1025 }
1026 if y == 0 {
1027 sYV.Set(ih, "0"+f32(y, 'f', 2, 64))
1028 }
1029 if y < 0 {
1030 sYV.Set(ih, f32(y, 'f', 2, 64))
1031 }
1032 sZ.Set("value", f32(z, 'f', -1, 64))
1033 if z > 0 {
1034 sZV.Set(ih, "+"+f32(z, 'f', 2, 64))
1035 }
1036 if z == 0 {
1037 sZV.Set(ih, "0"+f32(z, 'f', 2, 64))
1038 }
1039 if z < 0 {
1040 sZV.Set(ih, f32(z, 'f', 2, 64))
1041 }
1042
1043 var renderFrame js.Func
1044 renderFrame = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
1045 rr.Render(this, args)
1046 js.Global().Call("requestAnimationFrame", renderFrame)
1047 return nil
1048 })
1049 js.Global().Call("requestAnimationFrame", renderFrame)
1050
1051 done = make(chan struct{})
1052
1053 <-done
1054}
1055
1056func parseBase64File(input string) (output []byte, err js.Value) {
1057 searchString := "base64,"
1058 searchLength := len(searchString)
1059 var index = -1
1060 for i := 0; i <= len(input)-searchLength; i++ {
1061 if input[i:i+searchLength] == searchString {
1062 index = i
1063 break
1064 }
1065 }
1066 if index < 0 {
1067 err = js.Global().Get("Error").New("Error opening file")
1068 return
1069 }
1070 sBuffer := input[index+searchLength:]
1071 output, decodeErr := base64.StdEncoding.DecodeString(sBuffer)
1072 if decodeErr != nil {
1073 err = js.Global().Get("Error").New(decodeErr.Error())
1074 return
1075 }
1076 return output, js.Null()
1077}
1078
1079func getMaxScalar(vertices []float32) float32 {
1080 var max float32
1081 for baseIndex := 0; baseIndex < len(vertices); baseIndex += 3 {
1082 testScale := scalar(vertices[baseIndex], vertices[baseIndex], vertices[baseIndex])
1083 if testScale > max {
1084 max = testScale
1085 }
1086 }
1087 return max
1088}
1089
1090func scalar(x float32, y float32, z float32) float32 {
1091 xy := m.Sqrt(float64(x*x + y*y))
1092 return float32(m.Sqrt(xy*xy + float64(z*z)))
1093}
1094
1095func cryptoRandFloat32() float32 {
1096 b := make([]byte, 4)
1097 _, err := rand.Read(b)
1098 if err != nil {
1099 panic("crypto/rand read failed: " + err.Error())
1100 }
1101 u := uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3])
1102 return float32(u) / float32(m.MaxUint32)
1103}
1104
1105func stopApplication(_ js.Value, _ []js.Value) interface{} {
1106 running = false
1107 sZoomV.Set(ih, float32(0))
1108 currentZoom = float32(0)
1109 rr.SetZoom(float32(0))
1110 footer.Set(ih, originalHTML)
1111
1112 if callUpdateCartDisplay := js.Global().Get("callUpdateCartDisplay"); !callUpdateCartDisplay.IsUndefined() && !callUpdateCartDisplay.IsNull() {
1113 callUpdateCartDisplay.Invoke()
1114 } else {
1115 js.Global().Call("console.warn", "callUpdateCartDisplay is undefined or null")
1116 }
1117
1118 js.Global().Call("setTimeout", js.FuncOf(func(this js.Value, p []js.Value) interface{} {
1119 close(done)
1120 // done <- struct{}{}
1121 return nil
1122 }), 5000)
1123 return nil
1124}
1125
1126func sCX(this js.Value, _ []js.Value) interface{} {
1127 sSpeed := this.Get("value").String()
1128 s, _ := strconv.ParseFloat(sSpeed, 64)
1129 rr.SetX(float32(s))
1130 if s > 0 {
1131 sXV.Set(ih, "+"+f64(s, 'f', 2, 32))
1132 }
1133 if s == 0 {
1134 sXV.Set(ih, "0"+f64(s, 'f', 2, 32))
1135 }
1136 if s < 0 {
1137 sXV.Set(ih, f64(s, 'f', 2, 32))
1138 }
1139 return nil
1140}
1141
1142func sCY(this js.Value, _ []js.Value) interface{} {
1143 sS := this.Get("value").String()
1144 s, _ := strconv.ParseFloat(sS, 64)
1145 rr.SetY(float32(s))
1146 if s > 0 {
1147 sYV.Set(ih, "+"+f64(s, 'f', 2, 32))
1148 }
1149 if s == 0 {
1150 sYV.Set(ih, "0"+f64(s, 'f', 2, 32))
1151 }
1152 if s < 0 {
1153 sYV.Set(ih, f64(s, 'f', 2, 32))
1154 }
1155 return nil
1156}
1157
1158func sCZ(this js.Value, _ []js.Value) interface{} {
1159 sS := this.Get("value").String()
1160 s, _ := strconv.ParseFloat(sS, 64)
1161 rr.SetZ(float32(s))
1162 if s > 0 {
1163 sZV.Set(ih, "+"+f64(s, 'f', 2, 32))
1164 }
1165 if s == 0 {
1166 sZV.Set(ih, "0"+f64(s, 'f', 2, 32))
1167 }
1168 if s < 0 {
1169 sZV.Set(ih, f64(s, 'f', 2, 32))
1170 }
1171 return nil
1172}
1173
1174func sCZoom(this js.Value, _ []js.Value) interface{} {
1175 sS := this.Get("value").String()
1176 s, _ := strconv.ParseFloat(sS, 64)
1177 if s < 10 {
1178 sZoomV.Set(ih, "000"+f64(s, 'f', 2, 32))
1179 } else if s < 100 {
1180 sZoomV.Set(ih, "00"+f64(s, 'f', 2, 32))
1181 } else if s < 1000 {
1182 sZoomV.Set(ih, "0"+f64(s, 'f', 2, 32))
1183 } else {
1184 sZoomV.Set(ih, f64(s, 'f', 2, 32))
1185 }
1186 currentZoom = float32(s)
1187 rr.SetZoom(currentZoom)
1188 return nil
1189}
1190
1191// Model is an interface for a model
1192type Model interface {
1193 GetModel() ([]float32, []float32, []uint16)
1194}
1195
1196func cryptoRandIntn(max int) (int, js.Value) {
1197 if max <= 0 {
1198 return 0, js.Global().Get("Error").New("max must be a positive integer")
1199 }
1200 numBytes := (max + 7) / 8
1201 maxBytes := 1 << (numBytes * 8)
1202 randBytes := make([]byte, numBytes)
1203 randNum := 0
1204 for {
1205 _, err := rand.Read(randBytes)
1206 if err != nil {
1207 return 0, js.Global().Get("Error").New("error generating random number")
1208 }
1209 for _, b := range randBytes {
1210 randNum = (randNum << 8) | int(b)
1211 }
1212 if randNum < maxBytes-maxBytes%max {
1213 break
1214 }
1215 }
1216 return randNum % max, js.Null()
1217}
1218
1219// NewSTL returns a new STL & error
1220func NewSTL(buffer []byte) (o STL, err error) {
1221 bufferReader := bytes.NewReader(buffer)
1222 solid, err := stl.From(bufferReader)
1223 if err != nil {
1224 return
1225 }
1226
1227 // Generate random rotation matrix
1228 rotationMatrix := randomRotationMatrix()
1229
1230 // Generate colors
1231 numColors, _ := cryptoRandIntn(5)
1232 numColors += 2 // Random number between 2 and 6
1233 colors := GenerateGradient(numColors, int(solid.TriangleCount))
1234
1235 var index uint32
1236 for i, triangle := range solid.Triangles {
1237 colorR := colors[i].Red
1238 colorG := colors[i].Green
1239 colorB := colors[i].Blue
1240
1241 // Convert each triangle's vertices to custom Vertex type and apply rotation
1242 v0 := Vertex{X: float32(triangle.Vertices[0].X), Y: float32(triangle.Vertices[0].Y), Z: float32(triangle.Vertices[0].Z)}
1243 v1 := Vertex{X: float32(triangle.Vertices[1].X), Y: float32(triangle.Vertices[1].Y), Z: float32(triangle.Vertices[1].Z)}
1244 v2 := Vertex{X: float32(triangle.Vertices[2].X), Y: float32(triangle.Vertices[2].Y), Z: float32(triangle.Vertices[2].Z)}
1245
1246 // Rotate and add vertices
1247 o.addRotatedVertex(&index, v0, rotationMatrix, colorR, colorG, colorB)
1248 o.addRotatedVertex(&index, v1, rotationMatrix, colorR, colorG, colorB)
1249 o.addRotatedVertex(&index, v2, rotationMatrix, colorR, colorG, colorB)
1250 }
1251
1252 return o, err
1253}
1254
1255// Add a rotated vertex to the STL structure
1256func (s *STL) addRotatedVertex(index *uint32, vertex Vertex, rotation mgl32.Mat4, r, g, b float32) {
1257 // Apply rotation
1258 rotatedVertex := rotation.Mul4x1(mgl32.Vec3{vertex.X, vertex.Y, vertex.Z}.Vec4(1.0))
1259
1260 // Add rotated vertex to the STL struct
1261 s.v = append(s.v, rotatedVertex[0], rotatedVertex[1], rotatedVertex[2])
1262 s.i = append(s.i, *index)
1263 s.c = append(s.c, r, g, b)
1264 (*index)++
1265}
1266
1267// STL is a stereolithograph
1268type STL struct {
1269 v []float32
1270 c []float32
1271 i []uint32
1272}
1273
1274// Define a custom Vertex type for storing the vertex data.
1275type Vertex struct {
1276 X, Y, Z float32
1277}
1278
1279// GetModel gets the model
1280func (s STL) GetModel() ([]float32, []float32, []uint32) {
1281 return s.v, s.c, s.i
1282}
1283
1284// InitialConfig is the initial config
1285type InitialConfig struct {
1286 W int
1287 H int
1288 X float32
1289 Y float32
1290 Z float32
1291 Colors []float32
1292 Vertices []float32
1293 Indices []uint32
1294 FSC string
1295 VSC string
1296}
1297
1298// Renderer is the renderer
1299type Renderer struct {
1300 glContext js.Value
1301 glTypes GLTypes
1302 colors js.Value
1303 v js.Value
1304 i js.Value
1305 colorBuffer js.Value
1306 vertexBuffer js.Value
1307 indexBuffer js.Value
1308 numIndices int
1309 numVertices int
1310 fragShader js.Value
1311 vertShader js.Value
1312 shaderProgram js.Value
1313 tmark float32
1314 rX float32 //rotation X
1315 rY float32
1316 rZ float32
1317 movMatrix mgl32.Mat4
1318 PositionMatrix js.Value
1319 ViewMatrix js.Value
1320 ModelMatrix js.Value
1321 height int
1322 width int
1323 sX float32
1324 sY float32
1325 sZ float32
1326}
1327
1328// NewRenderer returns a new renderer & error
1329func NewRenderer(gl js.Value, config InitialConfig) (r Renderer, err js.Value) {
1330 // Get some WebGL bindings
1331 r.glContext = gl
1332 err = r.glTypes.New(r.glContext)
1333 r.numIndices = len(config.Indices)
1334 r.numVertices = len(config.Vertices)
1335 r.movMatrix = mgl32.Ident4()
1336 r.width = config.W
1337 r.height = config.H
1338
1339 r.sX = config.X
1340 r.sY = config.Y
1341 r.sZ = config.Z
1342
1343 // Convert buffers to JS TypedArrays
1344 r.UpdateColorBuffer(config.Colors)
1345 r.UpdateVerticesBuffer(config.Vertices)
1346 r.UpdateIndicesBuffer(config.Indices)
1347
1348 r.UpdateFragmentShader(config.FSC)
1349 r.UpdateVertexShader(config.VSC)
1350 r.updateShaderProgram()
1351 r.attachShaderProgram()
1352
1353 r.setContextFlags()
1354
1355 r.createMatrixes()
1356 r.EnableObject()
1357 return
1358}
1359
1360// SetModel sets a new model
1361func (r *Renderer) SetModel(Colors []float32, Vertices []float32, Indices []uint32) {
1362 r.numIndices = len(Indices)
1363 r.UpdateColorBuffer(Colors)
1364 r.UpdateVerticesBuffer(Vertices)
1365 r.UpdateIndicesBuffer(Indices)
1366 r.EnableObject()
1367}
1368
1369// Release releases the renderer
1370func (r *Renderer) Release() {
1371 return
1372}
1373
1374// EnableObject enables the object
1375func (r *Renderer) EnableObject() {
1376 r.glContext.Call("bindBuffer", r.glTypes.ElementArrayBuffer, r.indexBuffer)
1377}
1378
1379// SetX set rotation x axis speed
1380func (r *Renderer) SetX(x float32) {
1381 r.sX = x
1382}
1383
1384// SetY set rotation y axis speed
1385func (r *Renderer) SetY(y float32) {
1386 r.sY = y
1387}
1388
1389// SetZ set rotation z axis speed
1390func (r *Renderer) SetZ(z float32) {
1391 r.sZ = z
1392}
1393
1394// GetSpeed returns the rotation speeds
1395func (r *Renderer) GetSpeed() (x, y, z float32) {
1396 return r.sX, r.sY, r.sZ
1397}
1398
1399// SetSize sets the size of the rendering
1400func (r *Renderer) SetSize(height, width int) {
1401 r.height = height
1402 r.width = width
1403}
1404
1405func (r *Renderer) createMatrixes() {
1406 ratio := float32(r.width) / float32(r.height)
1407 // fmt.Println("Renderer.createMatrixes")
1408 projMatrix := mgl32.Perspective(mgl32.DegToRad(45.0), ratio, 1, 100000.0)
1409 projMatrixBuffer := (*[16]float32)(u.Pointer(&projMatrix)) // nolint
1410 typedProjMatrixBuffer := S2TA([]float32((*projMatrixBuffer)[:]))
1411 r.glContext.Call("uniformMatrix4fv", r.PositionMatrix, false, typedProjMatrixBuffer)
1412
1413 viewMatrix := mgl32.LookAtV(mgl32.Vec3{3.0, 3.0, 3.0}, mgl32.Vec3{0.0, 0.0, 0.0}, mgl32.Vec3{0.0, 1.0, 0.0})
1414 viewMatrixBuffer := (*[16]float32)(u.Pointer(&viewMatrix)) // nolint
1415 typedViewMatrixBuffer := S2TA([]float32((*viewMatrixBuffer)[:]))
1416 r.glContext.Call("uniformMatrix4fv", r.ViewMatrix, false, typedViewMatrixBuffer)
1417}
1418
1419func (r *Renderer) setContextFlags() {
1420 r.glContext.Call("clearColor", 0.0, 0.0, 0.0, 0.0) // Color the screen is cleared to
1421 r.glContext.Call("viewport", 0, 0, r.width, r.height) // Viewport size
1422 r.glContext.Call("depthFunc", r.glTypes.LEqual)
1423}
1424
1425// UpdateFragmentShader Updates the Fragment Shader
1426func (r *Renderer) UpdateFragmentShader(shaderCode string) {
1427 r.fragShader = r.glContext.Call("createShader", r.glTypes.FragmentShader)
1428 r.glContext.Call("shaderSource", r.fragShader, shaderCode)
1429 r.glContext.Call("compileShader", r.fragShader)
1430}
1431
1432// UpdateVertexShader updates the vertex shader
1433func (r *Renderer) UpdateVertexShader(shaderCode string) {
1434 r.vertShader = r.glContext.Call("createShader", r.glTypes.VertexShader)
1435 r.glContext.Call("shaderSource", r.vertShader, shaderCode)
1436 r.glContext.Call("compileShader", r.vertShader)
1437}
1438
1439func (r *Renderer) updateShaderProgram() {
1440 if r.fragShader.IsUndefined() || r.vertShader.IsUndefined() {
1441 return
1442 }
1443 r.shaderProgram = r.glContext.Call("createProgram")
1444 r.glContext.Call("attachShader", r.shaderProgram, r.vertShader)
1445 r.glContext.Call("attachShader", r.shaderProgram, r.fragShader)
1446 r.glContext.Call("linkProgram", r.shaderProgram)
1447}
1448
1449const gul = "getUniformLocation"
1450
1451func (r *Renderer) attachShaderProgram() {
1452 r.PositionMatrix = r.glContext.Call(gul, r.shaderProgram, "Pmatrix")
1453 r.ViewMatrix = r.glContext.Call(gul, r.shaderProgram, "Vmatrix")
1454 r.ModelMatrix = r.glContext.Call(gul, r.shaderProgram, "Mmatrix")
1455
1456 r.glContext.Call("bindBuffer", r.glTypes.ArrayBuffer, r.vertexBuffer)
1457 position := r.glContext.Call("getAttribLocation", r.shaderProgram, "position")
1458 r.glContext.Call("vertexAttribPointer", position, 3, r.glTypes.Float, false, 0, 0)
1459 r.glContext.Call("enableVertexAttribArray", position)
1460
1461 r.glContext.Call("bindBuffer", r.glTypes.ArrayBuffer, r.colorBuffer)
1462 color := r.glContext.Call("getAttribLocation", r.shaderProgram, "color")
1463 r.glContext.Call("vertexAttribPointer", color, 3, r.glTypes.Float, false, 0, 0)
1464 r.glContext.Call("enableVertexAttribArray", color)
1465
1466 r.glContext.Call("useProgram", r.shaderProgram)
1467 if stlFileName == ".stl" || stlFileName == "" {
1468
1469 uBaseColor := r.glContext.Call(gul, r.shaderProgram, "uBaseColor")
1470 uTopColor := r.glContext.Call(gul, r.shaderProgram, "uTopColor")
1471 uColor := r.glContext.Call(gul, r.shaderProgram, "uColor")
1472 r.glContext.Call("uniform3f", uBaseColor, 1.0, 0.0, 0.0)
1473 r.glContext.Call("uniform3f", uTopColor, 0.0, 0.0, 1.0)
1474 r.glContext.Call("uniform3f", uColor, 1.0, 1.0, 1.0)
1475 }
1476}
1477
1478// UpdateColorBuffer Updates the ColorBuffer
1479func (r *Renderer) UpdateColorBuffer(buffer []float32) {
1480 r.colors = S2TA(buffer)
1481 if r.colorBuffer.IsUndefined() {
1482 r.colorBuffer = r.glContext.Call("createBuffer")
1483 }
1484 r.glContext.Call("bindBuffer", r.glTypes.ArrayBuffer, r.colorBuffer)
1485 r.glContext.Call("bufferData", r.glTypes.ArrayBuffer, r.colors, r.glTypes.StaticDraw)
1486}
1487
1488// UpdateVerticesBuffer Updates the VerticesBuffer
1489func (r *Renderer) UpdateVerticesBuffer(buffer []float32) {
1490 r.v = S2TA(buffer)
1491 if r.vertexBuffer.IsUndefined() {
1492 r.vertexBuffer = r.glContext.Call("createBuffer")
1493 }
1494 r.glContext.Call("bindBuffer", r.glTypes.ArrayBuffer, r.vertexBuffer)
1495 r.glContext.Call("bufferData", r.glTypes.ArrayBuffer, r.v, r.glTypes.StaticDraw)
1496}
1497
1498// UpdateIndicesBuffer Updates the IndicesBuffer
1499func (r *Renderer) UpdateIndicesBuffer(buffer []uint32) {
1500 r.i = S2TA(buffer)
1501 if r.indexBuffer.IsUndefined() {
1502 r.indexBuffer = r.glContext.Call("createBuffer")
1503 }
1504 r.glContext.Call("bindBuffer", r.glTypes.ElementArrayBuffer, r.indexBuffer)
1505 r.glContext.Call("bufferData", r.glTypes.ElementArrayBuffer, r.i, r.glTypes.StaticDraw)
1506}
1507
1508// Render renders
1509func (r *Renderer) Render(_ js.Value, args []js.Value) interface{} { // nolint
1510 now := float32(args[0].Float())
1511 tdiff := now - r.tmark
1512 r.tmark = now
1513 r.rX = r.rX + r.sX*float32(tdiff)/500
1514 r.rY = r.rY + r.sY*float32(tdiff)/500
1515 r.rZ = r.rZ + r.sZ*float32(tdiff)/500
1516
1517 r.movMatrix = mgl32.HomogRotate3DX(r.rX)
1518 r.movMatrix = r.movMatrix.Mul4(mgl32.HomogRotate3DY(r.rY))
1519 r.movMatrix = r.movMatrix.Mul4(mgl32.HomogRotate3DZ(r.rZ))
1520
1521 modelMatrixBuffer := (*[16]float32)(u.Pointer(&r.movMatrix)) // nolint
1522 typedModelMatrixBuffer := S2TA([]float32((*modelMatrixBuffer)[:]))
1523
1524 r.glContext.Call("uniformMatrix4fv", r.ModelMatrix, false, typedModelMatrixBuffer)
1525
1526 r.glContext.Call("enable", r.glTypes.DepthTest)
1527 r.glContext.Call("clear", r.glTypes.ColorBufferBit)
1528 r.glContext.Call("clear", r.glTypes.DepthBufferBit)
1529 usegltype := r.glTypes.Triangles
1530 if stlFileName == ".stl" || stlFileName == "" {
1531 usegltype = r.glTypes.Line
1532 r.glContext.Call("drawArrays", r.glTypes.LineLoop, 0, r.numVertices/3)
1533 }
1534 r.glContext.Call("drawElements", usegltype, r.numIndices, r.glTypes.UnsignedInt, 0)
1535
1536 return nil
1537}
1538
1539// SetZoom Sets the Zoom
1540func (r *Renderer) SetZoom(currentZoom float32) {
1541 viewMatrix := mgl32.LookAtV(mgl32.Vec3{currentZoom, currentZoom, currentZoom}, mgl32.Vec3{0.0, 0.0, 0.0}, mgl32.Vec3{0.0, 1.0, 0.0})
1542 viewMatrixBuffer := (*[16]float32)(u.Pointer(&viewMatrix)) // nolint
1543 typedViewMatrixBuffer := S2TA([]float32((*viewMatrixBuffer)[:]))
1544 r.glContext.Call("uniformMatrix4fv", r.ViewMatrix, false, typedViewMatrixBuffer)
1545}
1546
1547// NewColorInterpolation generates color interpolation
1548func NewColorInterpolation(a Color, b Color) ColorInterpolation {
1549 return ColorInterpolation{
1550 a,
1551 b,
1552 a.Subtract(b),
1553 }
1554}
1555
1556// ColorInterpolation is interpolated color
1557type ColorInterpolation struct {
1558 startColor Color
1559 endColor Color
1560 deltaColor Color
1561}
1562
1563// Interpolate interpolates
1564func (c ColorInterpolation) Interpolate(percent float32) Color {
1565 scaled := c.deltaColor.MultiplyFloat(percent)
1566 return c.startColor.Add(scaled)
1567}
1568
1569// Color represents a color
1570type Color struct {
1571 Red float32
1572 Green float32
1573 Blue float32
1574}
1575
1576// NewRandomColor returns a New RandomColor
1577func NewRandomColor() Color {
1578 const maxRGB = 255
1579 var r, g, b float64
1580 buf := make([]byte, 3)
1581 rand.Read(buf)
1582 r = float64(buf[0]) / 256
1583 g = float64(buf[1]) / 256
1584 b = float64(buf[2]) / 256
1585 r = r * maxRGB
1586 g = g * maxRGB
1587 b = b * maxRGB
1588 return Color{float32(r), float32(g), float32(b)}
1589}
1590
1591// Subtract Subtracts color
1592func (c Color) Subtract(d Color) Color {
1593 return Color{
1594 c.Red - d.Red,
1595 c.Green - d.Green,
1596 c.Blue - d.Blue,
1597 }
1598}
1599
1600// Add Adds color
1601func (c Color) Add(d Color) Color {
1602 return Color{
1603 c.Red + d.Red,
1604 c.Green + d.Green,
1605 c.Blue + d.Blue,
1606 }
1607}
1608
1609// MultiplyFloat Multiplies Float
1610func (c Color) MultiplyFloat(x float32) Color {
1611 return Color{
1612 c.Red * x,
1613 c.Green * x,
1614 c.Blue * x,
1615 }
1616}
1617
1618// GenerateGradient Generates Gradient
1619func GenerateGradient(numColors int, steps int) []Color {
1620 distribution := distributeColors(numColors, steps)
1621 colors := make([]Color, numColors)
1622 for i := 0; i < numColors; i++ {
1623 colors[i] = NewRandomColor()
1624 }
1625 outputBuffer := make([]Color, 0, steps)
1626 for index := 0; index < numColors; index++ {
1627 if index >= numColors-1 {
1628 size := steps - distribution[index]
1629 interpolation := NewColorInterpolation(colors[index-1], colors[index])
1630 buffer := generateSingleGradient(interpolation, size)
1631 outputBuffer = append(outputBuffer, buffer...)
1632 break
1633 }
1634 currentStep := distribution[index]
1635 nextStep := distribution[index+1]
1636 size := nextStep - currentStep
1637 interpolation := NewColorInterpolation(colors[index], colors[index+1])
1638 buffer := generateSingleGradient(interpolation, size)
1639 outputBuffer = append(outputBuffer, buffer...)
1640 }
1641 return outputBuffer
1642}
1643
1644func distributeColors(numColors int, steps int) []int {
1645 diff := int(m.Ceil(float64(steps) / float64(numColors)))
1646 output := make([]int, numColors)
1647 for i := 0; i < numColors; i++ {
1648 output[i] = diff * i
1649 }
1650 return output
1651}
1652
1653func generateSingleGradient(c ColorInterpolation, numSteps int) []Color {
1654 output := make([]Color, numSteps)
1655 for i := 0; i < numSteps; i++ {
1656 percent := float32(i) / float32(numSteps)
1657 output[i] = c.Interpolate(percent)
1658 }
1659 return output
1660}
1661
1662func f32(f float32, g byte, prec, bitSize int) string {
1663 return strconv.FormatFloat(float64(f), g, prec, bitSize)
1664}
1665func f64(f float64, g byte, prec, bitSize int) string {
1666 return strconv.FormatFloat(f, g, prec, bitSize)
1667}
1668
1669func generateSphereVertices(radius float32, stacks, slices int) ([]float32, []uint32) {
1670 var vertices []float32
1671 var indices []uint32
1672
1673 // Generate random initial rotation
1674 rotationMatrix := randomRotationMatrix()
1675
1676 // Generate sphere vertices
1677 for i := 0; i <= stacks; i++ {
1678 phi := float32(i) * float32(m.Pi) / float32(stacks)
1679 for j := 0; j <= slices; j++ {
1680 theta := float32(j) * 2.0 * float32(m.Pi) / float32(slices)
1681 x := radius * float32(m.Sin(float64(phi))) * float32(m.Cos(float64(theta)))
1682 y := radius * float32(m.Sin(float64(phi))) * float32(m.Sin(float64(theta)))
1683 z := radius * float32(m.Cos(float64(phi)))
1684
1685 // Apply rotation to the vertex
1686 vertex := mgl32.Vec3{z, y, x}
1687 rotatedVertex := rotationMatrix.Mul4x1(vertex.Vec4(1.0))
1688
1689 // Append rotated vertex
1690 vertices = append(vertices, rotatedVertex[0], rotatedVertex[1], rotatedVertex[2])
1691 }
1692 }
1693
1694 // Generate sphere indices
1695 for i := 0; i < stacks; i++ {
1696 for j := 0; j <= slices; j++ {
1697 indices = append(indices, uint32(i*(slices+1)+j), uint32((i+1)*(slices+1)+j))
1698 }
1699 }
1700
1701 return vertices, indices
1702}
1703
1704func randomRotationMatrix() mgl32.Mat4 {
1705 // Generate random rotation angles (in radians) using crypto/rand
1706 rotX := randomFloat32() * 2 * float32(m.Pi)
1707 rotY := randomFloat32() * 2 * float32(m.Pi)
1708 rotZ := randomFloat32() * 2 * float32(m.Pi)
1709
1710 // Create rotation matrices for each axis
1711 rotMatrixX := mgl32.HomogRotate3DX(rotX)
1712 rotMatrixY := mgl32.HomogRotate3DY(rotY)
1713 rotMatrixZ := mgl32.HomogRotate3DZ(rotZ)
1714
1715 // Combine rotations (Z * Y * X)
1716 return rotMatrixZ.Mul4(rotMatrixY).Mul4(rotMatrixX)
1717}
1718
1719func randomFloat32() float32 {
1720 var randomValue uint32
1721 err := binary.Read(rand.Reader, binary.BigEndian, &randomValue)
1722 if err != nil {
1723 log.Fatalf("Failed to read random value: %v", err)
1724 }
1725 return float32(randomValue) / float32(0xFFFFFFFF)
1726}
1727
1728var verticesNative = []float32{
1729 -1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1,
1730 -1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1,
1731 -1, -1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1,
1732 1, -1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1,
1733 -1, -1, -1, -1, -1, 1, 1, -1, 1, 1, -1, -1,
1734 -1, 1, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1,
1735}
1736var colorsNative = []float32{
1737 5, 3, 7, 5, 3, 7, 5, 3, 7, 5, 3, 7,
1738 1, 1, 3, 1, 1, 3, 1, 1, 3, 1, 1, 3,
1739 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1,
1740 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0,
1741 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0,
1742 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0,
1743}
1744
1745var indicesNative = []uint32{
1746 0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7,
1747 8, 9, 10, 8, 10, 11, 12, 13, 14, 12, 14, 15,
1748 16, 17, 18, 16, 18, 19, 20, 21, 22, 20, 22, 23,
1749}
1750
1751const vertShaderCode = `
1752attribute vec3 position;
1753uniform mat4 Pmatrix;
1754uniform mat4 Vmatrix;
1755uniform mat4 Mmatrix;
1756attribute vec3 color;
1757varying vec3 vColor;
1758
1759void main(void) {
1760 gl_Position = Pmatrix*Vmatrix*Mmatrix*vec4(position, 1.);
1761 vColor = color;
1762}
1763`
1764const fragShaderCode = `
1765precision mediump float;
1766varying vec3 vColor;
1767void main(void) {
1768 gl_FragColor = vec4(vColor, 1.);
1769}
1770`
1771const fragShaderCode1 = `
1772precision mediump float;
1773uniform vec3 uBaseColor; // Color value at the base
1774uniform vec3 uTopColor; // Color value at the top
1775varying vec3 vPosition; // Interpolated vertex position
1776void main(void) {
1777 float t = (vPosition.y + 1.0) * 0.5; // Normalize the y-coordinate to [0, 1]
1778 vec3 rainbowColor = mix(uBaseColor, uTopColor, t);
1779 gl_FragColor = vec4(rainbowColor, 1.0);
1780}
1781`
1782const vertShaderCode1 = `
1783 attribute vec3 position;
1784 uniform mat4 Pmatrix;
1785 uniform mat4 Vmatrix;
1786 uniform mat4 Mmatrix;
1787 varying vec3 vPosition; // Pass vertex position to fragment shader
1788 void main(void) {
1789 gl_Position = Pmatrix * Vmatrix * Mmatrix * vec4(position, 1.0);
1790 vPosition = position; // Pass vertex position to fragment shader
1791 }
1792 `
1793
1794// GLTypes provides WebGL bindings.
1795type GLTypes struct {
1796 StaticDraw js.Value
1797 ArrayBuffer js.Value
1798 ElementArrayBuffer js.Value
1799 VertexShader js.Value
1800 FragmentShader js.Value
1801 Float js.Value
1802 DepthTest js.Value
1803 ColorBufferBit js.Value
1804 DepthBufferBit js.Value
1805 Triangles js.Value
1806 UnsignedShort js.Value
1807 UnsignedInt js.Value
1808 LEqual js.Value
1809 LineLoop js.Value
1810 Line js.Value
1811}
1812
1813// New grabs the WebGL bindings from a GL context.
1814func (types *GLTypes) New(gl js.Value) js.Value {
1815 types.StaticDraw = gl.Get("STATIC_DRAW")
1816 types.ArrayBuffer = gl.Get("ARRAY_BUFFER")
1817 types.ElementArrayBuffer = gl.Get("ELEMENT_ARRAY_BUFFER")
1818 types.VertexShader = gl.Get("VERTEX_SHADER")
1819 types.FragmentShader = gl.Get("FRAGMENT_SHADER")
1820 types.Float = gl.Get("FLOAT")
1821 types.DepthTest = gl.Get("DEPTH_TEST")
1822 types.ColorBufferBit = gl.Get("COLOR_BUFFER_BIT")
1823 types.Triangles = gl.Get("TRIANGLES")
1824 types.UnsignedShort = gl.Get("UNSIGNED_SHORT")
1825 types.LEqual = gl.Get("LEQUAL")
1826 types.DepthBufferBit = gl.Get("DEPTH_BUFFER_BIT")
1827 types.LineLoop = gl.Get("LINE_LOOP")
1828 types.Line = gl.Get("LINES")
1829 enabled := gl.Call("getExtension", "OES_element_index_uint")
1830 if !enabled.Truthy() {
1831 return js.Global().Get("Error").New("missing extension: OES_element_index_uint")
1832 }
1833 types.UnsignedInt = gl.Get("UNSIGNED_INT")
1834 return js.Null()
1835}
1836
1837func sliceToByteSlice(s interface{}) []byte {
1838 switch s := s.(type) {
1839 case []int8:
1840 h := (*r.SliceHeader)(u.Pointer(&s)) // nolint
1841 return *(*[]byte)(u.Pointer(h)) // nolint
1842 case []int16:
1843 h := (*r.SliceHeader)(u.Pointer(&s)) // nolint
1844 h.Len *= 2
1845 h.Cap *= 2
1846 return *(*[]byte)(u.Pointer(h)) // nolint
1847 case []int32:
1848 h := (*r.SliceHeader)(u.Pointer(&s)) // nolint
1849 h.Len *= 4
1850 h.Cap *= 4
1851 return *(*[]byte)(u.Pointer(h)) // nolint
1852 case []int64:
1853 h := (*r.SliceHeader)(u.Pointer(&s)) // nolint
1854 h.Len *= 8
1855 h.Cap *= 8
1856 return *(*[]byte)(u.Pointer(h)) // nolint
1857 case []uint8:
1858 return s
1859 case []uint16:
1860 h := (*r.SliceHeader)(u.Pointer(&s)) // nolint
1861 h.Len *= 2
1862 h.Cap *= 2
1863 return *(*[]byte)(u.Pointer(h)) // nolint
1864 case []uint32:
1865 h := (*r.SliceHeader)(u.Pointer(&s)) // nolint
1866 h.Len *= 4
1867 h.Cap *= 4
1868 return *(*[]byte)(u.Pointer(h)) // nolint
1869 case []uint64:
1870 h := (*r.SliceHeader)(u.Pointer(&s)) // nolint
1871 h.Len *= 8
1872 h.Cap *= 8
1873 return *(*[]byte)(u.Pointer(h)) // nolint
1874 case []float32:
1875 h := (*r.SliceHeader)(u.Pointer(&s)) // nolint
1876 h.Len *= 4
1877 h.Cap *= 4
1878 return *(*[]byte)(u.Pointer(h)) // nolint
1879 case []float64:
1880 h := (*r.SliceHeader)(u.Pointer(&s)) // nolint
1881 h.Len *= 8
1882 h.Cap *= 8
1883 return *(*[]byte)(u.Pointer(h)) // nolint
1884 default:
1885 // panic("jsutil: unexpected value at sliceToBytesSlice: " + r.TypeOf(s).String())
1886 panic("jsutil: unexpected value at sliceToBytesSlice: ")
1887 }
1888}
1889
1890const bo = "byteOffset"
1891const bl = "byteLength"
1892const b = "buffer"
1893const u8a = "Uint8Array"
1894
1895// S2TA converts Slice To TypedArray
1896func S2TA(s interface{}) js.Value {
1897 switch s := s.(type) {
1898 case []int8:
1899 a := js.Global().Get(u8a).New(len(s))
1900 js.CopyBytesToJS(a, sliceToByteSlice(s))
1901 runtime.KeepAlive(s)
1902 buf := a.Get(b)
1903 return js.Global().Get("Int8Array").New(buf, a.Get(bo), a.Get(bl))
1904 case []int16:
1905 a := js.Global().Get(u8a).New(len(s) * 2)
1906 js.CopyBytesToJS(a, sliceToByteSlice(s))
1907 runtime.KeepAlive(s)
1908 buf := a.Get(b)
1909 return js.Global().Get("Int16Array").New(buf, a.Get(bo), a.Get(bl).Int()/2)
1910 case []int32:
1911 a := js.Global().Get(u8a).New(len(s) * 4)
1912 js.CopyBytesToJS(a, sliceToByteSlice(s))
1913 runtime.KeepAlive(s)
1914 buf := a.Get(b)
1915 return js.Global().Get("Int32Array").New(buf, a.Get(bo), a.Get(bl).Int()/4)
1916 case []uint8:
1917 a := js.Global().Get(u8a).New(len(s))
1918 js.CopyBytesToJS(a, s)
1919 runtime.KeepAlive(s)
1920 return a
1921 case []uint16:
1922 a := js.Global().Get(u8a).New(len(s) * 2)
1923 js.CopyBytesToJS(a, sliceToByteSlice(s))
1924 runtime.KeepAlive(s)
1925 buf := a.Get(b)
1926 return js.Global().Get("Uint16Array").New(buf, a.Get(bo), a.Get(bl).Int()/2)
1927 case []uint32:
1928 a := js.Global().Get(u8a).New(len(s) * 4)
1929 js.CopyBytesToJS(a, sliceToByteSlice(s))
1930 runtime.KeepAlive(s)
1931 buf := a.Get(b)
1932 return js.Global().Get("Uint32Array").New(buf, a.Get(bo), a.Get(bl).Int()/4)
1933 case []float32:
1934 a := js.Global().Get(u8a).New(len(s) * 4)
1935 js.CopyBytesToJS(a, sliceToByteSlice(s))
1936 runtime.KeepAlive(s)
1937 buf := a.Get(b)
1938 return js.Global().Get("Float32Array").New(buf, a.Get(bo), a.Get(bl).Int()/4)
1939 case []float64:
1940 a := js.Global().Get(u8a).New(len(s) * 8)
1941 js.CopyBytesToJS(a, sliceToByteSlice(s))
1942 runtime.KeepAlive(s)
1943 buf := a.Get(b)
1944 return js.Global().Get("Float64Array").New(buf, a.Get(bo), a.Get(bl).Int()/8)
1945 default:
1946 // panic("jsutil: unexpected value at S2TA: " + r.TypeOf(s).String())
1947 panic("jsutil: unexpected value at S2TA: ")
1948 }
1949}
1950
1951