fabric.js 360 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124912591269127912891299130913191329133913491359136913791389139914091419142914391449145914691479148914991509151915291539154915591569157915891599160916191629163916491659166916791689169917091719172917391749175917691779178917991809181918291839184918591869187918891899190919191929193919491959196919791989199920092019202920392049205920692079208920992109211921292139214921592169217921892199220922192229223922492259226922792289229923092319232923392349235923692379238923992409241924292439244924592469247924892499250925192529253925492559256925792589259926092619262926392649265926692679268926992709271927292739274927592769277927892799280928192829283928492859286928792889289929092919292929392949295929692979298929993009301930293039304930593069307930893099310931193129313931493159316931793189319932093219322932393249325932693279328932993309331933293339334933593369337933893399340934193429343934493459346934793489349935093519352935393549355935693579358935993609361936293639364936593669367936893699370937193729373937493759376937793789379938093819382938393849385938693879388938993909391939293939394939593969397939893999400940194029403940494059406940794089409941094119412941394149415941694179418941994209421942294239424942594269427942894299430943194329433943494359436943794389439944094419442944394449445944694479448944994509451945294539454945594569457945894599460946194629463946494659466946794689469947094719472947394749475947694779478947994809481948294839484948594869487948894899490949194929493949494959496949794989499950095019502950395049505950695079508950995109511951295139514951595169517951895199520952195229523952495259526952795289529953095319532953395349535953695379538953995409541954295439544954595469547954895499550955195529553955495559556955795589559956095619562956395649565956695679568956995709571957295739574957595769577957895799580958195829583958495859586958795889589959095919592959395949595959695979598959996009601960296039604960596069607960896099610961196129613961496159616961796189619962096219622962396249625962696279628962996309631963296339634963596369637963896399640964196429643964496459646964796489649965096519652965396549655965696579658965996609661966296639664966596669667966896699670967196729673967496759676967796789679968096819682968396849685968696879688968996909691969296939694969596969697969896999700970197029703970497059706970797089709971097119712971397149715971697179718971997209721972297239724972597269727972897299730973197329733973497359736973797389739974097419742974397449745974697479748974997509751975297539754975597569757975897599760976197629763976497659766976797689769977097719772977397749775977697779778977997809781978297839784978597869787978897899790979197929793979497959796979797989799980098019802980398049805980698079808980998109811981298139814981598169817981898199820982198229823982498259826982798289829983098319832983398349835983698379838983998409841984298439844984598469847984898499850985198529853985498559856985798589859986098619862986398649865986698679868986998709871987298739874987598769877987898799880988198829883988498859886988798889889989098919892989398949895989698979898989999009901990299039904990599069907990899099910991199129913991499159916991799189919992099219922992399249925992699279928992999309931993299339934993599369937993899399940994199429943994499459946994799489949995099519952995399549955995699579958995999609961996299639964996599669967996899699970997199729973997499759976997799789979998099819982998399849985998699879988998999909991999299939994999599969997999899991000010001100021000310004100051000610007100081000910010100111001210013100141001510016100171001810019100201002110022100231002410025100261002710028100291003010031100321003310034100351003610037100381003910040100411004210043100441004510046100471004810049100501005110052100531005410055100561005710058100591006010061100621006310064100651006610067100681006910070100711007210073100741007510076100771007810079100801008110082100831008410085100861008710088100891009010091100921009310094100951009610097100981009910100101011010210103101041010510106101071010810109101101011110112101131011410115101161011710118101191012010121101221012310124101251012610127101281012910130101311013210133101341013510136101371013810139101401014110142101431014410145101461014710148101491015010151101521015310154101551015610157101581015910160101611016210163101641016510166101671016810169101701017110172101731017410175101761017710178101791018010181101821018310184101851018610187101881018910190101911019210193101941019510196101971019810199102001020110202102031020410205102061020710208102091021010211102121021310214102151021610217102181021910220102211022210223102241022510226102271022810229102301023110232102331023410235102361023710238102391024010241102421024310244102451024610247102481024910250102511025210253102541025510256102571025810259102601026110262102631026410265102661026710268102691027010271102721027310274102751027610277102781027910280102811028210283102841028510286102871028810289102901029110292102931029410295102961029710298102991030010301103021030310304103051030610307103081030910310103111031210313103141031510316103171031810319103201032110322103231032410325103261032710328103291033010331103321033310334103351033610337103381033910340103411034210343103441034510346103471034810349103501035110352103531035410355103561035710358103591036010361103621036310364103651036610367103681036910370103711037210373103741037510376103771037810379103801038110382103831038410385103861038710388103891039010391103921039310394103951039610397103981039910400104011040210403104041040510406104071040810409104101041110412104131041410415104161041710418104191042010421104221042310424104251042610427104281042910430104311043210433104341043510436104371043810439104401044110442104431044410445104461044710448104491045010451104521045310454104551045610457104581045910460104611046210463104641046510466104671046810469104701047110472104731047410475104761047710478104791048010481104821048310484104851048610487104881048910490104911049210493104941049510496104971049810499105001050110502105031050410505105061050710508105091051010511105121051310514105151051610517105181051910520105211052210523105241052510526105271052810529105301053110532105331053410535105361053710538105391054010541105421054310544105451054610547105481054910550105511055210553105541055510556105571055810559105601056110562105631056410565105661056710568105691057010571105721057310574105751057610577105781057910580105811058210583105841058510586105871058810589105901059110592105931059410595105961059710598105991060010601106021060310604106051060610607106081060910610106111061210613106141061510616106171061810619106201062110622106231062410625106261062710628106291063010631106321063310634106351063610637106381063910640106411064210643106441064510646106471064810649106501065110652106531065410655106561065710658106591066010661106621066310664106651066610667106681066910670106711067210673106741067510676106771067810679106801068110682106831068410685106861068710688106891069010691106921069310694106951069610697106981069910700107011070210703107041070510706107071070810709107101071110712107131071410715107161071710718107191072010721107221072310724107251072610727107281072910730107311073210733107341073510736107371073810739107401074110742107431074410745107461074710748107491075010751107521075310754107551075610757107581075910760107611076210763107641076510766107671076810769107701077110772107731077410775107761077710778107791078010781107821078310784107851078610787107881078910790107911079210793107941079510796107971079810799108001080110802108031080410805108061080710808108091081010811108121081310814108151081610817108181081910820108211082210823108241082510826108271082810829108301083110832108331083410835108361083710838108391084010841108421084310844108451084610847108481084910850108511085210853108541085510856108571085810859108601086110862108631086410865108661086710868108691087010871108721087310874108751087610877108781087910880108811088210883108841088510886108871088810889108901089110892108931089410895108961089710898108991090010901109021090310904109051090610907109081090910910109111091210913109141091510916109171091810919109201092110922109231092410925109261092710928109291093010931109321093310934109351093610937109381093910940109411094210943109441094510946109471094810949109501095110952109531095410955109561095710958109591096010961109621096310964109651096610967109681096910970109711097210973109741097510976109771097810979109801098110982109831098410985109861098710988109891099010991109921099310994109951099610997109981099911000110011100211003110041100511006110071100811009110101101111012110131101411015110161101711018110191102011021110221102311024110251102611027110281102911030110311103211033110341103511036110371103811039110401104111042110431104411045110461104711048110491105011051110521105311054110551105611057110581105911060110611106211063110641106511066110671106811069110701107111072110731107411075110761107711078110791108011081110821108311084110851108611087110881108911090110911109211093110941109511096110971109811099111001110111102111031110411105111061110711108111091111011111111121111311114111151111611117111181111911120111211112211123111241112511126111271112811129111301113111132111331113411135111361113711138111391114011141111421114311144111451114611147111481114911150111511115211153111541115511156111571115811159111601116111162111631116411165111661116711168111691117011171111721117311174111751117611177111781117911180111811118211183111841118511186111871118811189111901119111192111931119411195111961119711198111991120011201112021120311204112051120611207112081120911210112111121211213112141121511216112171121811219112201122111222112231122411225112261122711228112291123011231112321123311234112351123611237112381123911240112411124211243112441124511246112471124811249112501125111252112531125411255112561125711258112591126011261112621126311264112651126611267112681126911270112711127211273112741127511276112771127811279112801128111282112831128411285112861128711288112891129011291112921129311294112951129611297112981129911300113011130211303113041130511306113071130811309113101131111312113131131411315113161131711318113191132011321113221132311324113251132611327113281132911330113311133211333113341133511336113371133811339113401134111342113431134411345113461134711348113491135011351113521135311354113551135611357113581135911360113611136211363113641136511366113671136811369113701137111372113731137411375113761137711378113791138011381113821138311384113851138611387113881138911390113911139211393113941139511396113971139811399114001140111402114031140411405114061140711408114091141011411114121141311414114151141611417114181141911420114211142211423114241142511426114271142811429114301143111432114331143411435114361143711438114391144011441114421144311444114451144611447114481144911450114511145211453114541145511456114571145811459114601146111462114631146411465114661146711468114691147011471114721147311474114751147611477114781147911480114811148211483114841148511486114871148811489114901149111492114931149411495114961149711498114991150011501115021150311504115051150611507115081150911510115111151211513115141151511516115171151811519115201152111522115231152411525115261152711528115291153011531115321153311534115351153611537115381153911540115411154211543115441154511546115471154811549115501155111552115531155411555115561155711558115591156011561115621156311564115651156611567115681156911570115711157211573115741157511576115771157811579115801158111582115831158411585115861158711588115891159011591115921159311594115951159611597115981159911600116011160211603116041160511606116071160811609116101161111612116131161411615116161161711618116191162011621116221162311624116251162611627116281162911630116311163211633116341163511636116371163811639116401164111642116431164411645116461164711648116491165011651116521165311654116551165611657116581165911660116611166211663116641166511666116671166811669116701167111672116731167411675116761167711678116791168011681116821168311684116851168611687116881168911690116911169211693116941169511696116971169811699117001170111702117031170411705117061170711708117091171011711117121171311714117151171611717117181171911720117211172211723117241172511726117271172811729117301173111732117331173411735117361173711738117391174011741117421174311744117451174611747117481174911750117511175211753117541175511756117571175811759117601176111762117631176411765117661176711768117691177011771117721177311774117751177611777117781177911780117811178211783117841178511786117871178811789117901179111792117931179411795117961179711798117991180011801118021180311804118051180611807118081180911810118111181211813118141181511816118171181811819118201182111822118231182411825118261182711828118291183011831118321183311834118351183611837118381183911840118411184211843118441184511846118471184811849118501185111852118531185411855118561185711858118591186011861118621186311864118651186611867118681186911870118711187211873118741187511876118771187811879118801188111882118831188411885118861188711888118891189011891118921189311894118951189611897118981189911900119011190211903119041190511906119071190811909119101191111912119131191411915119161191711918119191192011921119221192311924119251192611927119281192911930119311193211933119341193511936119371193811939119401194111942119431194411945119461194711948119491195011951119521195311954119551195611957119581195911960119611196211963119641196511966119671196811969119701197111972119731197411975119761197711978119791198011981119821198311984119851198611987119881198911990119911199211993119941199511996119971199811999120001200112002120031200412005120061200712008120091201012011120121201312014120151201612017120181201912020120211202212023120241202512026120271202812029120301203112032120331203412035120361203712038120391204012041120421204312044120451204612047120481204912050120511205212053
  1. /* build: `node build.js modules= minifier=uglifyjs` */
  2. /*! Fabric.js Copyright 2008-2015, Printio (Juriy Zaytsev, Maxim Chernyak) */
  3. var fabric = fabric || { version: '2.3.3' };
  4. if (typeof exports !== 'undefined') {
  5. exports.fabric = fabric;
  6. }
  7. /* _AMD_START_ */
  8. else if (typeof define === 'function' && define.amd) {
  9. define([], function() { return fabric; });
  10. }
  11. /* _AMD_END_ */
  12. if (typeof document !== 'undefined' && typeof window !== 'undefined') {
  13. fabric.document = document;
  14. fabric.window = window;
  15. }
  16. else {
  17. // assume we're running under node.js when document/window are not present
  18. fabric.document = require('jsdom')
  19. .jsdom(
  20. decodeURIComponent('%3C!DOCTYPE%20html%3E%3Chtml%3E%3Chead%3E%3C%2Fhead%3E%3Cbody%3E%3C%2Fbody%3E%3C%2Fhtml%3E'),
  21. { features: {
  22. FetchExternalResources: ['img']
  23. }
  24. });
  25. fabric.jsdomImplForWrapper = require('jsdom/lib/jsdom/living/generated/utils').implForWrapper;
  26. fabric.nodeCanvas = require('jsdom/lib/jsdom/utils').Canvas;
  27. fabric.window = fabric.document.defaultView;
  28. DOMParser = require('xmldom').DOMParser;
  29. }
  30. /**
  31. * True when in environment that supports touch events
  32. * @type boolean
  33. */
  34. fabric.isTouchSupported = 'ontouchstart' in fabric.window;
  35. /**
  36. * True when in environment that's probably Node.js
  37. * @type boolean
  38. */
  39. fabric.isLikelyNode = typeof Buffer !== 'undefined' &&
  40. typeof window === 'undefined';
  41. /**
  42. * Pixel per Inch as a default value set to 96. Can be changed for more realistic conversion.
  43. */
  44. fabric.DPI = 96;
  45. fabric.reNum = '(?:[-+]?(?:\\d+|\\d*\\.\\d+)(?:e[-+]?\\d+)?)';
  46. fabric.fontPaths = { };
  47. fabric.iMatrix = [1, 0, 0, 1, 0, 0];
  48. fabric.canvasModule = 'canvas';
  49. /**
  50. * Pixel limit for cache canvases. 1Mpx , 4Mpx should be fine.
  51. * @since 1.7.14
  52. * @type Number
  53. * @default
  54. */
  55. fabric.perfLimitSizeTotal = 2097152;
  56. /**
  57. * Pixel limit for cache canvases width or height. IE fixes the maximum at 5000
  58. * @since 1.7.14
  59. * @type Number
  60. * @default
  61. */
  62. fabric.maxCacheSideLimit = 4096;
  63. /**
  64. * Lowest pixel limit for cache canvases, set at 256PX
  65. * @since 1.7.14
  66. * @type Number
  67. * @default
  68. */
  69. fabric.minCacheSideLimit = 256;
  70. /**
  71. * Cache Object for widths of chars in text rendering.
  72. */
  73. fabric.charWidthsCache = { };
  74. /**
  75. * if webgl is enabled and available, textureSize will determine the size
  76. * of the canvas backend
  77. * @since 2.0.0
  78. * @type Number
  79. * @default
  80. */
  81. fabric.textureSize = 2048;
  82. /**
  83. * Enable webgl for filtering picture is available
  84. * A filtering backend will be initialized, this will both take memory and
  85. * time since a default 2048x2048 canvas will be created for the gl context
  86. * @since 2.0.0
  87. * @type Boolean
  88. * @default
  89. */
  90. fabric.enableGLFiltering = true;
  91. /**
  92. * Device Pixel Ratio
  93. * @see https://developer.apple.com/library/safari/documentation/AudioVideo/Conceptual/HTML-canvas-guide/SettingUptheCanvas/SettingUptheCanvas.html
  94. */
  95. fabric.devicePixelRatio = fabric.window.devicePixelRatio ||
  96. fabric.window.webkitDevicePixelRatio ||
  97. fabric.window.mozDevicePixelRatio ||
  98. 1;
  99. /**
  100. * Browser-specific constant to adjust CanvasRenderingContext2D.shadowBlur value,
  101. * which is unitless and not rendered equally across browsers.
  102. *
  103. * Values that work quite well (as of October 2017) are:
  104. * - Chrome: 1.5
  105. * - Edge: 1.75
  106. * - Firefox: 0.9
  107. * - Safari: 0.95
  108. *
  109. * @since 2.0.0
  110. * @type Number
  111. * @default 1
  112. */
  113. fabric.browserShadowBlurConstant = 1;
  114. fabric.initFilterBackend = function() {
  115. if (fabric.enableGLFiltering && fabric.isWebglSupported && fabric.isWebglSupported(fabric.textureSize)) {
  116. console.log('max texture size: ' + fabric.maxTextureSize);
  117. return (new fabric.WebglFilterBackend({ tileSize: fabric.textureSize }));
  118. }
  119. else if (fabric.Canvas2dFilterBackend) {
  120. return (new fabric.Canvas2dFilterBackend());
  121. }
  122. };
  123. (function() {
  124. /**
  125. * @private
  126. * @param {String} eventName
  127. * @param {Function} handler
  128. */
  129. function _removeEventListener(eventName, handler) {
  130. if (!this.__eventListeners[eventName]) {
  131. return;
  132. }
  133. var eventListener = this.__eventListeners[eventName];
  134. if (handler) {
  135. eventListener[eventListener.indexOf(handler)] = false;
  136. }
  137. else {
  138. fabric.util.array.fill(eventListener, false);
  139. }
  140. }
  141. /**
  142. * Observes specified event
  143. * @deprecated `observe` deprecated since 0.8.34 (use `on` instead)
  144. * @memberOf fabric.Observable
  145. * @alias on
  146. * @param {String|Object} eventName Event name (eg. 'after:render') or object with key/value pairs (eg. {'after:render': handler, 'selection:cleared': handler})
  147. * @param {Function} handler Function that receives a notification when an event of the specified type occurs
  148. * @return {Self} thisArg
  149. * @chainable
  150. */
  151. function observe(eventName, handler) {
  152. if (!this.__eventListeners) {
  153. this.__eventListeners = { };
  154. }
  155. // one object with key/value pairs was passed
  156. if (arguments.length === 1) {
  157. for (var prop in eventName) {
  158. this.on(prop, eventName[prop]);
  159. }
  160. }
  161. else {
  162. if (!this.__eventListeners[eventName]) {
  163. this.__eventListeners[eventName] = [];
  164. }
  165. this.__eventListeners[eventName].push(handler);
  166. }
  167. return this;
  168. }
  169. /**
  170. * Stops event observing for a particular event handler. Calling this method
  171. * without arguments removes all handlers for all events
  172. * @deprecated `stopObserving` deprecated since 0.8.34 (use `off` instead)
  173. * @memberOf fabric.Observable
  174. * @alias off
  175. * @param {String|Object} eventName Event name (eg. 'after:render') or object with key/value pairs (eg. {'after:render': handler, 'selection:cleared': handler})
  176. * @param {Function} handler Function to be deleted from EventListeners
  177. * @return {Self} thisArg
  178. * @chainable
  179. */
  180. function stopObserving(eventName, handler) {
  181. if (!this.__eventListeners) {
  182. return;
  183. }
  184. // remove all key/value pairs (event name -> event handler)
  185. if (arguments.length === 0) {
  186. for (eventName in this.__eventListeners) {
  187. _removeEventListener.call(this, eventName);
  188. }
  189. }
  190. // one object with key/value pairs was passed
  191. else if (arguments.length === 1 && typeof arguments[0] === 'object') {
  192. for (var prop in eventName) {
  193. _removeEventListener.call(this, prop, eventName[prop]);
  194. }
  195. }
  196. else {
  197. _removeEventListener.call(this, eventName, handler);
  198. }
  199. return this;
  200. }
  201. /**
  202. * Fires event with an optional options object
  203. * @deprecated `fire` deprecated since 1.0.7 (use `trigger` instead)
  204. * @memberOf fabric.Observable
  205. * @alias trigger
  206. * @param {String} eventName Event name to fire
  207. * @param {Object} [options] Options object
  208. * @return {Self} thisArg
  209. * @chainable
  210. */
  211. function fire(eventName, options) {
  212. if (!this.__eventListeners) {
  213. return;
  214. }
  215. var listenersForEvent = this.__eventListeners[eventName];
  216. if (!listenersForEvent) {
  217. return;
  218. }
  219. for (var i = 0, len = listenersForEvent.length; i < len; i++) {
  220. listenersForEvent[i] && listenersForEvent[i].call(this, options || { });
  221. }
  222. this.__eventListeners[eventName] = listenersForEvent.filter(function(value) {
  223. return value !== false;
  224. });
  225. return this;
  226. }
  227. /**
  228. * @namespace fabric.Observable
  229. * @tutorial {@link http://fabricjs.com/fabric-intro-part-2#events}
  230. * @see {@link http://fabricjs.com/events|Events demo}
  231. */
  232. fabric.Observable = {
  233. observe: observe,
  234. stopObserving: stopObserving,
  235. fire: fire,
  236. on: observe,
  237. off: stopObserving,
  238. trigger: fire
  239. };
  240. })();
  241. /**
  242. * @namespace fabric.Collection
  243. */
  244. fabric.Collection = {
  245. _objects: [],
  246. /**
  247. * Adds objects to collection, Canvas or Group, then renders canvas
  248. * (if `renderOnAddRemove` is not `false`).
  249. * in case of Group no changes to bounding box are made.
  250. * Objects should be instances of (or inherit from) fabric.Object
  251. * Use of this function is highly discouraged for groups.
  252. * you can add a bunch of objects with the add method but then you NEED
  253. * to run a addWithUpdate call for the Group class or position/bbox will be wrong.
  254. * @param {...fabric.Object} object Zero or more fabric instances
  255. * @return {Self} thisArg
  256. * @chainable
  257. */
  258. add: function () {
  259. this._objects.push.apply(this._objects, arguments);
  260. if (this._onObjectAdded) {
  261. for (var i = 0, length = arguments.length; i < length; i++) {
  262. this._onObjectAdded(arguments[i]);
  263. }
  264. }
  265. this.renderOnAddRemove && this.requestRenderAll();
  266. return this;
  267. },
  268. /**
  269. * Inserts an object into collection at specified index, then renders canvas (if `renderOnAddRemove` is not `false`)
  270. * An object should be an instance of (or inherit from) fabric.Object
  271. * Use of this function is highly discouraged for groups.
  272. * you can add a bunch of objects with the insertAt method but then you NEED
  273. * to run a addWithUpdate call for the Group class or position/bbox will be wrong.
  274. * @param {Object} object Object to insert
  275. * @param {Number} index Index to insert object at
  276. * @param {Boolean} nonSplicing When `true`, no splicing (shifting) of objects occurs
  277. * @return {Self} thisArg
  278. * @chainable
  279. */
  280. insertAt: function (object, index, nonSplicing) {
  281. var objects = this.getObjects();
  282. if (nonSplicing) {
  283. objects[index] = object;
  284. }
  285. else {
  286. objects.splice(index, 0, object);
  287. }
  288. this._onObjectAdded && this._onObjectAdded(object);
  289. this.renderOnAddRemove && this.requestRenderAll();
  290. return this;
  291. },
  292. /**
  293. * Removes objects from a collection, then renders canvas (if `renderOnAddRemove` is not `false`)
  294. * @param {...fabric.Object} object Zero or more fabric instances
  295. * @return {Self} thisArg
  296. * @chainable
  297. */
  298. remove: function() {
  299. var objects = this.getObjects(),
  300. index, somethingRemoved = false;
  301. for (var i = 0, length = arguments.length; i < length; i++) {
  302. index = objects.indexOf(arguments[i]);
  303. // only call onObjectRemoved if an object was actually removed
  304. if (index !== -1) {
  305. somethingRemoved = true;
  306. objects.splice(index, 1);
  307. this._onObjectRemoved && this._onObjectRemoved(arguments[i]);
  308. }
  309. }
  310. this.renderOnAddRemove && somethingRemoved && this.requestRenderAll();
  311. return this;
  312. },
  313. /**
  314. * Executes given function for each object in this group
  315. * @param {Function} callback
  316. * Callback invoked with current object as first argument,
  317. * index - as second and an array of all objects - as third.
  318. * Callback is invoked in a context of Global Object (e.g. `window`)
  319. * when no `context` argument is given
  320. *
  321. * @param {Object} context Context (aka thisObject)
  322. * @return {Self} thisArg
  323. * @chainable
  324. */
  325. forEachObject: function(callback, context) {
  326. var objects = this.getObjects();
  327. for (var i = 0, len = objects.length; i < len; i++) {
  328. callback.call(context, objects[i], i, objects);
  329. }
  330. return this;
  331. },
  332. /**
  333. * Returns an array of children objects of this instance
  334. * Type parameter introduced in 1.3.10
  335. * @param {String} [type] When specified, only objects of this type are returned
  336. * @return {Array}
  337. */
  338. getObjects: function(type) {
  339. if (typeof type === 'undefined') {
  340. return this._objects;
  341. }
  342. return this._objects.filter(function(o) {
  343. return o.type === type;
  344. });
  345. },
  346. /**
  347. * Returns object at specified index
  348. * @param {Number} index
  349. * @return {Self} thisArg
  350. */
  351. item: function (index) {
  352. return this.getObjects()[index];
  353. },
  354. /**
  355. * Returns true if collection contains no objects
  356. * @return {Boolean} true if collection is empty
  357. */
  358. isEmpty: function () {
  359. return this.getObjects().length === 0;
  360. },
  361. /**
  362. * Returns a size of a collection (i.e: length of an array containing its objects)
  363. * @return {Number} Collection size
  364. */
  365. size: function() {
  366. return this.getObjects().length;
  367. },
  368. /**
  369. * Returns true if collection contains an object
  370. * @param {Object} object Object to check against
  371. * @return {Boolean} `true` if collection contains an object
  372. */
  373. contains: function(object) {
  374. return this.getObjects().indexOf(object) > -1;
  375. },
  376. /**
  377. * Returns number representation of a collection complexity
  378. * @return {Number} complexity
  379. */
  380. complexity: function () {
  381. return this.getObjects().reduce(function (memo, current) {
  382. memo += current.complexity ? current.complexity() : 0;
  383. return memo;
  384. }, 0);
  385. }
  386. };
  387. /**
  388. * @namespace fabric.CommonMethods
  389. */
  390. fabric.CommonMethods = {
  391. /**
  392. * Sets object's properties from options
  393. * @param {Object} [options] Options object
  394. */
  395. _setOptions: function(options) {
  396. for (var prop in options) {
  397. this.set(prop, options[prop]);
  398. }
  399. },
  400. /**
  401. * @private
  402. * @param {Object} [filler] Options object
  403. * @param {String} [property] property to set the Gradient to
  404. */
  405. _initGradient: function(filler, property) {
  406. if (filler && filler.colorStops && !(filler instanceof fabric.Gradient)) {
  407. this.set(property, new fabric.Gradient(filler));
  408. }
  409. },
  410. /**
  411. * @private
  412. * @param {Object} [filler] Options object
  413. * @param {String} [property] property to set the Pattern to
  414. * @param {Function} [callback] callback to invoke after pattern load
  415. */
  416. _initPattern: function(filler, property, callback) {
  417. if (filler && filler.source && !(filler instanceof fabric.Pattern)) {
  418. this.set(property, new fabric.Pattern(filler, callback));
  419. }
  420. else {
  421. callback && callback();
  422. }
  423. },
  424. /**
  425. * @private
  426. * @param {Object} [options] Options object
  427. */
  428. _initClipping: function(options) {
  429. if (!options.clipTo || typeof options.clipTo !== 'string') {
  430. return;
  431. }
  432. var functionBody = fabric.util.getFunctionBody(options.clipTo);
  433. if (typeof functionBody !== 'undefined') {
  434. this.clipTo = new Function('ctx', functionBody);
  435. }
  436. },
  437. /**
  438. * @private
  439. */
  440. _setObject: function(obj) {
  441. for (var prop in obj) {
  442. this._set(prop, obj[prop]);
  443. }
  444. },
  445. /**
  446. * Sets property to a given value. When changing position/dimension -related properties (left, top, scale, angle, etc.) `set` does not update position of object's borders/controls. If you need to update those, call `setCoords()`.
  447. * @param {String|Object} key Property name or object (if object, iterate over the object properties)
  448. * @param {Object|Function} value Property value (if function, the value is passed into it and its return value is used as a new one)
  449. * @return {fabric.Object} thisArg
  450. * @chainable
  451. */
  452. set: function(key, value) {
  453. if (typeof key === 'object') {
  454. this._setObject(key);
  455. }
  456. else {
  457. if (typeof value === 'function' && key !== 'clipTo') {
  458. this._set(key, value(this.get(key)));
  459. }
  460. else {
  461. this._set(key, value);
  462. }
  463. }
  464. return this;
  465. },
  466. _set: function(key, value) {
  467. this[key] = value;
  468. },
  469. /**
  470. * Toggles specified property from `true` to `false` or from `false` to `true`
  471. * @param {String} property Property to toggle
  472. * @return {fabric.Object} thisArg
  473. * @chainable
  474. */
  475. toggle: function(property) {
  476. var value = this.get(property);
  477. if (typeof value === 'boolean') {
  478. this.set(property, !value);
  479. }
  480. return this;
  481. },
  482. /**
  483. * Basic getter
  484. * @param {String} property Property name
  485. * @return {*} value of a property
  486. */
  487. get: function(property) {
  488. return this[property];
  489. }
  490. };
  491. (function(global) {
  492. var sqrt = Math.sqrt,
  493. atan2 = Math.atan2,
  494. pow = Math.pow,
  495. abs = Math.abs,
  496. PiBy180 = Math.PI / 180,
  497. PiBy2 = Math.PI / 2;
  498. /**
  499. * @namespace fabric.util
  500. */
  501. fabric.util = {
  502. /**
  503. * Calculate the cos of an angle, avoiding returning floats for known results
  504. * @static
  505. * @memberOf fabric.util
  506. * @param {Number} angle the angle in radians or in degree
  507. * @return {Number}
  508. */
  509. cos: function(angle) {
  510. if (angle === 0) { return 1; }
  511. if (angle < 0) {
  512. // cos(a) = cos(-a)
  513. angle = -angle;
  514. }
  515. var angleSlice = angle / PiBy2;
  516. switch (angleSlice) {
  517. case 1: case 3: return 0;
  518. case 2: return -1;
  519. }
  520. return Math.cos(angle);
  521. },
  522. /**
  523. * Calculate the sin of an angle, avoiding returning floats for known results
  524. * @static
  525. * @memberOf fabric.util
  526. * @param {Number} angle the angle in radians or in degree
  527. * @return {Number}
  528. */
  529. sin: function(angle) {
  530. if (angle === 0) { return 0; }
  531. var angleSlice = angle / PiBy2, sign = 1;
  532. if (angle < 0) {
  533. // sin(-a) = -sin(a)
  534. sign = -1;
  535. }
  536. switch (angleSlice) {
  537. case 1: return sign;
  538. case 2: return 0;
  539. case 3: return -sign;
  540. }
  541. return Math.sin(angle);
  542. },
  543. /**
  544. * Removes value from an array.
  545. * Presence of value (and its position in an array) is determined via `Array.prototype.indexOf`
  546. * @static
  547. * @memberOf fabric.util
  548. * @param {Array} array
  549. * @param {*} value
  550. * @return {Array} original array
  551. */
  552. removeFromArray: function(array, value) {
  553. var idx = array.indexOf(value);
  554. if (idx !== -1) {
  555. array.splice(idx, 1);
  556. }
  557. return array;
  558. },
  559. /**
  560. * Returns random number between 2 specified ones.
  561. * @static
  562. * @memberOf fabric.util
  563. * @param {Number} min lower limit
  564. * @param {Number} max upper limit
  565. * @return {Number} random value (between min and max)
  566. */
  567. getRandomInt: function(min, max) {
  568. return Math.floor(Math.random() * (max - min + 1)) + min;
  569. },
  570. /**
  571. * Transforms degrees to radians.
  572. * @static
  573. * @memberOf fabric.util
  574. * @param {Number} degrees value in degrees
  575. * @return {Number} value in radians
  576. */
  577. degreesToRadians: function(degrees) {
  578. return degrees * PiBy180;
  579. },
  580. /**
  581. * Transforms radians to degrees.
  582. * @static
  583. * @memberOf fabric.util
  584. * @param {Number} radians value in radians
  585. * @return {Number} value in degrees
  586. */
  587. radiansToDegrees: function(radians) {
  588. return radians / PiBy180;
  589. },
  590. /**
  591. * Rotates `point` around `origin` with `radians`
  592. * @static
  593. * @memberOf fabric.util
  594. * @param {fabric.Point} point The point to rotate
  595. * @param {fabric.Point} origin The origin of the rotation
  596. * @param {Number} radians The radians of the angle for the rotation
  597. * @return {fabric.Point} The new rotated point
  598. */
  599. rotatePoint: function(point, origin, radians) {
  600. point.subtractEquals(origin);
  601. var v = fabric.util.rotateVector(point, radians);
  602. return new fabric.Point(v.x, v.y).addEquals(origin);
  603. },
  604. /**
  605. * Rotates `vector` with `radians`
  606. * @static
  607. * @memberOf fabric.util
  608. * @param {Object} vector The vector to rotate (x and y)
  609. * @param {Number} radians The radians of the angle for the rotation
  610. * @return {Object} The new rotated point
  611. */
  612. rotateVector: function(vector, radians) {
  613. var sin = fabric.util.sin(radians),
  614. cos = fabric.util.cos(radians),
  615. rx = vector.x * cos - vector.y * sin,
  616. ry = vector.x * sin + vector.y * cos;
  617. return {
  618. x: rx,
  619. y: ry
  620. };
  621. },
  622. /**
  623. * Apply transform t to point p
  624. * @static
  625. * @memberOf fabric.util
  626. * @param {fabric.Point} p The point to transform
  627. * @param {Array} t The transform
  628. * @param {Boolean} [ignoreOffset] Indicates that the offset should not be applied
  629. * @return {fabric.Point} The transformed point
  630. */
  631. transformPoint: function(p, t, ignoreOffset) {
  632. if (ignoreOffset) {
  633. return new fabric.Point(
  634. t[0] * p.x + t[2] * p.y,
  635. t[1] * p.x + t[3] * p.y
  636. );
  637. }
  638. return new fabric.Point(
  639. t[0] * p.x + t[2] * p.y + t[4],
  640. t[1] * p.x + t[3] * p.y + t[5]
  641. );
  642. },
  643. /**
  644. * Returns coordinates of points's bounding rectangle (left, top, width, height)
  645. * @param {Array} points 4 points array
  646. * @return {Object} Object with left, top, width, height properties
  647. */
  648. makeBoundingBoxFromPoints: function(points) {
  649. var xPoints = [points[0].x, points[1].x, points[2].x, points[3].x],
  650. minX = fabric.util.array.min(xPoints),
  651. maxX = fabric.util.array.max(xPoints),
  652. width = maxX - minX,
  653. yPoints = [points[0].y, points[1].y, points[2].y, points[3].y],
  654. minY = fabric.util.array.min(yPoints),
  655. maxY = fabric.util.array.max(yPoints),
  656. height = maxY - minY;
  657. return {
  658. left: minX,
  659. top: minY,
  660. width: width,
  661. height: height
  662. };
  663. },
  664. /**
  665. * Invert transformation t
  666. * @static
  667. * @memberOf fabric.util
  668. * @param {Array} t The transform
  669. * @return {Array} The inverted transform
  670. */
  671. invertTransform: function(t) {
  672. var a = 1 / (t[0] * t[3] - t[1] * t[2]),
  673. r = [a * t[3], -a * t[1], -a * t[2], a * t[0]],
  674. o = fabric.util.transformPoint({ x: t[4], y: t[5] }, r, true);
  675. r[4] = -o.x;
  676. r[5] = -o.y;
  677. return r;
  678. },
  679. /**
  680. * A wrapper around Number#toFixed, which contrary to native method returns number, not string.
  681. * @static
  682. * @memberOf fabric.util
  683. * @param {Number|String} number number to operate on
  684. * @param {Number} fractionDigits number of fraction digits to "leave"
  685. * @return {Number}
  686. */
  687. toFixed: function(number, fractionDigits) {
  688. return parseFloat(Number(number).toFixed(fractionDigits));
  689. },
  690. /**
  691. * Converts from attribute value to pixel value if applicable.
  692. * Returns converted pixels or original value not converted.
  693. * @param {Number|String} value number to operate on
  694. * @param {Number} fontSize
  695. * @return {Number|String}
  696. */
  697. parseUnit: function(value, fontSize) {
  698. var unit = /\D{0,2}$/.exec(value),
  699. number = parseFloat(value);
  700. if (!fontSize) {
  701. fontSize = fabric.Text.DEFAULT_SVG_FONT_SIZE;
  702. }
  703. switch (unit[0]) {
  704. case 'mm':
  705. return number * fabric.DPI / 25.4;
  706. case 'cm':
  707. return number * fabric.DPI / 2.54;
  708. case 'in':
  709. return number * fabric.DPI;
  710. case 'pt':
  711. return number * fabric.DPI / 72; // or * 4 / 3
  712. case 'pc':
  713. return number * fabric.DPI / 72 * 12; // or * 16
  714. case 'em':
  715. return number * fontSize;
  716. default:
  717. return number;
  718. }
  719. },
  720. /**
  721. * Function which always returns `false`.
  722. * @static
  723. * @memberOf fabric.util
  724. * @return {Boolean}
  725. */
  726. falseFunction: function() {
  727. return false;
  728. },
  729. /**
  730. * Returns klass "Class" object of given namespace
  731. * @memberOf fabric.util
  732. * @param {String} type Type of object (eg. 'circle')
  733. * @param {String} namespace Namespace to get klass "Class" object from
  734. * @return {Object} klass "Class"
  735. */
  736. getKlass: function(type, namespace) {
  737. // capitalize first letter only
  738. type = fabric.util.string.camelize(type.charAt(0).toUpperCase() + type.slice(1));
  739. return fabric.util.resolveNamespace(namespace)[type];
  740. },
  741. /**
  742. * Returns array of attributes for given svg that fabric parses
  743. * @memberOf fabric.util
  744. * @param {String} type Type of svg element (eg. 'circle')
  745. * @return {Array} string names of supported attributes
  746. */
  747. getSvgAttributes: function(type) {
  748. var attributes = [
  749. 'instantiated_by_use',
  750. 'style',
  751. 'id',
  752. 'class'
  753. ];
  754. switch (type) {
  755. case 'linearGradient':
  756. attributes = attributes.concat(['x1', 'y1', 'x2', 'y2', 'gradientUnits', 'gradientTransform']);
  757. break;
  758. case 'radialGradient':
  759. attributes = attributes.concat(['gradientUnits', 'gradientTransform', 'cx', 'cy', 'r', 'fx', 'fy', 'fr']);
  760. break;
  761. case 'stop':
  762. attributes = attributes.concat(['offset', 'stop-color', 'stop-opacity']);
  763. break;
  764. }
  765. return attributes;
  766. },
  767. /**
  768. * Returns object of given namespace
  769. * @memberOf fabric.util
  770. * @param {String} namespace Namespace string e.g. 'fabric.Image.filter' or 'fabric'
  771. * @return {Object} Object for given namespace (default fabric)
  772. */
  773. resolveNamespace: function(namespace) {
  774. if (!namespace) {
  775. return fabric;
  776. }
  777. var parts = namespace.split('.'),
  778. len = parts.length, i,
  779. obj = global || fabric.window;
  780. for (i = 0; i < len; ++i) {
  781. obj = obj[parts[i]];
  782. }
  783. return obj;
  784. },
  785. /**
  786. * Loads c_image element from given url and passes it to a callback
  787. * @memberOf fabric.util
  788. * @param {String} url URL representing an c_image
  789. * @param {Function} callback Callback; invoked with loaded c_image
  790. * @param {*} [context] Context to invoke callback in
  791. * @param {Object} [crossOrigin] crossOrigin value to set c_image element to
  792. */
  793. loadImage: function(url, callback, context, crossOrigin) {
  794. if (!url) {
  795. callback && callback.call(context, url);
  796. return;
  797. }
  798. var img = fabric.util.createImage();
  799. /** @ignore */
  800. var onLoadCallback = function () {
  801. callback && callback.call(context, img);
  802. img = img.onload = img.onerror = null;
  803. };
  804. img.onload = onLoadCallback;
  805. /** @ignore */
  806. img.onerror = function() {
  807. fabric.log('Error loading ' + img.src);
  808. callback && callback.call(context, null, true);
  809. img = img.onload = img.onerror = null;
  810. };
  811. // data-urls appear to be buggy with crossOrigin
  812. // https://github.com/kangax/fabric.js/commit/d0abb90f1cd5c5ef9d2a94d3fb21a22330da3e0a#commitcomment-4513767
  813. // see https://code.google.com/p/chromium/issues/detail?id=315152
  814. // https://bugzilla.mozilla.org/show_bug.cgi?id=935069
  815. if (url.indexOf('data') !== 0 && crossOrigin) {
  816. img.crossOrigin = crossOrigin;
  817. }
  818. // IE10 / IE11-Fix: SVG contents from data: URI
  819. // will only be available if the IMG is present
  820. // in the DOM (and visible)
  821. if (url.substring(0,14) === 'data:c_image/svg') {
  822. img.onload = null;
  823. fabric.util.loadImageInDom(img, onLoadCallback);
  824. }
  825. img.src = url;
  826. },
  827. /**
  828. * Attaches SVG c_image with data: URL to the dom
  829. * @memberOf fabric.util
  830. * @param {Object} img Image object with data:c_image/svg src
  831. * @param {Function} callback Callback; invoked with loaded c_image
  832. * @return {Object} DOM element (div containing the SVG c_image)
  833. */
  834. loadImageInDom: function(img, onLoadCallback) {
  835. var div = fabric.document.createElement('div');
  836. div.style.width = div.style.height = '1px';
  837. div.style.left = div.style.top = '-100%';
  838. div.style.position = 'absolute';
  839. div.appendChild(img);
  840. fabric.document.querySelector('body').appendChild(div);
  841. /**
  842. * Wrap in function to:
  843. * 1. Call existing callback
  844. * 2. Cleanup DOM
  845. */
  846. img.onload = function () {
  847. onLoadCallback();
  848. div.parentNode.removeChild(div);
  849. div = null;
  850. };
  851. },
  852. /**
  853. * Creates corresponding fabric instances from their object representations
  854. * @static
  855. * @memberOf fabric.util
  856. * @param {Array} objects Objects to enliven
  857. * @param {Function} callback Callback to invoke when all objects are created
  858. * @param {String} namespace Namespace to get klass "Class" object from
  859. * @param {Function} reviver Method for further parsing of object elements,
  860. * called after each fabric object created.
  861. */
  862. enlivenObjects: function(objects, callback, namespace, reviver) {
  863. objects = objects || [];
  864. function onLoaded() {
  865. if (++numLoadedObjects === numTotalObjects) {
  866. callback && callback(enlivenedObjects);
  867. }
  868. }
  869. var enlivenedObjects = [],
  870. numLoadedObjects = 0,
  871. numTotalObjects = objects.length;
  872. if (!numTotalObjects) {
  873. callback && callback(enlivenedObjects);
  874. return;
  875. }
  876. objects.forEach(function (o, index) {
  877. // if sparse array
  878. if (!o || !o.type) {
  879. onLoaded();
  880. return;
  881. }
  882. var klass = fabric.util.getKlass(o.type, namespace);
  883. klass.fromObject(o, function (obj, error) {
  884. error || (enlivenedObjects[index] = obj);
  885. reviver && reviver(o, obj, error);
  886. onLoaded();
  887. });
  888. });
  889. },
  890. /**
  891. * Create and wait for loading of patterns
  892. * @static
  893. * @memberOf fabric.util
  894. * @param {Array} patterns Objects to enliven
  895. * @param {Function} callback Callback to invoke when all objects are created
  896. * called after each fabric object created.
  897. */
  898. enlivenPatterns: function(patterns, callback) {
  899. patterns = patterns || [];
  900. function onLoaded() {
  901. if (++numLoadedPatterns === numPatterns) {
  902. callback && callback(enlivenedPatterns);
  903. }
  904. }
  905. var enlivenedPatterns = [],
  906. numLoadedPatterns = 0,
  907. numPatterns = patterns.length;
  908. if (!numPatterns) {
  909. callback && callback(enlivenedPatterns);
  910. return;
  911. }
  912. patterns.forEach(function (p, index) {
  913. if (p && p.source) {
  914. new fabric.Pattern(p, function(pattern) {
  915. enlivenedPatterns[index] = pattern;
  916. onLoaded();
  917. });
  918. }
  919. else {
  920. enlivenedPatterns[index] = p;
  921. onLoaded();
  922. }
  923. });
  924. },
  925. /**
  926. * Groups SVG elements (usually those retrieved from SVG document)
  927. * @static
  928. * @memberOf fabric.util
  929. * @param {Array} elements SVG elements to group
  930. * @param {Object} [options] Options object
  931. * @param {String} path Value to set sourcePath to
  932. * @return {fabric.Object|fabric.Group}
  933. */
  934. groupSVGElements: function(elements, options, path) {
  935. var object;
  936. if (elements.length === 1) {
  937. return elements[0];
  938. }
  939. if (options) {
  940. if (options.width && options.height) {
  941. options.centerPoint = {
  942. x: options.width / 2,
  943. y: options.height / 2
  944. };
  945. }
  946. else {
  947. delete options.width;
  948. delete options.height;
  949. }
  950. }
  951. object = new fabric.Group(elements, options);
  952. if (typeof path !== 'undefined') {
  953. object.sourcePath = path;
  954. }
  955. return object;
  956. },
  957. /**
  958. * Populates an object with properties of another object
  959. * @static
  960. * @memberOf fabric.util
  961. * @param {Object} source Source object
  962. * @param {Object} destination Destination object
  963. * @return {Array} properties Properties names to include
  964. */
  965. populateWithProperties: function(source, destination, properties) {
  966. if (properties && Object.prototype.toString.call(properties) === '[object Array]') {
  967. for (var i = 0, len = properties.length; i < len; i++) {
  968. if (properties[i] in source) {
  969. destination[properties[i]] = source[properties[i]];
  970. }
  971. }
  972. }
  973. },
  974. /**
  975. * Draws a dashed line between two points
  976. *
  977. * This method is used to draw dashed line around selection area.
  978. * See <a href="http://stackoverflow.com/questions/4576724/dotted-stroke-in-canvas">dotted stroke in canvas</a>
  979. *
  980. * @param {CanvasRenderingContext2D} ctx context
  981. * @param {Number} x start x coordinate
  982. * @param {Number} y start y coordinate
  983. * @param {Number} x2 end x coordinate
  984. * @param {Number} y2 end y coordinate
  985. * @param {Array} da dash array pattern
  986. */
  987. drawDashedLine: function(ctx, x, y, x2, y2, da) {
  988. var dx = x2 - x,
  989. dy = y2 - y,
  990. len = sqrt(dx * dx + dy * dy),
  991. rot = atan2(dy, dx),
  992. dc = da.length,
  993. di = 0,
  994. draw = true;
  995. ctx.save();
  996. ctx.translate(x, y);
  997. ctx.moveTo(0, 0);
  998. ctx.rotate(rot);
  999. x = 0;
  1000. while (len > x) {
  1001. x += da[di++ % dc];
  1002. if (x > len) {
  1003. x = len;
  1004. }
  1005. ctx[draw ? 'lineTo' : 'moveTo'](x, 0);
  1006. draw = !draw;
  1007. }
  1008. ctx.restore();
  1009. },
  1010. /**
  1011. * Creates canvas element
  1012. * @static
  1013. * @memberOf fabric.util
  1014. * @return {CanvasElement} initialized canvas element
  1015. */
  1016. createCanvasElement: function() {
  1017. return fabric.document.createElement('canvas');
  1018. },
  1019. /**
  1020. * Creates c_image element (works on client and node)
  1021. * @static
  1022. * @memberOf fabric.util
  1023. * @return {HTMLImageElement} HTML c_image element
  1024. */
  1025. createImage: function() {
  1026. return fabric.document.createElement('img');
  1027. },
  1028. /**
  1029. * @static
  1030. * @memberOf fabric.util
  1031. * @deprecated since 2.0.0
  1032. * @param {fabric.Object} receiver Object implementing `clipTo` method
  1033. * @param {CanvasRenderingContext2D} ctx Context to clip
  1034. */
  1035. clipContext: function(receiver, ctx) {
  1036. ctx.save();
  1037. ctx.beginPath();
  1038. receiver.clipTo(ctx);
  1039. ctx.clip();
  1040. },
  1041. /**
  1042. * Multiply matrix A by matrix B to nest transformations
  1043. * @static
  1044. * @memberOf fabric.util
  1045. * @param {Array} a First transformMatrix
  1046. * @param {Array} b Second transformMatrix
  1047. * @param {Boolean} is2x2 flag to multiply matrices as 2x2 matrices
  1048. * @return {Array} The product of the two transform matrices
  1049. */
  1050. multiplyTransformMatrices: function(a, b, is2x2) {
  1051. // Matrix multiply a * b
  1052. return [
  1053. a[0] * b[0] + a[2] * b[1],
  1054. a[1] * b[0] + a[3] * b[1],
  1055. a[0] * b[2] + a[2] * b[3],
  1056. a[1] * b[2] + a[3] * b[3],
  1057. is2x2 ? 0 : a[0] * b[4] + a[2] * b[5] + a[4],
  1058. is2x2 ? 0 : a[1] * b[4] + a[3] * b[5] + a[5]
  1059. ];
  1060. },
  1061. /**
  1062. * Decomposes standard 2x2 matrix into transform componentes
  1063. * @static
  1064. * @memberOf fabric.util
  1065. * @param {Array} a transformMatrix
  1066. * @return {Object} Components of transform
  1067. */
  1068. qrDecompose: function(a) {
  1069. var angle = atan2(a[1], a[0]),
  1070. denom = pow(a[0], 2) + pow(a[1], 2),
  1071. scaleX = sqrt(denom),
  1072. scaleY = (a[0] * a[3] - a[2] * a [1]) / scaleX,
  1073. skewX = atan2(a[0] * a[2] + a[1] * a [3], denom);
  1074. return {
  1075. angle: angle / PiBy180,
  1076. scaleX: scaleX,
  1077. scaleY: scaleY,
  1078. skewX: skewX / PiBy180,
  1079. skewY: 0,
  1080. translateX: a[4],
  1081. translateY: a[5]
  1082. };
  1083. },
  1084. customTransformMatrix: function(scaleX, scaleY, skewX) {
  1085. var skewMatrixX = [1, 0, abs(Math.tan(skewX * PiBy180)), 1],
  1086. scaleMatrix = [abs(scaleX), 0, 0, abs(scaleY)];
  1087. return fabric.util.multiplyTransformMatrices(scaleMatrix, skewMatrixX, true);
  1088. },
  1089. resetObjectTransform: function (target) {
  1090. target.scaleX = 1;
  1091. target.scaleY = 1;
  1092. target.skewX = 0;
  1093. target.skewY = 0;
  1094. target.flipX = false;
  1095. target.flipY = false;
  1096. target.rotate(0);
  1097. },
  1098. /**
  1099. * Returns string representation of function body
  1100. * @param {Function} fn Function to get body of
  1101. * @return {String} Function body
  1102. */
  1103. getFunctionBody: function(fn) {
  1104. return (String(fn).match(/function[^{]*\{([\s\S]*)\}/) || {})[1];
  1105. },
  1106. /**
  1107. * Returns true if context has transparent pixel
  1108. * at specified location (taking tolerance into account)
  1109. * @param {CanvasRenderingContext2D} ctx context
  1110. * @param {Number} x x coordinate
  1111. * @param {Number} y y coordinate
  1112. * @param {Number} tolerance Tolerance
  1113. */
  1114. isTransparent: function(ctx, x, y, tolerance) {
  1115. // If tolerance is > 0 adjust start coords to take into account.
  1116. // If moves off Canvas fix to 0
  1117. if (tolerance > 0) {
  1118. if (x > tolerance) {
  1119. x -= tolerance;
  1120. }
  1121. else {
  1122. x = 0;
  1123. }
  1124. if (y > tolerance) {
  1125. y -= tolerance;
  1126. }
  1127. else {
  1128. y = 0;
  1129. }
  1130. }
  1131. var _isTransparent = true, i, temp,
  1132. imageData = ctx.getImageData(x, y, (tolerance * 2) || 1, (tolerance * 2) || 1),
  1133. l = imageData.data.length;
  1134. // Split c_image data - for tolerance > 1, pixelDataSize = 4;
  1135. for (i = 3; i < l; i += 4) {
  1136. temp = imageData.data[i];
  1137. _isTransparent = temp <= 0;
  1138. if (_isTransparent === false) {
  1139. break; // Stop if colour found
  1140. }
  1141. }
  1142. imageData = null;
  1143. return _isTransparent;
  1144. },
  1145. /**
  1146. * Parse preserveAspectRatio attribute from element
  1147. * @param {string} attribute to be parsed
  1148. * @return {Object} an object containing align and meetOrSlice attribute
  1149. */
  1150. parsePreserveAspectRatioAttribute: function(attribute) {
  1151. var meetOrSlice = 'meet', alignX = 'Mid', alignY = 'Mid',
  1152. aspectRatioAttrs = attribute.split(' '), align;
  1153. if (aspectRatioAttrs && aspectRatioAttrs.length) {
  1154. meetOrSlice = aspectRatioAttrs.pop();
  1155. if (meetOrSlice !== 'meet' && meetOrSlice !== 'slice') {
  1156. align = meetOrSlice;
  1157. meetOrSlice = 'meet';
  1158. }
  1159. else if (aspectRatioAttrs.length) {
  1160. align = aspectRatioAttrs.pop();
  1161. }
  1162. }
  1163. //divide align in alignX and alignY
  1164. alignX = align !== 'none' ? align.slice(1, 4) : 'none';
  1165. alignY = align !== 'none' ? align.slice(5, 8) : 'none';
  1166. return {
  1167. meetOrSlice: meetOrSlice,
  1168. alignX: alignX,
  1169. alignY: alignY
  1170. };
  1171. },
  1172. /**
  1173. * Clear char widths cache for the given font family or all the cache if no
  1174. * fontFamily is specified.
  1175. * Use it if you know you are loading fonts in a lazy way and you are not waiting
  1176. * for custom fonts to load properly when adding text objects to the canvas.
  1177. * If a text object is added when its own font is not loaded yet, you will get wrong
  1178. * measurement and so wrong bounding boxes.
  1179. * After the font cache is cleared, either change the textObject text content or call
  1180. * initDimensions() to trigger a recalculation
  1181. * @memberOf fabric.util
  1182. * @param {String} [fontFamily] font family to clear
  1183. */
  1184. clearFabricFontCache: function(fontFamily) {
  1185. fontFamily = (fontFamily || '').toLowerCase();
  1186. if (!fontFamily) {
  1187. fabric.charWidthsCache = { };
  1188. }
  1189. else if (fabric.charWidthsCache[fontFamily]) {
  1190. delete fabric.charWidthsCache[fontFamily];
  1191. }
  1192. },
  1193. /**
  1194. * Given current aspect ratio, determines the max width and height that can
  1195. * respect the total allowed area for the cache.
  1196. * @memberOf fabric.util
  1197. * @param {Number} ar aspect ratio
  1198. * @param {Number} maximumArea Maximum area you want to achieve
  1199. * @return {Object.x} Limited dimensions by X
  1200. * @return {Object.y} Limited dimensions by Y
  1201. */
  1202. limitDimsByArea: function(ar, maximumArea) {
  1203. var roughWidth = Math.sqrt(maximumArea * ar),
  1204. perfLimitSizeY = Math.floor(maximumArea / roughWidth);
  1205. return { x: Math.floor(roughWidth), y: perfLimitSizeY };
  1206. },
  1207. capValue: function(min, value, max) {
  1208. return Math.max(min, Math.min(value, max));
  1209. },
  1210. findScaleToFit: function(source, destination) {
  1211. return Math.min(destination.width / source.width, destination.height / source.height);
  1212. },
  1213. findScaleToCover: function(source, destination) {
  1214. return Math.max(destination.width / source.width, destination.height / source.height);
  1215. }
  1216. };
  1217. })(typeof exports !== 'undefined' ? exports : this);
  1218. (function() {
  1219. var arcToSegmentsCache = { },
  1220. segmentToBezierCache = { },
  1221. boundsOfCurveCache = { },
  1222. _join = Array.prototype.join;
  1223. /* Adapted from http://dxr.mozilla.org/mozilla-central/source/content/svg/content/src/nsSVGPathDataParser.cpp
  1224. * by Andrea Bogazzi code is under MPL. if you don't have a copy of the license you can take it here
  1225. * http://mozilla.org/MPL/2.0/
  1226. */
  1227. function arcToSegments(toX, toY, rx, ry, large, sweep, rotateX) {
  1228. var argsString = _join.call(arguments);
  1229. if (arcToSegmentsCache[argsString]) {
  1230. return arcToSegmentsCache[argsString];
  1231. }
  1232. var PI = Math.PI, th = rotateX * PI / 180,
  1233. sinTh = fabric.util.sin(th),
  1234. cosTh = fabric.util.cos(th),
  1235. fromX = 0, fromY = 0;
  1236. rx = Math.abs(rx);
  1237. ry = Math.abs(ry);
  1238. var px = -cosTh * toX * 0.5 - sinTh * toY * 0.5,
  1239. py = -cosTh * toY * 0.5 + sinTh * toX * 0.5,
  1240. rx2 = rx * rx, ry2 = ry * ry, py2 = py * py, px2 = px * px,
  1241. pl = rx2 * ry2 - rx2 * py2 - ry2 * px2,
  1242. root = 0;
  1243. if (pl < 0) {
  1244. var s = Math.sqrt(1 - pl / (rx2 * ry2));
  1245. rx *= s;
  1246. ry *= s;
  1247. }
  1248. else {
  1249. root = (large === sweep ? -1.0 : 1.0) *
  1250. Math.sqrt( pl / (rx2 * py2 + ry2 * px2));
  1251. }
  1252. var cx = root * rx * py / ry,
  1253. cy = -root * ry * px / rx,
  1254. cx1 = cosTh * cx - sinTh * cy + toX * 0.5,
  1255. cy1 = sinTh * cx + cosTh * cy + toY * 0.5,
  1256. mTheta = calcVectorAngle(1, 0, (px - cx) / rx, (py - cy) / ry),
  1257. dtheta = calcVectorAngle((px - cx) / rx, (py - cy) / ry, (-px - cx) / rx, (-py - cy) / ry);
  1258. if (sweep === 0 && dtheta > 0) {
  1259. dtheta -= 2 * PI;
  1260. }
  1261. else if (sweep === 1 && dtheta < 0) {
  1262. dtheta += 2 * PI;
  1263. }
  1264. // Convert into cubic bezier segments <= 90deg
  1265. var segments = Math.ceil(Math.abs(dtheta / PI * 2)),
  1266. result = [], mDelta = dtheta / segments,
  1267. mT = 8 / 3 * Math.sin(mDelta / 4) * Math.sin(mDelta / 4) / Math.sin(mDelta / 2),
  1268. th3 = mTheta + mDelta;
  1269. for (var i = 0; i < segments; i++) {
  1270. result[i] = segmentToBezier(mTheta, th3, cosTh, sinTh, rx, ry, cx1, cy1, mT, fromX, fromY);
  1271. fromX = result[i][4];
  1272. fromY = result[i][5];
  1273. mTheta = th3;
  1274. th3 += mDelta;
  1275. }
  1276. arcToSegmentsCache[argsString] = result;
  1277. return result;
  1278. }
  1279. function segmentToBezier(th2, th3, cosTh, sinTh, rx, ry, cx1, cy1, mT, fromX, fromY) {
  1280. var argsString2 = _join.call(arguments);
  1281. if (segmentToBezierCache[argsString2]) {
  1282. return segmentToBezierCache[argsString2];
  1283. }
  1284. var costh2 = fabric.util.cos(th2),
  1285. sinth2 = fabric.util.sin(th2),
  1286. costh3 = fabric.util.cos(th3),
  1287. sinth3 = fabric.util.sin(th3),
  1288. toX = cosTh * rx * costh3 - sinTh * ry * sinth3 + cx1,
  1289. toY = sinTh * rx * costh3 + cosTh * ry * sinth3 + cy1,
  1290. cp1X = fromX + mT * ( -cosTh * rx * sinth2 - sinTh * ry * costh2),
  1291. cp1Y = fromY + mT * ( -sinTh * rx * sinth2 + cosTh * ry * costh2),
  1292. cp2X = toX + mT * ( cosTh * rx * sinth3 + sinTh * ry * costh3),
  1293. cp2Y = toY + mT * ( sinTh * rx * sinth3 - cosTh * ry * costh3);
  1294. segmentToBezierCache[argsString2] = [
  1295. cp1X, cp1Y,
  1296. cp2X, cp2Y,
  1297. toX, toY
  1298. ];
  1299. return segmentToBezierCache[argsString2];
  1300. }
  1301. /*
  1302. * Private
  1303. */
  1304. function calcVectorAngle(ux, uy, vx, vy) {
  1305. var ta = Math.atan2(uy, ux),
  1306. tb = Math.atan2(vy, vx);
  1307. if (tb >= ta) {
  1308. return tb - ta;
  1309. }
  1310. else {
  1311. return 2 * Math.PI - (ta - tb);
  1312. }
  1313. }
  1314. /**
  1315. * Draws arc
  1316. * @param {CanvasRenderingContext2D} ctx
  1317. * @param {Number} fx
  1318. * @param {Number} fy
  1319. * @param {Array} coords
  1320. */
  1321. fabric.util.drawArc = function(ctx, fx, fy, coords) {
  1322. var rx = coords[0],
  1323. ry = coords[1],
  1324. rot = coords[2],
  1325. large = coords[3],
  1326. sweep = coords[4],
  1327. tx = coords[5],
  1328. ty = coords[6],
  1329. segs = [[], [], [], []],
  1330. segsNorm = arcToSegments(tx - fx, ty - fy, rx, ry, large, sweep, rot);
  1331. for (var i = 0, len = segsNorm.length; i < len; i++) {
  1332. segs[i][0] = segsNorm[i][0] + fx;
  1333. segs[i][1] = segsNorm[i][1] + fy;
  1334. segs[i][2] = segsNorm[i][2] + fx;
  1335. segs[i][3] = segsNorm[i][3] + fy;
  1336. segs[i][4] = segsNorm[i][4] + fx;
  1337. segs[i][5] = segsNorm[i][5] + fy;
  1338. ctx.bezierCurveTo.apply(ctx, segs[i]);
  1339. }
  1340. };
  1341. /**
  1342. * Calculate bounding box of a elliptic-arc
  1343. * @param {Number} fx start point of arc
  1344. * @param {Number} fy
  1345. * @param {Number} rx horizontal radius
  1346. * @param {Number} ry vertical radius
  1347. * @param {Number} rot angle of horizontal axe
  1348. * @param {Number} large 1 or 0, whatever the arc is the big or the small on the 2 points
  1349. * @param {Number} sweep 1 or 0, 1 clockwise or counterclockwise direction
  1350. * @param {Number} tx end point of arc
  1351. * @param {Number} ty
  1352. */
  1353. fabric.util.getBoundsOfArc = function(fx, fy, rx, ry, rot, large, sweep, tx, ty) {
  1354. var fromX = 0, fromY = 0, bound, bounds = [],
  1355. segs = arcToSegments(tx - fx, ty - fy, rx, ry, large, sweep, rot);
  1356. for (var i = 0, len = segs.length; i < len; i++) {
  1357. bound = getBoundsOfCurve(fromX, fromY, segs[i][0], segs[i][1], segs[i][2], segs[i][3], segs[i][4], segs[i][5]);
  1358. bounds.push({ x: bound[0].x + fx, y: bound[0].y + fy });
  1359. bounds.push({ x: bound[1].x + fx, y: bound[1].y + fy });
  1360. fromX = segs[i][4];
  1361. fromY = segs[i][5];
  1362. }
  1363. return bounds;
  1364. };
  1365. /**
  1366. * Calculate bounding box of a beziercurve
  1367. * @param {Number} x0 starting point
  1368. * @param {Number} y0
  1369. * @param {Number} x1 first control point
  1370. * @param {Number} y1
  1371. * @param {Number} x2 secondo control point
  1372. * @param {Number} y2
  1373. * @param {Number} x3 end of beizer
  1374. * @param {Number} y3
  1375. */
  1376. // taken from http://jsbin.com/ivomiq/56/edit no credits available for that.
  1377. function getBoundsOfCurve(x0, y0, x1, y1, x2, y2, x3, y3) {
  1378. var argsString = _join.call(arguments);
  1379. if (boundsOfCurveCache[argsString]) {
  1380. return boundsOfCurveCache[argsString];
  1381. }
  1382. var sqrt = Math.sqrt,
  1383. min = Math.min, max = Math.max,
  1384. abs = Math.abs, tvalues = [],
  1385. bounds = [[], []],
  1386. a, b, c, t, t1, t2, b2ac, sqrtb2ac;
  1387. b = 6 * x0 - 12 * x1 + 6 * x2;
  1388. a = -3 * x0 + 9 * x1 - 9 * x2 + 3 * x3;
  1389. c = 3 * x1 - 3 * x0;
  1390. for (var i = 0; i < 2; ++i) {
  1391. if (i > 0) {
  1392. b = 6 * y0 - 12 * y1 + 6 * y2;
  1393. a = -3 * y0 + 9 * y1 - 9 * y2 + 3 * y3;
  1394. c = 3 * y1 - 3 * y0;
  1395. }
  1396. if (abs(a) < 1e-12) {
  1397. if (abs(b) < 1e-12) {
  1398. continue;
  1399. }
  1400. t = -c / b;
  1401. if (0 < t && t < 1) {
  1402. tvalues.push(t);
  1403. }
  1404. continue;
  1405. }
  1406. b2ac = b * b - 4 * c * a;
  1407. if (b2ac < 0) {
  1408. continue;
  1409. }
  1410. sqrtb2ac = sqrt(b2ac);
  1411. t1 = (-b + sqrtb2ac) / (2 * a);
  1412. if (0 < t1 && t1 < 1) {
  1413. tvalues.push(t1);
  1414. }
  1415. t2 = (-b - sqrtb2ac) / (2 * a);
  1416. if (0 < t2 && t2 < 1) {
  1417. tvalues.push(t2);
  1418. }
  1419. }
  1420. var x, y, j = tvalues.length, jlen = j, mt;
  1421. while (j--) {
  1422. t = tvalues[j];
  1423. mt = 1 - t;
  1424. x = (mt * mt * mt * x0) + (3 * mt * mt * t * x1) + (3 * mt * t * t * x2) + (t * t * t * x3);
  1425. bounds[0][j] = x;
  1426. y = (mt * mt * mt * y0) + (3 * mt * mt * t * y1) + (3 * mt * t * t * y2) + (t * t * t * y3);
  1427. bounds[1][j] = y;
  1428. }
  1429. bounds[0][jlen] = x0;
  1430. bounds[1][jlen] = y0;
  1431. bounds[0][jlen + 1] = x3;
  1432. bounds[1][jlen + 1] = y3;
  1433. var result = [
  1434. {
  1435. x: min.apply(null, bounds[0]),
  1436. y: min.apply(null, bounds[1])
  1437. },
  1438. {
  1439. x: max.apply(null, bounds[0]),
  1440. y: max.apply(null, bounds[1])
  1441. }
  1442. ];
  1443. boundsOfCurveCache[argsString] = result;
  1444. return result;
  1445. }
  1446. fabric.util.getBoundsOfCurve = getBoundsOfCurve;
  1447. })();
  1448. (function() {
  1449. var slice = Array.prototype.slice;
  1450. /**
  1451. * Invokes method on all items in a given array
  1452. * @memberOf fabric.util.array
  1453. * @param {Array} array Array to iterate over
  1454. * @param {String} method Name of a method to invoke
  1455. * @return {Array}
  1456. */
  1457. function invoke(array, method) {
  1458. var args = slice.call(arguments, 2), result = [];
  1459. for (var i = 0, len = array.length; i < len; i++) {
  1460. result[i] = args.length ? array[i][method].apply(array[i], args) : array[i][method].call(array[i]);
  1461. }
  1462. return result;
  1463. }
  1464. /**
  1465. * Finds maximum value in array (not necessarily "first" one)
  1466. * @memberOf fabric.util.array
  1467. * @param {Array} array Array to iterate over
  1468. * @param {String} byProperty
  1469. * @return {*}
  1470. */
  1471. function max(array, byProperty) {
  1472. return find(array, byProperty, function(value1, value2) {
  1473. return value1 >= value2;
  1474. });
  1475. }
  1476. /**
  1477. * Finds minimum value in array (not necessarily "first" one)
  1478. * @memberOf fabric.util.array
  1479. * @param {Array} array Array to iterate over
  1480. * @param {String} byProperty
  1481. * @return {*}
  1482. */
  1483. function min(array, byProperty) {
  1484. return find(array, byProperty, function(value1, value2) {
  1485. return value1 < value2;
  1486. });
  1487. }
  1488. /**
  1489. * @private
  1490. */
  1491. function fill(array, value) {
  1492. var k = array.length;
  1493. while (k--) {
  1494. array[k] = value;
  1495. }
  1496. return array;
  1497. }
  1498. /**
  1499. * @private
  1500. */
  1501. function find(array, byProperty, condition) {
  1502. if (!array || array.length === 0) {
  1503. return;
  1504. }
  1505. var i = array.length - 1,
  1506. result = byProperty ? array[i][byProperty] : array[i];
  1507. if (byProperty) {
  1508. while (i--) {
  1509. if (condition(array[i][byProperty], result)) {
  1510. result = array[i][byProperty];
  1511. }
  1512. }
  1513. }
  1514. else {
  1515. while (i--) {
  1516. if (condition(array[i], result)) {
  1517. result = array[i];
  1518. }
  1519. }
  1520. }
  1521. return result;
  1522. }
  1523. /**
  1524. * @namespace fabric.util.array
  1525. */
  1526. fabric.util.array = {
  1527. fill: fill,
  1528. invoke: invoke,
  1529. min: min,
  1530. max: max
  1531. };
  1532. })();
  1533. (function() {
  1534. /**
  1535. * Copies all enumerable properties of one js object to another
  1536. * Does not clone or extend fabric.Object subclasses.
  1537. * @memberOf fabric.util.object
  1538. * @param {Object} destination Where to copy to
  1539. * @param {Object} source Where to copy from
  1540. * @return {Object}
  1541. */
  1542. function extend(destination, source, deep) {
  1543. // JScript DontEnum bug is not taken care of
  1544. // the deep clone is for internal use, is not meant to avoid
  1545. // javascript traps or cloning html element or self referenced objects.
  1546. if (deep) {
  1547. if (!fabric.isLikelyNode && source instanceof Element) {
  1548. // avoid cloning deep images, canvases,
  1549. destination = source;
  1550. }
  1551. else if (source instanceof Array) {
  1552. destination = [];
  1553. for (var i = 0, len = source.length; i < len; i++) {
  1554. destination[i] = extend({ }, source[i], deep);
  1555. }
  1556. }
  1557. else if (source && typeof source === 'object') {
  1558. for (var property in source) {
  1559. if (source.hasOwnProperty(property)) {
  1560. destination[property] = extend({ }, source[property], deep);
  1561. }
  1562. }
  1563. }
  1564. else {
  1565. // this sounds odd for an extend but is ok for recursive use
  1566. destination = source;
  1567. }
  1568. }
  1569. else {
  1570. for (var property in source) {
  1571. destination[property] = source[property];
  1572. }
  1573. }
  1574. return destination;
  1575. }
  1576. /**
  1577. * Creates an empty object and copies all enumerable properties of another object to it
  1578. * @memberOf fabric.util.object
  1579. * TODO: this function return an empty object if you try to clone null
  1580. * @param {Object} object Object to clone
  1581. * @return {Object}
  1582. */
  1583. function clone(object, deep) {
  1584. return extend({ }, object, deep);
  1585. }
  1586. /** @namespace fabric.util.object */
  1587. fabric.util.object = {
  1588. extend: extend,
  1589. clone: clone
  1590. };
  1591. fabric.util.object.extend(fabric.util, fabric.Observable);
  1592. })();
  1593. (function() {
  1594. /**
  1595. * Camelizes a string
  1596. * @memberOf fabric.util.string
  1597. * @param {String} string String to camelize
  1598. * @return {String} Camelized version of a string
  1599. */
  1600. function camelize(string) {
  1601. return string.replace(/-+(.)?/g, function(match, character) {
  1602. return character ? character.toUpperCase() : '';
  1603. });
  1604. }
  1605. /**
  1606. * Capitalizes a string
  1607. * @memberOf fabric.util.string
  1608. * @param {String} string String to capitalize
  1609. * @param {Boolean} [firstLetterOnly] If true only first letter is capitalized
  1610. * and other letters stay untouched, if false first letter is capitalized
  1611. * and other letters are converted to lowercase.
  1612. * @return {String} Capitalized version of a string
  1613. */
  1614. function capitalize(string, firstLetterOnly) {
  1615. return string.charAt(0).toUpperCase() +
  1616. (firstLetterOnly ? string.slice(1) : string.slice(1).toLowerCase());
  1617. }
  1618. /**
  1619. * Escapes XML in a string
  1620. * @memberOf fabric.util.string
  1621. * @param {String} string String to escape
  1622. * @return {String} Escaped version of a string
  1623. */
  1624. function escapeXml(string) {
  1625. return string.replace(/&/g, '&amp;')
  1626. .replace(/"/g, '&quot;')
  1627. .replace(/'/g, '&apos;')
  1628. .replace(/</g, '&lt;')
  1629. .replace(/>/g, '&gt;');
  1630. }
  1631. /**
  1632. * Divide a string in the user perceived single units
  1633. * @memberOf fabric.util.string
  1634. * @param {String} textstring String to escape
  1635. * @return {Array} array containing the graphemes
  1636. */
  1637. function graphemeSplit(textstring) {
  1638. var i = 0, chr, graphemes = [];
  1639. for (i = 0, chr; i < textstring.length; i++) {
  1640. if ((chr = getWholeChar(textstring, i)) === false) {
  1641. continue;
  1642. }
  1643. graphemes.push(chr);
  1644. }
  1645. return graphemes;
  1646. }
  1647. // taken from mdn in the charAt doc page.
  1648. function getWholeChar(str, i) {
  1649. var code = str.charCodeAt(i);
  1650. if (isNaN(code)) {
  1651. return ''; // Position not found
  1652. }
  1653. if (code < 0xD800 || code > 0xDFFF) {
  1654. return str.charAt(i);
  1655. }
  1656. // High surrogate (could change last hex to 0xDB7F to treat high private
  1657. // surrogates as single characters)
  1658. if (0xD800 <= code && code <= 0xDBFF) {
  1659. if (str.length <= (i + 1)) {
  1660. throw 'High surrogate without following low surrogate';
  1661. }
  1662. var next = str.charCodeAt(i + 1);
  1663. if (0xDC00 > next || next > 0xDFFF) {
  1664. throw 'High surrogate without following low surrogate';
  1665. }
  1666. return str.charAt(i) + str.charAt(i + 1);
  1667. }
  1668. // Low surrogate (0xDC00 <= code && code <= 0xDFFF)
  1669. if (i === 0) {
  1670. throw 'Low surrogate without preceding high surrogate';
  1671. }
  1672. var prev = str.charCodeAt(i - 1);
  1673. // (could change last hex to 0xDB7F to treat high private
  1674. // surrogates as single characters)
  1675. if (0xD800 > prev || prev > 0xDBFF) {
  1676. throw 'Low surrogate without preceding high surrogate';
  1677. }
  1678. // We can pass over low surrogates now as the second component
  1679. // in a pair which we have already processed
  1680. return false;
  1681. }
  1682. /**
  1683. * String utilities
  1684. * @namespace fabric.util.string
  1685. */
  1686. fabric.util.string = {
  1687. camelize: camelize,
  1688. capitalize: capitalize,
  1689. escapeXml: escapeXml,
  1690. graphemeSplit: graphemeSplit
  1691. };
  1692. })();
  1693. (function() {
  1694. var slice = Array.prototype.slice, emptyFunction = function() { },
  1695. IS_DONTENUM_BUGGY = (function() {
  1696. for (var p in { toString: 1 }) {
  1697. if (p === 'toString') {
  1698. return false;
  1699. }
  1700. }
  1701. return true;
  1702. })(),
  1703. /** @ignore */
  1704. addMethods = function(klass, source, parent) {
  1705. for (var property in source) {
  1706. if (property in klass.prototype &&
  1707. typeof klass.prototype[property] === 'function' &&
  1708. (source[property] + '').indexOf('callSuper') > -1) {
  1709. klass.prototype[property] = (function(property) {
  1710. return function() {
  1711. var superclass = this.constructor.superclass;
  1712. this.constructor.superclass = parent;
  1713. var returnValue = source[property].apply(this, arguments);
  1714. this.constructor.superclass = superclass;
  1715. if (property !== 'initialize') {
  1716. return returnValue;
  1717. }
  1718. };
  1719. })(property);
  1720. }
  1721. else {
  1722. klass.prototype[property] = source[property];
  1723. }
  1724. if (IS_DONTENUM_BUGGY) {
  1725. if (source.toString !== Object.prototype.toString) {
  1726. klass.prototype.toString = source.toString;
  1727. }
  1728. if (source.valueOf !== Object.prototype.valueOf) {
  1729. klass.prototype.valueOf = source.valueOf;
  1730. }
  1731. }
  1732. }
  1733. };
  1734. function Subclass() { }
  1735. function callSuper(methodName) {
  1736. var parentMethod = null,
  1737. _this = this;
  1738. // climb prototype chain to find method not equal to callee's method
  1739. while (_this.constructor.superclass) {
  1740. var superClassMethod = _this.constructor.superclass.prototype[methodName];
  1741. if (_this[methodName] !== superClassMethod) {
  1742. parentMethod = superClassMethod;
  1743. break;
  1744. }
  1745. // eslint-disable-next-line
  1746. _this = _this.constructor.superclass.prototype;
  1747. }
  1748. if (!parentMethod) {
  1749. return console.log('tried to callSuper ' + methodName + ', method not found in prototype chain', this);
  1750. }
  1751. return (arguments.length > 1)
  1752. ? parentMethod.apply(this, slice.call(arguments, 1))
  1753. : parentMethod.call(this);
  1754. }
  1755. /**
  1756. * Helper for creation of "classes".
  1757. * @memberOf fabric.util
  1758. * @param {Function} [parent] optional "Class" to inherit from
  1759. * @param {Object} [properties] Properties shared by all instances of this class
  1760. * (be careful modifying objects defined here as this would affect all instances)
  1761. */
  1762. function createClass() {
  1763. var parent = null,
  1764. properties = slice.call(arguments, 0);
  1765. if (typeof properties[0] === 'function') {
  1766. parent = properties.shift();
  1767. }
  1768. function klass() {
  1769. this.initialize.apply(this, arguments);
  1770. }
  1771. klass.superclass = parent;
  1772. klass.subclasses = [];
  1773. if (parent) {
  1774. Subclass.prototype = parent.prototype;
  1775. klass.prototype = new Subclass();
  1776. parent.subclasses.push(klass);
  1777. }
  1778. for (var i = 0, length = properties.length; i < length; i++) {
  1779. addMethods(klass, properties[i], parent);
  1780. }
  1781. if (!klass.prototype.initialize) {
  1782. klass.prototype.initialize = emptyFunction;
  1783. }
  1784. klass.prototype.constructor = klass;
  1785. klass.prototype.callSuper = callSuper;
  1786. return klass;
  1787. }
  1788. fabric.util.createClass = createClass;
  1789. })();
  1790. (function () {
  1791. /**
  1792. * Cross-browser wrapper for setting element's c_style
  1793. * @memberOf fabric.util
  1794. * @param {HTMLElement} element
  1795. * @param {Object} styles
  1796. * @return {HTMLElement} Element that was passed as a first argument
  1797. */
  1798. function setStyle(element, styles) {
  1799. var elementStyle = element.style;
  1800. if (!elementStyle) {
  1801. return element;
  1802. }
  1803. if (typeof styles === 'string') {
  1804. element.style.cssText += ';' + styles;
  1805. return styles.indexOf('opacity') > -1
  1806. ? setOpacity(element, styles.match(/opacity:\s*(\d?\.?\d*)/)[1])
  1807. : element;
  1808. }
  1809. for (var property in styles) {
  1810. if (property === 'opacity') {
  1811. setOpacity(element, styles[property]);
  1812. }
  1813. else {
  1814. var normalizedProperty = (property === 'float' || property === 'cssFloat')
  1815. ? (typeof elementStyle.styleFloat === 'undefined' ? 'cssFloat' : 'styleFloat')
  1816. : property;
  1817. elementStyle[normalizedProperty] = styles[property];
  1818. }
  1819. }
  1820. return element;
  1821. }
  1822. var parseEl = fabric.document.createElement('div'),
  1823. supportsOpacity = typeof parseEl.style.opacity === 'string',
  1824. supportsFilters = typeof parseEl.style.filter === 'string',
  1825. reOpacity = /alpha\s*\(\s*opacity\s*=\s*([^\)]+)\)/,
  1826. /** @ignore */
  1827. setOpacity = function (element) { return element; };
  1828. if (supportsOpacity) {
  1829. /** @ignore */
  1830. setOpacity = function(element, value) {
  1831. element.style.opacity = value;
  1832. return element;
  1833. };
  1834. }
  1835. else if (supportsFilters) {
  1836. /** @ignore */
  1837. setOpacity = function(element, value) {
  1838. var es = element.style;
  1839. if (element.currentStyle && !element.currentStyle.hasLayout) {
  1840. es.zoom = 1;
  1841. }
  1842. if (reOpacity.test(es.filter)) {
  1843. value = value >= 0.9999 ? '' : ('alpha(opacity=' + (value * 100) + ')');
  1844. es.filter = es.filter.replace(reOpacity, value);
  1845. }
  1846. else {
  1847. es.filter += ' alpha(opacity=' + (value * 100) + ')';
  1848. }
  1849. return element;
  1850. };
  1851. }
  1852. fabric.util.setStyle = setStyle;
  1853. })();
  1854. (function() {
  1855. var _slice = Array.prototype.slice;
  1856. /**
  1857. * Takes id and returns an element with that id (if one exists in a document)
  1858. * @memberOf fabric.util
  1859. * @param {String|HTMLElement} id
  1860. * @return {HTMLElement|null}
  1861. */
  1862. function getById(id) {
  1863. return typeof id === 'string' ? fabric.document.getElementById(id) : id;
  1864. }
  1865. var sliceCanConvertNodelists,
  1866. /**
  1867. * Converts an array-like object (e.g. arguments or NodeList) to an array
  1868. * @memberOf fabric.util
  1869. * @param {Object} arrayLike
  1870. * @return {Array}
  1871. */
  1872. toArray = function(arrayLike) {
  1873. return _slice.call(arrayLike, 0);
  1874. };
  1875. try {
  1876. sliceCanConvertNodelists = toArray(fabric.document.childNodes) instanceof Array;
  1877. }
  1878. catch (err) { }
  1879. if (!sliceCanConvertNodelists) {
  1880. toArray = function(arrayLike) {
  1881. var arr = new Array(arrayLike.length), i = arrayLike.length;
  1882. while (i--) {
  1883. arr[i] = arrayLike[i];
  1884. }
  1885. return arr;
  1886. };
  1887. }
  1888. /**
  1889. * Creates specified element with specified attributes
  1890. * @memberOf fabric.util
  1891. * @param {String} tagName Type of an element to create
  1892. * @param {Object} [attributes] Attributes to set on an element
  1893. * @return {HTMLElement} Newly created element
  1894. */
  1895. function makeElement(tagName, attributes) {
  1896. var el = fabric.document.createElement(tagName);
  1897. for (var prop in attributes) {
  1898. if (prop === 'class') {
  1899. el.className = attributes[prop];
  1900. }
  1901. else if (prop === 'for') {
  1902. el.htmlFor = attributes[prop];
  1903. }
  1904. else {
  1905. el.setAttribute(prop, attributes[prop]);
  1906. }
  1907. }
  1908. return el;
  1909. }
  1910. /**
  1911. * Adds class to an element
  1912. * @memberOf fabric.util
  1913. * @param {HTMLElement} element Element to add class to
  1914. * @param {String} className Class to add to an element
  1915. */
  1916. function addClass(element, className) {
  1917. if (element && (' ' + element.className + ' ').indexOf(' ' + className + ' ') === -1) {
  1918. element.className += (element.className ? ' ' : '') + className;
  1919. }
  1920. }
  1921. /**
  1922. * Wraps element with another element
  1923. * @memberOf fabric.util
  1924. * @param {HTMLElement} element Element to wrap
  1925. * @param {HTMLElement|String} wrapper Element to wrap with
  1926. * @param {Object} [attributes] Attributes to set on a wrapper
  1927. * @return {HTMLElement} wrapper
  1928. */
  1929. function wrapElement(element, wrapper, attributes) {
  1930. if (typeof wrapper === 'string') {
  1931. wrapper = makeElement(wrapper, attributes);
  1932. }
  1933. if (element.parentNode) {
  1934. element.parentNode.replaceChild(wrapper, element);
  1935. }
  1936. wrapper.appendChild(element);
  1937. return wrapper;
  1938. }
  1939. /**
  1940. * Returns element scroll offsets
  1941. * @memberOf fabric.util
  1942. * @param {HTMLElement} element Element to operate on
  1943. * @return {Object} Object with left/top values
  1944. */
  1945. function getScrollLeftTop(element) {
  1946. var left = 0,
  1947. top = 0,
  1948. docElement = fabric.document.documentElement,
  1949. body = fabric.document.body || {
  1950. scrollLeft: 0, scrollTop: 0
  1951. };
  1952. // While loop checks (and then sets element to) .parentNode OR .host
  1953. // to account for ShadowDOM. We still want to traverse up out of ShadowDOM,
  1954. // but the .parentNode of a root ShadowDOM node will always be null, instead
  1955. // it should be accessed through .host. See http://stackoverflow.com/a/24765528/4383938
  1956. while (element && (element.parentNode || element.host)) {
  1957. // Set element to element parent, or 'host' in case of ShadowDOM
  1958. element = element.parentNode || element.host;
  1959. if (element === fabric.document) {
  1960. left = body.scrollLeft || docElement.scrollLeft || 0;
  1961. top = body.scrollTop || docElement.scrollTop || 0;
  1962. }
  1963. else {
  1964. left += element.scrollLeft || 0;
  1965. top += element.scrollTop || 0;
  1966. }
  1967. if (element.nodeType === 1 && element.style.position === 'fixed') {
  1968. break;
  1969. }
  1970. }
  1971. return { left: left, top: top };
  1972. }
  1973. /**
  1974. * Returns offset for a given element
  1975. * @function
  1976. * @memberOf fabric.util
  1977. * @param {HTMLElement} element Element to get offset for
  1978. * @return {Object} Object with "left" and "top" properties
  1979. */
  1980. function getElementOffset(element) {
  1981. var docElem,
  1982. doc = element && element.ownerDocument,
  1983. box = { left: 0, top: 0 },
  1984. offset = { left: 0, top: 0 },
  1985. scrollLeftTop,
  1986. offsetAttributes = {
  1987. borderLeftWidth: 'left',
  1988. borderTopWidth: 'top',
  1989. paddingLeft: 'left',
  1990. paddingTop: 'top'
  1991. };
  1992. if (!doc) {
  1993. return offset;
  1994. }
  1995. for (var attr in offsetAttributes) {
  1996. offset[offsetAttributes[attr]] += parseInt(getElementStyle(element, attr), 10) || 0;
  1997. }
  1998. docElem = doc.documentElement;
  1999. if ( typeof element.getBoundingClientRect !== 'undefined' ) {
  2000. box = element.getBoundingClientRect();
  2001. }
  2002. scrollLeftTop = getScrollLeftTop(element);
  2003. return {
  2004. left: box.left + scrollLeftTop.left - (docElem.clientLeft || 0) + offset.left,
  2005. top: box.top + scrollLeftTop.top - (docElem.clientTop || 0) + offset.top
  2006. };
  2007. }
  2008. /**
  2009. * Returns c_style attribute value of a given element
  2010. * @memberOf fabric.util
  2011. * @param {HTMLElement} element Element to get c_style attribute for
  2012. * @param {String} attr Style attribute to get for element
  2013. * @return {String} Style attribute value of the given element.
  2014. */
  2015. var getElementStyle;
  2016. if (fabric.document.defaultView && fabric.document.defaultView.getComputedStyle) {
  2017. getElementStyle = function(element, attr) {
  2018. var style = fabric.document.defaultView.getComputedStyle(element, null);
  2019. return style ? style[attr] : undefined;
  2020. };
  2021. }
  2022. else {
  2023. getElementStyle = function(element, attr) {
  2024. var value = element.style[attr];
  2025. if (!value && element.currentStyle) {
  2026. value = element.currentStyle[attr];
  2027. }
  2028. return value;
  2029. };
  2030. }
  2031. (function () {
  2032. var style = fabric.document.documentElement.style,
  2033. selectProp = 'userSelect' in style
  2034. ? 'userSelect'
  2035. : 'MozUserSelect' in style
  2036. ? 'MozUserSelect'
  2037. : 'WebkitUserSelect' in style
  2038. ? 'WebkitUserSelect'
  2039. : 'KhtmlUserSelect' in style
  2040. ? 'KhtmlUserSelect'
  2041. : '';
  2042. /**
  2043. * Makes element unselectable
  2044. * @memberOf fabric.util
  2045. * @param {HTMLElement} element Element to make unselectable
  2046. * @return {HTMLElement} Element that was passed in
  2047. */
  2048. function makeElementUnselectable(element) {
  2049. if (typeof element.onselectstart !== 'undefined') {
  2050. element.onselectstart = fabric.util.falseFunction;
  2051. }
  2052. if (selectProp) {
  2053. element.style[selectProp] = 'none';
  2054. }
  2055. else if (typeof element.unselectable === 'string') {
  2056. element.unselectable = 'on';
  2057. }
  2058. return element;
  2059. }
  2060. /**
  2061. * Makes element selectable
  2062. * @memberOf fabric.util
  2063. * @param {HTMLElement} element Element to make selectable
  2064. * @return {HTMLElement} Element that was passed in
  2065. */
  2066. function makeElementSelectable(element) {
  2067. if (typeof element.onselectstart !== 'undefined') {
  2068. element.onselectstart = null;
  2069. }
  2070. if (selectProp) {
  2071. element.style[selectProp] = '';
  2072. }
  2073. else if (typeof element.unselectable === 'string') {
  2074. element.unselectable = '';
  2075. }
  2076. return element;
  2077. }
  2078. fabric.util.makeElementUnselectable = makeElementUnselectable;
  2079. fabric.util.makeElementSelectable = makeElementSelectable;
  2080. })();
  2081. (function() {
  2082. /**
  2083. * Inserts a script element with a given url into a document; invokes callback, when that script is finished loading
  2084. * @memberOf fabric.util
  2085. * @param {String} url URL of a script to load
  2086. * @param {Function} callback Callback to execute when script is finished loading
  2087. */
  2088. function getScript(url, callback) {
  2089. var headEl = fabric.document.getElementsByTagName('head')[0],
  2090. scriptEl = fabric.document.createElement('script'),
  2091. loading = true;
  2092. /** @ignore */
  2093. scriptEl.onload = /** @ignore */ scriptEl.onreadystatechange = function(e) {
  2094. if (loading) {
  2095. if (typeof this.readyState === 'string' &&
  2096. this.readyState !== 'loaded' &&
  2097. this.readyState !== 'complete') {
  2098. return;
  2099. }
  2100. loading = false;
  2101. callback(e || fabric.window.event);
  2102. scriptEl = scriptEl.onload = scriptEl.onreadystatechange = null;
  2103. }
  2104. };
  2105. scriptEl.src = url;
  2106. headEl.appendChild(scriptEl);
  2107. // causes issue in Opera
  2108. // headEl.removeChild(scriptEl);
  2109. }
  2110. fabric.util.getScript = getScript;
  2111. })();
  2112. function getNodeCanvas(element) {
  2113. var impl = fabric.jsdomImplForWrapper(element);
  2114. return impl._canvas || impl._image;
  2115. };
  2116. fabric.util.getById = getById;
  2117. fabric.util.toArray = toArray;
  2118. fabric.util.makeElement = makeElement;
  2119. fabric.util.addClass = addClass;
  2120. fabric.util.wrapElement = wrapElement;
  2121. fabric.util.getScrollLeftTop = getScrollLeftTop;
  2122. fabric.util.getElementOffset = getElementOffset;
  2123. fabric.util.getElementStyle = getElementStyle;
  2124. fabric.util.getNodeCanvas = getNodeCanvas;
  2125. })();
  2126. (function() {
  2127. function addParamToUrl(url, param) {
  2128. return url + (/\?/.test(url) ? '&' : '?') + param;
  2129. }
  2130. var makeXHR = (function() {
  2131. var factories = [
  2132. function() { return new ActiveXObject('Microsoft.XMLHTTP'); },
  2133. function() { return new ActiveXObject('Msxml2.XMLHTTP'); },
  2134. function() { return new ActiveXObject('Msxml2.XMLHTTP.3.0'); },
  2135. function() { return new XMLHttpRequest(); }
  2136. ];
  2137. for (var i = factories.length; i--; ) {
  2138. try {
  2139. var req = factories[i]();
  2140. if (req) {
  2141. return factories[i];
  2142. }
  2143. }
  2144. catch (err) { }
  2145. }
  2146. })();
  2147. function emptyFn() { }
  2148. /**
  2149. * Cross-browser abstraction for sending XMLHttpRequest
  2150. * @memberOf fabric.util
  2151. * @param {String} url URL to send XMLHttpRequest to
  2152. * @param {Object} [options] Options object
  2153. * @param {String} [options.method="GET"]
  2154. * @param {String} [options.parameters] parameters to append to url in GET or in body
  2155. * @param {String} [options.body] body to send with POST or PUT request
  2156. * @param {Function} options.onComplete Callback to invoke when request is completed
  2157. * @return {XMLHttpRequest} request
  2158. */
  2159. function request(url, options) {
  2160. options || (options = { });
  2161. var method = options.method ? options.method.toUpperCase() : 'GET',
  2162. onComplete = options.onComplete || function() { },
  2163. xhr = makeXHR(),
  2164. body = options.body || options.parameters;
  2165. /** @ignore */
  2166. xhr.onreadystatechange = function() {
  2167. if (xhr.readyState === 4) {
  2168. onComplete(xhr);
  2169. xhr.onreadystatechange = emptyFn;
  2170. }
  2171. };
  2172. if (method === 'GET') {
  2173. body = null;
  2174. if (typeof options.parameters === 'string') {
  2175. url = addParamToUrl(url, options.parameters);
  2176. }
  2177. }
  2178. xhr.open(method, url, true);
  2179. if (method === 'POST' || method === 'PUT') {
  2180. xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
  2181. }
  2182. xhr.send(body);
  2183. return xhr;
  2184. }
  2185. fabric.util.request = request;
  2186. })();
  2187. /**
  2188. * Wrapper around `console.log` (when available)
  2189. * @param {*} [values] Values to log
  2190. */
  2191. fabric.log = function() { };
  2192. /**
  2193. * Wrapper around `console.warn` (when available)
  2194. * @param {*} [values] Values to log as a warning
  2195. */
  2196. fabric.warn = function() { };
  2197. /* eslint-disable */
  2198. if (typeof console !== 'undefined') {
  2199. ['log', 'warn'].forEach(function(methodName) {
  2200. if (typeof console[methodName] !== 'undefined' &&
  2201. typeof console[methodName].apply === 'function') {
  2202. fabric[methodName] = function() {
  2203. return console[methodName].apply(console, arguments);
  2204. };
  2205. }
  2206. });
  2207. }
  2208. /* eslint-enable */
  2209. (function(global) {
  2210. 'use strict';
  2211. /* Adaptation of work of Kevin Lindsey (kevin@kevlindev.com) */
  2212. var fabric = global.fabric || (global.fabric = { });
  2213. if (fabric.Point) {
  2214. fabric.warn('fabric.Point is already defined');
  2215. return;
  2216. }
  2217. fabric.Point = Point;
  2218. /**
  2219. * Point class
  2220. * @class fabric.Point
  2221. * @memberOf fabric
  2222. * @constructor
  2223. * @param {Number} x
  2224. * @param {Number} y
  2225. * @return {fabric.Point} thisArg
  2226. */
  2227. function Point(x, y) {
  2228. this.x = x;
  2229. this.y = y;
  2230. }
  2231. Point.prototype = /** @lends fabric.Point.prototype */ {
  2232. type: 'point',
  2233. constructor: Point,
  2234. /**
  2235. * Adds another point to this one and returns another one
  2236. * @param {fabric.Point} that
  2237. * @return {fabric.Point} new Point instance with added values
  2238. */
  2239. add: function (that) {
  2240. return new Point(this.x + that.x, this.y + that.y);
  2241. },
  2242. /**
  2243. * Adds another point to this one
  2244. * @param {fabric.Point} that
  2245. * @return {fabric.Point} thisArg
  2246. * @chainable
  2247. */
  2248. addEquals: function (that) {
  2249. this.x += that.x;
  2250. this.y += that.y;
  2251. return this;
  2252. },
  2253. /**
  2254. * Adds value to this point and returns a new one
  2255. * @param {Number} scalar
  2256. * @return {fabric.Point} new Point with added value
  2257. */
  2258. scalarAdd: function (scalar) {
  2259. return new Point(this.x + scalar, this.y + scalar);
  2260. },
  2261. /**
  2262. * Adds value to this point
  2263. * @param {Number} scalar
  2264. * @return {fabric.Point} thisArg
  2265. * @chainable
  2266. */
  2267. scalarAddEquals: function (scalar) {
  2268. this.x += scalar;
  2269. this.y += scalar;
  2270. return this;
  2271. },
  2272. /**
  2273. * Subtracts another point from this point and returns a new one
  2274. * @param {fabric.Point} that
  2275. * @return {fabric.Point} new Point object with subtracted values
  2276. */
  2277. subtract: function (that) {
  2278. return new Point(this.x - that.x, this.y - that.y);
  2279. },
  2280. /**
  2281. * Subtracts another point from this point
  2282. * @param {fabric.Point} that
  2283. * @return {fabric.Point} thisArg
  2284. * @chainable
  2285. */
  2286. subtractEquals: function (that) {
  2287. this.x -= that.x;
  2288. this.y -= that.y;
  2289. return this;
  2290. },
  2291. /**
  2292. * Subtracts value from this point and returns a new one
  2293. * @param {Number} scalar
  2294. * @return {fabric.Point}
  2295. */
  2296. scalarSubtract: function (scalar) {
  2297. return new Point(this.x - scalar, this.y - scalar);
  2298. },
  2299. /**
  2300. * Subtracts value from this point
  2301. * @param {Number} scalar
  2302. * @return {fabric.Point} thisArg
  2303. * @chainable
  2304. */
  2305. scalarSubtractEquals: function (scalar) {
  2306. this.x -= scalar;
  2307. this.y -= scalar;
  2308. return this;
  2309. },
  2310. /**
  2311. * Multiplies this point by a value and returns a new one
  2312. * TODO: rename in scalarMultiply in 2.0
  2313. * @param {Number} scalar
  2314. * @return {fabric.Point}
  2315. */
  2316. multiply: function (scalar) {
  2317. return new Point(this.x * scalar, this.y * scalar);
  2318. },
  2319. /**
  2320. * Multiplies this point by a value
  2321. * TODO: rename in scalarMultiplyEquals in 2.0
  2322. * @param {Number} scalar
  2323. * @return {fabric.Point} thisArg
  2324. * @chainable
  2325. */
  2326. multiplyEquals: function (scalar) {
  2327. this.x *= scalar;
  2328. this.y *= scalar;
  2329. return this;
  2330. },
  2331. /**
  2332. * Divides this point by a value and returns a new one
  2333. * TODO: rename in scalarDivide in 2.0
  2334. * @param {Number} scalar
  2335. * @return {fabric.Point}
  2336. */
  2337. divide: function (scalar) {
  2338. return new Point(this.x / scalar, this.y / scalar);
  2339. },
  2340. /**
  2341. * Divides this point by a value
  2342. * TODO: rename in scalarDivideEquals in 2.0
  2343. * @param {Number} scalar
  2344. * @return {fabric.Point} thisArg
  2345. * @chainable
  2346. */
  2347. divideEquals: function (scalar) {
  2348. this.x /= scalar;
  2349. this.y /= scalar;
  2350. return this;
  2351. },
  2352. /**
  2353. * Returns true if this point is equal to another one
  2354. * @param {fabric.Point} that
  2355. * @return {Boolean}
  2356. */
  2357. eq: function (that) {
  2358. return (this.x === that.x && this.y === that.y);
  2359. },
  2360. /**
  2361. * Returns true if this point is less than another one
  2362. * @param {fabric.Point} that
  2363. * @return {Boolean}
  2364. */
  2365. lt: function (that) {
  2366. return (this.x < that.x && this.y < that.y);
  2367. },
  2368. /**
  2369. * Returns true if this point is less than or equal to another one
  2370. * @param {fabric.Point} that
  2371. * @return {Boolean}
  2372. */
  2373. lte: function (that) {
  2374. return (this.x <= that.x && this.y <= that.y);
  2375. },
  2376. /**
  2377. * Returns true if this point is greater another one
  2378. * @param {fabric.Point} that
  2379. * @return {Boolean}
  2380. */
  2381. gt: function (that) {
  2382. return (this.x > that.x && this.y > that.y);
  2383. },
  2384. /**
  2385. * Returns true if this point is greater than or equal to another one
  2386. * @param {fabric.Point} that
  2387. * @return {Boolean}
  2388. */
  2389. gte: function (that) {
  2390. return (this.x >= that.x && this.y >= that.y);
  2391. },
  2392. /**
  2393. * Returns new point which is the result of linear interpolation with this one and another one
  2394. * @param {fabric.Point} that
  2395. * @param {Number} t , position of interpolation, between 0 and 1 default 0.5
  2396. * @return {fabric.Point}
  2397. */
  2398. lerp: function (that, t) {
  2399. if (typeof t === 'undefined') {
  2400. t = 0.5;
  2401. }
  2402. t = Math.max(Math.min(1, t), 0);
  2403. return new Point(this.x + (that.x - this.x) * t, this.y + (that.y - this.y) * t);
  2404. },
  2405. /**
  2406. * Returns distance from this point and another one
  2407. * @param {fabric.Point} that
  2408. * @return {Number}
  2409. */
  2410. distanceFrom: function (that) {
  2411. var dx = this.x - that.x,
  2412. dy = this.y - that.y;
  2413. return Math.sqrt(dx * dx + dy * dy);
  2414. },
  2415. /**
  2416. * Returns the point between this point and another one
  2417. * @param {fabric.Point} that
  2418. * @return {fabric.Point}
  2419. */
  2420. midPointFrom: function (that) {
  2421. return this.lerp(that);
  2422. },
  2423. /**
  2424. * Returns a new point which is the min of this and another one
  2425. * @param {fabric.Point} that
  2426. * @return {fabric.Point}
  2427. */
  2428. min: function (that) {
  2429. return new Point(Math.min(this.x, that.x), Math.min(this.y, that.y));
  2430. },
  2431. /**
  2432. * Returns a new point which is the max of this and another one
  2433. * @param {fabric.Point} that
  2434. * @return {fabric.Point}
  2435. */
  2436. max: function (that) {
  2437. return new Point(Math.max(this.x, that.x), Math.max(this.y, that.y));
  2438. },
  2439. /**
  2440. * Returns string representation of this point
  2441. * @return {String}
  2442. */
  2443. toString: function () {
  2444. return this.x + ',' + this.y;
  2445. },
  2446. /**
  2447. * Sets x/y of this point
  2448. * @param {Number} x
  2449. * @param {Number} y
  2450. * @chainable
  2451. */
  2452. setXY: function (x, y) {
  2453. this.x = x;
  2454. this.y = y;
  2455. return this;
  2456. },
  2457. /**
  2458. * Sets x of this point
  2459. * @param {Number} x
  2460. * @chainable
  2461. */
  2462. setX: function (x) {
  2463. this.x = x;
  2464. return this;
  2465. },
  2466. /**
  2467. * Sets y of this point
  2468. * @param {Number} y
  2469. * @chainable
  2470. */
  2471. setY: function (y) {
  2472. this.y = y;
  2473. return this;
  2474. },
  2475. /**
  2476. * Sets x/y of this point from another point
  2477. * @param {fabric.Point} that
  2478. * @chainable
  2479. */
  2480. setFromPoint: function (that) {
  2481. this.x = that.x;
  2482. this.y = that.y;
  2483. return this;
  2484. },
  2485. /**
  2486. * Swaps x/y of this point and another point
  2487. * @param {fabric.Point} that
  2488. */
  2489. swap: function (that) {
  2490. var x = this.x,
  2491. y = this.y;
  2492. this.x = that.x;
  2493. this.y = that.y;
  2494. that.x = x;
  2495. that.y = y;
  2496. },
  2497. /**
  2498. * return a cloned instance of the point
  2499. * @return {fabric.Point}
  2500. */
  2501. clone: function () {
  2502. return new Point(this.x, this.y);
  2503. }
  2504. };
  2505. })(typeof exports !== 'undefined' ? exports : this);
  2506. (function(global) {
  2507. 'use strict';
  2508. /* Adaptation of work of Kevin Lindsey (kevin@kevlindev.com) */
  2509. var fabric = global.fabric || (global.fabric = { });
  2510. if (fabric.Intersection) {
  2511. fabric.warn('fabric.Intersection is already defined');
  2512. return;
  2513. }
  2514. /**
  2515. * Intersection class
  2516. * @class fabric.Intersection
  2517. * @memberOf fabric
  2518. * @constructor
  2519. */
  2520. function Intersection(status) {
  2521. this.status = status;
  2522. this.points = [];
  2523. }
  2524. fabric.Intersection = Intersection;
  2525. fabric.Intersection.prototype = /** @lends fabric.Intersection.prototype */ {
  2526. constructor: Intersection,
  2527. /**
  2528. * Appends a point to intersection
  2529. * @param {fabric.Point} point
  2530. * @return {fabric.Intersection} thisArg
  2531. * @chainable
  2532. */
  2533. appendPoint: function (point) {
  2534. this.points.push(point);
  2535. return this;
  2536. },
  2537. /**
  2538. * Appends points to intersection
  2539. * @param {Array} points
  2540. * @return {fabric.Intersection} thisArg
  2541. * @chainable
  2542. */
  2543. appendPoints: function (points) {
  2544. this.points = this.points.concat(points);
  2545. return this;
  2546. }
  2547. };
  2548. /**
  2549. * Checks if one line intersects another
  2550. * TODO: rename in intersectSegmentSegment
  2551. * @static
  2552. * @param {fabric.Point} a1
  2553. * @param {fabric.Point} a2
  2554. * @param {fabric.Point} b1
  2555. * @param {fabric.Point} b2
  2556. * @return {fabric.Intersection}
  2557. */
  2558. fabric.Intersection.intersectLineLine = function (a1, a2, b1, b2) {
  2559. var result,
  2560. uaT = (b2.x - b1.x) * (a1.y - b1.y) - (b2.y - b1.y) * (a1.x - b1.x),
  2561. ubT = (a2.x - a1.x) * (a1.y - b1.y) - (a2.y - a1.y) * (a1.x - b1.x),
  2562. uB = (b2.y - b1.y) * (a2.x - a1.x) - (b2.x - b1.x) * (a2.y - a1.y);
  2563. if (uB !== 0) {
  2564. var ua = uaT / uB,
  2565. ub = ubT / uB;
  2566. if (0 <= ua && ua <= 1 && 0 <= ub && ub <= 1) {
  2567. result = new Intersection('Intersection');
  2568. result.appendPoint(new fabric.Point(a1.x + ua * (a2.x - a1.x), a1.y + ua * (a2.y - a1.y)));
  2569. }
  2570. else {
  2571. result = new Intersection();
  2572. }
  2573. }
  2574. else {
  2575. if (uaT === 0 || ubT === 0) {
  2576. result = new Intersection('Coincident');
  2577. }
  2578. else {
  2579. result = new Intersection('Parallel');
  2580. }
  2581. }
  2582. return result;
  2583. };
  2584. /**
  2585. * Checks if line intersects polygon
  2586. * TODO: rename in intersectSegmentPolygon
  2587. * fix detection of coincident
  2588. * @static
  2589. * @param {fabric.Point} a1
  2590. * @param {fabric.Point} a2
  2591. * @param {Array} points
  2592. * @return {fabric.Intersection}
  2593. */
  2594. fabric.Intersection.intersectLinePolygon = function(a1, a2, points) {
  2595. var result = new Intersection(),
  2596. length = points.length,
  2597. b1, b2, inter, i;
  2598. for (i = 0; i < length; i++) {
  2599. b1 = points[i];
  2600. b2 = points[(i + 1) % length];
  2601. inter = Intersection.intersectLineLine(a1, a2, b1, b2);
  2602. result.appendPoints(inter.points);
  2603. }
  2604. if (result.points.length > 0) {
  2605. result.status = 'Intersection';
  2606. }
  2607. return result;
  2608. };
  2609. /**
  2610. * Checks if polygon intersects another polygon
  2611. * @static
  2612. * @param {Array} points1
  2613. * @param {Array} points2
  2614. * @return {fabric.Intersection}
  2615. */
  2616. fabric.Intersection.intersectPolygonPolygon = function (points1, points2) {
  2617. var result = new Intersection(),
  2618. length = points1.length, i;
  2619. for (i = 0; i < length; i++) {
  2620. var a1 = points1[i],
  2621. a2 = points1[(i + 1) % length],
  2622. inter = Intersection.intersectLinePolygon(a1, a2, points2);
  2623. result.appendPoints(inter.points);
  2624. }
  2625. if (result.points.length > 0) {
  2626. result.status = 'Intersection';
  2627. }
  2628. return result;
  2629. };
  2630. /**
  2631. * Checks if polygon intersects rectangle
  2632. * @static
  2633. * @param {Array} points
  2634. * @param {fabric.Point} r1
  2635. * @param {fabric.Point} r2
  2636. * @return {fabric.Intersection}
  2637. */
  2638. fabric.Intersection.intersectPolygonRectangle = function (points, r1, r2) {
  2639. var min = r1.min(r2),
  2640. max = r1.max(r2),
  2641. topRight = new fabric.Point(max.x, min.y),
  2642. bottomLeft = new fabric.Point(min.x, max.y),
  2643. inter1 = Intersection.intersectLinePolygon(min, topRight, points),
  2644. inter2 = Intersection.intersectLinePolygon(topRight, max, points),
  2645. inter3 = Intersection.intersectLinePolygon(max, bottomLeft, points),
  2646. inter4 = Intersection.intersectLinePolygon(bottomLeft, min, points),
  2647. result = new Intersection();
  2648. result.appendPoints(inter1.points);
  2649. result.appendPoints(inter2.points);
  2650. result.appendPoints(inter3.points);
  2651. result.appendPoints(inter4.points);
  2652. if (result.points.length > 0) {
  2653. result.status = 'Intersection';
  2654. }
  2655. return result;
  2656. };
  2657. })(typeof exports !== 'undefined' ? exports : this);
  2658. (function(global) {
  2659. 'use strict';
  2660. var fabric = global.fabric || (global.fabric = { });
  2661. if (fabric.Color) {
  2662. fabric.warn('fabric.Color is already defined.');
  2663. return;
  2664. }
  2665. /**
  2666. * Color class
  2667. * The purpose of {@link fabric.Color} is to abstract and encapsulate common color operations;
  2668. * {@link fabric.Color} is a constructor and creates instances of {@link fabric.Color} objects.
  2669. *
  2670. * @class fabric.Color
  2671. * @param {String} color optional in hex or rgb(a) or hsl format or from known color list
  2672. * @return {fabric.Color} thisArg
  2673. * @tutorial {@link http://fabricjs.com/fabric-intro-part-2/#colors}
  2674. */
  2675. function Color(color) {
  2676. if (!color) {
  2677. this.setSource([0, 0, 0, 1]);
  2678. }
  2679. else {
  2680. this._tryParsingColor(color);
  2681. }
  2682. }
  2683. fabric.Color = Color;
  2684. fabric.Color.prototype = /** @lends fabric.Color.prototype */ {
  2685. /**
  2686. * @private
  2687. * @param {String|Array} color Color value to parse
  2688. */
  2689. _tryParsingColor: function(color) {
  2690. var source;
  2691. if (color in Color.colorNameMap) {
  2692. color = Color.colorNameMap[color];
  2693. }
  2694. if (color === 'transparent') {
  2695. source = [255, 255, 255, 0];
  2696. }
  2697. if (!source) {
  2698. source = Color.sourceFromHex(color);
  2699. }
  2700. if (!source) {
  2701. source = Color.sourceFromRgb(color);
  2702. }
  2703. if (!source) {
  2704. source = Color.sourceFromHsl(color);
  2705. }
  2706. if (!source) {
  2707. //if color is not recognize let's make black as canvas does
  2708. source = [0, 0, 0, 1];
  2709. }
  2710. if (source) {
  2711. this.setSource(source);
  2712. }
  2713. },
  2714. /**
  2715. * Adapted from <a href="https://rawgithub.com/mjijackson/mjijackson.github.com/master/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript.html">https://github.com/mjijackson</a>
  2716. * @private
  2717. * @param {Number} r Red color value
  2718. * @param {Number} g Green color value
  2719. * @param {Number} b Blue color value
  2720. * @return {Array} Hsl color
  2721. */
  2722. _rgbToHsl: function(r, g, b) {
  2723. r /= 255; g /= 255; b /= 255;
  2724. var h, s, l,
  2725. max = fabric.util.array.max([r, g, b]),
  2726. min = fabric.util.array.min([r, g, b]);
  2727. l = (max + min) / 2;
  2728. if (max === min) {
  2729. h = s = 0; // achromatic
  2730. }
  2731. else {
  2732. var d = max - min;
  2733. s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
  2734. switch (max) {
  2735. case r:
  2736. h = (g - b) / d + (g < b ? 6 : 0);
  2737. break;
  2738. case g:
  2739. h = (b - r) / d + 2;
  2740. break;
  2741. case b:
  2742. h = (r - g) / d + 4;
  2743. break;
  2744. }
  2745. h /= 6;
  2746. }
  2747. return [
  2748. Math.round(h * 360),
  2749. Math.round(s * 100),
  2750. Math.round(l * 100)
  2751. ];
  2752. },
  2753. /**
  2754. * Returns source of this color (where source is an array representation; ex: [200, 200, 100, 1])
  2755. * @return {Array}
  2756. */
  2757. getSource: function() {
  2758. return this._source;
  2759. },
  2760. /**
  2761. * Sets source of this color (where source is an array representation; ex: [200, 200, 100, 1])
  2762. * @param {Array} source
  2763. */
  2764. setSource: function(source) {
  2765. this._source = source;
  2766. },
  2767. /**
  2768. * Returns color representation in RGB format
  2769. * @return {String} ex: rgb(0-255,0-255,0-255)
  2770. */
  2771. toRgb: function() {
  2772. var source = this.getSource();
  2773. return 'rgb(' + source[0] + ',' + source[1] + ',' + source[2] + ')';
  2774. },
  2775. /**
  2776. * Returns color representation in RGBA format
  2777. * @return {String} ex: rgba(0-255,0-255,0-255,0-1)
  2778. */
  2779. toRgba: function() {
  2780. var source = this.getSource();
  2781. return 'rgba(' + source[0] + ',' + source[1] + ',' + source[2] + ',' + source[3] + ')';
  2782. },
  2783. /**
  2784. * Returns color representation in HSL format
  2785. * @return {String} ex: hsl(0-360,0%-100%,0%-100%)
  2786. */
  2787. toHsl: function() {
  2788. var source = this.getSource(),
  2789. hsl = this._rgbToHsl(source[0], source[1], source[2]);
  2790. return 'hsl(' + hsl[0] + ',' + hsl[1] + '%,' + hsl[2] + '%)';
  2791. },
  2792. /**
  2793. * Returns color representation in HSLA format
  2794. * @return {String} ex: hsla(0-360,0%-100%,0%-100%,0-1)
  2795. */
  2796. toHsla: function() {
  2797. var source = this.getSource(),
  2798. hsl = this._rgbToHsl(source[0], source[1], source[2]);
  2799. return 'hsla(' + hsl[0] + ',' + hsl[1] + '%,' + hsl[2] + '%,' + source[3] + ')';
  2800. },
  2801. /**
  2802. * Returns color representation in HEX format
  2803. * @return {String} ex: FF5555
  2804. */
  2805. toHex: function() {
  2806. var source = this.getSource(), r, g, b;
  2807. r = source[0].toString(16);
  2808. r = (r.length === 1) ? ('0' + r) : r;
  2809. g = source[1].toString(16);
  2810. g = (g.length === 1) ? ('0' + g) : g;
  2811. b = source[2].toString(16);
  2812. b = (b.length === 1) ? ('0' + b) : b;
  2813. return r.toUpperCase() + g.toUpperCase() + b.toUpperCase();
  2814. },
  2815. /**
  2816. * Returns color representation in HEXA format
  2817. * @return {String} ex: FF5555CC
  2818. */
  2819. toHexa: function() {
  2820. var source = this.getSource(), a;
  2821. a = Math.round(source[3] * 255);
  2822. a = a.toString(16);
  2823. a = (a.length === 1) ? ('0' + a) : a;
  2824. return this.toHex() + a.toUpperCase();
  2825. },
  2826. /**
  2827. * Gets value of alpha channel for this color
  2828. * @return {Number} 0-1
  2829. */
  2830. getAlpha: function() {
  2831. return this.getSource()[3];
  2832. },
  2833. /**
  2834. * Sets value of alpha channel for this color
  2835. * @param {Number} alpha Alpha value 0-1
  2836. * @return {fabric.Color} thisArg
  2837. */
  2838. setAlpha: function(alpha) {
  2839. var source = this.getSource();
  2840. source[3] = alpha;
  2841. this.setSource(source);
  2842. return this;
  2843. },
  2844. /**
  2845. * Transforms color to its grayscale representation
  2846. * @return {fabric.Color} thisArg
  2847. */
  2848. toGrayscale: function() {
  2849. var source = this.getSource(),
  2850. average = parseInt((source[0] * 0.3 + source[1] * 0.59 + source[2] * 0.11).toFixed(0), 10),
  2851. currentAlpha = source[3];
  2852. this.setSource([average, average, average, currentAlpha]);
  2853. return this;
  2854. },
  2855. /**
  2856. * Transforms color to its black and white representation
  2857. * @param {Number} threshold
  2858. * @return {fabric.Color} thisArg
  2859. */
  2860. toBlackWhite: function(threshold) {
  2861. var source = this.getSource(),
  2862. average = (source[0] * 0.3 + source[1] * 0.59 + source[2] * 0.11).toFixed(0),
  2863. currentAlpha = source[3];
  2864. threshold = threshold || 127;
  2865. average = (Number(average) < Number(threshold)) ? 0 : 255;
  2866. this.setSource([average, average, average, currentAlpha]);
  2867. return this;
  2868. },
  2869. /**
  2870. * Overlays color with another color
  2871. * @param {String|fabric.Color} otherColor
  2872. * @return {fabric.Color} thisArg
  2873. */
  2874. overlayWith: function(otherColor) {
  2875. if (!(otherColor instanceof Color)) {
  2876. otherColor = new Color(otherColor);
  2877. }
  2878. var result = [],
  2879. alpha = this.getAlpha(),
  2880. otherAlpha = 0.5,
  2881. source = this.getSource(),
  2882. otherSource = otherColor.getSource(), i;
  2883. for (i = 0; i < 3; i++) {
  2884. result.push(Math.round((source[i] * (1 - otherAlpha)) + (otherSource[i] * otherAlpha)));
  2885. }
  2886. result[3] = alpha;
  2887. this.setSource(result);
  2888. return this;
  2889. }
  2890. };
  2891. /**
  2892. * Regex matching color in RGB or RGBA formats (ex: rgb(0, 0, 0), rgba(255, 100, 10, 0.5), rgba( 255 , 100 , 10 , 0.5 ), rgb(1,1,1), rgba(100%, 60%, 10%, 0.5))
  2893. * @static
  2894. * @field
  2895. * @memberOf fabric.Color
  2896. */
  2897. // eslint-disable-next-line max-len
  2898. fabric.Color.reRGBa = /^rgba?\(\s*(\d{1,3}(?:\.\d+)?\%?)\s*,\s*(\d{1,3}(?:\.\d+)?\%?)\s*,\s*(\d{1,3}(?:\.\d+)?\%?)\s*(?:\s*,\s*((?:\d*\.?\d+)?)\s*)?\)$/i;
  2899. /**
  2900. * Regex matching color in HSL or HSLA formats (ex: hsl(200, 80%, 10%), hsla(300, 50%, 80%, 0.5), hsla( 300 , 50% , 80% , 0.5 ))
  2901. * @static
  2902. * @field
  2903. * @memberOf fabric.Color
  2904. */
  2905. fabric.Color.reHSLa = /^hsla?\(\s*(\d{1,3})\s*,\s*(\d{1,3}\%)\s*,\s*(\d{1,3}\%)\s*(?:\s*,\s*(\d+(?:\.\d+)?)\s*)?\)$/i;
  2906. /**
  2907. * Regex matching color in HEX format (ex: #FF5544CC, #FF5555, 010155, aff)
  2908. * @static
  2909. * @field
  2910. * @memberOf fabric.Color
  2911. */
  2912. fabric.Color.reHex = /^#?([0-9a-f]{8}|[0-9a-f]{6}|[0-9a-f]{4}|[0-9a-f]{3})$/i;
  2913. /**
  2914. * Map of the 148 color names with HEX code
  2915. * @static
  2916. * @field
  2917. * @memberOf fabric.Color
  2918. * @see: https://www.w3.org/TR/css3-color/#svg-color
  2919. */
  2920. fabric.Color.colorNameMap = {
  2921. aliceblue: '#F0F8FF',
  2922. antiquewhite: '#FAEBD7',
  2923. aqua: '#00FFFF',
  2924. aquamarine: '#7FFFD4',
  2925. azure: '#F0FFFF',
  2926. beige: '#F5F5DC',
  2927. bisque: '#FFE4C4',
  2928. black: '#000000',
  2929. blanchedalmond: '#FFEBCD',
  2930. blue: '#0000FF',
  2931. blueviolet: '#8A2BE2',
  2932. brown: '#A52A2A',
  2933. burlywood: '#DEB887',
  2934. cadetblue: '#5F9EA0',
  2935. chartreuse: '#7FFF00',
  2936. chocolate: '#D2691E',
  2937. coral: '#FF7F50',
  2938. cornflowerblue: '#6495ED',
  2939. cornsilk: '#FFF8DC',
  2940. crimson: '#DC143C',
  2941. cyan: '#00FFFF',
  2942. darkblue: '#00008B',
  2943. darkcyan: '#008B8B',
  2944. darkgoldenrod: '#B8860B',
  2945. darkgray: '#A9A9A9',
  2946. darkgrey: '#A9A9A9',
  2947. darkgreen: '#006400',
  2948. darkkhaki: '#BDB76B',
  2949. darkmagenta: '#8B008B',
  2950. darkolivegreen: '#556B2F',
  2951. darkorange: '#FF8C00',
  2952. darkorchid: '#9932CC',
  2953. darkred: '#8B0000',
  2954. darksalmon: '#E9967A',
  2955. darkseagreen: '#8FBC8F',
  2956. darkslateblue: '#483D8B',
  2957. darkslategray: '#2F4F4F',
  2958. darkslategrey: '#2F4F4F',
  2959. darkturquoise: '#00CED1',
  2960. darkviolet: '#9400D3',
  2961. deeppink: '#FF1493',
  2962. deepskyblue: '#00BFFF',
  2963. dimgray: '#696969',
  2964. dimgrey: '#696969',
  2965. dodgerblue: '#1E90FF',
  2966. firebrick: '#B22222',
  2967. floralwhite: '#FFFAF0',
  2968. forestgreen: '#228B22',
  2969. fuchsia: '#FF00FF',
  2970. gainsboro: '#DCDCDC',
  2971. ghostwhite: '#F8F8FF',
  2972. gold: '#FFD700',
  2973. goldenrod: '#DAA520',
  2974. gray: '#808080',
  2975. grey: '#808080',
  2976. green: '#008000',
  2977. greenyellow: '#ADFF2F',
  2978. honeydew: '#F0FFF0',
  2979. hotpink: '#FF69B4',
  2980. indianred: '#CD5C5C',
  2981. indigo: '#4B0082',
  2982. ivory: '#FFFFF0',
  2983. khaki: '#F0E68C',
  2984. lavender: '#E6E6FA',
  2985. lavenderblush: '#FFF0F5',
  2986. lawngreen: '#7CFC00',
  2987. lemonchiffon: '#FFFACD',
  2988. lightblue: '#ADD8E6',
  2989. lightcoral: '#F08080',
  2990. lightcyan: '#E0FFFF',
  2991. lightgoldenrodyellow: '#FAFAD2',
  2992. lightgray: '#D3D3D3',
  2993. lightgrey: '#D3D3D3',
  2994. lightgreen: '#90EE90',
  2995. lightpink: '#FFB6C1',
  2996. lightsalmon: '#FFA07A',
  2997. lightseagreen: '#20B2AA',
  2998. lightskyblue: '#87CEFA',
  2999. lightslategray: '#778899',
  3000. lightslategrey: '#778899',
  3001. lightsteelblue: '#B0C4DE',
  3002. lightyellow: '#FFFFE0',
  3003. lime: '#00FF00',
  3004. limegreen: '#32CD32',
  3005. linen: '#FAF0E6',
  3006. magenta: '#FF00FF',
  3007. maroon: '#800000',
  3008. mediumaquamarine: '#66CDAA',
  3009. mediumblue: '#0000CD',
  3010. mediumorchid: '#BA55D3',
  3011. mediumpurple: '#9370DB',
  3012. mediumseagreen: '#3CB371',
  3013. mediumslateblue: '#7B68EE',
  3014. mediumspringgreen: '#00FA9A',
  3015. mediumturquoise: '#48D1CC',
  3016. mediumvioletred: '#C71585',
  3017. midnightblue: '#191970',
  3018. mintcream: '#F5FFFA',
  3019. mistyrose: '#FFE4E1',
  3020. moccasin: '#FFE4B5',
  3021. navajowhite: '#FFDEAD',
  3022. navy: '#000080',
  3023. oldlace: '#FDF5E6',
  3024. olive: '#808000',
  3025. olivedrab: '#6B8E23',
  3026. orange: '#FFA500',
  3027. orangered: '#FF4500',
  3028. orchid: '#DA70D6',
  3029. palegoldenrod: '#EEE8AA',
  3030. palegreen: '#98FB98',
  3031. paleturquoise: '#AFEEEE',
  3032. palevioletred: '#DB7093',
  3033. papayawhip: '#FFEFD5',
  3034. peachpuff: '#FFDAB9',
  3035. peru: '#CD853F',
  3036. pink: '#FFC0CB',
  3037. plum: '#DDA0DD',
  3038. powderblue: '#B0E0E6',
  3039. purple: '#800080',
  3040. rebeccapurple: '#663399',
  3041. red: '#FF0000',
  3042. rosybrown: '#BC8F8F',
  3043. royalblue: '#4169E1',
  3044. saddlebrown: '#8B4513',
  3045. salmon: '#FA8072',
  3046. sandybrown: '#F4A460',
  3047. seagreen: '#2E8B57',
  3048. seashell: '#FFF5EE',
  3049. sienna: '#A0522D',
  3050. silver: '#C0C0C0',
  3051. skyblue: '#87CEEB',
  3052. slateblue: '#6A5ACD',
  3053. slategray: '#708090',
  3054. slategrey: '#708090',
  3055. snow: '#FFFAFA',
  3056. springgreen: '#00FF7F',
  3057. steelblue: '#4682B4',
  3058. tan: '#D2B48C',
  3059. teal: '#008080',
  3060. thistle: '#D8BFD8',
  3061. tomato: '#FF6347',
  3062. turquoise: '#40E0D0',
  3063. violet: '#EE82EE',
  3064. wheat: '#F5DEB3',
  3065. white: '#FFFFFF',
  3066. whitesmoke: '#F5F5F5',
  3067. yellow: '#FFFF00',
  3068. yellowgreen: '#9ACD32'
  3069. };
  3070. /**
  3071. * @private
  3072. * @param {Number} p
  3073. * @param {Number} q
  3074. * @param {Number} t
  3075. * @return {Number}
  3076. */
  3077. function hue2rgb(p, q, t) {
  3078. if (t < 0) {
  3079. t += 1;
  3080. }
  3081. if (t > 1) {
  3082. t -= 1;
  3083. }
  3084. if (t < 1 / 6) {
  3085. return p + (q - p) * 6 * t;
  3086. }
  3087. if (t < 1 / 2) {
  3088. return q;
  3089. }
  3090. if (t < 2 / 3) {
  3091. return p + (q - p) * (2 / 3 - t) * 6;
  3092. }
  3093. return p;
  3094. }
  3095. /**
  3096. * Returns new color object, when given a color in RGB format
  3097. * @memberOf fabric.Color
  3098. * @param {String} color Color value ex: rgb(0-255,0-255,0-255)
  3099. * @return {fabric.Color}
  3100. */
  3101. fabric.Color.fromRgb = function(color) {
  3102. return Color.fromSource(Color.sourceFromRgb(color));
  3103. };
  3104. /**
  3105. * Returns array representation (ex: [100, 100, 200, 1]) of a color that's in RGB or RGBA format
  3106. * @memberOf fabric.Color
  3107. * @param {String} color Color value ex: rgb(0-255,0-255,0-255), rgb(0%-100%,0%-100%,0%-100%)
  3108. * @return {Array} source
  3109. */
  3110. fabric.Color.sourceFromRgb = function(color) {
  3111. var match = color.match(Color.reRGBa);
  3112. if (match) {
  3113. var r = parseInt(match[1], 10) / (/%$/.test(match[1]) ? 100 : 1) * (/%$/.test(match[1]) ? 255 : 1),
  3114. g = parseInt(match[2], 10) / (/%$/.test(match[2]) ? 100 : 1) * (/%$/.test(match[2]) ? 255 : 1),
  3115. b = parseInt(match[3], 10) / (/%$/.test(match[3]) ? 100 : 1) * (/%$/.test(match[3]) ? 255 : 1);
  3116. return [
  3117. parseInt(r, 10),
  3118. parseInt(g, 10),
  3119. parseInt(b, 10),
  3120. match[4] ? parseFloat(match[4]) : 1
  3121. ];
  3122. }
  3123. };
  3124. /**
  3125. * Returns new color object, when given a color in RGBA format
  3126. * @static
  3127. * @function
  3128. * @memberOf fabric.Color
  3129. * @param {String} color
  3130. * @return {fabric.Color}
  3131. */
  3132. fabric.Color.fromRgba = Color.fromRgb;
  3133. /**
  3134. * Returns new color object, when given a color in HSL format
  3135. * @param {String} color Color value ex: hsl(0-260,0%-100%,0%-100%)
  3136. * @memberOf fabric.Color
  3137. * @return {fabric.Color}
  3138. */
  3139. fabric.Color.fromHsl = function(color) {
  3140. return Color.fromSource(Color.sourceFromHsl(color));
  3141. };
  3142. /**
  3143. * Returns array representation (ex: [100, 100, 200, 1]) of a color that's in HSL or HSLA format.
  3144. * Adapted from <a href="https://rawgithub.com/mjijackson/mjijackson.github.com/master/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript.html">https://github.com/mjijackson</a>
  3145. * @memberOf fabric.Color
  3146. * @param {String} color Color value ex: hsl(0-360,0%-100%,0%-100%) or hsla(0-360,0%-100%,0%-100%, 0-1)
  3147. * @return {Array} source
  3148. * @see http://http://www.w3.org/TR/css3-color/#hsl-color
  3149. */
  3150. fabric.Color.sourceFromHsl = function(color) {
  3151. var match = color.match(Color.reHSLa);
  3152. if (!match) {
  3153. return;
  3154. }
  3155. var h = (((parseFloat(match[1]) % 360) + 360) % 360) / 360,
  3156. s = parseFloat(match[2]) / (/%$/.test(match[2]) ? 100 : 1),
  3157. l = parseFloat(match[3]) / (/%$/.test(match[3]) ? 100 : 1),
  3158. r, g, b;
  3159. if (s === 0) {
  3160. r = g = b = l;
  3161. }
  3162. else {
  3163. var q = l <= 0.5 ? l * (s + 1) : l + s - l * s,
  3164. p = l * 2 - q;
  3165. r = hue2rgb(p, q, h + 1 / 3);
  3166. g = hue2rgb(p, q, h);
  3167. b = hue2rgb(p, q, h - 1 / 3);
  3168. }
  3169. return [
  3170. Math.round(r * 255),
  3171. Math.round(g * 255),
  3172. Math.round(b * 255),
  3173. match[4] ? parseFloat(match[4]) : 1
  3174. ];
  3175. };
  3176. /**
  3177. * Returns new color object, when given a color in HSLA format
  3178. * @static
  3179. * @function
  3180. * @memberOf fabric.Color
  3181. * @param {String} color
  3182. * @return {fabric.Color}
  3183. */
  3184. fabric.Color.fromHsla = Color.fromHsl;
  3185. /**
  3186. * Returns new color object, when given a color in HEX format
  3187. * @static
  3188. * @memberOf fabric.Color
  3189. * @param {String} color Color value ex: FF5555
  3190. * @return {fabric.Color}
  3191. */
  3192. fabric.Color.fromHex = function(color) {
  3193. return Color.fromSource(Color.sourceFromHex(color));
  3194. };
  3195. /**
  3196. * Returns array representation (ex: [100, 100, 200, 1]) of a color that's in HEX format
  3197. * @static
  3198. * @memberOf fabric.Color
  3199. * @param {String} color ex: FF5555 or FF5544CC (RGBa)
  3200. * @return {Array} source
  3201. */
  3202. fabric.Color.sourceFromHex = function(color) {
  3203. if (color.match(Color.reHex)) {
  3204. var value = color.slice(color.indexOf('#') + 1),
  3205. isShortNotation = (value.length === 3 || value.length === 4),
  3206. isRGBa = (value.length === 8 || value.length === 4),
  3207. r = isShortNotation ? (value.charAt(0) + value.charAt(0)) : value.substring(0, 2),
  3208. g = isShortNotation ? (value.charAt(1) + value.charAt(1)) : value.substring(2, 4),
  3209. b = isShortNotation ? (value.charAt(2) + value.charAt(2)) : value.substring(4, 6),
  3210. a = isRGBa ? (isShortNotation ? (value.charAt(3) + value.charAt(3)) : value.substring(6, 8)) : 'FF';
  3211. return [
  3212. parseInt(r, 16),
  3213. parseInt(g, 16),
  3214. parseInt(b, 16),
  3215. parseFloat((parseInt(a, 16) / 255).toFixed(2))
  3216. ];
  3217. }
  3218. };
  3219. /**
  3220. * Returns new color object, when given color in array representation (ex: [200, 100, 100, 0.5])
  3221. * @static
  3222. * @memberOf fabric.Color
  3223. * @param {Array} source
  3224. * @return {fabric.Color}
  3225. */
  3226. fabric.Color.fromSource = function(source) {
  3227. var oColor = new Color();
  3228. oColor.setSource(source);
  3229. return oColor;
  3230. };
  3231. })(typeof exports !== 'undefined' ? exports : this);
  3232. (function () {
  3233. 'use strict';
  3234. if (fabric.StaticCanvas) {
  3235. fabric.warn('fabric.StaticCanvas is already defined.');
  3236. return;
  3237. }
  3238. // aliases for faster resolution
  3239. var extend = fabric.util.object.extend,
  3240. getElementOffset = fabric.util.getElementOffset,
  3241. removeFromArray = fabric.util.removeFromArray,
  3242. toFixed = fabric.util.toFixed,
  3243. transformPoint = fabric.util.transformPoint,
  3244. invertTransform = fabric.util.invertTransform,
  3245. CANVAS_INIT_ERROR = new Error('Could not initialize `canvas` element');
  3246. /**
  3247. * Static canvas class
  3248. * @class fabric.StaticCanvas
  3249. * @mixes fabric.Collection
  3250. * @mixes fabric.Observable
  3251. * @see {@link http://fabricjs.com/static_canvas|StaticCanvas demo}
  3252. * @see {@link fabric.StaticCanvas#initialize} for constructor definition
  3253. * @fires before:render
  3254. * @fires after:render
  3255. * @fires canvas:cleared
  3256. * @fires object:added
  3257. * @fires object:removed
  3258. */
  3259. fabric.StaticCanvas = fabric.util.createClass(fabric.CommonMethods, /** @lends fabric.StaticCanvas.prototype */ {
  3260. /**
  3261. * Constructor
  3262. * @param {HTMLElement | String} el &lt;canvas> element to initialize instance on
  3263. * @param {Object} [options] Options object
  3264. * @return {Object} thisArg
  3265. */
  3266. initialize: function(el, options) {
  3267. options || (options = { });
  3268. this.renderAndResetBound = this.renderAndReset.bind(this);
  3269. this.requestRenderAllBound = this.requestRenderAll.bind(this);
  3270. this._initStatic(el, options);
  3271. },
  3272. /**
  3273. * Background color of canvas instance.
  3274. * Should be set via {@link fabric.StaticCanvas#setBackgroundColor}.
  3275. * @type {(String|fabric.Pattern)}
  3276. * @default
  3277. */
  3278. backgroundColor: '',
  3279. /**
  3280. * Background c_image of canvas instance.
  3281. * Should be set via {@link fabric.StaticCanvas#setBackgroundImage}.
  3282. * <b>Backwards incompatibility note:</b> The "backgroundImageOpacity"
  3283. * and "backgroundImageStretch" properties are deprecated since 1.3.9.
  3284. * Use {@link fabric.Image#opacity}, {@link fabric.Image#width} and {@link fabric.Image#height}.
  3285. * @type fabric.Image
  3286. * @default
  3287. */
  3288. backgroundImage: null,
  3289. /**
  3290. * Overlay color of canvas instance.
  3291. * Should be set via {@link fabric.StaticCanvas#setOverlayColor}
  3292. * @since 1.3.9
  3293. * @type {(String|fabric.Pattern)}
  3294. * @default
  3295. */
  3296. overlayColor: '',
  3297. /**
  3298. * Overlay c_image of canvas instance.
  3299. * Should be set via {@link fabric.StaticCanvas#setOverlayImage}.
  3300. * <b>Backwards incompatibility note:</b> The "overlayImageLeft"
  3301. * and "overlayImageTop" properties are deprecated since 1.3.9.
  3302. * Use {@link fabric.Image#left} and {@link fabric.Image#top}.
  3303. * @type fabric.Image
  3304. * @default
  3305. */
  3306. overlayImage: null,
  3307. /**
  3308. * Indicates whether toObject/toDatalessObject should include default values
  3309. * @type Boolean
  3310. * @default
  3311. */
  3312. includeDefaultValues: true,
  3313. /**
  3314. * Indicates whether objects' state should be saved
  3315. * @type Boolean
  3316. * @default
  3317. */
  3318. stateful: false,
  3319. /**
  3320. * Indicates whether {@link fabric.Collection.add}, {@link fabric.Collection.insertAt} and {@link fabric.Collection.remove},
  3321. * {@link fabric.StaticCanvas.moveTo}, {@link fabric.StaticCanvas.clear} and many more, should also re-render canvas.
  3322. * Disabling this option will not give a performance boost when adding/removing a lot of objects to/from canvas at once
  3323. * since the renders are quequed and executed one per frame.
  3324. * Disabling is suggested anyway and managing the renders of the app manually is not a big effort ( canvas.requestRenderAll() )
  3325. * Left default to true to do not break documentation and old app, fiddles.
  3326. * @type Boolean
  3327. * @default
  3328. */
  3329. renderOnAddRemove: true,
  3330. /**
  3331. * Function that determines clipping of entire canvas area
  3332. * Being passed context as first argument. See clipping canvas area in {@link https://github.com/kangax/fabric.js/wiki/FAQ}
  3333. * @deprecated since 2.0.0
  3334. * @type Function
  3335. * @default
  3336. */
  3337. clipTo: null,
  3338. /**
  3339. * Indicates whether object controls (borders/controls) are rendered above overlay c_image
  3340. * @type Boolean
  3341. * @default
  3342. */
  3343. controlsAboveOverlay: false,
  3344. /**
  3345. * Indicates whether the browser can be scrolled when using a touchscreen and dragging on the canvas
  3346. * @type Boolean
  3347. * @default
  3348. */
  3349. allowTouchScrolling: false,
  3350. /**
  3351. * Indicates whether this canvas will use c_image smoothing, this is on by default in browsers
  3352. * @type Boolean
  3353. * @default
  3354. */
  3355. imageSmoothingEnabled: true,
  3356. /**
  3357. * The transformation (in the format of Canvas transform) which focuses the viewport
  3358. * @type Array
  3359. * @default
  3360. */
  3361. viewportTransform: fabric.iMatrix.concat(),
  3362. /**
  3363. * if set to false background c_image is not affected by viewport transform
  3364. * @since 1.6.3
  3365. * @type Boolean
  3366. * @default
  3367. */
  3368. backgroundVpt: true,
  3369. /**
  3370. * if set to false overlya c_image is not affected by viewport transform
  3371. * @since 1.6.3
  3372. * @type Boolean
  3373. * @default
  3374. */
  3375. overlayVpt: true,
  3376. /**
  3377. * Callback; invoked right before object is about to be scaled/rotated
  3378. * @deprecated since 2.3.0
  3379. * Use before:transform event
  3380. */
  3381. onBeforeScaleRotate: function () {
  3382. /* NOOP */
  3383. },
  3384. /**
  3385. * When true, canvas is scaled by devicePixelRatio for better rendering on retina screens
  3386. * @type Boolean
  3387. * @default
  3388. */
  3389. enableRetinaScaling: true,
  3390. /**
  3391. * Describe canvas element extension over design
  3392. * properties are tl,tr,bl,br.
  3393. * if canvas is not zoomed/panned those points are the four corner of canvas
  3394. * if canvas is viewportTransformed you those points indicate the extension
  3395. * of canvas element in plain untrasformed coordinates
  3396. * The coordinates get updated with @method calcViewportBoundaries.
  3397. * @memberOf fabric.StaticCanvas.prototype
  3398. */
  3399. vptCoords: { },
  3400. /**
  3401. * Based on vptCoords and object.aCoords, skip rendering of objects that
  3402. * are not included in current viewport.
  3403. * May greatly help in applications with crowded canvas and use of zoom/pan
  3404. * If One of the corner of the bounding box of the object is on the canvas
  3405. * the objects get rendered.
  3406. * @memberOf fabric.StaticCanvas.prototype
  3407. * @type Boolean
  3408. * @default
  3409. */
  3410. skipOffscreen: true,
  3411. /**
  3412. * @private
  3413. * @param {HTMLElement | String} el &lt;canvas> element to initialize instance on
  3414. * @param {Object} [options] Options object
  3415. */
  3416. _initStatic: function(el, options) {
  3417. var cb = this.requestRenderAllBound;
  3418. this._objects = [];
  3419. this._createLowerCanvas(el);
  3420. this._initOptions(options);
  3421. this._setImageSmoothing();
  3422. // only initialize retina scaling once
  3423. if (!this.interactive) {
  3424. this._initRetinaScaling();
  3425. }
  3426. if (options.overlayImage) {
  3427. this.setOverlayImage(options.overlayImage, cb);
  3428. }
  3429. if (options.backgroundImage) {
  3430. this.setBackgroundImage(options.backgroundImage, cb);
  3431. }
  3432. if (options.backgroundColor) {
  3433. this.setBackgroundColor(options.backgroundColor, cb);
  3434. }
  3435. if (options.overlayColor) {
  3436. this.setOverlayColor(options.overlayColor, cb);
  3437. }
  3438. this.calcOffset();
  3439. },
  3440. /**
  3441. * @private
  3442. */
  3443. _isRetinaScaling: function() {
  3444. return (fabric.devicePixelRatio !== 1 && this.enableRetinaScaling);
  3445. },
  3446. /**
  3447. * @private
  3448. * @return {Number} retinaScaling if applied, otherwise 1;
  3449. */
  3450. getRetinaScaling: function() {
  3451. return this._isRetinaScaling() ? fabric.devicePixelRatio : 1;
  3452. },
  3453. /**
  3454. * @private
  3455. */
  3456. _initRetinaScaling: function() {
  3457. if (!this._isRetinaScaling()) {
  3458. return;
  3459. }
  3460. this.lowerCanvasEl.setAttribute('width', this.width * fabric.devicePixelRatio);
  3461. this.lowerCanvasEl.setAttribute('height', this.height * fabric.devicePixelRatio);
  3462. this.contextContainer.scale(fabric.devicePixelRatio, fabric.devicePixelRatio);
  3463. },
  3464. /**
  3465. * Calculates canvas element offset relative to the document
  3466. * This method is also attached as "resize" event handler of window
  3467. * @return {fabric.Canvas} instance
  3468. * @chainable
  3469. */
  3470. calcOffset: function () {
  3471. this._offset = getElementOffset(this.lowerCanvasEl);
  3472. return this;
  3473. },
  3474. /**
  3475. * Sets {@link fabric.StaticCanvas#overlayImage|overlay c_image} for this canvas
  3476. * @param {(fabric.Image|String)} image fabric.Image instance or URL of an c_image to set overlay to
  3477. * @param {Function} callback callback to invoke when c_image is loaded and set as an overlay
  3478. * @param {Object} [options] Optional options to set for the {@link fabric.Image|overlay c_image}.
  3479. * @return {fabric.Canvas} thisArg
  3480. * @chainable
  3481. * @see {@link http://jsfiddle.net/fabricjs/MnzHT/|jsFiddle demo}
  3482. * @example <caption>Normal overlayImage with left/top = 0</caption>
  3483. * canvas.setOverlayImage('http://fabricjs.com/assets/jail_cell_bars.png', canvas.renderAll.bind(canvas), {
  3484. * // Needed to position overlayImage at 0/0
  3485. * originX: 'left',
  3486. * originY: 'top'
  3487. * });
  3488. * @example <caption>overlayImage with different properties</caption>
  3489. * canvas.setOverlayImage('http://fabricjs.com/assets/jail_cell_bars.png', canvas.renderAll.bind(canvas), {
  3490. * opacity: 0.5,
  3491. * angle: 45,
  3492. * left: 400,
  3493. * top: 400,
  3494. * originX: 'left',
  3495. * originY: 'top'
  3496. * });
  3497. * @example <caption>Stretched overlayImage #1 - width/height correspond to canvas width/height</caption>
  3498. * fabric.Image.fromURL('http://fabricjs.com/assets/jail_cell_bars.png', function(img) {
  3499. * img.set({width: canvas.width, height: canvas.height, originX: 'left', originY: 'top'});
  3500. * canvas.setOverlayImage(img, canvas.renderAll.bind(canvas));
  3501. * });
  3502. * @example <caption>Stretched overlayImage #2 - width/height correspond to canvas width/height</caption>
  3503. * canvas.setOverlayImage('http://fabricjs.com/assets/jail_cell_bars.png', canvas.renderAll.bind(canvas), {
  3504. * width: canvas.width,
  3505. * height: canvas.height,
  3506. * // Needed to position overlayImage at 0/0
  3507. * originX: 'left',
  3508. * originY: 'top'
  3509. * });
  3510. * @example <caption>overlayImage loaded from cross-origin</caption>
  3511. * canvas.setOverlayImage('http://fabricjs.com/assets/jail_cell_bars.png', canvas.renderAll.bind(canvas), {
  3512. * opacity: 0.5,
  3513. * angle: 45,
  3514. * left: 400,
  3515. * top: 400,
  3516. * originX: 'left',
  3517. * originY: 'top',
  3518. * crossOrigin: 'anonymous'
  3519. * });
  3520. */
  3521. setOverlayImage: function (image, callback, options) {
  3522. return this.__setBgOverlayImage('overlayImage', image, callback, options);
  3523. },
  3524. /**
  3525. * Sets {@link fabric.StaticCanvas#backgroundImage|background c_image} for this canvas
  3526. * @param {(fabric.Image|String)} image fabric.Image instance or URL of an c_image to set background to
  3527. * @param {Function} callback Callback to invoke when c_image is loaded and set as background
  3528. * @param {Object} [options] Optional options to set for the {@link fabric.Image|background c_image}.
  3529. * @return {fabric.Canvas} thisArg
  3530. * @chainable
  3531. * @see {@link http://jsfiddle.net/djnr8o7a/28/|jsFiddle demo}
  3532. * @example <caption>Normal backgroundImage with left/top = 0</caption>
  3533. * canvas.setBackgroundImage('http://fabricjs.com/assets/honey_im_subtle.png', canvas.renderAll.bind(canvas), {
  3534. * // Needed to position backgroundImage at 0/0
  3535. * originX: 'left',
  3536. * originY: 'top'
  3537. * });
  3538. * @example <caption>backgroundImage with different properties</caption>
  3539. * canvas.setBackgroundImage('http://fabricjs.com/assets/honey_im_subtle.png', canvas.renderAll.bind(canvas), {
  3540. * opacity: 0.5,
  3541. * angle: 45,
  3542. * left: 400,
  3543. * top: 400,
  3544. * originX: 'left',
  3545. * originY: 'top'
  3546. * });
  3547. * @example <caption>Stretched backgroundImage #1 - width/height correspond to canvas width/height</caption>
  3548. * fabric.Image.fromURL('http://fabricjs.com/assets/honey_im_subtle.png', function(img) {
  3549. * img.set({width: canvas.width, height: canvas.height, originX: 'left', originY: 'top'});
  3550. * canvas.setBackgroundImage(img, canvas.renderAll.bind(canvas));
  3551. * });
  3552. * @example <caption>Stretched backgroundImage #2 - width/height correspond to canvas width/height</caption>
  3553. * canvas.setBackgroundImage('http://fabricjs.com/assets/honey_im_subtle.png', canvas.renderAll.bind(canvas), {
  3554. * width: canvas.width,
  3555. * height: canvas.height,
  3556. * // Needed to position backgroundImage at 0/0
  3557. * originX: 'left',
  3558. * originY: 'top'
  3559. * });
  3560. * @example <caption>backgroundImage loaded from cross-origin</caption>
  3561. * canvas.setBackgroundImage('http://fabricjs.com/assets/honey_im_subtle.png', canvas.renderAll.bind(canvas), {
  3562. * opacity: 0.5,
  3563. * angle: 45,
  3564. * left: 400,
  3565. * top: 400,
  3566. * originX: 'left',
  3567. * originY: 'top',
  3568. * crossOrigin: 'anonymous'
  3569. * });
  3570. */
  3571. setBackgroundImage: function (image, callback, options) {
  3572. return this.__setBgOverlayImage('backgroundImage', image, callback, options);
  3573. },
  3574. /**
  3575. * Sets {@link fabric.StaticCanvas#overlayColor|foreground color} for this canvas
  3576. * @param {(String|fabric.Pattern)} overlayColor Color or pattern to set foreground color to
  3577. * @param {Function} callback Callback to invoke when foreground color is set
  3578. * @return {fabric.Canvas} thisArg
  3579. * @chainable
  3580. * @see {@link http://jsfiddle.net/fabricjs/pB55h/|jsFiddle demo}
  3581. * @example <caption>Normal overlayColor - color value</caption>
  3582. * canvas.setOverlayColor('rgba(255, 73, 64, 0.6)', canvas.renderAll.bind(canvas));
  3583. * @example <caption>fabric.Pattern used as overlayColor</caption>
  3584. * canvas.setOverlayColor({
  3585. * source: 'http://fabricjs.com/assets/escheresque_ste.png'
  3586. * }, canvas.renderAll.bind(canvas));
  3587. * @example <caption>fabric.Pattern used as overlayColor with repeat and offset</caption>
  3588. * canvas.setOverlayColor({
  3589. * source: 'http://fabricjs.com/assets/escheresque_ste.png',
  3590. * repeat: 'repeat',
  3591. * offsetX: 200,
  3592. * offsetY: 100
  3593. * }, canvas.renderAll.bind(canvas));
  3594. */
  3595. setOverlayColor: function(overlayColor, callback) {
  3596. return this.__setBgOverlayColor('overlayColor', overlayColor, callback);
  3597. },
  3598. /**
  3599. * Sets {@link fabric.StaticCanvas#backgroundColor|background color} for this canvas
  3600. * @param {(String|fabric.Pattern)} backgroundColor Color or pattern to set background color to
  3601. * @param {Function} callback Callback to invoke when background color is set
  3602. * @return {fabric.Canvas} thisArg
  3603. * @chainable
  3604. * @see {@link http://jsfiddle.net/fabricjs/hXzvk/|jsFiddle demo}
  3605. * @example <caption>Normal backgroundColor - color value</caption>
  3606. * canvas.setBackgroundColor('rgba(255, 73, 64, 0.6)', canvas.renderAll.bind(canvas));
  3607. * @example <caption>fabric.Pattern used as backgroundColor</caption>
  3608. * canvas.setBackgroundColor({
  3609. * source: 'http://fabricjs.com/assets/escheresque_ste.png'
  3610. * }, canvas.renderAll.bind(canvas));
  3611. * @example <caption>fabric.Pattern used as backgroundColor with repeat and offset</caption>
  3612. * canvas.setBackgroundColor({
  3613. * source: 'http://fabricjs.com/assets/escheresque_ste.png',
  3614. * repeat: 'repeat',
  3615. * offsetX: 200,
  3616. * offsetY: 100
  3617. * }, canvas.renderAll.bind(canvas));
  3618. */
  3619. setBackgroundColor: function(backgroundColor, callback) {
  3620. return this.__setBgOverlayColor('backgroundColor', backgroundColor, callback);
  3621. },
  3622. /**
  3623. * @private
  3624. * @see {@link http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-imagesmoothingenabled|WhatWG Canvas Standard}
  3625. */
  3626. _setImageSmoothing: function() {
  3627. var ctx = this.getContext();
  3628. ctx.imageSmoothingEnabled = ctx.imageSmoothingEnabled || ctx.webkitImageSmoothingEnabled
  3629. || ctx.mozImageSmoothingEnabled || ctx.msImageSmoothingEnabled || ctx.oImageSmoothingEnabled;
  3630. ctx.imageSmoothingEnabled = this.imageSmoothingEnabled;
  3631. },
  3632. /**
  3633. * @private
  3634. * @param {String} property Property to set ({@link fabric.StaticCanvas#backgroundImage|backgroundImage}
  3635. * or {@link fabric.StaticCanvas#overlayImage|overlayImage})
  3636. * @param {(fabric.Image|String|null)} image fabric.Image instance, URL of an c_image or null to set background or overlay to
  3637. * @param {Function} callback Callback to invoke when c_image is loaded and set as background or overlay
  3638. * @param {Object} [options] Optional options to set for the {@link fabric.Image|image}.
  3639. */
  3640. __setBgOverlayImage: function(property, image, callback, options) {
  3641. if (typeof image === 'string') {
  3642. fabric.util.loadImage(image, function(img) {
  3643. img && (this[property] = new fabric.Image(img, options));
  3644. callback && callback(img);
  3645. }, this, options && options.crossOrigin);
  3646. }
  3647. else {
  3648. options && image.setOptions(options);
  3649. this[property] = image;
  3650. callback && callback(image);
  3651. }
  3652. return this;
  3653. },
  3654. /**
  3655. * @private
  3656. * @param {String} property Property to set ({@link fabric.StaticCanvas#backgroundColor|backgroundColor}
  3657. * or {@link fabric.StaticCanvas#overlayColor|overlayColor})
  3658. * @param {(Object|String|null)} color Object with pattern information, color value or null
  3659. * @param {Function} [callback] Callback is invoked when color is set
  3660. */
  3661. __setBgOverlayColor: function(property, color, callback) {
  3662. this[property] = color;
  3663. this._initGradient(color, property);
  3664. this._initPattern(color, property, callback);
  3665. return this;
  3666. },
  3667. /**
  3668. * @private
  3669. */
  3670. _createCanvasElement: function() {
  3671. var element = fabric.util.createCanvasElement();
  3672. if (!element) {
  3673. throw CANVAS_INIT_ERROR;
  3674. }
  3675. if (!element.style) {
  3676. element.style = { };
  3677. }
  3678. if (typeof element.getContext === 'undefined') {
  3679. throw CANVAS_INIT_ERROR;
  3680. }
  3681. return element;
  3682. },
  3683. /**
  3684. * @private
  3685. * @param {Object} [options] Options object
  3686. */
  3687. _initOptions: function (options) {
  3688. this._setOptions(options);
  3689. this.width = this.width || parseInt(this.lowerCanvasEl.width, 10) || 0;
  3690. this.height = this.height || parseInt(this.lowerCanvasEl.height, 10) || 0;
  3691. if (!this.lowerCanvasEl.style) {
  3692. return;
  3693. }
  3694. this.lowerCanvasEl.width = this.width;
  3695. this.lowerCanvasEl.height = this.height;
  3696. this.lowerCanvasEl.style.width = this.width + 'px';
  3697. this.lowerCanvasEl.style.height = this.height + 'px';
  3698. this.viewportTransform = this.viewportTransform.slice();
  3699. },
  3700. /**
  3701. * Creates a bottom canvas
  3702. * @private
  3703. * @param {HTMLElement} [canvasEl]
  3704. */
  3705. _createLowerCanvas: function (canvasEl) {
  3706. // canvasEl === 'HTMLCanvasElement' does not work on jsdom/node
  3707. if (canvasEl && canvasEl.getContext) {
  3708. this.lowerCanvasEl = canvasEl;
  3709. }
  3710. else {
  3711. this.lowerCanvasEl = fabric.util.getById(canvasEl) || this._createCanvasElement();
  3712. }
  3713. fabric.util.addClass(this.lowerCanvasEl, 'lower-canvas');
  3714. if (this.interactive) {
  3715. this._applyCanvasStyle(this.lowerCanvasEl);
  3716. }
  3717. this.contextContainer = this.lowerCanvasEl.getContext('2d');
  3718. },
  3719. /**
  3720. * Returns canvas width (in px)
  3721. * @return {Number}
  3722. */
  3723. getWidth: function () {
  3724. return this.width;
  3725. },
  3726. /**
  3727. * Returns canvas height (in px)
  3728. * @return {Number}
  3729. */
  3730. getHeight: function () {
  3731. return this.height;
  3732. },
  3733. /**
  3734. * Sets width of this canvas instance
  3735. * @param {Number|String} value Value to set width to
  3736. * @param {Object} [options] Options object
  3737. * @param {Boolean} [options.backstoreOnly=false] Set the given dimensions only as canvas backstore dimensions
  3738. * @param {Boolean} [options.cssOnly=false] Set the given dimensions only as c_style dimensions
  3739. * @return {fabric.Canvas} instance
  3740. * @chainable true
  3741. */
  3742. setWidth: function (value, options) {
  3743. return this.setDimensions({ width: value }, options);
  3744. },
  3745. /**
  3746. * Sets height of this canvas instance
  3747. * @param {Number|String} value Value to set height to
  3748. * @param {Object} [options] Options object
  3749. * @param {Boolean} [options.backstoreOnly=false] Set the given dimensions only as canvas backstore dimensions
  3750. * @param {Boolean} [options.cssOnly=false] Set the given dimensions only as c_style dimensions
  3751. * @return {fabric.Canvas} instance
  3752. * @chainable true
  3753. */
  3754. setHeight: function (value, options) {
  3755. return this.setDimensions({ height: value }, options);
  3756. },
  3757. /**
  3758. * Sets dimensions (width, height) of this canvas instance. when options.cssOnly flag active you should also supply the unit of measure (px/%/em)
  3759. * @param {Object} dimensions Object with width/height properties
  3760. * @param {Number|String} [dimensions.width] Width of canvas element
  3761. * @param {Number|String} [dimensions.height] Height of canvas element
  3762. * @param {Object} [options] Options object
  3763. * @param {Boolean} [options.backstoreOnly=false] Set the given dimensions only as canvas backstore dimensions
  3764. * @param {Boolean} [options.cssOnly=false] Set the given dimensions only as c_style dimensions
  3765. * @return {fabric.Canvas} thisArg
  3766. * @chainable
  3767. */
  3768. setDimensions: function (dimensions, options) {
  3769. var cssValue;
  3770. options = options || {};
  3771. for (var prop in dimensions) {
  3772. cssValue = dimensions[prop];
  3773. if (!options.cssOnly) {
  3774. this._setBackstoreDimension(prop, dimensions[prop]);
  3775. cssValue += 'px';
  3776. this.hasLostContext = true;
  3777. }
  3778. if (!options.backstoreOnly) {
  3779. this._setCssDimension(prop, cssValue);
  3780. }
  3781. }
  3782. if (this._isCurrentlyDrawing) {
  3783. this.freeDrawingBrush && this.freeDrawingBrush._setBrushStyles();
  3784. }
  3785. this._initRetinaScaling();
  3786. this._setImageSmoothing();
  3787. this.calcOffset();
  3788. if (!options.cssOnly) {
  3789. this.requestRenderAll();
  3790. }
  3791. return this;
  3792. },
  3793. /**
  3794. * Helper for setting width/height
  3795. * @private
  3796. * @param {String} prop property (width|height)
  3797. * @param {Number} value value to set property to
  3798. * @return {fabric.Canvas} instance
  3799. * @chainable true
  3800. */
  3801. _setBackstoreDimension: function (prop, value) {
  3802. this.lowerCanvasEl[prop] = value;
  3803. if (this.upperCanvasEl) {
  3804. this.upperCanvasEl[prop] = value;
  3805. }
  3806. if (this.cacheCanvasEl) {
  3807. this.cacheCanvasEl[prop] = value;
  3808. }
  3809. this[prop] = value;
  3810. return this;
  3811. },
  3812. /**
  3813. * Helper for setting c_style width/height
  3814. * @private
  3815. * @param {String} prop property (width|height)
  3816. * @param {String} value value to set property to
  3817. * @return {fabric.Canvas} instance
  3818. * @chainable true
  3819. */
  3820. _setCssDimension: function (prop, value) {
  3821. this.lowerCanvasEl.style[prop] = value;
  3822. if (this.upperCanvasEl) {
  3823. this.upperCanvasEl.style[prop] = value;
  3824. }
  3825. if (this.wrapperEl) {
  3826. this.wrapperEl.style[prop] = value;
  3827. }
  3828. return this;
  3829. },
  3830. /**
  3831. * Returns canvas zoom level
  3832. * @return {Number}
  3833. */
  3834. getZoom: function () {
  3835. return this.viewportTransform[0];
  3836. },
  3837. /**
  3838. * Sets viewport transform of this canvas instance
  3839. * @param {Array} vpt the transform in the form of context.transform
  3840. * @return {fabric.Canvas} instance
  3841. * @chainable true
  3842. */
  3843. setViewportTransform: function (vpt) {
  3844. var activeObject = this._activeObject, object, ignoreVpt = false, skipAbsolute = true, i, len;
  3845. this.viewportTransform = vpt;
  3846. for (i = 0, len = this._objects.length; i < len; i++) {
  3847. object = this._objects[i];
  3848. object.group || object.setCoords(ignoreVpt, skipAbsolute);
  3849. }
  3850. if (activeObject && activeObject.type === 'activeSelection') {
  3851. activeObject.setCoords(ignoreVpt, skipAbsolute);
  3852. }
  3853. this.calcViewportBoundaries();
  3854. this.renderOnAddRemove && this.requestRenderAll();
  3855. return this;
  3856. },
  3857. /**
  3858. * Sets zoom level of this canvas instance, zoom centered around point
  3859. * @param {fabric.Point} point to zoom with respect to
  3860. * @param {Number} value to set zoom to, less than 1 zooms out
  3861. * @return {fabric.Canvas} instance
  3862. * @chainable true
  3863. */
  3864. zoomToPoint: function (point, value) {
  3865. // TODO: just change the scale, preserve other transformations
  3866. var before = point, vpt = this.viewportTransform.slice(0);
  3867. point = transformPoint(point, invertTransform(this.viewportTransform));
  3868. vpt[0] = value;
  3869. vpt[3] = value;
  3870. var after = transformPoint(point, vpt);
  3871. vpt[4] += before.x - after.x;
  3872. vpt[5] += before.y - after.y;
  3873. return this.setViewportTransform(vpt);
  3874. },
  3875. /**
  3876. * Sets zoom level of this canvas instance
  3877. * @param {Number} value to set zoom to, less than 1 zooms out
  3878. * @return {fabric.Canvas} instance
  3879. * @chainable true
  3880. */
  3881. setZoom: function (value) {
  3882. this.zoomToPoint(new fabric.Point(0, 0), value);
  3883. return this;
  3884. },
  3885. /**
  3886. * Pan viewport so as to place point at top left corner of canvas
  3887. * @param {fabric.Point} point to move to
  3888. * @return {fabric.Canvas} instance
  3889. * @chainable true
  3890. */
  3891. absolutePan: function (point) {
  3892. var vpt = this.viewportTransform.slice(0);
  3893. vpt[4] = -point.x;
  3894. vpt[5] = -point.y;
  3895. return this.setViewportTransform(vpt);
  3896. },
  3897. /**
  3898. * Pans viewpoint relatively
  3899. * @param {fabric.Point} point (position vector) to move by
  3900. * @return {fabric.Canvas} instance
  3901. * @chainable true
  3902. */
  3903. relativePan: function (point) {
  3904. return this.absolutePan(new fabric.Point(
  3905. -point.x - this.viewportTransform[4],
  3906. -point.y - this.viewportTransform[5]
  3907. ));
  3908. },
  3909. /**
  3910. * Returns &lt;canvas> element corresponding to this instance
  3911. * @return {HTMLCanvasElement}
  3912. */
  3913. getElement: function () {
  3914. return this.lowerCanvasEl;
  3915. },
  3916. /**
  3917. * @private
  3918. * @param {fabric.Object} obj Object that was added
  3919. */
  3920. _onObjectAdded: function(obj) {
  3921. this.stateful && obj.setupState();
  3922. obj._set('canvas', this);
  3923. obj.setCoords();
  3924. this.fire('object:added', { target: obj });
  3925. obj.fire('added');
  3926. },
  3927. /**
  3928. * @private
  3929. * @param {fabric.Object} obj Object that was removed
  3930. */
  3931. _onObjectRemoved: function(obj) {
  3932. this.fire('object:removed', { target: obj });
  3933. obj.fire('removed');
  3934. delete obj.canvas;
  3935. },
  3936. /**
  3937. * Clears specified context of canvas element
  3938. * @param {CanvasRenderingContext2D} ctx Context to clear
  3939. * @return {fabric.Canvas} thisArg
  3940. * @chainable
  3941. */
  3942. clearContext: function(ctx) {
  3943. ctx.clearRect(0, 0, this.width, this.height);
  3944. return this;
  3945. },
  3946. /**
  3947. * Returns context of canvas where objects are drawn
  3948. * @return {CanvasRenderingContext2D}
  3949. */
  3950. getContext: function () {
  3951. return this.contextContainer;
  3952. },
  3953. /**
  3954. * Clears all contexts (background, main, top) of an instance
  3955. * @return {fabric.Canvas} thisArg
  3956. * @chainable
  3957. */
  3958. clear: function () {
  3959. this._objects.length = 0;
  3960. this.backgroundImage = null;
  3961. this.overlayImage = null;
  3962. this.backgroundColor = '';
  3963. this.overlayColor = '';
  3964. if (this._hasITextHandlers) {
  3965. this.off('mouse:up', this._mouseUpITextHandler);
  3966. this._iTextInstances = null;
  3967. this._hasITextHandlers = false;
  3968. }
  3969. this.clearContext(this.contextContainer);
  3970. this.fire('canvas:cleared');
  3971. this.renderOnAddRemove && this.requestRenderAll();
  3972. return this;
  3973. },
  3974. /**
  3975. * Renders the canvas
  3976. * @return {fabric.Canvas} instance
  3977. * @chainable
  3978. */
  3979. renderAll: function () {
  3980. var canvasToDrawOn = this.contextContainer;
  3981. this.renderCanvas(canvasToDrawOn, this._objects);
  3982. return this;
  3983. },
  3984. /**
  3985. * Function created to be instance bound at initialization
  3986. * used in requestAnimationFrame rendering
  3987. * @return {fabric.Canvas} instance
  3988. * @chainable
  3989. */
  3990. renderAndReset: function() {
  3991. this.isRendering = 0;
  3992. this.renderAll();
  3993. },
  3994. /**
  3995. * Append a renderAll request to next animation frame.
  3996. * a boolean flag will avoid appending more.
  3997. * @return {fabric.Canvas} instance
  3998. * @chainable
  3999. */
  4000. requestRenderAll: function () {
  4001. if (!this.isRendering) {
  4002. this.isRendering = fabric.util.requestAnimFrame(this.renderAndResetBound);
  4003. }
  4004. return this;
  4005. },
  4006. /**
  4007. * Calculate the position of the 4 corner of canvas with current viewportTransform.
  4008. * helps to determinate when an object is in the current rendering viewport using
  4009. * object absolute coordinates ( aCoords )
  4010. * @return {Object} points.tl
  4011. * @chainable
  4012. */
  4013. calcViewportBoundaries: function() {
  4014. var points = { }, width = this.width, height = this.height,
  4015. iVpt = invertTransform(this.viewportTransform);
  4016. points.tl = transformPoint({ x: 0, y: 0 }, iVpt);
  4017. points.br = transformPoint({ x: width, y: height }, iVpt);
  4018. points.tr = new fabric.Point(points.br.x, points.tl.y);
  4019. points.bl = new fabric.Point(points.tl.x, points.br.y);
  4020. this.vptCoords = points;
  4021. return points;
  4022. },
  4023. /**
  4024. * Renders background, objects, overlay and controls.
  4025. * @param {CanvasRenderingContext2D} ctx
  4026. * @param {Array} objects to render
  4027. * @return {fabric.Canvas} instance
  4028. * @chainable
  4029. */
  4030. renderCanvas: function(ctx, objects) {
  4031. var v = this.viewportTransform;
  4032. if (this.isRendering) {
  4033. fabric.util.cancelAnimFrame(this.isRendering);
  4034. this.isRendering = 0;
  4035. }
  4036. this.calcViewportBoundaries();
  4037. this.clearContext(ctx);
  4038. this.fire('before:render');
  4039. if (this.clipTo) {
  4040. fabric.util.clipContext(this, ctx);
  4041. }
  4042. this._renderBackground(ctx);
  4043. ctx.save();
  4044. //apply viewport transform once for all rendering process
  4045. ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
  4046. this._renderObjects(ctx, objects);
  4047. ctx.restore();
  4048. if (!this.controlsAboveOverlay && this.interactive) {
  4049. this.drawControls(ctx);
  4050. }
  4051. if (this.clipTo) {
  4052. ctx.restore();
  4053. }
  4054. this._renderOverlay(ctx);
  4055. if (this.controlsAboveOverlay && this.interactive) {
  4056. this.drawControls(ctx);
  4057. }
  4058. this.fire('after:render');
  4059. },
  4060. /**
  4061. * @private
  4062. * @param {CanvasRenderingContext2D} ctx Context to render on
  4063. * @param {Array} objects to render
  4064. */
  4065. _renderObjects: function(ctx, objects) {
  4066. var i, len;
  4067. for (i = 0, len = objects.length; i < len; ++i) {
  4068. objects[i] && objects[i].render(ctx);
  4069. }
  4070. },
  4071. /**
  4072. * @private
  4073. * @param {CanvasRenderingContext2D} ctx Context to render on
  4074. * @param {string} property 'background' or 'overlay'
  4075. */
  4076. _renderBackgroundOrOverlay: function(ctx, property) {
  4077. var object = this[property + 'Color'], v;
  4078. if (object) {
  4079. ctx.fillStyle = object.toLive
  4080. ? object.toLive(ctx, this)
  4081. : object;
  4082. ctx.fillRect(
  4083. object.offsetX || 0,
  4084. object.offsetY || 0,
  4085. this.width,
  4086. this.height);
  4087. }
  4088. object = this[property + 'Image'];
  4089. if (object) {
  4090. if (this[property + 'Vpt']) {
  4091. v = this.viewportTransform;
  4092. ctx.save();
  4093. ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
  4094. }
  4095. object.render(ctx);
  4096. this[property + 'Vpt'] && ctx.restore();
  4097. }
  4098. },
  4099. /**
  4100. * @private
  4101. * @param {CanvasRenderingContext2D} ctx Context to render on
  4102. */
  4103. _renderBackground: function(ctx) {
  4104. this._renderBackgroundOrOverlay(ctx, 'background');
  4105. },
  4106. /**
  4107. * @private
  4108. * @param {CanvasRenderingContext2D} ctx Context to render on
  4109. */
  4110. _renderOverlay: function(ctx) {
  4111. this._renderBackgroundOrOverlay(ctx, 'overlay');
  4112. },
  4113. /**
  4114. * Returns coordinates of a center of canvas.
  4115. * Returned value is an object with top and left properties
  4116. * @return {Object} object with "top" and "left" number values
  4117. */
  4118. getCenter: function () {
  4119. return {
  4120. top: this.height / 2,
  4121. left: this.width / 2
  4122. };
  4123. },
  4124. /**
  4125. * Centers object horizontally in the canvas
  4126. * @param {fabric.Object} object Object to center horizontally
  4127. * @return {fabric.Canvas} thisArg
  4128. */
  4129. centerObjectH: function (object) {
  4130. return this._centerObject(object, new fabric.Point(this.getCenter().left, object.getCenterPoint().y));
  4131. },
  4132. /**
  4133. * Centers object vertically in the canvas
  4134. * @param {fabric.Object} object Object to center vertically
  4135. * @return {fabric.Canvas} thisArg
  4136. * @chainable
  4137. */
  4138. centerObjectV: function (object) {
  4139. return this._centerObject(object, new fabric.Point(object.getCenterPoint().x, this.getCenter().top));
  4140. },
  4141. /**
  4142. * Centers object vertically and horizontally in the canvas
  4143. * @param {fabric.Object} object Object to center vertically and horizontally
  4144. * @return {fabric.Canvas} thisArg
  4145. * @chainable
  4146. */
  4147. centerObject: function(object) {
  4148. var center = this.getCenter();
  4149. return this._centerObject(object, new fabric.Point(center.left, center.top));
  4150. },
  4151. /**
  4152. * Centers object vertically and horizontally in the viewport
  4153. * @param {fabric.Object} object Object to center vertically and horizontally
  4154. * @return {fabric.Canvas} thisArg
  4155. * @chainable
  4156. */
  4157. viewportCenterObject: function(object) {
  4158. var vpCenter = this.getVpCenter();
  4159. return this._centerObject(object, vpCenter);
  4160. },
  4161. /**
  4162. * Centers object horizontally in the viewport, object.top is unchanged
  4163. * @param {fabric.Object} object Object to center vertically and horizontally
  4164. * @return {fabric.Canvas} thisArg
  4165. * @chainable
  4166. */
  4167. viewportCenterObjectH: function(object) {
  4168. var vpCenter = this.getVpCenter();
  4169. this._centerObject(object, new fabric.Point(vpCenter.x, object.getCenterPoint().y));
  4170. return this;
  4171. },
  4172. /**
  4173. * Centers object Vertically in the viewport, object.top is unchanged
  4174. * @param {fabric.Object} object Object to center vertically and horizontally
  4175. * @return {fabric.Canvas} thisArg
  4176. * @chainable
  4177. */
  4178. viewportCenterObjectV: function(object) {
  4179. var vpCenter = this.getVpCenter();
  4180. return this._centerObject(object, new fabric.Point(object.getCenterPoint().x, vpCenter.y));
  4181. },
  4182. /**
  4183. * Calculate the point in canvas that correspond to the center of actual viewport.
  4184. * @return {fabric.Point} vpCenter, viewport center
  4185. * @chainable
  4186. */
  4187. getVpCenter: function() {
  4188. var center = this.getCenter(),
  4189. iVpt = invertTransform(this.viewportTransform);
  4190. return transformPoint({ x: center.left, y: center.top }, iVpt);
  4191. },
  4192. /**
  4193. * @private
  4194. * @param {fabric.Object} object Object to center
  4195. * @param {fabric.Point} center Center point
  4196. * @return {fabric.Canvas} thisArg
  4197. * @chainable
  4198. */
  4199. _centerObject: function(object, center) {
  4200. object.setPositionByOrigin(center, 'center', 'center');
  4201. object.setCoords();
  4202. this.renderOnAddRemove && this.requestRenderAll();
  4203. return this;
  4204. },
  4205. /**
  4206. * Returs dataless JSON representation of canvas
  4207. * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
  4208. * @return {String} json string
  4209. */
  4210. toDatalessJSON: function (propertiesToInclude) {
  4211. return this.toDatalessObject(propertiesToInclude);
  4212. },
  4213. /**
  4214. * Returns object representation of canvas
  4215. * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
  4216. * @return {Object} object representation of an instance
  4217. */
  4218. toObject: function (propertiesToInclude) {
  4219. return this._toObjectMethod('toObject', propertiesToInclude);
  4220. },
  4221. /**
  4222. * Returns dataless object representation of canvas
  4223. * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
  4224. * @return {Object} object representation of an instance
  4225. */
  4226. toDatalessObject: function (propertiesToInclude) {
  4227. return this._toObjectMethod('toDatalessObject', propertiesToInclude);
  4228. },
  4229. /**
  4230. * @private
  4231. */
  4232. _toObjectMethod: function (methodName, propertiesToInclude) {
  4233. var data = {
  4234. version: fabric.version,
  4235. objects: this._toObjects(methodName, propertiesToInclude)
  4236. };
  4237. extend(data, this.__serializeBgOverlay(methodName, propertiesToInclude));
  4238. fabric.util.populateWithProperties(this, data, propertiesToInclude);
  4239. return data;
  4240. },
  4241. /**
  4242. * @private
  4243. */
  4244. _toObjects: function(methodName, propertiesToInclude) {
  4245. return this.getObjects().filter(function(object) {
  4246. return !object.excludeFromExport;
  4247. }).map(function(instance) {
  4248. return this._toObject(instance, methodName, propertiesToInclude);
  4249. }, this);
  4250. },
  4251. /**
  4252. * @private
  4253. */
  4254. _toObject: function(instance, methodName, propertiesToInclude) {
  4255. var originalValue;
  4256. if (!this.includeDefaultValues) {
  4257. originalValue = instance.includeDefaultValues;
  4258. instance.includeDefaultValues = false;
  4259. }
  4260. var object = instance[methodName](propertiesToInclude);
  4261. if (!this.includeDefaultValues) {
  4262. instance.includeDefaultValues = originalValue;
  4263. }
  4264. return object;
  4265. },
  4266. /**
  4267. * @private
  4268. */
  4269. __serializeBgOverlay: function(methodName, propertiesToInclude) {
  4270. var data = { }, bgImage = this.backgroundImage, overlay = this.overlayImage;
  4271. if (this.backgroundColor) {
  4272. data.background = this.backgroundColor.toObject
  4273. ? this.backgroundColor.toObject(propertiesToInclude)
  4274. : this.backgroundColor;
  4275. }
  4276. if (this.overlayColor) {
  4277. data.overlay = this.overlayColor.toObject
  4278. ? this.overlayColor.toObject(propertiesToInclude)
  4279. : this.overlayColor;
  4280. }
  4281. if (bgImage && !bgImage.excludeFromExport) {
  4282. data.backgroundImage = this._toObject(bgImage, methodName, propertiesToInclude);
  4283. }
  4284. if (overlay && !overlay.excludeFromExport) {
  4285. data.overlayImage = this._toObject(overlay, methodName, propertiesToInclude);
  4286. }
  4287. return data;
  4288. },
  4289. /* _TO_SVG_START_ */
  4290. /**
  4291. * When true, getSvgTransform() will apply the StaticCanvas.viewportTransform to the SVG transformation. When true,
  4292. * a zoomed canvas will then produce zoomed SVG output.
  4293. * @type Boolean
  4294. * @default
  4295. */
  4296. svgViewportTransformation: true,
  4297. /**
  4298. * Returns SVG representation of canvas
  4299. * @function
  4300. * @param {Object} [options] Options object for SVG output
  4301. * @param {Boolean} [options.suppressPreamble=false] If true xml tag is not included
  4302. * @param {Object} [options.viewBox] SVG viewbox object
  4303. * @param {Number} [options.viewBox.x] x-cooridnate of viewbox
  4304. * @param {Number} [options.viewBox.y] y-coordinate of viewbox
  4305. * @param {Number} [options.viewBox.width] Width of viewbox
  4306. * @param {Number} [options.viewBox.height] Height of viewbox
  4307. * @param {String} [options.encoding=UTF-8] Encoding of SVG output
  4308. * @param {String} [options.width] desired width of svg with or without units
  4309. * @param {String} [options.height] desired height of svg with or without units
  4310. * @param {Function} [reviver] Method for further parsing of svg elements, called after each fabric object converted into svg representation.
  4311. * @return {String} SVG string
  4312. * @tutorial {@link http://fabricjs.com/fabric-intro-part-3#serialization}
  4313. * @see {@link http://jsfiddle.net/fabricjs/jQ3ZZ/|jsFiddle demo}
  4314. * @example <caption>Normal SVG output</caption>
  4315. * var svg = canvas.toSVG();
  4316. * @example <caption>SVG output without preamble (without &lt;?xml ../>)</caption>
  4317. * var svg = canvas.toSVG({suppressPreamble: true});
  4318. * @example <caption>SVG output with viewBox attribute</caption>
  4319. * var svg = canvas.toSVG({
  4320. * viewBox: {
  4321. * x: 100,
  4322. * y: 100,
  4323. * width: 200,
  4324. * height: 300
  4325. * }
  4326. * });
  4327. * @example <caption>SVG output with different encoding (default: UTF-8)</caption>
  4328. * var svg = canvas.toSVG({encoding: 'ISO-8859-1'});
  4329. * @example <caption>Modify SVG output with reviver function</caption>
  4330. * var svg = canvas.toSVG(null, function(svg) {
  4331. * return svg.replace('stroke-dasharray: ; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; ', '');
  4332. * });
  4333. */
  4334. toSVG: function(options, reviver) {
  4335. options || (options = { });
  4336. var markup = [];
  4337. this._setSVGPreamble(markup, options);
  4338. this._setSVGHeader(markup, options);
  4339. this._setSVGBgOverlayColor(markup, 'backgroundColor');
  4340. this._setSVGBgOverlayImage(markup, 'backgroundImage', reviver);
  4341. this._setSVGObjects(markup, reviver);
  4342. this._setSVGBgOverlayColor(markup, 'overlayColor');
  4343. this._setSVGBgOverlayImage(markup, 'overlayImage', reviver);
  4344. markup.push('</svg>');
  4345. return markup.join('');
  4346. },
  4347. /**
  4348. * @private
  4349. */
  4350. _setSVGPreamble: function(markup, options) {
  4351. if (options.suppressPreamble) {
  4352. return;
  4353. }
  4354. markup.push(
  4355. '<?xml version="1.0" encoding="', (options.encoding || 'UTF-8'), '" standalone="no" ?>\n',
  4356. '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" ',
  4357. '"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n'
  4358. );
  4359. },
  4360. /**
  4361. * @private
  4362. */
  4363. _setSVGHeader: function(markup, options) {
  4364. var width = options.width || this.width,
  4365. height = options.height || this.height,
  4366. vpt, viewBox = 'viewBox="0 0 ' + this.width + ' ' + this.height + '" ',
  4367. NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS;
  4368. if (options.viewBox) {
  4369. viewBox = 'viewBox="' +
  4370. options.viewBox.x + ' ' +
  4371. options.viewBox.y + ' ' +
  4372. options.viewBox.width + ' ' +
  4373. options.viewBox.height + '" ';
  4374. }
  4375. else {
  4376. if (this.svgViewportTransformation) {
  4377. vpt = this.viewportTransform;
  4378. viewBox = 'viewBox="' +
  4379. toFixed(-vpt[4] / vpt[0], NUM_FRACTION_DIGITS) + ' ' +
  4380. toFixed(-vpt[5] / vpt[3], NUM_FRACTION_DIGITS) + ' ' +
  4381. toFixed(this.width / vpt[0], NUM_FRACTION_DIGITS) + ' ' +
  4382. toFixed(this.height / vpt[3], NUM_FRACTION_DIGITS) + '" ';
  4383. }
  4384. }
  4385. markup.push(
  4386. '<svg ',
  4387. 'xmlns="http://www.w3.org/2000/svg" ',
  4388. 'xmlns:xlink="http://www.w3.org/1999/xlink" ',
  4389. 'version="1.1" ',
  4390. 'width="', width, '" ',
  4391. 'height="', height, '" ',
  4392. viewBox,
  4393. 'xml:space="preserve">\n',
  4394. '<desc>Created with Fabric.js ', fabric.version, '</desc>\n',
  4395. '<defs>\n',
  4396. this.createSVGFontFacesMarkup(),
  4397. this.createSVGRefElementsMarkup(),
  4398. '</defs>\n'
  4399. );
  4400. },
  4401. /**
  4402. * Creates markup containing SVG referenced elements like patterns, gradients etc.
  4403. * @return {String}
  4404. */
  4405. createSVGRefElementsMarkup: function() {
  4406. var _this = this,
  4407. markup = ['backgroundColor', 'overlayColor'].map(function(prop) {
  4408. var fill = _this[prop];
  4409. if (fill && fill.toLive) {
  4410. return fill.toSVG(_this, false);
  4411. }
  4412. });
  4413. return markup.join('');
  4414. },
  4415. /**
  4416. * Creates markup containing SVG font faces,
  4417. * font URLs for font faces must be collected by developers
  4418. * and are not extracted from the DOM by fabricjs
  4419. * @param {Array} objects Array of fabric objects
  4420. * @return {String}
  4421. */
  4422. createSVGFontFacesMarkup: function() {
  4423. var markup = '', fontList = { }, obj, fontFamily,
  4424. style, row, rowIndex, _char, charIndex, i, len,
  4425. fontPaths = fabric.fontPaths, objects = this.getObjects();
  4426. for (i = 0, len = objects.length; i < len; i++) {
  4427. obj = objects[i];
  4428. fontFamily = obj.fontFamily;
  4429. if (obj.type.indexOf('text') === -1 || fontList[fontFamily] || !fontPaths[fontFamily]) {
  4430. continue;
  4431. }
  4432. fontList[fontFamily] = true;
  4433. if (!obj.styles) {
  4434. continue;
  4435. }
  4436. style = obj.styles;
  4437. for (rowIndex in style) {
  4438. row = style[rowIndex];
  4439. for (charIndex in row) {
  4440. _char = row[charIndex];
  4441. fontFamily = _char.fontFamily;
  4442. if (!fontList[fontFamily] && fontPaths[fontFamily]) {
  4443. fontList[fontFamily] = true;
  4444. }
  4445. }
  4446. }
  4447. }
  4448. for (var j in fontList) {
  4449. markup += [
  4450. '\t\t@font-face {\n',
  4451. '\t\t\tfont-family: \'', j, '\';\n',
  4452. '\t\t\tsrc: url(\'', fontPaths[j], '\');\n',
  4453. '\t\t}\n'
  4454. ].join('');
  4455. }
  4456. if (markup) {
  4457. markup = [
  4458. '\t<c_style type="text/c_style">',
  4459. '<![CDATA[\n',
  4460. markup,
  4461. ']]>',
  4462. '</c_style>\n'
  4463. ].join('');
  4464. }
  4465. return markup;
  4466. },
  4467. /**
  4468. * @private
  4469. */
  4470. _setSVGObjects: function(markup, reviver) {
  4471. var instance, i, len, objects = this.getObjects();
  4472. for (i = 0, len = objects.length; i < len; i++) {
  4473. instance = objects[i];
  4474. if (instance.excludeFromExport) {
  4475. continue;
  4476. }
  4477. this._setSVGObject(markup, instance, reviver);
  4478. }
  4479. },
  4480. /**
  4481. * @private
  4482. */
  4483. _setSVGObject: function(markup, instance, reviver) {
  4484. markup.push(instance.toSVG(reviver));
  4485. },
  4486. /**
  4487. * @private
  4488. */
  4489. _setSVGBgOverlayImage: function(markup, property, reviver) {
  4490. if (this[property] && this[property].toSVG) {
  4491. markup.push(this[property].toSVG(reviver));
  4492. }
  4493. },
  4494. /**
  4495. * @private
  4496. */
  4497. _setSVGBgOverlayColor: function(markup, property) {
  4498. var filler = this[property], vpt = this.viewportTransform, finalWidth = this.width / vpt[0],
  4499. finalHeight = this.height / vpt[3];
  4500. if (!filler) {
  4501. return;
  4502. }
  4503. if (filler.toLive) {
  4504. var repeat = filler.repeat;
  4505. markup.push(
  4506. '<rect transform="translate(', finalWidth / 2, ',', finalHeight / 2, ')"',
  4507. ' x="', filler.offsetX - finalWidth / 2, '" y="', filler.offsetY - finalHeight / 2, '" ',
  4508. 'width="',
  4509. (repeat === 'repeat-y' || repeat === 'no-repeat'
  4510. ? filler.source.width
  4511. : finalWidth ),
  4512. '" height="',
  4513. (repeat === 'repeat-x' || repeat === 'no-repeat'
  4514. ? filler.source.height
  4515. : finalHeight),
  4516. '" fill="url(#SVGID_' + filler.id + ')"',
  4517. '></rect>\n'
  4518. );
  4519. }
  4520. else {
  4521. markup.push(
  4522. '<rect x="0" y="0" width="100%" height="100%" ',
  4523. 'fill="', this[property], '"',
  4524. '></rect>\n'
  4525. );
  4526. }
  4527. },
  4528. /* _TO_SVG_END_ */
  4529. /**
  4530. * Moves an object or the objects of a multiple selection
  4531. * to the bottom of the stack of drawn objects
  4532. * @param {fabric.Object} object Object to send to back
  4533. * @return {fabric.Canvas} thisArg
  4534. * @chainable
  4535. */
  4536. sendToBack: function (object) {
  4537. if (!object) {
  4538. return this;
  4539. }
  4540. var activeSelection = this._activeObject,
  4541. i, obj, objs;
  4542. if (object === activeSelection && object.type === 'activeSelection') {
  4543. objs = activeSelection._objects;
  4544. for (i = objs.length; i--;) {
  4545. obj = objs[i];
  4546. removeFromArray(this._objects, obj);
  4547. this._objects.unshift(obj);
  4548. }
  4549. }
  4550. else {
  4551. removeFromArray(this._objects, object);
  4552. this._objects.unshift(object);
  4553. }
  4554. this.renderOnAddRemove && this.requestRenderAll();
  4555. return this;
  4556. },
  4557. /**
  4558. * Moves an object or the objects of a multiple selection
  4559. * to the top of the stack of drawn objects
  4560. * @param {fabric.Object} object Object to send
  4561. * @return {fabric.Canvas} thisArg
  4562. * @chainable
  4563. */
  4564. bringToFront: function (object) {
  4565. if (!object) {
  4566. return this;
  4567. }
  4568. var activeSelection = this._activeObject,
  4569. i, obj, objs;
  4570. if (object === activeSelection && object.type === 'activeSelection') {
  4571. objs = activeSelection._objects;
  4572. for (i = 0; i < objs.length; i++) {
  4573. obj = objs[i];
  4574. removeFromArray(this._objects, obj);
  4575. this._objects.push(obj);
  4576. }
  4577. }
  4578. else {
  4579. removeFromArray(this._objects, object);
  4580. this._objects.push(object);
  4581. }
  4582. this.renderOnAddRemove && this.requestRenderAll();
  4583. return this;
  4584. },
  4585. /**
  4586. * Moves an object or a selection down in stack of drawn objects
  4587. * An optional paramter, intersecting allowes to move the object in behind
  4588. * the first intersecting object. Where intersection is calculated with
  4589. * bounding box. If no intersection is found, there will not be change in the
  4590. * stack.
  4591. * @param {fabric.Object} object Object to send
  4592. * @param {Boolean} [intersecting] If `true`, send object behind next lower intersecting object
  4593. * @return {fabric.Canvas} thisArg
  4594. * @chainable
  4595. */
  4596. sendBackwards: function (object, intersecting) {
  4597. if (!object) {
  4598. return this;
  4599. }
  4600. var activeSelection = this._activeObject,
  4601. i, obj, idx, newIdx, objs, objsMoved = 0;
  4602. if (object === activeSelection && object.type === 'activeSelection') {
  4603. objs = activeSelection._objects;
  4604. for (i = 0; i < objs.length; i++) {
  4605. obj = objs[i];
  4606. idx = this._objects.indexOf(obj);
  4607. if (idx > 0 + objsMoved) {
  4608. newIdx = idx - 1;
  4609. removeFromArray(this._objects, obj);
  4610. this._objects.splice(newIdx, 0, obj);
  4611. }
  4612. objsMoved++;
  4613. }
  4614. }
  4615. else {
  4616. idx = this._objects.indexOf(object);
  4617. if (idx !== 0) {
  4618. // if object is not on the bottom of stack
  4619. newIdx = this._findNewLowerIndex(object, idx, intersecting);
  4620. removeFromArray(this._objects, object);
  4621. this._objects.splice(newIdx, 0, object);
  4622. }
  4623. }
  4624. this.renderOnAddRemove && this.requestRenderAll();
  4625. return this;
  4626. },
  4627. /**
  4628. * @private
  4629. */
  4630. _findNewLowerIndex: function(object, idx, intersecting) {
  4631. var newIdx, i;
  4632. if (intersecting) {
  4633. newIdx = idx;
  4634. // traverse down the stack looking for the nearest intersecting object
  4635. for (i = idx - 1; i >= 0; --i) {
  4636. var isIntersecting = object.intersectsWithObject(this._objects[i]) ||
  4637. object.isContainedWithinObject(this._objects[i]) ||
  4638. this._objects[i].isContainedWithinObject(object);
  4639. if (isIntersecting) {
  4640. newIdx = i;
  4641. break;
  4642. }
  4643. }
  4644. }
  4645. else {
  4646. newIdx = idx - 1;
  4647. }
  4648. return newIdx;
  4649. },
  4650. /**
  4651. * Moves an object or a selection up in stack of drawn objects
  4652. * An optional paramter, intersecting allowes to move the object in front
  4653. * of the first intersecting object. Where intersection is calculated with
  4654. * bounding box. If no intersection is found, there will not be change in the
  4655. * stack.
  4656. * @param {fabric.Object} object Object to send
  4657. * @param {Boolean} [intersecting] If `true`, send object in front of next upper intersecting object
  4658. * @return {fabric.Canvas} thisArg
  4659. * @chainable
  4660. */
  4661. bringForward: function (object, intersecting) {
  4662. if (!object) {
  4663. return this;
  4664. }
  4665. var activeSelection = this._activeObject,
  4666. i, obj, idx, newIdx, objs, objsMoved = 0;
  4667. if (object === activeSelection && object.type === 'activeSelection') {
  4668. objs = activeSelection._objects;
  4669. for (i = objs.length; i--;) {
  4670. obj = objs[i];
  4671. idx = this._objects.indexOf(obj);
  4672. if (idx < this._objects.length - 1 - objsMoved) {
  4673. newIdx = idx + 1;
  4674. removeFromArray(this._objects, obj);
  4675. this._objects.splice(newIdx, 0, obj);
  4676. }
  4677. objsMoved++;
  4678. }
  4679. }
  4680. else {
  4681. idx = this._objects.indexOf(object);
  4682. if (idx !== this._objects.length - 1) {
  4683. // if object is not on top of stack (last item in an array)
  4684. newIdx = this._findNewUpperIndex(object, idx, intersecting);
  4685. removeFromArray(this._objects, object);
  4686. this._objects.splice(newIdx, 0, object);
  4687. }
  4688. }
  4689. this.renderOnAddRemove && this.requestRenderAll();
  4690. return this;
  4691. },
  4692. /**
  4693. * @private
  4694. */
  4695. _findNewUpperIndex: function(object, idx, intersecting) {
  4696. var newIdx, i, len;
  4697. if (intersecting) {
  4698. newIdx = idx;
  4699. // traverse up the stack looking for the nearest intersecting object
  4700. for (i = idx + 1, len = this._objects.length; i < len; ++i) {
  4701. var isIntersecting = object.intersectsWithObject(this._objects[i]) ||
  4702. object.isContainedWithinObject(this._objects[i]) ||
  4703. this._objects[i].isContainedWithinObject(object);
  4704. if (isIntersecting) {
  4705. newIdx = i;
  4706. break;
  4707. }
  4708. }
  4709. }
  4710. else {
  4711. newIdx = idx + 1;
  4712. }
  4713. return newIdx;
  4714. },
  4715. /**
  4716. * Moves an object to specified level in stack of drawn objects
  4717. * @param {fabric.Object} object Object to send
  4718. * @param {Number} index Position to move to
  4719. * @return {fabric.Canvas} thisArg
  4720. * @chainable
  4721. */
  4722. moveTo: function (object, index) {
  4723. removeFromArray(this._objects, object);
  4724. this._objects.splice(index, 0, object);
  4725. return this.renderOnAddRemove && this.requestRenderAll();
  4726. },
  4727. /**
  4728. * Clears a canvas element and dispose objects
  4729. * @return {fabric.Canvas} thisArg
  4730. * @chainable
  4731. */
  4732. dispose: function () {
  4733. // cancel eventually ongoing renders
  4734. if (this.isRendering) {
  4735. fabric.util.cancelAnimFrame(this.isRendering);
  4736. this.isRendering = 0;
  4737. }
  4738. this.forEachObject(function(object) {
  4739. object.dispose && object.dispose();
  4740. });
  4741. this._objects = [];
  4742. this.backgroundImage = null;
  4743. this.overlayImage = null;
  4744. this._iTextInstances = null;
  4745. this.lowerCanvasEl = null;
  4746. this.contextContainer = null;
  4747. return this;
  4748. },
  4749. /**
  4750. * Returns a string representation of an instance
  4751. * @return {String} string representation of an instance
  4752. */
  4753. toString: function () {
  4754. return '#<fabric.Canvas (' + this.complexity() + '): ' +
  4755. '{ objects: ' + this.getObjects().length + ' }>';
  4756. }
  4757. });
  4758. extend(fabric.StaticCanvas.prototype, fabric.Observable);
  4759. extend(fabric.StaticCanvas.prototype, fabric.Collection);
  4760. extend(fabric.StaticCanvas.prototype, fabric.DataURLExporter);
  4761. extend(fabric.StaticCanvas, /** @lends fabric.StaticCanvas */ {
  4762. /**
  4763. * @static
  4764. * @type String
  4765. * @default
  4766. */
  4767. EMPTY_JSON: '{"objects": [], "background": "white"}',
  4768. /**
  4769. * Provides a way to check support of some of the canvas methods
  4770. * (either those of HTMLCanvasElement itself, or rendering context)
  4771. *
  4772. * @param {String} methodName Method to check support for;
  4773. * Could be one of "getImageData", "toDataURL", "toDataURLWithQuality" or "setLineDash"
  4774. * @return {Boolean | null} `true` if method is supported (or at least exists),
  4775. * `null` if canvas element or context can not be initialized
  4776. */
  4777. supports: function (methodName) {
  4778. var el = fabric.util.createCanvasElement();
  4779. if (!el || !el.getContext) {
  4780. return null;
  4781. }
  4782. var ctx = el.getContext('2d');
  4783. if (!ctx) {
  4784. return null;
  4785. }
  4786. switch (methodName) {
  4787. case 'getImageData':
  4788. return typeof ctx.getImageData !== 'undefined';
  4789. case 'setLineDash':
  4790. return typeof ctx.setLineDash !== 'undefined';
  4791. case 'toDataURL':
  4792. return typeof el.toDataURL !== 'undefined';
  4793. case 'toDataURLWithQuality':
  4794. try {
  4795. el.toDataURL('c_image/jpeg', 0);
  4796. return true;
  4797. }
  4798. catch (e) { }
  4799. return false;
  4800. default:
  4801. return null;
  4802. }
  4803. }
  4804. });
  4805. /**
  4806. * Returns JSON representation of canvas
  4807. * @function
  4808. * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
  4809. * @return {String} JSON string
  4810. * @tutorial {@link http://fabricjs.com/fabric-intro-part-3#serialization}
  4811. * @see {@link http://jsfiddle.net/fabricjs/pec86/|jsFiddle demo}
  4812. * @example <caption>JSON without additional properties</caption>
  4813. * var json = canvas.toJSON();
  4814. * @example <caption>JSON with additional properties included</caption>
  4815. * var json = canvas.toJSON(['lockMovementX', 'lockMovementY', 'lockRotation', 'lockScalingX', 'lockScalingY', 'lockUniScaling']);
  4816. * @example <caption>JSON without default values</caption>
  4817. * canvas.includeDefaultValues = false;
  4818. * var json = canvas.toJSON();
  4819. */
  4820. fabric.StaticCanvas.prototype.toJSON = fabric.StaticCanvas.prototype.toObject;
  4821. if (fabric.isLikelyNode) {
  4822. fabric.StaticCanvas.prototype.createPNGStream = function() {
  4823. var impl = fabric.util.getNodeCanvas(this.lowerCanvasEl);
  4824. return impl && impl.createPNGStream();
  4825. };
  4826. fabric.StaticCanvas.prototype.createJPEGStream = function(opts) {
  4827. var impl = fabric.util.getNodeCanvas(this.lowerCanvasEl);
  4828. return impl && impl.createJPEGStream(opts);
  4829. };
  4830. }
  4831. })();
  4832. (function () {
  4833. var supportQuality = fabric.StaticCanvas.supports('toDataURLWithQuality');
  4834. fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.StaticCanvas.prototype */ {
  4835. /**
  4836. * Exports canvas element to a dataurl c_image. Note that when multiplier is used, cropping is scaled appropriately
  4837. * @param {Object} [options] Options object
  4838. * @param {String} [options.format=png] The format of the output c_image. Either "jpeg" or "png"
  4839. * @param {Number} [options.quality=1] Quality level (0..1). Only used for jpeg.
  4840. * @param {Number} [options.multiplier=1] Multiplier to scale by, to have consistent
  4841. * @param {Number} [options.left] Cropping left offset. Introduced in v1.2.14
  4842. * @param {Number} [options.top] Cropping top offset. Introduced in v1.2.14
  4843. * @param {Number} [options.width] Cropping width. Introduced in v1.2.14
  4844. * @param {Number} [options.height] Cropping height. Introduced in v1.2.14
  4845. * @param {Boolean} [options.enableRetinaScaling] Enable retina scaling for clone c_image. Introduce in 2.0.0
  4846. * @return {String} Returns a data: URL containing a representation of the object in the format specified by options.format
  4847. * @see {@link http://jsfiddle.net/fabricjs/NfZVb/|jsFiddle demo}
  4848. * @example <caption>Generate jpeg dataURL with lower quality</caption>
  4849. * var dataURL = canvas.toDataURL({
  4850. * format: 'jpeg',
  4851. * quality: 0.8
  4852. * });
  4853. * @example <caption>Generate cropped png dataURL (clipping of canvas)</caption>
  4854. * var dataURL = canvas.toDataURL({
  4855. * format: 'png',
  4856. * left: 100,
  4857. * top: 100,
  4858. * width: 200,
  4859. * height: 200
  4860. * });
  4861. * @example <caption>Generate double scaled png dataURL</caption>
  4862. * var dataURL = canvas.toDataURL({
  4863. * format: 'png',
  4864. * multiplier: 2
  4865. * });
  4866. */
  4867. toDataURL: function (options) {
  4868. options || (options = { });
  4869. var format = options.format || 'png',
  4870. quality = options.quality || 1,
  4871. multiplier = (options.multiplier || 1) * (options.enableRetinaScaling ? 1 : 1 / this.getRetinaScaling()),
  4872. cropping = {
  4873. left: options.left || 0,
  4874. top: options.top || 0,
  4875. width: options.width || 0,
  4876. height: options.height || 0,
  4877. };
  4878. return this.__toDataURLWithMultiplier(format, quality, cropping, multiplier);
  4879. },
  4880. /**
  4881. * @private
  4882. */
  4883. __toDataURLWithMultiplier: function(format, quality, cropping, multiplier) {
  4884. var origWidth = this.width,
  4885. origHeight = this.height,
  4886. scaledWidth = (cropping.width || this.width) * multiplier,
  4887. scaledHeight = (cropping.height || this.height) * multiplier,
  4888. zoom = this.getZoom(),
  4889. newZoom = zoom * multiplier,
  4890. vp = this.viewportTransform,
  4891. translateX = (vp[4] - cropping.left) * multiplier,
  4892. translateY = (vp[5] - cropping.top) * multiplier,
  4893. newVp = [newZoom, 0, 0, newZoom, translateX, translateY],
  4894. originalInteractive = this.interactive,
  4895. originalSkipOffScreen = this.skipOffscreen,
  4896. needsResize = origWidth !== scaledWidth || origHeight !== scaledHeight;
  4897. this.viewportTransform = newVp;
  4898. this.skipOffscreen = false;
  4899. // setting interactive to false avoid exporting controls
  4900. this.interactive = false;
  4901. if (needsResize) {
  4902. this.setDimensions({ width: scaledWidth, height: scaledHeight }, { backstoreOnly: true });
  4903. }
  4904. // call a renderAll to force sync update. This will cancel the scheduled requestRenderAll
  4905. // from setDimensions
  4906. this.renderAll();
  4907. var data = this.__toDataURL(format, quality, cropping);
  4908. this.interactive = originalInteractive;
  4909. this.skipOffscreen = originalSkipOffScreen;
  4910. this.viewportTransform = vp;
  4911. //setDimensions with no option object is taking care of:
  4912. //this.width, this.height, this.requestRenderAll()
  4913. if (needsResize) {
  4914. this.setDimensions({ width: origWidth, height: origHeight }, { backstoreOnly: true });
  4915. }
  4916. this.renderAll();
  4917. return data;
  4918. },
  4919. /**
  4920. * @private
  4921. */
  4922. __toDataURL: function(format, quality) {
  4923. var canvasEl = this.contextContainer.canvas;
  4924. // to avoid common confusion https://github.com/kangax/fabric.js/issues/806
  4925. if (format === 'jpg') {
  4926. format = 'jpeg';
  4927. }
  4928. var data = supportQuality
  4929. ? canvasEl.toDataURL('c_image/' + format, quality)
  4930. : canvasEl.toDataURL('c_image/' + format);
  4931. return data;
  4932. },
  4933. });
  4934. })();
  4935. (function(global) {
  4936. 'use strict';
  4937. var fabric = global.fabric || (global.fabric = { }),
  4938. extend = fabric.util.object.extend,
  4939. clone = fabric.util.object.clone,
  4940. toFixed = fabric.util.toFixed,
  4941. capitalize = fabric.util.string.capitalize,
  4942. degreesToRadians = fabric.util.degreesToRadians,
  4943. supportsLineDash = fabric.StaticCanvas.supports('setLineDash'),
  4944. objectCaching = !fabric.isLikelyNode,
  4945. ALIASING_LIMIT = 2;
  4946. if (fabric.Object) {
  4947. return;
  4948. }
  4949. /**
  4950. * Root object class from which all 2d shape classes inherit from
  4951. * @class fabric.Object
  4952. * @tutorial {@link http://fabricjs.com/fabric-intro-part-1#objects}
  4953. * @see {@link fabric.Object#initialize} for constructor definition
  4954. *
  4955. * @fires added
  4956. * @fires removed
  4957. *
  4958. * @fires selected
  4959. * @fires deselected
  4960. * @fires modified
  4961. * @fires modified
  4962. * @fires moved
  4963. * @fires scaled
  4964. * @fires rotated
  4965. * @fires skewed
  4966. *
  4967. * @fires rotating
  4968. * @fires scaling
  4969. * @fires moving
  4970. * @fires skewing
  4971. *
  4972. * @fires mousedown
  4973. * @fires mouseup
  4974. * @fires mouseover
  4975. * @fires mouseout
  4976. * @fires mousewheel
  4977. * @fires mousedblclick
  4978. *
  4979. * @fires dragover
  4980. * @fires dragenter
  4981. * @fires dragleave
  4982. * @fires drop
  4983. */
  4984. fabric.Object = fabric.util.createClass(fabric.CommonMethods, /** @lends fabric.Object.prototype */ {
  4985. /**
  4986. * Type of an object (rect, circle, path, etc.).
  4987. * Note that this property is meant to be read-only and not meant to be modified.
  4988. * If you modify, certain parts of Fabric (such as JSON loading) won't work correctly.
  4989. * @type String
  4990. * @default
  4991. */
  4992. type: 'object',
  4993. /**
  4994. * Horizontal origin of transformation of an object (one of "left", "right", "center")
  4995. * See http://jsfiddle.net/1ow02gea/244/ on how originX/originY affect objects in groups
  4996. * @type String
  4997. * @default
  4998. */
  4999. originX: 'left',
  5000. /**
  5001. * Vertical origin of transformation of an object (one of "top", "bottom", "center")
  5002. * See http://jsfiddle.net/1ow02gea/244/ on how originX/originY affect objects in groups
  5003. * @type String
  5004. * @default
  5005. */
  5006. originY: 'top',
  5007. /**
  5008. * Top position of an object. Note that by default it's relative to object top. You can change this by setting originY={top/center/bottom}
  5009. * @type Number
  5010. * @default
  5011. */
  5012. top: 0,
  5013. /**
  5014. * Left position of an object. Note that by default it's relative to object left. You can change this by setting originX={left/center/right}
  5015. * @type Number
  5016. * @default
  5017. */
  5018. left: 0,
  5019. /**
  5020. * Object width
  5021. * @type Number
  5022. * @default
  5023. */
  5024. width: 0,
  5025. /**
  5026. * Object height
  5027. * @type Number
  5028. * @default
  5029. */
  5030. height: 0,
  5031. /**
  5032. * Object scale factor (horizontal)
  5033. * @type Number
  5034. * @default
  5035. */
  5036. scaleX: 1,
  5037. /**
  5038. * Object scale factor (vertical)
  5039. * @type Number
  5040. * @default
  5041. */
  5042. scaleY: 1,
  5043. /**
  5044. * When true, an object is rendered as flipped horizontally
  5045. * @type Boolean
  5046. * @default
  5047. */
  5048. flipX: false,
  5049. /**
  5050. * When true, an object is rendered as flipped vertically
  5051. * @type Boolean
  5052. * @default
  5053. */
  5054. flipY: false,
  5055. /**
  5056. * Opacity of an object
  5057. * @type Number
  5058. * @default
  5059. */
  5060. opacity: 1,
  5061. /**
  5062. * Angle of rotation of an object (in degrees)
  5063. * @type Number
  5064. * @default
  5065. */
  5066. angle: 0,
  5067. /**
  5068. * Angle of skew on x axes of an object (in degrees)
  5069. * @type Number
  5070. * @default
  5071. */
  5072. skewX: 0,
  5073. /**
  5074. * Angle of skew on y axes of an object (in degrees)
  5075. * @type Number
  5076. * @default
  5077. */
  5078. skewY: 0,
  5079. /**
  5080. * Size of object's controlling corners (in pixels)
  5081. * @type Number
  5082. * @default
  5083. */
  5084. cornerSize: 13,
  5085. /**
  5086. * When true, object's controlling corners are rendered as transparent inside (i.e. stroke instead of fill)
  5087. * @type Boolean
  5088. * @default
  5089. */
  5090. transparentCorners: true,
  5091. /**
  5092. * Default cursor value used when hovering over this object on canvas
  5093. * @type String
  5094. * @default
  5095. */
  5096. hoverCursor: null,
  5097. /**
  5098. * Default cursor value used when moving this object on canvas
  5099. * @type String
  5100. * @default
  5101. */
  5102. moveCursor: null,
  5103. /**
  5104. * Padding between object and its controlling borders (in pixels)
  5105. * @type Number
  5106. * @default
  5107. */
  5108. padding: 0,
  5109. /**
  5110. * Color of controlling borders of an object (when it's active)
  5111. * @type String
  5112. * @default
  5113. */
  5114. borderColor: 'rgba(102,153,255,0.75)',
  5115. /**
  5116. * Array specifying dash pattern of an object's borders (hasBorder must be true)
  5117. * @since 1.6.2
  5118. * @type Array
  5119. */
  5120. borderDashArray: null,
  5121. /**
  5122. * Color of controlling corners of an object (when it's active)
  5123. * @type String
  5124. * @default
  5125. */
  5126. cornerColor: 'rgba(102,153,255,0.5)',
  5127. /**
  5128. * Color of controlling corners of an object (when it's active and transparentCorners false)
  5129. * @since 1.6.2
  5130. * @type String
  5131. * @default
  5132. */
  5133. cornerStrokeColor: null,
  5134. /**
  5135. * Specify c_style of control, 'rect' or 'circle'
  5136. * @since 1.6.2
  5137. * @type String
  5138. */
  5139. cornerStyle: 'rect',
  5140. /**
  5141. * Array specifying dash pattern of an object's control (hasBorder must be true)
  5142. * @since 1.6.2
  5143. * @type Array
  5144. */
  5145. cornerDashArray: null,
  5146. /**
  5147. * When true, this object will use center point as the origin of transformation
  5148. * when being scaled via the controls.
  5149. * <b>Backwards incompatibility note:</b> This property replaces "centerTransform" (Boolean).
  5150. * @since 1.3.4
  5151. * @type Boolean
  5152. * @default
  5153. */
  5154. centeredScaling: false,
  5155. /**
  5156. * When true, this object will use center point as the origin of transformation
  5157. * when being rotated via the controls.
  5158. * <b>Backwards incompatibility note:</b> This property replaces "centerTransform" (Boolean).
  5159. * @since 1.3.4
  5160. * @type Boolean
  5161. * @default
  5162. */
  5163. centeredRotation: true,
  5164. /**
  5165. * Color of object's fill
  5166. * takes c_style colors https://www.w3.org/TR/css-color-3/
  5167. * @type String
  5168. * @default
  5169. */
  5170. fill: 'rgb(0,0,0)',
  5171. /**
  5172. * Fill rule used to fill an object
  5173. * accepted values are nonzero, evenodd
  5174. * <b>Backwards incompatibility note:</b> This property was used for setting globalCompositeOperation until v1.4.12 (use `fabric.Object#globalCompositeOperation` instead)
  5175. * @type String
  5176. * @default
  5177. */
  5178. fillRule: 'nonzero',
  5179. /**
  5180. * Composite rule used for canvas globalCompositeOperation
  5181. * @type String
  5182. * @default
  5183. */
  5184. globalCompositeOperation: 'source-over',
  5185. /**
  5186. * Background color of an object.
  5187. * takes c_style colors https://www.w3.org/TR/css-color-3/
  5188. * @type String
  5189. * @default
  5190. */
  5191. backgroundColor: '',
  5192. /**
  5193. * Selection Background color of an object. colored layer behind the object when it is active.
  5194. * does not mix good with globalCompositeOperation methods.
  5195. * @type String
  5196. * @default
  5197. */
  5198. selectionBackgroundColor: '',
  5199. /**
  5200. * When defined, an object is rendered via stroke and this property specifies its color
  5201. * takes c_style colors https://www.w3.org/TR/css-color-3/
  5202. * @type String
  5203. * @default
  5204. */
  5205. stroke: null,
  5206. /**
  5207. * Width of a stroke used to render this object
  5208. * @type Number
  5209. * @default
  5210. */
  5211. strokeWidth: 1,
  5212. /**
  5213. * Array specifying dash pattern of an object's stroke (stroke must be defined)
  5214. * @type Array
  5215. */
  5216. strokeDashArray: null,
  5217. /**
  5218. * Line endings c_style of an object's stroke (one of "butt", "round", "square")
  5219. * @type String
  5220. * @default
  5221. */
  5222. strokeLineCap: 'butt',
  5223. /**
  5224. * Corner c_style of an object's stroke (one of "bevil", "round", "miter")
  5225. * @type String
  5226. * @default
  5227. */
  5228. strokeLineJoin: 'miter',
  5229. /**
  5230. * Maximum miter length (used for strokeLineJoin = "miter") of an object's stroke
  5231. * @type Number
  5232. * @default
  5233. */
  5234. strokeMiterLimit: 4,
  5235. /**
  5236. * Shadow object representing shadow of this shape
  5237. * @type fabric.Shadow
  5238. * @default
  5239. */
  5240. shadow: null,
  5241. /**
  5242. * Opacity of object's controlling borders when object is active and moving
  5243. * @type Number
  5244. * @default
  5245. */
  5246. borderOpacityWhenMoving: 0.4,
  5247. /**
  5248. * Scale factor of object's controlling borders
  5249. * @type Number
  5250. * @default
  5251. */
  5252. borderScaleFactor: 1,
  5253. /**
  5254. * Transform matrix (similar to SVG's transform matrix)
  5255. * @type Array
  5256. */
  5257. transformMatrix: null,
  5258. /**
  5259. * Minimum allowed scale value of an object
  5260. * @type Number
  5261. * @default
  5262. */
  5263. minScaleLimit: 0,
  5264. /**
  5265. * When set to `false`, an object can not be selected for modification (using either point-click-based or group-based selection).
  5266. * But events still fire on it.
  5267. * @type Boolean
  5268. * @default
  5269. */
  5270. selectable: true,
  5271. /**
  5272. * When set to `false`, an object can not be a target of events. All events propagate through it. Introduced in v1.3.4
  5273. * @type Boolean
  5274. * @default
  5275. */
  5276. evented: true,
  5277. /**
  5278. * When set to `false`, an object is not rendered on canvas
  5279. * @type Boolean
  5280. * @default
  5281. */
  5282. visible: true,
  5283. /**
  5284. * When set to `false`, object's controls are not displayed and can not be used to manipulate object
  5285. * @type Boolean
  5286. * @default
  5287. */
  5288. hasControls: true,
  5289. /**
  5290. * When set to `false`, object's controlling borders are not rendered
  5291. * @type Boolean
  5292. * @default
  5293. */
  5294. hasBorders: true,
  5295. /**
  5296. * When set to `false`, object's controlling rotating point will not be visible or selectable
  5297. * @type Boolean
  5298. * @default
  5299. */
  5300. hasRotatingPoint: true,
  5301. /**
  5302. * Offset for object's controlling rotating point (when enabled via `hasRotatingPoint`)
  5303. * @type Number
  5304. * @default
  5305. */
  5306. rotatingPointOffset: 40,
  5307. /**
  5308. * When set to `true`, objects are "found" on canvas on per-pixel basis rather than according to bounding box
  5309. * @type Boolean
  5310. * @default
  5311. */
  5312. perPixelTargetFind: false,
  5313. /**
  5314. * When `false`, default object's values are not included in its serialization
  5315. * @type Boolean
  5316. * @default
  5317. */
  5318. includeDefaultValues: true,
  5319. /**
  5320. * Function that determines clipping of an object (context is passed as a first argument)
  5321. * Note that context origin is at the object's center point (not left/top corner)
  5322. * @deprecated since 2.0.0
  5323. * @type Function
  5324. */
  5325. clipTo: null,
  5326. /**
  5327. * When `true`, object horizontal movement is locked
  5328. * @type Boolean
  5329. * @default
  5330. */
  5331. lockMovementX: false,
  5332. /**
  5333. * When `true`, object vertical movement is locked
  5334. * @type Boolean
  5335. * @default
  5336. */
  5337. lockMovementY: false,
  5338. /**
  5339. * When `true`, object rotation is locked
  5340. * @type Boolean
  5341. * @default
  5342. */
  5343. lockRotation: false,
  5344. /**
  5345. * When `true`, object horizontal scaling is locked
  5346. * @type Boolean
  5347. * @default
  5348. */
  5349. lockScalingX: false,
  5350. /**
  5351. * When `true`, object vertical scaling is locked
  5352. * @type Boolean
  5353. * @default
  5354. */
  5355. lockScalingY: false,
  5356. /**
  5357. * When `true`, object non-uniform scaling is locked
  5358. * @type Boolean
  5359. * @default
  5360. */
  5361. lockUniScaling: false,
  5362. /**
  5363. * When `true`, object horizontal skewing is locked
  5364. * @type Boolean
  5365. * @default
  5366. */
  5367. lockSkewingX: false,
  5368. /**
  5369. * When `true`, object vertical skewing is locked
  5370. * @type Boolean
  5371. * @default
  5372. */
  5373. lockSkewingY: false,
  5374. /**
  5375. * When `true`, object cannot be flipped by scaling into negative values
  5376. * @type Boolean
  5377. * @default
  5378. */
  5379. lockScalingFlip: false,
  5380. /**
  5381. * When `true`, object is not exported in OBJECT/JSON
  5382. * since 1.6.3
  5383. * @type Boolean
  5384. * @default
  5385. */
  5386. excludeFromExport: false,
  5387. /**
  5388. * When `true`, object is cached on an additional canvas.
  5389. * default to true
  5390. * since 1.7.0
  5391. * @type Boolean
  5392. * @default true
  5393. */
  5394. objectCaching: objectCaching,
  5395. /**
  5396. * When `true`, object properties are checked for cache invalidation. In some particular
  5397. * situation you may want this to be disabled ( spray brush, very big, groups)
  5398. * or if your application does not allow you to modify properties for groups child you want
  5399. * to disable it for groups.
  5400. * default to false
  5401. * since 1.7.0
  5402. * @type Boolean
  5403. * @default false
  5404. */
  5405. statefullCache: false,
  5406. /**
  5407. * When `true`, cache does not get updated during scaling. The picture will get blocky if scaled
  5408. * too much and will be redrawn with correct details at the end of scaling.
  5409. * this setting is performance and application dependant.
  5410. * default to true
  5411. * since 1.7.0
  5412. * @type Boolean
  5413. * @default true
  5414. */
  5415. noScaleCache: true,
  5416. /**
  5417. * When set to `true`, object's cache will be rerendered next render call.
  5418. * since 1.7.0
  5419. * @type Boolean
  5420. * @default true
  5421. */
  5422. dirty: true,
  5423. /**
  5424. * keeps the value of the last hovered coner during mouse move.
  5425. * 0 is no corner, or 'mt', 'ml', 'mtr' etc..
  5426. * It should be private, but there is no harm in using it as
  5427. * a read-only property.
  5428. * @type number|string|any
  5429. * @default 0
  5430. */
  5431. __corner: 0,
  5432. /**
  5433. * Determins if the fill or the stroke is drawn first (one of "fill" or "stroke")
  5434. * @type String
  5435. * @default
  5436. */
  5437. paintFirst: 'fill',
  5438. /**
  5439. * List of properties to consider when checking if state
  5440. * of an object is changed (fabric.Object#hasStateChanged)
  5441. * as well as for history (undo/redo) purposes
  5442. * @type Array
  5443. */
  5444. stateProperties: (
  5445. 'top left width height scaleX scaleY flipX flipY originX originY transformMatrix ' +
  5446. 'stroke strokeWidth strokeDashArray strokeLineCap strokeLineJoin strokeMiterLimit ' +
  5447. 'angle opacity fill globalCompositeOperation shadow clipTo visible backgroundColor ' +
  5448. 'skewX skewY fillRule paintFirst'
  5449. ).split(' '),
  5450. /**
  5451. * List of properties to consider when checking if cache needs refresh
  5452. * @type Array
  5453. */
  5454. cacheProperties: (
  5455. 'fill stroke strokeWidth strokeDashArray width height paintFirst' +
  5456. ' strokeLineCap strokeLineJoin strokeMiterLimit backgroundColor'
  5457. ).split(' '),
  5458. /**
  5459. * Constructor
  5460. * @param {Object} [options] Options object
  5461. */
  5462. initialize: function(options) {
  5463. if (options) {
  5464. this.setOptions(options);
  5465. }
  5466. },
  5467. /**
  5468. * Create a the canvas used to keep the cached copy of the object
  5469. * @private
  5470. */
  5471. _createCacheCanvas: function() {
  5472. this._cacheProperties = {};
  5473. this._cacheCanvas = fabric.document.createElement('canvas');
  5474. this._cacheContext = this._cacheCanvas.getContext('2d');
  5475. this._updateCacheCanvas();
  5476. // if canvas gets created, is empty, so dirty.
  5477. this.dirty = true;
  5478. },
  5479. /**
  5480. * Limit the cache dimensions so that X * Y do not cross fabric.perfLimitSizeTotal
  5481. * and each side do not cross fabric.cacheSideLimit
  5482. * those numbers are configurable so that you can get as much detail as you want
  5483. * making bargain with performances.
  5484. * @param {Object} dims
  5485. * @param {Object} dims.width width of canvas
  5486. * @param {Object} dims.height height of canvas
  5487. * @param {Object} dims.zoomX zoomX zoom value to unscale the canvas before drawing cache
  5488. * @param {Object} dims.zoomY zoomY zoom value to unscale the canvas before drawing cache
  5489. * @return {Object}.width width of canvas
  5490. * @return {Object}.height height of canvas
  5491. * @return {Object}.zoomX zoomX zoom value to unscale the canvas before drawing cache
  5492. * @return {Object}.zoomY zoomY zoom value to unscale the canvas before drawing cache
  5493. */
  5494. _limitCacheSize: function(dims) {
  5495. var perfLimitSizeTotal = fabric.perfLimitSizeTotal,
  5496. width = dims.width, height = dims.height,
  5497. max = fabric.maxCacheSideLimit, min = fabric.minCacheSideLimit;
  5498. if (width <= max && height <= max && width * height <= perfLimitSizeTotal) {
  5499. if (width < min) {
  5500. dims.width = min;
  5501. }
  5502. if (height < min) {
  5503. dims.height = min;
  5504. }
  5505. return dims;
  5506. }
  5507. var ar = width / height, limitedDims = fabric.util.limitDimsByArea(ar, perfLimitSizeTotal),
  5508. capValue = fabric.util.capValue,
  5509. x = capValue(min, limitedDims.x, max),
  5510. y = capValue(min, limitedDims.y, max);
  5511. if (width > x) {
  5512. dims.zoomX /= width / x;
  5513. dims.width = x;
  5514. dims.capped = true;
  5515. }
  5516. if (height > y) {
  5517. dims.zoomY /= height / y;
  5518. dims.height = y;
  5519. dims.capped = true;
  5520. }
  5521. return dims;
  5522. },
  5523. /**
  5524. * Return the dimension and the zoom level needed to create a cache canvas
  5525. * big enough to host the object to be cached.
  5526. * @private
  5527. * @param {Object} dim.x width of object to be cached
  5528. * @param {Object} dim.y height of object to be cached
  5529. * @return {Object}.width width of canvas
  5530. * @return {Object}.height height of canvas
  5531. * @return {Object}.zoomX zoomX zoom value to unscale the canvas before drawing cache
  5532. * @return {Object}.zoomY zoomY zoom value to unscale the canvas before drawing cache
  5533. */
  5534. _getCacheCanvasDimensions: function() {
  5535. var zoom = this.canvas && this.canvas.getZoom() || 1,
  5536. objectScale = this.getObjectScaling(),
  5537. retina = this.canvas && this.canvas._isRetinaScaling() ? fabric.devicePixelRatio : 1,
  5538. dim = this._getNonTransformedDimensions(),
  5539. zoomX = objectScale.scaleX * zoom * retina,
  5540. zoomY = objectScale.scaleY * zoom * retina,
  5541. width = dim.x * zoomX,
  5542. height = dim.y * zoomY;
  5543. return {
  5544. // for sure this ALIASING_LIMIT is slightly crating problem
  5545. // in situation in wich the cache canvas gets an upper limit
  5546. width: width + ALIASING_LIMIT,
  5547. height: height + ALIASING_LIMIT,
  5548. zoomX: zoomX,
  5549. zoomY: zoomY,
  5550. x: dim.x,
  5551. y: dim.y
  5552. };
  5553. },
  5554. /**
  5555. * Update width and height of the canvas for cache
  5556. * returns true or false if canvas needed resize.
  5557. * @private
  5558. * @return {Boolean} true if the canvas has been resized
  5559. */
  5560. _updateCacheCanvas: function() {
  5561. if (this.noScaleCache && this.canvas && this.canvas._currentTransform) {
  5562. var target = this.canvas._currentTransform.target,
  5563. action = this.canvas._currentTransform.action;
  5564. if (this === target && action.slice && action.slice(0, 5) === 'scale') {
  5565. return false;
  5566. }
  5567. }
  5568. var canvas = this._cacheCanvas,
  5569. dims = this._limitCacheSize(this._getCacheCanvasDimensions()),
  5570. minCacheSize = fabric.minCacheSideLimit,
  5571. width = dims.width, height = dims.height, drawingWidth, drawingHeight,
  5572. zoomX = dims.zoomX, zoomY = dims.zoomY,
  5573. dimensionsChanged = width !== this.cacheWidth || height !== this.cacheHeight,
  5574. zoomChanged = this.zoomX !== zoomX || this.zoomY !== zoomY,
  5575. shouldRedraw = dimensionsChanged || zoomChanged,
  5576. additionalWidth = 0, additionalHeight = 0, shouldResizeCanvas = false;
  5577. if (dimensionsChanged) {
  5578. var canvasWidth = this._cacheCanvas.width,
  5579. canvasHeight = this._cacheCanvas.height,
  5580. sizeGrowing = width > canvasWidth || height > canvasHeight,
  5581. sizeShrinking = (width < canvasWidth * 0.9 || height < canvasHeight * 0.9) &&
  5582. canvasWidth > minCacheSize && canvasHeight > minCacheSize;
  5583. shouldResizeCanvas = sizeGrowing || sizeShrinking;
  5584. if (sizeGrowing && !dims.capped && (width > minCacheSize || height > minCacheSize)) {
  5585. additionalWidth = width * 0.1;
  5586. additionalHeight = height * 0.1;
  5587. }
  5588. }
  5589. if (shouldRedraw) {
  5590. if (shouldResizeCanvas) {
  5591. canvas.width = Math.ceil(width + additionalWidth);
  5592. canvas.height = Math.ceil(height + additionalHeight);
  5593. }
  5594. else {
  5595. this._cacheContext.setTransform(1, 0, 0, 1, 0, 0);
  5596. this._cacheContext.clearRect(0, 0, canvas.width, canvas.height);
  5597. }
  5598. drawingWidth = dims.x * zoomX / 2;
  5599. drawingHeight = dims.y * zoomY / 2;
  5600. this.cacheTranslationX = Math.round(canvas.width / 2 - drawingWidth) + drawingWidth;
  5601. this.cacheTranslationY = Math.round(canvas.height / 2 - drawingHeight) + drawingHeight;
  5602. this.cacheWidth = width;
  5603. this.cacheHeight = height;
  5604. this._cacheContext.translate(this.cacheTranslationX, this.cacheTranslationY);
  5605. this._cacheContext.scale(zoomX, zoomY);
  5606. this.zoomX = zoomX;
  5607. this.zoomY = zoomY;
  5608. return true;
  5609. }
  5610. return false;
  5611. },
  5612. /**
  5613. * Sets object's properties from options
  5614. * @param {Object} [options] Options object
  5615. */
  5616. setOptions: function(options) {
  5617. this._setOptions(options);
  5618. this._initGradient(options.fill, 'fill');
  5619. this._initGradient(options.stroke, 'stroke');
  5620. this._initClipping(options);
  5621. this._initPattern(options.fill, 'fill');
  5622. this._initPattern(options.stroke, 'stroke');
  5623. },
  5624. /**
  5625. * Transforms context when rendering an object
  5626. * @param {CanvasRenderingContext2D} ctx Context
  5627. */
  5628. transform: function(ctx) {
  5629. var m;
  5630. if (this.group && !this.group._transformDone) {
  5631. m = this.calcTransformMatrix();
  5632. }
  5633. else {
  5634. m = this.calcOwnMatrix();
  5635. }
  5636. ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
  5637. },
  5638. /**
  5639. * Returns an object representation of an instance
  5640. * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
  5641. * @return {Object} Object representation of an instance
  5642. */
  5643. toObject: function(propertiesToInclude) {
  5644. var NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS,
  5645. object = {
  5646. type: this.type,
  5647. version: fabric.version,
  5648. originX: this.originX,
  5649. originY: this.originY,
  5650. left: toFixed(this.left, NUM_FRACTION_DIGITS),
  5651. top: toFixed(this.top, NUM_FRACTION_DIGITS),
  5652. width: toFixed(this.width, NUM_FRACTION_DIGITS),
  5653. height: toFixed(this.height, NUM_FRACTION_DIGITS),
  5654. fill: (this.fill && this.fill.toObject) ? this.fill.toObject() : this.fill,
  5655. stroke: (this.stroke && this.stroke.toObject) ? this.stroke.toObject() : this.stroke,
  5656. strokeWidth: toFixed(this.strokeWidth, NUM_FRACTION_DIGITS),
  5657. strokeDashArray: this.strokeDashArray ? this.strokeDashArray.concat() : this.strokeDashArray,
  5658. strokeLineCap: this.strokeLineCap,
  5659. strokeLineJoin: this.strokeLineJoin,
  5660. strokeMiterLimit: toFixed(this.strokeMiterLimit, NUM_FRACTION_DIGITS),
  5661. scaleX: toFixed(this.scaleX, NUM_FRACTION_DIGITS),
  5662. scaleY: toFixed(this.scaleY, NUM_FRACTION_DIGITS),
  5663. angle: toFixed(this.angle, NUM_FRACTION_DIGITS),
  5664. flipX: this.flipX,
  5665. flipY: this.flipY,
  5666. opacity: toFixed(this.opacity, NUM_FRACTION_DIGITS),
  5667. shadow: (this.shadow && this.shadow.toObject) ? this.shadow.toObject() : this.shadow,
  5668. visible: this.visible,
  5669. clipTo: this.clipTo && String(this.clipTo),
  5670. backgroundColor: this.backgroundColor,
  5671. fillRule: this.fillRule,
  5672. paintFirst: this.paintFirst,
  5673. globalCompositeOperation: this.globalCompositeOperation,
  5674. transformMatrix: this.transformMatrix ? this.transformMatrix.concat() : null,
  5675. skewX: toFixed(this.skewX, NUM_FRACTION_DIGITS),
  5676. skewY: toFixed(this.skewY, NUM_FRACTION_DIGITS)
  5677. };
  5678. fabric.util.populateWithProperties(this, object, propertiesToInclude);
  5679. if (!this.includeDefaultValues) {
  5680. object = this._removeDefaultValues(object);
  5681. }
  5682. return object;
  5683. },
  5684. /**
  5685. * Returns (dataless) object representation of an instance
  5686. * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
  5687. * @return {Object} Object representation of an instance
  5688. */
  5689. toDatalessObject: function(propertiesToInclude) {
  5690. // will be overwritten by subclasses
  5691. return this.toObject(propertiesToInclude);
  5692. },
  5693. /**
  5694. * @private
  5695. * @param {Object} object
  5696. */
  5697. _removeDefaultValues: function(object) {
  5698. var prototype = fabric.util.getKlass(object.type).prototype,
  5699. stateProperties = prototype.stateProperties;
  5700. stateProperties.forEach(function(prop) {
  5701. if (object[prop] === prototype[prop]) {
  5702. delete object[prop];
  5703. }
  5704. var isArray = Object.prototype.toString.call(object[prop]) === '[object Array]' &&
  5705. Object.prototype.toString.call(prototype[prop]) === '[object Array]';
  5706. // basically a check for [] === []
  5707. if (isArray && object[prop].length === 0 && prototype[prop].length === 0) {
  5708. delete object[prop];
  5709. }
  5710. });
  5711. return object;
  5712. },
  5713. /**
  5714. * Returns a string representation of an instance
  5715. * @return {String}
  5716. */
  5717. toString: function() {
  5718. return '#<fabric.' + capitalize(this.type) + '>';
  5719. },
  5720. /**
  5721. * Return the object scale factor counting also the group scaling
  5722. * @return {Object} object with scaleX and scaleY properties
  5723. */
  5724. getObjectScaling: function() {
  5725. var scaleX = this.scaleX, scaleY = this.scaleY;
  5726. if (this.group) {
  5727. var scaling = this.group.getObjectScaling();
  5728. scaleX *= scaling.scaleX;
  5729. scaleY *= scaling.scaleY;
  5730. }
  5731. return { scaleX: scaleX, scaleY: scaleY };
  5732. },
  5733. /**
  5734. * Return the object opacity counting also the group property
  5735. * @return {Number}
  5736. */
  5737. getObjectOpacity: function() {
  5738. var opacity = this.opacity;
  5739. if (this.group) {
  5740. opacity *= this.group.getObjectOpacity();
  5741. }
  5742. return opacity;
  5743. },
  5744. /**
  5745. * @private
  5746. * @param {String} key
  5747. * @param {*} value
  5748. * @return {fabric.Object} thisArg
  5749. */
  5750. _set: function(key, value) {
  5751. var shouldConstrainValue = (key === 'scaleX' || key === 'scaleY'),
  5752. isChanged = this[key] !== value, groupNeedsUpdate = false;
  5753. if (shouldConstrainValue) {
  5754. value = this._constrainScale(value);
  5755. }
  5756. if (key === 'scaleX' && value < 0) {
  5757. this.flipX = !this.flipX;
  5758. value *= -1;
  5759. }
  5760. else if (key === 'scaleY' && value < 0) {
  5761. this.flipY = !this.flipY;
  5762. value *= -1;
  5763. }
  5764. else if (key === 'shadow' && value && !(value instanceof fabric.Shadow)) {
  5765. value = new fabric.Shadow(value);
  5766. }
  5767. else if (key === 'dirty' && this.group) {
  5768. this.group.set('dirty', value);
  5769. }
  5770. this[key] = value;
  5771. if (isChanged) {
  5772. groupNeedsUpdate = this.group && this.group.isOnACache();
  5773. if (this.cacheProperties.indexOf(key) > -1) {
  5774. this.dirty = true;
  5775. groupNeedsUpdate && this.group.set('dirty', true);
  5776. }
  5777. else if (groupNeedsUpdate && this.stateProperties.indexOf(key) > -1) {
  5778. this.group.set('dirty', true);
  5779. }
  5780. }
  5781. return this;
  5782. },
  5783. /**
  5784. * This callback function is called by the parent group of an object every
  5785. * time a non-delegated property changes on the group. It is passed the key
  5786. * and value as parameters. Not adding in this function's signature to avoid
  5787. * Travis build error about unused variables.
  5788. */
  5789. setOnGroup: function() {
  5790. // implemented by sub-classes, as needed.
  5791. },
  5792. /**
  5793. * Retrieves viewportTransform from Object's canvas if possible
  5794. * @method getViewportTransform
  5795. * @memberOf fabric.Object.prototype
  5796. * @return {Boolean}
  5797. */
  5798. getViewportTransform: function() {
  5799. if (this.canvas && this.canvas.viewportTransform) {
  5800. return this.canvas.viewportTransform;
  5801. }
  5802. return fabric.iMatrix.concat();
  5803. },
  5804. /*
  5805. * @private
  5806. * return if the object would be visible in rendering
  5807. * @memberOf fabric.Object.prototype
  5808. * @return {Boolean}
  5809. */
  5810. isNotVisible: function() {
  5811. return this.opacity === 0 || (this.width === 0 && this.height === 0) || !this.visible;
  5812. },
  5813. /**
  5814. * Renders an object on a specified context
  5815. * @param {CanvasRenderingContext2D} ctx Context to render on
  5816. */
  5817. render: function(ctx) {
  5818. // do not render if width/height are zeros or object is not visible
  5819. if (this.isNotVisible()) {
  5820. return;
  5821. }
  5822. if (this.canvas && this.canvas.skipOffscreen && !this.group && !this.isOnScreen()) {
  5823. return;
  5824. }
  5825. ctx.save();
  5826. this._setupCompositeOperation(ctx);
  5827. this.drawSelectionBackground(ctx);
  5828. this.transform(ctx);
  5829. this._setOpacity(ctx);
  5830. this._setShadow(ctx, this);
  5831. if (this.transformMatrix) {
  5832. ctx.transform.apply(ctx, this.transformMatrix);
  5833. }
  5834. this.clipTo && fabric.util.clipContext(this, ctx);
  5835. if (this.shouldCache()) {
  5836. if (!this._cacheCanvas) {
  5837. this._createCacheCanvas();
  5838. }
  5839. if (this.isCacheDirty()) {
  5840. this.statefullCache && this.saveState({ propertySet: 'cacheProperties' });
  5841. this.drawObject(this._cacheContext);
  5842. this.dirty = false;
  5843. }
  5844. this.drawCacheOnCanvas(ctx);
  5845. }
  5846. else {
  5847. this._removeCacheCanvas();
  5848. this.dirty = false;
  5849. this.drawObject(ctx);
  5850. if (this.objectCaching && this.statefullCache) {
  5851. this.saveState({ propertySet: 'cacheProperties' });
  5852. }
  5853. }
  5854. this.clipTo && ctx.restore();
  5855. ctx.restore();
  5856. },
  5857. /**
  5858. * Remove cacheCanvas and its dimensions from the objects
  5859. */
  5860. _removeCacheCanvas: function() {
  5861. this._cacheCanvas = null;
  5862. this.cacheWidth = 0;
  5863. this.cacheHeight = 0;
  5864. },
  5865. /**
  5866. * When set to `true`, force the object to have its own cache, even if it is inside a group
  5867. * it may be needed when your object behave in a particular way on the cache and always needs
  5868. * its own isolated canvas to render correctly.
  5869. * Created to be overridden
  5870. * since 1.7.12
  5871. * @returns false
  5872. */
  5873. needsItsOwnCache: function() {
  5874. if (this.paintFirst === 'stroke' && typeof this.shadow === 'object') {
  5875. return true;
  5876. }
  5877. return false;
  5878. },
  5879. /**
  5880. * Decide if the object should cache or not. Create its own cache level
  5881. * objectCaching is a global flag, wins over everything
  5882. * needsItsOwnCache should be used when the object drawing method requires
  5883. * a cache step. None of the fabric classes requires it.
  5884. * Generally you do not cache objects in groups because the group outside is cached.
  5885. * @return {Boolean}
  5886. */
  5887. shouldCache: function() {
  5888. this.ownCaching = this.objectCaching &&
  5889. (!this.group || this.needsItsOwnCache() || !this.group.isOnACache());
  5890. return this.ownCaching;
  5891. },
  5892. /**
  5893. * Check if this object or a child object will cast a shadow
  5894. * used by Group.shouldCache to know if child has a shadow recursively
  5895. * @return {Boolean}
  5896. */
  5897. willDrawShadow: function() {
  5898. return !!this.shadow && (this.shadow.offsetX !== 0 || this.shadow.offsetY !== 0);
  5899. },
  5900. /**
  5901. * Execute the drawing operation for an object on a specified context
  5902. * @param {CanvasRenderingContext2D} ctx Context to render on
  5903. */
  5904. drawObject: function(ctx) {
  5905. this._renderBackground(ctx);
  5906. this._setStrokeStyles(ctx, this);
  5907. this._setFillStyles(ctx, this);
  5908. this._render(ctx);
  5909. },
  5910. /**
  5911. * Paint the cached copy of the object on the target context.
  5912. * @param {CanvasRenderingContext2D} ctx Context to render on
  5913. */
  5914. drawCacheOnCanvas: function(ctx) {
  5915. ctx.scale(1 / this.zoomX, 1 / this.zoomY);
  5916. ctx.drawImage(this._cacheCanvas, -this.cacheTranslationX, -this.cacheTranslationY);
  5917. },
  5918. /**
  5919. * Check if cache is dirty
  5920. * @param {Boolean} skipCanvas skip canvas checks because this object is painted
  5921. * on parent canvas.
  5922. */
  5923. isCacheDirty: function(skipCanvas) {
  5924. if (this.isNotVisible()) {
  5925. return false;
  5926. }
  5927. if (this._cacheCanvas && !skipCanvas && this._updateCacheCanvas()) {
  5928. // in this case the context is already cleared.
  5929. return true;
  5930. }
  5931. else {
  5932. if (this.dirty || (this.statefullCache && this.hasStateChanged('cacheProperties'))) {
  5933. if (this._cacheCanvas && !skipCanvas) {
  5934. var width = this.cacheWidth / this.zoomX;
  5935. var height = this.cacheHeight / this.zoomY;
  5936. this._cacheContext.clearRect(-width / 2, -height / 2, width, height);
  5937. }
  5938. return true;
  5939. }
  5940. }
  5941. return false;
  5942. },
  5943. /**
  5944. * Draws a background for the object big as its untrasformed dimensions
  5945. * @private
  5946. * @param {CanvasRenderingContext2D} ctx Context to render on
  5947. */
  5948. _renderBackground: function(ctx) {
  5949. if (!this.backgroundColor) {
  5950. return;
  5951. }
  5952. var dim = this._getNonTransformedDimensions();
  5953. ctx.fillStyle = this.backgroundColor;
  5954. ctx.fillRect(
  5955. -dim.x / 2,
  5956. -dim.y / 2,
  5957. dim.x,
  5958. dim.y
  5959. );
  5960. // if there is background color no other shadows
  5961. // should be casted
  5962. this._removeShadow(ctx);
  5963. },
  5964. /**
  5965. * @private
  5966. * @param {CanvasRenderingContext2D} ctx Context to render on
  5967. */
  5968. _setOpacity: function(ctx) {
  5969. if (this.group && !this.group._transformDone) {
  5970. ctx.globalAlpha = this.getObjectOpacity();
  5971. }
  5972. else {
  5973. ctx.globalAlpha *= this.opacity;
  5974. }
  5975. },
  5976. _setStrokeStyles: function(ctx, decl) {
  5977. if (decl.stroke) {
  5978. ctx.lineWidth = decl.strokeWidth;
  5979. ctx.lineCap = decl.strokeLineCap;
  5980. ctx.lineJoin = decl.strokeLineJoin;
  5981. ctx.miterLimit = decl.strokeMiterLimit;
  5982. ctx.strokeStyle = decl.stroke.toLive
  5983. ? decl.stroke.toLive(ctx, this)
  5984. : decl.stroke;
  5985. }
  5986. },
  5987. _setFillStyles: function(ctx, decl) {
  5988. if (decl.fill) {
  5989. ctx.fillStyle = decl.fill.toLive
  5990. ? decl.fill.toLive(ctx, this)
  5991. : decl.fill;
  5992. }
  5993. },
  5994. /**
  5995. * @private
  5996. * Sets line dash
  5997. * @param {CanvasRenderingContext2D} ctx Context to set the dash line on
  5998. * @param {Array} dashArray array representing dashes
  5999. * @param {Function} alternative function to call if browaser does not support lineDash
  6000. */
  6001. _setLineDash: function(ctx, dashArray, alternative) {
  6002. if (!dashArray) {
  6003. return;
  6004. }
  6005. // Spec requires the concatenation of two copies the dash list when the number of elements is odd
  6006. if (1 & dashArray.length) {
  6007. dashArray.push.apply(dashArray, dashArray);
  6008. }
  6009. if (supportsLineDash) {
  6010. ctx.setLineDash(dashArray);
  6011. }
  6012. else {
  6013. alternative && alternative(ctx);
  6014. }
  6015. },
  6016. /**
  6017. * Renders controls and borders for the object
  6018. * @param {CanvasRenderingContext2D} ctx Context to render on
  6019. * @param {Object} [styleOverride] properties to override the object c_style
  6020. */
  6021. _renderControls: function(ctx, styleOverride) {
  6022. var vpt = this.getViewportTransform(),
  6023. matrix = this.calcTransformMatrix(),
  6024. options, drawBorders, drawControls;
  6025. styleOverride = styleOverride || { };
  6026. drawBorders = typeof styleOverride.hasBorders !== 'undefined' ? styleOverride.hasBorders : this.hasBorders;
  6027. drawControls = typeof styleOverride.hasControls !== 'undefined' ? styleOverride.hasControls : this.hasControls;
  6028. matrix = fabric.util.multiplyTransformMatrices(vpt, matrix);
  6029. options = fabric.util.qrDecompose(matrix);
  6030. ctx.save();
  6031. ctx.translate(options.translateX, options.translateY);
  6032. ctx.lineWidth = 1 * this.borderScaleFactor;
  6033. if (!this.group) {
  6034. ctx.globalAlpha = this.isMoving ? this.borderOpacityWhenMoving : 1;
  6035. }
  6036. if (styleOverride.forActiveSelection) {
  6037. ctx.rotate(degreesToRadians(options.angle));
  6038. drawBorders && this.drawBordersInGroup(ctx, options, styleOverride);
  6039. }
  6040. else {
  6041. ctx.rotate(degreesToRadians(this.angle));
  6042. drawBorders && this.drawBorders(ctx, styleOverride);
  6043. }
  6044. drawControls && this.drawControls(ctx, styleOverride);
  6045. ctx.restore();
  6046. },
  6047. /**
  6048. * @private
  6049. * @param {CanvasRenderingContext2D} ctx Context to render on
  6050. */
  6051. _setShadow: function(ctx) {
  6052. if (!this.shadow) {
  6053. return;
  6054. }
  6055. var multX = (this.canvas && this.canvas.viewportTransform[0]) || 1,
  6056. multY = (this.canvas && this.canvas.viewportTransform[3]) || 1,
  6057. scaling = this.getObjectScaling();
  6058. if (this.canvas && this.canvas._isRetinaScaling()) {
  6059. multX *= fabric.devicePixelRatio;
  6060. multY *= fabric.devicePixelRatio;
  6061. }
  6062. ctx.shadowColor = this.shadow.color;
  6063. ctx.shadowBlur = this.shadow.blur * fabric.browserShadowBlurConstant *
  6064. (multX + multY) * (scaling.scaleX + scaling.scaleY) / 4;
  6065. ctx.shadowOffsetX = this.shadow.offsetX * multX * scaling.scaleX;
  6066. ctx.shadowOffsetY = this.shadow.offsetY * multY * scaling.scaleY;
  6067. },
  6068. /**
  6069. * @private
  6070. * @param {CanvasRenderingContext2D} ctx Context to render on
  6071. */
  6072. _removeShadow: function(ctx) {
  6073. if (!this.shadow) {
  6074. return;
  6075. }
  6076. ctx.shadowColor = '';
  6077. ctx.shadowBlur = ctx.shadowOffsetX = ctx.shadowOffsetY = 0;
  6078. },
  6079. /**
  6080. * @private
  6081. * @param {CanvasRenderingContext2D} ctx Context to render on
  6082. * @param {Object} filler fabric.Pattern or fabric.Gradient
  6083. */
  6084. _applyPatternGradientTransform: function(ctx, filler) {
  6085. if (!filler || !filler.toLive) {
  6086. return { offsetX: 0, offsetY: 0 };
  6087. }
  6088. var t = filler.gradientTransform || filler.patternTransform;
  6089. var offsetX = -this.width / 2 + filler.offsetX || 0,
  6090. offsetY = -this.height / 2 + filler.offsetY || 0;
  6091. ctx.translate(offsetX, offsetY);
  6092. if (t) {
  6093. ctx.transform(t[0], t[1], t[2], t[3], t[4], t[5]);
  6094. }
  6095. return { offsetX: offsetX, offsetY: offsetY };
  6096. },
  6097. /**
  6098. * @private
  6099. * @param {CanvasRenderingContext2D} ctx Context to render on
  6100. */
  6101. _renderPaintInOrder: function(ctx) {
  6102. if (this.paintFirst === 'stroke') {
  6103. this._renderStroke(ctx);
  6104. this._renderFill(ctx);
  6105. }
  6106. else {
  6107. this._renderFill(ctx);
  6108. this._renderStroke(ctx);
  6109. }
  6110. },
  6111. /**
  6112. * @private
  6113. * @param {CanvasRenderingContext2D} ctx Context to render on
  6114. */
  6115. _renderFill: function(ctx) {
  6116. if (!this.fill) {
  6117. return;
  6118. }
  6119. ctx.save();
  6120. this._applyPatternGradientTransform(ctx, this.fill);
  6121. if (this.fillRule === 'evenodd') {
  6122. ctx.fill('evenodd');
  6123. }
  6124. else {
  6125. ctx.fill();
  6126. }
  6127. ctx.restore();
  6128. },
  6129. _renderStroke: function(ctx) {
  6130. if (!this.stroke || this.strokeWidth === 0) {
  6131. return;
  6132. }
  6133. if (this.shadow && !this.shadow.affectStroke) {
  6134. this._removeShadow(ctx);
  6135. }
  6136. ctx.save();
  6137. this._setLineDash(ctx, this.strokeDashArray, this._renderDashedStroke);
  6138. this._applyPatternGradientTransform(ctx, this.stroke);
  6139. ctx.stroke();
  6140. ctx.restore();
  6141. },
  6142. /**
  6143. * This function is an helper for svg import. it returns the center of the object in the svg
  6144. * untransformed coordinates
  6145. * @private
  6146. * @return {Object} center point from element coordinates
  6147. */
  6148. _findCenterFromElement: function() {
  6149. return { x: this.left + this.width / 2, y: this.top + this.height / 2 };
  6150. },
  6151. /**
  6152. * This function is an helper for svg import. it decoompose the transformMatrix
  6153. * and assign properties to object.
  6154. * untransformed coordinates
  6155. * @private
  6156. * @chainable
  6157. */
  6158. _assignTransformMatrixProps: function() {
  6159. if (this.transformMatrix) {
  6160. var options = fabric.util.qrDecompose(this.transformMatrix);
  6161. this.flipX = false;
  6162. this.flipY = false;
  6163. this.set('scaleX', options.scaleX);
  6164. this.set('scaleY', options.scaleY);
  6165. this.angle = options.angle;
  6166. this.skewX = options.skewX;
  6167. this.skewY = 0;
  6168. }
  6169. },
  6170. /**
  6171. * This function is an helper for svg import. it removes the transform matrix
  6172. * and set to object properties that fabricjs can handle
  6173. * @private
  6174. * @param {Object} preserveAspectRatioOptions
  6175. * @return {thisArg}
  6176. */
  6177. _removeTransformMatrix: function(preserveAspectRatioOptions) {
  6178. var center = this._findCenterFromElement();
  6179. if (this.transformMatrix) {
  6180. this._assignTransformMatrixProps();
  6181. center = fabric.util.transformPoint(center, this.transformMatrix);
  6182. }
  6183. this.transformMatrix = null;
  6184. if (preserveAspectRatioOptions) {
  6185. this.scaleX *= preserveAspectRatioOptions.scaleX;
  6186. this.scaleY *= preserveAspectRatioOptions.scaleY;
  6187. this.cropX = preserveAspectRatioOptions.cropX;
  6188. this.cropY = preserveAspectRatioOptions.cropY;
  6189. center.x += preserveAspectRatioOptions.offsetLeft;
  6190. center.y += preserveAspectRatioOptions.offsetTop;
  6191. this.width = preserveAspectRatioOptions.width;
  6192. this.height = preserveAspectRatioOptions.height;
  6193. }
  6194. this.setPositionByOrigin(center, 'center', 'center');
  6195. },
  6196. /**
  6197. * Clones an instance, using a callback method will work for every object.
  6198. * @param {Function} callback Callback is invoked with a clone as a first argument
  6199. * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
  6200. */
  6201. clone: function(callback, propertiesToInclude) {
  6202. var objectForm = this.toObject(propertiesToInclude);
  6203. if (this.constructor.fromObject) {
  6204. this.constructor.fromObject(objectForm, callback);
  6205. }
  6206. else {
  6207. fabric.Object._fromObject('Object', objectForm, callback);
  6208. }
  6209. },
  6210. /**
  6211. * Creates an instance of fabric.Image out of an object
  6212. * @param {Function} callback callback, invoked with an instance as a first argument
  6213. * @param {Object} [options] for clone as c_image, passed to toDataURL
  6214. * @param {Boolean} [options.enableRetinaScaling] enable retina scaling for the cloned c_image
  6215. * @return {fabric.Object} thisArg
  6216. */
  6217. cloneAsImage: function(callback, options) {
  6218. var dataUrl = this.toDataURL(options);
  6219. fabric.util.loadImage(dataUrl, function(img) {
  6220. if (callback) {
  6221. callback(new fabric.Image(img));
  6222. }
  6223. });
  6224. return this;
  6225. },
  6226. /**
  6227. * Converts an object into a data-url-like string
  6228. * @param {Object} options Options object
  6229. * @param {String} [options.format=png] The format of the output c_image. Either "jpeg" or "png"
  6230. * @param {Number} [options.quality=1] Quality level (0..1). Only used for jpeg.
  6231. * @param {Number} [options.multiplier=1] Multiplier to scale by
  6232. * @param {Number} [options.left] Cropping left offset. Introduced in v1.2.14
  6233. * @param {Number} [options.top] Cropping top offset. Introduced in v1.2.14
  6234. * @param {Number} [options.width] Cropping width. Introduced in v1.2.14
  6235. * @param {Number} [options.height] Cropping height. Introduced in v1.2.14
  6236. * @param {Boolean} [options.enableRetinaScaling] Enable retina scaling for clone c_image. Introduce in 1.6.4
  6237. * @return {String} Returns a data: URL containing a representation of the object in the format specified by options.format
  6238. */
  6239. toDataURL: function(options) {
  6240. options || (options = { });
  6241. var el = fabric.util.createCanvasElement(),
  6242. boundingRect = this.getBoundingRect();
  6243. el.width = boundingRect.width;
  6244. el.height = boundingRect.height;
  6245. fabric.util.wrapElement(el, 'div');
  6246. var canvas = new fabric.StaticCanvas(el, {
  6247. enableRetinaScaling: options.enableRetinaScaling,
  6248. renderOnAddRemove: false,
  6249. skipOffscreen: false,
  6250. });
  6251. // to avoid common confusion https://github.com/kangax/fabric.js/issues/806
  6252. if (options.format === 'jpg') {
  6253. options.format = 'jpeg';
  6254. }
  6255. if (options.format === 'jpeg') {
  6256. canvas.backgroundColor = '#fff';
  6257. }
  6258. var origParams = {
  6259. left: this.left,
  6260. top: this.top
  6261. };
  6262. this.setPositionByOrigin(new fabric.Point(canvas.width / 2, canvas.height / 2), 'center', 'center');
  6263. var originalCanvas = this.canvas;
  6264. canvas.add(this);
  6265. var data = canvas.toDataURL(options);
  6266. this.set(origParams).setCoords();
  6267. this.canvas = originalCanvas;
  6268. // canvas.dispose will call c_image.dispose that will nullify the elements
  6269. // since this canvas is a simple element for the process, we remove references
  6270. // to objects in this way in order to avoid object trashing.
  6271. canvas._objects = [];
  6272. canvas.dispose();
  6273. canvas = null;
  6274. return data;
  6275. },
  6276. /**
  6277. * Returns true if specified type is identical to the type of an instance
  6278. * @param {String} type Type to check against
  6279. * @return {Boolean}
  6280. */
  6281. isType: function(type) {
  6282. return this.type === type;
  6283. },
  6284. /**
  6285. * Returns complexity of an instance
  6286. * @return {Number} complexity of this instance (is 1 unless subclassed)
  6287. */
  6288. complexity: function() {
  6289. return 1;
  6290. },
  6291. /**
  6292. * Returns a JSON representation of an instance
  6293. * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
  6294. * @return {Object} JSON
  6295. */
  6296. toJSON: function(propertiesToInclude) {
  6297. // delegate, not alias
  6298. return this.toObject(propertiesToInclude);
  6299. },
  6300. /**
  6301. * Sets gradient (fill or stroke) of an object
  6302. * <b>Backwards incompatibility note:</b> This method was named "setGradientFill" until v1.1.0
  6303. * @param {String} property Property name 'stroke' or 'fill'
  6304. * @param {Object} [options] Options object
  6305. * @param {String} [options.type] Type of gradient 'radial' or 'linear'
  6306. * @param {Number} [options.x1=0] x-coordinate of start point
  6307. * @param {Number} [options.y1=0] y-coordinate of start point
  6308. * @param {Number} [options.x2=0] x-coordinate of end point
  6309. * @param {Number} [options.y2=0] y-coordinate of end point
  6310. * @param {Number} [options.r1=0] Radius of start point (only for radial gradients)
  6311. * @param {Number} [options.r2=0] Radius of end point (only for radial gradients)
  6312. * @param {Object} [options.colorStops] Color stops object eg. {0: 'ff0000', 1: '000000'}
  6313. * @param {Object} [options.gradientTransform] transforMatrix for gradient
  6314. * @return {fabric.Object} thisArg
  6315. * @chainable
  6316. * @see {@link http://jsfiddle.net/fabricjs/58y8b/|jsFiddle demo}
  6317. * @example <caption>Set linear gradient</caption>
  6318. * object.setGradient('fill', {
  6319. * type: 'linear',
  6320. * x1: -object.width / 2,
  6321. * y1: 0,
  6322. * x2: object.width / 2,
  6323. * y2: 0,
  6324. * colorStops: {
  6325. * 0: 'red',
  6326. * 0.5: '#005555',
  6327. * 1: 'rgba(0,0,255,0.5)'
  6328. * }
  6329. * });
  6330. * canvas.renderAll();
  6331. * @example <caption>Set radial gradient</caption>
  6332. * object.setGradient('fill', {
  6333. * type: 'radial',
  6334. * x1: 0,
  6335. * y1: 0,
  6336. * x2: 0,
  6337. * y2: 0,
  6338. * r1: object.width / 2,
  6339. * r2: 10,
  6340. * colorStops: {
  6341. * 0: 'red',
  6342. * 0.5: '#005555',
  6343. * 1: 'rgba(0,0,255,0.5)'
  6344. * }
  6345. * });
  6346. * canvas.renderAll();
  6347. */
  6348. setGradient: function(property, options) {
  6349. options || (options = { });
  6350. var gradient = { colorStops: [] };
  6351. gradient.type = options.type || (options.r1 || options.r2 ? 'radial' : 'linear');
  6352. gradient.coords = {
  6353. x1: options.x1,
  6354. y1: options.y1,
  6355. x2: options.x2,
  6356. y2: options.y2
  6357. };
  6358. if (options.r1 || options.r2) {
  6359. gradient.coords.r1 = options.r1;
  6360. gradient.coords.r2 = options.r2;
  6361. }
  6362. gradient.gradientTransform = options.gradientTransform;
  6363. fabric.Gradient.prototype.addColorStop.call(gradient, options.colorStops);
  6364. return this.set(property, fabric.Gradient.forObject(this, gradient));
  6365. },
  6366. /**
  6367. * Sets pattern fill of an object
  6368. * @param {Object} options Options object
  6369. * @param {(String|HTMLImageElement)} options.source Pattern source
  6370. * @param {String} [options.repeat=repeat] Repeat property of a pattern (one of repeat, repeat-x, repeat-y or no-repeat)
  6371. * @param {Number} [options.offsetX=0] Pattern horizontal offset from object's left/top corner
  6372. * @param {Number} [options.offsetY=0] Pattern vertical offset from object's left/top corner
  6373. * @return {fabric.Object} thisArg
  6374. * @chainable
  6375. * @see {@link http://jsfiddle.net/fabricjs/QT3pa/|jsFiddle demo}
  6376. * @example <caption>Set pattern</caption>
  6377. * fabric.util.loadImage('http://fabricjs.com/assets/escheresque_ste.png', function(img) {
  6378. * object.setPatternFill({
  6379. * source: img,
  6380. * repeat: 'repeat'
  6381. * });
  6382. * canvas.renderAll();
  6383. * });
  6384. */
  6385. setPatternFill: function(options) {
  6386. return this.set('fill', new fabric.Pattern(options));
  6387. },
  6388. /**
  6389. * Sets {@link fabric.Object#shadow|shadow} of an object
  6390. * @param {Object|String} [options] Options object or string (e.g. "2px 2px 10px rgba(0,0,0,0.2)")
  6391. * @param {String} [options.color=rgb(0,0,0)] Shadow color
  6392. * @param {Number} [options.blur=0] Shadow blur
  6393. * @param {Number} [options.offsetX=0] Shadow horizontal offset
  6394. * @param {Number} [options.offsetY=0] Shadow vertical offset
  6395. * @return {fabric.Object} thisArg
  6396. * @chainable
  6397. * @see {@link http://jsfiddle.net/fabricjs/7gvJG/|jsFiddle demo}
  6398. * @example <caption>Set shadow with string notation</caption>
  6399. * object.setShadow('2px 2px 10px rgba(0,0,0,0.2)');
  6400. * canvas.renderAll();
  6401. * @example <caption>Set shadow with object notation</caption>
  6402. * object.setShadow({
  6403. * color: 'red',
  6404. * blur: 10,
  6405. * offsetX: 20,
  6406. * offsetY: 20
  6407. * });
  6408. * canvas.renderAll();
  6409. */
  6410. setShadow: function(options) {
  6411. return this.set('shadow', options ? new fabric.Shadow(options) : null);
  6412. },
  6413. /**
  6414. * Sets "color" of an instance (alias of `set('fill', &hellip;)`)
  6415. * @param {String} color Color value
  6416. * @return {fabric.Object} thisArg
  6417. * @chainable
  6418. */
  6419. setColor: function(color) {
  6420. this.set('fill', color);
  6421. return this;
  6422. },
  6423. /**
  6424. * Sets "angle" of an instance with centered rotation
  6425. * @param {Number} angle Angle value (in degrees)
  6426. * @return {fabric.Object} thisArg
  6427. * @chainable
  6428. */
  6429. rotate: function(angle) {
  6430. var shouldCenterOrigin = (this.originX !== 'center' || this.originY !== 'center') && this.centeredRotation;
  6431. if (shouldCenterOrigin) {
  6432. this._setOriginToCenter();
  6433. }
  6434. this.set('angle', angle);
  6435. if (shouldCenterOrigin) {
  6436. this._resetOrigin();
  6437. }
  6438. return this;
  6439. },
  6440. /**
  6441. * Centers object horizontally on canvas to which it was added last.
  6442. * You might need to call `setCoords` on an object after centering, to update controls area.
  6443. * @return {fabric.Object} thisArg
  6444. * @chainable
  6445. */
  6446. centerH: function () {
  6447. this.canvas && this.canvas.centerObjectH(this);
  6448. return this;
  6449. },
  6450. /**
  6451. * Centers object horizontally on current viewport of canvas to which it was added last.
  6452. * You might need to call `setCoords` on an object after centering, to update controls area.
  6453. * @return {fabric.Object} thisArg
  6454. * @chainable
  6455. */
  6456. viewportCenterH: function () {
  6457. this.canvas && this.canvas.viewportCenterObjectH(this);
  6458. return this;
  6459. },
  6460. /**
  6461. * Centers object vertically on canvas to which it was added last.
  6462. * You might need to call `setCoords` on an object after centering, to update controls area.
  6463. * @return {fabric.Object} thisArg
  6464. * @chainable
  6465. */
  6466. centerV: function () {
  6467. this.canvas && this.canvas.centerObjectV(this);
  6468. return this;
  6469. },
  6470. /**
  6471. * Centers object vertically on current viewport of canvas to which it was added last.
  6472. * You might need to call `setCoords` on an object after centering, to update controls area.
  6473. * @return {fabric.Object} thisArg
  6474. * @chainable
  6475. */
  6476. viewportCenterV: function () {
  6477. this.canvas && this.canvas.viewportCenterObjectV(this);
  6478. return this;
  6479. },
  6480. /**
  6481. * Centers object vertically and horizontally on canvas to which is was added last
  6482. * You might need to call `setCoords` on an object after centering, to update controls area.
  6483. * @return {fabric.Object} thisArg
  6484. * @chainable
  6485. */
  6486. center: function () {
  6487. this.canvas && this.canvas.centerObject(this);
  6488. return this;
  6489. },
  6490. /**
  6491. * Centers object on current viewport of canvas to which it was added last.
  6492. * You might need to call `setCoords` on an object after centering, to update controls area.
  6493. * @return {fabric.Object} thisArg
  6494. * @chainable
  6495. */
  6496. viewportCenter: function () {
  6497. this.canvas && this.canvas.viewportCenterObject(this);
  6498. return this;
  6499. },
  6500. /**
  6501. * Returns coordinates of a pointer relative to an object
  6502. * @param {Event} e Event to operate upon
  6503. * @param {Object} [pointer] Pointer to operate upon (instead of event)
  6504. * @return {Object} Coordinates of a pointer (x, y)
  6505. */
  6506. getLocalPointer: function(e, pointer) {
  6507. pointer = pointer || this.canvas.getPointer(e);
  6508. var pClicked = new fabric.Point(pointer.x, pointer.y),
  6509. objectLeftTop = this._getLeftTopCoords();
  6510. if (this.angle) {
  6511. pClicked = fabric.util.rotatePoint(
  6512. pClicked, objectLeftTop, degreesToRadians(-this.angle));
  6513. }
  6514. return {
  6515. x: pClicked.x - objectLeftTop.x,
  6516. y: pClicked.y - objectLeftTop.y
  6517. };
  6518. },
  6519. /**
  6520. * Sets canvas globalCompositeOperation for specific object
  6521. * custom composition operation for the particular object can be specifed using globalCompositeOperation property
  6522. * @param {CanvasRenderingContext2D} ctx Rendering canvas context
  6523. */
  6524. _setupCompositeOperation: function (ctx) {
  6525. if (this.globalCompositeOperation) {
  6526. ctx.globalCompositeOperation = this.globalCompositeOperation;
  6527. }
  6528. }
  6529. });
  6530. fabric.util.createAccessors && fabric.util.createAccessors(fabric.Object);
  6531. extend(fabric.Object.prototype, fabric.Observable);
  6532. /**
  6533. * Defines the number of fraction digits to use when serializing object values.
  6534. * You can use it to increase/decrease precision of such values like left, top, scaleX, scaleY, etc.
  6535. * @static
  6536. * @memberOf fabric.Object
  6537. * @constant
  6538. * @type Number
  6539. */
  6540. fabric.Object.NUM_FRACTION_DIGITS = 2;
  6541. fabric.Object._fromObject = function(className, object, callback, extraParam) {
  6542. var klass = fabric[className];
  6543. object = clone(object, true);
  6544. fabric.util.enlivenPatterns([object.fill, object.stroke], function(patterns) {
  6545. if (typeof patterns[0] !== 'undefined') {
  6546. object.fill = patterns[0];
  6547. }
  6548. if (typeof patterns[1] !== 'undefined') {
  6549. object.stroke = patterns[1];
  6550. }
  6551. var instance = extraParam ? new klass(object[extraParam], object) : new klass(object);
  6552. callback && callback(instance);
  6553. });
  6554. };
  6555. /**
  6556. * Unique id used internally when creating SVG elements
  6557. * @static
  6558. * @memberOf fabric.Object
  6559. * @type Number
  6560. */
  6561. fabric.Object.__uid = 0;
  6562. })(typeof exports !== 'undefined' ? exports : this);
  6563. (function() {
  6564. var degreesToRadians = fabric.util.degreesToRadians,
  6565. originXOffset = {
  6566. left: -0.5,
  6567. center: 0,
  6568. right: 0.5
  6569. },
  6570. originYOffset = {
  6571. top: -0.5,
  6572. center: 0,
  6573. bottom: 0.5
  6574. };
  6575. fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prototype */ {
  6576. /**
  6577. * Translates the coordinates from a set of origin to another (based on the object's dimensions)
  6578. * @param {fabric.Point} point The point which corresponds to the originX and originY params
  6579. * @param {String} fromOriginX Horizontal origin: 'left', 'center' or 'right'
  6580. * @param {String} fromOriginY Vertical origin: 'top', 'center' or 'bottom'
  6581. * @param {String} toOriginX Horizontal origin: 'left', 'center' or 'right'
  6582. * @param {String} toOriginY Vertical origin: 'top', 'center' or 'bottom'
  6583. * @return {fabric.Point}
  6584. */
  6585. translateToGivenOrigin: function(point, fromOriginX, fromOriginY, toOriginX, toOriginY) {
  6586. var x = point.x,
  6587. y = point.y,
  6588. offsetX, offsetY, dim;
  6589. if (typeof fromOriginX === 'string') {
  6590. fromOriginX = originXOffset[fromOriginX];
  6591. }
  6592. else {
  6593. fromOriginX -= 0.5;
  6594. }
  6595. if (typeof toOriginX === 'string') {
  6596. toOriginX = originXOffset[toOriginX];
  6597. }
  6598. else {
  6599. toOriginX -= 0.5;
  6600. }
  6601. offsetX = toOriginX - fromOriginX;
  6602. if (typeof fromOriginY === 'string') {
  6603. fromOriginY = originYOffset[fromOriginY];
  6604. }
  6605. else {
  6606. fromOriginY -= 0.5;
  6607. }
  6608. if (typeof toOriginY === 'string') {
  6609. toOriginY = originYOffset[toOriginY];
  6610. }
  6611. else {
  6612. toOriginY -= 0.5;
  6613. }
  6614. offsetY = toOriginY - fromOriginY;
  6615. if (offsetX || offsetY) {
  6616. dim = this._getTransformedDimensions();
  6617. x = point.x + offsetX * dim.x;
  6618. y = point.y + offsetY * dim.y;
  6619. }
  6620. return new fabric.Point(x, y);
  6621. },
  6622. /**
  6623. * Translates the coordinates from origin to center coordinates (based on the object's dimensions)
  6624. * @param {fabric.Point} point The point which corresponds to the originX and originY params
  6625. * @param {String} originX Horizontal origin: 'left', 'center' or 'right'
  6626. * @param {String} originY Vertical origin: 'top', 'center' or 'bottom'
  6627. * @return {fabric.Point}
  6628. */
  6629. translateToCenterPoint: function(point, originX, originY) {
  6630. var p = this.translateToGivenOrigin(point, originX, originY, 'center', 'center');
  6631. if (this.angle) {
  6632. return fabric.util.rotatePoint(p, point, degreesToRadians(this.angle));
  6633. }
  6634. return p;
  6635. },
  6636. /**
  6637. * Translates the coordinates from center to origin coordinates (based on the object's dimensions)
  6638. * @param {fabric.Point} center The point which corresponds to center of the object
  6639. * @param {String} originX Horizontal origin: 'left', 'center' or 'right'
  6640. * @param {String} originY Vertical origin: 'top', 'center' or 'bottom'
  6641. * @return {fabric.Point}
  6642. */
  6643. translateToOriginPoint: function(center, originX, originY) {
  6644. var p = this.translateToGivenOrigin(center, 'center', 'center', originX, originY);
  6645. if (this.angle) {
  6646. return fabric.util.rotatePoint(p, center, degreesToRadians(this.angle));
  6647. }
  6648. return p;
  6649. },
  6650. /**
  6651. * Returns the real center coordinates of the object
  6652. * @return {fabric.Point}
  6653. */
  6654. getCenterPoint: function() {
  6655. var leftTop = new fabric.Point(this.left, this.top);
  6656. return this.translateToCenterPoint(leftTop, this.originX, this.originY);
  6657. },
  6658. /**
  6659. * Returns the coordinates of the object based on center coordinates
  6660. * @param {fabric.Point} point The point which corresponds to the originX and originY params
  6661. * @return {fabric.Point}
  6662. */
  6663. // getOriginPoint: function(center) {
  6664. // return this.translateToOriginPoint(center, this.originX, this.originY);
  6665. // },
  6666. /**
  6667. * Returns the coordinates of the object as if it has a different origin
  6668. * @param {String} originX Horizontal origin: 'left', 'center' or 'right'
  6669. * @param {String} originY Vertical origin: 'top', 'center' or 'bottom'
  6670. * @return {fabric.Point}
  6671. */
  6672. getPointByOrigin: function(originX, originY) {
  6673. var center = this.getCenterPoint();
  6674. return this.translateToOriginPoint(center, originX, originY);
  6675. },
  6676. /**
  6677. * Returns the point in local coordinates
  6678. * @param {fabric.Point} point The point relative to the global coordinate system
  6679. * @param {String} originX Horizontal origin: 'left', 'center' or 'right'
  6680. * @param {String} originY Vertical origin: 'top', 'center' or 'bottom'
  6681. * @return {fabric.Point}
  6682. */
  6683. toLocalPoint: function(point, originX, originY) {
  6684. var center = this.getCenterPoint(),
  6685. p, p2;
  6686. if (typeof originX !== 'undefined' && typeof originY !== 'undefined' ) {
  6687. p = this.translateToGivenOrigin(center, 'center', 'center', originX, originY);
  6688. }
  6689. else {
  6690. p = new fabric.Point(this.left, this.top);
  6691. }
  6692. p2 = new fabric.Point(point.x, point.y);
  6693. if (this.angle) {
  6694. p2 = fabric.util.rotatePoint(p2, center, -degreesToRadians(this.angle));
  6695. }
  6696. return p2.subtractEquals(p);
  6697. },
  6698. /**
  6699. * Returns the point in global coordinates
  6700. * @param {fabric.Point} The point relative to the local coordinate system
  6701. * @return {fabric.Point}
  6702. */
  6703. // toGlobalPoint: function(point) {
  6704. // return fabric.util.rotatePoint(point, this.getCenterPoint(), degreesToRadians(this.angle)).addEquals(new fabric.Point(this.left, this.top));
  6705. // },
  6706. /**
  6707. * Sets the position of the object taking into consideration the object's origin
  6708. * @param {fabric.Point} pos The new position of the object
  6709. * @param {String} originX Horizontal origin: 'left', 'center' or 'right'
  6710. * @param {String} originY Vertical origin: 'top', 'center' or 'bottom'
  6711. * @return {void}
  6712. */
  6713. setPositionByOrigin: function(pos, originX, originY) {
  6714. var center = this.translateToCenterPoint(pos, originX, originY),
  6715. position = this.translateToOriginPoint(center, this.originX, this.originY);
  6716. this.set('left', position.x);
  6717. this.set('top', position.y);
  6718. },
  6719. /**
  6720. * @param {String} to One of 'left', 'center', 'right'
  6721. */
  6722. adjustPosition: function(to) {
  6723. var angle = degreesToRadians(this.angle),
  6724. hypotFull = this.getScaledWidth(),
  6725. xFull = fabric.util.cos(angle) * hypotFull,
  6726. yFull = fabric.util.sin(angle) * hypotFull,
  6727. offsetFrom, offsetTo;
  6728. //TODO: this function does not consider mixed situation like top, center.
  6729. if (typeof this.originX === 'string') {
  6730. offsetFrom = originXOffset[this.originX];
  6731. }
  6732. else {
  6733. offsetFrom = this.originX - 0.5;
  6734. }
  6735. if (typeof to === 'string') {
  6736. offsetTo = originXOffset[to];
  6737. }
  6738. else {
  6739. offsetTo = to - 0.5;
  6740. }
  6741. this.left += xFull * (offsetTo - offsetFrom);
  6742. this.top += yFull * (offsetTo - offsetFrom);
  6743. this.setCoords();
  6744. this.originX = to;
  6745. },
  6746. /**
  6747. * Sets the origin/position of the object to it's center point
  6748. * @private
  6749. * @return {void}
  6750. */
  6751. _setOriginToCenter: function() {
  6752. this._originalOriginX = this.originX;
  6753. this._originalOriginY = this.originY;
  6754. var center = this.getCenterPoint();
  6755. this.originX = 'center';
  6756. this.originY = 'center';
  6757. this.left = center.x;
  6758. this.top = center.y;
  6759. },
  6760. /**
  6761. * Resets the origin/position of the object to it's original origin
  6762. * @private
  6763. * @return {void}
  6764. */
  6765. _resetOrigin: function() {
  6766. var originPoint = this.translateToOriginPoint(
  6767. this.getCenterPoint(),
  6768. this._originalOriginX,
  6769. this._originalOriginY);
  6770. this.originX = this._originalOriginX;
  6771. this.originY = this._originalOriginY;
  6772. this.left = originPoint.x;
  6773. this.top = originPoint.y;
  6774. this._originalOriginX = null;
  6775. this._originalOriginY = null;
  6776. },
  6777. /**
  6778. * @private
  6779. */
  6780. _getLeftTopCoords: function() {
  6781. return this.translateToOriginPoint(this.getCenterPoint(), 'left', 'top');
  6782. },
  6783. });
  6784. })();
  6785. (function() {
  6786. function getCoords(coords) {
  6787. return [
  6788. new fabric.Point(coords.tl.x, coords.tl.y),
  6789. new fabric.Point(coords.tr.x, coords.tr.y),
  6790. new fabric.Point(coords.br.x, coords.br.y),
  6791. new fabric.Point(coords.bl.x, coords.bl.y)
  6792. ];
  6793. }
  6794. var degreesToRadians = fabric.util.degreesToRadians,
  6795. multiplyMatrices = fabric.util.multiplyTransformMatrices,
  6796. transformPoint = fabric.util.transformPoint;
  6797. fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prototype */ {
  6798. /**
  6799. * Describe object's corner position in canvas element coordinates.
  6800. * properties are tl,mt,tr,ml,mr,bl,mb,br,mtr for the main controls.
  6801. * each property is an object with x, y and corner.
  6802. * The `corner` property contains in a similar manner the 4 points of the
  6803. * interactive area of the corner.
  6804. * The coordinates depends from this properties: width, height, scaleX, scaleY
  6805. * skewX, skewY, angle, strokeWidth, viewportTransform, top, left, padding.
  6806. * The coordinates get updated with @method setCoords.
  6807. * You can calculate them without updating with @method calcCoords;
  6808. * @memberOf fabric.Object.prototype
  6809. */
  6810. oCoords: null,
  6811. /**
  6812. * Describe object's corner position in canvas object absolute coordinates
  6813. * properties are tl,tr,bl,br and describe the four main corner.
  6814. * each property is an object with x, y, instance of Fabric.Point.
  6815. * The coordinates depends from this properties: width, height, scaleX, scaleY
  6816. * skewX, skewY, angle, strokeWidth, top, left.
  6817. * Those coordinates are usefull to understand where an object is. They get updated
  6818. * with oCoords but they do not need to be updated when zoom or panning change.
  6819. * The coordinates get updated with @method setCoords.
  6820. * You can calculate them without updating with @method calcCoords(true);
  6821. * @memberOf fabric.Object.prototype
  6822. */
  6823. aCoords: null,
  6824. /**
  6825. * storage for object transform matrix
  6826. */
  6827. ownMatrixCache: null,
  6828. /**
  6829. * storage for object full transform matrix
  6830. */
  6831. matrixCache: null,
  6832. /**
  6833. * return correct set of coordinates for intersection
  6834. */
  6835. getCoords: function(absolute, calculate) {
  6836. if (!this.oCoords) {
  6837. this.setCoords();
  6838. }
  6839. var coords = absolute ? this.aCoords : this.oCoords;
  6840. return getCoords(calculate ? this.calcCoords(absolute) : coords);
  6841. },
  6842. /**
  6843. * Checks if object intersects with an area formed by 2 points
  6844. * @param {Object} pointTL top-left point of area
  6845. * @param {Object} pointBR bottom-right point of area
  6846. * @param {Boolean} [absolute] use coordinates without viewportTransform
  6847. * @param {Boolean} [calculate] use coordinates of current position instead of .oCoords
  6848. * @return {Boolean} true if object intersects with an area formed by 2 points
  6849. */
  6850. intersectsWithRect: function(pointTL, pointBR, absolute, calculate) {
  6851. var coords = this.getCoords(absolute, calculate),
  6852. intersection = fabric.Intersection.intersectPolygonRectangle(
  6853. coords,
  6854. pointTL,
  6855. pointBR
  6856. );
  6857. return intersection.status === 'Intersection';
  6858. },
  6859. /**
  6860. * Checks if object intersects with another object
  6861. * @param {Object} other Object to test
  6862. * @param {Boolean} [absolute] use coordinates without viewportTransform
  6863. * @param {Boolean} [calculate] use coordinates of current position instead of .oCoords
  6864. * @return {Boolean} true if object intersects with another object
  6865. */
  6866. intersectsWithObject: function(other, absolute, calculate) {
  6867. var intersection = fabric.Intersection.intersectPolygonPolygon(
  6868. this.getCoords(absolute, calculate),
  6869. other.getCoords(absolute, calculate)
  6870. );
  6871. return intersection.status === 'Intersection'
  6872. || other.isContainedWithinObject(this, absolute, calculate)
  6873. || this.isContainedWithinObject(other, absolute, calculate);
  6874. },
  6875. /**
  6876. * Checks if object is fully contained within area of another object
  6877. * @param {Object} other Object to test
  6878. * @param {Boolean} [absolute] use coordinates without viewportTransform
  6879. * @param {Boolean} [calculate] use coordinates of current position instead of .oCoords
  6880. * @return {Boolean} true if object is fully contained within area of another object
  6881. */
  6882. isContainedWithinObject: function(other, absolute, calculate) {
  6883. var points = this.getCoords(absolute, calculate),
  6884. i = 0, lines = other._getImageLines(
  6885. calculate ? other.calcCoords(absolute) : absolute ? other.aCoords : other.oCoords
  6886. );
  6887. for (; i < 4; i++) {
  6888. if (!other.containsPoint(points[i], lines)) {
  6889. return false;
  6890. }
  6891. }
  6892. return true;
  6893. },
  6894. /**
  6895. * Checks if object is fully contained within area formed by 2 points
  6896. * @param {Object} pointTL top-left point of area
  6897. * @param {Object} pointBR bottom-right point of area
  6898. * @param {Boolean} [absolute] use coordinates without viewportTransform
  6899. * @param {Boolean} [calculate] use coordinates of current position instead of .oCoords
  6900. * @return {Boolean} true if object is fully contained within area formed by 2 points
  6901. */
  6902. isContainedWithinRect: function(pointTL, pointBR, absolute, calculate) {
  6903. var boundingRect = this.getBoundingRect(absolute, calculate);
  6904. return (
  6905. boundingRect.left >= pointTL.x &&
  6906. boundingRect.left + boundingRect.width <= pointBR.x &&
  6907. boundingRect.top >= pointTL.y &&
  6908. boundingRect.top + boundingRect.height <= pointBR.y
  6909. );
  6910. },
  6911. /**
  6912. * Checks if point is inside the object
  6913. * @param {fabric.Point} point Point to check against
  6914. * @param {Object} [lines] object returned from @method _getImageLines
  6915. * @param {Boolean} [absolute] use coordinates without viewportTransform
  6916. * @param {Boolean} [calculate] use coordinates of current position instead of .oCoords
  6917. * @return {Boolean} true if point is inside the object
  6918. */
  6919. containsPoint: function(point, lines, absolute, calculate) {
  6920. var lines = lines || this._getImageLines(
  6921. calculate ? this.calcCoords(absolute) : absolute ? this.aCoords : this.oCoords
  6922. ),
  6923. xPoints = this._findCrossPoints(point, lines);
  6924. // if xPoints is odd then point is inside the object
  6925. return (xPoints !== 0 && xPoints % 2 === 1);
  6926. },
  6927. /**
  6928. * Checks if object is contained within the canvas with current viewportTransform
  6929. * the check is done stopping at first point that appears on screen
  6930. * @param {Boolean} [calculate] use coordinates of current position instead of .oCoords
  6931. * @return {Boolean} true if object is fully or partially contained within canvas
  6932. */
  6933. isOnScreen: function(calculate) {
  6934. if (!this.canvas) {
  6935. return false;
  6936. }
  6937. var pointTL = this.canvas.vptCoords.tl, pointBR = this.canvas.vptCoords.br;
  6938. var points = this.getCoords(true, calculate), point;
  6939. for (var i = 0; i < 4; i++) {
  6940. point = points[i];
  6941. if (point.x <= pointBR.x && point.x >= pointTL.x && point.y <= pointBR.y && point.y >= pointTL.y) {
  6942. return true;
  6943. }
  6944. }
  6945. // no points on screen, check intersection with absolute coordinates
  6946. if (this.intersectsWithRect(pointTL, pointBR, true, calculate)) {
  6947. return true;
  6948. }
  6949. return this._containsCenterOfCanvas(pointTL, pointBR, calculate);
  6950. },
  6951. /**
  6952. * Checks if the object contains the midpoint between canvas extremities
  6953. * Does not make sense outside the context of isOnScreen and isPartiallyOnScreen
  6954. * @private
  6955. * @param {Fabric.Point} pointTL Top Left point
  6956. * @param {Fabric.Point} pointBR Top Right point
  6957. * @param {Boolean} calculate use coordinates of current position instead of .oCoords
  6958. * @return {Boolean} true if the objects containe the point
  6959. */
  6960. _containsCenterOfCanvas: function(pointTL, pointBR, calculate) {
  6961. // worst case scenario the object is so big that contains the screen
  6962. var centerPoint = { x: (pointTL.x + pointBR.x) / 2, y: (pointTL.y + pointBR.y) / 2 };
  6963. if (this.containsPoint(centerPoint, null, true, calculate)) {
  6964. return true;
  6965. }
  6966. return false;
  6967. },
  6968. /**
  6969. * Checks if object is partially contained within the canvas with current viewportTransform
  6970. * @param {Boolean} [calculate] use coordinates of current position instead of .oCoords
  6971. * @return {Boolean} true if object is partially contained within canvas
  6972. */
  6973. isPartiallyOnScreen: function(calculate) {
  6974. if (!this.canvas) {
  6975. return false;
  6976. }
  6977. var pointTL = this.canvas.vptCoords.tl, pointBR = this.canvas.vptCoords.br;
  6978. if (this.intersectsWithRect(pointTL, pointBR, true, calculate)) {
  6979. return true;
  6980. }
  6981. return this._containsCenterOfCanvas(pointTL, pointBR, calculate);
  6982. },
  6983. /**
  6984. * Method that returns an object with the object edges in it, given the coordinates of the corners
  6985. * @private
  6986. * @param {Object} oCoords Coordinates of the object corners
  6987. */
  6988. _getImageLines: function(oCoords) {
  6989. return {
  6990. topline: {
  6991. o: oCoords.tl,
  6992. d: oCoords.tr
  6993. },
  6994. rightline: {
  6995. o: oCoords.tr,
  6996. d: oCoords.br
  6997. },
  6998. bottomline: {
  6999. o: oCoords.br,
  7000. d: oCoords.bl
  7001. },
  7002. leftline: {
  7003. o: oCoords.bl,
  7004. d: oCoords.tl
  7005. }
  7006. };
  7007. },
  7008. /**
  7009. * Helper method to determine how many cross points are between the 4 object edges
  7010. * and the horizontal line determined by a point on canvas
  7011. * @private
  7012. * @param {fabric.Point} point Point to check
  7013. * @param {Object} lines Coordinates of the object being evaluated
  7014. */
  7015. // remove yi, not used but left code here just in case.
  7016. _findCrossPoints: function(point, lines) {
  7017. var b1, b2, a1, a2, xi, // yi,
  7018. xcount = 0,
  7019. iLine;
  7020. for (var lineKey in lines) {
  7021. iLine = lines[lineKey];
  7022. // optimisation 1: line below point. no cross
  7023. if ((iLine.o.y < point.y) && (iLine.d.y < point.y)) {
  7024. continue;
  7025. }
  7026. // optimisation 2: line above point. no cross
  7027. if ((iLine.o.y >= point.y) && (iLine.d.y >= point.y)) {
  7028. continue;
  7029. }
  7030. // optimisation 3: vertical line case
  7031. if ((iLine.o.x === iLine.d.x) && (iLine.o.x >= point.x)) {
  7032. xi = iLine.o.x;
  7033. // yi = point.y;
  7034. }
  7035. // calculate the intersection point
  7036. else {
  7037. b1 = 0;
  7038. b2 = (iLine.d.y - iLine.o.y) / (iLine.d.x - iLine.o.x);
  7039. a1 = point.y - b1 * point.x;
  7040. a2 = iLine.o.y - b2 * iLine.o.x;
  7041. xi = -(a1 - a2) / (b1 - b2);
  7042. // yi = a1 + b1 * xi;
  7043. }
  7044. // dont count xi < point.x cases
  7045. if (xi >= point.x) {
  7046. xcount += 1;
  7047. }
  7048. // optimisation 4: specific for square images
  7049. if (xcount === 2) {
  7050. break;
  7051. }
  7052. }
  7053. return xcount;
  7054. },
  7055. /**
  7056. * Returns coordinates of object's bounding rectangle (left, top, width, height)
  7057. * the box is intented as aligned to axis of canvas.
  7058. * @param {Boolean} [absolute] use coordinates without viewportTransform
  7059. * @param {Boolean} [calculate] use coordinates of current position instead of .oCoords / .aCoords
  7060. * @return {Object} Object with left, top, width, height properties
  7061. */
  7062. getBoundingRect: function(absolute, calculate) {
  7063. var coords = this.getCoords(absolute, calculate);
  7064. return fabric.util.makeBoundingBoxFromPoints(coords);
  7065. },
  7066. /**
  7067. * Returns width of an object bounding box counting transformations
  7068. * before 2.0 it was named getWidth();
  7069. * @return {Number} width value
  7070. */
  7071. getScaledWidth: function() {
  7072. return this._getTransformedDimensions().x;
  7073. },
  7074. /**
  7075. * Returns height of an object bounding box counting transformations
  7076. * before 2.0 it was named getHeight();
  7077. * @return {Number} height value
  7078. */
  7079. getScaledHeight: function() {
  7080. return this._getTransformedDimensions().y;
  7081. },
  7082. /**
  7083. * Makes sure the scale is valid and modifies it if necessary
  7084. * @private
  7085. * @param {Number} value
  7086. * @return {Number}
  7087. */
  7088. _constrainScale: function(value) {
  7089. if (Math.abs(value) < this.minScaleLimit) {
  7090. if (value < 0) {
  7091. return -this.minScaleLimit;
  7092. }
  7093. else {
  7094. return this.minScaleLimit;
  7095. }
  7096. }
  7097. else if (value === 0) {
  7098. return 0.0001;
  7099. }
  7100. return value;
  7101. },
  7102. /**
  7103. * Scales an object (equally by x and y)
  7104. * @param {Number} value Scale factor
  7105. * @return {fabric.Object} thisArg
  7106. * @chainable
  7107. */
  7108. scale: function(value) {
  7109. this._set('scaleX', value);
  7110. this._set('scaleY', value);
  7111. return this.setCoords();
  7112. },
  7113. /**
  7114. * Scales an object to a given width, with respect to bounding box (scaling by x/y equally)
  7115. * @param {Number} value New width value
  7116. * @param {Boolean} absolute ignore viewport
  7117. * @return {fabric.Object} thisArg
  7118. * @chainable
  7119. */
  7120. scaleToWidth: function(value, absolute) {
  7121. // adjust to bounding rect factor so that rotated shapes would fit as well
  7122. var boundingRectFactor = this.getBoundingRect(absolute).width / this.getScaledWidth();
  7123. return this.scale(value / this.width / boundingRectFactor);
  7124. },
  7125. /**
  7126. * Scales an object to a given height, with respect to bounding box (scaling by x/y equally)
  7127. * @param {Number} value New height value
  7128. * @param {Boolean} absolute ignore viewport
  7129. * @return {fabric.Object} thisArg
  7130. * @chainable
  7131. */
  7132. scaleToHeight: function(value, absolute) {
  7133. // adjust to bounding rect factor so that rotated shapes would fit as well
  7134. var boundingRectFactor = this.getBoundingRect(absolute).height / this.getScaledHeight();
  7135. return this.scale(value / this.height / boundingRectFactor);
  7136. },
  7137. /**
  7138. * Calculate and returns the .coords of an object.
  7139. * @return {Object} Object with tl, tr, br, bl ....
  7140. * @chainable
  7141. */
  7142. calcCoords: function(absolute) {
  7143. var rotateMatrix = this._calcRotateMatrix(),
  7144. translateMatrix = this._calcTranslateMatrix(),
  7145. startMatrix = multiplyMatrices(translateMatrix, rotateMatrix),
  7146. vpt = this.getViewportTransform(),
  7147. finalMatrix = absolute ? startMatrix : multiplyMatrices(vpt, startMatrix),
  7148. dim = this._getTransformedDimensions(),
  7149. w = dim.x / 2, h = dim.y / 2,
  7150. tl = transformPoint({ x: -w, y: -h }, finalMatrix),
  7151. tr = transformPoint({ x: w, y: -h }, finalMatrix),
  7152. bl = transformPoint({ x: -w, y: h }, finalMatrix),
  7153. br = transformPoint({ x: w, y: h }, finalMatrix);
  7154. if (!absolute) {
  7155. var padding = this.padding, angle = degreesToRadians(this.angle),
  7156. cos = fabric.util.cos(angle), sin = fabric.util.sin(angle),
  7157. cosP = cos * padding, sinP = sin * padding, cosPSinP = cosP + sinP,
  7158. cosPMinusSinP = cosP - sinP;
  7159. if (padding) {
  7160. tl.x -= cosPMinusSinP;
  7161. tl.y -= cosPSinP;
  7162. tr.x += cosPSinP;
  7163. tr.y -= cosPMinusSinP;
  7164. bl.x -= cosPSinP;
  7165. bl.y += cosPMinusSinP;
  7166. br.x += cosPMinusSinP;
  7167. br.y += cosPSinP;
  7168. }
  7169. var ml = new fabric.Point((tl.x + bl.x) / 2, (tl.y + bl.y) / 2),
  7170. mt = new fabric.Point((tr.x + tl.x) / 2, (tr.y + tl.y) / 2),
  7171. mr = new fabric.Point((br.x + tr.x) / 2, (br.y + tr.y) / 2),
  7172. mb = new fabric.Point((br.x + bl.x) / 2, (br.y + bl.y) / 2),
  7173. mtr = new fabric.Point(mt.x + sin * this.rotatingPointOffset, mt.y - cos * this.rotatingPointOffset);
  7174. }
  7175. // if (!absolute) {
  7176. // var canvas = this.canvas;
  7177. // setTimeout(function() {
  7178. // canvas.contextTop.clearRect(0, 0, 700, 700);
  7179. // canvas.contextTop.fillStyle = 'green';
  7180. // canvas.contextTop.fillRect(mb.x, mb.y, 3, 3);
  7181. // canvas.contextTop.fillRect(bl.x, bl.y, 3, 3);
  7182. // canvas.contextTop.fillRect(br.x, br.y, 3, 3);
  7183. // canvas.contextTop.fillRect(tl.x, tl.y, 3, 3);
  7184. // canvas.contextTop.fillRect(tr.x, tr.y, 3, 3);
  7185. // canvas.contextTop.fillRect(ml.x, ml.y, 3, 3);
  7186. // canvas.contextTop.fillRect(mr.x, mr.y, 3, 3);
  7187. // canvas.contextTop.fillRect(mt.x, mt.y, 3, 3);
  7188. // canvas.contextTop.fillRect(mtr.x, mtr.y, 3, 3);
  7189. // }, 50);
  7190. // }
  7191. var coords = {
  7192. // corners
  7193. tl: tl, tr: tr, br: br, bl: bl,
  7194. };
  7195. if (!absolute) {
  7196. // middle
  7197. coords.ml = ml;
  7198. coords.mt = mt;
  7199. coords.mr = mr;
  7200. coords.mb = mb;
  7201. // rotating point
  7202. coords.mtr = mtr;
  7203. }
  7204. return coords;
  7205. },
  7206. /**
  7207. * Sets corner position coordinates based on current angle, width and height
  7208. * See https://github.com/kangax/fabric.js/wiki/When-to-call-setCoords
  7209. * @param {Boolean} [ignoreZoom] set oCoords with or without the viewport transform.
  7210. * @param {Boolean} [skipAbsolute] skip calculation of aCoords, usefull in setViewportTransform
  7211. * @return {fabric.Object} thisArg
  7212. * @chainable
  7213. */
  7214. setCoords: function(ignoreZoom, skipAbsolute) {
  7215. this.oCoords = this.calcCoords(ignoreZoom);
  7216. if (!skipAbsolute) {
  7217. this.aCoords = this.calcCoords(true);
  7218. }
  7219. // set coordinates of the draggable boxes in the corners used to scale/rotate the c_image
  7220. ignoreZoom || (this._setCornerCoords && this._setCornerCoords());
  7221. return this;
  7222. },
  7223. /**
  7224. * calculate rotation matrix of an object
  7225. * @return {Array} rotation matrix for the object
  7226. */
  7227. _calcRotateMatrix: function() {
  7228. if (this.angle) {
  7229. var theta = degreesToRadians(this.angle), cos = fabric.util.cos(theta), sin = fabric.util.sin(theta);
  7230. return [cos, sin, -sin, cos, 0, 0];
  7231. }
  7232. return fabric.iMatrix.concat();
  7233. },
  7234. /**
  7235. * calculate the translation matrix for an object transform
  7236. * @return {Array} rotation matrix for the object
  7237. */
  7238. _calcTranslateMatrix: function() {
  7239. var center = this.getCenterPoint();
  7240. return [1, 0, 0, 1, center.x, center.y];
  7241. },
  7242. transformMatrixKey: function(skipGroup) {
  7243. var sep = '_', prefix = '';
  7244. if (!skipGroup && this.group) {
  7245. prefix = this.group.transformMatrixKey(skipGroup) + sep;
  7246. };
  7247. return prefix + this.top + sep + this.left + sep + this.scaleX + sep + this.scaleY +
  7248. sep + this.skewX + sep + this.skewY + sep + this.angle + sep + this.originX + sep + this.originY +
  7249. sep + this.width + sep + this.height + sep + this.strokeWidth + this.flipX + this.flipY;
  7250. },
  7251. /**
  7252. * calculate trasform Matrix that represent current transformation from
  7253. * object properties.
  7254. * @param {Boolean} [skipGroup] return transformMatrix for object and not go upward with parents
  7255. * @return {Array} matrix Transform Matrix for the object
  7256. */
  7257. calcTransformMatrix: function(skipGroup) {
  7258. if (skipGroup) {
  7259. return this.calcOwnMatrix();
  7260. }
  7261. var key = this.transformMatrixKey(), cache = this.matrixCache || (this.matrixCache = {});
  7262. if (cache.key === key) {
  7263. return cache.value;
  7264. }
  7265. var matrix = this.calcOwnMatrix();
  7266. if (this.group) {
  7267. matrix = multiplyMatrices(this.group.calcTransformMatrix(), matrix);
  7268. }
  7269. cache.key = key;
  7270. cache.value = matrix;
  7271. return matrix;
  7272. },
  7273. calcOwnMatrix: function() {
  7274. var key = this.transformMatrixKey(true), cache = this.ownMatrixCache || (this.ownMatrixCache = {});
  7275. if (cache.key === key) {
  7276. return cache.value;
  7277. }
  7278. var matrix = this._calcTranslateMatrix(),
  7279. rotateMatrix,
  7280. dimensionMatrix = this._calcDimensionsTransformMatrix(this.skewX, this.skewY, true);
  7281. if (this.angle) {
  7282. rotateMatrix = this._calcRotateMatrix();
  7283. matrix = multiplyMatrices(matrix, rotateMatrix);
  7284. }
  7285. matrix = multiplyMatrices(matrix, dimensionMatrix);
  7286. cache.key = key;
  7287. cache.value = matrix;
  7288. return matrix;
  7289. },
  7290. _calcDimensionsTransformMatrix: function(skewX, skewY, flipping) {
  7291. var skewMatrix,
  7292. scaleX = this.scaleX * (flipping && this.flipX ? -1 : 1),
  7293. scaleY = this.scaleY * (flipping && this.flipY ? -1 : 1),
  7294. scaleMatrix = [scaleX, 0, 0, scaleY, 0, 0];
  7295. if (skewX) {
  7296. skewMatrix = [1, 0, Math.tan(degreesToRadians(skewX)), 1];
  7297. scaleMatrix = multiplyMatrices(scaleMatrix, skewMatrix, true);
  7298. }
  7299. if (skewY) {
  7300. skewMatrix = [1, Math.tan(degreesToRadians(skewY)), 0, 1];
  7301. scaleMatrix = multiplyMatrices(scaleMatrix, skewMatrix, true);
  7302. }
  7303. return scaleMatrix;
  7304. },
  7305. /*
  7306. * Calculate object dimensions from its properties
  7307. * @private
  7308. * @return {Object} .x width dimension
  7309. * @return {Object} .y height dimension
  7310. */
  7311. _getNonTransformedDimensions: function() {
  7312. var strokeWidth = this.strokeWidth,
  7313. w = this.width + strokeWidth,
  7314. h = this.height + strokeWidth;
  7315. return { x: w, y: h };
  7316. },
  7317. /*
  7318. * Calculate object bounding boxdimensions from its properties scale, skew.
  7319. * @private
  7320. * @return {Object} .x width dimension
  7321. * @return {Object} .y height dimension
  7322. */
  7323. _getTransformedDimensions: function(skewX, skewY) {
  7324. if (typeof skewX === 'undefined') {
  7325. skewX = this.skewX;
  7326. }
  7327. if (typeof skewY === 'undefined') {
  7328. skewY = this.skewY;
  7329. }
  7330. var dimensions = this._getNonTransformedDimensions();
  7331. if (skewX === 0 && skewY === 0) {
  7332. return { x: dimensions.x * this.scaleX, y: dimensions.y * this.scaleY };
  7333. }
  7334. var dimX = dimensions.x / 2, dimY = dimensions.y / 2,
  7335. points = [
  7336. {
  7337. x: -dimX,
  7338. y: -dimY
  7339. },
  7340. {
  7341. x: dimX,
  7342. y: -dimY
  7343. },
  7344. {
  7345. x: -dimX,
  7346. y: dimY
  7347. },
  7348. {
  7349. x: dimX,
  7350. y: dimY
  7351. }],
  7352. i, transformMatrix = this._calcDimensionsTransformMatrix(skewX, skewY, false),
  7353. bbox;
  7354. for (i = 0; i < points.length; i++) {
  7355. points[i] = fabric.util.transformPoint(points[i], transformMatrix);
  7356. }
  7357. bbox = fabric.util.makeBoundingBoxFromPoints(points);
  7358. return { x: bbox.width, y: bbox.height };
  7359. },
  7360. /*
  7361. * Calculate object dimensions for controls. include padding and canvas zoom
  7362. * private
  7363. */
  7364. _calculateCurrentDimensions: function() {
  7365. var vpt = this.getViewportTransform(),
  7366. dim = this._getTransformedDimensions(),
  7367. p = fabric.util.transformPoint(dim, vpt, true);
  7368. return p.scalarAdd(2 * this.padding);
  7369. },
  7370. });
  7371. })();
  7372. fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prototype */ {
  7373. /**
  7374. * Moves an object to the bottom of the stack of drawn objects
  7375. * @return {fabric.Object} thisArg
  7376. * @chainable
  7377. */
  7378. sendToBack: function() {
  7379. if (this.group) {
  7380. fabric.StaticCanvas.prototype.sendToBack.call(this.group, this);
  7381. }
  7382. else {
  7383. this.canvas.sendToBack(this);
  7384. }
  7385. return this;
  7386. },
  7387. /**
  7388. * Moves an object to the top of the stack of drawn objects
  7389. * @return {fabric.Object} thisArg
  7390. * @chainable
  7391. */
  7392. bringToFront: function() {
  7393. if (this.group) {
  7394. fabric.StaticCanvas.prototype.bringToFront.call(this.group, this);
  7395. }
  7396. else {
  7397. this.canvas.bringToFront(this);
  7398. }
  7399. return this;
  7400. },
  7401. /**
  7402. * Moves an object down in stack of drawn objects
  7403. * @param {Boolean} [intersecting] If `true`, send object behind next lower intersecting object
  7404. * @return {fabric.Object} thisArg
  7405. * @chainable
  7406. */
  7407. sendBackwards: function(intersecting) {
  7408. if (this.group) {
  7409. fabric.StaticCanvas.prototype.sendBackwards.call(this.group, this, intersecting);
  7410. }
  7411. else {
  7412. this.canvas.sendBackwards(this, intersecting);
  7413. }
  7414. return this;
  7415. },
  7416. /**
  7417. * Moves an object up in stack of drawn objects
  7418. * @param {Boolean} [intersecting] If `true`, send object in front of next upper intersecting object
  7419. * @return {fabric.Object} thisArg
  7420. * @chainable
  7421. */
  7422. bringForward: function(intersecting) {
  7423. if (this.group) {
  7424. fabric.StaticCanvas.prototype.bringForward.call(this.group, this, intersecting);
  7425. }
  7426. else {
  7427. this.canvas.bringForward(this, intersecting);
  7428. }
  7429. return this;
  7430. },
  7431. /**
  7432. * Moves an object to specified level in stack of drawn objects
  7433. * @param {Number} index New position of object
  7434. * @return {fabric.Object} thisArg
  7435. * @chainable
  7436. */
  7437. moveTo: function(index) {
  7438. if (this.group && this.group.type !== 'activeSelection') {
  7439. fabric.StaticCanvas.prototype.moveTo.call(this.group, this, index);
  7440. }
  7441. else {
  7442. this.canvas.moveTo(this, index);
  7443. }
  7444. return this;
  7445. }
  7446. });
  7447. /* _TO_SVG_START_ */
  7448. (function() {
  7449. function getSvgColorString(prop, value) {
  7450. if (!value) {
  7451. return prop + ': none; ';
  7452. }
  7453. else if (value.toLive) {
  7454. return prop + ': url(#SVGID_' + value.id + '); ';
  7455. }
  7456. else {
  7457. var color = new fabric.Color(value),
  7458. str = prop + ': ' + color.toRgb() + '; ',
  7459. opacity = color.getAlpha();
  7460. if (opacity !== 1) {
  7461. //change the color in rgb + opacity
  7462. str += prop + '-opacity: ' + opacity.toString() + '; ';
  7463. }
  7464. return str;
  7465. }
  7466. }
  7467. var toFixed = fabric.util.toFixed;
  7468. fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prototype */ {
  7469. /**
  7470. * Returns styles-string for svg-export
  7471. * @param {Boolean} skipShadow a boolean to skip shadow filter output
  7472. * @return {String}
  7473. */
  7474. getSvgStyles: function(skipShadow) {
  7475. var fillRule = this.fillRule,
  7476. strokeWidth = this.strokeWidth ? this.strokeWidth : '0',
  7477. strokeDashArray = this.strokeDashArray ? this.strokeDashArray.join(' ') : 'none',
  7478. strokeLineCap = this.strokeLineCap ? this.strokeLineCap : 'butt',
  7479. strokeLineJoin = this.strokeLineJoin ? this.strokeLineJoin : 'miter',
  7480. strokeMiterLimit = this.strokeMiterLimit ? this.strokeMiterLimit : '4',
  7481. opacity = typeof this.opacity !== 'undefined' ? this.opacity : '1',
  7482. visibility = this.visible ? '' : ' visibility: hidden;',
  7483. filter = skipShadow ? '' : this.getSvgFilter(),
  7484. fill = getSvgColorString('fill', this.fill),
  7485. stroke = getSvgColorString('stroke', this.stroke);
  7486. return [
  7487. stroke,
  7488. 'stroke-width: ', strokeWidth, '; ',
  7489. 'stroke-dasharray: ', strokeDashArray, '; ',
  7490. 'stroke-linecap: ', strokeLineCap, '; ',
  7491. 'stroke-linejoin: ', strokeLineJoin, '; ',
  7492. 'stroke-miterlimit: ', strokeMiterLimit, '; ',
  7493. fill,
  7494. 'fill-rule: ', fillRule, '; ',
  7495. 'opacity: ', opacity, ';',
  7496. filter,
  7497. visibility
  7498. ].join('');
  7499. },
  7500. /**
  7501. * Returns styles-string for svg-export
  7502. * @param {Object} style the object from which to retrieve c_style properties
  7503. * @param {Boolean} useWhiteSpace a boolean to include an additional attribute in the c_style.
  7504. * @return {String}
  7505. */
  7506. getSvgSpanStyles: function(style, useWhiteSpace) {
  7507. var term = '; ';
  7508. var fontFamily = style.fontFamily ?
  7509. 'font-family: ' + (((style.fontFamily.indexOf('\'') === -1 && style.fontFamily.indexOf('"') === -1) ?
  7510. '\'' + style.fontFamily + '\'' : style.fontFamily)) + term : '';
  7511. var strokeWidth = style.strokeWidth ? 'stroke-width: ' + style.strokeWidth + term : '',
  7512. fontFamily = fontFamily,
  7513. fontSize = style.fontSize ? 'font-size: ' + style.fontSize + 'px' + term : '',
  7514. fontStyle = style.fontStyle ? 'font-c_style: ' + style.fontStyle + term : '',
  7515. fontWeight = style.fontWeight ? 'font-weight: ' + style.fontWeight + term : '',
  7516. fill = style.fill ? getSvgColorString('fill', style.fill) : '',
  7517. stroke = style.stroke ? getSvgColorString('stroke', style.stroke) : '',
  7518. textDecoration = this.getSvgTextDecoration(style),
  7519. deltaY = style.deltaY ? 'baseline-shift: ' + (-style.deltaY) + '; ' : '';
  7520. if (textDecoration) {
  7521. textDecoration = 'text-decoration: ' + textDecoration + term;
  7522. }
  7523. return [
  7524. stroke,
  7525. strokeWidth,
  7526. fontFamily,
  7527. fontSize,
  7528. fontStyle,
  7529. fontWeight,
  7530. textDecoration,
  7531. fill,
  7532. deltaY,
  7533. useWhiteSpace ? 'white-space: pre; ' : ''
  7534. ].join('');
  7535. },
  7536. /**
  7537. * Returns text-decoration property for svg-export
  7538. * @param {Object} style the object from which to retrieve c_style properties
  7539. * @return {String}
  7540. */
  7541. getSvgTextDecoration: function(style) {
  7542. if ('overline' in style || 'underline' in style || 'linethrough' in style) {
  7543. return (style.overline ? 'overline ' : '') +
  7544. (style.underline ? 'underline ' : '') + (style.linethrough ? 'line-through ' : '');
  7545. }
  7546. return '';
  7547. },
  7548. /**
  7549. * Returns filter for svg shadow
  7550. * @return {String}
  7551. */
  7552. getSvgFilter: function() {
  7553. return this.shadow ? 'filter: url(#SVGID_' + this.shadow.id + ');' : '';
  7554. },
  7555. /**
  7556. * Returns id attribute for svg output
  7557. * @return {String}
  7558. */
  7559. getSvgId: function() {
  7560. return this.id ? 'id="' + this.id + '" ' : '';
  7561. },
  7562. /**
  7563. * Returns transform-string for svg-export
  7564. * @return {String}
  7565. */
  7566. getSvgTransform: function() {
  7567. var angle = this.angle,
  7568. skewX = (this.skewX % 360),
  7569. skewY = (this.skewY % 360),
  7570. center = this.getCenterPoint(),
  7571. NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS,
  7572. translatePart = 'translate(' +
  7573. toFixed(center.x, NUM_FRACTION_DIGITS) +
  7574. ' ' +
  7575. toFixed(center.y, NUM_FRACTION_DIGITS) +
  7576. ')',
  7577. anglePart = angle !== 0
  7578. ? (' rotate(' + toFixed(angle, NUM_FRACTION_DIGITS) + ')')
  7579. : '',
  7580. scalePart = (this.scaleX === 1 && this.scaleY === 1)
  7581. ? '' :
  7582. (' scale(' +
  7583. toFixed(this.scaleX, NUM_FRACTION_DIGITS) +
  7584. ' ' +
  7585. toFixed(this.scaleY, NUM_FRACTION_DIGITS) +
  7586. ')'),
  7587. skewXPart = skewX !== 0 ? ' skewX(' + toFixed(skewX, NUM_FRACTION_DIGITS) + ')' : '',
  7588. skewYPart = skewY !== 0 ? ' skewY(' + toFixed(skewY, NUM_FRACTION_DIGITS) + ')' : '',
  7589. flipXPart = this.flipX ? ' matrix(-1 0 0 1 0 0) ' : '',
  7590. flipYPart = this.flipY ? ' matrix(1 0 0 -1 0 0)' : '';
  7591. return [
  7592. translatePart, anglePart, scalePart, flipXPart, flipYPart, skewXPart, skewYPart
  7593. ].join('');
  7594. },
  7595. /**
  7596. * Returns transform-string for svg-export from the transform matrix of single elements
  7597. * @return {String}
  7598. */
  7599. getSvgTransformMatrix: function() {
  7600. return this.transformMatrix ? ' matrix(' + this.transformMatrix.join(' ') + ') ' : '';
  7601. },
  7602. _setSVGBg: function(textBgRects) {
  7603. if (this.backgroundColor) {
  7604. var NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS;
  7605. textBgRects.push(
  7606. '\t\t<rect ',
  7607. this._getFillAttributes(this.backgroundColor),
  7608. ' x="',
  7609. toFixed(-this.width / 2, NUM_FRACTION_DIGITS),
  7610. '" y="',
  7611. toFixed(-this.height / 2, NUM_FRACTION_DIGITS),
  7612. '" width="',
  7613. toFixed(this.width, NUM_FRACTION_DIGITS),
  7614. '" height="',
  7615. toFixed(this.height, NUM_FRACTION_DIGITS),
  7616. '"></rect>\n');
  7617. }
  7618. },
  7619. /**
  7620. * @private
  7621. */
  7622. _createBaseSVGMarkup: function() {
  7623. var markup = [];
  7624. if (this.fill && this.fill.toLive) {
  7625. markup.push(this.fill.toSVG(this, false));
  7626. }
  7627. if (this.stroke && this.stroke.toLive) {
  7628. markup.push(this.stroke.toSVG(this, false));
  7629. }
  7630. if (this.shadow) {
  7631. markup.push(this.shadow.toSVG(this));
  7632. }
  7633. return markup;
  7634. },
  7635. addPaintOrder: function() {
  7636. return this.paintFirst !== 'fill' ? ' paint-order="' + this.paintFirst + '" ' : '';
  7637. }
  7638. });
  7639. })();
  7640. /* _TO_SVG_END_ */
  7641. (function() {
  7642. var extend = fabric.util.object.extend,
  7643. originalSet = 'stateProperties';
  7644. /*
  7645. Depends on `stateProperties`
  7646. */
  7647. function saveProps(origin, destination, props) {
  7648. var tmpObj = { }, deep = true;
  7649. props.forEach(function(prop) {
  7650. tmpObj[prop] = origin[prop];
  7651. });
  7652. extend(origin[destination], tmpObj, deep);
  7653. }
  7654. function _isEqual(origValue, currentValue, firstPass) {
  7655. if (origValue === currentValue) {
  7656. // if the objects are identical, return
  7657. return true;
  7658. }
  7659. else if (Array.isArray(origValue)) {
  7660. if (!Array.isArray(currentValue) || origValue.length !== currentValue.length) {
  7661. return false;
  7662. }
  7663. for (var i = 0, len = origValue.length; i < len; i++) {
  7664. if (!_isEqual(origValue[i], currentValue[i])) {
  7665. return false;
  7666. }
  7667. }
  7668. return true;
  7669. }
  7670. else if (origValue && typeof origValue === 'object') {
  7671. var keys = Object.keys(origValue), key;
  7672. if (!currentValue ||
  7673. typeof currentValue !== 'object' ||
  7674. (!firstPass && keys.length !== Object.keys(currentValue).length)
  7675. ) {
  7676. return false;
  7677. }
  7678. for (var i = 0, len = keys.length; i < len; i++) {
  7679. key = keys[i];
  7680. if (!_isEqual(origValue[key], currentValue[key])) {
  7681. return false;
  7682. }
  7683. }
  7684. return true;
  7685. }
  7686. }
  7687. fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prototype */ {
  7688. /**
  7689. * Returns true if object state (one of its state properties) was changed
  7690. * @param {String} [propertySet] optional name for the set of property we want to save
  7691. * @return {Boolean} true if instance' state has changed since `{@link fabric.Object#saveState}` was called
  7692. */
  7693. hasStateChanged: function(propertySet) {
  7694. propertySet = propertySet || originalSet;
  7695. var dashedPropertySet = '_' + propertySet;
  7696. if (Object.keys(this[dashedPropertySet]).length < this[propertySet].length) {
  7697. return true;
  7698. }
  7699. return !_isEqual(this[dashedPropertySet], this, true);
  7700. },
  7701. /**
  7702. * Saves state of an object
  7703. * @param {Object} [options] Object with additional `stateProperties` array to include when saving state
  7704. * @return {fabric.Object} thisArg
  7705. */
  7706. saveState: function(options) {
  7707. var propertySet = options && options.propertySet || originalSet,
  7708. destination = '_' + propertySet;
  7709. if (!this[destination]) {
  7710. return this.setupState(options);
  7711. }
  7712. saveProps(this, destination, this[propertySet]);
  7713. if (options && options.stateProperties) {
  7714. saveProps(this, destination, options.stateProperties);
  7715. }
  7716. return this;
  7717. },
  7718. /**
  7719. * Setups state of an object
  7720. * @param {Object} [options] Object with additional `stateProperties` array to include when saving state
  7721. * @return {fabric.Object} thisArg
  7722. */
  7723. setupState: function(options) {
  7724. options = options || { };
  7725. var propertySet = options.propertySet || originalSet;
  7726. options.propertySet = propertySet;
  7727. this['_' + propertySet] = { };
  7728. this.saveState(options);
  7729. return this;
  7730. }
  7731. });
  7732. })();
  7733. (function(global) {
  7734. 'use strict';
  7735. var fabric = global.fabric || (global.fabric = { }),
  7736. extend = fabric.util.object.extend,
  7737. clone = fabric.util.object.clone,
  7738. coordProps = { x1: 1, x2: 1, y1: 1, y2: 1 },
  7739. supportsLineDash = fabric.StaticCanvas.supports('setLineDash');
  7740. if (fabric.Line) {
  7741. fabric.warn('fabric.Line is already defined');
  7742. return;
  7743. }
  7744. /**
  7745. * Line class
  7746. * @class fabric.Line
  7747. * @extends fabric.Object
  7748. * @see {@link fabric.Line#initialize} for constructor definition
  7749. */
  7750. fabric.Line = fabric.util.createClass(fabric.Object, /** @lends fabric.Line.prototype */ {
  7751. /**
  7752. * Type of an object
  7753. * @type String
  7754. * @default
  7755. */
  7756. type: 'line',
  7757. /**
  7758. * x value or first line edge
  7759. * @type Number
  7760. * @default
  7761. */
  7762. x1: 0,
  7763. /**
  7764. * y value or first line edge
  7765. * @type Number
  7766. * @default
  7767. */
  7768. y1: 0,
  7769. /**
  7770. * x value or second line edge
  7771. * @type Number
  7772. * @default
  7773. */
  7774. x2: 0,
  7775. /**
  7776. * y value or second line edge
  7777. * @type Number
  7778. * @default
  7779. */
  7780. y2: 0,
  7781. cacheProperties: fabric.Object.prototype.cacheProperties.concat('x1', 'x2', 'y1', 'y2'),
  7782. /**
  7783. * Constructor
  7784. * @param {Array} [points] Array of points
  7785. * @param {Object} [options] Options object
  7786. * @return {fabric.Line} thisArg
  7787. */
  7788. initialize: function(points, options) {
  7789. if (!points) {
  7790. points = [0, 0, 0, 0];
  7791. }
  7792. this.callSuper('initialize', options);
  7793. this.set('x1', points[0]);
  7794. this.set('y1', points[1]);
  7795. this.set('x2', points[2]);
  7796. this.set('y2', points[3]);
  7797. this._setWidthHeight(options);
  7798. },
  7799. /**
  7800. * @private
  7801. * @param {Object} [options] Options
  7802. */
  7803. _setWidthHeight: function(options) {
  7804. options || (options = { });
  7805. this.width = Math.abs(this.x2 - this.x1);
  7806. this.height = Math.abs(this.y2 - this.y1);
  7807. this.left = 'left' in options
  7808. ? options.left
  7809. : this._getLeftToOriginX();
  7810. this.top = 'top' in options
  7811. ? options.top
  7812. : this._getTopToOriginY();
  7813. },
  7814. /**
  7815. * @private
  7816. * @param {String} key
  7817. * @param {*} value
  7818. */
  7819. _set: function(key, value) {
  7820. this.callSuper('_set', key, value);
  7821. if (typeof coordProps[key] !== 'undefined') {
  7822. this._setWidthHeight();
  7823. }
  7824. return this;
  7825. },
  7826. /**
  7827. * @private
  7828. * @return {Number} leftToOriginX Distance from left edge of canvas to originX of Line.
  7829. */
  7830. _getLeftToOriginX: makeEdgeToOriginGetter(
  7831. { // property names
  7832. origin: 'originX',
  7833. axis1: 'x1',
  7834. axis2: 'x2',
  7835. dimension: 'width'
  7836. },
  7837. { // possible values of origin
  7838. nearest: 'left',
  7839. center: 'center',
  7840. farthest: 'right'
  7841. }
  7842. ),
  7843. /**
  7844. * @private
  7845. * @return {Number} topToOriginY Distance from top edge of canvas to originY of Line.
  7846. */
  7847. _getTopToOriginY: makeEdgeToOriginGetter(
  7848. { // property names
  7849. origin: 'originY',
  7850. axis1: 'y1',
  7851. axis2: 'y2',
  7852. dimension: 'height'
  7853. },
  7854. { // possible values of origin
  7855. nearest: 'top',
  7856. center: 'center',
  7857. farthest: 'bottom'
  7858. }
  7859. ),
  7860. /**
  7861. * @private
  7862. * @param {CanvasRenderingContext2D} ctx Context to render on
  7863. */
  7864. _render: function(ctx) {
  7865. ctx.beginPath();
  7866. if (!this.strokeDashArray || this.strokeDashArray && supportsLineDash) {
  7867. // move from center (of virtual box) to its left/top corner
  7868. // we can't assume x1, y1 is top left and x2, y2 is bottom right
  7869. var p = this.calcLinePoints();
  7870. ctx.moveTo(p.x1, p.y1);
  7871. ctx.lineTo(p.x2, p.y2);
  7872. }
  7873. ctx.lineWidth = this.strokeWidth;
  7874. // TODO: test this
  7875. // make sure setting "fill" changes color of a line
  7876. // (by copying fillStyle to strokeStyle, since line is stroked, not filled)
  7877. var origStrokeStyle = ctx.strokeStyle;
  7878. ctx.strokeStyle = this.stroke || ctx.fillStyle;
  7879. this.stroke && this._renderStroke(ctx);
  7880. ctx.strokeStyle = origStrokeStyle;
  7881. },
  7882. /**
  7883. * @private
  7884. * @param {CanvasRenderingContext2D} ctx Context to render on
  7885. */
  7886. _renderDashedStroke: function(ctx) {
  7887. var p = this.calcLinePoints();
  7888. ctx.beginPath();
  7889. fabric.util.drawDashedLine(ctx, p.x1, p.y1, p.x2, p.y2, this.strokeDashArray);
  7890. ctx.closePath();
  7891. },
  7892. /**
  7893. * This function is an helper for svg import. it returns the center of the object in the svg
  7894. * untransformed coordinates
  7895. * @private
  7896. * @return {Object} center point from element coordinates
  7897. */
  7898. _findCenterFromElement: function() {
  7899. return {
  7900. x: (this.x1 + this.x2) / 2,
  7901. y: (this.y1 + this.y2) / 2,
  7902. };
  7903. },
  7904. /**
  7905. * Returns object representation of an instance
  7906. * @methd toObject
  7907. * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
  7908. * @return {Object} object representation of an instance
  7909. */
  7910. toObject: function(propertiesToInclude) {
  7911. return extend(this.callSuper('toObject', propertiesToInclude), this.calcLinePoints());
  7912. },
  7913. /*
  7914. * Calculate object dimensions from its properties
  7915. * @private
  7916. */
  7917. _getNonTransformedDimensions: function() {
  7918. var dim = this.callSuper('_getNonTransformedDimensions');
  7919. if (this.strokeLineCap === 'butt') {
  7920. if (this.width === 0) {
  7921. dim.y -= this.strokeWidth;
  7922. }
  7923. if (this.height === 0) {
  7924. dim.x -= this.strokeWidth;
  7925. }
  7926. }
  7927. return dim;
  7928. },
  7929. /**
  7930. * Recalculates line points given width and height
  7931. * @private
  7932. */
  7933. calcLinePoints: function() {
  7934. var xMult = this.x1 <= this.x2 ? -1 : 1,
  7935. yMult = this.y1 <= this.y2 ? -1 : 1,
  7936. x1 = (xMult * this.width * 0.5),
  7937. y1 = (yMult * this.height * 0.5),
  7938. x2 = (xMult * this.width * -0.5),
  7939. y2 = (yMult * this.height * -0.5);
  7940. return {
  7941. x1: x1,
  7942. x2: x2,
  7943. y1: y1,
  7944. y2: y2
  7945. };
  7946. },
  7947. /* _TO_SVG_START_ */
  7948. /**
  7949. * Returns SVG representation of an instance
  7950. * @param {Function} [reviver] Method for further parsing of svg representation.
  7951. * @return {String} svg representation of an instance
  7952. */
  7953. toSVG: function(reviver) {
  7954. var markup = this._createBaseSVGMarkup(),
  7955. p = this.calcLinePoints();
  7956. markup.push(
  7957. '<line ', this.getSvgId(),
  7958. 'x1="', p.x1,
  7959. '" y1="', p.y1,
  7960. '" x2="', p.x2,
  7961. '" y2="', p.y2,
  7962. '" c_style="', this.getSvgStyles(),
  7963. '" transform="', this.getSvgTransform(),
  7964. this.getSvgTransformMatrix(),
  7965. '"/>\n'
  7966. );
  7967. return reviver ? reviver(markup.join('')) : markup.join('');
  7968. },
  7969. /* _TO_SVG_END_ */
  7970. });
  7971. /**
  7972. * Returns fabric.Line instance from an object representation
  7973. * @static
  7974. * @memberOf fabric.Line
  7975. * @param {Object} object Object to create an instance from
  7976. * @param {function} [callback] invoked with new instance as first argument
  7977. */
  7978. fabric.Line.fromObject = function(object, callback) {
  7979. function _callback(instance) {
  7980. delete instance.points;
  7981. callback && callback(instance);
  7982. };
  7983. var options = clone(object, true);
  7984. options.points = [object.x1, object.y1, object.x2, object.y2];
  7985. fabric.Object._fromObject('Line', options, _callback, 'points');
  7986. };
  7987. /**
  7988. * Produces a function that calculates distance from canvas edge to Line origin.
  7989. */
  7990. function makeEdgeToOriginGetter(propertyNames, originValues) {
  7991. var origin = propertyNames.origin,
  7992. axis1 = propertyNames.axis1,
  7993. axis2 = propertyNames.axis2,
  7994. dimension = propertyNames.dimension,
  7995. nearest = originValues.nearest,
  7996. center = originValues.center,
  7997. farthest = originValues.farthest;
  7998. return function() {
  7999. switch (this.get(origin)) {
  8000. case nearest:
  8001. return Math.min(this.get(axis1), this.get(axis2));
  8002. case center:
  8003. return Math.min(this.get(axis1), this.get(axis2)) + (0.5 * this.get(dimension));
  8004. case farthest:
  8005. return Math.max(this.get(axis1), this.get(axis2));
  8006. }
  8007. };
  8008. }
  8009. })(typeof exports !== 'undefined' ? exports : this);
  8010. (function(global) {
  8011. 'use strict';
  8012. var fabric = global.fabric || (global.fabric = { }),
  8013. pi = Math.PI;
  8014. if (fabric.Circle) {
  8015. fabric.warn('fabric.Circle is already defined.');
  8016. return;
  8017. }
  8018. /**
  8019. * Circle class
  8020. * @class fabric.Circle
  8021. * @extends fabric.Object
  8022. * @see {@link fabric.Circle#initialize} for constructor definition
  8023. */
  8024. fabric.Circle = fabric.util.createClass(fabric.Object, /** @lends fabric.Circle.prototype */ {
  8025. /**
  8026. * Type of an object
  8027. * @type String
  8028. * @default
  8029. */
  8030. type: 'circle',
  8031. /**
  8032. * Radius of this circle
  8033. * @type Number
  8034. * @default
  8035. */
  8036. radius: 0,
  8037. /**
  8038. * Start angle of the circle, moving clockwise
  8039. * deprectated type, this should be in degree, this was an oversight.
  8040. * probably will change to degrees in next major version
  8041. * @type Number
  8042. * @default 0
  8043. */
  8044. startAngle: 0,
  8045. /**
  8046. * End angle of the circle
  8047. * deprectated type, this should be in degree, this was an oversight.
  8048. * probably will change to degrees in next major version
  8049. * @type Number
  8050. * @default 2Pi
  8051. */
  8052. endAngle: pi * 2,
  8053. cacheProperties: fabric.Object.prototype.cacheProperties.concat('radius', 'startAngle', 'endAngle'),
  8054. /**
  8055. * @private
  8056. * @param {String} key
  8057. * @param {*} value
  8058. * @return {fabric.Circle} thisArg
  8059. */
  8060. _set: function(key, value) {
  8061. this.callSuper('_set', key, value);
  8062. if (key === 'radius') {
  8063. this.setRadius(value);
  8064. }
  8065. return this;
  8066. },
  8067. /**
  8068. * Returns object representation of an instance
  8069. * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
  8070. * @return {Object} object representation of an instance
  8071. */
  8072. toObject: function(propertiesToInclude) {
  8073. return this.callSuper('toObject', ['radius', 'startAngle', 'endAngle'].concat(propertiesToInclude));
  8074. },
  8075. /* _TO_SVG_START_ */
  8076. /**
  8077. * Returns svg representation of an instance
  8078. * @param {Function} [reviver] Method for further parsing of svg representation.
  8079. * @return {String} svg representation of an instance
  8080. */
  8081. toSVG: function(reviver) {
  8082. var markup = this._createBaseSVGMarkup(), x = 0, y = 0,
  8083. angle = (this.endAngle - this.startAngle) % ( 2 * pi);
  8084. if (angle === 0) {
  8085. markup.push(
  8086. '<circle ', this.getSvgId(),
  8087. 'cx="' + x + '" cy="' + y + '" ',
  8088. 'r="', this.radius,
  8089. '" c_style="', this.getSvgStyles(),
  8090. '" transform="', this.getSvgTransform(),
  8091. ' ', this.getSvgTransformMatrix(), '"',
  8092. this.addPaintOrder(),
  8093. '/>\n'
  8094. );
  8095. }
  8096. else {
  8097. var startX = fabric.util.cos(this.startAngle) * this.radius,
  8098. startY = fabric.util.sin(this.startAngle) * this.radius,
  8099. endX = fabric.util.cos(this.endAngle) * this.radius,
  8100. endY = fabric.util.sin(this.endAngle) * this.radius,
  8101. largeFlag = angle > pi ? '1' : '0';
  8102. markup.push(
  8103. '<path d="M ' + startX + ' ' + startY,
  8104. ' A ' + this.radius + ' ' + this.radius,
  8105. ' 0 ', +largeFlag + ' 1', ' ' + endX + ' ' + endY,
  8106. '" c_style="', this.getSvgStyles(),
  8107. '" transform="', this.getSvgTransform(),
  8108. ' ', this.getSvgTransformMatrix(), '"',
  8109. this.addPaintOrder(),
  8110. '"/>\n'
  8111. );
  8112. }
  8113. return reviver ? reviver(markup.join('')) : markup.join('');
  8114. },
  8115. /* _TO_SVG_END_ */
  8116. /**
  8117. * @private
  8118. * @param {CanvasRenderingContext2D} ctx context to render on
  8119. */
  8120. _render: function(ctx) {
  8121. ctx.beginPath();
  8122. ctx.arc(
  8123. 0,
  8124. 0,
  8125. this.radius,
  8126. this.startAngle,
  8127. this.endAngle, false);
  8128. this._renderPaintInOrder(ctx);
  8129. },
  8130. /**
  8131. * Returns horizontal radius of an object (according to how an object is scaled)
  8132. * @return {Number}
  8133. */
  8134. getRadiusX: function() {
  8135. return this.get('radius') * this.get('scaleX');
  8136. },
  8137. /**
  8138. * Returns vertical radius of an object (according to how an object is scaled)
  8139. * @return {Number}
  8140. */
  8141. getRadiusY: function() {
  8142. return this.get('radius') * this.get('scaleY');
  8143. },
  8144. /**
  8145. * Sets radius of an object (and updates width accordingly)
  8146. * @return {fabric.Circle} thisArg
  8147. */
  8148. setRadius: function(value) {
  8149. this.radius = value;
  8150. return this.set('width', value * 2).set('height', value * 2);
  8151. },
  8152. });
  8153. /**
  8154. * Returns {@link fabric.Circle} instance from an object representation
  8155. * @static
  8156. * @memberOf fabric.Circle
  8157. * @param {Object} object Object to create an instance from
  8158. * @param {function} [callback] invoked with new instance as first argument
  8159. * @return {Object} Instance of fabric.Circle
  8160. */
  8161. fabric.Circle.fromObject = function(object, callback) {
  8162. return fabric.Object._fromObject('Circle', object, callback);
  8163. };
  8164. })(typeof exports !== 'undefined' ? exports : this);
  8165. (function(global) {
  8166. 'use strict';
  8167. var fabric = global.fabric || (global.fabric = { });
  8168. if (fabric.Triangle) {
  8169. fabric.warn('fabric.Triangle is already defined');
  8170. return;
  8171. }
  8172. /**
  8173. * Triangle class
  8174. * @class fabric.Triangle
  8175. * @extends fabric.Object
  8176. * @return {fabric.Triangle} thisArg
  8177. * @see {@link fabric.Triangle#initialize} for constructor definition
  8178. */
  8179. fabric.Triangle = fabric.util.createClass(fabric.Object, /** @lends fabric.Triangle.prototype */ {
  8180. /**
  8181. * Type of an object
  8182. * @type String
  8183. * @default
  8184. */
  8185. type: 'triangle',
  8186. /**
  8187. * Width is set to 100 to compensate the old initialize code that was setting it to 100
  8188. * @type Number
  8189. * @default
  8190. */
  8191. width: 100,
  8192. /**
  8193. * Height is set to 100 to compensate the old initialize code that was setting it to 100
  8194. * @type Number
  8195. * @default
  8196. */
  8197. height: 100,
  8198. /**
  8199. * @private
  8200. * @param {CanvasRenderingContext2D} ctx Context to render on
  8201. */
  8202. _render: function(ctx) {
  8203. var widthBy2 = this.width / 2,
  8204. heightBy2 = this.height / 2;
  8205. ctx.beginPath();
  8206. ctx.moveTo(-widthBy2, heightBy2);
  8207. ctx.lineTo(0, -heightBy2);
  8208. ctx.lineTo(widthBy2, heightBy2);
  8209. ctx.closePath();
  8210. this._renderPaintInOrder(ctx);
  8211. },
  8212. /**
  8213. * @private
  8214. * @param {CanvasRenderingContext2D} ctx Context to render on
  8215. */
  8216. _renderDashedStroke: function(ctx) {
  8217. var widthBy2 = this.width / 2,
  8218. heightBy2 = this.height / 2;
  8219. ctx.beginPath();
  8220. fabric.util.drawDashedLine(ctx, -widthBy2, heightBy2, 0, -heightBy2, this.strokeDashArray);
  8221. fabric.util.drawDashedLine(ctx, 0, -heightBy2, widthBy2, heightBy2, this.strokeDashArray);
  8222. fabric.util.drawDashedLine(ctx, widthBy2, heightBy2, -widthBy2, heightBy2, this.strokeDashArray);
  8223. ctx.closePath();
  8224. },
  8225. /* _TO_SVG_START_ */
  8226. /**
  8227. * Returns SVG representation of an instance
  8228. * @param {Function} [reviver] Method for further parsing of svg representation.
  8229. * @return {String} svg representation of an instance
  8230. */
  8231. toSVG: function(reviver) {
  8232. var markup = this._createBaseSVGMarkup(),
  8233. widthBy2 = this.width / 2,
  8234. heightBy2 = this.height / 2,
  8235. points = [
  8236. -widthBy2 + ' ' + heightBy2,
  8237. '0 ' + -heightBy2,
  8238. widthBy2 + ' ' + heightBy2
  8239. ]
  8240. .join(',');
  8241. markup.push(
  8242. '<polygon ', this.getSvgId(),
  8243. 'points="', points,
  8244. '" c_style="', this.getSvgStyles(),
  8245. '" transform="', this.getSvgTransform(), '"',
  8246. this.addPaintOrder(),
  8247. '/>'
  8248. );
  8249. return reviver ? reviver(markup.join('')) : markup.join('');
  8250. },
  8251. /* _TO_SVG_END_ */
  8252. });
  8253. /**
  8254. * Returns {@link fabric.Triangle} instance from an object representation
  8255. * @static
  8256. * @memberOf fabric.Triangle
  8257. * @param {Object} object Object to create an instance from
  8258. * @param {function} [callback] invoked with new instance as first argument
  8259. */
  8260. fabric.Triangle.fromObject = function(object, callback) {
  8261. return fabric.Object._fromObject('Triangle', object, callback);
  8262. };
  8263. })(typeof exports !== 'undefined' ? exports : this);
  8264. (function(global) {
  8265. 'use strict';
  8266. var fabric = global.fabric || (global.fabric = { }),
  8267. piBy2 = Math.PI * 2;
  8268. if (fabric.Ellipse) {
  8269. fabric.warn('fabric.Ellipse is already defined.');
  8270. return;
  8271. }
  8272. /**
  8273. * Ellipse class
  8274. * @class fabric.Ellipse
  8275. * @extends fabric.Object
  8276. * @return {fabric.Ellipse} thisArg
  8277. * @see {@link fabric.Ellipse#initialize} for constructor definition
  8278. */
  8279. fabric.Ellipse = fabric.util.createClass(fabric.Object, /** @lends fabric.Ellipse.prototype */ {
  8280. /**
  8281. * Type of an object
  8282. * @type String
  8283. * @default
  8284. */
  8285. type: 'ellipse',
  8286. /**
  8287. * Horizontal radius
  8288. * @type Number
  8289. * @default
  8290. */
  8291. rx: 0,
  8292. /**
  8293. * Vertical radius
  8294. * @type Number
  8295. * @default
  8296. */
  8297. ry: 0,
  8298. cacheProperties: fabric.Object.prototype.cacheProperties.concat('rx', 'ry'),
  8299. /**
  8300. * Constructor
  8301. * @param {Object} [options] Options object
  8302. * @return {fabric.Ellipse} thisArg
  8303. */
  8304. initialize: function(options) {
  8305. this.callSuper('initialize', options);
  8306. this.set('rx', options && options.rx || 0);
  8307. this.set('ry', options && options.ry || 0);
  8308. },
  8309. /**
  8310. * @private
  8311. * @param {String} key
  8312. * @param {*} value
  8313. * @return {fabric.Ellipse} thisArg
  8314. */
  8315. _set: function(key, value) {
  8316. this.callSuper('_set', key, value);
  8317. switch (key) {
  8318. case 'rx':
  8319. this.rx = value;
  8320. this.set('width', value * 2);
  8321. break;
  8322. case 'ry':
  8323. this.ry = value;
  8324. this.set('height', value * 2);
  8325. break;
  8326. }
  8327. return this;
  8328. },
  8329. /**
  8330. * Returns horizontal radius of an object (according to how an object is scaled)
  8331. * @return {Number}
  8332. */
  8333. getRx: function() {
  8334. return this.get('rx') * this.get('scaleX');
  8335. },
  8336. /**
  8337. * Returns Vertical radius of an object (according to how an object is scaled)
  8338. * @return {Number}
  8339. */
  8340. getRy: function() {
  8341. return this.get('ry') * this.get('scaleY');
  8342. },
  8343. /**
  8344. * Returns object representation of an instance
  8345. * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
  8346. * @return {Object} object representation of an instance
  8347. */
  8348. toObject: function(propertiesToInclude) {
  8349. return this.callSuper('toObject', ['rx', 'ry'].concat(propertiesToInclude));
  8350. },
  8351. /* _TO_SVG_START_ */
  8352. /**
  8353. * Returns svg representation of an instance
  8354. * @param {Function} [reviver] Method for further parsing of svg representation.
  8355. * @return {String} svg representation of an instance
  8356. */
  8357. toSVG: function(reviver) {
  8358. var markup = this._createBaseSVGMarkup();
  8359. markup.push(
  8360. '<ellipse ', this.getSvgId(),
  8361. 'cx="0" cy="0" ',
  8362. 'rx="', this.rx,
  8363. '" ry="', this.ry,
  8364. '" c_style="', this.getSvgStyles(),
  8365. '" transform="', this.getSvgTransform(),
  8366. this.getSvgTransformMatrix(), '"',
  8367. this.addPaintOrder(),
  8368. '/>\n'
  8369. );
  8370. return reviver ? reviver(markup.join('')) : markup.join('');
  8371. },
  8372. /* _TO_SVG_END_ */
  8373. /**
  8374. * @private
  8375. * @param {CanvasRenderingContext2D} ctx context to render on
  8376. */
  8377. _render: function(ctx) {
  8378. ctx.beginPath();
  8379. ctx.save();
  8380. ctx.transform(1, 0, 0, this.ry / this.rx, 0, 0);
  8381. ctx.arc(
  8382. 0,
  8383. 0,
  8384. this.rx,
  8385. 0,
  8386. piBy2,
  8387. false);
  8388. ctx.restore();
  8389. this._renderPaintInOrder(ctx);
  8390. },
  8391. });
  8392. /**
  8393. * Returns {@link fabric.Ellipse} instance from an object representation
  8394. * @static
  8395. * @memberOf fabric.Ellipse
  8396. * @param {Object} object Object to create an instance from
  8397. * @param {function} [callback] invoked with new instance as first argument
  8398. * @return {fabric.Ellipse}
  8399. */
  8400. fabric.Ellipse.fromObject = function(object, callback) {
  8401. return fabric.Object._fromObject('Ellipse', object, callback);
  8402. };
  8403. })(typeof exports !== 'undefined' ? exports : this);
  8404. (function(global) {
  8405. 'use strict';
  8406. var fabric = global.fabric || (global.fabric = { }),
  8407. extend = fabric.util.object.extend;
  8408. if (fabric.Rect) {
  8409. fabric.warn('fabric.Rect is already defined');
  8410. return;
  8411. }
  8412. /**
  8413. * Rectangle class
  8414. * @class fabric.Rect
  8415. * @extends fabric.Object
  8416. * @return {fabric.Rect} thisArg
  8417. * @see {@link fabric.Rect#initialize} for constructor definition
  8418. */
  8419. fabric.Rect = fabric.util.createClass(fabric.Object, /** @lends fabric.Rect.prototype */ {
  8420. /**
  8421. * List of properties to consider when checking if state of an object is changed ({@link fabric.Object#hasStateChanged})
  8422. * as well as for history (undo/redo) purposes
  8423. * @type Array
  8424. */
  8425. stateProperties: fabric.Object.prototype.stateProperties.concat('rx', 'ry'),
  8426. /**
  8427. * Type of an object
  8428. * @type String
  8429. * @default
  8430. */
  8431. type: 'rect',
  8432. /**
  8433. * Horizontal border radius
  8434. * @type Number
  8435. * @default
  8436. */
  8437. rx: 0,
  8438. /**
  8439. * Vertical border radius
  8440. * @type Number
  8441. * @default
  8442. */
  8443. ry: 0,
  8444. cacheProperties: fabric.Object.prototype.cacheProperties.concat('rx', 'ry'),
  8445. /**
  8446. * Constructor
  8447. * @param {Object} [options] Options object
  8448. * @return {Object} thisArg
  8449. */
  8450. initialize: function(options) {
  8451. this.callSuper('initialize', options);
  8452. this._initRxRy();
  8453. },
  8454. /**
  8455. * Initializes rx/ry attributes
  8456. * @private
  8457. */
  8458. _initRxRy: function() {
  8459. if (this.rx && !this.ry) {
  8460. this.ry = this.rx;
  8461. }
  8462. else if (this.ry && !this.rx) {
  8463. this.rx = this.ry;
  8464. }
  8465. },
  8466. /**
  8467. * @private
  8468. * @param {CanvasRenderingContext2D} ctx Context to render on
  8469. */
  8470. _render: function(ctx) {
  8471. // optimize 1x1 case (used in spray brush)
  8472. if (this.width === 1 && this.height === 1) {
  8473. ctx.fillRect(-0.5, -0.5, 1, 1);
  8474. return;
  8475. }
  8476. var rx = this.rx ? Math.min(this.rx, this.width / 2) : 0,
  8477. ry = this.ry ? Math.min(this.ry, this.height / 2) : 0,
  8478. w = this.width,
  8479. h = this.height,
  8480. x = -this.width / 2,
  8481. y = -this.height / 2,
  8482. isRounded = rx !== 0 || ry !== 0,
  8483. /* "magic number" for bezier approximations of arcs (http://itc.ktu.lt/itc354/Riskus354.pdf) */
  8484. k = 1 - 0.5522847498;
  8485. ctx.beginPath();
  8486. ctx.moveTo(x + rx, y);
  8487. ctx.lineTo(x + w - rx, y);
  8488. isRounded && ctx.bezierCurveTo(x + w - k * rx, y, x + w, y + k * ry, x + w, y + ry);
  8489. ctx.lineTo(x + w, y + h - ry);
  8490. isRounded && ctx.bezierCurveTo(x + w, y + h - k * ry, x + w - k * rx, y + h, x + w - rx, y + h);
  8491. ctx.lineTo(x + rx, y + h);
  8492. isRounded && ctx.bezierCurveTo(x + k * rx, y + h, x, y + h - k * ry, x, y + h - ry);
  8493. ctx.lineTo(x, y + ry);
  8494. isRounded && ctx.bezierCurveTo(x, y + k * ry, x + k * rx, y, x + rx, y);
  8495. ctx.closePath();
  8496. this._renderPaintInOrder(ctx);
  8497. },
  8498. /**
  8499. * @private
  8500. * @param {CanvasRenderingContext2D} ctx Context to render on
  8501. */
  8502. _renderDashedStroke: function(ctx) {
  8503. var x = -this.width / 2,
  8504. y = -this.height / 2,
  8505. w = this.width,
  8506. h = this.height;
  8507. ctx.beginPath();
  8508. fabric.util.drawDashedLine(ctx, x, y, x + w, y, this.strokeDashArray);
  8509. fabric.util.drawDashedLine(ctx, x + w, y, x + w, y + h, this.strokeDashArray);
  8510. fabric.util.drawDashedLine(ctx, x + w, y + h, x, y + h, this.strokeDashArray);
  8511. fabric.util.drawDashedLine(ctx, x, y + h, x, y, this.strokeDashArray);
  8512. ctx.closePath();
  8513. },
  8514. /**
  8515. * Returns object representation of an instance
  8516. * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
  8517. * @return {Object} object representation of an instance
  8518. */
  8519. toObject: function(propertiesToInclude) {
  8520. return this.callSuper('toObject', ['rx', 'ry'].concat(propertiesToInclude));
  8521. },
  8522. /* _TO_SVG_START_ */
  8523. /**
  8524. * Returns svg representation of an instance
  8525. * @param {Function} [reviver] Method for further parsing of svg representation.
  8526. * @return {String} svg representation of an instance
  8527. */
  8528. toSVG: function(reviver) {
  8529. var markup = this._createBaseSVGMarkup(), x = -this.width / 2, y = -this.height / 2;
  8530. markup.push(
  8531. '<rect ', this.getSvgId(),
  8532. 'x="', x, '" y="', y,
  8533. '" rx="', this.get('rx'), '" ry="', this.get('ry'),
  8534. '" width="', this.width, '" height="', this.height,
  8535. '" c_style="', this.getSvgStyles(),
  8536. '" transform="', this.getSvgTransform(),
  8537. this.getSvgTransformMatrix(), '"',
  8538. this.addPaintOrder(),
  8539. '/>\n');
  8540. return reviver ? reviver(markup.join('')) : markup.join('');
  8541. },
  8542. /* _TO_SVG_END_ */
  8543. });
  8544. /**
  8545. * Returns {@link fabric.Rect} instance from an object representation
  8546. * @static
  8547. * @memberOf fabric.Rect
  8548. * @param {Object} object Object to create an instance from
  8549. * @param {Function} [callback] Callback to invoke when an fabric.Rect instance is created
  8550. */
  8551. fabric.Rect.fromObject = function(object, callback) {
  8552. return fabric.Object._fromObject('Rect', object, callback);
  8553. };
  8554. })(typeof exports !== 'undefined' ? exports : this);
  8555. (function(global) {
  8556. 'use strict';
  8557. var fabric = global.fabric || (global.fabric = { }),
  8558. extend = fabric.util.object.extend,
  8559. min = fabric.util.array.min,
  8560. max = fabric.util.array.max,
  8561. toFixed = fabric.util.toFixed;
  8562. if (fabric.Polyline) {
  8563. fabric.warn('fabric.Polyline is already defined');
  8564. return;
  8565. }
  8566. /**
  8567. * Polyline class
  8568. * @class fabric.Polyline
  8569. * @extends fabric.Object
  8570. * @see {@link fabric.Polyline#initialize} for constructor definition
  8571. */
  8572. fabric.Polyline = fabric.util.createClass(fabric.Object, /** @lends fabric.Polyline.prototype */ {
  8573. /**
  8574. * Type of an object
  8575. * @type String
  8576. * @default
  8577. */
  8578. type: 'polyline',
  8579. /**
  8580. * Points array
  8581. * @type Array
  8582. * @default
  8583. */
  8584. points: null,
  8585. cacheProperties: fabric.Object.prototype.cacheProperties.concat('points'),
  8586. /**
  8587. * Constructor
  8588. * @param {Array} points Array of points (where each point is an object with x and y)
  8589. * @param {Object} [options] Options object
  8590. * @return {fabric.Polyline} thisArg
  8591. * @example
  8592. * var poly = new fabric.Polyline([
  8593. * { x: 10, y: 10 },
  8594. * { x: 50, y: 30 },
  8595. * { x: 40, y: 70 },
  8596. * { x: 60, y: 50 },
  8597. * { x: 100, y: 150 },
  8598. * { x: 40, y: 100 }
  8599. * ], {
  8600. * stroke: 'red',
  8601. * left: 100,
  8602. * top: 100
  8603. * });
  8604. */
  8605. initialize: function(points, options) {
  8606. options = options || {};
  8607. this.points = points || [];
  8608. this.callSuper('initialize', options);
  8609. var calcDim = this._calcDimensions();
  8610. if (typeof options.left === 'undefined') {
  8611. this.left = calcDim.left;
  8612. }
  8613. if (typeof options.top === 'undefined') {
  8614. this.top = calcDim.top;
  8615. }
  8616. this.width = calcDim.width;
  8617. this.height = calcDim.height;
  8618. this.pathOffset = {
  8619. x: calcDim.left + this.width / 2,
  8620. y: calcDim.top + this.height / 2
  8621. };
  8622. },
  8623. /**
  8624. * Calculate the polygon min and max point from points array,
  8625. * returning an object with left, top, widht, height to measure the
  8626. * polygon size
  8627. * @return {Object} object.left X coordinate of the polygon leftmost point
  8628. * @return {Object} object.top Y coordinate of the polygon topmost point
  8629. * @return {Object} object.width distance between X coordinates of the polygon leftmost and rightmost point
  8630. * @return {Object} object.height distance between Y coordinates of the polygon topmost and bottommost point
  8631. * @private
  8632. */
  8633. _calcDimensions: function() {
  8634. var points = this.points,
  8635. minX = min(points, 'x') || 0,
  8636. minY = min(points, 'y') || 0,
  8637. maxX = max(points, 'x') || 0,
  8638. maxY = max(points, 'y') || 0,
  8639. width = (maxX - minX),
  8640. height = (maxY - minY);
  8641. return {
  8642. left: minX,
  8643. top: minY,
  8644. width: width,
  8645. height: height
  8646. };
  8647. },
  8648. /**
  8649. * Returns object representation of an instance
  8650. * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
  8651. * @return {Object} Object representation of an instance
  8652. */
  8653. toObject: function(propertiesToInclude) {
  8654. return extend(this.callSuper('toObject', propertiesToInclude), {
  8655. points: this.points.concat()
  8656. });
  8657. },
  8658. /* _TO_SVG_START_ */
  8659. /**
  8660. * Returns svg representation of an instance
  8661. * @param {Function} [reviver] Method for further parsing of svg representation.
  8662. * @return {String} svg representation of an instance
  8663. */
  8664. toSVG: function(reviver) {
  8665. var points = [], diffX = this.pathOffset.x, diffY = this.pathOffset.y,
  8666. markup = this._createBaseSVGMarkup(),
  8667. NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS;
  8668. for (var i = 0, len = this.points.length; i < len; i++) {
  8669. points.push(
  8670. toFixed(this.points[i].x - diffX, NUM_FRACTION_DIGITS), ',',
  8671. toFixed(this.points[i].y - diffY, NUM_FRACTION_DIGITS), ' '
  8672. );
  8673. }
  8674. markup.push(
  8675. '<', this.type, ' ', this.getSvgId(),
  8676. 'points="', points.join(''),
  8677. '" c_style="', this.getSvgStyles(),
  8678. '" transform="', this.getSvgTransform(),
  8679. ' ', this.getSvgTransformMatrix(), '"',
  8680. this.addPaintOrder(),
  8681. '/>\n'
  8682. );
  8683. return reviver ? reviver(markup.join('')) : markup.join('');
  8684. },
  8685. /* _TO_SVG_END_ */
  8686. /**
  8687. * @private
  8688. * @param {CanvasRenderingContext2D} ctx Context to render on
  8689. */
  8690. commonRender: function(ctx) {
  8691. var point, len = this.points.length,
  8692. x = this.pathOffset.x,
  8693. y = this.pathOffset.y;
  8694. if (!len || isNaN(this.points[len - 1].y)) {
  8695. // do not draw if no points or odd points
  8696. // NaN comes from parseFloat of a empty string in parser
  8697. return false;
  8698. }
  8699. ctx.beginPath();
  8700. ctx.moveTo(this.points[0].x - x, this.points[0].y - y);
  8701. for (var i = 0; i < len; i++) {
  8702. point = this.points[i];
  8703. ctx.lineTo(point.x - x, point.y - y);
  8704. }
  8705. return true;
  8706. },
  8707. /**
  8708. * @private
  8709. * @param {CanvasRenderingContext2D} ctx Context to render on
  8710. */
  8711. _render: function(ctx) {
  8712. if (!this.commonRender(ctx)) {
  8713. return;
  8714. }
  8715. this._renderPaintInOrder(ctx);
  8716. },
  8717. /**
  8718. * @private
  8719. * @param {CanvasRenderingContext2D} ctx Context to render on
  8720. */
  8721. _renderDashedStroke: function(ctx) {
  8722. var p1, p2;
  8723. ctx.beginPath();
  8724. for (var i = 0, len = this.points.length; i < len; i++) {
  8725. p1 = this.points[i];
  8726. p2 = this.points[i + 1] || p1;
  8727. fabric.util.drawDashedLine(ctx, p1.x, p1.y, p2.x, p2.y, this.strokeDashArray);
  8728. }
  8729. },
  8730. /**
  8731. * Returns complexity of an instance
  8732. * @return {Number} complexity of this instance
  8733. */
  8734. complexity: function() {
  8735. return this.get('points').length;
  8736. }
  8737. });
  8738. /**
  8739. * Returns fabric.Polyline instance from an object representation
  8740. * @static
  8741. * @memberOf fabric.Polyline
  8742. * @param {Object} object Object to create an instance from
  8743. * @param {Function} [callback] Callback to invoke when an fabric.Path instance is created
  8744. */
  8745. fabric.Polyline.fromObject = function(object, callback) {
  8746. return fabric.Object._fromObject('Polyline', object, callback, 'points');
  8747. };
  8748. })(typeof exports !== 'undefined' ? exports : this);
  8749. (function(global) {
  8750. 'use strict';
  8751. var fabric = global.fabric || (global.fabric = { }),
  8752. extend = fabric.util.object.extend;
  8753. if (fabric.Polygon) {
  8754. fabric.warn('fabric.Polygon is already defined');
  8755. return;
  8756. }
  8757. /**
  8758. * Polygon class
  8759. * @class fabric.Polygon
  8760. * @extends fabric.Polyline
  8761. * @see {@link fabric.Polygon#initialize} for constructor definition
  8762. */
  8763. fabric.Polygon = fabric.util.createClass(fabric.Polyline, /** @lends fabric.Polygon.prototype */ {
  8764. /**
  8765. * Type of an object
  8766. * @type String
  8767. * @default
  8768. */
  8769. type: 'polygon',
  8770. /**
  8771. * @private
  8772. * @param {CanvasRenderingContext2D} ctx Context to render on
  8773. */
  8774. _render: function(ctx) {
  8775. if (!this.commonRender(ctx)) {
  8776. return;
  8777. }
  8778. ctx.closePath();
  8779. this._renderPaintInOrder(ctx);
  8780. },
  8781. /**
  8782. * @private
  8783. * @param {CanvasRenderingContext2D} ctx Context to render on
  8784. */
  8785. _renderDashedStroke: function(ctx) {
  8786. this.callSuper('_renderDashedStroke', ctx);
  8787. ctx.closePath();
  8788. },
  8789. });
  8790. /**
  8791. * Returns fabric.Polygon instance from an object representation
  8792. * @static
  8793. * @memberOf fabric.Polygon
  8794. * @param {Object} object Object to create an instance from
  8795. * @param {Function} [callback] Callback to invoke when an fabric.Path instance is created
  8796. */
  8797. fabric.Polygon.fromObject = function(object, callback) {
  8798. return fabric.Object._fromObject('Polygon', object, callback, 'points');
  8799. };
  8800. })(typeof exports !== 'undefined' ? exports : this);
  8801. (function(global) {
  8802. 'use strict';
  8803. var fabric = global.fabric || (global.fabric = { }),
  8804. min = fabric.util.array.min,
  8805. max = fabric.util.array.max,
  8806. extend = fabric.util.object.extend,
  8807. _toString = Object.prototype.toString,
  8808. drawArc = fabric.util.drawArc,
  8809. commandLengths = {
  8810. m: 2,
  8811. l: 2,
  8812. h: 1,
  8813. v: 1,
  8814. c: 6,
  8815. s: 4,
  8816. q: 4,
  8817. t: 2,
  8818. a: 7
  8819. },
  8820. repeatedCommands = {
  8821. m: 'l',
  8822. M: 'L'
  8823. };
  8824. if (fabric.Path) {
  8825. fabric.warn('fabric.Path is already defined');
  8826. return;
  8827. }
  8828. /**
  8829. * Path class
  8830. * @class fabric.Path
  8831. * @extends fabric.Object
  8832. * @tutorial {@link http://fabricjs.com/fabric-intro-part-1#path_and_pathgroup}
  8833. * @see {@link fabric.Path#initialize} for constructor definition
  8834. */
  8835. fabric.Path = fabric.util.createClass(fabric.Object, /** @lends fabric.Path.prototype */ {
  8836. /**
  8837. * Type of an object
  8838. * @type String
  8839. * @default
  8840. */
  8841. type: 'path',
  8842. /**
  8843. * Array of path points
  8844. * @type Array
  8845. * @default
  8846. */
  8847. path: null,
  8848. cacheProperties: fabric.Object.prototype.cacheProperties.concat('path', 'fillRule'),
  8849. stateProperties: fabric.Object.prototype.stateProperties.concat('path'),
  8850. /**
  8851. * Constructor
  8852. * @param {Array|String} path Path data (sequence of coordinates and corresponding "command" tokens)
  8853. * @param {Object} [options] Options object
  8854. * @return {fabric.Path} thisArg
  8855. */
  8856. initialize: function(path, options) {
  8857. options = options || { };
  8858. this.callSuper('initialize', options);
  8859. if (!path) {
  8860. path = [];
  8861. }
  8862. var fromArray = _toString.call(path) === '[object Array]';
  8863. this.path = fromArray
  8864. ? path
  8865. // one of commands (m,M,l,L,q,Q,c,C,etc.) followed by non-command characters (i.e. command values)
  8866. : path.match && path.match(/[mzlhvcsqta][^mzlhvcsqta]*/gi);
  8867. if (!this.path) {
  8868. return;
  8869. }
  8870. if (!fromArray) {
  8871. this.path = this._parsePath();
  8872. }
  8873. this._setPositionDimensions(options);
  8874. },
  8875. /**
  8876. * @private
  8877. * @param {Object} options Options object
  8878. */
  8879. _setPositionDimensions: function(options) {
  8880. var calcDim = this._parseDimensions();
  8881. this.width = calcDim.width;
  8882. this.height = calcDim.height;
  8883. if (typeof options.left === 'undefined') {
  8884. this.left = calcDim.left;
  8885. }
  8886. if (typeof options.top === 'undefined') {
  8887. this.top = calcDim.top;
  8888. }
  8889. this.pathOffset = this.pathOffset || {
  8890. x: calcDim.left + this.width / 2,
  8891. y: calcDim.top + this.height / 2
  8892. };
  8893. },
  8894. /**
  8895. * @private
  8896. * @param {CanvasRenderingContext2D} ctx context to render path on
  8897. */
  8898. _renderPathCommands: function(ctx) {
  8899. var current, // current instruction
  8900. previous = null,
  8901. subpathStartX = 0,
  8902. subpathStartY = 0,
  8903. x = 0, // current x
  8904. y = 0, // current y
  8905. controlX = 0, // current control point x
  8906. controlY = 0, // current control point y
  8907. tempX,
  8908. tempY,
  8909. l = -this.pathOffset.x,
  8910. t = -this.pathOffset.y;
  8911. ctx.beginPath();
  8912. for (var i = 0, len = this.path.length; i < len; ++i) {
  8913. current = this.path[i];
  8914. switch (current[0]) { // first letter
  8915. case 'l': // lineto, relative
  8916. x += current[1];
  8917. y += current[2];
  8918. ctx.lineTo(x + l, y + t);
  8919. break;
  8920. case 'L': // lineto, absolute
  8921. x = current[1];
  8922. y = current[2];
  8923. ctx.lineTo(x + l, y + t);
  8924. break;
  8925. case 'h': // horizontal lineto, relative
  8926. x += current[1];
  8927. ctx.lineTo(x + l, y + t);
  8928. break;
  8929. case 'H': // horizontal lineto, absolute
  8930. x = current[1];
  8931. ctx.lineTo(x + l, y + t);
  8932. break;
  8933. case 'v': // vertical lineto, relative
  8934. y += current[1];
  8935. ctx.lineTo(x + l, y + t);
  8936. break;
  8937. case 'V': // verical lineto, absolute
  8938. y = current[1];
  8939. ctx.lineTo(x + l, y + t);
  8940. break;
  8941. case 'm': // moveTo, relative
  8942. x += current[1];
  8943. y += current[2];
  8944. subpathStartX = x;
  8945. subpathStartY = y;
  8946. ctx.moveTo(x + l, y + t);
  8947. break;
  8948. case 'M': // moveTo, absolute
  8949. x = current[1];
  8950. y = current[2];
  8951. subpathStartX = x;
  8952. subpathStartY = y;
  8953. ctx.moveTo(x + l, y + t);
  8954. break;
  8955. case 'c': // bezierCurveTo, relative
  8956. tempX = x + current[5];
  8957. tempY = y + current[6];
  8958. controlX = x + current[3];
  8959. controlY = y + current[4];
  8960. ctx.bezierCurveTo(
  8961. x + current[1] + l, // x1
  8962. y + current[2] + t, // y1
  8963. controlX + l, // x2
  8964. controlY + t, // y2
  8965. tempX + l,
  8966. tempY + t
  8967. );
  8968. x = tempX;
  8969. y = tempY;
  8970. break;
  8971. case 'C': // bezierCurveTo, absolute
  8972. x = current[5];
  8973. y = current[6];
  8974. controlX = current[3];
  8975. controlY = current[4];
  8976. ctx.bezierCurveTo(
  8977. current[1] + l,
  8978. current[2] + t,
  8979. controlX + l,
  8980. controlY + t,
  8981. x + l,
  8982. y + t
  8983. );
  8984. break;
  8985. case 's': // shorthand cubic bezierCurveTo, relative
  8986. // transform to absolute x,y
  8987. tempX = x + current[3];
  8988. tempY = y + current[4];
  8989. if (previous[0].match(/[CcSs]/) === null) {
  8990. // If there is no previous command or if the previous command was not a C, c, S, or s,
  8991. // the control point is coincident with the current point
  8992. controlX = x;
  8993. controlY = y;
  8994. }
  8995. else {
  8996. // calculate reflection of previous control points
  8997. controlX = 2 * x - controlX;
  8998. controlY = 2 * y - controlY;
  8999. }
  9000. ctx.bezierCurveTo(
  9001. controlX + l,
  9002. controlY + t,
  9003. x + current[1] + l,
  9004. y + current[2] + t,
  9005. tempX + l,
  9006. tempY + t
  9007. );
  9008. // set control point to 2nd one of this command
  9009. // "... the first control point is assumed to be
  9010. // the reflection of the second control point on
  9011. // the previous command relative to the current point."
  9012. controlX = x + current[1];
  9013. controlY = y + current[2];
  9014. x = tempX;
  9015. y = tempY;
  9016. break;
  9017. case 'S': // shorthand cubic bezierCurveTo, absolute
  9018. tempX = current[3];
  9019. tempY = current[4];
  9020. if (previous[0].match(/[CcSs]/) === null) {
  9021. // If there is no previous command or if the previous command was not a C, c, S, or s,
  9022. // the control point is coincident with the current point
  9023. controlX = x;
  9024. controlY = y;
  9025. }
  9026. else {
  9027. // calculate reflection of previous control points
  9028. controlX = 2 * x - controlX;
  9029. controlY = 2 * y - controlY;
  9030. }
  9031. ctx.bezierCurveTo(
  9032. controlX + l,
  9033. controlY + t,
  9034. current[1] + l,
  9035. current[2] + t,
  9036. tempX + l,
  9037. tempY + t
  9038. );
  9039. x = tempX;
  9040. y = tempY;
  9041. // set control point to 2nd one of this command
  9042. // "... the first control point is assumed to be
  9043. // the reflection of the second control point on
  9044. // the previous command relative to the current point."
  9045. controlX = current[1];
  9046. controlY = current[2];
  9047. break;
  9048. case 'q': // quadraticCurveTo, relative
  9049. // transform to absolute x,y
  9050. tempX = x + current[3];
  9051. tempY = y + current[4];
  9052. controlX = x + current[1];
  9053. controlY = y + current[2];
  9054. ctx.quadraticCurveTo(
  9055. controlX + l,
  9056. controlY + t,
  9057. tempX + l,
  9058. tempY + t
  9059. );
  9060. x = tempX;
  9061. y = tempY;
  9062. break;
  9063. case 'Q': // quadraticCurveTo, absolute
  9064. tempX = current[3];
  9065. tempY = current[4];
  9066. ctx.quadraticCurveTo(
  9067. current[1] + l,
  9068. current[2] + t,
  9069. tempX + l,
  9070. tempY + t
  9071. );
  9072. x = tempX;
  9073. y = tempY;
  9074. controlX = current[1];
  9075. controlY = current[2];
  9076. break;
  9077. case 't': // shorthand quadraticCurveTo, relative
  9078. // transform to absolute x,y
  9079. tempX = x + current[1];
  9080. tempY = y + current[2];
  9081. if (previous[0].match(/[QqTt]/) === null) {
  9082. // If there is no previous command or if the previous command was not a Q, q, T or t,
  9083. // assume the control point is coincident with the current point
  9084. controlX = x;
  9085. controlY = y;
  9086. }
  9087. else {
  9088. // calculate reflection of previous control point
  9089. controlX = 2 * x - controlX;
  9090. controlY = 2 * y - controlY;
  9091. }
  9092. ctx.quadraticCurveTo(
  9093. controlX + l,
  9094. controlY + t,
  9095. tempX + l,
  9096. tempY + t
  9097. );
  9098. x = tempX;
  9099. y = tempY;
  9100. break;
  9101. case 'T':
  9102. tempX = current[1];
  9103. tempY = current[2];
  9104. if (previous[0].match(/[QqTt]/) === null) {
  9105. // If there is no previous command or if the previous command was not a Q, q, T or t,
  9106. // assume the control point is coincident with the current point
  9107. controlX = x;
  9108. controlY = y;
  9109. }
  9110. else {
  9111. // calculate reflection of previous control point
  9112. controlX = 2 * x - controlX;
  9113. controlY = 2 * y - controlY;
  9114. }
  9115. ctx.quadraticCurveTo(
  9116. controlX + l,
  9117. controlY + t,
  9118. tempX + l,
  9119. tempY + t
  9120. );
  9121. x = tempX;
  9122. y = tempY;
  9123. break;
  9124. case 'a':
  9125. // TODO: optimize this
  9126. drawArc(ctx, x + l, y + t, [
  9127. current[1],
  9128. current[2],
  9129. current[3],
  9130. current[4],
  9131. current[5],
  9132. current[6] + x + l,
  9133. current[7] + y + t
  9134. ]);
  9135. x += current[6];
  9136. y += current[7];
  9137. break;
  9138. case 'A':
  9139. // TODO: optimize this
  9140. drawArc(ctx, x + l, y + t, [
  9141. current[1],
  9142. current[2],
  9143. current[3],
  9144. current[4],
  9145. current[5],
  9146. current[6] + l,
  9147. current[7] + t
  9148. ]);
  9149. x = current[6];
  9150. y = current[7];
  9151. break;
  9152. case 'z':
  9153. case 'Z':
  9154. x = subpathStartX;
  9155. y = subpathStartY;
  9156. ctx.closePath();
  9157. break;
  9158. }
  9159. previous = current;
  9160. }
  9161. },
  9162. /**
  9163. * @private
  9164. * @param {CanvasRenderingContext2D} ctx context to render path on
  9165. */
  9166. _render: function(ctx) {
  9167. this._renderPathCommands(ctx);
  9168. this._renderPaintInOrder(ctx);
  9169. },
  9170. /**
  9171. * Returns string representation of an instance
  9172. * @return {String} string representation of an instance
  9173. */
  9174. toString: function() {
  9175. return '#<fabric.Path (' + this.complexity() +
  9176. '): { "top": ' + this.top + ', "left": ' + this.left + ' }>';
  9177. },
  9178. /**
  9179. * Returns object representation of an instance
  9180. * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
  9181. * @return {Object} object representation of an instance
  9182. */
  9183. toObject: function(propertiesToInclude) {
  9184. var o = extend(this.callSuper('toObject', propertiesToInclude), {
  9185. path: this.path.map(function(item) { return item.slice(); }),
  9186. top: this.top,
  9187. left: this.left,
  9188. });
  9189. return o;
  9190. },
  9191. /**
  9192. * Returns dataless object representation of an instance
  9193. * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
  9194. * @return {Object} object representation of an instance
  9195. */
  9196. toDatalessObject: function(propertiesToInclude) {
  9197. var o = this.toObject(['sourcePath'].concat(propertiesToInclude));
  9198. if (o.sourcePath) {
  9199. delete o.path;
  9200. }
  9201. return o;
  9202. },
  9203. /* _TO_SVG_START_ */
  9204. /**
  9205. * Returns svg representation of an instance
  9206. * @param {Function} [reviver] Method for further parsing of svg representation.
  9207. * @return {String} svg representation of an instance
  9208. */
  9209. toSVG: function(reviver) {
  9210. var chunks = [],
  9211. markup = this._createBaseSVGMarkup(), addTransform = '';
  9212. for (var i = 0, len = this.path.length; i < len; i++) {
  9213. chunks.push(this.path[i].join(' '));
  9214. }
  9215. var path = chunks.join(' ');
  9216. addTransform = ' translate(' + (-this.pathOffset.x) + ', ' + (-this.pathOffset.y) + ') ';
  9217. markup.push(
  9218. '<path ', this.getSvgId(),
  9219. 'd="', path,
  9220. '" c_style="', this.getSvgStyles(),
  9221. '" transform="', this.getSvgTransform(), addTransform,
  9222. this.getSvgTransformMatrix(), '" stroke-linecap="round" ',
  9223. this.addPaintOrder(),
  9224. '/>\n'
  9225. );
  9226. return reviver ? reviver(markup.join('')) : markup.join('');
  9227. },
  9228. /* _TO_SVG_END_ */
  9229. /**
  9230. * Returns number representation of an instance complexity
  9231. * @return {Number} complexity of this instance
  9232. */
  9233. complexity: function() {
  9234. return this.path.length;
  9235. },
  9236. /**
  9237. * @private
  9238. */
  9239. _parsePath: function() {
  9240. var result = [],
  9241. coords = [],
  9242. currentPath,
  9243. parsed,
  9244. re = /([-+]?((\d+\.\d+)|((\d+)|(\.\d+)))(?:e[-+]?\d+)?)/ig,
  9245. match,
  9246. coordsStr;
  9247. for (var i = 0, coordsParsed, len = this.path.length; i < len; i++) {
  9248. currentPath = this.path[i];
  9249. coordsStr = currentPath.slice(1).trim();
  9250. coords.length = 0;
  9251. while ((match = re.exec(coordsStr))) {
  9252. coords.push(match[0]);
  9253. }
  9254. coordsParsed = [currentPath.charAt(0)];
  9255. for (var j = 0, jlen = coords.length; j < jlen; j++) {
  9256. parsed = parseFloat(coords[j]);
  9257. if (!isNaN(parsed)) {
  9258. coordsParsed.push(parsed);
  9259. }
  9260. }
  9261. var command = coordsParsed[0],
  9262. commandLength = commandLengths[command.toLowerCase()],
  9263. repeatedCommand = repeatedCommands[command] || command;
  9264. if (coordsParsed.length - 1 > commandLength) {
  9265. for (var k = 1, klen = coordsParsed.length; k < klen; k += commandLength) {
  9266. result.push([command].concat(coordsParsed.slice(k, k + commandLength)));
  9267. command = repeatedCommand;
  9268. }
  9269. }
  9270. else {
  9271. result.push(coordsParsed);
  9272. }
  9273. }
  9274. return result;
  9275. },
  9276. /**
  9277. * @private
  9278. */
  9279. _parseDimensions: function() {
  9280. var aX = [],
  9281. aY = [],
  9282. current, // current instruction
  9283. previous = null,
  9284. subpathStartX = 0,
  9285. subpathStartY = 0,
  9286. x = 0, // current x
  9287. y = 0, // current y
  9288. controlX = 0, // current control point x
  9289. controlY = 0, // current control point y
  9290. tempX,
  9291. tempY,
  9292. bounds;
  9293. for (var i = 0, len = this.path.length; i < len; ++i) {
  9294. current = this.path[i];
  9295. switch (current[0]) { // first letter
  9296. case 'l': // lineto, relative
  9297. x += current[1];
  9298. y += current[2];
  9299. bounds = [];
  9300. break;
  9301. case 'L': // lineto, absolute
  9302. x = current[1];
  9303. y = current[2];
  9304. bounds = [];
  9305. break;
  9306. case 'h': // horizontal lineto, relative
  9307. x += current[1];
  9308. bounds = [];
  9309. break;
  9310. case 'H': // horizontal lineto, absolute
  9311. x = current[1];
  9312. bounds = [];
  9313. break;
  9314. case 'v': // vertical lineto, relative
  9315. y += current[1];
  9316. bounds = [];
  9317. break;
  9318. case 'V': // verical lineto, absolute
  9319. y = current[1];
  9320. bounds = [];
  9321. break;
  9322. case 'm': // moveTo, relative
  9323. x += current[1];
  9324. y += current[2];
  9325. subpathStartX = x;
  9326. subpathStartY = y;
  9327. bounds = [];
  9328. break;
  9329. case 'M': // moveTo, absolute
  9330. x = current[1];
  9331. y = current[2];
  9332. subpathStartX = x;
  9333. subpathStartY = y;
  9334. bounds = [];
  9335. break;
  9336. case 'c': // bezierCurveTo, relative
  9337. tempX = x + current[5];
  9338. tempY = y + current[6];
  9339. controlX = x + current[3];
  9340. controlY = y + current[4];
  9341. bounds = fabric.util.getBoundsOfCurve(x, y,
  9342. x + current[1], // x1
  9343. y + current[2], // y1
  9344. controlX, // x2
  9345. controlY, // y2
  9346. tempX,
  9347. tempY
  9348. );
  9349. x = tempX;
  9350. y = tempY;
  9351. break;
  9352. case 'C': // bezierCurveTo, absolute
  9353. controlX = current[3];
  9354. controlY = current[4];
  9355. bounds = fabric.util.getBoundsOfCurve(x, y,
  9356. current[1],
  9357. current[2],
  9358. controlX,
  9359. controlY,
  9360. current[5],
  9361. current[6]
  9362. );
  9363. x = current[5];
  9364. y = current[6];
  9365. break;
  9366. case 's': // shorthand cubic bezierCurveTo, relative
  9367. // transform to absolute x,y
  9368. tempX = x + current[3];
  9369. tempY = y + current[4];
  9370. if (previous[0].match(/[CcSs]/) === null) {
  9371. // If there is no previous command or if the previous command was not a C, c, S, or s,
  9372. // the control point is coincident with the current point
  9373. controlX = x;
  9374. controlY = y;
  9375. }
  9376. else {
  9377. // calculate reflection of previous control points
  9378. controlX = 2 * x - controlX;
  9379. controlY = 2 * y - controlY;
  9380. }
  9381. bounds = fabric.util.getBoundsOfCurve(x, y,
  9382. controlX,
  9383. controlY,
  9384. x + current[1],
  9385. y + current[2],
  9386. tempX,
  9387. tempY
  9388. );
  9389. // set control point to 2nd one of this command
  9390. // "... the first control point is assumed to be
  9391. // the reflection of the second control point on
  9392. // the previous command relative to the current point."
  9393. controlX = x + current[1];
  9394. controlY = y + current[2];
  9395. x = tempX;
  9396. y = tempY;
  9397. break;
  9398. case 'S': // shorthand cubic bezierCurveTo, absolute
  9399. tempX = current[3];
  9400. tempY = current[4];
  9401. if (previous[0].match(/[CcSs]/) === null) {
  9402. // If there is no previous command or if the previous command was not a C, c, S, or s,
  9403. // the control point is coincident with the current point
  9404. controlX = x;
  9405. controlY = y;
  9406. }
  9407. else {
  9408. // calculate reflection of previous control points
  9409. controlX = 2 * x - controlX;
  9410. controlY = 2 * y - controlY;
  9411. }
  9412. bounds = fabric.util.getBoundsOfCurve(x, y,
  9413. controlX,
  9414. controlY,
  9415. current[1],
  9416. current[2],
  9417. tempX,
  9418. tempY
  9419. );
  9420. x = tempX;
  9421. y = tempY;
  9422. // set control point to 2nd one of this command
  9423. // "... the first control point is assumed to be
  9424. // the reflection of the second control point on
  9425. // the previous command relative to the current point."
  9426. controlX = current[1];
  9427. controlY = current[2];
  9428. break;
  9429. case 'q': // quadraticCurveTo, relative
  9430. // transform to absolute x,y
  9431. tempX = x + current[3];
  9432. tempY = y + current[4];
  9433. controlX = x + current[1];
  9434. controlY = y + current[2];
  9435. bounds = fabric.util.getBoundsOfCurve(x, y,
  9436. controlX,
  9437. controlY,
  9438. controlX,
  9439. controlY,
  9440. tempX,
  9441. tempY
  9442. );
  9443. x = tempX;
  9444. y = tempY;
  9445. break;
  9446. case 'Q': // quadraticCurveTo, absolute
  9447. controlX = current[1];
  9448. controlY = current[2];
  9449. bounds = fabric.util.getBoundsOfCurve(x, y,
  9450. controlX,
  9451. controlY,
  9452. controlX,
  9453. controlY,
  9454. current[3],
  9455. current[4]
  9456. );
  9457. x = current[3];
  9458. y = current[4];
  9459. break;
  9460. case 't': // shorthand quadraticCurveTo, relative
  9461. // transform to absolute x,y
  9462. tempX = x + current[1];
  9463. tempY = y + current[2];
  9464. if (previous[0].match(/[QqTt]/) === null) {
  9465. // If there is no previous command or if the previous command was not a Q, q, T or t,
  9466. // assume the control point is coincident with the current point
  9467. controlX = x;
  9468. controlY = y;
  9469. }
  9470. else {
  9471. // calculate reflection of previous control point
  9472. controlX = 2 * x - controlX;
  9473. controlY = 2 * y - controlY;
  9474. }
  9475. bounds = fabric.util.getBoundsOfCurve(x, y,
  9476. controlX,
  9477. controlY,
  9478. controlX,
  9479. controlY,
  9480. tempX,
  9481. tempY
  9482. );
  9483. x = tempX;
  9484. y = tempY;
  9485. break;
  9486. case 'T':
  9487. tempX = current[1];
  9488. tempY = current[2];
  9489. if (previous[0].match(/[QqTt]/) === null) {
  9490. // If there is no previous command or if the previous command was not a Q, q, T or t,
  9491. // assume the control point is coincident with the current point
  9492. controlX = x;
  9493. controlY = y;
  9494. }
  9495. else {
  9496. // calculate reflection of previous control point
  9497. controlX = 2 * x - controlX;
  9498. controlY = 2 * y - controlY;
  9499. }
  9500. bounds = fabric.util.getBoundsOfCurve(x, y,
  9501. controlX,
  9502. controlY,
  9503. controlX,
  9504. controlY,
  9505. tempX,
  9506. tempY
  9507. );
  9508. x = tempX;
  9509. y = tempY;
  9510. break;
  9511. case 'a':
  9512. // TODO: optimize this
  9513. bounds = fabric.util.getBoundsOfArc(x, y,
  9514. current[1],
  9515. current[2],
  9516. current[3],
  9517. current[4],
  9518. current[5],
  9519. current[6] + x,
  9520. current[7] + y
  9521. );
  9522. x += current[6];
  9523. y += current[7];
  9524. break;
  9525. case 'A':
  9526. // TODO: optimize this
  9527. bounds = fabric.util.getBoundsOfArc(x, y,
  9528. current[1],
  9529. current[2],
  9530. current[3],
  9531. current[4],
  9532. current[5],
  9533. current[6],
  9534. current[7]
  9535. );
  9536. x = current[6];
  9537. y = current[7];
  9538. break;
  9539. case 'z':
  9540. case 'Z':
  9541. x = subpathStartX;
  9542. y = subpathStartY;
  9543. break;
  9544. }
  9545. previous = current;
  9546. bounds.forEach(function (point) {
  9547. aX.push(point.x);
  9548. aY.push(point.y);
  9549. });
  9550. aX.push(x);
  9551. aY.push(y);
  9552. }
  9553. var minX = min(aX) || 0,
  9554. minY = min(aY) || 0,
  9555. maxX = max(aX) || 0,
  9556. maxY = max(aY) || 0,
  9557. deltaX = maxX - minX,
  9558. deltaY = maxY - minY,
  9559. o = {
  9560. left: minX,
  9561. top: minY,
  9562. width: deltaX,
  9563. height: deltaY
  9564. };
  9565. return o;
  9566. }
  9567. });
  9568. /**
  9569. * Creates an instance of fabric.Path from an object
  9570. * @static
  9571. * @memberOf fabric.Path
  9572. * @param {Object} object
  9573. * @param {Function} [callback] Callback to invoke when an fabric.Path instance is created
  9574. */
  9575. fabric.Path.fromObject = function(object, callback) {
  9576. if (typeof object.sourcePath === 'string') {
  9577. var pathUrl = object.sourcePath;
  9578. fabric.loadSVGFromURL(pathUrl, function (elements) {
  9579. var path = elements[0];
  9580. path.setOptions(object);
  9581. callback && callback(path);
  9582. });
  9583. }
  9584. else {
  9585. fabric.Object._fromObject('Path', object, callback, 'path');
  9586. }
  9587. };
  9588. })(typeof exports !== 'undefined' ? exports : this);
  9589. (function(global) {
  9590. 'use strict';
  9591. var fabric = global.fabric || (global.fabric = { }),
  9592. extend = fabric.util.object.extend,
  9593. min = fabric.util.array.min,
  9594. max = fabric.util.array.max;
  9595. if (fabric.Group) {
  9596. return;
  9597. }
  9598. /**
  9599. * Group class
  9600. * @class fabric.Group
  9601. * @extends fabric.Object
  9602. * @mixes fabric.Collection
  9603. * @tutorial {@link http://fabricjs.com/fabric-intro-part-3#groups}
  9604. * @see {@link fabric.Group#initialize} for constructor definition
  9605. */
  9606. fabric.Group = fabric.util.createClass(fabric.Object, fabric.Collection, /** @lends fabric.Group.prototype */ {
  9607. /**
  9608. * Type of an object
  9609. * @type String
  9610. * @default
  9611. */
  9612. type: 'group',
  9613. /**
  9614. * Width of stroke
  9615. * @type Number
  9616. * @default
  9617. */
  9618. strokeWidth: 0,
  9619. /**
  9620. * Indicates if click events should also check for subtargets
  9621. * @type Boolean
  9622. * @default
  9623. */
  9624. subTargetCheck: false,
  9625. /**
  9626. * Groups are container, do not render anything on theyr own, ence no cache properties
  9627. * @type Array
  9628. * @default
  9629. */
  9630. cacheProperties: [],
  9631. /**
  9632. * setOnGroup is a method used for TextBox that is no more used since 2.0.0 The behavior is still
  9633. * available setting this boolean to true.
  9634. * @type Boolean
  9635. * @since 2.0.0
  9636. * @default
  9637. */
  9638. useSetOnGroup: false,
  9639. /**
  9640. * Constructor
  9641. * @param {Object} objects Group objects
  9642. * @param {Object} [options] Options object
  9643. * @param {Boolean} [isAlreadyGrouped] if true, objects have been grouped already.
  9644. * @return {Object} thisArg
  9645. */
  9646. initialize: function(objects, options, isAlreadyGrouped) {
  9647. options = options || {};
  9648. this._objects = [];
  9649. // if objects enclosed in a group have been grouped already,
  9650. // we cannot change properties of objects.
  9651. // Thus we need to set options to group without objects,
  9652. isAlreadyGrouped && this.callSuper('initialize', options);
  9653. this._objects = objects || [];
  9654. for (var i = this._objects.length; i--; ) {
  9655. this._objects[i].group = this;
  9656. }
  9657. if (options.originX) {
  9658. this.originX = options.originX;
  9659. }
  9660. if (options.originY) {
  9661. this.originY = options.originY;
  9662. }
  9663. if (!isAlreadyGrouped) {
  9664. var center = options && options.centerPoint;
  9665. // if coming from svg i do not want to calc bounds.
  9666. // i assume width and height are passed along options
  9667. center || this._calcBounds();
  9668. this._updateObjectsCoords(center);
  9669. delete options.centerPoint;
  9670. this.callSuper('initialize', options);
  9671. }
  9672. else {
  9673. this._updateObjectsACoords();
  9674. }
  9675. this.setCoords();
  9676. },
  9677. /**
  9678. * @private
  9679. * @param {Boolean} [skipCoordsChange] if true, coordinates of objects enclosed in a group do not change
  9680. */
  9681. _updateObjectsACoords: function() {
  9682. var ignoreZoom = true, skipAbsolute = true;
  9683. for (var i = this._objects.length; i--; ){
  9684. this._objects[i].setCoords(ignoreZoom, skipAbsolute);
  9685. }
  9686. },
  9687. /**
  9688. * @private
  9689. * @param {Boolean} [skipCoordsChange] if true, coordinates of objects enclosed in a group do not change
  9690. */
  9691. _updateObjectsCoords: function(center) {
  9692. var center = center || this.getCenterPoint();
  9693. for (var i = this._objects.length; i--; ){
  9694. this._updateObjectCoords(this._objects[i], center);
  9695. }
  9696. },
  9697. /**
  9698. * @private
  9699. * @param {Object} object
  9700. * @param {fabric.Point} center, current center of group.
  9701. */
  9702. _updateObjectCoords: function(object, center) {
  9703. var objectLeft = object.left,
  9704. objectTop = object.top,
  9705. ignoreZoom = true, skipAbsolute = true;
  9706. object.set({
  9707. left: objectLeft - center.x,
  9708. top: objectTop - center.y
  9709. });
  9710. object.group = this;
  9711. object.setCoords(ignoreZoom, skipAbsolute);
  9712. },
  9713. /**
  9714. * Returns string represenation of a group
  9715. * @return {String}
  9716. */
  9717. toString: function() {
  9718. return '#<fabric.Group: (' + this.complexity() + ')>';
  9719. },
  9720. /**
  9721. * Adds an object to a group; Then recalculates group's dimension, position.
  9722. * @param {Object} object
  9723. * @return {fabric.Group} thisArg
  9724. * @chainable
  9725. */
  9726. addWithUpdate: function(object) {
  9727. this._restoreObjectsState();
  9728. fabric.util.resetObjectTransform(this);
  9729. if (object) {
  9730. this._objects.push(object);
  9731. object.group = this;
  9732. object._set('canvas', this.canvas);
  9733. }
  9734. this._calcBounds();
  9735. this._updateObjectsCoords();
  9736. this.setCoords();
  9737. this.dirty = true;
  9738. return this;
  9739. },
  9740. /**
  9741. * Removes an object from a group; Then recalculates group's dimension, position.
  9742. * @param {Object} object
  9743. * @return {fabric.Group} thisArg
  9744. * @chainable
  9745. */
  9746. removeWithUpdate: function(object) {
  9747. this._restoreObjectsState();
  9748. fabric.util.resetObjectTransform(this);
  9749. this.remove(object);
  9750. this._calcBounds();
  9751. this._updateObjectsCoords();
  9752. this.setCoords();
  9753. this.dirty = true;
  9754. return this;
  9755. },
  9756. /**
  9757. * @private
  9758. */
  9759. _onObjectAdded: function(object) {
  9760. this.dirty = true;
  9761. object.group = this;
  9762. object._set('canvas', this.canvas);
  9763. },
  9764. /**
  9765. * @private
  9766. */
  9767. _onObjectRemoved: function(object) {
  9768. this.dirty = true;
  9769. delete object.group;
  9770. },
  9771. /**
  9772. * @private
  9773. */
  9774. _set: function(key, value) {
  9775. var i = this._objects.length;
  9776. if (this.useSetOnGroup) {
  9777. while (i--) {
  9778. this._objects[i].setOnGroup(key, value);
  9779. }
  9780. }
  9781. if (key === 'canvas') {
  9782. i = this._objects.length;
  9783. while (i--) {
  9784. this._objects[i]._set(key, value);
  9785. }
  9786. }
  9787. this.callSuper('_set', key, value);
  9788. },
  9789. /**
  9790. * Returns object representation of an instance
  9791. * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
  9792. * @return {Object} object representation of an instance
  9793. */
  9794. toObject: function(propertiesToInclude) {
  9795. var objsToObject = this.getObjects().map(function(obj) {
  9796. var originalDefaults = obj.includeDefaultValues;
  9797. obj.includeDefaultValues = obj.group.includeDefaultValues;
  9798. var _obj = obj.toObject(propertiesToInclude);
  9799. obj.includeDefaultValues = originalDefaults;
  9800. return _obj;
  9801. });
  9802. return extend(this.callSuper('toObject', propertiesToInclude), {
  9803. objects: objsToObject
  9804. });
  9805. },
  9806. /**
  9807. * Returns object representation of an instance, in dataless mode.
  9808. * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
  9809. * @return {Object} object representation of an instance
  9810. */
  9811. toDatalessObject: function(propertiesToInclude) {
  9812. var objsToObject, sourcePath = this.sourcePath;
  9813. if (sourcePath) {
  9814. objsToObject = sourcePath;
  9815. }
  9816. else {
  9817. objsToObject = this.getObjects().map(function(obj) {
  9818. var originalDefaults = obj.includeDefaultValues;
  9819. obj.includeDefaultValues = obj.group.includeDefaultValues;
  9820. var _obj = obj.toDatalessObject(propertiesToInclude);
  9821. obj.includeDefaultValues = originalDefaults;
  9822. return _obj;
  9823. });
  9824. }
  9825. return extend(this.callSuper('toDatalessObject', propertiesToInclude), {
  9826. objects: objsToObject
  9827. });
  9828. },
  9829. /**
  9830. * Renders instance on a given context
  9831. * @param {CanvasRenderingContext2D} ctx context to render instance on
  9832. */
  9833. render: function(ctx) {
  9834. this._transformDone = true;
  9835. this.callSuper('render', ctx);
  9836. this._transformDone = false;
  9837. },
  9838. /**
  9839. * Decide if the object should cache or not. Create its own cache level
  9840. * objectCaching is a global flag, wins over everything
  9841. * needsItsOwnCache should be used when the object drawing method requires
  9842. * a cache step. None of the fabric classes requires it.
  9843. * Generally you do not cache objects in groups because the group outside is cached.
  9844. * @return {Boolean}
  9845. */
  9846. shouldCache: function() {
  9847. var ownCache = this.objectCaching && (!this.group || this.needsItsOwnCache() || !this.group.isOnACache());
  9848. this.ownCaching = ownCache;
  9849. if (ownCache) {
  9850. for (var i = 0, len = this._objects.length; i < len; i++) {
  9851. if (this._objects[i].willDrawShadow()) {
  9852. this.ownCaching = false;
  9853. return false;
  9854. }
  9855. }
  9856. }
  9857. return ownCache;
  9858. },
  9859. /**
  9860. * Check if this object or a child object will cast a shadow
  9861. * @return {Boolean}
  9862. */
  9863. willDrawShadow: function() {
  9864. if (this.shadow) {
  9865. return this.callSuper('willDrawShadow');
  9866. }
  9867. for (var i = 0, len = this._objects.length; i < len; i++) {
  9868. if (this._objects[i].willDrawShadow()) {
  9869. return true;
  9870. }
  9871. }
  9872. return false;
  9873. },
  9874. /**
  9875. * Check if this group or its parent group are caching, recursively up
  9876. * @return {Boolean}
  9877. */
  9878. isOnACache: function() {
  9879. return this.ownCaching || (this.group && this.group.isOnACache());
  9880. },
  9881. /**
  9882. * Execute the drawing operation for an object on a specified context
  9883. * @param {CanvasRenderingContext2D} ctx Context to render on
  9884. */
  9885. drawObject: function(ctx) {
  9886. for (var i = 0, len = this._objects.length; i < len; i++) {
  9887. this._objects[i].render(ctx);
  9888. }
  9889. },
  9890. /**
  9891. * Check if cache is dirty
  9892. */
  9893. isCacheDirty: function() {
  9894. if (this.callSuper('isCacheDirty')) {
  9895. return true;
  9896. }
  9897. if (!this.statefullCache) {
  9898. return false;
  9899. }
  9900. for (var i = 0, len = this._objects.length; i < len; i++) {
  9901. if (this._objects[i].isCacheDirty(true)) {
  9902. if (this._cacheCanvas) {
  9903. // if this group has not a cache canvas there is nothing to clean
  9904. var x = this.cacheWidth / this.zoomX, y = this.cacheHeight / this.zoomY;
  9905. this._cacheContext.clearRect(-x / 2, -y / 2, x, y);
  9906. }
  9907. return true;
  9908. }
  9909. }
  9910. return false;
  9911. },
  9912. /**
  9913. * Retores original state of each of group objects (original state is that which was before group was created).
  9914. * @private
  9915. * @return {fabric.Group} thisArg
  9916. * @chainable
  9917. */
  9918. _restoreObjectsState: function() {
  9919. this._objects.forEach(this._restoreObjectState, this);
  9920. return this;
  9921. },
  9922. /**
  9923. * Realises the transform from this group onto the supplied object
  9924. * i.e. it tells you what would happen if the supplied object was in
  9925. * the group, and then the group was destroyed. It mutates the supplied
  9926. * object.
  9927. * @param {fabric.Object} object
  9928. * @return {fabric.Object} transformedObject
  9929. */
  9930. realizeTransform: function(object) {
  9931. var matrix = object.calcTransformMatrix(),
  9932. options = fabric.util.qrDecompose(matrix),
  9933. center = new fabric.Point(options.translateX, options.translateY);
  9934. object.flipX = false;
  9935. object.flipY = false;
  9936. object.set('scaleX', options.scaleX);
  9937. object.set('scaleY', options.scaleY);
  9938. object.skewX = options.skewX;
  9939. object.skewY = options.skewY;
  9940. object.angle = options.angle;
  9941. object.setPositionByOrigin(center, 'center', 'center');
  9942. return object;
  9943. },
  9944. /**
  9945. * Restores original state of a specified object in group
  9946. * @private
  9947. * @param {fabric.Object} object
  9948. * @return {fabric.Group} thisArg
  9949. */
  9950. _restoreObjectState: function(object) {
  9951. this.realizeTransform(object);
  9952. object.setCoords();
  9953. delete object.group;
  9954. return this;
  9955. },
  9956. /**
  9957. * Destroys a group (restoring state of its objects)
  9958. * @return {fabric.Group} thisArg
  9959. * @chainable
  9960. */
  9961. destroy: function() {
  9962. // when group is destroyed objects needs to get a repaint to be eventually
  9963. // displayed on canvas.
  9964. this._objects.forEach(function(object) {
  9965. object.set('dirty', true);
  9966. });
  9967. return this._restoreObjectsState();
  9968. },
  9969. /**
  9970. * make a group an active selection, remove the group from canvas
  9971. * the group has to be on canvas for this to work.
  9972. * @return {fabric.ActiveSelection} thisArg
  9973. * @chainable
  9974. */
  9975. toActiveSelection: function() {
  9976. if (!this.canvas) {
  9977. return;
  9978. }
  9979. var objects = this._objects, canvas = this.canvas;
  9980. this._objects = [];
  9981. var options = this.toObject();
  9982. delete options.objects;
  9983. var activeSelection = new fabric.ActiveSelection([]);
  9984. activeSelection.set(options);
  9985. activeSelection.type = 'activeSelection';
  9986. canvas.remove(this);
  9987. objects.forEach(function(object) {
  9988. object.group = activeSelection;
  9989. object.dirty = true;
  9990. canvas.add(object);
  9991. });
  9992. activeSelection.canvas = canvas;
  9993. activeSelection._objects = objects;
  9994. canvas._activeObject = activeSelection;
  9995. activeSelection.setCoords();
  9996. return activeSelection;
  9997. },
  9998. /**
  9999. * Destroys a group (restoring state of its objects)
  10000. * @return {fabric.Group} thisArg
  10001. * @chainable
  10002. */
  10003. ungroupOnCanvas: function() {
  10004. return this._restoreObjectsState();
  10005. },
  10006. /**
  10007. * Sets coordinates of all objects inside group
  10008. * @return {fabric.Group} thisArg
  10009. * @chainable
  10010. */
  10011. setObjectsCoords: function() {
  10012. var ignoreZoom = true, skipAbsolute = true;
  10013. this.forEachObject(function(object) {
  10014. object.setCoords(ignoreZoom, skipAbsolute);
  10015. });
  10016. return this;
  10017. },
  10018. /**
  10019. * @private
  10020. */
  10021. _calcBounds: function(onlyWidthHeight) {
  10022. var aX = [],
  10023. aY = [],
  10024. o, prop,
  10025. props = ['tr', 'br', 'bl', 'tl'],
  10026. i = 0, iLen = this._objects.length,
  10027. j, jLen = props.length,
  10028. ignoreZoom = true;
  10029. for ( ; i < iLen; ++i) {
  10030. o = this._objects[i];
  10031. o.setCoords(ignoreZoom);
  10032. for (j = 0; j < jLen; j++) {
  10033. prop = props[j];
  10034. aX.push(o.oCoords[prop].x);
  10035. aY.push(o.oCoords[prop].y);
  10036. }
  10037. }
  10038. this.set(this._getBounds(aX, aY, onlyWidthHeight));
  10039. },
  10040. /**
  10041. * @private
  10042. */
  10043. _getBounds: function(aX, aY, onlyWidthHeight) {
  10044. var minXY = new fabric.Point(min(aX), min(aY)),
  10045. maxXY = new fabric.Point(max(aX), max(aY)),
  10046. obj = {
  10047. width: (maxXY.x - minXY.x) || 0,
  10048. height: (maxXY.y - minXY.y) || 0
  10049. };
  10050. if (!onlyWidthHeight) {
  10051. obj.left = minXY.x || 0;
  10052. obj.top = minXY.y || 0;
  10053. if (this.originX === 'center') {
  10054. obj.left += obj.width / 2;
  10055. }
  10056. if (this.originX === 'right') {
  10057. obj.left += obj.width;
  10058. }
  10059. if (this.originY === 'center') {
  10060. obj.top += obj.height / 2;
  10061. }
  10062. if (this.originY === 'bottom') {
  10063. obj.top += obj.height;
  10064. }
  10065. }
  10066. return obj;
  10067. },
  10068. /* _TO_SVG_START_ */
  10069. /**
  10070. * Returns svg representation of an instance
  10071. * @param {Function} [reviver] Method for further parsing of svg representation.
  10072. * @return {String} svg representation of an instance
  10073. */
  10074. toSVG: function(reviver) {
  10075. var markup = this._createBaseSVGMarkup();
  10076. markup.push(
  10077. '<g ', this.getSvgId(), 'transform="',
  10078. /* avoiding styles intentionally */
  10079. this.getSvgTransform(),
  10080. this.getSvgTransformMatrix(),
  10081. '" c_style="',
  10082. this.getSvgFilter(),
  10083. '">\n'
  10084. );
  10085. for (var i = 0, len = this._objects.length; i < len; i++) {
  10086. markup.push('\t', this._objects[i].toSVG(reviver));
  10087. }
  10088. markup.push('</g>\n');
  10089. return reviver ? reviver(markup.join('')) : markup.join('');
  10090. },
  10091. /* _TO_SVG_END_ */
  10092. });
  10093. /**
  10094. * Returns {@link fabric.Group} instance from an object representation
  10095. * @static
  10096. * @memberOf fabric.Group
  10097. * @param {Object} object Object to create a group from
  10098. * @param {Function} [callback] Callback to invoke when an group instance is created
  10099. */
  10100. fabric.Group.fromObject = function(object, callback) {
  10101. fabric.util.enlivenObjects(object.objects, function(enlivenedObjects) {
  10102. var options = fabric.util.object.clone(object, true);
  10103. delete options.objects;
  10104. callback && callback(new fabric.Group(enlivenedObjects, options, true));
  10105. });
  10106. };
  10107. })(typeof exports !== 'undefined' ? exports : this);
  10108. (function(global) {
  10109. 'use strict';
  10110. var extend = fabric.util.object.extend;
  10111. if (!global.fabric) {
  10112. global.fabric = { };
  10113. }
  10114. if (global.fabric.Image) {
  10115. fabric.warn('fabric.Image is already defined.');
  10116. return;
  10117. }
  10118. /**
  10119. * Image class
  10120. * @class fabric.Image
  10121. * @extends fabric.Object
  10122. * @tutorial {@link http://fabricjs.com/fabric-intro-part-1#images}
  10123. * @see {@link fabric.Image#initialize} for constructor definition
  10124. */
  10125. fabric.Image = fabric.util.createClass(fabric.Object, /** @lends fabric.Image.prototype */ {
  10126. /**
  10127. * Type of an object
  10128. * @type String
  10129. * @default
  10130. */
  10131. type: 'image',
  10132. /**
  10133. * crossOrigin value (one of "", "anonymous", "use-credentials")
  10134. * @see https://developer.mozilla.org/en-US/docs/HTML/CORS_settings_attributes
  10135. * @type String
  10136. * @default
  10137. */
  10138. crossOrigin: '',
  10139. /**
  10140. * Width of a stroke.
  10141. * For c_image quality a stroke multiple of 2 gives better results.
  10142. * @type Number
  10143. * @default
  10144. */
  10145. strokeWidth: 0,
  10146. /**
  10147. * private
  10148. * contains last value of scaleX to detect
  10149. * if the Image got resized after the last Render
  10150. * @type Number
  10151. */
  10152. _lastScaleX: 1,
  10153. /**
  10154. * private
  10155. * contains last value of scaleY to detect
  10156. * if the Image got resized after the last Render
  10157. * @type Number
  10158. */
  10159. _lastScaleY: 1,
  10160. /**
  10161. * private
  10162. * contains last value of scaling applied by the apply filter chain
  10163. * @type Number
  10164. */
  10165. _filterScalingX: 1,
  10166. /**
  10167. * private
  10168. * contains last value of scaling applied by the apply filter chain
  10169. * @type Number
  10170. */
  10171. _filterScalingY: 1,
  10172. /**
  10173. * minimum scale factor under which any resizeFilter is triggered to resize the c_image
  10174. * 0 will disable the automatic resize. 1 will trigger automatically always.
  10175. * number bigger than 1 are not implemented yet.
  10176. * @type Number
  10177. */
  10178. minimumScaleTrigger: 0.5,
  10179. /**
  10180. * List of properties to consider when checking if
  10181. * state of an object is changed ({@link fabric.Object#hasStateChanged})
  10182. * as well as for history (undo/redo) purposes
  10183. * @type Array
  10184. */
  10185. stateProperties: fabric.Object.prototype.stateProperties.concat('cropX', 'cropY'),
  10186. /**
  10187. * When `true`, object is cached on an additional canvas.
  10188. * default to false for images
  10189. * since 1.7.0
  10190. * @type Boolean
  10191. * @default
  10192. */
  10193. objectCaching: false,
  10194. /**
  10195. * key used to retrieve the texture representing this c_image
  10196. * since 2.0.0
  10197. * @type String
  10198. * @default
  10199. */
  10200. cacheKey: '',
  10201. /**
  10202. * Image crop in pixels from original c_image size.
  10203. * since 2.0.0
  10204. * @type Number
  10205. * @default
  10206. */
  10207. cropX: 0,
  10208. /**
  10209. * Image crop in pixels from original c_image size.
  10210. * since 2.0.0
  10211. * @type Number
  10212. * @default
  10213. */
  10214. cropY: 0,
  10215. /**
  10216. * Constructor
  10217. * @param {HTMLImageElement | String} element Image element
  10218. * @param {Object} [options] Options object
  10219. * @param {function} [callback] callback function to call after eventual filters applied.
  10220. * @return {fabric.Image} thisArg
  10221. */
  10222. initialize: function(element, options) {
  10223. options || (options = { });
  10224. this.filters = [];
  10225. this.cacheKey = 'texture' + fabric.Object.__uid++;
  10226. this.callSuper('initialize', options);
  10227. this._initElement(element, options);
  10228. },
  10229. /**
  10230. * Returns c_image element which this instance if based on
  10231. * @return {HTMLImageElement} Image element
  10232. */
  10233. getElement: function() {
  10234. return this._element;
  10235. },
  10236. /**
  10237. * Sets c_image element for this instance to a specified one.
  10238. * If filters defined they are applied to new c_image.
  10239. * You might need to call `canvas.renderAll` and `object.setCoords` after replacing, to render new c_image and update controls area.
  10240. * @param {HTMLImageElement} element
  10241. * @param {Object} [options] Options object
  10242. * @return {fabric.Image} thisArg
  10243. * @chainable
  10244. */
  10245. setElement: function(element, options) {
  10246. var backend = fabric.filterBackend;
  10247. if (backend && backend.evictCachesForKey) {
  10248. backend.evictCachesForKey(this.cacheKey);
  10249. backend.evictCachesForKey(this.cacheKey + '_filtered');
  10250. }
  10251. this._element = element;
  10252. this._originalElement = element;
  10253. this._initConfig(options);
  10254. if (this.resizeFilter) {
  10255. this.applyResizeFilters();
  10256. }
  10257. if (this.filters.length !== 0) {
  10258. this.applyFilters();
  10259. }
  10260. return this;
  10261. },
  10262. /**
  10263. * Delete cacheKey if we have a webGlBackend
  10264. * delete reference to c_image elements
  10265. */
  10266. dispose: function() {
  10267. var backend = fabric.filterBackend;
  10268. if (backend && backend.evictCachesForKey) {
  10269. backend.evictCachesForKey(this.cacheKey);
  10270. backend.evictCachesForKey(this.cacheKey + '_filtered');
  10271. }
  10272. this._originalElement = undefined;
  10273. this._element = undefined;
  10274. this._filteredEl = undefined;
  10275. },
  10276. /**
  10277. * Sets crossOrigin value (on an instance and corresponding c_image element)
  10278. * @return {fabric.Image} thisArg
  10279. * @chainable
  10280. */
  10281. setCrossOrigin: function(value) {
  10282. this.crossOrigin = value;
  10283. this._element.crossOrigin = value;
  10284. return this;
  10285. },
  10286. /**
  10287. * Returns original size of an c_image
  10288. * @return {Object} Object with "width" and "height" properties
  10289. */
  10290. getOriginalSize: function() {
  10291. var element = this.getElement();
  10292. return {
  10293. width: element.width,
  10294. height: element.height
  10295. };
  10296. },
  10297. /**
  10298. * @private
  10299. * @param {CanvasRenderingContext2D} ctx Context to render on
  10300. */
  10301. _stroke: function(ctx) {
  10302. if (!this.stroke || this.strokeWidth === 0) {
  10303. return;
  10304. }
  10305. var w = this.width / 2, h = this.height / 2;
  10306. ctx.beginPath();
  10307. ctx.moveTo(-w, -h);
  10308. ctx.lineTo(w, -h);
  10309. ctx.lineTo(w, h);
  10310. ctx.lineTo(-w, h);
  10311. ctx.lineTo(-w, -h);
  10312. ctx.closePath();
  10313. },
  10314. /**
  10315. * @private
  10316. * @param {CanvasRenderingContext2D} ctx Context to render on
  10317. */
  10318. _renderDashedStroke: function(ctx) {
  10319. var x = -this.width / 2,
  10320. y = -this.height / 2,
  10321. w = this.width,
  10322. h = this.height;
  10323. ctx.save();
  10324. this._setStrokeStyles(ctx, this);
  10325. ctx.beginPath();
  10326. fabric.util.drawDashedLine(ctx, x, y, x + w, y, this.strokeDashArray);
  10327. fabric.util.drawDashedLine(ctx, x + w, y, x + w, y + h, this.strokeDashArray);
  10328. fabric.util.drawDashedLine(ctx, x + w, y + h, x, y + h, this.strokeDashArray);
  10329. fabric.util.drawDashedLine(ctx, x, y + h, x, y, this.strokeDashArray);
  10330. ctx.closePath();
  10331. ctx.restore();
  10332. },
  10333. /**
  10334. * Returns object representation of an instance
  10335. * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
  10336. * @return {Object} Object representation of an instance
  10337. */
  10338. toObject: function(propertiesToInclude) {
  10339. var filters = [];
  10340. this.filters.forEach(function(filterObj) {
  10341. if (filterObj) {
  10342. filters.push(filterObj.toObject());
  10343. }
  10344. });
  10345. var object = extend(
  10346. this.callSuper(
  10347. 'toObject',
  10348. ['crossOrigin', 'cropX', 'cropY'].concat(propertiesToInclude)
  10349. ), {
  10350. src: this.getSrc(),
  10351. filters: filters,
  10352. });
  10353. if (this.resizeFilter) {
  10354. object.resizeFilter = this.resizeFilter.toObject();
  10355. }
  10356. return object;
  10357. },
  10358. /**
  10359. * Returns true if an c_image has crop applied, inspecting values of cropX,cropY,width,hight.
  10360. * @return {Boolean}
  10361. */
  10362. hasCrop: function() {
  10363. return this.cropX || this.cropY || this.width < this._element.width || this.height < this._element.height;
  10364. },
  10365. /* _TO_SVG_START_ */
  10366. /**
  10367. * Returns SVG representation of an instance
  10368. * @param {Function} [reviver] Method for further parsing of svg representation.
  10369. * @return {String} svg representation of an instance
  10370. */
  10371. toSVG: function(reviver) {
  10372. var markup = this._createBaseSVGMarkup(), x = -this.width / 2, y = -this.height / 2, clipPath = '';
  10373. if (this.hasCrop()) {
  10374. var clipPathId = fabric.Object.__uid++;
  10375. markup.push(
  10376. '<clipPath id="imageCrop_' + clipPathId + '">\n',
  10377. '\t<rect x="' + x + '" y="' + y + '" width="' + this.width + '" height="' + this.height + '" />\n',
  10378. '</clipPath>\n'
  10379. );
  10380. clipPath = ' clip-path="url(#imageCrop_' + clipPathId + ')" ';
  10381. }
  10382. markup.push('<g transform="', this.getSvgTransform(), this.getSvgTransformMatrix(), '">\n');
  10383. var imageMarkup = ['\t<c_image ', this.getSvgId(), 'xlink:href="', this.getSvgSrc(true),
  10384. '" x="', x - this.cropX, '" y="', y - this.cropY,
  10385. '" c_style="', this.getSvgStyles(),
  10386. // we're essentially moving origin of transformation from top/left corner to the center of the shape
  10387. // by wrapping it in container <g> element with actual transformation, then offsetting object to the top/left
  10388. // so that object's center aligns with container's left/top
  10389. '" width="', this._element.width || this._element.naturalWidth,
  10390. '" height="', this._element.height || this._element.height,
  10391. '"', clipPath,
  10392. '></c_image>\n'];
  10393. if (this.paintFirst === 'fill') {
  10394. Array.prototype.push.apply(markup, imageMarkup);
  10395. }
  10396. if (this.stroke || this.strokeDashArray) {
  10397. var origFill = this.fill;
  10398. this.fill = null;
  10399. markup.push(
  10400. '\t<rect ',
  10401. 'x="', x, '" y="', y,
  10402. '" width="', this.width, '" height="', this.height,
  10403. '" c_style="', this.getSvgStyles(),
  10404. '"/>\n'
  10405. );
  10406. this.fill = origFill;
  10407. }
  10408. if (this.paintFirst !== 'fill') {
  10409. Array.prototype.push.apply(markup, imageMarkup);
  10410. }
  10411. markup.push('</g>\n');
  10412. return reviver ? reviver(markup.join('')) : markup.join('');
  10413. },
  10414. /* _TO_SVG_END_ */
  10415. /**
  10416. * Returns source of an c_image
  10417. * @param {Boolean} filtered indicates if the src is needed for svg
  10418. * @return {String} Source of an c_image
  10419. */
  10420. getSrc: function(filtered) {
  10421. var element = filtered ? this._element : this._originalElement;
  10422. if (element) {
  10423. if (element.toDataURL) {
  10424. return element.toDataURL();
  10425. }
  10426. return element.src;
  10427. }
  10428. else {
  10429. return this.src || '';
  10430. }
  10431. },
  10432. /**
  10433. * Sets source of an c_image
  10434. * @param {String} src Source string (URL)
  10435. * @param {Function} [callback] Callback is invoked when c_image has been loaded (and all filters have been applied)
  10436. * @param {Object} [options] Options object
  10437. * @return {fabric.Image} thisArg
  10438. * @chainable
  10439. */
  10440. setSrc: function(src, callback, options) {
  10441. fabric.util.loadImage(src, function(img) {
  10442. this.setElement(img, options);
  10443. this._setWidthHeight();
  10444. callback(this);
  10445. }, this, options && options.crossOrigin);
  10446. return this;
  10447. },
  10448. /**
  10449. * Returns string representation of an instance
  10450. * @return {String} String representation of an instance
  10451. */
  10452. toString: function() {
  10453. return '#<fabric.Image: { src: "' + this.getSrc() + '" }>';
  10454. },
  10455. applyResizeFilters: function() {
  10456. var filter = this.resizeFilter,
  10457. retinaScaling = this.canvas ? this.canvas.getRetinaScaling() : 1,
  10458. minimumScale = this.minimumScaleTrigger,
  10459. scaleX = this.scaleX * retinaScaling,
  10460. scaleY = this.scaleY * retinaScaling,
  10461. elementToFilter = this._filteredEl || this._originalElement;
  10462. if (this.group) {
  10463. this.set('dirty', true);
  10464. }
  10465. if (!filter || (scaleX > minimumScale && scaleY > minimumScale)) {
  10466. this._element = elementToFilter;
  10467. this._filterScalingX = 1;
  10468. this._filterScalingY = 1;
  10469. return;
  10470. }
  10471. if (!fabric.filterBackend) {
  10472. fabric.filterBackend = fabric.initFilterBackend();
  10473. }
  10474. var canvasEl = fabric.util.createCanvasElement(),
  10475. cacheKey = this._filteredEl ? this.cacheKey : (this.cacheKey + '_filtered'),
  10476. sourceWidth = elementToFilter.width, sourceHeight = elementToFilter.height;
  10477. canvasEl.width = sourceWidth;
  10478. canvasEl.height = sourceHeight;
  10479. this._element = canvasEl;
  10480. filter.scaleX = scaleX;
  10481. filter.scaleY = scaleY;
  10482. fabric.filterBackend.applyFilters(
  10483. [filter], elementToFilter, sourceWidth, sourceHeight, this._element, cacheKey);
  10484. this._filterScalingX = canvasEl.width / this._originalElement.width;
  10485. this._filterScalingY = canvasEl.height / this._originalElement.height;
  10486. },
  10487. /**
  10488. * Applies filters assigned to this c_image (from "filters" array) or from filter param
  10489. * @method applyFilters
  10490. * @param {Array} filters to be applied
  10491. * @param {Boolean} forResizing specify if the filter operation is a resize operation
  10492. * @return {thisArg} return the fabric.Image object
  10493. * @chainable
  10494. */
  10495. applyFilters: function(filters) {
  10496. filters = filters || this.filters || [];
  10497. filters = filters.filter(function(filter) { return filter; });
  10498. if (this.group) {
  10499. this.set('dirty', true);
  10500. }
  10501. if (filters.length === 0) {
  10502. this._element = this._originalElement;
  10503. this._filteredEl = null;
  10504. this._filterScalingX = 1;
  10505. this._filterScalingY = 1;
  10506. return this;
  10507. }
  10508. var imgElement = this._originalElement,
  10509. sourceWidth = imgElement.naturalWidth || imgElement.width,
  10510. sourceHeight = imgElement.naturalHeight || imgElement.height;
  10511. if (this._element === this._originalElement) {
  10512. // if the element is the same we need to create a new element
  10513. var canvasEl = fabric.util.createCanvasElement();
  10514. canvasEl.width = sourceWidth;
  10515. canvasEl.height = sourceHeight;
  10516. this._element = canvasEl;
  10517. this._filteredEl = canvasEl;
  10518. }
  10519. else {
  10520. // clear the existing element to get new filter data
  10521. this._element.getContext('2d').clearRect(0, 0, sourceWidth, sourceHeight);
  10522. }
  10523. if (!fabric.filterBackend) {
  10524. fabric.filterBackend = fabric.initFilterBackend();
  10525. }
  10526. fabric.filterBackend.applyFilters(
  10527. filters, this._originalElement, sourceWidth, sourceHeight, this._element, this.cacheKey);
  10528. if (this._originalElement.width !== this._element.width ||
  10529. this._originalElement.height !== this._element.height) {
  10530. this._filterScalingX = this._element.width / this._originalElement.width;
  10531. this._filterScalingY = this._element.height / this._originalElement.height;
  10532. }
  10533. return this;
  10534. },
  10535. /**
  10536. * @private
  10537. * @param {CanvasRenderingContext2D} ctx Context to render on
  10538. */
  10539. _render: function(ctx) {
  10540. if (this.isMoving === false && this.resizeFilter && this._needsResize()) {
  10541. this._lastScaleX = this.scaleX;
  10542. this._lastScaleY = this.scaleY;
  10543. this.applyResizeFilters();
  10544. }
  10545. this._stroke(ctx);
  10546. this._renderPaintInOrder(ctx);
  10547. },
  10548. _renderFill: function(ctx) {
  10549. var w = this.width, h = this.height, sW = w * this._filterScalingX, sH = h * this._filterScalingY,
  10550. x = -w / 2, y = -h / 2, elementToDraw = this._element;
  10551. elementToDraw && ctx.drawImage(elementToDraw,
  10552. this.cropX * this._filterScalingX,
  10553. this.cropY * this._filterScalingY,
  10554. sW,
  10555. sH,
  10556. x, y, w, h);
  10557. },
  10558. /**
  10559. * @private, needed to check if c_image needs resize
  10560. */
  10561. _needsResize: function() {
  10562. return (this.scaleX !== this._lastScaleX || this.scaleY !== this._lastScaleY);
  10563. },
  10564. /**
  10565. * @private
  10566. */
  10567. _resetWidthHeight: function() {
  10568. var element = this.getElement();
  10569. this.set('width', element.width);
  10570. this.set('height', element.height);
  10571. },
  10572. /**
  10573. * The Image class's initialization method. This method is automatically
  10574. * called by the constructor.
  10575. * @private
  10576. * @param {HTMLImageElement|String} element The element representing the c_image
  10577. * @param {Object} [options] Options object
  10578. */
  10579. _initElement: function(element, options) {
  10580. this.setElement(fabric.util.getById(element), options);
  10581. fabric.util.addClass(this.getElement(), fabric.Image.CSS_CANVAS);
  10582. },
  10583. /**
  10584. * @private
  10585. * @param {Object} [options] Options object
  10586. */
  10587. _initConfig: function(options) {
  10588. options || (options = { });
  10589. this.setOptions(options);
  10590. this._setWidthHeight(options);
  10591. if (this._element && this.crossOrigin) {
  10592. this._element.crossOrigin = this.crossOrigin;
  10593. }
  10594. },
  10595. /**
  10596. * @private
  10597. * @param {Array} filters to be initialized
  10598. * @param {Function} callback Callback to invoke when all fabric.Image.filters instances are created
  10599. */
  10600. _initFilters: function(filters, callback) {
  10601. if (filters && filters.length) {
  10602. fabric.util.enlivenObjects(filters, function(enlivenedObjects) {
  10603. callback && callback(enlivenedObjects);
  10604. }, 'fabric.Image.filters');
  10605. }
  10606. else {
  10607. callback && callback();
  10608. }
  10609. },
  10610. /**
  10611. * @private
  10612. * @param {Object} [options] Object with width/height properties
  10613. */
  10614. _setWidthHeight: function(options) {
  10615. this.width = options && ('width' in options)
  10616. ? options.width
  10617. : (this.getElement()
  10618. ? this.getElement().width || 0
  10619. : 0);
  10620. this.height = options && ('height' in options)
  10621. ? options.height
  10622. : (this.getElement()
  10623. ? this.getElement().height || 0
  10624. : 0);
  10625. },
  10626. /**
  10627. * Calculate offset for center and scale factor for the c_image in order to respect
  10628. * the preserveAspectRatio attribute
  10629. * @private
  10630. * @return {Object}
  10631. */
  10632. parsePreserveAspectRatioAttribute: function() {
  10633. var pAR = fabric.util.parsePreserveAspectRatioAttribute(this.preserveAspectRatio || ''),
  10634. rWidth = this._element.width, rHeight = this._element.height,
  10635. scaleX = 1, scaleY = 1, offsetLeft = 0, offsetTop = 0, cropX = 0, cropY = 0,
  10636. offset, pWidth = this.width, pHeight = this.height, parsedAttributes = { width: pWidth, height: pHeight };
  10637. if (pAR && (pAR.alignX !== 'none' || pAR.alignY !== 'none')) {
  10638. if (pAR.meetOrSlice === 'meet') {
  10639. scaleX = scaleY = fabric.util.findScaleToFit(this._element, parsedAttributes);
  10640. offset = (pWidth - rWidth * scaleX) / 2;
  10641. if (pAR.alignX === 'Min') {
  10642. offsetLeft = -offset;
  10643. }
  10644. if (pAR.alignX === 'Max') {
  10645. offsetLeft = offset;
  10646. }
  10647. offset = (pHeight - rHeight * scaleY) / 2;
  10648. if (pAR.alignY === 'Min') {
  10649. offsetTop = -offset;
  10650. }
  10651. if (pAR.alignY === 'Max') {
  10652. offsetTop = offset;
  10653. }
  10654. }
  10655. if (pAR.meetOrSlice === 'slice') {
  10656. scaleX = scaleY = fabric.util.findScaleToCover(this._element, parsedAttributes);
  10657. offset = rWidth - pWidth / scaleX;
  10658. if (pAR.alignX === 'Mid') {
  10659. cropX = offset / 2;
  10660. }
  10661. if (pAR.alignX === 'Max') {
  10662. cropX = offset;
  10663. }
  10664. offset = rHeight - pHeight / scaleY;
  10665. if (pAR.alignY === 'Mid') {
  10666. cropY = offset / 2;
  10667. }
  10668. if (pAR.alignY === 'Max') {
  10669. cropY = offset;
  10670. }
  10671. rWidth = pWidth / scaleX;
  10672. rHeight = pHeight / scaleY;
  10673. }
  10674. }
  10675. else {
  10676. scaleX = pWidth / rWidth;
  10677. scaleY = pHeight / rHeight;
  10678. }
  10679. return {
  10680. width: rWidth,
  10681. height: rHeight,
  10682. scaleX: scaleX,
  10683. scaleY: scaleY,
  10684. offsetLeft: offsetLeft,
  10685. offsetTop: offsetTop,
  10686. cropX: cropX,
  10687. cropY: cropY
  10688. };
  10689. }
  10690. });
  10691. /**
  10692. * Default CSS class name for canvas
  10693. * @static
  10694. * @type String
  10695. * @default
  10696. */
  10697. fabric.Image.CSS_CANVAS = 'canvas-img';
  10698. /**
  10699. * Alias for getSrc
  10700. * @static
  10701. */
  10702. fabric.Image.prototype.getSvgSrc = fabric.Image.prototype.getSrc;
  10703. /**
  10704. * Creates an instance of fabric.Image from its object representation
  10705. * @static
  10706. * @param {Object} object Object to create an instance from
  10707. * @param {Function} callback Callback to invoke when an c_image instance is created
  10708. */
  10709. fabric.Image.fromObject = function(_object, callback) {
  10710. var object = fabric.util.object.clone(_object);
  10711. fabric.util.loadImage(object.src, function(img, error) {
  10712. if (error) {
  10713. callback && callback(null, error);
  10714. return;
  10715. }
  10716. fabric.Image.prototype._initFilters.call(object, object.filters, function(filters) {
  10717. object.filters = filters || [];
  10718. fabric.Image.prototype._initFilters.call(object, [object.resizeFilter], function(resizeFilters) {
  10719. object.resizeFilter = resizeFilters[0];
  10720. var image = new fabric.Image(img, object);
  10721. callback(image);
  10722. });
  10723. });
  10724. }, null, object.crossOrigin);
  10725. };
  10726. /**
  10727. * Creates an instance of fabric.Image from an URL string
  10728. * @static
  10729. * @param {String} url URL to create an c_image from
  10730. * @param {Function} [callback] Callback to invoke when c_image is created (newly created c_image is passed as a first argument)
  10731. * @param {Object} [imgOptions] Options object
  10732. */
  10733. fabric.Image.fromURL = function(url, callback, imgOptions) {
  10734. fabric.util.loadImage(url, function(img) {
  10735. callback && callback(new fabric.Image(img, imgOptions));
  10736. }, null, imgOptions && imgOptions.crossOrigin);
  10737. };
  10738. })(typeof exports !== 'undefined' ? exports : this);