PageSourceSearch

https://findskill.ai/js/saved-courses.min.7bf7d0bea7d0257231c4f3bed2e93c84a38c3753b5a95954636bd9aec68d0bc4.js

js findskill.ai collected 2026-09-25 00:30:37 UTC 6,858 bytes, 1 lines download raw bytes

1(()=>{(function(){"use strict";var l="/api/saved-courses",b="ai_skills_saved_courses_cache",S=36e5,w=document.body.dataset.stripeEnabled==="true",c=w?10:1/0,e=null,t=new Set,j=!1,a=c,o=!1,d=!1,M=!1;function h(){if(!window.AuthModule||!window.AuthModule.getAccessToken)return null;var e=window.AuthModule.getAccessToken();return e?{Authorization:"Bearer "+e,"Content-Type":"application/json"}:null}function m(){return!!(window.AuthModule&&window.AuthModule.isLoggedIn&&window.AuthModule.isLoggedIn())}function k(){try{var e,t=localStorage.getItem(b);return t?(e=JSON.parse(t),e.timestamp&&Date.now()-e.timestamp<S?e:null):null}catch{return null}}function p(e,t,n,s){try{localStorage.setItem(b,JSON.stringify({courses:e,total:t,remaining:n,isPro:s,timestamp:Date.now()}))}catch{}}function C(){try{localStorage.removeItem(b)}catch{}}function x(){if(d)return!1;var n=k();return!!(n&&n.courses)&&(e=n.courses,o=n.isPro||!1,a=n.remaining,t=new Set(n.courses.map(function(e){return e.course_slug})),d=!0,document.dispatchEvent(new CustomEvent("savedCourses:cacheRestored",{detail:{count:n.courses.length,remaining:n.remaining,isPro:n.isPro}})),!0)}function n(){var s,o,n=0;window.SavedPromptsModule&&window.SavedPromptsModule.getSavedPrompts&&(n=window.SavedPromptsModule.getSavedPrompts().length),s=e?e.length:t.size,o=n+s,document.querySelectorAll("[data-saved-count]").forEach(function(e){e.textContent=o})}function i(e,t){window.showToast?window.showToast(e,t):window.AuthModule&&window.AuthModule.showToast&&window.AuthModule.showToast(e,t)}async function r(n){n=n||0;var s,i,c=h();if(!c){e=[],t.clear(),g([],0);return}try{if(i=await fetch(l,{method:"GET",headers:c}),i.status===401&&n<1&&window.AuthModule&&window.AuthModule.refreshSession)try{return await window.AuthModule.refreshSession(),r(n+1)}catch{}if(!i.ok)throw new Error("Failed to fetch saved courses");s=await i.json(),e=s.courses||[],o=s.is_pro||!1,a=s.remaining,t=new Set(e.map(function(e){return e.course_slug})),p(e,s.total,s.remaining,o),g(e,s.total,s.remaining,o)}catch(n){console.error("[SavedCourses] Fetch error:",n),e=[],t.clear(),g([],0)}}function g(e,t,n,s){document.dispatchEvent(new CustomEvent("savedCourses:loaded",{detail:{courses:e,count:e.length,total:t,remaining:n,isPro:s}}))}async function y(s){if(!m())return f(),{success:!1,error:"LOGIN_REQUIRED"};var r,d,u=h();if(!u)return{success:!1,error:"NOT_AUTHENTICATED"};try{return d=await fetch(l,{method:"POST",headers:u,body:JSON.stringify(s)}),r=await d.json(),d.ok?(e&&(e.unshift(r.saved),p(e,e.length,r.remaining,o)),t.add(s.course_slug),a=r.remaining,n(),{success:!0,saved:r.saved,remaining:r.remaining}):(r.code==="LIMIT_REACHED"&&(i("You’ve reached the free limit of "+c+" saved items. Upgrade to Pro for unlimited saves!","warning"),document.dispatchEvent(new CustomEvent("savedCourses:limitReached",{detail:{limit:c}}))),r.code==="ALREADY_SAVED"?(t.add(s.course_slug),{success:!0,alreadySaved:!0}):{success:!1,error:r.code,message:r.error})}catch(e){return console.error("[SavedCourses] Save error:",e),{success:!1,error:"NETWORK_ERROR"}}}async function _(s){if(!m())return f(),{success:!1,error:"LOGIN_REQUIRED"};var i,r,c=h();if(!c)return{success:!1,error:"NOT_AUTHENTICATED"};try{return r=await fetch(l+"/"+encodeURIComponent(s),{method:"DELETE",headers:c}),i=await r.json(),r.ok?(e&&(e=e.filter(function(e){return e.course_slug!==s}),p(e,e.length,i.remaining,o)),t.delete(s),a=i.remaining,n(),{success:!0,remaining:i.remaining}):{success:!1,error:i.code}}catch(e){return console.error("[SavedCourses] Unsave error:",e),{success:!1,error:"NETWORK_ERROR"}}}async function E(e){if(!m())return{};var t,s,n=h();if(!n)return{};try{if(t=await fetch(l+"/bulk-check",{method:"POST",headers:n,body:JSON.stringify({course_slugs:e})}),!t.ok)throw new Error("Bulk check failed");return s=await t.json(),s.saved||{}}catch(e){return console.error("[SavedCourses] Bulk check error:",e),{}}}function f(){var e=document.querySelector("[data-show-auth]");e?e.click():window.AuthModule&&window.AuthModule.openAuthModal&&window.AuthModule.openAuthModal(),i("Sign in to save courses to your account","
1info")}function u(e,t){t?(e.classList.add("saved"),e.setAttribute("aria-label","Remove from saved courses"),e.setAttribute("data-saved","true")):(e.classList.remove("saved"),e.setAttribute("aria-label","Save course"),e.removeAttribute("data-saved"));var n=e.querySelector(".icon-bookmark-outline"),s=e.querySelector(".icon-bookmark-filled");n&&s&&(n.style.display=t?"none":"block",s.style.display=t?"block":"none")}function s(){document.querySelectorAll(".btn-save-course").forEach(function(e){var n=e.dataset.courseSlug;n&&u(e,t.has(n))})}async function A(e){var o,a,r,c,s=e.target.closest(".btn-save-course");if(!s)return;if(o=s.dataset.courseSlug,!o)return;if(e.preventDefault(),e.stopPropagation(),s.dataset.pending==="true")return;if(s.dataset.pending="true",!m()){f(),s.dataset.pending="";return}r=t.has(o),u(s,!r),r?t.delete(o):t.add(o),n(),s.classList.add("animate-bounce"),setTimeout(function(){s.classList.remove("animate-bounce")},500),r?(a=await _(o),a.success?(i("Course removed from saved","info"),O("unsave_course",{course_slug:o})):a.error!=="LOGIN_REQUIRED"&&(u(s,!0),t.add(o),n(),i("Failed to remove course","error"))):(c={course_slug:o,course_title:s.dataset.courseTitle||"",course_description:s.dataset.courseDescription||"",course_difficulty:s.dataset.courseDifficulty||"beginner",course_duration:s.dataset.courseDuration||"",course_lesson_count:parseInt(s.dataset.courseLessonCount,10)||0,is_premium:s.dataset.coursePremium==="true"},a=await y(c),a.success?(a.alreadySaved||i("Course saved!","success"),O("save_course",{course_slug:o})):a.error!=="LOGIN_REQUIRED"&&(u(s,!1),t.delete(o),n(),i("Failed to save course","error"))),s.dataset.pending=""}function O(e,t){typeof gtag!="undefined"&&gtag("event",e,{event_category:"engagement",...t}),window.location.hostname==="localhost"&&console.log("[SavedCourses] Track:",e,t)}async function v(){if(j)return;x()&&(s(),n()),await r(),s(),n(),document.addEventListener("click",A),document.addEventListener("auth:login",async function(){e=null,t.clear(),d=!1,await r(),s(),n()}),document.addEventListener("auth:logout",function(){e=null,t.clear(),d=!1,C(),s(),n()}),document.addEventListener("auth:ready",async function(){e=null,t.clear(),await r(),s(),n()}),document.addEventListener("savedPrompts:loaded",function(){n()}),j=!0}document.readyState==="loading"?document.addEventListener("DOMContentLoaded",v):v(),window.SavedCoursesModule={init:v,save:y,unsave:_,isSaved:function(e){return t.has(e)},getSavedCourses:function(){return e||[]},getSavedSlugs:function(){return Array.from(t)},getRemaining:function(){return a},getLimit:function(){return w?c:null},isPro:function(){return o},refresh:async function(){e=null,await r(),s(),n()},bulkCheck:E,syncButtons:s}})()})()

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.