(function(){ var loader=document.getElementById('flipnote-editor-loader'); var statusEl=loader&&loader.querySelector('[data-flipnote-loader-status]'); var progressEl=loader&&loader.querySelector('.flipnote-editor-loader-progress'); var progressFill=loader&&loader.querySelector('[data-flipnote-loader-progress]'); var copyEl=loader&&loader.querySelector('.flipnote-editor-loader-copy'); var titleEl=loader&&loader.querySelector('.flipnote-editor-loader-title'); var drawingEngineScript='./assets/flipnote-editor/drawing-engine.js?v=2.47-advanced-eraser-paint-memory'; var textToolScript='./assets/flipnote-editor/text-tool.js?v=3.26.2'; var editorScript='./assets/flipnote-editor/editor.js?v=3.26.2-canvas'; // The server enumerates every image in web/static/flipnote-editor on each request. // This means newly-added button states (including manually-added tab art) join the // preload automatically without needing another JavaScript manifest update. // Keep a small explicit list for image assets used by the editor but stored elsewhere. var imageUrls=[]; var imageCacheBank=document.getElementById('flipnote-editor-image-cache'); var decodedImageCache=window.__flipnoteEditorImageCache=window.__flipnoteEditorImageCache||new Map(); document.querySelectorAll('img[src]').forEach(function(img){imageUrls.push(img.getAttribute('src'));}); document.querySelectorAll('*').forEach(function(node){ Array.from(node.attributes||[]).forEach(function(attr){ if(/^data-.*src$/i.test(attr.name)&&attr.value)imageUrls.push(attr.value); }); }); imageUrls=Array.from(new Set(imageUrls.concat(["./assets/flipnote-editor/base.png","./assets/flipnote-editor/checker.png","./assets/flipnote-editor/clear.png","./assets/flipnote-editor/clone-placeholder.png","./assets/flipnote-editor/closepages.png","./assets/flipnote-editor/colour.png","./assets/flipnote-editor/copy.png","./assets/flipnote-editor/copy_pressed.png","./assets/flipnote-editor/cut.png","./assets/flipnote-editor/delete-frame.png","./assets/flipnote-editor/downloadwebm.png","./assets/flipnote-editor/Draw.png","./assets/flipnote-editor/Draw_selected.png","./assets/flipnote-editor/earsHurt.png","./assets/flipnote-editor/editpattern.png","./assets/flipnote-editor/Erase.png","./assets/flipnote-editor/erase1.png","./assets/flipnote-editor/erase1_Adv.png","./assets/flipnote-editor/erase2.png","./assets/flipnote-editor/erase2_Adv.png","./assets/flipnote-editor/erase3.png","./assets/flipnote-editor/erase3_Adv.png","./assets/flipnote-editor/erase4.png","./assets/flipnote-editor/erase4_Adv.png","./assets/flipnote-editor/erase5.png","./assets/flipnote-editor/erase5_Adv.png","./assets/flipnote-editor/erase6.png","./assets/flipnote-editor/erase6_Adv.png","./assets/flipnote-editor/erase7.png","./assets/flipnote-editor/erase7_Adv.png","./assets/flipnote-editor/erase8.png","./assets/flipnote-editor/erase8_Adv.png","./assets/flipnote-editor/eraseaudio.png","./assets/flipnote-editor/erasebig.png","./assets/flipnote-editor/eraser.png","./assets/flipnote-editor/Erase_selected.png","./assets/flipnote-editor/fill.png","./assets/flipnote-editor/fill1.png","./assets/flipnote-editor/fill2.png","./assets/flipnote-editor/fill3.png","./assets/flipnote-editor/fill4.png","./assets/flipnote-editor/fill5.png","./assets/flipnote-editor/fill6.png","./assets/flipnote-editor/fill7.png","./assets/flipnote-editor/fill8.png","./assets/flipnote-editor/fill_selected.png","./assets/flipnote-editor/filmstrip-dots.png","./assets/flipnote-editor/first-frame.png","./assets/flipnote-editor/flip_x.png","./assets/flipnote-editor/flip_y.png","./assets/flipnote-editor/goodVolume.png","./assets/flipnote-editor/ImportImage.png","./assets/flipnote-editor/invert.png","./assets/flipnote-editor/last-frame.png","./assets/flipnote-editor/layer1.png","./assets/flipnote-editor/layer1_inactive.png","./assets/flipnote-editor/layer2.png","./assets/flipnote-editor/layer2_inactive.png","./assets/flipnote-editor/lightbox-placeholder.png","./assets/flipnote-editor/lightbox.png","./assets/flipnote-editor/lightbox_pressed.png","./assets/flipnote-editor/loop_disabled.png","./assets/flipnote-editor/loop_enabled.png","./assets/flipnote-editor/musicBarEmpty.png","./assets/flipnote-editor/musicImportalt.png","./assets/flipnote-editor/musicPlay.png","./assets/flipnote-editor/musicRec.png","./assets/flipnote-editor/musicStop.png","./assets/flipnote-editor/new-frame.png","./assets/flipnote-editor/next-frame.png","./assets/flipnote-editor/openpages.png","./assets/flipnote-editor/openppm.png","./assets/flipnote-editor/pageCopy.png","./assets/flipnote-editor/pageErase.png","./assets/flipnote-editor/pageInsert.png","./assets/flipnote-editor/pagePaste.png","./assets/flipnote-editor/Paint.png","./assets/flipnote-editor/paint1.png","./assets/flipnote-editor/paint1_Adv.png","./assets/flipnote-editor/paint2.png","./assets/flipnote-editor/paint2_Adv.png","./assets/flipnote-editor/paint3.png","./assets/flipnote-editor/paint3_Adv.png","./assets/flipnote-editor/paint4.png","./assets/flipnote-editor/paint4_Adv.png","./assets/flipnote-editor/paint5.png","./assets/flipnote-editor/paint5_Adv.png","./assets/flipnote-editor/paint6.png","./assets/flipnote-editor/paint6_Adv.png","./assets/flipnote-editor/paint7.png","./assets/flipnote-editor/paint7_Adv.png","./assets/flipnote-editor/paint8.png","./assets/flipnote-editor/paint8_Adv.png","./assets/flipnote-editor/Paint_selected.png","./assets/flipnote-editor/paper-placeholder.png","./assets/flipnote-editor/paperB.png","./assets/flipnote-editor/paperW.png","./assets/flipnote-editor/paste.png","./assets/flipnote-editor/pen.png","./assets/flipnote-editor/pen1.png","./assets/flipnote-editor/pen1_Adv.png","./assets/flipnote-editor/pen2.png","./assets/flipnote-editor/pen2_Adv.png","./assets/flipnote-editor/pen3.png","./assets/flipnote-editor/pen3_Adv.png","./assets/flipnote-editor/pen4.png","./assets/flipnote-editor/pen4_Adv.png","./assets/flipnote-editor/pen5.png","./assets/flipnote-editor/pen5_Adv.png","./assets/flipnote-editor/pen6.png","./assets/flipnote-editor/pen6_Adv.png","./assets/flipnote-editor/pen7.png","./assets/flipnote-editor/pen7_Adv.png","./assets/flipnote-editor/pen8.png","./assets/flipnote-editor/pen8_Adv.png","./assets/flipnote-editor/play.png","./assets/flipnote-editor/previous-frame.png","./assets/flipnote-editor/redo.png","./assets/flipnote-editor/redo_grey.png","./assets/flipnote-editor/resize_larger.png","./assets/flipnote-editor/resize_smaller.png","./assets/flipnote-editor/rotate_anticlockwise.png","./assets/flipnote-editor/rotate_clockwise.png","./assets/flipnote-editor/selbase.png","./assets/flipnote-editor/select.png","./assets/flipnote-editor/selectionCopy.png","./assets/flipnote-editor/selectionCut.png","./assets/flipnote-editor/select_pressed.png","./assets/flipnote-editor/select_y.png","./assets/flipnote-editor/shiftl.png","./assets/flipnote-editor/shiftl_active.png","./assets/flipnote-editor/soundBarEmpry.png","./assets/flipnote-editor/soundImport1.png","./assets/flipnote-editor/soundImport1alt.png","./assets/flipnote-editor/soundImport2.png","./assets/flipnote-editor/soundImport2alt.png","./assets/flipnote-editor/soundImport3.png","./assets/flipnote-editor/soundImport3alt.png","./assets/flipnote-editor/soundPlay1.png","./assets/flipnote-editor/soundPlay2.png","./assets/flipnote-editor/soundPlay3.png","./assets/flipnote-editor/soundRecord1.png","./assets/flipnote-editor/soundRecord2.png","./assets/flipnote-editor/soundRecord3.png","./assets/flipnote-editor/soundStop1.png","./assets/flipnote-editor/soundStop2.png","./assets/flipnote-editor/soundStop3.png","./assets/flipnote-editor/spacer.png","./assets/flipnote-editor/speed1.png","./assets/flipnote-editor/speed2.png","./assets/flipnote-editor/speed3.png","./assets/flipnote-editor/speed4.png","./assets/flipnote-editor/speed5.png","./assets/flipnote-editor/speed6.png","./assets/flipnote-editor/speed7.png","./assets/flipnote-editor/speed8.png","./assets/flipnote-editor/stop.png","./assets/flipnote-editor/tab-1.png","./assets/flipnote-editor/tab-2.png","./assets/flipnote-editor/tab-3.png","./assets/flipnote-editor/tab-4.png","./assets/flipnote-editor/tab-5.png","./assets/flipnote-editor/tab1.png","./assets/flipnote-editor/tab1_selected.png","./assets/flipnote-editor/tab2.png","./assets/flipnote-editor/tab2_selected.png","./assets/flipnote-editor/tab3.png","./assets/flipnote-editor/tab3_selected.png","./assets/flipnote-editor/tab4.png","./assets/flipnote-editor/tab4_selected.png","./assets/flipnote-editor/tab5.png","./assets/flipnote-editor/tab5_selected.png","./assets/flipnote-editor/transform.png","./assets/flipnote-editor/transform_active.png","./assets/flipnote-editor/undo.png","./assets/flipnote-editor/undo_grey.png","./assets/flipnote-editor/webm.png"]).filter(Boolean))); var fontJobs=[{css:'16px "NDS BIOS"',sample:'Preloading assets 00 / 00'}]; var completed=0; var total=imageUrls.length+fontJobs.length+5; function updateProgress(){ var safeTotal=Math.max(total,1); var percent=Math.max(0,Math.min(100,Math.round((completed/safeTotal)*100))); if(statusEl)statusEl.textContent='Preloading assets '+Math.min(completed,total)+' / '+total; if(progressFill)progressFill.style.width=percent+'%'; if(progressEl)progressEl.setAttribute('aria-valuenow',String(percent)); } function doneOne(){completed+=1;updateProgress();} function preloadImage(url){ var cached=decodedImageCache.get(url); if(cached&&cached.promise)return cached.promise.then(function(){doneOne();}); var image=new Image(); image.alt=''; image.decoding='sync'; image.loading='eager'; image.setAttribute('aria-hidden','true'); if(imageCacheBank)imageCacheBank.appendChild(image); var promise=new Promise(function(resolve,reject){ var finished=false; var decoding=false; var finish=function(ok){ if(finished)return; finished=true; if(!ok){reject(new Error('asset '+url));return;} resolve(image); }; var decodeLoaded=function(){ if(finished||decoding)return; decoding=true; if(typeof image.decode==='function'){ image.decode().then(function(){finish(true);}).catch(function(){ finish(image.complete&&image.naturalWidth>0); }); }else{ finish(image.complete&&image.naturalWidth>0); } }; image.onload=decodeLoaded; image.onerror=function(){finish(false);}; image.src=url; if(image.complete&&image.naturalWidth>0)decodeLoaded(); }); decodedImageCache.set(url,{image:image,promise:promise}); return promise.then(function(){doneOne();}); } function preloadFont(job){ if(!document.fonts||!document.fonts.load)return Promise.reject(new Error('font loading unsupported')); return document.fonts.load(job.css,job.sample).then(function(faces){ if(!faces||!faces.length)throw new Error('font '+job.css); doneOne(); }); } function loadFonts(){ return Promise.all(fontJobs.map(preloadFont)).then(function(){ if(loader)loader.classList.add('fonts-ready'); if(copyEl)copyEl.setAttribute('aria-hidden','false'); }); } function loadScript(url,label){ return new Promise(function(resolve,reject){ var script=document.createElement('script'); script.src=url; script.onload=function(){doneOne();resolve();}; script.onerror=function(){reject(new Error(label||'editor script'));}; document.body.appendChild(script); }); } function loadEditor(){return loadScript('./assets/vendor/flipnote.js','PPM reader').then(function(){return loadScript('./assets/ppm-import.js','PPM importer');}).then(function(){return loadScript(drawingEngineScript,'drawing engine');}).then(function(){return loadScript(textToolScript,'text tool');}).then(function(){return loadScript(editorScript,'editor');});} function installEditorInputFixes(){ var titleField=document.getElementById('filename'); if(titleField&&!titleField.dataset.titleLimitFix){ titleField.dataset.titleLimitFix='1'; var titleLimit=80; function titleText(){return String(titleField.textContent||'').replace(/ /g,' ');} function selectionLength(){ var selection=window.getSelection&&window.getSelection(); if(!selection||!selection.rangeCount)return 0; var range=selection.getRangeAt(0); if(!titleField.contains(range.commonAncestorContainer))return 0; return range.toString().length; } function putCaretAtEnd(){ var selection=window.getSelection&&window.getSelection(); if(!selection)return; var range=document.createRange(); range.selectNodeContents(titleField); range.collapse(false); selection.removeAllRanges(); selection.addRange(range); } titleField.addEventListener('keydown',function(event){ if(event.defaultPrevented||event.ctrlKey||event.metaKey||event.altKey)return; if(event.key&&event.key.length===1&&titleText().length-selectionLength()>=titleLimit){ event.preventDefault(); event.stopImmediatePropagation(); } },true); titleField.addEventListener('beforeinput',function(event){ if(event.defaultPrevented||!event.inputType||event.inputType.indexOf('insert')!==0)return; var incoming=typeof event.data==='string'?event.data:''; if(event.inputType==='insertLineBreak'||event.inputType==='insertParagraph'){ event.preventDefault(); return; } if(titleText().length-selectionLength()+(incoming?incoming.length:1)>titleLimit){ event.preventDefault(); } },true); titleField.addEventListener('paste',function(event){ var clipboard=event.clipboardData||window.clipboardData; if(!clipboard)return; var incoming=String(clipboard.getData('text')||'').replace(/[\r\n]+/g,' '); var room=Math.max(0,titleLimit-(titleText().length-selectionLength())); if(incoming.length<=room)return; event.preventDefault(); if(room>0){ var partial=incoming.slice(0,room); if(document.execCommand)document.execCommand('insertText',false,partial); } },true); titleField.addEventListener('input',function(){ var value=titleText(); if(value.length<=titleLimit)return; titleField.textContent=value.slice(0,titleLimit); putCaretAtEnd(); },true); } var paperTool=document.getElementById('paper-tool'); function pageUsesBlackPaper(){ if(!paperTool)return false; if(paperTool.getAttribute('aria-pressed')==='true')return true; var image=paperTool.querySelector('img'); return !!(image&&/paperB\.png(?:[?#]|$)/i.test(image.getAttribute('src')||'')); } function syncMonochromeInkLabel(){ var label=pageUsesBlackPaper()?'White':'Black'; ['flipnote-image-import-colour','flipnote-image-import-second-colour'].forEach(function(id){ var select=document.getElementById(id); var option=select&&select.querySelector('option[value="black"]'); if(option)option.textContent=label; }); document.querySelectorAll('.flipnote-editor-layer-color[data-layer-color="black"]').forEach(function(button){ var layer=Number(button.getAttribute('data-layer'))+1; button.title=label; button.setAttribute('aria-label','Layer '+layer+' '+label.toLowerCase()); }); } syncMonochromeInkLabel(); if(paperTool&&!paperTool.dataset.rexiPaperInkLabelFix){ paperTool.dataset.rexiPaperInkLabelFix='1'; paperTool.addEventListener('click',function(){setTimeout(syncMonochromeInkLabel,0);}); var paperObserver=new MutationObserver(syncMonochromeInkLabel); paperObserver.observe(paperTool,{attributes:true,attributeFilter:['aria-pressed','title'],subtree:true}); var paperImage=paperTool.querySelector('img'); if(paperImage)paperObserver.observe(paperImage,{attributes:true,attributeFilter:['src']}); } var imageImportButton=document.getElementById('flipnote-import-image-button'); if(imageImportButton&&!imageImportButton.dataset.rexiInkLabelFix){ imageImportButton.dataset.rexiInkLabelFix='1'; imageImportButton.addEventListener('click',function(){setTimeout(syncMonochromeInkLabel,0);}); } var imageApply=document.getElementById('flipnote-image-import-apply'); if(imageApply&&!imageApply.dataset.rexiImageColourFix){ imageApply.dataset.rexiImageColourFix='1'; imageApply.addEventListener('click',function(){ var mode=document.getElementById('flipnote-image-import-mode'); var first=document.getElementById('flipnote-image-import-colour'); var second=document.getElementById('flipnote-image-import-second-colour'); var layer=document.getElementById('flipnote-image-import-layer'); var chosenMode=mode?mode.value:'single'; var firstColour=first?first.value:'black'; var secondColour=second?second.value:'red'; var chosenLayer=Math.max(0,Math.min(1,Number(layer&&layer.value)||0)); function applyLayerColour(index,colour){ var button=document.querySelector('.flipnote-editor-layer-color[data-layer="'+index+'"][data-layer-color="'+colour+'"]'); if(button)button.click(); } setTimeout(function(){ if(chosenMode==='dual'){ applyLayerColour(0,firstColour); applyLayerColour(1,secondColour); }else{ applyLayerColour(chosenLayer,firstColour); } syncMonochromeInkLabel(); },0); },true); } } function installMobileShiftToggle(){ var button=document.getElementById('flipnote-mobile-shift-toggle'); if(!button)return; var ua=String(navigator.userAgent||''); var uaMobile=!!(navigator.userAgentData&&navigator.userAgentData.mobile); var ipadLike=navigator.platform==='MacIntel'&&Number(navigator.maxTouchPoints||0)>1; var mobileOrTablet=uaMobile||ipadLike||/(Android|iPad|iPhone|iPod|Mobile|Tablet|Silk|Kindle)/i.test(ua); var desktopSetting=document.getElementById('flipnote-editor-desktop-shift'); var desktopSettingKey='flipnote-editor.flipnote-editor.desktop-shift.v1'; var desktopEnabled=false; try{desktopEnabled=window.localStorage.getItem(desktopSettingKey)==='1';}catch(_error){} if(desktopSetting)desktopSetting.checked=desktopEnabled; if(!mobileOrTablet&&desktopSetting)desktopSetting.addEventListener('change',function(){ desktopEnabled=desktopSetting.checked; try{window.localStorage.setItem(desktopSettingKey,desktopEnabled?'1':'0');}catch(_error){} if(!desktopEnabled)setVirtualShift(false); button.hidden=!desktopEnabled; }); var image=button.querySelector('img'); var normalSrc=image&&image.getAttribute('data-normal-src')||'./assets/flipnote-editor/shiftl.png'; var activeSrc=image&&image.getAttribute('data-active-src')||'./assets/flipnote-editor/shiftl_active.png'; var active=false; function makeShiftEvent(type,down){ var event; try{ event=new KeyboardEvent(type,{key:'Shift',code:'ShiftLeft',location:1,shiftKey:!!down,bubbles:true,cancelable:true}); }catch(_error){ event=document.createEvent('Event'); event.initEvent(type,true,true); } try{Object.defineProperty(event,'key',{configurable:true,get:function(){return 'Shift';}});}catch(_error){} try{Object.defineProperty(event,'code',{configurable:true,get:function(){return 'ShiftLeft';}});}catch(_error){} try{Object.defineProperty(event,'keyCode',{configurable:true,get:function(){return 16;}});}catch(_error){} try{Object.defineProperty(event,'which',{configurable:true,get:function(){return 16;}});}catch(_error){} try{Object.defineProperty(event,'shiftKey',{configurable:true,get:function(){return !!down;}});}catch(_error){} var target=document.body||document.documentElement||document; target.dispatchEvent(event); } function setVirtualShift(next){ next=!!next; if(active===next)return; active=next; button.setAttribute('aria-pressed',active?'true':'false'); button.title=active?'Shift enabled':'Shift'; if(image)image.src=active?activeSrc:normalSrc; makeShiftEvent(active?'keydown':'keyup',active); } function forceShiftModifier(event){ if(!active||!event)return; try{Object.defineProperty(event,'shiftKey',{configurable:true,get:function(){return true;}});}catch(_error){} if(typeof event.getModifierState==='function'){ var original=event.getModifierState.bind(event); try{Object.defineProperty(event,'getModifierState',{configurable:true,value:function(key){return String(key||'').toLowerCase()==='shift'?true:original(key);}});}catch(_error){} } } ['pointerdown','pointerup','mousedown','mouseup','click','dblclick'].forEach(function(type){ document.addEventListener(type,forceShiftModifier,true); }); function consumesOneShotShift(target){ if(!target||!target.closest)return false; var slot=target.closest('[data-tool-slot]'); if(slot){ var slotImage=slot.querySelector('img'); var slotSrc=slotImage&&String(slotImage.getAttribute('src')||''); return slotSrc.toLowerCase().indexOf('_adv.png')!==-1; } return !!target.closest('#copy-tool,.flipnote-editor-more-tool,[data-audio-control]'); } button.addEventListener('click',function(){setVirtualShift(!active);}); document.addEventListener('click',function(event){ if(!active||button.contains(event.target)||!consumesOneShotShift(event.target))return; window.setTimeout(function(){setVirtualShift(false);},0); },true); document.addEventListener('keyup',function(event){ if(active&&event.key==='Shift')window.setTimeout(function(){if(active)makeShiftEvent('keydown',true);},0); }); window.addEventListener('focus',function(){if(active)makeShiftEvent('keydown',true);}); window.addEventListener('pagehide',function(){if(active)makeShiftEvent('keyup',false);}); button.hidden=!(mobileOrTablet||desktopEnabled); } function reveal(){ updateProgress(); document.body.classList.remove('flipnote-editor-loading'); if(!loader)return; loader.classList.add('is-exiting'); var removed=false; var removeLoader=function(){ if(removed)return; removed=true; loader.remove(); }; loader.addEventListener('transitionend',function(event){ if(event.target===loader&&event.propertyName==='transform')removeLoader(); }); window.setTimeout(removeLoader,900); } function fail(error){ if(loader)loader.classList.add('load-failed'); if(titleEl)titleEl.textContent='Flipnote Editor could not load'; if(statusEl)statusEl.textContent=error&&error.message?error.message:'Try refreshing the page.'; } updateProgress(); var imageJobs=imageUrls.map(preloadImage); var fontPromise=loadFonts(); function settleDecodedImages(){ return new Promise(function(resolve){ requestAnimationFrame(function(){requestAnimationFrame(resolve);}); }); } Promise.all(imageJobs.concat([fontPromise])).then(settleDecodedImages).then(loadEditor).then(function(){installEditorInputFixes();installMobileShiftToggle();return settleDecodedImages();}).then(reveal).catch(fail); })();