html2canvas.svg.js 547 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124912591269127912891299130913191329133913491359136913791389139914091419142914391449145914691479148914991509151915291539154915591569157915891599160916191629163916491659166916791689169917091719172917391749175917691779178917991809181918291839184918591869187918891899190919191929193919491959196919791989199920092019202920392049205920692079208920992109211921292139214921592169217921892199220922192229223922492259226922792289229923092319232923392349235923692379238923992409241924292439244924592469247924892499250925192529253925492559256925792589259926092619262926392649265926692679268926992709271927292739274927592769277927892799280928192829283928492859286928792889289929092919292929392949295929692979298929993009301930293039304930593069307930893099310931193129313931493159316931793189319932093219322932393249325932693279328932993309331933293339334933593369337933893399340934193429343934493459346934793489349935093519352935393549355935693579358935993609361936293639364936593669367936893699370937193729373937493759376937793789379938093819382938393849385938693879388938993909391939293939394939593969397939893999400940194029403940494059406940794089409941094119412941394149415941694179418941994209421942294239424942594269427942894299430943194329433943494359436943794389439944094419442944394449445944694479448944994509451945294539454945594569457945894599460946194629463946494659466946794689469947094719472947394749475947694779478947994809481948294839484948594869487948894899490949194929493949494959496949794989499950095019502950395049505950695079508950995109511951295139514951595169517951895199520952195229523952495259526952795289529953095319532953395349535953695379538953995409541954295439544954595469547954895499550955195529553955495559556955795589559956095619562956395649565956695679568956995709571957295739574957595769577957895799580958195829583958495859586958795889589959095919592959395949595959695979598959996009601960296039604960596069607960896099610961196129613961496159616961796189619962096219622962396249625962696279628962996309631963296339634963596369637963896399640964196429643964496459646964796489649965096519652965396549655965696579658965996609661966296639664966596669667966896699670967196729673967496759676967796789679968096819682968396849685968696879688968996909691969296939694969596969697969896999700970197029703970497059706970797089709971097119712971397149715971697179718971997209721972297239724972597269727972897299730973197329733973497359736973797389739974097419742974397449745974697479748974997509751975297539754975597569757975897599760976197629763976497659766976797689769977097719772977397749775977697779778977997809781978297839784978597869787978897899790979197929793979497959796979797989799980098019802980398049805980698079808980998109811981298139814981598169817981898199820982198229823982498259826982798289829983098319832983398349835983698379838983998409841984298439844984598469847984898499850985198529853985498559856985798589859986098619862986398649865986698679868986998709871987298739874987598769877987898799880988198829883988498859886988798889889989098919892989398949895989698979898989999009901990299039904990599069907990899099910991199129913991499159916991799189919992099219922992399249925992699279928992999309931993299339934993599369937993899399940994199429943994499459946994799489949995099519952995399549955995699579958995999609961996299639964996599669967996899699970997199729973997499759976997799789979998099819982998399849985998699879988998999909991999299939994999599969997999899991000010001100021000310004100051000610007100081000910010100111001210013100141001510016100171001810019100201002110022100231002410025100261002710028100291003010031100321003310034100351003610037100381003910040100411004210043100441004510046100471004810049100501005110052100531005410055100561005710058100591006010061100621006310064100651006610067100681006910070100711007210073100741007510076100771007810079100801008110082100831008410085100861008710088100891009010091100921009310094100951009610097100981009910100101011010210103101041010510106101071010810109101101011110112101131011410115101161011710118101191012010121101221012310124101251012610127101281012910130101311013210133101341013510136101371013810139101401014110142101431014410145101461014710148101491015010151101521015310154101551015610157101581015910160101611016210163101641016510166101671016810169101701017110172101731017410175101761017710178101791018010181101821018310184101851018610187101881018910190101911019210193101941019510196101971019810199102001020110202102031020410205102061020710208102091021010211102121021310214102151021610217102181021910220102211022210223102241022510226102271022810229102301023110232102331023410235102361023710238102391024010241102421024310244102451024610247102481024910250102511025210253102541025510256102571025810259102601026110262102631026410265102661026710268102691027010271102721027310274102751027610277102781027910280102811028210283102841028510286102871028810289102901029110292102931029410295102961029710298102991030010301103021030310304103051030610307103081030910310103111031210313103141031510316103171031810319103201032110322103231032410325103261032710328103291033010331103321033310334103351033610337103381033910340103411034210343103441034510346103471034810349103501035110352103531035410355103561035710358103591036010361103621036310364103651036610367103681036910370103711037210373103741037510376103771037810379103801038110382103831038410385103861038710388103891039010391103921039310394103951039610397103981039910400104011040210403104041040510406104071040810409104101041110412104131041410415104161041710418104191042010421104221042310424104251042610427104281042910430104311043210433104341043510436104371043810439104401044110442104431044410445104461044710448104491045010451104521045310454104551045610457104581045910460104611046210463104641046510466104671046810469104701047110472104731047410475104761047710478104791048010481104821048310484104851048610487104881048910490104911049210493104941049510496104971049810499105001050110502105031050410505105061050710508105091051010511105121051310514105151051610517105181051910520105211052210523105241052510526105271052810529105301053110532105331053410535105361053710538105391054010541105421054310544105451054610547105481054910550105511055210553105541055510556105571055810559105601056110562105631056410565105661056710568105691057010571105721057310574105751057610577105781057910580105811058210583105841058510586105871058810589105901059110592105931059410595105961059710598105991060010601106021060310604106051060610607106081060910610106111061210613106141061510616106171061810619106201062110622106231062410625106261062710628106291063010631106321063310634106351063610637106381063910640106411064210643106441064510646106471064810649106501065110652106531065410655106561065710658106591066010661106621066310664106651066610667106681066910670106711067210673106741067510676106771067810679106801068110682106831068410685106861068710688106891069010691106921069310694106951069610697106981069910700107011070210703107041070510706107071070810709107101071110712107131071410715107161071710718107191072010721107221072310724107251072610727107281072910730107311073210733107341073510736107371073810739107401074110742107431074410745107461074710748107491075010751107521075310754107551075610757107581075910760107611076210763107641076510766107671076810769107701077110772107731077410775107761077710778107791078010781107821078310784107851078610787107881078910790107911079210793107941079510796107971079810799108001080110802108031080410805108061080710808108091081010811108121081310814108151081610817108181081910820108211082210823108241082510826108271082810829108301083110832108331083410835108361083710838108391084010841108421084310844108451084610847108481084910850108511085210853108541085510856108571085810859108601086110862108631086410865108661086710868108691087010871108721087310874108751087610877108781087910880108811088210883108841088510886108871088810889108901089110892108931089410895108961089710898108991090010901109021090310904109051090610907109081090910910109111091210913109141091510916109171091810919109201092110922109231092410925109261092710928109291093010931109321093310934109351093610937109381093910940109411094210943109441094510946109471094810949109501095110952109531095410955109561095710958109591096010961109621096310964109651096610967109681096910970109711097210973109741097510976109771097810979109801098110982109831098410985109861098710988109891099010991109921099310994109951099610997109981099911000110011100211003110041100511006110071100811009110101101111012110131101411015110161101711018110191102011021110221102311024110251102611027110281102911030110311103211033110341103511036110371103811039110401104111042110431104411045110461104711048110491105011051110521105311054110551105611057110581105911060110611106211063110641106511066110671106811069110701107111072110731107411075110761107711078110791108011081110821108311084110851108611087110881108911090110911109211093110941109511096110971109811099111001110111102111031110411105111061110711108111091111011111111121111311114111151111611117111181111911120111211112211123111241112511126111271112811129111301113111132111331113411135111361113711138111391114011141111421114311144111451114611147111481114911150111511115211153111541115511156111571115811159111601116111162111631116411165111661116711168111691117011171111721117311174111751117611177111781117911180111811118211183111841118511186111871118811189111901119111192111931119411195111961119711198111991120011201112021120311204112051120611207112081120911210112111121211213112141121511216112171121811219112201122111222112231122411225112261122711228112291123011231112321123311234112351123611237112381123911240112411124211243112441124511246112471124811249112501125111252112531125411255112561125711258112591126011261112621126311264112651126611267112681126911270112711127211273112741127511276112771127811279112801128111282112831128411285112861128711288112891129011291112921129311294112951129611297112981129911300113011130211303113041130511306113071130811309113101131111312113131131411315113161131711318113191132011321113221132311324113251132611327113281132911330113311133211333113341133511336113371133811339113401134111342113431134411345113461134711348113491135011351113521135311354113551135611357113581135911360113611136211363113641136511366113671136811369113701137111372113731137411375113761137711378113791138011381113821138311384113851138611387113881138911390113911139211393113941139511396113971139811399114001140111402114031140411405114061140711408114091141011411114121141311414114151141611417114181141911420114211142211423114241142511426114271142811429114301143111432114331143411435114361143711438114391144011441114421144311444114451144611447114481144911450114511145211453114541145511456114571145811459114601146111462114631146411465114661146711468114691147011471114721147311474114751147611477114781147911480114811148211483114841148511486114871148811489114901149111492114931149411495114961149711498114991150011501115021150311504115051150611507115081150911510115111151211513115141151511516115171151811519115201152111522115231152411525115261152711528115291153011531115321153311534115351153611537115381153911540115411154211543115441154511546115471154811549115501155111552115531155411555115561155711558115591156011561115621156311564115651156611567115681156911570115711157211573115741157511576115771157811579115801158111582115831158411585115861158711588115891159011591115921159311594115951159611597115981159911600116011160211603116041160511606116071160811609116101161111612116131161411615116161161711618116191162011621116221162311624116251162611627116281162911630116311163211633116341163511636116371163811639116401164111642116431164411645116461164711648116491165011651116521165311654116551165611657116581165911660116611166211663116641166511666116671166811669116701167111672116731167411675116761167711678116791168011681116821168311684116851168611687116881168911690116911169211693116941169511696116971169811699117001170111702117031170411705117061170711708117091171011711117121171311714117151171611717117181171911720117211172211723117241172511726117271172811729117301173111732117331173411735117361173711738117391174011741117421174311744117451174611747117481174911750117511175211753117541175511756117571175811759117601176111762117631176411765117661176711768117691177011771117721177311774117751177611777117781177911780117811178211783117841178511786117871178811789117901179111792117931179411795117961179711798117991180011801118021180311804118051180611807118081180911810118111181211813118141181511816118171181811819118201182111822118231182411825118261182711828118291183011831118321183311834118351183611837118381183911840118411184211843118441184511846118471184811849118501185111852118531185411855118561185711858118591186011861118621186311864118651186611867118681186911870118711187211873118741187511876118771187811879118801188111882118831188411885118861188711888118891189011891118921189311894118951189611897118981189911900119011190211903119041190511906119071190811909119101191111912119131191411915119161191711918119191192011921119221192311924119251192611927119281192911930119311193211933119341193511936119371193811939119401194111942119431194411945119461194711948119491195011951119521195311954119551195611957119581195911960119611196211963119641196511966119671196811969119701197111972119731197411975119761197711978119791198011981119821198311984119851198611987119881198911990119911199211993119941199511996119971199811999120001200112002120031200412005120061200712008120091201012011120121201312014120151201612017120181201912020120211202212023120241202512026120271202812029120301203112032120331203412035120361203712038120391204012041120421204312044120451204612047120481204912050120511205212053120541205512056120571205812059120601206112062120631206412065120661206712068120691207012071120721207312074120751207612077120781207912080120811208212083120841208512086120871208812089120901209112092120931209412095120961209712098120991210012101121021210312104121051210612107121081210912110121111211212113121141211512116121171211812119121201212112122121231212412125121261212712128121291213012131121321213312134121351213612137121381213912140121411214212143121441214512146121471214812149121501215112152121531215412155121561215712158121591216012161121621216312164121651216612167121681216912170121711217212173121741217512176121771217812179121801218112182121831218412185121861218712188121891219012191121921219312194121951219612197121981219912200122011220212203122041220512206122071220812209122101221112212122131221412215122161221712218122191222012221122221222312224122251222612227122281222912230122311223212233122341223512236122371223812239122401224112242122431224412245122461224712248122491225012251122521225312254122551225612257122581225912260122611226212263122641226512266122671226812269122701227112272122731227412275122761227712278122791228012281122821228312284122851228612287122881228912290122911229212293122941229512296122971229812299123001230112302123031230412305123061230712308123091231012311123121231312314123151231612317123181231912320123211232212323123241232512326123271232812329123301233112332123331233412335123361233712338123391234012341123421234312344123451234612347123481234912350123511235212353123541235512356123571235812359123601236112362123631236412365123661236712368123691237012371123721237312374123751237612377123781237912380123811238212383123841238512386123871238812389123901239112392123931239412395123961239712398123991240012401124021240312404124051240612407124081240912410124111241212413124141241512416124171241812419124201242112422124231242412425124261242712428124291243012431124321243312434124351243612437124381243912440124411244212443124441244512446124471244812449124501245112452124531245412455124561245712458124591246012461124621246312464124651246612467124681246912470124711247212473124741247512476124771247812479124801248112482124831248412485124861248712488124891249012491124921249312494124951249612497124981249912500125011250212503125041250512506125071250812509125101251112512125131251412515125161251712518125191252012521125221252312524125251252612527125281252912530125311253212533125341253512536125371253812539125401254112542125431254412545125461254712548125491255012551125521255312554125551255612557125581255912560125611256212563125641256512566125671256812569125701257112572125731257412575125761257712578125791258012581125821258312584125851258612587125881258912590125911259212593125941259512596125971259812599126001260112602126031260412605126061260712608126091261012611126121261312614126151261612617126181261912620126211262212623126241262512626126271262812629126301263112632126331263412635126361263712638126391264012641126421264312644126451264612647126481264912650126511265212653126541265512656126571265812659126601266112662126631266412665126661266712668126691267012671126721267312674126751267612677126781267912680126811268212683126841268512686126871268812689126901269112692126931269412695126961269712698126991270012701127021270312704127051270612707127081270912710127111271212713127141271512716127171271812719127201272112722127231272412725127261272712728127291273012731127321273312734127351273612737127381273912740127411274212743127441274512746127471274812749127501275112752127531275412755127561275712758127591276012761127621276312764127651276612767127681276912770127711277212773127741277512776127771277812779127801278112782127831278412785127861278712788127891279012791127921279312794127951279612797127981279912800128011280212803128041280512806128071280812809128101281112812128131281412815128161281712818128191282012821128221282312824128251282612827128281282912830128311283212833128341283512836128371283812839128401284112842128431284412845128461284712848128491285012851128521285312854128551285612857128581285912860128611286212863128641286512866128671286812869128701287112872128731287412875128761287712878128791288012881128821288312884128851288612887128881288912890128911289212893128941289512896128971289812899129001290112902129031290412905129061290712908129091291012911129121291312914129151291612917129181291912920129211292212923129241292512926129271292812929129301293112932129331293412935129361293712938129391294012941129421294312944129451294612947129481294912950129511295212953129541295512956129571295812959129601296112962129631296412965129661296712968129691297012971129721297312974129751297612977129781297912980129811298212983129841298512986129871298812989129901299112992129931299412995129961299712998129991300013001130021300313004130051300613007130081300913010130111301213013130141301513016130171301813019130201302113022130231302413025130261302713028130291303013031130321303313034130351303613037130381303913040130411304213043130441304513046130471304813049130501305113052130531305413055130561305713058130591306013061130621306313064130651306613067130681306913070130711307213073130741307513076130771307813079130801308113082130831308413085130861308713088130891309013091130921309313094130951309613097130981309913100131011310213103131041310513106131071310813109131101311113112131131311413115131161311713118131191312013121131221312313124131251312613127131281312913130131311313213133131341313513136131371313813139131401314113142131431314413145131461314713148131491315013151131521315313154131551315613157131581315913160131611316213163131641316513166131671316813169131701317113172131731317413175131761317713178131791318013181131821318313184131851318613187131881318913190131911319213193131941319513196131971319813199132001320113202132031320413205132061320713208132091321013211132121321313214132151321613217132181321913220132211322213223132241322513226132271322813229132301323113232132331323413235132361323713238132391324013241132421324313244132451324613247132481324913250132511325213253132541325513256132571325813259132601326113262132631326413265132661326713268132691327013271132721327313274132751327613277132781327913280132811328213283132841328513286132871328813289132901329113292132931329413295132961329713298132991330013301133021330313304133051330613307133081330913310133111331213313133141331513316133171331813319133201332113322133231332413325133261332713328133291333013331133321333313334133351333613337133381333913340133411334213343133441334513346133471334813349133501335113352133531335413355133561335713358133591336013361133621336313364133651336613367133681336913370133711337213373133741337513376133771337813379133801338113382133831338413385133861338713388133891339013391133921339313394133951339613397133981339913400134011340213403134041340513406134071340813409134101341113412134131341413415134161341713418134191342013421134221342313424134251342613427134281342913430134311343213433134341343513436134371343813439134401344113442134431344413445134461344713448134491345013451134521345313454134551345613457134581345913460134611346213463134641346513466134671346813469134701347113472134731347413475134761347713478134791348013481134821348313484134851348613487134881348913490134911349213493134941349513496134971349813499135001350113502135031350413505135061350713508135091351013511135121351313514135151351613517135181351913520135211352213523135241352513526135271352813529135301353113532135331353413535135361353713538135391354013541135421354313544135451354613547135481354913550135511355213553135541355513556135571355813559135601356113562135631356413565135661356713568135691357013571135721357313574135751357613577135781357913580135811358213583135841358513586135871358813589135901359113592135931359413595135961359713598135991360013601136021360313604136051360613607136081360913610136111361213613136141361513616136171361813619136201362113622136231362413625136261362713628136291363013631136321363313634136351363613637136381363913640136411364213643136441364513646136471364813649136501365113652136531365413655136561365713658136591366013661136621366313664136651366613667136681366913670136711367213673136741367513676136771367813679136801368113682136831368413685136861368713688136891369013691136921369313694136951369613697136981369913700137011370213703137041370513706137071370813709137101371113712137131371413715137161371713718137191372013721137221372313724137251372613727137281372913730137311373213733137341373513736137371373813739137401374113742137431374413745137461374713748137491375013751137521375313754137551375613757137581375913760137611376213763137641376513766137671376813769137701377113772137731377413775137761377713778137791378013781137821378313784137851378613787137881378913790137911379213793137941379513796137971379813799138001380113802138031380413805138061380713808138091381013811138121381313814138151381613817138181381913820138211382213823138241382513826138271382813829138301383113832138331383413835138361383713838138391384013841138421384313844138451384613847138481384913850138511385213853138541385513856138571385813859138601386113862138631386413865138661386713868138691387013871138721387313874138751387613877138781387913880138811388213883138841388513886138871388813889138901389113892138931389413895138961389713898138991390013901139021390313904139051390613907139081390913910139111391213913139141391513916139171391813919139201392113922139231392413925139261392713928139291393013931139321393313934139351393613937139381393913940139411394213943139441394513946139471394813949139501395113952139531395413955139561395713958139591396013961139621396313964139651396613967139681396913970139711397213973139741397513976139771397813979139801398113982139831398413985139861398713988139891399013991139921399313994139951399613997139981399914000140011400214003140041400514006140071400814009140101401114012140131401414015140161401714018140191402014021140221402314024140251402614027140281402914030140311403214033140341403514036140371403814039140401404114042140431404414045140461404714048140491405014051140521405314054140551405614057140581405914060140611406214063140641406514066140671406814069140701407114072140731407414075140761407714078140791408014081140821408314084140851408614087140881408914090140911409214093140941409514096140971409814099141001410114102141031410414105141061410714108141091411014111141121411314114141151411614117141181411914120141211412214123141241412514126141271412814129141301413114132141331413414135141361413714138141391414014141141421414314144141451414614147141481414914150141511415214153141541415514156141571415814159141601416114162141631416414165141661416714168141691417014171141721417314174141751417614177141781417914180141811418214183141841418514186141871418814189141901419114192141931419414195141961419714198141991420014201142021420314204142051420614207142081420914210142111421214213142141421514216142171421814219142201422114222142231422414225142261422714228142291423014231142321423314234142351423614237142381423914240142411424214243142441424514246142471424814249142501425114252142531425414255142561425714258142591426014261142621426314264142651426614267142681426914270142711427214273142741427514276142771427814279142801428114282142831428414285142861428714288142891429014291142921429314294142951429614297142981429914300143011430214303143041430514306143071430814309143101431114312143131431414315143161431714318143191432014321143221432314324143251432614327143281432914330143311433214333143341433514336143371433814339143401434114342143431434414345143461434714348143491435014351143521435314354143551435614357143581435914360143611436214363143641436514366143671436814369143701437114372143731437414375143761437714378143791438014381143821438314384143851438614387143881438914390143911439214393143941439514396143971439814399144001440114402144031440414405144061440714408144091441014411144121441314414144151441614417144181441914420144211442214423144241442514426144271442814429144301443114432144331443414435144361443714438144391444014441144421444314444144451444614447144481444914450144511445214453144541445514456144571445814459144601446114462144631446414465144661446714468144691447014471144721447314474144751447614477144781447914480144811448214483144841448514486144871448814489144901449114492144931449414495144961449714498144991450014501145021450314504145051450614507145081450914510145111451214513145141451514516145171451814519145201452114522145231452414525145261452714528145291453014531145321453314534145351453614537145381453914540145411454214543145441454514546145471454814549145501455114552145531455414555145561455714558145591456014561145621456314564145651456614567145681456914570145711457214573145741457514576145771457814579145801458114582145831458414585145861458714588145891459014591145921459314594145951459614597145981459914600146011460214603146041460514606146071460814609146101461114612146131461414615146161461714618146191462014621146221462314624146251462614627146281462914630146311463214633146341463514636146371463814639146401464114642146431464414645146461464714648146491465014651146521465314654146551465614657146581465914660146611466214663146641466514666146671466814669146701467114672146731467414675146761467714678146791468014681146821468314684146851468614687146881468914690146911469214693146941469514696146971469814699147001470114702147031470414705147061470714708147091471014711147121471314714147151471614717147181471914720147211472214723147241472514726147271472814729147301473114732147331473414735147361473714738147391474014741147421474314744147451474614747147481474914750147511475214753147541475514756147571475814759147601476114762147631476414765147661476714768147691477014771147721477314774147751477614777147781477914780147811478214783147841478514786147871478814789147901479114792147931479414795147961479714798147991480014801148021480314804148051480614807148081480914810148111481214813148141481514816148171481814819148201482114822148231482414825148261482714828148291483014831148321483314834148351483614837148381483914840148411484214843148441484514846148471484814849148501485114852148531485414855148561485714858148591486014861148621486314864148651486614867148681486914870148711487214873148741487514876148771487814879148801488114882148831488414885148861488714888148891489014891148921489314894148951489614897148981489914900149011490214903149041490514906149071490814909149101491114912149131491414915149161491714918149191492014921149221492314924149251492614927149281492914930149311493214933149341493514936149371493814939149401494114942149431494414945149461494714948149491495014951149521495314954149551495614957149581495914960149611496214963149641496514966149671496814969149701497114972149731497414975149761497714978149791498014981149821498314984149851498614987149881498914990149911499214993149941499514996149971499814999150001500115002150031500415005150061500715008150091501015011150121501315014150151501615017150181501915020150211502215023150241502515026150271502815029150301503115032150331503415035150361503715038150391504015041150421504315044150451504615047150481504915050150511505215053150541505515056150571505815059150601506115062150631506415065150661506715068150691507015071150721507315074150751507615077150781507915080150811508215083150841508515086150871508815089150901509115092150931509415095150961509715098150991510015101151021510315104151051510615107151081510915110151111511215113151141511515116151171511815119151201512115122151231512415125151261512715128151291513015131151321513315134151351513615137151381513915140151411514215143151441514515146151471514815149151501515115152151531515415155151561515715158151591516015161151621516315164151651516615167151681516915170151711517215173151741517515176151771517815179151801518115182151831518415185151861518715188151891519015191151921519315194151951519615197151981519915200152011520215203152041520515206152071520815209152101521115212152131521415215152161521715218152191522015221152221522315224152251522615227152281522915230152311523215233152341523515236152371523815239152401524115242152431524415245152461524715248152491525015251152521525315254152551525615257152581525915260152611526215263152641526515266152671526815269152701527115272152731527415275152761527715278152791528015281152821528315284152851528615287152881528915290152911529215293152941529515296152971529815299153001530115302153031530415305153061530715308153091531015311153121531315314153151531615317153181531915320153211532215323153241532515326153271532815329153301533115332153331533415335153361533715338153391534015341153421534315344153451534615347153481534915350153511535215353153541535515356153571535815359153601536115362153631536415365153661536715368153691537015371153721537315374153751537615377153781537915380153811538215383153841538515386153871538815389153901539115392153931539415395153961539715398153991540015401154021540315404154051540615407154081540915410154111541215413154141541515416154171541815419154201542115422154231542415425154261542715428154291543015431154321543315434154351543615437154381543915440154411544215443154441544515446154471544815449154501545115452154531545415455154561545715458154591546015461154621546315464154651546615467154681546915470154711547215473154741547515476154771547815479154801548115482154831548415485154861548715488154891549015491154921549315494154951549615497154981549915500155011550215503155041550515506155071550815509155101551115512155131551415515155161551715518155191552015521155221552315524155251552615527155281552915530155311553215533155341553515536155371553815539155401554115542155431554415545155461554715548155491555015551155521555315554155551555615557155581555915560155611556215563155641556515566155671556815569155701557115572155731557415575155761557715578155791558015581155821558315584155851558615587155881558915590155911559215593155941559515596155971559815599156001560115602156031560415605156061560715608156091561015611156121561315614156151561615617156181561915620156211562215623156241562515626156271562815629156301563115632156331563415635156361563715638156391564015641156421564315644156451564615647156481564915650156511565215653156541565515656156571565815659156601566115662156631566415665156661566715668156691567015671156721567315674156751567615677156781567915680156811568215683156841568515686156871568815689156901569115692156931569415695156961569715698156991570015701157021570315704157051570615707157081570915710157111571215713157141571515716157171571815719157201572115722157231572415725157261572715728157291573015731157321573315734157351573615737157381573915740157411574215743157441574515746157471574815749157501575115752157531575415755157561575715758157591576015761157621576315764157651576615767157681576915770157711577215773157741577515776157771577815779157801578115782157831578415785157861578715788157891579015791157921579315794157951579615797157981579915800158011580215803158041580515806158071580815809158101581115812158131581415815158161581715818158191582015821158221582315824158251582615827158281582915830158311583215833158341583515836158371583815839158401584115842158431584415845158461584715848158491585015851158521585315854158551585615857158581585915860158611586215863158641586515866158671586815869158701587115872158731587415875158761587715878158791588015881158821588315884158851588615887158881588915890158911589215893158941589515896158971589815899159001590115902159031590415905159061590715908159091591015911159121591315914159151591615917159181591915920159211592215923159241592515926159271592815929159301593115932159331593415935159361593715938159391594015941159421594315944159451594615947159481594915950159511595215953159541595515956159571595815959159601596115962159631596415965159661596715968159691597015971159721597315974159751597615977159781597915980159811598215983159841598515986159871598815989159901599115992159931599415995159961599715998159991600016001160021600316004160051600616007160081600916010160111601216013160141601516016160171601816019160201602116022160231602416025160261602716028160291603016031160321603316034160351603616037160381603916040160411604216043160441604516046160471604816049160501605116052160531605416055160561605716058160591606016061160621606316064160651606616067160681606916070160711607216073160741607516076160771607816079160801608116082160831608416085160861608716088160891609016091160921609316094160951609616097160981609916100161011610216103161041610516106161071610816109161101611116112161131611416115161161611716118161191612016121161221612316124161251612616127161281612916130161311613216133161341613516136161371613816139161401614116142161431614416145161461614716148161491615016151161521615316154161551615616157161581615916160161611616216163161641616516166161671616816169161701617116172161731617416175161761617716178161791618016181161821618316184161851618616187161881618916190161911619216193161941619516196161971619816199162001620116202162031620416205162061620716208162091621016211162121621316214162151621616217162181621916220162211622216223162241622516226162271622816229162301623116232162331623416235162361623716238162391624016241162421624316244162451624616247162481624916250162511625216253162541625516256162571625816259162601626116262162631626416265162661626716268162691627016271162721627316274162751627616277162781627916280162811628216283162841628516286162871628816289162901629116292162931629416295162961629716298162991630016301163021630316304163051630616307163081630916310163111631216313163141631516316163171631816319163201632116322163231632416325163261632716328163291633016331163321633316334163351633616337163381633916340163411634216343163441634516346163471634816349163501635116352163531635416355163561635716358163591636016361163621636316364163651636616367163681636916370163711637216373163741637516376163771637816379163801638116382163831638416385163861638716388163891639016391163921639316394163951639616397163981639916400164011640216403164041640516406164071640816409164101641116412164131641416415164161641716418164191642016421164221642316424164251642616427164281642916430164311643216433164341643516436164371643816439164401644116442164431644416445164461644716448164491645016451164521645316454164551645616457164581645916460164611646216463164641646516466164671646816469164701647116472164731647416475164761647716478164791648016481164821648316484164851648616487164881648916490164911649216493164941649516496164971649816499165001650116502165031650416505165061650716508165091651016511165121651316514165151651616517165181651916520165211652216523165241652516526165271652816529165301653116532165331653416535165361653716538165391654016541165421654316544165451654616547165481654916550165511655216553165541655516556165571655816559165601656116562165631656416565165661656716568165691657016571165721657316574165751657616577165781657916580165811658216583165841658516586165871658816589165901659116592165931659416595165961659716598165991660016601166021660316604166051660616607166081660916610166111661216613166141661516616166171661816619166201662116622166231662416625166261662716628166291663016631166321663316634166351663616637166381663916640166411664216643166441664516646166471664816649166501665116652166531665416655166561665716658166591666016661166621666316664166651666616667166681666916670166711667216673166741667516676166771667816679166801668116682166831668416685166861668716688166891669016691166921669316694166951669616697166981669916700167011670216703167041670516706167071670816709167101671116712167131671416715167161671716718167191672016721167221672316724167251672616727167281672916730167311673216733167341673516736167371673816739167401674116742167431674416745167461674716748167491675016751167521675316754167551675616757167581675916760167611676216763167641676516766167671676816769167701677116772167731677416775167761677716778167791678016781167821678316784167851678616787167881678916790167911679216793167941679516796167971679816799168001680116802168031680416805168061680716808168091681016811168121681316814168151681616817168181681916820168211682216823168241682516826168271682816829168301683116832168331683416835168361683716838168391684016841168421684316844168451684616847168481684916850168511685216853168541685516856168571685816859168601686116862168631686416865168661686716868168691687016871168721687316874168751687616877168781687916880168811688216883168841688516886168871688816889168901689116892168931689416895168961689716898168991690016901169021690316904169051690616907169081690916910169111691216913169141691516916169171691816919169201692116922169231692416925169261692716928169291693016931169321693316934169351693616937169381693916940169411694216943169441694516946169471694816949169501695116952169531695416955169561695716958169591696016961169621696316964169651696616967169681696916970169711697216973169741697516976169771697816979169801698116982169831698416985169861698716988169891699016991169921699316994169951699616997169981699917000170011700217003170041700517006170071700817009170101701117012170131701417015170161701717018170191702017021170221702317024170251702617027170281702917030170311703217033170341703517036170371703817039170401704117042170431704417045170461704717048170491705017051170521705317054170551705617057170581705917060170611706217063170641706517066170671706817069170701707117072170731707417075170761707717078170791708017081170821708317084170851708617087170881708917090170911709217093170941709517096170971709817099171001710117102171031710417105171061710717108171091711017111171121711317114171151711617117171181711917120171211712217123171241712517126171271712817129171301713117132171331713417135171361713717138171391714017141171421714317144171451714617147171481714917150171511715217153171541715517156171571715817159171601716117162171631716417165171661716717168171691717017171171721717317174171751717617177171781717917180171811718217183171841718517186171871718817189171901719117192171931719417195171961719717198171991720017201172021720317204172051720617207172081720917210172111721217213172141721517216172171721817219172201722117222172231722417225172261722717228172291723017231172321723317234172351723617237172381723917240172411724217243172441724517246172471724817249172501725117252172531725417255172561725717258172591726017261172621726317264172651726617267172681726917270172711727217273172741727517276172771727817279172801728117282172831728417285172861728717288172891729017291172921729317294172951729617297172981729917300173011730217303173041730517306173071730817309173101731117312173131731417315173161731717318173191732017321173221732317324173251732617327173281732917330173311733217333173341733517336173371733817339173401734117342173431734417345173461734717348173491735017351173521735317354173551735617357173581735917360173611736217363173641736517366173671736817369173701737117372173731737417375173761737717378173791738017381173821738317384173851738617387173881738917390173911739217393173941739517396173971739817399174001740117402174031740417405174061740717408174091741017411174121741317414174151741617417174181741917420174211742217423174241742517426174271742817429174301743117432174331743417435174361743717438174391744017441174421744317444174451744617447174481744917450174511745217453174541745517456174571745817459174601746117462174631746417465174661746717468174691747017471174721747317474174751747617477174781747917480174811748217483174841748517486174871748817489174901749117492174931749417495174961749717498174991750017501175021750317504175051750617507175081750917510175111751217513175141751517516175171751817519175201752117522175231752417525175261752717528175291753017531175321753317534175351753617537175381753917540175411754217543175441754517546175471754817549175501755117552175531755417555175561755717558175591756017561175621756317564175651756617567175681756917570175711757217573175741757517576175771757817579175801758117582175831758417585175861758717588175891759017591175921759317594175951759617597175981759917600176011760217603176041760517606176071760817609176101761117612176131761417615176161761717618176191762017621176221762317624176251762617627176281762917630176311763217633176341763517636176371763817639176401764117642176431764417645176461764717648176491765017651176521765317654176551765617657176581765917660176611766217663176641766517666176671766817669176701767117672176731767417675176761767717678176791768017681176821768317684176851768617687176881768917690176911769217693176941769517696176971769817699177001770117702177031770417705177061770717708177091771017711177121771317714177151771617717177181771917720177211772217723177241772517726177271772817729177301773117732177331773417735177361773717738177391774017741177421774317744177451774617747177481774917750177511775217753177541775517756177571775817759177601776117762177631776417765177661776717768177691777017771177721777317774177751777617777177781777917780177811778217783177841778517786177871778817789177901779117792177931779417795177961779717798177991780017801178021780317804178051780617807178081780917810178111781217813178141781517816178171781817819178201782117822178231782417825178261782717828178291783017831178321783317834178351783617837178381783917840178411784217843178441784517846178471784817849178501785117852178531785417855178561785717858178591786017861178621786317864178651786617867178681786917870178711787217873178741787517876178771787817879178801788117882178831788417885178861788717888178891789017891178921789317894178951789617897178981789917900179011790217903179041790517906179071790817909179101791117912179131791417915179161791717918179191792017921179221792317924179251792617927179281792917930179311793217933179341793517936179371793817939179401794117942179431794417945179461794717948179491795017951179521795317954179551795617957179581795917960179611796217963179641796517966179671796817969179701797117972179731797417975179761797717978179791798017981179821798317984179851798617987179881798917990179911799217993179941799517996179971799817999180001800118002180031800418005180061800718008180091801018011180121801318014180151801618017180181801918020180211802218023180241802518026180271802818029180301803118032180331803418035180361803718038180391804018041180421804318044180451804618047180481804918050180511805218053180541805518056180571805818059180601806118062180631806418065180661806718068180691807018071180721807318074180751807618077180781807918080180811808218083180841808518086180871808818089180901809118092180931809418095180961809718098180991810018101181021810318104181051810618107181081810918110181111811218113181141811518116181171811818119181201812118122181231812418125181261812718128181291813018131181321813318134181351813618137181381813918140181411814218143181441814518146181471814818149181501815118152181531815418155181561815718158181591816018161181621816318164181651816618167181681816918170181711817218173181741817518176181771817818179181801818118182181831818418185181861818718188181891819018191181921819318194181951819618197181981819918200182011820218203182041820518206182071820818209182101821118212182131821418215182161821718218182191822018221182221822318224182251822618227182281822918230182311823218233182341823518236182371823818239182401824118242182431824418245182461824718248182491825018251182521825318254182551825618257182581825918260182611826218263182641826518266182671826818269182701827118272182731827418275182761827718278182791828018281182821828318284182851828618287182881828918290182911829218293182941829518296182971829818299183001830118302183031830418305183061830718308183091831018311183121831318314183151831618317183181831918320183211832218323183241832518326183271832818329183301833118332183331833418335183361833718338183391834018341183421834318344183451834618347183481834918350183511835218353183541835518356183571835818359183601836118362183631836418365183661836718368183691837018371183721837318374183751837618377183781837918380183811838218383183841838518386183871838818389183901839118392183931839418395183961839718398183991840018401184021840318404184051840618407184081840918410184111841218413184141841518416184171841818419184201842118422184231842418425184261842718428184291843018431184321843318434184351843618437184381843918440184411844218443184441844518446184471844818449184501845118452184531845418455184561845718458184591846018461184621846318464184651846618467184681846918470184711847218473184741847518476184771847818479184801848118482184831848418485184861848718488184891849018491184921849318494184951849618497184981849918500185011850218503185041850518506185071850818509185101851118512185131851418515185161851718518185191852018521185221852318524185251852618527185281852918530185311853218533185341853518536185371853818539185401854118542185431854418545185461854718548185491855018551185521855318554185551855618557185581855918560185611856218563185641856518566185671856818569185701857118572185731857418575185761857718578185791858018581185821858318584185851858618587185881858918590185911859218593185941859518596185971859818599186001860118602186031860418605186061860718608186091861018611186121861318614186151861618617186181861918620186211862218623186241862518626186271862818629186301863118632186331863418635186361863718638186391864018641186421864318644186451864618647186481864918650186511865218653186541865518656186571865818659186601866118662186631866418665186661866718668186691867018671186721867318674186751867618677186781867918680186811868218683186841868518686186871868818689186901869118692186931869418695186961869718698186991870018701187021870318704187051870618707187081870918710187111871218713187141871518716187171871818719187201872118722187231872418725187261872718728187291873018731187321873318734187351873618737187381873918740187411874218743187441874518746187471874818749187501875118752187531875418755187561875718758187591876018761187621876318764187651876618767187681876918770187711877218773187741877518776187771877818779187801878118782187831878418785187861878718788187891879018791187921879318794187951879618797187981879918800188011880218803188041880518806188071880818809188101881118812188131881418815188161881718818188191882018821188221882318824188251882618827188281882918830188311883218833188341883518836188371883818839188401884118842188431884418845188461884718848188491885018851188521885318854188551885618857188581885918860188611886218863188641886518866188671886818869188701887118872188731887418875188761887718878188791888018881188821888318884188851888618887188881888918890188911889218893188941889518896188971889818899189001890118902189031890418905189061890718908189091891018911189121891318914189151891618917189181891918920189211892218923189241892518926189271892818929189301893118932189331893418935189361893718938189391894018941189421894318944189451894618947189481894918950189511895218953189541895518956189571895818959189601896118962189631896418965189661896718968189691897018971189721897318974189751897618977189781897918980189811898218983189841898518986189871898818989189901899118992189931899418995189961899718998189991900019001190021900319004190051900619007190081900919010190111901219013190141901519016190171901819019190201902119022190231902419025190261902719028190291903019031190321903319034190351903619037190381903919040190411904219043190441904519046190471904819049190501905119052190531905419055190561905719058190591906019061190621906319064190651906619067190681906919070190711907219073190741907519076190771907819079190801908119082190831908419085190861908719088190891909019091190921909319094190951909619097190981909919100191011910219103191041910519106191071910819109191101911119112191131911419115191161911719118191191912019121191221912319124191251912619127191281912919130191311913219133191341913519136191371913819139191401914119142191431914419145191461914719148191491915019151191521915319154191551915619157191581915919160191611916219163191641916519166191671916819169191701917119172191731917419175191761917719178191791918019181191821918319184191851918619187191881918919190191911919219193191941919519196191971919819199192001920119202192031920419205192061920719208192091921019211192121921319214192151921619217192181921919220192211922219223192241922519226192271922819229192301923119232192331923419235192361923719238192391924019241192421924319244192451924619247192481924919250192511925219253192541925519256192571925819259192601926119262192631926419265192661926719268192691927019271
  1. /*
  2. html2canvas 0.5.0-beta4 <http://html2canvas.hertzen.com>
  3. Copyright (c) 2016 Niklas von Hertzen
  4. Released under License
  5. */
  6. !function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var n;"undefined"!=typeof window?n=window:"undefined"!=typeof global?n=global:"undefined"!=typeof self&&(n=self),(n.html2canvas||(n.html2canvas={})).svg=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
  7. },{}],2:[function(_dereq_,module,exports){
  8. /*!
  9. * The buffer module from node.js, for the browser.
  10. *
  11. * @author Feross Aboukhadijeh <feross@feross.org> <http://feross.org>
  12. * @license MIT
  13. */
  14. var base64 = _dereq_('base64-js')
  15. var ieee754 = _dereq_('ieee754')
  16. var isArray = _dereq_('is-array')
  17. exports.Buffer = Buffer
  18. exports.SlowBuffer = SlowBuffer
  19. exports.INSPECT_MAX_BYTES = 50
  20. Buffer.poolSize = 8192 // not used by this implementation
  21. var kMaxLength = 0x3fffffff
  22. var rootParent = {}
  23. /**
  24. * If `Buffer.TYPED_ARRAY_SUPPORT`:
  25. * === true Use Uint8Array implementation (fastest)
  26. * === false Use Object implementation (most compatible, even IE6)
  27. *
  28. * Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+,
  29. * Opera 11.6+, iOS 4.2+.
  30. *
  31. * Note:
  32. *
  33. * - Implementation must support adding new properties to `Uint8Array` instances.
  34. * Firefox 4-29 lacked support, fixed in Firefox 30+.
  35. * See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438.
  36. *
  37. * - Chrome 9-10 is missing the `TypedArray.prototype.subarray` function.
  38. *
  39. * - IE10 has a broken `TypedArray.prototype.subarray` function which returns arrays of
  40. * incorrect length in some situations.
  41. *
  42. * We detect these buggy browsers and set `Buffer.TYPED_ARRAY_SUPPORT` to `false` so they will
  43. * get the Object implementation, which is slower but will work correctly.
  44. */
  45. Buffer.TYPED_ARRAY_SUPPORT = (function () {
  46. try {
  47. var buf = new ArrayBuffer(0)
  48. var arr = new Uint8Array(buf)
  49. arr.foo = function () { return 42 }
  50. return 42 === arr.foo() && // typed array instances can be augmented
  51. typeof arr.subarray === 'function' && // chrome 9-10 lack `subarray`
  52. new Uint8Array(1).subarray(1, 1).byteLength === 0 // ie10 has broken `subarray`
  53. } catch (e) {
  54. return false
  55. }
  56. })()
  57. /**
  58. * Class: Buffer
  59. * =============
  60. *
  61. * The Buffer constructor returns instances of `Uint8Array` that are augmented
  62. * with function properties for all the node `Buffer` API functions. We use
  63. * `Uint8Array` so that square bracket notation works as expected -- it returns
  64. * a single octet.
  65. *
  66. * By augmenting the instances, we can avoid modifying the `Uint8Array`
  67. * prototype.
  68. */
  69. function Buffer (subject, encoding, noZero) {
  70. if (!(this instanceof Buffer))
  71. return new Buffer(subject, encoding, noZero)
  72. var type = typeof subject
  73. // Find the length
  74. var length
  75. if (type === 'number')
  76. length = subject > 0 ? subject >>> 0 : 0
  77. else if (type === 'string') {
  78. length = Buffer.byteLength(subject, encoding)
  79. } else if (type === 'object' && subject !== null) { // assume object is array-like
  80. if (subject.type === 'Buffer' && isArray(subject.data))
  81. subject = subject.data
  82. length = +subject.length > 0 ? Math.floor(+subject.length) : 0
  83. } else
  84. throw new TypeError('must start with number, buffer, array or string')
  85. if (length > kMaxLength)
  86. throw new RangeError('Attempt to allocate Buffer larger than maximum ' +
  87. 'size: 0x' + kMaxLength.toString(16) + ' bytes')
  88. var buf
  89. if (Buffer.TYPED_ARRAY_SUPPORT) {
  90. // Preferred: Return an augmented `Uint8Array` instance for best performance
  91. buf = Buffer._augment(new Uint8Array(length))
  92. } else {
  93. // Fallback: Return THIS instance of Buffer (created by `new`)
  94. buf = this
  95. buf.length = length
  96. buf._isBuffer = true
  97. }
  98. var i
  99. if (Buffer.TYPED_ARRAY_SUPPORT && typeof subject.byteLength === 'number') {
  100. // Speed optimization -- use set if we're copying from a typed array
  101. buf._set(subject)
  102. } else if (isArrayish(subject)) {
  103. // Treat array-ish objects as a byte array
  104. if (Buffer.isBuffer(subject)) {
  105. for (i = 0; i < length; i++)
  106. buf[i] = subject.readUInt8(i)
  107. } else {
  108. for (i = 0; i < length; i++)
  109. buf[i] = ((subject[i] % 256) + 256) % 256
  110. }
  111. } else if (type === 'string') {
  112. buf.write(subject, 0, encoding)
  113. } else if (type === 'number' && !Buffer.TYPED_ARRAY_SUPPORT && !noZero) {
  114. for (i = 0; i < length; i++) {
  115. buf[i] = 0
  116. }
  117. }
  118. if (length > 0 && length <= Buffer.poolSize)
  119. buf.parent = rootParent
  120. return buf
  121. }
  122. function SlowBuffer(subject, encoding, noZero) {
  123. if (!(this instanceof SlowBuffer))
  124. return new SlowBuffer(subject, encoding, noZero)
  125. var buf = new Buffer(subject, encoding, noZero)
  126. delete buf.parent
  127. return buf
  128. }
  129. Buffer.isBuffer = function (b) {
  130. return !!(b != null && b._isBuffer)
  131. }
  132. Buffer.compare = function (a, b) {
  133. if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b))
  134. throw new TypeError('Arguments must be Buffers')
  135. var x = a.length
  136. var y = b.length
  137. for (var i = 0, len = Math.min(x, y); i < len && a[i] === b[i]; i++) {}
  138. if (i !== len) {
  139. x = a[i]
  140. y = b[i]
  141. }
  142. if (x < y) return -1
  143. if (y < x) return 1
  144. return 0
  145. }
  146. Buffer.isEncoding = function (encoding) {
  147. switch (String(encoding).toLowerCase()) {
  148. case 'hex':
  149. case 'utf8':
  150. case 'utf-8':
  151. case 'ascii':
  152. case 'binary':
  153. case 'base64':
  154. case 'raw':
  155. case 'ucs2':
  156. case 'ucs-2':
  157. case 'utf16le':
  158. case 'utf-16le':
  159. return true
  160. default:
  161. return false
  162. }
  163. }
  164. Buffer.concat = function (list, totalLength) {
  165. if (!isArray(list)) throw new TypeError('Usage: Buffer.concat(list[, length])')
  166. if (list.length === 0) {
  167. return new Buffer(0)
  168. } else if (list.length === 1) {
  169. return list[0]
  170. }
  171. var i
  172. if (totalLength === undefined) {
  173. totalLength = 0
  174. for (i = 0; i < list.length; i++) {
  175. totalLength += list[i].length
  176. }
  177. }
  178. var buf = new Buffer(totalLength)
  179. var pos = 0
  180. for (i = 0; i < list.length; i++) {
  181. var item = list[i]
  182. item.copy(buf, pos)
  183. pos += item.length
  184. }
  185. return buf
  186. }
  187. Buffer.byteLength = function (str, encoding) {
  188. var ret
  189. str = str + ''
  190. switch (encoding || 'utf8') {
  191. case 'ascii':
  192. case 'binary':
  193. case 'raw':
  194. ret = str.length
  195. break
  196. case 'ucs2':
  197. case 'ucs-2':
  198. case 'utf16le':
  199. case 'utf-16le':
  200. ret = str.length * 2
  201. break
  202. case 'hex':
  203. ret = str.length >>> 1
  204. break
  205. case 'utf8':
  206. case 'utf-8':
  207. ret = utf8ToBytes(str).length
  208. break
  209. case 'base64':
  210. ret = base64ToBytes(str).length
  211. break
  212. default:
  213. ret = str.length
  214. }
  215. return ret
  216. }
  217. // pre-set for values that may exist in the future
  218. Buffer.prototype.length = undefined
  219. Buffer.prototype.parent = undefined
  220. // toString(encoding, start=0, end=buffer.length)
  221. Buffer.prototype.toString = function (encoding, start, end) {
  222. var loweredCase = false
  223. start = start >>> 0
  224. end = end === undefined || end === Infinity ? this.length : end >>> 0
  225. if (!encoding) encoding = 'utf8'
  226. if (start < 0) start = 0
  227. if (end > this.length) end = this.length
  228. if (end <= start) return ''
  229. while (true) {
  230. switch (encoding) {
  231. case 'hex':
  232. return hexSlice(this, start, end)
  233. case 'utf8':
  234. case 'utf-8':
  235. return utf8Slice(this, start, end)
  236. case 'ascii':
  237. return asciiSlice(this, start, end)
  238. case 'binary':
  239. return binarySlice(this, start, end)
  240. case 'base64':
  241. return base64Slice(this, start, end)
  242. case 'ucs2':
  243. case 'ucs-2':
  244. case 'utf16le':
  245. case 'utf-16le':
  246. return utf16leSlice(this, start, end)
  247. default:
  248. if (loweredCase)
  249. throw new TypeError('Unknown encoding: ' + encoding)
  250. encoding = (encoding + '').toLowerCase()
  251. loweredCase = true
  252. }
  253. }
  254. }
  255. Buffer.prototype.equals = function (b) {
  256. if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer')
  257. return Buffer.compare(this, b) === 0
  258. }
  259. Buffer.prototype.inspect = function () {
  260. var str = ''
  261. var max = exports.INSPECT_MAX_BYTES
  262. if (this.length > 0) {
  263. str = this.toString('hex', 0, max).match(/.{2}/g).join(' ')
  264. if (this.length > max)
  265. str += ' ... '
  266. }
  267. return '<Buffer ' + str + '>'
  268. }
  269. Buffer.prototype.compare = function (b) {
  270. if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer')
  271. return Buffer.compare(this, b)
  272. }
  273. // `get` will be removed in Node 0.13+
  274. Buffer.prototype.get = function (offset) {
  275. console.log('.get() is deprecated. Access using array indexes instead.')
  276. return this.readUInt8(offset)
  277. }
  278. // `set` will be removed in Node 0.13+
  279. Buffer.prototype.set = function (v, offset) {
  280. console.log('.set() is deprecated. Access using array indexes instead.')
  281. return this.writeUInt8(v, offset)
  282. }
  283. function hexWrite (buf, string, offset, length) {
  284. offset = Number(offset) || 0
  285. var remaining = buf.length - offset
  286. if (!length) {
  287. length = remaining
  288. } else {
  289. length = Number(length)
  290. if (length > remaining) {
  291. length = remaining
  292. }
  293. }
  294. // must be an even number of digits
  295. var strLen = string.length
  296. if (strLen % 2 !== 0) throw new Error('Invalid hex string')
  297. if (length > strLen / 2) {
  298. length = strLen / 2
  299. }
  300. for (var i = 0; i < length; i++) {
  301. var byte = parseInt(string.substr(i * 2, 2), 16)
  302. if (isNaN(byte)) throw new Error('Invalid hex string')
  303. buf[offset + i] = byte
  304. }
  305. return i
  306. }
  307. function utf8Write (buf, string, offset, length) {
  308. var charsWritten = blitBuffer(utf8ToBytes(string, buf.length - offset), buf, offset, length)
  309. return charsWritten
  310. }
  311. function asciiWrite (buf, string, offset, length) {
  312. var charsWritten = blitBuffer(asciiToBytes(string), buf, offset, length)
  313. return charsWritten
  314. }
  315. function binaryWrite (buf, string, offset, length) {
  316. return asciiWrite(buf, string, offset, length)
  317. }
  318. function base64Write (buf, string, offset, length) {
  319. var charsWritten = blitBuffer(base64ToBytes(string), buf, offset, length)
  320. return charsWritten
  321. }
  322. function utf16leWrite (buf, string, offset, length) {
  323. var charsWritten = blitBuffer(utf16leToBytes(string, buf.length - offset), buf, offset, length, 2)
  324. return charsWritten
  325. }
  326. Buffer.prototype.write = function (string, offset, length, encoding) {
  327. // Support both (string, offset, length, encoding)
  328. // and the legacy (string, encoding, offset, length)
  329. if (isFinite(offset)) {
  330. if (!isFinite(length)) {
  331. encoding = length
  332. length = undefined
  333. }
  334. } else { // legacy
  335. var swap = encoding
  336. encoding = offset
  337. offset = length
  338. length = swap
  339. }
  340. offset = Number(offset) || 0
  341. if (length < 0 || offset < 0 || offset > this.length)
  342. throw new RangeError('attempt to write outside buffer bounds');
  343. var remaining = this.length - offset
  344. if (!length) {
  345. length = remaining
  346. } else {
  347. length = Number(length)
  348. if (length > remaining) {
  349. length = remaining
  350. }
  351. }
  352. encoding = String(encoding || 'utf8').toLowerCase()
  353. var ret
  354. switch (encoding) {
  355. case 'hex':
  356. ret = hexWrite(this, string, offset, length)
  357. break
  358. case 'utf8':
  359. case 'utf-8':
  360. ret = utf8Write(this, string, offset, length)
  361. break
  362. case 'ascii':
  363. ret = asciiWrite(this, string, offset, length)
  364. break
  365. case 'binary':
  366. ret = binaryWrite(this, string, offset, length)
  367. break
  368. case 'base64':
  369. ret = base64Write(this, string, offset, length)
  370. break
  371. case 'ucs2':
  372. case 'ucs-2':
  373. case 'utf16le':
  374. case 'utf-16le':
  375. ret = utf16leWrite(this, string, offset, length)
  376. break
  377. default:
  378. throw new TypeError('Unknown encoding: ' + encoding)
  379. }
  380. return ret
  381. }
  382. Buffer.prototype.toJSON = function () {
  383. return {
  384. type: 'Buffer',
  385. data: Array.prototype.slice.call(this._arr || this, 0)
  386. }
  387. }
  388. function base64Slice (buf, start, end) {
  389. if (start === 0 && end === buf.length) {
  390. return base64.fromByteArray(buf)
  391. } else {
  392. return base64.fromByteArray(buf.slice(start, end))
  393. }
  394. }
  395. function utf8Slice (buf, start, end) {
  396. var res = ''
  397. var tmp = ''
  398. end = Math.min(buf.length, end)
  399. for (var i = start; i < end; i++) {
  400. if (buf[i] <= 0x7F) {
  401. res += decodeUtf8Char(tmp) + String.fromCharCode(buf[i])
  402. tmp = ''
  403. } else {
  404. tmp += '%' + buf[i].toString(16)
  405. }
  406. }
  407. return res + decodeUtf8Char(tmp)
  408. }
  409. function asciiSlice (buf, start, end) {
  410. var ret = ''
  411. end = Math.min(buf.length, end)
  412. for (var i = start; i < end; i++) {
  413. ret += String.fromCharCode(buf[i] & 0x7F)
  414. }
  415. return ret
  416. }
  417. function binarySlice (buf, start, end) {
  418. var ret = ''
  419. end = Math.min(buf.length, end)
  420. for (var i = start; i < end; i++) {
  421. ret += String.fromCharCode(buf[i])
  422. }
  423. return ret
  424. }
  425. function hexSlice (buf, start, end) {
  426. var len = buf.length
  427. if (!start || start < 0) start = 0
  428. if (!end || end < 0 || end > len) end = len
  429. var out = ''
  430. for (var i = start; i < end; i++) {
  431. out += toHex(buf[i])
  432. }
  433. return out
  434. }
  435. function utf16leSlice (buf, start, end) {
  436. var bytes = buf.slice(start, end)
  437. var res = ''
  438. for (var i = 0; i < bytes.length; i += 2) {
  439. res += String.fromCharCode(bytes[i] + bytes[i + 1] * 256)
  440. }
  441. return res
  442. }
  443. Buffer.prototype.slice = function (start, end) {
  444. var len = this.length
  445. start = ~~start
  446. end = end === undefined ? len : ~~end
  447. if (start < 0) {
  448. start += len;
  449. if (start < 0)
  450. start = 0
  451. } else if (start > len) {
  452. start = len
  453. }
  454. if (end < 0) {
  455. end += len
  456. if (end < 0)
  457. end = 0
  458. } else if (end > len) {
  459. end = len
  460. }
  461. if (end < start)
  462. end = start
  463. var newBuf
  464. if (Buffer.TYPED_ARRAY_SUPPORT) {
  465. newBuf = Buffer._augment(this.subarray(start, end))
  466. } else {
  467. var sliceLen = end - start
  468. newBuf = new Buffer(sliceLen, undefined, true)
  469. for (var i = 0; i < sliceLen; i++) {
  470. newBuf[i] = this[i + start]
  471. }
  472. }
  473. if (newBuf.length)
  474. newBuf.parent = this.parent || this
  475. return newBuf
  476. }
  477. /*
  478. * Need to make sure that buffer isn't trying to write out of bounds.
  479. */
  480. function checkOffset (offset, ext, length) {
  481. if ((offset % 1) !== 0 || offset < 0)
  482. throw new RangeError('offset is not uint')
  483. if (offset + ext > length)
  484. throw new RangeError('Trying to access beyond buffer length')
  485. }
  486. Buffer.prototype.readUIntLE = function (offset, byteLength, noAssert) {
  487. offset = offset >>> 0
  488. byteLength = byteLength >>> 0
  489. if (!noAssert)
  490. checkOffset(offset, byteLength, this.length)
  491. var val = this[offset]
  492. var mul = 1
  493. var i = 0
  494. while (++i < byteLength && (mul *= 0x100))
  495. val += this[offset + i] * mul
  496. return val
  497. }
  498. Buffer.prototype.readUIntBE = function (offset, byteLength, noAssert) {
  499. offset = offset >>> 0
  500. byteLength = byteLength >>> 0
  501. if (!noAssert)
  502. checkOffset(offset, byteLength, this.length)
  503. var val = this[offset + --byteLength]
  504. var mul = 1
  505. while (byteLength > 0 && (mul *= 0x100))
  506. val += this[offset + --byteLength] * mul;
  507. return val
  508. }
  509. Buffer.prototype.readUInt8 = function (offset, noAssert) {
  510. if (!noAssert)
  511. checkOffset(offset, 1, this.length)
  512. return this[offset]
  513. }
  514. Buffer.prototype.readUInt16LE = function (offset, noAssert) {
  515. if (!noAssert)
  516. checkOffset(offset, 2, this.length)
  517. return this[offset] | (this[offset + 1] << 8)
  518. }
  519. Buffer.prototype.readUInt16BE = function (offset, noAssert) {
  520. if (!noAssert)
  521. checkOffset(offset, 2, this.length)
  522. return (this[offset] << 8) | this[offset + 1]
  523. }
  524. Buffer.prototype.readUInt32LE = function (offset, noAssert) {
  525. if (!noAssert)
  526. checkOffset(offset, 4, this.length)
  527. return ((this[offset]) |
  528. (this[offset + 1] << 8) |
  529. (this[offset + 2] << 16)) +
  530. (this[offset + 3] * 0x1000000)
  531. }
  532. Buffer.prototype.readUInt32BE = function (offset, noAssert) {
  533. if (!noAssert)
  534. checkOffset(offset, 4, this.length)
  535. return (this[offset] * 0x1000000) +
  536. ((this[offset + 1] << 16) |
  537. (this[offset + 2] << 8) |
  538. this[offset + 3])
  539. }
  540. Buffer.prototype.readIntLE = function (offset, byteLength, noAssert) {
  541. offset = offset >>> 0
  542. byteLength = byteLength >>> 0
  543. if (!noAssert)
  544. checkOffset(offset, byteLength, this.length)
  545. var val = this[offset]
  546. var mul = 1
  547. var i = 0
  548. while (++i < byteLength && (mul *= 0x100))
  549. val += this[offset + i] * mul
  550. mul *= 0x80
  551. if (val >= mul)
  552. val -= Math.pow(2, 8 * byteLength)
  553. return val
  554. }
  555. Buffer.prototype.readIntBE = function (offset, byteLength, noAssert) {
  556. offset = offset >>> 0
  557. byteLength = byteLength >>> 0
  558. if (!noAssert)
  559. checkOffset(offset, byteLength, this.length)
  560. var i = byteLength
  561. var mul = 1
  562. var val = this[offset + --i]
  563. while (i > 0 && (mul *= 0x100))
  564. val += this[offset + --i] * mul
  565. mul *= 0x80
  566. if (val >= mul)
  567. val -= Math.pow(2, 8 * byteLength)
  568. return val
  569. }
  570. Buffer.prototype.readInt8 = function (offset, noAssert) {
  571. if (!noAssert)
  572. checkOffset(offset, 1, this.length)
  573. if (!(this[offset] & 0x80))
  574. return (this[offset])
  575. return ((0xff - this[offset] + 1) * -1)
  576. }
  577. Buffer.prototype.readInt16LE = function (offset, noAssert) {
  578. if (!noAssert)
  579. checkOffset(offset, 2, this.length)
  580. var val = this[offset] | (this[offset + 1] << 8)
  581. return (val & 0x8000) ? val | 0xFFFF0000 : val
  582. }
  583. Buffer.prototype.readInt16BE = function (offset, noAssert) {
  584. if (!noAssert)
  585. checkOffset(offset, 2, this.length)
  586. var val = this[offset + 1] | (this[offset] << 8)
  587. return (val & 0x8000) ? val | 0xFFFF0000 : val
  588. }
  589. Buffer.prototype.readInt32LE = function (offset, noAssert) {
  590. if (!noAssert)
  591. checkOffset(offset, 4, this.length)
  592. return (this[offset]) |
  593. (this[offset + 1] << 8) |
  594. (this[offset + 2] << 16) |
  595. (this[offset + 3] << 24)
  596. }
  597. Buffer.prototype.readInt32BE = function (offset, noAssert) {
  598. if (!noAssert)
  599. checkOffset(offset, 4, this.length)
  600. return (this[offset] << 24) |
  601. (this[offset + 1] << 16) |
  602. (this[offset + 2] << 8) |
  603. (this[offset + 3])
  604. }
  605. Buffer.prototype.readFloatLE = function (offset, noAssert) {
  606. if (!noAssert)
  607. checkOffset(offset, 4, this.length)
  608. return ieee754.read(this, offset, true, 23, 4)
  609. }
  610. Buffer.prototype.readFloatBE = function (offset, noAssert) {
  611. if (!noAssert)
  612. checkOffset(offset, 4, this.length)
  613. return ieee754.read(this, offset, false, 23, 4)
  614. }
  615. Buffer.prototype.readDoubleLE = function (offset, noAssert) {
  616. if (!noAssert)
  617. checkOffset(offset, 8, this.length)
  618. return ieee754.read(this, offset, true, 52, 8)
  619. }
  620. Buffer.prototype.readDoubleBE = function (offset, noAssert) {
  621. if (!noAssert)
  622. checkOffset(offset, 8, this.length)
  623. return ieee754.read(this, offset, false, 52, 8)
  624. }
  625. function checkInt (buf, value, offset, ext, max, min) {
  626. if (!Buffer.isBuffer(buf)) throw new TypeError('buffer must be a Buffer instance')
  627. if (value > max || value < min) throw new RangeError('value is out of bounds')
  628. if (offset + ext > buf.length) throw new RangeError('index out of range')
  629. }
  630. Buffer.prototype.writeUIntLE = function (value, offset, byteLength, noAssert) {
  631. value = +value
  632. offset = offset >>> 0
  633. byteLength = byteLength >>> 0
  634. if (!noAssert)
  635. checkInt(this, value, offset, byteLength, Math.pow(2, 8 * byteLength), 0)
  636. var mul = 1
  637. var i = 0
  638. this[offset] = value & 0xFF
  639. while (++i < byteLength && (mul *= 0x100))
  640. this[offset + i] = (value / mul) >>> 0 & 0xFF
  641. return offset + byteLength
  642. }
  643. Buffer.prototype.writeUIntBE = function (value, offset, byteLength, noAssert) {
  644. value = +value
  645. offset = offset >>> 0
  646. byteLength = byteLength >>> 0
  647. if (!noAssert)
  648. checkInt(this, value, offset, byteLength, Math.pow(2, 8 * byteLength), 0)
  649. var i = byteLength - 1
  650. var mul = 1
  651. this[offset + i] = value & 0xFF
  652. while (--i >= 0 && (mul *= 0x100))
  653. this[offset + i] = (value / mul) >>> 0 & 0xFF
  654. return offset + byteLength
  655. }
  656. Buffer.prototype.writeUInt8 = function (value, offset, noAssert) {
  657. value = +value
  658. offset = offset >>> 0
  659. if (!noAssert)
  660. checkInt(this, value, offset, 1, 0xff, 0)
  661. if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value)
  662. this[offset] = value
  663. return offset + 1
  664. }
  665. function objectWriteUInt16 (buf, value, offset, littleEndian) {
  666. if (value < 0) value = 0xffff + value + 1
  667. for (var i = 0, j = Math.min(buf.length - offset, 2); i < j; i++) {
  668. buf[offset + i] = (value & (0xff << (8 * (littleEndian ? i : 1 - i)))) >>>
  669. (littleEndian ? i : 1 - i) * 8
  670. }
  671. }
  672. Buffer.prototype.writeUInt16LE = function (value, offset, noAssert) {
  673. value = +value
  674. offset = offset >>> 0
  675. if (!noAssert)
  676. checkInt(this, value, offset, 2, 0xffff, 0)
  677. if (Buffer.TYPED_ARRAY_SUPPORT) {
  678. this[offset] = value
  679. this[offset + 1] = (value >>> 8)
  680. } else objectWriteUInt16(this, value, offset, true)
  681. return offset + 2
  682. }
  683. Buffer.prototype.writeUInt16BE = function (value, offset, noAssert) {
  684. value = +value
  685. offset = offset >>> 0
  686. if (!noAssert)
  687. checkInt(this, value, offset, 2, 0xffff, 0)
  688. if (Buffer.TYPED_ARRAY_SUPPORT) {
  689. this[offset] = (value >>> 8)
  690. this[offset + 1] = value
  691. } else objectWriteUInt16(this, value, offset, false)
  692. return offset + 2
  693. }
  694. function objectWriteUInt32 (buf, value, offset, littleEndian) {
  695. if (value < 0) value = 0xffffffff + value + 1
  696. for (var i = 0, j = Math.min(buf.length - offset, 4); i < j; i++) {
  697. buf[offset + i] = (value >>> (littleEndian ? i : 3 - i) * 8) & 0xff
  698. }
  699. }
  700. Buffer.prototype.writeUInt32LE = function (value, offset, noAssert) {
  701. value = +value
  702. offset = offset >>> 0
  703. if (!noAssert)
  704. checkInt(this, value, offset, 4, 0xffffffff, 0)
  705. if (Buffer.TYPED_ARRAY_SUPPORT) {
  706. this[offset + 3] = (value >>> 24)
  707. this[offset + 2] = (value >>> 16)
  708. this[offset + 1] = (value >>> 8)
  709. this[offset] = value
  710. } else objectWriteUInt32(this, value, offset, true)
  711. return offset + 4
  712. }
  713. Buffer.prototype.writeUInt32BE = function (value, offset, noAssert) {
  714. value = +value
  715. offset = offset >>> 0
  716. if (!noAssert)
  717. checkInt(this, value, offset, 4, 0xffffffff, 0)
  718. if (Buffer.TYPED_ARRAY_SUPPORT) {
  719. this[offset] = (value >>> 24)
  720. this[offset + 1] = (value >>> 16)
  721. this[offset + 2] = (value >>> 8)
  722. this[offset + 3] = value
  723. } else objectWriteUInt32(this, value, offset, false)
  724. return offset + 4
  725. }
  726. Buffer.prototype.writeIntLE = function (value, offset, byteLength, noAssert) {
  727. value = +value
  728. offset = offset >>> 0
  729. if (!noAssert) {
  730. checkInt(this,
  731. value,
  732. offset,
  733. byteLength,
  734. Math.pow(2, 8 * byteLength - 1) - 1,
  735. -Math.pow(2, 8 * byteLength - 1))
  736. }
  737. var i = 0
  738. var mul = 1
  739. var sub = value < 0 ? 1 : 0
  740. this[offset] = value & 0xFF
  741. while (++i < byteLength && (mul *= 0x100))
  742. this[offset + i] = ((value / mul) >> 0) - sub & 0xFF
  743. return offset + byteLength
  744. }
  745. Buffer.prototype.writeIntBE = function (value, offset, byteLength, noAssert) {
  746. value = +value
  747. offset = offset >>> 0
  748. if (!noAssert) {
  749. checkInt(this,
  750. value,
  751. offset,
  752. byteLength,
  753. Math.pow(2, 8 * byteLength - 1) - 1,
  754. -Math.pow(2, 8 * byteLength - 1))
  755. }
  756. var i = byteLength - 1
  757. var mul = 1
  758. var sub = value < 0 ? 1 : 0
  759. this[offset + i] = value & 0xFF
  760. while (--i >= 0 && (mul *= 0x100))
  761. this[offset + i] = ((value / mul) >> 0) - sub & 0xFF
  762. return offset + byteLength
  763. }
  764. Buffer.prototype.writeInt8 = function (value, offset, noAssert) {
  765. value = +value
  766. offset = offset >>> 0
  767. if (!noAssert)
  768. checkInt(this, value, offset, 1, 0x7f, -0x80)
  769. if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value)
  770. if (value < 0) value = 0xff + value + 1
  771. this[offset] = value
  772. return offset + 1
  773. }
  774. Buffer.prototype.writeInt16LE = function (value, offset, noAssert) {
  775. value = +value
  776. offset = offset >>> 0
  777. if (!noAssert)
  778. checkInt(this, value, offset, 2, 0x7fff, -0x8000)
  779. if (Buffer.TYPED_ARRAY_SUPPORT) {
  780. this[offset] = value
  781. this[offset + 1] = (value >>> 8)
  782. } else objectWriteUInt16(this, value, offset, true)
  783. return offset + 2
  784. }
  785. Buffer.prototype.writeInt16BE = function (value, offset, noAssert) {
  786. value = +value
  787. offset = offset >>> 0
  788. if (!noAssert)
  789. checkInt(this, value, offset, 2, 0x7fff, -0x8000)
  790. if (Buffer.TYPED_ARRAY_SUPPORT) {
  791. this[offset] = (value >>> 8)
  792. this[offset + 1] = value
  793. } else objectWriteUInt16(this, value, offset, false)
  794. return offset + 2
  795. }
  796. Buffer.prototype.writeInt32LE = function (value, offset, noAssert) {
  797. value = +value
  798. offset = offset >>> 0
  799. if (!noAssert)
  800. checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000)
  801. if (Buffer.TYPED_ARRAY_SUPPORT) {
  802. this[offset] = value
  803. this[offset + 1] = (value >>> 8)
  804. this[offset + 2] = (value >>> 16)
  805. this[offset + 3] = (value >>> 24)
  806. } else objectWriteUInt32(this, value, offset, true)
  807. return offset + 4
  808. }
  809. Buffer.prototype.writeInt32BE = function (value, offset, noAssert) {
  810. value = +value
  811. offset = offset >>> 0
  812. if (!noAssert)
  813. checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000)
  814. if (value < 0) value = 0xffffffff + value + 1
  815. if (Buffer.TYPED_ARRAY_SUPPORT) {
  816. this[offset] = (value >>> 24)
  817. this[offset + 1] = (value >>> 16)
  818. this[offset + 2] = (value >>> 8)
  819. this[offset + 3] = value
  820. } else objectWriteUInt32(this, value, offset, false)
  821. return offset + 4
  822. }
  823. function checkIEEE754 (buf, value, offset, ext, max, min) {
  824. if (value > max || value < min) throw new RangeError('value is out of bounds')
  825. if (offset + ext > buf.length) throw new RangeError('index out of range')
  826. if (offset < 0) throw new RangeError('index out of range')
  827. }
  828. function writeFloat (buf, value, offset, littleEndian, noAssert) {
  829. if (!noAssert)
  830. checkIEEE754(buf, value, offset, 4, 3.4028234663852886e+38, -3.4028234663852886e+38)
  831. ieee754.write(buf, value, offset, littleEndian, 23, 4)
  832. return offset + 4
  833. }
  834. Buffer.prototype.writeFloatLE = function (value, offset, noAssert) {
  835. return writeFloat(this, value, offset, true, noAssert)
  836. }
  837. Buffer.prototype.writeFloatBE = function (value, offset, noAssert) {
  838. return writeFloat(this, value, offset, false, noAssert)
  839. }
  840. function writeDouble (buf, value, offset, littleEndian, noAssert) {
  841. if (!noAssert)
  842. checkIEEE754(buf, value, offset, 8, 1.7976931348623157E+308, -1.7976931348623157E+308)
  843. ieee754.write(buf, value, offset, littleEndian, 52, 8)
  844. return offset + 8
  845. }
  846. Buffer.prototype.writeDoubleLE = function (value, offset, noAssert) {
  847. return writeDouble(this, value, offset, true, noAssert)
  848. }
  849. Buffer.prototype.writeDoubleBE = function (value, offset, noAssert) {
  850. return writeDouble(this, value, offset, false, noAssert)
  851. }
  852. // copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length)
  853. Buffer.prototype.copy = function (target, target_start, start, end) {
  854. var source = this
  855. if (!start) start = 0
  856. if (!end && end !== 0) end = this.length
  857. if (target_start >= target.length) target_start = target.length
  858. if (!target_start) target_start = 0
  859. if (end > 0 && end < start) end = start
  860. // Copy 0 bytes; we're done
  861. if (end === start) return 0
  862. if (target.length === 0 || source.length === 0) return 0
  863. // Fatal error conditions
  864. if (target_start < 0)
  865. throw new RangeError('targetStart out of bounds')
  866. if (start < 0 || start >= source.length) throw new RangeError('sourceStart out of bounds')
  867. if (end < 0) throw new RangeError('sourceEnd out of bounds')
  868. // Are we oob?
  869. if (end > this.length)
  870. end = this.length
  871. if (target.length - target_start < end - start)
  872. end = target.length - target_start + start
  873. var len = end - start
  874. if (len < 1000 || !Buffer.TYPED_ARRAY_SUPPORT) {
  875. for (var i = 0; i < len; i++) {
  876. target[i + target_start] = this[i + start]
  877. }
  878. } else {
  879. target._set(this.subarray(start, start + len), target_start)
  880. }
  881. return len
  882. }
  883. // fill(value, start=0, end=buffer.length)
  884. Buffer.prototype.fill = function (value, start, end) {
  885. if (!value) value = 0
  886. if (!start) start = 0
  887. if (!end) end = this.length
  888. if (end < start) throw new RangeError('end < start')
  889. // Fill 0 bytes; we're done
  890. if (end === start) return
  891. if (this.length === 0) return
  892. if (start < 0 || start >= this.length) throw new RangeError('start out of bounds')
  893. if (end < 0 || end > this.length) throw new RangeError('end out of bounds')
  894. var i
  895. if (typeof value === 'number') {
  896. for (i = start; i < end; i++) {
  897. this[i] = value
  898. }
  899. } else {
  900. var bytes = utf8ToBytes(value.toString())
  901. var len = bytes.length
  902. for (i = start; i < end; i++) {
  903. this[i] = bytes[i % len]
  904. }
  905. }
  906. return this
  907. }
  908. /**
  909. * Creates a new `ArrayBuffer` with the *copied* memory of the buffer instance.
  910. * Added in Node 0.12. Only available in browsers that support ArrayBuffer.
  911. */
  912. Buffer.prototype.toArrayBuffer = function () {
  913. if (typeof Uint8Array !== 'undefined') {
  914. if (Buffer.TYPED_ARRAY_SUPPORT) {
  915. return (new Buffer(this)).buffer
  916. } else {
  917. var buf = new Uint8Array(this.length)
  918. for (var i = 0, len = buf.length; i < len; i += 1) {
  919. buf[i] = this[i]
  920. }
  921. return buf.buffer
  922. }
  923. } else {
  924. throw new TypeError('Buffer.toArrayBuffer not supported in this browser')
  925. }
  926. }
  927. // HELPER FUNCTIONS
  928. // ================
  929. var BP = Buffer.prototype
  930. /**
  931. * Augment a Uint8Array *instance* (not the Uint8Array class!) with Buffer methods
  932. */
  933. Buffer._augment = function (arr) {
  934. arr.constructor = Buffer
  935. arr._isBuffer = true
  936. // save reference to original Uint8Array get/set methods before overwriting
  937. arr._get = arr.get
  938. arr._set = arr.set
  939. // deprecated, will be removed in node 0.13+
  940. arr.get = BP.get
  941. arr.set = BP.set
  942. arr.write = BP.write
  943. arr.toString = BP.toString
  944. arr.toLocaleString = BP.toString
  945. arr.toJSON = BP.toJSON
  946. arr.equals = BP.equals
  947. arr.compare = BP.compare
  948. arr.copy = BP.copy
  949. arr.slice = BP.slice
  950. arr.readUIntLE = BP.readUIntLE
  951. arr.readUIntBE = BP.readUIntBE
  952. arr.readUInt8 = BP.readUInt8
  953. arr.readUInt16LE = BP.readUInt16LE
  954. arr.readUInt16BE = BP.readUInt16BE
  955. arr.readUInt32LE = BP.readUInt32LE
  956. arr.readUInt32BE = BP.readUInt32BE
  957. arr.readIntLE = BP.readIntLE
  958. arr.readIntBE = BP.readIntBE
  959. arr.readInt8 = BP.readInt8
  960. arr.readInt16LE = BP.readInt16LE
  961. arr.readInt16BE = BP.readInt16BE
  962. arr.readInt32LE = BP.readInt32LE
  963. arr.readInt32BE = BP.readInt32BE
  964. arr.readFloatLE = BP.readFloatLE
  965. arr.readFloatBE = BP.readFloatBE
  966. arr.readDoubleLE = BP.readDoubleLE
  967. arr.readDoubleBE = BP.readDoubleBE
  968. arr.writeUInt8 = BP.writeUInt8
  969. arr.writeUIntLE = BP.writeUIntLE
  970. arr.writeUIntBE = BP.writeUIntBE
  971. arr.writeUInt16LE = BP.writeUInt16LE
  972. arr.writeUInt16BE = BP.writeUInt16BE
  973. arr.writeUInt32LE = BP.writeUInt32LE
  974. arr.writeUInt32BE = BP.writeUInt32BE
  975. arr.writeIntLE = BP.writeIntLE
  976. arr.writeIntBE = BP.writeIntBE
  977. arr.writeInt8 = BP.writeInt8
  978. arr.writeInt16LE = BP.writeInt16LE
  979. arr.writeInt16BE = BP.writeInt16BE
  980. arr.writeInt32LE = BP.writeInt32LE
  981. arr.writeInt32BE = BP.writeInt32BE
  982. arr.writeFloatLE = BP.writeFloatLE
  983. arr.writeFloatBE = BP.writeFloatBE
  984. arr.writeDoubleLE = BP.writeDoubleLE
  985. arr.writeDoubleBE = BP.writeDoubleBE
  986. arr.fill = BP.fill
  987. arr.inspect = BP.inspect
  988. arr.toArrayBuffer = BP.toArrayBuffer
  989. return arr
  990. }
  991. var INVALID_BASE64_RE = /[^+\/0-9A-z\-]/g
  992. function base64clean (str) {
  993. // Node strips out invalid characters like \n and \t from the string, base64-js does not
  994. str = stringtrim(str).replace(INVALID_BASE64_RE, '')
  995. // Node converts strings with length < 2 to ''
  996. if (str.length < 2) return ''
  997. // Node allows for non-padded base64 strings (missing trailing ===), base64-js does not
  998. while (str.length % 4 !== 0) {
  999. str = str + '='
  1000. }
  1001. return str
  1002. }
  1003. function stringtrim (str) {
  1004. if (str.trim) return str.trim()
  1005. return str.replace(/^\s+|\s+$/g, '')
  1006. }
  1007. function isArrayish (subject) {
  1008. return isArray(subject) || Buffer.isBuffer(subject) ||
  1009. subject && typeof subject === 'object' &&
  1010. typeof subject.length === 'number'
  1011. }
  1012. function toHex (n) {
  1013. if (n < 16) return '0' + n.toString(16)
  1014. return n.toString(16)
  1015. }
  1016. function utf8ToBytes(string, units) {
  1017. var codePoint, length = string.length
  1018. var leadSurrogate = null
  1019. units = units || Infinity
  1020. var bytes = []
  1021. var i = 0
  1022. for (; i<length; i++) {
  1023. codePoint = string.charCodeAt(i)
  1024. // is surrogate component
  1025. if (codePoint > 0xD7FF && codePoint < 0xE000) {
  1026. // last char was a lead
  1027. if (leadSurrogate) {
  1028. // 2 leads in a row
  1029. if (codePoint < 0xDC00) {
  1030. if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
  1031. leadSurrogate = codePoint
  1032. continue
  1033. }
  1034. // valid surrogate pair
  1035. else {
  1036. codePoint = leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00 | 0x10000
  1037. leadSurrogate = null
  1038. }
  1039. }
  1040. // no lead yet
  1041. else {
  1042. // unexpected trail
  1043. if (codePoint > 0xDBFF) {
  1044. if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
  1045. continue
  1046. }
  1047. // unpaired lead
  1048. else if (i + 1 === length) {
  1049. if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
  1050. continue
  1051. }
  1052. // valid lead
  1053. else {
  1054. leadSurrogate = codePoint
  1055. continue
  1056. }
  1057. }
  1058. }
  1059. // valid bmp char, but last char was a lead
  1060. else if (leadSurrogate) {
  1061. if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
  1062. leadSurrogate = null
  1063. }
  1064. // encode utf8
  1065. if (codePoint < 0x80) {
  1066. if ((units -= 1) < 0) break
  1067. bytes.push(codePoint)
  1068. }
  1069. else if (codePoint < 0x800) {
  1070. if ((units -= 2) < 0) break
  1071. bytes.push(
  1072. codePoint >> 0x6 | 0xC0,
  1073. codePoint & 0x3F | 0x80
  1074. );
  1075. }
  1076. else if (codePoint < 0x10000) {
  1077. if ((units -= 3) < 0) break
  1078. bytes.push(
  1079. codePoint >> 0xC | 0xE0,
  1080. codePoint >> 0x6 & 0x3F | 0x80,
  1081. codePoint & 0x3F | 0x80
  1082. );
  1083. }
  1084. else if (codePoint < 0x200000) {
  1085. if ((units -= 4) < 0) break
  1086. bytes.push(
  1087. codePoint >> 0x12 | 0xF0,
  1088. codePoint >> 0xC & 0x3F | 0x80,
  1089. codePoint >> 0x6 & 0x3F | 0x80,
  1090. codePoint & 0x3F | 0x80
  1091. );
  1092. }
  1093. else {
  1094. throw new Error('Invalid code point')
  1095. }
  1096. }
  1097. return bytes
  1098. }
  1099. function asciiToBytes (str) {
  1100. var byteArray = []
  1101. for (var i = 0; i < str.length; i++) {
  1102. // Node's code seems to be doing this and not & 0x7F..
  1103. byteArray.push(str.charCodeAt(i) & 0xFF)
  1104. }
  1105. return byteArray
  1106. }
  1107. function utf16leToBytes (str, units) {
  1108. var c, hi, lo
  1109. var byteArray = []
  1110. for (var i = 0; i < str.length; i++) {
  1111. if ((units -= 2) < 0) break
  1112. c = str.charCodeAt(i)
  1113. hi = c >> 8
  1114. lo = c % 256
  1115. byteArray.push(lo)
  1116. byteArray.push(hi)
  1117. }
  1118. return byteArray
  1119. }
  1120. function base64ToBytes (str) {
  1121. return base64.toByteArray(base64clean(str))
  1122. }
  1123. function blitBuffer (src, dst, offset, length, unitSize) {
  1124. if (unitSize) length -= length % unitSize;
  1125. for (var i = 0; i < length; i++) {
  1126. if ((i + offset >= dst.length) || (i >= src.length))
  1127. break
  1128. dst[i + offset] = src[i]
  1129. }
  1130. return i
  1131. }
  1132. function decodeUtf8Char (str) {
  1133. try {
  1134. return decodeURIComponent(str)
  1135. } catch (err) {
  1136. return String.fromCharCode(0xFFFD) // UTF 8 invalid char
  1137. }
  1138. }
  1139. },{"base64-js":3,"ieee754":4,"is-array":5}],3:[function(_dereq_,module,exports){
  1140. var lookup = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
  1141. ;(function (exports) {
  1142. 'use strict';
  1143. var Arr = (typeof Uint8Array !== 'undefined')
  1144. ? Uint8Array
  1145. : Array
  1146. var PLUS = '+'.charCodeAt(0)
  1147. var SLASH = '/'.charCodeAt(0)
  1148. var NUMBER = '0'.charCodeAt(0)
  1149. var LOWER = 'a'.charCodeAt(0)
  1150. var UPPER = 'A'.charCodeAt(0)
  1151. var PLUS_URL_SAFE = '-'.charCodeAt(0)
  1152. var SLASH_URL_SAFE = '_'.charCodeAt(0)
  1153. function decode (elt) {
  1154. var code = elt.charCodeAt(0)
  1155. if (code === PLUS ||
  1156. code === PLUS_URL_SAFE)
  1157. return 62 // '+'
  1158. if (code === SLASH ||
  1159. code === SLASH_URL_SAFE)
  1160. return 63 // '/'
  1161. if (code < NUMBER)
  1162. return -1 //no match
  1163. if (code < NUMBER + 10)
  1164. return code - NUMBER + 26 + 26
  1165. if (code < UPPER + 26)
  1166. return code - UPPER
  1167. if (code < LOWER + 26)
  1168. return code - LOWER + 26
  1169. }
  1170. function b64ToByteArray (b64) {
  1171. var i, j, l, tmp, placeHolders, arr
  1172. if (b64.length % 4 > 0) {
  1173. throw new Error('Invalid string. Length must be a multiple of 4')
  1174. }
  1175. // the number of equal signs (place holders)
  1176. // if there are two placeholders, than the two characters before it
  1177. // represent one byte
  1178. // if there is only one, then the three characters before it represent 2 bytes
  1179. // this is just a cheap hack to not do indexOf twice
  1180. var len = b64.length
  1181. placeHolders = '=' === b64.charAt(len - 2) ? 2 : '=' === b64.charAt(len - 1) ? 1 : 0
  1182. // base64 is 4/3 + up to two characters of the original data
  1183. arr = new Arr(b64.length * 3 / 4 - placeHolders)
  1184. // if there are placeholders, only get up to the last complete 4 chars
  1185. l = placeHolders > 0 ? b64.length - 4 : b64.length
  1186. var L = 0
  1187. function push (v) {
  1188. arr[L++] = v
  1189. }
  1190. for (i = 0, j = 0; i < l; i += 4, j += 3) {
  1191. tmp = (decode(b64.charAt(i)) << 18) | (decode(b64.charAt(i + 1)) << 12) | (decode(b64.charAt(i + 2)) << 6) | decode(b64.charAt(i + 3))
  1192. push((tmp & 0xFF0000) >> 16)
  1193. push((tmp & 0xFF00) >> 8)
  1194. push(tmp & 0xFF)
  1195. }
  1196. if (placeHolders === 2) {
  1197. tmp = (decode(b64.charAt(i)) << 2) | (decode(b64.charAt(i + 1)) >> 4)
  1198. push(tmp & 0xFF)
  1199. } else if (placeHolders === 1) {
  1200. tmp = (decode(b64.charAt(i)) << 10) | (decode(b64.charAt(i + 1)) << 4) | (decode(b64.charAt(i + 2)) >> 2)
  1201. push((tmp >> 8) & 0xFF)
  1202. push(tmp & 0xFF)
  1203. }
  1204. return arr
  1205. }
  1206. function uint8ToBase64 (uint8) {
  1207. var i,
  1208. extraBytes = uint8.length % 3, // if we have 1 byte left, pad 2 bytes
  1209. output = "",
  1210. temp, length
  1211. function encode (num) {
  1212. return lookup.charAt(num)
  1213. }
  1214. function tripletToBase64 (num) {
  1215. return encode(num >> 18 & 0x3F) + encode(num >> 12 & 0x3F) + encode(num >> 6 & 0x3F) + encode(num & 0x3F)
  1216. }
  1217. // go through the array every three bytes, we'll deal with trailing stuff later
  1218. for (i = 0, length = uint8.length - extraBytes; i < length; i += 3) {
  1219. temp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2])
  1220. output += tripletToBase64(temp)
  1221. }
  1222. // pad the end with zeros, but make sure to not forget the extra bytes
  1223. switch (extraBytes) {
  1224. case 1:
  1225. temp = uint8[uint8.length - 1]
  1226. output += encode(temp >> 2)
  1227. output += encode((temp << 4) & 0x3F)
  1228. output += '=='
  1229. break
  1230. case 2:
  1231. temp = (uint8[uint8.length - 2] << 8) + (uint8[uint8.length - 1])
  1232. output += encode(temp >> 10)
  1233. output += encode((temp >> 4) & 0x3F)
  1234. output += encode((temp << 2) & 0x3F)
  1235. output += '='
  1236. break
  1237. }
  1238. return output
  1239. }
  1240. exports.toByteArray = b64ToByteArray
  1241. exports.fromByteArray = uint8ToBase64
  1242. }(typeof exports === 'undefined' ? (this.base64js = {}) : exports))
  1243. },{}],4:[function(_dereq_,module,exports){
  1244. exports.read = function(buffer, offset, isLE, mLen, nBytes) {
  1245. var e, m,
  1246. eLen = nBytes * 8 - mLen - 1,
  1247. eMax = (1 << eLen) - 1,
  1248. eBias = eMax >> 1,
  1249. nBits = -7,
  1250. i = isLE ? (nBytes - 1) : 0,
  1251. d = isLE ? -1 : 1,
  1252. s = buffer[offset + i];
  1253. i += d;
  1254. e = s & ((1 << (-nBits)) - 1);
  1255. s >>= (-nBits);
  1256. nBits += eLen;
  1257. for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8);
  1258. m = e & ((1 << (-nBits)) - 1);
  1259. e >>= (-nBits);
  1260. nBits += mLen;
  1261. for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8);
  1262. if (e === 0) {
  1263. e = 1 - eBias;
  1264. } else if (e === eMax) {
  1265. return m ? NaN : ((s ? -1 : 1) * Infinity);
  1266. } else {
  1267. m = m + Math.pow(2, mLen);
  1268. e = e - eBias;
  1269. }
  1270. return (s ? -1 : 1) * m * Math.pow(2, e - mLen);
  1271. };
  1272. exports.write = function(buffer, value, offset, isLE, mLen, nBytes) {
  1273. var e, m, c,
  1274. eLen = nBytes * 8 - mLen - 1,
  1275. eMax = (1 << eLen) - 1,
  1276. eBias = eMax >> 1,
  1277. rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0),
  1278. i = isLE ? 0 : (nBytes - 1),
  1279. d = isLE ? 1 : -1,
  1280. s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0;
  1281. value = Math.abs(value);
  1282. if (isNaN(value) || value === Infinity) {
  1283. m = isNaN(value) ? 1 : 0;
  1284. e = eMax;
  1285. } else {
  1286. e = Math.floor(Math.log(value) / Math.LN2);
  1287. if (value * (c = Math.pow(2, -e)) < 1) {
  1288. e--;
  1289. c *= 2;
  1290. }
  1291. if (e + eBias >= 1) {
  1292. value += rt / c;
  1293. } else {
  1294. value += rt * Math.pow(2, 1 - eBias);
  1295. }
  1296. if (value * c >= 2) {
  1297. e++;
  1298. c /= 2;
  1299. }
  1300. if (e + eBias >= eMax) {
  1301. m = 0;
  1302. e = eMax;
  1303. } else if (e + eBias >= 1) {
  1304. m = (value * c - 1) * Math.pow(2, mLen);
  1305. e = e + eBias;
  1306. } else {
  1307. m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen);
  1308. e = 0;
  1309. }
  1310. }
  1311. for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8);
  1312. e = (e << mLen) | m;
  1313. eLen += mLen;
  1314. for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8);
  1315. buffer[offset + i - d] |= s * 128;
  1316. };
  1317. },{}],5:[function(_dereq_,module,exports){
  1318. /**
  1319. * isArray
  1320. */
  1321. var isArray = Array.isArray;
  1322. /**
  1323. * toString
  1324. */
  1325. var str = Object.prototype.toString;
  1326. /**
  1327. * Whether or not the given `val`
  1328. * is an array.
  1329. *
  1330. * example:
  1331. *
  1332. * isArray([]);
  1333. * // > true
  1334. * isArray(arguments);
  1335. * // > false
  1336. * isArray('');
  1337. * // > false
  1338. *
  1339. * @param {mixed} val
  1340. * @return {bool}
  1341. */
  1342. module.exports = isArray || function (val) {
  1343. return !! val && '[object Array]' == str.call(val);
  1344. };
  1345. },{}],6:[function(_dereq_,module,exports){
  1346. (function (Buffer){
  1347. /* build: `node build.js modules=text,serialization,parser,gradient,pattern,shadow,freedrawing,image_filters,serialization no-es5-compat minifier=uglifyjs` */
  1348. /*! Fabric.js Copyright 2008-2014, Printio (Juriy Zaytsev, Maxim Chernyak) */
  1349. var fabric = fabric || { version: "1.4.11" };
  1350. if (typeof exports !== 'undefined') {
  1351. exports.fabric = fabric;
  1352. }
  1353. if (typeof document !== 'undefined' && typeof window !== 'undefined') {
  1354. fabric.document = document;
  1355. fabric.window = window;
  1356. }
  1357. else {
  1358. // assume we're running under node.js when document/window are not present
  1359. fabric.document = _dereq_("jsdom")
  1360. .jsdom("<!DOCTYPE html><html><head></head><body></body></html>");
  1361. fabric.window = fabric.document.createWindow();
  1362. }
  1363. /**
  1364. * True when in environment that supports touch events
  1365. * @type boolean
  1366. */
  1367. fabric.isTouchSupported = "ontouchstart" in fabric.document.documentElement;
  1368. /**
  1369. * True when in environment that's probably Node.js
  1370. * @type boolean
  1371. */
  1372. fabric.isLikelyNode = typeof Buffer !== 'undefined' &&
  1373. typeof window === 'undefined';
  1374. /**
  1375. * Attributes parsed from all SVG elements
  1376. * @type array
  1377. */
  1378. fabric.SHARED_ATTRIBUTES = [
  1379. "display",
  1380. "transform",
  1381. "fill", "fill-opacity", "fill-rule",
  1382. "opacity",
  1383. "stroke", "stroke-dasharray", "stroke-linecap",
  1384. "stroke-linejoin", "stroke-miterlimit",
  1385. "stroke-opacity", "stroke-width"
  1386. ];
  1387. /**
  1388. * Pixel per Inch as a default value set to 96. Can be changed for more realistic conversion.
  1389. */
  1390. fabric.DPI = 96;
  1391. /*!
  1392. * Copyright (c) 2009 Simo Kinnunen.
  1393. * Licensed under the MIT license.
  1394. */
  1395. var Cufon = (function() {
  1396. /** @ignore */
  1397. var api = function() {
  1398. return api.replace.apply(null, arguments);
  1399. };
  1400. /** @ignore */
  1401. var DOM = api.DOM = {
  1402. ready: (function() {
  1403. var complete = false, readyStatus = { loaded: 1, complete: 1 };
  1404. var queue = [], /** @ignore */ perform = function() {
  1405. if (complete) return;
  1406. complete = true;
  1407. for (var fn; fn = queue.shift(); fn());
  1408. };
  1409. // Gecko, Opera, WebKit r26101+
  1410. if (fabric.document.addEventListener) {
  1411. fabric.document.addEventListener('DOMContentLoaded', perform, false);
  1412. fabric.window.addEventListener('pageshow', perform, false); // For cached Gecko pages
  1413. }
  1414. // Old WebKit, Internet Explorer
  1415. if (!fabric.window.opera && fabric.document.readyState) (function() {
  1416. readyStatus[fabric.document.readyState] ? perform() : setTimeout(arguments.callee, 10);
  1417. })();
  1418. // Internet Explorer
  1419. if (fabric.document.readyState && fabric.document.createStyleSheet) (function() {
  1420. try {
  1421. fabric.document.body.doScroll('left');
  1422. perform();
  1423. }
  1424. catch (e) {
  1425. setTimeout(arguments.callee, 1);
  1426. }
  1427. })();
  1428. addEvent(fabric.window, 'load', perform); // Fallback
  1429. return function(listener) {
  1430. if (!arguments.length) perform();
  1431. else complete ? listener() : queue.push(listener);
  1432. };
  1433. })()
  1434. };
  1435. /** @ignore */
  1436. var CSS = api.CSS = /** @ignore */ {
  1437. /** @ignore */
  1438. Size: function(value, base) {
  1439. this.value = parseFloat(value);
  1440. this.unit = String(value).match(/[a-z%]*$/)[0] || 'px';
  1441. /** @ignore */
  1442. this.convert = function(value) {
  1443. return value / base * this.value;
  1444. };
  1445. /** @ignore */
  1446. this.convertFrom = function(value) {
  1447. return value / this.value * base;
  1448. };
  1449. /** @ignore */
  1450. this.toString = function() {
  1451. return this.value + this.unit;
  1452. };
  1453. },
  1454. /** @ignore */
  1455. getStyle: function(el) {
  1456. return new Style(el.style);
  1457. /*
  1458. var view = document.defaultView;
  1459. if (view && view.getComputedStyle) return new Style(view.getComputedStyle(el, null));
  1460. if (el.currentStyle) return new Style(el.currentStyle);
  1461. return new Style(el.style);
  1462. */
  1463. },
  1464. quotedList: cached(function(value) {
  1465. // doesn't work properly with empty quoted strings (""), but
  1466. // it's not worth the extra code.
  1467. var list = [], re = /\s*((["'])([\s\S]*?[^\\])\2|[^,]+)\s*/g, match;
  1468. while (match = re.exec(value)) list.push(match[3] || match[1]);
  1469. return list;
  1470. }),
  1471. ready: (function() {
  1472. var complete = false;
  1473. var queue = [], perform = function() {
  1474. complete = true;
  1475. for (var fn; fn = queue.shift(); fn());
  1476. };
  1477. // Safari 2 does not include <style> elements in document.styleSheets.
  1478. // Safari 2 also does not support Object.prototype.propertyIsEnumerable.
  1479. var styleElements = Object.prototype.propertyIsEnumerable ? elementsByTagName('style') : { length: 0 };
  1480. var linkElements = elementsByTagName('link');
  1481. DOM.ready(function() {
  1482. // These checks are actually only needed for WebKit-based browsers, but don't really hurt other browsers.
  1483. var linkStyles = 0, link;
  1484. for (var i = 0, l = linkElements.length; link = linkElements[i], i < l; ++i) {
  1485. // WebKit does not load alternate stylesheets.
  1486. if (!link.disabled && link.rel.toLowerCase() == 'stylesheet') ++linkStyles;
  1487. }
  1488. if (fabric.document.styleSheets.length >= styleElements.length + linkStyles) perform();
  1489. else setTimeout(arguments.callee, 10);
  1490. });
  1491. return function(listener) {
  1492. if (complete) listener();
  1493. else queue.push(listener);
  1494. };
  1495. })(),
  1496. /** @ignore */
  1497. supports: function(property, value) {
  1498. var checker = fabric.document.createElement('span').style;
  1499. if (checker[property] === undefined) return false;
  1500. checker[property] = value;
  1501. return checker[property] === value;
  1502. },
  1503. /** @ignore */
  1504. textAlign: function(word, style, position, wordCount) {
  1505. if (style.get('textAlign') == 'right') {
  1506. if (position > 0) word = ' ' + word;
  1507. }
  1508. else if (position < wordCount - 1) word += ' ';
  1509. return word;
  1510. },
  1511. /** @ignore */
  1512. textDecoration: function(el, style) {
  1513. if (!style) style = this.getStyle(el);
  1514. var types = {
  1515. underline: null,
  1516. overline: null,
  1517. 'line-through': null
  1518. };
  1519. for (var search = el; search.parentNode && search.parentNode.nodeType == 1; ) {
  1520. var foundAll = true;
  1521. for (var type in types) {
  1522. if (types[type]) continue;
  1523. if (style.get('textDecoration').indexOf(type) != -1) types[type] = style.get('color');
  1524. foundAll = false;
  1525. }
  1526. if (foundAll) break; // this is rather unlikely to happen
  1527. style = this.getStyle(search = search.parentNode);
  1528. }
  1529. return types;
  1530. },
  1531. textShadow: cached(function(value) {
  1532. if (value == 'none') return null;
  1533. var shadows = [], currentShadow = {}, result, offCount = 0;
  1534. var re = /(#[a-f0-9]+|[a-z]+\(.*?\)|[a-z]+)|(-?[\d.]+[a-z%]*)|,/ig;
  1535. while (result = re.exec(value)) {
  1536. if (result[0] == ',') {
  1537. shadows.push(currentShadow);
  1538. currentShadow = {}, offCount = 0;
  1539. }
  1540. else if (result[1]) {
  1541. currentShadow.color = result[1];
  1542. }
  1543. else {
  1544. currentShadow[[ 'offX', 'offY', 'blur' ][offCount++]] = result[2];
  1545. }
  1546. }
  1547. shadows.push(currentShadow);
  1548. return shadows;
  1549. }),
  1550. color: cached(function(value) {
  1551. var parsed = {};
  1552. parsed.color = value.replace(/^rgba\((.*?),\s*([\d.]+)\)/, function($0, $1, $2) {
  1553. parsed.opacity = parseFloat($2);
  1554. return 'rgb(' + $1 + ')';
  1555. });
  1556. return parsed;
  1557. }),
  1558. /** @ignore */
  1559. textTransform: function(text, style) {
  1560. return text[{
  1561. uppercase: 'toUpperCase',
  1562. lowercase: 'toLowerCase'
  1563. }[style.get('textTransform')] || 'toString']();
  1564. }
  1565. };
  1566. function Font(data) {
  1567. var face = this.face = data.face;
  1568. this.glyphs = data.glyphs;
  1569. this.w = data.w;
  1570. this.baseSize = parseInt(face['units-per-em'], 10);
  1571. this.family = face['font-family'].toLowerCase();
  1572. this.weight = face['font-weight'];
  1573. this.style = face['font-style'] || 'normal';
  1574. this.viewBox = (function () {
  1575. var parts = face.bbox.split(/\s+/);
  1576. var box = {
  1577. minX: parseInt(parts[0], 10),
  1578. minY: parseInt(parts[1], 10),
  1579. maxX: parseInt(parts[2], 10),
  1580. maxY: parseInt(parts[3], 10)
  1581. };
  1582. box.width = box.maxX - box.minX,
  1583. box.height = box.maxY - box.minY;
  1584. /** @ignore */
  1585. box.toString = function() {
  1586. return [ this.minX, this.minY, this.width, this.height ].join(' ');
  1587. };
  1588. return box;
  1589. })();
  1590. this.ascent = -parseInt(face.ascent, 10);
  1591. this.descent = -parseInt(face.descent, 10);
  1592. this.height = -this.ascent + this.descent;
  1593. }
  1594. function FontFamily() {
  1595. var styles = {}, mapping = {
  1596. oblique: 'italic',
  1597. italic: 'oblique'
  1598. };
  1599. this.add = function(font) {
  1600. (styles[font.style] || (styles[font.style] = {}))[font.weight] = font;
  1601. };
  1602. /** @ignore */
  1603. this.get = function(style, weight) {
  1604. var weights = styles[style] || styles[mapping[style]]
  1605. || styles.normal || styles.italic || styles.oblique;
  1606. if (!weights) return null;
  1607. // we don't have to worry about "bolder" and "lighter"
  1608. // because IE's currentStyle returns a numeric value for it,
  1609. // and other browsers use the computed value anyway
  1610. weight = {
  1611. normal: 400,
  1612. bold: 700
  1613. }[weight] || parseInt(weight, 10);
  1614. if (weights[weight]) return weights[weight];
  1615. // http://www.w3.org/TR/CSS21/fonts.html#propdef-font-weight
  1616. // Gecko uses x99/x01 for lighter/bolder
  1617. var up = {
  1618. 1: 1,
  1619. 99: 0
  1620. }[weight % 100], alts = [], min, max;
  1621. if (up === undefined) up = weight > 400;
  1622. if (weight == 500) weight = 400;
  1623. for (var alt in weights) {
  1624. alt = parseInt(alt, 10);
  1625. if (!min || alt < min) min = alt;
  1626. if (!max || alt > max) max = alt;
  1627. alts.push(alt);
  1628. }
  1629. if (weight < min) weight = min;
  1630. if (weight > max) weight = max;
  1631. alts.sort(function(a, b) {
  1632. return (up
  1633. ? (a > weight && b > weight) ? a < b : a > b
  1634. : (a < weight && b < weight) ? a > b : a < b) ? -1 : 1;
  1635. });
  1636. return weights[alts[0]];
  1637. };
  1638. }
  1639. function HoverHandler() {
  1640. function contains(node, anotherNode) {
  1641. if (node.contains) return node.contains(anotherNode);
  1642. return node.compareDocumentPosition(anotherNode) & 16;
  1643. }
  1644. function onOverOut(e) {
  1645. var related = e.relatedTarget;
  1646. if (!related || contains(this, related)) return;
  1647. trigger(this);
  1648. }
  1649. function onEnterLeave(e) {
  1650. trigger(this);
  1651. }
  1652. function trigger(el) {
  1653. // A timeout is needed so that the event can actually "happen"
  1654. // before replace is triggered. This ensures that styles are up
  1655. // to date.
  1656. setTimeout(function() {
  1657. api.replace(el, sharedStorage.get(el).options, true);
  1658. }, 10);
  1659. }
  1660. this.attach = function(el) {
  1661. if (el.onmouseenter === undefined) {
  1662. addEvent(el, 'mouseover', onOverOut);
  1663. addEvent(el, 'mouseout', onOverOut);
  1664. }
  1665. else {
  1666. addEvent(el, 'mouseenter', onEnterLeave);
  1667. addEvent(el, 'mouseleave', onEnterLeave);
  1668. }
  1669. };
  1670. }
  1671. function Storage() {
  1672. var map = {}, at = 0;
  1673. function identify(el) {
  1674. return el.cufid || (el.cufid = ++at);
  1675. }
  1676. /** @ignore */
  1677. this.get = function(el) {
  1678. var id = identify(el);
  1679. return map[id] || (map[id] = {});
  1680. };
  1681. }
  1682. function Style(style) {
  1683. var custom = {}, sizes = {};
  1684. this.get = function(property) {
  1685. return custom[property] != undefined ? custom[property] : style[property];
  1686. };
  1687. this.getSize = function(property, base) {
  1688. return sizes[property] || (sizes[property] = new CSS.Size(this.get(property), base));
  1689. };
  1690. this.extend = function(styles) {
  1691. for (var property in styles) custom[property] = styles[property];
  1692. return this;
  1693. };
  1694. }
  1695. function addEvent(el, type, listener) {
  1696. if (el.addEventListener) {
  1697. el.addEventListener(type, listener, false);
  1698. }
  1699. else if (el.attachEvent) {
  1700. el.attachEvent('on' + type, function() {
  1701. return listener.call(el, fabric.window.event);
  1702. });
  1703. }
  1704. }
  1705. function attach(el, options) {
  1706. var storage = sharedStorage.get(el);
  1707. if (storage.options) return el;
  1708. if (options.hover && options.hoverables[el.nodeName.toLowerCase()]) {
  1709. hoverHandler.attach(el);
  1710. }
  1711. storage.options = options;
  1712. return el;
  1713. }
  1714. function cached(fun) {
  1715. var cache = {};
  1716. return function(key) {
  1717. if (!cache.hasOwnProperty(key)) cache[key] = fun.apply(null, arguments);
  1718. return cache[key];
  1719. };
  1720. }
  1721. function getFont(el, style) {
  1722. if (!style) style = CSS.getStyle(el);
  1723. var families = CSS.quotedList(style.get('fontFamily').toLowerCase()), family;
  1724. for (var i = 0, l = families.length; i < l; ++i) {
  1725. family = families[i];
  1726. if (fonts[family]) return fonts[family].get(style.get('fontStyle'), style.get('fontWeight'));
  1727. }
  1728. return null;
  1729. }
  1730. function elementsByTagName(query) {
  1731. return fabric.document.getElementsByTagName(query);
  1732. }
  1733. function merge() {
  1734. var merged = {}, key;
  1735. for (var i = 0, l = arguments.length; i < l; ++i) {
  1736. for (key in arguments[i]) merged[key] = arguments[i][key];
  1737. }
  1738. return merged;
  1739. }
  1740. function process(font, text, style, options, node, el) {
  1741. var separate = options.separate;
  1742. if (separate == 'none') return engines[options.engine].apply(null, arguments);
  1743. var fragment = fabric.document.createDocumentFragment(), processed;
  1744. var parts = text.split(separators[separate]), needsAligning = (separate == 'words');
  1745. if (needsAligning && HAS_BROKEN_REGEXP) {
  1746. // @todo figure out a better way to do this
  1747. if (/^\s/.test(text)) parts.unshift('');
  1748. if (/\s$/.test(text)) parts.push('');
  1749. }
  1750. for (var i = 0, l = parts.length; i < l; ++i) {
  1751. processed = engines[options.engine](font,
  1752. needsAligning ? CSS.textAlign(parts[i], style, i, l) : parts[i],
  1753. style, options, node, el, i < l - 1);
  1754. if (processed) fragment.appendChild(processed);
  1755. }
  1756. return fragment;
  1757. }
  1758. /** @ignore */
  1759. function replaceElement(el, options) {
  1760. var font, style, nextNode, redraw;
  1761. for (var node = attach(el, options).firstChild; node; node = nextNode) {
  1762. nextNode = node.nextSibling;
  1763. redraw = false;
  1764. if (node.nodeType == 1) {
  1765. if (!node.firstChild) continue;
  1766. if (!/cufon/.test(node.className)) {
  1767. arguments.callee(node, options);
  1768. continue;
  1769. }
  1770. else redraw = true;
  1771. }
  1772. if (!style) style = CSS.getStyle(el).extend(options);
  1773. if (!font) font = getFont(el, style);
  1774. if (!font) continue;
  1775. if (redraw) {
  1776. engines[options.engine](font, null, style, options, node, el);
  1777. continue;
  1778. }
  1779. var text = node.data;
  1780. //for some reason, the carriage return is not stripped by IE but "\n" is, so let's keep \r as a new line marker...
  1781. if (typeof G_vmlCanvasManager != 'undefined') {
  1782. text = text.replace(/\r/g, "\n");
  1783. }
  1784. if (text === '') continue;
  1785. var processed = process(font, text, style, options, node, el);
  1786. if (processed) node.parentNode.replaceChild(processed, node);
  1787. else node.parentNode.removeChild(node);
  1788. }
  1789. }
  1790. var HAS_BROKEN_REGEXP = ' '.split(/\s+/).length == 0;
  1791. var sharedStorage = new Storage();
  1792. var hoverHandler = new HoverHandler();
  1793. var replaceHistory = [];
  1794. var engines = {}, fonts = {}, defaultOptions = {
  1795. engine: null,
  1796. //fontScale: 1,
  1797. //fontScaling: false,
  1798. hover: false,
  1799. hoverables: {
  1800. a: true
  1801. },
  1802. printable: true,
  1803. //rotation: 0,
  1804. //selectable: false,
  1805. selector: (
  1806. fabric.window.Sizzle
  1807. || (fabric.window.jQuery && function(query) { return jQuery(query); }) // avoid noConflict issues
  1808. || (fabric.window.dojo && dojo.query)
  1809. || (fabric.window.$$ && function(query) { return $$(query); })
  1810. || (fabric.window.$ && function(query) { return $(query); })
  1811. || (fabric.document.querySelectorAll && function(query) { return fabric.document.querySelectorAll(query); })
  1812. || elementsByTagName
  1813. ),
  1814. separate: 'words', // 'none' and 'characters' are also accepted
  1815. textShadow: 'none'
  1816. };
  1817. var separators = {
  1818. words: /\s+/,
  1819. characters: ''
  1820. };
  1821. /** @ignore */
  1822. api.now = function() {
  1823. DOM.ready();
  1824. return api;
  1825. };
  1826. /** @ignore */
  1827. api.refresh = function() {
  1828. var currentHistory = replaceHistory.splice(0, replaceHistory.length);
  1829. for (var i = 0, l = currentHistory.length; i < l; ++i) {
  1830. api.replace.apply(null, currentHistory[i]);
  1831. }
  1832. return api;
  1833. };
  1834. /** @ignore */
  1835. api.registerEngine = function(id, engine) {
  1836. if (!engine) return api;
  1837. engines[id] = engine;
  1838. return api.set('engine', id);
  1839. };
  1840. /** @ignore */
  1841. api.registerFont = function(data) {
  1842. var font = new Font(data), family = font.family;
  1843. if (!fonts[family]) fonts[family] = new FontFamily();
  1844. fonts[family].add(font);
  1845. return api.set('fontFamily', '"' + family + '"');
  1846. };
  1847. /** @ignore */
  1848. api.replace = function(elements, options, ignoreHistory) {
  1849. options = merge(defaultOptions, options);
  1850. if (!options.engine) return api; // there's no browser support so we'll just stop here
  1851. if (typeof options.textShadow == 'string' && options.textShadow)
  1852. options.textShadow = CSS.textShadow(options.textShadow);
  1853. if (!ignoreHistory) replaceHistory.push(arguments);
  1854. if (elements.nodeType || typeof elements == 'string') elements = [ elements ];
  1855. CSS.ready(function() {
  1856. for (var i = 0, l = elements.length; i < l; ++i) {
  1857. var el = elements[i];
  1858. if (typeof el == 'string') api.replace(options.selector(el), options, true);
  1859. else replaceElement(el, options);
  1860. }
  1861. });
  1862. return api;
  1863. };
  1864. /** @ignore */
  1865. api.replaceElement = function(el, options) {
  1866. options = merge(defaultOptions, options);
  1867. if (typeof options.textShadow == 'string' && options.textShadow)
  1868. options.textShadow = CSS.textShadow(options.textShadow);
  1869. return replaceElement(el, options);
  1870. };
  1871. api.engines = engines;
  1872. api.fonts = fonts;
  1873. /** @ignore */
  1874. api.getOptions = function() {
  1875. return merge(defaultOptions);
  1876. };
  1877. /** @ignore */
  1878. api.set = function(option, value) {
  1879. defaultOptions[option] = value;
  1880. return api;
  1881. };
  1882. return api;
  1883. })();
  1884. Cufon.registerEngine('canvas', (function() {
  1885. // Safari 2 doesn't support .apply() on native methods
  1886. var HAS_INLINE_BLOCK = Cufon.CSS.supports('display', 'inline-block');
  1887. // Firefox 2 w/ non-strict doctype (almost standards mode)
  1888. var HAS_BROKEN_LINEHEIGHT = !HAS_INLINE_BLOCK && (fabric.document.compatMode == 'BackCompat' || /frameset|transitional/i.test(fabric.document.doctype.publicId));
  1889. var styleSheet = fabric.document.createElement('style');
  1890. styleSheet.type = 'text/css';
  1891. var textNode = fabric.document.createTextNode(
  1892. '.cufon-canvas{text-indent:0}' +
  1893. '@media screen,projection{' +
  1894. '.cufon-canvas{display:inline;display:inline-block;position:relative;vertical-align:middle' +
  1895. (HAS_BROKEN_LINEHEIGHT
  1896. ? ''
  1897. : ';font-size:1px;line-height:1px') +
  1898. '}.cufon-canvas .cufon-alt{display:-moz-inline-box;display:inline-block;width:0;height:0;overflow:hidden}' +
  1899. (HAS_INLINE_BLOCK
  1900. ? '.cufon-canvas canvas{position:relative}'
  1901. : '.cufon-canvas canvas{position:absolute}') +
  1902. '}' +
  1903. '@media print{' +
  1904. '.cufon-canvas{padding:0 !important}' +
  1905. '.cufon-canvas canvas{display:none}' +
  1906. '.cufon-canvas .cufon-alt{display:inline}' +
  1907. '}'
  1908. )
  1909. try {
  1910. styleSheet.appendChild(textNode);
  1911. } catch(e) {
  1912. //IE8- can't do this...
  1913. styleSheet.setAttribute("type", "text/css");
  1914. styleSheet.styleSheet.cssText = textNode.data;
  1915. }
  1916. fabric.document.getElementsByTagName('head')[0].appendChild(styleSheet);
  1917. function generateFromVML(path, context) {
  1918. var atX = 0, atY = 0;
  1919. var code = [], re = /([mrvxe])([^a-z]*)/g, match;
  1920. generate: for (var i = 0; match = re.exec(path); ++i) {
  1921. var c = match[2].split(',');
  1922. switch (match[1]) {
  1923. case 'v':
  1924. code[i] = { m: 'bezierCurveTo', a: [ atX + ~~c[0], atY + ~~c[1], atX + ~~c[2], atY + ~~c[3], atX += ~~c[4], atY += ~~c[5] ] };
  1925. break;
  1926. case 'r':
  1927. code[i] = { m: 'lineTo', a: [ atX += ~~c[0], atY += ~~c[1] ] };
  1928. break;
  1929. case 'm':
  1930. code[i] = { m: 'moveTo', a: [ atX = ~~c[0], atY = ~~c[1] ] };
  1931. break;
  1932. case 'x':
  1933. code[i] = { m: 'closePath', a: [] };
  1934. break;
  1935. case 'e':
  1936. break generate;
  1937. }
  1938. context[code[i].m].apply(context, code[i].a);
  1939. }
  1940. return code;
  1941. }
  1942. function interpret(code, context) {
  1943. for (var i = 0, l = code.length; i < l; ++i) {
  1944. var line = code[i];
  1945. context[line.m].apply(context, line.a);
  1946. }
  1947. }
  1948. return function(font, text, style, options, node, el) {
  1949. var redraw = (text === null);
  1950. var viewBox = font.viewBox;
  1951. var size = style.getSize('fontSize', font.baseSize);
  1952. var letterSpacing = style.get('letterSpacing');
  1953. letterSpacing = (letterSpacing == 'normal') ? 0 : size.convertFrom(parseInt(letterSpacing, 10));
  1954. var expandTop = 0, expandRight = 0, expandBottom = 0, expandLeft = 0;
  1955. var shadows = options.textShadow, shadowOffsets = [];
  1956. Cufon.textOptions.shadowOffsets = [ ];
  1957. Cufon.textOptions.shadows = null;
  1958. if (shadows) {
  1959. Cufon.textOptions.shadows = shadows;
  1960. for (var i = 0, l = shadows.length; i < l; ++i) {
  1961. var shadow = shadows[i];
  1962. var x = size.convertFrom(parseFloat(shadow.offX));
  1963. var y = size.convertFrom(parseFloat(shadow.offY));
  1964. shadowOffsets[i] = [ x, y ];
  1965. //if (y < expandTop) expandTop = y;
  1966. //if (x > expandRight) expandRight = x;
  1967. //if (y > expandBottom) expandBottom = y;
  1968. //if (x < expandLeft) expandLeft = x;
  1969. }
  1970. }
  1971. var chars = Cufon.CSS.textTransform(redraw ? node.alt : text, style).split('');
  1972. var width = 0, lastWidth = null;
  1973. var maxWidth = 0, lines = 1, lineWidths = [ ];
  1974. for (var i = 0, l = chars.length; i < l; ++i) {
  1975. if (chars[i] === '\n') {
  1976. lines++;
  1977. if (width > maxWidth) {
  1978. maxWidth = width;
  1979. }
  1980. lineWidths.push(width);
  1981. width = 0;
  1982. continue;
  1983. }
  1984. var glyph = font.glyphs[chars[i]] || font.missingGlyph;
  1985. if (!glyph) continue;
  1986. width += lastWidth = Number(glyph.w || font.w) + letterSpacing;
  1987. }
  1988. lineWidths.push(width);
  1989. width = Math.max(maxWidth, width);
  1990. var lineOffsets = [ ];
  1991. for (var i = lineWidths.length; i--; ) {
  1992. lineOffsets[i] = width - lineWidths[i];
  1993. }
  1994. if (lastWidth === null) return null; // there's nothing to render
  1995. expandRight += (viewBox.width - lastWidth);
  1996. expandLeft += viewBox.minX;
  1997. var wrapper, canvas;
  1998. if (redraw) {
  1999. wrapper = node;
  2000. canvas = node.firstChild;
  2001. }
  2002. else {
  2003. wrapper = fabric.document.createElement('span');
  2004. wrapper.className = 'cufon cufon-canvas';
  2005. wrapper.alt = text;
  2006. canvas = fabric.document.createElement('canvas');
  2007. wrapper.appendChild(canvas);
  2008. if (options.printable) {
  2009. var print = fabric.document.createElement('span');
  2010. print.className = 'cufon-alt';
  2011. print.appendChild(fabric.document.createTextNode(text));
  2012. wrapper.appendChild(print);
  2013. }
  2014. }
  2015. var wStyle = wrapper.style;
  2016. var cStyle = canvas.style || { };
  2017. var height = size.convert(viewBox.height - expandTop + expandBottom);
  2018. var roundedHeight = Math.ceil(height);
  2019. var roundingFactor = roundedHeight / height;
  2020. canvas.width = Math.ceil(size.convert(width + expandRight - expandLeft) * roundingFactor);
  2021. canvas.height = roundedHeight;
  2022. expandTop += viewBox.minY;
  2023. cStyle.top = Math.round(size.convert(expandTop - font.ascent)) + 'px';
  2024. cStyle.left = Math.round(size.convert(expandLeft)) + 'px';
  2025. var _width = Math.ceil(size.convert(width * roundingFactor));
  2026. var wrapperWidth = _width + 'px';
  2027. var _height = size.convert(font.height);
  2028. var totalLineHeight = (options.lineHeight - 1) * size.convert(-font.ascent / 5) * (lines - 1);
  2029. Cufon.textOptions.width = _width;
  2030. Cufon.textOptions.height = (_height * lines) + totalLineHeight;
  2031. Cufon.textOptions.lines = lines;
  2032. Cufon.textOptions.totalLineHeight = totalLineHeight;
  2033. if (HAS_INLINE_BLOCK) {
  2034. wStyle.width = wrapperWidth;
  2035. wStyle.height = _height + 'px';
  2036. }
  2037. else {
  2038. wStyle.paddingLeft = wrapperWidth;
  2039. wStyle.paddingBottom = (_height - 1) + 'px';
  2040. }
  2041. var g = Cufon.textOptions.context || canvas.getContext('2d'),
  2042. scale = roundedHeight / viewBox.height;
  2043. Cufon.textOptions.fontAscent = font.ascent * scale;
  2044. Cufon.textOptions.boundaries = null;
  2045. for (var offsets = Cufon.textOptions.shadowOffsets, i = shadowOffsets.length; i--; ) {
  2046. offsets[i] = [ shadowOffsets[i][0] * scale, shadowOffsets[i][1] * scale ];
  2047. }
  2048. g.save();
  2049. g.scale(scale, scale);
  2050. g.translate(
  2051. // we're at the center of an object and need to jump to the top left corner
  2052. // where first character is to be drawn
  2053. -expandLeft - ((1/scale * canvas.width) / 2) + (Cufon.fonts[font.family].offsetLeft || 0),
  2054. -expandTop - ((Cufon.textOptions.height / scale) / 2) + (Cufon.fonts[font.family].offsetTop || 0)
  2055. );
  2056. g.lineWidth = font.face['underline-thickness'];
  2057. g.save();
  2058. function line(y, color) {
  2059. g.strokeStyle = color;
  2060. g.beginPath();
  2061. g.moveTo(0, y);
  2062. g.lineTo(width, y);
  2063. g.stroke();
  2064. }
  2065. var textDecoration = Cufon.getTextDecoration(options),
  2066. isItalic = options.fontStyle === 'italic';
  2067. function renderBackground() {
  2068. g.save();
  2069. var left = 0, lineNum = 0, boundaries = [{ left: 0 }];
  2070. if (options.backgroundColor) {
  2071. g.save();
  2072. g.fillStyle = options.backgroundColor;
  2073. g.translate(0, font.ascent);
  2074. g.fillRect(0, 0, width + 10, (-font.ascent + font.descent) * lines);
  2075. g.restore();
  2076. }
  2077. if (options.textAlign === 'right') {
  2078. g.translate(lineOffsets[lineNum], 0);
  2079. boundaries[0].left = lineOffsets[lineNum] * scale;
  2080. }
  2081. else if (options.textAlign === 'center') {
  2082. g.translate(lineOffsets[lineNum] / 2, 0);
  2083. boundaries[0].left = lineOffsets[lineNum] / 2 * scale;
  2084. }
  2085. for (var i = 0, l = chars.length; i < l; ++i) {
  2086. if (chars[i] === '\n') {
  2087. lineNum++;
  2088. var topOffset = -font.ascent - ((font.ascent / 5) * options.lineHeight);
  2089. var boundary = boundaries[boundaries.length - 1];
  2090. var nextBoundary = { left: 0 };
  2091. boundary.width = left * scale;
  2092. boundary.height = (-font.ascent + font.descent) * scale;
  2093. if (options.textAlign === 'right') {
  2094. g.translate(-width, topOffset);
  2095. g.translate(lineOffsets[lineNum], 0);
  2096. nextBoundary.left = lineOffsets[lineNum] * scale;
  2097. }
  2098. else if (options.textAlign === 'center') {
  2099. // offset to the start of text in previous line AND half of its offset
  2100. // (essentially moving caret to the left edge of bounding box)
  2101. g.translate(-left - (lineOffsets[lineNum - 1] / 2), topOffset);
  2102. g.translate(lineOffsets[lineNum] / 2, 0);
  2103. nextBoundary.left = lineOffsets[lineNum] / 2 * scale;
  2104. }
  2105. else {
  2106. g.translate(-left, topOffset);
  2107. }
  2108. /* push next boundary (for the next line) */
  2109. boundaries.push(nextBoundary);
  2110. left = 0;
  2111. continue;
  2112. }
  2113. var glyph = font.glyphs[chars[i]] || font.missingGlyph;
  2114. if (!glyph) continue;
  2115. var charWidth = Number(glyph.w || font.w) + letterSpacing;
  2116. // only draw text-background when there's some kind of value
  2117. if (options.textBackgroundColor) {
  2118. g.save();
  2119. g.fillStyle = options.textBackgroundColor;
  2120. g.translate(0, font.ascent);
  2121. g.fillRect(0, 0, charWidth + 10, -font.ascent + font.descent);
  2122. g.restore();
  2123. }
  2124. g.translate(charWidth, 0);
  2125. left += charWidth;
  2126. if (i == l-1) {
  2127. boundaries[boundaries.length - 1].width = left * scale;
  2128. boundaries[boundaries.length - 1].height = (-font.ascent + font.descent) * scale;
  2129. }
  2130. }
  2131. g.restore();
  2132. Cufon.textOptions.boundaries = boundaries;
  2133. }
  2134. function renderText(color) {
  2135. g.fillStyle = color || Cufon.textOptions.color || style.get('color');
  2136. var left = 0, lineNum = 0;
  2137. if (options.textAlign === 'right') {
  2138. g.translate(lineOffsets[lineNum], 0);
  2139. }
  2140. else if (options.textAlign === 'center') {
  2141. g.translate(lineOffsets[lineNum] / 2, 0);
  2142. }
  2143. for (var i = 0, l = chars.length; i < l; ++i) {
  2144. if (chars[i] === '\n') {
  2145. lineNum++;
  2146. var topOffset = -font.ascent - ((font.ascent / 5) * options.lineHeight);
  2147. if (options.textAlign === 'right') {
  2148. g.translate(-width, topOffset);
  2149. g.translate(lineOffsets[lineNum], 0);
  2150. }
  2151. else if (options.textAlign === 'center') {
  2152. // offset to the start of text in previous line AND half of its offset
  2153. // (essentially moving caret to the left edge of bounding box)
  2154. g.translate(-left - (lineOffsets[lineNum - 1] / 2), topOffset);
  2155. g.translate(lineOffsets[lineNum] / 2, 0);
  2156. }
  2157. else {
  2158. g.translate(-left, topOffset);
  2159. }
  2160. left = 0;
  2161. continue;
  2162. }
  2163. var glyph = font.glyphs[chars[i]] || font.missingGlyph;
  2164. if (!glyph) continue;
  2165. var charWidth = Number(glyph.w || font.w) + letterSpacing;
  2166. if (textDecoration) {
  2167. g.save();
  2168. g.strokeStyle = g.fillStyle;
  2169. // add 2x more thickness — closer to SVG rendering
  2170. g.lineWidth += g.lineWidth;
  2171. g.beginPath();
  2172. if (textDecoration.underline) {
  2173. g.moveTo(0, -font.face['underline-position'] + 0.5);
  2174. g.lineTo(charWidth, -font.face['underline-position'] + 0.5);
  2175. }
  2176. if (textDecoration.overline) {
  2177. g.moveTo(0, font.ascent + 0.5);
  2178. g.lineTo(charWidth, font.ascent + 0.5);
  2179. }
  2180. if (textDecoration['line-through']) {
  2181. g.moveTo(0, -font.descent + 0.5);
  2182. g.lineTo(charWidth, -font.descent + 0.5);
  2183. }
  2184. g.stroke();
  2185. g.restore();
  2186. }
  2187. if (isItalic) {
  2188. g.save();
  2189. g.transform(1, 0, -0.25, 1, 0, 0);
  2190. }
  2191. g.beginPath();
  2192. if (glyph.d) {
  2193. if (glyph.code) interpret(glyph.code, g);
  2194. else glyph.code = generateFromVML('m' + glyph.d, g);
  2195. }
  2196. g.fill();
  2197. if (options.strokeStyle) {
  2198. g.closePath();
  2199. g.save();
  2200. g.lineWidth = options.strokeWidth;
  2201. g.strokeStyle = options.strokeStyle;
  2202. g.stroke();
  2203. g.restore();
  2204. }
  2205. if (isItalic) {
  2206. g.restore();
  2207. }
  2208. g.translate(charWidth, 0);
  2209. left += charWidth;
  2210. }
  2211. }
  2212. g.save();
  2213. renderBackground();
  2214. if (shadows) {
  2215. for (var i = 0, l = shadows.length; i < l; ++i) {
  2216. var shadow = shadows[i];
  2217. g.save();
  2218. g.translate.apply(g, shadowOffsets[i]);
  2219. renderText(shadow.color);
  2220. g.restore();
  2221. }
  2222. }
  2223. renderText();
  2224. g.restore();
  2225. g.restore();
  2226. g.restore();
  2227. return wrapper;
  2228. };
  2229. })());
  2230. Cufon.registerEngine('vml', (function() {
  2231. if (!fabric.document.namespaces) return;
  2232. var canvasEl = fabric.document.createElement('canvas');
  2233. if (canvasEl && canvasEl.getContext && canvasEl.getContext.apply) return;
  2234. if (fabric.document.namespaces.cvml == null) {
  2235. fabric.document.namespaces.add('cvml', 'urn:schemas-microsoft-com:vml');
  2236. }
  2237. var check = fabric.document.createElement('cvml:shape');
  2238. check.style.behavior = 'url(#default#VML)';
  2239. if (!check.coordsize) return; // VML isn't supported
  2240. check = null;
  2241. fabric.document.write('<style type="text/css">' +
  2242. '.cufon-vml-canvas{text-indent:0}' +
  2243. '@media screen{' +
  2244. 'cvml\\:shape,cvml\\:shadow{behavior:url(#default#VML);display:block;antialias:true;position:absolute}' +
  2245. '.cufon-vml-canvas{position:absolute;text-align:left}' +
  2246. '.cufon-vml{display:inline-block;position:relative;vertical-align:middle}' +
  2247. '.cufon-vml .cufon-alt{position:absolute;left:-10000in;font-size:1px}' +
  2248. 'a .cufon-vml{cursor:pointer}' +
  2249. '}' +
  2250. '@media print{' +
  2251. '.cufon-vml *{display:none}' +
  2252. '.cufon-vml .cufon-alt{display:inline}' +
  2253. '}' +
  2254. '</style>');
  2255. function getFontSizeInPixels(el, value) {
  2256. return getSizeInPixels(el, /(?:em|ex|%)$/i.test(value) ? '1em' : value);
  2257. }
  2258. // Original by Dead Edwards.
  2259. // Combined with getFontSizeInPixels it also works with relative units.
  2260. function getSizeInPixels(el, value) {
  2261. if (/px$/i.test(value)) return parseFloat(value);
  2262. var style = el.style.left, runtimeStyle = el.runtimeStyle.left;
  2263. el.runtimeStyle.left = el.currentStyle.left;
  2264. el.style.left = value;
  2265. var result = el.style.pixelLeft;
  2266. el.style.left = style;
  2267. el.runtimeStyle.left = runtimeStyle;
  2268. return result;
  2269. }
  2270. return function(font, text, style, options, node, el, hasNext) {
  2271. var redraw = (text === null);
  2272. if (redraw) text = node.alt;
  2273. // @todo word-spacing, text-decoration
  2274. var viewBox = font.viewBox;
  2275. var size = style.computedFontSize ||
  2276. (style.computedFontSize = new Cufon.CSS.Size(getFontSizeInPixels(el, style.get('fontSize')) + 'px', font.baseSize));
  2277. var letterSpacing = style.computedLSpacing;
  2278. if (letterSpacing == undefined) {
  2279. letterSpacing = style.get('letterSpacing');
  2280. style.computedLSpacing = letterSpacing =
  2281. (letterSpacing == 'normal') ? 0 : ~~size.convertFrom(getSizeInPixels(el, letterSpacing));
  2282. }
  2283. var wrapper, canvas;
  2284. if (redraw) {
  2285. wrapper = node;
  2286. canvas = node.firstChild;
  2287. }
  2288. else {
  2289. wrapper = fabric.document.createElement('span');
  2290. wrapper.className = 'cufon cufon-vml';
  2291. wrapper.alt = text;
  2292. canvas = fabric.document.createElement('span');
  2293. canvas.className = 'cufon-vml-canvas';
  2294. wrapper.appendChild(canvas);
  2295. if (options.printable) {
  2296. var print = fabric.document.createElement('span');
  2297. print.className = 'cufon-alt';
  2298. print.appendChild(fabric.document.createTextNode(text));
  2299. wrapper.appendChild(print);
  2300. }
  2301. // ie6, for some reason, has trouble rendering the last VML element in the document.
  2302. // we can work around this by injecting a dummy element where needed.
  2303. // @todo find a better solution
  2304. if (!hasNext) wrapper.appendChild(fabric.document.createElement('cvml:shape'));
  2305. }
  2306. var wStyle = wrapper.style;
  2307. var cStyle = canvas.style;
  2308. var height = size.convert(viewBox.height), roundedHeight = Math.ceil(height);
  2309. var roundingFactor = roundedHeight / height;
  2310. var minX = viewBox.minX, minY = viewBox.minY;
  2311. cStyle.height = roundedHeight;
  2312. cStyle.top = Math.round(size.convert(minY - font.ascent));
  2313. cStyle.left = Math.round(size.convert(minX));
  2314. wStyle.height = size.convert(font.height) + 'px';
  2315. var textDecoration = Cufon.getTextDecoration(options);
  2316. var color = style.get('color');
  2317. var chars = Cufon.CSS.textTransform(text, style).split('');
  2318. var width = 0, offsetX = 0, advance = null;
  2319. var glyph, shape, shadows = options.textShadow;
  2320. // pre-calculate width
  2321. for (var i = 0, k = 0, l = chars.length; i < l; ++i) {
  2322. glyph = font.glyphs[chars[i]] || font.missingGlyph;
  2323. if (glyph) width += advance = ~~(glyph.w || font.w) + letterSpacing;
  2324. }
  2325. if (advance === null) return null;
  2326. var fullWidth = -minX + width + (viewBox.width - advance);
  2327. var shapeWidth = size.convert(fullWidth * roundingFactor), roundedShapeWidth = Math.round(shapeWidth);
  2328. var coordSize = fullWidth + ',' + viewBox.height, coordOrigin;
  2329. var stretch = 'r' + coordSize + 'nsnf';
  2330. for (i = 0; i < l; ++i) {
  2331. glyph = font.glyphs[chars[i]] || font.missingGlyph;
  2332. if (!glyph) continue;
  2333. if (redraw) {
  2334. // some glyphs may be missing so we can't use i
  2335. shape = canvas.childNodes[k];
  2336. if (shape.firstChild) shape.removeChild(shape.firstChild); // shadow
  2337. }
  2338. else {
  2339. shape = fabric.document.createElement('cvml:shape');
  2340. canvas.appendChild(shape);
  2341. }
  2342. shape.stroked = 'f';
  2343. shape.coordsize = coordSize;
  2344. shape.coordorigin = coordOrigin = (minX - offsetX) + ',' + minY;
  2345. shape.path = (glyph.d ? 'm' + glyph.d + 'xe' : '') + 'm' + coordOrigin + stretch;
  2346. shape.fillcolor = color;
  2347. // it's important to not set top/left or IE8 will grind to a halt
  2348. var sStyle = shape.style;
  2349. sStyle.width = roundedShapeWidth;
  2350. sStyle.height = roundedHeight;
  2351. if (shadows) {
  2352. // due to the limitations of the VML shadow element there
  2353. // can only be two visible shadows. opacity is shared
  2354. // for all shadows.
  2355. var shadow1 = shadows[0], shadow2 = shadows[1];
  2356. var color1 = Cufon.CSS.color(shadow1.color), color2;
  2357. var shadow = fabric.document.createElement('cvml:shadow');
  2358. shadow.on = 't';
  2359. shadow.color = color1.color;
  2360. shadow.offset = shadow1.offX + ',' + shadow1.offY;
  2361. if (shadow2) {
  2362. color2 = Cufon.CSS.color(shadow2.color);
  2363. shadow.type = 'double';
  2364. shadow.color2 = color2.color;
  2365. shadow.offset2 = shadow2.offX + ',' + shadow2.offY;
  2366. }
  2367. shadow.opacity = color1.opacity || (color2 && color2.opacity) || 1;
  2368. shape.appendChild(shadow);
  2369. }
  2370. offsetX += ~~(glyph.w || font.w) + letterSpacing;
  2371. ++k;
  2372. }
  2373. wStyle.width = Math.max(Math.ceil(size.convert(width * roundingFactor)), 0);
  2374. return wrapper;
  2375. };
  2376. })());
  2377. Cufon.getTextDecoration = function(options) {
  2378. return {
  2379. underline: options.textDecoration === 'underline',
  2380. overline: options.textDecoration === 'overline',
  2381. 'line-through': options.textDecoration === 'line-through'
  2382. };
  2383. };
  2384. if (typeof exports != 'undefined') {
  2385. exports.Cufon = Cufon;
  2386. }
  2387. /*
  2388. json2.js
  2389. 2014-02-04
  2390. Public Domain.
  2391. NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
  2392. See http://www.JSON.org/js.html
  2393. This code should be minified before deployment.
  2394. See http://javascript.crockford.com/jsmin.html
  2395. USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
  2396. NOT CONTROL.
  2397. This file creates a global JSON object containing two methods: stringify
  2398. and parse.
  2399. JSON.stringify(value, replacer, space)
  2400. value any JavaScript value, usually an object or array.
  2401. replacer an optional parameter that determines how object
  2402. values are stringified for objects. It can be a
  2403. function or an array of strings.
  2404. space an optional parameter that specifies the indentation
  2405. of nested structures. If it is omitted, the text will
  2406. be packed without extra whitespace. If it is a number,
  2407. it will specify the number of spaces to indent at each
  2408. level. If it is a string (such as '\t' or '&nbsp;'),
  2409. it contains the characters used to indent at each level.
  2410. This method produces a JSON text from a JavaScript value.
  2411. When an object value is found, if the object contains a toJSON
  2412. method, its toJSON method will be called and the result will be
  2413. stringified. A toJSON method does not serialize: it returns the
  2414. value represented by the name/value pair that should be serialized,
  2415. or undefined if nothing should be serialized. The toJSON method
  2416. will be passed the key associated with the value, and this will be
  2417. bound to the value
  2418. For example, this would serialize Dates as ISO strings.
  2419. Date.prototype.toJSON = function (key) {
  2420. function f(n) {
  2421. // Format integers to have at least two digits.
  2422. return n < 10 ? '0' + n : n;
  2423. }
  2424. return this.getUTCFullYear() + '-' +
  2425. f(this.getUTCMonth() + 1) + '-' +
  2426. f(this.getUTCDate()) + 'T' +
  2427. f(this.getUTCHours()) + ':' +
  2428. f(this.getUTCMinutes()) + ':' +
  2429. f(this.getUTCSeconds()) + 'Z';
  2430. };
  2431. You can provide an optional replacer method. It will be passed the
  2432. key and value of each member, with this bound to the containing
  2433. object. The value that is returned from your method will be
  2434. serialized. If your method returns undefined, then the member will
  2435. be excluded from the serialization.
  2436. If the replacer parameter is an array of strings, then it will be
  2437. used to select the members to be serialized. It filters the results
  2438. such that only members with keys listed in the replacer array are
  2439. stringified.
  2440. Values that do not have JSON representations, such as undefined or
  2441. functions, will not be serialized. Such values in objects will be
  2442. dropped; in arrays they will be replaced with null. You can use
  2443. a replacer function to replace those with JSON values.
  2444. JSON.stringify(undefined) returns undefined.
  2445. The optional space parameter produces a stringification of the
  2446. value that is filled with line breaks and indentation to make it
  2447. easier to read.
  2448. If the space parameter is a non-empty string, then that string will
  2449. be used for indentation. If the space parameter is a number, then
  2450. the indentation will be that many spaces.
  2451. Example:
  2452. text = JSON.stringify(['e', {pluribus: 'unum'}]);
  2453. // text is '["e",{"pluribus":"unum"}]'
  2454. text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
  2455. // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
  2456. text = JSON.stringify([new Date()], function (key, value) {
  2457. return this[key] instanceof Date ?
  2458. 'Date(' + this[key] + ')' : value;
  2459. });
  2460. // text is '["Date(---current time---)"]'
  2461. JSON.parse(text, reviver)
  2462. This method parses a JSON text to produce an object or array.
  2463. It can throw a SyntaxError exception.
  2464. The optional reviver parameter is a function that can filter and
  2465. transform the results. It receives each of the keys and values,
  2466. and its return value is used instead of the original value.
  2467. If it returns what it received, then the structure is not modified.
  2468. If it returns undefined then the member is deleted.
  2469. Example:
  2470. // Parse the text. Values that look like ISO date strings will
  2471. // be converted to Date objects.
  2472. myData = JSON.parse(text, function (key, value) {
  2473. var a;
  2474. if (typeof value === 'string') {
  2475. a =
  2476. /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
  2477. if (a) {
  2478. return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
  2479. +a[5], +a[6]));
  2480. }
  2481. }
  2482. return value;
  2483. });
  2484. myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
  2485. var d;
  2486. if (typeof value === 'string' &&
  2487. value.slice(0, 5) === 'Date(' &&
  2488. value.slice(-1) === ')') {
  2489. d = new Date(value.slice(5, -1));
  2490. if (d) {
  2491. return d;
  2492. }
  2493. }
  2494. return value;
  2495. });
  2496. This is a reference implementation. You are free to copy, modify, or
  2497. redistribute.
  2498. */
  2499. /*jslint evil: true, regexp: true */
  2500. /*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
  2501. call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
  2502. getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
  2503. lastIndex, length, parse, prototype, push, replace, slice, stringify,
  2504. test, toJSON, toString, valueOf
  2505. */
  2506. // Create a JSON object only if one does not already exist. We create the
  2507. // methods in a closure to avoid creating global variables.
  2508. if (typeof JSON !== 'object') {
  2509. JSON = {};
  2510. }
  2511. (function () {
  2512. 'use strict';
  2513. function f(n) {
  2514. // Format integers to have at least two digits.
  2515. return n < 10 ? '0' + n : n;
  2516. }
  2517. if (typeof Date.prototype.toJSON !== 'function') {
  2518. Date.prototype.toJSON = function () {
  2519. return isFinite(this.valueOf())
  2520. ? this.getUTCFullYear() + '-' +
  2521. f(this.getUTCMonth() + 1) + '-' +
  2522. f(this.getUTCDate()) + 'T' +
  2523. f(this.getUTCHours()) + ':' +
  2524. f(this.getUTCMinutes()) + ':' +
  2525. f(this.getUTCSeconds()) + 'Z'
  2526. : null;
  2527. };
  2528. String.prototype.toJSON =
  2529. Number.prototype.toJSON =
  2530. Boolean.prototype.toJSON = function () {
  2531. return this.valueOf();
  2532. };
  2533. }
  2534. var cx,
  2535. escapable,
  2536. gap,
  2537. indent,
  2538. meta,
  2539. rep;
  2540. function quote(string) {
  2541. // If the string contains no control characters, no quote characters, and no
  2542. // backslash characters, then we can safely slap some quotes around it.
  2543. // Otherwise we must also replace the offending characters with safe escape
  2544. // sequences.
  2545. escapable.lastIndex = 0;
  2546. return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
  2547. var c = meta[a];
  2548. return typeof c === 'string'
  2549. ? c
  2550. : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
  2551. }) + '"' : '"' + string + '"';
  2552. }
  2553. function str(key, holder) {
  2554. // Produce a string from holder[key].
  2555. var i, // The loop counter.
  2556. k, // The member key.
  2557. v, // The member value.
  2558. length,
  2559. mind = gap,
  2560. partial,
  2561. value = holder[key];
  2562. // If the value has a toJSON method, call it to obtain a replacement value.
  2563. if (value && typeof value === 'object' &&
  2564. typeof value.toJSON === 'function') {
  2565. value = value.toJSON(key);
  2566. }
  2567. // If we were called with a replacer function, then call the replacer to
  2568. // obtain a replacement value.
  2569. if (typeof rep === 'function') {
  2570. value = rep.call(holder, key, value);
  2571. }
  2572. // What happens next depends on the value's type.
  2573. switch (typeof value) {
  2574. case 'string':
  2575. return quote(value);
  2576. case 'number':
  2577. // JSON numbers must be finite. Encode non-finite numbers as null.
  2578. return isFinite(value) ? String(value) : 'null';
  2579. case 'boolean':
  2580. case 'null':
  2581. // If the value is a boolean or null, convert it to a string. Note:
  2582. // typeof null does not produce 'null'. The case is included here in
  2583. // the remote chance that this gets fixed someday.
  2584. return String(value);
  2585. // If the type is 'object', we might be dealing with an object or an array or
  2586. // null.
  2587. case 'object':
  2588. // Due to a specification blunder in ECMAScript, typeof null is 'object',
  2589. // so watch out for that case.
  2590. if (!value) {
  2591. return 'null';
  2592. }
  2593. // Make an array to hold the partial results of stringifying this object value.
  2594. gap += indent;
  2595. partial = [];
  2596. // Is the value an array?
  2597. if (Object.prototype.toString.apply(value) === '[object Array]') {
  2598. // The value is an array. Stringify every element. Use null as a placeholder
  2599. // for non-JSON values.
  2600. length = value.length;
  2601. for (i = 0; i < length; i += 1) {
  2602. partial[i] = str(i, value) || 'null';
  2603. }
  2604. // Join all of the elements together, separated with commas, and wrap them in
  2605. // brackets.
  2606. v = partial.length === 0
  2607. ? '[]'
  2608. : gap
  2609. ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']'
  2610. : '[' + partial.join(',') + ']';
  2611. gap = mind;
  2612. return v;
  2613. }
  2614. // If the replacer is an array, use it to select the members to be stringified.
  2615. if (rep && typeof rep === 'object') {
  2616. length = rep.length;
  2617. for (i = 0; i < length; i += 1) {
  2618. if (typeof rep[i] === 'string') {
  2619. k = rep[i];
  2620. v = str(k, value);
  2621. if (v) {
  2622. partial.push(quote(k) + (gap ? ': ' : ':') + v);
  2623. }
  2624. }
  2625. }
  2626. } else {
  2627. // Otherwise, iterate through all of the keys in the object.
  2628. for (k in value) {
  2629. if (Object.prototype.hasOwnProperty.call(value, k)) {
  2630. v = str(k, value);
  2631. if (v) {
  2632. partial.push(quote(k) + (gap ? ': ' : ':') + v);
  2633. }
  2634. }
  2635. }
  2636. }
  2637. // Join all of the member texts together, separated with commas,
  2638. // and wrap them in braces.
  2639. v = partial.length === 0
  2640. ? '{}'
  2641. : gap
  2642. ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}'
  2643. : '{' + partial.join(',') + '}';
  2644. gap = mind;
  2645. return v;
  2646. }
  2647. }
  2648. // If the JSON object does not yet have a stringify method, give it one.
  2649. if (typeof JSON.stringify !== 'function') {
  2650. escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
  2651. meta = { // table of character substitutions
  2652. '\b': '\\b',
  2653. '\t': '\\t',
  2654. '\n': '\\n',
  2655. '\f': '\\f',
  2656. '\r': '\\r',
  2657. '"' : '\\"',
  2658. '\\': '\\\\'
  2659. };
  2660. JSON.stringify = function (value, replacer, space) {
  2661. // The stringify method takes a value and an optional replacer, and an optional
  2662. // space parameter, and returns a JSON text. The replacer can be a function
  2663. // that can replace values, or an array of strings that will select the keys.
  2664. // A default replacer method can be provided. Use of the space parameter can
  2665. // produce text that is more easily readable.
  2666. var i;
  2667. gap = '';
  2668. indent = '';
  2669. // If the space parameter is a number, make an indent string containing that
  2670. // many spaces.
  2671. if (typeof space === 'number') {
  2672. for (i = 0; i < space; i += 1) {
  2673. indent += ' ';
  2674. }
  2675. // If the space parameter is a string, it will be used as the indent string.
  2676. } else if (typeof space === 'string') {
  2677. indent = space;
  2678. }
  2679. // If there is a replacer, it must be a function or an array.
  2680. // Otherwise, throw an error.
  2681. rep = replacer;
  2682. if (replacer && typeof replacer !== 'function' &&
  2683. (typeof replacer !== 'object' ||
  2684. typeof replacer.length !== 'number')) {
  2685. throw new Error('JSON.stringify');
  2686. }
  2687. // Make a fake root object containing our value under the key of ''.
  2688. // Return the result of stringifying the value.
  2689. return str('', {'': value});
  2690. };
  2691. }
  2692. // If the JSON object does not yet have a parse method, give it one.
  2693. if (typeof JSON.parse !== 'function') {
  2694. cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
  2695. JSON.parse = function (text, reviver) {
  2696. // The parse method takes a text and an optional reviver function, and returns
  2697. // a JavaScript value if the text is a valid JSON text.
  2698. var j;
  2699. function walk(holder, key) {
  2700. // The walk method is used to recursively walk the resulting structure so
  2701. // that modifications can be made.
  2702. var k, v, value = holder[key];
  2703. if (value && typeof value === 'object') {
  2704. for (k in value) {
  2705. if (Object.prototype.hasOwnProperty.call(value, k)) {
  2706. v = walk(value, k);
  2707. if (v !== undefined) {
  2708. value[k] = v;
  2709. } else {
  2710. delete value[k];
  2711. }
  2712. }
  2713. }
  2714. }
  2715. return reviver.call(holder, key, value);
  2716. }
  2717. // Parsing happens in four stages. In the first stage, we replace certain
  2718. // Unicode characters with escape sequences. JavaScript handles many characters
  2719. // incorrectly, either silently deleting them, or treating them as line endings.
  2720. text = String(text);
  2721. cx.lastIndex = 0;
  2722. if (cx.test(text)) {
  2723. text = text.replace(cx, function (a) {
  2724. return '\\u' +
  2725. ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
  2726. });
  2727. }
  2728. // In the second stage, we run the text against regular expressions that look
  2729. // for non-JSON patterns. We are especially concerned with '()' and 'new'
  2730. // because they can cause invocation, and '=' because it can cause mutation.
  2731. // But just to be safe, we want to reject all unexpected forms.
  2732. // We split the second stage into 4 regexp operations in order to work around
  2733. // crippling inefficiencies in IE's and Safari's regexp engines. First we
  2734. // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
  2735. // replace all simple value tokens with ']' characters. Third, we delete all
  2736. // open brackets that follow a colon or comma or that begin the text. Finally,
  2737. // we look to see that the remaining characters are only whitespace or ']' or
  2738. // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
  2739. if (/^[\],:{}\s]*$/
  2740. .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
  2741. .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
  2742. .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
  2743. // In the third stage we use the eval function to compile the text into a
  2744. // JavaScript structure. The '{' operator is subject to a syntactic ambiguity
  2745. // in JavaScript: it can begin a block or an object literal. We wrap the text
  2746. // in parens to eliminate the ambiguity.
  2747. j = eval('(' + text + ')');
  2748. // In the optional fourth stage, we recursively walk the new structure, passing
  2749. // each name/value pair to a reviver function for possible transformation.
  2750. return typeof reviver === 'function'
  2751. ? walk({'': j}, '')
  2752. : j;
  2753. }
  2754. // If the text is not JSON parseable, then a SyntaxError is thrown.
  2755. throw new SyntaxError('JSON.parse');
  2756. };
  2757. }
  2758. }());
  2759. (function(){
  2760. /**
  2761. * @private
  2762. * @param {String} eventName
  2763. * @param {Function} handler
  2764. */
  2765. function _removeEventListener(eventName, handler) {
  2766. if (!this.__eventListeners[eventName]) {
  2767. return;
  2768. }
  2769. if (handler) {
  2770. fabric.util.removeFromArray(this.__eventListeners[eventName], handler);
  2771. }
  2772. else {
  2773. this.__eventListeners[eventName].length = 0;
  2774. }
  2775. }
  2776. /**
  2777. * Observes specified event
  2778. * @deprecated `observe` deprecated since 0.8.34 (use `on` instead)
  2779. * @memberOf fabric.Observable
  2780. * @alias on
  2781. * @param {String|Object} eventName Event name (eg. 'after:render') or object with key/value pairs (eg. {'after:render': handler, 'selection:cleared': handler})
  2782. * @param {Function} handler Function that receives a notification when an event of the specified type occurs
  2783. * @return {Self} thisArg
  2784. * @chainable
  2785. */
  2786. function observe(eventName, handler) {
  2787. if (!this.__eventListeners) {
  2788. this.__eventListeners = { };
  2789. }
  2790. // one object with key/value pairs was passed
  2791. if (arguments.length === 1) {
  2792. for (var prop in eventName) {
  2793. this.on(prop, eventName[prop]);
  2794. }
  2795. }
  2796. else {
  2797. if (!this.__eventListeners[eventName]) {
  2798. this.__eventListeners[eventName] = [ ];
  2799. }
  2800. this.__eventListeners[eventName].push(handler);
  2801. }
  2802. return this;
  2803. }
  2804. /**
  2805. * Stops event observing for a particular event handler. Calling this method
  2806. * without arguments removes all handlers for all events
  2807. * @deprecated `stopObserving` deprecated since 0.8.34 (use `off` instead)
  2808. * @memberOf fabric.Observable
  2809. * @alias off
  2810. * @param {String|Object} eventName Event name (eg. 'after:render') or object with key/value pairs (eg. {'after:render': handler, 'selection:cleared': handler})
  2811. * @param {Function} handler Function to be deleted from EventListeners
  2812. * @return {Self} thisArg
  2813. * @chainable
  2814. */
  2815. function stopObserving(eventName, handler) {
  2816. if (!this.__eventListeners) {
  2817. return;
  2818. }
  2819. // remove all key/value pairs (event name -> event handler)
  2820. if (arguments.length === 0) {
  2821. this.__eventListeners = { };
  2822. }
  2823. // one object with key/value pairs was passed
  2824. else if (arguments.length === 1 && typeof arguments[0] === 'object') {
  2825. for (var prop in eventName) {
  2826. _removeEventListener.call(this, prop, eventName[prop]);
  2827. }
  2828. }
  2829. else {
  2830. _removeEventListener.call(this, eventName, handler);
  2831. }
  2832. return this;
  2833. }
  2834. /**
  2835. * Fires event with an optional options object
  2836. * @deprecated `fire` deprecated since 1.0.7 (use `trigger` instead)
  2837. * @memberOf fabric.Observable
  2838. * @alias trigger
  2839. * @param {String} eventName Event name to fire
  2840. * @param {Object} [options] Options object
  2841. * @return {Self} thisArg
  2842. * @chainable
  2843. */
  2844. function fire(eventName, options) {
  2845. if (!this.__eventListeners) {
  2846. return;
  2847. }
  2848. var listenersForEvent = this.__eventListeners[eventName];
  2849. if (!listenersForEvent) {
  2850. return;
  2851. }
  2852. for (var i = 0, len = listenersForEvent.length; i < len; i++) {
  2853. // avoiding try/catch for perf. reasons
  2854. listenersForEvent[i].call(this, options || { });
  2855. }
  2856. return this;
  2857. }
  2858. /**
  2859. * @namespace fabric.Observable
  2860. * @tutorial {@link http://fabricjs.com/fabric-intro-part-2/#events}
  2861. * @see {@link http://fabricjs.com/events/|Events demo}
  2862. */
  2863. fabric.Observable = {
  2864. observe: observe,
  2865. stopObserving: stopObserving,
  2866. fire: fire,
  2867. on: observe,
  2868. off: stopObserving,
  2869. trigger: fire
  2870. };
  2871. })();
  2872. /**
  2873. * @namespace fabric.Collection
  2874. */
  2875. fabric.Collection = {
  2876. /**
  2877. * Adds objects to collection, then renders canvas (if `renderOnAddRemove` is not `false`)
  2878. * Objects should be instances of (or inherit from) fabric.Object
  2879. * @param {...fabric.Object} object Zero or more fabric instances
  2880. * @return {Self} thisArg
  2881. */
  2882. add: function () {
  2883. this._objects.push.apply(this._objects, arguments);
  2884. for (var i = 0, length = arguments.length; i < length; i++) {
  2885. this._onObjectAdded(arguments[i]);
  2886. }
  2887. this.renderOnAddRemove && this.renderAll();
  2888. return this;
  2889. },
  2890. /**
  2891. * Inserts an object into collection at specified index, then renders canvas (if `renderOnAddRemove` is not `false`)
  2892. * An object should be an instance of (or inherit from) fabric.Object
  2893. * @param {Object} object Object to insert
  2894. * @param {Number} index Index to insert object at
  2895. * @param {Boolean} nonSplicing When `true`, no splicing (shifting) of objects occurs
  2896. * @return {Self} thisArg
  2897. * @chainable
  2898. */
  2899. insertAt: function (object, index, nonSplicing) {
  2900. var objects = this.getObjects();
  2901. if (nonSplicing) {
  2902. objects[index] = object;
  2903. }
  2904. else {
  2905. objects.splice(index, 0, object);
  2906. }
  2907. this._onObjectAdded(object);
  2908. this.renderOnAddRemove && this.renderAll();
  2909. return this;
  2910. },
  2911. /**
  2912. * Removes objects from a collection, then renders canvas (if `renderOnAddRemove` is not `false`)
  2913. * @param {...fabric.Object} object Zero or more fabric instances
  2914. * @return {Self} thisArg
  2915. * @chainable
  2916. */
  2917. remove: function() {
  2918. var objects = this.getObjects(),
  2919. index;
  2920. for (var i = 0, length = arguments.length; i < length; i++) {
  2921. index = objects.indexOf(arguments[i]);
  2922. // only call onObjectRemoved if an object was actually removed
  2923. if (index !== -1) {
  2924. objects.splice(index, 1);
  2925. this._onObjectRemoved(arguments[i]);
  2926. }
  2927. }
  2928. this.renderOnAddRemove && this.renderAll();
  2929. return this;
  2930. },
  2931. /**
  2932. * Executes given function for each object in this group
  2933. * @param {Function} callback
  2934. * Callback invoked with current object as first argument,
  2935. * index - as second and an array of all objects - as third.
  2936. * Iteration happens in reverse order (for performance reasons).
  2937. * Callback is invoked in a context of Global Object (e.g. `window`)
  2938. * when no `context` argument is given
  2939. *
  2940. * @param {Object} context Context (aka thisObject)
  2941. * @return {Self} thisArg
  2942. */
  2943. forEachObject: function(callback, context) {
  2944. var objects = this.getObjects(),
  2945. i = objects.length;
  2946. while (i--) {
  2947. callback.call(context, objects[i], i, objects);
  2948. }
  2949. return this;
  2950. },
  2951. /**
  2952. * Returns an array of children objects of this instance
  2953. * Type parameter introduced in 1.3.10
  2954. * @param {String} [type] When specified, only objects of this type are returned
  2955. * @return {Array}
  2956. */
  2957. getObjects: function(type) {
  2958. if (typeof type === 'undefined') {
  2959. return this._objects;
  2960. }
  2961. return this._objects.filter(function(o) {
  2962. return o.type === type;
  2963. });
  2964. },
  2965. /**
  2966. * Returns object at specified index
  2967. * @param {Number} index
  2968. * @return {Self} thisArg
  2969. */
  2970. item: function (index) {
  2971. return this.getObjects()[index];
  2972. },
  2973. /**
  2974. * Returns true if collection contains no objects
  2975. * @return {Boolean} true if collection is empty
  2976. */
  2977. isEmpty: function () {
  2978. return this.getObjects().length === 0;
  2979. },
  2980. /**
  2981. * Returns a size of a collection (i.e: length of an array containing its objects)
  2982. * @return {Number} Collection size
  2983. */
  2984. size: function() {
  2985. return this.getObjects().length;
  2986. },
  2987. /**
  2988. * Returns true if collection contains an object
  2989. * @param {Object} object Object to check against
  2990. * @return {Boolean} `true` if collection contains an object
  2991. */
  2992. contains: function(object) {
  2993. return this.getObjects().indexOf(object) > -1;
  2994. },
  2995. /**
  2996. * Returns number representation of a collection complexity
  2997. * @return {Number} complexity
  2998. */
  2999. complexity: function () {
  3000. return this.getObjects().reduce(function (memo, current) {
  3001. memo += current.complexity ? current.complexity() : 0;
  3002. return memo;
  3003. }, 0);
  3004. }
  3005. };
  3006. (function(global) {
  3007. var sqrt = Math.sqrt,
  3008. atan2 = Math.atan2,
  3009. PiBy180 = Math.PI / 180;
  3010. /**
  3011. * @namespace fabric.util
  3012. */
  3013. fabric.util = {
  3014. /**
  3015. * Removes value from an array.
  3016. * Presence of value (and its position in an array) is determined via `Array.prototype.indexOf`
  3017. * @static
  3018. * @memberOf fabric.util
  3019. * @param {Array} array
  3020. * @param {Any} value
  3021. * @return {Array} original array
  3022. */
  3023. removeFromArray: function(array, value) {
  3024. var idx = array.indexOf(value);
  3025. if (idx !== -1) {
  3026. array.splice(idx, 1);
  3027. }
  3028. return array;
  3029. },
  3030. /**
  3031. * Returns random number between 2 specified ones.
  3032. * @static
  3033. * @memberOf fabric.util
  3034. * @param {Number} min lower limit
  3035. * @param {Number} max upper limit
  3036. * @return {Number} random value (between min and max)
  3037. */
  3038. getRandomInt: function(min, max) {
  3039. return Math.floor(Math.random() * (max - min + 1)) + min;
  3040. },
  3041. /**
  3042. * Transforms degrees to radians.
  3043. * @static
  3044. * @memberOf fabric.util
  3045. * @param {Number} degrees value in degrees
  3046. * @return {Number} value in radians
  3047. */
  3048. degreesToRadians: function(degrees) {
  3049. return degrees * PiBy180;
  3050. },
  3051. /**
  3052. * Transforms radians to degrees.
  3053. * @static
  3054. * @memberOf fabric.util
  3055. * @param {Number} radians value in radians
  3056. * @return {Number} value in degrees
  3057. */
  3058. radiansToDegrees: function(radians) {
  3059. return radians / PiBy180;
  3060. },
  3061. /**
  3062. * Rotates `point` around `origin` with `radians`
  3063. * @static
  3064. * @memberOf fabric.util
  3065. * @param {fabric.Point} point The point to rotate
  3066. * @param {fabric.Point} origin The origin of the rotation
  3067. * @param {Number} radians The radians of the angle for the rotation
  3068. * @return {fabric.Point} The new rotated point
  3069. */
  3070. rotatePoint: function(point, origin, radians) {
  3071. var sin = Math.sin(radians),
  3072. cos = Math.cos(radians);
  3073. point.subtractEquals(origin);
  3074. var rx = point.x * cos - point.y * sin,
  3075. ry = point.x * sin + point.y * cos;
  3076. return new fabric.Point(rx, ry).addEquals(origin);
  3077. },
  3078. /**
  3079. * Apply transform t to point p
  3080. * @static
  3081. * @memberOf fabric.util
  3082. * @param {fabric.Point} p The point to transform
  3083. * @param {Array} t The transform
  3084. * @param {Boolean} [ignoreOffset] Indicates that the offset should not be applied
  3085. * @return {fabric.Point} The transformed point
  3086. */
  3087. transformPoint: function(p, t, ignoreOffset) {
  3088. if (ignoreOffset) {
  3089. return new fabric.Point(
  3090. t[0] * p.x + t[1] * p.y,
  3091. t[2] * p.x + t[3] * p.y
  3092. );
  3093. }
  3094. return new fabric.Point(
  3095. t[0] * p.x + t[1] * p.y + t[4],
  3096. t[2] * p.x + t[3] * p.y + t[5]
  3097. );
  3098. },
  3099. /**
  3100. * Invert transformation t
  3101. * @static
  3102. * @memberOf fabric.util
  3103. * @param {Array} t The transform
  3104. * @return {Array} The inverted transform
  3105. */
  3106. invertTransform: function(t) {
  3107. var r = t.slice(),
  3108. a = 1 / (t[0] * t[3] - t[1] * t[2]);
  3109. r = [a * t[3], -a * t[1], -a * t[2], a * t[0], 0, 0];
  3110. var o = fabric.util.transformPoint({ x: t[4], y: t[5] }, r);
  3111. r[4] = -o.x;
  3112. r[5] = -o.y;
  3113. return r;
  3114. },
  3115. /**
  3116. * A wrapper around Number#toFixed, which contrary to native method returns number, not string.
  3117. * @static
  3118. * @memberOf fabric.util
  3119. * @param {Number|String} number number to operate on
  3120. * @param {Number} fractionDigits number of fraction digits to "leave"
  3121. * @return {Number}
  3122. */
  3123. toFixed: function(number, fractionDigits) {
  3124. return parseFloat(Number(number).toFixed(fractionDigits));
  3125. },
  3126. /**
  3127. * Converts from attribute value to pixel value if applicable.
  3128. * Returns converted pixels or original value not converted.
  3129. * @param {Number|String} value number to operate on
  3130. * @return {Number|String}
  3131. */
  3132. parseUnit: function(value) {
  3133. var unit = /\D{0,2}$/.exec(value),
  3134. number = parseFloat(value);
  3135. switch (unit[0]) {
  3136. case 'mm':
  3137. return number * fabric.DPI / 25.4;
  3138. case 'cm':
  3139. return number * fabric.DPI / 2.54;
  3140. case 'in':
  3141. return number * fabric.DPI;
  3142. case 'pt':
  3143. return number * fabric.DPI / 72; // or * 4 / 3
  3144. case 'pc':
  3145. return number * fabric.DPI / 72 * 12; // or * 16
  3146. default:
  3147. return number;
  3148. }
  3149. },
  3150. /**
  3151. * Function which always returns `false`.
  3152. * @static
  3153. * @memberOf fabric.util
  3154. * @return {Boolean}
  3155. */
  3156. falseFunction: function() {
  3157. return false;
  3158. },
  3159. /**
  3160. * Returns klass "Class" object of given namespace
  3161. * @memberOf fabric.util
  3162. * @param {String} type Type of object (eg. 'circle')
  3163. * @param {String} namespace Namespace to get klass "Class" object from
  3164. * @return {Object} klass "Class"
  3165. */
  3166. getKlass: function(type, namespace) {
  3167. // capitalize first letter only
  3168. type = fabric.util.string.camelize(type.charAt(0).toUpperCase() + type.slice(1));
  3169. return fabric.util.resolveNamespace(namespace)[type];
  3170. },
  3171. /**
  3172. * Returns object of given namespace
  3173. * @memberOf fabric.util
  3174. * @param {String} namespace Namespace string e.g. 'fabric.Image.filter' or 'fabric'
  3175. * @return {Object} Object for given namespace (default fabric)
  3176. */
  3177. resolveNamespace: function(namespace) {
  3178. if (!namespace) {
  3179. return fabric;
  3180. }
  3181. var parts = namespace.split('.'),
  3182. len = parts.length,
  3183. obj = global || fabric.window;
  3184. for (var i = 0; i < len; ++i) {
  3185. obj = obj[parts[i]];
  3186. }
  3187. return obj;
  3188. },
  3189. /**
  3190. * Loads image element from given url and passes it to a callback
  3191. * @memberOf fabric.util
  3192. * @param {String} url URL representing an image
  3193. * @param {Function} callback Callback; invoked with loaded image
  3194. * @param {Any} [context] Context to invoke callback in
  3195. * @param {Object} [crossOrigin] crossOrigin value to set image element to
  3196. */
  3197. loadImage: function(url, callback, context, crossOrigin) {
  3198. if (!url) {
  3199. callback && callback.call(context, url);
  3200. return;
  3201. }
  3202. var img = fabric.util.createImage();
  3203. /** @ignore */
  3204. img.onload = function () {
  3205. callback && callback.call(context, img);
  3206. img = img.onload = img.onerror = null;
  3207. };
  3208. /** @ignore */
  3209. img.onerror = function() {
  3210. fabric.log('Error loading ' + img.src);
  3211. callback && callback.call(context, null, true);
  3212. img = img.onload = img.onerror = null;
  3213. };
  3214. // data-urls appear to be buggy with crossOrigin
  3215. // https://github.com/kangax/fabric.js/commit/d0abb90f1cd5c5ef9d2a94d3fb21a22330da3e0a#commitcomment-4513767
  3216. // see https://code.google.com/p/chromium/issues/detail?id=315152
  3217. // https://bugzilla.mozilla.org/show_bug.cgi?id=935069
  3218. if (url.indexOf('data') !== 0 && typeof crossOrigin !== 'undefined') {
  3219. img.crossOrigin = crossOrigin;
  3220. }
  3221. img.src = url;
  3222. },
  3223. /**
  3224. * Creates corresponding fabric instances from their object representations
  3225. * @static
  3226. * @memberOf fabric.util
  3227. * @param {Array} objects Objects to enliven
  3228. * @param {Function} callback Callback to invoke when all objects are created
  3229. * @param {String} namespace Namespace to get klass "Class" object from
  3230. * @param {Function} reviver Method for further parsing of object elements,
  3231. * called after each fabric object created.
  3232. */
  3233. enlivenObjects: function(objects, callback, namespace, reviver) {
  3234. objects = objects || [ ];
  3235. function onLoaded() {
  3236. if (++numLoadedObjects === numTotalObjects) {
  3237. callback && callback(enlivenedObjects);
  3238. }
  3239. }
  3240. var enlivenedObjects = [ ],
  3241. numLoadedObjects = 0,
  3242. numTotalObjects = objects.length;
  3243. if (!numTotalObjects) {
  3244. callback && callback(enlivenedObjects);
  3245. return;
  3246. }
  3247. objects.forEach(function (o, index) {
  3248. // if sparse array
  3249. if (!o || !o.type) {
  3250. onLoaded();
  3251. return;
  3252. }
  3253. var klass = fabric.util.getKlass(o.type, namespace);
  3254. if (klass.async) {
  3255. klass.fromObject(o, function (obj, error) {
  3256. if (!error) {
  3257. enlivenedObjects[index] = obj;
  3258. reviver && reviver(o, enlivenedObjects[index]);
  3259. }
  3260. onLoaded();
  3261. });
  3262. }
  3263. else {
  3264. enlivenedObjects[index] = klass.fromObject(o);
  3265. reviver && reviver(o, enlivenedObjects[index]);
  3266. onLoaded();
  3267. }
  3268. });
  3269. },
  3270. /**
  3271. * Groups SVG elements (usually those retrieved from SVG document)
  3272. * @static
  3273. * @memberOf fabric.util
  3274. * @param {Array} elements SVG elements to group
  3275. * @param {Object} [options] Options object
  3276. * @return {fabric.Object|fabric.PathGroup}
  3277. */
  3278. groupSVGElements: function(elements, options, path) {
  3279. var object;
  3280. object = new fabric.PathGroup(elements, options);
  3281. if (typeof path !== 'undefined') {
  3282. object.setSourcePath(path);
  3283. }
  3284. return object;
  3285. },
  3286. /**
  3287. * Populates an object with properties of another object
  3288. * @static
  3289. * @memberOf fabric.util
  3290. * @param {Object} source Source object
  3291. * @param {Object} destination Destination object
  3292. * @return {Array} properties Propertie names to include
  3293. */
  3294. populateWithProperties: function(source, destination, properties) {
  3295. if (properties && Object.prototype.toString.call(properties) === '[object Array]') {
  3296. for (var i = 0, len = properties.length; i < len; i++) {
  3297. if (properties[i] in source) {
  3298. destination[properties[i]] = source[properties[i]];
  3299. }
  3300. }
  3301. }
  3302. },
  3303. /**
  3304. * Draws a dashed line between two points
  3305. *
  3306. * This method is used to draw dashed line around selection area.
  3307. * See <a href="http://stackoverflow.com/questions/4576724/dotted-stroke-in-canvas">dotted stroke in canvas</a>
  3308. *
  3309. * @param {CanvasRenderingContext2D} ctx context
  3310. * @param {Number} x start x coordinate
  3311. * @param {Number} y start y coordinate
  3312. * @param {Number} x2 end x coordinate
  3313. * @param {Number} y2 end y coordinate
  3314. * @param {Array} da dash array pattern
  3315. */
  3316. drawDashedLine: function(ctx, x, y, x2, y2, da) {
  3317. var dx = x2 - x,
  3318. dy = y2 - y,
  3319. len = sqrt(dx * dx + dy * dy),
  3320. rot = atan2(dy, dx),
  3321. dc = da.length,
  3322. di = 0,
  3323. draw = true;
  3324. ctx.save();
  3325. ctx.translate(x, y);
  3326. ctx.moveTo(0, 0);
  3327. ctx.rotate(rot);
  3328. x = 0;
  3329. while (len > x) {
  3330. x += da[di++ % dc];
  3331. if (x > len) {
  3332. x = len;
  3333. }
  3334. ctx[draw ? 'lineTo' : 'moveTo'](x, 0);
  3335. draw = !draw;
  3336. }
  3337. ctx.restore();
  3338. },
  3339. /**
  3340. * Creates canvas element and initializes it via excanvas if necessary
  3341. * @static
  3342. * @memberOf fabric.util
  3343. * @param {CanvasElement} [canvasEl] optional canvas element to initialize;
  3344. * when not given, element is created implicitly
  3345. * @return {CanvasElement} initialized canvas element
  3346. */
  3347. createCanvasElement: function(canvasEl) {
  3348. canvasEl || (canvasEl = fabric.document.createElement('canvas'));
  3349. //jscs:disable requireCamelCaseOrUpperCaseIdentifiers
  3350. if (!canvasEl.getContext && typeof G_vmlCanvasManager !== 'undefined') {
  3351. G_vmlCanvasManager.initElement(canvasEl);
  3352. }
  3353. //jscs:enable requireCamelCaseOrUpperCaseIdentifiers
  3354. return canvasEl;
  3355. },
  3356. /**
  3357. * Creates image element (works on client and node)
  3358. * @static
  3359. * @memberOf fabric.util
  3360. * @return {HTMLImageElement} HTML image element
  3361. */
  3362. createImage: function() {
  3363. return fabric.isLikelyNode
  3364. ? new (_dereq_('canvas').Image)()
  3365. : fabric.document.createElement('img');
  3366. },
  3367. /**
  3368. * Creates accessors (getXXX, setXXX) for a "class", based on "stateProperties" array
  3369. * @static
  3370. * @memberOf fabric.util
  3371. * @param {Object} klass "Class" to create accessors for
  3372. */
  3373. createAccessors: function(klass) {
  3374. var proto = klass.prototype;
  3375. for (var i = proto.stateProperties.length; i--; ) {
  3376. var propName = proto.stateProperties[i],
  3377. capitalizedPropName = propName.charAt(0).toUpperCase() + propName.slice(1),
  3378. setterName = 'set' + capitalizedPropName,
  3379. getterName = 'get' + capitalizedPropName;
  3380. // using `new Function` for better introspection
  3381. if (!proto[getterName]) {
  3382. proto[getterName] = (function(property) {
  3383. return new Function('return this.get("' + property + '")');
  3384. })(propName);
  3385. }
  3386. if (!proto[setterName]) {
  3387. proto[setterName] = (function(property) {
  3388. return new Function('value', 'return this.set("' + property + '", value)');
  3389. })(propName);
  3390. }
  3391. }
  3392. },
  3393. /**
  3394. * @static
  3395. * @memberOf fabric.util
  3396. * @param {fabric.Object} receiver Object implementing `clipTo` method
  3397. * @param {CanvasRenderingContext2D} ctx Context to clip
  3398. */
  3399. clipContext: function(receiver, ctx) {
  3400. ctx.save();
  3401. ctx.beginPath();
  3402. receiver.clipTo(ctx);
  3403. ctx.clip();
  3404. },
  3405. /**
  3406. * Multiply matrix A by matrix B to nest transformations
  3407. * @static
  3408. * @memberOf fabric.util
  3409. * @param {Array} matrixA First transformMatrix
  3410. * @param {Array} matrixB Second transformMatrix
  3411. * @return {Array} The product of the two transform matrices
  3412. */
  3413. multiplyTransformMatrices: function(matrixA, matrixB) {
  3414. // Matrix multiply matrixA * matrixB
  3415. var a = [
  3416. [matrixA[0], matrixA[2], matrixA[4]],
  3417. [matrixA[1], matrixA[3], matrixA[5]],
  3418. [0, 0, 1 ]
  3419. ],
  3420. b = [
  3421. [matrixB[0], matrixB[2], matrixB[4]],
  3422. [matrixB[1], matrixB[3], matrixB[5]],
  3423. [0, 0, 1 ]
  3424. ],
  3425. result = [];
  3426. for (var r = 0; r < 3; r++) {
  3427. result[r] = [];
  3428. for (var c = 0; c < 3; c++) {
  3429. var sum = 0;
  3430. for (var k = 0; k < 3; k++) {
  3431. sum += a[r][k] * b[k][c];
  3432. }
  3433. result[r][c] = sum;
  3434. }
  3435. }
  3436. return [
  3437. result[0][0],
  3438. result[1][0],
  3439. result[0][1],
  3440. result[1][1],
  3441. result[0][2],
  3442. result[1][2]
  3443. ];
  3444. },
  3445. /**
  3446. * Returns string representation of function body
  3447. * @param {Function} fn Function to get body of
  3448. * @return {String} Function body
  3449. */
  3450. getFunctionBody: function(fn) {
  3451. return (String(fn).match(/function[^{]*\{([\s\S]*)\}/) || {})[1];
  3452. },
  3453. /**
  3454. * Returns true if context has transparent pixel
  3455. * at specified location (taking tolerance into account)
  3456. * @param {CanvasRenderingContext2D} ctx context
  3457. * @param {Number} x x coordinate
  3458. * @param {Number} y y coordinate
  3459. * @param {Number} tolerance Tolerance
  3460. */
  3461. isTransparent: function(ctx, x, y, tolerance) {
  3462. // If tolerance is > 0 adjust start coords to take into account.
  3463. // If moves off Canvas fix to 0
  3464. if (tolerance > 0) {
  3465. if (x > tolerance) {
  3466. x -= tolerance;
  3467. }
  3468. else {
  3469. x = 0;
  3470. }
  3471. if (y > tolerance) {
  3472. y -= tolerance;
  3473. }
  3474. else {
  3475. y = 0;
  3476. }
  3477. }
  3478. var _isTransparent = true,
  3479. imageData = ctx.getImageData(x, y, (tolerance * 2) || 1, (tolerance * 2) || 1);
  3480. // Split image data - for tolerance > 1, pixelDataSize = 4;
  3481. for (var i = 3, l = imageData.data.length; i < l; i += 4) {
  3482. var temp = imageData.data[i];
  3483. _isTransparent = temp <= 0;
  3484. if (_isTransparent === false) {
  3485. break; // Stop if colour found
  3486. }
  3487. }
  3488. imageData = null;
  3489. return _isTransparent;
  3490. }
  3491. };
  3492. })(typeof exports !== 'undefined' ? exports : this);
  3493. (function() {
  3494. var arcToSegmentsCache = { },
  3495. segmentToBezierCache = { },
  3496. _join = Array.prototype.join;
  3497. /* Adapted from http://dxr.mozilla.org/mozilla-central/source/content/svg/content/src/nsSVGPathDataParser.cpp
  3498. * by Andrea Bogazzi code is under MPL. if you don't have a copy of the license you can take it here
  3499. * http://mozilla.org/MPL/2.0/
  3500. */
  3501. function arcToSegments(toX, toY, rx, ry, large, sweep, rotateX) {
  3502. var argsString = _join.call(arguments);
  3503. if (arcToSegmentsCache[argsString]) {
  3504. return arcToSegmentsCache[argsString];
  3505. }
  3506. var PI = Math.PI, th = rotateX * (PI / 180),
  3507. sinTh = Math.sin(th),
  3508. cosTh = Math.cos(th),
  3509. fromX = 0, fromY = 0;
  3510. rx = Math.abs(rx);
  3511. ry = Math.abs(ry);
  3512. var px = -cosTh * toX - sinTh * toY,
  3513. py = -cosTh * toY + sinTh * toX,
  3514. rx2 = rx * rx, ry2 = ry * ry, py2 = py * py, px2 = px * px,
  3515. pl = 4 * rx2 * ry2 - rx2 * py2 - ry2 * px2,
  3516. root = 0;
  3517. if (pl < 0) {
  3518. var s = Math.sqrt(1 - 0.25 * pl/(rx2 * ry2));
  3519. rx *= s;
  3520. ry *= s;
  3521. }
  3522. else {
  3523. root = (large === sweep ? -0.5 : 0.5) *
  3524. Math.sqrt( pl /(rx2 * py2 + ry2 * px2));
  3525. }
  3526. var cx = root * rx * py / ry,
  3527. cy = -root * ry * px / rx,
  3528. cx1 = cosTh * cx - sinTh * cy + toX / 2,
  3529. cy1 = sinTh * cx + cosTh * cy + toY / 2,
  3530. mTheta = calcVectorAngle(1, 0, (px - cx) / rx, (py - cy) / ry),
  3531. dtheta = calcVectorAngle((px - cx) / rx, (py - cy) / ry, (-px - cx) / rx, (-py - cy) / ry);
  3532. if (sweep === 0 && dtheta > 0) {
  3533. dtheta -= 2 * PI;
  3534. }
  3535. else if (sweep === 1 && dtheta < 0) {
  3536. dtheta += 2 * PI;
  3537. }
  3538. // Convert into cubic bezier segments <= 90deg
  3539. var segments = Math.ceil(Math.abs(dtheta / (PI * 0.5))),
  3540. result = [], mDelta = dtheta / segments,
  3541. mT = 8 / 3 * Math.sin(mDelta / 4) * Math.sin(mDelta / 4) / Math.sin(mDelta / 2),
  3542. th3 = mTheta + mDelta;
  3543. for (var i = 0; i < segments; i++) {
  3544. result[i] = segmentToBezier(mTheta, th3, cosTh, sinTh, rx, ry, cx1, cy1, mT, fromX, fromY);
  3545. fromX = result[i][4];
  3546. fromY = result[i][5];
  3547. mTheta += mDelta;
  3548. th3 += mDelta;
  3549. }
  3550. arcToSegmentsCache[argsString] = result;
  3551. return result;
  3552. }
  3553. function segmentToBezier(th2, th3, cosTh, sinTh, rx, ry, cx1, cy1, mT, fromX, fromY) {
  3554. var argsString2 = _join.call(arguments);
  3555. if (segmentToBezierCache[argsString2]) {
  3556. return segmentToBezierCache[argsString2];
  3557. }
  3558. var costh2 = Math.cos(th2),
  3559. sinth2 = Math.sin(th2),
  3560. costh3 = Math.cos(th3),
  3561. sinth3 = Math.sin(th3),
  3562. toX = cosTh * rx * costh3 - sinTh * ry * sinth3 + cx1,
  3563. toY = sinTh * rx * costh3 + cosTh * ry * sinth3 + cy1,
  3564. cp1X = fromX + mT * ( - cosTh * rx * sinth2 - sinTh * ry * costh2),
  3565. cp1Y = fromY + mT * ( - sinTh * rx * sinth2 + cosTh * ry * costh2),
  3566. cp2X = toX + mT * ( cosTh * rx * sinth3 + sinTh * ry * costh3),
  3567. cp2Y = toY + mT * ( sinTh * rx * sinth3 - cosTh * ry * costh3);
  3568. segmentToBezierCache[argsString2] = [
  3569. cp1X, cp1Y,
  3570. cp2X, cp2Y,
  3571. toX, toY
  3572. ];
  3573. return segmentToBezierCache[argsString2];
  3574. }
  3575. /*
  3576. * Private
  3577. */
  3578. function calcVectorAngle(ux, uy, vx, vy) {
  3579. var ta = Math.atan2(uy, ux),
  3580. tb = Math.atan2(vy, vx);
  3581. if (tb >= ta) {
  3582. return tb - ta;
  3583. }
  3584. else {
  3585. return 2 * Math.PI - (ta - tb);
  3586. }
  3587. }
  3588. /**
  3589. * Draws arc
  3590. * @param {CanvasRenderingContext2D} ctx
  3591. * @param {Number} fx
  3592. * @param {Number} fy
  3593. * @param {Array} coords
  3594. */
  3595. fabric.util.drawArc = function(ctx, fx, fy, coords) {
  3596. var rx = coords[0],
  3597. ry = coords[1],
  3598. rot = coords[2],
  3599. large = coords[3],
  3600. sweep = coords[4],
  3601. tx = coords[5],
  3602. ty = coords[6],
  3603. segs = [[ ], [ ], [ ], [ ]],
  3604. segsNorm = arcToSegments(tx - fx, ty - fy, rx, ry, large, sweep, rot);
  3605. for (var i = 0, len = segsNorm.length; i < len; i++) {
  3606. segs[i][0] = segsNorm[i][0] + fx;
  3607. segs[i][1] = segsNorm[i][1] + fy;
  3608. segs[i][2] = segsNorm[i][2] + fx;
  3609. segs[i][3] = segsNorm[i][3] + fy;
  3610. segs[i][4] = segsNorm[i][4] + fx;
  3611. segs[i][5] = segsNorm[i][5] + fy;
  3612. ctx.bezierCurveTo.apply(ctx, segs[i]);
  3613. }
  3614. };
  3615. })();
  3616. (function() {
  3617. var slice = Array.prototype.slice;
  3618. /**
  3619. * Invokes method on all items in a given array
  3620. * @memberOf fabric.util.array
  3621. * @param {Array} array Array to iterate over
  3622. * @param {String} method Name of a method to invoke
  3623. * @return {Array}
  3624. */
  3625. function invoke(array, method) {
  3626. var args = slice.call(arguments, 2), result = [ ];
  3627. for (var i = 0, len = array.length; i < len; i++) {
  3628. result[i] = args.length ? array[i][method].apply(array[i], args) : array[i][method].call(array[i]);
  3629. }
  3630. return result;
  3631. }
  3632. /**
  3633. * Finds maximum value in array (not necessarily "first" one)
  3634. * @memberOf fabric.util.array
  3635. * @param {Array} array Array to iterate over
  3636. * @param {String} byProperty
  3637. * @return {Any}
  3638. */
  3639. function max(array, byProperty) {
  3640. return find(array, byProperty, function(value1, value2) {
  3641. return value1 >= value2;
  3642. });
  3643. }
  3644. /**
  3645. * Finds minimum value in array (not necessarily "first" one)
  3646. * @memberOf fabric.util.array
  3647. * @param {Array} array Array to iterate over
  3648. * @param {String} byProperty
  3649. * @return {Any}
  3650. */
  3651. function min(array, byProperty) {
  3652. return find(array, byProperty, function(value1, value2) {
  3653. return value1 < value2;
  3654. });
  3655. }
  3656. /**
  3657. * @private
  3658. */
  3659. function find(array, byProperty, condition) {
  3660. if (!array || array.length === 0) {
  3661. return;
  3662. }
  3663. var i = array.length - 1,
  3664. result = byProperty ? array[i][byProperty] : array[i];
  3665. if (byProperty) {
  3666. while (i--) {
  3667. if (condition(array[i][byProperty], result)) {
  3668. result = array[i][byProperty];
  3669. }
  3670. }
  3671. }
  3672. else {
  3673. while (i--) {
  3674. if (condition(array[i], result)) {
  3675. result = array[i];
  3676. }
  3677. }
  3678. }
  3679. return result;
  3680. }
  3681. /**
  3682. * @namespace fabric.util.array
  3683. */
  3684. fabric.util.array = {
  3685. invoke: invoke,
  3686. min: min,
  3687. max: max
  3688. };
  3689. })();
  3690. (function(){
  3691. /**
  3692. * Copies all enumerable properties of one object to another
  3693. * @memberOf fabric.util.object
  3694. * @param {Object} destination Where to copy to
  3695. * @param {Object} source Where to copy from
  3696. * @return {Object}
  3697. */
  3698. function extend(destination, source) {
  3699. // JScript DontEnum bug is not taken care of
  3700. for (var property in source) {
  3701. destination[property] = source[property];
  3702. }
  3703. return destination;
  3704. }
  3705. /**
  3706. * Creates an empty object and copies all enumerable properties of another object to it
  3707. * @memberOf fabric.util.object
  3708. * @param {Object} object Object to clone
  3709. * @return {Object}
  3710. */
  3711. function clone(object) {
  3712. return extend({ }, object);
  3713. }
  3714. /** @namespace fabric.util.object */
  3715. fabric.util.object = {
  3716. extend: extend,
  3717. clone: clone
  3718. };
  3719. })();
  3720. (function() {
  3721. /**
  3722. * Camelizes a string
  3723. * @memberOf fabric.util.string
  3724. * @param {String} string String to camelize
  3725. * @return {String} Camelized version of a string
  3726. */
  3727. function camelize(string) {
  3728. return string.replace(/-+(.)?/g, function(match, character) {
  3729. return character ? character.toUpperCase() : '';
  3730. });
  3731. }
  3732. /**
  3733. * Capitalizes a string
  3734. * @memberOf fabric.util.string
  3735. * @param {String} string String to capitalize
  3736. * @param {Boolean} [firstLetterOnly] If true only first letter is capitalized
  3737. * and other letters stay untouched, if false first letter is capitalized
  3738. * and other letters are converted to lowercase.
  3739. * @return {String} Capitalized version of a string
  3740. */
  3741. function capitalize(string, firstLetterOnly) {
  3742. return string.charAt(0).toUpperCase() +
  3743. (firstLetterOnly ? string.slice(1) : string.slice(1).toLowerCase());
  3744. }
  3745. /**
  3746. * Escapes XML in a string
  3747. * @memberOf fabric.util.string
  3748. * @param {String} string String to escape
  3749. * @return {String} Escaped version of a string
  3750. */
  3751. function escapeXml(string) {
  3752. return string.replace(/&/g, '&amp;')
  3753. .replace(/"/g, '&quot;')
  3754. .replace(/'/g, '&apos;')
  3755. .replace(/</g, '&lt;')
  3756. .replace(/>/g, '&gt;');
  3757. }
  3758. /**
  3759. * String utilities
  3760. * @namespace fabric.util.string
  3761. */
  3762. fabric.util.string = {
  3763. camelize: camelize,
  3764. capitalize: capitalize,
  3765. escapeXml: escapeXml
  3766. };
  3767. }());
  3768. (function() {
  3769. var slice = Array.prototype.slice, emptyFunction = function() { },
  3770. IS_DONTENUM_BUGGY = (function(){
  3771. for (var p in { toString: 1 }) {
  3772. if (p === 'toString') {
  3773. return false;
  3774. }
  3775. }
  3776. return true;
  3777. })(),
  3778. /** @ignore */
  3779. addMethods = function(klass, source, parent) {
  3780. for (var property in source) {
  3781. if (property in klass.prototype &&
  3782. typeof klass.prototype[property] === 'function' &&
  3783. (source[property] + '').indexOf('callSuper') > -1) {
  3784. klass.prototype[property] = (function(property) {
  3785. return function() {
  3786. var superclass = this.constructor.superclass;
  3787. this.constructor.superclass = parent;
  3788. var returnValue = source[property].apply(this, arguments);
  3789. this.constructor.superclass = superclass;
  3790. if (property !== 'initialize') {
  3791. return returnValue;
  3792. }
  3793. };
  3794. })(property);
  3795. }
  3796. else {
  3797. klass.prototype[property] = source[property];
  3798. }
  3799. if (IS_DONTENUM_BUGGY) {
  3800. if (source.toString !== Object.prototype.toString) {
  3801. klass.prototype.toString = source.toString;
  3802. }
  3803. if (source.valueOf !== Object.prototype.valueOf) {
  3804. klass.prototype.valueOf = source.valueOf;
  3805. }
  3806. }
  3807. }
  3808. };
  3809. function Subclass() { }
  3810. function callSuper(methodName) {
  3811. var fn = this.constructor.superclass.prototype[methodName];
  3812. return (arguments.length > 1)
  3813. ? fn.apply(this, slice.call(arguments, 1))
  3814. : fn.call(this);
  3815. }
  3816. /**
  3817. * Helper for creation of "classes".
  3818. * @memberOf fabric.util
  3819. * @param {Function} [parent] optional "Class" to inherit from
  3820. * @param {Object} [properties] Properties shared by all instances of this class
  3821. * (be careful modifying objects defined here as this would affect all instances)
  3822. */
  3823. function createClass() {
  3824. var parent = null,
  3825. properties = slice.call(arguments, 0);
  3826. if (typeof properties[0] === 'function') {
  3827. parent = properties.shift();
  3828. }
  3829. function klass() {
  3830. this.initialize.apply(this, arguments);
  3831. }
  3832. klass.superclass = parent;
  3833. klass.subclasses = [ ];
  3834. if (parent) {
  3835. Subclass.prototype = parent.prototype;
  3836. klass.prototype = new Subclass();
  3837. parent.subclasses.push(klass);
  3838. }
  3839. for (var i = 0, length = properties.length; i < length; i++) {
  3840. addMethods(klass, properties[i], parent);
  3841. }
  3842. if (!klass.prototype.initialize) {
  3843. klass.prototype.initialize = emptyFunction;
  3844. }
  3845. klass.prototype.constructor = klass;
  3846. klass.prototype.callSuper = callSuper;
  3847. return klass;
  3848. }
  3849. fabric.util.createClass = createClass;
  3850. })();
  3851. (function () {
  3852. var unknown = 'unknown';
  3853. /* EVENT HANDLING */
  3854. function areHostMethods(object) {
  3855. var methodNames = Array.prototype.slice.call(arguments, 1),
  3856. t, i, len = methodNames.length;
  3857. for (i = 0; i < len; i++) {
  3858. t = typeof object[methodNames[i]];
  3859. if (!(/^(?:function|object|unknown)$/).test(t)) {
  3860. return false;
  3861. }
  3862. }
  3863. return true;
  3864. }
  3865. /** @ignore */
  3866. var getElement,
  3867. setElement,
  3868. getUniqueId = (function () {
  3869. var uid = 0;
  3870. return function (element) {
  3871. return element.__uniqueID || (element.__uniqueID = 'uniqueID__' + uid++);
  3872. };
  3873. })();
  3874. (function () {
  3875. var elements = { };
  3876. /** @ignore */
  3877. getElement = function (uid) {
  3878. return elements[uid];
  3879. };
  3880. /** @ignore */
  3881. setElement = function (uid, element) {
  3882. elements[uid] = element;
  3883. };
  3884. })();
  3885. function createListener(uid, handler) {
  3886. return {
  3887. handler: handler,
  3888. wrappedHandler: createWrappedHandler(uid, handler)
  3889. };
  3890. }
  3891. function createWrappedHandler(uid, handler) {
  3892. return function (e) {
  3893. handler.call(getElement(uid), e || fabric.window.event);
  3894. };
  3895. }
  3896. function createDispatcher(uid, eventName) {
  3897. return function (e) {
  3898. if (handlers[uid] && handlers[uid][eventName]) {
  3899. var handlersForEvent = handlers[uid][eventName];
  3900. for (var i = 0, len = handlersForEvent.length; i < len; i++) {
  3901. handlersForEvent[i].call(this, e || fabric.window.event);
  3902. }
  3903. }
  3904. };
  3905. }
  3906. var shouldUseAddListenerRemoveListener = (
  3907. areHostMethods(fabric.document.documentElement, 'addEventListener', 'removeEventListener') &&
  3908. areHostMethods(fabric.window, 'addEventListener', 'removeEventListener')),
  3909. shouldUseAttachEventDetachEvent = (
  3910. areHostMethods(fabric.document.documentElement, 'attachEvent', 'detachEvent') &&
  3911. areHostMethods(fabric.window, 'attachEvent', 'detachEvent')),
  3912. // IE branch
  3913. listeners = { },
  3914. // DOM L0 branch
  3915. handlers = { },
  3916. addListener, removeListener;
  3917. if (shouldUseAddListenerRemoveListener) {
  3918. /** @ignore */
  3919. addListener = function (element, eventName, handler) {
  3920. element.addEventListener(eventName, handler, false);
  3921. };
  3922. /** @ignore */
  3923. removeListener = function (element, eventName, handler) {
  3924. element.removeEventListener(eventName, handler, false);
  3925. };
  3926. }
  3927. else if (shouldUseAttachEventDetachEvent) {
  3928. /** @ignore */
  3929. addListener = function (element, eventName, handler) {
  3930. var uid = getUniqueId(element);
  3931. setElement(uid, element);
  3932. if (!listeners[uid]) {
  3933. listeners[uid] = { };
  3934. }
  3935. if (!listeners[uid][eventName]) {
  3936. listeners[uid][eventName] = [ ];
  3937. }
  3938. var listener = createListener(uid, handler);
  3939. listeners[uid][eventName].push(listener);
  3940. element.attachEvent('on' + eventName, listener.wrappedHandler);
  3941. };
  3942. /** @ignore */
  3943. removeListener = function (element, eventName, handler) {
  3944. var uid = getUniqueId(element), listener;
  3945. if (listeners[uid] && listeners[uid][eventName]) {
  3946. for (var i = 0, len = listeners[uid][eventName].length; i < len; i++) {
  3947. listener = listeners[uid][eventName][i];
  3948. if (listener && listener.handler === handler) {
  3949. element.detachEvent('on' + eventName, listener.wrappedHandler);
  3950. listeners[uid][eventName][i] = null;
  3951. }
  3952. }
  3953. }
  3954. };
  3955. }
  3956. else {
  3957. /** @ignore */
  3958. addListener = function (element, eventName, handler) {
  3959. var uid = getUniqueId(element);
  3960. if (!handlers[uid]) {
  3961. handlers[uid] = { };
  3962. }
  3963. if (!handlers[uid][eventName]) {
  3964. handlers[uid][eventName] = [ ];
  3965. var existingHandler = element['on' + eventName];
  3966. if (existingHandler) {
  3967. handlers[uid][eventName].push(existingHandler);
  3968. }
  3969. element['on' + eventName] = createDispatcher(uid, eventName);
  3970. }
  3971. handlers[uid][eventName].push(handler);
  3972. };
  3973. /** @ignore */
  3974. removeListener = function (element, eventName, handler) {
  3975. var uid = getUniqueId(element);
  3976. if (handlers[uid] && handlers[uid][eventName]) {
  3977. var handlersForEvent = handlers[uid][eventName];
  3978. for (var i = 0, len = handlersForEvent.length; i < len; i++) {
  3979. if (handlersForEvent[i] === handler) {
  3980. handlersForEvent.splice(i, 1);
  3981. }
  3982. }
  3983. }
  3984. };
  3985. }
  3986. /**
  3987. * Adds an event listener to an element
  3988. * @function
  3989. * @memberOf fabric.util
  3990. * @param {HTMLElement} element
  3991. * @param {String} eventName
  3992. * @param {Function} handler
  3993. */
  3994. fabric.util.addListener = addListener;
  3995. /**
  3996. * Removes an event listener from an element
  3997. * @function
  3998. * @memberOf fabric.util
  3999. * @param {HTMLElement} element
  4000. * @param {String} eventName
  4001. * @param {Function} handler
  4002. */
  4003. fabric.util.removeListener = removeListener;
  4004. /**
  4005. * Cross-browser wrapper for getting event's coordinates
  4006. * @memberOf fabric.util
  4007. * @param {Event} event Event object
  4008. * @param {HTMLCanvasElement} upperCanvasEl &lt;canvas> element on which object selection is drawn
  4009. */
  4010. function getPointer(event, upperCanvasEl) {
  4011. event || (event = fabric.window.event);
  4012. var element = event.target ||
  4013. (typeof event.srcElement !== unknown ? event.srcElement : null),
  4014. scroll = fabric.util.getScrollLeftTop(element, upperCanvasEl);
  4015. return {
  4016. x: pointerX(event) + scroll.left,
  4017. y: pointerY(event) + scroll.top
  4018. };
  4019. }
  4020. var pointerX = function(event) {
  4021. // looks like in IE (<9) clientX at certain point (apparently when mouseup fires on VML element)
  4022. // is represented as COM object, with all the consequences, like "unknown" type and error on [[Get]]
  4023. // need to investigate later
  4024. return (typeof event.clientX !== unknown ? event.clientX : 0);
  4025. },
  4026. pointerY = function(event) {
  4027. return (typeof event.clientY !== unknown ? event.clientY : 0);
  4028. };
  4029. function _getPointer(event, pageProp, clientProp) {
  4030. var touchProp = event.type === 'touchend' ? 'changedTouches' : 'touches';
  4031. return (event[touchProp] && event[touchProp][0]
  4032. ? (event[touchProp][0][pageProp] - (event[touchProp][0][pageProp] - event[touchProp][0][clientProp]))
  4033. || event[clientProp]
  4034. : event[clientProp]);
  4035. }
  4036. if (fabric.isTouchSupported) {
  4037. pointerX = function(event) {
  4038. return _getPointer(event, 'pageX', 'clientX');
  4039. };
  4040. pointerY = function(event) {
  4041. return _getPointer(event, 'pageY', 'clientY');
  4042. };
  4043. }
  4044. fabric.util.getPointer = getPointer;
  4045. fabric.util.object.extend(fabric.util, fabric.Observable);
  4046. })();
  4047. (function () {
  4048. /**
  4049. * Cross-browser wrapper for setting element's style
  4050. * @memberOf fabric.util
  4051. * @param {HTMLElement} element
  4052. * @param {Object} styles
  4053. * @return {HTMLElement} Element that was passed as a first argument
  4054. */
  4055. function setStyle(element, styles) {
  4056. var elementStyle = element.style;
  4057. if (!elementStyle) {
  4058. return element;
  4059. }
  4060. if (typeof styles === 'string') {
  4061. element.style.cssText += ';' + styles;
  4062. return styles.indexOf('opacity') > -1
  4063. ? setOpacity(element, styles.match(/opacity:\s*(\d?\.?\d*)/)[1])
  4064. : element;
  4065. }
  4066. for (var property in styles) {
  4067. if (property === 'opacity') {
  4068. setOpacity(element, styles[property]);
  4069. }
  4070. else {
  4071. var normalizedProperty = (property === 'float' || property === 'cssFloat')
  4072. ? (typeof elementStyle.styleFloat === 'undefined' ? 'cssFloat' : 'styleFloat')
  4073. : property;
  4074. elementStyle[normalizedProperty] = styles[property];
  4075. }
  4076. }
  4077. return element;
  4078. }
  4079. var parseEl = fabric.document.createElement('div'),
  4080. supportsOpacity = typeof parseEl.style.opacity === 'string',
  4081. supportsFilters = typeof parseEl.style.filter === 'string',
  4082. reOpacity = /alpha\s*\(\s*opacity\s*=\s*([^\)]+)\)/,
  4083. /** @ignore */
  4084. setOpacity = function (element) { return element; };
  4085. if (supportsOpacity) {
  4086. /** @ignore */
  4087. setOpacity = function(element, value) {
  4088. element.style.opacity = value;
  4089. return element;
  4090. };
  4091. }
  4092. else if (supportsFilters) {
  4093. /** @ignore */
  4094. setOpacity = function(element, value) {
  4095. var es = element.style;
  4096. if (element.currentStyle && !element.currentStyle.hasLayout) {
  4097. es.zoom = 1;
  4098. }
  4099. if (reOpacity.test(es.filter)) {
  4100. value = value >= 0.9999 ? '' : ('alpha(opacity=' + (value * 100) + ')');
  4101. es.filter = es.filter.replace(reOpacity, value);
  4102. }
  4103. else {
  4104. es.filter += ' alpha(opacity=' + (value * 100) + ')';
  4105. }
  4106. return element;
  4107. };
  4108. }
  4109. fabric.util.setStyle = setStyle;
  4110. })();
  4111. (function() {
  4112. var _slice = Array.prototype.slice;
  4113. /**
  4114. * Takes id and returns an element with that id (if one exists in a document)
  4115. * @memberOf fabric.util
  4116. * @param {String|HTMLElement} id
  4117. * @return {HTMLElement|null}
  4118. */
  4119. function getById(id) {
  4120. return typeof id === 'string' ? fabric.document.getElementById(id) : id;
  4121. }
  4122. var sliceCanConvertNodelists,
  4123. /**
  4124. * Converts an array-like object (e.g. arguments or NodeList) to an array
  4125. * @memberOf fabric.util
  4126. * @param {Object} arrayLike
  4127. * @return {Array}
  4128. */
  4129. toArray = function(arrayLike) {
  4130. return _slice.call(arrayLike, 0);
  4131. };
  4132. try {
  4133. sliceCanConvertNodelists = toArray(fabric.document.childNodes) instanceof Array;
  4134. }
  4135. catch (err) { }
  4136. if (!sliceCanConvertNodelists) {
  4137. toArray = function(arrayLike) {
  4138. var arr = new Array(arrayLike.length), i = arrayLike.length;
  4139. while (i--) {
  4140. arr[i] = arrayLike[i];
  4141. }
  4142. return arr;
  4143. };
  4144. }
  4145. /**
  4146. * Creates specified element with specified attributes
  4147. * @memberOf fabric.util
  4148. * @param {String} tagName Type of an element to create
  4149. * @param {Object} [attributes] Attributes to set on an element
  4150. * @return {HTMLElement} Newly created element
  4151. */
  4152. function makeElement(tagName, attributes) {
  4153. var el = fabric.document.createElement(tagName);
  4154. for (var prop in attributes) {
  4155. if (prop === 'class') {
  4156. el.className = attributes[prop];
  4157. }
  4158. else if (prop === 'for') {
  4159. el.htmlFor = attributes[prop];
  4160. }
  4161. else {
  4162. el.setAttribute(prop, attributes[prop]);
  4163. }
  4164. }
  4165. return el;
  4166. }
  4167. /**
  4168. * Adds class to an element
  4169. * @memberOf fabric.util
  4170. * @param {HTMLElement} element Element to add class to
  4171. * @param {String} className Class to add to an element
  4172. */
  4173. function addClass(element, className) {
  4174. if (element && (' ' + element.className + ' ').indexOf(' ' + className + ' ') === -1) {
  4175. element.className += (element.className ? ' ' : '') + className;
  4176. }
  4177. }
  4178. /**
  4179. * Wraps element with another element
  4180. * @memberOf fabric.util
  4181. * @param {HTMLElement} element Element to wrap
  4182. * @param {HTMLElement|String} wrapper Element to wrap with
  4183. * @param {Object} [attributes] Attributes to set on a wrapper
  4184. * @return {HTMLElement} wrapper
  4185. */
  4186. function wrapElement(element, wrapper, attributes) {
  4187. if (typeof wrapper === 'string') {
  4188. wrapper = makeElement(wrapper, attributes);
  4189. }
  4190. if (element.parentNode) {
  4191. element.parentNode.replaceChild(wrapper, element);
  4192. }
  4193. wrapper.appendChild(element);
  4194. return wrapper;
  4195. }
  4196. /**
  4197. * Returns element scroll offsets
  4198. * @memberOf fabric.util
  4199. * @param {HTMLElement} element Element to operate on
  4200. * @param {HTMLElement} upperCanvasEl Upper canvas element
  4201. * @return {Object} Object with left/top values
  4202. */
  4203. function getScrollLeftTop(element, upperCanvasEl) {
  4204. var firstFixedAncestor,
  4205. origElement,
  4206. left = 0,
  4207. top = 0,
  4208. docElement = fabric.document.documentElement,
  4209. body = fabric.document.body || {
  4210. scrollLeft: 0, scrollTop: 0
  4211. };
  4212. origElement = element;
  4213. while (element && element.parentNode && !firstFixedAncestor) {
  4214. element = element.parentNode;
  4215. if (element !== fabric.document &&
  4216. fabric.util.getElementStyle(element, 'position') === 'fixed') {
  4217. firstFixedAncestor = element;
  4218. }
  4219. if (element !== fabric.document &&
  4220. origElement !== upperCanvasEl &&
  4221. fabric.util.getElementStyle(element, 'position') === 'absolute') {
  4222. left = 0;
  4223. top = 0;
  4224. }
  4225. else if (element === fabric.document) {
  4226. left = body.scrollLeft || docElement.scrollLeft || 0;
  4227. top = body.scrollTop || docElement.scrollTop || 0;
  4228. }
  4229. else {
  4230. left += element.scrollLeft || 0;
  4231. top += element.scrollTop || 0;
  4232. }
  4233. }
  4234. return { left: left, top: top };
  4235. }
  4236. /**
  4237. * Returns offset for a given element
  4238. * @function
  4239. * @memberOf fabric.util
  4240. * @param {HTMLElement} element Element to get offset for
  4241. * @return {Object} Object with "left" and "top" properties
  4242. */
  4243. function getElementOffset(element) {
  4244. var docElem,
  4245. doc = element && element.ownerDocument,
  4246. box = { left: 0, top: 0 },
  4247. offset = { left: 0, top: 0 },
  4248. scrollLeftTop,
  4249. offsetAttributes = {
  4250. borderLeftWidth: 'left',
  4251. borderTopWidth: 'top',
  4252. paddingLeft: 'left',
  4253. paddingTop: 'top'
  4254. };
  4255. if (!doc) {
  4256. return { left: 0, top: 0 };
  4257. }
  4258. for (var attr in offsetAttributes) {
  4259. offset[offsetAttributes[attr]] += parseInt(getElementStyle(element, attr), 10) || 0;
  4260. }
  4261. docElem = doc.documentElement;
  4262. if ( typeof element.getBoundingClientRect !== 'undefined' ) {
  4263. box = element.getBoundingClientRect();
  4264. }
  4265. scrollLeftTop = fabric.util.getScrollLeftTop(element, null);
  4266. return {
  4267. left: box.left + scrollLeftTop.left - (docElem.clientLeft || 0) + offset.left,
  4268. top: box.top + scrollLeftTop.top - (docElem.clientTop || 0) + offset.top
  4269. };
  4270. }
  4271. /**
  4272. * Returns style attribute value of a given element
  4273. * @memberOf fabric.util
  4274. * @param {HTMLElement} element Element to get style attribute for
  4275. * @param {String} attr Style attribute to get for element
  4276. * @return {String} Style attribute value of the given element.
  4277. */
  4278. var getElementStyle;
  4279. if (fabric.document.defaultView && fabric.document.defaultView.getComputedStyle) {
  4280. getElementStyle = function(element, attr) {
  4281. return fabric.document.defaultView.getComputedStyle(element, null)[attr];
  4282. };
  4283. }
  4284. else {
  4285. getElementStyle = function(element, attr) {
  4286. var value = element.style[attr];
  4287. if (!value && element.currentStyle) {
  4288. value = element.currentStyle[attr];
  4289. }
  4290. return value;
  4291. };
  4292. }
  4293. (function () {
  4294. var style = fabric.document.documentElement.style,
  4295. selectProp = 'userSelect' in style
  4296. ? 'userSelect'
  4297. : 'MozUserSelect' in style
  4298. ? 'MozUserSelect'
  4299. : 'WebkitUserSelect' in style
  4300. ? 'WebkitUserSelect'
  4301. : 'KhtmlUserSelect' in style
  4302. ? 'KhtmlUserSelect'
  4303. : '';
  4304. /**
  4305. * Makes element unselectable
  4306. * @memberOf fabric.util
  4307. * @param {HTMLElement} element Element to make unselectable
  4308. * @return {HTMLElement} Element that was passed in
  4309. */
  4310. function makeElementUnselectable(element) {
  4311. if (typeof element.onselectstart !== 'undefined') {
  4312. element.onselectstart = fabric.util.falseFunction;
  4313. }
  4314. if (selectProp) {
  4315. element.style[selectProp] = 'none';
  4316. }
  4317. else if (typeof element.unselectable === 'string') {
  4318. element.unselectable = 'on';
  4319. }
  4320. return element;
  4321. }
  4322. /**
  4323. * Makes element selectable
  4324. * @memberOf fabric.util
  4325. * @param {HTMLElement} element Element to make selectable
  4326. * @return {HTMLElement} Element that was passed in
  4327. */
  4328. function makeElementSelectable(element) {
  4329. if (typeof element.onselectstart !== 'undefined') {
  4330. element.onselectstart = null;
  4331. }
  4332. if (selectProp) {
  4333. element.style[selectProp] = '';
  4334. }
  4335. else if (typeof element.unselectable === 'string') {
  4336. element.unselectable = '';
  4337. }
  4338. return element;
  4339. }
  4340. fabric.util.makeElementUnselectable = makeElementUnselectable;
  4341. fabric.util.makeElementSelectable = makeElementSelectable;
  4342. })();
  4343. (function() {
  4344. /**
  4345. * Inserts a script element with a given url into a document; invokes callback, when that script is finished loading
  4346. * @memberOf fabric.util
  4347. * @param {String} url URL of a script to load
  4348. * @param {Function} callback Callback to execute when script is finished loading
  4349. */
  4350. function getScript(url, callback) {
  4351. var headEl = fabric.document.getElementsByTagName('head')[0],
  4352. scriptEl = fabric.document.createElement('script'),
  4353. loading = true;
  4354. /** @ignore */
  4355. scriptEl.onload = /** @ignore */ scriptEl.onreadystatechange = function(e) {
  4356. if (loading) {
  4357. if (typeof this.readyState === 'string' &&
  4358. this.readyState !== 'loaded' &&
  4359. this.readyState !== 'complete') {
  4360. return;
  4361. }
  4362. loading = false;
  4363. callback(e || fabric.window.event);
  4364. scriptEl = scriptEl.onload = scriptEl.onreadystatechange = null;
  4365. }
  4366. };
  4367. scriptEl.src = url;
  4368. headEl.appendChild(scriptEl);
  4369. // causes issue in Opera
  4370. // headEl.removeChild(scriptEl);
  4371. }
  4372. fabric.util.getScript = getScript;
  4373. })();
  4374. fabric.util.getById = getById;
  4375. fabric.util.toArray = toArray;
  4376. fabric.util.makeElement = makeElement;
  4377. fabric.util.addClass = addClass;
  4378. fabric.util.wrapElement = wrapElement;
  4379. fabric.util.getScrollLeftTop = getScrollLeftTop;
  4380. fabric.util.getElementOffset = getElementOffset;
  4381. fabric.util.getElementStyle = getElementStyle;
  4382. })();
  4383. (function(){
  4384. function addParamToUrl(url, param) {
  4385. return url + (/\?/.test(url) ? '&' : '?') + param;
  4386. }
  4387. var makeXHR = (function() {
  4388. var factories = [
  4389. function() { return new ActiveXObject('Microsoft.XMLHTTP'); },
  4390. function() { return new ActiveXObject('Msxml2.XMLHTTP'); },
  4391. function() { return new ActiveXObject('Msxml2.XMLHTTP.3.0'); },
  4392. function() { return new XMLHttpRequest(); }
  4393. ];
  4394. for (var i = factories.length; i--; ) {
  4395. try {
  4396. var req = factories[i]();
  4397. if (req) {
  4398. return factories[i];
  4399. }
  4400. }
  4401. catch (err) { }
  4402. }
  4403. })();
  4404. function emptyFn() { }
  4405. /**
  4406. * Cross-browser abstraction for sending XMLHttpRequest
  4407. * @memberOf fabric.util
  4408. * @param {String} url URL to send XMLHttpRequest to
  4409. * @param {Object} [options] Options object
  4410. * @param {String} [options.method="GET"]
  4411. * @param {Function} options.onComplete Callback to invoke when request is completed
  4412. * @return {XMLHttpRequest} request
  4413. */
  4414. function request(url, options) {
  4415. options || (options = { });
  4416. var method = options.method ? options.method.toUpperCase() : 'GET',
  4417. onComplete = options.onComplete || function() { },
  4418. xhr = makeXHR(),
  4419. body;
  4420. /** @ignore */
  4421. xhr.onreadystatechange = function() {
  4422. if (xhr.readyState === 4) {
  4423. onComplete(xhr);
  4424. xhr.onreadystatechange = emptyFn;
  4425. }
  4426. };
  4427. if (method === 'GET') {
  4428. body = null;
  4429. if (typeof options.parameters === 'string') {
  4430. url = addParamToUrl(url, options.parameters);
  4431. }
  4432. }
  4433. xhr.open(method, url, true);
  4434. if (method === 'POST' || method === 'PUT') {
  4435. xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
  4436. }
  4437. xhr.send(body);
  4438. return xhr;
  4439. }
  4440. fabric.util.request = request;
  4441. })();
  4442. /**
  4443. * Wrapper around `console.log` (when available)
  4444. * @param {Any} [values] Values to log
  4445. */
  4446. fabric.log = function() { };
  4447. /**
  4448. * Wrapper around `console.warn` (when available)
  4449. * @param {Any} [values] Values to log as a warning
  4450. */
  4451. fabric.warn = function() { };
  4452. if (typeof console !== 'undefined') {
  4453. ['log', 'warn'].forEach(function(methodName) {
  4454. if (typeof console[methodName] !== 'undefined' && console[methodName].apply) {
  4455. fabric[methodName] = function() {
  4456. return console[methodName].apply(console, arguments);
  4457. };
  4458. }
  4459. });
  4460. }
  4461. (function(global) {
  4462. 'use strict';
  4463. /**
  4464. * @name fabric
  4465. * @namespace
  4466. */
  4467. var fabric = global.fabric || (global.fabric = { }),
  4468. extend = fabric.util.object.extend,
  4469. capitalize = fabric.util.string.capitalize,
  4470. clone = fabric.util.object.clone,
  4471. toFixed = fabric.util.toFixed,
  4472. parseUnit = fabric.util.parseUnit,
  4473. multiplyTransformMatrices = fabric.util.multiplyTransformMatrices,
  4474. attributesMap = {
  4475. cx: 'left',
  4476. x: 'left',
  4477. r: 'radius',
  4478. cy: 'top',
  4479. y: 'top',
  4480. display: 'visible',
  4481. visibility: 'visible',
  4482. transform: 'transformMatrix',
  4483. 'fill-opacity': 'fillOpacity',
  4484. 'fill-rule': 'fillRule',
  4485. 'font-family': 'fontFamily',
  4486. 'font-size': 'fontSize',
  4487. 'font-style': 'fontStyle',
  4488. 'font-weight': 'fontWeight',
  4489. 'stroke-dasharray': 'strokeDashArray',
  4490. 'stroke-linecap': 'strokeLineCap',
  4491. 'stroke-linejoin': 'strokeLineJoin',
  4492. 'stroke-miterlimit': 'strokeMiterLimit',
  4493. 'stroke-opacity': 'strokeOpacity',
  4494. 'stroke-width': 'strokeWidth',
  4495. 'text-decoration': 'textDecoration',
  4496. 'text-anchor': 'originX'
  4497. },
  4498. colorAttributes = {
  4499. stroke: 'strokeOpacity',
  4500. fill: 'fillOpacity'
  4501. };
  4502. function normalizeAttr(attr) {
  4503. // transform attribute names
  4504. if (attr in attributesMap) {
  4505. return attributesMap[attr];
  4506. }
  4507. return attr;
  4508. }
  4509. function normalizeValue(attr, value, parentAttributes) {
  4510. var isArray = Object.prototype.toString.call(value) === '[object Array]',
  4511. parsed;
  4512. if ((attr === 'fill' || attr === 'stroke') && value === 'none') {
  4513. value = '';
  4514. }
  4515. else if (attr === 'fillRule') {
  4516. value = (value === 'evenodd') ? 'destination-over' : value;
  4517. }
  4518. else if (attr === 'strokeDashArray') {
  4519. value = value.replace(/,/g, ' ').split(/\s+/).map(function(n) {
  4520. return parseInt(n);
  4521. });
  4522. }
  4523. else if (attr === 'transformMatrix') {
  4524. if (parentAttributes && parentAttributes.transformMatrix) {
  4525. value = multiplyTransformMatrices(
  4526. parentAttributes.transformMatrix, fabric.parseTransformAttribute(value));
  4527. }
  4528. else {
  4529. value = fabric.parseTransformAttribute(value);
  4530. }
  4531. }
  4532. else if (attr === 'visible') {
  4533. value = (value === 'none' || value === 'hidden') ? false : true;
  4534. // display=none on parent element always takes precedence over child element
  4535. if (parentAttributes && parentAttributes.visible === false) {
  4536. value = false;
  4537. }
  4538. }
  4539. else if (attr === 'originX' /* text-anchor */) {
  4540. value = value === 'start' ? 'left' : value === 'end' ? 'right' : 'center';
  4541. }
  4542. else {
  4543. parsed = isArray ? value.map(parseUnit) : parseUnit(value);
  4544. }
  4545. return (!isArray && isNaN(parsed) ? value : parsed);
  4546. }
  4547. /**
  4548. * @private
  4549. * @param {Object} attributes Array of attributes to parse
  4550. */
  4551. function _setStrokeFillOpacity(attributes) {
  4552. for (var attr in colorAttributes) {
  4553. if (!attributes[attr] || typeof attributes[colorAttributes[attr]] === 'undefined') {
  4554. continue;
  4555. }
  4556. if (attributes[attr].indexOf('url(') === 0) {
  4557. continue;
  4558. }
  4559. var color = new fabric.Color(attributes[attr]);
  4560. attributes[attr] = color.setAlpha(toFixed(color.getAlpha() * attributes[colorAttributes[attr]], 2)).toRgba();
  4561. }
  4562. return attributes;
  4563. }
  4564. /**
  4565. * Parses "transform" attribute, returning an array of values
  4566. * @static
  4567. * @function
  4568. * @memberOf fabric
  4569. * @param {String} attributeValue String containing attribute value
  4570. * @return {Array} Array of 6 elements representing transformation matrix
  4571. */
  4572. fabric.parseTransformAttribute = (function() {
  4573. function rotateMatrix(matrix, args) {
  4574. var angle = args[0];
  4575. matrix[0] = Math.cos(angle);
  4576. matrix[1] = Math.sin(angle);
  4577. matrix[2] = -Math.sin(angle);
  4578. matrix[3] = Math.cos(angle);
  4579. }
  4580. function scaleMatrix(matrix, args) {
  4581. var multiplierX = args[0],
  4582. multiplierY = (args.length === 2) ? args[1] : args[0];
  4583. matrix[0] = multiplierX;
  4584. matrix[3] = multiplierY;
  4585. }
  4586. function skewXMatrix(matrix, args) {
  4587. matrix[2] = args[0];
  4588. }
  4589. function skewYMatrix(matrix, args) {
  4590. matrix[1] = args[0];
  4591. }
  4592. function translateMatrix(matrix, args) {
  4593. matrix[4] = args[0];
  4594. if (args.length === 2) {
  4595. matrix[5] = args[1];
  4596. }
  4597. }
  4598. // identity matrix
  4599. var iMatrix = [
  4600. 1, // a
  4601. 0, // b
  4602. 0, // c
  4603. 1, // d
  4604. 0, // e
  4605. 0 // f
  4606. ],
  4607. // == begin transform regexp
  4608. number = '(?:[-+]?(?:\\d+|\\d*\\.\\d+)(?:e[-+]?\\d+)?)',
  4609. commaWsp = '(?:\\s+,?\\s*|,\\s*)',
  4610. skewX = '(?:(skewX)\\s*\\(\\s*(' + number + ')\\s*\\))',
  4611. skewY = '(?:(skewY)\\s*\\(\\s*(' + number + ')\\s*\\))',
  4612. rotate = '(?:(rotate)\\s*\\(\\s*(' + number + ')(?:' +
  4613. commaWsp + '(' + number + ')' +
  4614. commaWsp + '(' + number + '))?\\s*\\))',
  4615. scale = '(?:(scale)\\s*\\(\\s*(' + number + ')(?:' +
  4616. commaWsp + '(' + number + '))?\\s*\\))',
  4617. translate = '(?:(translate)\\s*\\(\\s*(' + number + ')(?:' +
  4618. commaWsp + '(' + number + '))?\\s*\\))',
  4619. matrix = '(?:(matrix)\\s*\\(\\s*' +
  4620. '(' + number + ')' + commaWsp +
  4621. '(' + number + ')' + commaWsp +
  4622. '(' + number + ')' + commaWsp +
  4623. '(' + number + ')' + commaWsp +
  4624. '(' + number + ')' + commaWsp +
  4625. '(' + number + ')' +
  4626. '\\s*\\))',
  4627. transform = '(?:' +
  4628. matrix + '|' +
  4629. translate + '|' +
  4630. scale + '|' +
  4631. rotate + '|' +
  4632. skewX + '|' +
  4633. skewY +
  4634. ')',
  4635. transforms = '(?:' + transform + '(?:' + commaWsp + transform + ')*' + ')',
  4636. transformList = '^\\s*(?:' + transforms + '?)\\s*$',
  4637. // http://www.w3.org/TR/SVG/coords.html#TransformAttribute
  4638. reTransformList = new RegExp(transformList),
  4639. // == end transform regexp
  4640. reTransform = new RegExp(transform, 'g');
  4641. return function(attributeValue) {
  4642. // start with identity matrix
  4643. var matrix = iMatrix.concat(),
  4644. matrices = [ ];
  4645. // return if no argument was given or
  4646. // an argument does not match transform attribute regexp
  4647. if (!attributeValue || (attributeValue && !reTransformList.test(attributeValue))) {
  4648. return matrix;
  4649. }
  4650. attributeValue.replace(reTransform, function(match) {
  4651. var m = new RegExp(transform).exec(match).filter(function (match) {
  4652. return (match !== '' && match != null);
  4653. }),
  4654. operation = m[1],
  4655. args = m.slice(2).map(parseFloat);
  4656. switch (operation) {
  4657. case 'translate':
  4658. translateMatrix(matrix, args);
  4659. break;
  4660. case 'rotate':
  4661. args[0] = fabric.util.degreesToRadians(args[0]);
  4662. rotateMatrix(matrix, args);
  4663. break;
  4664. case 'scale':
  4665. scaleMatrix(matrix, args);
  4666. break;
  4667. case 'skewX':
  4668. skewXMatrix(matrix, args);
  4669. break;
  4670. case 'skewY':
  4671. skewYMatrix(matrix, args);
  4672. break;
  4673. case 'matrix':
  4674. matrix = args;
  4675. break;
  4676. }
  4677. // snapshot current matrix into matrices array
  4678. matrices.push(matrix.concat());
  4679. // reset
  4680. matrix = iMatrix.concat();
  4681. });
  4682. var combinedMatrix = matrices[0];
  4683. while (matrices.length > 1) {
  4684. matrices.shift();
  4685. combinedMatrix = fabric.util.multiplyTransformMatrices(combinedMatrix, matrices[0]);
  4686. }
  4687. return combinedMatrix;
  4688. };
  4689. })();
  4690. function parseFontDeclaration(value, oStyle) {
  4691. // TODO: support non-px font size
  4692. var match = value.match(/(normal|italic)?\s*(normal|small-caps)?\s*(normal|bold|bolder|lighter|100|200|300|400|500|600|700|800|900)?\s*(\d+)px(?:\/(normal|[\d\.]+))?\s+(.*)/);
  4693. if (!match) {
  4694. return;
  4695. }
  4696. var fontStyle = match[1],
  4697. // font variant is not used
  4698. // fontVariant = match[2],
  4699. fontWeight = match[3],
  4700. fontSize = match[4],
  4701. lineHeight = match[5],
  4702. fontFamily = match[6];
  4703. if (fontStyle) {
  4704. oStyle.fontStyle = fontStyle;
  4705. }
  4706. if (fontWeight) {
  4707. oStyle.fontWeight = isNaN(parseFloat(fontWeight)) ? fontWeight : parseFloat(fontWeight);
  4708. }
  4709. if (fontSize) {
  4710. oStyle.fontSize = parseFloat(fontSize);
  4711. }
  4712. if (fontFamily) {
  4713. oStyle.fontFamily = fontFamily;
  4714. }
  4715. if (lineHeight) {
  4716. oStyle.lineHeight = lineHeight === 'normal' ? 1 : lineHeight;
  4717. }
  4718. }
  4719. /**
  4720. * @private
  4721. */
  4722. function parseStyleString(style, oStyle) {
  4723. var attr, value;
  4724. style.replace(/;$/, '').split(';').forEach(function (chunk) {
  4725. var pair = chunk.split(':');
  4726. attr = normalizeAttr(pair[0].trim().toLowerCase());
  4727. value = normalizeValue(attr, pair[1].trim());
  4728. if (attr === 'font') {
  4729. parseFontDeclaration(value, oStyle);
  4730. }
  4731. else {
  4732. oStyle[attr] = value;
  4733. }
  4734. });
  4735. }
  4736. /**
  4737. * @private
  4738. */
  4739. function parseStyleObject(style, oStyle) {
  4740. var attr, value;
  4741. for (var prop in style) {
  4742. if (typeof style[prop] === 'undefined') {
  4743. continue;
  4744. }
  4745. attr = normalizeAttr(prop.toLowerCase());
  4746. value = normalizeValue(attr, style[prop]);
  4747. if (attr === 'font') {
  4748. parseFontDeclaration(value, oStyle);
  4749. }
  4750. else {
  4751. oStyle[attr] = value;
  4752. }
  4753. }
  4754. }
  4755. /**
  4756. * @private
  4757. */
  4758. function getGlobalStylesForElement(element) {
  4759. var styles = { };
  4760. for (var rule in fabric.cssRules) {
  4761. if (elementMatchesRule(element, rule.split(' '))) {
  4762. for (var property in fabric.cssRules[rule]) {
  4763. styles[property] = fabric.cssRules[rule][property];
  4764. }
  4765. }
  4766. }
  4767. return styles;
  4768. }
  4769. /**
  4770. * @private
  4771. */
  4772. function elementMatchesRule(element, selectors) {
  4773. var firstMatching, parentMatching = true;
  4774. //start from rightmost selector.
  4775. firstMatching = selectorMatches(element, selectors.pop());
  4776. if (firstMatching && selectors.length) {
  4777. parentMatching = doesSomeParentMatch(element, selectors);
  4778. }
  4779. return firstMatching && parentMatching && (selectors.length === 0);
  4780. }
  4781. function doesSomeParentMatch(element, selectors) {
  4782. var selector, parentMatching = true;
  4783. while (element.parentNode && element.parentNode.nodeType === 1 && selectors.length) {
  4784. if (parentMatching) {
  4785. selector = selectors.pop();
  4786. }
  4787. element = element.parentNode;
  4788. parentMatching = selectorMatches(element, selector);
  4789. }
  4790. return selectors.length === 0;
  4791. }
  4792. /**
  4793. * @private
  4794. */
  4795. function selectorMatches(element, selector) {
  4796. var nodeName = element.nodeName,
  4797. classNames = element.getAttribute('class'),
  4798. id = element.getAttribute('id'), matcher;
  4799. // i check if a selector matches slicing away part from it.
  4800. // if i get empty string i should match
  4801. matcher = new RegExp('^' + nodeName, 'i');
  4802. selector = selector.replace(matcher, '');
  4803. if (id && selector.length) {
  4804. matcher = new RegExp('#' + id + '(?![a-zA-Z\\-]+)', 'i');
  4805. selector = selector.replace(matcher, '');
  4806. }
  4807. if (classNames && selector.length) {
  4808. classNames = classNames.split(' ');
  4809. for (var i = classNames.length; i--;) {
  4810. matcher = new RegExp('\\.' + classNames[i] + '(?![a-zA-Z\\-]+)', 'i');
  4811. selector = selector.replace(matcher, '');
  4812. }
  4813. }
  4814. return selector.length === 0;
  4815. }
  4816. /**
  4817. * @private
  4818. */
  4819. function parseUseDirectives(doc) {
  4820. var nodelist = doc.getElementsByTagName('use');
  4821. while (nodelist.length) {
  4822. var el = nodelist[0],
  4823. xlink = el.getAttribute('xlink:href').substr(1),
  4824. x = el.getAttribute('x') || 0,
  4825. y = el.getAttribute('y') || 0,
  4826. el2 = doc.getElementById(xlink).cloneNode(true),
  4827. currentTrans = (el.getAttribute('transform') || '') + ' translate(' + x + ', ' + y + ')',
  4828. parentNode;
  4829. for (var j = 0, attrs = el.attributes, l = attrs.length; j < l; j++) {
  4830. var attr = attrs.item(j);
  4831. if (attr.nodeName === 'x' || attr.nodeName === 'y' || attr.nodeName === 'xlink:href') {
  4832. continue;
  4833. }
  4834. if (attr.nodeName === 'transform') {
  4835. currentTrans = currentTrans + ' ' + attr.nodeValue;
  4836. }
  4837. else {
  4838. el2.setAttribute(attr.nodeName, attr.nodeValue);
  4839. }
  4840. }
  4841. el2.setAttribute('transform', currentTrans);
  4842. el2.removeAttribute('id');
  4843. parentNode = el.parentNode;
  4844. parentNode.replaceChild(el2, el);
  4845. }
  4846. }
  4847. /**
  4848. * Add a <g> element that envelop all SCG elements and makes the viewbox transformMatrix descend on all elements
  4849. */
  4850. function addSvgTransform(doc, matrix) {
  4851. matrix[3] = matrix[0] = (matrix[0] > matrix[3] ? matrix[3] : matrix[0]);
  4852. if (!(matrix[0] !== 1 || matrix[3] !== 1 || matrix[4] !== 0 || matrix[5] !== 0)) {
  4853. return;
  4854. }
  4855. // default is to preserve aspect ratio
  4856. // preserveAspectRatio attribute to be implemented
  4857. var el = doc.ownerDocument.createElement('g');
  4858. while (doc.firstChild != null) {
  4859. el.appendChild(doc.firstChild);
  4860. }
  4861. el.setAttribute('transform','matrix(' + matrix[0] + ' ' + matrix[1] + ' ' + matrix[2] + ' ' + matrix[3] + ' ' + matrix[4] + ' ' + matrix[5] + ')');
  4862. doc.appendChild(el);
  4863. }
  4864. /**
  4865. * Parses an SVG document, converts it to an array of corresponding fabric.* instances and passes them to a callback
  4866. * @static
  4867. * @function
  4868. * @memberOf fabric
  4869. * @param {SVGDocument} doc SVG document to parse
  4870. * @param {Function} callback Callback to call when parsing is finished; It's being passed an array of elements (parsed from a document).
  4871. * @param {Function} [reviver] Method for further parsing of SVG elements, called after each fabric object created.
  4872. */
  4873. fabric.parseSVGDocument = (function() {
  4874. var reAllowedSVGTagNames = /^(path|circle|polygon|polyline|ellipse|rect|line|image|text)$/,
  4875. // http://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute
  4876. // \d doesn't quite cut it (as we need to match an actual float number)
  4877. // matches, e.g.: +14.56e-12, etc.
  4878. reNum = '(?:[-+]?(?:\\d+|\\d*\\.\\d+)(?:e[-+]?\\d+)?)',
  4879. reViewBoxAttrValue = new RegExp(
  4880. '^' +
  4881. '\\s*(' + reNum + '+)\\s*,?' +
  4882. '\\s*(' + reNum + '+)\\s*,?' +
  4883. '\\s*(' + reNum + '+)\\s*,?' +
  4884. '\\s*(' + reNum + '+)\\s*' +
  4885. '$'
  4886. );
  4887. function hasAncestorWithNodeName(element, nodeName) {
  4888. while (element && (element = element.parentNode)) {
  4889. if (nodeName.test(element.nodeName)) {
  4890. return true;
  4891. }
  4892. }
  4893. return false;
  4894. }
  4895. return function(doc, callback, reviver) {
  4896. if (!doc) {
  4897. return;
  4898. }
  4899. var startTime = new Date();
  4900. parseUseDirectives(doc);
  4901. /* http://www.w3.org/TR/SVG/struct.html#SVGElementWidthAttribute
  4902. * as per spec, width and height attributes are to be considered
  4903. * 100% if no value is specified.
  4904. */
  4905. var viewBoxAttr = doc.getAttribute('viewBox'),
  4906. widthAttr = parseUnit(doc.getAttribute('width') || '100%'),
  4907. heightAttr = parseUnit(doc.getAttribute('height') || '100%'),
  4908. viewBoxWidth,
  4909. viewBoxHeight;
  4910. if (viewBoxAttr && (viewBoxAttr = viewBoxAttr.match(reViewBoxAttrValue))) {
  4911. var minX = parseFloat(viewBoxAttr[1]),
  4912. minY = parseFloat(viewBoxAttr[2]),
  4913. scaleX = 1, scaleY = 1;
  4914. viewBoxWidth = parseFloat(viewBoxAttr[3]);
  4915. viewBoxHeight = parseFloat(viewBoxAttr[4]);
  4916. if (widthAttr && widthAttr !== viewBoxWidth ) {
  4917. scaleX = widthAttr / viewBoxWidth;
  4918. }
  4919. if (heightAttr && heightAttr !== viewBoxHeight) {
  4920. scaleY = heightAttr / viewBoxHeight;
  4921. }
  4922. addSvgTransform(doc, [scaleX, 0, 0, scaleY, scaleX * -minX, scaleY * -minY]);
  4923. }
  4924. var descendants = fabric.util.toArray(doc.getElementsByTagName('*'));
  4925. if (descendants.length === 0 && fabric.isLikelyNode) {
  4926. // we're likely in node, where "o3-xml" library fails to gEBTN("*")
  4927. // https://github.com/ajaxorg/node-o3-xml/issues/21
  4928. descendants = doc.selectNodes('//*[name(.)!="svg"]');
  4929. var arr = [ ];
  4930. for (var i = 0, len = descendants.length; i < len; i++) {
  4931. arr[i] = descendants[i];
  4932. }
  4933. descendants = arr;
  4934. }
  4935. var elements = descendants.filter(function(el) {
  4936. return reAllowedSVGTagNames.test(el.tagName) &&
  4937. !hasAncestorWithNodeName(el, /^(?:pattern|defs)$/); // http://www.w3.org/TR/SVG/struct.html#DefsElement
  4938. });
  4939. if (!elements || (elements && !elements.length)) {
  4940. callback && callback([], {});
  4941. return;
  4942. }
  4943. var options = {
  4944. width: widthAttr ? widthAttr : viewBoxWidth,
  4945. height: heightAttr ? heightAttr : viewBoxHeight,
  4946. widthAttr: widthAttr,
  4947. heightAttr: heightAttr
  4948. };
  4949. fabric.gradientDefs = fabric.getGradientDefs(doc);
  4950. fabric.cssRules = fabric.getCSSRules(doc);
  4951. // Precedence of rules: style > class > attribute
  4952. fabric.parseElements(elements, function(instances) {
  4953. fabric.documentParsingTime = new Date() - startTime;
  4954. if (callback) {
  4955. callback(instances, options);
  4956. }
  4957. }, clone(options), reviver);
  4958. };
  4959. })();
  4960. /**
  4961. * Used for caching SVG documents (loaded via `fabric.Canvas#loadSVGFromURL`)
  4962. * @namespace
  4963. */
  4964. var svgCache = {
  4965. /**
  4966. * @param {String} name
  4967. * @param {Function} callback
  4968. */
  4969. has: function (name, callback) {
  4970. callback(false);
  4971. },
  4972. get: function () {
  4973. /* NOOP */
  4974. },
  4975. set: function () {
  4976. /* NOOP */
  4977. }
  4978. };
  4979. /**
  4980. * @private
  4981. */
  4982. function _enlivenCachedObject(cachedObject) {
  4983. var objects = cachedObject.objects,
  4984. options = cachedObject.options;
  4985. objects = objects.map(function (o) {
  4986. return fabric[capitalize(o.type)].fromObject(o);
  4987. });
  4988. return ({ objects: objects, options: options });
  4989. }
  4990. /**
  4991. * @private
  4992. */
  4993. function _createSVGPattern(markup, canvas, property) {
  4994. if (canvas[property] && canvas[property].toSVG) {
  4995. markup.push(
  4996. '<pattern x="0" y="0" id="', property, 'Pattern" ',
  4997. 'width="', canvas[property].source.width,
  4998. '" height="', canvas[property].source.height,
  4999. '" patternUnits="userSpaceOnUse">',
  5000. '<image x="0" y="0" ',
  5001. 'width="', canvas[property].source.width,
  5002. '" height="', canvas[property].source.height,
  5003. '" xlink:href="', canvas[property].source.src,
  5004. '"></image></pattern>'
  5005. );
  5006. }
  5007. }
  5008. extend(fabric, {
  5009. /**
  5010. * Parses an SVG document, returning all of the gradient declarations found in it
  5011. * @static
  5012. * @function
  5013. * @memberOf fabric
  5014. * @param {SVGDocument} doc SVG document to parse
  5015. * @return {Object} Gradient definitions; key corresponds to element id, value -- to gradient definition element
  5016. */
  5017. getGradientDefs: function(doc) {
  5018. var linearGradientEls = doc.getElementsByTagName('linearGradient'),
  5019. radialGradientEls = doc.getElementsByTagName('radialGradient'),
  5020. el, i, j = 0, id, xlink, elList = [ ],
  5021. gradientDefs = { }, idsToXlinkMap = { };
  5022. elList.length = linearGradientEls.length + radialGradientEls.length;
  5023. i = linearGradientEls.length;
  5024. while (i--) {
  5025. elList[j++] = linearGradientEls[i];
  5026. }
  5027. i = radialGradientEls.length;
  5028. while (i--) {
  5029. elList[j++] = radialGradientEls[i];
  5030. }
  5031. while (j--) {
  5032. el = elList[j];
  5033. xlink = el.getAttribute('xlink:href');
  5034. id = el.getAttribute('id');
  5035. if (xlink) {
  5036. idsToXlinkMap[id] = xlink.substr(1);
  5037. }
  5038. gradientDefs[id] = el;
  5039. }
  5040. for (id in idsToXlinkMap) {
  5041. var el2 = gradientDefs[idsToXlinkMap[id]].cloneNode(true);
  5042. el = gradientDefs[id];
  5043. while (el2.firstChild) {
  5044. el.appendChild(el2.firstChild);
  5045. }
  5046. }
  5047. return gradientDefs;
  5048. },
  5049. /**
  5050. * Returns an object of attributes' name/value, given element and an array of attribute names;
  5051. * Parses parent "g" nodes recursively upwards.
  5052. * @static
  5053. * @memberOf fabric
  5054. * @param {DOMElement} element Element to parse
  5055. * @param {Array} attributes Array of attributes to parse
  5056. * @return {Object} object containing parsed attributes' names/values
  5057. */
  5058. parseAttributes: function(element, attributes) {
  5059. if (!element) {
  5060. return;
  5061. }
  5062. var value,
  5063. parentAttributes = { };
  5064. // if there's a parent container (`g` or `a` or `symbol` node), parse its attributes recursively upwards
  5065. if (element.parentNode && /^symbol|[g|a]$/i.test(element.parentNode.nodeName)) {
  5066. parentAttributes = fabric.parseAttributes(element.parentNode, attributes);
  5067. }
  5068. var ownAttributes = attributes.reduce(function(memo, attr) {
  5069. value = element.getAttribute(attr);
  5070. if (value) {
  5071. attr = normalizeAttr(attr);
  5072. value = normalizeValue(attr, value, parentAttributes);
  5073. memo[attr] = value;
  5074. }
  5075. return memo;
  5076. }, { });
  5077. // add values parsed from style, which take precedence over attributes
  5078. // (see: http://www.w3.org/TR/SVG/styling.html#UsingPresentationAttributes)
  5079. ownAttributes = extend(ownAttributes,
  5080. extend(getGlobalStylesForElement(element), fabric.parseStyleAttribute(element)));
  5081. return _setStrokeFillOpacity(extend(parentAttributes, ownAttributes));
  5082. },
  5083. /**
  5084. * Transforms an array of svg elements to corresponding fabric.* instances
  5085. * @static
  5086. * @memberOf fabric
  5087. * @param {Array} elements Array of elements to parse
  5088. * @param {Function} callback Being passed an array of fabric instances (transformed from SVG elements)
  5089. * @param {Object} [options] Options object
  5090. * @param {Function} [reviver] Method for further parsing of SVG elements, called after each fabric object created.
  5091. */
  5092. parseElements: function(elements, callback, options, reviver) {
  5093. new fabric.ElementsParser(elements, callback, options, reviver).parse();
  5094. },
  5095. /**
  5096. * Parses "style" attribute, retuning an object with values
  5097. * @static
  5098. * @memberOf fabric
  5099. * @param {SVGElement} element Element to parse
  5100. * @return {Object} Objects with values parsed from style attribute of an element
  5101. */
  5102. parseStyleAttribute: function(element) {
  5103. var oStyle = { },
  5104. style = element.getAttribute('style');
  5105. if (!style) {
  5106. return oStyle;
  5107. }
  5108. if (typeof style === 'string') {
  5109. parseStyleString(style, oStyle);
  5110. }
  5111. else {
  5112. parseStyleObject(style, oStyle);
  5113. }
  5114. return oStyle;
  5115. },
  5116. /**
  5117. * Parses "points" attribute, returning an array of values
  5118. * @static
  5119. * @memberOf fabric
  5120. * @param {String} points points attribute string
  5121. * @return {Array} array of points
  5122. */
  5123. parsePointsAttribute: function(points) {
  5124. // points attribute is required and must not be empty
  5125. if (!points) {
  5126. return null;
  5127. }
  5128. // replace commas with whitespace and remove bookending whitespace
  5129. points = points.replace(/,/g, ' ').trim();
  5130. points = points.split(/\s+/);
  5131. var parsedPoints = [ ], i, len;
  5132. i = 0;
  5133. len = points.length;
  5134. for (; i < len; i+=2) {
  5135. parsedPoints.push({
  5136. x: parseFloat(points[i]),
  5137. y: parseFloat(points[i + 1])
  5138. });
  5139. }
  5140. // odd number of points is an error
  5141. // if (parsedPoints.length % 2 !== 0) {
  5142. // return null;
  5143. // }
  5144. return parsedPoints;
  5145. },
  5146. /**
  5147. * Returns CSS rules for a given SVG document
  5148. * @static
  5149. * @function
  5150. * @memberOf fabric
  5151. * @param {SVGDocument} doc SVG document to parse
  5152. * @return {Object} CSS rules of this document
  5153. */
  5154. getCSSRules: function(doc) {
  5155. var styles = doc.getElementsByTagName('style'),
  5156. allRules = { }, rules;
  5157. // very crude parsing of style contents
  5158. for (var i = 0, len = styles.length; i < len; i++) {
  5159. var styleContents = styles[0].textContent;
  5160. // remove comments
  5161. styleContents = styleContents.replace(/\/\*[\s\S]*?\*\//g, '');
  5162. rules = styleContents.match(/[^{]*\{[\s\S]*?\}/g);
  5163. rules = rules.map(function(rule) { return rule.trim(); });
  5164. rules.forEach(function(rule) {
  5165. var match = rule.match(/([\s\S]*?)\s*\{([^}]*)\}/),
  5166. ruleObj = { }, declaration = match[2].trim(),
  5167. propertyValuePairs = declaration.replace(/;$/, '').split(/\s*;\s*/);
  5168. for (var i = 0, len = propertyValuePairs.length; i < len; i++) {
  5169. var pair = propertyValuePairs[i].split(/\s*:\s*/),
  5170. property = normalizeAttr(pair[0]),
  5171. value = normalizeValue(property,pair[1],pair[0]);
  5172. ruleObj[property] = value;
  5173. }
  5174. rule = match[1];
  5175. rule.split(',').forEach(function(_rule) {
  5176. allRules[_rule.trim()] = fabric.util.object.clone(ruleObj);
  5177. });
  5178. });
  5179. }
  5180. return allRules;
  5181. },
  5182. /**
  5183. * Takes url corresponding to an SVG document, and parses it into a set of fabric objects. Note that SVG is fetched via XMLHttpRequest, so it needs to conform to SOP (Same Origin Policy)
  5184. * @memberof fabric
  5185. * @param {String} url
  5186. * @param {Function} callback
  5187. * @param {Function} [reviver] Method for further parsing of SVG elements, called after each fabric object created.
  5188. */
  5189. loadSVGFromURL: function(url, callback, reviver) {
  5190. url = url.replace(/^\n\s*/, '').trim();
  5191. svgCache.has(url, function (hasUrl) {
  5192. if (hasUrl) {
  5193. svgCache.get(url, function (value) {
  5194. var enlivedRecord = _enlivenCachedObject(value);
  5195. callback(enlivedRecord.objects, enlivedRecord.options);
  5196. });
  5197. }
  5198. else {
  5199. new fabric.util.request(url, {
  5200. method: 'get',
  5201. onComplete: onComplete
  5202. });
  5203. }
  5204. });
  5205. function onComplete(r) {
  5206. var xml = r.responseXML;
  5207. if (xml && !xml.documentElement && fabric.window.ActiveXObject && r.responseText) {
  5208. xml = new ActiveXObject('Microsoft.XMLDOM');
  5209. xml.async = 'false';
  5210. //IE chokes on DOCTYPE
  5211. xml.loadXML(r.responseText.replace(/<!DOCTYPE[\s\S]*?(\[[\s\S]*\])*?>/i,''));
  5212. }
  5213. if (!xml || !xml.documentElement) {
  5214. return;
  5215. }
  5216. fabric.parseSVGDocument(xml.documentElement, function (results, options) {
  5217. svgCache.set(url, {
  5218. objects: fabric.util.array.invoke(results, 'toObject'),
  5219. options: options
  5220. });
  5221. callback(results, options);
  5222. }, reviver);
  5223. }
  5224. },
  5225. /**
  5226. * Takes string corresponding to an SVG document, and parses it into a set of fabric objects
  5227. * @memberof fabric
  5228. * @param {String} string
  5229. * @param {Function} callback
  5230. * @param {Function} [reviver] Method for further parsing of SVG elements, called after each fabric object created.
  5231. */
  5232. loadSVGFromString: function(string, callback, reviver) {
  5233. string = string.trim();
  5234. var doc;
  5235. if (typeof DOMParser !== 'undefined') {
  5236. var parser = new DOMParser();
  5237. if (parser && parser.parseFromString) {
  5238. doc = parser.parseFromString(string, 'text/xml');
  5239. }
  5240. }
  5241. else if (fabric.window.ActiveXObject) {
  5242. doc = new ActiveXObject('Microsoft.XMLDOM');
  5243. doc.async = 'false';
  5244. //IE chokes on DOCTYPE
  5245. doc.loadXML(string.replace(/<!DOCTYPE[\s\S]*?(\[[\s\S]*\])*?>/i,''));
  5246. }
  5247. fabric.parseSVGDocument(doc.documentElement, function (results, options) {
  5248. callback(results, options);
  5249. }, reviver);
  5250. },
  5251. /**
  5252. * Creates markup containing SVG font faces
  5253. * @param {Array} objects Array of fabric objects
  5254. * @return {String}
  5255. */
  5256. createSVGFontFacesMarkup: function(objects) {
  5257. var markup = '';
  5258. for (var i = 0, len = objects.length; i < len; i++) {
  5259. if (objects[i].type !== 'text' || !objects[i].path) {
  5260. continue;
  5261. }
  5262. markup += [
  5263. //jscs:disable validateIndentation
  5264. '@font-face {',
  5265. 'font-family: ', objects[i].fontFamily, '; ',
  5266. 'src: url(\'', objects[i].path, '\')',
  5267. '}'
  5268. //jscs:enable validateIndentation
  5269. ].join('');
  5270. }
  5271. if (markup) {
  5272. markup = [
  5273. //jscs:disable validateIndentation
  5274. '<style type="text/css">',
  5275. '<![CDATA[',
  5276. markup,
  5277. ']]>',
  5278. '</style>'
  5279. //jscs:enable validateIndentation
  5280. ].join('');
  5281. }
  5282. return markup;
  5283. },
  5284. /**
  5285. * Creates markup containing SVG referenced elements like patterns, gradients etc.
  5286. * @param {fabric.Canvas} canvas instance of fabric.Canvas
  5287. * @return {String}
  5288. */
  5289. createSVGRefElementsMarkup: function(canvas) {
  5290. var markup = [ ];
  5291. _createSVGPattern(markup, canvas, 'backgroundColor');
  5292. _createSVGPattern(markup, canvas, 'overlayColor');
  5293. return markup.join('');
  5294. }
  5295. });
  5296. })(typeof exports !== 'undefined' ? exports : this);
  5297. fabric.ElementsParser = function(elements, callback, options, reviver) {
  5298. this.elements = elements;
  5299. this.callback = callback;
  5300. this.options = options;
  5301. this.reviver = reviver;
  5302. };
  5303. fabric.ElementsParser.prototype.parse = function() {
  5304. this.instances = new Array(this.elements.length);
  5305. this.numElements = this.elements.length;
  5306. this.createObjects();
  5307. };
  5308. fabric.ElementsParser.prototype.createObjects = function() {
  5309. for (var i = 0, len = this.elements.length; i < len; i++) {
  5310. (function(_this, i) {
  5311. setTimeout(function() {
  5312. _this.createObject(_this.elements[i], i);
  5313. }, 0);
  5314. })(this, i);
  5315. }
  5316. };
  5317. fabric.ElementsParser.prototype.createObject = function(el, index) {
  5318. var klass = fabric[fabric.util.string.capitalize(el.tagName)];
  5319. if (klass && klass.fromElement) {
  5320. try {
  5321. this._createObject(klass, el, index);
  5322. }
  5323. catch (err) {
  5324. fabric.log(err);
  5325. }
  5326. }
  5327. else {
  5328. this.checkIfDone();
  5329. }
  5330. };
  5331. fabric.ElementsParser.prototype._createObject = function(klass, el, index) {
  5332. if (klass.async) {
  5333. klass.fromElement(el, this.createCallback(index, el), this.options);
  5334. }
  5335. else {
  5336. var obj = klass.fromElement(el, this.options);
  5337. this.resolveGradient(obj, 'fill');
  5338. this.resolveGradient(obj, 'stroke');
  5339. this.reviver && this.reviver(el, obj);
  5340. this.instances[index] = obj;
  5341. this.checkIfDone();
  5342. }
  5343. };
  5344. fabric.ElementsParser.prototype.createCallback = function(index, el) {
  5345. var _this = this;
  5346. return function(obj) {
  5347. _this.resolveGradient(obj, 'fill');
  5348. _this.resolveGradient(obj, 'stroke');
  5349. _this.reviver && _this.reviver(el, obj);
  5350. _this.instances[index] = obj;
  5351. _this.checkIfDone();
  5352. };
  5353. };
  5354. fabric.ElementsParser.prototype.resolveGradient = function(obj, property) {
  5355. var instanceFillValue = obj.get(property);
  5356. if (!(/^url\(/).test(instanceFillValue)) {
  5357. return;
  5358. }
  5359. var gradientId = instanceFillValue.slice(5, instanceFillValue.length - 1);
  5360. if (fabric.gradientDefs[gradientId]) {
  5361. obj.set(property,
  5362. fabric.Gradient.fromElement(fabric.gradientDefs[gradientId], obj));
  5363. }
  5364. };
  5365. fabric.ElementsParser.prototype.checkIfDone = function() {
  5366. if (--this.numElements === 0) {
  5367. this.instances = this.instances.filter(function(el) {
  5368. return el != null;
  5369. });
  5370. this.callback(this.instances);
  5371. }
  5372. };
  5373. (function(global) {
  5374. 'use strict';
  5375. /* Adaptation of work of Kevin Lindsey (kevin@kevlindev.com) */
  5376. var fabric = global.fabric || (global.fabric = { });
  5377. if (fabric.Point) {
  5378. fabric.warn('fabric.Point is already defined');
  5379. return;
  5380. }
  5381. fabric.Point = Point;
  5382. /**
  5383. * Point class
  5384. * @class fabric.Point
  5385. * @memberOf fabric
  5386. * @constructor
  5387. * @param {Number} x
  5388. * @param {Number} y
  5389. * @return {fabric.Point} thisArg
  5390. */
  5391. function Point(x, y) {
  5392. this.x = x;
  5393. this.y = y;
  5394. }
  5395. Point.prototype = /** @lends fabric.Point.prototype */ {
  5396. constructor: Point,
  5397. /**
  5398. * Adds another point to this one and returns another one
  5399. * @param {fabric.Point} that
  5400. * @return {fabric.Point} new Point instance with added values
  5401. */
  5402. add: function (that) {
  5403. return new Point(this.x + that.x, this.y + that.y);
  5404. },
  5405. /**
  5406. * Adds another point to this one
  5407. * @param {fabric.Point} that
  5408. * @return {fabric.Point} thisArg
  5409. */
  5410. addEquals: function (that) {
  5411. this.x += that.x;
  5412. this.y += that.y;
  5413. return this;
  5414. },
  5415. /**
  5416. * Adds value to this point and returns a new one
  5417. * @param {Number} scalar
  5418. * @return {fabric.Point} new Point with added value
  5419. */
  5420. scalarAdd: function (scalar) {
  5421. return new Point(this.x + scalar, this.y + scalar);
  5422. },
  5423. /**
  5424. * Adds value to this point
  5425. * @param {Number} scalar
  5426. * @return {fabric.Point} thisArg
  5427. */
  5428. scalarAddEquals: function (scalar) {
  5429. this.x += scalar;
  5430. this.y += scalar;
  5431. return this;
  5432. },
  5433. /**
  5434. * Subtracts another point from this point and returns a new one
  5435. * @param {fabric.Point} that
  5436. * @return {fabric.Point} new Point object with subtracted values
  5437. */
  5438. subtract: function (that) {
  5439. return new Point(this.x - that.x, this.y - that.y);
  5440. },
  5441. /**
  5442. * Subtracts another point from this point
  5443. * @param {fabric.Point} that
  5444. * @return {fabric.Point} thisArg
  5445. */
  5446. subtractEquals: function (that) {
  5447. this.x -= that.x;
  5448. this.y -= that.y;
  5449. return this;
  5450. },
  5451. /**
  5452. * Subtracts value from this point and returns a new one
  5453. * @param {Number} scalar
  5454. * @return {fabric.Point}
  5455. */
  5456. scalarSubtract: function (scalar) {
  5457. return new Point(this.x - scalar, this.y - scalar);
  5458. },
  5459. /**
  5460. * Subtracts value from this point
  5461. * @param {Number} scalar
  5462. * @return {fabric.Point} thisArg
  5463. */
  5464. scalarSubtractEquals: function (scalar) {
  5465. this.x -= scalar;
  5466. this.y -= scalar;
  5467. return this;
  5468. },
  5469. /**
  5470. * Miltiplies this point by a value and returns a new one
  5471. * @param {Number} scalar
  5472. * @return {fabric.Point}
  5473. */
  5474. multiply: function (scalar) {
  5475. return new Point(this.x * scalar, this.y * scalar);
  5476. },
  5477. /**
  5478. * Miltiplies this point by a value
  5479. * @param {Number} scalar
  5480. * @return {fabric.Point} thisArg
  5481. */
  5482. multiplyEquals: function (scalar) {
  5483. this.x *= scalar;
  5484. this.y *= scalar;
  5485. return this;
  5486. },
  5487. /**
  5488. * Divides this point by a value and returns a new one
  5489. * @param {Number} scalar
  5490. * @return {fabric.Point}
  5491. */
  5492. divide: function (scalar) {
  5493. return new Point(this.x / scalar, this.y / scalar);
  5494. },
  5495. /**
  5496. * Divides this point by a value
  5497. * @param {Number} scalar
  5498. * @return {fabric.Point} thisArg
  5499. */
  5500. divideEquals: function (scalar) {
  5501. this.x /= scalar;
  5502. this.y /= scalar;
  5503. return this;
  5504. },
  5505. /**
  5506. * Returns true if this point is equal to another one
  5507. * @param {fabric.Point} that
  5508. * @return {Boolean}
  5509. */
  5510. eq: function (that) {
  5511. return (this.x === that.x && this.y === that.y);
  5512. },
  5513. /**
  5514. * Returns true if this point is less than another one
  5515. * @param {fabric.Point} that
  5516. * @return {Boolean}
  5517. */
  5518. lt: function (that) {
  5519. return (this.x < that.x && this.y < that.y);
  5520. },
  5521. /**
  5522. * Returns true if this point is less than or equal to another one
  5523. * @param {fabric.Point} that
  5524. * @return {Boolean}
  5525. */
  5526. lte: function (that) {
  5527. return (this.x <= that.x && this.y <= that.y);
  5528. },
  5529. /**
  5530. * Returns true if this point is greater another one
  5531. * @param {fabric.Point} that
  5532. * @return {Boolean}
  5533. */
  5534. gt: function (that) {
  5535. return (this.x > that.x && this.y > that.y);
  5536. },
  5537. /**
  5538. * Returns true if this point is greater than or equal to another one
  5539. * @param {fabric.Point} that
  5540. * @return {Boolean}
  5541. */
  5542. gte: function (that) {
  5543. return (this.x >= that.x && this.y >= that.y);
  5544. },
  5545. /**
  5546. * Returns new point which is the result of linear interpolation with this one and another one
  5547. * @param {fabric.Point} that
  5548. * @param {Number} t
  5549. * @return {fabric.Point}
  5550. */
  5551. lerp: function (that, t) {
  5552. return new Point(this.x + (that.x - this.x) * t, this.y + (that.y - this.y) * t);
  5553. },
  5554. /**
  5555. * Returns distance from this point and another one
  5556. * @param {fabric.Point} that
  5557. * @return {Number}
  5558. */
  5559. distanceFrom: function (that) {
  5560. var dx = this.x - that.x,
  5561. dy = this.y - that.y;
  5562. return Math.sqrt(dx * dx + dy * dy);
  5563. },
  5564. /**
  5565. * Returns the point between this point and another one
  5566. * @param {fabric.Point} that
  5567. * @return {fabric.Point}
  5568. */
  5569. midPointFrom: function (that) {
  5570. return new Point(this.x + (that.x - this.x)/2, this.y + (that.y - this.y)/2);
  5571. },
  5572. /**
  5573. * Returns a new point which is the min of this and another one
  5574. * @param {fabric.Point} that
  5575. * @return {fabric.Point}
  5576. */
  5577. min: function (that) {
  5578. return new Point(Math.min(this.x, that.x), Math.min(this.y, that.y));
  5579. },
  5580. /**
  5581. * Returns a new point which is the max of this and another one
  5582. * @param {fabric.Point} that
  5583. * @return {fabric.Point}
  5584. */
  5585. max: function (that) {
  5586. return new Point(Math.max(this.x, that.x), Math.max(this.y, that.y));
  5587. },
  5588. /**
  5589. * Returns string representation of this point
  5590. * @return {String}
  5591. */
  5592. toString: function () {
  5593. return this.x + ',' + this.y;
  5594. },
  5595. /**
  5596. * Sets x/y of this point
  5597. * @param {Number} x
  5598. * @return {Number} y
  5599. */
  5600. setXY: function (x, y) {
  5601. this.x = x;
  5602. this.y = y;
  5603. },
  5604. /**
  5605. * Sets x/y of this point from another point
  5606. * @param {fabric.Point} that
  5607. */
  5608. setFromPoint: function (that) {
  5609. this.x = that.x;
  5610. this.y = that.y;
  5611. },
  5612. /**
  5613. * Swaps x/y of this point and another point
  5614. * @param {fabric.Point} that
  5615. */
  5616. swap: function (that) {
  5617. var x = this.x,
  5618. y = this.y;
  5619. this.x = that.x;
  5620. this.y = that.y;
  5621. that.x = x;
  5622. that.y = y;
  5623. }
  5624. };
  5625. })(typeof exports !== 'undefined' ? exports : this);
  5626. (function(global) {
  5627. 'use strict';
  5628. /* Adaptation of work of Kevin Lindsey (kevin@kevlindev.com) */
  5629. var fabric = global.fabric || (global.fabric = { });
  5630. if (fabric.Intersection) {
  5631. fabric.warn('fabric.Intersection is already defined');
  5632. return;
  5633. }
  5634. /**
  5635. * Intersection class
  5636. * @class fabric.Intersection
  5637. * @memberOf fabric
  5638. * @constructor
  5639. */
  5640. function Intersection(status) {
  5641. this.status = status;
  5642. this.points = [];
  5643. }
  5644. fabric.Intersection = Intersection;
  5645. fabric.Intersection.prototype = /** @lends fabric.Intersection.prototype */ {
  5646. /**
  5647. * Appends a point to intersection
  5648. * @param {fabric.Point} point
  5649. */
  5650. appendPoint: function (point) {
  5651. this.points.push(point);
  5652. },
  5653. /**
  5654. * Appends points to intersection
  5655. * @param {Array} points
  5656. */
  5657. appendPoints: function (points) {
  5658. this.points = this.points.concat(points);
  5659. }
  5660. };
  5661. /**
  5662. * Checks if one line intersects another
  5663. * @static
  5664. * @param {fabric.Point} a1
  5665. * @param {fabric.Point} a2
  5666. * @param {fabric.Point} b1
  5667. * @param {fabric.Point} b2
  5668. * @return {fabric.Intersection}
  5669. */
  5670. fabric.Intersection.intersectLineLine = function (a1, a2, b1, b2) {
  5671. var result,
  5672. uaT = (b2.x - b1.x) * (a1.y - b1.y) - (b2.y - b1.y) * (a1.x - b1.x),
  5673. ubT = (a2.x - a1.x) * (a1.y - b1.y) - (a2.y - a1.y) * (a1.x - b1.x),
  5674. uB = (b2.y - b1.y) * (a2.x - a1.x) - (b2.x - b1.x) * (a2.y - a1.y);
  5675. if (uB !== 0) {
  5676. var ua = uaT / uB,
  5677. ub = ubT / uB;
  5678. if (0 <= ua && ua <= 1 && 0 <= ub && ub <= 1) {
  5679. result = new Intersection('Intersection');
  5680. result.points.push(new fabric.Point(a1.x + ua * (a2.x - a1.x), a1.y + ua * (a2.y - a1.y)));
  5681. }
  5682. else {
  5683. result = new Intersection();
  5684. }
  5685. }
  5686. else {
  5687. if (uaT === 0 || ubT === 0) {
  5688. result = new Intersection('Coincident');
  5689. }
  5690. else {
  5691. result = new Intersection('Parallel');
  5692. }
  5693. }
  5694. return result;
  5695. };
  5696. /**
  5697. * Checks if line intersects polygon
  5698. * @static
  5699. * @param {fabric.Point} a1
  5700. * @param {fabric.Point} a2
  5701. * @param {Array} points
  5702. * @return {fabric.Intersection}
  5703. */
  5704. fabric.Intersection.intersectLinePolygon = function(a1,a2,points){
  5705. var result = new Intersection(),
  5706. length = points.length;
  5707. for (var i = 0; i < length; i++) {
  5708. var b1 = points[i],
  5709. b2 = points[(i + 1) % length],
  5710. inter = Intersection.intersectLineLine(a1, a2, b1, b2);
  5711. result.appendPoints(inter.points);
  5712. }
  5713. if (result.points.length > 0) {
  5714. result.status = 'Intersection';
  5715. }
  5716. return result;
  5717. };
  5718. /**
  5719. * Checks if polygon intersects another polygon
  5720. * @static
  5721. * @param {Array} points1
  5722. * @param {Array} points2
  5723. * @return {fabric.Intersection}
  5724. */
  5725. fabric.Intersection.intersectPolygonPolygon = function (points1, points2) {
  5726. var result = new Intersection(),
  5727. length = points1.length;
  5728. for (var i = 0; i < length; i++) {
  5729. var a1 = points1[i],
  5730. a2 = points1[(i + 1) % length],
  5731. inter = Intersection.intersectLinePolygon(a1, a2, points2);
  5732. result.appendPoints(inter.points);
  5733. }
  5734. if (result.points.length > 0) {
  5735. result.status = 'Intersection';
  5736. }
  5737. return result;
  5738. };
  5739. /**
  5740. * Checks if polygon intersects rectangle
  5741. * @static
  5742. * @param {Array} points
  5743. * @param {Number} r1
  5744. * @param {Number} r2
  5745. * @return {fabric.Intersection}
  5746. */
  5747. fabric.Intersection.intersectPolygonRectangle = function (points, r1, r2) {
  5748. var min = r1.min(r2),
  5749. max = r1.max(r2),
  5750. topRight = new fabric.Point(max.x, min.y),
  5751. bottomLeft = new fabric.Point(min.x, max.y),
  5752. inter1 = Intersection.intersectLinePolygon(min, topRight, points),
  5753. inter2 = Intersection.intersectLinePolygon(topRight, max, points),
  5754. inter3 = Intersection.intersectLinePolygon(max, bottomLeft, points),
  5755. inter4 = Intersection.intersectLinePolygon(bottomLeft, min, points),
  5756. result = new Intersection();
  5757. result.appendPoints(inter1.points);
  5758. result.appendPoints(inter2.points);
  5759. result.appendPoints(inter3.points);
  5760. result.appendPoints(inter4.points);
  5761. if (result.points.length > 0) {
  5762. result.status = 'Intersection';
  5763. }
  5764. return result;
  5765. };
  5766. })(typeof exports !== 'undefined' ? exports : this);
  5767. (function(global) {
  5768. 'use strict';
  5769. var fabric = global.fabric || (global.fabric = { });
  5770. if (fabric.Color) {
  5771. fabric.warn('fabric.Color is already defined.');
  5772. return;
  5773. }
  5774. /**
  5775. * Color class
  5776. * The purpose of {@link fabric.Color} is to abstract and encapsulate common color operations;
  5777. * {@link fabric.Color} is a constructor and creates instances of {@link fabric.Color} objects.
  5778. *
  5779. * @class fabric.Color
  5780. * @param {String} color optional in hex or rgb(a) format
  5781. * @return {fabric.Color} thisArg
  5782. * @tutorial {@link http://fabricjs.com/fabric-intro-part-2/#colors}
  5783. */
  5784. function Color(color) {
  5785. if (!color) {
  5786. this.setSource([0, 0, 0, 1]);
  5787. }
  5788. else {
  5789. this._tryParsingColor(color);
  5790. }
  5791. }
  5792. fabric.Color = Color;
  5793. fabric.Color.prototype = /** @lends fabric.Color.prototype */ {
  5794. /**
  5795. * @private
  5796. * @param {String|Array} color Color value to parse
  5797. */
  5798. _tryParsingColor: function(color) {
  5799. var source;
  5800. if (color in Color.colorNameMap) {
  5801. color = Color.colorNameMap[color];
  5802. }
  5803. if (color === 'transparent') {
  5804. this.setSource([255,255,255,0]);
  5805. return;
  5806. }
  5807. source = Color.sourceFromHex(color);
  5808. if (!source) {
  5809. source = Color.sourceFromRgb(color);
  5810. }
  5811. if (!source) {
  5812. source = Color.sourceFromHsl(color);
  5813. }
  5814. if (source) {
  5815. this.setSource(source);
  5816. }
  5817. },
  5818. /**
  5819. * 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>
  5820. * @private
  5821. * @param {Number} r Red color value
  5822. * @param {Number} g Green color value
  5823. * @param {Number} b Blue color value
  5824. * @return {Array} Hsl color
  5825. */
  5826. _rgbToHsl: function(r, g, b) {
  5827. r /= 255, g /= 255, b /= 255;
  5828. var h, s, l,
  5829. max = fabric.util.array.max([r, g, b]),
  5830. min = fabric.util.array.min([r, g, b]);
  5831. l = (max + min) / 2;
  5832. if (max === min) {
  5833. h = s = 0; // achromatic
  5834. }
  5835. else {
  5836. var d = max - min;
  5837. s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
  5838. switch (max) {
  5839. case r:
  5840. h = (g - b) / d + (g < b ? 6 : 0);
  5841. break;
  5842. case g:
  5843. h = (b - r) / d + 2;
  5844. break;
  5845. case b:
  5846. h = (r - g) / d + 4;
  5847. break;
  5848. }
  5849. h /= 6;
  5850. }
  5851. return [
  5852. Math.round(h * 360),
  5853. Math.round(s * 100),
  5854. Math.round(l * 100)
  5855. ];
  5856. },
  5857. /**
  5858. * Returns source of this color (where source is an array representation; ex: [200, 200, 100, 1])
  5859. * @return {Array}
  5860. */
  5861. getSource: function() {
  5862. return this._source;
  5863. },
  5864. /**
  5865. * Sets source of this color (where source is an array representation; ex: [200, 200, 100, 1])
  5866. * @param {Array} source
  5867. */
  5868. setSource: function(source) {
  5869. this._source = source;
  5870. },
  5871. /**
  5872. * Returns color represenation in RGB format
  5873. * @return {String} ex: rgb(0-255,0-255,0-255)
  5874. */
  5875. toRgb: function() {
  5876. var source = this.getSource();
  5877. return 'rgb(' + source[0] + ',' + source[1] + ',' + source[2] + ')';
  5878. },
  5879. /**
  5880. * Returns color represenation in RGBA format
  5881. * @return {String} ex: rgba(0-255,0-255,0-255,0-1)
  5882. */
  5883. toRgba: function() {
  5884. var source = this.getSource();
  5885. return 'rgba(' + source[0] + ',' + source[1] + ',' + source[2] + ',' + source[3] + ')';
  5886. },
  5887. /**
  5888. * Returns color represenation in HSL format
  5889. * @return {String} ex: hsl(0-360,0%-100%,0%-100%)
  5890. */
  5891. toHsl: function() {
  5892. var source = this.getSource(),
  5893. hsl = this._rgbToHsl(source[0], source[1], source[2]);
  5894. return 'hsl(' + hsl[0] + ',' + hsl[1] + '%,' + hsl[2] + '%)';
  5895. },
  5896. /**
  5897. * Returns color represenation in HSLA format
  5898. * @return {String} ex: hsla(0-360,0%-100%,0%-100%,0-1)
  5899. */
  5900. toHsla: function() {
  5901. var source = this.getSource(),
  5902. hsl = this._rgbToHsl(source[0], source[1], source[2]);
  5903. return 'hsla(' + hsl[0] + ',' + hsl[1] + '%,' + hsl[2] + '%,' + source[3] + ')';
  5904. },
  5905. /**
  5906. * Returns color represenation in HEX format
  5907. * @return {String} ex: FF5555
  5908. */
  5909. toHex: function() {
  5910. var source = this.getSource(), r, g, b;
  5911. r = source[0].toString(16);
  5912. r = (r.length === 1) ? ('0' + r) : r;
  5913. g = source[1].toString(16);
  5914. g = (g.length === 1) ? ('0' + g) : g;
  5915. b = source[2].toString(16);
  5916. b = (b.length === 1) ? ('0' + b) : b;
  5917. return r.toUpperCase() + g.toUpperCase() + b.toUpperCase();
  5918. },
  5919. /**
  5920. * Gets value of alpha channel for this color
  5921. * @return {Number} 0-1
  5922. */
  5923. getAlpha: function() {
  5924. return this.getSource()[3];
  5925. },
  5926. /**
  5927. * Sets value of alpha channel for this color
  5928. * @param {Number} alpha Alpha value 0-1
  5929. * @return {fabric.Color} thisArg
  5930. */
  5931. setAlpha: function(alpha) {
  5932. var source = this.getSource();
  5933. source[3] = alpha;
  5934. this.setSource(source);
  5935. return this;
  5936. },
  5937. /**
  5938. * Transforms color to its grayscale representation
  5939. * @return {fabric.Color} thisArg
  5940. */
  5941. toGrayscale: function() {
  5942. var source = this.getSource(),
  5943. average = parseInt((source[0] * 0.3 + source[1] * 0.59 + source[2] * 0.11).toFixed(0), 10),
  5944. currentAlpha = source[3];
  5945. this.setSource([average, average, average, currentAlpha]);
  5946. return this;
  5947. },
  5948. /**
  5949. * Transforms color to its black and white representation
  5950. * @param {Number} threshold
  5951. * @return {fabric.Color} thisArg
  5952. */
  5953. toBlackWhite: function(threshold) {
  5954. var source = this.getSource(),
  5955. average = (source[0] * 0.3 + source[1] * 0.59 + source[2] * 0.11).toFixed(0),
  5956. currentAlpha = source[3];
  5957. threshold = threshold || 127;
  5958. average = (Number(average) < Number(threshold)) ? 0 : 255;
  5959. this.setSource([average, average, average, currentAlpha]);
  5960. return this;
  5961. },
  5962. /**
  5963. * Overlays color with another color
  5964. * @param {String|fabric.Color} otherColor
  5965. * @return {fabric.Color} thisArg
  5966. */
  5967. overlayWith: function(otherColor) {
  5968. if (!(otherColor instanceof Color)) {
  5969. otherColor = new Color(otherColor);
  5970. }
  5971. var result = [],
  5972. alpha = this.getAlpha(),
  5973. otherAlpha = 0.5,
  5974. source = this.getSource(),
  5975. otherSource = otherColor.getSource();
  5976. for (var i = 0; i < 3; i++) {
  5977. result.push(Math.round((source[i] * (1 - otherAlpha)) + (otherSource[i] * otherAlpha)));
  5978. }
  5979. result[3] = alpha;
  5980. this.setSource(result);
  5981. return this;
  5982. }
  5983. };
  5984. /**
  5985. * 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))
  5986. * @static
  5987. * @field
  5988. * @memberOf fabric.Color
  5989. */
  5990. 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*)?\)$/;
  5991. /**
  5992. * 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 ))
  5993. * @static
  5994. * @field
  5995. * @memberOf fabric.Color
  5996. */
  5997. fabric.Color.reHSLa = /^hsla?\(\s*(\d{1,3})\s*,\s*(\d{1,3}\%)\s*,\s*(\d{1,3}\%)\s*(?:\s*,\s*(\d+(?:\.\d+)?)\s*)?\)$/;
  5998. /**
  5999. * Regex matching color in HEX format (ex: #FF5555, 010155, aff)
  6000. * @static
  6001. * @field
  6002. * @memberOf fabric.Color
  6003. */
  6004. fabric.Color.reHex = /^#?([0-9a-f]{6}|[0-9a-f]{3})$/i;
  6005. /**
  6006. * Map of the 17 basic color names with HEX code
  6007. * @static
  6008. * @field
  6009. * @memberOf fabric.Color
  6010. * @see: http://www.w3.org/TR/CSS2/syndata.html#color-units
  6011. */
  6012. fabric.Color.colorNameMap = {
  6013. aqua: '#00FFFF',
  6014. black: '#000000',
  6015. blue: '#0000FF',
  6016. fuchsia: '#FF00FF',
  6017. gray: '#808080',
  6018. green: '#008000',
  6019. lime: '#00FF00',
  6020. maroon: '#800000',
  6021. navy: '#000080',
  6022. olive: '#808000',
  6023. orange: '#FFA500',
  6024. purple: '#800080',
  6025. red: '#FF0000',
  6026. silver: '#C0C0C0',
  6027. teal: '#008080',
  6028. white: '#FFFFFF',
  6029. yellow: '#FFFF00'
  6030. };
  6031. /**
  6032. * @private
  6033. * @param {Number} p
  6034. * @param {Number} q
  6035. * @param {Number} t
  6036. * @return {Number}
  6037. */
  6038. function hue2rgb(p, q, t){
  6039. if (t < 0) {
  6040. t += 1;
  6041. }
  6042. if (t > 1) {
  6043. t -= 1;
  6044. }
  6045. if (t < 1/6) {
  6046. return p + (q - p) * 6 * t;
  6047. }
  6048. if (t < 1/2) {
  6049. return q;
  6050. }
  6051. if (t < 2/3) {
  6052. return p + (q - p) * (2/3 - t) * 6;
  6053. }
  6054. return p;
  6055. }
  6056. /**
  6057. * Returns new color object, when given a color in RGB format
  6058. * @memberOf fabric.Color
  6059. * @param {String} color Color value ex: rgb(0-255,0-255,0-255)
  6060. * @return {fabric.Color}
  6061. */
  6062. fabric.Color.fromRgb = function(color) {
  6063. return Color.fromSource(Color.sourceFromRgb(color));
  6064. };
  6065. /**
  6066. * Returns array represenatation (ex: [100, 100, 200, 1]) of a color that's in RGB or RGBA format
  6067. * @memberOf fabric.Color
  6068. * @param {String} color Color value ex: rgb(0-255,0-255,0-255), rgb(0%-100%,0%-100%,0%-100%)
  6069. * @return {Array} source
  6070. */
  6071. fabric.Color.sourceFromRgb = function(color) {
  6072. var match = color.match(Color.reRGBa);
  6073. if (match) {
  6074. var r = parseInt(match[1], 10) / (/%$/.test(match[1]) ? 100 : 1) * (/%$/.test(match[1]) ? 255 : 1),
  6075. g = parseInt(match[2], 10) / (/%$/.test(match[2]) ? 100 : 1) * (/%$/.test(match[2]) ? 255 : 1),
  6076. b = parseInt(match[3], 10) / (/%$/.test(match[3]) ? 100 : 1) * (/%$/.test(match[3]) ? 255 : 1);
  6077. return [
  6078. parseInt(r, 10),
  6079. parseInt(g, 10),
  6080. parseInt(b, 10),
  6081. match[4] ? parseFloat(match[4]) : 1
  6082. ];
  6083. }
  6084. };
  6085. /**
  6086. * Returns new color object, when given a color in RGBA format
  6087. * @static
  6088. * @function
  6089. * @memberOf fabric.Color
  6090. * @param {String} color
  6091. * @return {fabric.Color}
  6092. */
  6093. fabric.Color.fromRgba = Color.fromRgb;
  6094. /**
  6095. * Returns new color object, when given a color in HSL format
  6096. * @param {String} color Color value ex: hsl(0-260,0%-100%,0%-100%)
  6097. * @memberOf fabric.Color
  6098. * @return {fabric.Color}
  6099. */
  6100. fabric.Color.fromHsl = function(color) {
  6101. return Color.fromSource(Color.sourceFromHsl(color));
  6102. };
  6103. /**
  6104. * Returns array represenatation (ex: [100, 100, 200, 1]) of a color that's in HSL or HSLA format.
  6105. * 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>
  6106. * @memberOf fabric.Color
  6107. * @param {String} color Color value ex: hsl(0-360,0%-100%,0%-100%) or hsla(0-360,0%-100%,0%-100%, 0-1)
  6108. * @return {Array} source
  6109. * @see http://http://www.w3.org/TR/css3-color/#hsl-color
  6110. */
  6111. fabric.Color.sourceFromHsl = function(color) {
  6112. var match = color.match(Color.reHSLa);
  6113. if (!match) {
  6114. return;
  6115. }
  6116. var h = (((parseFloat(match[1]) % 360) + 360) % 360) / 360,
  6117. s = parseFloat(match[2]) / (/%$/.test(match[2]) ? 100 : 1),
  6118. l = parseFloat(match[3]) / (/%$/.test(match[3]) ? 100 : 1),
  6119. r, g, b;
  6120. if (s === 0) {
  6121. r = g = b = l;
  6122. }
  6123. else {
  6124. var q = l <= 0.5 ? l * (s + 1) : l + s - l * s,
  6125. p = l * 2 - q;
  6126. r = hue2rgb(p, q, h + 1/3);
  6127. g = hue2rgb(p, q, h);
  6128. b = hue2rgb(p, q, h - 1/3);
  6129. }
  6130. return [
  6131. Math.round(r * 255),
  6132. Math.round(g * 255),
  6133. Math.round(b * 255),
  6134. match[4] ? parseFloat(match[4]) : 1
  6135. ];
  6136. };
  6137. /**
  6138. * Returns new color object, when given a color in HSLA format
  6139. * @static
  6140. * @function
  6141. * @memberOf fabric.Color
  6142. * @param {String} color
  6143. * @return {fabric.Color}
  6144. */
  6145. fabric.Color.fromHsla = Color.fromHsl;
  6146. /**
  6147. * Returns new color object, when given a color in HEX format
  6148. * @static
  6149. * @memberOf fabric.Color
  6150. * @param {String} color Color value ex: FF5555
  6151. * @return {fabric.Color}
  6152. */
  6153. fabric.Color.fromHex = function(color) {
  6154. return Color.fromSource(Color.sourceFromHex(color));
  6155. };
  6156. /**
  6157. * Returns array represenatation (ex: [100, 100, 200, 1]) of a color that's in HEX format
  6158. * @static
  6159. * @memberOf fabric.Color
  6160. * @param {String} color ex: FF5555
  6161. * @return {Array} source
  6162. */
  6163. fabric.Color.sourceFromHex = function(color) {
  6164. if (color.match(Color.reHex)) {
  6165. var value = color.slice(color.indexOf('#') + 1),
  6166. isShortNotation = (value.length === 3),
  6167. r = isShortNotation ? (value.charAt(0) + value.charAt(0)) : value.substring(0, 2),
  6168. g = isShortNotation ? (value.charAt(1) + value.charAt(1)) : value.substring(2, 4),
  6169. b = isShortNotation ? (value.charAt(2) + value.charAt(2)) : value.substring(4, 6);
  6170. return [
  6171. parseInt(r, 16),
  6172. parseInt(g, 16),
  6173. parseInt(b, 16),
  6174. 1
  6175. ];
  6176. }
  6177. };
  6178. /**
  6179. * Returns new color object, when given color in array representation (ex: [200, 100, 100, 0.5])
  6180. * @static
  6181. * @memberOf fabric.Color
  6182. * @param {Array} source
  6183. * @return {fabric.Color}
  6184. */
  6185. fabric.Color.fromSource = function(source) {
  6186. var oColor = new Color();
  6187. oColor.setSource(source);
  6188. return oColor;
  6189. };
  6190. })(typeof exports !== 'undefined' ? exports : this);
  6191. (function() {
  6192. /* _FROM_SVG_START_ */
  6193. function getColorStop(el) {
  6194. var style = el.getAttribute('style'),
  6195. offset = el.getAttribute('offset'),
  6196. color, colorAlpha, opacity;
  6197. // convert percents to absolute values
  6198. offset = parseFloat(offset) / (/%$/.test(offset) ? 100 : 1);
  6199. offset = offset < 0 ? 0 : offset > 1 ? 1 : offset;
  6200. if (style) {
  6201. var keyValuePairs = style.split(/\s*;\s*/);
  6202. if (keyValuePairs[keyValuePairs.length - 1] === '') {
  6203. keyValuePairs.pop();
  6204. }
  6205. for (var i = keyValuePairs.length; i--; ) {
  6206. var split = keyValuePairs[i].split(/\s*:\s*/),
  6207. key = split[0].trim(),
  6208. value = split[1].trim();
  6209. if (key === 'stop-color') {
  6210. color = value;
  6211. }
  6212. else if (key === 'stop-opacity') {
  6213. opacity = value;
  6214. }
  6215. }
  6216. }
  6217. if (!color) {
  6218. color = el.getAttribute('stop-color') || 'rgb(0,0,0)';
  6219. }
  6220. if (!opacity) {
  6221. opacity = el.getAttribute('stop-opacity');
  6222. }
  6223. color = new fabric.Color(color);
  6224. colorAlpha = color.getAlpha();
  6225. opacity = isNaN(parseFloat(opacity)) ? 1 : parseFloat(opacity);
  6226. opacity *= colorAlpha;
  6227. return {
  6228. offset: offset,
  6229. color: color.toRgb(),
  6230. opacity: opacity
  6231. };
  6232. }
  6233. function getLinearCoords(el) {
  6234. return {
  6235. x1: el.getAttribute('x1') || 0,
  6236. y1: el.getAttribute('y1') || 0,
  6237. x2: el.getAttribute('x2') || '100%',
  6238. y2: el.getAttribute('y2') || 0
  6239. };
  6240. }
  6241. function getRadialCoords(el) {
  6242. return {
  6243. x1: el.getAttribute('fx') || el.getAttribute('cx') || '50%',
  6244. y1: el.getAttribute('fy') || el.getAttribute('cy') || '50%',
  6245. r1: 0,
  6246. x2: el.getAttribute('cx') || '50%',
  6247. y2: el.getAttribute('cy') || '50%',
  6248. r2: el.getAttribute('r') || '50%'
  6249. };
  6250. }
  6251. /* _FROM_SVG_END_ */
  6252. /**
  6253. * Gradient class
  6254. * @class fabric.Gradient
  6255. * @tutorial {@link http://fabricjs.com/fabric-intro-part-2/#gradients}
  6256. * @see {@link fabric.Gradient#initialize} for constructor definition
  6257. */
  6258. fabric.Gradient = fabric.util.createClass(/** @lends fabric.Gradient.prototype */ {
  6259. /**
  6260. * Horizontal offset for aligning gradients coming from SVG when outside pathgroups
  6261. * @type Number
  6262. * @default 0
  6263. */
  6264. offsetX: 0,
  6265. /**
  6266. * Vertical offset for aligning gradients coming from SVG when outside pathgroups
  6267. * @type Number
  6268. * @default 0
  6269. */
  6270. offsetY: 0,
  6271. /**
  6272. * Constructor
  6273. * @param {Object} [options] Options object with type, coords, gradientUnits and colorStops
  6274. * @return {fabric.Gradient} thisArg
  6275. */
  6276. initialize: function(options) {
  6277. options || (options = { });
  6278. var coords = { };
  6279. this.id = fabric.Object.__uid++;
  6280. this.type = options.type || 'linear';
  6281. coords = {
  6282. x1: options.coords.x1 || 0,
  6283. y1: options.coords.y1 || 0,
  6284. x2: options.coords.x2 || 0,
  6285. y2: options.coords.y2 || 0
  6286. };
  6287. if (this.type === 'radial') {
  6288. coords.r1 = options.coords.r1 || 0;
  6289. coords.r2 = options.coords.r2 || 0;
  6290. }
  6291. this.coords = coords;
  6292. this.colorStops = options.colorStops.slice();
  6293. if (options.gradientTransform) {
  6294. this.gradientTransform = options.gradientTransform;
  6295. }
  6296. this.offsetX = options.offsetX || this.offsetX;
  6297. this.offsetY = options.offsetY || this.offsetY;
  6298. },
  6299. /**
  6300. * Adds another colorStop
  6301. * @param {Object} colorStop Object with offset and color
  6302. * @return {fabric.Gradient} thisArg
  6303. */
  6304. addColorStop: function(colorStop) {
  6305. for (var position in colorStop) {
  6306. var color = new fabric.Color(colorStop[position]);
  6307. this.colorStops.push({
  6308. offset: position,
  6309. color: color.toRgb(),
  6310. opacity: color.getAlpha()
  6311. });
  6312. }
  6313. return this;
  6314. },
  6315. /**
  6316. * Returns object representation of a gradient
  6317. * @return {Object}
  6318. */
  6319. toObject: function() {
  6320. return {
  6321. type: this.type,
  6322. coords: this.coords,
  6323. colorStops: this.colorStops,
  6324. offsetX: this.offsetX,
  6325. offsetY: this.offsetY
  6326. };
  6327. },
  6328. /* _TO_SVG_START_ */
  6329. /**
  6330. * Returns SVG representation of an gradient
  6331. * @param {Object} object Object to create a gradient for
  6332. * @param {Boolean} normalize Whether coords should be normalized
  6333. * @return {String} SVG representation of an gradient (linear/radial)
  6334. */
  6335. toSVG: function(object) {
  6336. var coords = fabric.util.object.clone(this.coords),
  6337. markup, commonAttributes;
  6338. // colorStops must be sorted ascending
  6339. this.colorStops.sort(function(a, b) {
  6340. return a.offset - b.offset;
  6341. });
  6342. if (!(object.group && object.group.type === 'path-group')) {
  6343. for (var prop in coords) {
  6344. if (prop === 'x1' || prop === 'x2' || prop === 'r2') {
  6345. coords[prop] += this.offsetX - object.width / 2;
  6346. }
  6347. else if (prop === 'y1' || prop === 'y2') {
  6348. coords[prop] += this.offsetY - object.height / 2;
  6349. }
  6350. }
  6351. }
  6352. commonAttributes = 'id="SVGID_' + this.id +
  6353. '" gradientUnits="userSpaceOnUse"';
  6354. if (this.gradientTransform) {
  6355. commonAttributes += ' gradientTransform="matrix(' + this.gradientTransform.join(' ') + ')" ';
  6356. }
  6357. if (this.type === 'linear') {
  6358. markup = [
  6359. //jscs:disable validateIndentation
  6360. '<linearGradient ',
  6361. commonAttributes,
  6362. ' x1="', coords.x1,
  6363. '" y1="', coords.y1,
  6364. '" x2="', coords.x2,
  6365. '" y2="', coords.y2,
  6366. '">\n'
  6367. //jscs:enable validateIndentation
  6368. ];
  6369. }
  6370. else if (this.type === 'radial') {
  6371. markup = [
  6372. //jscs:disable validateIndentation
  6373. '<radialGradient ',
  6374. commonAttributes,
  6375. ' cx="', coords.x2,
  6376. '" cy="', coords.y2,
  6377. '" r="', coords.r2,
  6378. '" fx="', coords.x1,
  6379. '" fy="', coords.y1,
  6380. '">\n'
  6381. //jscs:enable validateIndentation
  6382. ];
  6383. }
  6384. for (var i = 0; i < this.colorStops.length; i++) {
  6385. markup.push(
  6386. //jscs:disable validateIndentation
  6387. '<stop ',
  6388. 'offset="', (this.colorStops[i].offset * 100) + '%',
  6389. '" style="stop-color:', this.colorStops[i].color,
  6390. (this.colorStops[i].opacity != null ? ';stop-opacity: ' + this.colorStops[i].opacity : ';'),
  6391. '"/>\n'
  6392. //jscs:enable validateIndentation
  6393. );
  6394. }
  6395. markup.push((this.type === 'linear' ? '</linearGradient>\n' : '</radialGradient>\n'));
  6396. return markup.join('');
  6397. },
  6398. /* _TO_SVG_END_ */
  6399. /**
  6400. * Returns an instance of CanvasGradient
  6401. * @param {CanvasRenderingContext2D} ctx Context to render on
  6402. * @return {CanvasGradient}
  6403. */
  6404. toLive: function(ctx) {
  6405. var gradient;
  6406. if (!this.type) {
  6407. return;
  6408. }
  6409. if (this.type === 'linear') {
  6410. gradient = ctx.createLinearGradient(
  6411. this.coords.x1, this.coords.y1, this.coords.x2, this.coords.y2);
  6412. }
  6413. else if (this.type === 'radial') {
  6414. gradient = ctx.createRadialGradient(
  6415. this.coords.x1, this.coords.y1, this.coords.r1, this.coords.x2, this.coords.y2, this.coords.r2);
  6416. }
  6417. for (var i = 0, len = this.colorStops.length; i < len; i++) {
  6418. var color = this.colorStops[i].color,
  6419. opacity = this.colorStops[i].opacity,
  6420. offset = this.colorStops[i].offset;
  6421. if (typeof opacity !== 'undefined') {
  6422. color = new fabric.Color(color).setAlpha(opacity).toRgba();
  6423. }
  6424. gradient.addColorStop(parseFloat(offset), color);
  6425. }
  6426. return gradient;
  6427. }
  6428. });
  6429. fabric.util.object.extend(fabric.Gradient, {
  6430. /* _FROM_SVG_START_ */
  6431. /**
  6432. * Returns {@link fabric.Gradient} instance from an SVG element
  6433. * @static
  6434. * @memberof fabric.Gradient
  6435. * @param {SVGGradientElement} el SVG gradient element
  6436. * @param {fabric.Object} instance
  6437. * @return {fabric.Gradient} Gradient instance
  6438. * @see http://www.w3.org/TR/SVG/pservers.html#LinearGradientElement
  6439. * @see http://www.w3.org/TR/SVG/pservers.html#RadialGradientElement
  6440. */
  6441. fromElement: function(el, instance) {
  6442. /**
  6443. * @example:
  6444. *
  6445. * <linearGradient id="linearGrad1">
  6446. * <stop offset="0%" stop-color="white"/>
  6447. * <stop offset="100%" stop-color="black"/>
  6448. * </linearGradient>
  6449. *
  6450. * OR
  6451. *
  6452. * <linearGradient id="linearGrad2">
  6453. * <stop offset="0" style="stop-color:rgb(255,255,255)"/>
  6454. * <stop offset="1" style="stop-color:rgb(0,0,0)"/>
  6455. * </linearGradient>
  6456. *
  6457. * OR
  6458. *
  6459. * <radialGradient id="radialGrad1">
  6460. * <stop offset="0%" stop-color="white" stop-opacity="1" />
  6461. * <stop offset="50%" stop-color="black" stop-opacity="0.5" />
  6462. * <stop offset="100%" stop-color="white" stop-opacity="1" />
  6463. * </radialGradient>
  6464. *
  6465. * OR
  6466. *
  6467. * <radialGradient id="radialGrad2">
  6468. * <stop offset="0" stop-color="rgb(255,255,255)" />
  6469. * <stop offset="0.5" stop-color="rgb(0,0,0)" />
  6470. * <stop offset="1" stop-color="rgb(255,255,255)" />
  6471. * </radialGradient>
  6472. *
  6473. */
  6474. var colorStopEls = el.getElementsByTagName('stop'),
  6475. type = (el.nodeName === 'linearGradient' ? 'linear' : 'radial'),
  6476. gradientUnits = el.getAttribute('gradientUnits') || 'objectBoundingBox',
  6477. gradientTransform = el.getAttribute('gradientTransform'),
  6478. colorStops = [],
  6479. coords = { }, ellipseMatrix;
  6480. if (type === 'linear') {
  6481. coords = getLinearCoords(el);
  6482. }
  6483. else if (type === 'radial') {
  6484. coords = getRadialCoords(el);
  6485. }
  6486. for (var i = colorStopEls.length; i--; ) {
  6487. colorStops.push(getColorStop(colorStopEls[i]));
  6488. }
  6489. ellipseMatrix = _convertPercentUnitsToValues(instance, coords, gradientUnits);
  6490. var gradient = new fabric.Gradient({
  6491. type: type,
  6492. coords: coords,
  6493. colorStops: colorStops,
  6494. offsetX: -instance.left,
  6495. offsetY: -instance.top
  6496. });
  6497. if (gradientTransform || ellipseMatrix !== '') {
  6498. gradient.gradientTransform = fabric.parseTransformAttribute((gradientTransform || '') + ellipseMatrix);
  6499. }
  6500. return gradient;
  6501. },
  6502. /* _FROM_SVG_END_ */
  6503. /**
  6504. * Returns {@link fabric.Gradient} instance from its object representation
  6505. * @static
  6506. * @memberof fabric.Gradient
  6507. * @param {Object} obj
  6508. * @param {Object} [options] Options object
  6509. */
  6510. forObject: function(obj, options) {
  6511. options || (options = { });
  6512. _convertPercentUnitsToValues(obj, options.coords, 'userSpaceOnUse');
  6513. return new fabric.Gradient(options);
  6514. }
  6515. });
  6516. /**
  6517. * @private
  6518. */
  6519. function _convertPercentUnitsToValues(object, options, gradientUnits) {
  6520. var propValue, addFactor = 0, multFactor = 1, ellipseMatrix = '';
  6521. for (var prop in options) {
  6522. propValue = parseFloat(options[prop], 10);
  6523. if (typeof options[prop] === 'string' && /^\d+%$/.test(options[prop])) {
  6524. multFactor = 0.01;
  6525. }
  6526. else {
  6527. multFactor = 1;
  6528. }
  6529. if (prop === 'x1' || prop === 'x2' || prop === 'r2') {
  6530. multFactor *= gradientUnits === 'objectBoundingBox' ? object.width : 1;
  6531. addFactor = gradientUnits === 'objectBoundingBox' ? object.left || 0 : 0;
  6532. }
  6533. else if (prop === 'y1' || prop === 'y2') {
  6534. multFactor *= gradientUnits === 'objectBoundingBox' ? object.height : 1;
  6535. addFactor = gradientUnits === 'objectBoundingBox' ? object.top || 0 : 0;
  6536. }
  6537. options[prop] = propValue * multFactor + addFactor;
  6538. }
  6539. if (object.type === 'ellipse' && options.r2 !== null && gradientUnits === 'objectBoundingBox' && object.rx !== object.ry) {
  6540. var scaleFactor = object.ry/object.rx;
  6541. ellipseMatrix = ' scale(1, ' + scaleFactor + ')';
  6542. if (options.y1) {
  6543. options.y1 /= scaleFactor;
  6544. }
  6545. if (options.y2) {
  6546. options.y2 /= scaleFactor;
  6547. }
  6548. }
  6549. return ellipseMatrix;
  6550. }
  6551. })();
  6552. /**
  6553. * Pattern class
  6554. * @class fabric.Pattern
  6555. * @see {@link http://fabricjs.com/patterns/|Pattern demo}
  6556. * @see {@link http://fabricjs.com/dynamic-patterns/|DynamicPattern demo}
  6557. * @see {@link fabric.Pattern#initialize} for constructor definition
  6558. */
  6559. fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */ {
  6560. /**
  6561. * Repeat property of a pattern (one of repeat, repeat-x, repeat-y or no-repeat)
  6562. * @type String
  6563. * @default
  6564. */
  6565. repeat: 'repeat',
  6566. /**
  6567. * Pattern horizontal offset from object's left/top corner
  6568. * @type Number
  6569. * @default
  6570. */
  6571. offsetX: 0,
  6572. /**
  6573. * Pattern vertical offset from object's left/top corner
  6574. * @type Number
  6575. * @default
  6576. */
  6577. offsetY: 0,
  6578. /**
  6579. * Constructor
  6580. * @param {Object} [options] Options object
  6581. * @return {fabric.Pattern} thisArg
  6582. */
  6583. initialize: function(options) {
  6584. options || (options = { });
  6585. this.id = fabric.Object.__uid++;
  6586. if (options.source) {
  6587. if (typeof options.source === 'string') {
  6588. // function string
  6589. if (typeof fabric.util.getFunctionBody(options.source) !== 'undefined') {
  6590. this.source = new Function(fabric.util.getFunctionBody(options.source));
  6591. }
  6592. else {
  6593. // img src string
  6594. var _this = this;
  6595. this.source = fabric.util.createImage();
  6596. fabric.util.loadImage(options.source, function(img) {
  6597. _this.source = img;
  6598. });
  6599. }
  6600. }
  6601. else {
  6602. // img element
  6603. this.source = options.source;
  6604. }
  6605. }
  6606. if (options.repeat) {
  6607. this.repeat = options.repeat;
  6608. }
  6609. if (options.offsetX) {
  6610. this.offsetX = options.offsetX;
  6611. }
  6612. if (options.offsetY) {
  6613. this.offsetY = options.offsetY;
  6614. }
  6615. },
  6616. /**
  6617. * Returns object representation of a pattern
  6618. * @return {Object} Object representation of a pattern instance
  6619. */
  6620. toObject: function() {
  6621. var source;
  6622. // callback
  6623. if (typeof this.source === 'function') {
  6624. source = String(this.source);
  6625. }
  6626. // <img> element
  6627. else if (typeof this.source.src === 'string') {
  6628. source = this.source.src;
  6629. }
  6630. return {
  6631. source: source,
  6632. repeat: this.repeat,
  6633. offsetX: this.offsetX,
  6634. offsetY: this.offsetY
  6635. };
  6636. },
  6637. /* _TO_SVG_START_ */
  6638. /**
  6639. * Returns SVG representation of a pattern
  6640. * @param {fabric.Object} object
  6641. * @return {String} SVG representation of a pattern
  6642. */
  6643. toSVG: function(object) {
  6644. var patternSource = typeof this.source === 'function' ? this.source() : this.source,
  6645. patternWidth = patternSource.width / object.getWidth(),
  6646. patternHeight = patternSource.height / object.getHeight(),
  6647. patternImgSrc = '';
  6648. if (patternSource.src) {
  6649. patternImgSrc = patternSource.src;
  6650. }
  6651. else if (patternSource.toDataURL) {
  6652. patternImgSrc = patternSource.toDataURL();
  6653. }
  6654. return '<pattern id="SVGID_' + this.id +
  6655. '" x="' + this.offsetX +
  6656. '" y="' + this.offsetY +
  6657. '" width="' + patternWidth +
  6658. '" height="' + patternHeight + '">' +
  6659. '<image x="0" y="0"' +
  6660. ' width="' + patternSource.width +
  6661. '" height="' + patternSource.height +
  6662. '" xlink:href="' + patternImgSrc +
  6663. '"></image>' +
  6664. '</pattern>';
  6665. },
  6666. /* _TO_SVG_END_ */
  6667. /**
  6668. * Returns an instance of CanvasPattern
  6669. * @param {CanvasRenderingContext2D} ctx Context to create pattern
  6670. * @return {CanvasPattern}
  6671. */
  6672. toLive: function(ctx) {
  6673. var source = typeof this.source === 'function'
  6674. ? this.source()
  6675. : this.source;
  6676. // if the image failed to load, return, and allow rest to continue loading
  6677. if (!source) {
  6678. return '';
  6679. }
  6680. // if an image
  6681. if (typeof source.src !== 'undefined') {
  6682. if (!source.complete) {
  6683. return '';
  6684. }
  6685. if (source.naturalWidth === 0 || source.naturalHeight === 0) {
  6686. return '';
  6687. }
  6688. }
  6689. return ctx.createPattern(source, this.repeat);
  6690. }
  6691. });
  6692. (function(global) {
  6693. 'use strict';
  6694. var fabric = global.fabric || (global.fabric = { });
  6695. if (fabric.Shadow) {
  6696. fabric.warn('fabric.Shadow is already defined.');
  6697. return;
  6698. }
  6699. /**
  6700. * Shadow class
  6701. * @class fabric.Shadow
  6702. * @see {@link http://fabricjs.com/shadows/|Shadow demo}
  6703. * @see {@link fabric.Shadow#initialize} for constructor definition
  6704. */
  6705. fabric.Shadow = fabric.util.createClass(/** @lends fabric.Shadow.prototype */ {
  6706. /**
  6707. * Shadow color
  6708. * @type String
  6709. * @default
  6710. */
  6711. color: 'rgb(0,0,0)',
  6712. /**
  6713. * Shadow blur
  6714. * @type Number
  6715. */
  6716. blur: 0,
  6717. /**
  6718. * Shadow horizontal offset
  6719. * @type Number
  6720. * @default
  6721. */
  6722. offsetX: 0,
  6723. /**
  6724. * Shadow vertical offset
  6725. * @type Number
  6726. * @default
  6727. */
  6728. offsetY: 0,
  6729. /**
  6730. * Whether the shadow should affect stroke operations
  6731. * @type Boolean
  6732. * @default
  6733. */
  6734. affectStroke: false,
  6735. /**
  6736. * Indicates whether toObject should include default values
  6737. * @type Boolean
  6738. * @default
  6739. */
  6740. includeDefaultValues: true,
  6741. /**
  6742. * Constructor
  6743. * @param {Object|String} [options] Options object with any of color, blur, offsetX, offsetX properties or string (e.g. "rgba(0,0,0,0.2) 2px 2px 10px, "2px 2px 10px rgba(0,0,0,0.2)")
  6744. * @return {fabric.Shadow} thisArg
  6745. */
  6746. initialize: function(options) {
  6747. if (typeof options === 'string') {
  6748. options = this._parseShadow(options);
  6749. }
  6750. for (var prop in options) {
  6751. this[prop] = options[prop];
  6752. }
  6753. this.id = fabric.Object.__uid++;
  6754. },
  6755. /**
  6756. * @private
  6757. * @param {String} shadow Shadow value to parse
  6758. * @return {Object} Shadow object with color, offsetX, offsetY and blur
  6759. */
  6760. _parseShadow: function(shadow) {
  6761. var shadowStr = shadow.trim(),
  6762. offsetsAndBlur = fabric.Shadow.reOffsetsAndBlur.exec(shadowStr) || [ ],
  6763. color = shadowStr.replace(fabric.Shadow.reOffsetsAndBlur, '') || 'rgb(0,0,0)';
  6764. return {
  6765. color: color.trim(),
  6766. offsetX: parseInt(offsetsAndBlur[1], 10) || 0,
  6767. offsetY: parseInt(offsetsAndBlur[2], 10) || 0,
  6768. blur: parseInt(offsetsAndBlur[3], 10) || 0
  6769. };
  6770. },
  6771. /**
  6772. * Returns a string representation of an instance
  6773. * @see http://www.w3.org/TR/css-text-decor-3/#text-shadow
  6774. * @return {String} Returns CSS3 text-shadow declaration
  6775. */
  6776. toString: function() {
  6777. return [this.offsetX, this.offsetY, this.blur, this.color].join('px ');
  6778. },
  6779. /* _TO_SVG_START_ */
  6780. /**
  6781. * Returns SVG representation of a shadow
  6782. * @param {fabric.Object} object
  6783. * @return {String} SVG representation of a shadow
  6784. */
  6785. toSVG: function(object) {
  6786. var mode = 'SourceAlpha';
  6787. if (object && (object.fill === this.color || object.stroke === this.color)) {
  6788. mode = 'SourceGraphic';
  6789. }
  6790. return (
  6791. '<filter id="SVGID_' + this.id + '" y="-40%" height="180%">' +
  6792. '<feGaussianBlur in="' + mode + '" stdDeviation="' +
  6793. (this.blur ? this.blur / 3 : 0) +
  6794. '"></feGaussianBlur>' +
  6795. '<feOffset dx="' + this.offsetX + '" dy="' + this.offsetY + '"></feOffset>' +
  6796. '<feMerge>' +
  6797. '<feMergeNode></feMergeNode>' +
  6798. '<feMergeNode in="SourceGraphic"></feMergeNode>' +
  6799. '</feMerge>' +
  6800. '</filter>');
  6801. },
  6802. /* _TO_SVG_END_ */
  6803. /**
  6804. * Returns object representation of a shadow
  6805. * @return {Object} Object representation of a shadow instance
  6806. */
  6807. toObject: function() {
  6808. if (this.includeDefaultValues) {
  6809. return {
  6810. color: this.color,
  6811. blur: this.blur,
  6812. offsetX: this.offsetX,
  6813. offsetY: this.offsetY
  6814. };
  6815. }
  6816. var obj = { }, proto = fabric.Shadow.prototype;
  6817. if (this.color !== proto.color) {
  6818. obj.color = this.color;
  6819. }
  6820. if (this.blur !== proto.blur) {
  6821. obj.blur = this.blur;
  6822. }
  6823. if (this.offsetX !== proto.offsetX) {
  6824. obj.offsetX = this.offsetX;
  6825. }
  6826. if (this.offsetY !== proto.offsetY) {
  6827. obj.offsetY = this.offsetY;
  6828. }
  6829. return obj;
  6830. }
  6831. });
  6832. /**
  6833. * Regex matching shadow offsetX, offsetY and blur (ex: "2px 2px 10px rgba(0,0,0,0.2)", "rgb(0,255,0) 2px 2px")
  6834. * @static
  6835. * @field
  6836. * @memberOf fabric.Shadow
  6837. */
  6838. fabric.Shadow.reOffsetsAndBlur = /(?:\s|^)(-?\d+(?:px)?(?:\s?|$))?(-?\d+(?:px)?(?:\s?|$))?(\d+(?:px)?)?(?:\s?|$)(?:$|\s)/;
  6839. })(typeof exports !== 'undefined' ? exports : this);
  6840. (function () {
  6841. 'use strict';
  6842. if (fabric.StaticCanvas) {
  6843. fabric.warn('fabric.StaticCanvas is already defined.');
  6844. return;
  6845. }
  6846. // aliases for faster resolution
  6847. var extend = fabric.util.object.extend,
  6848. getElementOffset = fabric.util.getElementOffset,
  6849. removeFromArray = fabric.util.removeFromArray,
  6850. CANVAS_INIT_ERROR = new Error('Could not initialize `canvas` element');
  6851. /**
  6852. * Static canvas class
  6853. * @class fabric.StaticCanvas
  6854. * @mixes fabric.Collection
  6855. * @mixes fabric.Observable
  6856. * @see {@link http://fabricjs.com/static_canvas/|StaticCanvas demo}
  6857. * @see {@link fabric.StaticCanvas#initialize} for constructor definition
  6858. * @fires before:render
  6859. * @fires after:render
  6860. * @fires canvas:cleared
  6861. * @fires object:added
  6862. * @fires object:removed
  6863. */
  6864. fabric.StaticCanvas = fabric.util.createClass(/** @lends fabric.StaticCanvas.prototype */ {
  6865. /**
  6866. * Constructor
  6867. * @param {HTMLElement | String} el &lt;canvas> element to initialize instance on
  6868. * @param {Object} [options] Options object
  6869. * @return {Object} thisArg
  6870. */
  6871. initialize: function(el, options) {
  6872. options || (options = { });
  6873. this._initStatic(el, options);
  6874. fabric.StaticCanvas.activeInstance = this;
  6875. },
  6876. /**
  6877. * Background color of canvas instance.
  6878. * Should be set via {@link fabric.StaticCanvas#setBackgroundColor}.
  6879. * @type {(String|fabric.Pattern)}
  6880. * @default
  6881. */
  6882. backgroundColor: '',
  6883. /**
  6884. * Background image of canvas instance.
  6885. * Should be set via {@link fabric.StaticCanvas#setBackgroundImage}.
  6886. * <b>Backwards incompatibility note:</b> The "backgroundImageOpacity"
  6887. * and "backgroundImageStretch" properties are deprecated since 1.3.9.
  6888. * Use {@link fabric.Image#opacity}, {@link fabric.Image#width} and {@link fabric.Image#height}.
  6889. * @type fabric.Image
  6890. * @default
  6891. */
  6892. backgroundImage: null,
  6893. /**
  6894. * Overlay color of canvas instance.
  6895. * Should be set via {@link fabric.StaticCanvas#setOverlayColor}
  6896. * @since 1.3.9
  6897. * @type {(String|fabric.Pattern)}
  6898. * @default
  6899. */
  6900. overlayColor: '',
  6901. /**
  6902. * Overlay image of canvas instance.
  6903. * Should be set via {@link fabric.StaticCanvas#setOverlayImage}.
  6904. * <b>Backwards incompatibility note:</b> The "overlayImageLeft"
  6905. * and "overlayImageTop" properties are deprecated since 1.3.9.
  6906. * Use {@link fabric.Image#left} and {@link fabric.Image#top}.
  6907. * @type fabric.Image
  6908. * @default
  6909. */
  6910. overlayImage: null,
  6911. /**
  6912. * Indicates whether toObject/toDatalessObject should include default values
  6913. * @type Boolean
  6914. * @default
  6915. */
  6916. includeDefaultValues: true,
  6917. /**
  6918. * Indicates whether objects' state should be saved
  6919. * @type Boolean
  6920. * @default
  6921. */
  6922. stateful: true,
  6923. /**
  6924. * Indicates whether {@link fabric.Collection.add}, {@link fabric.Collection.insertAt} and {@link fabric.Collection.remove} should also re-render canvas.
  6925. * Disabling this option could give a great performance boost when adding/removing a lot of objects to/from canvas at once
  6926. * (followed by a manual rendering after addition/deletion)
  6927. * @type Boolean
  6928. * @default
  6929. */
  6930. renderOnAddRemove: true,
  6931. /**
  6932. * Function that determines clipping of entire canvas area
  6933. * Being passed context as first argument. See clipping canvas area in {@link https://github.com/kangax/fabric.js/wiki/FAQ}
  6934. * @type Function
  6935. * @default
  6936. */
  6937. clipTo: null,
  6938. /**
  6939. * Indicates whether object controls (borders/controls) are rendered above overlay image
  6940. * @type Boolean
  6941. * @default
  6942. */
  6943. controlsAboveOverlay: false,
  6944. /**
  6945. * Indicates whether the browser can be scrolled when using a touchscreen and dragging on the canvas
  6946. * @type Boolean
  6947. * @default
  6948. */
  6949. allowTouchScrolling: false,
  6950. /**
  6951. * Indicates whether this canvas will use image smoothing, this is on by default in browsers
  6952. * @type Boolean
  6953. * @default
  6954. */
  6955. imageSmoothingEnabled: true,
  6956. /**
  6957. * The transformation (in the format of Canvas transform) which focuses the viewport
  6958. * @type Array
  6959. * @default
  6960. */
  6961. viewportTransform: [1, 0, 0, 1, 0, 0],
  6962. /**
  6963. * Callback; invoked right before object is about to be scaled/rotated
  6964. */
  6965. onBeforeScaleRotate: function () {
  6966. /* NOOP */
  6967. },
  6968. /**
  6969. * @private
  6970. * @param {HTMLElement | String} el &lt;canvas> element to initialize instance on
  6971. * @param {Object} [options] Options object
  6972. */
  6973. _initStatic: function(el, options) {
  6974. this._objects = [];
  6975. this._createLowerCanvas(el);
  6976. this._initOptions(options);
  6977. this._setImageSmoothing();
  6978. if (options.overlayImage) {
  6979. this.setOverlayImage(options.overlayImage, this.renderAll.bind(this));
  6980. }
  6981. if (options.backgroundImage) {
  6982. this.setBackgroundImage(options.backgroundImage, this.renderAll.bind(this));
  6983. }
  6984. if (options.backgroundColor) {
  6985. this.setBackgroundColor(options.backgroundColor, this.renderAll.bind(this));
  6986. }
  6987. if (options.overlayColor) {
  6988. this.setOverlayColor(options.overlayColor, this.renderAll.bind(this));
  6989. }
  6990. this.calcOffset();
  6991. },
  6992. /**
  6993. * Calculates canvas element offset relative to the document
  6994. * This method is also attached as "resize" event handler of window
  6995. * @return {fabric.Canvas} instance
  6996. * @chainable
  6997. */
  6998. calcOffset: function () {
  6999. this._offset = getElementOffset(this.lowerCanvasEl);
  7000. return this;
  7001. },
  7002. /**
  7003. * Sets {@link fabric.StaticCanvas#overlayImage|overlay image} for this canvas
  7004. * @param {(fabric.Image|String)} image fabric.Image instance or URL of an image to set overlay to
  7005. * @param {Function} callback callback to invoke when image is loaded and set as an overlay
  7006. * @param {Object} [options] Optional options to set for the {@link fabric.Image|overlay image}.
  7007. * @return {fabric.Canvas} thisArg
  7008. * @chainable
  7009. * @see {@link http://jsfiddle.net/fabricjs/MnzHT/|jsFiddle demo}
  7010. * @example <caption>Normal overlayImage with left/top = 0</caption>
  7011. * canvas.setOverlayImage('http://fabricjs.com/assets/jail_cell_bars.png', canvas.renderAll.bind(canvas), {
  7012. * // Needed to position overlayImage at 0/0
  7013. * originX: 'left',
  7014. * originY: 'top'
  7015. * });
  7016. * @example <caption>overlayImage with different properties</caption>
  7017. * canvas.setOverlayImage('http://fabricjs.com/assets/jail_cell_bars.png', canvas.renderAll.bind(canvas), {
  7018. * opacity: 0.5,
  7019. * angle: 45,
  7020. * left: 400,
  7021. * top: 400,
  7022. * originX: 'left',
  7023. * originY: 'top'
  7024. * });
  7025. * @example <caption>Stretched overlayImage #1 - width/height correspond to canvas width/height</caption>
  7026. * fabric.Image.fromURL('http://fabricjs.com/assets/jail_cell_bars.png', function(img) {
  7027. * img.set({width: canvas.width, height: canvas.height, originX: 'left', originY: 'top'});
  7028. * canvas.setOverlayImage(img, canvas.renderAll.bind(canvas));
  7029. * });
  7030. * @example <caption>Stretched overlayImage #2 - width/height correspond to canvas width/height</caption>
  7031. * canvas.setOverlayImage('http://fabricjs.com/assets/jail_cell_bars.png', canvas.renderAll.bind(canvas), {
  7032. * width: canvas.width,
  7033. * height: canvas.height,
  7034. * // Needed to position overlayImage at 0/0
  7035. * originX: 'left',
  7036. * originY: 'top'
  7037. * });
  7038. */
  7039. setOverlayImage: function (image, callback, options) {
  7040. return this.__setBgOverlayImage('overlayImage', image, callback, options);
  7041. },
  7042. /**
  7043. * Sets {@link fabric.StaticCanvas#backgroundImage|background image} for this canvas
  7044. * @param {(fabric.Image|String)} image fabric.Image instance or URL of an image to set background to
  7045. * @param {Function} callback Callback to invoke when image is loaded and set as background
  7046. * @param {Object} [options] Optional options to set for the {@link fabric.Image|background image}.
  7047. * @return {fabric.Canvas} thisArg
  7048. * @chainable
  7049. * @see {@link http://jsfiddle.net/fabricjs/YH9yD/|jsFiddle demo}
  7050. * @example <caption>Normal backgroundImage with left/top = 0</caption>
  7051. * canvas.setBackgroundImage('http://fabricjs.com/assets/honey_im_subtle.png', canvas.renderAll.bind(canvas), {
  7052. * // Needed to position backgroundImage at 0/0
  7053. * originX: 'left',
  7054. * originY: 'top'
  7055. * });
  7056. * @example <caption>backgroundImage with different properties</caption>
  7057. * canvas.setBackgroundImage('http://fabricjs.com/assets/honey_im_subtle.png', canvas.renderAll.bind(canvas), {
  7058. * opacity: 0.5,
  7059. * angle: 45,
  7060. * left: 400,
  7061. * top: 400,
  7062. * originX: 'left',
  7063. * originY: 'top'
  7064. * });
  7065. * @example <caption>Stretched backgroundImage #1 - width/height correspond to canvas width/height</caption>
  7066. * fabric.Image.fromURL('http://fabricjs.com/assets/honey_im_subtle.png', function(img) {
  7067. * img.set({width: canvas.width, height: canvas.height, originX: 'left', originY: 'top'});
  7068. * canvas.setBackgroundImage(img, canvas.renderAll.bind(canvas));
  7069. * });
  7070. * @example <caption>Stretched backgroundImage #2 - width/height correspond to canvas width/height</caption>
  7071. * canvas.setBackgroundImage('http://fabricjs.com/assets/honey_im_subtle.png', canvas.renderAll.bind(canvas), {
  7072. * width: canvas.width,
  7073. * height: canvas.height,
  7074. * // Needed to position backgroundImage at 0/0
  7075. * originX: 'left',
  7076. * originY: 'top'
  7077. * });
  7078. */
  7079. setBackgroundImage: function (image, callback, options) {
  7080. return this.__setBgOverlayImage('backgroundImage', image, callback, options);
  7081. },
  7082. /**
  7083. * Sets {@link fabric.StaticCanvas#overlayColor|background color} for this canvas
  7084. * @param {(String|fabric.Pattern)} overlayColor Color or pattern to set background color to
  7085. * @param {Function} callback Callback to invoke when background color is set
  7086. * @return {fabric.Canvas} thisArg
  7087. * @chainable
  7088. * @see {@link http://jsfiddle.net/fabricjs/pB55h/|jsFiddle demo}
  7089. * @example <caption>Normal overlayColor - color value</caption>
  7090. * canvas.setOverlayColor('rgba(255, 73, 64, 0.6)', canvas.renderAll.bind(canvas));
  7091. * @example <caption>fabric.Pattern used as overlayColor</caption>
  7092. * canvas.setOverlayColor({
  7093. * source: 'http://fabricjs.com/assets/escheresque_ste.png'
  7094. * }, canvas.renderAll.bind(canvas));
  7095. * @example <caption>fabric.Pattern used as overlayColor with repeat and offset</caption>
  7096. * canvas.setOverlayColor({
  7097. * source: 'http://fabricjs.com/assets/escheresque_ste.png',
  7098. * repeat: 'repeat',
  7099. * offsetX: 200,
  7100. * offsetY: 100
  7101. * }, canvas.renderAll.bind(canvas));
  7102. */
  7103. setOverlayColor: function(overlayColor, callback) {
  7104. return this.__setBgOverlayColor('overlayColor', overlayColor, callback);
  7105. },
  7106. /**
  7107. * Sets {@link fabric.StaticCanvas#backgroundColor|background color} for this canvas
  7108. * @param {(String|fabric.Pattern)} backgroundColor Color or pattern to set background color to
  7109. * @param {Function} callback Callback to invoke when background color is set
  7110. * @return {fabric.Canvas} thisArg
  7111. * @chainable
  7112. * @see {@link http://jsfiddle.net/fabricjs/hXzvk/|jsFiddle demo}
  7113. * @example <caption>Normal backgroundColor - color value</caption>
  7114. * canvas.setBackgroundColor('rgba(255, 73, 64, 0.6)', canvas.renderAll.bind(canvas));
  7115. * @example <caption>fabric.Pattern used as backgroundColor</caption>
  7116. * canvas.setBackgroundColor({
  7117. * source: 'http://fabricjs.com/assets/escheresque_ste.png'
  7118. * }, canvas.renderAll.bind(canvas));
  7119. * @example <caption>fabric.Pattern used as backgroundColor with repeat and offset</caption>
  7120. * canvas.setBackgroundColor({
  7121. * source: 'http://fabricjs.com/assets/escheresque_ste.png',
  7122. * repeat: 'repeat',
  7123. * offsetX: 200,
  7124. * offsetY: 100
  7125. * }, canvas.renderAll.bind(canvas));
  7126. */
  7127. setBackgroundColor: function(backgroundColor, callback) {
  7128. return this.__setBgOverlayColor('backgroundColor', backgroundColor, callback);
  7129. },
  7130. /**
  7131. * @private
  7132. * @see {@link http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-imagesmoothingenabled|WhatWG Canvas Standard}
  7133. */
  7134. _setImageSmoothing: function(){
  7135. var ctx = this.getContext();
  7136. ctx.imageSmoothingEnabled = this.imageSmoothingEnabled;
  7137. ctx.webkitImageSmoothingEnabled = this.imageSmoothingEnabled;
  7138. ctx.mozImageSmoothingEnabled = this.imageSmoothingEnabled;
  7139. ctx.msImageSmoothingEnabled = this.imageSmoothingEnabled;
  7140. ctx.oImageSmoothingEnabled = this.imageSmoothingEnabled;
  7141. },
  7142. /**
  7143. * @private
  7144. * @param {String} property Property to set ({@link fabric.StaticCanvas#backgroundImage|backgroundImage}
  7145. * or {@link fabric.StaticCanvas#overlayImage|overlayImage})
  7146. * @param {(fabric.Image|String|null)} image fabric.Image instance, URL of an image or null to set background or overlay to
  7147. * @param {Function} callback Callback to invoke when image is loaded and set as background or overlay
  7148. * @param {Object} [options] Optional options to set for the {@link fabric.Image|image}.
  7149. */
  7150. __setBgOverlayImage: function(property, image, callback, options) {
  7151. if (typeof image === 'string') {
  7152. fabric.util.loadImage(image, function(img) {
  7153. this[property] = new fabric.Image(img, options);
  7154. callback && callback();
  7155. }, this);
  7156. }
  7157. else {
  7158. this[property] = image;
  7159. callback && callback();
  7160. }
  7161. return this;
  7162. },
  7163. /**
  7164. * @private
  7165. * @param {String} property Property to set ({@link fabric.StaticCanvas#backgroundColor|backgroundColor}
  7166. * or {@link fabric.StaticCanvas#overlayColor|overlayColor})
  7167. * @param {(Object|String|null)} color Object with pattern information, color value or null
  7168. * @param {Function} [callback] Callback is invoked when color is set
  7169. */
  7170. __setBgOverlayColor: function(property, color, callback) {
  7171. if (color && color.source) {
  7172. var _this = this;
  7173. fabric.util.loadImage(color.source, function(img) {
  7174. _this[property] = new fabric.Pattern({
  7175. source: img,
  7176. repeat: color.repeat,
  7177. offsetX: color.offsetX,
  7178. offsetY: color.offsetY
  7179. });
  7180. callback && callback();
  7181. });
  7182. }
  7183. else {
  7184. this[property] = color;
  7185. callback && callback();
  7186. }
  7187. return this;
  7188. },
  7189. /**
  7190. * @private
  7191. */
  7192. _createCanvasElement: function() {
  7193. var element = fabric.document.createElement('canvas');
  7194. if (!element.style) {
  7195. element.style = { };
  7196. }
  7197. if (!element) {
  7198. throw CANVAS_INIT_ERROR;
  7199. }
  7200. this._initCanvasElement(element);
  7201. return element;
  7202. },
  7203. /**
  7204. * @private
  7205. * @param {HTMLElement} element
  7206. */
  7207. _initCanvasElement: function(element) {
  7208. fabric.util.createCanvasElement(element);
  7209. if (typeof element.getContext === 'undefined') {
  7210. throw CANVAS_INIT_ERROR;
  7211. }
  7212. },
  7213. /**
  7214. * @private
  7215. * @param {Object} [options] Options object
  7216. */
  7217. _initOptions: function (options) {
  7218. for (var prop in options) {
  7219. this[prop] = options[prop];
  7220. }
  7221. this.width = this.width || parseInt(this.lowerCanvasEl.width, 10) || 0;
  7222. this.height = this.height || parseInt(this.lowerCanvasEl.height, 10) || 0;
  7223. if (!this.lowerCanvasEl.style) {
  7224. return;
  7225. }
  7226. this.lowerCanvasEl.width = this.width;
  7227. this.lowerCanvasEl.height = this.height;
  7228. this.lowerCanvasEl.style.width = this.width + 'px';
  7229. this.lowerCanvasEl.style.height = this.height + 'px';
  7230. this.viewportTransform = this.viewportTransform.slice();
  7231. },
  7232. /**
  7233. * Creates a bottom canvas
  7234. * @private
  7235. * @param {HTMLElement} [canvasEl]
  7236. */
  7237. _createLowerCanvas: function (canvasEl) {
  7238. this.lowerCanvasEl = fabric.util.getById(canvasEl) || this._createCanvasElement();
  7239. this._initCanvasElement(this.lowerCanvasEl);
  7240. fabric.util.addClass(this.lowerCanvasEl, 'lower-canvas');
  7241. if (this.interactive) {
  7242. this._applyCanvasStyle(this.lowerCanvasEl);
  7243. }
  7244. this.contextContainer = this.lowerCanvasEl.getContext('2d');
  7245. },
  7246. /**
  7247. * Returns canvas width (in px)
  7248. * @return {Number}
  7249. */
  7250. getWidth: function () {
  7251. return this.width;
  7252. },
  7253. /**
  7254. * Returns canvas height (in px)
  7255. * @return {Number}
  7256. */
  7257. getHeight: function () {
  7258. return this.height;
  7259. },
  7260. /**
  7261. * Sets width of this canvas instance
  7262. * @param {Number|String} value Value to set width to
  7263. * @param {Object} [options] Options object
  7264. * @param {Boolean} [options.backstoreOnly=false] Set the given dimensions only as canvas backstore dimensions
  7265. * @param {Boolean} [options.cssOnly=false] Set the given dimensions only as css dimensions
  7266. * @return {fabric.Canvas} instance
  7267. * @chainable true
  7268. */
  7269. setWidth: function (value, options) {
  7270. return this.setDimensions({ width: value }, options);
  7271. },
  7272. /**
  7273. * Sets height of this canvas instance
  7274. * @param {Number|String} value Value to set height to
  7275. * @param {Object} [options] Options object
  7276. * @param {Boolean} [options.backstoreOnly=false] Set the given dimensions only as canvas backstore dimensions
  7277. * @param {Boolean} [options.cssOnly=false] Set the given dimensions only as css dimensions
  7278. * @return {fabric.Canvas} instance
  7279. * @chainable true
  7280. */
  7281. setHeight: function (value, options) {
  7282. return this.setDimensions({ height: value }, options);
  7283. },
  7284. /**
  7285. * Sets dimensions (width, height) of this canvas instance. when options.cssOnly flag active you should also supply the unit of measure (px/%/em)
  7286. * @param {Object} dimensions Object with width/height properties
  7287. * @param {Number|String} [dimensions.width] Width of canvas element
  7288. * @param {Number|String} [dimensions.height] Height of canvas element
  7289. * @param {Object} [options] Options object
  7290. * @param {Boolean} [options.backstoreOnly=false] Set the given dimensions only as canvas backstore dimensions
  7291. * @param {Boolean} [options.cssOnly=false] Set the given dimensions only as css dimensions
  7292. * @return {fabric.Canvas} thisArg
  7293. * @chainable
  7294. */
  7295. setDimensions: function (dimensions, options) {
  7296. var cssValue;
  7297. options = options || {};
  7298. for (var prop in dimensions) {
  7299. cssValue = dimensions[prop];
  7300. if (!options.cssOnly) {
  7301. this._setBackstoreDimension(prop, dimensions[prop]);
  7302. cssValue += 'px';
  7303. }
  7304. if (!options.backstoreOnly) {
  7305. this._setCssDimension(prop, cssValue);
  7306. }
  7307. }
  7308. if (!options.cssOnly) {
  7309. this.renderAll();
  7310. }
  7311. this.calcOffset();
  7312. return this;
  7313. },
  7314. /**
  7315. * Helper for setting width/height
  7316. * @private
  7317. * @param {String} prop property (width|height)
  7318. * @param {Number} value value to set property to
  7319. * @return {fabric.Canvas} instance
  7320. * @chainable true
  7321. */
  7322. _setBackstoreDimension: function (prop, value) {
  7323. this.lowerCanvasEl[prop] = value;
  7324. if (this.upperCanvasEl) {
  7325. this.upperCanvasEl[prop] = value;
  7326. }
  7327. if (this.cacheCanvasEl) {
  7328. this.cacheCanvasEl[prop] = value;
  7329. }
  7330. this[prop] = value;
  7331. return this;
  7332. },
  7333. /**
  7334. * Helper for setting css width/height
  7335. * @private
  7336. * @param {String} prop property (width|height)
  7337. * @param {String} value value to set property to
  7338. * @return {fabric.Canvas} instance
  7339. * @chainable true
  7340. */
  7341. _setCssDimension: function (prop, value) {
  7342. this.lowerCanvasEl.style[prop] = value;
  7343. if (this.upperCanvasEl) {
  7344. this.upperCanvasEl.style[prop] = value;
  7345. }
  7346. if (this.wrapperEl) {
  7347. this.wrapperEl.style[prop] = value;
  7348. }
  7349. return this;
  7350. },
  7351. /**
  7352. * Returns canvas zoom level
  7353. * @return {Number}
  7354. */
  7355. getZoom: function () {
  7356. return Math.sqrt(this.viewportTransform[0] * this.viewportTransform[3]);
  7357. },
  7358. /**
  7359. * Sets viewport transform of this canvas instance
  7360. * @param {Array} vpt the transform in the form of context.transform
  7361. * @return {fabric.Canvas} instance
  7362. * @chainable true
  7363. */
  7364. setViewportTransform: function (vpt) {
  7365. this.viewportTransform = vpt;
  7366. this.renderAll();
  7367. for (var i = 0, len = this._objects.length; i < len; i++) {
  7368. this._objects[i].setCoords();
  7369. }
  7370. return this;
  7371. },
  7372. /**
  7373. * Sets zoom level of this canvas instance, zoom centered around point
  7374. * @param {fabric.Point} point to zoom with respect to
  7375. * @param {Number} value to set zoom to, less than 1 zooms out
  7376. * @return {fabric.Canvas} instance
  7377. * @chainable true
  7378. */
  7379. zoomToPoint: function (point, value) {
  7380. // TODO: just change the scale, preserve other transformations
  7381. var before = point;
  7382. point = fabric.util.transformPoint(point, fabric.util.invertTransform(this.viewportTransform));
  7383. this.viewportTransform[0] = value;
  7384. this.viewportTransform[3] = value;
  7385. var after = fabric.util.transformPoint(point, this.viewportTransform);
  7386. this.viewportTransform[4] += before.x - after.x;
  7387. this.viewportTransform[5] += before.y - after.y;
  7388. this.renderAll();
  7389. for (var i = 0, len = this._objects.length; i < len; i++) {
  7390. this._objects[i].setCoords();
  7391. }
  7392. return this;
  7393. },
  7394. /**
  7395. * Sets zoom level of this canvas instance
  7396. * @param {Number} value to set zoom to, less than 1 zooms out
  7397. * @return {fabric.Canvas} instance
  7398. * @chainable true
  7399. */
  7400. setZoom: function (value) {
  7401. this.zoomToPoint(new fabric.Point(0, 0), value);
  7402. return this;
  7403. },
  7404. /**
  7405. * Pan viewport so as to place point at top left corner of canvas
  7406. * @param {fabric.Point} point to move to
  7407. * @return {fabric.Canvas} instance
  7408. * @chainable true
  7409. */
  7410. absolutePan: function (point) {
  7411. this.viewportTransform[4] = -point.x;
  7412. this.viewportTransform[5] = -point.y;
  7413. this.renderAll();
  7414. for (var i = 0, len = this._objects.length; i < len; i++) {
  7415. this._objects[i].setCoords();
  7416. }
  7417. return this;
  7418. },
  7419. /**
  7420. * Pans viewpoint relatively
  7421. * @param {fabric.Point} point (position vector) to move by
  7422. * @return {fabric.Canvas} instance
  7423. * @chainable true
  7424. */
  7425. relativePan: function (point) {
  7426. return this.absolutePan(new fabric.Point(
  7427. -point.x - this.viewportTransform[4],
  7428. -point.y - this.viewportTransform[5]
  7429. ));
  7430. },
  7431. /**
  7432. * Returns &lt;canvas> element corresponding to this instance
  7433. * @return {HTMLCanvasElement}
  7434. */
  7435. getElement: function () {
  7436. return this.lowerCanvasEl;
  7437. },
  7438. /**
  7439. * Returns currently selected object, if any
  7440. * @return {fabric.Object}
  7441. */
  7442. getActiveObject: function() {
  7443. return null;
  7444. },
  7445. /**
  7446. * Returns currently selected group of object, if any
  7447. * @return {fabric.Group}
  7448. */
  7449. getActiveGroup: function() {
  7450. return null;
  7451. },
  7452. /**
  7453. * Given a context, renders an object on that context
  7454. * @param {CanvasRenderingContext2D} ctx Context to render object on
  7455. * @param {fabric.Object} object Object to render
  7456. * @private
  7457. */
  7458. _draw: function (ctx, object) {
  7459. if (!object) {
  7460. return;
  7461. }
  7462. ctx.save();
  7463. var v = this.viewportTransform;
  7464. ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
  7465. object.render(ctx);
  7466. ctx.restore();
  7467. if (!this.controlsAboveOverlay) {
  7468. object._renderControls(ctx);
  7469. }
  7470. },
  7471. /**
  7472. * @private
  7473. * @param {fabric.Object} obj Object that was added
  7474. */
  7475. _onObjectAdded: function(obj) {
  7476. this.stateful && obj.setupState();
  7477. obj.canvas = this;
  7478. obj.setCoords();
  7479. this.fire('object:added', { target: obj });
  7480. obj.fire('added');
  7481. },
  7482. /**
  7483. * @private
  7484. * @param {fabric.Object} obj Object that was removed
  7485. */
  7486. _onObjectRemoved: function(obj) {
  7487. // removing active object should fire "selection:cleared" events
  7488. if (this.getActiveObject() === obj) {
  7489. this.fire('before:selection:cleared', { target: obj });
  7490. this._discardActiveObject();
  7491. this.fire('selection:cleared');
  7492. }
  7493. this.fire('object:removed', { target: obj });
  7494. obj.fire('removed');
  7495. },
  7496. /**
  7497. * Clears specified context of canvas element
  7498. * @param {CanvasRenderingContext2D} ctx Context to clear
  7499. * @return {fabric.Canvas} thisArg
  7500. * @chainable
  7501. */
  7502. clearContext: function(ctx) {
  7503. ctx.clearRect(0, 0, this.width, this.height);
  7504. return this;
  7505. },
  7506. /**
  7507. * Returns context of canvas where objects are drawn
  7508. * @return {CanvasRenderingContext2D}
  7509. */
  7510. getContext: function () {
  7511. return this.contextContainer;
  7512. },
  7513. /**
  7514. * Clears all contexts (background, main, top) of an instance
  7515. * @return {fabric.Canvas} thisArg
  7516. * @chainable
  7517. */
  7518. clear: function () {
  7519. this._objects.length = 0;
  7520. if (this.discardActiveGroup) {
  7521. this.discardActiveGroup();
  7522. }
  7523. if (this.discardActiveObject) {
  7524. this.discardActiveObject();
  7525. }
  7526. this.clearContext(this.contextContainer);
  7527. if (this.contextTop) {
  7528. this.clearContext(this.contextTop);
  7529. }
  7530. this.fire('canvas:cleared');
  7531. this.renderAll();
  7532. return this;
  7533. },
  7534. /**
  7535. * Renders both the top canvas and the secondary container canvas.
  7536. * @param {Boolean} [allOnTop] Whether we want to force all images to be rendered on the top canvas
  7537. * @return {fabric.Canvas} instance
  7538. * @chainable
  7539. */
  7540. renderAll: function (allOnTop) {
  7541. var canvasToDrawOn = this[(allOnTop === true && this.interactive) ? 'contextTop' : 'contextContainer'],
  7542. activeGroup = this.getActiveGroup();
  7543. if (this.contextTop && this.selection && !this._groupSelector) {
  7544. this.clearContext(this.contextTop);
  7545. }
  7546. if (!allOnTop) {
  7547. this.clearContext(canvasToDrawOn);
  7548. }
  7549. this.fire('before:render');
  7550. if (this.clipTo) {
  7551. fabric.util.clipContext(this, canvasToDrawOn);
  7552. }
  7553. this._renderBackground(canvasToDrawOn);
  7554. this._renderObjects(canvasToDrawOn, activeGroup);
  7555. this._renderActiveGroup(canvasToDrawOn, activeGroup);
  7556. if (this.clipTo) {
  7557. canvasToDrawOn.restore();
  7558. }
  7559. this._renderOverlay(canvasToDrawOn);
  7560. if (this.controlsAboveOverlay && this.interactive) {
  7561. this.drawControls(canvasToDrawOn);
  7562. }
  7563. this.fire('after:render');
  7564. return this;
  7565. },
  7566. /**
  7567. * @private
  7568. * @param {CanvasRenderingContext2D} ctx Context to render on
  7569. * @param {fabric.Group} activeGroup
  7570. */
  7571. _renderObjects: function(ctx, activeGroup) {
  7572. var i, length;
  7573. // fast path
  7574. if (!activeGroup) {
  7575. for (i = 0, length = this._objects.length; i < length; ++i) {
  7576. this._draw(ctx, this._objects[i]);
  7577. }
  7578. }
  7579. else {
  7580. for (i = 0, length = this._objects.length; i < length; ++i) {
  7581. if (this._objects[i] && !activeGroup.contains(this._objects[i])) {
  7582. this._draw(ctx, this._objects[i]);
  7583. }
  7584. }
  7585. }
  7586. },
  7587. /**
  7588. * @private
  7589. * @param {CanvasRenderingContext2D} ctx Context to render on
  7590. * @param {fabric.Group} activeGroup
  7591. */
  7592. _renderActiveGroup: function(ctx, activeGroup) {
  7593. // delegate rendering to group selection (if one exists)
  7594. if (activeGroup) {
  7595. //Store objects in group preserving order, then replace
  7596. var sortedObjects = [];
  7597. this.forEachObject(function (object) {
  7598. if (activeGroup.contains(object)) {
  7599. sortedObjects.push(object);
  7600. }
  7601. });
  7602. activeGroup._set('objects', sortedObjects);
  7603. this._draw(ctx, activeGroup);
  7604. }
  7605. },
  7606. /**
  7607. * @private
  7608. * @param {CanvasRenderingContext2D} ctx Context to render on
  7609. */
  7610. _renderBackground: function(ctx) {
  7611. if (this.backgroundColor) {
  7612. ctx.fillStyle = this.backgroundColor.toLive
  7613. ? this.backgroundColor.toLive(ctx)
  7614. : this.backgroundColor;
  7615. ctx.fillRect(
  7616. this.backgroundColor.offsetX || 0,
  7617. this.backgroundColor.offsetY || 0,
  7618. this.width,
  7619. this.height);
  7620. }
  7621. if (this.backgroundImage) {
  7622. this._draw(ctx, this.backgroundImage);
  7623. }
  7624. },
  7625. /**
  7626. * @private
  7627. * @param {CanvasRenderingContext2D} ctx Context to render on
  7628. */
  7629. _renderOverlay: function(ctx) {
  7630. if (this.overlayColor) {
  7631. ctx.fillStyle = this.overlayColor.toLive
  7632. ? this.overlayColor.toLive(ctx)
  7633. : this.overlayColor;
  7634. ctx.fillRect(
  7635. this.overlayColor.offsetX || 0,
  7636. this.overlayColor.offsetY || 0,
  7637. this.width,
  7638. this.height);
  7639. }
  7640. if (this.overlayImage) {
  7641. this._draw(ctx, this.overlayImage);
  7642. }
  7643. },
  7644. /**
  7645. * Method to render only the top canvas.
  7646. * Also used to render the group selection box.
  7647. * @return {fabric.Canvas} thisArg
  7648. * @chainable
  7649. */
  7650. renderTop: function () {
  7651. var ctx = this.contextTop || this.contextContainer;
  7652. this.clearContext(ctx);
  7653. // we render the top context - last object
  7654. if (this.selection && this._groupSelector) {
  7655. this._drawSelection();
  7656. }
  7657. // delegate rendering to group selection if one exists
  7658. // used for drawing selection borders/controls
  7659. var activeGroup = this.getActiveGroup();
  7660. if (activeGroup) {
  7661. activeGroup.render(ctx);
  7662. }
  7663. this._renderOverlay(ctx);
  7664. this.fire('after:render');
  7665. return this;
  7666. },
  7667. /**
  7668. * Returns coordinates of a center of canvas.
  7669. * Returned value is an object with top and left properties
  7670. * @return {Object} object with "top" and "left" number values
  7671. */
  7672. getCenter: function () {
  7673. return {
  7674. top: this.getHeight() / 2,
  7675. left: this.getWidth() / 2
  7676. };
  7677. },
  7678. /**
  7679. * Centers object horizontally.
  7680. * You might need to call `setCoords` on an object after centering, to update controls area.
  7681. * @param {fabric.Object} object Object to center horizontally
  7682. * @return {fabric.Canvas} thisArg
  7683. */
  7684. centerObjectH: function (object) {
  7685. this._centerObject(object, new fabric.Point(this.getCenter().left, object.getCenterPoint().y));
  7686. this.renderAll();
  7687. return this;
  7688. },
  7689. /**
  7690. * Centers object vertically.
  7691. * You might need to call `setCoords` on an object after centering, to update controls area.
  7692. * @param {fabric.Object} object Object to center vertically
  7693. * @return {fabric.Canvas} thisArg
  7694. * @chainable
  7695. */
  7696. centerObjectV: function (object) {
  7697. this._centerObject(object, new fabric.Point(object.getCenterPoint().x, this.getCenter().top));
  7698. this.renderAll();
  7699. return this;
  7700. },
  7701. /**
  7702. * Centers object vertically and horizontally.
  7703. * You might need to call `setCoords` on an object after centering, to update controls area.
  7704. * @param {fabric.Object} object Object to center vertically and horizontally
  7705. * @return {fabric.Canvas} thisArg
  7706. * @chainable
  7707. */
  7708. centerObject: function(object) {
  7709. var center = this.getCenter();
  7710. this._centerObject(object, new fabric.Point(center.left, center.top));
  7711. this.renderAll();
  7712. return this;
  7713. },
  7714. /**
  7715. * @private
  7716. * @param {fabric.Object} object Object to center
  7717. * @param {fabric.Point} center Center point
  7718. * @return {fabric.Canvas} thisArg
  7719. * @chainable
  7720. */
  7721. _centerObject: function(object, center) {
  7722. object.setPositionByOrigin(center, 'center', 'center');
  7723. return this;
  7724. },
  7725. /**
  7726. * Returs dataless JSON representation of canvas
  7727. * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
  7728. * @return {String} json string
  7729. */
  7730. toDatalessJSON: function (propertiesToInclude) {
  7731. return this.toDatalessObject(propertiesToInclude);
  7732. },
  7733. /**
  7734. * Returns object representation of canvas
  7735. * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
  7736. * @return {Object} object representation of an instance
  7737. */
  7738. toObject: function (propertiesToInclude) {
  7739. return this._toObjectMethod('toObject', propertiesToInclude);
  7740. },
  7741. /**
  7742. * Returns dataless object representation of canvas
  7743. * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
  7744. * @return {Object} object representation of an instance
  7745. */
  7746. toDatalessObject: function (propertiesToInclude) {
  7747. return this._toObjectMethod('toDatalessObject', propertiesToInclude);
  7748. },
  7749. /**
  7750. * @private
  7751. */
  7752. _toObjectMethod: function (methodName, propertiesToInclude) {
  7753. var activeGroup = this.getActiveGroup();
  7754. if (activeGroup) {
  7755. this.discardActiveGroup();
  7756. }
  7757. var data = {
  7758. objects: this._toObjects(methodName, propertiesToInclude)
  7759. };
  7760. extend(data, this.__serializeBgOverlay());
  7761. fabric.util.populateWithProperties(this, data, propertiesToInclude);
  7762. if (activeGroup) {
  7763. this.setActiveGroup(new fabric.Group(activeGroup.getObjects(), {
  7764. originX: 'center',
  7765. originY: 'center'
  7766. }));
  7767. activeGroup.forEachObject(function(o) {
  7768. o.set('active', true);
  7769. });
  7770. if (this._currentTransform) {
  7771. this._currentTransform.target = this.getActiveGroup();
  7772. }
  7773. }
  7774. return data;
  7775. },
  7776. /**
  7777. * @private
  7778. */
  7779. _toObjects: function(methodName, propertiesToInclude) {
  7780. return this.getObjects().map(function(instance) {
  7781. return this._toObject(instance, methodName, propertiesToInclude);
  7782. }, this);
  7783. },
  7784. /**
  7785. * @private
  7786. */
  7787. _toObject: function(instance, methodName, propertiesToInclude) {
  7788. var originalValue;
  7789. if (!this.includeDefaultValues) {
  7790. originalValue = instance.includeDefaultValues;
  7791. instance.includeDefaultValues = false;
  7792. }
  7793. var object = instance[methodName](propertiesToInclude);
  7794. if (!this.includeDefaultValues) {
  7795. instance.includeDefaultValues = originalValue;
  7796. }
  7797. return object;
  7798. },
  7799. /**
  7800. * @private
  7801. */
  7802. __serializeBgOverlay: function() {
  7803. var data = {
  7804. background: (this.backgroundColor && this.backgroundColor.toObject)
  7805. ? this.backgroundColor.toObject()
  7806. : this.backgroundColor
  7807. };
  7808. if (this.overlayColor) {
  7809. data.overlay = this.overlayColor.toObject
  7810. ? this.overlayColor.toObject()
  7811. : this.overlayColor;
  7812. }
  7813. if (this.backgroundImage) {
  7814. data.backgroundImage = this.backgroundImage.toObject();
  7815. }
  7816. if (this.overlayImage) {
  7817. data.overlayImage = this.overlayImage.toObject();
  7818. }
  7819. return data;
  7820. },
  7821. /* _TO_SVG_START_ */
  7822. /**
  7823. * When true, getSvgTransform() will apply the StaticCanvas.viewportTransform to the SVG transformation. When true,
  7824. * a zoomed canvas will then produce zoomed SVG output.
  7825. * @type Boolean
  7826. * @default
  7827. */
  7828. svgViewportTransformation: true,
  7829. /**
  7830. * Returns SVG representation of canvas
  7831. * @function
  7832. * @param {Object} [options] Options object for SVG output
  7833. * @param {Boolean} [options.suppressPreamble=false] If true xml tag is not included
  7834. * @param {Object} [options.viewBox] SVG viewbox object
  7835. * @param {Number} [options.viewBox.x] x-cooridnate of viewbox
  7836. * @param {Number} [options.viewBox.y] y-coordinate of viewbox
  7837. * @param {Number} [options.viewBox.width] Width of viewbox
  7838. * @param {Number} [options.viewBox.height] Height of viewbox
  7839. * @param {String} [options.encoding=UTF-8] Encoding of SVG output
  7840. * @param {Function} [reviver] Method for further parsing of svg elements, called after each fabric object converted into svg representation.
  7841. * @return {String} SVG string
  7842. * @tutorial {@link http://fabricjs.com/fabric-intro-part-3/#serialization}
  7843. * @see {@link http://jsfiddle.net/fabricjs/jQ3ZZ/|jsFiddle demo}
  7844. * @example <caption>Normal SVG output</caption>
  7845. * var svg = canvas.toSVG();
  7846. * @example <caption>SVG output without preamble (without &lt;?xml ../>)</caption>
  7847. * var svg = canvas.toSVG({suppressPreamble: true});
  7848. * @example <caption>SVG output with viewBox attribute</caption>
  7849. * var svg = canvas.toSVG({
  7850. * viewBox: {
  7851. * x: 100,
  7852. * y: 100,
  7853. * width: 200,
  7854. * height: 300
  7855. * }
  7856. * });
  7857. * @example <caption>SVG output with different encoding (default: UTF-8)</caption>
  7858. * var svg = canvas.toSVG({encoding: 'ISO-8859-1'});
  7859. * @example <caption>Modify SVG output with reviver function</caption>
  7860. * var svg = canvas.toSVG(null, function(svg) {
  7861. * return svg.replace('stroke-dasharray: ; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; ', '');
  7862. * });
  7863. */
  7864. toSVG: function(options, reviver) {
  7865. options || (options = { });
  7866. var markup = [];
  7867. this._setSVGPreamble(markup, options);
  7868. this._setSVGHeader(markup, options);
  7869. this._setSVGBgOverlayColor(markup, 'backgroundColor');
  7870. this._setSVGBgOverlayImage(markup, 'backgroundImage');
  7871. this._setSVGObjects(markup, reviver);
  7872. this._setSVGBgOverlayColor(markup, 'overlayColor');
  7873. this._setSVGBgOverlayImage(markup, 'overlayImage');
  7874. markup.push('</svg>');
  7875. return markup.join('');
  7876. },
  7877. /**
  7878. * @private
  7879. */
  7880. _setSVGPreamble: function(markup, options) {
  7881. if (!options.suppressPreamble) {
  7882. markup.push(
  7883. '<?xml version="1.0" encoding="', (options.encoding || 'UTF-8'), '" standalone="no" ?>',
  7884. '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" ',
  7885. '"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n'
  7886. );
  7887. }
  7888. },
  7889. /**
  7890. * @private
  7891. */
  7892. _setSVGHeader: function(markup, options) {
  7893. var width, height, vpt;
  7894. if (options.viewBox) {
  7895. width = options.viewBox.width;
  7896. height = options.viewBox.height;
  7897. }
  7898. else {
  7899. width = this.width;
  7900. height = this.height;
  7901. if (!this.svgViewportTransformation) {
  7902. vpt = this.viewportTransform;
  7903. width /= vpt[0];
  7904. height /= vpt[3];
  7905. }
  7906. }
  7907. markup.push(
  7908. '<svg ',
  7909. 'xmlns="http://www.w3.org/2000/svg" ',
  7910. 'xmlns:xlink="http://www.w3.org/1999/xlink" ',
  7911. 'version="1.1" ',
  7912. 'width="', width, '" ',
  7913. 'height="', height, '" ',
  7914. (this.backgroundColor && !this.backgroundColor.toLive
  7915. ? 'style="background-color: ' + this.backgroundColor + '" '
  7916. : null),
  7917. (options.viewBox
  7918. ? 'viewBox="' +
  7919. options.viewBox.x + ' ' +
  7920. options.viewBox.y + ' ' +
  7921. options.viewBox.width + ' ' +
  7922. options.viewBox.height + '" '
  7923. : null),
  7924. 'xml:space="preserve">',
  7925. '<desc>Created with Fabric.js ', fabric.version, '</desc>',
  7926. '<defs>',
  7927. fabric.createSVGFontFacesMarkup(this.getObjects()),
  7928. fabric.createSVGRefElementsMarkup(this),
  7929. '</defs>'
  7930. );
  7931. },
  7932. /**
  7933. * @private
  7934. */
  7935. _setSVGObjects: function(markup, reviver) {
  7936. var activeGroup = this.getActiveGroup();
  7937. if (activeGroup) {
  7938. this.discardActiveGroup();
  7939. }
  7940. for (var i = 0, objects = this.getObjects(), len = objects.length; i < len; i++) {
  7941. markup.push(objects[i].toSVG(reviver));
  7942. }
  7943. if (activeGroup) {
  7944. this.setActiveGroup(new fabric.Group(activeGroup.getObjects()));
  7945. activeGroup.forEachObject(function(o) {
  7946. o.set('active', true);
  7947. });
  7948. }
  7949. },
  7950. /**
  7951. * @private
  7952. */
  7953. _setSVGBgOverlayImage: function(markup, property) {
  7954. if (this[property] && this[property].toSVG) {
  7955. markup.push(this[property].toSVG());
  7956. }
  7957. },
  7958. /**
  7959. * @private
  7960. */
  7961. _setSVGBgOverlayColor: function(markup, property) {
  7962. if (this[property] && this[property].source) {
  7963. markup.push(
  7964. '<rect x="', this[property].offsetX, '" y="', this[property].offsetY, '" ',
  7965. 'width="',
  7966. (this[property].repeat === 'repeat-y' || this[property].repeat === 'no-repeat'
  7967. ? this[property].source.width
  7968. : this.width),
  7969. '" height="',
  7970. (this[property].repeat === 'repeat-x' || this[property].repeat === 'no-repeat'
  7971. ? this[property].source.height
  7972. : this.height),
  7973. '" fill="url(#' + property + 'Pattern)"',
  7974. '></rect>'
  7975. );
  7976. }
  7977. else if (this[property] && property === 'overlayColor') {
  7978. markup.push(
  7979. '<rect x="0" y="0" ',
  7980. 'width="', this.width,
  7981. '" height="', this.height,
  7982. '" fill="', this[property], '"',
  7983. '></rect>'
  7984. );
  7985. }
  7986. },
  7987. /* _TO_SVG_END_ */
  7988. /**
  7989. * Moves an object to the bottom of the stack of drawn objects
  7990. * @param {fabric.Object} object Object to send to back
  7991. * @return {fabric.Canvas} thisArg
  7992. * @chainable
  7993. */
  7994. sendToBack: function (object) {
  7995. removeFromArray(this._objects, object);
  7996. this._objects.unshift(object);
  7997. return this.renderAll && this.renderAll();
  7998. },
  7999. /**
  8000. * Moves an object to the top of the stack of drawn objects
  8001. * @param {fabric.Object} object Object to send
  8002. * @return {fabric.Canvas} thisArg
  8003. * @chainable
  8004. */
  8005. bringToFront: function (object) {
  8006. removeFromArray(this._objects, object);
  8007. this._objects.push(object);
  8008. return this.renderAll && this.renderAll();
  8009. },
  8010. /**
  8011. * Moves an object down in stack of drawn objects
  8012. * @param {fabric.Object} object Object to send
  8013. * @param {Boolean} [intersecting] If `true`, send object behind next lower intersecting object
  8014. * @return {fabric.Canvas} thisArg
  8015. * @chainable
  8016. */
  8017. sendBackwards: function (object, intersecting) {
  8018. var idx = this._objects.indexOf(object);
  8019. // if object is not on the bottom of stack
  8020. if (idx !== 0) {
  8021. var newIdx = this._findNewLowerIndex(object, idx, intersecting);
  8022. removeFromArray(this._objects, object);
  8023. this._objects.splice(newIdx, 0, object);
  8024. this.renderAll && this.renderAll();
  8025. }
  8026. return this;
  8027. },
  8028. /**
  8029. * @private
  8030. */
  8031. _findNewLowerIndex: function(object, idx, intersecting) {
  8032. var newIdx;
  8033. if (intersecting) {
  8034. newIdx = idx;
  8035. // traverse down the stack looking for the nearest intersecting object
  8036. for (var i = idx - 1; i >= 0; --i) {
  8037. var isIntersecting = object.intersectsWithObject(this._objects[i]) ||
  8038. object.isContainedWithinObject(this._objects[i]) ||
  8039. this._objects[i].isContainedWithinObject(object);
  8040. if (isIntersecting) {
  8041. newIdx = i;
  8042. break;
  8043. }
  8044. }
  8045. }
  8046. else {
  8047. newIdx = idx - 1;
  8048. }
  8049. return newIdx;
  8050. },
  8051. /**
  8052. * Moves an object up in stack of drawn objects
  8053. * @param {fabric.Object} object Object to send
  8054. * @param {Boolean} [intersecting] If `true`, send object in front of next upper intersecting object
  8055. * @return {fabric.Canvas} thisArg
  8056. * @chainable
  8057. */
  8058. bringForward: function (object, intersecting) {
  8059. var idx = this._objects.indexOf(object);
  8060. // if object is not on top of stack (last item in an array)
  8061. if (idx !== this._objects.length - 1) {
  8062. var newIdx = this._findNewUpperIndex(object, idx, intersecting);
  8063. removeFromArray(this._objects, object);
  8064. this._objects.splice(newIdx, 0, object);
  8065. this.renderAll && this.renderAll();
  8066. }
  8067. return this;
  8068. },
  8069. /**
  8070. * @private
  8071. */
  8072. _findNewUpperIndex: function(object, idx, intersecting) {
  8073. var newIdx;
  8074. if (intersecting) {
  8075. newIdx = idx;
  8076. // traverse up the stack looking for the nearest intersecting object
  8077. for (var i = idx + 1; i < this._objects.length; ++i) {
  8078. var isIntersecting = object.intersectsWithObject(this._objects[i]) ||
  8079. object.isContainedWithinObject(this._objects[i]) ||
  8080. this._objects[i].isContainedWithinObject(object);
  8081. if (isIntersecting) {
  8082. newIdx = i;
  8083. break;
  8084. }
  8085. }
  8086. }
  8087. else {
  8088. newIdx = idx + 1;
  8089. }
  8090. return newIdx;
  8091. },
  8092. /**
  8093. * Moves an object to specified level in stack of drawn objects
  8094. * @param {fabric.Object} object Object to send
  8095. * @param {Number} index Position to move to
  8096. * @return {fabric.Canvas} thisArg
  8097. * @chainable
  8098. */
  8099. moveTo: function (object, index) {
  8100. removeFromArray(this._objects, object);
  8101. this._objects.splice(index, 0, object);
  8102. return this.renderAll && this.renderAll();
  8103. },
  8104. /**
  8105. * Clears a canvas element and removes all event listeners
  8106. * @return {fabric.Canvas} thisArg
  8107. * @chainable
  8108. */
  8109. dispose: function () {
  8110. this.clear();
  8111. this.interactive && this.removeListeners();
  8112. return this;
  8113. },
  8114. /**
  8115. * Returns a string representation of an instance
  8116. * @return {String} string representation of an instance
  8117. */
  8118. toString: function () {
  8119. return '#<fabric.Canvas (' + this.complexity() + '): ' +
  8120. '{ objects: ' + this.getObjects().length + ' }>';
  8121. }
  8122. });
  8123. extend(fabric.StaticCanvas.prototype, fabric.Observable);
  8124. extend(fabric.StaticCanvas.prototype, fabric.Collection);
  8125. extend(fabric.StaticCanvas.prototype, fabric.DataURLExporter);
  8126. extend(fabric.StaticCanvas, /** @lends fabric.StaticCanvas */ {
  8127. /**
  8128. * @static
  8129. * @type String
  8130. * @default
  8131. */
  8132. EMPTY_JSON: '{"objects": [], "background": "white"}',
  8133. /**
  8134. * Provides a way to check support of some of the canvas methods
  8135. * (either those of HTMLCanvasElement itself, or rendering context)
  8136. *
  8137. * @param {String} methodName Method to check support for;
  8138. * Could be one of "getImageData", "toDataURL", "toDataURLWithQuality" or "setLineDash"
  8139. * @return {Boolean | null} `true` if method is supported (or at least exists),
  8140. * `null` if canvas element or context can not be initialized
  8141. */
  8142. supports: function (methodName) {
  8143. var el = fabric.util.createCanvasElement();
  8144. if (!el || !el.getContext) {
  8145. return null;
  8146. }
  8147. var ctx = el.getContext('2d');
  8148. if (!ctx) {
  8149. return null;
  8150. }
  8151. switch (methodName) {
  8152. case 'getImageData':
  8153. return typeof ctx.getImageData !== 'undefined';
  8154. case 'setLineDash':
  8155. return typeof ctx.setLineDash !== 'undefined';
  8156. case 'toDataURL':
  8157. return typeof el.toDataURL !== 'undefined';
  8158. case 'toDataURLWithQuality':
  8159. try {
  8160. el.toDataURL('image/jpeg', 0);
  8161. return true;
  8162. }
  8163. catch (e) { }
  8164. return false;
  8165. default:
  8166. return null;
  8167. }
  8168. }
  8169. });
  8170. /**
  8171. * Returns JSON representation of canvas
  8172. * @function
  8173. * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
  8174. * @return {String} JSON string
  8175. * @tutorial {@link http://fabricjs.com/fabric-intro-part-3/#serialization}
  8176. * @see {@link http://jsfiddle.net/fabricjs/pec86/|jsFiddle demo}
  8177. * @example <caption>JSON without additional properties</caption>
  8178. * var json = canvas.toJSON();
  8179. * @example <caption>JSON with additional properties included</caption>
  8180. * var json = canvas.toJSON(['lockMovementX', 'lockMovementY', 'lockRotation', 'lockScalingX', 'lockScalingY', 'lockUniScaling']);
  8181. * @example <caption>JSON without default values</caption>
  8182. * canvas.includeDefaultValues = false;
  8183. * var json = canvas.toJSON();
  8184. */
  8185. fabric.StaticCanvas.prototype.toJSON = fabric.StaticCanvas.prototype.toObject;
  8186. })();
  8187. /**
  8188. * BaseBrush class
  8189. * @class fabric.BaseBrush
  8190. * @see {@link http://fabricjs.com/freedrawing/|Freedrawing demo}
  8191. */
  8192. fabric.BaseBrush = fabric.util.createClass(/** @lends fabric.BaseBrush.prototype */ {
  8193. /**
  8194. * Color of a brush
  8195. * @type String
  8196. * @default
  8197. */
  8198. color: 'rgb(0, 0, 0)',
  8199. /**
  8200. * Width of a brush
  8201. * @type Number
  8202. * @default
  8203. */
  8204. width: 1,
  8205. /**
  8206. * Shadow object representing shadow of this shape.
  8207. * <b>Backwards incompatibility note:</b> This property replaces "shadowColor" (String), "shadowOffsetX" (Number),
  8208. * "shadowOffsetY" (Number) and "shadowBlur" (Number) since v1.2.12
  8209. * @type fabric.Shadow
  8210. * @default
  8211. */
  8212. shadow: null,
  8213. /**
  8214. * Line endings style of a brush (one of "butt", "round", "square")
  8215. * @type String
  8216. * @default
  8217. */
  8218. strokeLineCap: 'round',
  8219. /**
  8220. * Corner style of a brush (one of "bevil", "round", "miter")
  8221. * @type String
  8222. * @default
  8223. */
  8224. strokeLineJoin: 'round',
  8225. /**
  8226. * Sets shadow of an object
  8227. * @param {Object|String} [options] Options object or string (e.g. "2px 2px 10px rgba(0,0,0,0.2)")
  8228. * @return {fabric.Object} thisArg
  8229. * @chainable
  8230. */
  8231. setShadow: function(options) {
  8232. this.shadow = new fabric.Shadow(options);
  8233. return this;
  8234. },
  8235. /**
  8236. * Sets brush styles
  8237. * @private
  8238. */
  8239. _setBrushStyles: function() {
  8240. var ctx = this.canvas.contextTop;
  8241. ctx.strokeStyle = this.color;
  8242. ctx.lineWidth = this.width;
  8243. ctx.lineCap = this.strokeLineCap;
  8244. ctx.lineJoin = this.strokeLineJoin;
  8245. },
  8246. /**
  8247. * Sets brush shadow styles
  8248. * @private
  8249. */
  8250. _setShadow: function() {
  8251. if (!this.shadow) {
  8252. return;
  8253. }
  8254. var ctx = this.canvas.contextTop;
  8255. ctx.shadowColor = this.shadow.color;
  8256. ctx.shadowBlur = this.shadow.blur;
  8257. ctx.shadowOffsetX = this.shadow.offsetX;
  8258. ctx.shadowOffsetY = this.shadow.offsetY;
  8259. },
  8260. /**
  8261. * Removes brush shadow styles
  8262. * @private
  8263. */
  8264. _resetShadow: function() {
  8265. var ctx = this.canvas.contextTop;
  8266. ctx.shadowColor = '';
  8267. ctx.shadowBlur = ctx.shadowOffsetX = ctx.shadowOffsetY = 0;
  8268. }
  8269. });
  8270. (function() {
  8271. var utilMin = fabric.util.array.min,
  8272. utilMax = fabric.util.array.max;
  8273. /**
  8274. * PencilBrush class
  8275. * @class fabric.PencilBrush
  8276. * @extends fabric.BaseBrush
  8277. */
  8278. fabric.PencilBrush = fabric.util.createClass(fabric.BaseBrush, /** @lends fabric.PencilBrush.prototype */ {
  8279. /**
  8280. * Constructor
  8281. * @param {fabric.Canvas} canvas
  8282. * @return {fabric.PencilBrush} Instance of a pencil brush
  8283. */
  8284. initialize: function(canvas) {
  8285. this.canvas = canvas;
  8286. this._points = [ ];
  8287. },
  8288. /**
  8289. * Inovoked on mouse down
  8290. * @param {Object} pointer
  8291. */
  8292. onMouseDown: function(pointer) {
  8293. this._prepareForDrawing(pointer);
  8294. // capture coordinates immediately
  8295. // this allows to draw dots (when movement never occurs)
  8296. this._captureDrawingPath(pointer);
  8297. this._render();
  8298. },
  8299. /**
  8300. * Inovoked on mouse move
  8301. * @param {Object} pointer
  8302. */
  8303. onMouseMove: function(pointer) {
  8304. this._captureDrawingPath(pointer);
  8305. // redraw curve
  8306. // clear top canvas
  8307. this.canvas.clearContext(this.canvas.contextTop);
  8308. this._render();
  8309. },
  8310. /**
  8311. * Invoked on mouse up
  8312. */
  8313. onMouseUp: function() {
  8314. this._finalizeAndAddPath();
  8315. },
  8316. /**
  8317. * @private
  8318. * @param {Object} pointer Actual mouse position related to the canvas.
  8319. */
  8320. _prepareForDrawing: function(pointer) {
  8321. var p = new fabric.Point(pointer.x, pointer.y);
  8322. this._reset();
  8323. this._addPoint(p);
  8324. this.canvas.contextTop.moveTo(p.x, p.y);
  8325. },
  8326. /**
  8327. * @private
  8328. * @param {fabric.Point} point Point to be added to points array
  8329. */
  8330. _addPoint: function(point) {
  8331. this._points.push(point);
  8332. },
  8333. /**
  8334. * Clear points array and set contextTop canvas style.
  8335. * @private
  8336. */
  8337. _reset: function() {
  8338. this._points.length = 0;
  8339. this._setBrushStyles();
  8340. this._setShadow();
  8341. },
  8342. /**
  8343. * @private
  8344. * @param {Object} pointer Actual mouse position related to the canvas.
  8345. */
  8346. _captureDrawingPath: function(pointer) {
  8347. var pointerPoint = new fabric.Point(pointer.x, pointer.y);
  8348. this._addPoint(pointerPoint);
  8349. },
  8350. /**
  8351. * Draw a smooth path on the topCanvas using quadraticCurveTo
  8352. * @private
  8353. */
  8354. _render: function() {
  8355. var ctx = this.canvas.contextTop,
  8356. v = this.canvas.viewportTransform,
  8357. p1 = this._points[0],
  8358. p2 = this._points[1];
  8359. ctx.save();
  8360. ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
  8361. ctx.beginPath();
  8362. //if we only have 2 points in the path and they are the same
  8363. //it means that the user only clicked the canvas without moving the mouse
  8364. //then we should be drawing a dot. A path isn't drawn between two identical dots
  8365. //that's why we set them apart a bit
  8366. if (this._points.length === 2 && p1.x === p2.x && p1.y === p2.y) {
  8367. p1.x -= 0.5;
  8368. p2.x += 0.5;
  8369. }
  8370. ctx.moveTo(p1.x, p1.y);
  8371. for (var i = 1, len = this._points.length; i < len; i++) {
  8372. // we pick the point between pi + 1 & pi + 2 as the
  8373. // end point and p1 as our control point.
  8374. var midPoint = p1.midPointFrom(p2);
  8375. ctx.quadraticCurveTo(p1.x, p1.y, midPoint.x, midPoint.y);
  8376. p1 = this._points[i];
  8377. p2 = this._points[i + 1];
  8378. }
  8379. // Draw last line as a straight line while
  8380. // we wait for the next point to be able to calculate
  8381. // the bezier control point
  8382. ctx.lineTo(p1.x, p1.y);
  8383. ctx.stroke();
  8384. ctx.restore();
  8385. },
  8386. /**
  8387. * Return an SVG path based on our captured points and their bounding box
  8388. * @private
  8389. */
  8390. _getSVGPathData: function() {
  8391. this.box = this.getPathBoundingBox(this._points);
  8392. return this.convertPointsToSVGPath(
  8393. this._points, this.box.minX, this.box.minY);
  8394. },
  8395. /**
  8396. * Returns bounding box of a path based on given points
  8397. * @param {Array} points Array of points
  8398. * @return {Object} Object with minX, minY, maxX, maxY
  8399. */
  8400. getPathBoundingBox: function(points) {
  8401. var xBounds = [],
  8402. yBounds = [],
  8403. p1 = points[0],
  8404. p2 = points[1],
  8405. startPoint = p1;
  8406. for (var i = 1, len = points.length; i < len; i++) {
  8407. var midPoint = p1.midPointFrom(p2);
  8408. // with startPoint, p1 as control point, midpoint as end point
  8409. xBounds.push(startPoint.x);
  8410. xBounds.push(midPoint.x);
  8411. yBounds.push(startPoint.y);
  8412. yBounds.push(midPoint.y);
  8413. p1 = points[i];
  8414. p2 = points[i + 1];
  8415. startPoint = midPoint;
  8416. }
  8417. xBounds.push(p1.x);
  8418. yBounds.push(p1.y);
  8419. return {
  8420. minX: utilMin(xBounds),
  8421. minY: utilMin(yBounds),
  8422. maxX: utilMax(xBounds),
  8423. maxY: utilMax(yBounds)
  8424. };
  8425. },
  8426. /**
  8427. * Converts points to SVG path
  8428. * @param {Array} points Array of points
  8429. * @param {Number} minX
  8430. * @param {Number} minY
  8431. * @return {String} SVG path
  8432. */
  8433. convertPointsToSVGPath: function(points, minX, minY) {
  8434. var path = [],
  8435. p1 = new fabric.Point(points[0].x - minX, points[0].y - minY),
  8436. p2 = new fabric.Point(points[1].x - minX, points[1].y - minY);
  8437. path.push('M ', points[0].x - minX, ' ', points[0].y - minY, ' ');
  8438. for (var i = 1, len = points.length; i < len; i++) {
  8439. var midPoint = p1.midPointFrom(p2);
  8440. // p1 is our bezier control point
  8441. // midpoint is our endpoint
  8442. // start point is p(i-1) value.
  8443. path.push('Q ', p1.x, ' ', p1.y, ' ', midPoint.x, ' ', midPoint.y, ' ');
  8444. p1 = new fabric.Point(points[i].x - minX, points[i].y - minY);
  8445. if ((i + 1) < points.length) {
  8446. p2 = new fabric.Point(points[i + 1].x - minX, points[i + 1].y - minY);
  8447. }
  8448. }
  8449. path.push('L ', p1.x, ' ', p1.y, ' ');
  8450. return path;
  8451. },
  8452. /**
  8453. * Creates fabric.Path object to add on canvas
  8454. * @param {String} pathData Path data
  8455. * @return {fabric.Path} Path to add on canvas
  8456. */
  8457. createPath: function(pathData) {
  8458. var path = new fabric.Path(pathData);
  8459. path.fill = null;
  8460. path.stroke = this.color;
  8461. path.strokeWidth = this.width;
  8462. path.strokeLineCap = this.strokeLineCap;
  8463. path.strokeLineJoin = this.strokeLineJoin;
  8464. if (this.shadow) {
  8465. this.shadow.affectStroke = true;
  8466. path.setShadow(this.shadow);
  8467. }
  8468. return path;
  8469. },
  8470. /**
  8471. * On mouseup after drawing the path on contextTop canvas
  8472. * we use the points captured to create an new fabric path object
  8473. * and add it to the fabric canvas.
  8474. */
  8475. _finalizeAndAddPath: function() {
  8476. var ctx = this.canvas.contextTop;
  8477. ctx.closePath();
  8478. var pathData = this._getSVGPathData().join('');
  8479. if (pathData === 'M 0 0 Q 0 0 0 0 L 0 0') {
  8480. // do not create 0 width/height paths, as they are
  8481. // rendered inconsistently across browsers
  8482. // Firefox 4, for example, renders a dot,
  8483. // whereas Chrome 10 renders nothing
  8484. this.canvas.renderAll();
  8485. return;
  8486. }
  8487. // set path origin coordinates based on our bounding box
  8488. var originLeft = this.box.minX + (this.box.maxX - this.box.minX) / 2,
  8489. originTop = this.box.minY + (this.box.maxY - this.box.minY) / 2;
  8490. this.canvas.contextTop.arc(originLeft, originTop, 3, 0, Math.PI * 2, false);
  8491. var path = this.createPath(pathData);
  8492. path.set({
  8493. left: originLeft,
  8494. top: originTop,
  8495. originX: 'center',
  8496. originY: 'center'
  8497. });
  8498. this.canvas.add(path);
  8499. path.setCoords();
  8500. this.canvas.clearContext(this.canvas.contextTop);
  8501. this._resetShadow();
  8502. this.canvas.renderAll();
  8503. // fire event 'path' created
  8504. this.canvas.fire('path:created', { path: path });
  8505. }
  8506. });
  8507. })();
  8508. /**
  8509. * CircleBrush class
  8510. * @class fabric.CircleBrush
  8511. */
  8512. fabric.CircleBrush = fabric.util.createClass(fabric.BaseBrush, /** @lends fabric.CircleBrush.prototype */ {
  8513. /**
  8514. * Width of a brush
  8515. * @type Number
  8516. * @default
  8517. */
  8518. width: 10,
  8519. /**
  8520. * Constructor
  8521. * @param {fabric.Canvas} canvas
  8522. * @return {fabric.CircleBrush} Instance of a circle brush
  8523. */
  8524. initialize: function(canvas) {
  8525. this.canvas = canvas;
  8526. this.points = [ ];
  8527. },
  8528. /**
  8529. * Invoked inside on mouse down and mouse move
  8530. * @param {Object} pointer
  8531. */
  8532. drawDot: function(pointer) {
  8533. var point = this.addPoint(pointer),
  8534. ctx = this.canvas.contextTop,
  8535. v = this.canvas.viewportTransform;
  8536. ctx.save();
  8537. ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
  8538. ctx.fillStyle = point.fill;
  8539. ctx.beginPath();
  8540. ctx.arc(point.x, point.y, point.radius, 0, Math.PI * 2, false);
  8541. ctx.closePath();
  8542. ctx.fill();
  8543. ctx.restore();
  8544. },
  8545. /**
  8546. * Invoked on mouse down
  8547. */
  8548. onMouseDown: function(pointer) {
  8549. this.points.length = 0;
  8550. this.canvas.clearContext(this.canvas.contextTop);
  8551. this._setShadow();
  8552. this.drawDot(pointer);
  8553. },
  8554. /**
  8555. * Invoked on mouse move
  8556. * @param {Object} pointer
  8557. */
  8558. onMouseMove: function(pointer) {
  8559. this.drawDot(pointer);
  8560. },
  8561. /**
  8562. * Invoked on mouse up
  8563. */
  8564. onMouseUp: function() {
  8565. var originalRenderOnAddRemove = this.canvas.renderOnAddRemove;
  8566. this.canvas.renderOnAddRemove = false;
  8567. var circles = [ ];
  8568. for (var i = 0, len = this.points.length; i < len; i++) {
  8569. var point = this.points[i],
  8570. circle = new fabric.Circle({
  8571. radius: point.radius,
  8572. left: point.x,
  8573. top: point.y,
  8574. originX: 'center',
  8575. originY: 'center',
  8576. fill: point.fill
  8577. });
  8578. this.shadow && circle.setShadow(this.shadow);
  8579. circles.push(circle);
  8580. }
  8581. var group = new fabric.Group(circles, { originX: 'center', originY: 'center' });
  8582. group.canvas = this.canvas;
  8583. this.canvas.add(group);
  8584. this.canvas.fire('path:created', { path: group });
  8585. this.canvas.clearContext(this.canvas.contextTop);
  8586. this._resetShadow();
  8587. this.canvas.renderOnAddRemove = originalRenderOnAddRemove;
  8588. this.canvas.renderAll();
  8589. },
  8590. /**
  8591. * @param {Object} pointer
  8592. * @return {fabric.Point} Just added pointer point
  8593. */
  8594. addPoint: function(pointer) {
  8595. var pointerPoint = new fabric.Point(pointer.x, pointer.y),
  8596. circleRadius = fabric.util.getRandomInt(
  8597. Math.max(0, this.width - 20), this.width + 20) / 2,
  8598. circleColor = new fabric.Color(this.color)
  8599. .setAlpha(fabric.util.getRandomInt(0, 100) / 100)
  8600. .toRgba();
  8601. pointerPoint.radius = circleRadius;
  8602. pointerPoint.fill = circleColor;
  8603. this.points.push(pointerPoint);
  8604. return pointerPoint;
  8605. }
  8606. });
  8607. /**
  8608. * SprayBrush class
  8609. * @class fabric.SprayBrush
  8610. */
  8611. fabric.SprayBrush = fabric.util.createClass( fabric.BaseBrush, /** @lends fabric.SprayBrush.prototype */ {
  8612. /**
  8613. * Width of a spray
  8614. * @type Number
  8615. * @default
  8616. */
  8617. width: 10,
  8618. /**
  8619. * Density of a spray (number of dots per chunk)
  8620. * @type Number
  8621. * @default
  8622. */
  8623. density: 20,
  8624. /**
  8625. * Width of spray dots
  8626. * @type Number
  8627. * @default
  8628. */
  8629. dotWidth: 1,
  8630. /**
  8631. * Width variance of spray dots
  8632. * @type Number
  8633. * @default
  8634. */
  8635. dotWidthVariance: 1,
  8636. /**
  8637. * Whether opacity of a dot should be random
  8638. * @type Boolean
  8639. * @default
  8640. */
  8641. randomOpacity: false,
  8642. /**
  8643. * Whether overlapping dots (rectangles) should be removed (for performance reasons)
  8644. * @type Boolean
  8645. * @default
  8646. */
  8647. optimizeOverlapping: true,
  8648. /**
  8649. * Constructor
  8650. * @param {fabric.Canvas} canvas
  8651. * @return {fabric.SprayBrush} Instance of a spray brush
  8652. */
  8653. initialize: function(canvas) {
  8654. this.canvas = canvas;
  8655. this.sprayChunks = [ ];
  8656. },
  8657. /**
  8658. * Invoked on mouse down
  8659. * @param {Object} pointer
  8660. */
  8661. onMouseDown: function(pointer) {
  8662. this.sprayChunks.length = 0;
  8663. this.canvas.clearContext(this.canvas.contextTop);
  8664. this._setShadow();
  8665. this.addSprayChunk(pointer);
  8666. this.render();
  8667. },
  8668. /**
  8669. * Invoked on mouse move
  8670. * @param {Object} pointer
  8671. */
  8672. onMouseMove: function(pointer) {
  8673. this.addSprayChunk(pointer);
  8674. this.render();
  8675. },
  8676. /**
  8677. * Invoked on mouse up
  8678. */
  8679. onMouseUp: function() {
  8680. var originalRenderOnAddRemove = this.canvas.renderOnAddRemove;
  8681. this.canvas.renderOnAddRemove = false;
  8682. var rects = [ ];
  8683. for (var i = 0, ilen = this.sprayChunks.length; i < ilen; i++) {
  8684. var sprayChunk = this.sprayChunks[i];
  8685. for (var j = 0, jlen = sprayChunk.length; j < jlen; j++) {
  8686. var rect = new fabric.Rect({
  8687. width: sprayChunk[j].width,
  8688. height: sprayChunk[j].width,
  8689. left: sprayChunk[j].x + 1,
  8690. top: sprayChunk[j].y + 1,
  8691. originX: 'center',
  8692. originY: 'center',
  8693. fill: this.color
  8694. });
  8695. this.shadow && rect.setShadow(this.shadow);
  8696. rects.push(rect);
  8697. }
  8698. }
  8699. if (this.optimizeOverlapping) {
  8700. rects = this._getOptimizedRects(rects);
  8701. }
  8702. var group = new fabric.Group(rects, { originX: 'center', originY: 'center' });
  8703. group.canvas = this.canvas;
  8704. this.canvas.add(group);
  8705. this.canvas.fire('path:created', { path: group });
  8706. this.canvas.clearContext(this.canvas.contextTop);
  8707. this._resetShadow();
  8708. this.canvas.renderOnAddRemove = originalRenderOnAddRemove;
  8709. this.canvas.renderAll();
  8710. },
  8711. /**
  8712. * @private
  8713. * @param {Array} rects
  8714. */
  8715. _getOptimizedRects: function(rects) {
  8716. // avoid creating duplicate rects at the same coordinates
  8717. var uniqueRects = { }, key;
  8718. for (var i = 0, len = rects.length; i < len; i++) {
  8719. key = rects[i].left + '' + rects[i].top;
  8720. if (!uniqueRects[key]) {
  8721. uniqueRects[key] = rects[i];
  8722. }
  8723. }
  8724. var uniqueRectsArray = [ ];
  8725. for (key in uniqueRects) {
  8726. uniqueRectsArray.push(uniqueRects[key]);
  8727. }
  8728. return uniqueRectsArray;
  8729. },
  8730. /**
  8731. * Renders brush
  8732. */
  8733. render: function() {
  8734. var ctx = this.canvas.contextTop;
  8735. ctx.fillStyle = this.color;
  8736. var v = this.canvas.viewportTransform;
  8737. ctx.save();
  8738. ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
  8739. for (var i = 0, len = this.sprayChunkPoints.length; i < len; i++) {
  8740. var point = this.sprayChunkPoints[i];
  8741. if (typeof point.opacity !== 'undefined') {
  8742. ctx.globalAlpha = point.opacity;
  8743. }
  8744. ctx.fillRect(point.x, point.y, point.width, point.width);
  8745. }
  8746. ctx.restore();
  8747. },
  8748. /**
  8749. * @param {Object} pointer
  8750. */
  8751. addSprayChunk: function(pointer) {
  8752. this.sprayChunkPoints = [ ];
  8753. var x, y, width, radius = this.width / 2;
  8754. for (var i = 0; i < this.density; i++) {
  8755. x = fabric.util.getRandomInt(pointer.x - radius, pointer.x + radius);
  8756. y = fabric.util.getRandomInt(pointer.y - radius, pointer.y + radius);
  8757. if (this.dotWidthVariance) {
  8758. width = fabric.util.getRandomInt(
  8759. // bottom clamp width to 1
  8760. Math.max(1, this.dotWidth - this.dotWidthVariance),
  8761. this.dotWidth + this.dotWidthVariance);
  8762. }
  8763. else {
  8764. width = this.dotWidth;
  8765. }
  8766. var point = new fabric.Point(x, y);
  8767. point.width = width;
  8768. if (this.randomOpacity) {
  8769. point.opacity = fabric.util.getRandomInt(0, 100) / 100;
  8770. }
  8771. this.sprayChunkPoints.push(point);
  8772. }
  8773. this.sprayChunks.push(this.sprayChunkPoints);
  8774. }
  8775. });
  8776. /**
  8777. * PatternBrush class
  8778. * @class fabric.PatternBrush
  8779. * @extends fabric.BaseBrush
  8780. */
  8781. fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fabric.PatternBrush.prototype */ {
  8782. getPatternSrc: function() {
  8783. var dotWidth = 20,
  8784. dotDistance = 5,
  8785. patternCanvas = fabric.document.createElement('canvas'),
  8786. patternCtx = patternCanvas.getContext('2d');
  8787. patternCanvas.width = patternCanvas.height = dotWidth + dotDistance;
  8788. patternCtx.fillStyle = this.color;
  8789. patternCtx.beginPath();
  8790. patternCtx.arc(dotWidth / 2, dotWidth / 2, dotWidth / 2, 0, Math.PI * 2, false);
  8791. patternCtx.closePath();
  8792. patternCtx.fill();
  8793. return patternCanvas;
  8794. },
  8795. getPatternSrcFunction: function() {
  8796. return String(this.getPatternSrc).replace('this.color', '"' + this.color + '"');
  8797. },
  8798. /**
  8799. * Creates "pattern" instance property
  8800. */
  8801. getPattern: function() {
  8802. return this.canvas.contextTop.createPattern(this.source || this.getPatternSrc(), 'repeat');
  8803. },
  8804. /**
  8805. * Sets brush styles
  8806. */
  8807. _setBrushStyles: function() {
  8808. this.callSuper('_setBrushStyles');
  8809. this.canvas.contextTop.strokeStyle = this.getPattern();
  8810. },
  8811. /**
  8812. * Creates path
  8813. */
  8814. createPath: function(pathData) {
  8815. var path = this.callSuper('createPath', pathData);
  8816. path.stroke = new fabric.Pattern({
  8817. source: this.source || this.getPatternSrcFunction()
  8818. });
  8819. return path;
  8820. }
  8821. });
  8822. fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.StaticCanvas.prototype */ {
  8823. /**
  8824. * Exports canvas element to a dataurl image. Note that when multiplier is used, cropping is scaled appropriately
  8825. * @param {Object} [options] Options object
  8826. * @param {String} [options.format=png] The format of the output image. Either "jpeg" or "png"
  8827. * @param {Number} [options.quality=1] Quality level (0..1). Only used for jpeg.
  8828. * @param {Number} [options.multiplier=1] Multiplier to scale by
  8829. * @param {Number} [options.left] Cropping left offset. Introduced in v1.2.14
  8830. * @param {Number} [options.top] Cropping top offset. Introduced in v1.2.14
  8831. * @param {Number} [options.width] Cropping width. Introduced in v1.2.14
  8832. * @param {Number} [options.height] Cropping height. Introduced in v1.2.14
  8833. * @return {String} Returns a data: URL containing a representation of the object in the format specified by options.format
  8834. * @see {@link http://jsfiddle.net/fabricjs/NfZVb/|jsFiddle demo}
  8835. * @example <caption>Generate jpeg dataURL with lower quality</caption>
  8836. * var dataURL = canvas.toDataURL({
  8837. * format: 'jpeg',
  8838. * quality: 0.8
  8839. * });
  8840. * @example <caption>Generate cropped png dataURL (clipping of canvas)</caption>
  8841. * var dataURL = canvas.toDataURL({
  8842. * format: 'png',
  8843. * left: 100,
  8844. * top: 100,
  8845. * width: 200,
  8846. * height: 200
  8847. * });
  8848. * @example <caption>Generate double scaled png dataURL</caption>
  8849. * var dataURL = canvas.toDataURL({
  8850. * format: 'png',
  8851. * multiplier: 2
  8852. * });
  8853. */
  8854. toDataURL: function (options) {
  8855. options || (options = { });
  8856. var format = options.format || 'png',
  8857. quality = options.quality || 1,
  8858. multiplier = options.multiplier || 1,
  8859. cropping = {
  8860. left: options.left,
  8861. top: options.top,
  8862. width: options.width,
  8863. height: options.height
  8864. };
  8865. if (multiplier !== 1) {
  8866. return this.__toDataURLWithMultiplier(format, quality, cropping, multiplier);
  8867. }
  8868. else {
  8869. return this.__toDataURL(format, quality, cropping);
  8870. }
  8871. },
  8872. /**
  8873. * @private
  8874. */
  8875. __toDataURL: function(format, quality, cropping) {
  8876. this.renderAll(true);
  8877. var canvasEl = this.upperCanvasEl || this.lowerCanvasEl,
  8878. croppedCanvasEl = this.__getCroppedCanvas(canvasEl, cropping);
  8879. // to avoid common confusion https://github.com/kangax/fabric.js/issues/806
  8880. if (format === 'jpg') {
  8881. format = 'jpeg';
  8882. }
  8883. var data = (fabric.StaticCanvas.supports('toDataURLWithQuality'))
  8884. ? (croppedCanvasEl || canvasEl).toDataURL('image/' + format, quality)
  8885. : (croppedCanvasEl || canvasEl).toDataURL('image/' + format);
  8886. this.contextTop && this.clearContext(this.contextTop);
  8887. this.renderAll();
  8888. if (croppedCanvasEl) {
  8889. croppedCanvasEl = null;
  8890. }
  8891. return data;
  8892. },
  8893. /**
  8894. * @private
  8895. */
  8896. __getCroppedCanvas: function(canvasEl, cropping) {
  8897. var croppedCanvasEl,
  8898. croppedCtx,
  8899. shouldCrop = 'left' in cropping ||
  8900. 'top' in cropping ||
  8901. 'width' in cropping ||
  8902. 'height' in cropping;
  8903. if (shouldCrop) {
  8904. croppedCanvasEl = fabric.util.createCanvasElement();
  8905. croppedCtx = croppedCanvasEl.getContext('2d');
  8906. croppedCanvasEl.width = cropping.width || this.width;
  8907. croppedCanvasEl.height = cropping.height || this.height;
  8908. croppedCtx.drawImage(canvasEl, -cropping.left || 0, -cropping.top || 0);
  8909. }
  8910. return croppedCanvasEl;
  8911. },
  8912. /**
  8913. * @private
  8914. */
  8915. __toDataURLWithMultiplier: function(format, quality, cropping, multiplier) {
  8916. var origWidth = this.getWidth(),
  8917. origHeight = this.getHeight(),
  8918. scaledWidth = origWidth * multiplier,
  8919. scaledHeight = origHeight * multiplier,
  8920. activeObject = this.getActiveObject(),
  8921. activeGroup = this.getActiveGroup(),
  8922. ctx = this.contextTop || this.contextContainer;
  8923. if (multiplier > 1) {
  8924. this.setWidth(scaledWidth).setHeight(scaledHeight);
  8925. }
  8926. ctx.scale(multiplier, multiplier);
  8927. if (cropping.left) {
  8928. cropping.left *= multiplier;
  8929. }
  8930. if (cropping.top) {
  8931. cropping.top *= multiplier;
  8932. }
  8933. if (cropping.width) {
  8934. cropping.width *= multiplier;
  8935. }
  8936. else if (multiplier < 1) {
  8937. cropping.width = scaledWidth;
  8938. }
  8939. if (cropping.height) {
  8940. cropping.height *= multiplier;
  8941. }
  8942. else if (multiplier < 1) {
  8943. cropping.height = scaledHeight;
  8944. }
  8945. if (activeGroup) {
  8946. // not removing group due to complications with restoring it with correct state afterwords
  8947. this._tempRemoveBordersControlsFromGroup(activeGroup);
  8948. }
  8949. else if (activeObject && this.deactivateAll) {
  8950. this.deactivateAll();
  8951. }
  8952. this.renderAll(true);
  8953. var data = this.__toDataURL(format, quality, cropping);
  8954. // restoring width, height for `renderAll` to draw
  8955. // background properly (while context is scaled)
  8956. this.width = origWidth;
  8957. this.height = origHeight;
  8958. ctx.scale(1 / multiplier, 1 / multiplier);
  8959. this.setWidth(origWidth).setHeight(origHeight);
  8960. if (activeGroup) {
  8961. this._restoreBordersControlsOnGroup(activeGroup);
  8962. }
  8963. else if (activeObject && this.setActiveObject) {
  8964. this.setActiveObject(activeObject);
  8965. }
  8966. this.contextTop && this.clearContext(this.contextTop);
  8967. this.renderAll();
  8968. return data;
  8969. },
  8970. /**
  8971. * Exports canvas element to a dataurl image (allowing to change image size via multiplier).
  8972. * @deprecated since 1.0.13
  8973. * @param {String} format (png|jpeg)
  8974. * @param {Number} multiplier
  8975. * @param {Number} quality (0..1)
  8976. * @return {String}
  8977. */
  8978. toDataURLWithMultiplier: function (format, multiplier, quality) {
  8979. return this.toDataURL({
  8980. format: format,
  8981. multiplier: multiplier,
  8982. quality: quality
  8983. });
  8984. },
  8985. /**
  8986. * @private
  8987. */
  8988. _tempRemoveBordersControlsFromGroup: function(group) {
  8989. group.origHasControls = group.hasControls;
  8990. group.origBorderColor = group.borderColor;
  8991. group.hasControls = true;
  8992. group.borderColor = 'rgba(0,0,0,0)';
  8993. group.forEachObject(function(o) {
  8994. o.origBorderColor = o.borderColor;
  8995. o.borderColor = 'rgba(0,0,0,0)';
  8996. });
  8997. },
  8998. /**
  8999. * @private
  9000. */
  9001. _restoreBordersControlsOnGroup: function(group) {
  9002. group.hideControls = group.origHideControls;
  9003. group.borderColor = group.origBorderColor;
  9004. group.forEachObject(function(o) {
  9005. o.borderColor = o.origBorderColor;
  9006. delete o.origBorderColor;
  9007. });
  9008. }
  9009. });
  9010. fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.StaticCanvas.prototype */ {
  9011. /**
  9012. * Populates canvas with data from the specified dataless JSON.
  9013. * JSON format must conform to the one of {@link fabric.Canvas#toDatalessJSON}
  9014. * @deprecated since 1.2.2
  9015. * @param {String|Object} json JSON string or object
  9016. * @param {Function} callback Callback, invoked when json is parsed
  9017. * and corresponding objects (e.g: {@link fabric.Image})
  9018. * are initialized
  9019. * @param {Function} [reviver] Method for further parsing of JSON elements, called after each fabric object created.
  9020. * @return {fabric.Canvas} instance
  9021. * @chainable
  9022. * @tutorial {@link http://fabricjs.com/fabric-intro-part-3/#deserialization}
  9023. */
  9024. loadFromDatalessJSON: function (json, callback, reviver) {
  9025. return this.loadFromJSON(json, callback, reviver);
  9026. },
  9027. /**
  9028. * Populates canvas with data from the specified JSON.
  9029. * JSON format must conform to the one of {@link fabric.Canvas#toJSON}
  9030. * @param {String|Object} json JSON string or object
  9031. * @param {Function} callback Callback, invoked when json is parsed
  9032. * and corresponding objects (e.g: {@link fabric.Image})
  9033. * are initialized
  9034. * @param {Function} [reviver] Method for further parsing of JSON elements, called after each fabric object created.
  9035. * @return {fabric.Canvas} instance
  9036. * @chainable
  9037. * @tutorial {@link http://fabricjs.com/fabric-intro-part-3/#deserialization}
  9038. * @see {@link http://jsfiddle.net/fabricjs/fmgXt/|jsFiddle demo}
  9039. * @example <caption>loadFromJSON</caption>
  9040. * canvas.loadFromJSON(json, canvas.renderAll.bind(canvas));
  9041. * @example <caption>loadFromJSON with reviver</caption>
  9042. * canvas.loadFromJSON(json, canvas.renderAll.bind(canvas), function(o, object) {
  9043. * // `o` = json object
  9044. * // `object` = fabric.Object instance
  9045. * // ... do some stuff ...
  9046. * });
  9047. */
  9048. loadFromJSON: function (json, callback, reviver) {
  9049. if (!json) {
  9050. return;
  9051. }
  9052. // serialize if it wasn't already
  9053. var serialized = (typeof json === 'string')
  9054. ? JSON.parse(json)
  9055. : json;
  9056. this.clear();
  9057. var _this = this;
  9058. this._enlivenObjects(serialized.objects, function () {
  9059. _this._setBgOverlay(serialized, callback);
  9060. }, reviver);
  9061. return this;
  9062. },
  9063. /**
  9064. * @private
  9065. * @param {Object} serialized Object with background and overlay information
  9066. * @param {Function} callback Invoked after all background and overlay images/patterns loaded
  9067. */
  9068. _setBgOverlay: function(serialized, callback) {
  9069. var _this = this,
  9070. loaded = {
  9071. backgroundColor: false,
  9072. overlayColor: false,
  9073. backgroundImage: false,
  9074. overlayImage: false
  9075. };
  9076. if (!serialized.backgroundImage && !serialized.overlayImage && !serialized.background && !serialized.overlay) {
  9077. callback && callback();
  9078. return;
  9079. }
  9080. var cbIfLoaded = function () {
  9081. if (loaded.backgroundImage && loaded.overlayImage && loaded.backgroundColor && loaded.overlayColor) {
  9082. _this.renderAll();
  9083. callback && callback();
  9084. }
  9085. };
  9086. this.__setBgOverlay('backgroundImage', serialized.backgroundImage, loaded, cbIfLoaded);
  9087. this.__setBgOverlay('overlayImage', serialized.overlayImage, loaded, cbIfLoaded);
  9088. this.__setBgOverlay('backgroundColor', serialized.background, loaded, cbIfLoaded);
  9089. this.__setBgOverlay('overlayColor', serialized.overlay, loaded, cbIfLoaded);
  9090. cbIfLoaded();
  9091. },
  9092. /**
  9093. * @private
  9094. * @param {String} property Property to set (backgroundImage, overlayImage, backgroundColor, overlayColor)
  9095. * @param {(Object|String)} value Value to set
  9096. * @param {Object} loaded Set loaded property to true if property is set
  9097. * @param {Object} callback Callback function to invoke after property is set
  9098. */
  9099. __setBgOverlay: function(property, value, loaded, callback) {
  9100. var _this = this;
  9101. if (!value) {
  9102. loaded[property] = true;
  9103. return;
  9104. }
  9105. if (property === 'backgroundImage' || property === 'overlayImage') {
  9106. fabric.Image.fromObject(value, function(img) {
  9107. _this[property] = img;
  9108. loaded[property] = true;
  9109. callback && callback();
  9110. });
  9111. }
  9112. else {
  9113. this['set' + fabric.util.string.capitalize(property, true)](value, function() {
  9114. loaded[property] = true;
  9115. callback && callback();
  9116. });
  9117. }
  9118. },
  9119. /**
  9120. * @private
  9121. * @param {Array} objects
  9122. * @param {Function} callback
  9123. * @param {Function} [reviver]
  9124. */
  9125. _enlivenObjects: function (objects, callback, reviver) {
  9126. var _this = this;
  9127. if (!objects || objects.length === 0) {
  9128. callback && callback();
  9129. return;
  9130. }
  9131. var renderOnAddRemove = this.renderOnAddRemove;
  9132. this.renderOnAddRemove = false;
  9133. fabric.util.enlivenObjects(objects, function(enlivenedObjects) {
  9134. enlivenedObjects.forEach(function(obj, index) {
  9135. _this.insertAt(obj, index, true);
  9136. });
  9137. _this.renderOnAddRemove = renderOnAddRemove;
  9138. callback && callback();
  9139. }, null, reviver);
  9140. },
  9141. /**
  9142. * @private
  9143. * @param {String} format
  9144. * @param {Function} callback
  9145. */
  9146. _toDataURL: function (format, callback) {
  9147. this.clone(function (clone) {
  9148. callback(clone.toDataURL(format));
  9149. });
  9150. },
  9151. /**
  9152. * @private
  9153. * @param {String} format
  9154. * @param {Number} multiplier
  9155. * @param {Function} callback
  9156. */
  9157. _toDataURLWithMultiplier: function (format, multiplier, callback) {
  9158. this.clone(function (clone) {
  9159. callback(clone.toDataURLWithMultiplier(format, multiplier));
  9160. });
  9161. },
  9162. /**
  9163. * Clones canvas instance
  9164. * @param {Object} [callback] Receives cloned instance as a first argument
  9165. * @param {Array} [properties] Array of properties to include in the cloned canvas and children
  9166. */
  9167. clone: function (callback, properties) {
  9168. var data = JSON.stringify(this.toJSON(properties));
  9169. this.cloneWithoutData(function(clone) {
  9170. clone.loadFromJSON(data, function() {
  9171. callback && callback(clone);
  9172. });
  9173. });
  9174. },
  9175. /**
  9176. * Clones canvas instance without cloning existing data.
  9177. * This essentially copies canvas dimensions, clipping properties, etc.
  9178. * but leaves data empty (so that you can populate it with your own)
  9179. * @param {Object} [callback] Receives cloned instance as a first argument
  9180. */
  9181. cloneWithoutData: function(callback) {
  9182. var el = fabric.document.createElement('canvas');
  9183. el.width = this.getWidth();
  9184. el.height = this.getHeight();
  9185. var clone = new fabric.Canvas(el);
  9186. clone.clipTo = this.clipTo;
  9187. if (this.backgroundImage) {
  9188. clone.setBackgroundImage(this.backgroundImage.src, function() {
  9189. clone.renderAll();
  9190. callback && callback(clone);
  9191. });
  9192. clone.backgroundImageOpacity = this.backgroundImageOpacity;
  9193. clone.backgroundImageStretch = this.backgroundImageStretch;
  9194. }
  9195. else {
  9196. callback && callback(clone);
  9197. }
  9198. }
  9199. });
  9200. (function(global) {
  9201. 'use strict';
  9202. var fabric = global.fabric || (global.fabric = { }),
  9203. extend = fabric.util.object.extend,
  9204. toFixed = fabric.util.toFixed,
  9205. capitalize = fabric.util.string.capitalize,
  9206. degreesToRadians = fabric.util.degreesToRadians,
  9207. supportsLineDash = fabric.StaticCanvas.supports('setLineDash');
  9208. if (fabric.Object) {
  9209. return;
  9210. }
  9211. /**
  9212. * Root object class from which all 2d shape classes inherit from
  9213. * @class fabric.Object
  9214. * @tutorial {@link http://fabricjs.com/fabric-intro-part-1/#objects}
  9215. * @see {@link fabric.Object#initialize} for constructor definition
  9216. *
  9217. * @fires added
  9218. * @fires removed
  9219. *
  9220. * @fires selected
  9221. * @fires modified
  9222. * @fires rotating
  9223. * @fires scaling
  9224. * @fires moving
  9225. *
  9226. * @fires mousedown
  9227. * @fires mouseup
  9228. */
  9229. fabric.Object = fabric.util.createClass(/** @lends fabric.Object.prototype */ {
  9230. /**
  9231. * Retrieves object's {@link fabric.Object#clipTo|clipping function}
  9232. * @method getClipTo
  9233. * @memberOf fabric.Object.prototype
  9234. * @return {Function}
  9235. */
  9236. /**
  9237. * Sets object's {@link fabric.Object#clipTo|clipping function}
  9238. * @method setClipTo
  9239. * @memberOf fabric.Object.prototype
  9240. * @param {Function} clipTo Clipping function
  9241. * @return {fabric.Object} thisArg
  9242. * @chainable
  9243. */
  9244. /**
  9245. * Retrieves object's {@link fabric.Object#transformMatrix|transformMatrix}
  9246. * @method getTransformMatrix
  9247. * @memberOf fabric.Object.prototype
  9248. * @return {Array} transformMatrix
  9249. */
  9250. /**
  9251. * Sets object's {@link fabric.Object#transformMatrix|transformMatrix}
  9252. * @method setTransformMatrix
  9253. * @memberOf fabric.Object.prototype
  9254. * @param {Array} transformMatrix
  9255. * @return {fabric.Object} thisArg
  9256. * @chainable
  9257. */
  9258. /**
  9259. * Retrieves object's {@link fabric.Object#visible|visible} state
  9260. * @method getVisible
  9261. * @memberOf fabric.Object.prototype
  9262. * @return {Boolean} True if visible
  9263. */
  9264. /**
  9265. * Sets object's {@link fabric.Object#visible|visible} state
  9266. * @method setVisible
  9267. * @memberOf fabric.Object.prototype
  9268. * @param {Boolean} value visible value
  9269. * @return {fabric.Object} thisArg
  9270. * @chainable
  9271. */
  9272. /**
  9273. * Retrieves object's {@link fabric.Object#shadow|shadow}
  9274. * @method getShadow
  9275. * @memberOf fabric.Object.prototype
  9276. * @return {Object} Shadow instance
  9277. */
  9278. /**
  9279. * Retrieves object's {@link fabric.Object#stroke|stroke}
  9280. * @method getStroke
  9281. * @memberOf fabric.Object.prototype
  9282. * @return {String} stroke value
  9283. */
  9284. /**
  9285. * Sets object's {@link fabric.Object#stroke|stroke}
  9286. * @method setStroke
  9287. * @memberOf fabric.Object.prototype
  9288. * @param {String} value stroke value
  9289. * @return {fabric.Object} thisArg
  9290. * @chainable
  9291. */
  9292. /**
  9293. * Retrieves object's {@link fabric.Object#strokeWidth|strokeWidth}
  9294. * @method getStrokeWidth
  9295. * @memberOf fabric.Object.prototype
  9296. * @return {Number} strokeWidth value
  9297. */
  9298. /**
  9299. * Sets object's {@link fabric.Object#strokeWidth|strokeWidth}
  9300. * @method setStrokeWidth
  9301. * @memberOf fabric.Object.prototype
  9302. * @param {Number} value strokeWidth value
  9303. * @return {fabric.Object} thisArg
  9304. * @chainable
  9305. */
  9306. /**
  9307. * Retrieves object's {@link fabric.Object#originX|originX}
  9308. * @method getOriginX
  9309. * @memberOf fabric.Object.prototype
  9310. * @return {String} originX value
  9311. */
  9312. /**
  9313. * Sets object's {@link fabric.Object#originX|originX}
  9314. * @method setOriginX
  9315. * @memberOf fabric.Object.prototype
  9316. * @param {String} value originX value
  9317. * @return {fabric.Object} thisArg
  9318. * @chainable
  9319. */
  9320. /**
  9321. * Retrieves object's {@link fabric.Object#originY|originY}
  9322. * @method getOriginY
  9323. * @memberOf fabric.Object.prototype
  9324. * @return {String} originY value
  9325. */
  9326. /**
  9327. * Sets object's {@link fabric.Object#originY|originY}
  9328. * @method setOriginY
  9329. * @memberOf fabric.Object.prototype
  9330. * @param {String} value originY value
  9331. * @return {fabric.Object} thisArg
  9332. * @chainable
  9333. */
  9334. /**
  9335. * Retrieves object's {@link fabric.Object#fill|fill}
  9336. * @method getFill
  9337. * @memberOf fabric.Object.prototype
  9338. * @return {String} Fill value
  9339. */
  9340. /**
  9341. * Sets object's {@link fabric.Object#fill|fill}
  9342. * @method setFill
  9343. * @memberOf fabric.Object.prototype
  9344. * @param {String} value Fill value
  9345. * @return {fabric.Object} thisArg
  9346. * @chainable
  9347. */
  9348. /**
  9349. * Retrieves object's {@link fabric.Object#opacity|opacity}
  9350. * @method getOpacity
  9351. * @memberOf fabric.Object.prototype
  9352. * @return {Number} Opacity value (0-1)
  9353. */
  9354. /**
  9355. * Sets object's {@link fabric.Object#opacity|opacity}
  9356. * @method setOpacity
  9357. * @memberOf fabric.Object.prototype
  9358. * @param {Number} value Opacity value (0-1)
  9359. * @return {fabric.Object} thisArg
  9360. * @chainable
  9361. */
  9362. /**
  9363. * Retrieves object's {@link fabric.Object#angle|angle} (in degrees)
  9364. * @method getAngle
  9365. * @memberOf fabric.Object.prototype
  9366. * @return {Number}
  9367. */
  9368. /**
  9369. * Sets object's {@link fabric.Object#angle|angle}
  9370. * @method setAngle
  9371. * @memberOf fabric.Object.prototype
  9372. * @param {Number} value Angle value (in degrees)
  9373. * @return {fabric.Object} thisArg
  9374. * @chainable
  9375. */
  9376. /**
  9377. * Retrieves object's {@link fabric.Object#top|top position}
  9378. * @method getTop
  9379. * @memberOf fabric.Object.prototype
  9380. * @return {Number} Top value (in pixels)
  9381. */
  9382. /**
  9383. * Sets object's {@link fabric.Object#top|top position}
  9384. * @method setTop
  9385. * @memberOf fabric.Object.prototype
  9386. * @param {Number} value Top value (in pixels)
  9387. * @return {fabric.Object} thisArg
  9388. * @chainable
  9389. */
  9390. /**
  9391. * Retrieves object's {@link fabric.Object#left|left position}
  9392. * @method getLeft
  9393. * @memberOf fabric.Object.prototype
  9394. * @return {Number} Left value (in pixels)
  9395. */
  9396. /**
  9397. * Sets object's {@link fabric.Object#left|left position}
  9398. * @method setLeft
  9399. * @memberOf fabric.Object.prototype
  9400. * @param {Number} value Left value (in pixels)
  9401. * @return {fabric.Object} thisArg
  9402. * @chainable
  9403. */
  9404. /**
  9405. * Retrieves object's {@link fabric.Object#scaleX|scaleX} value
  9406. * @method getScaleX
  9407. * @memberOf fabric.Object.prototype
  9408. * @return {Number} scaleX value
  9409. */
  9410. /**
  9411. * Sets object's {@link fabric.Object#scaleX|scaleX} value
  9412. * @method setScaleX
  9413. * @memberOf fabric.Object.prototype
  9414. * @param {Number} value scaleX value
  9415. * @return {fabric.Object} thisArg
  9416. * @chainable
  9417. */
  9418. /**
  9419. * Retrieves object's {@link fabric.Object#scaleY|scaleY} value
  9420. * @method getScaleY
  9421. * @memberOf fabric.Object.prototype
  9422. * @return {Number} scaleY value
  9423. */
  9424. /**
  9425. * Sets object's {@link fabric.Object#scaleY|scaleY} value
  9426. * @method setScaleY
  9427. * @memberOf fabric.Object.prototype
  9428. * @param {Number} value scaleY value
  9429. * @return {fabric.Object} thisArg
  9430. * @chainable
  9431. */
  9432. /**
  9433. * Retrieves object's {@link fabric.Object#flipX|flipX} value
  9434. * @method getFlipX
  9435. * @memberOf fabric.Object.prototype
  9436. * @return {Boolean} flipX value
  9437. */
  9438. /**
  9439. * Sets object's {@link fabric.Object#flipX|flipX} value
  9440. * @method setFlipX
  9441. * @memberOf fabric.Object.prototype
  9442. * @param {Boolean} value flipX value
  9443. * @return {fabric.Object} thisArg
  9444. * @chainable
  9445. */
  9446. /**
  9447. * Retrieves object's {@link fabric.Object#flipY|flipY} value
  9448. * @method getFlipY
  9449. * @memberOf fabric.Object.prototype
  9450. * @return {Boolean} flipY value
  9451. */
  9452. /**
  9453. * Sets object's {@link fabric.Object#flipY|flipY} value
  9454. * @method setFlipY
  9455. * @memberOf fabric.Object.prototype
  9456. * @param {Boolean} value flipY value
  9457. * @return {fabric.Object} thisArg
  9458. * @chainable
  9459. */
  9460. /**
  9461. * Type of an object (rect, circle, path, etc.)
  9462. * @type String
  9463. * @default
  9464. */
  9465. type: 'object',
  9466. /**
  9467. * Horizontal origin of transformation of an object (one of "left", "right", "center")
  9468. * @type String
  9469. * @default
  9470. */
  9471. originX: 'left',
  9472. /**
  9473. * Vertical origin of transformation of an object (one of "top", "bottom", "center")
  9474. * @type String
  9475. * @default
  9476. */
  9477. originY: 'top',
  9478. /**
  9479. * Top position of an object. Note that by default it's relative to object center. You can change this by setting originY={top/center/bottom}
  9480. * @type Number
  9481. * @default
  9482. */
  9483. top: 0,
  9484. /**
  9485. * Left position of an object. Note that by default it's relative to object center. You can change this by setting originX={left/center/right}
  9486. * @type Number
  9487. * @default
  9488. */
  9489. left: 0,
  9490. /**
  9491. * Object width
  9492. * @type Number
  9493. * @default
  9494. */
  9495. width: 0,
  9496. /**
  9497. * Object height
  9498. * @type Number
  9499. * @default
  9500. */
  9501. height: 0,
  9502. /**
  9503. * Object scale factor (horizontal)
  9504. * @type Number
  9505. * @default
  9506. */
  9507. scaleX: 1,
  9508. /**
  9509. * Object scale factor (vertical)
  9510. * @type Number
  9511. * @default
  9512. */
  9513. scaleY: 1,
  9514. /**
  9515. * When true, an object is rendered as flipped horizontally
  9516. * @type Boolean
  9517. * @default
  9518. */
  9519. flipX: false,
  9520. /**
  9521. * When true, an object is rendered as flipped vertically
  9522. * @type Boolean
  9523. * @default
  9524. */
  9525. flipY: false,
  9526. /**
  9527. * Opacity of an object
  9528. * @type Number
  9529. * @default
  9530. */
  9531. opacity: 1,
  9532. /**
  9533. * Angle of rotation of an object (in degrees)
  9534. * @type Number
  9535. * @default
  9536. */
  9537. angle: 0,
  9538. /**
  9539. * Size of object's controlling corners (in pixels)
  9540. * @type Number
  9541. * @default
  9542. */
  9543. cornerSize: 12,
  9544. /**
  9545. * When true, object's controlling corners are rendered as transparent inside (i.e. stroke instead of fill)
  9546. * @type Boolean
  9547. * @default
  9548. */
  9549. transparentCorners: true,
  9550. /**
  9551. * Default cursor value used when hovering over this object on canvas
  9552. * @type String
  9553. * @default
  9554. */
  9555. hoverCursor: null,
  9556. /**
  9557. * Padding between object and its controlling borders (in pixels)
  9558. * @type Number
  9559. * @default
  9560. */
  9561. padding: 0,
  9562. /**
  9563. * Color of controlling borders of an object (when it's active)
  9564. * @type String
  9565. * @default
  9566. */
  9567. borderColor: 'rgba(102,153,255,0.75)',
  9568. /**
  9569. * Color of controlling corners of an object (when it's active)
  9570. * @type String
  9571. * @default
  9572. */
  9573. cornerColor: 'rgba(102,153,255,0.5)',
  9574. /**
  9575. * When true, this object will use center point as the origin of transformation
  9576. * when being scaled via the controls.
  9577. * <b>Backwards incompatibility note:</b> This property replaces "centerTransform" (Boolean).
  9578. * @since 1.3.4
  9579. * @type Boolean
  9580. * @default
  9581. */
  9582. centeredScaling: false,
  9583. /**
  9584. * When true, this object will use center point as the origin of transformation
  9585. * when being rotated via the controls.
  9586. * <b>Backwards incompatibility note:</b> This property replaces "centerTransform" (Boolean).
  9587. * @since 1.3.4
  9588. * @type Boolean
  9589. * @default
  9590. */
  9591. centeredRotation: true,
  9592. /**
  9593. * Color of object's fill
  9594. * @type String
  9595. * @default
  9596. */
  9597. fill: 'rgb(0,0,0)',
  9598. /**
  9599. * Fill rule used to fill an object
  9600. * @type String
  9601. * @default
  9602. */
  9603. fillRule: 'source-over',
  9604. /**
  9605. * Background color of an object. Only works with text objects at the moment.
  9606. * @type String
  9607. * @default
  9608. */
  9609. backgroundColor: '',
  9610. /**
  9611. * When defined, an object is rendered via stroke and this property specifies its color
  9612. * @type String
  9613. * @default
  9614. */
  9615. stroke: null,
  9616. /**
  9617. * Width of a stroke used to render this object
  9618. * @type Number
  9619. * @default
  9620. */
  9621. strokeWidth: 1,
  9622. /**
  9623. * Array specifying dash pattern of an object's stroke (stroke must be defined)
  9624. * @type Array
  9625. */
  9626. strokeDashArray: null,
  9627. /**
  9628. * Line endings style of an object's stroke (one of "butt", "round", "square")
  9629. * @type String
  9630. * @default
  9631. */
  9632. strokeLineCap: 'butt',
  9633. /**
  9634. * Corner style of an object's stroke (one of "bevil", "round", "miter")
  9635. * @type String
  9636. * @default
  9637. */
  9638. strokeLineJoin: 'miter',
  9639. /**
  9640. * Maximum miter length (used for strokeLineJoin = "miter") of an object's stroke
  9641. * @type Number
  9642. * @default
  9643. */
  9644. strokeMiterLimit: 10,
  9645. /**
  9646. * Shadow object representing shadow of this shape
  9647. * @type fabric.Shadow
  9648. * @default
  9649. */
  9650. shadow: null,
  9651. /**
  9652. * Opacity of object's controlling borders when object is active and moving
  9653. * @type Number
  9654. * @default
  9655. */
  9656. borderOpacityWhenMoving: 0.4,
  9657. /**
  9658. * Scale factor of object's controlling borders
  9659. * @type Number
  9660. * @default
  9661. */
  9662. borderScaleFactor: 1,
  9663. /**
  9664. * Transform matrix (similar to SVG's transform matrix)
  9665. * @type Array
  9666. */
  9667. transformMatrix: null,
  9668. /**
  9669. * Minimum allowed scale value of an object
  9670. * @type Number
  9671. * @default
  9672. */
  9673. minScaleLimit: 0.01,
  9674. /**
  9675. * When set to `false`, an object can not be selected for modification (using either point-click-based or group-based selection).
  9676. * But events still fire on it.
  9677. * @type Boolean
  9678. * @default
  9679. */
  9680. selectable: true,
  9681. /**
  9682. * When set to `false`, an object can not be a target of events. All events propagate through it. Introduced in v1.3.4
  9683. * @type Boolean
  9684. * @default
  9685. */
  9686. evented: true,
  9687. /**
  9688. * When set to `false`, an object is not rendered on canvas
  9689. * @type Boolean
  9690. * @default
  9691. */
  9692. visible: true,
  9693. /**
  9694. * When set to `false`, object's controls are not displayed and can not be used to manipulate object
  9695. * @type Boolean
  9696. * @default
  9697. */
  9698. hasControls: true,
  9699. /**
  9700. * When set to `false`, object's controlling borders are not rendered
  9701. * @type Boolean
  9702. * @default
  9703. */
  9704. hasBorders: true,
  9705. /**
  9706. * When set to `false`, object's controlling rotating point will not be visible or selectable
  9707. * @type Boolean
  9708. * @default
  9709. */
  9710. hasRotatingPoint: true,
  9711. /**
  9712. * Offset for object's controlling rotating point (when enabled via `hasRotatingPoint`)
  9713. * @type Number
  9714. * @default
  9715. */
  9716. rotatingPointOffset: 40,
  9717. /**
  9718. * When set to `true`, objects are "found" on canvas on per-pixel basis rather than according to bounding box
  9719. * @type Boolean
  9720. * @default
  9721. */
  9722. perPixelTargetFind: false,
  9723. /**
  9724. * When `false`, default object's values are not included in its serialization
  9725. * @type Boolean
  9726. * @default
  9727. */
  9728. includeDefaultValues: true,
  9729. /**
  9730. * Function that determines clipping of an object (context is passed as a first argument)
  9731. * Note that context origin is at the object's center point (not left/top corner)
  9732. * @type Function
  9733. */
  9734. clipTo: null,
  9735. /**
  9736. * When `true`, object horizontal movement is locked
  9737. * @type Boolean
  9738. * @default
  9739. */
  9740. lockMovementX: false,
  9741. /**
  9742. * When `true`, object vertical movement is locked
  9743. * @type Boolean
  9744. * @default
  9745. */
  9746. lockMovementY: false,
  9747. /**
  9748. * When `true`, object rotation is locked
  9749. * @type Boolean
  9750. * @default
  9751. */
  9752. lockRotation: false,
  9753. /**
  9754. * When `true`, object horizontal scaling is locked
  9755. * @type Boolean
  9756. * @default
  9757. */
  9758. lockScalingX: false,
  9759. /**
  9760. * When `true`, object vertical scaling is locked
  9761. * @type Boolean
  9762. * @default
  9763. */
  9764. lockScalingY: false,
  9765. /**
  9766. * When `true`, object non-uniform scaling is locked
  9767. * @type Boolean
  9768. * @default
  9769. */
  9770. lockUniScaling: false,
  9771. /**
  9772. * When `true`, object cannot be flipped by scaling into negative values
  9773. * @type Boolean
  9774. * @default
  9775. */
  9776. lockScalingFlip: false,
  9777. /**
  9778. * List of properties to consider when checking if state
  9779. * of an object is changed (fabric.Object#hasStateChanged)
  9780. * as well as for history (undo/redo) purposes
  9781. * @type Array
  9782. */
  9783. stateProperties: (
  9784. 'top left width height scaleX scaleY flipX flipY originX originY transformMatrix ' +
  9785. 'stroke strokeWidth strokeDashArray strokeLineCap strokeLineJoin strokeMiterLimit ' +
  9786. 'angle opacity fill fillRule shadow clipTo visible backgroundColor'
  9787. ).split(' '),
  9788. /**
  9789. * Constructor
  9790. * @param {Object} [options] Options object
  9791. */
  9792. initialize: function(options) {
  9793. if (options) {
  9794. this.setOptions(options);
  9795. }
  9796. },
  9797. /**
  9798. * @private
  9799. * @param {Object} [options] Options object
  9800. */
  9801. _initGradient: function(options) {
  9802. if (options.fill && options.fill.colorStops && !(options.fill instanceof fabric.Gradient)) {
  9803. this.set('fill', new fabric.Gradient(options.fill));
  9804. }
  9805. },
  9806. /**
  9807. * @private
  9808. * @param {Object} [options] Options object
  9809. */
  9810. _initPattern: function(options) {
  9811. if (options.fill && options.fill.source && !(options.fill instanceof fabric.Pattern)) {
  9812. this.set('fill', new fabric.Pattern(options.fill));
  9813. }
  9814. if (options.stroke && options.stroke.source && !(options.stroke instanceof fabric.Pattern)) {
  9815. this.set('stroke', new fabric.Pattern(options.stroke));
  9816. }
  9817. },
  9818. /**
  9819. * @private
  9820. * @param {Object} [options] Options object
  9821. */
  9822. _initClipping: function(options) {
  9823. if (!options.clipTo || typeof options.clipTo !== 'string') {
  9824. return;
  9825. }
  9826. var functionBody = fabric.util.getFunctionBody(options.clipTo);
  9827. if (typeof functionBody !== 'undefined') {
  9828. this.clipTo = new Function('ctx', functionBody);
  9829. }
  9830. },
  9831. /**
  9832. * Sets object's properties from options
  9833. * @param {Object} [options] Options object
  9834. */
  9835. setOptions: function(options) {
  9836. for (var prop in options) {
  9837. this.set(prop, options[prop]);
  9838. }
  9839. this._initGradient(options);
  9840. this._initPattern(options);
  9841. this._initClipping(options);
  9842. },
  9843. /**
  9844. * Transforms context when rendering an object
  9845. * @param {CanvasRenderingContext2D} ctx Context
  9846. * @param {Boolean} fromLeft When true, context is transformed to object's top/left corner. This is used when rendering text on Node
  9847. */
  9848. transform: function(ctx, fromLeft) {
  9849. if (this.group) {
  9850. this.group.transform(ctx, fromLeft);
  9851. }
  9852. ctx.globalAlpha = this.opacity;
  9853. var center = fromLeft ? this._getLeftTopCoords() : this.getCenterPoint();
  9854. ctx.translate(center.x, center.y);
  9855. ctx.rotate(degreesToRadians(this.angle));
  9856. ctx.scale(
  9857. this.scaleX * (this.flipX ? -1 : 1),
  9858. this.scaleY * (this.flipY ? -1 : 1)
  9859. );
  9860. },
  9861. /**
  9862. * Returns an object representation of an instance
  9863. * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
  9864. * @return {Object} Object representation of an instance
  9865. */
  9866. toObject: function(propertiesToInclude) {
  9867. var NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS,
  9868. object = {
  9869. type: this.type,
  9870. originX: this.originX,
  9871. originY: this.originY,
  9872. left: toFixed(this.left, NUM_FRACTION_DIGITS),
  9873. top: toFixed(this.top, NUM_FRACTION_DIGITS),
  9874. width: toFixed(this.width, NUM_FRACTION_DIGITS),
  9875. height: toFixed(this.height, NUM_FRACTION_DIGITS),
  9876. fill: (this.fill && this.fill.toObject) ? this.fill.toObject() : this.fill,
  9877. stroke: (this.stroke && this.stroke.toObject) ? this.stroke.toObject() : this.stroke,
  9878. strokeWidth: toFixed(this.strokeWidth, NUM_FRACTION_DIGITS),
  9879. strokeDashArray: this.strokeDashArray,
  9880. strokeLineCap: this.strokeLineCap,
  9881. strokeLineJoin: this.strokeLineJoin,
  9882. strokeMiterLimit: toFixed(this.strokeMiterLimit, NUM_FRACTION_DIGITS),
  9883. scaleX: toFixed(this.scaleX, NUM_FRACTION_DIGITS),
  9884. scaleY: toFixed(this.scaleY, NUM_FRACTION_DIGITS),
  9885. angle: toFixed(this.getAngle(), NUM_FRACTION_DIGITS),
  9886. flipX: this.flipX,
  9887. flipY: this.flipY,
  9888. opacity: toFixed(this.opacity, NUM_FRACTION_DIGITS),
  9889. shadow: (this.shadow && this.shadow.toObject) ? this.shadow.toObject() : this.shadow,
  9890. visible: this.visible,
  9891. clipTo: this.clipTo && String(this.clipTo),
  9892. backgroundColor: this.backgroundColor
  9893. };
  9894. if (!this.includeDefaultValues) {
  9895. object = this._removeDefaultValues(object);
  9896. }
  9897. fabric.util.populateWithProperties(this, object, propertiesToInclude);
  9898. return object;
  9899. },
  9900. /**
  9901. * Returns (dataless) object representation of an instance
  9902. * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
  9903. * @return {Object} Object representation of an instance
  9904. */
  9905. toDatalessObject: function(propertiesToInclude) {
  9906. // will be overwritten by subclasses
  9907. return this.toObject(propertiesToInclude);
  9908. },
  9909. /**
  9910. * @private
  9911. * @param {Object} object
  9912. */
  9913. _removeDefaultValues: function(object) {
  9914. var prototype = fabric.util.getKlass(object.type).prototype,
  9915. stateProperties = prototype.stateProperties;
  9916. stateProperties.forEach(function(prop) {
  9917. if (object[prop] === prototype[prop]) {
  9918. delete object[prop];
  9919. }
  9920. });
  9921. return object;
  9922. },
  9923. /**
  9924. * Returns a string representation of an instance
  9925. * @return {String}
  9926. */
  9927. toString: function() {
  9928. return '#<fabric.' + capitalize(this.type) + '>';
  9929. },
  9930. /**
  9931. * Basic getter
  9932. * @param {String} property Property name
  9933. * @return {Any} value of a property
  9934. */
  9935. get: function(property) {
  9936. return this[property];
  9937. },
  9938. /**
  9939. * @private
  9940. */
  9941. _setObject: function(obj) {
  9942. for (var prop in obj) {
  9943. this._set(prop, obj[prop]);
  9944. }
  9945. },
  9946. /**
  9947. * 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()`.
  9948. * @param {String|Object} key Property name or object (if object, iterate over the object properties)
  9949. * @param {Object|Function} value Property value (if function, the value is passed into it and its return value is used as a new one)
  9950. * @return {fabric.Object} thisArg
  9951. * @chainable
  9952. */
  9953. set: function(key, value) {
  9954. if (typeof key === 'object') {
  9955. this._setObject(key);
  9956. }
  9957. else {
  9958. if (typeof value === 'function' && key !== 'clipTo') {
  9959. this._set(key, value(this.get(key)));
  9960. }
  9961. else {
  9962. this._set(key, value);
  9963. }
  9964. }
  9965. return this;
  9966. },
  9967. /**
  9968. * @private
  9969. * @param {String} key
  9970. * @param {Any} value
  9971. * @return {fabric.Object} thisArg
  9972. */
  9973. _set: function(key, value) {
  9974. var shouldConstrainValue = (key === 'scaleX' || key === 'scaleY');
  9975. if (shouldConstrainValue) {
  9976. value = this._constrainScale(value);
  9977. }
  9978. if (key === 'scaleX' && value < 0) {
  9979. this.flipX = !this.flipX;
  9980. value *= -1;
  9981. }
  9982. else if (key === 'scaleY' && value < 0) {
  9983. this.flipY = !this.flipY;
  9984. value *= -1;
  9985. }
  9986. else if (key === 'width' || key === 'height') {
  9987. this.minScaleLimit = toFixed(Math.min(0.1, 1/Math.max(this.width, this.height)), 2);
  9988. }
  9989. else if (key === 'shadow' && value && !(value instanceof fabric.Shadow)) {
  9990. value = new fabric.Shadow(value);
  9991. }
  9992. this[key] = value;
  9993. return this;
  9994. },
  9995. /**
  9996. * Toggles specified property from `true` to `false` or from `false` to `true`
  9997. * @param {String} property Property to toggle
  9998. * @return {fabric.Object} thisArg
  9999. * @chainable
  10000. */
  10001. toggle: function(property) {
  10002. var value = this.get(property);
  10003. if (typeof value === 'boolean') {
  10004. this.set(property, !value);
  10005. }
  10006. return this;
  10007. },
  10008. /**
  10009. * Sets sourcePath of an object
  10010. * @param {String} value Value to set sourcePath to
  10011. * @return {fabric.Object} thisArg
  10012. * @chainable
  10013. */
  10014. setSourcePath: function(value) {
  10015. this.sourcePath = value;
  10016. return this;
  10017. },
  10018. /**
  10019. * Retrieves viewportTransform from Object's canvas if possible
  10020. * @method getViewportTransform
  10021. * @memberOf fabric.Object.prototype
  10022. * @return {Boolean} flipY value // TODO
  10023. */
  10024. getViewportTransform: function() {
  10025. if (this.canvas && this.canvas.viewportTransform) {
  10026. return this.canvas.viewportTransform;
  10027. }
  10028. return [1, 0, 0, 1, 0, 0];
  10029. },
  10030. /**
  10031. * Renders an object on a specified context
  10032. * @param {CanvasRenderingContext2D} ctx Context to render on
  10033. * @param {Boolean} [noTransform] When true, context is not transformed
  10034. */
  10035. render: function(ctx, noTransform) {
  10036. // do not render if width/height are zeros or object is not visible
  10037. if (this.width === 0 || this.height === 0 || !this.visible) {
  10038. return;
  10039. }
  10040. ctx.save();
  10041. //setup fill rule for current object
  10042. this._setupFillRule(ctx);
  10043. this._transform(ctx, noTransform);
  10044. this._setStrokeStyles(ctx);
  10045. this._setFillStyles(ctx);
  10046. if (this.group && this.group.type === 'path-group') {
  10047. ctx.translate(-this.group.width/2, -this.group.height/2);
  10048. var m = this.transformMatrix;
  10049. if (m) {
  10050. ctx.transform.apply(ctx, m);
  10051. }
  10052. }
  10053. ctx.globalAlpha = this.group ? (ctx.globalAlpha * this.opacity) : this.opacity;
  10054. this._setShadow(ctx);
  10055. this.clipTo && fabric.util.clipContext(this, ctx);
  10056. this._render(ctx, noTransform);
  10057. this.clipTo && ctx.restore();
  10058. this._removeShadow(ctx);
  10059. this._restoreFillRule(ctx);
  10060. ctx.restore();
  10061. },
  10062. _transform: function(ctx, noTransform) {
  10063. var m = this.transformMatrix;
  10064. if (m && !this.group) {
  10065. ctx.setTransform.apply(ctx, m);
  10066. }
  10067. if (!noTransform) {
  10068. this.transform(ctx);
  10069. }
  10070. },
  10071. _setStrokeStyles: function(ctx) {
  10072. if (this.stroke) {
  10073. ctx.lineWidth = this.strokeWidth;
  10074. ctx.lineCap = this.strokeLineCap;
  10075. ctx.lineJoin = this.strokeLineJoin;
  10076. ctx.miterLimit = this.strokeMiterLimit;
  10077. ctx.strokeStyle = this.stroke.toLive
  10078. ? this.stroke.toLive(ctx)
  10079. : this.stroke;
  10080. }
  10081. },
  10082. _setFillStyles: function(ctx) {
  10083. if (this.fill) {
  10084. ctx.fillStyle = this.fill.toLive
  10085. ? this.fill.toLive(ctx)
  10086. : this.fill;
  10087. }
  10088. },
  10089. /**
  10090. * Renders controls and borders for the object
  10091. * @param {CanvasRenderingContext2D} ctx Context to render on
  10092. * @param {Boolean} [noTransform] When true, context is not transformed
  10093. */
  10094. _renderControls: function(ctx, noTransform) {
  10095. var vpt = this.getViewportTransform();
  10096. ctx.save();
  10097. if (this.active && !noTransform) {
  10098. var center;
  10099. if (this.group) {
  10100. center = fabric.util.transformPoint(this.group.getCenterPoint(), vpt);
  10101. ctx.translate(center.x, center.y);
  10102. ctx.rotate(degreesToRadians(this.group.angle));
  10103. }
  10104. center = fabric.util.transformPoint(this.getCenterPoint(), vpt, null != this.group);
  10105. if (this.group) {
  10106. center.x *= this.group.scaleX;
  10107. center.y *= this.group.scaleY;
  10108. }
  10109. ctx.translate(center.x, center.y);
  10110. ctx.rotate(degreesToRadians(this.angle));
  10111. this.drawBorders(ctx);
  10112. this.drawControls(ctx);
  10113. }
  10114. ctx.restore();
  10115. },
  10116. /**
  10117. * @private
  10118. * @param {CanvasRenderingContext2D} ctx Context to render on
  10119. */
  10120. _setShadow: function(ctx) {
  10121. if (!this.shadow) {
  10122. return;
  10123. }
  10124. ctx.shadowColor = this.shadow.color;
  10125. ctx.shadowBlur = this.shadow.blur;
  10126. ctx.shadowOffsetX = this.shadow.offsetX;
  10127. ctx.shadowOffsetY = this.shadow.offsetY;
  10128. },
  10129. /**
  10130. * @private
  10131. * @param {CanvasRenderingContext2D} ctx Context to render on
  10132. */
  10133. _removeShadow: function(ctx) {
  10134. if (!this.shadow) {
  10135. return;
  10136. }
  10137. ctx.shadowColor = '';
  10138. ctx.shadowBlur = ctx.shadowOffsetX = ctx.shadowOffsetY = 0;
  10139. },
  10140. /**
  10141. * @private
  10142. * @param {CanvasRenderingContext2D} ctx Context to render on
  10143. */
  10144. _renderFill: function(ctx) {
  10145. if (!this.fill) {
  10146. return;
  10147. }
  10148. ctx.save();
  10149. if (this.fill.toLive) {
  10150. ctx.translate(
  10151. -this.width / 2 + this.fill.offsetX || 0,
  10152. -this.height / 2 + this.fill.offsetY || 0);
  10153. }
  10154. if (this.fill.gradientTransform) {
  10155. var g = this.fill.gradientTransform;
  10156. ctx.transform.apply(ctx, g);
  10157. }
  10158. if (this.fillRule === 'destination-over') {
  10159. ctx.fill('evenodd');
  10160. }
  10161. else {
  10162. ctx.fill();
  10163. }
  10164. ctx.restore();
  10165. if (this.shadow && !this.shadow.affectStroke) {
  10166. this._removeShadow(ctx);
  10167. }
  10168. },
  10169. /**
  10170. * @private
  10171. * @param {CanvasRenderingContext2D} ctx Context to render on
  10172. */
  10173. _renderStroke: function(ctx) {
  10174. if (!this.stroke || this.strokeWidth === 0) {
  10175. return;
  10176. }
  10177. ctx.save();
  10178. if (this.strokeDashArray) {
  10179. // Spec requires the concatenation of two copies the dash list when the number of elements is odd
  10180. if (1 & this.strokeDashArray.length) {
  10181. this.strokeDashArray.push.apply(this.strokeDashArray, this.strokeDashArray);
  10182. }
  10183. if (supportsLineDash) {
  10184. ctx.setLineDash(this.strokeDashArray);
  10185. this._stroke && this._stroke(ctx);
  10186. }
  10187. else {
  10188. this._renderDashedStroke && this._renderDashedStroke(ctx);
  10189. }
  10190. ctx.stroke();
  10191. }
  10192. else {
  10193. if (this.stroke.gradientTransform) {
  10194. var g = this.stroke.gradientTransform;
  10195. ctx.transform.apply(ctx, g);
  10196. }
  10197. this._stroke ? this._stroke(ctx) : ctx.stroke();
  10198. }
  10199. this._removeShadow(ctx);
  10200. ctx.restore();
  10201. },
  10202. /**
  10203. * Clones an instance
  10204. * @param {Function} callback Callback is invoked with a clone as a first argument
  10205. * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
  10206. * @return {fabric.Object} clone of an instance
  10207. */
  10208. clone: function(callback, propertiesToInclude) {
  10209. if (this.constructor.fromObject) {
  10210. return this.constructor.fromObject(this.toObject(propertiesToInclude), callback);
  10211. }
  10212. return new fabric.Object(this.toObject(propertiesToInclude));
  10213. },
  10214. /**
  10215. * Creates an instance of fabric.Image out of an object
  10216. * @param {Function} callback callback, invoked with an instance as a first argument
  10217. * @return {fabric.Object} thisArg
  10218. */
  10219. cloneAsImage: function(callback) {
  10220. var dataUrl = this.toDataURL();
  10221. fabric.util.loadImage(dataUrl, function(img) {
  10222. if (callback) {
  10223. callback(new fabric.Image(img));
  10224. }
  10225. });
  10226. return this;
  10227. },
  10228. /**
  10229. * Converts an object into a data-url-like string
  10230. * @param {Object} options Options object
  10231. * @param {String} [options.format=png] The format of the output image. Either "jpeg" or "png"
  10232. * @param {Number} [options.quality=1] Quality level (0..1). Only used for jpeg.
  10233. * @param {Number} [options.multiplier=1] Multiplier to scale by
  10234. * @param {Number} [options.left] Cropping left offset. Introduced in v1.2.14
  10235. * @param {Number} [options.top] Cropping top offset. Introduced in v1.2.14
  10236. * @param {Number} [options.width] Cropping width. Introduced in v1.2.14
  10237. * @param {Number} [options.height] Cropping height. Introduced in v1.2.14
  10238. * @return {String} Returns a data: URL containing a representation of the object in the format specified by options.format
  10239. */
  10240. toDataURL: function(options) {
  10241. options || (options = { });
  10242. var el = fabric.util.createCanvasElement(),
  10243. boundingRect = this.getBoundingRect();
  10244. el.width = boundingRect.width;
  10245. el.height = boundingRect.height;
  10246. fabric.util.wrapElement(el, 'div');
  10247. var canvas = new fabric.Canvas(el);
  10248. // to avoid common confusion https://github.com/kangax/fabric.js/issues/806
  10249. if (options.format === 'jpg') {
  10250. options.format = 'jpeg';
  10251. }
  10252. if (options.format === 'jpeg') {
  10253. canvas.backgroundColor = '#fff';
  10254. }
  10255. var origParams = {
  10256. active: this.get('active'),
  10257. left: this.getLeft(),
  10258. top: this.getTop()
  10259. };
  10260. this.set('active', false);
  10261. this.setPositionByOrigin(new fabric.Point(el.width / 2, el.height / 2), 'center', 'center');
  10262. var originalCanvas = this.canvas;
  10263. canvas.add(this);
  10264. var data = canvas.toDataURL(options);
  10265. this.set(origParams).setCoords();
  10266. this.canvas = originalCanvas;
  10267. canvas.dispose();
  10268. canvas = null;
  10269. return data;
  10270. },
  10271. /**
  10272. * Returns true if specified type is identical to the type of an instance
  10273. * @param {String} type Type to check against
  10274. * @return {Boolean}
  10275. */
  10276. isType: function(type) {
  10277. return this.type === type;
  10278. },
  10279. /**
  10280. * Returns complexity of an instance
  10281. * @return {Number} complexity of this instance
  10282. */
  10283. complexity: function() {
  10284. return 0;
  10285. },
  10286. /**
  10287. * Returns a JSON representation of an instance
  10288. * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
  10289. * @return {Object} JSON
  10290. */
  10291. toJSON: function(propertiesToInclude) {
  10292. // delegate, not alias
  10293. return this.toObject(propertiesToInclude);
  10294. },
  10295. /**
  10296. * Sets gradient (fill or stroke) of an object
  10297. * <b>Backwards incompatibility note:</b> This method was named "setGradientFill" until v1.1.0
  10298. * @param {String} property Property name 'stroke' or 'fill'
  10299. * @param {Object} [options] Options object
  10300. * @param {String} [options.type] Type of gradient 'radial' or 'linear'
  10301. * @param {Number} [options.x1=0] x-coordinate of start point
  10302. * @param {Number} [options.y1=0] y-coordinate of start point
  10303. * @param {Number} [options.x2=0] x-coordinate of end point
  10304. * @param {Number} [options.y2=0] y-coordinate of end point
  10305. * @param {Number} [options.r1=0] Radius of start point (only for radial gradients)
  10306. * @param {Number} [options.r2=0] Radius of end point (only for radial gradients)
  10307. * @param {Object} [options.colorStops] Color stops object eg. {0: 'ff0000', 1: '000000'}
  10308. * @return {fabric.Object} thisArg
  10309. * @chainable
  10310. * @see {@link http://jsfiddle.net/fabricjs/58y8b/|jsFiddle demo}
  10311. * @example <caption>Set linear gradient</caption>
  10312. * object.setGradient('fill', {
  10313. * type: 'linear',
  10314. * x1: -object.width / 2,
  10315. * y1: 0,
  10316. * x2: object.width / 2,
  10317. * y2: 0,
  10318. * colorStops: {
  10319. * 0: 'red',
  10320. * 0.5: '#005555',
  10321. * 1: 'rgba(0,0,255,0.5)'
  10322. * }
  10323. * });
  10324. * canvas.renderAll();
  10325. * @example <caption>Set radial gradient</caption>
  10326. * object.setGradient('fill', {
  10327. * type: 'radial',
  10328. * x1: 0,
  10329. * y1: 0,
  10330. * x2: 0,
  10331. * y2: 0,
  10332. * r1: object.width / 2,
  10333. * r2: 10,
  10334. * colorStops: {
  10335. * 0: 'red',
  10336. * 0.5: '#005555',
  10337. * 1: 'rgba(0,0,255,0.5)'
  10338. * }
  10339. * });
  10340. * canvas.renderAll();
  10341. */
  10342. setGradient: function(property, options) {
  10343. options || (options = { });
  10344. var gradient = { colorStops: [] };
  10345. gradient.type = options.type || (options.r1 || options.r2 ? 'radial' : 'linear');
  10346. gradient.coords = {
  10347. x1: options.x1,
  10348. y1: options.y1,
  10349. x2: options.x2,
  10350. y2: options.y2
  10351. };
  10352. if (options.r1 || options.r2) {
  10353. gradient.coords.r1 = options.r1;
  10354. gradient.coords.r2 = options.r2;
  10355. }
  10356. for (var position in options.colorStops) {
  10357. var color = new fabric.Color(options.colorStops[position]);
  10358. gradient.colorStops.push({
  10359. offset: position,
  10360. color: color.toRgb(),
  10361. opacity: color.getAlpha()
  10362. });
  10363. }
  10364. return this.set(property, fabric.Gradient.forObject(this, gradient));
  10365. },
  10366. /**
  10367. * Sets pattern fill of an object
  10368. * @param {Object} options Options object
  10369. * @param {(String|HTMLImageElement)} options.source Pattern source
  10370. * @param {String} [options.repeat=repeat] Repeat property of a pattern (one of repeat, repeat-x, repeat-y or no-repeat)
  10371. * @param {Number} [options.offsetX=0] Pattern horizontal offset from object's left/top corner
  10372. * @param {Number} [options.offsetY=0] Pattern vertical offset from object's left/top corner
  10373. * @return {fabric.Object} thisArg
  10374. * @chainable
  10375. * @see {@link http://jsfiddle.net/fabricjs/QT3pa/|jsFiddle demo}
  10376. * @example <caption>Set pattern</caption>
  10377. * fabric.util.loadImage('http://fabricjs.com/assets/escheresque_ste.png', function(img) {
  10378. * object.setPatternFill({
  10379. * source: img,
  10380. * repeat: 'repeat'
  10381. * });
  10382. * canvas.renderAll();
  10383. * });
  10384. */
  10385. setPatternFill: function(options) {
  10386. return this.set('fill', new fabric.Pattern(options));
  10387. },
  10388. /**
  10389. * Sets {@link fabric.Object#shadow|shadow} of an object
  10390. * @param {Object|String} [options] Options object or string (e.g. "2px 2px 10px rgba(0,0,0,0.2)")
  10391. * @param {String} [options.color=rgb(0,0,0)] Shadow color
  10392. * @param {Number} [options.blur=0] Shadow blur
  10393. * @param {Number} [options.offsetX=0] Shadow horizontal offset
  10394. * @param {Number} [options.offsetY=0] Shadow vertical offset
  10395. * @return {fabric.Object} thisArg
  10396. * @chainable
  10397. * @see {@link http://jsfiddle.net/fabricjs/7gvJG/|jsFiddle demo}
  10398. * @example <caption>Set shadow with string notation</caption>
  10399. * object.setShadow('2px 2px 10px rgba(0,0,0,0.2)');
  10400. * canvas.renderAll();
  10401. * @example <caption>Set shadow with object notation</caption>
  10402. * object.setShadow({
  10403. * color: 'red',
  10404. * blur: 10,
  10405. * offsetX: 20,
  10406. * offsetY: 20
  10407. * });
  10408. * canvas.renderAll();
  10409. */
  10410. setShadow: function(options) {
  10411. return this.set('shadow', options ? new fabric.Shadow(options) : null);
  10412. },
  10413. /**
  10414. * Sets "color" of an instance (alias of `set('fill', &hellip;)`)
  10415. * @param {String} color Color value
  10416. * @return {fabric.Object} thisArg
  10417. * @chainable
  10418. */
  10419. setColor: function(color) {
  10420. this.set('fill', color);
  10421. return this;
  10422. },
  10423. /**
  10424. * Sets "angle" of an instance
  10425. * @param {Number} angle Angle value
  10426. * @return {fabric.Object} thisArg
  10427. * @chainable
  10428. */
  10429. setAngle: function(angle) {
  10430. var shouldCenterOrigin = (this.originX !== 'center' || this.originY !== 'center') && this.centeredRotation;
  10431. if (shouldCenterOrigin) {
  10432. this._setOriginToCenter();
  10433. }
  10434. this.set('angle', angle);
  10435. if (shouldCenterOrigin) {
  10436. this._resetOrigin();
  10437. }
  10438. return this;
  10439. },
  10440. /**
  10441. * Centers object horizontally on canvas to which it was added last.
  10442. * You might need to call `setCoords` on an object after centering, to update controls area.
  10443. * @return {fabric.Object} thisArg
  10444. * @chainable
  10445. */
  10446. centerH: function () {
  10447. this.canvas.centerObjectH(this);
  10448. return this;
  10449. },
  10450. /**
  10451. * Centers object vertically on canvas to which it was added last.
  10452. * You might need to call `setCoords` on an object after centering, to update controls area.
  10453. * @return {fabric.Object} thisArg
  10454. * @chainable
  10455. */
  10456. centerV: function () {
  10457. this.canvas.centerObjectV(this);
  10458. return this;
  10459. },
  10460. /**
  10461. * Centers object vertically and horizontally on canvas to which is was added last
  10462. * You might need to call `setCoords` on an object after centering, to update controls area.
  10463. * @return {fabric.Object} thisArg
  10464. * @chainable
  10465. */
  10466. center: function () {
  10467. this.canvas.centerObject(this);
  10468. return this;
  10469. },
  10470. /**
  10471. * Removes object from canvas to which it was added last
  10472. * @return {fabric.Object} thisArg
  10473. * @chainable
  10474. */
  10475. remove: function() {
  10476. this.canvas.remove(this);
  10477. return this;
  10478. },
  10479. /**
  10480. * Returns coordinates of a pointer relative to an object
  10481. * @param {Event} e Event to operate upon
  10482. * @param {Object} [pointer] Pointer to operate upon (instead of event)
  10483. * @return {Object} Coordinates of a pointer (x, y)
  10484. */
  10485. getLocalPointer: function(e, pointer) {
  10486. pointer = pointer || this.canvas.getPointer(e);
  10487. var objectLeftTop = this.translateToOriginPoint(this.getCenterPoint(), 'left', 'top');
  10488. return {
  10489. x: pointer.x - objectLeftTop.x,
  10490. y: pointer.y - objectLeftTop.y
  10491. };
  10492. },
  10493. /**
  10494. * Sets canvas globalCompositeOperation for specific object
  10495. * custom composition operation for the particular object can be specifed using fillRule property
  10496. * @param {CanvasRenderingContext2D} ctx Rendering canvas context
  10497. */
  10498. _setupFillRule: function (ctx) {
  10499. if (this.fillRule) {
  10500. this._prevFillRule = ctx.globalCompositeOperation;
  10501. ctx.globalCompositeOperation = this.fillRule;
  10502. }
  10503. },
  10504. /**
  10505. * Restores previously saved canvas globalCompositeOperation after obeject rendering
  10506. * @param {CanvasRenderingContext2D} ctx Rendering canvas context
  10507. */
  10508. _restoreFillRule: function (ctx) {
  10509. if (this.fillRule && this._prevFillRule) {
  10510. ctx.globalCompositeOperation = this._prevFillRule;
  10511. }
  10512. }
  10513. });
  10514. fabric.util.createAccessors(fabric.Object);
  10515. /**
  10516. * Alias for {@link fabric.Object.prototype.setAngle}
  10517. * @alias rotate -> setAngle
  10518. * @memberof fabric.Object
  10519. */
  10520. fabric.Object.prototype.rotate = fabric.Object.prototype.setAngle;
  10521. extend(fabric.Object.prototype, fabric.Observable);
  10522. /**
  10523. * Defines the number of fraction digits to use when serializing object values.
  10524. * You can use it to increase/decrease precision of such values like left, top, scaleX, scaleY, etc.
  10525. * @static
  10526. * @memberof fabric.Object
  10527. * @constant
  10528. * @type Number
  10529. */
  10530. fabric.Object.NUM_FRACTION_DIGITS = 2;
  10531. /**
  10532. * Unique id used internally when creating SVG elements
  10533. * @static
  10534. * @memberof fabric.Object
  10535. * @type Number
  10536. */
  10537. fabric.Object.__uid = 0;
  10538. })(typeof exports !== 'undefined' ? exports : this);
  10539. (function() {
  10540. var degreesToRadians = fabric.util.degreesToRadians;
  10541. fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prototype */ {
  10542. /**
  10543. * Translates the coordinates from origin to center coordinates (based on the object's dimensions)
  10544. * @param {fabric.Point} point The point which corresponds to the originX and originY params
  10545. * @param {String} originX Horizontal origin: 'left', 'center' or 'right'
  10546. * @param {String} originY Vertical origin: 'top', 'center' or 'bottom'
  10547. * @return {fabric.Point}
  10548. */
  10549. translateToCenterPoint: function(point, originX, originY) {
  10550. var cx = point.x,
  10551. cy = point.y,
  10552. strokeWidth = this.stroke ? this.strokeWidth : 0;
  10553. if (originX === 'left') {
  10554. cx = point.x + (this.getWidth() + strokeWidth * this.scaleX) / 2;
  10555. }
  10556. else if (originX === 'right') {
  10557. cx = point.x - (this.getWidth() + strokeWidth * this.scaleX) / 2;
  10558. }
  10559. if (originY === 'top') {
  10560. cy = point.y + (this.getHeight() + strokeWidth * this.scaleY) / 2;
  10561. }
  10562. else if (originY === 'bottom') {
  10563. cy = point.y - (this.getHeight() + strokeWidth * this.scaleY) / 2;
  10564. }
  10565. // Apply the reverse rotation to the point (it's already scaled properly)
  10566. return fabric.util.rotatePoint(new fabric.Point(cx, cy), point, degreesToRadians(this.angle));
  10567. },
  10568. /**
  10569. * Translates the coordinates from center to origin coordinates (based on the object's dimensions)
  10570. * @param {fabric.Point} center The point which corresponds to center of the object
  10571. * @param {String} originX Horizontal origin: 'left', 'center' or 'right'
  10572. * @param {String} originY Vertical origin: 'top', 'center' or 'bottom'
  10573. * @return {fabric.Point}
  10574. */
  10575. translateToOriginPoint: function(center, originX, originY) {
  10576. var x = center.x,
  10577. y = center.y,
  10578. strokeWidth = this.stroke ? this.strokeWidth : 0;
  10579. // Get the point coordinates
  10580. if (originX === 'left') {
  10581. x = center.x - (this.getWidth() + strokeWidth * this.scaleX) / 2;
  10582. }
  10583. else if (originX === 'right') {
  10584. x = center.x + (this.getWidth() + strokeWidth * this.scaleX) / 2;
  10585. }
  10586. if (originY === 'top') {
  10587. y = center.y - (this.getHeight() + strokeWidth * this.scaleY) / 2;
  10588. }
  10589. else if (originY === 'bottom') {
  10590. y = center.y + (this.getHeight() + strokeWidth * this.scaleY) / 2;
  10591. }
  10592. // Apply the rotation to the point (it's already scaled properly)
  10593. return fabric.util.rotatePoint(new fabric.Point(x, y), center, degreesToRadians(this.angle));
  10594. },
  10595. /**
  10596. * Returns the real center coordinates of the object
  10597. * @return {fabric.Point}
  10598. */
  10599. getCenterPoint: function() {
  10600. var leftTop = new fabric.Point(this.left, this.top);
  10601. return this.translateToCenterPoint(leftTop, this.originX, this.originY);
  10602. },
  10603. /**
  10604. * Returns the coordinates of the object based on center coordinates
  10605. * @param {fabric.Point} point The point which corresponds to the originX and originY params
  10606. * @return {fabric.Point}
  10607. */
  10608. // getOriginPoint: function(center) {
  10609. // return this.translateToOriginPoint(center, this.originX, this.originY);
  10610. // },
  10611. /**
  10612. * Returns the coordinates of the object as if it has a different origin
  10613. * @param {String} originX Horizontal origin: 'left', 'center' or 'right'
  10614. * @param {String} originY Vertical origin: 'top', 'center' or 'bottom'
  10615. * @return {fabric.Point}
  10616. */
  10617. getPointByOrigin: function(originX, originY) {
  10618. var center = this.getCenterPoint();
  10619. return this.translateToOriginPoint(center, originX, originY);
  10620. },
  10621. /**
  10622. * Returns the point in local coordinates
  10623. * @param {fabric.Point} point The point relative to the global coordinate system
  10624. * @param {String} originX Horizontal origin: 'left', 'center' or 'right'
  10625. * @param {String} originY Vertical origin: 'top', 'center' or 'bottom'
  10626. * @return {fabric.Point}
  10627. */
  10628. toLocalPoint: function(point, originX, originY) {
  10629. var center = this.getCenterPoint(),
  10630. strokeWidth = this.stroke ? this.strokeWidth : 0,
  10631. x, y;
  10632. if (originX && originY) {
  10633. if (originX === 'left') {
  10634. x = center.x - (this.getWidth() + strokeWidth * this.scaleX) / 2;
  10635. }
  10636. else if (originX === 'right') {
  10637. x = center.x + (this.getWidth() + strokeWidth * this.scaleX) / 2;
  10638. }
  10639. else {
  10640. x = center.x;
  10641. }
  10642. if (originY === 'top') {
  10643. y = center.y - (this.getHeight() + strokeWidth * this.scaleY) / 2;
  10644. }
  10645. else if (originY === 'bottom') {
  10646. y = center.y + (this.getHeight() + strokeWidth * this.scaleY) / 2;
  10647. }
  10648. else {
  10649. y = center.y;
  10650. }
  10651. }
  10652. else {
  10653. x = this.left;
  10654. y = this.top;
  10655. }
  10656. return fabric.util.rotatePoint(new fabric.Point(point.x, point.y), center, -degreesToRadians(this.angle))
  10657. .subtractEquals(new fabric.Point(x, y));
  10658. },
  10659. /**
  10660. * Returns the point in global coordinates
  10661. * @param {fabric.Point} The point relative to the local coordinate system
  10662. * @return {fabric.Point}
  10663. */
  10664. // toGlobalPoint: function(point) {
  10665. // return fabric.util.rotatePoint(point, this.getCenterPoint(), degreesToRadians(this.angle)).addEquals(new fabric.Point(this.left, this.top));
  10666. // },
  10667. /**
  10668. * Sets the position of the object taking into consideration the object's origin
  10669. * @param {fabric.Point} pos The new position of the object
  10670. * @param {String} originX Horizontal origin: 'left', 'center' or 'right'
  10671. * @param {String} originY Vertical origin: 'top', 'center' or 'bottom'
  10672. * @return {void}
  10673. */
  10674. setPositionByOrigin: function(pos, originX, originY) {
  10675. var center = this.translateToCenterPoint(pos, originX, originY),
  10676. position = this.translateToOriginPoint(center, this.originX, this.originY);
  10677. this.set('left', position.x);
  10678. this.set('top', position.y);
  10679. },
  10680. /**
  10681. * @param {String} to One of 'left', 'center', 'right'
  10682. */
  10683. adjustPosition: function(to) {
  10684. var angle = degreesToRadians(this.angle),
  10685. hypotHalf = this.getWidth() / 2,
  10686. xHalf = Math.cos(angle) * hypotHalf,
  10687. yHalf = Math.sin(angle) * hypotHalf,
  10688. hypotFull = this.getWidth(),
  10689. xFull = Math.cos(angle) * hypotFull,
  10690. yFull = Math.sin(angle) * hypotFull;
  10691. if (this.originX === 'center' && to === 'left' ||
  10692. this.originX === 'right' && to === 'center') {
  10693. // move half left
  10694. this.left -= xHalf;
  10695. this.top -= yHalf;
  10696. }
  10697. else if (this.originX === 'left' && to === 'center' ||
  10698. this.originX === 'center' && to === 'right') {
  10699. // move half right
  10700. this.left += xHalf;
  10701. this.top += yHalf;
  10702. }
  10703. else if (this.originX === 'left' && to === 'right') {
  10704. // move full right
  10705. this.left += xFull;
  10706. this.top += yFull;
  10707. }
  10708. else if (this.originX === 'right' && to === 'left') {
  10709. // move full left
  10710. this.left -= xFull;
  10711. this.top -= yFull;
  10712. }
  10713. this.setCoords();
  10714. this.originX = to;
  10715. },
  10716. /**
  10717. * Sets the origin/position of the object to it's center point
  10718. * @private
  10719. * @return {void}
  10720. */
  10721. _setOriginToCenter: function() {
  10722. this._originalOriginX = this.originX;
  10723. this._originalOriginY = this.originY;
  10724. var center = this.getCenterPoint();
  10725. this.originX = 'center';
  10726. this.originY = 'center';
  10727. this.left = center.x;
  10728. this.top = center.y;
  10729. },
  10730. /**
  10731. * Resets the origin/position of the object to it's original origin
  10732. * @private
  10733. * @return {void}
  10734. */
  10735. _resetOrigin: function() {
  10736. var originPoint = this.translateToOriginPoint(
  10737. this.getCenterPoint(),
  10738. this._originalOriginX,
  10739. this._originalOriginY);
  10740. this.originX = this._originalOriginX;
  10741. this.originY = this._originalOriginY;
  10742. this.left = originPoint.x;
  10743. this.top = originPoint.y;
  10744. this._originalOriginX = null;
  10745. this._originalOriginY = null;
  10746. },
  10747. /**
  10748. * @private
  10749. */
  10750. _getLeftTopCoords: function() {
  10751. return this.translateToOriginPoint(this.getCenterPoint(), 'left', 'center');
  10752. }
  10753. });
  10754. })();
  10755. (function() {
  10756. var degreesToRadians = fabric.util.degreesToRadians;
  10757. fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prototype */ {
  10758. /**
  10759. * Object containing coordinates of object's controls
  10760. * @type Object
  10761. * @default
  10762. */
  10763. oCoords: null,
  10764. /**
  10765. * Checks if object intersects with an area formed by 2 points
  10766. * @param {Object} pointTL top-left point of area
  10767. * @param {Object} pointBR bottom-right point of area
  10768. * @return {Boolean} true if object intersects with an area formed by 2 points
  10769. */
  10770. intersectsWithRect: function(pointTL, pointBR) {
  10771. var oCoords = this.oCoords,
  10772. tl = new fabric.Point(oCoords.tl.x, oCoords.tl.y),
  10773. tr = new fabric.Point(oCoords.tr.x, oCoords.tr.y),
  10774. bl = new fabric.Point(oCoords.bl.x, oCoords.bl.y),
  10775. br = new fabric.Point(oCoords.br.x, oCoords.br.y),
  10776. intersection = fabric.Intersection.intersectPolygonRectangle(
  10777. [tl, tr, br, bl],
  10778. pointTL,
  10779. pointBR
  10780. );
  10781. return intersection.status === 'Intersection';
  10782. },
  10783. /**
  10784. * Checks if object intersects with another object
  10785. * @param {Object} other Object to test
  10786. * @return {Boolean} true if object intersects with another object
  10787. */
  10788. intersectsWithObject: function(other) {
  10789. // extracts coords
  10790. function getCoords(oCoords) {
  10791. return {
  10792. tl: new fabric.Point(oCoords.tl.x, oCoords.tl.y),
  10793. tr: new fabric.Point(oCoords.tr.x, oCoords.tr.y),
  10794. bl: new fabric.Point(oCoords.bl.x, oCoords.bl.y),
  10795. br: new fabric.Point(oCoords.br.x, oCoords.br.y)
  10796. };
  10797. }
  10798. var thisCoords = getCoords(this.oCoords),
  10799. otherCoords = getCoords(other.oCoords),
  10800. intersection = fabric.Intersection.intersectPolygonPolygon(
  10801. [thisCoords.tl, thisCoords.tr, thisCoords.br, thisCoords.bl],
  10802. [otherCoords.tl, otherCoords.tr, otherCoords.br, otherCoords.bl]
  10803. );
  10804. return intersection.status === 'Intersection';
  10805. },
  10806. /**
  10807. * Checks if object is fully contained within area of another object
  10808. * @param {Object} other Object to test
  10809. * @return {Boolean} true if object is fully contained within area of another object
  10810. */
  10811. isContainedWithinObject: function(other) {
  10812. var boundingRect = other.getBoundingRect(),
  10813. point1 = new fabric.Point(boundingRect.left, boundingRect.top),
  10814. point2 = new fabric.Point(boundingRect.left + boundingRect.width, boundingRect.top + boundingRect.height);
  10815. return this.isContainedWithinRect(point1, point2);
  10816. },
  10817. /**
  10818. * Checks if object is fully contained within area formed by 2 points
  10819. * @param {Object} pointTL top-left point of area
  10820. * @param {Object} pointBR bottom-right point of area
  10821. * @return {Boolean} true if object is fully contained within area formed by 2 points
  10822. */
  10823. isContainedWithinRect: function(pointTL, pointBR) {
  10824. var boundingRect = this.getBoundingRect();
  10825. return (
  10826. boundingRect.left >= pointTL.x &&
  10827. boundingRect.left + boundingRect.width <= pointBR.x &&
  10828. boundingRect.top >= pointTL.y &&
  10829. boundingRect.top + boundingRect.height <= pointBR.y
  10830. );
  10831. },
  10832. /**
  10833. * Checks if point is inside the object
  10834. * @param {fabric.Point} point Point to check against
  10835. * @return {Boolean} true if point is inside the object
  10836. */
  10837. containsPoint: function(point) {
  10838. var lines = this._getImageLines(this.oCoords),
  10839. xPoints = this._findCrossPoints(point, lines);
  10840. // if xPoints is odd then point is inside the object
  10841. return (xPoints !== 0 && xPoints % 2 === 1);
  10842. },
  10843. /**
  10844. * Method that returns an object with the object edges in it, given the coordinates of the corners
  10845. * @private
  10846. * @param {Object} oCoords Coordinates of the object corners
  10847. */
  10848. _getImageLines: function(oCoords) {
  10849. return {
  10850. topline: {
  10851. o: oCoords.tl,
  10852. d: oCoords.tr
  10853. },
  10854. rightline: {
  10855. o: oCoords.tr,
  10856. d: oCoords.br
  10857. },
  10858. bottomline: {
  10859. o: oCoords.br,
  10860. d: oCoords.bl
  10861. },
  10862. leftline: {
  10863. o: oCoords.bl,
  10864. d: oCoords.tl
  10865. }
  10866. };
  10867. },
  10868. /**
  10869. * Helper method to determine how many cross points are between the 4 object edges
  10870. * and the horizontal line determined by a point on canvas
  10871. * @private
  10872. * @param {fabric.Point} point Point to check
  10873. * @param {Object} oCoords Coordinates of the object being evaluated
  10874. */
  10875. _findCrossPoints: function(point, oCoords) {
  10876. var b1, b2, a1, a2, xi, yi,
  10877. xcount = 0,
  10878. iLine;
  10879. for (var lineKey in oCoords) {
  10880. iLine = oCoords[lineKey];
  10881. // optimisation 1: line below point. no cross
  10882. if ((iLine.o.y < point.y) && (iLine.d.y < point.y)) {
  10883. continue;
  10884. }
  10885. // optimisation 2: line above point. no cross
  10886. if ((iLine.o.y >= point.y) && (iLine.d.y >= point.y)) {
  10887. continue;
  10888. }
  10889. // optimisation 3: vertical line case
  10890. if ((iLine.o.x === iLine.d.x) && (iLine.o.x >= point.x)) {
  10891. xi = iLine.o.x;
  10892. yi = point.y;
  10893. }
  10894. // calculate the intersection point
  10895. else {
  10896. b1 = 0;
  10897. b2 = (iLine.d.y - iLine.o.y) / (iLine.d.x - iLine.o.x);
  10898. a1 = point.y - b1 * point.x;
  10899. a2 = iLine.o.y - b2 * iLine.o.x;
  10900. xi = - (a1 - a2) / (b1 - b2);
  10901. yi = a1 + b1 * xi;
  10902. }
  10903. // dont count xi < point.x cases
  10904. if (xi >= point.x) {
  10905. xcount += 1;
  10906. }
  10907. // optimisation 4: specific for square images
  10908. if (xcount === 2) {
  10909. break;
  10910. }
  10911. }
  10912. return xcount;
  10913. },
  10914. /**
  10915. * Returns width of an object's bounding rectangle
  10916. * @deprecated since 1.0.4
  10917. * @return {Number} width value
  10918. */
  10919. getBoundingRectWidth: function() {
  10920. return this.getBoundingRect().width;
  10921. },
  10922. /**
  10923. * Returns height of an object's bounding rectangle
  10924. * @deprecated since 1.0.4
  10925. * @return {Number} height value
  10926. */
  10927. getBoundingRectHeight: function() {
  10928. return this.getBoundingRect().height;
  10929. },
  10930. /**
  10931. * Returns coordinates of object's bounding rectangle (left, top, width, height)
  10932. * @return {Object} Object with left, top, width, height properties
  10933. */
  10934. getBoundingRect: function() {
  10935. this.oCoords || this.setCoords();
  10936. var xCoords = [this.oCoords.tl.x, this.oCoords.tr.x, this.oCoords.br.x, this.oCoords.bl.x],
  10937. minX = fabric.util.array.min(xCoords),
  10938. maxX = fabric.util.array.max(xCoords),
  10939. width = Math.abs(minX - maxX),
  10940. yCoords = [this.oCoords.tl.y, this.oCoords.tr.y, this.oCoords.br.y, this.oCoords.bl.y],
  10941. minY = fabric.util.array.min(yCoords),
  10942. maxY = fabric.util.array.max(yCoords),
  10943. height = Math.abs(minY - maxY);
  10944. return {
  10945. left: minX,
  10946. top: minY,
  10947. width: width,
  10948. height: height
  10949. };
  10950. },
  10951. /**
  10952. * Returns width of an object
  10953. * @return {Number} width value
  10954. */
  10955. getWidth: function() {
  10956. return this.width * this.scaleX;
  10957. },
  10958. /**
  10959. * Returns height of an object
  10960. * @return {Number} height value
  10961. */
  10962. getHeight: function() {
  10963. return this.height * this.scaleY;
  10964. },
  10965. /**
  10966. * Makes sure the scale is valid and modifies it if necessary
  10967. * @private
  10968. * @param {Number} value
  10969. * @return {Number}
  10970. */
  10971. _constrainScale: function(value) {
  10972. if (Math.abs(value) < this.minScaleLimit) {
  10973. if (value < 0) {
  10974. return -this.minScaleLimit;
  10975. }
  10976. else {
  10977. return this.minScaleLimit;
  10978. }
  10979. }
  10980. return value;
  10981. },
  10982. /**
  10983. * Scales an object (equally by x and y)
  10984. * @param {Number} value Scale factor
  10985. * @return {fabric.Object} thisArg
  10986. * @chainable
  10987. */
  10988. scale: function(value) {
  10989. value = this._constrainScale(value);
  10990. if (value < 0) {
  10991. this.flipX = !this.flipX;
  10992. this.flipY = !this.flipY;
  10993. value *= -1;
  10994. }
  10995. this.scaleX = value;
  10996. this.scaleY = value;
  10997. this.setCoords();
  10998. return this;
  10999. },
  11000. /**
  11001. * Scales an object to a given width, with respect to bounding box (scaling by x/y equally)
  11002. * @param {Number} value New width value
  11003. * @return {fabric.Object} thisArg
  11004. * @chainable
  11005. */
  11006. scaleToWidth: function(value) {
  11007. // adjust to bounding rect factor so that rotated shapes would fit as well
  11008. var boundingRectFactor = this.getBoundingRectWidth() / this.getWidth();
  11009. return this.scale(value / this.width / boundingRectFactor);
  11010. },
  11011. /**
  11012. * Scales an object to a given height, with respect to bounding box (scaling by x/y equally)
  11013. * @param {Number} value New height value
  11014. * @return {fabric.Object} thisArg
  11015. * @chainable
  11016. */
  11017. scaleToHeight: function(value) {
  11018. // adjust to bounding rect factor so that rotated shapes would fit as well
  11019. var boundingRectFactor = this.getBoundingRectHeight() / this.getHeight();
  11020. return this.scale(value / this.height / boundingRectFactor);
  11021. },
  11022. /**
  11023. * Sets corner position coordinates based on current angle, width and height
  11024. * @return {fabric.Object} thisArg
  11025. * @chainable
  11026. */
  11027. setCoords: function() {
  11028. var strokeWidth = this.strokeWidth > 1 ? this.strokeWidth : 0,
  11029. theta = degreesToRadians(this.angle),
  11030. vpt = this.getViewportTransform(),
  11031. f = function (p) {
  11032. return fabric.util.transformPoint(p, vpt);
  11033. },
  11034. w = this.width,
  11035. h = this.height,
  11036. capped = this.strokeLineCap === 'round' || this.strokeLineCap === 'square',
  11037. vLine = this.type === 'line' && this.width === 1,
  11038. hLine = this.type === 'line' && this.height === 1,
  11039. strokeW = (capped && hLine) || this.type !== 'line',
  11040. strokeH = (capped && vLine) || this.type !== 'line';
  11041. if (vLine) {
  11042. w = strokeWidth;
  11043. }
  11044. else if (hLine) {
  11045. h = strokeWidth;
  11046. }
  11047. if (strokeW) {
  11048. w += strokeWidth;
  11049. }
  11050. if (strokeH) {
  11051. h += strokeWidth;
  11052. }
  11053. this.currentWidth = w * this.scaleX;
  11054. this.currentHeight = h * this.scaleY;
  11055. // If width is negative, make postive. Fixes path selection issue
  11056. if (this.currentWidth < 0) {
  11057. this.currentWidth = Math.abs(this.currentWidth);
  11058. }
  11059. var _hypotenuse = Math.sqrt(
  11060. Math.pow(this.currentWidth / 2, 2) +
  11061. Math.pow(this.currentHeight / 2, 2)),
  11062. _angle = Math.atan(isFinite(this.currentHeight / this.currentWidth) ? this.currentHeight / this.currentWidth : 0),
  11063. // offset added for rotate and scale actions
  11064. offsetX = Math.cos(_angle + theta) * _hypotenuse,
  11065. offsetY = Math.sin(_angle + theta) * _hypotenuse,
  11066. sinTh = Math.sin(theta),
  11067. cosTh = Math.cos(theta),
  11068. coords = this.getCenterPoint(),
  11069. wh = new fabric.Point(this.currentWidth, this.currentHeight),
  11070. _tl = new fabric.Point(coords.x - offsetX, coords.y - offsetY),
  11071. _tr = new fabric.Point(_tl.x + (wh.x * cosTh), _tl.y + (wh.x * sinTh)),
  11072. _bl = new fabric.Point(_tl.x - (wh.y * sinTh), _tl.y + (wh.y * cosTh)),
  11073. _mt = new fabric.Point(_tl.x + (wh.x/2 * cosTh), _tl.y + (wh.x/2 * sinTh)),
  11074. tl = f(_tl),
  11075. tr = f(_tr),
  11076. br = f(new fabric.Point(_tr.x - (wh.y * sinTh), _tr.y + (wh.y * cosTh))),
  11077. bl = f(_bl),
  11078. ml = f(new fabric.Point(_tl.x - (wh.y/2 * sinTh), _tl.y + (wh.y/2 * cosTh))),
  11079. mt = f(_mt),
  11080. mr = f(new fabric.Point(_tr.x - (wh.y/2 * sinTh), _tr.y + (wh.y/2 * cosTh))),
  11081. mb = f(new fabric.Point(_bl.x + (wh.x/2 * cosTh), _bl.y + (wh.x/2 * sinTh))),
  11082. mtr = f(new fabric.Point(_mt.x, _mt.y)),
  11083. // padding
  11084. padX = Math.cos(_angle + theta) * this.padding * Math.sqrt(2),
  11085. padY = Math.sin(_angle + theta) * this.padding * Math.sqrt(2);
  11086. tl = tl.add(new fabric.Point(-padX, -padY));
  11087. tr = tr.add(new fabric.Point(padY, -padX));
  11088. br = br.add(new fabric.Point(padX, padY));
  11089. bl = bl.add(new fabric.Point(-padY, padX));
  11090. ml = ml.add(new fabric.Point((-padX - padY) / 2, (-padY + padX) / 2));
  11091. mt = mt.add(new fabric.Point((padY - padX) / 2, -(padY + padX) / 2));
  11092. mr = mr.add(new fabric.Point((padY + padX) / 2, (padY - padX) / 2));
  11093. mb = mb.add(new fabric.Point((padX - padY) / 2, (padX + padY) / 2));
  11094. mtr = mtr.add(new fabric.Point((padY - padX) / 2, -(padY + padX) / 2));
  11095. // debugging
  11096. // setTimeout(function() {
  11097. // canvas.contextTop.fillStyle = 'green';
  11098. // canvas.contextTop.fillRect(mb.x, mb.y, 3, 3);
  11099. // canvas.contextTop.fillRect(bl.x, bl.y, 3, 3);
  11100. // canvas.contextTop.fillRect(br.x, br.y, 3, 3);
  11101. // canvas.contextTop.fillRect(tl.x, tl.y, 3, 3);
  11102. // canvas.contextTop.fillRect(tr.x, tr.y, 3, 3);
  11103. // canvas.contextTop.fillRect(ml.x, ml.y, 3, 3);
  11104. // canvas.contextTop.fillRect(mr.x, mr.y, 3, 3);
  11105. // canvas.contextTop.fillRect(mt.x, mt.y, 3, 3);
  11106. // }, 50);
  11107. this.oCoords = {
  11108. // corners
  11109. tl: tl, tr: tr, br: br, bl: bl,
  11110. // middle
  11111. ml: ml, mt: mt, mr: mr, mb: mb,
  11112. // rotating point
  11113. mtr: mtr
  11114. };
  11115. // set coordinates of the draggable boxes in the corners used to scale/rotate the image
  11116. this._setCornerCoords && this._setCornerCoords();
  11117. return this;
  11118. }
  11119. });
  11120. })();
  11121. fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prototype */ {
  11122. /**
  11123. * Moves an object to the bottom of the stack of drawn objects
  11124. * @return {fabric.Object} thisArg
  11125. * @chainable
  11126. */
  11127. sendToBack: function() {
  11128. if (this.group) {
  11129. fabric.StaticCanvas.prototype.sendToBack.call(this.group, this);
  11130. }
  11131. else {
  11132. this.canvas.sendToBack(this);
  11133. }
  11134. return this;
  11135. },
  11136. /**
  11137. * Moves an object to the top of the stack of drawn objects
  11138. * @return {fabric.Object} thisArg
  11139. * @chainable
  11140. */
  11141. bringToFront: function() {
  11142. if (this.group) {
  11143. fabric.StaticCanvas.prototype.bringToFront.call(this.group, this);
  11144. }
  11145. else {
  11146. this.canvas.bringToFront(this);
  11147. }
  11148. return this;
  11149. },
  11150. /**
  11151. * Moves an object down in stack of drawn objects
  11152. * @param {Boolean} [intersecting] If `true`, send object behind next lower intersecting object
  11153. * @return {fabric.Object} thisArg
  11154. * @chainable
  11155. */
  11156. sendBackwards: function(intersecting) {
  11157. if (this.group) {
  11158. fabric.StaticCanvas.prototype.sendBackwards.call(this.group, this, intersecting);
  11159. }
  11160. else {
  11161. this.canvas.sendBackwards(this, intersecting);
  11162. }
  11163. return this;
  11164. },
  11165. /**
  11166. * Moves an object up in stack of drawn objects
  11167. * @param {Boolean} [intersecting] If `true`, send object in front of next upper intersecting object
  11168. * @return {fabric.Object} thisArg
  11169. * @chainable
  11170. */
  11171. bringForward: function(intersecting) {
  11172. if (this.group) {
  11173. fabric.StaticCanvas.prototype.bringForward.call(this.group, this, intersecting);
  11174. }
  11175. else {
  11176. this.canvas.bringForward(this, intersecting);
  11177. }
  11178. return this;
  11179. },
  11180. /**
  11181. * Moves an object to specified level in stack of drawn objects
  11182. * @param {Number} index New position of object
  11183. * @return {fabric.Object} thisArg
  11184. * @chainable
  11185. */
  11186. moveTo: function(index) {
  11187. if (this.group) {
  11188. fabric.StaticCanvas.prototype.moveTo.call(this.group, this, index);
  11189. }
  11190. else {
  11191. this.canvas.moveTo(this, index);
  11192. }
  11193. return this;
  11194. }
  11195. });
  11196. /* _TO_SVG_START_ */
  11197. fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prototype */ {
  11198. /**
  11199. * Returns styles-string for svg-export
  11200. * @return {String}
  11201. */
  11202. getSvgStyles: function() {
  11203. var fill = this.fill
  11204. ? (this.fill.toLive ? 'url(#SVGID_' + this.fill.id + ')' : this.fill)
  11205. : 'none',
  11206. fillRule = (this.fillRule === 'destination-over' ? 'evenodd' : this.fillRule),
  11207. stroke = this.stroke
  11208. ? (this.stroke.toLive ? 'url(#SVGID_' + this.stroke.id + ')' : this.stroke)
  11209. : 'none',
  11210. strokeWidth = this.strokeWidth ? this.strokeWidth : '0',
  11211. strokeDashArray = this.strokeDashArray ? this.strokeDashArray.join(' ') : '',
  11212. strokeLineCap = this.strokeLineCap ? this.strokeLineCap : 'butt',
  11213. strokeLineJoin = this.strokeLineJoin ? this.strokeLineJoin : 'miter',
  11214. strokeMiterLimit = this.strokeMiterLimit ? this.strokeMiterLimit : '4',
  11215. opacity = typeof this.opacity !== 'undefined' ? this.opacity : '1',
  11216. visibility = this.visible ? '' : ' visibility: hidden;',
  11217. filter = this.shadow && this.type !== 'text' ? 'filter: url(#SVGID_' + this.shadow.id + ');' : '';
  11218. return [
  11219. 'stroke: ', stroke, '; ',
  11220. 'stroke-width: ', strokeWidth, '; ',
  11221. 'stroke-dasharray: ', strokeDashArray, '; ',
  11222. 'stroke-linecap: ', strokeLineCap, '; ',
  11223. 'stroke-linejoin: ', strokeLineJoin, '; ',
  11224. 'stroke-miterlimit: ', strokeMiterLimit, '; ',
  11225. 'fill: ', fill, '; ',
  11226. 'fill-rule: ', fillRule, '; ',
  11227. 'opacity: ', opacity, ';',
  11228. filter,
  11229. visibility
  11230. ].join('');
  11231. },
  11232. /**
  11233. * Returns transform-string for svg-export
  11234. * @return {String}
  11235. */
  11236. getSvgTransform: function() {
  11237. if (this.group) {
  11238. return '';
  11239. }
  11240. var toFixed = fabric.util.toFixed,
  11241. angle = this.getAngle(),
  11242. vpt = !this.canvas || this.canvas.svgViewportTransformation ? this.getViewportTransform() : [1, 0, 0, 1, 0, 0],
  11243. center = fabric.util.transformPoint(this.getCenterPoint(), vpt),
  11244. NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS,
  11245. translatePart = this.type === 'path-group' ? '' : 'translate(' +
  11246. toFixed(center.x, NUM_FRACTION_DIGITS) +
  11247. ' ' +
  11248. toFixed(center.y, NUM_FRACTION_DIGITS) +
  11249. ')',
  11250. anglePart = angle !== 0
  11251. ? (' rotate(' + toFixed(angle, NUM_FRACTION_DIGITS) + ')')
  11252. : '',
  11253. scalePart = (this.scaleX === 1 && this.scaleY === 1 && vpt[0] === 1 && vpt[3] === 1)
  11254. ? '' :
  11255. (' scale(' +
  11256. toFixed(this.scaleX * vpt[0], NUM_FRACTION_DIGITS) +
  11257. ' ' +
  11258. toFixed(this.scaleY * vpt[3], NUM_FRACTION_DIGITS) +
  11259. ')'),
  11260. addTranslateX = this.type === 'path-group' ? this.width * vpt[0] : 0,
  11261. flipXPart = this.flipX ? ' matrix(-1 0 0 1 ' + addTranslateX + ' 0) ' : '',
  11262. addTranslateY = this.type === 'path-group' ? this.height * vpt[3] : 0,
  11263. flipYPart = this.flipY ? ' matrix(1 0 0 -1 0 ' + addTranslateY + ')' : '';
  11264. return [
  11265. translatePart, anglePart, scalePart, flipXPart, flipYPart
  11266. ].join('');
  11267. },
  11268. /**
  11269. * Returns transform-string for svg-export from the transform matrix of single elements
  11270. * @return {String}
  11271. */
  11272. getSvgTransformMatrix: function() {
  11273. return this.transformMatrix ? ' matrix(' + this.transformMatrix.join(' ') + ')' : '';
  11274. },
  11275. /**
  11276. * @private
  11277. */
  11278. _createBaseSVGMarkup: function() {
  11279. var markup = [ ];
  11280. if (this.fill && this.fill.toLive) {
  11281. markup.push(this.fill.toSVG(this, false));
  11282. }
  11283. if (this.stroke && this.stroke.toLive) {
  11284. markup.push(this.stroke.toSVG(this, false));
  11285. }
  11286. if (this.shadow) {
  11287. markup.push(this.shadow.toSVG(this));
  11288. }
  11289. return markup;
  11290. }
  11291. });
  11292. /* _TO_SVG_END_ */
  11293. /*
  11294. Depends on `stateProperties`
  11295. */
  11296. fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prototype */ {
  11297. /**
  11298. * Returns true if object state (one of its state properties) was changed
  11299. * @return {Boolean} true if instance' state has changed since `{@link fabric.Object#saveState}` was called
  11300. */
  11301. hasStateChanged: function() {
  11302. return this.stateProperties.some(function(prop) {
  11303. return this.get(prop) !== this.originalState[prop];
  11304. }, this);
  11305. },
  11306. /**
  11307. * Saves state of an object
  11308. * @param {Object} [options] Object with additional `stateProperties` array to include when saving state
  11309. * @return {fabric.Object} thisArg
  11310. */
  11311. saveState: function(options) {
  11312. this.stateProperties.forEach(function(prop) {
  11313. this.originalState[prop] = this.get(prop);
  11314. }, this);
  11315. if (options && options.stateProperties) {
  11316. options.stateProperties.forEach(function(prop) {
  11317. this.originalState[prop] = this.get(prop);
  11318. }, this);
  11319. }
  11320. return this;
  11321. },
  11322. /**
  11323. * Setups state of an object
  11324. * @return {fabric.Object} thisArg
  11325. */
  11326. setupState: function() {
  11327. this.originalState = { };
  11328. this.saveState();
  11329. return this;
  11330. }
  11331. });
  11332. (function(global) {
  11333. 'use strict';
  11334. var fabric = global.fabric || (global.fabric = { }),
  11335. extend = fabric.util.object.extend,
  11336. coordProps = { x1: 1, x2: 1, y1: 1, y2: 1 },
  11337. supportsLineDash = fabric.StaticCanvas.supports('setLineDash');
  11338. if (fabric.Line) {
  11339. fabric.warn('fabric.Line is already defined');
  11340. return;
  11341. }
  11342. /**
  11343. * Line class
  11344. * @class fabric.Line
  11345. * @extends fabric.Object
  11346. * @see {@link fabric.Line#initialize} for constructor definition
  11347. */
  11348. fabric.Line = fabric.util.createClass(fabric.Object, /** @lends fabric.Line.prototype */ {
  11349. /**
  11350. * Type of an object
  11351. * @type String
  11352. * @default
  11353. */
  11354. type: 'line',
  11355. /**
  11356. * x value or first line edge
  11357. * @type Number
  11358. * @default
  11359. */
  11360. x1: 0,
  11361. /**
  11362. * y value or first line edge
  11363. * @type Number
  11364. * @default
  11365. */
  11366. y1: 0,
  11367. /**
  11368. * x value or second line edge
  11369. * @type Number
  11370. * @default
  11371. */
  11372. x2: 0,
  11373. /**
  11374. * y value or second line edge
  11375. * @type Number
  11376. * @default
  11377. */
  11378. y2: 0,
  11379. /**
  11380. * Constructor
  11381. * @param {Array} [points] Array of points
  11382. * @param {Object} [options] Options object
  11383. * @return {fabric.Line} thisArg
  11384. */
  11385. initialize: function(points, options) {
  11386. options = options || { };
  11387. if (!points) {
  11388. points = [0, 0, 0, 0];
  11389. }
  11390. this.callSuper('initialize', options);
  11391. this.set('x1', points[0]);
  11392. this.set('y1', points[1]);
  11393. this.set('x2', points[2]);
  11394. this.set('y2', points[3]);
  11395. this._setWidthHeight(options);
  11396. },
  11397. /**
  11398. * @private
  11399. * @param {Object} [options] Options
  11400. */
  11401. _setWidthHeight: function(options) {
  11402. options || (options = { });
  11403. this.width = Math.abs(this.x2 - this.x1) || 1;
  11404. this.height = Math.abs(this.y2 - this.y1) || 1;
  11405. this.left = 'left' in options
  11406. ? options.left
  11407. : this._getLeftToOriginX();
  11408. this.top = 'top' in options
  11409. ? options.top
  11410. : this._getTopToOriginY();
  11411. },
  11412. /**
  11413. * @private
  11414. * @param {String} key
  11415. * @param {Any} value
  11416. */
  11417. _set: function(key, value) {
  11418. this[key] = value;
  11419. if (typeof coordProps[key] !== 'undefined') {
  11420. this._setWidthHeight();
  11421. }
  11422. return this;
  11423. },
  11424. /**
  11425. * @private
  11426. * @return {Number} leftToOriginX Distance from left edge of canvas to originX of Line.
  11427. */
  11428. _getLeftToOriginX: makeEdgeToOriginGetter(
  11429. { // property names
  11430. origin: 'originX',
  11431. axis1: 'x1',
  11432. axis2: 'x2',
  11433. dimension: 'width'
  11434. },
  11435. { // possible values of origin
  11436. nearest: 'left',
  11437. center: 'center',
  11438. farthest: 'right'
  11439. }
  11440. ),
  11441. /**
  11442. * @private
  11443. * @return {Number} topToOriginY Distance from top edge of canvas to originY of Line.
  11444. */
  11445. _getTopToOriginY: makeEdgeToOriginGetter(
  11446. { // property names
  11447. origin: 'originY',
  11448. axis1: 'y1',
  11449. axis2: 'y2',
  11450. dimension: 'height'
  11451. },
  11452. { // possible values of origin
  11453. nearest: 'top',
  11454. center: 'center',
  11455. farthest: 'bottom'
  11456. }
  11457. ),
  11458. /**
  11459. * @private
  11460. * @param {CanvasRenderingContext2D} ctx Context to render on
  11461. */
  11462. _render: function(ctx, noTransform) {
  11463. ctx.beginPath();
  11464. if (noTransform) {
  11465. // Line coords are distances from left-top of canvas to origin of line.
  11466. //
  11467. // To render line in a path-group, we need to translate them to
  11468. // distances from center of path-group to center of line.
  11469. var cp = this.getCenterPoint();
  11470. ctx.translate(
  11471. cp.x,
  11472. cp.y
  11473. );
  11474. }
  11475. if (!this.strokeDashArray || this.strokeDashArray && supportsLineDash) {
  11476. // move from center (of virtual box) to its left/top corner
  11477. // we can't assume x1, y1 is top left and x2, y2 is bottom right
  11478. var xMult = this.x1 <= this.x2 ? -1 : 1,
  11479. yMult = this.y1 <= this.y2 ? -1 : 1;
  11480. ctx.moveTo(
  11481. this.width === 1 ? 0 : (xMult * this.width / 2),
  11482. this.height === 1 ? 0 : (yMult * this.height / 2));
  11483. ctx.lineTo(
  11484. this.width === 1 ? 0 : (xMult * -1 * this.width / 2),
  11485. this.height === 1 ? 0 : (yMult * -1 * this.height / 2));
  11486. }
  11487. ctx.lineWidth = this.strokeWidth;
  11488. // TODO: test this
  11489. // make sure setting "fill" changes color of a line
  11490. // (by copying fillStyle to strokeStyle, since line is stroked, not filled)
  11491. var origStrokeStyle = ctx.strokeStyle;
  11492. ctx.strokeStyle = this.stroke || ctx.fillStyle;
  11493. this.stroke && this._renderStroke(ctx);
  11494. ctx.strokeStyle = origStrokeStyle;
  11495. },
  11496. /**
  11497. * @private
  11498. * @param {CanvasRenderingContext2D} ctx Context to render on
  11499. */
  11500. _renderDashedStroke: function(ctx) {
  11501. var
  11502. xMult = this.x1 <= this.x2 ? -1 : 1,
  11503. yMult = this.y1 <= this.y2 ? -1 : 1,
  11504. x = this.width === 1 ? 0 : xMult * this.width / 2,
  11505. y = this.height === 1 ? 0 : yMult * this.height / 2;
  11506. ctx.beginPath();
  11507. fabric.util.drawDashedLine(ctx, x, y, -x, -y, this.strokeDashArray);
  11508. ctx.closePath();
  11509. },
  11510. /**
  11511. * Returns object representation of an instance
  11512. * @methd toObject
  11513. * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
  11514. * @return {Object} object representation of an instance
  11515. */
  11516. toObject: function(propertiesToInclude) {
  11517. return extend(this.callSuper('toObject', propertiesToInclude), {
  11518. x1: this.get('x1'),
  11519. y1: this.get('y1'),
  11520. x2: this.get('x2'),
  11521. y2: this.get('y2')
  11522. });
  11523. },
  11524. /* _TO_SVG_START_ */
  11525. /**
  11526. * Returns SVG representation of an instance
  11527. * @param {Function} [reviver] Method for further parsing of svg representation.
  11528. * @return {String} svg representation of an instance
  11529. */
  11530. toSVG: function(reviver) {
  11531. var markup = this._createBaseSVGMarkup(), addTranslate = '';
  11532. if (!this.group) {
  11533. var x = - this.width / 2 - (this.x1 > this.x2 ? this.x2 : this.x1),
  11534. y = - this.height / 2 - (this.y1 > this.y2 ? this.y2 : this.y1);
  11535. addTranslate = 'translate(' + x + ', ' + y + ') ';
  11536. }
  11537. markup.push(
  11538. '<line ',
  11539. 'x1="', this.x1,
  11540. '" y1="', this.y1,
  11541. '" x2="', this.x2,
  11542. '" y2="', this.y2,
  11543. '" style="', this.getSvgStyles(),
  11544. '" transform="', this.getSvgTransform(), addTranslate,
  11545. this.getSvgTransformMatrix(),
  11546. '"/>\n'
  11547. );
  11548. return reviver ? reviver(markup.join('')) : markup.join('');
  11549. },
  11550. /* _TO_SVG_END_ */
  11551. /**
  11552. * Returns complexity of an instance
  11553. * @return {Number} complexity
  11554. */
  11555. complexity: function() {
  11556. return 1;
  11557. }
  11558. });
  11559. /* _FROM_SVG_START_ */
  11560. /**
  11561. * List of attribute names to account for when parsing SVG element (used by {@link fabric.Line.fromElement})
  11562. * @static
  11563. * @memberOf fabric.Line
  11564. * @see http://www.w3.org/TR/SVG/shapes.html#LineElement
  11565. */
  11566. fabric.Line.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat('x1 y1 x2 y2'.split(' '));
  11567. /**
  11568. * Returns fabric.Line instance from an SVG element
  11569. * @static
  11570. * @memberOf fabric.Line
  11571. * @param {SVGElement} element Element to parse
  11572. * @param {Object} [options] Options object
  11573. * @return {fabric.Line} instance of fabric.Line
  11574. */
  11575. fabric.Line.fromElement = function(element, options) {
  11576. var parsedAttributes = fabric.parseAttributes(element, fabric.Line.ATTRIBUTE_NAMES),
  11577. points = [
  11578. parsedAttributes.x1 || 0,
  11579. parsedAttributes.y1 || 0,
  11580. parsedAttributes.x2 || 0,
  11581. parsedAttributes.y2 || 0
  11582. ];
  11583. return new fabric.Line(points, extend(parsedAttributes, options));
  11584. };
  11585. /* _FROM_SVG_END_ */
  11586. /**
  11587. * Returns fabric.Line instance from an object representation
  11588. * @static
  11589. * @memberOf fabric.Line
  11590. * @param {Object} object Object to create an instance from
  11591. * @return {fabric.Line} instance of fabric.Line
  11592. */
  11593. fabric.Line.fromObject = function(object) {
  11594. var points = [object.x1, object.y1, object.x2, object.y2];
  11595. return new fabric.Line(points, object);
  11596. };
  11597. /**
  11598. * Produces a function that calculates distance from canvas edge to Line origin.
  11599. */
  11600. function makeEdgeToOriginGetter(propertyNames, originValues) {
  11601. var origin = propertyNames.origin,
  11602. axis1 = propertyNames.axis1,
  11603. axis2 = propertyNames.axis2,
  11604. dimension = propertyNames.dimension,
  11605. nearest = originValues.nearest,
  11606. center = originValues.center,
  11607. farthest = originValues.farthest;
  11608. return function() {
  11609. switch (this.get(origin)) {
  11610. case nearest:
  11611. return Math.min(this.get(axis1), this.get(axis2));
  11612. case center:
  11613. return Math.min(this.get(axis1), this.get(axis2)) + (0.5 * this.get(dimension));
  11614. case farthest:
  11615. return Math.max(this.get(axis1), this.get(axis2));
  11616. }
  11617. };
  11618. }
  11619. })(typeof exports !== 'undefined' ? exports : this);
  11620. (function(global) {
  11621. 'use strict';
  11622. var fabric = global.fabric || (global.fabric = { }),
  11623. piBy2 = Math.PI * 2,
  11624. extend = fabric.util.object.extend;
  11625. if (fabric.Circle) {
  11626. fabric.warn('fabric.Circle is already defined.');
  11627. return;
  11628. }
  11629. /**
  11630. * Circle class
  11631. * @class fabric.Circle
  11632. * @extends fabric.Object
  11633. * @see {@link fabric.Circle#initialize} for constructor definition
  11634. */
  11635. fabric.Circle = fabric.util.createClass(fabric.Object, /** @lends fabric.Circle.prototype */ {
  11636. /**
  11637. * Type of an object
  11638. * @type String
  11639. * @default
  11640. */
  11641. type: 'circle',
  11642. /**
  11643. * Radius of this circle
  11644. * @type Number
  11645. * @default
  11646. */
  11647. radius: 0,
  11648. /**
  11649. * Constructor
  11650. * @param {Object} [options] Options object
  11651. * @return {fabric.Circle} thisArg
  11652. */
  11653. initialize: function(options) {
  11654. options = options || { };
  11655. this.callSuper('initialize', options);
  11656. this.set('radius', options.radius || 0);
  11657. },
  11658. /**
  11659. * @private
  11660. * @param {String} key
  11661. * @param {Any} value
  11662. * @return {fabric.Circle} thisArg
  11663. */
  11664. _set: function(key, value) {
  11665. this.callSuper('_set', key, value);
  11666. if (key === 'radius') {
  11667. this.setRadius(value);
  11668. }
  11669. return this;
  11670. },
  11671. /**
  11672. * Returns object representation of an instance
  11673. * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
  11674. * @return {Object} object representation of an instance
  11675. */
  11676. toObject: function(propertiesToInclude) {
  11677. return extend(this.callSuper('toObject', propertiesToInclude), {
  11678. radius: this.get('radius')
  11679. });
  11680. },
  11681. /* _TO_SVG_START_ */
  11682. /**
  11683. * Returns svg representation of an instance
  11684. * @param {Function} [reviver] Method for further parsing of svg representation.
  11685. * @return {String} svg representation of an instance
  11686. */
  11687. toSVG: function(reviver) {
  11688. var markup = this._createBaseSVGMarkup(), x = 0, y = 0;
  11689. if (this.group) {
  11690. x = this.left + this.radius;
  11691. y = this.top + this.radius;
  11692. }
  11693. markup.push(
  11694. '<circle ',
  11695. 'cx="' + x + '" cy="' + y + '" ',
  11696. 'r="', this.radius,
  11697. '" style="', this.getSvgStyles(),
  11698. '" transform="', this.getSvgTransform(),
  11699. ' ', this.getSvgTransformMatrix(),
  11700. '"/>\n'
  11701. );
  11702. return reviver ? reviver(markup.join('')) : markup.join('');
  11703. },
  11704. /* _TO_SVG_END_ */
  11705. /**
  11706. * @private
  11707. * @param {CanvasRenderingContext2D} ctx context to render on
  11708. * @param {Boolean} [noTransform] When true, context is not transformed
  11709. */
  11710. _render: function(ctx, noTransform) {
  11711. ctx.beginPath();
  11712. ctx.arc(noTransform ? this.left + this.radius : 0, noTransform ? this.top + this.radius : 0, this.radius, 0, piBy2, false);
  11713. this._renderFill(ctx);
  11714. this._renderStroke(ctx);
  11715. },
  11716. /**
  11717. * Returns horizontal radius of an object (according to how an object is scaled)
  11718. * @return {Number}
  11719. */
  11720. getRadiusX: function() {
  11721. return this.get('radius') * this.get('scaleX');
  11722. },
  11723. /**
  11724. * Returns vertical radius of an object (according to how an object is scaled)
  11725. * @return {Number}
  11726. */
  11727. getRadiusY: function() {
  11728. return this.get('radius') * this.get('scaleY');
  11729. },
  11730. /**
  11731. * Sets radius of an object (and updates width accordingly)
  11732. * @return {Number}
  11733. */
  11734. setRadius: function(value) {
  11735. this.radius = value;
  11736. this.set('width', value * 2).set('height', value * 2);
  11737. },
  11738. /**
  11739. * Returns complexity of an instance
  11740. * @return {Number} complexity of this instance
  11741. */
  11742. complexity: function() {
  11743. return 1;
  11744. }
  11745. });
  11746. /* _FROM_SVG_START_ */
  11747. /**
  11748. * List of attribute names to account for when parsing SVG element (used by {@link fabric.Circle.fromElement})
  11749. * @static
  11750. * @memberOf fabric.Circle
  11751. * @see: http://www.w3.org/TR/SVG/shapes.html#CircleElement
  11752. */
  11753. fabric.Circle.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat('cx cy r'.split(' '));
  11754. /**
  11755. * Returns {@link fabric.Circle} instance from an SVG element
  11756. * @static
  11757. * @memberOf fabric.Circle
  11758. * @param {SVGElement} element Element to parse
  11759. * @param {Object} [options] Options object
  11760. * @throws {Error} If value of `r` attribute is missing or invalid
  11761. * @return {fabric.Circle} Instance of fabric.Circle
  11762. */
  11763. fabric.Circle.fromElement = function(element, options) {
  11764. options || (options = { });
  11765. var parsedAttributes = fabric.parseAttributes(element, fabric.Circle.ATTRIBUTE_NAMES);
  11766. if (!isValidRadius(parsedAttributes)) {
  11767. throw new Error('value of `r` attribute is required and can not be negative');
  11768. }
  11769. parsedAttributes.left = parsedAttributes.left || 0;
  11770. parsedAttributes.top = parsedAttributes.top || 0;
  11771. var obj = new fabric.Circle(extend(parsedAttributes, options));
  11772. obj.left -= obj.radius;
  11773. obj.top -= obj.radius;
  11774. return obj;
  11775. };
  11776. /**
  11777. * @private
  11778. */
  11779. function isValidRadius(attributes) {
  11780. return (('radius' in attributes) && (attributes.radius > 0));
  11781. }
  11782. /* _FROM_SVG_END_ */
  11783. /**
  11784. * Returns {@link fabric.Circle} instance from an object representation
  11785. * @static
  11786. * @memberOf fabric.Circle
  11787. * @param {Object} object Object to create an instance from
  11788. * @return {Object} Instance of fabric.Circle
  11789. */
  11790. fabric.Circle.fromObject = function(object) {
  11791. return new fabric.Circle(object);
  11792. };
  11793. })(typeof exports !== 'undefined' ? exports : this);
  11794. (function(global) {
  11795. 'use strict';
  11796. var fabric = global.fabric || (global.fabric = { });
  11797. if (fabric.Triangle) {
  11798. fabric.warn('fabric.Triangle is already defined');
  11799. return;
  11800. }
  11801. /**
  11802. * Triangle class
  11803. * @class fabric.Triangle
  11804. * @extends fabric.Object
  11805. * @return {fabric.Triangle} thisArg
  11806. * @see {@link fabric.Triangle#initialize} for constructor definition
  11807. */
  11808. fabric.Triangle = fabric.util.createClass(fabric.Object, /** @lends fabric.Triangle.prototype */ {
  11809. /**
  11810. * Type of an object
  11811. * @type String
  11812. * @default
  11813. */
  11814. type: 'triangle',
  11815. /**
  11816. * Constructor
  11817. * @param {Object} [options] Options object
  11818. * @return {Object} thisArg
  11819. */
  11820. initialize: function(options) {
  11821. options = options || { };
  11822. this.callSuper('initialize', options);
  11823. this.set('width', options.width || 100)
  11824. .set('height', options.height || 100);
  11825. },
  11826. /**
  11827. * @private
  11828. * @param {CanvasRenderingContext2D} ctx Context to render on
  11829. */
  11830. _render: function(ctx) {
  11831. var widthBy2 = this.width / 2,
  11832. heightBy2 = this.height / 2;
  11833. ctx.beginPath();
  11834. ctx.moveTo(-widthBy2, heightBy2);
  11835. ctx.lineTo(0, -heightBy2);
  11836. ctx.lineTo(widthBy2, heightBy2);
  11837. ctx.closePath();
  11838. this._renderFill(ctx);
  11839. this._renderStroke(ctx);
  11840. },
  11841. /**
  11842. * @private
  11843. * @param {CanvasRenderingContext2D} ctx Context to render on
  11844. */
  11845. _renderDashedStroke: function(ctx) {
  11846. var widthBy2 = this.width / 2,
  11847. heightBy2 = this.height / 2;
  11848. ctx.beginPath();
  11849. fabric.util.drawDashedLine(ctx, -widthBy2, heightBy2, 0, -heightBy2, this.strokeDashArray);
  11850. fabric.util.drawDashedLine(ctx, 0, -heightBy2, widthBy2, heightBy2, this.strokeDashArray);
  11851. fabric.util.drawDashedLine(ctx, widthBy2, heightBy2, -widthBy2, heightBy2, this.strokeDashArray);
  11852. ctx.closePath();
  11853. },
  11854. /* _TO_SVG_START_ */
  11855. /**
  11856. * Returns SVG representation of an instance
  11857. * @param {Function} [reviver] Method for further parsing of svg representation.
  11858. * @return {String} svg representation of an instance
  11859. */
  11860. toSVG: function(reviver) {
  11861. var markup = this._createBaseSVGMarkup(),
  11862. widthBy2 = this.width / 2,
  11863. heightBy2 = this.height / 2,
  11864. points = [
  11865. -widthBy2 + ' ' + heightBy2,
  11866. '0 ' + -heightBy2,
  11867. widthBy2 + ' ' + heightBy2
  11868. ]
  11869. .join(',');
  11870. markup.push(
  11871. '<polygon ',
  11872. 'points="', points,
  11873. '" style="', this.getSvgStyles(),
  11874. '" transform="', this.getSvgTransform(),
  11875. '"/>'
  11876. );
  11877. return reviver ? reviver(markup.join('')) : markup.join('');
  11878. },
  11879. /* _TO_SVG_END_ */
  11880. /**
  11881. * Returns complexity of an instance
  11882. * @return {Number} complexity of this instance
  11883. */
  11884. complexity: function() {
  11885. return 1;
  11886. }
  11887. });
  11888. /**
  11889. * Returns fabric.Triangle instance from an object representation
  11890. * @static
  11891. * @memberOf fabric.Triangle
  11892. * @param {Object} object Object to create an instance from
  11893. * @return {Object} instance of Canvas.Triangle
  11894. */
  11895. fabric.Triangle.fromObject = function(object) {
  11896. return new fabric.Triangle(object);
  11897. };
  11898. })(typeof exports !== 'undefined' ? exports : this);
  11899. (function(global){
  11900. 'use strict';
  11901. var fabric = global.fabric || (global.fabric = { }),
  11902. piBy2 = Math.PI * 2,
  11903. extend = fabric.util.object.extend;
  11904. if (fabric.Ellipse) {
  11905. fabric.warn('fabric.Ellipse is already defined.');
  11906. return;
  11907. }
  11908. /**
  11909. * Ellipse class
  11910. * @class fabric.Ellipse
  11911. * @extends fabric.Object
  11912. * @return {fabric.Ellipse} thisArg
  11913. * @see {@link fabric.Ellipse#initialize} for constructor definition
  11914. */
  11915. fabric.Ellipse = fabric.util.createClass(fabric.Object, /** @lends fabric.Ellipse.prototype */ {
  11916. /**
  11917. * Type of an object
  11918. * @type String
  11919. * @default
  11920. */
  11921. type: 'ellipse',
  11922. /**
  11923. * Horizontal radius
  11924. * @type Number
  11925. * @default
  11926. */
  11927. rx: 0,
  11928. /**
  11929. * Vertical radius
  11930. * @type Number
  11931. * @default
  11932. */
  11933. ry: 0,
  11934. /**
  11935. * Constructor
  11936. * @param {Object} [options] Options object
  11937. * @return {fabric.Ellipse} thisArg
  11938. */
  11939. initialize: function(options) {
  11940. options = options || { };
  11941. this.callSuper('initialize', options);
  11942. this.set('rx', options.rx || 0);
  11943. this.set('ry', options.ry || 0);
  11944. this.set('width', this.get('rx') * 2);
  11945. this.set('height', this.get('ry') * 2);
  11946. },
  11947. /**
  11948. * Returns object representation of an instance
  11949. * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
  11950. * @return {Object} object representation of an instance
  11951. */
  11952. toObject: function(propertiesToInclude) {
  11953. return extend(this.callSuper('toObject', propertiesToInclude), {
  11954. rx: this.get('rx'),
  11955. ry: this.get('ry')
  11956. });
  11957. },
  11958. /* _TO_SVG_START_ */
  11959. /**
  11960. * Returns svg representation of an instance
  11961. * @param {Function} [reviver] Method for further parsing of svg representation.
  11962. * @return {String} svg representation of an instance
  11963. */
  11964. toSVG: function(reviver) {
  11965. var markup = this._createBaseSVGMarkup(), x = 0, y = 0;
  11966. if (this.group) {
  11967. x = this.left + this.rx;
  11968. y = this.top + this.ry;
  11969. }
  11970. markup.push(
  11971. '<ellipse ',
  11972. 'cx="', x, '" cy="', y, '" ',
  11973. 'rx="', this.rx,
  11974. '" ry="', this.ry,
  11975. '" style="', this.getSvgStyles(),
  11976. '" transform="', this.getSvgTransform(),
  11977. this.getSvgTransformMatrix(),
  11978. '"/>\n'
  11979. );
  11980. return reviver ? reviver(markup.join('')) : markup.join('');
  11981. },
  11982. /* _TO_SVG_END_ */
  11983. /**
  11984. * @private
  11985. * @param {CanvasRenderingContext2D} ctx context to render on
  11986. * @param {Boolean} [noTransform] When true, context is not transformed
  11987. */
  11988. _render: function(ctx, noTransform) {
  11989. ctx.beginPath();
  11990. ctx.save();
  11991. ctx.transform(1, 0, 0, this.ry/this.rx, 0, 0);
  11992. ctx.arc(noTransform ? this.left + this.rx : 0, noTransform ? (this.top + this.ry) * this.rx/this.ry : 0, this.rx, 0, piBy2, false);
  11993. ctx.restore();
  11994. this._renderFill(ctx);
  11995. this._renderStroke(ctx);
  11996. },
  11997. /**
  11998. * Returns complexity of an instance
  11999. * @return {Number} complexity
  12000. */
  12001. complexity: function() {
  12002. return 1;
  12003. }
  12004. });
  12005. /* _FROM_SVG_START_ */
  12006. /**
  12007. * List of attribute names to account for when parsing SVG element (used by {@link fabric.Ellipse.fromElement})
  12008. * @static
  12009. * @memberOf fabric.Ellipse
  12010. * @see http://www.w3.org/TR/SVG/shapes.html#EllipseElement
  12011. */
  12012. fabric.Ellipse.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat('cx cy rx ry'.split(' '));
  12013. /**
  12014. * Returns {@link fabric.Ellipse} instance from an SVG element
  12015. * @static
  12016. * @memberOf fabric.Ellipse
  12017. * @param {SVGElement} element Element to parse
  12018. * @param {Object} [options] Options object
  12019. * @return {fabric.Ellipse}
  12020. */
  12021. fabric.Ellipse.fromElement = function(element, options) {
  12022. options || (options = { });
  12023. var parsedAttributes = fabric.parseAttributes(element, fabric.Ellipse.ATTRIBUTE_NAMES);
  12024. parsedAttributes.left = parsedAttributes.left || 0;
  12025. parsedAttributes.top = parsedAttributes.top || 0;
  12026. var ellipse = new fabric.Ellipse(extend(parsedAttributes, options));
  12027. ellipse.top -= ellipse.ry;
  12028. ellipse.left -= ellipse.rx;
  12029. return ellipse;
  12030. };
  12031. /* _FROM_SVG_END_ */
  12032. /**
  12033. * Returns {@link fabric.Ellipse} instance from an object representation
  12034. * @static
  12035. * @memberOf fabric.Ellipse
  12036. * @param {Object} object Object to create an instance from
  12037. * @return {fabric.Ellipse}
  12038. */
  12039. fabric.Ellipse.fromObject = function(object) {
  12040. return new fabric.Ellipse(object);
  12041. };
  12042. })(typeof exports !== 'undefined' ? exports : this);
  12043. (function(global) {
  12044. 'use strict';
  12045. var fabric = global.fabric || (global.fabric = { }),
  12046. extend = fabric.util.object.extend;
  12047. if (fabric.Rect) {
  12048. console.warn('fabric.Rect is already defined');
  12049. return;
  12050. }
  12051. var stateProperties = fabric.Object.prototype.stateProperties.concat();
  12052. stateProperties.push('rx', 'ry', 'x', 'y');
  12053. /**
  12054. * Rectangle class
  12055. * @class fabric.Rect
  12056. * @extends fabric.Object
  12057. * @return {fabric.Rect} thisArg
  12058. * @see {@link fabric.Rect#initialize} for constructor definition
  12059. */
  12060. fabric.Rect = fabric.util.createClass(fabric.Object, /** @lends fabric.Rect.prototype */ {
  12061. /**
  12062. * List of properties to consider when checking if state of an object is changed ({@link fabric.Object#hasStateChanged})
  12063. * as well as for history (undo/redo) purposes
  12064. * @type Array
  12065. */
  12066. stateProperties: stateProperties,
  12067. /**
  12068. * Type of an object
  12069. * @type String
  12070. * @default
  12071. */
  12072. type: 'rect',
  12073. /**
  12074. * Horizontal border radius
  12075. * @type Number
  12076. * @default
  12077. */
  12078. rx: 0,
  12079. /**
  12080. * Vertical border radius
  12081. * @type Number
  12082. * @default
  12083. */
  12084. ry: 0,
  12085. /**
  12086. * Used to specify dash pattern for stroke on this object
  12087. * @type Array
  12088. */
  12089. strokeDashArray: null,
  12090. /**
  12091. * Constructor
  12092. * @param {Object} [options] Options object
  12093. * @return {Object} thisArg
  12094. */
  12095. initialize: function(options) {
  12096. options = options || { };
  12097. this.callSuper('initialize', options);
  12098. this._initRxRy();
  12099. },
  12100. /**
  12101. * Initializes rx/ry attributes
  12102. * @private
  12103. */
  12104. _initRxRy: function() {
  12105. if (this.rx && !this.ry) {
  12106. this.ry = this.rx;
  12107. }
  12108. else if (this.ry && !this.rx) {
  12109. this.rx = this.ry;
  12110. }
  12111. },
  12112. /**
  12113. * @private
  12114. * @param {CanvasRenderingContext2D} ctx Context to render on
  12115. */
  12116. _render: function(ctx, noTransform) {
  12117. // optimize 1x1 case (used in spray brush)
  12118. if (this.width === 1 && this.height === 1) {
  12119. ctx.fillRect(0, 0, 1, 1);
  12120. return;
  12121. }
  12122. var rx = this.rx ? Math.min(this.rx, this.width / 2) : 0,
  12123. ry = this.ry ? Math.min(this.ry, this.height / 2) : 0,
  12124. w = this.width,
  12125. h = this.height,
  12126. x = noTransform ? this.left : -this.width / 2,
  12127. y = noTransform ? this.top : -this.height / 2,
  12128. isRounded = rx !== 0 || ry !== 0,
  12129. k = 1 - 0.5522847498 /* "magic number" for bezier approximations of arcs (http://itc.ktu.lt/itc354/Riskus354.pdf) */;
  12130. ctx.beginPath();
  12131. ctx.moveTo(x + rx, y);
  12132. ctx.lineTo(x + w - rx, y);
  12133. isRounded && ctx.bezierCurveTo(x + w - k * rx, y, x + w, y + k * ry, x + w, y + ry);
  12134. ctx.lineTo(x + w, y + h - ry);
  12135. isRounded && ctx.bezierCurveTo(x + w, y + h - k * ry, x + w - k * rx, y + h, x + w - rx, y + h);
  12136. ctx.lineTo(x + rx, y + h);
  12137. isRounded && ctx.bezierCurveTo(x + k * rx, y + h, x, y + h - k * ry, x, y + h - ry);
  12138. ctx.lineTo(x, y + ry);
  12139. isRounded && ctx.bezierCurveTo(x, y + k * ry, x + k * rx, y, x + rx, y);
  12140. ctx.closePath();
  12141. this._renderFill(ctx);
  12142. this._renderStroke(ctx);
  12143. },
  12144. /**
  12145. * @private
  12146. * @param {CanvasRenderingContext2D} ctx Context to render on
  12147. */
  12148. _renderDashedStroke: function(ctx) {
  12149. var x = -this.width / 2,
  12150. y = -this.height / 2,
  12151. w = this.width,
  12152. h = this.height;
  12153. ctx.beginPath();
  12154. fabric.util.drawDashedLine(ctx, x, y, x + w, y, this.strokeDashArray);
  12155. fabric.util.drawDashedLine(ctx, x + w, y, x + w, y + h, this.strokeDashArray);
  12156. fabric.util.drawDashedLine(ctx, x + w, y + h, x, y + h, this.strokeDashArray);
  12157. fabric.util.drawDashedLine(ctx, x, y + h, x, y, this.strokeDashArray);
  12158. ctx.closePath();
  12159. },
  12160. /**
  12161. * Returns object representation of an instance
  12162. * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
  12163. * @return {Object} object representation of an instance
  12164. */
  12165. toObject: function(propertiesToInclude) {
  12166. var object = extend(this.callSuper('toObject', propertiesToInclude), {
  12167. rx: this.get('rx') || 0,
  12168. ry: this.get('ry') || 0
  12169. });
  12170. if (!this.includeDefaultValues) {
  12171. this._removeDefaultValues(object);
  12172. }
  12173. return object;
  12174. },
  12175. /* _TO_SVG_START_ */
  12176. /**
  12177. * Returns svg representation of an instance
  12178. * @param {Function} [reviver] Method for further parsing of svg representation.
  12179. * @return {String} svg representation of an instance
  12180. */
  12181. toSVG: function(reviver) {
  12182. var markup = this._createBaseSVGMarkup(), x = this.left, y = this.top;
  12183. if (!this.group) {
  12184. x = -this.width / 2;
  12185. y = -this.height / 2;
  12186. }
  12187. markup.push(
  12188. '<rect ',
  12189. 'x="', x, '" y="', y,
  12190. '" rx="', this.get('rx'), '" ry="', this.get('ry'),
  12191. '" width="', this.width, '" height="', this.height,
  12192. '" style="', this.getSvgStyles(),
  12193. '" transform="', this.getSvgTransform(),
  12194. this.getSvgTransformMatrix(),
  12195. '"/>\n');
  12196. return reviver ? reviver(markup.join('')) : markup.join('');
  12197. },
  12198. /* _TO_SVG_END_ */
  12199. /**
  12200. * Returns complexity of an instance
  12201. * @return {Number} complexity
  12202. */
  12203. complexity: function() {
  12204. return 1;
  12205. }
  12206. });
  12207. /* _FROM_SVG_START_ */
  12208. /**
  12209. * List of attribute names to account for when parsing SVG element (used by `fabric.Rect.fromElement`)
  12210. * @static
  12211. * @memberOf fabric.Rect
  12212. * @see: http://www.w3.org/TR/SVG/shapes.html#RectElement
  12213. */
  12214. fabric.Rect.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat('x y rx ry width height'.split(' '));
  12215. /**
  12216. * Returns {@link fabric.Rect} instance from an SVG element
  12217. * @static
  12218. * @memberOf fabric.Rect
  12219. * @param {SVGElement} element Element to parse
  12220. * @param {Object} [options] Options object
  12221. * @return {fabric.Rect} Instance of fabric.Rect
  12222. */
  12223. fabric.Rect.fromElement = function(element, options) {
  12224. if (!element) {
  12225. return null;
  12226. }
  12227. options = options || { };
  12228. var parsedAttributes = fabric.parseAttributes(element, fabric.Rect.ATTRIBUTE_NAMES);
  12229. parsedAttributes.left = parsedAttributes.left || 0;
  12230. parsedAttributes.top = parsedAttributes.top || 0;
  12231. return new fabric.Rect(extend((options ? fabric.util.object.clone(options) : { }), parsedAttributes));
  12232. };
  12233. /* _FROM_SVG_END_ */
  12234. /**
  12235. * Returns {@link fabric.Rect} instance from an object representation
  12236. * @static
  12237. * @memberOf fabric.Rect
  12238. * @param {Object} object Object to create an instance from
  12239. * @return {Object} instance of fabric.Rect
  12240. */
  12241. fabric.Rect.fromObject = function(object) {
  12242. return new fabric.Rect(object);
  12243. };
  12244. })(typeof exports !== 'undefined' ? exports : this);
  12245. (function(global) {
  12246. 'use strict';
  12247. var fabric = global.fabric || (global.fabric = { }),
  12248. toFixed = fabric.util.toFixed;
  12249. if (fabric.Polyline) {
  12250. fabric.warn('fabric.Polyline is already defined');
  12251. return;
  12252. }
  12253. /**
  12254. * Polyline class
  12255. * @class fabric.Polyline
  12256. * @extends fabric.Object
  12257. * @see {@link fabric.Polyline#initialize} for constructor definition
  12258. */
  12259. fabric.Polyline = fabric.util.createClass(fabric.Object, /** @lends fabric.Polyline.prototype */ {
  12260. /**
  12261. * Type of an object
  12262. * @type String
  12263. * @default
  12264. */
  12265. type: 'polyline',
  12266. /**
  12267. * Points array
  12268. * @type Array
  12269. * @default
  12270. */
  12271. points: null,
  12272. /**
  12273. * Constructor
  12274. * @param {Array} points Array of points (where each point is an object with x and y)
  12275. * @param {Object} [options] Options object
  12276. * @param {Boolean} [skipOffset] Whether points offsetting should be skipped
  12277. * @return {fabric.Polyline} thisArg
  12278. * @example
  12279. * var poly = new fabric.Polyline([
  12280. * { x: 10, y: 10 },
  12281. * { x: 50, y: 30 },
  12282. * { x: 40, y: 70 },
  12283. * { x: 60, y: 50 },
  12284. * { x: 100, y: 150 },
  12285. * { x: 40, y: 100 }
  12286. * ], {
  12287. * stroke: 'red',
  12288. * left: 100,
  12289. * top: 100
  12290. * });
  12291. */
  12292. initialize: function(points, options) {
  12293. options = options || { };
  12294. this.set('points', points);
  12295. this.callSuper('initialize', options);
  12296. this._calcDimensions();
  12297. },
  12298. /**
  12299. * @private
  12300. */
  12301. _calcDimensions: function() {
  12302. return fabric.Polygon.prototype._calcDimensions.call(this);
  12303. },
  12304. /**
  12305. * @private
  12306. */
  12307. _applyPointOffset: function() {
  12308. return fabric.Polygon.prototype._applyPointOffset.call(this);
  12309. },
  12310. /**
  12311. * Returns object representation of an instance
  12312. * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
  12313. * @return {Object} Object representation of an instance
  12314. */
  12315. toObject: function(propertiesToInclude) {
  12316. return fabric.Polygon.prototype.toObject.call(this, propertiesToInclude);
  12317. },
  12318. /* _TO_SVG_START_ */
  12319. /**
  12320. * Returns SVG representation of an instance
  12321. * @param {Function} [reviver] Method for further parsing of svg representation.
  12322. * @return {String} svg representation of an instance
  12323. */
  12324. toSVG: function(reviver) {
  12325. var points = [],
  12326. markup = this._createBaseSVGMarkup();
  12327. for (var i = 0, len = this.points.length; i < len; i++) {
  12328. points.push(toFixed(this.points[i].x, 2), ',', toFixed(this.points[i].y, 2), ' ');
  12329. }
  12330. markup.push(
  12331. '<polyline ',
  12332. 'points="', points.join(''),
  12333. '" style="', this.getSvgStyles(),
  12334. '" transform="', this.getSvgTransform(),
  12335. ' ', this.getSvgTransformMatrix(),
  12336. '"/>\n'
  12337. );
  12338. return reviver ? reviver(markup.join('')) : markup.join('');
  12339. },
  12340. /* _TO_SVG_END_ */
  12341. /**
  12342. * @private
  12343. * @param {CanvasRenderingContext2D} ctx Context to render on
  12344. */
  12345. _render: function(ctx) {
  12346. var point;
  12347. ctx.beginPath();
  12348. if (this._applyPointOffset) {
  12349. if (!(this.group && this.group.type === 'path-group')) {
  12350. this._applyPointOffset();
  12351. }
  12352. this._applyPointOffset = null;
  12353. }
  12354. ctx.moveTo(this.points[0].x, this.points[0].y);
  12355. for (var i = 0, len = this.points.length; i < len; i++) {
  12356. point = this.points[i];
  12357. ctx.lineTo(point.x, point.y);
  12358. }
  12359. this._renderFill(ctx);
  12360. this._renderStroke(ctx);
  12361. },
  12362. /**
  12363. * @private
  12364. * @param {CanvasRenderingContext2D} ctx Context to render on
  12365. */
  12366. _renderDashedStroke: function(ctx) {
  12367. var p1, p2;
  12368. ctx.beginPath();
  12369. for (var i = 0, len = this.points.length; i < len; i++) {
  12370. p1 = this.points[i];
  12371. p2 = this.points[i + 1] || p1;
  12372. fabric.util.drawDashedLine(ctx, p1.x, p1.y, p2.x, p2.y, this.strokeDashArray);
  12373. }
  12374. },
  12375. /**
  12376. * Returns complexity of an instance
  12377. * @return {Number} complexity of this instance
  12378. */
  12379. complexity: function() {
  12380. return this.get('points').length;
  12381. }
  12382. });
  12383. /* _FROM_SVG_START_ */
  12384. /**
  12385. * List of attribute names to account for when parsing SVG element (used by {@link fabric.Polyline.fromElement})
  12386. * @static
  12387. * @memberOf fabric.Polyline
  12388. * @see: http://www.w3.org/TR/SVG/shapes.html#PolylineElement
  12389. */
  12390. fabric.Polyline.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat();
  12391. /**
  12392. * Returns fabric.Polyline instance from an SVG element
  12393. * @static
  12394. * @memberOf fabric.Polyline
  12395. * @param {SVGElement} element Element to parse
  12396. * @param {Object} [options] Options object
  12397. * @return {fabric.Polyline} Instance of fabric.Polyline
  12398. */
  12399. fabric.Polyline.fromElement = function(element, options) {
  12400. if (!element) {
  12401. return null;
  12402. }
  12403. options || (options = { });
  12404. var points = fabric.parsePointsAttribute(element.getAttribute('points')),
  12405. parsedAttributes = fabric.parseAttributes(element, fabric.Polyline.ATTRIBUTE_NAMES);
  12406. if (points === null) {
  12407. return null;
  12408. }
  12409. return new fabric.Polyline(points, fabric.util.object.extend(parsedAttributes, options));
  12410. };
  12411. /* _FROM_SVG_END_ */
  12412. /**
  12413. * Returns fabric.Polyline instance from an object representation
  12414. * @static
  12415. * @memberOf fabric.Polyline
  12416. * @param {Object} object Object to create an instance from
  12417. * @return {fabric.Polyline} Instance of fabric.Polyline
  12418. */
  12419. fabric.Polyline.fromObject = function(object) {
  12420. var points = object.points;
  12421. return new fabric.Polyline(points, object, true);
  12422. };
  12423. })(typeof exports !== 'undefined' ? exports : this);
  12424. (function(global) {
  12425. 'use strict';
  12426. var fabric = global.fabric || (global.fabric = { }),
  12427. extend = fabric.util.object.extend,
  12428. min = fabric.util.array.min,
  12429. max = fabric.util.array.max,
  12430. toFixed = fabric.util.toFixed;
  12431. if (fabric.Polygon) {
  12432. fabric.warn('fabric.Polygon is already defined');
  12433. return;
  12434. }
  12435. /**
  12436. * Polygon class
  12437. * @class fabric.Polygon
  12438. * @extends fabric.Object
  12439. * @see {@link fabric.Polygon#initialize} for constructor definition
  12440. */
  12441. fabric.Polygon = fabric.util.createClass(fabric.Object, /** @lends fabric.Polygon.prototype */ {
  12442. /**
  12443. * Type of an object
  12444. * @type String
  12445. * @default
  12446. */
  12447. type: 'polygon',
  12448. /**
  12449. * Points array
  12450. * @type Array
  12451. * @default
  12452. */
  12453. points: null,
  12454. /**
  12455. * Constructor
  12456. * @param {Array} points Array of points
  12457. * @param {Object} [options] Options object
  12458. * @return {fabric.Polygon} thisArg
  12459. */
  12460. initialize: function(points, options) {
  12461. options = options || { };
  12462. this.points = points;
  12463. this.callSuper('initialize', options);
  12464. this._calcDimensions();
  12465. },
  12466. /**
  12467. * @private
  12468. */
  12469. _calcDimensions: function() {
  12470. var points = this.points,
  12471. minX = min(points, 'x'),
  12472. minY = min(points, 'y'),
  12473. maxX = max(points, 'x'),
  12474. maxY = max(points, 'y');
  12475. this.width = (maxX - minX) || 1;
  12476. this.height = (maxY - minY) || 1;
  12477. this.left = minX,
  12478. this.top = minY;
  12479. },
  12480. /**
  12481. * @private
  12482. */
  12483. _applyPointOffset: function() {
  12484. // change points to offset polygon into a bounding box
  12485. // executed one time
  12486. this.points.forEach(function(p) {
  12487. p.x -= (this.left + this.width / 2);
  12488. p.y -= (this.top + this.height / 2);
  12489. }, this);
  12490. },
  12491. /**
  12492. * Returns object representation of an instance
  12493. * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
  12494. * @return {Object} Object representation of an instance
  12495. */
  12496. toObject: function(propertiesToInclude) {
  12497. return extend(this.callSuper('toObject', propertiesToInclude), {
  12498. points: this.points.concat()
  12499. });
  12500. },
  12501. /* _TO_SVG_START_ */
  12502. /**
  12503. * Returns svg representation of an instance
  12504. * @param {Function} [reviver] Method for further parsing of svg representation.
  12505. * @return {String} svg representation of an instance
  12506. */
  12507. toSVG: function(reviver) {
  12508. var points = [],
  12509. markup = this._createBaseSVGMarkup();
  12510. for (var i = 0, len = this.points.length; i < len; i++) {
  12511. points.push(toFixed(this.points[i].x, 2), ',', toFixed(this.points[i].y, 2), ' ');
  12512. }
  12513. markup.push(
  12514. '<polygon ',
  12515. 'points="', points.join(''),
  12516. '" style="', this.getSvgStyles(),
  12517. '" transform="', this.getSvgTransform(),
  12518. ' ', this.getSvgTransformMatrix(),
  12519. '"/>\n'
  12520. );
  12521. return reviver ? reviver(markup.join('')) : markup.join('');
  12522. },
  12523. /* _TO_SVG_END_ */
  12524. /**
  12525. * @private
  12526. * @param {CanvasRenderingContext2D} ctx Context to render on
  12527. */
  12528. _render: function(ctx) {
  12529. var point;
  12530. ctx.beginPath();
  12531. if (this._applyPointOffset) {
  12532. if (!(this.group && this.group.type === 'path-group')) {
  12533. this._applyPointOffset();
  12534. }
  12535. this._applyPointOffset = null;
  12536. }
  12537. ctx.moveTo(this.points[0].x, this.points[0].y);
  12538. for (var i = 0, len = this.points.length; i < len; i++) {
  12539. point = this.points[i];
  12540. ctx.lineTo(point.x, point.y);
  12541. }
  12542. this._renderFill(ctx);
  12543. if (this.stroke || this.strokeDashArray) {
  12544. ctx.closePath();
  12545. this._renderStroke(ctx);
  12546. }
  12547. },
  12548. /**
  12549. * @private
  12550. * @param {CanvasRenderingContext2D} ctx Context to render on
  12551. */
  12552. _renderDashedStroke: function(ctx) {
  12553. var p1, p2;
  12554. ctx.beginPath();
  12555. for (var i = 0, len = this.points.length; i < len; i++) {
  12556. p1 = this.points[i];
  12557. p2 = this.points[i + 1] || this.points[0];
  12558. fabric.util.drawDashedLine(ctx, p1.x, p1.y, p2.x, p2.y, this.strokeDashArray);
  12559. }
  12560. ctx.closePath();
  12561. },
  12562. /**
  12563. * Returns complexity of an instance
  12564. * @return {Number} complexity of this instance
  12565. */
  12566. complexity: function() {
  12567. return this.points.length;
  12568. }
  12569. });
  12570. /* _FROM_SVG_START_ */
  12571. /**
  12572. * List of attribute names to account for when parsing SVG element (used by `fabric.Polygon.fromElement`)
  12573. * @static
  12574. * @memberOf fabric.Polygon
  12575. * @see: http://www.w3.org/TR/SVG/shapes.html#PolygonElement
  12576. */
  12577. fabric.Polygon.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat();
  12578. /**
  12579. * Returns {@link fabric.Polygon} instance from an SVG element
  12580. * @static
  12581. * @memberOf fabric.Polygon
  12582. * @param {SVGElement} element Element to parse
  12583. * @param {Object} [options] Options object
  12584. * @return {fabric.Polygon} Instance of fabric.Polygon
  12585. */
  12586. fabric.Polygon.fromElement = function(element, options) {
  12587. if (!element) {
  12588. return null;
  12589. }
  12590. options || (options = { });
  12591. var points = fabric.parsePointsAttribute(element.getAttribute('points')),
  12592. parsedAttributes = fabric.parseAttributes(element, fabric.Polygon.ATTRIBUTE_NAMES);
  12593. if (points === null) {
  12594. return null;
  12595. }
  12596. return new fabric.Polygon(points, extend(parsedAttributes, options));
  12597. };
  12598. /* _FROM_SVG_END_ */
  12599. /**
  12600. * Returns fabric.Polygon instance from an object representation
  12601. * @static
  12602. * @memberOf fabric.Polygon
  12603. * @param {Object} object Object to create an instance from
  12604. * @return {fabric.Polygon} Instance of fabric.Polygon
  12605. */
  12606. fabric.Polygon.fromObject = function(object) {
  12607. return new fabric.Polygon(object.points, object, true);
  12608. };
  12609. })(typeof exports !== 'undefined' ? exports : this);
  12610. (function(global) {
  12611. 'use strict';
  12612. var fabric = global.fabric || (global.fabric = { }),
  12613. min = fabric.util.array.min,
  12614. max = fabric.util.array.max,
  12615. extend = fabric.util.object.extend,
  12616. _toString = Object.prototype.toString,
  12617. drawArc = fabric.util.drawArc,
  12618. commandLengths = {
  12619. m: 2,
  12620. l: 2,
  12621. h: 1,
  12622. v: 1,
  12623. c: 6,
  12624. s: 4,
  12625. q: 4,
  12626. t: 2,
  12627. a: 7
  12628. },
  12629. repeatedCommands = {
  12630. m: 'l',
  12631. M: 'L'
  12632. };
  12633. if (fabric.Path) {
  12634. fabric.warn('fabric.Path is already defined');
  12635. return;
  12636. }
  12637. /**
  12638. * @private
  12639. */
  12640. function getX(item) {
  12641. if (item[0] === 'H') {
  12642. return item[1];
  12643. }
  12644. return item[item.length - 2];
  12645. }
  12646. /**
  12647. * @private
  12648. */
  12649. function getY(item) {
  12650. if (item[0] === 'V') {
  12651. return item[1];
  12652. }
  12653. return item[item.length - 1];
  12654. }
  12655. /**
  12656. * Path class
  12657. * @class fabric.Path
  12658. * @extends fabric.Object
  12659. * @tutorial {@link http://fabricjs.com/fabric-intro-part-1/#path_and_pathgroup}
  12660. * @see {@link fabric.Path#initialize} for constructor definition
  12661. */
  12662. fabric.Path = fabric.util.createClass(fabric.Object, /** @lends fabric.Path.prototype */ {
  12663. /**
  12664. * Type of an object
  12665. * @type String
  12666. * @default
  12667. */
  12668. type: 'path',
  12669. /**
  12670. * Array of path points
  12671. * @type Array
  12672. * @default
  12673. */
  12674. path: null,
  12675. /**
  12676. * Constructor
  12677. * @param {Array|String} path Path data (sequence of coordinates and corresponding "command" tokens)
  12678. * @param {Object} [options] Options object
  12679. * @return {fabric.Path} thisArg
  12680. */
  12681. initialize: function(path, options) {
  12682. options = options || { };
  12683. this.setOptions(options);
  12684. if (!path) {
  12685. throw new Error('`path` argument is required');
  12686. }
  12687. var fromArray = _toString.call(path) === '[object Array]';
  12688. this.path = fromArray
  12689. ? path
  12690. // one of commands (m,M,l,L,q,Q,c,C,etc.) followed by non-command characters (i.e. command values)
  12691. : path.match && path.match(/[mzlhvcsqta][^mzlhvcsqta]*/gi);
  12692. if (!this.path) {
  12693. return;
  12694. }
  12695. if (!fromArray) {
  12696. this.path = this._parsePath();
  12697. }
  12698. this._initializePath(options);
  12699. if (options.sourcePath) {
  12700. this.setSourcePath(options.sourcePath);
  12701. }
  12702. },
  12703. /**
  12704. * @private
  12705. * @param {Object} [options] Options object
  12706. */
  12707. _initializePath: function (options) {
  12708. var isWidthSet = 'width' in options && options.width != null,
  12709. isHeightSet = 'height' in options && options.width != null,
  12710. isLeftSet = 'left' in options,
  12711. isTopSet = 'top' in options,
  12712. origLeft = isLeftSet ? this.left : 0,
  12713. origTop = isTopSet ? this.top : 0;
  12714. if (!isWidthSet || !isHeightSet) {
  12715. extend(this, this._parseDimensions());
  12716. if (isWidthSet) {
  12717. this.width = options.width;
  12718. }
  12719. if (isHeightSet) {
  12720. this.height = options.height;
  12721. }
  12722. }
  12723. else { //Set center location relative to given height/width if not specified
  12724. if (!isTopSet) {
  12725. this.top = this.height / 2;
  12726. }
  12727. if (!isLeftSet) {
  12728. this.left = this.width / 2;
  12729. }
  12730. }
  12731. this.pathOffset = this.pathOffset ||
  12732. // Save top-left coords as offset
  12733. this._calculatePathOffset(origLeft, origTop);
  12734. },
  12735. /**
  12736. * @private
  12737. * @param {Number} origLeft Original left position
  12738. * @param {Number} origTop Original top position
  12739. */
  12740. _calculatePathOffset: function (origLeft, origTop) {
  12741. return {
  12742. x: this.left - origLeft - (this.width / 2),
  12743. y: this.top - origTop - (this.height / 2)
  12744. };
  12745. },
  12746. /**
  12747. * @private
  12748. * @param {CanvasRenderingContext2D} ctx context to render path on
  12749. */
  12750. _render: function(ctx, noTransform) {
  12751. var current, // current instruction
  12752. previous = null,
  12753. subpathStartX = 0,
  12754. subpathStartY = 0,
  12755. x = 0, // current x
  12756. y = 0, // current y
  12757. controlX = 0, // current control point x
  12758. controlY = 0, // current control point y
  12759. tempX,
  12760. tempY,
  12761. tempControlX,
  12762. tempControlY,
  12763. l = -((this.width / 2) + this.pathOffset.x),
  12764. t = -((this.height / 2) + this.pathOffset.y);
  12765. if (noTransform) {
  12766. l += this.width / 2;
  12767. t += this.height / 2;
  12768. }
  12769. for (var i = 0, len = this.path.length; i < len; ++i) {
  12770. current = this.path[i];
  12771. switch (current[0]) { // first letter
  12772. case 'l': // lineto, relative
  12773. x += current[1];
  12774. y += current[2];
  12775. ctx.lineTo(x + l, y + t);
  12776. break;
  12777. case 'L': // lineto, absolute
  12778. x = current[1];
  12779. y = current[2];
  12780. ctx.lineTo(x + l, y + t);
  12781. break;
  12782. case 'h': // horizontal lineto, relative
  12783. x += current[1];
  12784. ctx.lineTo(x + l, y + t);
  12785. break;
  12786. case 'H': // horizontal lineto, absolute
  12787. x = current[1];
  12788. ctx.lineTo(x + l, y + t);
  12789. break;
  12790. case 'v': // vertical lineto, relative
  12791. y += current[1];
  12792. ctx.lineTo(x + l, y + t);
  12793. break;
  12794. case 'V': // verical lineto, absolute
  12795. y = current[1];
  12796. ctx.lineTo(x + l, y + t);
  12797. break;
  12798. case 'm': // moveTo, relative
  12799. x += current[1];
  12800. y += current[2];
  12801. subpathStartX = x;
  12802. subpathStartY = y;
  12803. ctx.moveTo(x + l, y + t);
  12804. break;
  12805. case 'M': // moveTo, absolute
  12806. x = current[1];
  12807. y = current[2];
  12808. subpathStartX = x;
  12809. subpathStartY = y;
  12810. ctx.moveTo(x + l, y + t);
  12811. break;
  12812. case 'c': // bezierCurveTo, relative
  12813. tempX = x + current[5];
  12814. tempY = y + current[6];
  12815. controlX = x + current[3];
  12816. controlY = y + current[4];
  12817. ctx.bezierCurveTo(
  12818. x + current[1] + l, // x1
  12819. y + current[2] + t, // y1
  12820. controlX + l, // x2
  12821. controlY + t, // y2
  12822. tempX + l,
  12823. tempY + t
  12824. );
  12825. x = tempX;
  12826. y = tempY;
  12827. break;
  12828. case 'C': // bezierCurveTo, absolute
  12829. x = current[5];
  12830. y = current[6];
  12831. controlX = current[3];
  12832. controlY = current[4];
  12833. ctx.bezierCurveTo(
  12834. current[1] + l,
  12835. current[2] + t,
  12836. controlX + l,
  12837. controlY + t,
  12838. x + l,
  12839. y + t
  12840. );
  12841. break;
  12842. case 's': // shorthand cubic bezierCurveTo, relative
  12843. // transform to absolute x,y
  12844. tempX = x + current[3];
  12845. tempY = y + current[4];
  12846. // calculate reflection of previous control points
  12847. controlX = controlX ? (2 * x - controlX) : x;
  12848. controlY = controlY ? (2 * y - controlY) : y;
  12849. ctx.bezierCurveTo(
  12850. controlX + l,
  12851. controlY + t,
  12852. x + current[1] + l,
  12853. y + current[2] + t,
  12854. tempX + l,
  12855. tempY + t
  12856. );
  12857. // set control point to 2nd one of this command
  12858. // "... the first control point is assumed to be
  12859. // the reflection of the second control point on
  12860. // the previous command relative to the current point."
  12861. controlX = x + current[1];
  12862. controlY = y + current[2];
  12863. x = tempX;
  12864. y = tempY;
  12865. break;
  12866. case 'S': // shorthand cubic bezierCurveTo, absolute
  12867. tempX = current[3];
  12868. tempY = current[4];
  12869. // calculate reflection of previous control points
  12870. controlX = 2 * x - controlX;
  12871. controlY = 2 * y - controlY;
  12872. ctx.bezierCurveTo(
  12873. controlX + l,
  12874. controlY + t,
  12875. current[1] + l,
  12876. current[2] + t,
  12877. tempX + l,
  12878. tempY + t
  12879. );
  12880. x = tempX;
  12881. y = tempY;
  12882. // set control point to 2nd one of this command
  12883. // "... the first control point is assumed to be
  12884. // the reflection of the second control point on
  12885. // the previous command relative to the current point."
  12886. controlX = current[1];
  12887. controlY = current[2];
  12888. break;
  12889. case 'q': // quadraticCurveTo, relative
  12890. // transform to absolute x,y
  12891. tempX = x + current[3];
  12892. tempY = y + current[4];
  12893. controlX = x + current[1];
  12894. controlY = y + current[2];
  12895. ctx.quadraticCurveTo(
  12896. controlX + l,
  12897. controlY + t,
  12898. tempX + l,
  12899. tempY + t
  12900. );
  12901. x = tempX;
  12902. y = tempY;
  12903. break;
  12904. case 'Q': // quadraticCurveTo, absolute
  12905. tempX = current[3];
  12906. tempY = current[4];
  12907. ctx.quadraticCurveTo(
  12908. current[1] + l,
  12909. current[2] + t,
  12910. tempX + l,
  12911. tempY + t
  12912. );
  12913. x = tempX;
  12914. y = tempY;
  12915. controlX = current[1];
  12916. controlY = current[2];
  12917. break;
  12918. case 't': // shorthand quadraticCurveTo, relative
  12919. // transform to absolute x,y
  12920. tempX = x + current[1];
  12921. tempY = y + current[2];
  12922. if (previous[0].match(/[QqTt]/) === null) {
  12923. // If there is no previous command or if the previous command was not a Q, q, T or t,
  12924. // assume the control point is coincident with the current point
  12925. controlX = x;
  12926. controlY = y;
  12927. }
  12928. else if (previous[0] === 't') {
  12929. // calculate reflection of previous control points for t
  12930. controlX = 2 * x - tempControlX;
  12931. controlY = 2 * y - tempControlY;
  12932. }
  12933. else if (previous[0] === 'q') {
  12934. // calculate reflection of previous control points for q
  12935. controlX = 2 * x - controlX;
  12936. controlY = 2 * y - controlY;
  12937. }
  12938. tempControlX = controlX;
  12939. tempControlY = controlY;
  12940. ctx.quadraticCurveTo(
  12941. controlX + l,
  12942. controlY + t,
  12943. tempX + l,
  12944. tempY + t
  12945. );
  12946. x = tempX;
  12947. y = tempY;
  12948. controlX = x + current[1];
  12949. controlY = y + current[2];
  12950. break;
  12951. case 'T':
  12952. tempX = current[1];
  12953. tempY = current[2];
  12954. // calculate reflection of previous control points
  12955. controlX = 2 * x - controlX;
  12956. controlY = 2 * y - controlY;
  12957. ctx.quadraticCurveTo(
  12958. controlX + l,
  12959. controlY + t,
  12960. tempX + l,
  12961. tempY + t
  12962. );
  12963. x = tempX;
  12964. y = tempY;
  12965. break;
  12966. case 'a':
  12967. // TODO: optimize this
  12968. drawArc(ctx, x + l, y + t, [
  12969. current[1],
  12970. current[2],
  12971. current[3],
  12972. current[4],
  12973. current[5],
  12974. current[6] + x + l,
  12975. current[7] + y + t
  12976. ]);
  12977. x += current[6];
  12978. y += current[7];
  12979. break;
  12980. case 'A':
  12981. // TODO: optimize this
  12982. drawArc(ctx, x + l, y + t, [
  12983. current[1],
  12984. current[2],
  12985. current[3],
  12986. current[4],
  12987. current[5],
  12988. current[6] + l,
  12989. current[7] + t
  12990. ]);
  12991. x = current[6];
  12992. y = current[7];
  12993. break;
  12994. case 'z':
  12995. case 'Z':
  12996. x = subpathStartX;
  12997. y = subpathStartY;
  12998. ctx.closePath();
  12999. break;
  13000. }
  13001. previous = current;
  13002. }
  13003. },
  13004. /**
  13005. * Renders path on a specified context
  13006. * @param {CanvasRenderingContext2D} ctx context to render path on
  13007. * @param {Boolean} [noTransform] When true, context is not transformed
  13008. */
  13009. render: function(ctx, noTransform) {
  13010. // do not render if object is not visible
  13011. if (!this.visible) {
  13012. return;
  13013. }
  13014. ctx.save();
  13015. if (noTransform) {
  13016. ctx.translate(-this.width/2, -this.height/2);
  13017. }
  13018. var m = this.transformMatrix;
  13019. if (m) {
  13020. ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
  13021. }
  13022. if (!noTransform) {
  13023. this.transform(ctx);
  13024. }
  13025. this._setStrokeStyles(ctx);
  13026. this._setFillStyles(ctx);
  13027. this._setShadow(ctx);
  13028. this.clipTo && fabric.util.clipContext(this, ctx);
  13029. ctx.beginPath();
  13030. ctx.globalAlpha = this.group ? (ctx.globalAlpha * this.opacity) : this.opacity;
  13031. this._render(ctx, noTransform);
  13032. this._renderFill(ctx);
  13033. this._renderStroke(ctx);
  13034. this.clipTo && ctx.restore();
  13035. this._removeShadow(ctx);
  13036. ctx.restore();
  13037. },
  13038. /**
  13039. * Returns string representation of an instance
  13040. * @return {String} string representation of an instance
  13041. */
  13042. toString: function() {
  13043. return '#<fabric.Path (' + this.complexity() +
  13044. '): { "top": ' + this.top + ', "left": ' + this.left + ' }>';
  13045. },
  13046. /**
  13047. * Returns object representation of an instance
  13048. * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
  13049. * @return {Object} object representation of an instance
  13050. */
  13051. toObject: function(propertiesToInclude) {
  13052. var o = extend(this.callSuper('toObject', propertiesToInclude), {
  13053. path: this.path.map(function(item) { return item.slice() }),
  13054. pathOffset: this.pathOffset
  13055. });
  13056. if (this.sourcePath) {
  13057. o.sourcePath = this.sourcePath;
  13058. }
  13059. if (this.transformMatrix) {
  13060. o.transformMatrix = this.transformMatrix;
  13061. }
  13062. return o;
  13063. },
  13064. /**
  13065. * Returns dataless object representation of an instance
  13066. * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
  13067. * @return {Object} object representation of an instance
  13068. */
  13069. toDatalessObject: function(propertiesToInclude) {
  13070. var o = this.toObject(propertiesToInclude);
  13071. if (this.sourcePath) {
  13072. o.path = this.sourcePath;
  13073. }
  13074. delete o.sourcePath;
  13075. return o;
  13076. },
  13077. /* _TO_SVG_START_ */
  13078. /**
  13079. * Returns svg representation of an instance
  13080. * @param {Function} [reviver] Method for further parsing of svg representation.
  13081. * @return {String} svg representation of an instance
  13082. */
  13083. toSVG: function(reviver) {
  13084. var chunks = [],
  13085. markup = this._createBaseSVGMarkup();
  13086. for (var i = 0, len = this.path.length; i < len; i++) {
  13087. chunks.push(this.path[i].join(' '));
  13088. }
  13089. var path = chunks.join(' ');
  13090. markup.push(
  13091. //jscs:disable validateIndentation
  13092. '<path ',
  13093. 'd="', path,
  13094. '" style="', this.getSvgStyles(),
  13095. '" transform="', this.getSvgTransform(),
  13096. this.getSvgTransformMatrix(), '" stroke-linecap="round" ',
  13097. '/>\n'
  13098. //jscs:enable validateIndentation
  13099. );
  13100. return reviver ? reviver(markup.join('')) : markup.join('');
  13101. },
  13102. /* _TO_SVG_END_ */
  13103. /**
  13104. * Returns number representation of an instance complexity
  13105. * @return {Number} complexity of this instance
  13106. */
  13107. complexity: function() {
  13108. return this.path.length;
  13109. },
  13110. /**
  13111. * @private
  13112. */
  13113. _parsePath: function() {
  13114. var result = [ ],
  13115. coords = [ ],
  13116. currentPath,
  13117. parsed,
  13118. re = /([-+]?((\d+\.\d+)|((\d+)|(\.\d+)))(?:e[-+]?\d+)?)/ig,
  13119. match,
  13120. coordsStr;
  13121. for (var i = 0, coordsParsed, len = this.path.length; i < len; i++) {
  13122. currentPath = this.path[i];
  13123. coordsStr = currentPath.slice(1).trim();
  13124. coords.length = 0;
  13125. while ((match = re.exec(coordsStr))) {
  13126. coords.push(match[0]);
  13127. }
  13128. coordsParsed = [ currentPath.charAt(0) ];
  13129. for (var j = 0, jlen = coords.length; j < jlen; j++) {
  13130. parsed = parseFloat(coords[j]);
  13131. if (!isNaN(parsed)) {
  13132. coordsParsed.push(parsed);
  13133. }
  13134. }
  13135. var command = coordsParsed[0],
  13136. commandLength = commandLengths[command.toLowerCase()],
  13137. repeatedCommand = repeatedCommands[command] || command;
  13138. if (coordsParsed.length - 1 > commandLength) {
  13139. for (var k = 1, klen = coordsParsed.length; k < klen; k += commandLength) {
  13140. result.push([ command ].concat(coordsParsed.slice(k, k + commandLength)));
  13141. command = repeatedCommand;
  13142. }
  13143. }
  13144. else {
  13145. result.push(coordsParsed);
  13146. }
  13147. }
  13148. return result;
  13149. },
  13150. /**
  13151. * @private
  13152. */
  13153. _parseDimensions: function() {
  13154. var aX = [],
  13155. aY = [],
  13156. previous = { };
  13157. this.path.forEach(function(item, i) {
  13158. this._getCoordsFromCommand(item, i, aX, aY, previous);
  13159. }, this);
  13160. var minX = min(aX),
  13161. minY = min(aY),
  13162. maxX = max(aX),
  13163. maxY = max(aY),
  13164. deltaX = maxX - minX,
  13165. deltaY = maxY - minY,
  13166. o = {
  13167. left: this.left + (minX + deltaX / 2),
  13168. top: this.top + (minY + deltaY / 2),
  13169. width: deltaX,
  13170. height: deltaY
  13171. };
  13172. return o;
  13173. },
  13174. _getCoordsFromCommand: function(item, i, aX, aY, previous) {
  13175. var isLowerCase = false;
  13176. if (item[0] !== 'H') {
  13177. previous.x = (i === 0) ? getX(item) : getX(this.path[i - 1]);
  13178. }
  13179. if (item[0] !== 'V') {
  13180. previous.y = (i === 0) ? getY(item) : getY(this.path[i - 1]);
  13181. }
  13182. // lowercased letter denotes relative position;
  13183. // transform to absolute
  13184. if (item[0] === item[0].toLowerCase()) {
  13185. isLowerCase = true;
  13186. }
  13187. var xy = this._getXY(item, isLowerCase, previous),
  13188. val;
  13189. val = parseInt(xy.x, 10);
  13190. if (!isNaN(val)) {
  13191. aX.push(val);
  13192. }
  13193. val = parseInt(xy.y, 10);
  13194. if (!isNaN(val)) {
  13195. aY.push(val);
  13196. }
  13197. },
  13198. _getXY: function(item, isLowerCase, previous) {
  13199. // last 2 items in an array of coordinates are the actualy x/y (except H/V), collect them
  13200. // TODO (kangax): support relative h/v commands
  13201. var x = isLowerCase
  13202. ? previous.x + getX(item)
  13203. : item[0] === 'V'
  13204. ? previous.x
  13205. : getX(item),
  13206. y = isLowerCase
  13207. ? previous.y + getY(item)
  13208. : item[0] === 'H'
  13209. ? previous.y
  13210. : getY(item);
  13211. return { x: x, y: y };
  13212. }
  13213. });
  13214. /**
  13215. * Creates an instance of fabric.Path from an object
  13216. * @static
  13217. * @memberOf fabric.Path
  13218. * @param {Object} object
  13219. * @param {Function} callback Callback to invoke when an fabric.Path instance is created
  13220. */
  13221. fabric.Path.fromObject = function(object, callback) {
  13222. if (typeof object.path === 'string') {
  13223. fabric.loadSVGFromURL(object.path, function (elements) {
  13224. var path = elements[0],
  13225. pathUrl = object.path;
  13226. delete object.path;
  13227. fabric.util.object.extend(path, object);
  13228. path.setSourcePath(pathUrl);
  13229. callback(path);
  13230. });
  13231. }
  13232. else {
  13233. callback(new fabric.Path(object.path, object));
  13234. }
  13235. };
  13236. /* _FROM_SVG_START_ */
  13237. /**
  13238. * List of attribute names to account for when parsing SVG element (used by `fabric.Path.fromElement`)
  13239. * @static
  13240. * @memberOf fabric.Path
  13241. * @see http://www.w3.org/TR/SVG/paths.html#PathElement
  13242. */
  13243. fabric.Path.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat(['d']);
  13244. /**
  13245. * Creates an instance of fabric.Path from an SVG <path> element
  13246. * @static
  13247. * @memberOf fabric.Path
  13248. * @param {SVGElement} element to parse
  13249. * @param {Function} callback Callback to invoke when an fabric.Path instance is created
  13250. * @param {Object} [options] Options object
  13251. */
  13252. fabric.Path.fromElement = function(element, callback, options) {
  13253. var parsedAttributes = fabric.parseAttributes(element, fabric.Path.ATTRIBUTE_NAMES);
  13254. callback && callback(new fabric.Path(parsedAttributes.d, extend(parsedAttributes, options)));
  13255. };
  13256. /* _FROM_SVG_END_ */
  13257. /**
  13258. * Indicates that instances of this type are async
  13259. * @static
  13260. * @memberOf fabric.Path
  13261. * @type Boolean
  13262. * @default
  13263. */
  13264. fabric.Path.async = true;
  13265. })(typeof exports !== 'undefined' ? exports : this);
  13266. (function(global) {
  13267. 'use strict';
  13268. var fabric = global.fabric || (global.fabric = { }),
  13269. extend = fabric.util.object.extend,
  13270. invoke = fabric.util.array.invoke,
  13271. parentToObject = fabric.Object.prototype.toObject;
  13272. if (fabric.PathGroup) {
  13273. fabric.warn('fabric.PathGroup is already defined');
  13274. return;
  13275. }
  13276. /**
  13277. * Path group class
  13278. * @class fabric.PathGroup
  13279. * @extends fabric.Path
  13280. * @tutorial {@link http://fabricjs.com/fabric-intro-part-1/#path_and_pathgroup}
  13281. * @see {@link fabric.PathGroup#initialize} for constructor definition
  13282. */
  13283. fabric.PathGroup = fabric.util.createClass(fabric.Path, /** @lends fabric.PathGroup.prototype */ {
  13284. /**
  13285. * Type of an object
  13286. * @type String
  13287. * @default
  13288. */
  13289. type: 'path-group',
  13290. /**
  13291. * Fill value
  13292. * @type String
  13293. * @default
  13294. */
  13295. fill: '',
  13296. /**
  13297. * Constructor
  13298. * @param {Array} paths
  13299. * @param {Object} [options] Options object
  13300. * @return {fabric.PathGroup} thisArg
  13301. */
  13302. initialize: function(paths, options) {
  13303. options = options || { };
  13304. this.paths = paths || [ ];
  13305. for (var i = this.paths.length; i--; ) {
  13306. this.paths[i].group = this;
  13307. }
  13308. this.setOptions(options);
  13309. if (options.widthAttr) {
  13310. this.scaleX = options.widthAttr / options.width;
  13311. }
  13312. if (options.heightAttr) {
  13313. this.scaleY = options.heightAttr / options.height;
  13314. }
  13315. this.setCoords();
  13316. if (options.sourcePath) {
  13317. this.setSourcePath(options.sourcePath);
  13318. }
  13319. },
  13320. /**
  13321. * Renders this group on a specified context
  13322. * @param {CanvasRenderingContext2D} ctx Context to render this instance on
  13323. */
  13324. render: function(ctx) {
  13325. // do not render if object is not visible
  13326. if (!this.visible) {
  13327. return;
  13328. }
  13329. ctx.save();
  13330. var m = this.transformMatrix;
  13331. if (m) {
  13332. ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
  13333. }
  13334. this.transform(ctx);
  13335. this._setShadow(ctx);
  13336. this.clipTo && fabric.util.clipContext(this, ctx);
  13337. for (var i = 0, l = this.paths.length; i < l; ++i) {
  13338. this.paths[i].render(ctx, true);
  13339. }
  13340. this.clipTo && ctx.restore();
  13341. this._removeShadow(ctx);
  13342. ctx.restore();
  13343. },
  13344. /**
  13345. * Sets certain property to a certain value
  13346. * @param {String} prop
  13347. * @param {Any} value
  13348. * @return {fabric.PathGroup} thisArg
  13349. */
  13350. _set: function(prop, value) {
  13351. if (prop === 'fill' && value && this.isSameColor()) {
  13352. var i = this.paths.length;
  13353. while (i--) {
  13354. this.paths[i]._set(prop, value);
  13355. }
  13356. }
  13357. return this.callSuper('_set', prop, value);
  13358. },
  13359. /**
  13360. * Returns object representation of this path group
  13361. * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
  13362. * @return {Object} object representation of an instance
  13363. */
  13364. toObject: function(propertiesToInclude) {
  13365. var o = extend(parentToObject.call(this, propertiesToInclude), {
  13366. paths: invoke(this.getObjects(), 'toObject', propertiesToInclude)
  13367. });
  13368. if (this.sourcePath) {
  13369. o.sourcePath = this.sourcePath;
  13370. }
  13371. return o;
  13372. },
  13373. /**
  13374. * Returns dataless object representation of this path group
  13375. * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
  13376. * @return {Object} dataless object representation of an instance
  13377. */
  13378. toDatalessObject: function(propertiesToInclude) {
  13379. var o = this.toObject(propertiesToInclude);
  13380. if (this.sourcePath) {
  13381. o.paths = this.sourcePath;
  13382. }
  13383. return o;
  13384. },
  13385. /* _TO_SVG_START_ */
  13386. /**
  13387. * Returns svg representation of an instance
  13388. * @param {Function} [reviver] Method for further parsing of svg representation.
  13389. * @return {String} svg representation of an instance
  13390. */
  13391. toSVG: function(reviver) {
  13392. var objects = this.getObjects(),
  13393. translatePart = 'translate(' + this.left + ' ' + this.top + ')',
  13394. markup = [
  13395. //jscs:disable validateIndentation
  13396. '<g ',
  13397. 'style="', this.getSvgStyles(), '" ',
  13398. 'transform="', translatePart, this.getSvgTransform(), '" ',
  13399. '>\n'
  13400. //jscs:enable validateIndentation
  13401. ];
  13402. for (var i = 0, len = objects.length; i < len; i++) {
  13403. markup.push(objects[i].toSVG(reviver));
  13404. }
  13405. markup.push('</g>\n');
  13406. return reviver ? reviver(markup.join('')) : markup.join('');
  13407. },
  13408. /* _TO_SVG_END_ */
  13409. /**
  13410. * Returns a string representation of this path group
  13411. * @return {String} string representation of an object
  13412. */
  13413. toString: function() {
  13414. return '#<fabric.PathGroup (' + this.complexity() +
  13415. '): { top: ' + this.top + ', left: ' + this.left + ' }>';
  13416. },
  13417. /**
  13418. * Returns true if all paths in this group are of same color
  13419. * @return {Boolean} true if all paths are of the same color (`fill`)
  13420. */
  13421. isSameColor: function() {
  13422. var firstPathFill = (this.getObjects()[0].get('fill') || '').toLowerCase();
  13423. return this.getObjects().every(function(path) {
  13424. return (path.get('fill') || '').toLowerCase() === firstPathFill;
  13425. });
  13426. },
  13427. /**
  13428. * Returns number representation of object's complexity
  13429. * @return {Number} complexity
  13430. */
  13431. complexity: function() {
  13432. return this.paths.reduce(function(total, path) {
  13433. return total + ((path && path.complexity) ? path.complexity() : 0);
  13434. }, 0);
  13435. },
  13436. /**
  13437. * Returns all paths in this path group
  13438. * @return {Array} array of path objects included in this path group
  13439. */
  13440. getObjects: function() {
  13441. return this.paths;
  13442. }
  13443. });
  13444. /**
  13445. * Creates fabric.PathGroup instance from an object representation
  13446. * @static
  13447. * @memberOf fabric.PathGroup
  13448. * @param {Object} object Object to create an instance from
  13449. * @param {Function} callback Callback to invoke when an fabric.PathGroup instance is created
  13450. */
  13451. fabric.PathGroup.fromObject = function(object, callback) {
  13452. if (typeof object.paths === 'string') {
  13453. fabric.loadSVGFromURL(object.paths, function (elements) {
  13454. var pathUrl = object.paths;
  13455. delete object.paths;
  13456. var pathGroup = fabric.util.groupSVGElements(elements, object, pathUrl);
  13457. callback(pathGroup);
  13458. });
  13459. }
  13460. else {
  13461. fabric.util.enlivenObjects(object.paths, function(enlivenedObjects) {
  13462. delete object.paths;
  13463. callback(new fabric.PathGroup(enlivenedObjects, object));
  13464. });
  13465. }
  13466. };
  13467. /**
  13468. * Indicates that instances of this type are async
  13469. * @static
  13470. * @memberOf fabric.PathGroup
  13471. * @type Boolean
  13472. * @default
  13473. */
  13474. fabric.PathGroup.async = true;
  13475. })(typeof exports !== 'undefined' ? exports : this);
  13476. (function(global){
  13477. 'use strict';
  13478. var fabric = global.fabric || (global.fabric = { }),
  13479. extend = fabric.util.object.extend,
  13480. min = fabric.util.array.min,
  13481. max = fabric.util.array.max,
  13482. invoke = fabric.util.array.invoke;
  13483. if (fabric.Group) {
  13484. return;
  13485. }
  13486. // lock-related properties, for use in fabric.Group#get
  13487. // to enable locking behavior on group
  13488. // when one of its objects has lock-related properties set
  13489. var _lockProperties = {
  13490. lockMovementX: true,
  13491. lockMovementY: true,
  13492. lockRotation: true,
  13493. lockScalingX: true,
  13494. lockScalingY: true,
  13495. lockUniScaling: true
  13496. };
  13497. /**
  13498. * Group class
  13499. * @class fabric.Group
  13500. * @extends fabric.Object
  13501. * @mixes fabric.Collection
  13502. * @tutorial {@link http://fabricjs.com/fabric-intro-part-3/#groups}
  13503. * @see {@link fabric.Group#initialize} for constructor definition
  13504. */
  13505. fabric.Group = fabric.util.createClass(fabric.Object, fabric.Collection, /** @lends fabric.Group.prototype */ {
  13506. /**
  13507. * Type of an object
  13508. * @type String
  13509. * @default
  13510. */
  13511. type: 'group',
  13512. /**
  13513. * Constructor
  13514. * @param {Object} objects Group objects
  13515. * @param {Object} [options] Options object
  13516. * @return {Object} thisArg
  13517. */
  13518. initialize: function(objects, options) {
  13519. options = options || { };
  13520. this._objects = objects || [];
  13521. for (var i = this._objects.length; i--; ) {
  13522. this._objects[i].group = this;
  13523. }
  13524. this.originalState = { };
  13525. this.callSuper('initialize');
  13526. this._calcBounds();
  13527. this._updateObjectsCoords();
  13528. if (options) {
  13529. extend(this, options);
  13530. }
  13531. this._setOpacityIfSame();
  13532. this.setCoords();
  13533. this.saveCoords();
  13534. },
  13535. /**
  13536. * @private
  13537. */
  13538. _updateObjectsCoords: function() {
  13539. this.forEachObject(this._updateObjectCoords, this);
  13540. },
  13541. /**
  13542. * @private
  13543. */
  13544. _updateObjectCoords: function(object) {
  13545. var objectLeft = object.getLeft(),
  13546. objectTop = object.getTop();
  13547. object.set({
  13548. originalLeft: objectLeft,
  13549. originalTop: objectTop,
  13550. left: objectLeft - this.left,
  13551. top: objectTop - this.top
  13552. });
  13553. object.setCoords();
  13554. // do not display corners of objects enclosed in a group
  13555. object.__origHasControls = object.hasControls;
  13556. object.hasControls = false;
  13557. },
  13558. /**
  13559. * Returns string represenation of a group
  13560. * @return {String}
  13561. */
  13562. toString: function() {
  13563. return '#<fabric.Group: (' + this.complexity() + ')>';
  13564. },
  13565. /**
  13566. * Adds an object to a group; Then recalculates group's dimension, position.
  13567. * @param {Object} object
  13568. * @return {fabric.Group} thisArg
  13569. * @chainable
  13570. */
  13571. addWithUpdate: function(object) {
  13572. this._restoreObjectsState();
  13573. if (object) {
  13574. this._objects.push(object);
  13575. object.group = this;
  13576. }
  13577. // since _restoreObjectsState set objects inactive
  13578. this.forEachObject(this._setObjectActive, this);
  13579. this._calcBounds();
  13580. this._updateObjectsCoords();
  13581. return this;
  13582. },
  13583. /**
  13584. * @private
  13585. */
  13586. _setObjectActive: function(object) {
  13587. object.set('active', true);
  13588. object.group = this;
  13589. },
  13590. /**
  13591. * Removes an object from a group; Then recalculates group's dimension, position.
  13592. * @param {Object} object
  13593. * @return {fabric.Group} thisArg
  13594. * @chainable
  13595. */
  13596. removeWithUpdate: function(object) {
  13597. this._moveFlippedObject(object);
  13598. this._restoreObjectsState();
  13599. // since _restoreObjectsState set objects inactive
  13600. this.forEachObject(this._setObjectActive, this);
  13601. this.remove(object);
  13602. this._calcBounds();
  13603. this._updateObjectsCoords();
  13604. return this;
  13605. },
  13606. /**
  13607. * @private
  13608. */
  13609. _onObjectAdded: function(object) {
  13610. object.group = this;
  13611. },
  13612. /**
  13613. * @private
  13614. */
  13615. _onObjectRemoved: function(object) {
  13616. delete object.group;
  13617. object.set('active', false);
  13618. },
  13619. /**
  13620. * Properties that are delegated to group objects when reading/writing
  13621. * @param {Object} delegatedProperties
  13622. */
  13623. delegatedProperties: {
  13624. fill: true,
  13625. opacity: true,
  13626. fontFamily: true,
  13627. fontWeight: true,
  13628. fontSize: true,
  13629. fontStyle: true,
  13630. lineHeight: true,
  13631. textDecoration: true,
  13632. textAlign: true,
  13633. backgroundColor: true
  13634. },
  13635. /**
  13636. * @private
  13637. */
  13638. _set: function(key, value) {
  13639. if (key in this.delegatedProperties) {
  13640. var i = this._objects.length;
  13641. this[key] = value;
  13642. while (i--) {
  13643. this._objects[i].set(key, value);
  13644. }
  13645. }
  13646. else {
  13647. this[key] = value;
  13648. }
  13649. },
  13650. /**
  13651. * Returns object representation of an instance
  13652. * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
  13653. * @return {Object} object representation of an instance
  13654. */
  13655. toObject: function(propertiesToInclude) {
  13656. return extend(this.callSuper('toObject', propertiesToInclude), {
  13657. objects: invoke(this._objects, 'toObject', propertiesToInclude)
  13658. });
  13659. },
  13660. /**
  13661. * Renders instance on a given context
  13662. * @param {CanvasRenderingContext2D} ctx context to render instance on
  13663. */
  13664. render: function(ctx) {
  13665. // do not render if object is not visible
  13666. if (!this.visible) {
  13667. return;
  13668. }
  13669. ctx.save();
  13670. this.clipTo && fabric.util.clipContext(this, ctx);
  13671. // the array is now sorted in order of highest first, so start from end
  13672. for (var i = 0, len = this._objects.length; i < len; i++) {
  13673. this._renderObject(this._objects[i], ctx);
  13674. }
  13675. this.clipTo && ctx.restore();
  13676. ctx.restore();
  13677. },
  13678. /**
  13679. * Renders controls and borders for the object
  13680. * @param {CanvasRenderingContext2D} ctx Context to render on
  13681. * @param {Boolean} [noTransform] When true, context is not transformed
  13682. */
  13683. _renderControls: function(ctx, noTransform) {
  13684. this.callSuper('_renderControls', ctx, noTransform);
  13685. for (var i = 0, len = this._objects.length; i < len; i++) {
  13686. this._objects[i]._renderControls(ctx);
  13687. }
  13688. },
  13689. /**
  13690. * @private
  13691. */
  13692. _renderObject: function(object, ctx) {
  13693. var originalHasRotatingPoint = object.hasRotatingPoint;
  13694. // do not render if object is not visible
  13695. if (!object.visible) {
  13696. return;
  13697. }
  13698. object.hasRotatingPoint = false;
  13699. object.render(ctx);
  13700. object.hasRotatingPoint = originalHasRotatingPoint;
  13701. },
  13702. /**
  13703. * Retores original state of each of group objects (original state is that which was before group was created).
  13704. * @private
  13705. * @return {fabric.Group} thisArg
  13706. * @chainable
  13707. */
  13708. _restoreObjectsState: function() {
  13709. this._objects.forEach(this._restoreObjectState, this);
  13710. return this;
  13711. },
  13712. /**
  13713. * Moves a flipped object to the position where it's displayed
  13714. * @private
  13715. * @param {fabric.Object} object
  13716. * @return {fabric.Group} thisArg
  13717. */
  13718. _moveFlippedObject: function(object) {
  13719. var oldOriginX = object.get('originX'),
  13720. oldOriginY = object.get('originY'),
  13721. center = object.getCenterPoint();
  13722. object.set({
  13723. originX: 'center',
  13724. originY: 'center',
  13725. left: center.x,
  13726. top: center.y
  13727. });
  13728. this._toggleFlipping(object);
  13729. var newOrigin = object.getPointByOrigin(oldOriginX, oldOriginY);
  13730. object.set({
  13731. originX: oldOriginX,
  13732. originY: oldOriginY,
  13733. left: newOrigin.x,
  13734. top: newOrigin.y
  13735. });
  13736. return this;
  13737. },
  13738. /**
  13739. * @private
  13740. */
  13741. _toggleFlipping: function(object) {
  13742. if (this.flipX) {
  13743. object.toggle('flipX');
  13744. object.set('left', -object.get('left'));
  13745. object.setAngle(-object.getAngle());
  13746. }
  13747. if (this.flipY) {
  13748. object.toggle('flipY');
  13749. object.set('top', -object.get('top'));
  13750. object.setAngle(-object.getAngle());
  13751. }
  13752. },
  13753. /**
  13754. * Restores original state of a specified object in group
  13755. * @private
  13756. * @param {fabric.Object} object
  13757. * @return {fabric.Group} thisArg
  13758. */
  13759. _restoreObjectState: function(object) {
  13760. this._setObjectPosition(object);
  13761. object.setCoords();
  13762. object.hasControls = object.__origHasControls;
  13763. delete object.__origHasControls;
  13764. object.set('active', false);
  13765. object.setCoords();
  13766. delete object.group;
  13767. return this;
  13768. },
  13769. /**
  13770. * @private
  13771. */
  13772. _setObjectPosition: function(object) {
  13773. var groupLeft = this.getLeft(),
  13774. groupTop = this.getTop(),
  13775. rotated = this._getRotatedLeftTop(object);
  13776. object.set({
  13777. angle: object.getAngle() + this.getAngle(),
  13778. left: groupLeft + rotated.left,
  13779. top: groupTop + rotated.top,
  13780. scaleX: object.get('scaleX') * this.get('scaleX'),
  13781. scaleY: object.get('scaleY') * this.get('scaleY')
  13782. });
  13783. },
  13784. /**
  13785. * @private
  13786. */
  13787. _getRotatedLeftTop: function(object) {
  13788. var groupAngle = this.getAngle() * (Math.PI / 180);
  13789. return {
  13790. left: (-Math.sin(groupAngle) * object.getTop() * this.get('scaleY') +
  13791. Math.cos(groupAngle) * object.getLeft() * this.get('scaleX')),
  13792. top: (Math.cos(groupAngle) * object.getTop() * this.get('scaleY') +
  13793. Math.sin(groupAngle) * object.getLeft() * this.get('scaleX'))
  13794. };
  13795. },
  13796. /**
  13797. * Destroys a group (restoring state of its objects)
  13798. * @return {fabric.Group} thisArg
  13799. * @chainable
  13800. */
  13801. destroy: function() {
  13802. this._objects.forEach(this._moveFlippedObject, this);
  13803. return this._restoreObjectsState();
  13804. },
  13805. /**
  13806. * Saves coordinates of this instance (to be used together with `hasMoved`)
  13807. * @saveCoords
  13808. * @return {fabric.Group} thisArg
  13809. * @chainable
  13810. */
  13811. saveCoords: function() {
  13812. this._originalLeft = this.get('left');
  13813. this._originalTop = this.get('top');
  13814. return this;
  13815. },
  13816. /**
  13817. * Checks whether this group was moved (since `saveCoords` was called last)
  13818. * @return {Boolean} true if an object was moved (since fabric.Group#saveCoords was called)
  13819. */
  13820. hasMoved: function() {
  13821. return this._originalLeft !== this.get('left') ||
  13822. this._originalTop !== this.get('top');
  13823. },
  13824. /**
  13825. * Sets coordinates of all group objects
  13826. * @return {fabric.Group} thisArg
  13827. * @chainable
  13828. */
  13829. setObjectsCoords: function() {
  13830. this.forEachObject(function(object) {
  13831. object.setCoords();
  13832. });
  13833. return this;
  13834. },
  13835. /**
  13836. * @private
  13837. */
  13838. _setOpacityIfSame: function() {
  13839. var objects = this.getObjects(),
  13840. firstValue = objects[0] ? objects[0].get('opacity') : 1,
  13841. isSameOpacity = objects.every(function(o) {
  13842. return o.get('opacity') === firstValue;
  13843. });
  13844. if (isSameOpacity) {
  13845. this.opacity = firstValue;
  13846. }
  13847. },
  13848. /**
  13849. * @private
  13850. */
  13851. _calcBounds: function(onlyWidthHeight) {
  13852. var aX = [],
  13853. aY = [],
  13854. o;
  13855. for (var i = 0, len = this._objects.length; i < len; ++i) {
  13856. o = this._objects[i];
  13857. o.setCoords();
  13858. for (var prop in o.oCoords) {
  13859. aX.push(o.oCoords[prop].x);
  13860. aY.push(o.oCoords[prop].y);
  13861. }
  13862. }
  13863. this.set(this._getBounds(aX, aY, onlyWidthHeight));
  13864. },
  13865. /**
  13866. * @private
  13867. */
  13868. _getBounds: function(aX, aY, onlyWidthHeight) {
  13869. var ivt = fabric.util.invertTransform(this.getViewportTransform()),
  13870. minXY = fabric.util.transformPoint(new fabric.Point(min(aX), min(aY)), ivt),
  13871. maxXY = fabric.util.transformPoint(new fabric.Point(max(aX), max(aY)), ivt),
  13872. obj = {
  13873. width: (maxXY.x - minXY.x) || 0,
  13874. height: (maxXY.y - minXY.y) || 0
  13875. };
  13876. if (!onlyWidthHeight) {
  13877. obj.left = (minXY.x + maxXY.x) / 2 || 0;
  13878. obj.top = (minXY.y + maxXY.y) / 2 || 0;
  13879. }
  13880. return obj;
  13881. },
  13882. /* _TO_SVG_START_ */
  13883. /**
  13884. * Returns svg representation of an instance
  13885. * @param {Function} [reviver] Method for further parsing of svg representation.
  13886. * @return {String} svg representation of an instance
  13887. */
  13888. toSVG: function(reviver) {
  13889. var markup = [
  13890. //jscs:disable validateIndentation
  13891. '<g ',
  13892. 'transform="', this.getSvgTransform(),
  13893. '">\n'
  13894. //jscs:enable validateIndentation
  13895. ];
  13896. for (var i = 0, len = this._objects.length; i < len; i++) {
  13897. markup.push(this._objects[i].toSVG(reviver));
  13898. }
  13899. markup.push('</g>\n');
  13900. return reviver ? reviver(markup.join('')) : markup.join('');
  13901. },
  13902. /* _TO_SVG_END_ */
  13903. /**
  13904. * Returns requested property
  13905. * @param {String} prop Property to get
  13906. * @return {Any}
  13907. */
  13908. get: function(prop) {
  13909. if (prop in _lockProperties) {
  13910. if (this[prop]) {
  13911. return this[prop];
  13912. }
  13913. else {
  13914. for (var i = 0, len = this._objects.length; i < len; i++) {
  13915. if (this._objects[i][prop]) {
  13916. return true;
  13917. }
  13918. }
  13919. return false;
  13920. }
  13921. }
  13922. else {
  13923. if (prop in this.delegatedProperties) {
  13924. return this._objects[0] && this._objects[0].get(prop);
  13925. }
  13926. return this[prop];
  13927. }
  13928. }
  13929. });
  13930. /**
  13931. * Returns {@link fabric.Group} instance from an object representation
  13932. * @static
  13933. * @memberOf fabric.Group
  13934. * @param {Object} object Object to create a group from
  13935. * @param {Function} [callback] Callback to invoke when an group instance is created
  13936. * @return {fabric.Group} An instance of fabric.Group
  13937. */
  13938. fabric.Group.fromObject = function(object, callback) {
  13939. fabric.util.enlivenObjects(object.objects, function(enlivenedObjects) {
  13940. delete object.objects;
  13941. callback && callback(new fabric.Group(enlivenedObjects, object));
  13942. });
  13943. };
  13944. /**
  13945. * Indicates that instances of this type are async
  13946. * @static
  13947. * @memberOf fabric.Group
  13948. * @type Boolean
  13949. * @default
  13950. */
  13951. fabric.Group.async = true;
  13952. })(typeof exports !== 'undefined' ? exports : this);
  13953. (function(global) {
  13954. 'use strict';
  13955. var extend = fabric.util.object.extend;
  13956. if (!global.fabric) {
  13957. global.fabric = { };
  13958. }
  13959. if (global.fabric.Image) {
  13960. fabric.warn('fabric.Image is already defined.');
  13961. return;
  13962. }
  13963. /**
  13964. * Image class
  13965. * @class fabric.Image
  13966. * @extends fabric.Object
  13967. * @tutorial {@link http://fabricjs.com/fabric-intro-part-1/#images}
  13968. * @see {@link fabric.Image#initialize} for constructor definition
  13969. */
  13970. fabric.Image = fabric.util.createClass(fabric.Object, /** @lends fabric.Image.prototype */ {
  13971. /**
  13972. * Type of an object
  13973. * @type String
  13974. * @default
  13975. */
  13976. type: 'image',
  13977. /**
  13978. * crossOrigin value (one of "", "anonymous", "allow-credentials")
  13979. * @see https://developer.mozilla.org/en-US/docs/HTML/CORS_settings_attributes
  13980. * @type String
  13981. * @default
  13982. */
  13983. crossOrigin: '',
  13984. /**
  13985. * Constructor
  13986. * @param {HTMLImageElement | String} element Image element
  13987. * @param {Object} [options] Options object
  13988. * @return {fabric.Image} thisArg
  13989. */
  13990. initialize: function(element, options) {
  13991. options || (options = { });
  13992. this.filters = [ ];
  13993. this.callSuper('initialize', options);
  13994. this._initElement(element, options);
  13995. this._initConfig(options);
  13996. if (options.filters) {
  13997. this.filters = options.filters;
  13998. this.applyFilters();
  13999. }
  14000. },
  14001. /**
  14002. * Returns image element which this instance if based on
  14003. * @return {HTMLImageElement} Image element
  14004. */
  14005. getElement: function() {
  14006. return this._element;
  14007. },
  14008. /**
  14009. * Sets image element for this instance to a specified one.
  14010. * If filters defined they are applied to new image.
  14011. * You might need to call `canvas.renderAll` and `object.setCoords` after replacing, to render new image and update controls area.
  14012. * @param {HTMLImageElement} element
  14013. * @param {Function} [callback] Callback is invoked when all filters have been applied and new image is generated
  14014. * @return {fabric.Image} thisArg
  14015. * @chainable
  14016. */
  14017. setElement: function(element, callback) {
  14018. this._element = element;
  14019. this._originalElement = element;
  14020. this._initConfig();
  14021. if (this.filters.length !== 0) {
  14022. this.applyFilters(callback);
  14023. }
  14024. return this;
  14025. },
  14026. /**
  14027. * Sets crossOrigin value (on an instance and corresponding image element)
  14028. * @return {fabric.Image} thisArg
  14029. * @chainable
  14030. */
  14031. setCrossOrigin: function(value) {
  14032. this.crossOrigin = value;
  14033. this._element.crossOrigin = value;
  14034. return this;
  14035. },
  14036. /**
  14037. * Returns original size of an image
  14038. * @return {Object} Object with "width" and "height" properties
  14039. */
  14040. getOriginalSize: function() {
  14041. var element = this.getElement();
  14042. return {
  14043. width: element.width,
  14044. height: element.height
  14045. };
  14046. },
  14047. /**
  14048. * @private
  14049. * @param {CanvasRenderingContext2D} ctx Context to render on
  14050. */
  14051. _stroke: function(ctx) {
  14052. ctx.save();
  14053. this._setStrokeStyles(ctx);
  14054. ctx.beginPath();
  14055. ctx.strokeRect(-this.width / 2, -this.height / 2, this.width, this.height);
  14056. ctx.closePath();
  14057. ctx.restore();
  14058. },
  14059. /**
  14060. * @private
  14061. * @param {CanvasRenderingContext2D} ctx Context to render on
  14062. */
  14063. _renderDashedStroke: function(ctx) {
  14064. var x = -this.width / 2,
  14065. y = -this.height / 2,
  14066. w = this.width,
  14067. h = this.height;
  14068. ctx.save();
  14069. this._setStrokeStyles(ctx);
  14070. ctx.beginPath();
  14071. fabric.util.drawDashedLine(ctx, x, y, x + w, y, this.strokeDashArray);
  14072. fabric.util.drawDashedLine(ctx, x + w, y, x + w, y + h, this.strokeDashArray);
  14073. fabric.util.drawDashedLine(ctx, x + w, y + h, x, y + h, this.strokeDashArray);
  14074. fabric.util.drawDashedLine(ctx, x, y + h, x, y, this.strokeDashArray);
  14075. ctx.closePath();
  14076. ctx.restore();
  14077. },
  14078. /**
  14079. * Returns object representation of an instance
  14080. * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
  14081. * @return {Object} Object representation of an instance
  14082. */
  14083. toObject: function(propertiesToInclude) {
  14084. return extend(this.callSuper('toObject', propertiesToInclude), {
  14085. src: this._originalElement.src || this._originalElement._src,
  14086. filters: this.filters.map(function(filterObj) {
  14087. return filterObj && filterObj.toObject();
  14088. }),
  14089. crossOrigin: this.crossOrigin
  14090. });
  14091. },
  14092. /* _TO_SVG_START_ */
  14093. /**
  14094. * Returns SVG representation of an instance
  14095. * @param {Function} [reviver] Method for further parsing of svg representation.
  14096. * @return {String} svg representation of an instance
  14097. */
  14098. toSVG: function(reviver) {
  14099. var markup = [], x = -this.width / 2, y = -this.height / 2;
  14100. if (this.group) {
  14101. x = this.left;
  14102. y = this.top;
  14103. }
  14104. markup.push(
  14105. '<g transform="', this.getSvgTransform(), this.getSvgTransformMatrix(), '">\n',
  14106. '<image xlink:href="', this.getSvgSrc(),
  14107. '" x="', x, '" y="', y,
  14108. '" style="', this.getSvgStyles(),
  14109. // we're essentially moving origin of transformation from top/left corner to the center of the shape
  14110. // by wrapping it in container <g> element with actual transformation, then offsetting object to the top/left
  14111. // so that object's center aligns with container's left/top
  14112. '" width="', this.width,
  14113. '" height="', this.height,
  14114. '" preserveAspectRatio="none"',
  14115. '></image>\n'
  14116. );
  14117. if (this.stroke || this.strokeDashArray) {
  14118. var origFill = this.fill;
  14119. this.fill = null;
  14120. markup.push(
  14121. '<rect ',
  14122. 'x="', x, '" y="', y,
  14123. '" width="', this.width, '" height="', this.height,
  14124. '" style="', this.getSvgStyles(),
  14125. '"/>\n'
  14126. );
  14127. this.fill = origFill;
  14128. }
  14129. markup.push('</g>\n');
  14130. return reviver ? reviver(markup.join('')) : markup.join('');
  14131. },
  14132. /* _TO_SVG_END_ */
  14133. /**
  14134. * Returns source of an image
  14135. * @return {String} Source of an image
  14136. */
  14137. getSrc: function() {
  14138. if (this.getElement()) {
  14139. return this.getElement().src || this.getElement()._src;
  14140. }
  14141. },
  14142. /**
  14143. * Returns string representation of an instance
  14144. * @return {String} String representation of an instance
  14145. */
  14146. toString: function() {
  14147. return '#<fabric.Image: { src: "' + this.getSrc() + '" }>';
  14148. },
  14149. /**
  14150. * Returns a clone of an instance
  14151. * @param {Function} callback Callback is invoked with a clone as a first argument
  14152. * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
  14153. */
  14154. clone: function(callback, propertiesToInclude) {
  14155. this.constructor.fromObject(this.toObject(propertiesToInclude), callback);
  14156. },
  14157. /**
  14158. * Applies filters assigned to this image (from "filters" array)
  14159. * @mthod applyFilters
  14160. * @param {Function} callback Callback is invoked when all filters have been applied and new image is generated
  14161. * @return {fabric.Image} thisArg
  14162. * @chainable
  14163. */
  14164. applyFilters: function(callback) {
  14165. if (!this._originalElement) {
  14166. return;
  14167. }
  14168. if (this.filters.length === 0) {
  14169. this._element = this._originalElement;
  14170. callback && callback();
  14171. return;
  14172. }
  14173. var imgEl = this._originalElement,
  14174. canvasEl = fabric.util.createCanvasElement(),
  14175. replacement = fabric.util.createImage(),
  14176. _this = this;
  14177. canvasEl.width = imgEl.width;
  14178. canvasEl.height = imgEl.height;
  14179. canvasEl.getContext('2d').drawImage(imgEl, 0, 0, imgEl.width, imgEl.height);
  14180. this.filters.forEach(function(filter) {
  14181. filter && filter.applyTo(canvasEl);
  14182. });
  14183. /** @ignore */
  14184. replacement.width = imgEl.width;
  14185. replacement.height = imgEl.height;
  14186. if (fabric.isLikelyNode) {
  14187. replacement.src = canvasEl.toBuffer(undefined, fabric.Image.pngCompression);
  14188. // onload doesn't fire in some node versions, so we invoke callback manually
  14189. _this._element = replacement;
  14190. callback && callback();
  14191. }
  14192. else {
  14193. replacement.onload = function() {
  14194. _this._element = replacement;
  14195. callback && callback();
  14196. replacement.onload = canvasEl = imgEl = null;
  14197. };
  14198. replacement.src = canvasEl.toDataURL('image/png');
  14199. }
  14200. return this;
  14201. },
  14202. /**
  14203. * @private
  14204. * @param {CanvasRenderingContext2D} ctx Context to render on
  14205. */
  14206. _render: function(ctx, noTransform) {
  14207. this._element &&
  14208. ctx.drawImage(
  14209. this._element,
  14210. noTransform ? this.left : -this.width/2,
  14211. noTransform ? this.top : -this.height/2,
  14212. this.width,
  14213. this.height
  14214. );
  14215. this._renderStroke(ctx);
  14216. },
  14217. /**
  14218. * @private
  14219. */
  14220. _resetWidthHeight: function() {
  14221. var element = this.getElement();
  14222. this.set('width', element.width);
  14223. this.set('height', element.height);
  14224. },
  14225. /**
  14226. * The Image class's initialization method. This method is automatically
  14227. * called by the constructor.
  14228. * @private
  14229. * @param {HTMLImageElement|String} element The element representing the image
  14230. */
  14231. _initElement: function(element) {
  14232. this.setElement(fabric.util.getById(element));
  14233. fabric.util.addClass(this.getElement(), fabric.Image.CSS_CANVAS);
  14234. },
  14235. /**
  14236. * @private
  14237. * @param {Object} [options] Options object
  14238. */
  14239. _initConfig: function(options) {
  14240. options || (options = { });
  14241. this.setOptions(options);
  14242. this._setWidthHeight(options);
  14243. if (this._element && this.crossOrigin) {
  14244. this._element.crossOrigin = this.crossOrigin;
  14245. }
  14246. },
  14247. /**
  14248. * @private
  14249. * @param {Object} object Object with filters property
  14250. * @param {Function} callback Callback to invoke when all fabric.Image.filters instances are created
  14251. */
  14252. _initFilters: function(object, callback) {
  14253. if (object.filters && object.filters.length) {
  14254. fabric.util.enlivenObjects(object.filters, function(enlivenedObjects) {
  14255. callback && callback(enlivenedObjects);
  14256. }, 'fabric.Image.filters');
  14257. }
  14258. else {
  14259. callback && callback();
  14260. }
  14261. },
  14262. /**
  14263. * @private
  14264. * @param {Object} [options] Object with width/height properties
  14265. */
  14266. _setWidthHeight: function(options) {
  14267. this.width = 'width' in options
  14268. ? options.width
  14269. : (this.getElement()
  14270. ? this.getElement().width || 0
  14271. : 0);
  14272. this.height = 'height' in options
  14273. ? options.height
  14274. : (this.getElement()
  14275. ? this.getElement().height || 0
  14276. : 0);
  14277. },
  14278. /**
  14279. * Returns complexity of an instance
  14280. * @return {Number} complexity of this instance
  14281. */
  14282. complexity: function() {
  14283. return 1;
  14284. }
  14285. });
  14286. /**
  14287. * Default CSS class name for canvas
  14288. * @static
  14289. * @type String
  14290. * @default
  14291. */
  14292. fabric.Image.CSS_CANVAS = 'canvas-img';
  14293. /**
  14294. * Alias for getSrc
  14295. * @static
  14296. */
  14297. fabric.Image.prototype.getSvgSrc = fabric.Image.prototype.getSrc;
  14298. /**
  14299. * Creates an instance of fabric.Image from its object representation
  14300. * @static
  14301. * @param {Object} object Object to create an instance from
  14302. * @param {Function} [callback] Callback to invoke when an image instance is created
  14303. */
  14304. fabric.Image.fromObject = function(object, callback) {
  14305. fabric.util.loadImage(object.src, function(img) {
  14306. fabric.Image.prototype._initFilters.call(object, object, function(filters) {
  14307. object.filters = filters || [ ];
  14308. var instance = new fabric.Image(img, object);
  14309. callback && callback(instance);
  14310. });
  14311. }, null, object.crossOrigin);
  14312. };
  14313. /**
  14314. * Creates an instance of fabric.Image from an URL string
  14315. * @static
  14316. * @param {String} url URL to create an image from
  14317. * @param {Function} [callback] Callback to invoke when image is created (newly created image is passed as a first argument)
  14318. * @param {Object} [imgOptions] Options object
  14319. */
  14320. fabric.Image.fromURL = function(url, callback, imgOptions) {
  14321. fabric.util.loadImage(url, function(img) {
  14322. callback(new fabric.Image(img, imgOptions));
  14323. }, null, imgOptions && imgOptions.crossOrigin);
  14324. };
  14325. /* _FROM_SVG_START_ */
  14326. /**
  14327. * List of attribute names to account for when parsing SVG element (used by {@link fabric.Image.fromElement})
  14328. * @static
  14329. * @see {@link http://www.w3.org/TR/SVG/struct.html#ImageElement}
  14330. */
  14331. fabric.Image.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat('x y width height xlink:href'.split(' '));
  14332. /**
  14333. * Returns {@link fabric.Image} instance from an SVG element
  14334. * @static
  14335. * @param {SVGElement} element Element to parse
  14336. * @param {Function} callback Callback to execute when fabric.Image object is created
  14337. * @param {Object} [options] Options object
  14338. * @return {fabric.Image} Instance of fabric.Image
  14339. */
  14340. fabric.Image.fromElement = function(element, callback, options) {
  14341. var parsedAttributes = fabric.parseAttributes(element, fabric.Image.ATTRIBUTE_NAMES);
  14342. fabric.Image.fromURL(parsedAttributes['xlink:href'], callback,
  14343. extend((options ? fabric.util.object.clone(options) : { }), parsedAttributes));
  14344. };
  14345. /* _FROM_SVG_END_ */
  14346. /**
  14347. * Indicates that instances of this type are async
  14348. * @static
  14349. * @type Boolean
  14350. * @default
  14351. */
  14352. fabric.Image.async = true;
  14353. /**
  14354. * Indicates compression level used when generating PNG under Node (in applyFilters). Any of 0-9
  14355. * @static
  14356. * @type Number
  14357. * @default
  14358. */
  14359. fabric.Image.pngCompression = 1;
  14360. })(typeof exports !== 'undefined' ? exports : this);
  14361. /**
  14362. * @namespace fabric.Image.filters
  14363. * @memberOf fabric.Image
  14364. * @tutorial {@link http://fabricjs.com/fabric-intro-part-2/#image_filters}
  14365. * @see {@link http://fabricjs.com/image-filters/|ImageFilters demo}
  14366. */
  14367. fabric.Image.filters = fabric.Image.filters || { };
  14368. /**
  14369. * Root filter class from which all filter classes inherit from
  14370. * @class fabric.Image.filters.BaseFilter
  14371. * @memberOf fabric.Image.filters
  14372. */
  14373. fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Image.filters.BaseFilter.prototype */ {
  14374. /**
  14375. * Filter type
  14376. * @param {String} type
  14377. * @default
  14378. */
  14379. type: 'BaseFilter',
  14380. /**
  14381. * Returns object representation of an instance
  14382. * @return {Object} Object representation of an instance
  14383. */
  14384. toObject: function() {
  14385. return { type: this.type };
  14386. },
  14387. /**
  14388. * Returns a JSON representation of an instance
  14389. * @return {Object} JSON
  14390. */
  14391. toJSON: function() {
  14392. // delegate, not alias
  14393. return this.toObject();
  14394. }
  14395. });
  14396. (function(global) {
  14397. 'use strict';
  14398. var fabric = global.fabric || (global.fabric = { }),
  14399. extend = fabric.util.object.extend;
  14400. /**
  14401. * Brightness filter class
  14402. * @class fabric.Image.filters.Brightness
  14403. * @memberOf fabric.Image.filters
  14404. * @extends fabric.Image.filters.BaseFilter
  14405. * @see {@link fabric.Image.filters.Brightness#initialize} for constructor definition
  14406. * @see {@link http://fabricjs.com/image-filters/|ImageFilters demo}
  14407. * @example
  14408. * var filter = new fabric.Image.filters.Brightness({
  14409. * brightness: 200
  14410. * });
  14411. * object.filters.push(filter);
  14412. * object.applyFilters(canvas.renderAll.bind(canvas));
  14413. */
  14414. fabric.Image.filters.Brightness = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.Brightness.prototype */ {
  14415. /**
  14416. * Filter type
  14417. * @param {String} type
  14418. * @default
  14419. */
  14420. type: 'Brightness',
  14421. /**
  14422. * Constructor
  14423. * @memberOf fabric.Image.filters.Brightness.prototype
  14424. * @param {Object} [options] Options object
  14425. * @param {Number} [options.brightness=0] Value to brighten the image up (0..255)
  14426. */
  14427. initialize: function(options) {
  14428. options = options || { };
  14429. this.brightness = options.brightness || 0;
  14430. },
  14431. /**
  14432. * Applies filter to canvas element
  14433. * @param {Object} canvasEl Canvas element to apply filter to
  14434. */
  14435. applyTo: function(canvasEl) {
  14436. var context = canvasEl.getContext('2d'),
  14437. imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
  14438. data = imageData.data,
  14439. brightness = this.brightness;
  14440. for (var i = 0, len = data.length; i < len; i += 4) {
  14441. data[i] += brightness;
  14442. data[i + 1] += brightness;
  14443. data[i + 2] += brightness;
  14444. }
  14445. context.putImageData(imageData, 0, 0);
  14446. },
  14447. /**
  14448. * Returns object representation of an instance
  14449. * @return {Object} Object representation of an instance
  14450. */
  14451. toObject: function() {
  14452. return extend(this.callSuper('toObject'), {
  14453. brightness: this.brightness
  14454. });
  14455. }
  14456. });
  14457. /**
  14458. * Returns filter instance from an object representation
  14459. * @static
  14460. * @param {Object} object Object to create an instance from
  14461. * @return {fabric.Image.filters.Brightness} Instance of fabric.Image.filters.Brightness
  14462. */
  14463. fabric.Image.filters.Brightness.fromObject = function(object) {
  14464. return new fabric.Image.filters.Brightness(object);
  14465. };
  14466. })(typeof exports !== 'undefined' ? exports : this);
  14467. (function(global) {
  14468. 'use strict';
  14469. var fabric = global.fabric || (global.fabric = { }),
  14470. extend = fabric.util.object.extend;
  14471. /**
  14472. * Adapted from <a href="http://www.html5rocks.com/en/tutorials/canvas/imagefilters/">html5rocks article</a>
  14473. * @class fabric.Image.filters.Convolute
  14474. * @memberOf fabric.Image.filters
  14475. * @extends fabric.Image.filters.BaseFilter
  14476. * @see {@link fabric.Image.filters.Convolute#initialize} for constructor definition
  14477. * @see {@link http://fabricjs.com/image-filters/|ImageFilters demo}
  14478. * @example <caption>Sharpen filter</caption>
  14479. * var filter = new fabric.Image.filters.Convolute({
  14480. * matrix: [ 0, -1, 0,
  14481. * -1, 5, -1,
  14482. * 0, -1, 0 ]
  14483. * });
  14484. * object.filters.push(filter);
  14485. * object.applyFilters(canvas.renderAll.bind(canvas));
  14486. * @example <caption>Blur filter</caption>
  14487. * var filter = new fabric.Image.filters.Convolute({
  14488. * matrix: [ 1/9, 1/9, 1/9,
  14489. * 1/9, 1/9, 1/9,
  14490. * 1/9, 1/9, 1/9 ]
  14491. * });
  14492. * object.filters.push(filter);
  14493. * object.applyFilters(canvas.renderAll.bind(canvas));
  14494. * @example <caption>Emboss filter</caption>
  14495. * var filter = new fabric.Image.filters.Convolute({
  14496. * matrix: [ 1, 1, 1,
  14497. * 1, 0.7, -1,
  14498. * -1, -1, -1 ]
  14499. * });
  14500. * object.filters.push(filter);
  14501. * object.applyFilters(canvas.renderAll.bind(canvas));
  14502. * @example <caption>Emboss filter with opaqueness</caption>
  14503. * var filter = new fabric.Image.filters.Convolute({
  14504. * opaque: true,
  14505. * matrix: [ 1, 1, 1,
  14506. * 1, 0.7, -1,
  14507. * -1, -1, -1 ]
  14508. * });
  14509. * object.filters.push(filter);
  14510. * object.applyFilters(canvas.renderAll.bind(canvas));
  14511. */
  14512. fabric.Image.filters.Convolute = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.Convolute.prototype */ {
  14513. /**
  14514. * Filter type
  14515. * @param {String} type
  14516. * @default
  14517. */
  14518. type: 'Convolute',
  14519. /**
  14520. * Constructor
  14521. * @memberOf fabric.Image.filters.Convolute.prototype
  14522. * @param {Object} [options] Options object
  14523. * @param {Boolean} [options.opaque=false] Opaque value (true/false)
  14524. * @param {Array} [options.matrix] Filter matrix
  14525. */
  14526. initialize: function(options) {
  14527. options = options || { };
  14528. this.opaque = options.opaque;
  14529. this.matrix = options.matrix || [
  14530. 0, 0, 0,
  14531. 0, 1, 0,
  14532. 0, 0, 0
  14533. ];
  14534. var canvasEl = fabric.util.createCanvasElement();
  14535. this.tmpCtx = canvasEl.getContext('2d');
  14536. },
  14537. /**
  14538. * @private
  14539. */
  14540. _createImageData: function(w, h) {
  14541. return this.tmpCtx.createImageData(w, h);
  14542. },
  14543. /**
  14544. * Applies filter to canvas element
  14545. * @param {Object} canvasEl Canvas element to apply filter to
  14546. */
  14547. applyTo: function(canvasEl) {
  14548. var weights = this.matrix,
  14549. context = canvasEl.getContext('2d'),
  14550. pixels = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
  14551. side = Math.round(Math.sqrt(weights.length)),
  14552. halfSide = Math.floor(side/2),
  14553. src = pixels.data,
  14554. sw = pixels.width,
  14555. sh = pixels.height,
  14556. // pad output by the convolution matrix
  14557. w = sw,
  14558. h = sh,
  14559. output = this._createImageData(w, h),
  14560. dst = output.data,
  14561. // go through the destination image pixels
  14562. alphaFac = this.opaque ? 1 : 0;
  14563. for (var y = 0; y < h; y++) {
  14564. for (var x = 0; x < w; x++) {
  14565. var sy = y,
  14566. sx = x,
  14567. dstOff = (y * w + x) * 4,
  14568. // calculate the weighed sum of the source image pixels that
  14569. // fall under the convolution matrix
  14570. r = 0, g = 0, b = 0, a = 0;
  14571. for (var cy = 0; cy < side; cy++) {
  14572. for (var cx = 0; cx < side; cx++) {
  14573. var scy = sy + cy - halfSide,
  14574. scx = sx + cx - halfSide;
  14575. /* jshint maxdepth:5 */
  14576. if (scy < 0 || scy > sh || scx < 0 || scx > sw) {
  14577. continue;
  14578. }
  14579. var srcOff = (scy * sw + scx) * 4,
  14580. wt = weights[cy * side + cx];
  14581. r += src[srcOff] * wt;
  14582. g += src[srcOff + 1] * wt;
  14583. b += src[srcOff + 2] * wt;
  14584. a += src[srcOff + 3] * wt;
  14585. }
  14586. }
  14587. dst[dstOff] = r;
  14588. dst[dstOff + 1] = g;
  14589. dst[dstOff + 2] = b;
  14590. dst[dstOff + 3] = a + alphaFac * (255 - a);
  14591. }
  14592. }
  14593. context.putImageData(output, 0, 0);
  14594. },
  14595. /**
  14596. * Returns object representation of an instance
  14597. * @return {Object} Object representation of an instance
  14598. */
  14599. toObject: function() {
  14600. return extend(this.callSuper('toObject'), {
  14601. opaque: this.opaque,
  14602. matrix: this.matrix
  14603. });
  14604. }
  14605. });
  14606. /**
  14607. * Returns filter instance from an object representation
  14608. * @static
  14609. * @param {Object} object Object to create an instance from
  14610. * @return {fabric.Image.filters.Convolute} Instance of fabric.Image.filters.Convolute
  14611. */
  14612. fabric.Image.filters.Convolute.fromObject = function(object) {
  14613. return new fabric.Image.filters.Convolute(object);
  14614. };
  14615. })(typeof exports !== 'undefined' ? exports : this);
  14616. (function(global) {
  14617. 'use strict';
  14618. var fabric = global.fabric || (global.fabric = { }),
  14619. extend = fabric.util.object.extend;
  14620. /**
  14621. * GradientTransparency filter class
  14622. * @class fabric.Image.filters.GradientTransparency
  14623. * @memberOf fabric.Image.filters
  14624. * @extends fabric.Image.filters.BaseFilter
  14625. * @see {@link fabric.Image.filters.GradientTransparency#initialize} for constructor definition
  14626. * @see {@link http://fabricjs.com/image-filters/|ImageFilters demo}
  14627. * @example
  14628. * var filter = new fabric.Image.filters.GradientTransparency({
  14629. * threshold: 200
  14630. * });
  14631. * object.filters.push(filter);
  14632. * object.applyFilters(canvas.renderAll.bind(canvas));
  14633. */
  14634. fabric.Image.filters.GradientTransparency = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.GradientTransparency.prototype */ {
  14635. /**
  14636. * Filter type
  14637. * @param {String} type
  14638. * @default
  14639. */
  14640. type: 'GradientTransparency',
  14641. /**
  14642. * Constructor
  14643. * @memberOf fabric.Image.filters.GradientTransparency.prototype
  14644. * @param {Object} [options] Options object
  14645. * @param {Number} [options.threshold=100] Threshold value
  14646. */
  14647. initialize: function(options) {
  14648. options = options || { };
  14649. this.threshold = options.threshold || 100;
  14650. },
  14651. /**
  14652. * Applies filter to canvas element
  14653. * @param {Object} canvasEl Canvas element to apply filter to
  14654. */
  14655. applyTo: function(canvasEl) {
  14656. var context = canvasEl.getContext('2d'),
  14657. imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
  14658. data = imageData.data,
  14659. threshold = this.threshold,
  14660. total = data.length;
  14661. for (var i = 0, len = data.length; i < len; i += 4) {
  14662. data[i + 3] = threshold + 255 * (total - i) / total;
  14663. }
  14664. context.putImageData(imageData, 0, 0);
  14665. },
  14666. /**
  14667. * Returns object representation of an instance
  14668. * @return {Object} Object representation of an instance
  14669. */
  14670. toObject: function() {
  14671. return extend(this.callSuper('toObject'), {
  14672. threshold: this.threshold
  14673. });
  14674. }
  14675. });
  14676. /**
  14677. * Returns filter instance from an object representation
  14678. * @static
  14679. * @param {Object} object Object to create an instance from
  14680. * @return {fabric.Image.filters.GradientTransparency} Instance of fabric.Image.filters.GradientTransparency
  14681. */
  14682. fabric.Image.filters.GradientTransparency.fromObject = function(object) {
  14683. return new fabric.Image.filters.GradientTransparency(object);
  14684. };
  14685. })(typeof exports !== 'undefined' ? exports : this);
  14686. (function(global) {
  14687. 'use strict';
  14688. var fabric = global.fabric || (global.fabric = { });
  14689. /**
  14690. * Grayscale image filter class
  14691. * @class fabric.Image.filters.Grayscale
  14692. * @memberOf fabric.Image.filters
  14693. * @extends fabric.Image.filters.BaseFilter
  14694. * @see {@link http://fabricjs.com/image-filters/|ImageFilters demo}
  14695. * @example
  14696. * var filter = new fabric.Image.filters.Grayscale();
  14697. * object.filters.push(filter);
  14698. * object.applyFilters(canvas.renderAll.bind(canvas));
  14699. */
  14700. fabric.Image.filters.Grayscale = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.Grayscale.prototype */ {
  14701. /**
  14702. * Filter type
  14703. * @param {String} type
  14704. * @default
  14705. */
  14706. type: 'Grayscale',
  14707. /**
  14708. * Applies filter to canvas element
  14709. * @memberOf fabric.Image.filters.Grayscale.prototype
  14710. * @param {Object} canvasEl Canvas element to apply filter to
  14711. */
  14712. applyTo: function(canvasEl) {
  14713. var context = canvasEl.getContext('2d'),
  14714. imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
  14715. data = imageData.data,
  14716. len = imageData.width * imageData.height * 4,
  14717. index = 0,
  14718. average;
  14719. while (index < len) {
  14720. average = (data[index] + data[index + 1] + data[index + 2]) / 3;
  14721. data[index] = average;
  14722. data[index + 1] = average;
  14723. data[index + 2] = average;
  14724. index += 4;
  14725. }
  14726. context.putImageData(imageData, 0, 0);
  14727. }
  14728. });
  14729. /**
  14730. * Returns filter instance from an object representation
  14731. * @static
  14732. * @return {fabric.Image.filters.Grayscale} Instance of fabric.Image.filters.Grayscale
  14733. */
  14734. fabric.Image.filters.Grayscale.fromObject = function() {
  14735. return new fabric.Image.filters.Grayscale();
  14736. };
  14737. })(typeof exports !== 'undefined' ? exports : this);
  14738. (function(global) {
  14739. 'use strict';
  14740. var fabric = global.fabric || (global.fabric = { });
  14741. /**
  14742. * Invert filter class
  14743. * @class fabric.Image.filters.Invert
  14744. * @memberOf fabric.Image.filters
  14745. * @extends fabric.Image.filters.BaseFilter
  14746. * @see {@link http://fabricjs.com/image-filters/|ImageFilters demo}
  14747. * @example
  14748. * var filter = new fabric.Image.filters.Invert();
  14749. * object.filters.push(filter);
  14750. * object.applyFilters(canvas.renderAll.bind(canvas));
  14751. */
  14752. fabric.Image.filters.Invert = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.Invert.prototype */ {
  14753. /**
  14754. * Filter type
  14755. * @param {String} type
  14756. * @default
  14757. */
  14758. type: 'Invert',
  14759. /**
  14760. * Applies filter to canvas element
  14761. * @memberOf fabric.Image.filters.Invert.prototype
  14762. * @param {Object} canvasEl Canvas element to apply filter to
  14763. */
  14764. applyTo: function(canvasEl) {
  14765. var context = canvasEl.getContext('2d'),
  14766. imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
  14767. data = imageData.data,
  14768. iLen = data.length, i;
  14769. for (i = 0; i < iLen; i+=4) {
  14770. data[i] = 255 - data[i];
  14771. data[i + 1] = 255 - data[i + 1];
  14772. data[i + 2] = 255 - data[i + 2];
  14773. }
  14774. context.putImageData(imageData, 0, 0);
  14775. }
  14776. });
  14777. /**
  14778. * Returns filter instance from an object representation
  14779. * @static
  14780. * @return {fabric.Image.filters.Invert} Instance of fabric.Image.filters.Invert
  14781. */
  14782. fabric.Image.filters.Invert.fromObject = function() {
  14783. return new fabric.Image.filters.Invert();
  14784. };
  14785. })(typeof exports !== 'undefined' ? exports : this);
  14786. (function(global) {
  14787. 'use strict';
  14788. var fabric = global.fabric || (global.fabric = { }),
  14789. extend = fabric.util.object.extend;
  14790. /**
  14791. * Mask filter class
  14792. * See http://resources.aleph-1.com/mask/
  14793. * @class fabric.Image.filters.Mask
  14794. * @memberOf fabric.Image.filters
  14795. * @extends fabric.Image.filters.BaseFilter
  14796. * @see {@link fabric.Image.filters.Mask#initialize} for constructor definition
  14797. */
  14798. fabric.Image.filters.Mask = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.Mask.prototype */ {
  14799. /**
  14800. * Filter type
  14801. * @param {String} type
  14802. * @default
  14803. */
  14804. type: 'Mask',
  14805. /**
  14806. * Constructor
  14807. * @memberOf fabric.Image.filters.Mask.prototype
  14808. * @param {Object} [options] Options object
  14809. * @param {fabric.Image} [options.mask] Mask image object
  14810. * @param {Number} [options.channel=0] Rgb channel (0, 1, 2 or 3)
  14811. */
  14812. initialize: function(options) {
  14813. options = options || { };
  14814. this.mask = options.mask;
  14815. this.channel = [ 0, 1, 2, 3 ].indexOf(options.channel) > -1 ? options.channel : 0;
  14816. },
  14817. /**
  14818. * Applies filter to canvas element
  14819. * @param {Object} canvasEl Canvas element to apply filter to
  14820. */
  14821. applyTo: function(canvasEl) {
  14822. if (!this.mask) {
  14823. return;
  14824. }
  14825. var context = canvasEl.getContext('2d'),
  14826. imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
  14827. data = imageData.data,
  14828. maskEl = this.mask.getElement(),
  14829. maskCanvasEl = fabric.util.createCanvasElement(),
  14830. channel = this.channel,
  14831. i,
  14832. iLen = imageData.width * imageData.height * 4;
  14833. maskCanvasEl.width = maskEl.width;
  14834. maskCanvasEl.height = maskEl.height;
  14835. maskCanvasEl.getContext('2d').drawImage(maskEl, 0, 0, maskEl.width, maskEl.height);
  14836. var maskImageData = maskCanvasEl.getContext('2d').getImageData(0, 0, maskEl.width, maskEl.height),
  14837. maskData = maskImageData.data;
  14838. for (i = 0; i < iLen; i += 4) {
  14839. data[i + 3] = maskData[i + channel];
  14840. }
  14841. context.putImageData(imageData, 0, 0);
  14842. },
  14843. /**
  14844. * Returns object representation of an instance
  14845. * @return {Object} Object representation of an instance
  14846. */
  14847. toObject: function() {
  14848. return extend(this.callSuper('toObject'), {
  14849. mask: this.mask.toObject(),
  14850. channel: this.channel
  14851. });
  14852. }
  14853. });
  14854. /**
  14855. * Returns filter instance from an object representation
  14856. * @static
  14857. * @param {Object} object Object to create an instance from
  14858. * @param {Function} [callback] Callback to invoke when a mask filter instance is created
  14859. */
  14860. fabric.Image.filters.Mask.fromObject = function(object, callback) {
  14861. fabric.util.loadImage(object.mask.src, function(img) {
  14862. object.mask = new fabric.Image(img, object.mask);
  14863. callback && callback(new fabric.Image.filters.Mask(object));
  14864. });
  14865. };
  14866. /**
  14867. * Indicates that instances of this type are async
  14868. * @static
  14869. * @type Boolean
  14870. * @default
  14871. */
  14872. fabric.Image.filters.Mask.async = true;
  14873. })(typeof exports !== 'undefined' ? exports : this);
  14874. (function(global) {
  14875. 'use strict';
  14876. var fabric = global.fabric || (global.fabric = { }),
  14877. extend = fabric.util.object.extend;
  14878. /**
  14879. * Noise filter class
  14880. * @class fabric.Image.filters.Noise
  14881. * @memberOf fabric.Image.filters
  14882. * @extends fabric.Image.filters.BaseFilter
  14883. * @see {@link fabric.Image.filters.Noise#initialize} for constructor definition
  14884. * @see {@link http://fabricjs.com/image-filters/|ImageFilters demo}
  14885. * @example
  14886. * var filter = new fabric.Image.filters.Noise({
  14887. * noise: 700
  14888. * });
  14889. * object.filters.push(filter);
  14890. * object.applyFilters(canvas.renderAll.bind(canvas));
  14891. */
  14892. fabric.Image.filters.Noise = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.Noise.prototype */ {
  14893. /**
  14894. * Filter type
  14895. * @param {String} type
  14896. * @default
  14897. */
  14898. type: 'Noise',
  14899. /**
  14900. * Constructor
  14901. * @memberOf fabric.Image.filters.Noise.prototype
  14902. * @param {Object} [options] Options object
  14903. * @param {Number} [options.noise=0] Noise value
  14904. */
  14905. initialize: function(options) {
  14906. options = options || { };
  14907. this.noise = options.noise || 0;
  14908. },
  14909. /**
  14910. * Applies filter to canvas element
  14911. * @param {Object} canvasEl Canvas element to apply filter to
  14912. */
  14913. applyTo: function(canvasEl) {
  14914. var context = canvasEl.getContext('2d'),
  14915. imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
  14916. data = imageData.data,
  14917. noise = this.noise, rand;
  14918. for (var i = 0, len = data.length; i < len; i += 4) {
  14919. rand = (0.5 - Math.random()) * noise;
  14920. data[i] += rand;
  14921. data[i + 1] += rand;
  14922. data[i + 2] += rand;
  14923. }
  14924. context.putImageData(imageData, 0, 0);
  14925. },
  14926. /**
  14927. * Returns object representation of an instance
  14928. * @return {Object} Object representation of an instance
  14929. */
  14930. toObject: function() {
  14931. return extend(this.callSuper('toObject'), {
  14932. noise: this.noise
  14933. });
  14934. }
  14935. });
  14936. /**
  14937. * Returns filter instance from an object representation
  14938. * @static
  14939. * @param {Object} object Object to create an instance from
  14940. * @return {fabric.Image.filters.Noise} Instance of fabric.Image.filters.Noise
  14941. */
  14942. fabric.Image.filters.Noise.fromObject = function(object) {
  14943. return new fabric.Image.filters.Noise(object);
  14944. };
  14945. })(typeof exports !== 'undefined' ? exports : this);
  14946. (function(global) {
  14947. 'use strict';
  14948. var fabric = global.fabric || (global.fabric = { }),
  14949. extend = fabric.util.object.extend;
  14950. /**
  14951. * Pixelate filter class
  14952. * @class fabric.Image.filters.Pixelate
  14953. * @memberOf fabric.Image.filters
  14954. * @extends fabric.Image.filters.BaseFilter
  14955. * @see {@link fabric.Image.filters.Pixelate#initialize} for constructor definition
  14956. * @see {@link http://fabricjs.com/image-filters/|ImageFilters demo}
  14957. * @example
  14958. * var filter = new fabric.Image.filters.Pixelate({
  14959. * blocksize: 8
  14960. * });
  14961. * object.filters.push(filter);
  14962. * object.applyFilters(canvas.renderAll.bind(canvas));
  14963. */
  14964. fabric.Image.filters.Pixelate = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.Pixelate.prototype */ {
  14965. /**
  14966. * Filter type
  14967. * @param {String} type
  14968. * @default
  14969. */
  14970. type: 'Pixelate',
  14971. /**
  14972. * Constructor
  14973. * @memberOf fabric.Image.filters.Pixelate.prototype
  14974. * @param {Object} [options] Options object
  14975. * @param {Number} [options.blocksize=4] Blocksize for pixelate
  14976. */
  14977. initialize: function(options) {
  14978. options = options || { };
  14979. this.blocksize = options.blocksize || 4;
  14980. },
  14981. /**
  14982. * Applies filter to canvas element
  14983. * @param {Object} canvasEl Canvas element to apply filter to
  14984. */
  14985. applyTo: function(canvasEl) {
  14986. var context = canvasEl.getContext('2d'),
  14987. imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
  14988. data = imageData.data,
  14989. iLen = imageData.height,
  14990. jLen = imageData.width,
  14991. index, i, j, r, g, b, a;
  14992. for (i = 0; i < iLen; i += this.blocksize) {
  14993. for (j = 0; j < jLen; j += this.blocksize) {
  14994. index = (i * 4) * jLen + (j * 4);
  14995. r = data[index];
  14996. g = data[index + 1];
  14997. b = data[index + 2];
  14998. a = data[index + 3];
  14999. /*
  15000. blocksize: 4
  15001. [1,x,x,x,1]
  15002. [x,x,x,x,1]
  15003. [x,x,x,x,1]
  15004. [x,x,x,x,1]
  15005. [1,1,1,1,1]
  15006. */
  15007. for (var _i = i, _ilen = i + this.blocksize; _i < _ilen; _i++) {
  15008. for (var _j = j, _jlen = j + this.blocksize; _j < _jlen; _j++) {
  15009. index = (_i * 4) * jLen + (_j * 4);
  15010. data[index] = r;
  15011. data[index + 1] = g;
  15012. data[index + 2] = b;
  15013. data[index + 3] = a;
  15014. }
  15015. }
  15016. }
  15017. }
  15018. context.putImageData(imageData, 0, 0);
  15019. },
  15020. /**
  15021. * Returns object representation of an instance
  15022. * @return {Object} Object representation of an instance
  15023. */
  15024. toObject: function() {
  15025. return extend(this.callSuper('toObject'), {
  15026. blocksize: this.blocksize
  15027. });
  15028. }
  15029. });
  15030. /**
  15031. * Returns filter instance from an object representation
  15032. * @static
  15033. * @param {Object} object Object to create an instance from
  15034. * @return {fabric.Image.filters.Pixelate} Instance of fabric.Image.filters.Pixelate
  15035. */
  15036. fabric.Image.filters.Pixelate.fromObject = function(object) {
  15037. return new fabric.Image.filters.Pixelate(object);
  15038. };
  15039. })(typeof exports !== 'undefined' ? exports : this);
  15040. (function(global) {
  15041. 'use strict';
  15042. var fabric = global.fabric || (global.fabric = { }),
  15043. extend = fabric.util.object.extend;
  15044. /**
  15045. * Remove white filter class
  15046. * @class fabric.Image.filters.RemoveWhite
  15047. * @memberOf fabric.Image.filters
  15048. * @extends fabric.Image.filters.BaseFilter
  15049. * @see {@link fabric.Image.filters.RemoveWhite#initialize} for constructor definition
  15050. * @see {@link http://fabricjs.com/image-filters/|ImageFilters demo}
  15051. * @example
  15052. * var filter = new fabric.Image.filters.RemoveWhite({
  15053. * threshold: 40,
  15054. * distance: 140
  15055. * });
  15056. * object.filters.push(filter);
  15057. * object.applyFilters(canvas.renderAll.bind(canvas));
  15058. */
  15059. fabric.Image.filters.RemoveWhite = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.RemoveWhite.prototype */ {
  15060. /**
  15061. * Filter type
  15062. * @param {String} type
  15063. * @default
  15064. */
  15065. type: 'RemoveWhite',
  15066. /**
  15067. * Constructor
  15068. * @memberOf fabric.Image.filters.RemoveWhite.prototype
  15069. * @param {Object} [options] Options object
  15070. * @param {Number} [options.threshold=30] Threshold value
  15071. * @param {Number} [options.distance=20] Distance value
  15072. */
  15073. initialize: function(options) {
  15074. options = options || { };
  15075. this.threshold = options.threshold || 30;
  15076. this.distance = options.distance || 20;
  15077. },
  15078. /**
  15079. * Applies filter to canvas element
  15080. * @param {Object} canvasEl Canvas element to apply filter to
  15081. */
  15082. applyTo: function(canvasEl) {
  15083. var context = canvasEl.getContext('2d'),
  15084. imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
  15085. data = imageData.data,
  15086. threshold = this.threshold,
  15087. distance = this.distance,
  15088. limit = 255 - threshold,
  15089. abs = Math.abs,
  15090. r, g, b;
  15091. for (var i = 0, len = data.length; i < len; i += 4) {
  15092. r = data[i];
  15093. g = data[i + 1];
  15094. b = data[i + 2];
  15095. if (r > limit &&
  15096. g > limit &&
  15097. b > limit &&
  15098. abs(r - g) < distance &&
  15099. abs(r - b) < distance &&
  15100. abs(g - b) < distance
  15101. ) {
  15102. data[i + 3] = 1;
  15103. }
  15104. }
  15105. context.putImageData(imageData, 0, 0);
  15106. },
  15107. /**
  15108. * Returns object representation of an instance
  15109. * @return {Object} Object representation of an instance
  15110. */
  15111. toObject: function() {
  15112. return extend(this.callSuper('toObject'), {
  15113. threshold: this.threshold,
  15114. distance: this.distance
  15115. });
  15116. }
  15117. });
  15118. /**
  15119. * Returns filter instance from an object representation
  15120. * @static
  15121. * @param {Object} object Object to create an instance from
  15122. * @return {fabric.Image.filters.RemoveWhite} Instance of fabric.Image.filters.RemoveWhite
  15123. */
  15124. fabric.Image.filters.RemoveWhite.fromObject = function(object) {
  15125. return new fabric.Image.filters.RemoveWhite(object);
  15126. };
  15127. })(typeof exports !== 'undefined' ? exports : this);
  15128. (function(global) {
  15129. 'use strict';
  15130. var fabric = global.fabric || (global.fabric = { });
  15131. /**
  15132. * Sepia filter class
  15133. * @class fabric.Image.filters.Sepia
  15134. * @memberOf fabric.Image.filters
  15135. * @extends fabric.Image.filters.BaseFilter
  15136. * @see {@link http://fabricjs.com/image-filters/|ImageFilters demo}
  15137. * @example
  15138. * var filter = new fabric.Image.filters.Sepia();
  15139. * object.filters.push(filter);
  15140. * object.applyFilters(canvas.renderAll.bind(canvas));
  15141. */
  15142. fabric.Image.filters.Sepia = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.Sepia.prototype */ {
  15143. /**
  15144. * Filter type
  15145. * @param {String} type
  15146. * @default
  15147. */
  15148. type: 'Sepia',
  15149. /**
  15150. * Applies filter to canvas element
  15151. * @memberOf fabric.Image.filters.Sepia.prototype
  15152. * @param {Object} canvasEl Canvas element to apply filter to
  15153. */
  15154. applyTo: function(canvasEl) {
  15155. var context = canvasEl.getContext('2d'),
  15156. imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
  15157. data = imageData.data,
  15158. iLen = data.length, i, avg;
  15159. for (i = 0; i < iLen; i+=4) {
  15160. avg = 0.3 * data[i] + 0.59 * data[i + 1] + 0.11 * data[i + 2];
  15161. data[i] = avg + 100;
  15162. data[i + 1] = avg + 50;
  15163. data[i + 2] = avg + 255;
  15164. }
  15165. context.putImageData(imageData, 0, 0);
  15166. }
  15167. });
  15168. /**
  15169. * Returns filter instance from an object representation
  15170. * @static
  15171. * @return {fabric.Image.filters.Sepia} Instance of fabric.Image.filters.Sepia
  15172. */
  15173. fabric.Image.filters.Sepia.fromObject = function() {
  15174. return new fabric.Image.filters.Sepia();
  15175. };
  15176. })(typeof exports !== 'undefined' ? exports : this);
  15177. (function(global) {
  15178. 'use strict';
  15179. var fabric = global.fabric || (global.fabric = { });
  15180. /**
  15181. * Sepia2 filter class
  15182. * @class fabric.Image.filters.Sepia2
  15183. * @memberOf fabric.Image.filters
  15184. * @extends fabric.Image.filters.BaseFilter
  15185. * @see {@link http://fabricjs.com/image-filters/|ImageFilters demo}
  15186. * @example
  15187. * var filter = new fabric.Image.filters.Sepia2();
  15188. * object.filters.push(filter);
  15189. * object.applyFilters(canvas.renderAll.bind(canvas));
  15190. */
  15191. fabric.Image.filters.Sepia2 = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.Sepia2.prototype */ {
  15192. /**
  15193. * Filter type
  15194. * @param {String} type
  15195. * @default
  15196. */
  15197. type: 'Sepia2',
  15198. /**
  15199. * Applies filter to canvas element
  15200. * @memberOf fabric.Image.filters.Sepia.prototype
  15201. * @param {Object} canvasEl Canvas element to apply filter to
  15202. */
  15203. applyTo: function(canvasEl) {
  15204. var context = canvasEl.getContext('2d'),
  15205. imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
  15206. data = imageData.data,
  15207. iLen = data.length, i, r, g, b;
  15208. for (i = 0; i < iLen; i+=4) {
  15209. r = data[i];
  15210. g = data[i + 1];
  15211. b = data[i + 2];
  15212. data[i] = (r * 0.393 + g * 0.769 + b * 0.189 ) / 1.351;
  15213. data[i + 1] = (r * 0.349 + g * 0.686 + b * 0.168 ) / 1.203;
  15214. data[i + 2] = (r * 0.272 + g * 0.534 + b * 0.131 ) / 2.140;
  15215. }
  15216. context.putImageData(imageData, 0, 0);
  15217. }
  15218. });
  15219. /**
  15220. * Returns filter instance from an object representation
  15221. * @static
  15222. * @return {fabric.Image.filters.Sepia2} Instance of fabric.Image.filters.Sepia2
  15223. */
  15224. fabric.Image.filters.Sepia2.fromObject = function() {
  15225. return new fabric.Image.filters.Sepia2();
  15226. };
  15227. })(typeof exports !== 'undefined' ? exports : this);
  15228. (function(global) {
  15229. 'use strict';
  15230. var fabric = global.fabric || (global.fabric = { }),
  15231. extend = fabric.util.object.extend;
  15232. /**
  15233. * Tint filter class
  15234. * Adapted from <a href="https://github.com/mezzoblue/PaintbrushJS">https://github.com/mezzoblue/PaintbrushJS</a>
  15235. * @class fabric.Image.filters.Tint
  15236. * @memberOf fabric.Image.filters
  15237. * @extends fabric.Image.filters.BaseFilter
  15238. * @see {@link fabric.Image.filters.Tint#initialize} for constructor definition
  15239. * @see {@link http://fabricjs.com/image-filters/|ImageFilters demo}
  15240. * @example <caption>Tint filter with hex color and opacity</caption>
  15241. * var filter = new fabric.Image.filters.Tint({
  15242. * color: '#3513B0',
  15243. * opacity: 0.5
  15244. * });
  15245. * object.filters.push(filter);
  15246. * object.applyFilters(canvas.renderAll.bind(canvas));
  15247. * @example <caption>Tint filter with rgba color</caption>
  15248. * var filter = new fabric.Image.filters.Tint({
  15249. * color: 'rgba(53, 21, 176, 0.5)'
  15250. * });
  15251. * object.filters.push(filter);
  15252. * object.applyFilters(canvas.renderAll.bind(canvas));
  15253. */
  15254. fabric.Image.filters.Tint = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.Tint.prototype */ {
  15255. /**
  15256. * Filter type
  15257. * @param {String} type
  15258. * @default
  15259. */
  15260. type: 'Tint',
  15261. /**
  15262. * Constructor
  15263. * @memberOf fabric.Image.filters.Tint.prototype
  15264. * @param {Object} [options] Options object
  15265. * @param {String} [options.color=#000000] Color to tint the image with
  15266. * @param {Number} [options.opacity] Opacity value that controls the tint effect's transparency (0..1)
  15267. */
  15268. initialize: function(options) {
  15269. options = options || { };
  15270. this.color = options.color || '#000000';
  15271. this.opacity = typeof options.opacity !== 'undefined'
  15272. ? options.opacity
  15273. : new fabric.Color(this.color).getAlpha();
  15274. },
  15275. /**
  15276. * Applies filter to canvas element
  15277. * @param {Object} canvasEl Canvas element to apply filter to
  15278. */
  15279. applyTo: function(canvasEl) {
  15280. var context = canvasEl.getContext('2d'),
  15281. imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
  15282. data = imageData.data,
  15283. iLen = data.length, i,
  15284. tintR, tintG, tintB,
  15285. r, g, b, alpha1,
  15286. source;
  15287. source = new fabric.Color(this.color).getSource();
  15288. tintR = source[0] * this.opacity;
  15289. tintG = source[1] * this.opacity;
  15290. tintB = source[2] * this.opacity;
  15291. alpha1 = 1 - this.opacity;
  15292. for (i = 0; i < iLen; i+=4) {
  15293. r = data[i];
  15294. g = data[i + 1];
  15295. b = data[i + 2];
  15296. // alpha compositing
  15297. data[i] = tintR + r * alpha1;
  15298. data[i + 1] = tintG + g * alpha1;
  15299. data[i + 2] = tintB + b * alpha1;
  15300. }
  15301. context.putImageData(imageData, 0, 0);
  15302. },
  15303. /**
  15304. * Returns object representation of an instance
  15305. * @return {Object} Object representation of an instance
  15306. */
  15307. toObject: function() {
  15308. return extend(this.callSuper('toObject'), {
  15309. color: this.color,
  15310. opacity: this.opacity
  15311. });
  15312. }
  15313. });
  15314. /**
  15315. * Returns filter instance from an object representation
  15316. * @static
  15317. * @param {Object} object Object to create an instance from
  15318. * @return {fabric.Image.filters.Tint} Instance of fabric.Image.filters.Tint
  15319. */
  15320. fabric.Image.filters.Tint.fromObject = function(object) {
  15321. return new fabric.Image.filters.Tint(object);
  15322. };
  15323. })(typeof exports !== 'undefined' ? exports : this);
  15324. (function(global) {
  15325. 'use strict';
  15326. var fabric = global.fabric || (global.fabric = { }),
  15327. extend = fabric.util.object.extend;
  15328. /**
  15329. * Multiply filter class
  15330. * Adapted from <a href="http://www.laurenscorijn.com/articles/colormath-basics">http://www.laurenscorijn.com/articles/colormath-basics</a>
  15331. * @class fabric.Image.filters.Multiply
  15332. * @memberOf fabric.Image.filters
  15333. * @extends fabric.Image.filters.BaseFilter
  15334. * @example <caption>Multiply filter with hex color</caption>
  15335. * var filter = new fabric.Image.filters.Multiply({
  15336. * color: '#F0F'
  15337. * });
  15338. * object.filters.push(filter);
  15339. * object.applyFilters(canvas.renderAll.bind(canvas));
  15340. * @example <caption>Multiply filter with rgb color</caption>
  15341. * var filter = new fabric.Image.filters.Multiply({
  15342. * color: 'rgb(53, 21, 176)'
  15343. * });
  15344. * object.filters.push(filter);
  15345. * object.applyFilters(canvas.renderAll.bind(canvas));
  15346. */
  15347. fabric.Image.filters.Multiply = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.Multiply.prototype */ {
  15348. /**
  15349. * Filter type
  15350. * @param {String} type
  15351. * @default
  15352. */
  15353. type: 'Multiply',
  15354. /**
  15355. * Constructor
  15356. * @memberOf fabric.Image.filters.Multiply.prototype
  15357. * @param {Object} [options] Options object
  15358. * @param {String} [options.color=#000000] Color to multiply the image pixels with
  15359. */
  15360. initialize: function(options) {
  15361. options = options || { };
  15362. this.color = options.color || '#000000';
  15363. },
  15364. /**
  15365. * Applies filter to canvas element
  15366. * @param {Object} canvasEl Canvas element to apply filter to
  15367. */
  15368. applyTo: function(canvasEl) {
  15369. var context = canvasEl.getContext('2d'),
  15370. imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
  15371. data = imageData.data,
  15372. iLen = data.length, i,
  15373. source;
  15374. source = new fabric.Color(this.color).getSource();
  15375. for (i = 0; i < iLen; i+=4) {
  15376. data[i] *= source[0] / 255;
  15377. data[i + 1] *= source[1] / 255;
  15378. data[i + 2] *= source[2] / 255;
  15379. }
  15380. context.putImageData(imageData, 0, 0);
  15381. },
  15382. /**
  15383. * Returns object representation of an instance
  15384. * @return {Object} Object representation of an instance
  15385. */
  15386. toObject: function() {
  15387. return extend(this.callSuper('toObject'), {
  15388. color: this.color
  15389. });
  15390. }
  15391. });
  15392. /**
  15393. * Returns filter instance from an object representation
  15394. * @static
  15395. * @param {Object} object Object to create an instance from
  15396. * @return {fabric.Image.filters.Multiply} Instance of fabric.Image.filters.Multiply
  15397. */
  15398. fabric.Image.filters.Multiply.fromObject = function(object) {
  15399. return new fabric.Image.filters.Multiply(object);
  15400. };
  15401. })(typeof exports !== 'undefined' ? exports : this);
  15402. (function(global){
  15403. 'use strict';
  15404. var fabric = global.fabric;
  15405. /**
  15406. * Color Blend filter class
  15407. * @class fabric.Image.filter.Blend
  15408. * @memberOf fabric.Image.filters
  15409. * @extends fabric.Image.filters.BaseFilter
  15410. * @example
  15411. * var filter = new fabric.Image.filters.Blend({
  15412. * color: '#000',
  15413. * mode: 'multiply'
  15414. * });
  15415. *
  15416. * var filter = new fabric.Image.filters.Blend({
  15417. * image: fabricImageObject,
  15418. * mode: 'multiply',
  15419. * alpha: 0.5
  15420. * });
  15421. * object.filters.push(filter);
  15422. * object.applyFilters(canvas.renderAll.bind(canvas));
  15423. */
  15424. fabric.Image.filters.Blend = fabric.util.createClass({
  15425. type: 'Blend',
  15426. initialize: function(options){
  15427. options = options || {};
  15428. this.color = options.color || '#000';
  15429. this.image = options.image || false;
  15430. this.mode = options.mode || 'multiply';
  15431. this.alpha = options.alpha || 1;
  15432. },
  15433. applyTo: function(canvasEl) {
  15434. var context = canvasEl.getContext('2d'),
  15435. imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
  15436. data = imageData.data,
  15437. tr, tg, tb,
  15438. r, g, b,
  15439. source,
  15440. isImage = false;
  15441. if (this.image) {
  15442. // Blend images
  15443. isImage = true;
  15444. var _el = fabric.util.createCanvasElement();
  15445. _el.width = this.image.width;
  15446. _el.height = this.image.height;
  15447. var tmpCanvas = new fabric.StaticCanvas(_el);
  15448. tmpCanvas.add(this.image);
  15449. var context2 = tmpCanvas.getContext('2d');
  15450. source = context2.getImageData(0, 0, tmpCanvas.width, tmpCanvas.height).data;
  15451. }
  15452. else {
  15453. // Blend color
  15454. source = new fabric.Color(this.color).getSource();
  15455. tr = source[0] * this.alpha;
  15456. tg = source[1] * this.alpha;
  15457. tb = source[2] * this.alpha;
  15458. }
  15459. for (var i = 0, len = data.length; i < len; i += 4) {
  15460. r = data[i];
  15461. g = data[i + 1];
  15462. b = data[i + 2];
  15463. if (isImage) {
  15464. tr = source[i] * this.alpha;
  15465. tg = source[i + 1] * this.alpha;
  15466. tb = source[i + 2] * this.alpha;
  15467. }
  15468. switch (this.mode) {
  15469. case 'multiply':
  15470. data[i] = r * tr / 255;
  15471. data[i + 1] = g * tg / 255;
  15472. data[i + 2] = b * tb / 255;
  15473. break;
  15474. case 'screen':
  15475. data[i] = 1 - (1 - r) * (1 - tr);
  15476. data[i + 1] = 1 - (1 - g) * (1 - tg);
  15477. data[i + 2] = 1 - (1 - b) * (1 - tb);
  15478. break;
  15479. case 'add':
  15480. data[i] = Math.min(255, r + tr);
  15481. data[i + 1] = Math.min(255, g + tg);
  15482. data[i + 2] = Math.min(255, b + tb);
  15483. break;
  15484. case 'diff':
  15485. case 'difference':
  15486. data[i] = Math.abs(r - tr);
  15487. data[i + 1] = Math.abs(g - tg);
  15488. data[i + 2] = Math.abs(b - tb);
  15489. break;
  15490. case 'subtract':
  15491. var _r = r - tr,
  15492. _g = g - tg,
  15493. _b = b - tb;
  15494. data[i] = (_r < 0) ? 0 : _r;
  15495. data[i + 1] = (_g < 0) ? 0 : _g;
  15496. data[i + 2] = (_b < 0) ? 0 : _b;
  15497. break;
  15498. case 'darken':
  15499. data[i] = Math.min(r, tr);
  15500. data[i + 1] = Math.min(g, tg);
  15501. data[i + 2] = Math.min(b, tb);
  15502. break;
  15503. case 'lighten':
  15504. data[i] = Math.max(r, tr);
  15505. data[i + 1] = Math.max(g, tg);
  15506. data[i + 2] = Math.max(b, tb);
  15507. break;
  15508. }
  15509. }
  15510. context.putImageData(imageData, 0, 0);
  15511. }
  15512. });
  15513. fabric.Image.filters.Blend.fromObject = function(object) {
  15514. return new fabric.Image.filters.Blend(object);
  15515. };
  15516. })(typeof exports !== 'undefined' ? exports : this);
  15517. (function(global) {
  15518. 'use strict';
  15519. var fabric = global.fabric || (global.fabric = { }),
  15520. extend = fabric.util.object.extend,
  15521. clone = fabric.util.object.clone,
  15522. toFixed = fabric.util.toFixed,
  15523. supportsLineDash = fabric.StaticCanvas.supports('setLineDash');
  15524. if (fabric.Text) {
  15525. fabric.warn('fabric.Text is already defined');
  15526. return;
  15527. }
  15528. var stateProperties = fabric.Object.prototype.stateProperties.concat();
  15529. stateProperties.push(
  15530. 'fontFamily',
  15531. 'fontWeight',
  15532. 'fontSize',
  15533. 'text',
  15534. 'textDecoration',
  15535. 'textAlign',
  15536. 'fontStyle',
  15537. 'lineHeight',
  15538. 'textBackgroundColor',
  15539. 'useNative',
  15540. 'path'
  15541. );
  15542. /**
  15543. * Text class
  15544. * @class fabric.Text
  15545. * @extends fabric.Object
  15546. * @return {fabric.Text} thisArg
  15547. * @tutorial {@link http://fabricjs.com/fabric-intro-part-2/#text}
  15548. * @see {@link fabric.Text#initialize} for constructor definition
  15549. */
  15550. fabric.Text = fabric.util.createClass(fabric.Object, /** @lends fabric.Text.prototype */ {
  15551. /**
  15552. * Properties which when set cause object to change dimensions
  15553. * @type Object
  15554. * @private
  15555. */
  15556. _dimensionAffectingProps: {
  15557. fontSize: true,
  15558. fontWeight: true,
  15559. fontFamily: true,
  15560. textDecoration: true,
  15561. fontStyle: true,
  15562. lineHeight: true,
  15563. stroke: true,
  15564. strokeWidth: true,
  15565. text: true
  15566. },
  15567. /**
  15568. * @private
  15569. */
  15570. _reNewline: /\r?\n/,
  15571. /**
  15572. * Retrieves object's fontSize
  15573. * @method getFontSize
  15574. * @memberOf fabric.Text.prototype
  15575. * @return {String} Font size (in pixels)
  15576. */
  15577. /**
  15578. * Sets object's fontSize
  15579. * @method setFontSize
  15580. * @memberOf fabric.Text.prototype
  15581. * @param {Number} fontSize Font size (in pixels)
  15582. * @return {fabric.Text}
  15583. * @chainable
  15584. */
  15585. /**
  15586. * Retrieves object's fontWeight
  15587. * @method getFontWeight
  15588. * @memberOf fabric.Text.prototype
  15589. * @return {(String|Number)} Font weight
  15590. */
  15591. /**
  15592. * Sets object's fontWeight
  15593. * @method setFontWeight
  15594. * @memberOf fabric.Text.prototype
  15595. * @param {(Number|String)} fontWeight Font weight
  15596. * @return {fabric.Text}
  15597. * @chainable
  15598. */
  15599. /**
  15600. * Retrieves object's fontFamily
  15601. * @method getFontFamily
  15602. * @memberOf fabric.Text.prototype
  15603. * @return {String} Font family
  15604. */
  15605. /**
  15606. * Sets object's fontFamily
  15607. * @method setFontFamily
  15608. * @memberOf fabric.Text.prototype
  15609. * @param {String} fontFamily Font family
  15610. * @return {fabric.Text}
  15611. * @chainable
  15612. */
  15613. /**
  15614. * Retrieves object's text
  15615. * @method getText
  15616. * @memberOf fabric.Text.prototype
  15617. * @return {String} text
  15618. */
  15619. /**
  15620. * Sets object's text
  15621. * @method setText
  15622. * @memberOf fabric.Text.prototype
  15623. * @param {String} text Text
  15624. * @return {fabric.Text}
  15625. * @chainable
  15626. */
  15627. /**
  15628. * Retrieves object's textDecoration
  15629. * @method getTextDecoration
  15630. * @memberOf fabric.Text.prototype
  15631. * @return {String} Text decoration
  15632. */
  15633. /**
  15634. * Sets object's textDecoration
  15635. * @method setTextDecoration
  15636. * @memberOf fabric.Text.prototype
  15637. * @param {String} textDecoration Text decoration
  15638. * @return {fabric.Text}
  15639. * @chainable
  15640. */
  15641. /**
  15642. * Retrieves object's fontStyle
  15643. * @method getFontStyle
  15644. * @memberOf fabric.Text.prototype
  15645. * @return {String} Font style
  15646. */
  15647. /**
  15648. * Sets object's fontStyle
  15649. * @method setFontStyle
  15650. * @memberOf fabric.Text.prototype
  15651. * @param {String} fontStyle Font style
  15652. * @return {fabric.Text}
  15653. * @chainable
  15654. */
  15655. /**
  15656. * Retrieves object's lineHeight
  15657. * @method getLineHeight
  15658. * @memberOf fabric.Text.prototype
  15659. * @return {Number} Line height
  15660. */
  15661. /**
  15662. * Sets object's lineHeight
  15663. * @method setLineHeight
  15664. * @memberOf fabric.Text.prototype
  15665. * @param {Number} lineHeight Line height
  15666. * @return {fabric.Text}
  15667. * @chainable
  15668. */
  15669. /**
  15670. * Retrieves object's textAlign
  15671. * @method getTextAlign
  15672. * @memberOf fabric.Text.prototype
  15673. * @return {String} Text alignment
  15674. */
  15675. /**
  15676. * Sets object's textAlign
  15677. * @method setTextAlign
  15678. * @memberOf fabric.Text.prototype
  15679. * @param {String} textAlign Text alignment
  15680. * @return {fabric.Text}
  15681. * @chainable
  15682. */
  15683. /**
  15684. * Retrieves object's textBackgroundColor
  15685. * @method getTextBackgroundColor
  15686. * @memberOf fabric.Text.prototype
  15687. * @return {String} Text background color
  15688. */
  15689. /**
  15690. * Sets object's textBackgroundColor
  15691. * @method setTextBackgroundColor
  15692. * @memberOf fabric.Text.prototype
  15693. * @param {String} textBackgroundColor Text background color
  15694. * @return {fabric.Text}
  15695. * @chainable
  15696. */
  15697. /**
  15698. * Type of an object
  15699. * @type String
  15700. * @default
  15701. */
  15702. type: 'text',
  15703. /**
  15704. * Font size (in pixels)
  15705. * @type Number
  15706. * @default
  15707. */
  15708. fontSize: 40,
  15709. /**
  15710. * Font weight (e.g. bold, normal, 400, 600, 800)
  15711. * @type {(Number|String)}
  15712. * @default
  15713. */
  15714. fontWeight: 'normal',
  15715. /**
  15716. * Font family
  15717. * @type String
  15718. * @default
  15719. */
  15720. fontFamily: 'Times New Roman',
  15721. /**
  15722. * Text decoration Possible values: "", "underline", "overline" or "line-through".
  15723. * @type String
  15724. * @default
  15725. */
  15726. textDecoration: '',
  15727. /**
  15728. * Text alignment. Possible values: "left", "center", or "right".
  15729. * @type String
  15730. * @default
  15731. */
  15732. textAlign: 'left',
  15733. /**
  15734. * Font style . Possible values: "", "normal", "italic" or "oblique".
  15735. * @type String
  15736. * @default
  15737. */
  15738. fontStyle: '',
  15739. /**
  15740. * Line height
  15741. * @type Number
  15742. * @default
  15743. */
  15744. lineHeight: 1.3,
  15745. /**
  15746. * Background color of text lines
  15747. * @type String
  15748. * @default
  15749. */
  15750. textBackgroundColor: '',
  15751. /**
  15752. * URL of a font file, when using Cufon
  15753. * @type String | null
  15754. * @default
  15755. */
  15756. path: null,
  15757. /**
  15758. * Indicates whether canvas native text methods should be used to render text (otherwise, Cufon is used)
  15759. * @type Boolean
  15760. * @default
  15761. */
  15762. useNative: true,
  15763. /**
  15764. * List of properties to consider when checking if
  15765. * state of an object is changed ({@link fabric.Object#hasStateChanged})
  15766. * as well as for history (undo/redo) purposes
  15767. * @type Array
  15768. */
  15769. stateProperties: stateProperties,
  15770. /**
  15771. * When defined, an object is rendered via stroke and this property specifies its color.
  15772. * <b>Backwards incompatibility note:</b> This property was named "strokeStyle" until v1.1.6
  15773. * @type String
  15774. * @default
  15775. */
  15776. stroke: null,
  15777. /**
  15778. * Shadow object representing shadow of this shape.
  15779. * <b>Backwards incompatibility note:</b> This property was named "textShadow" (String) until v1.2.11
  15780. * @type fabric.Shadow
  15781. * @default
  15782. */
  15783. shadow: null,
  15784. /**
  15785. * Constructor
  15786. * @param {String} text Text string
  15787. * @param {Object} [options] Options object
  15788. * @return {fabric.Text} thisArg
  15789. */
  15790. initialize: function(text, options) {
  15791. options = options || { };
  15792. this.text = text;
  15793. this.__skipDimension = true;
  15794. this.setOptions(options);
  15795. this.__skipDimension = false;
  15796. this._initDimensions();
  15797. },
  15798. /**
  15799. * Renders text object on offscreen canvas, so that it would get dimensions
  15800. * @private
  15801. */
  15802. _initDimensions: function() {
  15803. if (this.__skipDimension) {
  15804. return;
  15805. }
  15806. var canvasEl = fabric.util.createCanvasElement();
  15807. this._render(canvasEl.getContext('2d'));
  15808. },
  15809. /**
  15810. * Returns string representation of an instance
  15811. * @return {String} String representation of text object
  15812. */
  15813. toString: function() {
  15814. return '#<fabric.Text (' + this.complexity() +
  15815. '): { "text": "' + this.text + '", "fontFamily": "' + this.fontFamily + '" }>';
  15816. },
  15817. /**
  15818. * @private
  15819. * @param {CanvasRenderingContext2D} ctx Context to render on
  15820. */
  15821. _render: function(ctx) {
  15822. if (typeof Cufon === 'undefined' || this.useNative === true) {
  15823. this._renderViaNative(ctx);
  15824. }
  15825. else {
  15826. this._renderViaCufon(ctx);
  15827. }
  15828. },
  15829. /**
  15830. * @private
  15831. * @param {CanvasRenderingContext2D} ctx Context to render on
  15832. */
  15833. _renderViaNative: function(ctx) {
  15834. var textLines = this.text.split(this._reNewline);
  15835. this._setTextStyles(ctx);
  15836. this.width = this._getTextWidth(ctx, textLines);
  15837. this.height = this._getTextHeight(ctx, textLines);
  15838. this.clipTo && fabric.util.clipContext(this, ctx);
  15839. this._renderTextBackground(ctx, textLines);
  15840. this._translateForTextAlign(ctx);
  15841. this._renderText(ctx, textLines);
  15842. if (this.textAlign !== 'left' && this.textAlign !== 'justify') {
  15843. ctx.restore();
  15844. }
  15845. this._renderTextDecoration(ctx, textLines);
  15846. this.clipTo && ctx.restore();
  15847. this._setBoundaries(ctx, textLines);
  15848. this._totalLineHeight = 0;
  15849. },
  15850. /**
  15851. * @private
  15852. * @param {CanvasRenderingContext2D} ctx Context to render on
  15853. */
  15854. _renderText: function(ctx, textLines) {
  15855. ctx.save();
  15856. this._setShadow(ctx);
  15857. this._setupFillRule(ctx);
  15858. this._renderTextFill(ctx, textLines);
  15859. this._renderTextStroke(ctx, textLines);
  15860. this._restoreFillRule(ctx);
  15861. this._removeShadow(ctx);
  15862. ctx.restore();
  15863. },
  15864. /**
  15865. * @private
  15866. * @param {CanvasRenderingContext2D} ctx Context to render on
  15867. */
  15868. _translateForTextAlign: function(ctx) {
  15869. if (this.textAlign !== 'left' && this.textAlign !== 'justify') {
  15870. ctx.save();
  15871. ctx.translate(this.textAlign === 'center' ? (this.width / 2) : this.width, 0);
  15872. }
  15873. },
  15874. /**
  15875. * @private
  15876. * @param {CanvasRenderingContext2D} ctx Context to render on
  15877. * @param {Array} textLines Array of all text lines
  15878. */
  15879. _setBoundaries: function(ctx, textLines) {
  15880. this._boundaries = [ ];
  15881. for (var i = 0, len = textLines.length; i < len; i++) {
  15882. var lineWidth = this._getLineWidth(ctx, textLines[i]),
  15883. lineLeftOffset = this._getLineLeftOffset(lineWidth);
  15884. this._boundaries.push({
  15885. height: this.fontSize * this.lineHeight,
  15886. width: lineWidth,
  15887. left: lineLeftOffset
  15888. });
  15889. }
  15890. },
  15891. /**
  15892. * @private
  15893. * @param {CanvasRenderingContext2D} ctx Context to render on
  15894. */
  15895. _setTextStyles: function(ctx) {
  15896. this._setFillStyles(ctx);
  15897. this._setStrokeStyles(ctx);
  15898. ctx.textBaseline = 'alphabetic';
  15899. if (!this.skipTextAlign) {
  15900. ctx.textAlign = this.textAlign;
  15901. }
  15902. ctx.font = this._getFontDeclaration();
  15903. },
  15904. /**
  15905. * @private
  15906. * @param {CanvasRenderingContext2D} ctx Context to render on
  15907. * @param {Array} textLines Array of all text lines
  15908. * @return {Number} Height of fabric.Text object
  15909. */
  15910. _getTextHeight: function(ctx, textLines) {
  15911. return this.fontSize * textLines.length * this.lineHeight;
  15912. },
  15913. /**
  15914. * @private
  15915. * @param {CanvasRenderingContext2D} ctx Context to render on
  15916. * @param {Array} textLines Array of all text lines
  15917. * @return {Number} Maximum width of fabric.Text object
  15918. */
  15919. _getTextWidth: function(ctx, textLines) {
  15920. var maxWidth = ctx.measureText(textLines[0] || '|').width;
  15921. for (var i = 1, len = textLines.length; i < len; i++) {
  15922. var currentLineWidth = ctx.measureText(textLines[i]).width;
  15923. if (currentLineWidth > maxWidth) {
  15924. maxWidth = currentLineWidth;
  15925. }
  15926. }
  15927. return maxWidth;
  15928. },
  15929. /**
  15930. * @private
  15931. * @param {String} method Method name ("fillText" or "strokeText")
  15932. * @param {CanvasRenderingContext2D} ctx Context to render on
  15933. * @param {String} chars Chars to render
  15934. * @param {Number} left Left position of text
  15935. * @param {Number} top Top position of text
  15936. */
  15937. _renderChars: function(method, ctx, chars, left, top) {
  15938. ctx[method](chars, left, top);
  15939. },
  15940. /**
  15941. * @private
  15942. * @param {String} method Method name ("fillText" or "strokeText")
  15943. * @param {CanvasRenderingContext2D} ctx Context to render on
  15944. * @param {String} line Text to render
  15945. * @param {Number} left Left position of text
  15946. * @param {Number} top Top position of text
  15947. * @param {Number} lineIndex Index of a line in a text
  15948. */
  15949. _renderTextLine: function(method, ctx, line, left, top, lineIndex) {
  15950. // lift the line by quarter of fontSize
  15951. top -= this.fontSize / 4;
  15952. // short-circuit
  15953. if (this.textAlign !== 'justify') {
  15954. this._renderChars(method, ctx, line, left, top, lineIndex);
  15955. return;
  15956. }
  15957. var lineWidth = ctx.measureText(line).width,
  15958. totalWidth = this.width;
  15959. if (totalWidth > lineWidth) {
  15960. // stretch the line
  15961. var words = line.split(/\s+/),
  15962. wordsWidth = ctx.measureText(line.replace(/\s+/g, '')).width,
  15963. widthDiff = totalWidth - wordsWidth,
  15964. numSpaces = words.length - 1,
  15965. spaceWidth = widthDiff / numSpaces,
  15966. leftOffset = 0;
  15967. for (var i = 0, len = words.length; i < len; i++) {
  15968. this._renderChars(method, ctx, words[i], left + leftOffset, top, lineIndex);
  15969. leftOffset += ctx.measureText(words[i]).width + spaceWidth;
  15970. }
  15971. }
  15972. else {
  15973. this._renderChars(method, ctx, line, left, top, lineIndex);
  15974. }
  15975. },
  15976. /**
  15977. * @private
  15978. * @return {Number} Left offset
  15979. */
  15980. _getLeftOffset: function() {
  15981. if (fabric.isLikelyNode) {
  15982. return 0;
  15983. }
  15984. return -this.width / 2;
  15985. },
  15986. /**
  15987. * @private
  15988. * @return {Number} Top offset
  15989. */
  15990. _getTopOffset: function() {
  15991. return -this.height / 2;
  15992. },
  15993. /**
  15994. * @private
  15995. * @param {CanvasRenderingContext2D} ctx Context to render on
  15996. * @param {Array} textLines Array of all text lines
  15997. */
  15998. _renderTextFill: function(ctx, textLines) {
  15999. if (!this.fill && !this._skipFillStrokeCheck) {
  16000. return;
  16001. }
  16002. this._boundaries = [ ];
  16003. var lineHeights = 0;
  16004. for (var i = 0, len = textLines.length; i < len; i++) {
  16005. var heightOfLine = this._getHeightOfLine(ctx, i, textLines);
  16006. lineHeights += heightOfLine;
  16007. this._renderTextLine(
  16008. 'fillText',
  16009. ctx,
  16010. textLines[i],
  16011. this._getLeftOffset(),
  16012. this._getTopOffset() + lineHeights,
  16013. i
  16014. );
  16015. }
  16016. },
  16017. /**
  16018. * @private
  16019. * @param {CanvasRenderingContext2D} ctx Context to render on
  16020. * @param {Array} textLines Array of all text lines
  16021. */
  16022. _renderTextStroke: function(ctx, textLines) {
  16023. if ((!this.stroke || this.strokeWidth === 0) && !this._skipFillStrokeCheck) {
  16024. return;
  16025. }
  16026. var lineHeights = 0;
  16027. ctx.save();
  16028. if (this.strokeDashArray) {
  16029. // Spec requires the concatenation of two copies the dash list when the number of elements is odd
  16030. if (1 & this.strokeDashArray.length) {
  16031. this.strokeDashArray.push.apply(this.strokeDashArray, this.strokeDashArray);
  16032. }
  16033. supportsLineDash && ctx.setLineDash(this.strokeDashArray);
  16034. }
  16035. ctx.beginPath();
  16036. for (var i = 0, len = textLines.length; i < len; i++) {
  16037. var heightOfLine = this._getHeightOfLine(ctx, i, textLines);
  16038. lineHeights += heightOfLine;
  16039. this._renderTextLine(
  16040. 'strokeText',
  16041. ctx,
  16042. textLines[i],
  16043. this._getLeftOffset(),
  16044. this._getTopOffset() + lineHeights,
  16045. i
  16046. );
  16047. }
  16048. ctx.closePath();
  16049. ctx.restore();
  16050. },
  16051. _getHeightOfLine: function() {
  16052. return this.fontSize * this.lineHeight;
  16053. },
  16054. /**
  16055. * @private
  16056. * @param {CanvasRenderingContext2D} ctx Context to render on
  16057. * @param {Array} textLines Array of all text lines
  16058. */
  16059. _renderTextBackground: function(ctx, textLines) {
  16060. this._renderTextBoxBackground(ctx);
  16061. this._renderTextLinesBackground(ctx, textLines);
  16062. },
  16063. /**
  16064. * @private
  16065. * @param {CanvasRenderingContext2D} ctx Context to render on
  16066. */
  16067. _renderTextBoxBackground: function(ctx) {
  16068. if (!this.backgroundColor) {
  16069. return;
  16070. }
  16071. ctx.save();
  16072. ctx.fillStyle = this.backgroundColor;
  16073. ctx.fillRect(
  16074. this._getLeftOffset(),
  16075. this._getTopOffset(),
  16076. this.width,
  16077. this.height
  16078. );
  16079. ctx.restore();
  16080. },
  16081. /**
  16082. * @private
  16083. * @param {CanvasRenderingContext2D} ctx Context to render on
  16084. * @param {Array} textLines Array of all text lines
  16085. */
  16086. _renderTextLinesBackground: function(ctx, textLines) {
  16087. if (!this.textBackgroundColor) {
  16088. return;
  16089. }
  16090. ctx.save();
  16091. ctx.fillStyle = this.textBackgroundColor;
  16092. for (var i = 0, len = textLines.length; i < len; i++) {
  16093. if (textLines[i] !== '') {
  16094. var lineWidth = this._getLineWidth(ctx, textLines[i]),
  16095. lineLeftOffset = this._getLineLeftOffset(lineWidth);
  16096. ctx.fillRect(
  16097. this._getLeftOffset() + lineLeftOffset,
  16098. this._getTopOffset() + (i * this.fontSize * this.lineHeight),
  16099. lineWidth,
  16100. this.fontSize * this.lineHeight
  16101. );
  16102. }
  16103. }
  16104. ctx.restore();
  16105. },
  16106. /**
  16107. * @private
  16108. * @param {Number} lineWidth Width of text line
  16109. * @return {Number} Line left offset
  16110. */
  16111. _getLineLeftOffset: function(lineWidth) {
  16112. if (this.textAlign === 'center') {
  16113. return (this.width - lineWidth) / 2;
  16114. }
  16115. if (this.textAlign === 'right') {
  16116. return this.width - lineWidth;
  16117. }
  16118. return 0;
  16119. },
  16120. /**
  16121. * @private
  16122. * @param {CanvasRenderingContext2D} ctx Context to render on
  16123. * @param {String} line Text line
  16124. * @return {Number} Line width
  16125. */
  16126. _getLineWidth: function(ctx, line) {
  16127. return this.textAlign === 'justify'
  16128. ? this.width
  16129. : ctx.measureText(line).width;
  16130. },
  16131. /**
  16132. * @private
  16133. * @param {CanvasRenderingContext2D} ctx Context to render on
  16134. * @param {Array} textLines Array of all text lines
  16135. */
  16136. _renderTextDecoration: function(ctx, textLines) {
  16137. if (!this.textDecoration) {
  16138. return;
  16139. }
  16140. // var halfOfVerticalBox = this.originY === 'top' ? 0 : this._getTextHeight(ctx, textLines) / 2;
  16141. var halfOfVerticalBox = this._getTextHeight(ctx, textLines) / 2,
  16142. _this = this;
  16143. /** @ignore */
  16144. function renderLinesAtOffset(offset) {
  16145. for (var i = 0, len = textLines.length; i < len; i++) {
  16146. var lineWidth = _this._getLineWidth(ctx, textLines[i]),
  16147. lineLeftOffset = _this._getLineLeftOffset(lineWidth);
  16148. ctx.fillRect(
  16149. _this._getLeftOffset() + lineLeftOffset,
  16150. ~~((offset + (i * _this._getHeightOfLine(ctx, i, textLines))) - halfOfVerticalBox),
  16151. lineWidth,
  16152. 1);
  16153. }
  16154. }
  16155. if (this.textDecoration.indexOf('underline') > -1) {
  16156. renderLinesAtOffset(this.fontSize * this.lineHeight);
  16157. }
  16158. if (this.textDecoration.indexOf('line-through') > -1) {
  16159. renderLinesAtOffset(this.fontSize * this.lineHeight - this.fontSize / 2);
  16160. }
  16161. if (this.textDecoration.indexOf('overline') > -1) {
  16162. renderLinesAtOffset(this.fontSize * this.lineHeight - this.fontSize);
  16163. }
  16164. },
  16165. /**
  16166. * @private
  16167. */
  16168. _getFontDeclaration: function() {
  16169. return [
  16170. // node-canvas needs "weight style", while browsers need "style weight"
  16171. (fabric.isLikelyNode ? this.fontWeight : this.fontStyle),
  16172. (fabric.isLikelyNode ? this.fontStyle : this.fontWeight),
  16173. this.fontSize + 'px',
  16174. (fabric.isLikelyNode ? ('"' + this.fontFamily + '"') : this.fontFamily)
  16175. ].join(' ');
  16176. },
  16177. /**
  16178. * Renders text instance on a specified context
  16179. * @param {CanvasRenderingContext2D} ctx Context to render on
  16180. */
  16181. render: function(ctx, noTransform) {
  16182. // do not render if object is not visible
  16183. if (!this.visible) {
  16184. return;
  16185. }
  16186. ctx.save();
  16187. this._transform(ctx, noTransform);
  16188. var m = this.transformMatrix,
  16189. isInPathGroup = this.group && this.group.type === 'path-group';
  16190. if (isInPathGroup) {
  16191. ctx.translate(-this.group.width/2, -this.group.height/2);
  16192. }
  16193. if (m) {
  16194. ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
  16195. }
  16196. if (isInPathGroup) {
  16197. ctx.translate(this.left, this.top);
  16198. }
  16199. this._render(ctx);
  16200. ctx.restore();
  16201. },
  16202. /**
  16203. * Returns object representation of an instance
  16204. * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
  16205. * @return {Object} Object representation of an instance
  16206. */
  16207. toObject: function(propertiesToInclude) {
  16208. var object = extend(this.callSuper('toObject', propertiesToInclude), {
  16209. text: this.text,
  16210. fontSize: this.fontSize,
  16211. fontWeight: this.fontWeight,
  16212. fontFamily: this.fontFamily,
  16213. fontStyle: this.fontStyle,
  16214. lineHeight: this.lineHeight,
  16215. textDecoration: this.textDecoration,
  16216. textAlign: this.textAlign,
  16217. path: this.path,
  16218. textBackgroundColor: this.textBackgroundColor,
  16219. useNative: this.useNative
  16220. });
  16221. if (!this.includeDefaultValues) {
  16222. this._removeDefaultValues(object);
  16223. }
  16224. return object;
  16225. },
  16226. /* _TO_SVG_START_ */
  16227. /**
  16228. * Returns SVG representation of an instance
  16229. * @param {Function} [reviver] Method for further parsing of svg representation.
  16230. * @return {String} svg representation of an instance
  16231. */
  16232. toSVG: function(reviver) {
  16233. var markup = [ ],
  16234. textLines = this.text.split(this._reNewline),
  16235. offsets = this._getSVGLeftTopOffsets(textLines),
  16236. textAndBg = this._getSVGTextAndBg(offsets.lineTop, offsets.textLeft, textLines),
  16237. shadowSpans = this._getSVGShadows(offsets.lineTop, textLines);
  16238. // move top offset by an ascent
  16239. offsets.textTop += (this._fontAscent ? ((this._fontAscent / 5) * this.lineHeight) : 0);
  16240. this._wrapSVGTextAndBg(markup, textAndBg, shadowSpans, offsets);
  16241. return reviver ? reviver(markup.join('')) : markup.join('');
  16242. },
  16243. /**
  16244. * @private
  16245. */
  16246. _getSVGLeftTopOffsets: function(textLines) {
  16247. var lineTop = this.useNative
  16248. ? this.fontSize * this.lineHeight
  16249. : (-this._fontAscent - ((this._fontAscent / 5) * this.lineHeight)),
  16250. textLeft = -(this.width/2),
  16251. textTop = this.useNative
  16252. ? this.fontSize - 1
  16253. : (this.height/2) - (textLines.length * this.fontSize) - this._totalLineHeight;
  16254. return {
  16255. textLeft: textLeft + (this.group && this.group.type === 'path-group' ? this.left : 0),
  16256. textTop: textTop + (this.group && this.group.type === 'path-group' ? this.top : 0),
  16257. lineTop: lineTop
  16258. };
  16259. },
  16260. /**
  16261. * @private
  16262. */
  16263. _wrapSVGTextAndBg: function(markup, textAndBg, shadowSpans, offsets) {
  16264. markup.push(
  16265. '<g transform="', this.getSvgTransform(), this.getSvgTransformMatrix(), '">\n',
  16266. textAndBg.textBgRects.join(''),
  16267. '<text ',
  16268. (this.fontFamily ? 'font-family="' + this.fontFamily.replace(/"/g,'\'') + '" ': ''),
  16269. (this.fontSize ? 'font-size="' + this.fontSize + '" ': ''),
  16270. (this.fontStyle ? 'font-style="' + this.fontStyle + '" ': ''),
  16271. (this.fontWeight ? 'font-weight="' + this.fontWeight + '" ': ''),
  16272. (this.textDecoration ? 'text-decoration="' + this.textDecoration + '" ': ''),
  16273. 'style="', this.getSvgStyles(), '" ',
  16274. /* svg starts from left/bottom corner so we normalize height */
  16275. 'transform="translate(', toFixed(offsets.textLeft, 2), ' ', toFixed(offsets.textTop, 2), ')">',
  16276. shadowSpans.join(''),
  16277. textAndBg.textSpans.join(''),
  16278. '</text>\n',
  16279. '</g>\n'
  16280. );
  16281. },
  16282. /**
  16283. * @private
  16284. * @param {Number} lineHeight
  16285. * @param {Array} textLines Array of all text lines
  16286. * @return {Array}
  16287. */
  16288. _getSVGShadows: function(lineHeight, textLines) {
  16289. var shadowSpans = [],
  16290. i, len,
  16291. lineTopOffsetMultiplier = 1;
  16292. if (!this.shadow || !this._boundaries) {
  16293. return shadowSpans;
  16294. }
  16295. for (i = 0, len = textLines.length; i < len; i++) {
  16296. if (textLines[i] !== '') {
  16297. var lineLeftOffset = (this._boundaries && this._boundaries[i]) ? this._boundaries[i].left : 0;
  16298. shadowSpans.push(
  16299. '<tspan x="',
  16300. toFixed((lineLeftOffset + lineTopOffsetMultiplier) + this.shadow.offsetX, 2),
  16301. ((i === 0 || this.useNative) ? '" y' : '" dy'), '="',
  16302. toFixed(this.useNative
  16303. ? ((lineHeight * i) - this.height / 2 + this.shadow.offsetY)
  16304. : (lineHeight + (i === 0 ? this.shadow.offsetY : 0)), 2),
  16305. '" ',
  16306. this._getFillAttributes(this.shadow.color), '>',
  16307. fabric.util.string.escapeXml(textLines[i]),
  16308. '</tspan>');
  16309. lineTopOffsetMultiplier = 1;
  16310. }
  16311. else {
  16312. // in some environments (e.g. IE 7 & 8) empty tspans are completely ignored, using a lineTopOffsetMultiplier
  16313. // prevents empty tspans
  16314. lineTopOffsetMultiplier++;
  16315. }
  16316. }
  16317. return shadowSpans;
  16318. },
  16319. /**
  16320. * @private
  16321. * @param {Number} lineHeight
  16322. * @param {Number} textLeftOffset Text left offset
  16323. * @param {Array} textLines Array of all text lines
  16324. * @return {Object}
  16325. */
  16326. _getSVGTextAndBg: function(lineHeight, textLeftOffset, textLines) {
  16327. var textSpans = [ ],
  16328. textBgRects = [ ],
  16329. lineTopOffsetMultiplier = 1;
  16330. // bounding-box background
  16331. this._setSVGBg(textBgRects);
  16332. // text and text-background
  16333. for (var i = 0, len = textLines.length; i < len; i++) {
  16334. if (textLines[i] !== '') {
  16335. this._setSVGTextLineText(textLines[i], i, textSpans, lineHeight, lineTopOffsetMultiplier, textBgRects);
  16336. lineTopOffsetMultiplier = 1;
  16337. }
  16338. else {
  16339. // in some environments (e.g. IE 7 & 8) empty tspans are completely ignored, using a lineTopOffsetMultiplier
  16340. // prevents empty tspans
  16341. lineTopOffsetMultiplier++;
  16342. }
  16343. if (!this.textBackgroundColor || !this._boundaries) {
  16344. continue;
  16345. }
  16346. this._setSVGTextLineBg(textBgRects, i, textLeftOffset, lineHeight);
  16347. }
  16348. return {
  16349. textSpans: textSpans,
  16350. textBgRects: textBgRects
  16351. };
  16352. },
  16353. _setSVGTextLineText: function(textLine, i, textSpans, lineHeight, lineTopOffsetMultiplier) {
  16354. var lineLeftOffset = (this._boundaries && this._boundaries[i])
  16355. ? toFixed(this._boundaries[i].left, 2)
  16356. : 0;
  16357. textSpans.push(
  16358. '<tspan x="',
  16359. lineLeftOffset, '" ',
  16360. (i === 0 || this.useNative ? 'y' : 'dy'), '="',
  16361. toFixed(this.useNative
  16362. ? ((lineHeight * i) - this.height / 2)
  16363. : (lineHeight * lineTopOffsetMultiplier), 2), '" ',
  16364. // doing this on <tspan> elements since setting opacity
  16365. // on containing <text> one doesn't work in Illustrator
  16366. this._getFillAttributes(this.fill), '>',
  16367. fabric.util.string.escapeXml(textLine),
  16368. '</tspan>'
  16369. );
  16370. },
  16371. _setSVGTextLineBg: function(textBgRects, i, textLeftOffset, lineHeight) {
  16372. textBgRects.push(
  16373. '<rect ',
  16374. this._getFillAttributes(this.textBackgroundColor),
  16375. ' x="',
  16376. toFixed(textLeftOffset + this._boundaries[i].left, 2),
  16377. '" y="',
  16378. /* an offset that seems to straighten things out */
  16379. toFixed((lineHeight * i) - this.height / 2, 2),
  16380. '" width="',
  16381. toFixed(this._boundaries[i].width, 2),
  16382. '" height="',
  16383. toFixed(this._boundaries[i].height, 2),
  16384. '"></rect>\n');
  16385. },
  16386. _setSVGBg: function(textBgRects) {
  16387. if (this.backgroundColor && this._boundaries) {
  16388. textBgRects.push(
  16389. '<rect ',
  16390. this._getFillAttributes(this.backgroundColor),
  16391. ' x="',
  16392. toFixed(-this.width / 2, 2),
  16393. '" y="',
  16394. toFixed(-this.height / 2, 2),
  16395. '" width="',
  16396. toFixed(this.width, 2),
  16397. '" height="',
  16398. toFixed(this.height, 2),
  16399. '"></rect>');
  16400. }
  16401. },
  16402. /**
  16403. * Adobe Illustrator (at least CS5) is unable to render rgba()-based fill values
  16404. * we work around it by "moving" alpha channel into opacity attribute and setting fill's alpha to 1
  16405. *
  16406. * @private
  16407. * @param {Any} value
  16408. * @return {String}
  16409. */
  16410. _getFillAttributes: function(value) {
  16411. var fillColor = (value && typeof value === 'string') ? new fabric.Color(value) : '';
  16412. if (!fillColor || !fillColor.getSource() || fillColor.getAlpha() === 1) {
  16413. return 'fill="' + value + '"';
  16414. }
  16415. return 'opacity="' + fillColor.getAlpha() + '" fill="' + fillColor.setAlpha(1).toRgb() + '"';
  16416. },
  16417. /* _TO_SVG_END_ */
  16418. /**
  16419. * Sets specified property to a specified value
  16420. * @param {String} key
  16421. * @param {Any} value
  16422. * @return {fabric.Text} thisArg
  16423. * @chainable
  16424. */
  16425. _set: function(key, value) {
  16426. if (key === 'fontFamily' && this.path) {
  16427. this.path = this.path.replace(/(.*?)([^\/]*)(\.font\.js)/, '$1' + value + '$3');
  16428. }
  16429. this.callSuper('_set', key, value);
  16430. if (key in this._dimensionAffectingProps) {
  16431. this._initDimensions();
  16432. this.setCoords();
  16433. }
  16434. },
  16435. /**
  16436. * Returns complexity of an instance
  16437. * @return {Number} complexity
  16438. */
  16439. complexity: function() {
  16440. return 1;
  16441. }
  16442. });
  16443. /* _FROM_SVG_START_ */
  16444. /**
  16445. * List of attribute names to account for when parsing SVG element (used by {@link fabric.Text.fromElement})
  16446. * @static
  16447. * @memberOf fabric.Text
  16448. * @see: http://www.w3.org/TR/SVG/text.html#TextElement
  16449. */
  16450. fabric.Text.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat(
  16451. 'x y dx dy font-family font-style font-weight font-size text-decoration text-anchor'.split(' '));
  16452. /**
  16453. * Default SVG font size
  16454. * @static
  16455. * @memberOf fabric.Text
  16456. */
  16457. fabric.Text.DEFAULT_SVG_FONT_SIZE = 16;
  16458. /**
  16459. * Returns fabric.Text instance from an SVG element (<b>not yet implemented</b>)
  16460. * @static
  16461. * @memberOf fabric.Text
  16462. * @param {SVGElement} element Element to parse
  16463. * @param {Object} [options] Options object
  16464. * @return {fabric.Text} Instance of fabric.Text
  16465. */
  16466. fabric.Text.fromElement = function(element, options) {
  16467. if (!element) {
  16468. return null;
  16469. }
  16470. var parsedAttributes = fabric.parseAttributes(element, fabric.Text.ATTRIBUTE_NAMES);
  16471. options = fabric.util.object.extend((options ? fabric.util.object.clone(options) : { }), parsedAttributes);
  16472. if ('dx' in parsedAttributes) {
  16473. options.left += parsedAttributes.dx;
  16474. }
  16475. if ('dy' in parsedAttributes) {
  16476. options.top += parsedAttributes.dy;
  16477. }
  16478. if (!('fontSize' in options)) {
  16479. options.fontSize = fabric.Text.DEFAULT_SVG_FONT_SIZE;
  16480. }
  16481. if (!options.originX) {
  16482. options.originX = 'left';
  16483. }
  16484. var text = new fabric.Text(element.textContent, options),
  16485. /*
  16486. Adjust positioning:
  16487. x/y attributes in SVG correspond to the bottom-left corner of text bounding box
  16488. top/left properties in Fabric correspond to center point of text bounding box
  16489. */
  16490. offX = 0;
  16491. if (text.originX === 'left') {
  16492. offX = text.getWidth() / 2;
  16493. }
  16494. if (text.originX === 'right') {
  16495. offX = -text.getWidth() / 2;
  16496. }
  16497. text.set({
  16498. left: text.getLeft() + offX,
  16499. top: text.getTop() - text.getHeight() / 2
  16500. });
  16501. return text;
  16502. };
  16503. /* _FROM_SVG_END_ */
  16504. /**
  16505. * Returns fabric.Text instance from an object representation
  16506. * @static
  16507. * @memberOf fabric.Text
  16508. * @param {Object} object Object to create an instance from
  16509. * @return {fabric.Text} Instance of fabric.Text
  16510. */
  16511. fabric.Text.fromObject = function(object) {
  16512. return new fabric.Text(object.text, clone(object));
  16513. };
  16514. fabric.util.createAccessors(fabric.Text);
  16515. })(typeof exports !== 'undefined' ? exports : this);
  16516. }).call(this,_dereq_("buffer").Buffer)
  16517. },{"buffer":2,"canvas":1,"jsdom":1}]},{},[6])(6)
  16518. });