');for(const n of document.getElementsByTagName("script")){if(n.dataset.astroExec==="")continue;const o=n.getAttribute("type");if(o&&o!=="module"&&o!=="text/javascript")continue;const r=document.createElement("script");r.innerHTML=n.innerHTML;for(const i of n.attributes){if(i.name==="src"){const u=new Promise(a=>{r.onload=r.onerror=a});e=e.then(()=>u)}r.setAttribute(i.name,i.value)}r.dataset.astroExec="",n.replaceWith(r)}return e}const G=(e,t,n,o,r)=>{const i=W(t,e),u=document.title;document.title=o;let a=!1;if(e.href!==location.href&&!r)if(n.history==="replace"){const c=history.state;E({...n.state,index:c.index,scrollX:c.scrollX,scrollY:c.scrollY},"",e.href)}else be({...n.state,index:++v,scrollX:0,scrollY:0},"",e.href);if(document.title=u,R=e,i||(scrollTo({left:0,top:0,behavior:"instant"}),a=!0),r)scrollTo(r.scrollX,r.scrollY);else{if(e.hash){history.scrollRestoration="auto";const c=history.state;location.href=e.href,history.state||(E(c,""),i&&window.dispatchEvent(new PopStateEvent("popstate")))}else a||scrollTo({left:0,top:0,behavior:"instant"});history.scrollRestoration="manual"}};function Ee(e){const t=[];for(const n of e.querySelectorAll("head link[rel=stylesheet]"))if(!document.querySelector(`[${H}="${n.getAttribute(H)}"], link[rel=stylesheet][href="${n.getAttribute("href")}"]`)){const o=document.createElement("link");o.setAttribute("rel","preload"),o.setAttribute("as","style"),o.setAttribute("href",n.getAttribute("href")),t.push(new Promise(r=>{["load","error"].forEach(i=>o.addEventListener(i,r)),document.head.append(o)}))}return t}async function _(e,t,n,o,r){async function i(f){function l(d){const p=d.effect;return!p||!(p instanceof KeyframeEffect)||!p.target?!1:window.getComputedStyle(p.target,p.pseudoElement).animationIterationCount==="infinite"}const s=document.getAnimations();document.documentElement.setAttribute(P,f);const b=document.getAnimations().filter(d=>!s.includes(d)&&!l(d));return Promise.allSettled(b.map(d=>d.finished))}const u=async()=>{if(r==="animate"&&!n.transitionSkipped&&!e.signal.aborted)try{await i("old")}catch{}},a=document.title,c=await ye(e,n.viewTransition,u);G(c.to,c.from,t,a,o),V(me),r==="animate"&&(!n.transitionSkipped&&!c.signal.aborted?i("new").finally(()=>n.viewTransitionFinished()):n.viewTransitionFinished())}function Se(){return m?.controller.abort(),m={controller:new AbortController}}async function z(e,t,n,o,r){const i=Se();if(!N()||location.origin!==n.origin){i===m&&(m=void 0),location.href=n.href;return}const u=r?"traverse":o.history==="replace"?"replace":"push";if(u!=="traverse"&&x({scrollX,scrollY}),W(t,n)&&!o.formData&&(e!=="back"&&n.hash||e==="back"&&t.hash)){G(n,t,o,document.title,r),i===m&&(m=void 0);return}const a=await ge(t,n,e,u,o.sourceElement,o.info,i.controller.signal,o.formData,c);if(a.defaultPrevented||a.signal.aborted){i===m&&(m=void 0),a.signal.aborted||(location.href=n.href);return}async function c(s){const h=s.to.href,b={signal:s.signal};if(s.formData){b.method="POST";const w=s.sourceElement instanceof HTMLFormElement?s.sourceElement:s.sourceElement instanceof HTMLElement&&"form"in s.sourceElement?s.sourceElement.form:s.sourceElement?.closest("form");b.body=t!==void 0&&Reflect.get(HTMLFormElement.prototype,"attributes",w).getNamedItem("enctype")?.value==="application/x-www-form-urlencoded"?new URLSearchParams(s.formData):s.formData}const d=await Te(h,b);if(d===null){s.preventDefault();return}if(d.redirected){const w=new URL(d.redirected);if(w.origin!==s.to.origin){s.preventDefault();return}s.to=w}if(C??=new DOMParser,s.newDocument=C.parseFromString(d.html,d.mediaType),s.newDocument.querySelectorAll("noscript").forEach(w=>w.remove()),!s.newDocument.querySelector('[name="astro-view-transitions-enabled"]')&&!s.formData){s.preventDefault();return}const p=Ee(s.newDocument);p.length&&!s.signal.aborted&&await Promise.all(p)}async function f(){if(g&&g.viewTransition){try{g.viewTransition.skipTransition()}catch{}try{await g.viewTransition.updateCallbackDone}catch{}}return g={transitionSkipped:!1}}const l=await f();if(a.signal.aborted){i===m&&(m=void 0);return}if(document.documentElement.setAttribute(F,a.direction),I)l.viewTransition=document.startViewTransition(async()=>await _(a,o,l,r));else{const s=(async()=>{await Promise.resolve(),await _(a,o,l,r,K())})();l.viewTransition={updateCallbackDone:s,ready:s,finished:new Promise(h=>l.viewTransitionFinished=h),skipTransition:()=>{l.transitionSkipped=!0,document.documentElement.removeAttribute(P)},types:new Set}}l.viewTransition?.updateCallbackDone.finally(async()=>{await Ae(),j(),ve()}),l.viewTransition?.finished.finally(()=>{l.viewTransition=void 0,l===g&&(g=void 0),i===m&&(m=void 0),document.documentElement.removeAttribute(F),document.documentElement.removeAttribute(P)});try{await l.viewTransition?.updateCallbackDone}catch(s){const h=s;console.log("[astro]",h.name,h.message,h.stack)}}async function X(e,t){await z("forward",R,new URL(e,location.href),t??{})}function Re(e){if(!N()&&e.state){location.reload();return}if(e.state===null)return;const t=history.state,n=t.index,o=n>v?"forward":"back";v=n,z(o,R,new URL(location.href),{},t)}const Y=()=>{history.state&&(scrollX!==history.state.scrollX||scrollY!==history.state.scrollY)&&x({scrollX,scrollY})};{if(I||K()!=="none")if(R=new URL(location.href),addEventListener("popstate",Re),addEventListener("load",j),"onscrollend"in window)addEventListener("scrollend",Y);else{let e,t,n,o;const r=()=>{if(o!==history.state?.index){clearInterval(e),e=void 0;return}if(t===scrollY&&n===scrollX){clearInterval(e),e=void 0,Y();return}else t=scrollY,n=scrollX};addEventListener("scroll",()=>{e===void 0&&(o=history.state?.index,t=scrollY,n=scrollX,e=window.setInterval(r,50))},{passive:!0})}for(const e of document.getElementsByTagName("script"))U(e),e.dataset.astroExec=""}const J=new Set,S=new WeakSet;let D,Q,B=!1;function Le(e){B||(B=!0,D??=e?.prefetchAll,Q??=e?.defaultStrategy??"hover",ke(),Pe(),De(),Ie())}function ke(){for(const e of["touchstart","mousedown"])document.body.addEventListener(e,t=>{T(t.target,"tap")&&L(t.target.href,{ignoreSlowConnection:!0})},{passive:!0})}function Pe(){let e;document.body.addEventListener("focusin",o=>{T(o.target,"hover")&&t(o)},{passive:!0}),document.body.addEventListener("focusout",n,{passive:!0}),O(()=>{for(const o of document.getElementsByTagName("a"))S.has(o)||T(o,"hover")&&(S.add(o),o.addEventListener("mouseenter",t,{passive:!0}),o.addEventListener("mouseleave",n,{passive:!0}))});function t(o){const r=o.target.href;e&&clearTimeout(e),e=setTimeout(()=>{L(r)},80)}function n(){e&&(clearTimeout(e),e=0)}}function De(){let e;O(()=>{for(const t of document.getElementsByTagName("a"))S.has(t)||T(t,"viewport")&&(S.add(t),e??=xe(),e.observe(t))})}function xe(){const e=new WeakMap;return new IntersectionObserver((t,n)=>{for(const o of t){const r=o.target,i=e.get(r);o.isIntersecting?(i&&clearTimeout(i),e.set(r,setTimeout(()=>{n.unobserve(r),e.delete(r),L(r.href)},300))):i&&(clearTimeout(i),e.delete(r))}})}function Ie(){O(()=>{for(const e of document.getElementsByTagName("a"))T(e,"load")&&L(e.href)})}function L(e,t){e=e.replace(/#.*/,"");const n=t?.ignoreSlowConnection??!1;if(Ne(e,n))if(J.add(e),document.createElement("link").relList?.supports?.("prefetch")&&t?.with!=="fetch"){const o=document.createElement("link");o.rel="prefetch",o.setAttribute("href",e),document.head.append(o)}else fetch(e,{priority:"low"})}function Ne(e,t){if(!navigator.onLine||!t&&Z())return!1;try{const n=new URL(e,location.href);return location.origin===n.origin&&(location.pathname!==n.pathname||location.search!==n.search)&&!J.has(e)}catch{}return!1}function T(e,t){if(e?.tagName!=="A")return!1;const n=e.dataset.astroPrefetch;return n==="false"?!1:t==="tap"&&(n!=null||D)&&Z()?!0:n==null&&D||n===""?t===Q:n===t}function Z(){if("connection"in navigator){const e=navigator.connection;return e.saveData||/2g/.test(e.effectiveType)}return!1}function O(e){e();let t=!1;document.addEventListener("astro:page-load",()=>{if(!t){t=!0;return}e()})}let A=null;function Oe(){const e=document.querySelector('[name="astro-view-transitions-fallback"]');return e?e.getAttribute("content"):"animate"}function $(e){return e.dataset.astroReload!==void 0}const Me=e=>e.button&&e.button!==0||e.metaKey||e.ctrlKey||e.altKey||e.shiftKey;(I||Oe()!=="none")&&(document.addEventListener("click",e=>{let t=e.target;if(A=Me(e)?t:null,e.composed&&(t=e.composedPath()[0]),t instanceof Element&&(t=t.closest("a, area")),!(t instanceof HTMLAnchorElement)&&!(t instanceof SVGAElement)&&!(t instanceof HTMLAreaElement))return;const n=t instanceof HTMLElement?t.target:t.target.baseVal,o=t instanceof HTMLElement?t.href:t.href.baseVal,r=new URL(o,location.href).origin;$(t)||t.hasAttribute("download")||!t.href||n&&n!=="_self"||r!==location.origin||A||e.defaultPrevented||(e.preventDefault(),X(o,{history:t.dataset.astroHistory==="replace"?"replace":"auto",sourceElement:t}))}),document.addEventListener("submit",e=>{let t=e.target;const n=e.submitter,o=n&&n===A;if(A=null,t.tagName!=="FORM"||e.defaultPrevented||$(t)||o)return;const r=t,i=new FormData(r,n),u=typeof r.action=="string"?r.action:r.getAttribute("action"),a=typeof r.method=="string"?r.method:r.getAttribute("method");let c=n?.getAttribute("formaction")??u??location.pathname;const f=n?.getAttribute("formmethod")??a??"get";if(f==="dialog"||location.origin!==new URL(c,location.href).origin)return;const l={sourceElement:n??r};if(f==="get"){const s=new URLSearchParams(i),h=new URL(c);h.search=s.toString(),c=h.toString()}else l.formData=i;e.preventDefault(),X(c,l)}),Le({prefetchAll:!0}));
Wednesday 15 January 2020 at 16:19:52 GMT | 1 min read
View project
tic-tac-cok tic-tac-cok light mode UI tic-tac-cok
is the inaugural React application I developed upon completing a React course on Udemy, which I undertook in mid-2019. It’s a tic-tac-toe game inspired by a tutorial from a YouTube video by Web Dev Simplified, one of my favorite channel for learning web development.
In the video, the game was developed using vanilla JavaScript. I rewrote it in React, incorporating several enhancements such as an increased number of boxes, the ability to undo moves, restart or reset the game, and a light-dark theme toggler.
The game doesn’t include fancier features like player vs. AI or online multiplayer matches. To capture the authentic vibe of playing tic-tac-toe, the game is designed for two players competing on the same device.