3 const SELECTED_ITEM_BG_CLASS = 'bg-slate-500';
4 const SELECTED_ITEM_BG_OPACITY_CLASS = 'bg-opacity-50';
5 const SELECTED_ITEM_BG_CLASS_DARK = 'dark:bg-slate-600';
6 var isExampleSelected = false;
7 var clickOutsideSuggestionsListenerAdded = false;
9 function navItemOnClick(itemId) {
10 // needed so a element isn't hidden behind the navbar on scroll
11 const navbarOffsetHeight = document.getElementById('top-navbar').offsetHeight;
13 // remove the SELECTED_ITEM_BG_CLASS class from all nav items
14 const navItems = document.getElementsByClassName('nav-item');
15 const ovNavItems = document.getElementsByClassName("overlay-item");
16 [...navItems, ...ovNavItems].forEach(element => {
17 element.classList.remove(SELECTED_ITEM_BG_CLASS);
18 element.classList.remove(SELECTED_ITEM_BG_OPACITY_CLASS);
19 element.classList.remove(SELECTED_ITEM_BG_CLASS_DARK);
22 const item = document.getElementById(itemId);
23 item.classList.add(SELECTED_ITEM_BG_CLASS);
24 item.classList.add(SELECTED_ITEM_BG_OPACITY_CLASS);
25 item.classList.add(SELECTED_ITEM_BG_CLASS_DARK);
26 const otherItemsId = itemId.includes('ov-') ? itemId.replace('ov-', '') : 'ov-' + itemId;
27 const otherItem = document.getElementById(otherItemsId);
28 otherItem.classList.add(SELECTED_ITEM_BG_CLASS);
29 otherItem.classList.add(SELECTED_ITEM_BG_OPACITY_CLASS);
30 otherItem.classList.add(SELECTED_ITEM_BG_CLASS_DARK);
36 document.body.scrollTop = 0;
37 document.documentElement.scrollTop = 0;
40 case "ov-nav-features":
42 //document.getElementById("section-features").scrollIntoView();
44 top: document.getElementById("section-features").offsetTop - navbarOffsetHeight,
51 //document.getElementById("section-stdio").scrollIntoView();
53 top: document.getElementById("section-stdio").offsetTop - navbarOffsetHeight,
58 case "ov-nav-pico-wireless":
59 case "nav-pico-wireless":
60 // document.getElementById("section-pico-wireless").scrollIntoView();
62 top: document.getElementById("section-pico-wireless").offsetTop - navbarOffsetHeight,
67 case "ov-nav-code-gen":
69 // document.getElementById("section-code-gen").scrollIntoView();
71 top: document.getElementById("section-code-gen").offsetTop - navbarOffsetHeight,
76 case "ov-nav-debugger":
78 // document.getElementById("section-debugger").scrollIntoView();
80 top: document.getElementById("section-debugger").offsetTop - navbarOffsetHeight,
89 window.hideCustomInputs = function (divs, disable) {
91 //const inputAndSelects = div.querySelectorAll('input, select');
92 /*inputAndSelects.forEach(inputOrSelect => {
93 inputOrSelect.disabled = disable;
96 div.classList.add('hidden');
98 div.classList.remove('hidden');
103 window.toggleCreateFromExampleMode = function (forceOn, forceOff) {
104 const createFromExampleBtn = document.getElementById('btn-create-from-example');
105 const projectNameInput = document.getElementById('inp-project-name');
106 var isExampleMode = createFromExampleBtn ? createFromExampleBtn.getAttribute('data-example-mode') === 'true' : true;
107 const projectOptionsDivs = document.querySelectorAll('.project-options');
108 const examplesList = document.getElementById('examples-list');
109 const projectNameGrid = document.getElementById('project-name-grid');
110 const projectNameDropdownButton = document.getElementById('project-name-dropdown-button');
111 const defaultBoardTypeOption = document.getElementById('sel-default');
113 if (isExampleMode && (forceOn === undefined || !forceOn) && (forceOff === undefined || forceOff)) {
114 // clear input to avoid crashing the webview
115 projectNameInput.value = '';
117 if (createFromExampleBtn) {
118 createFromExampleBtn.setAttribute('data-example-mode', 'false');
119 createFromExampleBtn.innerText = 'Example';
120 // add md:grid-cols-2 from projectNameGrid
121 projectNameGrid.classList.add('md:grid-cols-2');
122 // hide dropdown button
123 projectNameDropdownButton.classList.add('hidden');
124 // crashes the webview
125 //projectNameInput.required = true;
127 // crashes the webview
128 /*if (window.removeExampleItems) {
129 window.removeExampleItems();
133 if (defaultBoardTypeOption) {
134 defaultBoardTypeOption.hidden = true;
135 defaultBoardTypeOption.disabled = true;
137 // if selected switch selection to first not hidden option
138 if (defaultBoardTypeOption.selected) {
139 const boardTypeSelector = document.getElementById('sel-board-type');
141 if (boardTypeSelector) {
142 // select first not hidden option
143 for (let i = 0; i < boardTypeSelector.options.length; i++) {
144 const option = boardTypeSelector.options[i];
146 // Check if the option is not hidden
147 if (option.style.display !== 'none' && option.hidden === false) {
148 boardTypeSelector.selectedIndex = i;
156 if (projectNameInput) {
157 // old datalist approach: projectNameInput.setAttribute('list', undefined);
158 // remove keyup event listener from projectNameInput if it exists
159 if (window.projectNameInputOnKeyup) {
160 projectNameInput.removeEventListener('keyup', window.projectNameInputOnKeyup);
162 projectNameInput.setAttribute('placeholder', 'Project name');
165 if (projectOptionsDivs) {
166 hideCustomInputs(projectOptionsDivs, false);
168 } else if (forceOff === undefined || !forceOff) {
169 if (createFromExampleBtn) {
170 createFromExampleBtn.setAttribute('data-example-mode', 'true');
171 createFromExampleBtn.innerText = 'Custom';
172 // remove md:grid-cols-2 from projectNameGrid
173 projectNameGrid.classList.remove('md:grid-cols-2');
174 // show dropdown button
175 projectNameDropdownButton.classList.remove('hidden');
177 // crashes the webview
178 // projectName required has issues with the suggestions
179 //projectNameInput.required = false;
182 if (projectNameInput && examplesList && typeof examples !== 'undefined') {
183 // clear input to avoid crashing the webview
184 projectNameInput.value = '';
186 //projectNameInput.setAttribute('list', "examples-list");
187 projectNameInput.setAttribute('placeholder', 'Select an example');
189 if (defaultBoardTypeOption) {
190 defaultBoardTypeOption.hidden = false;
191 defaultBoardTypeOption.disabled = false;
192 defaultBoardTypeOption.selected = true;
195 window.removeExampleItems = window.removeExampleItems || function () {
196 if (window.selectedSuggestion) {
197 window.selectedSuggestion.classList.remove('hovered');
198 window.selectedSuggestion.classList.remove('unhovered');
199 window.selectedSuggestion = null;
201 if (examplesList !== null) {
202 projectNameInput.removeEventListener('keydown', window.suggestionsOnKeydownNav);
203 projectNameDropdownButton.removeEventListener('keydown', window.suggestionsOnKeyupNav);
204 projectNameInput.classList.replace('rounded-t-lg', 'rounded-lg');
205 projectNameDropdownButton.classList.replace("rounded-tr-lg", "rounded-r-lg");
206 examplesList.classList.remove('border');
209 examplesList.innerHTML = '';
213 window.examplesListSelect = window.examplesListSelect || function (exampleName) {
214 projectNameInput.value = exampleName;
215 // Create and dispatch an input event, for the input event listener to be triggered
216 projectNameInput.dispatchEvent(new Event('input', {
220 removeExampleItems();
223 window.removeClickOutsideSuggestionsListener = window.removeClickOutsideSuggestionsListener || function () {
224 document.body.removeEventListener('click', handleOutsideSuggestionsClick);
225 clickOutsideSuggestionsListenerAdded = false;
228 window.handleOutsideSuggestionsClick = window.handleOutsideSuggestionsClick || function (event) {
229 // check if the clicked element is not inside the examplesList
230 if (!examplesList.contains(event.target) && event.target !== projectNameDropdownButton && event.target !== projectNameInput) {
231 // click occurred outside the suggestions "popup" so remove the suggestions
232 removeExampleItems();
233 removeClickOutsideSuggestionsListener();
237 window.suggestionsOnKeydownNav = window.suggestionsOnKeydownNav || function (event, arg2) {
238 if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
242 if (examplesList.childNodes.length === 0) {
245 const selected = window.selectedSuggestion;
246 const suggestions = document.querySelectorAll('.examples-list-suggestion');
247 if (suggestions.length === 0) {
250 event.preventDefault();
251 event.stopPropagation();
252 const index = selected ? Array.from(suggestions).indexOf(selected) : -1;
255 selected.classList.remove('hovered');
256 selected.classList.add('unhovered');
259 if (event.key === 'ArrowDown') {
260 if (index === suggestions.length - 1) {
261 suggestions[0].classList.add('hovered');
262 suggestions[0].classList.remove('unhovered');
263 window.selectedSuggestion = suggestions[0];
265 suggestions[index + 1].classList.add('hovered');
266 suggestions[index + 1].classList.remove('unhovered');
267 window.selectedSuggestion = suggestions[index + 1];
271 suggestions[suggestions.length - 1].classList.add('hovered');
272 suggestions[suggestions.length - 1].classList.remove('unhovered');
273 window.selectedSuggestion = suggestions[suggestions.length - 1];
275 suggestions[index - 1].classList.add('hovered');
276 suggestions[index - 1].classList.remove('unhovered');
277 window.selectedSuggestion = suggestions[index - 1];
280 window.isElementInView = window.isElementInView || ((el, container) => {
281 const containerRect = container.getBoundingClientRect();
282 const elementRect = el.getBoundingClientRect();
284 // Check if the element is outside of the visible container bounds
285 const isAbove = elementRect.top < containerRect.top;
286 const isBelow = elementRect.bottom > containerRect.bottom;
288 return !isAbove && !isBelow;
290 // if not in view then set window.ignoreNextMouseOver to true
291 if (!isElementInView(window.selectedSuggestion, examplesList)) {
292 window.ignoreNextMouseOver = true;
294 // Scroll the selected suggestion into view
295 window.selectedSuggestion.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'nearest' });
297 } else if (event.key === 'Enter') {
301 if (examplesList.childNodes.length === 0) {
304 event.preventDefault();
305 event.stopPropagation();
306 if (window.selectedSuggestion) {
307 window.selectedSuggestion.click();
309 // check if one example is exactly the same as the input value and select it
310 const suggestions = document.querySelectorAll('.examples-list-suggestion');
311 const equalElement = Array.from(suggestions).find(suggestion => {
312 const innerHTML = suggestion.innerHTML.trim(); // Get innerHTML and trim whitespace
313 return innerHTML.startsWith('<b>') && innerHTML.endsWith('</b>');
316 equalElement.click();
319 } else if (event.key === 'Escape') {
323 if (examplesList.childNodes.length === 0) {
326 event.preventDefault();
327 event.stopPropagation();
328 removeExampleItems();
336 window.projectNameInputOnKeyup = window.projectNameInputOnKeyup || function (e) {
337 // call to see if this is a key that is even relevant for the suggestions
338 if (window.suggestionsOnKeydownNav(e, true)) {
339 // return as true means the event was handled
343 removeExampleItems();
345 if (!clickOutsideSuggestionsListenerAdded) {
346 clickOutsideSuggestionsListenerAdded = true;
348 // add on click event listener to
349 document.body.addEventListener('click', handleOutsideSuggestionsClick);
352 const isInputEmpty = projectNameInput.value === "";
353 for (let i of Object.keys(examples).sort()) {
354 // startsWith was to strict for the examples name format we use
355 if (isInputEmpty || i.toLowerCase().includes(projectNameInput.value.toLowerCase())) {
356 // TODO: check if not added multiple times
357 projectNameInput.addEventListener('keydown', suggestionsOnKeydownNav);
358 projectNameDropdownButton.addEventListener('keydown', suggestionsOnKeydownNav);
359 projectNameInput.classList.replace('rounded-lg', 'rounded-t-lg');
360 projectNameDropdownButton.classList.replace("rounded-r-lg", "rounded-tr-lg");
361 examplesList.classList.add('border');
364 let listItem = document.createElement("li");
365 // one common class name
366 listItem.classList.add("examples-list-suggestion");
367 listItem.style.cursor = "pointer";
368 // listItem.setAttribute("onclick", "examplesListSelect('" + i + "')");
369 // added as event listener because of content security policy
370 listItem.addEventListener("click", (event) => {
371 event.stopPropagation();
372 removeClickOutsideSuggestionsListener();
373 examplesListSelect(i);
375 listItem.addEventListener('mouseover', () => {
376 if (window.ignoreNextMouseOver) {
377 // check if element is now in view if not then don't set ignoreMouseOver to false
378 if (window.isElementInView(window.selectedSuggestion, examplesList)) {
379 window.ignoreNextMouseOver = false;
384 if (window.selectedSuggestion) {
385 if (window.selectedSuggestion === listItem) {
386 // so it can automatically lose the hover effect
387 listItem.classList.remove('hovered');
388 listItem.classList.remove('unhovered');
391 window.selectedSuggestion.classList.remove('hovered');
392 window.selectedSuggestion.classList.add('unhovered');
395 window.selectedSuggestion = listItem; // Set the hovered element as "selected"
396 listItem.classList.add('hovered');
397 listItem.classList.remove('unhovered');
399 listItem.addEventListener('mouseout', () => {
400 if (window.ignoreNextMouseOver) {
401 // check if element is now in view if not then don't set ignoreMouseOver to false
402 if (window.isElementInView(window.selectedSuggestion, examplesList)) {
403 window.ignoreNextMouseOver = false;
408 listItem.classList.remove('hovered');
409 listItem.classList.add('unhovered');
410 if (window.selectedSuggestion === listItem) {
411 window.selectedSuggestion = undefined;
413 window.selectedSuggestion.classList.remove('hovered');
414 window.selectedSuggestion.classList.add('unhovered');
418 // display matched part as bold text
419 // this is for .startsWith selector above
420 //let word = isInputEmpty ? "" : "<b>" + i.substr(0, projectNameInput.value.length) + "</b>";
421 //word += i.substr(projectNameInput.value.length);
422 // this is for .includes selector above
423 const startIndex = isInputEmpty ? 0 : i.indexOf(projectNameInput.value);
424 let word = isInputEmpty ? "" : i.substring(0, startIndex);
425 word += isInputEmpty ? "" : "<b>" + i.substring(startIndex, startIndex + projectNameInput.value.length) + "</b>";
426 word += i.substring(startIndex + projectNameInput.value.length);
428 // set value of li elemetn
429 listItem.innerHTML = word;
430 examplesList.appendChild(listItem);
435 projectNameDropdownButton.addEventListener('click', (event) => {
436 // without this the webview crashes if project name input contains any text
437 event.preventDefault();
439 if (examplesList.childNodes.length === 0) {
440 // this is required to prevent the outside suggestions listener to fire after it has been
442 event.stopPropagation();
443 projectNameInputOnKeyup(event);
445 removeExampleItems();
449 projectNameInput.addEventListener('keyup', projectNameInputOnKeyup);
452 if (projectOptionsDivs) {
453 hideCustomInputs(projectOptionsDivs, true);
458 //run navItemOnClick after page loaded
459 window.onload = function () {
460 // pre-select the first nav item
461 const navItems = document.getElementsByClassName('nav-item') ?? [];
462 const ovNavItems = document.getElementsByClassName('overlay-item');
463 Array.prototype.forEach.call([...navItems, ...ovNavItems], item => {
464 item.addEventListener('click', function () {
465 navItemOnClick(item.id);
468 navItemOnClick(navItems[0].id);
470 const projectNameInput = document.getElementById('inp-project-name');
471 const createFromExampleBtn = document.getElementById('btn-create-from-example');
473 if (projectNameInput) {
474 projectNameInput.addEventListener('input', function () {
475 var isExampleMode = createFromExampleBtn ? createFromExampleBtn.getAttribute('data-example-mode') === 'true' : true;
476 if (!isExampleMode) {
477 isExampleSelected = false;
481 //const examplesList = document.getElementById('examples-list');
482 //const exampleOptions = Array.from(examplesList.options).map(option => option.value);
484 const inputValue = projectNameInput.value;
485 const isValueInOptions = Object.keys(examples).includes(inputValue);
487 if (isValueInOptions) {
489 isExampleSelected = true;
491 // No example selected
492 isExampleSelected = false;
497 if (createFromExampleBtn) {
498 createFromExampleBtn.addEventListener('click', () => {
499 toggleCreateFromExampleMode();
503 if (forceCreateFromExample !== undefined && forceCreateFromExample) {
504 if (createFromExampleBtn) {
505 // display: none; example btn
506 createFromExampleBtn.classList.add('hidden');
509 toggleCreateFromExampleMode(true);
511 // hide if not force from example
512 const defaultBoardTypeOption = document.getElementById('sel-default');
513 if (defaultBoardTypeOption) {
514 defaultBoardTypeOption.hidden = true;
515 defaultBoardTypeOption.disabled = true;
519 // TODO: maybe can remove if option-board-type-pico2 disable is moved into state restore
520 const sdkSelector = document.getElementById('sel-pico-sdk');
522 if (parseInt(sdkSelector.value.split(".")[0]) < 2) {
523 const selPico2 = document.getElementById('option-board-type-pico2');
525 selPico2.disabled = true;
530 const burgerMenu = document.getElementById("burger-menu");
531 const navOverlay = document.getElementById("nav-overlay");
533 function toggleOverlay() {
534 navOverlay.classList.toggle("hidden");
537 function closeOverlay(e) {
538 if (!navOverlay.contains(e.target) && e.target !== burgerMenu) {
539 navOverlay.classList.add("hidden");
543 burgerMenu.addEventListener("click", toggleOverlay);
544 window.addEventListener("click", closeOverlay);
546 window.addEventListener("resize", function () {
547 if (window.innerWidth >= 1024) {
548 navOverlay.classList.add("hidden");