ibom.html 258 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <title>Interactive BOM for KiCAD</title>
  7. <style type="text/css">
  8. :root {
  9. --pcb-edge-color: black;
  10. --pad-color: #878787;
  11. --pad-hole-color: #CCCCCC;
  12. --pad-color-highlight: #D04040;
  13. --pad-color-highlight-both: #D0D040;
  14. --pad-color-highlight-marked: #44a344;
  15. --pin1-outline-color: #ffb629;
  16. --pin1-outline-color-highlight: #ffb629;
  17. --pin1-outline-color-highlight-both: #fcbb39;
  18. --pin1-outline-color-highlight-marked: #fdbe41;
  19. --silkscreen-edge-color: #aa4;
  20. --silkscreen-polygon-color: #4aa;
  21. --silkscreen-text-color: #4aa;
  22. --fabrication-edge-color: #907651;
  23. --fabrication-polygon-color: #907651;
  24. --fabrication-text-color: #a27c24;
  25. --track-color: #def5f1;
  26. --track-color-highlight: #D04040;
  27. --zone-color: #def5f1;
  28. --zone-color-highlight: #d0404080;
  29. }
  30. html,
  31. body {
  32. margin: 0px;
  33. height: 100%;
  34. font-family: Verdana, sans-serif;
  35. }
  36. .dark.topmostdiv {
  37. --pcb-edge-color: #eee;
  38. --pad-color: #808080;
  39. --pin1-outline-color: #ffa800;
  40. --pin1-outline-color-highlight: #ccff00;
  41. --track-color: #42524f;
  42. --zone-color: #42524f;
  43. background-color: #252c30;
  44. color: #eee;
  45. }
  46. button {
  47. background-color: #eee;
  48. border: 1px solid #888;
  49. color: black;
  50. height: 44px;
  51. width: 44px;
  52. text-align: center;
  53. text-decoration: none;
  54. display: inline-block;
  55. font-size: 14px;
  56. font-weight: bolder;
  57. }
  58. .dark button {
  59. /* This will be inverted */
  60. background-color: #c3b7b5;
  61. }
  62. button.depressed {
  63. background-color: #0a0;
  64. color: white;
  65. }
  66. .dark button.depressed {
  67. /* This will be inverted */
  68. background-color: #b3b;
  69. }
  70. button:focus {
  71. outline: 0;
  72. }
  73. button#tb-btn {
  74. background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8.47 8.47'%3E%3Crect transform='translate(0 -288.53)' ry='1.17' y='288.8' x='.27' height='7.94' width='7.94' fill='%23f9f9f9'/%3E%3Cg transform='translate(0 -288.53)'%3E%3Crect width='7.94' height='7.94' x='.27' y='288.8' ry='1.17' fill='none' stroke='%23000' stroke-width='.4' stroke-linejoin='round'/%3E%3Cpath d='M1.32 290.12h5.82M1.32 291.45h5.82' fill='none' stroke='%23000' stroke-width='.4'/%3E%3Cpath d='M4.37 292.5v4.23M.26 292.63H8.2' fill='none' stroke='%23000' stroke-width='.3'/%3E%3Ctext font-weight='700' font-size='3.17' font-family='sans-serif'%3E%3Ctspan x='1.35' y='295.73'%3EF%3C/tspan%3E%3Ctspan x='5.03' y='295.68'%3EB%3C/tspan%3E%3C/text%3E%3C/g%3E%3C/svg%3E%0A");
  75. }
  76. button#lr-btn {
  77. background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8.47 8.47'%3E%3Crect transform='translate(0 -288.53)' ry='1.17' y='288.8' x='.27' height='7.94' width='7.94' fill='%23f9f9f9'/%3E%3Cg transform='translate(0 -288.53)'%3E%3Crect width='7.94' height='7.94' x='.27' y='288.8' ry='1.17' fill='none' stroke='%23000' stroke-width='.4' stroke-linejoin='round'/%3E%3Cpath d='M1.06 290.12H3.7m-2.64 1.33H3.7m-2.64 1.32H3.7m-2.64 1.3H3.7m-2.64 1.33H3.7' fill='none' stroke='%23000' stroke-width='.4'/%3E%3Cpath d='M4.37 288.8v7.94m0-4.11h3.96' fill='none' stroke='%23000' stroke-width='.3'/%3E%3Ctext font-weight='700' font-size='3.17' font-family='sans-serif'%3E%3Ctspan x='5.11' y='291.96'%3EF%3C/tspan%3E%3Ctspan x='5.03' y='295.68'%3EB%3C/tspan%3E%3C/text%3E%3C/g%3E%3C/svg%3E%0A");
  78. }
  79. button#bom-btn {
  80. background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8.47 8.47'%3E%3Crect transform='translate(0 -288.53)' ry='1.17' y='288.8' x='.27' height='7.94' width='7.94' fill='%23f9f9f9'/%3E%3Cg transform='translate(0 -288.53)' fill='none' stroke='%23000' stroke-width='.4'%3E%3Crect width='7.94' height='7.94' x='.27' y='288.8' ry='1.17' stroke-linejoin='round'/%3E%3Cpath d='M1.59 290.12h5.29M1.59 291.45h5.33M1.59 292.75h5.33M1.59 294.09h5.33M1.59 295.41h5.33'/%3E%3C/g%3E%3C/svg%3E");
  81. }
  82. button#bom-grouped-btn {
  83. background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32'%3E%3Cg stroke='%23000' stroke-linejoin='round' class='layer'%3E%3Crect width='29' height='29' x='1.5' y='1.5' stroke-width='2' fill='%23fff' rx='5' ry='5'/%3E%3Cpath stroke-linecap='square' stroke-width='2' d='M6 10h4m4 0h5m4 0h3M6.1 22h3m3.9 0h5m4 0h4m-16-8h4m4 0h4'/%3E%3Cpath stroke-linecap='null' d='M5 17.5h22M5 26.6h22M5 5.5h22'/%3E%3C/g%3E%3C/svg%3E");
  84. }
  85. button#bom-ungrouped-btn {
  86. background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32'%3E%3Cg stroke='%23000' stroke-linejoin='round' class='layer'%3E%3Crect width='29' height='29' x='1.5' y='1.5' stroke-width='2' fill='%23fff' rx='5' ry='5'/%3E%3Cpath stroke-linecap='square' stroke-width='2' d='M6 10h4m-4 8h3m-3 8h4'/%3E%3Cpath stroke-linecap='null' d='M5 13.5h22m-22 8h22M5 5.5h22'/%3E%3C/g%3E%3C/svg%3E");
  87. }
  88. button#bom-netlist-btn {
  89. background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32'%3E%3Cg fill='none' stroke='%23000' class='layer'%3E%3Crect width='29' height='29' x='1.5' y='1.5' stroke-width='2' fill='%23fff' rx='5' ry='5'/%3E%3Cpath stroke-width='2' d='M6 26l6-6v-8m13.8-6.3l-6 6v8'/%3E%3Ccircle cx='11.8' cy='9.5' r='2.8' stroke-width='2'/%3E%3Ccircle cx='19.8' cy='22.8' r='2.8' stroke-width='2'/%3E%3C/g%3E%3C/svg%3E");
  90. }
  91. button#copy {
  92. background-image: url("data:image/svg+xml,%3Csvg height='48' viewBox='0 0 48 48' width='48' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0 0h48v48h-48z' fill='none'/%3E%3Cpath d='M32 2h-24c-2.21 0-4 1.79-4 4v28h4v-28h24v-4zm6 8h-22c-2.21 0-4 1.79-4 4v28c0 2.21 1.79 4 4 4h22c2.21 0 4-1.79 4-4v-28c0-2.21-1.79-4-4-4zm0 32h-22v-28h22v28z'/%3E%3C/svg%3E");
  93. background-position: 6px 6px;
  94. background-repeat: no-repeat;
  95. background-size: 26px 26px;
  96. border-radius: 6px;
  97. height: 40px;
  98. width: 40px;
  99. margin: 10px 5px;
  100. }
  101. button#copy:active {
  102. box-shadow: inset 0px 0px 5px #6c6c6c;
  103. }
  104. textarea.clipboard-temp {
  105. position: fixed;
  106. top: 0;
  107. left: 0;
  108. width: 2em;
  109. height: 2em;
  110. padding: 0;
  111. border: None;
  112. outline: None;
  113. box-shadow: None;
  114. background: transparent;
  115. }
  116. .left-most-button {
  117. border-right: 0;
  118. border-top-left-radius: 6px;
  119. border-bottom-left-radius: 6px;
  120. }
  121. .middle-button {
  122. border-right: 0;
  123. }
  124. .right-most-button {
  125. border-top-right-radius: 6px;
  126. border-bottom-right-radius: 6px;
  127. }
  128. .button-container {
  129. font-size: 0;
  130. margin: 10px 10px 10px 0px;
  131. }
  132. .dark .button-container {
  133. filter: invert(1);
  134. }
  135. .button-container button {
  136. background-size: 32px 32px;
  137. background-position: 5px 5px;
  138. background-repeat: no-repeat;
  139. }
  140. @media print {
  141. .hideonprint {
  142. display: none;
  143. }
  144. }
  145. canvas {
  146. cursor: crosshair;
  147. }
  148. canvas:active {
  149. cursor: grabbing;
  150. }
  151. .fileinfo {
  152. width: 100%;
  153. max-width: 1000px;
  154. border: none;
  155. padding: 5px;
  156. }
  157. .fileinfo .title {
  158. font-size: 20pt;
  159. font-weight: bold;
  160. }
  161. .fileinfo td {
  162. overflow: hidden;
  163. white-space: nowrap;
  164. max-width: 1px;
  165. width: 50%;
  166. text-overflow: ellipsis;
  167. }
  168. .bom {
  169. border-collapse: collapse;
  170. font-family: Consolas, "DejaVu Sans Mono", Monaco, monospace;
  171. font-size: 10pt;
  172. table-layout: fixed;
  173. width: 100%;
  174. margin-top: 1px;
  175. position: relative;
  176. }
  177. .bom th,
  178. .bom td {
  179. border: 1px solid black;
  180. padding: 5px;
  181. word-wrap: break-word;
  182. text-align: center;
  183. position: relative;
  184. }
  185. .dark .bom th,
  186. .dark .bom td {
  187. border: 1px solid #777;
  188. }
  189. .bom th {
  190. background-color: #CCCCCC;
  191. background-clip: padding-box;
  192. }
  193. .dark .bom th {
  194. background-color: #3b4749;
  195. }
  196. .bom tr.highlighted:nth-child(n) {
  197. background-color: #cfc;
  198. }
  199. .dark .bom tr.highlighted:nth-child(n) {
  200. background-color: #226022;
  201. }
  202. .bom tr:nth-child(even) {
  203. background-color: #f2f2f2;
  204. }
  205. .dark .bom tr:nth-child(even) {
  206. background-color: #313b40;
  207. }
  208. .bom tr.checked {
  209. color: #1cb53d;
  210. }
  211. .dark .bom tr.checked {
  212. color: #2cce54;
  213. }
  214. .bom tr {
  215. transition: background-color 0.2s;
  216. }
  217. .bom .numCol {
  218. width: 30px;
  219. }
  220. .bom .value {
  221. width: 15%;
  222. }
  223. .bom .quantity {
  224. width: 65px;
  225. }
  226. .bom th .sortmark {
  227. position: absolute;
  228. right: 1px;
  229. top: 1px;
  230. margin-top: -5px;
  231. border-width: 5px;
  232. border-style: solid;
  233. border-color: transparent transparent #221 transparent;
  234. transform-origin: 50% 85%;
  235. transition: opacity 0.2s, transform 0.4s;
  236. }
  237. .dark .bom th .sortmark {
  238. filter: invert(1);
  239. }
  240. .bom th .sortmark.none {
  241. opacity: 0;
  242. }
  243. .bom th .sortmark.desc {
  244. transform: rotate(180deg);
  245. }
  246. .bom th:hover .sortmark.none {
  247. opacity: 0.5;
  248. }
  249. .bom .bom-checkbox {
  250. width: 30px;
  251. position: relative;
  252. user-select: none;
  253. -moz-user-select: none;
  254. }
  255. .bom .bom-checkbox:before {
  256. content: "";
  257. position: absolute;
  258. border-width: 15px;
  259. border-style: solid;
  260. border-color: #51829f transparent transparent transparent;
  261. visibility: hidden;
  262. top: -15px;
  263. }
  264. .bom .bom-checkbox:after {
  265. content: "Double click to set/unset all";
  266. position: absolute;
  267. color: white;
  268. top: -35px;
  269. left: -26px;
  270. background: #51829f;
  271. padding: 5px 15px;
  272. border-radius: 8px;
  273. white-space: nowrap;
  274. visibility: hidden;
  275. }
  276. .bom .bom-checkbox:hover:before,
  277. .bom .bom-checkbox:hover:after {
  278. visibility: visible;
  279. transition: visibility 0.2s linear 1s;
  280. }
  281. .split {
  282. -webkit-box-sizing: border-box;
  283. -moz-box-sizing: border-box;
  284. box-sizing: border-box;
  285. overflow-y: auto;
  286. overflow-x: hidden;
  287. background-color: inherit;
  288. }
  289. .split.split-horizontal,
  290. .gutter.gutter-horizontal {
  291. height: 100%;
  292. float: left;
  293. }
  294. .gutter {
  295. background-color: #ddd;
  296. background-repeat: no-repeat;
  297. background-position: 50%;
  298. transition: background-color 0.3s;
  299. }
  300. .dark .gutter {
  301. background-color: #777;
  302. }
  303. .gutter.gutter-horizontal {
  304. background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAeCAYAAADkftS9AAAAIklEQVQoU2M4c+bMfxAGAgYYmwGrIIiDjrELjpo5aiZeMwF+yNnOs5KSvgAAAABJRU5ErkJggg==');
  305. cursor: ew-resize;
  306. width: 5px;
  307. }
  308. .gutter.gutter-vertical {
  309. background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAFAQMAAABo7865AAAABlBMVEVHcEzMzMzyAv2sAAAAAXRSTlMAQObYZgAAABBJREFUeF5jOAMEEAIEEFwAn3kMwcB6I2AAAAAASUVORK5CYII=');
  310. cursor: ns-resize;
  311. height: 5px;
  312. }
  313. .searchbox {
  314. float: left;
  315. height: 40px;
  316. margin: 10px 5px;
  317. padding: 12px 32px;
  318. font-family: Consolas, "DejaVu Sans Mono", Monaco, monospace;
  319. font-size: 18px;
  320. box-sizing: border-box;
  321. border: 1px solid #888;
  322. border-radius: 6px;
  323. outline: none;
  324. background-color: #eee;
  325. transition: background-color 0.2s, border 0.2s;
  326. background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABNklEQVQ4T8XSMUvDQBQH8P/LElFa/AIZHcTBQSz0I/gFstTBRR2KUC4ldDxw7h0Bl3RRUATxi4iiODgoiLNrbQYp5J6cpJJqomkX33Z37/14d/dIa33MzDuYI4johOI4XhyNRteO46zNYjDzAxE1yBZprVeZ+QbAUhXEGJMA2Ox2u4+fQIa0mPmsCgCgJYQ4t7lfgF0opQYAdv9ABkKI/UnOFCClXKjX61cA1osQY8x9kiRNKeV7IWA3oyhaSdP0FkAtjxhj3hzH2RBCPOf3pzqYHCilfAAX+URm9oMguPzeWSGQvUcMYC8rOBJCHBRdqxTo9/vbRHRqi8bj8XKv1xvODbiuW2u32/bvf0SlDv4XYOY7z/Mavu+nM1+BmQ+NMc0wDF/LprP0DbTWW0T00ul0nn4b7Q87+X4Qmfiq2wAAAABJRU5ErkJggg==');
  327. background-position: 10px 10px;
  328. background-repeat: no-repeat;
  329. }
  330. .dark .searchbox {
  331. background-color: #111;
  332. color: #eee;
  333. }
  334. .searchbox::placeholder {
  335. color: #ccc;
  336. }
  337. .dark .searchbox::placeholder {
  338. color: #666;
  339. }
  340. .filter {
  341. width: calc(60% - 64px);
  342. }
  343. .reflookup {
  344. width: calc(40% - 10px);
  345. }
  346. input[type=text]:focus {
  347. background-color: white;
  348. border: 1px solid #333;
  349. }
  350. .dark input[type=text]:focus {
  351. background-color: #333;
  352. border: 1px solid #ccc;
  353. }
  354. mark.highlight {
  355. background-color: #5050ff;
  356. color: #fff;
  357. padding: 2px;
  358. border-radius: 6px;
  359. }
  360. .dark mark.highlight {
  361. background-color: #76a6da;
  362. color: #111;
  363. }
  364. .menubtn {
  365. background-color: white;
  366. border: none;
  367. background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='36' height='36' viewBox='0 0 20 20'%3E%3Cpath fill='none' d='M0 0h20v20H0V0z'/%3E%3Cpath d='M15.95 10.78c.03-.25.05-.51.05-.78s-.02-.53-.06-.78l1.69-1.32c.15-.12.19-.34.1-.51l-1.6-2.77c-.1-.18-.31-.24-.49-.18l-1.99.8c-.42-.32-.86-.58-1.35-.78L12 2.34c-.03-.2-.2-.34-.4-.34H8.4c-.2 0-.36.14-.39.34l-.3 2.12c-.49.2-.94.47-1.35.78l-1.99-.8c-.18-.07-.39 0-.49.18l-1.6 2.77c-.1.18-.06.39.1.51l1.69 1.32c-.04.25-.07.52-.07.78s.02.53.06.78L2.37 12.1c-.15.12-.19.34-.1.51l1.6 2.77c.1.18.31.24.49.18l1.99-.8c.42.32.86.58 1.35.78l.3 2.12c.04.2.2.34.4.34h3.2c.2 0 .37-.14.39-.34l.3-2.12c.49-.2.94-.47 1.35-.78l1.99.8c.18.07.39 0 .49-.18l1.6-2.77c.1-.18.06-.39-.1-.51l-1.67-1.32zM10 13c-1.65 0-3-1.35-3-3s1.35-3 3-3 3 1.35 3 3-1.35 3-3 3z'/%3E%3C/svg%3E%0A");
  368. background-position: center;
  369. background-repeat: no-repeat;
  370. }
  371. .statsbtn {
  372. background-color: white;
  373. border: none;
  374. background-image: url("data:image/svg+xml,%3Csvg width='36' height='36' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M4 6h28v24H4V6zm0 8h28v8H4m9-16v24h10V5.8' fill='none' stroke='%23000' stroke-width='2'/%3E%3C/svg%3E");
  375. background-position: center;
  376. background-repeat: no-repeat;
  377. }
  378. .iobtn {
  379. background-color: white;
  380. border: none;
  381. background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='36' height='36'%3E%3Cpath fill='none' stroke='%23000' stroke-width='2' d='M3 33v-7l6.8-7h16.5l6.7 7v7H3zM3.2 26H33M21 9l5-5.9 5 6h-2.5V15h-5V9H21zm-4.9 0l-5 6-5-6h2.5V3h5v6h2.5z'/%3E%3Cpath fill='none' stroke='%23000' d='M6.1 29.5H10'/%3E%3C/svg%3E");
  382. background-position: center;
  383. background-repeat: no-repeat;
  384. }
  385. .visbtn {
  386. background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24'%3E%3Cpath fill='none' stroke='%23333' d='M2.5 4.5h5v15h-5zM9.5 4.5h5v15h-5zM16.5 4.5h5v15h-5z'/%3E%3C/svg%3E");
  387. background-position: center;
  388. background-repeat: no-repeat;
  389. padding: 15px;
  390. }
  391. #vismenu-content {
  392. left: 0px;
  393. font-family: Verdana, sans-serif;
  394. }
  395. .dark .statsbtn,
  396. .dark .savebtn,
  397. .dark .menubtn,
  398. .dark .iobtn,
  399. .dark .visbtn {
  400. filter: invert(1);
  401. }
  402. .flexbox {
  403. display: flex;
  404. align-items: center;
  405. justify-content: space-between;
  406. width: 100%;
  407. }
  408. .savebtn {
  409. background-color: #d6d6d6;
  410. width: auto;
  411. height: 30px;
  412. flex-grow: 1;
  413. margin: 5px;
  414. border-radius: 4px;
  415. }
  416. .savebtn:active {
  417. background-color: #0a0;
  418. color: white;
  419. }
  420. .dark .savebtn:active {
  421. /* This will be inverted */
  422. background-color: #b3b;
  423. }
  424. .stats {
  425. border-collapse: collapse;
  426. font-size: 12pt;
  427. table-layout: fixed;
  428. width: 100%;
  429. min-width: 450px;
  430. }
  431. .dark .stats td {
  432. border: 1px solid #bbb;
  433. }
  434. .stats td {
  435. border: 1px solid black;
  436. padding: 5px;
  437. word-wrap: break-word;
  438. text-align: center;
  439. position: relative;
  440. }
  441. #checkbox-stats div {
  442. position: absolute;
  443. left: 0;
  444. top: 0;
  445. height: 100%;
  446. width: 100%;
  447. display: flex;
  448. align-items: center;
  449. justify-content: center;
  450. }
  451. #checkbox-stats .bar {
  452. background-color: rgba(28, 251, 0, 0.6);
  453. }
  454. .menu {
  455. position: relative;
  456. display: inline-block;
  457. margin: 10px 10px 10px 0px;
  458. }
  459. .menu-content {
  460. font-size: 12pt !important;
  461. text-align: left !important;
  462. font-weight: normal !important;
  463. display: none;
  464. position: absolute;
  465. background-color: white;
  466. right: 0;
  467. min-width: 300px;
  468. box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
  469. z-index: 100;
  470. padding: 8px;
  471. }
  472. .dark .menu-content {
  473. background-color: #111;
  474. }
  475. .menu:hover .menu-content {
  476. display: block;
  477. }
  478. .menu:hover .menubtn,
  479. .menu:hover .iobtn,
  480. .menu:hover .statsbtn {
  481. background-color: #eee;
  482. }
  483. .menu-label {
  484. display: inline-block;
  485. padding: 8px;
  486. border: 1px solid #ccc;
  487. border-top: 0;
  488. width: calc(100% - 18px);
  489. }
  490. .menu-label-top {
  491. border-top: 1px solid #ccc;
  492. }
  493. .menu-textbox {
  494. float: left;
  495. height: 24px;
  496. margin: 10px 5px;
  497. padding: 5px 5px;
  498. font-family: Consolas, "DejaVu Sans Mono", Monaco, monospace;
  499. font-size: 14px;
  500. box-sizing: border-box;
  501. border: 1px solid #888;
  502. border-radius: 4px;
  503. outline: none;
  504. background-color: #eee;
  505. transition: background-color 0.2s, border 0.2s;
  506. width: calc(100% - 10px);
  507. }
  508. .menu-textbox.invalid,
  509. .dark .menu-textbox.invalid {
  510. color: red;
  511. }
  512. .dark .menu-textbox {
  513. background-color: #222;
  514. color: #eee;
  515. }
  516. .radio-container {
  517. margin: 4px;
  518. }
  519. .topmostdiv {
  520. width: 100%;
  521. height: 100%;
  522. background-color: white;
  523. transition: background-color 0.3s;
  524. }
  525. #top {
  526. height: 78px;
  527. border-bottom: 2px solid black;
  528. }
  529. .dark #top {
  530. border-bottom: 2px solid #ccc;
  531. }
  532. #dbg {
  533. display: block;
  534. }
  535. ::-webkit-scrollbar {
  536. width: 8px;
  537. }
  538. ::-webkit-scrollbar-track {
  539. background: #aaa;
  540. }
  541. ::-webkit-scrollbar-thumb {
  542. background: #666;
  543. border-radius: 3px;
  544. }
  545. ::-webkit-scrollbar-thumb:hover {
  546. background: #555;
  547. }
  548. .slider {
  549. -webkit-appearance: none;
  550. width: 100%;
  551. margin: 3px 0;
  552. padding: 0;
  553. outline: none;
  554. opacity: 0.7;
  555. -webkit-transition: .2s;
  556. transition: opacity .2s;
  557. border-radius: 3px;
  558. }
  559. .slider:hover {
  560. opacity: 1;
  561. }
  562. .slider:focus {
  563. outline: none;
  564. }
  565. .slider::-webkit-slider-runnable-track {
  566. -webkit-appearance: none;
  567. width: 100%;
  568. height: 8px;
  569. background: #d3d3d3;
  570. border-radius: 3px;
  571. border: none;
  572. }
  573. .slider::-webkit-slider-thumb {
  574. -webkit-appearance: none;
  575. width: 15px;
  576. height: 15px;
  577. border-radius: 50%;
  578. background: #0a0;
  579. cursor: pointer;
  580. margin-top: -4px;
  581. }
  582. .dark .slider::-webkit-slider-thumb {
  583. background: #3d3;
  584. }
  585. .slider::-moz-range-thumb {
  586. width: 15px;
  587. height: 15px;
  588. border-radius: 50%;
  589. background: #0a0;
  590. cursor: pointer;
  591. }
  592. .slider::-moz-range-track {
  593. height: 8px;
  594. background: #d3d3d3;
  595. border-radius: 3px;
  596. }
  597. .dark .slider::-moz-range-thumb {
  598. background: #3d3;
  599. }
  600. .slider::-ms-track {
  601. width: 100%;
  602. height: 8px;
  603. border-width: 3px 0;
  604. background: transparent;
  605. border-color: transparent;
  606. color: transparent;
  607. transition: opacity .2s;
  608. }
  609. .slider::-ms-fill-lower {
  610. background: #d3d3d3;
  611. border: none;
  612. border-radius: 3px;
  613. }
  614. .slider::-ms-fill-upper {
  615. background: #d3d3d3;
  616. border: none;
  617. border-radius: 3px;
  618. }
  619. .slider::-ms-thumb {
  620. width: 15px;
  621. height: 15px;
  622. border-radius: 50%;
  623. background: #0a0;
  624. cursor: pointer;
  625. margin: 0;
  626. }
  627. .shameless-plug {
  628. font-size: 0.8em;
  629. text-align: center;
  630. display: block;
  631. }
  632. a {
  633. color: #0278a4;
  634. }
  635. .dark a {
  636. color: #00b9fd;
  637. }
  638. #frontcanvas,
  639. #backcanvas {
  640. touch-action: none;
  641. }
  642. .placeholder {
  643. border: 1px dashed #9f9fda !important;
  644. background-color: #edf2f7 !important;
  645. }
  646. .dragging {
  647. z-index: 999;
  648. }
  649. .dark .dragging>table>tbody>tr {
  650. background-color: #252c30;
  651. }
  652. .dark .placeholder {
  653. filter: invert(1);
  654. }
  655. .column-spacer {
  656. top: 0;
  657. left: 0;
  658. width: calc(100% - 4px);
  659. position: absolute;
  660. cursor: pointer;
  661. user-select: none;
  662. height: 100%;
  663. }
  664. .column-width-handle {
  665. top: 0;
  666. right: 0;
  667. width: 4px;
  668. position: absolute;
  669. cursor: col-resize;
  670. user-select: none;
  671. height: 100%;
  672. }
  673. .column-width-handle:hover {
  674. background-color: #4f99bd;
  675. }
  676. .help-link {
  677. border: 1px solid #0278a4;
  678. padding-inline: 0.3rem;
  679. border-radius: 3px;
  680. cursor: pointer;
  681. }
  682. .dark .help-link {
  683. border: 1px solid #00b9fd;
  684. }
  685. </style>
  686. <script type="text/javascript" >
  687. ///////////////////////////////////////////////
  688. /*
  689. Split.js - v1.3.5
  690. MIT License
  691. https://github.com/nathancahill/Split.js
  692. */
  693. !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.Split=t()}(this,function(){"use strict";var e=window,t=e.document,n="addEventListener",i="removeEventListener",r="getBoundingClientRect",s=function(){return!1},o=e.attachEvent&&!e[n],a=["","-webkit-","-moz-","-o-"].filter(function(e){var n=t.createElement("div");return n.style.cssText="width:"+e+"calc(9px)",!!n.style.length}).shift()+"calc",l=function(e){return"string"==typeof e||e instanceof String?t.querySelector(e):e};return function(u,c){function z(e,t,n){var i=A(y,t,n);Object.keys(i).forEach(function(t){return e.style[t]=i[t]})}function h(e,t){var n=B(y,t);Object.keys(n).forEach(function(t){return e.style[t]=n[t]})}function f(e){var t=E[this.a],n=E[this.b],i=t.size+n.size;t.size=e/this.size*i,n.size=i-e/this.size*i,z(t.element,t.size,this.aGutterSize),z(n.element,n.size,this.bGutterSize)}function m(e){var t;this.dragging&&((t="touches"in e?e.touches[0][b]-this.start:e[b]-this.start)<=E[this.a].minSize+M+this.aGutterSize?t=E[this.a].minSize+this.aGutterSize:t>=this.size-(E[this.b].minSize+M+this.bGutterSize)&&(t=this.size-(E[this.b].minSize+this.bGutterSize)),f.call(this,t),c.onDrag&&c.onDrag())}function g(){var e=E[this.a].element,t=E[this.b].element;this.size=e[r]()[y]+t[r]()[y]+this.aGutterSize+this.bGutterSize,this.start=e[r]()[G]}function d(){var t=this,n=E[t.a].element,r=E[t.b].element;t.dragging&&c.onDragEnd&&c.onDragEnd(),t.dragging=!1,e[i]("mouseup",t.stop),e[i]("touchend",t.stop),e[i]("touchcancel",t.stop),t.parent[i]("mousemove",t.move),t.parent[i]("touchmove",t.move),delete t.stop,delete t.move,n[i]("selectstart",s),n[i]("dragstart",s),r[i]("selectstart",s),r[i]("dragstart",s),n.style.userSelect="",n.style.webkitUserSelect="",n.style.MozUserSelect="",n.style.pointerEvents="",r.style.userSelect="",r.style.webkitUserSelect="",r.style.MozUserSelect="",r.style.pointerEvents="",t.gutter.style.cursor="",t.parent.style.cursor=""}function S(t){var i=this,r=E[i.a].element,o=E[i.b].element;!i.dragging&&c.onDragStart&&c.onDragStart(),t.preventDefault(),i.dragging=!0,i.move=m.bind(i),i.stop=d.bind(i),e[n]("mouseup",i.stop),e[n]("touchend",i.stop),e[n]("touchcancel",i.stop),i.parent[n]("mousemove",i.move),i.parent[n]("touchmove",i.move),r[n]("selectstart",s),r[n]("dragstart",s),o[n]("selectstart",s),o[n]("dragstart",s),r.style.userSelect="none",r.style.webkitUserSelect="none",r.style.MozUserSelect="none",r.style.pointerEvents="none",o.style.userSelect="none",o.style.webkitUserSelect="none",o.style.MozUserSelect="none",o.style.pointerEvents="none",i.gutter.style.cursor=j,i.parent.style.cursor=j,g.call(i)}function v(e){e.forEach(function(t,n){if(n>0){var i=F[n-1],r=E[i.a],s=E[i.b];r.size=e[n-1],s.size=t,z(r.element,r.size,i.aGutterSize),z(s.element,s.size,i.bGutterSize)}})}function p(){F.forEach(function(e){e.parent.removeChild(e.gutter),E[e.a].element.style[y]="",E[e.b].element.style[y]=""})}void 0===c&&(c={});var y,b,G,E,w=l(u[0]).parentNode,D=e.getComputedStyle(w).flexDirection,U=c.sizes||u.map(function(){return 100/u.length}),k=void 0!==c.minSize?c.minSize:100,x=Array.isArray(k)?k:u.map(function(){return k}),L=void 0!==c.gutterSize?c.gutterSize:10,M=void 0!==c.snapOffset?c.snapOffset:30,O=c.direction||"horizontal",j=c.cursor||("horizontal"===O?"ew-resize":"ns-resize"),C=c.gutter||function(e,n){var i=t.createElement("div");return i.className="gutter gutter-"+n,i},A=c.elementStyle||function(e,t,n){var i={};return"string"==typeof t||t instanceof String?i[e]=t:i[e]=o?t+"%":a+"("+t+"% - "+n+"px)",i},B=c.gutterStyle||function(e,t){return n={},n[e]=t+"px",n;var n};"horizontal"===O?(y="width","clientWidth",b="clientX",G="left","paddingLeft"):"vertical"===O&&(y="height","clientHeight",b="clientY",G="top","paddingTop");var F=[];return E=u.map(function(e,t){var i,s={element:l(e),size:U[t],minSize:x[t]};if(t>0&&(i={a:t-1,b:t,dragging:!1,isFirst:1===t,isLast:t===u.length-1,direction:O,parent:w},i.aGutterSize=L,i.bGutterSize=L,i.isFirst&&(i.aGutterSize=L/2),i.isLast&&(i.bGutterSize=L/2),"row-reverse"===D||"column-reverse"===D)){var a=i.a;i.a=i.b,i.b=a}if(!o&&t>0){var c=C(t,O);h(c,L),c[n]("mousedown",S.bind(i)),c[n]("touchstart",S.bind(i)),w.insertBefore(c,s.element),i.gutter=c}0===t||t===u.length-1?z(s.element,s.size,L/2):z(s.element,s.size,L);var f=s.element[r]()[y];return f<s.minSize&&(s.minSize=f),t>0&&F.push(i),s}),o?{setSizes:v,destroy:p}:{setSizes:v,getSizes:function(){return E.map(function(e){return e.size})},collapse:function(e){if(e===F.length){var t=F[e-1];g.call(t),o||f.call(t,t.size-t.bGutterSize)}else{var n=F[e];g.call(n),o||f.call(n,n.aGutterSize)}},destroy:p}}});
  694. ///////////////////////////////////////////////
  695. ///////////////////////////////////////////////
  696. // Copyright (c) 2013 Pieroxy <pieroxy@pieroxy.net>
  697. // This work is free. You can redistribute it and/or modify it
  698. // under the terms of the WTFPL, Version 2
  699. // For more information see LICENSE.txt or http://www.wtfpl.net/
  700. //
  701. // For more information, the home page:
  702. // http://pieroxy.net/blog/pages/lz-string/testing.html
  703. //
  704. // LZ-based compression algorithm, version 1.4.4
  705. var LZString=function(){var o=String.fromCharCode,i={};var n={decompressFromBase64:function(o){return null==o?"":""==o?null:n._decompress(o.length,32,function(n){return function(o,n){if(!i[o]){i[o]={};for(var t=0;t<o.length;t++)i[o][o.charAt(t)]=t}return i[o][n]}("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",o.charAt(n))})},_decompress:function(i,n,t){var r,e,a,s,p,u,l,f=[],c=4,d=4,h=3,v="",g=[],m={val:t(0),position:n,index:1};for(r=0;r<3;r+=1)f[r]=r;for(a=0,p=Math.pow(2,2),u=1;u!=p;)s=m.val&m.position,m.position>>=1,0==m.position&&(m.position=n,m.val=t(m.index++)),a|=(s>0?1:0)*u,u<<=1;switch(a){case 0:for(a=0,p=Math.pow(2,8),u=1;u!=p;)s=m.val&m.position,m.position>>=1,0==m.position&&(m.position=n,m.val=t(m.index++)),a|=(s>0?1:0)*u,u<<=1;l=o(a);break;case 1:for(a=0,p=Math.pow(2,16),u=1;u!=p;)s=m.val&m.position,m.position>>=1,0==m.position&&(m.position=n,m.val=t(m.index++)),a|=(s>0?1:0)*u,u<<=1;l=o(a);break;case 2:return""}for(f[3]=l,e=l,g.push(l);;){if(m.index>i)return"";for(a=0,p=Math.pow(2,h),u=1;u!=p;)s=m.val&m.position,m.position>>=1,0==m.position&&(m.position=n,m.val=t(m.index++)),a|=(s>0?1:0)*u,u<<=1;switch(l=a){case 0:for(a=0,p=Math.pow(2,8),u=1;u!=p;)s=m.val&m.position,m.position>>=1,0==m.position&&(m.position=n,m.val=t(m.index++)),a|=(s>0?1:0)*u,u<<=1;f[d++]=o(a),l=d-1,c--;break;case 1:for(a=0,p=Math.pow(2,16),u=1;u!=p;)s=m.val&m.position,m.position>>=1,0==m.position&&(m.position=n,m.val=t(m.index++)),a|=(s>0?1:0)*u,u<<=1;f[d++]=o(a),l=d-1,c--;break;case 2:return g.join("")}if(0==c&&(c=Math.pow(2,h),h++),f[l])v=f[l];else{if(l!==d)return null;v=e+e.charAt(0)}g.push(v),f[d++]=e+v.charAt(0),e=v,0==--c&&(c=Math.pow(2,h),h++)}}};return n}();"function"==typeof define&&define.amd?define(function(){return LZString}):"undefined"!=typeof module&&null!=module?module.exports=LZString:"undefined"!=typeof angular&&null!=angular&&angular.module("LZString",[]).factory("LZString",function(){return LZString});
  706. ///////////////////////////////////////////////
  707. ///////////////////////////////////////////////
  708. /*!
  709. * PEP v0.4.3 | https://github.com/jquery/PEP
  710. * Copyright jQuery Foundation and other contributors | http://jquery.org/license
  711. */
  712. !function(a,b){"object"==typeof exports&&"undefined"!=typeof module?module.exports=b():"function"==typeof define&&define.amd?define(b):a.PointerEventsPolyfill=b()}(this,function(){"use strict";function a(a,b){b=b||Object.create(null);var c=document.createEvent("Event");c.initEvent(a,b.bubbles||!1,b.cancelable||!1);
  713. for(var d,e=2;e<m.length;e++)d=m[e],c[d]=b[d]||n[e];c.buttons=b.buttons||0;
  714. var f=0;return f=b.pressure&&c.buttons?b.pressure:c.buttons?.5:0,c.x=c.clientX,c.y=c.clientY,c.pointerId=b.pointerId||0,c.width=b.width||0,c.height=b.height||0,c.pressure=f,c.tiltX=b.tiltX||0,c.tiltY=b.tiltY||0,c.twist=b.twist||0,c.tangentialPressure=b.tangentialPressure||0,c.pointerType=b.pointerType||"",c.hwTimestamp=b.hwTimestamp||0,c.isPrimary=b.isPrimary||!1,c}function b(){this.array=[],this.size=0}function c(a,b,c,d){this.addCallback=a.bind(d),this.removeCallback=b.bind(d),this.changedCallback=c.bind(d),A&&(this.observer=new A(this.mutationWatcher.bind(this)))}function d(a){return"body /shadow-deep/ "+e(a)}function e(a){return'[touch-action="'+a+'"]'}function f(a){return"{ -ms-touch-action: "+a+"; touch-action: "+a+"; }"}function g(){if(F){D.forEach(function(a){String(a)===a?(E+=e(a)+f(a)+"\n",G&&(E+=d(a)+f(a)+"\n")):(E+=a.selectors.map(e)+f(a.rule)+"\n",G&&(E+=a.selectors.map(d)+f(a.rule)+"\n"))});var a=document.createElement("style");a.textContent=E,document.head.appendChild(a)}}function h(){if(!window.PointerEvent){if(window.PointerEvent=a,window.navigator.msPointerEnabled){var b=window.navigator.msMaxTouchPoints;Object.defineProperty(window.navigator,"maxTouchPoints",{value:b,enumerable:!0}),u.registerSource("ms",_)}else Object.defineProperty(window.navigator,"maxTouchPoints",{value:0,enumerable:!0}),u.registerSource("mouse",N),void 0!==window.ontouchstart&&u.registerSource("touch",V);u.register(document)}}function i(a){if(!u.pointermap.has(a)){var b=new Error("InvalidPointerId");throw b.name="InvalidPointerId",b}}function j(a){for(var b=a.parentNode;b&&b!==a.ownerDocument;)b=b.parentNode;if(!b){var c=new Error("InvalidStateError");throw c.name="InvalidStateError",c}}function k(a){var b=u.pointermap.get(a);return 0!==b.buttons}function l(){window.Element&&!Element.prototype.setPointerCapture&&Object.defineProperties(Element.prototype,{setPointerCapture:{value:W},releasePointerCapture:{value:X},hasPointerCapture:{value:Y}})}
  715. var m=["bubbles","cancelable","view","detail","screenX","screenY","clientX","clientY","ctrlKey","altKey","shiftKey","metaKey","button","relatedTarget","pageX","pageY"],n=[!1,!1,null,null,0,0,0,0,!1,!1,!1,!1,0,null,0,0],o=window.Map&&window.Map.prototype.forEach,p=o?Map:b;b.prototype={set:function(a,b){return void 0===b?this["delete"](a):(this.has(a)||this.size++,void(this.array[a]=b))},has:function(a){return void 0!==this.array[a]},"delete":function(a){this.has(a)&&(delete this.array[a],this.size--)},get:function(a){return this.array[a]},clear:function(){this.array.length=0,this.size=0},forEach:function(a,b){return this.array.forEach(function(c,d){a.call(b,c,d,this)},this)}};var q=["bubbles","cancelable","view","detail","screenX","screenY","clientX","clientY","ctrlKey","altKey","shiftKey","metaKey","button","relatedTarget","buttons","pointerId","width","height","pressure","tiltX","tiltY","pointerType","hwTimestamp","isPrimary","type","target","currentTarget","which","pageX","pageY","timeStamp"],r=[!1,!1,null,null,0,0,0,0,!1,!1,!1,!1,0,null,0,0,0,0,0,0,0,"",0,!1,"",null,null,0,0,0,0],s={pointerover:1,pointerout:1,pointerenter:1,pointerleave:1},t="undefined"!=typeof SVGElementInstance,u={pointermap:new p,eventMap:Object.create(null),captureInfo:Object.create(null),eventSources:Object.create(null),eventSourceList:[],registerSource:function(a,b){var c=b,d=c.events;d&&(d.forEach(function(a){c[a]&&(this.eventMap[a]=c[a].bind(c))},this),this.eventSources[a]=c,this.eventSourceList.push(c))},register:function(a){for(var b,c=this.eventSourceList.length,d=0;d<c&&(b=this.eventSourceList[d]);d++)
  716. b.register.call(b,a)},unregister:function(a){for(var b,c=this.eventSourceList.length,d=0;d<c&&(b=this.eventSourceList[d]);d++)
  717. b.unregister.call(b,a)},contains:function(a,b){try{return a.contains(b)}catch(c){return!1}},down:function(a){a.bubbles=!0,this.fireEvent("pointerdown",a)},move:function(a){a.bubbles=!0,this.fireEvent("pointermove",a)},up:function(a){a.bubbles=!0,this.fireEvent("pointerup",a)},enter:function(a){a.bubbles=!1,this.fireEvent("pointerenter",a)},leave:function(a){a.bubbles=!1,this.fireEvent("pointerleave",a)},over:function(a){a.bubbles=!0,this.fireEvent("pointerover",a)},out:function(a){a.bubbles=!0,this.fireEvent("pointerout",a)},cancel:function(a){a.bubbles=!0,this.fireEvent("pointercancel",a)},leaveOut:function(a){this.out(a),this.propagate(a,this.leave,!1)},enterOver:function(a){this.over(a),this.propagate(a,this.enter,!0)},eventHandler:function(a){if(!a._handledByPE){var b=a.type,c=this.eventMap&&this.eventMap[b];c&&c(a),a._handledByPE=!0}},listen:function(a,b){b.forEach(function(b){this.addEvent(a,b)},this)},unlisten:function(a,b){b.forEach(function(b){this.removeEvent(a,b)},this)},addEvent:function(a,b){a.addEventListener(b,this.boundHandler)},removeEvent:function(a,b){a.removeEventListener(b,this.boundHandler)},makeEvent:function(b,c){this.captureInfo[c.pointerId]&&(c.relatedTarget=null);var d=new a(b,c);return c.preventDefault&&(d.preventDefault=c.preventDefault),d._target=d._target||c.target,d},fireEvent:function(a,b){var c=this.makeEvent(a,b);return this.dispatchEvent(c)},cloneEvent:function(a){for(var b,c=Object.create(null),d=0;d<q.length;d++)b=q[d],c[b]=a[b]||r[d],!t||"target"!==b&&"relatedTarget"!==b||c[b]instanceof SVGElementInstance&&(c[b]=c[b].correspondingUseElement);return a.preventDefault&&(c.preventDefault=function(){a.preventDefault()}),c},getTarget:function(a){var b=this.captureInfo[a.pointerId];return b?a._target!==b&&a.type in s?void 0:b:a._target},propagate:function(a,b,c){for(var d=a.target,e=[];d!==document&&!d.contains(a.relatedTarget);) if(e.push(d),d=d.parentNode,!d)return;c&&e.reverse(),e.forEach(function(c){a.target=c,b.call(this,a)},this)},setCapture:function(b,c,d){this.captureInfo[b]&&this.releaseCapture(b,d),this.captureInfo[b]=c,this.implicitRelease=this.releaseCapture.bind(this,b,d),document.addEventListener("pointerup",this.implicitRelease),document.addEventListener("pointercancel",this.implicitRelease);var e=new a("gotpointercapture");e.pointerId=b,e._target=c,d||this.asyncDispatchEvent(e)},releaseCapture:function(b,c){var d=this.captureInfo[b];if(d){this.captureInfo[b]=void 0,document.removeEventListener("pointerup",this.implicitRelease),document.removeEventListener("pointercancel",this.implicitRelease);var e=new a("lostpointercapture");e.pointerId=b,e._target=d,c||this.asyncDispatchEvent(e)}},dispatchEvent:/*scope.external.dispatchEvent || */function(a){var b=this.getTarget(a);if(b)return b.dispatchEvent(a)},asyncDispatchEvent:function(a){requestAnimationFrame(this.dispatchEvent.bind(this,a))}};u.boundHandler=u.eventHandler.bind(u);var v={shadow:function(a){if(a)return a.shadowRoot||a.webkitShadowRoot},canTarget:function(a){return a&&Boolean(a.elementFromPoint)},targetingShadow:function(a){var b=this.shadow(a);if(this.canTarget(b))return b},olderShadow:function(a){var b=a.olderShadowRoot;if(!b){var c=a.querySelector("shadow");c&&(b=c.olderShadowRoot)}return b},allShadows:function(a){for(var b=[],c=this.shadow(a);c;)b.push(c),c=this.olderShadow(c);return b},searchRoot:function(a,b,c){if(a){var d,e,f=a.elementFromPoint(b,c);for(e=this.targetingShadow(f);e;){if(d=e.elementFromPoint(b,c)){var g=this.targetingShadow(d);return this.searchRoot(g,b,c)||d} e=this.olderShadow(e)} return f}},owner:function(a){
  718. for(var b=a;b.parentNode;)b=b.parentNode;
  719. return b.nodeType!==Node.DOCUMENT_NODE&&b.nodeType!==Node.DOCUMENT_FRAGMENT_NODE&&(b=document),b},findTarget:function(a){var b=a.clientX,c=a.clientY,d=this.owner(a.target);
  720. return d.elementFromPoint(b,c)||(d=document),this.searchRoot(d,b,c)}},w=Array.prototype.forEach.call.bind(Array.prototype.forEach),x=Array.prototype.map.call.bind(Array.prototype.map),y=Array.prototype.slice.call.bind(Array.prototype.slice),z=Array.prototype.filter.call.bind(Array.prototype.filter),A=window.MutationObserver||window.WebKitMutationObserver,B="[touch-action]",C={subtree:!0,childList:!0,attributes:!0,attributeOldValue:!0,attributeFilter:["touch-action"]};c.prototype={watchSubtree:function(a){
  721. //
  722. this.observer&&v.canTarget(a)&&this.observer.observe(a,C)},enableOnSubtree:function(a){this.watchSubtree(a),a===document&&"complete"!==document.readyState?this.installOnLoad():this.installNewSubtree(a)},installNewSubtree:function(a){w(this.findElements(a),this.addElement,this)},findElements:function(a){return a.querySelectorAll?a.querySelectorAll(B):[]},removeElement:function(a){this.removeCallback(a)},addElement:function(a){this.addCallback(a)},elementChanged:function(a,b){this.changedCallback(a,b)},concatLists:function(a,b){return a.concat(y(b))},
  723. installOnLoad:function(){document.addEventListener("readystatechange",function(){"complete"===document.readyState&&this.installNewSubtree(document)}.bind(this))},isElement:function(a){return a.nodeType===Node.ELEMENT_NODE},flattenMutationTree:function(a){
  724. var b=x(a,this.findElements,this);
  725. return b.push(z(a,this.isElement)),b.reduce(this.concatLists,[])},mutationWatcher:function(a){a.forEach(this.mutationHandler,this)},mutationHandler:function(a){if("childList"===a.type){var b=this.flattenMutationTree(a.addedNodes);b.forEach(this.addElement,this);var c=this.flattenMutationTree(a.removedNodes);c.forEach(this.removeElement,this)}else"attributes"===a.type&&this.elementChanged(a.target,a.oldValue)}};var D=["none","auto","pan-x","pan-y",{rule:"pan-x pan-y",selectors:["pan-x pan-y","pan-y pan-x"]}],E="",F=window.PointerEvent||window.MSPointerEvent,G=!window.ShadowDOMPolyfill&&document.head.createShadowRoot,H=u.pointermap,I=25,J=[1,4,2,8,16],K=!1;try{K=1===new MouseEvent("test",{buttons:1}).buttons}catch(L){}
  726. var M,N={POINTER_ID:1,POINTER_TYPE:"mouse",events:["mousedown","mousemove","mouseup","mouseover","mouseout"],register:function(a){u.listen(a,this.events)},unregister:function(a){u.unlisten(a,this.events)},lastTouches:[],
  727. isEventSimulatedFromTouch:function(a){for(var b,c=this.lastTouches,d=a.clientX,e=a.clientY,f=0,g=c.length;f<g&&(b=c[f]);f++){
  728. var h=Math.abs(d-b.x),i=Math.abs(e-b.y);if(h<=I&&i<=I)return!0}},prepareEvent:function(a){var b=u.cloneEvent(a),c=b.preventDefault;return b.preventDefault=function(){a.preventDefault(),c()},b.pointerId=this.POINTER_ID,b.isPrimary=!0,b.pointerType=this.POINTER_TYPE,b},prepareButtonsForMove:function(a,b){var c=H.get(this.POINTER_ID);
  729. 0!==b.which&&c?a.buttons=c.buttons:a.buttons=0,b.buttons=a.buttons},mousedown:function(a){if(!this.isEventSimulatedFromTouch(a)){var b=H.get(this.POINTER_ID),c=this.prepareEvent(a);K||(c.buttons=J[c.button],b&&(c.buttons|=b.buttons),a.buttons=c.buttons),H.set(this.POINTER_ID,a),b&&0!==b.buttons?u.move(c):u.down(c)}},mousemove:function(a){if(!this.isEventSimulatedFromTouch(a)){var b=this.prepareEvent(a);K||this.prepareButtonsForMove(b,a),b.button=-1,H.set(this.POINTER_ID,a),u.move(b)}},mouseup:function(a){if(!this.isEventSimulatedFromTouch(a)){var b=H.get(this.POINTER_ID),c=this.prepareEvent(a);if(!K){var d=J[c.button];
  730. c.buttons=b?b.buttons&~d:0,a.buttons=c.buttons}H.set(this.POINTER_ID,a),
  731. c.buttons&=~J[c.button],0===c.buttons?u.up(c):u.move(c)}},mouseover:function(a){if(!this.isEventSimulatedFromTouch(a)){var b=this.prepareEvent(a);K||this.prepareButtonsForMove(b,a),b.button=-1,H.set(this.POINTER_ID,a),u.enterOver(b)}},mouseout:function(a){if(!this.isEventSimulatedFromTouch(a)){var b=this.prepareEvent(a);K||this.prepareButtonsForMove(b,a),b.button=-1,u.leaveOut(b)}},cancel:function(a){var b=this.prepareEvent(a);u.cancel(b),this.deactivateMouse()},deactivateMouse:function(){H["delete"](this.POINTER_ID)}},O=u.captureInfo,P=v.findTarget.bind(v),Q=v.allShadows.bind(v),R=u.pointermap,S=2500,T=200,U="touch-action",V={events:["touchstart","touchmove","touchend","touchcancel"],register:function(a){M.enableOnSubtree(a)},unregister:function(){},elementAdded:function(a){var b=a.getAttribute(U),c=this.touchActionToScrollType(b);c&&(a._scrollType=c,u.listen(a,this.events),
  732. Q(a).forEach(function(a){a._scrollType=c,u.listen(a,this.events)},this))},elementRemoved:function(a){a._scrollType=void 0,u.unlisten(a,this.events),
  733. Q(a).forEach(function(a){a._scrollType=void 0,u.unlisten(a,this.events)},this)},elementChanged:function(a,b){var c=a.getAttribute(U),d=this.touchActionToScrollType(c),e=this.touchActionToScrollType(b);
  734. d&&e?(a._scrollType=d,Q(a).forEach(function(a){a._scrollType=d},this)):e?this.elementRemoved(a):d&&this.elementAdded(a)},scrollTypes:{EMITTER:"none",XSCROLLER:"pan-x",YSCROLLER:"pan-y",SCROLLER:/^(?:pan-x pan-y)|(?:pan-y pan-x)|auto$/},touchActionToScrollType:function(a){var b=a,c=this.scrollTypes;return"none"===b?"none":b===c.XSCROLLER?"X":b===c.YSCROLLER?"Y":c.SCROLLER.exec(b)?"XY":void 0},POINTER_TYPE:"touch",firstTouch:null,isPrimaryTouch:function(a){return this.firstTouch===a.identifier},setPrimaryTouch:function(a){
  735. (0===R.size||1===R.size&&R.has(1))&&(this.firstTouch=a.identifier,this.firstXY={X:a.clientX,Y:a.clientY},this.scrolling=!1,this.cancelResetClickCount())},removePrimaryPointer:function(a){a.isPrimary&&(this.firstTouch=null,this.firstXY=null,this.resetClickCount())},clickCount:0,resetId:null,resetClickCount:function(){var a=function(){this.clickCount=0,this.resetId=null}.bind(this);this.resetId=setTimeout(a,T)},cancelResetClickCount:function(){this.resetId&&clearTimeout(this.resetId)},typeToButtons:function(a){var b=0;return"touchstart"!==a&&"touchmove"!==a||(b=1),b},touchToPointer:function(a){var b=this.currentTouchEvent,c=u.cloneEvent(a),d=c.pointerId=a.identifier+2;c.target=O[d]||P(c),c.bubbles=!0,c.cancelable=!0,c.detail=this.clickCount,c.button=0,c.buttons=this.typeToButtons(b.type),c.width=2*(a.radiusX||a.webkitRadiusX||0),c.height=2*(a.radiusY||a.webkitRadiusY||0),c.pressure=a.force||a.webkitForce||.5,c.isPrimary=this.isPrimaryTouch(a),c.pointerType=this.POINTER_TYPE,
  736. c.altKey=b.altKey,c.ctrlKey=b.ctrlKey,c.metaKey=b.metaKey,c.shiftKey=b.shiftKey;
  737. var e=this;return c.preventDefault=function(){e.scrolling=!1,e.firstXY=null,b.preventDefault()},c},processTouches:function(a,b){var c=a.changedTouches;this.currentTouchEvent=a;for(var d,e=0;e<c.length;e++)d=c[e],b.call(this,this.touchToPointer(d))},
  738. shouldScroll:function(a){if(this.firstXY){var b,c=a.currentTarget._scrollType;if("none"===c)
  739. b=!1;else if("XY"===c)
  740. b=!0;else{var d=a.changedTouches[0],e=c,f="Y"===c?"X":"Y",g=Math.abs(d["client"+e]-this.firstXY[e]),h=Math.abs(d["client"+f]-this.firstXY[f]);
  741. b=g>=h}return this.firstXY=null,b}},findTouch:function(a,b){for(var c,d=0,e=a.length;d<e&&(c=a[d]);d++)if(c.identifier===b)return!0},
  742. vacuumTouches:function(a){var b=a.touches;
  743. if(R.size>=b.length){var c=[];R.forEach(function(a,d){
  744. if(1!==d&&!this.findTouch(b,d-2)){var e=a.out;c.push(e)}},this),c.forEach(this.cancelOut,this)}},touchstart:function(a){this.vacuumTouches(a),this.setPrimaryTouch(a.changedTouches[0]),this.dedupSynthMouse(a),this.scrolling||(this.clickCount++,this.processTouches(a,this.overDown))},overDown:function(a){R.set(a.pointerId,{target:a.target,out:a,outTarget:a.target}),u.enterOver(a),u.down(a)},touchmove:function(a){this.scrolling||(this.shouldScroll(a)?(this.scrolling=!0,this.touchcancel(a)):(a.preventDefault(),this.processTouches(a,this.moveOverOut)))},moveOverOut:function(a){var b=a,c=R.get(b.pointerId);
  745. if(c){var d=c.out,e=c.outTarget;u.move(b),d&&e!==b.target&&(d.relatedTarget=b.target,b.relatedTarget=e,
  746. d.target=e,b.target?(u.leaveOut(d),u.enterOver(b)):(
  747. b.target=e,b.relatedTarget=null,this.cancelOut(b))),c.out=b,c.outTarget=b.target}},touchend:function(a){this.dedupSynthMouse(a),this.processTouches(a,this.upOut)},upOut:function(a){this.scrolling||(u.up(a),u.leaveOut(a)),this.cleanUpPointer(a)},touchcancel:function(a){this.processTouches(a,this.cancelOut)},cancelOut:function(a){u.cancel(a),u.leaveOut(a),this.cleanUpPointer(a)},cleanUpPointer:function(a){R["delete"](a.pointerId),this.removePrimaryPointer(a)},
  748. dedupSynthMouse:function(a){var b=N.lastTouches,c=a.changedTouches[0];
  749. if(this.isPrimaryTouch(c)){
  750. var d={x:c.clientX,y:c.clientY};b.push(d);var e=function(a,b){var c=a.indexOf(b);c>-1&&a.splice(c,1)}.bind(null,b,d);setTimeout(e,S)}}};M=new c(V.elementAdded,V.elementRemoved,V.elementChanged,V);var W,X,Y,Z=u.pointermap,$=window.MSPointerEvent&&"number"==typeof window.MSPointerEvent.MSPOINTER_TYPE_MOUSE,_={events:["MSPointerDown","MSPointerMove","MSPointerUp","MSPointerOut","MSPointerOver","MSPointerCancel","MSGotPointerCapture","MSLostPointerCapture"],register:function(a){u.listen(a,this.events)},unregister:function(a){u.unlisten(a,this.events)},POINTER_TYPES:["","unavailable","touch","pen","mouse"],prepareEvent:function(a){var b=a;return $&&(b=u.cloneEvent(a),b.pointerType=this.POINTER_TYPES[a.pointerType]),b},cleanup:function(a){Z["delete"](a)},MSPointerDown:function(a){Z.set(a.pointerId,a);var b=this.prepareEvent(a);u.down(b)},MSPointerMove:function(a){var b=this.prepareEvent(a);u.move(b)},MSPointerUp:function(a){var b=this.prepareEvent(a);u.up(b),this.cleanup(a.pointerId)},MSPointerOut:function(a){var b=this.prepareEvent(a);u.leaveOut(b)},MSPointerOver:function(a){var b=this.prepareEvent(a);u.enterOver(b)},MSPointerCancel:function(a){var b=this.prepareEvent(a);u.cancel(b),this.cleanup(a.pointerId)},MSLostPointerCapture:function(a){var b=u.makeEvent("lostpointercapture",a);u.dispatchEvent(b)},MSGotPointerCapture:function(a){var b=u.makeEvent("gotpointercapture",a);u.dispatchEvent(b)}},aa=window.navigator;aa.msPointerEnabled?(W=function(a){i(a),j(this),k(a)&&(u.setCapture(a,this,!0),this.msSetPointerCapture(a))},X=function(a){i(a),u.releaseCapture(a,!0),this.msReleasePointerCapture(a)}):(W=function(a){i(a),j(this),k(a)&&u.setCapture(a,this)},X=function(a){i(a),u.releaseCapture(a)}),Y=function(a){return!!u.captureInfo[a]},g(),h(),l();var ba={dispatcher:u,Installer:c,PointerEvent:a,PointerMap:p,targetFinding:v};return ba});
  751. ///////////////////////////////////////////////
  752. ///////////////////////////////////////////////
  753. var config = {"dark_mode": false, "show_pads": true, "show_fabrication": false, "show_silkscreen": true, "highlight_pin1": false, "redraw_on_drag": true, "board_rotation": 0, "checkboxes": "Sourced,Placed", "bom_view": "left-right", "layer_view": "FB", "fields": ["Value", "Partkeepr"]}
  754. ///////////////////////////////////////////////
  755. ///////////////////////////////////////////////
  756. var pcbdata = JSON.parse(LZString.decompressFromBase64("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QOJAEkbicOJ24mGrK4cVImX8VmJLYmHieGJs4nbCYWJhUDFicuJv4n9iZvxlYmoSUBJ6EkBPIPwWEk28ThJL4m6iUeJnQknid8J34nwSfAJ5ElGiSaJNYmM8TRJDIL0SUQJjEkQSXqa+YkOiV8JRfFwSUUJZElISRRJ64nGiaCJpon+iT48g8pgSZOJrQl4SeJJHYmSSTGJ54kySQhJZYnXiecJikmbib4JAklNMOpJuEmviSxJ0EmficRJHEkGSVxJckk8SUpJfEnojPSCk6hWSUxJYYnaSXOJH4mnibBJ+knuidxJlEmmSWhJqthHxN5Jokl5iVBJEklESV0AJEk/iYZJV4n/iSZJvElbiZFJakmz8dSJTYkiSTmJzEn4Se+Juklnib8JnEmISScJaUm+icpJ94k+PBaJOUnYSfuJMUlviaxJAUnsSdJJIUkuSWFJGUm1iS48GdSuALCJE4nWSYVJfkmESTBJX4kdSfGJXUkKST1JjPF1iYlkAdRPiflJU4k2SUVJrUklSUFJZUlOSRVJ5YnySShJ4UlASfNJEALRSQVJvklxSTpJCUmuAElJ5UlGSVVJt4k1SYHgLjwsgg1JDElNSadJkEkRif5JG0kTScFJU0mVSWuJ+0mzSWaJ9II4pCdJK0kjSedJX0mXSbAA10nbSbdJAMnpSW5JsonzSVthYMmaSatJo0nMieNJDkmTSSuJ/0nISYjJZklzSWmJoEkvScJJb0ngyWdJn0ljSfZJiUmOSZ1JJwlciTyJDfFaAFvxgonCiV/AYokhBiAAUonc8b1J/jj6UPtAFGBgAEqJ1gB7AJsqeoAHAACQ6ol1IsQg+QnoyYVJa/Ga8YaJ3UlIybWJLroxAGNA6oljxHkJ1rCtiWtJdkmBST9JW0kMySbxXonjCarJRMnAyVGUmsmITI2JOsnwzO9JYkmQyTTJRsk4yb9JeMm7Sa5JVsm9QEaO9IBayWTJKwmvFACg+smYyWxJUkkeybJJ+Ml7SYTJw4nccPMJAclCSUHJ6fF6yVpJLslYybTJV0n0yX9JXsmWyXHJIqjzCYA0SckIScHJqckYyenJ4cl6SSbJOcnGSdVJ7km+yZHUFHDayRwUIclpydTJGcluyXTJuMlRybnJM0lqycTJL8BXNMGA9sktyWXJEMntyZXJpUmuiclJzknRyd7Jw4nxKEk0RckDSblJDsmtyeXJE8ltSRHJ1cmeydXxxgnciXXxLMmcAGzJpICt8cgg7fGiidVhvMk4AP3xvgna6GlgZGDCyaLJ4/HiQVPxvaiWiSVSceAV4M1JygBKyRvx88lASfaSErigkM3JV0A34D/JBsnxSdjJXcmRySlJHokh8ebJg5wAKQ9JHQgFWA9EoCkcAOApTsmxSZvJ30nuyTvJPcm1yfdJ9clAYFbkHQJ2ybCJZQiYKevJ48kESR3J7UmwKbPJvcmAyf3Jd6AhJuQpSwmUKcPwWCmUyR9JdCmTyZtJ08k3SalJCMl1ybKJWxA7YuPEnCmrydwpNClUyfwpW8lVyUIpcMkiKQTJYil3yeKCHCkYKdgAPCkKyfIpxUnQybDJpslEKYBJKCle+lIp2im6KaHJFcmKKVPJJYlwKaFJfck+yUBgxVQWKYHJCElgKXIpfCkGKdApWcndyQ4p00ksKc4pKMIeHglgICnuKVQpOileKc7JuCmGKdnJu8l3SaYpJClYYJopmkK5kCPJniljyfop60lxKf4pTCkmKVRJKCklwikY6SlcKZkpEClhybYpgin2KfkpiSmFKckp1Fho0OlglinRKTgpCil4KTApBCkBKXPJecmAKWQpaSktKVkp3ik5Kb4pMMnxKYQpdSkHSSgphAQlKYMpFSk2KR0pfimMKTtJBSlTKckpb8BsuOEpxcmRKVYpbcntKbkpyynwyWopxCmyiRspWikRKbIpQykxKfspoylGKTXJTMmHyXyJrMkCiafJQonnySKJAACaEok8yb3xfMmM8XU4mTDWsELxsoniycPwkvHksjLJxAkHYDFJQFa/yfqJyskaojHJ6in/KcIg09BBieUp2CmwqZApF0m3KeMp3SlmyaMJ3olOKcOJfMiAqeip1ClXKbIA4oiVKYspYyl5KSspDynCgE8px8kvKZwAZ8mNAO3xAAAKBuA98VzxN8nJKXUJxZDeipqwKHDPyX0AoKn+UFPxsezDybCJNGhyyaUYmgli8Zwif8kqyU4paAkLyRduUgRJ/kwSMqm5SY/IzQlEcjFJj2C2SVApmcmJCckJTImaUBigzgCZCdwJXCmu4MegAgmkSfipnomEqRbJaqleCcEpGSaEsGoEPnST6EGJRHLp8Yap2CnGqdippIBWqdpQncnyIFYJ3AmWqWig1qkxqWAJMikOqcCUNSkMqfvJzMnMqTHJ7MnvKZzJAADKAADq3ynXyY0ApylQPJIKT8kgqdZoU/F92B/J5CCgINqJ2CniySRAKqmIqd7J6qmAKW1skuJegFucoNAOBE+J/UhESDFJnpAmqS4AEak2qVGp5qkmCXQp46mJqU+JRCAePF0ptSmiKYSJHakPST+oTAC64gQUOYBiEEGJg6m7LLwp5AA0lJjJs6lmqUYJjAnTqXGpWlATqUkJ06mDSa6cOtBpqUcpSKmrqZ6pw4lEcOYQueBkMG1g0imX8cdwO0iaCSOpYamcAKepk6nnqbep1glXqQmptqlJqfLxuQkaSU+pAImuqWMJSCmWyWup6ynlqU0KAalBqcOpx6npyaBpCQngaRapnQkEackJXCnLScopximTKbvxb6lASfwK2DaGBNXkkrAMoNhpeQlL8UiJQGknqfGpkamEaXOpUGk8aWRpMikUaYhpnIkZqY8poQD8iVHxOakcqSKJook8qZKJvyn8qbKJ86DeIrmQwKm+CXsAk2xT8buJ7ilXXKaAl0AwqeKIral9AMcpSSnKaXpQ5yAK+NsptozBMIZpLUmGyQwpS6k7SchpRKlBKSSpwPCWabqpcGk2aeyIh6lUqfZppqlRqXSphymqKS+pZmm+CbDQwyCWSRcpdpC+aXopuUDUqQspBylOac+pyCnJKZfYUWmp8DFp6fEOBJoJWKk0qclplGk1ydRprCmExFyivLReabwJsWm5aUiJ+WlJabip9Kmpab0pD0mlMJlpVmkryd5p1Wl2aaOpUMkNaSFpf4krqeFp/yly+BVpzcldaZipiWmxKX1pKWmhaWlpymkwrJ5pY2k5ad1pwGn0KdvJRWkJKYNp9SmnKVdkxHTtaSPJ42l+aSIKPWmuyY5pG2kTKVtpayk7aVhIMR5LaXkJNWm5icdpq2kCKcbJ52n4qaspQMnIYLtpbPT7aVwph2nxaTgAT2kFadNpb2nLqaZp22kRaYPKi2nZafdpK2nA6ZnJwWkzaQNp4OlXab4JFgLDIM74d2m2aSGp0lHw6UFpdymbaSjpn2lKUDdpc/BY6XFpmglA6fVpCOkE6RdpROmlaSTpmpha0OTpD2mcYFTpU2k06XipYOlhaRDpjPHo6TEAlJgs6UapuOnU6fjpXOkrKSVpwSn86WCA+ohC6TjpAWk4qZzpjWmzac1p6ynfafHClWm2Cf9plOki6RzpYunK6cjpPOmo6Xzp6ukoFHLpR2m66TcpSun9aY4pbml0aabpWWnWadrpSIns6Vbp+uk26YEpscn26eKiaEjm6QDpOimW6T4p1ulI6bbpXunrqerp7lR+6TrpCum9acHpoOkS6ZdpxOn9APgk0OlO6ctpE2kx6adp62kiaaHpyKkQYOrpT4hR6S7pgekjKXHpOeme6XnpX2niogFMRemPaSXpDmnZ6c6p3OlzaWjp6ulvELXpbOn16YFpZ2nl6T0pxKlASVOk9vwZiTDp2Ol+aZnpa2lKKb3pzClh6elpcvgGQJrpKwnO6RvJbuk96U3pCen06cEprWkuHMPpaemw6dgp4+kvafgp8elNaf3pLWnWVLhs0skj6RTpeylB6e7pIekV6ScpEWmmnBfpO+kdaVVp6elj6SdpE+l2KWvpJ+l26S1pwqIv6QvpCElL6bQpt+mr6TPJ6+lG6UnpTFbb6cAZesAf6f7pB+lVKa9pU+kfaQzpyek3adFpu+mj6f7pdWl66RAZwimG6S3p/yni7HAZHenZKQ3pk+m/6Srpp+npabLJ8+kUGcMpVBk/6ZAZf+kz6cppJWBAGUwZ1yngGY3pbBm0Gf/p6WkcaOQZV+ms6ZQZ3en8GUQZuemP6f8pmtzcGWIZ8ykEGVIZKinEGarpymmFYKIZOBnX6cvpfBnUGQIZahl0GRoZXKKMGYoZ++lf6YfpnSnH6YIZHBmQ6eKiOmnaGeIZzBmSGfoZ0hkP6UNpsWAWaQoZjhlKGSvpKhlUaYnpGBnv0LYil+k+GdgpHQx46YQZqhkyGR4ZFBTladgZb+la6YgZeWmTaX4ZrhnRGe4ZvOmhBLcgzPI8GTLpFhkoGUfpaBmS6WhJFaRD9K/pB2nJGUiJERmi6VEZARkb6aUZJhmR6WYZfmk1GcoZ6Rn1GdAZQRllGRXA8Bk+aU4ZOABtGWkZrBluGX3pQhlbiT0ZSvEtGf7pQxl6GSMZGRljGbYZ/EmTGaEZiRmL6VUZuYmzGaXpd+nWGYYZ4xnmST0Z3UR5GVsZLBnVKTQZexlLGdkZTiIVGX9pGxmcYCcZLhnzGZ0ZJBlXGQLpjulrGSAZdxnXAA8Ziuk7GcUZgRleqWUZXhJ9GaAZ3xnIGbSptOnvaSUZA+kiGf8gv2kyKaCZCWngmYVp/xkNGdRJQJlk6dMZmgk/G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RKZ1yAxOc7yutm02SxZ2dmQOfkpATnC2aLZWDlKObg5btlo6SVYBfJaOSdZOjm42fXZ1gAvNP+UMUm4WaTZD1mcOWE6OCCDkOhgI8lxWcFZYJnc2Sg5LqmFORw5xTkjOaAgOtkXKZM5/Tl5OX45vNkBOQTJgokcWaqiZiKHSK05y8kc9KI5BgD7IGWAFPF1OSE5DTns8ZE55dkfNLA5vzDtOQk5ujni4PjZ6fHQqbdZxjlk2QA5ymnz7BSQhnrlOZzZ5JkzOVvZf9nzOSPZnDl5zsQgKzmaWV45X1mgub/Z6akrWaBZpjkbWZ4J/Nk8ifs5lvyMUP85xzkRtLA5ZznekBc5fNkF2cuZoTmkgEo5T9BS2fc5KWDfqE85RDlX2YrZbzn6OR85hjmDGXdZJjm/OXYZ6FgAueM51jkVOZDZiLnVOc5pELn/WaPZvOi8ubC5HxnzRMC5MxkbOX2ZVFl82QKAAtlDObs5KLlACfypBzme1OPY+Lnz2N+oRLmLSCS5VzmI2RS5nABUuZLZdzlKaeZJXlnxhCPxElk42Uk57zl5CZ85fmnE2VQ54Vk0OcU5bLRmrLkSQLmW2bk5wrkOOYnZYrks2dRJXlnCQCjkEznwucNZLDn5OWw5yrm18ai5JrkKOQ05+QCu2VE5BCAuOOaIzzmdOU65rLkuuey5EvEDOerZBFlmOVngMOxFetk5Uzn8APTZEDnBufpZobkuObWJ2bk8yNW5Wdlz2Q2529kaufDZGDnkuWm5tzkn2bS5j0lwoPmoCgC5ucy53TmhAL05hnCpOdQ5ZbkSuQF6ukJRuQK5crmVOZ258bmmyf/Zi9n7WW1IwyBlOas5MblW2VU5Xbk1OWg5cjlkuVtZIAAqOUoAGbnDuSnAiFiBPPa52NmJOXo5xckzua658rnfOUM5xTkPucUIfrmHuYK5mxkKuUspTZnsOZC5v7k2GOyA7Nn+uTk50zkG2TzZCbm1OUE59Tnt8So5ZdnWuYzxnCINWHE5DrmvuSy577myWUW5JblcuTu54invqN9gK7meOUB59xkgeYjpuxkpWe+ZYbkoKRR55tmweTW5HLlBuZu5QFmYuatZgTnrWcE5prkNOYvBNLmYeRBgLjhtOYy5NdmTuck5bLlzuZ65C7nDORJ50rnRuTR51wB1ub45irljWdu5L9lVqKAgYzntufrZcbmbOUh557n8efI5mDloeUwAd7lieXlgriKSeYqJeHmvOVO5fQAfuUW57rlpOeW5r9kS6Cp5q7kBuep5XenIOWC5orkyiUx5zbnDaekYfLmGeRbpdHmQmWe5Pbm72YuZqbloeYcANnlbmb6cBDkMuY55L7nOebJ5hbnyeYM5XrktmSqZojm+edR5a7mxuSe53HkhuaF5w9niuVC5UthReex5Hbn2OdV5qDkJeeg5SXmWeSKJhamqOXg5W4moqXeZ2XkK2UR4EvH+rvjZruAIOAV5pbndWek5zeiOIGEpIDklmdmyCIktef3ZWnkFObV5zjnQOejZV8CaQr+oT4mBqat5RnlVeSZ5MjlmeSm53Xn5qQWpETlDubZ5EiDCIKHQE7mjeVLJTpD6OZN5t1DTeaR5L9kdcHcJk9lxmSQSc1kneRu5Z3mOOVt5RTnFeVngMYgxAP95Hxn6qUD5MXlceaD5tlm8ecm5KHnXORfJt3lqObWJwiDGJhO5a3I32XIw6om24HfU38kZ6d+5RXmxGamJfrRRKNopvdnk+Qh5szngueD5CzmQ+YHguPlf0PeZTDm2OT45UjlteVA53LlzSQnGPfiGUBkp3PneObF5Ypk2Gb+5wvmtcHT5M9lWWUj5G3kJueF5wMlCqRpZMrnD8PT5PPmS+clZrgnIeQJ5qHmyacfZ2PlzSZhpi6nPuQrZBaB12TJZaJKawBApnnmLuZgatAT8uTIpfDnA+a15yPmNuSz5EHls+TXCh1l9Ge75iPmM+cF54pls+boh6lnFmW754vnruZ75yvnPGXN5XhKWOYH5MfmVeSD58fl8mYL5kCDO+bmM8Dna+XTZgXm4mUz5yLmo+YLZl3n9uRfJlrl3eel5lNk1WLTEw3lMuSQ5t9nFyeQ5Cvm5iVipjvmcOXOU20AHeVwpQfkS+Ur5oHkMeRBoqVkQ+VT59CCo0D35eflt+ZxgdjnreUP5VJkv2f75ARy9+dH5EjkM+cZ5Gfko+Sq59lkXuX25V7mcqRh5Nfm5hLQU6MwN+S/JPkgQkJLxQCB1qfzApPmJoCtpnfl5mZGZ6WnYmB3wMlnoWLD58lmraZWZqCBTqZBptZnQaRepWQl6qbbxv6j9mU25AJn5mRFpRTjv+crZn/lkWbP5vPljqemZvin/+bGpgAUCaXepoAXoWH+ZOZnT6c/5IADeCelpMaBwBYR5CAVDWe35yAXhqagFZ6mpmaggpGnYBZfxuAUQaIv5aGkv+cppkKBkBR8ZI9hRKZQFSAUVmbQFYGn0BZ8AjAUgBcwFCPnD+c2ZVYnQBSbpDbjcBY2JvAVf+Yr5P/lCBbxpMGkzqWoFmZk28SwF4AVKuQb5FnkV+SXZg7mm+bFgOGyzBBO5TfkkmbCJrflFuZy5PzlkeRFpYRhMIKyA0/l2BYP59HkQBT759XnFOb3sLgVLeWv5+fmz2XH5C/lKuar5DznoWDoRbgWSOUF5SLlGSQYFl7lO2de5Ltmiecf5saRUvBDcUnnaOTJ5zrl9OV85HrmFeYp5vgXurICUrvneaWs5+QWb+aEF2nmQBVn5ekSRBcnczXmVBad5W/nteaX5vbldeUYFyjkogGl51VkpYLwFt4S4eTl5XTl5eXkFbrkkeQ4FL9kFJEwgjQWAeRV5x7np+dUFm3mj+az54/nTBSIgpjpNBcH5VQWeBfoFF3no+UJ5KXlV+aYFxoAoFOPEg4DPefh5LnlWAG55X3mTBYn5ZwUyVAyScwX+ebW5hfk8mSK5ZYk6eQ8FFtDt7HjgWwUF+br5+AWJuaq5iXnouV0FYTka2qkFfQWv2ckQiWQWQJcFuXm5BbO5TakTBT+5bPnHBmasrJAAhbH58/m7BTUF3gXMebPpSliEcDpoUHiqefMFgbkh+XEFV4kJBfv5SQXhOSYF/XlP6Xgw5wXPgIiFIwXIhZ+5IZn2BeiFawXk8oxUzwVwuWp5bwVAhWB53wVeeUZKUdwj4HqQFIWvBYDp7wXf6ae5Jfk7+Xx55flXuY05t7nQhcSFcIXlROyFWQUdOTkFBbljBf7pHnnzubN5koUzFIKFsoV+eXB5ooUeBXF5IXkrBb75awVWhX8FDxAvBXaFCoVihVIFsNn7BYb5GPkiiY05R/kwhYcEqeRDBSN5VwWjBSiF4wW8hZT5hJl0OT486vDReYCFDoVS+fAphIXhBYmFz4iRuSmFuIX1ufz58XntBWCFgnnJeUGFzpbV+TCFFajhhRyF+bmEeSk5qIVxhUUFbPnVhb65ZQW8CRUF2wUtBUsFbDkShRK5rYU4cLmFOIVp+SEF+IVbOf6FhgUahWWgvQXpaRQJ1pC1hW+5PAVEeXcFfIUJhZgZxEgAecKFlIUBeT6FXgXOhT4FGIVzhUOFnoUceQHpu4V7BR15e/mdBVe5LPFXycyF/ynt6H1ylgWvuc2yxPm28e1pLdnmhWi56UnEBcpp7qxPhbpptvHoYMEFeIWOhV8FtQXQGb+FEWlpZABFAPksBcBFG9maeT2F53mXheZ5Dtm55DeFJvn3hdYAfIFMBBGFjfkvhVR5DdnvhaeFTYUWhYDJUEWM8QZEeEVBifBFpEVphXr5b5l1eR9plEXXFJH56dl0RTEFRfmh+bSFE4XoRRCFpIAs8ccF2EWhAPfU2pS/qJfZ0nmSyT3EZDkkRQ75X4VE6axFokXvqMMAtEU5afRF1IWfBUhpEEU8SUpFBxTwCapFgEWcRcw53YVjhaZ5qEUpuRhFSQUs8XmpIYX0GePYw/GSRdo5o4xT8cLExPnhiqaARDryRQp55EWEybIFEGBeGfGSv6k28Ud5+qlcRSBpagUpmRoF/GkZmUwFvAlqwJ/ZLGn4BX2FfkUcBSyFbuLIiUlFAPmhRSQS4UUaUJFFVZkiBSgFdZkaBWUpuxIIOVlFvoXpWf5FdnkkhZlFHjl/qSt5YUUmRe3Jv/mfAOgFpgkMBVoFcUW2CQlFFUUOBGwFHqlEBVLpXhn9kA1FIUVNRblFLUWaBSVFwgXRRZgFsUXiBfFF5UWSLk6pQjlQBWlFEXlwhUFFAamTRcGpn+mqBbNF6gXABRgFXUVHRdoFvUUrRZtaa0W82ZLpNUUpYAKFtEmVgLtFBqkPNAdFXGlHRVFFJ0WdRaIF3UVLRZdF1xCrRcCFd0WbRbFgP/pznO0QL0VBqW9FSBlJmQVFf/nVmQAFZ0VABRBpdqkyKX1FQMVgedVFoMW+KHIwEMXPRYBFOUX7RbDFh0UoxV9FqMU/RcVFKMUXRSsJGMXXRcDFG0XDRWhJjwU7RYTFe0UwxSBFC0VoBYjFp0W/RedFPUW0xVdFasA3RSr5jMV6RYRIvwVjRVDFbGkcxYhFfMVkxYVF80XIxVgF/0WCxYDF9MVYxSDFTMUD6fp4oCASvtLFzUXvRfhp8MXtRTzFlMU0BfzFqsUeKULFlUWDRUEp90X9AODF9UUGxVNFRsWtRSbFLgAdRTNF1MUCxdbF6sXCxcCFdIVJeVZFB9nXuXeFzTkPhVS8LchORYaFjrmLhYoFy4WUOU/5MfHixcP4UiAtyHKFXoVtKaBF6YWMecxF1GmpxSpiMCAZxbaFGkU7BWBFvEUWRQcFw0VXucXZWPkiRcAwuAXegJYFdGCkOW+FUrIP2SuF8YXGieLFiWmmECn56/ldhYsFZkVbuTpF3Um9xfAJYeClGKA5g8WphZpFyoVh+TvxqcXvqFSc0QUe+TnFjEV+hVXFAYWeqVe5AACCAACidkUDeZHUBD7POT6oNan7EOqJ6vCB2ZVF3/nJxUvFXqlO6sXFUfnMBXqABZA6WcPFFcUXGYbxvcWeMUwgFvlPicdIH8Ub+aZF38VKmdrF4dnzLEwsQCW0iaeFc/kFhV75czmEhUHFG1khxWa5IACrmfXFEcU6gI9AbPSGUDHFLzm42e2F07kq2fkFD8UXCb3FeCXxICQlKwmdhV+588WFhU6FYXmJ6VQlw/C2dERFHYVHubR5DEWBxXxFe9noJVg5q5lNOZm5/QV0gOWwC4V42caFdEkD+QUFM3nfhb/Fm+k5iMMgctknhZ/Fo4XgJSP5LCWKRZvpT2Iw+TtImcXwJdQFHwULxZXFxYWdeWglAkXbWeHFoiWBpOIlBYSW+Uy5ccVSJfWFHzlGJWRFCiUpxUolDvLnIHmFKgXlxbnFWiX5xawluiX3CL4lw4VUBeeF/jn8JcHFViWYJVhFOCVfmUk0n4CSJbQlNwVkJSC5ciXfeePFNJlJNPJofiXAebwl4oVjxTNJE8WDxBwlBSU8JYwlSCVFhaqFaPk7xTXFSQWrmcJFiSUPeaHcnfiOJdJ5ziVpJR+5GkUUJT3FXqmmOkK4lSVIOdxFNIUZhfuFLEVPxWckwyURJd/ZSEUjxTx5dSVl+dXFgiXi2Z2WJwUk6bUJP5kGhUQlAVlchaeFZoU+RZ4lj8XDifzQtsl7oHMlO4VFJb6FKUWKJWclqEz0gLogIyX2hdUlrQXdueYlV4WWJVe5q5lMha0l/FA0+eiAqSXRhYTZuYlHJYUFvkX3Jd7p1xAOUB7gVyWvJQElm8V3JV4lDyU0+R2q8KXehTclaBmoJaWFayUiieuZWoVWuWkF09DbQILxDVlOJfh5PSUZJSTF/SWQJa/5JfT8onFphiV5RZYZyEVg+ZMlBcWb6UgqjKXjRbYJ9CWcxQslmiWFWdvF8jl4pWuZ1LlEpTCF0K7+tDtIhCV5uUrZriUuuX0lCkWQRYMltaoypS8lvBmIJe8lzPkcpSElEUkzJRqlGKXZxdqlbKXb+Um5KyUNJWKlLgAs8cfFEWnt6D/U5/nZBdJFhnCyRSwFH4V4SbSlqcXurI6lcPlARWXFYCWBJUxF23k6JSSpaWS+pbAl92kBpV/FQaVbxZ8laEUCJXElLPF9ef8li3BMIAmwz4W6Oa+Fd9lyRQz5XqXBKQSo6aUGJfepxkU6+Vilg9mZhfqlh0nDLBmlRkXqReolG8V8JSKl/EU3hdgltiV9cuJFmaVSWdmlLfm5pQdF+aVxySvRnaV1pVGlLKW9mWal3vl6paGlQEn6csOlcEX1pdNFjaVYxTil9plJpSIlw7lppbQE+EVSRYRFbqXp8R6lT9kqpbpFwSmbvjRFI6UW+QKlfPk1JcwlwSXTpQ9JhaVbpWpFo6WLpaaliyXmpaCFFiW4pUmlGyUNxVYAuEUbBbKl5KU7pVmlnCWiRX2lNKVHpTklw4nURQBlHEULpW7FGiWxpcilpyWAKX7JfIFPpRelcsWxBVpFomnNpYmlV7k1gDOFyMmQNPAskiXXBbcFScWQZaUlJ6V+cDuJxaXlefKFJqWCpUhlJSUURSeli5J0ZZqlTGVXpTqlKoUWpR0F3yVJBcXZKaW2JZvIACWAZfE519ltxTmlHcUUObGFFPnNhShl4dnwCbk+A8VBBSOFS6W3JaxlP4U0mZEFlQjiOeplCwWIZZvFUyUkqe+o/I5rxUPFxmVNpfGllkVxJcXZ66X3eUCACWQ/rC3FddlupdcQcmUMJQOlM6XOHDhwi+y8Oan5hSVvJROlyCVTpaql+cnz7EAplmUMJYilDMV3pckpC7wMwqv53mn9+Qi5IWVvpW0FyyUCZV+ltcU2JcO5YaxPQBJFQGXOpa3FzfkfGbYFXcWKZZQlwSmu1j35amUz+dclGWVCpeB5B4VKZQ0pNhzcMDFl+YXMZSZlnKXvqW3sBSjdZRplr6UtZSCFu/kJpbEltcUpBZKliWVh1O50EmUSWVYF1wWVZY2FCmWQpSilvmUqRewMw2VGZZple4XaJRFlW2WggOQqu2VUhXFlmsVVpfelKuALZQ1lRbkaeTxloWW1JfxlJYWrpdNldqVURThsIqRuZYT5K2UC2E3ZVWUbZe1lsok6EdKFBMKBZbPFPWWPZZllYWWHZcel0GXurOx8d2VreaNlsaWmZUBJjVT1vKolEOWGZedlgaUmZSulpIDWpaSApdkiZRulE3hFZT9l0mW9pbJljWX8AOCl8iUJZcJZ7ehUGGdlTWUXZVpllaVM5cZZLOVdZQZldOWYpc1laOX9Zb5lBGz1+Zr5aWUjZb1lNmXZZa9lROVxJVS5RGXmSY950cUlZbHFUYVchUW5HflUZWxlaElDJV80XGUIJdLlxSVc5Udl4dluOStGxqVG5dDlY2WE5bvFDIUJJaJlj4XNxbsl8qUEeUuFDYX9pTrlOmXuaeqlq8VqJS+lxuWc5eFl8OUD6UXFcpBcZVEl44V4ZVNlDIUtJe2lCLAQxcVlkmVGhYqlJoWfhccl3OUDyYRBtEkpZVwlIoVapUHlB2W3pWblc2VIJM+IsPnMpYHlNuVo5XbljSWhxUo5P6X/Jf+lQ2Wu5anlHuVyeZRlmeWl5SDl+bZA2QEF5QXcJaMlJiVMJeBFpuWh5SgpmRD95XnlfKVD5c4Z2GWmJbhltmWrJQrlcgBK5cOZJ2UdJXKl7eUJxZ7lsiU+ZfelzaFoKR0lVeVlpULlSKXaZVClh+WyIEy4huXGJUqFo+VmJbLln6VvZQyFrlnahbKJYkUJYNjlnSXZBc4l5GWJxV7l3eUT5ckpD45fXD/lp+UkxRzlxeUhpT3lvgn6lFiAaWAQFaXFY6VJWTLlL2Uv5fLlGoUVWR/lxll+ya3lTqXq5UiF0iXchZ6l3uVX5bu5FeDT5calUeW9hZflm2XXZeXllOUB5Qhl+2UXhcvlVqWr5Y5laQX/pfaSZGUgpe4FWSX3BdRl0GV95UnlNBXlpQSFIeVQZahlhqV8FSwVUBX45egVH6VfJbllb+WO5fe5JGW1pYQVeyXxxbCJFGVn5Qfl+1kcZUWld+W0FaPF4+UyFVGZtGWmFVbl9+WspTDlz2UqFZNlgmUN5adg6+WHHKHc2hW/5UQVnIUkFVrlRmnkFQwVAqn65dBIdhXmFeylcOVWFQKpbjlhFQoV3/nQFewVz+WqFa/lbhX5ZU5lFjj6gK4FbeX/5QIV3kUQpSclNWW+5RY5WRUD5fnl24Xz5WMlOGV5xbAVIBWcGe+k4+BlFbPlBeXcZQvlj+VL5ckVLhVqFW4VGhUZFe3oUQU5FRrl/hX5FYzlcBWkGeqlAxVbhYxlERU1edIVIhVh5Q0V+YRNFXQlc+WF5TXlBOUxJa4VGCUqOfHlw7kt5Xa52+W5FZrlIxXZJfMVD0lThDVYdrmQFZelbRXXpWPlcxW65bIVwV6iUJHlkhXR5RwVoqUK5c+AHhUNycRI26V/5UMVaeUxhRBlwBXRFcjJMVlYhRIV5+XJRfQVwOXGWUwV2Ej0ZYPlLRUzFVllGBUpFVgVDIVHAD8V/QLUcOPEyeVOeX4VQJWkFYeloJVnFaAVj9BfXCvZKBXV5bcVvGX3FVEV5JWf5cTK5Ckz5SsVKJVvFeZFHxUtpViV3BUwhbwVDiWHFYCVHeX5eV3lBRVZ5ThFYhWIla8V0JUm5Q8VPuVPFROcDiXXFVhlVRWL5VXxdeXE5ea5oMA4lUCA+BUHFWrluhXu5bvlneVAFeKVYxXAyTnlv8JQlYkVUhWMlY8VjBWGpdaV8RWqlSPldxVP5eiVXRWpFdsVMMC6lR2lXGAElcMFdYUilenlZBVklQ6ViWXH0UQg4og2lUoVcpX2lQqVh+Xs2fmobJUISfylrpUP5e6VHRWelXZl02VtpQVlUWWw+dvly2UeZQDla2VCFauFAyXvqS5lawkS5UFleOUxpRfllhVMlRopkdQAwsjl68Wo5X1lV2UNKW3s8hU45QLlZ4WclShF3JX4ZUkFhal8lftZafGFWP8VJ1kJZKCpRwDqiXiVIiBk+YYVQRVwlXNJNhx4BeQFKckNpV2VMJXNlRGVmUkRHNuVS4UsBZSp1uV0lU9lN6W1FWCVtYk2HLLoCcXnlXuVReVhBT2Vx5UhGamVvAWOyWfltpXvFZ0VuZXjlQWp6RVpBbhwOHnPOfOVPqhLlVS8q5X75euVRRWAKXpOYICnlU+Vu5WgJY2VB5XylRQVpymDZShV+hW28ReV9hXjpY4VN5Vj+QhVRSkRHI+V+FXPlehV1mWXZRKV+ZCUVcsV6SU0Vb+VcZVVRZqVq+VN5bYltZgD8IGVkYXEFcSVARUeJQxVWeB6yIylqZWyuYxll5Vqle0VNRVkVVWVh0kiEDVYntkulZElw5VLJTmVK+V82Z4AAQAXAAEA+zkEALYAnGAgAAAA4pypeakhwEmABABigMqiLgB/AG0AKAB4AGmAvAD2VR/x6YCfAI9gDADoWXQAYvEMALIAFACYAPrAQVXJKlKiqAB4AKqiqAAZgFYAMVXxJZ0A+YDFAMaJzSXxVfSQSVUssoSSFwm/JfFVZrBJVTNlo/C5VRKJiCCJVZgl7+WyAECQuVXuGiKpmVW/EqaAt0C5VZa5zNCJVblVnQDhAMVVMFktVWtATVWYJRE5A6DxAF1VwiWdAIzAfVUcALFVq5mVVSMA/VU4FQFVw1V9ALlVIQbjuV1VKjkROZHgI1XXudZ5d9TlVWtV7hoFAMVVKjko2YWAu1WqAJ0AsaCHVSyylInGieE5EonIgLtVvpWODDdVy1U5Vde5lADHVY9V4TnuGmCoi1USiQ0Jq1XhOZ0AipA/Vem5mCkeBBdVgNWi+LtVVhyYKfQUF1XZIJ0AiOCHVRKJi5U/Ve4VmskzVbFVSjnuGuDlF1Uo2VOAu1WWuVzAu1UssheA4NUSiRDA4NXyaV8g4NUROVZpINUhBmzAqNUg1YSlzAA3VZa5vEA3VSyymRgXVfrAnQCTwLtV8wDc1fy4F1XowNzVwNXXuc6W5AQ/VdOFXFT01aLVf1WvVbc5z6Ag1e4arADS1So5f1XkYJ9V2NVU0J9VlrnkuItVPQVMINVAXVWEZUMA6tUjVcaJtqXxVQLVIADJpfFVItUs8RE5VqDFVSzxIQbIQE7VGNVxkObVKNnlgE7VlrlChebVLLKUQNLVbPGdABNVZtXW1TNl+QDS1eKJf1VR1eTVUdXLVVrVYdWiiSEGhtVJ1e4aeyBdVaKJKNnPRZnVlrmIkl1VXymDVdLVhalfVcXVBanyaRmJXVWFqctVOIBV1QWpIQY3+XXV71VFAF1VpdktVVDV2Dnyaah4xol1xYNVdVXYOS7Vj1XF2e4aOWQ91SjZ2SDFVcXZtzkSkKtVpdmEpT60Fwml2V9VVtWk5Z0AvcQ91VNVK+Cz1YzWeiDS1YfFEokyBF1VjplXVUHVtkWDVRnVYdVd8S1V0tWH+dzVodWzVSAA3KnxVX1V0vESgAQADgBOAFcgowBZQAQAIslKWaAAt7k78VWAB8Us8fAAHfHiAFMAsYB/AHCABuCjAChpxomZgFGAgQAoAPAAool5qbnkWgCCELe5owAG4DvxAAByB8UAAPJ7AIFwfwCbYtWJmqw78ZmATABYJbBZFwrYNVoAySo78auZQkUUAAAAEsIlaRDQNZ1y1YnBwDvxZYCZgJUAMvHQNbni1YkJGDvxiADpgLYA+vG/1X8AiwBFgNWJoU478XmpAADSiCAs8XzAAABqplXcySoAa+WjAES4yjUOmdzcF4HYgEo5plVkNTmA1YnTEDvxnKnigDepDMCarHA1gDUXCadgDgAYuVTADDWcAGvl/DUMAO41cjXOwF41IACuWTvxbhBEAB41m0TViRVZoTWLAOE1ATWNYFE1TDUXCVuASgD+NZg1gTVRNXw1rjWlgAAAOlMAiwCIAPIAETUVWXA14jUXCWE1ETUJNXA1SjXlNbE1lTXBwHA1hjUXCUEU0DVValE1NjUXCXg1nKl4NWQ1mpDViTDV+DXdNb0196D9NfA1IABdNT01WgDxaEE17hU78bfAKjVyNYWAMzWUNbU1CzWYNdg2/TVJNcaJhKXjNauZ/TVZNds1fwB4NXs1ODVlNYc1WgDHNf01NTXGiTIAazWcAPkQDTVTNU01NzVKAHc1IAD5EDM1HTUvNW81HzUUNS413zWLNS7ZowDYlTE1PzUbNcC1uDUXCYcAOzX5EMkqwLUrNcaJ0LWAtXC1WgDPVXM1iODItRQ1BzUgAEQABvGnQCoAiwAUNWc1PMnU8fi185QUNdc1IAB5qQ6ZwiVYEtA1U4CPNZwAGtpUNQAAkmNVdrhSybd5QFZEuMC1XzVUtcY1LWAyAHg1lAAFqQAAGiwAGLlAVqFOowB81cw1hDV4NXg18ACPgKK1CTWYNW4Q1YlC1YM1kzWcANM1GrWQtTc1MNXrNfo1jDUItSAAbwhgtRegGrVbNcE1hrX3NbGg4fEytdi1iCAwtfa1GrXEtc61gLUOtYw1lLUetUa1XrWcAAagMfG+tXa1xrUBtXy1wbXvNa61owAjNaE1trWRtaG1IABgwDHxwDWgNUo5KABTADAA0DUGILw1+rUgACm18ABptRm1N6lwgE41WgDQwMm1IDUFtem1mbVaACW1vDXWtXQA/6DwAFy1KxC8Ndi1agCLNawAvDXEtRM1wzVBNeoAMfF7xd4AXgBQNWq1MSC8Nc81ZrV0wHk1BTVFNfE10rVlteG1SgBxNek1VTVaAM7AQbUztUoAhTWVNQkYowD5gEG1dTXxNYy1IAAMoIe1y7X1NWI1prUVNce1YjXWtTe1q7UntYTA57Urtfc1kTX7te61R7WPtWI1lLX5tYW1NbXeNdm1+7WTtX+11bXFtUB167V8taB1RbVZtUtAijX/NXm1lbX/teB1cHWjAFhsQDVIdWB1sHWltZwAjAAx8adgW7WFNWw1cjW9cIo1prW9tVM1fTVodda1FHU6tVR1WgAdtVq1fbWKNT21QzWUdSM1aHWUtbR1IAC6tWh1k7Xcdbx1DHVLtRa1QTVTgDHxHbXrNV21BjVjNRJ1drVSdVoAF4DiddVh04DydZwAEMBKdZ21e7UKdQ21ynXkitWJcTA78bJ1kbWqdSAAbMAadZJ1WnVqdT61LrUw1QY1k7XhACJ1+nV8tUi1RrUotZwAhYAx8S51IbVudSAAsaCedRi1rnXWNbm1XnXxtT51yID+dS61NLqjAGiAEXWYtdF12LUhdbC11jXEtYl1BnTWNVx1bHV0dRx1WgBAICnFhDWiiQ6Z8rXfKXo1sDU5dXy12eT5dYV1eDWwWSV1UTWEtaC1gLXYNVKi6qKzVQVVLVWpVSVV8mkfEKtV65ko2W1ZPXWWtaaAidUP1euZLVUUSElVXXWdVWHV65kROauA0tU7WS1VCQCfVYzVHXXhOSzVr1VQhXGEN1XT1f3VmoX81bzVEokGoLzV8mni1RcJjTk9VffVaNUROf9VZ3Wg1fqFt3Xy1R3VN7mw1eN1a1UI1VN1D9WNObLVu1Xy1T6AitV/VbXVYdVLVbDVq3XrVQIAm1XhOQ9VKtXyadYE2tV/VcN1V3Uo2QkEXVXB1UHxQdUR1eNAmdUSiabVD9VyaZ0AmPVJ1QnVUdUp1VHV6dVR1dnVBPW49XnVLdVh1YXVhHBl1aXVddUV1Ut1YdXV1bDVgPUP1W3VnkWT1ePVr3VT1dzVHtXYOTvVH3WxVcfVx1XS1VfVpoA31Yd1l3XGiU/VoCAv1WgJoQCjVSjZCVU9dZa5WUVpVfFVm1VZVWzQbVV5VX91JVWVVRz1o1U9VZT1o1UhBkNV/VXjVSrVGNWG9Uo52NWvdUo5eNWC9Y3l+PWrdWvlxHhk1fj1r1VTVdTVyQW01Sb10NXbVU91XbXDwodVlrknVUjVZ1WrddiVqQg3VfJpXJA/Vc9VffQ3Ve9VLPWfdV9VT3Wg1W4Qh1Xk1eb1INWa1dLVxtXf5ej1ltVO1fJph4BO1Q7VHdXO1WvVr3W3hYNVgvUs8V7VHXVCRYNVm1Us8QHVIvXGiROVx1UF9VS1wFXHVTT1nPVd1ZtVvdWkhZPVg9WT1SPVhvVz1WvV/dVL1WvVK9WlVevVQvUROSvgXVX71YNVCPXm1WfViEgv1ZTxyqK2APAA+lXiAAqAUqIEAOFV8ABJgBIJSqIqoqSASYAiEMoAQolAAAA=="))
  757. ///////////////////////////////////////////////
  758. ///////////////////////////////////////////////
  759. /* Utility functions */
  760. var storagePrefix = 'KiCad_HTML_BOM__' + pcbdata.metadata.title + '__' +
  761. pcbdata.metadata.revision + '__#';
  762. var storage;
  763. function initStorage(key) {
  764. try {
  765. window.localStorage.getItem("blank");
  766. storage = window.localStorage;
  767. } catch (e) {
  768. // localStorage not available
  769. }
  770. if (!storage) {
  771. try {
  772. window.sessionStorage.getItem("blank");
  773. storage = window.sessionStorage;
  774. } catch (e) {
  775. // sessionStorage also not available
  776. }
  777. }
  778. }
  779. function readStorage(key) {
  780. if (storage) {
  781. return storage.getItem(storagePrefix + key);
  782. } else {
  783. return null;
  784. }
  785. }
  786. function writeStorage(key, value) {
  787. if (storage) {
  788. storage.setItem(storagePrefix + key, value);
  789. }
  790. }
  791. function fancyDblClickHandler(el, onsingle, ondouble) {
  792. return function() {
  793. if (el.getAttribute("data-dblclick") == null) {
  794. el.setAttribute("data-dblclick", 1);
  795. setTimeout(function() {
  796. if (el.getAttribute("data-dblclick") == 1) {
  797. onsingle();
  798. }
  799. el.removeAttribute("data-dblclick");
  800. }, 200);
  801. } else {
  802. el.removeAttribute("data-dblclick");
  803. ondouble();
  804. }
  805. }
  806. }
  807. function smoothScrollToRow(rowid) {
  808. document.getElementById(rowid).scrollIntoView({
  809. behavior: "smooth",
  810. block: "center",
  811. inline: "nearest"
  812. });
  813. }
  814. function focusInputField(input) {
  815. input.scrollIntoView(false);
  816. input.focus();
  817. input.select();
  818. }
  819. function saveBomTable(output) {
  820. var text = '';
  821. for (var node of bomhead.childNodes[0].childNodes) {
  822. if (node.firstChild) {
  823. text += (output == 'csv' ? `"${node.firstChild.nodeValue}"` : node.firstChild.nodeValue);
  824. }
  825. if (node != bomhead.childNodes[0].lastChild) {
  826. text += (output == 'csv' ? ',' : '\t');
  827. }
  828. }
  829. text += '\n';
  830. for (var row of bombody.childNodes) {
  831. for (var cell of row.childNodes) {
  832. let val = '';
  833. for (var node of cell.childNodes) {
  834. if (node.nodeName == "INPUT") {
  835. if (node.checked) {
  836. val += '✓';
  837. }
  838. } else if (node.nodeName == "MARK") {
  839. val += node.firstChild.nodeValue;
  840. } else {
  841. val += node.nodeValue;
  842. }
  843. }
  844. if (output == 'csv') {
  845. val = val.replace(/\"/g, '\"\"'); // pair of double-quote characters
  846. if (isNumeric(val)) {
  847. val = +val; // use number
  848. } else {
  849. val = `"${val}"`; // enclosed within double-quote
  850. }
  851. }
  852. text += val;
  853. if (cell != row.lastChild) {
  854. text += (output == 'csv' ? ',' : '\t');
  855. }
  856. }
  857. text += '\n';
  858. }
  859. if (output != 'clipboard') {
  860. // To file: csv or txt
  861. var blob = new Blob([text], {
  862. type: `text/${output}`
  863. });
  864. saveFile(`${pcbdata.metadata.title}.${output}`, blob);
  865. } else {
  866. // To clipboard
  867. var textArea = document.createElement("textarea");
  868. textArea.classList.add('clipboard-temp');
  869. textArea.value = text;
  870. document.body.appendChild(textArea);
  871. textArea.focus();
  872. textArea.select();
  873. try {
  874. if (document.execCommand('copy')) {
  875. console.log('Bom copied to clipboard.');
  876. }
  877. } catch (err) {
  878. console.log('Can not copy to clipboard.');
  879. }
  880. document.body.removeChild(textArea);
  881. }
  882. }
  883. function isNumeric(str) {
  884. /* https://stackoverflow.com/a/175787 */
  885. return (typeof str != "string" ? false : !isNaN(str) && !isNaN(parseFloat(str)));
  886. }
  887. function removeGutterNode(node) {
  888. for (var i = 0; i < node.childNodes.length; i++) {
  889. if (node.childNodes[i].classList &&
  890. node.childNodes[i].classList.contains("gutter")) {
  891. node.removeChild(node.childNodes[i]);
  892. break;
  893. }
  894. }
  895. }
  896. function cleanGutters() {
  897. removeGutterNode(document.getElementById("bot"));
  898. removeGutterNode(document.getElementById("canvasdiv"));
  899. }
  900. var units = {
  901. prefixes: {
  902. giga: ["G", "g", "giga", "Giga", "GIGA"],
  903. mega: ["M", "mega", "Mega", "MEGA"],
  904. kilo: ["K", "k", "kilo", "Kilo", "KILO"],
  905. milli: ["m", "milli", "Milli", "MILLI"],
  906. micro: ["U", "u", "micro", "Micro", "MICRO", "μ", "µ"], // different utf8 μ
  907. nano: ["N", "n", "nano", "Nano", "NANO"],
  908. pico: ["P", "p", "pico", "Pico", "PICO"],
  909. },
  910. unitsShort: ["R", "r", "Ω", "F", "f", "H", "h"],
  911. unitsLong: [
  912. "OHM", "Ohm", "ohm", "ohms",
  913. "FARAD", "Farad", "farad",
  914. "HENRY", "Henry", "henry"
  915. ],
  916. getMultiplier: function(s) {
  917. if (this.prefixes.giga.includes(s)) return 1e9;
  918. if (this.prefixes.mega.includes(s)) return 1e6;
  919. if (this.prefixes.kilo.includes(s)) return 1e3;
  920. if (this.prefixes.milli.includes(s)) return 1e-3;
  921. if (this.prefixes.micro.includes(s)) return 1e-6;
  922. if (this.prefixes.nano.includes(s)) return 1e-9;
  923. if (this.prefixes.pico.includes(s)) return 1e-12;
  924. return 1;
  925. },
  926. valueRegex: null,
  927. }
  928. function initUtils() {
  929. var allPrefixes = units.prefixes.giga
  930. .concat(units.prefixes.mega)
  931. .concat(units.prefixes.kilo)
  932. .concat(units.prefixes.milli)
  933. .concat(units.prefixes.micro)
  934. .concat(units.prefixes.nano)
  935. .concat(units.prefixes.pico);
  936. var allUnits = units.unitsShort.concat(units.unitsLong);
  937. units.valueRegex = new RegExp("^([0-9\.]+)" +
  938. "\\s*(" + allPrefixes.join("|") + ")?" +
  939. "(" + allUnits.join("|") + ")?" +
  940. "(\\b.*)?$", "");
  941. units.valueAltRegex = new RegExp("^([0-9]*)" +
  942. "(" + units.unitsShort.join("|") + ")?" +
  943. "([GgMmKkUuNnPp])?" +
  944. "([0-9]*)" +
  945. "(\\b.*)?$", "");
  946. if (config.fields.includes("Value")) {
  947. var index = config.fields.indexOf("Value");
  948. pcbdata.bom["parsedValues"] = {};
  949. for (var id in pcbdata.bom.fields) {
  950. pcbdata.bom.parsedValues[id] = parseValue(pcbdata.bom.fields[id][index])
  951. }
  952. }
  953. }
  954. function parseValue(val, ref) {
  955. var inferUnit = (unit, ref) => {
  956. if (unit) {
  957. unit = unit.toLowerCase();
  958. if (unit == 'Ω' || unit == "ohm" || unit == "ohms") {
  959. unit = 'r';
  960. }
  961. unit = unit[0];
  962. } else {
  963. ref = /^([a-z]+)\d+$/i.exec(ref);
  964. if (ref) {
  965. ref = ref[1].toLowerCase();
  966. if (ref == "c") unit = 'f';
  967. else if (ref == "l") unit = 'h';
  968. else if (ref == "r" || ref == "rv") unit = 'r';
  969. else unit = null;
  970. }
  971. }
  972. return unit;
  973. };
  974. val = val.replace(/,/g, "");
  975. var match = units.valueRegex.exec(val);
  976. var unit;
  977. if (match) {
  978. val = parseFloat(match[1]);
  979. if (match[2]) {
  980. val = val * units.getMultiplier(match[2]);
  981. }
  982. unit = inferUnit(match[3], ref);
  983. if (!unit) return null;
  984. else return {
  985. val: val,
  986. unit: unit,
  987. extra: match[4],
  988. }
  989. }
  990. match = units.valueAltRegex.exec(val);
  991. if (match && (match[1] || match[4])) {
  992. val = parseFloat(match[1] + "." + match[4]);
  993. if (match[3]) {
  994. val = val * units.getMultiplier(match[3]);
  995. }
  996. unit = inferUnit(match[2], ref);
  997. if (!unit) return null;
  998. else return {
  999. val: val,
  1000. unit: unit,
  1001. extra: match[5],
  1002. }
  1003. }
  1004. return null;
  1005. }
  1006. function valueCompare(a, b, stra, strb) {
  1007. if (a === null && b === null) {
  1008. // Failed to parse both values, compare them as strings.
  1009. if (stra != strb) return stra > strb ? 1 : -1;
  1010. else return 0;
  1011. } else if (a === null) {
  1012. return 1;
  1013. } else if (b === null) {
  1014. return -1;
  1015. } else {
  1016. if (a.unit != b.unit) return a.unit > b.unit ? 1 : -1;
  1017. else if (a.val != b.val) return a.val > b.val ? 1 : -1;
  1018. else if (a.extra != b.extra) return a.extra > b.extra ? 1 : -1;
  1019. else return 0;
  1020. }
  1021. }
  1022. function validateSaveImgDimension(element) {
  1023. var valid = false;
  1024. var intValue = 0;
  1025. if (/^[1-9]\d*$/.test(element.value)) {
  1026. intValue = parseInt(element.value);
  1027. if (intValue <= 16000) {
  1028. valid = true;
  1029. }
  1030. }
  1031. if (valid) {
  1032. element.classList.remove("invalid");
  1033. } else {
  1034. element.classList.add("invalid");
  1035. }
  1036. return intValue;
  1037. }
  1038. function saveImage(layer) {
  1039. var width = validateSaveImgDimension(document.getElementById("render-save-width"));
  1040. var height = validateSaveImgDimension(document.getElementById("render-save-height"));
  1041. var bgcolor = null;
  1042. if (!document.getElementById("render-save-transparent").checked) {
  1043. var style = getComputedStyle(topmostdiv);
  1044. bgcolor = style.getPropertyValue("background-color");
  1045. }
  1046. if (!width || !height) return;
  1047. // Prepare image
  1048. var canvas = document.createElement("canvas");
  1049. var layerdict = {
  1050. transform: {
  1051. x: 0,
  1052. y: 0,
  1053. s: 1,
  1054. panx: 0,
  1055. pany: 0,
  1056. zoom: 1,
  1057. },
  1058. bg: canvas,
  1059. fab: canvas,
  1060. silk: canvas,
  1061. highlight: canvas,
  1062. layer: layer,
  1063. }
  1064. // Do the rendering
  1065. recalcLayerScale(layerdict, width, height);
  1066. prepareLayer(layerdict);
  1067. clearCanvas(canvas, bgcolor);
  1068. drawBackground(layerdict, false);
  1069. drawHighlightsOnLayer(layerdict, false);
  1070. // Save image
  1071. var imgdata = canvas.toDataURL("image/png");
  1072. var filename = pcbdata.metadata.title;
  1073. if (pcbdata.metadata.revision) {
  1074. filename += `.${pcbdata.metadata.revision}`;
  1075. }
  1076. filename += `.${layer}.png`;
  1077. saveFile(filename, dataURLtoBlob(imgdata));
  1078. }
  1079. function saveSettings() {
  1080. var data = {
  1081. type: "InteractiveHtmlBom settings",
  1082. version: 1,
  1083. pcbmetadata: pcbdata.metadata,
  1084. settings: settings,
  1085. }
  1086. var blob = new Blob([JSON.stringify(data, null, 4)], {
  1087. type: "application/json"
  1088. });
  1089. saveFile(`${pcbdata.metadata.title}.settings.json`, blob);
  1090. }
  1091. function loadSettings() {
  1092. var input = document.createElement("input");
  1093. input.type = "file";
  1094. input.accept = ".settings.json";
  1095. input.onchange = function(e) {
  1096. var file = e.target.files[0];
  1097. var reader = new FileReader();
  1098. reader.onload = readerEvent => {
  1099. var content = readerEvent.target.result;
  1100. var newSettings;
  1101. try {
  1102. newSettings = JSON.parse(content);
  1103. } catch (e) {
  1104. alert("Selected file is not InteractiveHtmlBom settings file.");
  1105. return;
  1106. }
  1107. if (newSettings.type != "InteractiveHtmlBom settings") {
  1108. alert("Selected file is not InteractiveHtmlBom settings file.");
  1109. return;
  1110. }
  1111. var metadataMatches = newSettings.hasOwnProperty("pcbmetadata");
  1112. if (metadataMatches) {
  1113. for (var k in pcbdata.metadata) {
  1114. if (!newSettings.pcbmetadata.hasOwnProperty(k) || newSettings.pcbmetadata[k] != pcbdata.metadata[k]) {
  1115. metadataMatches = false;
  1116. }
  1117. }
  1118. }
  1119. if (!metadataMatches) {
  1120. var currentMetadata = JSON.stringify(pcbdata.metadata, null, 4);
  1121. var fileMetadata = JSON.stringify(newSettings.pcbmetadata, null, 4);
  1122. if (!confirm(
  1123. `Settins file metadata does not match current metadata.\n\n` +
  1124. `Page metadata:\n${currentMetadata}\n\n` +
  1125. `Settings file metadata:\n${fileMetadata}\n\n` +
  1126. `Press OK if you would like to import settings anyway.`)) {
  1127. return;
  1128. }
  1129. }
  1130. overwriteSettings(newSettings.settings);
  1131. }
  1132. reader.readAsText(file, 'UTF-8');
  1133. }
  1134. input.click();
  1135. }
  1136. function overwriteSettings(newSettings) {
  1137. initDone = false;
  1138. Object.assign(settings, newSettings);
  1139. writeStorage("bomlayout", settings.bomlayout);
  1140. writeStorage("bommode", settings.bommode);
  1141. writeStorage("canvaslayout", settings.canvaslayout);
  1142. writeStorage("bomCheckboxes", settings.checkboxes.join(","));
  1143. document.getElementById("bomCheckboxes").value = settings.checkboxes.join(",");
  1144. for (var checkbox of settings.checkboxes) {
  1145. writeStorage("checkbox_" + checkbox, settings.checkboxStoredRefs[checkbox]);
  1146. }
  1147. writeStorage("markWhenChecked", settings.markWhenChecked);
  1148. padsVisible(settings.renderPads);
  1149. document.getElementById("padsCheckbox").checked = settings.renderPads;
  1150. fabricationVisible(settings.renderFabrication);
  1151. document.getElementById("fabricationCheckbox").checked = settings.renderFabrication;
  1152. silkscreenVisible(settings.renderSilkscreen);
  1153. document.getElementById("silkscreenCheckbox").checked = settings.renderSilkscreen;
  1154. referencesVisible(settings.renderReferences);
  1155. document.getElementById("referencesCheckbox").checked = settings.renderReferences;
  1156. valuesVisible(settings.renderValues);
  1157. document.getElementById("valuesCheckbox").checked = settings.renderValues;
  1158. tracksVisible(settings.renderTracks);
  1159. document.getElementById("tracksCheckbox").checked = settings.renderTracks;
  1160. zonesVisible(settings.renderZones);
  1161. document.getElementById("zonesCheckbox").checked = settings.renderZones;
  1162. dnpOutline(settings.renderDnpOutline);
  1163. document.getElementById("dnpOutlineCheckbox").checked = settings.renderDnpOutline;
  1164. setRedrawOnDrag(settings.redrawOnDrag);
  1165. document.getElementById("dragCheckbox").checked = settings.redrawOnDrag;
  1166. setDarkMode(settings.darkMode);
  1167. document.getElementById("darkmodeCheckbox").checked = settings.darkMode;
  1168. setHighlightPin1(settings.highlightpin1);
  1169. document.getElementById("highlightpin1Checkbox").checked = settings.highlightpin1;
  1170. showFootprints(settings.show_footprints);
  1171. writeStorage("boardRotation", settings.boardRotation);
  1172. document.getElementById("boardRotation").value = settings.boardRotation / 5;
  1173. document.getElementById("rotationDegree").textContent = settings.boardRotation;
  1174. initDone = true;
  1175. prepCheckboxes();
  1176. changeBomLayout(settings.bomlayout);
  1177. }
  1178. function saveFile(filename, blob) {
  1179. var link = document.createElement("a");
  1180. var objurl = URL.createObjectURL(blob);
  1181. link.download = filename;
  1182. link.href = objurl;
  1183. link.click();
  1184. }
  1185. function dataURLtoBlob(dataurl) {
  1186. var arr = dataurl.split(','),
  1187. mime = arr[0].match(/:(.*?);/)[1],
  1188. bstr = atob(arr[1]),
  1189. n = bstr.length,
  1190. u8arr = new Uint8Array(n);
  1191. while (n--) {
  1192. u8arr[n] = bstr.charCodeAt(n);
  1193. }
  1194. return new Blob([u8arr], {
  1195. type: mime
  1196. });
  1197. }
  1198. var settings = {
  1199. canvaslayout: "default",
  1200. bomlayout: "default",
  1201. bommode: "grouped",
  1202. checkboxes: [],
  1203. checkboxStoredRefs: {},
  1204. darkMode: false,
  1205. highlightpin1: false,
  1206. redrawOnDrag: true,
  1207. boardRotation: 0,
  1208. renderPads: true,
  1209. renderReferences: true,
  1210. renderValues: true,
  1211. renderSilkscreen: true,
  1212. renderFabrication: true,
  1213. renderDnpOutline: false,
  1214. renderTracks: true,
  1215. renderZones: true,
  1216. columnOrder: [],
  1217. hiddenColumns: [],
  1218. }
  1219. function initDefaults() {
  1220. settings.bomlayout = readStorage("bomlayout");
  1221. if (settings.bomlayout === null) {
  1222. settings.bomlayout = config.bom_view;
  1223. }
  1224. if (!['bom-only', 'left-right', 'top-bottom'].includes(settings.bomlayout)) {
  1225. settings.bomlayout = config.bom_view;
  1226. }
  1227. settings.bommode = readStorage("bommode");
  1228. if (settings.bommode === null) {
  1229. settings.bommode = "grouped";
  1230. }
  1231. if (!["grouped", "ungrouped", "netlist"].includes(settings.bommode)) {
  1232. settings.bommode = "grouped";
  1233. }
  1234. settings.canvaslayout = readStorage("canvaslayout");
  1235. if (settings.canvaslayout === null) {
  1236. settings.canvaslayout = config.layer_view;
  1237. }
  1238. var bomCheckboxes = readStorage("bomCheckboxes");
  1239. if (bomCheckboxes === null) {
  1240. bomCheckboxes = config.checkboxes;
  1241. }
  1242. settings.checkboxes = bomCheckboxes.split(",").filter((e) => e);
  1243. document.getElementById("bomCheckboxes").value = bomCheckboxes;
  1244. settings.markWhenChecked = readStorage("markWhenChecked") || "";
  1245. populateMarkWhenCheckedOptions();
  1246. function initBooleanSetting(storageString, def, elementId, func) {
  1247. var b = readStorage(storageString);
  1248. if (b === null) {
  1249. b = def;
  1250. } else {
  1251. b = (b == "true");
  1252. }
  1253. document.getElementById(elementId).checked = b;
  1254. func(b);
  1255. }
  1256. initBooleanSetting("padsVisible", config.show_pads, "padsCheckbox", padsVisible);
  1257. initBooleanSetting("fabricationVisible", config.show_fabrication, "fabricationCheckbox", fabricationVisible);
  1258. initBooleanSetting("silkscreenVisible", config.show_silkscreen, "silkscreenCheckbox", silkscreenVisible);
  1259. initBooleanSetting("referencesVisible", true, "referencesCheckbox", referencesVisible);
  1260. initBooleanSetting("valuesVisible", true, "valuesCheckbox", valuesVisible);
  1261. if ("tracks" in pcbdata) {
  1262. initBooleanSetting("tracksVisible", true, "tracksCheckbox", tracksVisible);
  1263. initBooleanSetting("zonesVisible", true, "zonesCheckbox", zonesVisible);
  1264. } else {
  1265. document.getElementById("tracksAndZonesCheckboxes").style.display = "none";
  1266. tracksVisible(false);
  1267. zonesVisible(false);
  1268. }
  1269. initBooleanSetting("dnpOutline", false, "dnpOutlineCheckbox", dnpOutline);
  1270. initBooleanSetting("redrawOnDrag", config.redraw_on_drag, "dragCheckbox", setRedrawOnDrag);
  1271. initBooleanSetting("darkmode", config.dark_mode, "darkmodeCheckbox", setDarkMode);
  1272. initBooleanSetting("highlightpin1", config.highlight_pin1, "highlightpin1Checkbox", setHighlightPin1);
  1273. var fields = ["checkboxes", "References"].concat(config.fields).concat(["Quantity"]);
  1274. var hcols = JSON.parse(readStorage("hiddenColumns"));
  1275. if (hcols === null) {
  1276. hcols = [];
  1277. }
  1278. settings.hiddenColumns = hcols.filter(e => fields.includes(e));
  1279. var cord = JSON.parse(readStorage("columnOrder"));
  1280. if (cord === null) {
  1281. cord = fields;
  1282. } else {
  1283. cord = cord.filter(e => fields.includes(e));
  1284. if (cord.length != fields.length)
  1285. cord = fields;
  1286. }
  1287. settings.columnOrder = cord;
  1288. settings.boardRotation = readStorage("boardRotation");
  1289. if (settings.boardRotation === null) {
  1290. settings.boardRotation = config.board_rotation * 5;
  1291. } else {
  1292. settings.boardRotation = parseInt(settings.boardRotation);
  1293. }
  1294. document.getElementById("boardRotation").value = settings.boardRotation / 5;
  1295. document.getElementById("rotationDegree").textContent = settings.boardRotation;
  1296. }
  1297. // Helper classes for user js callbacks.
  1298. const IBOM_EVENT_TYPES = {
  1299. ALL: "all",
  1300. HIGHLIGHT_EVENT: "highlightEvent",
  1301. CHECKBOX_CHANGE_EVENT: "checkboxChangeEvent",
  1302. BOM_BODY_CHANGE_EVENT: "bomBodyChangeEvent",
  1303. }
  1304. const EventHandler = {
  1305. callbacks: {},
  1306. init: function() {
  1307. for (eventType of Object.values(IBOM_EVENT_TYPES))
  1308. this.callbacks[eventType] = [];
  1309. },
  1310. registerCallback: function(eventType, callback) {
  1311. this.callbacks[eventType].push(callback);
  1312. },
  1313. emitEvent: function(eventType, eventArgs) {
  1314. event = {
  1315. eventType: eventType,
  1316. args: eventArgs,
  1317. }
  1318. var callback;
  1319. for (callback of this.callbacks[eventType])
  1320. callback(event);
  1321. for (callback of this.callbacks[IBOM_EVENT_TYPES.ALL])
  1322. callback(event);
  1323. }
  1324. }
  1325. EventHandler.init();
  1326. ///////////////////////////////////////////////
  1327. ///////////////////////////////////////////////
  1328. /* PCB rendering code */
  1329. var emptyContext2d = document.createElement("canvas").getContext("2d");
  1330. function deg2rad(deg) {
  1331. return deg * Math.PI / 180;
  1332. }
  1333. function calcFontPoint(linepoint, text, offsetx, offsety, tilt) {
  1334. var point = [
  1335. linepoint[0] * text.width + offsetx,
  1336. linepoint[1] * text.height + offsety
  1337. ];
  1338. // This approximates pcbnew behavior with how text tilts depending on horizontal justification
  1339. point[0] -= (linepoint[1] + 0.5 * (1 + text.justify[0])) * text.height * tilt;
  1340. return point;
  1341. }
  1342. function drawText(ctx, text, color) {
  1343. if ("ref" in text && !settings.renderReferences) return;
  1344. if ("val" in text && !settings.renderValues) return;
  1345. ctx.save();
  1346. ctx.fillStyle = color;
  1347. ctx.strokeStyle = color;
  1348. ctx.lineCap = "round";
  1349. ctx.lineJoin = "round";
  1350. ctx.lineWidth = text.thickness;
  1351. if ("svgpath" in text) {
  1352. ctx.stroke(new Path2D(text.svgpath));
  1353. ctx.restore();
  1354. return;
  1355. }
  1356. if ("polygons" in text) {
  1357. ctx.fill(getPolygonsPath(text));
  1358. ctx.restore();
  1359. return;
  1360. }
  1361. ctx.translate(...text.pos);
  1362. ctx.translate(text.thickness * 0.5, 0);
  1363. var angle = -text.angle;
  1364. if (text.attr.includes("mirrored")) {
  1365. ctx.scale(-1, 1);
  1366. angle = -angle;
  1367. }
  1368. var tilt = 0;
  1369. if (text.attr.includes("italic")) {
  1370. tilt = 0.125;
  1371. }
  1372. var interline = text.height * 1.5 + text.thickness;
  1373. var txt = text.text.split("\n");
  1374. // KiCad ignores last empty line.
  1375. if (txt[txt.length - 1] == '') txt.pop();
  1376. ctx.rotate(deg2rad(angle));
  1377. var offsety = (1 - text.justify[1]) / 2 * text.height; // One line offset
  1378. offsety -= (txt.length - 1) * (text.justify[1] + 1) / 2 * interline; // Multiline offset
  1379. for (var i in txt) {
  1380. var lineWidth = text.thickness + interline / 2 * tilt;
  1381. for (var j = 0; j < txt[i].length; j++) {
  1382. if (txt[i][j] == '\t') {
  1383. var fourSpaces = 4 * pcbdata.font_data[' '].w * text.width;
  1384. lineWidth += fourSpaces - lineWidth % fourSpaces;
  1385. } else {
  1386. if (txt[i][j] == '~') {
  1387. j++;
  1388. if (j == txt[i].length)
  1389. break;
  1390. }
  1391. lineWidth += pcbdata.font_data[txt[i][j]].w * text.width;
  1392. }
  1393. }
  1394. var offsetx = -lineWidth * (text.justify[0] + 1) / 2;
  1395. var inOverbar = false;
  1396. for (var j = 0; j < txt[i].length; j++) {
  1397. if (txt[i][j] == '\t') {
  1398. var fourSpaces = 4 * pcbdata.font_data[' '].w * text.width;
  1399. offsetx += fourSpaces - offsetx % fourSpaces;
  1400. continue;
  1401. } else if (txt[i][j] == '~') {
  1402. j++;
  1403. if (j == txt[i].length)
  1404. break;
  1405. if (txt[i][j] != '~') {
  1406. inOverbar = !inOverbar;
  1407. }
  1408. }
  1409. var glyph = pcbdata.font_data[txt[i][j]];
  1410. if (inOverbar) {
  1411. var overbarStart = [offsetx, -text.height * 1.4 + offsety];
  1412. var overbarEnd = [offsetx + text.width * glyph.w, overbarStart[1]];
  1413. if (!lastHadOverbar) {
  1414. overbarStart[0] += text.height * 1.4 * tilt;
  1415. lastHadOverbar = true;
  1416. }
  1417. ctx.beginPath();
  1418. ctx.moveTo(...overbarStart);
  1419. ctx.lineTo(...overbarEnd);
  1420. ctx.stroke();
  1421. } else {
  1422. lastHadOverbar = false;
  1423. }
  1424. for (var line of glyph.l) {
  1425. ctx.beginPath();
  1426. ctx.moveTo(...calcFontPoint(line[0], text, offsetx, offsety, tilt));
  1427. for (var k = 1; k < line.length; k++) {
  1428. ctx.lineTo(...calcFontPoint(line[k], text, offsetx, offsety, tilt));
  1429. }
  1430. ctx.stroke();
  1431. }
  1432. offsetx += glyph.w * text.width;
  1433. }
  1434. offsety += interline;
  1435. }
  1436. ctx.restore();
  1437. }
  1438. function drawedge(ctx, scalefactor, edge, color) {
  1439. ctx.strokeStyle = color;
  1440. ctx.fillStyle = color;
  1441. ctx.lineWidth = Math.max(1 / scalefactor, edge.width);
  1442. ctx.lineCap = "round";
  1443. ctx.lineJoin = "round";
  1444. if ("svgpath" in edge) {
  1445. ctx.stroke(new Path2D(edge.svgpath));
  1446. } else {
  1447. ctx.beginPath();
  1448. if (edge.type == "segment") {
  1449. ctx.moveTo(...edge.start);
  1450. ctx.lineTo(...edge.end);
  1451. }
  1452. if (edge.type == "rect") {
  1453. ctx.moveTo(...edge.start);
  1454. ctx.lineTo(edge.start[0], edge.end[1]);
  1455. ctx.lineTo(...edge.end);
  1456. ctx.lineTo(edge.end[0], edge.start[1]);
  1457. ctx.lineTo(...edge.start);
  1458. }
  1459. if (edge.type == "arc") {
  1460. ctx.arc(
  1461. ...edge.start,
  1462. edge.radius,
  1463. deg2rad(edge.startangle),
  1464. deg2rad(edge.endangle));
  1465. }
  1466. if (edge.type == "circle") {
  1467. ctx.arc(
  1468. ...edge.start,
  1469. edge.radius,
  1470. 0, 2 * Math.PI);
  1471. ctx.closePath();
  1472. }
  1473. if (edge.type == "curve") {
  1474. ctx.moveTo(...edge.start);
  1475. ctx.bezierCurveTo(...edge.cpa, ...edge.cpb, ...edge.end);
  1476. }
  1477. if("filled" in edge && edge.filled)
  1478. ctx.fill();
  1479. else
  1480. ctx.stroke();
  1481. }
  1482. }
  1483. function getChamferedRectPath(size, radius, chamfpos, chamfratio) {
  1484. // chamfpos is a bitmask, left = 1, right = 2, bottom left = 4, bottom right = 8
  1485. var path = new Path2D();
  1486. var width = size[0];
  1487. var height = size[1];
  1488. var x = width * -0.5;
  1489. var y = height * -0.5;
  1490. var chamfOffset = Math.min(width, height) * chamfratio;
  1491. path.moveTo(x, 0);
  1492. if (chamfpos & 4) {
  1493. path.lineTo(x, y + height - chamfOffset);
  1494. path.lineTo(x + chamfOffset, y + height);
  1495. path.lineTo(0, y + height);
  1496. } else {
  1497. path.arcTo(x, y + height, x + width, y + height, radius);
  1498. }
  1499. if (chamfpos & 8) {
  1500. path.lineTo(x + width - chamfOffset, y + height);
  1501. path.lineTo(x + width, y + height - chamfOffset);
  1502. path.lineTo(x + width, 0);
  1503. } else {
  1504. path.arcTo(x + width, y + height, x + width, y, radius);
  1505. }
  1506. if (chamfpos & 2) {
  1507. path.lineTo(x + width, y + chamfOffset);
  1508. path.lineTo(x + width - chamfOffset, y);
  1509. path.lineTo(0, y);
  1510. } else {
  1511. path.arcTo(x + width, y, x, y, radius);
  1512. }
  1513. if (chamfpos & 1) {
  1514. path.lineTo(x + chamfOffset, y);
  1515. path.lineTo(x, y + chamfOffset);
  1516. path.lineTo(x, 0);
  1517. } else {
  1518. path.arcTo(x, y, x, y + height, radius);
  1519. }
  1520. path.closePath();
  1521. return path;
  1522. }
  1523. function getOblongPath(size) {
  1524. return getChamferedRectPath(size, Math.min(size[0], size[1]) / 2, 0, 0);
  1525. }
  1526. function getPolygonsPath(shape) {
  1527. if (shape.path2d) {
  1528. return shape.path2d;
  1529. }
  1530. if ("svgpath" in shape) {
  1531. shape.path2d = new Path2D(shape.svgpath);
  1532. } else {
  1533. var path = new Path2D();
  1534. for (var polygon of shape.polygons) {
  1535. path.moveTo(...polygon[0]);
  1536. for (var i = 1; i < polygon.length; i++) {
  1537. path.lineTo(...polygon[i]);
  1538. }
  1539. path.closePath();
  1540. }
  1541. shape.path2d = path;
  1542. }
  1543. return shape.path2d;
  1544. }
  1545. function drawPolygonShape(ctx, scalefactor, shape, color) {
  1546. ctx.save();
  1547. if (!("svgpath" in shape)) {
  1548. ctx.translate(...shape.pos);
  1549. ctx.rotate(deg2rad(-shape.angle));
  1550. }
  1551. if("filled" in shape && !shape.filled) {
  1552. ctx.strokeStyle = color;
  1553. ctx.lineWidth = Math.max(1 / scalefactor, shape.width);
  1554. ctx.lineCap = "round";
  1555. ctx.lineJoin = "round";
  1556. ctx.stroke(getPolygonsPath(shape));
  1557. } else {
  1558. ctx.fillStyle = color;
  1559. ctx.fill(getPolygonsPath(shape));
  1560. }
  1561. ctx.restore();
  1562. }
  1563. function drawDrawing(ctx, scalefactor, drawing, color) {
  1564. if (["segment", "arc", "circle", "curve", "rect"].includes(drawing.type)) {
  1565. drawedge(ctx, scalefactor, drawing, color);
  1566. } else if (drawing.type == "polygon") {
  1567. drawPolygonShape(ctx, scalefactor, drawing, color);
  1568. } else {
  1569. drawText(ctx, drawing, color);
  1570. }
  1571. }
  1572. function getCirclePath(radius) {
  1573. var path = new Path2D();
  1574. path.arc(0, 0, radius, 0, 2 * Math.PI);
  1575. path.closePath();
  1576. return path;
  1577. }
  1578. function getCachedPadPath(pad) {
  1579. if (!pad.path2d) {
  1580. // if path2d is not set, build one and cache it on pad object
  1581. if (pad.shape == "rect") {
  1582. pad.path2d = new Path2D();
  1583. pad.path2d.rect(...pad.size.map(c => -c * 0.5), ...pad.size);
  1584. } else if (pad.shape == "oval") {
  1585. pad.path2d = getOblongPath(pad.size);
  1586. } else if (pad.shape == "circle") {
  1587. pad.path2d = getCirclePath(pad.size[0] / 2);
  1588. } else if (pad.shape == "roundrect") {
  1589. pad.path2d = getChamferedRectPath(pad.size, pad.radius, 0, 0);
  1590. } else if (pad.shape == "chamfrect") {
  1591. pad.path2d = getChamferedRectPath(pad.size, pad.radius, pad.chamfpos, pad.chamfratio)
  1592. } else if (pad.shape == "custom") {
  1593. pad.path2d = getPolygonsPath(pad);
  1594. }
  1595. }
  1596. return pad.path2d;
  1597. }
  1598. function drawPad(ctx, pad, color, outline) {
  1599. ctx.save();
  1600. ctx.translate(...pad.pos);
  1601. ctx.rotate(-deg2rad(pad.angle));
  1602. if (pad.offset) {
  1603. ctx.translate(...pad.offset);
  1604. }
  1605. ctx.fillStyle = color;
  1606. ctx.strokeStyle = color;
  1607. var path = getCachedPadPath(pad);
  1608. if (outline) {
  1609. ctx.stroke(path);
  1610. } else {
  1611. ctx.fill(path);
  1612. }
  1613. ctx.restore();
  1614. }
  1615. function drawPadHole(ctx, pad, padHoleColor) {
  1616. if (pad.type != "th") return;
  1617. ctx.save();
  1618. ctx.translate(...pad.pos);
  1619. ctx.rotate(-deg2rad(pad.angle));
  1620. ctx.fillStyle = padHoleColor;
  1621. if (pad.drillshape == "oblong") {
  1622. ctx.fill(getOblongPath(pad.drillsize));
  1623. } else {
  1624. ctx.fill(getCirclePath(pad.drillsize[0] / 2));
  1625. }
  1626. ctx.restore();
  1627. }
  1628. function drawFootprint(ctx, layer, scalefactor, footprint, colors, highlight, outline) {
  1629. if (highlight) {
  1630. // draw bounding box
  1631. if (footprint.layer == layer) {
  1632. ctx.save();
  1633. ctx.globalAlpha = 0.2;
  1634. ctx.translate(...footprint.bbox.pos);
  1635. ctx.rotate(deg2rad(-footprint.bbox.angle));
  1636. ctx.translate(...footprint.bbox.relpos);
  1637. ctx.fillStyle = colors.pad;
  1638. ctx.fillRect(0, 0, ...footprint.bbox.size);
  1639. ctx.globalAlpha = 1;
  1640. ctx.strokeStyle = colors.pad;
  1641. ctx.strokeRect(0, 0, ...footprint.bbox.size);
  1642. ctx.restore();
  1643. }
  1644. }
  1645. // draw drawings
  1646. for (var drawing of footprint.drawings) {
  1647. if (drawing.layer == layer) {
  1648. drawDrawing(ctx, scalefactor, drawing.drawing, colors.pad);
  1649. }
  1650. }
  1651. // draw pads
  1652. if (settings.renderPads) {
  1653. for (var pad of footprint.pads) {
  1654. if (pad.layers.includes(layer)) {
  1655. drawPad(ctx, pad, colors.pad, outline);
  1656. if (pad.pin1 && settings.highlightpin1) {
  1657. drawPad(ctx, pad, colors.outline, true);
  1658. }
  1659. }
  1660. }
  1661. for (var pad of footprint.pads) {
  1662. drawPadHole(ctx, pad, colors.padHole);
  1663. }
  1664. }
  1665. }
  1666. function drawEdgeCuts(canvas, scalefactor) {
  1667. var ctx = canvas.getContext("2d");
  1668. var edgecolor = getComputedStyle(topmostdiv).getPropertyValue('--pcb-edge-color');
  1669. for (var edge of pcbdata.edges) {
  1670. drawDrawing(ctx, scalefactor, edge, edgecolor);
  1671. }
  1672. }
  1673. function drawFootprints(canvas, layer, scalefactor, highlight) {
  1674. var ctx = canvas.getContext("2d");
  1675. ctx.lineWidth = 3 / scalefactor;
  1676. var style = getComputedStyle(topmostdiv);
  1677. var colors = {
  1678. pad: style.getPropertyValue('--pad-color'),
  1679. padHole: style.getPropertyValue('--pad-hole-color'),
  1680. outline: style.getPropertyValue('--pin1-outline-color'),
  1681. }
  1682. for (var i = 0; i < pcbdata.footprints.length; i++) {
  1683. var mod = pcbdata.footprints[i];
  1684. var outline = settings.renderDnpOutline && pcbdata.bom.skipped.includes(i);
  1685. var h = highlightedFootprints.includes(i);
  1686. var d = markedFootprints.has(i);
  1687. if (highlight) {
  1688. if(h && d) {
  1689. colors.pad = style.getPropertyValue('--pad-color-highlight-both');
  1690. colors.outline = style.getPropertyValue('--pin1-outline-color-highlight-both');
  1691. } else if (h) {
  1692. colors.pad = style.getPropertyValue('--pad-color-highlight');
  1693. colors.outline = style.getPropertyValue('--pin1-outline-color-highlight');
  1694. } else if (d) {
  1695. colors.pad = style.getPropertyValue('--pad-color-highlight-marked');
  1696. colors.outline = style.getPropertyValue('--pin1-outline-color-highlight-marked');
  1697. }
  1698. }
  1699. if( h || d || !highlight) {
  1700. drawFootprint(ctx, layer, scalefactor, mod, colors, highlight, outline);
  1701. }
  1702. }
  1703. }
  1704. function drawBgLayer(layername, canvas, layer, scalefactor, edgeColor, polygonColor, textColor) {
  1705. var ctx = canvas.getContext("2d");
  1706. for (var d of pcbdata.drawings[layername][layer]) {
  1707. if (["segment", "arc", "circle", "curve", "rect"].includes(d.type)) {
  1708. drawedge(ctx, scalefactor, d, edgeColor);
  1709. } else if (d.type == "polygon") {
  1710. drawPolygonShape(ctx, scalefactor, d, polygonColor);
  1711. } else {
  1712. drawText(ctx, d, textColor);
  1713. }
  1714. }
  1715. }
  1716. function drawTracks(canvas, layer, color, highlight) {
  1717. ctx = canvas.getContext("2d");
  1718. ctx.strokeStyle = color;
  1719. ctx.lineCap = "round";
  1720. for (var track of pcbdata.tracks[layer]) {
  1721. if (highlight && highlightedNet != track.net) continue;
  1722. ctx.lineWidth = track.width;
  1723. ctx.beginPath();
  1724. if ('radius' in track) {
  1725. ctx.arc(
  1726. ...track.center,
  1727. track.radius,
  1728. deg2rad(track.startangle),
  1729. deg2rad(track.endangle));
  1730. } else {
  1731. ctx.moveTo(...track.start);
  1732. ctx.lineTo(...track.end);
  1733. }
  1734. ctx.stroke();
  1735. }
  1736. }
  1737. function drawZones(canvas, layer, color, highlight) {
  1738. ctx = canvas.getContext("2d");
  1739. ctx.strokeStyle = color;
  1740. ctx.fillStyle = color;
  1741. ctx.lineJoin = "round";
  1742. for (var zone of pcbdata.zones[layer]) {
  1743. if (!zone.path2d) {
  1744. zone.path2d = getPolygonsPath(zone);
  1745. }
  1746. if (highlight && highlightedNet != zone.net) continue;
  1747. ctx.fill(zone.path2d);
  1748. if (zone.width > 0) {
  1749. ctx.lineWidth = zone.width;
  1750. ctx.stroke(zone.path2d);
  1751. }
  1752. }
  1753. }
  1754. function clearCanvas(canvas, color = null) {
  1755. var ctx = canvas.getContext("2d");
  1756. ctx.save();
  1757. ctx.setTransform(1, 0, 0, 1, 0, 0);
  1758. if (color) {
  1759. ctx.fillStyle = color;
  1760. ctx.fillRect(0, 0, canvas.width, canvas.height);
  1761. } else {
  1762. if (!window.matchMedia("print").matches)
  1763. ctx.clearRect(0, 0, canvas.width, canvas.height);
  1764. }
  1765. ctx.restore();
  1766. }
  1767. function drawNets(canvas, layer, highlight) {
  1768. var style = getComputedStyle(topmostdiv);
  1769. if (settings.renderTracks) {
  1770. var trackColor = style.getPropertyValue(highlight ? '--track-color-highlight' : '--track-color');
  1771. drawTracks(canvas, layer, trackColor, highlight);
  1772. }
  1773. if (settings.renderZones) {
  1774. var zoneColor = style.getPropertyValue(highlight ? '--zone-color-highlight' : '--zone-color');
  1775. drawZones(canvas, layer, zoneColor, highlight);
  1776. }
  1777. if (highlight && settings.renderPads) {
  1778. var padColor = style.getPropertyValue('--pad-color-highlight');
  1779. var padHoleColor = style.getPropertyValue('--pad-hole-color');
  1780. var ctx = canvas.getContext("2d");
  1781. for (var footprint of pcbdata.footprints) {
  1782. // draw pads
  1783. var padDrawn = false;
  1784. for (var pad of footprint.pads) {
  1785. if (highlightedNet != pad.net) continue;
  1786. if (pad.layers.includes(layer)) {
  1787. drawPad(ctx, pad, padColor, false);
  1788. padDrawn = true;
  1789. }
  1790. }
  1791. if (padDrawn) {
  1792. // redraw all pad holes because some pads may overlap
  1793. for (var pad of footprint.pads) {
  1794. drawPadHole(ctx, pad, padHoleColor);
  1795. }
  1796. }
  1797. }
  1798. }
  1799. }
  1800. function drawHighlightsOnLayer(canvasdict, clear = true) {
  1801. if (clear) {
  1802. clearCanvas(canvasdict.highlight);
  1803. }
  1804. if (markedFootprints.size > 0 || highlightedFootprints.length > 0) {
  1805. drawFootprints(canvasdict.highlight, canvasdict.layer,
  1806. canvasdict.transform.s * canvasdict.transform.zoom, true);
  1807. }
  1808. if (highlightedNet !== null) {
  1809. drawNets(canvasdict.highlight, canvasdict.layer, true);
  1810. }
  1811. }
  1812. function drawHighlights() {
  1813. drawHighlightsOnLayer(allcanvas.front);
  1814. drawHighlightsOnLayer(allcanvas.back);
  1815. }
  1816. function drawBackground(canvasdict, clear = true) {
  1817. if (clear) {
  1818. clearCanvas(canvasdict.bg);
  1819. clearCanvas(canvasdict.fab);
  1820. clearCanvas(canvasdict.silk);
  1821. }
  1822. drawNets(canvasdict.bg, canvasdict.layer, false);
  1823. drawFootprints(canvasdict.bg, canvasdict.layer,
  1824. canvasdict.transform.s * canvasdict.transform.zoom, false);
  1825. drawEdgeCuts(canvasdict.bg, canvasdict.transform.s * canvasdict.transform.zoom);
  1826. var style = getComputedStyle(topmostdiv);
  1827. var edgeColor = style.getPropertyValue('--silkscreen-edge-color');
  1828. var polygonColor = style.getPropertyValue('--silkscreen-polygon-color');
  1829. var textColor = style.getPropertyValue('--silkscreen-text-color');
  1830. if (settings.renderSilkscreen) {
  1831. drawBgLayer(
  1832. "silkscreen", canvasdict.silk, canvasdict.layer,
  1833. canvasdict.transform.s * canvasdict.transform.zoom,
  1834. edgeColor, polygonColor, textColor);
  1835. }
  1836. edgeColor = style.getPropertyValue('--fabrication-edge-color');
  1837. polygonColor = style.getPropertyValue('--fabrication-polygon-color');
  1838. textColor = style.getPropertyValue('--fabrication-text-color');
  1839. if (settings.renderFabrication) {
  1840. drawBgLayer(
  1841. "fabrication", canvasdict.fab, canvasdict.layer,
  1842. canvasdict.transform.s * canvasdict.transform.zoom,
  1843. edgeColor, polygonColor, textColor);
  1844. }
  1845. }
  1846. function prepareCanvas(canvas, flip, transform) {
  1847. var ctx = canvas.getContext("2d");
  1848. ctx.setTransform(1, 0, 0, 1, 0, 0);
  1849. ctx.scale(transform.zoom, transform.zoom);
  1850. ctx.translate(transform.panx, transform.pany);
  1851. if (flip) {
  1852. ctx.scale(-1, 1);
  1853. }
  1854. ctx.translate(transform.x, transform.y);
  1855. ctx.rotate(deg2rad(settings.boardRotation));
  1856. ctx.scale(transform.s, transform.s);
  1857. }
  1858. function prepareLayer(canvasdict) {
  1859. var flip = (canvasdict.layer == "B");
  1860. for (var c of ["bg", "fab", "silk", "highlight"]) {
  1861. prepareCanvas(canvasdict[c], flip, canvasdict.transform);
  1862. }
  1863. }
  1864. function rotateVector(v, angle) {
  1865. angle = deg2rad(angle);
  1866. return [
  1867. v[0] * Math.cos(angle) - v[1] * Math.sin(angle),
  1868. v[0] * Math.sin(angle) + v[1] * Math.cos(angle)
  1869. ];
  1870. }
  1871. function applyRotation(bbox) {
  1872. var corners = [
  1873. [bbox.minx, bbox.miny],
  1874. [bbox.minx, bbox.maxy],
  1875. [bbox.maxx, bbox.miny],
  1876. [bbox.maxx, bbox.maxy],
  1877. ];
  1878. corners = corners.map((v) => rotateVector(v, settings.boardRotation));
  1879. return {
  1880. minx: corners.reduce((a, v) => Math.min(a, v[0]), Infinity),
  1881. miny: corners.reduce((a, v) => Math.min(a, v[1]), Infinity),
  1882. maxx: corners.reduce((a, v) => Math.max(a, v[0]), -Infinity),
  1883. maxy: corners.reduce((a, v) => Math.max(a, v[1]), -Infinity),
  1884. }
  1885. }
  1886. function recalcLayerScale(layerdict, width, height) {
  1887. var bbox = applyRotation(pcbdata.edges_bbox);
  1888. var scalefactor = 0.98 * Math.min(
  1889. width / (bbox.maxx - bbox.minx),
  1890. height / (bbox.maxy - bbox.miny)
  1891. );
  1892. if (scalefactor < 0.1) {
  1893. scalefactor = 1;
  1894. }
  1895. layerdict.transform.s = scalefactor;
  1896. var flip = (layerdict.layer == "B");
  1897. if (flip) {
  1898. layerdict.transform.x = -((bbox.maxx + bbox.minx) * scalefactor + width) * 0.5;
  1899. } else {
  1900. layerdict.transform.x = -((bbox.maxx + bbox.minx) * scalefactor - width) * 0.5;
  1901. }
  1902. layerdict.transform.y = -((bbox.maxy + bbox.miny) * scalefactor - height) * 0.5;
  1903. for (var c of ["bg", "fab", "silk", "highlight"]) {
  1904. canvas = layerdict[c];
  1905. canvas.width = width;
  1906. canvas.height = height;
  1907. canvas.style.width = (width / devicePixelRatio) + "px";
  1908. canvas.style.height = (height / devicePixelRatio) + "px";
  1909. }
  1910. }
  1911. function redrawCanvas(layerdict) {
  1912. prepareLayer(layerdict);
  1913. drawBackground(layerdict);
  1914. drawHighlightsOnLayer(layerdict);
  1915. }
  1916. function resizeCanvas(layerdict) {
  1917. var canvasdivid = {
  1918. "F": "frontcanvas",
  1919. "B": "backcanvas"
  1920. } [layerdict.layer];
  1921. var width = document.getElementById(canvasdivid).clientWidth * devicePixelRatio;
  1922. var height = document.getElementById(canvasdivid).clientHeight * devicePixelRatio;
  1923. recalcLayerScale(layerdict, width, height);
  1924. redrawCanvas(layerdict);
  1925. }
  1926. function resizeAll() {
  1927. resizeCanvas(allcanvas.front);
  1928. resizeCanvas(allcanvas.back);
  1929. }
  1930. function pointWithinDistanceToSegment(x, y, x1, y1, x2, y2, d) {
  1931. var A = x - x1;
  1932. var B = y - y1;
  1933. var C = x2 - x1;
  1934. var D = y2 - y1;
  1935. var dot = A * C + B * D;
  1936. var len_sq = C * C + D * D;
  1937. var dx, dy;
  1938. if (len_sq == 0) {
  1939. // start and end of the segment coincide
  1940. dx = x - x1;
  1941. dy = y - y1;
  1942. } else {
  1943. var param = dot / len_sq;
  1944. var xx, yy;
  1945. if (param < 0) {
  1946. xx = x1;
  1947. yy = y1;
  1948. } else if (param > 1) {
  1949. xx = x2;
  1950. yy = y2;
  1951. } else {
  1952. xx = x1 + param * C;
  1953. yy = y1 + param * D;
  1954. }
  1955. dx = x - xx;
  1956. dy = y - yy;
  1957. }
  1958. return dx * dx + dy * dy <= d * d;
  1959. }
  1960. function modulo(n, mod) {
  1961. return ((n % mod) + mod) % mod;
  1962. }
  1963. function pointWithinDistanceToArc(x, y, xc, yc, radius, startangle, endangle, d) {
  1964. var dx = x - xc;
  1965. var dy = y - yc;
  1966. var r_sq = dx * dx + dy * dy;
  1967. var rmin = Math.max(0, radius - d);
  1968. var rmax = radius + d;
  1969. if (r_sq < rmin * rmin || r_sq > rmax * rmax)
  1970. return false;
  1971. var angle1 = modulo(deg2rad(startangle), 2 * Math.PI);
  1972. var dx1 = xc + radius * Math.cos(angle1) - x;
  1973. var dy1 = yc + radius * Math.sin(angle1) - y;
  1974. if (dx1 * dx1 + dy1 * dy1 <= d * d)
  1975. return true;
  1976. var angle2 = modulo(deg2rad(endangle), 2 * Math.PI);
  1977. var dx2 = xc + radius * Math.cos(angle2) - x;
  1978. var dy2 = yc + radius * Math.sin(angle2) - y;
  1979. if (dx2 * dx2 + dy2 * dy2 <= d * d)
  1980. return true;
  1981. var angle = modulo(Math.atan2(dy, dx), 2 * Math.PI);
  1982. if (angle1 > angle2)
  1983. return (angle >= angle2 || angle <= angle1);
  1984. else
  1985. return (angle >= angle1 && angle <= angle2);
  1986. }
  1987. function pointWithinPad(x, y, pad) {
  1988. var v = [x - pad.pos[0], y - pad.pos[1]];
  1989. v = rotateVector(v, pad.angle);
  1990. if (pad.offset) {
  1991. v[0] -= pad.offset[0];
  1992. v[1] -= pad.offset[1];
  1993. }
  1994. return emptyContext2d.isPointInPath(getCachedPadPath(pad), ...v);
  1995. }
  1996. function netHitScan(layer, x, y) {
  1997. // Check track segments
  1998. if (settings.renderTracks && pcbdata.tracks) {
  1999. for (var track of pcbdata.tracks[layer]) {
  2000. if ('radius' in track) {
  2001. if (pointWithinDistanceToArc(x, y, ...track.center, track.radius, track.startangle, track.endangle, track.width / 2)) {
  2002. return track.net;
  2003. }
  2004. } else {
  2005. if (pointWithinDistanceToSegment(x, y, ...track.start, ...track.end, track.width / 2)) {
  2006. return track.net;
  2007. }
  2008. }
  2009. }
  2010. }
  2011. // Check pads
  2012. if (settings.renderPads) {
  2013. for (var footprint of pcbdata.footprints) {
  2014. for (var pad of footprint.pads) {
  2015. if (pad.layers.includes(layer) && pointWithinPad(x, y, pad)) {
  2016. return pad.net;
  2017. }
  2018. }
  2019. }
  2020. }
  2021. return null;
  2022. }
  2023. function pointWithinFootprintBbox(x, y, bbox) {
  2024. var v = [x - bbox.pos[0], y - bbox.pos[1]];
  2025. v = rotateVector(v, bbox.angle);
  2026. return bbox.relpos[0] <= v[0] && v[0] <= bbox.relpos[0] + bbox.size[0] &&
  2027. bbox.relpos[1] <= v[1] && v[1] <= bbox.relpos[1] + bbox.size[1];
  2028. }
  2029. function bboxHitScan(layer, x, y) {
  2030. var result = [];
  2031. for (var i = 0; i < pcbdata.footprints.length; i++) {
  2032. var footprint = pcbdata.footprints[i];
  2033. if (footprint.layer == layer) {
  2034. if (pointWithinFootprintBbox(x, y, footprint.bbox)) {
  2035. result.push(i);
  2036. }
  2037. }
  2038. }
  2039. return result;
  2040. }
  2041. function handlePointerDown(e, layerdict) {
  2042. if (e.button != 0 && e.button != 1) {
  2043. return;
  2044. }
  2045. e.preventDefault();
  2046. e.stopPropagation();
  2047. if (!e.hasOwnProperty("offsetX")) {
  2048. // The polyfill doesn't set this properly
  2049. e.offsetX = e.pageX - e.currentTarget.offsetLeft;
  2050. e.offsetY = e.pageY - e.currentTarget.offsetTop;
  2051. }
  2052. layerdict.pointerStates[e.pointerId] = {
  2053. distanceTravelled: 0,
  2054. lastX: e.offsetX,
  2055. lastY: e.offsetY,
  2056. downTime: Date.now(),
  2057. };
  2058. }
  2059. function handleMouseClick(e, layerdict) {
  2060. if (!e.hasOwnProperty("offsetX")) {
  2061. // The polyfill doesn't set this properly
  2062. e.offsetX = e.pageX - e.currentTarget.offsetLeft;
  2063. e.offsetY = e.pageY - e.currentTarget.offsetTop;
  2064. }
  2065. var x = e.offsetX;
  2066. var y = e.offsetY;
  2067. var t = layerdict.transform;
  2068. if (layerdict.layer == "B") {
  2069. x = (devicePixelRatio * x / t.zoom - t.panx + t.x) / -t.s;
  2070. } else {
  2071. x = (devicePixelRatio * x / t.zoom - t.panx - t.x) / t.s;
  2072. }
  2073. y = (devicePixelRatio * y / t.zoom - t.y - t.pany) / t.s;
  2074. var v = rotateVector([x, y], -settings.boardRotation);
  2075. if ("nets" in pcbdata) {
  2076. var net = netHitScan(layerdict.layer, ...v);
  2077. if (net !== highlightedNet) {
  2078. netClicked(net);
  2079. }
  2080. }
  2081. if (highlightedNet === null) {
  2082. var footprints = bboxHitScan(layerdict.layer, ...v);
  2083. if (footprints.length > 0) {
  2084. footprintsClicked(footprints);
  2085. }
  2086. }
  2087. }
  2088. function handlePointerLeave(e, layerdict) {
  2089. e.preventDefault();
  2090. e.stopPropagation();
  2091. if (!settings.redrawOnDrag) {
  2092. redrawCanvas(layerdict);
  2093. }
  2094. delete layerdict.pointerStates[e.pointerId];
  2095. }
  2096. function resetTransform(layerdict) {
  2097. layerdict.transform.panx = 0;
  2098. layerdict.transform.pany = 0;
  2099. layerdict.transform.zoom = 1;
  2100. redrawCanvas(layerdict);
  2101. }
  2102. function handlePointerUp(e, layerdict) {
  2103. if (!e.hasOwnProperty("offsetX")) {
  2104. // The polyfill doesn't set this properly
  2105. e.offsetX = e.pageX - e.currentTarget.offsetLeft;
  2106. e.offsetY = e.pageY - e.currentTarget.offsetTop;
  2107. }
  2108. e.preventDefault();
  2109. e.stopPropagation();
  2110. if (e.button == 2) {
  2111. // Reset pan and zoom on right click.
  2112. resetTransform(layerdict);
  2113. layerdict.anotherPointerTapped = false;
  2114. return;
  2115. }
  2116. // We haven't necessarily had a pointermove event since the interaction started, so make sure we update this now
  2117. var ptr = layerdict.pointerStates[e.pointerId];
  2118. ptr.distanceTravelled += Math.abs(e.offsetX - ptr.lastX) + Math.abs(e.offsetY - ptr.lastY);
  2119. if (e.button == 0 && ptr.distanceTravelled < 10 && Date.now() - ptr.downTime <= 500) {
  2120. if (Object.keys(layerdict.pointerStates).length == 1) {
  2121. if (layerdict.anotherPointerTapped) {
  2122. // This is the second pointer coming off of a two-finger tap
  2123. resetTransform(layerdict);
  2124. } else {
  2125. // This is just a regular tap
  2126. handleMouseClick(e, layerdict);
  2127. }
  2128. layerdict.anotherPointerTapped = false;
  2129. } else {
  2130. // This is the first finger coming off of what could become a two-finger tap
  2131. layerdict.anotherPointerTapped = true;
  2132. }
  2133. } else {
  2134. if (!settings.redrawOnDrag) {
  2135. redrawCanvas(layerdict);
  2136. }
  2137. layerdict.anotherPointerTapped = false;
  2138. }
  2139. delete layerdict.pointerStates[e.pointerId];
  2140. }
  2141. function handlePointerMove(e, layerdict) {
  2142. if (!layerdict.pointerStates.hasOwnProperty(e.pointerId)) {
  2143. return;
  2144. }
  2145. e.preventDefault();
  2146. e.stopPropagation();
  2147. if (!e.hasOwnProperty("offsetX")) {
  2148. // The polyfill doesn't set this properly
  2149. e.offsetX = e.pageX - e.currentTarget.offsetLeft;
  2150. e.offsetY = e.pageY - e.currentTarget.offsetTop;
  2151. }
  2152. var thisPtr = layerdict.pointerStates[e.pointerId];
  2153. var dx = e.offsetX - thisPtr.lastX;
  2154. var dy = e.offsetY - thisPtr.lastY;
  2155. // If this number is low on pointer up, we count the action as a click
  2156. thisPtr.distanceTravelled += Math.abs(dx) + Math.abs(dy);
  2157. if (Object.keys(layerdict.pointerStates).length == 1) {
  2158. // This is a simple drag
  2159. layerdict.transform.panx += devicePixelRatio * dx / layerdict.transform.zoom;
  2160. layerdict.transform.pany += devicePixelRatio * dy / layerdict.transform.zoom;
  2161. } else if (Object.keys(layerdict.pointerStates).length == 2) {
  2162. var otherPtr = Object.values(layerdict.pointerStates).filter((ptr) => ptr != thisPtr)[0];
  2163. var oldDist = Math.sqrt(Math.pow(thisPtr.lastX - otherPtr.lastX, 2) + Math.pow(thisPtr.lastY - otherPtr.lastY, 2));
  2164. var newDist = Math.sqrt(Math.pow(e.offsetX - otherPtr.lastX, 2) + Math.pow(e.offsetY - otherPtr.lastY, 2));
  2165. var scaleFactor = newDist / oldDist;
  2166. if (scaleFactor != NaN) {
  2167. layerdict.transform.zoom *= scaleFactor;
  2168. var zoomd = (1 - scaleFactor) / layerdict.transform.zoom;
  2169. layerdict.transform.panx += devicePixelRatio * otherPtr.lastX * zoomd;
  2170. layerdict.transform.pany += devicePixelRatio * otherPtr.lastY * zoomd;
  2171. }
  2172. }
  2173. thisPtr.lastX = e.offsetX;
  2174. thisPtr.lastY = e.offsetY;
  2175. if (settings.redrawOnDrag) {
  2176. redrawCanvas(layerdict);
  2177. }
  2178. }
  2179. function handleMouseWheel(e, layerdict) {
  2180. e.preventDefault();
  2181. e.stopPropagation();
  2182. var t = layerdict.transform;
  2183. var wheeldelta = e.deltaY;
  2184. if (e.deltaMode == 1) {
  2185. // FF only, scroll by lines
  2186. wheeldelta *= 30;
  2187. } else if (e.deltaMode == 2) {
  2188. wheeldelta *= 300;
  2189. }
  2190. var m = Math.pow(1.1, -wheeldelta / 40);
  2191. // Limit amount of zoom per tick.
  2192. if (m > 2) {
  2193. m = 2;
  2194. } else if (m < 0.5) {
  2195. m = 0.5;
  2196. }
  2197. t.zoom *= m;
  2198. var zoomd = (1 - m) / t.zoom;
  2199. t.panx += devicePixelRatio * e.offsetX * zoomd;
  2200. t.pany += devicePixelRatio * e.offsetY * zoomd;
  2201. redrawCanvas(layerdict);
  2202. }
  2203. function addMouseHandlers(div, layerdict) {
  2204. div.addEventListener("pointerdown", function(e) {
  2205. handlePointerDown(e, layerdict);
  2206. });
  2207. div.addEventListener("pointermove", function(e) {
  2208. handlePointerMove(e, layerdict);
  2209. });
  2210. div.addEventListener("pointerup", function(e) {
  2211. handlePointerUp(e, layerdict);
  2212. });
  2213. var pointerleave = function(e) {
  2214. handlePointerLeave(e, layerdict);
  2215. }
  2216. div.addEventListener("pointercancel", pointerleave);
  2217. div.addEventListener("pointerleave", pointerleave);
  2218. div.addEventListener("pointerout", pointerleave);
  2219. div.onwheel = function(e) {
  2220. handleMouseWheel(e, layerdict);
  2221. }
  2222. for (var element of [div, layerdict.bg, layerdict.fab, layerdict.silk, layerdict.highlight]) {
  2223. element.addEventListener("contextmenu", function(e) {
  2224. e.preventDefault();
  2225. }, false);
  2226. }
  2227. }
  2228. function setRedrawOnDrag(value) {
  2229. settings.redrawOnDrag = value;
  2230. writeStorage("redrawOnDrag", value);
  2231. }
  2232. function setBoardRotation(value) {
  2233. settings.boardRotation = value * 5;
  2234. writeStorage("boardRotation", settings.boardRotation);
  2235. document.getElementById("rotationDegree").textContent = settings.boardRotation;
  2236. resizeAll();
  2237. }
  2238. function initRender() {
  2239. allcanvas = {
  2240. front: {
  2241. transform: {
  2242. x: 0,
  2243. y: 0,
  2244. s: 1,
  2245. panx: 0,
  2246. pany: 0,
  2247. zoom: 1,
  2248. },
  2249. pointerStates: {},
  2250. anotherPointerTapped: false,
  2251. bg: document.getElementById("F_bg"),
  2252. fab: document.getElementById("F_fab"),
  2253. silk: document.getElementById("F_slk"),
  2254. highlight: document.getElementById("F_hl"),
  2255. layer: "F",
  2256. },
  2257. back: {
  2258. transform: {
  2259. x: 0,
  2260. y: 0,
  2261. s: 1,
  2262. panx: 0,
  2263. pany: 0,
  2264. zoom: 1,
  2265. },
  2266. pointerStates: {},
  2267. anotherPointerTapped: false,
  2268. bg: document.getElementById("B_bg"),
  2269. fab: document.getElementById("B_fab"),
  2270. silk: document.getElementById("B_slk"),
  2271. highlight: document.getElementById("B_hl"),
  2272. layer: "B",
  2273. }
  2274. };
  2275. addMouseHandlers(document.getElementById("frontcanvas"), allcanvas.front);
  2276. addMouseHandlers(document.getElementById("backcanvas"), allcanvas.back);
  2277. }
  2278. ///////////////////////////////////////////////
  2279. ///////////////////////////////////////////////
  2280. /*
  2281. * Table reordering via Drag'n'Drop
  2282. * Inspired by: https://htmldom.dev/drag-and-drop-table-column
  2283. */
  2284. function setBomHandlers() {
  2285. const bom = document.getElementById('bomtable');
  2286. let dragName;
  2287. let placeHolderElements;
  2288. let draggingElement;
  2289. let forcePopulation;
  2290. let xOffset;
  2291. let yOffset;
  2292. let wasDragged;
  2293. const mouseUpHandler = function(e) {
  2294. // Delete dragging element
  2295. draggingElement.remove();
  2296. // Make BOM selectable again
  2297. bom.style.removeProperty("userSelect");
  2298. // Remove listeners
  2299. document.removeEventListener('mousemove', mouseMoveHandler);
  2300. document.removeEventListener('mouseup', mouseUpHandler);
  2301. if (wasDragged) {
  2302. // Redraw whole BOM
  2303. populateBomTable();
  2304. }
  2305. }
  2306. const mouseMoveHandler = function(e) {
  2307. // Notice the dragging
  2308. wasDragged = true;
  2309. // Make the dragged element visible
  2310. draggingElement.style.removeProperty("display");
  2311. // Set elements position to mouse position
  2312. draggingElement.style.left = `${e.screenX - xOffset}px`;
  2313. draggingElement.style.top = `${e.screenY - yOffset}px`;
  2314. // Forced redrawing of BOM table
  2315. if (forcePopulation) {
  2316. forcePopulation = false;
  2317. // Copy array
  2318. phe = Array.from(placeHolderElements);
  2319. // populate BOM table again
  2320. populateBomHeader(dragName, phe);
  2321. populateBomBody(dragName, phe);
  2322. }
  2323. // Set up array of hidden columns
  2324. var hiddenColumns = Array.from(settings.hiddenColumns);
  2325. // In the ungrouped mode, quantity don't exist
  2326. if (settings.bommode === "ungrouped")
  2327. hiddenColumns.push("Quantity");
  2328. // If no checkbox fields can be found, we consider them hidden
  2329. if (settings.checkboxes.length == 0)
  2330. hiddenColumns.push("checkboxes");
  2331. // Get table headers and group them into checkboxes, extrafields and normal headers
  2332. const bh = document.getElementById("bomhead");
  2333. headers = Array.from(bh.querySelectorAll("th"))
  2334. headers.shift() // numCol is not part of the columnOrder
  2335. headerGroups = []
  2336. lastCompoundClass = null;
  2337. for (i = 0; i < settings.columnOrder.length; i++) {
  2338. cElem = settings.columnOrder[i];
  2339. if (hiddenColumns.includes(cElem)) {
  2340. // Hidden columns appear as a dummy element
  2341. headerGroups.push([]);
  2342. continue;
  2343. }
  2344. elem = headers.filter(e => getColumnOrderName(e) === cElem)[0];
  2345. if (elem.classList.contains("bom-checkbox")) {
  2346. if (lastCompoundClass === "bom-checkbox") {
  2347. cbGroup = headerGroups.pop();
  2348. cbGroup.push(elem);
  2349. headerGroups.push(cbGroup);
  2350. } else {
  2351. lastCompoundClass = "bom-checkbox";
  2352. headerGroups.push([elem])
  2353. }
  2354. } else {
  2355. headerGroups.push([elem])
  2356. }
  2357. }
  2358. // Copy settings.columnOrder
  2359. var columns = Array.from(settings.columnOrder)
  2360. // Set up array with indices of hidden columns
  2361. var hiddenIndices = hiddenColumns.map(e => settings.columnOrder.indexOf(e));
  2362. var dragIndex = columns.indexOf(dragName);
  2363. var swapIndex = dragIndex;
  2364. var swapDone = false;
  2365. // Check if the current dragged element is swapable with the left or right element
  2366. if (dragIndex > 0) {
  2367. // Get left headers boundingbox
  2368. swapIndex = dragIndex - 1;
  2369. while (hiddenIndices.includes(swapIndex) && swapIndex > 0)
  2370. swapIndex--;
  2371. if (!hiddenIndices.includes(swapIndex)) {
  2372. box = getBoundingClientRectFromMultiple(headerGroups[swapIndex]);
  2373. if (e.clientX < box.left + window.scrollX + (box.width / 2)) {
  2374. swapElement = columns[dragIndex];
  2375. columns.splice(dragIndex, 1);
  2376. columns.splice(swapIndex, 0, swapElement);
  2377. forcePopulation = true;
  2378. swapDone = true;
  2379. }
  2380. }
  2381. }
  2382. if ((!swapDone) && dragIndex < headerGroups.length - 1) {
  2383. // Get right headers boundingbox
  2384. swapIndex = dragIndex + 1;
  2385. while (hiddenIndices.includes(swapIndex))
  2386. swapIndex++;
  2387. if (swapIndex < headerGroups.length) {
  2388. box = getBoundingClientRectFromMultiple(headerGroups[swapIndex]);
  2389. if (e.clientX > box.left + window.scrollX + (box.width / 2)) {
  2390. swapElement = columns[dragIndex];
  2391. columns.splice(dragIndex, 1);
  2392. columns.splice(swapIndex, 0, swapElement);
  2393. forcePopulation = true;
  2394. swapDone = true;
  2395. }
  2396. }
  2397. }
  2398. // Write back change to storage
  2399. if (swapDone) {
  2400. settings.columnOrder = columns
  2401. writeStorage("columnOrder", JSON.stringify(columns));
  2402. }
  2403. }
  2404. const mouseDownHandler = function(e) {
  2405. var target = e.target;
  2406. if (target.tagName.toLowerCase() != "td")
  2407. target = target.parentElement;
  2408. // Used to check if a dragging has ever happened
  2409. wasDragged = false;
  2410. // Create new element which will be displayed as the dragged column
  2411. draggingElement = document.createElement("div")
  2412. draggingElement.classList.add("dragging");
  2413. draggingElement.style.display = "none";
  2414. draggingElement.style.position = "absolute";
  2415. draggingElement.style.overflow = "hidden";
  2416. // Get bomhead and bombody elements
  2417. const bh = document.getElementById("bomhead");
  2418. const bb = document.getElementById("bombody");
  2419. // Get all compound headers for the current column
  2420. var compoundHeaders;
  2421. if (target.classList.contains("bom-checkbox")) {
  2422. compoundHeaders = Array.from(bh.querySelectorAll("th.bom-checkbox"));
  2423. } else {
  2424. compoundHeaders = [target];
  2425. }
  2426. // Create new table which will display the column
  2427. var newTable = document.createElement("table");
  2428. newTable.classList.add("bom");
  2429. newTable.style.background = "white";
  2430. draggingElement.append(newTable);
  2431. // Create new header element
  2432. var newHeader = document.createElement("thead");
  2433. newTable.append(newHeader);
  2434. // Set up array for storing all placeholder elements
  2435. placeHolderElements = [];
  2436. // Add all compound headers to the new thead element and placeholders
  2437. compoundHeaders.forEach(function(h) {
  2438. clone = cloneElementWithDimensions(h);
  2439. newHeader.append(clone);
  2440. placeHolderElements.push(clone);
  2441. });
  2442. // Create new body element
  2443. var newBody = document.createElement("tbody");
  2444. newTable.append(newBody);
  2445. // Get indices for compound headers
  2446. var idxs = compoundHeaders.map(e => getBomTableHeaderIndex(e));
  2447. // For each row in the BOM body...
  2448. var rows = bb.querySelectorAll("tr");
  2449. rows.forEach(function(row) {
  2450. // ..get the cells for the compound column
  2451. const tds = row.querySelectorAll("td");
  2452. var copytds = idxs.map(i => tds[i]);
  2453. // Add them to the new element and the placeholders
  2454. var newRow = document.createElement("tr");
  2455. copytds.forEach(function(td) {
  2456. clone = cloneElementWithDimensions(td);
  2457. newRow.append(clone);
  2458. placeHolderElements.push(clone);
  2459. });
  2460. newBody.append(newRow);
  2461. });
  2462. // Compute width for compound header
  2463. var width = compoundHeaders.reduce((acc, x) => acc + x.clientWidth, 0);
  2464. draggingElement.style.width = `${width}px`;
  2465. // Insert the new dragging element and disable selection on BOM
  2466. bom.insertBefore(draggingElement, null);
  2467. bom.style.userSelect = "none";
  2468. // Determine the mouse position offset
  2469. xOffset = e.screenX - compoundHeaders.reduce((acc, x) => Math.min(acc, x.offsetLeft), compoundHeaders[0].offsetLeft);
  2470. yOffset = e.screenY - compoundHeaders[0].offsetTop;
  2471. // Get name for the column in settings.columnOrder
  2472. dragName = getColumnOrderName(target);
  2473. // Change text and class for placeholder elements
  2474. placeHolderElements = placeHolderElements.map(function(e) {
  2475. newElem = cloneElementWithDimensions(e);
  2476. newElem.textContent = "";
  2477. newElem.classList.add("placeholder");
  2478. return newElem;
  2479. });
  2480. // On next mouse move, the whole BOM needs to be redrawn to show the placeholders
  2481. forcePopulation = true;
  2482. // Add listeners for move and up on mouse
  2483. document.addEventListener('mousemove', mouseMoveHandler);
  2484. document.addEventListener('mouseup', mouseUpHandler);
  2485. }
  2486. // In netlist mode, there is nothing to reorder
  2487. if (settings.bommode === "netlist")
  2488. return;
  2489. // Add mouseDownHandler to every column except the numCol
  2490. bom.querySelectorAll("th")
  2491. .forEach(function(head) {
  2492. if (!head.classList.contains("numCol")) {
  2493. head.onmousedown = mouseDownHandler;
  2494. }
  2495. });
  2496. }
  2497. function getBoundingClientRectFromMultiple(elements) {
  2498. var elems = Array.from(elements);
  2499. if (elems.length == 0)
  2500. return null;
  2501. var box = elems.shift()
  2502. .getBoundingClientRect();
  2503. elems.forEach(function(elem) {
  2504. var elembox = elem.getBoundingClientRect();
  2505. box.left = Math.min(elembox.left, box.left);
  2506. box.top = Math.min(elembox.top, box.top);
  2507. box.width += elembox.width;
  2508. box.height = Math.max(elembox.height, box.height);
  2509. });
  2510. return box;
  2511. }
  2512. function cloneElementWithDimensions(elem) {
  2513. var newElem = elem.cloneNode(true);
  2514. newElem.style.height = window.getComputedStyle(elem).height;
  2515. newElem.style.width = window.getComputedStyle(elem).width;
  2516. return newElem;
  2517. }
  2518. function getBomTableHeaderIndex(elem) {
  2519. const bh = document.getElementById('bomhead');
  2520. const ths = Array.from(bh.querySelectorAll("th"));
  2521. return ths.indexOf(elem);
  2522. }
  2523. function getColumnOrderName(elem) {
  2524. var cname = elem.getAttribute("col_name");
  2525. if (cname === "bom-checkbox")
  2526. return "checkboxes";
  2527. else
  2528. return cname;
  2529. }
  2530. function resizableGrid(tablehead) {
  2531. var cols = tablehead.firstElementChild.children;
  2532. var rowWidth = tablehead.offsetWidth;
  2533. for (var i = 1; i < cols.length; i++) {
  2534. if (cols[i].classList.contains("bom-checkbox"))
  2535. continue;
  2536. cols[i].style.width = ((cols[i].clientWidth - paddingDiff(cols[i])) * 100 / rowWidth) + '%';
  2537. }
  2538. for (var i = 1; i < cols.length - 1; i++) {
  2539. var div = document.createElement('div');
  2540. div.className = "column-width-handle";
  2541. cols[i].appendChild(div);
  2542. setListeners(div);
  2543. }
  2544. function setListeners(div) {
  2545. var startX, curCol, nxtCol, curColWidth, nxtColWidth, rowWidth;
  2546. div.addEventListener('mousedown', function(e) {
  2547. e.preventDefault();
  2548. e.stopPropagation();
  2549. curCol = e.target.parentElement;
  2550. nxtCol = curCol.nextElementSibling;
  2551. startX = e.pageX;
  2552. var padding = paddingDiff(curCol);
  2553. rowWidth = curCol.parentElement.offsetWidth;
  2554. curColWidth = curCol.clientWidth - padding;
  2555. nxtColWidth = nxtCol.clientWidth - padding;
  2556. });
  2557. document.addEventListener('mousemove', function(e) {
  2558. if (startX) {
  2559. var diffX = e.pageX - startX;
  2560. diffX = -Math.min(-diffX, curColWidth - 20);
  2561. diffX = Math.min(diffX, nxtColWidth - 20);
  2562. curCol.style.width = ((curColWidth + diffX) * 100 / rowWidth) + '%';
  2563. nxtCol.style.width = ((nxtColWidth - diffX) * 100 / rowWidth) + '%';
  2564. console.log(`${curColWidth + nxtColWidth} ${(curColWidth + diffX) * 100 / rowWidth + (nxtColWidth - diffX) * 100 / rowWidth}`);
  2565. }
  2566. });
  2567. document.addEventListener('mouseup', function(e) {
  2568. curCol = undefined;
  2569. nxtCol = undefined;
  2570. startX = undefined;
  2571. nxtColWidth = undefined;
  2572. curColWidth = undefined
  2573. });
  2574. }
  2575. function paddingDiff(col) {
  2576. if (getStyleVal(col, 'box-sizing') == 'border-box') {
  2577. return 0;
  2578. }
  2579. var padLeft = getStyleVal(col, 'padding-left');
  2580. var padRight = getStyleVal(col, 'padding-right');
  2581. return (parseInt(padLeft) + parseInt(padRight));
  2582. }
  2583. function getStyleVal(elm, css) {
  2584. return (window.getComputedStyle(elm, null).getPropertyValue(css))
  2585. }
  2586. }
  2587. ///////////////////////////////////////////////
  2588. ///////////////////////////////////////////////
  2589. /* DOM manipulation and misc code */
  2590. var bomsplit;
  2591. var canvassplit;
  2592. var initDone = false;
  2593. var bomSortFunction = null;
  2594. var currentSortColumn = null;
  2595. var currentSortOrder = null;
  2596. var currentHighlightedRowId;
  2597. var highlightHandlers = [];
  2598. var footprintIndexToHandler = {};
  2599. var netsToHandler = {};
  2600. var markedFootprints = new Set();
  2601. var highlightedFootprints = [];
  2602. var highlightedNet = null;
  2603. var lastClicked;
  2604. function dbg(html) {
  2605. dbgdiv.innerHTML = html;
  2606. }
  2607. function redrawIfInitDone() {
  2608. if (initDone) {
  2609. redrawCanvas(allcanvas.front);
  2610. redrawCanvas(allcanvas.back);
  2611. }
  2612. }
  2613. function padsVisible(value) {
  2614. writeStorage("padsVisible", value);
  2615. settings.renderPads = value;
  2616. redrawIfInitDone();
  2617. }
  2618. function referencesVisible(value) {
  2619. writeStorage("referencesVisible", value);
  2620. settings.renderReferences = value;
  2621. redrawIfInitDone();
  2622. }
  2623. function valuesVisible(value) {
  2624. writeStorage("valuesVisible", value);
  2625. settings.renderValues = value;
  2626. redrawIfInitDone();
  2627. }
  2628. function tracksVisible(value) {
  2629. writeStorage("tracksVisible", value);
  2630. settings.renderTracks = value;
  2631. redrawIfInitDone();
  2632. }
  2633. function zonesVisible(value) {
  2634. writeStorage("zonesVisible", value);
  2635. settings.renderZones = value;
  2636. redrawIfInitDone();
  2637. }
  2638. function dnpOutline(value) {
  2639. writeStorage("dnpOutline", value);
  2640. settings.renderDnpOutline = value;
  2641. redrawIfInitDone();
  2642. }
  2643. function setDarkMode(value) {
  2644. if (value) {
  2645. topmostdiv.classList.add("dark");
  2646. } else {
  2647. topmostdiv.classList.remove("dark");
  2648. }
  2649. writeStorage("darkmode", value);
  2650. settings.darkMode = value;
  2651. redrawIfInitDone();
  2652. }
  2653. function setShowBOMColumn(field, value) {
  2654. if (field === "references") {
  2655. var rl = document.getElementById("reflookup");
  2656. rl.disabled = !value;
  2657. if (!value) {
  2658. rl.value = "";
  2659. updateRefLookup("");
  2660. }
  2661. }
  2662. var n = settings.hiddenColumns.indexOf(field);
  2663. if (value) {
  2664. if (n != -1) {
  2665. settings.hiddenColumns.splice(n, 1);
  2666. }
  2667. } else {
  2668. if (n == -1) {
  2669. settings.hiddenColumns.push(field);
  2670. }
  2671. }
  2672. writeStorage("hiddenColumns", JSON.stringify(settings.hiddenColumns));
  2673. if (initDone) {
  2674. populateBomTable();
  2675. }
  2676. redrawIfInitDone();
  2677. }
  2678. function setFullscreen(value) {
  2679. if (value) {
  2680. document.documentElement.requestFullscreen();
  2681. } else {
  2682. document.exitFullscreen();
  2683. }
  2684. }
  2685. function fabricationVisible(value) {
  2686. writeStorage("fabricationVisible", value);
  2687. settings.renderFabrication = value;
  2688. redrawIfInitDone();
  2689. }
  2690. function silkscreenVisible(value) {
  2691. writeStorage("silkscreenVisible", value);
  2692. settings.renderSilkscreen = value;
  2693. redrawIfInitDone();
  2694. }
  2695. function setHighlightPin1(value) {
  2696. writeStorage("highlightpin1", value);
  2697. settings.highlightpin1 = value;
  2698. redrawIfInitDone();
  2699. }
  2700. function getStoredCheckboxRefs(checkbox) {
  2701. function convert(ref) {
  2702. var intref = parseInt(ref);
  2703. if (isNaN(intref)) {
  2704. for (var i = 0; i < pcbdata.footprints.length; i++) {
  2705. if (pcbdata.footprints[i].ref == ref) {
  2706. return i;
  2707. }
  2708. }
  2709. return -1;
  2710. } else {
  2711. return intref;
  2712. }
  2713. }
  2714. if (!(checkbox in settings.checkboxStoredRefs)) {
  2715. var val = readStorage("checkbox_" + checkbox);
  2716. settings.checkboxStoredRefs[checkbox] = val ? val : "";
  2717. }
  2718. if (!settings.checkboxStoredRefs[checkbox]) {
  2719. return new Set();
  2720. } else {
  2721. return new Set(settings.checkboxStoredRefs[checkbox].split(",").map(r => convert(r)).filter(a => a >= 0));
  2722. }
  2723. }
  2724. function getCheckboxState(checkbox, references) {
  2725. var storedRefsSet = getStoredCheckboxRefs(checkbox);
  2726. var currentRefsSet = new Set(references.map(r => r[1]));
  2727. // Get difference of current - stored
  2728. var difference = new Set(currentRefsSet);
  2729. for (ref of storedRefsSet) {
  2730. difference.delete(ref);
  2731. }
  2732. if (difference.size == 0) {
  2733. // All the current refs are stored
  2734. return "checked";
  2735. } else if (difference.size == currentRefsSet.size) {
  2736. // None of the current refs are stored
  2737. return "unchecked";
  2738. } else {
  2739. // Some of the refs are stored
  2740. return "indeterminate";
  2741. }
  2742. }
  2743. function setBomCheckboxState(checkbox, element, references) {
  2744. var state = getCheckboxState(checkbox, references);
  2745. element.checked = (state == "checked");
  2746. element.indeterminate = (state == "indeterminate");
  2747. }
  2748. function createCheckboxChangeHandler(checkbox, references, row) {
  2749. return function () {
  2750. refsSet = getStoredCheckboxRefs(checkbox);
  2751. var markWhenChecked = settings.markWhenChecked == checkbox;
  2752. eventArgs = {
  2753. checkbox: checkbox,
  2754. refs: references,
  2755. }
  2756. if (this.checked) {
  2757. // checkbox ticked
  2758. for (var ref of references) {
  2759. refsSet.add(ref[1]);
  2760. }
  2761. if (markWhenChecked) {
  2762. row.classList.add("checked");
  2763. for (var ref of references) {
  2764. markedFootprints.add(ref[1]);
  2765. }
  2766. drawHighlights();
  2767. }
  2768. eventArgs.state = 'checked';
  2769. } else {
  2770. // checkbox unticked
  2771. for (var ref of references) {
  2772. refsSet.delete(ref[1]);
  2773. }
  2774. if (markWhenChecked) {
  2775. row.classList.remove("checked");
  2776. for (var ref of references) {
  2777. markedFootprints.delete(ref[1]);
  2778. }
  2779. drawHighlights();
  2780. }
  2781. eventArgs.state = 'unchecked';
  2782. }
  2783. settings.checkboxStoredRefs[checkbox] = [...refsSet].join(",");
  2784. writeStorage("checkbox_" + checkbox, settings.checkboxStoredRefs[checkbox]);
  2785. updateCheckboxStats(checkbox);
  2786. EventHandler.emitEvent(IBOM_EVENT_TYPES.CHECKBOX_CHANGE_EVENT, eventArgs);
  2787. }
  2788. }
  2789. function clearHighlightedFootprints() {
  2790. if (currentHighlightedRowId) {
  2791. document.getElementById(currentHighlightedRowId).classList.remove("highlighted");
  2792. currentHighlightedRowId = null;
  2793. highlightedFootprints = [];
  2794. highlightedNet = null;
  2795. }
  2796. }
  2797. function createRowHighlightHandler(rowid, refs, net) {
  2798. return function () {
  2799. if (currentHighlightedRowId) {
  2800. if (currentHighlightedRowId == rowid) {
  2801. return;
  2802. }
  2803. document.getElementById(currentHighlightedRowId).classList.remove("highlighted");
  2804. }
  2805. document.getElementById(rowid).classList.add("highlighted");
  2806. currentHighlightedRowId = rowid;
  2807. highlightedFootprints = refs ? refs.map(r => r[1]) : [];
  2808. highlightedNet = net;
  2809. drawHighlights();
  2810. EventHandler.emitEvent(
  2811. IBOM_EVENT_TYPES.HIGHLIGHT_EVENT, {
  2812. rowid: rowid,
  2813. refs: refs,
  2814. net: net
  2815. });
  2816. }
  2817. }
  2818. function entryMatches(entry) {
  2819. if (settings.bommode == "netlist") {
  2820. // entry is just a net name
  2821. return entry.toLowerCase().indexOf(filter) >= 0;
  2822. }
  2823. // check refs
  2824. if (!settings.hiddenColumns.includes("references")) {
  2825. for (var ref of entry) {
  2826. if (ref[0].toLowerCase().indexOf(filter) >= 0) {
  2827. return true;
  2828. }
  2829. }
  2830. }
  2831. // check fields
  2832. for (var i in config.fields) {
  2833. var f = config.fields[i];
  2834. if (!settings.hiddenColumns.includes(f)) {
  2835. for (var ref of entry) {
  2836. if (pcbdata.bom.fields[ref[1]][i].toLowerCase().indexOf(filter) >= 0) {
  2837. return true;
  2838. }
  2839. }
  2840. }
  2841. }
  2842. return false;
  2843. }
  2844. function findRefInEntry(entry) {
  2845. return entry.filter(r => r[0].toLowerCase() == reflookup);
  2846. }
  2847. function highlightFilter(s) {
  2848. if (!filter) {
  2849. return s;
  2850. }
  2851. var parts = s.toLowerCase().split(filter);
  2852. if (parts.length == 1) {
  2853. return s;
  2854. }
  2855. var r = "";
  2856. var pos = 0;
  2857. for (var i in parts) {
  2858. if (i > 0) {
  2859. r += '<mark class="highlight">' +
  2860. s.substring(pos, pos + filter.length) +
  2861. '</mark>';
  2862. pos += filter.length;
  2863. }
  2864. r += s.substring(pos, pos + parts[i].length);
  2865. pos += parts[i].length;
  2866. }
  2867. return r;
  2868. }
  2869. function checkboxSetUnsetAllHandler(checkboxname) {
  2870. return function () {
  2871. var checkboxnum = 0;
  2872. while (checkboxnum < settings.checkboxes.length &&
  2873. settings.checkboxes[checkboxnum].toLowerCase() != checkboxname.toLowerCase()) {
  2874. checkboxnum++;
  2875. }
  2876. if (checkboxnum >= settings.checkboxes.length) {
  2877. return;
  2878. }
  2879. var allset = true;
  2880. var checkbox;
  2881. var row;
  2882. for (row of bombody.childNodes) {
  2883. checkbox = row.childNodes[checkboxnum + 1].childNodes[0];
  2884. if (!checkbox.checked || checkbox.indeterminate) {
  2885. allset = false;
  2886. break;
  2887. }
  2888. }
  2889. for (row of bombody.childNodes) {
  2890. checkbox = row.childNodes[checkboxnum + 1].childNodes[0];
  2891. checkbox.checked = !allset;
  2892. checkbox.indeterminate = false;
  2893. checkbox.onchange();
  2894. }
  2895. }
  2896. }
  2897. function createColumnHeader(name, cls, comparator, is_checkbox = false) {
  2898. var th = document.createElement("TH");
  2899. th.innerHTML = name;
  2900. th.classList.add(cls);
  2901. if (is_checkbox)
  2902. th.setAttribute("col_name", "bom-checkbox");
  2903. else
  2904. th.setAttribute("col_name", name);
  2905. var span = document.createElement("SPAN");
  2906. span.classList.add("sortmark");
  2907. span.classList.add("none");
  2908. th.appendChild(span);
  2909. var spacer = document.createElement("div");
  2910. spacer.className = "column-spacer";
  2911. th.appendChild(spacer);
  2912. spacer.onclick = function () {
  2913. if (currentSortColumn && th !== currentSortColumn) {
  2914. // Currently sorted by another column
  2915. currentSortColumn.childNodes[1].classList.remove(currentSortOrder);
  2916. currentSortColumn.childNodes[1].classList.add("none");
  2917. currentSortColumn = null;
  2918. currentSortOrder = null;
  2919. }
  2920. if (currentSortColumn && th === currentSortColumn) {
  2921. // Already sorted by this column
  2922. if (currentSortOrder == "asc") {
  2923. // Sort by this column, descending order
  2924. bomSortFunction = function (a, b) {
  2925. return -comparator(a, b);
  2926. }
  2927. currentSortColumn.childNodes[1].classList.remove("asc");
  2928. currentSortColumn.childNodes[1].classList.add("desc");
  2929. currentSortOrder = "desc";
  2930. } else {
  2931. // Unsort
  2932. bomSortFunction = null;
  2933. currentSortColumn.childNodes[1].classList.remove("desc");
  2934. currentSortColumn.childNodes[1].classList.add("none");
  2935. currentSortColumn = null;
  2936. currentSortOrder = null;
  2937. }
  2938. } else {
  2939. // Sort by this column, ascending order
  2940. bomSortFunction = comparator;
  2941. currentSortColumn = th;
  2942. currentSortColumn.childNodes[1].classList.remove("none");
  2943. currentSortColumn.childNodes[1].classList.add("asc");
  2944. currentSortOrder = "asc";
  2945. }
  2946. populateBomBody();
  2947. }
  2948. if (is_checkbox) {
  2949. spacer.onclick = fancyDblClickHandler(
  2950. spacer, spacer.onclick, checkboxSetUnsetAllHandler(name));
  2951. }
  2952. return th;
  2953. }
  2954. function populateBomHeader(placeHolderColumn = null, placeHolderElements = null) {
  2955. while (bomhead.firstChild) {
  2956. bomhead.removeChild(bomhead.firstChild);
  2957. }
  2958. var tr = document.createElement("TR");
  2959. var th = document.createElement("TH");
  2960. th.classList.add("numCol");
  2961. var vismenu = document.createElement("div");
  2962. vismenu.id = "vismenu";
  2963. vismenu.classList.add("menu");
  2964. var visbutton = document.createElement("div");
  2965. visbutton.classList.add("visbtn");
  2966. visbutton.classList.add("hideonprint");
  2967. var viscontent = document.createElement("div");
  2968. viscontent.classList.add("menu-content");
  2969. viscontent.id = "vismenu-content";
  2970. settings.columnOrder.forEach(column => {
  2971. if (typeof column !== "string")
  2972. return;
  2973. // Skip empty columns
  2974. if (column === "checkboxes" && settings.checkboxes.length == 0)
  2975. return;
  2976. else if (column === "Quantity" && settings.bommode == "ungrouped")
  2977. return;
  2978. var label = document.createElement("label");
  2979. label.classList.add("menu-label");
  2980. var input = document.createElement("input");
  2981. input.classList.add("visibility_checkbox");
  2982. input.type = "checkbox";
  2983. input.onchange = function (e) {
  2984. setShowBOMColumn(column, e.target.checked)
  2985. };
  2986. input.checked = !(settings.hiddenColumns.includes(column));
  2987. label.appendChild(input);
  2988. if (column.length > 0)
  2989. label.append(column[0].toUpperCase() + column.slice(1));
  2990. viscontent.appendChild(label);
  2991. });
  2992. viscontent.childNodes[0].classList.add("menu-label-top");
  2993. vismenu.appendChild(visbutton);
  2994. if (settings.bommode != "netlist") {
  2995. vismenu.appendChild(viscontent);
  2996. th.appendChild(vismenu);
  2997. }
  2998. tr.appendChild(th);
  2999. var checkboxCompareClosure = function (checkbox) {
  3000. return (a, b) => {
  3001. var stateA = getCheckboxState(checkbox, a);
  3002. var stateB = getCheckboxState(checkbox, b);
  3003. if (stateA > stateB) return -1;
  3004. if (stateA < stateB) return 1;
  3005. return 0;
  3006. }
  3007. }
  3008. var stringFieldCompareClosure = function (fieldIndex) {
  3009. return (a, b) => {
  3010. var fa = pcbdata.bom.fields[a[0][1]][fieldIndex];
  3011. var fb = pcbdata.bom.fields[b[0][1]][fieldIndex];
  3012. if (fa != fb) return fa > fb ? 1 : -1;
  3013. else return 0;
  3014. }
  3015. }
  3016. var referenceRegex = /(?<prefix>[^0-9]+)(?<number>[0-9]+)/;
  3017. var compareRefs = (a, b) => {
  3018. var ra = referenceRegex.exec(a);
  3019. var rb = referenceRegex.exec(b);
  3020. if (ra === null || rb === null) {
  3021. if (a != b) return a > b ? 1 : -1;
  3022. return 0;
  3023. } else {
  3024. if (ra.groups.prefix != rb.groups.prefix) {
  3025. return ra.groups.prefix > rb.groups.prefix ? 1 : -1;
  3026. }
  3027. if (ra.groups.number != rb.groups.number) {
  3028. return parseInt(ra.groups.number) > parseInt(rb.groups.number) ? 1 : -1;
  3029. }
  3030. return 0;
  3031. }
  3032. }
  3033. if (settings.bommode == "netlist") {
  3034. th = createColumnHeader("Net name", "bom-netname", (a, b) => {
  3035. if (a > b) return -1;
  3036. if (a < b) return 1;
  3037. return 0;
  3038. });
  3039. tr.appendChild(th);
  3040. } else {
  3041. // Filter hidden columns
  3042. var columns = settings.columnOrder.filter(e => !settings.hiddenColumns.includes(e));
  3043. var valueIndex = config.fields.indexOf("Value");
  3044. var footprintIndex = config.fields.indexOf("Footprint");
  3045. columns.forEach((column) => {
  3046. if (column === placeHolderColumn) {
  3047. var n = 1;
  3048. if (column === "checkboxes")
  3049. n = settings.checkboxes.length;
  3050. for (i = 0; i < n; i++) {
  3051. td = placeHolderElements.shift();
  3052. tr.appendChild(td);
  3053. }
  3054. return;
  3055. } else if (column === "checkboxes") {
  3056. for (var checkbox of settings.checkboxes) {
  3057. th = createColumnHeader(
  3058. checkbox, "bom-checkbox", checkboxCompareClosure(checkbox), true);
  3059. tr.appendChild(th);
  3060. }
  3061. } else if (column === "References") {
  3062. tr.appendChild(createColumnHeader("References", "references", (a, b) => {
  3063. var i = 0;
  3064. while (i < a.length && i < b.length) {
  3065. if (a[i] != b[i]) return compareRefs(a[i][0], b[i][0]);
  3066. i++;
  3067. }
  3068. return a.length - b.length;
  3069. }));
  3070. } else if (column === "Value") {
  3071. tr.appendChild(createColumnHeader("Value", "value", (a, b) => {
  3072. var ra = a[0][1], rb = b[0][1];
  3073. return valueCompare(
  3074. pcbdata.bom.parsedValues[ra], pcbdata.bom.parsedValues[rb],
  3075. pcbdata.bom.fields[ra][valueIndex], pcbdata.bom.fields[rb][valueIndex]);
  3076. }));
  3077. return;
  3078. } else if (column === "Footprint") {
  3079. tr.appendChild(createColumnHeader(
  3080. "Footprint", "footprint", stringFieldCompareClosure(footprintIndex)));
  3081. } else if (column === "Quantity" && settings.bommode == "grouped") {
  3082. tr.appendChild(createColumnHeader("Quantity", "quantity", (a, b) => {
  3083. return a.length - b.length;
  3084. }));
  3085. } else {
  3086. // Other fields
  3087. var i = config.fields.indexOf(column);
  3088. if (i < 0)
  3089. return;
  3090. tr.appendChild(createColumnHeader(
  3091. column, `field${i + 1}`, stringFieldCompareClosure(i)));
  3092. }
  3093. });
  3094. }
  3095. bomhead.appendChild(tr);
  3096. }
  3097. function populateBomBody(placeholderColumn = null, placeHolderElements = null) {
  3098. while (bom.firstChild) {
  3099. bom.removeChild(bom.firstChild);
  3100. }
  3101. highlightHandlers = [];
  3102. footprintIndexToHandler = {};
  3103. netsToHandler = {};
  3104. currentHighlightedRowId = null;
  3105. var first = true;
  3106. if (settings.bommode == "netlist") {
  3107. bomtable = pcbdata.nets.slice();
  3108. } else {
  3109. switch (settings.canvaslayout) {
  3110. case 'F':
  3111. bomtable = pcbdata.bom.F.slice();
  3112. break;
  3113. case 'FB':
  3114. bomtable = pcbdata.bom.both.slice();
  3115. break;
  3116. case 'B':
  3117. bomtable = pcbdata.bom.B.slice();
  3118. break;
  3119. }
  3120. if (settings.bommode == "ungrouped") {
  3121. // expand bom table
  3122. expandedTable = []
  3123. for (var bomentry of bomtable) {
  3124. for (var ref of bomentry) {
  3125. expandedTable.push([ref]);
  3126. }
  3127. }
  3128. bomtable = expandedTable;
  3129. }
  3130. }
  3131. if (bomSortFunction) {
  3132. bomtable = bomtable.sort(bomSortFunction);
  3133. }
  3134. for (var i in bomtable) {
  3135. var bomentry = bomtable[i];
  3136. if (filter && !entryMatches(bomentry)) {
  3137. continue;
  3138. }
  3139. var references = null;
  3140. var netname = null;
  3141. var tr = document.createElement("TR");
  3142. var td = document.createElement("TD");
  3143. var rownum = +i + 1;
  3144. tr.id = "bomrow" + rownum;
  3145. td.textContent = rownum;
  3146. tr.appendChild(td);
  3147. if (settings.bommode == "netlist") {
  3148. netname = bomentry;
  3149. td = document.createElement("TD");
  3150. td.innerHTML = highlightFilter(netname ? netname : "&lt;no net&gt;");
  3151. tr.appendChild(td);
  3152. } else {
  3153. if (reflookup) {
  3154. references = findRefInEntry(bomentry);
  3155. if (references.length == 0) {
  3156. continue;
  3157. }
  3158. } else {
  3159. references = bomentry;
  3160. }
  3161. // Filter hidden columns
  3162. var columns = settings.columnOrder.filter(e => !settings.hiddenColumns.includes(e));
  3163. columns.forEach((column) => {
  3164. if (column === placeholderColumn) {
  3165. var n = 1;
  3166. if (column === "checkboxes")
  3167. n = settings.checkboxes.length;
  3168. for (i = 0; i < n; i++) {
  3169. td = placeHolderElements.shift();
  3170. tr.appendChild(td);
  3171. }
  3172. return;
  3173. } else if (column === "checkboxes") {
  3174. for (var checkbox of settings.checkboxes) {
  3175. if (checkbox) {
  3176. td = document.createElement("TD");
  3177. var input = document.createElement("input");
  3178. input.type = "checkbox";
  3179. input.onchange = createCheckboxChangeHandler(checkbox, references, tr);
  3180. setBomCheckboxState(checkbox, input, references);
  3181. if (input.checked && settings.markWhenChecked == checkbox) {
  3182. tr.classList.add("checked");
  3183. }
  3184. td.appendChild(input);
  3185. tr.appendChild(td);
  3186. }
  3187. }
  3188. } else if (column === "References") {
  3189. td = document.createElement("TD");
  3190. td.innerHTML = highlightFilter(references.map(r => r[0]).join(", "));
  3191. tr.appendChild(td);
  3192. } else if (column === "Quantity" && settings.bommode == "grouped") {
  3193. // Quantity
  3194. td = document.createElement("TD");
  3195. td.textContent = references.length;
  3196. tr.appendChild(td);
  3197. } else {
  3198. // All the other fields
  3199. var field_index = config.fields.indexOf(column)
  3200. if (field_index < 0)
  3201. return;
  3202. var valueSet = new Set();
  3203. references.map(r => r[1]).forEach((id) => valueSet.add(pcbdata.bom.fields[id][field_index]));
  3204. td = document.createElement("TD");
  3205. td.innerHTML = highlightFilter(Array.from(valueSet).join(", "));
  3206. tr.appendChild(td);
  3207. }
  3208. });
  3209. }
  3210. bom.appendChild(tr);
  3211. var handler = createRowHighlightHandler(tr.id, references, netname);
  3212. tr.onmousemove = handler;
  3213. highlightHandlers.push({
  3214. id: tr.id,
  3215. handler: handler,
  3216. });
  3217. if (references !== null) {
  3218. for (var refIndex of references.map(r => r[1])) {
  3219. footprintIndexToHandler[refIndex] = handler;
  3220. }
  3221. }
  3222. if (netname !== null) {
  3223. netsToHandler[netname] = handler;
  3224. }
  3225. if ((filter || reflookup) && first) {
  3226. handler();
  3227. first = false;
  3228. }
  3229. }
  3230. EventHandler.emitEvent(
  3231. IBOM_EVENT_TYPES.BOM_BODY_CHANGE_EVENT, {
  3232. filter: filter,
  3233. reflookup: reflookup,
  3234. checkboxes: settings.checkboxes,
  3235. bommode: settings.bommode,
  3236. });
  3237. }
  3238. function highlightPreviousRow() {
  3239. if (!currentHighlightedRowId) {
  3240. highlightHandlers[highlightHandlers.length - 1].handler();
  3241. } else {
  3242. if (highlightHandlers.length > 1 &&
  3243. highlightHandlers[0].id == currentHighlightedRowId) {
  3244. highlightHandlers[highlightHandlers.length - 1].handler();
  3245. } else {
  3246. for (var i = 0; i < highlightHandlers.length - 1; i++) {
  3247. if (highlightHandlers[i + 1].id == currentHighlightedRowId) {
  3248. highlightHandlers[i].handler();
  3249. break;
  3250. }
  3251. }
  3252. }
  3253. }
  3254. smoothScrollToRow(currentHighlightedRowId);
  3255. }
  3256. function highlightNextRow() {
  3257. if (!currentHighlightedRowId) {
  3258. highlightHandlers[0].handler();
  3259. } else {
  3260. if (highlightHandlers.length > 1 &&
  3261. highlightHandlers[highlightHandlers.length - 1].id == currentHighlightedRowId) {
  3262. highlightHandlers[0].handler();
  3263. } else {
  3264. for (var i = 1; i < highlightHandlers.length; i++) {
  3265. if (highlightHandlers[i - 1].id == currentHighlightedRowId) {
  3266. highlightHandlers[i].handler();
  3267. break;
  3268. }
  3269. }
  3270. }
  3271. }
  3272. smoothScrollToRow(currentHighlightedRowId);
  3273. }
  3274. function populateBomTable() {
  3275. populateBomHeader();
  3276. populateBomBody();
  3277. setBomHandlers();
  3278. resizableGrid(bomhead);
  3279. }
  3280. function footprintsClicked(footprintIndexes) {
  3281. var lastClickedIndex = footprintIndexes.indexOf(lastClicked);
  3282. for (var i = 1; i <= footprintIndexes.length; i++) {
  3283. var refIndex = footprintIndexes[(lastClickedIndex + i) % footprintIndexes.length];
  3284. if (refIndex in footprintIndexToHandler) {
  3285. lastClicked = refIndex;
  3286. footprintIndexToHandler[refIndex]();
  3287. smoothScrollToRow(currentHighlightedRowId);
  3288. break;
  3289. }
  3290. }
  3291. }
  3292. function netClicked(net) {
  3293. if (net in netsToHandler) {
  3294. netsToHandler[net]();
  3295. smoothScrollToRow(currentHighlightedRowId);
  3296. } else {
  3297. clearHighlightedFootprints();
  3298. highlightedNet = net;
  3299. drawHighlights();
  3300. }
  3301. }
  3302. function updateFilter(input) {
  3303. filter = input.toLowerCase();
  3304. populateBomTable();
  3305. }
  3306. function updateRefLookup(input) {
  3307. reflookup = input.toLowerCase();
  3308. populateBomTable();
  3309. }
  3310. function changeCanvasLayout(layout) {
  3311. document.getElementById("fl-btn").classList.remove("depressed");
  3312. document.getElementById("fb-btn").classList.remove("depressed");
  3313. document.getElementById("bl-btn").classList.remove("depressed");
  3314. switch (layout) {
  3315. case 'F':
  3316. document.getElementById("fl-btn").classList.add("depressed");
  3317. if (settings.bomlayout != "bom-only") {
  3318. canvassplit.collapse(1);
  3319. }
  3320. break;
  3321. case 'B':
  3322. document.getElementById("bl-btn").classList.add("depressed");
  3323. if (settings.bomlayout != "bom-only") {
  3324. canvassplit.collapse(0);
  3325. }
  3326. break;
  3327. default:
  3328. document.getElementById("fb-btn").classList.add("depressed");
  3329. if (settings.bomlayout != "bom-only") {
  3330. canvassplit.setSizes([50, 50]);
  3331. }
  3332. }
  3333. settings.canvaslayout = layout;
  3334. writeStorage("canvaslayout", layout);
  3335. resizeAll();
  3336. changeBomMode(settings.bommode);
  3337. }
  3338. function populateMetadata() {
  3339. document.getElementById("title").innerHTML = pcbdata.metadata.title;
  3340. document.getElementById("revision").innerHTML = "Rev: " + pcbdata.metadata.revision;
  3341. document.getElementById("company").innerHTML = pcbdata.metadata.company;
  3342. document.getElementById("filedate").innerHTML = pcbdata.metadata.date;
  3343. if (pcbdata.metadata.title != "") {
  3344. document.title = pcbdata.metadata.title + " BOM";
  3345. }
  3346. // Calculate board stats
  3347. var fp_f = 0,
  3348. fp_b = 0,
  3349. pads_f = 0,
  3350. pads_b = 0,
  3351. pads_th = 0;
  3352. for (var i = 0; i < pcbdata.footprints.length; i++) {
  3353. if (pcbdata.bom.skipped.includes(i)) continue;
  3354. var mod = pcbdata.footprints[i];
  3355. if (mod.layer == "F") {
  3356. fp_f++;
  3357. } else {
  3358. fp_b++;
  3359. }
  3360. for (var pad of mod.pads) {
  3361. if (pad.type == "th") {
  3362. pads_th++;
  3363. } else {
  3364. if (pad.layers.includes("F")) {
  3365. pads_f++;
  3366. }
  3367. if (pad.layers.includes("B")) {
  3368. pads_b++;
  3369. }
  3370. }
  3371. }
  3372. }
  3373. document.getElementById("stats-components-front").innerHTML = fp_f;
  3374. document.getElementById("stats-components-back").innerHTML = fp_b;
  3375. document.getElementById("stats-components-total").innerHTML = fp_f + fp_b;
  3376. document.getElementById("stats-groups-front").innerHTML = pcbdata.bom.F.length;
  3377. document.getElementById("stats-groups-back").innerHTML = pcbdata.bom.B.length;
  3378. document.getElementById("stats-groups-total").innerHTML = pcbdata.bom.both.length;
  3379. document.getElementById("stats-smd-pads-front").innerHTML = pads_f;
  3380. document.getElementById("stats-smd-pads-back").innerHTML = pads_b;
  3381. document.getElementById("stats-smd-pads-total").innerHTML = pads_f + pads_b;
  3382. document.getElementById("stats-th-pads").innerHTML = pads_th;
  3383. // Update version string
  3384. document.getElementById("github-link").innerHTML = "InteractiveHtmlBom&nbsp;" +
  3385. /^v\d+\.\d+/.exec(pcbdata.ibom_version)[0];
  3386. }
  3387. function changeBomLayout(layout) {
  3388. document.getElementById("bom-btn").classList.remove("depressed");
  3389. document.getElementById("lr-btn").classList.remove("depressed");
  3390. document.getElementById("tb-btn").classList.remove("depressed");
  3391. switch (layout) {
  3392. case 'bom-only':
  3393. document.getElementById("bom-btn").classList.add("depressed");
  3394. if (bomsplit) {
  3395. bomsplit.destroy();
  3396. bomsplit = null;
  3397. canvassplit.destroy();
  3398. canvassplit = null;
  3399. }
  3400. document.getElementById("frontcanvas").style.display = "none";
  3401. document.getElementById("backcanvas").style.display = "none";
  3402. document.getElementById("bot").style.height = "";
  3403. break;
  3404. case 'top-bottom':
  3405. document.getElementById("tb-btn").classList.add("depressed");
  3406. document.getElementById("frontcanvas").style.display = "";
  3407. document.getElementById("backcanvas").style.display = "";
  3408. document.getElementById("bot").style.height = "calc(100% - 80px)";
  3409. document.getElementById("bomdiv").classList.remove("split-horizontal");
  3410. document.getElementById("canvasdiv").classList.remove("split-horizontal");
  3411. document.getElementById("frontcanvas").classList.add("split-horizontal");
  3412. document.getElementById("backcanvas").classList.add("split-horizontal");
  3413. if (bomsplit) {
  3414. bomsplit.destroy();
  3415. bomsplit = null;
  3416. canvassplit.destroy();
  3417. canvassplit = null;
  3418. }
  3419. bomsplit = Split(['#bomdiv', '#canvasdiv'], {
  3420. sizes: [50, 50],
  3421. onDragEnd: resizeAll,
  3422. direction: "vertical",
  3423. gutterSize: 5
  3424. });
  3425. canvassplit = Split(['#frontcanvas', '#backcanvas'], {
  3426. sizes: [50, 50],
  3427. gutterSize: 5,
  3428. onDragEnd: resizeAll
  3429. });
  3430. break;
  3431. case 'left-right':
  3432. document.getElementById("lr-btn").classList.add("depressed");
  3433. document.getElementById("frontcanvas").style.display = "";
  3434. document.getElementById("backcanvas").style.display = "";
  3435. document.getElementById("bot").style.height = "calc(100% - 80px)";
  3436. document.getElementById("bomdiv").classList.add("split-horizontal");
  3437. document.getElementById("canvasdiv").classList.add("split-horizontal");
  3438. document.getElementById("frontcanvas").classList.remove("split-horizontal");
  3439. document.getElementById("backcanvas").classList.remove("split-horizontal");
  3440. if (bomsplit) {
  3441. bomsplit.destroy();
  3442. bomsplit = null;
  3443. canvassplit.destroy();
  3444. canvassplit = null;
  3445. }
  3446. bomsplit = Split(['#bomdiv', '#canvasdiv'], {
  3447. sizes: [50, 50],
  3448. onDragEnd: resizeAll,
  3449. gutterSize: 5
  3450. });
  3451. canvassplit = Split(['#frontcanvas', '#backcanvas'], {
  3452. sizes: [50, 50],
  3453. gutterSize: 5,
  3454. direction: "vertical",
  3455. onDragEnd: resizeAll
  3456. });
  3457. }
  3458. settings.bomlayout = layout;
  3459. writeStorage("bomlayout", layout);
  3460. changeCanvasLayout(settings.canvaslayout);
  3461. }
  3462. function changeBomMode(mode) {
  3463. document.getElementById("bom-grouped-btn").classList.remove("depressed");
  3464. document.getElementById("bom-ungrouped-btn").classList.remove("depressed");
  3465. document.getElementById("bom-netlist-btn").classList.remove("depressed");
  3466. var chkbxs = document.getElementsByClassName("visibility_checkbox");
  3467. switch (mode) {
  3468. case 'grouped':
  3469. document.getElementById("bom-grouped-btn").classList.add("depressed");
  3470. for (var i = 0; i < chkbxs.length; i++) {
  3471. chkbxs[i].disabled = false;
  3472. }
  3473. break;
  3474. case 'ungrouped':
  3475. document.getElementById("bom-ungrouped-btn").classList.add("depressed");
  3476. for (var i = 0; i < chkbxs.length; i++) {
  3477. chkbxs[i].disabled = false;
  3478. }
  3479. break;
  3480. case 'netlist':
  3481. document.getElementById("bom-netlist-btn").classList.add("depressed");
  3482. for (var i = 0; i < chkbxs.length; i++) {
  3483. chkbxs[i].disabled = true;
  3484. }
  3485. }
  3486. writeStorage("bommode", mode);
  3487. if (mode != settings.bommode) {
  3488. settings.bommode = mode;
  3489. bomSortFunction = null;
  3490. currentSortColumn = null;
  3491. currentSortOrder = null;
  3492. clearHighlightedFootprints();
  3493. }
  3494. populateBomTable();
  3495. }
  3496. function focusFilterField() {
  3497. focusInputField(document.getElementById("filter"));
  3498. }
  3499. function focusRefLookupField() {
  3500. focusInputField(document.getElementById("reflookup"));
  3501. }
  3502. function toggleBomCheckbox(bomrowid, checkboxnum) {
  3503. if (!bomrowid || checkboxnum > settings.checkboxes.length) {
  3504. return;
  3505. }
  3506. var bomrow = document.getElementById(bomrowid);
  3507. var checkbox = bomrow.childNodes[checkboxnum].childNodes[0];
  3508. checkbox.checked = !checkbox.checked;
  3509. checkbox.indeterminate = false;
  3510. checkbox.onchange();
  3511. }
  3512. function checkBomCheckbox(bomrowid, checkboxname) {
  3513. var checkboxnum = 0;
  3514. while (checkboxnum < settings.checkboxes.length &&
  3515. settings.checkboxes[checkboxnum].toLowerCase() != checkboxname.toLowerCase()) {
  3516. checkboxnum++;
  3517. }
  3518. if (!bomrowid || checkboxnum >= settings.checkboxes.length) {
  3519. return;
  3520. }
  3521. var bomrow = document.getElementById(bomrowid);
  3522. var checkbox = bomrow.childNodes[checkboxnum + 1].childNodes[0];
  3523. checkbox.checked = true;
  3524. checkbox.indeterminate = false;
  3525. checkbox.onchange();
  3526. }
  3527. function setBomCheckboxes(value) {
  3528. writeStorage("bomCheckboxes", value);
  3529. settings.checkboxes = value.split(",").map((e) => e.trim()).filter((e) => e);
  3530. prepCheckboxes();
  3531. populateMarkWhenCheckedOptions();
  3532. setMarkWhenChecked(settings.markWhenChecked);
  3533. }
  3534. function setMarkWhenChecked(value) {
  3535. writeStorage("markWhenChecked", value);
  3536. settings.markWhenChecked = value;
  3537. markedFootprints.clear();
  3538. for (var ref of (value ? getStoredCheckboxRefs(value) : [])) {
  3539. markedFootprints.add(ref);
  3540. }
  3541. populateBomTable();
  3542. drawHighlights();
  3543. }
  3544. function prepCheckboxes() {
  3545. var table = document.getElementById("checkbox-stats");
  3546. while (table.childElementCount > 1) {
  3547. table.removeChild(table.lastChild);
  3548. }
  3549. if (settings.checkboxes.length) {
  3550. table.style.display = "";
  3551. } else {
  3552. table.style.display = "none";
  3553. }
  3554. for (var checkbox of settings.checkboxes) {
  3555. var tr = document.createElement("TR");
  3556. var td = document.createElement("TD");
  3557. td.innerHTML = checkbox;
  3558. tr.appendChild(td);
  3559. td = document.createElement("TD");
  3560. td.id = "checkbox-stats-" + checkbox;
  3561. var progressbar = document.createElement("div");
  3562. progressbar.classList.add("bar");
  3563. td.appendChild(progressbar);
  3564. var text = document.createElement("div");
  3565. text.classList.add("text");
  3566. td.appendChild(text);
  3567. tr.appendChild(td);
  3568. table.appendChild(tr);
  3569. updateCheckboxStats(checkbox);
  3570. }
  3571. }
  3572. function populateMarkWhenCheckedOptions() {
  3573. var container = document.getElementById("markWhenCheckedContainer");
  3574. if (settings.checkboxes.length == 0) {
  3575. container.parentElement.style.display = "none";
  3576. return;
  3577. }
  3578. container.innerHTML = '';
  3579. container.parentElement.style.display = "inline-block";
  3580. function createOption(name, displayName) {
  3581. var id = "markWhenChecked-" + name;
  3582. var div = document.createElement("div");
  3583. div.classList.add("radio-container");
  3584. var input = document.createElement("input");
  3585. input.type = "radio";
  3586. input.name = "markWhenChecked";
  3587. input.value = name;
  3588. input.id = id;
  3589. input.onchange = () => setMarkWhenChecked(name);
  3590. div.appendChild(input);
  3591. // Preserve the selected element when the checkboxes change
  3592. if (name == settings.markWhenChecked) {
  3593. input.checked = true;
  3594. }
  3595. var label = document.createElement("label");
  3596. label.innerHTML = displayName;
  3597. label.htmlFor = id;
  3598. div.appendChild(label);
  3599. container.appendChild(div);
  3600. }
  3601. createOption("", "None");
  3602. for (var checkbox of settings.checkboxes) {
  3603. createOption(checkbox, checkbox);
  3604. }
  3605. }
  3606. function updateCheckboxStats(checkbox) {
  3607. var checked = getStoredCheckboxRefs(checkbox).size;
  3608. var total = pcbdata.footprints.length - pcbdata.bom.skipped.length;
  3609. var percent = checked * 100.0 / total;
  3610. var td = document.getElementById("checkbox-stats-" + checkbox);
  3611. td.firstChild.style.width = percent + "%";
  3612. td.lastChild.innerHTML = checked + "/" + total + " (" + Math.round(percent) + "%)";
  3613. }
  3614. function constrain(number, min, max){
  3615. return Math.min(Math.max(parseInt(number), min), max);
  3616. }
  3617. document.onkeydown = function (e) {
  3618. switch (e.key) {
  3619. case "n":
  3620. if (document.activeElement.type == "text") {
  3621. return;
  3622. }
  3623. if (currentHighlightedRowId !== null) {
  3624. checkBomCheckbox(currentHighlightedRowId, "placed");
  3625. highlightNextRow();
  3626. e.preventDefault();
  3627. }
  3628. break;
  3629. case "ArrowUp":
  3630. highlightPreviousRow();
  3631. e.preventDefault();
  3632. break;
  3633. case "ArrowDown":
  3634. highlightNextRow();
  3635. e.preventDefault();
  3636. break;
  3637. case "ArrowLeft":
  3638. case "ArrowRight":
  3639. if (document.activeElement.type != "text"){
  3640. e.preventDefault();
  3641. let boardRotationElement = document.getElementById("boardRotation")
  3642. settings.boardRotation = parseInt(boardRotationElement.value); // degrees / 5
  3643. if (e.key == "ArrowLeft"){
  3644. settings.boardRotation += 3; // 15 degrees
  3645. }
  3646. else{
  3647. settings.boardRotation -= 3;
  3648. }
  3649. settings.boardRotation = constrain(settings.boardRotation, boardRotationElement.min, boardRotationElement.max);
  3650. boardRotationElement.value = settings.boardRotation
  3651. setBoardRotation(settings.boardRotation);
  3652. }
  3653. break;
  3654. default:
  3655. break;
  3656. }
  3657. if (e.altKey) {
  3658. switch (e.key) {
  3659. case "f":
  3660. focusFilterField();
  3661. e.preventDefault();
  3662. break;
  3663. case "r":
  3664. focusRefLookupField();
  3665. e.preventDefault();
  3666. break;
  3667. case "z":
  3668. changeBomLayout("bom-only");
  3669. e.preventDefault();
  3670. break;
  3671. case "x":
  3672. changeBomLayout("left-right");
  3673. e.preventDefault();
  3674. break;
  3675. case "c":
  3676. changeBomLayout("top-bottom");
  3677. e.preventDefault();
  3678. break;
  3679. case "v":
  3680. changeCanvasLayout("F");
  3681. e.preventDefault();
  3682. break;
  3683. case "b":
  3684. changeCanvasLayout("FB");
  3685. e.preventDefault();
  3686. break;
  3687. case "n":
  3688. changeCanvasLayout("B");
  3689. e.preventDefault();
  3690. break;
  3691. default:
  3692. break;
  3693. }
  3694. if (e.key >= '1' && e.key <= '9') {
  3695. toggleBomCheckbox(currentHighlightedRowId, parseInt(e.key));
  3696. e.preventDefault();
  3697. }
  3698. }
  3699. }
  3700. function hideNetlistButton() {
  3701. document.getElementById("bom-ungrouped-btn").classList.remove("middle-button");
  3702. document.getElementById("bom-ungrouped-btn").classList.add("right-most-button");
  3703. document.getElementById("bom-netlist-btn").style.display = "none";
  3704. }
  3705. window.onload = function (e) {
  3706. initUtils();
  3707. initRender();
  3708. initStorage();
  3709. initDefaults();
  3710. cleanGutters();
  3711. populateMetadata();
  3712. dbgdiv = document.getElementById("dbg");
  3713. bom = document.getElementById("bombody");
  3714. bomhead = document.getElementById("bomhead");
  3715. filter = "";
  3716. reflookup = "";
  3717. if (!("nets" in pcbdata)) {
  3718. hideNetlistButton();
  3719. }
  3720. initDone = true;
  3721. setBomCheckboxes(document.getElementById("bomCheckboxes").value);
  3722. // Triggers render
  3723. changeBomLayout(settings.bomlayout);
  3724. // Users may leave fullscreen without touching the checkbox. Uncheck.
  3725. document.addEventListener('fullscreenchange', () => {
  3726. if (!document.fullscreenElement)
  3727. document.getElementById('fullscreenCheckbox').checked = false;
  3728. });
  3729. }
  3730. window.onresize = resizeAll;
  3731. window.matchMedia("print").addListener(resizeAll);
  3732. ///////////////////////////////////////////////
  3733. ///////////////////////////////////////////////
  3734. ///////////////////////////////////////////////
  3735. </script>
  3736. </head>
  3737. <body>
  3738. <div id="topmostdiv" class="topmostdiv">
  3739. <div id="top">
  3740. <div style="float: right; height: 100%;">
  3741. <div class="hideonprint menu" style="float: right; top: 8px;">
  3742. <button class="menubtn"></button>
  3743. <div class="menu-content">
  3744. <label class="menu-label menu-label-top" style="width: calc(50% - 18px)">
  3745. <input id="darkmodeCheckbox" type="checkbox" onchange="setDarkMode(this.checked)">
  3746. Dark mode
  3747. </label><!-- This comment eats space! All of it!
  3748. --><label class="menu-label menu-label-top" style="width: calc(50% - 17px); border-left: 0;">
  3749. <input id="fullscreenCheckbox" type="checkbox" onchange="setFullscreen(this.checked)">
  3750. Full Screen
  3751. </label>
  3752. <label class="menu-label" style="width: calc(50% - 18px)">
  3753. <input id="fabricationCheckbox" type="checkbox" checked onchange="fabricationVisible(this.checked)">
  3754. Fab layer
  3755. </label><!-- This comment eats space! All of it!
  3756. --><label class="menu-label" style="width: calc(50% - 17px); border-left: 0;">
  3757. <input id="silkscreenCheckbox" type="checkbox" checked onchange="silkscreenVisible(this.checked)">
  3758. Silkscreen
  3759. </label>
  3760. <label class="menu-label" style="width: calc(50% - 18px)">
  3761. <input id="referencesCheckbox" type="checkbox" checked onchange="referencesVisible(this.checked)">
  3762. References
  3763. </label><!-- This comment eats space! All of it!
  3764. --><label class="menu-label" style="width: calc(50% - 17px); border-left: 0;">
  3765. <input id="valuesCheckbox" type="checkbox" checked onchange="valuesVisible(this.checked)">
  3766. Values
  3767. </label>
  3768. <div id="tracksAndZonesCheckboxes">
  3769. <label class="menu-label" style="width: calc(50% - 18px)">
  3770. <input id="tracksCheckbox" type="checkbox" checked onchange="tracksVisible(this.checked)">
  3771. Tracks
  3772. </label><!-- This comment eats space! All of it!
  3773. --><label class="menu-label" style="width: calc(50% - 17px); border-left: 0;">
  3774. <input id="zonesCheckbox" type="checkbox" checked onchange="zonesVisible(this.checked)">
  3775. Zones
  3776. </label>
  3777. </div>
  3778. <label class="menu-label" style="width: calc(50% - 18px)">
  3779. <input id="padsCheckbox" type="checkbox" checked onchange="padsVisible(this.checked)">
  3780. Pads
  3781. </label><!-- This comment eats space! All of it!
  3782. --><label class="menu-label" style="width: calc(50% - 17px); border-left: 0;">
  3783. <input id="dnpOutlineCheckbox" type="checkbox" checked onchange="dnpOutline(this.checked)">
  3784. DNP outlined
  3785. </label>
  3786. <label class="menu-label">
  3787. <input id="highlightpin1Checkbox" type="checkbox" onchange="setHighlightPin1(this.checked)">
  3788. Highlight first pin
  3789. </label>
  3790. <label class="menu-label">
  3791. <input id="dragCheckbox" type="checkbox" checked onchange="setRedrawOnDrag(this.checked)">
  3792. Continuous redraw on drag
  3793. </label>
  3794. <label class="menu-label">
  3795. <span>Board rotation</span>
  3796. <span style="float: right"><span id="rotationDegree">0</span>&#176;</span>
  3797. <input id="boardRotation" type="range" min="-36" max="36" value="0" class="slider" oninput="setBoardRotation(this.value)">
  3798. </label>
  3799. <label class="menu-label">
  3800. <div style="margin-left: 5px">Bom checkboxes</div>
  3801. <input id="bomCheckboxes" class="menu-textbox" type=text
  3802. oninput="setBomCheckboxes(this.value)">
  3803. </label>
  3804. <label class="menu-label">
  3805. <div style="margin-left: 5px">Mark when checked</div>
  3806. <div id="markWhenCheckedContainer"></div>
  3807. </label>
  3808. <label class="menu-label">
  3809. <span class="shameless-plug">
  3810. <span>Created using</span>
  3811. <a id="github-link" target="blank" href="https://github.com/openscopeproject/InteractiveHtmlBom">InteractiveHtmlBom</a>
  3812. <a target="blank" title="Mouse and keyboard help" href="https://github.com/openscopeproject/InteractiveHtmlBom/wiki/Usage#bom-page-mouse-actions" style="text-decoration: none;"><label class="help-link">?</label></a>
  3813. </span>
  3814. </label>
  3815. </div>
  3816. </div>
  3817. <div class="button-container hideonprint"
  3818. style="float: right; position: relative; top: 8px">
  3819. <button id="fl-btn" class="left-most-button" onclick="changeCanvasLayout('F')"
  3820. title="Front only">F
  3821. </button>
  3822. <button id="fb-btn" class="middle-button" onclick="changeCanvasLayout('FB')"
  3823. title="Front and Back">FB
  3824. </button>
  3825. <button id="bl-btn" class="right-most-button" onclick="changeCanvasLayout('B')"
  3826. title="Back only">B
  3827. </button>
  3828. </div>
  3829. <div class="button-container hideonprint"
  3830. style="float: right; position: relative; top: 8px">
  3831. <button id="bom-btn" class="left-most-button" onclick="changeBomLayout('bom-only')"
  3832. title="BOM only"></button>
  3833. <button id="lr-btn" class="middle-button" onclick="changeBomLayout('left-right')"
  3834. title="BOM left, drawings right"></button>
  3835. <button id="tb-btn" class="right-most-button" onclick="changeBomLayout('top-bottom')"
  3836. title="BOM top, drawings bot"></button>
  3837. </div>
  3838. <div class="button-container hideonprint"
  3839. style="float: right; position: relative; top: 8px">
  3840. <button id="bom-grouped-btn" class="left-most-button" onclick="changeBomMode('grouped')"
  3841. title="Grouped BOM"></button>
  3842. <button id="bom-ungrouped-btn" class="middle-button" onclick="changeBomMode('ungrouped')"
  3843. title="Ungrouped BOM"></button>
  3844. <button id="bom-netlist-btn" class="right-most-button" onclick="changeBomMode('netlist')"
  3845. title="Netlist"></button>
  3846. </div>
  3847. <div class="hideonprint menu" style="float: right; top: 8px;">
  3848. <button class="statsbtn"></button>
  3849. <div class="menu-content">
  3850. <table class="stats">
  3851. <tbody>
  3852. <tr>
  3853. <td width="40%">Board stats</td>
  3854. <td>Front</td>
  3855. <td>Back</td>
  3856. <td>Total</td>
  3857. </tr>
  3858. <tr>
  3859. <td>Components</td>
  3860. <td id="stats-components-front">~</td>
  3861. <td id="stats-components-back">~</td>
  3862. <td id="stats-components-total">~</td>
  3863. </tr>
  3864. <tr>
  3865. <td>Groups</td>
  3866. <td id="stats-groups-front">~</td>
  3867. <td id="stats-groups-back">~</td>
  3868. <td id="stats-groups-total">~</td>
  3869. </tr>
  3870. <tr>
  3871. <td>SMD pads</td>
  3872. <td id="stats-smd-pads-front">~</td>
  3873. <td id="stats-smd-pads-back">~</td>
  3874. <td id="stats-smd-pads-total">~</td>
  3875. </tr>
  3876. <tr>
  3877. <td>TH pads</td>
  3878. <td colspan=3 id="stats-th-pads">~</td>
  3879. </tr>
  3880. </tbody>
  3881. </table>
  3882. <table class="stats">
  3883. <col width="40%"/><col />
  3884. <tbody id="checkbox-stats">
  3885. <tr>
  3886. <td colspan=2 style="border-top: 0">Checkboxes</td>
  3887. </tr>
  3888. </tbody>
  3889. </table>
  3890. </div>
  3891. </div>
  3892. <div class="hideonprint menu" style="float: right; top: 8px;">
  3893. <button class="iobtn"></button>
  3894. <div class="menu-content">
  3895. <div class="menu-label menu-label-top">
  3896. <div style="margin-left: 5px;">Save board image</div>
  3897. <div class="flexbox">
  3898. <input id="render-save-width" class="menu-textbox" type="text" value="1000" placeholder="Width"
  3899. style="flex-grow: 1; width: 50px;" oninput="validateSaveImgDimension(this)">
  3900. <span>X</span>
  3901. <input id="render-save-height" class="menu-textbox" type="text" value="1000" placeholder="Height"
  3902. style="flex-grow: 1; width: 50px;" oninput="validateSaveImgDimension(this)">
  3903. </div>
  3904. <label>
  3905. <input id="render-save-transparent" type="checkbox">
  3906. Transparent background
  3907. </label>
  3908. <div class="flexbox">
  3909. <button class="savebtn" onclick="saveImage('F')">Front</button>
  3910. <button class="savebtn" onclick="saveImage('B')">Back</button>
  3911. </div>
  3912. </div>
  3913. <div class="menu-label">
  3914. <span style="margin-left: 5px;">Config and checkbox state</span>
  3915. <div class="flexbox">
  3916. <button class="savebtn" onclick="saveSettings()">Export</button>
  3917. <button class="savebtn" onclick="loadSettings()">Import</button>
  3918. </div>
  3919. </div>
  3920. <div class="menu-label">
  3921. <span style="margin-left: 5px;">Save bom table as</span>
  3922. <div class="flexbox">
  3923. <button class="savebtn" onclick="saveBomTable('csv')">csv</button>
  3924. <button class="savebtn" onclick="saveBomTable('txt')">txt</button>
  3925. </div>
  3926. </div>
  3927. </div>
  3928. </div>
  3929. </div>
  3930. <div id="fileinfodiv" style="overflow: auto;">
  3931. <table class="fileinfo">
  3932. <tbody>
  3933. <tr>
  3934. <td id="title" class="title" style="width: 70%">
  3935. Title
  3936. </td>
  3937. <td id="revision" class="title" style="width: 30%">
  3938. Revision
  3939. </td>
  3940. </tr>
  3941. <tr>
  3942. <td id="company">
  3943. Company
  3944. </td>
  3945. <td id="filedate">
  3946. Date
  3947. </td>
  3948. </tr>
  3949. </tbody>
  3950. </table>
  3951. </div>
  3952. </div>
  3953. <div id="bot" class="split" style="height: calc(100% - 80px)">
  3954. <div id="bomdiv" class="split split-horizontal">
  3955. <div style="width: 100%">
  3956. <input id="reflookup" class="textbox searchbox reflookup hideonprint" type="text" placeholder="Ref lookup"
  3957. oninput="updateRefLookup(this.value)">
  3958. <input id="filter" class="textbox searchbox filter hideonprint" type="text" placeholder="Filter"
  3959. oninput="updateFilter(this.value)">
  3960. <div class="button-container hideonprint" style="float: left; margin: 0;">
  3961. <button id="copy" title="Copy bom table to clipboard"
  3962. onclick="saveBomTable('clipboard')"></button>
  3963. </div>
  3964. </div>
  3965. <div id="dbg"></div>
  3966. <table class="bom" id="bomtable">
  3967. <thead id="bomhead">
  3968. </thead>
  3969. <tbody id="bombody">
  3970. </tbody>
  3971. </table>
  3972. </div>
  3973. <div id="canvasdiv" class="split split-horizontal">
  3974. <div id="frontcanvas" class="split" touch-action="none" style="overflow: hidden">
  3975. <div style="position: relative; width: 100%; height: 100%;">
  3976. <canvas id="F_bg" style="position: absolute; left: 0; top: 0; z-index: 0;"></canvas>
  3977. <canvas id="F_fab" style="position: absolute; left: 0; top: 0; z-index: 1;"></canvas>
  3978. <canvas id="F_slk" style="position: absolute; left: 0; top: 0; z-index: 2;"></canvas>
  3979. <canvas id="F_hl" style="position: absolute; left: 0; top: 0; z-index: 3;"></canvas>
  3980. </div>
  3981. </div>
  3982. <div id="backcanvas" class="split" touch-action="none" style="overflow: hidden">
  3983. <div style="position: relative; width: 100%; height: 100%;">
  3984. <canvas id="B_bg" style="position: absolute; left: 0; top: 0; z-index: 0;"></canvas>
  3985. <canvas id="B_fab" style="position: absolute; left: 0; top: 0; z-index: 1;"></canvas>
  3986. <canvas id="B_slk" style="position: absolute; left: 0; top: 0; z-index: 2;"></canvas>
  3987. <canvas id="B_hl" style="position: absolute; left: 0; top: 0; z-index: 3;"></canvas>
  3988. </div>
  3989. </div>
  3990. </div>
  3991. </div>
  3992. </div>
  3993. </body>
  3994. </html>