crafty.js 427 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124912591269127912891299130913191329133913491359136913791389139914091419142914391449145914691479148914991509151915291539154915591569157915891599160916191629163916491659166916791689169917091719172917391749175917691779178917991809181918291839184918591869187918891899190919191929193919491959196919791989199920092019202920392049205920692079208920992109211921292139214921592169217921892199220922192229223922492259226922792289229923092319232923392349235923692379238923992409241924292439244924592469247924892499250925192529253925492559256925792589259926092619262926392649265926692679268926992709271927292739274927592769277927892799280928192829283928492859286928792889289929092919292929392949295929692979298929993009301930293039304930593069307930893099310931193129313931493159316931793189319932093219322932393249325932693279328932993309331933293339334933593369337933893399340934193429343934493459346934793489349935093519352935393549355935693579358935993609361936293639364936593669367936893699370937193729373937493759376937793789379938093819382938393849385938693879388938993909391939293939394939593969397939893999400940194029403940494059406940794089409941094119412941394149415941694179418941994209421942294239424942594269427942894299430943194329433943494359436943794389439944094419442944394449445944694479448944994509451945294539454945594569457945894599460946194629463946494659466946794689469947094719472947394749475947694779478947994809481948294839484948594869487948894899490949194929493949494959496949794989499950095019502950395049505950695079508950995109511951295139514951595169517951895199520952195229523952495259526952795289529953095319532953395349535953695379538953995409541954295439544954595469547954895499550955195529553955495559556955795589559956095619562956395649565956695679568956995709571957295739574957595769577957895799580958195829583958495859586958795889589959095919592959395949595959695979598959996009601960296039604960596069607960896099610961196129613961496159616961796189619962096219622962396249625962696279628962996309631963296339634963596369637963896399640964196429643964496459646964796489649965096519652965396549655965696579658965996609661966296639664966596669667966896699670967196729673967496759676967796789679968096819682968396849685968696879688968996909691969296939694969596969697969896999700970197029703970497059706970797089709971097119712971397149715971697179718971997209721972297239724972597269727972897299730973197329733973497359736973797389739974097419742974397449745974697479748974997509751975297539754975597569757975897599760976197629763976497659766976797689769977097719772977397749775977697779778977997809781978297839784978597869787978897899790979197929793979497959796979797989799980098019802980398049805980698079808980998109811981298139814981598169817981898199820982198229823982498259826982798289829983098319832983398349835983698379838983998409841984298439844984598469847984898499850985198529853985498559856985798589859986098619862986398649865986698679868986998709871987298739874987598769877987898799880988198829883988498859886988798889889989098919892989398949895989698979898989999009901990299039904990599069907990899099910991199129913991499159916991799189919992099219922992399249925992699279928992999309931993299339934993599369937993899399940994199429943994499459946994799489949995099519952995399549955995699579958995999609961996299639964996599669967996899699970997199729973997499759976997799789979998099819982998399849985998699879988998999909991999299939994999599969997999899991000010001100021000310004100051000610007100081000910010100111001210013100141001510016100171001810019100201002110022100231002410025100261002710028100291003010031100321003310034100351003610037100381003910040100411004210043100441004510046100471004810049100501005110052100531005410055100561005710058100591006010061100621006310064100651006610067100681006910070100711007210073100741007510076100771007810079100801008110082100831008410085100861008710088100891009010091100921009310094100951009610097100981009910100101011010210103101041010510106101071010810109101101011110112101131011410115101161011710118101191012010121101221012310124101251012610127101281012910130101311013210133101341013510136101371013810139101401014110142101431014410145101461014710148101491015010151101521015310154101551015610157101581015910160101611016210163101641016510166101671016810169101701017110172101731017410175101761017710178101791018010181101821018310184101851018610187101881018910190101911019210193101941019510196101971019810199102001020110202102031020410205102061020710208102091021010211102121021310214102151021610217102181021910220102211022210223102241022510226102271022810229102301023110232102331023410235102361023710238102391024010241102421024310244102451024610247102481024910250102511025210253102541025510256102571025810259102601026110262102631026410265102661026710268102691027010271102721027310274102751027610277102781027910280102811028210283102841028510286102871028810289102901029110292102931029410295102961029710298102991030010301103021030310304103051030610307103081030910310103111031210313103141031510316103171031810319103201032110322103231032410325103261032710328103291033010331103321033310334103351033610337103381033910340103411034210343103441034510346103471034810349103501035110352103531035410355103561035710358103591036010361103621036310364103651036610367103681036910370103711037210373103741037510376103771037810379103801038110382103831038410385103861038710388103891039010391103921039310394103951039610397103981039910400104011040210403104041040510406104071040810409104101041110412104131041410415104161041710418104191042010421104221042310424104251042610427104281042910430104311043210433104341043510436104371043810439104401044110442104431044410445104461044710448104491045010451104521045310454104551045610457104581045910460104611046210463104641046510466104671046810469104701047110472104731047410475104761047710478104791048010481104821048310484104851048610487104881048910490104911049210493104941049510496104971049810499105001050110502105031050410505105061050710508105091051010511105121051310514105151051610517105181051910520105211052210523105241052510526105271052810529105301053110532105331053410535105361053710538105391054010541105421054310544105451054610547105481054910550105511055210553105541055510556105571055810559105601056110562105631056410565105661056710568105691057010571105721057310574105751057610577105781057910580105811058210583105841058510586105871058810589105901059110592105931059410595105961059710598105991060010601106021060310604106051060610607106081060910610106111061210613106141061510616106171061810619106201062110622106231062410625106261062710628106291063010631106321063310634106351063610637106381063910640106411064210643106441064510646106471064810649106501065110652106531065410655106561065710658106591066010661106621066310664106651066610667106681066910670106711067210673106741067510676106771067810679106801068110682106831068410685106861068710688106891069010691106921069310694106951069610697106981069910700107011070210703107041070510706107071070810709107101071110712107131071410715107161071710718107191072010721107221072310724107251072610727107281072910730107311073210733107341073510736107371073810739107401074110742107431074410745107461074710748107491075010751107521075310754107551075610757107581075910760107611076210763107641076510766107671076810769107701077110772107731077410775107761077710778107791078010781107821078310784107851078610787107881078910790107911079210793107941079510796107971079810799108001080110802108031080410805108061080710808108091081010811108121081310814108151081610817108181081910820108211082210823108241082510826108271082810829108301083110832108331083410835108361083710838108391084010841108421084310844108451084610847108481084910850108511085210853108541085510856108571085810859108601086110862108631086410865108661086710868108691087010871108721087310874108751087610877108781087910880108811088210883108841088510886108871088810889108901089110892108931089410895108961089710898108991090010901109021090310904109051090610907109081090910910109111091210913109141091510916109171091810919109201092110922109231092410925109261092710928109291093010931109321093310934109351093610937109381093910940109411094210943109441094510946109471094810949109501095110952109531095410955109561095710958109591096010961109621096310964109651096610967109681096910970109711097210973109741097510976109771097810979109801098110982109831098410985109861098710988109891099010991109921099310994109951099610997109981099911000110011100211003110041100511006110071100811009110101101111012110131101411015110161101711018110191102011021110221102311024110251102611027110281102911030110311103211033110341103511036110371103811039110401104111042110431104411045110461104711048110491105011051110521105311054110551105611057110581105911060110611106211063110641106511066110671106811069110701107111072110731107411075110761107711078110791108011081110821108311084110851108611087110881108911090110911109211093110941109511096110971109811099111001110111102111031110411105111061110711108111091111011111111121111311114111151111611117111181111911120111211112211123111241112511126111271112811129111301113111132111331113411135111361113711138111391114011141111421114311144111451114611147111481114911150111511115211153111541115511156111571115811159111601116111162111631116411165111661116711168111691117011171111721117311174111751117611177111781117911180111811118211183111841118511186111871118811189111901119111192111931119411195111961119711198111991120011201112021120311204112051120611207112081120911210112111121211213112141121511216112171121811219112201122111222112231122411225112261122711228112291123011231112321123311234112351123611237112381123911240112411124211243112441124511246112471124811249112501125111252112531125411255112561125711258112591126011261112621126311264112651126611267112681126911270112711127211273112741127511276112771127811279112801128111282112831128411285112861128711288112891129011291112921129311294112951129611297112981129911300113011130211303113041130511306113071130811309113101131111312113131131411315113161131711318113191132011321113221132311324113251132611327113281132911330113311133211333113341133511336113371133811339113401134111342113431134411345113461134711348113491135011351113521135311354113551135611357113581135911360113611136211363113641136511366113671136811369113701137111372113731137411375113761137711378113791138011381113821138311384113851138611387113881138911390113911139211393113941139511396113971139811399114001140111402114031140411405114061140711408114091141011411114121141311414114151141611417114181141911420114211142211423114241142511426114271142811429114301143111432114331143411435114361143711438114391144011441114421144311444114451144611447114481144911450114511145211453114541145511456114571145811459114601146111462114631146411465114661146711468114691147011471114721147311474114751147611477114781147911480114811148211483114841148511486114871148811489114901149111492114931149411495114961149711498114991150011501115021150311504115051150611507115081150911510115111151211513115141151511516115171151811519115201152111522115231152411525115261152711528115291153011531115321153311534115351153611537115381153911540115411154211543115441154511546115471154811549115501155111552115531155411555115561155711558115591156011561115621156311564115651156611567115681156911570115711157211573115741157511576115771157811579115801158111582115831158411585115861158711588115891159011591115921159311594115951159611597115981159911600116011160211603116041160511606116071160811609116101161111612116131161411615116161161711618116191162011621116221162311624116251162611627116281162911630116311163211633116341163511636116371163811639116401164111642116431164411645116461164711648116491165011651116521165311654116551165611657116581165911660116611166211663116641166511666116671166811669116701167111672116731167411675116761167711678116791168011681116821168311684116851168611687116881168911690116911169211693116941169511696116971169811699117001170111702117031170411705117061170711708117091171011711117121171311714117151171611717117181171911720117211172211723117241172511726117271172811729117301173111732117331173411735117361173711738117391174011741117421174311744117451174611747117481174911750117511175211753117541175511756117571175811759117601176111762117631176411765117661176711768117691177011771117721177311774117751177611777117781177911780117811178211783117841178511786117871178811789117901179111792117931179411795117961179711798117991180011801118021180311804118051180611807118081180911810118111181211813118141181511816118171181811819118201182111822118231182411825118261182711828118291183011831118321183311834118351183611837118381183911840118411184211843118441184511846118471184811849118501185111852118531185411855118561185711858118591186011861118621186311864118651186611867118681186911870118711187211873118741187511876118771187811879118801188111882118831188411885118861188711888118891189011891118921189311894118951189611897118981189911900119011190211903119041190511906119071190811909119101191111912119131191411915119161191711918119191192011921119221192311924119251192611927119281192911930119311193211933119341193511936119371193811939119401194111942119431194411945119461194711948119491195011951119521195311954119551195611957119581195911960119611196211963119641196511966119671196811969119701197111972119731197411975119761197711978119791198011981119821198311984119851198611987119881198911990119911199211993119941199511996119971199811999120001200112002120031200412005120061200712008120091201012011120121201312014120151201612017120181201912020120211202212023120241202512026120271202812029120301203112032120331203412035120361203712038120391204012041120421204312044120451204612047120481204912050120511205212053120541205512056120571205812059120601206112062120631206412065120661206712068120691207012071120721207312074120751207612077120781207912080120811208212083120841208512086120871208812089120901209112092120931209412095120961209712098120991210012101121021210312104121051210612107121081210912110121111211212113121141211512116121171211812119121201212112122121231212412125121261212712128121291213012131121321213312134121351213612137121381213912140121411214212143121441214512146121471214812149121501215112152121531215412155121561215712158121591216012161121621216312164121651216612167121681216912170121711217212173121741217512176121771217812179121801218112182121831218412185121861218712188121891219012191121921219312194121951219612197121981219912200122011220212203122041220512206122071220812209122101221112212122131221412215122161221712218122191222012221122221222312224122251222612227122281222912230122311223212233122341223512236122371223812239122401224112242122431224412245122461224712248122491225012251122521225312254122551225612257122581225912260122611226212263122641226512266122671226812269122701227112272122731227412275122761227712278122791228012281122821228312284122851228612287122881228912290122911229212293122941229512296122971229812299123001230112302123031230412305123061230712308123091231012311123121231312314123151231612317123181231912320123211232212323123241232512326123271232812329123301233112332123331233412335123361233712338123391234012341123421234312344123451234612347123481234912350123511235212353123541235512356123571235812359123601236112362123631236412365123661236712368123691237012371123721237312374123751237612377123781237912380123811238212383123841238512386123871238812389123901239112392123931239412395123961239712398123991240012401124021240312404124051240612407124081240912410124111241212413124141241512416124171241812419124201242112422124231242412425124261242712428124291243012431124321243312434124351243612437124381243912440124411244212443124441244512446124471244812449124501245112452124531245412455124561245712458124591246012461124621246312464124651246612467124681246912470124711247212473124741247512476124771247812479124801248112482124831248412485124861248712488124891249012491124921249312494124951249612497124981249912500125011250212503125041250512506125071250812509125101251112512125131251412515125161251712518125191252012521125221252312524125251252612527125281252912530125311253212533125341253512536125371253812539125401254112542125431254412545125461254712548125491255012551125521255312554125551255612557125581255912560125611256212563125641256512566125671256812569125701257112572125731257412575125761257712578125791258012581125821258312584125851258612587125881258912590125911259212593125941259512596125971259812599126001260112602126031260412605126061260712608126091261012611126121261312614126151261612617126181261912620126211262212623126241262512626126271262812629126301263112632126331263412635126361263712638126391264012641126421264312644126451264612647126481264912650126511265212653126541265512656126571265812659126601266112662126631266412665126661266712668126691267012671126721267312674126751267612677126781267912680126811268212683126841268512686126871268812689126901269112692126931269412695126961269712698126991270012701127021270312704127051270612707127081270912710127111271212713127141271512716127171271812719127201272112722127231272412725127261272712728127291273012731127321273312734127351273612737127381273912740127411274212743127441274512746127471274812749127501275112752127531275412755127561275712758127591276012761127621276312764127651276612767127681276912770127711277212773127741277512776127771277812779127801278112782127831278412785127861278712788127891279012791127921279312794127951279612797127981279912800128011280212803128041280512806128071280812809128101281112812128131281412815128161281712818128191282012821128221282312824128251282612827128281282912830128311283212833128341283512836128371283812839128401284112842128431284412845128461284712848128491285012851128521285312854128551285612857128581285912860128611286212863128641286512866128671286812869128701287112872128731287412875128761287712878128791288012881128821288312884128851288612887128881288912890128911289212893128941289512896128971289812899129001290112902129031290412905129061290712908129091291012911129121291312914129151291612917129181291912920129211292212923129241292512926129271292812929129301293112932129331293412935129361293712938129391294012941129421294312944129451294612947129481294912950129511295212953129541295512956129571295812959129601296112962129631296412965129661296712968129691297012971129721297312974129751297612977129781297912980129811298212983129841298512986129871298812989129901299112992129931299412995129961299712998129991300013001130021300313004130051300613007130081300913010130111301213013130141301513016130171301813019130201302113022130231302413025130261302713028130291303013031130321303313034130351303613037130381303913040130411304213043130441304513046130471304813049130501305113052130531305413055130561305713058130591306013061130621306313064130651306613067130681306913070130711307213073130741307513076130771307813079130801308113082130831308413085130861308713088130891309013091130921309313094130951309613097130981309913100131011310213103131041310513106131071310813109131101311113112131131311413115131161311713118131191312013121131221312313124131251312613127131281312913130131311313213133131341313513136131371313813139131401314113142131431314413145131461314713148131491315013151131521315313154131551315613157131581315913160131611316213163131641316513166131671316813169131701317113172131731317413175131761317713178131791318013181131821318313184131851318613187131881318913190131911319213193131941319513196131971319813199132001320113202132031320413205132061320713208132091321013211132121321313214132151321613217132181321913220132211322213223132241322513226132271322813229132301323113232132331323413235132361323713238132391324013241132421324313244132451324613247132481324913250132511325213253132541325513256132571325813259132601326113262
  1. /**
  2. * crafty 0.6.2
  3. * http://craftyjs.com/
  4. *
  5. * Copyright 2014, Louis Stowasser
  6. * Dual licensed under the MIT or GPL licenses.
  7. */
  8. ;(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
  9. var Crafty = require('./core.js'),
  10. document = window.document,
  11. HashMap = require('./HashMap.js');
  12. // Crafty._rectPool
  13. //
  14. // This is a private object used internally by 2D methods
  15. // Cascade and _attr need to keep track of an entity's old position,
  16. // but we want to avoid creating temp objects every time an attribute is set.
  17. // The solution is to have a pool of objects that can be reused.
  18. //
  19. // The current implementation makes a BIG ASSUMPTION: that if multiple rectangles are requested,
  20. // the later one is recycled before any preceding ones. This matches how they are used in the code.
  21. // Each rect is created by a triggered event, and will be recycled by the time the event is complete.
  22. Crafty._rectPool = (function () {
  23. var pool = [],
  24. pointer = 0;
  25. return {
  26. get: function (x, y, w, h) {
  27. if (pool.length <= pointer)
  28. pool.push({});
  29. var r = pool[pointer++];
  30. r._x = x;
  31. r._y = y;
  32. r._w = w;
  33. r._h = h;
  34. return r;
  35. },
  36. copy: function (o) {
  37. if (pool.length <= pointer)
  38. pool.push({});
  39. var r = pool[pointer++];
  40. r._x = o._x;
  41. r._y = o._y;
  42. r._w = o._w;
  43. r._h = o._h;
  44. return r;
  45. },
  46. recycle: function (o) {
  47. pointer--;
  48. }
  49. };
  50. })();
  51. /**@
  52. * #Crafty.map
  53. * @category 2D
  54. * Functions related with querying entities.
  55. * @see Crafty.HashMap
  56. */
  57. Crafty.map = new HashMap();
  58. var M = Math,
  59. Mc = M.cos,
  60. Ms = M.sin,
  61. PI = M.PI,
  62. DEG_TO_RAD = PI / 180;
  63. Crafty.extend({
  64. zeroFill: function (number, width) {
  65. width -= number.toString().length;
  66. if (width > 0)
  67. return new Array(width + (/\./.test(number) ? 2 : 1)).join('0') + number;
  68. return number.toString();
  69. }
  70. });
  71. /**@
  72. * #2D
  73. * @category 2D
  74. * Component for any entity that has a position on the stage.
  75. * @trigger Move - when the entity has moved - { _x:Number, _y:Number, _w:Number, _h:Number } - Old position
  76. * @trigger Invalidate - when the entity needs to be redrawn
  77. * @trigger Rotate - when the entity is rotated - { cos:Number, sin:Number, deg:Number, rad:Number, o: {x:Number, y:Number}}
  78. */
  79. Crafty.c("2D", {
  80. /**@
  81. * #.x
  82. * @comp 2D
  83. * The `x` position on the stage. When modified, will automatically be redrawn.
  84. * Is actually a getter/setter so when using this value for calculations and not modifying it,
  85. * use the `._x` property.
  86. * @see ._attr
  87. */
  88. _x: 0,
  89. /**@
  90. * #.y
  91. * @comp 2D
  92. * The `y` position on the stage. When modified, will automatically be redrawn.
  93. * Is actually a getter/setter so when using this value for calculations and not modifying it,
  94. * use the `._y` property.
  95. * @see ._attr
  96. */
  97. _y: 0,
  98. /**@
  99. * #.w
  100. * @comp 2D
  101. * The width of the entity. When modified, will automatically be redrawn.
  102. * Is actually a getter/setter so when using this value for calculations and not modifying it,
  103. * use the `._w` property.
  104. *
  105. * Changing this value is not recommended as canvas has terrible resize quality and DOM will just clip the image.
  106. * @see ._attr
  107. */
  108. _w: 0,
  109. /**@
  110. * #.h
  111. * @comp 2D
  112. * The height of the entity. When modified, will automatically be redrawn.
  113. * Is actually a getter/setter so when using this value for calculations and not modifying it,
  114. * use the `._h` property.
  115. *
  116. * Changing this value is not recommended as canvas has terrible resize quality and DOM will just clip the image.
  117. * @see ._attr
  118. */
  119. _h: 0,
  120. /**@
  121. * #.z
  122. * @comp 2D
  123. * The `z` index on the stage. When modified, will automatically be redrawn.
  124. * Is actually a getter/setter so when using this value for calculations and not modifying it,
  125. * use the `._z` property.
  126. *
  127. * A higher `z` value will be closer to the front of the stage. A smaller `z` value will be closer to the back.
  128. * A global Z index is produced based on its `z` value as well as the GID (which entity was created first).
  129. * Therefore entities will naturally maintain order depending on when it was created if same z value.
  130. *
  131. * `z` is required to be an integer, e.g. `z=11.2` is not allowed.
  132. * @see ._attr
  133. */
  134. _z: 0,
  135. /**@
  136. * #.rotation
  137. * @comp 2D
  138. * The rotation state of the entity, in clockwise degrees.
  139. * `this.rotation = 0` sets it to its original orientation; `this.rotation = 10`
  140. * sets it to 10 degrees clockwise from its original orientation;
  141. * `this.rotation = -10` sets it to 10 degrees counterclockwise from its
  142. * original orientation, etc.
  143. *
  144. * When modified, will automatically be redrawn. Is actually a getter/setter
  145. * so when using this value for calculations and not modifying it,
  146. * use the `._rotation` property.
  147. *
  148. * `this.rotation = 0` does the same thing as `this.rotation = 360` or `720` or
  149. * `-360` or `36000` etc. So you can keep increasing or decreasing the angle for continuous
  150. * rotation. (Numerical errors do not occur until you get to millions of degrees.)
  151. *
  152. * The default is to rotate the entity around its (initial) top-left corner; use
  153. * `.origin()` to change that.
  154. *
  155. * @see ._attr, .origin
  156. */
  157. _rotation: 0,
  158. /**@
  159. * #.alpha
  160. * @comp 2D
  161. * Transparency of an entity. Must be a decimal value between 0.0 being fully transparent to 1.0 being fully opaque.
  162. */
  163. _alpha: 1.0,
  164. /**@
  165. * #.visible
  166. * @comp 2D
  167. * If the entity is visible or not. Accepts a true or false value.
  168. * Can be used for optimization by setting an entities visibility to false when not needed to be drawn.
  169. *
  170. * The entity will still exist and can be collided with but just won't be drawn.
  171. * @see Crafty.DrawManager.draw, Crafty.DrawManager.drawAll
  172. */
  173. _visible: true,
  174. /**@
  175. * #._globalZ
  176. * @comp 2D
  177. * When two entities overlap, the one with the larger `_globalZ` will be on top of the other.
  178. * @see Crafty.DrawManager.draw, Crafty.DrawManager.drawAll
  179. */
  180. _globalZ: null,
  181. _origin: null,
  182. _mbr: null,
  183. _entry: null,
  184. _children: null,
  185. _parent: null,
  186. _changed: false,
  187. _defineGetterSetter_setter: function () {
  188. //create getters and setters using __defineSetter__ and __defineGetter__
  189. this.__defineSetter__('x', function (v) {
  190. this._attr('_x', v);
  191. });
  192. this.__defineSetter__('y', function (v) {
  193. this._attr('_y', v);
  194. });
  195. this.__defineSetter__('w', function (v) {
  196. this._attr('_w', v);
  197. });
  198. this.__defineSetter__('h', function (v) {
  199. this._attr('_h', v);
  200. });
  201. this.__defineSetter__('z', function (v) {
  202. this._attr('_z', v);
  203. });
  204. this.__defineSetter__('rotation', function (v) {
  205. this._attr('_rotation', v);
  206. });
  207. this.__defineSetter__('alpha', function (v) {
  208. this._attr('_alpha', v);
  209. });
  210. this.__defineSetter__('visible', function (v) {
  211. this._attr('_visible', v);
  212. });
  213. this.__defineGetter__('x', function () {
  214. return this._x;
  215. });
  216. this.__defineGetter__('y', function () {
  217. return this._y;
  218. });
  219. this.__defineGetter__('w', function () {
  220. return this._w;
  221. });
  222. this.__defineGetter__('h', function () {
  223. return this._h;
  224. });
  225. this.__defineGetter__('z', function () {
  226. return this._z;
  227. });
  228. this.__defineGetter__('rotation', function () {
  229. return this._rotation;
  230. });
  231. this.__defineGetter__('alpha', function () {
  232. return this._alpha;
  233. });
  234. this.__defineGetter__('visible', function () {
  235. return this._visible;
  236. });
  237. this.__defineGetter__('parent', function () {
  238. return this._parent;
  239. });
  240. this.__defineGetter__('numChildren', function () {
  241. return this._children.length;
  242. });
  243. },
  244. _defineGetterSetter_defineProperty: function () {
  245. Object.defineProperty(this, 'x', {
  246. set: function (v) {
  247. this._attr('_x', v);
  248. },
  249. get: function () {
  250. return this._x;
  251. },
  252. configurable: true
  253. });
  254. Object.defineProperty(this, 'y', {
  255. set: function (v) {
  256. this._attr('_y', v);
  257. },
  258. get: function () {
  259. return this._y;
  260. },
  261. configurable: true
  262. });
  263. Object.defineProperty(this, 'w', {
  264. set: function (v) {
  265. this._attr('_w', v);
  266. },
  267. get: function () {
  268. return this._w;
  269. },
  270. configurable: true
  271. });
  272. Object.defineProperty(this, 'h', {
  273. set: function (v) {
  274. this._attr('_h', v);
  275. },
  276. get: function () {
  277. return this._h;
  278. },
  279. configurable: true
  280. });
  281. Object.defineProperty(this, 'z', {
  282. set: function (v) {
  283. this._attr('_z', v);
  284. },
  285. get: function () {
  286. return this._z;
  287. },
  288. configurable: true
  289. });
  290. Object.defineProperty(this, 'rotation', {
  291. set: function (v) {
  292. this._attr('_rotation', v);
  293. },
  294. get: function () {
  295. return this._rotation;
  296. },
  297. configurable: true
  298. });
  299. Object.defineProperty(this, 'alpha', {
  300. set: function (v) {
  301. this._attr('_alpha', v);
  302. },
  303. get: function () {
  304. return this._alpha;
  305. },
  306. configurable: true
  307. });
  308. Object.defineProperty(this, 'visible', {
  309. set: function (v) {
  310. this._attr('_visible', v);
  311. },
  312. get: function () {
  313. return this._visible;
  314. },
  315. configurable: true
  316. });
  317. },
  318. init: function () {
  319. this._globalZ = this[0];
  320. this._origin = {
  321. x: 0,
  322. y: 0
  323. };
  324. // offsets for the basic bounding box
  325. this._bx1 = 0;
  326. this._bx2 = 0;
  327. this._by1 = 0;
  328. this._by2 = 0;
  329. this._children = [];
  330. if (Crafty.support.setter) {
  331. this._defineGetterSetter_setter();
  332. } else if (Crafty.support.defineProperty) {
  333. //IE9 supports Object.defineProperty
  334. this._defineGetterSetter_defineProperty();
  335. }
  336. //insert self into the HashMap
  337. this._entry = Crafty.map.insert(this);
  338. //when object changes, update HashMap
  339. this.bind("Move", function (e) {
  340. // Choose the largest bounding region that exists
  341. var area = this._cbr || this._mbr || this;
  342. this._entry.update(area);
  343. // Move children (if any) by the same amount
  344. if (this._children.length > 0) {
  345. this._cascade(e);
  346. }
  347. });
  348. this.bind("Rotate", function (e) {
  349. // Choose the largest bounding region that exists
  350. var old = this._cbr || this._mbr || this;
  351. this._entry.update(old);
  352. // Rotate children (if any) by the same amount
  353. if (this._children.length > 0) {
  354. this._cascade(e);
  355. }
  356. });
  357. //when object is removed, remove from HashMap and destroy attached children
  358. this.bind("Remove", function () {
  359. if (this._children) {
  360. for (var i = 0; i < this._children.length; i++) {
  361. // delete the child's _parent link, or else the child will splice itself out of
  362. // this._children while destroying itself (which messes up this for-loop iteration).
  363. delete this._children[i]._parent;
  364. // Destroy child if possible (It's not always possible, e.g. the polygon attached
  365. // by areaMap has no .destroy(), it will just get garbage-collected.)
  366. if (this._children[i].destroy) {
  367. this._children[i].destroy();
  368. }
  369. }
  370. this._children = [];
  371. }
  372. if (this._parent) {
  373. this._parent.detach(this);
  374. }
  375. Crafty.map.remove(this);
  376. this.detach();
  377. });
  378. },
  379. /**@
  380. * #.offsetBoundary
  381. * @comp 2D
  382. * Extends the MBR of the entity by a specified amount.
  383. *
  384. * @trigger BoundaryOffset - when the MBR offset changes
  385. * @sign public this .offsetBoundary(Number dx1, Number dy1, Number dx2, Number dy2)
  386. * @param dx1 - Extends the MBR to the left by this amount
  387. * @param dy1 - Extends the MBR upward by this amount
  388. * @param dx2 - Extends the MBR to the right by this amount
  389. * @param dy2 - Extends the MBR downward by this amount
  390. *
  391. * @sign public this .offsetBoundary(Number offset)
  392. * @param offset - Extend the MBR in all directions by this amount
  393. *
  394. * You would most likely use this function to ensure that custom canvas rendering beyond the extent of the entity's normal bounds is not clipped.
  395. */
  396. offsetBoundary: function(x1, y1, x2, y2){
  397. if (arguments.length === 1)
  398. y1 = x2 = y2 = x1;
  399. this._bx1 = x1;
  400. this._bx2 = x2;
  401. this._by1 = y1;
  402. this._by2 = y2;
  403. this.trigger("BoundaryOffset");
  404. this._calculateMBR();
  405. return this;
  406. },
  407. /**
  408. * Calculates the MBR when rotated some number of radians about an origin point o.
  409. * Necessary on a rotation, or a resize
  410. */
  411. _calculateMBR: function () {
  412. var ox = this._origin.x + this._x,
  413. oy = this._origin.y + this._y,
  414. rad = -this._rotation * DEG_TO_RAD;
  415. // axis-aligned (unrotated) coordinates, relative to the origin point
  416. var dx1 = this._x - this._bx1 - ox,
  417. dx2 = this._x + this._w + this._bx2 - ox,
  418. dy1 = this._y - this._by1 - oy,
  419. dy2 = this._y + this._h + this._by2 - oy;
  420. var ct = Math.cos(rad),
  421. st = Math.sin(rad);
  422. // Special case 90 degree rotations to prevent rounding problems
  423. ct = (ct < 1e-10 && ct > -1e-10) ? 0 : ct;
  424. st = (st < 1e-10 && st > -1e-10) ? 0 : st;
  425. // Calculate the new points relative to the origin, then find the new (absolute) bounding coordinates!
  426. var x0 = dx1 * ct + dy1 * st,
  427. y0 = - dx1 * st + dy1 * ct,
  428. x1 = dx2 * ct + dy1 * st,
  429. y1 = - dx2 * st + dy1 * ct,
  430. x2 = dx2 * ct + dy2 * st,
  431. y2 = - dx2 * st + dy2 * ct,
  432. x3 = dx1 * ct + dy2 * st,
  433. y3 = - dx1 * st + dy2 * ct,
  434. minx = Math.floor(Math.min(x0, x1, x2, x3) + ox),
  435. miny = Math.floor(Math.min(y0, y1, y2, y3) + oy),
  436. maxx = Math.ceil(Math.max(x0, x1, x2, x3) + ox),
  437. maxy = Math.ceil(Math.max(y0, y1, y2, y3) + oy);
  438. if (!this._mbr) {
  439. this._mbr = {
  440. _x: minx,
  441. _y: miny,
  442. _w: maxx - minx,
  443. _h: maxy - miny
  444. };
  445. } else {
  446. this._mbr._x = minx;
  447. this._mbr._y = miny;
  448. this._mbr._w = maxx - minx;
  449. this._mbr._h = maxy - miny;
  450. }
  451. // If a collision hitbox exists AND sits outside the entity, find a bounding box for both.
  452. // `_cbr` contains information about a bounding circle of the hitbox.
  453. // The bounds of `_cbr` will be the union of the `_mbr` and the bounding box of that circle.
  454. // This will not be a minimal region, but since it's only used for the broad phase pass it's good enough.
  455. //
  456. // cbr is calculated by the `_checkBounds` method of the "Collision" component
  457. if (this._cbr) {
  458. var cbr = this._cbr;
  459. var cx = cbr.cx, cy = cbr.cy, r = cbr.r;
  460. var cx2 = ox + (cx + this._x - ox) * ct + (cy + this._y - oy) * st;
  461. var cy2 = oy - (cx + this._x - ox) * st + (cy + this._y - oy) * ct;
  462. cbr._x = Math.min(cx2 - r, minx);
  463. cbr._y = Math.min(cy2 - r, miny);
  464. cbr._w = Math.max(cx2 + r, maxx) - cbr._x;
  465. cbr._h = Math.max(cy2 + r, maxy) - cbr._y;
  466. }
  467. },
  468. /**
  469. * Handle changes that need to happen on a rotation
  470. */
  471. _rotate: function (v) {
  472. var theta = -1 * (v % 360); //angle always between 0 and 359
  473. var difference = this._rotation - v;
  474. // skip if there's no rotation!
  475. if (difference === 0)
  476. return;
  477. else
  478. this._rotation = v;
  479. //Calculate the new MBR
  480. var rad = theta * DEG_TO_RAD,
  481. o = {
  482. x: this._origin.x + this._x,
  483. y: this._origin.y + this._y
  484. };
  485. this._calculateMBR();
  486. //trigger "Rotate" event
  487. var drad = difference * DEG_TO_RAD,
  488. ct = Math.cos(rad),
  489. st = Math.sin(rad);
  490. this.trigger("Rotate", {
  491. cos: Math.cos(drad),
  492. sin: Math.sin(drad),
  493. deg: difference,
  494. rad: drad,
  495. o: o
  496. });
  497. },
  498. /**@
  499. * #.area
  500. * @comp 2D
  501. * @sign public Number .area(void)
  502. * Calculates the area of the entity
  503. */
  504. area: function () {
  505. return this._w * this._h;
  506. },
  507. /**@
  508. * #.intersect
  509. * @comp 2D
  510. * @sign public Boolean .intersect(Number x, Number y, Number w, Number h)
  511. * @param x - X position of the rect
  512. * @param y - Y position of the rect
  513. * @param w - Width of the rect
  514. * @param h - Height of the rect
  515. * @sign public Boolean .intersect(Object rect)
  516. * @param rect - An object that must have the `x, y, w, h` values as properties
  517. * Determines if this entity intersects a rectangle. If the entity is rotated, its MBR is used for the test.
  518. */
  519. intersect: function (x, y, w, h) {
  520. var rect, mbr = this._mbr || this;
  521. if (typeof x === "object") {
  522. rect = x;
  523. } else {
  524. rect = {
  525. x: x,
  526. y: y,
  527. w: w,
  528. h: h
  529. };
  530. }
  531. return mbr._x < rect.x + rect.w && mbr._x + mbr._w > rect.x &&
  532. mbr._y < rect.y + rect.h && mbr._h + mbr._y > rect.y;
  533. },
  534. /**@
  535. * #.within
  536. * @comp 2D
  537. * @sign public Boolean .within(Number x, Number y, Number w, Number h)
  538. * @param x - X position of the rect
  539. * @param y - Y position of the rect
  540. * @param w - Width of the rect
  541. * @param h - Height of the rect
  542. * @sign public Boolean .within(Object rect)
  543. * @param rect - An object that must have the `_x, _y, _w, _h` values as properties
  544. * Determines if this current entity is within another rectangle.
  545. */
  546. within: function (x, y, w, h) {
  547. var rect, mbr = this._mbr || this;
  548. if (typeof x === "object") {
  549. rect = x;
  550. } else {
  551. rect = {
  552. _x: x,
  553. _y: y,
  554. _w: w,
  555. _h: h
  556. };
  557. }
  558. return rect._x <= mbr._x && rect._x + rect._w >= mbr._x + mbr._w &&
  559. rect._y <= mbr._y && rect._y + rect._h >= mbr._y + mbr._h;
  560. },
  561. /**@
  562. * #.contains
  563. * @comp 2D
  564. * @sign public Boolean .contains(Number x, Number y, Number w, Number h)
  565. * @param x - X position of the rect
  566. * @param y - Y position of the rect
  567. * @param w - Width of the rect
  568. * @param h - Height of the rect
  569. * @sign public Boolean .contains(Object rect)
  570. * @param rect - An object that must have the `_x, _y, _w, _h` values as properties.
  571. * Determines if the rectangle is within the current entity. If the entity is rotated, its MBR is used for the test.
  572. */
  573. contains: function (x, y, w, h) {
  574. var rect, mbr = this._mbr || this;
  575. if (typeof x === "object") {
  576. rect = x;
  577. } else {
  578. rect = {
  579. _x: x,
  580. _y: y,
  581. _w: w,
  582. _h: h
  583. };
  584. }
  585. return rect._x >= mbr._x && rect._x + rect._w <= mbr._x + mbr._w &&
  586. rect._y >= mbr._y && rect._y + rect._h <= mbr._y + mbr._h;
  587. },
  588. /**@
  589. * #.pos
  590. * @comp 2D
  591. * @sign public Object .pos(void)
  592. * Returns the x, y, w, h properties as a rect object
  593. * (a rect object is just an object with the keys _x, _y, _w, _h).
  594. *
  595. * The keys have an underscore prefix. This is due to the x, y, w, h
  596. * properties being merely setters and getters that wrap the properties with an underscore (_x, _y, _w, _h).
  597. */
  598. pos: function () {
  599. return {
  600. _x: (this._x),
  601. _y: (this._y),
  602. _w: (this._w),
  603. _h: (this._h)
  604. };
  605. },
  606. /**@
  607. * #.mbr
  608. * @comp 2D
  609. * @sign public Object .mbr()
  610. * Returns the minimum bounding rectangle. If there is no rotation
  611. * on the entity it will return the rect.
  612. */
  613. mbr: function () {
  614. if (!this._mbr) return this.pos();
  615. return {
  616. _x: (this._mbr._x),
  617. _y: (this._mbr._y),
  618. _w: (this._mbr._w),
  619. _h: (this._mbr._h)
  620. };
  621. },
  622. /**@
  623. * #.isAt
  624. * @comp 2D
  625. * @sign public Boolean .isAt(Number x, Number y)
  626. * @param x - X position of the point
  627. * @param y - Y position of the point
  628. * Determines whether a point is contained by the entity. Unlike other methods,
  629. * an object can't be passed. The arguments require the x and y value.
  630. *
  631. * The given point is tested against the first of the following that exists: a mapArea associated with "Mouse", the hitarea associated with "Collision", or the object's MBR.
  632. */
  633. isAt: function (x, y) {
  634. if (this.mapArea) {
  635. return this.mapArea.containsPoint(x, y);
  636. } else if (this.map) {
  637. return this.map.containsPoint(x, y);
  638. }
  639. var mbr = this._mbr || this;
  640. return mbr._x <= x && mbr._x + mbr._w >= x &&
  641. mbr._y <= y && mbr._y + mbr._h >= y;
  642. },
  643. /**@
  644. * #.move
  645. * @comp 2D
  646. * @sign public this .move(String dir, Number by)
  647. * @param dir - Direction to move (n,s,e,w,ne,nw,se,sw)
  648. * @param by - Amount to move in the specified direction
  649. * Quick method to move the entity in a direction (n, s, e, w, ne, nw, se, sw) by an amount of pixels.
  650. */
  651. move: function (dir, by) {
  652. if (dir.charAt(0) === 'n') this.y -= by;
  653. if (dir.charAt(0) === 's') this.y += by;
  654. if (dir === 'e' || dir.charAt(1) === 'e') this.x += by;
  655. if (dir === 'w' || dir.charAt(1) === 'w') this.x -= by;
  656. return this;
  657. },
  658. /**@
  659. * #.shift
  660. * @comp 2D
  661. * @sign public this .shift(Number x, Number y, Number w, Number h)
  662. * @param x - Amount to move X
  663. * @param y - Amount to move Y
  664. * @param w - Amount to widen
  665. * @param h - Amount to increase height
  666. * Shift or move the entity by an amount. Use negative values
  667. * for an opposite direction.
  668. */
  669. shift: function (x, y, w, h) {
  670. if (x) this.x += x;
  671. if (y) this.y += y;
  672. if (w) this.w += w;
  673. if (h) this.h += h;
  674. return this;
  675. },
  676. /**@
  677. * #._cascade
  678. * @comp 2D
  679. * @sign public void ._cascade(e)
  680. * @param e - An object describing the motion
  681. * Move or rotate the entity's children according to a certain motion.
  682. * This method is part of a function bound to "Move": It is used
  683. * internally for ensuring that when a parent moves, the child also
  684. * moves in the same way.
  685. */
  686. _cascade: function (e) {
  687. if (!e) return; //no change in position
  688. var i = 0,
  689. children = this._children,
  690. l = children.length,
  691. obj;
  692. //rotation
  693. if (e.cos) {
  694. for (; i < l; ++i) {
  695. obj = children[i];
  696. if ('rotate' in obj) obj.rotate(e);
  697. }
  698. } else {
  699. //use current position
  700. var dx = this._x - e._x,
  701. dy = this._y - e._y,
  702. dw = this._w - e._w,
  703. dh = this._h - e._h;
  704. for (; i < l; ++i) {
  705. obj = children[i];
  706. obj.shift(dx, dy, dw, dh);
  707. }
  708. }
  709. },
  710. /**@
  711. * #.attach
  712. * @comp 2D
  713. * @sign public this .attach(Entity obj[, .., Entity objN])
  714. * @param obj - Child entity(s) to attach
  715. * Sets one or more entities to be children, with the current entity (`this`)
  716. * as the parent. When the parent moves or rotates, its children move or
  717. * rotate by the same amount. (But not vice-versa: If you move a child, it
  718. * will not move the parent.) When the parent is destroyed, its children are
  719. * destroyed.
  720. *
  721. * For any entity, `this._children` is the array of its children entity
  722. * objects (if any), and `this._parent` is its parent entity object (if any).
  723. *
  724. * As many objects as wanted can be attached, and a hierarchy of objects is
  725. * possible by attaching.
  726. */
  727. attach: function () {
  728. var i = 0,
  729. arg = arguments,
  730. l = arguments.length,
  731. obj;
  732. for (; i < l; ++i) {
  733. obj = arg[i];
  734. if (obj._parent) {
  735. obj._parent.detach(obj);
  736. }
  737. obj._parent = this;
  738. this._children.push(obj);
  739. }
  740. return this;
  741. },
  742. /**@
  743. * #.detach
  744. * @comp 2D
  745. * @sign public this .detach([Entity obj])
  746. * @param obj - The entity to detach. Left blank will remove all attached entities
  747. * Stop an entity from following the current entity. Passing no arguments will stop
  748. * every entity attached.
  749. */
  750. detach: function (obj) {
  751. var i;
  752. //if nothing passed, remove all attached objects
  753. if (!obj) {
  754. for (i = 0; i < this._children.length; i++) {
  755. this._children[i]._parent = null;
  756. }
  757. this._children = [];
  758. return this;
  759. }
  760. //if obj passed, find the handler and unbind
  761. for (i = 0; i < this._children.length; i++) {
  762. if (this._children[i] == obj) {
  763. this._children.splice(i, 1);
  764. }
  765. }
  766. obj._parent = null;
  767. return this;
  768. },
  769. /**@
  770. * #.origin
  771. * @comp 2D
  772. * @sign public this .origin(Number x, Number y)
  773. * @param x - Pixel value of origin offset on the X axis
  774. * @param y - Pixel value of origin offset on the Y axis
  775. * @sign public this .origin(String offset)
  776. * @param offset - Combination of center, top, bottom, middle, left and right
  777. * Set the origin point of an entity for it to rotate around.
  778. *
  779. * @example
  780. * ~~~
  781. * this.origin("top left")
  782. * this.origin("center")
  783. * this.origin("bottom right")
  784. * this.origin("middle right")
  785. * ~~~
  786. *
  787. * @see .rotation
  788. */
  789. origin: function (x, y) {
  790. //text based origin
  791. if (typeof x === "string") {
  792. if (x === "centre" || x === "center" || x.indexOf(' ') === -1) {
  793. x = this._w / 2;
  794. y = this._h / 2;
  795. } else {
  796. var cmd = x.split(' ');
  797. if (cmd[0] === "top") y = 0;
  798. else if (cmd[0] === "bottom") y = this._h;
  799. else if (cmd[0] === "middle" || cmd[1] === "center" || cmd[1] === "centre") y = this._h / 2;
  800. if (cmd[1] === "center" || cmd[1] === "centre" || cmd[1] === "middle") x = this._w / 2;
  801. else if (cmd[1] === "left") x = 0;
  802. else if (cmd[1] === "right") x = this._w;
  803. }
  804. }
  805. this._origin.x = x;
  806. this._origin.y = y;
  807. return this;
  808. },
  809. /**@
  810. * #.flip
  811. * @comp 2D
  812. * @trigger Invalidate - when the entity has flipped
  813. * @sign public this .flip(String dir)
  814. * @param dir - Flip direction
  815. *
  816. * Flip entity on passed direction
  817. *
  818. * @example
  819. * ~~~
  820. * this.flip("X")
  821. * ~~~
  822. */
  823. flip: function (dir) {
  824. dir = dir || "X";
  825. if (!this["_flip" + dir]) {
  826. this["_flip" + dir] = true;
  827. this.trigger("Invalidate");
  828. }
  829. return this;
  830. },
  831. /**@
  832. * #.unflip
  833. * @comp 2D
  834. * @trigger Invalidate - when the entity has unflipped
  835. * @sign public this .unflip(String dir)
  836. * @param dir - Unflip direction
  837. *
  838. * Unflip entity on passed direction (if it's flipped)
  839. *
  840. * @example
  841. * ~~~
  842. * this.unflip("X")
  843. * ~~~
  844. */
  845. unflip: function (dir) {
  846. dir = dir || "X";
  847. if (this["_flip" + dir]) {
  848. this["_flip" + dir] = false;
  849. this.trigger("Invalidate");
  850. }
  851. return this;
  852. },
  853. /**
  854. * Method for rotation rather than through a setter
  855. */
  856. rotate: function (e) {
  857. var x2, y2;
  858. x2 = (this._x + this._origin.x - e.o.x) * e.cos + (this._y + this._origin.y - e.o.y) * e.sin + (e.o.x - this._origin.x);
  859. y2 = (this._y + this._origin.y - e.o.y) * e.cos - (this._x + this._origin.x - e.o.x) * e.sin + (e.o.y - this._origin.y);
  860. this._attr('_rotation', this._rotation - e.deg);
  861. this._attr('_x', x2 );
  862. this._attr('_y', y2 );
  863. },
  864. /**@
  865. * #._attr
  866. * @comp 2D
  867. * Setter method for all 2D properties including
  868. * x, y, w, h, alpha, rotation and visible.
  869. */
  870. _attr: function (name, value) {
  871. // Return if there is no change
  872. if (this[name] === value) {
  873. return;
  874. }
  875. //keep a reference of the old positions
  876. var old = Crafty._rectPool.copy(this);
  877. var mbr;
  878. //if rotation, use the rotate method
  879. if (name === '_rotation') {
  880. this._rotate(value); // _rotate triggers "Rotate"
  881. //set the global Z and trigger reorder just in case
  882. } else if (name === '_z') {
  883. this._globalZ = parseInt(value + Crafty.zeroFill(this[0], 5), 10); //magic number 10^5 is the max num of entities
  884. this.trigger("reorder");
  885. //if the rect bounds change, update the MBR and trigger move
  886. } else if (name === '_x' || name === '_y') {
  887. // mbr is the minimal bounding rectangle of the entity
  888. mbr = this._mbr;
  889. if (mbr) {
  890. mbr[name] -= this[name] - value;
  891. // cbr is a non-minmal bounding rectangle that contains both hitbox and mbr
  892. // It will exist only when the collision hitbox sits outside the entity
  893. if (this._cbr){
  894. this._cbr[name] -= this[name] - value;
  895. }
  896. }
  897. this[name] = value;
  898. this.trigger("Move", old);
  899. } else if (name === '_h' || name === '_w') {
  900. mbr = this._mbr;
  901. var oldValue = this[name];
  902. this[name] = value;
  903. if (mbr) {
  904. this._calculateMBR();
  905. }
  906. if (name === '_w') {
  907. this.trigger("Resize", {
  908. axis: 'w',
  909. amount: value - oldValue
  910. });
  911. } else if (name === '_h') {
  912. this.trigger("Resize", {
  913. axis: 'h',
  914. amount: value - oldValue
  915. });
  916. }
  917. this.trigger("Move", old);
  918. }
  919. //everything will assume the value
  920. this[name] = value;
  921. // flag for redraw
  922. this.trigger("Invalidate");
  923. Crafty._rectPool.recycle(old);
  924. }
  925. });
  926. /**@
  927. * #Gravity
  928. * @category 2D
  929. * @trigger Moved - When entity has moved on y-axis a Moved event is triggered with an object specifying the old position {x: old_x, y: old_y}
  930. *
  931. * Adds gravitational pull to the entity.
  932. */
  933. Crafty.c("Gravity", {
  934. _gravityConst: 0.2,
  935. _gy: 0,
  936. _falling: true,
  937. _anti: null,
  938. init: function () {
  939. this.requires("2D");
  940. },
  941. /**@
  942. * #.gravity
  943. * @comp Gravity
  944. * @sign public this .gravity([comp])
  945. * @param comp - The name of a component that will stop this entity from falling
  946. *
  947. * Enable gravity for this entity no matter whether comp parameter is not specified,
  948. * If comp parameter is specified all entities with that component will stop this entity from falling.
  949. * For a player entity in a platform game this would be a component that is added to all entities
  950. * that the player should be able to walk on.
  951. *
  952. * @example
  953. * ~~~
  954. * Crafty.e("2D, DOM, Color, Gravity")
  955. * .color("red")
  956. * .attr({ w: 100, h: 100 })
  957. * .gravity("platform");
  958. * ~~~
  959. */
  960. gravity: function (comp) {
  961. if (comp) this._anti = comp;
  962. if(isNaN(this._jumpSpeed)) this._jumpSpeed = 0; //set to 0 if Twoway component is not present
  963. this.bind("EnterFrame", this._enterFrame);
  964. return this;
  965. },
  966. /**@
  967. * #.gravityConst
  968. * @comp Gravity
  969. * @sign public this .gravityConst(g)
  970. * @param g - gravitational constant
  971. *
  972. * Set the gravitational constant to g. The default is .2. The greater g, the faster the object falls.
  973. *
  974. * @example
  975. * ~~~
  976. * Crafty.e("2D, DOM, Color, Gravity")
  977. * .color("red")
  978. * .attr({ w: 100, h: 100 })
  979. * .gravity("platform")
  980. * .gravityConst(2)
  981. * ~~~
  982. */
  983. gravityConst: function (g) {
  984. this._gravityConst = g;
  985. return this;
  986. },
  987. _enterFrame: function () {
  988. if (this._falling) {
  989. //if falling, move the players Y
  990. this._gy += this._gravityConst;
  991. this.y += this._gy;
  992. this.trigger('Moved', { x: this._x, y: this._y - this._gy });
  993. } else {
  994. this._gy = 0; //reset change in y
  995. }
  996. var obj, hit = false,
  997. pos = this.pos(),
  998. q, i = 0,
  999. l;
  1000. //Increase by 1 to make sure map.search() finds the floor
  1001. pos._y++;
  1002. //map.search wants _x and intersect wants x...
  1003. pos.x = pos._x;
  1004. pos.y = pos._y;
  1005. pos.w = pos._w;
  1006. pos.h = pos._h;
  1007. q = Crafty.map.search(pos);
  1008. l = q.length;
  1009. for (; i < l; ++i) {
  1010. obj = q[i];
  1011. //check for an intersection directly below the player
  1012. if (obj !== this && obj.has(this._anti) && obj.intersect(pos)) {
  1013. hit = obj;
  1014. break;
  1015. }
  1016. }
  1017. if (hit) { //stop falling if found and player is moving down
  1018. if (this._falling && ((this._gy > this._jumpSpeed) || !this._up)){
  1019. this.stopFalling(hit);
  1020. }
  1021. } else {
  1022. this._falling = true; //keep falling otherwise
  1023. }
  1024. },
  1025. stopFalling: function (e) {
  1026. if (e) this.y = e._y - this._h; //move object
  1027. //this._gy = -1 * this._bounce;
  1028. this._falling = false;
  1029. if (this._up) this._up = false;
  1030. this.trigger("hit");
  1031. },
  1032. /**@
  1033. * #.antigravity
  1034. * @comp Gravity
  1035. * @sign public this .antigravity()
  1036. * Disable gravity for this component. It can be reenabled by calling .gravity()
  1037. */
  1038. antigravity: function () {
  1039. this.unbind("EnterFrame", this._enterFrame);
  1040. }
  1041. });
  1042. /**@
  1043. * #Crafty.polygon
  1044. * @category 2D
  1045. *
  1046. * Polygon object used for hitboxes and click maps. Must pass an Array for each point as an
  1047. * argument where index 0 is the x position and index 1 is the y position.
  1048. *
  1049. * For example one point of a polygon will look like this: `[0,5]` where the `x` is `0` and the `y` is `5`.
  1050. *
  1051. * Can pass an array of the points or simply put each point as an argument.
  1052. *
  1053. * When creating a polygon for an entity, each point should be offset or relative from the entities `x` and `y`
  1054. * (don't include the absolute values as it will automatically calculate this).
  1055. *
  1056. *
  1057. * @example
  1058. * ~~~
  1059. * new Crafty.polygon([50,0],[100,100],[0,100]);
  1060. * new Crafty.polygon([[50,0],[100,100],[0,100]]);
  1061. * ~~~
  1062. */
  1063. Crafty.polygon = function (poly) {
  1064. if (arguments.length > 1) {
  1065. poly = Array.prototype.slice.call(arguments, 0);
  1066. }
  1067. this.points = poly;
  1068. };
  1069. Crafty.polygon.prototype = {
  1070. /**@
  1071. * #.containsPoint
  1072. * @comp Crafty.polygon
  1073. * @sign public Boolean .containsPoint(Number x, Number y)
  1074. * @param x - X position of the point
  1075. * @param y - Y position of the point
  1076. *
  1077. * Method is used to determine if a given point is contained by the polygon.
  1078. *
  1079. * @example
  1080. * ~~~
  1081. * var poly = new Crafty.polygon([50,0],[100,100],[0,100]);
  1082. * poly.containsPoint(50, 50); //TRUE
  1083. * poly.containsPoint(0, 0); //FALSE
  1084. * ~~~
  1085. */
  1086. containsPoint: function (x, y) {
  1087. var p = this.points,
  1088. i, j, c = false;
  1089. for (i = 0, j = p.length - 1; i < p.length; j = i++) {
  1090. if (((p[i][1] > y) != (p[j][1] > y)) && (x < (p[j][0] - p[i][0]) * (y - p[i][1]) / (p[j][1] - p[i][1]) + p[i][0])) {
  1091. c = !c;
  1092. }
  1093. }
  1094. return c;
  1095. },
  1096. /**@
  1097. * #.shift
  1098. * @comp Crafty.polygon
  1099. * @sign public void .shift(Number x, Number y)
  1100. * @param x - Amount to shift the `x` axis
  1101. * @param y - Amount to shift the `y` axis
  1102. *
  1103. * Shifts every single point in the polygon by the specified amount.
  1104. *
  1105. * @example
  1106. * ~~~
  1107. * var poly = new Crafty.polygon([50,0],[100,100],[0,100]);
  1108. * poly.shift(5,5);
  1109. * //[[55,5], [105,5], [5,105]];
  1110. * ~~~
  1111. */
  1112. shift: function (x, y) {
  1113. var i = 0,
  1114. l = this.points.length,
  1115. current;
  1116. for (; i < l; i++) {
  1117. current = this.points[i];
  1118. current[0] += x;
  1119. current[1] += y;
  1120. }
  1121. },
  1122. rotate: function (e) {
  1123. var i = 0,
  1124. l = this.points.length,
  1125. current, x, y;
  1126. for (; i < l; i++) {
  1127. current = this.points[i];
  1128. x = e.o.x + (current[0] - e.o.x) * e.cos + (current[1] - e.o.y) * e.sin;
  1129. y = e.o.y - (current[0] - e.o.x) * e.sin + (current[1] - e.o.y) * e.cos;
  1130. current[0] = x;
  1131. current[1] = y;
  1132. }
  1133. }
  1134. };
  1135. /**@
  1136. * #Crafty.circle
  1137. * @category 2D
  1138. * Circle object used for hitboxes and click maps. Must pass a `x`, a `y` and a `radius` value.
  1139. *
  1140. *@example
  1141. * ~~~
  1142. * var centerX = 5,
  1143. * centerY = 10,
  1144. * radius = 25;
  1145. *
  1146. * new Crafty.circle(centerX, centerY, radius);
  1147. * ~~~
  1148. *
  1149. * When creating a circle for an entity, each point should be offset or relative from the entities `x` and `y`
  1150. * (don't include the absolute values as it will automatically calculate this).
  1151. */
  1152. Crafty.circle = function (x, y, radius) {
  1153. this.x = x;
  1154. this.y = y;
  1155. this.radius = radius;
  1156. // Creates an octagon that approximate the circle for backward compatibility.
  1157. this.points = [];
  1158. var theta;
  1159. for (var i = 0; i < 8; i++) {
  1160. theta = i * Math.PI / 4;
  1161. this.points[i] = [this.x + (Math.sin(theta) * radius), this.y + (Math.cos(theta) * radius)];
  1162. }
  1163. };
  1164. Crafty.circle.prototype = {
  1165. /**@
  1166. * #.containsPoint
  1167. * @comp Crafty.circle
  1168. * @sign public Boolean .containsPoint(Number x, Number y)
  1169. * @param x - X position of the point
  1170. * @param y - Y position of the point
  1171. *
  1172. * Method is used to determine if a given point is contained by the circle.
  1173. *
  1174. * @example
  1175. * ~~~
  1176. * var circle = new Crafty.circle(0, 0, 10);
  1177. * circle.containsPoint(0, 0); //TRUE
  1178. * circle.containsPoint(50, 50); //FALSE
  1179. * ~~~
  1180. */
  1181. containsPoint: function (x, y) {
  1182. var radius = this.radius,
  1183. sqrt = Math.sqrt,
  1184. deltaX = this.x - x,
  1185. deltaY = this.y - y;
  1186. return (deltaX * deltaX + deltaY * deltaY) < (radius * radius);
  1187. },
  1188. /**@
  1189. * #.shift
  1190. * @comp Crafty.circle
  1191. * @sign public void .shift(Number x, Number y)
  1192. * @param x - Amount to shift the `x` axis
  1193. * @param y - Amount to shift the `y` axis
  1194. *
  1195. * Shifts the circle by the specified amount.
  1196. *
  1197. * @example
  1198. * ~~~
  1199. * var circle = new Crafty.circle(0, 0, 10);
  1200. * circle.shift(5,5);
  1201. * //{x: 5, y: 5, radius: 10};
  1202. * ~~~
  1203. */
  1204. shift: function (x, y) {
  1205. this.x += x;
  1206. this.y += y;
  1207. var i = 0,
  1208. l = this.points.length,
  1209. current;
  1210. for (; i < l; i++) {
  1211. current = this.points[i];
  1212. current[0] += x;
  1213. current[1] += y;
  1214. }
  1215. },
  1216. rotate: function () {
  1217. // We are a circle, we don't have to rotate :)
  1218. }
  1219. };
  1220. Crafty.matrix = function (m) {
  1221. this.mtx = m;
  1222. this.width = m[0].length;
  1223. this.height = m.length;
  1224. };
  1225. Crafty.matrix.prototype = {
  1226. x: function (other) {
  1227. if (this.width != other.height) {
  1228. return;
  1229. }
  1230. var result = [];
  1231. for (var i = 0; i < this.height; i++) {
  1232. result[i] = [];
  1233. for (var j = 0; j < other.width; j++) {
  1234. var sum = 0;
  1235. for (var k = 0; k < this.width; k++) {
  1236. sum += this.mtx[i][k] * other.mtx[k][j];
  1237. }
  1238. result[i][j] = sum;
  1239. }
  1240. }
  1241. return new Crafty.matrix(result);
  1242. },
  1243. e: function (row, col) {
  1244. //test if out of bounds
  1245. if (row < 1 || row > this.mtx.length || col < 1 || col > this.mtx[0].length) return null;
  1246. return this.mtx[row - 1][col - 1];
  1247. }
  1248. };
  1249. },{"./HashMap.js":4,"./core.js":9}],2:[function(require,module,exports){
  1250. var Crafty = require('./core.js'),
  1251. document = window.document;
  1252. /**@
  1253. * #DOM
  1254. * @category Graphics
  1255. * Draws entities as DOM nodes, specifically `<DIV>`s.
  1256. */
  1257. Crafty.c("DOM", {
  1258. /**@
  1259. * #._element
  1260. * @comp DOM
  1261. * The DOM element used to represent the entity.
  1262. */
  1263. _element: null,
  1264. //holds current styles, so we can check if there are changes to be written to the DOM
  1265. _cssStyles: null,
  1266. /**@
  1267. * #.avoidCss3dTransforms
  1268. * @comp DOM
  1269. * Avoids using of CSS 3D Transform for positioning when true. Default value is false.
  1270. */
  1271. avoidCss3dTransforms: false,
  1272. init: function () {
  1273. this._cssStyles = {
  1274. visibility: '',
  1275. left: '',
  1276. top: '',
  1277. width: '',
  1278. height: '',
  1279. zIndex: '',
  1280. opacity: '',
  1281. transformOrigin: '',
  1282. transform: ''
  1283. };
  1284. this._element = document.createElement("div");
  1285. Crafty.stage.inner.appendChild(this._element);
  1286. this._element.style.position = "absolute";
  1287. this._element.id = "ent" + this[0];
  1288. this.bind("Invalidate", function () {
  1289. if (!this._changed) {
  1290. this._changed = true;
  1291. Crafty.DrawManager.addDom(this);
  1292. }
  1293. });
  1294. function updateClass() {
  1295. var i = 0,
  1296. c = this.__c,
  1297. str = "";
  1298. for (i in c) {
  1299. str += ' ' + i;
  1300. }
  1301. str = str.substr(1);
  1302. this._element.className = str;
  1303. }
  1304. function removeClass(removedComponent) {
  1305. var i = 0,
  1306. c = this.__c,
  1307. str = "";
  1308. for (i in c) {
  1309. if(i != removedComponent) {
  1310. str += ' ' + i;
  1311. }
  1312. }
  1313. str = str.substr(1);
  1314. this._element.className = str;
  1315. }
  1316. this.bind("NewComponent", updateClass).bind("RemoveComponent", removeClass);
  1317. this.bind("Remove", this.undraw);
  1318. this.bind("RemoveComponent", function (compName) {
  1319. if (compName === "DOM")
  1320. this.undraw();
  1321. });
  1322. },
  1323. /**@
  1324. * #.getDomId
  1325. * @comp DOM
  1326. * @sign public this .getId()
  1327. *
  1328. * Get the Id of the DOM element used to represent the entity.
  1329. */
  1330. getDomId: function () {
  1331. return this._element.id;
  1332. },
  1333. /**@
  1334. * #.DOM
  1335. * @comp DOM
  1336. * @trigger Draw - when the entity is ready to be drawn to the stage - { style:String, type:"DOM", co}
  1337. * @sign public this .DOM(HTMLElement elem)
  1338. * @param elem - HTML element that will replace the dynamically created one
  1339. *
  1340. * Pass a DOM element to use rather than one created. Will set `._element` to this value. Removes the old element.
  1341. */
  1342. DOM: function (elem) {
  1343. if (elem && elem.nodeType) {
  1344. this.undraw();
  1345. this._element = elem;
  1346. this._element.style.position = 'absolute';
  1347. }
  1348. return this;
  1349. },
  1350. /**@
  1351. * #.draw
  1352. * @comp DOM
  1353. * @sign public this .draw(void)
  1354. *
  1355. * Updates the CSS properties of the node to draw on the stage.
  1356. */
  1357. draw: function () {
  1358. var style = this._element.style,
  1359. coord = this.__coord || [0, 0, 0, 0],
  1360. co = {
  1361. x: coord[0],
  1362. y: coord[1],
  1363. w: coord[2],
  1364. h: coord[3]
  1365. },
  1366. prefix = Crafty.support.prefix,
  1367. trans = [];
  1368. if (this._cssStyles.visibility !== this._visible) {
  1369. this._cssStyles.visibility = this._visible;
  1370. if (!this._visible) {
  1371. style.visibility = "hidden";
  1372. } else {
  1373. style.visibility = "visible";
  1374. }
  1375. }
  1376. //utilize CSS3 if supported
  1377. if (Crafty.support.css3dtransform && !this.avoidCss3dTransforms) {
  1378. trans.push("translate3d(" + (~~this._x) + "px," + (~~this._y) + "px,0)");
  1379. } else {
  1380. if (this._cssStyles.left !== this._x) {
  1381. this._cssStyles.left = this._x;
  1382. style.left = ~~ (this._x) + "px";
  1383. }
  1384. if (this._cssStyles.top !== this._y) {
  1385. this._cssStyles.top = this._y;
  1386. style.top = ~~ (this._y) + "px";
  1387. }
  1388. }
  1389. if (this._cssStyles.width !== this._w) {
  1390. this._cssStyles.width = this._w;
  1391. style.width = ~~ (this._w) + "px";
  1392. }
  1393. if (this._cssStyles.height !== this._h) {
  1394. this._cssStyles.height = this._h;
  1395. style.height = ~~ (this._h) + "px";
  1396. }
  1397. if (this._cssStyles.zIndex !== this._z) {
  1398. this._cssStyles.zIndex = this._z;
  1399. style.zIndex = this._z;
  1400. }
  1401. if (this._cssStyles.opacity !== this._alpha) {
  1402. this._cssStyles.opacity = this._alpha;
  1403. style.opacity = this._alpha;
  1404. style[prefix + "Opacity"] = this._alpha;
  1405. }
  1406. if (this._mbr) {
  1407. var origin = this._origin.x + "px " + this._origin.y + "px";
  1408. style.transformOrigin = origin;
  1409. style[prefix + "TransformOrigin"] = origin;
  1410. if (Crafty.support.css3dtransform) trans.push("rotateZ(" + this._rotation + "deg)");
  1411. else trans.push("rotate(" + this._rotation + "deg)");
  1412. }
  1413. if (this._flipX) {
  1414. trans.push("scaleX(-1)");
  1415. }
  1416. if (this._flipY) {
  1417. trans.push("scaleY(-1)");
  1418. }
  1419. if (this._cssStyles.transform != trans.join(" ")) {
  1420. this._cssStyles.transform = trans.join(" ");
  1421. style.transform = this._cssStyles.transform;
  1422. style[prefix + "Transform"] = this._cssStyles.transform;
  1423. }
  1424. this.trigger("Draw", {
  1425. style: style,
  1426. type: "DOM",
  1427. co: co
  1428. });
  1429. return this;
  1430. },
  1431. /**@
  1432. * #.undraw
  1433. * @comp DOM
  1434. * @sign public this .undraw(void)
  1435. *
  1436. * Removes the element from the stage.
  1437. */
  1438. undraw: function () {
  1439. if (this._element) {
  1440. Crafty.stage.inner.removeChild(this._element);
  1441. }
  1442. return this;
  1443. },
  1444. /**@
  1445. * #.css
  1446. * @comp DOM
  1447. * @sign public css(String property, String value)
  1448. * @param property - CSS property to modify
  1449. * @param value - Value to give the CSS property
  1450. *
  1451. * @sign public css(Object map)
  1452. * @param map - Object where the key is the CSS property and the value is CSS value
  1453. *
  1454. * Apply CSS styles to the element.
  1455. *
  1456. * Can pass an object where the key is the style property and the value is style value.
  1457. *
  1458. * For setting one style, simply pass the style as the first argument and the value as the second.
  1459. *
  1460. * The notation can be CSS or JS (e.g. `text-align` or `textAlign`).
  1461. *
  1462. * To return a value, pass the property.
  1463. *
  1464. * Note: For entities with "Text" component, some css properties are controlled by separate functions
  1465. * `.textFont()` and `.textColor()`, and ignore `.css()` settings. See Text component for details.
  1466. *
  1467. * @example
  1468. * ~~~
  1469. * this.css({'text-align', 'center', 'text-decoration': 'line-through'});
  1470. * this.css("textAlign", "center");
  1471. * this.css("text-align"); //returns center
  1472. * ~~~
  1473. */
  1474. css: function (obj, value) {
  1475. var key,
  1476. elem = this._element,
  1477. val,
  1478. style = elem.style;
  1479. //if an object passed
  1480. if (typeof obj === "object") {
  1481. for (key in obj) {
  1482. if (!obj.hasOwnProperty(key)) continue;
  1483. val = obj[key];
  1484. if (typeof val === "number") val += 'px';
  1485. style[Crafty.DOM.camelize(key)] = val;
  1486. }
  1487. } else {
  1488. //if a value is passed, set the property
  1489. if (value) {
  1490. if (typeof value === "number") value += 'px';
  1491. style[Crafty.DOM.camelize(obj)] = value;
  1492. } else { //otherwise return the computed property
  1493. return Crafty.DOM.getStyle(elem, obj);
  1494. }
  1495. }
  1496. this.trigger("Invalidate");
  1497. return this;
  1498. }
  1499. });
  1500. Crafty.extend({
  1501. /**@
  1502. * #Crafty.DOM
  1503. * @category Graphics
  1504. *
  1505. * Collection of utilities for using the DOM.
  1506. */
  1507. DOM: {
  1508. /**@
  1509. * #Crafty.DOM.window
  1510. * @comp Crafty.DOM
  1511. *
  1512. * Object with `width` and `height` values representing the width
  1513. * and height of the `window`.
  1514. */
  1515. window: {
  1516. init: function () {
  1517. this.width = window.innerWidth || (window.document.documentElement.clientWidth || window.document.body.clientWidth);
  1518. this.height = window.innerHeight || (window.document.documentElement.clientHeight || window.document.body.clientHeight);
  1519. // Bind scene rendering (see drawing.js)
  1520. Crafty.uniqueBind("RenderScene", Crafty.DrawManager.renderDOM);
  1521. // Resize the viewport
  1522. Crafty.uniqueBind("ViewportResize", this._resize);
  1523. },
  1524. _resize: function(){
  1525. Crafty.stage.elem.style.width = Crafty.viewport.width + "px";
  1526. Crafty.stage.elem.style.height = Crafty.viewport.height + "px";
  1527. },
  1528. width: 0,
  1529. height: 0
  1530. },
  1531. /**@
  1532. * #Crafty.DOM.inner
  1533. * @comp Crafty.DOM
  1534. * @sign public Object Crafty.DOM.inner(HTMLElement obj)
  1535. * @param obj - HTML element to calculate the position
  1536. * @returns Object with `x` key being the `x` position, `y` being the `y` position
  1537. *
  1538. * Find a DOM elements position including
  1539. * padding and border.
  1540. */
  1541. inner: function (obj) {
  1542. var rect = obj.getBoundingClientRect(),
  1543. x = rect.left + (window.pageXOffset ? window.pageXOffset : document.body.scrollLeft),
  1544. y = rect.top + (window.pageYOffset ? window.pageYOffset : document.body.scrollTop),
  1545. //border left
  1546. borderX = parseInt(this.getStyle(obj, 'border-left-width') || 0, 10) || parseInt(this.getStyle(obj, 'borderLeftWidth') || 0, 10) || 0,
  1547. borderY = parseInt(this.getStyle(obj, 'border-top-width') || 0, 10) || parseInt(this.getStyle(obj, 'borderTopWidth') || 0, 10) || 0;
  1548. x += borderX;
  1549. y += borderY;
  1550. return {
  1551. x: x,
  1552. y: y
  1553. };
  1554. },
  1555. /**@
  1556. * #Crafty.DOM.getStyle
  1557. * @comp Crafty.DOM
  1558. * @sign public Object Crafty.DOM.getStyle(HTMLElement obj, String property)
  1559. * @param obj - HTML element to find the style
  1560. * @param property - Style to return
  1561. *
  1562. * Determine the value of a style on an HTML element. Notation can be
  1563. * in either CSS or JS.
  1564. */
  1565. getStyle: function (obj, prop) {
  1566. var result;
  1567. if (obj.currentStyle)
  1568. result = obj.currentStyle[this.camelize(prop)];
  1569. else if (window.getComputedStyle)
  1570. result = document.defaultView.getComputedStyle(obj, null).getPropertyValue(this.csselize(prop));
  1571. return result;
  1572. },
  1573. /**
  1574. * Used in the Zepto framework
  1575. *
  1576. * Converts CSS notation to JS notation
  1577. */
  1578. camelize: function (str) {
  1579. return str.replace(/-+(.)?/g, function (match, chr) {
  1580. return chr ? chr.toUpperCase() : '';
  1581. });
  1582. },
  1583. /**
  1584. * Converts JS notation to CSS notation
  1585. */
  1586. csselize: function (str) {
  1587. return str.replace(/[A-Z]/g, function (chr) {
  1588. return chr ? '-' + chr.toLowerCase() : '';
  1589. });
  1590. },
  1591. /**@
  1592. * #Crafty.DOM.translate
  1593. * @comp Crafty.DOM
  1594. * @sign public Object Crafty.DOM.translate(Number clientX, Number clientY)
  1595. * @param clientX - clientX position in the browser screen
  1596. * @param clientY - clientY position in the browser screen
  1597. * @return Object `{x: ..., y: ...}` with Crafty coordinates.
  1598. *
  1599. * The parameters clientX and clientY are pixel coordinates within the visible
  1600. * browser window. This function translates those to Crafty coordinates (i.e.,
  1601. * the coordinates that you might apply to an entity), by taking into account
  1602. * where the stage is within the screen, what the current viewport is, etc.
  1603. */
  1604. translate: function (clientX, clientY) {
  1605. var doc = document.documentElement;
  1606. var body = document.body;
  1607. return {
  1608. x: (clientX - Crafty.stage.x + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 )) / Crafty.viewport._scale - Crafty.viewport._x,
  1609. y: (clientY - Crafty.stage.y + ( doc && doc.scrollTop || body && body.scrollTop || 0 )) / Crafty.viewport._scale - Crafty.viewport._y
  1610. };
  1611. }
  1612. }
  1613. });
  1614. },{"./core.js":9}],3:[function(require,module,exports){
  1615. var Crafty = require('./core.js'),
  1616. document = window.document;
  1617. /**@
  1618. * #DebugCanvas
  1619. * @category Debug
  1620. * @trigger Draw - when the entity is ready to be drawn to the stage
  1621. * @trigger NoCanvas - if the browser does not support canvas
  1622. *
  1623. * When this component is added to an entity it will be drawn by the DebugCanvas layer.
  1624. *
  1625. * Crafty.debugCanvas.init() will be automatically called if it is not called already to initialize the canvas element.
  1626. *
  1627. * To visualise an object's MBR, use "VisibleMBR". To visualise a "Collision" object's hitbox, use "WiredHitBox" or "SolidHitBox".
  1628. * @see DebugPolygon, DebugRectangle
  1629. */
  1630. Crafty.c("DebugCanvas", {
  1631. init: function () {
  1632. this.requires("2D");
  1633. if (!Crafty.DebugCanvas.context)
  1634. Crafty.DebugCanvas.init();
  1635. Crafty.DebugCanvas.add(this);
  1636. this._debug = {
  1637. alpha: 1.0,
  1638. lineWidth: 1
  1639. };
  1640. this.bind("RemoveComponent", this.onDebugRemove);
  1641. this.bind("Remove", this.onDebugDestroy);
  1642. },
  1643. // When component is removed
  1644. onDebugRemove: function (id) {
  1645. if (id === "DebugCanvas") {
  1646. Crafty.DebugCanvas.remove(this);
  1647. }
  1648. },
  1649. //When entity is destroyed
  1650. onDebugDestroy: function (id) {
  1651. Crafty.DebugCanvas.remove(this);
  1652. },
  1653. /**@
  1654. * #.debugAlpha
  1655. * @comp DebugCanvas
  1656. * @sign public .debugAlpha(Number alpha)
  1657. * @param alpha - The alpha level the component will be drawn with
  1658. */
  1659. debugAlpha: function (alpha) {
  1660. this._debug.alpha = alpha;
  1661. return this;
  1662. },
  1663. /**@
  1664. * #.debugFill
  1665. * @comp DebugCanvas
  1666. * @sign public .debugFill([String fillStyle])
  1667. * @param fillStyle - The color the component will be filled with. Defaults to "red". Pass the boolean false to turn off filling.
  1668. * @example
  1669. * ~~~
  1670. * var myEntity = Crafty.e("2D, Collision, SolidHitBox ").debugFill("purple")
  1671. * ~~~
  1672. */
  1673. debugFill: function (fillStyle) {
  1674. if (typeof fillStyle === 'undefined')
  1675. fillStyle = "red";
  1676. this._debug.fillStyle = fillStyle;
  1677. return this;
  1678. },
  1679. /**@
  1680. * #.debugStroke
  1681. * @comp DebugCanvas
  1682. * @sign public .debugStroke([String strokeStyle])
  1683. * @param strokeStyle - The color the component will be outlined with. Defaults to "red". Pass the boolean false to turn this off.
  1684. * @example
  1685. * ~~~
  1686. * var myEntity = Crafty.e("2D, Collision, WiredHitBox ").debugStroke("white")
  1687. * ~~~
  1688. */
  1689. debugStroke: function (strokeStyle) {
  1690. if (typeof strokeStyle === 'undefined')
  1691. strokeStyle = "red";
  1692. this._debug.strokeStyle = strokeStyle;
  1693. return this;
  1694. },
  1695. debugDraw: function (ctx) {
  1696. var ga = ctx.globalAlpha;
  1697. var props = this._debug;
  1698. if (props.alpha)
  1699. ctx.globalAlpha = this._debug.alpha;
  1700. if (props.strokeStyle)
  1701. ctx.strokeStyle = props.strokeStyle;
  1702. if (props.lineWidth)
  1703. ctx.lineWidth = props.lineWidth;
  1704. if (props.fillStyle)
  1705. ctx.fillStyle = props.fillStyle;
  1706. this.trigger("DebugDraw");
  1707. ctx.globalAlpha = ga;
  1708. }
  1709. });
  1710. /**@
  1711. * #DebugRectangle
  1712. * @category Debug
  1713. *
  1714. * A component for rendering an object with a position and dimensions to the debug canvas.
  1715. *
  1716. *
  1717. * ~~~
  1718. * var myEntity = Crafty.e("2D, DebugRectangle")
  1719. * .attr({x: 13, y: 37, w: 42, h: 42})
  1720. * .debugStroke("green");
  1721. * myEntity.debugRectangle(myEntity)
  1722. *~~~
  1723. * @see DebugCanvas
  1724. */
  1725. Crafty.c("DebugRectangle", {
  1726. init: function () {
  1727. this.requires("2D, DebugCanvas");
  1728. },
  1729. /**@
  1730. * #.debugRectangle
  1731. * @comp DebugRectangle
  1732. * @sign public .debugRectangle(Object rect)
  1733. * @param rect - an object with _x, _y, _w, and _h to draw
  1734. * Sets the rectangle that this component draws to the debug canvas.
  1735. *
  1736. */
  1737. debugRectangle: function (rect) {
  1738. this.debugRect = rect;
  1739. this.unbind("DebugDraw", this.drawDebugRect);
  1740. this.bind("DebugDraw", this.drawDebugRect);
  1741. return this;
  1742. },
  1743. drawDebugRect: function () {
  1744. ctx = Crafty.DebugCanvas.context;
  1745. var rect = this.debugRect;
  1746. if (rect === null || rect === undefined)
  1747. return;
  1748. if (rect._h && rect._w) {
  1749. if (this._debug.fillStyle)
  1750. ctx.fillRect(rect._x, rect._y, rect._w, rect._h);
  1751. if (this._debug.strokeStyle)
  1752. ctx.strokeRect(rect._x, rect._y, rect._w, rect._h);
  1753. }
  1754. }
  1755. });
  1756. /**@
  1757. * #VisibleMBR
  1758. * @category Debug
  1759. *
  1760. * Adding this component to an entity will cause it's MBR to be drawn to the debug canvas.
  1761. *
  1762. * The methods of DebugCanvas can be used to control this component's appearance.
  1763. * @see 2D, DebugRectangle, DebugCanvas
  1764. */
  1765. Crafty.c("VisibleMBR", {
  1766. init: function () {
  1767. this.requires("DebugRectangle")
  1768. .debugFill("purple")
  1769. .bind("EnterFrame", this._assignRect);
  1770. },
  1771. // Internal method for updating the MBR drawn.
  1772. _assignRect: function () {
  1773. if (this._mbr)
  1774. this.debugRectangle(this._mbr);
  1775. else
  1776. this.debugRectangle(this);
  1777. }
  1778. });
  1779. /**@
  1780. * #DebugPolygon
  1781. * @category Debug
  1782. *
  1783. * For drawing a polygon to the debug canvas
  1784. *
  1785. * The methods of DebugCanvas can be used to control this component's appearance -- by default it is neither filled nor outlined
  1786. *
  1787. * For debugging hitboxes, use WiredHitBox or SolidHitBox. For debugging MBR, use VisibleMBR
  1788. *
  1789. * @see DebugCanvas
  1790. */
  1791. Crafty.c("DebugPolygon", {
  1792. init: function () {
  1793. this.requires("2D, DebugCanvas");
  1794. },
  1795. /**@
  1796. * #.debugPolygon
  1797. * @comp DebugPolygon
  1798. * @sign public .debugPolygon(Polygon poly)
  1799. * @param poly - a polygon to render
  1800. * Sets the polygon that this component renders to the debug canvas.
  1801. *
  1802. */
  1803. debugPolygon: function (poly) {
  1804. this.polygon = poly;
  1805. this.unbind("DebugDraw", this.drawDebugPolygon);
  1806. this.bind("DebugDraw", this.drawDebugPolygon);
  1807. return this;
  1808. },
  1809. drawDebugPolygon: function () {
  1810. if (typeof this.polygon === "undefined")
  1811. return;
  1812. ctx = Crafty.DebugCanvas.context;
  1813. ctx.beginPath();
  1814. for (var p in this.polygon.points) {
  1815. ctx.lineTo(this.polygon.points[p][0], this.polygon.points[p][1]);
  1816. }
  1817. ctx.closePath();
  1818. if (this._debug.fillStyle)
  1819. ctx.fill();
  1820. if (this._debug.strokeStyle)
  1821. ctx.stroke();
  1822. }
  1823. });
  1824. /**@
  1825. * #WiredHitBox
  1826. * @category Debug
  1827. *
  1828. * Adding this component to an entity with a Collision component will cause its collision polygon to be drawn to the debug canvas as an outline
  1829. *
  1830. * The methods of DebugCanvas can be used to control this component's appearance.
  1831. * @see DebugPolygon, DebugCanvas
  1832. */
  1833. Crafty.c("WiredHitBox", {
  1834. init: function () {
  1835. this.requires("DebugPolygon")
  1836. .debugStroke("red")
  1837. .matchHitBox();
  1838. this.bind("NewHitbox", this.matchHitBox);
  1839. },
  1840. matchHitBox: function () {
  1841. this.debugPolygon(this.map);
  1842. }
  1843. });
  1844. /**@
  1845. * #SolidHitBox
  1846. * @category Debug
  1847. *
  1848. * Adding this component to an entity with a Collision component will cause its collision polygon to be drawn to the debug canvas, with a default alpha level of 0.7.
  1849. *
  1850. * The methods of DebugCanvas can be used to control this component's appearance.
  1851. * @see DebugPolygon, DebugCanvas
  1852. */
  1853. Crafty.c("SolidHitBox", {
  1854. init: function () {
  1855. this.requires("Collision, DebugPolygon")
  1856. .debugFill("orange").debugAlpha(0.7)
  1857. .matchHitBox();
  1858. this.bind("NewHitbox", this.matchHitBox);
  1859. },
  1860. matchHitBox: function () {
  1861. this.debugPolygon(this.map);
  1862. }
  1863. });
  1864. Crafty.DebugCanvas = {
  1865. context: null,
  1866. entities: [],
  1867. onetimeEntities: [],
  1868. add: function (ent) {
  1869. this.entities.push(ent);
  1870. },
  1871. remove: function (ent) {
  1872. var list = this.entities;
  1873. for (var i = list.length - 1; i >= 0; i--)
  1874. if (list[i] == ent)
  1875. list.splice(i, 1);
  1876. },
  1877. // Mostly copied from canvas.init()
  1878. // Called the first time a "DebugCanvas" component is added to an entity
  1879. // We should consider how to abstract the idea of multiple canvases
  1880. init: function () {
  1881. if (!Crafty.DebugCanvas.context) {
  1882. //check if canvas is supported
  1883. if (!Crafty.support.canvas) {
  1884. Crafty.trigger("NoCanvas");
  1885. Crafty.stop();
  1886. return;
  1887. }
  1888. //create an empty canvas element
  1889. var c;
  1890. c = document.createElement("canvas");
  1891. c.width = Crafty.viewport.width;
  1892. c.height = Crafty.viewport.height;
  1893. c.style.position = 'absolute';
  1894. c.style.left = "0px";
  1895. c.style.top = "0px";
  1896. c.id = "debug-canvas";
  1897. // The debug canvas should be on the very top; the highest a regular zindex can get is ~10000
  1898. c.style.zIndex = 100000;
  1899. Crafty.stage.elem.appendChild(c);
  1900. Crafty.DebugCanvas.context = c.getContext('2d');
  1901. Crafty.DebugCanvas._canvas = c;
  1902. }
  1903. //Bind rendering of canvas context (see drawing.js)
  1904. Crafty.unbind("RenderScene", Crafty.DebugCanvas.renderScene);
  1905. Crafty.bind("RenderScene", Crafty.DebugCanvas.renderScene);
  1906. },
  1907. // copied from drawAll()
  1908. renderScene: function (rect) {
  1909. rect = rect || Crafty.viewport.rect();
  1910. var q = Crafty.DebugCanvas.entities,
  1911. i = 0,
  1912. l = q.length,
  1913. ctx = Crafty.DebugCanvas.context,
  1914. current;
  1915. var view = Crafty.viewport;
  1916. ctx.setTransform(view._scale, 0, 0, view._scale, view._x, view._y);
  1917. ctx.clearRect(rect._x, rect._y, rect._w, rect._h);
  1918. //sort the objects by the global Z
  1919. //q.sort(zsort);
  1920. for (; i < l; i++) {
  1921. current = q[i];
  1922. current.debugDraw(ctx);
  1923. }
  1924. }
  1925. };
  1926. },{"./core.js":9}],4:[function(require,module,exports){
  1927. var Crafty = require('./core.js'),
  1928. document = window.document;
  1929. /**
  1930. * Spatial HashMap for broad phase collision
  1931. *
  1932. * @author Louis Stowasser
  1933. */
  1934. /**@
  1935. * #Crafty.HashMap.constructor
  1936. * @comp Crafty.HashMap
  1937. * @sign public void Crafty.HashMap([cellsize])
  1938. * @param cellsize - the cell size. If omitted, `cellsize` is 64.
  1939. *
  1940. * Set `cellsize`.
  1941. * And create `this.map`.
  1942. */
  1943. var cellsize,
  1944. HashMap = function (cell) {
  1945. cellsize = cell || 64;
  1946. this.map = {};
  1947. },
  1948. SPACE = " ",
  1949. keyHolder = {};
  1950. HashMap.prototype = {
  1951. /**@
  1952. * #Crafty.map.insert
  1953. * @comp Crafty.map
  1954. * @sign public Object Crafty.map.insert(Object obj)
  1955. * @param obj - An entity to be inserted.
  1956. *
  1957. * `obj` is inserted in '.map' of the corresponding broad phase cells. An object of the following fields is returned.
  1958. * ~~~
  1959. * - the object that keep track of cells (keys)
  1960. * - `obj`
  1961. * - the HashMap object
  1962. * ~~~
  1963. */
  1964. insert: function (obj) {
  1965. var keys = HashMap.key(obj),
  1966. entry = new Entry(keys, obj, this),
  1967. i = 0,
  1968. j,
  1969. hash;
  1970. //insert into all x buckets
  1971. for (i = keys.x1; i <= keys.x2; i++) {
  1972. //insert into all y buckets
  1973. for (j = keys.y1; j <= keys.y2; j++) {
  1974. hash = (i << 16) ^ j;
  1975. if (!this.map[hash]) this.map[hash] = [];
  1976. this.map[hash].push(obj);
  1977. }
  1978. }
  1979. return entry;
  1980. },
  1981. /**@
  1982. * #Crafty.map.search
  1983. * @comp Crafty.map
  1984. * @sign public Object Crafty.map.search(Object rect[, Boolean filter])
  1985. * @param rect - the rectangular region to search for entities.
  1986. * @param filter - Default value is true. Otherwise, must be false.
  1987. *
  1988. * - If `filter` is `false`, just search for all the entries in the give `rect` region by broad phase collision. Entity may be returned duplicated.
  1989. * - If `filter` is `true`, filter the above results by checking that they actually overlap `rect`.
  1990. * The easier usage is with `filter`=`true`. For performance reason, you may use `filter`=`false`, and filter the result yourself. See examples in drawing.js and collision.js
  1991. */
  1992. search: function (rect, filter) {
  1993. var keys = HashMap.key(rect, keyHolder),
  1994. i, j, k,
  1995. results = [];
  1996. if (filter === undefined) filter = true; //default filter to true
  1997. //search in all x buckets
  1998. for (i = keys.x1; i <= keys.x2; i++) {
  1999. //insert into all y buckets
  2000. for (j = keys.y1; j <= keys.y2; j++) {
  2001. cell = this.map[(i << 16) ^ j];
  2002. if (cell) {
  2003. for (k = 0; k < cell.length; k++)
  2004. results.push(cell[k]);
  2005. }
  2006. }
  2007. }
  2008. if (filter) {
  2009. var obj, id, finalresult = [],
  2010. found = {};
  2011. //add unique elements to lookup table with the entity ID as unique key
  2012. for (i = 0, l = results.length; i < l; i++) {
  2013. obj = results[i];
  2014. if (!obj) continue; //skip if deleted
  2015. id = obj[0]; //unique ID
  2016. obj = obj._mbr || obj;
  2017. //check if not added to hash and that actually intersects
  2018. if (!found[id] && obj._x < rect._x + rect._w && obj._x + obj._w > rect._x &&
  2019. obj._y < rect._y + rect._h && obj._h + obj._y > rect._y)
  2020. found[id] = results[i];
  2021. }
  2022. //loop over lookup table and copy to final array
  2023. for (obj in found) finalresult.push(found[obj]);
  2024. return finalresult;
  2025. } else {
  2026. return results;
  2027. }
  2028. },
  2029. /**@
  2030. * #Crafty.map.remove
  2031. * @comp Crafty.map
  2032. * @sign public void Crafty.map.remove([Object keys, ]Object obj)
  2033. * @param keys - key region. If omitted, it will be derived from obj by `Crafty.HashMap.key`.
  2034. * @param obj - need more document.
  2035. *
  2036. * Remove an entity in a broad phase map.
  2037. * - The second form is only used in Crafty.HashMap to save time for computing keys again, where keys were computed previously from obj. End users should not call this form directly.
  2038. *
  2039. * @example
  2040. * ~~~
  2041. * Crafty.map.remove(e);
  2042. * ~~~
  2043. */
  2044. remove: function (keys, obj) {
  2045. var i = 0,
  2046. j, hash;
  2047. if (arguments.length == 1) {
  2048. obj = keys;
  2049. keys = HashMap.key(obj, keyHolder);
  2050. }
  2051. //search in all x buckets
  2052. for (i = keys.x1; i <= keys.x2; i++) {
  2053. //insert into all y buckets
  2054. for (j = keys.y1; j <= keys.y2; j++) {
  2055. hash = (i << 16) ^ j;
  2056. if (this.map[hash]) {
  2057. var cell = this.map[hash],
  2058. m, n = cell.length;
  2059. //loop over objs in cell and delete
  2060. for (m = 0; m < n; m++)
  2061. if (cell[m] && cell[m][0] === obj[0])
  2062. cell.splice(m, 1);
  2063. }
  2064. }
  2065. }
  2066. },
  2067. /**@
  2068. * #Crafty.map.refresh
  2069. * @comp Crafty.map
  2070. * @sign public void Crafty.map.remove(Entry entry)
  2071. * @param entry - An entry to update
  2072. *
  2073. * Refresh an entry's keys, and its position in the broad phrase map.
  2074. *
  2075. * @example
  2076. * ~~~
  2077. * Crafty.map.refresh(e);
  2078. * ~~~
  2079. */
  2080. refresh: function (entry) {
  2081. var keys = entry.keys;
  2082. var obj = entry.obj;
  2083. var cell, i, j, m, n;
  2084. //First delete current object from appropriate cells
  2085. for (i = keys.x1; i <= keys.x2; i++) {
  2086. for (j = keys.y1; j <= keys.y2; j++) {
  2087. cell = this.map[(i << 16) ^ j];
  2088. if (cell) {
  2089. n = cell.length;
  2090. //loop over objs in cell and delete
  2091. for (m = 0; m < n; m++)
  2092. if (cell[m] && cell[m][0] === obj[0])
  2093. cell.splice(m, 1);
  2094. }
  2095. }
  2096. }
  2097. //update keys
  2098. HashMap.key(obj, keys);
  2099. //insert into all rows and columns
  2100. for (i = keys.x1; i <= keys.x2; i++) {
  2101. for (j = keys.y1; j <= keys.y2; j++) {
  2102. cell = this.map[(i << 16) ^ j];
  2103. if (!cell) cell = this.map[(i << 16) ^ j] = [];
  2104. cell.push(obj);
  2105. }
  2106. }
  2107. return entry;
  2108. },
  2109. /**@
  2110. * #Crafty.map.boundaries
  2111. * @comp Crafty.map
  2112. * @sign public Object Crafty.map.boundaries()
  2113. *
  2114. * The return `Object` is of the following format.
  2115. * ~~~
  2116. * {
  2117. * min: {
  2118. * x: val_x,
  2119. * y: val_y
  2120. * },
  2121. * max: {
  2122. * x: val_x,
  2123. * y: val_y
  2124. * }
  2125. * }
  2126. * ~~~
  2127. */
  2128. boundaries: function () {
  2129. var k, ent,
  2130. hash = {
  2131. max: {
  2132. x: -Infinity,
  2133. y: -Infinity
  2134. },
  2135. min: {
  2136. x: Infinity,
  2137. y: Infinity
  2138. }
  2139. },
  2140. coords = {
  2141. max: {
  2142. x: -Infinity,
  2143. y: -Infinity
  2144. },
  2145. min: {
  2146. x: Infinity,
  2147. y: Infinity
  2148. }
  2149. };
  2150. //Using broad phase hash to speed up the computation of boundaries.
  2151. for (var h in this.map) {
  2152. if (!this.map[h].length) continue;
  2153. //broad phase coordinate
  2154. var i = h >> 16,
  2155. j = (h << 16) >> 16;
  2156. if (j < 0) {
  2157. i = i ^ -1;
  2158. }
  2159. if (i >= hash.max.x) {
  2160. hash.max.x = i;
  2161. for (k in this.map[h]) {
  2162. ent = this.map[h][k];
  2163. //make sure that this is a Crafty entity
  2164. if (typeof ent == 'object' && 'requires' in ent) {
  2165. coords.max.x = Math.max(coords.max.x, ent.x + ent.w);
  2166. }
  2167. }
  2168. }
  2169. if (i <= hash.min.x) {
  2170. hash.min.x = i;
  2171. for (k in this.map[h]) {
  2172. ent = this.map[h][k];
  2173. if (typeof ent == 'object' && 'requires' in ent) {
  2174. coords.min.x = Math.min(coords.min.x, ent.x);
  2175. }
  2176. }
  2177. }
  2178. if (j >= hash.max.y) {
  2179. hash.max.y = j;
  2180. for (k in this.map[h]) {
  2181. ent = this.map[h][k];
  2182. if (typeof ent == 'object' && 'requires' in ent) {
  2183. coords.max.y = Math.max(coords.max.y, ent.y + ent.h);
  2184. }
  2185. }
  2186. }
  2187. if (j <= hash.min.y) {
  2188. hash.min.y = j;
  2189. for (k in this.map[h]) {
  2190. ent = this.map[h][k];
  2191. if (typeof ent == 'object' && 'requires' in ent) {
  2192. coords.min.y = Math.min(coords.min.y, ent.y);
  2193. }
  2194. }
  2195. }
  2196. }
  2197. return coords;
  2198. }
  2199. };
  2200. /**@
  2201. * #Crafty.HashMap
  2202. * @category 2D
  2203. * Broad-phase collision detection engine. See background information at
  2204. *
  2205. * - [N Tutorial B - Broad-Phase Collision](http://www.metanetsoftware.com/technique/tutorialB.html)
  2206. * - [Broad-Phase Collision Detection with CUDA](http.developer.nvidia.com/GPUGems3/gpugems3_ch32.html)
  2207. * @see Crafty.map
  2208. */
  2209. /**@
  2210. * #Crafty.HashMap.key
  2211. * @comp Crafty.HashMap
  2212. * @sign public Object Crafty.HashMap.key(Object obj)
  2213. * @param obj - an Object that has .mbr() or _x, _y, _w and _h.
  2214. * Get the rectangular region (in terms of the grid, with grid size `cellsize`), where the object may fall in. This region is determined by the object's bounding box.
  2215. * The `cellsize` is 64 by default.
  2216. *
  2217. * @see Crafty.HashMap.constructor
  2218. */
  2219. HashMap.key = function (obj, keys) {
  2220. if (obj._mbr) {
  2221. obj = obj._mbr;
  2222. }
  2223. if (!keys) {
  2224. keys = {};
  2225. }
  2226. keys.x1 = Math.floor(obj._x / cellsize);
  2227. keys.y1 = Math.floor(obj._y / cellsize);
  2228. keys.x2 = Math.floor((obj._w + obj._x) / cellsize);
  2229. keys.y2 = Math.floor((obj._h + obj._y) / cellsize);
  2230. return keys;
  2231. };
  2232. HashMap.hash = function (keys) {
  2233. return keys.x1 + SPACE + keys.y1 + SPACE + keys.x2 + SPACE + keys.y2;
  2234. };
  2235. function Entry(keys, obj, map) {
  2236. this.keys = keys;
  2237. this.map = map;
  2238. this.obj = obj;
  2239. }
  2240. Entry.prototype = {
  2241. update: function (rect) {
  2242. //check if buckets change
  2243. if (HashMap.hash(HashMap.key(rect, keyHolder)) != HashMap.hash(this.keys)) {
  2244. this.map.refresh(this);
  2245. }
  2246. }
  2247. };
  2248. module.exports = HashMap;
  2249. },{"./core.js":9}],5:[function(require,module,exports){
  2250. var Crafty = require('./core.js'),
  2251. document = window.document;
  2252. Crafty.easing = function(duration) {
  2253. this.timePerFrame = 1000 / Crafty.timer.FPS();
  2254. this.duration = duration; //default duration given in ms
  2255. this.reset();
  2256. };
  2257. Crafty.easing.prototype = {
  2258. duration: 0,
  2259. clock:0,
  2260. steps: null,
  2261. complete: false,
  2262. paused: false,
  2263. // init values
  2264. reset: function(){
  2265. this.loops = 1;
  2266. this.clock = 0;
  2267. this.complete = false;
  2268. this.paused = false;
  2269. },
  2270. repeat: function(loopCount){
  2271. this.loops = loopCount;
  2272. },
  2273. setProgress: function(progress, loopCount){
  2274. this.clock = this.duration * progress;
  2275. if (typeof loopCount !== "undefined")
  2276. this.loops = loopCount;
  2277. },
  2278. pause: function(){
  2279. this.paused = true;
  2280. },
  2281. resume: function(){
  2282. this.paused = false;
  2283. this.complete = false;
  2284. },
  2285. // Increment the clock by some amount dt
  2286. // Handles looping and sets a flag on completion
  2287. tick: function(dt){
  2288. if (this.paused || this.complete) return;
  2289. this.clock += dt;
  2290. this.frames = Math.floor(this.clock/this.timePerFrame);
  2291. while (this.clock >= this.duration && this.complete === false){
  2292. this.loops--;
  2293. if (this.loops > 0)
  2294. this.clock -= this.duration;
  2295. else
  2296. this.complete = true;
  2297. }
  2298. },
  2299. // same as value for now; with other time value functions would be more useful
  2300. time: function(){
  2301. return ( Math.min(this.clock/this.duration, 1) );
  2302. },
  2303. // Value is where along the tweening curve we are
  2304. // For now it's simply linear; but we can easily add new types
  2305. value: function(){
  2306. return this.time();
  2307. }
  2308. };
  2309. /**@
  2310. * #Tween
  2311. * @category Animation
  2312. * @trigger TweenEnd - when a tween finishes - String - property
  2313. *
  2314. * Component to animate the change in 2D properties over time.
  2315. */
  2316. Crafty.c("Tween", {
  2317. init: function(){
  2318. this.tweenGroup = {};
  2319. this.tweenStart = {};
  2320. this.tweens = [];
  2321. this.bind("EnterFrame", this._tweenTick);
  2322. },
  2323. _tweenTick: function(frameData){
  2324. var tween, v, i;
  2325. for ( i = this.tweens.length-1; i>=0; i--){
  2326. tween = this.tweens[i];
  2327. tween.easing.tick(frameData.dt);
  2328. v = tween.easing.value();
  2329. this._doTween(tween.props, v);
  2330. if (tween.easing.complete) {
  2331. this.tweens.splice(i, 1);
  2332. this._endTween(tween.props);
  2333. }
  2334. }
  2335. },
  2336. _doTween: function(props, v){
  2337. for (var name in props)
  2338. this[name] = (1-v) * this.tweenStart[name] + v * props[name];
  2339. },
  2340. /**@
  2341. * #.tween
  2342. * @comp Tween
  2343. * @sign public this .tween(Object properties, Number|String duration)
  2344. * @param properties - Object of numeric properties and what they should animate to
  2345. * @param duration - Duration to animate the properties over, in milliseconds.
  2346. *
  2347. * This method will animate numeric properties over the specified duration.
  2348. * These include `x`, `y`, `w`, `h`, `alpha` and `rotation`.
  2349. *
  2350. * The object passed should have the properties as keys and the value should be the resulting
  2351. * values of the properties. The passed object might be modified if later calls to tween animate the same properties.
  2352. *
  2353. * @example
  2354. * Move an object to 100,100 and fade out over 200 ms.
  2355. * ~~~
  2356. * Crafty.e("2D, Tween")
  2357. * .attr({alpha: 1.0, x: 0, y: 0})
  2358. * .tween({alpha: 0.0, x: 100, y: 100}, 200)
  2359. * ~~~
  2360. * @example
  2361. * Rotate an object over 2 seconds
  2362. * ~~~
  2363. * Crafty.e("2D, Tween")
  2364. * .attr({rotate:0})
  2365. * .tween({rotate:180}, 2000)
  2366. * ~~~
  2367. *
  2368. */
  2369. tween: function (props, duration) {
  2370. var tween = {
  2371. props: props,
  2372. easing: new Crafty.easing(duration)
  2373. };
  2374. // Tweens are grouped together by the original function call.
  2375. // Individual properties must belong to only a single group
  2376. // When a new tween starts, if it already belongs to a group, move it to the new one
  2377. // Record the group it currently belongs to, as well as its starting coordinate.
  2378. for (var propname in props){
  2379. if (typeof this.tweenGroup[propname] !== "undefined")
  2380. this.cancelTween(propname);
  2381. this.tweenStart[propname] = this[propname];
  2382. this.tweenGroup[propname] = props;
  2383. }
  2384. this.tweens.push(tween);
  2385. return this;
  2386. },
  2387. /**@
  2388. * #.cancelTween
  2389. * @comp Tween
  2390. * @sign public this .cancelTween(String target)
  2391. * @param target - The property to cancel
  2392. *
  2393. * @sign public this .cancelTween(Object target)
  2394. * @param target - An object containing the properties to cancel.
  2395. *
  2396. * Stops tweening the specified property or properties.
  2397. * Passing the object used to start the tween might be a typical use of the second signature.
  2398. */
  2399. cancelTween: function(target){
  2400. if (typeof target === "string"){
  2401. if (typeof this.tweenGroup[target] == "object" )
  2402. delete this.tweenGroup[target][target];
  2403. } else if (typeof target === "object") {
  2404. for (var propname in target)
  2405. this.cancelTween(propname);
  2406. }
  2407. return this;
  2408. },
  2409. /*
  2410. * Stops tweening the specified group of properties, and fires the "TweenEnd" event.
  2411. */
  2412. _endTween: function(properties){
  2413. for (var propname in properties){
  2414. delete this.tweenGroup[propname];
  2415. }
  2416. this.trigger("TweenEnd", properties);
  2417. }
  2418. });
  2419. },{"./core.js":9}],6:[function(require,module,exports){
  2420. var Crafty = require('./core.js'),
  2421. document = window.document;
  2422. /**@
  2423. * #Canvas
  2424. * @category Graphics
  2425. * @trigger Draw - when the entity is ready to be drawn to the stage - {type: "canvas", pos, co, ctx}
  2426. * @trigger NoCanvas - if the browser does not support canvas
  2427. *
  2428. * When this component is added to an entity it will be drawn to the global canvas element. The canvas element (and hence all Canvas entities) is always rendered below any DOM entities.
  2429. *
  2430. * Crafty.canvas.init() will be automatically called if it is not called already to initialize the canvas element.
  2431. *
  2432. * Create a canvas entity like this
  2433. * ~~~
  2434. * var myEntity = Crafty.e("2D, Canvas, Color")
  2435. * .color("green")
  2436. * .attr({x: 13, y: 37, w: 42, h: 42});
  2437. *~~~
  2438. */
  2439. Crafty.c("Canvas", {
  2440. init: function () {
  2441. if (!Crafty.canvas.context) {
  2442. Crafty.canvas.init();
  2443. }
  2444. //increment the amount of canvas objs
  2445. Crafty.DrawManager.total2D++;
  2446. //Allocate an object to hold this components current region
  2447. this.currentRect = {};
  2448. this._changed = true;
  2449. Crafty.DrawManager.addCanvas(this);
  2450. this.bind("Invalidate", function (e) {
  2451. //flag if changed
  2452. if (this._changed === false) {
  2453. this._changed = true;
  2454. Crafty.DrawManager.addCanvas(this);
  2455. }
  2456. });
  2457. this.bind("Remove", function () {
  2458. Crafty.DrawManager.total2D--;
  2459. this._changed = true;
  2460. Crafty.DrawManager.addCanvas(this);
  2461. });
  2462. },
  2463. /**@
  2464. * #.draw
  2465. * @comp Canvas
  2466. * @sign public this .draw([[Context ctx, ]Number x, Number y, Number w, Number h])
  2467. * @param ctx - Canvas 2D context if drawing on another canvas is required
  2468. * @param x - X offset for drawing a segment
  2469. * @param y - Y offset for drawing a segment
  2470. * @param w - Width of the segment to draw
  2471. * @param h - Height of the segment to draw
  2472. *
  2473. * Method to draw the entity on the canvas element. Can pass rect values for redrawing a segment of the entity.
  2474. */
  2475. // Cache the various objects and arrays used in draw:
  2476. drawVars: {
  2477. type: "canvas",
  2478. pos: {},
  2479. ctx: null,
  2480. coord: [0, 0, 0, 0],
  2481. co: {
  2482. x: 0,
  2483. y: 0,
  2484. w: 0,
  2485. h: 0
  2486. }
  2487. },
  2488. draw: function (ctx, x, y, w, h) {
  2489. if (!this.ready) return;
  2490. if (arguments.length === 4) {
  2491. h = w;
  2492. w = y;
  2493. y = x;
  2494. x = ctx;
  2495. ctx = Crafty.canvas.context;
  2496. }
  2497. var pos = this.drawVars.pos;
  2498. pos._x = (this._x + (x || 0));
  2499. pos._y = (this._y + (y || 0));
  2500. pos._w = (w || this._w);
  2501. pos._h = (h || this._h);
  2502. context = ctx || Crafty.canvas.context;
  2503. coord = this.__coord || [0, 0, 0, 0];
  2504. var co = this.drawVars.co;
  2505. co.x = coord[0] + (x || 0);
  2506. co.y = coord[1] + (y || 0);
  2507. co.w = w || coord[2];
  2508. co.h = h || coord[3];
  2509. if (this._rotation !== 0) {
  2510. context.save();
  2511. context.translate(this._origin.x + this._x, this._origin.y + this._y);
  2512. pos._x = -this._origin.x;
  2513. pos._y = -this._origin.y;
  2514. context.rotate((this._rotation % 360) * (Math.PI / 180));
  2515. }
  2516. if (this._flipX || this._flipY) {
  2517. context.save();
  2518. context.scale((this._flipX ? -1 : 1), (this._flipY ? -1 : 1));
  2519. if (this._flipX) {
  2520. pos._x = -(pos._x + pos._w);
  2521. }
  2522. if (this._flipY) {
  2523. pos._y = -(pos._y + pos._h);
  2524. }
  2525. }
  2526. var globalpha;
  2527. //draw with alpha
  2528. if (this._alpha < 1.0) {
  2529. globalpha = context.globalAlpha;
  2530. context.globalAlpha = this._alpha;
  2531. }
  2532. this.drawVars.ctx = context;
  2533. this.trigger("Draw", this.drawVars);
  2534. if (this._rotation !== 0 || (this._flipX || this._flipY)) {
  2535. context.restore();
  2536. }
  2537. if (globalpha) {
  2538. context.globalAlpha = globalpha;
  2539. }
  2540. return this;
  2541. }
  2542. });
  2543. /**@
  2544. * #Crafty.canvas
  2545. * @category Graphics
  2546. *
  2547. * Collection of methods to draw on canvas.
  2548. */
  2549. Crafty.extend({
  2550. canvas: {
  2551. /**@
  2552. * #Crafty.canvas.context
  2553. * @comp Crafty.canvas
  2554. *
  2555. * This will return the 2D context of the main canvas element.
  2556. * The value returned from `Crafty.canvas._canvas.getContext('2d')`.
  2557. */
  2558. context: null,
  2559. /**@
  2560. * #Crafty.canvas._canvas
  2561. * @comp Crafty.canvas
  2562. *
  2563. * Main Canvas element
  2564. */
  2565. /**@
  2566. * #Crafty.canvas.init
  2567. * @comp Crafty.canvas
  2568. * @sign public void Crafty.canvas.init(void)
  2569. * @trigger NoCanvas - triggered if `Crafty.support.canvas` is false
  2570. *
  2571. * Creates a `canvas` element inside `Crafty.stage.elem`. Must be called
  2572. * before any entities with the Canvas component can be drawn.
  2573. *
  2574. * This method will automatically be called if no `Crafty.canvas.context` is
  2575. * found.
  2576. */
  2577. init: function () {
  2578. //check if canvas is supported
  2579. if (!Crafty.support.canvas) {
  2580. Crafty.trigger("NoCanvas");
  2581. Crafty.stop();
  2582. return;
  2583. }
  2584. //create an empty canvas element
  2585. var c;
  2586. c = document.createElement("canvas");
  2587. c.width = Crafty.viewport.width;
  2588. c.height = Crafty.viewport.height;
  2589. c.style.position = 'absolute';
  2590. c.style.left = "0px";
  2591. c.style.top = "0px";
  2592. Crafty.stage.elem.appendChild(c);
  2593. Crafty.canvas.context = c.getContext('2d');
  2594. Crafty.canvas._canvas = c;
  2595. //Set any existing transformations
  2596. var zoom = Crafty.viewport._scale;
  2597. if (zoom != 1)
  2598. Crafty.canvas.context.scale(zoom, zoom);
  2599. //Bind rendering of canvas context (see drawing.js)
  2600. Crafty.uniqueBind("RenderScene", Crafty.DrawManager.renderCanvas);
  2601. Crafty.uniqueBind("ViewportResize", this._resize);
  2602. },
  2603. // Resize the canvas element to the current viewport
  2604. _resize: function() {
  2605. var c = Crafty.canvas._canvas;
  2606. c.width = Crafty.viewport.width;
  2607. c.height = Crafty.viewport.height;
  2608. }
  2609. }
  2610. });
  2611. },{"./core.js":9}],7:[function(require,module,exports){
  2612. var Crafty = require('./core.js'),
  2613. document = window.document,
  2614. DEG_TO_RAD = Math.PI / 180;
  2615. /**@
  2616. * #Collision
  2617. * @category 2D
  2618. * Component to detect collision between any two convex polygons.
  2619. */
  2620. Crafty.c("Collision", {
  2621. /**@
  2622. * #.init
  2623. * @comp Collision
  2624. * Create a rectangle polygon based on the x, y, w, h dimensions.
  2625. *
  2626. * By default, the collision hitbox will match the dimensions (x, y, w, h) and rotation of the object.
  2627. */
  2628. init: function () {
  2629. this.requires("2D");
  2630. this.collision();
  2631. },
  2632. // Run by Crafty when the component is removed
  2633. remove: function() {
  2634. this._cbr = null;
  2635. this.unbind("Resize", this._resizeMap);
  2636. this.unbind("Resize", this._checkBounds);
  2637. },
  2638. /**@
  2639. * #.collision
  2640. * @comp Collision
  2641. *
  2642. * @trigger NewHitbox - when a new hitbox is assigned - Crafty.polygon
  2643. *
  2644. * @sign public this .collision([Crafty.polygon polygon])
  2645. * @param polygon - Crafty.polygon object that will act as the hit area
  2646. *
  2647. * @sign public this .collision(Array point1, .., Array pointN)
  2648. * @param point# - Array with an `x` and `y` position to generate a polygon
  2649. *
  2650. * Constructor takes a polygon or array of points to use as the hit area.
  2651. *
  2652. * The hit area (polygon) must be a convex shape and not concave
  2653. * for the collision detection to work.
  2654. *
  2655. * Points are relative to the object's position and its unrotated state.
  2656. *
  2657. * If no parameter is passed, the x, y, w, h properties of the entity will be used, and the hitbox will be resized when the entity is.
  2658. *
  2659. * If a hitbox is set that is outside of the bounds of the entity itself, there will be a small performance penalty as it is tracked separately.
  2660. *
  2661. * @example
  2662. * ~~~
  2663. * Crafty.e("2D, Collision").collision(
  2664. * new Crafty.polygon([50,0], [100,100], [0,100])
  2665. * );
  2666. *
  2667. * Crafty.e("2D, Collision").collision([50,0], [100,100], [0,100]);
  2668. * ~~~
  2669. *
  2670. * @see Crafty.polygon
  2671. */
  2672. collision: function (poly) {
  2673. // Unbind anything bound to "Resize"
  2674. this.unbind("Resize", this._resizeMap);
  2675. this.unbind("Resize", this._checkBounds);
  2676. if (!poly) {
  2677. // If no polygon is specified, then a polygon is created that matches the bounds of the entity
  2678. // It will be adjusted on a "Resize" event
  2679. poly = new Crafty.polygon([0, 0], [this._w, 0], [this._w, this._h], [0, this._h]);
  2680. this.bind("Resize", this._resizeMap);
  2681. this._cbr = null;
  2682. } else {
  2683. // Otherwise, we set the specified hitbox, converting from a list of arguments to a polygon if necessary
  2684. if (arguments.length > 1) {
  2685. //convert args to array to create polygon
  2686. var args = Array.prototype.slice.call(arguments, 0);
  2687. poly = new Crafty.polygon(args);
  2688. }
  2689. // Check to see if the polygon sits outside the entity, and set _cbr appropriately
  2690. // On resize, the new bounds will be checked if necessary
  2691. this._findBounds(poly.points);
  2692. }
  2693. // If the entity is currently rotated, the points in the hitbox must also be rotated
  2694. if (this.rotation) {
  2695. poly.rotate({
  2696. cos: Math.cos(-this.rotation * DEG_TO_RAD),
  2697. sin: Math.sin(-this.rotation * DEG_TO_RAD),
  2698. o: {
  2699. x: this._origin.x,
  2700. y: this._origin.y
  2701. }
  2702. });
  2703. }
  2704. // Finally, assign the hitbox, and attach it to the "Collision" entity
  2705. this.map = poly;
  2706. this.attach(this.map);
  2707. this.map.shift(this._x, this._y);
  2708. this.trigger("NewHitbox", poly);
  2709. return this;
  2710. },
  2711. // If the hitbox is set by hand, it might extend beyond the entity.
  2712. // In such a case, we need to track this separately.
  2713. // This function finds a (non-minimal) bounding circle around the hitbox.
  2714. //
  2715. // It uses a pretty naive algorithm to do so, for more complicated options see [wikipedia](http://en.wikipedia.org/wiki/Bounding_sphere).
  2716. _findBounds: function(points) {
  2717. var minX = Infinity, maxX = -Infinity, minY=Infinity, maxY=-Infinity;
  2718. var p;
  2719. // Calculate the MBR of the points by finding the min/max x and y
  2720. for (var i=0; i<points.length; ++i){
  2721. p = points[i];
  2722. if (p[0] < minX)
  2723. minX = p[0];
  2724. if (p[0] > maxX)
  2725. maxX = p[0];
  2726. if (p[1] < minY)
  2727. minY = p[1];
  2728. if (p[1] > maxY)
  2729. maxY = p[1];
  2730. }
  2731. // This describes a circle centered on the MBR of the points, with a diameter equal to its diagonal
  2732. // It will be used to find a rough bounding box round the points, even if they've been rotated
  2733. var cbr = {
  2734. cx: (minX + maxX) / 2,
  2735. cy: (minY + maxY) / 2,
  2736. r: Math.sqrt( (maxX - minX)*(maxX - minX) + (maxY - minY)*(maxY - minY))/2,
  2737. };
  2738. // We need to worry about resizing, but only if resizing could possibly change whether the hitbox is in or out of bounds
  2739. // Thus if the upper-left corner is out of bounds, then there's no need to recheck on resize
  2740. if (minX >= 0 && minY >= 0) {
  2741. this._checkBounds = function() {
  2742. if (this._cbr === null && this._w < maxX || this._h < maxY ){
  2743. this._cbr = cbr;
  2744. this._calculateMBR();
  2745. } else if (this._cbr) {
  2746. this._cbr = null;
  2747. this._calculateMBR();
  2748. }
  2749. };
  2750. this.bind("Resize", this._checkBounds);
  2751. }
  2752. // If the hitbox is within the entity, _cbr is null
  2753. // Otherwise, set it, and immediately calculate the bounding box.
  2754. if (minX >= 0 && minY >= 0 && maxX <= this._w && maxY <= this._h){
  2755. this._cbr = null;
  2756. return false;
  2757. } else {
  2758. this._cbr = cbr;
  2759. this._calculateMBR();
  2760. return true;
  2761. }
  2762. },
  2763. // The default behavior is to match the hitbox to the entity.
  2764. // This function will change the hitbox when a "Resize" event triggers.
  2765. _resizeMap: function (e) {
  2766. var dx, dy, rot = this.rotation * DEG_TO_RAD,
  2767. points = this.map.points;
  2768. // Depending on the change of axis, move the corners of the rectangle appropriately
  2769. if (e.axis === 'w') {
  2770. if (rot) {
  2771. dx = e.amount * Math.cos(rot);
  2772. dy = e.amount * Math.sin(rot);
  2773. } else {
  2774. dx = e.amount;
  2775. dy = 0;
  2776. }
  2777. // "top right" point shifts on change of w
  2778. points[1][0] += dx;
  2779. points[1][1] += dy;
  2780. } else {
  2781. if (rot) {
  2782. dy = e.amount * Math.cos(rot);
  2783. dx = -e.amount * Math.sin(rot);
  2784. } else {
  2785. dx = 0;
  2786. dy = e.amount;
  2787. }
  2788. // "bottom left" point shifts on change of h
  2789. points[3][0] += dx;
  2790. points[3][1] += dy;
  2791. }
  2792. // "bottom right" point shifts on either change
  2793. points[2][0] += dx;
  2794. points[2][1] += dy;
  2795. },
  2796. /**@
  2797. * #.hit
  2798. * @comp Collision
  2799. * @sign public Boolean/Array hit(String component)
  2800. * @param component - Check collision with entities that has this component
  2801. * @return `false` if no collision. If a collision is detected, returns an Array of objects that are colliding.
  2802. *
  2803. * Takes an argument for a component to test collision for. If a collision is found, an array of
  2804. * every object in collision along with the amount of overlap is passed.
  2805. *
  2806. * If no collision, will return false. The return collision data will be an Array of Objects with the
  2807. * type of collision used, the object collided and if the type used was SAT (a polygon was used as the hitbox) then an amount of overlap.\
  2808. * ~~~
  2809. * [{
  2810. * obj: [entity],
  2811. * type: "MBR" or "SAT",
  2812. * overlap: [number]
  2813. * }]
  2814. * ~~~
  2815. * `MBR` is your standard axis aligned rectangle intersection (`.intersect` in the 2D component).
  2816. * `SAT` is collision between any convex polygon.
  2817. *
  2818. * @see .onHit, 2D
  2819. */
  2820. hit: function (comp) {
  2821. var area = this._cbr || this._mbr || this,
  2822. results = Crafty.map.search(area, false),
  2823. i = 0,
  2824. l = results.length,
  2825. dupes = {},
  2826. id, obj, oarea, key,
  2827. hasMap = ('map' in this && 'containsPoint' in this.map),
  2828. finalresult = [];
  2829. if (!l) {
  2830. return false;
  2831. }
  2832. for (; i < l; ++i) {
  2833. obj = results[i];
  2834. oarea = obj._cbr || obj._mbr || obj; //use the mbr
  2835. if (!obj) continue;
  2836. id = obj[0];
  2837. //check if not added to hash and that actually intersects
  2838. if (!dupes[id] && this[0] !== id && obj.__c[comp] &&
  2839. oarea._x < area._x + area._w && oarea._x + oarea._w > area._x &&
  2840. oarea._y < area._y + area._h && oarea._h + oarea._y > area._y)
  2841. dupes[id] = obj;
  2842. }
  2843. for (key in dupes) {
  2844. obj = dupes[key];
  2845. if (hasMap && 'map' in obj) {
  2846. var SAT = this._SAT(this.map, obj.map);
  2847. SAT.obj = obj;
  2848. SAT.type = "SAT";
  2849. if (SAT) finalresult.push(SAT);
  2850. } else {
  2851. finalresult.push({
  2852. obj: obj,
  2853. type: "MBR"
  2854. });
  2855. }
  2856. }
  2857. if (!finalresult.length) {
  2858. return false;
  2859. }
  2860. return finalresult;
  2861. },
  2862. /**@
  2863. * #.onHit
  2864. * @comp Collision
  2865. * @sign public this .onHit(String component, Function hit[, Function noHit])
  2866. * @param component - Component to check collisions for
  2867. * @param hit - Callback method to execute upon collision with component. Will be passed the results of the collision check in the same format documented for hit().
  2868. * @param noHit - Callback method executed once as soon as collision stops
  2869. *
  2870. * Creates an EnterFrame event calling .hit() each frame. When a collision is detected the callback will be invoked.
  2871. *
  2872. * @see .hit
  2873. */
  2874. onHit: function (comp, callback, callbackOff) {
  2875. var justHit = false;
  2876. this.bind("EnterFrame", function () {
  2877. var hitdata = this.hit(comp);
  2878. if (hitdata) {
  2879. justHit = true;
  2880. callback.call(this, hitdata);
  2881. } else if (justHit) {
  2882. if (typeof callbackOff == 'function') {
  2883. callbackOff.call(this);
  2884. }
  2885. justHit = false;
  2886. }
  2887. });
  2888. return this;
  2889. },
  2890. _SAT: function (poly1, poly2) {
  2891. var points1 = poly1.points,
  2892. points2 = poly2.points,
  2893. i = 0,
  2894. l = points1.length,
  2895. j, k = points2.length,
  2896. normal = {
  2897. x: 0,
  2898. y: 0
  2899. },
  2900. length,
  2901. min1, min2,
  2902. max1, max2,
  2903. interval,
  2904. MTV = null,
  2905. MTV2 = null,
  2906. MN = null,
  2907. dot,
  2908. nextPoint,
  2909. currentPoint;
  2910. //loop through the edges of Polygon 1
  2911. for (; i < l; i++) {
  2912. nextPoint = points1[(i == l - 1 ? 0 : i + 1)];
  2913. currentPoint = points1[i];
  2914. //generate the normal for the current edge
  2915. normal.x = -(nextPoint[1] - currentPoint[1]);
  2916. normal.y = (nextPoint[0] - currentPoint[0]);
  2917. //normalize the vector
  2918. length = Math.sqrt(normal.x * normal.x + normal.y * normal.y);
  2919. normal.x /= length;
  2920. normal.y /= length;
  2921. //default min max
  2922. min1 = min2 = -1;
  2923. max1 = max2 = -1;
  2924. //project all vertices from poly1 onto axis
  2925. for (j = 0; j < l; ++j) {
  2926. dot = points1[j][0] * normal.x + points1[j][1] * normal.y;
  2927. if (dot > max1 || max1 === -1) max1 = dot;
  2928. if (dot < min1 || min1 === -1) min1 = dot;
  2929. }
  2930. //project all vertices from poly2 onto axis
  2931. for (j = 0; j < k; ++j) {
  2932. dot = points2[j][0] * normal.x + points2[j][1] * normal.y;
  2933. if (dot > max2 || max2 === -1) max2 = dot;
  2934. if (dot < min2 || min2 === -1) min2 = dot;
  2935. }
  2936. //calculate the minimum translation vector should be negative
  2937. if (min1 < min2) {
  2938. interval = min2 - max1;
  2939. normal.x = -normal.x;
  2940. normal.y = -normal.y;
  2941. } else {
  2942. interval = min1 - max2;
  2943. }
  2944. //exit early if positive
  2945. if (interval >= 0) {
  2946. return false;
  2947. }
  2948. if (MTV === null || interval > MTV) {
  2949. MTV = interval;
  2950. MN = {
  2951. x: normal.x,
  2952. y: normal.y
  2953. };
  2954. }
  2955. }
  2956. //loop through the edges of Polygon 2
  2957. for (i = 0; i < k; i++) {
  2958. nextPoint = points2[(i == k - 1 ? 0 : i + 1)];
  2959. currentPoint = points2[i];
  2960. //generate the normal for the current edge
  2961. normal.x = -(nextPoint[1] - currentPoint[1]);
  2962. normal.y = (nextPoint[0] - currentPoint[0]);
  2963. //normalize the vector
  2964. length = Math.sqrt(normal.x * normal.x + normal.y * normal.y);
  2965. normal.x /= length;
  2966. normal.y /= length;
  2967. //default min max
  2968. min1 = min2 = -1;
  2969. max1 = max2 = -1;
  2970. //project all vertices from poly1 onto axis
  2971. for (j = 0; j < l; ++j) {
  2972. dot = points1[j][0] * normal.x + points1[j][1] * normal.y;
  2973. if (dot > max1 || max1 === -1) max1 = dot;
  2974. if (dot < min1 || min1 === -1) min1 = dot;
  2975. }
  2976. //project all vertices from poly2 onto axis
  2977. for (j = 0; j < k; ++j) {
  2978. dot = points2[j][0] * normal.x + points2[j][1] * normal.y;
  2979. if (dot > max2 || max2 === -1) max2 = dot;
  2980. if (dot < min2 || min2 === -1) min2 = dot;
  2981. }
  2982. //calculate the minimum translation vector should be negative
  2983. if (min1 < min2) {
  2984. interval = min2 - max1;
  2985. normal.x = -normal.x;
  2986. normal.y = -normal.y;
  2987. } else {
  2988. interval = min1 - max2;
  2989. }
  2990. //exit early if positive
  2991. if (interval >= 0) {
  2992. return false;
  2993. }
  2994. if (MTV === null || interval > MTV) MTV = interval;
  2995. if (interval > MTV2 || MTV2 === null) {
  2996. MTV2 = interval;
  2997. MN = {
  2998. x: normal.x,
  2999. y: normal.y
  3000. };
  3001. }
  3002. }
  3003. return {
  3004. overlap: MTV2,
  3005. normal: MN
  3006. };
  3007. }
  3008. });
  3009. },{"./core.js":9}],8:[function(require,module,exports){
  3010. var Crafty = require('./core.js'),
  3011. document = window.document;
  3012. Crafty.extend({
  3013. over: null, //object mouseover, waiting for out
  3014. mouseObjs: 0,
  3015. mousePos: {},
  3016. lastEvent: null,
  3017. keydown: {},
  3018. selected: false,
  3019. /**@
  3020. * #Crafty.keydown
  3021. * @category Input
  3022. * Remembering what keys (referred by Unicode) are down.
  3023. *
  3024. * @example
  3025. * ~~~
  3026. * Crafty.c("Keyboard", {
  3027. * isDown: function (key) {
  3028. * if (typeof key === "string") {
  3029. * key = Crafty.keys[key];
  3030. * }
  3031. * return !!Crafty.keydown[key];
  3032. * }
  3033. * });
  3034. * ~~~
  3035. * @see Keyboard, Crafty.keys
  3036. */
  3037. detectBlur: function (e) {
  3038. var selected = ((e.clientX > Crafty.stage.x && e.clientX < Crafty.stage.x + Crafty.viewport.width) &&
  3039. (e.clientY > Crafty.stage.y && e.clientY < Crafty.stage.y + Crafty.viewport.height));
  3040. if (!Crafty.selected && selected)
  3041. Crafty.trigger("CraftyFocus");
  3042. if (Crafty.selected && !selected)
  3043. Crafty.trigger("CraftyBlur");
  3044. Crafty.selected = selected;
  3045. },
  3046. /**@
  3047. * #Crafty.mouseDispatch
  3048. * @category Input
  3049. *
  3050. * Internal method which dispatches mouse events received by Crafty (crafty.stage.elem).
  3051. * The mouse events get dispatched to the closest entity to the source of the event (if available).
  3052. *
  3053. * This method also sets a global property Crafty.lastEvent, which holds the most recent event that
  3054. * occured (useful for determining mouse position in every frame).
  3055. * ~~~
  3056. * var newestX = Crafty.lastEvent.realX,
  3057. * newestY = Crafty.lastEvent.realY;
  3058. * ~~~
  3059. *
  3060. * Notable properties of a MouseEvent e:
  3061. * ~~~
  3062. * //(x,y) coordinates of mouse event in web browser screen space
  3063. * e.clientX, e.clientY
  3064. * //(x,y) coordinates of mouse event in world/viewport space
  3065. * e.realX, e.realY
  3066. * // Normalized mouse button according to Crafty.mouseButtons
  3067. * e.mouseButton
  3068. * ~~~
  3069. * @see Crafty.touchDispatch
  3070. */
  3071. mouseDispatch: function (e) {
  3072. if (!Crafty.mouseObjs) return;
  3073. Crafty.lastEvent = e;
  3074. var maxz = -1,
  3075. closest,
  3076. q,
  3077. i = 0,
  3078. l,
  3079. pos = Crafty.DOM.translate(e.clientX, e.clientY),
  3080. x, y,
  3081. dupes = {},
  3082. tar = e.target ? e.target : e.srcElement,
  3083. type = e.type;
  3084. //Normalize button according to http://unixpapa.com/js/mouse.html
  3085. if (typeof e.which === 'undefined') {
  3086. e.mouseButton = (e.button < 2) ? Crafty.mouseButtons.LEFT : ((e.button == 4) ? Crafty.mouseButtons.MIDDLE : Crafty.mouseButtons.RIGHT);
  3087. } else {
  3088. e.mouseButton = (e.which < 2) ? Crafty.mouseButtons.LEFT : ((e.which == 2) ? Crafty.mouseButtons.MIDDLE : Crafty.mouseButtons.RIGHT);
  3089. }
  3090. e.realX = x = Crafty.mousePos.x = pos.x;
  3091. e.realY = y = Crafty.mousePos.y = pos.y;
  3092. //if it's a DOM element with Mouse component we are done
  3093. if (tar.nodeName != "CANVAS") {
  3094. while (typeof (tar.id) != 'string' && tar.id.indexOf('ent') == -1) {
  3095. tar = tar.parentNode;
  3096. }
  3097. ent = Crafty(parseInt(tar.id.replace('ent', ''), 10));
  3098. if (ent.has('Mouse') && ent.isAt(x, y))
  3099. closest = ent;
  3100. }
  3101. //else we search for an entity with Mouse component
  3102. if (!closest) {
  3103. q = Crafty.map.search({
  3104. _x: x,
  3105. _y: y,
  3106. _w: 1,
  3107. _h: 1
  3108. }, false);
  3109. for (l = q.length; i < l; ++i) {
  3110. if (!q[i].__c.Mouse || !q[i]._visible) continue;
  3111. var current = q[i],
  3112. flag = false;
  3113. //weed out duplicates
  3114. if (dupes[current[0]]) continue;
  3115. else dupes[current[0]] = true;
  3116. if (current.mapArea) {
  3117. if (current.mapArea.containsPoint(x, y)) {
  3118. flag = true;
  3119. }
  3120. } else if (current.isAt(x, y)) flag = true;
  3121. if (flag && (current._z >= maxz || maxz === -1)) {
  3122. //if the Z is the same, select the closest GUID
  3123. if (current._z === maxz && current[0] < closest[0]) {
  3124. continue;
  3125. }
  3126. maxz = current._z;
  3127. closest = current;
  3128. }
  3129. }
  3130. }
  3131. //found closest object to mouse
  3132. if (closest) {
  3133. //click must mousedown and out on tile
  3134. if (type === "mousedown") {
  3135. closest.trigger("MouseDown", e);
  3136. } else if (type === "mouseup") {
  3137. closest.trigger("MouseUp", e);
  3138. } else if (type == "dblclick") {
  3139. closest.trigger("DoubleClick", e);
  3140. } else if (type == "click") {
  3141. closest.trigger("Click", e);
  3142. } else if (type === "mousemove") {
  3143. closest.trigger("MouseMove", e);
  3144. if (this.over !== closest) { //if new mousemove, it is over
  3145. if (this.over) {
  3146. this.over.trigger("MouseOut", e); //if over wasn't null, send mouseout
  3147. this.over = null;
  3148. }
  3149. this.over = closest;
  3150. closest.trigger("MouseOver", e);
  3151. }
  3152. } else closest.trigger(type, e); //trigger whatever it is
  3153. } else {
  3154. if (type === "mousemove" && this.over) {
  3155. this.over.trigger("MouseOut", e);
  3156. this.over = null;
  3157. }
  3158. if (type === "mousedown") {
  3159. Crafty.viewport.mouselook('start', e);
  3160. } else if (type === "mousemove") {
  3161. Crafty.viewport.mouselook('drag', e);
  3162. } else if (type == "mouseup") {
  3163. Crafty.viewport.mouselook('stop');
  3164. }
  3165. }
  3166. if (type === "mousemove") {
  3167. this.lastEvent = e;
  3168. }
  3169. },
  3170. /**@
  3171. * #Crafty.touchDispatch
  3172. * @category Input
  3173. *
  3174. * TouchEvents have a different structure then MouseEvents.
  3175. * The relevant data lives in e.changedTouches[0].
  3176. * To normalize TouchEvents we catch them and dispatch a mock MouseEvent instead.
  3177. *
  3178. * @see Crafty.mouseDispatch
  3179. */
  3180. touchDispatch: function (e) {
  3181. var type,
  3182. lastEvent = Crafty.lastEvent;
  3183. if (e.type === "touchstart") type = "mousedown";
  3184. else if (e.type === "touchmove") type = "mousemove";
  3185. else if (e.type === "touchend") type = "mouseup";
  3186. else if (e.type === "touchcancel") type = "mouseup";
  3187. else if (e.type === "touchleave") type = "mouseup";
  3188. if (e.touches && e.touches.length) {
  3189. first = e.touches[0];
  3190. } else if (e.changedTouches && e.changedTouches.length) {
  3191. first = e.changedTouches[0];
  3192. }
  3193. var simulatedEvent = document.createEvent("MouseEvent");
  3194. simulatedEvent.initMouseEvent(type, true, true, window, 1,
  3195. first.screenX,
  3196. first.screenY,
  3197. first.clientX,
  3198. first.clientY,
  3199. false, false, false, false, 0, e.relatedTarget
  3200. );
  3201. first.target.dispatchEvent(simulatedEvent);
  3202. // trigger click when it should be triggered
  3203. if (lastEvent !== null && lastEvent.type == 'mousedown' && type == 'mouseup') {
  3204. type = 'click';
  3205. simulatedEvent = document.createEvent("MouseEvent");
  3206. simulatedEvent.initMouseEvent(type, true, true, window, 1,
  3207. first.screenX,
  3208. first.screenY,
  3209. first.clientX,
  3210. first.clientY,
  3211. false, false, false, false, 0, e.relatedTarget
  3212. );
  3213. first.target.dispatchEvent(simulatedEvent);
  3214. }
  3215. //Don't prevent default actions if target node is input or textarea.
  3216. if (e.target && e.target.nodeName !== 'INPUT' && e.target.nodeName !== 'TEXTAREA') {
  3217. if (e.preventDefault) {
  3218. e.preventDefault();
  3219. } else {
  3220. e.returnValue = false;
  3221. }
  3222. }
  3223. },
  3224. /**@
  3225. * #KeyboardEvent
  3226. * @category Input
  3227. * Keyboard Event triggered by Crafty Core
  3228. * @trigger KeyDown - is triggered for each entity when the DOM 'keydown' event is triggered.
  3229. * @trigger KeyUp - is triggered for each entity when the DOM 'keyup' event is triggered.
  3230. *
  3231. * @example
  3232. * ~~~
  3233. * Crafty.e("2D, DOM, Color")
  3234. * .attr({x: 100, y: 100, w: 50, h: 50})
  3235. * .color("red")
  3236. * .bind('KeyDown', function(e) {
  3237. * if(e.key == Crafty.keys.LEFT_ARROW) {
  3238. * this.x = this.x-1;
  3239. * } else if (e.key == Crafty.keys.RIGHT_ARROW) {
  3240. * this.x = this.x+1;
  3241. * } else if (e.key == Crafty.keys.UP_ARROW) {
  3242. * this.y = this.y-1;
  3243. * } else if (e.key == Crafty.keys.DOWN_ARROW) {
  3244. * this.y = this.y+1;
  3245. * }
  3246. * });
  3247. * ~~~
  3248. *
  3249. * @see Crafty.keys
  3250. */
  3251. /**@
  3252. * #Crafty.eventObject
  3253. * @category Input
  3254. *
  3255. * Event Object used in Crafty for cross browser compatibility
  3256. */
  3257. /**@
  3258. * #.key
  3259. * @comp Crafty.eventObject
  3260. *
  3261. * Unicode of the key pressed
  3262. */
  3263. keyboardDispatch: function (e) {
  3264. // Use a Crafty-standard event object to avoid cross-browser issues
  3265. var original = e,
  3266. evnt = {},
  3267. props = "char charCode keyCode type shiftKey ctrlKey metaKey timestamp".split(" ");
  3268. for (var i = props.length; i;) {
  3269. var prop = props[--i];
  3270. evnt[prop] = original[prop];
  3271. }
  3272. evnt.which = original.charCode !== null ? original.charCode : original.keyCode;
  3273. evnt.key = original.keyCode || original.which;
  3274. evnt.originalEvent = original;
  3275. e = evnt;
  3276. if (e.type === "keydown") {
  3277. if (Crafty.keydown[e.key] !== true) {
  3278. Crafty.keydown[e.key] = true;
  3279. Crafty.trigger("KeyDown", e);
  3280. }
  3281. } else if (e.type === "keyup") {
  3282. delete Crafty.keydown[e.key];
  3283. Crafty.trigger("KeyUp", e);
  3284. }
  3285. //prevent default actions for all keys except backspace and F1-F12 and except actions in INPUT and TEXTAREA.
  3286. //prevent bubbling up for all keys except backspace and F1-F12.
  3287. //Among others this prevent the arrow keys from scrolling the parent page
  3288. //of an iframe hosting the game
  3289. if (Crafty.selected && !(e.key == 8 || e.key >= 112 && e.key <= 135)) {
  3290. if (e.stopPropagation) e.stopPropagation();
  3291. else e.cancelBubble = true;
  3292. //Don't prevent default actions if target node is input or textarea.
  3293. if (e.target && e.target.nodeName !== 'INPUT' && e.target.nodeName !== 'TEXTAREA') {
  3294. if (e.preventDefault) {
  3295. e.preventDefault();
  3296. } else {
  3297. e.returnValue = false;
  3298. }
  3299. }
  3300. return false;
  3301. }
  3302. }
  3303. });
  3304. //initialize the input events onload
  3305. Crafty.bind("Load", function () {
  3306. Crafty.addEvent(this, "keydown", Crafty.keyboardDispatch);
  3307. Crafty.addEvent(this, "keyup", Crafty.keyboardDispatch);
  3308. Crafty.addEvent(this, Crafty.stage.elem, "mousedown", Crafty.mouseDispatch);
  3309. Crafty.addEvent(this, Crafty.stage.elem, "mouseup", Crafty.mouseDispatch);
  3310. Crafty.addEvent(this, document.body, "mouseup", Crafty.detectBlur);
  3311. Crafty.addEvent(this, Crafty.stage.elem, "mousemove", Crafty.mouseDispatch);
  3312. Crafty.addEvent(this, Crafty.stage.elem, "click", Crafty.mouseDispatch);
  3313. Crafty.addEvent(this, Crafty.stage.elem, "dblclick", Crafty.mouseDispatch);
  3314. Crafty.addEvent(this, Crafty.stage.elem, "touchstart", Crafty.touchDispatch);
  3315. Crafty.addEvent(this, Crafty.stage.elem, "touchmove", Crafty.touchDispatch);
  3316. Crafty.addEvent(this, Crafty.stage.elem, "touchend", Crafty.touchDispatch);
  3317. Crafty.addEvent(this, Crafty.stage.elem, "touchcancel", Crafty.touchDispatch);
  3318. Crafty.addEvent(this, Crafty.stage.elem, "touchleave", Crafty.touchDispatch);
  3319. });
  3320. Crafty.bind("CraftyStop", function () {
  3321. Crafty.removeEvent(this, "keydown", Crafty.keyboardDispatch);
  3322. Crafty.removeEvent(this, "keyup", Crafty.keyboardDispatch);
  3323. if (Crafty.stage) {
  3324. Crafty.removeEvent(this, Crafty.stage.elem, "mousedown", Crafty.mouseDispatch);
  3325. Crafty.removeEvent(this, Crafty.stage.elem, "mouseup", Crafty.mouseDispatch);
  3326. Crafty.removeEvent(this, Crafty.stage.elem, "mousemove", Crafty.mouseDispatch);
  3327. Crafty.removeEvent(this, Crafty.stage.elem, "click", Crafty.mouseDispatch);
  3328. Crafty.removeEvent(this, Crafty.stage.elem, "dblclick", Crafty.mouseDispatch);
  3329. Crafty.removeEvent(this, Crafty.stage.elem, "touchstart", Crafty.touchDispatch);
  3330. Crafty.removeEvent(this, Crafty.stage.elem, "touchmove", Crafty.touchDispatch);
  3331. Crafty.removeEvent(this, Crafty.stage.elem, "touchend", Crafty.touchDispatch);
  3332. Crafty.removeEvent(this, Crafty.stage.elem, "touchcancel", Crafty.touchDispatch);
  3333. Crafty.removeEvent(this, Crafty.stage.elem, "touchleave", Crafty.touchDispatch);
  3334. }
  3335. Crafty.removeEvent(this, document.body, "mouseup", Crafty.detectBlur);
  3336. });
  3337. /**@
  3338. * #Mouse
  3339. * @category Input
  3340. * Provides the entity with mouse related events
  3341. * @trigger MouseOver - when the mouse enters the entity - MouseEvent
  3342. * @trigger MouseOut - when the mouse leaves the entity - MouseEvent
  3343. * @trigger MouseDown - when the mouse button is pressed on the entity - MouseEvent
  3344. * @trigger MouseUp - when the mouse button is released on the entity - MouseEvent
  3345. * @trigger Click - when the user clicks the entity. [See documentation](http://www.quirksmode.org/dom/events/click.html) - MouseEvent
  3346. * @trigger DoubleClick - when the user double clicks the entity - MouseEvent
  3347. * @trigger MouseMove - when the mouse is over the entity and moves - MouseEvent
  3348. * Crafty adds the mouseButton property to MouseEvents that match one of
  3349. *
  3350. * - Crafty.mouseButtons.LEFT
  3351. * - Crafty.mouseButtons.RIGHT
  3352. * - Crafty.mouseButtons.MIDDLE
  3353. *
  3354. * @example
  3355. * ~~~
  3356. * myEntity.bind('Click', function() {
  3357. * console.log("Clicked!!");
  3358. * })
  3359. *
  3360. * myEntity.bind('MouseUp', function(e) {
  3361. * if( e.mouseButton == Crafty.mouseButtons.RIGHT )
  3362. * console.log("Clicked right button");
  3363. * })
  3364. * ~~~
  3365. * @see Crafty.mouseDispatch
  3366. */
  3367. Crafty.c("Mouse", {
  3368. init: function () {
  3369. Crafty.mouseObjs++;
  3370. this.bind("Remove", function () {
  3371. Crafty.mouseObjs--;
  3372. });
  3373. },
  3374. /**@
  3375. * #.areaMap
  3376. * @comp Mouse
  3377. * @sign public this .areaMap(Crafty.polygon polygon)
  3378. * @param polygon - Instance of Crafty.polygon used to check if the mouse coordinates are inside this region
  3379. * @sign public this .areaMap(Array point1, .., Array pointN)
  3380. * @param point# - Array with an `x` and `y` position to generate a polygon
  3381. *
  3382. * Assign a polygon to the entity so that mouse events will only be triggered if
  3383. * the coordinates are inside the given polygon.
  3384. *
  3385. * @example
  3386. * ~~~
  3387. * Crafty.e("2D, DOM, Color, Mouse")
  3388. * .color("red")
  3389. * .attr({ w: 100, h: 100 })
  3390. * .bind('MouseOver', function() {console.log("over")})
  3391. * .areaMap([0,0], [50,0], [50,50], [0,50])
  3392. * ~~~
  3393. *
  3394. * @see Crafty.polygon
  3395. */
  3396. areaMap: function (poly) {
  3397. //create polygon
  3398. if (arguments.length > 1) {
  3399. //convert args to array to create polygon
  3400. var args = Array.prototype.slice.call(arguments, 0);
  3401. poly = new Crafty.polygon(args);
  3402. }
  3403. poly.shift(this._x, this._y);
  3404. //this.map = poly;
  3405. this.mapArea = poly;
  3406. this.attach(this.mapArea);
  3407. return this;
  3408. }
  3409. });
  3410. /**@
  3411. * #Draggable
  3412. * @category Input
  3413. * Enable drag and drop of the entity.
  3414. * @trigger Dragging - is triggered each frame the entity is being dragged - MouseEvent
  3415. * @trigger StartDrag - is triggered when dragging begins - MouseEvent
  3416. * @trigger StopDrag - is triggered when dragging ends - MouseEvent
  3417. */
  3418. Crafty.c("Draggable", {
  3419. _origMouseDOMPos: null,
  3420. _oldX: null,
  3421. _oldY: null,
  3422. _dragging: false,
  3423. _dir: null,
  3424. //Note: the code is not tested with zoom, etc., that may distort the direction between the viewport and the coordinate on the canvas.
  3425. init: function () {
  3426. this.requires("Mouse");
  3427. this.enableDrag();
  3428. },
  3429. _ondrag: function (e) {
  3430. // While a drag is occurring, this method is bound to the mousemove DOM event
  3431. var pos = Crafty.DOM.translate(e.clientX, e.clientY);
  3432. // ignore invalid 0 0 position - strange problem on ipad
  3433. if (pos.x === 0 || pos.y === 0) {
  3434. return false;
  3435. }
  3436. if (this._dir) {
  3437. var len = (pos.x - this._origMouseDOMPos.x) * this._dir.x + (pos.y - this._origMouseDOMPos.y) * this._dir.y;
  3438. this.x = this._oldX + len * this._dir.x;
  3439. this.y = this._oldY + len * this._dir.y;
  3440. } else {
  3441. this.x = this._oldX + (pos.x - this._origMouseDOMPos.x);
  3442. this.y = this._oldY + (pos.y - this._origMouseDOMPos.y);
  3443. }
  3444. this.trigger("Dragging", e);
  3445. },
  3446. _ondown: function (e) {
  3447. // When dragging is enabled, this method is bound to the MouseDown crafty event
  3448. if (e.mouseButton !== Crafty.mouseButtons.LEFT) return;
  3449. this._startDrag(e);
  3450. },
  3451. _onup: function (e) {
  3452. // While a drag is occurring, this method is bound to mouseup DOM event
  3453. if (this._dragging === true) {
  3454. Crafty.removeEvent(this, Crafty.stage.elem, "mousemove", this._ondrag);
  3455. Crafty.removeEvent(this, Crafty.stage.elem, "mouseup", this._onup);
  3456. this._dragging = false;
  3457. this.trigger("StopDrag", e);
  3458. }
  3459. },
  3460. /**@
  3461. * #.dragDirection
  3462. * @comp Draggable
  3463. * @sign public this .dragDirection()
  3464. * Remove any previously specified direction.
  3465. *
  3466. * @sign public this .dragDirection(vector)
  3467. * @param vector - Of the form of {x: valx, y: valy}, the vector (valx, valy) denotes the move direction.
  3468. *
  3469. * @sign public this .dragDirection(degree)
  3470. * @param degree - A number, the degree (clockwise) of the move direction with respect to the x axis.
  3471. * Specify the dragging direction.
  3472. *
  3473. * @example
  3474. * ~~~
  3475. * this.dragDirection()
  3476. * this.dragDirection({x:1, y:0}) //Horizontal
  3477. * this.dragDirection({x:0, y:1}) //Vertical
  3478. * // Note: because of the orientation of x and y axis,
  3479. * // this is 45 degree clockwise with respect to the x axis.
  3480. * this.dragDirection({x:1, y:1}) //45 degree.
  3481. * this.dragDirection(60) //60 degree.
  3482. * ~~~
  3483. */
  3484. dragDirection: function (dir) {
  3485. if (typeof dir === 'undefined') {
  3486. this._dir = null;
  3487. } else if (("" + parseInt(dir, 10)) == dir) { //dir is a number
  3488. this._dir = {
  3489. x: Math.cos(dir / 180 * Math.PI),
  3490. y: Math.sin(dir / 180 * Math.PI)
  3491. };
  3492. } else {
  3493. var r = Math.sqrt(dir.x * dir.x + dir.y * dir.y);
  3494. this._dir = {
  3495. x: dir.x / r,
  3496. y: dir.y / r
  3497. };
  3498. }
  3499. },
  3500. /**@
  3501. * #._startDrag
  3502. * @comp Draggable
  3503. * Internal method for starting a drag of an entity either programatically or via Mouse click
  3504. *
  3505. * @param e - a mouse event
  3506. */
  3507. _startDrag: function (e) {
  3508. this._origMouseDOMPos = Crafty.DOM.translate(e.clientX, e.clientY);
  3509. this._oldX = this._x;
  3510. this._oldY = this._y;
  3511. this._dragging = true;
  3512. Crafty.addEvent(this, Crafty.stage.elem, "mousemove", this._ondrag);
  3513. Crafty.addEvent(this, Crafty.stage.elem, "mouseup", this._onup);
  3514. this.trigger("StartDrag", e);
  3515. },
  3516. /**@
  3517. * #.stopDrag
  3518. * @comp Draggable
  3519. * @sign public this .stopDrag(void)
  3520. * @trigger StopDrag - Called right after the mouse listeners are removed
  3521. *
  3522. * Stop the entity from dragging. Essentially reproducing the drop.
  3523. *
  3524. * @see .startDrag
  3525. */
  3526. stopDrag: function () {
  3527. Crafty.removeEvent(this, Crafty.stage.elem, "mousemove", this._ondrag);
  3528. Crafty.removeEvent(this, Crafty.stage.elem, "mouseup", this._onup);
  3529. this._dragging = false;
  3530. this.trigger("StopDrag");
  3531. return this;
  3532. },
  3533. /**@
  3534. * #.startDrag
  3535. * @comp Draggable
  3536. * @sign public this .startDrag(void)
  3537. *
  3538. * Make the entity follow the mouse positions.
  3539. *
  3540. * @see .stopDrag
  3541. */
  3542. startDrag: function () {
  3543. if (!this._dragging) {
  3544. //Use the last known position of the mouse
  3545. this._startDrag(Crafty.lastEvent);
  3546. }
  3547. return this;
  3548. },
  3549. /**@
  3550. * #.enableDrag
  3551. * @comp Draggable
  3552. * @sign public this .enableDrag(void)
  3553. *
  3554. * Rebind the mouse events. Use if `.disableDrag` has been called.
  3555. *
  3556. * @see .disableDrag
  3557. */
  3558. enableDrag: function () {
  3559. this.bind("MouseDown", this._ondown);
  3560. Crafty.addEvent(this, Crafty.stage.elem, "mouseup", this._onup);
  3561. return this;
  3562. },
  3563. /**@
  3564. * #.disableDrag
  3565. * @comp Draggable
  3566. * @sign public this .disableDrag(void)
  3567. *
  3568. * Stops entity from being draggable. Reenable with `.enableDrag()`.
  3569. *
  3570. * @see .enableDrag
  3571. */
  3572. disableDrag: function () {
  3573. this.unbind("MouseDown", this._ondown);
  3574. if (this._dragging) {
  3575. this.stopDrag();
  3576. }
  3577. return this;
  3578. }
  3579. });
  3580. /**@
  3581. * #Keyboard
  3582. * @category Input
  3583. * Give entities keyboard events (`keydown` and `keyup`).
  3584. */
  3585. Crafty.c("Keyboard", {
  3586. /**@
  3587. * #.isDown
  3588. * @comp Keyboard
  3589. * @sign public Boolean isDown(String keyName)
  3590. * @param keyName - Name of the key to check. See `Crafty.keys`.
  3591. * @sign public Boolean isDown(Number keyCode)
  3592. * @param keyCode - Key code in `Crafty.keys`.
  3593. *
  3594. * Determine if a certain key is currently down.
  3595. *
  3596. * @example
  3597. * ~~~
  3598. * entity.requires('Keyboard').bind('KeyDown', function () { if (this.isDown('SPACE')) jump(); });
  3599. * ~~~
  3600. *
  3601. * @see Crafty.keys
  3602. */
  3603. isDown: function (key) {
  3604. if (typeof key === "string") {
  3605. key = Crafty.keys[key];
  3606. }
  3607. return !!Crafty.keydown[key];
  3608. }
  3609. });
  3610. /**@
  3611. * #Multiway
  3612. * @category Input
  3613. * Used to bind keys to directions and have the entity move accordingly
  3614. * @trigger NewDirection - triggered when direction changes - { x:Number, y:Number } - New direction
  3615. * @trigger Moved - triggered on movement on either x or y axis. If the entity has moved on both axes for diagonal movement the event is triggered twice - { x:Number, y:Number } - Old position
  3616. */
  3617. Crafty.c("Multiway", {
  3618. _speed: 3,
  3619. _keydown: function (e) {
  3620. if (this._keys[e.key]) {
  3621. this._movement.x = Math.round((this._movement.x + this._keys[e.key].x) * 1000) / 1000;
  3622. this._movement.y = Math.round((this._movement.y + this._keys[e.key].y) * 1000) / 1000;
  3623. this.trigger('NewDirection', this._movement);
  3624. }
  3625. },
  3626. _keyup: function (e) {
  3627. if (this._keys[e.key]) {
  3628. this._movement.x = Math.round((this._movement.x - this._keys[e.key].x) * 1000) / 1000;
  3629. this._movement.y = Math.round((this._movement.y - this._keys[e.key].y) * 1000) / 1000;
  3630. this.trigger('NewDirection', this._movement);
  3631. }
  3632. },
  3633. _enterframe: function () {
  3634. if (this.disableControls) return;
  3635. if (this._movement.x !== 0) {
  3636. this.x += this._movement.x;
  3637. this.trigger('Moved', {
  3638. x: this.x - this._movement.x,
  3639. y: this.y
  3640. });
  3641. }
  3642. if (this._movement.y !== 0) {
  3643. this.y += this._movement.y;
  3644. this.trigger('Moved', {
  3645. x: this.x,
  3646. y: this.y - this._movement.y
  3647. });
  3648. }
  3649. },
  3650. _initializeControl: function () {
  3651. return this.unbind("KeyDown", this._keydown)
  3652. .unbind("KeyUp", this._keyup)
  3653. .unbind("EnterFrame", this._enterframe)
  3654. .bind("KeyDown", this._keydown)
  3655. .bind("KeyUp", this._keyup)
  3656. .bind("EnterFrame", this._enterframe);
  3657. },
  3658. /**@
  3659. * #.multiway
  3660. * @comp Multiway
  3661. * @sign public this .multiway([Number speed,] Object keyBindings )
  3662. * @param speed - Amount of pixels to move the entity whilst a key is down
  3663. * @param keyBindings - What keys should make the entity go in which direction. Direction is specified in degrees
  3664. * Constructor to initialize the speed and keyBindings. Component will listen to key events and move the entity appropriately.
  3665. *
  3666. * When direction changes a NewDirection event is triggered with an object detailing the new direction: {x: x_movement, y: y_movement}
  3667. * When entity has moved on either x- or y-axis a Moved event is triggered with an object specifying the old position {x: old_x, y: old_y}
  3668. *
  3669. * @example
  3670. * ~~~
  3671. * this.multiway(3, {UP_ARROW: -90, DOWN_ARROW: 90, RIGHT_ARROW: 0, LEFT_ARROW: 180});
  3672. * this.multiway({x:3,y:1.5}, {UP_ARROW: -90, DOWN_ARROW: 90, RIGHT_ARROW: 0, LEFT_ARROW: 180});
  3673. * this.multiway({W: -90, S: 90, D: 0, A: 180});
  3674. * ~~~
  3675. */
  3676. multiway: function (speed, keys) {
  3677. this._keyDirection = {};
  3678. this._keys = {};
  3679. this._movement = {
  3680. x: 0,
  3681. y: 0
  3682. };
  3683. this._speed = {
  3684. x: 3,
  3685. y: 3
  3686. };
  3687. if (keys) {
  3688. if (speed.x !== undefined && speed.y !== undefined) {
  3689. this._speed.x = speed.x;
  3690. this._speed.y = speed.y;
  3691. } else {
  3692. this._speed.x = speed;
  3693. this._speed.y = speed;
  3694. }
  3695. } else {
  3696. keys = speed;
  3697. }
  3698. this._keyDirection = keys;
  3699. this.speed(this._speed);
  3700. this._initializeControl();
  3701. //Apply movement if key is down when created
  3702. for (var k in keys) {
  3703. if (Crafty.keydown[Crafty.keys[k]]) {
  3704. this.trigger("KeyDown", {
  3705. key: Crafty.keys[k]
  3706. });
  3707. }
  3708. }
  3709. return this;
  3710. },
  3711. /**@
  3712. * #.enableControl
  3713. * @comp Multiway
  3714. * @sign public this .enableControl()
  3715. *
  3716. * Enable the component to listen to key events.
  3717. *
  3718. * @example
  3719. * ~~~
  3720. * this.enableControl();
  3721. * ~~~
  3722. */
  3723. enableControl: function () {
  3724. this.disableControls = false;
  3725. return this;
  3726. },
  3727. /**@
  3728. * #.disableControl
  3729. * @comp Multiway
  3730. * @sign public this .disableControl()
  3731. *
  3732. * Disable the component to listen to key events.
  3733. *
  3734. * @example
  3735. * ~~~
  3736. * this.disableControl();
  3737. * ~~~
  3738. */
  3739. disableControl: function () {
  3740. this.disableControls = true;
  3741. return this;
  3742. },
  3743. /**@
  3744. * #.speed
  3745. * @comp Multiway
  3746. * @sign public this .speed(Number speed)
  3747. * @param speed - The speed the entity has.
  3748. *
  3749. * Change the speed that the entity moves with.
  3750. *
  3751. * @example
  3752. * ~~~
  3753. * this.speed(2);
  3754. * ~~~
  3755. */
  3756. speed: function (speed) {
  3757. for (var k in this._keyDirection) {
  3758. var keyCode = Crafty.keys[k] || k;
  3759. this._keys[keyCode] = {
  3760. x: Math.round(Math.cos(this._keyDirection[k] * (Math.PI / 180)) * 1000 * speed.x) / 1000,
  3761. y: Math.round(Math.sin(this._keyDirection[k] * (Math.PI / 180)) * 1000 * speed.y) / 1000
  3762. };
  3763. }
  3764. return this;
  3765. }
  3766. });
  3767. /**@
  3768. * #Fourway
  3769. * @category Input
  3770. * Move an entity in four directions by using the
  3771. * arrow keys or `W`, `A`, `S`, `D`.
  3772. */
  3773. Crafty.c("Fourway", {
  3774. init: function () {
  3775. this.requires("Multiway");
  3776. },
  3777. /**@
  3778. * #.fourway
  3779. * @comp Fourway
  3780. * @sign public this .fourway(Number speed)
  3781. * @param speed - Amount of pixels to move the entity whilst a key is down
  3782. * Constructor to initialize the speed. Component will listen for key events and move the entity appropriately.
  3783. * This includes `Up Arrow`, `Right Arrow`, `Down Arrow`, `Left Arrow` as well as `W`, `A`, `S`, `D`.
  3784. *
  3785. * When direction changes a NewDirection event is triggered with an object detailing the new direction: {x: x_movement, y: y_movement}
  3786. * When entity has moved on either x- or y-axis a Moved event is triggered with an object specifying the old position {x: old_x, y: old_y}
  3787. *
  3788. * The key presses will move the entity in that direction by the speed passed in the argument.
  3789. *
  3790. * @see Multiway
  3791. */
  3792. fourway: function (speed) {
  3793. this.multiway(speed, {
  3794. UP_ARROW: -90,
  3795. DOWN_ARROW: 90,
  3796. RIGHT_ARROW: 0,
  3797. LEFT_ARROW: 180,
  3798. W: -90,
  3799. S: 90,
  3800. D: 0,
  3801. A: 180,
  3802. Z: -90,
  3803. Q: 180
  3804. });
  3805. return this;
  3806. }
  3807. });
  3808. /**@
  3809. * #Twoway
  3810. * @category Input
  3811. * @trigger NewDirection - When direction changes a NewDirection event is triggered with an object detailing the new direction: {x: x_movement, y: y_movement}. This is consistent with Fourway and Multiway components.
  3812. * @trigger Moved - When entity has moved on x-axis a Moved event is triggered with an object specifying the old position {x: old_x, y: old_y}
  3813. *
  3814. * Move an entity left or right using the arrow keys or `D` and `A` and jump using up arrow or `W`.
  3815. */
  3816. Crafty.c("Twoway", {
  3817. _speed: 3,
  3818. _up: false,
  3819. init: function () {
  3820. this.requires("Fourway, Keyboard, Gravity");
  3821. },
  3822. /**@
  3823. * #.twoway
  3824. * @comp Twoway
  3825. * @sign public this .twoway(Number speed[, Number jump])
  3826. * @param speed - Amount of pixels to move left or right
  3827. * @param jump - Vertical jump speed
  3828. *
  3829. * Constructor to initialize the speed and power of jump. Component will
  3830. * listen for key events and move the entity appropriately. This includes
  3831. * `Up Arrow`, `Right Arrow`, `Left Arrow` as well as `W`, `A`, `D`. Used with the
  3832. * `gravity` component to simulate jumping.
  3833. *
  3834. * The key presses will move the entity in that direction by the speed passed in
  3835. * the argument. Pressing the `Up Arrow` or `W` will cause the entity to jump.
  3836. *
  3837. * @see Gravity, Fourway
  3838. */
  3839. twoway: function (speed, jump) {
  3840. this.multiway(speed, {
  3841. RIGHT_ARROW: 0,
  3842. LEFT_ARROW: 180,
  3843. D: 0,
  3844. A: 180,
  3845. Q: 180
  3846. });
  3847. if (speed) this._speed = speed;
  3848. if (arguments.length < 2){
  3849. this._jumpSpeed = this._speed * 2;
  3850. } else{
  3851. this._jumpSpeed = jump;
  3852. }
  3853. this.bind("EnterFrame", function () {
  3854. if (this.disableControls) return;
  3855. if (this._up) {
  3856. this.y -= this._jumpSpeed;
  3857. this._falling = true;
  3858. this.trigger('Moved', { x: this._x, y: this._y + this._jumpSpeed });
  3859. }
  3860. }).bind("KeyDown", function (e) {
  3861. if (!this._falling && (e.key === Crafty.keys.UP_ARROW || e.key === Crafty.keys.W || e.key === Crafty.keys.Z))
  3862. this._up = true;
  3863. });
  3864. return this;
  3865. }
  3866. });
  3867. },{"./core.js":9}],9:[function(require,module,exports){
  3868. var version = require('./version');
  3869. /**@
  3870. * #Crafty
  3871. * @category Core
  3872. * Select a set of or single entities by components or an entity's ID.
  3873. *
  3874. * Crafty uses syntax similar to jQuery by having a selector engine to select entities by their components.
  3875. *
  3876. * If there is more than one match, the return value is an Array-like object listing the ID numbers of each matching entity. If there is exactly one match, the entity itself is returned. If you're not sure how many matches to expect, check the number of matches via Crafty(...).length. Alternatively, use Crafty(...).each(...), which works in all cases.
  3877. *
  3878. * @example
  3879. * ~~~
  3880. * Crafty("MyComponent")
  3881. * Crafty("Hello 2D Component")
  3882. * Crafty("Hello, 2D, Component")
  3883. * ~~~
  3884. *
  3885. * The first selector will return all entities that have the component `MyComponent`. The second will return all entities that have `Hello` and `2D` and `Component` whereas the last will return all entities that have at least one of those components (or).
  3886. *
  3887. * ~~~
  3888. * Crafty("*")
  3889. * ~~~
  3890. * Passing `*` will select all entities.
  3891. *
  3892. * ~~~
  3893. * Crafty(1)
  3894. * ~~~
  3895. * Passing an integer will select the entity with that `ID`.
  3896. *
  3897. * To work directly with an array of entities, use the `get()` method on a selection.
  3898. * To call a function in the context of each entity, use the `.each()` method.
  3899. *
  3900. * The event related methods such as `bind` and `trigger` will work on selections of entities.
  3901. *
  3902. * @see .get
  3903. * @see .each
  3904. */
  3905. var Crafty = function (selector) {
  3906. return new Crafty.fn.init(selector);
  3907. },
  3908. // Internal variables
  3909. GUID, frame, components, entities, handlers, onloads,
  3910. slice, rlist, rspace, milliSecPerFrame;
  3911. initState = function () {
  3912. GUID = 1, //GUID for entity IDs
  3913. frame = 0;
  3914. components = {}; //map of components and their functions
  3915. entities = {}; //map of entities and their data
  3916. handlers = {}; //global event handlers
  3917. onloads = []; //temporary storage of onload handlers
  3918. slice = Array.prototype.slice;
  3919. rlist = /\s*,\s*/;
  3920. rspace = /\s+/;
  3921. };
  3922. initState();
  3923. /**@
  3924. * #Crafty Core
  3925. * @category Core
  3926. * @trigger NewEntityName - After setting new name for entity - String - entity name
  3927. * @trigger NewComponent - when a new component is added to the entity - String - Component
  3928. * @trigger RemoveComponent - when a component is removed from the entity - String - Component
  3929. * @trigger Remove - when the entity is removed by calling .destroy()
  3930. *
  3931. * Set of methods added to every single entity.
  3932. */
  3933. Crafty.fn = Crafty.prototype = {
  3934. init: function (selector) {
  3935. //select entities by component
  3936. if (typeof selector === "string") {
  3937. var elem = 0, //index elements
  3938. e, //entity forEach
  3939. current,
  3940. and = false, //flags for multiple
  3941. or = false,
  3942. del,
  3943. comps,
  3944. score,
  3945. i, l;
  3946. if (selector === '*') {
  3947. i = 0;
  3948. for (e in entities) {
  3949. // entities is something like {2:entity2, 3:entity3, 11:entity11, ...}
  3950. // The for...in loop sets e to "2", "3", "11", ... i.e. all
  3951. // the entity ID numbers. e is a string, so +e converts to number type.
  3952. this[i] = +e;
  3953. i++;
  3954. }
  3955. this.length = i;
  3956. // if there's only one entity, return the actual entity
  3957. if (i === 1) {
  3958. return entities[this[0]];
  3959. }
  3960. return this;
  3961. }
  3962. //multiple components OR
  3963. if (selector.indexOf(',') !== -1) {
  3964. or = true;
  3965. del = rlist;
  3966. //deal with multiple components AND
  3967. } else if (selector.indexOf(' ') !== -1) {
  3968. and = true;
  3969. del = rspace;
  3970. }
  3971. //loop over entities
  3972. for (e in entities) {
  3973. if (!entities.hasOwnProperty(e)) continue; //skip
  3974. current = entities[e];
  3975. if (and || or) { //multiple components
  3976. comps = selector.split(del);
  3977. i = 0;
  3978. l = comps.length;
  3979. score = 0;
  3980. for (; i < l; i++) //loop over components
  3981. if (current.__c[comps[i]]) score++; //if component exists add to score
  3982. //if anded comps and has all OR ored comps and at least 1
  3983. if (and && score === l || or && score > 0) this[elem++] = +e;
  3984. } else if (current.__c[selector]) this[elem++] = +e; //convert to int
  3985. }
  3986. //extend all common components
  3987. if (elem > 0 && !and && !or) this.extend(components[selector]);
  3988. if (comps && and)
  3989. for (i = 0; i < l; i++) this.extend(components[comps[i]]);
  3990. this.length = elem; //length is the last index (already incremented)
  3991. // if there's only one entity, return the actual entity
  3992. if (elem === 1) {
  3993. return entities[this[elem - 1]];
  3994. }
  3995. } else { //Select a specific entity
  3996. if (!selector) { //nothin passed creates God entity
  3997. selector = 0;
  3998. if (!(selector in entities)) entities[selector] = this;
  3999. }
  4000. //if not exists, return undefined
  4001. if (!(selector in entities)) {
  4002. this.length = 0;
  4003. return this;
  4004. }
  4005. this[0] = selector;
  4006. this.length = 1;
  4007. //update from the cache
  4008. if (!this.__c) this.__c = {};
  4009. //update to the cache if NULL
  4010. if (!entities[selector]) entities[selector] = this;
  4011. return entities[selector]; //return the cached selector
  4012. }
  4013. return this;
  4014. },
  4015. /**@
  4016. * #.setName
  4017. * @comp Crafty Core
  4018. * @sign public this .setName(String name)
  4019. * @param name - A human readable name for debugging purposes.
  4020. *
  4021. * @example
  4022. * ~~~
  4023. * this.setName("Player");
  4024. * ~~~
  4025. */
  4026. setName: function (name) {
  4027. var entityName = String(name);
  4028. this._entityName = entityName;
  4029. this.trigger("NewEntityName", entityName);
  4030. return this;
  4031. },
  4032. /**@
  4033. * #.addComponent
  4034. * @comp Crafty Core
  4035. * @sign public this .addComponent(String componentList)
  4036. * @param componentList - A string of components to add separated by a comma `,`
  4037. * @sign public this .addComponent(String Component1[, .., String ComponentN])
  4038. * @param Component# - Component ID to add.
  4039. * Adds a component to the selected entities or entity.
  4040. *
  4041. * Components are used to extend the functionality of entities.
  4042. * This means it will copy properties and assign methods to
  4043. * augment the functionality of the entity.
  4044. *
  4045. * For adding multiple components, you can either pass a string with
  4046. * all the component names (separated by commas), or pass each component name as
  4047. * an argument.
  4048. *
  4049. * If the component has a function named `init` it will be called.
  4050. *
  4051. * If the entity already has the component, the component is skipped (nothing happens).
  4052. *
  4053. * @example
  4054. * ~~~
  4055. * this.addComponent("2D, Canvas");
  4056. * this.addComponent("2D", "Canvas");
  4057. * ~~~
  4058. */
  4059. addComponent: function (id) {
  4060. var uninit = [],
  4061. c = 0,
  4062. ul, //array of components to init
  4063. i = 0,
  4064. l, comps, comp;
  4065. //add multiple arguments
  4066. if (arguments.length > 1) {
  4067. l = arguments.length;
  4068. for (; i < l; i++) {
  4069. uninit.push(arguments[i]);
  4070. }
  4071. //split components if contains comma
  4072. } else if (id.indexOf(',') !== -1) {
  4073. comps = id.split(rlist);
  4074. l = comps.length;
  4075. for (; i < l; i++) {
  4076. uninit.push(comps[i]);
  4077. }
  4078. //single component passed
  4079. } else {
  4080. uninit.push(id);
  4081. }
  4082. //extend the components
  4083. ul = uninit.length;
  4084. for (; c < ul; c++) {
  4085. if (this.__c[uninit[c]] === true)
  4086. continue;
  4087. this.__c[uninit[c]] = true;
  4088. comp = components[uninit[c]];
  4089. this.extend(comp);
  4090. //if constructor, call it
  4091. if (comp && "init" in comp) {
  4092. comp.init.call(this);
  4093. }
  4094. }
  4095. this.trigger("NewComponent", uninit);
  4096. return this;
  4097. },
  4098. /**@
  4099. * #.toggleComponent
  4100. * @comp Crafty Core
  4101. * @sign public this .toggleComponent(String ComponentList)
  4102. * @param ComponentList - A string of components to add or remove separated by a comma `,`
  4103. * @sign public this .toggleComponent(String Component1[, .., String componentN])
  4104. * @param Component# - Component ID to add or remove.
  4105. * Add or Remove Components from an entity.
  4106. *
  4107. * @example
  4108. * ~~~
  4109. * var e = Crafty.e("2D,DOM,Test");
  4110. * e.toggleComponent("Test,Test2"); //Remove Test, add Test2
  4111. * e.toggleComponent("Test,Test2"); //Add Test, remove Test2
  4112. * ~~~
  4113. *
  4114. * ~~~
  4115. * var e = Crafty.e("2D,DOM,Test");
  4116. * e.toggleComponent("Test","Test2"); //Remove Test, add Test2
  4117. * e.toggleComponent("Test","Test2"); //Add Test, remove Test2
  4118. * e.toggleComponent("Test"); //Remove Test
  4119. * ~~~
  4120. */
  4121. toggleComponent: function (toggle) {
  4122. var i = 0,
  4123. l, comps;
  4124. if (arguments.length > 1) {
  4125. l = arguments.length;
  4126. for (; i < l; i++) {
  4127. if (this.has(arguments[i])) {
  4128. this.removeComponent(arguments[i]);
  4129. } else {
  4130. this.addComponent(arguments[i]);
  4131. }
  4132. }
  4133. //split components if contains comma
  4134. } else if (toggle.indexOf(',') !== -1) {
  4135. comps = toggle.split(rlist);
  4136. l = comps.length;
  4137. for (; i < l; i++) {
  4138. if (this.has(comps[i])) {
  4139. this.removeComponent(comps[i]);
  4140. } else {
  4141. this.addComponent(comps[i]);
  4142. }
  4143. }
  4144. //single component passed
  4145. } else {
  4146. if (this.has(toggle)) {
  4147. this.removeComponent(toggle);
  4148. } else {
  4149. this.addComponent(toggle);
  4150. }
  4151. }
  4152. return this;
  4153. },
  4154. /**@
  4155. * #.requires
  4156. * @comp Crafty Core
  4157. * @sign public this .requires(String componentList)
  4158. * @param componentList - List of components that must be added
  4159. *
  4160. * Makes sure the entity has the components listed. If the entity does not
  4161. * have the component, it will add it.
  4162. *
  4163. * (In the current version of Crafty, this function behaves exactly the same
  4164. * as `addComponent`. By convention, developers have used `requires` for
  4165. * component dependencies -- i.e. to indicate specifically that one component
  4166. * will only work properly if another component is present -- and used
  4167. * `addComponent` in all other situations.)
  4168. *
  4169. * @see .addComponent
  4170. */
  4171. requires: function (list) {
  4172. return this.addComponent(list);
  4173. },
  4174. /**@
  4175. * #.removeComponent
  4176. * @comp Crafty Core
  4177. * @sign public this .removeComponent(String Component[, soft])
  4178. * @param component - Component to remove
  4179. * @param soft - Whether to soft remove it (defaults to `true`)
  4180. *
  4181. * Removes a component from an entity. A soft remove (the default) will only
  4182. * refrain `.has()` from returning true. Hard will remove all
  4183. * associated properties and methods.
  4184. *
  4185. * @example
  4186. * ~~~
  4187. * var e = Crafty.e("2D,DOM,Test");
  4188. * e.removeComponent("Test"); //Soft remove Test component
  4189. * e.removeComponent("Test", false); //Hard remove Test component
  4190. * ~~~
  4191. */
  4192. removeComponent: function (id, soft) {
  4193. var comp = components[id];
  4194. this.trigger("RemoveComponent", id);
  4195. if (comp && "remove" in comp) {
  4196. comp.remove.call(this, false);
  4197. }
  4198. if (soft === false && comp) {
  4199. for (var prop in comp) {
  4200. delete this[prop];
  4201. }
  4202. }
  4203. delete this.__c[id];
  4204. return this;
  4205. },
  4206. /**@
  4207. * #.getId
  4208. * @comp Crafty Core
  4209. * @sign public Number .getId(void)
  4210. * Returns the ID of this entity.
  4211. *
  4212. * For better performance, simply use the this[0] property.
  4213. *
  4214. * @example
  4215. * Finding out the `ID` of an entity can be done by returning the property `0`.
  4216. * ~~~
  4217. * var ent = Crafty.e("2D");
  4218. * ent[0]; //ID
  4219. * ent.getId(); //also ID
  4220. * ~~~
  4221. */
  4222. getId: function () {
  4223. return this[0];
  4224. },
  4225. /**@
  4226. * #.has
  4227. * @comp Crafty Core
  4228. * @sign public Boolean .has(String component)
  4229. * Returns `true` or `false` depending on if the
  4230. * entity has the given component.
  4231. *
  4232. * For better performance, simply use the `.__c` object
  4233. * which will be `true` if the entity has the component or
  4234. * will not exist (or be `false`).
  4235. */
  4236. has: function (id) {
  4237. return !!this.__c[id];
  4238. },
  4239. /**@
  4240. * #.attr
  4241. * @comp Crafty Core
  4242. * @sign public this .attr(String property, * value)
  4243. * @param property - Property of the entity to modify
  4244. * @param value - Value to set the property to
  4245. * @sign public this .attr(Object map)
  4246. * @param map - Object where the key is the property to modify and the value as the property value
  4247. * @trigger Change - when properties change - {key: value}
  4248. *
  4249. * Use this method to set any property of the entity.
  4250. *
  4251. * @example
  4252. * ~~~
  4253. * this.attr({key: "value", prop: 5});
  4254. * this.key; //value
  4255. * this.prop; //5
  4256. *
  4257. * this.attr("key", "newvalue");
  4258. * this.key; //newvalue
  4259. * ~~~
  4260. */
  4261. attr: function (key, value) {
  4262. if (arguments.length === 1) {
  4263. //if just the key, return the value
  4264. if (typeof key === "string") {
  4265. return this[key];
  4266. }
  4267. //extend if object
  4268. this.extend(key);
  4269. this.trigger("Change", key); //trigger change event
  4270. return this;
  4271. }
  4272. //if key value pair
  4273. this[key] = value;
  4274. var change = {};
  4275. change[key] = value;
  4276. this.trigger("Change", change); //trigger change event
  4277. return this;
  4278. },
  4279. /**@
  4280. * #.toArray
  4281. * @comp Crafty Core
  4282. * @sign public this .toArray(void)
  4283. *
  4284. * This method will simply return the found entities as an array of ids. To get an array of the actual entities, use `get()`.
  4285. * @see .get
  4286. */
  4287. toArray: function () {
  4288. return slice.call(this, 0);
  4289. },
  4290. /**@
  4291. * #.timeout
  4292. * @comp Crafty Core
  4293. * @sign public this .timeout(Function callback, Number delay)
  4294. * @param callback - Method to execute after given amount of milliseconds
  4295. * @param delay - Amount of milliseconds to execute the method
  4296. *
  4297. * The delay method will execute a function after a given amount of time in milliseconds.
  4298. *
  4299. * Essentially a wrapper for `setTimeout`.
  4300. *
  4301. * @example
  4302. * Destroy itself after 100 milliseconds
  4303. * ~~~
  4304. * this.timeout(function() {
  4305. this.destroy();
  4306. * }, 100);
  4307. * ~~~
  4308. */
  4309. timeout: function (callback, duration) {
  4310. this.each(function () {
  4311. var self = this;
  4312. setTimeout(function () {
  4313. callback.call(self);
  4314. }, duration);
  4315. });
  4316. return this;
  4317. },
  4318. /**@
  4319. * #.bind
  4320. * @comp Crafty Core
  4321. * @sign public this .bind(String eventName, Function callback)
  4322. * @param eventName - Name of the event to bind to
  4323. * @param callback - Method to execute when the event is triggered
  4324. * Attach the current entity (or entities) to listen for an event.
  4325. *
  4326. * Callback will be invoked when an event with the event name passed
  4327. * is triggered. Depending on the event, some data may be passed
  4328. * via an argument to the callback function.
  4329. *
  4330. * The first argument is the event name (can be anything) whilst the
  4331. * second argument is the callback. If the event has data, the
  4332. * callback should have an argument.
  4333. *
  4334. * Events are arbitrary and provide communication between components.
  4335. * You can trigger or bind an event even if it doesn't exist yet.
  4336. *
  4337. * Unlike DOM events, Crafty events are exectued synchronously.
  4338. *
  4339. * @example
  4340. * ~~~
  4341. * this.attr("triggers", 0); //set a trigger count
  4342. * this.bind("myevent", function() {
  4343. * this.triggers++; //whenever myevent is triggered, increment
  4344. * });
  4345. * this.bind("EnterFrame", function() {
  4346. * this.trigger("myevent"); //trigger myevent on every frame
  4347. * });
  4348. * ~~~
  4349. *
  4350. * @see .trigger, .unbind
  4351. */
  4352. bind: function (event, callback) {
  4353. // (To learn how the handlers object works, see inline comment at Crafty.bind)
  4354. //optimization for 1 entity
  4355. if (this.length === 1) {
  4356. if (!handlers[event]) handlers[event] = {};
  4357. var h = handlers[event];
  4358. if (!h[this[0]]) h[this[0]] = []; //init handler array for entity
  4359. h[this[0]].push(callback); //add current callback
  4360. return this;
  4361. }
  4362. this.each(function () {
  4363. //init event collection
  4364. if (!handlers[event]) handlers[event] = {};
  4365. var h = handlers[event];
  4366. if (!h[this[0]]) h[this[0]] = []; //init handler array for entity
  4367. h[this[0]].push(callback); //add current callback
  4368. });
  4369. return this;
  4370. },
  4371. /**@
  4372. * #.uniqueBind
  4373. * @comp Crafty Core
  4374. * @sign public Number .uniqueBind(String eventName, Function callback)
  4375. * @param eventName - Name of the event to bind to
  4376. * @param callback - Method to execute upon event triggered
  4377. * @returns ID of the current callback used to unbind
  4378. *
  4379. * Works like Crafty.bind, but prevents a callback from being bound multiple times.
  4380. *
  4381. * @see .bind
  4382. */
  4383. uniqueBind: function (event, callback) {
  4384. this.unbind(event, callback);
  4385. this.bind(event, callback);
  4386. },
  4387. /**@
  4388. * #.one
  4389. * @comp Crafty Core
  4390. * @sign public Number one(String eventName, Function callback)
  4391. * @param eventName - Name of the event to bind to
  4392. * @param callback - Method to execute upon event triggered
  4393. * @returns ID of the current callback used to unbind
  4394. *
  4395. * Works like Crafty.bind, but will be unbound once the event triggers.
  4396. *
  4397. * @see .bind
  4398. */
  4399. one: function (event, callback) {
  4400. var self = this;
  4401. var oneHandler = function (data) {
  4402. callback.call(self, data);
  4403. self.unbind(event, oneHandler);
  4404. };
  4405. return self.bind(event, oneHandler);
  4406. },
  4407. /**@
  4408. * #.unbind
  4409. * @comp Crafty Core
  4410. * @sign public this .unbind(String eventName[, Function callback])
  4411. * @param eventName - Name of the event to unbind
  4412. * @param callback - Function to unbind
  4413. * Removes binding with an event from current entity.
  4414. *
  4415. * Passing an event name will remove all events bound to
  4416. * that event. Passing a reference to the callback will
  4417. * unbind only that callback.
  4418. * @see .bind, .trigger
  4419. */
  4420. unbind: function (event, callback) {
  4421. // (To learn how the handlers object works, see inline comment at Crafty.bind)
  4422. this.each(function () {
  4423. var hdl = handlers[event],
  4424. i = 0,
  4425. l, current;
  4426. //if no events, cancel
  4427. if (hdl && hdl[this[0]]) l = hdl[this[0]].length;
  4428. else return this;
  4429. //if no function, delete all
  4430. if (!callback) {
  4431. delete hdl[this[0]];
  4432. return this;
  4433. }
  4434. //look for a match if the function is passed
  4435. for (; i < l; i++) {
  4436. current = hdl[this[0]];
  4437. if (current[i] == callback) {
  4438. delete current[i];
  4439. }
  4440. }
  4441. });
  4442. return this;
  4443. },
  4444. /**@
  4445. * #.trigger
  4446. * @comp Crafty Core
  4447. * @sign public this .trigger(String eventName[, Object data])
  4448. * @param eventName - Event to trigger
  4449. * @param data - Arbitrary data that will be passed into every callback as an argument
  4450. * Trigger an event with arbitrary data. Will invoke all callbacks with
  4451. * the context (value of `this`) of the current entity object.
  4452. *
  4453. * *Note: This will only execute callbacks within the current entity, no other entity.*
  4454. *
  4455. * The first argument is the event name to trigger and the optional
  4456. * second argument is the arbitrary event data. This can be absolutely anything.
  4457. *
  4458. * Unlike DOM events, Crafty events are exectued synchronously.
  4459. */
  4460. trigger: function (event, data) {
  4461. // (To learn how the handlers object works, see inline comment at Crafty.bind)
  4462. if (this.length === 1) {
  4463. //find the handlers assigned to the event and entity
  4464. if (handlers[event] && handlers[event][this[0]]) {
  4465. var callbacks = handlers[event][this[0]],
  4466. i;
  4467. for (i = 0; i < callbacks.length; i++) {
  4468. if (typeof callbacks[i] === "undefined") {
  4469. callbacks.splice(i, 1);
  4470. i--;
  4471. } else {
  4472. callbacks[i].call(this, data);
  4473. }
  4474. }
  4475. }
  4476. return this;
  4477. }
  4478. this.each(function () {
  4479. //find the handlers assigned to the event and entity
  4480. if (handlers[event] && handlers[event][this[0]]) {
  4481. var callbacks = handlers[event][this[0]],
  4482. i;
  4483. for (i = 0; i < callbacks.length; i++) {
  4484. if (typeof callbacks[i] === "undefined") {
  4485. callbacks.splice(i, 1);
  4486. i--;
  4487. } else {
  4488. callbacks[i].call(this, data);
  4489. }
  4490. }
  4491. }
  4492. });
  4493. return this;
  4494. },
  4495. /**@
  4496. * #.each
  4497. * @comp Crafty Core
  4498. * @sign public this .each(Function method)
  4499. * @param method - Method to call on each iteration
  4500. * Iterates over found entities, calling a function for every entity.
  4501. *
  4502. * The function will be called for every entity and will pass the index
  4503. * in the iteration as an argument. The context (value of `this`) of the
  4504. * function will be the current entity in the iteration.
  4505. *
  4506. * @example
  4507. * Destroy every second 2D entity
  4508. * ~~~
  4509. * Crafty("2D").each(function(i) {
  4510. * if(i % 2 === 0) {
  4511. * this.destroy();
  4512. * }
  4513. * });
  4514. * ~~~
  4515. */
  4516. each: function (func) {
  4517. var i = 0,
  4518. l = this.length;
  4519. for (; i < l; i++) {
  4520. //skip if not exists
  4521. if (!entities[this[i]]) continue;
  4522. func.call(entities[this[i]], i);
  4523. }
  4524. return this;
  4525. },
  4526. /**@
  4527. * #.get
  4528. * @comp Crafty Core
  4529. * @sign public Array .get()
  4530. * @returns An array of entities corresponding to the active selector
  4531. *
  4532. * @sign public Entity .get(Number index)
  4533. * @returns an entity belonging to the current selection
  4534. * @param index - The index of the entity to return. If negative, counts back from the end of the array.
  4535. *
  4536. *
  4537. * @example
  4538. * Get an array containing every "2D" entity
  4539. * ~~~
  4540. * var arr = Crafty("2D").get()
  4541. * ~~~
  4542. * Get the first entity matching the selector
  4543. * ~~~
  4544. * // equivalent to Crafty("2D").get()[0], but doesn't create a new array
  4545. * var e = Crafty("2D").get(0)
  4546. * ~~~
  4547. * Get the last "2D" entity matching the selector
  4548. * ~~~
  4549. * var e = Crafty("2D").get(-1)
  4550. * ~~~
  4551. *
  4552. */
  4553. get: function(index) {
  4554. var l = this.length;
  4555. if (typeof index !== "undefined") {
  4556. if (index >= l || index+l < 0)
  4557. return undefined;
  4558. if (index>=0)
  4559. return entities[this[index]];
  4560. else
  4561. return entities[this[index+l]];
  4562. } else {
  4563. var i=0, result = [];
  4564. for (; i < l; i++) {
  4565. //skip if not exists
  4566. if (!entities[this[i]]) continue;
  4567. result.push( entities[this[i]] );
  4568. }
  4569. return result;
  4570. }
  4571. },
  4572. /**@
  4573. * #.clone
  4574. * @comp Crafty Core
  4575. * @sign public Entity .clone(void)
  4576. * @returns Cloned entity of the current entity
  4577. *
  4578. * Method will create another entity with the exact same
  4579. * properties, components and methods as the current entity.
  4580. */
  4581. clone: function () {
  4582. var comps = this.__c,
  4583. comp,
  4584. prop,
  4585. clone = Crafty.e();
  4586. for (comp in comps) {
  4587. clone.addComponent(comp);
  4588. }
  4589. for (prop in this) {
  4590. if (prop != "0" && prop != "_global" && prop != "_changed" && typeof this[prop] != "function" && typeof this[prop] != "object") {
  4591. clone[prop] = this[prop];
  4592. }
  4593. }
  4594. return clone;
  4595. },
  4596. /**@
  4597. * #.setter
  4598. * @comp Crafty Core
  4599. * @sign public this .setter(String property, Function callback)
  4600. * @param property - Property to watch for modification
  4601. * @param callback - Method to execute if the property is modified
  4602. * Will watch a property waiting for modification and will then invoke the
  4603. * given callback when attempting to modify.
  4604. *
  4605. */
  4606. setter: function (prop, callback) {
  4607. if (Crafty.support.setter) {
  4608. this.__defineSetter__(prop, callback);
  4609. } else if (Crafty.support.defineProperty) {
  4610. Object.defineProperty(this, prop, {
  4611. set: callback,
  4612. configurable: true
  4613. });
  4614. }
  4615. return this;
  4616. },
  4617. /**@
  4618. * #.destroy
  4619. * @comp Crafty Core
  4620. * @sign public this .destroy(void)
  4621. * Will remove all event listeners and delete all properties as well as removing from the stage
  4622. */
  4623. destroy: function () {
  4624. //remove all event handlers, delete from entities
  4625. this.each(function () {
  4626. var comp;
  4627. this.trigger("Remove");
  4628. for (var compName in this.__c) {
  4629. comp = components[compName];
  4630. if (comp && "remove" in comp)
  4631. comp.remove.call(this, true);
  4632. }
  4633. for (var e in handlers) {
  4634. this.unbind(e);
  4635. }
  4636. delete entities[this[0]];
  4637. });
  4638. }
  4639. };
  4640. //give the init instances the Crafty prototype
  4641. Crafty.fn.init.prototype = Crafty.fn;
  4642. /**@
  4643. * #Crafty.extend
  4644. * @category Core
  4645. * Used to extend the Crafty namespace.
  4646. *
  4647. */
  4648. Crafty.extend = Crafty.fn.extend = function (obj) {
  4649. var target = this,
  4650. key;
  4651. //don't bother with nulls
  4652. if (!obj) return target;
  4653. for (key in obj) {
  4654. if (target === obj[key]) continue; //handle circular reference
  4655. target[key] = obj[key];
  4656. }
  4657. return target;
  4658. };
  4659. Crafty.extend({
  4660. /**@
  4661. * #Crafty.init
  4662. * @category Core
  4663. * @trigger Load - Just after the viewport is initialised. Before the EnterFrame loops is started
  4664. * @sign public this Crafty.init([Number width, Number height, String stage_elem])
  4665. * @sign public this Crafty.init([Number width, Number height, HTMLElement stage_elem])
  4666. * @param Number width - Width of the stage
  4667. * @param Number height - Height of the stage
  4668. * @param String or HTMLElement stage_elem - the element to use for the stage
  4669. *
  4670. * Sets the element to use as the stage, creating it if necessary. By default a div with id 'cr-stage' is used, but if the 'stage_elem' argument is provided that will be used instead. (see `Crafty.viewport.init`)
  4671. *
  4672. * Starts the `EnterFrame` interval. This will call the `EnterFrame` event for every frame.
  4673. *
  4674. * Can pass width and height values for the stage otherwise will default to window size (see `Crafty.DOM.window`).
  4675. *
  4676. * All `Load` events will be executed.
  4677. *
  4678. * Uses `requestAnimationFrame` to sync the drawing with the browser but will default to `setInterval` if the browser does not support it.
  4679. * @see Crafty.stop, Crafty.viewport
  4680. */
  4681. init: function (w, h, stage_elem) {
  4682. Crafty.viewport.init(w, h, stage_elem);
  4683. //call all arbitrary functions attached to onload
  4684. this.trigger("Load");
  4685. this.timer.init();
  4686. return this;
  4687. },
  4688. /**@
  4689. * #Crafty.getVersion
  4690. * @category Core
  4691. * @sign public String Crafty.getVersion()
  4692. * @returns Current version of Crafty as a string
  4693. *
  4694. * Return current version of crafty
  4695. *
  4696. * @example
  4697. * ~~~
  4698. * Crafty.getVersion(); //'0.5.2'
  4699. * ~~~
  4700. */
  4701. getVersion: function () {
  4702. return version;
  4703. },
  4704. /**@
  4705. * #Crafty.stop
  4706. * @category Core
  4707. * @trigger CraftyStop - when the game is stopped
  4708. * @sign public this Crafty.stop([bool clearState])
  4709. * @param clearState - if true the stage and all game state is cleared.
  4710. *
  4711. * Stops the EnterFrame interval and removes the stage element.
  4712. *
  4713. * To restart, use `Crafty.init()`.
  4714. * @see Crafty.init
  4715. */
  4716. stop: function (clearState) {
  4717. this.timer.stop();
  4718. if (clearState) {
  4719. Crafty.audio.remove();
  4720. if (Crafty.stage && Crafty.stage.elem.parentNode) {
  4721. var newCrStage = document.createElement('div');
  4722. newCrStage.id = Crafty.stage.elem.id;
  4723. Crafty.stage.elem.parentNode.replaceChild(newCrStage, Crafty.stage.elem);
  4724. }
  4725. initState();
  4726. }
  4727. Crafty.trigger("CraftyStop");
  4728. return this;
  4729. },
  4730. /**@
  4731. * #Crafty.pause
  4732. * @category Core
  4733. * @trigger Pause - when the game is paused
  4734. * @trigger Unpause - when the game is unpaused
  4735. * @sign public this Crafty.pause(void)
  4736. *
  4737. * Pauses the game by stopping the EnterFrame event from firing. If the game is already paused it is unpaused.
  4738. * You can pass a boolean parameter if you want to pause or unpause mo matter what the current state is.
  4739. * Modern browsers pauses the game when the page is not visible to the user. If you want the Pause event
  4740. * to be triggered when that happens you can enable autoPause in `Crafty.settings`.
  4741. *
  4742. * @example
  4743. * Have an entity pause the game when it is clicked.
  4744. * ~~~
  4745. * button.bind("click", function() {
  4746. * Crafty.pause();
  4747. * });
  4748. * ~~~
  4749. */
  4750. pause: function (toggle) {
  4751. if (arguments.length === 1 ? toggle : !this._paused) {
  4752. this.trigger('Pause');
  4753. this._paused = true;
  4754. setTimeout(function () {
  4755. Crafty.timer.stop();
  4756. }, 0);
  4757. Crafty.keydown = {};
  4758. } else {
  4759. this.trigger('Unpause');
  4760. this._paused = false;
  4761. setTimeout(function () {
  4762. Crafty.timer.init();
  4763. }, 0);
  4764. }
  4765. return this;
  4766. },
  4767. /**@
  4768. * #Crafty.isPaused
  4769. * @category Core
  4770. * @sign public this Crafty.isPaused()
  4771. *
  4772. * Check whether the game is already paused or not.
  4773. *
  4774. * @example
  4775. * ~~~
  4776. * Crafty.isPaused();
  4777. * ~~~
  4778. */
  4779. isPaused: function () {
  4780. return this._paused;
  4781. },
  4782. /**@
  4783. * #Crafty.timer
  4784. * @category Game Loop
  4785. * Handles game ticks
  4786. */
  4787. timer: (function () {
  4788. /*
  4789. * `window.requestAnimationFrame` or its variants is called for animation.
  4790. * `.requestID` keeps a record of the return value previous `window.requestAnimationFrame` call.
  4791. * This is an internal variable. Used to stop frame.
  4792. */
  4793. var tick, requestID;
  4794. // Internal variables used to control the game loop. Use Crafty.timer.steptype() to set these.
  4795. var mode = "fixed",
  4796. maxFramesPerStep = 5,
  4797. maxTimestep = 40;
  4798. // variables used by the game loop to track state
  4799. var endTime = 0,
  4800. timeSlip = 0,
  4801. gameTime;
  4802. // Controls the target rate of fixed mode loop. Set these with the Crafty.timer.FPS function
  4803. var FPS = 50,
  4804. milliSecPerFrame = 1000 / FPS;
  4805. return {
  4806. init: function () {
  4807. // When first called, set the gametime one frame before now!
  4808. if (typeof gameTime === "undefined")
  4809. gameTime = (new Date().getTime()) - milliSecPerFrame;
  4810. var onFrame = window.requestAnimationFrame ||
  4811. window.webkitRequestAnimationFrame ||
  4812. window.mozRequestAnimationFrame ||
  4813. window.oRequestAnimationFrame ||
  4814. window.msRequestAnimationFrame ||
  4815. null;
  4816. if (onFrame) {
  4817. tick = function () {
  4818. Crafty.timer.step();
  4819. requestID = onFrame(tick);
  4820. //console.log(requestID + ', ' + frame)
  4821. };
  4822. tick();
  4823. } else {
  4824. tick = setInterval(function () {
  4825. Crafty.timer.step();
  4826. }, 1000 / FPS);
  4827. }
  4828. },
  4829. stop: function () {
  4830. Crafty.trigger("CraftyStopTimer");
  4831. if (typeof tick === "number") clearInterval(tick);
  4832. var onFrame = window.cancelRequestAnimationFrame ||
  4833. window.webkitCancelRequestAnimationFrame ||
  4834. window.mozCancelRequestAnimationFrame ||
  4835. window.oCancelRequestAnimationFrame ||
  4836. window.msCancelRequestAnimationFrame ||
  4837. null;
  4838. if (onFrame) onFrame(requestID);
  4839. tick = null;
  4840. },
  4841. /**@
  4842. * #Crafty.timer.steptype
  4843. * @comp Crafty.timer
  4844. * @sign public void Crafty.timer.steptype(mode [, maxTimeStep])
  4845. * Can be called to set the type of timestep the game loop uses
  4846. * @param mode - the type of time loop. Allowed values are "fixed", "semifixed", and "variable". Crafty defaults to "fixed".
  4847. * @param mode - For "fixed", sets the max number of frames per step. For "variable" and "semifixed", sets the maximum time step allowed.
  4848. *
  4849. * * In "fixed" mode, each frame is sent the same value of `dt`, and to achieve the target game speed, mulitiple frame events are triggered before each render.
  4850. * * In "variable" mode, there is only one frame triggered per render. This recieves a value of `dt` equal to the actual elapsed time since the last frame.
  4851. * * In "semifixed" mode, multiple frames per render are processed, and the total time since the last frame is divided evenly between them.
  4852. *
  4853. */
  4854. steptype: function (newmode, option) {
  4855. if (newmode === "variable" || newmode === "semifixed") {
  4856. mode = newmode;
  4857. if (option)
  4858. maxTimestep = option;
  4859. } else if (newmode === "fixed") {
  4860. mode = "fixed";
  4861. if (option)
  4862. maxFramesPerStep = option;
  4863. } else {
  4864. throw "Invalid step type specified";
  4865. }
  4866. },
  4867. /**@
  4868. * #Crafty.timer.step
  4869. * @comp Crafty.timer
  4870. * @sign public void Crafty.timer.step()
  4871. * @trigger EnterFrame - Triggered on each frame. Passes the frame number, and the amount of time since the last frame. If the time is greater than maxTimestep, that will be used instead. (The default value of maxTimestep is 50 ms.) - { frame: Number, dt:Number }
  4872. * @trigger RenderScene - Triggered every time a scene should be rendered
  4873. * @trigger MeasureWaitTime - Triggered at the beginning of each step after the first. Passes the time the game loop waited between steps. - Number
  4874. * @trigger MeasureFrameTime - Triggered after each step. Passes the time it took to advance one frame. - Number
  4875. * @trigger MeasureRenderTime - Triggered after each render. Passes the time it took to render the scene - Number
  4876. * Advances the game by triggering `EnterFrame` and `RenderScene`
  4877. */
  4878. step: function () {
  4879. var drawTimeStart, dt, lastFrameTime, loops = 0;
  4880. currentTime = new Date().getTime();
  4881. if (endTime > 0)
  4882. Crafty.trigger("MeasureWaitTime", currentTime - endTime);
  4883. // If we're currently ahead of the current time, we need to wait until we're not!
  4884. if (gameTime + timeSlip >= currentTime) {
  4885. endTime = currentTime;
  4886. return;
  4887. }
  4888. var netTimeStep = currentTime - (gameTime + timeSlip);
  4889. // We try to keep up with the target FPS by processing multiple frames per render
  4890. // If we're hopelessly behind, stop trying to catch up.
  4891. if (netTimeStep > milliSecPerFrame * 20) {
  4892. //gameTime = currentTime - milliSecPerFrame;
  4893. timeSlip += netTimeStep - milliSecPerFrame;
  4894. netTimeStep = milliSecPerFrame;
  4895. }
  4896. // Set up how time is incremented
  4897. if (mode === "fixed") {
  4898. loops = Math.ceil(netTimeStep / milliSecPerFrame);
  4899. // maxFramesPerStep adjusts how willing we are to delay drawing in order to keep at the target FPS
  4900. loops = Math.min(loops, maxFramesPerStep);
  4901. dt = milliSecPerFrame;
  4902. } else if (mode === "variable") {
  4903. loops = 1;
  4904. dt = netTimeStep;
  4905. // maxTimestep is the maximum time to be processed in a frame. (Large dt => unstable physics)
  4906. dt = Math.min(dt, maxTimestep);
  4907. } else if (mode === "semifixed") {
  4908. loops = Math.ceil(netTimeStep / maxTimestep);
  4909. dt = netTimeStep / loops;
  4910. }
  4911. // Process frames, incrementing the game clock with each frame.
  4912. // dt is determined by the mode
  4913. for (var i = 0; i < loops; i++) {
  4914. lastFrameTime = currentTime;
  4915. // Everything that changes over time hooks into this event
  4916. Crafty.trigger("EnterFrame", {
  4917. frame: frame++,
  4918. dt: dt,
  4919. gameTime: gameTime
  4920. });
  4921. gameTime += dt;
  4922. currentTime = new Date().getTime();
  4923. Crafty.trigger("MeasureFrameTime", currentTime - lastFrameTime);
  4924. }
  4925. //If any frames were processed, render the results
  4926. if (loops > 0) {
  4927. drawTimeStart = currentTime;
  4928. Crafty.trigger("RenderScene");
  4929. // Post-render cleanup opportunity
  4930. Crafty.trigger("PostRender");
  4931. currentTime = new Date().getTime();
  4932. Crafty.trigger("MeasureRenderTime", currentTime - drawTimeStart);
  4933. }
  4934. endTime = currentTime;
  4935. },
  4936. /**@
  4937. * #Crafty.timer.FPS
  4938. * @comp Crafty.timer
  4939. * @sign public void Crafty.timer.FPS()
  4940. * Returns the target frames per second. This is not an actual frame rate.
  4941. * @sign public void Crafty.timer.FPS(Number value)
  4942. * @param value - the target rate
  4943. * Sets the target frames per second. This is not an actual frame rate.
  4944. * The default rate is 50.
  4945. */
  4946. FPS: function (value) {
  4947. if (typeof value == "undefined")
  4948. return FPS;
  4949. else {
  4950. FPS = value;
  4951. milliSecPerFrame = 1000 / FPS;
  4952. }
  4953. },
  4954. /**@
  4955. * #Crafty.timer.simulateFrames
  4956. * @comp Crafty.timer
  4957. * @sign public this Crafty.timer.simulateFrames(Number frames[, Number timestep])
  4958. * Advances the game state by a number of frames and draws the resulting stage at the end. Useful for tests and debugging.
  4959. * @param frames - number of frames to simulate
  4960. * @param timestep - the duration to pass each frame. Defaults to milliSecPerFrame (20 ms) if not specified.
  4961. */
  4962. simulateFrames: function (frames, timestep) {
  4963. if (typeof timestep === "undefined")
  4964. timestep = milliSecPerFrame;
  4965. while (frames-- > 0) {
  4966. Crafty.trigger("EnterFrame", {
  4967. frame: frame++,
  4968. dt: timestep
  4969. });
  4970. }
  4971. Crafty.trigger("RenderScene");
  4972. }
  4973. };
  4974. })(),
  4975. /**@
  4976. * #Crafty.e
  4977. * @category Core
  4978. * @trigger NewEntity - When the entity is created and all components are added - { id:Number }
  4979. * @sign public Entity Crafty.e(String componentList)
  4980. * @param componentList - List of components to assign to new entity
  4981. * @sign public Entity Crafty.e(String component1[, .., String componentN])
  4982. * @param component# - Component to add
  4983. *
  4984. * Creates an entity. Any arguments will be applied in the same
  4985. * way `.addComponent()` is applied as a quick way to add components.
  4986. *
  4987. * Any component added will augment the functionality of
  4988. * the created entity by assigning the properties and methods from the component to the entity.
  4989. *
  4990. * @example
  4991. * ~~~
  4992. * var myEntity = Crafty.e("2D, DOM, Color");
  4993. * ~~~
  4994. *
  4995. * @see Crafty.c
  4996. */
  4997. e: function () {
  4998. var id = UID(),
  4999. craft;
  5000. entities[id] = null; //register the space
  5001. entities[id] = craft = Crafty(id);
  5002. if (arguments.length > 0) {
  5003. craft.addComponent.apply(craft, arguments);
  5004. }
  5005. craft.setName('Entity #' + id); //set default entity human readable name
  5006. craft.addComponent("obj"); //every entity automatically assumes obj
  5007. Crafty.trigger("NewEntity", {
  5008. id: id
  5009. });
  5010. return craft;
  5011. },
  5012. /**@
  5013. * #Crafty.c
  5014. * @category Core
  5015. * @sign public void Crafty.c(String name, Object component)
  5016. * @param name - Name of the component
  5017. * @param component - Object with the component's properties and methods
  5018. * Creates a component where the first argument is the ID and the second
  5019. * is the object that will be inherited by entities.
  5020. *
  5021. * A couple of methods are treated specially. They are invoked in partiular contexts, and (in those contexts) cannot be overridden by other components.
  5022. *
  5023. * - `init` will be called when the component is added to an entity
  5024. * - `remove` will be called just before a component is removed, or before an entity is destroyed. It is passed a single boolean parameter that is `true` if the entity is being destroyed.
  5025. *
  5026. * In addition to these hardcoded special methods, there are some conventions for writing components.
  5027. *
  5028. * - Properties or methods that start with an underscore are considered private.
  5029. * - A method with the same name as the component is considered to be a constructor
  5030. * and is generally used when you need to pass configuration data to the component on a per entity basis.
  5031. *
  5032. * @example
  5033. * ~~~
  5034. * Crafty.c("Annoying", {
  5035. * _message: "HiHi",
  5036. * init: function() {
  5037. * this.bind("EnterFrame", function() { alert(this.message); });
  5038. * },
  5039. * annoying: function(message) { this.message = message; }
  5040. * });
  5041. *
  5042. * Crafty.e("Annoying").annoying("I'm an orange...");
  5043. * ~~~
  5044. *
  5045. *
  5046. * WARNING:
  5047. *
  5048. * in the example above the field _message is local to the entity. That is, if you create many entities with the Annoying component they can all have different values for _message. That is because it is a simple value, and simple values are copied by value. If however the field had been an object or array, the value would have been shared by all entities with the component because complex types are copied by reference in javascript. This is probably not what you want and the following example demonstrates how to work around it:
  5049. *
  5050. * ~~~
  5051. * Crafty.c("MyComponent", {
  5052. * _iAmShared: { a: 3, b: 4 },
  5053. * init: function() {
  5054. * this._iAmNotShared = { a: 3, b: 4 };
  5055. * },
  5056. * });
  5057. * ~~~
  5058. *
  5059. * @see Crafty.e
  5060. */
  5061. c: function (compName, component) {
  5062. components[compName] = component;
  5063. },
  5064. /**@
  5065. * #Crafty.trigger
  5066. * @category Core, Events
  5067. * @sign public void Crafty.trigger(String eventName, * data)
  5068. * @param eventName - Name of the event to trigger
  5069. * @param data - Arbitrary data to pass into the callback as an argument
  5070. *
  5071. * This method will trigger every single callback attached to the event name. This means
  5072. * every global event and every entity that has a callback.
  5073. *
  5074. * @see Crafty.bind
  5075. */
  5076. trigger: function (event, data) {
  5077. // (To learn how the handlers object works, see inline comment at Crafty.bind)
  5078. var hdl = handlers[event],
  5079. h, i, l, callbacks, context;
  5080. //loop over every object bound
  5081. for (h in hdl) {
  5082. // Check whether h needs to be processed
  5083. if (!hdl.hasOwnProperty(h)) continue;
  5084. callbacks = hdl[h];
  5085. if (!callbacks || callbacks.length === 0) continue;
  5086. //if an entity, call with that context; else the global context
  5087. if (entities[h])
  5088. context = Crafty(+h);
  5089. else
  5090. context = Crafty;
  5091. //loop over every handler within object
  5092. for (i = 0; i < callbacks.length; i++) {
  5093. // Remove a callback if it has been deleted
  5094. if (typeof callbacks[i] === "undefined") {
  5095. callbacks.splice(i, 1);
  5096. i--;
  5097. } else
  5098. callbacks[i].call(context, data);
  5099. }
  5100. }
  5101. },
  5102. /**@
  5103. * #Crafty.bind
  5104. * @category Core, Events
  5105. * @sign public Number bind(String eventName, Function callback)
  5106. * @param eventName - Name of the event to bind to
  5107. * @param callback - Method to execute upon event triggered
  5108. * @returns callback function which can be used for unbind
  5109. *
  5110. * Binds to a global event. Method will be executed when `Crafty.trigger` is used
  5111. * with the event name.
  5112. *
  5113. * @see Crafty.trigger, Crafty.unbind
  5114. */
  5115. bind: function (event, callback) {
  5116. // Background: The structure of the global object "handlers"
  5117. // ---------------------------------------------------------
  5118. // Here is an example of what "handlers" can look like:
  5119. // handlers ===
  5120. // { Move: {5:[fnA], 6:[fnB, fnC], global:[fnD]},
  5121. // Change: {6:[fnE]}
  5122. // }
  5123. // In this example, when the 'Move' event is triggered on entity #6 (e.g.
  5124. // entity6.trigger('Move')), it causes the execution of fnB() and fnC(). When
  5125. // the Move event is triggered globally (i.e. Crafty.trigger('Move')), it
  5126. // will execute fnA, fnB, fnC, fnD.
  5127. //
  5128. // In this example, "this" is bound to entity #6 whenever fnB() is executed, and
  5129. // "this" is bound to Crafty whenever fnD() is executed.
  5130. //
  5131. // In other words, the structure of "handlers" is:
  5132. //
  5133. // handlers[event][entityID or 'global'] === (Array of callback functions)
  5134. if (!handlers[event]) handlers[event] = {};
  5135. var hdl = handlers[event];
  5136. if (!hdl.global) hdl.global = [];
  5137. hdl.global.push(callback);
  5138. return callback;
  5139. },
  5140. /**@
  5141. * #Crafty.uniqueBind
  5142. * @category Core, Events
  5143. * @sign public Number uniqueBind(String eventName, Function callback)
  5144. * @param eventName - Name of the event to bind to
  5145. * @param callback - Method to execute upon event triggered
  5146. * @returns callback function which can be used for unbind
  5147. *
  5148. * Works like Crafty.bind, but prevents a callback from being bound multiple times.
  5149. *
  5150. * @see Crafty.bind
  5151. */
  5152. uniqueBind: function (event, callback) {
  5153. this.unbind(event, callback);
  5154. return this.bind(event, callback);
  5155. },
  5156. /**@
  5157. * #Crafty.one
  5158. * @category Core, Events
  5159. * @sign public Number one(String eventName, Function callback)
  5160. * @param eventName - Name of the event to bind to
  5161. * @param callback - Method to execute upon event triggered
  5162. * @returns callback function which can be used for unbind
  5163. *
  5164. * Works like Crafty.bind, but will be unbound once the event triggers.
  5165. *
  5166. * @see Crafty.bind
  5167. */
  5168. one: function (event, callback) {
  5169. var self = this;
  5170. var oneHandler = function (data) {
  5171. callback.call(self, data);
  5172. self.unbind(event, oneHandler);
  5173. };
  5174. return self.bind(event, oneHandler);
  5175. },
  5176. /**@
  5177. * #Crafty.unbind
  5178. * @category Core, Events
  5179. * @sign public Boolean Crafty.unbind(String eventName, Function callback)
  5180. * @param eventName - Name of the event to unbind
  5181. * @param callback - Function to unbind
  5182. * @sign public Boolean Crafty.unbind(String eventName, Number callbackID)
  5183. * @param callbackID - ID of the callback
  5184. * @returns True or false depending on if a callback was unbound
  5185. * Unbind any event from any entity or global event.
  5186. * @example
  5187. * ~~~
  5188. * var play_gameover_sound = function () {...};
  5189. * Crafty.bind('GameOver', play_gameover_sound);
  5190. * ...
  5191. * Crafty.unbind('GameOver', play_gameover_sound);
  5192. * ~~~
  5193. *
  5194. * The first line defines a callback function. The second line binds that
  5195. * function so that `Crafty.trigger('GameOver')` causes that function to
  5196. * run. The third line unbinds that function.
  5197. *
  5198. * ~~~
  5199. * Crafty.unbind('GameOver');
  5200. * ~~~
  5201. *
  5202. * This unbinds ALL global callbacks for the event 'GameOver'. That
  5203. * includes all callbacks attached by `Crafty.bind('GameOver', ...)`, but
  5204. * none of the callbacks attached by `some_entity.bind('GameOver', ...)`.
  5205. */
  5206. unbind: function (event, callback) {
  5207. // (To learn how the handlers object works, see inline comment at Crafty.bind)
  5208. var hdl = handlers[event],
  5209. i, l, global_callbacks, found_match;
  5210. if (hdl === undefined || hdl.global === undefined || hdl.global.length === 0) {
  5211. return false;
  5212. }
  5213. // If no callback was supplied, delete everything
  5214. if (arguments.length === 1) {
  5215. delete hdl.global;
  5216. return true;
  5217. }
  5218. // loop over the globally-attached events
  5219. global_callbacks = hdl.global;
  5220. found_match = false;
  5221. for (i = 0, l = global_callbacks.length; i < l; i++) {
  5222. if (global_callbacks[i] === callback) {
  5223. found_match = true;
  5224. delete global_callbacks[i];
  5225. }
  5226. }
  5227. return found_match;
  5228. },
  5229. /**@
  5230. * #Crafty.frame
  5231. * @category Core
  5232. * @sign public Number Crafty.frame(void)
  5233. * Returns the current frame number
  5234. */
  5235. frame: function () {
  5236. return frame;
  5237. },
  5238. components: function () {
  5239. return components;
  5240. },
  5241. isComp: function (comp) {
  5242. return comp in components;
  5243. },
  5244. debug: function (str) {
  5245. // access internal variables - handlers or entities
  5246. if (str === 'handlers') {
  5247. return handlers;
  5248. }
  5249. return entities;
  5250. },
  5251. /**@
  5252. * #Crafty.settings
  5253. * @category Core
  5254. * Modify the inner workings of Crafty through the settings.
  5255. */
  5256. settings: (function () {
  5257. var states = {},
  5258. callbacks = {};
  5259. return {
  5260. /**@
  5261. * #Crafty.settings.register
  5262. * @comp Crafty.settings
  5263. * @sign public void Crafty.settings.register(String settingName, Function callback)
  5264. * @param settingName - Name of the setting
  5265. * @param callback - Function to execute when use modifies setting
  5266. *
  5267. * Use this to register custom settings. Callback will be executed when `Crafty.settings.modify` is used.
  5268. *
  5269. * @see Crafty.settings.modify
  5270. */
  5271. register: function (setting, callback) {
  5272. callbacks[setting] = callback;
  5273. },
  5274. /**@
  5275. * #Crafty.settings.modify
  5276. * @comp Crafty.settings
  5277. * @sign public void Crafty.settings.modify(String settingName, * value)
  5278. * @param settingName - Name of the setting
  5279. * @param value - Value to set the setting to
  5280. *
  5281. * Modify settings through this method.
  5282. *
  5283. * @see Crafty.settings.register, Crafty.settings.get
  5284. */
  5285. modify: function (setting, value) {
  5286. if (!callbacks[setting]) return;
  5287. callbacks[setting].call(states[setting], value);
  5288. states[setting] = value;
  5289. },
  5290. /**@
  5291. * #Crafty.settings.get
  5292. * @comp Crafty.settings
  5293. * @sign public * Crafty.settings.get(String settingName)
  5294. * @param settingName - Name of the setting
  5295. * @returns Current value of the setting
  5296. *
  5297. * Returns the current value of the setting.
  5298. *
  5299. * @see Crafty.settings.register, Crafty.settings.get
  5300. */
  5301. get: function (setting) {
  5302. return states[setting];
  5303. }
  5304. };
  5305. })(),
  5306. clone: clone
  5307. });
  5308. /**
  5309. * Return a unique ID
  5310. */
  5311. function UID() {
  5312. var id = GUID++;
  5313. //if GUID is not unique
  5314. if (id in entities) {
  5315. return UID(); //recurse until it is unique
  5316. }
  5317. return id;
  5318. }
  5319. /**@
  5320. * #Crafty.clone
  5321. * @category Core
  5322. * @sign public Object .clone(Object obj)
  5323. * @param obj - an object
  5324. *
  5325. * Deep copy (a.k.a clone) of an object.
  5326. */
  5327. function clone(obj) {
  5328. if (obj === null || typeof (obj) != 'object')
  5329. return obj;
  5330. var temp = obj.constructor(); // changed
  5331. for (var key in obj)
  5332. temp[key] = clone(obj[key]);
  5333. return temp;
  5334. }
  5335. // export Crafty
  5336. if (typeof define === 'function') { // AMD
  5337. define('crafty', [], function () {
  5338. return Crafty;
  5339. });
  5340. }
  5341. module.exports = Crafty;
  5342. window.Crafty = Crafty;
  5343. },{"./version":28}],10:[function(require,module,exports){
  5344. var Crafty = require('./core.js'),
  5345. document = window.document;
  5346. Crafty.extend({
  5347. /**@
  5348. * #Crafty.device
  5349. * @category Misc
  5350. */
  5351. device: {
  5352. _deviceOrientationCallback: false,
  5353. _deviceMotionCallback: false,
  5354. /**
  5355. * The HTML5 DeviceOrientation event returns three pieces of data:
  5356. * * alpha the direction the device is facing according to the compass
  5357. * * beta the angle in degrees the device is tilted front-to-back
  5358. * * gamma the angle in degrees the device is tilted left-to-right.
  5359. * * The angles values increase as you tilt the device to the right or towards you.
  5360. *
  5361. * Since Firefox uses the MozOrientationEvent which returns similar data but
  5362. * using different parameters and a different measurement system, we want to
  5363. * normalize that before we pass it to our _deviceOrientationCallback function.
  5364. *
  5365. * @param eventData HTML5 DeviceOrientation event
  5366. */
  5367. _normalizeDeviceOrientation: function (eventData) {
  5368. var data;
  5369. if (window.DeviceOrientationEvent) {
  5370. data = {
  5371. // gamma is the left-to-right tilt in degrees, where right is positive
  5372. 'tiltLR': eventData.gamma,
  5373. // beta is the front-to-back tilt in degrees, where front is positive
  5374. 'tiltFB': eventData.beta,
  5375. // alpha is the compass direction the device is facing in degrees
  5376. 'dir': eventData.alpha,
  5377. // deviceorientation does not provide this data
  5378. 'motUD': null
  5379. };
  5380. } else if (window.OrientationEvent) {
  5381. data = {
  5382. // x is the left-to-right tilt from -1 to +1, so we need to convert to degrees
  5383. 'tiltLR': eventData.x * 90,
  5384. // y is the front-to-back tilt from -1 to +1, so we need to convert to degrees
  5385. // We also need to invert the value so tilting the device towards us (forward)
  5386. // results in a positive value.
  5387. 'tiltFB': eventData.y * -90,
  5388. // MozOrientation does not provide this data
  5389. 'dir': null,
  5390. // z is the vertical acceleration of the device
  5391. 'motUD': eventData.z
  5392. };
  5393. }
  5394. Crafty.device._deviceOrientationCallback(data);
  5395. },
  5396. /**
  5397. * @param eventData HTML5 DeviceMotion event
  5398. */
  5399. _normalizeDeviceMotion: function (eventData) {
  5400. var acceleration = eventData.accelerationIncludingGravity,
  5401. facingUp = (acceleration.z > 0) ? +1 : -1;
  5402. var data = {
  5403. // Grab the acceleration including gravity from the results
  5404. 'acceleration': acceleration,
  5405. 'rawAcceleration': "[" + Math.round(acceleration.x) + ", " + Math.round(acceleration.y) + ", " + Math.round(acceleration.z) + "]",
  5406. // Z is the acceleration in the Z axis, and if the device is facing up or down
  5407. 'facingUp': facingUp,
  5408. // Convert the value from acceleration to degrees acceleration.x|y is the
  5409. // acceleration according to gravity, we'll assume we're on Earth and divide
  5410. // by 9.81 (earth gravity) to get a percentage value, and then multiply that
  5411. // by 90 to convert to degrees.
  5412. 'tiltLR': Math.round(((acceleration.x) / 9.81) * -90),
  5413. 'tiltFB': Math.round(((acceleration.y + 9.81) / 9.81) * 90 * facingUp)
  5414. };
  5415. Crafty.device._deviceMotionCallback(data);
  5416. },
  5417. /**@
  5418. * #Crafty.device.deviceOrientation
  5419. * @comp Crafty.device
  5420. * @sign public Crafty.device.deviceOrientation(Function callback)
  5421. * @param callback - Callback method executed once as soon as device orientation is change
  5422. *
  5423. * Do something with normalized device orientation data:
  5424. * ~~~
  5425. * {
  5426. * 'tiltLR' : 'gamma the angle in degrees the device is tilted left-to-right.',
  5427. * 'tiltFB' : 'beta the angle in degrees the device is tilted front-to-back',
  5428. * 'dir' : 'alpha the direction the device is facing according to the compass',
  5429. * 'motUD' : 'The angles values increase as you tilt the device to the right or towards you.'
  5430. * }
  5431. * ~~~
  5432. *
  5433. * @example
  5434. * ~~~
  5435. * // Get DeviceOrientation event normalized data.
  5436. * Crafty.device.deviceOrientation(function(data){
  5437. * console.log('data.tiltLR : '+Math.round(data.tiltLR)+', data.tiltFB : '+Math.round(data.tiltFB)+', data.dir : '+Math.round(data.dir)+', data.motUD : '+data.motUD+'');
  5438. * });
  5439. * ~~~
  5440. *
  5441. * See browser support at http://caniuse.com/#search=device orientation.
  5442. */
  5443. deviceOrientation: function (func) {
  5444. this._deviceOrientationCallback = func;
  5445. if (Crafty.support.deviceorientation) {
  5446. if (window.DeviceOrientationEvent) {
  5447. // Listen for the deviceorientation event and handle DeviceOrientationEvent object
  5448. Crafty.addEvent(this, window, 'deviceorientation', this._normalizeDeviceOrientation);
  5449. } else if (window.OrientationEvent) {
  5450. // Listen for the MozOrientation event and handle OrientationData object
  5451. Crafty.addEvent(this, window, 'MozOrientation', this._normalizeDeviceOrientation);
  5452. }
  5453. }
  5454. },
  5455. /**@
  5456. * #Crafty.device.deviceMotion
  5457. * @comp Crafty.device
  5458. * @sign public Crafty.device.deviceMotion(Function callback)
  5459. * @param callback - Callback method executed once as soon as device motion is change
  5460. *
  5461. * Do something with normalized device motion data:
  5462. * ~~~
  5463. * {
  5464. * 'acceleration' : ' Grab the acceleration including gravity from the results',
  5465. * 'rawAcceleration' : 'Display the raw acceleration data',
  5466. * 'facingUp' : 'Z is the acceleration in the Z axis, and if the device is facing up or down',
  5467. * 'tiltLR' : 'Convert the value from acceleration to degrees. acceleration.x is the acceleration according to gravity, we'll assume we're on Earth and divide by 9.81 (earth gravity) to get a percentage value, and then multiply that by 90 to convert to degrees.',
  5468. * 'tiltFB' : 'Convert the value from acceleration to degrees.'
  5469. * }
  5470. * ~~~
  5471. *
  5472. * @example
  5473. * ~~~
  5474. * // Get DeviceMotion event normalized data.
  5475. * Crafty.device.deviceMotion(function(data){
  5476. * console.log('data.moAccel : '+data.rawAcceleration+', data.moCalcTiltLR : '+Math.round(data.tiltLR)+', data.moCalcTiltFB : '+Math.round(data.tiltFB)+'');
  5477. * });
  5478. * ~~~
  5479. *
  5480. * See browser support at http://caniuse.com/#search=motion.
  5481. */
  5482. deviceMotion: function (func) {
  5483. this._deviceMotionCallback = func;
  5484. if (Crafty.support.devicemotion) {
  5485. if (window.DeviceMotionEvent) {
  5486. // Listen for the devicemotion event and handle DeviceMotionEvent object
  5487. Crafty.addEvent(this, window, 'devicemotion', this._normalizeDeviceMotion);
  5488. }
  5489. }
  5490. }
  5491. }
  5492. });
  5493. },{"./core.js":9}],11:[function(require,module,exports){
  5494. var Crafty = require('./core.js'),
  5495. document = window.document;
  5496. Crafty.extend({
  5497. /**@
  5498. * #Crafty.diamondIso
  5499. * @category 2D
  5500. * Place entities in a 45deg diamond isometric fashion. It is similar to isometric but has another grid locations
  5501. */
  5502. diamondIso: {
  5503. _tile: {
  5504. width: 0,
  5505. height: 0,
  5506. r: 0
  5507. },
  5508. _map: {
  5509. width: 0,
  5510. height: 0,
  5511. x: 0,
  5512. y: 0
  5513. },
  5514. _origin: {
  5515. x: 0,
  5516. y: 0
  5517. },
  5518. /**@
  5519. * #Crafty.diamondIso.init
  5520. * @comp Crafty.diamondIso
  5521. * @sign public this Crafty.diamondIso.init(Number tileWidth,Number tileHeight,Number mapWidth,Number mapHeight)
  5522. * @param tileWidth - The size of base tile width in Pixel
  5523. * @param tileHeight - The size of base tile height in Pixel
  5524. * @param mapWidth - The width of whole map in Tiles
  5525. * @param mapHeight - The height of whole map in Tiles
  5526. *
  5527. * Method used to initialize the size of the isometric placement.
  5528. * Recommended to use a size alues in the power of `2` (128, 64 or 32).
  5529. * This makes it easy to calculate positions and implement zooming.
  5530. *
  5531. * @example
  5532. * ~~~
  5533. * var iso = Crafty.diamondIso.init(64,128,20,20);
  5534. * ~~~
  5535. *
  5536. * @see Crafty.diamondIso.place
  5537. */
  5538. init: function (tw, th, mw, mh) {
  5539. this._tile.width = parseInt(tw, 10);
  5540. this._tile.height = parseInt(th, 10) || parseInt(tw, 10) / 2;
  5541. this._tile.r = this._tile.width / this._tile.height;
  5542. this._map.width = parseInt(mw, 10);
  5543. this._map.height = parseInt(mh, 10) || parseInt(mw, 10);
  5544. this._origin.x = this._map.height * this._tile.width / 2;
  5545. return this;
  5546. },
  5547. /**@
  5548. * #Crafty.diamondIso.place
  5549. * @comp Crafty.diamondIso
  5550. * @sign public this Crafty.diamondIso.place(Entity tile,Number x, Number y, Number layer)
  5551. * @param x - The `x` position to place the tile
  5552. * @param y - The `y` position to place the tile
  5553. * @param layer - The `z` position to place the tile (calculated by y position * layer)
  5554. * @param tile - The entity that should be position in the isometric fashion
  5555. *
  5556. * Use this method to place an entity in an isometric grid.
  5557. *
  5558. * @example
  5559. * ~~~
  5560. * var iso = Crafty.diamondIso.init(64,128,20,20);
  5561. * isos.place(Crafty.e('2D, DOM, Color').color('red').attr({w:128, h:128}),1,1,2);
  5562. * ~~~
  5563. *
  5564. * @see Crafty.diamondIso.size
  5565. */
  5566. place: function (obj, x, y, layer) {
  5567. var pos = this.pos2px(x, y);
  5568. if (!layer) layer = 1;
  5569. var marginX = 0,
  5570. marginY = 0;
  5571. if (obj.__margin !== undefined) {
  5572. marginX = obj.__margin[0];
  5573. marginY = obj.__margin[1];
  5574. }
  5575. obj.x = pos.left + (marginX);
  5576. obj.y = (pos.top + marginY) - obj.h;
  5577. obj.z = (pos.top) * layer;
  5578. },
  5579. centerAt: function (x, y) {
  5580. var pos = this.pos2px(x, y);
  5581. Crafty.viewport.x = -pos.left + Crafty.viewport.width / 2 - this._tile.width;
  5582. Crafty.viewport.y = -pos.top + Crafty.viewport.height / 2;
  5583. },
  5584. area: function (offset) {
  5585. if (!offset) offset = 0;
  5586. //calculate the corners
  5587. var vp = Crafty.viewport.rect(),
  5588. x = vp._x,
  5589. y = vp._y,
  5590. w = vp._w,
  5591. h = vp._h;
  5592. var ow = offset * this._tile.width;
  5593. var oh = offset * this._tile.height;
  5594. x -= (this._tile.width / 2 + ow);
  5595. y -= (this._tile.height / 2 + oh);
  5596. w += (this._tile.width / 2 + ow);
  5597. h += (this._tile.height / 2 + oh);
  5598. /* Crafty.viewport.x = -x;
  5599. Crafty.viewport.y = -y;
  5600. Crafty.viewport.width = w;
  5601. Crafty.viewport.height = h; */
  5602. var grid = [];
  5603. for (yl = (y + h); y < yl; y += this._tile.height / 2) {
  5604. for (xl = (x + w); x < xl; x += this._tile.width / 2) {
  5605. var row = this.px2pos(x, y);
  5606. grid.push([~~row.x, ~~row.y]);
  5607. }
  5608. }
  5609. return grid;
  5610. },
  5611. pos2px: function (x, y) {
  5612. return {
  5613. left: ((x - y) * this._tile.width / 2 + this._origin.x),
  5614. top: ((x + y) * this._tile.height / 2)
  5615. };
  5616. },
  5617. px2pos: function (left, top) {
  5618. var x = (left - this._origin.x) / this._tile.r;
  5619. return {
  5620. x: ((top + x) / this._tile.height),
  5621. y: ((top - x) / this._tile.height)
  5622. };
  5623. },
  5624. polygon: function (obj) {
  5625. obj.requires("Collision");
  5626. var marginX = 0,
  5627. marginY = 0;
  5628. if (obj.__margin !== undefined) {
  5629. marginX = obj.__margin[0];
  5630. marginY = obj.__margin[1];
  5631. }
  5632. var points = [
  5633. [marginX - 0, obj.h - marginY - this._tile.height / 2],
  5634. [marginX - this._tile.width / 2, obj.h - marginY - 0],
  5635. [marginX - this._tile.width, obj.h - marginY - this._tile.height / 2],
  5636. [marginX - this._tile.width / 2, obj.h - marginY - this._tile.height]
  5637. ];
  5638. var poly = new Crafty.polygon(points);
  5639. return poly;
  5640. }
  5641. }
  5642. });
  5643. },{"./core.js":9}],12:[function(require,module,exports){
  5644. var Crafty = require('./core.js'),
  5645. document = window.document;
  5646. /**@
  5647. * #Color
  5648. * @category Graphics
  5649. * Draw a solid color for the entity
  5650. */
  5651. Crafty.c("Color", {
  5652. _color: "",
  5653. ready: true,
  5654. init: function () {
  5655. this.bind("Draw", function (e) {
  5656. if (e.type === "DOM") {
  5657. e.style.backgroundColor = this._color;
  5658. e.style.lineHeight = 0;
  5659. } else if (e.type === "canvas") {
  5660. if (this._color) e.ctx.fillStyle = this._color;
  5661. e.ctx.fillRect(e.pos._x, e.pos._y, e.pos._w, e.pos._h);
  5662. }
  5663. });
  5664. },
  5665. /**@
  5666. * #.color
  5667. * @comp Color
  5668. * @trigger Invalidate - when the color changes
  5669. * @sign public this .color(String color)
  5670. * @sign public String .color()
  5671. * @param color - Color of the rectangle
  5672. * Will create a rectangle of solid color for the entity, or return the color if no argument is given.
  5673. *
  5674. * The argument must be a color readable depending on which browser you
  5675. * choose to support.
  5676. *
  5677. * @example
  5678. * ```
  5679. * Crafty.e("2D, DOM, Color")
  5680. * .color("#969696");
  5681. * ```
  5682. */
  5683. color: function (color) {
  5684. if (!color) return this._color;
  5685. this._color = color;
  5686. this.trigger("Invalidate");
  5687. return this;
  5688. }
  5689. });
  5690. /**@
  5691. * #Tint
  5692. * @category Graphics
  5693. * Similar to Color by adding an overlay of semi-transparent color.
  5694. *
  5695. * *Note: Currently only works for Canvas*
  5696. */
  5697. Crafty.c("Tint", {
  5698. _color: null,
  5699. _strength: 1.0,
  5700. init: function () {
  5701. var draw = function d(e) {
  5702. var context = e.ctx || Crafty.canvas.context;
  5703. context.fillStyle = this._color || "rgba(0,0,0, 0)";
  5704. context.fillRect(e.pos._x, e.pos._y, e.pos._w, e.pos._h);
  5705. };
  5706. this.bind("Draw", draw).bind("RemoveComponent", function (id) {
  5707. if (id === "Tint") this.unbind("Draw", draw);
  5708. });
  5709. },
  5710. /**@
  5711. * #.tint
  5712. * @comp Tint
  5713. * @trigger Invalidate - when the tint is applied
  5714. * @sign public this .tint(String color, Number strength)
  5715. * @param color - The color in hexadecimal
  5716. * @param strength - Level of opacity
  5717. *
  5718. * Modify the color and level opacity to give a tint on the entity.
  5719. *
  5720. * @example
  5721. * ~~~
  5722. * Crafty.e("2D, Canvas, Tint")
  5723. * .tint("#969696", 0.3);
  5724. * ~~~
  5725. */
  5726. tint: function (color, strength) {
  5727. this._strength = strength;
  5728. this._color = Crafty.toRGB(color, this._strength);
  5729. this.trigger("Invalidate");
  5730. return this;
  5731. }
  5732. });
  5733. /**@
  5734. * #Image
  5735. * @category Graphics
  5736. * Draw an image with or without repeating (tiling).
  5737. */
  5738. Crafty.c("Image", {
  5739. _repeat: "repeat",
  5740. ready: false,
  5741. init: function () {
  5742. var draw = function (e) {
  5743. if (e.type === "canvas") {
  5744. //skip if no image
  5745. if (!this.ready || !this._pattern) return;
  5746. var context = e.ctx;
  5747. context.fillStyle = this._pattern;
  5748. context.save();
  5749. context.translate(e.pos._x, e.pos._y);
  5750. context.fillRect(0, 0, this._w, this._h);
  5751. context.restore();
  5752. } else if (e.type === "DOM") {
  5753. if (this.__image) {
  5754. e.style.backgroundImage = "url(" + this.__image + ")";
  5755. e.style.backgroundRepeat = this._repeat;
  5756. }
  5757. }
  5758. };
  5759. this.bind("Draw", draw).bind("RemoveComponent", function (id) {
  5760. if (id === "Image") this.unbind("Draw", draw);
  5761. });
  5762. },
  5763. /**@
  5764. * #.image
  5765. * @comp Image
  5766. * @trigger Invalidate - when the image is loaded
  5767. * @sign public this .image(String url[, String repeat])
  5768. * @param url - URL of the image
  5769. * @param repeat - If the image should be repeated to fill the entity.
  5770. *
  5771. * Draw specified image. Repeat follows CSS syntax (`"no-repeat", "repeat", "repeat-x", "repeat-y"`);
  5772. *
  5773. * *Note: Default repeat is `no-repeat` which is different to standard DOM (which is `repeat`)*
  5774. *
  5775. * If the width and height are `0` and repeat is set to `no-repeat` the width and
  5776. * height will automatically assume that of the image. This is an
  5777. * easy way to create an image without needing sprites.
  5778. *
  5779. * @example
  5780. * Will default to no-repeat. Entity width and height will be set to the images width and height
  5781. * ~~~
  5782. * var ent = Crafty.e("2D, DOM, Image").image("myimage.png");
  5783. * ~~~
  5784. * Create a repeating background.
  5785. * ~~~
  5786. * var bg = Crafty.e("2D, DOM, Image")
  5787. * .attr({w: Crafty.viewport.width, h: Crafty.viewport.height})
  5788. * .image("bg.png", "repeat");
  5789. * ~~~
  5790. *
  5791. * @see Crafty.sprite
  5792. */
  5793. image: function (url, repeat) {
  5794. this.__image = url;
  5795. this._repeat = repeat || "no-repeat";
  5796. this.img = Crafty.asset(url);
  5797. if (!this.img) {
  5798. this.img = new Image();
  5799. Crafty.asset(url, this.img);
  5800. this.img.src = url;
  5801. var self = this;
  5802. this.img.onload = function () {
  5803. if (self.has("Canvas")) self._pattern = Crafty.canvas.context.createPattern(self.img, self._repeat);
  5804. self.ready = true;
  5805. if (self._repeat === "no-repeat") {
  5806. self.w = self.img.width;
  5807. self.h = self.img.height;
  5808. }
  5809. self.trigger("Invalidate");
  5810. };
  5811. return this;
  5812. } else {
  5813. this.ready = true;
  5814. if (this.has("Canvas")) this._pattern = Crafty.canvas.context.createPattern(this.img, this._repeat);
  5815. if (this._repeat === "no-repeat") {
  5816. this.w = this.img.width;
  5817. this.h = this.img.height;
  5818. }
  5819. }
  5820. this.trigger("Invalidate");
  5821. return this;
  5822. }
  5823. });
  5824. Crafty.extend({
  5825. /**@
  5826. * #Crafty.toRGB
  5827. * @category Graphics
  5828. * @sign public String Crafty.scene(String hex[, Number alpha])
  5829. * @param hex - a 6 character hex number string representing RGB color
  5830. * @param alpha - The alpha value.
  5831. *
  5832. * Get a rgb string or rgba string (if `alpha` presents).
  5833. *
  5834. * @example
  5835. * ~~~
  5836. * Crafty.toRGB("ffffff"); // rgb(255,255,255)
  5837. * Crafty.toRGB("#ffffff"); // rgb(255,255,255)
  5838. * Crafty.toRGB("ffffff", .5); // rgba(255,255,255,0.5)
  5839. * ~~~
  5840. *
  5841. * @see Text.textColor
  5842. */
  5843. toRGB: function (hex, alpha) {
  5844. hex = (hex.charAt(0) === '#') ? hex.substr(1) : hex;
  5845. var c = [],
  5846. result;
  5847. c[0] = parseInt(hex.substr(0, 2), 16);
  5848. c[1] = parseInt(hex.substr(2, 2), 16);
  5849. c[2] = parseInt(hex.substr(4, 2), 16);
  5850. result = alpha === undefined ? 'rgb(' + c.join(',') + ')' : 'rgba(' + c.join(',') + ',' + alpha + ')';
  5851. return result;
  5852. }
  5853. });
  5854. /**@
  5855. * #Crafty.DrawManager
  5856. * @category Graphics
  5857. * @sign Crafty.DrawManager
  5858. *
  5859. * An internal object manage objects to be drawn and implement
  5860. * the best method of drawing in both DOM and canvas
  5861. */
  5862. Crafty.DrawManager = (function () {
  5863. /** Helper function to sort by globalZ */
  5864. function zsort(a, b) {
  5865. return a._globalZ - b._globalZ;
  5866. }
  5867. /** array of dirty rects on screen */
  5868. var dirty_rects = [],
  5869. changed_objs = [],
  5870. /** array of DOMs needed updating */
  5871. dom = [],
  5872. dirtyViewport = false,
  5873. /** recManager: an object for managing dirty rectangles. */
  5874. rectManager = {
  5875. /** Finds smallest rectangles that overlaps a and b, merges them into target */
  5876. merge: function (a, b, target) {
  5877. if (typeof target === 'undefined')
  5878. target = {};
  5879. // Doing it in this order means we can use either a or b as the target, with no conflict
  5880. target._h = Math.max(a._y + a._h, b._y + b._h);
  5881. target._w = Math.max(a._x + a._w, b._x + b._w);
  5882. target._x = Math.min(a._x, b._x);
  5883. target._y = Math.min(a._y, b._y);
  5884. target._w -= target._x;
  5885. target._h -= target._y;
  5886. return target;
  5887. },
  5888. /** cleans up current dirty state, stores stale state for future passes */
  5889. clean: function () {
  5890. var rect, obj, i;
  5891. for (i = 0, l = changed_objs.length; i < l; i++) {
  5892. obj = changed_objs[i];
  5893. rect = obj._mbr || obj;
  5894. if (typeof obj.staleRect === 'undefined')
  5895. obj.staleRect = {};
  5896. obj.staleRect._x = rect._x;
  5897. obj.staleRect._y = rect._y;
  5898. obj.staleRect._w = rect._w;
  5899. obj.staleRect._h = rect._h;
  5900. obj._changed = false;
  5901. }
  5902. changed_objs.length = 0;
  5903. dirty_rects.length = 0;
  5904. },
  5905. /** Takes the current and previous position of an object, and pushes the dirty regions onto the stack
  5906. * If the entity has only moved/changed a little bit, the regions are squashed together */
  5907. createDirty: function (obj) {
  5908. var rect = obj._mbr || obj;
  5909. if (obj.staleRect) {
  5910. //If overlap, merge stale and current position together, then return
  5911. //Otherwise just push stale rectangle
  5912. if (rectManager.overlap(obj.staleRect, rect)) {
  5913. rectManager.merge(obj.staleRect, rect, obj.staleRect);
  5914. dirty_rects.push(obj.staleRect);
  5915. return;
  5916. } else {
  5917. dirty_rects.push(obj.staleRect);
  5918. }
  5919. }
  5920. // We use the intermediate "currentRect" so it can be modified without messing with obj
  5921. obj.currentRect._x = rect._x;
  5922. obj.currentRect._y = rect._y;
  5923. obj.currentRect._w = rect._w;
  5924. obj.currentRect._h = rect._h;
  5925. dirty_rects.push(obj.currentRect);
  5926. },
  5927. /** Checks whether two rectangles overlap */
  5928. overlap: function (a, b) {
  5929. return (a._x < b._x + b._w && a._y < b._y + b._h && a._x + a._w > b._x && a._y + a._h > b._y);
  5930. }
  5931. };
  5932. Crafty.bind("InvalidateViewport", function () {
  5933. dirtyViewport = true;
  5934. });
  5935. Crafty.bind("PostRender", function () {
  5936. dirtyViewport = false;
  5937. });
  5938. return {
  5939. /**@
  5940. * #Crafty.DrawManager.total2D
  5941. * @comp Crafty.DrawManager
  5942. *
  5943. * Total number of the entities that have the `2D` component.
  5944. */
  5945. total2D: Crafty("2D").length,
  5946. /**@
  5947. * #Crafty.DrawManager.onScreen
  5948. * @comp Crafty.DrawManager
  5949. * @sign public Crafty.DrawManager.onScreen(Object rect)
  5950. * @param rect - A rectangle with field {_x: x_val, _y: y_val, _w: w_val, _h: h_val}
  5951. *
  5952. * Test if a rectangle is completely in viewport
  5953. */
  5954. onScreen: function (rect) {
  5955. return Crafty.viewport._x + rect._x + rect._w > 0 && Crafty.viewport._y + rect._y + rect._h > 0 &&
  5956. Crafty.viewport._x + rect._x < Crafty.viewport.width && Crafty.viewport._y + rect._y < Crafty.viewport.height;
  5957. },
  5958. /**@
  5959. * #Crafty.DrawManager.mergeSet
  5960. * @comp Crafty.DrawManager
  5961. * @sign public Object Crafty.DrawManager.mergeSet(Object set)
  5962. * @param set - an array of rectangular regions
  5963. *
  5964. * Merge any consecutive, overlapping rects into each other.
  5965. * Its an optimization for the redraw regions.
  5966. *
  5967. * The order of set isn't strictly meaningful,
  5968. * but overlapping objects will often cause each other to change,
  5969. * and so might be consecutive.
  5970. */
  5971. mergeSet: function (set) {
  5972. var i = 0;
  5973. while (i < set.length - 1) {
  5974. // If current and next overlap, merge them together into the first, removing the second
  5975. // Then skip the index backwards to compare the previous pair.
  5976. // Otherwise skip forward
  5977. if (rectManager.overlap(set[i], set[i + 1])) {
  5978. rectManager.merge(set[i], set[i + 1], set[i]);
  5979. set.splice(i + 1, 1);
  5980. if (i > 0) i--;
  5981. } else
  5982. i++;
  5983. }
  5984. return set;
  5985. },
  5986. /**@
  5987. * #Crafty.DrawManager.addCanvas
  5988. * @comp Crafty.DrawManager
  5989. * @sign public Crafty.DrawManager.addCanvas(ent)
  5990. * @param ent - The entity to add
  5991. *
  5992. * Add an entity to the list of Canvas objects to draw
  5993. */
  5994. addCanvas: function addCanvas(ent) {
  5995. changed_objs.push(ent);
  5996. },
  5997. /**@
  5998. * #Crafty.DrawManager.addDom
  5999. * @comp Crafty.DrawManager
  6000. * @sign public Crafty.DrawManager.addDom(ent)
  6001. * @param ent - The entity to add
  6002. *
  6003. * Add an entity to the list of DOM object to draw
  6004. */
  6005. addDom: function addDom(ent) {
  6006. dom.push(ent);
  6007. },
  6008. /**@
  6009. * #Crafty.DrawManager.debug
  6010. * @comp Crafty.DrawManager
  6011. * @sign public Crafty.DrawManager.debug()
  6012. */
  6013. debug: function () {
  6014. console.log(changed_objs, dom);
  6015. },
  6016. /**@
  6017. * #Crafty.DrawManager.drawAll
  6018. * @comp Crafty.DrawManager
  6019. * @sign public Crafty.DrawManager.drawAll([Object rect])
  6020. * @param rect - a rectangular region {_x: x_val, _y: y_val, _w: w_val, _h: h_val}
  6021. *
  6022. * - If rect is omitted, redraw within the viewport
  6023. * - If rect is provided, redraw within the rect
  6024. */
  6025. drawAll: function (rect) {
  6026. rect = rect || Crafty.viewport.rect();
  6027. var q = Crafty.map.search(rect),
  6028. i = 0,
  6029. l = q.length,
  6030. ctx = Crafty.canvas.context,
  6031. current;
  6032. ctx.clearRect(rect._x, rect._y, rect._w, rect._h);
  6033. //sort the objects by the global Z
  6034. q.sort(zsort);
  6035. for (; i < l; i++) {
  6036. current = q[i];
  6037. if (current._visible && current.__c.Canvas) {
  6038. current.draw();
  6039. current._changed = false;
  6040. }
  6041. }
  6042. },
  6043. /**@
  6044. * #Crafty.DrawManager.boundingRect
  6045. * @comp Crafty.DrawManager
  6046. * @sign public Crafty.DrawManager.boundingRect(set)
  6047. * @param set - Undocumented
  6048. *
  6049. * - Calculate the common bounding rect of multiple canvas entities.
  6050. * - Returns coords
  6051. */
  6052. boundingRect: function (set) {
  6053. if (!set || !set.length) return;
  6054. var newset = [],
  6055. i = 1,
  6056. l = set.length,
  6057. current, master = set[0],
  6058. tmp;
  6059. master = [master._x, master._y, master._x + master._w, master._y + master._h];
  6060. while (i < l) {
  6061. current = set[i];
  6062. tmp = [current._x, current._y, current._x + current._w, current._y + current._h];
  6063. if (tmp[0] < master[0]) master[0] = tmp[0];
  6064. if (tmp[1] < master[1]) master[1] = tmp[1];
  6065. if (tmp[2] > master[2]) master[2] = tmp[2];
  6066. if (tmp[3] > master[3]) master[3] = tmp[3];
  6067. i++;
  6068. }
  6069. tmp = master;
  6070. master = {
  6071. _x: tmp[0],
  6072. _y: tmp[1],
  6073. _w: tmp[2] - tmp[0],
  6074. _h: tmp[3] - tmp[1]
  6075. };
  6076. return master;
  6077. },
  6078. /**@
  6079. * #Crafty.DrawManager.renderCanvas
  6080. * @comp Crafty.DrawManager
  6081. * @sign public Crafty.DrawManager.renderCanvas()
  6082. *
  6083. * - Triggered by the "RenderScene" event
  6084. * - If the number of rects is over 60% of the total number of objects
  6085. * do the naive method redrawing `Crafty.DrawManager.drawAll`
  6086. * - Otherwise, clear the dirty regions, and redraw entities overlapping the dirty regions.
  6087. *
  6088. * @see Canvas.draw
  6089. */
  6090. renderCanvas: function () {
  6091. var l = changed_objs.length;
  6092. if (!l && !dirtyViewport) {
  6093. return;
  6094. }
  6095. var i = 0,
  6096. rect, q,
  6097. j, len, obj, ent, ctx = Crafty.canvas.context,
  6098. DM = Crafty.DrawManager;
  6099. if (dirtyViewport) {
  6100. var view = Crafty.viewport;
  6101. ctx.setTransform(view._scale, 0, 0, view._scale, view._x*view._scale, view._y*view._scale);
  6102. }
  6103. //if the amount of changed objects is over 60% of the total objects
  6104. //do the naive method redrawing
  6105. // TODO: I'm not sure this condition really makes that much sense!
  6106. if (l / DM.total2D > 0.6 || dirtyViewport) {
  6107. DM.drawAll();
  6108. rectManager.clean();
  6109. return;
  6110. }
  6111. // Calculate dirty_rects from all changed objects, then merge some overlapping regions together
  6112. for (i = 0; i < l; i++) {
  6113. rectManager.createDirty(changed_objs[i]);
  6114. }
  6115. dirty_rects = DM.mergeSet(dirty_rects);
  6116. l = dirty_rects.length;
  6117. var dupes = [],
  6118. objs = [];
  6119. // For each dirty rectangle, find entities near it, and draw the overlapping ones
  6120. for (i = 0; i < l; ++i) { //loop over every dirty rect
  6121. rect = dirty_rects[i];
  6122. dupes.length = 0;
  6123. objs.length = 0;
  6124. if (!rect) continue;
  6125. // Find the smallest rectangle with integer coordinates that encloses rect
  6126. rect._w = rect._x + rect._w;
  6127. rect._h = rect._y + rect._h;
  6128. rect._x = (rect._x > 0) ? (rect._x|0) : (rect._x|0) - 1;
  6129. rect._y = (rect._y > 0) ? (rect._y|0) : (rect._y|0) - 1;
  6130. rect._w -= rect._x;
  6131. rect._h -= rect._y;
  6132. rect._w = (rect._w === (rect._w|0)) ? rect._w : (rect._w|0) + 1;
  6133. rect._h = (rect._h === (rect._h|0)) ? rect._h : (rect._h|0) + 1;
  6134. //search for ents under dirty rect
  6135. q = Crafty.map.search(rect, false);
  6136. //clear the rect from the main canvas
  6137. ctx.clearRect(rect._x, rect._y, rect._w, rect._h);
  6138. //Then clip drawing region to dirty rectangle
  6139. ctx.save();
  6140. ctx.beginPath();
  6141. ctx.rect(rect._x, rect._y, rect._w, rect._h);
  6142. ctx.clip();
  6143. // Loop over found objects removing dupes and adding visible canvas objects to array
  6144. for (j = 0, len = q.length; j < len; ++j) {
  6145. obj = q[j];
  6146. if (dupes[obj[0]] || !obj._visible || !obj.__c.Canvas)
  6147. continue;
  6148. dupes[obj[0]] = true;
  6149. objs.push(obj);
  6150. }
  6151. // Sort objects by z level
  6152. objs.sort(zsort);
  6153. // Then draw each object in that order
  6154. for (j = 0, len = objs.length; j < len; ++j) {
  6155. obj = objs[j];
  6156. var area = obj._mbr || obj;
  6157. if (rectManager.overlap(area, rect))
  6158. obj.draw();
  6159. obj._changed = false;
  6160. }
  6161. // Close rectangle clipping
  6162. ctx.closePath();
  6163. ctx.restore();
  6164. }
  6165. // Draw dirty rectangles for debugging, if that flag is set
  6166. if (Crafty.DrawManager.debugDirty === true) {
  6167. ctx.strokeStyle = 'red';
  6168. for (i = 0, l = dirty_rects.length; i < l; ++i) {
  6169. rect = dirty_rects[i];
  6170. ctx.strokeRect(rect._x, rect._y, rect._w, rect._h);
  6171. }
  6172. }
  6173. //Clean up lists etc
  6174. rectManager.clean();
  6175. },
  6176. /**@
  6177. * #Crafty.DrawManager.renderDOM
  6178. * @comp Crafty.DrawManager
  6179. * @sign public Crafty.DrawManager.renderDOM()
  6180. *
  6181. * When "RenderScene" is triggered, draws all DOM entities that have been flagged
  6182. *
  6183. * @see DOM.draw
  6184. */
  6185. renderDOM: function () {
  6186. // Adjust the viewport
  6187. if (dirtyViewport) {
  6188. var style = Crafty.stage.inner.style,
  6189. view = Crafty.viewport;
  6190. style.transform = style[Crafty.support.prefix + "Transform"] = "scale(" + view._scale + ", " + view._scale + ")";
  6191. style.left = view.x * view._scale + "px";
  6192. style.top = view.y * view._scale + "px";
  6193. style.zIndex = 10;
  6194. }
  6195. //if no objects have been changed, stop
  6196. if (!dom.length) return;
  6197. var i = 0,
  6198. k = dom.length;
  6199. //loop over all DOM elements needing updating
  6200. for (; i < k; ++i) {
  6201. dom[i].draw()._changed = false;
  6202. }
  6203. //reset DOM array
  6204. dom.length = 0;
  6205. }
  6206. };
  6207. })();
  6208. Crafty.extend({
  6209. /**@
  6210. * #Crafty.pixelart
  6211. * @category Graphics
  6212. * @sign public void Crafty.pixelart(Boolean enabled)
  6213. *
  6214. * Sets the image smoothing for drawing images (for both DOM and Canvas).
  6215. * Setting this to true disables smoothing for images, which is the preferred
  6216. * way for drawing pixel art. Defaults to false.
  6217. *
  6218. * This feature is experimental and you should be careful with cross-browser compatibility.
  6219. * The best way to disable image smoothing is to use the Canvas render method and the Sprite component for drawing your entities.
  6220. *
  6221. * This method will have no effect for Canvas image smoothing if the canvas is not initialized yet.
  6222. *
  6223. * Note that Firefox_26 currently has a [bug](https://bugzilla.mozilla.org/show_bug.cgi?id=696630)
  6224. * which prevents disabling image smoothing for Canvas entities that use the Image component. Use the Sprite
  6225. * component instead.
  6226. * Note that Webkit (Chrome & Safari) currently has a bug [link1](http://code.google.com/p/chromium/issues/detail?id=134040)
  6227. * [link2](http://code.google.com/p/chromium/issues/detail?id=106662) that prevents disabling image smoothing
  6228. * for DOM entities.
  6229. *
  6230. * @example
  6231. * This is the preferred way to draw pixel art with the best cross-browser compatibility.
  6232. * ~~~
  6233. * Crafty.canvas.init();
  6234. * Crafty.pixelart(true);
  6235. *
  6236. * Crafty.sprite(imgWidth, imgHeight, "spriteMap.png", {sprite1:[0,0]});
  6237. * Crafty.e("2D, Canvas, sprite1");
  6238. * ~~~
  6239. */
  6240. pixelart: function(enabled) {
  6241. var context = Crafty.canvas.context;
  6242. if (context) {
  6243. context.imageSmoothingEnabled = !enabled;
  6244. context.mozImageSmoothingEnabled = !enabled;
  6245. context.webkitImageSmoothingEnabled = !enabled;
  6246. context.oImageSmoothingEnabled = !enabled;
  6247. context.msImageSmoothingEnabled = !enabled;
  6248. }
  6249. var style = Crafty.stage.inner.style;
  6250. if (enabled) {
  6251. style[Crafty.DOM.camelize("image-rendering")] = "optimizeSpeed"; /* legacy */
  6252. style[Crafty.DOM.camelize("image-rendering")] = "-moz-crisp-edges"; /* Firefox */
  6253. style[Crafty.DOM.camelize("image-rendering")] = "-o-crisp-edges"; /* Opera */
  6254. style[Crafty.DOM.camelize("image-rendering")] = "-webkit-optimize-contrast"; /* Webkit (Chrome & Safari) */
  6255. style[Crafty.DOM.camelize("-ms-interpolation-mode")] = "nearest-neighbor"; /* IE */
  6256. style[Crafty.DOM.camelize("image-rendering")] = "optimize-contrast"; /* CSS3 proposed */
  6257. style[Crafty.DOM.camelize("image-rendering")] = "pixelated"; /* CSS4 proposed */
  6258. style[Crafty.DOM.camelize("image-rendering")] = "crisp-edges"; /* CSS4 proposed */
  6259. } else {
  6260. style[Crafty.DOM.camelize("image-rendering")] = "optimizeQuality"; /* legacy */
  6261. style[Crafty.DOM.camelize("-ms-interpolation-mode")] = "bicubic"; /* IE */
  6262. style[Crafty.DOM.camelize("image-rendering")] = "auto"; /* CSS3 */
  6263. }
  6264. }
  6265. });
  6266. },{"./core.js":9}],13:[function(require,module,exports){
  6267. var Crafty = require('./core.js'),
  6268. document = window.document;
  6269. /**@
  6270. * #Crafty.support
  6271. * @category Misc, Core
  6272. * Determines feature support for what Crafty can do.
  6273. */
  6274. (function testSupport() {
  6275. var support = Crafty.support = {},
  6276. ua = navigator.userAgent.toLowerCase(),
  6277. match = /(webkit)[ \/]([\w.]+)/.exec(ua) ||
  6278. /(o)pera(?:.*version)?[ \/]([\w.]+)/.exec(ua) ||
  6279. /(ms)ie ([\w.]+)/.exec(ua) ||
  6280. /(moz)illa(?:.*? rv:([\w.]+))?/.exec(ua) || [],
  6281. mobile = /iPad|iPod|iPhone|Android|webOS|IEMobile/i.exec(ua);
  6282. /**@
  6283. * #Crafty.mobile
  6284. * @comp Crafty.device
  6285. *
  6286. * Determines if Crafty is running on mobile device.
  6287. *
  6288. * If Crafty.mobile is equal true Crafty does some things under hood:
  6289. * ~~~
  6290. * - set viewport on max device width and height
  6291. * - set Crafty.stage.fullscreen on true
  6292. * - hide window scrollbars
  6293. * ~~~
  6294. *
  6295. * @see Crafty.viewport
  6296. */
  6297. if (mobile) Crafty.mobile = mobile[0];
  6298. /**@
  6299. * #Crafty.support.setter
  6300. * @comp Crafty.support
  6301. * Is `__defineSetter__` supported?
  6302. */
  6303. support.setter = ('__defineSetter__' in this && '__defineGetter__' in this);
  6304. /**@
  6305. * #Crafty.support.defineProperty
  6306. * @comp Crafty.support
  6307. * Is `Object.defineProperty` supported?
  6308. */
  6309. support.defineProperty = (function () {
  6310. if (!('defineProperty' in Object)) return false;
  6311. try {
  6312. Object.defineProperty({}, 'x', {});
  6313. } catch (e) {
  6314. return false;
  6315. }
  6316. return true;
  6317. })();
  6318. /**@
  6319. * #Crafty.support.audio
  6320. * @comp Crafty.support
  6321. * Is HTML5 `Audio` supported?
  6322. */
  6323. support.audio = ('Audio' in window);
  6324. /**@
  6325. * #Crafty.support.prefix
  6326. * @comp Crafty.support
  6327. * Returns the browser specific prefix (`Moz`, `O`, `ms`, `webkit`).
  6328. */
  6329. support.prefix = (match[1] || match[0]);
  6330. //browser specific quirks
  6331. if (support.prefix === "moz") support.prefix = "Moz";
  6332. if (support.prefix === "o") support.prefix = "O";
  6333. if (match[2]) {
  6334. /**@
  6335. * #Crafty.support.versionName
  6336. * @comp Crafty.support
  6337. * Version of the browser
  6338. */
  6339. support.versionName = match[2];
  6340. /**@
  6341. * #Crafty.support.version
  6342. * @comp Crafty.support
  6343. * Version number of the browser as an Integer (first number)
  6344. */
  6345. support.version = +(match[2].split("."))[0];
  6346. }
  6347. /**@
  6348. * #Crafty.support.canvas
  6349. * @comp Crafty.support
  6350. * Is the `canvas` element supported?
  6351. */
  6352. support.canvas = ('getContext' in document.createElement("canvas"));
  6353. /**@
  6354. * #Crafty.support.webgl
  6355. * @comp Crafty.support
  6356. * Is WebGL supported on the canvas element?
  6357. */
  6358. if (support.canvas) {
  6359. var gl;
  6360. try {
  6361. gl = document.createElement("canvas").getContext("experimental-webgl");
  6362. gl.viewportWidth = support.canvas.width;
  6363. gl.viewportHeight = support.canvas.height;
  6364. } catch (e) {}
  6365. support.webgl = !! gl;
  6366. } else {
  6367. support.webgl = false;
  6368. }
  6369. /**@
  6370. * #Crafty.support.css3dtransform
  6371. * @comp Crafty.support
  6372. * Is css3Dtransform supported by browser.
  6373. */
  6374. support.css3dtransform = (typeof document.createElement("div").style.Perspective !== "undefined") || (typeof document.createElement("div").style[support.prefix + "Perspective"] !== "undefined");
  6375. /**@
  6376. * #Crafty.support.deviceorientation
  6377. * @comp Crafty.support
  6378. * Is deviceorientation event supported by browser.
  6379. */
  6380. support.deviceorientation = (typeof window.DeviceOrientationEvent !== "undefined") || (typeof window.OrientationEvent !== "undefined");
  6381. /**@
  6382. * #Crafty.support.devicemotion
  6383. * @comp Crafty.support
  6384. * Is devicemotion event supported by browser.
  6385. */
  6386. support.devicemotion = (typeof window.DeviceMotionEvent !== "undefined");
  6387. })();
  6388. Crafty.extend({
  6389. _events: {},
  6390. /**@
  6391. * #Crafty.addEvent
  6392. * @category Events, Misc
  6393. * @sign public this Crafty.addEvent(Object ctx, HTMLElement obj, String event, Function callback)
  6394. * @param ctx - Context of the callback or the value of `this`
  6395. * @param obj - Element to add the DOM event to
  6396. * @param event - Event name to bind to
  6397. * @param callback - Method to execute when triggered
  6398. *
  6399. * Adds DOM level 3 events to elements. The arguments it accepts are the call
  6400. * context (the value of `this`), the DOM element to attach the event to,
  6401. * the event name (without `on` (`click` rather than `onclick`)) and
  6402. * finally the callback method.
  6403. *
  6404. * If no element is passed, the default element will be `window.document`.
  6405. *
  6406. * Callbacks are passed with event data.
  6407. *
  6408. * @example
  6409. * Will add a stage-wide MouseDown event listener to the player. Will log which button was pressed
  6410. * & the (x,y) coordinates in viewport/world/game space.
  6411. * ~~~
  6412. * var player = Crafty.e("2D");
  6413. * player.onMouseDown = function(e) {
  6414. * console.log(e.mouseButton, e.realX, e.realY);
  6415. * };
  6416. * Crafty.addEvent(player, Crafty.stage.elem, "mousedown", player.onMouseDown);
  6417. * ~~~
  6418. * @see Crafty.removeEvent
  6419. */
  6420. addEvent: function (ctx, obj, type, callback) {
  6421. if (arguments.length === 3) {
  6422. callback = type;
  6423. type = obj;
  6424. obj = window.document;
  6425. }
  6426. //save anonymous function to be able to remove
  6427. var afn = function (e) {
  6428. e = e || window.event;
  6429. if (typeof callback === 'function') {
  6430. callback.call(ctx, e);
  6431. }
  6432. },
  6433. id = ctx[0] || "";
  6434. if (!this._events[id + obj + type + callback]) this._events[id + obj + type + callback] = afn;
  6435. else return;
  6436. if (obj.attachEvent) { //IE
  6437. obj.attachEvent('on' + type, afn);
  6438. } else { //Everyone else
  6439. obj.addEventListener(type, afn, false);
  6440. }
  6441. },
  6442. /**@
  6443. * #Crafty.removeEvent
  6444. * @category Events, Misc
  6445. * @sign public this Crafty.removeEvent(Object ctx, HTMLElement obj, String event, Function callback)
  6446. * @param ctx - Context of the callback or the value of `this`
  6447. * @param obj - Element the event is on
  6448. * @param event - Name of the event
  6449. * @param callback - Method executed when triggered
  6450. *
  6451. * Removes events attached by `Crafty.addEvent()`. All parameters must
  6452. * be the same that were used to attach the event including a reference
  6453. * to the callback method.
  6454. *
  6455. * @see Crafty.addEvent
  6456. */
  6457. removeEvent: function (ctx, obj, type, callback) {
  6458. if (arguments.length === 3) {
  6459. callback = type;
  6460. type = obj;
  6461. obj = window.document;
  6462. }
  6463. //retrieve anonymous function
  6464. var id = ctx[0] || "",
  6465. afn = this._events[id + obj + type + callback];
  6466. if (afn) {
  6467. if (obj.detachEvent) {
  6468. obj.detachEvent('on' + type, afn);
  6469. } else obj.removeEventListener(type, afn, false);
  6470. delete this._events[id + obj + type + callback];
  6471. }
  6472. },
  6473. /**@
  6474. * #Crafty.background
  6475. * @category Graphics, Stage
  6476. * @sign public void Crafty.background(String value)
  6477. * @param style - Modify the background with a color or image
  6478. *
  6479. * This method is a shortcut for adding a background
  6480. * style to the stage element, i.e.
  6481. * `Crafty.stage.elem.style.background = ...`
  6482. *
  6483. * For example, if you want the background to be white,
  6484. * with an image in the center, you might use:
  6485. * ~~~
  6486. * Crafty.background('#FFFFFF url(landscape.png) no-repeat center center');
  6487. * ~~~
  6488. *
  6489. */
  6490. background: function (style) {
  6491. Crafty.stage.elem.style.background = style;
  6492. }
  6493. });
  6494. },{"./core.js":9}],14:[function(require,module,exports){
  6495. var Crafty = require('./core.js'),
  6496. document = window.document;
  6497. /**@
  6498. * #HTML
  6499. * @category Graphics
  6500. * Component allow for insertion of arbitrary HTML into an entity
  6501. */
  6502. Crafty.c("HTML", {
  6503. inner: '',
  6504. init: function () {
  6505. this.requires('2D, DOM');
  6506. },
  6507. /**@
  6508. * #.replace
  6509. * @comp HTML
  6510. * @sign public this .replace(String html)
  6511. * @param html - arbitrary html
  6512. *
  6513. * This method will replace the content of this entity with the supplied html
  6514. *
  6515. * @example
  6516. * Create a link
  6517. * ~~~
  6518. * Crafty.e("HTML")
  6519. * .attr({x:20, y:20, w:100, h:100})
  6520. * .replace("<a href='index.html'>Index</a>");
  6521. * ~~~
  6522. */
  6523. replace: function (new_html) {
  6524. this.inner = new_html;
  6525. this._element.innerHTML = new_html;
  6526. return this;
  6527. },
  6528. /**@
  6529. * #.append
  6530. * @comp HTML
  6531. * @sign public this .append(String html)
  6532. * @param html - arbitrary html
  6533. *
  6534. * This method will add the supplied html in the end of the entity
  6535. *
  6536. * @example
  6537. * Create a link
  6538. * ~~~
  6539. * Crafty.e("HTML")
  6540. * .attr({x:20, y:20, w:100, h:100})
  6541. * .append("<a href='index.html'>Index</a>");
  6542. * ~~~
  6543. */
  6544. append: function (new_html) {
  6545. this.inner += new_html;
  6546. this._element.innerHTML += new_html;
  6547. return this;
  6548. },
  6549. /**@
  6550. * #.prepend
  6551. * @comp HTML
  6552. * @sign public this .prepend(String html)
  6553. * @param html - arbitrary html
  6554. *
  6555. * This method will add the supplied html in the beginning of the entity
  6556. *
  6557. * @example
  6558. * Create a link
  6559. * ~~~
  6560. * Crafty.e("HTML")
  6561. * .attr({x:20, y:20, w:100, h:100})
  6562. * .prepend("<a href='index.html'>Index</a>");
  6563. * ~~~
  6564. */
  6565. prepend: function (new_html) {
  6566. this.inner = new_html + this.inner;
  6567. this._element.innerHTML = new_html + this.inner;
  6568. return this;
  6569. }
  6570. });
  6571. },{"./core.js":9}],15:[function(require,module,exports){
  6572. var Crafty = require('./core.js'),
  6573. document = window.document;
  6574. /**@
  6575. * #Crafty.import
  6576. * @sign public void Crafty.import(String url[, String scene])
  6577. * @param url - Path to the saved file
  6578. * @param scene - Name of the scene to load if saved multiple scenes
  6579. * @sign public void Crafty.import(Object sceneData)
  6580. * @param sceneData - Scene data generated from builder
  6581. * This method will load in scene data generated by the Crafty Builder.
  6582. *
  6583. * @example
  6584. * ~~~
  6585. * Crafty.import({
  6586. * '0': {props: value},
  6587. * 'n': [
  6588. * {c: "comp, list", image: ''}
  6589. * ]
  6590. * });
  6591. * ~~~
  6592. */
  6593. Crafty['import'] = function (obj, scene) {
  6594. //if its a string, load the script file
  6595. if (typeof obj === "string") {
  6596. if (levelData) {
  6597. if (scene) Crafty.import(levelData[scene]);
  6598. else Crafty.import(levelData);
  6599. } else {
  6600. var elem;
  6601. elem = document.createElement("script");
  6602. elem.onload = function () {
  6603. if (scene) Crafty.import(levelData[scene]);
  6604. else Crafty.import(levelData);
  6605. };
  6606. elem.src = obj;
  6607. }
  6608. return;
  6609. }
  6610. var key, i = 0,
  6611. l, current, ent;
  6612. //loop over new entities to create
  6613. if (obj.n && typeof obj.n === "object") {
  6614. for (l = obj.n.length; i < l; ++i) {
  6615. current = obj.n[i];
  6616. //create entity with components
  6617. ent = Crafty.e(current.c);
  6618. delete current.c; //remove the components
  6619. //apply the other properties
  6620. ent.attr(current);
  6621. }
  6622. }
  6623. //loop over modified entities
  6624. for (key in obj) {
  6625. ent = Crafty(key);
  6626. ent.attr(obj[key]);
  6627. }
  6628. };
  6629. },{"./core.js":9}],16:[function(require,module,exports){
  6630. var Crafty = require('./core.js'),
  6631. document = window.document;
  6632. Crafty.extend({
  6633. /**@
  6634. * #Crafty.isometric
  6635. * @category 2D
  6636. * Place entities in a 45deg isometric fashion.
  6637. */
  6638. isometric: {
  6639. _tile: {
  6640. width: 0,
  6641. height: 0
  6642. },
  6643. _elements: {},
  6644. _pos: {
  6645. x: 0,
  6646. y: 0
  6647. },
  6648. _z: 0,
  6649. /**@
  6650. * #Crafty.isometric.size
  6651. * @comp Crafty.isometric
  6652. * @sign public this Crafty.isometric.size(Number tileSize)
  6653. * @param tileSize - The size of the tiles to place.
  6654. *
  6655. * Method used to initialize the size of the isometric placement.
  6656. * Recommended to use a size values in the power of `2` (128, 64 or 32).
  6657. * This makes it easy to calculate positions and implement zooming.
  6658. *
  6659. * @example
  6660. * ~~~
  6661. * var iso = Crafty.isometric.size(128);
  6662. * ~~~
  6663. *
  6664. * @see Crafty.isometric.place
  6665. */
  6666. size: function (width, height) {
  6667. this._tile.width = width;
  6668. this._tile.height = height > 0 ? height : width / 2; //Setup width/2 if height isn't set
  6669. return this;
  6670. },
  6671. /**@
  6672. * #Crafty.isometric.place
  6673. * @comp Crafty.isometric
  6674. * @sign public this Crafty.isometric.place(Number x, Number y, Number z, Entity tile)
  6675. * @param x - The `x` position to place the tile
  6676. * @param y - The `y` position to place the tile
  6677. * @param z - The `z` position or height to place the tile
  6678. * @param tile - The entity that should be position in the isometric fashion
  6679. *
  6680. * Use this method to place an entity in an isometric grid.
  6681. *
  6682. * @example
  6683. * ~~~
  6684. * var iso = Crafty.isometric.size(128);
  6685. * iso.place(2, 1, 0, Crafty.e('2D, DOM, Color').color('red').attr({w:128, h:128}));
  6686. * ~~~
  6687. *
  6688. * @see Crafty.isometric.size
  6689. */
  6690. place: function (x, y, z, obj) {
  6691. var pos = this.pos2px(x, y);
  6692. pos.top -= z * (this._tile.height / 2);
  6693. obj.attr({
  6694. x: pos.left + Crafty.viewport._x,
  6695. y: pos.top + Crafty.viewport._y
  6696. }).z += z;
  6697. return this;
  6698. },
  6699. /**@
  6700. * #Crafty.isometric.pos2px
  6701. * @comp Crafty.isometric
  6702. * @sign public this Crafty.isometric.pos2px(Number x,Number y)
  6703. * @param x
  6704. * @param y
  6705. * @return Object {left Number,top Number}
  6706. *
  6707. * This method calculate the X and Y Coordinates to Pixel Positions
  6708. *
  6709. * @example
  6710. * ~~~
  6711. * var iso = Crafty.isometric.size(128,96);
  6712. * var position = iso.pos2px(100,100); //Object { left=12800, top=4800}
  6713. * ~~~
  6714. */
  6715. pos2px: function (x, y) {
  6716. return {
  6717. left: x * this._tile.width + (y & 1) * (this._tile.width / 2),
  6718. top: y * this._tile.height / 2
  6719. };
  6720. },
  6721. /**@
  6722. * #Crafty.isometric.px2pos
  6723. * @comp Crafty.isometric
  6724. * @sign public this Crafty.isometric.px2pos(Number left,Number top)
  6725. * @param top
  6726. * @param left
  6727. * @return Object {x Number,y Number}
  6728. *
  6729. * This method calculate pixel top,left positions to x,y coordinates
  6730. *
  6731. * @example
  6732. * ~~~
  6733. * var iso = Crafty.isometric.size(128,96);
  6734. * var px = iso.pos2px(12800,4800);
  6735. * console.log(px); //Object { x=100, y=100}
  6736. * ~~~
  6737. */
  6738. px2pos: function (left, top) {
  6739. return {
  6740. x: -Math.ceil(-left / this._tile.width - (top & 1) * 0.5),
  6741. y: top / this._tile.height * 2
  6742. };
  6743. },
  6744. /**@
  6745. * #Crafty.isometric.centerAt
  6746. * @comp Crafty.isometric
  6747. * @sign public this Crafty.isometric.centerAt(Number x,Number y)
  6748. * @param top
  6749. * @param left
  6750. *
  6751. * This method center the Viewport at x/y location or gives the current centerpoint of the viewport
  6752. *
  6753. * @example
  6754. * ~~~
  6755. * var iso = Crafty.isometric.size(128,96).centerAt(10,10); //Viewport is now moved
  6756. * //After moving the viewport by another event you can get the new center point
  6757. * console.log(iso.centerAt());
  6758. * ~~~
  6759. */
  6760. centerAt: function (x, y) {
  6761. if (typeof x == "number" && typeof y == "number") {
  6762. var center = this.pos2px(x, y);
  6763. Crafty.viewport._x = -center.left + Crafty.viewport.width / 2 - this._tile.width / 2;
  6764. Crafty.viewport._y = -center.top + Crafty.viewport.height / 2 - this._tile.height / 2;
  6765. return this;
  6766. } else {
  6767. return {
  6768. top: -Crafty.viewport._y + Crafty.viewport.height / 2 - this._tile.height / 2,
  6769. left: -Crafty.viewport._x + Crafty.viewport.width / 2 - this._tile.width / 2
  6770. };
  6771. }
  6772. },
  6773. /**@
  6774. * #Crafty.isometric.area
  6775. * @comp Crafty.isometric
  6776. * @sign public this Crafty.isometric.area()
  6777. * @return Object {x:{start Number,end Number},y:{start Number,end Number}}
  6778. *
  6779. * This method get the Area surrounding by the centerpoint depends on viewport height and width
  6780. *
  6781. * @example
  6782. * ~~~
  6783. * var iso = Crafty.isometric.size(128,96).centerAt(10,10); //Viewport is now moved
  6784. * var area = iso.area(); //get the area
  6785. * for(var y = area.y.start;y <= area.y.end;y++){
  6786. * for(var x = area.x.start ;x <= area.x.end;x++){
  6787. * iso.place(x,y,0,Crafty.e("2D,DOM,gras")); //Display tiles in the Screen
  6788. * }
  6789. * }
  6790. * ~~~
  6791. */
  6792. area: function () {
  6793. //Get the center Point in the viewport
  6794. var center = this.centerAt();
  6795. var start = this.px2pos(-center.left + Crafty.viewport.width / 2, -center.top + Crafty.viewport.height / 2);
  6796. var end = this.px2pos(-center.left - Crafty.viewport.width / 2, -center.top - Crafty.viewport.height / 2);
  6797. return {
  6798. x: {
  6799. start: start.x,
  6800. end: end.x
  6801. },
  6802. y: {
  6803. start: start.y,
  6804. end: end.y
  6805. }
  6806. };
  6807. }
  6808. }
  6809. });
  6810. },{"./core.js":9}],17:[function(require,module,exports){
  6811. var Crafty = require('./core.js'),
  6812. document = window.document;
  6813. Crafty.extend({
  6814. /**@
  6815. * #Crafty.keys
  6816. * @category Input
  6817. * Object of key names and the corresponding key code.
  6818. *
  6819. * ~~~
  6820. * BACKSPACE: 8,
  6821. * TAB: 9,
  6822. * ENTER: 13,
  6823. * PAUSE: 19,
  6824. * CAPS: 20,
  6825. * ESC: 27,
  6826. * SPACE: 32,
  6827. * PAGE_UP: 33,
  6828. * PAGE_DOWN: 34,
  6829. * END: 35,
  6830. * HOME: 36,
  6831. * LEFT_ARROW: 37,
  6832. * UP_ARROW: 38,
  6833. * RIGHT_ARROW: 39,
  6834. * DOWN_ARROW: 40,
  6835. * INSERT: 45,
  6836. * DELETE: 46,
  6837. * 0: 48,
  6838. * 1: 49,
  6839. * 2: 50,
  6840. * 3: 51,
  6841. * 4: 52,
  6842. * 5: 53,
  6843. * 6: 54,
  6844. * 7: 55,
  6845. * 8: 56,
  6846. * 9: 57,
  6847. * A: 65,
  6848. * B: 66,
  6849. * C: 67,
  6850. * D: 68,
  6851. * E: 69,
  6852. * F: 70,
  6853. * G: 71,
  6854. * H: 72,
  6855. * I: 73,
  6856. * J: 74,
  6857. * K: 75,
  6858. * L: 76,
  6859. * M: 77,
  6860. * N: 78,
  6861. * O: 79,
  6862. * P: 80,
  6863. * Q: 81,
  6864. * R: 82,
  6865. * S: 83,
  6866. * T: 84,
  6867. * U: 85,
  6868. * V: 86,
  6869. * W: 87,
  6870. * X: 88,
  6871. * Y: 89,
  6872. * Z: 90,
  6873. * NUMPAD_0: 96,
  6874. * NUMPAD_1: 97,
  6875. * NUMPAD_2: 98,
  6876. * NUMPAD_3: 99,
  6877. * NUMPAD_4: 100,
  6878. * NUMPAD_5: 101,
  6879. * NUMPAD_6: 102,
  6880. * NUMPAD_7: 103,
  6881. * NUMPAD_8: 104,
  6882. * NUMPAD_9: 105,
  6883. * MULTIPLY: 106,
  6884. * ADD: 107,
  6885. * SUBSTRACT: 109,
  6886. * DECIMAL: 110,
  6887. * DIVIDE: 111,
  6888. * F1: 112,
  6889. * F2: 113,
  6890. * F3: 114,
  6891. * F4: 115,
  6892. * F5: 116,
  6893. * F6: 117,
  6894. * F7: 118,
  6895. * F8: 119,
  6896. * F9: 120,
  6897. * F10: 121,
  6898. * F11: 122,
  6899. * F12: 123,
  6900. * SHIFT: 16,
  6901. * CTRL: 17,
  6902. * ALT: 18,
  6903. * PLUS: 187,
  6904. * COMMA: 188,
  6905. * MINUS: 189,
  6906. * PERIOD: 190,
  6907. * PULT_UP: 29460,
  6908. * PULT_DOWN: 29461,
  6909. * PULT_LEFT: 4,
  6910. * PULT_RIGHT': 5
  6911. * ~~~
  6912. */
  6913. keys: {
  6914. 'BACKSPACE': 8,
  6915. 'TAB': 9,
  6916. 'ENTER': 13,
  6917. 'PAUSE': 19,
  6918. 'CAPS': 20,
  6919. 'ESC': 27,
  6920. 'SPACE': 32,
  6921. 'PAGE_UP': 33,
  6922. 'PAGE_DOWN': 34,
  6923. 'END': 35,
  6924. 'HOME': 36,
  6925. 'LEFT_ARROW': 37,
  6926. 'UP_ARROW': 38,
  6927. 'RIGHT_ARROW': 39,
  6928. 'DOWN_ARROW': 40,
  6929. 'INSERT': 45,
  6930. 'DELETE': 46,
  6931. '0': 48,
  6932. '1': 49,
  6933. '2': 50,
  6934. '3': 51,
  6935. '4': 52,
  6936. '5': 53,
  6937. '6': 54,
  6938. '7': 55,
  6939. '8': 56,
  6940. '9': 57,
  6941. 'A': 65,
  6942. 'B': 66,
  6943. 'C': 67,
  6944. 'D': 68,
  6945. 'E': 69,
  6946. 'F': 70,
  6947. 'G': 71,
  6948. 'H': 72,
  6949. 'I': 73,
  6950. 'J': 74,
  6951. 'K': 75,
  6952. 'L': 76,
  6953. 'M': 77,
  6954. 'N': 78,
  6955. 'O': 79,
  6956. 'P': 80,
  6957. 'Q': 81,
  6958. 'R': 82,
  6959. 'S': 83,
  6960. 'T': 84,
  6961. 'U': 85,
  6962. 'V': 86,
  6963. 'W': 87,
  6964. 'X': 88,
  6965. 'Y': 89,
  6966. 'Z': 90,
  6967. 'NUMPAD_0': 96,
  6968. 'NUMPAD_1': 97,
  6969. 'NUMPAD_2': 98,
  6970. 'NUMPAD_3': 99,
  6971. 'NUMPAD_4': 100,
  6972. 'NUMPAD_5': 101,
  6973. 'NUMPAD_6': 102,
  6974. 'NUMPAD_7': 103,
  6975. 'NUMPAD_8': 104,
  6976. 'NUMPAD_9': 105,
  6977. 'MULTIPLY': 106,
  6978. 'ADD': 107,
  6979. 'SUBSTRACT': 109,
  6980. 'DECIMAL': 110,
  6981. 'DIVIDE': 111,
  6982. 'F1': 112,
  6983. 'F2': 113,
  6984. 'F3': 114,
  6985. 'F4': 115,
  6986. 'F5': 116,
  6987. 'F6': 117,
  6988. 'F7': 118,
  6989. 'F8': 119,
  6990. 'F9': 120,
  6991. 'F10': 121,
  6992. 'F11': 122,
  6993. 'F12': 123,
  6994. 'SHIFT': 16,
  6995. 'CTRL': 17,
  6996. 'ALT': 18,
  6997. 'PLUS': 187,
  6998. 'COMMA': 188,
  6999. 'MINUS': 189,
  7000. 'PERIOD': 190,
  7001. 'PULT_UP': 29460,
  7002. 'PULT_DOWN': 29461,
  7003. 'PULT_LEFT': 4,
  7004. 'PULT_RIGHT': 5
  7005. },
  7006. /**@
  7007. * #Crafty.mouseButtons
  7008. * @category Input
  7009. * An object mapping mouseButton names to the corresponding button ID.
  7010. * In all mouseEvents, we add the `e.mouseButton` property with a value normalized to match e.button of modern webkit browsers:
  7011. *
  7012. * ~~~
  7013. * LEFT: 0,
  7014. * MIDDLE: 1,
  7015. * RIGHT: 2
  7016. * ~~~
  7017. */
  7018. mouseButtons: {
  7019. LEFT: 0,
  7020. MIDDLE: 1,
  7021. RIGHT: 2
  7022. }
  7023. });
  7024. },{"./core.js":9}],18:[function(require,module,exports){
  7025. var Crafty = require('./core.js'),
  7026. document = window.document;
  7027. Crafty.extend({
  7028. /**@
  7029. * #Crafty.assets
  7030. * @category Assets
  7031. * An object containing every asset used in the current Crafty game.
  7032. * The key is the URL and the value is the `Audio` or `Image` object.
  7033. *
  7034. * If loading an asset, check that it is in this object first to avoid loading twice.
  7035. *
  7036. * @example
  7037. * ~~~
  7038. * var isLoaded = !!Crafty.assets["images/sprite.png"];
  7039. * ~~~
  7040. * @see Crafty.loader
  7041. */
  7042. assets: {},
  7043. /**@
  7044. * #Crafty.asset
  7045. * @category Assets
  7046. *
  7047. * @trigger NewAsset - After setting new asset - Object - key and value of new added asset.
  7048. * @sign public void Crafty.asset(String key, Object asset)
  7049. * @param key - asset url.
  7050. * @param asset - Audio` or `Image` object.
  7051. * Add new asset to assets object.
  7052. *
  7053. * @sign public void Crafty.asset(String key)
  7054. * @param key - asset url.
  7055. * Get asset from assets object.
  7056. *
  7057. * @example
  7058. * ~~~
  7059. * Crafty.asset(key, value);
  7060. * var asset = Crafty.asset(key); //object with key and value fields
  7061. * ~~~
  7062. *
  7063. * @see Crafty.assets
  7064. */
  7065. asset: function (key, value) {
  7066. if (arguments.length === 1) {
  7067. return Crafty.assets[key];
  7068. }
  7069. if (!Crafty.assets[key]) {
  7070. Crafty.assets[key] = value;
  7071. this.trigger("NewAsset", {
  7072. key: key,
  7073. value: value
  7074. });
  7075. return value;
  7076. }
  7077. },
  7078. /**@
  7079. * #Crafty.image_whitelist
  7080. * @category Assets
  7081. *
  7082. *
  7083. * A list of file extensions that can be loaded as images by Crafty.load
  7084. *
  7085. * @example
  7086. * ~~~
  7087. * Crafty.image_whitelist.push("tif")
  7088. * Crafty.load(["images/sprite.tif", "sounds/jump.mp3"],
  7089. * function() {
  7090. * //when loaded
  7091. * Crafty.scene("main"); //go to main scene
  7092. * Crafty.audio.play("jump.mp3"); //Play the audio file
  7093. * },
  7094. *
  7095. * function(e) {
  7096. * //progress
  7097. * },
  7098. *
  7099. * function(e) {
  7100. * //uh oh, error loading
  7101. * }
  7102. * );
  7103. * ~~~
  7104. *
  7105. * @see Crafty.asset
  7106. * @see Crafty.load
  7107. */
  7108. image_whitelist: ["jpg", "jpeg", "gif", "png", "svg"],
  7109. /**@
  7110. * #Crafty.loader
  7111. * @category Assets
  7112. * @sign public void Crafty.load(Array assets, Function onLoad[, Function onProgress, Function onError])
  7113. * @param assets - Array of assets to load (accepts sounds and images)
  7114. * @param onLoad - Callback when the assets are loaded
  7115. * @param onProgress - Callback when an asset is loaded. Contains information about assets loaded
  7116. * @param onError - Callback when an asset fails to load
  7117. *
  7118. * Preloader for all assets. Takes an array of URLs and
  7119. * adds them to the `Crafty.assets` object.
  7120. *
  7121. * Files with suffixes in `image_whitelist` (case insensitive) will be loaded.
  7122. *
  7123. * If `Crafty.support.audio` is `true`, files with the following suffixes `mp3`, `wav`, `ogg` and `mp4` (case insensitive) can be loaded.
  7124. *
  7125. * The `onProgress` function will be passed on object with information about
  7126. * the progress including how many assets loaded, total of all the assets to
  7127. * load and a percentage of the progress.
  7128. * ~~~
  7129. * { loaded: j, total: total, percent: (j / total * 100) ,src:src})
  7130. * ~~~
  7131. *
  7132. * `onError` will be passed with the asset that couldn't load.
  7133. *
  7134. * When `onError` is not provided, the onLoad is loaded even some assets are not successfully loaded. Otherwise, onLoad will be called no matter whether there are errors or not.
  7135. *
  7136. * @example
  7137. * ~~~
  7138. * Crafty.load(["images/sprite.png", "sounds/jump.mp3"],
  7139. * function() {
  7140. * //when loaded
  7141. * Crafty.scene("main"); //go to main scene
  7142. * Crafty.audio.play("jump.mp3"); //Play the audio file
  7143. * },
  7144. *
  7145. * function(e) {
  7146. * //progress
  7147. * },
  7148. *
  7149. * function(e) {
  7150. * //uh oh, error loading
  7151. * }
  7152. * );
  7153. * ~~~
  7154. *
  7155. * @see Crafty.assets
  7156. * @see Crafty.image_whitelist
  7157. */
  7158. load: function (data, oncomplete, onprogress, onerror) {
  7159. var i = 0,
  7160. l = data.length,
  7161. current, obj, total = l,
  7162. j = 0,
  7163. ext = "";
  7164. //Progress function
  7165. function pro() {
  7166. var src = this.src;
  7167. //Remove events cause audio trigger this event more than once(depends on browser)
  7168. if (this.removeEventListener) {
  7169. this.removeEventListener('canplaythrough', pro, false);
  7170. }
  7171. ++j;
  7172. //if progress callback, give information of assets loaded, total and percent
  7173. if (onprogress)
  7174. onprogress({
  7175. loaded: j,
  7176. total: total,
  7177. percent: (j / total * 100),
  7178. src: src
  7179. });
  7180. if (j === total && oncomplete) oncomplete();
  7181. }
  7182. //Error function
  7183. function err() {
  7184. var src = this.src;
  7185. if (onerror)
  7186. onerror({
  7187. loaded: j,
  7188. total: total,
  7189. percent: (j / total * 100),
  7190. src: src
  7191. });
  7192. j++;
  7193. if (j === total && oncomplete) oncomplete();
  7194. }
  7195. for (; i < l; ++i) {
  7196. current = data[i];
  7197. ext = current.substr(current.lastIndexOf('.') + 1, 3).toLowerCase();
  7198. obj = Crafty.asset(current) || null;
  7199. if (Crafty.audio.supports(ext)) {
  7200. //Create a new asset if necessary, using the file name as an id
  7201. if (!obj) {
  7202. var name = current.substr(current.lastIndexOf('/') + 1).toLowerCase();
  7203. obj = Crafty.audio.create(name, current).obj;
  7204. }
  7205. //addEventListener is supported on IE9 , Audio as well
  7206. if (obj.addEventListener) {
  7207. obj.addEventListener('canplaythrough', pro, false);
  7208. }
  7209. } else if (Crafty.image_whitelist.indexOf(ext) >= 0) {
  7210. if (!obj) {
  7211. obj = new Image();
  7212. Crafty.asset(current, obj);
  7213. }
  7214. obj.onload = pro;
  7215. if (Crafty.support.prefix === 'webkit') {
  7216. obj.src = ""; // workaround for webkit bug
  7217. }
  7218. obj.src = current; //setup src after onload function Opera/IE Bug
  7219. } else {
  7220. total--;
  7221. continue; //skip if not applicable
  7222. }
  7223. obj.onerror = err;
  7224. }
  7225. // If we aren't trying to handle *any* of the files, that's as complete as it gets!
  7226. if (total === 0)
  7227. oncomplete();
  7228. },
  7229. /**@
  7230. * #Crafty.modules
  7231. * @category Assets
  7232. * @sign public void Crafty.modules([String repoLocation,] Object moduleMap[, Function onLoad])
  7233. * @param modules - Map of name:version pairs for modules to load
  7234. * @param onLoad - Callback when the modules are loaded
  7235. *
  7236. * Browse the selection of community modules on http://craftycomponents.com
  7237. *
  7238. * It is possible to create your own repository.
  7239. *
  7240. *
  7241. * @example
  7242. * ~~~
  7243. * // Loading from default repository
  7244. * Crafty.modules({ moveto: 'DEV' }, function () {
  7245. * //module is ready
  7246. * Crafty.e("MoveTo, 2D, DOM");
  7247. * });
  7248. *
  7249. * // Loading from your own server
  7250. * Crafty.modules({ 'http://mydomain.com/js/mystuff.js': 'DEV' }, function () {
  7251. * //module is ready
  7252. * Crafty.e("MoveTo, 2D, DOM");
  7253. * });
  7254. *
  7255. * // Loading from alternative repository
  7256. * Crafty.modules('http://cdn.crafty-modules.com', { moveto: 'DEV' }, function () {
  7257. * //module is ready
  7258. * Crafty.e("MoveTo, 2D, DOM");
  7259. * });
  7260. *
  7261. * // Loading from the latest component website
  7262. * Crafty.modules(
  7263. * 'http://cdn.craftycomponents.com'
  7264. * , { MoveTo: 'release' }
  7265. * , function () {
  7266. * Crafty.e("2D, DOM, Color, MoveTo")
  7267. * .attr({x: 0, y: 0, w: 50, h: 50})
  7268. * .color("green");
  7269. * });
  7270. * });
  7271. * ~~~
  7272. *
  7273. */
  7274. modules: function (modulesRepository, moduleMap, oncomplete) {
  7275. if (arguments.length === 2 && typeof modulesRepository === "object") {
  7276. oncomplete = moduleMap;
  7277. moduleMap = modulesRepository;
  7278. modulesRepository = 'http://cdn.craftycomponents.com';
  7279. }
  7280. /*!
  7281. * $script.js Async loader & dependency manager
  7282. * https://github.com/ded/script.js
  7283. * (c) Dustin Diaz, Jacob Thornton 2011
  7284. * License: MIT
  7285. */
  7286. var $script = (function () {
  7287. var win = this,
  7288. doc = document,
  7289. head = doc.getElementsByTagName('head')[0],
  7290. validBase = /^https?:\/\//,
  7291. old = win.$script,
  7292. list = {}, ids = {}, delay = {}, scriptpath, scripts = {}, s = 'string',
  7293. f = false,
  7294. push = 'push',
  7295. domContentLoaded = 'DOMContentLoaded',
  7296. readyState = 'readyState',
  7297. addEventListener = 'addEventListener',
  7298. onreadystatechange = 'onreadystatechange';
  7299. function every(ar, fn, i) {
  7300. for (i = 0, j = ar.length; i < j; ++i)
  7301. if (!fn(ar[i])) return f;
  7302. return 1;
  7303. }
  7304. function each(ar, fn) {
  7305. every(ar, function (el) {
  7306. return !fn(el);
  7307. });
  7308. }
  7309. if (!doc[readyState] && doc[addEventListener]) {
  7310. doc[addEventListener](domContentLoaded, function fn() {
  7311. doc.removeEventListener(domContentLoaded, fn, f);
  7312. doc[readyState] = 'complete';
  7313. }, f);
  7314. doc[readyState] = 'loading';
  7315. }
  7316. function $script(paths, idOrDone, optDone) {
  7317. paths = paths[push] ? paths : [paths];
  7318. var idOrDoneIsDone = idOrDone && idOrDone.call,
  7319. done = idOrDoneIsDone ? idOrDone : optDone,
  7320. id = idOrDoneIsDone ? paths.join('') : idOrDone,
  7321. queue = paths.length;
  7322. function loopFn(item) {
  7323. return item.call ? item() : list[item];
  7324. }
  7325. function callback() {
  7326. if (!--queue) {
  7327. list[id] = 1;
  7328. if (done)
  7329. done();
  7330. for (var dset in delay) {
  7331. if (every(dset.split('|'), loopFn) && !each(delay[dset], loopFn))
  7332. delay[dset] = [];
  7333. }
  7334. }
  7335. }
  7336. setTimeout(function () {
  7337. each(paths, function (path) {
  7338. if (scripts[path]) {
  7339. if (id)
  7340. ids[id] = 1;
  7341. return scripts[path] == 2 && callback();
  7342. }
  7343. scripts[path] = 1;
  7344. if (id)
  7345. ids[id] = 1;
  7346. create(!validBase.test(path) && scriptpath ? scriptpath + path + '.js' : path, callback);
  7347. });
  7348. }, 0);
  7349. return $script;
  7350. }
  7351. function create(path, fn) {
  7352. var el = doc.createElement('script'),
  7353. loaded = f;
  7354. el.onload = el.onerror = el[onreadystatechange] = function () {
  7355. if ((el[readyState] && !(/^c|loade/.test(el[readyState]))) || loaded) return;
  7356. el.onload = el[onreadystatechange] = null;
  7357. loaded = 1;
  7358. scripts[path] = 2;
  7359. fn();
  7360. };
  7361. el.async = 1;
  7362. el.src = path;
  7363. head.insertBefore(el, head.firstChild);
  7364. }
  7365. $script.get = create;
  7366. $script.order = function (scripts, id, done) {
  7367. (function callback(s) {
  7368. s = scripts.shift();
  7369. if (!scripts.length) $script(s, id, done);
  7370. else $script(s, callback);
  7371. }());
  7372. };
  7373. $script.path = function (p) {
  7374. scriptpath = p;
  7375. };
  7376. // This function is a tangled mess of conciseness, so suppress warnings here
  7377. /* jshint -W030 */
  7378. $script.ready = function (deps, ready, req) {
  7379. deps = deps[push] ? deps : [deps];
  7380. var missing = [];
  7381. !each(deps, function (dep) {
  7382. list[dep] || missing[push](dep);
  7383. }) && every(deps, function (dep) {
  7384. return list[dep];
  7385. }) ?
  7386. ready() : ! function (key) {
  7387. delay[key] = delay[key] || [];
  7388. delay[key][push](ready);
  7389. req && req(missing);
  7390. }(deps.join('|'));
  7391. return $script;
  7392. };
  7393. /* jshint +W030 */
  7394. $script.noConflict = function () {
  7395. win.$script = old;
  7396. return this;
  7397. };
  7398. return $script;
  7399. })();
  7400. var modules = [];
  7401. var validBase = /^(https?|file):\/\//;
  7402. for (var i in moduleMap) {
  7403. if (validBase.test(i))
  7404. modules.push(i);
  7405. else
  7406. modules.push(modulesRepository + '/' + i.toLowerCase() + '-' + moduleMap[i].toLowerCase() + '.js');
  7407. }
  7408. $script(modules, function () {
  7409. if (oncomplete) oncomplete();
  7410. });
  7411. }
  7412. });
  7413. },{"./core.js":9}],19:[function(require,module,exports){
  7414. var Crafty = require('./core.js'),
  7415. document = window.document;
  7416. /**@
  7417. * #Crafty.math
  7418. * @category 2D
  7419. * Static functions.
  7420. */
  7421. Crafty.math = {
  7422. /**@
  7423. * #Crafty.math.abs
  7424. * @comp Crafty.math
  7425. * @sign public this Crafty.math.abs(Number n)
  7426. * @param n - Some value.
  7427. * @return Absolute value.
  7428. *
  7429. * Returns the absolute value.
  7430. */
  7431. abs: function (x) {
  7432. return x < 0 ? -x : x;
  7433. },
  7434. /**@
  7435. * #Crafty.math.amountOf
  7436. * @comp Crafty.math
  7437. * @sign public Number Crafty.math.amountOf(Number checkValue, Number minValue, Number maxValue)
  7438. * @param checkValue - Value that should checked with minimum and maximum.
  7439. * @param minValue - Minimum value to check.
  7440. * @param maxValue - Maximum value to check.
  7441. * @return Amount of checkValue compared to minValue and maxValue.
  7442. *
  7443. * Returns the amount of how much a checkValue is more like minValue (=0)
  7444. * or more like maxValue (=1)
  7445. */
  7446. amountOf: function (checkValue, minValue, maxValue) {
  7447. if (minValue < maxValue)
  7448. return (checkValue - minValue) / (maxValue - minValue);
  7449. else
  7450. return (checkValue - maxValue) / (minValue - maxValue);
  7451. },
  7452. /**@
  7453. * #Crafty.math.clamp
  7454. * @comp Crafty.math
  7455. * @sign public Number Crafty.math.clamp(Number value, Number min, Number max)
  7456. * @param value - A value.
  7457. * @param max - Maximum that value can be.
  7458. * @param min - Minimum that value can be.
  7459. * @return The value between minimum and maximum.
  7460. *
  7461. * Restricts a value to be within a specified range.
  7462. */
  7463. clamp: function (value, min, max) {
  7464. if (value > max)
  7465. return max;
  7466. else if (value < min)
  7467. return min;
  7468. else
  7469. return value;
  7470. },
  7471. /**@
  7472. * #Crafty.math.degToRad
  7473. * Converts angle from degree to radian.
  7474. * @comp Crafty.math
  7475. * @param angleInDeg - The angle in degree.
  7476. * @return The angle in radian.
  7477. */
  7478. degToRad: function (angleInDeg) {
  7479. return angleInDeg * Math.PI / 180;
  7480. },
  7481. /**@
  7482. * #Crafty.math.distance
  7483. * @comp Crafty.math
  7484. * @sign public Number Crafty.math.distance(Number x1, Number y1, Number x2, Number y2)
  7485. * @param x1 - First x coordinate.
  7486. * @param y1 - First y coordinate.
  7487. * @param x2 - Second x coordinate.
  7488. * @param y2 - Second y coordinate.
  7489. * @return The distance between the two points.
  7490. *
  7491. * Distance between two points.
  7492. */
  7493. distance: function (x1, y1, x2, y2) {
  7494. var squaredDistance = Crafty.math.squaredDistance(x1, y1, x2, y2);
  7495. return Math.sqrt(parseFloat(squaredDistance));
  7496. },
  7497. /**@
  7498. * #Crafty.math.lerp
  7499. * @comp Crafty.math
  7500. * @sign public Number Crafty.math.lerp(Number value1, Number value2, Number amount)
  7501. * @param value1 - One value.
  7502. * @param value2 - Another value.
  7503. * @param amount - Amount of value2 to value1.
  7504. * @return Linear interpolated value.
  7505. *
  7506. * Linear interpolation. Passing amount with a value of 0 will cause value1 to be returned,
  7507. * a value of 1 will cause value2 to be returned.
  7508. */
  7509. lerp: function (value1, value2, amount) {
  7510. return value1 + (value2 - value1) * amount;
  7511. },
  7512. /**@
  7513. * #Crafty.math.negate
  7514. * @comp Crafty.math
  7515. * @sign public Number Crafty.math.negate(Number percent)
  7516. * @param percent - If you pass 1 a -1 will be returned. If you pass 0 a 1 will be returned.
  7517. * @return 1 or -1.
  7518. *
  7519. * Returnes "randomly" -1.
  7520. */
  7521. negate: function (percent) {
  7522. if (Math.random() < percent)
  7523. return -1;
  7524. else
  7525. return 1;
  7526. },
  7527. /**@
  7528. * #Crafty.math.radToDeg
  7529. * @comp Crafty.math
  7530. * @sign public Number Crafty.math.radToDeg(Number angle)
  7531. * @param angleInRad - The angle in radian.
  7532. * @return The angle in degree.
  7533. *
  7534. * Converts angle from radian to degree.
  7535. */
  7536. radToDeg: function (angleInRad) {
  7537. return angleInRad * 180 / Math.PI;
  7538. },
  7539. /**@
  7540. * #Crafty.math.randomElementOfArray
  7541. * @comp Crafty.math
  7542. * @sign public Object Crafty.math.randomElementOfArray(Array array)
  7543. * @param array - A specific array.
  7544. * @return A random element of a specific array.
  7545. *
  7546. * Returns a random element of a specific array.
  7547. */
  7548. randomElementOfArray: function (array) {
  7549. return array[Math.floor(array.length * Math.random())];
  7550. },
  7551. /**@
  7552. * #Crafty.math.randomInt
  7553. * @comp Crafty.math
  7554. * @sign public Number Crafty.math.randomInt(Number start, Number end)
  7555. * @param start - Smallest int value that can be returned.
  7556. * @param end - Biggest int value that can be returned.
  7557. * @return A random int.
  7558. *
  7559. * Returns a random int in within a specific range.
  7560. */
  7561. randomInt: function (start, end) {
  7562. return start + Math.floor((1 + end - start) * Math.random());
  7563. },
  7564. /**@
  7565. * #Crafty.math.randomNumber
  7566. * @comp Crafty.math
  7567. * @sign public Number Crafty.math.randomInt(Number start, Number end)
  7568. * @param start - Smallest number value that can be returned.
  7569. * @param end - Biggest number value that can be returned.
  7570. * @return A random number.
  7571. *
  7572. * Returns a random number in within a specific range.
  7573. */
  7574. randomNumber: function (start, end) {
  7575. return start + (end - start) * Math.random();
  7576. },
  7577. /**@
  7578. * #Crafty.math.squaredDistance
  7579. * @comp Crafty.math
  7580. * @sign public Number Crafty.math.squaredDistance(Number x1, Number y1, Number x2, Number y2)
  7581. * @param x1 - First x coordinate.
  7582. * @param y1 - First y coordinate.
  7583. * @param x2 - Second x coordinate.
  7584. * @param y2 - Second y coordinate.
  7585. * @return The squared distance between the two points.
  7586. *
  7587. * Squared distance between two points.
  7588. */
  7589. squaredDistance: function (x1, y1, x2, y2) {
  7590. return (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2);
  7591. },
  7592. /**@
  7593. * #Crafty.math.withinRange
  7594. * @comp Crafty.math
  7595. * @sign public Boolean Crafty.math.withinRange(Number value, Number min, Number max)
  7596. * @param value - The specific value.
  7597. * @param min - Minimum value.
  7598. * @param max - Maximum value.
  7599. * @return Returns true if value is within a specific range.
  7600. *
  7601. * Check if a value is within a specific range.
  7602. */
  7603. withinRange: function (value, min, max) {
  7604. return (value >= min && value <= max);
  7605. }
  7606. };
  7607. Crafty.math.Vector2D = (function () {
  7608. /**@
  7609. * #Crafty.math.Vector2D
  7610. * @category 2D
  7611. * @class This is a general purpose 2D vector class
  7612. *
  7613. * Vector2D uses the following form:
  7614. * <x, y>
  7615. *
  7616. * @public
  7617. * @sign public {Vector2D} Vector2D();
  7618. * @sign public {Vector2D} Vector2D(Vector2D);
  7619. * @sign public {Vector2D} Vector2D(Number, Number);
  7620. * @param {Vector2D|Number=0} x
  7621. * @param {Number=0} y
  7622. */
  7623. function Vector2D(x, y) {
  7624. if (x instanceof Vector2D) {
  7625. this.x = x.x;
  7626. this.y = x.y;
  7627. } else if (arguments.length === 2) {
  7628. this.x = x;
  7629. this.y = y;
  7630. } else if (arguments.length > 0)
  7631. throw "Unexpected number of arguments for Vector2D()";
  7632. } // class Vector2D
  7633. Vector2D.prototype.x = 0;
  7634. Vector2D.prototype.y = 0;
  7635. /**@
  7636. * #.add
  7637. * @comp Crafty.math.Vector2D
  7638. *
  7639. * Adds the passed vector to this vector
  7640. *
  7641. * @public
  7642. * @sign public {Vector2D} add(Vector2D);
  7643. * @param {vector2D} vecRH
  7644. * @returns {Vector2D} this after adding
  7645. */
  7646. Vector2D.prototype.add = function (vecRH) {
  7647. this.x += vecRH.x;
  7648. this.y += vecRH.y;
  7649. return this;
  7650. }; // add
  7651. /**@
  7652. * #.angleBetween
  7653. * @comp Crafty.math.Vector2D
  7654. *
  7655. * Calculates the angle between the passed vector and this vector, using <0,0> as the point of reference.
  7656. * Angles returned have the range (-π, π].
  7657. *
  7658. * @public
  7659. * @sign public {Number} angleBetween(Vector2D);
  7660. * @param {Vector2D} vecRH
  7661. * @returns {Number} the angle between the two vectors in radians
  7662. */
  7663. Vector2D.prototype.angleBetween = function (vecRH) {
  7664. return Math.atan2(this.x * vecRH.y - this.y * vecRH.x, this.x * vecRH.x + this.y * vecRH.y);
  7665. }; // angleBetween
  7666. /**@
  7667. * #.angleTo
  7668. * @comp Crafty.math.Vector2D
  7669. *
  7670. * Calculates the angle to the passed vector from this vector, using this vector as the point of reference.
  7671. *
  7672. * @public
  7673. * @sign public {Number} angleTo(Vector2D);
  7674. * @param {Vector2D} vecRH
  7675. * @returns {Number} the angle to the passed vector in radians
  7676. */
  7677. Vector2D.prototype.angleTo = function (vecRH) {
  7678. return Math.atan2(vecRH.y - this.y, vecRH.x - this.x);
  7679. };
  7680. /**@
  7681. * #.clone
  7682. * @comp Crafty.math.Vector2D
  7683. *
  7684. * Creates and exact, numeric copy of this vector
  7685. *
  7686. * @public
  7687. * @sign public {Vector2D} clone();
  7688. * @returns {Vector2D} the new vector
  7689. */
  7690. Vector2D.prototype.clone = function () {
  7691. return new Vector2D(this);
  7692. }; // clone
  7693. /**@
  7694. * #.distance
  7695. * @comp Crafty.math.Vector2D
  7696. *
  7697. * Calculates the distance from this vector to the passed vector.
  7698. *
  7699. * @public
  7700. * @sign public {Number} distance(Vector2D);
  7701. * @param {Vector2D} vecRH
  7702. * @returns {Number} the distance between the two vectors
  7703. */
  7704. Vector2D.prototype.distance = function (vecRH) {
  7705. return Math.sqrt((vecRH.x - this.x) * (vecRH.x - this.x) + (vecRH.y - this.y) * (vecRH.y - this.y));
  7706. }; // distance
  7707. /**@
  7708. * #.distanceSq
  7709. * @comp Crafty.math.Vector2D
  7710. *
  7711. * Calculates the squared distance from this vector to the passed vector.
  7712. * This function avoids calculating the square root, thus being slightly faster than .distance( ).
  7713. *
  7714. * @public
  7715. * @sign public {Number} distanceSq(Vector2D);
  7716. * @param {Vector2D} vecRH
  7717. * @returns {Number} the squared distance between the two vectors
  7718. * @see .distance
  7719. */
  7720. Vector2D.prototype.distanceSq = function (vecRH) {
  7721. return (vecRH.x - this.x) * (vecRH.x - this.x) + (vecRH.y - this.y) * (vecRH.y - this.y);
  7722. }; // distanceSq
  7723. /**@
  7724. * #.divide
  7725. * @comp Crafty.math.Vector2D
  7726. *
  7727. * Divides this vector by the passed vector.
  7728. *
  7729. * @public
  7730. * @sign public {Vector2D} divide(Vector2D);
  7731. * @param {Vector2D} vecRH
  7732. * @returns {Vector2D} this vector after dividing
  7733. */
  7734. Vector2D.prototype.divide = function (vecRH) {
  7735. this.x /= vecRH.x;
  7736. this.y /= vecRH.y;
  7737. return this;
  7738. }; // divide
  7739. /**@
  7740. * #.dotProduct
  7741. * @comp Crafty.math.Vector2D
  7742. *
  7743. * Calculates the dot product of this and the passed vectors
  7744. *
  7745. * @public
  7746. * @sign public {Number} dotProduct(Vector2D);
  7747. * @param {Vector2D} vecRH
  7748. * @returns {Number} the resultant dot product
  7749. */
  7750. Vector2D.prototype.dotProduct = function (vecRH) {
  7751. return this.x * vecRH.x + this.y * vecRH.y;
  7752. }; // dotProduct
  7753. /**@
  7754. * #.equals
  7755. * @comp Crafty.math.Vector2D
  7756. *
  7757. * Determines if this vector is numerically equivalent to the passed vector.
  7758. *
  7759. * @public
  7760. * @sign public {Boolean} equals(Vector2D);
  7761. * @param {Vector2D} vecRH
  7762. * @returns {Boolean} true if the vectors are equivalent
  7763. */
  7764. Vector2D.prototype.equals = function (vecRH) {
  7765. return vecRH instanceof Vector2D &&
  7766. this.x == vecRH.x && this.y == vecRH.y;
  7767. }; // equals
  7768. /**@
  7769. * #.getNormal
  7770. * @comp Crafty.math.Vector2D
  7771. *
  7772. * Calculates a new right-handed normal vector for the line created by this and the passed vectors.
  7773. *
  7774. * @public
  7775. * @sign public {Vector2D} getNormal([Vector2D]);
  7776. * @param {Vector2D=<0,0>} [vecRH]
  7777. * @returns {Vector2D} the new normal vector
  7778. */
  7779. Vector2D.prototype.getNormal = function (vecRH) {
  7780. if (vecRH === undefined)
  7781. return new Vector2D(-this.y, this.x); // assume vecRH is <0, 0>
  7782. return new Vector2D(vecRH.y - this.y, this.x - vecRH.x).normalize();
  7783. }; // getNormal
  7784. /**@
  7785. * #.isZero
  7786. * @comp Crafty.math.Vector2D
  7787. *
  7788. * Determines if this vector is equal to <0,0>
  7789. *
  7790. * @public
  7791. * @sign public {Boolean} isZero();
  7792. * @returns {Boolean} true if this vector is equal to <0,0>
  7793. */
  7794. Vector2D.prototype.isZero = function () {
  7795. return this.x === 0 && this.y === 0;
  7796. }; // isZero
  7797. /**@
  7798. * #.magnitude
  7799. * @comp Crafty.math.Vector2D
  7800. *
  7801. * Calculates the magnitude of this vector.
  7802. * Note: Function objects in JavaScript already have a 'length' member, hence the use of magnitude instead.
  7803. *
  7804. * @public
  7805. * @sign public {Number} magnitude();
  7806. * @returns {Number} the magnitude of this vector
  7807. */
  7808. Vector2D.prototype.magnitude = function () {
  7809. return Math.sqrt(this.x * this.x + this.y * this.y);
  7810. }; // magnitude
  7811. /**@
  7812. * #.magnitudeSq
  7813. * @comp Crafty.math.Vector2D
  7814. *
  7815. * Calculates the square of the magnitude of this vector.
  7816. * This function avoids calculating the square root, thus being slightly faster than .magnitude( ).
  7817. *
  7818. * @public
  7819. * @sign public {Number} magnitudeSq();
  7820. * @returns {Number} the square of the magnitude of this vector
  7821. * @see .magnitude
  7822. */
  7823. Vector2D.prototype.magnitudeSq = function () {
  7824. return this.x * this.x + this.y * this.y;
  7825. }; // magnitudeSq
  7826. /**@
  7827. * #.multiply
  7828. * @comp Crafty.math.Vector2D
  7829. *
  7830. * Multiplies this vector by the passed vector
  7831. *
  7832. * @public
  7833. * @sign public {Vector2D} multiply(Vector2D);
  7834. * @param {Vector2D} vecRH
  7835. * @returns {Vector2D} this vector after multiplying
  7836. */
  7837. Vector2D.prototype.multiply = function (vecRH) {
  7838. this.x *= vecRH.x;
  7839. this.y *= vecRH.y;
  7840. return this;
  7841. }; // multiply
  7842. /**@
  7843. * #.negate
  7844. * @comp Crafty.math.Vector2D
  7845. *
  7846. * Negates this vector (ie. <-x,-y>)
  7847. *
  7848. * @public
  7849. * @sign public {Vector2D} negate();
  7850. * @returns {Vector2D} this vector after negation
  7851. */
  7852. Vector2D.prototype.negate = function () {
  7853. this.x = -this.x;
  7854. this.y = -this.y;
  7855. return this;
  7856. }; // negate
  7857. /**@
  7858. * #.normalize
  7859. * @comp Crafty.math.Vector2D
  7860. *
  7861. * Normalizes this vector (scales the vector so that its new magnitude is 1)
  7862. * For vectors where magnitude is 0, <1,0> is returned.
  7863. *
  7864. * @public
  7865. * @sign public {Vector2D} normalize();
  7866. * @returns {Vector2D} this vector after normalization
  7867. */
  7868. Vector2D.prototype.normalize = function () {
  7869. var lng = Math.sqrt(this.x * this.x + this.y * this.y);
  7870. if (lng === 0) {
  7871. // default due East
  7872. this.x = 1;
  7873. this.y = 0;
  7874. } else {
  7875. this.x /= lng;
  7876. this.y /= lng;
  7877. } // else
  7878. return this;
  7879. }; // normalize
  7880. /**@
  7881. * #.scale
  7882. * @comp Crafty.math.Vector2D
  7883. *
  7884. * Scales this vector by the passed amount(s)
  7885. * If scalarY is omitted, scalarX is used for both axes
  7886. *
  7887. * @public
  7888. * @sign public {Vector2D} scale(Number[, Number]);
  7889. * @param {Number} scalarX
  7890. * @param {Number} [scalarY]
  7891. * @returns {Vector2D} this after scaling
  7892. */
  7893. Vector2D.prototype.scale = function (scalarX, scalarY) {
  7894. if (scalarY === undefined)
  7895. scalarY = scalarX;
  7896. this.x *= scalarX;
  7897. this.y *= scalarY;
  7898. return this;
  7899. }; // scale
  7900. /**@
  7901. * #.scaleToMagnitude
  7902. * @comp Crafty.math.Vector2D
  7903. *
  7904. * Scales this vector such that its new magnitude is equal to the passed value.
  7905. *
  7906. * @public
  7907. * @sign public {Vector2D} scaleToMagnitude(Number);
  7908. * @param {Number} mag
  7909. * @returns {Vector2D} this vector after scaling
  7910. */
  7911. Vector2D.prototype.scaleToMagnitude = function (mag) {
  7912. var k = mag / this.magnitude();
  7913. this.x *= k;
  7914. this.y *= k;
  7915. return this;
  7916. }; // scaleToMagnitude
  7917. /**@
  7918. * #.setValues
  7919. * @comp Crafty.math.Vector2D
  7920. *
  7921. * Sets the values of this vector using a passed vector or pair of numbers.
  7922. *
  7923. * @public
  7924. * @sign public {Vector2D} setValues(Vector2D);
  7925. * @sign public {Vector2D} setValues(Number, Number);
  7926. * @param {Number|Vector2D} x
  7927. * @param {Number} y
  7928. * @returns {Vector2D} this vector after setting of values
  7929. */
  7930. Vector2D.prototype.setValues = function (x, y) {
  7931. if (x instanceof Vector2D) {
  7932. this.x = x.x;
  7933. this.y = x.y;
  7934. } else {
  7935. this.x = x;
  7936. this.y = y;
  7937. } // else
  7938. return this;
  7939. }; // setValues
  7940. /**@
  7941. * #.subtract
  7942. * @comp Crafty.math.Vector2D
  7943. *
  7944. * Subtracts the passed vector from this vector.
  7945. *
  7946. * @public
  7947. * @sign public {Vector2D} subtract(Vector2D);
  7948. * @param {Vector2D} vecRH
  7949. * @returns {vector2D} this vector after subtracting
  7950. */
  7951. Vector2D.prototype.subtract = function (vecRH) {
  7952. this.x -= vecRH.x;
  7953. this.y -= vecRH.y;
  7954. return this;
  7955. }; // subtract
  7956. /**@
  7957. * #.toString
  7958. * @comp Crafty.math.Vector2D
  7959. *
  7960. * Returns a string representation of this vector.
  7961. *
  7962. * @public
  7963. * @sign public {String} toString();
  7964. * @returns {String}
  7965. */
  7966. Vector2D.prototype.toString = function () {
  7967. return "Vector2D(" + this.x + ", " + this.y + ")";
  7968. }; // toString
  7969. /**@
  7970. * #.translate
  7971. * @comp Crafty.math.Vector2D
  7972. *
  7973. * Translates (moves) this vector by the passed amounts.
  7974. * If dy is omitted, dx is used for both axes.
  7975. *
  7976. * @public
  7977. * @sign public {Vector2D} translate(Number[, Number]);
  7978. * @param {Number} dx
  7979. * @param {Number} [dy]
  7980. * @returns {Vector2D} this vector after translating
  7981. */
  7982. Vector2D.prototype.translate = function (dx, dy) {
  7983. if (dy === undefined)
  7984. dy = dx;
  7985. this.x += dx;
  7986. this.y += dy;
  7987. return this;
  7988. }; // translate
  7989. /**@
  7990. * #.tripleProduct
  7991. * @comp Crafty.math.Vector2D
  7992. *
  7993. * Calculates the triple product of three vectors.
  7994. * triple vector product = b(aoc) - a(boc)
  7995. *
  7996. * @public
  7997. * @static
  7998. * @sign public {Vector2D} tripleProduct(Vector2D, Vector2D, Vector2D);
  7999. * @param {Vector2D} a
  8000. * @param {Vector2D} b
  8001. * @param {Vector2D} c
  8002. * @return {Vector2D} the triple product as a new vector
  8003. */
  8004. Vector2D.tripleProduct = function (a, b, c) {
  8005. var ac = a.dotProduct(c);
  8006. var bc = b.dotProduct(c);
  8007. return new Crafty.math.Vector2D(b.x * ac - a.x * bc, b.y * ac - a.y * bc);
  8008. };
  8009. return Vector2D;
  8010. })();
  8011. Crafty.math.Matrix2D = (function () {
  8012. /**@
  8013. * #Crafty.math.Matrix2D
  8014. * @category 2D
  8015. *
  8016. * @class This is a 2D Matrix2D class. It is 3x3 to allow for affine transformations in 2D space.
  8017. * The third row is always assumed to be [0, 0, 1].
  8018. *
  8019. * Matrix2D uses the following form, as per the whatwg.org specifications for canvas.transform():
  8020. * [a, c, e]
  8021. * [b, d, f]
  8022. * [0, 0, 1]
  8023. *
  8024. * @public
  8025. * @sign public {Matrix2D} new Matrix2D();
  8026. * @sign public {Matrix2D} new Matrix2D(Matrix2D);
  8027. * @sign public {Matrix2D} new Matrix2D(Number, Number, Number, Number, Number, Number);
  8028. * @param {Matrix2D|Number=1} a
  8029. * @param {Number=0} b
  8030. * @param {Number=0} c
  8031. * @param {Number=1} d
  8032. * @param {Number=0} e
  8033. * @param {Number=0} f
  8034. */
  8035. Matrix2D = function (a, b, c, d, e, f) {
  8036. if (a instanceof Matrix2D) {
  8037. this.a = a.a;
  8038. this.b = a.b;
  8039. this.c = a.c;
  8040. this.d = a.d;
  8041. this.e = a.e;
  8042. this.f = a.f;
  8043. } else if (arguments.length === 6) {
  8044. this.a = a;
  8045. this.b = b;
  8046. this.c = c;
  8047. this.d = d;
  8048. this.e = e;
  8049. this.f = f;
  8050. } else if (arguments.length > 0)
  8051. throw "Unexpected number of arguments for Matrix2D()";
  8052. }; // class Matrix2D
  8053. Matrix2D.prototype.a = 1;
  8054. Matrix2D.prototype.b = 0;
  8055. Matrix2D.prototype.c = 0;
  8056. Matrix2D.prototype.d = 1;
  8057. Matrix2D.prototype.e = 0;
  8058. Matrix2D.prototype.f = 0;
  8059. /**@
  8060. * #.apply
  8061. * @comp Crafty.math.Matrix2D
  8062. *
  8063. * Applies the matrix transformations to the passed object
  8064. *
  8065. * @public
  8066. * @sign public {Vector2D} apply(Vector2D);
  8067. * @param {Vector2D} vecRH - vector to be transformed
  8068. * @returns {Vector2D} the passed vector object after transforming
  8069. */
  8070. Matrix2D.prototype.apply = function (vecRH) {
  8071. // I'm not sure of the best way for this function to be implemented. Ideally
  8072. // support for other objects (rectangles, polygons, etc) should be easily
  8073. // addable in the future. Maybe a function (apply) is not the best way to do
  8074. // this...?
  8075. var tmpX = vecRH.x;
  8076. vecRH.x = tmpX * this.a + vecRH.y * this.c + this.e;
  8077. vecRH.y = tmpX * this.b + vecRH.y * this.d + this.f;
  8078. // no need to homogenize since the third row is always [0, 0, 1]
  8079. return vecRH;
  8080. }; // apply
  8081. /**@
  8082. * #.clone
  8083. * @comp Crafty.math.Matrix2D
  8084. *
  8085. * Creates an exact, numeric copy of the current matrix
  8086. *
  8087. * @public
  8088. * @sign public {Matrix2D} clone();
  8089. * @returns {Matrix2D}
  8090. */
  8091. Matrix2D.prototype.clone = function () {
  8092. return new Matrix2D(this);
  8093. }; // clone
  8094. /**@
  8095. * #.combine
  8096. * @comp Crafty.math.Matrix2D
  8097. *
  8098. * Multiplies this matrix with another, overriding the values of this matrix.
  8099. * The passed matrix is assumed to be on the right-hand side.
  8100. *
  8101. * @public
  8102. * @sign public {Matrix2D} combine(Matrix2D);
  8103. * @param {Matrix2D} mtrxRH
  8104. * @returns {Matrix2D} this matrix after combination
  8105. */
  8106. Matrix2D.prototype.combine = function (mtrxRH) {
  8107. var tmp = this.a;
  8108. this.a = tmp * mtrxRH.a + this.b * mtrxRH.c;
  8109. this.b = tmp * mtrxRH.b + this.b * mtrxRH.d;
  8110. tmp = this.c;
  8111. this.c = tmp * mtrxRH.a + this.d * mtrxRH.c;
  8112. this.d = tmp * mtrxRH.b + this.d * mtrxRH.d;
  8113. tmp = this.e;
  8114. this.e = tmp * mtrxRH.a + this.f * mtrxRH.c + mtrxRH.e;
  8115. this.f = tmp * mtrxRH.b + this.f * mtrxRH.d + mtrxRH.f;
  8116. return this;
  8117. }; // combine
  8118. /**@
  8119. * #.equals
  8120. * @comp Crafty.math.Matrix2D
  8121. *
  8122. * Checks for the numeric equality of this matrix versus another.
  8123. *
  8124. * @public
  8125. * @sign public {Boolean} equals(Matrix2D);
  8126. * @param {Matrix2D} mtrxRH
  8127. * @returns {Boolean} true if the two matrices are numerically equal
  8128. */
  8129. Matrix2D.prototype.equals = function (mtrxRH) {
  8130. return mtrxRH instanceof Matrix2D &&
  8131. this.a == mtrxRH.a && this.b == mtrxRH.b && this.c == mtrxRH.c &&
  8132. this.d == mtrxRH.d && this.e == mtrxRH.e && this.f == mtrxRH.f;
  8133. }; // equals
  8134. /**@
  8135. * #.determinant
  8136. * @comp Crafty.math.Matrix2D
  8137. *
  8138. * Calculates the determinant of this matrix
  8139. *
  8140. * @public
  8141. * @sign public {Number} determinant();
  8142. * @returns {Number} det(this matrix)
  8143. */
  8144. Matrix2D.prototype.determinant = function () {
  8145. return this.a * this.d - this.b * this.c;
  8146. }; // determinant
  8147. /**@
  8148. * #.invert
  8149. * @comp Crafty.math.Matrix2D
  8150. *
  8151. * Inverts this matrix if possible
  8152. *
  8153. * @public
  8154. * @sign public {Matrix2D} invert();
  8155. * @returns {Matrix2D} this inverted matrix or the original matrix on failure
  8156. * @see .isInvertible
  8157. */
  8158. Matrix2D.prototype.invert = function () {
  8159. var det = this.determinant();
  8160. // matrix is invertible if its determinant is non-zero
  8161. if (det !== 0) {
  8162. var old = {
  8163. a: this.a,
  8164. b: this.b,
  8165. c: this.c,
  8166. d: this.d,
  8167. e: this.e,
  8168. f: this.f
  8169. };
  8170. this.a = old.d / det;
  8171. this.b = -old.b / det;
  8172. this.c = -old.c / det;
  8173. this.d = old.a / det;
  8174. this.e = (old.c * old.f - old.e * old.d) / det;
  8175. this.f = (old.e * old.b - old.a * old.f) / det;
  8176. } // if
  8177. return this;
  8178. }; // invert
  8179. /**@
  8180. * #.isIdentity
  8181. * @comp Crafty.math.Matrix2D
  8182. *
  8183. * Returns true if this matrix is the identity matrix
  8184. *
  8185. * @public
  8186. * @sign public {Boolean} isIdentity();
  8187. * @returns {Boolean}
  8188. */
  8189. Matrix2D.prototype.isIdentity = function () {
  8190. return this.a === 1 && this.b === 0 && this.c === 0 && this.d === 1 && this.e === 0 && this.f === 0;
  8191. }; // isIdentity
  8192. /**@
  8193. * #.isInvertible
  8194. * @comp Crafty.math.Matrix2D
  8195. *
  8196. * Determines is this matrix is invertible.
  8197. *
  8198. * @public
  8199. * @sign public {Boolean} isInvertible();
  8200. * @returns {Boolean} true if this matrix is invertible
  8201. * @see .invert
  8202. */
  8203. Matrix2D.prototype.isInvertible = function () {
  8204. return this.determinant() !== 0;
  8205. }; // isInvertible
  8206. /**@
  8207. * #.preRotate
  8208. * @comp Crafty.math.Matrix2D
  8209. *
  8210. * Applies a counter-clockwise pre-rotation to this matrix
  8211. *
  8212. * @public
  8213. * @sign public {Matrix2D} preRotate(Number);
  8214. * @param {number} rads - angle to rotate in radians
  8215. * @returns {Matrix2D} this matrix after pre-rotation
  8216. */
  8217. Matrix2D.prototype.preRotate = function (rads) {
  8218. var nCos = Math.cos(rads);
  8219. var nSin = Math.sin(rads);
  8220. var tmp = this.a;
  8221. this.a = nCos * tmp - nSin * this.b;
  8222. this.b = nSin * tmp + nCos * this.b;
  8223. tmp = this.c;
  8224. this.c = nCos * tmp - nSin * this.d;
  8225. this.d = nSin * tmp + nCos * this.d;
  8226. return this;
  8227. }; // preRotate
  8228. /**@
  8229. * #.preScale
  8230. * @comp Crafty.math.Matrix2D
  8231. *
  8232. * Applies a pre-scaling to this matrix
  8233. *
  8234. * @public
  8235. * @sign public {Matrix2D} preScale(Number[, Number]);
  8236. * @param {Number} scalarX
  8237. * @param {Number} [scalarY] scalarX is used if scalarY is undefined
  8238. * @returns {Matrix2D} this after pre-scaling
  8239. */
  8240. Matrix2D.prototype.preScale = function (scalarX, scalarY) {
  8241. if (scalarY === undefined)
  8242. scalarY = scalarX;
  8243. this.a *= scalarX;
  8244. this.b *= scalarY;
  8245. this.c *= scalarX;
  8246. this.d *= scalarY;
  8247. return this;
  8248. }; // preScale
  8249. /**@
  8250. * #.preTranslate
  8251. * @comp Crafty.math.Matrix2D
  8252. *
  8253. * Applies a pre-translation to this matrix
  8254. *
  8255. * @public
  8256. * @sign public {Matrix2D} preTranslate(Vector2D);
  8257. * @sign public {Matrix2D} preTranslate(Number, Number);
  8258. * @param {Number|Vector2D} dx
  8259. * @param {Number} dy
  8260. * @returns {Matrix2D} this matrix after pre-translation
  8261. */
  8262. Matrix2D.prototype.preTranslate = function (dx, dy) {
  8263. if (typeof dx === "number") {
  8264. this.e += dx;
  8265. this.f += dy;
  8266. } else {
  8267. this.e += dx.x;
  8268. this.f += dx.y;
  8269. } // else
  8270. return this;
  8271. }; // preTranslate
  8272. /**@
  8273. * #.rotate
  8274. * @comp Crafty.math.Matrix2D
  8275. *
  8276. * Applies a counter-clockwise post-rotation to this matrix
  8277. *
  8278. * @public
  8279. * @sign public {Matrix2D} rotate(Number);
  8280. * @param {Number} rads - angle to rotate in radians
  8281. * @returns {Matrix2D} this matrix after rotation
  8282. */
  8283. Matrix2D.prototype.rotate = function (rads) {
  8284. var nCos = Math.cos(rads);
  8285. var nSin = Math.sin(rads);
  8286. var tmp = this.a;
  8287. this.a = nCos * tmp - nSin * this.b;
  8288. this.b = nSin * tmp + nCos * this.b;
  8289. tmp = this.c;
  8290. this.c = nCos * tmp - nSin * this.d;
  8291. this.d = nSin * tmp + nCos * this.d;
  8292. tmp = this.e;
  8293. this.e = nCos * tmp - nSin * this.f;
  8294. this.f = nSin * tmp + nCos * this.f;
  8295. return this;
  8296. }; // rotate
  8297. /**@
  8298. * #.scale
  8299. * @comp Crafty.math.Matrix2D
  8300. *
  8301. * Applies a post-scaling to this matrix
  8302. *
  8303. * @public
  8304. * @sign public {Matrix2D} scale(Number[, Number]);
  8305. * @param {Number} scalarX
  8306. * @param {Number} [scalarY] scalarX is used if scalarY is undefined
  8307. * @returns {Matrix2D} this after post-scaling
  8308. */
  8309. Matrix2D.prototype.scale = function (scalarX, scalarY) {
  8310. if (scalarY === undefined)
  8311. scalarY = scalarX;
  8312. this.a *= scalarX;
  8313. this.b *= scalarY;
  8314. this.c *= scalarX;
  8315. this.d *= scalarY;
  8316. this.e *= scalarX;
  8317. this.f *= scalarY;
  8318. return this;
  8319. }; // scale
  8320. /**@
  8321. * #.setValues
  8322. * @comp Crafty.math.Matrix2D
  8323. *
  8324. * Sets the values of this matrix
  8325. *
  8326. * @public
  8327. * @sign public {Matrix2D} setValues(Matrix2D);
  8328. * @sign public {Matrix2D} setValues(Number, Number, Number, Number, Number, Number);
  8329. * @param {Matrix2D|Number} a
  8330. * @param {Number} b
  8331. * @param {Number} c
  8332. * @param {Number} d
  8333. * @param {Number} e
  8334. * @param {Number} f
  8335. * @returns {Matrix2D} this matrix containing the new values
  8336. */
  8337. Matrix2D.prototype.setValues = function (a, b, c, d, e, f) {
  8338. if (a instanceof Matrix2D) {
  8339. this.a = a.a;
  8340. this.b = a.b;
  8341. this.c = a.c;
  8342. this.d = a.d;
  8343. this.e = a.e;
  8344. this.f = a.f;
  8345. } else {
  8346. this.a = a;
  8347. this.b = b;
  8348. this.c = c;
  8349. this.d = d;
  8350. this.e = e;
  8351. this.f = f;
  8352. } // else
  8353. return this;
  8354. }; // setValues
  8355. /**@
  8356. * #.toString
  8357. * @comp Crafty.math.Matrix2D
  8358. *
  8359. * Returns the string representation of this matrix.
  8360. *
  8361. * @public
  8362. * @sign public {String} toString();
  8363. * @returns {String}
  8364. */
  8365. Matrix2D.prototype.toString = function () {
  8366. return "Matrix2D([" + this.a + ", " + this.c + ", " + this.e +
  8367. "] [" + this.b + ", " + this.d + ", " + this.f + "] [0, 0, 1])";
  8368. }; // toString
  8369. /**@
  8370. * #.translate
  8371. * @comp Crafty.math.Matrix2D
  8372. *
  8373. * Applies a post-translation to this matrix
  8374. *
  8375. * @public
  8376. * @sign public {Matrix2D} translate(Vector2D);
  8377. * @sign public {Matrix2D} translate(Number, Number);
  8378. * @param {Number|Vector2D} dx
  8379. * @param {Number} dy
  8380. * @returns {Matrix2D} this matrix after post-translation
  8381. */
  8382. Matrix2D.prototype.translate = function (dx, dy) {
  8383. if (typeof dx === "number") {
  8384. this.e += this.a * dx + this.c * dy;
  8385. this.f += this.b * dx + this.d * dy;
  8386. } else {
  8387. this.e += this.a * dx.x + this.c * dx.y;
  8388. this.f += this.b * dx.x + this.d * dx.y;
  8389. } // else
  8390. return this;
  8391. }; // translate
  8392. return Matrix2D;
  8393. })();
  8394. },{"./core.js":9}],20:[function(require,module,exports){
  8395. var Crafty = require('./core.js'),
  8396. document = window.document;
  8397. /**@
  8398. * #Particles
  8399. * @category Graphics
  8400. * @trigger ParticleEnd - when the particle animation has finished
  8401. * Based on Parcycle by Mr. Speaker, licensed under the MIT, Ported by Leo Koppelkamm
  8402. * **This is canvas only & won't do anything if the browser doesn't support it!**
  8403. * To see how this works take a look in https://github.com/craftyjs/Crafty/blob/master/src/particles.js
  8404. */
  8405. Crafty.c("Particles", {
  8406. init: function () {
  8407. //We need to clone it
  8408. this._Particles = Crafty.clone(this._Particles);
  8409. this._Particles.parentEntity = this;
  8410. },
  8411. /**@
  8412. * #.particles
  8413. * @comp Particles
  8414. * @sign public this .particles(Object options)
  8415. * @param options - Map of options that specify the behavior and look of the particles.
  8416. *
  8417. * @example
  8418. * ~~~
  8419. * var options = {
  8420. * maxParticles: 150,
  8421. * size: 18,
  8422. * sizeRandom: 4,
  8423. * speed: 1,
  8424. * speedRandom: 1.2,
  8425. * // Lifespan in frames
  8426. * lifeSpan: 29,
  8427. * lifeSpanRandom: 7,
  8428. * // Angle is calculated clockwise: 12pm is 0deg, 3pm is 90deg etc.
  8429. * angle: 65,
  8430. * angleRandom: 34,
  8431. * startColour: [255, 131, 0, 1],
  8432. * startColourRandom: [48, 50, 45, 0],
  8433. * endColour: [245, 35, 0, 0],
  8434. * endColourRandom: [60, 60, 60, 0],
  8435. * // Only applies when fastMode is off, specifies how sharp the gradients are drawn
  8436. * sharpness: 20,
  8437. * sharpnessRandom: 10,
  8438. * // Random spread from origin
  8439. * spread: 10,
  8440. * // How many frames should this last
  8441. * duration: -1,
  8442. * // Will draw squares instead of circle gradients
  8443. * fastMode: false,
  8444. * gravity: { x: 0, y: 0.1 },
  8445. * // sensible values are 0-3
  8446. * jitter: 0
  8447. * }
  8448. *
  8449. * Crafty.e("2D,Canvas,Particles").particles(options);
  8450. * ~~~
  8451. */
  8452. particles: function (options) {
  8453. if (!Crafty.support.canvas || Crafty.deactivateParticles) return this;
  8454. //If we drew on the main canvas, we'd have to redraw
  8455. //potentially huge sections of the screen every frame
  8456. //So we create a separate canvas, where we only have to redraw
  8457. //the changed particles.
  8458. var c, ctx, relativeX, relativeY, bounding;
  8459. c = document.createElement("canvas");
  8460. c.width = Crafty.viewport.width;
  8461. c.height = Crafty.viewport.height;
  8462. c.style.position = 'absolute';
  8463. c.style.left = "0px";
  8464. c.style.top = "0px";
  8465. Crafty.stage.elem.appendChild(c);
  8466. ctx = c.getContext('2d');
  8467. this._Particles.init(options);
  8468. // Clean up the DOM when this component is removed
  8469. this.bind('Remove', function () {
  8470. Crafty.stage.elem.removeChild(c);
  8471. }).bind("RemoveComponent", function (id) {
  8472. if (id === "particles")
  8473. Crafty.stage.elem.removeChild(c);
  8474. });
  8475. relativeX = this.x + Crafty.viewport.x;
  8476. relativeY = this.y + Crafty.viewport.y;
  8477. this._Particles.position = this._Particles.vectorHelpers.create(relativeX, relativeY);
  8478. var oldViewport = {
  8479. x: Crafty.viewport.x,
  8480. y: Crafty.viewport.y
  8481. };
  8482. this.bind('EnterFrame', function () {
  8483. relativeX = this.x + Crafty.viewport.x;
  8484. relativeY = this.y + Crafty.viewport.y;
  8485. this._Particles.viewportDelta = {
  8486. x: Crafty.viewport.x - oldViewport.x,
  8487. y: Crafty.viewport.y - oldViewport.y
  8488. };
  8489. oldViewport = {
  8490. x: Crafty.viewport.x,
  8491. y: Crafty.viewport.y
  8492. };
  8493. this._Particles.position = this._Particles.vectorHelpers.create(relativeX, relativeY);
  8494. //Selective clearing
  8495. if (typeof Crafty.DrawManager.boundingRect == 'function') {
  8496. bounding = Crafty.DrawManager.boundingRect(this._Particles.register);
  8497. if (bounding) ctx.clearRect(bounding._x, bounding._y, bounding._w, bounding._h);
  8498. } else {
  8499. ctx.clearRect(0, 0, Crafty.viewport.width, Crafty.viewport.height);
  8500. }
  8501. //This updates all particle colors & positions
  8502. this._Particles.update();
  8503. //This renders the updated particles
  8504. this._Particles.render(ctx);
  8505. });
  8506. return this;
  8507. },
  8508. _Particles: {
  8509. presets: {
  8510. maxParticles: 150,
  8511. size: 18,
  8512. sizeRandom: 4,
  8513. speed: 1,
  8514. speedRandom: 1.2,
  8515. // Lifespan in frames
  8516. lifeSpan: 29,
  8517. lifeSpanRandom: 7,
  8518. // Angle is calculated clockwise: 12pm is 0deg, 3pm is 90deg etc.
  8519. angle: 65,
  8520. angleRandom: 34,
  8521. startColour: [255, 131, 0, 1],
  8522. startColourRandom: [48, 50, 45, 0],
  8523. endColour: [245, 35, 0, 0],
  8524. endColourRandom: [60, 60, 60, 0],
  8525. // Only applies when fastMode is off, specifies how sharp the gradients are drawn
  8526. sharpness: 20,
  8527. sharpnessRandom: 10,
  8528. // Random spread from origin
  8529. spread: 10,
  8530. // How many frames should this last
  8531. duration: -1,
  8532. // Will draw squares instead of circle gradients
  8533. fastMode: false,
  8534. gravity: {
  8535. x: 0,
  8536. y: 0.1
  8537. },
  8538. // sensible values are 0-3
  8539. jitter: 0,
  8540. //Don't modify the following
  8541. particles: [],
  8542. active: true,
  8543. particleCount: 0,
  8544. elapsedFrames: 0,
  8545. emissionRate: 0,
  8546. emitCounter: 0,
  8547. particleIndex: 0
  8548. },
  8549. init: function (options) {
  8550. this.position = this.vectorHelpers.create(0, 0);
  8551. if (typeof options == 'undefined') options = {};
  8552. //Create current config by merging given options and presets.
  8553. for (var key in this.presets) {
  8554. if (typeof options[key] != 'undefined') this[key] = options[key];
  8555. else this[key] = this.presets[key];
  8556. }
  8557. this.emissionRate = this.maxParticles / this.lifeSpan;
  8558. this.positionRandom = this.vectorHelpers.create(this.spread, this.spread);
  8559. },
  8560. addParticle: function () {
  8561. if (this.particleCount == this.maxParticles) {
  8562. return false;
  8563. }
  8564. // Take the next particle out of the particle pool we have created and initialize it
  8565. var particle = new this.particle(this.vectorHelpers);
  8566. this.initParticle(particle);
  8567. this.particles[this.particleCount] = particle;
  8568. // Increment the particle count
  8569. this.particleCount++;
  8570. return true;
  8571. },
  8572. RANDM1TO1: function () {
  8573. return Math.random() * 2 - 1;
  8574. },
  8575. initParticle: function (particle) {
  8576. particle.position.x = this.position.x + this.positionRandom.x * this.RANDM1TO1();
  8577. particle.position.y = this.position.y + this.positionRandom.y * this.RANDM1TO1();
  8578. var newAngle = (this.angle + this.angleRandom * this.RANDM1TO1()) * (Math.PI / 180); // convert to radians
  8579. var vector = this.vectorHelpers.create(Math.sin(newAngle), -Math.cos(newAngle)); // Could move to lookup for speed
  8580. var vectorSpeed = this.speed + this.speedRandom * this.RANDM1TO1();
  8581. particle.direction = this.vectorHelpers.multiply(vector, vectorSpeed);
  8582. particle.size = this.size + this.sizeRandom * this.RANDM1TO1();
  8583. particle.size = particle.size < 0 ? 0 : ~~particle.size;
  8584. particle.timeToLive = this.lifeSpan + this.lifeSpanRandom * this.RANDM1TO1();
  8585. particle.sharpness = this.sharpness + this.sharpnessRandom * this.RANDM1TO1();
  8586. particle.sharpness = particle.sharpness > 100 ? 100 : particle.sharpness < 0 ? 0 : particle.sharpness;
  8587. // internal circle gradient size - affects the sharpness of the radial gradient
  8588. particle.sizeSmall = ~~ ((particle.size / 200) * particle.sharpness); //(size/2/100)
  8589. var start = [
  8590. this.startColour[0] + this.startColourRandom[0] * this.RANDM1TO1(),
  8591. this.startColour[1] + this.startColourRandom[1] * this.RANDM1TO1(),
  8592. this.startColour[2] + this.startColourRandom[2] * this.RANDM1TO1(),
  8593. this.startColour[3] + this.startColourRandom[3] * this.RANDM1TO1()
  8594. ];
  8595. var end = [
  8596. this.endColour[0] + this.endColourRandom[0] * this.RANDM1TO1(),
  8597. this.endColour[1] + this.endColourRandom[1] * this.RANDM1TO1(),
  8598. this.endColour[2] + this.endColourRandom[2] * this.RANDM1TO1(),
  8599. this.endColour[3] + this.endColourRandom[3] * this.RANDM1TO1()
  8600. ];
  8601. particle.colour = start;
  8602. particle.deltaColour[0] = (end[0] - start[0]) / particle.timeToLive;
  8603. particle.deltaColour[1] = (end[1] - start[1]) / particle.timeToLive;
  8604. particle.deltaColour[2] = (end[2] - start[2]) / particle.timeToLive;
  8605. particle.deltaColour[3] = (end[3] - start[3]) / particle.timeToLive;
  8606. },
  8607. update: function () {
  8608. if (this.active && this.emissionRate > 0) {
  8609. var rate = 1 / this.emissionRate;
  8610. this.emitCounter++;
  8611. while (this.particleCount < this.maxParticles && this.emitCounter > rate) {
  8612. this.addParticle();
  8613. this.emitCounter -= rate;
  8614. }
  8615. this.elapsedFrames++;
  8616. if (this.duration != -1 && this.duration < this.elapsedFrames) {
  8617. this.stop();
  8618. }
  8619. }
  8620. this.particleIndex = 0;
  8621. this.register = [];
  8622. var draw;
  8623. while (this.particleIndex < this.particleCount) {
  8624. var currentParticle = this.particles[this.particleIndex];
  8625. // If the current particle is alive then update it
  8626. if (currentParticle.timeToLive > 0) {
  8627. // Calculate the new direction based on gravity
  8628. currentParticle.direction = this.vectorHelpers.add(currentParticle.direction, this.gravity);
  8629. currentParticle.position = this.vectorHelpers.add(currentParticle.position, currentParticle.direction);
  8630. currentParticle.position = this.vectorHelpers.add(currentParticle.position, this.viewportDelta);
  8631. if (this.jitter) {
  8632. currentParticle.position.x += this.jitter * this.RANDM1TO1();
  8633. currentParticle.position.y += this.jitter * this.RANDM1TO1();
  8634. }
  8635. currentParticle.timeToLive--;
  8636. // Update colours
  8637. var r = currentParticle.colour[0] += currentParticle.deltaColour[0];
  8638. var g = currentParticle.colour[1] += currentParticle.deltaColour[1];
  8639. var b = currentParticle.colour[2] += currentParticle.deltaColour[2];
  8640. var a = currentParticle.colour[3] += currentParticle.deltaColour[3];
  8641. // Calculate the rgba string to draw.
  8642. draw = [];
  8643. draw.push("rgba(" + (r > 255 ? 255 : r < 0 ? 0 : ~~r));
  8644. draw.push(g > 255 ? 255 : g < 0 ? 0 : ~~g);
  8645. draw.push(b > 255 ? 255 : b < 0 ? 0 : ~~b);
  8646. draw.push((a > 1 ? 1 : a < 0 ? 0 : a.toFixed(2)) + ")");
  8647. currentParticle.drawColour = draw.join(",");
  8648. if (!this.fastMode) {
  8649. draw[3] = "0)";
  8650. currentParticle.drawColourEnd = draw.join(",");
  8651. }
  8652. this.particleIndex++;
  8653. } else {
  8654. // Replace particle with the last active
  8655. if (this.particleIndex != this.particleCount - 1) {
  8656. this.particles[this.particleIndex] = this.particles[this.particleCount - 1];
  8657. }
  8658. this.particleCount--;
  8659. }
  8660. var rect = {};
  8661. rect._x = ~~currentParticle.position.x;
  8662. rect._y = ~~currentParticle.position.y;
  8663. rect._w = currentParticle.size;
  8664. rect._h = currentParticle.size;
  8665. this.register.push(rect);
  8666. }
  8667. },
  8668. stop: function () {
  8669. this.active = false;
  8670. this.elapsedFrames = 0;
  8671. this.emitCounter = 0;
  8672. this.parentEntity.trigger("ParticleEnd");
  8673. },
  8674. render: function (context) {
  8675. for (var i = 0, j = this.particleCount; i < j; i++) {
  8676. var particle = this.particles[i];
  8677. var size = particle.size;
  8678. var halfSize = size >> 1;
  8679. if (particle.position.x + size < 0 || particle.position.y + size < 0 || particle.position.x - size > Crafty.viewport.width || particle.position.y - size > Crafty.viewport.height) {
  8680. //Particle is outside
  8681. continue;
  8682. }
  8683. var x = ~~particle.position.x;
  8684. var y = ~~particle.position.y;
  8685. if (this.fastMode) {
  8686. context.fillStyle = particle.drawColour;
  8687. } else {
  8688. var radgrad = context.createRadialGradient(x + halfSize, y + halfSize, particle.sizeSmall, x + halfSize, y + halfSize, halfSize);
  8689. radgrad.addColorStop(0, particle.drawColour);
  8690. //0.9 to avoid visible boxing
  8691. radgrad.addColorStop(0.9, particle.drawColourEnd);
  8692. context.fillStyle = radgrad;
  8693. }
  8694. context.fillRect(x, y, size, size);
  8695. }
  8696. },
  8697. particle: function (vectorHelpers) {
  8698. this.position = vectorHelpers.create(0, 0);
  8699. this.direction = vectorHelpers.create(0, 0);
  8700. this.size = 0;
  8701. this.sizeSmall = 0;
  8702. this.timeToLive = 0;
  8703. this.colour = [];
  8704. this.drawColour = "";
  8705. this.deltaColour = [];
  8706. this.sharpness = 0;
  8707. },
  8708. vectorHelpers: {
  8709. create: function (x, y) {
  8710. return {
  8711. "x": x,
  8712. "y": y
  8713. };
  8714. },
  8715. multiply: function (vector, scaleFactor) {
  8716. vector.x *= scaleFactor;
  8717. vector.y *= scaleFactor;
  8718. return vector;
  8719. },
  8720. add: function (vector1, vector2) {
  8721. vector1.x += vector2.x;
  8722. vector1.y += vector2.y;
  8723. return vector1;
  8724. }
  8725. }
  8726. }
  8727. });
  8728. },{"./core.js":9}],21:[function(require,module,exports){
  8729. var Crafty = require('./core.js'),
  8730. document = window.document;
  8731. Crafty.extend({
  8732. _scenes: {},
  8733. _current: null,
  8734. /**@
  8735. * #Crafty.scene
  8736. * @category Scenes, Stage
  8737. * @trigger SceneChange - just before a new scene is initialized - { oldScene:String, newScene:String }
  8738. * @trigger SceneDestroy - just before the current scene is destroyed - { newScene:String }
  8739. *
  8740. * @sign public void Crafty.scene(String sceneName, Function init[, Function uninit])
  8741. * @param sceneName - Name of the scene to add
  8742. * @param init - Function to execute when scene is played
  8743. * @param uninit - Function to execute before next scene is played, after entities with `2D` are destroyed
  8744. * This is equivalent to calling `Crafty.defineScene`.
  8745. *
  8746. * @sign public void Crafty.scene(String sceneName[, Data])
  8747. * @param sceneName - Name of scene to play
  8748. * @param Data - The init function of the scene will be called with this data as its parameter. Can be of any type other than a function.
  8749. * This is equivalent to calling `Crafty.enterScene`.
  8750. *
  8751. * Method to create scenes on the stage. Pass an ID and function to register a scene.
  8752. *
  8753. * To play a scene, just pass the ID. When a scene is played, all
  8754. * previously-created entities with the `2D` component are destroyed. The
  8755. * viewport is also reset.
  8756. *
  8757. * You can optionally specify an arugment that will be passed to the scene's init function.
  8758. *
  8759. * If you want some entities to persist over scenes (as in, not be destroyed)
  8760. * simply add the component `Persist`.
  8761. *
  8762. * @example
  8763. * ~~~
  8764. * Crafty.defineScene("loading", function() {
  8765. * Crafty.background("#000");
  8766. * Crafty.e("2D, DOM, Text")
  8767. * .attr({ w: 100, h: 20, x: 150, y: 120 })
  8768. * .text("Loading")
  8769. * .css({ "text-align": "center"})
  8770. * .textColor("#FFFFFF");
  8771. * });
  8772. *
  8773. * Crafty.defineScene("UFO_dance",
  8774. * function() {Crafty.background("#444"); Crafty.e("UFO");},
  8775. * function() {...send message to server...});
  8776. *
  8777. * // An example of an init function which accepts arguments, in this case an object.
  8778. * Crafty.defineScene("square", function(attributes) {
  8779. * Crafty.background("#000");
  8780. * Crafty.e("2D, DOM, Color")
  8781. * .attr(attributes)
  8782. * .color("red");
  8783. *
  8784. * });
  8785. *
  8786. * ~~~
  8787. * This defines (but does not play) two scenes as discussed below.
  8788. * ~~~
  8789. * Crafty.enterScene("loading");
  8790. * ~~~
  8791. * This command will clear the stage by destroying all `2D` entities (except
  8792. * those with the `Persist` component). Then it will set the background to
  8793. * black and display the text "Loading".
  8794. * ~~~
  8795. * Crafty.enterScene("UFO_dance");
  8796. * ~~~
  8797. * This command will clear the stage by destroying all `2D` entities (except
  8798. * those with the `Persist` component). Then it will set the background to
  8799. * gray and create a UFO entity. Finally, the next time the game encounters
  8800. * another command of the form `Crafty.scene(scene_name)` (if ever), then the
  8801. * game will send a message to the server.
  8802. * ~~~
  8803. * Crafty.enterScene("square", {x:10, y:10, w:20, h:20});
  8804. * ~~~
  8805. * This will clear the stage, set the background black, and create a red square with the specified position and dimensions.
  8806. * ~~~
  8807. */
  8808. scene: function (name, intro, outro) {
  8809. // If there's one argument, or the second argument isn't a function, play the scene
  8810. if (arguments.length === 1 || typeof(arguments[1]) !== "function") {
  8811. Crafty.enterScene(name, arguments[1]);
  8812. return;
  8813. }
  8814. // Otherwise, this is a call to create a scene
  8815. Crafty.defineScene(name, intro, outro);
  8816. },
  8817. /*
  8818. * #Crafty.defineScene
  8819. * @category Scenes, Stage
  8820. *
  8821. * @sign public void Crafty.enterScene(String name[, Data])
  8822. * @param name - Name of the scene to run.
  8823. * @param Data - The init function of the scene will be called with this data as its parameter. Can be of any type other than a function.
  8824. *
  8825. * @see Crafty.enterScene
  8826. * @see Crafty.scene
  8827. */
  8828. defineScene: function(name, init, uninit){
  8829. if (typeof init !== "function")
  8830. throw("Init function is the wrong type.");
  8831. this._scenes[name] = {};
  8832. this._scenes[name].initialize = init;
  8833. if (typeof uninit !== 'undefined') {
  8834. this._scenes[name].uninitialize = uninit;
  8835. }
  8836. return;
  8837. },
  8838. /*
  8839. * #Crafty.enterScene
  8840. * @category Scenes, Stage
  8841. * @trigger SceneChange - just before a new scene is initialized - { oldScene:String, newScene:String }
  8842. * @trigger SceneDestroy - just before the current scene is destroyed - { newScene:String }
  8843. *
  8844. * @sign public void Crafty.enterScene(String name[, Data])
  8845. * @param name - Name of the scene to run.
  8846. * @param Data - The init function of the scene will be called with this data as its parameter. Can be of any type other than a function.
  8847. *
  8848. * @see Crafty.defineScene
  8849. * @see Crafty.scene
  8850. */
  8851. enterScene: function(name, data){
  8852. if (typeof data === "function")
  8853. throw("Scene data cannot be a function");
  8854. // ---FYI---
  8855. // this._current is the name (ID) of the scene in progress.
  8856. // this._scenes is an object like the following:
  8857. // {'Opening scene': {'initialize': fnA, 'uninitialize': fnB},
  8858. // 'Another scene': {'initialize': fnC, 'uninitialize': fnD}}
  8859. Crafty.trigger("SceneDestroy", {
  8860. newScene: name
  8861. });
  8862. Crafty.viewport.reset();
  8863. Crafty("2D").each(function () {
  8864. if (!this.has("Persist")) this.destroy();
  8865. });
  8866. // uninitialize previous scene
  8867. if (this._current !== null && 'uninitialize' in this._scenes[this._current]) {
  8868. this._scenes[this._current].uninitialize.call(this);
  8869. }
  8870. // initialize next scene
  8871. var oldScene = this._current;
  8872. this._current = name;
  8873. Crafty.trigger("SceneChange", {
  8874. oldScene: oldScene,
  8875. newScene: name
  8876. });
  8877. this._scenes[name].initialize.call(this, data);
  8878. return;
  8879. }
  8880. });
  8881. },{"./core.js":9}],22:[function(require,module,exports){
  8882. var Crafty = require('./core.js'),
  8883. document = window.document;
  8884. Crafty.extend({
  8885. /**@
  8886. * #Crafty.audio
  8887. * @category Audio
  8888. *
  8889. * Add sound files and play them. Chooses best format for browser support.
  8890. * Due to the nature of HTML5 audio, three types of audio files will be
  8891. * required for cross-browser capabilities. These formats are MP3, Ogg and WAV.
  8892. * When sound was not muted on before pause, sound will be unmuted after unpause.
  8893. * When sound is muted Crafty.pause() does not have any effect on sound
  8894. *
  8895. * The maximum number of sounds that can be played simultaneously is defined by Crafty.audio.maxChannels. The default value is 7.
  8896. */
  8897. audio: {
  8898. sounds: {},
  8899. supported: null,
  8900. codecs: { // Chart from jPlayer
  8901. ogg: 'audio/ogg; codecs="vorbis"', //OGG
  8902. wav: 'audio/wav; codecs="1"', // PCM
  8903. webma: 'audio/webm; codecs="vorbis"', // WEBM
  8904. mp3: 'audio/mpeg; codecs="mp3"', //MP3
  8905. m4a: 'audio/mp4; codecs="mp4a.40.2"' // AAC / MP4
  8906. },
  8907. volume: 1, //Global Volume
  8908. muted: false,
  8909. paused: false,
  8910. playCheck: null,
  8911. /**
  8912. * Function to setup supported formats
  8913. **/
  8914. _canPlay: function () {
  8915. this.supported = {};
  8916. // Without support, no formats are supported
  8917. if (!Crafty.support.audio)
  8918. return;
  8919. var audio = this.audioElement(),
  8920. canplay;
  8921. for (var i in this.codecs) {
  8922. canplay = audio.canPlayType(this.codecs[i]);
  8923. if (canplay !== "" && canplay !== "no") {
  8924. this.supported[i] = true;
  8925. } else {
  8926. this.supported[i] = false;
  8927. }
  8928. }
  8929. },
  8930. /**@
  8931. * #Crafty.audio.supports
  8932. * @comp Crafty.audio
  8933. * @sign public this Crafty.audio.supports(String extension)
  8934. * @param extension - A file extension to check audio support for
  8935. *
  8936. * Return true if the browser thinks it can play the given file type, otherwise false
  8937. */
  8938. supports: function (extension) {
  8939. // Build cache of supported formats, if necessary
  8940. if (this.supported === null)
  8941. this._canPlay();
  8942. if (this.supported[extension])
  8943. return true;
  8944. else
  8945. return false;
  8946. },
  8947. /**
  8948. * Function to get an Audio Element
  8949. **/
  8950. audioElement: function () {
  8951. //IE does not support Audio Object
  8952. return typeof Audio !== 'undefined' ? new Audio("") : document.createElement('audio');
  8953. },
  8954. /**@
  8955. * #Crafty.audio.create
  8956. * @comp Crafty.audio
  8957. * @sign public this Crafty.audio.create(String id, String url)
  8958. * @param id - A string to refer to sounds
  8959. * @param url - A string pointing to the sound file
  8960. *
  8961. * Creates an audio asset with the given id and resource. `Crafty.audio.add` is a more flexible interface that allows cross-browser compatibility.
  8962. *
  8963. * If the sound file extension is not supported, returns false; otherwise, returns the audio asset.
  8964. */
  8965. create: function (id, path) {
  8966. //check extension, return if not supported
  8967. var ext = path.substr(path.lastIndexOf('.') + 1).toLowerCase();
  8968. if (!this.supports(ext))
  8969. return false;
  8970. //initiate the audio element
  8971. var audio = this.audioElement();
  8972. audio.id = id;
  8973. audio.preload = "auto";
  8974. audio.volume = Crafty.audio.volume;
  8975. audio.src = path;
  8976. //create an asset and metadata for the audio element
  8977. Crafty.asset(path, audio);
  8978. this.sounds[id] = {
  8979. obj: audio,
  8980. played: 0,
  8981. volume: Crafty.audio.volume
  8982. };
  8983. return this.sounds[id];
  8984. },
  8985. /**@
  8986. * #Crafty.audio.add
  8987. * @comp Crafty.audio
  8988. * @sign public this Crafty.audio.add(String id, String url)
  8989. * @param id - A string to refer to sounds
  8990. * @param url - A string pointing to the sound file
  8991. * @sign public this Crafty.audio.add(String id, Array urls)
  8992. * @param urls - Array of urls pointing to different format of the same sound, selecting the first that is playable
  8993. * @sign public this Crafty.audio.add(Object map)
  8994. * @param map - key-value pairs where the key is the `id` and the value is either a `url` or `urls`
  8995. *
  8996. * Loads a sound to be played. Due to the nature of HTML5 audio,
  8997. * three types of audio files will be required for cross-browser capabilities.
  8998. * These formats are MP3, Ogg and WAV.
  8999. *
  9000. * Passing an array of URLs will determine which format the browser can play and select it over any other.
  9001. *
  9002. * Accepts an object where the key is the audio name and
  9003. * either a URL or an Array of URLs (to determine which type to use).
  9004. *
  9005. * The ID you use will be how you refer to that sound when using `Crafty.audio.play`.
  9006. *
  9007. * @example
  9008. * ~~~
  9009. * //adding audio from an object
  9010. * Crafty.audio.add({
  9011. * shoot: ["sounds/shoot.wav",
  9012. * "sounds/shoot.mp3",
  9013. * "sounds/shoot.ogg"],
  9014. *
  9015. * coin: "sounds/coin.mp3"
  9016. * });
  9017. *
  9018. * //adding a single sound
  9019. * Crafty.audio.add("walk", [
  9020. * "sounds/walk.mp3",
  9021. * "sounds/walk.ogg",
  9022. * "sounds/walk.wav"
  9023. * ]);
  9024. *
  9025. * //only one format
  9026. * Crafty.audio.add("jump", "sounds/jump.mp3");
  9027. * ~~~
  9028. */
  9029. add: function (id, url) {
  9030. if (!Crafty.support.audio)
  9031. return;
  9032. var src;
  9033. if (arguments.length === 1 && typeof id === "object") {
  9034. for (var i in id) {
  9035. for (src in id[i]) {
  9036. if (Crafty.audio.create(i, id[i][src]))
  9037. break;
  9038. }
  9039. }
  9040. }
  9041. if (typeof id === "string") {
  9042. if (typeof url === "string") {
  9043. Crafty.audio.create(id, url);
  9044. }
  9045. if (typeof url === "object") {
  9046. for (src in url) {
  9047. if (Crafty.audio.create(id, url[src]))
  9048. break;
  9049. }
  9050. }
  9051. }
  9052. },
  9053. /**@
  9054. * #Crafty.audio.play
  9055. * @comp Crafty.audio
  9056. * @sign public this Crafty.audio.play(String id)
  9057. * @sign public this Crafty.audio.play(String id, Number repeatCount)
  9058. * @sign public this Crafty.audio.play(String id, Number repeatCount, Number volume)
  9059. * @param id - A string to refer to sounds
  9060. * @param repeatCount - Repeat count for the file, where -1 stands for repeat forever.
  9061. * @param volume - volume can be a number between 0.0 and 1.0
  9062. * @returns The audio element used to play the sound. Null if the call failed due to a lack of open channels.
  9063. *
  9064. * Will play a sound previously added by using the ID that was used in `Crafty.audio.add`.
  9065. * Has a default maximum of 5 channels so that the same sound can play simultaneously unless all of the channels are playing.
  9066. * *Note that the implementation of HTML5 Audio is buggy at best.*
  9067. *
  9068. * @example
  9069. * ~~~
  9070. * Crafty.audio.play("walk");
  9071. *
  9072. * //play and repeat forever
  9073. * Crafty.audio.play("backgroundMusic", -1);
  9074. * Crafty.audio.play("explosion",1,0.5); //play sound once with volume of 50%
  9075. * ~~~
  9076. */
  9077. play: function (id, repeat, volume) {
  9078. if (repeat === 0 || !Crafty.support.audio || !this.sounds[id])
  9079. return;
  9080. var s = this.sounds[id];
  9081. var c = this.getOpenChannel();
  9082. if (!c)
  9083. return null;
  9084. c.id = id;
  9085. c.repeat = repeat;
  9086. var a = c.obj;
  9087. c.volume = s.volume = s.obj.volume = volume || Crafty.audio.volume;
  9088. a.volume = s.volume;
  9089. a.src = s.obj.src;
  9090. if (this.muted)
  9091. a.volume = 0;
  9092. a.play();
  9093. s.played++;
  9094. c.onEnd = function () {
  9095. if (s.played < c.repeat || repeat == -1) {
  9096. if (this.currentTime)
  9097. this.currentTime = 0;
  9098. this.play();
  9099. s.played++;
  9100. } else {
  9101. c.active = false;
  9102. this.pause();
  9103. this.removeEventListener("ended", c.onEnd, true);
  9104. this.currentTime = 0;
  9105. Crafty.trigger("SoundComplete", {
  9106. id: c.id
  9107. });
  9108. }
  9109. };
  9110. a.addEventListener("ended", c.onEnd, true);
  9111. return a;
  9112. },
  9113. /**@
  9114. * #Crafty.audio.setChannels
  9115. * @comp Crafty.audio
  9116. * @sign public this Crafty.audio.setChannels(Number n)
  9117. * @param n - The maximum number of channels
  9118. */
  9119. maxChannels: 7,
  9120. setChannels: function (n) {
  9121. this.maxChannels = n;
  9122. if (n < this.channels.length)
  9123. this.channels.length = n;
  9124. },
  9125. channels: [],
  9126. // Finds an unused audio element, marks it as in use, and return it.
  9127. getOpenChannel: function () {
  9128. for (var i = 0; i < this.channels.length; i++) {
  9129. var chan = this.channels[i];
  9130. /*
  9131. * Second test looks for stuff that's out of use,
  9132. * but fallen foul of Chromium bug 280417
  9133. */
  9134. if (chan.active === false ||
  9135. chan.obj.ended && chan.repeat <= this.sounds[chan.id].played) {
  9136. chan.active = true;
  9137. return chan;
  9138. }
  9139. }
  9140. // If necessary, create a new element, unless we've already reached the max limit
  9141. if (i < this.maxChannels) {
  9142. var c = {
  9143. obj: this.audioElement(),
  9144. active: true,
  9145. // Checks that the channel is being used to play sound id
  9146. _is: function (id) {
  9147. return this.id === id && this.active;
  9148. }
  9149. };
  9150. this.channels.push(c);
  9151. return c;
  9152. }
  9153. // In that case, return null
  9154. return null;
  9155. },
  9156. /**@
  9157. * #Crafty.audio.remove
  9158. * @comp Crafty.audio
  9159. * @sign public this Crafty.audio.remove([String id])
  9160. * @param id - A string to refer to sounds
  9161. *
  9162. * Will stop the sound and remove all references to the audio object allowing the browser to free the memory.
  9163. * If no id is given, all sounds will be removed.
  9164. *
  9165. * @example
  9166. * ~~~
  9167. * Crafty.audio.remove("walk");
  9168. * ~~~
  9169. */
  9170. remove: function (id) {
  9171. if (!Crafty.support.audio)
  9172. return;
  9173. var s;
  9174. if (!id) {
  9175. for (var i in this.sounds) {
  9176. s = this.sounds[i];
  9177. Crafty.audio.stop(id);
  9178. delete Crafty.assets[s.obj.src];
  9179. delete Crafty.audio.sounds[id];
  9180. }
  9181. return;
  9182. }
  9183. if (!this.sounds[id])
  9184. return;
  9185. s = this.sounds[id];
  9186. Crafty.audio.stop(id);
  9187. delete Crafty.assets[s.obj.src];
  9188. delete Crafty.audio.sounds[id];
  9189. },
  9190. /**@
  9191. * #Crafty.audio.stop
  9192. * @comp Crafty.audio
  9193. * @sign public this Crafty.audio.stop([Number ID])
  9194. *
  9195. * Stops any playing sound. if id is not set, stop all sounds which are playing
  9196. *
  9197. * @example
  9198. * ~~~
  9199. * //all sounds stopped playing now
  9200. * Crafty.audio.stop();
  9201. *
  9202. * ~~~
  9203. */
  9204. stop: function (id) {
  9205. if (!Crafty.support.audio)
  9206. return;
  9207. for (var i in this.channels) {
  9208. c = this.channels[i];
  9209. if ( (!id && c.active) || c._is(id) ) {
  9210. c.active = false;
  9211. c.obj.pause();
  9212. }
  9213. }
  9214. return;
  9215. },
  9216. /**
  9217. * #Crafty.audio._mute
  9218. * @comp Crafty.audio
  9219. * @sign public this Crafty.audio._mute([Boolean mute])
  9220. *
  9221. * Mute or unmute every Audio instance that is playing.
  9222. */
  9223. _mute: function (mute) {
  9224. if (!Crafty.support.audio)
  9225. return;
  9226. var c;
  9227. for (var i in this.channels) {
  9228. c = this.channels[i];
  9229. c.obj.volume = mute ? 0 : c.volume;
  9230. }
  9231. this.muted = mute;
  9232. },
  9233. /**@
  9234. * #Crafty.audio.toggleMute
  9235. * @comp Crafty.audio
  9236. * @sign public this Crafty.audio.toggleMute()
  9237. *
  9238. * Mute or unmute every Audio instance that is playing. Toggles between
  9239. * pausing or playing depending on the state.
  9240. *
  9241. * @example
  9242. * ~~~
  9243. * //toggle mute and unmute depending on current state
  9244. * Crafty.audio.toggleMute();
  9245. * ~~~
  9246. */
  9247. toggleMute: function () {
  9248. if (!this.muted) {
  9249. this._mute(true);
  9250. } else {
  9251. this._mute(false);
  9252. }
  9253. },
  9254. /**@
  9255. * #Crafty.audio.mute
  9256. * @comp Crafty.audio
  9257. * @sign public this Crafty.audio.mute()
  9258. *
  9259. * Mute every Audio instance that is playing.
  9260. *
  9261. * @example
  9262. * ~~~
  9263. * Crafty.audio.mute();
  9264. * ~~~
  9265. */
  9266. mute: function () {
  9267. this._mute(true);
  9268. },
  9269. /**@
  9270. * #Crafty.audio.unmute
  9271. * @comp Crafty.audio
  9272. * @sign public this Crafty.audio.unmute()
  9273. *
  9274. * Unmute every Audio instance that is playing.
  9275. *
  9276. * @example
  9277. * ~~~
  9278. * Crafty.audio.unmute();
  9279. * ~~~
  9280. */
  9281. unmute: function () {
  9282. this._mute(false);
  9283. },
  9284. /**@
  9285. * #Crafty.audio.pause
  9286. * @comp Crafty.audio
  9287. * @sign public this Crafty.audio.pause(string ID)
  9288. * @param {string} id - The id of the audio object to pause
  9289. *
  9290. * Pause the Audio instance specified by id param.
  9291. *
  9292. * @example
  9293. * ~~~
  9294. * Crafty.audio.pause('music');
  9295. * ~~~
  9296. *
  9297. */
  9298. pause: function (id) {
  9299. if (!Crafty.support.audio || !id || !this.sounds[id])
  9300. return;
  9301. var c;
  9302. for (var i in this.channels) {
  9303. c = this.channels[i];
  9304. if (c._is(id) && !c.obj.paused)
  9305. c.obj.pause();
  9306. }
  9307. },
  9308. /**@
  9309. * #Crafty.audio.unpause
  9310. * @comp Crafty.audio
  9311. * @sign public this Crafty.audio.unpause(string ID)
  9312. * @param {string} id - The id of the audio object to unpause
  9313. *
  9314. * Resume playing the Audio instance specified by id param.
  9315. *
  9316. * @example
  9317. * ~~~
  9318. * Crafty.audio.unpause('music');
  9319. * ~~~
  9320. *
  9321. */
  9322. unpause: function (id) {
  9323. if (!Crafty.support.audio || !id || !this.sounds[id])
  9324. return;
  9325. var c;
  9326. for (var i in this.channels) {
  9327. c = this.channels[i];
  9328. if (c._is(id) && c.obj.paused)
  9329. c.obj.play();
  9330. }
  9331. },
  9332. /**@
  9333. * #Crafty.audio.togglePause
  9334. * @comp Crafty.audio
  9335. * @sign public this Crafty.audio.togglePause(string ID)
  9336. * @param {string} id - The id of the audio object to pause/
  9337. *
  9338. * Toggle the pause status of the Audio instance specified by id param.
  9339. *
  9340. * @example
  9341. * ~~~
  9342. * Crafty.audio.togglePause('music');
  9343. * ~~~
  9344. *
  9345. */
  9346. togglePause: function (id) {
  9347. if (!Crafty.support.audio || !id || !this.sounds[id])
  9348. return;
  9349. var c;
  9350. for (var i in this.channels) {
  9351. c = this.channels[i];
  9352. if (c._is(id)) {
  9353. if (c.obj.paused) {
  9354. c.obj.play();
  9355. } else {
  9356. c.obj.pause();
  9357. }
  9358. }
  9359. }
  9360. }
  9361. }
  9362. });
  9363. },{"./core.js":9}],23:[function(require,module,exports){
  9364. var Crafty = require('./core.js'),
  9365. Animation = require('./animation.js'),
  9366. document = window.document;
  9367. /**@
  9368. * #SpriteAnimation
  9369. * @category Animation
  9370. * @trigger StartAnimation - When an animation starts playing, or is resumed from the paused state - {Reel}
  9371. * @trigger AnimationEnd - When the animation finishes - { Reel }
  9372. * @trigger FrameChange - Each time the frame of the current reel changes - { Reel }
  9373. * @trigger ReelChange - When the reel changes - { Reel }
  9374. *
  9375. * Used to animate sprites by treating a sprite map as a set of animation frames.
  9376. * Must be applied to an entity that has a sprite-map component.
  9377. *
  9378. * To define an animation, see the `reel` method. To play an animation, see the `animate` method.
  9379. *
  9380. * A reel is an object that contains the animation frames and current state for an animation. The reel object has the following properties:
  9381. * @param id: (String) - the name of the reel
  9382. * @param frames: (Array) - A list of frames in the format [xpos, ypos]
  9383. * @param currentFrame: (Number) - The index of the current frame
  9384. * @param easing: (Crafty.easing object) - The object that handles the internal progress of the animation.
  9385. * @param duration: (Number) - The duration in milliseconds.
  9386. *
  9387. * Many animation related events pass a reel object as data. As typical with events, this should be treated as read only data that might be later altered by the entity. If you wish to preserve the data, make a copy of it.
  9388. *
  9389. * @see crafty.sprite
  9390. */
  9391. Crafty.c("SpriteAnimation", {
  9392. /*
  9393. *
  9394. * A map in which the keys are the names assigned to animations defined using
  9395. * the component (also known as reelIDs), and the values are objects describing
  9396. * the animation and its state.
  9397. */
  9398. _reels: null,
  9399. /*
  9400. * The reelID of the currently active reel (which is one of the elements in `this._reels`).
  9401. * This value is `null` if no reel is active. Some of the component's actions can be invoked
  9402. * without specifying a reel, in which case they will work on the active reel.
  9403. */
  9404. _currentReelId: null,
  9405. /*
  9406. * The currently active reel.
  9407. * This value is `null` if no reel is active.
  9408. */
  9409. _currentReel: null,
  9410. /*
  9411. * Whether or not an animation is currently playing.
  9412. */
  9413. _isPlaying: false,
  9414. /**@
  9415. * #.animationSpeed
  9416. * @comp SpriteAnimation
  9417. *
  9418. * The playback rate of the animation. This property defaults to 1.
  9419. */
  9420. animationSpeed: 1,
  9421. init: function () {
  9422. this._reels = {};
  9423. },
  9424. /**@
  9425. * #.reel
  9426. * @comp SpriteAnimation
  9427. * Used to define reels, to change the active reel, and to fetch the id of the active reel.
  9428. *
  9429. * @sign public this .reel(String reelId, Duration duration, Number fromX, Number fromY, Number frameCount)
  9430. * Defines a reel by starting and ending position on the sprite sheet.
  9431. * @param reelId - ID of the animation reel being created
  9432. * @param duration - The length of the animation in milliseconds.
  9433. * @param fromX - Starting `x` position on the sprite map (x's unit is the horizontal size of the sprite in the sprite map).
  9434. * @param fromY - `y` position on the sprite map (y's unit is the horizontal size of the sprite in the sprite map). Remains constant through the animation.
  9435. * @param frameCount - The number of sequential frames in the animation. If negative, the animation will play backwards.
  9436. *
  9437. * @sign public this .reel(String reelId, Duration duration, Array frames)
  9438. * Defines a reel by an explicit list of frames
  9439. * @param reelId - ID of the animation reel being created
  9440. * @param duration - The length of the animation in milliseconds.
  9441. * @param frames - An array of arrays containing the `x` and `y` values of successive frames: [[x1,y1],[x2,y2],...] (the values are in the unit of the sprite map's width/height respectively).
  9442. *
  9443. * @sign public this .reel(String reelId)
  9444. * Switches to the specified reel. The sprite will be updated to that reel's current frame
  9445. * @param reelID - the ID to switch to
  9446. *
  9447. * @sign public Reel .reel()
  9448. * @return The id of the current reel
  9449. *
  9450. *
  9451. * A method to handle animation reels. Only works for sprites built with the Crafty.sprite methods.
  9452. * See the Tween component for animation of 2D properties.
  9453. *
  9454. * To setup an animation reel, pass the name of the reel (used to identify the reel later), and either an
  9455. * array of absolute sprite positions or the start x on the sprite map, the y on the sprite map and then the end x on the sprite map.
  9456. *
  9457. *
  9458. * @example
  9459. * ~~~
  9460. * // Define a sprite-map component
  9461. * Crafty.sprite(16, "images/sprite.png", {
  9462. * PlayerSprite: [0,0]
  9463. * });
  9464. *
  9465. * // Define an animation on the second row of the sprite map (fromY = 1)
  9466. * // from the left most sprite (fromX = 0) to the fourth sprite
  9467. * // on that row (frameCount = 4), with a duration of 1 second
  9468. * Crafty.e("2D, DOM, SpriteAnimation, PlayerSprite").reel('PlayerRunning', 1000, 0, 1, 4);
  9469. *
  9470. * // This is the same animation definition, but using the alternative method
  9471. * Crafty.e("2D, DOM, SpriteAnimation, PlayerSprite").reel('PlayerRunning', 1000, [[0, 1], [1, 1], [2, 1], [3, 1]]);
  9472. * ~~~
  9473. */
  9474. reel: function (reelId, duration, fromX, fromY, frameCount) {
  9475. // @sign public this .reel()
  9476. if (arguments.length === 0)
  9477. return this._currentReelId;
  9478. // @sign public this .reel(String reelID)
  9479. if (arguments.length === 1 && typeof reelId === "string"){
  9480. if (typeof this._reels[reelId] === "undefined")
  9481. throw("The specified reel " + reelId + " is undefined.");
  9482. this.pauseAnimation();
  9483. if (this._currentReelId !== reelId) {
  9484. this._currentReelId = reelId;
  9485. this._currentReel = this._reels[reelId];
  9486. // Change the visible sprite
  9487. this._updateSprite();
  9488. // Trigger event
  9489. this.trigger("ReelChange", this._currentReel);
  9490. }
  9491. return this;
  9492. }
  9493. var reel, i;
  9494. reel = {
  9495. id: reelId,
  9496. frames: [],
  9497. currentFrame: 0,
  9498. easing: new Crafty.easing(duration),
  9499. defaultLoops: 1
  9500. };
  9501. reel.duration = reel.easing.duration;
  9502. // @sign public this .reel(String reelId, Number duration, Number fromX, Number fromY, Number frameDuration)
  9503. if (typeof fromX === "number") {
  9504. i = fromX;
  9505. y = fromY;
  9506. if (frameCount >= 0) {
  9507. for (; i < fromX + frameCount ; i++) {
  9508. reel.frames.push([i, y]);
  9509. }
  9510. }
  9511. else {
  9512. for (; i > fromX + frameCount; i--) {
  9513. reel.frames.push([i, y]);
  9514. }
  9515. }
  9516. }
  9517. // @sign public this .reel(String reelId, Number duration, Array frames)
  9518. else if (arguments.length === 3 && typeof fromX === "object") {
  9519. reel.frames = fromX;
  9520. }
  9521. else {
  9522. throw "Urecognized arguments. Please see the documentation for 'reel(...)'.";
  9523. }
  9524. this._reels[reelId] = reel;
  9525. return this;
  9526. },
  9527. /**@
  9528. * #.animate
  9529. * @comp SpriteAnimation
  9530. * @sign public this .animate([String reelId] [, Number loopCount])
  9531. * @param reelId - ID of the animation reel to play. Defaults to the current reel if none is specified.
  9532. * @param loopCount - Number of times to repeat the animation. Use -1 to repeat indefinitely. Defaults to 1.
  9533. *
  9534. * Play one of the reels previously defined through `.reel(...)`. Simply pass the name of the reel. If you wish the
  9535. * animation to play multiple times in succession, pass in the amount of times as an additional parameter.
  9536. * To have the animation repeat indefinitely, pass in `-1`.
  9537. *
  9538. * If another animation is currently playing, it will be paused.
  9539. *
  9540. * This will always play an animation from the beginning. If you wish to resume from the current state of a reel, use `resumeAnimation()`.
  9541. *
  9542. * Once an animation ends, it will remain at its last frame.
  9543. *
  9544. *
  9545. * @example
  9546. * ~~~
  9547. * // Define a sprite-map component
  9548. * Crafty.sprite(16, "images/sprite.png", {
  9549. * PlayerSprite: [0,0]
  9550. * });
  9551. *
  9552. * // Play the animation across 20 frames (so each sprite in the 4 sprite animation should be seen for 5 frames) and repeat indefinitely
  9553. * Crafty.e("2D, DOM, SpriteAnimation, PlayerSprite")
  9554. * .reel('PlayerRunning', 20, 0, 0, 3) // setup animation
  9555. * .animate('PlayerRunning', -1); // start animation
  9556. * ~~~
  9557. */
  9558. animate: function(reelId, loopCount) {
  9559. var pos;
  9560. // switch to the specified reel if necessary
  9561. if (typeof reelId === "string")
  9562. this.reel(reelId);
  9563. var currentReel = this._currentReel;
  9564. if (typeof currentReel === "undefined" || currentReel === null)
  9565. throw("No reel is specified, and there is no currently active reel.");
  9566. this.pauseAnimation(); // This will pause the current animation, if one is playing
  9567. // Handle repeats; if loopCount is undefined and reelID is a number, calling with that signature
  9568. if (typeof loopCount === "undefined")
  9569. if (typeof reelId === "number")
  9570. loopCount = reelId;
  9571. else
  9572. loopCount = 1;
  9573. // set the animation to the beginning
  9574. currentReel.easing.reset();
  9575. // user provided loop count.
  9576. this.loops(loopCount);
  9577. // trigger the necessary events and switch to the first frame
  9578. this._setFrame(0);
  9579. // Start the anim
  9580. this.bind("EnterFrame", this._animationTick);
  9581. this._isPlaying = true;
  9582. this.trigger("StartAnimation", currentReel);
  9583. return this;
  9584. },
  9585. /**@
  9586. * #.resumeAnimation
  9587. * @comp SpriteAnimation
  9588. * @sign public this .resumeAnimation()
  9589. *
  9590. * This will resume animation of the current reel from its current state.
  9591. * If a reel is already playing, or there is no current reel, there will be no effect.
  9592. */
  9593. resumeAnimation: function() {
  9594. if (this._isPlaying === false && this._currentReel !== null) {
  9595. this.bind("EnterFrame", this._animationTick);
  9596. this._isPlaying = true;
  9597. this._currentReel.easing.resume();
  9598. this.trigger("StartAnimation", this._currentReel);
  9599. }
  9600. return this;
  9601. },
  9602. /**@
  9603. * #.pauseAnimation
  9604. * @comp SpriteAnimation
  9605. * @sign public this .pauseAnimation(void)
  9606. *
  9607. * Pauses the currently playing animation, or does nothing if no animation is playing.
  9608. */
  9609. pauseAnimation: function () {
  9610. if (this._isPlaying === true) {
  9611. this.unbind("EnterFrame", this._animationTick);
  9612. this._isPlaying = false;
  9613. this._reels[this._currentReelId].easing.pause();
  9614. }
  9615. return this;
  9616. },
  9617. /**@
  9618. * #.resetAnimation
  9619. * @comp SpriteAnimation
  9620. * @sign public this .resetAnimation()
  9621. *
  9622. * Resets the current animation to its initial state. Resets the number of loops to the last specified value, which defaults to 1.
  9623. *
  9624. * Neither pauses nor resumes the current animation.
  9625. */
  9626. resetAnimation: function(){
  9627. var currentReel = this._currentReel;
  9628. if (currentReel === null)
  9629. throw("No active reel to reset.");
  9630. this.reelPosition(0);
  9631. currentReel.easing.repeat(currentReel.defaultLoops);
  9632. return this;
  9633. },
  9634. /**@
  9635. * #.loops
  9636. * @comp SpriteAnimation
  9637. * @sign public this .loops(Number loopCount)
  9638. * @param loopCount - The number of times to play the animation
  9639. *
  9640. * Sets the number of times the animation will loop for.
  9641. * If called while an animation is in progress, the current state will be considered the first loop.
  9642. *
  9643. * @sign public Number .loops()
  9644. * @returns The number of loops left. Returns 0 if no reel is active.
  9645. */
  9646. loops: function(loopCount) {
  9647. if (arguments.length === 0){
  9648. if (this._currentReel !== null)
  9649. return this._currentReel.easing.loops;
  9650. else
  9651. return 0;
  9652. }
  9653. if (this._currentReel !== null){
  9654. if (loopCount < 0)
  9655. loopCount = Infinity;
  9656. this._currentReel.easing.repeat(loopCount);
  9657. this._currentReel.defaultLoops = loopCount;
  9658. }
  9659. return this;
  9660. },
  9661. /**@
  9662. * #.reelPosition
  9663. * @comp SpriteAnimation
  9664. *
  9665. * @sign public this .reelPosition(Integer position)
  9666. * Sets the position of the current reel by frame number.
  9667. * @param position - the frame to jump to. This is zero-indexed. A negative values counts back from the last frame.
  9668. *
  9669. * @sign public this .reelPosition(Number position)
  9670. * Sets the position of the current reel by percent progress.
  9671. * @param position - a non-integer number between 0 and 1
  9672. *
  9673. * @sign public this .reelPosition(String position)
  9674. * Jumps to the specified position. The only currently accepted value is "end", which will jump to the end of the reel.
  9675. *
  9676. * @sign public Number .reelPosition()
  9677. * @returns The current frame number
  9678. *
  9679. */
  9680. reelPosition: function(position) {
  9681. if (this._currentReel === null)
  9682. throw("No active reel.");
  9683. if (arguments.length === 0)
  9684. return this._currentReel.currentFrame;
  9685. var progress,
  9686. l = this._currentReel.frames.length;
  9687. if (position === "end")
  9688. position = l - 1;
  9689. if (position < 1 && position > 0) {
  9690. progress = position;
  9691. position = Math.floor(l * progress);
  9692. } else {
  9693. if (position !== Math.floor(position))
  9694. throw("Position " + position + " is invalid.");
  9695. if (position < 0)
  9696. position = l - 1 + position;
  9697. progress = position / l;
  9698. }
  9699. // cap to last frame
  9700. position = Math.min(position, l-1);
  9701. position = Math.max(position, 0);
  9702. this._setProgress(progress);
  9703. this._setFrame(position);
  9704. return this;
  9705. },
  9706. // Bound to "EnterFrame". Progresses the animation by dt, changing the frame if necessary.
  9707. // dt is multiplied by the animationSpeed property
  9708. _animationTick: function(frameData) {
  9709. var currentReel = this._reels[this._currentReelId];
  9710. currentReel.easing.tick(frameData.dt * this.animationSpeed);
  9711. var progress = currentReel.easing.value();
  9712. var frameNumber = Math.min( Math.floor(currentReel.frames.length * progress), currentReel.frames.length - 1);
  9713. this._setFrame(frameNumber);
  9714. if(currentReel.easing.complete === true){
  9715. this.trigger("AnimationEnd", this._currentReel);
  9716. this.pauseAnimation();
  9717. }
  9718. },
  9719. // Set the current frame and update the displayed sprite
  9720. // The actual progress for the animation must be set seperately.
  9721. _setFrame: function(frameNumber) {
  9722. var currentReel = this._currentReel;
  9723. if (frameNumber === currentReel.currentFrame)
  9724. return;
  9725. currentReel.currentFrame = frameNumber;
  9726. this._updateSprite();
  9727. this.trigger("FrameChange", currentReel);
  9728. },
  9729. // Update the displayed sprite.
  9730. _updateSprite: function() {
  9731. var currentReel = this._currentReel;
  9732. var pos = currentReel.frames[currentReel.currentFrame];
  9733. this.sprite(pos[0], pos[1]); // .sprite will trigger redraw
  9734. },
  9735. // Sets the internal state of the current reel's easing object
  9736. _setProgress: function(progress, repeats) {
  9737. this._currentReel.easing.setProgress(progress, repeats);
  9738. },
  9739. /**@
  9740. * #.isPlaying
  9741. * @comp SpriteAnimation
  9742. * @sign public Boolean .isPlaying([String reelId])
  9743. * @param reelId - The reelId of the reel we wish to examine
  9744. * @returns The current animation state
  9745. *
  9746. * Determines if the specified animation is currently playing. If no reelId is specified,
  9747. * checks if any animation is playing.
  9748. *
  9749. * @example
  9750. * ~~~
  9751. * myEntity.isPlaying() // is any animation playing
  9752. * myEntity.isPlaying('PlayerRunning') // is the PlayerRunning animation playing
  9753. * ~~~
  9754. */
  9755. isPlaying: function (reelId) {
  9756. if (!this._isPlaying) return false;
  9757. if (!reelId) return !!this._currentReelId;
  9758. return this._currentReelId === reelId;
  9759. },
  9760. /**@
  9761. * #.getReel
  9762. * @comp SpriteAnimation
  9763. * @sign public Reel .getReel()
  9764. * @returns The current reel, or null if there is no active reel
  9765. *
  9766. * @sign public Reel .getReel(reelId)
  9767. * @param reelId - The id of the reel to fetch.
  9768. * @returns The specified reel, or `undefined` if no such reel exists.
  9769. *
  9770. */
  9771. getReel: function (reelId) {
  9772. if (arguments.length === 0){
  9773. if (!this._currentReelId) return null;
  9774. reelId = this._currentReelId;
  9775. }
  9776. return this._reels[reelId];
  9777. }
  9778. });
  9779. },{"./animation.js":5,"./core.js":9}],24:[function(require,module,exports){
  9780. var Crafty = require('./core.js'),
  9781. document = window.document;
  9782. Crafty.extend({
  9783. /**@
  9784. * #Crafty.sprite
  9785. * @category Graphics
  9786. * @sign public this Crafty.sprite([Number tile, [Number tileh]], String url, Object map[, Number paddingX[, Number paddingY[, Boolean paddingAroundBorder]]])
  9787. * @param tile - Tile size of the sprite map, defaults to 1
  9788. * @param tileh - Height of the tile; if provided, tile is interpreted as the width
  9789. * @param url - URL of the sprite image
  9790. * @param map - Object where the key is what becomes a new component and the value points to a position on the sprite map
  9791. * @param paddingX - Horizontal space in between tiles. Defaults to 0.
  9792. * @param paddingY - Vertical space in between tiles. Defaults to paddingX.
  9793. * @param paddingAroundBorder - If padding should be applied around the border of the sprite sheet. If enabled the first tile starts at (paddingX,paddingY) instead of (0,0). Defaults to false.
  9794. * Generates components based on positions in a sprite image to be applied to entities.
  9795. *
  9796. * Accepts a tile size, URL and map for the name of the sprite and its position.
  9797. *
  9798. * The position must be an array containing the position of the sprite where index `0`
  9799. * is the `x` position, `1` is the `y` position and optionally `2` is the width and `3`
  9800. * is the height. If the sprite map has padding, pass the values for the `x` padding
  9801. * or `y` padding. If they are the same, just add one value.
  9802. *
  9803. * If the sprite image has no consistent tile size, `1` or no argument need be
  9804. * passed for tile size.
  9805. *
  9806. * Entities that add the generated components are also given the `2D` component, and
  9807. * a component called `Sprite`.
  9808. *
  9809. * @example
  9810. * ~~~
  9811. * Crafty.sprite("imgs/spritemap6.png", {flower:[0,0,20,30]});
  9812. * var flower_entity = Crafty.e("2D, DOM, flower");
  9813. * ~~~
  9814. * The first line creates a component called `flower` associated with the sub-image of
  9815. * spritemap6.png with top-left corner (0,0), width 20 pixels, and height 30 pixels.
  9816. * The second line creates an entity with that image. (Note: The `2D` is not really
  9817. * necessary here, because adding the `flower` component automatically also adds the
  9818. * `2D` component.)
  9819. * ~~~
  9820. * Crafty.sprite(50, "imgs/spritemap6.png", {flower:[0,0], grass:[0,1,3,1]});
  9821. * ~~~
  9822. * In this case, the `flower` component is pixels 0 <= x < 50, 0 <= y < 50, and the
  9823. * `grass` component is pixels 0 <= x < 150, 50 <= y < 100. (The `3` means grass has a
  9824. * width of 3 tiles, i.e. 150 pixels.)
  9825. * ~~~
  9826. * Crafty.sprite(50, 100, "imgs/spritemap6.png", {flower:[0,0], grass:[0,1]}, 10);
  9827. * ~~~
  9828. * In this case, each tile is 50x100, and there is a spacing of 10 pixels between
  9829. * consecutive tiles. So `flower` is pixels 0 <= x < 50, 0 <= y < 100, and `grass` is
  9830. * pixels 0 <= x < 50, 110 <= y < 210.
  9831. *
  9832. * @see Sprite
  9833. */
  9834. sprite: function (tile, tileh, url, map, paddingX, paddingY, paddingAroundBorder) {
  9835. var spriteName, temp, x, y, w, h, img;
  9836. //if no tile value, default to 1.
  9837. //(if the first passed argument is a string, it must be the url.)
  9838. if (typeof tile === "string") {
  9839. paddingY = paddingX;
  9840. paddingX = map;
  9841. map = tileh;
  9842. url = tile;
  9843. tile = 1;
  9844. tileh = 1;
  9845. }
  9846. if (typeof tileh == "string") {
  9847. paddingY = paddingX;
  9848. paddingX = map;
  9849. map = url;
  9850. url = tileh;
  9851. tileh = tile;
  9852. }
  9853. //if no paddingY, use paddingX
  9854. if (!paddingY && paddingX) paddingY = paddingX;
  9855. paddingX = parseInt(paddingX || 0, 10); //just incase
  9856. paddingY = parseInt(paddingY || 0, 10);
  9857. var markSpritesReady = function() {
  9858. this.ready = true;
  9859. this.trigger("Invalidate");
  9860. };
  9861. img = Crafty.asset(url);
  9862. if (!img) {
  9863. img = new Image();
  9864. img.src = url;
  9865. Crafty.asset(url, img);
  9866. img.onload = function () {
  9867. //all components with this img are now ready
  9868. for (var spriteName in map) {
  9869. Crafty(spriteName).each(markSpritesReady);
  9870. }
  9871. };
  9872. }
  9873. var sharedSpriteInit = function() {
  9874. this.requires("2D, Sprite");
  9875. this.__trim = [0, 0, 0, 0];
  9876. this.__image = url;
  9877. this.__coord = [this.__coord[0], this.__coord[1], this.__coord[2], this.__coord[3]];
  9878. this.__tile = tile;
  9879. this.__tileh = tileh;
  9880. this.__padding = [paddingX, paddingY];
  9881. this.__padBorder = paddingAroundBorder;
  9882. this.sprite(this.__coord[0], this.__coord[1], this.__coord[2], this.__coord[3]);
  9883. this.img = img;
  9884. //draw now
  9885. if (this.img.complete && this.img.width > 0) {
  9886. this.ready = true;
  9887. this.trigger("Invalidate");
  9888. }
  9889. //set the width and height to the sprite size
  9890. this.w = this.__coord[2];
  9891. this.h = this.__coord[3];
  9892. };
  9893. for (spriteName in map) {
  9894. if (!map.hasOwnProperty(spriteName)) continue;
  9895. temp = map[spriteName];
  9896. //generates sprite components for each tile in the map
  9897. Crafty.c(spriteName, {
  9898. ready: false,
  9899. __coord: [temp[0], temp[1], temp[2] || 1, temp[3] || 1],
  9900. init: sharedSpriteInit
  9901. });
  9902. }
  9903. return this;
  9904. }
  9905. });
  9906. /**@
  9907. * #Sprite
  9908. * @category Graphics
  9909. * @trigger Invalidate - when the sprites change
  9910. * Component for using tiles in a sprite map.
  9911. */
  9912. Crafty.c("Sprite", {
  9913. __image: '',
  9914. /*
  9915. * #.__tile
  9916. * @comp Sprite
  9917. *
  9918. * Horizontal sprite tile size.
  9919. */
  9920. __tile: 0,
  9921. /*
  9922. * #.__tileh
  9923. * @comp Sprite
  9924. *
  9925. * Vertical sprite tile size.
  9926. */
  9927. __tileh: 0,
  9928. __padding: null,
  9929. __trim: null,
  9930. img: null,
  9931. //ready is changed to true in Crafty.sprite
  9932. ready: false,
  9933. init: function () {
  9934. this.__trim = [0, 0, 0, 0];
  9935. var draw = function (e) {
  9936. var co = e.co,
  9937. pos = e.pos,
  9938. context = e.ctx;
  9939. if (e.type === "canvas") {
  9940. //draw the image on the canvas element
  9941. context.drawImage(this.img, //image element
  9942. co.x, //x position on sprite
  9943. co.y, //y position on sprite
  9944. co.w, //width on sprite
  9945. co.h, //height on sprite
  9946. pos._x, //x position on canvas
  9947. pos._y, //y position on canvas
  9948. pos._w, //width on canvas
  9949. pos._h //height on canvas
  9950. );
  9951. } else if (e.type === "DOM") {
  9952. // Get scale (ratio of entity dimensions to sprite's dimensions)
  9953. // If needed, we will scale up the entire sprite sheet, and then modify the position accordingly
  9954. var vscale = this._h / co.h,
  9955. hscale = this._w / co.w,
  9956. style = this._element.style;
  9957. style.background = style.backgroundColor + " url('" + this.__image + "') no-repeat";
  9958. style.backgroundPosition = "-" + co.x * hscale + "px -" + co.y * vscale + "px";
  9959. // style.backgroundSize must be set AFTER style.background!
  9960. if (vscale != 1 || hscale != 1) {
  9961. style.backgroundSize = (this.img.width * hscale) + "px" + " " + (this.img.height * vscale) + "px";
  9962. }
  9963. }
  9964. };
  9965. this.bind("Draw", draw).bind("RemoveComponent", function (id) {
  9966. if (id === "Sprite") this.unbind("Draw", draw);
  9967. });
  9968. },
  9969. /**@
  9970. * #.sprite
  9971. * @comp Sprite
  9972. * @sign public this .sprite(Number x, Number y[, Number w, Number h])
  9973. * @param x - X cell position
  9974. * @param y - Y cell position
  9975. * @param w - Width in cells. Optional.
  9976. * @param h - Height in cells. Optional.
  9977. *
  9978. * Uses a new location on the sprite map as its sprite. If w or h are ommitted, the width and height are not changed.
  9979. *
  9980. * Values should be in tiles or cells (not pixels).
  9981. *
  9982. * @example
  9983. * ~~~
  9984. * Crafty.e("2D, DOM, Sprite")
  9985. * .sprite(0, 0, 2, 2);
  9986. * ~~~
  9987. */
  9988. /**@
  9989. * #.__coord
  9990. * @comp Sprite
  9991. *
  9992. * The coordinate of the slide within the sprite in the format of [x, y, w, h].
  9993. */
  9994. sprite: function (x, y, w, h) {
  9995. this.__coord = this.__coord || [0, 0, 0, 0];
  9996. this.__coord[0] = x * (this.__tile + this.__padding[0]) + (this.__padBorder ? this.__padding[0] : 0) + this.__trim[0];
  9997. this.__coord[1] = y * (this.__tileh + this.__padding[1]) + (this.__padBorder ? this.__padding[1] : 0) + this.__trim[1];
  9998. if (typeof(w)!=='undefined' && typeof(h)!=='undefined') {
  9999. this.__coord[2] = this.__trim[2] || w * this.__tile || this.__tile;
  10000. this.__coord[3] = this.__trim[3] || h * this.__tileh || this.__tileh;
  10001. }
  10002. this.trigger("Invalidate");
  10003. return this;
  10004. },
  10005. /**@
  10006. * #.crop
  10007. * @comp Sprite
  10008. * @sign public this .crop(Number x, Number y, Number w, Number h)
  10009. * @param x - Offset x position
  10010. * @param y - Offset y position
  10011. * @param w - New width
  10012. * @param h - New height
  10013. *
  10014. * If the entity needs to be smaller than the tile size, use this method to crop it.
  10015. *
  10016. * The values should be in pixels rather than tiles.
  10017. *
  10018. * @example
  10019. * ~~~
  10020. * Crafty.e("2D, DOM, Sprite")
  10021. * .crop(40, 40, 22, 23);
  10022. * ~~~
  10023. */
  10024. crop: function (x, y, w, h) {
  10025. var old = this._mbr || this.pos();
  10026. this.__trim = [];
  10027. this.__trim[0] = x;
  10028. this.__trim[1] = y;
  10029. this.__trim[2] = w;
  10030. this.__trim[3] = h;
  10031. this.__coord[0] += x;
  10032. this.__coord[1] += y;
  10033. this.__coord[2] = w;
  10034. this.__coord[3] = h;
  10035. this._w = w;
  10036. this._h = h;
  10037. this.trigger("Invalidate", old);
  10038. return this;
  10039. }
  10040. });
  10041. },{"./core.js":9}],25:[function(require,module,exports){
  10042. var Crafty = require('./core.js'),
  10043. document = window.document;
  10044. /**@
  10045. * #Storage
  10046. * @category Utilities
  10047. * Very simple way to get and set values, which will persist when the browser is closed also.
  10048. */
  10049. /**@
  10050. * #.storage
  10051. * @comp Storage
  10052. * @sign .storage(String key)
  10053. * @param key - a key you would like to get from the storage. It will return null if the key does not exists.
  10054. * @sign .storage(String key, String value)
  10055. * @param key - the key you would like to save the data under.
  10056. * @param value - the value you would like to save.
  10057. * @sign .storage(String key, [Object value, Array value, Boolean value])
  10058. * @param key - the key you would like to save the data under.
  10059. * @param value - the value you would like to save, can be an Object or an Array.
  10060. *
  10061. * Storage function is very simple and can be used to either get or set values.
  10062. * You can store both booleans, strings, objects and arrays.
  10063. *
  10064. * Please note: You should not store data, while the game is playing, as it can cause the game to slow down. You should load data when you start the game, or when the user for an example click a "Save gameprocess" button.
  10065. *
  10066. * @example
  10067. * Get an already stored value
  10068. * ~~~
  10069. * var playername = Crafty.storage('playername');
  10070. * ~~~
  10071. *
  10072. * @example
  10073. * Save a value
  10074. * ~~~
  10075. * Crafty.storage('playername', 'Hero');
  10076. * ~~~
  10077. *
  10078. * @example
  10079. * Test to see if a value is already there.
  10080. * ~~~
  10081. * var heroname = Crafty.storage('name');
  10082. * if(!heroname){
  10083. * // Maybe ask the player what their name is here
  10084. * heroname = 'Guest';
  10085. * }
  10086. * // Do something with heroname
  10087. * ~~~
  10088. */
  10089. Crafty.storage = function(key, value){
  10090. var storage = window.localStorage,
  10091. _value = value;
  10092. if(!storage){
  10093. return false;
  10094. }
  10095. if(arguments.length === 1) {
  10096. try {
  10097. return JSON.parse(storage.getItem(key));
  10098. }
  10099. catch (e) {
  10100. return storage.getItem(key);
  10101. }
  10102. } else {
  10103. if(typeof value === "object") {
  10104. _value = JSON.stringify(value);
  10105. }
  10106. storage.setItem(key, _value);
  10107. }
  10108. };
  10109. /**@
  10110. * #.storage.remove
  10111. * @comp Storage
  10112. * @sign .storage.remove(String key)
  10113. * @param key - a key where you will like to delete the value of.
  10114. *
  10115. * Generally you do not need to remove values from localStorage, but if you do
  10116. * store large amount of text, or want to unset something you can do that with
  10117. * this function.
  10118. *
  10119. * @example
  10120. * Get an already stored value
  10121. * ~~~
  10122. * Crafty.storage.remove('playername');
  10123. * ~~~
  10124. *
  10125. */
  10126. Crafty.storage.remove = function(key){
  10127. window.localStorage.removeItem(key);
  10128. };
  10129. },{"./core.js":9}],26:[function(require,module,exports){
  10130. var Crafty = require('./core.js'),
  10131. document = window.document;
  10132. /**@
  10133. * #Text
  10134. * @category Graphics
  10135. * @trigger Invalidate - when the text is changed
  10136. * @requires Canvas or DOM
  10137. * Component to make a text entity.
  10138. *
  10139. * By default, text will have the style "10px sans-serif".
  10140. *
  10141. * Note 1: An entity with the text component is just text! If you want to write text
  10142. * inside an image, you need one entity for the text and another entity for the image.
  10143. * More tips for writing text inside an image: (1) Use the z-index (from 2D component)
  10144. * to ensure that the text is on top of the image, not the other way around; (2)
  10145. * use .attach() (from 2D component) to glue the text to the image so they move and
  10146. * rotate together.
  10147. *
  10148. * Note 2: For DOM (but not canvas) text entities, various font settings (like
  10149. * text-decoration and text-align) can be set using `.css()` (see DOM component). But
  10150. * you cannot use `.css()` to set the properties which are controlled by `.textFont()`
  10151. * or `.textColor()` -- the settings will be ignored.
  10152. *
  10153. * Note 3: If you use canvas text with glyphs that are taller than standard letters, portions of the glyphs might be cut off.
  10154. */
  10155. Crafty.c("Text", {
  10156. _text: "",
  10157. defaultSize: "10px",
  10158. defaultFamily: "sans-serif",
  10159. defaultVariant: "normal",
  10160. defaultLineHeight: "normal",
  10161. ready: true,
  10162. init: function () {
  10163. this.requires("2D");
  10164. this._textFont = {
  10165. "type": "",
  10166. "weight": "",
  10167. "size": this.defaultSize,
  10168. "lineHeight":this.defaultLineHeight,
  10169. "family": this.defaultFamily,
  10170. "variant": this.defaultVariant
  10171. };
  10172. this.bind("Draw", function (e) {
  10173. var font = this._fontString();
  10174. if (e.type === "DOM") {
  10175. var el = this._element,
  10176. style = el.style;
  10177. style.color = this._textColor;
  10178. style.font = font;
  10179. el.innerHTML = this._text;
  10180. } else if (e.type === "canvas") {
  10181. var context = e.ctx;
  10182. context.save();
  10183. context.textBaseline = "top";
  10184. context.fillStyle = this._textColor || "rgb(0,0,0)";
  10185. context.font = font;
  10186. context.fillText(this._text, this._x, this._y);
  10187. context.restore();
  10188. }
  10189. });
  10190. },
  10191. // takes a CSS font-size string and gets the height of the resulting font in px
  10192. _getFontHeight: (function(){
  10193. // regex for grabbing the first string of letters
  10194. var re = /([a-zA-Z]+)\b/;
  10195. // From the CSS spec. "em" and "ex" are undefined on a canvas.
  10196. var multipliers = {
  10197. "px": 1,
  10198. "pt": 4/3,
  10199. "pc": 16,
  10200. "cm": 96/2.54,
  10201. "mm": 96/25.4,
  10202. "in": 96,
  10203. "em": undefined,
  10204. "ex": undefined
  10205. };
  10206. return function (font){
  10207. var number = parseFloat(font);
  10208. var match = re.exec(font);
  10209. var unit = match ? match[1] : "px";
  10210. if (multipliers[unit] !== undefined)
  10211. return Math.ceil(number * multipliers[unit]);
  10212. else
  10213. return Math.ceil(number);
  10214. };
  10215. })(),
  10216. /**@
  10217. * #.text
  10218. * @comp Text
  10219. * @sign public this .text(String text)
  10220. * @sign public this .text(Function textgenerator)
  10221. * @param text - String of text that will be inserted into the DOM or Canvas element.
  10222. *
  10223. * This method will update the text inside the entity.
  10224. *
  10225. * If you need to reference attributes on the entity itself you can pass a function instead of a string.
  10226. *
  10227. * @example
  10228. * ~~~
  10229. * Crafty.e("2D, DOM, Text").attr({ x: 100, y: 100 }).text("Look at me!!");
  10230. *
  10231. * Crafty.e("2D, DOM, Text").attr({ x: 100, y: 100 })
  10232. * .text(function () { return "My position is " + this._x });
  10233. *
  10234. * Crafty.e("2D, Canvas, Text").attr({ x: 100, y: 100 }).text("Look at me!!");
  10235. *
  10236. * Crafty.e("2D, Canvas, Text").attr({ x: 100, y: 100 })
  10237. * .text(function () { return "My position is " + this._x });
  10238. * ~~~
  10239. */
  10240. text: function (text) {
  10241. if (!(typeof text !== "undefined" && text !== null)) return this._text;
  10242. if (typeof (text) == "function")
  10243. this._text = text.call(this);
  10244. else
  10245. this._text = text;
  10246. if (this.has("Canvas") )
  10247. this._resizeForCanvas();
  10248. this.trigger("Invalidate");
  10249. return this;
  10250. },
  10251. // Calculates the height and width of text on the canvas
  10252. // Width is found by using the canvas measureText function
  10253. // Height is only estimated -- it calculates the font size in pixels, and sets the height to 110% of that.
  10254. _resizeForCanvas: function(){
  10255. var ctx = Crafty.canvas.context;
  10256. ctx.font = this._fontString();
  10257. this.w = ctx.measureText(this._text).width;
  10258. var size = (this._textFont.size || this.defaultSize);
  10259. this.h = 1.1 * this._getFontHeight(size);
  10260. },
  10261. // Returns the font string to use
  10262. _fontString: function(){
  10263. return this._textFont.type + ' ' + this._textFont.variant + ' ' + this._textFont.weight + ' ' + this._textFont.size + ' / ' + this._textFont.lineHeight + ' ' + this._textFont.family;
  10264. },
  10265. /**@
  10266. * #.textColor
  10267. * @comp Text
  10268. * @sign public this .textColor(String color, Number strength)
  10269. * @param color - The color in hexadecimal
  10270. * @param strength - Level of opacity
  10271. *
  10272. * Modify the text color and level of opacity.
  10273. *
  10274. * @example
  10275. * ~~~
  10276. * Crafty.e("2D, DOM, Text").attr({ x: 100, y: 100 }).text("Look at me!!")
  10277. * .textColor('#FF0000');
  10278. *
  10279. * Crafty.e("2D, Canvas, Text").attr({ x: 100, y: 100 }).text('Look at me!!')
  10280. * .textColor('#FF0000', 0.6);
  10281. * ~~~
  10282. * @see Crafty.toRGB
  10283. */
  10284. textColor: function (color, strength) {
  10285. this._strength = strength;
  10286. this._textColor = Crafty.toRGB(color, this._strength);
  10287. this.trigger("Invalidate");
  10288. return this;
  10289. },
  10290. /**@
  10291. * #.textFont
  10292. * @comp Text
  10293. * @triggers Invalidate
  10294. * @sign public this .textFont(String key, * value)
  10295. * @param key - Property of the entity to modify
  10296. * @param value - Value to set the property to
  10297. *
  10298. * @sign public this .textFont(Object map)
  10299. * @param map - Object where the key is the property to modify and the value as the property value
  10300. *
  10301. * Use this method to set font property of the text entity. Possible values are: type, weight, size, family, lineHeight, and variant.
  10302. *
  10303. * When rendered by the canvas, lineHeight and variant will be ignored.
  10304. *
  10305. * @example
  10306. * ~~~
  10307. * Crafty.e("2D, DOM, Text").textFont({ type: 'italic', family: 'Arial' });
  10308. * Crafty.e("2D, Canvas, Text").textFont({ size: '20px', weight: 'bold' });
  10309. *
  10310. * Crafty.e("2D, Canvas, Text").textFont("type", "italic");
  10311. * Crafty.e("2D, Canvas, Text").textFont("type"); // italic
  10312. * ~~~
  10313. */
  10314. textFont: function (key, value) {
  10315. if (arguments.length === 1) {
  10316. //if just the key, return the value
  10317. if (typeof key === "string") {
  10318. return this._textFont[key];
  10319. }
  10320. if (typeof key === "object") {
  10321. for (var propertyKey in key) {
  10322. if(propertyKey == 'family'){
  10323. this._textFont[propertyKey] = "'" + key[propertyKey] + "'";
  10324. } else {
  10325. this._textFont[propertyKey] = key[propertyKey];
  10326. }
  10327. }
  10328. }
  10329. } else {
  10330. this._textFont[key] = value;
  10331. }
  10332. if (this.has("Canvas") )
  10333. this._resizeForCanvas();
  10334. this.trigger("Invalidate");
  10335. return this;
  10336. },
  10337. /**@
  10338. * #.unselectable
  10339. * @comp Text
  10340. * @triggers Invalidate
  10341. * @sign public this .unselectable()
  10342. *
  10343. * This method sets the text so that it cannot be selected (highlighted) by dragging.
  10344. * (Canvas text can never be highlighted, so this only matters for DOM text.)
  10345. * Works by changing the css property "user-select" and its variants.
  10346. *
  10347. * @example
  10348. * ~~~
  10349. * Crafty.e("2D, DOM, Text").text('This text cannot be highlighted!').unselectable();
  10350. * ~~~
  10351. */
  10352. unselectable: function () {
  10353. // http://stackoverflow.com/questions/826782/css-rule-to-disable-text-selection-highlighting
  10354. if (this.has("DOM")) {
  10355. this.css({
  10356. '-webkit-touch-callout': 'none',
  10357. '-webkit-user-select': 'none',
  10358. '-khtml-user-select': 'none',
  10359. '-moz-user-select': 'none',
  10360. '-ms-user-select': 'none',
  10361. 'user-select': 'none'
  10362. });
  10363. this.trigger("Invalidate");
  10364. }
  10365. return this;
  10366. }
  10367. });
  10368. },{"./core.js":9}],27:[function(require,module,exports){
  10369. var Crafty = require('./core.js'),
  10370. document = window.document;
  10371. /**@
  10372. * #Delay
  10373. * @category Utilities
  10374. */
  10375. Crafty.c("Delay", {
  10376. init: function () {
  10377. this._delays = [];
  10378. this.bind("EnterFrame", function () {
  10379. var now = new Date().getTime();
  10380. var index = this._delays.length;
  10381. while (--index >= 0) {
  10382. var item = this._delays[index];
  10383. if (item.start + item.delay + item.pause < now) {
  10384. item.func.call(this);
  10385. if (item.repeat > 0) {
  10386. // reschedule item
  10387. item.start = now;
  10388. item.pause = 0;
  10389. item.pauseBuffer = 0;
  10390. item.repeat--;
  10391. } else if (item.repeat <= 0) {
  10392. // remove item from array
  10393. this._delays.splice(index, 1);
  10394. }
  10395. }
  10396. }
  10397. });
  10398. this.bind("Pause", function () {
  10399. var now = new Date().getTime();
  10400. for (var index in this._delays) {
  10401. this._delays[index].pauseBuffer = now;
  10402. }
  10403. });
  10404. this.bind("Unpause", function () {
  10405. var now = new Date().getTime();
  10406. for (var index in this._delays) {
  10407. var item = this._delays[index];
  10408. item.pause += now - item.pauseBuffer;
  10409. }
  10410. });
  10411. },
  10412. /**@
  10413. * #.delay
  10414. * @comp Delay
  10415. * @sign public this.delay(Function callback, Number delay)
  10416. * @param callback - Method to execute after given amount of milliseconds
  10417. * @param delay - Amount of milliseconds to execute the method
  10418. * @param repeat - How often to repeat the delayed function. A value of 0 triggers the delayed
  10419. * function exactly once. A value n > 0 triggers the delayed function exactly n+1 times. A
  10420. * value of -1 triggers the delayed function indefinitely.
  10421. *
  10422. * The delay method will execute a function after a given amount of time in milliseconds.
  10423. *
  10424. * It is not a wrapper for `setTimeout`.
  10425. *
  10426. * If Crafty is paused, the delay is interrupted with the pause and then resume when unpaused
  10427. *
  10428. * If the entity is destroyed, the delay is also destroyed and will not have effect.
  10429. *
  10430. * @example
  10431. * ~~~
  10432. * console.log("start");
  10433. * Crafty.e("Delay").delay(function() {
  10434. * console.log("100ms later");
  10435. * }, 100, 0);
  10436. * ~~~
  10437. */
  10438. delay: function (func, delay, repeat) {
  10439. this._delays.push({
  10440. start: new Date().getTime(),
  10441. func: func,
  10442. delay: delay,
  10443. repeat: (repeat < 0 ? Infinity : repeat) || 0,
  10444. pauseBuffer: 0,
  10445. pause: 0
  10446. });
  10447. return this;
  10448. }
  10449. });
  10450. },{"./core.js":9}],28:[function(require,module,exports){
  10451. module.exports = "0.6.2";
  10452. },{}],29:[function(require,module,exports){
  10453. var Crafty = require('./core.js'),
  10454. document = window.document;
  10455. Crafty.extend({
  10456. /**@
  10457. * #Crafty.viewport
  10458. * @category Stage
  10459. * @trigger ViewportScroll - when the viewport's x or y coordinates change
  10460. * @trigger ViewportScale - when the viewport's scale changes
  10461. * @trigger ViewportResize - when the viewport's dimension's change
  10462. * @trigger InvalidateViewport - when the viewport changes
  10463. * @trigger StopCamera - when any camera animations should stop, such as at the start of a new animation.
  10464. * @trigger CameraAnimationDone - when a camera animation comes reaches completion
  10465. *
  10466. * Viewport is essentially a 2D camera looking at the stage. Can be moved or zoomed, which
  10467. * in turn will react just like a camera moving in that direction.
  10468. *
  10469. * Tip: At any given moment, the stuff that you can see is...
  10470. *
  10471. * `x` between `(-Crafty.viewport._x)` and `(-Crafty.viewport._x + (Crafty.viewport._width / Crafty.viewport._scale))`
  10472. *
  10473. * `y` between `(-Crafty.viewport._y)` and `(-Crafty.viewport._y + (Crafty.viewport._height / Crafty.viewport._scale))`
  10474. */
  10475. viewport: {
  10476. /**@
  10477. * #Crafty.viewport.clampToEntities
  10478. * @comp Crafty.viewport
  10479. *
  10480. * Decides if the viewport functions should clamp to game entities.
  10481. * When set to `true` functions such as Crafty.viewport.mouselook() will not allow you to move the
  10482. * viewport over areas of the game that has no entities.
  10483. * For development it can be useful to set this to false.
  10484. */
  10485. clampToEntities: true,
  10486. _width: 0,
  10487. _height: 0,
  10488. /**@
  10489. * #Crafty.viewport.x
  10490. * @comp Crafty.viewport
  10491. *
  10492. * Will move the stage and therefore every visible entity along the `x`
  10493. * axis in the opposite direction.
  10494. *
  10495. * When this value is set, it will shift the entire stage. This means that entity
  10496. * positions are not exactly where they are on screen. To get the exact position,
  10497. * simply add `Crafty.viewport.x` onto the entities `x` position.
  10498. */
  10499. _x: 0,
  10500. /**@
  10501. * #Crafty.viewport.y
  10502. * @comp Crafty.viewport
  10503. *
  10504. * Will move the stage and therefore every visible entity along the `y`
  10505. * axis in the opposite direction.
  10506. *
  10507. * When this value is set, it will shift the entire stage. This means that entity
  10508. * positions are not exactly where they are on screen. To get the exact position,
  10509. * simply add `Crafty.viewport.y` onto the entities `y` position.
  10510. */
  10511. _y: 0,
  10512. /**@
  10513. * #Crafty.viewport._scale
  10514. * @comp Crafty.viewport
  10515. *
  10516. * This value is the current scale (zoom) of the viewport. When the value is bigger than 1, everything
  10517. * looks bigger (zoomed in). When the value is less than 1, everything looks smaller (zoomed out). This
  10518. * does not alter the size of the stage itself, just the magnification of what it shows.
  10519. *
  10520. * This is a read-only property: Do not set it directly. Instead, use `Crafty.viewport.scale(...)`
  10521. * or `Crafty.viewport.zoom(...)`
  10522. */
  10523. _scale: 1,
  10524. /**@
  10525. * #Crafty.viewport.bounds
  10526. * @comp Crafty.viewport
  10527. *
  10528. * A rectangle which defines the bounds of the viewport.
  10529. * It should be an object with two properties, `max` and `min`,
  10530. * which are each an object with `x` and `y` properties.
  10531. *
  10532. * If this property is null, Crafty uses the bounding box of all the items
  10533. * on the stage. This is the initial value. (To prevent this behavior, set `Crafty.viewport.clampToEntities` to `false`)
  10534. *
  10535. * If you wish to bound the viewport along one axis but not the other, you can use `-Infinity` and `+Infinity` as bounds.
  10536. *
  10537. * @see Crafty.viewport.clampToEntities
  10538. *
  10539. * @example
  10540. * Set the bounds to a 500 by 500 square:
  10541. *
  10542. * ~~~
  10543. * Crafty.viewport.bounds = {min:{x:0, y:0}, max:{x:500, y:500}};
  10544. * ~~~
  10545. */
  10546. bounds: null,
  10547. /**@
  10548. * #Crafty.viewport.scroll
  10549. * @comp Crafty.viewport
  10550. * @sign Crafty.viewport.scroll(String axis, Number val)
  10551. * @param axis - 'x' or 'y'
  10552. * @param val - The new absolute position on the axis
  10553. *
  10554. * Will move the viewport to the position given on the specified axis
  10555. *
  10556. * @example
  10557. * Will move the camera 500 pixels right of its initial position, in effect
  10558. * shifting everything in the viewport 500 pixels to the left.
  10559. *
  10560. * ~~~
  10561. * Crafty.viewport.scroll('_x', 500);
  10562. * ~~~
  10563. */
  10564. scroll: function (axis, val) {
  10565. this[axis] = val;
  10566. Crafty.trigger("ViewportScroll");
  10567. Crafty.trigger("InvalidateViewport");
  10568. },
  10569. rect_object: { _x: 0, _y: 0, _w: 0, _h: 0},
  10570. rect: function () {
  10571. this.rect_object._x = -this._x;
  10572. this.rect_object._y = -this._y;
  10573. this.rect_object._w = this._width / this._scale;
  10574. this.rect_object._h = this._height / this._scale;
  10575. return this.rect_object;
  10576. },
  10577. /**@
  10578. * #Crafty.viewport.pan
  10579. * @comp Crafty.viewport
  10580. * @sign public void Crafty.viewport.pan(String axis, Number v, Number time)
  10581. * @param String axis - 'x' or 'y'. The axis to move the camera on
  10582. * @param Number v - the distance to move the camera by
  10583. * @param Number time - The duration in ms for the entire camera movement
  10584. *
  10585. * Pans the camera a given number of pixels over the specified time
  10586. */
  10587. pan: (function () {
  10588. var tweens = {}, i, bound = false;
  10589. var targetX, targetY, startingX, startingY, easing;
  10590. function enterFrame(e) {
  10591. easing.tick(e.dt);
  10592. var v = easing.value();
  10593. Crafty.viewport.x = (1-v) * startingX + v * targetX;
  10594. Crafty.viewport.y = (1-v) * startingY + v * targetY;
  10595. Crafty.viewport._clamp();
  10596. if (easing.complete){
  10597. stopPan();
  10598. Crafty.trigger("CameraAnimationDone");
  10599. }
  10600. }
  10601. function stopPan(){
  10602. Crafty.unbind("EnterFrame", enterFrame);
  10603. }
  10604. Crafty.bind("StopCamera", stopPan);
  10605. return function (dx, dy, time) {
  10606. // Cancel any current camera control
  10607. Crafty.trigger("StopCamera");
  10608. // Handle request to reset
  10609. if (dx == 'reset') {
  10610. return;
  10611. }
  10612. startingX = Crafty.viewport._x;
  10613. startingY = Crafty.viewport._y;
  10614. targetX = startingX - dx;
  10615. targetY = startingY - dy;
  10616. easing = new Crafty.easing(time);
  10617. // bind to event, using uniqueBind prevents multiple copies from being bound
  10618. Crafty.uniqueBind("EnterFrame", enterFrame);
  10619. };
  10620. })(),
  10621. /**@
  10622. * #Crafty.viewport.follow
  10623. * @comp Crafty.viewport
  10624. * @sign public void Crafty.viewport.follow(Object target, Number offsetx, Number offsety)
  10625. * @param Object target - An entity with the 2D component
  10626. * @param Number offsetx - Follow target should be offsetx pixels away from center
  10627. * @param Number offsety - Positive puts target to the right of center
  10628. *
  10629. * Follows a given entity with the 2D component. If following target will take a portion of
  10630. * the viewport out of bounds of the world, following will stop until the target moves away.
  10631. *
  10632. * @example
  10633. * ~~~
  10634. * var ent = Crafty.e('2D, DOM').attr({w: 100, h: 100:});
  10635. * Crafty.viewport.follow(ent, 0, 0);
  10636. * ~~~
  10637. */
  10638. follow: (function () {
  10639. var oldTarget, offx, offy;
  10640. function change() {
  10641. Crafty.viewport.scroll('_x', -(this.x + (this.w / 2) - (Crafty.viewport.width / 2) - offx));
  10642. Crafty.viewport.scroll('_y', -(this.y + (this.h / 2) - (Crafty.viewport.height / 2) - offy));
  10643. Crafty.viewport._clamp();
  10644. }
  10645. function stopFollow(){
  10646. if (oldTarget)
  10647. oldTarget.unbind('Move', change);
  10648. }
  10649. Crafty.bind("StopCamera", stopFollow);
  10650. return function (target, offsetx, offsety) {
  10651. if (!target || !target.has('2D'))
  10652. return;
  10653. Crafty.trigger("StopCamera");
  10654. oldTarget = target;
  10655. offx = (typeof offsetx != 'undefined') ? offsetx : 0;
  10656. offy = (typeof offsety != 'undefined') ? offsety : 0;
  10657. target.bind('Move', change);
  10658. change.call(target);
  10659. };
  10660. })(),
  10661. /**@
  10662. * #Crafty.viewport.centerOn
  10663. * @comp Crafty.viewport
  10664. * @sign public void Crafty.viewport.centerOn(Object target, Number time)
  10665. * @param Object target - An entity with the 2D component
  10666. * @param Number time - The duration in ms of the camera motion
  10667. *
  10668. * Centers the viewport on the given entity.
  10669. */
  10670. centerOn: function (targ, time) {
  10671. var x = targ.x + Crafty.viewport.x,
  10672. y = targ.y + Crafty.viewport.y,
  10673. mid_x = targ.w / 2,
  10674. mid_y = targ.h / 2,
  10675. cent_x = Crafty.viewport.width / 2,
  10676. cent_y = Crafty.viewport.height / 2,
  10677. new_x = x + mid_x - cent_x,
  10678. new_y = y + mid_y - cent_y;
  10679. Crafty.viewport.pan(new_x, new_y, time);
  10680. },
  10681. /**@
  10682. * #Crafty.viewport.zoom
  10683. * @comp Crafty.viewport
  10684. * @sign public void Crafty.viewport.zoom(Number amt, Number cent_x, Number cent_y, Number time)
  10685. * @param Number amt - amount to zoom in on the target by (eg. 2, 4, 0.5)
  10686. * @param Number cent_x - the center to zoom on
  10687. * @param Number cent_y - the center to zoom on
  10688. * @param Number time - the duration in ms of the entire zoom operation
  10689. *
  10690. * Zooms the camera in on a given point. amt > 1 will bring the camera closer to the subject
  10691. * amt < 1 will bring it farther away. amt = 0 will reset to the default zoom level
  10692. * Zooming is multiplicative. To reset the zoom amount, pass 0.
  10693. */
  10694. zoom: (function () {
  10695. function stopZoom(){
  10696. Crafty.unbind("EnterFrame", enterFrame);
  10697. }
  10698. Crafty.bind("StopCamera", stopZoom);
  10699. var startingZoom, finalZoom, finalAmount, startingX, finalX, startingY, finalY, easing;
  10700. function enterFrame(e){
  10701. var amount, v;
  10702. easing.tick(e.dt);
  10703. // The scaling should happen smoothly -- start at 1, end at finalAmount, and at half way scaling should be by finalAmount^(1/2)
  10704. // Since value goes smoothly from 0 to 1, this fufills those requirements
  10705. amount = Math.pow(finalAmount, easing.value() );
  10706. // The viewport should move in such a way that no point reverses
  10707. // If a and b are the top left/bottom right of the viewport, then the below can be derived from
  10708. // (a_0-b_0)/(a-b) = amount,
  10709. // and the assumption that both a and b have the same form
  10710. // a = a_0 * (1-v) + a_f * v,
  10711. // b = b_0 * (1-v) + b_f * v.
  10712. // This is just an arbitrary parameterization of the only sensible path for the viewport corners to take.
  10713. // And by symmetry they should be parameterized in the same way! So not much choice here.
  10714. if (finalAmount === 1)
  10715. v = easing.value(); // prevent NaN! If zoom is used this way, it'll just become a pan.
  10716. else
  10717. v = (1/amount - 1 ) / (1/finalAmount - 1);
  10718. // Set new scale and viewport position
  10719. Crafty.viewport.scale( amount * startingZoom );
  10720. Crafty.viewport.scroll("_x", startingX * (1-v) + finalX * v );
  10721. Crafty.viewport.scroll("_y", startingY * (1-v) + finalY * v );
  10722. Crafty.viewport._clamp();
  10723. if (easing.complete){
  10724. stopZoom();
  10725. Crafty.trigger("CameraAnimationDone");
  10726. }
  10727. }
  10728. return function (amt, cent_x, cent_y, time){
  10729. if (!amt) { // we're resetting to defaults
  10730. Crafty.viewport.scale(1);
  10731. return;
  10732. }
  10733. if (arguments.length <= 2) {
  10734. time = cent_x;
  10735. cent_x = Crafty.viewport.x - Crafty.viewport.width;
  10736. cent_y = Crafty.viewport.y - Crafty.viewport.height;
  10737. }
  10738. Crafty.trigger("StopCamera");
  10739. startingZoom = Crafty.viewport._scale;
  10740. finalAmount = amt;
  10741. finalZoom = startingZoom * finalAmount;
  10742. startingX = Crafty.viewport.x;
  10743. startingY = Crafty.viewport.y;
  10744. finalX = - (cent_x - Crafty.viewport.width / (2 * finalZoom) );
  10745. finalY = - (cent_y - Crafty.viewport.height / (2 * finalZoom) );
  10746. easing = new Crafty.easing(time);
  10747. Crafty.uniqueBind("EnterFrame", enterFrame);
  10748. };
  10749. })(),
  10750. /**@
  10751. * #Crafty.viewport.scale
  10752. * @comp Crafty.viewport
  10753. * @sign public void Crafty.viewport.scale(Number amt)
  10754. * @param Number amt - amount to zoom/scale in on the elements
  10755. *
  10756. * Adjusts the scale (zoom). When `amt` is 1, it is set to the normal scale,
  10757. * e.g. an entity with `this.w == 20` would appear exactly 20 pixels wide.
  10758. * When `amt` is 10, that same entity would appear 200 pixels wide (i.e., zoomed in
  10759. * by a factor of 10), and when `amt` is 0.1, that same entity would be 2 pixels wide
  10760. * (i.e., zoomed out by a factor of `(1 / 0.1)`).
  10761. *
  10762. * If you pass an `amt` of 0, it is treated the same as passing 1, i.e. the scale is reset.
  10763. *
  10764. * This method sets the absolute scale, while `Crafty.viewport.zoom` sets the scale relative to the existing value.
  10765. * @see Crafty.viewport.zoom
  10766. *
  10767. * @example
  10768. * ~~~
  10769. * Crafty.viewport.scale(2); // Zoom in -- all entities will appear twice as large.
  10770. * ~~~
  10771. */
  10772. scale: (function () {
  10773. return function (amt) {
  10774. this._scale = amt ? amt : 1;
  10775. Crafty.trigger("InvalidateViewport");
  10776. Crafty.trigger("ViewportScale");
  10777. };
  10778. })(),
  10779. /**@
  10780. * #Crafty.viewport.mouselook
  10781. * @comp Crafty.viewport
  10782. * @sign public void Crafty.viewport.mouselook(Boolean active)
  10783. * @param Boolean active - Activate or deactivate mouselook
  10784. *
  10785. * Toggle mouselook on the current viewport.
  10786. * Simply call this function and the user will be able to
  10787. * drag the viewport around.
  10788. *
  10789. * If the user starts a drag, "StopCamera" will be triggered, which will cancel any existing camera animations.
  10790. */
  10791. mouselook: (function () {
  10792. var active = false,
  10793. dragging = false,
  10794. lastMouse = {};
  10795. old = {};
  10796. function stopLook(){
  10797. dragging = false;
  10798. }
  10799. return function (op, arg) {
  10800. if (typeof op == 'boolean') {
  10801. active = op;
  10802. if (active) {
  10803. Crafty.mouseObjs++;
  10804. } else {
  10805. Crafty.mouseObjs = Math.max(0, Crafty.mouseObjs - 1);
  10806. }
  10807. return;
  10808. }
  10809. if (!active) return;
  10810. switch (op) {
  10811. case 'move':
  10812. case 'drag':
  10813. if (!dragging) return;
  10814. diff = {
  10815. x: arg.clientX - lastMouse.x,
  10816. y: arg.clientY - lastMouse.y
  10817. };
  10818. lastMouse.x = arg.clientX;
  10819. lastMouse.y = arg.clientY;
  10820. Crafty.viewport.x += diff.x;
  10821. Crafty.viewport.y += diff.y;
  10822. Crafty.viewport._clamp();
  10823. break;
  10824. case 'start':
  10825. Crafty.trigger("StopCamera");
  10826. lastMouse.x = arg.clientX;
  10827. lastMouse.y = arg.clientY;
  10828. dragging = true;
  10829. break;
  10830. case 'stop':
  10831. dragging = false;
  10832. break;
  10833. }
  10834. };
  10835. })(),
  10836. _clamp: function () {
  10837. // clamps the viewport to the viewable area
  10838. // under no circumstances should the viewport see something outside the boundary of the 'world'
  10839. if (!this.clampToEntities) return;
  10840. var bound = this.bounds || Crafty.map.boundaries();
  10841. bound.max.x *= this._scale;
  10842. bound.min.x *= this._scale;
  10843. bound.max.y *= this._scale;
  10844. bound.min.y *= this._scale;
  10845. if (bound.max.x - bound.min.x > Crafty.viewport.width) {
  10846. if (Crafty.viewport.x < -bound.max.x + Crafty.viewport.width) {
  10847. Crafty.viewport.x = -bound.max.x + Crafty.viewport.width;
  10848. } else if (Crafty.viewport.x > -bound.min.x) {
  10849. Crafty.viewport.x = -bound.min.x;
  10850. }
  10851. } else {
  10852. Crafty.viewport.x = -1 * (bound.min.x + (bound.max.x - bound.min.x) / 2 - Crafty.viewport.width / 2);
  10853. }
  10854. if (bound.max.y - bound.min.y > Crafty.viewport.height) {
  10855. if (Crafty.viewport.y < -bound.max.y + Crafty.viewport.height) {
  10856. Crafty.viewport.y = -bound.max.y + Crafty.viewport.height;
  10857. } else if (Crafty.viewport.y > -bound.min.y) {
  10858. Crafty.viewport.y = -bound.min.y;
  10859. }
  10860. } else {
  10861. Crafty.viewport.y = -1 * (bound.min.y + (bound.max.y - bound.min.y) / 2 - Crafty.viewport.height / 2);
  10862. }
  10863. },
  10864. /**@
  10865. * #Crafty.viewport.init
  10866. * @comp Crafty.viewport
  10867. * @sign public void Crafty.viewport.init([Number width, Number height, String stage_elem])
  10868. * @sign public void Crafty.viewport.init([Number width, Number height, HTMLElement stage_elem])
  10869. * @param Number width - Width of the viewport
  10870. * @param Number height - Height of the viewport
  10871. * @param String or HTMLElement stage_elem - the element to use as the stage (either its id or the actual element).
  10872. *
  10873. * Initialize the viewport. If the arguments 'width' or 'height' are missing, use Crafty.DOM.window.width and Crafty.DOM.window.height (full screen model).
  10874. *
  10875. * The argument 'stage_elem' is used to specify a stage element other than the default, and can be either a string or an HTMLElement. If a string is provided, it will look for an element with that id and, if none exists, create a div. If an HTMLElement is provided, that is used directly. Omitting this argument is the same as passing an id of 'cr-stage'.
  10876. *
  10877. * @see Crafty.device, Crafty.DOM, Crafty.stage
  10878. */
  10879. init: function (w, h, stage_elem) {
  10880. Crafty.DOM.window.init();
  10881. // setters+getters for the viewport
  10882. this._defineViewportProperties();
  10883. // If no width or height is defined, the width and height is set to fullscreen
  10884. this._width = (!w) ? Crafty.DOM.window.width : w;
  10885. this._height = (!h) ? Crafty.DOM.window.height : h;
  10886. //check if stage exists
  10887. if (typeof stage_elem === 'undefined')
  10888. stage_elem = "cr-stage";
  10889. var crstage;
  10890. if (typeof stage_elem === 'string')
  10891. crstage = document.getElementById(stage_elem);
  10892. else if (typeof HTMLElement !== "undefined" ? stage_elem instanceof HTMLElement : stage_elem instanceof Element)
  10893. crstage = stage_elem;
  10894. else
  10895. throw new TypeError("stage_elem must be a string or an HTMLElement");
  10896. /**@
  10897. * #Crafty.stage
  10898. * @category Core
  10899. * The stage where all the DOM entities will be placed.
  10900. */
  10901. /**@
  10902. * #Crafty.stage.elem
  10903. * @comp Crafty.stage
  10904. * The `#cr-stage` div element.
  10905. */
  10906. /**@
  10907. * #Crafty.stage.inner
  10908. * @comp Crafty.stage
  10909. * `Crafty.stage.inner` is a div inside the `#cr-stage` div that holds all DOM entities.
  10910. * If you use canvas, a `canvas` element is created at the same level in the dom
  10911. * as the the `Crafty.stage.inner` div. So the hierarchy in the DOM is
  10912. *
  10913. * ~~~
  10914. * Crafty.stage.elem
  10915. * - Crafty.stage.inner (a div HTMLElement)
  10916. * - Crafty.canvas._canvas (a canvas HTMLElement)
  10917. * ~~~
  10918. */
  10919. //create stage div to contain everything
  10920. Crafty.stage = {
  10921. x: 0,
  10922. y: 0,
  10923. fullscreen: false,
  10924. elem: (crstage ? crstage : document.createElement("div")),
  10925. inner: document.createElement("div")
  10926. };
  10927. //fullscreen, stop scrollbars
  10928. if (!w && !h) {
  10929. document.body.style.overflow = "hidden";
  10930. Crafty.stage.fullscreen = true;
  10931. }
  10932. Crafty.addEvent(this, window, "resize", Crafty.viewport.reload);
  10933. Crafty.addEvent(this, window, "blur", function () {
  10934. if (Crafty.settings.get("autoPause")) {
  10935. if (!Crafty._paused) Crafty.pause();
  10936. }
  10937. });
  10938. Crafty.addEvent(this, window, "focus", function () {
  10939. if (Crafty._paused && Crafty.settings.get("autoPause")) {
  10940. Crafty.pause();
  10941. }
  10942. });
  10943. //make the stage unselectable
  10944. Crafty.settings.register("stageSelectable", function (v) {
  10945. Crafty.stage.elem.onselectstart = v ? function () {
  10946. return true;
  10947. } : function () {
  10948. return false;
  10949. };
  10950. });
  10951. Crafty.settings.modify("stageSelectable", false);
  10952. //make the stage have no context menu
  10953. Crafty.settings.register("stageContextMenu", function (v) {
  10954. Crafty.stage.elem.oncontextmenu = v ? function () {
  10955. return true;
  10956. } : function () {
  10957. return false;
  10958. };
  10959. });
  10960. Crafty.settings.modify("stageContextMenu", false);
  10961. Crafty.settings.register("autoPause", function () {});
  10962. Crafty.settings.modify("autoPause", false);
  10963. //add to the body and give it an ID if not exists
  10964. if (!crstage) {
  10965. document.body.appendChild(Crafty.stage.elem);
  10966. Crafty.stage.elem.id = stage_elem;
  10967. }
  10968. var elem = Crafty.stage.elem.style,
  10969. offset;
  10970. Crafty.stage.elem.appendChild(Crafty.stage.inner);
  10971. Crafty.stage.inner.style.position = "absolute";
  10972. Crafty.stage.inner.style.zIndex = "1";
  10973. Crafty.stage.inner.style.transformStyle = "preserve-3d"; // Seems necessary for Firefox to preserve zIndexes?
  10974. //css style
  10975. elem.width = this.width + "px";
  10976. elem.height = this.height + "px";
  10977. elem.overflow = "hidden";
  10978. // resize events
  10979. Crafty.bind("ViewportResize", function(){Crafty.trigger("InvalidateViewport");});
  10980. if (Crafty.mobile) {
  10981. // remove default gray highlighting after touch
  10982. if (typeof elem.webkitTapHighlightColor !== undefined) {
  10983. elem.webkitTapHighlightColor = "rgba(0,0,0,0)";
  10984. }
  10985. var meta = document.createElement("meta"),
  10986. head = document.getElementsByTagName("HEAD")[0];
  10987. //hide the address bar
  10988. meta = document.createElement("meta");
  10989. meta.setAttribute("name", "apple-mobile-web-app-capable");
  10990. meta.setAttribute("content", "yes");
  10991. head.appendChild(meta);
  10992. Crafty.addEvent(this, Crafty.stage.elem, "touchmove", function (e) {
  10993. e.preventDefault();
  10994. });
  10995. } else {
  10996. elem.position = "relative";
  10997. //find out the offset position of the stage
  10998. offset = Crafty.DOM.inner(Crafty.stage.elem);
  10999. Crafty.stage.x = offset.x;
  11000. Crafty.stage.y = offset.y;
  11001. }
  11002. },
  11003. // Create setters/getters for x, y, width, height
  11004. _defineViewportProperties: function(){
  11005. if (Crafty.support.setter) {
  11006. //define getters and setters to scroll the viewport
  11007. this.__defineSetter__('x', function (v) {
  11008. this.scroll('_x', v);
  11009. });
  11010. this.__defineSetter__('y', function (v) {
  11011. this.scroll('_y', v);
  11012. });
  11013. this.__defineSetter__('width', function (v) {
  11014. this._width = v;
  11015. Crafty.trigger("ViewportResize");
  11016. });
  11017. this.__defineSetter__('height', function (v) {
  11018. this._height = v;
  11019. Crafty.trigger("ViewportResize");
  11020. });
  11021. this.__defineGetter__('x', function () {
  11022. return this._x;
  11023. });
  11024. this.__defineGetter__('y', function () {
  11025. return this._y;
  11026. });
  11027. this.__defineGetter__('width', function () {
  11028. return this._width;
  11029. });
  11030. this.__defineGetter__('height', function () {
  11031. return this._height;
  11032. });
  11033. //IE9
  11034. } else if (Crafty.support.defineProperty) {
  11035. Object.defineProperty(this, 'x', {
  11036. set: function (v) {
  11037. this.scroll('_x', v);
  11038. },
  11039. get: function () {
  11040. return this._x;
  11041. },
  11042. configurable : true
  11043. });
  11044. Object.defineProperty(this, 'y', {
  11045. set: function (v) {
  11046. this.scroll('_y', v);
  11047. },
  11048. get: function () {
  11049. return this._y;
  11050. },
  11051. configurable : true
  11052. });
  11053. Object.defineProperty(this, 'width', {
  11054. set: function (v) {
  11055. this._width = v;
  11056. Crafty.trigger("ViewportResize");
  11057. },
  11058. get: function () {
  11059. return this._width;
  11060. },
  11061. configurable : true
  11062. });
  11063. Object.defineProperty(this, 'height', {
  11064. set: function (v) {
  11065. this._height = v;
  11066. Crafty.trigger("ViewportResize");
  11067. },
  11068. get: function () {
  11069. return this._height;
  11070. },
  11071. configurable : true
  11072. });
  11073. }
  11074. },
  11075. /**@
  11076. * #Crafty.viewport.reload
  11077. * @comp Crafty.stage
  11078. *
  11079. * @sign public Crafty.viewport.reload()
  11080. *
  11081. * Recalculate and reload stage width, height and position.
  11082. * Useful when browser return wrong results on init (like safari on Ipad2).
  11083. *
  11084. */
  11085. reload: function () {
  11086. Crafty.DOM.window.init();
  11087. var w = Crafty.DOM.window.width,
  11088. h = Crafty.DOM.window.height,
  11089. offset;
  11090. if (Crafty.stage.fullscreen) {
  11091. this._width = w;
  11092. this._height = h;
  11093. Crafty.trigger("ViewportResize");
  11094. }
  11095. offset = Crafty.DOM.inner(Crafty.stage.elem);
  11096. Crafty.stage.x = offset.x;
  11097. Crafty.stage.y = offset.y;
  11098. },
  11099. /**@
  11100. * #Crafty.viewport.reset
  11101. * @comp Crafty.stage
  11102. * @trigger StopCamera - called to cancel camera animations
  11103. *
  11104. * @sign public Crafty.viewport.reset()
  11105. *
  11106. * Resets the viewport to starting values, and cancels any existing camera animations.
  11107. * Called when scene() is run.
  11108. */
  11109. reset: function () {
  11110. Crafty.viewport.mouselook("stop");
  11111. Crafty.trigger("StopCamera");
  11112. Crafty.viewport.scale(1);
  11113. }
  11114. }
  11115. });
  11116. },{"./core.js":9}],30:[function(require,module,exports){
  11117. var Crafty = require('./core.js'),
  11118. document = window.document;
  11119. // test fragment shader -- everything is white!
  11120. var FRAGMENT_SHADER_SRC =
  11121. "precision mediump float;"
  11122. +"void main(void) {"
  11123. +"gl_FragColor = vec4(0.0, 1.0, 1.0, 0.5);"
  11124. +"}";
  11125. var FRAGMENT_SHADER_SRC_2 =
  11126. "precision mediump float;"
  11127. +"void main(void) {"
  11128. +"gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);"
  11129. +"}";
  11130. // test vertex shader
  11131. var VERTEX_SHADER_SRC_OLD =
  11132. "attribute vec2 aVertexPosition;"
  11133. + "uniform mat2 uGlobalScaleMatrix;"
  11134. + "void main(void) {"
  11135. + " gl_Position = vec4(aVertexPosition, 0, 1);"
  11136. + "}";
  11137. glHelpers = {
  11138. // Either x,y signature or x1, y1, x2, y2, etc
  11139. writeVec2: function (data, offset, stride, x, y){
  11140. //console.log(arguments);
  11141. if (arguments.length == 5){
  11142. for (var i = 0; i<4; i++){
  11143. data[offset + stride*i] = x;
  11144. data[offset + stride*i + 1] = y;
  11145. }
  11146. } else {
  11147. for (var i = 0; i<4; i++){
  11148. data[offset + stride*i] = arguments[3 + i*2];
  11149. data[offset + stride*i + 1] = arguments[4 + i*2];
  11150. }
  11151. }
  11152. },
  11153. // Either x,y, z, w signature or x1, y1, x2, y2, etc
  11154. writeVec4: function (data, offset, stride, x, y, z, w){
  11155. if (arguments.length == 7){
  11156. for (var i = 0; i<4; i++){
  11157. data[offset + stride*i] = x;
  11158. data[offset + stride*i + 1] = y;
  11159. data[offset + stride*i + 2] = z;
  11160. data[offset + stride*i + 3] = w;
  11161. }
  11162. } else {
  11163. for (var i =0; i<4; i++){
  11164. data[offset + stride*i] = arguments[3 + i*4];
  11165. data[offset + stride*i + 1] = arguments[4 + i*4];
  11166. data[offset + stride*i + 2] = arguments[5 + i*4];
  11167. data[offset + stride*i + 3] = arguments[6 + i*4];
  11168. }
  11169. }
  11170. },
  11171. makeProgram: function (gl, fragment_src, vertex_src){
  11172. var gl = this.context;
  11173. var fragment_shader = this.compileShader(gl, fragment_src, gl.FRAGMENT_SHADER);
  11174. var vertex_shader = this.compileShader(gl, vertex_src, gl.VERTEX_SHADER);
  11175. var shaderProgram = gl.createProgram();
  11176. gl.attachShader(shaderProgram, vertex_shader);
  11177. gl.attachShader(shaderProgram, fragment_shader);
  11178. gl.linkProgram(shaderProgram);
  11179. if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
  11180. throw("Could not initialise shaders");
  11181. }
  11182. shaderProgram.viewport = gl.getUniformLocation(shaderProgram, "uViewport");
  11183. return shaderProgram;
  11184. },
  11185. compileShader: function (gl, src, type){
  11186. var shader = gl.createShader(type);
  11187. gl.shaderSource(shader, src);
  11188. gl.compileShader(shader);
  11189. if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
  11190. throw(gl.getShaderInfoLog(shader));
  11191. };
  11192. return shader;
  11193. },
  11194. };
  11195. // fragment shader source for an image/etc
  11196. /*
  11197. varying highp vec2 vTextureCoord;
  11198. uniform sampler2D uSampler;
  11199. uniform highp vec2 uTextureDimensions;
  11200. uniform highp vec4 uSpriteCoords;
  11201. void main(void) {
  11202. highp vec2 coord = ( uSpriteCoords.zw * vTextureCoord + uSpriteCoords.xy) / uTextureDimensions;
  11203. gl_FragColor = texture2D(uSampler, coord);
  11204. }
  11205. */
  11206. var TEXTURE_FRAGMENT_SHADER_SRC =
  11207. "varying highp vec2 vTextureCoord;\r\n \r\nuniform sampler2D uSampler;\r\nuniform highp vec2 uTextureDimensions;\r\nuniform highp vec4 uSpriteCoords;\r\n\r\nvoid main(void) {\r\n highp vec2 coord = ( uSpriteCoords.zw * vTextureCoord + uSpriteCoords.xy) \/ uTextureDimensions;\r\n gl_FragColor = texture2D(uSampler, coord);\r\n}";
  11208. // Vertex shader source, unformatted
  11209. /*
  11210. attribute vec2 a_position;
  11211. uniform vec4 uViewport;
  11212. uniform vec4 uEntityPos;
  11213. uniform vec4 uEntityExtra;
  11214. varying highp vec2 vTextureCoord;
  11215. mat4 viewportScale = mat4(2.0 / uViewport.z, 0, 0, 0, 0, -2.0 / uViewport.w, 0,0, 0, 0,1,0, -1,+1,0,1);
  11216. vec4 viewportTranslation = vec4(uViewport.xy, 0, 0);
  11217. vec2 entityScale = uEntityPos.zw;
  11218. vec2 entityTranslation = uEntityPos.xy;
  11219. vec2 entityOrigin = uEntityExtra.xy;
  11220. mat2 entityRotationMatrix = mat2(cos(uEntityExtra.w), sin(uEntityExtra.w), -sin(uEntityExtra.w), cos(uEntityExtra.w));
  11221. void main() {
  11222. vec2 pos = entityScale * a_position;
  11223. pos = entityRotationMatrix * (pos - entityOrigin) + entityOrigin + entityTranslation;
  11224. gl_Position = viewportScale * (viewportTranslation + vec4(pos, uEntityExtra.z, 1) );
  11225. vTextureCoord = a_position;
  11226. }
  11227. */
  11228. // Escape using a tool like [this one](http://www.freeformatter.com/javascript-escape.html).
  11229. var VERTEX_SHADER_SRC =
  11230. "attribute vec2 a_position;\r\nuniform vec4 uViewport;\r\nuniform vec4 uEntityPos;\r\nuniform vec4 uEntityExtra;\r\n\r\n\r\nvarying highp vec2 vTextureCoord;\r\n\r\nmat4 viewportScale = mat4(2.0 \/ uViewport.z, 0, 0, 0, 0, -2.0 \/ uViewport.w, 0,0, 0, 0,1,0, -1,+1,0,1);\r\nvec4 viewportTranslation = vec4(uViewport.xy, 0, 0);\r\n\r\nvec2 entityScale = uEntityPos.zw;\r\nvec2 entityTranslation = uEntityPos.xy;\r\nvec2 entityOrigin = uEntityExtra.xy;\r\nmat2 entityRotationMatrix = mat2(cos(uEntityExtra.w), sin(uEntityExtra.w), -sin(uEntityExtra.w), cos(uEntityExtra.w));\r\n\r\nvoid main() {\r\n vec2 pos = entityScale * a_position;\r\n pos = entityRotationMatrix * (pos - entityOrigin) + entityOrigin + entityTranslation;\r\n gl_Position = viewportScale * (viewportTranslation + vec4(pos, uEntityExtra.z, 1) );\r\n vTextureCoord = a_position;\r\n}";
  11231. // New fragmetn/vertex for color
  11232. /*
  11233. attribute vec2 aPosition;
  11234. attribute vec4 aExtras;
  11235. attribute vec4 aColor;
  11236. varying lowp vec4 vColor;
  11237. uniform vec4 uViewport;
  11238. mat4 viewportScale = mat4(2.0 / uViewport.z, 0, 0, 0, 0, -2.0 / uViewport.w, 0,0, 0, 0,1,0, -1,+1,0,1);
  11239. vec4 viewportTranslation = vec4(uViewport.xy, 0, 0);
  11240. vec2 entityOrigin = aExtras.xy;
  11241. mat2 entityRotationMatrix = mat2(cos(aExtras.w), sin(aExtras.w), -sin(aExtras.w), cos(aExtras.w));
  11242. void main() {
  11243. vec2 pos = aPosition;
  11244. pos = entityRotationMatrix * (pos - entityOrigin) + entityOrigin ;
  11245. gl_Position = viewportScale * (viewportTranslation + vec4(pos, 1.0/(1.0+exp(aExtras.z) ), 1) );
  11246. vColor = aColor;
  11247. }
  11248. */
  11249. var COLOR_VERTEX_SHADER =
  11250. "attribute vec2 aPosition;\r\nattribute vec4 aExtras;\r\nattribute vec4 aColor;\r\n\r\nvarying lowp vec4 vColor;\r\n\r\nuniform vec4 uViewport;\r\n\r\nmat4 viewportScale = mat4(2.0 \/ uViewport.z, 0, 0, 0, 0, -2.0 \/ uViewport.w, 0,0, 0, 0,1,0, -1,+1,0,1);\r\nvec4 viewportTranslation = vec4(uViewport.xy, 0, 0);\r\n\r\nvec2 entityOrigin = aExtras.xy;\r\nmat2 entityRotationMatrix = mat2(cos(aExtras.w), sin(aExtras.w), -sin(aExtras.w), cos(aExtras.w));\r\n\r\nvoid main() {\r\n vec2 pos = aPosition;\r\n pos = entityRotationMatrix * (pos - entityOrigin) + entityOrigin ;\r\n gl_Position = viewportScale * (viewportTranslation + vec4(pos, 1.0\/(1.0+exp(aExtras.z) ), 1) );\r\n vColor = aColor;\r\n}";
  11251. var COLOR_FRAGMENT_SHADER = "";
  11252. /*
  11253. attribute vec2 aPosition;
  11254. attribute vec4 aExtras;
  11255. attribute vec2 aTextureCoord;
  11256. varying mediump vec2 vTextureCoord;
  11257. uniform vec4 uViewport;
  11258. uniform mediump vec2 uTextureDimensions;
  11259. mat4 viewportScale = mat4(2.0 / uViewport.z, 0, 0, 0, 0, -2.0 / uViewport.w, 0,0, 0, 0,1,0, -1,+1,0,1);
  11260. vec4 viewportTranslation = vec4(uViewport.xy, 0, 0);
  11261. vec2 entityOrigin = aExtras.xy;
  11262. mat2 entityRotationMatrix = mat2(cos(aExtras.w), sin(aExtras.w), -sin(aExtras.w), cos(aExtras.w));
  11263. void main() {
  11264. vec2 pos = aPosition;
  11265. pos = entityRotationMatrix * (pos - entityOrigin) + entityOrigin ;
  11266. gl_Position = viewportScale * (viewportTranslation + vec4(pos, 1.0/(1.0+exp(aExtras.z) ), 1) );
  11267. vTextureCoord = aTextureCoord;
  11268. }
  11269. */
  11270. var SPRITE_VERTEX_SHADER =
  11271. "attribute vec2 aPosition;\r\nattribute vec4 aExtras;\r\nattribute vec2 aTextureCoord;\r\n\r\nvarying mediump vec2 vTextureCoord;\r\n\r\nuniform vec4 uViewport;\r\nuniform mediump vec2 uTextureDimensions;\r\n\r\nmat4 viewportScale = mat4(2.0 \/ uViewport.z, 0, 0, 0, 0, -2.0 \/ uViewport.w, 0,0, 0, 0,1,0, -1,+1,0,1);\r\nvec4 viewportTranslation = vec4(uViewport.xy, 0, 0);\r\n\r\nvec2 entityOrigin = aExtras.xy;\r\nmat2 entityRotationMatrix = mat2(cos(aExtras.w), sin(aExtras.w), -sin(aExtras.w), cos(aExtras.w));\r\n\r\nvoid main() {\r\n vec2 pos = aPosition;\r\n pos = entityRotationMatrix * (pos - entityOrigin) + entityOrigin ;\r\n gl_Position = viewportScale * (viewportTranslation + vec4(pos, 1.0\/(1.0+exp(aExtras.z) ), 1) );\r\n vTextureCoord = aTextureCoord;\r\n}\r\n";
  11272. /*
  11273. varying mediump vec2 vTextureCoord;
  11274. uniform sampler2D uSampler;
  11275. uniform mediump vec2 uTextureDimensions;
  11276. void main(void) {
  11277. highp vec2 coord = vTextureCoord / uTextureDimensions;
  11278. gl_FragColor = texture2D(uSampler, coord);
  11279. }
  11280. */
  11281. var SPRITE_FRAGMENT_SHADER =
  11282. " varying mediump vec2 vTextureCoord;\r\n \r\n uniform sampler2D uSampler;\r\n uniform mediump vec2 uTextureDimensions;\r\n\r\n void main(void) {\r\n highp vec2 coord = vTextureCoord \/ uTextureDimensions;\r\n gl_FragColor = texture2D(uSampler, coord);\r\n }";
  11283. Crafty.c("TestSquare", {
  11284. init: function(){
  11285. if (this.has("WebGL")){
  11286. this._establishShader("TestSquare", this._fragmentShader)
  11287. }
  11288. },
  11289. _fragmentShader: FRAGMENT_SHADER_SRC
  11290. });
  11291. Crafty.c("TestSquareWhite", {
  11292. init: function(){
  11293. if (this.has("WebGL")){
  11294. this._establishShader("TestSquareWhite", this._fragmentShader)
  11295. }
  11296. },
  11297. _fragmentShader: FRAGMENT_SHADER_SRC_2
  11298. });
  11299. Crafty.c("TestColor", {
  11300. init: function(){
  11301. if (this.has("WebGL")){
  11302. var gl = this.webgl.context;
  11303. this._establishShader("TestColor", this._fragmentShader, this._vertexShader);
  11304. if (typeof this._shaderProgram.posLocation === "undefined"){
  11305. this._specializeProgram();
  11306. }
  11307. this._glNum = this._shaderProgram._elementCount++;
  11308. }
  11309. this._red = this._blue = this._green = 1.0;
  11310. this.bind("Draw", this._drawColor);
  11311. },
  11312. _specializeProgram: function(){
  11313. var gl = this.webgl.context;
  11314. console.log('setting positions');
  11315. var prog = this._shaderProgram;
  11316. prog._bufferArray = new Float32Array(4000);
  11317. prog._kingBuffer = gl.createBuffer();
  11318. prog.index = new Uint16Array(600);
  11319. prog._indexBuffer = gl.createBuffer();
  11320. prog.posLocation = gl.getAttribLocation(prog, "aPosition");
  11321. gl.enableVertexAttribArray(prog.posLocation);
  11322. prog.extrasLocation = gl.getAttribLocation(prog, "aExtras");
  11323. gl.enableVertexAttribArray(prog.extrasLocation);
  11324. prog.colLocation = gl.getAttribLocation(prog, "aColor");
  11325. gl.enableVertexAttribArray(prog.colLocation);
  11326. prog._elementCount = 0;
  11327. var size = Float32Array.BYTES_PER_ELEMENT;
  11328. var stride = (2+4+4) * size;
  11329. prog.stride = stride;
  11330. prog.switchTo = function(){
  11331. gl.useProgram(prog);
  11332. gl.bindBuffer(gl.ARRAY_BUFFER, prog._kingBuffer);
  11333. gl.vertexAttribPointer(prog.posLocation, 2, gl.FLOAT, false, stride, 0);
  11334. gl.vertexAttribPointer(prog.extrasLocation, 4, gl.FLOAT, false, stride, 2*size);
  11335. gl.vertexAttribPointer(prog.colLocation, 4, gl.FLOAT, false, stride, (2+4)*size);
  11336. };
  11337. prog.renderBatch = function(){
  11338. gl.bindBuffer(gl.ARRAY_BUFFER, prog._kingBuffer);
  11339. gl.bufferData(gl.ARRAY_BUFFER, prog._bufferArray, gl.STATIC_DRAW);
  11340. gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, prog._indexBuffer);
  11341. gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, prog.index, gl.STATIC_DRAW);
  11342. gl.drawElements(gl.TRIANGLES, prog.pointer, gl.UNSIGNED_SHORT, 0);
  11343. };
  11344. },
  11345. _fragmentShader:
  11346. "precision mediump float;"
  11347. + "varying lowp vec4 vColor;"
  11348. + "void main(void) {"
  11349. + " gl_FragColor = vColor;"
  11350. + "}",
  11351. _vertexShader: COLOR_VERTEX_SHADER,
  11352. _drawColor: function(drawVars){
  11353. //console.log("Drawing color");
  11354. var gl = drawVars.gl, prog = drawVars.program;
  11355. // Write the vertex data into the array
  11356. this._writeToArray(prog._bufferArray);
  11357. //console.log(prog._bufferArray);
  11358. // Register the vertex groups to be drawn
  11359. // Two triangles; (0, 1, 2) and (1, 2, 3)
  11360. var offset = this._glNum * 4;
  11361. var index = prog.index;
  11362. var l = prog.pointer;
  11363. index[0+l] = 0 + offset;
  11364. index[1+l] = 1 + offset;
  11365. index[2+l] = 2 + offset;
  11366. index[3+l] = 1 + offset;
  11367. index[4+l] = 2 + offset;
  11368. index[5+l] = 3 + offset;
  11369. prog.pointer += 6;
  11370. },
  11371. _writeToArray: function(data){
  11372. //intermediate: just CREATE the matrix right here
  11373. var width = 2 + 4 + 4;
  11374. var offset = (width * 4) * this._glNum;
  11375. // Write position; x, y, w, h
  11376. glHelpers.writeVec2(data, offset, width,
  11377. this._x, this._y,
  11378. this._x , this._y + this._h,
  11379. this._x + this._w, this._y,
  11380. this._x + this._w, this._y + this._h
  11381. );
  11382. // Write orientation and z level
  11383. glHelpers.writeVec4(data, offset + 2, width,
  11384. this._origin.x + this._x,
  11385. this._origin.y + this._y,
  11386. this._z,
  11387. this._rotation
  11388. );
  11389. glHelpers.writeVec4(data, offset + 6, width,
  11390. this._red,
  11391. this._green,
  11392. this._blue,
  11393. 1
  11394. );
  11395. },
  11396. color: function (r, g, b){
  11397. this._red = r;
  11398. this._green = g;
  11399. this._blue = b;
  11400. return this;
  11401. }
  11402. });
  11403. /*
  11404. console.log("Initing webgl sprite");
  11405. var webgl = this.webgl;
  11406. this._establishShader(url, TEXTURE_FRAGMENT_SHADER_SRC)
  11407. this.__texture = webgl.makeTexture(this.__image, this.img);
  11408. console.log("Made texture")
  11409. console.log(this.__texture);
  11410. console.log("Image complete? " + img.complete)
  11411. webgl.bindTexture(this._shaderProgram, this.__texture)
  11412. */
  11413. Crafty.c("GLSprite", {
  11414. init: function(){
  11415. if (this.has("WebGL")){
  11416. var gl = this.webgl.context;
  11417. this._establishShader(this.__image, this._fragmentShader, this._vertexShader);
  11418. if (typeof this._shaderProgram.posLocation === "undefined"){
  11419. this._specializeProgram();
  11420. }
  11421. this._glNum = this._shaderProgram._elementCount++;
  11422. }
  11423. this.bind("Draw", this._drawSprite);
  11424. },
  11425. // For sprite
  11426. _specializeProgram: function(){
  11427. var gl = this.webgl.context;
  11428. var webgl =this.webgl;
  11429. console.log('setting sprite positions');
  11430. var prog = this._shaderProgram;
  11431. prog.__texture = webgl.makeTexture(this.__image, this.img);
  11432. //console.log("Made texture")
  11433. //console.log(this.__texture);
  11434. //console.log("Image complete? " + img.complete)
  11435. webgl.bindTexture(this._shaderProgram, prog.__texture);
  11436. prog._bufferArray = new Float32Array(4000);
  11437. prog._kingBuffer = gl.createBuffer();
  11438. prog.index = new Uint16Array(600);
  11439. prog._indexBuffer = gl.createBuffer();
  11440. prog.posLocation = gl.getAttribLocation(prog, "aPosition");
  11441. gl.enableVertexAttribArray(prog.posLocation);
  11442. prog.extrasLocation = gl.getAttribLocation(prog, "aExtras");
  11443. gl.enableVertexAttribArray(prog.extrasLocation);
  11444. prog.textureLocation = gl.getAttribLocation(prog, "aTextureCoord");
  11445. gl.enableVertexAttribArray(prog.textureLocation);
  11446. prog._elementCount = 0;
  11447. var size = Float32Array.BYTES_PER_ELEMENT;
  11448. var stride = (2+4+2) * size;
  11449. prog.stride = stride;
  11450. prog.switchTo = function(){
  11451. gl.useProgram(prog);
  11452. gl.bindBuffer(gl.ARRAY_BUFFER, prog._kingBuffer);
  11453. gl.vertexAttribPointer(prog.posLocation, 2, gl.FLOAT, false, stride, 0);
  11454. gl.vertexAttribPointer(prog.extrasLocation, 4, gl.FLOAT, false, stride, 2*size);
  11455. gl.vertexAttribPointer(prog.colLocation, 4, gl.FLOAT, false, stride, (2+4)*size);
  11456. };
  11457. prog.renderBatch = function(){
  11458. gl.bindBuffer(gl.ARRAY_BUFFER, prog._kingBuffer);
  11459. gl.bufferData(gl.ARRAY_BUFFER, prog._bufferArray, gl.STATIC_DRAW);
  11460. gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, prog._indexBuffer);
  11461. gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, prog.index, gl.STATIC_DRAW);
  11462. gl.drawElements(gl.TRIANGLES, prog.pointer, gl.UNSIGNED_SHORT, 0);
  11463. };
  11464. },
  11465. _fragmentShader: SPRITE_FRAGMENT_SHADER,
  11466. _vertexShader: SPRITE_VERTEX_SHADER,
  11467. _drawSprite: function(drawVars){
  11468. //console.log("Drawing color");
  11469. var gl = drawVars.gl, prog = drawVars.program;
  11470. // Write the vertex data into the array
  11471. this._writeToArray(prog._bufferArray, drawVars.co);
  11472. // Register the vertex groups to be drawn
  11473. // Two triangles; (0, 1, 2) and (1, 2, 3)
  11474. var offset = this._glNum * 4;
  11475. var index = prog.index;
  11476. var l = prog.pointer;
  11477. index[0+l] = 0 + offset;
  11478. index[1+l] = 1 + offset;
  11479. index[2+l] = 2 + offset;
  11480. index[3+l] = 1 + offset;
  11481. index[4+l] = 2 + offset;
  11482. index[5+l] = 3 + offset;
  11483. prog.pointer += 6;
  11484. },
  11485. _writeToArray: function(data, co){
  11486. //intermediate: just CREATE the matrix right here
  11487. var width = 2 + 4 + 2;
  11488. var offset = (width * 4) * this._glNum;
  11489. // Write position; x, y, w, h
  11490. glHelpers.writeVec2(data, offset, width,
  11491. this._x, this._y,
  11492. this._x , this._y + this._h,
  11493. this._x + this._w, this._y,
  11494. this._x + this._w, this._y + this._h
  11495. );
  11496. // Write orientation and z level
  11497. glHelpers.writeVec4(data, offset + 2, width,
  11498. this._origin.x + this._x,
  11499. this._origin.y + this._y,
  11500. this._z,
  11501. this._rotation
  11502. );
  11503. // Write array coordinates
  11504. glHelpers.writeVec2(data, offset + 6, width,
  11505. co.x, co.y,
  11506. co.x, co.y + co.h,
  11507. co.x + co.w, co.y,
  11508. co.x + co.w, co.y + co.h
  11509. );
  11510. }
  11511. });
  11512. // This will totally assume, for now, that gl-matrix is available
  11513. Crafty.c("WebGL", {
  11514. init: function () {
  11515. if (!Crafty.webgl.context) {
  11516. Crafty.webgl.init();
  11517. }
  11518. var webgl = this.webgl = Crafty.webgl;
  11519. var gl = webgl.context;
  11520. //increment the amount of canvas objs
  11521. webgl.entities++;
  11522. this._changed = true;
  11523. webgl.add(this);
  11524. this.bind("Change", function (e) {
  11525. //flag if changed
  11526. if (this._changed === false) {
  11527. this._changed = true;
  11528. webgl.add(this);
  11529. }
  11530. });
  11531. this.bind("Remove", function () {
  11532. webgl.entities--;
  11533. this._changed = true;
  11534. webgl.add(this);
  11535. });
  11536. },
  11537. /**@
  11538. * #.draw
  11539. * @comp WebGL
  11540. * @sign public this .draw([[Context ctx, ]Number x, Number y, Number w, Number h])
  11541. * @param ctx - Canvas 2D context if drawing on another canvas is required
  11542. * @param x - X offset for drawing a segment
  11543. * @param y - Y offset for drawing a segment
  11544. * @param w - Width of the segment to draw
  11545. * @param h - Height of the segment to draw
  11546. *
  11547. * Method to draw the entity on the canvas element. Can pass rect values for redrawing a segment of the entity.
  11548. */
  11549. // Cache the various objects and arrays used in draw
  11550. drawVars: {
  11551. type: "webgl",
  11552. pos: {},
  11553. ctx: null,
  11554. coord: [0, 0, 0, 0],
  11555. co: {
  11556. x: 0,
  11557. y: 0,
  11558. w: 0,
  11559. h: 0
  11560. }
  11561. },
  11562. draw: function (ctx, x, y, w, h) {
  11563. if (!this.ready) return;
  11564. if (arguments.length === 4) {
  11565. h = w;
  11566. w = y;
  11567. y = x;
  11568. x = ctx;
  11569. ctx = this.webgl.context;
  11570. }
  11571. var pos = this.drawVars.pos;
  11572. pos._x = (this._x + (x || 0));
  11573. pos._y = (this._y + (y || 0));
  11574. pos._w = (w || this._w);
  11575. pos._h = (h || this._h);
  11576. var coord = this.__coord || [0, 0, 0, 0];
  11577. var co = this.drawVars.co;
  11578. co.x = coord[0] + (x || 0);
  11579. co.y = coord[1] + (y || 0);
  11580. co.w = w || coord[2];
  11581. co.h = h || coord[3];
  11582. // Handle flipX, flipY
  11583. if (this._flipX || this._flipY) {
  11584. }
  11585. //set alpha
  11586. if (this._alpha < 1.0) {
  11587. }
  11588. //Draw entity
  11589. var gl = this.webgl.context;
  11590. this.drawVars.gl = gl;
  11591. this.drawVars.program = this._shaderProgram;
  11592. this.trigger("Draw", this.drawVars);
  11593. //gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
  11594. return this;
  11595. },
  11596. // v_src is optional, there's a default vertex shader that works for regular rectangular entities
  11597. _establishShader: function(compName, f_src, v_src){
  11598. console.log("Establishing shader");
  11599. var wgl = this.webgl;
  11600. if (typeof wgl.programs[compName] === "undefined"){
  11601. wgl.programs[compName] = glHelpers.makeProgram(gl, f_src, v_src);
  11602. }
  11603. this._shaderProgram = wgl.programs[compName];
  11604. // Shader program means ready
  11605. this.ready = true;
  11606. },
  11607. });
  11608. /**@
  11609. * #Crafty.webgl
  11610. * @category Graphics
  11611. *
  11612. * Collection of methods to draw on canvas.
  11613. */
  11614. Crafty.extend({
  11615. webgl: {
  11616. /**@
  11617. * #Crafty.webgl.context
  11618. * @comp Crafty.webgl
  11619. *
  11620. * This will return the context of the webgl canvas element.
  11621. * FIXME The value returned from `Crafty.canvas._canvas.getContext('2d')`.
  11622. */
  11623. context: null,
  11624. entities: 0,
  11625. changed_objects: [],
  11626. add: function(e){
  11627. this.changed_objects.push(e);
  11628. },
  11629. /**@
  11630. * #Crafty.canvas._glCanvas
  11631. * @comp Crafty.webgl
  11632. *
  11633. * WebGL Canvas element
  11634. */
  11635. programs: {},
  11636. compileShader: function (src, type){
  11637. var gl = this.context;
  11638. var shader = gl.createShader(type);
  11639. gl.shaderSource(shader, src);
  11640. gl.compileShader(shader);
  11641. if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
  11642. throw(gl.getShaderInfoLog(shader));
  11643. };
  11644. return shader;
  11645. },
  11646. makeProgram: function (fragment_src, vertex_src){
  11647. console.log("Making program");
  11648. console.log(fragment_src);
  11649. var gl = this.context;
  11650. var fragment_shader = this.compileShader(fragment_src, gl.FRAGMENT_SHADER);
  11651. var vertex_shader = (vertex_src) ? this.compileShader(vertex_src, gl.VERTEX_SHADER) : this.defaultVertexShader;
  11652. var shaderProgram = gl.createProgram();
  11653. gl.attachShader(shaderProgram, vertex_shader);
  11654. gl.attachShader(shaderProgram, fragment_shader);
  11655. gl.linkProgram(shaderProgram);
  11656. if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
  11657. throw("Could not initialise shaders");
  11658. }
  11659. shaderProgram.viewport = gl.getUniformLocation(shaderProgram, "uViewport");
  11660. return shaderProgram;
  11661. },
  11662. textures: {},
  11663. textureCount: 0,
  11664. makeTexture: function(url, image){
  11665. var webgl = this;
  11666. if (typeof webgl.textures[url] !== 'undefined')
  11667. return webgl.textures[url];
  11668. var gl = webgl.context;
  11669. var texture = gl.createTexture();
  11670. gl.bindTexture(gl.TEXTURE_2D, texture);
  11671. gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
  11672. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); //gl.NEAREST is also allowed, instead of gl.LINEAR, as neither mipmap.
  11673. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); //Prevents s-coordinate wrapping (repeating).
  11674. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); //Prevents t-coordinate wrapping (repeating).
  11675. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
  11676. //gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST);
  11677. //gl.generateMipmap(gl.TEXTURE_2D);
  11678. gl.bindTexture(gl.TEXTURE_2D, null);
  11679. gl.activeTexture(gl["TEXTURE" + webgl.textureCount]);
  11680. gl.bindTexture(gl.TEXTURE_2D, texture);
  11681. webgl.textures[url] = {
  11682. t: texture,
  11683. sampler: webgl.textureCount,
  11684. key: "TEXTURE" + webgl.textureCount,
  11685. width: image.width,
  11686. height: image.height,
  11687. url: url
  11688. };
  11689. webgl.textureCount++;
  11690. gl.activeTexture(gl["TEXTURE" + (webgl.textureCount)]);
  11691. return webgl.textures[url];
  11692. },
  11693. bindTexture: function(program, texture_obj) {
  11694. if (typeof program.texture_obj !== "undefined")
  11695. return;
  11696. this.context;
  11697. var webgl = this;
  11698. gl.useProgram(program);
  11699. // Set the texture buffer to use
  11700. gl.uniform1i(gl.getUniformLocation(program, "uSampler"), texture_obj.sampler);
  11701. // Set the image dimensions
  11702. gl.uniform2f(gl.getUniformLocation(program, "uTextureDimensions"), texture_obj.width, texture_obj.height);
  11703. program.texture_obj = texture_obj;
  11704. },
  11705. /**@
  11706. * #Crafty.webgl.init
  11707. * @comp Crafty.webgl
  11708. * @sign public void Crafty.webgl.init(void)
  11709. * @trigger NoWebGL - triggered if `Crafty.support.webgl` is false FIXME actually implement!
  11710. *
  11711. * Creates a `canvas` element inside `Crafty.stage.elem`. Must be called
  11712. * before any entities with the WebGL component can be drawn.
  11713. *
  11714. * This method will automatically be called if no `Crafty.webgl.context` is
  11715. * found.
  11716. */
  11717. init: function () {
  11718. //check if canvas is supported
  11719. if (!Crafty.support.webgl) {
  11720. Crafty.trigger("NoWebGL");
  11721. Crafty.stop();
  11722. return;
  11723. }
  11724. //create an empty canvas element
  11725. var c;
  11726. c = document.createElement("canvas");
  11727. c.width = Crafty.viewport.width;
  11728. c.height = Crafty.viewport.height;
  11729. c.style.position = 'absolute';
  11730. c.style.left = "0px";
  11731. c.style.top = "0px";
  11732. Crafty.stage.elem.appendChild(c);
  11733. // Equivalent of initGL in sample prog
  11734. var gl;
  11735. try {
  11736. gl = c.getContext("webgl") || c.getContext("experimental-webgl");
  11737. gl.viewportWidth = c.width;
  11738. gl.viewportHeight = c.height;
  11739. } catch(e) {
  11740. //Do nothing!
  11741. }
  11742. if (!gl) {
  11743. Crafty.trigger("NoWebGL");
  11744. return;
  11745. }
  11746. this.context = gl;
  11747. this._canvas = c;
  11748. gl.clearColor(0.0, 0.0, 0.0, 0.0);
  11749. gl.enable(gl.DEPTH_TEST);
  11750. //gl.disable(gl.DEPTH_TEST);
  11751. //gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
  11752. //gl.enable(gl.BLEND);
  11753. //Bind rendering of canvas context (see drawing.js)
  11754. var webgl = this;
  11755. Crafty.uniqueBind("RenderScene", webgl.render);
  11756. Crafty.uniqueBind("ViewportResize", webgl._resize)
  11757. Crafty.uniqueBind("InvalidateViewport", function(){webgl.dirtyViewport = true;})
  11758. this.dirtyViewport = true;
  11759. console.log("webgl inited");
  11760. },
  11761. _resize: function(){
  11762. var c = Crafty.webgl._canvas;
  11763. c.width = Crafty.viewport.width;
  11764. c.height = Crafty.viewport.height;
  11765. gl.viewportWidth = c.widtxh;
  11766. gl.viewportHeight = c.height;
  11767. },
  11768. setViewportUniforms: function(shaderProgram){
  11769. gl = this.webgl.context;
  11770. gl.useProgram(shaderProgram);
  11771. var viewport = Crafty.viewport;
  11772. gl.uniform4f(shaderProgram.viewport, viewport._x, viewport._y, viewport._width, viewport._height);
  11773. },
  11774. render: function(rect){
  11775. //console.log("Rendering webgl context")
  11776. rect = rect || Crafty.viewport.rect();
  11777. var q = Crafty.map.search(rect),
  11778. i = 0,
  11779. l = q.length,
  11780. webgl = Crafty.webgl,
  11781. gl = webgl.context,
  11782. current;
  11783. // Set viewport and clear it
  11784. gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
  11785. gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
  11786. //We don't set the perspective because the default is what we WANT -- no depth
  11787. //Set the viewport uniform variables
  11788. var shaderProgram;
  11789. var programs = webgl.programs;
  11790. if (webgl.dirtyViewport){
  11791. for (var comp in programs){
  11792. webgl.setViewportUniforms(programs[comp]);
  11793. }
  11794. webgl.dirtyViewport = false;
  11795. }
  11796. var batchCount = 0;
  11797. shaderProgram = null;
  11798. for (; i < l; i++) {
  11799. current = q[i];
  11800. if (current._visible && current.__c.WebGL) {
  11801. if (shaderProgram !== current._shaderProgram){
  11802. if (shaderProgram !== null){
  11803. shaderProgram.renderBatch();
  11804. batchCount++;
  11805. }
  11806. shaderProgram = current._shaderProgram;
  11807. shaderProgram.pointer = 0;
  11808. shaderProgram.switchTo();
  11809. }
  11810. current.draw();
  11811. current._changed = false;
  11812. }
  11813. }
  11814. if (shaderProgram !== null){
  11815. shaderProgram.renderBatch();
  11816. batchCount++;
  11817. }
  11818. console.log("Batches: " + batchCount)
  11819. }
  11820. }
  11821. });
  11822. },{"./core.js":9}]},{},[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30])
  11823. ;/* |xGv00|ef4c7bb9647e9dafb7bb410a343d07b3 */