From 1d6b486c50331370c6b8dde3eea7dea10079cf67 Mon Sep 17 00:00:00 2001 From: Derik Badman Date: Thu, 6 Oct 2016 01:17:41 +0000 Subject: [PATCH] fixes #19 --- main.js.map | 2 +- main.min.js | 2 +- src/main.js | 6 ++++++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/main.js.map b/main.js.map index 6264a83..01ec147 100644 --- a/main.js.map +++ b/main.js.map @@ -1 +1 @@ -{"version":3,"sources":["node_modules/browser-pack/_prelude.js","src/main.js"],"names":["checkFeatures","window","navigator","serviceWorker","register","scope","Alert","el","document","querySelector","setContent","content","Array","isArray","this","clear","f","createDocumentFragment","forEach","appendChild","btn","createElement","setAttribute","classList","add","textContent","firstChild","removeChild","initialize","_this","addEventListener","e","target","contains","preventDefault","key","getAttribute","Manager","deleteCharacter","p","updateAppCache","event","applicationCache","status","UPDATEREADY","backup_dialog","reset","remove","input_file","console","log","files","length","from","reader","FileReader","onload","theFile","restoreCharacter","result","readAsText","input","value","action_menu","action_opener","style","overflow","toggle","getComputedStyle","getPropertyValue","action_btn_backup","Blob","display","downloadBackup","action_btn_email","emailBackup","action_btn_save","saveCharacter","action_btn_new","location","hash","generateKey","action_btn_restore","action_btn_load","currentTarget","nextElementSibling","LoadMenu","open","close","addCharacter","data","Storage","get","JSON","parse","existing","charname","charclass","level","li","a","del","innerHTML","message","removeCharacter","loadlink","parentNode","isEmpty","_this2","body","closest","id","deletePrompt","attribute_fields","querySelectorAll","attribute_saves","skill_checks","dialog_unsaved","calcAttrMod","val","raw","Math","floor","calcSaveMod","attr","prof","attr_field","attr_mod","save_mod","checked","parseInt","calcSkillMod","skill_checked","attribute","mod_field","innerText","calcProfMod","before","bonus","ceil","after","removeAttribute","field","skills","character_model","app","race","background","alignment","experience","inspiration","proficiency","armor_class","speed","hp_cur","hp_max","hd_cur","hd_max","str","dex","con","intel","wis","cha","saves","acrobatics","animal_handling","arcana","athletics","deception","history","insight","intimidation","investigation","medicine","nature","perception","performance","persuasion","religion","sleight_of_Hand","stealth","survival","weapons","proficiencies_other","languages","traits","ideals","bonds","flaws","equipment","cp","sp","gp","pp","features","notes","spell_ability","spell_save","spell_attack","spell_slots","1","2","3","4","5","6","7","8","9","spells","0","updated","txt","localStorage","getItem","set","setItem","removeItem","getAllKeys","keys","i","push","exports","manager","cur_character","characterJSON","obj","prop","stringify","currentTimestamp","d","Date","toUTCString","random","toString","slice","setCurCharacterData","_this3","_typeof","props","Object","changeCharacter","urlhash","substr","loadCharacter","json","create","renderCharacter","_this4","fields","subf","tagName","Event","dispatchEvent","_this5","hasOwnProperty","text","focus","select","file","type","url","URL","createObjectURL","href","download","click","setTimeout","revokeObjectURL","encodeURIComponent","substring","indexOf","lastIndexOf","trim","replace","char_obj","Error","ex_char","key_prev","temp_key","h","p1","p4","p2","p3","dialog_help","getElementById","help_open"],"mappings":"AAAA;mOCKMA,cAAgB,WACrB,KAAI,gBAAkBC,SAAqC,OAA3BA,OAAA,cAG/B,OAAO,EAOL,kBAAmBC,YACtBA,UAAUC,cAAcC,SAAS,qBAChCC,MAAO,KAQT,IAAMC,QAILC,GAAIC,SAASC,cAAc,eAK3BC,WAAY,SAAUC,GAChBC,MAAMC,QAAQF,KAClBA,GAAWA,IAEZG,KAAKC,OACL,IAAMC,GAAIR,SAASS,wBACnBN,GAAQO,QAAQ,SAACX,GAChBS,EAAEG,YAAYZ,IAEf,IAAMa,GAAMZ,SAASa,cAAc,SACnCD,GAAIE,aAAa,OAAQ,UACzBF,EAAIG,UAAUC,IAAI,SAClBJ,EAAIK,YAAc,QAClBT,EAAEG,YAAYC,GACdN,KAAKP,GAAGY,YAAYH,IAKrBD,MAAO,WACN,KAAOD,KAAKP,GAAGmB,YACdZ,KAAKP,GAAGoB,YAAYb,KAAKP,GAAGmB,aAM9BE,WAAY,WAAY,GAAAC,GAAAf,IACvBA,MAAKP,GAAGuB,iBAAiB,QAAS,SAACC,GAClC,GAAIA,EAAEC,OAAOT,UAAUU,SAAS,SAE/BF,EAAEG,iBACFL,EAAKd,YACC,IAAIgB,EAAEC,OAAOT,UAAUU,SAAS,eAAgB,CAEtD,GAAME,GAAMJ,EAAEC,OAAOI,aAAa,WAClCP,GAAKd,QACLsB,QAAQC,gBAAgBH,OAO5B,IAFA7B,MAAMsB,aAEF5B,mBAAoB,EAAO,CAE9B,GAAMuC,GAAI/B,SAASa,cAAc,IACjCkB,GAAEd,YAAF,8IACAnB,MAAMI,WAAW6B,GAMlB,GAAI,oBAAsBtC,QAAQ,CACjC,GAAMuC,gBAAiB,SAAUC,GAChC,GAAMF,GAAI/B,SAASa,cAAc,IACjCkB,GAAEd,YAAF,6LACAnB,MAAMI,WAAW6B,GAElBtC,QAAOyC,iBAAiBZ,iBAAiB,cAAeU,gBAAgB,GACpEvC,OAAOyC,iBAAiBC,SAAW1C,OAAOyC,iBAAiBE,aAC9DJ,iBAOF,GAAMK,eAAgBrC,SAASC,cAAc,iBAC7CoC,eAAcpC,cAAc,uBAAuBqB,iBAAiB,QAAS,SAACC,GAC7Ec,cAAcpC,cAAc,QAAQqC,QACpCD,cAActB,UAAUwB,OAAO,UAEhCF,cAAcpC,cAAc,QAAQqB,iBAAiB,SAAU,SAACC,GAC/DA,EAAEG,gBACF,IAAMc,GAAajB,EAAEC,OAAOvB,cAAc,mBAE1C,IADAwC,QAAQC,IAAIF,EAAWG,OACnBH,EAAWG,OAASH,EAAWG,MAAMC,OAAS,EACjDxC,MAAMyC,KAAKL,EAAWG,OAAOjC,QAAQ,SAACF,GACrC,GAAMsC,GAAS,GAAIC,WAEnBD,GAAOE,OAAU,SAAUC,GAC1B,MAAO,UAAU1B,GAChBM,QAAQqB,iBAAiB3B,EAAEC,OAAO2B,UAEjC3C,GACHsC,EAAOM,WAAW5C,SAEb,CACN,GAAM6C,GAAQ9B,EAAEC,OAAOvB,cAAc,WACrC,IAAoB,KAAhBoD,EAAMC,MACT,MAEDzB,SAAQqB,iBAAiBG,EAAMC,OAEhCjB,cAActB,UAAUwB,OAAO,QAC/BhB,EAAEC,OAAOc,SAMV,IAAMiB,aAAcvD,SAASC,cAAc,gBACrCuD,cAAgBxD,SAASC,cAAc,oBAC7CuD,eAAclC,iBAAiB,QAAS,SAACC,GACpCgC,YAAYxC,UAAUU,SAAS,UAElC8B,YAAYE,MAAMC,SAAW,UAE9BH,YAAYxC,UAAU4C,OAAO,UAK9BJ,YAAYjC,iBAAiB,gBAAiB,SAACC,GAC9CkB,QAAQC,IAAInB,EACZ,IAAMkC,GAAQhE,OAAOmE,iBAAiBL,YACO,SAAzCE,EAAMI,iBAAiB,gBAC1BN,YAAYE,MAAMC,SAAW,YAG/B,IAAMI,mBAAoBP,YAAYtD,cAAc,mBACzB,mBAAhBR,QAAOsE,OACjBD,kBAAkBL,MAAMO,QAAU,QAEnCF,kBAAkBxC,iBAAiB,QAAS,SAACC,GAC5CM,QAAQoC,kBAET,IAAMC,kBAAmBX,YAAYtD,cAAc,oBACnDiE,kBAAiB5C,iBAAiB,QAAS,SAACC,GAC3CM,QAAQsC,eAET,IAAMC,iBAAkBb,YAAYtD,cAAc,YAClDmE,iBAAgB9C,iBAAiB,QAAS,SAACC,GAC1CM,QAAQwC,iBAET,IAAMC,gBAAiBf,YAAYtD,cAAc,qBACjDqE,gBAAehD,iBAAiB,QAAS,SAACC,GAEzC9B,OAAO8E,SAASC,KAAhB,IAA2B3C,QAAQ4C,eAEpC,IAAMC,oBAAqBnB,YAAYtD,cAAc,sBACrDyE,oBAAmBpD,iBAAiB,QAAS,SAACC,GAC7Cc,cAActB,UAAUC,IAAI,SAE7B,IAAM2D,iBAAkBpB,YAAYtD,cAAc,YAClD0E,iBAAgBrD,iBAAiB,QAAS,SAACC,GAC1CA,EAAEqD,cAAcC,mBAAmB9D,UAAU4C,OAAO,SAMrD,IAAMmB,WAIL/E,GAAIC,SAASC,cAAc,mBAI3B8E,KAAM,WACLzE,KAAKP,GAAGgB,UAAUC,IAAI,SAKvBgE,MAAO,WACN1E,KAAKP,GAAGgB,UAAUwB,OAAO,SAM1B0C,aAAc,SAAUtD,GACvB,GAAIuD,GAAOC,QAAQC,IAAIzD,EAEvB,IAAa,KAATuD,EAGJ,IAGC,GAFAA,EAAOG,KAAKC,MAAMJ,GAEdA,EAAKvD,KAAoB,KAAbuD,EAAKvD,IAAY,CAEhC,GAAM4D,GAAWjF,KAAKP,GAAGE,cAAR,YAAkCiF,EAAKvD,IAAvC,KACjB,IAAiB,OAAb4D,EAGH,YADAA,EAAStE,YAAiBiE,EAAKM,SAA/B,KAA4CN,EAAKO,UAAjD,IAA8DP,EAAKQ,MAAnE,IAGD,IAAMC,GAAK3F,SAASa,cAAc,MAC5B+E,EAAI5F,SAASa,cAAc,IACjC+E,GAAE3E,YAAiBiE,EAAKM,SAAxB,KAAqCN,EAAKO,UAA1C,IAAuDP,EAAKQ,MAA5D,IACAE,EAAE9E,aAAa,OAAf,IAA2BoE,EAAKvD,KAChCgE,EAAGhF,YAAYiF,EACf,IAAMC,GAAM7F,SAASa,cAAc,IACnCgF,GAAI9E,UAAUC,IAAI,UAClB6E,EAAIC,UAAY,IAChBD,EAAI/E,aAAa,OAAQ,KACzB+E,EAAI/E,aAAa,WAAYoE,EAAKvD,KAClCgE,EAAGhF,YAAYkF,GACfvF,KAAKP,GAAGE,cAAc,qBAAqBU,YAAYgF,IAEvD,MAAOpE,GACRkB,QAAQC,IAAInB,EAAEwE,WAOhBC,gBAAiB,SAAUrE,GAC1B,GAAMsE,GAAW3F,KAAKP,GAAGE,cAAR,YAAkC0B,EAAlC,MACXgE,EAAKM,EAASC,UACpBP,GAAGO,WAAW/E,YAAYwE,IAM3BQ,QAAS,WACR,MAAuC,QAAhC7F,KAAKP,GAAGE,cAAc,OAK9BmB,WAAY,WAAY,GAAAgF,GAAA9F,IACvBN,UAASqG,KAAK/E,iBAAiB,QAAS,SAACC,GACxC,GAAMyD,GAAQzD,EAAEC,OAAO8E,QAAT,IAAqBF,EAAKrG,GAAGwG,GAC3C,IAAc,OAAVvB,EAAgB,CAEnB,GAAIzD,EAAEC,OAAOT,UAAUU,SAAS,YAAe,MAE/C2E,GAAKpB,YAGDzD,GAAEC,OAAOT,UAAUU,SAAS,YAC/BF,EAAEG,iBACFe,QAAQC,IAAI,WACZb,QAAQ2E,aAAajF,EAAEC,OAAOI,aAAa,aAC3CwE,EAAKpB,YAMVF,UAAS1D,YAKT,IAAMqF,kBAAmBrG,MAAMyC,KAAK7C,SAAS0G,iBAAiB,sCACxDC,gBAAkBvG,MAAMyC,KAAK7C,SAAS0G,iBAAiB,wCACvDE,aAAexG,MAAMyC,KAAK7C,SAAS0G,iBAAiB,8BACpDG,eAAiB7G,SAASC,cAAc,kBAMxC6G,YAAc,SAAUC,GAC7B,GAAMC,GAAMC,KAAKC,OAAOH,EAAM,IAAM,EACpC,OAAQC,GAAM,EAAP,IAAgBA,EAAQA,GAO1BG,YAAc,SAAUC,GAC7B,GAAIC,GAAO,EACLC,EAAatH,SAASC,cAAT,cAAqCmH,EAArC,KAA8ClB,WAC3DqB,EAAWD,EAAWrH,cAAc,qBAAqB6F,UACzD0B,EAAWF,EAAWrH,cAAc,gBACpCwH,EAAUH,EAAWrH,cAAc,wBAAwBwH,OAC7DA,KACHJ,EAAOrH,SAASC,cAAc,6BAA6B6F,UAE1DuB,EADY,KAATA,EACI,EAEAK,SAASL,EAAM,IAGxB,IAAML,GAAM,EAAIU,SAASL,EAAM,IAAMK,SAASH,EAAU,GACxDC,GAAS1B,UAAakB,EAAM,EAAP,IAAgBA,EAAQA,GAMxCW,aAAe,SAAU5H,GAC9B,GAAM6H,GAAgB7H,EAAG0H,QACnBI,EAAY9H,EAAG6B,aAAa,aAC5BkG,EAAY/H,EAAGmG,WAAWrB,mBAE5BwC,EAAO,CACPO,KACHP,EAAOrH,SAASC,cAAc,6BAA6B6F,UAE1DuB,EADY,KAATA,EACI,EAEAK,SAASL,EAAM,IAGxB,IAAMD,GAAOpH,SAASC,cAAT,cAAqC4H,EAArC,KACPN,EAAWG,SAASZ,YAAYM,EAAK9D,OAAQ,IAC7C0D,EAAM,EAAIK,EAAOE,CACvBO,GAAUC,UAAaf,EAAM,EAAP,IAAgBA,EAAQA,GAKzCgB,YAAc,WACnB,GAAMX,GAAOrH,SAASC,cAAT,2BACPgI,EAASZ,EAAKvB,UACdJ,EAAQgC,SAAS1H,SAASC,cAAT,qBAA4C6F,UAAW,IACxEoC,EAAQjB,KAAKkB,KAAKzC,EAAQ,GAAK,EAC/B0C,EAAAA,IAAYF,CAElB,IAAID,IAAWG,EAAO,CACrBf,EAAKvB,UAAL,IAAqBoC,CAErB,IAAMzB,GAAmBrG,MAAMyC,KAAK7C,SAAS0G,iBAAiB,qCAC9DD,GAAiB/F,QAAQ,SAACX,GACzBoH,YAAYpH,EAAG6B,aAAa,eAE7B,IAAMgF,GAAexG,MAAMyC,KAAK7C,SAAS0G,iBAAiB,6BAC1DE,GAAalG,QAAQ,SAACX,GACrB4H,aAAa5H,MAShBC,UAASC,cAAc,cAAcqB,iBAAiB,QAAS,SAACC,GACd,SAA7CA,EAAEC,OAAOI,aAAa,oBACzBL,EAAEC,OAAOV,aAAa,cAAeS,EAAEC,OAAOsE,aAE7C,GACH9F,SAASC,cAAc,cAAcqB,iBAAiB,OAAQ,SAACC,GAC9D,GAAiD,SAA7CA,EAAEC,OAAOI,aAAa,mBAA+B,CACxD,GAAMqG,GAAS1G,EAAEC,OAAOI,aAAa,cACjCqG,KAAW1G,EAAEC,OAAOsE,YACvBrD,QAAQC,IAAI,WACZnB,EAAEC,OAAO6G,gBAAgB,eACzBxB,eAAe9F,UAAUC,IAAI,QAGc,UAAvCO,EAAEC,OAAOI,aAAa,cACzBoG,kBAMD,GAIHvB,iBAAiB/F,QAAQ,SAACX,GACzB,GAAMuI,GAAQvI,EAAGmG,WACX4B,EAAYQ,EAAMrI,cAAc,oBACtCF,GAAGuB,iBAAiB,SAAU,SAACC,GAC9BuG,EAAUC,UAAYjB,YAAYvF,EAAEqD,cAActB,MAClD,IAAM8D,GAAOrH,EAAG6B,aAAa,YAC7BuF,aAAYC,EACZ,IAAMmB,GAASnI,MAAMyC,KAAK7C,SAAS0G,iBAAT,cAAwCU,EAAxC,KAC1BmB,GAAO7H,QAAQ,SAACX,GACf4H,aAAa5H,KAEd8G,eAAe9F,UAAUC,IAAI,YAM/B2F,gBAAgBjG,QAAQ,SAACX,GACxBA,EAAGuB,iBAAiB,SAAU,SAACC,GAC9B,GAAM6F,GAAO7F,EAAEqD,cAAchD,aAAa,gBAC1CuF,aAAYC,GACZP,eAAe9F,UAAUC,IAAI,YAM/B4F,aAAalG,QAAQ,SAACX,GACrBA,EAAGuB,iBAAiB,SAAU,SAACC,GAC9BkB,QAAQC,IAAI,gBACZiF,aAAapG,EAAEqD,eACfiC,eAAe9F,UAAUC,IAAI,WAO/B,IAAMwH,kBACLC,IAAK,qBACL9G,IAAK,GACL6D,SAAU,GACVC,UAAW,GACXiD,KAAM,GACNC,WAAY,GACZC,UAAW,GACXlD,MAAO,EACPmD,WAAY,EACZC,YAAa,GACbC,YAAa,KACbC,YAAa,GACbC,MAAO,GACPC,OAAQ,EACRC,OAAQ,EACRC,OAAQ,EACRC,OAAQ,EACRC,IAAK,GACLC,IAAK,GACLC,IAAK,GACLC,MAAO,GACPC,IAAK,GACLC,IAAK,GACLC,OACCN,IAAO,EACPC,IAAO,EACPC,IAAO,EACPC,MAAS,EACTC,IAAO,EACPC,IAAO,GAERpB,QACCsB,WAAY,EACZC,gBAAiB,EACjBC,OAAQ,EACRC,UAAW,EACXC,UAAW,EACXC,QAAS,EACTC,QAAS,EACTC,aAAc,EACdC,cAAe,EACfC,SAAU,EACVC,OAAQ,EACRC,WAAY,EACZC,YAAa,EACbC,WAAY,EACZC,SAAU,EACVC,gBAAiB,EACjBC,QAAS,EACTC,SAAU,GAEXC,QAAS,GACTC,oBAAqB,GACrBC,UAAW,GACXC,OAAQ,GACRC,OAAQ,GACRC,MAAO,GACPC,MAAO,GACPC,UAAW,GACXC,GAAI,EACJC,GAAI,EACJC,GAAI,EACJC,GAAI,EACJC,SAAU,GACVC,MAAO,GACPC,cAAe,GACfC,WAAY,GACZC,aAAc,GACdC,aACCC,EAAG,EACHC,EAAG,EACHC,EAAG,EACHC,EAAG,EACHC,EAAG,EACHC,EAAG,EACHC,EAAG,EACHC,EAAG,EACHC,EAAG,GAEJC,QACCC,EAAG,GACHV,EAAG,GACHC,EAAG,GACHC,EAAG,GACHC,EAAG,GACHC,EAAG,GACHC,EAAG,GACHC,EAAG,GACHC,EAAG,GACHC,EAAG,IAEJG,QAAS,IAOJzH,SAMLC,IAAK,SAAUzD,GACd,GAAMkL,GAAMC,aAAaC,QAAQpL,EACjC,OAAgB,QAARkL,EAAgBA,EAAM,IAS/BG,IAAK,SAAUrL,EAAKkL,GACnB,IACCC,aAAaG,QAAQtL,EAAKkL,GACzB,MAAOtL,GAER,OAAO,EAER,OAAO,GAORgB,OAAQ,SAAUZ,GACjBmL,aAAaI,WAAWvL,IAMzBwL,WAAY,WACX,GAAMC,KACN,IAAIN,aAAalK,OAAS,EACzB,IAAK,GAAIyK,GAAI,EAAGA,EAAIP,aAAalK,OAAQyK,IACxCD,EAAKE,KAAKR,aAAanL,IAAI0L,GAG7B,OAAOD,KAYHvL,QAAU0L,QAAQC,SAIvBC,cAAe,KAMfC,cAAe,WACd,GAAMC,KACN,KAAK,GAAMC,KAAQtN,MAAKmN,cACvBE,EAAIC,GAAQtN,KAAKmN,cAAcG,EAEhC,OAAOvI,MAAKwI,UAAUF,IAMvBG,iBAAkB,WACjB,GAAMC,GAAI,GAAIC,KACd,OAAOD,GAAEE,eAOVxJ,YAAa,WAEZ,IADA,GAAI9C,IAAUsF,KAAKiH,SAASC,SAAS,IAA3B,qBAAmDC,MAAM,EAAG,GAC1C,KAArBjJ,QAAQC,IAAIzD,IAClBA,GAAUsF,KAAKiH,SAASC,SAAS,IAA3B,qBAAmDC,MAAM,EAAG,EAEnE,OAAOzM,IAKR0M,oBAAqB,SAAUnJ,GAAM,GAAAoJ,GAAAhO,IACpC,IAAoB,YAAhB,mBAAO4E,GAAP,YAAAqJ,QAAOrJ,IAAX,CACA,GAAMsJ,GAAQC,OAAOrB,KAAKlI,EAC1BsJ,GAAM9N,QAAQ,SAACkN,GAC0B,mBAA7BU,GAAKb,cAAcG,KAC7BU,EAAKb,cAAcG,GAAQ1I,EAAK0I,QASnCc,gBAAiB,WAChB,GAAMC,GAAUlP,OAAO8E,SAASC,KAAKoK,OAAO,EAC5CnM,SAAQC,IAAI,mBACZpC,KAAKuO,cAAcF,GACnB7J,SAASE,SAMV6J,cAAe,SAAUlN,GACxBc,QAAQC,IAAR,iBAA6Bf,GAC7BkF,eAAe9F,UAAUwB,OAAO,OAChC,IAAMuM,GAAO3J,QAAQC,IAAIzD,EACzB,IAAa,KAATmN,EAMH,MALArM,SAAQC,IAAI,sBAEZpC,KAAKmN,cAAgBgB,OAAOM,OAAOvG,iBACnClI,KAAKmN,cAAc9L,IAAMA,MACzBrB,MAAK0O,iBAGN,IAAM9J,GAAOG,KAAKC,MAAMwJ,EAExBxO,MAAKmN,cAAgBgB,OAAOM,OAAOvG,iBACnClI,KAAK+N,oBAAoBnJ,GACzB5E,KAAK0O,mBAKNA,gBAAiB,WAAY,GAAAC,GAAA3O,IAC5B,IAA2B,OAAvBA,KAAKmN,cAAT,CACA,GAAMyB,GAAS9O,MAAMyC,KAAK7C,SAAS0G,iBAAiB,gBACpDwI,GAAOxO,QAAQ,SAACX,GACf,GAAMS,GAAIT,EAAG6B,aAAa,YAC1B,IAAqC,mBAA1BqN,GAAKxB,cAAcjN,GAA9B,CAGA,GAAM2O,GAAOpP,EAAG6B,aAAa,gBAC7B,KAAa,OAATuN,GACwC,mBAAhCF,GAAKxB,cAAcjN,GAAG2O,KAIG,mBAA1BF,GAAKxB,cAAcjN,GAC7B,OAAQT,EAAGqP,SACV,IAAK,QACL,IAAK,SACL,IAAK,WACJ,GAAgC,aAA5BrP,EAAG6B,aAAa,QAAwB,CAC3C,GAAM6F,GAAW0H,EAAQF,EAAKxB,cAAcjN,GAAG2O,GAAQF,EAAKxB,cAAcjN,EAC1D,KAAZiH,EACH1H,EAAG0H,SAAU,EAEb1H,EAAG0H,SAAU,CAEd,OAED1H,EAAGuD,MAAS6L,EAAQF,EAAKxB,cAAcjN,GAAG2O,GAAQF,EAAKxB,cAAcjN,EACrE,IAAMyB,GAAQ,GAAIoN,OAAM,SACxBtP,GAAGuP,cAAcrN,EACjB,MACD,SACClC,EAAG+F,UAAaqJ,EAAQF,EAAKxB,cAAcjN,GAAG2O,GAAQF,EAAKxB,cAAcjN,OAM7EwH,cACAvB,iBAAiB/F,QAAQ,SAACX,GACzB,GAAMuI,GAAQvI,EAAGmG,WACX4B,EAAYQ,EAAMrI,cAAc,oBACtC6H,GAAUC,UAAYjB,YAAY/G,EAAGuD,MACrC,IAAM8D,GAAOrH,EAAG6B,aAAa,YAC7BuF,aAAYC,EACZ,IAAMmB,GAASnI,MAAMyC,KAAK7C,SAAS0G,iBAAT,cAAwCU,EAAxC,KAC1BmB,GAAO7H,QAAQ,SAACX,GACf4H,aAAa5H,OAGf8G,eAAe9F,UAAUwB,OAAO,UAKjC8B,cAAe,WAAY,GAAAkL,GAAAjP,IAC1BmC,SAAQC,IAAI,QACe,OAAvBpC,KAAKmN,gBACRnN,KAAKmN,cAAgBgB,OAAOM,OAAOvG,iBAEpC,IAAM0G,GAAS9O,MAAMyC,KAAK7C,SAAS0G,iBAAiB,gBACpDwI,GAAOxO,QAAQ,SAACX,GACf,GAAMS,GAAIT,EAAG6B,aAAa,YAC1B,IAAqC,mBAA1B2N,GAAK9B,cAAcjN,GAA9B,CAGA,GAAM2O,GAAOpP,EAAG6B,aAAa,gBAC7B,IAAa,OAATuN,EAAe,CAClB,GAA2C,mBAAhCI,GAAK9B,cAAcjN,GAAG2O,GAChC,MAIII,GAAK9B,cAAc+B,eAAehP,KACtC+O,EAAK9B,cAAcjN,GAAKgI,gBAAgBhI,IAG1C,OAAQT,EAAGqP,SACV,IAAK,QACL,IAAK,SACL,IAAK,WACJ,GAAgC,aAA5BrP,EAAG6B,aAAa,QAAwB,CAC3C,GAAM6F,GAAU1H,EAAG0H,QAAU,EAAI,CAC7B0H,GACHI,EAAK9B,cAAcjN,GAAG2O,GAAQ1H,EAE9B8H,EAAK9B,cAAcjN,GAAKiH,CAEzB,OAEG0H,EACHI,EAAK9B,cAAcjN,GAAG2O,GAAQpP,EAAGuD,MAEjCiM,EAAK9B,cAAcjN,GAAKT,EAAGuD,KAE5B,MACD,SACK6L,EACHI,EAAK9B,cAAcjN,GAAG2O,GAAQpP,EAAG+F,UAEjCyJ,EAAK9B,cAAcjN,GAAKT,EAAG+F,cAK/BxF,KAAKmN,cAAcb,QAAUtM,KAAKwN,mBAClC3I,QAAQ6H,IAAI1M,KAAKmN,cAAc9L,IAAK0D,KAAKwI,UAAUvN,KAAKmN,gBACxD5G,eAAe9F,UAAUwB,OAAO,QAChCuC,SAASG,aAAa3E,KAAKmN,cAAc9L,MAM1CsC,eAAgB,WACf,GAAMiB,GAAO5E,KAAKoN,eAClB,IAA2B,kBAAhBjO,QAAOsE,KAAqB,CAEtC,GAAM5D,MACA4B,EAAI/B,SAASa,cAAc,IACjCkB,GAAE+D,UAAF,4GACA,IAAM2J,GAAOzP,SAASa,cAAc,WAQpC,OAPA4O,GAAK1O,UAAUC,IAAI,SACnByO,EAAKnM,MAAQ4B,EACb/E,EAAQmN,KAAKvL,GACb5B,EAAQmN,KAAKmC,GACb3P,MAAMI,WAAWC,GACjBsP,EAAKC,YACLD,GAAKE,SAIN,GAAM/J,GAAI5F,SAASa,cAAc,KAC3B+O,EAAO,GAAI7L,OAAMmB,IAAS2K,KAAM,qBAChCC,EAAMC,IAAIC,gBAAgBJ,EAChChK,GAAEqK,KAAOH,EACTlK,EAAEsK,SAAF,aAA0B5P,KAAKmN,cAAcjI,SAC7CxF,SAASqG,KAAK1F,YAAYiF,GAC1BA,EAAEuK,QACFC,WAAW,WACVpQ,SAASqG,KAAKlF,YAAYyE,GAC1BnG,OAAOsQ,IAAIM,gBAAgBP,IACzB,IAKJ3L,YAAa,WACZ,GAAMe,GAAO5E,KAAKoN,gBACZrH,EAAAA,+CAAsD/F,KAAKmN,cAAcjI,SAAzE,qCAEmB/F,OAAO8E,SAAS0L,KAFnC,qGAMN/K,EAEM4K,EAAAA,mBAAyBQ,mBAAAA,oBAAuChQ,KAAKmN,cAAcjI,UAAnF,SAAuG8K,mBAAmBjK,GAI1HlG,KACAyF,EAAI5F,SAASa,cAAc,IACjC+E,GAAEqK,KAAOH,EACTlK,EAAEE,UAAY,2CACdF,EAAEtE,iBAAiB,QAAS,SAACC,GAC5BzB,MAAMS,UAEPJ,EAAQmN,KAAK1H,GACb9F,MAAMI,WAAWC,IAMlB+C,iBAAkB,SAAUgC,GAC3BzC,QAAQC,IAAI,mBACZ,KAECwC,EAAOA,EAAKqL,UAAUrL,EAAKsL,QAAQ,MACnCtL,EAAOA,EAAKqL,UAAU,EAAGrL,EAAKuL,YAAY,KAAO,GACjDvL,EAAOA,EAAKwL,OAEZxL,EAAOA,EAAKyL,QAAQ,kBAAmB,QACvC,IAAMC,GAAWvL,KAAKC,MAAMJ,EAC5B,KAAK0L,EAASjP,KAAwB,uBAAjBiP,EAASnI,IAC7B,KAAM,IAAIoI,OAAM,8BAEjB,IAAMC,GAAU3L,QAAQC,IAAIwL,EAASjP,IACrC,IAAgB,KAAZmP,GAAuC,KAArBA,EAAQtL,UAAmBsL,EAAQtL,WAAaoL,EAASpL,SAE9E,GAAKoL,EAASG,SAGP,CACN,GAAMC,GAAWJ,EAASG,QAC1BH,GAASG,SAAWH,EAASjP,IAC7BiP,EAASjP,IAAMqP,MALfJ,GAASG,SAAWH,EAASjP,IAC7BiP,EAASjP,IAAMrB,KAAKmE,aAQtBU,SAAQ6H,IAAI4D,EAASjP,IAAK0D,KAAKwI,UAAU+C,IACzC9L,SAASG,aAAa2L,EAASjP,KAE3BiP,EAASjP,MAAQrB,KAAKmN,cAAc9L,KACvCrB,KAAKuO,cAAc+B,EAASjP,KAE5B,MAAOJ,GACR,GAAMQ,GAAI/B,SAASa,cAAc,IACjCkB,GAAE+D,UAAF,iCAA+CvE,EAAEwE,QACjDjG,MAAMI,WAAW6B,KAOnByE,aAAc,SAAU7E,GACvB,GAAIuD,GAAOC,QAAQC,IAAIzD,EACvB,IAAa,KAATuD,EAAJ,CAGA,IACCA,EAAOG,KAAKC,MAAMJ,GACjB,MAAO3D,GACR,OAED,GAAMpB,MACA4B,EAAI/B,SAASa,cAAc,IACjCkB,GAAE+D,UAAF,mDAAiEZ,EAAKM,SAAYN,EAAKM,SAAW,aAClGrF,EAAQmN,KAAKvL,EACb,IAAMnB,GAAMZ,SAASa,cAAc,SACnCD,GAAIkF,UAAY,eAChBlF,EAAIE,aAAa,WAAYoE,EAAKvD,KAClCf,EAAIG,UAAUC,IAAI,eAClBb,EAAQmN,KAAK1M,GACbd,MAAMI,WAAWC,KAMlB2B,gBAAiB,SAAUH,GAC1B,GAAY,KAARA,GAAsB,aAARA,EAElB,GADAwD,QAAQ5C,OAAOZ,GACU,KAArBwD,QAAQC,IAAIzD,GAAa,CAE5B,GAAMI,GAAI/B,SAASa,cAAc,IACjCkB,GAAE+D,UAAF,kCACAhG,MAAMI,WAAW6B,OAIjB+C,UAASkB,gBAAgBrE,GAEE,OAAvBrB,KAAKmN,eAA0BnN,KAAKmN,cAAc9L,MAAQA,IAC7DlC,OAAO8E,SAASC,KAAhB,IAA2B3C,QAAQ4C,gBAgBvC,IAPAU,QAAQgI,aAAazM,QAAQ,SAACiB,GAC7BmD,SAASG,aAAatD,KAMnBmD,SAASqB,UAAW,CACvB,GAAMhG,YACA8Q,EAAIjR,SAASa,cAAc,KACjCoQ,GAAEnL,UAAY,uBACd3F,QAAQmN,KAAK2D,EACb,IAAMC,IAAKlR,SAASa,cAAc,IAClCqQ,IAAGpL,UAAH,oFACA3F,QAAQmN,KAAK4D,GACb,IAAMC,IAAKnR,SAASa,cAAc,IAClCsQ,IAAGrL,UAAH,oGACA3F,QAAQmN,KAAK6D,GACb,IAAMC,IAAKpR,SAASa,cAAc,IAClCuQ,IAAGtL,UAAH,2SACA3F,QAAQmN,KAAK8D,GACb,IAAMC,IAAKrR,SAASa,cAAc,IAClCwQ,IAAGvL,UAAH,qEACA3F,QAAQmN,KAAK+D,IACbvR,MAAMI,WAAWC,SAMlBV,OAAO6B,iBAAiB,aAAc,SAACC,GAAQM,QAAQ6M,oBAAsB,EAK7E,IAAMC,SAAUlP,OAAO8E,SAASC,KAAKoK,OAAO,EAC5B,MAAZD,QACH9M,QAAQgN,cAAcF,SAEtBlP,OAAO8E,SAASC,KAAhB,IAA2B3C,QAAQ4C,aAMpC,IAAM6M,aAActR,SAASuR,eAAe,eACtCC,UAAYxR,SAASC,cAAc,YACzCuR,WAAUlQ,iBAAiB,QAAS,SAACC,GACpC+P,YAAYvQ,UAAUC,IAAI,QAC1BsQ,YAAYrR,cAAc,MAAMyP,UAEjC4B,YAAYrR,cAAc,UAAUqB,iBAAiB,QAAS,SAACC,GAC9D+P,YAAYvQ,UAAUwB,OAAO,UAE9BvC,SAASqG,KAAK/E,iBAAiB,QAAS,SAACC,GACxC,GAAMyD,GAAQzD,EAAEC,OAAO8E,QAAQ,eAC/B,IAAc,OAAVtB,EAAgB,CACnB,GAAIzD,EAAEC,OAAOT,UAAUU,SAAS,YAAe,MAE/C6P,aAAYvQ,UAAUwB,OAAO","file":"bundle.js","sourcesContent":["(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require==\"function\"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error(\"Cannot find module '\"+o+\"'\");throw f.code=\"MODULE_NOT_FOUND\",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require==\"function\"&&require;for(var o=0;o {\n\t\t\tf.appendChild(el);\n\t\t});\n\t\tconst btn = document.createElement('button');\n\t\tbtn.setAttribute('type', 'button');\n\t\tbtn.classList.add('close');\n\t\tbtn.textContent = 'Close';\n\t\tf.appendChild(btn);\n\t\tthis.el.appendChild(f);\n\t},\n\t/**\n\t * Clear the alert (which makes it disappear)\n\t */\n\tclear: function () {\n\t\twhile (this.el.firstChild) {\n\t\t\tthis.el.removeChild(this.el.firstChild);\n\t\t}\n\t},\n\t/**\n\t * Setup some events, etc.\n\t */\n\tinitialize: function () {\n\t\tthis.el.addEventListener('click', (e) => {\n\t\t\tif (e.target.classList.contains('close')) {\n\t\t\t\t// close button click\n\t\t\t\te.preventDefault();\n\t\t\t\tthis.clear();\n\t\t\t} else if (e.target.classList.contains('delete-conf')) {\n\t\t\t\t// delete confirmation\n\t\t\t\tconst key = e.target.getAttribute('data-key');\n\t\t\t\tthis.clear();\n\t\t\t\tManager.deleteCharacter(key);\n\t\t\t}\n\t\t});\n\t}\n};\nAlert.initialize();\n\nif (checkFeatures() === false) {\n\t// display some kind of alert at the top of the page.\n\tconst p = document.createElement('p');\n\tp.textContent = `Sorry, your browser does not supported the required features for this app to work. Try using the latest Chrome or Firefox for best results.`;\n\tAlert.setContent(p);\n}\n\n/**\n * If AppCache is supported and used, this prompts for reloa\n */\nif ('applicationCache' in window) {\n\tconst updateAppCache = function (event) {\n\t\tconst p = document.createElement('p');\n\t\tp.textContent = `Character Sheet has been updated with new features or bug fixes. Please reload the page to get the newest code. If you have this site open in multiple tabs/windows please close them all.`;\n\t\tAlert.setContent(p);\n\t};\n\twindow.applicationCache.addEventListener('updateready', updateAppCache, false);\n\tif (window.applicationCache.status === window.applicationCache.UPDATEREADY) {\n\t\tupdateAppCache();\n\t}\n}\n\n/**\n * Restore backup dialog\n */\nconst backup_dialog = document.querySelector('.dialog-backup');\nbackup_dialog.querySelector('button[type=button]').addEventListener('click', (e) => {\n\tbackup_dialog.querySelector('form').reset();\n\tbackup_dialog.classList.remove('open');\n});\nbackup_dialog.querySelector('form').addEventListener('submit', (e) => {\n\te.preventDefault();\n\tconst input_file = e.target.querySelector('input[type=file]');\n\tconsole.log(input_file.files);\n\tif (input_file.files && input_file.files.length > 0) {\n\t\tArray.from(input_file.files).forEach((f) => {\n\t\t\tconst reader = new FileReader();\n\t\t\t// Closure to capture the file information.\n\t\t\treader.onload = (function (theFile) {\n\t\t\t\treturn function (e) {\n\t\t\t\t\tManager.restoreCharacter(e.target.result);\n\t\t\t\t};\n\t\t\t})(f);\n\t\t\treader.readAsText(f);\n\t\t});\n\t} else {\n\t\tconst input = e.target.querySelector('textarea');\n\t\tif (input.value === '') {\n\t\t\treturn;\n\t\t}\n\t\tManager.restoreCharacter(input.value);\n\t}\n\tbackup_dialog.classList.remove('open');\n\te.target.reset();\n});\n\n/**\n * Actions menu and button events\n */\nconst action_menu = document.querySelector('.app-actions');\nconst action_opener = document.querySelector('.btn-open-actions');\naction_opener.addEventListener('click', (e) => {\n\tif (action_menu.classList.contains('open')) {\n\t\t// set menu to hide overflow BEFORE it closes\n\t\taction_menu.style.overflow = 'hidden';\n\t}\n\taction_menu.classList.toggle('open');\n});\n/**\n * When the menu transitions to open we want to set overflow to visible so the Load dropdown can be visible\n */\naction_menu.addEventListener('transitionend', (e) => {\n\tconsole.log(e);\n\tconst style = window.getComputedStyle(action_menu);\n\tif (style.getPropertyValue('max-height') !== '0px') {\n\t\taction_menu.style.overflow = 'visible';\n\t}\n});\nconst action_btn_backup = action_menu.querySelector('.btn-file-backup');\nif (typeof window.Blob !== 'function') {\n\taction_btn_backup.style.display = 'none';\n}\naction_btn_backup.addEventListener('click', (e) => {\n\tManager.downloadBackup();\n});\nconst action_btn_email = action_menu.querySelector('.btn-email-backup');\naction_btn_email.addEventListener('click', (e) => {\n\tManager.emailBackup();\n});\nconst action_btn_save = action_menu.querySelector('.btn-save');\naction_btn_save.addEventListener('click', (e) => {\n\tManager.saveCharacter();\n});\nconst action_btn_new = action_menu.querySelector('.btn-new-character');\naction_btn_new.addEventListener('click', (e) => {\n\t// change hash to trigger new character?\n\twindow.location.hash = `#${Manager.generateKey()}`;\n});\nconst action_btn_restore = action_menu.querySelector('.btn-restore-backup');\naction_btn_restore.addEventListener('click', (e) => {\n\tbackup_dialog.classList.add('open');\n});\nconst action_btn_load = action_menu.querySelector('.btn-load');\naction_btn_load.addEventListener('click', (e) => {\n\te.currentTarget.nextElementSibling.classList.toggle('open');\n});\n\n/**\n * Load Menu\n */\nconst LoadMenu = {\n\t/**\n\t * DOMELement\n\t */\n\tel: document.querySelector('#character_list'),\n\t/**\n\t * Open menu\n\t */\n\topen: function () {\n\t\tthis.el.classList.add('open');\n\t},\n\t/**\n\t * Close menu\n\t */\n\tclose: function () {\n\t\tthis.el.classList.remove('open');\n\t},\n\t/**\n\t * Add character to list\n\t * @param {String} key\n\t */\n\taddCharacter: function (key) {\n\t\tlet data = Storage.get(key);\n\t\t\n\t\tif (data === '') {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tdata = JSON.parse(data);\n\t\t\t\n\t\t\tif (data.key && data.key !== '') {\n\t\t\t\t// check if it's already there\n\t\t\t\tconst existing = this.el.querySelector(`a[href=\"#${data.key}\"]`);\n\t\t\t\tif (existing !== null) {\n\t\t\t\t\t// update the text as appropriate\n\t\t\t\t\texisting.textContent = `${data.charname} (${data.charclass} ${data.level})`;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst li = document.createElement('li');\n\t\t\t\tconst a = document.createElement('a');\n\t\t\t\ta.textContent = `${data.charname} (${data.charclass} ${data.level})`;\n\t\t\t\ta.setAttribute('href', `#${data.key}`);\n\t\t\t\tli.appendChild(a);\n\t\t\t\tconst del = document.createElement('a');\n\t\t\t\tdel.classList.add('delete');\n\t\t\t\tdel.innerHTML = '❌';\n\t\t\t\tdel.setAttribute('href', '#');\n\t\t\t\tdel.setAttribute('data-key', data.key);\n\t\t\t\tli.appendChild(del);\n\t\t\t\tthis.el.querySelector('#saved_characters').appendChild(li);\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tconsole.log(e.message);\n\t\t}\n\t},\n\t/**\n\t * Remove character from list\n\t * @param {String} key\n\t */\n\tremoveCharacter: function (key) {\n\t\tconst loadlink = this.el.querySelector(`a[href=\"#${key}\"]`);\n\t\tconst li = loadlink.parentNode;\n\t\tli.parentNode.removeChild(li);\n\t},\n\t/**\n\t * Do we have any saved characters here?\n\t * @return {Boolean}\n\t */\n\tisEmpty: function () {\n\t\treturn this.el.querySelector('li') === null;\n\t},\n\t/**\n\t * Set up event handlers\n\t */\n\tinitialize: function () {\n\t\tdocument.body.addEventListener('click', (e) => {\n\t\t\tconst close = e.target.closest(`#${this.el.id}`);\n\t\t\tif (close === null) {\n\t\t\t\t// Ignore the load button (it's already handled elsewhere)\n\t\t\t\tif (e.target.classList.contains('btn-load')) { return; }\n\t\t\t\t// Hide the menus.\n\t\t\t\tthis.close();\n\t\t\t} else {\n\t\t\t\t// delete link in load menu\n\t\t\t\tif (e.target.classList.contains('delete')) {\n\t\t\t\t\te.preventDefault();\n\t\t\t\t\tconsole.log('delete!');\n\t\t\t\t\tManager.deletePrompt(e.target.getAttribute('data-key'));\n\t\t\t\t\tthis.close();\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n};\nLoadMenu.initialize();\n\n/**\n * Grab some arrays of DOM elements we might need multiple times\n */\nconst attribute_fields = Array.from(document.querySelectorAll('.pc-attributes input[type=number]'));\nconst attribute_saves = Array.from(document.querySelectorAll('.pc-attributes input[type=checkbox]'));\nconst skill_checks = Array.from(document.querySelectorAll('input[data-name=\"skills\"]'));\nconst dialog_unsaved = document.querySelector('.alert-unsaved');\n\n/**\n * Calculate the attribute modifier based on the score\n * @return {String|Number} 0, a negative number, or a positive number preceded by a +\n */\nconst calcAttrMod = function (val) {\n\tconst raw = Math.floor((val - 10) / 2);\n\treturn (raw > 0) ? `+${raw}` : raw;\n};\n/**\n * Calculate the save modifier based on the score, proficiency, and prof bonus\n * and set it\n * @param {String} attr attribute name\n */\nconst calcSaveMod = function (attr) {\n\tlet prof = 0;\n\tconst attr_field = document.querySelector(`[data-name=${attr}]`).parentNode;\n\tconst attr_mod = attr_field.querySelector('.pc-attribute-mod').innerHTML;\n\tconst save_mod = attr_field.querySelector('.pc-save-mod');\n\tconst checked = attr_field.querySelector('input[type=checkbox]').checked;\n\tif (checked) {\n\t\tprof = document.querySelector('[data-name=\"proficiency\"]').innerHTML;\n\t\tif (prof === '') {\n\t\t\tprof = 0;\n\t\t} else {\n\t\t\tprof = parseInt(prof, 10);\n\t\t}\n\t}\n\tconst raw = 0 + parseInt(prof, 10) + parseInt(attr_mod, 10);\n\tsave_mod.innerHTML = (raw > 0) ? `+${raw}` : raw;\n};\n/**\n * Calculate the modifier for a skill based on proficiency and attribute\n * @param {DOMElement} el the skill checkbox element\n */\nconst calcSkillMod = function (el) {\n\tconst skill_checked = el.checked;\n\tconst attribute = el.getAttribute('data-attr');\n\tconst mod_field = el.parentNode.nextElementSibling;\n\t\n\tlet prof = 0;\n\tif (skill_checked) {\n\t\tprof = document.querySelector('[data-name=\"proficiency\"]').innerHTML;\n\t\tif (prof === '') {\n\t\t\tprof = 0;\n\t\t} else {\n\t\t\tprof = parseInt(prof, 10);\n\t\t}\n\t}\n\tconst attr = document.querySelector(`[data-name=${attribute}]`);\n\tconst attr_mod = parseInt(calcAttrMod(attr.value), 10);\n\tconst raw = 0 + prof + attr_mod;\n\tmod_field.innerText = (raw > 0) ? `+${raw}` : raw;\n};\n/**\n * Calculate proficiency modifier based on level\n */\nconst calcProfMod = function () {\n\tconst prof = document.querySelector(`[data-name=proficiency]`);\n\tconst before = prof.innerHTML;\n\tconst level = parseInt(document.querySelector(`[data-name=level]`).innerHTML, 10);\n\tconst bonus = Math.ceil(level / 4) + 1;\n\tconst after = `+${bonus}`;\n\t// did it change?\n\tif (before !== after) {\n\t\tprof.innerHTML = `+${bonus}`;\n\t\t// recalculate saves and skills\n\t\tconst attribute_fields = Array.from(document.querySelectorAll('.pc-attributes input[type=number]'));\n\t\tattribute_fields.forEach((el) => {\n\t\t\tcalcSaveMod(el.getAttribute('data-name'));\n\t\t});\n\t\tconst skill_checks = Array.from(document.querySelectorAll('input[data-name=\"skills\"]'));\n\t\tskill_checks.forEach((el) => {\n\t\t\tcalcSkillMod(el);\n\t\t});\n\t}\n};\n/**\n * Event: Listen for contenteditable changes\n * delegate focus/blur from container (body didn't seem to work)\n * use a temporary data-before attribute to check for change\n */\ndocument.querySelector('.container').addEventListener('focus', (e) => {\n\tif (e.target.getAttribute('contenteditable') === 'true') {\n\t\te.target.setAttribute('data-before', e.target.innerHTML);\n\t}\n}, true);\ndocument.querySelector('.container').addEventListener('blur', (e) => {\n\tif (e.target.getAttribute('contenteditable') === 'true') {\n\t\tconst before = e.target.getAttribute('data-before');\n\t\tif (before !== e.target.innerHTML) {\n\t\t\tconsole.log('Changed');\n\t\t\te.target.removeAttribute('data-before');\n\t\t\tdialog_unsaved.classList.add('open');\n\t\t\t\n\t\t\t// if level then update proficiency\n\t\t\tif (e.target.getAttribute('data-name') === 'level') {\n\t\t\t\tcalcProfMod();\n\t\t\t}\n\t\t\t\n\t\t\t// Do something here... Save?\n\t\t}\n\t}\n}, true);\n/**\n * Event: When attributes change update the related modifier\n */\nattribute_fields.forEach((el) => {\n\tconst field = el.parentNode;\n\tconst mod_field = field.querySelector('.pc-attribute-mod');\n\tel.addEventListener('change', (e) => {\n\t\tmod_field.innerText = calcAttrMod(e.currentTarget.value);\n\t\tconst attr = el.getAttribute('data-name');\n\t\tcalcSaveMod(attr);\n\t\tconst skills = Array.from(document.querySelectorAll(`[data-attr=${attr}]`));\n\t\tskills.forEach((el) => {\n\t\t\tcalcSkillMod(el);\n\t\t});\n\t\tdialog_unsaved.classList.add('open');\n\t});\n});\n/**\n * Event: When save is checked recalc save mod\n */\nattribute_saves.forEach((el) => {\n\tel.addEventListener('change', (e) => {\n\t\tconst attr = e.currentTarget.getAttribute('data-subfield');\n\t\tcalcSaveMod(attr);\n\t\tdialog_unsaved.classList.add('open');\n\t});\n});\n/**\n * Event: When a skill is un/checked adjust the modifier\n */\nskill_checks.forEach((el) => {\n\tel.addEventListener('change', (e) => {\n\t\tconsole.log('check change');\n\t\tcalcSkillMod(e.currentTarget);\n\t\tdialog_unsaved.classList.add('open');\n\t});\n});\n\n/**\n * Model for character data\n */\nconst character_model = {\n\tapp: 'character-sheet-5e',\n\tkey: '',\n\tcharname: '',\n\tcharclass: '',\n\trace: '',\n\tbackground: '',\n\talignment: '',\n\tlevel: 1,\n\texperience: 0,\n\tinspiration: '',\n\tproficiency: '+2',\n\tarmor_class: 10,\n\tspeed: 30,\n\thp_cur: 0,\n\thp_max: 0,\n\thd_cur: 1,\n\thd_max: 1,\n\tstr: 10,\n\tdex: 10,\n\tcon: 10,\n\tintel: 10,\n\twis: 10,\n\tcha: 10,\n\tsaves: {\n\t\t'str': 0,\n\t\t'dex': 0,\n\t\t'con': 0,\n\t\t'intel': 0,\n\t\t'wis': 0,\n\t\t'cha': 0\n\t},\n\tskills: {\n\t\tacrobatics: 0,\n\t\tanimal_handling: 0,\n\t\tarcana: 0,\n\t\tathletics: 0,\n\t\tdeception: 0,\n\t\thistory: 0,\n\t\tinsight: 0,\n\t\tintimidation: 0,\n\t\tinvestigation: 0,\n\t\tmedicine: 0,\n\t\tnature: 0,\n\t\tperception: 0,\n\t\tperformance: 0,\n\t\tpersuasion: 0,\n\t\treligion: 0,\n\t\tsleight_of_Hand: 0,\n\t\tstealth: 0,\n\t\tsurvival: 0\n\t},\n\tweapons: '',\n\tproficiencies_other: '',\n\tlanguages: '',\n\ttraits: '',\n\tideals: '',\n\tbonds: '',\n\tflaws: '',\n\tequipment: '',\n\tcp: 0,\n\tsp: 0,\n\tgp: 0,\n\tpp: 0,\n\tfeatures: '',\n\tnotes: '',\n\tspell_ability: '',\n\tspell_save: '',\n\tspell_attack: '',\n\tspell_slots: {\n\t\t1: 0,\n\t\t2: 0,\n\t\t3: 0,\n\t\t4: 0,\n\t\t5: 0,\n\t\t6: 0,\n\t\t7: 0,\n\t\t8: 0,\n\t\t9: 0\n\t},\n\tspells: {\n\t\t0: '',\n\t\t1: '',\n\t\t2: '',\n\t\t3: '',\n\t\t4: '',\n\t\t5: '',\n\t\t6: '',\n\t\t7: '',\n\t\t8: '',\n\t\t9: ''\n\t},\n\tupdated: ''\n};\n\n/**\n * Storage:\n * Interface for localStorage\n */\nconst Storage = {\n\t/**\n\t * Returns blank or the value for the key\n\t * @param {String} key\n\t * @return {String}\n\t */\n\tget: function (key) {\n\t\tconst txt = localStorage.getItem(key);\n\t\treturn (txt !== null) ? txt : '';\n\t},\n\t/**\n\t * Store a value for the key\n\t * Warning: browsers vary for the amount of data you can store (usually ~5mb)\n\t * @param {String} key\n\t * @param {String} txt\n\t * @return {Boolean} returns false on error\n\t */\n\tset: function (key, txt) {\n\t\ttry {\n\t\t\tlocalStorage.setItem(key, txt);\n\t\t} catch (e) {\n\t\t\t// Should only happen when over quota\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t},\n\t/**\n\t * Remove a key\n\t * @param {String} key\n\t * @return void\n\t */\n\tremove: function (key) {\n\t\tlocalStorage.removeItem(key);\n\t},\n\t/**\n\t * Get an array of all keys\n\t * @return {Array}\n\t */\n\tgetAllKeys: function () {\n\t\tconst keys = [];\n\t\tif (localStorage.length > 0) {\n\t\t\tfor (let i = 0; i < localStorage.length; i++) {\n\t\t\t\tkeys.push(localStorage.key(i));\n\t\t\t}\n\t\t}\n\t\treturn keys;\n\t}\n};\n\n/**\n * If we save based on character name key then we have to account for name changes to change the localStorage key..\n */\n\n/**\n * Manager:\n * Interface for save/backup/restore of data...\n */\nconst Manager = exports.manager = {\n\t/**\n\t * Currently loaded character data is here\n\t */\n\tcur_character: null,\n\t/**\n\t * Get a json string as a character backup of the current character\n\t * In this way we make sure any new properties are included in the backup when saving\n\t * @return {String}\n\t */\n\tcharacterJSON: function () {\n\t\tconst obj = {};\n\t\tfor (const prop in this.cur_character) {\n\t\t\tobj[prop] = this.cur_character[prop];\n\t\t}\n\t\treturn JSON.stringify(obj);\n\t},\n\t/**\n\t * Return UTC datetime string for right now\n\t * @return {String}\n\t */\n\tcurrentTimestamp: function () {\n\t\tconst d = new Date();\n\t\treturn d.toUTCString();\n\t},\n\t/**\n\t * Generate a random key for character storage\n\t * make sure key is not already existing\n\t * @return {String}\n\t */\n\tgenerateKey: function () {\n\t\tlet key = (`${Math.random().toString(36)}00000000000000000`).slice(2, 9);\n\t\twhile (Storage.get(key) !== '') {\n\t\t\tkey = (`${Math.random().toString(36)}00000000000000000`).slice(2, 9);\n\t\t}\n\t\treturn key;\n\t},\n\t/**\n\t * Set data on character object\n\t */\n\tsetCurCharacterData: function (data) {\n\t\tif (typeof data !== 'object') { return; }\n\t\tconst props = Object.keys(data);\n\t\tprops.forEach((prop) => {\n\t\t\tif (typeof this.cur_character[prop] !== 'undefined') {\n\t\t\t\tthis.cur_character[prop] = data[prop];\n\t\t\t}\n\t\t});\n\t},\n\t/**\n\t * Change the character based on a hash change\n\t * or maybe I should process the event in the handler and pass it here if necessary...\n\t * @param {Object} e event object from hash change\n\t */\n\tchangeCharacter: function () {\n\t\tconst urlhash = window.location.hash.substr(1);\n\t\tconsole.log('changecharacter');\n\t\tthis.loadCharacter(urlhash);\n\t\tLoadMenu.close();\n\t},\n\t/**\n\t * Load character data based on a key\n\t * @param {String} key character identifier...????\n\t */\n\tloadCharacter: function (key) {\n\t\tconsole.log(`loadcharacter ${key}`);\n\t\tdialog_unsaved.classList.remove('open');\n\t\tconst json = Storage.get(key);\n\t\tif (json === '') {\n\t\t\tconsole.log('no character found');\n\t\t\t// prompt to load backup?\n\t\t\tthis.cur_character = Object.create(character_model);\n\t\t\tthis.cur_character.key = key;\n\t\t\tthis.renderCharacter();\n\t\t\treturn;\n\t\t}\n\t\tconst data = JSON.parse(json);\n\t\t\n\t\tthis.cur_character = Object.create(character_model);\n\t\tthis.setCurCharacterData(data);\n\t\tthis.renderCharacter();\n\t},\n\t/**\n\t * Take character data and fill it into the page\n\t */\n\trenderCharacter: function () {\n\t\tif (this.cur_character === null) { return; }\n\t\tconst fields = Array.from(document.querySelectorAll('*[data-name]'));\n\t\tfields.forEach((el) => {\n\t\t\tconst f = el.getAttribute('data-name');\n\t\t\tif (typeof this.cur_character[f] === 'undefined') {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst subf = el.getAttribute('data-subfield');\n\t\t\tif (subf !== null) {\n\t\t\t\tif (typeof this.cur_character[f][subf] === 'undefined') {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (typeof this.cur_character[f] !== 'undefined') {\n\t\t\t\tswitch (el.tagName) {\n\t\t\t\t\tcase 'INPUT':\n\t\t\t\t\tcase 'SELECT':\n\t\t\t\t\tcase 'TEXTAREA':\n\t\t\t\t\t\tif (el.getAttribute('type') === 'checkbox') {\n\t\t\t\t\t\t\tconst checked = (subf) ? this.cur_character[f][subf] : this.cur_character[f];\n\t\t\t\t\t\t\tif (checked === 1) {\n\t\t\t\t\t\t\t\tel.checked = true;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tel.checked = false;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tel.value = (subf) ? this.cur_character[f][subf] : this.cur_character[f];\n\t\t\t\t\t\tconst event = new Event('change');\n\t\t\t\t\t\tel.dispatchEvent(event);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tel.innerHTML = (subf) ? this.cur_character[f][subf] : this.cur_character[f];\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\t// Update proficiency/attr/save/skill modifiers\n\t\tcalcProfMod();\n\t\tattribute_fields.forEach((el) => {\n\t\t\tconst field = el.parentNode;\n\t\t\tconst mod_field = field.querySelector('.pc-attribute-mod');\n\t\t\tmod_field.innerText = calcAttrMod(el.value);\n\t\t\tconst attr = el.getAttribute('data-name');\n\t\t\tcalcSaveMod(attr);\n\t\t\tconst skills = Array.from(document.querySelectorAll(`[data-attr=${attr}]`));\n\t\t\tskills.forEach((el) => {\n\t\t\t\tcalcSkillMod(el);\n\t\t\t});\n\t\t});\n\t\tdialog_unsaved.classList.remove('open');\n\t},\n\t/**\n\t * Save character data to localStorage\n\t */\n\tsaveCharacter: function () {\n\t\tconsole.log('save');\n\t\tif (this.cur_character === null) {\n\t\t\tthis.cur_character = Object.create(character_model);\n\t\t}\n\t\tconst fields = Array.from(document.querySelectorAll('*[data-name]'));\n\t\tfields.forEach((el) => {\n\t\t\tconst f = el.getAttribute('data-name');\n\t\t\tif (typeof this.cur_character[f] === 'undefined') {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst subf = el.getAttribute('data-subfield');\n\t\t\tif (subf !== null) {\n\t\t\t\tif (typeof this.cur_character[f][subf] === 'undefined') {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\t// for some reason trying to set the property of an object that is a property on the prototype\n\t\t\t\t// does not get the object to exist as an own property...\n\t\t\t\tif (!this.cur_character.hasOwnProperty(f)) {\n\t\t\t\t\tthis.cur_character[f] = character_model[f];\n\t\t\t\t}\n\t\t\t}\n\t\t\tswitch (el.tagName) {\n\t\t\t\tcase 'INPUT':\n\t\t\t\tcase 'SELECT':\n\t\t\t\tcase 'TEXTAREA':\n\t\t\t\t\tif (el.getAttribute('type') === 'checkbox') {\n\t\t\t\t\t\tconst checked = el.checked ? 1 : 0;\n\t\t\t\t\t\tif (subf) {\n\t\t\t\t\t\t\tthis.cur_character[f][subf] = checked;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tthis.cur_character[f] = checked;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tif (subf) {\n\t\t\t\t\t\tthis.cur_character[f][subf] = el.value;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.cur_character[f] = el.value;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tif (subf) {\n\t\t\t\t\t\tthis.cur_character[f][subf] = el.innerHTML;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.cur_character[f] = el.innerHTML;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t});\n\t\tthis.cur_character.updated = this.currentTimestamp();\n\t\tStorage.set(this.cur_character.key, JSON.stringify(this.cur_character));\n\t\tdialog_unsaved.classList.remove('open');\n\t\tLoadMenu.addCharacter(this.cur_character.key);\n\t},\n\t/**\n\t * Save a file of the current character\n\t * Falls back to showing the data for copy/pasting\n\t */\n\tdownloadBackup: function () {\n\t\tconst data = this.characterJSON();\n\t\tif (typeof window.Blob !== 'function') {\n\t\t\t// fallback to displaying the data for copy/pasting\n\t\t\tconst content = [];\n\t\t\tconst p = document.createElement('p');\n\t\t\tp.innerHTML = `Your current browser/os does not support direct file downloads, so here is the data for you to copy/paste.`;\n\t\t\tconst text = document.createElement('textarea');\n\t\t\ttext.classList.add('large');\n\t\t\ttext.value = data;\n\t\t\tcontent.push(p);\n\t\t\tcontent.push(text);\n\t\t\tAlert.setContent(content);\n\t\t\ttext.focus();\n\t\t\ttext.select();\n\t\t\treturn;\n\t\t}\n\t\t// for env that support it, create a file for download\n\t\tconst a = document.createElement('a');\n\t\tconst file = new Blob([data], { type: 'application/json' });\n\t\tconst url = URL.createObjectURL(file);\n\t\ta.href = url;\n\t\ta.download = `character_${this.cur_character.charname}`;\n\t\tdocument.body.appendChild(a);\n\t\ta.click();\n\t\tsetTimeout(function () {\n\t\t\tdocument.body.removeChild(a);\n\t\t\twindow.URL.revokeObjectURL(url);\n\t\t}, 0);\n\t},\n\t/**\n\t * Open an email with the character data and instructions\n\t */\n\temailBackup: function () {\n\t\tconst data = this.characterJSON();\n\t\tconst body = `Below is the backup data for your character ${this.cur_character.charname}.\n\t\t\nTo use this data, go to: ${window.location.href} and click the \"Restore Backup\" button. Then paste the text below into the box.\n\t\t\n---\n\t\t\n${data}`;\n\t\t\n\t\tconst url = `mailto:?subject=${encodeURIComponent(`Character backup ${this.cur_character.charname}`)}&body=${encodeURIComponent(body)}`;\n\t\t\n\t\t// Sadly this simple solution doesn't work in iOS\n\t\t// document.location.href = url;\n\t\tconst content = [];\n\t\tconst a = document.createElement('a');\n\t\ta.href = url;\n\t\ta.innerHTML = 'Open new message in default email client';\n\t\ta.addEventListener('click', (e) => {\n\t\t\tAlert.clear();\n\t\t});\n\t\tcontent.push(a);\n\t\tAlert.setContent(content);\n\t},\n\t/**\n\t * Take json backup data and load the character\n\t * @param {String} data JSON data (we hope)\n\t */\n\trestoreCharacter: function (data) {\n\t\tconsole.log('restoreCharacter');\n\t\ttry {\n\t\t\t// strip out everything before the first \"{\" and after the last \"}\"\n\t\t\tdata = data.substring(data.indexOf('{'));\n\t\t\tdata = data.substring(0, data.lastIndexOf('}') + 1);\n\t\t\tdata = data.trim(); // just in case\n\t\t\t// convert linebreaks to html br else JSON.parse breaks\n\t\t\tdata = data.replace(/(?:\\r\\n|\\r|\\n)/g, '
');\n\t\t\tconst char_obj = JSON.parse(data);\n\t\t\tif (!char_obj.key || char_obj.app !== 'character-sheet-5e') {\n\t\t\t\tthrow new Error('Data appears to be invalid.');\n\t\t\t}\n\t\t\tconst ex_char = Storage.get(char_obj.key);\n\t\t\tif (ex_char !== '' && ex_char.charname !== '' && ex_char.charname !== char_obj.charname) {\n\t\t\t\t// existing key but different name\n\t\t\t\tif (!char_obj.key_prev) {\n\t\t\t\t\tchar_obj.key_prev = char_obj.key;\n\t\t\t\t\tchar_obj.key = this.generateKey();\n\t\t\t\t} else {\n\t\t\t\t\tconst temp_key = char_obj.key_prev;\n\t\t\t\t\tchar_obj.key_prev = char_obj.key;\n\t\t\t\t\tchar_obj.key = temp_key;\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tStorage.set(char_obj.key, JSON.stringify(char_obj));\n\t\t\tLoadMenu.addCharacter(char_obj.key);\n\t\t\t// if its the current character we should reload them\n\t\t\tif (char_obj.key === this.cur_character.key) {\n\t\t\t\tthis.loadCharacter(char_obj.key);\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tconst p = document.createElement('p');\n\t\t\tp.innerHTML = `Error processing backup data: ${e.message}`;\n\t\t\tAlert.setContent(p);\n\t\t}\n\t},\n\t/**\n\t * Prompt to confirm deletion of character\n\t * @param {String} key character key\n\t */\n\tdeletePrompt: function (key) {\n\t\tlet data = Storage.get(key);\n\t\tif (data === '') {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tdata = JSON.parse(data);\n\t\t} catch (e) {\n\t\t\treturn;\n\t\t}\n\t\tconst content = [];\n\t\tconst p = document.createElement('p');\n\t\tp.innerHTML = `Are you sure you want to delete the character: ${(data.charname) ? data.charname : '[Unnamed]'}`;\n\t\tcontent.push(p);\n\t\tconst btn = document.createElement('button');\n\t\tbtn.innerHTML = 'Yes, Delete.';\n\t\tbtn.setAttribute('data-key', data.key);\n\t\tbtn.classList.add('delete-conf');\n\t\tcontent.push(btn);\n\t\tAlert.setContent(content);\n\t},\n\t/**\n\t * Delete a character from local storage\n\t * @param {String} key character key\n\t */\n\tdeleteCharacter: function (key) {\n\t\tif (key === '' || key === 'settings') { return; }\n\t\tStorage.remove(key);\n\t\tif (Storage.get(key) !== '') {\n\t\t\t// error\n\t\t\tconst p = document.createElement('p');\n\t\t\tp.innerHTML = `Error deleting the character...`;\n\t\t\tAlert.setContent(p);\n\t\t} else {\n\t\t\t// success\n\t\t\t// remove from load list\n\t\t\tLoadMenu.removeCharacter(key);\n\t\t\t// if its the current character we should trigger \"new character\" action\n\t\t\tif (this.cur_character !== null && this.cur_character.key === key) {\n\t\t\t\twindow.location.hash = `#${Manager.generateKey()}`;\n\t\t\t}\n\t\t}\n\t}\n};\n\n/**\n * Load the saved characters into the dropdown\n */\nStorage.getAllKeys().forEach((key) => {\n\tLoadMenu.addCharacter(key);\n});\n\n/**\n * If there are no saved characters, display a first time user message\n */\nif (LoadMenu.isEmpty()) {\n\tconst content = [];\n\tconst h = document.createElement('h2');\n\th.innerHTML = 'Character Sheet. 5e.';\n\tcontent.push(h);\n\tconst p1 = document.createElement('p');\n\tp1.innerHTML = `An online character sheet for 5th edition D&D, usable offline (in some browsers).`;\n\tcontent.push(p1);\n\tconst p4 = document.createElement('p');\n\tp4.innerHTML = `Designed for modern browsers, if all else fails Chrome is your best bet and IE is your worst bet.`;\n\tcontent.push(p4);\n\tconst p2 = document.createElement('p');\n\tp2.innerHTML = `Warning: Character data is saved to your browser's local storage. This means it can be erased if you delete browser data and will not automatically transfer between browsers even on the same computer. Please Save and Backup often (or at least at the end of every gaming session)!`;\n\tcontent.push(p2);\n\tconst p3 = document.createElement('p');\n\tp3.innerHTML = `This message will only appear until you save your first character.`;\n\tcontent.push(p3);\n\tAlert.setContent(content);\n}\n\n/**\n * Event: Listen for hashchange and change the current character\n */\nwindow.addEventListener('hashchange', (e) => { Manager.changeCharacter(); }, false);\n\n/**\n * Check the hash to see if we need to load a specific character\n */\nconst urlhash = window.location.hash.substr(1);\nif (urlhash !== '') {\n\tManager.loadCharacter(urlhash);\n} else {\n\twindow.location.hash = `#${Manager.generateKey()}`;\n}\n\n/**\n * Help\n */\nconst dialog_help = document.getElementById('dialog_help');\nconst help_open = document.querySelector('.btn-help');\nhelp_open.addEventListener('click', (e) => {\n\tdialog_help.classList.add('open');\n\tdialog_help.querySelector('h1').focus();\n});\ndialog_help.querySelector('.close').addEventListener('click', (e) => {\n\tdialog_help.classList.remove('open');\n});\ndocument.body.addEventListener('click', (e) => {\n\tconst close = e.target.closest('#dialog_help');\n\tif (close === null) {\n\t\tif (e.target.classList.contains('btn-help')) { return; }\n\t\t// Hide the help.\n\t\tdialog_help.classList.remove('open');\n\t}\n});\n"]} \ No newline at end of file +{"version":3,"sources":["node_modules/browser-pack/_prelude.js","src/main.js"],"names":["checkFeatures","window","navigator","serviceWorker","register","scope","Alert","el","document","querySelector","setContent","content","Array","isArray","this","clear","f","createDocumentFragment","forEach","appendChild","btn","createElement","setAttribute","classList","add","textContent","firstChild","removeChild","initialize","_this","addEventListener","e","target","contains","preventDefault","key","getAttribute","Manager","deleteCharacter","p","updateAppCache","event","applicationCache","status","UPDATEREADY","backup_dialog","reset","remove","input_file","console","log","files","length","from","reader","FileReader","onload","theFile","restoreCharacter","result","readAsText","input","value","action_menu","action_opener","style","overflow","toggle","getComputedStyle","getPropertyValue","action_btn_backup","Blob","display","downloadBackup","action_btn_email","emailBackup","action_btn_save","saveCharacter","action_btn_new","location","hash","generateKey","action_btn_restore","action_btn_load","currentTarget","nextElementSibling","LoadMenu","open","close","addCharacter","data","Storage","get","JSON","parse","existing","charname","charclass","level","li","a","del","innerHTML","message","removeCharacter","loadlink","parentNode","isEmpty","_this2","body","closest","id","deletePrompt","attribute_fields","querySelectorAll","attribute_saves","skill_checks","dialog_unsaved","calcAttrMod","val","raw","Math","floor","calcSaveMod","attr","prof","attr_field","attr_mod","save_mod","checked","parseInt","calcSkillMod","skill_checked","attribute","mod_field","innerText","calcProfMod","before","bonus","ceil","after","removeAttribute","field","skills","character_model","app","race","background","alignment","experience","inspiration","proficiency","armor_class","speed","hp_cur","hp_max","hd_cur","hd_max","str","dex","con","intel","wis","cha","saves","acrobatics","animal_handling","arcana","athletics","deception","history","insight","intimidation","investigation","medicine","nature","perception","performance","persuasion","religion","sleight_of_Hand","stealth","survival","weapons","proficiencies_other","languages","traits","ideals","bonds","flaws","equipment","cp","sp","gp","pp","features","notes","spell_ability","spell_save","spell_attack","spell_slots","1","2","3","4","5","6","7","8","9","spells","0","updated","txt","localStorage","getItem","set","setItem","removeItem","getAllKeys","keys","i","push","exports","manager","cur_character","characterJSON","obj","prop","stringify","currentTimestamp","d","Date","toUTCString","random","toString","slice","setCurCharacterData","_this3","_typeof","props","Object","changeCharacter","urlhash","substr","loadCharacter","json","create","renderCharacter","_this4","fields","subf","tagName","Event","dispatchEvent","_this5","hasOwnProperty","text","focus","select","file","type","url","URL","createObjectURL","href","download","click","setTimeout","revokeObjectURL","encodeURIComponent","substring","indexOf","lastIndexOf","trim","replace","char_obj","Error","ex_char","key_prev","temp_key","h","p1","p4","p2","p3","dialog_help","getElementById","help_open"],"mappings":"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","file":"bundle.js","sourcesContent":["(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require==\"function\"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error(\"Cannot find module '\"+o+\"'\");throw f.code=\"MODULE_NOT_FOUND\",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require==\"function\"&&require;for(var o=0;o {\n\t\t\tf.appendChild(el);\n\t\t});\n\t\tconst btn = document.createElement('button');\n\t\tbtn.setAttribute('type', 'button');\n\t\tbtn.classList.add('close');\n\t\tbtn.textContent = 'Close';\n\t\tf.appendChild(btn);\n\t\tthis.el.appendChild(f);\n\t},\n\t/**\n\t * Clear the alert (which makes it disappear)\n\t */\n\tclear: function () {\n\t\twhile (this.el.firstChild) {\n\t\t\tthis.el.removeChild(this.el.firstChild);\n\t\t}\n\t},\n\t/**\n\t * Setup some events, etc.\n\t */\n\tinitialize: function () {\n\t\tthis.el.addEventListener('click', (e) => {\n\t\t\tif (e.target.classList.contains('close')) {\n\t\t\t\t// close button click\n\t\t\t\te.preventDefault();\n\t\t\t\tthis.clear();\n\t\t\t} else if (e.target.classList.contains('delete-conf')) {\n\t\t\t\t// delete confirmation\n\t\t\t\tconst key = e.target.getAttribute('data-key');\n\t\t\t\tthis.clear();\n\t\t\t\tManager.deleteCharacter(key);\n\t\t\t}\n\t\t});\n\t}\n};\nAlert.initialize();\n\nif (checkFeatures() === false) {\n\t// display some kind of alert at the top of the page.\n\tconst p = document.createElement('p');\n\tp.textContent = `Sorry, your browser does not supported the required features for this app to work. Try using the latest Chrome or Firefox for best results.`;\n\tAlert.setContent(p);\n}\n\n/**\n * If AppCache is supported and used, this prompts for reloa\n */\nif ('applicationCache' in window) {\n\tconst updateAppCache = function (event) {\n\t\tconst p = document.createElement('p');\n\t\tp.textContent = `Character Sheet has been updated with new features or bug fixes. Please reload the page to get the newest code. If you have this site open in multiple tabs/windows please close them all.`;\n\t\tAlert.setContent(p);\n\t};\n\twindow.applicationCache.addEventListener('updateready', updateAppCache, false);\n\tif (window.applicationCache.status === window.applicationCache.UPDATEREADY) {\n\t\tupdateAppCache();\n\t}\n}\n\n/**\n * Restore backup dialog\n */\nconst backup_dialog = document.querySelector('.dialog-backup');\nbackup_dialog.querySelector('button[type=button]').addEventListener('click', (e) => {\n\tbackup_dialog.querySelector('form').reset();\n\tbackup_dialog.classList.remove('open');\n});\nbackup_dialog.querySelector('form').addEventListener('submit', (e) => {\n\te.preventDefault();\n\tconst input_file = e.target.querySelector('input[type=file]');\n\tconsole.log(input_file.files);\n\tif (input_file.files && input_file.files.length > 0) {\n\t\tArray.from(input_file.files).forEach((f) => {\n\t\t\tconst reader = new FileReader();\n\t\t\t// Closure to capture the file information.\n\t\t\treader.onload = (function (theFile) {\n\t\t\t\treturn function (e) {\n\t\t\t\t\tManager.restoreCharacter(e.target.result);\n\t\t\t\t};\n\t\t\t})(f);\n\t\t\treader.readAsText(f);\n\t\t});\n\t} else {\n\t\tconst input = e.target.querySelector('textarea');\n\t\tif (input.value === '') {\n\t\t\treturn;\n\t\t}\n\t\tManager.restoreCharacter(input.value);\n\t}\n\tbackup_dialog.classList.remove('open');\n\te.target.reset();\n});\n\n/**\n * Actions menu and button events\n */\nconst action_menu = document.querySelector('.app-actions');\nconst action_opener = document.querySelector('.btn-open-actions');\naction_opener.addEventListener('click', (e) => {\n\tif (action_menu.classList.contains('open')) {\n\t\t// set menu to hide overflow BEFORE it closes\n\t\taction_menu.style.overflow = 'hidden';\n\t}\n\taction_menu.classList.toggle('open');\n});\n/**\n * When the menu transitions to open we want to set overflow to visible so the Load dropdown can be visible\n */\naction_menu.addEventListener('transitionend', (e) => {\n\tconsole.log(e);\n\tconst style = window.getComputedStyle(action_menu);\n\tif (style.getPropertyValue('max-height') !== '0px') {\n\t\taction_menu.style.overflow = 'visible';\n\t}\n});\nconst action_btn_backup = action_menu.querySelector('.btn-file-backup');\nif (typeof window.Blob !== 'function') {\n\taction_btn_backup.style.display = 'none';\n}\naction_btn_backup.addEventListener('click', (e) => {\n\tManager.downloadBackup();\n});\nconst action_btn_email = action_menu.querySelector('.btn-email-backup');\naction_btn_email.addEventListener('click', (e) => {\n\tManager.emailBackup();\n});\nconst action_btn_save = action_menu.querySelector('.btn-save');\naction_btn_save.addEventListener('click', (e) => {\n\tManager.saveCharacter();\n});\nconst action_btn_new = action_menu.querySelector('.btn-new-character');\naction_btn_new.addEventListener('click', (e) => {\n\t// change hash to trigger new character?\n\twindow.location.hash = `#${Manager.generateKey()}`;\n});\nconst action_btn_restore = action_menu.querySelector('.btn-restore-backup');\naction_btn_restore.addEventListener('click', (e) => {\n\tbackup_dialog.classList.add('open');\n});\nconst action_btn_load = action_menu.querySelector('.btn-load');\naction_btn_load.addEventListener('click', (e) => {\n\te.currentTarget.nextElementSibling.classList.toggle('open');\n});\n\n/**\n * Load Menu\n */\nconst LoadMenu = {\n\t/**\n\t * DOMELement\n\t */\n\tel: document.querySelector('#character_list'),\n\t/**\n\t * Open menu\n\t */\n\topen: function () {\n\t\tthis.el.classList.add('open');\n\t},\n\t/**\n\t * Close menu\n\t */\n\tclose: function () {\n\t\tthis.el.classList.remove('open');\n\t},\n\t/**\n\t * Add character to list\n\t * @param {String} key\n\t */\n\taddCharacter: function (key) {\n\t\tlet data = Storage.get(key);\n\t\t\n\t\tif (data === '') {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tdata = JSON.parse(data);\n\t\t\t\n\t\t\tif (data.key && data.key !== '') {\n\t\t\t\t// check if it's already there\n\t\t\t\tconst existing = this.el.querySelector(`a[href=\"#${data.key}\"]`);\n\t\t\t\tif (existing !== null) {\n\t\t\t\t\t// update the text as appropriate\n\t\t\t\t\texisting.textContent = `${data.charname} (${data.charclass} ${data.level})`;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst li = document.createElement('li');\n\t\t\t\tconst a = document.createElement('a');\n\t\t\t\ta.textContent = `${data.charname} (${data.charclass} ${data.level})`;\n\t\t\t\ta.setAttribute('href', `#${data.key}`);\n\t\t\t\tli.appendChild(a);\n\t\t\t\tconst del = document.createElement('a');\n\t\t\t\tdel.classList.add('delete');\n\t\t\t\tdel.innerHTML = '❌';\n\t\t\t\tdel.setAttribute('href', '#');\n\t\t\t\tdel.setAttribute('data-key', data.key);\n\t\t\t\tli.appendChild(del);\n\t\t\t\tthis.el.querySelector('#saved_characters').appendChild(li);\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tconsole.log(e.message);\n\t\t}\n\t},\n\t/**\n\t * Remove character from list\n\t * @param {String} key\n\t */\n\tremoveCharacter: function (key) {\n\t\tconst loadlink = this.el.querySelector(`a[href=\"#${key}\"]`);\n\t\tconst li = loadlink.parentNode;\n\t\tli.parentNode.removeChild(li);\n\t},\n\t/**\n\t * Do we have any saved characters here?\n\t * @return {Boolean}\n\t */\n\tisEmpty: function () {\n\t\treturn this.el.querySelector('li') === null;\n\t},\n\t/**\n\t * Set up event handlers\n\t */\n\tinitialize: function () {\n\t\tdocument.body.addEventListener('click', (e) => {\n\t\t\tconst close = e.target.closest(`#${this.el.id}`);\n\t\t\tif (close === null) {\n\t\t\t\t// Ignore the load button (it's already handled elsewhere)\n\t\t\t\tif (e.target.classList.contains('btn-load')) { return; }\n\t\t\t\t// Hide the menus.\n\t\t\t\tthis.close();\n\t\t\t} else {\n\t\t\t\t// delete link in load menu\n\t\t\t\tif (e.target.classList.contains('delete')) {\n\t\t\t\t\te.preventDefault();\n\t\t\t\t\tconsole.log('delete!');\n\t\t\t\t\tManager.deletePrompt(e.target.getAttribute('data-key'));\n\t\t\t\t\tthis.close();\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n};\nLoadMenu.initialize();\n\n/**\n * Grab some arrays of DOM elements we might need multiple times\n */\nconst attribute_fields = Array.from(document.querySelectorAll('.pc-attributes input[type=number]'));\nconst attribute_saves = Array.from(document.querySelectorAll('.pc-attributes input[type=checkbox]'));\nconst skill_checks = Array.from(document.querySelectorAll('input[data-name=\"skills\"]'));\nconst dialog_unsaved = document.querySelector('.alert-unsaved');\n\n/**\n * Calculate the attribute modifier based on the score\n * @return {String|Number} 0, a negative number, or a positive number preceded by a +\n */\nconst calcAttrMod = function (val) {\n\tconst raw = Math.floor((val - 10) / 2);\n\treturn (raw > 0) ? `+${raw}` : raw;\n};\n/**\n * Calculate the save modifier based on the score, proficiency, and prof bonus\n * and set it\n * @param {String} attr attribute name\n */\nconst calcSaveMod = function (attr) {\n\tlet prof = 0;\n\tconst attr_field = document.querySelector(`[data-name=${attr}]`).parentNode;\n\tconst attr_mod = attr_field.querySelector('.pc-attribute-mod').innerHTML;\n\tconst save_mod = attr_field.querySelector('.pc-save-mod');\n\tconst checked = attr_field.querySelector('input[type=checkbox]').checked;\n\tif (checked) {\n\t\tprof = document.querySelector('[data-name=\"proficiency\"]').innerHTML;\n\t\tif (prof === '') {\n\t\t\tprof = 0;\n\t\t} else {\n\t\t\tprof = parseInt(prof, 10);\n\t\t}\n\t}\n\tconst raw = 0 + parseInt(prof, 10) + parseInt(attr_mod, 10);\n\tsave_mod.innerHTML = (raw > 0) ? `+${raw}` : raw;\n};\n/**\n * Calculate the modifier for a skill based on proficiency and attribute\n * @param {DOMElement} el the skill checkbox element\n */\nconst calcSkillMod = function (el) {\n\tconst skill_checked = el.checked;\n\tconst attribute = el.getAttribute('data-attr');\n\tconst mod_field = el.parentNode.nextElementSibling;\n\t\n\tlet prof = 0;\n\tif (skill_checked) {\n\t\tprof = document.querySelector('[data-name=\"proficiency\"]').innerHTML;\n\t\tif (prof === '') {\n\t\t\tprof = 0;\n\t\t} else {\n\t\t\tprof = parseInt(prof, 10);\n\t\t}\n\t}\n\tconst attr = document.querySelector(`[data-name=${attribute}]`);\n\tconst attr_mod = parseInt(calcAttrMod(attr.value), 10);\n\tconst raw = 0 + prof + attr_mod;\n\tmod_field.innerText = (raw > 0) ? `+${raw}` : raw;\n};\n/**\n * Calculate proficiency modifier based on level\n */\nconst calcProfMod = function () {\n\tconst prof = document.querySelector(`[data-name=proficiency]`);\n\tconst before = prof.innerHTML;\n\tconst level = parseInt(document.querySelector(`[data-name=level]`).innerHTML, 10);\n\tconst bonus = Math.ceil(level / 4) + 1;\n\tconst after = `+${bonus}`;\n\t// did it change?\n\tif (before !== after) {\n\t\tprof.innerHTML = `+${bonus}`;\n\t\t// recalculate saves and skills\n\t\tconst attribute_fields = Array.from(document.querySelectorAll('.pc-attributes input[type=number]'));\n\t\tattribute_fields.forEach((el) => {\n\t\t\tcalcSaveMod(el.getAttribute('data-name'));\n\t\t});\n\t\tconst skill_checks = Array.from(document.querySelectorAll('input[data-name=\"skills\"]'));\n\t\tskill_checks.forEach((el) => {\n\t\t\tcalcSkillMod(el);\n\t\t});\n\t}\n};\n/**\n * Event: Listen for contenteditable changes\n * delegate focus/blur from container (body didn't seem to work)\n * use a temporary data-before attribute to check for change\n */\ndocument.querySelector('.container').addEventListener('focus', (e) => {\n\tif (e.target.getAttribute('contenteditable') === 'true') {\n\t\te.target.setAttribute('data-before', e.target.innerHTML);\n\t}\n}, true);\ndocument.querySelector('.container').addEventListener('blur', (e) => {\n\tif (e.target.getAttribute('contenteditable') === 'true') {\n\t\tconst before = e.target.getAttribute('data-before');\n\t\tif (before !== e.target.innerHTML) {\n\t\t\tconsole.log('Changed');\n\t\t\te.target.removeAttribute('data-before');\n\t\t\tdialog_unsaved.classList.add('open');\n\t\t\t\n\t\t\t// if level then update proficiency\n\t\t\tif (e.target.getAttribute('data-name') === 'level') {\n\t\t\t\tcalcProfMod();\n\t\t\t}\n\t\t\t\n\t\t\t// Do something here... Save?\n\t\t}\n\t}\n}, true);\n/**\n * Event: When attributes change update the related modifier\n */\nattribute_fields.forEach((el) => {\n\tconst field = el.parentNode;\n\tconst mod_field = field.querySelector('.pc-attribute-mod');\n\tel.addEventListener('change', (e) => {\n\t\tmod_field.innerText = calcAttrMod(e.currentTarget.value);\n\t\tconst attr = el.getAttribute('data-name');\n\t\tcalcSaveMod(attr);\n\t\tconst skills = Array.from(document.querySelectorAll(`[data-attr=${attr}]`));\n\t\tskills.forEach((el) => {\n\t\t\tcalcSkillMod(el);\n\t\t});\n\t\tdialog_unsaved.classList.add('open');\n\t});\n});\n/**\n * Event: When save is checked recalc save mod\n */\nattribute_saves.forEach((el) => {\n\tel.addEventListener('change', (e) => {\n\t\tconst attr = e.currentTarget.getAttribute('data-subfield');\n\t\tcalcSaveMod(attr);\n\t\tdialog_unsaved.classList.add('open');\n\t});\n});\n/**\n * Event: When a skill is un/checked adjust the modifier\n */\nskill_checks.forEach((el) => {\n\tel.addEventListener('change', (e) => {\n\t\tconsole.log('check change');\n\t\tcalcSkillMod(e.currentTarget);\n\t\tdialog_unsaved.classList.add('open');\n\t});\n});\n\n/**\n * Model for character data\n */\nconst character_model = {\n\tapp: 'character-sheet-5e',\n\tkey: '',\n\tcharname: '',\n\tcharclass: '',\n\trace: '',\n\tbackground: '',\n\talignment: '',\n\tlevel: 1,\n\texperience: 0,\n\tinspiration: '',\n\tproficiency: '+2',\n\tarmor_class: 10,\n\tspeed: 30,\n\thp_cur: 0,\n\thp_max: 0,\n\thd_cur: 1,\n\thd_max: 1,\n\tstr: 10,\n\tdex: 10,\n\tcon: 10,\n\tintel: 10,\n\twis: 10,\n\tcha: 10,\n\tsaves: {\n\t\t'str': 0,\n\t\t'dex': 0,\n\t\t'con': 0,\n\t\t'intel': 0,\n\t\t'wis': 0,\n\t\t'cha': 0\n\t},\n\tskills: {\n\t\tacrobatics: 0,\n\t\tanimal_handling: 0,\n\t\tarcana: 0,\n\t\tathletics: 0,\n\t\tdeception: 0,\n\t\thistory: 0,\n\t\tinsight: 0,\n\t\tintimidation: 0,\n\t\tinvestigation: 0,\n\t\tmedicine: 0,\n\t\tnature: 0,\n\t\tperception: 0,\n\t\tperformance: 0,\n\t\tpersuasion: 0,\n\t\treligion: 0,\n\t\tsleight_of_Hand: 0,\n\t\tstealth: 0,\n\t\tsurvival: 0\n\t},\n\tweapons: '',\n\tproficiencies_other: '',\n\tlanguages: '',\n\ttraits: '',\n\tideals: '',\n\tbonds: '',\n\tflaws: '',\n\tequipment: '',\n\tcp: 0,\n\tsp: 0,\n\tgp: 0,\n\tpp: 0,\n\tfeatures: '',\n\tnotes: '',\n\tspell_ability: '',\n\tspell_save: '',\n\tspell_attack: '',\n\tspell_slots: {\n\t\t1: 0,\n\t\t2: 0,\n\t\t3: 0,\n\t\t4: 0,\n\t\t5: 0,\n\t\t6: 0,\n\t\t7: 0,\n\t\t8: 0,\n\t\t9: 0\n\t},\n\tspells: {\n\t\t0: '',\n\t\t1: '',\n\t\t2: '',\n\t\t3: '',\n\t\t4: '',\n\t\t5: '',\n\t\t6: '',\n\t\t7: '',\n\t\t8: '',\n\t\t9: ''\n\t},\n\tupdated: ''\n};\n\n/**\n * Storage:\n * Interface for localStorage\n */\nconst Storage = {\n\t/**\n\t * Returns blank or the value for the key\n\t * @param {String} key\n\t * @return {String}\n\t */\n\tget: function (key) {\n\t\tconst txt = localStorage.getItem(key);\n\t\treturn (txt !== null) ? txt : '';\n\t},\n\t/**\n\t * Store a value for the key\n\t * Warning: browsers vary for the amount of data you can store (usually ~5mb)\n\t * @param {String} key\n\t * @param {String} txt\n\t * @return {Boolean} returns false on error\n\t */\n\tset: function (key, txt) {\n\t\ttry {\n\t\t\tlocalStorage.setItem(key, txt);\n\t\t} catch (e) {\n\t\t\t// Should only happen when over quota\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t},\n\t/**\n\t * Remove a key\n\t * @param {String} key\n\t * @return void\n\t */\n\tremove: function (key) {\n\t\tlocalStorage.removeItem(key);\n\t},\n\t/**\n\t * Get an array of all keys\n\t * @return {Array}\n\t */\n\tgetAllKeys: function () {\n\t\tconst keys = [];\n\t\tif (localStorage.length > 0) {\n\t\t\tfor (let i = 0; i < localStorage.length; i++) {\n\t\t\t\tkeys.push(localStorage.key(i));\n\t\t\t}\n\t\t}\n\t\treturn keys;\n\t}\n};\n\n/**\n * If we save based on character name key then we have to account for name changes to change the localStorage key..\n */\n\n/**\n * Manager:\n * Interface for save/backup/restore of data...\n */\nconst Manager = exports.manager = {\n\t/**\n\t * Currently loaded character data is here\n\t */\n\tcur_character: null,\n\t/**\n\t * Get a json string as a character backup of the current character\n\t * In this way we make sure any new properties are included in the backup when saving\n\t * @return {String}\n\t */\n\tcharacterJSON: function () {\n\t\tconst obj = {};\n\t\tfor (const prop in this.cur_character) {\n\t\t\tobj[prop] = this.cur_character[prop];\n\t\t}\n\t\treturn JSON.stringify(obj);\n\t},\n\t/**\n\t * Return UTC datetime string for right now\n\t * @return {String}\n\t */\n\tcurrentTimestamp: function () {\n\t\tconst d = new Date();\n\t\treturn d.toUTCString();\n\t},\n\t/**\n\t * Generate a random key for character storage\n\t * make sure key is not already existing\n\t * @return {String}\n\t */\n\tgenerateKey: function () {\n\t\tlet key = (`${Math.random().toString(36)}00000000000000000`).slice(2, 9);\n\t\twhile (Storage.get(key) !== '') {\n\t\t\tkey = (`${Math.random().toString(36)}00000000000000000`).slice(2, 9);\n\t\t}\n\t\treturn key;\n\t},\n\t/**\n\t * Set data on character object\n\t */\n\tsetCurCharacterData: function (data) {\n\t\tif (typeof data !== 'object') { return; }\n\t\tconst props = Object.keys(data);\n\t\tprops.forEach((prop) => {\n\t\t\tif (typeof this.cur_character[prop] !== 'undefined') {\n\t\t\t\tthis.cur_character[prop] = data[prop];\n\t\t\t}\n\t\t});\n\t},\n\t/**\n\t * Change the character based on a hash change\n\t * or maybe I should process the event in the handler and pass it here if necessary...\n\t * @param {Object} e event object from hash change\n\t */\n\tchangeCharacter: function () {\n\t\tconst urlhash = window.location.hash.substr(1);\n\t\tconsole.log('changecharacter');\n\t\tthis.loadCharacter(urlhash);\n\t\tLoadMenu.close();\n\t},\n\t/**\n\t * Load character data based on a key\n\t * @param {String} key character identifier...????\n\t */\n\tloadCharacter: function (key) {\n\t\tconsole.log(`loadcharacter ${key}`);\n\t\tdialog_unsaved.classList.remove('open');\n\t\tconst json = Storage.get(key);\n\t\tif (json === '') {\n\t\t\tconsole.log('no character found');\n\t\t\t// prompt to load backup?\n\t\t\tthis.cur_character = Object.create(character_model);\n\t\t\tthis.cur_character.key = key;\n\t\t\tthis.renderCharacter();\n\t\t\treturn;\n\t\t}\n\t\tconst data = JSON.parse(json);\n\t\t\n\t\tthis.cur_character = Object.create(character_model);\n\t\tthis.setCurCharacterData(data);\n\t\tthis.renderCharacter();\n\t},\n\t/**\n\t * Take character data and fill it into the page\n\t */\n\trenderCharacter: function () {\n\t\tif (this.cur_character === null) { return; }\n\t\tconst fields = Array.from(document.querySelectorAll('*[data-name]'));\n\t\tfields.forEach((el) => {\n\t\t\tconst f = el.getAttribute('data-name');\n\t\t\tif (typeof this.cur_character[f] === 'undefined') {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst subf = el.getAttribute('data-subfield');\n\t\t\tif (subf !== null) {\n\t\t\t\tif (typeof this.cur_character[f][subf] === 'undefined') {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (typeof this.cur_character[f] !== 'undefined') {\n\t\t\t\tswitch (el.tagName) {\n\t\t\t\t\tcase 'INPUT':\n\t\t\t\t\tcase 'SELECT':\n\t\t\t\t\tcase 'TEXTAREA':\n\t\t\t\t\t\tif (el.getAttribute('type') === 'checkbox') {\n\t\t\t\t\t\t\tconst checked = (subf) ? this.cur_character[f][subf] : this.cur_character[f];\n\t\t\t\t\t\t\tif (checked === 1) {\n\t\t\t\t\t\t\t\tel.checked = true;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tel.checked = false;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tel.value = (subf) ? this.cur_character[f][subf] : this.cur_character[f];\n\t\t\t\t\t\tconst event = new Event('change');\n\t\t\t\t\t\tel.dispatchEvent(event);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tel.innerHTML = (subf) ? this.cur_character[f][subf] : this.cur_character[f];\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\t// Update proficiency/attr/save/skill modifiers\n\t\tcalcProfMod();\n\t\tattribute_fields.forEach((el) => {\n\t\t\tconst field = el.parentNode;\n\t\t\tconst mod_field = field.querySelector('.pc-attribute-mod');\n\t\t\tmod_field.innerText = calcAttrMod(el.value);\n\t\t\tconst attr = el.getAttribute('data-name');\n\t\t\tcalcSaveMod(attr);\n\t\t\tconst skills = Array.from(document.querySelectorAll(`[data-attr=${attr}]`));\n\t\t\tskills.forEach((el) => {\n\t\t\t\tcalcSkillMod(el);\n\t\t\t});\n\t\t});\n\t\tdialog_unsaved.classList.remove('open');\n\t},\n\t/**\n\t * Save character data to localStorage\n\t */\n\tsaveCharacter: function () {\n\t\tconsole.log('save');\n\t\tif (this.cur_character === null) {\n\t\t\tthis.cur_character = Object.create(character_model);\n\t\t}\n\t\tconst fields = Array.from(document.querySelectorAll('*[data-name]'));\n\t\tfields.forEach((el) => {\n\t\t\tconst f = el.getAttribute('data-name');\n\t\t\tif (typeof this.cur_character[f] === 'undefined') {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst subf = el.getAttribute('data-subfield');\n\t\t\tif (subf !== null) {\n\t\t\t\tif (typeof this.cur_character[f][subf] === 'undefined') {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\t// for some reason trying to set the property of an object that is a property on the prototype\n\t\t\t\t// does not get the object to exist as an own property...\n\t\t\t\tif (!this.cur_character.hasOwnProperty(f)) {\n\t\t\t\t\tthis.cur_character[f] = character_model[f];\n\t\t\t\t}\n\t\t\t}\n\t\t\tswitch (el.tagName) {\n\t\t\t\tcase 'INPUT':\n\t\t\t\tcase 'SELECT':\n\t\t\t\tcase 'TEXTAREA':\n\t\t\t\t\tif (el.getAttribute('type') === 'checkbox') {\n\t\t\t\t\t\tconst checked = el.checked ? 1 : 0;\n\t\t\t\t\t\tif (subf) {\n\t\t\t\t\t\t\tthis.cur_character[f][subf] = checked;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tthis.cur_character[f] = checked;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tif (subf) {\n\t\t\t\t\t\tthis.cur_character[f][subf] = el.value;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.cur_character[f] = el.value;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tif (subf) {\n\t\t\t\t\t\tthis.cur_character[f][subf] = el.innerHTML;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.cur_character[f] = el.innerHTML;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t});\n\t\tif (this.cur_character.charname === '') {\n\t\t\tconst p = document.createElement('p');\n\t\t\tp.innerHTML = 'Your character must have name to save!';\n\t\t\tAlert.setContent(p);\n\t\t\treturn;\n\t\t}\n\t\tthis.cur_character.updated = this.currentTimestamp();\n\t\tStorage.set(this.cur_character.key, JSON.stringify(this.cur_character));\n\t\tdialog_unsaved.classList.remove('open');\n\t\tLoadMenu.addCharacter(this.cur_character.key);\n\t},\n\t/**\n\t * Save a file of the current character\n\t * Falls back to showing the data for copy/pasting\n\t */\n\tdownloadBackup: function () {\n\t\tconst data = this.characterJSON();\n\t\tif (typeof window.Blob !== 'function') {\n\t\t\t// fallback to displaying the data for copy/pasting\n\t\t\tconst content = [];\n\t\t\tconst p = document.createElement('p');\n\t\t\tp.innerHTML = `Your current browser/os does not support direct file downloads, so here is the data for you to copy/paste.`;\n\t\t\tconst text = document.createElement('textarea');\n\t\t\ttext.classList.add('large');\n\t\t\ttext.value = data;\n\t\t\tcontent.push(p);\n\t\t\tcontent.push(text);\n\t\t\tAlert.setContent(content);\n\t\t\ttext.focus();\n\t\t\ttext.select();\n\t\t\treturn;\n\t\t}\n\t\t// for env that support it, create a file for download\n\t\tconst a = document.createElement('a');\n\t\tconst file = new Blob([data], { type: 'application/json' });\n\t\tconst url = URL.createObjectURL(file);\n\t\ta.href = url;\n\t\ta.download = `character_${this.cur_character.charname}`;\n\t\tdocument.body.appendChild(a);\n\t\ta.click();\n\t\tsetTimeout(function () {\n\t\t\tdocument.body.removeChild(a);\n\t\t\twindow.URL.revokeObjectURL(url);\n\t\t}, 0);\n\t},\n\t/**\n\t * Open an email with the character data and instructions\n\t */\n\temailBackup: function () {\n\t\tconst data = this.characterJSON();\n\t\tconst body = `Below is the backup data for your character ${this.cur_character.charname}.\n\t\t\nTo use this data, go to: ${window.location.href} and click the \"Restore Backup\" button. Then paste the text below into the box.\n\t\t\n---\n\t\t\n${data}`;\n\t\t\n\t\tconst url = `mailto:?subject=${encodeURIComponent(`Character backup ${this.cur_character.charname}`)}&body=${encodeURIComponent(body)}`;\n\t\t\n\t\t// Sadly this simple solution doesn't work in iOS\n\t\t// document.location.href = url;\n\t\tconst content = [];\n\t\tconst a = document.createElement('a');\n\t\ta.href = url;\n\t\ta.innerHTML = 'Open new message in default email client';\n\t\ta.addEventListener('click', (e) => {\n\t\t\tAlert.clear();\n\t\t});\n\t\tcontent.push(a);\n\t\tAlert.setContent(content);\n\t},\n\t/**\n\t * Take json backup data and load the character\n\t * @param {String} data JSON data (we hope)\n\t */\n\trestoreCharacter: function (data) {\n\t\tconsole.log('restoreCharacter');\n\t\ttry {\n\t\t\t// strip out everything before the first \"{\" and after the last \"}\"\n\t\t\tdata = data.substring(data.indexOf('{'));\n\t\t\tdata = data.substring(0, data.lastIndexOf('}') + 1);\n\t\t\tdata = data.trim(); // just in case\n\t\t\t// convert linebreaks to html br else JSON.parse breaks\n\t\t\tdata = data.replace(/(?:\\r\\n|\\r|\\n)/g, '
');\n\t\t\tconst char_obj = JSON.parse(data);\n\t\t\tif (!char_obj.key || char_obj.app !== 'character-sheet-5e') {\n\t\t\t\tthrow new Error('Data appears to be invalid.');\n\t\t\t}\n\t\t\tconst ex_char = Storage.get(char_obj.key);\n\t\t\tif (ex_char !== '' && ex_char.charname !== '' && ex_char.charname !== char_obj.charname) {\n\t\t\t\t// existing key but different name\n\t\t\t\tif (!char_obj.key_prev) {\n\t\t\t\t\tchar_obj.key_prev = char_obj.key;\n\t\t\t\t\tchar_obj.key = this.generateKey();\n\t\t\t\t} else {\n\t\t\t\t\tconst temp_key = char_obj.key_prev;\n\t\t\t\t\tchar_obj.key_prev = char_obj.key;\n\t\t\t\t\tchar_obj.key = temp_key;\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tStorage.set(char_obj.key, JSON.stringify(char_obj));\n\t\t\tLoadMenu.addCharacter(char_obj.key);\n\t\t\t// if its the current character we should reload them\n\t\t\tif (char_obj.key === this.cur_character.key) {\n\t\t\t\tthis.loadCharacter(char_obj.key);\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tconst p = document.createElement('p');\n\t\t\tp.innerHTML = `Error processing backup data: ${e.message}`;\n\t\t\tAlert.setContent(p);\n\t\t}\n\t},\n\t/**\n\t * Prompt to confirm deletion of character\n\t * @param {String} key character key\n\t */\n\tdeletePrompt: function (key) {\n\t\tlet data = Storage.get(key);\n\t\tif (data === '') {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tdata = JSON.parse(data);\n\t\t} catch (e) {\n\t\t\treturn;\n\t\t}\n\t\tconst content = [];\n\t\tconst p = document.createElement('p');\n\t\tp.innerHTML = `Are you sure you want to delete the character: ${(data.charname) ? data.charname : '[Unnamed]'}`;\n\t\tcontent.push(p);\n\t\tconst btn = document.createElement('button');\n\t\tbtn.innerHTML = 'Yes, Delete.';\n\t\tbtn.setAttribute('data-key', data.key);\n\t\tbtn.classList.add('delete-conf');\n\t\tcontent.push(btn);\n\t\tAlert.setContent(content);\n\t},\n\t/**\n\t * Delete a character from local storage\n\t * @param {String} key character key\n\t */\n\tdeleteCharacter: function (key) {\n\t\tif (key === '' || key === 'settings') { return; }\n\t\tStorage.remove(key);\n\t\tif (Storage.get(key) !== '') {\n\t\t\t// error\n\t\t\tconst p = document.createElement('p');\n\t\t\tp.innerHTML = `Error deleting the character...`;\n\t\t\tAlert.setContent(p);\n\t\t} else {\n\t\t\t// success\n\t\t\t// remove from load list\n\t\t\tLoadMenu.removeCharacter(key);\n\t\t\t// if its the current character we should trigger \"new character\" action\n\t\t\tif (this.cur_character !== null && this.cur_character.key === key) {\n\t\t\t\twindow.location.hash = `#${Manager.generateKey()}`;\n\t\t\t}\n\t\t}\n\t}\n};\n\n/**\n * Load the saved characters into the dropdown\n */\nStorage.getAllKeys().forEach((key) => {\n\tLoadMenu.addCharacter(key);\n});\n\n/**\n * If there are no saved characters, display a first time user message\n */\nif (LoadMenu.isEmpty()) {\n\tconst content = [];\n\tconst h = document.createElement('h2');\n\th.innerHTML = 'Character Sheet. 5e.';\n\tcontent.push(h);\n\tconst p1 = document.createElement('p');\n\tp1.innerHTML = `An online character sheet for 5th edition D&D, usable offline (in some browsers).`;\n\tcontent.push(p1);\n\tconst p4 = document.createElement('p');\n\tp4.innerHTML = `Designed for modern browsers, if all else fails Chrome is your best bet and IE is your worst bet.`;\n\tcontent.push(p4);\n\tconst p2 = document.createElement('p');\n\tp2.innerHTML = `Warning: Character data is saved to your browser's local storage. This means it can be erased if you delete browser data and will not automatically transfer between browsers even on the same computer. Please Save and Backup often (or at least at the end of every gaming session)!`;\n\tcontent.push(p2);\n\tconst p3 = document.createElement('p');\n\tp3.innerHTML = `This message will only appear until you save your first character.`;\n\tcontent.push(p3);\n\tAlert.setContent(content);\n}\n\n/**\n * Event: Listen for hashchange and change the current character\n */\nwindow.addEventListener('hashchange', (e) => { Manager.changeCharacter(); }, false);\n\n/**\n * Check the hash to see if we need to load a specific character\n */\nconst urlhash = window.location.hash.substr(1);\nif (urlhash !== '') {\n\tManager.loadCharacter(urlhash);\n} else {\n\twindow.location.hash = `#${Manager.generateKey()}`;\n}\n\n/**\n * Help\n */\nconst dialog_help = document.getElementById('dialog_help');\nconst help_open = document.querySelector('.btn-help');\nhelp_open.addEventListener('click', (e) => {\n\tdialog_help.classList.add('open');\n\tdialog_help.querySelector('h1').focus();\n});\ndialog_help.querySelector('.close').addEventListener('click', (e) => {\n\tdialog_help.classList.remove('open');\n});\ndocument.body.addEventListener('click', (e) => {\n\tconst close = e.target.closest('#dialog_help');\n\tif (close === null) {\n\t\tif (e.target.classList.contains('btn-help')) { return; }\n\t\t// Hide the help.\n\t\tdialog_help.classList.remove('open');\n\t}\n});\n"]} \ No newline at end of file diff --git a/main.min.js b/main.min.js index e7aa708..2c7b9e0 100644 --- a/main.min.js +++ b/main.min.js @@ -1,5 +1,5 @@ (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.charsheet = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o0)Array.from(t.files).forEach(function(e){var t=new FileReader;t.onload=function(e){return function(e){Manager.restoreCharacter(e.target.result)}}(e),t.readAsText(e)});else{var a=e.target.querySelector("textarea");if(""===a.value)return;Manager.restoreCharacter(a.value)}backup_dialog.classList.remove("open"),e.target.reset()});var action_menu=document.querySelector(".app-actions"),action_opener=document.querySelector(".btn-open-actions");action_opener.addEventListener("click",function(e){action_menu.classList.contains("open")&&(action_menu.style.overflow="hidden"),action_menu.classList.toggle("open")}),action_menu.addEventListener("transitionend",function(e){console.log(e);var t=window.getComputedStyle(action_menu);"0px"!==t.getPropertyValue("max-height")&&(action_menu.style.overflow="visible")});var action_btn_backup=action_menu.querySelector(".btn-file-backup");"function"!=typeof window.Blob&&(action_btn_backup.style.display="none"),action_btn_backup.addEventListener("click",function(e){Manager.downloadBackup()});var action_btn_email=action_menu.querySelector(".btn-email-backup");action_btn_email.addEventListener("click",function(e){Manager.emailBackup()});var action_btn_save=action_menu.querySelector(".btn-save");action_btn_save.addEventListener("click",function(e){Manager.saveCharacter()});var action_btn_new=action_menu.querySelector(".btn-new-character");action_btn_new.addEventListener("click",function(e){window.location.hash="#"+Manager.generateKey()});var action_btn_restore=action_menu.querySelector(".btn-restore-backup");action_btn_restore.addEventListener("click",function(e){backup_dialog.classList.add("open")});var action_btn_load=action_menu.querySelector(".btn-load");action_btn_load.addEventListener("click",function(e){e.currentTarget.nextElementSibling.classList.toggle("open")});var LoadMenu={el:document.querySelector("#character_list"),open:function(){this.el.classList.add("open")},close:function(){this.el.classList.remove("open")},addCharacter:function(e){var t=Storage.get(e);if(""!==t)try{if(t=JSON.parse(t),t.key&&""!==t.key){var a=this.el.querySelector('a[href="#'+t.key+'"]');if(null!==a)return void(a.textContent=t.charname+" ("+t.charclass+" "+t.level+")");var r=document.createElement("li"),n=document.createElement("a");n.textContent=t.charname+" ("+t.charclass+" "+t.level+")",n.setAttribute("href","#"+t.key),r.appendChild(n);var c=document.createElement("a");c.classList.add("delete"),c.innerHTML="❌",c.setAttribute("href","#"),c.setAttribute("data-key",t.key),r.appendChild(c),this.el.querySelector("#saved_characters").appendChild(r)}}catch(e){console.log(e.message)}},removeCharacter:function(e){var t=this.el.querySelector('a[href="#'+e+'"]'),a=t.parentNode;a.parentNode.removeChild(a)},isEmpty:function(){return null===this.el.querySelector("li")},initialize:function(){var e=this;document.body.addEventListener("click",function(t){var a=t.target.closest("#"+e.el.id);if(null===a){if(t.target.classList.contains("btn-load"))return;e.close()}else t.target.classList.contains("delete")&&(t.preventDefault(),console.log("delete!"),Manager.deletePrompt(t.target.getAttribute("data-key")),e.close())})}};LoadMenu.initialize();var attribute_fields=Array.from(document.querySelectorAll(".pc-attributes input[type=number]")),attribute_saves=Array.from(document.querySelectorAll(".pc-attributes input[type=checkbox]")),skill_checks=Array.from(document.querySelectorAll('input[data-name="skills"]')),dialog_unsaved=document.querySelector(".alert-unsaved"),calcAttrMod=function(e){var t=Math.floor((e-10)/2);return t>0?"+"+t:t},calcSaveMod=function(e){var t=0,a=document.querySelector("[data-name="+e+"]").parentNode,r=a.querySelector(".pc-attribute-mod").innerHTML,n=a.querySelector(".pc-save-mod"),c=a.querySelector("input[type=checkbox]").checked;c&&(t=document.querySelector('[data-name="proficiency"]').innerHTML,t=""===t?0:parseInt(t,10));var o=0+parseInt(t,10)+parseInt(r,10);n.innerHTML=o>0?"+"+o:o},calcSkillMod=function(e){var t=e.checked,a=e.getAttribute("data-attr"),r=e.parentNode.nextElementSibling,n=0;t&&(n=document.querySelector('[data-name="proficiency"]').innerHTML,n=""===n?0:parseInt(n,10));var c=document.querySelector("[data-name="+a+"]"),o=parseInt(calcAttrMod(c.value),10),i=0+n+o;r.innerText=i>0?"+"+i:i},calcProfMod=function(){var e=document.querySelector("[data-name=proficiency]"),t=e.innerHTML,a=parseInt(document.querySelector("[data-name=level]").innerHTML,10),r=Math.ceil(a/4)+1,n="+"+r;if(t!==n){e.innerHTML="+"+r;var c=Array.from(document.querySelectorAll(".pc-attributes input[type=number]"));c.forEach(function(e){calcSaveMod(e.getAttribute("data-name"))});var o=Array.from(document.querySelectorAll('input[data-name="skills"]'));o.forEach(function(e){calcSkillMod(e)})}};document.querySelector(".container").addEventListener("focus",function(e){"true"===e.target.getAttribute("contenteditable")&&e.target.setAttribute("data-before",e.target.innerHTML)},!0),document.querySelector(".container").addEventListener("blur",function(e){if("true"===e.target.getAttribute("contenteditable")){var t=e.target.getAttribute("data-before");t!==e.target.innerHTML&&(console.log("Changed"),e.target.removeAttribute("data-before"),dialog_unsaved.classList.add("open"),"level"===e.target.getAttribute("data-name")&&calcProfMod())}},!0),attribute_fields.forEach(function(e){var t=e.parentNode,a=t.querySelector(".pc-attribute-mod");e.addEventListener("change",function(t){a.innerText=calcAttrMod(t.currentTarget.value);var r=e.getAttribute("data-name");calcSaveMod(r);var n=Array.from(document.querySelectorAll("[data-attr="+r+"]"));n.forEach(function(e){calcSkillMod(e)}),dialog_unsaved.classList.add("open")})}),attribute_saves.forEach(function(e){e.addEventListener("change",function(e){var t=e.currentTarget.getAttribute("data-subfield");calcSaveMod(t),dialog_unsaved.classList.add("open")})}),skill_checks.forEach(function(e){e.addEventListener("change",function(e){console.log("check change"),calcSkillMod(e.currentTarget),dialog_unsaved.classList.add("open")})});var character_model={app:"character-sheet-5e",key:"",charname:"",charclass:"",race:"",background:"",alignment:"",level:1,experience:0,inspiration:"",proficiency:"+2",armor_class:10,speed:30,hp_cur:0,hp_max:0,hd_cur:1,hd_max:1,str:10,dex:10,con:10,intel:10,wis:10,cha:10,saves:{str:0,dex:0,con:0,intel:0,wis:0,cha:0},skills:{acrobatics:0,animal_handling:0,arcana:0,athletics:0,deception:0,history:0,insight:0,intimidation:0,investigation:0,medicine:0,nature:0,perception:0,performance:0,persuasion:0,religion:0,sleight_of_Hand:0,stealth:0,survival:0},weapons:"",proficiencies_other:"",languages:"",traits:"",ideals:"",bonds:"",flaws:"",equipment:"",cp:0,sp:0,gp:0,pp:0,features:"",notes:"",spell_ability:"",spell_save:"",spell_attack:"",spell_slots:{1:0,2:0,3:0,4:0,5:0,6:0,7:0,8:0,9:0},spells:{0:"",1:"",2:"",3:"",4:"",5:"",6:"",7:"",8:"",9:""},updated:""},Storage={get:function(e){var t=localStorage.getItem(e);return null!==t?t:""},set:function(e,t){try{localStorage.setItem(e,t)}catch(e){return!1}return!0},remove:function(e){localStorage.removeItem(e)},getAllKeys:function(){var e=[];if(localStorage.length>0)for(var t=0;t");var t=JSON.parse(e);if(!t.key||"character-sheet-5e"!==t.app)throw new Error("Data appears to be invalid.");var a=Storage.get(t.key);if(""!==a&&""!==a.charname&&a.charname!==t.charname)if(t.key_prev){var r=t.key_prev;t.key_prev=t.key,t.key=r}else t.key_prev=t.key,t.key=this.generateKey();Storage.set(t.key,JSON.stringify(t)),LoadMenu.addCharacter(t.key),t.key===this.cur_character.key&&this.loadCharacter(t.key)}catch(e){var n=document.createElement("p");n.innerHTML="Error processing backup data: "+e.message,Alert.setContent(n)}},deletePrompt:function(e){var t=Storage.get(e);if(""!==t){try{t=JSON.parse(t)}catch(e){return}var a=[],r=document.createElement("p");r.innerHTML="Are you sure you want to delete the character: "+(t.charname?t.charname:"[Unnamed]"),a.push(r);var n=document.createElement("button");n.innerHTML="Yes, Delete.",n.setAttribute("data-key",t.key),n.classList.add("delete-conf"),a.push(n),Alert.setContent(a)}},deleteCharacter:function(e){if(""!==e&&"settings"!==e)if(Storage.remove(e),""!==Storage.get(e)){var t=document.createElement("p");t.innerHTML="Error deleting the character...",Alert.setContent(t)}else LoadMenu.removeCharacter(e),null!==this.cur_character&&this.cur_character.key===e&&(window.location.hash="#"+Manager.generateKey())}};if(Storage.getAllKeys().forEach(function(e){LoadMenu.addCharacter(e)}),LoadMenu.isEmpty()){var content=[],h=document.createElement("h2");h.innerHTML="Character Sheet. 5e.",content.push(h);var p1=document.createElement("p");p1.innerHTML="An online character sheet for 5th edition D&D, usable offline (in some browsers).",content.push(p1);var p4=document.createElement("p");p4.innerHTML="Designed for modern browsers, if all else fails Chrome is your best bet and IE is your worst bet.",content.push(p4);var p2=document.createElement("p");p2.innerHTML="Warning: Character data is saved to your browser's local storage. This means it can be erased if you delete browser data and will not automatically transfer between browsers even on the same computer. Please Save and Backup often (or at least at the end of every gaming session)!",content.push(p2);var p3=document.createElement("p");p3.innerHTML="This message will only appear until you save your first character.",content.push(p3),Alert.setContent(content)}window.addEventListener("hashchange",function(e){Manager.changeCharacter()},!1);var urlhash=window.location.hash.substr(1);""!==urlhash?Manager.loadCharacter(urlhash):window.location.hash="#"+Manager.generateKey();var dialog_help=document.getElementById("dialog_help"),help_open=document.querySelector(".btn-help");help_open.addEventListener("click",function(e){dialog_help.classList.add("open"),dialog_help.querySelector("h1").focus()}),dialog_help.querySelector(".close").addEventListener("click",function(e){dialog_help.classList.remove("open")}),document.body.addEventListener("click",function(e){var t=e.target.closest("#dialog_help");if(null===t){if(e.target.classList.contains("btn-help"))return;dialog_help.classList.remove("open")}}); +"use strict";var _typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},checkFeatures=function(){if(!("localStorage"in window&&null!==window.localStorage))return!1};"serviceWorker"in navigator&&navigator.serviceWorker.register("service_worker.js",{scope:"/"});var Alert={el:document.querySelector(".alert-main"),setContent:function(e){Array.isArray(e)||(e=[e]),this.clear();var t=document.createDocumentFragment();e.forEach(function(e){t.appendChild(e)});var a=document.createElement("button");a.setAttribute("type","button"),a.classList.add("close"),a.textContent="Close",t.appendChild(a),this.el.appendChild(t)},clear:function(){for(;this.el.firstChild;)this.el.removeChild(this.el.firstChild)},initialize:function(){var e=this;this.el.addEventListener("click",function(t){if(t.target.classList.contains("close"))t.preventDefault(),e.clear();else if(t.target.classList.contains("delete-conf")){var a=t.target.getAttribute("data-key");e.clear(),Manager.deleteCharacter(a)}})}};if(Alert.initialize(),checkFeatures()===!1){var p=document.createElement("p");p.textContent="Sorry, your browser does not supported the required features for this app to work. Try using the latest Chrome or Firefox for best results.",Alert.setContent(p)}if("applicationCache"in window){var updateAppCache=function(e){var t=document.createElement("p");t.textContent="Character Sheet has been updated with new features or bug fixes. Please reload the page to get the newest code. If you have this site open in multiple tabs/windows please close them all.",Alert.setContent(t)};window.applicationCache.addEventListener("updateready",updateAppCache,!1),window.applicationCache.status===window.applicationCache.UPDATEREADY&&updateAppCache()}var backup_dialog=document.querySelector(".dialog-backup");backup_dialog.querySelector("button[type=button]").addEventListener("click",function(e){backup_dialog.querySelector("form").reset(),backup_dialog.classList.remove("open")}),backup_dialog.querySelector("form").addEventListener("submit",function(e){e.preventDefault();var t=e.target.querySelector("input[type=file]");if(console.log(t.files),t.files&&t.files.length>0)Array.from(t.files).forEach(function(e){var t=new FileReader;t.onload=function(e){return function(e){Manager.restoreCharacter(e.target.result)}}(e),t.readAsText(e)});else{var a=e.target.querySelector("textarea");if(""===a.value)return;Manager.restoreCharacter(a.value)}backup_dialog.classList.remove("open"),e.target.reset()});var action_menu=document.querySelector(".app-actions"),action_opener=document.querySelector(".btn-open-actions");action_opener.addEventListener("click",function(e){action_menu.classList.contains("open")&&(action_menu.style.overflow="hidden"),action_menu.classList.toggle("open")}),action_menu.addEventListener("transitionend",function(e){console.log(e);var t=window.getComputedStyle(action_menu);"0px"!==t.getPropertyValue("max-height")&&(action_menu.style.overflow="visible")});var action_btn_backup=action_menu.querySelector(".btn-file-backup");"function"!=typeof window.Blob&&(action_btn_backup.style.display="none"),action_btn_backup.addEventListener("click",function(e){Manager.downloadBackup()});var action_btn_email=action_menu.querySelector(".btn-email-backup");action_btn_email.addEventListener("click",function(e){Manager.emailBackup()});var action_btn_save=action_menu.querySelector(".btn-save");action_btn_save.addEventListener("click",function(e){Manager.saveCharacter()});var action_btn_new=action_menu.querySelector(".btn-new-character");action_btn_new.addEventListener("click",function(e){window.location.hash="#"+Manager.generateKey()});var action_btn_restore=action_menu.querySelector(".btn-restore-backup");action_btn_restore.addEventListener("click",function(e){backup_dialog.classList.add("open")});var action_btn_load=action_menu.querySelector(".btn-load");action_btn_load.addEventListener("click",function(e){e.currentTarget.nextElementSibling.classList.toggle("open")});var LoadMenu={el:document.querySelector("#character_list"),open:function(){this.el.classList.add("open")},close:function(){this.el.classList.remove("open")},addCharacter:function(e){var t=Storage.get(e);if(""!==t)try{if(t=JSON.parse(t),t.key&&""!==t.key){var a=this.el.querySelector('a[href="#'+t.key+'"]');if(null!==a)return void(a.textContent=t.charname+" ("+t.charclass+" "+t.level+")");var r=document.createElement("li"),n=document.createElement("a");n.textContent=t.charname+" ("+t.charclass+" "+t.level+")",n.setAttribute("href","#"+t.key),r.appendChild(n);var c=document.createElement("a");c.classList.add("delete"),c.innerHTML="❌",c.setAttribute("href","#"),c.setAttribute("data-key",t.key),r.appendChild(c),this.el.querySelector("#saved_characters").appendChild(r)}}catch(e){console.log(e.message)}},removeCharacter:function(e){var t=this.el.querySelector('a[href="#'+e+'"]'),a=t.parentNode;a.parentNode.removeChild(a)},isEmpty:function(){return null===this.el.querySelector("li")},initialize:function(){var e=this;document.body.addEventListener("click",function(t){var a=t.target.closest("#"+e.el.id);if(null===a){if(t.target.classList.contains("btn-load"))return;e.close()}else t.target.classList.contains("delete")&&(t.preventDefault(),console.log("delete!"),Manager.deletePrompt(t.target.getAttribute("data-key")),e.close())})}};LoadMenu.initialize();var attribute_fields=Array.from(document.querySelectorAll(".pc-attributes input[type=number]")),attribute_saves=Array.from(document.querySelectorAll(".pc-attributes input[type=checkbox]")),skill_checks=Array.from(document.querySelectorAll('input[data-name="skills"]')),dialog_unsaved=document.querySelector(".alert-unsaved"),calcAttrMod=function(e){var t=Math.floor((e-10)/2);return t>0?"+"+t:t},calcSaveMod=function(e){var t=0,a=document.querySelector("[data-name="+e+"]").parentNode,r=a.querySelector(".pc-attribute-mod").innerHTML,n=a.querySelector(".pc-save-mod"),c=a.querySelector("input[type=checkbox]").checked;c&&(t=document.querySelector('[data-name="proficiency"]').innerHTML,t=""===t?0:parseInt(t,10));var o=0+parseInt(t,10)+parseInt(r,10);n.innerHTML=o>0?"+"+o:o},calcSkillMod=function(e){var t=e.checked,a=e.getAttribute("data-attr"),r=e.parentNode.nextElementSibling,n=0;t&&(n=document.querySelector('[data-name="proficiency"]').innerHTML,n=""===n?0:parseInt(n,10));var c=document.querySelector("[data-name="+a+"]"),o=parseInt(calcAttrMod(c.value),10),i=0+n+o;r.innerText=i>0?"+"+i:i},calcProfMod=function(){var e=document.querySelector("[data-name=proficiency]"),t=e.innerHTML,a=parseInt(document.querySelector("[data-name=level]").innerHTML,10),r=Math.ceil(a/4)+1,n="+"+r;if(t!==n){e.innerHTML="+"+r;var c=Array.from(document.querySelectorAll(".pc-attributes input[type=number]"));c.forEach(function(e){calcSaveMod(e.getAttribute("data-name"))});var o=Array.from(document.querySelectorAll('input[data-name="skills"]'));o.forEach(function(e){calcSkillMod(e)})}};document.querySelector(".container").addEventListener("focus",function(e){"true"===e.target.getAttribute("contenteditable")&&e.target.setAttribute("data-before",e.target.innerHTML)},!0),document.querySelector(".container").addEventListener("blur",function(e){if("true"===e.target.getAttribute("contenteditable")){var t=e.target.getAttribute("data-before");t!==e.target.innerHTML&&(console.log("Changed"),e.target.removeAttribute("data-before"),dialog_unsaved.classList.add("open"),"level"===e.target.getAttribute("data-name")&&calcProfMod())}},!0),attribute_fields.forEach(function(e){var t=e.parentNode,a=t.querySelector(".pc-attribute-mod");e.addEventListener("change",function(t){a.innerText=calcAttrMod(t.currentTarget.value);var r=e.getAttribute("data-name");calcSaveMod(r);var n=Array.from(document.querySelectorAll("[data-attr="+r+"]"));n.forEach(function(e){calcSkillMod(e)}),dialog_unsaved.classList.add("open")})}),attribute_saves.forEach(function(e){e.addEventListener("change",function(e){var t=e.currentTarget.getAttribute("data-subfield");calcSaveMod(t),dialog_unsaved.classList.add("open")})}),skill_checks.forEach(function(e){e.addEventListener("change",function(e){console.log("check change"),calcSkillMod(e.currentTarget),dialog_unsaved.classList.add("open")})});var character_model={app:"character-sheet-5e",key:"",charname:"",charclass:"",race:"",background:"",alignment:"",level:1,experience:0,inspiration:"",proficiency:"+2",armor_class:10,speed:30,hp_cur:0,hp_max:0,hd_cur:1,hd_max:1,str:10,dex:10,con:10,intel:10,wis:10,cha:10,saves:{str:0,dex:0,con:0,intel:0,wis:0,cha:0},skills:{acrobatics:0,animal_handling:0,arcana:0,athletics:0,deception:0,history:0,insight:0,intimidation:0,investigation:0,medicine:0,nature:0,perception:0,performance:0,persuasion:0,religion:0,sleight_of_Hand:0,stealth:0,survival:0},weapons:"",proficiencies_other:"",languages:"",traits:"",ideals:"",bonds:"",flaws:"",equipment:"",cp:0,sp:0,gp:0,pp:0,features:"",notes:"",spell_ability:"",spell_save:"",spell_attack:"",spell_slots:{1:0,2:0,3:0,4:0,5:0,6:0,7:0,8:0,9:0},spells:{0:"",1:"",2:"",3:"",4:"",5:"",6:"",7:"",8:"",9:""},updated:""},Storage={get:function(e){var t=localStorage.getItem(e);return null!==t?t:""},set:function(e,t){try{localStorage.setItem(e,t)}catch(e){return!1}return!0},remove:function(e){localStorage.removeItem(e)},getAllKeys:function(){var e=[];if(localStorage.length>0)for(var t=0;t");var t=JSON.parse(e);if(!t.key||"character-sheet-5e"!==t.app)throw new Error("Data appears to be invalid.");var a=Storage.get(t.key);if(""!==a&&""!==a.charname&&a.charname!==t.charname)if(t.key_prev){var r=t.key_prev;t.key_prev=t.key,t.key=r}else t.key_prev=t.key,t.key=this.generateKey();Storage.set(t.key,JSON.stringify(t)),LoadMenu.addCharacter(t.key),t.key===this.cur_character.key&&this.loadCharacter(t.key)}catch(e){var n=document.createElement("p");n.innerHTML="Error processing backup data: "+e.message,Alert.setContent(n)}},deletePrompt:function(e){var t=Storage.get(e);if(""!==t){try{t=JSON.parse(t)}catch(e){return}var a=[],r=document.createElement("p");r.innerHTML="Are you sure you want to delete the character: "+(t.charname?t.charname:"[Unnamed]"),a.push(r);var n=document.createElement("button");n.innerHTML="Yes, Delete.",n.setAttribute("data-key",t.key),n.classList.add("delete-conf"),a.push(n),Alert.setContent(a)}},deleteCharacter:function(e){if(""!==e&&"settings"!==e)if(Storage.remove(e),""!==Storage.get(e)){var t=document.createElement("p");t.innerHTML="Error deleting the character...",Alert.setContent(t)}else LoadMenu.removeCharacter(e),null!==this.cur_character&&this.cur_character.key===e&&(window.location.hash="#"+Manager.generateKey())}};if(Storage.getAllKeys().forEach(function(e){LoadMenu.addCharacter(e)}),LoadMenu.isEmpty()){var content=[],h=document.createElement("h2");h.innerHTML="Character Sheet. 5e.",content.push(h);var p1=document.createElement("p");p1.innerHTML="An online character sheet for 5th edition D&D, usable offline (in some browsers).",content.push(p1);var p4=document.createElement("p");p4.innerHTML="Designed for modern browsers, if all else fails Chrome is your best bet and IE is your worst bet.",content.push(p4);var p2=document.createElement("p");p2.innerHTML="Warning: Character data is saved to your browser's local storage. This means it can be erased if you delete browser data and will not automatically transfer between browsers even on the same computer. Please Save and Backup often (or at least at the end of every gaming session)!",content.push(p2);var p3=document.createElement("p");p3.innerHTML="This message will only appear until you save your first character.",content.push(p3),Alert.setContent(content)}window.addEventListener("hashchange",function(e){Manager.changeCharacter()},!1);var urlhash=window.location.hash.substr(1);""!==urlhash?Manager.loadCharacter(urlhash):window.location.hash="#"+Manager.generateKey();var dialog_help=document.getElementById("dialog_help"),help_open=document.querySelector(".btn-help");help_open.addEventListener("click",function(e){dialog_help.classList.add("open"),dialog_help.querySelector("h1").focus()}),dialog_help.querySelector(".close").addEventListener("click",function(e){dialog_help.classList.remove("open")}),document.body.addEventListener("click",function(e){var t=e.target.closest("#dialog_help");if(null===t){if(e.target.classList.contains("btn-help"))return;dialog_help.classList.remove("open")}}); },{}]},{},[1])(1) }); diff --git a/src/main.js b/src/main.js index 23e6e70..4d93ea2 100644 --- a/src/main.js +++ b/src/main.js @@ -782,6 +782,12 @@ const Manager = exports.manager = { break; } }); + if (this.cur_character.charname === '') { + const p = document.createElement('p'); + p.innerHTML = 'Your character must have name to save!'; + Alert.setContent(p); + return; + } this.cur_character.updated = this.currentTimestamp(); Storage.set(this.cur_character.key, JSON.stringify(this.cur_character)); dialog_unsaved.classList.remove('open');