1document.body.dataset.extraRegions='za'; 2(() => { 3 'use strict'; 4 const base=document.body.dataset.base,brand=document.body.dataset.brand; 5 const key='brand-list:'+brand,regionKey='brand-country:'+brand; 6 const validRegions=new Set(['us','gb','in','ca','au','ph','de','fr','br','mx',...(document.body.dataset.extraRegions||'').split(',').filter(code=>/^[a-z]{2}$/.test(code))]); 7 const read=(k,fallback)=>{try{return JSON.parse(localStorage.getItem(k))??fallback}catch{return fallback}}; 8 let saved=read(key,[]);if(!Array.isArray(saved))saved=[];saved=saved.filter(x=>typeof x==='string'); 9 let country=read(regionKey,'us');if(!validRegions.has(country))country='us'; 10 let timer; 11 function message(text){let node=document.querySelector('.storage-toast');if(!node){node=document.createElement('div');node.className='storage-toast';node.setAttribute('role','status');document.body.append(node)}node.textContent=text;node.hidden=false;clearTimeout(timer);timer=setTimeout(()=>node.hidden=true,3500);document.querySelectorAll('[data-storage-message]').forEach(n=>n.textContent=text)} 12 function write(k,v){try{localStorage.setItem(k,JSON.stringify(v));return true}catch{message('Your browser could not save this change. Check its storage settings.');return false}} 13 const menu=document.querySelector('.menu-toggle'),nav=document.querySelector('#mobile-nav'); 14 function closeMenu(){if(!menu||!nav)return;menu.setAttribute('aria-expanded','false');menu.setAttribute('aria-label','Open navigation');nav.hidden=true} 15 menu?.addEventListener('click',()=>{const open=menu.getAttribute('aria-expanded')==='true';menu.setAttribute('aria-expanded',String(!open));menu.setAttribute('aria-label',open?'Open navigation':'Close navigation');nav.hidden=open}); 16 document.addEventListener('keydown',e=>{if(e.key==='Escape'&&menu?.getAttribute('aria-expanded')==='true'){closeMenu();menu.focus()}}); 17 document.addEventListener('click',e=>{if(!nav?.hidden&&!nav?.contains(e.target)&&!menu?.contains(e.target))closeMenu()}); 18 nav?.addEventListener('click',e=>{if(e.target.closest('a'))closeMenu()}); 19 window.addEventListener('resize',()=>{if(menu&&getComputedStyle(menu).display==='none')closeMenu()}); 20 function syncButtons(){document.querySelectorAll('[data-save]').forEach(b=>{const yes=saved.includes(b.dataset.save);b.setAttribute('aria-pressed',String(yes));b.textContent=yes?'â In My List':'+ My List'})} 21 document.addEventListener('click',e=>{const b=e.target.closest('[data-save]');if(!b)return;const id=b.dataset.save,exists=saved.includes(id),next=exists?saved.filter(x=>x!==id):[...saved,id];if(write(key,next)){saved=next;syncButtons();message(exists?'Removed from My List':'Saved to My List');renderList()}}); 22 function syncCountry(){document.querySelectorAll('[data-country]').forEach(b=>b.setAttribute('aria-pressed',String(b.dataset.country===country)));document.querySelectorAll('[data-watch-title]').forEach(a=>a.href='https://9moviestv.cyou/home/'+(country==='gb'?'uk':country)+'/search?q='+encodeURIComponent(a.dataset.watchTitle));const notice=document.querySelector('[data-country-notice]'),button=document.querySelector('[data-country="'+country+'"]');if(notice&&button)notice.textContent='Viewing region: '+button.textContent+'.'} 23 document.querySelectorAll('[data-country]').forEach(b=>b.addEventListener('click',()=>{if(write(regionKey,b.dataset.country)){country=b.dataset.country;syncCountry();message('Country preference saved.')}})); 24 syncButtons();syncCountry(); 25 let catalog=null; 26 async function getCatalog(){if(catalog)return catalog;const response=await fetch(base+'data/catalog.json');if(!response.ok)throw new Error('Catalog request failed: '+response.status);const data=await response.json();if(!Array.isArray(data.titles))throw new Error('Invalid catalog');return catalog=data.titles} 27 function card(t){const c=document.createElement('article');c.className='card';const a=document.createElement('a');a.className='poster';a.href=t.route;const img=document.createElement('img');img.src=t.poster;img.alt=t.title+' ('+t.year+') poster';img.width=500;img.height=750;img.loading='lazy';a.append(img);if(t.rating>0){const r=document.createElement('span');r.className='rating';r.textContent='â '+Number(t.rating).toFixed(1);a.append(r)}const h=document.createElement('h3'),link=document.createElement('a');link.href=t.route;link.textContent=t.title;h.append(link);const p=document.createElement('p');p.textContent=t.year+' · '+(t.type==='movie'?'Movie':'TV Series');c.append(a,h,p);return c} 28 function draw(container,items){const grid=document.createElement('div');grid.className='poster-grid';
28items.forEach(t=>grid.append(card(t)));container.replaceChildren(grid)} 29 const list=document.querySelector('[data-watchlist]'); 30 async function renderList(){if(!list)return;try{const titles=await getCatalog(),items=titles.filter(t=>saved.includes(t.id));draw(list,items);document.querySelector('[data-list-empty]').hidden=items.length>0}catch{message('Your list could not load. Reload this page to try again.')}} 31 renderList(); 32 const form=document.querySelector('[data-filters]'); 33 function fold(v){return String(v).normalize('NFKD').replace(/[\u0300-\u036f]/g,'').toLocaleLowerCase().trim()} 34 async function filter(){if(!form)return;try{let items=[...await getCatalog()];const data=new FormData(form),q=fold(data.get('q')||''),type=data.get('type'),genre=data.get('genre'),year=data.get('year'),sort=data.get('sort');items=items.filter(t=>(!q||fold(t.title+' '+t.originalTitle).includes(q))&&(!type||t.type===type)&&(!genre||t.genres.includes(genre))&&(!year||String(t.year)===year));if(sort==='rating')items.sort((a,b)=>b.rating-a.rating);else if(sort==='year')items.sort((a,b)=>String(b.releaseDate).localeCompare(String(a.releaseDate)));else if(sort==='title')items.sort((a,b)=>a.title.localeCompare(b.title));else items.sort((a,b)=>b.popularity-a.popularity);draw(document.querySelector('[data-results]'),items);document.querySelector('[data-empty]').hidden=items.length>0;document.querySelector('[data-result-count]').textContent=items.length+' matching '+(items.length===1?'title':'titles');const params=new URLSearchParams();for(const [k,v] of data){if(v&&!(k==='sort'&&v==='popular'))params.set(k,v)}history.replaceState(null,'',base+'search/'+(params.size?'?'+params:''))}catch{message('Search could not load. Please reload and try again.')}} 35 if(form){const params=new URLSearchParams(location.search);for(const [k,v] of params){const field=form.elements.namedItem(k);if(field&&['q','type','genre','year','sort'].includes(k))field.value=v}form.addEventListener('submit',e=>{e.preventDefault();filter()});form.addEventListener('change',filter);filter()} 36 window.addEventListener('storage',e=>{if(e.key===key){const value=read(key,[]);saved=Array.isArray(value)?value.filter(x=>typeof x==='string'):[];syncButtons();renderList()}if(e.key===regionKey){const value=read(regionKey,'us');country=validRegions.has(value)?value:'us';syncCountry()}}); 37 const context=document.modelContext; 38 if(context?.registerTool){const lifecycle=new AbortController();const register=tool=>{try{Promise.resolve(context.registerTool(tool,{signal:lifecycle.signal})).catch(()=>{})}catch{}}; 39 register({name:'read_saved_titles',description:'Read the movies and TV shows saved in this browser for '+brand+'.',inputSchema:{type:'object',properties:{},additionalProperties:false},annotations:{readOnlyHint:true,untrustedContentHint:false},async execute(){const titles=await getCatalog();return {brand,titles:titles.filter(t=>saved.includes(t.id)).map(t=>({id:t.id,title:t.title,year:t.year,url:t.route}))}}}); 40 register({name:'set_saved_title',description:'Save or remove a catalog title in '+brand+' My List and update the visible saved state.',inputSchema:{type:'object',properties:{id:{type:'string'},saved:{type:'boolean'}},required:['id','saved'],additionalProperties:false},annotations:{readOnlyHint:false,untrustedContentHint:false},async execute(input){if(!input||typeof input.id!=='string'||typeof input.saved!=='boolean'||Object.keys(input).some(k=>!['id','saved'].includes(k)))throw new Error('Expected a title id and a boolean saved value.');const titles=await getCatalog();if(!titles.some(t=>t.id===input.id))throw new Error('Title is not in this catalog.');const next=input.saved?[...new Set([...saved,input.id])]:saved.filter(id=>id!==input.id);if(!write(key,next))throw new Error('Browser storage is unavailable.');saved=next;syncButtons();await renderList();message(input.saved?'Saved to My List':'Removed from My List');return {id:input.id,saved:input.saved}}}); 41 window.addEventListener('pagehide',()=>lifecycle.abort(),{once:true}); 42 } 43})();
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.