protovis-d3.1.js 249 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705
  1. /**
  2. * @class The built-in Array class.
  3. * @name Array
  4. */
  5. if (!Array.prototype.map) {
  6. /**
  7. * Creates a new array with the results of calling a provided function on
  8. * every element in this array. Implemented in Javascript 1.6.
  9. *
  10. * @see <a
  11. * href="https://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Objects/Array/Map">map</a>
  12. * documentation.
  13. * @param {function} f function that produces an element of the new Array from
  14. * an element of the current one.
  15. * @param [o] object to use as <tt>this</tt> when executing <tt>f</tt>.
  16. */
  17. Array.prototype.map = function(f, o) {
  18. var n = this.length;
  19. var result = new Array(n);
  20. for (var i = 0; i < n; i++) {
  21. if (i in this) {
  22. result[i] = f.call(o, this[i], i, this);
  23. }
  24. }
  25. return result;
  26. };
  27. }
  28. if (!Array.prototype.filter) {
  29. /**
  30. * Creates a new array with all elements that pass the test implemented by the
  31. * provided function. Implemented in Javascript 1.6.
  32. *
  33. * @see <a
  34. * href="https://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Objects/Array/filter">filter</a>
  35. * documentation.
  36. * @param {function} f function to test each element of the array.
  37. * @param [o] object to use as <tt>this</tt> when executing <tt>f</tt>.
  38. */
  39. Array.prototype.filter = function(f, o) {
  40. var n = this.length;
  41. var result = new Array();
  42. for (var i = 0; i < n; i++) {
  43. if (i in this) {
  44. var v = this[i];
  45. if (f.call(o, v, i, this)) result.push(v);
  46. }
  47. }
  48. return result;
  49. };
  50. }
  51. if (!Array.prototype.forEach) {
  52. /**
  53. * Executes a provided function once per array element. Implemented in
  54. * Javascript 1.6.
  55. *
  56. * @see <a
  57. * href="https://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Objects/Array/ForEach">forEach</a>
  58. * documentation.
  59. * @param {function} f function to execute for each element.
  60. * @param [o] object to use as <tt>this</tt> when executing <tt>f</tt>.
  61. */
  62. Array.prototype.forEach = function(f, o) {
  63. var n = this.length >>> 0;
  64. for (var i = 0; i < n; i++) {
  65. if (i in this) f.call(o, this[i], i, this);
  66. }
  67. };
  68. }
  69. if (!Array.prototype.reduce) {
  70. /**
  71. * Apply a function against an accumulator and each value of the array (from
  72. * left-to-right) as to reduce it to a single value. Implemented in Javascript
  73. * 1.8.
  74. *
  75. * @see <a
  76. * href="https://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Objects/Array/Reduce">reduce</a>
  77. * documentation.
  78. * @param {function} f function to execute on each value in the array.
  79. * @param [v] object to use as the first argument to the first call of
  80. * <tt>t</tt>.
  81. */
  82. Array.prototype.reduce = function(f, v) {
  83. var len = this.length;
  84. if (!len && (arguments.length == 1)) {
  85. throw new Error("reduce: empty array, no initial value");
  86. }
  87. var i = 0;
  88. if (arguments.length < 2) {
  89. while (true) {
  90. if (i in this) {
  91. v = this[i++];
  92. break;
  93. }
  94. if (++i >= len) {
  95. throw new Error("reduce: no values, no initial value");
  96. }
  97. }
  98. }
  99. for (; i < len; i++) {
  100. if (i in this) {
  101. v = f(v, this[i], i, this);
  102. }
  103. }
  104. return v;
  105. };
  106. }
  107. /**
  108. * @class The built-in Date class.
  109. * @name Date
  110. */
  111. Date.__parse__ = Date.parse;
  112. /**
  113. * Parses a date from a string, optionally using the specified formatting. If
  114. * only a single argument is specified (i.e., <tt>format</tt> is not specified),
  115. * this method invokes the native implementation to guarantee
  116. * backwards-compatibility.
  117. *
  118. * <p>The format string is in the same format expected by the <tt>strptime</tt>
  119. * function in C. The following conversion specifications are supported:<ul>
  120. *
  121. * <li>%b - abbreviated month names.</li>
  122. * <li>%B - full month names.</li>
  123. * <li>%h - same as %b.</li>
  124. * <li>%d - day of month [1,31].</li>
  125. * <li>%e - same as %d.</li>
  126. * <li>%H - hour (24-hour clock) [0,23].</li>
  127. * <li>%m - month number [1,12].</li>
  128. * <li>%M - minute [0,59].</li>
  129. * <li>%S - second [0,61].</li>
  130. * <li>%y - year with century [0,99].</li>
  131. * <li>%Y - year including century.</li>
  132. * <li>%% - %.</li>
  133. *
  134. * </ul>The following conversion specifications are <i>unsupported</i> (for now):<ul>
  135. *
  136. * <li>%a - day of week, either abbreviated or full name.</li>
  137. * <li>%A - same as %a.</li>
  138. * <li>%c - locale's appropriate date and time.</li>
  139. * <li>%C - century number.</li>
  140. * <li>%D - same as %m/%d/%y.</li>
  141. * <li>%I - hour (12-hour clock) [1,12].</li>
  142. * <li>%j - day number [1,366].</li>
  143. * <li>%n - any white space.</li>
  144. * <li>%p - locale's equivalent of a.m. or p.m.</li>
  145. * <li>%r - same as %I:%M:%S %p.</li>
  146. * <li>%R - same as %H:%M.</li>
  147. * <li>%t - same as %n.</li>
  148. * <li>%T - same as %H:%M:%S.</li>
  149. * <li>%U - week number [0,53].</li>
  150. * <li>%w - weekday [0,6].</li>
  151. * <li>%W - week number [0,53].</li>
  152. * <li>%x - locale's equivalent to %m/%d/%y.</li>
  153. * <li>%X - locale's equivalent to %I:%M:%S %p.</li>
  154. *
  155. * </ul>
  156. *
  157. * @see <a
  158. * href="http://www.opengroup.org/onlinepubs/007908799/xsh/strptime.html">strptime</a>
  159. * documentation.
  160. * @param {string} s the string to parse as a date.
  161. * @param {string} [format] an optional format string.
  162. * @returns {Date} the parsed date.
  163. */
  164. Date.parse = function(s, format) {
  165. if (arguments.length == 1) {
  166. return Date.__parse__(s);
  167. }
  168. var year = 1970, month = 0, date = 1, hour = 0, minute = 0, second = 0;
  169. var fields = [function() {}];
  170. format = format.replace(/[\\\^\$\*\+\?\[\]\(\)\.\{\}]/g, "\\$&");
  171. format = format.replace(/%[a-zA-Z0-9]/g, function(s) {
  172. switch (s) {
  173. // TODO %a: day of week, either abbreviated or full name
  174. // TODO %A: same as %a
  175. case '%b': {
  176. fields.push(function(x) { month = {
  177. Jan: 0, Feb: 1, Mar: 2, Apr: 3, May: 4, Jun: 5, Jul: 6, Aug: 7,
  178. Sep: 8, Oct: 9, Nov: 10, Dec: 11
  179. }[x]; });
  180. return "([A-Za-z]+)";
  181. }
  182. case '%h':
  183. case '%B': {
  184. fields.push(function(x) { month = {
  185. January: 0, February: 1, March: 2, April: 3, May: 4, June: 5,
  186. July: 6, August: 7, September: 8, October: 9, November: 10,
  187. December: 11
  188. }[x]; });
  189. return "([A-Za-z]+)";
  190. }
  191. // TODO %c: locale's appropriate date and time
  192. // TODO %C: century number[0,99]
  193. case '%e':
  194. case '%d': {
  195. fields.push(function(x) { date = x; });
  196. return "([0-9]+)";
  197. }
  198. // TODO %D: same as %m/%d/%y
  199. case '%H': {
  200. fields.push(function(x) { hour = x; });
  201. return "([0-9]+)";
  202. }
  203. // TODO %I: hour (12-hour clock) [1,12]
  204. // TODO %j: day number [1,366]
  205. case '%m': {
  206. fields.push(function(x) { month = x - 1; });
  207. return "([0-9]+)";
  208. }
  209. case '%M': {
  210. fields.push(function(x) { minute = x; });
  211. return "([0-9]+)";
  212. }
  213. // TODO %n: any white space
  214. // TODO %p: locale's equivalent of a.m. or p.m.
  215. // TODO %r: %I:%M:%S %p
  216. // TODO %R: %H:%M
  217. case '%S': {
  218. fields.push(function(x) { second = x; });
  219. return "([0-9]+)";
  220. }
  221. // TODO %t: any white space
  222. // TODO %T: %H:%M:%S
  223. // TODO %U: week number [00,53]
  224. // TODO %w: weekday [0,6]
  225. // TODO %W: week number [00, 53]
  226. // TODO %x: locale date (%m/%d/%y)
  227. // TODO %X: locale time (%I:%M:%S %p)
  228. case '%y': {
  229. fields.push(function(x) {
  230. x = Number(x);
  231. year = x + (((0 <= x) && (x < 69)) ? 2000
  232. : (((x >= 69) && (x < 100) ? 1900 : 0)));
  233. });
  234. return "([0-9]+)";
  235. }
  236. case '%Y': {
  237. fields.push(function(x) { year = x; });
  238. return "([0-9]+)";
  239. }
  240. case '%%': {
  241. fields.push(function() {});
  242. return "%";
  243. }
  244. }
  245. return s;
  246. });
  247. var match = s.match(format);
  248. if (match) match.forEach(function(m, i) { fields[i](m); });
  249. return new Date(year, month, date, hour, minute, second);
  250. };
  251. if (Date.prototype.toLocaleFormat) {
  252. Date.prototype.format = Date.prototype.toLocaleFormat;
  253. } else {
  254. /**
  255. * Converts a date to a string using the specified formatting. If the
  256. * <tt>Date</tt> object already supports the <tt>toLocaleFormat</tt> method, as
  257. * in Firefox, this is simply an alias to the built-in method.
  258. *
  259. * <p>The format string is in the same format expected by the <tt>strftime</tt>
  260. * function in C. The following conversion specifications are supported:<ul>
  261. *
  262. * <li>%a - abbreviated weekday name.</li>
  263. * <li>%A - full weekday name.</li>
  264. * <li>%b - abbreviated month names.</li>
  265. * <li>%B - full month names.</li>
  266. * <li>%c - locale's appropriate date and time.</li>
  267. * <li>%C - century number.</li>
  268. * <li>%d - day of month [01,31] (zero padded).</li>
  269. * <li>%D - same as %m/%d/%y.</li>
  270. * <li>%e - day of month [ 1,31] (space padded).</li>
  271. * <li>%h - same as %b.</li>
  272. * <li>%H - hour (24-hour clock) [00,23] (zero padded).</li>
  273. * <li>%I - hour (12-hour clock) [01,12] (zero padded).</li>
  274. * <li>%m - month number [01,12] (zero padded).</li>
  275. * <li>%M - minute [0,59] (zero padded).</li>
  276. * <li>%n - newline character.</li>
  277. * <li>%p - locale's equivalent of a.m. or p.m.</li>
  278. * <li>%r - same as %I:%M:%S %p.</li>
  279. * <li>%R - same as %H:%M.</li>
  280. * <li>%S - second [00,61] (zero padded).</li>
  281. * <li>%t - tab character.</li>
  282. * <li>%T - same as %H:%M:%S.</li>
  283. * <li>%x - same as %m/%d/%y.</li>
  284. * <li>%X - same as %I:%M:%S %p.</li>
  285. * <li>%y - year with century [00,99] (zero padded).</li>
  286. * <li>%Y - year including century.</li>
  287. * <li>%% - %.</li>
  288. *
  289. * </ul>The following conversion specifications are <i>unsupported</i> (for now):<ul>
  290. *
  291. * <li>%j - day number [1,366].</li>
  292. * <li>%u - weekday number [1,7].</li>
  293. * <li>%U - week number [00,53].</li>
  294. * <li>%V - week number [01,53].</li>
  295. * <li>%w - weekday number [0,6].</li>
  296. * <li>%W - week number [00,53].</li>
  297. * <li>%Z - timezone name or abbreviation.</li>
  298. *
  299. * </ul>
  300. *
  301. * @see <a
  302. * href="http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Date/toLocaleFormat">Date.toLocaleFormat</a>
  303. * documentation.
  304. * @see <a
  305. * href="http://www.opengroup.org/onlinepubs/007908799/xsh/strftime.html">strftime</a>
  306. * documentation.
  307. * @param {string} format a format string.
  308. * @returns {string} the formatted date.
  309. */
  310. Date.prototype.format = function(format) {
  311. function pad(n, p) { return (n < 10) ? (p || "0") + n : n; }
  312. var d = this;
  313. return format.replace(/%[a-zA-Z0-9]/g, function(s) {
  314. switch (s) {
  315. case '%a': return [
  316. "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
  317. ][d.getDay()];
  318. case '%A': return [
  319. "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday",
  320. "Saturday"
  321. ][d.getDay()];
  322. case '%h':
  323. case '%b': return [
  324. "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep",
  325. "Oct", "Nov", "Dec"
  326. ][d.getMonth()];
  327. case '%B': return [
  328. "January", "February", "March", "April", "May", "June", "July",
  329. "August", "September", "October", "November", "December"
  330. ][d.getMonth()];
  331. case '%c': return d.toLocaleString();
  332. case '%C': return pad(Math.floor(d.getFullYear() / 100) % 100);
  333. case '%d': return pad(d.getDate());
  334. case '%x':
  335. case '%D': return pad(d.getMonth() + 1)
  336. + "/" + pad(d.getDate())
  337. + "/" + pad(d.getFullYear() % 100);
  338. case '%e': return pad(d.getDate(), " ");
  339. case '%H': return pad(d.getHours());
  340. case '%I': {
  341. var h = d.getHours() % 12;
  342. return h ? pad(h) : 12;
  343. }
  344. // TODO %j: day of year as a decimal number [001,366]
  345. case '%m': return pad(d.getMonth() + 1);
  346. case '%M': return pad(d.getMinutes());
  347. case '%n': return "\n";
  348. case '%p': return d.getHours() < 12 ? "AM" : "PM";
  349. case '%T':
  350. case '%X':
  351. case '%r': {
  352. var h = d.getHours() % 12;
  353. return (h ? pad(h) : 12)
  354. + ":" + pad(d.getMinutes())
  355. + ":" + pad(d.getSeconds())
  356. + " " + (d.getHours() < 12 ? "AM" : "PM");
  357. }
  358. case '%R': return pad(d.getHours()) + ":" + pad(d.getMinutes());
  359. case '%S': return pad(d.getSeconds());
  360. case '%t': return "\t";
  361. case '%u': {
  362. var w = d.getDay();
  363. return w ? w : 1;
  364. }
  365. // TODO %U: week number (sunday first day) [00,53]
  366. // TODO %V: week number (monday first day) [01,53] ... with weirdness
  367. case '%w': return d.getDay();
  368. // TODO %W: week number (monday first day) [00,53] ... with weirdness
  369. case '%y': return pad(d.getFullYear() % 100);
  370. case '%Y': return d.getFullYear();
  371. // TODO %Z: timezone name or abbreviation
  372. case '%%': return "%";
  373. }
  374. return s;
  375. });
  376. };
  377. }
  378. var pv = function() {/**
  379. * The top-level Protovis namespace. All public methods and fields should be
  380. * registered on this object. Note that core Protovis source is surrounded by an
  381. * anonymous function, so any other declared globals will not be visible outside
  382. * of core methods. This also allows multiple versions of Protovis to coexist,
  383. * since each version will see their own <tt>pv</tt> namespace.
  384. *
  385. * @namespace The top-level Protovis namespace, <tt>pv</tt>.
  386. */
  387. var pv = {};
  388. /**
  389. * @private Returns a prototype object suitable for extending the given class
  390. * <tt>f</tt>. Rather than constructing a new instance of <tt>f</tt> to serve as
  391. * the prototype (which unnecessarily runs the constructor on the created
  392. * prototype object, potentially polluting it), an anonymous function is
  393. * generated internally that shares the same prototype:
  394. *
  395. * <pre>function g() {}
  396. * g.prototype = f.prototype;
  397. * return new g();</pre>
  398. *
  399. * For more details, see Douglas Crockford's essay on prototypal inheritance.
  400. *
  401. * @param {function} f a constructor.
  402. * @returns a suitable prototype object.
  403. * @see Douglas Crockford's essay on <a
  404. * href="http://javascript.crockford.com/prototypal.html">prototypal
  405. * inheritance</a>.
  406. */
  407. pv.extend = function(f) {
  408. function g() {}
  409. g.prototype = f.prototype || f;
  410. return new g();
  411. };
  412. try {
  413. eval("pv.parse = function(x) x;"); // native support
  414. } catch (e) {
  415. /**
  416. * @private Parses a Protovis specification, which may use JavaScript 1.8
  417. * function expresses, replacing those function expressions with proper
  418. * functions such that the code can be run by a JavaScript 1.6 interpreter. This
  419. * hack only supports function expressions (using clumsy regular expressions, no
  420. * less), and not other JavaScript 1.8 features such as let expressions.
  421. *
  422. * @param {string} s a Protovis specification (i.e., a string of JavaScript 1.8
  423. * source code).
  424. * @returns {string} a conformant JavaScript 1.6 source code.
  425. */
  426. pv.parse = function(js) { // hacky regex support
  427. var re = new RegExp("function(\\s+\\w+)?\\([^)]*\\)\\s*", "mg"), m, d, i = 0, s = "";
  428. while (m = re.exec(js)) {
  429. var j = m.index + m[0].length;
  430. if (js.charAt(j--) != '{') {
  431. s += js.substring(i, j) + "{return ";
  432. i = j;
  433. for (var p = 0; p >= 0 && j < js.length; j++) {
  434. var c = js.charAt(j);
  435. switch (c) {
  436. case '"': case '\'': {
  437. while (++j < js.length && (d = js.charAt(j)) != c) {
  438. if (d == '\\') j++;
  439. }
  440. break;
  441. }
  442. case '[': case '(': p++; break;
  443. case ']': case ')': p--; break;
  444. case ';':
  445. case ',': if (p == 0) p--; break;
  446. }
  447. }
  448. s += pv.parse(js.substring(i, --j)) + ";}";
  449. i = j;
  450. }
  451. re.lastIndex = j;
  452. }
  453. s += js.substring(i);
  454. return s;
  455. };
  456. }
  457. /**
  458. * Returns the passed-in argument, <tt>x</tt>; the identity function. This method
  459. * is provided for convenience since it is used as the default behavior for a
  460. * number of property functions.
  461. *
  462. * @param x a value.
  463. * @returns the value <tt>x</tt>.
  464. */
  465. pv.identity = function(x) { return x; };
  466. /**
  467. * Returns <tt>this.index</tt>. This method is provided for convenience for use
  468. * with scales. For example, to color bars by their index, say:
  469. *
  470. * <pre>.fillStyle(pv.Colors.category10().by(pv.index))</pre>
  471. *
  472. * This method is equivalent to <tt>function() this.index</tt>, but more
  473. * succinct. Note that the <tt>index</tt> property is also supported for
  474. * accessor functions with {@link pv.max}, {@link pv.min} and other array
  475. * utility methods.
  476. *
  477. * @see pv.Scale
  478. * @see pv.Mark#index
  479. */
  480. pv.index = function() { return this.index; };
  481. /**
  482. * Returns <tt>this.childIndex</tt>. This method is provided for convenience for
  483. * use with scales. For example, to color bars by their child index, say:
  484. *
  485. * <pre>.fillStyle(pv.Colors.category10().by(pv.child))</pre>
  486. *
  487. * This method is equivalent to <tt>function() this.childIndex</tt>, but more
  488. * succinct.
  489. *
  490. * @see pv.Scale
  491. * @see pv.Mark#childIndex
  492. */
  493. pv.child = function() { return this.childIndex; };
  494. /**
  495. * Returns <tt>this.parent.index</tt>. This method is provided for convenience
  496. * for use with scales. This method is provided for convenience for use with
  497. * scales. For example, to color bars by their parent index, say:
  498. *
  499. * <pre>.fillStyle(pv.Colors.category10().by(pv.parent))</pre>
  500. *
  501. * Tthis method is equivalent to <tt>function() this.parent.index</tt>, but more
  502. * succinct.
  503. *
  504. * @see pv.Scale
  505. * @see pv.Mark#index
  506. */
  507. pv.parent = function() { return this.parent.index; };
  508. /**
  509. * Returns an array of numbers, starting at <tt>start</tt>, incrementing by
  510. * <tt>step</tt>, until <tt>stop</tt> is reached. The stop value is exclusive. If
  511. * only a single argument is specified, this value is interpeted as the
  512. * <i>stop</i> value, with the <i>start</i> value as zero. If only two arguments
  513. * are specified, the step value is implied to be one.
  514. *
  515. * <p>The method is modeled after the built-in <tt>range</tt> method from
  516. * Python. See the Python documentation for more details.
  517. *
  518. * @see <a href="http://docs.python.org/library/functions.html#range">Python range</a>
  519. * @param {number} [start] the start value.
  520. * @param {number} stop the stop value.
  521. * @param {number} [step] the step value.
  522. * @returns {number[]} an array of numbers.
  523. */
  524. pv.range = function(start, stop, step) {
  525. if (arguments.length == 1) {
  526. stop = start;
  527. start = 0;
  528. }
  529. if (step == undefined) step = 1;
  530. else if (!step) throw new Error("step must be non-zero");
  531. var array = [], i = 0, j;
  532. if (step < 0) {
  533. while ((j = start + step * i++) > stop) {
  534. array.push(j);
  535. }
  536. } else {
  537. while ((j = start + step * i++) < stop) {
  538. array.push(j);
  539. }
  540. }
  541. return array;
  542. };
  543. /**
  544. * Returns a random number in the range [<tt>min</tt>, <tt>max</tt>) that is a
  545. * multiple of <tt>step</tt>. More specifically, the returned number is of the
  546. * form <tt>min</tt> + <i>n</i> * <tt>step</tt>, where <i>n</i> is a nonnegative
  547. * integer. If <tt>step</tt> is not specified, it defaults to 1, returning a
  548. * random integer if <tt>min</tt> is also an integer.
  549. *
  550. * @param min {number} minimum value.
  551. * @param [max] {number} maximum value.
  552. * @param [step] {numbeR} step value.
  553. */
  554. pv.random = function(min, max, step) {
  555. if (arguments.length == 1) {
  556. max = min;
  557. min = 0;
  558. }
  559. if (step == undefined) {
  560. step = 1;
  561. }
  562. return step
  563. ? (Math.floor(Math.random() * (max - min) / step) * step + min)
  564. : (Math.random() * (max - min) + min);
  565. };
  566. /**
  567. * Concatenates the specified array with itself <i>n</i> times. For example,
  568. * <tt>pv.repeat([1, 2])</tt> returns [1, 2, 1, 2].
  569. *
  570. * @param {array} a an array.
  571. * @param {number} [n] the number of times to repeat; defaults to two.
  572. * @returns {array} an array that repeats the specified array.
  573. */
  574. pv.repeat = function(array, n) {
  575. if (arguments.length == 1) n = 2;
  576. return pv.blend(pv.range(n).map(function() { return array; }));
  577. };
  578. /**
  579. * Given two arrays <tt>a</tt> and <tt>b</tt>, <style
  580. * type="text/css">sub{line-height:0}</style> returns an array of all possible
  581. * pairs of elements [a<sub>i</sub>, b<sub>j</sub>]. The outer loop is on array
  582. * <i>a</i>, while the inner loop is on <i>b</i>, such that the order of
  583. * returned elements is [a<sub>0</sub>, b<sub>0</sub>], [a<sub>0</sub>,
  584. * b<sub>1</sub>], ... [a<sub>0</sub>, b<sub>m</sub>], [a<sub>1</sub>,
  585. * b<sub>0</sub>], [a<sub>1</sub>, b<sub>1</sub>], ... [a<sub>1</sub>,
  586. * b<sub>m</sub>], ... [a<sub>n</sub>, b<sub>m</sub>]. If either array is empty,
  587. * an empty array is returned.
  588. *
  589. * @param {array} a an array.
  590. * @param {array} b an array.
  591. * @returns {array} an array of pairs of elements in <tt>a</tt> and <tt>b</tt>.
  592. */
  593. pv.cross = function(a, b) {
  594. var array = [];
  595. for (var i = 0, n = a.length, m = b.length; i < n; i++) {
  596. for (var j = 0, x = a[i]; j < m; j++) {
  597. array.push([x, b[j]]);
  598. }
  599. }
  600. return array;
  601. };
  602. /**
  603. * Given the specified array of arrays, concatenates the arrays into a single
  604. * array. If the individual arrays are explicitly known, an alternative to blend
  605. * is to use JavaScript's <tt>concat</tt> method directly. These two equivalent
  606. * expressions:<ul>
  607. *
  608. * <li><tt>pv.blend([[1, 2, 3], ["a", "b", "c"]])</tt>
  609. * <li><tt>[1, 2, 3].concat(["a", "b", "c"])</tt>
  610. *
  611. * </ul>return [1, 2, 3, "a", "b", "c"].
  612. *
  613. * @param {array[]} arrays an array of arrays.
  614. * @returns {array} an array containing all the elements of each array in
  615. * <tt>arrays</tt>.
  616. */
  617. pv.blend = function(arrays) {
  618. return Array.prototype.concat.apply([], arrays);
  619. };
  620. /**
  621. * Given the specified array of arrays, <style
  622. * type="text/css">sub{line-height:0}</style> transposes each element
  623. * array<sub>ij</sub> with array<sub>ji</sub>. If the array has dimensions
  624. * <i>n</i>&times;<i>m</i>, it will have dimensions <i>m</i>&times;<i>n</i>
  625. * after this method returns. This method transposes the elements of the array
  626. * in place, mutating the array, and returning a reference to the array.
  627. *
  628. * @param {array[]} arrays an array of arrays.
  629. * @returns {array[]} the passed-in array, after transposing the elements.
  630. */
  631. pv.transpose = function(arrays) {
  632. var n = arrays.length, m = pv.max(arrays, function(d) { return d.length; });
  633. if (m > n) {
  634. arrays.length = m;
  635. for (var i = n; i < m; i++) {
  636. arrays[i] = new Array(n);
  637. }
  638. for (var i = 0; i < n; i++) {
  639. for (var j = i + 1; j < m; j++) {
  640. var t = arrays[i][j];
  641. arrays[i][j] = arrays[j][i];
  642. arrays[j][i] = t;
  643. }
  644. }
  645. } else {
  646. for (var i = 0; i < m; i++) {
  647. arrays[i].length = n;
  648. }
  649. for (var i = 0; i < n; i++) {
  650. for (var j = 0; j < i; j++) {
  651. var t = arrays[i][j];
  652. arrays[i][j] = arrays[j][i];
  653. arrays[j][i] = t;
  654. }
  655. }
  656. }
  657. arrays.length = m;
  658. for (var i = 0; i < m; i++) {
  659. arrays[i].length = n;
  660. }
  661. return arrays;
  662. };
  663. /**
  664. * Returns all of the property names (keys) of the specified object (a map). The
  665. * order of the returned array is not defined.
  666. *
  667. * @param map an object.
  668. * @returns {string[]} an array of strings corresponding to the keys.
  669. * @see #entries
  670. */
  671. pv.keys = function(map) {
  672. var array = [];
  673. for (var key in map) {
  674. array.push(key);
  675. }
  676. return array;
  677. };
  678. /**
  679. * Returns all of the entries (key-value pairs) of the specified object (a
  680. * map). The order of the returned array is not defined. Each key-value pair is
  681. * represented as an object with <tt>key</tt> and <tt>value</tt> attributes,
  682. * e.g., <tt>{key: "foo", value: 42}</tt>.
  683. *
  684. * @param map an object.
  685. * @returns {array} an array of key-value pairs corresponding to the keys.
  686. */
  687. pv.entries = function(map) {
  688. var array = [];
  689. for (var key in map) {
  690. array.push({ key: key, value: map[key] });
  691. }
  692. return array;
  693. };
  694. /**
  695. * Returns all of the values (attribute values) of the specified object (a
  696. * map). The order of the returned array is not defined.
  697. *
  698. * @param map an object.
  699. * @returns {array} an array of objects corresponding to the values.
  700. * @see #entries
  701. */
  702. pv.values = function(map) {
  703. var array = [];
  704. for (var key in map) {
  705. array.push(map[key]);
  706. }
  707. return array;
  708. };
  709. /**
  710. * @private A private variant of Array.prototype.map that supports the index
  711. * property.
  712. */
  713. function map(array, f) {
  714. var o = {};
  715. return f
  716. ? array.map(function(d, i) { o.index = i; return f.call(o, d); })
  717. : array.slice();
  718. };
  719. /**
  720. * Returns a normalized copy of the specified array, such that the sum of the
  721. * returned elements sum to one. If the specified array is not an array of
  722. * numbers, an optional accessor function <tt>f</tt> can be specified to map the
  723. * elements to numbers. For example, if <tt>array</tt> is an array of objects,
  724. * and each object has a numeric property "foo", the expression
  725. *
  726. * <pre>pv.normalize(array, function(d) d.foo)</pre>
  727. *
  728. * returns a normalized array on the "foo" property. If an accessor function is
  729. * not specified, the identity function is used. Accessor functions can refer to
  730. * <tt>this.index</tt>.
  731. *
  732. * @param {array} array an array of objects, or numbers.
  733. * @param {function} [f] an optional accessor function.
  734. * @returns {number[]} an array of numbers that sums to one.
  735. */
  736. pv.normalize = function(array, f) {
  737. var norm = map(array, f), sum = pv.sum(norm);
  738. for (var i = 0; i < norm.length; i++) norm[i] /= sum;
  739. return norm;
  740. };
  741. /**
  742. * Returns the sum of the specified array. If the specified array is not an
  743. * array of numbers, an optional accessor function <tt>f</tt> can be specified
  744. * to map the elements to numbers. See {@link #normalize} for an example.
  745. * Accessor functions can refer to <tt>this.index</tt>.
  746. *
  747. * @param {array} array an array of objects, or numbers.
  748. * @param {function} [f] an optional accessor function.
  749. * @returns {number} the sum of the specified array.
  750. */
  751. pv.sum = function(array, f) {
  752. var o = {};
  753. return array.reduce(f
  754. ? function(p, d, i) { o.index = i; return p + f.call(o, d); }
  755. : function(p, d) { return p + d; }, 0);
  756. };
  757. /**
  758. * Returns the maximum value of the specified array. If the specified array is
  759. * not an array of numbers, an optional accessor function <tt>f</tt> can be
  760. * specified to map the elements to numbers. See {@link #normalize} for an
  761. * example. Accessor functions can refer to <tt>this.index</tt>.
  762. *
  763. * @param {array} array an array of objects, or numbers.
  764. * @param {function} [f] an optional accessor function.
  765. * @returns {number} the maximum value of the specified array.
  766. */
  767. pv.max = function(array, f) {
  768. if (f == pv.index) return array.length - 1;
  769. return Math.max.apply(null, f ? map(array, f) : array);
  770. };
  771. /**
  772. * Returns the index of the maximum value of the specified array. If the
  773. * specified array is not an array of numbers, an optional accessor function
  774. * <tt>f</tt> can be specified to map the elements to numbers. See
  775. * {@link #normalize} for an example. Accessor functions can refer to
  776. * <tt>this.index</tt>.
  777. *
  778. * @param {array} array an array of objects, or numbers.
  779. * @param {function} [f] an optional accessor function.
  780. * @returns {number} the index of the maximum value of the specified array.
  781. */
  782. pv.max.index = function(array, f) {
  783. if (f == pv.index) return array.length - 1;
  784. if (!f) f = pv.identity;
  785. var maxi = -1, maxx = -Infinity, o = {};
  786. for (var i = 0; i < array.length; i++) {
  787. o.index = i;
  788. var x = f.call(o, array[i]);
  789. if (x > maxx) {
  790. maxx = x;
  791. maxi = i;
  792. }
  793. }
  794. return maxi;
  795. }
  796. /**
  797. * Returns the minimum value of the specified array of numbers. If the specified
  798. * array is not an array of numbers, an optional accessor function <tt>f</tt>
  799. * can be specified to map the elements to numbers. See {@link #normalize} for
  800. * an example. Accessor functions can refer to <tt>this.index</tt>.
  801. *
  802. * @param {array} array an array of objects, or numbers.
  803. * @param {function} [f] an optional accessor function.
  804. * @returns {number} the minimum value of the specified array.
  805. */
  806. pv.min = function(array, f) {
  807. if (f == pv.index) return 0;
  808. return Math.min.apply(null, f ? map(array, f) : array);
  809. };
  810. /**
  811. * Returns the index of the minimum value of the specified array. If the
  812. * specified array is not an array of numbers, an optional accessor function
  813. * <tt>f</tt> can be specified to map the elements to numbers. See
  814. * {@link #normalize} for an example. Accessor functions can refer to
  815. * <tt>this.index</tt>.
  816. *
  817. * @param {array} array an array of objects, or numbers.
  818. * @param {function} [f] an optional accessor function.
  819. * @returns {number} the index of the minimum value of the specified array.
  820. */
  821. pv.min.index = function(array, f) {
  822. if (f == pv.index) return 0;
  823. if (!f) f = pv.identity;
  824. var mini = -1, minx = Infinity, o = {};
  825. for (var i = 0; i < array.length; i++) {
  826. o.index = i;
  827. var x = f.call(o, array[i]);
  828. if (x < minx) {
  829. minx = x;
  830. mini = i;
  831. }
  832. }
  833. return mini;
  834. }
  835. /**
  836. * Returns the arithmetic mean, or average, of the specified array. If the
  837. * specified array is not an array of numbers, an optional accessor function
  838. * <tt>f</tt> can be specified to map the elements to numbers. See
  839. * {@link #normalize} for an example. Accessor functions can refer to
  840. * <tt>this.index</tt>.
  841. *
  842. * @param {array} array an array of objects, or numbers.
  843. * @param {function} [f] an optional accessor function.
  844. * @returns {number} the mean of the specified array.
  845. */
  846. pv.mean = function(array, f) {
  847. return pv.sum(array, f) / array.length;
  848. };
  849. /**
  850. * Returns the median of the specified array. If the specified array is not an
  851. * array of numbers, an optional accessor function <tt>f</tt> can be specified
  852. * to map the elements to numbers. See {@link #normalize} for an example.
  853. * Accessor functions can refer to <tt>this.index</tt>.
  854. *
  855. * @param {array} array an array of objects, or numbers.
  856. * @param {function} [f] an optional accessor function.
  857. * @returns {number} the median of the specified array.
  858. */
  859. pv.median = function(array, f) {
  860. if (f == pv.index) return (array.length - 1) / 2;
  861. array = map(array, f).sort(pv.naturalOrder);
  862. if (array.length % 2) return array[Math.floor(array.length / 2)];
  863. var i = array.length / 2;
  864. return (array[i - 1] + array[i]) / 2;
  865. };
  866. /**
  867. * Returns a map constructed from the specified <tt>keys</tt>, using the
  868. * function <tt>f</tt> to compute the value for each key. The single argument to
  869. * the value function is the key. The callback is invoked only for indexes of
  870. * the array which have assigned values; it is not invoked for indexes which
  871. * have been deleted or which have never been assigned values.
  872. *
  873. * <p>For example, this expression creates a map from strings to string length:
  874. *
  875. * <pre>pv.dict(["one", "three", "seventeen"], function(s) s.length)</pre>
  876. *
  877. * The returned value is <tt>{one: 3, three: 5, seventeen: 9}</tt>. Accessor
  878. * functions can refer to <tt>this.index</tt>.
  879. *
  880. * @param {array} keys an array.
  881. * @param {function} f a value function.
  882. * @returns a map from keys to values.
  883. */
  884. pv.dict = function(keys, f) {
  885. var m = {}, o = {};
  886. for (var i = 0; i < keys.length; i++) {
  887. if (i in keys) {
  888. var k = keys[i];
  889. o.index = i;
  890. m[k] = f.call(o, k);
  891. }
  892. }
  893. return m;
  894. };
  895. /**
  896. * Returns a permutation of the specified array, using the specified array of
  897. * indexes. The returned array contains the corresponding element in
  898. * <tt>array</tt> for each index in <tt>indexes</tt>, in order. For example,
  899. *
  900. * <pre>pv.permute(["a", "b", "c"], [1, 2, 0])</pre>
  901. *
  902. * returns <tt>["b", "c", "a"]</tt>. It is acceptable for the array of indexes
  903. * to be a different length from the array of elements, and for indexes to be
  904. * duplicated or omitted. The optional accessor function <tt>f</tt> can be used
  905. * to perform a simultaneous mapping of the array elements. Accessor functions
  906. * can refer to <tt>this.index</tt>.
  907. *
  908. * @param {array} array an array.
  909. * @param {number[]} indexes an array of indexes into <tt>array</tt>.
  910. * @param {function} [f] an optional accessor function.
  911. * @returns {array} an array of elements from <tt>array</tt>; a permutation.
  912. */
  913. pv.permute = function(array, indexes, f) {
  914. if (!f) f = pv.identity;
  915. var p = new Array(indexes.length), o = {};
  916. indexes.forEach(function(j, i) { o.index = j; p[i] = f.call(o, array[j]); });
  917. return p;
  918. };
  919. /**
  920. * Returns a map from key to index for the specified <tt>keys</tt> array. For
  921. * example,
  922. *
  923. * <pre>pv.numerate(["a", "b", "c"])</pre>
  924. *
  925. * returns <tt>{a: 0, b: 1, c: 2}</tt>. Note that since JavaScript maps only
  926. * support string keys, <tt>keys</tt> must contain strings, or other values that
  927. * naturally map to distinct string values. Alternatively, an optional accessor
  928. * function <tt>f</tt> can be specified to compute the string key for the given
  929. * element. Accessor functions can refer to <tt>this.index</tt>.
  930. *
  931. * @param {array} keys an array, usually of string keys.
  932. * @param {function} [f] an optional key function.
  933. * @returns a map from key to index.
  934. */
  935. pv.numerate = function(keys, f) {
  936. if (!f) f = pv.identity;
  937. var map = {}, o = {};
  938. keys.forEach(function(x, i) { o.index = i; map[f.call(o, x)] = i; });
  939. return map;
  940. };
  941. /**
  942. * The comparator function for natural order. This can be used in conjunction with
  943. * the built-in array <tt>sort</tt> method to sort elements by their natural
  944. * order, ascending. Note that if no comparator function is specified to the
  945. * built-in <tt>sort</tt> method, the default order is lexicographic, <i>not</i>
  946. * natural!
  947. *
  948. * @see <a
  949. * href="http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Array/sort">Array.sort</a>.
  950. * @param a an element to compare.
  951. * @param b an element to compare.
  952. * @returns {number} negative if a &lt; b; positive if a &gt; b; otherwise 0.
  953. */
  954. pv.naturalOrder = function(a, b) {
  955. return (a < b) ? -1 : ((a > b) ? 1 : 0);
  956. };
  957. /**
  958. * The comparator function for reverse natural order. This can be used in
  959. * conjunction with the built-in array <tt>sort</tt> method to sort elements by
  960. * their natural order, descending. Note that if no comparator function is
  961. * specified to the built-in <tt>sort</tt> method, the default order is
  962. * lexicographic, <i>not</i> natural!
  963. *
  964. * @see #naturalOrder
  965. * @param a an element to compare.
  966. * @param b an element to compare.
  967. * @returns {number} negative if a &lt; b; positive if a &gt; b; otherwise 0.
  968. */
  969. pv.reverseOrder = function(b, a) {
  970. return (a < b) ? -1 : ((a > b) ? 1 : 0);
  971. };
  972. /**
  973. * @private Computes the value of the specified CSS property <tt>p</tt> on the
  974. * specified element <tt>e</tt>.
  975. *
  976. * @param {string} p the name of the CSS property.
  977. * @param e the element on which to compute the CSS property.
  978. */
  979. pv.css = function(e, p) {
  980. return window.getComputedStyle
  981. ? window.getComputedStyle(e, null).getPropertyValue(p)
  982. : e.currentStyle[p];
  983. };
  984. /**
  985. * Namespace constants for SVG, XMLNS, and XLINK.
  986. *
  987. * @namespace Namespace constants for SVG, XMLNS, and XLINK.
  988. */
  989. pv.ns = {
  990. /**
  991. * The SVG namespace, "http://www.w3.org/2000/svg".
  992. *
  993. * @type string
  994. * @constant
  995. */
  996. svg: "http://www.w3.org/2000/svg",
  997. /**
  998. * The XMLNS namespace, "http://www.w3.org/2000/xmlns".
  999. *
  1000. * @type string
  1001. * @constant
  1002. */
  1003. xmlns: "http://www.w3.org/2000/xmlns",
  1004. /**
  1005. * The XLINK namespace, "http://www.w3.org/1999/xlink".
  1006. *
  1007. * @type string
  1008. * @constant
  1009. */
  1010. xlink: "http://www.w3.org/1999/xlink"
  1011. };
  1012. /**
  1013. * Protovis major and minor version numbers.
  1014. *
  1015. * @namespace Protovis major and minor version numbers.
  1016. */
  1017. pv.version = {
  1018. /**
  1019. * The major version number.
  1020. *
  1021. * @type number
  1022. * @constant
  1023. */
  1024. major: 3,
  1025. /**
  1026. * The minor version number.
  1027. *
  1028. * @type number
  1029. * @constant
  1030. */
  1031. minor: 1
  1032. };
  1033. /**
  1034. * @private Reports the specified error to the JavaScript console. Mozilla only
  1035. * allows logging to the console for privileged code; if the console is
  1036. * unavailable, the alert dialog box is used instead.
  1037. *
  1038. * @param e the exception that triggered the error.
  1039. */
  1040. pv.error = function(e) {
  1041. (typeof console == "undefined") ? alert(e) : console.error(e);
  1042. };
  1043. /**
  1044. * @private Registers the specified listener for events of the specified type on
  1045. * the specified target. For standards-compliant browsers, this method uses
  1046. * <tt>addEventListener</tt>; for Internet Explorer, <tt>attachEvent</tt>.
  1047. *
  1048. * @param target a DOM element.
  1049. * @param {string} type the type of event, such as "click".
  1050. * @param {function} the listener callback function.
  1051. */
  1052. pv.listen = function(target, type, listener) {
  1053. return target.addEventListener
  1054. ? target.addEventListener(type, listener, false)
  1055. : target.attachEvent("on" + type, listener);
  1056. };
  1057. /**
  1058. * Returns the logarithm with a given base value.
  1059. *
  1060. * @param {number} x the number for which to compute the logarithm.
  1061. * @param {number} b the base of the logarithm.
  1062. * @returns {number} the logarithm value.
  1063. */
  1064. pv.log = function(x, b) {
  1065. return Math.log(x) / Math.log(b);
  1066. };
  1067. /**
  1068. * Computes a zero-symmetric logarithm. Computes the logarithm of the absolute
  1069. * value of the input, and determines the sign of the output according to the
  1070. * sign of the input value.
  1071. *
  1072. * @param {number} x the number for which to compute the logarithm.
  1073. * @param {number} b the base of the logarithm.
  1074. * @returns {number} the symmetric log value.
  1075. */
  1076. pv.logSymmetric = function(x, b) {
  1077. return (x == 0) ? 0 : ((x < 0) ? -pv.log(-x, b) : pv.log(x, b));
  1078. };
  1079. /**
  1080. * Computes a zero-symmetric logarithm, with adjustment to values between zero
  1081. * and the logarithm base. This adjustment introduces distortion for values less
  1082. * than the base number, but enables simultaneous plotting of log-transformed
  1083. * data involving both positive and negative numbers.
  1084. *
  1085. * @param {number} x the number for which to compute the logarithm.
  1086. * @param {number} b the base of the logarithm.
  1087. * @returns {number} the adjusted, symmetric log value.
  1088. */
  1089. pv.logAdjusted = function(x, b) {
  1090. var negative = x < 0;
  1091. if (x < b) x += (b - x) / b;
  1092. return negative ? -pv.log(x, b) : pv.log(x, b);
  1093. };
  1094. /**
  1095. * Rounds an input value down according to its logarithm. The method takes the
  1096. * floor of the logarithm of the value and then uses the resulting value as an
  1097. * exponent for the base value.
  1098. *
  1099. * @param {number} x the number for which to compute the logarithm floor.
  1100. * @param {number} b the base of the logarithm.
  1101. * @return {number} the rounded-by-logarithm value.
  1102. */
  1103. pv.logFloor = function(x, b) {
  1104. return (x > 0)
  1105. ? Math.pow(b, Math.floor(pv.log(x, b)))
  1106. : -Math.pow(b, -Math.floor(-pv.log(-x, b)));
  1107. };
  1108. /**
  1109. * Rounds an input value up according to its logarithm. The method takes the
  1110. * ceiling of the logarithm of the value and then uses the resulting value as an
  1111. * exponent for the base value.
  1112. *
  1113. * @param {number} x the number for which to compute the logarithm ceiling.
  1114. * @param {number} b the base of the logarithm.
  1115. * @return {number} the rounded-by-logarithm value.
  1116. */
  1117. pv.logCeil = function(x, b) {
  1118. return (x > 0)
  1119. ? Math.pow(b, Math.ceil(pv.log(x, b)))
  1120. : -Math.pow(b, -Math.ceil(-pv.log(-x, b)));
  1121. };
  1122. /**
  1123. * Searches the specified array of numbers for the specified value using the
  1124. * binary search algorithm. The array must be sorted (as by the <tt>sort</tt>
  1125. * method) prior to making this call. If it is not sorted, the results are
  1126. * undefined. If the array contains multiple elements with the specified value,
  1127. * there is no guarantee which one will be found.
  1128. *
  1129. * <p>The <i>insertion point</i> is defined as the point at which the value
  1130. * would be inserted into the array: the index of the first element greater than
  1131. * the value, or <tt>array.length</tt>, if all elements in the array are less
  1132. * than the specified value. Note that this guarantees that the return value
  1133. * will be nonnegative if and only if the value is found.
  1134. *
  1135. * @param {number[]} array the array to be searched.
  1136. * @param {number} value the value to be searched for.
  1137. * @returns the index of the search value, if it is contained in the array;
  1138. * otherwise, (-(<i>insertion point</i>) - 1).
  1139. * @param {function} [f] an optional key function.
  1140. */
  1141. pv.search = function(array, value, f) {
  1142. if (!f) f = pv.identity;
  1143. var low = 0, high = array.length - 1;
  1144. while (low <= high) {
  1145. var mid = (low + high) >> 1, midValue = f(array[mid]);
  1146. if (midValue < value) low = mid + 1;
  1147. else if (midValue > value) high = mid - 1;
  1148. else return mid;
  1149. }
  1150. return -low - 1;
  1151. };
  1152. pv.search.index = function(array, value, f) {
  1153. var i = pv.search(array, value, f);
  1154. return (i < 0) ? (-i - 1) : i;
  1155. };
  1156. /**
  1157. * Returns a {@link pv.Tree} operator for the specified array. This is a
  1158. * convenience factory method, equivalent to <tt>new pv.Tree(array)</tt>.
  1159. *
  1160. * @see pv.Tree
  1161. * @param {array} array an array from which to construct a tree.
  1162. * @returns {pv.Tree} a tree operator for the specified array.
  1163. */
  1164. pv.tree = function(array) {
  1165. return new pv.Tree(array);
  1166. };
  1167. /**
  1168. * Constructs a tree operator for the specified array. This constructor should
  1169. * not be invoked directly; use {@link pv.tree} instead.
  1170. *
  1171. * @class Represents a tree operator for the specified array. The tree operator
  1172. * allows a hierarchical map to be constructed from an array; it is similar to
  1173. * the {@link pv.Nest} operator, except the hierarchy is derived dynamically
  1174. * from the array elements.
  1175. *
  1176. * <p>For example, given an array of size information for ActionScript classes:
  1177. *
  1178. * <pre>{ name: "flare.flex.FlareVis", size: 4116 },
  1179. * { name: "flare.physics.DragForce", size: 1082 },
  1180. * { name: "flare.physics.GravityForce", size: 1336 }, ...</pre>
  1181. *
  1182. * To facilitate visualization, it may be useful to nest the elements by their
  1183. * package hierarchy:
  1184. *
  1185. * <pre>var tree = pv.tree(classes)
  1186. * .keys(function(d) d.name.split("."))
  1187. * .map();</pre>
  1188. *
  1189. * The resulting tree is:
  1190. *
  1191. * <pre>{ flare: {
  1192. * flex: {
  1193. * FlareVis: {
  1194. * name: "flare.flex.FlareVis",
  1195. * size: 4116 } },
  1196. * physics: {
  1197. * DragForce: {
  1198. * name: "flare.physics.DragForce",
  1199. * size: 1082 },
  1200. * GravityForce: {
  1201. * name: "flare.physics.GravityForce",
  1202. * size: 1336 } },
  1203. * ... } }</pre>
  1204. *
  1205. * By specifying a value function,
  1206. *
  1207. * <pre>var tree = pv.tree(classes)
  1208. * .keys(function(d) d.name.split("."))
  1209. * .value(function(d) d.size)
  1210. * .map();</pre>
  1211. *
  1212. * we can further eliminate redundant data:
  1213. *
  1214. * <pre>{ flare: {
  1215. * flex: {
  1216. * FlareVis: 4116 },
  1217. * physics: {
  1218. * DragForce: 1082,
  1219. * GravityForce: 1336 },
  1220. * ... } }</pre>
  1221. *
  1222. * For visualizations with large data sets, performance improvements may be seen
  1223. * by storing the data in a tree format, and then flattening it into an array at
  1224. * runtime with {@link pv.Flatten}.
  1225. *
  1226. * @param {array} array an array from which to construct a tree.
  1227. */
  1228. pv.Tree = function(array) {
  1229. this.array = array;
  1230. };
  1231. /**
  1232. * Assigns a <i>keys</i> function to this operator; required. The keys function
  1233. * returns an array of <tt>string</tt>s for each element in the associated
  1234. * array; these keys determine how the elements are nested in the tree. The
  1235. * returned keys should be unique for each element in the array; otherwise, the
  1236. * behavior of this operator is undefined.
  1237. *
  1238. * @param {function} k the keys function.
  1239. * @returns {pv.Tree} this.
  1240. */
  1241. pv.Tree.prototype.keys = function(k) {
  1242. this.k = k;
  1243. return this;
  1244. };
  1245. /**
  1246. * Assigns a <i>value</i> function to this operator; optional. The value
  1247. * function specifies an optional transformation of the element in the array
  1248. * before it is inserted into the map. If no value function is specified, it is
  1249. * equivalent to using the identity function.
  1250. *
  1251. * @param {function} k the value function.
  1252. * @returns {pv.Tree} this.
  1253. */
  1254. pv.Tree.prototype.value = function(v) {
  1255. this.v = v;
  1256. return this;
  1257. };
  1258. /**
  1259. * Returns a hierarchical map of values. The hierarchy is determined by the keys
  1260. * function; the values in the map are determined by the value function.
  1261. *
  1262. * @returns a hierarchical map of values.
  1263. */
  1264. pv.Tree.prototype.map = function() {
  1265. var map = {}, o = {};
  1266. for (var i = 0; i < this.array.length; i++) {
  1267. o.index = i;
  1268. var value = this.array[i], keys = this.k.call(o, value), node = map;
  1269. for (var j = 0; j < keys.length - 1; j++) {
  1270. node = node[keys[j]] || (node[keys[j]] = {});
  1271. }
  1272. node[keys[j]] = this.v ? this.v.call(o, value) : value;
  1273. }
  1274. return map;
  1275. };
  1276. /**
  1277. * Returns a {@link pv.Nest} operator for the specified array. This is a
  1278. * convenience factory method, equivalent to <tt>new pv.Nest(array)</tt>.
  1279. *
  1280. * @see pv.Nest
  1281. * @param {array} array an array of elements to nest.
  1282. * @returns {pv.Nest} a nest operator for the specified array.
  1283. */
  1284. pv.nest = function(array) {
  1285. return new pv.Nest(array);
  1286. };
  1287. /**
  1288. * Constructs a nest operator for the specified array. This constructor should
  1289. * not be invoked directly; use {@link pv.nest} instead.
  1290. *
  1291. * @class Represents a {@link Nest} operator for the specified array. Nesting
  1292. * allows elements in an array to be grouped into a hierarchical tree
  1293. * structure. The levels in the tree are specified by <i>key</i> functions. The
  1294. * leaf nodes of the tree can be sorted by value, while the internal nodes can
  1295. * be sorted by key. Finally, the tree can be returned either has a
  1296. * multidimensional array via {@link #entries}, or as a hierarchical map via
  1297. * {@link #map}. The {@link #rollup} routine similarly returns a map, collapsing
  1298. * the elements in each leaf node using a summary function.
  1299. *
  1300. * <p>For example, consider the following tabular data structure of Barley
  1301. * yields, from various sites in Minnesota during 1931-2:
  1302. *
  1303. * <pre>{ yield: 27.00, variety: "Manchuria", year: 1931, site: "University Farm" },
  1304. * { yield: 48.87, variety: "Manchuria", year: 1931, site: "Waseca" },
  1305. * { yield: 27.43, variety: "Manchuria", year: 1931, site: "Morris" }, ...</pre>
  1306. *
  1307. * To facilitate visualization, it may be useful to nest the elements first by
  1308. * year, and then by variety, as follows:
  1309. *
  1310. * <pre>var nest = pv.nest(yields)
  1311. * .key(function(d) d.year)
  1312. * .key(function(d) d.variety)
  1313. * .entries();</pre>
  1314. *
  1315. * This returns a nested array. Each element of the outer array is a key-values
  1316. * pair, listing the values for each distinct key:
  1317. *
  1318. * <pre>{ key: 1931, values: [
  1319. * { key: "Manchuria", values: [
  1320. * { yield: 27.00, variety: "Manchuria", year: 1931, site: "University Farm" },
  1321. * { yield: 48.87, variety: "Manchuria", year: 1931, site: "Waseca" },
  1322. * { yield: 27.43, variety: "Manchuria", year: 1931, site: "Morris" },
  1323. * ...
  1324. * ] },
  1325. * { key: "Glabron", values: [
  1326. * { yield: 43.07, variety: "Glabron", year: 1931, site: "University Farm" },
  1327. * { yield: 55.20, variety: "Glabron", year: 1931, site: "Waseca" },
  1328. * ...
  1329. * ] },
  1330. * ] },
  1331. * { key: 1932, values: ... }</pre>
  1332. *
  1333. * Further details, including sorting and rollup, is provided below on the
  1334. * corresponding methods.
  1335. *
  1336. * @param {array} array an array of elements to nest.
  1337. */
  1338. pv.Nest = function(array) {
  1339. this.array = array;
  1340. this.keys = [];
  1341. };
  1342. /**
  1343. * Nests using the specified key function. Multiple keys may be added to the
  1344. * nest; the array elements will be nested in the order keys are specified.
  1345. *
  1346. * @param {function} key a key function; must return a string or suitable map
  1347. * key.
  1348. * @return {pv.Nest} this.
  1349. */
  1350. pv.Nest.prototype.key = function(key) {
  1351. this.keys.push(key);
  1352. return this;
  1353. };
  1354. /**
  1355. * Sorts the previously-added keys. The natural sort order is used by default
  1356. * (see {@link pv.naturalOrder}); if an alternative order is desired,
  1357. * <tt>order</tt> should be a comparator function. If this method is not called
  1358. * (i.e., keys are <i>unsorted</i>), keys will appear in the order they appear
  1359. * in the underlying elements array. For example,
  1360. *
  1361. * <pre>pv.nest(yields)
  1362. * .key(function(d) d.year)
  1363. * .key(function(d) d.variety)
  1364. * .sortKeys()
  1365. * .entries()</pre>
  1366. *
  1367. * groups yield data by year, then variety, and sorts the variety groups
  1368. * lexicographically (since the variety attribute is a string).
  1369. *
  1370. * <p>Key sort order is only used in conjunction with {@link #entries}, which
  1371. * returns an array of key-values pairs. If the nest is used to construct a
  1372. * {@link #map} instead, keys are unsorted.
  1373. *
  1374. * @param {function} [order] an optional comparator function.
  1375. * @returns {pv.Nest} this.
  1376. */
  1377. pv.Nest.prototype.sortKeys = function(order) {
  1378. this.keys[this.keys.length - 1].order = order || pv.naturalOrder;
  1379. return this;
  1380. };
  1381. /**
  1382. * Sorts the leaf values. The natural sort order is used by default (see
  1383. * {@link pv.naturalOrder}); if an alternative order is desired, <tt>order</tt>
  1384. * should be a comparator function. If this method is not called (i.e., values
  1385. * are <i>unsorted</i>), values will appear in the order they appear in the
  1386. * underlying elements array. For example,
  1387. *
  1388. * <pre>pv.nest(yields)
  1389. * .key(function(d) d.year)
  1390. * .key(function(d) d.variety)
  1391. * .sortValues(function(a, b) a.yield - b.yield)
  1392. * .entries()</pre>
  1393. *
  1394. * groups yield data by year, then variety, and sorts the values for each
  1395. * variety group by yield.
  1396. *
  1397. * <p>Value sort order, unlike keys, applies to both {@link #entries} and
  1398. * {@link #map}. It has no effect on {@link #rollup}.
  1399. *
  1400. * @param {function} [order] an optional comparator function.
  1401. * @return {pv.Nest} this.
  1402. */
  1403. pv.Nest.prototype.sortValues = function(order) {
  1404. this.order = order || pv.naturalOrder;
  1405. return this;
  1406. };
  1407. /**
  1408. * Returns a hierarchical map of values. Each key adds one level to the
  1409. * hierarchy. With only a single key, the returned map will have a key for each
  1410. * distinct value of the key function; the correspond value with be an array of
  1411. * elements with that key value. If a second key is added, this will be a nested
  1412. * map. For example:
  1413. *
  1414. * <pre>pv.nest(yields)
  1415. * .key(function(d) d.variety)
  1416. * .key(function(d) d.site)
  1417. * .map()</pre>
  1418. *
  1419. * returns a map <tt>m</tt> such that <tt>m[variety][site]</tt> is an array, a subset of
  1420. * <tt>yields</tt>, with each element having the given variety and site.
  1421. *
  1422. * @returns a hierarchical map of values.
  1423. */
  1424. pv.Nest.prototype.map = function() {
  1425. var map = {}, values = [];
  1426. /* Build the map. */
  1427. for (var i, j = 0; j < this.array.length; j++) {
  1428. var x = this.array[j];
  1429. var m = map;
  1430. for (i = 0; i < this.keys.length - 1; i++) {
  1431. var k = this.keys[i](x);
  1432. if (!m[k]) m[k] = {};
  1433. m = m[k];
  1434. }
  1435. k = this.keys[i](x);
  1436. if (!m[k]) {
  1437. var a = [];
  1438. values.push(a);
  1439. m[k] = a;
  1440. }
  1441. m[k].push(x);
  1442. }
  1443. /* Sort each leaf array. */
  1444. if (this.order) {
  1445. for (var i = 0; i < values.length; i++) {
  1446. values[i].sort(this.order);
  1447. }
  1448. }
  1449. return map;
  1450. };
  1451. /**
  1452. * Returns a hierarchical nested array. This method is similar to
  1453. * {@link pv.entries}, but works recursively on the entire hierarchy. Rather
  1454. * than returning a map like {@link #map}, this method returns a nested
  1455. * array. Each element of the array has a <tt>key</tt> and <tt>values</tt>
  1456. * field. For leaf nodes, the <tt>values</tt> array will be a subset of the
  1457. * underlying elements array; for non-leaf nodes, the <tt>values</tt> array will
  1458. * contain more key-values pairs.
  1459. *
  1460. * <p>For an example usage, see the {@link Nest} constructor.
  1461. *
  1462. * @returns a hierarchical nested array.
  1463. */
  1464. pv.Nest.prototype.entries = function() {
  1465. /** Recursively extracts the entries for the given map. */
  1466. function entries(map) {
  1467. var array = [];
  1468. for (var k in map) {
  1469. var v = map[k];
  1470. array.push({ key: k, values: (v instanceof Array) ? v : entries(v) });
  1471. };
  1472. return array;
  1473. }
  1474. /** Recursively sorts the values for the given key-values array. */
  1475. function sort(array, i) {
  1476. var o = this.keys[i].order;
  1477. if (o) array.sort(function(a, b) { return o(a.key, b.key); });
  1478. if (++i < this.keys.length) {
  1479. for (var j = 0; j < array.length; j++) {
  1480. sort.call(this, array[j].values, i);
  1481. }
  1482. }
  1483. return array;
  1484. }
  1485. return sort.call(this, entries(this.map()), 0);
  1486. };
  1487. /**
  1488. * Returns a rollup map. The behavior of this method is the same as
  1489. * {@link #map}, except that the leaf values are replaced with the return value
  1490. * of the specified rollup function <tt>f</tt>. For example,
  1491. *
  1492. * <pre>pv.nest(yields)
  1493. * .key(function(d) d.site)
  1494. * .rollup(function(v) pv.median(v, function(d) d.yield))</pre>
  1495. *
  1496. * first groups yield data by site, and then returns a map from site to median
  1497. * yield for the given site.
  1498. *
  1499. * @see #map
  1500. * @param {function} f a rollup function.
  1501. * @returns a hierarchical map, with the leaf values computed by <tt>f</tt>.
  1502. */
  1503. pv.Nest.prototype.rollup = function(f) {
  1504. /** Recursively descends to the leaf nodes (arrays) and does rollup. */
  1505. function rollup(map) {
  1506. for (var key in map) {
  1507. var value = map[key];
  1508. if (value instanceof Array) {
  1509. map[key] = f(value);
  1510. } else {
  1511. rollup(value);
  1512. }
  1513. }
  1514. return map;
  1515. }
  1516. return rollup(this.map());
  1517. };
  1518. /**
  1519. * Returns a {@link pv.Flatten} operator for the specified map. This is a
  1520. * convenience factory method, equivalent to <tt>new pv.Flatten(map)</tt>.
  1521. *
  1522. * @see pv.Flatten
  1523. * @param map a map to flatten.
  1524. * @returns {pv.Flatten} a flatten operator for the specified map.
  1525. */
  1526. pv.flatten = function(map) {
  1527. return new pv.Flatten(map);
  1528. };
  1529. /**
  1530. * Constructs a flatten operator for the specified map. This constructor should
  1531. * not be invoked directly; use {@link pv.flatten} instead.
  1532. *
  1533. * @class Represents a flatten operator for the specified array. Flattening
  1534. * allows hierarchical maps to be flattened into an array. The levels in the
  1535. * input tree are specified by <i>key</i> functions.
  1536. *
  1537. * <p>For example, consider the following hierarchical data structure of Barley
  1538. * yields, from various sites in Minnesota during 1931-2:
  1539. *
  1540. * <pre>{ 1931: {
  1541. * Manchuria: {
  1542. * "University Farm": 27.00,
  1543. * "Waseca": 48.87,
  1544. * "Morris": 27.43,
  1545. * ... },
  1546. * Glabron: {
  1547. * "University Farm": 43.07,
  1548. * "Waseca": 55.20,
  1549. * ... } },
  1550. * 1932: {
  1551. * ... } }</pre>
  1552. *
  1553. * To facilitate visualization, it may be useful to flatten the tree into a
  1554. * tabular array:
  1555. *
  1556. * <pre>var array = pv.flatten(yields)
  1557. * .key("year")
  1558. * .key("variety")
  1559. * .key("site")
  1560. * .key("yield")
  1561. * .array();</pre>
  1562. *
  1563. * This returns an array of object elements. Each element in the array has
  1564. * attributes corresponding to this flatten operator's keys:
  1565. *
  1566. * <pre>{ site: "University Farm", variety: "Manchuria", year: 1931, yield: 27 },
  1567. * { site: "Waseca", variety: "Manchuria", year: 1931, yield: 48.87 },
  1568. * { site: "Morris", variety: "Manchuria", year: 1931, yield: 27.43 },
  1569. * { site: "University Farm", variety: "Glabron", year: 1931, yield: 43.07 },
  1570. * { site: "Waseca", variety: "Glabron", year: 1931, yield: 55.2 }, ...</pre>
  1571. *
  1572. * <p>The flatten operator is roughly the inverse of the {@link pv.Nest} and
  1573. * {@link pv.Tree} operators.
  1574. *
  1575. * @param map a map to flatten.
  1576. */
  1577. pv.Flatten = function(map) {
  1578. this.map = map;
  1579. this.keys = [];
  1580. };
  1581. /**
  1582. * Flattens using the specified key function. Multiple keys may be added to the
  1583. * flatten; the tiers of the underlying tree must correspond to the specified
  1584. * keys, in order. The order of the returned array is undefined; however, you
  1585. * can easily sort it.
  1586. *
  1587. * @param {string} key the key name.
  1588. * @param {function} [f] an optional value map function.
  1589. * @return {pv.Nest} this.
  1590. */
  1591. pv.Flatten.prototype.key = function(key, f) {
  1592. this.keys.push({name: key, value: f});
  1593. return this;
  1594. };
  1595. /**
  1596. * Returns the flattened array. Each entry in the array is an object; each
  1597. * object has attributes corresponding to this flatten operator's keys.
  1598. *
  1599. * @returns an array of elements from the flattened map.
  1600. */
  1601. pv.Flatten.prototype.array = function() {
  1602. var entries = [], stack = [], keys = this.keys;
  1603. /* Recursively visits the specified value. */
  1604. function visit(value, i) {
  1605. if (i < keys.length - 1) {
  1606. for (var key in value) {
  1607. stack.push(key);
  1608. visit(value[key], i + 1);
  1609. stack.pop();
  1610. }
  1611. } else {
  1612. entries.push(stack.concat(value));
  1613. }
  1614. }
  1615. visit(this.map, 0);
  1616. return entries.map(function(stack) {
  1617. var m = {};
  1618. for (var i = 0; i < keys.length; i++) {
  1619. var k = keys[i], v = stack[i];
  1620. m[k.name] = k.value ? k.value.call(null, v) : v;
  1621. }
  1622. return m;
  1623. });
  1624. };
  1625. /**
  1626. * Returns a {@link pv.Vector} for the specified <i>x</i> and <i>y</i>
  1627. * coordinate. This is a convenience factory method, equivalent to <tt>new
  1628. * pv.Vector(x, y)</tt>.
  1629. *
  1630. * @see pv.Vector
  1631. * @param {number} x the <i>x</i> coordinate.
  1632. * @param {number} y the <i>y</i> coordinate.
  1633. * @returns {pv.Vector} a vector for the specified coordinates.
  1634. */
  1635. pv.vector = function(x, y) {
  1636. return new pv.Vector(x, y);
  1637. };
  1638. /**
  1639. * Constructs a {@link pv.Vector} for the specified <i>x</i> and <i>y</i>
  1640. * coordinate. This constructor should not be invoked directly; use
  1641. * {@link pv.vector} instead.
  1642. *
  1643. * @class Represents a two-dimensional vector; a 2-tuple <i>&#x27e8;x,
  1644. * y&#x27e9;</i>.
  1645. *
  1646. * @param {number} x the <i>x</i> coordinate.
  1647. * @param {number} y the <i>y</i> coordinate.
  1648. */
  1649. pv.Vector = function(x, y) {
  1650. this.x = x;
  1651. this.y = y;
  1652. };
  1653. /**
  1654. * Returns a vector perpendicular to this vector: <i>&#x27e8;-y, x&#x27e9;</i>.
  1655. *
  1656. * @returns {pv.Vector} a perpendicular vector.
  1657. */
  1658. pv.Vector.prototype.perp = function() {
  1659. return new pv.Vector(-this.y, this.x);
  1660. };
  1661. /**
  1662. * Returns a normalized copy of this vector: a vector with the same direction,
  1663. * but unit length. If this vector has zero length this method returns a copy of
  1664. * this vector.
  1665. *
  1666. * @returns {pv.Vector} a unit vector.
  1667. */
  1668. pv.Vector.prototype.norm = function() {
  1669. var l = this.length();
  1670. return this.times(l ? (1 / l) : 1);
  1671. };
  1672. /**
  1673. * Returns the magnitude of this vector, defined as <i>sqrt(x * x + y * y)</i>.
  1674. *
  1675. * @returns {number} a length.
  1676. */
  1677. pv.Vector.prototype.length = function() {
  1678. return Math.sqrt(this.x * this.x + this.y * this.y);
  1679. };
  1680. /**
  1681. * Returns a scaled copy of this vector: <i>&#x27e8;x * k, y * k&#x27e9;</i>.
  1682. * To perform the equivalent divide operation, use <i>1 / k</i>.
  1683. *
  1684. * @param {number} k the scale factor.
  1685. * @returns {pv.Vector} a scaled vector.
  1686. */
  1687. pv.Vector.prototype.times = function(k) {
  1688. return new pv.Vector(this.x * k, this.y * k);
  1689. };
  1690. /**
  1691. * Returns this vector plus the vector <i>v</i>: <i>&#x27e8;x + v.x, y +
  1692. * v.y&#x27e9;</i>. If only one argument is specified, it is interpreted as the
  1693. * vector <i>v</i>.
  1694. *
  1695. * @param {number} x the <i>x</i> coordinate to add.
  1696. * @param {number} y the <i>y</i> coordinate to add.
  1697. * @returns {pv.Vector} a new vector.
  1698. */
  1699. pv.Vector.prototype.plus = function(x, y) {
  1700. return (arguments.length == 1)
  1701. ? new pv.Vector(this.x + x.x, this.y + x.y)
  1702. : new pv.Vector(this.x + x, this.y + y);
  1703. };
  1704. /**
  1705. * Returns this vector minus the vector <i>v</i>: <i>&#x27e8;x - v.x, y -
  1706. * v.y&#x27e9;</i>. If only one argument is specified, it is interpreted as the
  1707. * vector <i>v</i>.
  1708. *
  1709. * @param {number} x the <i>x</i> coordinate to subtract.
  1710. * @param {number} y the <i>y</i> coordinate to subtract.
  1711. * @returns {pv.Vector} a new vector.
  1712. */
  1713. pv.Vector.prototype.minus = function(x, y) {
  1714. return (arguments.length == 1)
  1715. ? new pv.Vector(this.x - x.x, this.y - x.y)
  1716. : new pv.Vector(this.x - x, this.y - y);
  1717. };
  1718. /**
  1719. * Returns the dot product of this vector and the vector <i>v</i>: <i>x * v.x +
  1720. * y * v.y</i>. If only one argument is specified, it is interpreted as the
  1721. * vector <i>v</i>.
  1722. *
  1723. * @param {number} x the <i>x</i> coordinate to dot.
  1724. * @param {number} y the <i>y</i> coordinate to dot.
  1725. * @returns {number} a dot product.
  1726. */
  1727. pv.Vector.prototype.dot = function(x, y) {
  1728. return (arguments.length == 1)
  1729. ? this.x * x.x + this.y * x.y
  1730. : this.x * x + this.y * y;
  1731. };
  1732. // TODO code-sharing between scales
  1733. /**
  1734. * @ignore
  1735. * @class
  1736. */
  1737. pv.Scale = function() {};
  1738. /**
  1739. * @private Returns a function that interpolators from the start value to the
  1740. * end value, given a parameter <i>t</i> in [0, 1].
  1741. *
  1742. * @param start the start value.
  1743. * @param end the end value.
  1744. */
  1745. pv.Scale.interpolator = function(start, end) {
  1746. if (typeof start == "number") {
  1747. return function(t) {
  1748. return t * (end - start) + start;
  1749. };
  1750. }
  1751. /* For now, assume color. */
  1752. start = pv.color(start).rgb();
  1753. end = pv.color(end).rgb();
  1754. return function(t) {
  1755. var a = start.a * (1 - t) + end.a * t;
  1756. if (a < 1e-5) a = 0; // avoid scientific notation
  1757. return (start.a == 0) ? pv.rgb(end.r, end.g, end.b, a)
  1758. : ((end.a == 0) ? pv.rgb(start.r, start.g, start.b, a)
  1759. : pv.rgb(
  1760. Math.round(start.r * (1 - t) + end.r * t),
  1761. Math.round(start.g * (1 - t) + end.g * t),
  1762. Math.round(start.b * (1 - t) + end.b * t), a));
  1763. };
  1764. };
  1765. /**
  1766. * Returns a linear scale for the specified domain. The arguments to this
  1767. * constructor are optional, and equivalent to calling {@link #domain}.
  1768. *
  1769. * @class Represents a linear scale. <style
  1770. * type="text/css">sub{line-height:0}</style> <img src="../linear.png"
  1771. * width="180" height="175" align="right"> Most commonly, a linear scale
  1772. * represents a 1-dimensional linear transformation from a numeric domain of
  1773. * input data [<i>d<sub>0</sub></i>, <i>d<sub>1</sub></i>] to a numeric range of
  1774. * pixels [<i>r<sub>0</sub></i>, <i>r<sub>1</sub></i>]. The equation for such a
  1775. * scale is:
  1776. *
  1777. * <blockquote><i>f(x) = (x - d<sub>0</sub>) / (d<sub>1</sub> - d<sub>0</sub>) *
  1778. * (r<sub>1</sub> - r<sub>0</sub>) + r<sub>0</sub></i></blockquote>
  1779. *
  1780. * For example, a linear scale from the domain [0, 100] to range [0, 640]:
  1781. *
  1782. * <blockquote><i>f(x) = (x - 0) / (100 - 0) * (640 - 0) + 0</i><br>
  1783. * <i>f(x) = x / 100 * 640</i><br>
  1784. * <i>f(x) = x * 6.4</i><br>
  1785. * </blockquote>
  1786. *
  1787. * Thus, saying
  1788. *
  1789. * <pre>.height(function(d) d * 6.4)</pre>
  1790. *
  1791. * is identical to
  1792. *
  1793. * <pre>.height(pv.Scale.linear(0, 100).range(0, 640))</pre>
  1794. *
  1795. * As you can see, scales do not always make code smaller, but they should make
  1796. * code more explicit and easier to maintain. In addition to readability, scales
  1797. * offer several useful features:
  1798. *
  1799. * <p>1. The range can be expressed in colors, rather than pixels. Changing the
  1800. * example above to
  1801. *
  1802. * <pre>.fillStyle(pv.Scale.linear(0, 100).range("red", "green"))</pre>
  1803. *
  1804. * will cause it to fill the marks "red" on an input value of 0, "green" on an
  1805. * input value of 100, and some color in-between for intermediate values.
  1806. *
  1807. * <p>2. The domain and range can be subdivided for a "poly-linear"
  1808. * transformation. For example, you may want a diverging color scale that is
  1809. * increasingly red for negative values, and increasingly green for positive
  1810. * values:
  1811. *
  1812. * <pre>.fillStyle(pv.Scale.linear(-1, 0, 1).range("red", "white", "green"))</pre>
  1813. *
  1814. * The domain can be specified as a series of <i>n</i> monotonically-increasing
  1815. * values; the range must also be specified as <i>n</i> values, resulting in
  1816. * <i>n - 1</i> contiguous linear scales.
  1817. *
  1818. * <p>3. Linear scales can be inverted for interaction. The {@link #invert}
  1819. * method takes a value in the output range, and returns the corresponding value
  1820. * in the input domain. This is frequently used to convert the mouse location
  1821. * (see {@link pv.Mark#mouse}) to a value in the input domain. Note that
  1822. * inversion is only supported for numeric ranges, and not colors.
  1823. *
  1824. * <p>4. A scale can be queried for reasonable "tick" values. The {@link #ticks}
  1825. * method provides a convenient way to get a series of evenly-spaced rounded
  1826. * values in the input domain. Frequently these are used in conjunction with
  1827. * {@link pv.Rule} to display tick marks or grid lines.
  1828. *
  1829. * <p>5. A scale can be "niced" to extend the domain to suitable rounded
  1830. * numbers. If the minimum and maximum of the domain are messy because they are
  1831. * derived from data, you can use {@link #nice} to round these values down and
  1832. * up to even numbers.
  1833. *
  1834. * @param {number...} domain... domain values.
  1835. * @returns {pv.Scale.linear} a linear scale.
  1836. */
  1837. pv.Scale.linear = function() {
  1838. var d = [0, 1], r = [0, 1], i = [pv.identity], precision = 0;
  1839. /** @private */
  1840. function scale(x) {
  1841. var j = pv.search(d, x);
  1842. if (j < 0) j = -j - 2;
  1843. j = Math.max(0, Math.min(i.length - 1, j));
  1844. return i[j]((x - d[j]) / (d[j + 1] - d[j]));
  1845. }
  1846. /**
  1847. * Sets or gets the input domain. This method can be invoked several ways:
  1848. *
  1849. * <p>1. <tt>domain(min, ..., max)</tt>
  1850. *
  1851. * <p>Specifying the domain as a series of numbers is the most explicit and
  1852. * recommended approach. Most commonly, two numbers are specified: the minimum
  1853. * and maximum value. However, for a diverging scale, or other subdivided
  1854. * poly-linear scales, multiple values can be specified. Values can be derived
  1855. * from data using {@link pv.min} and {@link pv.max}. For example:
  1856. *
  1857. * <pre>.domain(0, pv.max(array))</pre>
  1858. *
  1859. * An alternative method for deriving minimum and maximum values from data
  1860. * follows.
  1861. *
  1862. * <p>2. <tt>domain(array, minf, maxf)</tt>
  1863. *
  1864. * <p>When both the minimum and maximum value are derived from data, the
  1865. * arguments to the <tt>domain</tt> method can be specified as the array of
  1866. * data, followed by zero, one or two accessor functions. For example, if the
  1867. * array of data is just an array of numbers:
  1868. *
  1869. * <pre>.domain(array)</pre>
  1870. *
  1871. * On the other hand, if the array elements are objects representing stock
  1872. * values per day, and the domain should consider the stock's daily low and
  1873. * daily high:
  1874. *
  1875. * <pre>.domain(array, function(d) d.low, function(d) d.high)</pre>
  1876. *
  1877. * The first method of setting the domain is preferred because it is more
  1878. * explicit; setting the domain using this second method should be used only
  1879. * if brevity is required.
  1880. *
  1881. * <p>3. <tt>domain()</tt>
  1882. *
  1883. * <p>Invoking the <tt>domain</tt> method with no arguments returns the
  1884. * current domain as an array of numbers.
  1885. *
  1886. * @function
  1887. * @name pv.Scale.linear.prototype.domain
  1888. * @param {number...} domain... domain values.
  1889. * @returns {pv.Scale.linear} <tt>this</tt>, or the current domain.
  1890. */
  1891. scale.domain = function(array, min, max) {
  1892. if (arguments.length) {
  1893. if (array instanceof Array) {
  1894. if (arguments.length < 2) min = pv.identity;
  1895. if (arguments.length < 3) max = min;
  1896. d = [pv.min(array, min), pv.max(array, max)];
  1897. } else {
  1898. d = Array.prototype.slice.call(arguments);
  1899. }
  1900. return this;
  1901. }
  1902. return d;
  1903. };
  1904. /**
  1905. * Sets or gets the output range. This method can be invoked several ways:
  1906. *
  1907. * <p>1. <tt>range(min, ..., max)</tt>
  1908. *
  1909. * <p>The range may be specified as a series of numbers or colors. Most
  1910. * commonly, two numbers are specified: the minimum and maximum pixel values.
  1911. * For a color scale, values may be specified as {@link pv.Color}s or
  1912. * equivalent strings. For a diverging scale, or other subdivided poly-linear
  1913. * scales, multiple values can be specified. For example:
  1914. *
  1915. * <pre>.range("red", "white", "green")</pre>
  1916. *
  1917. * <p>Currently, only numbers and colors are supported as range values. The
  1918. * number of range values must exactly match the number of domain values, or
  1919. * the behavior of the scale is undefined.
  1920. *
  1921. * <p>2. <tt>range()</tt>
  1922. *
  1923. * <p>Invoking the <tt>range</tt> method with no arguments returns the current
  1924. * range as an array of numbers or colors.
  1925. *
  1926. * @function
  1927. * @name pv.Scale.linear.prototype.range
  1928. * @param {...} range... range values.
  1929. * @returns {pv.Scale.linear} <tt>this</tt>, or the current range.
  1930. */
  1931. scale.range = function() {
  1932. if (arguments.length) {
  1933. r = Array.prototype.slice.call(arguments);
  1934. i = [];
  1935. for (var j = 0; j < r.length - 1; j++) {
  1936. i.push(pv.Scale.interpolator(r[j], r[j + 1]));
  1937. }
  1938. return this;
  1939. }
  1940. return r;
  1941. };
  1942. /**
  1943. * Inverts the specified value in the output range, returning the
  1944. * corresponding value in the input domain. This is frequently used to convert
  1945. * the mouse location (see {@link pv.Mark#mouse}) to a value in the input
  1946. * domain. Inversion is only supported for numeric ranges, and not colors.
  1947. *
  1948. * <p>Note that this method does not do any rounding or bounds checking. If
  1949. * the input domain is discrete (e.g., an array index), the returned value
  1950. * should be rounded. If the specified <tt>y</tt> value is outside the range,
  1951. * the returned value may be equivalently outside the input domain.
  1952. *
  1953. * @function
  1954. * @name pv.Scale.linear.prototype.invert
  1955. * @param {number} y a value in the output range (a pixel location).
  1956. * @returns {number} a value in the input domain.
  1957. */
  1958. scale.invert = function(y) {
  1959. var j = pv.search(r, y);
  1960. if (j < 0) j = -j - 2;
  1961. j = Math.max(0, Math.min(i.length - 1, j));
  1962. return (y - r[j]) / (r[j + 1] - r[j]) * (d[j + 1] - d[j]) + d[j];
  1963. };
  1964. /**
  1965. * Returns an array of evenly-spaced, suitably-rounded values in the input
  1966. * domain. This method attempts to return between 5 and 10 tick values. These
  1967. * values are frequently used in conjunction with {@link pv.Rule} to display
  1968. * tick marks or grid lines.
  1969. *
  1970. * @function
  1971. * @name pv.Scale.linear.prototype.ticks
  1972. * @returns {number[]} an array input domain values to use as ticks.
  1973. */
  1974. scale.ticks = function() {
  1975. var min = d[0],
  1976. max = d[d.length - 1],
  1977. span = max - min,
  1978. step = pv.logCeil(span / 10, 10);
  1979. if (span / step < 2) step /= 5;
  1980. else if (span / step < 5) step /= 2;
  1981. var start = Math.ceil(min / step) * step,
  1982. end = Math.floor(max / step) * step;
  1983. precision = Math.max(0, -Math.floor(pv.log(step, 10) + .01));
  1984. return pv.range(start, end + step, step);
  1985. };
  1986. /**
  1987. * Formats the specified tick value using the appropriate precision, based on
  1988. * the step interval between tick marks.
  1989. *
  1990. * @function
  1991. * @name pv.Scale.linear.prototype.tickFormat
  1992. * @param {number} t a tick value.
  1993. * @return {string} a formatted tick value.
  1994. */
  1995. scale.tickFormat = function(t) {
  1996. return t.toFixed(precision);
  1997. };
  1998. /**
  1999. * "Nices" this scale, extending the bounds of the input domain to
  2000. * evenly-rounded values. Nicing is useful if the domain is computed
  2001. * dynamically from data, and may be irregular. For example, given a domain of
  2002. * [0.20147987687960267, 0.996679553296417], a call to <tt>nice()</tt> might
  2003. * extend the domain to [0.2, 1].
  2004. *
  2005. * <p>This method must be invoked each time after setting the domain.
  2006. *
  2007. * @function
  2008. * @name pv.Scale.linear.prototype.nice
  2009. * @returns {pv.Scale.linear} <tt>this</tt>.
  2010. */
  2011. scale.nice = function() {
  2012. var min = d[0],
  2013. max = d[d.length - 1],
  2014. step = Math.pow(10, Math.round(Math.log(max - min) / Math.log(10)) - 1);
  2015. d = [Math.floor(min / step) * step, Math.ceil(max / step) * step];
  2016. return this;
  2017. };
  2018. /**
  2019. * Returns a view of this scale by the specified accessor function <tt>f</tt>.
  2020. * Given a scale <tt>y</tt>, <tt>y.by(function(d) d.foo)</tt> is equivalent to
  2021. * <tt>function(d) y(d.foo)</tt>.
  2022. *
  2023. * <p>This method is provided for convenience, such that scales can be
  2024. * succinctly defined inline. For example, given an array of data elements
  2025. * that have a <tt>score</tt> attribute with the domain [0, 1], the height
  2026. * property could be specified as:
  2027. *
  2028. * <pre>.height(pv.Scale.linear().range(0, 480).by(function(d) d.score))</pre>
  2029. *
  2030. * This is equivalent to:
  2031. *
  2032. * <pre>.height(function(d) d.score * 480)</pre>
  2033. *
  2034. * This method should be used judiciously; it is typically more clear to
  2035. * invoke the scale directly, passing in the value to be scaled.
  2036. *
  2037. * @function
  2038. * @name pv.Scale.linear.prototype.by
  2039. * @param {function} f an accessor function.
  2040. * @returns {pv.Scale.linear} a view of this scale by the specified accessor
  2041. * function.
  2042. */
  2043. scale.by = function(f) {
  2044. function by() { return scale(f.apply(this, arguments)); }
  2045. for (var method in scale) by[method] = scale[method];
  2046. return by;
  2047. };
  2048. scale.domain.apply(scale, arguments);
  2049. return scale;
  2050. };
  2051. /**
  2052. * Returns a log scale for the specified domain. The arguments to this
  2053. * constructor are optional, and equivalent to calling {@link #domain}.
  2054. *
  2055. * @class Represents a log scale. <style
  2056. * type="text/css">sub{line-height:0}</style> <img src="../log.png"
  2057. * width="190" height="175" align="right"> Most commonly, a log scale represents
  2058. * a 1-dimensional log transformation from a numeric domain of input data
  2059. * [<i>d<sub>0</sub></i>, <i>d<sub>1</sub></i>] to a numeric range of pixels
  2060. * [<i>r<sub>0</sub></i>, <i>r<sub>1</sub></i>]. The equation for such a scale
  2061. * is:
  2062. *
  2063. * <blockquote><i>f(x) = (log(x) - log(d<sub>0</sub>)) / (log(d<sub>1</sub>) -
  2064. * log(d<sub>0</sub>)) * (r<sub>1</sub> - r<sub>0</sub>) +
  2065. * r<sub>0</sub></i></blockquote>
  2066. *
  2067. * where <i>log(x)</i> represents the zero-symmetric logarthim of <i>x</i> using
  2068. * the scale's associated base (default: 10, see {@link pv.logSymmetric}). For
  2069. * example, a log scale from the domain [1, 100] to range [0, 640]:
  2070. *
  2071. * <blockquote><i>f(x) = (log(x) - log(1)) / (log(100) - log(1)) * (640 - 0) + 0</i><br>
  2072. * <i>f(x) = log(x) / 2 * 640</i><br>
  2073. * <i>f(x) = log(x) * 320</i><br>
  2074. * </blockquote>
  2075. *
  2076. * Thus, saying
  2077. *
  2078. * <pre>.height(function(d) Math.log(d) * 138.974)</pre>
  2079. *
  2080. * is equivalent to
  2081. *
  2082. * <pre>.height(pv.Scale.log(1, 100).range(0, 640))</pre>
  2083. *
  2084. * As you can see, scales do not always make code smaller, but they should make
  2085. * code more explicit and easier to maintain. In addition to readability, scales
  2086. * offer several useful features:
  2087. *
  2088. * <p>1. The range can be expressed in colors, rather than pixels. Changing the
  2089. * example above to
  2090. *
  2091. * <pre>.fillStyle(pv.Scale.log(1, 100).range("red", "green"))</pre>
  2092. *
  2093. * will cause it to fill the marks "red" on an input value of 1, "green" on an
  2094. * input value of 100, and some color in-between for intermediate values.
  2095. *
  2096. * <p>2. The domain and range can be subdivided for a "poly-log"
  2097. * transformation. For example, you may want a diverging color scale that is
  2098. * increasingly red for small values, and increasingly green for large values:
  2099. *
  2100. * <pre>.fillStyle(pv.Scale.log(1, 10, 100).range("red", "white", "green"))</pre>
  2101. *
  2102. * The domain can be specified as a series of <i>n</i> monotonically-increasing
  2103. * values; the range must also be specified as <i>n</i> values, resulting in
  2104. * <i>n - 1</i> contiguous log scales.
  2105. *
  2106. * <p>3. Log scales can be inverted for interaction. The {@link #invert} method
  2107. * takes a value in the output range, and returns the corresponding value in the
  2108. * input domain. This is frequently used to convert the mouse location (see
  2109. * {@link pv.Mark#mouse}) to a value in the input domain. Note that inversion is
  2110. * only supported for numeric ranges, and not colors.
  2111. *
  2112. * <p>4. A scale can be queried for reasonable "tick" values. The {@link #ticks}
  2113. * method provides a convenient way to get a series of evenly-spaced rounded
  2114. * values in the input domain. Frequently these are used in conjunction with
  2115. * {@link pv.Rule} to display tick marks or grid lines.
  2116. *
  2117. * <p>5. A scale can be "niced" to extend the domain to suitable rounded
  2118. * numbers. If the minimum and maximum of the domain are messy because they are
  2119. * derived from data, you can use {@link #nice} to round these values down and
  2120. * up to even numbers.
  2121. *
  2122. * @param {number...} domain... domain values.
  2123. * @returns {pv.Scale.log} a log scale.
  2124. */
  2125. pv.Scale.log = function() {
  2126. var d = [1, 10], l = [0, 1], b = 10, r = [0, 1], i = [pv.identity];
  2127. /** @private */
  2128. function scale(x) {
  2129. var j = pv.search(d, x);
  2130. if (j < 0) j = -j - 2;
  2131. j = Math.max(0, Math.min(i.length - 1, j));
  2132. return i[j]((log(x) - l[j]) / (l[j + 1] - l[j]));
  2133. }
  2134. /** @private */
  2135. function log(x) {
  2136. return pv.logSymmetric(x, b);
  2137. }
  2138. /**
  2139. * Sets or gets the input domain. This method can be invoked several ways:
  2140. *
  2141. * <p>1. <tt>domain(min, ..., max)</tt>
  2142. *
  2143. * <p>Specifying the domain as a series of numbers is the most explicit and
  2144. * recommended approach. Most commonly, two numbers are specified: the minimum
  2145. * and maximum value. However, for a diverging scale, or other subdivided
  2146. * poly-log scales, multiple values can be specified. Values can be derived
  2147. * from data using {@link pv.min} and {@link pv.max}. For example:
  2148. *
  2149. * <pre>.domain(1, pv.max(array))</pre>
  2150. *
  2151. * An alternative method for deriving minimum and maximum values from data
  2152. * follows.
  2153. *
  2154. * <p>2. <tt>domain(array, minf, maxf)</tt>
  2155. *
  2156. * <p>When both the minimum and maximum value are derived from data, the
  2157. * arguments to the <tt>domain</tt> method can be specified as the array of
  2158. * data, followed by zero, one or two accessor functions. For example, if the
  2159. * array of data is just an array of numbers:
  2160. *
  2161. * <pre>.domain(array)</pre>
  2162. *
  2163. * On the other hand, if the array elements are objects representing stock
  2164. * values per day, and the domain should consider the stock's daily low and
  2165. * daily high:
  2166. *
  2167. * <pre>.domain(array, function(d) d.low, function(d) d.high)</pre>
  2168. *
  2169. * The first method of setting the domain is preferred because it is more
  2170. * explicit; setting the domain using this second method should be used only
  2171. * if brevity is required.
  2172. *
  2173. * <p>3. <tt>domain()</tt>
  2174. *
  2175. * <p>Invoking the <tt>domain</tt> method with no arguments returns the
  2176. * current domain as an array of numbers.
  2177. *
  2178. * @function
  2179. * @name pv.Scale.log.prototype.domain
  2180. * @param {number...} domain... domain values.
  2181. * @returns {pv.Scale.log} <tt>this</tt>, or the current domain.
  2182. */
  2183. scale.domain = function(array, min, max) {
  2184. if (arguments.length) {
  2185. if (array instanceof Array) {
  2186. if (arguments.length < 2) min = pv.identity;
  2187. if (arguments.length < 3) max = min;
  2188. d = [pv.min(array, min), pv.max(array, max)];
  2189. } else {
  2190. d = Array.prototype.slice.call(arguments);
  2191. }
  2192. l = d.map(log);
  2193. return this;
  2194. }
  2195. return d;
  2196. };
  2197. /**
  2198. * @function
  2199. * @name pv.Scale.log.prototype.range
  2200. * @param {...} range... range values.
  2201. * @returns {pv.Scale.log} <tt>this</tt>.
  2202. */
  2203. scale.range = function() {
  2204. if (arguments.length) {
  2205. r = Array.prototype.slice.call(arguments);
  2206. i = [];
  2207. for (var j = 0; j < r.length - 1; j++) {
  2208. i.push(pv.Scale.interpolator(r[j], r[j + 1]));
  2209. }
  2210. return this;
  2211. }
  2212. return r;
  2213. };
  2214. /**
  2215. * Sets or gets the output range. This method can be invoked several ways:
  2216. *
  2217. * <p>1. <tt>range(min, ..., max)</tt>
  2218. *
  2219. * <p>The range may be specified as a series of numbers or colors. Most
  2220. * commonly, two numbers are specified: the minimum and maximum pixel values.
  2221. * For a color scale, values may be specified as {@link pv.Color}s or
  2222. * equivalent strings. For a diverging scale, or other subdivided poly-log
  2223. * scales, multiple values can be specified. For example:
  2224. *
  2225. * <pre>.range("red", "white", "green")</pre>
  2226. *
  2227. * <p>Currently, only numbers and colors are supported as range values. The
  2228. * number of range values must exactly match the number of domain values, or
  2229. * the behavior of the scale is undefined.
  2230. *
  2231. * <p>2. <tt>range()</tt>
  2232. *
  2233. * <p>Invoking the <tt>range</tt> method with no arguments returns the current
  2234. * range as an array of numbers or colors.
  2235. *
  2236. * @function
  2237. * @name pv.Scale.log.prototype.invert
  2238. * @param {...} range... range values.
  2239. * @returns {pv.Scale.log} <tt>this</tt>, or the current range.
  2240. */
  2241. scale.invert = function(y) {
  2242. var j = pv.search(r, y);
  2243. if (j < 0) j = -j - 2;
  2244. j = Math.max(0, Math.min(i.length - 1, j));
  2245. var t = l[j] + (y - r[j]) / (r[j + 1] - r[j]) * (l[j + 1] - l[j]);
  2246. return (d[j] < 0) ? -Math.pow(b, -t) : Math.pow(b, t);
  2247. };
  2248. /**
  2249. * Returns an array of evenly-spaced, suitably-rounded values in the input
  2250. * domain. These values are frequently used in conjunction with {@link
  2251. * pv.Rule} to display tick marks or grid lines.
  2252. *
  2253. * @function
  2254. * @name pv.Scale.log.prototype.ticks
  2255. * @returns {number[]} an array input domain values to use as ticks.
  2256. */
  2257. scale.ticks = function() {
  2258. // TODO: support multiple domains
  2259. var start = Math.floor(l[0]),
  2260. end = Math.ceil(l[1]),
  2261. ticks = [];
  2262. for (var i = start; i < end; i++) {
  2263. var x = Math.pow(b, i);
  2264. if (d[0] < 0) x = -x;
  2265. for (var j = 1; j < b; j++) {
  2266. ticks.push(x * j);
  2267. }
  2268. }
  2269. ticks.push(Math.pow(b, end));
  2270. if (ticks[0] < d[0]) ticks.shift();
  2271. if (ticks[ticks.length - 1] > d[1]) ticks.pop();
  2272. return ticks;
  2273. };
  2274. /**
  2275. * Formats the specified tick value using the appropriate precision, assuming
  2276. * base 10.
  2277. *
  2278. * @function
  2279. * @name pv.Scale.log.prototype.tickFormat
  2280. * @param {number} t a tick value.
  2281. * @return {string} a formatted tick value.
  2282. */
  2283. scale.tickFormat = function(t) {
  2284. return t.toPrecision(1);
  2285. };
  2286. /**
  2287. * "Nices" this scale, extending the bounds of the input domain to
  2288. * evenly-rounded values. This method uses {@link pv.logFloor} and {@link
  2289. * pv.logCeil}. Nicing is useful if the domain is computed dynamically from
  2290. * data, and may be irregular. For example, given a domain of
  2291. * [0.20147987687960267, 0.996679553296417], a call to <tt>nice()</tt> might
  2292. * extend the domain to [0.1, 1].
  2293. *
  2294. * <p>This method must be invoked each time after setting the domain (and
  2295. * base).
  2296. *
  2297. * @function
  2298. * @name pv.Scale.log.prototype.nice
  2299. * @returns {pv.Scale.log} <tt>this</tt>.
  2300. */
  2301. scale.nice = function() {
  2302. // TODO: support multiple domains
  2303. d = [pv.logFloor(d[0], b), pv.logCeil(d[1], b)];
  2304. l = d.map(log);
  2305. return this;
  2306. };
  2307. /**
  2308. * Sets or gets the logarithm base. Defaults to 10.
  2309. *
  2310. * @function
  2311. * @name pv.Scale.log.prototype.base
  2312. * @param {number} [v] the new base.
  2313. * @returns {pv.Scale.log} <tt>this</tt>, or the current base.
  2314. */
  2315. scale.base = function(v) {
  2316. if (arguments.length) {
  2317. b = v;
  2318. l = d.map(log);
  2319. return this;
  2320. }
  2321. return b;
  2322. };
  2323. /**
  2324. * Returns a view of this scale by the specified accessor function <tt>f</tt>.
  2325. * Given a scale <tt>y</tt>, <tt>y.by(function(d) d.foo)</tt> is equivalent to
  2326. * <tt>function(d) y(d.foo)</tt>.
  2327. *
  2328. * <p>This method is provided for convenience, such that scales can be
  2329. * succinctly defined inline. For example, given an array of data elements
  2330. * that have a <tt>score</tt> attribute with the domain [0, 1], the height
  2331. * property could be specified as:
  2332. *
  2333. * <pre>.height(pv.Scale.log().range(0, 480).by(function(d) d.score))</pre>
  2334. *
  2335. * This is equivalent to:
  2336. *
  2337. * <pre>.height(function(d) d.score * 480)</pre>
  2338. *
  2339. * This method should be used judiciously; it is typically more clear to
  2340. * invoke the scale directly, passing in the value to be scaled.
  2341. *
  2342. * @function
  2343. * @name pv.Scale.log.prototype.by
  2344. * @param {function} f an accessor function.
  2345. * @returns {pv.Scale.log} a view of this scale by the specified accessor
  2346. * function.
  2347. */
  2348. scale.by = function(f) {
  2349. function by() { return scale(f.apply(this, arguments)); }
  2350. for (var method in scale) by[method] = scale[method];
  2351. return by;
  2352. };
  2353. scale.domain.apply(scale, arguments);
  2354. return scale;
  2355. };
  2356. /**
  2357. * Returns an ordinal scale for the specified domain. The arguments to this
  2358. * constructor are optional, and equivalent to calling {@link #domain}.
  2359. *
  2360. * @class Represents an ordinal scale. <style
  2361. * type="text/css">sub{line-height:0}</style> An ordinal scale represents a
  2362. * pairwise mapping from <i>n</i> discrete values in the input domain to
  2363. * <i>n</i> discrete values in the output range. For example, an ordinal scale
  2364. * might map a domain of species ["setosa", "versicolor", "virginica"] to colors
  2365. * ["red", "green", "blue"]. Thus, saying
  2366. *
  2367. * <pre>.fillStyle(function(d) {
  2368. * switch (d.species) {
  2369. * case "setosa": return "red";
  2370. * case "versicolor": return "green";
  2371. * case "virginica": return "blue";
  2372. * }
  2373. * })</pre>
  2374. *
  2375. * is equivalent to
  2376. *
  2377. * <pre>.fillStyle(pv.Scale.ordinal("setosa", "versicolor", "virginica")
  2378. * .range("red", "green", "blue")
  2379. * .by(function(d) d.species))</pre>
  2380. *
  2381. * If the mapping from species to color does not need to be specified
  2382. * explicitly, the domain can be omitted. In this case it will be inferred
  2383. * lazily from the data:
  2384. *
  2385. * <pre>.fillStyle(pv.colors("red", "green", "blue")
  2386. * .by(function(d) d.species))</pre>
  2387. *
  2388. * When the domain is inferred, the first time the scale is invoked, the first
  2389. * element from the range will be returned. Subsequent calls with unique values
  2390. * will return subsequent elements from the range. If the inferred domain grows
  2391. * larger than the range, range values will be reused. However, it is strongly
  2392. * recommended that the domain and the range contain the same number of
  2393. * elements.
  2394. *
  2395. * <p>A range can be discretized from a continuous interval (e.g., for pixel
  2396. * positioning) by using {@link #split}, {@link #splitFlush} or
  2397. * {@link #splitBanded} after the domain has been set. For example, if
  2398. * <tt>states</tt> is an array of the fifty U.S. state names, the state name can
  2399. * be encoded in the left position:
  2400. *
  2401. * <pre>.left(pv.Scale.ordinal(states)
  2402. * .split(0, 640)
  2403. * .by(function(d) d.state))</pre>
  2404. *
  2405. * <p>N.B.: ordinal scales are not invertible (at least not yet), since the
  2406. * domain and range and discontinuous. A workaround is to use a linear scale.
  2407. *
  2408. * @param {...} domain... domain values.
  2409. * @returns {pv.Scale.ordinal} an ordinal scale.
  2410. * @see pv.colors
  2411. */
  2412. pv.Scale.ordinal = function() {
  2413. var d = [], i = {}, r = [], band = 0;
  2414. /** @private */
  2415. function scale(x) {
  2416. if (!(x in i)) i[x] = d.push(x) - 1;
  2417. return r[i[x] % r.length];
  2418. }
  2419. /**
  2420. * Sets or gets the input domain. This method can be invoked several ways:
  2421. *
  2422. * <p>1. <tt>domain(values...)</tt>
  2423. *
  2424. * <p>Specifying the domain as a series of values is the most explicit and
  2425. * recommended approach. However, if the domain values are derived from data,
  2426. * you may find the second method more appropriate.
  2427. *
  2428. * <p>2. <tt>domain(array, f)</tt>
  2429. *
  2430. * <p>Rather than enumerating the domain values as explicit arguments to this
  2431. * method, you can specify a single argument of an array. In addition, you can
  2432. * specify an optional accessor function to extract the domain values from the
  2433. * array.
  2434. *
  2435. * <p>3. <tt>domain()</tt>
  2436. *
  2437. * <p>Invoking the <tt>domain</tt> method with no arguments returns the
  2438. * current domain as an array.
  2439. *
  2440. * @function
  2441. * @name pv.Scale.ordinal.prototype.domain
  2442. * @param {...} domain... domain values.
  2443. * @returns {pv.Scale.ordinal} <tt>this</tt>, or the current domain.
  2444. */
  2445. scale.domain = function(array, f) {
  2446. if (arguments.length) {
  2447. array = (array instanceof Array)
  2448. ? ((arguments.length > 1) ? map(array, f) : array)
  2449. : Array.prototype.slice.call(arguments);
  2450. /* Filter the specified ordinals to their unique values. */
  2451. d = [];
  2452. var seen = {};
  2453. for (var j = 0; j < array.length; j++) {
  2454. var o = array[j];
  2455. if (!(o in seen)) {
  2456. seen[o] = true;
  2457. d.push(o);
  2458. }
  2459. }
  2460. i = pv.numerate(d);
  2461. return this;
  2462. }
  2463. return d;
  2464. };
  2465. /**
  2466. * Sets or gets the output range. This method can be invoked several ways:
  2467. *
  2468. * <p>1. <tt>range(values...)</tt>
  2469. *
  2470. * <p>Specifying the range as a series of values is the most explicit and
  2471. * recommended approach. However, if the range values are derived from data,
  2472. * you may find the second method more appropriate.
  2473. *
  2474. * <p>2. <tt>range(array, f)</tt>
  2475. *
  2476. * <p>Rather than enumerating the range values as explicit arguments to this
  2477. * method, you can specify a single argument of an array. In addition, you can
  2478. * specify an optional accessor function to extract the range values from the
  2479. * array.
  2480. *
  2481. * <p>3. <tt>range()</tt>
  2482. *
  2483. * <p>Invoking the <tt>range</tt> method with no arguments returns the
  2484. * current range as an array.
  2485. *
  2486. * @function
  2487. * @name pv.Scale.ordinal.prototype.range
  2488. * @param {...} range... range values.
  2489. * @returns {pv.Scale.ordinal} <tt>this</tt>, or the current range.
  2490. */
  2491. scale.range = function(array, f) {
  2492. if (arguments.length) {
  2493. r = (array instanceof Array)
  2494. ? ((arguments.length > 1) ? map(array, f) : array)
  2495. : Array.prototype.slice.call(arguments);
  2496. if (typeof r[0] == "string") r = r.map(pv.color);
  2497. return this;
  2498. }
  2499. return r;
  2500. };
  2501. /**
  2502. * Sets the range from the given continuous interval. The interval
  2503. * [<i>min</i>, <i>max</i>] is subdivided into <i>n</i> equispaced points,
  2504. * where <i>n</i> is the number of (unique) values in the domain. The first
  2505. * and last point are offset from the edge of the range by half the distance
  2506. * between points.
  2507. *
  2508. * <p>This method must be called <i>after</i> the domain is set.
  2509. *
  2510. * @function
  2511. * @name pv.Scale.ordinal.prototype.split
  2512. * @param {number} min minimum value of the output range.
  2513. * @param {number} max maximum value of the output range.
  2514. * @returns {pv.Scale.ordinal} <tt>this</tt>.
  2515. * @see #splitFlush
  2516. * @see #splitBanded
  2517. */
  2518. scale.split = function(min, max) {
  2519. var step = (max - min) / this.domain().length;
  2520. r = pv.range(min + step / 2, max, step);
  2521. return this;
  2522. };
  2523. /**
  2524. * Sets the range from the given continuous interval. The interval
  2525. * [<i>min</i>, <i>max</i>] is subdivided into <i>n</i> equispaced points,
  2526. * where <i>n</i> is the number of (unique) values in the domain. The first
  2527. * and last point are exactly on the edge of the range.
  2528. *
  2529. * <p>This method must be called <i>after</i> the domain is set.
  2530. *
  2531. * @function
  2532. * @name pv.Scale.ordinal.prototype.splitFlush
  2533. * @param {number} min minimum value of the output range.
  2534. * @param {number} max maximum value of the output range.
  2535. * @returns {pv.Scale.ordinal} <tt>this</tt>.
  2536. * @see #split
  2537. */
  2538. scale.splitFlush = function(min, max) {
  2539. var n = this.domain().length, step = (max - min) / (n - 1);
  2540. r = (n == 1) ? [(min + max) / 2]
  2541. : pv.range(min, max + step / 2, step);
  2542. return this;
  2543. };
  2544. /**
  2545. * Sets the range from the given continuous interval. The interval
  2546. * [<i>min</i>, <i>max</i>] is subdivided into <i>n</i> equispaced bands,
  2547. * where <i>n</i> is the number of (unique) values in the domain. The first
  2548. * and last band are offset from the edge of the range by the distance between
  2549. * bands.
  2550. *
  2551. * <p>The band width argument, <tt>band</tt>, is typically in the range [0, 1]
  2552. * and defaults to 1. This fraction corresponds to the amount of space in the
  2553. * range to allocate to the bands, as opposed to padding. A value of 0.5 means
  2554. * that the band width will be equal to the padding width. The computed
  2555. * absolute band width can be retrieved from the range as
  2556. * <tt>scale.range().band</tt>.
  2557. *
  2558. * <p>If the band width argument is negative, this method will allocate bands
  2559. * of a <i>fixed</i> width <tt>-band</tt>, rather than a relative fraction of
  2560. * the available space.
  2561. *
  2562. * <p>Tip: to inset the bands by a fixed amount <tt>p</tt>, specify a minimum
  2563. * value of <tt>min + p</tt> (or simply <tt>p</tt>, if <tt>min</tt> is
  2564. * 0). Then set the mark width to <tt>scale.range().band - p</tt>.
  2565. *
  2566. * <p>This method must be called <i>after</i> the domain is set.
  2567. *
  2568. * @function
  2569. * @name pv.Scale.ordinal.prototype.splitBanded
  2570. * @param {number} min minimum value of the output range.
  2571. * @param {number} max maximum value of the output range.
  2572. * @param {number} [band] the fractional band width in [0, 1]; defaults to 1.
  2573. * @returns {pv.Scale.ordinal} <tt>this</tt>.
  2574. * @see #split
  2575. */
  2576. scale.splitBanded = function(min, max, band) {
  2577. if (arguments.length < 3) band = 1;
  2578. if (band < 0) {
  2579. var n = this.domain().length,
  2580. total = -band * n,
  2581. remaining = max - min - total,
  2582. padding = remaining / (n + 1);
  2583. r = pv.range(min + padding, max, padding - band);
  2584. r.band = -band;
  2585. } else {
  2586. var step = (max - min) / (this.domain().length + (1 - band));
  2587. r = pv.range(min + step * (1 - band), max, step);
  2588. r.band = step * band;
  2589. }
  2590. return this;
  2591. };
  2592. /**
  2593. * Returns a view of this scale by the specified accessor function <tt>f</tt>.
  2594. * Given a scale <tt>y</tt>, <tt>y.by(function(d) d.foo)</tt> is equivalent to
  2595. * <tt>function(d) y(d.foo)</tt>. This method should be used judiciously; it
  2596. * is typically more clear to invoke the scale directly, passing in the value
  2597. * to be scaled.
  2598. *
  2599. * @function
  2600. * @name pv.Scale.ordinal.prototype.by
  2601. * @param {function} f an accessor function.
  2602. * @returns {pv.Scale.ordinal} a view of this scale by the specified accessor
  2603. * function.
  2604. */
  2605. scale.by = function(f) {
  2606. function by() { return scale(f.apply(this, arguments)); }
  2607. for (var method in scale) by[method] = scale[method];
  2608. return by;
  2609. };
  2610. scale.domain.apply(scale, arguments);
  2611. return scale;
  2612. };
  2613. /**
  2614. * Returns the {@link pv.Color} for the specified color format string. Colors
  2615. * may have an associated opacity, or alpha channel. Color formats are specified
  2616. * by CSS Color Modular Level 3, using either in RGB or HSL color space. For
  2617. * example:<ul>
  2618. *
  2619. * <li>#f00 // #rgb
  2620. * <li>#ff0000 // #rrggbb
  2621. * <li>rgb(255, 0, 0)
  2622. * <li>rgb(100%, 0%, 0%)
  2623. * <li>hsl(0, 100%, 50%)
  2624. * <li>rgba(0, 0, 255, 0.5)
  2625. * <li>hsla(120, 100%, 50%, 1)
  2626. *
  2627. * </ul>The SVG 1.0 color keywords names are also supported, such as "aliceblue"
  2628. * and "yellowgreen". The "transparent" keyword is supported for a
  2629. * fully-transparent color.
  2630. *
  2631. * <p>If the <tt>format</tt> argument is already an instance of <tt>Color</tt>,
  2632. * the argument is returned with no further processing.
  2633. *
  2634. * @param {string} format the color specification string, such as "#f00".
  2635. * @returns {pv.Color} the corresponding <tt>Color</tt>.
  2636. * @see <a href="http://www.w3.org/TR/SVG/types.html#ColorKeywords">SVG color
  2637. * keywords</a>
  2638. * @see <a href="http://www.w3.org/TR/css3-color/">CSS3 color module</a>
  2639. */
  2640. pv.color = function(format) {
  2641. if (!format || (format == "transparent")) {
  2642. return pv.rgb(0, 0, 0, 0);
  2643. }
  2644. if (format instanceof pv.Color) {
  2645. return format;
  2646. }
  2647. /* Handle hsl, rgb. */
  2648. var m1 = /([a-z]+)\((.*)\)/i.exec(format);
  2649. if (m1) {
  2650. var m2 = m1[2].split(","), a = 1;
  2651. switch (m1[1]) {
  2652. case "hsla":
  2653. case "rgba": {
  2654. a = parseFloat(m2[3]);
  2655. break;
  2656. }
  2657. }
  2658. switch (m1[1]) {
  2659. case "hsla":
  2660. case "hsl": {
  2661. var h = parseFloat(m2[0]), // degrees
  2662. s = parseFloat(m2[1]) / 100, // percentage
  2663. l = parseFloat(m2[2]) / 100; // percentage
  2664. return (new pv.Color.Hsl(h, s, l, a)).rgb();
  2665. }
  2666. case "rgba":
  2667. case "rgb": {
  2668. function parse(c) { // either integer or percentage
  2669. var f = parseFloat(c);
  2670. return (c[c.length - 1] == '%') ? Math.round(f * 2.55) : f;
  2671. }
  2672. var r = parse(m2[0]), g = parse(m2[1]), b = parse(m2[2]);
  2673. return pv.rgb(r, g, b, a);
  2674. }
  2675. }
  2676. }
  2677. /* Named colors. */
  2678. format = pv.Color.names[format] || format;
  2679. /* Hexadecimal colors: #rgb and #rrggbb. */
  2680. if (format.charAt(0) == "#") {
  2681. var r, g, b;
  2682. if (format.length == 4) {
  2683. r = format.charAt(1); r += r;
  2684. g = format.charAt(2); g += g;
  2685. b = format.charAt(3); b += b;
  2686. } else if (format.length == 7) {
  2687. r = format.substring(1, 3);
  2688. g = format.substring(3, 5);
  2689. b = format.substring(5, 7);
  2690. }
  2691. return pv.rgb(parseInt(r, 16), parseInt(g, 16), parseInt(b, 16), 1);
  2692. }
  2693. /* Otherwise, assume named colors. TODO allow lazy conversion to RGB. */
  2694. return new pv.Color(format, 1);
  2695. };
  2696. /**
  2697. * Constructs a color with the specified color format string and opacity. This
  2698. * constructor should not be invoked directly; use {@link pv.color} instead.
  2699. *
  2700. * @class Represents an abstract (possibly translucent) color. The color is
  2701. * divided into two parts: the <tt>color</tt> attribute, an opaque color format
  2702. * string, and the <tt>opacity</tt> attribute, a float in [0, 1]. The color
  2703. * space is dependent on the implementing class; all colors support the
  2704. * {@link #rgb} method to convert to RGB color space for interpolation.
  2705. *
  2706. * <p>See also the <a href="../../api/Color.html">Color guide</a>.
  2707. *
  2708. * @param {string} color an opaque color format string, such as "#f00".
  2709. * @param {number} opacity the opacity, in [0,1].
  2710. * @see pv.color
  2711. */
  2712. pv.Color = function(color, opacity) {
  2713. /**
  2714. * An opaque color format string, such as "#f00".
  2715. *
  2716. * @type string
  2717. * @see <a href="http://www.w3.org/TR/SVG/types.html#ColorKeywords">SVG color
  2718. * keywords</a>
  2719. * @see <a href="http://www.w3.org/TR/css3-color/">CSS3 color module</a>
  2720. */
  2721. this.color = color;
  2722. /**
  2723. * The opacity, a float in [0, 1].
  2724. *
  2725. * @type number
  2726. */
  2727. this.opacity = opacity;
  2728. };
  2729. /**
  2730. * Returns a new color that is a brighter version of this color. The behavior of
  2731. * this method may vary slightly depending on the underlying color space.
  2732. * Although brighter and darker are inverse operations, the results of a series
  2733. * of invocations of these two methods might be inconsistent because of rounding
  2734. * errors.
  2735. *
  2736. * @param [k] {number} an optional scale factor; defaults to 1.
  2737. * @see #darker
  2738. * @returns {pv.Color} a brighter color.
  2739. */
  2740. pv.Color.prototype.brighter = function(k) {
  2741. return this.rgb().brighter(k);
  2742. };
  2743. /**
  2744. * Returns a new color that is a brighter version of this color. The behavior of
  2745. * this method may vary slightly depending on the underlying color space.
  2746. * Although brighter and darker are inverse operations, the results of a series
  2747. * of invocations of these two methods might be inconsistent because of rounding
  2748. * errors.
  2749. *
  2750. * @param [k] {number} an optional scale factor; defaults to 1.
  2751. * @see #brighter
  2752. * @returns {pv.Color} a darker color.
  2753. */
  2754. pv.Color.prototype.darker = function(k) {
  2755. return this.rgb().darker(k);
  2756. };
  2757. /**
  2758. * Constructs a new RGB color with the specified channel values.
  2759. *
  2760. * @param {number} r the red channel, an integer in [0,255].
  2761. * @param {number} g the green channel, an integer in [0,255].
  2762. * @param {number} b the blue channel, an integer in [0,255].
  2763. * @param {number} [a] the alpha channel, a float in [0,1].
  2764. * @returns pv.Color.Rgb
  2765. */
  2766. pv.rgb = function(r, g, b, a) {
  2767. return new pv.Color.Rgb(r, g, b, (arguments.length == 4) ? a : 1);
  2768. };
  2769. /**
  2770. * Constructs a new RGB color with the specified channel values.
  2771. *
  2772. * @class Represents a color in RGB space.
  2773. *
  2774. * @param {number} r the red channel, an integer in [0,255].
  2775. * @param {number} g the green channel, an integer in [0,255].
  2776. * @param {number} b the blue channel, an integer in [0,255].
  2777. * @param {number} a the alpha channel, a float in [0,1].
  2778. * @extends pv.Color
  2779. */
  2780. pv.Color.Rgb = function(r, g, b, a) {
  2781. pv.Color.call(this, a ? ("rgb(" + r + "," + g + "," + b + ")") : "none", a);
  2782. /**
  2783. * The red channel, an integer in [0, 255].
  2784. *
  2785. * @type number
  2786. */
  2787. this.r = r;
  2788. /**
  2789. * The green channel, an integer in [0, 255].
  2790. *
  2791. * @type number
  2792. */
  2793. this.g = g;
  2794. /**
  2795. * The blue channel, an integer in [0, 255].
  2796. *
  2797. * @type number
  2798. */
  2799. this.b = b;
  2800. /**
  2801. * The alpha channel, a float in [0, 1].
  2802. *
  2803. * @type number
  2804. */
  2805. this.a = a;
  2806. };
  2807. pv.Color.Rgb.prototype = pv.extend(pv.Color);
  2808. /**
  2809. * Constructs a new RGB color with the same green, blue and alpha channels as
  2810. * this color, with the specified red channel.
  2811. *
  2812. * @param {number} r the red channel, an integer in [0,255].
  2813. */
  2814. pv.Color.Rgb.prototype.red = function(r) {
  2815. return pv.rgb(r, this.g, this.b, this.a);
  2816. };
  2817. /**
  2818. * Constructs a new RGB color with the same red, blue and alpha channels as this
  2819. * color, with the specified green channel.
  2820. *
  2821. * @param {number} g the green channel, an integer in [0,255].
  2822. */
  2823. pv.Color.Rgb.prototype.green = function(g) {
  2824. return pv.rgb(this.r, g, this.b, this.a);
  2825. };
  2826. /**
  2827. * Constructs a new RGB color with the same red, green and alpha channels as
  2828. * this color, with the specified blue channel.
  2829. *
  2830. * @param {number} b the blue channel, an integer in [0,255].
  2831. */
  2832. pv.Color.Rgb.prototype.blue = function(b) {
  2833. return pv.rgb(this.r, this.g, b, this.a);
  2834. };
  2835. /**
  2836. * Constructs a new RGB color with the same red, green and blue channels as this
  2837. * color, with the specified alpha channel.
  2838. *
  2839. * @param {number} a the alpha channel, a float in [0,1].
  2840. */
  2841. pv.Color.Rgb.prototype.alpha = function(a) {
  2842. return pv.rgb(this.r, this.g, this.b, a);
  2843. };
  2844. /**
  2845. * Returns the RGB color equivalent to this color. This method is abstract and
  2846. * must be implemented by subclasses.
  2847. *
  2848. * @returns {pv.Color.Rgb} an RGB color.
  2849. * @function
  2850. * @name pv.Color.prototype.rgb
  2851. */
  2852. /**
  2853. * Returns this.
  2854. *
  2855. * @returns {pv.Color.Rgb} this.
  2856. */
  2857. pv.Color.Rgb.prototype.rgb = function() { return this; };
  2858. /**
  2859. * Returns a new color that is a brighter version of this color. This method
  2860. * applies an arbitrary scale factor to each of the three RGB components of this
  2861. * color to create a brighter version of this color. Although brighter and
  2862. * darker are inverse operations, the results of a series of invocations of
  2863. * these two methods might be inconsistent because of rounding errors.
  2864. *
  2865. * @param [k] {number} an optional scale factor; defaults to 1.
  2866. * @see #darker
  2867. * @returns {pv.Color.Rgb} a brighter color.
  2868. */
  2869. pv.Color.Rgb.prototype.brighter = function(k) {
  2870. k = Math.pow(0.7, arguments.length ? k : 1);
  2871. var r = this.r, g = this.g, b = this.b, i = 30;
  2872. if (!r && !g && !b) return pv.rgb(i, i, i, this.a);
  2873. if (r && (r < i)) r = i;
  2874. if (g && (g < i)) g = i;
  2875. if (b && (b < i)) b = i;
  2876. return pv.rgb(
  2877. Math.min(255, Math.floor(r / k)),
  2878. Math.min(255, Math.floor(g / k)),
  2879. Math.min(255, Math.floor(b / k)),
  2880. this.a);
  2881. };
  2882. /**
  2883. * Returns a new color that is a darker version of this color. This method
  2884. * applies an arbitrary scale factor to each of the three RGB components of this
  2885. * color to create a darker version of this color. Although brighter and darker
  2886. * are inverse operations, the results of a series of invocations of these two
  2887. * methods might be inconsistent because of rounding errors.
  2888. *
  2889. * @param [k] {number} an optional scale factor; defaults to 1.
  2890. * @see #brighter
  2891. * @returns {pv.Color.Rgb} a darker color.
  2892. */
  2893. pv.Color.Rgb.prototype.darker = function(k) {
  2894. k = Math.pow(0.7, arguments.length ? k : 1);
  2895. return pv.rgb(
  2896. Math.max(0, Math.floor(k * this.r)),
  2897. Math.max(0, Math.floor(k * this.g)),
  2898. Math.max(0, Math.floor(k * this.b)),
  2899. this.a);
  2900. };
  2901. /**
  2902. * Constructs a new HSL color with the specified values.
  2903. *
  2904. * @param {number} h the hue, an integer in [0, 360].
  2905. * @param {number} s the saturation, a float in [0, 1].
  2906. * @param {number} l the lightness, a float in [0, 1].
  2907. * @param {number} [a] the opacity, a float in [0, 1].
  2908. * @returns pv.Color.Hsl
  2909. */
  2910. pv.hsl = function(h, s, l, a) {
  2911. return new pv.Color.Hsl(h, s, l, (arguments.length == 4) ? a : 1);
  2912. };
  2913. /**
  2914. * Constructs a new HSL color with the specified values.
  2915. *
  2916. * @class Represents a color in HSL space.
  2917. *
  2918. * @param {number} h the hue, an integer in [0, 360].
  2919. * @param {number} s the saturation, a float in [0, 1].
  2920. * @param {number} l the lightness, a float in [0, 1].
  2921. * @param {number} a the opacity, a float in [0, 1].
  2922. * @extends pv.Color
  2923. */
  2924. pv.Color.Hsl = function(h, s, l, a) {
  2925. pv.Color.call(this, "hsl(" + h + "," + (s * 100) + "%," + (l * 100) + "%)", a);
  2926. /**
  2927. * The hue, an integer in [0, 360].
  2928. *
  2929. * @type number
  2930. */
  2931. this.h = h;
  2932. /**
  2933. * The saturation, a float in [0, 1].
  2934. *
  2935. * @type number
  2936. */
  2937. this.s = s;
  2938. /**
  2939. * The lightness, a float in [0, 1].
  2940. *
  2941. * @type number
  2942. */
  2943. this.l = l;
  2944. /**
  2945. * The opacity, a float in [0, 1].
  2946. *
  2947. * @type number
  2948. */
  2949. this.a = a;
  2950. };
  2951. pv.Color.Hsl.prototype = pv.extend(pv.Color);
  2952. /**
  2953. * Constructs a new HSL color with the same saturation, lightness and alpha as
  2954. * this color, and the specified hue.
  2955. *
  2956. * @param {number} h the hue, an integer in [0, 360].
  2957. */
  2958. pv.Color.Hsl.prototype.hue = function(h) {
  2959. return pv.hsl(h, this.s, this.l, this.a);
  2960. };
  2961. /**
  2962. * Constructs a new HSL color with the same hue, lightness and alpha as this
  2963. * color, and the specified saturation.
  2964. *
  2965. * @param {number} s the saturation, a float in [0, 1].
  2966. */
  2967. pv.Color.Hsl.prototype.saturation = function(s) {
  2968. return pv.hsl(this.h, s, this.l, this.a);
  2969. };
  2970. /**
  2971. * Constructs a new HSL color with the same hue, saturation and alpha as this
  2972. * color, and the specified lightness.
  2973. *
  2974. * @param {number} l the lightness, a float in [0, 1].
  2975. */
  2976. pv.Color.Hsl.prototype.lightness = function(l) {
  2977. return pv.hsl(this.h, this.s, l, this.a);
  2978. };
  2979. /**
  2980. * Constructs a new HSL color with the same hue, saturation and lightness as
  2981. * this color, and the specified alpha.
  2982. *
  2983. * @param {number} a the opacity, a float in [0, 1].
  2984. */
  2985. pv.Color.Hsl.prototype.alpha = function(a) {
  2986. return pv.hsl(this.h, this.s, this.l, a);
  2987. };
  2988. /**
  2989. * Returns the RGB color equivalent to this HSL color.
  2990. *
  2991. * @returns {pv.Color.Rgb} an RGB color.
  2992. */
  2993. pv.Color.Hsl.prototype.rgb = function() {
  2994. var h = this.h, s = this.s, l = this.l;
  2995. /* Some simple corrections for h, s and l. */
  2996. h = h % 360; if (h < 0) h += 360;
  2997. s = Math.max(0, Math.min(s, 1));
  2998. l = Math.max(0, Math.min(l, 1));
  2999. /* From FvD 13.37, CSS Color Module Level 3 */
  3000. var m2 = (l <= .5) ? (l * (1 + s)) : (l + s - l * s);
  3001. var m1 = 2 * l - m2;
  3002. function v(h) {
  3003. if (h > 360) h -= 360;
  3004. else if (h < 0) h += 360;
  3005. if (h < 60) return m1 + (m2 - m1) * h / 60;
  3006. if (h < 180) return m2;
  3007. if (h < 240) return m1 + (m2 - m1) * (240 - h) / 60;
  3008. return m1;
  3009. }
  3010. function vv(h) {
  3011. return Math.round(v(h) * 255);
  3012. }
  3013. return pv.rgb(vv(h + 120), vv(h), vv(h - 120), this.a);
  3014. };
  3015. /**
  3016. * @private SVG color keywords, per CSS Color Module Level 3.
  3017. *
  3018. * @see <a href="http://www.w3.org/TR/SVG/types.html#ColorKeywords">SVG color
  3019. * keywords</a>
  3020. */
  3021. pv.Color.names = {
  3022. aliceblue: "#f0f8ff",
  3023. antiquewhite: "#faebd7",
  3024. aqua: "#00ffff",
  3025. aquamarine: "#7fffd4",
  3026. azure: "#f0ffff",
  3027. beige: "#f5f5dc",
  3028. bisque: "#ffe4c4",
  3029. black: "#000000",
  3030. blanchedalmond: "#ffebcd",
  3031. blue: "#0000ff",
  3032. blueviolet: "#8a2be2",
  3033. brown: "#a52a2a",
  3034. burlywood: "#deb887",
  3035. cadetblue: "#5f9ea0",
  3036. chartreuse: "#7fff00",
  3037. chocolate: "#d2691e",
  3038. coral: "#ff7f50",
  3039. cornflowerblue: "#6495ed",
  3040. cornsilk: "#fff8dc",
  3041. crimson: "#dc143c",
  3042. cyan: "#00ffff",
  3043. darkblue: "#00008b",
  3044. darkcyan: "#008b8b",
  3045. darkgoldenrod: "#b8860b",
  3046. darkgray: "#a9a9a9",
  3047. darkgreen: "#006400",
  3048. darkgrey: "#a9a9a9",
  3049. darkkhaki: "#bdb76b",
  3050. darkmagenta: "#8b008b",
  3051. darkolivegreen: "#556b2f",
  3052. darkorange: "#ff8c00",
  3053. darkorchid: "#9932cc",
  3054. darkred: "#8b0000",
  3055. darksalmon: "#e9967a",
  3056. darkseagreen: "#8fbc8f",
  3057. darkslateblue: "#483d8b",
  3058. darkslategray: "#2f4f4f",
  3059. darkslategrey: "#2f4f4f",
  3060. darkturquoise: "#00ced1",
  3061. darkviolet: "#9400d3",
  3062. deeppink: "#ff1493",
  3063. deepskyblue: "#00bfff",
  3064. dimgray: "#696969",
  3065. dimgrey: "#696969",
  3066. dodgerblue: "#1e90ff",
  3067. firebrick: "#b22222",
  3068. floralwhite: "#fffaf0",
  3069. forestgreen: "#228b22",
  3070. fuchsia: "#ff00ff",
  3071. gainsboro: "#dcdcdc",
  3072. ghostwhite: "#f8f8ff",
  3073. gold: "#ffd700",
  3074. goldenrod: "#daa520",
  3075. gray: "#808080",
  3076. green: "#008000",
  3077. greenyellow: "#adff2f",
  3078. grey: "#808080",
  3079. honeydew: "#f0fff0",
  3080. hotpink: "#ff69b4",
  3081. indianred: "#cd5c5c",
  3082. indigo: "#4b0082",
  3083. ivory: "#fffff0",
  3084. khaki: "#f0e68c",
  3085. lavender: "#e6e6fa",
  3086. lavenderblush: "#fff0f5",
  3087. lawngreen: "#7cfc00",
  3088. lemonchiffon: "#fffacd",
  3089. lightblue: "#add8e6",
  3090. lightcoral: "#f08080",
  3091. lightcyan: "#e0ffff",
  3092. lightgoldenrodyellow: "#fafad2",
  3093. lightgray: "#d3d3d3",
  3094. lightgreen: "#90ee90",
  3095. lightgrey: "#d3d3d3",
  3096. lightpink: "#ffb6c1",
  3097. lightsalmon: "#ffa07a",
  3098. lightseagreen: "#20b2aa",
  3099. lightskyblue: "#87cefa",
  3100. lightslategray: "#778899",
  3101. lightslategrey: "#778899",
  3102. lightsteelblue: "#b0c4de",
  3103. lightyellow: "#ffffe0",
  3104. lime: "#00ff00",
  3105. limegreen: "#32cd32",
  3106. linen: "#faf0e6",
  3107. magenta: "#ff00ff",
  3108. maroon: "#800000",
  3109. mediumaquamarine: "#66cdaa",
  3110. mediumblue: "#0000cd",
  3111. mediumorchid: "#ba55d3",
  3112. mediumpurple: "#9370db",
  3113. mediumseagreen: "#3cb371",
  3114. mediumslateblue: "#7b68ee",
  3115. mediumspringgreen: "#00fa9a",
  3116. mediumturquoise: "#48d1cc",
  3117. mediumvioletred: "#c71585",
  3118. midnightblue: "#191970",
  3119. mintcream: "#f5fffa",
  3120. mistyrose: "#ffe4e1",
  3121. moccasin: "#ffe4b5",
  3122. navajowhite: "#ffdead",
  3123. navy: "#000080",
  3124. oldlace: "#fdf5e6",
  3125. olive: "#808000",
  3126. olivedrab: "#6b8e23",
  3127. orange: "#ffa500",
  3128. orangered: "#ff4500",
  3129. orchid: "#da70d6",
  3130. palegoldenrod: "#eee8aa",
  3131. palegreen: "#98fb98",
  3132. paleturquoise: "#afeeee",
  3133. palevioletred: "#db7093",
  3134. papayawhip: "#ffefd5",
  3135. peachpuff: "#ffdab9",
  3136. peru: "#cd853f",
  3137. pink: "#ffc0cb",
  3138. plum: "#dda0dd",
  3139. powderblue: "#b0e0e6",
  3140. purple: "#800080",
  3141. red: "#ff0000",
  3142. rosybrown: "#bc8f8f",
  3143. royalblue: "#4169e1",
  3144. saddlebrown: "#8b4513",
  3145. salmon: "#fa8072",
  3146. sandybrown: "#f4a460",
  3147. seagreen: "#2e8b57",
  3148. seashell: "#fff5ee",
  3149. sienna: "#a0522d",
  3150. silver: "#c0c0c0",
  3151. skyblue: "#87ceeb",
  3152. slateblue: "#6a5acd",
  3153. slategray: "#708090",
  3154. slategrey: "#708090",
  3155. snow: "#fffafa",
  3156. springgreen: "#00ff7f",
  3157. steelblue: "#4682b4",
  3158. tan: "#d2b48c",
  3159. teal: "#008080",
  3160. thistle: "#d8bfd8",
  3161. tomato: "#ff6347",
  3162. turquoise: "#40e0d0",
  3163. violet: "#ee82ee",
  3164. wheat: "#f5deb3",
  3165. white: "#ffffff",
  3166. whitesmoke: "#f5f5f5",
  3167. yellow: "#ffff00",
  3168. yellowgreen: "#9acd32"
  3169. };
  3170. /**
  3171. * Returns a new categorical color encoding using the specified colors. The
  3172. * arguments to this method are an array of colors; see {@link pv.color}. For
  3173. * example, to create a categorical color encoding using the <tt>species</tt>
  3174. * attribute:
  3175. *
  3176. * <pre>pv.colors("red", "green", "blue").by(function(d) d.species)</pre>
  3177. *
  3178. * The result of this expression can be used as a fill- or stroke-style
  3179. * property. This assumes that the data's <tt>species</tt> attribute is a
  3180. * string.
  3181. *
  3182. * @param {string} colors... categorical colors.
  3183. * @see pv.Scale.ordinal
  3184. * @returns {pv.Scale.ordinal} an ordinal color scale.
  3185. */
  3186. pv.colors = function() {
  3187. var scale = pv.Scale.ordinal();
  3188. scale.range.apply(scale, arguments);
  3189. return scale;
  3190. };
  3191. /**
  3192. * A collection of standard color palettes for categorical encoding.
  3193. *
  3194. * @namespace A collection of standard color palettes for categorical encoding.
  3195. */
  3196. pv.Colors = {};
  3197. /**
  3198. * Returns a new 10-color scheme. The arguments to this constructor are
  3199. * optional, and equivalent to calling {@link pv.Scale.OrdinalScale#domain}. The
  3200. * following colors are used:
  3201. *
  3202. * <div style="background:#1f77b4;">#1f77b4</div>
  3203. * <div style="background:#ff7f0e;">#ff7f0e</div>
  3204. * <div style="background:#2ca02c;">#2ca02c</div>
  3205. * <div style="background:#d62728;">#d62728</div>
  3206. * <div style="background:#9467bd;">#9467bd</div>
  3207. * <div style="background:#8c564b;">#8c564b</div>
  3208. * <div style="background:#e377c2;">#e377c2</div>
  3209. * <div style="background:#7f7f7f;">#7f7f7f</div>
  3210. * <div style="background:#bcbd22;">#bcbd22</div>
  3211. * <div style="background:#17becf;">#17becf</div>
  3212. *
  3213. * @param {number...} domain... domain values.
  3214. * @returns {pv.Scale.ordinal} a new ordinal color scale.
  3215. * @see pv.color
  3216. */
  3217. pv.Colors.category10 = function() {
  3218. var scale = pv.colors(
  3219. "#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd",
  3220. "#8c564b", "#e377c2", "#7f7f7f", "#bcbd22", "#17becf");
  3221. scale.domain.apply(scale, arguments);
  3222. return scale;
  3223. };
  3224. /**
  3225. * Returns a new 20-color scheme. The arguments to this constructor are
  3226. * optional, and equivalent to calling {@link pv.Scale.OrdinalScale#domain}. The
  3227. * following colors are used:
  3228. *
  3229. * <div style="background:#1f77b4;">#1f77b4</div>
  3230. * <div style="background:#aec7e8;">#aec7e8</div>
  3231. * <div style="background:#ff7f0e;">#ff7f0e</div>
  3232. * <div style="background:#ffbb78;">#ffbb78</div>
  3233. * <div style="background:#2ca02c;">#2ca02c</div>
  3234. * <div style="background:#98df8a;">#98df8a</div>
  3235. * <div style="background:#d62728;">#d62728</div>
  3236. * <div style="background:#ff9896;">#ff9896</div>
  3237. * <div style="background:#9467bd;">#9467bd</div>
  3238. * <div style="background:#c5b0d5;">#c5b0d5</div>
  3239. * <div style="background:#8c564b;">#8c564b</div>
  3240. * <div style="background:#c49c94;">#c49c94</div>
  3241. * <div style="background:#e377c2;">#e377c2</div>
  3242. * <div style="background:#f7b6d2;">#f7b6d2</div>
  3243. * <div style="background:#7f7f7f;">#7f7f7f</div>
  3244. * <div style="background:#c7c7c7;">#c7c7c7</div>
  3245. * <div style="background:#bcbd22;">#bcbd22</div>
  3246. * <div style="background:#dbdb8d;">#dbdb8d</div>
  3247. * <div style="background:#17becf;">#17becf</div>
  3248. * <div style="background:#9edae5;">#9edae5</div>
  3249. *
  3250. * @param {number...} domain... domain values.
  3251. * @returns {pv.Scale.ordinal} a new ordinal color scale.
  3252. * @see pv.color
  3253. */
  3254. pv.Colors.category20 = function() {
  3255. var scale = pv.colors(
  3256. "#1f77b4", "#aec7e8", "#ff7f0e", "#ffbb78", "#2ca02c",
  3257. "#98df8a", "#d62728", "#ff9896", "#9467bd", "#c5b0d5",
  3258. "#8c564b", "#c49c94", "#e377c2", "#f7b6d2", "#7f7f7f",
  3259. "#c7c7c7", "#bcbd22", "#dbdb8d", "#17becf", "#9edae5");
  3260. scale.domain.apply(scale, arguments);
  3261. return scale;
  3262. };
  3263. /**
  3264. * Returns a new alternative 19-color scheme. The arguments to this constructor
  3265. * are optional, and equivalent to calling
  3266. * {@link pv.Scale.OrdinalScale#domain}. The following colors are used:
  3267. *
  3268. * <div style="background:#9c9ede;">#9c9ede</div>
  3269. * <div style="background:#7375b5;">#7375b5</div>
  3270. * <div style="background:#4a5584;">#4a5584</div>
  3271. * <div style="background:#cedb9c;">#cedb9c</div>
  3272. * <div style="background:#b5cf6b;">#b5cf6b</div>
  3273. * <div style="background:#8ca252;">#8ca252</div>
  3274. * <div style="background:#637939;">#637939</div>
  3275. * <div style="background:#e7cb94;">#e7cb94</div>
  3276. * <div style="background:#e7ba52;">#e7ba52</div>
  3277. * <div style="background:#bd9e39;">#bd9e39</div>
  3278. * <div style="background:#8c6d31;">#8c6d31</div>
  3279. * <div style="background:#e7969c;">#e7969c</div>
  3280. * <div style="background:#d6616b;">#d6616b</div>
  3281. * <div style="background:#ad494a;">#ad494a</div>
  3282. * <div style="background:#843c39;">#843c39</div>
  3283. * <div style="background:#de9ed6;">#de9ed6</div>
  3284. * <div style="background:#ce6dbd;">#ce6dbd</div>
  3285. * <div style="background:#a55194;">#a55194</div>
  3286. * <div style="background:#7b4173;">#7b4173</div>
  3287. *
  3288. * @param {number...} domain... domain values.
  3289. * @returns {pv.Scale.ordinal} a new ordinal color scale.
  3290. * @see pv.color
  3291. */
  3292. pv.Colors.category19 = function() {
  3293. var scale = pv.colors(
  3294. "#9c9ede", "#7375b5", "#4a5584", "#cedb9c", "#b5cf6b",
  3295. "#8ca252", "#637939", "#e7cb94", "#e7ba52", "#bd9e39",
  3296. "#8c6d31", "#e7969c", "#d6616b", "#ad494a", "#843c39",
  3297. "#de9ed6", "#ce6dbd", "#a55194", "#7b4173");
  3298. scale.domain.apply(scale, arguments);
  3299. return scale;
  3300. };
  3301. /**
  3302. * Returns a linear color ramp from the specified <tt>start</tt> color to the
  3303. * specified <tt>end</tt> color. The color arguments may be specified either as
  3304. * <tt>string</tt>s or as {@link pv.Color}s.
  3305. *
  3306. * @param {string} start the start color; may be a <tt>pv.Color</tt>.
  3307. * @param {string} end the end color; may be a <tt>pv.Color</tt>.
  3308. * @returns {Function} a color ramp from <tt>start</tt> to <tt>end</tt>.
  3309. * @see pv.Scale.linear
  3310. */
  3311. pv.ramp = function(start, end) {
  3312. var scale = pv.Scale.linear();
  3313. scale.range.apply(scale, arguments);
  3314. return scale;
  3315. };
  3316. // TODO don't populate default attributes?
  3317. /**
  3318. * @private
  3319. * @namespace
  3320. */
  3321. pv.Scene = pv.SvgScene = {};
  3322. /**
  3323. * Updates the display for the specified array of scene nodes.
  3324. *
  3325. * @param scenes {array} an array of scene nodes.
  3326. */
  3327. pv.SvgScene.updateAll = function(scenes) {
  3328. if (!scenes.length) return;
  3329. if ((scenes[0].reverse)
  3330. && (scenes.type != "line")
  3331. && (scenes.type != "area")) {
  3332. var reversed = pv.extend(scenes);
  3333. for (var i = 0, j = scenes.length - 1; j >= 0; i++, j--) {
  3334. reversed[i] = scenes[j];
  3335. }
  3336. scenes = reversed;
  3337. }
  3338. this.removeSiblings(this[scenes.type](scenes));
  3339. };
  3340. /**
  3341. * Creates a new SVG element of the specified type.
  3342. *
  3343. * @param type {string} an SVG element type, such as "rect".
  3344. * @return a new SVG element.
  3345. */
  3346. pv.SvgScene.create = function(type) {
  3347. return document.createElementNS(pv.ns.svg, type);
  3348. };
  3349. /**
  3350. * Expects the element <i>e</i> to be the specified type. If the element does
  3351. * not exist, a new one is created. If the element does exist but is the wrong
  3352. * type, it is replaced with the specified element.
  3353. *
  3354. * @param type {string} an SVG element type, such as "rect".
  3355. * @return a new SVG element.
  3356. */
  3357. pv.SvgScene.expect = function(type, e) {
  3358. if (!e) return this.create(type);
  3359. if (e.tagName == "a") e = e.firstChild;
  3360. if (e.tagName == type) return e;
  3361. var n = this.create(type);
  3362. e.parentNode.replaceChild(n, e);
  3363. return n;
  3364. };
  3365. /** TODO */
  3366. pv.SvgScene.append = function(e, scenes, index) {
  3367. e.$scene = {scenes:scenes, index:index};
  3368. e = this.title(e, scenes[index]);
  3369. if (!e.parentNode) scenes.$g.appendChild(e);
  3370. return e.nextSibling;
  3371. };
  3372. /**
  3373. * Applies a title tooltip to the specified element <tt>e</tt>, using the
  3374. * <tt>title</tt> property of the specified scene node <tt>s</tt>. Note that
  3375. * this implementation does not create an SVG <tt>title</tt> element as a child
  3376. * of <tt>e</tt>; although this is the recommended standard, it is only
  3377. * supported in Opera. Instead, an anchor element is created around the element
  3378. * <tt>e</tt>, and the <tt>xlink:title</tt> attribute is set accordingly.
  3379. *
  3380. * @param e an SVG element.
  3381. * @param s a scene node.
  3382. */
  3383. pv.SvgScene.title = function(e, s) {
  3384. var a = e.parentNode, t = String(s.title);
  3385. if (a && (a.tagName != "a")) a = null;
  3386. if (t) {
  3387. if (!a) {
  3388. a = this.create("a");
  3389. if (e.parentNode) e.parentNode.replaceChild(a, e);
  3390. a.appendChild(e);
  3391. }
  3392. a.setAttributeNS(pv.ns.xlink, "title", t);
  3393. return a;
  3394. }
  3395. if (a) a.parentNode.replaceChild(e, a);
  3396. return e;
  3397. };
  3398. /** TODO */
  3399. pv.SvgScene.dispatch = function(e) {
  3400. var t = e.target.$scene;
  3401. if (t) {
  3402. t.scenes.mark.dispatch(e.type, t.scenes, t.index);
  3403. e.preventDefault();
  3404. }
  3405. };
  3406. /** TODO */
  3407. pv.SvgScene.removeSiblings = function(e) {
  3408. while (e) {
  3409. var n = e.nextSibling;
  3410. e.parentNode.removeChild(e);
  3411. e = n;
  3412. }
  3413. };
  3414. // TODO strokeStyle for areaSegment?
  3415. pv.SvgScene.area = function(scenes) {
  3416. var e = scenes.$g.firstChild;
  3417. if (!scenes.length) return e;
  3418. var s = scenes[0];
  3419. /* segmented */
  3420. if (s.segmented) return this.areaSegment(scenes);
  3421. /* visible */
  3422. if (!s.visible) return e;
  3423. var fill = pv.color(s.fillStyle), stroke = pv.color(s.strokeStyle);
  3424. if (!fill.opacity && !stroke.opacity) return e;
  3425. /* points */
  3426. var p1 = "", p2 = "";
  3427. for (var i = 0, j = scenes.length - 1; j >= 0; i++, j--) {
  3428. var si = scenes[i], sj = scenes[j];
  3429. p1 += si.left + "," + si.top + " ";
  3430. p2 += (sj.left + sj.width) + "," + (sj.top + sj.height) + " ";
  3431. /* interpolate (assume linear by default) */
  3432. if (i < scenes.length - 1) {
  3433. var sk = scenes[i + 1], sl = scenes[j - 1];
  3434. switch (s.interpolate) {
  3435. case "step-before": {
  3436. p1 += si.left + "," + sk.top + " ";
  3437. p2 += (sl.left + sl.width) + "," + (sj.top + sj.height) + " ";
  3438. break;
  3439. }
  3440. case "step-after": {
  3441. p1 += sk.left + "," + si.top + " ";
  3442. p2 += (sj.left + sj.width) + "," + (sl.top + sl.height) + " ";
  3443. break;
  3444. }
  3445. }
  3446. }
  3447. }
  3448. e = this.expect("polygon", e);
  3449. e.setAttribute("cursor", s.cursor);
  3450. e.setAttribute("points", p1 + p2);
  3451. var fill = pv.color(s.fillStyle);
  3452. e.setAttribute("fill", fill.color);
  3453. e.setAttribute("fill-opacity", fill.opacity);
  3454. var stroke = pv.color(s.strokeStyle);
  3455. e.setAttribute("stroke", stroke.color);
  3456. e.setAttribute("stroke-opacity", stroke.opacity);
  3457. e.setAttribute("stroke-width", s.lineWidth);
  3458. return this.append(e, scenes, 0);
  3459. };
  3460. pv.SvgScene.areaSegment = function(scenes) {
  3461. var e = scenes.$g.firstChild;
  3462. for (var i = 0, n = scenes.length - 1; i < n; i++) {
  3463. var s1 = scenes[i], s2 = scenes[i + 1];
  3464. /* visible */
  3465. if (!s1.visible || !s2.visible) continue;
  3466. var fill = pv.color(s1.fillStyle), stroke = pv.color(s1.strokeStyle);
  3467. if (!fill.opacity && !stroke.opacity) continue;
  3468. /* points */
  3469. var p = s1.left + "," + s1.top + " "
  3470. + s2.left + "," + s2.top + " "
  3471. + (s2.left + s2.width) + "," + (s2.top + s2.height) + " "
  3472. + (s1.left + s1.width) + "," + (s1.top + s1.height);
  3473. e = this.expect("polygon", e);
  3474. e.setAttribute("cursor", s1.cursor);
  3475. e.setAttribute("points", p);
  3476. e.setAttribute("fill", fill.color);
  3477. e.setAttribute("fill-opacity", fill.opacity);
  3478. e.setAttribute("stroke", stroke.color);
  3479. e.setAttribute("stroke-opacity", stroke.opacity);
  3480. e.setAttribute("stroke-width", s1.lineWidth);
  3481. e = this.append(e, scenes, i);
  3482. }
  3483. return e;
  3484. };
  3485. pv.SvgScene.bar = function(scenes) {
  3486. var e = scenes.$g.firstChild;
  3487. for (var i = 0; i < scenes.length; i++) {
  3488. var s = scenes[i];
  3489. /* visible */
  3490. if (!s.visible) continue;
  3491. var fill = pv.color(s.fillStyle), stroke = pv.color(s.strokeStyle);
  3492. if (!fill.opacity && !stroke.opacity) continue;
  3493. e = this.expect("rect", e);
  3494. e.setAttribute("cursor", s.cursor);
  3495. e.setAttribute("x", s.left);
  3496. e.setAttribute("y", s.top);
  3497. e.setAttribute("width", Math.max(1E-10, s.width));
  3498. e.setAttribute("height", Math.max(1E-10, s.height));
  3499. e.setAttribute("fill", fill.color);
  3500. e.setAttribute("fill-opacity", fill.opacity);
  3501. e.setAttribute("stroke", stroke.color);
  3502. e.setAttribute("stroke-opacity", stroke.opacity);
  3503. e.setAttribute("stroke-width", s.lineWidth);
  3504. e = this.append(e, scenes, i);
  3505. }
  3506. return e;
  3507. };
  3508. pv.SvgScene.dot = function(scenes) {
  3509. var e = scenes.$g.firstChild;
  3510. for (var i = 0; i < scenes.length; i++) {
  3511. var s = scenes[i];
  3512. /* visible */
  3513. if (!s.visible) continue;
  3514. var fill = pv.color(s.fillStyle), stroke = pv.color(s.strokeStyle);
  3515. if (!fill.opacity && !stroke.opacity) continue;
  3516. /* points */
  3517. var radius = Math.sqrt(s.size), fillPath = "", strokePath = "";
  3518. switch (s.shape) {
  3519. case "cross": {
  3520. fillPath = "M" + -radius + "," + -radius
  3521. + "L" + radius + "," + radius
  3522. + "M" + radius + "," + -radius
  3523. + "L" + -radius + "," + radius;
  3524. break;
  3525. }
  3526. case "triangle": {
  3527. var h = radius, w = radius * 2 / Math.sqrt(3);
  3528. fillPath = "M0," + h
  3529. + "L" + w +"," + -h
  3530. + " " + -w + "," + -h
  3531. + "Z";
  3532. break;
  3533. }
  3534. case "diamond": {
  3535. radius *= Math.sqrt(2);
  3536. fillPath = "M0," + -radius
  3537. + "L" + radius + ",0"
  3538. + " 0," + radius
  3539. + " " + -radius + ",0"
  3540. + "Z";
  3541. break;
  3542. }
  3543. case "square": {
  3544. fillPath = "M" + -radius + "," + -radius
  3545. + "L" + radius + "," + -radius
  3546. + " " + radius + "," + radius
  3547. + " " + -radius + "," + radius
  3548. + "Z";
  3549. break;
  3550. }
  3551. case "tick": {
  3552. fillPath = "M0,0L0," + -s.size;
  3553. break;
  3554. }
  3555. default: {
  3556. function circle(r) {
  3557. return "M0," + r
  3558. + "A" + r + "," + r + " 0 1,1 0," + (-r)
  3559. + "A" + r + "," + r + " 0 1,1 0," + r
  3560. + "Z";
  3561. }
  3562. if (s.lineWidth / 2 > radius) strokePath = circle(s.lineWidth);
  3563. fillPath = circle(radius);
  3564. break;
  3565. }
  3566. }
  3567. /* transform */
  3568. var transform = "translate(" + s.left + "," + s.top + ")"
  3569. + (s.angle ? " rotate(" + 180 * s.angle / Math.PI + ")" : "");
  3570. /* The normal fill path. */
  3571. e = this.expect("path", e);
  3572. e.setAttribute("d", fillPath);
  3573. e.setAttribute("transform", transform);
  3574. e.setAttribute("fill", fill.color);
  3575. e.setAttribute("fill-opacity", fill.opacity);
  3576. e.setAttribute("cursor", s.cursor);
  3577. if (strokePath) {
  3578. e.setAttribute("stroke", "none");
  3579. } else {
  3580. e.setAttribute("stroke", stroke.color);
  3581. e.setAttribute("stroke-opacity", stroke.opacity);
  3582. e.setAttribute("stroke-width", s.lineWidth);
  3583. }
  3584. e = this.append(e, scenes, i);
  3585. /* The special-case stroke path. */
  3586. if (strokePath) {
  3587. e = this.expect("path", e);
  3588. e.setAttribute("d", strokePath);
  3589. e.setAttribute("transform", transform);
  3590. e.setAttribute("fill", stroke.color);
  3591. e.setAttribute("fill-opacity", stroke.opacity);
  3592. e.setAttribute("cursor", s.cursor);
  3593. e = this.append(e, scenes, i);
  3594. }
  3595. }
  3596. return e;
  3597. };
  3598. pv.SvgScene.image = function(scenes) {
  3599. var e = scenes.$g.firstChild;
  3600. for (var i = 0; i < scenes.length; i++) {
  3601. var s = scenes[i];
  3602. /* visible */
  3603. if (!s.visible) continue;
  3604. /* fill */
  3605. e = this.fill(e, scenes, i);
  3606. /* image */
  3607. e = this.expect("image", e);
  3608. e.setAttribute("preserveAspectRatio", "none");
  3609. e.setAttribute("x", s.left);
  3610. e.setAttribute("y", s.top);
  3611. e.setAttribute("width", s.width);
  3612. e.setAttribute("height", s.height);
  3613. e.setAttribute("cursor", s.cursor);
  3614. e.setAttributeNS(pv.ns.xlink, "href", s.url);
  3615. e = this.append(e, scenes, i);
  3616. /* stroke */
  3617. e = this.stroke(e, scenes, i);
  3618. }
  3619. return e;
  3620. };
  3621. pv.SvgScene.label = function(scenes) {
  3622. var e = scenes.$g.firstChild;
  3623. for (var i = 0; i < scenes.length; i++) {
  3624. var s = scenes[i];
  3625. /* visible */
  3626. if (!s.visible) continue;
  3627. var fill = pv.color(s.textStyle);
  3628. if (!fill.opacity) continue;
  3629. /* text-baseline, text-align */
  3630. var x = 0, y = 0, dy = 0, anchor = "start";
  3631. switch (s.textBaseline) {
  3632. case "middle": dy = ".35em"; break;
  3633. case "top": dy = ".71em"; y = s.textMargin; break;
  3634. case "bottom": y = "-" + s.textMargin; break;
  3635. }
  3636. switch (s.textAlign) {
  3637. case "right": anchor = "end"; x = "-" + s.textMargin; break;
  3638. case "center": anchor = "middle"; break;
  3639. case "left": x = s.textMargin; break;
  3640. }
  3641. e = this.expect("text", e);
  3642. e.setAttribute("pointer-events", "none");
  3643. e.setAttribute("x", x);
  3644. e.setAttribute("y", y);
  3645. e.setAttribute("dy", dy);
  3646. e.setAttribute("text-anchor", anchor);
  3647. e.setAttribute("transform",
  3648. "translate(" + s.left + "," + s.top + ")"
  3649. + (s.textAngle ? " rotate(" + 180 * s.textAngle / Math.PI + ")" : ""));
  3650. e.setAttribute("fill", fill.color);
  3651. e.setAttribute("fill-opacity", fill.opacity);
  3652. e.style.font = s.font;
  3653. e.style.textShadow = s.textShadow;
  3654. if (e.firstChild) e.firstChild.nodeValue = s.text;
  3655. else e.appendChild(document.createTextNode(s.text));
  3656. e = this.append(e, scenes, i);
  3657. }
  3658. return e;
  3659. };
  3660. // TODO fillStyle for lineSegment?
  3661. // TODO lineOffset for flow maps?
  3662. pv.SvgScene.line = function(scenes) {
  3663. var e = scenes.$g.firstChild;
  3664. if (scenes.length < 2) return e;
  3665. var s = scenes[0];
  3666. /* segmented */
  3667. if (s.segmented) return this.lineSegment(scenes);
  3668. /* visible */
  3669. if (!s.visible) return e;
  3670. var fill = pv.color(s.fillStyle), stroke = pv.color(s.strokeStyle);
  3671. if (!fill.opacity && !stroke.opacity) return e;
  3672. /* points */
  3673. var p = "";
  3674. for (var i = 0; i < scenes.length; i++) {
  3675. var si = scenes[i];
  3676. p += si.left + "," + si.top + " ";
  3677. /* interpolate (assume linear by default) */
  3678. if (i < scenes.length - 1) {
  3679. var sj = scenes[i + 1];
  3680. switch (s.interpolate) {
  3681. case "step-before": {
  3682. p += si.left + "," + sj.top + " ";
  3683. break;
  3684. }
  3685. case "step-after": {
  3686. p += sj.left + "," + si.top + " ";
  3687. break;
  3688. }
  3689. }
  3690. }
  3691. }
  3692. e = this.expect("polyline", e);
  3693. e.setAttribute("cursor", s.cursor);
  3694. e.setAttribute("points", p);
  3695. e.setAttribute("fill", fill.color);
  3696. e.setAttribute("fill-opacity", fill.opacity);
  3697. e.setAttribute("stroke", stroke.color);
  3698. e.setAttribute("stroke-opacity", stroke.opacity);
  3699. e.setAttribute("stroke-width", s.lineWidth);
  3700. return this.append(e, scenes, 0);
  3701. };
  3702. pv.SvgScene.lineSegment = function(scenes) {
  3703. var e = scenes.$g.firstChild;
  3704. for (var i = 0, n = scenes.length - 1; i < n; i++) {
  3705. var s1 = scenes[i], s2 = scenes[i + 1];
  3706. /* visible */
  3707. if (!s1.visible || !s2.visible) continue;
  3708. var stroke = pv.color(s1.strokeStyle);
  3709. if (!stroke.opacity) continue;
  3710. /* Line-line intersection, per Akenine-Moller 16.16.1. */
  3711. function intersect(o1, d1, o2, d2) {
  3712. return o1.plus(d1.times(o2.minus(o1).dot(d2.perp()) / d1.dot(d2.perp())));
  3713. }
  3714. /*
  3715. * P1-P2 is the current line segment. V is a vector that is perpendicular to
  3716. * the line segment, and has length lineWidth / 2. ABCD forms the initial
  3717. * bounding box of the line segment (i.e., the line segment if we were to do
  3718. * no joins).
  3719. */
  3720. var p1 = pv.vector(s1.left, s1.top),
  3721. p2 = pv.vector(s2.left, s2.top),
  3722. p = p2.minus(p1),
  3723. v = p.perp().norm(),
  3724. w = v.times(s1.lineWidth / 2),
  3725. a = p1.plus(w),
  3726. b = p2.plus(w),
  3727. c = p2.minus(w),
  3728. d = p1.minus(w);
  3729. /*
  3730. * Start join. P0 is the previous line segment's start point. We define the
  3731. * cutting plane as the average of the vector perpendicular to P0-P1, and
  3732. * the vector perpendicular to P1-P2. This insures that the cross-section of
  3733. * the line on the cutting plane is equal if the line-width is unchanged.
  3734. * Note that we don't implement miter limits, so these can get wild.
  3735. */
  3736. if (i > 0) {
  3737. var s0 = scenes[i - 1];
  3738. if (s0.visible) {
  3739. var v1 = p1.minus(s0.left, s0.top).perp().norm().plus(v);
  3740. d = intersect(p1, v1, d, p);
  3741. a = intersect(p1, v1, a, p);
  3742. }
  3743. }
  3744. /* Similarly, for end join. */
  3745. if (i < (n - 1)) {
  3746. var s3 = scenes[i + 2];
  3747. if (s3.visible) {
  3748. var v2 = pv.vector(s3.left, s3.top).minus(p2).perp().norm().plus(v);
  3749. c = intersect(p2, v2, c, p);
  3750. b = intersect(p2, v2, b, p);
  3751. }
  3752. }
  3753. /* points */
  3754. var p = a.x + "," + a.y + " "
  3755. + b.x + "," + b.y + " "
  3756. + c.x + "," + c.y + " "
  3757. + d.x + "," + d.y;
  3758. e = this.expect("polygon", e);
  3759. e.setAttribute("cursor", s1.cursor);
  3760. e.setAttribute("points", p);
  3761. e.setAttribute("fill", stroke.color);
  3762. e.setAttribute("fill-opacity", stroke.opacity);
  3763. e = this.append(e, scenes, i);
  3764. }
  3765. return e;
  3766. };
  3767. var guid = 0;
  3768. pv.SvgScene.panel = function(scenes) {
  3769. var g = scenes.$g, e = g && g.firstChild;
  3770. for (var i = 0; i < scenes.length; i++) {
  3771. var s = scenes[i];
  3772. /* visible */
  3773. if (!s.visible) continue;
  3774. /* svg */
  3775. if (!scenes.parent) {
  3776. s.canvas.style.display = "inline-block";
  3777. g = s.canvas.firstChild;
  3778. if (!g) {
  3779. g = s.canvas.appendChild(this.create("svg"));
  3780. g.onclick
  3781. = g.onmousedown
  3782. = g.onmouseup
  3783. = g.onmousemove
  3784. = g.onmouseout
  3785. = g.onmouseover
  3786. = pv.SvgScene.dispatch;
  3787. }
  3788. scenes.$g = g;
  3789. g.setAttribute("width", s.width + s.left + s.right);
  3790. g.setAttribute("height", s.height + s.top + s.bottom);
  3791. if (typeof e == "undefined") e = g.firstChild;
  3792. }
  3793. /* clip (nest children) */
  3794. if (s.overflow == "hidden") {
  3795. var c = this.expect("g", e), id = (guid++).toString(36);
  3796. c.setAttribute("clip-path", "url(#" + id + ")");
  3797. if (!c.parentNode) g.appendChild(c);
  3798. scenes.$g = g = c;
  3799. e = c.firstChild;
  3800. e = this.expect("clipPath", e);
  3801. e.setAttribute("id", id);
  3802. var r = e.firstChild || e.appendChild(this.create("rect"));
  3803. r.setAttribute("x", s.left);
  3804. r.setAttribute("y", s.top);
  3805. r.setAttribute("width", s.width);
  3806. r.setAttribute("height", s.height);
  3807. if (!e.parentNode) g.appendChild(e);
  3808. e = e.nextSibling;
  3809. }
  3810. /* fill */
  3811. e = this.fill(e, scenes, i);
  3812. /* children */
  3813. for (var j = 0; j < s.children.length; j++) {
  3814. s.children[j].$g = e = this.expect("g", e);
  3815. e.setAttribute("transform", "translate(" + s.left + "," + s.top + ")");
  3816. this.updateAll(s.children[j]);
  3817. if (!e.parentNode) g.appendChild(e);
  3818. e = e.nextSibling;
  3819. }
  3820. /* stroke */
  3821. e = this.stroke(e, scenes, i);
  3822. /* clip (restore group) */
  3823. if (s.overflow == "hidden") {
  3824. scenes.$g = g = c.parentNode;
  3825. e = c.nextSibling;
  3826. }
  3827. }
  3828. return e;
  3829. };
  3830. pv.SvgScene.fill = function(e, scenes, i) {
  3831. var s = scenes[i], fill = pv.color(s.fillStyle);
  3832. if (fill.opacity) {
  3833. e = this.expect("rect", e);
  3834. e.setAttribute("x", s.left);
  3835. e.setAttribute("y", s.top);
  3836. e.setAttribute("width", s.width);
  3837. e.setAttribute("height", s.height);
  3838. e.setAttribute("cursor", s.cursor);
  3839. e.setAttribute("fill", fill.color);
  3840. e.setAttribute("fill-opacity", fill.opacity);
  3841. e = this.append(e, scenes, i);
  3842. }
  3843. return e;
  3844. };
  3845. pv.SvgScene.stroke = function(e, scenes, i) {
  3846. var s = scenes[i], stroke = pv.color(s.strokeStyle);
  3847. if (stroke.opacity) {
  3848. e = this.expect("rect", e);
  3849. e.setAttribute("x", s.left);
  3850. e.setAttribute("y", s.top);
  3851. e.setAttribute("width", Math.max(1E-10, s.width));
  3852. e.setAttribute("height", Math.max(1E-10, s.height));
  3853. e.setAttribute("cursor", s.cursor);
  3854. e.setAttribute("fill", "none");
  3855. e.setAttribute("stroke", stroke.color);
  3856. e.setAttribute("stroke-opacity", stroke.opacity);
  3857. e.setAttribute("stroke-width", s.lineWidth);
  3858. e = this.append(e, scenes, i);
  3859. }
  3860. return e;
  3861. };
  3862. pv.SvgScene.rule = function(scenes) {
  3863. var e = scenes.$g.firstChild;
  3864. for (var i = 0; i < scenes.length; i++) {
  3865. var s = scenes[i];
  3866. /* visible */
  3867. if (!s.visible) continue;
  3868. var stroke = pv.color(s.strokeStyle);
  3869. if (!stroke.opacity) continue;
  3870. e = this.expect("line", e);
  3871. e.setAttribute("cursor", s.cursor);
  3872. e.setAttribute("x1", s.left);
  3873. e.setAttribute("y1", s.top);
  3874. e.setAttribute("x2", s.left + s.width);
  3875. e.setAttribute("y2", s.top + s.height);
  3876. e.setAttribute("stroke", stroke.color);
  3877. e.setAttribute("stroke-opacity", stroke.opacity);
  3878. e.setAttribute("stroke-width", s.lineWidth);
  3879. e = this.append(e, scenes, i);
  3880. }
  3881. return e;
  3882. };
  3883. pv.SvgScene.wedge = function(scenes) {
  3884. var e = scenes.$g.firstChild;
  3885. for (var i = 0; i < scenes.length; i++) {
  3886. var s = scenes[i];
  3887. /* visible */
  3888. if (!s.visible) continue;
  3889. var fill = pv.color(s.fillStyle), stroke = pv.color(s.strokeStyle);
  3890. if (!fill.opacity && !stroke.opacity) continue;
  3891. /* points */
  3892. var r1 = s.innerRadius, r2 = s.outerRadius, a = Math.abs(s.angle), p;
  3893. if (a >= 2 * Math.PI) {
  3894. if (r1) {
  3895. p = "M0," + r2
  3896. + "A" + r2 + "," + r2 + " 0 1,1 0," + (-r2)
  3897. + "A" + r2 + "," + r2 + " 0 1,1 0," + r2
  3898. + "M0," + r1
  3899. + "A" + r1 + "," + r1 + " 0 1,1 0," + (-r1)
  3900. + "A" + r1 + "," + r1 + " 0 1,1 0," + r1
  3901. + "Z";
  3902. } else {
  3903. p = "M0," + r2
  3904. + "A" + r2 + "," + r2 + " 0 1,1 0," + (-r2)
  3905. + "A" + r2 + "," + r2 + " 0 1,1 0," + r2
  3906. + "Z";
  3907. }
  3908. } else {
  3909. var sa = Math.min(s.startAngle, s.endAngle),
  3910. ea = Math.max(s.startAngle, s.endAngle),
  3911. c1 = Math.cos(sa), c2 = Math.cos(ea),
  3912. s1 = Math.sin(sa), s2 = Math.sin(ea);
  3913. if (r1) {
  3914. p = "M" + r2 * c1 + "," + r2 * s1
  3915. + "A" + r2 + "," + r2 + " 0 "
  3916. + ((a < Math.PI) ? "0" : "1") + ",1 "
  3917. + r2 * c2 + "," + r2 * s2
  3918. + "L" + r1 * c2 + "," + r1 * s2
  3919. + "A" + r1 + "," + r1 + " 0 "
  3920. + ((a < Math.PI) ? "0" : "1") + ",0 "
  3921. + r1 * c1 + "," + r1 * s1 + "Z";
  3922. } else {
  3923. p = "M" + r2 * c1 + "," + r2 * s1
  3924. + "A" + r2 + "," + r2 + " 0 "
  3925. + ((a < Math.PI) ? "0" : "1") + ",1 "
  3926. + r2 * c2 + "," + r2 * s2 + "L0,0Z";
  3927. }
  3928. }
  3929. e = this.expect("path", e);
  3930. e.setAttribute("fill-rule", "evenodd");
  3931. e.setAttribute("cursor", s.cursor);
  3932. e.setAttribute("transform", "translate(" + s.left + "," + s.top + ")");
  3933. e.setAttribute("d", p);
  3934. e.setAttribute("fill", fill.color);
  3935. e.setAttribute("fill-opacity", fill.opacity);
  3936. e.setAttribute("stroke", stroke.color);
  3937. e.setAttribute("stroke-opacity", stroke.opacity);
  3938. e.setAttribute("stroke-width", s.lineWidth);
  3939. e = this.append(e, scenes, i);
  3940. }
  3941. return e;
  3942. };
  3943. /**
  3944. * Constructs a new mark with default properties. Marks, with the exception of
  3945. * the root panel, are not typically constructed directly; instead, they are
  3946. * added to a panel or an existing mark via {@link pv.Mark#add}.
  3947. *
  3948. * @class Represents a data-driven graphical mark. The <tt>Mark</tt> class is
  3949. * the base class for all graphical marks in Protovis; it does not provide any
  3950. * specific rendering functionality, but together with {@link Panel} establishes
  3951. * the core framework.
  3952. *
  3953. * <p>Concrete mark types include familiar visual elements such as bars, lines
  3954. * and labels. Although a bar mark may be used to construct a bar chart, marks
  3955. * know nothing about charts; it is only through their specification and
  3956. * composition that charts are produced. These building blocks permit many
  3957. * combinatorial possibilities.
  3958. *
  3959. * <p>Marks are associated with <b>data</b>: a mark is generated once per
  3960. * associated datum, mapping the datum to visual <b>properties</b> such as
  3961. * position and color. Thus, a single mark specification represents a set of
  3962. * visual elements that share the same data and visual encoding. The type of
  3963. * mark defines the names of properties and their meaning. A property may be
  3964. * static, ignoring the associated datum and returning a constant; or, it may be
  3965. * dynamic, derived from the associated datum or index. Such dynamic encodings
  3966. * can be specified succinctly using anonymous functions. Special properties
  3967. * called event handlers can be registered to add interactivity.
  3968. *
  3969. * <p>Protovis uses <b>inheritance</b> to simplify the specification of related
  3970. * marks: a new mark can be derived from an existing mark, inheriting its
  3971. * properties. The new mark can then override properties to specify new
  3972. * behavior, potentially in terms of the old behavior. In this way, the old mark
  3973. * serves as the <b>prototype</b> for the new mark. Most mark types share the
  3974. * same basic properties for consistency and to facilitate inheritance.
  3975. *
  3976. * <p>The prioritization of redundant properties is as follows:<ol>
  3977. *
  3978. * <li>If the <tt>width</tt> property is not specified (i.e., null), its value
  3979. * is the width of the parent panel, minus this mark's left and right margins;
  3980. * the left and right margins are zero if not specified.
  3981. *
  3982. * <li>Otherwise, if the <tt>right</tt> margin is not specified, its value is
  3983. * the width of the parent panel, minus this mark's width and left margin; the
  3984. * left margin is zero if not specified.
  3985. *
  3986. * <li>Otherwise, if the <tt>left</tt> property is not specified, its value is
  3987. * the width of the parent panel, minus this mark's width and the right margin.
  3988. *
  3989. * </ol>This prioritization is then duplicated for the <tt>height</tt>,
  3990. * <tt>bottom</tt> and <tt>top</tt> properties, respectively.
  3991. *
  3992. * <p>While most properties are <i>variable</i>, some mark types, such as lines
  3993. * and areas, generate a single visual element rather than a distinct visual
  3994. * element per datum. With these marks, some properties may be <b>fixed</b>.
  3995. * Fixed properties can vary per mark, but not <i>per datum</i>! These
  3996. * properties are evaluated solely for the first (0-index) datum, and typically
  3997. * are specified as a constant. However, it is valid to use a function if the
  3998. * property varies between panels or is dynamically generated.
  3999. *
  4000. * <p>See also the <a href="../../api/">Protovis guide</a>.
  4001. */
  4002. pv.Mark = function() {
  4003. /*
  4004. * TYPE 0 constant defs
  4005. * TYPE 1 function defs
  4006. * TYPE 2 constant properties
  4007. * TYPE 3 function properties
  4008. * in order of evaluation!
  4009. */
  4010. this.$properties = [];
  4011. };
  4012. /** @private TOOD */
  4013. pv.Mark.prototype.properties = {};
  4014. /**
  4015. * @private Defines and registers a property method for the property with the
  4016. * given name. This method should be called on a mark class prototype to define
  4017. * each exposed property. (Note this refers to the JavaScript
  4018. * <tt>prototype</tt>, not the Protovis mark prototype, which is the {@link
  4019. * #proto} field.)
  4020. *
  4021. * <p>The created property method supports several modes of invocation: <ol>
  4022. *
  4023. * <li>If invoked with a <tt>Function</tt> argument, this function is evaluated
  4024. * for each associated datum. The return value of the function is used as the
  4025. * computed property value. The context of the function (<tt>this</tt>) is this
  4026. * mark. The arguments to the function are the associated data of this mark and
  4027. * any enclosing panels. For example, a linear encoding of numerical data to
  4028. * height is specified as
  4029. *
  4030. * <pre>m.height(function(d) d * 100);</pre>
  4031. *
  4032. * The expression <tt>d * 100</tt> will be evaluated for the height property of
  4033. * each mark instance. The return value of the property method (e.g.,
  4034. * <tt>m.height</tt>) is this mark (<tt>m</tt>)).<p>
  4035. *
  4036. * <li>If invoked with a non-function argument, the property is treated as a
  4037. * constant. The return value of the property method (e.g., <tt>m.height</tt>)
  4038. * is this mark.<p>
  4039. *
  4040. * <li>If invoked with no arguments, the computed property value for the current
  4041. * mark instance in the scene graph is returned. This facilitates <i>property
  4042. * chaining</i>, where one mark's properties are defined in terms of another's.
  4043. * For example, to offset a mark's location from its prototype, you might say
  4044. *
  4045. * <pre>m.top(function() this.proto.top() + 10);</pre>
  4046. *
  4047. * Note that the index of the mark being evaluated (in the above example,
  4048. * <tt>this.proto</tt>) is inherited from the <tt>Mark</tt> class and set by
  4049. * this mark. So, if the fifth element's top property is being evaluated, the
  4050. * fifth instance of <tt>this.proto</tt> will similarly be queried for the value
  4051. * of its top property. If the mark being evaluated has a different number of
  4052. * instances, or its data is unrelated, the behavior of this method is
  4053. * undefined. In these cases it may be better to index the <tt>scene</tt>
  4054. * explicitly to specify the exact instance.
  4055. *
  4056. * </ol><p>Property names should follow standard JavaScript method naming
  4057. * conventions, using lowerCamel-style capitalization.
  4058. *
  4059. * <p>In addition to creating the property method, every property is registered
  4060. * in the {@link #properties} map on the <tt>prototype</tt>. Although this is an
  4061. * instance field, it is considered immutable and shared by all instances of a
  4062. * given mark type. The <tt>properties</tt> map can be queried to see if a mark
  4063. * type defines a particular property, such as width or height.
  4064. *
  4065. * @param {string} name the property name.
  4066. */
  4067. pv.Mark.prototype.property = function(name) {
  4068. if (!this.hasOwnProperty("properties")) {
  4069. this.properties = pv.extend(this.properties);
  4070. }
  4071. this.properties[name] = true;
  4072. /*
  4073. * Define the setter-getter globally, since the default behavior should be the
  4074. * same for all properties, and since the Protovis inheritance chain is
  4075. * independent of the JavaScript inheritance chain. For example, anchors
  4076. * define a "name" property that is evaluated on derived marks, even though
  4077. * those marks don't normally have a name.
  4078. */
  4079. pv.Mark.prototype[name] = function(v) {
  4080. if (arguments.length) {
  4081. this.$properties.push({
  4082. name: name,
  4083. type: (typeof v == "function") ? 3 : 2,
  4084. value: v
  4085. });
  4086. return this;
  4087. }
  4088. return this.scene[this.index][name];
  4089. };
  4090. return this;
  4091. };
  4092. /* Define all global properties. */
  4093. pv.Mark.prototype
  4094. .property("data")
  4095. .property("visible")
  4096. .property("left")
  4097. .property("right")
  4098. .property("top")
  4099. .property("bottom")
  4100. .property("cursor")
  4101. .property("title")
  4102. .property("reverse");
  4103. /**
  4104. * The mark type; a lower camelCase name. The type name controls rendering
  4105. * behavior, and unless the rendering engine is extended, must be one of the
  4106. * built-in concrete mark types: area, bar, dot, image, label, line, panel,
  4107. * rule, or wedge.
  4108. *
  4109. * @type string
  4110. * @name pv.Mark.prototype.type
  4111. */
  4112. /**
  4113. * The mark prototype, possibly undefined, from which to inherit property
  4114. * functions. The mark prototype is not necessarily of the same type as this
  4115. * mark. Any properties defined on this mark will override properties inherited
  4116. * either from the prototype or from the type-specific defaults.
  4117. *
  4118. * @type pv.Mark
  4119. * @name pv.Mark.prototype.proto
  4120. */
  4121. /**
  4122. * The enclosing parent panel. The parent panel is generally undefined only for
  4123. * the root panel; however, it is possible to create "offscreen" marks that are
  4124. * used only for inheritance purposes.
  4125. *
  4126. * @type pv.Panel
  4127. * @name pv.Mark.prototype.parent
  4128. */
  4129. /**
  4130. * The child index. -1 if the enclosing parent panel is null; otherwise, the
  4131. * zero-based index of this mark into the parent panel's <tt>children</tt> array.
  4132. *
  4133. * @type number
  4134. */
  4135. pv.Mark.prototype.childIndex = -1;
  4136. /**
  4137. * The mark index. The value of this field depends on which instance (i.e.,
  4138. * which element of the data array) is currently being evaluated. During the
  4139. * build phase, the index is incremented over each datum; when handling events,
  4140. * the index is set to the instance that triggered the event.
  4141. *
  4142. * @type number
  4143. */
  4144. pv.Mark.prototype.index = -1;
  4145. /**
  4146. * The scene graph. The scene graph is an array of objects; each object (or
  4147. * "node") corresponds to an instance of this mark and an element in the data
  4148. * array. The scene graph can be traversed to lookup previously-evaluated
  4149. * properties.
  4150. *
  4151. * <p>For instance, consider a stacked area chart. The bottom property of the
  4152. * area can be defined using the <i>cousin</i> instance, which is the current
  4153. * area instance in the previous instantiation of the parent panel. In this
  4154. * sample code,
  4155. *
  4156. * <pre>new pv.Panel()
  4157. * .width(150).height(150)
  4158. * .add(pv.Panel)
  4159. * .data([[1, 1.2, 1.7, 1.5, 1.7],
  4160. * [.5, 1, .8, 1.1, 1.3],
  4161. * [.2, .5, .8, .9, 1]])
  4162. * .add(pv.Area)
  4163. * .data(function(d) d)
  4164. * .bottom(function() {
  4165. * var c = this.cousin();
  4166. * return c ? (c.bottom + c.height) : 0;
  4167. * })
  4168. * .height(function(d) d * 40)
  4169. * .left(function() this.index * 35)
  4170. * .root.render();</pre>
  4171. *
  4172. * the bottom property is computed based on the upper edge of the corresponding
  4173. * datum in the previous series. The area's parent panel is instantiated once
  4174. * per series, so the cousin refers to the previous (below) area mark. (Note
  4175. * that the position of the upper edge is not the same as the top property,
  4176. * which refers to the top margin: the distance from the top edge of the panel
  4177. * to the top edge of the mark.)
  4178. *
  4179. * @see #first
  4180. * @see #last
  4181. * @see #sibling
  4182. * @see #cousin
  4183. * @name pv.Mark.prototype.scene
  4184. */
  4185. /**
  4186. * The root parent panel. This may be undefined for "offscreen" marks that are
  4187. * created for inheritance purposes only.
  4188. *
  4189. * @type pv.Panel
  4190. * @name pv.Mark.prototype.root
  4191. */
  4192. /**
  4193. * The data property; an array of objects. The size of the array determines the
  4194. * number of marks that will be instantiated; each element in the array will be
  4195. * passed to property functions to compute the property values. Typically, the
  4196. * data property is specified as a constant array, such as
  4197. *
  4198. * <pre>m.data([1, 2, 3, 4, 5]);</pre>
  4199. *
  4200. * However, it is perfectly acceptable to define the data property as a
  4201. * function. This function might compute the data dynamically, allowing
  4202. * different data to be used per enclosing panel. For instance, in the stacked
  4203. * area graph example (see {@link #scene}), the data function on the area mark
  4204. * dereferences each series.
  4205. *
  4206. * @type array
  4207. * @name pv.Mark.prototype.data
  4208. */
  4209. /**
  4210. * The visible property; a boolean determining whether or not the mark instance
  4211. * is visible. If a mark instance is not visible, its other properties will not
  4212. * be evaluated. Similarly, for panels no child marks will be rendered.
  4213. *
  4214. * @type boolean
  4215. * @name pv.Mark.prototype.visible
  4216. */
  4217. /**
  4218. * The left margin; the distance, in pixels, between the left edge of the
  4219. * enclosing panel and the left edge of this mark. Note that in some cases this
  4220. * property may be redundant with the right property, or with the conjunction of
  4221. * right and width.
  4222. *
  4223. * @type number
  4224. * @name pv.Mark.prototype.left
  4225. */
  4226. /**
  4227. * The right margin; the distance, in pixels, between the right edge of the
  4228. * enclosing panel and the right edge of this mark. Note that in some cases this
  4229. * property may be redundant with the left property, or with the conjunction of
  4230. * left and width.
  4231. *
  4232. * @type number
  4233. * @name pv.Mark.prototype.right
  4234. */
  4235. /**
  4236. * The top margin; the distance, in pixels, between the top edge of the
  4237. * enclosing panel and the top edge of this mark. Note that in some cases this
  4238. * property may be redundant with the bottom property, or with the conjunction
  4239. * of bottom and height.
  4240. *
  4241. * @type number
  4242. * @name pv.Mark.prototype.top
  4243. */
  4244. /**
  4245. * The bottom margin; the distance, in pixels, between the bottom edge of the
  4246. * enclosing panel and the bottom edge of this mark. Note that in some cases
  4247. * this property may be redundant with the top property, or with the conjunction
  4248. * of top and height.
  4249. *
  4250. * @type number
  4251. * @name pv.Mark.prototype.bottom
  4252. */
  4253. /**
  4254. * The cursor property; corresponds to the CSS cursor property. This is
  4255. * typically used in conjunction with event handlers to indicate interactivity.
  4256. *
  4257. * @type string
  4258. * @name pv.Mark.prototype.cursor
  4259. * @see <a href="http://www.w3.org/TR/CSS2/ui.html#propdef-cursor">CSS2 cursor</a>
  4260. */
  4261. /**
  4262. * The title property; corresponds to the HTML/SVG title property, allowing the
  4263. * general of simple plain text tooltips.
  4264. *
  4265. * @type string
  4266. * @name pv.Mark.prototype.title
  4267. */
  4268. /**
  4269. * The reverse property; a boolean determining whether marks are ordered from
  4270. * front-to-back or back-to-front. SVG does not support explicit z-ordering;
  4271. * shapes are rendered in the order they appear. Thus, by default, marks are
  4272. * rendered in data order. Setting the reverse property to false reverses the
  4273. * order in which they are rendered; however, the properties are still evaluated
  4274. * (i.e., built) in forward order.
  4275. *
  4276. * @type boolean
  4277. * @name pv.Mark.prototype.reverse
  4278. */
  4279. /**
  4280. * Default properties for all mark types. By default, the data array is the
  4281. * parent data as a single-element array; if the data property is not specified,
  4282. * this causes each mark to be instantiated as a singleton with the parents
  4283. * datum. The visible property is true by default, and the reverse property is
  4284. * false.
  4285. *
  4286. * @type pv.Mark
  4287. */
  4288. pv.Mark.prototype.defaults = new pv.Mark()
  4289. .data(function(d) { return [d]; })
  4290. .visible(true)
  4291. .reverse(false)
  4292. .cursor("")
  4293. .title("");
  4294. /* Private categorical colors for default fill & stroke styles. */
  4295. var defaultFillStyle = pv.Colors.category20().by(pv.parent),
  4296. defaultStrokeStyle = pv.Colors.category10().by(pv.parent);
  4297. /**
  4298. * Sets the prototype of this mark to the specified mark. Any properties not
  4299. * defined on this mark may be inherited from the specified prototype mark, or
  4300. * its prototype, and so on. The prototype mark need not be the same type of
  4301. * mark as this mark. (Note that for inheritance to be useful, properties with
  4302. * the same name on different mark types should have equivalent meaning.)
  4303. *
  4304. * @param {pv.Mark} proto the new prototype.
  4305. * @return {pv.Mark} this mark.
  4306. * @see #add
  4307. */
  4308. pv.Mark.prototype.extend = function(proto) {
  4309. this.proto = proto;
  4310. return this;
  4311. };
  4312. /**
  4313. * Adds a new mark of the specified type to the enclosing parent panel, whilst
  4314. * simultaneously setting the prototype of the new mark to be this mark.
  4315. *
  4316. * @param {function} type the type of mark to add; a constructor, such as
  4317. * <tt>pv.Bar</tt>.
  4318. * @return {pv.Mark} the new mark.
  4319. * @see #extend
  4320. */
  4321. pv.Mark.prototype.add = function(type) {
  4322. return this.parent.add(type).extend(this);
  4323. };
  4324. /**
  4325. * Defines a local variable on this mark. Local variables are initialized once
  4326. * per mark (i.e., per parent panel instance), and can be used to store local
  4327. * state for the mark. Here are a few reasons you might want to use
  4328. * <tt>def</tt>:
  4329. *
  4330. * <p>1. To store local state. For example, say you were visualizing employment
  4331. * statistics, and your root panel had an array of occupations. In a child
  4332. * panel, you might want to initialize a local scale, and reference it from a
  4333. * property function:
  4334. *
  4335. * <pre>.def("y", function(d) pv.Scale.linear(0, pv.max(d.values)).range(0, h))
  4336. * .height(function(d) this.y()(d))</pre>
  4337. *
  4338. * In this example, <tt>this.y()</tt> returns the defined local scale. We then
  4339. * invoke the scale function, passing in the datum, to compute the height. Note
  4340. * that defs are similar to fixed properties: they are only evaluated once per
  4341. * parent panel, and <tt>this.y()</tt> returns a function, rather than
  4342. * automatically evaluating this function as a property.
  4343. *
  4344. * <p>2. To store temporary state for interaction. Say you have an array of
  4345. * bars, and you want to color the bar differently if the mouse is over it. Use
  4346. * <tt>def</tt> to define a local variable, and event handlers to override this
  4347. * variable interactively:
  4348. *
  4349. * <pre>.def("i", -1)
  4350. * .event("mouseover", function() this.i(this.index))
  4351. * .event("mouseout", function() this.i(-1))
  4352. * .fillStyle(function() this.i() == this.index ? "red" : "blue")</pre>
  4353. *
  4354. * Notice that <tt>this.i()</tt> can be used both to set the value of <i>i</i>
  4355. * (when an argument is specified), and to get the value of <i>i</i> (when no
  4356. * arguments are specified). In this way, it's like other property methods.
  4357. *
  4358. * <p>3. To specify fixed properties efficiently. Sometimes, the value of a
  4359. * property may be locally a constant, but dependent on parent panel data which
  4360. * is variable. In this scenario, you can use <tt>def</tt> to define a property;
  4361. * it will only get computed once per mark, rather than once per datum.
  4362. *
  4363. * @param {string} name the name of the local variable.
  4364. * @param {function} [value] an optional initializer; may be a constant or a
  4365. * function.
  4366. */
  4367. pv.Mark.prototype.def = function(name, value) {
  4368. this.$properties.push({
  4369. name: name,
  4370. type: (typeof value == "function") ? 1 : 0,
  4371. value: value
  4372. });
  4373. return this;
  4374. };
  4375. /**
  4376. * Returns an anchor with the specified name. While anchor names are typically
  4377. * constants, the anchor name is a true property, which means you can specify a
  4378. * function to compute the anchor name dynamically. See the
  4379. * {@link pv.Anchor#name} property for details.
  4380. *
  4381. * @param {string} name the anchor name; either a string or a property function.
  4382. * @returns {pv.Anchor} the new anchor.
  4383. */
  4384. pv.Mark.prototype.anchor = function(name) {
  4385. var anchor = new pv.Anchor().extend(this).name(name);
  4386. anchor.parent = this.parent;
  4387. return anchor;
  4388. };
  4389. /**
  4390. * Returns the anchor target of this mark, if it is derived from an anchor;
  4391. * otherwise returns null. For example, if a label is derived from a bar anchor,
  4392. *
  4393. * <pre>bar.anchor("top").add(pv.Label);</pre>
  4394. *
  4395. * then property functions on the label can refer to the bar via the
  4396. * <tt>anchorTarget</tt> method. This method is also useful for mark types
  4397. * defining properties on custom anchors.
  4398. *
  4399. * @returns {pv.Mark} the anchor target of this mark; possibly null.
  4400. */
  4401. pv.Mark.prototype.anchorTarget = function() {
  4402. var target = this;
  4403. while (!(target instanceof pv.Anchor)) {
  4404. target = target.proto;
  4405. if (!target) return null;
  4406. }
  4407. return target.proto;
  4408. };
  4409. /**
  4410. * Returns the first instance of this mark in the scene graph. This method can
  4411. * only be called when the mark is bound to the scene graph (for example, from
  4412. * an event handler, or within a property function).
  4413. *
  4414. * @returns a node in the scene graph.
  4415. */
  4416. pv.Mark.prototype.first = function() {
  4417. return this.scene[0];
  4418. };
  4419. /**
  4420. * Returns the last instance of this mark in the scene graph. This method can
  4421. * only be called when the mark is bound to the scene graph (for example, from
  4422. * an event handler, or within a property function). In addition, note that mark
  4423. * instances are built sequentially, so the last instance of this mark may not
  4424. * yet be constructed.
  4425. *
  4426. * @returns a node in the scene graph.
  4427. */
  4428. pv.Mark.prototype.last = function() {
  4429. return this.scene[this.scene.length - 1];
  4430. };
  4431. /**
  4432. * Returns the previous instance of this mark in the scene graph, or null if
  4433. * this is the first instance.
  4434. *
  4435. * @returns a node in the scene graph, or null.
  4436. */
  4437. pv.Mark.prototype.sibling = function() {
  4438. return (this.index == 0) ? null : this.scene[this.index - 1];
  4439. };
  4440. /**
  4441. * Returns the current instance in the scene graph of this mark, in the previous
  4442. * instance of the enclosing parent panel. May return null if this instance
  4443. * could not be found. See the {@link pv.Layout.stack} function for an example
  4444. * property function using cousin.
  4445. *
  4446. * @see pv.Layout.stack
  4447. * @returns a node in the scene graph, or null.
  4448. */
  4449. pv.Mark.prototype.cousin = function() {
  4450. var p = this.parent, s = p && p.sibling();
  4451. return (s && s.children) ? s.children[this.childIndex][this.index] : null;
  4452. };
  4453. /**
  4454. * Renders this mark, including recursively rendering all child marks if this is
  4455. * a panel.
  4456. */
  4457. pv.Mark.prototype.render = function() {
  4458. /*
  4459. * Rendering consists of three phases: bind, build and update. The update
  4460. * phase is decoupled to allow different rendering engines.
  4461. *
  4462. * In the bind phase, inherited property definitions are cached so they do not
  4463. * need to be queried during build. In the build phase, properties are
  4464. * evaluated, and the scene graph is generated. In the update phase, the scene
  4465. * is rendered by creating and updating elements and attributes in the SVG
  4466. * image. No properties are evaluated during the update phase; instead the
  4467. * values computed previously in the build phase are simply translated into
  4468. * SVG.
  4469. */
  4470. this.bind();
  4471. this.build();
  4472. pv.Scene.updateAll(this.scene);
  4473. };
  4474. /** @private Computes the root data stack for the specified mark. */
  4475. function argv(mark) {
  4476. var stack = [];
  4477. while (mark) {
  4478. stack.push(mark.scene[mark.index].data);
  4479. mark = mark.parent;
  4480. }
  4481. return stack;
  4482. }
  4483. /** @private TODO */
  4484. pv.Mark.prototype.bind = function() {
  4485. var seen = {}, types = [[], [], [], []], data, visible;
  4486. /** TODO */
  4487. function bind(mark) {
  4488. do {
  4489. var properties = mark.$properties;
  4490. for (var i = properties.length - 1; i >= 0 ; i--) {
  4491. var p = properties[i];
  4492. if (!(p.name in seen)) {
  4493. seen[p.name] = 1;
  4494. switch (p.name) {
  4495. case "data": data = p; break;
  4496. case "visible": visible = p; break;
  4497. default: types[p.type].push(p); break;
  4498. }
  4499. }
  4500. }
  4501. } while (mark = mark.proto);
  4502. }
  4503. /** TODO */
  4504. function def(name) {
  4505. return function(v) {
  4506. var defs = this.scene.defs;
  4507. if (arguments.length) {
  4508. if (v == undefined) {
  4509. delete defs.locked[name];
  4510. } else {
  4511. defs.locked[name] = true;
  4512. }
  4513. defs.values[name] = v;
  4514. return this;
  4515. } else {
  4516. return defs.values[name];
  4517. }
  4518. };
  4519. }
  4520. /* Scan the proto chain for all defined properties. */
  4521. bind(this);
  4522. bind(this.defaults);
  4523. types[1].reverse();
  4524. types[3].reverse();
  4525. /* Any undefined properties are null. */
  4526. var mark = this;
  4527. do for (var name in mark.properties) {
  4528. if (!(name in seen)) {
  4529. seen[name] = 1;
  4530. types[2].push({name: name, type: 2, value: null});
  4531. }
  4532. } while (mark = mark.proto);
  4533. /* Define setter-getter for inherited defs. */
  4534. var defs = types[0].concat(types[1]);
  4535. for (var i = 0; i < defs.length; i++) {
  4536. var d = defs[i];
  4537. this[d.name] = def(d.name);
  4538. }
  4539. /* Setup binds to evaluate constants before functions. */
  4540. this.binds = {
  4541. data: data,
  4542. visible: visible,
  4543. defs: defs,
  4544. properties: pv.blend(types)
  4545. };
  4546. };
  4547. /**
  4548. * @private Evaluates properties and computes implied properties. Properties are
  4549. * stored in the {@link #scene} array for each instance of this mark.
  4550. *
  4551. * <p>As marks are built recursively, the {@link #index} property is updated to
  4552. * match the current index into the data array for each mark. Note that the
  4553. * index property is only set for the mark currently being built and its
  4554. * enclosing parent panels. The index property for other marks is unset, but is
  4555. * inherited from the global <tt>Mark</tt> class prototype. This allows mark
  4556. * properties to refer to properties on other marks <i>in the same panel</i>
  4557. * conveniently; however, in general it is better to reference mark instances
  4558. * specifically through the scene graph rather than depending on the magical
  4559. * behavior of {@link #index}.
  4560. *
  4561. * <p>The root scene array has a special property, <tt>data</tt>, which stores
  4562. * the current data stack. The first element in this stack is the current datum,
  4563. * followed by the datum of the enclosing parent panel, and so on. The data
  4564. * stack should not be accessed directly; instead, property functions are passed
  4565. * the current data stack as arguments.
  4566. *
  4567. * <p>The evaluation of the <tt>data</tt> and <tt>visible</tt> properties is
  4568. * special. The <tt>data</tt> property is evaluated first; unlike the other
  4569. * properties, the data stack is from the parent panel, rather than the current
  4570. * mark, since the data is not defined until the data property is evaluated.
  4571. * The <tt>visisble</tt> property is subsequently evaluated for each instance;
  4572. * only if true will the {@link #buildInstance} method be called, evaluating
  4573. * other properties and recursively building the scene graph.
  4574. *
  4575. * <p>If this mark is being re-built, any old instances of this mark that no
  4576. * longer exist (because the new data array contains fewer elements) will be
  4577. * cleared using {@link #clearInstance}.
  4578. *
  4579. * @param parent the instance of the parent panel from the scene graph.
  4580. */
  4581. pv.Mark.prototype.build = function() {
  4582. var scene = this.scene;
  4583. if (!scene) {
  4584. scene = this.scene = [];
  4585. scene.mark = this;
  4586. scene.type = this.type;
  4587. scene.childIndex = this.childIndex;
  4588. if (this.parent) {
  4589. scene.parent = this.parent.scene;
  4590. scene.parentIndex = this.parent.index;
  4591. }
  4592. }
  4593. /* Set the data stack. */
  4594. var stack = this.root.scene.data;
  4595. if (!stack) this.root.scene.data = stack = argv(this.parent);
  4596. /* Evaluate defs. */
  4597. if (this.binds.defs.length) {
  4598. var defs = scene.defs;
  4599. if (!defs) scene.defs = defs = {values: {}, locked: {}};
  4600. for (var i = 0; i < this.binds.defs.length; i++) {
  4601. var d = this.binds.defs[i];
  4602. if (!(d.name in defs.locked)) {
  4603. var v = d.value;
  4604. if (d.type == 1) {
  4605. property = d.name;
  4606. v = v.apply(this, stack);
  4607. }
  4608. defs.values[d.name] = v;
  4609. }
  4610. }
  4611. }
  4612. /* Evaluate special data property. */
  4613. var data = this.binds.data;
  4614. switch (data.type) {
  4615. case 0: case 1: data = defs.values.data; break;
  4616. case 2: data = data.value; break;
  4617. case 3: {
  4618. property = "data";
  4619. data = data.value.apply(this, stack);
  4620. break;
  4621. }
  4622. }
  4623. /* Create, update and delete scene nodes. */
  4624. stack.unshift(null);
  4625. scene.length = data.length;
  4626. for (var i = 0; i < data.length; i++) {
  4627. pv.Mark.prototype.index = this.index = i;
  4628. var s = scene[i];
  4629. if (!s) scene[i] = s = {};
  4630. s.data = stack[0] = data[i];
  4631. /* Evaluate special visible property. */
  4632. var visible = this.binds.visible;
  4633. switch (visible.type) {
  4634. case 0: case 1: visible = defs.values.visible; break;
  4635. case 2: visible = visible.value; break;
  4636. case 3: {
  4637. property = "visible";
  4638. visible = visible.value.apply(this, stack);
  4639. break;
  4640. }
  4641. }
  4642. if (s.visible = visible) this.buildInstance(s);
  4643. }
  4644. stack.shift();
  4645. delete this.index;
  4646. pv.Mark.prototype.index = -1;
  4647. if (!this.parent) scene.data = null;
  4648. return this;
  4649. };
  4650. /**
  4651. * @private Evaluates the specified array of properties for the specified
  4652. * instance <tt>s</tt> in the scene graph.
  4653. *
  4654. * @param s a node in the scene graph; the instance of the mark to build.
  4655. * @param properties an array of properties.
  4656. */
  4657. pv.Mark.prototype.buildProperties = function(s, properties) {
  4658. for (var i = 0, n = properties.length; i < n; i++) {
  4659. var p = properties[i], v = p.value;
  4660. switch (p.type) {
  4661. case 0: case 1: v = this.scene.defs.values[p.name]; break;
  4662. case 3: {
  4663. property = p.name;
  4664. v = v.apply(this, this.root.scene.data);
  4665. break;
  4666. }
  4667. }
  4668. s[p.name] = v;
  4669. }
  4670. };
  4671. /**
  4672. * @private Evaluates all of the properties for this mark for the specified
  4673. * instance <tt>s</tt> in the scene graph. The set of properties to evaluate is
  4674. * retrieved from the {@link #properties} array for this mark type (see {@link
  4675. * #type}). After these properties are evaluated, any <b>implied</b> properties
  4676. * may be computed by the mark and set on the scene graph; see
  4677. * {@link #buildImplied}.
  4678. *
  4679. * <p>For panels, this method recursively builds the scene graph for all child
  4680. * marks as well. In general, this method should not need to be overridden by
  4681. * concrete mark types.
  4682. *
  4683. * @param s a node in the scene graph; the instance of the mark to build.
  4684. */
  4685. pv.Mark.prototype.buildInstance = function(s) {
  4686. this.buildProperties(s, this.binds.properties);
  4687. this.buildImplied(s);
  4688. };
  4689. /**
  4690. * @private Computes the implied properties for this mark for the specified
  4691. * instance <tt>s</tt> in the scene graph. Implied properties are those with
  4692. * dependencies on multiple other properties; for example, the width property
  4693. * may be implied if the left and right properties are set. This method can be
  4694. * overridden by concrete mark types to define new implied properties, if
  4695. * necessary.
  4696. *
  4697. * @param s a node in the scene graph; the instance of the mark to build.
  4698. */
  4699. pv.Mark.prototype.buildImplied = function(s) {
  4700. var l = s.left;
  4701. var r = s.right;
  4702. var t = s.top;
  4703. var b = s.bottom;
  4704. /* Assume width and height are zero if not supported by this mark type. */
  4705. var p = this.properties;
  4706. var w = p.width ? s.width : 0;
  4707. var h = p.height ? s.height : 0;
  4708. /* Compute implied width, right and left. */
  4709. var width = this.parent ? this.parent.width() : (w + l + r);
  4710. if (w == null) {
  4711. w = width - (r = r || 0) - (l = l || 0);
  4712. } else if (r == null) {
  4713. r = width - w - (l = l || 0);
  4714. } else if (l == null) {
  4715. l = width - w - (r = r || 0);
  4716. }
  4717. /* Compute implied height, bottom and top. */
  4718. var height = this.parent ? this.parent.height() : (h + t + b);
  4719. if (h == null) {
  4720. h = height - (t = t || 0) - (b = b || 0);
  4721. } else if (b == null) {
  4722. b = height - h - (t = t || 0);
  4723. } else if (t == null) {
  4724. t = height - h - (b = b || 0);
  4725. }
  4726. s.left = l;
  4727. s.right = r;
  4728. s.top = t;
  4729. s.bottom = b;
  4730. /* Only set width and height if they are supported by this mark type. */
  4731. if (p.width) s.width = w;
  4732. if (p.height) s.height = h;
  4733. };
  4734. /**
  4735. * @private The name of the property being evaluated, for so-called "smart"
  4736. * functions that change behavior depending on which property is being
  4737. * evaluated. This functionality is somewhat magical, so for now, this feature
  4738. * is not exposed outside the library.
  4739. *
  4740. * @type string
  4741. */
  4742. var property;
  4743. /** @private The current mouse location. */
  4744. var pageX = 0, pageY = 0;
  4745. pv.listen(window, "mousemove", function(e) { pageX = e.pageX; pageY = e.pageY; });
  4746. /**
  4747. * Returns the current location of the mouse (cursor) relative to this mark's
  4748. * parent. The <i>x</i> coordinate corresponds to the left margin, while the
  4749. * <i>y</i> coordinate corresponds to the top margin.
  4750. *
  4751. * @returns {pv.Vector} the mouse location.
  4752. */
  4753. pv.Mark.prototype.mouse = function() {
  4754. var x = 0, y = 0, mark = (this instanceof pv.Panel) ? this : this.parent;
  4755. do {
  4756. x += mark.left();
  4757. y += mark.top();
  4758. } while (mark = mark.parent);
  4759. var node = this.root.canvas();
  4760. do {
  4761. x += node.offsetLeft;
  4762. y += node.offsetTop;
  4763. } while (node = node.offsetParent);
  4764. return pv.vector(pageX - x, pageY - y);
  4765. };
  4766. /**
  4767. * Registers an event handler for the specified event type with this mark. When
  4768. * an event of the specified type is triggered, the specified handler will be
  4769. * invoked. The handler is invoked in a similar method to property functions:
  4770. * the context is <tt>this</tt> mark instance, and the arguments are the full
  4771. * data stack. Event handlers can use property methods to manipulate the display
  4772. * properties of the mark:
  4773. *
  4774. * <pre>m.event("click", function() this.fillStyle("red"));</pre>
  4775. *
  4776. * Alternatively, the external data can be manipulated and the visualization
  4777. * redrawn:
  4778. *
  4779. * <pre>m.event("click", function(d) {
  4780. * data = all.filter(function(k) k.name == d);
  4781. * vis.render();
  4782. * });</pre>
  4783. *
  4784. * The return value of the event handler determines which mark gets re-rendered.
  4785. * Use defs ({@link #def}) to set temporary state from event handlers.
  4786. *
  4787. * <p>The complete set of event types is defined by SVG; see the reference
  4788. * below. The set of supported event types is:<ul>
  4789. *
  4790. * <li>click
  4791. * <li>mousedown
  4792. * <li>mouseup
  4793. * <li>mouseover
  4794. * <li>mousemove
  4795. * <li>mouseout
  4796. *
  4797. * </ul>Since Protovis does not specify any concept of focus, it does not
  4798. * support key events; these should be handled outside the visualization using
  4799. * standard JavaScript. In the future, support for interaction may be extended
  4800. * to support additional event types, particularly those most relevant to
  4801. * interactive visualization, such as selection.
  4802. *
  4803. * <p>TODO In the current implementation, event handlers are not inherited from
  4804. * prototype marks. They must be defined explicitly on each interactive mark. In
  4805. * addition, only one event handler for a given event type can be defined; when
  4806. * specifying multiple event handlers for the same type, only the last one will
  4807. * be used.
  4808. *
  4809. * @see <a href="http://www.w3.org/TR/SVGTiny12/interact.html#SVGEvents">SVG events</a>
  4810. * @param {string} type the event type.
  4811. * @param {function} handler the event handler.
  4812. * @returns {pv.Mark} this.
  4813. */
  4814. pv.Mark.prototype.event = function(type, handler) {
  4815. if (!this.$handlers) this.$handlers = {};
  4816. this.$handlers[type] = handler;
  4817. return this;
  4818. };
  4819. /** @private TODO */
  4820. pv.Mark.prototype.dispatch = function(type, scenes, index) {
  4821. var l = this.$handlers && this.$handlers[type];
  4822. if (!l) {
  4823. if (this.parent) {
  4824. this.parent.dispatch(type, scenes.parent, scenes.parentIndex);
  4825. }
  4826. return;
  4827. }
  4828. try {
  4829. /* Setup the scene stack. */
  4830. var mark = this;
  4831. do {
  4832. mark.index = index;
  4833. mark.scene = scenes;
  4834. index = scenes.parentIndex;
  4835. scenes = scenes.parent;
  4836. } while (mark = mark.parent);
  4837. /* Execute the event listener. */
  4838. try {
  4839. mark = l.apply(this, this.root.scene.data = argv(this));
  4840. } finally {
  4841. this.root.scene.data = null;
  4842. }
  4843. /* Update the display. TODO dirtying. */
  4844. if (mark instanceof pv.Mark) mark.render();
  4845. } finally {
  4846. /* Restore the scene stack. */
  4847. var mark = this;
  4848. do {
  4849. if (mark.parent) delete mark.scene;
  4850. delete mark.index;
  4851. } while (mark = mark.parent);
  4852. }
  4853. };
  4854. /**
  4855. * Constructs a new mark anchor with default properties.
  4856. *
  4857. * @class Represents an anchor on a given mark. An anchor is itself a mark, but
  4858. * without a visual representation. It serves only to provide useful default
  4859. * properties that can be inherited by other marks. Each type of mark can define
  4860. * any number of named anchors for convenience. If the concrete mark type does
  4861. * not define an anchor implementation specifically, one will be inherited from
  4862. * the mark's parent class.
  4863. *
  4864. * <p>For example, the bar mark provides anchors for its four sides: left,
  4865. * right, top and bottom. Adding a label to the top anchor of a bar,
  4866. *
  4867. * <pre>bar.anchor("top").add(pv.Label);</pre>
  4868. *
  4869. * will render a text label on the top edge of the bar; the top anchor defines
  4870. * the appropriate position properties (top and left), as well as text-rendering
  4871. * properties for convenience (textAlign and textBaseline).
  4872. *
  4873. * @extends pv.Mark
  4874. */
  4875. pv.Anchor = function() {
  4876. pv.Mark.call(this);
  4877. };
  4878. pv.Anchor.prototype = pv.extend(pv.Mark)
  4879. .property("name");
  4880. /**
  4881. * The anchor name. The set of supported anchor names is dependent on the
  4882. * concrete mark type; see the mark type for details. For example, bars support
  4883. * left, right, top and bottom anchors.
  4884. *
  4885. * <p>While anchor names are typically constants, the anchor name is a true
  4886. * property, which means you can specify a function to compute the anchor name
  4887. * dynamically. For instance, if you wanted to alternate top and bottom anchors,
  4888. * saying
  4889. *
  4890. * <pre>m.anchor(function() (this.index % 2) ? "top" : "bottom").add(pv.Dot);</pre>
  4891. *
  4892. * would have the desired effect.
  4893. *
  4894. * @type string
  4895. * @name pv.Anchor.prototype.name
  4896. */
  4897. /**
  4898. * Constructs a new area mark with default properties. Areas are not typically
  4899. * constructed directly, but by adding to a panel or an existing mark via
  4900. * {@link pv.Mark#add}.
  4901. *
  4902. * @class Represents an area mark: the solid area between two series of
  4903. * connected line segments. Unsurprisingly, areas are used most frequently for
  4904. * area charts.
  4905. *
  4906. * <p>Just as a line represents a polyline, the <tt>Area</tt> mark type
  4907. * represents a <i>polygon</i>. However, an area is not an arbitrary polygon;
  4908. * vertices are paired either horizontally or vertically into parallel
  4909. * <i>spans</i>, and each span corresponds to an associated datum. Either the
  4910. * width or the height must be specified, but not both; this determines whether
  4911. * the area is horizontally-oriented or vertically-oriented. Like lines, areas
  4912. * can be stroked and filled with arbitrary colors.
  4913. *
  4914. * <p>See also the <a href="../../api/Area.html">Area guide</a>.
  4915. *
  4916. * @extends pv.Mark
  4917. */
  4918. pv.Area = function() {
  4919. pv.Mark.call(this);
  4920. };
  4921. pv.Area.prototype = pv.extend(pv.Mark)
  4922. .property("width")
  4923. .property("height")
  4924. .property("lineWidth")
  4925. .property("strokeStyle")
  4926. .property("fillStyle")
  4927. .property("segmented")
  4928. .property("interpolate");
  4929. pv.Area.prototype.type = "area";
  4930. /**
  4931. * The width of a given span, in pixels; used for horizontal spans. If the width
  4932. * is specified, the height property should be 0 (the default). Either the top
  4933. * or bottom property should be used to space the spans vertically, typically as
  4934. * a multiple of the index.
  4935. *
  4936. * @type number
  4937. * @name pv.Area.prototype.width
  4938. */
  4939. /**
  4940. * The height of a given span, in pixels; used for vertical spans. If the height
  4941. * is specified, the width property should be 0 (the default). Either the left
  4942. * or right property should be used to space the spans horizontally, typically
  4943. * as a multiple of the index.
  4944. *
  4945. * @type number
  4946. * @name pv.Area.prototype.height
  4947. */
  4948. /**
  4949. * The width of stroked lines, in pixels; used in conjunction with
  4950. * <tt>strokeStyle</tt> to stroke the perimeter of the area. Unlike the
  4951. * {@link Line} mark type, the entire perimeter is stroked, rather than just one
  4952. * edge. The default value of this property is 1.5, but since the default stroke
  4953. * style is null, area marks are not stroked by default.
  4954. *
  4955. * <p>This property is <i>fixed</i> for non-segmented areas. See
  4956. * {@link pv.Mark}.
  4957. *
  4958. * @type number
  4959. * @name pv.Area.prototype.lineWidth
  4960. */
  4961. /**
  4962. * The style of stroked lines; used in conjunction with <tt>lineWidth</tt> to
  4963. * stroke the perimeter of the area. Unlike the {@link Line} mark type, the
  4964. * entire perimeter is stroked, rather than just one edge. The default value of
  4965. * this property is null, meaning areas are not stroked by default.
  4966. *
  4967. * <p>This property is <i>fixed</i> for non-segmented areas. See
  4968. * {@link pv.Mark}.
  4969. *
  4970. * @type string
  4971. * @name pv.Area.prototype.strokeStyle
  4972. * @see pv.color
  4973. */
  4974. /**
  4975. * The area fill style; if non-null, the interior of the polygon forming the
  4976. * area is filled with the specified color. The default value of this property
  4977. * is a categorical color.
  4978. *
  4979. * <p>This property is <i>fixed</i> for non-segmented areas. See
  4980. * {@link pv.Mark}.
  4981. *
  4982. * @type string
  4983. * @name pv.Area.prototype.fillStyle
  4984. * @see pv.color
  4985. */
  4986. /**
  4987. * Whether the area is segmented; whether variations in fill style, stroke
  4988. * style, and the other properties are treated as fixed. Rendering segmented
  4989. * areas is noticeably slower than non-segmented areas.
  4990. *
  4991. * <p>This property is <i>fixed</i>. See {@link pv.Mark}.
  4992. *
  4993. * @type boolean
  4994. * @name pv.Area.prototype.segmented
  4995. */
  4996. /**
  4997. * How to interpolate between values. Linear interpolation ("linear") is the
  4998. * default, producing a straight line between points. For piecewise constant
  4999. * functions (i.e., step functions), either "step-before" or "step-after" can be
  5000. * specified.
  5001. *
  5002. * <p>Note: this property is currently supported only on non-segmented areas.
  5003. *
  5004. * <p>This property is <i>fixed</i>. See {@link pv.Mark}.
  5005. *
  5006. * @type string
  5007. * @name pv.Area.prototype.interpolate
  5008. */
  5009. /**
  5010. * Default properties for areas. By default, there is no stroke and the fill
  5011. * style is a categorical color.
  5012. *
  5013. * @type pv.Area
  5014. */
  5015. pv.Area.prototype.defaults = new pv.Area()
  5016. .extend(pv.Mark.prototype.defaults)
  5017. .lineWidth(1.5)
  5018. .fillStyle(defaultFillStyle)
  5019. .interpolate("linear");
  5020. /**
  5021. * Constructs a new area anchor with default properties. Areas support five
  5022. * different anchors:<ul>
  5023. *
  5024. * <li>top
  5025. * <li>left
  5026. * <li>center
  5027. * <li>bottom
  5028. * <li>right
  5029. *
  5030. * </ul>In addition to positioning properties (left, right, top bottom), the
  5031. * anchors support text rendering properties (text-align, text-baseline). Text is
  5032. * rendered to appear inside the area polygon.
  5033. *
  5034. * <p>To facilitate stacking of areas, the anchors are defined in terms of their
  5035. * opposite edge. For example, the top anchor defines the bottom property, such
  5036. * that the area grows upwards; the bottom anchor instead defines the top
  5037. * property, such that the area grows downwards. Of course, in general it is
  5038. * more robust to use panels and the cousin accessor to define stacked area
  5039. * marks; see {@link pv.Mark#scene} for an example.
  5040. *
  5041. * @param {string} name the anchor name; either a string or a property function.
  5042. * @returns {pv.Anchor}
  5043. */
  5044. pv.Area.prototype.anchor = function(name) {
  5045. var area = this;
  5046. return pv.Mark.prototype.anchor.call(this, name)
  5047. .left(function() {
  5048. switch (this.name()) {
  5049. case "bottom":
  5050. case "top":
  5051. case "center": return area.left() + area.width() / 2;
  5052. case "right": return area.left() + area.width();
  5053. }
  5054. return null;
  5055. })
  5056. .right(function() {
  5057. switch (this.name()) {
  5058. case "bottom":
  5059. case "top":
  5060. case "center": return area.right() + area.width() / 2;
  5061. case "left": return area.right() + area.width();
  5062. }
  5063. return null;
  5064. })
  5065. .top(function() {
  5066. switch (this.name()) {
  5067. case "left":
  5068. case "right":
  5069. case "center": return area.top() + area.height() / 2;
  5070. case "bottom": return area.top() + area.height();
  5071. }
  5072. return null;
  5073. })
  5074. .bottom(function() {
  5075. switch (this.name()) {
  5076. case "left":
  5077. case "right":
  5078. case "center": return area.bottom() + area.height() / 2;
  5079. case "top": return area.bottom() + area.height();
  5080. }
  5081. return null;
  5082. })
  5083. .textAlign(function() {
  5084. switch (this.name()) {
  5085. case "bottom":
  5086. case "top":
  5087. case "center": return "center";
  5088. case "right": return "right";
  5089. }
  5090. return "left";
  5091. })
  5092. .textBaseline(function() {
  5093. switch (this.name()) {
  5094. case "right":
  5095. case "left":
  5096. case "center": return "middle";
  5097. case "top": return "top";
  5098. }
  5099. return "bottom";
  5100. });
  5101. };
  5102. /**
  5103. * @private Overrides the default behavior of {@link pv.Mark.buildImplied} such
  5104. * that the width and height are set to zero if null.
  5105. *
  5106. * @param s a node in the scene graph; the instance of the mark to build.
  5107. */
  5108. pv.Area.prototype.buildImplied = function(s) {
  5109. if (s.height == null) s.height = 0;
  5110. if (s.width == null) s.width = 0;
  5111. pv.Mark.prototype.buildImplied.call(this, s);
  5112. };
  5113. /** @private */
  5114. var pv_Area_specials = {left:1, top:1, right:1, bottom:1, width:1, height:1, name:1};
  5115. /** @private */
  5116. pv.Area.prototype.bind = function() {
  5117. pv.Mark.prototype.bind.call(this);
  5118. var binds = this.binds,
  5119. properties = binds.properties,
  5120. specials = binds.specials = [];
  5121. for (var i = 0, n = properties.length; i < n; i++) {
  5122. var p = properties[i];
  5123. if (p.name in pv_Area_specials) specials.push(p);
  5124. }
  5125. };
  5126. /** @private */
  5127. pv.Area.prototype.buildInstance = function(s) {
  5128. if (this.index && !this.scene[0].segmented) {
  5129. this.buildProperties(s, this.binds.specials);
  5130. this.buildImplied(s);
  5131. } else {
  5132. pv.Mark.prototype.buildInstance.call(this, s);
  5133. }
  5134. };
  5135. /**
  5136. * Constructs a new bar mark with default properties. Bars are not typically
  5137. * constructed directly, but by adding to a panel or an existing mark via
  5138. * {@link pv.Mark#add}.
  5139. *
  5140. * @class Represents a bar: an axis-aligned rectangle that can be stroked and
  5141. * filled. Bars are used for many chart types, including bar charts, histograms
  5142. * and Gantt charts. Bars can also be used as decorations, for example to draw a
  5143. * frame border around a panel; in fact, a panel is a special type (a subclass)
  5144. * of bar.
  5145. *
  5146. * <p>Bars can be positioned in several ways. Most commonly, one of the four
  5147. * corners is fixed using two margins, and then the width and height properties
  5148. * determine the extent of the bar relative to this fixed location. For example,
  5149. * using the bottom and left properties fixes the bottom-left corner; the width
  5150. * then extends to the right, while the height extends to the top. As an
  5151. * alternative to the four corners, a bar can be positioned exclusively using
  5152. * margins; this is convenient as an inset from the containing panel, for
  5153. * example. See {@link pv.Mark} for details on the prioritization of redundant
  5154. * positioning properties.
  5155. *
  5156. * <p>See also the <a href="../../api/Bar.html">Bar guide</a>.
  5157. *
  5158. * @extends pv.Mark
  5159. */
  5160. pv.Bar = function() {
  5161. pv.Mark.call(this);
  5162. };
  5163. pv.Bar.prototype = pv.extend(pv.Mark)
  5164. .property("width")
  5165. .property("height")
  5166. .property("lineWidth")
  5167. .property("strokeStyle")
  5168. .property("fillStyle");
  5169. pv.Bar.prototype.type = "bar";
  5170. /**
  5171. * The width of the bar, in pixels. If the left position is specified, the bar
  5172. * extends rightward from the left edge; if the right position is specified, the
  5173. * bar extends leftward from the right edge.
  5174. *
  5175. * @type number
  5176. * @name pv.Bar.prototype.width
  5177. */
  5178. /**
  5179. * The height of the bar, in pixels. If the bottom position is specified, the
  5180. * bar extends upward from the bottom edge; if the top position is specified,
  5181. * the bar extends downward from the top edge.
  5182. *
  5183. * @type number
  5184. * @name pv.Bar.prototype.height
  5185. */
  5186. /**
  5187. * The width of stroked lines, in pixels; used in conjunction with
  5188. * <tt>strokeStyle</tt> to stroke the bar's border.
  5189. *
  5190. * @type number
  5191. * @name pv.Bar.prototype.lineWidth
  5192. */
  5193. /**
  5194. * The style of stroked lines; used in conjunction with <tt>lineWidth</tt> to
  5195. * stroke the bar's border. The default value of this property is null, meaning
  5196. * bars are not stroked by default.
  5197. *
  5198. * @type string
  5199. * @name pv.Bar.prototype.strokeStyle
  5200. * @see pv.color
  5201. */
  5202. /**
  5203. * The bar fill style; if non-null, the interior of the bar is filled with the
  5204. * specified color. The default value of this property is a categorical color.
  5205. *
  5206. * @type string
  5207. * @name pv.Bar.prototype.fillStyle
  5208. * @see pv.color
  5209. */
  5210. /**
  5211. * Default properties for bars. By default, there is no stroke and the fill
  5212. * style is a categorical color.
  5213. *
  5214. * @type pv.Bar
  5215. */
  5216. pv.Bar.prototype.defaults = new pv.Bar()
  5217. .extend(pv.Mark.prototype.defaults)
  5218. .lineWidth(1.5)
  5219. .fillStyle(defaultFillStyle);
  5220. /**
  5221. * Constructs a new bar anchor with default properties. Bars support five
  5222. * different anchors:<ul>
  5223. *
  5224. * <li>top
  5225. * <li>left
  5226. * <li>center
  5227. * <li>bottom
  5228. * <li>right
  5229. *
  5230. * </ul>In addition to positioning properties (left, right, top bottom), the
  5231. * anchors support text rendering properties (text-align, text-baseline). Text
  5232. * is rendered to appear inside the bar.
  5233. *
  5234. * <p>To facilitate stacking of bars, the anchors are defined in terms of their
  5235. * opposite edge. For example, the top anchor defines the bottom property, such
  5236. * that the bar grows upwards; the bottom anchor instead defines the top
  5237. * property, such that the bar grows downwards. Of course, in general it is more
  5238. * robust to use panels and the cousin accessor to define stacked bars; see
  5239. * {@link pv.Mark#scene} for an example.
  5240. *
  5241. * <p>Bar anchors also "smartly" specify position properties based on whether
  5242. * the derived mark type supports the width and height properties. If the
  5243. * derived mark type does not support these properties (e.g., dots), the
  5244. * position will be centered on the corresponding edge. Otherwise (e.g., bars),
  5245. * the position will be in the opposite side.
  5246. *
  5247. * @param {string} name the anchor name; either a string or a property function.
  5248. * @returns {pv.Anchor}
  5249. */
  5250. pv.Bar.prototype.anchor = function(name) {
  5251. var bar = this;
  5252. return pv.Mark.prototype.anchor.call(this, name)
  5253. .left(function() {
  5254. switch (this.name()) {
  5255. case "bottom":
  5256. case "top":
  5257. case "center": return bar.left() + (this.properties.width ? 0 : (bar.width() / 2));
  5258. case "right": return bar.left() + bar.width();
  5259. }
  5260. return null;
  5261. })
  5262. .right(function() {
  5263. switch (this.name()) {
  5264. case "bottom":
  5265. case "top":
  5266. case "center": return bar.right() + (this.properties.width ? 0 : (bar.width() / 2));
  5267. case "left": return bar.right() + bar.width();
  5268. }
  5269. return null;
  5270. })
  5271. .top(function() {
  5272. switch (this.name()) {
  5273. case "left":
  5274. case "right":
  5275. case "center": return bar.top() + (this.properties.height ? 0 : (bar.height() / 2));
  5276. case "bottom": return bar.top() + bar.height();
  5277. }
  5278. return null;
  5279. })
  5280. .bottom(function() {
  5281. switch (this.name()) {
  5282. case "left":
  5283. case "right":
  5284. case "center": return bar.bottom() + (this.properties.height ? 0 : (bar.height() / 2));
  5285. case "top": return bar.bottom() + bar.height();
  5286. }
  5287. return null;
  5288. })
  5289. .textAlign(function() {
  5290. switch (this.name()) {
  5291. case "bottom":
  5292. case "top":
  5293. case "center": return "center";
  5294. case "right": return "right";
  5295. }
  5296. return "left";
  5297. })
  5298. .textBaseline(function() {
  5299. switch (this.name()) {
  5300. case "right":
  5301. case "left":
  5302. case "center": return "middle";
  5303. case "top": return "top";
  5304. }
  5305. return "bottom";
  5306. });
  5307. };
  5308. /**
  5309. * Constructs a new dot mark with default properties. Dots are not typically
  5310. * constructed directly, but by adding to a panel or an existing mark via
  5311. * {@link pv.Mark#add}.
  5312. *
  5313. * @class Represents a dot; a dot is simply a sized glyph centered at a given
  5314. * point that can also be stroked and filled. The <tt>size</tt> property is
  5315. * proportional to the area of the rendered glyph to encourage meaningful visual
  5316. * encodings. Dots can visually encode up to eight dimensions of data, though
  5317. * this may be unwise due to integrality. See {@link pv.Mark} for details on the
  5318. * prioritization of redundant positioning properties.
  5319. *
  5320. * <p>See also the <a href="../../api/Dot.html">Dot guide</a>.
  5321. *
  5322. * @extends pv.Mark
  5323. */
  5324. pv.Dot = function() {
  5325. pv.Mark.call(this);
  5326. };
  5327. pv.Dot.prototype = pv.extend(pv.Mark)
  5328. .property("size")
  5329. .property("shape")
  5330. .property("angle")
  5331. .property("lineWidth")
  5332. .property("strokeStyle")
  5333. .property("fillStyle");
  5334. pv.Dot.prototype.type = "dot";
  5335. /**
  5336. * The size of the dot, in square pixels. Square pixels are used such that the
  5337. * area of the dot is linearly proportional to the value of the size property,
  5338. * facilitating representative encodings.
  5339. *
  5340. * @see #radius
  5341. * @type number
  5342. * @name pv.Dot.prototype.size
  5343. */
  5344. /**
  5345. * The shape name. Several shapes are supported:<ul>
  5346. *
  5347. * <li>cross
  5348. * <li>triangle
  5349. * <li>diamond
  5350. * <li>square
  5351. * <li>tick
  5352. * <li>circle
  5353. *
  5354. * </ul>These shapes can be further changed using the {@link #angle} property;
  5355. * for instance, a cross can be turned into a plus by rotating. Similarly, the
  5356. * tick, which is vertical by default, can be rotated horizontally. Note that
  5357. * some shapes (cross and tick) do not have interior areas, and thus do not
  5358. * support fill style meaningfully.
  5359. *
  5360. * <p>Note: it may be more natural to use the {@link pv.Rule} mark for
  5361. * horizontal and vertical ticks. The tick shape is only necessary if angled
  5362. * ticks are needed.
  5363. *
  5364. * @type string
  5365. * @name pv.Dot.prototype.shape
  5366. */
  5367. /**
  5368. * The rotation angle, in radians. Used to rotate shapes, such as to turn a
  5369. * cross into a plus.
  5370. *
  5371. * @type number
  5372. * @name pv.Dot.prototype.angle
  5373. */
  5374. /**
  5375. * The width of stroked lines, in pixels; used in conjunction with
  5376. * <tt>strokeStyle</tt> to stroke the dot's shape.
  5377. *
  5378. * @type number
  5379. * @name pv.Dot.prototype.lineWidth
  5380. */
  5381. /**
  5382. * The style of stroked lines; used in conjunction with <tt>lineWidth</tt> to
  5383. * stroke the dot's shape. The default value of this property is a categorical
  5384. * color.
  5385. *
  5386. * @type string
  5387. * @name pv.Dot.prototype.strokeStyle
  5388. * @see pv.color
  5389. */
  5390. /**
  5391. * The fill style; if non-null, the interior of the dot is filled with the
  5392. * specified color. The default value of this property is null, meaning dots are
  5393. * not filled by default.
  5394. *
  5395. * @type string
  5396. * @name pv.Dot.prototype.fillStyle
  5397. * @see pv.color
  5398. */
  5399. /**
  5400. * Default properties for dots. By default, there is no fill and the stroke
  5401. * style is a categorical color. The default shape is "circle" with size 20.
  5402. *
  5403. * @type pv.Dot
  5404. */
  5405. pv.Dot.prototype.defaults = new pv.Dot()
  5406. .extend(pv.Mark.prototype.defaults)
  5407. .size(20)
  5408. .shape("circle")
  5409. .lineWidth(1.5)
  5410. .strokeStyle(defaultStrokeStyle);
  5411. /**
  5412. * Constructs a new dot anchor with default properties. Dots support five
  5413. * different anchors:<ul>
  5414. *
  5415. * <li>top
  5416. * <li>left
  5417. * <li>center
  5418. * <li>bottom
  5419. * <li>right
  5420. *
  5421. * </ul>In addition to positioning properties (left, right, top bottom), the
  5422. * anchors support text rendering properties (text-align, text-baseline). Text is
  5423. * rendered to appear outside the dot. Note that this behavior is different from
  5424. * other mark anchors, which default to rendering text <i>inside</i> the mark.
  5425. *
  5426. * <p>For consistency with the other mark types, the anchor positions are
  5427. * defined in terms of their opposite edge. For example, the top anchor defines
  5428. * the bottom property, such that a bar added to the top anchor grows upward.
  5429. *
  5430. * @param {string} name the anchor name; either a string or a property function.
  5431. * @returns {pv.Anchor}
  5432. */
  5433. pv.Dot.prototype.anchor = function(name) {
  5434. var dot = this;
  5435. return pv.Mark.prototype.anchor.call(this, name)
  5436. .left(function(d) {
  5437. switch (this.name()) {
  5438. case "bottom":
  5439. case "top":
  5440. case "center": return dot.left();
  5441. case "right": return dot.left() + dot.radius();
  5442. }
  5443. return null;
  5444. })
  5445. .right(function(d) {
  5446. switch (this.name()) {
  5447. case "bottom":
  5448. case "top":
  5449. case "center": return dot.right();
  5450. case "left": return dot.right() + dot.radius();
  5451. }
  5452. return null;
  5453. })
  5454. .top(function(d) {
  5455. switch (this.name()) {
  5456. case "left":
  5457. case "right":
  5458. case "center": return dot.top();
  5459. case "bottom": return dot.top() + dot.radius();
  5460. }
  5461. return null;
  5462. })
  5463. .bottom(function(d) {
  5464. switch (this.name()) {
  5465. case "left":
  5466. case "right":
  5467. case "center": return dot.bottom();
  5468. case "top": return dot.bottom() + dot.radius();
  5469. }
  5470. return null;
  5471. })
  5472. .textAlign(function(d) {
  5473. switch (this.name()) {
  5474. case "left": return "right";
  5475. case "bottom":
  5476. case "top":
  5477. case "center": return "center";
  5478. }
  5479. return "left";
  5480. })
  5481. .textBaseline(function(d) {
  5482. switch (this.name()) {
  5483. case "right":
  5484. case "left":
  5485. case "center": return "middle";
  5486. case "bottom": return "top";
  5487. }
  5488. return "bottom";
  5489. });
  5490. };
  5491. /**
  5492. * Returns the radius of the dot, which is defined to be the square root of the
  5493. * {@link #size} property.
  5494. *
  5495. * @returns {number} the radius.
  5496. */
  5497. pv.Dot.prototype.radius = function() {
  5498. return Math.sqrt(this.size());
  5499. };
  5500. /**
  5501. * Constructs a new label mark with default properties. Labels are not typically
  5502. * constructed directly, but by adding to a panel or an existing mark via
  5503. * {@link pv.Mark#add}.
  5504. *
  5505. * @class Represents a text label, allowing textual annotation of other marks or
  5506. * arbitrary text within the visualization. The character data must be plain
  5507. * text (unicode), though the text can be styled using the {@link #font}
  5508. * property. If rich text is needed, external HTML elements can be overlaid on
  5509. * the canvas by hand.
  5510. *
  5511. * <p>Labels are positioned using the box model, similarly to {@link Dot}. Thus,
  5512. * a label has no width or height, but merely a text anchor location. The text
  5513. * is positioned relative to this anchor location based on the
  5514. * {@link #textAlign}, {@link #textBaseline} and {@link #textMargin} properties.
  5515. * Furthermore, the text may be rotated using {@link #textAngle}.
  5516. *
  5517. * <p>Labels ignore events, so as to not interfere with event handlers on
  5518. * underlying marks, such as bars. In the future, we may support event handlers
  5519. * on labels.
  5520. *
  5521. * <p>See also the <a href="../../api/Label.html">Label guide</a>.
  5522. *
  5523. * @extends pv.Mark
  5524. */
  5525. pv.Label = function() {
  5526. pv.Mark.call(this);
  5527. };
  5528. pv.Label.prototype = pv.extend(pv.Mark)
  5529. .property("text")
  5530. .property("font")
  5531. .property("textAngle")
  5532. .property("textStyle")
  5533. .property("textAlign")
  5534. .property("textBaseline")
  5535. .property("textMargin")
  5536. .property("textShadow");
  5537. pv.Label.prototype.type = "label";
  5538. /**
  5539. * The character data to render; a string. The default value of the text
  5540. * property is the identity function, meaning the label's associated datum will
  5541. * be rendered using its <tt>toString</tt>.
  5542. *
  5543. * @type string
  5544. * @name pv.Label.prototype.text
  5545. */
  5546. /**
  5547. * The font format, per the CSS Level 2 specification. The default font is "10px
  5548. * sans-serif", for consistency with the HTML 5 canvas element specification.
  5549. * Note that since text is not wrapped, any line-height property will be
  5550. * ignored. The other font-style, font-variant, font-weight, font-size and
  5551. * font-family properties are supported.
  5552. *
  5553. * @see <a href="http://www.w3.org/TR/CSS2/fonts.html#font-shorthand">CSS2 fonts</a>
  5554. * @type string
  5555. * @name pv.Label.prototype.font
  5556. */
  5557. /**
  5558. * The rotation angle, in radians. Text is rotated clockwise relative to the
  5559. * anchor location. For example, with the default left alignment, an angle of
  5560. * Math.PI / 2 causes text to proceed downwards. The default angle is zero.
  5561. *
  5562. * @type number
  5563. * @name pv.Label.prototype.textAngle
  5564. */
  5565. /**
  5566. * The text color. The name "textStyle" is used for consistency with "fillStyle"
  5567. * and "strokeStyle", although it might be better to rename this property (and
  5568. * perhaps use the same name as "strokeStyle"). The default color is black.
  5569. *
  5570. * @type string
  5571. * @name pv.Label.prototype.textStyle
  5572. * @see pv.color
  5573. */
  5574. /**
  5575. * The horizontal text alignment. One of:<ul>
  5576. *
  5577. * <li>left
  5578. * <li>center
  5579. * <li>right
  5580. *
  5581. * </ul>The default horizontal alignment is left.
  5582. *
  5583. * @type string
  5584. * @name pv.Label.prototype.textAlign
  5585. */
  5586. /**
  5587. * The vertical text alignment. One of:<ul>
  5588. *
  5589. * <li>top
  5590. * <li>middle
  5591. * <li>bottom
  5592. *
  5593. * </ul>The default vertical alignment is bottom.
  5594. *
  5595. * @type string
  5596. * @name pv.Label.prototype.textBaseline
  5597. */
  5598. /**
  5599. * The text margin; may be specified in pixels, or in font-dependent units (such
  5600. * as ".1ex"). The margin can be used to pad text away from its anchor location,
  5601. * in a direction dependent on the horizontal and vertical alignment
  5602. * properties. For example, if the text is left- and middle-aligned, the margin
  5603. * shifts the text to the right. The default margin is 3 pixels.
  5604. *
  5605. * @type number
  5606. * @name pv.Label.prototype.textMargin
  5607. */
  5608. /**
  5609. * A list of shadow effects to be applied to text, per the CSS Text Level 3
  5610. * text-shadow property. An example specification is "0.1em 0.1em 0.1em
  5611. * rgba(0,0,0,.5)"; the first length is the horizontal offset, the second the
  5612. * vertical offset, and the third the blur radius.
  5613. *
  5614. * @see <a href="http://www.w3.org/TR/css3-text/#text-shadow">CSS3 text</a>
  5615. * @type string
  5616. * @name pv.Label.prototype.textShadow
  5617. */
  5618. /**
  5619. * Default properties for labels. See the individual properties for the default
  5620. * values.
  5621. *
  5622. * @type pv.Label
  5623. */
  5624. pv.Label.prototype.defaults = new pv.Label()
  5625. .extend(pv.Mark.prototype.defaults)
  5626. .text(pv.identity)
  5627. .font("10px sans-serif")
  5628. .textAngle(0)
  5629. .textStyle("black")
  5630. .textAlign("left")
  5631. .textBaseline("bottom")
  5632. .textMargin(3);
  5633. /**
  5634. * Constructs a new line mark with default properties. Lines are not typically
  5635. * constructed directly, but by adding to a panel or an existing mark via
  5636. * {@link pv.Mark#add}.
  5637. *
  5638. * @class Represents a series of connected line segments, or <i>polyline</i>,
  5639. * that can be stroked with a configurable color and thickness. Each
  5640. * articulation point in the line corresponds to a datum; for <i>n</i> points,
  5641. * <i>n</i>-1 connected line segments are drawn. The point is positioned using
  5642. * the box model. Arbitrary paths are also possible, allowing radar plots and
  5643. * other custom visualizations.
  5644. *
  5645. * <p>Like areas, lines can be stroked and filled with arbitrary colors. In most
  5646. * cases, lines are only stroked, but the fill style can be used to construct
  5647. * arbitrary polygons.
  5648. *
  5649. * <p>See also the <a href="../../api/Line.html">Line guide</a>.
  5650. *
  5651. * @extends pv.Mark
  5652. */
  5653. pv.Line = function() {
  5654. pv.Mark.call(this);
  5655. };
  5656. pv.Line.prototype = pv.extend(pv.Mark)
  5657. .property("lineWidth")
  5658. .property("strokeStyle")
  5659. .property("fillStyle")
  5660. .property("segmented")
  5661. .property("interpolate");
  5662. pv.Line.prototype.type = "line";
  5663. /**
  5664. * The width of stroked lines, in pixels; used in conjunction with
  5665. * <tt>strokeStyle</tt> to stroke the line.
  5666. *
  5667. * @type number
  5668. * @name pv.Line.prototype.lineWidth
  5669. */
  5670. /**
  5671. * The style of stroked lines; used in conjunction with <tt>lineWidth</tt> to
  5672. * stroke the line. The default value of this property is a categorical color.
  5673. *
  5674. * @type string
  5675. * @name pv.Line.prototype.strokeStyle
  5676. * @see pv.color
  5677. */
  5678. /**
  5679. * The line fill style; if non-null, the interior of the line is closed and
  5680. * filled with the specified color. The default value of this property is a
  5681. * null, meaning that lines are not filled by default.
  5682. *
  5683. * @type string
  5684. * @name pv.Line.prototype.fillStyle
  5685. * @see pv.color
  5686. */
  5687. /**
  5688. * Whether the line is segmented; whether variations in stroke style, line width
  5689. * and the other properties are treated as fixed. Rendering segmented lines is
  5690. * noticeably slower than non-segmented lines.
  5691. *
  5692. * <p>This property is <i>fixed</i>. See {@link pv.Mark}.
  5693. *
  5694. * @type boolean
  5695. * @name pv.Line.prototype.segmented
  5696. */
  5697. /**
  5698. * How to interpolate between values. Linear interpolation ("linear") is the
  5699. * default, producing a straight line between points. For piecewise constant
  5700. * functions (i.e., step functions), either "step-before" or "step-after" can be
  5701. * specified.
  5702. *
  5703. * <p>Note: this property is currently supported only on non-segmented lines.
  5704. *
  5705. * <p>This property is <i>fixed</i>. See {@link pv.Mark}.
  5706. *
  5707. * @type string
  5708. * @name pv.Line.prototype.interpolate
  5709. */
  5710. /**
  5711. * Default properties for lines. By default, there is no fill and the stroke
  5712. * style is a categorical color. The default interpolation is linear.
  5713. *
  5714. * @type pv.Line
  5715. */
  5716. pv.Line.prototype.defaults = new pv.Line()
  5717. .extend(pv.Mark.prototype.defaults)
  5718. .lineWidth(1.5)
  5719. .strokeStyle(defaultStrokeStyle)
  5720. .interpolate("linear");
  5721. /** @private */
  5722. var pv_Line_specials = {left:1, top:1, right:1, bottom:1, name:1};
  5723. /** @private */
  5724. pv.Line.prototype.bind = function() {
  5725. pv.Mark.prototype.bind.call(this);
  5726. var binds = this.binds,
  5727. properties = binds.properties,
  5728. specials = binds.specials = [];
  5729. for (var i = 0, n = properties.length; i < n; i++) {
  5730. var p = properties[i];
  5731. if (p.name in pv_Line_specials) specials.push(p);
  5732. }
  5733. };
  5734. /** @private */
  5735. pv.Line.prototype.buildInstance = function(s) {
  5736. if (this.index && !this.scene[0].segmented) {
  5737. this.buildProperties(s, this.binds.specials);
  5738. this.buildImplied(s);
  5739. } else {
  5740. pv.Mark.prototype.buildInstance.call(this, s);
  5741. }
  5742. };
  5743. /**
  5744. * Constructs a new rule with default properties. Rules are not typically
  5745. * constructed directly, but by adding to a panel or an existing mark via
  5746. * {@link pv.Mark#add}.
  5747. *
  5748. * @class Represents a horizontal or vertical rule. Rules are frequently used
  5749. * for axes and grid lines. For example, specifying only the bottom property
  5750. * draws horizontal rules, while specifying only the left draws vertical
  5751. * rules. Rules can also be used as thin bars. The visual style is controlled in
  5752. * the same manner as lines.
  5753. *
  5754. * <p>Rules are positioned exclusively the standard box model properties. The
  5755. * following combinations of properties are supported:
  5756. *
  5757. * <table>
  5758. * <thead><th style="width:12em;">Properties</th><th>Orientation</th></thead>
  5759. * <tbody>
  5760. * <tr><td>left</td><td>vertical</td></tr>
  5761. * <tr><td>right</td><td>vertical</td></tr>
  5762. * <tr><td>left, bottom, top</td><td>vertical</td></tr>
  5763. * <tr><td>right, bottom, top</td><td>vertical</td></tr>
  5764. * <tr><td>top</td><td>horizontal</td></tr>
  5765. * <tr><td>bottom</td><td>horizontal</td></tr>
  5766. * <tr><td>top, left, right</td><td>horizontal</td></tr>
  5767. * <tr><td>bottom, left, right</td><td>horizontal</td></tr>
  5768. * <tr><td>left, top, height</td><td>vertical</td></tr>
  5769. * <tr><td>left, bottom, height</td><td>vertical</td></tr>
  5770. * <tr><td>right, top, height</td><td>vertical</td></tr>
  5771. * <tr><td>right, bottom, height</td><td>vertical</td></tr>
  5772. * <tr><td>left, top, width</td><td>horizontal</td></tr>
  5773. * <tr><td>left, bottom, width</td><td>horizontal</td></tr>
  5774. * <tr><td>right, top, width</td><td>horizontal</td></tr>
  5775. * <tr><td>right, bottom, width</td><td>horizontal</td></tr>
  5776. * </tbody>
  5777. * </table>
  5778. *
  5779. * <p>Small rules can be used as tick marks; alternatively, a {@link Dot} with
  5780. * the "tick" shape can be used.
  5781. *
  5782. * <p>See also the <a href="../../api/Rule.html">Rule guide</a>.
  5783. *
  5784. * @see pv.Line
  5785. * @extends pv.Mark
  5786. */
  5787. pv.Rule = function() {
  5788. pv.Mark.call(this);
  5789. };
  5790. pv.Rule.prototype = pv.extend(pv.Mark)
  5791. .property("width")
  5792. .property("height")
  5793. .property("lineWidth")
  5794. .property("strokeStyle");
  5795. pv.Rule.prototype.type = "rule";
  5796. /**
  5797. * The width of the rule, in pixels. If the left position is specified, the rule
  5798. * extends rightward from the left edge; if the right position is specified, the
  5799. * rule extends leftward from the right edge.
  5800. *
  5801. * @type number
  5802. * @name pv.Rule.prototype.width
  5803. */
  5804. /**
  5805. * The height of the rule, in pixels. If the bottom position is specified, the
  5806. * rule extends upward from the bottom edge; if the top position is specified,
  5807. * the rule extends downward from the top edge.
  5808. *
  5809. * @type number
  5810. * @name pv.Rule.prototype.height
  5811. */
  5812. /**
  5813. * The width of stroked lines, in pixels; used in conjunction with
  5814. * <tt>strokeStyle</tt> to stroke the rule. The default value is 1 pixel.
  5815. *
  5816. * @type number
  5817. * @name pv.Rule.prototype.lineWidth
  5818. */
  5819. /**
  5820. * The style of stroked lines; used in conjunction with <tt>lineWidth</tt> to
  5821. * stroke the rule. The default value of this property is black.
  5822. *
  5823. * @type string
  5824. * @name pv.Rule.prototype.strokeStyle
  5825. * @see pv.color
  5826. */
  5827. /**
  5828. * Default properties for rules. By default, a single-pixel black line is
  5829. * stroked.
  5830. *
  5831. * @type pv.Rule
  5832. */
  5833. pv.Rule.prototype.defaults = new pv.Rule()
  5834. .extend(pv.Mark.prototype.defaults)
  5835. .lineWidth(1)
  5836. .strokeStyle("black");
  5837. /**
  5838. * Constructs a new rule anchor with default properties. Rules support five
  5839. * different anchors:<ul>
  5840. *
  5841. * <li>top
  5842. * <li>left
  5843. * <li>center
  5844. * <li>bottom
  5845. * <li>right
  5846. *
  5847. * </ul>In addition to positioning properties (left, right, top bottom), the
  5848. * anchors support text rendering properties (text-align, text-baseline). Text is
  5849. * rendered to appear outside the rule. Note that this behavior is different
  5850. * from other mark anchors, which default to rendering text <i>inside</i> the
  5851. * mark.
  5852. *
  5853. * <p>For consistency with the other mark types, the anchor positions are
  5854. * defined in terms of their opposite edge. For example, the top anchor defines
  5855. * the bottom property, such that a bar added to the top anchor grows upward.
  5856. *
  5857. * @param {string} name the anchor name; either a string or a property function.
  5858. * @returns {pv.Anchor}
  5859. */
  5860. pv.Rule.prototype.anchor = function(name) {
  5861. return pv.Bar.prototype.anchor.call(this, name)
  5862. .textAlign(function(d) {
  5863. switch (this.name()) {
  5864. case "left": return "right";
  5865. case "bottom":
  5866. case "top":
  5867. case "center": return "center";
  5868. case "right": return "left";
  5869. }
  5870. })
  5871. .textBaseline(function(d) {
  5872. switch (this.name()) {
  5873. case "right":
  5874. case "left":
  5875. case "center": return "middle";
  5876. case "top": return "bottom";
  5877. case "bottom": return "top";
  5878. }
  5879. });
  5880. };
  5881. /**
  5882. * @private Overrides the default behavior of {@link pv.Mark.buildImplied} to
  5883. * determine the orientation (vertical or horizontal) of the rule.
  5884. *
  5885. * @param s a node in the scene graph; the instance of the rule to build.
  5886. */
  5887. pv.Rule.prototype.buildImplied = function(s) {
  5888. var l = s.left, r = s.right, t = s.top, b = s.bottom;
  5889. /* Determine horizontal or vertical orientation. */
  5890. if ((s.width != null)
  5891. || ((l == null) && (r == null))
  5892. || ((r != null) && (l != null))) {
  5893. s.height = 0;
  5894. } else {
  5895. s.width = 0;
  5896. }
  5897. pv.Mark.prototype.buildImplied.call(this, s);
  5898. };
  5899. /**
  5900. * Constructs a new, empty panel with default properties. Panels, with the
  5901. * exception of the root panel, are not typically constructed directly; instead,
  5902. * they are added to an existing panel or mark via {@link pv.Mark#add}.
  5903. *
  5904. * @class Represents a container mark. Panels allow repeated or nested
  5905. * structures, commonly used in small multiple displays where a small
  5906. * visualization is tiled to facilitate comparison across one or more
  5907. * dimensions. Other types of visualizations may benefit from repeated and
  5908. * possibly overlapping structure as well, such as stacked area charts. Panels
  5909. * can also offset the position of marks to provide padding from surrounding
  5910. * content.
  5911. *
  5912. * <p>All Protovis displays have at least one panel; this is the root panel to
  5913. * which marks are rendered. The box model properties (four margins, width and
  5914. * height) are used to offset the positions of contained marks. The data
  5915. * property determines the panel count: a panel is generated once per associated
  5916. * datum. When nested panels are used, property functions can declare additional
  5917. * arguments to access the data associated with enclosing panels.
  5918. *
  5919. * <p>Panels can be rendered inline, facilitating the creation of sparklines.
  5920. * This allows designers to reuse browser layout features, such as text flow and
  5921. * tables; designers can also overlay HTML elements such as rich text and
  5922. * images.
  5923. *
  5924. * <p>All panels have a <tt>children</tt> array (possibly empty) containing the
  5925. * child marks in the order they were added. Panels also have a <tt>root</tt>
  5926. * field which points to the root (outermost) panel; the root panel's root field
  5927. * points to itself.
  5928. *
  5929. * <p>See also the <a href="../../api/">Protovis guide</a>.
  5930. *
  5931. * @extends pv.Bar
  5932. */
  5933. pv.Panel = function() {
  5934. pv.Bar.call(this);
  5935. /**
  5936. * The child marks; zero or more {@link pv.Mark}s in the order they were
  5937. * added.
  5938. *
  5939. * @see #add
  5940. * @type pv.Mark[]
  5941. */
  5942. this.children = [];
  5943. this.root = this;
  5944. /**
  5945. * The internal $dom field is set by the Protovis loader; see lang/init.js. It
  5946. * refers to the script element that contains the Protovis specification, so
  5947. * that the panel knows where in the DOM to insert the generated SVG element.
  5948. *
  5949. * @private
  5950. */
  5951. this.$dom = pv.Panel.$dom;
  5952. };
  5953. pv.Panel.prototype = pv.extend(pv.Bar)
  5954. .property("canvas")
  5955. .property("overflow");
  5956. pv.Panel.prototype.type = "panel";
  5957. /**
  5958. * The canvas element; either the string ID of the canvas element in the current
  5959. * document, or a reference to the canvas element itself. If null, a canvas
  5960. * element will be created and inserted into the document at the location of the
  5961. * script element containing the current Protovis specification. This property
  5962. * only applies to root panels and is ignored on nested panels.
  5963. *
  5964. * <p>Note: the "canvas" element here refers to a <tt>div</tt> (or other suitable
  5965. * HTML container element), <i>not</i> a <tt>canvas</tt> element. The name of
  5966. * this property is a historical anachronism from the first implementation that
  5967. * used HTML 5 canvas, rather than SVG.
  5968. *
  5969. * @type string
  5970. * @name pv.Panel.prototype.canvas
  5971. */
  5972. /**
  5973. * Default properties for panels. By default, the margins are zero, the fill
  5974. * style is transparent.
  5975. *
  5976. * @type pv.Panel
  5977. */
  5978. pv.Panel.prototype.defaults = new pv.Panel()
  5979. .extend(pv.Bar.prototype.defaults)
  5980. .fillStyle(null)
  5981. .overflow("visible");
  5982. /**
  5983. * Returns an anchor with the specified name. This method is overridden since
  5984. * the behavior of Panel anchors is slightly different from normal anchors:
  5985. * adding to an anchor adds to the anchor target's, rather than the anchor
  5986. * target's parent. To avoid double margins, we override the anchor's proto so
  5987. * that the margins are zero.
  5988. *
  5989. * @param {string} name the anchor name; either a string or a property function.
  5990. * @returns {pv.Anchor} the new anchor.
  5991. */
  5992. pv.Panel.prototype.anchor = function(name) {
  5993. /* A "view" of this panel whose margins appear to be zero. */
  5994. function z() { return 0; }
  5995. z.prototype = this;
  5996. z.prototype.left = z.prototype.right = z.prototype.top = z.prototype.bottom = z;
  5997. var anchor = pv.Bar.prototype.anchor.call(new z(), name)
  5998. .data(function(d) { return [d]; });
  5999. anchor.parent = this;
  6000. return anchor;
  6001. };
  6002. /**
  6003. * Adds a new mark of the specified type to this panel. Unlike the normal
  6004. * {@link Mark#add} behavior, adding a mark to a panel does not cause the mark
  6005. * to inherit from the panel. Since the contained marks are offset by the panel
  6006. * margins already, inheriting properties is generally undesirable; of course,
  6007. * it is always possible to change this behavior by calling {@link Mark#extend}
  6008. * explicitly.
  6009. *
  6010. * @param {function} type the type of the new mark to add.
  6011. * @returns {pv.Mark} the new mark.
  6012. */
  6013. pv.Panel.prototype.add = function(type) {
  6014. var child = new type();
  6015. child.parent = this;
  6016. child.root = this.root;
  6017. child.childIndex = this.children.length;
  6018. this.children.push(child);
  6019. return child;
  6020. };
  6021. /** @private TODO */
  6022. pv.Panel.prototype.bind = function() {
  6023. pv.Mark.prototype.bind.call(this);
  6024. for (var i = 0; i < this.children.length; i++) {
  6025. this.children[i].bind();
  6026. }
  6027. };
  6028. /**
  6029. * @private Evaluates all of the properties for this panel for the specified
  6030. * instance <tt>s</tt> in the scene graph, including recursively building the
  6031. * scene graph for child marks.
  6032. *
  6033. * @param s a node in the scene graph; the instance of the panel to build.
  6034. * @see Mark#scene
  6035. */
  6036. pv.Panel.prototype.buildInstance = function(s) {
  6037. pv.Bar.prototype.buildInstance.call(this, s);
  6038. if (!s.children) s.children = [];
  6039. /*
  6040. * Build each child, passing in the parent (this panel) scene graph node. The
  6041. * child mark's scene is initialized from the corresponding entry in the
  6042. * existing scene graph, such that properties from the previous build can be
  6043. * reused; this is largely to facilitate the recycling of SVG elements.
  6044. */
  6045. for (var i = 0; i < this.children.length; i++) {
  6046. this.children[i].scene = s.children[i]; // possibly undefined
  6047. this.children[i].build();
  6048. }
  6049. /*
  6050. * Once the child marks have been built, the new scene graph nodes are removed
  6051. * from the child marks and placed into the scene graph. The nodes cannot
  6052. * remain on the child nodes because this panel (or a parent panel) may be
  6053. * instantiated multiple times!
  6054. */
  6055. for (var i = 0; i < this.children.length; i++) {
  6056. s.children[i] = this.children[i].scene;
  6057. delete this.children[i].scene;
  6058. }
  6059. /* Delete any expired child scenes, should child marks have been removed. */
  6060. s.children.length = this.children.length;
  6061. };
  6062. /**
  6063. * @private Computes the implied properties for this panel for the specified
  6064. * instance <tt>s</tt> in the scene graph. Panels have two implied
  6065. * properties:<ul>
  6066. *
  6067. * <li>The <tt>canvas</tt> property references the DOM element, typically a DIV,
  6068. * that contains the SVG element that is used to display the visualization. This
  6069. * property may be specified as a string, referring to the unique ID of the
  6070. * element in the DOM. The string is converted to a reference to the DOM
  6071. * element. The width and height of the SVG element is inferred from this DOM
  6072. * element. If no canvas property is specified, a new SVG element is created and
  6073. * inserted into the document, using the panel dimensions; see
  6074. * {@link #createCanvas}.
  6075. *
  6076. * <li>The <tt>children</tt> array, while not a property per se, contains the
  6077. * scene graph for each child mark. This array is initialized to be empty, and
  6078. * is populated above in {@link #buildInstance}.
  6079. *
  6080. * </ul>The current implementation creates the SVG element, if necessary, during
  6081. * the build phase; in the future, it may be preferrable to move this to the
  6082. * update phase, although then the canvas property would be undefined. In
  6083. * addition, DOM inspection is necessary to define the implied width and height
  6084. * properties that may be inferred from the DOM.
  6085. *
  6086. * @param s a node in the scene graph; the instance of the panel to build.
  6087. */
  6088. pv.Panel.prototype.buildImplied = function(s) {
  6089. if (!this.parent) {
  6090. var c = s.canvas;
  6091. if (c) {
  6092. if (typeof c == "string") c = document.getElementById(c);
  6093. /* Clear the container if it's not associated with this panel. */
  6094. if (c.$panel != this) {
  6095. c.$panel = this;
  6096. c.innerHTML = "";
  6097. }
  6098. /* If width and height weren't specified, inspect the container. */
  6099. var w, h;
  6100. if (s.width == null) {
  6101. w = parseFloat(pv.css(c, "width"));
  6102. s.width = w - s.left - s.right;
  6103. }
  6104. if (s.height == null) {
  6105. h = parseFloat(pv.css(c, "height"));
  6106. s.height = h - s.top - s.bottom;
  6107. }
  6108. } else if (s.$canvas) {
  6109. /*
  6110. * If the canvas property is null, and we previously created a canvas for
  6111. * this scene node, reuse the previous canvas rather than creating a new
  6112. * one.
  6113. */
  6114. c = s.$canvas;
  6115. } else {
  6116. /**
  6117. * Returns the last element in the current document's body. The canvas
  6118. * element is appended to this last element if another DOM element has not
  6119. * already been specified via the <tt>$dom</tt> field.
  6120. */
  6121. function lastElement() {
  6122. var node = document.body;
  6123. while (node.lastChild && node.lastChild.tagName) {
  6124. node = node.lastChild;
  6125. }
  6126. return (node == document.body) ? node : node.parentNode;
  6127. }
  6128. /* Insert a new container into the DOM. */
  6129. c = s.$canvas = document.createElement("span");
  6130. this.$dom // script element for text/javascript+protovis
  6131. ? ((this.$dom.tagName != 'SCRIPT') ? this.$dom.appendChild(c) : this.$dom.parentNode.insertBefore(c, this.$dom))
  6132. : lastElement().appendChild(c);
  6133. }
  6134. s.canvas = c;
  6135. }
  6136. pv.Bar.prototype.buildImplied.call(this, s);
  6137. };
  6138. /**
  6139. * Constructs a new dot mark with default properties. Images are not typically
  6140. * constructed directly, but by adding to a panel or an existing mark via
  6141. * {@link pv.Mark#add}.
  6142. *
  6143. * @class Represents an image. Images share the same layout and style properties as
  6144. * bars, in conjunction with an external image such as PNG or JPEG. The image is
  6145. * specified via the {@link #url} property. The fill, if specified, appears
  6146. * beneath the image, while the optional stroke appears above the image.
  6147. *
  6148. * <p>TODO Restore support for dynamic images (such as heatmaps). These were
  6149. * supported in the canvas implementation using the pixel buffer API; although
  6150. * SVG does not support pixel manipulation, it is possible to embed a canvas
  6151. * element in SVG using foreign objects.
  6152. *
  6153. * <p>TODO Allow different modes of image placement: "scale" -- scale and
  6154. * preserve aspect ratio, "tile" -- repeat the image, "center" -- center the
  6155. * image, "fill" -- scale without preserving aspect ratio.
  6156. *
  6157. * <p>See {@link pv.Bar} for details on positioning properties.
  6158. *
  6159. * @extends pv.Bar
  6160. */
  6161. pv.Image = function() {
  6162. pv.Bar.call(this);
  6163. };
  6164. pv.Image.prototype = pv.extend(pv.Bar)
  6165. .property("url");
  6166. pv.Image.prototype.type = "image";
  6167. /**
  6168. * The URL of the image to display. The set of supported image types is
  6169. * browser-dependent; PNG and JPEG are recommended.
  6170. *
  6171. * @type string
  6172. * @name pv.Image.prototype.url
  6173. */
  6174. /**
  6175. * Default properties for images. By default, there is no stroke or fill style.
  6176. *
  6177. * @type pv.Image
  6178. */
  6179. pv.Image.prototype.defaults = new pv.Image()
  6180. .extend(pv.Bar.prototype.defaults)
  6181. .fillStyle(null);
  6182. /**
  6183. * Constructs a new wedge with default properties. Wedges are not typically
  6184. * constructed directly, but by adding to a panel or an existing mark via
  6185. * {@link pv.Mark#add}.
  6186. *
  6187. * @class Represents a wedge, or pie slice. Specified in terms of start and end
  6188. * angle, inner and outer radius, wedges can be used to construct donut charts
  6189. * and polar bar charts as well. If the {@link #angle} property is used, the end
  6190. * angle is implied by adding this value to start angle. By default, the start
  6191. * angle is the previously-generated wedge's end angle. This design allows
  6192. * explicit control over the wedge placement if desired, while offering
  6193. * convenient defaults for the construction of radial graphs.
  6194. *
  6195. * <p>The center point of the circle is positioned using the standard box model.
  6196. * The wedge can be stroked and filled, similar to {link Bar}.
  6197. *
  6198. * <p>See also the <a href="../../api/Wedge.html">Wedge guide</a>.
  6199. *
  6200. * @extends pv.Mark
  6201. */
  6202. pv.Wedge = function() {
  6203. pv.Mark.call(this);
  6204. };
  6205. pv.Wedge.prototype = pv.extend(pv.Mark)
  6206. .property("startAngle")
  6207. .property("endAngle")
  6208. .property("angle")
  6209. .property("innerRadius")
  6210. .property("outerRadius")
  6211. .property("lineWidth")
  6212. .property("strokeStyle")
  6213. .property("fillStyle");
  6214. pv.Wedge.prototype.type = "wedge";
  6215. /**
  6216. * The start angle of the wedge, in radians. The start angle is measured
  6217. * clockwise from the 3 o'clock position. The default value of this property is
  6218. * the end angle of the previous instance (the {@link Mark#sibling}), or -PI / 2
  6219. * for the first wedge; for pie and donut charts, typically only the
  6220. * {@link #angle} property needs to be specified.
  6221. *
  6222. * @type number
  6223. * @name pv.Wedge.prototype.startAngle
  6224. */
  6225. /**
  6226. * The end angle of the wedge, in radians. If not specified, the end angle is
  6227. * implied as the start angle plus the {@link #angle}.
  6228. *
  6229. * @type number
  6230. * @name pv.Wedge.prototype.endAngle
  6231. */
  6232. /**
  6233. * The angular span of the wedge, in radians. This property is used if end angle
  6234. * is not specified.
  6235. *
  6236. * @type number
  6237. * @name pv.Wedge.prototype.angle
  6238. */
  6239. /**
  6240. * The inner radius of the wedge, in pixels. The default value of this property
  6241. * is zero; a positive value will produce a donut slice rather than a pie slice.
  6242. * The inner radius can vary per-wedge.
  6243. *
  6244. * @type number
  6245. * @name pv.Wedge.prototype.innerRadius
  6246. */
  6247. /**
  6248. * The outer radius of the wedge, in pixels. This property is required. For
  6249. * pies, only this radius is required; for donuts, the inner radius must be
  6250. * specified as well. The outer radius can vary per-wedge.
  6251. *
  6252. * @type number
  6253. * @name pv.Wedge.prototype.outerRadius
  6254. */
  6255. /**
  6256. * The width of stroked lines, in pixels; used in conjunction with
  6257. * <tt>strokeStyle</tt> to stroke the wedge's border.
  6258. *
  6259. * @type number
  6260. * @name pv.Wedge.prototype.lineWidth
  6261. */
  6262. /**
  6263. * The style of stroked lines; used in conjunction with <tt>lineWidth</tt> to
  6264. * stroke the wedge's border. The default value of this property is null,
  6265. * meaning wedges are not stroked by default.
  6266. *
  6267. * @type string
  6268. * @name pv.Wedge.prototype.strokeStyle
  6269. * @see pv.color
  6270. */
  6271. /**
  6272. * The wedge fill style; if non-null, the interior of the wedge is filled with
  6273. * the specified color. The default value of this property is a categorical
  6274. * color.
  6275. *
  6276. * @type string
  6277. * @name pv.Wedge.prototype.fillStyle
  6278. * @see pv.color
  6279. */
  6280. /**
  6281. * Default properties for wedges. By default, there is no stroke and the fill
  6282. * style is a categorical color.
  6283. *
  6284. * @type pv.Wedge
  6285. */
  6286. pv.Wedge.prototype.defaults = new pv.Wedge()
  6287. .extend(pv.Mark.prototype.defaults)
  6288. .startAngle(function() {
  6289. var s = this.sibling();
  6290. return s ? s.endAngle : -Math.PI / 2;
  6291. })
  6292. .innerRadius(0)
  6293. .lineWidth(1.5)
  6294. .strokeStyle(null)
  6295. .fillStyle(defaultFillStyle.by(pv.index));
  6296. /**
  6297. * Returns the mid-radius of the wedge, which is defined as half-way between the
  6298. * inner and outer radii.
  6299. *
  6300. * @see #innerRadius
  6301. * @see #outerRadius
  6302. * @returns {number} the mid-radius, in pixels.
  6303. */
  6304. pv.Wedge.prototype.midRadius = function() {
  6305. return (this.innerRadius() + this.outerRadius()) / 2;
  6306. };
  6307. /**
  6308. * Returns the mid-angle of the wedge, which is defined as half-way between the
  6309. * start and end angles.
  6310. *
  6311. * @see #startAngle
  6312. * @see #endAngle
  6313. * @returns {number} the mid-angle, in radians.
  6314. */
  6315. pv.Wedge.prototype.midAngle = function() {
  6316. return (this.startAngle() + this.endAngle()) / 2;
  6317. };
  6318. /**
  6319. * Constructs a new wedge anchor with default properties. Wedges support five
  6320. * different anchors:<ul>
  6321. *
  6322. * <li>outer
  6323. * <li>inner
  6324. * <li>center
  6325. * <li>start
  6326. * <li>end
  6327. *
  6328. * </ul>In addition to positioning properties (left, right, top bottom), the
  6329. * anchors support text rendering properties (text-align, text-baseline,
  6330. * textAngle). Text is rendered to appear inside the wedge.
  6331. *
  6332. * @param {string} name the anchor name; either a string or a property function.
  6333. * @returns {pv.Anchor}
  6334. */
  6335. pv.Wedge.prototype.anchor = function(name) {
  6336. var w = this;
  6337. return pv.Mark.prototype.anchor.call(this, name)
  6338. .left(function() {
  6339. switch (this.name()) {
  6340. case "outer": return w.left() + w.outerRadius() * Math.cos(w.midAngle());
  6341. case "inner": return w.left() + w.innerRadius() * Math.cos(w.midAngle());
  6342. case "start": return w.left() + w.midRadius() * Math.cos(w.startAngle());
  6343. case "center": return w.left() + w.midRadius() * Math.cos(w.midAngle());
  6344. case "end": return w.left() + w.midRadius() * Math.cos(w.endAngle());
  6345. }
  6346. })
  6347. .right(function() {
  6348. switch (this.name()) {
  6349. case "outer": return w.right() + w.outerRadius() * Math.cos(w.midAngle());
  6350. case "inner": return w.right() + w.innerRadius() * Math.cos(w.midAngle());
  6351. case "start": return w.right() + w.midRadius() * Math.cos(w.startAngle());
  6352. case "center": return w.right() + w.midRadius() * Math.cos(w.midAngle());
  6353. case "end": return w.right() + w.midRadius() * Math.cos(w.endAngle());
  6354. }
  6355. })
  6356. .top(function() {
  6357. switch (this.name()) {
  6358. case "outer": return w.top() + w.outerRadius() * Math.sin(w.midAngle());
  6359. case "inner": return w.top() + w.innerRadius() * Math.sin(w.midAngle());
  6360. case "start": return w.top() + w.midRadius() * Math.sin(w.startAngle());
  6361. case "center": return w.top() + w.midRadius() * Math.sin(w.midAngle());
  6362. case "end": return w.top() + w.midRadius() * Math.sin(w.endAngle());
  6363. }
  6364. })
  6365. .bottom(function() {
  6366. switch (this.name()) {
  6367. case "outer": return w.bottom() + w.outerRadius() * Math.sin(w.midAngle());
  6368. case "inner": return w.bottom() + w.innerRadius() * Math.sin(w.midAngle());
  6369. case "start": return w.bottom() + w.midRadius() * Math.sin(w.startAngle());
  6370. case "center": return w.bottom() + w.midRadius() * Math.sin(w.midAngle());
  6371. case "end": return w.bottom() + w.midRadius() * Math.sin(w.endAngle());
  6372. }
  6373. })
  6374. .textAlign(function() {
  6375. switch (this.name()) {
  6376. case "outer": return pv.Wedge.upright(w.midAngle()) ? "right" : "left";
  6377. case "inner": return pv.Wedge.upright(w.midAngle()) ? "left" : "right";
  6378. }
  6379. return "center";
  6380. })
  6381. .textBaseline(function() {
  6382. switch (this.name()) {
  6383. case "start": return pv.Wedge.upright(w.startAngle()) ? "top" : "bottom";
  6384. case "end": return pv.Wedge.upright(w.endAngle()) ? "bottom" : "top";
  6385. }
  6386. return "middle";
  6387. })
  6388. .textAngle(function() {
  6389. var a = 0;
  6390. switch (this.name()) {
  6391. case "center":
  6392. case "inner":
  6393. case "outer": a = w.midAngle(); break;
  6394. case "start": a = w.startAngle(); break;
  6395. case "end": a = w.endAngle(); break;
  6396. }
  6397. return pv.Wedge.upright(a) ? a : (a + Math.PI);
  6398. });
  6399. };
  6400. /**
  6401. * Returns true if the specified angle is considered "upright", as in, text
  6402. * rendered at that angle would appear upright. If the angle is not upright,
  6403. * text is rotated 180 degrees to be upright, and the text alignment properties
  6404. * are correspondingly changed.
  6405. *
  6406. * @param {number} angle an angle, in radius.
  6407. * @returns {boolean} true if the specified angle is upright.
  6408. */
  6409. pv.Wedge.upright = function(angle) {
  6410. angle = angle % (2 * Math.PI);
  6411. angle = (angle < 0) ? (2 * Math.PI + angle) : angle;
  6412. return (angle < Math.PI / 2) || (angle > 3 * Math.PI / 2);
  6413. };
  6414. /**
  6415. * @private Overrides the default behavior of {@link pv.Mark.buildImplied} such
  6416. * that the end angle is computed from the start angle and angle (angular span)
  6417. * if not specified.
  6418. *
  6419. * @param s a node in the scene graph; the instance of the wedge to build.
  6420. */
  6421. pv.Wedge.prototype.buildImplied = function(s) {
  6422. pv.Mark.prototype.buildImplied.call(this, s);
  6423. /*
  6424. * TODO If the angle or endAngle is updated by an event handler, the implied
  6425. * properties won't recompute correctly, so this will lead to potentially
  6426. * buggy redraw. How to re-evaluate implied properties on update?
  6427. */
  6428. if (s.endAngle == null) s.endAngle = s.startAngle + s.angle;
  6429. if (s.angle == null) s.angle = s.endAngle - s.startAngle;
  6430. };
  6431. /**
  6432. * @ignore
  6433. * @namespace
  6434. */
  6435. pv.Layout = {};
  6436. /**
  6437. * Returns a new grid layout.
  6438. *
  6439. * @class A grid layout with regularly-sized rows and columns. <img
  6440. * src="../grid.png" width="160" height="160" align="right"> The number of rows
  6441. * and columns are determined from the array, which should be in row-major
  6442. * order. For example, the 2&times;3 array:
  6443. *
  6444. * <pre>1 2 3
  6445. * 4 5 6</pre>
  6446. *
  6447. * should be represented as:
  6448. *
  6449. * <pre>[[1, 2, 3], [4, 5, 6]]</pre>
  6450. *
  6451. * If your data is in column-major order, you can use {@link pv.transpose} to
  6452. * transpose it.
  6453. *
  6454. * <p>This layout defines left, top, width, height and data properties. The data
  6455. * property will be the associated element in the array. For example, if the
  6456. * array is a two-dimensional array of values in the range [0,1], a simple
  6457. * heatmap can be generated as:
  6458. *
  6459. * <pre>.add(pv.Bar)
  6460. * .extend(pv.Layout.grid(array))
  6461. * .fillStyle(pv.ramp("white", "black"))</pre>
  6462. *
  6463. * By default, the grid fills the full width and height of the parent panel.
  6464. *
  6465. * @param {array[]} arrays an array of arrays.
  6466. * @returns {pv.Layout.grid} a grid layout.
  6467. */
  6468. pv.Layout.grid = function(arrays) {
  6469. var rows = arrays.length, cols = arrays[0].length;
  6470. /** @private */
  6471. function w() { return this.parent.width() / cols; }
  6472. /** @private */
  6473. function h() { return this.parent.height() / rows; }
  6474. /* A dummy mark, like an anchor, which the caller extends. */
  6475. return new pv.Mark()
  6476. .data(pv.blend(arrays))
  6477. .left(function() { return w.call(this) * (this.index % cols); })
  6478. .top(function() { return h.call(this) * Math.floor(this.index / cols); })
  6479. .width(w)
  6480. .height(h);
  6481. };
  6482. /**
  6483. * Returns a new stack layout.
  6484. *
  6485. * @class A layout for stacking marks vertically or horizontally, using the
  6486. * <i>cousin</i> instance. This layout is designed to be used for one of the
  6487. * four positional properties in the box model, and changes behavior depending
  6488. * on the property being evaluated:<ul>
  6489. *
  6490. * <li>bottom: cousin.bottom + cousin.height
  6491. * <li>top: cousin.top + cousin.height
  6492. * <li>left: cousin.left + cousin.width
  6493. * <li>right: cousin.right + cousin.width
  6494. *
  6495. * </ul>If no cousin instance is available (for example, for first instance),
  6496. * the specified offset is used. If no offset is specified, zero is used. For
  6497. * example,
  6498. *
  6499. * <pre>new pv.Panel()
  6500. * .width(150).height(150)
  6501. * .add(pv.Panel)
  6502. * .data([[1, 1.2, 1.7, 1.5, 1.7],
  6503. * [.5, 1, .8, 1.1, 1.3],
  6504. * [.2, .5, .8, .9, 1]])
  6505. * .add(pv.Area)
  6506. * .data(function(d) d)
  6507. * .bottom(pv.Layout.stack())
  6508. * .height(function(d) d * 40)
  6509. * .left(function() this.index * 35)
  6510. * .root.render();</pre>
  6511. *
  6512. * specifies a vertically-stacked area chart.
  6513. *
  6514. * @returns {pv.Layout.stack} a stack property function.
  6515. * @see pv.Mark#cousin
  6516. */
  6517. pv.Layout.stack = function() {
  6518. /** @private */
  6519. var offset = function() { return 0; };
  6520. /** @private */
  6521. function layout() {
  6522. /* Find the previous visible parent instance. */
  6523. var i = this.parent.index, p, c;
  6524. while ((i-- > 0) && !c) {
  6525. p = this.parent.scene[i];
  6526. if (p.visible) c = p.children[this.childIndex][this.index];
  6527. }
  6528. if (c) {
  6529. switch (property) {
  6530. case "bottom": return c.bottom + c.height;
  6531. case "top": return c.top + c.height;
  6532. case "left": return c.left + c.width;
  6533. case "right": return c.right + c.width;
  6534. }
  6535. }
  6536. return offset.apply(this, arguments);
  6537. }
  6538. /**
  6539. * Sets the offset for this stack layout. The offset can either be specified
  6540. * as a function or as a constant. If a function, the function is invoked in
  6541. * the same context as a normal property function: <tt>this</tt> refers to the
  6542. * mark, and the arguments are the full data stack. By default the offset is
  6543. * zero.
  6544. *
  6545. * @function
  6546. * @name pv.Layout.stack.prototype.offset
  6547. * @param {function} f offset function, or constant value.
  6548. * @returns {pv.Layout.stack} this.
  6549. */
  6550. layout.offset = function(f) {
  6551. offset = (f instanceof Function) ? f : function() { return f; };
  6552. return this;
  6553. };
  6554. return layout;
  6555. };
  6556. // TODO share code with Treemap
  6557. // TODO vertical / horizontal orientation?
  6558. /**
  6559. * Returns a new icicle tree layout.
  6560. *
  6561. * @class A tree layout in the form of an icicle. <img src="../icicle.png"
  6562. * width="160" height="160" align="right"> The first row corresponds to the root
  6563. * of the tree; subsequent rows correspond to each tier. Rows are subdivided
  6564. * into cells based on the size of nodes, per {@link #size}. Within a row, cells
  6565. * are sorted by size.
  6566. *
  6567. * <p>This tree layout is intended to be extended (see {@link pv.Mark#extend})
  6568. * by a {@link pv.Bar}. The data property returns an array of nodes for use by
  6569. * other property functions. The following node attributes are supported:
  6570. *
  6571. * <ul>
  6572. * <li><tt>left</tt> - the cell left position.
  6573. * <li><tt>top</tt> - the cell top position.
  6574. * <li><tt>width</tt> - the cell width.
  6575. * <li><tt>height</tt> - the cell height.
  6576. * <li><tt>depth</tt> - the node depth (tier; the root is 0).
  6577. * <li><tt>keys</tt> - an array of string keys for the node.
  6578. * <li><tt>size</tt> - the aggregate node size.
  6579. * <li><tt>children</tt> - child nodes, if any.
  6580. * <li><tt>data</tt> - the associated tree element, for leaf nodes.
  6581. * </ul>
  6582. *
  6583. * To produce a default icicle layout, say:
  6584. *
  6585. * <pre>.add(pv.Bar)
  6586. * .extend(pv.Layout.icicle(tree))</pre>
  6587. *
  6588. * To customize the tree to highlight leaf nodes bigger than 10,000 (1E4), you
  6589. * might say:
  6590. *
  6591. * <pre>.add(pv.Bar)
  6592. * .extend(pv.Layout.icicle(tree))
  6593. * .fillStyle(function(n) n.data > 1e4 ? "#ff0" : "#fff")</pre>
  6594. *
  6595. * The format of the <tt>tree</tt> argument is any hierarchical object whose
  6596. * leaf nodes are numbers corresponding to their size. For an example, and
  6597. * information on how to convert tabular data into such a tree, see
  6598. * {@link pv.Tree}. If the leaf nodes are not numbers, a {@link #size} function
  6599. * can be specified to override how the tree is interpreted. This size function
  6600. * can also be used to transform the data.
  6601. *
  6602. * <p>By default, the icicle fills the full width and height of the parent
  6603. * panel. An optional root key can be specified using {@link #root} for
  6604. * convenience.
  6605. *
  6606. * @param tree a tree (an object) who leaf attributes have sizes.
  6607. * @returns {pv.Layout.icicle} a tree layout.
  6608. */
  6609. pv.Layout.icicle = function(tree) {
  6610. var keys = [], sizeof = Number;
  6611. /** @private */
  6612. function accumulate(map) {
  6613. var node = {size: 0, children: [], keys: keys.slice()};
  6614. for (var key in map) {
  6615. var child = map[key], size = sizeof(child);
  6616. keys.push(key);
  6617. if (isNaN(size)) {
  6618. child = accumulate(child);
  6619. } else {
  6620. child = {size: size, data: child, keys: keys.slice()};
  6621. }
  6622. node.children.push(child);
  6623. node.size += child.size;
  6624. keys.pop();
  6625. }
  6626. node.children.sort(function(a, b) { return b.size - a.size; });
  6627. return node;
  6628. }
  6629. /** @private */
  6630. function scale(node, k) {
  6631. node.size *= k;
  6632. if (node.children) {
  6633. for (var i = 0; i < node.children.length; i++) {
  6634. scale(node.children[i], k);
  6635. }
  6636. }
  6637. }
  6638. /** @private */
  6639. function depth(node, i) {
  6640. i = i ? (i + 1) : 1;
  6641. return node.children
  6642. ? pv.max(node.children, function(n) { return depth(n, i); })
  6643. : i;
  6644. }
  6645. /** @private */
  6646. function layout(node) {
  6647. if (node.children) {
  6648. icify(node);
  6649. for (var i = 0; i < node.children.length; i++) {
  6650. layout(node.children[i]);
  6651. }
  6652. }
  6653. }
  6654. /** @private */
  6655. function icify(node) {
  6656. var left = node.left;
  6657. for (var i = 0; i < node.children.length; i++) {
  6658. var child = node.children[i], width = (child.size / node.size) * node.width;
  6659. child.left = left;
  6660. child.top = node.top + node.height;
  6661. child.width = width;
  6662. child.height = node.height;
  6663. child.depth = node.depth + 1;
  6664. left += width;
  6665. if (child.children) {
  6666. icify(child);
  6667. }
  6668. }
  6669. }
  6670. /** @private */
  6671. function flatten(node, array) {
  6672. if (node.children) {
  6673. for (var i = 0; i < node.children.length; i++) {
  6674. flatten(node.children[i], array);
  6675. }
  6676. }
  6677. array.push(node)
  6678. return array;
  6679. }
  6680. /** @private */
  6681. function data() {
  6682. var root = accumulate(tree);
  6683. root.top = 0;
  6684. root.left = 0;
  6685. root.width = this.parent.width();
  6686. root.height = this.parent.height() / depth(root);
  6687. root.depth = 0;
  6688. layout(root);
  6689. return flatten(root, []).reverse();
  6690. }
  6691. /* A dummy mark, like an anchor, which the caller extends. */
  6692. var mark = new pv.Mark()
  6693. .data(data)
  6694. .left(function(n) { return n.left; })
  6695. .top(function(n) { return n.top; })
  6696. .width(function(n) { return n.width; })
  6697. .height(function(n) { return n.height; });
  6698. /**
  6699. * Specifies the root key; optional. The root key is prepended to the
  6700. * <tt>keys</tt> attribute for all generated nodes. This method is provided
  6701. * for convenience and does not affect layout.
  6702. *
  6703. * @param {string} v the root key.
  6704. * @function
  6705. * @name pv.Layout.icicle.prototype.root
  6706. * @returns {pv.Layout.icicle} this.
  6707. */
  6708. mark.root = function(v) {
  6709. keys = [v];
  6710. return this;
  6711. };
  6712. /**
  6713. * Specifies the sizing function. By default, the sizing function is
  6714. * <tt>Number</tt>. The sizing function is invoked for each node in the tree
  6715. * (passed to the constructor): the sizing function must return
  6716. * <tt>undefined</tt> or <tt>NaN</tt> for internal nodes, and a number for
  6717. * leaf nodes. The aggregate sizes of internal nodes will be automatically
  6718. * computed by the layout.
  6719. *
  6720. * <p>For example, if the tree data structure represents a file system, with
  6721. * files as leaf nodes, and each file has a <tt>bytes</tt> attribute, you can
  6722. * specify a size function as:
  6723. *
  6724. * <pre>.size(function(d) d.bytes)</pre>
  6725. *
  6726. * This function will return <tt>undefined</tt> for internal nodes (since
  6727. * these do not have a <tt>bytes</tt> attribute), and a number for leaf nodes.
  6728. *
  6729. * <p>Note that the built-in <tt>Math.sqrt</tt> and <tt>Math.log</tt> methods
  6730. * can also be used as sizing functions. These function similarly to
  6731. * <tt>Number</tt>, except perform a root and log scale, respectively.
  6732. *
  6733. * @param {function} f the new sizing function.
  6734. * @function
  6735. * @name pv.Layout.icicle.prototype.size
  6736. * @returns {pv.Layout.icicle} this.
  6737. */
  6738. mark.size = function(f) {
  6739. sizeof = f;
  6740. return this;
  6741. };
  6742. return mark;
  6743. };
  6744. // TODO share code with Treemap
  6745. // TODO inspect parent panel dimensions to set inner and outer radii
  6746. /**
  6747. * Returns a new sunburst tree layout.
  6748. *
  6749. * @class A tree layout in the form of a sunburst. <img
  6750. * src="../sunburst.png" width="160" height="160" align="right"> The
  6751. * center circle corresponds to the root of the tree; subsequent rings
  6752. * correspond to each tier. Rings are subdivided into wedges based on the size
  6753. * of nodes, per {@link #size}. Within a ring, wedges are sorted by size.
  6754. *
  6755. * <p>The tree layout is intended to be extended (see {@link pv.Mark#extend} by
  6756. * a {@link pv.Wedge}. The data property returns an array of nodes for use by
  6757. * other property functions. The following node attributes are supported:
  6758. *
  6759. * <ul>
  6760. * <li><tt>left</tt> - the wedge left position.
  6761. * <li><tt>top</tt> - the wedge top position.
  6762. * <li><tt>innerRadius</tt> - the wedge inner radius.
  6763. * <li><tt>outerRadius</tt> - the wedge outer radius.
  6764. * <li><tt>startAngle</tt> - the wedge start angle.
  6765. * <li><tt>endAngle</tt> - the wedge end angle.
  6766. * <li><tt>angle</tt> - the wedge angle.
  6767. * <li><tt>depth</tt> - the node depth (tier; the root is 0).
  6768. * <li><tt>keys</tt> - an array of string keys for the node.
  6769. * <li><tt>size</tt> - the aggregate node size.
  6770. * <li><tt>children</tt> - child nodes, if any.
  6771. * <li><tt>data</tt> - the associated tree element, for leaf nodes.
  6772. * </ul>
  6773. *
  6774. * <p>To produce a default sunburst layout, say:
  6775. *
  6776. * <pre>.add(pv.Wedge)
  6777. * .extend(pv.Layout.sunburst(tree))</pre>
  6778. *
  6779. * To only show nodes at a depth of two or greater, you might say:
  6780. *
  6781. * <pre>.add(pv.Wedge)
  6782. * .extend(pv.Layout.sunburst(tree))
  6783. * .visible(function(n) n.depth > 1)</pre>
  6784. *
  6785. * The format of the <tt>tree</tt> argument is a hierarchical object whose leaf
  6786. * nodes are numbers corresponding to their size. For an example, and
  6787. * information on how to convert tabular data into such a tree, see
  6788. * {@link pv.Tree}. If the leaf nodes are not numbers, a {@link #size} function
  6789. * can be specified to override how the tree is interpreted. This size function
  6790. * can also be used to transform the data.
  6791. *
  6792. * <p>By default, the sunburst fills the full width and height of the parent
  6793. * panel. An optional root key can be specified using {@link #root} for
  6794. * convenience.
  6795. *
  6796. * @param tree a tree (an object) who leaf attributes have sizes.
  6797. * @returns {pv.Layout.sunburst} a tree layout.
  6798. */
  6799. pv.Layout.sunburst = function(tree) {
  6800. var keys = [], sizeof = Number, w, h, r;
  6801. /** @private */
  6802. function accumulate(map) {
  6803. var node = {size: 0, children: [], keys: keys.slice()};
  6804. for (var key in map) {
  6805. var child = map[key], size = sizeof(child);
  6806. keys.push(key);
  6807. if (isNaN(size)) {
  6808. child = accumulate(child);
  6809. } else {
  6810. child = {size: size, data: child, keys: keys.slice()};
  6811. }
  6812. node.children.push(child);
  6813. node.size += child.size;
  6814. keys.pop();
  6815. }
  6816. node.children.sort(function(a, b) { return b.size - a.size; });
  6817. return node;
  6818. }
  6819. /** @private */
  6820. function scale(node, k) {
  6821. node.size *= k;
  6822. if (node.children) {
  6823. for (var i = 0; i < node.children.length; i++) {
  6824. scale(node.children[i], k);
  6825. }
  6826. }
  6827. }
  6828. /** @private */
  6829. function depth(node, i) {
  6830. i = i ? (i + 1) : 1;
  6831. return node.children
  6832. ? pv.max(node.children, function(n) { return depth(n, i); })
  6833. : i;
  6834. }
  6835. /** @private */
  6836. function layout(node) {
  6837. if (node.children) {
  6838. wedgify(node);
  6839. for (var i = 0; i < node.children.length; i++) {
  6840. layout(node.children[i]);
  6841. }
  6842. }
  6843. }
  6844. /** @private */
  6845. function wedgify(node) {
  6846. var startAngle = node.startAngle;
  6847. for (var i = 0; i < node.children.length; i++) {
  6848. var child = node.children[i], angle = (child.size / node.size) * node.angle;
  6849. child.startAngle = startAngle;
  6850. child.angle = angle;
  6851. child.endAngle = startAngle + angle;
  6852. child.depth = node.depth + 1;
  6853. child.left = w / 2;
  6854. child.top = h / 2;
  6855. child.innerRadius = Math.max(0, child.depth - .5) * r;
  6856. child.outerRadius = (child.depth + .5) * r;
  6857. startAngle += angle;
  6858. if (child.children) {
  6859. wedgify(child);
  6860. }
  6861. }
  6862. }
  6863. /** @private */
  6864. function flatten(node, array) {
  6865. if (node.children) {
  6866. for (var i = 0; i < node.children.length; i++) {
  6867. flatten(node.children[i], array);
  6868. }
  6869. }
  6870. array.push(node)
  6871. return array;
  6872. }
  6873. /** @private */
  6874. function data() {
  6875. var root = accumulate(tree);
  6876. w = this.parent.width();
  6877. h = this.parent.height();
  6878. r = Math.min(w, h) / 2 / (depth(root) - .5);
  6879. root.left = w / 2;
  6880. root.top = h / 2;
  6881. root.startAngle = 0;
  6882. root.angle = 2 * Math.PI;
  6883. root.endAngle = 2 * Math.PI;
  6884. root.innerRadius = 0;
  6885. root.outerRadius = r;
  6886. root.depth = 0;
  6887. layout(root);
  6888. return flatten(root, []).reverse();
  6889. }
  6890. /* A dummy mark, like an anchor, which the caller extends. */
  6891. var mark = new pv.Mark()
  6892. .data(data)
  6893. .left(function(n) { return n.left; })
  6894. .top(function(n) { return n.top; })
  6895. .startAngle(function(n) { return n.startAngle; })
  6896. .angle(function(n) { return n.angle; })
  6897. .innerRadius(function(n) { return n.innerRadius; })
  6898. .outerRadius(function(n) { return n.outerRadius; });
  6899. /**
  6900. * Specifies the root key; optional. The root key is prepended to the
  6901. * <tt>keys</tt> attribute for all generated nodes. This method is provided
  6902. * for convenience and does not affect layout.
  6903. *
  6904. * @param {string} v the root key.
  6905. * @function
  6906. * @name pv.Layout.sunburst.prototype.root
  6907. * @returns {pv.Layout.sunburst} this.
  6908. */
  6909. mark.root = function(v) {
  6910. keys = [v];
  6911. return this;
  6912. };
  6913. /**
  6914. * Specifies the sizing function. By default, the sizing function is
  6915. * <tt>Number</tt>. The sizing function is invoked for each node in the tree
  6916. * (passed to the constructor): the sizing function must return
  6917. * <tt>undefined</tt> or <tt>NaN</tt> for internal nodes, and a number for
  6918. * leaf nodes. The aggregate sizes of internal nodes will be automatically
  6919. * computed by the layout.
  6920. *
  6921. * <p>For example, if the tree data structure represents a file system, with
  6922. * files as leaf nodes, and each file has a <tt>bytes</tt> attribute, you can
  6923. * specify a size function as:
  6924. *
  6925. * <pre>.size(function(d) d.bytes)</pre>
  6926. *
  6927. * This function will return <tt>undefined</tt> for internal nodes (since
  6928. * these do not have a <tt>bytes</tt> attribute), and a number for leaf nodes.
  6929. *
  6930. * <p>Note that the built-in <tt>Math.sqrt</tt> and <tt>Math.log</tt> methods
  6931. * can be used as sizing functions. These function similarly to
  6932. * <tt>Number</tt>, except perform a root and log scale, respectively.
  6933. *
  6934. * @param {function} f the new sizing function.
  6935. * @function
  6936. * @name pv.Layout.sunburst.prototype.size
  6937. * @returns {pv.Layout.sunburst} this.
  6938. */
  6939. mark.size = function(f) {
  6940. sizeof = f;
  6941. return this;
  6942. };
  6943. return mark;
  6944. };
  6945. // TODO add `by` function for determining size (and children?)
  6946. /**
  6947. * Returns a new treemap tree layout.
  6948. *
  6949. * @class A tree layout in the form of an treemap. <img
  6950. * src="../treemap.png" width="160" height="160" align="right"> Treemaps
  6951. * are a form of space-filling layout that represents nodes as boxes, with child
  6952. * nodes placed within parent boxes. The size of each box is proportional to the
  6953. * size of the node in the tree.
  6954. *
  6955. * <p>This particular algorithm is taken from Bruls, D.M., C. Huizing, and
  6956. * J.J. van Wijk, <a href="http://www.win.tue.nl/~vanwijk/stm.pdf">"Squarified
  6957. * Treemaps"</a> in <i>Data Visualization 2000, Proceedings of the Joint
  6958. * Eurographics and IEEE TCVG Sumposium on Visualization</i>, 2000,
  6959. * pp. 33-42.
  6960. *
  6961. * <p>This tree layout is intended to be extended (see {@link pv.Mark#extend})
  6962. * by a {@link pv.Bar}. The data property returns an array of nodes for use by
  6963. * other property functions. The following node attributes are supported:
  6964. *
  6965. * <ul>
  6966. * <li><tt>left</tt> - the cell left position.
  6967. * <li><tt>top</tt> - the cell top position.
  6968. * <li><tt>width</tt> - the cell width.
  6969. * <li><tt>height</tt> - the cell height.
  6970. * <li><tt>depth</tt> - the node depth (tier; the root is 0).
  6971. * <li><tt>keys</tt> - an array of string keys for the node.
  6972. * <li><tt>size</tt> - the aggregate node size.
  6973. * <li><tt>children</tt> - child nodes, if any.
  6974. * <li><tt>data</tt> - the associated tree element, for leaf nodes.
  6975. * </ul>
  6976. *
  6977. * To produce a default treemap layout, say:
  6978. *
  6979. * <pre>.add(pv.Bar)
  6980. * .extend(pv.Layout.treemap(tree))</pre>
  6981. *
  6982. * To display internal nodes, and color by depth, say:
  6983. *
  6984. * <pre>.add(pv.Bar)
  6985. * .extend(pv.Layout.treemap(tree).inset(10))
  6986. * .fillStyle(pv.Colors.category19().by(function(n) n.depth))</pre>
  6987. *
  6988. * The format of the <tt>tree</tt> argument is a hierarchical object whose leaf
  6989. * nodes are numbers corresponding to their size. For an example, and
  6990. * information on how to convert tabular data into such a tree, see
  6991. * {@link pv.Tree}. If the leaf nodes are not numbers, a {@link #size} function
  6992. * can be specified to override how the tree is interpreted. This size function
  6993. * can also be used to transform the data.
  6994. *
  6995. * <p>By default, the treemap fills the full width and height of the parent
  6996. * panel, and only leaf nodes are rendered. If an {@link #inset} is specified,
  6997. * internal nodes will be rendered, each inset from their parent by the
  6998. * specified margins. Rounding can be enabled using {@link #round}. Finally, an
  6999. * optional root key can be specified using {@link #root} for convenience.
  7000. *
  7001. * @param tree a tree (an object) who leaf attributes have sizes.
  7002. * @returns {pv.Layout.treemap} a tree layout.
  7003. */
  7004. pv.Layout.treemap = function(tree) {
  7005. var keys = [], round, inset, sizeof = Number;
  7006. /** @private */
  7007. function rnd(i) {
  7008. return round ? Math.round(i) : i;
  7009. }
  7010. /** @private */
  7011. function accumulate(map) {
  7012. var node = {size: 0, children: [], keys: keys.slice()};
  7013. for (var key in map) {
  7014. var child = map[key], size = sizeof(child);
  7015. keys.push(key);
  7016. if (isNaN(size)) {
  7017. child = accumulate(child);
  7018. } else {
  7019. child = {size: size, data: child, keys: keys.slice()};
  7020. }
  7021. node.children.push(child);
  7022. node.size += child.size;
  7023. keys.pop();
  7024. }
  7025. node.children.sort(function(a, b) { return a.size - b.size; });
  7026. return node;
  7027. }
  7028. /** @private */
  7029. function scale(node, k) {
  7030. node.size *= k;
  7031. if (node.children) {
  7032. for (var i = 0; i < node.children.length; i++) {
  7033. scale(node.children[i], k);
  7034. }
  7035. }
  7036. }
  7037. /** @private */
  7038. function ratio(row, l) {
  7039. var rmax = -Infinity, rmin = Infinity, s = 0;
  7040. for (var i = 0; i < row.length; i++) {
  7041. var r = row[i].size;
  7042. if (r < rmin) rmin = r;
  7043. if (r > rmax) rmax = r;
  7044. s += r;
  7045. }
  7046. s = s * s;
  7047. l = l * l;
  7048. return Math.max(l * rmax / s, s / (l * rmin));
  7049. }
  7050. /** @private */
  7051. function squarify(node) {
  7052. var row = [], mink = Infinity;
  7053. var x = node.left + (inset ? inset.left : 0),
  7054. y = node.top + (inset ? inset.top : 0),
  7055. w = node.width - (inset ? inset.left + inset.right : 0),
  7056. h = node.height - (inset ? inset.top + inset.bottom : 0),
  7057. l = Math.min(w, h);
  7058. scale(node, w * h / node.size);
  7059. function position(row) {
  7060. var s = pv.sum(row, function(node) { return node.size; }),
  7061. hh = (l == 0) ? 0 : rnd(s / l);
  7062. for (var i = 0, d = 0; i < row.length; i++) {
  7063. var n = row[i], nw = rnd(n.size / hh);
  7064. if (w == l) {
  7065. n.left = x + d;
  7066. n.top = y;
  7067. n.width = nw;
  7068. n.height = hh;
  7069. } else {
  7070. n.left = x;
  7071. n.top = y + d;
  7072. n.width = hh;
  7073. n.height = nw;
  7074. }
  7075. d += nw;
  7076. }
  7077. if (w == l) {
  7078. if (n) n.width += w - d; // correct rounding error
  7079. y += hh;
  7080. h -= hh;
  7081. } else {
  7082. if (n) n.height += h - d; // correct rounding error
  7083. x += hh;
  7084. w -= hh;
  7085. }
  7086. l = Math.min(w, h);
  7087. }
  7088. var children = node.children.slice(); // copy
  7089. while (children.length > 0) {
  7090. var child = children[children.length - 1];
  7091. if (child.size <= 0) {
  7092. children.pop();
  7093. continue;
  7094. }
  7095. row.push(child);
  7096. var k = ratio(row, l);
  7097. if (k <= mink) {
  7098. children.pop();
  7099. mink = k;
  7100. } else {
  7101. row.pop();
  7102. position(row);
  7103. row.length = 0;
  7104. mink = Infinity;
  7105. }
  7106. }
  7107. if (row.length > 0) {
  7108. position(row);
  7109. }
  7110. /* correct rounding error */
  7111. if (w == l) {
  7112. for (var i = 0; i < row.length; i++) {
  7113. row[i].width += w;
  7114. }
  7115. } else {
  7116. for (var i = 0; i < row.length; i++) {
  7117. row[i].height += h;
  7118. }
  7119. }
  7120. }
  7121. /** @private */
  7122. function layout(node) {
  7123. if (node.children) {
  7124. squarify(node);
  7125. for (var i = 0; i < node.children.length; i++) {
  7126. var child = node.children[i];
  7127. child.depth = node.depth + 1;
  7128. layout(child);
  7129. }
  7130. }
  7131. }
  7132. /** @private */
  7133. function flatten(node, array) {
  7134. if (node.children) {
  7135. for (var i = 0; i < node.children.length; i++) {
  7136. flatten(node.children[i], array);
  7137. }
  7138. }
  7139. if (inset || !node.children) {
  7140. array.push(node)
  7141. }
  7142. return array;
  7143. }
  7144. /** @private */
  7145. function data() {
  7146. var root = accumulate(tree);
  7147. root.left = 0;
  7148. root.top = 0;
  7149. root.width = this.parent.width();
  7150. root.height = this.parent.height();
  7151. root.depth = 0;
  7152. layout(root);
  7153. return flatten(root, []).reverse();
  7154. }
  7155. /* A dummy mark, like an anchor, which the caller extends. */
  7156. var mark = new pv.Mark()
  7157. .data(data)
  7158. .left(function(n) { return n.left; })
  7159. .top(function(n) { return n.top; })
  7160. .width(function(n) { return n.width; })
  7161. .height(function(n) { return n.height; });
  7162. /**
  7163. * Enables or disables rounding. When rounding is enabled, the left, top,
  7164. * width and height properties will be rounded to integer pixel values. The
  7165. * rounding algorithm uses error accumulation to ensure an exact fit.
  7166. *
  7167. * @param {boolean} v whether rounding should be enabled.
  7168. * @function
  7169. * @name pv.Layout.treemap.prototype.round
  7170. * @returns {pv.Layout.treemap} this.
  7171. */
  7172. mark.round = function(v) {
  7173. round = v;
  7174. return this;
  7175. };
  7176. /**
  7177. * Specifies the margins to inset child nodes from their parents; as a side
  7178. * effect, this also enables the display of internal nodes, which are hidden
  7179. * by default. If only a single argument is specified, this value is used to
  7180. * inset all four sides.
  7181. *
  7182. * @param {number} top the top margin.
  7183. * @param {number} [right] the right margin.
  7184. * @param {number} [bottom] the bottom margin.
  7185. * @param {number} [left] the left margin.
  7186. * @function
  7187. * @name pv.Layout.treemap.prototype.inset
  7188. * @returns {pv.Layout.treemap} this.
  7189. */
  7190. mark.inset = function(top, right, bottom, left) {
  7191. if (arguments.length == 1) right = bottom = left = top;
  7192. inset = {top:top, right:right, bottom:bottom, left:left};
  7193. return this;
  7194. };
  7195. /**
  7196. * Specifies the root key; optional. The root key is prepended to the
  7197. * <tt>keys</tt> attribute for all generated nodes. This method is provided
  7198. * for convenience and does not affect layout.
  7199. *
  7200. * @param {string} v the root key.
  7201. * @function
  7202. * @name pv.Layout.treemap.prototype.root
  7203. * @returns {pv.Layout.treemap} this.
  7204. */
  7205. mark.root = function(v) {
  7206. keys = [v];
  7207. return this;
  7208. };
  7209. /**
  7210. * Specifies the sizing function. By default, the sizing function is
  7211. * <tt>Number</tt>. The sizing function is invoked for each node in the tree
  7212. * (passed to the constructor): the sizing function must return
  7213. * <tt>undefined</tt> or <tt>NaN</tt> for internal nodes, and a number for
  7214. * leaf nodes. The aggregate sizes of internal nodes will be automatically
  7215. * computed by the layout.
  7216. *
  7217. * <p>For example, if the tree data structure represents a file system, with
  7218. * files as leaf nodes, and each file has a <tt>bytes</tt> attribute, you can
  7219. * specify a size function as:
  7220. *
  7221. * <pre>.size(function(d) d.bytes)</pre>
  7222. *
  7223. * This function will return <tt>undefined</tt> for internal nodes (since
  7224. * these do not have a <tt>bytes</tt> attribute), and a number for leaf nodes.
  7225. *
  7226. * <p>Note that the built-in <tt>Math.sqrt</tt> and <tt>Math.log</tt> methods
  7227. * can be used as sizing functions. These function similarly to
  7228. * <tt>Number</tt>, except perform a root and log scale, respectively.
  7229. *
  7230. * @param {function} f the new sizing function.
  7231. * @function
  7232. * @name pv.Layout.treemap.prototype.size
  7233. * @returns {pv.Layout.treemap} this.
  7234. */
  7235. mark.size = function(f) {
  7236. sizeof = f;
  7237. return this;
  7238. };
  7239. return mark;
  7240. };
  7241. return pv;}();