jquery.jqplot.js 459 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124912591269127912891299130913191329133913491359136913791389139914091419142914391449145914691479148914991509151915291539154915591569157915891599160916191629163916491659166916791689169917091719172917391749175917691779178917991809181918291839184918591869187918891899190919191929193919491959196919791989199920092019202920392049205920692079208920992109211921292139214921592169217921892199220922192229223922492259226922792289229923092319232923392349235923692379238923992409241924292439244924592469247924892499250925192529253925492559256925792589259926092619262926392649265926692679268926992709271927292739274927592769277927892799280928192829283928492859286928792889289929092919292929392949295929692979298929993009301930293039304930593069307930893099310931193129313931493159316931793189319932093219322932393249325932693279328932993309331933293339334933593369337933893399340934193429343934493459346934793489349935093519352935393549355935693579358935993609361936293639364936593669367936893699370937193729373937493759376937793789379938093819382938393849385938693879388938993909391939293939394939593969397939893999400940194029403940494059406940794089409941094119412941394149415941694179418941994209421942294239424942594269427942894299430943194329433943494359436943794389439944094419442944394449445944694479448944994509451945294539454945594569457945894599460946194629463946494659466946794689469947094719472947394749475947694779478947994809481948294839484948594869487948894899490949194929493949494959496949794989499950095019502950395049505950695079508950995109511951295139514951595169517951895199520952195229523952495259526952795289529953095319532953395349535953695379538953995409541954295439544954595469547954895499550955195529553955495559556955795589559956095619562956395649565956695679568956995709571957295739574957595769577957895799580958195829583958495859586958795889589959095919592959395949595959695979598959996009601960296039604960596069607960896099610961196129613961496159616961796189619962096219622962396249625962696279628962996309631963296339634963596369637963896399640964196429643964496459646964796489649965096519652965396549655965696579658965996609661966296639664966596669667966896699670967196729673967496759676967796789679968096819682968396849685968696879688968996909691969296939694969596969697969896999700970197029703970497059706970797089709971097119712971397149715971697179718971997209721972297239724972597269727972897299730973197329733973497359736973797389739974097419742974397449745974697479748974997509751975297539754975597569757975897599760976197629763976497659766976797689769977097719772977397749775977697779778977997809781978297839784978597869787978897899790979197929793979497959796979797989799980098019802980398049805980698079808980998109811981298139814981598169817981898199820982198229823982498259826982798289829983098319832983398349835983698379838983998409841984298439844984598469847984898499850985198529853985498559856985798589859986098619862986398649865986698679868986998709871987298739874987598769877987898799880988198829883988498859886988798889889989098919892989398949895989698979898989999009901990299039904990599069907990899099910991199129913991499159916991799189919992099219922992399249925992699279928992999309931993299339934993599369937993899399940994199429943994499459946994799489949995099519952995399549955995699579958995999609961996299639964996599669967996899699970997199729973997499759976997799789979998099819982998399849985998699879988998999909991999299939994999599969997999899991000010001100021000310004100051000610007100081000910010100111001210013100141001510016100171001810019100201002110022100231002410025100261002710028100291003010031100321003310034100351003610037100381003910040100411004210043100441004510046100471004810049100501005110052100531005410055100561005710058100591006010061100621006310064100651006610067100681006910070100711007210073100741007510076100771007810079100801008110082100831008410085100861008710088100891009010091100921009310094100951009610097100981009910100101011010210103101041010510106101071010810109101101011110112101131011410115101161011710118101191012010121101221012310124101251012610127101281012910130101311013210133101341013510136101371013810139101401014110142101431014410145101461014710148101491015010151101521015310154101551015610157101581015910160101611016210163101641016510166101671016810169101701017110172101731017410175101761017710178101791018010181101821018310184101851018610187101881018910190101911019210193101941019510196101971019810199102001020110202102031020410205102061020710208102091021010211102121021310214102151021610217102181021910220102211022210223102241022510226102271022810229102301023110232102331023410235102361023710238102391024010241102421024310244102451024610247102481024910250102511025210253102541025510256102571025810259102601026110262102631026410265102661026710268102691027010271102721027310274102751027610277102781027910280102811028210283102841028510286102871028810289102901029110292102931029410295102961029710298102991030010301103021030310304103051030610307103081030910310103111031210313103141031510316103171031810319103201032110322103231032410325103261032710328103291033010331103321033310334103351033610337103381033910340103411034210343103441034510346103471034810349103501035110352103531035410355103561035710358103591036010361103621036310364103651036610367103681036910370103711037210373103741037510376103771037810379103801038110382103831038410385103861038710388103891039010391103921039310394103951039610397103981039910400104011040210403104041040510406104071040810409104101041110412104131041410415104161041710418104191042010421104221042310424104251042610427104281042910430104311043210433104341043510436104371043810439104401044110442104431044410445104461044710448104491045010451104521045310454104551045610457104581045910460104611046210463104641046510466104671046810469104701047110472104731047410475104761047710478104791048010481104821048310484104851048610487104881048910490104911049210493104941049510496104971049810499105001050110502105031050410505105061050710508105091051010511105121051310514105151051610517105181051910520105211052210523105241052510526105271052810529105301053110532105331053410535105361053710538105391054010541105421054310544105451054610547105481054910550105511055210553105541055510556105571055810559105601056110562105631056410565105661056710568105691057010571105721057310574105751057610577105781057910580105811058210583105841058510586105871058810589105901059110592105931059410595105961059710598105991060010601106021060310604106051060610607106081060910610106111061210613106141061510616106171061810619106201062110622106231062410625106261062710628106291063010631106321063310634106351063610637106381063910640106411064210643106441064510646106471064810649106501065110652106531065410655106561065710658106591066010661106621066310664106651066610667106681066910670106711067210673106741067510676106771067810679106801068110682106831068410685106861068710688106891069010691106921069310694106951069610697106981069910700107011070210703107041070510706107071070810709107101071110712107131071410715107161071710718107191072010721107221072310724107251072610727107281072910730107311073210733107341073510736107371073810739107401074110742107431074410745107461074710748107491075010751107521075310754107551075610757107581075910760107611076210763107641076510766107671076810769107701077110772107731077410775107761077710778107791078010781107821078310784107851078610787107881078910790107911079210793107941079510796107971079810799108001080110802108031080410805108061080710808108091081010811108121081310814108151081610817108181081910820108211082210823108241082510826108271082810829108301083110832108331083410835108361083710838108391084010841108421084310844108451084610847108481084910850108511085210853108541085510856108571085810859108601086110862108631086410865108661086710868108691087010871108721087310874108751087610877108781087910880108811088210883108841088510886108871088810889108901089110892108931089410895108961089710898108991090010901109021090310904109051090610907109081090910910109111091210913109141091510916109171091810919109201092110922109231092410925109261092710928109291093010931109321093310934109351093610937109381093910940109411094210943109441094510946109471094810949109501095110952109531095410955109561095710958109591096010961109621096310964109651096610967109681096910970109711097210973109741097510976109771097810979109801098110982109831098410985109861098710988109891099010991109921099310994109951099610997109981099911000110011100211003110041100511006110071100811009110101101111012110131101411015110161101711018110191102011021110221102311024110251102611027110281102911030110311103211033110341103511036110371103811039110401104111042110431104411045110461104711048110491105011051110521105311054110551105611057110581105911060110611106211063110641106511066110671106811069110701107111072110731107411075110761107711078110791108011081110821108311084110851108611087110881108911090110911109211093110941109511096110971109811099111001110111102111031110411105111061110711108111091111011111111121111311114111151111611117111181111911120111211112211123111241112511126111271112811129111301113111132111331113411135111361113711138111391114011141111421114311144111451114611147111481114911150111511115211153111541115511156111571115811159111601116111162111631116411165111661116711168111691117011171111721117311174111751117611177111781117911180111811118211183111841118511186111871118811189111901119111192111931119411195111961119711198111991120011201112021120311204112051120611207112081120911210112111121211213112141121511216112171121811219112201122111222112231122411225112261122711228112291123011231112321123311234112351123611237112381123911240112411124211243112441124511246112471124811249112501125111252112531125411255112561125711258112591126011261112621126311264112651126611267112681126911270112711127211273112741127511276112771127811279112801128111282112831128411285112861128711288112891129011291112921129311294112951129611297112981129911300113011130211303113041130511306113071130811309113101131111312113131131411315113161131711318113191132011321113221132311324113251132611327113281132911330113311133211333113341133511336113371133811339113401134111342113431134411345113461134711348113491135011351113521135311354113551135611357113581135911360113611136211363113641136511366113671136811369113701137111372113731137411375113761137711378113791138011381113821138311384113851138611387113881138911390113911139211393113941139511396113971139811399114001140111402114031140411405114061140711408114091141011411114121141311414114151141611417114181141911420114211142211423114241142511426114271142811429114301143111432114331143411435114361143711438114391144011441114421144311444114451144611447114481144911450114511145211453114541145511456114571145811459114601146111462114631146411465114661146711468114691147011471114721147311474114751147611477
  1. /**
  2. * Title: jqPlot Charts
  3. *
  4. * Pure JavaScript plotting plugin for jQuery.
  5. *
  6. * About: Version
  7. *
  8. * version: 1.0.9
  9. * revision: d96a669
  10. *
  11. * About: Copyright & License
  12. *
  13. * Copyright (c) 2009-2016 Chris Leonello
  14. * jqPlot is currently available for use in all personal or commercial projects
  15. * under both the MIT and GPL version 2.0 licenses. This means that you can
  16. * choose the license that best suits your project and use it accordingly.
  17. *
  18. * See <GPL Version 2> and <MIT License> contained within this distribution for further information.
  19. *
  20. * The author would appreciate an email letting him know of any substantial
  21. * use of jqPlot. You can reach the author at: chris at jqplot dot com
  22. * or see http://www.jqplot.com/info.php. This is, of course, not required.
  23. *
  24. * If you are feeling kind and generous, consider supporting the project by
  25. * making a donation at: http://www.jqplot.com/donate.php.
  26. *
  27. * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
  28. *
  29. * version 2007.04.27
  30. * author Ash Searle
  31. * http://hexmen.com/blog/2007/03/printf-sprintf/
  32. * http://hexmen.com/js/sprintf.js
  33. * The author (Ash Searle) has placed this code in the public domain:
  34. * "This code is unrestricted: you are free to use it however you like."
  35. *
  36. *
  37. * About: Introduction
  38. *
  39. * jqPlot requires jQuery (1.4+ required for certain features). jQuery 1.4.2 is included in the distribution.
  40. * To use jqPlot include jQuery, the jqPlot jQuery plugin, the jqPlot css file and optionally
  41. * the excanvas script for IE support in your web page:
  42. *
  43. * > <!--[if lt IE 9]><script language="javascript" type="text/javascript" src="excanvas.js"></script><![endif]-->
  44. * > <script language="javascript" type="text/javascript" src="jquery-1.4.4.min.js"></script>
  45. * > <script language="javascript" type="text/javascript" src="jquery.jqplot.min.js"></script>
  46. * > <link rel="stylesheet" type="text/css" href="jquery.jqplot.css" />
  47. *
  48. * jqPlot can be customized by overriding the defaults of any of the objects which make
  49. * up the plot. The general usage of jqplot is:
  50. *
  51. * > chart = $.jqplot('targetElemId', [dataArray,...], {optionsObject});
  52. *
  53. * The options available to jqplot are detailed in <jqPlot Options> in the jqPlotOptions.txt file.
  54. *
  55. * An actual call to $.jqplot() may look like the
  56. * examples below:
  57. *
  58. * > chart = $.jqplot('chartdiv', [[[1, 2],[3,5.12],[5,13.1],[7,33.6],[9,85.9],[11,219.9]]]);
  59. *
  60. * or
  61. *
  62. * > dataArray = [34,12,43,55,77];
  63. * > chart = $.jqplot('targetElemId', [dataArray, ...], {title:'My Plot', axes:{yaxis:{min:20, max:100}}});
  64. *
  65. * For more inforrmation, see <jqPlot Usage>.
  66. *
  67. * About: Usage
  68. *
  69. * See <jqPlot Usage>
  70. *
  71. * About: Available Options
  72. *
  73. * See <jqPlot Options> for a list of options available thorugh the options object (not complete yet!)
  74. *
  75. * About: Options Usage
  76. *
  77. * See <Options Tutorial>
  78. *
  79. * About: Changes
  80. *
  81. * See <Change Log>
  82. *
  83. */
  84. (function($) {
  85. // make sure undefined is undefined
  86. var undefined;
  87. $.fn.emptyForce = function() {
  88. for ( var i = 0, elem; (elem = $(this)[i]) != null; i++ ) {
  89. // Remove element nodes and prevent memory leaks
  90. if ( elem.nodeType === 1 ) {
  91. $.cleanData( elem.getElementsByTagName("*") );
  92. }
  93. // Remove any remaining nodes
  94. if ($.jqplot.use_excanvas) {
  95. elem.outerHTML = "";
  96. }
  97. else {
  98. while ( elem.firstChild ) {
  99. elem.removeChild( elem.firstChild );
  100. }
  101. }
  102. elem = null;
  103. }
  104. return $(this);
  105. };
  106. $.fn.removeChildForce = function(parent) {
  107. while ( parent.firstChild ) {
  108. this.removeChildForce( parent.firstChild );
  109. parent.removeChild( parent.firstChild );
  110. }
  111. };
  112. $.fn.jqplot = function() {
  113. var datas = [];
  114. var options = [];
  115. // see how many data arrays we have
  116. for (var i=0, l=arguments.length; i<l; i++) {
  117. if ($.isArray(arguments[i])) {
  118. datas.push(arguments[i]);
  119. }
  120. else if ($.isPlainObject(arguments[i])) {
  121. options.push(arguments[i]);
  122. }
  123. }
  124. return this.each(function(index) {
  125. var tid,
  126. plot,
  127. $this = $(this),
  128. dl = datas.length,
  129. ol = options.length,
  130. data,
  131. opts;
  132. if (index < dl) {
  133. data = datas[index];
  134. }
  135. else {
  136. data = dl ? datas[dl-1] : null;
  137. }
  138. if (index < ol) {
  139. opts = options[index];
  140. }
  141. else {
  142. opts = ol ? options[ol-1] : null;
  143. }
  144. // does el have an id?
  145. // if not assign it one.
  146. tid = $this.attr('id');
  147. if (tid === undefined) {
  148. tid = 'jqplot_target_' + $.jqplot.targetCounter++;
  149. $this.attr('id', tid);
  150. }
  151. plot = $.jqplot(tid, data, opts);
  152. $this.data('jqplot', plot);
  153. });
  154. };
  155. /**
  156. * Namespace: $.jqplot
  157. * jQuery function called by the user to create a plot.
  158. *
  159. * Parameters:
  160. * target - ID of target element to render the plot into.
  161. * data - an array of data series.
  162. * options - user defined options object. See the individual classes for available options.
  163. *
  164. * Properties:
  165. * config - object to hold configuration information for jqPlot plot object.
  166. *
  167. * attributes:
  168. * enablePlugins - False to disable plugins by default. Plugins must then be explicitly
  169. * enabled in the individual plot options. Default: false.
  170. * This property sets the "show" property of certain plugins to true or false.
  171. * Only plugins that can be immediately active upon loading are affected. This includes
  172. * non-renderer plugins like cursor, dragable, highlighter, and trendline.
  173. * defaultHeight - Default height for plots where no css height specification exists. This
  174. * is a jqplot wide default.
  175. * defaultWidth - Default height for plots where no css height specification exists. This
  176. * is a jqplot wide default.
  177. */
  178. $.jqplot = function(target, data, options) {
  179. var _data = null, _options = null;
  180. if (arguments.length === 3) {
  181. _data = data;
  182. _options = options;
  183. }
  184. else if (arguments.length === 2) {
  185. if ($.isArray(data)) {
  186. _data = data;
  187. }
  188. else if ($.isPlainObject(data)) {
  189. _options = data;
  190. }
  191. }
  192. if (_data === null && _options !== null && _options.data) {
  193. _data = _options.data;
  194. }
  195. var plot = new jqPlot();
  196. // remove any error class that may be stuck on target.
  197. $('#'+target).removeClass('jqplot-error');
  198. if ($.jqplot.config.catchErrors) {
  199. try {
  200. plot.init(target, _data, _options);
  201. plot.draw();
  202. plot.themeEngine.init.call(plot);
  203. return plot;
  204. }
  205. catch(e) {
  206. var msg = $.jqplot.config.errorMessage || e.message;
  207. $('#'+target).append('<div class="jqplot-error-message">'+msg+'</div>');
  208. $('#'+target).addClass('jqplot-error');
  209. document.getElementById(target).style.background = $.jqplot.config.errorBackground;
  210. document.getElementById(target).style.border = $.jqplot.config.errorBorder;
  211. document.getElementById(target).style.fontFamily = $.jqplot.config.errorFontFamily;
  212. document.getElementById(target).style.fontSize = $.jqplot.config.errorFontSize;
  213. document.getElementById(target).style.fontStyle = $.jqplot.config.errorFontStyle;
  214. document.getElementById(target).style.fontWeight = $.jqplot.config.errorFontWeight;
  215. }
  216. }
  217. else {
  218. plot.init(target, _data, _options);
  219. plot.draw();
  220. plot.themeEngine.init.call(plot);
  221. return plot;
  222. }
  223. };
  224. $.jqplot.version = "1.0.9";
  225. $.jqplot.revision = "d96a669";
  226. $.jqplot.targetCounter = 1;
  227. // canvas manager to reuse canvases on the plot.
  228. // Should help solve problem of canvases not being freed and
  229. // problem of waiting forever for firefox to decide to free memory.
  230. $.jqplot.CanvasManager = function() {
  231. // canvases are managed globally so that they can be reused
  232. // across plots after they have been freed
  233. if (typeof $.jqplot.CanvasManager.canvases == 'undefined') {
  234. $.jqplot.CanvasManager.canvases = [];
  235. $.jqplot.CanvasManager.free = [];
  236. }
  237. var myCanvases = [];
  238. this.getCanvas = function() {
  239. var canvas;
  240. var makeNew = true;
  241. if (!$.jqplot.use_excanvas) {
  242. for (var i = 0, l = $.jqplot.CanvasManager.canvases.length; i < l; i++) {
  243. if ($.jqplot.CanvasManager.free[i] === true) {
  244. makeNew = false;
  245. canvas = $.jqplot.CanvasManager.canvases[i];
  246. // $(canvas).removeClass('jqplot-canvasManager-free').addClass('jqplot-canvasManager-inuse');
  247. $.jqplot.CanvasManager.free[i] = false;
  248. myCanvases.push(i);
  249. break;
  250. }
  251. }
  252. }
  253. if (makeNew) {
  254. canvas = document.createElement('canvas');
  255. myCanvases.push($.jqplot.CanvasManager.canvases.length);
  256. $.jqplot.CanvasManager.canvases.push(canvas);
  257. $.jqplot.CanvasManager.free.push(false);
  258. }
  259. return canvas;
  260. };
  261. // this method has to be used after settings the dimesions
  262. // on the element returned by getCanvas()
  263. this.initCanvas = function(canvas) {
  264. if ($.jqplot.use_excanvas) {
  265. return window.G_vmlCanvasManager.initElement(canvas);
  266. }
  267. var cctx = canvas.getContext('2d');
  268. var canvasBackingScale = 1;
  269. if (window.devicePixelRatio > 1 && (cctx.webkitBackingStorePixelRatio === undefined ||
  270. cctx.webkitBackingStorePixelRatio < 2)) {
  271. canvasBackingScale = window.devicePixelRatio;
  272. }
  273. var oldWidth = canvas.width;
  274. var oldHeight = canvas.height;
  275. canvas.width = canvasBackingScale * canvas.width;
  276. canvas.height = canvasBackingScale * canvas.height;
  277. canvas.style.width = oldWidth + 'px';
  278. canvas.style.height = oldHeight + 'px';
  279. cctx.save();
  280. cctx.scale(canvasBackingScale, canvasBackingScale);
  281. return canvas;
  282. };
  283. this.freeAllCanvases = function() {
  284. for (var i = 0, l=myCanvases.length; i < l; i++) {
  285. this.freeCanvas(myCanvases[i]);
  286. }
  287. myCanvases = [];
  288. };
  289. this.freeCanvas = function(idx) {
  290. if ($.jqplot.use_excanvas && window.G_vmlCanvasManager.uninitElement !== undefined) {
  291. // excanvas can't be reused, but properly unset
  292. window.G_vmlCanvasManager.uninitElement($.jqplot.CanvasManager.canvases[idx]);
  293. $.jqplot.CanvasManager.canvases[idx] = null;
  294. }
  295. else {
  296. var canvas = $.jqplot.CanvasManager.canvases[idx];
  297. canvas.getContext('2d').clearRect(0, 0, canvas.width, canvas.height);
  298. $(canvas).unbind().removeAttr('class').removeAttr('style');
  299. // Style attributes seemed to be still hanging around. wierd. Some ticks
  300. // still retained a left: 0px attribute after reusing a canvas.
  301. $(canvas).css({left: '', top: '', position: ''});
  302. // setting size to 0 may save memory of unused canvases?
  303. canvas.width = 0;
  304. canvas.height = 0;
  305. $.jqplot.CanvasManager.free[idx] = true;
  306. }
  307. };
  308. };
  309. // Convienence function that won't hang IE or FF without FireBug.
  310. $.jqplot.log = function() {
  311. if (window.console) {
  312. window.console.log.apply(window.console, arguments);
  313. }
  314. };
  315. $.jqplot.config = {
  316. addDomReference: false,
  317. enablePlugins:false,
  318. defaultHeight:300,
  319. defaultWidth:400,
  320. UTCAdjust:false,
  321. timezoneOffset: new Date(new Date().getTimezoneOffset() * 60000),
  322. errorMessage: '',
  323. errorBackground: '',
  324. errorBorder: '',
  325. errorFontFamily: '',
  326. errorFontSize: '',
  327. errorFontStyle: '',
  328. errorFontWeight: '',
  329. catchErrors: false,
  330. defaultTickFormatString: "%.1f",
  331. defaultColors: [ "#4bb2c5", "#EAA228", "#c5b47f", "#579575", "#839557", "#958c12", "#953579", "#4b5de4", "#d8b83f", "#ff5800", "#0085cc", "#c747a3", "#cddf54", "#FBD178", "#26B4E3", "#bd70c7"],
  332. defaultNegativeColors: [ "#498991", "#C08840", "#9F9274", "#546D61", "#646C4A", "#6F6621", "#6E3F5F", "#4F64B0", "#A89050", "#C45923", "#187399", "#945381", "#959E5C", "#C7AF7B", "#478396", "#907294"],
  333. dashLength: 4,
  334. gapLength: 4,
  335. dotGapLength: 2.5,
  336. srcLocation: 'jqplot/src/',
  337. pluginLocation: 'jqplot/src/plugins/'
  338. };
  339. $.jqplot.arrayMax = function( array ){
  340. return Math.max.apply( Math, array );
  341. };
  342. $.jqplot.arrayMin = function( array ){
  343. return Math.min.apply( Math, array );
  344. };
  345. $.jqplot.enablePlugins = $.jqplot.config.enablePlugins;
  346. // canvas related tests taken from modernizer:
  347. // Copyright (c) 2009 - 2010 Faruk Ates.
  348. // http://www.modernizr.com
  349. $.jqplot.support_canvas = function() {
  350. if (typeof $.jqplot.support_canvas.result == 'undefined') {
  351. $.jqplot.support_canvas.result = !!document.createElement('canvas').getContext;
  352. }
  353. return $.jqplot.support_canvas.result;
  354. };
  355. $.jqplot.support_canvas_text = function() {
  356. if (typeof $.jqplot.support_canvas_text.result == 'undefined') {
  357. if (window.G_vmlCanvasManager !== undefined && window.G_vmlCanvasManager._version > 887) {
  358. $.jqplot.support_canvas_text.result = true;
  359. }
  360. else {
  361. $.jqplot.support_canvas_text.result = !!(document.createElement('canvas').getContext && typeof document.createElement('canvas').getContext('2d').fillText == 'function');
  362. }
  363. }
  364. return $.jqplot.support_canvas_text.result;
  365. };
  366. $.jqplot.use_excanvas = ((!$.support.boxModel || !$.support.objectAll || !$support.leadingWhitespace) && !$.jqplot.support_canvas()) ? true : false;
  367. /**
  368. *
  369. * Hooks: jqPlot Pugin Hooks
  370. *
  371. * $.jqplot.preInitHooks - called before initialization.
  372. * $.jqplot.postInitHooks - called after initialization.
  373. * $.jqplot.preParseOptionsHooks - called before user options are parsed.
  374. * $.jqplot.postParseOptionsHooks - called after user options are parsed.
  375. * $.jqplot.preDrawHooks - called before plot draw.
  376. * $.jqplot.postDrawHooks - called after plot draw.
  377. * $.jqplot.preDrawSeriesHooks - called before each series is drawn.
  378. * $.jqplot.postDrawSeriesHooks - called after each series is drawn.
  379. * $.jqplot.preDrawLegendHooks - called before the legend is drawn.
  380. * $.jqplot.addLegendRowHooks - called at the end of legend draw, so plugins
  381. * can add rows to the legend table.
  382. * $.jqplot.preSeriesInitHooks - called before series is initialized.
  383. * $.jqplot.postSeriesInitHooks - called after series is initialized.
  384. * $.jqplot.preParseSeriesOptionsHooks - called before series related options
  385. * are parsed.
  386. * $.jqplot.postParseSeriesOptionsHooks - called after series related options
  387. * are parsed.
  388. * $.jqplot.eventListenerHooks - called at the end of plot drawing, binds
  389. * listeners to the event canvas which lays on top of the grid area.
  390. * $.jqplot.preDrawSeriesShadowHooks - called before series shadows are drawn.
  391. * $.jqplot.postDrawSeriesShadowHooks - called after series shadows are drawn.
  392. *
  393. */
  394. $.jqplot.preInitHooks = [];
  395. $.jqplot.postInitHooks = [];
  396. $.jqplot.preParseOptionsHooks = [];
  397. $.jqplot.postParseOptionsHooks = [];
  398. $.jqplot.preDrawHooks = [];
  399. $.jqplot.postDrawHooks = [];
  400. $.jqplot.preDrawSeriesHooks = [];
  401. $.jqplot.postDrawSeriesHooks = [];
  402. $.jqplot.preDrawLegendHooks = [];
  403. $.jqplot.addLegendRowHooks = [];
  404. $.jqplot.preSeriesInitHooks = [];
  405. $.jqplot.postSeriesInitHooks = [];
  406. $.jqplot.preParseSeriesOptionsHooks = [];
  407. $.jqplot.postParseSeriesOptionsHooks = [];
  408. $.jqplot.eventListenerHooks = [];
  409. $.jqplot.preDrawSeriesShadowHooks = [];
  410. $.jqplot.postDrawSeriesShadowHooks = [];
  411. // A superclass holding some common properties and methods.
  412. $.jqplot.ElemContainer = function() {
  413. this._elem;
  414. this._plotWidth;
  415. this._plotHeight;
  416. this._plotDimensions = {height:null, width:null};
  417. };
  418. $.jqplot.ElemContainer.prototype.createElement = function(el, offsets, clss, cssopts, attrib) {
  419. this._offsets = offsets;
  420. var klass = clss || 'jqplot';
  421. var elem = document.createElement(el);
  422. this._elem = $(elem);
  423. this._elem.addClass(klass);
  424. this._elem.css(cssopts);
  425. this._elem.attr(attrib);
  426. // avoid memory leak;
  427. elem = null;
  428. return this._elem;
  429. };
  430. $.jqplot.ElemContainer.prototype.getWidth = function() {
  431. if (this._elem) {
  432. return this._elem.outerWidth(true);
  433. }
  434. else {
  435. return null;
  436. }
  437. };
  438. $.jqplot.ElemContainer.prototype.getHeight = function() {
  439. if (this._elem) {
  440. return this._elem.outerHeight(true);
  441. }
  442. else {
  443. return null;
  444. }
  445. };
  446. $.jqplot.ElemContainer.prototype.getPosition = function() {
  447. if (this._elem) {
  448. return this._elem.position();
  449. }
  450. else {
  451. return {top:null, left:null, bottom:null, right:null};
  452. }
  453. };
  454. $.jqplot.ElemContainer.prototype.getTop = function() {
  455. return this.getPosition().top;
  456. };
  457. $.jqplot.ElemContainer.prototype.getLeft = function() {
  458. return this.getPosition().left;
  459. };
  460. $.jqplot.ElemContainer.prototype.getBottom = function() {
  461. return this._elem.css('bottom');
  462. };
  463. $.jqplot.ElemContainer.prototype.getRight = function() {
  464. return this._elem.css('right');
  465. };
  466. /**
  467. * Class: Axis
  468. * An individual axis object. Cannot be instantiated directly, but created
  469. * by the Plot object. Axis properties can be set or overridden by the
  470. * options passed in from the user.
  471. *
  472. */
  473. function Axis(name) {
  474. $.jqplot.ElemContainer.call(this);
  475. // Group: Properties
  476. //
  477. // Axes options are specified within an axes object at the top level of the
  478. // plot options like so:
  479. // > {
  480. // > axes: {
  481. // > xaxis: {min: 5},
  482. // > yaxis: {min: 2, max: 8, numberTicks:4},
  483. // > x2axis: {pad: 1.5},
  484. // > y2axis: {ticks:[22, 44, 66, 88]}
  485. // > }
  486. // > }
  487. // There are 2 x axes, 'xaxis' and 'x2axis', and
  488. // 9 yaxes, 'yaxis', 'y2axis'. 'y3axis', ... Any or all of which may be specified.
  489. this.name = name;
  490. this._series = [];
  491. // prop: show
  492. // Wether to display the axis on the graph.
  493. this.show = false;
  494. // prop: tickRenderer
  495. // A class of a rendering engine for creating the ticks labels displayed on the plot,
  496. // See <$.jqplot.AxisTickRenderer>.
  497. this.tickRenderer = $.jqplot.AxisTickRenderer;
  498. // prop: tickOptions
  499. // Options that will be passed to the tickRenderer, see <$.jqplot.AxisTickRenderer> options.
  500. this.tickOptions = {};
  501. // prop: labelRenderer
  502. // A class of a rendering engine for creating an axis label.
  503. this.labelRenderer = $.jqplot.AxisLabelRenderer;
  504. // prop: labelOptions
  505. // Options passed to the label renderer.
  506. this.labelOptions = {};
  507. // prop: label
  508. // Label for the axis
  509. this.label = null;
  510. // prop: showLabel
  511. // true to show the axis label.
  512. this.showLabel = true;
  513. // prop: min
  514. // minimum value of the axis (in data units, not pixels).
  515. this.min = null;
  516. // prop: max
  517. // maximum value of the axis (in data units, not pixels).
  518. this.max = null;
  519. // prop: autoscale
  520. // DEPRECATED
  521. // the default scaling algorithm produces superior results.
  522. this.autoscale = false;
  523. // prop: pad
  524. // Padding to extend the range above and below the data bounds.
  525. // The data range is multiplied by this factor to determine minimum and maximum axis bounds.
  526. // A value of 0 will be interpreted to mean no padding, and pad will be set to 1.0.
  527. this.pad = 1.2;
  528. // prop: padMax
  529. // Padding to extend the range above data bounds.
  530. // The top of the data range is multiplied by this factor to determine maximum axis bounds.
  531. // A value of 0 will be interpreted to mean no padding, and padMax will be set to 1.0.
  532. this.padMax = null;
  533. // prop: padMin
  534. // Padding to extend the range below data bounds.
  535. // The bottom of the data range is multiplied by this factor to determine minimum axis bounds.
  536. // A value of 0 will be interpreted to mean no padding, and padMin will be set to 1.0.
  537. this.padMin = null;
  538. // prop: ticks
  539. // 1D [val, val, ...] or 2D [[val, label], [val, label], ...] array of ticks for the axis.
  540. // If no label is specified, the value is formatted into an appropriate label.
  541. this.ticks = [];
  542. // prop: numberTicks
  543. // Desired number of ticks. Default is to compute automatically.
  544. this.numberTicks;
  545. // prop: tickInterval
  546. // number of units between ticks. Mutually exclusive with numberTicks.
  547. this.tickInterval;
  548. // prop: renderer
  549. // A class of a rendering engine that handles tick generation,
  550. // scaling input data to pixel grid units and drawing the axis element.
  551. this.renderer = $.jqplot.LinearAxisRenderer;
  552. // prop: rendererOptions
  553. // renderer specific options. See <$.jqplot.LinearAxisRenderer> for options.
  554. this.rendererOptions = {};
  555. // prop: showTicks
  556. // Wether to show the ticks (both marks and labels) or not.
  557. // Will not override showMark and showLabel options if specified on the ticks themselves.
  558. this.showTicks = true;
  559. // prop: showTickMarks
  560. // Wether to show the tick marks (line crossing grid) or not.
  561. // Overridden by showTicks and showMark option of tick itself.
  562. this.showTickMarks = true;
  563. // prop: showMinorTicks
  564. // Wether or not to show minor ticks. This is renderer dependent.
  565. this.showMinorTicks = true;
  566. // prop: drawMajorGridlines
  567. // True to draw gridlines for major axis ticks.
  568. this.drawMajorGridlines = true;
  569. // prop: drawMinorGridlines
  570. // True to draw gridlines for minor ticks.
  571. this.drawMinorGridlines = false;
  572. // prop: drawMajorTickMarks
  573. // True to draw tick marks for major axis ticks.
  574. this.drawMajorTickMarks = true;
  575. // prop: drawMinorTickMarks
  576. // True to draw tick marks for minor ticks. This is renderer dependent.
  577. this.drawMinorTickMarks = true;
  578. // prop: useSeriesColor
  579. // Use the color of the first series associated with this axis for the
  580. // tick marks and line bordering this axis.
  581. this.useSeriesColor = false;
  582. // prop: borderWidth
  583. // width of line stroked at the border of the axis. Defaults
  584. // to the width of the grid boarder.
  585. this.borderWidth = null;
  586. // prop: borderColor
  587. // color of the border adjacent to the axis. Defaults to grid border color.
  588. this.borderColor = null;
  589. // prop: scaleToHiddenSeries
  590. // True to include hidden series when computing axes bounds and scaling.
  591. this.scaleToHiddenSeries = false;
  592. // minimum and maximum values on the axis.
  593. this._dataBounds = {min:null, max:null};
  594. // statistics (min, max, mean) as well as actual data intervals for each series attached to axis.
  595. // holds collection of {intervals:[], min:, max:, mean: } objects for each series on axis.
  596. this._intervalStats = [];
  597. // pixel position from the top left of the min value and max value on the axis.
  598. this._offsets = {min:null, max:null};
  599. this._ticks=[];
  600. this._label = null;
  601. // prop: syncTicks
  602. // true to try and synchronize tick spacing across multiple axes so that ticks and
  603. // grid lines line up. This has an impact on autoscaling algorithm, however.
  604. // In general, autoscaling an individual axis will work better if it does not
  605. // have to sync ticks.
  606. this.syncTicks = null;
  607. // prop: tickSpacing
  608. // Approximate pixel spacing between ticks on graph. Used during autoscaling.
  609. // This number will be an upper bound, actual spacing will be less.
  610. this.tickSpacing = 75;
  611. // Properties to hold the original values for min, max, ticks, tickInterval and numberTicks
  612. // so they can be restored if altered by plugins.
  613. this._min = null;
  614. this._max = null;
  615. this._tickInterval = null;
  616. this._numberTicks = null;
  617. this.__ticks = null;
  618. // hold original user options.
  619. this._options = {};
  620. }
  621. Axis.prototype = new $.jqplot.ElemContainer();
  622. Axis.prototype.constructor = Axis;
  623. Axis.prototype.init = function() {
  624. if ($.isFunction(this.renderer)) {
  625. this.renderer = new this.renderer();
  626. }
  627. // set the axis name
  628. this.tickOptions.axis = this.name;
  629. // if showMark or showLabel tick options not specified, use value of axis option.
  630. // showTicks overrides showTickMarks.
  631. if (this.tickOptions.showMark == null) {
  632. this.tickOptions.showMark = this.showTicks;
  633. }
  634. if (this.tickOptions.showMark == null) {
  635. this.tickOptions.showMark = this.showTickMarks;
  636. }
  637. if (this.tickOptions.showLabel == null) {
  638. this.tickOptions.showLabel = this.showTicks;
  639. }
  640. if (this.label == null || this.label == '') {
  641. this.showLabel = false;
  642. }
  643. else {
  644. this.labelOptions.label = this.label;
  645. }
  646. if (this.showLabel == false) {
  647. this.labelOptions.show = false;
  648. }
  649. // set the default padMax, padMin if not specified
  650. // special check, if no padding desired, padding
  651. // should be set to 1.0
  652. if (this.pad == 0) {
  653. this.pad = 1.0;
  654. }
  655. if (this.padMax == 0) {
  656. this.padMax = 1.0;
  657. }
  658. if (this.padMin == 0) {
  659. this.padMin = 1.0;
  660. }
  661. if (this.padMax == null) {
  662. this.padMax = (this.pad-1)/2 + 1;
  663. }
  664. if (this.padMin == null) {
  665. this.padMin = (this.pad-1)/2 + 1;
  666. }
  667. // now that padMin and padMax are correctly set, reset pad in case user has supplied
  668. // padMin and/or padMax
  669. this.pad = this.padMax + this.padMin - 1;
  670. if (this.min != null || this.max != null) {
  671. this.autoscale = false;
  672. }
  673. // if not set, sync ticks for y axes but not x by default.
  674. if (this.syncTicks == null && this.name.indexOf('y') > -1) {
  675. this.syncTicks = true;
  676. }
  677. else if (this.syncTicks == null){
  678. this.syncTicks = false;
  679. }
  680. this.renderer.init.call(this, this.rendererOptions);
  681. };
  682. Axis.prototype.draw = function(ctx, plot) {
  683. // Memory Leaks patch
  684. if (this.__ticks) {
  685. this.__ticks = null;
  686. }
  687. return this.renderer.draw.call(this, ctx, plot);
  688. };
  689. Axis.prototype.set = function() {
  690. this.renderer.set.call(this);
  691. };
  692. Axis.prototype.pack = function(pos, offsets) {
  693. if (this.show) {
  694. this.renderer.pack.call(this, pos, offsets);
  695. }
  696. // these properties should all be available now.
  697. if (this._min == null) {
  698. this._min = this.min;
  699. this._max = this.max;
  700. this._tickInterval = this.tickInterval;
  701. this._numberTicks = this.numberTicks;
  702. this.__ticks = this._ticks;
  703. }
  704. };
  705. // reset the axis back to original values if it has been scaled, zoomed, etc.
  706. Axis.prototype.reset = function() {
  707. this.renderer.reset.call(this);
  708. };
  709. Axis.prototype.resetScale = function(opts) {
  710. $.extend(true, this, {min: null, max: null, numberTicks: null, tickInterval: null, _ticks: [], ticks: []}, opts);
  711. this.resetDataBounds();
  712. };
  713. Axis.prototype.resetDataBounds = function() {
  714. // Go through all the series attached to this axis and find
  715. // the min/max bounds for this axis.
  716. var db = this._dataBounds;
  717. db.min = null;
  718. db.max = null;
  719. var l, s, d;
  720. // check for when to force min 0 on bar series plots.
  721. var doforce = (this.show) ? true : false;
  722. for (var i=0; i<this._series.length; i++) {
  723. s = this._series[i];
  724. if (s.show || this.scaleToHiddenSeries) {
  725. d = s._plotData;
  726. if (s._type === 'line' && s.renderer.bands.show && this.name.charAt(0) !== 'x') {
  727. d = [[0, s.renderer.bands._min], [1, s.renderer.bands._max]];
  728. }
  729. var minyidx = 1, maxyidx = 1;
  730. if (s._type != null && s._type == 'ohlc') {
  731. minyidx = 3;
  732. maxyidx = 2;
  733. }
  734. for (var j=0, l=d.length; j<l; j++) {
  735. if (this.name == 'xaxis' || this.name == 'x2axis') {
  736. if ((d[j][0] != null && d[j][0] < db.min) || db.min == null) {
  737. db.min = d[j][0];
  738. }
  739. if ((d[j][0] != null && d[j][0] > db.max) || db.max == null) {
  740. db.max = d[j][0];
  741. }
  742. }
  743. else {
  744. if ((d[j][minyidx] != null && d[j][minyidx] < db.min) || db.min == null) {
  745. db.min = d[j][minyidx];
  746. }
  747. if ((d[j][maxyidx] != null && d[j][maxyidx] > db.max) || db.max == null) {
  748. db.max = d[j][maxyidx];
  749. }
  750. }
  751. }
  752. // Hack to not pad out bottom of bar plots unless user has specified a padding.
  753. // every series will have a chance to set doforce to false. once it is set to
  754. // false, it cannot be reset to true.
  755. // If any series attached to axis is not a bar, wont force 0.
  756. if (doforce && s.renderer.constructor !== $.jqplot.BarRenderer) {
  757. doforce = false;
  758. }
  759. else if (doforce && this._options.hasOwnProperty('forceTickAt0') && this._options.forceTickAt0 == false) {
  760. doforce = false;
  761. }
  762. else if (doforce && s.renderer.constructor === $.jqplot.BarRenderer) {
  763. if (s.barDirection == 'vertical' && this.name != 'xaxis' && this.name != 'x2axis') {
  764. if (this._options.pad != null || this._options.padMin != null) {
  765. doforce = false;
  766. }
  767. }
  768. else if (s.barDirection == 'horizontal' && (this.name == 'xaxis' || this.name == 'x2axis')) {
  769. if (this._options.pad != null || this._options.padMin != null) {
  770. doforce = false;
  771. }
  772. }
  773. }
  774. }
  775. }
  776. if (doforce && this.renderer.constructor === $.jqplot.LinearAxisRenderer && db.min >= 0) {
  777. this.padMin = 1.0;
  778. this.forceTickAt0 = true;
  779. }
  780. };
  781. /**
  782. * Class: Legend
  783. * Legend object. Cannot be instantiated directly, but created
  784. * by the Plot object. Legend properties can be set or overridden by the
  785. * options passed in from the user.
  786. */
  787. function Legend(options) {
  788. $.jqplot.ElemContainer.call(this);
  789. // Group: Properties
  790. // prop: show
  791. // Wether to display the legend on the graph.
  792. this.show = false;
  793. // prop: location
  794. // Placement of the legend. one of the compass directions: nw, n, ne, e, se, s, sw, w
  795. this.location = 'ne';
  796. // prop: labels
  797. // Array of labels to use. By default the renderer will look for labels on the series.
  798. // Labels specified in this array will override labels specified on the series.
  799. this.labels = [];
  800. // prop: showLabels
  801. // true to show the label text on the legend.
  802. this.showLabels = true;
  803. // prop: showSwatch
  804. // true to show the color swatches on the legend.
  805. this.showSwatches = true;
  806. // prop: placement
  807. // "insideGrid" places legend inside the grid area of the plot.
  808. // "outsideGrid" places the legend outside the grid but inside the plot container,
  809. // shrinking the grid to accomodate the legend.
  810. // "inside" synonym for "insideGrid",
  811. // "outside" places the legend ouside the grid area, but does not shrink the grid which
  812. // can cause the legend to overflow the plot container.
  813. this.placement = "insideGrid";
  814. // prop: xoffset
  815. // DEPRECATED. Set the margins on the legend using the marginTop, marginLeft, etc.
  816. // properties or via CSS margin styling of the .jqplot-table-legend class.
  817. this.xoffset = 0;
  818. // prop: yoffset
  819. // DEPRECATED. Set the margins on the legend using the marginTop, marginLeft, etc.
  820. // properties or via CSS margin styling of the .jqplot-table-legend class.
  821. this.yoffset = 0;
  822. // prop: border
  823. // css spec for the border around the legend box.
  824. this.border;
  825. // prop: background
  826. // css spec for the background of the legend box.
  827. this.background;
  828. // prop: textColor
  829. // css color spec for the legend text.
  830. this.textColor;
  831. // prop: fontFamily
  832. // css font-family spec for the legend text.
  833. this.fontFamily;
  834. // prop: fontSize
  835. // css font-size spec for the legend text.
  836. this.fontSize ;
  837. // prop: rowSpacing
  838. // css padding-top spec for the rows in the legend.
  839. this.rowSpacing = '0.5em';
  840. // renderer
  841. // A class that will create a DOM object for the legend,
  842. // see <$.jqplot.TableLegendRenderer>.
  843. this.renderer = $.jqplot.TableLegendRenderer;
  844. // prop: rendererOptions
  845. // renderer specific options passed to the renderer.
  846. this.rendererOptions = {};
  847. // prop: predraw
  848. // Wether to draw the legend before the series or not.
  849. // Used with series specific legend renderers for pie, donut, mekko charts, etc.
  850. this.preDraw = false;
  851. // prop: marginTop
  852. // CSS margin for the legend DOM element. This will set an element
  853. // CSS style for the margin which will override any style sheet setting.
  854. // The default will be taken from the stylesheet.
  855. this.marginTop = null;
  856. // prop: marginRight
  857. // CSS margin for the legend DOM element. This will set an element
  858. // CSS style for the margin which will override any style sheet setting.
  859. // The default will be taken from the stylesheet.
  860. this.marginRight = null;
  861. // prop: marginBottom
  862. // CSS margin for the legend DOM element. This will set an element
  863. // CSS style for the margin which will override any style sheet setting.
  864. // The default will be taken from the stylesheet.
  865. this.marginBottom = null;
  866. // prop: marginLeft
  867. // CSS margin for the legend DOM element. This will set an element
  868. // CSS style for the margin which will override any style sheet setting.
  869. // The default will be taken from the stylesheet.
  870. this.marginLeft = null;
  871. // prop: escapeHtml
  872. // True to escape special characters with their html entity equivalents
  873. // in legend text. "<" becomes &lt; and so on, so html tags are not rendered.
  874. this.escapeHtml = false;
  875. this._series = [];
  876. $.extend(true, this, options);
  877. }
  878. Legend.prototype = new $.jqplot.ElemContainer();
  879. Legend.prototype.constructor = Legend;
  880. Legend.prototype.setOptions = function(options) {
  881. $.extend(true, this, options);
  882. // Try to emulate deprecated behaviour
  883. // if user has specified xoffset or yoffset, copy these to
  884. // the margin properties.
  885. if (this.placement == 'inside') {
  886. this.placement = 'insideGrid';
  887. }
  888. if (this.xoffset >0) {
  889. if (this.placement == 'insideGrid') {
  890. switch (this.location) {
  891. case 'nw':
  892. case 'w':
  893. case 'sw':
  894. if (this.marginLeft == null) {
  895. this.marginLeft = this.xoffset + 'px';
  896. }
  897. this.marginRight = '0px';
  898. break;
  899. case 'ne':
  900. case 'e':
  901. case 'se':
  902. default:
  903. if (this.marginRight == null) {
  904. this.marginRight = this.xoffset + 'px';
  905. }
  906. this.marginLeft = '0px';
  907. break;
  908. }
  909. }
  910. else if (this.placement == 'outside') {
  911. switch (this.location) {
  912. case 'nw':
  913. case 'w':
  914. case 'sw':
  915. if (this.marginRight == null) {
  916. this.marginRight = this.xoffset + 'px';
  917. }
  918. this.marginLeft = '0px';
  919. break;
  920. case 'ne':
  921. case 'e':
  922. case 'se':
  923. default:
  924. if (this.marginLeft == null) {
  925. this.marginLeft = this.xoffset + 'px';
  926. }
  927. this.marginRight = '0px';
  928. break;
  929. }
  930. }
  931. this.xoffset = 0;
  932. }
  933. if (this.yoffset >0) {
  934. if (this.placement == 'outside') {
  935. switch (this.location) {
  936. case 'sw':
  937. case 's':
  938. case 'se':
  939. if (this.marginTop == null) {
  940. this.marginTop = this.yoffset + 'px';
  941. }
  942. this.marginBottom = '0px';
  943. break;
  944. case 'ne':
  945. case 'n':
  946. case 'nw':
  947. default:
  948. if (this.marginBottom == null) {
  949. this.marginBottom = this.yoffset + 'px';
  950. }
  951. this.marginTop = '0px';
  952. break;
  953. }
  954. }
  955. else if (this.placement == 'insideGrid') {
  956. switch (this.location) {
  957. case 'sw':
  958. case 's':
  959. case 'se':
  960. if (this.marginBottom == null) {
  961. this.marginBottom = this.yoffset + 'px';
  962. }
  963. this.marginTop = '0px';
  964. break;
  965. case 'ne':
  966. case 'n':
  967. case 'nw':
  968. default:
  969. if (this.marginTop == null) {
  970. this.marginTop = this.yoffset + 'px';
  971. }
  972. this.marginBottom = '0px';
  973. break;
  974. }
  975. }
  976. this.yoffset = 0;
  977. }
  978. // TO-DO:
  979. // Handle case where offsets are < 0.
  980. //
  981. };
  982. Legend.prototype.init = function() {
  983. if ($.isFunction(this.renderer)) {
  984. this.renderer = new this.renderer();
  985. }
  986. this.renderer.init.call(this, this.rendererOptions);
  987. };
  988. Legend.prototype.draw = function(offsets, plot) {
  989. for (var i=0; i<$.jqplot.preDrawLegendHooks.length; i++){
  990. $.jqplot.preDrawLegendHooks[i].call(this, offsets);
  991. }
  992. return this.renderer.draw.call(this, offsets, plot);
  993. };
  994. Legend.prototype.pack = function(offsets) {
  995. this.renderer.pack.call(this, offsets);
  996. };
  997. /**
  998. * Class: Title
  999. * Plot Title object. Cannot be instantiated directly, but created
  1000. * by the Plot object. Title properties can be set or overridden by the
  1001. * options passed in from the user.
  1002. *
  1003. * Parameters:
  1004. * text - text of the title.
  1005. */
  1006. function Title(text) {
  1007. $.jqplot.ElemContainer.call(this);
  1008. // Group: Properties
  1009. // prop: text
  1010. // text of the title;
  1011. this.text = text;
  1012. // prop: show
  1013. // whether or not to show the title
  1014. this.show = true;
  1015. // prop: fontFamily
  1016. // css font-family spec for the text.
  1017. this.fontFamily;
  1018. // prop: fontSize
  1019. // css font-size spec for the text.
  1020. this.fontSize ;
  1021. // prop: textAlign
  1022. // css text-align spec for the text.
  1023. this.textAlign;
  1024. // prop: textColor
  1025. // css color spec for the text.
  1026. this.textColor;
  1027. // prop: renderer
  1028. // A class for creating a DOM element for the title,
  1029. // see <$.jqplot.DivTitleRenderer>.
  1030. this.renderer = $.jqplot.DivTitleRenderer;
  1031. // prop: rendererOptions
  1032. // renderer specific options passed to the renderer.
  1033. this.rendererOptions = {};
  1034. // prop: escapeHtml
  1035. // True to escape special characters with their html entity equivalents
  1036. // in title text. "<" becomes &lt; and so on, so html tags are not rendered.
  1037. this.escapeHtml = false;
  1038. }
  1039. Title.prototype = new $.jqplot.ElemContainer();
  1040. Title.prototype.constructor = Title;
  1041. Title.prototype.init = function() {
  1042. if ($.isFunction(this.renderer)) {
  1043. this.renderer = new this.renderer();
  1044. }
  1045. this.renderer.init.call(this, this.rendererOptions);
  1046. };
  1047. Title.prototype.draw = function(width) {
  1048. return this.renderer.draw.call(this, width);
  1049. };
  1050. Title.prototype.pack = function() {
  1051. this.renderer.pack.call(this);
  1052. };
  1053. /**
  1054. * Class: Series
  1055. * An individual data series object. Cannot be instantiated directly, but created
  1056. * by the Plot object. Series properties can be set or overridden by the
  1057. * options passed in from the user.
  1058. */
  1059. function Series(options) {
  1060. options = options || {};
  1061. $.jqplot.ElemContainer.call(this);
  1062. // Group: Properties
  1063. // Properties will be assigned from a series array at the top level of the
  1064. // options. If you had two series and wanted to change the color and line
  1065. // width of the first and set the second to use the secondary y axis with
  1066. // no shadow and supply custom labels for each:
  1067. // > {
  1068. // > series:[
  1069. // > {color: '#ff4466', lineWidth: 5, label:'good line'},
  1070. // > {yaxis: 'y2axis', shadow: false, label:'bad line'}
  1071. // > ]
  1072. // > }
  1073. // prop: show
  1074. // whether or not to draw the series.
  1075. this.show = true;
  1076. // prop: xaxis
  1077. // which x axis to use with this series, either 'xaxis' or 'x2axis'.
  1078. this.xaxis = 'xaxis';
  1079. this._xaxis;
  1080. // prop: yaxis
  1081. // which y axis to use with this series, either 'yaxis' or 'y2axis'.
  1082. this.yaxis = 'yaxis';
  1083. this._yaxis;
  1084. this.gridBorderWidth = 2.0;
  1085. // prop: renderer
  1086. // A class of a renderer which will draw the series,
  1087. // see <$.jqplot.LineRenderer>.
  1088. this.renderer = $.jqplot.LineRenderer;
  1089. // prop: rendererOptions
  1090. // Options to pass on to the renderer.
  1091. this.rendererOptions = {};
  1092. this.data = [];
  1093. this.gridData = [];
  1094. // prop: label
  1095. // Line label to use in the legend.
  1096. this.label = '';
  1097. // prop: showLabel
  1098. // true to show label for this series in the legend.
  1099. this.showLabel = true;
  1100. // prop: color
  1101. // css color spec for the series
  1102. this.color;
  1103. // prop: negativeColor
  1104. // css color spec used for filled (area) plots that are filled to zero and
  1105. // the "useNegativeColors" option is true.
  1106. this.negativeColor;
  1107. // prop: lineWidth
  1108. // width of the line in pixels. May have different meanings depending on renderer.
  1109. this.lineWidth = 2.5;
  1110. // prop: lineJoin
  1111. // Canvas lineJoin style between segments of series.
  1112. this.lineJoin = 'round';
  1113. // prop: lineCap
  1114. // Canvas lineCap style at ends of line.
  1115. this.lineCap = 'round';
  1116. // prop: linePattern
  1117. // line pattern 'dashed', 'dotted', 'solid', some combination
  1118. // of '-' and '.' characters such as '.-.' or a numerical array like
  1119. // [draw, skip, draw, skip, ...] such as [1, 10] to draw a dotted line,
  1120. // [1, 10, 20, 10] to draw a dot-dash line, and so on.
  1121. this.linePattern = 'solid';
  1122. this.shadow = true;
  1123. // prop: shadowAngle
  1124. // Shadow angle in degrees
  1125. this.shadowAngle = 45;
  1126. // prop: shadowOffset
  1127. // Shadow offset from line in pixels
  1128. this.shadowOffset = 1.25;
  1129. // prop: shadowDepth
  1130. // Number of times shadow is stroked, each stroke offset shadowOffset from the last.
  1131. this.shadowDepth = 3;
  1132. // prop: shadowAlpha
  1133. // Alpha channel transparency of shadow. 0 = transparent.
  1134. this.shadowAlpha = '0.1';
  1135. // prop: breakOnNull
  1136. // Wether line segments should be be broken at null value.
  1137. // False will join point on either side of line.
  1138. this.breakOnNull = false;
  1139. // prop: markerRenderer
  1140. // A class of a renderer which will draw marker (e.g. circle, square, ...) at the data points,
  1141. // see <$.jqplot.MarkerRenderer>.
  1142. this.markerRenderer = $.jqplot.MarkerRenderer;
  1143. // prop: markerOptions
  1144. // renderer specific options to pass to the markerRenderer,
  1145. // see <$.jqplot.MarkerRenderer>.
  1146. this.markerOptions = {};
  1147. // prop: showLine
  1148. // whether to actually draw the line or not. Series will still be renderered, even if no line is drawn.
  1149. this.showLine = true;
  1150. // prop: showMarker
  1151. // whether or not to show the markers at the data points.
  1152. this.showMarker = true;
  1153. // prop: index
  1154. // 0 based index of this series in the plot series array.
  1155. this.index;
  1156. // prop: fill
  1157. // true or false, whether to fill under lines or in bars.
  1158. // May not be implemented in all renderers.
  1159. this.fill = false;
  1160. // prop: fillColor
  1161. // CSS color spec to use for fill under line. Defaults to line color.
  1162. this.fillColor;
  1163. // prop: fillAlpha
  1164. // Alpha transparency to apply to the fill under the line.
  1165. // Use this to adjust alpha separate from fill color.
  1166. this.fillAlpha;
  1167. // prop: fillAndStroke
  1168. // If true will stroke the line (with color this.color) as well as fill under it.
  1169. // Applies only when fill is true.
  1170. this.fillAndStroke = false;
  1171. // prop: disableStack
  1172. // true to not stack this series with other series in the plot.
  1173. // To render properly, non-stacked series must come after any stacked series
  1174. // in the plot's data series array. So, the plot's data series array would look like:
  1175. // > [stackedSeries1, stackedSeries2, ..., nonStackedSeries1, nonStackedSeries2, ...]
  1176. // disableStack will put a gap in the stacking order of series, and subsequent
  1177. // stacked series will not fill down through the non-stacked series and will
  1178. // most likely not stack properly on top of the non-stacked series.
  1179. this.disableStack = false;
  1180. // _stack is set by the Plot if the plot is a stacked chart.
  1181. // will stack lines or bars on top of one another to build a "mountain" style chart.
  1182. // May not be implemented in all renderers.
  1183. this._stack = false;
  1184. // prop: neighborThreshold
  1185. // how close or far (in pixels) the cursor must be from a point marker to detect the point.
  1186. this.neighborThreshold = 4;
  1187. // prop: fillToZero
  1188. // true will force bar and filled series to fill toward zero on the fill Axis.
  1189. this.fillToZero = false;
  1190. // prop: fillToValue
  1191. // fill a filled series to this value on the fill axis.
  1192. // Works in conjunction with fillToZero, so that must be true.
  1193. this.fillToValue = 0;
  1194. // prop: fillAxis
  1195. // Either 'x' or 'y'. Which axis to fill the line toward if fillToZero is true.
  1196. // 'y' means fill up/down to 0 on the y axis for this series.
  1197. this.fillAxis = 'y';
  1198. // prop: useNegativeColors
  1199. // true to color negative values differently in filled and bar charts.
  1200. this.useNegativeColors = true;
  1201. this._stackData = [];
  1202. // _plotData accounts for stacking. If plots not stacked, _plotData and data are same. If
  1203. // stacked, _plotData is accumulation of stacking data.
  1204. this._plotData = [];
  1205. // _plotValues hold the individual x and y values that will be plotted for this series.
  1206. this._plotValues = {x:[], y:[]};
  1207. // statistics about the intervals between data points. Used for auto scaling.
  1208. this._intervals = {x:{}, y:{}};
  1209. // data from the previous series, for stacked charts.
  1210. this._prevPlotData = [];
  1211. this._prevGridData = [];
  1212. this._stackAxis = 'y';
  1213. this._primaryAxis = '_xaxis';
  1214. // give each series a canvas to draw on. This should allow for redrawing speedups.
  1215. this.canvas = new $.jqplot.GenericCanvas();
  1216. this.shadowCanvas = new $.jqplot.GenericCanvas();
  1217. this.plugins = {};
  1218. // sum of y values in this series.
  1219. this._sumy = 0;
  1220. this._sumx = 0;
  1221. this._type = '';
  1222. this.step = false;
  1223. }
  1224. Series.prototype = new $.jqplot.ElemContainer();
  1225. Series.prototype.constructor = Series;
  1226. Series.prototype.init = function(index, gridbw, plot) {
  1227. // weed out any null values in the data.
  1228. this.index = index;
  1229. this.gridBorderWidth = gridbw;
  1230. var d = this.data;
  1231. var temp = [], i, l;
  1232. for (i=0, l=d.length; i<l; i++) {
  1233. if (! this.breakOnNull) {
  1234. if (d[i] == null || d[i][0] == null || d[i][1] == null) {
  1235. continue;
  1236. }
  1237. else {
  1238. temp.push(d[i]);
  1239. }
  1240. }
  1241. else {
  1242. // TODO: figure out what to do with null values
  1243. // probably involve keeping nulls in data array
  1244. // and then updating renderers to break line
  1245. // when it hits null value.
  1246. // For now, just keep value.
  1247. temp.push(d[i]);
  1248. }
  1249. }
  1250. this.data = temp;
  1251. // parse the renderer options and apply default colors if not provided
  1252. // Set color even if not shown, so series don't change colors when other
  1253. // series on plot shown/hidden.
  1254. if (!this.color) {
  1255. this.color = plot.colorGenerator.get(this.index);
  1256. }
  1257. if (!this.negativeColor) {
  1258. this.negativeColor = plot.negativeColorGenerator.get(this.index);
  1259. }
  1260. if (!this.fillColor) {
  1261. this.fillColor = this.color;
  1262. }
  1263. if (this.fillAlpha) {
  1264. var comp = $.jqplot.normalize2rgb(this.fillColor);
  1265. var comp = $.jqplot.getColorComponents(comp);
  1266. this.fillColor = 'rgba('+comp[0]+','+comp[1]+','+comp[2]+','+this.fillAlpha+')';
  1267. }
  1268. if ($.isFunction(this.renderer)) {
  1269. this.renderer = new this.renderer();
  1270. }
  1271. this.renderer.init.call(this, this.rendererOptions, plot);
  1272. this.markerRenderer = new this.markerRenderer();
  1273. if (!this.markerOptions.color) {
  1274. this.markerOptions.color = this.color;
  1275. }
  1276. if (this.markerOptions.show == null) {
  1277. this.markerOptions.show = this.showMarker;
  1278. }
  1279. this.showMarker = this.markerOptions.show;
  1280. // the markerRenderer is called within its own scope, don't want to overwrite series options!!
  1281. this.markerRenderer.init(this.markerOptions);
  1282. };
  1283. // data - optional data point array to draw using this series renderer
  1284. // gridData - optional grid data point array to draw using this series renderer
  1285. // stackData - array of cumulative data for stacked plots.
  1286. Series.prototype.draw = function(sctx, opts, plot) {
  1287. var options = (opts == undefined) ? {} : opts;
  1288. sctx = (sctx == undefined) ? this.canvas._ctx : sctx;
  1289. var j, data, gridData;
  1290. // hooks get called even if series not shown
  1291. // we don't clear canvas here, it would wipe out all other series as well.
  1292. for (j=0; j<$.jqplot.preDrawSeriesHooks.length; j++) {
  1293. $.jqplot.preDrawSeriesHooks[j].call(this, sctx, options);
  1294. }
  1295. if (this.show) {
  1296. this.renderer.setGridData.call(this, plot);
  1297. if (!options.preventJqPlotSeriesDrawTrigger) {
  1298. $(sctx.canvas).trigger('jqplotSeriesDraw', [this.data, this.gridData]);
  1299. }
  1300. data = [];
  1301. if (options.data) {
  1302. data = options.data;
  1303. }
  1304. else if (!this._stack) {
  1305. data = this.data;
  1306. }
  1307. else {
  1308. data = this._plotData;
  1309. }
  1310. gridData = options.gridData || this.renderer.makeGridData.call(this, data, plot);
  1311. if (this._type === 'line' && this.renderer.smooth && this.renderer._smoothedData.length) {
  1312. gridData = this.renderer._smoothedData;
  1313. }
  1314. this.renderer.draw.call(this, sctx, gridData, options, plot);
  1315. }
  1316. for (j=0; j<$.jqplot.postDrawSeriesHooks.length; j++) {
  1317. $.jqplot.postDrawSeriesHooks[j].call(this, sctx, options, plot);
  1318. }
  1319. sctx = opts = plot = j = data = gridData = null;
  1320. };
  1321. Series.prototype.drawShadow = function(sctx, opts, plot) {
  1322. var options = (opts == undefined) ? {} : opts;
  1323. sctx = (sctx == undefined) ? this.shadowCanvas._ctx : sctx;
  1324. var j, data, gridData;
  1325. // hooks get called even if series not shown
  1326. // we don't clear canvas here, it would wipe out all other series as well.
  1327. for (j=0; j<$.jqplot.preDrawSeriesShadowHooks.length; j++) {
  1328. $.jqplot.preDrawSeriesShadowHooks[j].call(this, sctx, options);
  1329. }
  1330. if (this.shadow) {
  1331. this.renderer.setGridData.call(this, plot);
  1332. data = [];
  1333. if (options.data) {
  1334. data = options.data;
  1335. }
  1336. else if (!this._stack) {
  1337. data = this.data;
  1338. }
  1339. else {
  1340. data = this._plotData;
  1341. }
  1342. gridData = options.gridData || this.renderer.makeGridData.call(this, data, plot);
  1343. this.renderer.drawShadow.call(this, sctx, gridData, options, plot);
  1344. }
  1345. for (j=0; j<$.jqplot.postDrawSeriesShadowHooks.length; j++) {
  1346. $.jqplot.postDrawSeriesShadowHooks[j].call(this, sctx, options);
  1347. }
  1348. sctx = opts = plot = j = data = gridData = null;
  1349. };
  1350. // toggles series display on plot, e.g. show/hide series
  1351. Series.prototype.toggleDisplay = function(ev, callback) {
  1352. var s, speed;
  1353. if (ev.data.series) {
  1354. s = ev.data.series;
  1355. }
  1356. else {
  1357. s = this;
  1358. }
  1359. if (ev.data.speed) {
  1360. speed = ev.data.speed;
  1361. }
  1362. if (speed) {
  1363. // this can be tricky because series may not have a canvas element if replotting.
  1364. if (s.canvas._elem.is(':hidden') || !s.show) {
  1365. s.show = true;
  1366. s.canvas._elem.removeClass('jqplot-series-hidden');
  1367. if (s.shadowCanvas._elem) {
  1368. s.shadowCanvas._elem.fadeIn(speed);
  1369. }
  1370. s.canvas._elem.fadeIn(speed, callback);
  1371. s.canvas._elem.nextAll('.jqplot-point-label.jqplot-series-'+s.index).fadeIn(speed);
  1372. }
  1373. else {
  1374. s.show = false;
  1375. s.canvas._elem.addClass('jqplot-series-hidden');
  1376. if (s.shadowCanvas._elem) {
  1377. s.shadowCanvas._elem.fadeOut(speed);
  1378. }
  1379. s.canvas._elem.fadeOut(speed, callback);
  1380. s.canvas._elem.nextAll('.jqplot-point-label.jqplot-series-'+s.index).fadeOut(speed);
  1381. }
  1382. }
  1383. else {
  1384. // this can be tricky because series may not have a canvas element if replotting.
  1385. if (s.canvas._elem.is(':hidden') || !s.show) {
  1386. s.show = true;
  1387. s.canvas._elem.removeClass('jqplot-series-hidden');
  1388. if (s.shadowCanvas._elem) {
  1389. s.shadowCanvas._elem.show();
  1390. }
  1391. s.canvas._elem.show(0, callback);
  1392. s.canvas._elem.nextAll('.jqplot-point-label.jqplot-series-'+s.index).show();
  1393. }
  1394. else {
  1395. s.show = false;
  1396. s.canvas._elem.addClass('jqplot-series-hidden');
  1397. if (s.shadowCanvas._elem) {
  1398. s.shadowCanvas._elem.hide();
  1399. }
  1400. s.canvas._elem.hide(0, callback);
  1401. s.canvas._elem.nextAll('.jqplot-point-label.jqplot-series-'+s.index).hide();
  1402. }
  1403. }
  1404. };
  1405. /**
  1406. * Class: Grid
  1407. *
  1408. * Object representing the grid on which the plot is drawn. The grid in this
  1409. * context is the area bounded by the axes, the area which will contain the series.
  1410. * Note, the series are drawn on their own canvas.
  1411. * The Grid object cannot be instantiated directly, but is created by the Plot object.
  1412. * Grid properties can be set or overridden by the options passed in from the user.
  1413. */
  1414. function Grid() {
  1415. $.jqplot.ElemContainer.call(this);
  1416. // Group: Properties
  1417. // prop: drawGridlines
  1418. // whether to draw the gridlines on the plot.
  1419. this.drawGridlines = true;
  1420. // prop: gridLineColor
  1421. // color of the grid lines.
  1422. this.gridLineColor = '#cccccc';
  1423. // prop: gridLineWidth
  1424. // width of the grid lines.
  1425. this.gridLineWidth = 1.0;
  1426. // prop: background
  1427. // css spec for the background color.
  1428. this.background = '#fffdf6';
  1429. // prop: borderColor
  1430. // css spec for the color of the grid border.
  1431. this.borderColor = '#999999';
  1432. // prop: borderWidth
  1433. // width of the border in pixels.
  1434. this.borderWidth = 2.0;
  1435. // prop: drawBorder
  1436. // True to draw border around grid.
  1437. this.drawBorder = true;
  1438. // prop: shadow
  1439. // whether to show a shadow behind the grid.
  1440. this.shadow = true;
  1441. // prop: shadowAngle
  1442. // shadow angle in degrees
  1443. this.shadowAngle = 45;
  1444. // prop: shadowOffset
  1445. // Offset of each shadow stroke from the border in pixels
  1446. this.shadowOffset = 1.5;
  1447. // prop: shadowWidth
  1448. // width of the stoke for the shadow
  1449. this.shadowWidth = 3;
  1450. // prop: shadowDepth
  1451. // Number of times shadow is stroked, each stroke offset shadowOffset from the last.
  1452. this.shadowDepth = 3;
  1453. // prop: shadowColor
  1454. // an optional css color spec for the shadow in 'rgba(n, n, n, n)' form
  1455. this.shadowColor = null;
  1456. // prop: shadowAlpha
  1457. // Alpha channel transparency of shadow. 0 = transparent.
  1458. this.shadowAlpha = '0.07';
  1459. this._left;
  1460. this._top;
  1461. this._right;
  1462. this._bottom;
  1463. this._width;
  1464. this._height;
  1465. this._axes = [];
  1466. // prop: renderer
  1467. // Instance of a renderer which will actually render the grid,
  1468. // see <$.jqplot.CanvasGridRenderer>.
  1469. this.renderer = $.jqplot.CanvasGridRenderer;
  1470. // prop: rendererOptions
  1471. // Options to pass on to the renderer,
  1472. // see <$.jqplot.CanvasGridRenderer>.
  1473. this.rendererOptions = {};
  1474. this._offsets = {top:null, bottom:null, left:null, right:null};
  1475. }
  1476. Grid.prototype = new $.jqplot.ElemContainer();
  1477. Grid.prototype.constructor = Grid;
  1478. Grid.prototype.init = function() {
  1479. if ($.isFunction(this.renderer)) {
  1480. this.renderer = new this.renderer();
  1481. }
  1482. this.renderer.init.call(this, this.rendererOptions);
  1483. };
  1484. Grid.prototype.createElement = function(offsets,plot) {
  1485. this._offsets = offsets;
  1486. return this.renderer.createElement.call(this, plot);
  1487. };
  1488. Grid.prototype.draw = function() {
  1489. this.renderer.draw.call(this);
  1490. };
  1491. $.jqplot.GenericCanvas = function() {
  1492. $.jqplot.ElemContainer.call(this);
  1493. this._ctx;
  1494. };
  1495. $.jqplot.GenericCanvas.prototype = new $.jqplot.ElemContainer();
  1496. $.jqplot.GenericCanvas.prototype.constructor = $.jqplot.GenericCanvas;
  1497. $.jqplot.GenericCanvas.prototype.createElement = function(offsets, clss, plotDimensions, plot) {
  1498. this._offsets = offsets;
  1499. var klass = 'jqplot';
  1500. if (clss != undefined) {
  1501. klass = clss;
  1502. }
  1503. var elem;
  1504. elem = plot.canvasManager.getCanvas();
  1505. // if new plotDimensions supplied, use them.
  1506. if (plotDimensions != null) {
  1507. this._plotDimensions = plotDimensions;
  1508. }
  1509. elem.width = this._plotDimensions.width - this._offsets.left - this._offsets.right;
  1510. elem.height = this._plotDimensions.height - this._offsets.top - this._offsets.bottom;
  1511. this._elem = $(elem);
  1512. this._elem.css({ position: 'absolute', left: this._offsets.left, top: this._offsets.top });
  1513. this._elem.addClass(klass);
  1514. elem = plot.canvasManager.initCanvas(elem);
  1515. elem = null;
  1516. return this._elem;
  1517. };
  1518. $.jqplot.GenericCanvas.prototype.setContext = function() {
  1519. this._ctx = this._elem.get(0).getContext("2d");
  1520. return this._ctx;
  1521. };
  1522. // Memory Leaks patch
  1523. $.jqplot.GenericCanvas.prototype.resetCanvas = function() {
  1524. if (this._elem) {
  1525. if ($.jqplot.use_excanvas && window.G_vmlCanvasManager.uninitElement !== undefined) {
  1526. window.G_vmlCanvasManager.uninitElement(this._elem.get(0));
  1527. }
  1528. //this._elem.remove();
  1529. this._elem.emptyForce();
  1530. }
  1531. this._ctx = null;
  1532. };
  1533. $.jqplot.HooksManager = function () {
  1534. this.hooks =[];
  1535. this.args = [];
  1536. };
  1537. $.jqplot.HooksManager.prototype.addOnce = function(fn, args) {
  1538. args = args || [];
  1539. var havehook = false;
  1540. for (var i=0, l=this.hooks.length; i<l; i++) {
  1541. if (this.hooks[i] == fn) {
  1542. havehook = true;
  1543. }
  1544. }
  1545. if (!havehook) {
  1546. this.hooks.push(fn);
  1547. this.args.push(args);
  1548. }
  1549. };
  1550. $.jqplot.HooksManager.prototype.add = function(fn, args) {
  1551. args = args || [];
  1552. this.hooks.push(fn);
  1553. this.args.push(args);
  1554. };
  1555. $.jqplot.EventListenerManager = function () {
  1556. this.hooks =[];
  1557. };
  1558. $.jqplot.EventListenerManager.prototype.addOnce = function(ev, fn) {
  1559. var havehook = false, h, i;
  1560. for (var i=0, l=this.hooks.length; i<l; i++) {
  1561. h = this.hooks[i];
  1562. if (h[0] == ev && h[1] == fn) {
  1563. havehook = true;
  1564. }
  1565. }
  1566. if (!havehook) {
  1567. this.hooks.push([ev, fn]);
  1568. }
  1569. };
  1570. $.jqplot.EventListenerManager.prototype.add = function(ev, fn) {
  1571. this.hooks.push([ev, fn]);
  1572. };
  1573. var _axisNames = ['yMidAxis', 'xaxis', 'yaxis', 'x2axis', 'y2axis', 'y3axis', 'y4axis', 'y5axis', 'y6axis', 'y7axis', 'y8axis', 'y9axis'];
  1574. /**
  1575. * Class: jqPlot
  1576. * Plot object returned by call to $.jqplot. Handles parsing user options,
  1577. * creating sub objects (Axes, legend, title, series) and rendering the plot.
  1578. */
  1579. function jqPlot() {
  1580. // Group: Properties
  1581. // These properties are specified at the top of the options object
  1582. // like so:
  1583. // > {
  1584. // > axesDefaults:{min:0},
  1585. // > series:[{color:'#6633dd'}],
  1586. // > title: 'A Plot'
  1587. // > }
  1588. //
  1589. // prop: animate
  1590. // True to animate the series on initial plot draw (renderer dependent).
  1591. // Actual animation functionality must be supported in the renderer.
  1592. this.animate = false;
  1593. // prop: animateReplot
  1594. // True to animate series after a call to the replot() method.
  1595. // Use with caution! Replots can happen very frequently under
  1596. // certain circumstances (e.g. resizing, dragging points) and
  1597. // animation in these situations can cause problems.
  1598. this.animateReplot = false;
  1599. // prop: axes
  1600. // up to 4 axes are supported, each with its own options,
  1601. // See <Axis> for axis specific options.
  1602. this.axes = {xaxis: new Axis('xaxis'), yaxis: new Axis('yaxis'), x2axis: new Axis('x2axis'), y2axis: new Axis('y2axis'), y3axis: new Axis('y3axis'), y4axis: new Axis('y4axis'), y5axis: new Axis('y5axis'), y6axis: new Axis('y6axis'), y7axis: new Axis('y7axis'), y8axis: new Axis('y8axis'), y9axis: new Axis('y9axis'), yMidAxis: new Axis('yMidAxis')};
  1603. this.baseCanvas = new $.jqplot.GenericCanvas();
  1604. // true to intercept right click events and fire a 'jqplotRightClick' event.
  1605. // this will also block the context menu.
  1606. this.captureRightClick = false;
  1607. // prop: data
  1608. // user's data. Data should *NOT* be specified in the options object,
  1609. // but be passed in as the second argument to the $.jqplot() function.
  1610. // The data property is described here soley for reference.
  1611. // The data should be in the form of an array of 2D or 1D arrays like
  1612. // > [ [[x1, y1], [x2, y2],...], [y1, y2, ...] ].
  1613. this.data = [];
  1614. // prop: dataRenderer
  1615. // A callable which can be used to preprocess data passed into the plot.
  1616. // Will be called with 3 arguments: the plot data, a reference to the plot,
  1617. // and the value of dataRendererOptions.
  1618. this.dataRenderer;
  1619. // prop: dataRendererOptions
  1620. // Options that will be passed to the dataRenderer.
  1621. // Can be of any type.
  1622. this.dataRendererOptions;
  1623. this.defaults = {
  1624. // prop: axesDefaults
  1625. // default options that will be applied to all axes.
  1626. // see <Axis> for axes options.
  1627. axesDefaults: {},
  1628. axes: {xaxis:{}, yaxis:{}, x2axis:{}, y2axis:{}, y3axis:{}, y4axis:{}, y5axis:{}, y6axis:{}, y7axis:{}, y8axis:{}, y9axis:{}, yMidAxis:{}},
  1629. // prop: seriesDefaults
  1630. // default options that will be applied to all series.
  1631. // see <Series> for series options.
  1632. seriesDefaults: {},
  1633. series:[]
  1634. };
  1635. // prop: defaultAxisStart
  1636. // 1-D data series are internally converted into 2-D [x,y] data point arrays
  1637. // by jqPlot. This is the default starting value for the missing x or y value.
  1638. // The added data will be a monotonically increasing series (e.g. [1, 2, 3, ...])
  1639. // starting at this value.
  1640. this.defaultAxisStart = 1;
  1641. // this.doCustomEventBinding = true;
  1642. // prop: drawIfHidden
  1643. // True to execute the draw method even if the plot target is hidden.
  1644. // Generally, this should be false. Most plot elements will not be sized/
  1645. // positioned correclty if renderered into a hidden container. To render into
  1646. // a hidden container, call the replot method when the container is shown.
  1647. this.drawIfHidden = false;
  1648. this.eventCanvas = new $.jqplot.GenericCanvas();
  1649. // prop: fillBetween
  1650. // Fill between 2 line series in a plot.
  1651. // Options object:
  1652. // {
  1653. // series1: first index (0 based) of series in fill
  1654. // series2: second index (0 based) of series in fill
  1655. // color: color of fill [default fillColor of series1]
  1656. // baseSeries: fill will be drawn below this series (0 based index)
  1657. // fill: false to turn off fill [default true].
  1658. // }
  1659. this.fillBetween = {
  1660. series1: null,
  1661. series2: null,
  1662. color: null,
  1663. baseSeries: 0,
  1664. fill: true
  1665. };
  1666. // prop; fontFamily
  1667. // css spec for the font-family attribute. Default for the entire plot.
  1668. this.fontFamily;
  1669. // prop: fontSize
  1670. // css spec for the font-size attribute. Default for the entire plot.
  1671. this.fontSize;
  1672. // prop: grid
  1673. // See <Grid> for grid specific options.
  1674. this.grid = new Grid();
  1675. // prop: legend
  1676. // see <$.jqplot.TableLegendRenderer>
  1677. this.legend = new Legend();
  1678. // prop: noDataIndicator
  1679. // Options to set up a mock plot with a data loading indicator if no data is specified.
  1680. this.noDataIndicator = {
  1681. show: false,
  1682. indicator: 'Loading Data...',
  1683. axes: {
  1684. xaxis: {
  1685. min: 0,
  1686. max: 10,
  1687. tickInterval: 2,
  1688. show: true
  1689. },
  1690. yaxis: {
  1691. min: 0,
  1692. max: 12,
  1693. tickInterval: 3,
  1694. show: true
  1695. }
  1696. }
  1697. };
  1698. // prop: negativeSeriesColors
  1699. // colors to use for portions of the line below zero.
  1700. this.negativeSeriesColors = $.jqplot.config.defaultNegativeColors;
  1701. // container to hold all of the merged options. Convienence for plugins.
  1702. this.options = {};
  1703. this.previousSeriesStack = [];
  1704. // Namespace to hold plugins. Generally non-renderer plugins add themselves to here.
  1705. this.plugins = {};
  1706. // prop: series
  1707. // Array of series object options.
  1708. // see <Series> for series specific options.
  1709. this.series = [];
  1710. // array of series indices. Keep track of order
  1711. // which series canvases are displayed, lowest
  1712. // to highest, back to front.
  1713. this.seriesStack = [];
  1714. // prop: seriesColors
  1715. // Ann array of CSS color specifications that will be applied, in order,
  1716. // to the series in the plot. Colors will wrap around so, if their
  1717. // are more series than colors, colors will be reused starting at the
  1718. // beginning. For pie charts, this specifies the colors of the slices.
  1719. this.seriesColors = $.jqplot.config.defaultColors;
  1720. // prop: sortData
  1721. // false to not sort the data passed in by the user.
  1722. // Many bar, stacked and other graphs as well as many plugins depend on
  1723. // having sorted data.
  1724. this.sortData = true;
  1725. // prop: stackSeries
  1726. // true or false, creates a stack or "mountain" plot.
  1727. // Not all series renderers may implement this option.
  1728. this.stackSeries = false;
  1729. // a shortcut for axis syncTicks options. Not implemented yet.
  1730. this.syncXTicks = true;
  1731. // a shortcut for axis syncTicks options. Not implemented yet.
  1732. this.syncYTicks = true;
  1733. // the jquery object for the dom target.
  1734. this.target = null;
  1735. // The id of the dom element to render the plot into
  1736. this.targetId = null;
  1737. // prop textColor
  1738. // css spec for the css color attribute. Default for the entire plot.
  1739. this.textColor;
  1740. // prop: title
  1741. // Title object. See <Title> for specific options. As a shortcut, you
  1742. // can specify the title option as just a string like: title: 'My Plot'
  1743. // and this will create a new title object with the specified text.
  1744. this.title = new Title();
  1745. // Count how many times the draw method has been called while the plot is visible.
  1746. // Mostly used to test if plot has never been dran (=0), has been successfully drawn
  1747. // into a visible container once (=1) or draw more than once into a visible container.
  1748. // Can use this in tests to see if plot has been visibly drawn at least one time.
  1749. // After plot has been visibly drawn once, it generally doesn't need redrawing if its
  1750. // container is hidden and shown.
  1751. this._drawCount = 0;
  1752. // sum of y values for all series in plot.
  1753. // used in mekko chart.
  1754. this._sumy = 0;
  1755. this._sumx = 0;
  1756. // array to hold the cumulative stacked series data.
  1757. // used to ajust the individual series data, which won't have access to other
  1758. // series data.
  1759. this._stackData = [];
  1760. // array that holds the data to be plotted. This will be the series data
  1761. // merged with the the appropriate data from _stackData according to the stackAxis.
  1762. this._plotData = [];
  1763. this._width = null;
  1764. this._height = null;
  1765. this._plotDimensions = {height:null, width:null};
  1766. this._gridPadding = {top:null, right:null, bottom:null, left:null};
  1767. this._defaultGridPadding = {top:10, right:10, bottom:23, left:10};
  1768. this._addDomReference = $.jqplot.config.addDomReference;
  1769. this.preInitHooks = new $.jqplot.HooksManager();
  1770. this.postInitHooks = new $.jqplot.HooksManager();
  1771. this.preParseOptionsHooks = new $.jqplot.HooksManager();
  1772. this.postParseOptionsHooks = new $.jqplot.HooksManager();
  1773. this.preDrawHooks = new $.jqplot.HooksManager();
  1774. this.postDrawHooks = new $.jqplot.HooksManager();
  1775. this.preDrawSeriesHooks = new $.jqplot.HooksManager();
  1776. this.postDrawSeriesHooks = new $.jqplot.HooksManager();
  1777. this.preDrawLegendHooks = new $.jqplot.HooksManager();
  1778. this.addLegendRowHooks = new $.jqplot.HooksManager();
  1779. this.preSeriesInitHooks = new $.jqplot.HooksManager();
  1780. this.postSeriesInitHooks = new $.jqplot.HooksManager();
  1781. this.preParseSeriesOptionsHooks = new $.jqplot.HooksManager();
  1782. this.postParseSeriesOptionsHooks = new $.jqplot.HooksManager();
  1783. this.eventListenerHooks = new $.jqplot.EventListenerManager();
  1784. this.preDrawSeriesShadowHooks = new $.jqplot.HooksManager();
  1785. this.postDrawSeriesShadowHooks = new $.jqplot.HooksManager();
  1786. this.colorGenerator = new $.jqplot.ColorGenerator();
  1787. this.negativeColorGenerator = new $.jqplot.ColorGenerator();
  1788. this.canvasManager = new $.jqplot.CanvasManager();
  1789. this.themeEngine = new $.jqplot.ThemeEngine();
  1790. var seriesColorsIndex = 0;
  1791. // Group: methods
  1792. //
  1793. // method: init
  1794. // sets the plot target, checks data and applies user
  1795. // options to plot.
  1796. this.init = function(target, data, options) {
  1797. options = options || {};
  1798. for (var i=0; i<$.jqplot.preInitHooks.length; i++) {
  1799. $.jqplot.preInitHooks[i].call(this, target, data, options);
  1800. }
  1801. for (var i=0; i<this.preInitHooks.hooks.length; i++) {
  1802. this.preInitHooks.hooks[i].call(this, target, data, options);
  1803. }
  1804. this.targetId = '#'+target;
  1805. this.target = $('#'+target);
  1806. //////
  1807. // Add a reference to plot
  1808. //////
  1809. if (this._addDomReference) {
  1810. this.target.data('jqplot', this);
  1811. }
  1812. // remove any error class that may be stuck on target.
  1813. this.target.removeClass('jqplot-error');
  1814. if (!this.target.get(0)) {
  1815. throw new Error("No plot target specified");
  1816. }
  1817. // make sure the target is positioned by some means and set css
  1818. if (this.target.css('position') == 'static') {
  1819. this.target.css('position', 'relative');
  1820. }
  1821. if (!this.target.hasClass('jqplot-target')) {
  1822. this.target.addClass('jqplot-target');
  1823. }
  1824. // if no height or width specified, use a default.
  1825. if (!this.target.height()) {
  1826. var h;
  1827. if (options && options.height) {
  1828. h = parseInt(options.height, 10);
  1829. }
  1830. else if (this.target.attr('data-height')) {
  1831. h = parseInt(this.target.attr('data-height'), 10);
  1832. }
  1833. else {
  1834. h = parseInt($.jqplot.config.defaultHeight, 10);
  1835. }
  1836. this._height = h;
  1837. this.target.css('height', h+'px');
  1838. }
  1839. else {
  1840. this._height = h = this.target.height();
  1841. }
  1842. if (!this.target.width()) {
  1843. var w;
  1844. if (options && options.width) {
  1845. w = parseInt(options.width, 10);
  1846. }
  1847. else if (this.target.attr('data-width')) {
  1848. w = parseInt(this.target.attr('data-width'), 10);
  1849. }
  1850. else {
  1851. w = parseInt($.jqplot.config.defaultWidth, 10);
  1852. }
  1853. this._width = w;
  1854. this.target.css('width', w+'px');
  1855. }
  1856. else {
  1857. this._width = w = this.target.width();
  1858. }
  1859. for (var i=0, l=_axisNames.length; i<l; i++) {
  1860. this.axes[_axisNames[i]] = new Axis(_axisNames[i]);
  1861. }
  1862. this._plotDimensions.height = this._height;
  1863. this._plotDimensions.width = this._width;
  1864. this.grid._plotDimensions = this._plotDimensions;
  1865. this.title._plotDimensions = this._plotDimensions;
  1866. this.baseCanvas._plotDimensions = this._plotDimensions;
  1867. this.eventCanvas._plotDimensions = this._plotDimensions;
  1868. this.legend._plotDimensions = this._plotDimensions;
  1869. if (this._height <=0 || this._width <=0 || !this._height || !this._width) {
  1870. throw new Error("Canvas dimension not set");
  1871. }
  1872. if (options.dataRenderer && $.isFunction(options.dataRenderer)) {
  1873. if (options.dataRendererOptions) {
  1874. this.dataRendererOptions = options.dataRendererOptions;
  1875. }
  1876. this.dataRenderer = options.dataRenderer;
  1877. data = this.dataRenderer(data, this, this.dataRendererOptions);
  1878. }
  1879. if (options.noDataIndicator && $.isPlainObject(options.noDataIndicator)) {
  1880. $.extend(true, this.noDataIndicator, options.noDataIndicator);
  1881. }
  1882. if (data == null || $.isArray(data) == false || data.length == 0 || $.isArray(data[0]) == false || data[0].length == 0) {
  1883. if (this.noDataIndicator.show == false) {
  1884. throw new Error("No data specified");
  1885. }
  1886. else {
  1887. // have to be descructive here in order for plot to not try and render series.
  1888. // This means that $.jqplot() will have to be called again when there is data.
  1889. //delete options.series;
  1890. for (var ax in this.noDataIndicator.axes) {
  1891. for (var prop in this.noDataIndicator.axes[ax]) {
  1892. this.axes[ax][prop] = this.noDataIndicator.axes[ax][prop];
  1893. }
  1894. }
  1895. this.postDrawHooks.add(function() {
  1896. var eh = this.eventCanvas.getHeight();
  1897. var ew = this.eventCanvas.getWidth();
  1898. var temp = $('<div class="jqplot-noData-container" style="position:absolute;"></div>');
  1899. this.target.append(temp);
  1900. temp.height(eh);
  1901. temp.width(ew);
  1902. temp.css('top', this.eventCanvas._offsets.top);
  1903. temp.css('left', this.eventCanvas._offsets.left);
  1904. var temp2 = $('<div class="jqplot-noData-contents" style="text-align:center; position:relative; margin-left:auto; margin-right:auto;"></div>');
  1905. temp.append(temp2);
  1906. temp2.html(this.noDataIndicator.indicator);
  1907. var th = temp2.height();
  1908. var tw = temp2.width();
  1909. temp2.height(th);
  1910. temp2.width(tw);
  1911. temp2.css('top', (eh - th)/2 + 'px');
  1912. });
  1913. }
  1914. }
  1915. // make a copy of the data
  1916. this.data = $.extend(true, [], data);
  1917. this.parseOptions(options);
  1918. if (this.textColor) {
  1919. this.target.css('color', this.textColor);
  1920. }
  1921. if (this.fontFamily) {
  1922. this.target.css('font-family', this.fontFamily);
  1923. }
  1924. if (this.fontSize) {
  1925. this.target.css('font-size', this.fontSize);
  1926. }
  1927. this.title.init();
  1928. this.legend.init();
  1929. this._sumy = 0;
  1930. this._sumx = 0;
  1931. this.computePlotData();
  1932. for (var i=0; i<this.series.length; i++) {
  1933. // set default stacking order for series canvases
  1934. this.seriesStack.push(i);
  1935. this.previousSeriesStack.push(i);
  1936. this.series[i].shadowCanvas._plotDimensions = this._plotDimensions;
  1937. this.series[i].canvas._plotDimensions = this._plotDimensions;
  1938. for (var j=0; j<$.jqplot.preSeriesInitHooks.length; j++) {
  1939. $.jqplot.preSeriesInitHooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this);
  1940. }
  1941. for (var j=0; j<this.preSeriesInitHooks.hooks.length; j++) {
  1942. this.preSeriesInitHooks.hooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this);
  1943. }
  1944. // this.populatePlotData(this.series[i], i);
  1945. this.series[i]._plotDimensions = this._plotDimensions;
  1946. this.series[i].init(i, this.grid.borderWidth, this);
  1947. for (var j=0; j<$.jqplot.postSeriesInitHooks.length; j++) {
  1948. $.jqplot.postSeriesInitHooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this);
  1949. }
  1950. for (var j=0; j<this.postSeriesInitHooks.hooks.length; j++) {
  1951. this.postSeriesInitHooks.hooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this);
  1952. }
  1953. this._sumy += this.series[i]._sumy;
  1954. this._sumx += this.series[i]._sumx;
  1955. }
  1956. var name,
  1957. axis;
  1958. for (var i=0, l=_axisNames.length; i<l; i++) {
  1959. name = _axisNames[i];
  1960. axis = this.axes[name];
  1961. axis._plotDimensions = this._plotDimensions;
  1962. axis.init();
  1963. if (this.axes[name].borderColor == null) {
  1964. if (name.charAt(0) !== 'x' && axis.useSeriesColor === true && axis.show) {
  1965. axis.borderColor = axis._series[0].color;
  1966. }
  1967. else {
  1968. axis.borderColor = this.grid.borderColor;
  1969. }
  1970. }
  1971. }
  1972. if (this.sortData) {
  1973. sortData(this.series);
  1974. }
  1975. this.grid.init();
  1976. this.grid._axes = this.axes;
  1977. this.legend._series = this.series;
  1978. for (var i=0; i<$.jqplot.postInitHooks.length; i++) {
  1979. $.jqplot.postInitHooks[i].call(this, target, this.data, options);
  1980. }
  1981. for (var i=0; i<this.postInitHooks.hooks.length; i++) {
  1982. this.postInitHooks.hooks[i].call(this, target, this.data, options);
  1983. }
  1984. };
  1985. // method: resetAxesScale
  1986. // Reset the specified axes min, max, numberTicks and tickInterval properties to null
  1987. // or reset these properties on all axes if no list of axes is provided.
  1988. //
  1989. // Parameters:
  1990. // axes - Boolean to reset or not reset all axes or an array or object of axis names to reset.
  1991. this.resetAxesScale = function(axes, options) {
  1992. var opts = options || {};
  1993. var ax = axes || this.axes;
  1994. if (ax === true) {
  1995. ax = this.axes;
  1996. }
  1997. if ($.isArray(ax)) {
  1998. for (var i = 0; i < ax.length; i++) {
  1999. this.axes[ax[i]].resetScale(opts[ax[i]]);
  2000. }
  2001. }
  2002. else if (typeof(ax) === 'object') {
  2003. for (var name in ax) {
  2004. this.axes[name].resetScale(opts[name]);
  2005. }
  2006. }
  2007. };
  2008. // method: reInitialize
  2009. // reinitialize plot for replotting.
  2010. // not called directly.
  2011. this.reInitialize = function (data, opts) {
  2012. // Plot should be visible and have a height and width.
  2013. // If plot doesn't have height and width for some
  2014. // reason, set it by other means. Plot must not have
  2015. // a display:none attribute, however.
  2016. var options = $.extend(true, {}, this.options, opts);
  2017. var target = this.targetId.substr(1);
  2018. var tdata = (data == null) ? this.data : data;
  2019. for (var i=0; i<$.jqplot.preInitHooks.length; i++) {
  2020. $.jqplot.preInitHooks[i].call(this, target, tdata, options);
  2021. }
  2022. for (var i=0; i<this.preInitHooks.hooks.length; i++) {
  2023. this.preInitHooks.hooks[i].call(this, target, tdata, options);
  2024. }
  2025. this._height = this.target.height();
  2026. this._width = this.target.width();
  2027. if (this._height <=0 || this._width <=0 || !this._height || !this._width) {
  2028. throw new Error("Target dimension not set");
  2029. }
  2030. this._plotDimensions.height = this._height;
  2031. this._plotDimensions.width = this._width;
  2032. this.grid._plotDimensions = this._plotDimensions;
  2033. this.title._plotDimensions = this._plotDimensions;
  2034. this.baseCanvas._plotDimensions = this._plotDimensions;
  2035. this.eventCanvas._plotDimensions = this._plotDimensions;
  2036. this.legend._plotDimensions = this._plotDimensions;
  2037. var name,
  2038. t,
  2039. j,
  2040. axis;
  2041. for (var i=0, l=_axisNames.length; i<l; i++) {
  2042. name = _axisNames[i];
  2043. axis = this.axes[name];
  2044. // Memory Leaks patch : clear ticks elements
  2045. t = axis._ticks;
  2046. for (var j = 0, tlen = t.length; j < tlen; j++) {
  2047. var el = t[j]._elem;
  2048. if (el) {
  2049. // if canvas renderer
  2050. if ($.jqplot.use_excanvas && window.G_vmlCanvasManager.uninitElement !== undefined) {
  2051. window.G_vmlCanvasManager.uninitElement(el.get(0));
  2052. }
  2053. el.emptyForce();
  2054. el = null;
  2055. t._elem = null;
  2056. }
  2057. }
  2058. t = null;
  2059. delete axis.ticks;
  2060. delete axis._ticks;
  2061. this.axes[name] = new Axis(name);
  2062. this.axes[name]._plotWidth = this._width;
  2063. this.axes[name]._plotHeight = this._height;
  2064. }
  2065. if (data) {
  2066. if (options.dataRenderer && $.isFunction(options.dataRenderer)) {
  2067. if (options.dataRendererOptions) {
  2068. this.dataRendererOptions = options.dataRendererOptions;
  2069. }
  2070. this.dataRenderer = options.dataRenderer;
  2071. data = this.dataRenderer(data, this, this.dataRendererOptions);
  2072. }
  2073. // make a copy of the data
  2074. this.data = $.extend(true, [], data);
  2075. }
  2076. if (opts) {
  2077. this.parseOptions(options);
  2078. }
  2079. this.title._plotWidth = this._width;
  2080. if (this.textColor) {
  2081. this.target.css('color', this.textColor);
  2082. }
  2083. if (this.fontFamily) {
  2084. this.target.css('font-family', this.fontFamily);
  2085. }
  2086. if (this.fontSize) {
  2087. this.target.css('font-size', this.fontSize);
  2088. }
  2089. this.title.init();
  2090. this.legend.init();
  2091. this._sumy = 0;
  2092. this._sumx = 0;
  2093. this.seriesStack = [];
  2094. this.previousSeriesStack = [];
  2095. this.computePlotData();
  2096. for (var i=0, l=this.series.length; i<l; i++) {
  2097. // set default stacking order for series canvases
  2098. this.seriesStack.push(i);
  2099. this.previousSeriesStack.push(i);
  2100. this.series[i].shadowCanvas._plotDimensions = this._plotDimensions;
  2101. this.series[i].canvas._plotDimensions = this._plotDimensions;
  2102. for (var j=0; j<$.jqplot.preSeriesInitHooks.length; j++) {
  2103. $.jqplot.preSeriesInitHooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this);
  2104. }
  2105. for (var j=0; j<this.preSeriesInitHooks.hooks.length; j++) {
  2106. this.preSeriesInitHooks.hooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this);
  2107. }
  2108. // this.populatePlotData(this.series[i], i);
  2109. this.series[i]._plotDimensions = this._plotDimensions;
  2110. this.series[i].init(i, this.grid.borderWidth, this);
  2111. for (var j=0; j<$.jqplot.postSeriesInitHooks.length; j++) {
  2112. $.jqplot.postSeriesInitHooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this);
  2113. }
  2114. for (var j=0; j<this.postSeriesInitHooks.hooks.length; j++) {
  2115. this.postSeriesInitHooks.hooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this);
  2116. }
  2117. this._sumy += this.series[i]._sumy;
  2118. this._sumx += this.series[i]._sumx;
  2119. }
  2120. for (var i=0, l=_axisNames.length; i<l; i++) {
  2121. name = _axisNames[i];
  2122. axis = this.axes[name];
  2123. axis._plotDimensions = this._plotDimensions;
  2124. axis.init();
  2125. if (axis.borderColor == null) {
  2126. if (name.charAt(0) !== 'x' && axis.useSeriesColor === true && axis.show) {
  2127. axis.borderColor = axis._series[0].color;
  2128. }
  2129. else {
  2130. axis.borderColor = this.grid.borderColor;
  2131. }
  2132. }
  2133. }
  2134. if (this.sortData) {
  2135. sortData(this.series);
  2136. }
  2137. this.grid.init();
  2138. this.grid._axes = this.axes;
  2139. this.legend._series = this.series;
  2140. for (var i=0, l=$.jqplot.postInitHooks.length; i<l; i++) {
  2141. $.jqplot.postInitHooks[i].call(this, target, this.data, options);
  2142. }
  2143. for (var i=0, l=this.postInitHooks.hooks.length; i<l; i++) {
  2144. this.postInitHooks.hooks[i].call(this, target, this.data, options);
  2145. }
  2146. };
  2147. // method: quickInit
  2148. //
  2149. // Quick reinitialization plot for replotting.
  2150. // Does not parse options ore recreate axes and series.
  2151. // not called directly.
  2152. this.quickInit = function () {
  2153. // Plot should be visible and have a height and width.
  2154. // If plot doesn't have height and width for some
  2155. // reason, set it by other means. Plot must not have
  2156. // a display:none attribute, however.
  2157. this._height = this.target.height();
  2158. this._width = this.target.width();
  2159. if (this._height <=0 || this._width <=0 || !this._height || !this._width) {
  2160. throw new Error("Target dimension not set");
  2161. }
  2162. this._plotDimensions.height = this._height;
  2163. this._plotDimensions.width = this._width;
  2164. this.grid._plotDimensions = this._plotDimensions;
  2165. this.title._plotDimensions = this._plotDimensions;
  2166. this.baseCanvas._plotDimensions = this._plotDimensions;
  2167. this.eventCanvas._plotDimensions = this._plotDimensions;
  2168. this.legend._plotDimensions = this._plotDimensions;
  2169. for (var n in this.axes) {
  2170. this.axes[n]._plotWidth = this._width;
  2171. this.axes[n]._plotHeight = this._height;
  2172. }
  2173. this.title._plotWidth = this._width;
  2174. if (this.textColor) {
  2175. this.target.css('color', this.textColor);
  2176. }
  2177. if (this.fontFamily) {
  2178. this.target.css('font-family', this.fontFamily);
  2179. }
  2180. if (this.fontSize) {
  2181. this.target.css('font-size', this.fontSize);
  2182. }
  2183. this._sumy = 0;
  2184. this._sumx = 0;
  2185. this.computePlotData();
  2186. for (var i=0; i<this.series.length; i++) {
  2187. // this.populatePlotData(this.series[i], i);
  2188. if (this.series[i]._type === 'line' && this.series[i].renderer.bands.show) {
  2189. this.series[i].renderer.initBands.call(this.series[i], this.series[i].renderer.options, this);
  2190. }
  2191. this.series[i]._plotDimensions = this._plotDimensions;
  2192. this.series[i].canvas._plotDimensions = this._plotDimensions;
  2193. //this.series[i].init(i, this.grid.borderWidth);
  2194. this._sumy += this.series[i]._sumy;
  2195. this._sumx += this.series[i]._sumx;
  2196. }
  2197. var name;
  2198. for (var j=0; j<12; j++) {
  2199. name = _axisNames[j];
  2200. // Memory Leaks patch : clear ticks elements
  2201. var t = this.axes[name]._ticks;
  2202. for (var i = 0; i < t.length; i++) {
  2203. var el = t[i]._elem;
  2204. if (el) {
  2205. // if canvas renderer
  2206. if ($.jqplot.use_excanvas && window.G_vmlCanvasManager.uninitElement !== undefined) {
  2207. window.G_vmlCanvasManager.uninitElement(el.get(0));
  2208. }
  2209. el.emptyForce();
  2210. el = null;
  2211. t._elem = null;
  2212. }
  2213. }
  2214. t = null;
  2215. this.axes[name]._plotDimensions = this._plotDimensions;
  2216. this.axes[name]._ticks = [];
  2217. // this.axes[name].renderer.init.call(this.axes[name], {});
  2218. }
  2219. if (this.sortData) {
  2220. sortData(this.series);
  2221. }
  2222. this.grid._axes = this.axes;
  2223. this.legend._series = this.series;
  2224. };
  2225. // sort the series data in increasing order.
  2226. function sortData(series) {
  2227. var d, sd, pd, ppd, ret;
  2228. for (var i=0; i<series.length; i++) {
  2229. var check;
  2230. var bat = [series[i].data, series[i]._stackData, series[i]._plotData, series[i]._prevPlotData];
  2231. for (var n=0; n<4; n++) {
  2232. check = true;
  2233. d = bat[n];
  2234. if (series[i]._stackAxis == 'x') {
  2235. for (var j = 0; j < d.length; j++) {
  2236. if (typeof(d[j][1]) != "number") {
  2237. check = false;
  2238. break;
  2239. }
  2240. }
  2241. if (check) {
  2242. d.sort(function(a,b) { return a[1] - b[1]; });
  2243. }
  2244. }
  2245. else {
  2246. for (var j = 0; j < d.length; j++) {
  2247. if (typeof(d[j][0]) != "number") {
  2248. check = false;
  2249. break;
  2250. }
  2251. }
  2252. if (check) {
  2253. d.sort(function(a,b) { return a[0] - b[0]; });
  2254. }
  2255. }
  2256. }
  2257. }
  2258. }
  2259. this.computePlotData = function() {
  2260. this._plotData = [];
  2261. this._stackData = [];
  2262. var series,
  2263. index,
  2264. l;
  2265. for (index=0, l=this.series.length; index<l; index++) {
  2266. series = this.series[index];
  2267. this._plotData.push([]);
  2268. this._stackData.push([]);
  2269. var cd = series.data;
  2270. this._plotData[index] = $.extend(true, [], cd);
  2271. this._stackData[index] = $.extend(true, [], cd);
  2272. series._plotData = this._plotData[index];
  2273. series._stackData = this._stackData[index];
  2274. var plotValues = {x:[], y:[]};
  2275. if (this.stackSeries && !series.disableStack) {
  2276. series._stack = true;
  2277. ///////////////////////////
  2278. // have to check for nulls
  2279. ///////////////////////////
  2280. var sidx = (series._stackAxis === 'x') ? 0 : 1;
  2281. for (var k=0, cdl=cd.length; k<cdl; k++) {
  2282. var temp = cd[k][sidx];
  2283. if (temp == null) {
  2284. temp = 0;
  2285. }
  2286. this._plotData[index][k][sidx] = temp;
  2287. this._stackData[index][k][sidx] = temp;
  2288. if (index > 0) {
  2289. for (var j=index; j--;) {
  2290. var prevval = this._plotData[j][k][sidx];
  2291. // only need to sum up the stack axis column of data
  2292. // and only sum if it is of same sign.
  2293. // if previous series isn't same sign, keep looking
  2294. // at earlier series untill we find one of same sign.
  2295. if (temp * prevval >= 0) {
  2296. this._plotData[index][k][sidx] += prevval;
  2297. this._stackData[index][k][sidx] += prevval;
  2298. break;
  2299. }
  2300. }
  2301. }
  2302. }
  2303. }
  2304. else {
  2305. for (var i=0; i<series.data.length; i++) {
  2306. plotValues.x.push(series.data[i][0]);
  2307. plotValues.y.push(series.data[i][1]);
  2308. }
  2309. this._stackData.push(series.data);
  2310. this.series[index]._stackData = series.data;
  2311. this._plotData.push(series.data);
  2312. series._plotData = series.data;
  2313. series._plotValues = plotValues;
  2314. }
  2315. if (index>0) {
  2316. series._prevPlotData = this.series[index-1]._plotData;
  2317. }
  2318. series._sumy = 0;
  2319. series._sumx = 0;
  2320. for (i=series.data.length-1; i>-1; i--) {
  2321. series._sumy += series.data[i][1];
  2322. series._sumx += series.data[i][0];
  2323. }
  2324. }
  2325. };
  2326. // populate the _stackData and _plotData arrays for the plot and the series.
  2327. this.populatePlotData = function(series, index) {
  2328. // if a stacked chart, compute the stacked data
  2329. this._plotData = [];
  2330. this._stackData = [];
  2331. series._stackData = [];
  2332. series._plotData = [];
  2333. var plotValues = {x:[], y:[]};
  2334. if (this.stackSeries && !series.disableStack) {
  2335. series._stack = true;
  2336. var sidx = (series._stackAxis === 'x') ? 0 : 1;
  2337. // var idx = sidx ? 0 : 1;
  2338. // push the current data into stackData
  2339. //this._stackData.push(this.series[i].data);
  2340. var temp = $.extend(true, [], series.data);
  2341. // create the data that will be plotted for this series
  2342. var plotdata = $.extend(true, [], series.data);
  2343. var tempx, tempy, dval, stackval, comparator;
  2344. // for first series, nothing to add to stackData.
  2345. for (var j=0; j<index; j++) {
  2346. var cd = this.series[j].data;
  2347. for (var k=0; k<cd.length; k++) {
  2348. dval = cd[k];
  2349. tempx = (dval[0] != null) ? dval[0] : 0;
  2350. tempy = (dval[1] != null) ? dval[1] : 0;
  2351. temp[k][0] += tempx;
  2352. temp[k][1] += tempy;
  2353. stackval = (sidx) ? tempy : tempx;
  2354. // only need to sum up the stack axis column of data
  2355. // and only sum if it is of same sign.
  2356. if (series.data[k][sidx] * stackval >= 0) {
  2357. plotdata[k][sidx] += stackval;
  2358. }
  2359. }
  2360. }
  2361. for (var i=0; i<plotdata.length; i++) {
  2362. plotValues.x.push(plotdata[i][0]);
  2363. plotValues.y.push(plotdata[i][1]);
  2364. }
  2365. this._plotData.push(plotdata);
  2366. this._stackData.push(temp);
  2367. series._stackData = temp;
  2368. series._plotData = plotdata;
  2369. series._plotValues = plotValues;
  2370. }
  2371. else {
  2372. for (var i=0; i<series.data.length; i++) {
  2373. plotValues.x.push(series.data[i][0]);
  2374. plotValues.y.push(series.data[i][1]);
  2375. }
  2376. this._stackData.push(series.data);
  2377. this.series[index]._stackData = series.data;
  2378. this._plotData.push(series.data);
  2379. series._plotData = series.data;
  2380. series._plotValues = plotValues;
  2381. }
  2382. if (index>0) {
  2383. series._prevPlotData = this.series[index-1]._plotData;
  2384. }
  2385. series._sumy = 0;
  2386. series._sumx = 0;
  2387. for (i=series.data.length-1; i>-1; i--) {
  2388. series._sumy += series.data[i][1];
  2389. series._sumx += series.data[i][0];
  2390. }
  2391. };
  2392. // function to safely return colors from the color array and wrap around at the end.
  2393. this.getNextSeriesColor = (function(t) {
  2394. var idx = 0;
  2395. var sc = t.seriesColors;
  2396. return function () {
  2397. if (idx < sc.length) {
  2398. return sc[idx++];
  2399. }
  2400. else {
  2401. idx = 0;
  2402. return sc[idx++];
  2403. }
  2404. };
  2405. })(this);
  2406. this.parseOptions = function(options){
  2407. for (var i=0; i<this.preParseOptionsHooks.hooks.length; i++) {
  2408. this.preParseOptionsHooks.hooks[i].call(this, options);
  2409. }
  2410. for (var i=0; i<$.jqplot.preParseOptionsHooks.length; i++) {
  2411. $.jqplot.preParseOptionsHooks[i].call(this, options);
  2412. }
  2413. this.options = $.extend(true, {}, this.defaults, options);
  2414. var opts = this.options;
  2415. this.animate = opts.animate;
  2416. this.animateReplot = opts.animateReplot;
  2417. this.stackSeries = opts.stackSeries;
  2418. if ($.isPlainObject(opts.fillBetween)) {
  2419. var temp = ['series1', 'series2', 'color', 'baseSeries', 'fill'],
  2420. tempi;
  2421. for (var i=0, l=temp.length; i<l; i++) {
  2422. tempi = temp[i];
  2423. if (opts.fillBetween[tempi] != null) {
  2424. this.fillBetween[tempi] = opts.fillBetween[tempi];
  2425. }
  2426. }
  2427. }
  2428. if (opts.seriesColors) {
  2429. this.seriesColors = opts.seriesColors;
  2430. }
  2431. if (opts.negativeSeriesColors) {
  2432. this.negativeSeriesColors = opts.negativeSeriesColors;
  2433. }
  2434. if (opts.captureRightClick) {
  2435. this.captureRightClick = opts.captureRightClick;
  2436. }
  2437. this.defaultAxisStart = (options && options.defaultAxisStart != null) ? options.defaultAxisStart : this.defaultAxisStart;
  2438. this.colorGenerator.setColors(this.seriesColors);
  2439. this.negativeColorGenerator.setColors(this.negativeSeriesColors);
  2440. // var cg = new this.colorGenerator(this.seriesColors);
  2441. // var ncg = new this.colorGenerator(this.negativeSeriesColors);
  2442. // this._gridPadding = this.options.gridPadding;
  2443. $.extend(true, this._gridPadding, opts.gridPadding);
  2444. this.sortData = (opts.sortData != null) ? opts.sortData : this.sortData;
  2445. for (var i=0; i<12; i++) {
  2446. var n = _axisNames[i];
  2447. var axis = this.axes[n];
  2448. axis._options = $.extend(true, {}, opts.axesDefaults, opts.axes[n]);
  2449. $.extend(true, axis, opts.axesDefaults, opts.axes[n]);
  2450. axis._plotWidth = this._width;
  2451. axis._plotHeight = this._height;
  2452. }
  2453. // if (this.data.length == 0) {
  2454. // this.data = [];
  2455. // for (var i=0; i<this.options.series.length; i++) {
  2456. // this.data.push(this.options.series.data);
  2457. // }
  2458. // }
  2459. var normalizeData = function(data, dir, start) {
  2460. // return data as an array of point arrays,
  2461. // in form [[x1,y1...], [x2,y2...], ...]
  2462. var temp = [];
  2463. var i, l;
  2464. dir = dir || 'vertical';
  2465. if (!$.isArray(data[0])) {
  2466. // we have a series of scalars. One line with just y values.
  2467. // turn the scalar list of data into a data array of form:
  2468. // [[1, data[0]], [2, data[1]], ...]
  2469. for (i=0, l=data.length; i<l; i++) {
  2470. if (dir == 'vertical') {
  2471. temp.push([start + i, data[i]]);
  2472. }
  2473. else {
  2474. temp.push([data[i], start+i]);
  2475. }
  2476. }
  2477. }
  2478. else {
  2479. // we have a properly formatted data series, copy it.
  2480. $.extend(true, temp, data);
  2481. }
  2482. return temp;
  2483. };
  2484. var colorIndex = 0;
  2485. this.series = [];
  2486. for (var i=0; i<this.data.length; i++) {
  2487. var sopts = $.extend(true, {index: i}, {seriesColors:this.seriesColors, negativeSeriesColors:this.negativeSeriesColors}, this.options.seriesDefaults, this.options.series[i], {rendererOptions:{animation:{show: this.animate}}});
  2488. // pass in options in case something needs set prior to initialization.
  2489. var temp = new Series(sopts);
  2490. for (var j=0; j<$.jqplot.preParseSeriesOptionsHooks.length; j++) {
  2491. $.jqplot.preParseSeriesOptionsHooks[j].call(temp, this.options.seriesDefaults, this.options.series[i]);
  2492. }
  2493. for (var j=0; j<this.preParseSeriesOptionsHooks.hooks.length; j++) {
  2494. this.preParseSeriesOptionsHooks.hooks[j].call(temp, this.options.seriesDefaults, this.options.series[i]);
  2495. }
  2496. // Now go back and apply the options to the series. Really should just do this during initializaiton, but don't want to
  2497. // mess up preParseSeriesOptionsHooks at this point.
  2498. $.extend(true, temp, sopts);
  2499. var dir = 'vertical';
  2500. if (temp.renderer === $.jqplot.BarRenderer && temp.rendererOptions && temp.rendererOptions.barDirection == 'horizontal') {
  2501. dir = 'horizontal';
  2502. temp._stackAxis = 'x';
  2503. temp._primaryAxis = '_yaxis';
  2504. }
  2505. temp.data = normalizeData(this.data[i], dir, this.defaultAxisStart);
  2506. switch (temp.xaxis) {
  2507. case 'xaxis':
  2508. temp._xaxis = this.axes.xaxis;
  2509. break;
  2510. case 'x2axis':
  2511. temp._xaxis = this.axes.x2axis;
  2512. break;
  2513. default:
  2514. break;
  2515. }
  2516. temp._yaxis = this.axes[temp.yaxis];
  2517. temp._xaxis._series.push(temp);
  2518. temp._yaxis._series.push(temp);
  2519. if (temp.show) {
  2520. temp._xaxis.show = true;
  2521. temp._yaxis.show = true;
  2522. }
  2523. else {
  2524. if (temp._xaxis.scaleToHiddenSeries) {
  2525. temp._xaxis.show = true;
  2526. }
  2527. if (temp._yaxis.scaleToHiddenSeries) {
  2528. temp._yaxis.show = true;
  2529. }
  2530. }
  2531. // // parse the renderer options and apply default colors if not provided
  2532. // if (!temp.color && temp.show != false) {
  2533. // temp.color = cg.next();
  2534. // colorIndex = cg.getIndex() - 1;;
  2535. // }
  2536. // if (!temp.negativeColor && temp.show != false) {
  2537. // temp.negativeColor = ncg.get(colorIndex);
  2538. // ncg.setIndex(colorIndex);
  2539. // }
  2540. if (!temp.label) {
  2541. temp.label = 'Series '+ (i+1).toString();
  2542. }
  2543. // temp.rendererOptions.show = temp.show;
  2544. // $.extend(true, temp.renderer, {color:this.seriesColors[i]}, this.rendererOptions);
  2545. this.series.push(temp);
  2546. for (var j=0; j<$.jqplot.postParseSeriesOptionsHooks.length; j++) {
  2547. $.jqplot.postParseSeriesOptionsHooks[j].call(this.series[i], this.options.seriesDefaults, this.options.series[i]);
  2548. }
  2549. for (var j=0; j<this.postParseSeriesOptionsHooks.hooks.length; j++) {
  2550. this.postParseSeriesOptionsHooks.hooks[j].call(this.series[i], this.options.seriesDefaults, this.options.series[i]);
  2551. }
  2552. }
  2553. // copy the grid and title options into this object.
  2554. $.extend(true, this.grid, this.options.grid);
  2555. // if axis border properties aren't set, set default.
  2556. for (var i=0, l=_axisNames.length; i<l; i++) {
  2557. var n = _axisNames[i];
  2558. var axis = this.axes[n];
  2559. if (axis.borderWidth == null) {
  2560. axis.borderWidth =this.grid.borderWidth;
  2561. }
  2562. }
  2563. if (typeof this.options.title == 'string') {
  2564. this.title.text = this.options.title;
  2565. }
  2566. else if (typeof this.options.title == 'object') {
  2567. $.extend(true, this.title, this.options.title);
  2568. }
  2569. this.title._plotWidth = this._width;
  2570. this.legend.setOptions(this.options.legend);
  2571. for (var i=0; i<$.jqplot.postParseOptionsHooks.length; i++) {
  2572. $.jqplot.postParseOptionsHooks[i].call(this, options);
  2573. }
  2574. for (var i=0; i<this.postParseOptionsHooks.hooks.length; i++) {
  2575. this.postParseOptionsHooks.hooks[i].call(this, options);
  2576. }
  2577. };
  2578. // method: destroy
  2579. // Releases all resources occupied by the plot
  2580. this.destroy = function() {
  2581. this.canvasManager.freeAllCanvases();
  2582. if (this.eventCanvas && this.eventCanvas._elem) {
  2583. this.eventCanvas._elem.unbind();
  2584. }
  2585. // Couple of posts on Stack Overflow indicate that empty() doesn't
  2586. // always cear up the dom and release memory. Sometimes setting
  2587. // innerHTML property to null is needed. Particularly on IE, may
  2588. // have to directly set it to null, bypassing $.
  2589. this.target.empty();
  2590. this.target[0].innerHTML = '';
  2591. };
  2592. // method: replot
  2593. // Does a reinitialization of the plot followed by
  2594. // a redraw. Method could be used to interactively
  2595. // change plot characteristics and then replot.
  2596. //
  2597. // Parameters:
  2598. // options - Options used for replotting.
  2599. //
  2600. // Properties:
  2601. // clear - false to not clear (empty) the plot container before replotting (default: true).
  2602. // resetAxes - true to reset all axes min, max, numberTicks and tickInterval setting so axes will rescale themselves.
  2603. // optionally pass in list of axes to reset (e.g. ['xaxis', 'y2axis']) (default: false).
  2604. this.replot = function(options) {
  2605. var opts = options || {};
  2606. var data = opts.data || null;
  2607. var clear = (opts.clear === false) ? false : true;
  2608. var resetAxes = opts.resetAxes || false;
  2609. delete opts.data;
  2610. delete opts.clear;
  2611. delete opts.resetAxes;
  2612. this.target.trigger('jqplotPreReplot');
  2613. if (clear) {
  2614. this.destroy();
  2615. }
  2616. // if have data or other options, full reinit.
  2617. // otherwise, quickinit.
  2618. if (data || !$.isEmptyObject(opts)) {
  2619. this.reInitialize(data, opts);
  2620. }
  2621. else {
  2622. this.quickInit();
  2623. }
  2624. if (resetAxes) {
  2625. this.resetAxesScale(resetAxes, opts.axes);
  2626. }
  2627. this.draw();
  2628. this.target.trigger('jqplotPostReplot');
  2629. };
  2630. // method: redraw
  2631. // Empties the plot target div and redraws the plot.
  2632. // This enables plot data and properties to be changed
  2633. // and then to comletely clear the plot and redraw.
  2634. // redraw *will not* reinitialize any plot elements.
  2635. // That is, axes will not be autoscaled and defaults
  2636. // will not be reapplied to any plot elements. redraw
  2637. // is used primarily with zooming.
  2638. //
  2639. // Parameters:
  2640. // clear - false to not clear (empty) the plot container before redrawing (default: true).
  2641. this.redraw = function(clear) {
  2642. clear = (clear != null) ? clear : true;
  2643. this.target.trigger('jqplotPreRedraw');
  2644. if (clear) {
  2645. this.canvasManager.freeAllCanvases();
  2646. this.eventCanvas._elem.unbind();
  2647. // Dont think I bind any events to the target, this shouldn't be necessary.
  2648. // It will remove user's events.
  2649. // this.target.unbind();
  2650. this.target.empty();
  2651. }
  2652. for (var ax in this.axes) {
  2653. this.axes[ax]._ticks = [];
  2654. }
  2655. this.computePlotData();
  2656. // for (var i=0; i<this.series.length; i++) {
  2657. // this.populatePlotData(this.series[i], i);
  2658. // }
  2659. this._sumy = 0;
  2660. this._sumx = 0;
  2661. for (var i=0, tsl = this.series.length; i<tsl; i++) {
  2662. this._sumy += this.series[i]._sumy;
  2663. this._sumx += this.series[i]._sumx;
  2664. }
  2665. this.draw();
  2666. this.target.trigger('jqplotPostRedraw');
  2667. };
  2668. // method: draw
  2669. // Draws all elements of the plot into the container.
  2670. // Does not clear the container before drawing.
  2671. this.draw = function(){
  2672. if (this.drawIfHidden || this.target.is(':visible')) {
  2673. this.target.trigger('jqplotPreDraw');
  2674. var i,
  2675. j,
  2676. l,
  2677. tempseries;
  2678. for (i=0, l=$.jqplot.preDrawHooks.length; i<l; i++) {
  2679. $.jqplot.preDrawHooks[i].call(this);
  2680. }
  2681. for (i=0, l=this.preDrawHooks.hooks.length; i<l; i++) {
  2682. this.preDrawHooks.hooks[i].apply(this, this.preDrawSeriesHooks.args[i]);
  2683. }
  2684. // create an underlying canvas to be used for special features.
  2685. this.target.append(this.baseCanvas.createElement({left:0, right:0, top:0, bottom:0}, 'jqplot-base-canvas', null, this));
  2686. this.baseCanvas.setContext();
  2687. this.target.append(this.title.draw());
  2688. this.title.pack({top:0, left:0});
  2689. // make room for the legend between the grid and the edge.
  2690. // pass a dummy offsets object and a reference to the plot.
  2691. var legendElem = this.legend.draw({}, this);
  2692. var gridPadding = {top:0, left:0, bottom:0, right:0};
  2693. if (this.legend.placement == "outsideGrid") {
  2694. // temporarily append the legend to get dimensions
  2695. this.target.append(legendElem);
  2696. switch (this.legend.location) {
  2697. case 'n':
  2698. gridPadding.top += this.legend.getHeight();
  2699. break;
  2700. case 's':
  2701. gridPadding.bottom += this.legend.getHeight();
  2702. break;
  2703. case 'ne':
  2704. case 'e':
  2705. case 'se':
  2706. gridPadding.right += this.legend.getWidth();
  2707. break;
  2708. case 'nw':
  2709. case 'w':
  2710. case 'sw':
  2711. gridPadding.left += this.legend.getWidth();
  2712. break;
  2713. default: // same as 'ne'
  2714. gridPadding.right += this.legend.getWidth();
  2715. break;
  2716. }
  2717. legendElem = legendElem.detach();
  2718. }
  2719. var ax = this.axes;
  2720. var name;
  2721. // draw the yMidAxis first, so xaxis of pyramid chart can adjust itself if needed.
  2722. for (i=0; i<12; i++) {
  2723. name = _axisNames[i];
  2724. this.target.append(ax[name].draw(this.baseCanvas._ctx, this));
  2725. ax[name].set();
  2726. }
  2727. if (ax.yaxis.show) {
  2728. gridPadding.left += ax.yaxis.getWidth();
  2729. }
  2730. var ra = ['y2axis', 'y3axis', 'y4axis', 'y5axis', 'y6axis', 'y7axis', 'y8axis', 'y9axis'];
  2731. var rapad = [0, 0, 0, 0, 0, 0, 0, 0];
  2732. var gpr = 0;
  2733. var n;
  2734. for (n=0; n<8; n++) {
  2735. if (ax[ra[n]].show) {
  2736. gpr += ax[ra[n]].getWidth();
  2737. rapad[n] = gpr;
  2738. }
  2739. }
  2740. gridPadding.right += gpr;
  2741. if (ax.x2axis.show) {
  2742. gridPadding.top += ax.x2axis.getHeight();
  2743. }
  2744. if (this.title.show) {
  2745. gridPadding.top += this.title.getHeight();
  2746. }
  2747. if (ax.xaxis.show) {
  2748. gridPadding.bottom += ax.xaxis.getHeight();
  2749. }
  2750. // end of gridPadding adjustments.
  2751. // if user passed in gridDimensions option, check against calculated gridPadding
  2752. if (this.options.gridDimensions && $.isPlainObject(this.options.gridDimensions)) {
  2753. var gdw = parseInt(this.options.gridDimensions.width, 10) || 0;
  2754. var gdh = parseInt(this.options.gridDimensions.height, 10) || 0;
  2755. var widthAdj = (this._width - gridPadding.left - gridPadding.right - gdw)/2;
  2756. var heightAdj = (this._height - gridPadding.top - gridPadding.bottom - gdh)/2;
  2757. if (heightAdj >= 0 && widthAdj >= 0) {
  2758. gridPadding.top += heightAdj;
  2759. gridPadding.bottom += heightAdj;
  2760. gridPadding.left += widthAdj;
  2761. gridPadding.right += widthAdj;
  2762. }
  2763. }
  2764. var arr = ['top', 'bottom', 'left', 'right'];
  2765. for (var n in arr) {
  2766. if (this._gridPadding[arr[n]] == null && gridPadding[arr[n]] > 0) {
  2767. this._gridPadding[arr[n]] = gridPadding[arr[n]];
  2768. }
  2769. else if (this._gridPadding[arr[n]] == null) {
  2770. this._gridPadding[arr[n]] = this._defaultGridPadding[arr[n]];
  2771. }
  2772. }
  2773. var legendPadding = this._gridPadding;
  2774. if (this.legend.placement === 'outsideGrid') {
  2775. legendPadding = {top:this.title.getHeight(), left: 0, right: 0, bottom: 0};
  2776. if (this.legend.location === 's') {
  2777. legendPadding.left = this._gridPadding.left;
  2778. legendPadding.right = this._gridPadding.right;
  2779. }
  2780. }
  2781. ax.xaxis.pack({position:'absolute', bottom:this._gridPadding.bottom - ax.xaxis.getHeight(), left:0, width:this._width}, {min:this._gridPadding.left, max:this._width - this._gridPadding.right});
  2782. ax.yaxis.pack({position:'absolute', top:0, left:this._gridPadding.left - ax.yaxis.getWidth(), height:this._height}, {min:this._height - this._gridPadding.bottom, max: this._gridPadding.top});
  2783. ax.x2axis.pack({position:'absolute', top:this._gridPadding.top - ax.x2axis.getHeight(), left:0, width:this._width}, {min:this._gridPadding.left, max:this._width - this._gridPadding.right});
  2784. for (i=8; i>0; i--) {
  2785. ax[ra[i-1]].pack({position:'absolute', top:0, right:this._gridPadding.right - rapad[i-1]}, {min:this._height - this._gridPadding.bottom, max: this._gridPadding.top});
  2786. }
  2787. var ltemp = (this._width - this._gridPadding.left - this._gridPadding.right)/2.0 + this._gridPadding.left - ax.yMidAxis.getWidth()/2.0;
  2788. ax.yMidAxis.pack({position:'absolute', top:0, left:ltemp, zIndex:9, textAlign: 'center'}, {min:this._height - this._gridPadding.bottom, max: this._gridPadding.top});
  2789. this.target.append(this.grid.createElement(this._gridPadding, this));
  2790. this.grid.draw();
  2791. var series = this.series;
  2792. var seriesLength = series.length;
  2793. // put the shadow canvases behind the series canvases so shadows don't overlap on stacked bars.
  2794. for (i=0, l=seriesLength; i<l; i++) {
  2795. // draw series in order of stacking. This affects only
  2796. // order in which canvases are added to dom.
  2797. j = this.seriesStack[i];
  2798. this.target.append(series[j].shadowCanvas.createElement(this._gridPadding, 'jqplot-series-shadowCanvas', null, this));
  2799. series[j].shadowCanvas.setContext();
  2800. series[j].shadowCanvas._elem.data('seriesIndex', j);
  2801. }
  2802. for (i=0, l=seriesLength; i<l; i++) {
  2803. // draw series in order of stacking. This affects only
  2804. // order in which canvases are added to dom.
  2805. j = this.seriesStack[i];
  2806. this.target.append(series[j].canvas.createElement(this._gridPadding, 'jqplot-series-canvas', null, this));
  2807. series[j].canvas.setContext();
  2808. series[j].canvas._elem.data('seriesIndex', j);
  2809. }
  2810. // Need to use filled canvas to capture events in IE.
  2811. // Also, canvas seems to block selection of other elements in document on FF.
  2812. this.target.append(this.eventCanvas.createElement(this._gridPadding, 'jqplot-event-canvas', null, this));
  2813. this.eventCanvas.setContext();
  2814. this.eventCanvas._ctx.fillStyle = 'rgba(0,0,0,0)';
  2815. this.eventCanvas._ctx.fillRect(0,0,this.eventCanvas._ctx.canvas.width, this.eventCanvas._ctx.canvas.height);
  2816. // bind custom event handlers to regular events.
  2817. this.bindCustomEvents();
  2818. // draw legend before series if the series needs to know the legend dimensions.
  2819. if (this.legend.preDraw) {
  2820. this.eventCanvas._elem.before(legendElem);
  2821. this.legend.pack(legendPadding);
  2822. if (this.legend._elem) {
  2823. this.drawSeries({legendInfo:{location:this.legend.location, placement:this.legend.placement, width:this.legend.getWidth(), height:this.legend.getHeight(), xoffset:this.legend.xoffset, yoffset:this.legend.yoffset}});
  2824. }
  2825. else {
  2826. this.drawSeries();
  2827. }
  2828. }
  2829. else { // draw series before legend
  2830. this.drawSeries();
  2831. if (seriesLength) {
  2832. $(series[seriesLength-1].canvas._elem).after(legendElem);
  2833. }
  2834. this.legend.pack(legendPadding);
  2835. }
  2836. // register event listeners on the overlay canvas
  2837. for (var i=0, l=$.jqplot.eventListenerHooks.length; i<l; i++) {
  2838. // in the handler, this will refer to the eventCanvas dom element.
  2839. // make sure there are references back into plot objects.
  2840. this.eventCanvas._elem.bind($.jqplot.eventListenerHooks[i][0], {plot:this}, $.jqplot.eventListenerHooks[i][1]);
  2841. }
  2842. // register event listeners on the overlay canvas
  2843. for (var i=0, l=this.eventListenerHooks.hooks.length; i<l; i++) {
  2844. // in the handler, this will refer to the eventCanvas dom element.
  2845. // make sure there are references back into plot objects.
  2846. this.eventCanvas._elem.bind(this.eventListenerHooks.hooks[i][0], {plot:this}, this.eventListenerHooks.hooks[i][1]);
  2847. }
  2848. var fb = this.fillBetween;
  2849. if(typeof fb.series1 == 'number'){
  2850. if(fb.fill&&fb.series1!==fb.series2&&fb.series1<seriesLength&&fb.series2<seriesLength&&series[fb.series1]._type==="line"&&series[fb.series2]._type==="line")
  2851. this.doFillBetweenLines();
  2852. }
  2853. else{
  2854. if(fb.series1 != null && fb.series2 != null){
  2855. var doFb = false;
  2856. if(fb.series1.length === fb.series2.length){
  2857. var tempSeries1 = 0;
  2858. var tempSeries2 = 0;
  2859. for(var cnt = 0; cnt < fb.series1.length; cnt++){
  2860. tempSeries1 = fb.series1[cnt];
  2861. tempSeries2 = fb.series2[cnt];
  2862. if(tempSeries1!==tempSeries2&&tempSeries1<seriesLength&&tempSeries2<seriesLength&&series[tempSeries1]._type==="line"&&series[tempSeries2]._type==="line"){
  2863. doFb = true;
  2864. }
  2865. else{
  2866. doFb = false;
  2867. break;
  2868. }
  2869. }
  2870. }
  2871. if(fb.fill && doFb){
  2872. this.doFillBetweenLines();
  2873. }
  2874. }
  2875. }
  2876. for (var i=0, l=$.jqplot.postDrawHooks.length; i<l; i++) {
  2877. $.jqplot.postDrawHooks[i].call(this);
  2878. }
  2879. for (var i=0, l=this.postDrawHooks.hooks.length; i<l; i++) {
  2880. this.postDrawHooks.hooks[i].apply(this, this.postDrawHooks.args[i]);
  2881. }
  2882. if (this.target.is(':visible')) {
  2883. this._drawCount += 1;
  2884. }
  2885. var temps,
  2886. tempr,
  2887. sel,
  2888. _els;
  2889. // ughh. ideally would hide all series then show them.
  2890. for (i=0, l=seriesLength; i<l; i++) {
  2891. temps = series[i];
  2892. tempr = temps.renderer;
  2893. sel = '.jqplot-point-label.jqplot-series-'+i;
  2894. if (tempr.animation && tempr.animation._supported && tempr.animation.show && (this._drawCount < 2 || this.animateReplot)) {
  2895. _els = this.target.find(sel);
  2896. _els.stop(true, true).hide();
  2897. temps.canvas._elem.stop(true, true).hide();
  2898. temps.shadowCanvas._elem.stop(true, true).hide();
  2899. temps.canvas._elem.jqplotEffect('blind', {mode: 'show', direction: tempr.animation.direction}, tempr.animation.speed);
  2900. temps.shadowCanvas._elem.jqplotEffect('blind', {mode: 'show', direction: tempr.animation.direction}, tempr.animation.speed);
  2901. _els.fadeIn(tempr.animation.speed*0.8);
  2902. }
  2903. }
  2904. _els = null;
  2905. this.target.trigger('jqplotPostDraw', [this]);
  2906. }
  2907. };
  2908. jqPlot.prototype.doFillBetweenLines = function () {
  2909. var fb = this.fillBetween;
  2910. var series = this.series;
  2911. var sid1 = fb.series1;
  2912. var sid2 = fb.series2;
  2913. var id1 = 0, id2 = 0;
  2914. function fill(id1, id2){
  2915. var series1 = series[id1];
  2916. var series2 = series[id2];
  2917. if (series2.renderer.smooth)
  2918. var tempgd = series2.renderer._smoothedData.slice(0).reverse();
  2919. else
  2920. var tempgd = series2.gridData.slice(0).reverse();
  2921. if (series1.renderer.smooth)
  2922. var gd = series1.renderer._smoothedData.concat(tempgd);
  2923. else
  2924. var gd = series1.gridData.concat(tempgd);
  2925. var color = fb.color !== null ? fb.color : series[sid1].fillColor;
  2926. var baseSeries = fb.baseSeries !== null ? fb.baseSeries : id1;
  2927. var sr =
  2928. series[baseSeries].renderer.shapeRenderer;
  2929. var opts =
  2930. {
  2931. fillStyle : color,
  2932. fill : true,
  2933. closePath : true
  2934. };
  2935. sr.draw(series1.shadowCanvas._ctx, gd, opts)
  2936. }
  2937. if(typeof sid1 == 'number' && typeof sid2 == 'number'){
  2938. id1 = sid1 < sid2 ? sid1 : sid2;
  2939. id2 = sid2 > sid1 ? sid2 : sid1;
  2940. fill(id1, id2);
  2941. }
  2942. else{
  2943. for(var cnt = 0; cnt < sid1.length ; cnt++){
  2944. id1 = sid1[cnt] < sid2[cnt] ? sid1[cnt] : sid2[cnt];
  2945. id2 = sid2[cnt] > sid1[cnt] ? sid2[cnt] : sid1[cnt];
  2946. fill(id1, id2);
  2947. }
  2948. }
  2949. };
  2950. this.bindCustomEvents = function() {
  2951. this.eventCanvas._elem.bind('click', {plot:this}, this.onClick);
  2952. this.eventCanvas._elem.bind('dblclick', {plot:this}, this.onDblClick);
  2953. this.eventCanvas._elem.bind('mousedown', {plot:this}, this.onMouseDown);
  2954. this.eventCanvas._elem.bind('mousemove', {plot:this}, this.onMouseMove);
  2955. this.eventCanvas._elem.bind('mouseenter', {plot:this}, this.onMouseEnter);
  2956. this.eventCanvas._elem.bind('mouseleave', {plot:this}, this.onMouseLeave);
  2957. if (this.captureRightClick) {
  2958. this.eventCanvas._elem.bind('mouseup', {plot:this}, this.onRightClick);
  2959. this.eventCanvas._elem.get(0).oncontextmenu = function() {
  2960. return false;
  2961. };
  2962. }
  2963. else {
  2964. this.eventCanvas._elem.bind('mouseup', {plot:this}, this.onMouseUp);
  2965. }
  2966. };
  2967. function getEventPosition(ev) {
  2968. var plot = ev.data.plot;
  2969. var go = plot.eventCanvas._elem.offset();
  2970. var gridPos = {x:ev.pageX - go.left, y:ev.pageY - go.top};
  2971. var dataPos = {xaxis:null, yaxis:null, x2axis:null, y2axis:null, y3axis:null, y4axis:null, y5axis:null, y6axis:null, y7axis:null, y8axis:null, y9axis:null, yMidAxis:null};
  2972. var an = ['xaxis', 'yaxis', 'x2axis', 'y2axis', 'y3axis', 'y4axis', 'y5axis', 'y6axis', 'y7axis', 'y8axis', 'y9axis', 'yMidAxis'];
  2973. var ax = plot.axes;
  2974. var n, axis;
  2975. for (n=11; n>0; n--) {
  2976. axis = an[n-1];
  2977. if (ax[axis].show) {
  2978. dataPos[axis] = ax[axis].series_p2u(gridPos[axis.charAt(0)]);
  2979. }
  2980. }
  2981. return {offsets:go, gridPos:gridPos, dataPos:dataPos};
  2982. }
  2983. // function to check if event location is over a area area
  2984. function checkIntersection(gridpos, plot) {
  2985. var series = plot.series;
  2986. var i, j, k, s, r, x, y, theta, sm, sa, minang, maxang;
  2987. var d0, d, p, pp, points, bw, hp;
  2988. var threshold, t;
  2989. for (k=plot.seriesStack.length-1; k>=0; k--) {
  2990. i = plot.seriesStack[k];
  2991. s = series[i];
  2992. hp = s._highlightThreshold;
  2993. switch (s.renderer.constructor) {
  2994. case $.jqplot.BarRenderer:
  2995. x = gridpos.x;
  2996. y = gridpos.y;
  2997. for (j=0; j<s._barPoints.length; j++) {
  2998. points = s._barPoints[j];
  2999. p = s.gridData[j];
  3000. if (x>points[0][0] && x<points[2][0] && (y>points[2][1] && y<points[0][1] || y<points[2][1] && y>points[0][1])) {
  3001. return {seriesIndex:s.index, pointIndex:j, gridData:p, data:s.data[j], points:s._barPoints[j]};
  3002. }
  3003. }
  3004. break;
  3005. case $.jqplot.PyramidRenderer:
  3006. x = gridpos.x;
  3007. y = gridpos.y;
  3008. for (j=0; j<s._barPoints.length; j++) {
  3009. points = s._barPoints[j];
  3010. p = s.gridData[j];
  3011. if (x > points[0][0] + hp[0][0] && x < points[2][0] + hp[2][0] && y > points[2][1] && y < points[0][1]) {
  3012. return {seriesIndex:s.index, pointIndex:j, gridData:p, data:s.data[j], points:s._barPoints[j]};
  3013. }
  3014. }
  3015. break;
  3016. case $.jqplot.DonutRenderer:
  3017. sa = s.startAngle/180*Math.PI;
  3018. x = gridpos.x - s._center[0];
  3019. y = gridpos.y - s._center[1];
  3020. r = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
  3021. if (x > 0 && -y >= 0) {
  3022. theta = 2*Math.PI - Math.atan(-y/x);
  3023. }
  3024. else if (x > 0 && -y < 0) {
  3025. theta = -Math.atan(-y/x);
  3026. }
  3027. else if (x < 0) {
  3028. theta = Math.PI - Math.atan(-y/x);
  3029. }
  3030. else if (x == 0 && -y > 0) {
  3031. theta = 3*Math.PI/2;
  3032. }
  3033. else if (x == 0 && -y < 0) {
  3034. theta = Math.PI/2;
  3035. }
  3036. else if (x == 0 && y == 0) {
  3037. theta = 0;
  3038. }
  3039. if (sa) {
  3040. theta -= sa;
  3041. if (theta < 0) {
  3042. theta += 2*Math.PI;
  3043. }
  3044. else if (theta > 2*Math.PI) {
  3045. theta -= 2*Math.PI;
  3046. }
  3047. }
  3048. sm = s.sliceMargin/180*Math.PI;
  3049. if (r < s._radius && r > s._innerRadius) {
  3050. for (j=0; j<s.gridData.length; j++) {
  3051. minang = (j>0) ? s.gridData[j-1][1]+sm : sm;
  3052. maxang = s.gridData[j][1];
  3053. if (theta > minang && theta < maxang) {
  3054. return {seriesIndex:s.index, pointIndex:j, gridData:[gridpos.x,gridpos.y], data:s.data[j]};
  3055. }
  3056. }
  3057. }
  3058. break;
  3059. case $.jqplot.PieRenderer:
  3060. sa = s.startAngle/180*Math.PI;
  3061. x = gridpos.x - s._center[0];
  3062. y = gridpos.y - s._center[1];
  3063. r = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
  3064. if (x > 0 && -y >= 0) {
  3065. theta = 2*Math.PI - Math.atan(-y/x);
  3066. }
  3067. else if (x > 0 && -y < 0) {
  3068. theta = -Math.atan(-y/x);
  3069. }
  3070. else if (x < 0) {
  3071. theta = Math.PI - Math.atan(-y/x);
  3072. }
  3073. else if (x == 0 && -y > 0) {
  3074. theta = 3*Math.PI/2;
  3075. }
  3076. else if (x == 0 && -y < 0) {
  3077. theta = Math.PI/2;
  3078. }
  3079. else if (x == 0 && y == 0) {
  3080. theta = 0;
  3081. }
  3082. if (sa) {
  3083. theta -= sa;
  3084. if (theta < 0) {
  3085. theta += 2*Math.PI;
  3086. }
  3087. else if (theta > 2*Math.PI) {
  3088. theta -= 2*Math.PI;
  3089. }
  3090. }
  3091. sm = s.sliceMargin/180*Math.PI;
  3092. if (r < s._radius) {
  3093. for (j=0; j<s.gridData.length; j++) {
  3094. minang = (j>0) ? s.gridData[j-1][1]+sm : sm;
  3095. maxang = s.gridData[j][1];
  3096. if (theta > minang && theta < maxang) {
  3097. return {seriesIndex:s.index, pointIndex:j, gridData:[gridpos.x,gridpos.y], data:s.data[j]};
  3098. }
  3099. }
  3100. }
  3101. break;
  3102. case $.jqplot.BubbleRenderer:
  3103. x = gridpos.x;
  3104. y = gridpos.y;
  3105. var ret = null;
  3106. if (s.show) {
  3107. for (var j=0; j<s.gridData.length; j++) {
  3108. p = s.gridData[j];
  3109. d = Math.sqrt( (x-p[0]) * (x-p[0]) + (y-p[1]) * (y-p[1]) );
  3110. if (d <= p[2] && (d <= d0 || d0 == null)) {
  3111. d0 = d;
  3112. ret = {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]};
  3113. }
  3114. }
  3115. if (ret != null) {
  3116. return ret;
  3117. }
  3118. }
  3119. break;
  3120. case $.jqplot.FunnelRenderer:
  3121. x = gridpos.x;
  3122. y = gridpos.y;
  3123. var v = s._vertices,
  3124. vfirst = v[0],
  3125. vlast = v[v.length-1],
  3126. lex,
  3127. rex,
  3128. cv;
  3129. // equations of right and left sides, returns x, y values given height of section (y value and 2 points)
  3130. function findedge (l, p1 , p2) {
  3131. var m = (p1[1] - p2[1])/(p1[0] - p2[0]);
  3132. var b = p1[1] - m*p1[0];
  3133. var y = l + p1[1];
  3134. return [(y - b)/m, y];
  3135. }
  3136. // check each section
  3137. lex = findedge(y, vfirst[0], vlast[3]);
  3138. rex = findedge(y, vfirst[1], vlast[2]);
  3139. for (j=0; j<v.length; j++) {
  3140. cv = v[j];
  3141. if (y >= cv[0][1] && y <= cv[3][1] && x >= lex[0] && x <= rex[0]) {
  3142. return {seriesIndex:s.index, pointIndex:j, gridData:null, data:s.data[j]};
  3143. }
  3144. }
  3145. break;
  3146. case $.jqplot.LineRenderer:
  3147. x = gridpos.x;
  3148. y = gridpos.y;
  3149. r = s.renderer;
  3150. if (s.show) {
  3151. if ((s.fill || (s.renderer.bands.show && s.renderer.bands.fill)) && (!plot.plugins.highlighter || !plot.plugins.highlighter.show)) {
  3152. // first check if it is in bounding box
  3153. var inside = false;
  3154. if (x>s._boundingBox[0][0] && x<s._boundingBox[1][0] && y>s._boundingBox[1][1] && y<s._boundingBox[0][1]) {
  3155. // now check the crossing number
  3156. var numPoints = s._areaPoints.length;
  3157. var ii;
  3158. var j = numPoints-1;
  3159. for(var ii=0; ii < numPoints; ii++) {
  3160. var vertex1 = [s._areaPoints[ii][0], s._areaPoints[ii][1]];
  3161. var vertex2 = [s._areaPoints[j][0], s._areaPoints[j][1]];
  3162. if (vertex1[1] < y && vertex2[1] >= y || vertex2[1] < y && vertex1[1] >= y) {
  3163. if (vertex1[0] + (y - vertex1[1]) / (vertex2[1] - vertex1[1]) * (vertex2[0] - vertex1[0]) < x) {
  3164. inside = !inside;
  3165. }
  3166. }
  3167. j = ii;
  3168. }
  3169. }
  3170. if (inside) {
  3171. return {seriesIndex:i, pointIndex:null, gridData:s.gridData, data:s.data, points:s._areaPoints};
  3172. }
  3173. break;
  3174. }
  3175. else {
  3176. t = s.markerRenderer.size/2+s.neighborThreshold;
  3177. threshold = (t > 0) ? t : 0;
  3178. for (var j=0; j<s.gridData.length; j++) {
  3179. p = s.gridData[j];
  3180. // neighbor looks different to OHLC chart.
  3181. if (r.constructor == $.jqplot.OHLCRenderer) {
  3182. if (r.candleStick) {
  3183. var yp = s._yaxis.series_u2p;
  3184. if (x >= p[0]-r._bodyWidth/2 && x <= p[0]+r._bodyWidth/2 && y >= yp(s.data[j][2]) && y <= yp(s.data[j][3])) {
  3185. return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]};
  3186. }
  3187. }
  3188. // if an open hi low close chart
  3189. else if (!r.hlc){
  3190. var yp = s._yaxis.series_u2p;
  3191. if (x >= p[0]-r._tickLength && x <= p[0]+r._tickLength && y >= yp(s.data[j][2]) && y <= yp(s.data[j][3])) {
  3192. return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]};
  3193. }
  3194. }
  3195. // a hi low close chart
  3196. else {
  3197. var yp = s._yaxis.series_u2p;
  3198. if (x >= p[0]-r._tickLength && x <= p[0]+r._tickLength && y >= yp(s.data[j][1]) && y <= yp(s.data[j][2])) {
  3199. return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]};
  3200. }
  3201. }
  3202. }
  3203. else if (p[0] != null && p[1] != null){
  3204. d = Math.sqrt( (x-p[0]) * (x-p[0]) + (y-p[1]) * (y-p[1]) );
  3205. if (d <= threshold && (d <= d0 || d0 == null)) {
  3206. d0 = d;
  3207. return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]};
  3208. }
  3209. }
  3210. }
  3211. }
  3212. }
  3213. break;
  3214. default:
  3215. x = gridpos.x;
  3216. y = gridpos.y;
  3217. r = s.renderer;
  3218. if (s.show) {
  3219. t = s.markerRenderer.size/2+s.neighborThreshold;
  3220. threshold = (t > 0) ? t : 0;
  3221. for (var j=0; j<s.gridData.length; j++) {
  3222. p = s.gridData[j];
  3223. // neighbor looks different to OHLC chart.
  3224. if (r.constructor == $.jqplot.OHLCRenderer) {
  3225. if (r.candleStick) {
  3226. var yp = s._yaxis.series_u2p;
  3227. if (x >= p[0]-r._bodyWidth/2 && x <= p[0]+r._bodyWidth/2 && y >= yp(s.data[j][2]) && y <= yp(s.data[j][3])) {
  3228. return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]};
  3229. }
  3230. }
  3231. // if an open hi low close chart
  3232. else if (!r.hlc){
  3233. var yp = s._yaxis.series_u2p;
  3234. if (x >= p[0]-r._tickLength && x <= p[0]+r._tickLength && y >= yp(s.data[j][2]) && y <= yp(s.data[j][3])) {
  3235. return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]};
  3236. }
  3237. }
  3238. // a hi low close chart
  3239. else {
  3240. var yp = s._yaxis.series_u2p;
  3241. if (x >= p[0]-r._tickLength && x <= p[0]+r._tickLength && y >= yp(s.data[j][1]) && y <= yp(s.data[j][2])) {
  3242. return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]};
  3243. }
  3244. }
  3245. }
  3246. else {
  3247. d = Math.sqrt( (x-p[0]) * (x-p[0]) + (y-p[1]) * (y-p[1]) );
  3248. if (d <= threshold && (d <= d0 || d0 == null)) {
  3249. d0 = d;
  3250. return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]};
  3251. }
  3252. }
  3253. }
  3254. }
  3255. break;
  3256. }
  3257. }
  3258. return null;
  3259. }
  3260. this.onClick = function(ev) {
  3261. // Event passed in is normalized and will have data attribute.
  3262. // Event passed out is unnormalized.
  3263. var positions = getEventPosition(ev);
  3264. var p = ev.data.plot;
  3265. var neighbor = checkIntersection(positions.gridPos, p);
  3266. var evt = $.Event('jqplotClick');
  3267. evt.pageX = ev.pageX;
  3268. evt.pageY = ev.pageY;
  3269. $(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]);
  3270. };
  3271. this.onDblClick = function(ev) {
  3272. // Event passed in is normalized and will have data attribute.
  3273. // Event passed out is unnormalized.
  3274. var positions = getEventPosition(ev);
  3275. var p = ev.data.plot;
  3276. var neighbor = checkIntersection(positions.gridPos, p);
  3277. var evt = $.Event('jqplotDblClick');
  3278. evt.pageX = ev.pageX;
  3279. evt.pageY = ev.pageY;
  3280. $(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]);
  3281. };
  3282. this.onMouseDown = function(ev) {
  3283. var positions = getEventPosition(ev);
  3284. var p = ev.data.plot;
  3285. var neighbor = checkIntersection(positions.gridPos, p);
  3286. var evt = $.Event('jqplotMouseDown');
  3287. evt.pageX = ev.pageX;
  3288. evt.pageY = ev.pageY;
  3289. $(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]);
  3290. };
  3291. this.onMouseUp = function(ev) {
  3292. var positions = getEventPosition(ev);
  3293. var evt = $.Event('jqplotMouseUp');
  3294. evt.pageX = ev.pageX;
  3295. evt.pageY = ev.pageY;
  3296. $(this).trigger(evt, [positions.gridPos, positions.dataPos, null, ev.data.plot]);
  3297. };
  3298. this.onRightClick = function(ev) {
  3299. var positions = getEventPosition(ev);
  3300. var p = ev.data.plot;
  3301. var neighbor = checkIntersection(positions.gridPos, p);
  3302. if (p.captureRightClick) {
  3303. if (ev.which == 3) {
  3304. var evt = $.Event('jqplotRightClick');
  3305. evt.pageX = ev.pageX;
  3306. evt.pageY = ev.pageY;
  3307. $(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]);
  3308. }
  3309. else {
  3310. var evt = $.Event('jqplotMouseUp');
  3311. evt.pageX = ev.pageX;
  3312. evt.pageY = ev.pageY;
  3313. $(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]);
  3314. }
  3315. }
  3316. };
  3317. this.onMouseMove = function(ev) {
  3318. var positions = getEventPosition(ev);
  3319. var p = ev.data.plot;
  3320. var neighbor = checkIntersection(positions.gridPos, p);
  3321. var evt = $.Event('jqplotMouseMove');
  3322. evt.pageX = ev.pageX;
  3323. evt.pageY = ev.pageY;
  3324. $(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]);
  3325. };
  3326. this.onMouseEnter = function(ev) {
  3327. var positions = getEventPosition(ev);
  3328. var p = ev.data.plot;
  3329. var evt = $.Event('jqplotMouseEnter');
  3330. evt.pageX = ev.pageX;
  3331. evt.pageY = ev.pageY;
  3332. evt.relatedTarget = ev.relatedTarget;
  3333. $(this).trigger(evt, [positions.gridPos, positions.dataPos, null, p]);
  3334. };
  3335. this.onMouseLeave = function(ev) {
  3336. var positions = getEventPosition(ev);
  3337. var p = ev.data.plot;
  3338. var evt = $.Event('jqplotMouseLeave');
  3339. evt.pageX = ev.pageX;
  3340. evt.pageY = ev.pageY;
  3341. evt.relatedTarget = ev.relatedTarget;
  3342. $(this).trigger(evt, [positions.gridPos, positions.dataPos, null, p]);
  3343. };
  3344. // method: drawSeries
  3345. // Redraws all or just one series on the plot. No axis scaling
  3346. // is performed and no other elements on the plot are redrawn.
  3347. // options is an options object to pass on to the series renderers.
  3348. // It can be an empty object {}. idx is the series index
  3349. // to redraw if only one series is to be redrawn.
  3350. this.drawSeries = function(options, idx){
  3351. var i, series, ctx;
  3352. // if only one argument passed in and it is a number, use it ad idx.
  3353. idx = (typeof(options) === "number" && idx == null) ? options : idx;
  3354. options = (typeof(options) === "object") ? options : {};
  3355. // draw specified series
  3356. if (idx != undefined) {
  3357. series = this.series[idx];
  3358. ctx = series.shadowCanvas._ctx;
  3359. ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
  3360. series.drawShadow(ctx, options, this);
  3361. ctx = series.canvas._ctx;
  3362. ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
  3363. series.draw(ctx, options, this);
  3364. if (series.renderer.constructor == $.jqplot.BezierCurveRenderer) {
  3365. if (idx < this.series.length - 1) {
  3366. this.drawSeries(idx+1);
  3367. }
  3368. }
  3369. }
  3370. else {
  3371. // if call series drawShadow method first, in case all series shadows
  3372. // should be drawn before any series. This will ensure, like for
  3373. // stacked bar plots, that shadows don't overlap series.
  3374. for (i=0; i<this.series.length; i++) {
  3375. // first clear the canvas
  3376. series = this.series[i];
  3377. ctx = series.shadowCanvas._ctx;
  3378. ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
  3379. series.drawShadow(ctx, options, this);
  3380. ctx = series.canvas._ctx;
  3381. ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
  3382. series.draw(ctx, options, this);
  3383. }
  3384. }
  3385. options = idx = i = series = ctx = null;
  3386. };
  3387. // method: moveSeriesToFront
  3388. // This method requires jQuery 1.4+
  3389. // Moves the specified series canvas in front of all other series canvases.
  3390. // This effectively "draws" the specified series on top of all other series,
  3391. // although it is performed through DOM manipulation, no redrawing is performed.
  3392. //
  3393. // Parameters:
  3394. // idx - 0 based index of the series to move. This will be the index of the series
  3395. // as it was first passed into the jqplot function.
  3396. this.moveSeriesToFront = function (idx) {
  3397. idx = parseInt(idx, 10);
  3398. var stackIndex = $.inArray(idx, this.seriesStack);
  3399. // if already in front, return
  3400. if (stackIndex == -1) {
  3401. return;
  3402. }
  3403. if (stackIndex == this.seriesStack.length -1) {
  3404. this.previousSeriesStack = this.seriesStack.slice(0);
  3405. return;
  3406. }
  3407. var opidx = this.seriesStack[this.seriesStack.length -1];
  3408. var serelem = this.series[idx].canvas._elem.detach();
  3409. var shadelem = this.series[idx].shadowCanvas._elem.detach();
  3410. this.series[opidx].shadowCanvas._elem.after(shadelem);
  3411. this.series[opidx].canvas._elem.after(serelem);
  3412. this.previousSeriesStack = this.seriesStack.slice(0);
  3413. this.seriesStack.splice(stackIndex, 1);
  3414. this.seriesStack.push(idx);
  3415. };
  3416. // method: moveSeriesToBack
  3417. // This method requires jQuery 1.4+
  3418. // Moves the specified series canvas behind all other series canvases.
  3419. //
  3420. // Parameters:
  3421. // idx - 0 based index of the series to move. This will be the index of the series
  3422. // as it was first passed into the jqplot function.
  3423. this.moveSeriesToBack = function (idx) {
  3424. idx = parseInt(idx, 10);
  3425. var stackIndex = $.inArray(idx, this.seriesStack);
  3426. // if already in back, return
  3427. if (stackIndex == 0 || stackIndex == -1) {
  3428. return;
  3429. }
  3430. var opidx = this.seriesStack[0];
  3431. var serelem = this.series[idx].canvas._elem.detach();
  3432. var shadelem = this.series[idx].shadowCanvas._elem.detach();
  3433. this.series[opidx].shadowCanvas._elem.before(shadelem);
  3434. this.series[opidx].canvas._elem.before(serelem);
  3435. this.previousSeriesStack = this.seriesStack.slice(0);
  3436. this.seriesStack.splice(stackIndex, 1);
  3437. this.seriesStack.unshift(idx);
  3438. };
  3439. // method: restorePreviousSeriesOrder
  3440. // This method requires jQuery 1.4+
  3441. // Restore the series canvas order to its previous state.
  3442. // Useful to put a series back where it belongs after moving
  3443. // it to the front.
  3444. this.restorePreviousSeriesOrder = function () {
  3445. var i, j, serelem, shadelem, temp, move, keep;
  3446. // if no change, return.
  3447. if (this.seriesStack == this.previousSeriesStack) {
  3448. return;
  3449. }
  3450. for (i=1; i<this.previousSeriesStack.length; i++) {
  3451. move = this.previousSeriesStack[i];
  3452. keep = this.previousSeriesStack[i-1];
  3453. serelem = this.series[move].canvas._elem.detach();
  3454. shadelem = this.series[move].shadowCanvas._elem.detach();
  3455. this.series[keep].shadowCanvas._elem.after(shadelem);
  3456. this.series[keep].canvas._elem.after(serelem);
  3457. }
  3458. temp = this.seriesStack.slice(0);
  3459. this.seriesStack = this.previousSeriesStack.slice(0);
  3460. this.previousSeriesStack = temp;
  3461. };
  3462. // method: restoreOriginalSeriesOrder
  3463. // This method requires jQuery 1.4+
  3464. // Restore the series canvas order to its original order
  3465. // when the plot was created.
  3466. this.restoreOriginalSeriesOrder = function () {
  3467. var i, j, arr=[], serelem, shadelem;
  3468. for (i=0; i<this.series.length; i++) {
  3469. arr.push(i);
  3470. }
  3471. if (this.seriesStack == arr) {
  3472. return;
  3473. }
  3474. this.previousSeriesStack = this.seriesStack.slice(0);
  3475. this.seriesStack = arr;
  3476. for (i=1; i<this.seriesStack.length; i++) {
  3477. serelem = this.series[i].canvas._elem.detach();
  3478. shadelem = this.series[i].shadowCanvas._elem.detach();
  3479. this.series[i-1].shadowCanvas._elem.after(shadelem);
  3480. this.series[i-1].canvas._elem.after(serelem);
  3481. }
  3482. };
  3483. this.activateTheme = function (name) {
  3484. this.themeEngine.activate(this, name);
  3485. };
  3486. }
  3487. // conpute a highlight color or array of highlight colors from given colors.
  3488. $.jqplot.computeHighlightColors = function(colors) {
  3489. var ret;
  3490. if ($.isArray(colors)) {
  3491. ret = [];
  3492. for (var i=0; i<colors.length; i++){
  3493. var rgba = $.jqplot.getColorComponents(colors[i]);
  3494. var newrgb = [rgba[0], rgba[1], rgba[2]];
  3495. var sum = newrgb[0] + newrgb[1] + newrgb[2];
  3496. for (var j=0; j<3; j++) {
  3497. // when darkening, lowest color component can be is 60.
  3498. newrgb[j] = (sum > 660) ? newrgb[j] * 0.85 : 0.73 * newrgb[j] + 90;
  3499. newrgb[j] = parseInt(newrgb[j], 10);
  3500. (newrgb[j] > 255) ? 255 : newrgb[j];
  3501. }
  3502. // newrgb[3] = (rgba[3] > 0.4) ? rgba[3] * 0.4 : rgba[3] * 1.5;
  3503. // newrgb[3] = (rgba[3] > 0.5) ? 0.8 * rgba[3] - .1 : rgba[3] + 0.2;
  3504. newrgb[3] = 0.3 + 0.35 * rgba[3];
  3505. ret.push('rgba('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+','+newrgb[3]+')');
  3506. }
  3507. }
  3508. else {
  3509. var rgba = $.jqplot.getColorComponents(colors);
  3510. var newrgb = [rgba[0], rgba[1], rgba[2]];
  3511. var sum = newrgb[0] + newrgb[1] + newrgb[2];
  3512. for (var j=0; j<3; j++) {
  3513. // when darkening, lowest color component can be is 60.
  3514. // newrgb[j] = (sum > 570) ? newrgb[j] * 0.8 : newrgb[j] + 0.3 * (255 - newrgb[j]);
  3515. // newrgb[j] = parseInt(newrgb[j], 10);
  3516. newrgb[j] = (sum > 660) ? newrgb[j] * 0.85 : 0.73 * newrgb[j] + 90;
  3517. newrgb[j] = parseInt(newrgb[j], 10);
  3518. (newrgb[j] > 255) ? 255 : newrgb[j];
  3519. }
  3520. // newrgb[3] = (rgba[3] > 0.4) ? rgba[3] * 0.4 : rgba[3] * 1.5;
  3521. // newrgb[3] = (rgba[3] > 0.5) ? 0.8 * rgba[3] - .1 : rgba[3] + 0.2;
  3522. newrgb[3] = 0.3 + 0.35 * rgba[3];
  3523. ret = 'rgba('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+','+newrgb[3]+')';
  3524. }
  3525. return ret;
  3526. };
  3527. $.jqplot.ColorGenerator = function(colors) {
  3528. colors = colors || $.jqplot.config.defaultColors;
  3529. var idx = 0;
  3530. this.next = function () {
  3531. if (idx < colors.length) {
  3532. return colors[idx++];
  3533. }
  3534. else {
  3535. idx = 0;
  3536. return colors[idx++];
  3537. }
  3538. };
  3539. this.previous = function () {
  3540. if (idx > 0) {
  3541. return colors[idx--];
  3542. }
  3543. else {
  3544. idx = colors.length-1;
  3545. return colors[idx];
  3546. }
  3547. };
  3548. // get a color by index without advancing pointer.
  3549. this.get = function(i) {
  3550. var idx = i - colors.length * Math.floor(i/colors.length);
  3551. return colors[idx];
  3552. };
  3553. this.setColors = function(c) {
  3554. colors = c;
  3555. };
  3556. this.reset = function() {
  3557. idx = 0;
  3558. };
  3559. this.getIndex = function() {
  3560. return idx;
  3561. };
  3562. this.setIndex = function(index) {
  3563. idx = index;
  3564. };
  3565. };
  3566. // convert a hex color string to rgb string.
  3567. // h - 3 or 6 character hex string, with or without leading #
  3568. // a - optional alpha
  3569. $.jqplot.hex2rgb = function(h, a) {
  3570. h = h.replace('#', '');
  3571. if (h.length == 3) {
  3572. h = h.charAt(0)+h.charAt(0)+h.charAt(1)+h.charAt(1)+h.charAt(2)+h.charAt(2);
  3573. }
  3574. var rgb;
  3575. rgb = 'rgba('+parseInt(h.slice(0,2), 16)+', '+parseInt(h.slice(2,4), 16)+', '+parseInt(h.slice(4,6), 16);
  3576. if (a) {
  3577. rgb += ', '+a;
  3578. }
  3579. rgb += ')';
  3580. return rgb;
  3581. };
  3582. // convert an rgb color spec to a hex spec. ignore any alpha specification.
  3583. $.jqplot.rgb2hex = function(s) {
  3584. var pat = /rgba?\( *([0-9]{1,3}\.?[0-9]*%?) *, *([0-9]{1,3}\.?[0-9]*%?) *, *([0-9]{1,3}\.?[0-9]*%?) *(?:, *[0-9.]*)?\)/;
  3585. var m = s.match(pat);
  3586. var h = '#';
  3587. for (var i=1; i<4; i++) {
  3588. var temp;
  3589. if (m[i].search(/%/) != -1) {
  3590. temp = parseInt(255*m[i]/100, 10).toString(16);
  3591. if (temp.length == 1) {
  3592. temp = '0'+temp;
  3593. }
  3594. }
  3595. else {
  3596. temp = parseInt(m[i], 10).toString(16);
  3597. if (temp.length == 1) {
  3598. temp = '0'+temp;
  3599. }
  3600. }
  3601. h += temp;
  3602. }
  3603. return h;
  3604. };
  3605. // given a css color spec, return an rgb css color spec
  3606. $.jqplot.normalize2rgb = function(s, a) {
  3607. if (s.search(/^ *rgba?\(/) != -1) {
  3608. return s;
  3609. }
  3610. else if (s.search(/^ *#?[0-9a-fA-F]?[0-9a-fA-F]/) != -1) {
  3611. return $.jqplot.hex2rgb(s, a);
  3612. }
  3613. else {
  3614. throw new Error('Invalid color spec');
  3615. }
  3616. };
  3617. // extract the r, g, b, a color components out of a css color spec.
  3618. $.jqplot.getColorComponents = function(s) {
  3619. // check to see if a color keyword.
  3620. s = $.jqplot.colorKeywordMap[s] || s;
  3621. var rgb = $.jqplot.normalize2rgb(s);
  3622. var pat = /rgba?\( *([0-9]{1,3}\.?[0-9]*%?) *, *([0-9]{1,3}\.?[0-9]*%?) *, *([0-9]{1,3}\.?[0-9]*%?) *,? *([0-9.]* *)?\)/;
  3623. var m = rgb.match(pat);
  3624. var ret = [];
  3625. for (var i=1; i<4; i++) {
  3626. if (m[i].search(/%/) != -1) {
  3627. ret[i-1] = parseInt(255*m[i]/100, 10);
  3628. }
  3629. else {
  3630. ret[i-1] = parseInt(m[i], 10);
  3631. }
  3632. }
  3633. ret[3] = parseFloat(m[4]) ? parseFloat(m[4]) : 1.0;
  3634. return ret;
  3635. };
  3636. $.jqplot.colorKeywordMap = {
  3637. aliceblue: 'rgb(240, 248, 255)',
  3638. antiquewhite: 'rgb(250, 235, 215)',
  3639. aqua: 'rgb( 0, 255, 255)',
  3640. aquamarine: 'rgb(127, 255, 212)',
  3641. azure: 'rgb(240, 255, 255)',
  3642. beige: 'rgb(245, 245, 220)',
  3643. bisque: 'rgb(255, 228, 196)',
  3644. black: 'rgb( 0, 0, 0)',
  3645. blanchedalmond: 'rgb(255, 235, 205)',
  3646. blue: 'rgb( 0, 0, 255)',
  3647. blueviolet: 'rgb(138, 43, 226)',
  3648. brown: 'rgb(165, 42, 42)',
  3649. burlywood: 'rgb(222, 184, 135)',
  3650. cadetblue: 'rgb( 95, 158, 160)',
  3651. chartreuse: 'rgb(127, 255, 0)',
  3652. chocolate: 'rgb(210, 105, 30)',
  3653. coral: 'rgb(255, 127, 80)',
  3654. cornflowerblue: 'rgb(100, 149, 237)',
  3655. cornsilk: 'rgb(255, 248, 220)',
  3656. crimson: 'rgb(220, 20, 60)',
  3657. cyan: 'rgb( 0, 255, 255)',
  3658. darkblue: 'rgb( 0, 0, 139)',
  3659. darkcyan: 'rgb( 0, 139, 139)',
  3660. darkgoldenrod: 'rgb(184, 134, 11)',
  3661. darkgray: 'rgb(169, 169, 169)',
  3662. darkgreen: 'rgb( 0, 100, 0)',
  3663. darkgrey: 'rgb(169, 169, 169)',
  3664. darkkhaki: 'rgb(189, 183, 107)',
  3665. darkmagenta: 'rgb(139, 0, 139)',
  3666. darkolivegreen: 'rgb( 85, 107, 47)',
  3667. darkorange: 'rgb(255, 140, 0)',
  3668. darkorchid: 'rgb(153, 50, 204)',
  3669. darkred: 'rgb(139, 0, 0)',
  3670. darksalmon: 'rgb(233, 150, 122)',
  3671. darkseagreen: 'rgb(143, 188, 143)',
  3672. darkslateblue: 'rgb( 72, 61, 139)',
  3673. darkslategray: 'rgb( 47, 79, 79)',
  3674. darkslategrey: 'rgb( 47, 79, 79)',
  3675. darkturquoise: 'rgb( 0, 206, 209)',
  3676. darkviolet: 'rgb(148, 0, 211)',
  3677. deeppink: 'rgb(255, 20, 147)',
  3678. deepskyblue: 'rgb( 0, 191, 255)',
  3679. dimgray: 'rgb(105, 105, 105)',
  3680. dimgrey: 'rgb(105, 105, 105)',
  3681. dodgerblue: 'rgb( 30, 144, 255)',
  3682. firebrick: 'rgb(178, 34, 34)',
  3683. floralwhite: 'rgb(255, 250, 240)',
  3684. forestgreen: 'rgb( 34, 139, 34)',
  3685. fuchsia: 'rgb(255, 0, 255)',
  3686. gainsboro: 'rgb(220, 220, 220)',
  3687. ghostwhite: 'rgb(248, 248, 255)',
  3688. gold: 'rgb(255, 215, 0)',
  3689. goldenrod: 'rgb(218, 165, 32)',
  3690. gray: 'rgb(128, 128, 128)',
  3691. grey: 'rgb(128, 128, 128)',
  3692. green: 'rgb( 0, 128, 0)',
  3693. greenyellow: 'rgb(173, 255, 47)',
  3694. honeydew: 'rgb(240, 255, 240)',
  3695. hotpink: 'rgb(255, 105, 180)',
  3696. indianred: 'rgb(205, 92, 92)',
  3697. indigo: 'rgb( 75, 0, 130)',
  3698. ivory: 'rgb(255, 255, 240)',
  3699. khaki: 'rgb(240, 230, 140)',
  3700. lavender: 'rgb(230, 230, 250)',
  3701. lavenderblush: 'rgb(255, 240, 245)',
  3702. lawngreen: 'rgb(124, 252, 0)',
  3703. lemonchiffon: 'rgb(255, 250, 205)',
  3704. lightblue: 'rgb(173, 216, 230)',
  3705. lightcoral: 'rgb(240, 128, 128)',
  3706. lightcyan: 'rgb(224, 255, 255)',
  3707. lightgoldenrodyellow: 'rgb(250, 250, 210)',
  3708. lightgray: 'rgb(211, 211, 211)',
  3709. lightgreen: 'rgb(144, 238, 144)',
  3710. lightgrey: 'rgb(211, 211, 211)',
  3711. lightpink: 'rgb(255, 182, 193)',
  3712. lightsalmon: 'rgb(255, 160, 122)',
  3713. lightseagreen: 'rgb( 32, 178, 170)',
  3714. lightskyblue: 'rgb(135, 206, 250)',
  3715. lightslategray: 'rgb(119, 136, 153)',
  3716. lightslategrey: 'rgb(119, 136, 153)',
  3717. lightsteelblue: 'rgb(176, 196, 222)',
  3718. lightyellow: 'rgb(255, 255, 224)',
  3719. lime: 'rgb( 0, 255, 0)',
  3720. limegreen: 'rgb( 50, 205, 50)',
  3721. linen: 'rgb(250, 240, 230)',
  3722. magenta: 'rgb(255, 0, 255)',
  3723. maroon: 'rgb(128, 0, 0)',
  3724. mediumaquamarine: 'rgb(102, 205, 170)',
  3725. mediumblue: 'rgb( 0, 0, 205)',
  3726. mediumorchid: 'rgb(186, 85, 211)',
  3727. mediumpurple: 'rgb(147, 112, 219)',
  3728. mediumseagreen: 'rgb( 60, 179, 113)',
  3729. mediumslateblue: 'rgb(123, 104, 238)',
  3730. mediumspringgreen: 'rgb( 0, 250, 154)',
  3731. mediumturquoise: 'rgb( 72, 209, 204)',
  3732. mediumvioletred: 'rgb(199, 21, 133)',
  3733. midnightblue: 'rgb( 25, 25, 112)',
  3734. mintcream: 'rgb(245, 255, 250)',
  3735. mistyrose: 'rgb(255, 228, 225)',
  3736. moccasin: 'rgb(255, 228, 181)',
  3737. navajowhite: 'rgb(255, 222, 173)',
  3738. navy: 'rgb( 0, 0, 128)',
  3739. oldlace: 'rgb(253, 245, 230)',
  3740. olive: 'rgb(128, 128, 0)',
  3741. olivedrab: 'rgb(107, 142, 35)',
  3742. orange: 'rgb(255, 165, 0)',
  3743. orangered: 'rgb(255, 69, 0)',
  3744. orchid: 'rgb(218, 112, 214)',
  3745. palegoldenrod: 'rgb(238, 232, 170)',
  3746. palegreen: 'rgb(152, 251, 152)',
  3747. paleturquoise: 'rgb(175, 238, 238)',
  3748. palevioletred: 'rgb(219, 112, 147)',
  3749. papayawhip: 'rgb(255, 239, 213)',
  3750. peachpuff: 'rgb(255, 218, 185)',
  3751. peru: 'rgb(205, 133, 63)',
  3752. pink: 'rgb(255, 192, 203)',
  3753. plum: 'rgb(221, 160, 221)',
  3754. powderblue: 'rgb(176, 224, 230)',
  3755. purple: 'rgb(128, 0, 128)',
  3756. red: 'rgb(255, 0, 0)',
  3757. rosybrown: 'rgb(188, 143, 143)',
  3758. royalblue: 'rgb( 65, 105, 225)',
  3759. saddlebrown: 'rgb(139, 69, 19)',
  3760. salmon: 'rgb(250, 128, 114)',
  3761. sandybrown: 'rgb(244, 164, 96)',
  3762. seagreen: 'rgb( 46, 139, 87)',
  3763. seashell: 'rgb(255, 245, 238)',
  3764. sienna: 'rgb(160, 82, 45)',
  3765. silver: 'rgb(192, 192, 192)',
  3766. skyblue: 'rgb(135, 206, 235)',
  3767. slateblue: 'rgb(106, 90, 205)',
  3768. slategray: 'rgb(112, 128, 144)',
  3769. slategrey: 'rgb(112, 128, 144)',
  3770. snow: 'rgb(255, 250, 250)',
  3771. springgreen: 'rgb( 0, 255, 127)',
  3772. steelblue: 'rgb( 70, 130, 180)',
  3773. tan: 'rgb(210, 180, 140)',
  3774. teal: 'rgb( 0, 128, 128)',
  3775. thistle: 'rgb(216, 191, 216)',
  3776. tomato: 'rgb(255, 99, 71)',
  3777. turquoise: 'rgb( 64, 224, 208)',
  3778. violet: 'rgb(238, 130, 238)',
  3779. wheat: 'rgb(245, 222, 179)',
  3780. white: 'rgb(255, 255, 255)',
  3781. whitesmoke: 'rgb(245, 245, 245)',
  3782. yellow: 'rgb(255, 255, 0)',
  3783. yellowgreen: 'rgb(154, 205, 50)'
  3784. };
  3785. // class: $.jqplot.AxisLabelRenderer
  3786. // Renderer to place labels on the axes.
  3787. $.jqplot.AxisLabelRenderer = function(options) {
  3788. // Group: Properties
  3789. $.jqplot.ElemContainer.call(this);
  3790. // name of the axis associated with this tick
  3791. this.axis;
  3792. // prop: show
  3793. // whether or not to show the tick (mark and label).
  3794. this.show = true;
  3795. // prop: label
  3796. // The text or html for the label.
  3797. this.label = '';
  3798. this.fontFamily = null;
  3799. this.fontSize = null;
  3800. this.textColor = null;
  3801. this._elem;
  3802. // prop: escapeHTML
  3803. // true to escape HTML entities in the label.
  3804. this.escapeHTML = false;
  3805. $.extend(true, this, options);
  3806. };
  3807. $.jqplot.AxisLabelRenderer.prototype = new $.jqplot.ElemContainer();
  3808. $.jqplot.AxisLabelRenderer.prototype.constructor = $.jqplot.AxisLabelRenderer;
  3809. $.jqplot.AxisLabelRenderer.prototype.init = function(options) {
  3810. $.extend(true, this, options);
  3811. };
  3812. $.jqplot.AxisLabelRenderer.prototype.draw = function(ctx, plot) {
  3813. // Memory Leaks patch
  3814. if (this._elem) {
  3815. this._elem.emptyForce();
  3816. this._elem = null;
  3817. }
  3818. this._elem = $('<div style="position:absolute;" class="jqplot-'+this.axis+'-label"></div>');
  3819. if (Number(this.label)) {
  3820. this._elem.css('white-space', 'nowrap');
  3821. }
  3822. if (!this.escapeHTML) {
  3823. this._elem.html(this.label);
  3824. }
  3825. else {
  3826. this._elem.text(this.label);
  3827. }
  3828. if (this.fontFamily) {
  3829. this._elem.css('font-family', this.fontFamily);
  3830. }
  3831. if (this.fontSize) {
  3832. this._elem.css('font-size', this.fontSize);
  3833. }
  3834. if (this.textColor) {
  3835. this._elem.css('color', this.textColor);
  3836. }
  3837. return this._elem;
  3838. };
  3839. $.jqplot.AxisLabelRenderer.prototype.pack = function() {
  3840. };
  3841. // class: $.jqplot.AxisTickRenderer
  3842. // A "tick" object showing the value of a tick/gridline on the plot.
  3843. $.jqplot.AxisTickRenderer = function(options) {
  3844. // Group: Properties
  3845. $.jqplot.ElemContainer.call(this);
  3846. // prop: mark
  3847. // tick mark on the axis. One of 'inside', 'outside', 'cross', '' or null.
  3848. this.mark = 'outside';
  3849. // name of the axis associated with this tick
  3850. this.axis;
  3851. // prop: showMark
  3852. // whether or not to show the mark on the axis.
  3853. this.showMark = true;
  3854. // prop: showGridline
  3855. // whether or not to draw the gridline on the grid at this tick.
  3856. this.showGridline = true;
  3857. // prop: isMinorTick
  3858. // if this is a minor tick.
  3859. this.isMinorTick = false;
  3860. // prop: size
  3861. // Length of the tick beyond the grid in pixels.
  3862. // DEPRECATED: This has been superceeded by markSize
  3863. this.size = 4;
  3864. // prop: markSize
  3865. // Length of the tick marks in pixels. For 'cross' style, length
  3866. // will be stoked above and below axis, so total length will be twice this.
  3867. this.markSize = 6;
  3868. // prop: show
  3869. // whether or not to show the tick (mark and label).
  3870. // Setting this to false requires more testing. It is recommended
  3871. // to set showLabel and showMark to false instead.
  3872. this.show = true;
  3873. // prop: showLabel
  3874. // whether or not to show the label.
  3875. this.showLabel = true;
  3876. this.label = null;
  3877. this.value = null;
  3878. this._styles = {};
  3879. // prop: formatter
  3880. // A class of a formatter for the tick text. sprintf by default.
  3881. this.formatter = $.jqplot.DefaultTickFormatter;
  3882. // prop: prefix
  3883. // String to prepend to the tick label.
  3884. // Prefix is prepended to the formatted tick label.
  3885. this.prefix = '';
  3886. // prop: suffix
  3887. // String to append to the tick label.
  3888. // Suffix is appended to the formatted tick label.
  3889. this.suffix = '';
  3890. // prop: formatString
  3891. // string passed to the formatter.
  3892. this.formatString = '';
  3893. // prop: fontFamily
  3894. // css spec for the font-family css attribute.
  3895. this.fontFamily;
  3896. // prop: fontSize
  3897. // css spec for the font-size css attribute.
  3898. this.fontSize;
  3899. // prop: textColor
  3900. // css spec for the color attribute.
  3901. this.textColor;
  3902. // prop: escapeHTML
  3903. // true to escape HTML entities in the label.
  3904. this.escapeHTML = false;
  3905. this._elem;
  3906. this._breakTick = false;
  3907. $.extend(true, this, options);
  3908. };
  3909. $.jqplot.AxisTickRenderer.prototype.init = function(options) {
  3910. $.extend(true, this, options);
  3911. };
  3912. $.jqplot.AxisTickRenderer.prototype = new $.jqplot.ElemContainer();
  3913. $.jqplot.AxisTickRenderer.prototype.constructor = $.jqplot.AxisTickRenderer;
  3914. $.jqplot.AxisTickRenderer.prototype.setTick = function(value, axisName, isMinor) {
  3915. this.value = value;
  3916. this.axis = axisName;
  3917. if (isMinor) {
  3918. this.isMinorTick = true;
  3919. }
  3920. return this;
  3921. };
  3922. $.jqplot.AxisTickRenderer.prototype.draw = function() {
  3923. if (this.label === null) {
  3924. this.label = this.prefix + this.formatter(this.formatString, this.value) + this.suffix;
  3925. }
  3926. var style = {position: 'absolute'};
  3927. if (Number(this.label)) {
  3928. style['whitSpace'] = 'nowrap';
  3929. }
  3930. // Memory Leaks patch
  3931. if (this._elem) {
  3932. this._elem.emptyForce();
  3933. this._elem = null;
  3934. }
  3935. this._elem = $(document.createElement('div'));
  3936. this._elem.addClass("jqplot-"+this.axis+"-tick");
  3937. if (!this.escapeHTML) {
  3938. this._elem.html(this.label);
  3939. }
  3940. else {
  3941. this._elem.text(this.label);
  3942. }
  3943. this._elem.css(style);
  3944. for (var s in this._styles) {
  3945. this._elem.css(s, this._styles[s]);
  3946. }
  3947. if (this.fontFamily) {
  3948. this._elem.css('font-family', this.fontFamily);
  3949. }
  3950. if (this.fontSize) {
  3951. this._elem.css('font-size', this.fontSize);
  3952. }
  3953. if (this.textColor) {
  3954. this._elem.css('color', this.textColor);
  3955. }
  3956. if (this._breakTick) {
  3957. this._elem.addClass('jqplot-breakTick');
  3958. }
  3959. return this._elem;
  3960. };
  3961. $.jqplot.DefaultTickFormatter = function (format, val) {
  3962. if (typeof val == 'number') {
  3963. if (!format) {
  3964. format = $.jqplot.config.defaultTickFormatString;
  3965. }
  3966. return $.jqplot.sprintf(format, val);
  3967. }
  3968. else {
  3969. return String(val);
  3970. }
  3971. };
  3972. $.jqplot.PercentTickFormatter = function (format, val) {
  3973. if (typeof val == 'number') {
  3974. val = 100 * val;
  3975. if (!format) {
  3976. format = $.jqplot.config.defaultTickFormatString;
  3977. }
  3978. return $.jqplot.sprintf(format, val);
  3979. }
  3980. else {
  3981. return String(val);
  3982. }
  3983. };
  3984. $.jqplot.AxisTickRenderer.prototype.pack = function() {
  3985. };
  3986. // Class: $.jqplot.CanvasGridRenderer
  3987. // The default jqPlot grid renderer, creating a grid on a canvas element.
  3988. // The renderer has no additional options beyond the <Grid> class.
  3989. $.jqplot.CanvasGridRenderer = function(){
  3990. this.shadowRenderer = new $.jqplot.ShadowRenderer();
  3991. };
  3992. // called with context of Grid object
  3993. $.jqplot.CanvasGridRenderer.prototype.init = function(options) {
  3994. this._ctx;
  3995. $.extend(true, this, options);
  3996. // set the shadow renderer options
  3997. var sopts = {lineJoin:'miter', lineCap:'round', fill:false, isarc:false, angle:this.shadowAngle, offset:this.shadowOffset, alpha:this.shadowAlpha, depth:this.shadowDepth, lineWidth:this.shadowWidth, closePath:false, strokeStyle:this.shadowColor};
  3998. this.renderer.shadowRenderer.init(sopts);
  3999. };
  4000. // called with context of Grid.
  4001. $.jqplot.CanvasGridRenderer.prototype.createElement = function(plot) {
  4002. var elem;
  4003. // Memory Leaks patch
  4004. if (this._elem) {
  4005. if ($.jqplot.use_excanvas && window.G_vmlCanvasManager.uninitElement !== undefined) {
  4006. elem = this._elem.get(0);
  4007. window.G_vmlCanvasManager.uninitElement(elem);
  4008. elem = null;
  4009. }
  4010. this._elem.emptyForce();
  4011. this._elem = null;
  4012. }
  4013. elem = plot.canvasManager.getCanvas();
  4014. var w = this._plotDimensions.width;
  4015. var h = this._plotDimensions.height;
  4016. elem.width = w;
  4017. elem.height = h;
  4018. this._elem = $(elem);
  4019. this._elem.addClass('jqplot-grid-canvas');
  4020. this._elem.css({ position: 'absolute', left: 0, top: 0 });
  4021. elem = plot.canvasManager.initCanvas(elem);
  4022. this._top = this._offsets.top;
  4023. this._bottom = h - this._offsets.bottom;
  4024. this._left = this._offsets.left;
  4025. this._right = w - this._offsets.right;
  4026. this._width = this._right - this._left;
  4027. this._height = this._bottom - this._top;
  4028. // avoid memory leak
  4029. elem = null;
  4030. return this._elem;
  4031. };
  4032. $.jqplot.CanvasGridRenderer.prototype.draw = function() {
  4033. this._ctx = this._elem.get(0).getContext("2d");
  4034. var ctx = this._ctx;
  4035. var axes = this._axes;
  4036. // Add the grid onto the grid canvas. This is the bottom most layer.
  4037. ctx.save();
  4038. ctx.clearRect(0, 0, this._plotDimensions.width, this._plotDimensions.height);
  4039. ctx.fillStyle = this.backgroundColor || this.background;
  4040. ctx.fillRect(this._left, this._top, this._width, this._height);
  4041. ctx.save();
  4042. ctx.lineJoin = 'miter';
  4043. ctx.lineCap = 'butt';
  4044. ctx.lineWidth = this.gridLineWidth;
  4045. ctx.strokeStyle = this.gridLineColor;
  4046. var b, e, s, m;
  4047. var ax = ['xaxis', 'yaxis', 'x2axis', 'y2axis'];
  4048. for (var i=4; i>0; i--) {
  4049. var name = ax[i-1];
  4050. var axis = axes[name];
  4051. var ticks = axis._ticks;
  4052. var numticks = ticks.length;
  4053. if (axis.show) {
  4054. if (axis.drawBaseline) {
  4055. var bopts = {};
  4056. if (axis.baselineWidth !== null) {
  4057. bopts.lineWidth = axis.baselineWidth;
  4058. }
  4059. if (axis.baselineColor !== null) {
  4060. bopts.strokeStyle = axis.baselineColor;
  4061. }
  4062. switch (name) {
  4063. case 'xaxis':
  4064. drawLine (this._left, this._bottom, this._right, this._bottom, bopts);
  4065. break;
  4066. case 'yaxis':
  4067. drawLine (this._left, this._bottom, this._left, this._top, bopts);
  4068. break;
  4069. case 'x2axis':
  4070. drawLine (this._left, this._bottom, this._right, this._bottom, bopts);
  4071. break;
  4072. case 'y2axis':
  4073. drawLine (this._right, this._bottom, this._right, this._top, bopts);
  4074. break;
  4075. }
  4076. }
  4077. for (var j=numticks; j>0; j--) {
  4078. var t = ticks[j-1];
  4079. if (t.show) {
  4080. var pos = Math.round(axis.u2p(t.value)) + 0.5;
  4081. switch (name) {
  4082. case 'xaxis':
  4083. // draw the grid line if we should
  4084. if (t.showGridline && this.drawGridlines && ((!t.isMinorTick && axis.drawMajorGridlines) || (t.isMinorTick && axis.drawMinorGridlines)) ) {
  4085. drawLine(pos, this._top, pos, this._bottom);
  4086. }
  4087. // draw the mark
  4088. if (t.showMark && t.mark && ((!t.isMinorTick && axis.drawMajorTickMarks) || (t.isMinorTick && axis.drawMinorTickMarks)) ) {
  4089. s = t.markSize;
  4090. m = t.mark;
  4091. var pos = Math.round(axis.u2p(t.value)) + 0.5;
  4092. switch (m) {
  4093. case 'outside':
  4094. b = this._bottom;
  4095. e = this._bottom+s;
  4096. break;
  4097. case 'inside':
  4098. b = this._bottom-s;
  4099. e = this._bottom;
  4100. break;
  4101. case 'cross':
  4102. b = this._bottom-s;
  4103. e = this._bottom+s;
  4104. break;
  4105. default:
  4106. b = this._bottom;
  4107. e = this._bottom+s;
  4108. break;
  4109. }
  4110. // draw the shadow
  4111. if (this.shadow) {
  4112. this.renderer.shadowRenderer.draw(ctx, [[pos,b],[pos,e]], {lineCap:'butt', lineWidth:this.gridLineWidth, offset:this.gridLineWidth*0.75, depth:2, fill:false, closePath:false});
  4113. }
  4114. // draw the line
  4115. drawLine(pos, b, pos, e);
  4116. }
  4117. break;
  4118. case 'yaxis':
  4119. // draw the grid line
  4120. if (t.showGridline && this.drawGridlines && ((!t.isMinorTick && axis.drawMajorGridlines) || (t.isMinorTick && axis.drawMinorGridlines)) ) {
  4121. drawLine(this._right, pos, this._left, pos);
  4122. }
  4123. // draw the mark
  4124. if (t.showMark && t.mark && ((!t.isMinorTick && axis.drawMajorTickMarks) || (t.isMinorTick && axis.drawMinorTickMarks)) ) {
  4125. s = t.markSize;
  4126. m = t.mark;
  4127. var pos = Math.round(axis.u2p(t.value)) + 0.5;
  4128. switch (m) {
  4129. case 'outside':
  4130. b = this._left-s;
  4131. e = this._left;
  4132. break;
  4133. case 'inside':
  4134. b = this._left;
  4135. e = this._left+s;
  4136. break;
  4137. case 'cross':
  4138. b = this._left-s;
  4139. e = this._left+s;
  4140. break;
  4141. default:
  4142. b = this._left-s;
  4143. e = this._left;
  4144. break;
  4145. }
  4146. // draw the shadow
  4147. if (this.shadow) {
  4148. this.renderer.shadowRenderer.draw(ctx, [[b, pos], [e, pos]], {lineCap:'butt', lineWidth:this.gridLineWidth*1.5, offset:this.gridLineWidth*0.75, fill:false, closePath:false});
  4149. }
  4150. drawLine(b, pos, e, pos, {strokeStyle:axis.borderColor});
  4151. }
  4152. break;
  4153. case 'x2axis':
  4154. // draw the grid line
  4155. if (t.showGridline && this.drawGridlines && ((!t.isMinorTick && axis.drawMajorGridlines) || (t.isMinorTick && axis.drawMinorGridlines)) ) {
  4156. drawLine(pos, this._bottom, pos, this._top);
  4157. }
  4158. // draw the mark
  4159. if (t.showMark && t.mark && ((!t.isMinorTick && axis.drawMajorTickMarks) || (t.isMinorTick && axis.drawMinorTickMarks)) ) {
  4160. s = t.markSize;
  4161. m = t.mark;
  4162. var pos = Math.round(axis.u2p(t.value)) + 0.5;
  4163. switch (m) {
  4164. case 'outside':
  4165. b = this._top-s;
  4166. e = this._top;
  4167. break;
  4168. case 'inside':
  4169. b = this._top;
  4170. e = this._top+s;
  4171. break;
  4172. case 'cross':
  4173. b = this._top-s;
  4174. e = this._top+s;
  4175. break;
  4176. default:
  4177. b = this._top-s;
  4178. e = this._top;
  4179. break;
  4180. }
  4181. // draw the shadow
  4182. if (this.shadow) {
  4183. this.renderer.shadowRenderer.draw(ctx, [[pos,b],[pos,e]], {lineCap:'butt', lineWidth:this.gridLineWidth, offset:this.gridLineWidth*0.75, depth:2, fill:false, closePath:false});
  4184. }
  4185. drawLine(pos, b, pos, e);
  4186. }
  4187. break;
  4188. case 'y2axis':
  4189. // draw the grid line
  4190. if (t.showGridline && this.drawGridlines && ((!t.isMinorTick && axis.drawMajorGridlines) || (t.isMinorTick && axis.drawMinorGridlines)) ) {
  4191. drawLine(this._left, pos, this._right, pos);
  4192. }
  4193. // draw the mark
  4194. if (t.showMark && t.mark && ((!t.isMinorTick && axis.drawMajorTickMarks) || (t.isMinorTick && axis.drawMinorTickMarks)) ) {
  4195. s = t.markSize;
  4196. m = t.mark;
  4197. var pos = Math.round(axis.u2p(t.value)) + 0.5;
  4198. switch (m) {
  4199. case 'outside':
  4200. b = this._right;
  4201. e = this._right+s;
  4202. break;
  4203. case 'inside':
  4204. b = this._right-s;
  4205. e = this._right;
  4206. break;
  4207. case 'cross':
  4208. b = this._right-s;
  4209. e = this._right+s;
  4210. break;
  4211. default:
  4212. b = this._right;
  4213. e = this._right+s;
  4214. break;
  4215. }
  4216. // draw the shadow
  4217. if (this.shadow) {
  4218. this.renderer.shadowRenderer.draw(ctx, [[b, pos], [e, pos]], {lineCap:'butt', lineWidth:this.gridLineWidth*1.5, offset:this.gridLineWidth*0.75, fill:false, closePath:false});
  4219. }
  4220. drawLine(b, pos, e, pos, {strokeStyle:axis.borderColor});
  4221. }
  4222. break;
  4223. default:
  4224. break;
  4225. }
  4226. }
  4227. }
  4228. t = null;
  4229. }
  4230. axis = null;
  4231. ticks = null;
  4232. }
  4233. // Now draw grid lines for additional y axes
  4234. //////
  4235. // TO DO: handle yMidAxis
  4236. //////
  4237. ax = ['y3axis', 'y4axis', 'y5axis', 'y6axis', 'y7axis', 'y8axis', 'y9axis', 'yMidAxis'];
  4238. for (var i=7; i>0; i--) {
  4239. var axis = axes[ax[i-1]];
  4240. var ticks = axis._ticks;
  4241. if (axis.show) {
  4242. var tn = ticks[axis.numberTicks-1];
  4243. var t0 = ticks[0];
  4244. var left = axis.getLeft();
  4245. var points = [[left, tn.getTop() + tn.getHeight()/2], [left, t0.getTop() + t0.getHeight()/2 + 1.0]];
  4246. // draw the shadow
  4247. if (this.shadow) {
  4248. this.renderer.shadowRenderer.draw(ctx, points, {lineCap:'butt', fill:false, closePath:false});
  4249. }
  4250. // draw the line
  4251. drawLine(points[0][0], points[0][1], points[1][0], points[1][1], {lineCap:'butt', strokeStyle:axis.borderColor, lineWidth:axis.borderWidth});
  4252. // draw the tick marks
  4253. for (var j=ticks.length; j>0; j--) {
  4254. var t = ticks[j-1];
  4255. s = t.markSize;
  4256. m = t.mark;
  4257. var pos = Math.round(axis.u2p(t.value)) + 0.5;
  4258. if (t.showMark && t.mark) {
  4259. switch (m) {
  4260. case 'outside':
  4261. b = left;
  4262. e = left+s;
  4263. break;
  4264. case 'inside':
  4265. b = left-s;
  4266. e = left;
  4267. break;
  4268. case 'cross':
  4269. b = left-s;
  4270. e = left+s;
  4271. break;
  4272. default:
  4273. b = left;
  4274. e = left+s;
  4275. break;
  4276. }
  4277. points = [[b,pos], [e,pos]];
  4278. // draw the shadow
  4279. if (this.shadow) {
  4280. this.renderer.shadowRenderer.draw(ctx, points, {lineCap:'butt', lineWidth:this.gridLineWidth*1.5, offset:this.gridLineWidth*0.75, fill:false, closePath:false});
  4281. }
  4282. // draw the line
  4283. drawLine(b, pos, e, pos, {strokeStyle:axis.borderColor});
  4284. }
  4285. t = null;
  4286. }
  4287. t0 = null;
  4288. }
  4289. axis = null;
  4290. ticks = null;
  4291. }
  4292. ctx.restore();
  4293. function drawLine(bx, by, ex, ey, opts) {
  4294. ctx.save();
  4295. opts = opts || {};
  4296. if (opts.lineWidth == null || opts.lineWidth != 0){
  4297. $.extend(true, ctx, opts);
  4298. ctx.beginPath();
  4299. ctx.moveTo(bx, by);
  4300. ctx.lineTo(ex, ey);
  4301. ctx.stroke();
  4302. ctx.restore();
  4303. }
  4304. }
  4305. if (this.shadow) {
  4306. var points = [[this._left, this._bottom], [this._right, this._bottom], [this._right, this._top]];
  4307. this.renderer.shadowRenderer.draw(ctx, points);
  4308. }
  4309. // Now draw border around grid. Use axis border definitions. start at
  4310. // upper left and go clockwise.
  4311. if (this.borderWidth != 0 && this.drawBorder) {
  4312. drawLine (this._left, this._top, this._right, this._top, {lineCap:'round', strokeStyle:axes.x2axis.borderColor, lineWidth:axes.x2axis.borderWidth});
  4313. drawLine (this._right, this._top, this._right, this._bottom, {lineCap:'round', strokeStyle:axes.y2axis.borderColor, lineWidth:axes.y2axis.borderWidth});
  4314. drawLine (this._right, this._bottom, this._left, this._bottom, {lineCap:'round', strokeStyle:axes.xaxis.borderColor, lineWidth:axes.xaxis.borderWidth});
  4315. drawLine (this._left, this._bottom, this._left, this._top, {lineCap:'round', strokeStyle:axes.yaxis.borderColor, lineWidth:axes.yaxis.borderWidth});
  4316. }
  4317. // ctx.lineWidth = this.borderWidth;
  4318. // ctx.strokeStyle = this.borderColor;
  4319. // ctx.strokeRect(this._left, this._top, this._width, this._height);
  4320. ctx.restore();
  4321. ctx = null;
  4322. axes = null;
  4323. };
  4324. // Class: $.jqplot.DivTitleRenderer
  4325. // The default title renderer for jqPlot. This class has no options beyond the <Title> class.
  4326. $.jqplot.DivTitleRenderer = function() {
  4327. };
  4328. $.jqplot.DivTitleRenderer.prototype.init = function(options) {
  4329. $.extend(true, this, options);
  4330. };
  4331. $.jqplot.DivTitleRenderer.prototype.draw = function() {
  4332. // Memory Leaks patch
  4333. if (this._elem) {
  4334. this._elem.emptyForce();
  4335. this._elem = null;
  4336. }
  4337. var r = this.renderer;
  4338. var elem = document.createElement('div');
  4339. this._elem = $(elem);
  4340. this._elem.addClass('jqplot-title');
  4341. if (!this.text) {
  4342. this.show = false;
  4343. this._elem.height(0);
  4344. this._elem.width(0);
  4345. }
  4346. else if (this.text) {
  4347. var color;
  4348. if (this.color) {
  4349. color = this.color;
  4350. }
  4351. else if (this.textColor) {
  4352. color = this.textColor;
  4353. }
  4354. // don't trust that a stylesheet is present, set the position.
  4355. var styles = {position:'absolute', top:'0px', left:'0px'};
  4356. if (this._plotWidth) {
  4357. styles['width'] = this._plotWidth+'px';
  4358. }
  4359. if (this.fontSize) {
  4360. styles['fontSize'] = this.fontSize;
  4361. }
  4362. if (typeof this.textAlign === 'string') {
  4363. styles['textAlign'] = this.textAlign;
  4364. }
  4365. else {
  4366. styles['textAlign'] = 'center';
  4367. }
  4368. if (color) {
  4369. styles['color'] = color;
  4370. }
  4371. if (this.paddingBottom) {
  4372. styles['paddingBottom'] = this.paddingBottom;
  4373. }
  4374. if (this.fontFamily) {
  4375. styles['fontFamily'] = this.fontFamily;
  4376. }
  4377. this._elem.css(styles);
  4378. if (this.escapeHtml) {
  4379. this._elem.text(this.text);
  4380. }
  4381. else {
  4382. this._elem.html(this.text);
  4383. }
  4384. // styletext += (this._plotWidth) ? 'width:'+this._plotWidth+'px;' : '';
  4385. // styletext += (this.fontSize) ? 'font-size:'+this.fontSize+';' : '';
  4386. // styletext += (this.textAlign) ? 'text-align:'+this.textAlign+';' : 'text-align:center;';
  4387. // styletext += (color) ? 'color:'+color+';' : '';
  4388. // styletext += (this.paddingBottom) ? 'padding-bottom:'+this.paddingBottom+';' : '';
  4389. // this._elem = $('<div class="jqplot-title" style="'+styletext+'">'+this.text+'</div>');
  4390. // if (this.fontFamily) {
  4391. // this._elem.css('font-family', this.fontFamily);
  4392. // }
  4393. }
  4394. elem = null;
  4395. return this._elem;
  4396. };
  4397. $.jqplot.DivTitleRenderer.prototype.pack = function() {
  4398. // nothing to do here
  4399. };
  4400. var dotlen = 0.1;
  4401. $.jqplot.LinePattern = function (ctx, pattern) {
  4402. var defaultLinePatterns = {
  4403. dotted: [ dotlen, $.jqplot.config.dotGapLength ],
  4404. dashed: [ $.jqplot.config.dashLength, $.jqplot.config.gapLength ],
  4405. solid: null
  4406. };
  4407. if (typeof pattern === 'string') {
  4408. if (pattern[0] === '.' || pattern[0] === '-') {
  4409. var s = pattern;
  4410. pattern = [];
  4411. for (var i=0, imax=s.length; i<imax; i++) {
  4412. if (s[i] === '.') {
  4413. pattern.push( dotlen );
  4414. }
  4415. else if (s[i] === '-') {
  4416. pattern.push( $.jqplot.config.dashLength );
  4417. }
  4418. else {
  4419. continue;
  4420. }
  4421. pattern.push( $.jqplot.config.gapLength );
  4422. }
  4423. }
  4424. else {
  4425. pattern = defaultLinePatterns[pattern];
  4426. }
  4427. }
  4428. if (!(pattern && pattern.length)) {
  4429. return ctx;
  4430. }
  4431. var patternIndex = 0;
  4432. var patternDistance = pattern[0];
  4433. var px = 0;
  4434. var py = 0;
  4435. var pathx0 = 0;
  4436. var pathy0 = 0;
  4437. var moveTo = function (x, y) {
  4438. ctx.moveTo( x, y );
  4439. px = x;
  4440. py = y;
  4441. pathx0 = x;
  4442. pathy0 = y;
  4443. };
  4444. var lineTo = function (x, y) {
  4445. var scale = ctx.lineWidth;
  4446. var dx = x - px;
  4447. var dy = y - py;
  4448. var dist = Math.sqrt(dx*dx+dy*dy);
  4449. if ((dist > 0) && (scale > 0)) {
  4450. dx /= dist;
  4451. dy /= dist;
  4452. while (true) {
  4453. var dp = scale * patternDistance;
  4454. if (dp < dist) {
  4455. px += dp * dx;
  4456. py += dp * dy;
  4457. if ((patternIndex & 1) == 0) {
  4458. ctx.lineTo( px, py );
  4459. }
  4460. else {
  4461. ctx.moveTo( px, py );
  4462. }
  4463. dist -= dp;
  4464. patternIndex++;
  4465. if (patternIndex >= pattern.length) {
  4466. patternIndex = 0;
  4467. }
  4468. patternDistance = pattern[patternIndex];
  4469. }
  4470. else {
  4471. px = x;
  4472. py = y;
  4473. if ((patternIndex & 1) == 0) {
  4474. ctx.lineTo( px, py );
  4475. }
  4476. else {
  4477. ctx.moveTo( px, py );
  4478. }
  4479. patternDistance -= dist / scale;
  4480. break;
  4481. }
  4482. }
  4483. }
  4484. };
  4485. var beginPath = function () {
  4486. ctx.beginPath();
  4487. };
  4488. var closePath = function () {
  4489. lineTo( pathx0, pathy0 );
  4490. };
  4491. return {
  4492. moveTo: moveTo,
  4493. lineTo: lineTo,
  4494. beginPath: beginPath,
  4495. closePath: closePath
  4496. };
  4497. };
  4498. // Class: $.jqplot.LineRenderer
  4499. // The default line renderer for jqPlot, this class has no options beyond the <Series> class.
  4500. // Draws series as a line.
  4501. $.jqplot.LineRenderer = function(){
  4502. this.shapeRenderer = new $.jqplot.ShapeRenderer();
  4503. this.shadowRenderer = new $.jqplot.ShadowRenderer();
  4504. };
  4505. // called with scope of series.
  4506. $.jqplot.LineRenderer.prototype.init = function(options, plot) {
  4507. // Group: Properties
  4508. //
  4509. options = options || {};
  4510. this._type='line';
  4511. this.renderer.animation = {
  4512. show: false,
  4513. direction: 'left',
  4514. speed: 2500,
  4515. _supported: true
  4516. };
  4517. // prop: smooth
  4518. // True to draw a smoothed (interpolated) line through the data points
  4519. // with automatically computed number of smoothing points.
  4520. // Set to an integer number > 2 to specify number of smoothing points
  4521. // to use between each data point.
  4522. this.renderer.smooth = false; // true or a number > 2 for smoothing.
  4523. this.renderer.tension = null; // null to auto compute or a number typically > 6. Fewer points requires higher tension.
  4524. // prop: constrainSmoothing
  4525. // True to use a more accurate smoothing algorithm that will
  4526. // not overshoot any data points. False to allow overshoot but
  4527. // produce a smoother looking line.
  4528. this.renderer.constrainSmoothing = true;
  4529. // this is smoothed data in grid coordinates, like gridData
  4530. this.renderer._smoothedData = [];
  4531. // this is smoothed data in plot units (plot coordinates), like plotData.
  4532. this.renderer._smoothedPlotData = [];
  4533. this.renderer._hiBandGridData = [];
  4534. this.renderer._lowBandGridData = [];
  4535. this.renderer._hiBandSmoothedData = [];
  4536. this.renderer._lowBandSmoothedData = [];
  4537. // prop: bandData
  4538. // Data used to draw error bands or confidence intervals above/below a line.
  4539. //
  4540. // bandData can be input in 3 forms. jqPlot will figure out which is the
  4541. // low band line and which is the high band line for all forms:
  4542. //
  4543. // A 2 dimensional array like [[yl1, yl2, ...], [yu1, yu2, ...]] where
  4544. // [yl1, yl2, ...] are y values of the lower line and
  4545. // [yu1, yu2, ...] are y values of the upper line.
  4546. // In this case there must be the same number of y data points as data points
  4547. // in the series and the bands will inherit the x values of the series.
  4548. //
  4549. // A 2 dimensional array like [[[xl1, yl1], [xl2, yl2], ...], [[xh1, yh1], [xh2, yh2], ...]]
  4550. // where [xl1, yl1] are x,y data points for the lower line and
  4551. // [xh1, yh1] are x,y data points for the high line.
  4552. // x values do not have to correspond to the x values of the series and can
  4553. // be of any arbitrary length.
  4554. //
  4555. // Can be of form [[yl1, yu1], [yl2, yu2], [yl3, yu3], ...] where
  4556. // there must be 3 or more arrays and there must be the same number of arrays
  4557. // as there are data points in the series. In this case,
  4558. // [yl1, yu1] specifies the lower and upper y values for the 1st
  4559. // data point and so on. The bands will inherit the x
  4560. // values from the series.
  4561. this.renderer.bandData = [];
  4562. // Group: bands
  4563. // Banding around line, e.g error bands or confidence intervals.
  4564. this.renderer.bands = {
  4565. // prop: show
  4566. // true to show the bands. If bandData or interval is
  4567. // supplied, show will be set to true by default.
  4568. show: false,
  4569. hiData: [],
  4570. lowData: [],
  4571. // prop: color
  4572. // color of lines at top and bottom of bands [default: series color].
  4573. color: this.color,
  4574. // prop: showLines
  4575. // True to show lines at top and bottom of bands [default: false].
  4576. showLines: false,
  4577. // prop: fill
  4578. // True to fill area between bands [default: true].
  4579. fill: true,
  4580. // prop: fillColor
  4581. // css color spec for filled area. [default: series color].
  4582. fillColor: null,
  4583. _min: null,
  4584. _max: null,
  4585. // prop: interval
  4586. // User specified interval above and below line for bands [default: '3%''].
  4587. // Can be a value like 3 or a string like '3%'
  4588. // or an upper/lower array like [1, -2] or ['2%', '-1.5%']
  4589. interval: '3%'
  4590. };
  4591. var lopts = {highlightMouseOver: options.highlightMouseOver, highlightMouseDown: options.highlightMouseDown, highlightColor: options.highlightColor};
  4592. delete (options.highlightMouseOver);
  4593. delete (options.highlightMouseDown);
  4594. delete (options.highlightColor);
  4595. $.extend(true, this.renderer, options);
  4596. this.renderer.options = options;
  4597. // if we are given some band data, and bands aren't explicity set to false in options, turn them on.
  4598. if (this.renderer.bandData.length > 1 && (!options.bands || options.bands.show == null)) {
  4599. this.renderer.bands.show = true;
  4600. }
  4601. // if we are given an interval, and bands aren't explicity set to false in options, turn them on.
  4602. else if (options.bands && options.bands.show == null && options.bands.interval != null) {
  4603. this.renderer.bands.show = true;
  4604. }
  4605. // if plot is filled, turn off bands.
  4606. if (this.fill) {
  4607. this.renderer.bands.show = false;
  4608. }
  4609. if (this.renderer.bands.show) {
  4610. this.renderer.initBands.call(this, this.renderer.options, plot);
  4611. }
  4612. // smoothing is not compatible with stacked lines, disable
  4613. if (this._stack) {
  4614. this.renderer.smooth = false;
  4615. }
  4616. // set the shape renderer options
  4617. var opts = {lineJoin:this.lineJoin, lineCap:this.lineCap, fill:this.fill, isarc:false, strokeStyle:this.color, fillStyle:this.fillColor, lineWidth:this.lineWidth, linePattern:this.linePattern, closePath:this.fill};
  4618. this.renderer.shapeRenderer.init(opts);
  4619. var shadow_offset = options.shadowOffset;
  4620. // set the shadow renderer options
  4621. if (shadow_offset == null) {
  4622. // scale the shadowOffset to the width of the line.
  4623. if (this.lineWidth > 2.5) {
  4624. shadow_offset = 1.25 * (1 + (Math.atan((this.lineWidth/2.5))/0.785398163 - 1)*0.6);
  4625. // var shadow_offset = this.shadowOffset;
  4626. }
  4627. // for skinny lines, don't make such a big shadow.
  4628. else {
  4629. shadow_offset = 1.25 * Math.atan((this.lineWidth/2.5))/0.785398163;
  4630. }
  4631. }
  4632. var sopts = {lineJoin:this.lineJoin, lineCap:this.lineCap, fill:this.fill, isarc:false, angle:this.shadowAngle, offset:shadow_offset, alpha:this.shadowAlpha, depth:this.shadowDepth, lineWidth:this.lineWidth, linePattern:this.linePattern, closePath:this.fill};
  4633. this.renderer.shadowRenderer.init(sopts);
  4634. this._areaPoints = [];
  4635. this._boundingBox = [[],[]];
  4636. if (!this.isTrendline && this.fill || this.renderer.bands.show) {
  4637. // Group: Properties
  4638. //
  4639. // prop: highlightMouseOver
  4640. // True to highlight area on a filled plot when moused over.
  4641. // This must be false to enable highlightMouseDown to highlight when clicking on an area on a filled plot.
  4642. this.highlightMouseOver = true;
  4643. // prop: highlightMouseDown
  4644. // True to highlight when a mouse button is pressed over an area on a filled plot.
  4645. // This will be disabled if highlightMouseOver is true.
  4646. this.highlightMouseDown = false;
  4647. // prop: highlightColor
  4648. // color to use when highlighting an area on a filled plot.
  4649. this.highlightColor = null;
  4650. // if user has passed in highlightMouseDown option and not set highlightMouseOver, disable highlightMouseOver
  4651. if (lopts.highlightMouseDown && lopts.highlightMouseOver == null) {
  4652. lopts.highlightMouseOver = false;
  4653. }
  4654. $.extend(true, this, {highlightMouseOver: lopts.highlightMouseOver, highlightMouseDown: lopts.highlightMouseDown, highlightColor: lopts.highlightColor});
  4655. if (!this.highlightColor) {
  4656. var fc = (this.renderer.bands.show) ? this.renderer.bands.fillColor : this.fillColor;
  4657. this.highlightColor = $.jqplot.computeHighlightColors(fc);
  4658. }
  4659. // turn off (disable) the highlighter plugin
  4660. if (this.highlighter) {
  4661. this.highlighter.show = false;
  4662. }
  4663. }
  4664. if (!this.isTrendline && plot) {
  4665. plot.plugins.lineRenderer = {};
  4666. plot.postInitHooks.addOnce(postInit);
  4667. plot.postDrawHooks.addOnce(postPlotDraw);
  4668. plot.eventListenerHooks.addOnce('jqplotMouseMove', handleMove);
  4669. plot.eventListenerHooks.addOnce('jqplotMouseDown', handleMouseDown);
  4670. plot.eventListenerHooks.addOnce('jqplotMouseUp', handleMouseUp);
  4671. plot.eventListenerHooks.addOnce('jqplotClick', handleClick);
  4672. plot.eventListenerHooks.addOnce('jqplotRightClick', handleRightClick);
  4673. }
  4674. };
  4675. $.jqplot.LineRenderer.prototype.initBands = function(options, plot) {
  4676. // use bandData if no data specified in bands option
  4677. //var bd = this.renderer.bandData;
  4678. var bd = options.bandData || [];
  4679. var bands = this.renderer.bands;
  4680. bands.hiData = [];
  4681. bands.lowData = [];
  4682. var data = this.data;
  4683. bands._max = null;
  4684. bands._min = null;
  4685. // If 2 arrays, and each array greater than 2 elements, assume it is hi and low data bands of y values.
  4686. if (bd.length == 2) {
  4687. // Do we have an array of x,y values?
  4688. // like [[[1,1], [2,4], [3,3]], [[1,3], [2,6], [3,5]]]
  4689. if ($.isArray(bd[0][0])) {
  4690. // since an arbitrary array of points, spin through all of them to determine max and min lines.
  4691. var p;
  4692. var bdminidx = 0, bdmaxidx = 0;
  4693. for (var i = 0, l = bd[0].length; i<l; i++) {
  4694. p = bd[0][i];
  4695. if ((p[1] != null && p[1] > bands._max) || bands._max == null) {
  4696. bands._max = p[1];
  4697. }
  4698. if ((p[1] != null && p[1] < bands._min) || bands._min == null) {
  4699. bands._min = p[1];
  4700. }
  4701. }
  4702. for (var i = 0, l = bd[1].length; i<l; i++) {
  4703. p = bd[1][i];
  4704. if ((p[1] != null && p[1] > bands._max) || bands._max == null) {
  4705. bands._max = p[1];
  4706. bdmaxidx = 1;
  4707. }
  4708. if ((p[1] != null && p[1] < bands._min) || bands._min == null) {
  4709. bands._min = p[1];
  4710. bdminidx = 1;
  4711. }
  4712. }
  4713. if (bdmaxidx === bdminidx) {
  4714. bands.show = false;
  4715. }
  4716. bands.hiData = bd[bdmaxidx];
  4717. bands.lowData = bd[bdminidx];
  4718. }
  4719. // else data is arrays of y values
  4720. // like [[1,4,3], [3,6,5]]
  4721. // must have same number of band data points as points in series
  4722. else if (bd[0].length === data.length && bd[1].length === data.length) {
  4723. var hi = (bd[0][0] > bd[1][0]) ? 0 : 1;
  4724. var low = (hi) ? 0 : 1;
  4725. for (var i=0, l=data.length; i < l; i++) {
  4726. bands.hiData.push([data[i][0], bd[hi][i]]);
  4727. bands.lowData.push([data[i][0], bd[low][i]]);
  4728. }
  4729. }
  4730. // we don't have proper data array, don't show bands.
  4731. else {
  4732. bands.show = false;
  4733. }
  4734. }
  4735. // if more than 2 arrays, have arrays of [ylow, yhi] values.
  4736. // note, can't distinguish case of [[ylow, yhi], [ylow, yhi]] from [[ylow, ylow], [yhi, yhi]]
  4737. // this is assumed to be of the latter form.
  4738. else if (bd.length > 2 && !$.isArray(bd[0][0])) {
  4739. var hi = (bd[0][0] > bd[0][1]) ? 0 : 1;
  4740. var low = (hi) ? 0 : 1;
  4741. for (var i=0, l=bd.length; i<l; i++) {
  4742. bands.hiData.push([data[i][0], bd[i][hi]]);
  4743. bands.lowData.push([data[i][0], bd[i][low]]);
  4744. }
  4745. }
  4746. // don't have proper data, auto calculate
  4747. else {
  4748. var intrv = bands.interval;
  4749. var a = null;
  4750. var b = null;
  4751. var afunc = null;
  4752. var bfunc = null;
  4753. if ($.isArray(intrv)) {
  4754. a = intrv[0];
  4755. b = intrv[1];
  4756. }
  4757. else {
  4758. a = intrv;
  4759. }
  4760. if (isNaN(a)) {
  4761. // we have a string
  4762. if (a.charAt(a.length - 1) === '%') {
  4763. afunc = 'multiply';
  4764. a = parseFloat(a)/100 + 1;
  4765. }
  4766. }
  4767. else {
  4768. a = parseFloat(a);
  4769. afunc = 'add';
  4770. }
  4771. if (b !== null && isNaN(b)) {
  4772. // we have a string
  4773. if (b.charAt(b.length - 1) === '%') {
  4774. bfunc = 'multiply';
  4775. b = parseFloat(b)/100 + 1;
  4776. }
  4777. }
  4778. else if (b !== null) {
  4779. b = parseFloat(b);
  4780. bfunc = 'add';
  4781. }
  4782. if (a !== null) {
  4783. if (b === null) {
  4784. b = -a;
  4785. bfunc = afunc;
  4786. if (bfunc === 'multiply') {
  4787. b += 2;
  4788. }
  4789. }
  4790. // make sure a always applies to hi band.
  4791. if (a < b) {
  4792. var temp = a;
  4793. a = b;
  4794. b = temp;
  4795. temp = afunc;
  4796. afunc = bfunc;
  4797. bfunc = temp;
  4798. }
  4799. for (var i=0, l = data.length; i < l; i++) {
  4800. switch (afunc) {
  4801. case 'add':
  4802. bands.hiData.push([data[i][0], data[i][1] + a]);
  4803. break;
  4804. case 'multiply':
  4805. bands.hiData.push([data[i][0], data[i][1] * a]);
  4806. break;
  4807. }
  4808. switch (bfunc) {
  4809. case 'add':
  4810. bands.lowData.push([data[i][0], data[i][1] + b]);
  4811. break;
  4812. case 'multiply':
  4813. bands.lowData.push([data[i][0], data[i][1] * b]);
  4814. break;
  4815. }
  4816. }
  4817. }
  4818. else {
  4819. bands.show = false;
  4820. }
  4821. }
  4822. var hd = bands.hiData;
  4823. var ld = bands.lowData;
  4824. for (var i = 0, l = hd.length; i<l; i++) {
  4825. if ((hd[i][1] != null && hd[i][1] > bands._max) || bands._max == null) {
  4826. bands._max = hd[i][1];
  4827. }
  4828. }
  4829. for (var i = 0, l = ld.length; i<l; i++) {
  4830. if ((ld[i][1] != null && ld[i][1] < bands._min) || bands._min == null) {
  4831. bands._min = ld[i][1];
  4832. }
  4833. }
  4834. // one last check for proper data
  4835. // these don't apply any more since allowing arbitrary x,y values
  4836. // if (bands.hiData.length != bands.lowData.length) {
  4837. // bands.show = false;
  4838. // }
  4839. // if (bands.hiData.length != this.data.length) {
  4840. // bands.show = false;
  4841. // }
  4842. if (bands.fillColor === null) {
  4843. var c = $.jqplot.getColorComponents(bands.color);
  4844. // now adjust alpha to differentiate fill
  4845. c[3] = c[3] * 0.5;
  4846. bands.fillColor = 'rgba(' + c[0] +', '+ c[1] +', '+ c[2] +', '+ c[3] + ')';
  4847. }
  4848. };
  4849. function getSteps (d, f) {
  4850. return (3.4182054+f) * Math.pow(d, -0.3534992);
  4851. }
  4852. function computeSteps (d1, d2) {
  4853. var s = Math.sqrt(Math.pow((d2[0]- d1[0]), 2) + Math.pow ((d2[1] - d1[1]), 2));
  4854. return 5.7648 * Math.log(s) + 7.4456;
  4855. }
  4856. function tanh (x) {
  4857. var a = (Math.exp(2*x) - 1) / (Math.exp(2*x) + 1);
  4858. return a;
  4859. }
  4860. //////////
  4861. // computeConstrainedSmoothedData
  4862. // An implementation of the constrained cubic spline interpolation
  4863. // method as presented in:
  4864. //
  4865. // Kruger, CJC, Constrained Cubic Spine Interpolation for Chemical Engineering Applications
  4866. // http://www.korf.co.uk/spline.pdf
  4867. //
  4868. // The implementation below borrows heavily from the sample Visual Basic
  4869. // implementation by CJC Kruger found in http://www.korf.co.uk/spline.xls
  4870. //
  4871. /////////
  4872. // called with scope of series
  4873. function computeConstrainedSmoothedData (gd) {
  4874. var smooth = this.renderer.smooth;
  4875. var dim = this.canvas.getWidth();
  4876. var xp = this._xaxis.series_p2u;
  4877. var yp = this._yaxis.series_p2u;
  4878. var steps =null;
  4879. var _steps = null;
  4880. var dist = gd.length/dim;
  4881. var _smoothedData = [];
  4882. var _smoothedPlotData = [];
  4883. if (!isNaN(parseFloat(smooth))) {
  4884. steps = parseFloat(smooth);
  4885. }
  4886. else {
  4887. steps = getSteps(dist, 0.5);
  4888. }
  4889. var yy = [];
  4890. var xx = [];
  4891. for (var i=0, l = gd.length; i<l; i++) {
  4892. yy.push(gd[i][1]);
  4893. xx.push(gd[i][0]);
  4894. }
  4895. function dxx(x1, x0) {
  4896. if (x1 - x0 == 0) {
  4897. return Math.pow(10,10);
  4898. }
  4899. else {
  4900. return x1 - x0;
  4901. }
  4902. }
  4903. var A, B, C, D;
  4904. // loop through each line segment. Have # points - 1 line segments. Nmber segments starting at 1.
  4905. var nmax = gd.length - 1;
  4906. for (var num = 1, gdl = gd.length; num<gdl; num++) {
  4907. var gxx = [];
  4908. var ggxx = [];
  4909. // point at each end of segment.
  4910. for (var j = 0; j < 2; j++) {
  4911. var i = num - 1 + j; // point number, 0 to # points.
  4912. if (i == 0 || i == nmax) {
  4913. gxx[j] = Math.pow(10, 10);
  4914. }
  4915. else if (yy[i+1] - yy[i] == 0 || yy[i] - yy[i-1] == 0) {
  4916. gxx[j] = 0;
  4917. }
  4918. else if (((xx[i+1] - xx[i]) / (yy[i+1] - yy[i]) + (xx[i] - xx[i-1]) / (yy[i] - yy[i-1])) == 0 ) {
  4919. gxx[j] = 0;
  4920. }
  4921. else if ( (yy[i+1] - yy[i]) * (yy[i] - yy[i-1]) < 0 ) {
  4922. gxx[j] = 0;
  4923. }
  4924. else {
  4925. gxx[j] = 2 / (dxx(xx[i + 1], xx[i]) / (yy[i + 1] - yy[i]) + dxx(xx[i], xx[i - 1]) / (yy[i] - yy[i - 1]));
  4926. }
  4927. }
  4928. // Reset first derivative (slope) at first and last point
  4929. if (num == 1) {
  4930. // First point has 0 2nd derivative
  4931. gxx[0] = 3 / 2 * (yy[1] - yy[0]) / dxx(xx[1], xx[0]) - gxx[1] / 2;
  4932. }
  4933. else if (num == nmax) {
  4934. // Last point has 0 2nd derivative
  4935. gxx[1] = 3 / 2 * (yy[nmax] - yy[nmax - 1]) / dxx(xx[nmax], xx[nmax - 1]) - gxx[0] / 2;
  4936. }
  4937. // Calc second derivative at points
  4938. ggxx[0] = -2 * (gxx[1] + 2 * gxx[0]) / dxx(xx[num], xx[num - 1]) + 6 * (yy[num] - yy[num - 1]) / Math.pow(dxx(xx[num], xx[num - 1]), 2);
  4939. ggxx[1] = 2 * (2 * gxx[1] + gxx[0]) / dxx(xx[num], xx[num - 1]) - 6 * (yy[num] - yy[num - 1]) / Math.pow(dxx(xx[num], xx[num - 1]), 2);
  4940. // Calc constants for cubic interpolation
  4941. D = 1 / 6 * (ggxx[1] - ggxx[0]) / dxx(xx[num], xx[num - 1]);
  4942. C = 1 / 2 * (xx[num] * ggxx[0] - xx[num - 1] * ggxx[1]) / dxx(xx[num], xx[num - 1]);
  4943. B = (yy[num] - yy[num - 1] - C * (Math.pow(xx[num], 2) - Math.pow(xx[num - 1], 2)) - D * (Math.pow(xx[num], 3) - Math.pow(xx[num - 1], 3))) / dxx(xx[num], xx[num - 1]);
  4944. A = yy[num - 1] - B * xx[num - 1] - C * Math.pow(xx[num - 1], 2) - D * Math.pow(xx[num - 1], 3);
  4945. var increment = (xx[num] - xx[num - 1]) / steps;
  4946. var temp, tempx;
  4947. for (var j = 0, l = steps; j < l; j++) {
  4948. temp = [];
  4949. tempx = xx[num - 1] + j * increment;
  4950. temp.push(tempx);
  4951. temp.push(A + B * tempx + C * Math.pow(tempx, 2) + D * Math.pow(tempx, 3));
  4952. _smoothedData.push(temp);
  4953. _smoothedPlotData.push([xp(temp[0]), yp(temp[1])]);
  4954. }
  4955. }
  4956. _smoothedData.push(gd[i]);
  4957. _smoothedPlotData.push([xp(gd[i][0]), yp(gd[i][1])]);
  4958. return [_smoothedData, _smoothedPlotData];
  4959. }
  4960. ///////
  4961. // computeHermiteSmoothedData
  4962. // A hermite spline smoothing of the plot data.
  4963. // This implementation is derived from the one posted
  4964. // by krypin on the jqplot-users mailing list:
  4965. //
  4966. // http://groups.google.com/group/jqplot-users/browse_thread/thread/748be6a445723cea?pli=1
  4967. //
  4968. // with a blog post:
  4969. //
  4970. // http://blog.statscollector.com/a-plugin-renderer-for-jqplot-to-draw-a-hermite-spline/
  4971. //
  4972. // and download of the original plugin:
  4973. //
  4974. // http://blog.statscollector.com/wp-content/uploads/2010/02/jqplot.hermiteSplineRenderer.js
  4975. //////////
  4976. // called with scope of series
  4977. function computeHermiteSmoothedData (gd) {
  4978. var smooth = this.renderer.smooth;
  4979. var tension = this.renderer.tension;
  4980. var dim = this.canvas.getWidth();
  4981. var xp = this._xaxis.series_p2u;
  4982. var yp = this._yaxis.series_p2u;
  4983. var steps =null;
  4984. var _steps = null;
  4985. var a = null;
  4986. var a1 = null;
  4987. var a2 = null;
  4988. var slope = null;
  4989. var slope2 = null;
  4990. var temp = null;
  4991. var t, s, h1, h2, h3, h4;
  4992. var TiX, TiY, Ti1X, Ti1Y;
  4993. var pX, pY, p;
  4994. var sd = [];
  4995. var spd = [];
  4996. var dist = gd.length/dim;
  4997. var min, max, stretch, scale, shift;
  4998. var _smoothedData = [];
  4999. var _smoothedPlotData = [];
  5000. if (!isNaN(parseFloat(smooth))) {
  5001. steps = parseFloat(smooth);
  5002. }
  5003. else {
  5004. steps = getSteps(dist, 0.5);
  5005. }
  5006. if (!isNaN(parseFloat(tension))) {
  5007. tension = parseFloat(tension);
  5008. }
  5009. for (var i=0, l = gd.length-1; i < l; i++) {
  5010. if (tension === null) {
  5011. slope = Math.abs((gd[i+1][1] - gd[i][1]) / (gd[i+1][0] - gd[i][0]));
  5012. min = 0.3;
  5013. max = 0.6;
  5014. stretch = (max - min)/2.0;
  5015. scale = 2.5;
  5016. shift = -1.4;
  5017. temp = slope/scale + shift;
  5018. a1 = stretch * tanh(temp) - stretch * tanh(shift) + min;
  5019. // if have both left and right line segments, will use minimum tension.
  5020. if (i > 0) {
  5021. slope2 = Math.abs((gd[i][1] - gd[i-1][1]) / (gd[i][0] - gd[i-1][0]));
  5022. }
  5023. temp = slope2/scale + shift;
  5024. a2 = stretch * tanh(temp) - stretch * tanh(shift) + min;
  5025. a = (a1 + a2)/2.0;
  5026. }
  5027. else {
  5028. a = tension;
  5029. }
  5030. for (t=0; t < steps; t++) {
  5031. s = t / steps;
  5032. h1 = (1 + 2*s)*Math.pow((1-s),2);
  5033. h2 = s*Math.pow((1-s),2);
  5034. h3 = Math.pow(s,2)*(3-2*s);
  5035. h4 = Math.pow(s,2)*(s-1);
  5036. if (gd[i-1]) {
  5037. TiX = a * (gd[i+1][0] - gd[i-1][0]);
  5038. TiY = a * (gd[i+1][1] - gd[i-1][1]);
  5039. } else {
  5040. TiX = a * (gd[i+1][0] - gd[i][0]);
  5041. TiY = a * (gd[i+1][1] - gd[i][1]);
  5042. }
  5043. if (gd[i+2]) {
  5044. Ti1X = a * (gd[i+2][0] - gd[i][0]);
  5045. Ti1Y = a * (gd[i+2][1] - gd[i][1]);
  5046. } else {
  5047. Ti1X = a * (gd[i+1][0] - gd[i][0]);
  5048. Ti1Y = a * (gd[i+1][1] - gd[i][1]);
  5049. }
  5050. pX = h1*gd[i][0] + h3*gd[i+1][0] + h2*TiX + h4*Ti1X;
  5051. pY = h1*gd[i][1] + h3*gd[i+1][1] + h2*TiY + h4*Ti1Y;
  5052. p = [pX, pY];
  5053. _smoothedData.push(p);
  5054. _smoothedPlotData.push([xp(pX), yp(pY)]);
  5055. }
  5056. }
  5057. _smoothedData.push(gd[l]);
  5058. _smoothedPlotData.push([xp(gd[l][0]), yp(gd[l][1])]);
  5059. return [_smoothedData, _smoothedPlotData];
  5060. }
  5061. // setGridData
  5062. // converts the user data values to grid coordinates and stores them
  5063. // in the gridData array.
  5064. // Called with scope of a series.
  5065. $.jqplot.LineRenderer.prototype.setGridData = function(plot) {
  5066. // recalculate the grid data
  5067. var xp = this._xaxis.series_u2p;
  5068. var yp = this._yaxis.series_u2p;
  5069. var data = this._plotData;
  5070. var pdata = this._prevPlotData;
  5071. this.gridData = [];
  5072. this._prevGridData = [];
  5073. this.renderer._smoothedData = [];
  5074. this.renderer._smoothedPlotData = [];
  5075. this.renderer._hiBandGridData = [];
  5076. this.renderer._lowBandGridData = [];
  5077. this.renderer._hiBandSmoothedData = [];
  5078. this.renderer._lowBandSmoothedData = [];
  5079. var bands = this.renderer.bands;
  5080. var hasNull = false;
  5081. for (var i=0, l=data.length; i < l; i++) {
  5082. // if not a line series or if no nulls in data, push the converted point onto the array.
  5083. if (data[i][0] != null && data[i][1] != null) {
  5084. this.gridData.push([xp.call(this._xaxis, data[i][0]), yp.call(this._yaxis, data[i][1])]);
  5085. }
  5086. // else if there is a null, preserve it.
  5087. else if (data[i][0] == null) {
  5088. hasNull = true;
  5089. this.gridData.push([null, yp.call(this._yaxis, data[i][1])]);
  5090. }
  5091. else if (data[i][1] == null) {
  5092. hasNull = true;
  5093. this.gridData.push([xp.call(this._xaxis, data[i][0]), null]);
  5094. }
  5095. // if not a line series or if no nulls in data, push the converted point onto the array.
  5096. if (pdata[i] != null && pdata[i][0] != null && pdata[i][1] != null) {
  5097. this._prevGridData.push([xp.call(this._xaxis, pdata[i][0]), yp.call(this._yaxis, pdata[i][1])]);
  5098. }
  5099. // else if there is a null, preserve it.
  5100. else if (pdata[i] != null && pdata[i][0] == null) {
  5101. this._prevGridData.push([null, yp.call(this._yaxis, pdata[i][1])]);
  5102. }
  5103. else if (pdata[i] != null && pdata[i][0] != null && pdata[i][1] == null) {
  5104. this._prevGridData.push([xp.call(this._xaxis, pdata[i][0]), null]);
  5105. }
  5106. }
  5107. // don't do smoothing or bands on broken lines.
  5108. if (hasNull) {
  5109. this.renderer.smooth = false;
  5110. if (this._type === 'line') {
  5111. bands.show = false;
  5112. }
  5113. }
  5114. if (this._type === 'line' && bands.show) {
  5115. for (var i=0, l=bands.hiData.length; i<l; i++) {
  5116. this.renderer._hiBandGridData.push([xp.call(this._xaxis, bands.hiData[i][0]), yp.call(this._yaxis, bands.hiData[i][1])]);
  5117. }
  5118. for (var i=0, l=bands.lowData.length; i<l; i++) {
  5119. this.renderer._lowBandGridData.push([xp.call(this._xaxis, bands.lowData[i][0]), yp.call(this._yaxis, bands.lowData[i][1])]);
  5120. }
  5121. }
  5122. // calculate smoothed data if enough points and no nulls
  5123. if (this._type === 'line' && this.renderer.smooth && this.gridData.length > 2) {
  5124. var ret;
  5125. if (this.renderer.constrainSmoothing) {
  5126. ret = computeConstrainedSmoothedData.call(this, this.gridData);
  5127. this.renderer._smoothedData = ret[0];
  5128. this.renderer._smoothedPlotData = ret[1];
  5129. if (bands.show) {
  5130. ret = computeConstrainedSmoothedData.call(this, this.renderer._hiBandGridData);
  5131. this.renderer._hiBandSmoothedData = ret[0];
  5132. ret = computeConstrainedSmoothedData.call(this, this.renderer._lowBandGridData);
  5133. this.renderer._lowBandSmoothedData = ret[0];
  5134. }
  5135. ret = null;
  5136. }
  5137. else {
  5138. ret = computeHermiteSmoothedData.call(this, this.gridData);
  5139. this.renderer._smoothedData = ret[0];
  5140. this.renderer._smoothedPlotData = ret[1];
  5141. if (bands.show) {
  5142. ret = computeHermiteSmoothedData.call(this, this.renderer._hiBandGridData);
  5143. this.renderer._hiBandSmoothedData = ret[0];
  5144. ret = computeHermiteSmoothedData.call(this, this.renderer._lowBandGridData);
  5145. this.renderer._lowBandSmoothedData = ret[0];
  5146. }
  5147. ret = null;
  5148. }
  5149. }
  5150. };
  5151. // makeGridData
  5152. // converts any arbitrary data values to grid coordinates and
  5153. // returns them. This method exists so that plugins can use a series'
  5154. // linerenderer to generate grid data points without overwriting the
  5155. // grid data associated with that series.
  5156. // Called with scope of a series.
  5157. $.jqplot.LineRenderer.prototype.makeGridData = function(data, plot) {
  5158. // recalculate the grid data
  5159. var xp = this._xaxis.series_u2p;
  5160. var yp = this._yaxis.series_u2p;
  5161. var gd = [];
  5162. var pgd = [];
  5163. this.renderer._smoothedData = [];
  5164. this.renderer._smoothedPlotData = [];
  5165. this.renderer._hiBandGridData = [];
  5166. this.renderer._lowBandGridData = [];
  5167. this.renderer._hiBandSmoothedData = [];
  5168. this.renderer._lowBandSmoothedData = [];
  5169. var bands = this.renderer.bands;
  5170. var hasNull = false;
  5171. for (var i=0; i<data.length; i++) {
  5172. // if not a line series or if no nulls in data, push the converted point onto the array.
  5173. if (data[i][0] != null && data[i][1] != null) {
  5174. if (this.step && i>0) {
  5175. gd.push([xp.call(this._xaxis, data[i][0]), yp.call(this._yaxis, data[i-1][1])]);
  5176. }
  5177. gd.push([xp.call(this._xaxis, data[i][0]), yp.call(this._yaxis, data[i][1])]);
  5178. }
  5179. // else if there is a null, preserve it.
  5180. else if (data[i][0] == null) {
  5181. hasNull = true;
  5182. gd.push([null, yp.call(this._yaxis, data[i][1])]);
  5183. }
  5184. else if (data[i][1] == null) {
  5185. hasNull = true;
  5186. gd.push([xp.call(this._xaxis, data[i][0]), null]);
  5187. }
  5188. }
  5189. // don't do smoothing or bands on broken lines.
  5190. if (hasNull) {
  5191. this.renderer.smooth = false;
  5192. if (this._type === 'line') {
  5193. bands.show = false;
  5194. }
  5195. }
  5196. if (this._type === 'line' && bands.show) {
  5197. for (var i=0, l=bands.hiData.length; i<l; i++) {
  5198. this.renderer._hiBandGridData.push([xp.call(this._xaxis, bands.hiData[i][0]), yp.call(this._yaxis, bands.hiData[i][1])]);
  5199. }
  5200. for (var i=0, l=bands.lowData.length; i<l; i++) {
  5201. this.renderer._lowBandGridData.push([xp.call(this._xaxis, bands.lowData[i][0]), yp.call(this._yaxis, bands.lowData[i][1])]);
  5202. }
  5203. }
  5204. if (this._type === 'line' && this.renderer.smooth && gd.length > 2) {
  5205. var ret;
  5206. if (this.renderer.constrainSmoothing) {
  5207. ret = computeConstrainedSmoothedData.call(this, gd);
  5208. this.renderer._smoothedData = ret[0];
  5209. this.renderer._smoothedPlotData = ret[1];
  5210. if (bands.show) {
  5211. ret = computeConstrainedSmoothedData.call(this, this.renderer._hiBandGridData);
  5212. this.renderer._hiBandSmoothedData = ret[0];
  5213. ret = computeConstrainedSmoothedData.call(this, this.renderer._lowBandGridData);
  5214. this.renderer._lowBandSmoothedData = ret[0];
  5215. }
  5216. ret = null;
  5217. }
  5218. else {
  5219. ret = computeHermiteSmoothedData.call(this, gd);
  5220. this.renderer._smoothedData = ret[0];
  5221. this.renderer._smoothedPlotData = ret[1];
  5222. if (bands.show) {
  5223. ret = computeHermiteSmoothedData.call(this, this.renderer._hiBandGridData);
  5224. this.renderer._hiBandSmoothedData = ret[0];
  5225. ret = computeHermiteSmoothedData.call(this, this.renderer._lowBandGridData);
  5226. this.renderer._lowBandSmoothedData = ret[0];
  5227. }
  5228. ret = null;
  5229. }
  5230. }
  5231. return gd;
  5232. };
  5233. // called within scope of series.
  5234. $.jqplot.LineRenderer.prototype.draw = function(ctx, gd, options, plot) {
  5235. var i;
  5236. // get a copy of the options, so we don't modify the original object.
  5237. var opts = $.extend(true, {}, options);
  5238. var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow;
  5239. var showLine = (opts.showLine != undefined) ? opts.showLine : this.showLine;
  5240. var fill = (opts.fill != undefined) ? opts.fill : this.fill;
  5241. var fillAndStroke = (opts.fillAndStroke != undefined) ? opts.fillAndStroke : this.fillAndStroke;
  5242. var xmin, ymin, xmax, ymax;
  5243. ctx.save();
  5244. if (gd.length) {
  5245. if (showLine) {
  5246. // if we fill, we'll have to add points to close the curve.
  5247. if (fill) {
  5248. if (this.fillToZero) {
  5249. // have to break line up into shapes at axis crossings
  5250. var negativeColor = this.negativeColor;
  5251. if (! this.useNegativeColors) {
  5252. negativeColor = opts.fillStyle;
  5253. }
  5254. var isnegative = false;
  5255. var posfs = opts.fillStyle;
  5256. // if stoking line as well as filling, get a copy of line data.
  5257. if (fillAndStroke) {
  5258. var fasgd = gd.slice(0);
  5259. }
  5260. // if not stacked, fill down to axis
  5261. if (this.index == 0 || !this._stack) {
  5262. var tempgd = [];
  5263. var pd = (this.renderer.smooth) ? this.renderer._smoothedPlotData : this._plotData;
  5264. this._areaPoints = [];
  5265. var pyzero = this._yaxis.series_u2p(this.fillToValue);
  5266. var pxzero = this._xaxis.series_u2p(this.fillToValue);
  5267. opts.closePath = true;
  5268. if (this.fillAxis == 'y') {
  5269. tempgd.push([gd[0][0], pyzero]);
  5270. this._areaPoints.push([gd[0][0], pyzero]);
  5271. for (var i=0; i<gd.length-1; i++) {
  5272. tempgd.push(gd[i]);
  5273. this._areaPoints.push(gd[i]);
  5274. // do we have an axis crossing?
  5275. if (pd[i][1] * pd[i+1][1] <= 0) {
  5276. if (pd[i][1] < 0) {
  5277. isnegative = true;
  5278. opts.fillStyle = negativeColor;
  5279. }
  5280. else {
  5281. isnegative = false;
  5282. opts.fillStyle = posfs;
  5283. }
  5284. var xintercept = gd[i][0] + (gd[i+1][0] - gd[i][0]) * (pyzero-gd[i][1])/(gd[i+1][1] - gd[i][1]);
  5285. tempgd.push([xintercept, pyzero]);
  5286. this._areaPoints.push([xintercept, pyzero]);
  5287. // now draw this shape and shadow.
  5288. if (shadow) {
  5289. this.renderer.shadowRenderer.draw(ctx, tempgd, opts);
  5290. }
  5291. this.renderer.shapeRenderer.draw(ctx, tempgd, opts);
  5292. // now empty temp array and continue
  5293. tempgd = [[xintercept, pyzero]];
  5294. // this._areaPoints = [[xintercept, pyzero]];
  5295. }
  5296. }
  5297. if (pd[gd.length-1][1] < 0) {
  5298. isnegative = true;
  5299. opts.fillStyle = negativeColor;
  5300. }
  5301. else {
  5302. isnegative = false;
  5303. opts.fillStyle = posfs;
  5304. }
  5305. tempgd.push(gd[gd.length-1]);
  5306. this._areaPoints.push(gd[gd.length-1]);
  5307. tempgd.push([gd[gd.length-1][0], pyzero]);
  5308. this._areaPoints.push([gd[gd.length-1][0], pyzero]);
  5309. }
  5310. // now draw the last area.
  5311. if (shadow) {
  5312. this.renderer.shadowRenderer.draw(ctx, tempgd, opts);
  5313. }
  5314. this.renderer.shapeRenderer.draw(ctx, tempgd, opts);
  5315. // var gridymin = this._yaxis.series_u2p(0);
  5316. // // IE doesn't return new length on unshift
  5317. // gd.unshift([gd[0][0], gridymin]);
  5318. // len = gd.length;
  5319. // gd.push([gd[len - 1][0], gridymin]);
  5320. }
  5321. // if stacked, fill to line below
  5322. else {
  5323. var prev = this._prevGridData;
  5324. for (var i=prev.length; i>0; i--) {
  5325. gd.push(prev[i-1]);
  5326. // this._areaPoints.push(prev[i-1]);
  5327. }
  5328. if (shadow) {
  5329. this.renderer.shadowRenderer.draw(ctx, gd, opts);
  5330. }
  5331. this._areaPoints = gd;
  5332. this.renderer.shapeRenderer.draw(ctx, gd, opts);
  5333. }
  5334. }
  5335. /////////////////////////
  5336. // Not filled to zero
  5337. ////////////////////////
  5338. else {
  5339. // if stoking line as well as filling, get a copy of line data.
  5340. if (fillAndStroke) {
  5341. var fasgd = gd.slice(0);
  5342. }
  5343. // if not stacked, fill down to axis
  5344. if (this.index == 0 || !this._stack) {
  5345. // var gridymin = this._yaxis.series_u2p(this._yaxis.min) - this.gridBorderWidth / 2;
  5346. var gridymin = ctx.canvas.height;
  5347. // IE doesn't return new length on unshift
  5348. gd.unshift([gd[0][0], gridymin]);
  5349. var len = gd.length;
  5350. gd.push([gd[len - 1][0], gridymin]);
  5351. }
  5352. // if stacked, fill to line below
  5353. else {
  5354. var prev = this._prevGridData;
  5355. for (var i=prev.length; i>0; i--) {
  5356. gd.push(prev[i-1]);
  5357. }
  5358. }
  5359. this._areaPoints = gd;
  5360. if (shadow) {
  5361. this.renderer.shadowRenderer.draw(ctx, gd, opts);
  5362. }
  5363. this.renderer.shapeRenderer.draw(ctx, gd, opts);
  5364. }
  5365. if (fillAndStroke) {
  5366. var fasopts = $.extend(true, {}, opts, {fill:false, closePath:false});
  5367. this.renderer.shapeRenderer.draw(ctx, fasgd, fasopts);
  5368. //////////
  5369. // TODO: figure out some way to do shadows nicely
  5370. // if (shadow) {
  5371. // this.renderer.shadowRenderer.draw(ctx, fasgd, fasopts);
  5372. // }
  5373. // now draw the markers
  5374. if (this.markerRenderer.show) {
  5375. if (this.renderer.smooth) {
  5376. fasgd = this.gridData;
  5377. }
  5378. for (i=0; i<fasgd.length; i++) {
  5379. this.markerRenderer.draw(fasgd[i][0], fasgd[i][1], ctx, opts.markerOptions);
  5380. }
  5381. }
  5382. }
  5383. }
  5384. else {
  5385. if (this.renderer.bands.show) {
  5386. var bdat;
  5387. var bopts = $.extend(true, {}, opts);
  5388. if (this.renderer.bands.showLines) {
  5389. bdat = (this.renderer.smooth) ? this.renderer._hiBandSmoothedData : this.renderer._hiBandGridData;
  5390. this.renderer.shapeRenderer.draw(ctx, bdat, opts);
  5391. bdat = (this.renderer.smooth) ? this.renderer._lowBandSmoothedData : this.renderer._lowBandGridData;
  5392. this.renderer.shapeRenderer.draw(ctx, bdat, bopts);
  5393. }
  5394. if (this.renderer.bands.fill) {
  5395. if (this.renderer.smooth) {
  5396. bdat = this.renderer._hiBandSmoothedData.concat(this.renderer._lowBandSmoothedData.reverse());
  5397. }
  5398. else {
  5399. bdat = this.renderer._hiBandGridData.concat(this.renderer._lowBandGridData.reverse());
  5400. }
  5401. this._areaPoints = bdat;
  5402. bopts.closePath = true;
  5403. bopts.fill = true;
  5404. bopts.fillStyle = this.renderer.bands.fillColor;
  5405. this.renderer.shapeRenderer.draw(ctx, bdat, bopts);
  5406. }
  5407. }
  5408. if (shadow) {
  5409. this.renderer.shadowRenderer.draw(ctx, gd, opts);
  5410. }
  5411. this.renderer.shapeRenderer.draw(ctx, gd, opts);
  5412. }
  5413. }
  5414. // calculate the bounding box
  5415. var xmin = xmax = ymin = ymax = null;
  5416. for (i=0; i<this._areaPoints.length; i++) {
  5417. var p = this._areaPoints[i];
  5418. if (xmin > p[0] || xmin == null) {
  5419. xmin = p[0];
  5420. }
  5421. if (ymax < p[1] || ymax == null) {
  5422. ymax = p[1];
  5423. }
  5424. if (xmax < p[0] || xmax == null) {
  5425. xmax = p[0];
  5426. }
  5427. if (ymin > p[1] || ymin == null) {
  5428. ymin = p[1];
  5429. }
  5430. }
  5431. if (this.type === 'line' && this.renderer.bands.show) {
  5432. ymax = this._yaxis.series_u2p(this.renderer.bands._min);
  5433. ymin = this._yaxis.series_u2p(this.renderer.bands._max);
  5434. }
  5435. this._boundingBox = [[xmin, ymax], [xmax, ymin]];
  5436. // now draw the markers
  5437. if (this.markerRenderer.show && !fill) {
  5438. if (this.renderer.smooth) {
  5439. gd = this.gridData;
  5440. }
  5441. for (i=0; i<gd.length; i++) {
  5442. if (gd[i][0] != null && gd[i][1] != null) {
  5443. this.markerRenderer.draw(gd[i][0], gd[i][1], ctx, opts.markerOptions);
  5444. }
  5445. }
  5446. }
  5447. }
  5448. ctx.restore();
  5449. };
  5450. $.jqplot.LineRenderer.prototype.drawShadow = function(ctx, gd, options) {
  5451. // This is a no-op, shadows drawn with lines.
  5452. };
  5453. // called with scope of plot.
  5454. // make sure to not leave anything highlighted.
  5455. function postInit(target, data, options) {
  5456. for (var i=0; i<this.series.length; i++) {
  5457. if (this.series[i].renderer.constructor == $.jqplot.LineRenderer) {
  5458. // don't allow mouseover and mousedown at same time.
  5459. if (this.series[i].highlightMouseOver) {
  5460. this.series[i].highlightMouseDown = false;
  5461. }
  5462. }
  5463. }
  5464. }
  5465. // called within context of plot
  5466. // create a canvas which we can draw on.
  5467. // insert it before the eventCanvas, so eventCanvas will still capture events.
  5468. function postPlotDraw() {
  5469. // Memory Leaks patch
  5470. if (this.plugins.lineRenderer && this.plugins.lineRenderer.highlightCanvas) {
  5471. this.plugins.lineRenderer.highlightCanvas.resetCanvas();
  5472. this.plugins.lineRenderer.highlightCanvas = null;
  5473. }
  5474. this.plugins.lineRenderer.highlightedSeriesIndex = null;
  5475. this.plugins.lineRenderer.highlightCanvas = new $.jqplot.GenericCanvas();
  5476. this.eventCanvas._elem.before(this.plugins.lineRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-lineRenderer-highlight-canvas', this._plotDimensions, this));
  5477. this.plugins.lineRenderer.highlightCanvas.setContext();
  5478. this.eventCanvas._elem.bind('mouseleave', {plot:this}, function (ev) { unhighlight(ev.data.plot); });
  5479. }
  5480. function highlight (plot, sidx, pidx, points) {
  5481. var s = plot.series[sidx];
  5482. var canvas = plot.plugins.lineRenderer.highlightCanvas;
  5483. canvas._ctx.clearRect(0,0,canvas._ctx.canvas.width, canvas._ctx.canvas.height);
  5484. s._highlightedPoint = pidx;
  5485. plot.plugins.lineRenderer.highlightedSeriesIndex = sidx;
  5486. var opts = {fillStyle: s.highlightColor};
  5487. if (s.type === 'line' && s.renderer.bands.show) {
  5488. opts.fill = true;
  5489. opts.closePath = true;
  5490. }
  5491. s.renderer.shapeRenderer.draw(canvas._ctx, points, opts);
  5492. canvas = null;
  5493. }
  5494. function unhighlight (plot) {
  5495. var canvas = plot.plugins.lineRenderer.highlightCanvas;
  5496. canvas._ctx.clearRect(0,0, canvas._ctx.canvas.width, canvas._ctx.canvas.height);
  5497. for (var i=0; i<plot.series.length; i++) {
  5498. plot.series[i]._highlightedPoint = null;
  5499. }
  5500. plot.plugins.lineRenderer.highlightedSeriesIndex = null;
  5501. plot.target.trigger('jqplotDataUnhighlight');
  5502. canvas = null;
  5503. }
  5504. function handleMove(ev, gridpos, datapos, neighbor, plot) {
  5505. if (neighbor) {
  5506. var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
  5507. var evt1 = jQuery.Event('jqplotDataMouseOver');
  5508. evt1.pageX = ev.pageX;
  5509. evt1.pageY = ev.pageY;
  5510. plot.target.trigger(evt1, ins);
  5511. if (plot.series[ins[0]].highlightMouseOver && !(ins[0] == plot.plugins.lineRenderer.highlightedSeriesIndex)) {
  5512. var evt = jQuery.Event('jqplotDataHighlight');
  5513. evt.which = ev.which;
  5514. evt.pageX = ev.pageX;
  5515. evt.pageY = ev.pageY;
  5516. plot.target.trigger(evt, ins);
  5517. highlight (plot, neighbor.seriesIndex, neighbor.pointIndex, neighbor.points);
  5518. }
  5519. }
  5520. else if (neighbor == null) {
  5521. unhighlight (plot);
  5522. }
  5523. }
  5524. function handleMouseDown(ev, gridpos, datapos, neighbor, plot) {
  5525. if (neighbor) {
  5526. var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
  5527. if (plot.series[ins[0]].highlightMouseDown && !(ins[0] == plot.plugins.lineRenderer.highlightedSeriesIndex)) {
  5528. var evt = jQuery.Event('jqplotDataHighlight');
  5529. evt.which = ev.which;
  5530. evt.pageX = ev.pageX;
  5531. evt.pageY = ev.pageY;
  5532. plot.target.trigger(evt, ins);
  5533. highlight (plot, neighbor.seriesIndex, neighbor.pointIndex, neighbor.points);
  5534. }
  5535. }
  5536. else if (neighbor == null) {
  5537. unhighlight (plot);
  5538. }
  5539. }
  5540. function handleMouseUp(ev, gridpos, datapos, neighbor, plot) {
  5541. var idx = plot.plugins.lineRenderer.highlightedSeriesIndex;
  5542. if (idx != null && plot.series[idx].highlightMouseDown) {
  5543. unhighlight(plot);
  5544. }
  5545. }
  5546. function handleClick(ev, gridpos, datapos, neighbor, plot) {
  5547. if (neighbor) {
  5548. var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
  5549. var evt = jQuery.Event('jqplotDataClick');
  5550. evt.which = ev.which;
  5551. evt.pageX = ev.pageX;
  5552. evt.pageY = ev.pageY;
  5553. plot.target.trigger(evt, ins);
  5554. }
  5555. }
  5556. function handleRightClick(ev, gridpos, datapos, neighbor, plot) {
  5557. if (neighbor) {
  5558. var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
  5559. var idx = plot.plugins.lineRenderer.highlightedSeriesIndex;
  5560. if (idx != null && plot.series[idx].highlightMouseDown) {
  5561. unhighlight(plot);
  5562. }
  5563. var evt = jQuery.Event('jqplotDataRightClick');
  5564. evt.which = ev.which;
  5565. evt.pageX = ev.pageX;
  5566. evt.pageY = ev.pageY;
  5567. plot.target.trigger(evt, ins);
  5568. }
  5569. }
  5570. // class: $.jqplot.LinearAxisRenderer
  5571. // The default jqPlot axis renderer, creating a numeric axis.
  5572. $.jqplot.LinearAxisRenderer = function() {
  5573. };
  5574. // called with scope of axis object.
  5575. $.jqplot.LinearAxisRenderer.prototype.init = function(options){
  5576. // prop: breakPoints
  5577. // EXPERIMENTAL!! Use at your own risk!
  5578. // Works only with linear axes and the default tick renderer.
  5579. // Array of [start, stop] points to create a broken axis.
  5580. // Broken axes have a "jump" in them, which is an immediate
  5581. // transition from a smaller value to a larger value.
  5582. // Currently, axis ticks MUST be manually assigned if using breakPoints
  5583. // by using the axis ticks array option.
  5584. this.breakPoints = null;
  5585. // prop: breakTickLabel
  5586. // Label to use at the axis break if breakPoints are specified.
  5587. this.breakTickLabel = "&asymp;";
  5588. // prop: drawBaseline
  5589. // True to draw the axis baseline.
  5590. this.drawBaseline = true;
  5591. // prop: baselineWidth
  5592. // width of the baseline in pixels.
  5593. this.baselineWidth = null;
  5594. // prop: baselineColor
  5595. // CSS color spec for the baseline.
  5596. this.baselineColor = null;
  5597. // prop: forceTickAt0
  5598. // This will ensure that there is always a tick mark at 0.
  5599. // If data range is strictly positive or negative,
  5600. // this will force 0 to be inside the axis bounds unless
  5601. // the appropriate axis pad (pad, padMin or padMax) is set
  5602. // to 0, then this will force an axis min or max value at 0.
  5603. // This has know effect when any of the following options
  5604. // are set: autoscale, min, max, numberTicks or tickInterval.
  5605. this.forceTickAt0 = false;
  5606. // prop: forceTickAt100
  5607. // This will ensure that there is always a tick mark at 100.
  5608. // If data range is strictly above or below 100,
  5609. // this will force 100 to be inside the axis bounds unless
  5610. // the appropriate axis pad (pad, padMin or padMax) is set
  5611. // to 0, then this will force an axis min or max value at 100.
  5612. // This has know effect when any of the following options
  5613. // are set: autoscale, min, max, numberTicks or tickInterval.
  5614. this.forceTickAt100 = false;
  5615. // prop: tickInset
  5616. // Controls the amount to inset the first and last ticks from
  5617. // the edges of the grid, in multiples of the tick interval.
  5618. // 0 is no inset, 0.5 is one half a tick interval, 1 is a full
  5619. // tick interval, etc.
  5620. this.tickInset = 0;
  5621. // prop: minorTicks
  5622. // Number of ticks to add between "major" ticks.
  5623. // Major ticks are ticks supplied by user or auto computed.
  5624. // Minor ticks cannot be created by user.
  5625. this.minorTicks = 0;
  5626. // prop: alignTicks
  5627. // true to align tick marks across opposed axes
  5628. // such as from the y2axis to yaxis.
  5629. this.alignTicks = false;
  5630. this._autoFormatString = '';
  5631. this._overrideFormatString = false;
  5632. this._scalefact = 1.0;
  5633. $.extend(true, this, options);
  5634. if (this.breakPoints) {
  5635. if (!$.isArray(this.breakPoints)) {
  5636. this.breakPoints = null;
  5637. }
  5638. else if (this.breakPoints.length < 2 || this.breakPoints[1] <= this.breakPoints[0]) {
  5639. this.breakPoints = null;
  5640. }
  5641. }
  5642. if (this.numberTicks != null && this.numberTicks < 2) {
  5643. this.numberTicks = 2;
  5644. }
  5645. this.resetDataBounds();
  5646. };
  5647. // called with scope of axis
  5648. $.jqplot.LinearAxisRenderer.prototype.draw = function(ctx, plot) {
  5649. if (this.show) {
  5650. // populate the axis label and value properties.
  5651. // createTicks is a method on the renderer, but
  5652. // call it within the scope of the axis.
  5653. this.renderer.createTicks.call(this, plot);
  5654. // fill a div with axes labels in the right direction.
  5655. // Need to pregenerate each axis to get its bounds and
  5656. // position it and the labels correctly on the plot.
  5657. var dim=0;
  5658. var temp;
  5659. // Added for theming.
  5660. if (this._elem) {
  5661. // Memory Leaks patch
  5662. //this._elem.empty();
  5663. this._elem.emptyForce();
  5664. this._elem = null;
  5665. }
  5666. this._elem = $(document.createElement('div'));
  5667. this._elem.addClass('jqplot-axis jqplot-'+this.name);
  5668. this._elem.css('position', 'absolute');
  5669. if (this.name == 'xaxis' || this.name == 'x2axis') {
  5670. this._elem.width(this._plotDimensions.width);
  5671. }
  5672. else {
  5673. this._elem.height(this._plotDimensions.height);
  5674. }
  5675. // create a _label object.
  5676. this.labelOptions.axis = this.name;
  5677. this._label = new this.labelRenderer(this.labelOptions);
  5678. if (this._label.show) {
  5679. var elem = this._label.draw(ctx, plot);
  5680. elem.appendTo(this._elem);
  5681. elem = null;
  5682. }
  5683. var t = this._ticks;
  5684. var tick;
  5685. for (var i=0; i<t.length; i++) {
  5686. tick = t[i];
  5687. if (tick.show && tick.showLabel && (!tick.isMinorTick || this.showMinorTicks)) {
  5688. this._elem.append(tick.draw(ctx, plot));
  5689. }
  5690. }
  5691. tick = null;
  5692. t = null;
  5693. }
  5694. return this._elem;
  5695. };
  5696. // called with scope of an axis
  5697. $.jqplot.LinearAxisRenderer.prototype.reset = function() {
  5698. this.min = this._options.min;
  5699. this.max = this._options.max;
  5700. this.tickInterval = this._options.tickInterval;
  5701. this.numberTicks = this._options.numberTicks;
  5702. this._autoFormatString = '';
  5703. if (this._overrideFormatString && this.tickOptions && this.tickOptions.formatString) {
  5704. this.tickOptions.formatString = '';
  5705. }
  5706. // this._ticks = this.__ticks;
  5707. };
  5708. // called with scope of axis
  5709. $.jqplot.LinearAxisRenderer.prototype.set = function() {
  5710. var dim = 0;
  5711. var temp;
  5712. var w = 0;
  5713. var h = 0;
  5714. var lshow = (this._label == null) ? false : this._label.show;
  5715. if (this.show) {
  5716. var t = this._ticks;
  5717. var tick;
  5718. for (var i=0; i<t.length; i++) {
  5719. tick = t[i];
  5720. if (!tick._breakTick && tick.show && tick.showLabel && (!tick.isMinorTick || this.showMinorTicks)) {
  5721. if (this.name == 'xaxis' || this.name == 'x2axis') {
  5722. temp = tick._elem.outerHeight(true);
  5723. }
  5724. else {
  5725. temp = tick._elem.outerWidth(true);
  5726. }
  5727. if (temp > dim) {
  5728. dim = temp;
  5729. }
  5730. }
  5731. }
  5732. tick = null;
  5733. t = null;
  5734. if (lshow) {
  5735. w = this._label._elem.outerWidth(true);
  5736. h = this._label._elem.outerHeight(true);
  5737. }
  5738. if (this.name == 'xaxis') {
  5739. dim = dim + h;
  5740. this._elem.css({'height':dim+'px', left:'0px', bottom:'0px'});
  5741. }
  5742. else if (this.name == 'x2axis') {
  5743. dim = dim + h;
  5744. this._elem.css({'height':dim+'px', left:'0px', top:'0px'});
  5745. }
  5746. else if (this.name == 'yaxis') {
  5747. dim = dim + w;
  5748. this._elem.css({'width':dim+'px', left:'0px', top:'0px'});
  5749. if (lshow && this._label.constructor == $.jqplot.AxisLabelRenderer) {
  5750. this._label._elem.css('width', w+'px');
  5751. }
  5752. }
  5753. else {
  5754. dim = dim + w;
  5755. this._elem.css({'width':dim+'px', right:'0px', top:'0px'});
  5756. if (lshow && this._label.constructor == $.jqplot.AxisLabelRenderer) {
  5757. this._label._elem.css('width', w+'px');
  5758. }
  5759. }
  5760. }
  5761. };
  5762. // called with scope of axis
  5763. $.jqplot.LinearAxisRenderer.prototype.createTicks = function(plot) {
  5764. // we're are operating on an axis here
  5765. var ticks = this._ticks;
  5766. var userTicks = this.ticks;
  5767. var name = this.name;
  5768. // databounds were set on axis initialization.
  5769. var db = this._dataBounds;
  5770. var dim = (this.name.charAt(0) === 'x') ? this._plotDimensions.width : this._plotDimensions.height;
  5771. var interval;
  5772. var min, max;
  5773. var pos1, pos2;
  5774. var tt, i;
  5775. // get a copy of user's settings for min/max.
  5776. var userMin = this.min;
  5777. var userMax = this.max;
  5778. var userNT = this.numberTicks;
  5779. var userTI = this.tickInterval;
  5780. var threshold = 30;
  5781. this._scalefact = (Math.max(dim, threshold+1) - threshold)/300.0;
  5782. // if we already have ticks, use them.
  5783. // ticks must be in order of increasing value.
  5784. if (userTicks.length) {
  5785. // ticks could be 1D or 2D array of [val, val, ,,,] or [[val, label], [val, label], ...] or mixed
  5786. for (i=0; i<userTicks.length; i++){
  5787. var ut = userTicks[i];
  5788. var t = new this.tickRenderer(this.tickOptions);
  5789. if ($.isArray(ut)) {
  5790. t.value = ut[0];
  5791. if (this.breakPoints) {
  5792. if (ut[0] == this.breakPoints[0]) {
  5793. t.label = this.breakTickLabel;
  5794. t._breakTick = true;
  5795. t.showGridline = false;
  5796. t.showMark = false;
  5797. }
  5798. else if (ut[0] > this.breakPoints[0] && ut[0] <= this.breakPoints[1]) {
  5799. t.show = false;
  5800. t.showGridline = false;
  5801. t.label = ut[1];
  5802. }
  5803. else {
  5804. t.label = ut[1];
  5805. }
  5806. }
  5807. else {
  5808. t.label = ut[1];
  5809. }
  5810. t.setTick(ut[0], this.name);
  5811. this._ticks.push(t);
  5812. }
  5813. else if ($.isPlainObject(ut)) {
  5814. $.extend(true, t, ut);
  5815. t.axis = this.name;
  5816. this._ticks.push(t);
  5817. }
  5818. else {
  5819. t.value = ut;
  5820. if (this.breakPoints) {
  5821. if (ut == this.breakPoints[0]) {
  5822. t.label = this.breakTickLabel;
  5823. t._breakTick = true;
  5824. t.showGridline = false;
  5825. t.showMark = false;
  5826. }
  5827. else if (ut > this.breakPoints[0] && ut <= this.breakPoints[1]) {
  5828. t.show = false;
  5829. t.showGridline = false;
  5830. }
  5831. }
  5832. t.setTick(ut, this.name);
  5833. this._ticks.push(t);
  5834. }
  5835. }
  5836. this.numberTicks = userTicks.length;
  5837. this.min = this._ticks[0].value;
  5838. this.max = this._ticks[this.numberTicks-1].value;
  5839. this.tickInterval = (this.max - this.min) / (this.numberTicks - 1);
  5840. }
  5841. // we don't have any ticks yet, let's make some!
  5842. else {
  5843. if (name == 'xaxis' || name == 'x2axis') {
  5844. dim = this._plotDimensions.width;
  5845. }
  5846. else {
  5847. dim = this._plotDimensions.height;
  5848. }
  5849. var _numberTicks = this.numberTicks;
  5850. // if aligning this axis, use number of ticks from previous axis.
  5851. // Do I need to reset somehow if alignTicks is changed and then graph is replotted??
  5852. if (this.alignTicks) {
  5853. if (this.name === 'x2axis' && plot.axes.xaxis.show) {
  5854. _numberTicks = plot.axes.xaxis.numberTicks;
  5855. }
  5856. else if (this.name.charAt(0) === 'y' && this.name !== 'yaxis' && this.name !== 'yMidAxis' && plot.axes.yaxis.show) {
  5857. _numberTicks = plot.axes.yaxis.numberTicks;
  5858. }
  5859. }
  5860. min = ((this.min != null) ? this.min : db.min);
  5861. max = ((this.max != null) ? this.max : db.max);
  5862. var range = max - min;
  5863. var rmin, rmax;
  5864. var temp;
  5865. if (this.tickOptions == null || !this.tickOptions.formatString) {
  5866. this._overrideFormatString = true;
  5867. }
  5868. // Doing complete autoscaling
  5869. if (this.min == null || this.max == null && this.tickInterval == null && !this.autoscale) {
  5870. // Check if user must have tick at 0 or 100 and ensure they are in range.
  5871. // The autoscaling algorithm will always place ticks at 0 and 100 if they are in range.
  5872. if (this.forceTickAt0) {
  5873. if (min > 0) {
  5874. min = 0;
  5875. }
  5876. if (max < 0) {
  5877. max = 0;
  5878. }
  5879. }
  5880. if (this.forceTickAt100) {
  5881. if (min > 100) {
  5882. min = 100;
  5883. }
  5884. if (max < 100) {
  5885. max = 100;
  5886. }
  5887. }
  5888. var keepMin = false,
  5889. keepMax = false;
  5890. if (this.min != null) {
  5891. keepMin = true;
  5892. }
  5893. else if (this.max != null) {
  5894. keepMax = true;
  5895. }
  5896. // var threshold = 30;
  5897. // var tdim = Math.max(dim, threshold+1);
  5898. // this._scalefact = (tdim-threshold)/300.0;
  5899. var ret = $.jqplot.LinearTickGenerator(min, max, this._scalefact, _numberTicks, keepMin, keepMax);
  5900. // calculate a padded max and min, points should be less than these
  5901. // so that they aren't too close to the edges of the plot.
  5902. // User can adjust how much padding is allowed with pad, padMin and PadMax options.
  5903. // If min or max is set, don't pad that end of axis.
  5904. var tumin = (this.min != null) ? min : min + range*(this.padMin - 1);
  5905. var tumax = (this.max != null) ? max : max - range*(this.padMax - 1);
  5906. // if they're equal, we shouldn't have to do anything, right?
  5907. // if (min <=tumin || max >= tumax) {
  5908. if (min <tumin || max > tumax) {
  5909. tumin = (this.min != null) ? min : min - range*(this.padMin - 1);
  5910. tumax = (this.max != null) ? max : max + range*(this.padMax - 1);
  5911. ret = $.jqplot.LinearTickGenerator(tumin, tumax, this._scalefact, _numberTicks, keepMin, keepMax);
  5912. }
  5913. this.min = ret[0];
  5914. this.max = ret[1];
  5915. // if numberTicks specified, it should return the same.
  5916. this.numberTicks = ret[2];
  5917. this._autoFormatString = ret[3];
  5918. this.tickInterval = ret[4];
  5919. }
  5920. // User has specified some axis scale related option, can use auto algorithm
  5921. else {
  5922. // if min and max are same, space them out a bit
  5923. if (min == max) {
  5924. var adj = 0.05;
  5925. if (min > 0) {
  5926. adj = Math.max(Math.log(min)/Math.LN10, 0.05);
  5927. }
  5928. min -= adj;
  5929. max += adj;
  5930. }
  5931. // autoscale. Can't autoscale if min or max is supplied.
  5932. // Will use numberTicks and tickInterval if supplied. Ticks
  5933. // across multiple axes may not line up depending on how
  5934. // bars are to be plotted.
  5935. if (this.autoscale && this.min == null && this.max == null) {
  5936. var rrange, ti, margin;
  5937. var forceMinZero = false;
  5938. var forceZeroLine = false;
  5939. var intervals = {min:null, max:null, average:null, stddev:null};
  5940. // if any series are bars, or if any are fill to zero, and if this
  5941. // is the axis to fill toward, check to see if we can start axis at zero.
  5942. for (var i=0; i<this._series.length; i++) {
  5943. var s = this._series[i];
  5944. var faname = (s.fillAxis == 'x') ? s._xaxis.name : s._yaxis.name;
  5945. // check to see if this is the fill axis
  5946. if (this.name == faname) {
  5947. var vals = s._plotValues[s.fillAxis];
  5948. var vmin = vals[0];
  5949. var vmax = vals[0];
  5950. for (var j=1; j<vals.length; j++) {
  5951. if (vals[j] < vmin) {
  5952. vmin = vals[j];
  5953. }
  5954. else if (vals[j] > vmax) {
  5955. vmax = vals[j];
  5956. }
  5957. }
  5958. var dp = (vmax - vmin) / vmax;
  5959. // is this sries a bar?
  5960. if (s.renderer.constructor == $.jqplot.BarRenderer) {
  5961. // if no negative values and could also check range.
  5962. if (vmin >= 0 && (s.fillToZero || dp > 0.1)) {
  5963. forceMinZero = true;
  5964. }
  5965. else {
  5966. forceMinZero = false;
  5967. if (s.fill && s.fillToZero && vmin < 0 && vmax > 0) {
  5968. forceZeroLine = true;
  5969. }
  5970. else {
  5971. forceZeroLine = false;
  5972. }
  5973. }
  5974. }
  5975. // if not a bar and filling, use appropriate method.
  5976. else if (s.fill) {
  5977. if (vmin >= 0 && (s.fillToZero || dp > 0.1)) {
  5978. forceMinZero = true;
  5979. }
  5980. else if (vmin < 0 && vmax > 0 && s.fillToZero) {
  5981. forceMinZero = false;
  5982. forceZeroLine = true;
  5983. }
  5984. else {
  5985. forceMinZero = false;
  5986. forceZeroLine = false;
  5987. }
  5988. }
  5989. // if not a bar and not filling, only change existing state
  5990. // if it doesn't make sense
  5991. else if (vmin < 0) {
  5992. forceMinZero = false;
  5993. }
  5994. }
  5995. }
  5996. // check if we need make axis min at 0.
  5997. if (forceMinZero) {
  5998. // compute number of ticks
  5999. this.numberTicks = 2 + Math.ceil((dim-(this.tickSpacing-1))/this.tickSpacing);
  6000. this.min = 0;
  6001. userMin = 0;
  6002. // what order is this range?
  6003. // what tick interval does that give us?
  6004. ti = max/(this.numberTicks-1);
  6005. temp = Math.pow(10, Math.abs(Math.floor(Math.log(ti)/Math.LN10)));
  6006. if (ti/temp == parseInt(ti/temp, 10)) {
  6007. ti += temp;
  6008. }
  6009. this.tickInterval = Math.ceil(ti/temp) * temp;
  6010. this.max = this.tickInterval * (this.numberTicks - 1);
  6011. }
  6012. // check if we need to make sure there is a tick at 0.
  6013. else if (forceZeroLine) {
  6014. // compute number of ticks
  6015. this.numberTicks = 2 + Math.ceil((dim-(this.tickSpacing-1))/this.tickSpacing);
  6016. var ntmin = Math.ceil(Math.abs(min)/range*(this.numberTicks-1));
  6017. var ntmax = this.numberTicks - 1 - ntmin;
  6018. ti = Math.max(Math.abs(min/ntmin), Math.abs(max/ntmax));
  6019. temp = Math.pow(10, Math.abs(Math.floor(Math.log(ti)/Math.LN10)));
  6020. this.tickInterval = Math.ceil(ti/temp) * temp;
  6021. this.max = this.tickInterval * ntmax;
  6022. this.min = -this.tickInterval * ntmin;
  6023. }
  6024. // if nothing else, do autoscaling which will try to line up ticks across axes.
  6025. else {
  6026. if (this.numberTicks == null){
  6027. if (this.tickInterval) {
  6028. this.numberTicks = 3 + Math.ceil(range / this.tickInterval);
  6029. }
  6030. else {
  6031. this.numberTicks = 2 + Math.ceil((dim-(this.tickSpacing-1))/this.tickSpacing);
  6032. }
  6033. }
  6034. if (this.tickInterval == null) {
  6035. // get a tick interval
  6036. ti = range/(this.numberTicks - 1);
  6037. if (ti < 1) {
  6038. temp = Math.pow(10, Math.abs(Math.floor(Math.log(ti)/Math.LN10)));
  6039. }
  6040. else {
  6041. temp = 1;
  6042. }
  6043. this.tickInterval = Math.ceil(ti*temp*this.pad)/temp;
  6044. }
  6045. else {
  6046. temp = 1 / this.tickInterval;
  6047. }
  6048. // try to compute a nicer, more even tick interval
  6049. // temp = Math.pow(10, Math.floor(Math.log(ti)/Math.LN10));
  6050. // this.tickInterval = Math.ceil(ti/temp) * temp;
  6051. rrange = this.tickInterval * (this.numberTicks - 1);
  6052. margin = (rrange - range)/2;
  6053. if (this.min == null) {
  6054. this.min = Math.floor(temp*(min-margin))/temp;
  6055. }
  6056. if (this.max == null) {
  6057. this.max = this.min + rrange;
  6058. }
  6059. }
  6060. // Compute a somewhat decent format string if it is needed.
  6061. // get precision of interval and determine a format string.
  6062. var sf = $.jqplot.getSignificantFigures(this.tickInterval);
  6063. var fstr;
  6064. // if we have only a whole number, use integer formatting
  6065. if (sf.digitsLeft >= sf.significantDigits) {
  6066. fstr = '%d';
  6067. }
  6068. else {
  6069. var temp = Math.max(0, 5 - sf.digitsLeft);
  6070. temp = Math.min(temp, sf.digitsRight);
  6071. fstr = '%.'+ temp + 'f';
  6072. }
  6073. this._autoFormatString = fstr;
  6074. }
  6075. // Use the default algorithm which pads each axis to make the chart
  6076. // centered nicely on the grid.
  6077. else {
  6078. rmin = (this.min != null) ? this.min : min - range*(this.padMin - 1);
  6079. rmax = (this.max != null) ? this.max : max + range*(this.padMax - 1);
  6080. range = rmax - rmin;
  6081. if (this.numberTicks == null){
  6082. // if tickInterval is specified by user, we will ignore computed maximum.
  6083. // max will be equal or greater to fit even # of ticks.
  6084. if (this.tickInterval != null) {
  6085. this.numberTicks = Math.ceil((rmax - rmin)/this.tickInterval)+1;
  6086. }
  6087. else if (dim > 100) {
  6088. this.numberTicks = parseInt(3+(dim-100)/75, 10);
  6089. }
  6090. else {
  6091. this.numberTicks = 2;
  6092. }
  6093. }
  6094. if (this.tickInterval == null) {
  6095. this.tickInterval = range / (this.numberTicks-1);
  6096. }
  6097. if (this.max == null) {
  6098. rmax = rmin + this.tickInterval*(this.numberTicks - 1);
  6099. }
  6100. if (this.min == null) {
  6101. rmin = rmax - this.tickInterval*(this.numberTicks - 1);
  6102. }
  6103. // get precision of interval and determine a format string.
  6104. var sf = $.jqplot.getSignificantFigures(this.tickInterval);
  6105. var fstr;
  6106. // if we have only a whole number, use integer formatting
  6107. if (sf.digitsLeft >= sf.significantDigits) {
  6108. fstr = '%d';
  6109. }
  6110. else {
  6111. var temp = Math.max(0, 5 - sf.digitsLeft);
  6112. temp = Math.min(temp, sf.digitsRight);
  6113. fstr = '%.'+ temp + 'f';
  6114. }
  6115. this._autoFormatString = fstr;
  6116. this.min = rmin;
  6117. this.max = rmax;
  6118. }
  6119. if (this.renderer.constructor == $.jqplot.LinearAxisRenderer && this._autoFormatString == '') {
  6120. // fix for misleading tick display with small range and low precision.
  6121. range = this.max - this.min;
  6122. // figure out precision
  6123. var temptick = new this.tickRenderer(this.tickOptions);
  6124. // use the tick formatString or, the default.
  6125. var fs = temptick.formatString || $.jqplot.config.defaultTickFormatString;
  6126. var fs = fs.match($.jqplot.sprintf.regex)[0];
  6127. var precision = 0;
  6128. if (fs) {
  6129. if (fs.search(/[fFeEgGpP]/) > -1) {
  6130. var m = fs.match(/\%\.(\d{0,})?[eEfFgGpP]/);
  6131. if (m) {
  6132. precision = parseInt(m[1], 10);
  6133. }
  6134. else {
  6135. precision = 6;
  6136. }
  6137. }
  6138. else if (fs.search(/[di]/) > -1) {
  6139. precision = 0;
  6140. }
  6141. // fact will be <= 1;
  6142. var fact = Math.pow(10, -precision);
  6143. if (this.tickInterval < fact) {
  6144. // need to correct underrange
  6145. if (userNT == null && userTI == null) {
  6146. this.tickInterval = fact;
  6147. if (userMax == null && userMin == null) {
  6148. // this.min = Math.floor((this._dataBounds.min - this.tickInterval)/fact) * fact;
  6149. this.min = Math.floor(this._dataBounds.min/fact) * fact;
  6150. if (this.min == this._dataBounds.min) {
  6151. this.min = this._dataBounds.min - this.tickInterval;
  6152. }
  6153. // this.max = Math.ceil((this._dataBounds.max + this.tickInterval)/fact) * fact;
  6154. this.max = Math.ceil(this._dataBounds.max/fact) * fact;
  6155. if (this.max == this._dataBounds.max) {
  6156. this.max = this._dataBounds.max + this.tickInterval;
  6157. }
  6158. var n = (this.max - this.min)/this.tickInterval;
  6159. n = n.toFixed(11);
  6160. n = Math.ceil(n);
  6161. this.numberTicks = n + 1;
  6162. }
  6163. else if (userMax == null) {
  6164. // add one tick for top of range.
  6165. var n = (this._dataBounds.max - this.min) / this.tickInterval;
  6166. n = n.toFixed(11);
  6167. this.numberTicks = Math.ceil(n) + 2;
  6168. this.max = this.min + this.tickInterval * (this.numberTicks-1);
  6169. }
  6170. else if (userMin == null) {
  6171. // add one tick for bottom of range.
  6172. var n = (this.max - this._dataBounds.min) / this.tickInterval;
  6173. n = n.toFixed(11);
  6174. this.numberTicks = Math.ceil(n) + 2;
  6175. this.min = this.max - this.tickInterval * (this.numberTicks-1);
  6176. }
  6177. else {
  6178. // calculate a number of ticks so max is within axis scale
  6179. this.numberTicks = Math.ceil((userMax - userMin)/this.tickInterval) + 1;
  6180. // if user's min and max don't fit evenly in ticks, adjust.
  6181. // This takes care of cases such as user min set to 0, max set to 3.5 but tick
  6182. // format string set to %d (integer ticks)
  6183. this.min = Math.floor(userMin*Math.pow(10, precision))/Math.pow(10, precision);
  6184. this.max = Math.ceil(userMax*Math.pow(10, precision))/Math.pow(10, precision);
  6185. // this.max = this.min + this.tickInterval*(this.numberTicks-1);
  6186. this.numberTicks = Math.ceil((this.max - this.min)/this.tickInterval) + 1;
  6187. }
  6188. }
  6189. }
  6190. }
  6191. }
  6192. }
  6193. if (this._overrideFormatString && this._autoFormatString != '') {
  6194. this.tickOptions = this.tickOptions || {};
  6195. this.tickOptions.formatString = this._autoFormatString;
  6196. }
  6197. var t, to;
  6198. for (var i=0; i<this.numberTicks; i++){
  6199. tt = this.min + i * this.tickInterval;
  6200. t = new this.tickRenderer(this.tickOptions);
  6201. // var t = new $.jqplot.AxisTickRenderer(this.tickOptions);
  6202. t.setTick(tt, this.name);
  6203. this._ticks.push(t);
  6204. if (i < this.numberTicks - 1) {
  6205. for (var j=0; j<this.minorTicks; j++) {
  6206. tt += this.tickInterval/(this.minorTicks+1);
  6207. to = $.extend(true, {}, this.tickOptions, {name:this.name, value:tt, label:'', isMinorTick:true});
  6208. t = new this.tickRenderer(to);
  6209. this._ticks.push(t);
  6210. }
  6211. }
  6212. t = null;
  6213. }
  6214. }
  6215. if (this.tickInset) {
  6216. this.min = this.min - this.tickInset * this.tickInterval;
  6217. this.max = this.max + this.tickInset * this.tickInterval;
  6218. }
  6219. ticks = null;
  6220. };
  6221. // Used to reset just the values of the ticks and then repack, which will
  6222. // recalculate the positioning functions. It is assuemd that the
  6223. // number of ticks is the same and the values of the new array are at the
  6224. // proper interval.
  6225. // This method needs to be called with the scope of an axis object, like:
  6226. //
  6227. // > plot.axes.yaxis.renderer.resetTickValues.call(plot.axes.yaxis, yarr);
  6228. //
  6229. $.jqplot.LinearAxisRenderer.prototype.resetTickValues = function(opts) {
  6230. if ($.isArray(opts) && opts.length == this._ticks.length) {
  6231. var t;
  6232. for (var i=0; i<opts.length; i++) {
  6233. t = this._ticks[i];
  6234. t.value = opts[i];
  6235. t.label = t.formatter(t.formatString, opts[i]);
  6236. t.label = t.prefix + t.label;
  6237. t._elem.html(t.label);
  6238. }
  6239. t = null;
  6240. this.min = $.jqplot.arrayMin(opts);
  6241. this.max = $.jqplot.arrayMax(opts);
  6242. this.pack();
  6243. }
  6244. // Not implemented yet.
  6245. // else if ($.isPlainObject(opts)) {
  6246. //
  6247. // }
  6248. };
  6249. // called with scope of axis
  6250. $.jqplot.LinearAxisRenderer.prototype.pack = function(pos, offsets) {
  6251. // Add defaults for repacking from resetTickValues function.
  6252. pos = pos || {};
  6253. offsets = offsets || this._offsets;
  6254. var ticks = this._ticks;
  6255. var max = this.max;
  6256. var min = this.min;
  6257. var offmax = offsets.max;
  6258. var offmin = offsets.min;
  6259. var lshow = (this._label == null) ? false : this._label.show;
  6260. for (var p in pos) {
  6261. this._elem.css(p, pos[p]);
  6262. }
  6263. this._offsets = offsets;
  6264. // pixellength will be + for x axes and - for y axes becasue pixels always measured from top left.
  6265. var pixellength = offmax - offmin;
  6266. var unitlength = max - min;
  6267. // point to unit and unit to point conversions references to Plot DOM element top left corner.
  6268. if (this.breakPoints) {
  6269. unitlength = unitlength - this.breakPoints[1] + this.breakPoints[0];
  6270. this.p2u = function(p){
  6271. return (p - offmin) * unitlength / pixellength + min;
  6272. };
  6273. this.u2p = function(u){
  6274. if (u > this.breakPoints[0] && u < this.breakPoints[1]){
  6275. u = this.breakPoints[0];
  6276. }
  6277. if (u <= this.breakPoints[0]) {
  6278. return (u - min) * pixellength / unitlength + offmin;
  6279. }
  6280. else {
  6281. return (u - this.breakPoints[1] + this.breakPoints[0] - min) * pixellength / unitlength + offmin;
  6282. }
  6283. };
  6284. if (this.name.charAt(0) == 'x'){
  6285. this.series_u2p = function(u){
  6286. if (u > this.breakPoints[0] && u < this.breakPoints[1]){
  6287. u = this.breakPoints[0];
  6288. }
  6289. if (u <= this.breakPoints[0]) {
  6290. return (u - min) * pixellength / unitlength;
  6291. }
  6292. else {
  6293. return (u - this.breakPoints[1] + this.breakPoints[0] - min) * pixellength / unitlength;
  6294. }
  6295. };
  6296. this.series_p2u = function(p){
  6297. return p * unitlength / pixellength + min;
  6298. };
  6299. }
  6300. else {
  6301. this.series_u2p = function(u){
  6302. if (u > this.breakPoints[0] && u < this.breakPoints[1]){
  6303. u = this.breakPoints[0];
  6304. }
  6305. if (u >= this.breakPoints[1]) {
  6306. return (u - max) * pixellength / unitlength;
  6307. }
  6308. else {
  6309. return (u + this.breakPoints[1] - this.breakPoints[0] - max) * pixellength / unitlength;
  6310. }
  6311. };
  6312. this.series_p2u = function(p){
  6313. return p * unitlength / pixellength + max;
  6314. };
  6315. }
  6316. }
  6317. else {
  6318. this.p2u = function(p){
  6319. return (p - offmin) * unitlength / pixellength + min;
  6320. };
  6321. this.u2p = function(u){
  6322. return (u - min) * pixellength / unitlength + offmin;
  6323. };
  6324. if (this.name == 'xaxis' || this.name == 'x2axis'){
  6325. this.series_u2p = function(u){
  6326. return (u - min) * pixellength / unitlength;
  6327. };
  6328. this.series_p2u = function(p){
  6329. return p * unitlength / pixellength + min;
  6330. };
  6331. }
  6332. else {
  6333. this.series_u2p = function(u){
  6334. return (u - max) * pixellength / unitlength;
  6335. };
  6336. this.series_p2u = function(p){
  6337. return p * unitlength / pixellength + max;
  6338. };
  6339. }
  6340. }
  6341. if (this.show) {
  6342. if (this.name == 'xaxis' || this.name == 'x2axis') {
  6343. for (var i=0; i<ticks.length; i++) {
  6344. var t = ticks[i];
  6345. if (t.show && t.showLabel) {
  6346. var shim;
  6347. if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) {
  6348. // will need to adjust auto positioning based on which axis this is.
  6349. var temp = (this.name == 'xaxis') ? 1 : -1;
  6350. switch (t.labelPosition) {
  6351. case 'auto':
  6352. // position at end
  6353. if (temp * t.angle < 0) {
  6354. shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
  6355. }
  6356. // position at start
  6357. else {
  6358. shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2;
  6359. }
  6360. break;
  6361. case 'end':
  6362. shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
  6363. break;
  6364. case 'start':
  6365. shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2;
  6366. break;
  6367. case 'middle':
  6368. shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
  6369. break;
  6370. default:
  6371. shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
  6372. break;
  6373. }
  6374. }
  6375. else {
  6376. shim = -t.getWidth()/2;
  6377. }
  6378. var val = this.u2p(t.value) + shim + 'px';
  6379. t._elem.css('left', val);
  6380. t.pack();
  6381. }
  6382. }
  6383. if (lshow) {
  6384. var w = this._label._elem.outerWidth(true);
  6385. this._label._elem.css('left', offmin + pixellength/2 - w/2 + 'px');
  6386. if (this.name == 'xaxis') {
  6387. this._label._elem.css('bottom', '0px');
  6388. }
  6389. else {
  6390. this._label._elem.css('top', '0px');
  6391. }
  6392. this._label.pack();
  6393. }
  6394. }
  6395. else {
  6396. for (var i=0; i<ticks.length; i++) {
  6397. var t = ticks[i];
  6398. if (t.show && t.showLabel) {
  6399. var shim;
  6400. if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) {
  6401. // will need to adjust auto positioning based on which axis this is.
  6402. var temp = (this.name == 'yaxis') ? 1 : -1;
  6403. switch (t.labelPosition) {
  6404. case 'auto':
  6405. // position at end
  6406. case 'end':
  6407. if (temp * t.angle < 0) {
  6408. shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2;
  6409. }
  6410. else {
  6411. shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2;
  6412. }
  6413. break;
  6414. case 'start':
  6415. if (t.angle > 0) {
  6416. shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2;
  6417. }
  6418. else {
  6419. shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2;
  6420. }
  6421. break;
  6422. case 'middle':
  6423. // if (t.angle > 0) {
  6424. // shim = -t.getHeight()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
  6425. // }
  6426. // else {
  6427. // shim = -t.getHeight()/2 - t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2;
  6428. // }
  6429. shim = -t.getHeight()/2;
  6430. break;
  6431. default:
  6432. shim = -t.getHeight()/2;
  6433. break;
  6434. }
  6435. }
  6436. else {
  6437. shim = -t.getHeight()/2;
  6438. }
  6439. var val = this.u2p(t.value) + shim + 'px';
  6440. t._elem.css('top', val);
  6441. t.pack();
  6442. }
  6443. }
  6444. if (lshow) {
  6445. var h = this._label._elem.outerHeight(true);
  6446. this._label._elem.css('top', offmax - pixellength/2 - h/2 + 'px');
  6447. if (this.name == 'yaxis') {
  6448. this._label._elem.css('left', '0px');
  6449. }
  6450. else {
  6451. this._label._elem.css('right', '0px');
  6452. }
  6453. this._label.pack();
  6454. }
  6455. }
  6456. }
  6457. ticks = null;
  6458. };
  6459. /**
  6460. * The following code was generaously given to me a while back by Scott Prahl.
  6461. * He did a good job at computing axes min, max and number of ticks for the
  6462. * case where the user has not set any scale related parameters (tickInterval,
  6463. * numberTicks, min or max). I had ignored this use case for a long time,
  6464. * focusing on the more difficult case where user has set some option controlling
  6465. * tick generation. Anyway, about time I got this into jqPlot.
  6466. * Thanks Scott!!
  6467. */
  6468. /**
  6469. * Copyright (c) 2010 Scott Prahl
  6470. * The next three routines are currently available for use in all personal
  6471. * or commercial projects under both the MIT and GPL version 2.0 licenses.
  6472. * This means that you can choose the license that best suits your project
  6473. * and use it accordingly.
  6474. */
  6475. // A good format string depends on the interval. If the interval is greater
  6476. // than 1 then there is no need to show any decimal digits. If it is < 1.0, then
  6477. // use the magnitude of the interval to determine the number of digits to show.
  6478. function bestFormatString (interval)
  6479. {
  6480. var fstr;
  6481. interval = Math.abs(interval);
  6482. if (interval >= 10) {
  6483. fstr = '%d';
  6484. }
  6485. else if (interval > 1) {
  6486. if (interval === parseInt(interval, 10)) {
  6487. fstr = '%d';
  6488. }
  6489. else {
  6490. fstr = '%.1f';
  6491. }
  6492. }
  6493. else {
  6494. var expv = -Math.floor(Math.log(interval)/Math.LN10);
  6495. fstr = '%.' + expv + 'f';
  6496. }
  6497. return fstr;
  6498. }
  6499. var _factors = [0.1, 0.2, 0.3, 0.4, 0.5, 0.8, 1, 2, 3, 4, 5];
  6500. var _getLowerFactor = function(f) {
  6501. var i = _factors.indexOf(f);
  6502. if (i > 0) {
  6503. return _factors[i-1];
  6504. }
  6505. else {
  6506. return _factors[_factors.length - 1] / 100;
  6507. }
  6508. };
  6509. var _getHigherFactor = function(f) {
  6510. var i = _factors.indexOf(f);
  6511. if (i < _factors.length-1) {
  6512. return _factors[i+1];
  6513. }
  6514. else {
  6515. return _factors[0] * 100;
  6516. }
  6517. };
  6518. // Given a fixed minimum and maximum and a target number ot ticks
  6519. // figure out the best interval and
  6520. // return min, max, number ticks, format string and tick interval
  6521. function bestConstrainedInterval(min, max, nttarget) {
  6522. // run through possible number to ticks and see which interval is best
  6523. var low = Math.floor(nttarget/2);
  6524. var hi = Math.ceil(nttarget*1.5);
  6525. var badness = Number.MAX_VALUE;
  6526. var r = (max - min);
  6527. var temp;
  6528. var sd;
  6529. var bestNT;
  6530. var gsf = $.jqplot.getSignificantFigures;
  6531. var fsd;
  6532. var fs;
  6533. var currentNT;
  6534. var bestPrec;
  6535. for (var i=0, l=hi-low+1; i<l; i++) {
  6536. currentNT = low + i;
  6537. temp = r/(currentNT-1);
  6538. sd = gsf(temp);
  6539. temp = Math.abs(nttarget - currentNT) + sd.digitsRight;
  6540. if (temp < badness) {
  6541. badness = temp;
  6542. bestNT = currentNT;
  6543. bestPrec = sd.digitsRight;
  6544. }
  6545. else if (temp === badness) {
  6546. // let nicer ticks trump number ot ticks
  6547. if (sd.digitsRight < bestPrec) {
  6548. bestNT = currentNT;
  6549. bestPrec = sd.digitsRight;
  6550. }
  6551. }
  6552. }
  6553. fsd = Math.max(bestPrec, Math.max(gsf(min).digitsRight, gsf(max).digitsRight));
  6554. if (fsd === 0) {
  6555. fs = '%d';
  6556. }
  6557. else {
  6558. fs = '%.' + fsd + 'f';
  6559. }
  6560. temp = r / (bestNT - 1);
  6561. // min, max, number ticks, format string, tick interval
  6562. return [min, max, bestNT, fs, temp];
  6563. }
  6564. // This will return an interval of form 2 * 10^n, 5 * 10^n or 10 * 10^n
  6565. // it is based soley on the range and number of ticks. So if user specifies
  6566. // number of ticks, use this.
  6567. function bestInterval(range, numberTicks) {
  6568. numberTicks = numberTicks || 7;
  6569. var minimum = range / (numberTicks - 1);
  6570. var magnitude = Math.pow(10, Math.floor(Math.log(minimum) / Math.LN10));
  6571. var residual = minimum / magnitude;
  6572. var interval;
  6573. // "nicest" ranges are 1, 2, 5 or powers of these.
  6574. // for magnitudes below 1, only allow these.
  6575. if (magnitude < 1) {
  6576. if (residual > 5) {
  6577. interval = 10 * magnitude;
  6578. }
  6579. else if (residual > 2) {
  6580. interval = 5 * magnitude;
  6581. }
  6582. else if (residual > 1) {
  6583. interval = 2 * magnitude;
  6584. }
  6585. else {
  6586. interval = magnitude;
  6587. }
  6588. }
  6589. // for large ranges (whole integers), allow intervals like 3, 4 or powers of these.
  6590. // this helps a lot with poor choices for number of ticks.
  6591. else {
  6592. if (residual > 5) {
  6593. interval = 10 * magnitude;
  6594. }
  6595. else if (residual > 4) {
  6596. interval = 5 * magnitude;
  6597. }
  6598. else if (residual > 3) {
  6599. interval = 4 * magnitude;
  6600. }
  6601. else if (residual > 2) {
  6602. interval = 3 * magnitude;
  6603. }
  6604. else if (residual > 1) {
  6605. interval = 2 * magnitude;
  6606. }
  6607. else {
  6608. interval = magnitude;
  6609. }
  6610. }
  6611. return interval;
  6612. }
  6613. // This will return an interval of form 2 * 10^n, 5 * 10^n or 10 * 10^n
  6614. // it is based soley on the range of data, number of ticks must be computed later.
  6615. function bestLinearInterval(range, scalefact) {
  6616. scalefact = scalefact || 1;
  6617. var expv = Math.floor(Math.log(range)/Math.LN10);
  6618. var magnitude = Math.pow(10, expv);
  6619. // 0 < f < 10
  6620. var f = range / magnitude;
  6621. var fact;
  6622. // for large plots, scalefact will decrease f and increase number of ticks.
  6623. // for small plots, scalefact will increase f and decrease number of ticks.
  6624. f = f/scalefact;
  6625. // for large plots, smaller interval, more ticks.
  6626. if (f<=0.38) {
  6627. fact = 0.1;
  6628. }
  6629. else if (f<=1.6) {
  6630. fact = 0.2;
  6631. }
  6632. else if (f<=4.0) {
  6633. fact = 0.5;
  6634. }
  6635. else if (f<=8.0) {
  6636. fact = 1.0;
  6637. }
  6638. // for very small plots, larger interval, less ticks in number ticks
  6639. else if (f<=16.0) {
  6640. fact = 2;
  6641. }
  6642. else {
  6643. fact = 5;
  6644. }
  6645. return fact*magnitude;
  6646. }
  6647. function bestLinearComponents(range, scalefact) {
  6648. var expv = Math.floor(Math.log(range)/Math.LN10);
  6649. var magnitude = Math.pow(10, expv);
  6650. // 0 < f < 10
  6651. var f = range / magnitude;
  6652. var interval;
  6653. var fact;
  6654. // for large plots, scalefact will decrease f and increase number of ticks.
  6655. // for small plots, scalefact will increase f and decrease number of ticks.
  6656. f = f/scalefact;
  6657. // for large plots, smaller interval, more ticks.
  6658. if (f<=0.38) {
  6659. fact = 0.1;
  6660. }
  6661. else if (f<=1.6) {
  6662. fact = 0.2;
  6663. }
  6664. else if (f<=4.0) {
  6665. fact = 0.5;
  6666. }
  6667. else if (f<=8.0) {
  6668. fact = 1.0;
  6669. }
  6670. // for very small plots, larger interval, less ticks in number ticks
  6671. else if (f<=16.0) {
  6672. fact = 2;
  6673. }
  6674. // else if (f<=20.0) {
  6675. // fact = 3;
  6676. // }
  6677. // else if (f<=24.0) {
  6678. // fact = 4;
  6679. // }
  6680. else {
  6681. fact = 5;
  6682. }
  6683. interval = fact * magnitude;
  6684. return [interval, fact, magnitude];
  6685. }
  6686. // Given the min and max for a dataset, return suitable endpoints
  6687. // for the graphing, a good number for the number of ticks, and a
  6688. // format string so that extraneous digits are not displayed.
  6689. // returned is an array containing [min, max, nTicks, format]
  6690. $.jqplot.LinearTickGenerator = function(axis_min, axis_max, scalefact, numberTicks, keepMin, keepMax) {
  6691. // Set to preserve EITHER min OR max.
  6692. // If min is preserved, max must be free.
  6693. keepMin = (keepMin === null) ? false : keepMin;
  6694. keepMax = (keepMax === null || keepMin) ? false : keepMax;
  6695. // if endpoints are equal try to include zero otherwise include one
  6696. if (axis_min === axis_max) {
  6697. axis_max = (axis_max) ? 0 : 1;
  6698. }
  6699. scalefact = scalefact || 1.0;
  6700. // make sure range is positive
  6701. if (axis_max < axis_min) {
  6702. var a = axis_max;
  6703. axis_max = axis_min;
  6704. axis_min = a;
  6705. }
  6706. var r = [];
  6707. var ss = bestLinearInterval(axis_max - axis_min, scalefact);
  6708. var gsf = $.jqplot.getSignificantFigures;
  6709. if (numberTicks == null) {
  6710. // Figure out the axis min, max and number of ticks
  6711. // the min and max will be some multiple of the tick interval,
  6712. // 1*10^n, 2*10^n or 5*10^n. This gaurantees that, if the
  6713. // axis min is negative, 0 will be a tick.
  6714. if (!keepMin && !keepMax) {
  6715. r[0] = Math.floor(axis_min / ss) * ss; // min
  6716. r[1] = Math.ceil(axis_max / ss) * ss; // max
  6717. r[2] = Math.round((r[1]-r[0])/ss+1.0); // number of ticks
  6718. r[3] = bestFormatString(ss); // format string
  6719. r[4] = ss; // tick Interval
  6720. }
  6721. else if (keepMin) {
  6722. r[0] = axis_min; // min
  6723. r[2] = Math.ceil((axis_max - axis_min) / ss + 1.0); // number of ticks
  6724. r[1] = axis_min + (r[2] - 1) * ss; // max
  6725. var digitsMin = gsf(axis_min).digitsRight;
  6726. var digitsSS = gsf(ss).digitsRight;
  6727. if (digitsMin < digitsSS) {
  6728. r[3] = bestFormatString(ss); // format string
  6729. }
  6730. else {
  6731. r[3] = '%.' + digitsMin + 'f';
  6732. }
  6733. r[4] = ss; // tick Interval
  6734. }
  6735. else if (keepMax) {
  6736. r[1] = axis_max; // max
  6737. r[2] = Math.ceil((axis_max - axis_min) / ss + 1.0); // number of ticks
  6738. r[0] = axis_max - (r[2] - 1) * ss; // min
  6739. var digitsMax = gsf(axis_max).digitsRight;
  6740. var digitsSS = gsf(ss).digitsRight;
  6741. if (digitsMax < digitsSS) {
  6742. r[3] = bestFormatString(ss); // format string
  6743. }
  6744. else {
  6745. r[3] = '%.' + digitsMax + 'f';
  6746. }
  6747. r[4] = ss; // tick Interval
  6748. }
  6749. }
  6750. else {
  6751. var tempr = [];
  6752. // Figure out the axis min, max and number of ticks
  6753. // the min and max will be some multiple of the tick interval,
  6754. // 1*10^n, 2*10^n or 5*10^n. This gaurantees that, if the
  6755. // axis min is negative, 0 will be a tick.
  6756. tempr[0] = Math.floor(axis_min / ss) * ss; // min
  6757. tempr[1] = Math.ceil(axis_max / ss) * ss; // max
  6758. tempr[2] = Math.round((tempr[1]-tempr[0])/ss+1.0); // number of ticks
  6759. tempr[3] = bestFormatString(ss); // format string
  6760. tempr[4] = ss; // tick Interval
  6761. // first, see if we happen to get the right number of ticks
  6762. if (tempr[2] === numberTicks) {
  6763. r = tempr;
  6764. }
  6765. else {
  6766. var newti = bestInterval(tempr[1] - tempr[0], numberTicks);
  6767. r[0] = tempr[0]; // min
  6768. r[2] = numberTicks; // number of ticks
  6769. r[4] = newti; // tick interval
  6770. r[3] = bestFormatString(newti); // format string
  6771. r[1] = r[0] + (r[2] - 1) * r[4]; // max
  6772. }
  6773. }
  6774. return r;
  6775. };
  6776. $.jqplot.LinearTickGenerator.bestLinearInterval = bestLinearInterval;
  6777. $.jqplot.LinearTickGenerator.bestInterval = bestInterval;
  6778. $.jqplot.LinearTickGenerator.bestLinearComponents = bestLinearComponents;
  6779. $.jqplot.LinearTickGenerator.bestConstrainedInterval = bestConstrainedInterval;
  6780. // class: $.jqplot.MarkerRenderer
  6781. // The default jqPlot marker renderer, rendering the points on the line.
  6782. $.jqplot.MarkerRenderer = function(options){
  6783. // Group: Properties
  6784. // prop: show
  6785. // whether or not to show the marker.
  6786. this.show = true;
  6787. // prop: style
  6788. // One of diamond, circle, square, x, plus, dash, filledDiamond, filledCircle, filledSquare
  6789. this.style = 'filledCircle';
  6790. // prop: lineWidth
  6791. // size of the line for non-filled markers.
  6792. this.lineWidth = 2;
  6793. // prop: size
  6794. // Size of the marker (diameter or circle, length of edge of square, etc.)
  6795. this.size = 9.0;
  6796. // prop: color
  6797. // color of marker. Will be set to color of series by default on init.
  6798. this.color = '#666666';
  6799. // prop: shadow
  6800. // whether or not to draw a shadow on the line
  6801. this.shadow = true;
  6802. // prop: shadowAngle
  6803. // Shadow angle in degrees
  6804. this.shadowAngle = 45;
  6805. // prop: shadowOffset
  6806. // Shadow offset from line in pixels
  6807. this.shadowOffset = 1;
  6808. // prop: shadowDepth
  6809. // Number of times shadow is stroked, each stroke offset shadowOffset from the last.
  6810. this.shadowDepth = 3;
  6811. // prop: shadowAlpha
  6812. // Alpha channel transparency of shadow. 0 = transparent.
  6813. this.shadowAlpha = '0.07';
  6814. // prop: shadowRenderer
  6815. // Renderer that will draws the shadows on the marker.
  6816. this.shadowRenderer = new $.jqplot.ShadowRenderer();
  6817. // prop: shapeRenderer
  6818. // Renderer that will draw the marker.
  6819. this.shapeRenderer = new $.jqplot.ShapeRenderer();
  6820. $.extend(true, this, options);
  6821. };
  6822. $.jqplot.MarkerRenderer.prototype.init = function(options) {
  6823. $.extend(true, this, options);
  6824. var sdopt = {angle:this.shadowAngle, offset:this.shadowOffset, alpha:this.shadowAlpha, lineWidth:this.lineWidth, depth:this.shadowDepth, closePath:true};
  6825. if (this.style.indexOf('filled') != -1) {
  6826. sdopt.fill = true;
  6827. }
  6828. if (this.style.indexOf('ircle') != -1) {
  6829. sdopt.isarc = true;
  6830. sdopt.closePath = false;
  6831. }
  6832. this.shadowRenderer.init(sdopt);
  6833. var shopt = {fill:false, isarc:false, strokeStyle:this.color, fillStyle:this.color, lineWidth:this.lineWidth, closePath:true};
  6834. if (this.style.indexOf('filled') != -1) {
  6835. shopt.fill = true;
  6836. }
  6837. if (this.style.indexOf('ircle') != -1) {
  6838. shopt.isarc = true;
  6839. shopt.closePath = false;
  6840. }
  6841. this.shapeRenderer.init(shopt);
  6842. };
  6843. $.jqplot.MarkerRenderer.prototype.drawDiamond = function(x, y, ctx, fill, options) {
  6844. var stretch = 1.2;
  6845. var dx = this.size/2/stretch;
  6846. var dy = this.size/2*stretch;
  6847. var points = [[x-dx, y], [x, y+dy], [x+dx, y], [x, y-dy]];
  6848. if (this.shadow) {
  6849. this.shadowRenderer.draw(ctx, points);
  6850. }
  6851. this.shapeRenderer.draw(ctx, points, options);
  6852. };
  6853. $.jqplot.MarkerRenderer.prototype.drawPlus = function(x, y, ctx, fill, options) {
  6854. var stretch = 1.0;
  6855. var dx = this.size/2*stretch;
  6856. var dy = this.size/2*stretch;
  6857. var points1 = [[x, y-dy], [x, y+dy]];
  6858. var points2 = [[x+dx, y], [x-dx, y]];
  6859. var opts = $.extend(true, {}, this.options, {closePath:false});
  6860. if (this.shadow) {
  6861. this.shadowRenderer.draw(ctx, points1, {closePath:false});
  6862. this.shadowRenderer.draw(ctx, points2, {closePath:false});
  6863. }
  6864. this.shapeRenderer.draw(ctx, points1, opts);
  6865. this.shapeRenderer.draw(ctx, points2, opts);
  6866. };
  6867. $.jqplot.MarkerRenderer.prototype.drawX = function(x, y, ctx, fill, options) {
  6868. var stretch = 1.0;
  6869. var dx = this.size/2*stretch;
  6870. var dy = this.size/2*stretch;
  6871. var opts = $.extend(true, {}, this.options, {closePath:false});
  6872. var points1 = [[x-dx, y-dy], [x+dx, y+dy]];
  6873. var points2 = [[x-dx, y+dy], [x+dx, y-dy]];
  6874. if (this.shadow) {
  6875. this.shadowRenderer.draw(ctx, points1, {closePath:false});
  6876. this.shadowRenderer.draw(ctx, points2, {closePath:false});
  6877. }
  6878. this.shapeRenderer.draw(ctx, points1, opts);
  6879. this.shapeRenderer.draw(ctx, points2, opts);
  6880. };
  6881. $.jqplot.MarkerRenderer.prototype.drawDash = function(x, y, ctx, fill, options) {
  6882. var stretch = 1.0;
  6883. var dx = this.size/2*stretch;
  6884. var dy = this.size/2*stretch;
  6885. var points = [[x-dx, y], [x+dx, y]];
  6886. if (this.shadow) {
  6887. this.shadowRenderer.draw(ctx, points);
  6888. }
  6889. this.shapeRenderer.draw(ctx, points, options);
  6890. };
  6891. $.jqplot.MarkerRenderer.prototype.drawLine = function(p1, p2, ctx, fill, options) {
  6892. var points = [p1, p2];
  6893. if (this.shadow) {
  6894. this.shadowRenderer.draw(ctx, points);
  6895. }
  6896. this.shapeRenderer.draw(ctx, points, options);
  6897. };
  6898. $.jqplot.MarkerRenderer.prototype.drawSquare = function(x, y, ctx, fill, options) {
  6899. var stretch = 1.0;
  6900. var dx = this.size/2/stretch;
  6901. var dy = this.size/2*stretch;
  6902. var points = [[x-dx, y-dy], [x-dx, y+dy], [x+dx, y+dy], [x+dx, y-dy]];
  6903. if (this.shadow) {
  6904. this.shadowRenderer.draw(ctx, points);
  6905. }
  6906. this.shapeRenderer.draw(ctx, points, options);
  6907. };
  6908. $.jqplot.MarkerRenderer.prototype.drawCircle = function(x, y, ctx, fill, options) {
  6909. var radius = this.size/2;
  6910. var end = 2*Math.PI;
  6911. var points = [x, y, radius, 0, end, true];
  6912. if (this.shadow) {
  6913. this.shadowRenderer.draw(ctx, points);
  6914. }
  6915. this.shapeRenderer.draw(ctx, points, options);
  6916. };
  6917. $.jqplot.MarkerRenderer.prototype.draw = function(x, y, ctx, options) {
  6918. options = options || {};
  6919. // hack here b/c shape renderer uses canvas based color style options
  6920. // and marker uses css style names.
  6921. if (options.show == null || options.show != false) {
  6922. if (options.color && !options.fillStyle) {
  6923. options.fillStyle = options.color;
  6924. }
  6925. if (options.color && !options.strokeStyle) {
  6926. options.strokeStyle = options.color;
  6927. }
  6928. switch (this.style) {
  6929. case 'diamond':
  6930. this.drawDiamond(x,y,ctx, false, options);
  6931. break;
  6932. case 'filledDiamond':
  6933. this.drawDiamond(x,y,ctx, true, options);
  6934. break;
  6935. case 'circle':
  6936. this.drawCircle(x,y,ctx, false, options);
  6937. break;
  6938. case 'filledCircle':
  6939. this.drawCircle(x,y,ctx, true, options);
  6940. break;
  6941. case 'square':
  6942. this.drawSquare(x,y,ctx, false, options);
  6943. break;
  6944. case 'filledSquare':
  6945. this.drawSquare(x,y,ctx, true, options);
  6946. break;
  6947. case 'x':
  6948. this.drawX(x,y,ctx, true, options);
  6949. break;
  6950. case 'plus':
  6951. this.drawPlus(x,y,ctx, true, options);
  6952. break;
  6953. case 'dash':
  6954. this.drawDash(x,y,ctx, true, options);
  6955. break;
  6956. case 'line':
  6957. this.drawLine(x, y, ctx, false, options);
  6958. break;
  6959. default:
  6960. this.drawDiamond(x,y,ctx, false, options);
  6961. break;
  6962. }
  6963. }
  6964. };
  6965. // class: $.jqplot.shadowRenderer
  6966. // The default jqPlot shadow renderer, rendering shadows behind shapes.
  6967. $.jqplot.ShadowRenderer = function(options){
  6968. // Group: Properties
  6969. // prop: angle
  6970. // Angle of the shadow in degrees. Measured counter-clockwise from the x axis.
  6971. this.angle = 45;
  6972. // prop: offset
  6973. // Pixel offset at the given shadow angle of each shadow stroke from the last stroke.
  6974. this.offset = 1;
  6975. // prop: alpha
  6976. // alpha transparency of shadow stroke.
  6977. this.alpha = 0.07;
  6978. // prop: lineWidth
  6979. // width of the shadow line stroke.
  6980. this.lineWidth = 1.5;
  6981. // prop: lineJoin
  6982. // How line segments of the shadow are joined.
  6983. this.lineJoin = 'miter';
  6984. // prop: lineCap
  6985. // how ends of the shadow line are rendered.
  6986. this.lineCap = 'round';
  6987. // prop; closePath
  6988. // whether line path segment is closed upon itself.
  6989. this.closePath = false;
  6990. // prop: fill
  6991. // whether to fill the shape.
  6992. this.fill = false;
  6993. // prop: depth
  6994. // how many times the shadow is stroked. Each stroke will be offset by offset at angle degrees.
  6995. this.depth = 3;
  6996. this.strokeStyle = 'rgba(0,0,0,0.1)';
  6997. // prop: isarc
  6998. // whether the shadow is an arc or not.
  6999. this.isarc = false;
  7000. $.extend(true, this, options);
  7001. };
  7002. $.jqplot.ShadowRenderer.prototype.init = function(options) {
  7003. $.extend(true, this, options);
  7004. };
  7005. // function: draw
  7006. // draws an transparent black (i.e. gray) shadow.
  7007. //
  7008. // ctx - canvas drawing context
  7009. // points - array of points or [x, y, radius, start angle (rad), end angle (rad)]
  7010. $.jqplot.ShadowRenderer.prototype.draw = function(ctx, points, options) {
  7011. ctx.save();
  7012. var opts = (options != null) ? options : {};
  7013. var fill = (opts.fill != null) ? opts.fill : this.fill;
  7014. var fillRect = (opts.fillRect != null) ? opts.fillRect : this.fillRect;
  7015. var closePath = (opts.closePath != null) ? opts.closePath : this.closePath;
  7016. var offset = (opts.offset != null) ? opts.offset : this.offset;
  7017. var alpha = (opts.alpha != null) ? opts.alpha : this.alpha;
  7018. var depth = (opts.depth != null) ? opts.depth : this.depth;
  7019. var isarc = (opts.isarc != null) ? opts.isarc : this.isarc;
  7020. var linePattern = (opts.linePattern != null) ? opts.linePattern : this.linePattern;
  7021. ctx.lineWidth = (opts.lineWidth != null) ? opts.lineWidth : this.lineWidth;
  7022. ctx.lineJoin = (opts.lineJoin != null) ? opts.lineJoin : this.lineJoin;
  7023. ctx.lineCap = (opts.lineCap != null) ? opts.lineCap : this.lineCap;
  7024. ctx.strokeStyle = opts.strokeStyle || this.strokeStyle || 'rgba(0,0,0,'+alpha+')';
  7025. ctx.fillStyle = opts.fillStyle || this.fillStyle || 'rgba(0,0,0,'+alpha+')';
  7026. for (var j=0; j<depth; j++) {
  7027. var ctxPattern = $.jqplot.LinePattern(ctx, linePattern);
  7028. ctx.translate(Math.cos(this.angle*Math.PI/180)*offset, Math.sin(this.angle*Math.PI/180)*offset);
  7029. ctxPattern.beginPath();
  7030. if (isarc) {
  7031. ctx.arc(points[0], points[1], points[2], points[3], points[4], true);
  7032. }
  7033. else if (fillRect) {
  7034. if (fillRect) {
  7035. ctx.fillRect(points[0], points[1], points[2], points[3]);
  7036. }
  7037. }
  7038. else if (points && points.length){
  7039. var move = true;
  7040. for (var i=0; i<points.length; i++) {
  7041. // skip to the first non-null point and move to it.
  7042. if (points[i][0] != null && points[i][1] != null) {
  7043. if (move) {
  7044. ctxPattern.moveTo(points[i][0], points[i][1]);
  7045. move = false;
  7046. }
  7047. else {
  7048. ctxPattern.lineTo(points[i][0], points[i][1]);
  7049. }
  7050. }
  7051. else {
  7052. move = true;
  7053. }
  7054. }
  7055. }
  7056. if (closePath) {
  7057. ctxPattern.closePath();
  7058. }
  7059. if (fill) {
  7060. ctx.fill();
  7061. }
  7062. else {
  7063. ctx.stroke();
  7064. }
  7065. }
  7066. ctx.restore();
  7067. };
  7068. // class: $.jqplot.shapeRenderer
  7069. // The default jqPlot shape renderer. Given a set of points will
  7070. // plot them and either stroke a line (fill = false) or fill them (fill = true).
  7071. // If a filled shape is desired, closePath = true must also be set to close
  7072. // the shape.
  7073. $.jqplot.ShapeRenderer = function(options){
  7074. this.lineWidth = 1.5;
  7075. // prop: linePattern
  7076. // line pattern 'dashed', 'dotted', 'solid', some combination
  7077. // of '-' and '.' characters such as '.-.' or a numerical array like
  7078. // [draw, skip, draw, skip, ...] such as [1, 10] to draw a dotted line,
  7079. // [1, 10, 20, 10] to draw a dot-dash line, and so on.
  7080. this.linePattern = 'solid';
  7081. // prop: lineJoin
  7082. // How line segments of the shadow are joined.
  7083. this.lineJoin = 'miter';
  7084. // prop: lineCap
  7085. // how ends of the shadow line are rendered.
  7086. this.lineCap = 'round';
  7087. // prop; closePath
  7088. // whether line path segment is closed upon itself.
  7089. this.closePath = false;
  7090. // prop: fill
  7091. // whether to fill the shape.
  7092. this.fill = false;
  7093. // prop: isarc
  7094. // whether the shadow is an arc or not.
  7095. this.isarc = false;
  7096. // prop: fillRect
  7097. // true to draw shape as a filled rectangle.
  7098. this.fillRect = false;
  7099. // prop: strokeRect
  7100. // true to draw shape as a stroked rectangle.
  7101. this.strokeRect = false;
  7102. // prop: clearRect
  7103. // true to cear a rectangle.
  7104. this.clearRect = false;
  7105. // prop: strokeStyle
  7106. // css color spec for the stoke style
  7107. this.strokeStyle = '#999999';
  7108. // prop: fillStyle
  7109. // css color spec for the fill style.
  7110. this.fillStyle = '#999999';
  7111. $.extend(true, this, options);
  7112. };
  7113. $.jqplot.ShapeRenderer.prototype.init = function(options) {
  7114. $.extend(true, this, options);
  7115. };
  7116. // function: draw
  7117. // draws the shape.
  7118. //
  7119. // ctx - canvas drawing context
  7120. // points - array of points for shapes or
  7121. // [x, y, width, height] for rectangles or
  7122. // [x, y, radius, start angle (rad), end angle (rad)] for circles and arcs.
  7123. $.jqplot.ShapeRenderer.prototype.draw = function(ctx, points, options) {
  7124. ctx.save();
  7125. var opts = (options != null) ? options : {};
  7126. var fill = (opts.fill != null) ? opts.fill : this.fill;
  7127. var closePath = (opts.closePath != null) ? opts.closePath : this.closePath;
  7128. var fillRect = (opts.fillRect != null) ? opts.fillRect : this.fillRect;
  7129. var strokeRect = (opts.strokeRect != null) ? opts.strokeRect : this.strokeRect;
  7130. var clearRect = (opts.clearRect != null) ? opts.clearRect : this.clearRect;
  7131. var isarc = (opts.isarc != null) ? opts.isarc : this.isarc;
  7132. var linePattern = (opts.linePattern != null) ? opts.linePattern : this.linePattern;
  7133. var ctxPattern = $.jqplot.LinePattern(ctx, linePattern);
  7134. ctx.lineWidth = opts.lineWidth || this.lineWidth;
  7135. ctx.lineJoin = opts.lineJoin || this.lineJoin;
  7136. ctx.lineCap = opts.lineCap || this.lineCap;
  7137. ctx.strokeStyle = (opts.strokeStyle || opts.color) || this.strokeStyle;
  7138. ctx.fillStyle = opts.fillStyle || this.fillStyle;
  7139. ctx.beginPath();
  7140. if (isarc) {
  7141. ctx.arc(points[0], points[1], points[2], points[3], points[4], true);
  7142. if (closePath) {
  7143. ctx.closePath();
  7144. }
  7145. if (fill) {
  7146. ctx.fill();
  7147. }
  7148. else {
  7149. ctx.stroke();
  7150. }
  7151. ctx.restore();
  7152. return;
  7153. }
  7154. else if (clearRect) {
  7155. ctx.clearRect(points[0], points[1], points[2], points[3]);
  7156. ctx.restore();
  7157. return;
  7158. }
  7159. else if (fillRect || strokeRect) {
  7160. if (fillRect) {
  7161. ctx.fillRect(points[0], points[1], points[2], points[3]);
  7162. }
  7163. if (strokeRect) {
  7164. ctx.strokeRect(points[0], points[1], points[2], points[3]);
  7165. ctx.restore();
  7166. return;
  7167. }
  7168. }
  7169. else if (points && points.length){
  7170. var move = true;
  7171. for (var i=0; i<points.length; i++) {
  7172. // skip to the first non-null point and move to it.
  7173. if (points[i][0] != null && points[i][1] != null) {
  7174. if (move) {
  7175. ctxPattern.moveTo(points[i][0], points[i][1]);
  7176. move = false;
  7177. }
  7178. else {
  7179. ctxPattern.lineTo(points[i][0], points[i][1]);
  7180. }
  7181. }
  7182. else {
  7183. move = true;
  7184. }
  7185. }
  7186. if (closePath) {
  7187. ctxPattern.closePath();
  7188. }
  7189. if (fill) {
  7190. ctx.fill();
  7191. }
  7192. else {
  7193. ctx.stroke();
  7194. }
  7195. }
  7196. ctx.restore();
  7197. };
  7198. // class $.jqplot.TableLegendRenderer
  7199. // The default legend renderer for jqPlot.
  7200. $.jqplot.TableLegendRenderer = function(){
  7201. //
  7202. };
  7203. $.jqplot.TableLegendRenderer.prototype.init = function(options) {
  7204. $.extend(true, this, options);
  7205. };
  7206. $.jqplot.TableLegendRenderer.prototype.addrow = function (label, color, pad, reverse) {
  7207. var rs = (pad) ? this.rowSpacing+'px' : '0px';
  7208. var tr;
  7209. var td;
  7210. var elem;
  7211. var div0;
  7212. var div1;
  7213. elem = document.createElement('tr');
  7214. tr = $(elem);
  7215. tr.addClass('jqplot-table-legend');
  7216. elem = null;
  7217. if (reverse){
  7218. tr.prependTo(this._elem);
  7219. }
  7220. else{
  7221. tr.appendTo(this._elem);
  7222. }
  7223. if (this.showSwatches) {
  7224. td = $(document.createElement('td'));
  7225. td.addClass('jqplot-table-legend jqplot-table-legend-swatch');
  7226. td.css({textAlign: 'center', paddingTop: rs});
  7227. div0 = $(document.createElement('div'));
  7228. div0.addClass('jqplot-table-legend-swatch-outline');
  7229. div1 = $(document.createElement('div'));
  7230. div1.addClass('jqplot-table-legend-swatch');
  7231. div1.css({backgroundColor: color, borderColor: color});
  7232. tr.append(td.append(div0.append(div1)));
  7233. // $('<td class="jqplot-table-legend" style="text-align:center;padding-top:'+rs+';">'+
  7234. // '<div><div class="jqplot-table-legend-swatch" style="background-color:'+color+';border-color:'+color+';"></div>'+
  7235. // '</div></td>').appendTo(tr);
  7236. }
  7237. if (this.showLabels) {
  7238. td = $(document.createElement('td'));
  7239. td.addClass('jqplot-table-legend jqplot-table-legend-label');
  7240. td.css('paddingTop', rs);
  7241. tr.append(td);
  7242. // elem = $('<td class="jqplot-table-legend" style="padding-top:'+rs+';"></td>');
  7243. // elem.appendTo(tr);
  7244. if (this.escapeHtml) {
  7245. td.text(label);
  7246. }
  7247. else {
  7248. td.html(label);
  7249. }
  7250. }
  7251. td = null;
  7252. div0 = null;
  7253. div1 = null;
  7254. tr = null;
  7255. elem = null;
  7256. };
  7257. // called with scope of legend
  7258. $.jqplot.TableLegendRenderer.prototype.draw = function() {
  7259. if (this._elem) {
  7260. this._elem.emptyForce();
  7261. this._elem = null;
  7262. }
  7263. if (this.show) {
  7264. var series = this._series;
  7265. // make a table. one line label per row.
  7266. var elem = document.createElement('table');
  7267. this._elem = $(elem);
  7268. this._elem.addClass('jqplot-table-legend');
  7269. var ss = {position:'absolute'};
  7270. if (this.background) {
  7271. ss['background'] = this.background;
  7272. }
  7273. if (this.border) {
  7274. ss['border'] = this.border;
  7275. }
  7276. if (this.fontSize) {
  7277. ss['fontSize'] = this.fontSize;
  7278. }
  7279. if (this.fontFamily) {
  7280. ss['fontFamily'] = this.fontFamily;
  7281. }
  7282. if (this.textColor) {
  7283. ss['textColor'] = this.textColor;
  7284. }
  7285. if (this.marginTop != null) {
  7286. ss['marginTop'] = this.marginTop;
  7287. }
  7288. if (this.marginBottom != null) {
  7289. ss['marginBottom'] = this.marginBottom;
  7290. }
  7291. if (this.marginLeft != null) {
  7292. ss['marginLeft'] = this.marginLeft;
  7293. }
  7294. if (this.marginRight != null) {
  7295. ss['marginRight'] = this.marginRight;
  7296. }
  7297. var pad = false,
  7298. reverse = false,
  7299. s;
  7300. for (var i = 0; i< series.length; i++) {
  7301. s = series[i];
  7302. if (s._stack || s.renderer.constructor == $.jqplot.BezierCurveRenderer){
  7303. reverse = true;
  7304. }
  7305. if (s.show && s.showLabel) {
  7306. var lt = this.labels[i] || s.label.toString();
  7307. if (lt) {
  7308. var color = s.color;
  7309. if (reverse && i < series.length - 1){
  7310. pad = true;
  7311. }
  7312. else if (reverse && i == series.length - 1){
  7313. pad = false;
  7314. }
  7315. this.renderer.addrow.call(this, lt, color, pad, reverse);
  7316. pad = true;
  7317. }
  7318. // let plugins add more rows to legend. Used by trend line plugin.
  7319. for (var j=0; j<$.jqplot.addLegendRowHooks.length; j++) {
  7320. var item = $.jqplot.addLegendRowHooks[j].call(this, s);
  7321. if (item) {
  7322. this.renderer.addrow.call(this, item.label, item.color, pad);
  7323. pad = true;
  7324. }
  7325. }
  7326. lt = null;
  7327. }
  7328. }
  7329. }
  7330. return this._elem;
  7331. };
  7332. $.jqplot.TableLegendRenderer.prototype.pack = function(offsets) {
  7333. if (this.show) {
  7334. if (this.placement == 'insideGrid') {
  7335. switch (this.location) {
  7336. case 'nw':
  7337. var a = offsets.left;
  7338. var b = offsets.top;
  7339. this._elem.css('left', a);
  7340. this._elem.css('top', b);
  7341. break;
  7342. case 'n':
  7343. var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
  7344. var b = offsets.top;
  7345. this._elem.css('left', a);
  7346. this._elem.css('top', b);
  7347. break;
  7348. case 'ne':
  7349. var a = offsets.right;
  7350. var b = offsets.top;
  7351. this._elem.css({right:a, top:b});
  7352. break;
  7353. case 'e':
  7354. var a = offsets.right;
  7355. var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
  7356. this._elem.css({right:a, top:b});
  7357. break;
  7358. case 'se':
  7359. var a = offsets.right;
  7360. var b = offsets.bottom;
  7361. this._elem.css({right:a, bottom:b});
  7362. break;
  7363. case 's':
  7364. var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
  7365. var b = offsets.bottom;
  7366. this._elem.css({left:a, bottom:b});
  7367. break;
  7368. case 'sw':
  7369. var a = offsets.left;
  7370. var b = offsets.bottom;
  7371. this._elem.css({left:a, bottom:b});
  7372. break;
  7373. case 'w':
  7374. var a = offsets.left;
  7375. var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
  7376. this._elem.css({left:a, top:b});
  7377. break;
  7378. default: // same as 'se'
  7379. var a = offsets.right;
  7380. var b = offsets.bottom;
  7381. this._elem.css({right:a, bottom:b});
  7382. break;
  7383. }
  7384. }
  7385. else if (this.placement == 'outside'){
  7386. switch (this.location) {
  7387. case 'nw':
  7388. var a = this._plotDimensions.width - offsets.left;
  7389. var b = offsets.top;
  7390. this._elem.css('right', a);
  7391. this._elem.css('top', b);
  7392. break;
  7393. case 'n':
  7394. var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
  7395. var b = this._plotDimensions.height - offsets.top;
  7396. this._elem.css('left', a);
  7397. this._elem.css('bottom', b);
  7398. break;
  7399. case 'ne':
  7400. var a = this._plotDimensions.width - offsets.right;
  7401. var b = offsets.top;
  7402. this._elem.css({left:a, top:b});
  7403. break;
  7404. case 'e':
  7405. var a = this._plotDimensions.width - offsets.right;
  7406. var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
  7407. this._elem.css({left:a, top:b});
  7408. break;
  7409. case 'se':
  7410. var a = this._plotDimensions.width - offsets.right;
  7411. var b = offsets.bottom;
  7412. this._elem.css({left:a, bottom:b});
  7413. break;
  7414. case 's':
  7415. var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
  7416. var b = this._plotDimensions.height - offsets.bottom;
  7417. this._elem.css({left:a, top:b});
  7418. break;
  7419. case 'sw':
  7420. var a = this._plotDimensions.width - offsets.left;
  7421. var b = offsets.bottom;
  7422. this._elem.css({right:a, bottom:b});
  7423. break;
  7424. case 'w':
  7425. var a = this._plotDimensions.width - offsets.left;
  7426. var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
  7427. this._elem.css({right:a, top:b});
  7428. break;
  7429. default: // same as 'se'
  7430. var a = offsets.right;
  7431. var b = offsets.bottom;
  7432. this._elem.css({right:a, bottom:b});
  7433. break;
  7434. }
  7435. }
  7436. else {
  7437. switch (this.location) {
  7438. case 'nw':
  7439. this._elem.css({left:0, top:offsets.top});
  7440. break;
  7441. case 'n':
  7442. var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
  7443. this._elem.css({left: a, top:offsets.top});
  7444. break;
  7445. case 'ne':
  7446. this._elem.css({right:0, top:offsets.top});
  7447. break;
  7448. case 'e':
  7449. var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
  7450. this._elem.css({right:offsets.right, top:b});
  7451. break;
  7452. case 'se':
  7453. this._elem.css({right:offsets.right, bottom:offsets.bottom});
  7454. break;
  7455. case 's':
  7456. var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
  7457. this._elem.css({left: a, bottom:offsets.bottom});
  7458. break;
  7459. case 'sw':
  7460. this._elem.css({left:offsets.left, bottom:offsets.bottom});
  7461. break;
  7462. case 'w':
  7463. var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
  7464. this._elem.css({left:offsets.left, top:b});
  7465. break;
  7466. default: // same as 'se'
  7467. this._elem.css({right:offsets.right, bottom:offsets.bottom});
  7468. break;
  7469. }
  7470. }
  7471. }
  7472. };
  7473. /**
  7474. * Class: $.jqplot.ThemeEngine
  7475. * Theme Engine provides a programatic way to change some of the more
  7476. * common jqplot styling options such as fonts, colors and grid options.
  7477. * A theme engine instance is created with each plot. The theme engine
  7478. * manages a collection of themes which can be modified, added to, or
  7479. * applied to the plot.
  7480. *
  7481. * The themeEngine class is not instantiated directly.
  7482. * When a plot is initialized, the current plot options are scanned
  7483. * an a default theme named "Default" is created. This theme is
  7484. * used as the basis for other themes added to the theme engine and
  7485. * is always available.
  7486. *
  7487. * A theme is a simple javascript object with styling parameters for
  7488. * various entities of the plot. A theme has the form:
  7489. *
  7490. *
  7491. * > {
  7492. * > _name:f "Default",
  7493. * > target: {
  7494. * > backgroundColor: "transparent"
  7495. * > },
  7496. * > legend: {
  7497. * > textColor: null,
  7498. * > fontFamily: null,
  7499. * > fontSize: null,
  7500. * > border: null,
  7501. * > background: null
  7502. * > },
  7503. * > title: {
  7504. * > textColor: "rgb(102, 102, 102)",
  7505. * > fontFamily: "'Trebuchet MS',Arial,Helvetica,sans-serif",
  7506. * > fontSize: "19.2px",
  7507. * > textAlign: "center"
  7508. * > },
  7509. * > seriesStyles: {},
  7510. * > series: [{
  7511. * > color: "#4bb2c5",
  7512. * > lineWidth: 2.5,
  7513. * > linePattern: "solid",
  7514. * > shadow: true,
  7515. * > fillColor: "#4bb2c5",
  7516. * > showMarker: true,
  7517. * > markerOptions: {
  7518. * > color: "#4bb2c5",
  7519. * > show: true,
  7520. * > style: 'filledCircle',
  7521. * > lineWidth: 1.5,
  7522. * > size: 4,
  7523. * > shadow: true
  7524. * > }
  7525. * > }],
  7526. * > grid: {
  7527. * > drawGridlines: true,
  7528. * > gridLineColor: "#cccccc",
  7529. * > gridLineWidth: 1,
  7530. * > backgroundColor: "#fffdf6",
  7531. * > borderColor: "#999999",
  7532. * > borderWidth: 2,
  7533. * > shadow: true
  7534. * > },
  7535. * > axesStyles: {
  7536. * > label: {},
  7537. * > ticks: {}
  7538. * > },
  7539. * > axes: {
  7540. * > xaxis: {
  7541. * > borderColor: "#999999",
  7542. * > borderWidth: 2,
  7543. * > ticks: {
  7544. * > show: true,
  7545. * > showGridline: true,
  7546. * > showLabel: true,
  7547. * > showMark: true,
  7548. * > size: 4,
  7549. * > textColor: "",
  7550. * > whiteSpace: "nowrap",
  7551. * > fontSize: "12px",
  7552. * > fontFamily: "'Trebuchet MS',Arial,Helvetica,sans-serif"
  7553. * > },
  7554. * > label: {
  7555. * > textColor: "rgb(102, 102, 102)",
  7556. * > whiteSpace: "normal",
  7557. * > fontSize: "14.6667px",
  7558. * > fontFamily: "'Trebuchet MS',Arial,Helvetica,sans-serif",
  7559. * > fontWeight: "400"
  7560. * > }
  7561. * > },
  7562. * > yaxis: {
  7563. * > borderColor: "#999999",
  7564. * > borderWidth: 2,
  7565. * > ticks: {
  7566. * > show: true,
  7567. * > showGridline: true,
  7568. * > showLabel: true,
  7569. * > showMark: true,
  7570. * > size: 4,
  7571. * > textColor: "",
  7572. * > whiteSpace: "nowrap",
  7573. * > fontSize: "12px",
  7574. * > fontFamily: "'Trebuchet MS',Arial,Helvetica,sans-serif"
  7575. * > },
  7576. * > label: {
  7577. * > textColor: null,
  7578. * > whiteSpace: null,
  7579. * > fontSize: null,
  7580. * > fontFamily: null,
  7581. * > fontWeight: null
  7582. * > }
  7583. * > },
  7584. * > x2axis: {...
  7585. * > },
  7586. * > ...
  7587. * > y9axis: {...
  7588. * > }
  7589. * > }
  7590. * > }
  7591. *
  7592. * "seriesStyles" is a style object that will be applied to all series in the plot.
  7593. * It will forcibly override any styles applied on the individual series. "axesStyles" is
  7594. * a style object that will be applied to all axes in the plot. It will also forcibly
  7595. * override any styles on the individual axes.
  7596. *
  7597. * The example shown above has series options for a line series. Options for other
  7598. * series types are shown below:
  7599. *
  7600. * Bar Series:
  7601. *
  7602. * > {
  7603. * > color: "#4bb2c5",
  7604. * > seriesColors: ["#4bb2c5", "#EAA228", "#c5b47f", "#579575", "#839557", "#958c12", "#953579", "#4b5de4", "#d8b83f", "#ff5800", "#0085cc", "#c747a3", "#cddf54", "#FBD178", "#26B4E3", "#bd70c7"],
  7605. * > lineWidth: 2.5,
  7606. * > shadow: true,
  7607. * > barPadding: 2,
  7608. * > barMargin: 10,
  7609. * > barWidth: 15.09375,
  7610. * > highlightColors: ["rgb(129,201,214)", "rgb(129,201,214)", "rgb(129,201,214)", "rgb(129,201,214)", "rgb(129,201,214)", "rgb(129,201,214)", "rgb(129,201,214)", "rgb(129,201,214)"]
  7611. * > }
  7612. *
  7613. * Pie Series:
  7614. *
  7615. * > {
  7616. * > seriesColors: ["#4bb2c5", "#EAA228", "#c5b47f", "#579575", "#839557", "#958c12", "#953579", "#4b5de4", "#d8b83f", "#ff5800", "#0085cc", "#c747a3", "#cddf54", "#FBD178", "#26B4E3", "#bd70c7"],
  7617. * > padding: 20,
  7618. * > sliceMargin: 0,
  7619. * > fill: true,
  7620. * > shadow: true,
  7621. * > startAngle: 0,
  7622. * > lineWidth: 2.5,
  7623. * > highlightColors: ["rgb(129,201,214)", "rgb(240,189,104)", "rgb(214,202,165)", "rgb(137,180,158)", "rgb(168,180,137)", "rgb(180,174,89)", "rgb(180,113,161)", "rgb(129,141,236)", "rgb(227,205,120)", "rgb(255,138,76)", "rgb(76,169,219)", "rgb(215,126,190)", "rgb(220,232,135)", "rgb(200,167,96)", "rgb(103,202,235)", "rgb(208,154,215)"]
  7624. * > }
  7625. *
  7626. * Funnel Series:
  7627. *
  7628. * > {
  7629. * > color: "#4bb2c5",
  7630. * > lineWidth: 2,
  7631. * > shadow: true,
  7632. * > padding: {
  7633. * > top: 20,
  7634. * > right: 20,
  7635. * > bottom: 20,
  7636. * > left: 20
  7637. * > },
  7638. * > sectionMargin: 6,
  7639. * > seriesColors: ["#4bb2c5", "#EAA228", "#c5b47f", "#579575", "#839557", "#958c12", "#953579", "#4b5de4", "#d8b83f", "#ff5800", "#0085cc", "#c747a3", "#cddf54", "#FBD178", "#26B4E3", "#bd70c7"],
  7640. * > highlightColors: ["rgb(147,208,220)", "rgb(242,199,126)", "rgb(220,210,178)", "rgb(154,191,172)", "rgb(180,191,154)", "rgb(191,186,112)", "rgb(191,133,174)", "rgb(147,157,238)", "rgb(231,212,139)", "rgb(255,154,102)", "rgb(102,181,224)", "rgb(221,144,199)", "rgb(225,235,152)", "rgb(200,167,96)", "rgb(124,210,238)", "rgb(215,169,221)"]
  7641. * > }
  7642. *
  7643. */
  7644. $.jqplot.ThemeEngine = function(){
  7645. // Group: Properties
  7646. //
  7647. // prop: themes
  7648. // hash of themes managed by the theme engine.
  7649. // Indexed by theme name.
  7650. this.themes = {};
  7651. // prop: activeTheme
  7652. // Pointer to currently active theme
  7653. this.activeTheme=null;
  7654. };
  7655. // called with scope of plot
  7656. $.jqplot.ThemeEngine.prototype.init = function() {
  7657. // get the Default theme from the current plot settings.
  7658. var th = new $.jqplot.Theme({_name:'Default'});
  7659. var n, i, nn;
  7660. for (n in th.target) {
  7661. if (n == "textColor") {
  7662. th.target[n] = this.target.css('color');
  7663. }
  7664. else {
  7665. th.target[n] = this.target.css(n);
  7666. }
  7667. }
  7668. if (this.title.show && this.title._elem) {
  7669. for (n in th.title) {
  7670. if (n == "textColor") {
  7671. th.title[n] = this.title._elem.css('color');
  7672. }
  7673. else {
  7674. th.title[n] = this.title._elem.css(n);
  7675. }
  7676. }
  7677. }
  7678. for (n in th.grid) {
  7679. th.grid[n] = this.grid[n];
  7680. }
  7681. if (th.grid.backgroundColor == null && this.grid.background != null) {
  7682. th.grid.backgroundColor = this.grid.background;
  7683. }
  7684. if (this.legend.show && this.legend._elem) {
  7685. for (n in th.legend) {
  7686. if (n == 'textColor') {
  7687. th.legend[n] = this.legend._elem.css('color');
  7688. }
  7689. else {
  7690. th.legend[n] = this.legend._elem.css(n);
  7691. }
  7692. }
  7693. }
  7694. var s;
  7695. for (i=0; i<this.series.length; i++) {
  7696. s = this.series[i];
  7697. if (s.renderer.constructor == $.jqplot.LineRenderer) {
  7698. th.series.push(new LineSeriesProperties());
  7699. }
  7700. else if (s.renderer.constructor == $.jqplot.BarRenderer) {
  7701. th.series.push(new BarSeriesProperties());
  7702. }
  7703. else if (s.renderer.constructor == $.jqplot.PieRenderer) {
  7704. th.series.push(new PieSeriesProperties());
  7705. }
  7706. else if (s.renderer.constructor == $.jqplot.DonutRenderer) {
  7707. th.series.push(new DonutSeriesProperties());
  7708. }
  7709. else if (s.renderer.constructor == $.jqplot.FunnelRenderer) {
  7710. th.series.push(new FunnelSeriesProperties());
  7711. }
  7712. else if (s.renderer.constructor == $.jqplot.MeterGaugeRenderer) {
  7713. th.series.push(new MeterSeriesProperties());
  7714. }
  7715. else {
  7716. th.series.push({});
  7717. }
  7718. for (n in th.series[i]) {
  7719. th.series[i][n] = s[n];
  7720. }
  7721. }
  7722. var a, ax;
  7723. for (n in this.axes) {
  7724. ax = this.axes[n];
  7725. a = th.axes[n] = new AxisProperties();
  7726. a.borderColor = ax.borderColor;
  7727. a.borderWidth = ax.borderWidth;
  7728. if (ax._ticks && ax._ticks[0]) {
  7729. for (nn in a.ticks) {
  7730. if (ax._ticks[0].hasOwnProperty(nn)) {
  7731. a.ticks[nn] = ax._ticks[0][nn];
  7732. }
  7733. else if (ax._ticks[0]._elem){
  7734. a.ticks[nn] = ax._ticks[0]._elem.css(nn);
  7735. }
  7736. }
  7737. }
  7738. if (ax._label && ax._label.show) {
  7739. for (nn in a.label) {
  7740. // a.label[nn] = ax._label._elem.css(nn);
  7741. if (ax._label[nn]) {
  7742. a.label[nn] = ax._label[nn];
  7743. }
  7744. else if (ax._label._elem){
  7745. if (nn == 'textColor') {
  7746. a.label[nn] = ax._label._elem.css('color');
  7747. }
  7748. else {
  7749. a.label[nn] = ax._label._elem.css(nn);
  7750. }
  7751. }
  7752. }
  7753. }
  7754. }
  7755. this.themeEngine._add(th);
  7756. this.themeEngine.activeTheme = this.themeEngine.themes[th._name];
  7757. };
  7758. /**
  7759. * Group: methods
  7760. *
  7761. * method: get
  7762. *
  7763. * Get and return the named theme or the active theme if no name given.
  7764. *
  7765. * parameter:
  7766. *
  7767. * name - name of theme to get.
  7768. *
  7769. * returns:
  7770. *
  7771. * Theme instance of given name.
  7772. */
  7773. $.jqplot.ThemeEngine.prototype.get = function(name) {
  7774. if (!name) {
  7775. // return the active theme
  7776. return this.activeTheme;
  7777. }
  7778. else {
  7779. return this.themes[name];
  7780. }
  7781. };
  7782. function numericalOrder(a,b) { return a-b; }
  7783. /**
  7784. * method: getThemeNames
  7785. *
  7786. * Return the list of theme names in this manager in alpha-numerical order.
  7787. *
  7788. * parameter:
  7789. *
  7790. * None
  7791. *
  7792. * returns:
  7793. *
  7794. * A the list of theme names in this manager in alpha-numerical order.
  7795. */
  7796. $.jqplot.ThemeEngine.prototype.getThemeNames = function() {
  7797. var tn = [];
  7798. for (var n in this.themes) {
  7799. tn.push(n);
  7800. }
  7801. return tn.sort(numericalOrder);
  7802. };
  7803. /**
  7804. * method: getThemes
  7805. *
  7806. * Return a list of themes in alpha-numerical order by name.
  7807. *
  7808. * parameter:
  7809. *
  7810. * None
  7811. *
  7812. * returns:
  7813. *
  7814. * A list of themes in alpha-numerical order by name.
  7815. */
  7816. $.jqplot.ThemeEngine.prototype.getThemes = function() {
  7817. var tn = [];
  7818. var themes = [];
  7819. for (var n in this.themes) {
  7820. tn.push(n);
  7821. }
  7822. tn.sort(numericalOrder);
  7823. for (var i=0; i<tn.length; i++) {
  7824. themes.push(this.themes[tn[i]]);
  7825. }
  7826. return themes;
  7827. };
  7828. $.jqplot.ThemeEngine.prototype.activate = function(plot, name) {
  7829. // sometimes need to redraw whole plot.
  7830. var redrawPlot = false;
  7831. if (!name && this.activeTheme && this.activeTheme._name) {
  7832. name = this.activeTheme._name;
  7833. }
  7834. if (!this.themes.hasOwnProperty(name)) {
  7835. throw new Error("No theme of that name");
  7836. }
  7837. else {
  7838. var th = this.themes[name];
  7839. this.activeTheme = th;
  7840. var val, checkBorderColor = false, checkBorderWidth = false;
  7841. var arr = ['xaxis', 'x2axis', 'yaxis', 'y2axis'];
  7842. for (i=0; i<arr.length; i++) {
  7843. var ax = arr[i];
  7844. if (th.axesStyles.borderColor != null) {
  7845. plot.axes[ax].borderColor = th.axesStyles.borderColor;
  7846. }
  7847. if (th.axesStyles.borderWidth != null) {
  7848. plot.axes[ax].borderWidth = th.axesStyles.borderWidth;
  7849. }
  7850. }
  7851. for (var axname in plot.axes) {
  7852. var axis = plot.axes[axname];
  7853. if (axis.show) {
  7854. var thaxis = th.axes[axname] || {};
  7855. var thaxstyle = th.axesStyles;
  7856. var thax = $.jqplot.extend(true, {}, thaxis, thaxstyle);
  7857. val = (th.axesStyles.borderColor != null) ? th.axesStyles.borderColor : thax.borderColor;
  7858. if (thax.borderColor != null) {
  7859. axis.borderColor = thax.borderColor;
  7860. redrawPlot = true;
  7861. }
  7862. val = (th.axesStyles.borderWidth != null) ? th.axesStyles.borderWidth : thax.borderWidth;
  7863. if (thax.borderWidth != null) {
  7864. axis.borderWidth = thax.borderWidth;
  7865. redrawPlot = true;
  7866. }
  7867. if (axis._ticks && axis._ticks[0]) {
  7868. for (var nn in thax.ticks) {
  7869. // val = null;
  7870. // if (th.axesStyles.ticks && th.axesStyles.ticks[nn] != null) {
  7871. // val = th.axesStyles.ticks[nn];
  7872. // }
  7873. // else if (thax.ticks[nn] != null){
  7874. // val = thax.ticks[nn]
  7875. // }
  7876. val = thax.ticks[nn];
  7877. if (val != null) {
  7878. axis.tickOptions[nn] = val;
  7879. axis._ticks = [];
  7880. redrawPlot = true;
  7881. }
  7882. }
  7883. }
  7884. if (axis._label && axis._label.show) {
  7885. for (var nn in thax.label) {
  7886. // val = null;
  7887. // if (th.axesStyles.label && th.axesStyles.label[nn] != null) {
  7888. // val = th.axesStyles.label[nn];
  7889. // }
  7890. // else if (thax.label && thax.label[nn] != null){
  7891. // val = thax.label[nn]
  7892. // }
  7893. val = thax.label[nn];
  7894. if (val != null) {
  7895. axis.labelOptions[nn] = val;
  7896. redrawPlot = true;
  7897. }
  7898. }
  7899. }
  7900. }
  7901. }
  7902. for (var n in th.grid) {
  7903. if (th.grid[n] != null) {
  7904. plot.grid[n] = th.grid[n];
  7905. }
  7906. }
  7907. if (!redrawPlot) {
  7908. plot.grid.draw();
  7909. }
  7910. if (plot.legend.show) {
  7911. for (n in th.legend) {
  7912. if (th.legend[n] != null) {
  7913. plot.legend[n] = th.legend[n];
  7914. }
  7915. }
  7916. }
  7917. if (plot.title.show) {
  7918. for (n in th.title) {
  7919. if (th.title[n] != null) {
  7920. plot.title[n] = th.title[n];
  7921. }
  7922. }
  7923. }
  7924. var i;
  7925. for (i=0; i<th.series.length; i++) {
  7926. var opts = {};
  7927. var redrawSeries = false;
  7928. for (n in th.series[i]) {
  7929. val = (th.seriesStyles[n] != null) ? th.seriesStyles[n] : th.series[i][n];
  7930. if (val != null) {
  7931. opts[n] = val;
  7932. if (n == 'color') {
  7933. plot.series[i].renderer.shapeRenderer.fillStyle = val;
  7934. plot.series[i].renderer.shapeRenderer.strokeStyle = val;
  7935. plot.series[i][n] = val;
  7936. }
  7937. else if ((n == 'lineWidth') || (n == 'linePattern')) {
  7938. plot.series[i].renderer.shapeRenderer[n] = val;
  7939. plot.series[i][n] = val;
  7940. }
  7941. else if (n == 'markerOptions') {
  7942. merge (plot.series[i].markerOptions, val);
  7943. merge (plot.series[i].markerRenderer, val);
  7944. }
  7945. else {
  7946. plot.series[i][n] = val;
  7947. }
  7948. redrawPlot = true;
  7949. }
  7950. }
  7951. }
  7952. if (redrawPlot) {
  7953. plot.target.empty();
  7954. plot.draw();
  7955. }
  7956. for (n in th.target) {
  7957. if (th.target[n] != null) {
  7958. plot.target.css(n, th.target[n]);
  7959. }
  7960. }
  7961. }
  7962. };
  7963. $.jqplot.ThemeEngine.prototype._add = function(theme, name) {
  7964. if (name) {
  7965. theme._name = name;
  7966. }
  7967. if (!theme._name) {
  7968. theme._name = Date.parse(new Date());
  7969. }
  7970. if (!this.themes.hasOwnProperty(theme._name)) {
  7971. this.themes[theme._name] = theme;
  7972. }
  7973. else {
  7974. throw new Error("jqplot.ThemeEngine Error: Theme already in use");
  7975. }
  7976. };
  7977. // method remove
  7978. // Delete the named theme, return true on success, false on failure.
  7979. /**
  7980. * method: remove
  7981. *
  7982. * Remove the given theme from the themeEngine.
  7983. *
  7984. * parameters:
  7985. *
  7986. * name - name of the theme to remove.
  7987. *
  7988. * returns:
  7989. *
  7990. * true on success, false on failure.
  7991. */
  7992. $.jqplot.ThemeEngine.prototype.remove = function(name) {
  7993. if (name == 'Default') {
  7994. return false;
  7995. }
  7996. return delete this.themes[name];
  7997. };
  7998. /**
  7999. * method: newTheme
  8000. *
  8001. * Create a new theme based on the default theme, adding it the themeEngine.
  8002. *
  8003. * parameters:
  8004. *
  8005. * name - name of the new theme.
  8006. * obj - optional object of styles to be applied to this new theme.
  8007. *
  8008. * returns:
  8009. *
  8010. * new Theme object.
  8011. */
  8012. $.jqplot.ThemeEngine.prototype.newTheme = function(name, obj) {
  8013. if (typeof(name) == 'object') {
  8014. obj = obj || name;
  8015. name = null;
  8016. }
  8017. if (obj && obj._name) {
  8018. name = obj._name;
  8019. }
  8020. else {
  8021. name = name || Date.parse(new Date());
  8022. }
  8023. // var th = new $.jqplot.Theme(name);
  8024. var th = this.copy(this.themes['Default']._name, name);
  8025. $.jqplot.extend(th, obj);
  8026. return th;
  8027. };
  8028. // function clone(obj) {
  8029. // return eval(obj.toSource());
  8030. // }
  8031. function clone(obj){
  8032. if(obj == null || typeof(obj) != 'object'){
  8033. return obj;
  8034. }
  8035. var temp = new obj.constructor();
  8036. for(var key in obj){
  8037. temp[key] = clone(obj[key]);
  8038. }
  8039. return temp;
  8040. }
  8041. $.jqplot.clone = clone;
  8042. function merge(obj1, obj2) {
  8043. if (obj2 == null || typeof(obj2) != 'object') {
  8044. return;
  8045. }
  8046. for (var key in obj2) {
  8047. if (key == 'highlightColors') {
  8048. obj1[key] = clone(obj2[key]);
  8049. }
  8050. if (obj2[key] != null && typeof(obj2[key]) == 'object') {
  8051. if (!obj1.hasOwnProperty(key)) {
  8052. obj1[key] = {};
  8053. }
  8054. merge(obj1[key], obj2[key]);
  8055. }
  8056. else {
  8057. obj1[key] = obj2[key];
  8058. }
  8059. }
  8060. }
  8061. $.jqplot.merge = merge;
  8062. // Use the jQuery 1.3.2 extend function since behaviour in jQuery 1.4 seems problematic
  8063. $.jqplot.extend = function() {
  8064. // copy reference to target object
  8065. var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options;
  8066. // Handle a deep copy situation
  8067. if ( typeof target === "boolean" ) {
  8068. deep = target;
  8069. target = arguments[1] || {};
  8070. // skip the boolean and the target
  8071. i = 2;
  8072. }
  8073. // Handle case when target is a string or something (possible in deep copy)
  8074. if ( typeof target !== "object" && !toString.call(target) === "[object Function]" ) {
  8075. target = {};
  8076. }
  8077. for ( ; i < length; i++ ){
  8078. // Only deal with non-null/undefined values
  8079. if ( (options = arguments[ i ]) != null ) {
  8080. // Extend the base object
  8081. for ( var name in options ) {
  8082. var src = target[ name ], copy = options[ name ];
  8083. // Prevent never-ending loop
  8084. if ( target === copy ) {
  8085. continue;
  8086. }
  8087. // Recurse if we're merging object values
  8088. if ( deep && copy && typeof copy === "object" && !copy.nodeType ) {
  8089. target[ name ] = $.jqplot.extend( deep,
  8090. // Never move original objects, clone them
  8091. src || ( copy.length != null ? [ ] : { } )
  8092. , copy );
  8093. }
  8094. // Don't bring in undefined values
  8095. else if ( copy !== undefined ) {
  8096. target[ name ] = copy;
  8097. }
  8098. }
  8099. }
  8100. }
  8101. // Return the modified object
  8102. return target;
  8103. };
  8104. /**
  8105. * method: rename
  8106. *
  8107. * Rename a theme.
  8108. *
  8109. * parameters:
  8110. *
  8111. * oldName - current name of the theme.
  8112. * newName - desired name of the theme.
  8113. *
  8114. * returns:
  8115. *
  8116. * new Theme object.
  8117. */
  8118. $.jqplot.ThemeEngine.prototype.rename = function (oldName, newName) {
  8119. if (oldName == 'Default' || newName == 'Default') {
  8120. throw new Error ("jqplot.ThemeEngine Error: Cannot rename from/to Default");
  8121. }
  8122. if (this.themes.hasOwnProperty(newName)) {
  8123. throw new Error ("jqplot.ThemeEngine Error: New name already in use.");
  8124. }
  8125. else if (this.themes.hasOwnProperty(oldName)) {
  8126. var th = this.copy (oldName, newName);
  8127. this.remove(oldName);
  8128. return th;
  8129. }
  8130. throw new Error("jqplot.ThemeEngine Error: Old name or new name invalid");
  8131. };
  8132. /**
  8133. * method: copy
  8134. *
  8135. * Create a copy of an existing theme in the themeEngine, adding it the themeEngine.
  8136. *
  8137. * parameters:
  8138. *
  8139. * sourceName - name of the existing theme.
  8140. * targetName - name of the copy.
  8141. * obj - optional object of style parameter to apply to the new theme.
  8142. *
  8143. * returns:
  8144. *
  8145. * new Theme object.
  8146. */
  8147. $.jqplot.ThemeEngine.prototype.copy = function (sourceName, targetName, obj) {
  8148. if (targetName == 'Default') {
  8149. throw new Error ("jqplot.ThemeEngine Error: Cannot copy over Default theme");
  8150. }
  8151. if (!this.themes.hasOwnProperty(sourceName)) {
  8152. var s = "jqplot.ThemeEngine Error: Source name invalid";
  8153. throw new Error(s);
  8154. }
  8155. if (this.themes.hasOwnProperty(targetName)) {
  8156. var s = "jqplot.ThemeEngine Error: Target name invalid";
  8157. throw new Error(s);
  8158. }
  8159. else {
  8160. var th = clone(this.themes[sourceName]);
  8161. th._name = targetName;
  8162. $.jqplot.extend(true, th, obj);
  8163. this._add(th);
  8164. return th;
  8165. }
  8166. };
  8167. $.jqplot.Theme = function(name, obj) {
  8168. if (typeof(name) == 'object') {
  8169. obj = obj || name;
  8170. name = null;
  8171. }
  8172. name = name || Date.parse(new Date());
  8173. this._name = name;
  8174. this.target = {
  8175. backgroundColor: null
  8176. };
  8177. this.legend = {
  8178. textColor: null,
  8179. fontFamily: null,
  8180. fontSize: null,
  8181. border: null,
  8182. background: null
  8183. };
  8184. this.title = {
  8185. textColor: null,
  8186. fontFamily: null,
  8187. fontSize: null,
  8188. textAlign: null
  8189. };
  8190. this.seriesStyles = {};
  8191. this.series = [];
  8192. this.grid = {
  8193. drawGridlines: null,
  8194. gridLineColor: null,
  8195. gridLineWidth: null,
  8196. backgroundColor: null,
  8197. borderColor: null,
  8198. borderWidth: null,
  8199. shadow: null
  8200. };
  8201. this.axesStyles = {label:{}, ticks:{}};
  8202. this.axes = {};
  8203. if (typeof(obj) == 'string') {
  8204. this._name = obj;
  8205. }
  8206. else if(typeof(obj) == 'object') {
  8207. $.jqplot.extend(true, this, obj);
  8208. }
  8209. };
  8210. var AxisProperties = function() {
  8211. this.borderColor = null;
  8212. this.borderWidth = null;
  8213. this.ticks = new AxisTicks();
  8214. this.label = new AxisLabel();
  8215. };
  8216. var AxisTicks = function() {
  8217. this.show = null;
  8218. this.showGridline = null;
  8219. this.showLabel = null;
  8220. this.showMark = null;
  8221. this.size = null;
  8222. this.textColor = null;
  8223. this.whiteSpace = null;
  8224. this.fontSize = null;
  8225. this.fontFamily = null;
  8226. };
  8227. var AxisLabel = function() {
  8228. this.textColor = null;
  8229. this.whiteSpace = null;
  8230. this.fontSize = null;
  8231. this.fontFamily = null;
  8232. this.fontWeight = null;
  8233. };
  8234. var LineSeriesProperties = function() {
  8235. this.color=null;
  8236. this.lineWidth=null;
  8237. this.linePattern=null;
  8238. this.shadow=null;
  8239. this.fillColor=null;
  8240. this.showMarker=null;
  8241. this.markerOptions = new MarkerOptions();
  8242. };
  8243. var MarkerOptions = function() {
  8244. this.show = null;
  8245. this.style = null;
  8246. this.lineWidth = null;
  8247. this.size = null;
  8248. this.color = null;
  8249. this.shadow = null;
  8250. };
  8251. var BarSeriesProperties = function() {
  8252. this.color=null;
  8253. this.seriesColors=null;
  8254. this.lineWidth=null;
  8255. this.shadow=null;
  8256. this.barPadding=null;
  8257. this.barMargin=null;
  8258. this.barWidth=null;
  8259. this.highlightColors=null;
  8260. };
  8261. var PieSeriesProperties = function() {
  8262. this.seriesColors=null;
  8263. this.padding=null;
  8264. this.sliceMargin=null;
  8265. this.fill=null;
  8266. this.shadow=null;
  8267. this.startAngle=null;
  8268. this.lineWidth=null;
  8269. this.highlightColors=null;
  8270. };
  8271. var DonutSeriesProperties = function() {
  8272. this.seriesColors=null;
  8273. this.padding=null;
  8274. this.sliceMargin=null;
  8275. this.fill=null;
  8276. this.shadow=null;
  8277. this.startAngle=null;
  8278. this.lineWidth=null;
  8279. this.innerDiameter=null;
  8280. this.thickness=null;
  8281. this.ringMargin=null;
  8282. this.highlightColors=null;
  8283. };
  8284. var FunnelSeriesProperties = function() {
  8285. this.color=null;
  8286. this.lineWidth=null;
  8287. this.shadow=null;
  8288. this.padding=null;
  8289. this.sectionMargin=null;
  8290. this.seriesColors=null;
  8291. this.highlightColors=null;
  8292. };
  8293. var MeterSeriesProperties = function() {
  8294. this.padding=null;
  8295. this.backgroundColor=null;
  8296. this.ringColor=null;
  8297. this.tickColor=null;
  8298. this.ringWidth=null;
  8299. this.intervalColors=null;
  8300. this.intervalInnerRadius=null;
  8301. this.intervalOuterRadius=null;
  8302. this.hubRadius=null;
  8303. this.needleThickness=null;
  8304. this.needlePad=null;
  8305. };
  8306. $.fn.jqplotChildText = function() {
  8307. return $(this).contents().filter(function() {
  8308. return this.nodeType == 3; // Node.TEXT_NODE not defined in I7
  8309. }).text();
  8310. };
  8311. // Returns font style as abbreviation for "font" property.
  8312. $.fn.jqplotGetComputedFontStyle = function() {
  8313. var css = window.getComputedStyle ? window.getComputedStyle(this[0], "") : this[0].currentStyle;
  8314. var attrs = css['font-style'] ? ['font-style', 'font-weight', 'font-size', 'font-family'] : ['fontStyle', 'fontWeight', 'fontSize', 'fontFamily'];
  8315. var style = [];
  8316. for (var i=0 ; i < attrs.length; ++i) {
  8317. var attr = String(css[attrs[i]]);
  8318. if (attr && attr != 'normal') {
  8319. style.push(attr);
  8320. }
  8321. }
  8322. return style.join(' ');
  8323. };
  8324. /**
  8325. * Namespace: $.fn
  8326. * jQuery namespace to attach functions to jQuery elements.
  8327. *
  8328. */
  8329. $.fn.jqplotToImageCanvas = function(options) {
  8330. options = options || {};
  8331. var x_offset = (options.x_offset == null) ? 0 : options.x_offset;
  8332. var y_offset = (options.y_offset == null) ? 0 : options.y_offset;
  8333. var backgroundColor = (options.backgroundColor == null) ? 'rgb(255,255,255)' : options.backgroundColor;
  8334. if ($(this).width() == 0 || $(this).height() == 0) {
  8335. return null;
  8336. }
  8337. // excanvas and hence IE < 9 do not support toDataURL and cannot export images.
  8338. if ($.jqplot.use_excanvas) {
  8339. return null;
  8340. }
  8341. var newCanvas = document.createElement("canvas");
  8342. var h = $(this).outerHeight(true);
  8343. var w = $(this).outerWidth(true);
  8344. var offs = $(this).offset();
  8345. var plotleft = offs.left;
  8346. var plottop = offs.top;
  8347. var transx = 0, transy = 0;
  8348. // have to check if any elements are hanging outside of plot area before rendering,
  8349. // since changing width of canvas will erase canvas.
  8350. var clses = ['jqplot-table-legend', 'jqplot-xaxis-tick', 'jqplot-x2axis-tick', 'jqplot-yaxis-tick', 'jqplot-y2axis-tick', 'jqplot-y3axis-tick',
  8351. 'jqplot-y4axis-tick', 'jqplot-y5axis-tick', 'jqplot-y6axis-tick', 'jqplot-y7axis-tick', 'jqplot-y8axis-tick', 'jqplot-y9axis-tick',
  8352. 'jqplot-xaxis-label', 'jqplot-x2axis-label', 'jqplot-yaxis-label', 'jqplot-y2axis-label', 'jqplot-y3axis-label', 'jqplot-y4axis-label',
  8353. 'jqplot-y5axis-label', 'jqplot-y6axis-label', 'jqplot-y7axis-label', 'jqplot-y8axis-label', 'jqplot-y9axis-label' ];
  8354. var temptop, templeft, tempbottom, tempright;
  8355. for (var i = 0; i < clses.length; i++) {
  8356. $(this).find('.'+clses[i]).each(function() {
  8357. temptop = $(this).offset().top - plottop;
  8358. templeft = $(this).offset().left - plotleft;
  8359. tempright = templeft + $(this).outerWidth(true) + transx;
  8360. tempbottom = temptop + $(this).outerHeight(true) + transy;
  8361. if (templeft < -transx) {
  8362. w = w - transx - templeft;
  8363. transx = -templeft;
  8364. }
  8365. if (temptop < -transy) {
  8366. h = h - transy - temptop;
  8367. transy = - temptop;
  8368. }
  8369. if (tempright > w) {
  8370. w = tempright;
  8371. }
  8372. if (tempbottom > h) {
  8373. h = tempbottom;
  8374. }
  8375. });
  8376. }
  8377. newCanvas.width = w + Number(x_offset);
  8378. newCanvas.height = h + Number(y_offset);
  8379. var newContext = newCanvas.getContext("2d");
  8380. newContext.save();
  8381. newContext.fillStyle = backgroundColor;
  8382. newContext.fillRect(0,0, newCanvas.width, newCanvas.height);
  8383. newContext.restore();
  8384. newContext.translate(transx, transy);
  8385. newContext.textAlign = 'left';
  8386. newContext.textBaseline = 'top';
  8387. function getLineheight(el) {
  8388. var lineheight = parseInt($(el).css('line-height'), 10);
  8389. if (isNaN(lineheight)) {
  8390. lineheight = parseInt($(el).css('font-size'), 10) * 1.2;
  8391. }
  8392. return lineheight;
  8393. }
  8394. function writeWrappedText (el, context, text, left, top, canvasWidth) {
  8395. var lineheight = getLineheight(el);
  8396. var tagwidth = $(el).innerWidth();
  8397. var tagheight = $(el).innerHeight();
  8398. var words = text.split(/\s+/);
  8399. var wl = words.length;
  8400. var w = '';
  8401. var breaks = [];
  8402. var temptop = top;
  8403. var templeft = left;
  8404. for (var i=0; i<wl; i++) {
  8405. w += words[i];
  8406. if (context.measureText(w).width > tagwidth && w.length > words[i].length) {
  8407. breaks.push(i);
  8408. w = '';
  8409. i--;
  8410. }
  8411. }
  8412. if (breaks.length === 0) {
  8413. // center text if necessary
  8414. if ($(el).css('textAlign') === 'center') {
  8415. templeft = left + (canvasWidth - context.measureText(w).width)/2 - transx;
  8416. }
  8417. context.fillText(text, templeft, top);
  8418. }
  8419. else {
  8420. w = words.slice(0, breaks[0]).join(' ');
  8421. // center text if necessary
  8422. if ($(el).css('textAlign') === 'center') {
  8423. templeft = left + (canvasWidth - context.measureText(w).width)/2 - transx;
  8424. }
  8425. context.fillText(w, templeft, temptop);
  8426. temptop += lineheight;
  8427. for (var i=1, l=breaks.length; i<l; i++) {
  8428. w = words.slice(breaks[i-1], breaks[i]).join(' ');
  8429. // center text if necessary
  8430. if ($(el).css('textAlign') === 'center') {
  8431. templeft = left + (canvasWidth - context.measureText(w).width)/2 - transx;
  8432. }
  8433. context.fillText(w, templeft, temptop);
  8434. temptop += lineheight;
  8435. }
  8436. w = words.slice(breaks[i-1], words.length).join(' ');
  8437. // center text if necessary
  8438. if ($(el).css('textAlign') === 'center') {
  8439. templeft = left + (canvasWidth - context.measureText(w).width)/2 - transx;
  8440. }
  8441. context.fillText(w, templeft, temptop);
  8442. }
  8443. }
  8444. function _jqpToImage(el, x_offset, y_offset) {
  8445. var tagname = el.tagName.toLowerCase();
  8446. var p = $(el).position();
  8447. var css = window.getComputedStyle ? window.getComputedStyle(el, "") : el.currentStyle; // for IE < 9
  8448. var left = x_offset + p.left + parseInt(css.marginLeft, 10) + parseInt(css.borderLeftWidth, 10) + parseInt(css.paddingLeft, 10);
  8449. var top = y_offset + p.top + parseInt(css.marginTop, 10) + parseInt(css.borderTopWidth, 10)+ parseInt(css.paddingTop, 10);
  8450. var w = newCanvas.width;
  8451. // var left = x_offset + p.left + $(el).css('marginLeft') + $(el).css('borderLeftWidth')
  8452. // somehow in here, for divs within divs, the width of the inner div should be used instead of the canvas.
  8453. if ((tagname == 'div' || tagname == 'span') && !$(el).hasClass('jqplot-highlighter-tooltip')) {
  8454. $(el).children().each(function() {
  8455. _jqpToImage(this, left, top);
  8456. });
  8457. var text = $(el).jqplotChildText();
  8458. if (text) {
  8459. newContext.font = $(el).jqplotGetComputedFontStyle();
  8460. newContext.fillStyle = $(el).css('color');
  8461. writeWrappedText(el, newContext, text, left, top, w);
  8462. }
  8463. }
  8464. // handle the standard table legend
  8465. else if (tagname === 'table' && $(el).hasClass('jqplot-table-legend')) {
  8466. newContext.strokeStyle = $(el).css('border-top-color');
  8467. newContext.fillStyle = $(el).css('background-color');
  8468. newContext.fillRect(left, top, $(el).innerWidth(), $(el).innerHeight());
  8469. if (parseInt($(el).css('border-top-width'), 10) > 0) {
  8470. newContext.strokeRect(left, top, $(el).innerWidth(), $(el).innerHeight());
  8471. }
  8472. // find all the swatches
  8473. $(el).find('div.jqplot-table-legend-swatch-outline').each(function() {
  8474. // get the first div and stroke it
  8475. var elem = $(this);
  8476. newContext.strokeStyle = elem.css('border-top-color');
  8477. var l = left + elem.position().left;
  8478. var t = top + elem.position().top;
  8479. newContext.strokeRect(l, t, elem.innerWidth(), elem.innerHeight());
  8480. // now fill the swatch
  8481. l += parseInt(elem.css('padding-left'), 10);
  8482. t += parseInt(elem.css('padding-top'), 10);
  8483. var h = elem.innerHeight() - 2 * parseInt(elem.css('padding-top'), 10);
  8484. var w = elem.innerWidth() - 2 * parseInt(elem.css('padding-left'), 10);
  8485. var swatch = elem.children('div.jqplot-table-legend-swatch');
  8486. newContext.fillStyle = swatch.css('background-color');
  8487. newContext.fillRect(l, t, w, h);
  8488. });
  8489. // now add text
  8490. $(el).find('td.jqplot-table-legend-label').each(function(){
  8491. var elem = $(this);
  8492. var l = left + elem.position().left;
  8493. var t = top + elem.position().top + parseInt(elem.css('padding-top'), 10);
  8494. newContext.font = elem.jqplotGetComputedFontStyle();
  8495. newContext.fillStyle = elem.css('color');
  8496. writeWrappedText(elem, newContext, elem.text(), l, t, w);
  8497. });
  8498. var elem = null;
  8499. }
  8500. else if (tagname == 'canvas') {
  8501. newContext.drawImage(el, left, top);
  8502. }
  8503. }
  8504. $(this).children().each(function() {
  8505. _jqpToImage(this, x_offset, y_offset);
  8506. });
  8507. return newCanvas;
  8508. };
  8509. // return the raw image data string.
  8510. // Should work on canvas supporting browsers.
  8511. $.fn.jqplotToImageStr = function(options) {
  8512. var imgCanvas = $(this).jqplotToImageCanvas(options);
  8513. if (imgCanvas) {
  8514. return imgCanvas.toDataURL("image/png");
  8515. }
  8516. else {
  8517. return null;
  8518. }
  8519. };
  8520. // return a DOM <img> element and return it.
  8521. // Should work on canvas supporting browsers.
  8522. $.fn.jqplotToImageElem = function(options) {
  8523. var elem = document.createElement("img");
  8524. var str = $(this).jqplotToImageStr(options);
  8525. elem.src = str;
  8526. return elem;
  8527. };
  8528. // return a string for an <img> element and return it.
  8529. // Should work on canvas supporting browsers.
  8530. $.fn.jqplotToImageElemStr = function(options) {
  8531. var str = '<img src='+$(this).jqplotToImageStr(options)+' />';
  8532. return str;
  8533. };
  8534. // Not guaranteed to work, even on canvas supporting browsers due to
  8535. // limitations with location.href and browser support.
  8536. $.fn.jqplotSaveImage = function() {
  8537. var imgData = $(this).jqplotToImageStr({});
  8538. if (imgData) {
  8539. window.location.href = imgData.replace("image/png", "image/octet-stream");
  8540. }
  8541. };
  8542. // Not guaranteed to work, even on canvas supporting browsers due to
  8543. // limitations with window.open and arbitrary data.
  8544. $.fn.jqplotViewImage = function() {
  8545. var imgStr = $(this).jqplotToImageElemStr({});
  8546. var imgData = $(this).jqplotToImageStr({});
  8547. if (imgStr) {
  8548. var w = window.open('');
  8549. w.document.open("image/png");
  8550. w.document.write(imgStr);
  8551. w.document.close();
  8552. w = null;
  8553. }
  8554. };
  8555. /**
  8556. * @description
  8557. * <p>Object with extended date parsing and formatting capabilities.
  8558. * This library borrows many concepts and ideas from the Date Instance
  8559. * Methods by Ken Snyder along with some parts of Ken's actual code.</p>
  8560. *
  8561. * <p>jsDate takes a different approach by not extending the built-in
  8562. * Date Object, improving date parsing, allowing for multiple formatting
  8563. * syntaxes and multiple and more easily expandable localization.</p>
  8564. *
  8565. * @author Chris Leonello
  8566. * @date #date#
  8567. * @version #VERSION#
  8568. * @copyright (c) 2010-2015 Chris Leonello
  8569. * jsDate is currently available for use in all personal or commercial projects
  8570. * under both the MIT and GPL version 2.0 licenses. This means that you can
  8571. * choose the license that best suits your project and use it accordingly.
  8572. *
  8573. * <p>Ken's original Date Instance Methods and copyright notice:</p>
  8574. * <pre>
  8575. * Ken Snyder (ken d snyder at gmail dot com)
  8576. * 2008-09-10
  8577. * version 2.0.2 (http://kendsnyder.com/sandbox/date/)
  8578. * Creative Commons Attribution License 3.0 (http://creativecommons.org/licenses/by/3.0/)
  8579. * </pre>
  8580. *
  8581. * @class
  8582. * @name jsDate
  8583. * @param {String | Number | Array | Date&nbsp;Object | Options&nbsp;Object} arguments Optional arguments, either a parsable date/time string,
  8584. * a JavaScript timestamp, an array of numbers of form [year, month, day, hours, minutes, seconds, milliseconds],
  8585. * a Date object, or an options object of form {syntax: "perl", date:some Date} where all options are optional.
  8586. */
  8587. var jsDate = function () {
  8588. this.syntax = jsDate.config.syntax;
  8589. this._type = "jsDate";
  8590. this.proxy = new Date();
  8591. this.options = {};
  8592. this.locale = jsDate.regional.getLocale();
  8593. this.formatString = '';
  8594. this.defaultCentury = jsDate.config.defaultCentury;
  8595. switch ( arguments.length ) {
  8596. case 0:
  8597. break;
  8598. case 1:
  8599. // other objects either won't have a _type property or,
  8600. // if they do, it shouldn't be set to "jsDate", so
  8601. // assume it is an options argument.
  8602. if (get_type(arguments[0]) == "[object Object]" && arguments[0]._type != "jsDate") {
  8603. var opts = this.options = arguments[0];
  8604. this.syntax = opts.syntax || this.syntax;
  8605. this.defaultCentury = opts.defaultCentury || this.defaultCentury;
  8606. this.proxy = jsDate.createDate(opts.date);
  8607. }
  8608. else {
  8609. this.proxy = jsDate.createDate(arguments[0]);
  8610. }
  8611. break;
  8612. default:
  8613. var a = [];
  8614. for ( var i=0; i<arguments.length; i++ ) {
  8615. a.push(arguments[i]);
  8616. }
  8617. // this should be the current date/time?
  8618. this.proxy = new Date();
  8619. this.proxy.setFullYear.apply( this.proxy, a.slice(0,3) );
  8620. if ( a.slice(3).length ) {
  8621. this.proxy.setHours.apply( this.proxy, a.slice(3) );
  8622. }
  8623. break;
  8624. }
  8625. };
  8626. /**
  8627. * @namespace Configuration options that will be used as defaults for all instances on the page.
  8628. * @property {String} defaultLocale The default locale to use [en].
  8629. * @property {String} syntax The default syntax to use [perl].
  8630. * @property {Number} defaultCentury The default centry for 2 digit dates.
  8631. */
  8632. jsDate.config = {
  8633. defaultLocale: 'en',
  8634. syntax: 'perl',
  8635. defaultCentury: 1900
  8636. };
  8637. /**
  8638. * Add an arbitrary amount to the currently stored date
  8639. *
  8640. * @param {Number} number
  8641. * @param {String} unit
  8642. * @returns {jsDate}
  8643. */
  8644. jsDate.prototype.add = function(number, unit) {
  8645. var factor = multipliers[unit] || multipliers.day;
  8646. if (typeof factor == 'number') {
  8647. this.proxy.setTime(this.proxy.getTime() + (factor * number));
  8648. } else {
  8649. factor.add(this, number);
  8650. }
  8651. return this;
  8652. };
  8653. /**
  8654. * Create a new jqplot.date object with the same date
  8655. *
  8656. * @returns {jsDate}
  8657. */
  8658. jsDate.prototype.clone = function() {
  8659. return new jsDate(this.proxy.getTime());
  8660. };
  8661. /**
  8662. * Get the UTC TimeZone Offset of this date in milliseconds.
  8663. *
  8664. * @returns {Number}
  8665. */
  8666. jsDate.prototype.getUtcOffset = function() {
  8667. return this.proxy.getTimezoneOffset() * 60000;
  8668. };
  8669. /**
  8670. * Find the difference between this jsDate and another date.
  8671. *
  8672. * @param {String| Number| Array| jsDate&nbsp;Object| Date&nbsp;Object} dateObj
  8673. * @param {String} unit
  8674. * @param {Boolean} allowDecimal
  8675. * @returns {Number} Number of units difference between dates.
  8676. */
  8677. jsDate.prototype.diff = function(dateObj, unit, allowDecimal) {
  8678. // ensure we have a Date object
  8679. dateObj = new jsDate(dateObj);
  8680. if (dateObj === null) {
  8681. return null;
  8682. }
  8683. // get the multiplying factor integer or factor function
  8684. var factor = multipliers[unit] || multipliers.day;
  8685. if (typeof factor == 'number') {
  8686. // multiply
  8687. var unitDiff = (this.proxy.getTime() - dateObj.proxy.getTime()) / factor;
  8688. } else {
  8689. // run function
  8690. var unitDiff = factor.diff(this.proxy, dateObj.proxy);
  8691. }
  8692. // if decimals are not allowed, round toward zero
  8693. return (allowDecimal ? unitDiff : Math[unitDiff > 0 ? 'floor' : 'ceil'](unitDiff));
  8694. };
  8695. /**
  8696. * Get the abbreviated name of the current week day
  8697. *
  8698. * @returns {String}
  8699. */
  8700. jsDate.prototype.getAbbrDayName = function() {
  8701. return jsDate.regional[this.locale]["dayNamesShort"][this.proxy.getDay()];
  8702. };
  8703. /**
  8704. * Get the abbreviated name of the current month
  8705. *
  8706. * @returns {String}
  8707. */
  8708. jsDate.prototype.getAbbrMonthName = function() {
  8709. return jsDate.regional[this.locale]["monthNamesShort"][this.proxy.getMonth()];
  8710. };
  8711. /**
  8712. * Get UPPER CASE AM or PM for the current time
  8713. *
  8714. * @returns {String}
  8715. */
  8716. jsDate.prototype.getAMPM = function() {
  8717. return this.proxy.getHours() >= 12 ? 'PM' : 'AM';
  8718. };
  8719. /**
  8720. * Get lower case am or pm for the current time
  8721. *
  8722. * @returns {String}
  8723. */
  8724. jsDate.prototype.getAmPm = function() {
  8725. return this.proxy.getHours() >= 12 ? 'pm' : 'am';
  8726. };
  8727. /**
  8728. * Get the century (19 for 20th Century)
  8729. *
  8730. * @returns {Integer} Century (19 for 20th century).
  8731. */
  8732. jsDate.prototype.getCentury = function() {
  8733. return parseInt(this.proxy.getFullYear()/100, 10);
  8734. };
  8735. /**
  8736. * Implements Date functionality
  8737. */
  8738. jsDate.prototype.getDate = function() {
  8739. return this.proxy.getDate();
  8740. };
  8741. /**
  8742. * Implements Date functionality
  8743. */
  8744. jsDate.prototype.getDay = function() {
  8745. return this.proxy.getDay();
  8746. };
  8747. /**
  8748. * Get the Day of week 1 (Monday) thru 7 (Sunday)
  8749. *
  8750. * @returns {Integer} Day of week 1 (Monday) thru 7 (Sunday)
  8751. */
  8752. jsDate.prototype.getDayOfWeek = function() {
  8753. var dow = this.proxy.getDay();
  8754. return dow===0?7:dow;
  8755. };
  8756. /**
  8757. * Get the day of the year
  8758. *
  8759. * @returns {Integer} 1 - 366, day of the year
  8760. */
  8761. jsDate.prototype.getDayOfYear = function() {
  8762. var d = this.proxy;
  8763. var ms = d - new Date('' + d.getFullYear() + '/1/1 GMT');
  8764. ms += d.getTimezoneOffset()*60000;
  8765. d = null;
  8766. return parseInt(ms/60000/60/24, 10)+1;
  8767. };
  8768. /**
  8769. * Get the name of the current week day
  8770. *
  8771. * @returns {String}
  8772. */
  8773. jsDate.prototype.getDayName = function() {
  8774. return jsDate.regional[this.locale]["dayNames"][this.proxy.getDay()];
  8775. };
  8776. /**
  8777. * Get the week number of the given year, starting with the first Sunday as the first week
  8778. * @returns {Integer} Week number (13 for the 13th full week of the year).
  8779. */
  8780. jsDate.prototype.getFullWeekOfYear = function() {
  8781. var d = this.proxy;
  8782. var doy = this.getDayOfYear();
  8783. var rdow = 6-d.getDay();
  8784. var woy = parseInt((doy+rdow)/7, 10);
  8785. return woy;
  8786. };
  8787. /**
  8788. * Implements Date functionality
  8789. */
  8790. jsDate.prototype.getFullYear = function() {
  8791. return this.proxy.getFullYear();
  8792. };
  8793. /**
  8794. * Get the GMT offset in hours and minutes (e.g. +06:30)
  8795. *
  8796. * @returns {String}
  8797. */
  8798. jsDate.prototype.getGmtOffset = function() {
  8799. // divide the minutes offset by 60
  8800. var hours = this.proxy.getTimezoneOffset() / 60;
  8801. // decide if we are ahead of or behind GMT
  8802. var prefix = hours < 0 ? '+' : '-';
  8803. // remove the negative sign if any
  8804. hours = Math.abs(hours);
  8805. // add the +/- to the padded number of hours to : to the padded minutes
  8806. return prefix + addZeros(Math.floor(hours), 2) + ':' + addZeros((hours % 1) * 60, 2);
  8807. };
  8808. /**
  8809. * Implements Date functionality
  8810. */
  8811. jsDate.prototype.getHours = function() {
  8812. return this.proxy.getHours();
  8813. };
  8814. /**
  8815. * Get the current hour on a 12-hour scheme
  8816. *
  8817. * @returns {Integer}
  8818. */
  8819. jsDate.prototype.getHours12 = function() {
  8820. var hours = this.proxy.getHours();
  8821. return hours > 12 ? hours - 12 : (hours == 0 ? 12 : hours);
  8822. };
  8823. jsDate.prototype.getIsoWeek = function() {
  8824. var d = this.proxy;
  8825. var woy = this.getWeekOfYear();
  8826. var dow1_1 = (new Date('' + d.getFullYear() + '/1/1')).getDay();
  8827. // First week is 01 and not 00 as in the case of %U and %W,
  8828. // so we add 1 to the final result except if day 1 of the year
  8829. // is a Monday (then %W returns 01).
  8830. // We also need to subtract 1 if the day 1 of the year is
  8831. // Friday-Sunday, so the resulting equation becomes:
  8832. var idow = woy + (dow1_1 > 4 || dow1_1 <= 1 ? 0 : 1);
  8833. if(idow == 53 && (new Date('' + d.getFullYear() + '/12/31')).getDay() < 4)
  8834. {
  8835. idow = 1;
  8836. }
  8837. else if(idow === 0)
  8838. {
  8839. d = new jsDate(new Date('' + (d.getFullYear()-1) + '/12/31'));
  8840. idow = d.getIsoWeek();
  8841. }
  8842. d = null;
  8843. return idow;
  8844. };
  8845. /**
  8846. * Implements Date functionality
  8847. */
  8848. jsDate.prototype.getMilliseconds = function() {
  8849. return this.proxy.getMilliseconds();
  8850. };
  8851. /**
  8852. * Implements Date functionality
  8853. */
  8854. jsDate.prototype.getMinutes = function() {
  8855. return this.proxy.getMinutes();
  8856. };
  8857. /**
  8858. * Implements Date functionality
  8859. */
  8860. jsDate.prototype.getMonth = function() {
  8861. return this.proxy.getMonth();
  8862. };
  8863. /**
  8864. * Get the name of the current month
  8865. *
  8866. * @returns {String}
  8867. */
  8868. jsDate.prototype.getMonthName = function() {
  8869. return jsDate.regional[this.locale]["monthNames"][this.proxy.getMonth()];
  8870. };
  8871. /**
  8872. * Get the number of the current month, 1-12
  8873. *
  8874. * @returns {Integer}
  8875. */
  8876. jsDate.prototype.getMonthNumber = function() {
  8877. return this.proxy.getMonth() + 1;
  8878. };
  8879. /**
  8880. * Implements Date functionality
  8881. */
  8882. jsDate.prototype.getSeconds = function() {
  8883. return this.proxy.getSeconds();
  8884. };
  8885. /**
  8886. * Return a proper two-digit year integer
  8887. *
  8888. * @returns {Integer}
  8889. */
  8890. jsDate.prototype.getShortYear = function() {
  8891. return this.proxy.getYear() % 100;
  8892. };
  8893. /**
  8894. * Implements Date functionality
  8895. */
  8896. jsDate.prototype.getTime = function() {
  8897. return this.proxy.getTime();
  8898. };
  8899. /**
  8900. * Get the timezone abbreviation
  8901. *
  8902. * @returns {String} Abbreviation for the timezone
  8903. */
  8904. jsDate.prototype.getTimezoneAbbr = function() {
  8905. return this.proxy.toString().replace(/^.*\(([^)]+)\)$/, '$1');
  8906. };
  8907. /**
  8908. * Get the browser-reported name for the current timezone (e.g. MDT, Mountain Daylight Time)
  8909. *
  8910. * @returns {String}
  8911. */
  8912. jsDate.prototype.getTimezoneName = function() {
  8913. var match = /(?:\((.+)\)$| ([A-Z]{3}) )/.exec(this.toString());
  8914. return match[1] || match[2] || 'GMT' + this.getGmtOffset();
  8915. };
  8916. /**
  8917. * Implements Date functionality
  8918. */
  8919. jsDate.prototype.getTimezoneOffset = function() {
  8920. return this.proxy.getTimezoneOffset();
  8921. };
  8922. /**
  8923. * Get the week number of the given year, starting with the first Monday as the first week
  8924. * @returns {Integer} Week number (13 for the 13th week of the year).
  8925. */
  8926. jsDate.prototype.getWeekOfYear = function() {
  8927. var doy = this.getDayOfYear();
  8928. var rdow = 7 - this.getDayOfWeek();
  8929. var woy = parseInt((doy+rdow)/7, 10);
  8930. return woy;
  8931. };
  8932. /**
  8933. * Get the current date as a Unix timestamp
  8934. *
  8935. * @returns {Integer}
  8936. */
  8937. jsDate.prototype.getUnix = function() {
  8938. return Math.round(this.proxy.getTime() / 1000, 0);
  8939. };
  8940. /**
  8941. * Implements Date functionality
  8942. */
  8943. jsDate.prototype.getYear = function() {
  8944. return this.proxy.getYear();
  8945. };
  8946. /**
  8947. * Return a date one day ahead (or any other unit)
  8948. *
  8949. * @param {String} unit Optional, year | month | day | week | hour | minute | second | millisecond
  8950. * @returns {jsDate}
  8951. */
  8952. jsDate.prototype.next = function(unit) {
  8953. unit = unit || 'day';
  8954. return this.clone().add(1, unit);
  8955. };
  8956. /**
  8957. * Set the jsDate instance to a new date.
  8958. *
  8959. * @param {String | Number | Array | Date Object | jsDate Object | Options Object} arguments Optional arguments,
  8960. * either a parsable date/time string,
  8961. * a JavaScript timestamp, an array of numbers of form [year, month, day, hours, minutes, seconds, milliseconds],
  8962. * a Date object, jsDate Object or an options object of form {syntax: "perl", date:some Date} where all options are optional.
  8963. */
  8964. jsDate.prototype.set = function() {
  8965. switch ( arguments.length ) {
  8966. case 0:
  8967. this.proxy = new Date();
  8968. break;
  8969. case 1:
  8970. // other objects either won't have a _type property or,
  8971. // if they do, it shouldn't be set to "jsDate", so
  8972. // assume it is an options argument.
  8973. if (get_type(arguments[0]) == "[object Object]" && arguments[0]._type != "jsDate") {
  8974. var opts = this.options = arguments[0];
  8975. this.syntax = opts.syntax || this.syntax;
  8976. this.defaultCentury = opts.defaultCentury || this.defaultCentury;
  8977. this.proxy = jsDate.createDate(opts.date);
  8978. }
  8979. else {
  8980. this.proxy = jsDate.createDate(arguments[0]);
  8981. }
  8982. break;
  8983. default:
  8984. var a = [];
  8985. for ( var i=0; i<arguments.length; i++ ) {
  8986. a.push(arguments[i]);
  8987. }
  8988. // this should be the current date/time
  8989. this.proxy = new Date();
  8990. this.proxy.setFullYear.apply( this.proxy, a.slice(0,3) );
  8991. if ( a.slice(3).length ) {
  8992. this.proxy.setHours.apply( this.proxy, a.slice(3) );
  8993. }
  8994. break;
  8995. }
  8996. return this;
  8997. };
  8998. /**
  8999. * Sets the day of the month for a specified date according to local time.
  9000. * @param {Integer} dayValue An integer from 1 to 31, representing the day of the month.
  9001. */
  9002. jsDate.prototype.setDate = function(n) {
  9003. this.proxy.setDate(n);
  9004. return this;
  9005. };
  9006. /**
  9007. * Sets the full year for a specified date according to local time.
  9008. * @param {Integer} yearValue The numeric value of the year, for example, 1995.
  9009. * @param {Integer} monthValue Optional, between 0 and 11 representing the months January through December.
  9010. * @param {Integer} dayValue Optional, between 1 and 31 representing the day of the month. If you specify the dayValue parameter, you must also specify the monthValue.
  9011. */
  9012. jsDate.prototype.setFullYear = function() {
  9013. this.proxy.setFullYear.apply(this.proxy, arguments);
  9014. return this;
  9015. };
  9016. /**
  9017. * Sets the hours for a specified date according to local time.
  9018. *
  9019. * @param {Integer} hoursValue An integer between 0 and 23, representing the hour.
  9020. * @param {Integer} minutesValue Optional, An integer between 0 and 59, representing the minutes.
  9021. * @param {Integer} secondsValue Optional, An integer between 0 and 59, representing the seconds.
  9022. * If you specify the secondsValue parameter, you must also specify the minutesValue.
  9023. * @param {Integer} msValue Optional, A number between 0 and 999, representing the milliseconds.
  9024. * If you specify the msValue parameter, you must also specify the minutesValue and secondsValue.
  9025. */
  9026. jsDate.prototype.setHours = function() {
  9027. this.proxy.setHours.apply(this.proxy, arguments);
  9028. return this;
  9029. };
  9030. /**
  9031. * Implements Date functionality
  9032. */
  9033. jsDate.prototype.setMilliseconds = function(n) {
  9034. this.proxy.setMilliseconds(n);
  9035. return this;
  9036. };
  9037. /**
  9038. * Implements Date functionality
  9039. */
  9040. jsDate.prototype.setMinutes = function() {
  9041. this.proxy.setMinutes.apply(this.proxy, arguments);
  9042. return this;
  9043. };
  9044. /**
  9045. * Implements Date functionality
  9046. */
  9047. jsDate.prototype.setMonth = function() {
  9048. this.proxy.setMonth.apply(this.proxy, arguments);
  9049. return this;
  9050. };
  9051. /**
  9052. * Implements Date functionality
  9053. */
  9054. jsDate.prototype.setSeconds = function() {
  9055. this.proxy.setSeconds.apply(this.proxy, arguments);
  9056. return this;
  9057. };
  9058. /**
  9059. * Implements Date functionality
  9060. */
  9061. jsDate.prototype.setTime = function(n) {
  9062. this.proxy.setTime(n);
  9063. return this;
  9064. };
  9065. /**
  9066. * Implements Date functionality
  9067. */
  9068. jsDate.prototype.setYear = function() {
  9069. this.proxy.setYear.apply(this.proxy, arguments);
  9070. return this;
  9071. };
  9072. /**
  9073. * Provide a formatted string representation of this date.
  9074. *
  9075. * @param {String} formatString A format string.
  9076. * See: {@link jsDate.formats}.
  9077. * @returns {String} Date String.
  9078. */
  9079. jsDate.prototype.strftime = function(formatString) {
  9080. formatString = formatString || this.formatString || jsDate.regional[this.locale]['formatString'];
  9081. return jsDate.strftime(this, formatString, this.syntax);
  9082. };
  9083. /**
  9084. * Return a String representation of this jsDate object.
  9085. * @returns {String} Date string.
  9086. */
  9087. jsDate.prototype.toString = function() {
  9088. return this.proxy.toString();
  9089. };
  9090. /**
  9091. * Convert the current date to an 8-digit integer (%Y%m%d)
  9092. *
  9093. * @returns {Integer}
  9094. */
  9095. jsDate.prototype.toYmdInt = function() {
  9096. return (this.proxy.getFullYear() * 10000) + (this.getMonthNumber() * 100) + this.proxy.getDate();
  9097. };
  9098. /**
  9099. * @namespace Holds localizations for month/day names.
  9100. * <p>jsDate attempts to detect locale when loaded and defaults to 'en'.
  9101. * If a localization is detected which is not available, jsDate defaults to 'en'.
  9102. * Additional localizations can be added after jsDate loads. After adding a localization,
  9103. * call the jsDate.regional.getLocale() method. Currently, en, fr and de are defined.</p>
  9104. *
  9105. * <p>Localizations must be an object and have the following properties defined: monthNames, monthNamesShort, dayNames, dayNamesShort and Localizations are added like:</p>
  9106. * <pre class="code">
  9107. * jsDate.regional['en'] = {
  9108. * monthNames : 'January February March April May June July August September October November December'.split(' '),
  9109. * monthNamesShort : 'Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec'.split(' '),
  9110. * dayNames : 'Sunday Monday Tuesday Wednesday Thursday Friday Saturday'.split(' '),
  9111. * dayNamesShort : 'Sun Mon Tue Wed Thu Fri Sat'.split(' ')
  9112. * };
  9113. * </pre>
  9114. * <p>After adding localizations, call <code>jsDate.regional.getLocale();</code> to update the locale setting with the
  9115. * new localizations.</p>
  9116. */
  9117. jsDate.regional = {
  9118. 'en': {
  9119. monthNames: ['January','February','March','April','May','June','July','August','September','October','November','December'],
  9120. monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun','Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
  9121. dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
  9122. dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
  9123. formatString: '%Y-%m-%d %H:%M:%S'
  9124. },
  9125. 'fr': {
  9126. monthNames: ['Janvier','Février','Mars','Avril','Mai','Juin','Juillet','Août','Septembre','Octobre','Novembre','Décembre'],
  9127. monthNamesShort: ['Jan','Fév','Mar','Avr','Mai','Jun','Jul','Aoû','Sep','Oct','Nov','Déc'],
  9128. dayNames: ['Dimanche','Lundi','Mardi','Mercredi','Jeudi','Vendredi','Samedi'],
  9129. dayNamesShort: ['Dim','Lun','Mar','Mer','Jeu','Ven','Sam'],
  9130. formatString: '%Y-%m-%d %H:%M:%S'
  9131. },
  9132. 'de': {
  9133. monthNames: ['Januar','Februar','März','April','Mai','Juni','Juli','August','September','Oktober','November','Dezember'],
  9134. monthNamesShort: ['Jan','Feb','Mär','Apr','Mai','Jun','Jul','Aug','Sep','Okt','Nov','Dez'],
  9135. dayNames: ['Sonntag','Montag','Dienstag','Mittwoch','Donnerstag','Freitag','Samstag'],
  9136. dayNamesShort: ['So','Mo','Di','Mi','Do','Fr','Sa'],
  9137. formatString: '%Y-%m-%d %H:%M:%S'
  9138. },
  9139. 'es': {
  9140. monthNames: ['Enero','Febrero','Marzo','Abril','Mayo','Junio', 'Julio','Agosto','Septiembre','Octubre','Noviembre','Diciembre'],
  9141. monthNamesShort: ['Ene','Feb','Mar','Abr','May','Jun', 'Jul','Ago','Sep','Oct','Nov','Dic'],
  9142. dayNames: ['Domingo','Lunes','Martes','Mi&eacute;rcoles','Jueves','Viernes','S&aacute;bado'],
  9143. dayNamesShort: ['Dom','Lun','Mar','Mi&eacute;','Juv','Vie','S&aacute;b'],
  9144. formatString: '%Y-%m-%d %H:%M:%S'
  9145. },
  9146. 'ru': {
  9147. monthNames: ['Январь','Февраль','Март','Апрель','Май','Июнь','Июль','Август','Сентябрь','Октябрь','Ноябрь','Декабрь'],
  9148. monthNamesShort: ['Янв','Фев','Мар','Апр','Май','Июн','Июл','Авг','Сен','Окт','Ноя','Дек'],
  9149. dayNames: ['воскресенье','понедельник','вторник','среда','четверг','пятница','суббота'],
  9150. dayNamesShort: ['вск','пнд','втр','срд','чтв','птн','сбт'],
  9151. formatString: '%Y-%m-%d %H:%M:%S'
  9152. },
  9153. 'ar': {
  9154. monthNames: ['كانون الثاني', 'شباط', 'آذار', 'نيسان', 'آذار', 'حزيران','تموز', 'آب', 'أيلول', 'تشرين الأول', 'تشرين الثاني', 'كانون الأول'],
  9155. monthNamesShort: ['1','2','3','4','5','6','7','8','9','10','11','12'],
  9156. dayNames: ['السبت', 'الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة'],
  9157. dayNamesShort: ['سبت', 'أحد', 'اثنين', 'ثلاثاء', 'أربعاء', 'خميس', 'جمعة'],
  9158. formatString: '%Y-%m-%d %H:%M:%S'
  9159. },
  9160. 'pt': {
  9161. monthNames: ['Janeiro','Fevereiro','Mar&ccedil;o','Abril','Maio','Junho','Julho','Agosto','Setembro','Outubro','Novembro','Dezembro'],
  9162. monthNamesShort: ['Jan','Fev','Mar','Abr','Mai','Jun','Jul','Ago','Set','Out','Nov','Dez'],
  9163. dayNames: ['Domingo','Segunda-feira','Ter&ccedil;a-feira','Quarta-feira','Quinta-feira','Sexta-feira','S&aacute;bado'],
  9164. dayNamesShort: ['Dom','Seg','Ter','Qua','Qui','Sex','S&aacute;b'],
  9165. formatString: '%Y-%m-%d %H:%M:%S'
  9166. },
  9167. 'pt-BR': {
  9168. monthNames: ['Janeiro','Fevereiro','Mar&ccedil;o','Abril','Maio','Junho', 'Julho','Agosto','Setembro','Outubro','Novembro','Dezembro'],
  9169. monthNamesShort: ['Jan','Fev','Mar','Abr','Mai','Jun','Jul','Ago','Set','Out','Nov','Dez'],
  9170. dayNames: ['Domingo','Segunda-feira','Ter&ccedil;a-feira','Quarta-feira','Quinta-feira','Sexta-feira','S&aacute;bado'],
  9171. dayNamesShort: ['Dom','Seg','Ter','Qua','Qui','Sex','S&aacute;b'],
  9172. formatString: '%Y-%m-%d %H:%M:%S'
  9173. },
  9174. 'pl': {
  9175. monthNames: ['Styczeń','Luty','Marzec','Kwiecień','Maj','Czerwiec','Lipiec','Sierpień','Wrzesień','Październik','Listopad','Grudzień'],
  9176. monthNamesShort: ['Sty', 'Lut', 'Mar', 'Kwi', 'Maj', 'Cze','Lip', 'Sie', 'Wrz', 'Paź', 'Lis', 'Gru'],
  9177. dayNames: ['Niedziela', 'Poniedziałek', 'Wtorek', 'Środa', 'Czwartek', 'Piątek', 'Sobota'],
  9178. dayNamesShort: ['Ni', 'Pn', 'Wt', 'Śr', 'Cz', 'Pt', 'Sb'],
  9179. formatString: '%Y-%m-%d %H:%M:%S'
  9180. },
  9181. 'nl': {
  9182. monthNames: ['Januari','Februari','Maart','April','Mei','Juni','July','Augustus','September','Oktober','November','December'],
  9183. monthNamesShort: ['Jan','Feb','Mar','Apr','Mei','Jun','Jul','Aug','Sep','Okt','Nov','Dec'],
  9184. dayNames:','['Zondag','Maandag','Dinsdag','Woensdag','Donderdag','Vrijdag','Zaterdag'],
  9185. dayNamesShort: ['Zo','Ma','Di','Wo','Do','Vr','Za'],
  9186. formatString: '%Y-%m-%d %H:%M:%S'
  9187. },
  9188. 'sv': {
  9189. monthNames: ['januari','februari','mars','april','maj','juni','juli','augusti','september','oktober','november','december'],
  9190. monthNamesShort: ['jan','feb','mar','apr','maj','jun','jul','aug','sep','okt','nov','dec'],
  9191. dayNames: ['söndag','måndag','tisdag','onsdag','torsdag','fredag','lördag'],
  9192. dayNamesShort: ['sön','mån','tis','ons','tor','fre','lör'],
  9193. formatString: '%Y-%m-%d %H:%M:%S'
  9194. },
  9195. 'it': {
  9196. monthNames: ['Gennaio','Febbraio','Marzo','Aprile','Maggio','Giugno','Luglio','Agosto','Settembre','Ottobre','Novembre','Dicembre'],
  9197. monthNamesShort: ['Gen','Feb','Mar','Apr','Mag','Giu','Lug','Ago','Set','Ott','Nov','Dic'],
  9198. dayNames: ['Domenica','Lunedi','Martedi','Mercoledi','Giovedi','Venerdi','Sabato'],
  9199. dayNamesShort: ['Dom','Lun','Mar','Mer','Gio','Ven','Sab'],
  9200. formatString: '%d-%m-%Y %H:%M:%S'
  9201. }
  9202. };
  9203. // Set english variants to 'en'
  9204. jsDate.regional['en-US'] = jsDate.regional['en-GB'] = jsDate.regional['en'];
  9205. /**
  9206. * Try to determine the users locale based on the lang attribute of the html page. Defaults to 'en'
  9207. * if it cannot figure out a locale of if the locale does not have a localization defined.
  9208. * @returns {String} locale
  9209. */
  9210. jsDate.regional.getLocale = function () {
  9211. var l = jsDate.config.defaultLocale;
  9212. if ( document && document.getElementsByTagName('html') && document.getElementsByTagName('html')[0].lang ) {
  9213. l = document.getElementsByTagName('html')[0].lang;
  9214. if (!jsDate.regional.hasOwnProperty(l)) {
  9215. l = jsDate.config.defaultLocale;
  9216. }
  9217. }
  9218. return l;
  9219. };
  9220. // ms in day
  9221. var day = 24 * 60 * 60 * 1000;
  9222. // padd a number with zeros
  9223. var addZeros = function(num, digits) {
  9224. num = String(num);
  9225. var i = digits - num.length;
  9226. var s = String(Math.pow(10, i)).slice(1);
  9227. return s.concat(num);
  9228. };
  9229. // representations used for calculating differences between dates.
  9230. // This borrows heavily from Ken Snyder's work.
  9231. var multipliers = {
  9232. millisecond: 1,
  9233. second: 1000,
  9234. minute: 60 * 1000,
  9235. hour: 60 * 60 * 1000,
  9236. day: day,
  9237. week: 7 * day,
  9238. month: {
  9239. // add a number of months
  9240. add: function(d, number) {
  9241. // add any years needed (increments of 12)
  9242. multipliers.year.add(d, Math[number > 0 ? 'floor' : 'ceil'](number / 12));
  9243. // ensure that we properly wrap betwen December and January
  9244. // 11 % 12 = 11
  9245. // 12 % 12 = 0
  9246. var prevMonth = d.getMonth() + (number % 12);
  9247. if (prevMonth == 12) {
  9248. prevMonth = 0;
  9249. d.setYear(d.getFullYear() + 1);
  9250. } else if (prevMonth == -1) {
  9251. prevMonth = 11;
  9252. d.setYear(d.getFullYear() - 1);
  9253. }
  9254. d.setMonth(prevMonth);
  9255. },
  9256. // get the number of months between two Date objects (decimal to the nearest day)
  9257. diff: function(d1, d2) {
  9258. // get the number of years
  9259. var diffYears = d1.getFullYear() - d2.getFullYear();
  9260. // get the number of remaining months
  9261. var diffMonths = d1.getMonth() - d2.getMonth() + (diffYears * 12);
  9262. // get the number of remaining days
  9263. var diffDays = d1.getDate() - d2.getDate();
  9264. // return the month difference with the days difference as a decimal
  9265. return diffMonths + (diffDays / 30);
  9266. }
  9267. },
  9268. year: {
  9269. // add a number of years
  9270. add: function(d, number) {
  9271. d.setYear(d.getFullYear() + Math[number > 0 ? 'floor' : 'ceil'](number));
  9272. },
  9273. // get the number of years between two Date objects (decimal to the nearest day)
  9274. diff: function(d1, d2) {
  9275. return multipliers.month.diff(d1, d2) / 12;
  9276. }
  9277. }
  9278. };
  9279. //
  9280. // Alias each multiplier with an 's' to allow 'year' and 'years' for example.
  9281. // This comes from Ken Snyders work.
  9282. //
  9283. for (var unit in multipliers) {
  9284. if (unit.substring(unit.length - 1) != 's') { // IE will iterate newly added properties :|
  9285. multipliers[unit + 's'] = multipliers[unit];
  9286. }
  9287. }
  9288. //
  9289. // take a jsDate instance and a format code and return the formatted value.
  9290. // This is a somewhat modified version of Ken Snyder's method.
  9291. //
  9292. var format = function(d, code, syntax) {
  9293. // if shorcut codes are used, recursively expand those.
  9294. if (jsDate.formats[syntax]["shortcuts"][code]) {
  9295. return jsDate.strftime(d, jsDate.formats[syntax]["shortcuts"][code], syntax);
  9296. } else {
  9297. // get the format code function and addZeros() argument
  9298. var getter = (jsDate.formats[syntax]["codes"][code] || '').split('.');
  9299. var nbr = d['get' + getter[0]] ? d['get' + getter[0]]() : '';
  9300. if (getter[1]) {
  9301. nbr = addZeros(nbr, getter[1]);
  9302. }
  9303. return nbr;
  9304. }
  9305. };
  9306. /**
  9307. * @static
  9308. * Static function for convert a date to a string according to a given format. Also acts as namespace for strftime format codes.
  9309. * <p>strftime formatting can be accomplished without creating a jsDate object by calling jsDate.strftime():</p>
  9310. * <pre class="code">
  9311. * var formattedDate = jsDate.strftime('Feb 8, 2006 8:48:32', '%Y-%m-%d %H:%M:%S');
  9312. * </pre>
  9313. * @param {String | Number | Array | jsDate&nbsp;Object | Date&nbsp;Object} date A parsable date string, JavaScript time stamp, Array of form [year, month, day, hours, minutes, seconds, milliseconds], jsDate Object or Date object.
  9314. * @param {String} formatString String with embedded date formatting codes.
  9315. * See: {@link jsDate.formats}.
  9316. * @param {String} syntax Optional syntax to use [default perl].
  9317. * @param {String} locale Optional locale to use.
  9318. * @returns {String} Formatted representation of the date.
  9319. */
  9320. //
  9321. // Logic as implemented here is very similar to Ken Snyder's Date Instance Methods.
  9322. //
  9323. jsDate.strftime = function(d, formatString, syntax, locale) {
  9324. var syn = 'perl';
  9325. var loc = jsDate.regional.getLocale();
  9326. // check if syntax and locale are available or reversed
  9327. if (syntax && jsDate.formats.hasOwnProperty(syntax)) {
  9328. syn = syntax;
  9329. }
  9330. else if (syntax && jsDate.regional.hasOwnProperty(syntax)) {
  9331. loc = syntax;
  9332. }
  9333. if (locale && jsDate.formats.hasOwnProperty(locale)) {
  9334. syn = locale;
  9335. }
  9336. else if (locale && jsDate.regional.hasOwnProperty(locale)) {
  9337. loc = locale;
  9338. }
  9339. if (get_type(d) != "[object Object]" || d._type != "jsDate") {
  9340. d = new jsDate(d);
  9341. d.locale = loc;
  9342. }
  9343. if (!formatString) {
  9344. formatString = d.formatString || jsDate.regional[loc]['formatString'];
  9345. }
  9346. // default the format string to year-month-day
  9347. var source = formatString || '%Y-%m-%d',
  9348. result = '',
  9349. match;
  9350. // replace each format code
  9351. while (source.length > 0) {
  9352. if (match = source.match(jsDate.formats[syn].codes.matcher)) {
  9353. result += source.slice(0, match.index);
  9354. result += (match[1] || '') + format(d, match[2], syn);
  9355. source = source.slice(match.index + match[0].length);
  9356. } else {
  9357. result += source;
  9358. source = '';
  9359. }
  9360. }
  9361. return result;
  9362. };
  9363. /**
  9364. * @namespace
  9365. * Namespace to hold format codes and format shortcuts. "perl" and "php" format codes
  9366. * and shortcuts are defined by default. Additional codes and shortcuts can be
  9367. * added like:
  9368. *
  9369. * <pre class="code">
  9370. * jsDate.formats["perl"] = {
  9371. * "codes": {
  9372. * matcher: /someregex/,
  9373. * Y: "fullYear", // name of "get" method without the "get",
  9374. * ..., // more codes
  9375. * },
  9376. * "shortcuts": {
  9377. * F: '%Y-%m-%d',
  9378. * ..., // more shortcuts
  9379. * }
  9380. * };
  9381. * </pre>
  9382. *
  9383. * <p>Additionally, ISO and SQL shortcuts are defined and can be accesses via:
  9384. * <code>jsDate.formats.ISO</code> and <code>jsDate.formats.SQL</code>
  9385. */
  9386. jsDate.formats = {
  9387. ISO:'%Y-%m-%dT%H:%M:%S.%N%G',
  9388. SQL:'%Y-%m-%d %H:%M:%S'
  9389. };
  9390. /**
  9391. * Perl format codes and shortcuts for strftime.
  9392. *
  9393. * A hash (object) of codes where each code must be an array where the first member is
  9394. * the name of a Date.prototype or jsDate.prototype function to call
  9395. * and optionally a second member indicating the number to pass to addZeros()
  9396. *
  9397. * <p>The following format codes are defined:</p>
  9398. *
  9399. * <pre class="code">
  9400. * Code Result Description
  9401. * == Years ==
  9402. * %Y 2008 Four-digit year
  9403. * %y 08 Two-digit year
  9404. *
  9405. * == Months ==
  9406. * %m 09 Two-digit month
  9407. * %#m 9 One or two-digit month
  9408. * %B September Full month name
  9409. * %b Sep Abbreviated month name
  9410. *
  9411. * == Days ==
  9412. * %d 05 Two-digit day of month
  9413. * %#d 5 One or two-digit day of month
  9414. * %e 5 One or two-digit day of month
  9415. * %A Sunday Full name of the day of the week
  9416. * %a Sun Abbreviated name of the day of the week
  9417. * %w 0 Number of the day of the week (0 = Sunday, 6 = Saturday)
  9418. *
  9419. * == Hours ==
  9420. * %H 23 Hours in 24-hour format (two digits)
  9421. * %#H 3 Hours in 24-hour integer format (one or two digits)
  9422. * %I 11 Hours in 12-hour format (two digits)
  9423. * %#I 3 Hours in 12-hour integer format (one or two digits)
  9424. * %p PM AM or PM
  9425. *
  9426. * == Minutes ==
  9427. * %M 09 Minutes (two digits)
  9428. * %#M 9 Minutes (one or two digits)
  9429. *
  9430. * == Seconds ==
  9431. * %S 02 Seconds (two digits)
  9432. * %#S 2 Seconds (one or two digits)
  9433. * %s 1206567625723 Unix timestamp (Seconds past 1970-01-01 00:00:00)
  9434. *
  9435. * == Milliseconds ==
  9436. * %N 008 Milliseconds (three digits)
  9437. * %#N 8 Milliseconds (one to three digits)
  9438. *
  9439. * == Timezone ==
  9440. * %O 360 difference in minutes between local time and GMT
  9441. * %Z Mountain Standard Time Name of timezone as reported by browser
  9442. * %G 06:00 Hours and minutes between GMT
  9443. *
  9444. * == Shortcuts ==
  9445. * %F 2008-03-26 %Y-%m-%d
  9446. * %T 05:06:30 %H:%M:%S
  9447. * %X 05:06:30 %H:%M:%S
  9448. * %x 03/26/08 %m/%d/%y
  9449. * %D 03/26/08 %m/%d/%y
  9450. * %#c Wed Mar 26 15:31:00 2008 %a %b %e %H:%M:%S %Y
  9451. * %v 3-Sep-2008 %e-%b-%Y
  9452. * %R 15:31 %H:%M
  9453. * %r 03:31:00 PM %I:%M:%S %p
  9454. *
  9455. * == Characters ==
  9456. * %n \n Newline
  9457. * %t \t Tab
  9458. * %% % Percent Symbol
  9459. * </pre>
  9460. *
  9461. * <p>Formatting shortcuts that will be translated into their longer version.
  9462. * Be sure that format shortcuts do not refer to themselves: this will cause an infinite loop.</p>
  9463. *
  9464. * <p>Format codes and format shortcuts can be redefined after the jsDate
  9465. * module is imported.</p>
  9466. *
  9467. * <p>Note that if you redefine the whole hash (object), you must supply a "matcher"
  9468. * regex for the parser. The default matcher is:</p>
  9469. *
  9470. * <code>/()%(#?(%|[a-z]))/i</code>
  9471. *
  9472. * <p>which corresponds to the Perl syntax used by default.</p>
  9473. *
  9474. * <p>By customizing the matcher and format codes, nearly any strftime functionality is possible.</p>
  9475. */
  9476. jsDate.formats.perl = {
  9477. codes: {
  9478. //
  9479. // 2-part regex matcher for format codes
  9480. //
  9481. // first match must be the character before the code (to account for escaping)
  9482. // second match must be the format code character(s)
  9483. //
  9484. matcher: /()%(#?(%|[a-z]))/i,
  9485. // year
  9486. Y: 'FullYear',
  9487. y: 'ShortYear.2',
  9488. // month
  9489. m: 'MonthNumber.2',
  9490. '#m': 'MonthNumber',
  9491. B: 'MonthName',
  9492. b: 'AbbrMonthName',
  9493. // day
  9494. d: 'Date.2',
  9495. '#d': 'Date',
  9496. e: 'Date',
  9497. A: 'DayName',
  9498. a: 'AbbrDayName',
  9499. w: 'Day',
  9500. // hours
  9501. H: 'Hours.2',
  9502. '#H': 'Hours',
  9503. I: 'Hours12.2',
  9504. '#I': 'Hours12',
  9505. p: 'AMPM',
  9506. // minutes
  9507. M: 'Minutes.2',
  9508. '#M': 'Minutes',
  9509. // seconds
  9510. S: 'Seconds.2',
  9511. '#S': 'Seconds',
  9512. s: 'Unix',
  9513. // milliseconds
  9514. N: 'Milliseconds.3',
  9515. '#N': 'Milliseconds',
  9516. // timezone
  9517. O: 'TimezoneOffset',
  9518. Z: 'TimezoneName',
  9519. G: 'GmtOffset'
  9520. },
  9521. shortcuts: {
  9522. // date
  9523. F: '%Y-%m-%d',
  9524. // time
  9525. T: '%H:%M:%S',
  9526. X: '%H:%M:%S',
  9527. // local format date
  9528. x: '%m/%d/%y',
  9529. D: '%m/%d/%y',
  9530. // local format extended
  9531. '#c': '%a %b %e %H:%M:%S %Y',
  9532. // local format short
  9533. v: '%e-%b-%Y',
  9534. R: '%H:%M',
  9535. r: '%I:%M:%S %p',
  9536. // tab and newline
  9537. t: '\t',
  9538. n: '\n',
  9539. '%': '%'
  9540. }
  9541. };
  9542. /**
  9543. * PHP format codes and shortcuts for strftime.
  9544. *
  9545. * A hash (object) of codes where each code must be an array where the first member is
  9546. * the name of a Date.prototype or jsDate.prototype function to call
  9547. * and optionally a second member indicating the number to pass to addZeros()
  9548. *
  9549. * <p>The following format codes are defined:</p>
  9550. *
  9551. * <pre class="code">
  9552. * Code Result Description
  9553. * === Days ===
  9554. * %a Sun through Sat An abbreviated textual representation of the day
  9555. * %A Sunday - Saturday A full textual representation of the day
  9556. * %d 01 to 31 Two-digit day of the month (with leading zeros)
  9557. * %e 1 to 31 Day of the month, with a space preceding single digits.
  9558. * %j 001 to 366 Day of the year, 3 digits with leading zeros
  9559. * %u 1 - 7 (Mon - Sun) ISO-8601 numeric representation of the day of the week
  9560. * %w 0 - 6 (Sun - Sat) Numeric representation of the day of the week
  9561. *
  9562. * === Week ===
  9563. * %U 13 Full Week number, starting with the first Sunday as the first week
  9564. * %V 01 through 53 ISO-8601:1988 week number, starting with the first week of the year
  9565. * with at least 4 weekdays, with Monday being the start of the week
  9566. * %W 46 A numeric representation of the week of the year,
  9567. * starting with the first Monday as the first week
  9568. * === Month ===
  9569. * %b Jan through Dec Abbreviated month name, based on the locale
  9570. * %B January - December Full month name, based on the locale
  9571. * %h Jan through Dec Abbreviated month name, based on the locale (an alias of %b)
  9572. * %m 01 - 12 (Jan - Dec) Two digit representation of the month
  9573. *
  9574. * === Year ===
  9575. * %C 19 Two digit century (year/100, truncated to an integer)
  9576. * %y 09 for 2009 Two digit year
  9577. * %Y 2038 Four digit year
  9578. *
  9579. * === Time ===
  9580. * %H 00 through 23 Two digit representation of the hour in 24-hour format
  9581. * %I 01 through 12 Two digit representation of the hour in 12-hour format
  9582. * %l 1 through 12 Hour in 12-hour format, with a space preceeding single digits
  9583. * %M 00 through 59 Two digit representation of the minute
  9584. * %p AM/PM UPPER-CASE 'AM' or 'PM' based on the given time
  9585. * %P am/pm lower-case 'am' or 'pm' based on the given time
  9586. * %r 09:34:17 PM Same as %I:%M:%S %p
  9587. * %R 00:35 Same as %H:%M
  9588. * %S 00 through 59 Two digit representation of the second
  9589. * %T 21:34:17 Same as %H:%M:%S
  9590. * %X 03:59:16 Preferred time representation based on locale, without the date
  9591. * %z -0500 or EST Either the time zone offset from UTC or the abbreviation
  9592. * %Z -0500 or EST The time zone offset/abbreviation option NOT given by %z
  9593. *
  9594. * === Time and Date ===
  9595. * %D 02/05/09 Same as %m/%d/%y
  9596. * %F 2009-02-05 Same as %Y-%m-%d (commonly used in database datestamps)
  9597. * %s 305815200 Unix Epoch Time timestamp (same as the time() function)
  9598. * %x 02/05/09 Preferred date representation, without the time
  9599. *
  9600. * === Miscellaneous ===
  9601. * %n --- A newline character (\n)
  9602. * %t --- A Tab character (\t)
  9603. * %% --- A literal percentage character (%)
  9604. * </pre>
  9605. */
  9606. jsDate.formats.php = {
  9607. codes: {
  9608. //
  9609. // 2-part regex matcher for format codes
  9610. //
  9611. // first match must be the character before the code (to account for escaping)
  9612. // second match must be the format code character(s)
  9613. //
  9614. matcher: /()%((%|[a-z]))/i,
  9615. // day
  9616. a: 'AbbrDayName',
  9617. A: 'DayName',
  9618. d: 'Date.2',
  9619. e: 'Date',
  9620. j: 'DayOfYear.3',
  9621. u: 'DayOfWeek',
  9622. w: 'Day',
  9623. // week
  9624. U: 'FullWeekOfYear.2',
  9625. V: 'IsoWeek.2',
  9626. W: 'WeekOfYear.2',
  9627. // month
  9628. b: 'AbbrMonthName',
  9629. B: 'MonthName',
  9630. m: 'MonthNumber.2',
  9631. h: 'AbbrMonthName',
  9632. // year
  9633. C: 'Century.2',
  9634. y: 'ShortYear.2',
  9635. Y: 'FullYear',
  9636. // time
  9637. H: 'Hours.2',
  9638. I: 'Hours12.2',
  9639. l: 'Hours12',
  9640. p: 'AMPM',
  9641. P: 'AmPm',
  9642. M: 'Minutes.2',
  9643. S: 'Seconds.2',
  9644. s: 'Unix',
  9645. O: 'TimezoneOffset',
  9646. z: 'GmtOffset',
  9647. Z: 'TimezoneAbbr'
  9648. },
  9649. shortcuts: {
  9650. D: '%m/%d/%y',
  9651. F: '%Y-%m-%d',
  9652. T: '%H:%M:%S',
  9653. X: '%H:%M:%S',
  9654. x: '%m/%d/%y',
  9655. R: '%H:%M',
  9656. r: '%I:%M:%S %p',
  9657. t: '\t',
  9658. n: '\n',
  9659. '%': '%'
  9660. }
  9661. };
  9662. //
  9663. // Conceptually, the logic implemented here is similar to Ken Snyder's Date Instance Methods.
  9664. // I use his idea of a set of parsers which can be regular expressions or functions,
  9665. // iterating through those, and then seeing if Date.parse() will create a date.
  9666. // The parser expressions and functions are a little different and some bugs have been
  9667. // worked out. Also, a lot of "pre-parsing" is done to fix implementation
  9668. // variations of Date.parse() between browsers.
  9669. //
  9670. jsDate.createDate = function(date) {
  9671. // if passing in multiple arguments, try Date constructor
  9672. if (date == null) {
  9673. return new Date();
  9674. }
  9675. // If the passed value is already a date object, return it
  9676. if (date instanceof Date) {
  9677. return date;
  9678. }
  9679. // if (typeof date == 'number') return new Date(date * 1000);
  9680. // If the passed value is an integer, interpret it as a javascript timestamp
  9681. if (typeof date == 'number') {
  9682. return new Date(date);
  9683. }
  9684. // Before passing strings into Date.parse(), have to normalize them for certain conditions.
  9685. // If strings are not formatted staccording to the EcmaScript spec, results from Date parse will be implementation dependent.
  9686. //
  9687. // For example:
  9688. // * FF and Opera assume 2 digit dates are pre y2k, Chome assumes <50 is pre y2k, 50+ is 21st century.
  9689. // * Chrome will correctly parse '1984-1-25' into localtime, FF and Opera will not parse.
  9690. // * Both FF, Chrome and Opera will parse '1984/1/25' into localtime.
  9691. // remove leading and trailing spaces
  9692. var parsable = String(date).replace(/^\s*(.+)\s*$/g, '$1');
  9693. // replace dahses (-) with slashes (/) in dates like n[nnn]/n[n]/n[nnn]
  9694. parsable = parsable.replace(/^([0-9]{1,4})-([0-9]{1,2})-([0-9]{1,4})/, "$1/$2/$3");
  9695. /////////
  9696. // Need to check for '15-Dec-09' also.
  9697. // FF will not parse, but Chrome will.
  9698. // Chrome will set date to 2009 as well.
  9699. /////////
  9700. // first check for 'dd-mmm-yyyy' or 'dd/mmm/yyyy' like '15-Dec-2010'
  9701. parsable = parsable.replace(/^(3[01]|[0-2]?\d)[-\/]([a-z]{3,})[-\/](\d{4})/i, "$1 $2 $3");
  9702. // Now check for 'dd-mmm-yy' or 'dd/mmm/yy' and normalize years to default century.
  9703. var match = parsable.match(/^(3[01]|[0-2]?\d)[-\/]([a-z]{3,})[-\/](\d{2})\D*/i);
  9704. if (match && match.length > 3) {
  9705. var m3 = parseFloat(match[3]);
  9706. var ny = jsDate.config.defaultCentury + m3;
  9707. ny = String(ny);
  9708. // now replace 2 digit year with 4 digit year
  9709. parsable = parsable.replace(/^(3[01]|[0-2]?\d)[-\/]([a-z]{3,})[-\/](\d{2})\D*/i, match[1] +' '+ match[2] +' '+ ny);
  9710. }
  9711. // Check for '1/19/70 8:14PM'
  9712. // where starts with mm/dd/yy or yy/mm/dd and have something after
  9713. // Check if 1st postiion is greater than 31, assume it is year.
  9714. // Assme all 2 digit years are 1900's.
  9715. // Finally, change them into US style mm/dd/yyyy representations.
  9716. match = parsable.match(/^([0-9]{1,2})[-\/]([0-9]{1,2})[-\/]([0-9]{1,2})[^0-9]/);
  9717. function h1(parsable, match) {
  9718. var m1 = parseFloat(match[1]);
  9719. var m2 = parseFloat(match[2]);
  9720. var m3 = parseFloat(match[3]);
  9721. var cent = jsDate.config.defaultCentury;
  9722. var ny, nd, nm, str;
  9723. if (m1 > 31) { // first number is a year
  9724. nd = m3;
  9725. nm = m2;
  9726. ny = cent + m1;
  9727. }
  9728. else { // last number is the year
  9729. nd = m2;
  9730. nm = m1;
  9731. ny = cent + m3;
  9732. }
  9733. str = nm+'/'+nd+'/'+ny;
  9734. // now replace 2 digit year with 4 digit year
  9735. return parsable.replace(/^([0-9]{1,2})[-\/]([0-9]{1,2})[-\/]([0-9]{1,2})/, str);
  9736. }
  9737. if (match && match.length > 3) {
  9738. parsable = h1(parsable, match);
  9739. }
  9740. // Now check for '1/19/70' with nothing after and do as above
  9741. var match = parsable.match(/^([0-9]{1,2})[-\/]([0-9]{1,2})[-\/]([0-9]{1,2})$/);
  9742. if (match && match.length > 3) {
  9743. parsable = h1(parsable, match);
  9744. }
  9745. var i = 0;
  9746. var length = jsDate.matchers.length;
  9747. var pattern,
  9748. ms,
  9749. current = parsable,
  9750. obj;
  9751. while (i < length) {
  9752. ms = Date.parse(current);
  9753. if (!isNaN(ms)) {
  9754. return new Date(ms);
  9755. }
  9756. pattern = jsDate.matchers[i];
  9757. if (typeof pattern == 'function') {
  9758. obj = pattern.call(jsDate, current);
  9759. if (obj instanceof Date) {
  9760. return obj;
  9761. }
  9762. } else {
  9763. current = parsable.replace(pattern[0], pattern[1]);
  9764. }
  9765. i++;
  9766. }
  9767. return NaN;
  9768. };
  9769. /**
  9770. * @static
  9771. * Handy static utility function to return the number of days in a given month.
  9772. * @param {Integer} year Year
  9773. * @param {Integer} month Month (1-12)
  9774. * @returns {Integer} Number of days in the month.
  9775. */
  9776. //
  9777. // handy utility method Borrowed right from Ken Snyder's Date Instance Mehtods.
  9778. //
  9779. jsDate.daysInMonth = function(year, month) {
  9780. if (month == 2) {
  9781. return new Date(year, 1, 29).getDate() == 29 ? 29 : 28;
  9782. }
  9783. return [undefined,31,undefined,31,30,31,30,31,31,30,31,30,31][month];
  9784. };
  9785. //
  9786. // An Array of regular expressions or functions that will attempt to match the date string.
  9787. // Functions are called with scope of a jsDate instance.
  9788. //
  9789. jsDate.matchers = [
  9790. // convert dd.mmm.yyyy to mm/dd/yyyy (world date to US date).
  9791. [/(3[01]|[0-2]\d)\s*\.\s*(1[0-2]|0\d)\s*\.\s*([1-9]\d{3})/, '$2/$1/$3'],
  9792. // convert yyyy-mm-dd to mm/dd/yyyy (ISO date to US date).
  9793. [/([1-9]\d{3})\s*-\s*(1[0-2]|0\d)\s*-\s*(3[01]|[0-2]\d)/, '$2/$3/$1'],
  9794. // Handle 12 hour or 24 hour time with milliseconds am/pm and optional date part.
  9795. function(str) {
  9796. var match = str.match(/^(?:(.+)\s+)?([012]?\d)(?:\s*\:\s*(\d\d))?(?:\s*\:\s*(\d\d(\.\d*)?))?\s*(am|pm)?\s*$/i);
  9797. // opt. date hour opt. minute opt. second opt. msec opt. am or pm
  9798. if (match) {
  9799. if (match[1]) {
  9800. var d = this.createDate(match[1]);
  9801. if (isNaN(d)) {
  9802. return;
  9803. }
  9804. } else {
  9805. var d = new Date();
  9806. d.setMilliseconds(0);
  9807. }
  9808. var hour = parseFloat(match[2]);
  9809. if (match[6]) {
  9810. hour = match[6].toLowerCase() == 'am' ? (hour == 12 ? 0 : hour) : (hour == 12 ? 12 : hour + 12);
  9811. }
  9812. d.setHours(hour, parseInt(match[3] || 0, 10), parseInt(match[4] || 0, 10), ((parseFloat(match[5] || 0)) || 0)*1000);
  9813. return d;
  9814. }
  9815. else {
  9816. return str;
  9817. }
  9818. },
  9819. // Handle ISO timestamp with time zone.
  9820. function(str) {
  9821. var match = str.match(/^(?:(.+))[T|\s+]([012]\d)(?:\:(\d\d))(?:\:(\d\d))(?:\.\d+)([\+\-]\d\d\:\d\d)$/i);
  9822. if (match) {
  9823. if (match[1]) {
  9824. var d = this.createDate(match[1]);
  9825. if (isNaN(d)) {
  9826. return;
  9827. }
  9828. } else {
  9829. var d = new Date();
  9830. d.setMilliseconds(0);
  9831. }
  9832. var hour = parseFloat(match[2]);
  9833. d.setHours(hour, parseInt(match[3], 10), parseInt(match[4], 10), parseFloat(match[5])*1000);
  9834. return d;
  9835. }
  9836. else {
  9837. return str;
  9838. }
  9839. },
  9840. // Try to match ambiguous strings like 12/8/22.
  9841. // Use FF date assumption that 2 digit years are 20th century (i.e. 1900's).
  9842. // This may be redundant with pre processing of date already performed.
  9843. function(str) {
  9844. var match = str.match(/^([0-3]?\d)\s*[-\/.\s]{1}\s*([a-zA-Z]{3,9})\s*[-\/.\s]{1}\s*([0-3]?\d)$/);
  9845. if (match) {
  9846. var d = new Date();
  9847. var cent = jsDate.config.defaultCentury;
  9848. var m1 = parseFloat(match[1]);
  9849. var m3 = parseFloat(match[3]);
  9850. var ny, nd, nm;
  9851. if (m1 > 31) { // first number is a year
  9852. nd = m3;
  9853. ny = cent + m1;
  9854. }
  9855. else { // last number is the year
  9856. nd = m1;
  9857. ny = cent + m3;
  9858. }
  9859. var nm = inArray(match[2], jsDate.regional[jsDate.regional.getLocale()]["monthNamesShort"]);
  9860. if (nm == -1) {
  9861. nm = inArray(match[2], jsDate.regional[jsDate.regional.getLocale()]["monthNames"]);
  9862. }
  9863. d.setFullYear(ny, nm, nd);
  9864. d.setHours(0,0,0,0);
  9865. return d;
  9866. }
  9867. else {
  9868. return str;
  9869. }
  9870. }
  9871. ];
  9872. //
  9873. // I think John Reisig published this method on his blog, ejohn.
  9874. //
  9875. function inArray( elem, array ) {
  9876. if ( array.indexOf ) {
  9877. return array.indexOf( elem );
  9878. }
  9879. for ( var i = 0, length = array.length; i < length; i++ ) {
  9880. if ( array[ i ] === elem ) {
  9881. return i;
  9882. }
  9883. }
  9884. return -1;
  9885. }
  9886. //
  9887. // Thanks to Kangax, Christian Sciberras and Stack Overflow for this method.
  9888. //
  9889. function get_type(thing){
  9890. if(thing===null) return "[object Null]"; // special case
  9891. return Object.prototype.toString.call(thing);
  9892. }
  9893. $.jsDate = jsDate;
  9894. /**
  9895. * JavaScript printf/sprintf functions.
  9896. *
  9897. * This code has been adapted from the publicly available sprintf methods
  9898. * by Ash Searle. His original header follows:
  9899. *
  9900. * This code is unrestricted: you are free to use it however you like.
  9901. *
  9902. * The functions should work as expected, performing left or right alignment,
  9903. * truncating strings, outputting numbers with a required precision etc.
  9904. *
  9905. * For complex cases, these functions follow the Perl implementations of
  9906. * (s)printf, allowing arguments to be passed out-of-order, and to set the
  9907. * precision or length of the output based on arguments instead of fixed
  9908. * numbers.
  9909. *
  9910. * See http://perldoc.perl.org/functions/sprintf.html for more information.
  9911. *
  9912. * Implemented:
  9913. * - zero and space-padding
  9914. * - right and left-alignment,
  9915. * - base X prefix (binary, octal and hex)
  9916. * - positive number prefix
  9917. * - (minimum) width
  9918. * - precision / truncation / maximum width
  9919. * - out of order arguments
  9920. *
  9921. * Not implemented (yet):
  9922. * - vector flag
  9923. * - size (bytes, words, long-words etc.)
  9924. *
  9925. * Will not implement:
  9926. * - %n or %p (no pass-by-reference in JavaScript)
  9927. *
  9928. * @version 2007.04.27
  9929. * @author Ash Searle
  9930. *
  9931. * You can see the original work and comments on his blog:
  9932. * http://hexmen.com/blog/2007/03/printf-sprintf/
  9933. * http://hexmen.com/js/sprintf.js
  9934. */
  9935. /**
  9936. * @Modifications 2009.05.26
  9937. * @author Chris Leonello
  9938. *
  9939. * Added %p %P specifier
  9940. * Acts like %g or %G but will not add more significant digits to the output than present in the input.
  9941. * Example:
  9942. * Format: '%.3p', Input: 0.012, Output: 0.012
  9943. * Format: '%.3g', Input: 0.012, Output: 0.0120
  9944. * Format: '%.4p', Input: 12.0, Output: 12.0
  9945. * Format: '%.4g', Input: 12.0, Output: 12.00
  9946. * Format: '%.4p', Input: 4.321e-5, Output: 4.321e-5
  9947. * Format: '%.4g', Input: 4.321e-5, Output: 4.3210e-5
  9948. *
  9949. * Example:
  9950. * >>> $.jqplot.sprintf('%.2f, %d', 23.3452, 43.23)
  9951. * "23.35, 43"
  9952. * >>> $.jqplot.sprintf("no value: %n, decimal with thousands separator: %'d", 23.3452, 433524)
  9953. * "no value: , decimal with thousands separator: 433,524"
  9954. */
  9955. $.jqplot.sprintf = function() {
  9956. function pad(str, len, chr, leftJustify) {
  9957. var padding = (str.length >= len) ? '' : Array(1 + len - str.length >>> 0).join(chr);
  9958. return leftJustify ? str + padding : padding + str;
  9959. }
  9960. function thousand_separate(value) {
  9961. var value_str = new String(value);
  9962. for (var i=10; i>0; i--) {
  9963. if (value_str == (value_str = value_str.replace(/^(\d+)(\d{3})/, "$1"+$.jqplot.sprintf.thousandsSeparator+"$2"))) break;
  9964. }
  9965. return value_str;
  9966. }
  9967. function justify(value, prefix, leftJustify, minWidth, zeroPad, htmlSpace) {
  9968. var diff = minWidth - value.length;
  9969. if (diff > 0) {
  9970. var spchar = ' ';
  9971. if (htmlSpace) { spchar = '&nbsp;'; }
  9972. if (leftJustify || !zeroPad) {
  9973. value = pad(value, minWidth, spchar, leftJustify);
  9974. } else {
  9975. value = value.slice(0, prefix.length) + pad('', diff, '0', true) + value.slice(prefix.length);
  9976. }
  9977. }
  9978. return value;
  9979. }
  9980. function formatBaseX(value, base, prefix, leftJustify, minWidth, precision, zeroPad, htmlSpace) {
  9981. // Note: casts negative numbers to positive ones
  9982. var number = value >>> 0;
  9983. prefix = prefix && number && {'2': '0b', '8': '0', '16': '0x'}[base] || '';
  9984. value = prefix + pad(number.toString(base), precision || 0, '0', false);
  9985. return justify(value, prefix, leftJustify, minWidth, zeroPad, htmlSpace);
  9986. }
  9987. function formatString(value, leftJustify, minWidth, precision, zeroPad, htmlSpace) {
  9988. if (precision != null) {
  9989. value = value.slice(0, precision);
  9990. }
  9991. return justify(value, '', leftJustify, minWidth, zeroPad, htmlSpace);
  9992. }
  9993. var a = arguments, i = 0, format = a[i++];
  9994. return format.replace($.jqplot.sprintf.regex, function(substring, valueIndex, flags, minWidth, _, precision, type) {
  9995. if (substring == '%%') { return '%'; }
  9996. // parse flags
  9997. var leftJustify = false, positivePrefix = '', zeroPad = false, prefixBaseX = false, htmlSpace = false, thousandSeparation = false;
  9998. for (var j = 0; flags && j < flags.length; j++) switch (flags.charAt(j)) {
  9999. case ' ': positivePrefix = ' '; break;
  10000. case '+': positivePrefix = '+'; break;
  10001. case '-': leftJustify = true; break;
  10002. case '0': zeroPad = true; break;
  10003. case '#': prefixBaseX = true; break;
  10004. case '&': htmlSpace = true; break;
  10005. case '\'': thousandSeparation = true; break;
  10006. }
  10007. // parameters may be null, undefined, empty-string or real valued
  10008. // we want to ignore null, undefined and empty-string values
  10009. if (!minWidth) {
  10010. minWidth = 0;
  10011. }
  10012. else if (minWidth == '*') {
  10013. minWidth = +a[i++];
  10014. }
  10015. else if (minWidth.charAt(0) == '*') {
  10016. minWidth = +a[minWidth.slice(1, -1)];
  10017. }
  10018. else {
  10019. minWidth = +minWidth;
  10020. }
  10021. // Note: undocumented perl feature:
  10022. if (minWidth < 0) {
  10023. minWidth = -minWidth;
  10024. leftJustify = true;
  10025. }
  10026. if (!isFinite(minWidth)) {
  10027. throw new Error('$.jqplot.sprintf: (minimum-)width must be finite');
  10028. }
  10029. if (!precision) {
  10030. precision = 'fFeE'.indexOf(type) > -1 ? 6 : (type == 'd') ? 0 : void(0);
  10031. }
  10032. else if (precision == '*') {
  10033. precision = +a[i++];
  10034. }
  10035. else if (precision.charAt(0) == '*') {
  10036. precision = +a[precision.slice(1, -1)];
  10037. }
  10038. else {
  10039. precision = +precision;
  10040. }
  10041. // grab value using valueIndex if required?
  10042. var value = valueIndex ? a[valueIndex.slice(0, -1)] : a[i++];
  10043. switch (type) {
  10044. case 's': {
  10045. if (value == null) {
  10046. return '';
  10047. }
  10048. return formatString(String(value), leftJustify, minWidth, precision, zeroPad, htmlSpace);
  10049. }
  10050. case 'c': return formatString(String.fromCharCode(+value), leftJustify, minWidth, precision, zeroPad, htmlSpace);
  10051. case 'b': return formatBaseX(value, 2, prefixBaseX, leftJustify, minWidth, precision, zeroPad,htmlSpace);
  10052. case 'o': return formatBaseX(value, 8, prefixBaseX, leftJustify, minWidth, precision, zeroPad, htmlSpace);
  10053. case 'x': return formatBaseX(value, 16, prefixBaseX, leftJustify, minWidth, precision, zeroPad, htmlSpace);
  10054. case 'X': return formatBaseX(value, 16, prefixBaseX, leftJustify, minWidth, precision, zeroPad, htmlSpace).toUpperCase();
  10055. case 'u': return formatBaseX(value, 10, prefixBaseX, leftJustify, minWidth, precision, zeroPad, htmlSpace);
  10056. case 'i': {
  10057. var number = parseInt(+value, 10);
  10058. if (isNaN(number)) {
  10059. return '';
  10060. }
  10061. var prefix = number < 0 ? '-' : positivePrefix;
  10062. var number_str = thousandSeparation ? thousand_separate(String(Math.abs(number))): String(Math.abs(number));
  10063. value = prefix + pad(number_str, precision, '0', false);
  10064. //value = prefix + pad(String(Math.abs(number)), precision, '0', false);
  10065. return justify(value, prefix, leftJustify, minWidth, zeroPad, htmlSpace);
  10066. }
  10067. case 'd': {
  10068. var number = Math.round(+value);
  10069. if (isNaN(number)) {
  10070. return '';
  10071. }
  10072. var prefix = number < 0 ? '-' : positivePrefix;
  10073. var number_str = thousandSeparation ? thousand_separate(String(Math.abs(number))): String(Math.abs(number));
  10074. value = prefix + pad(number_str, precision, '0', false);
  10075. return justify(value, prefix, leftJustify, minWidth, zeroPad, htmlSpace);
  10076. }
  10077. case 'e':
  10078. case 'E':
  10079. case 'f':
  10080. case 'F':
  10081. case 'g':
  10082. case 'G':
  10083. {
  10084. var number = +value;
  10085. if (isNaN(number)) {
  10086. return '';
  10087. }
  10088. var prefix = number < 0 ? '-' : positivePrefix;
  10089. var method = ['toExponential', 'toFixed', 'toPrecision']['efg'.indexOf(type.toLowerCase())];
  10090. var textTransform = ['toString', 'toUpperCase']['eEfFgG'.indexOf(type) % 2];
  10091. var number_str = Math.abs(number)[method](precision);
  10092. // Apply the decimal mark properly by splitting the number by the
  10093. // decimalMark, applying thousands separator, and then placing it
  10094. // back in.
  10095. var parts = number_str.toString().split('.');
  10096. parts[0] = thousandSeparation ? thousand_separate(parts[0]) : parts[0];
  10097. number_str = parts.join($.jqplot.sprintf.decimalMark);
  10098. value = prefix + number_str;
  10099. var justified = justify(value, prefix, leftJustify, minWidth, zeroPad, htmlSpace)[textTransform]();
  10100. return justified;
  10101. }
  10102. case 'p':
  10103. case 'P':
  10104. {
  10105. // make sure number is a number
  10106. var number = +value;
  10107. if (isNaN(number)) {
  10108. return '';
  10109. }
  10110. var prefix = number < 0 ? '-' : positivePrefix;
  10111. var parts = String(Number(Math.abs(number)).toExponential()).split(/e|E/);
  10112. var sd = (parts[0].indexOf('.') != -1) ? parts[0].length - 1 : String(number).length;
  10113. var zeros = (parts[1] < 0) ? -parts[1] - 1 : 0;
  10114. if (Math.abs(number) < 1) {
  10115. if (sd + zeros <= precision) {
  10116. value = prefix + Math.abs(number).toPrecision(sd);
  10117. }
  10118. else {
  10119. if (sd <= precision - 1) {
  10120. value = prefix + Math.abs(number).toExponential(sd-1);
  10121. }
  10122. else {
  10123. value = prefix + Math.abs(number).toExponential(precision-1);
  10124. }
  10125. }
  10126. }
  10127. else {
  10128. var prec = (sd <= precision) ? sd : precision;
  10129. value = prefix + Math.abs(number).toPrecision(prec);
  10130. }
  10131. var textTransform = ['toString', 'toUpperCase']['pP'.indexOf(type) % 2];
  10132. return justify(value, prefix, leftJustify, minWidth, zeroPad, htmlSpace)[textTransform]();
  10133. }
  10134. case 'n': return '';
  10135. default: return substring;
  10136. }
  10137. });
  10138. };
  10139. $.jqplot.sprintf.thousandsSeparator = ',';
  10140. // Specifies the decimal mark for floating point values. By default a period '.'
  10141. // is used. If you change this value to for example a comma be sure to also
  10142. // change the thousands separator or else this won't work since a simple String
  10143. // replace is used (replacing all periods with the mark specified here).
  10144. $.jqplot.sprintf.decimalMark = '.';
  10145. $.jqplot.sprintf.regex = /%%|%(\d+\$)?([-+#0&\' ]*)(\*\d+\$|\*|\d+)?(\.(\*\d+\$|\*|\d+))?([nAscboxXuidfegpEGP])/g;
  10146. $.jqplot.getSignificantFigures = function(number) {
  10147. var parts = String(Number(Math.abs(number)).toExponential()).split(/e|E/);
  10148. // total significant digits
  10149. var sd = (parts[0].indexOf('.') != -1) ? parts[0].length - 1 : parts[0].length;
  10150. var zeros = (parts[1] < 0) ? -parts[1] - 1 : 0;
  10151. // exponent
  10152. var expn = parseInt(parts[1], 10);
  10153. // digits to the left of the decimal place
  10154. var dleft = (expn + 1 > 0) ? expn + 1 : 0;
  10155. // digits to the right of the decimal place
  10156. var dright = (sd <= dleft) ? 0 : sd - expn - 1;
  10157. return {significantDigits: sd, digitsLeft: dleft, digitsRight: dright, zeros: zeros, exponent: expn} ;
  10158. };
  10159. $.jqplot.getPrecision = function(number) {
  10160. return $.jqplot.getSignificantFigures(number).digitsRight;
  10161. };
  10162. var backCompat = $.uiBackCompat !== false;
  10163. $.jqplot.effects = {
  10164. effect: {}
  10165. };
  10166. // prefix used for storing data on .data()
  10167. var dataSpace = "jqplot.storage.";
  10168. /******************************************************************************/
  10169. /*********************************** EFFECTS **********************************/
  10170. /******************************************************************************/
  10171. $.extend( $.jqplot.effects, {
  10172. version: "1.9pre",
  10173. // Saves a set of properties in a data storage
  10174. save: function( element, set ) {
  10175. for( var i=0; i < set.length; i++ ) {
  10176. if ( set[ i ] !== null ) {
  10177. element.data( dataSpace + set[ i ], element[ 0 ].style[ set[ i ] ] );
  10178. }
  10179. }
  10180. },
  10181. // Restores a set of previously saved properties from a data storage
  10182. restore: function( element, set ) {
  10183. for( var i=0; i < set.length; i++ ) {
  10184. if ( set[ i ] !== null ) {
  10185. element.css( set[ i ], element.data( dataSpace + set[ i ] ) );
  10186. }
  10187. }
  10188. },
  10189. setMode: function( el, mode ) {
  10190. if (mode === "toggle") {
  10191. mode = el.is( ":hidden" ) ? "show" : "hide";
  10192. }
  10193. return mode;
  10194. },
  10195. // Wraps the element around a wrapper that copies position properties
  10196. createWrapper: function( element ) {
  10197. // if the element is already wrapped, return it
  10198. if ( element.parent().is( ".ui-effects-wrapper" )) {
  10199. return element.parent();
  10200. }
  10201. // wrap the element
  10202. var props = {
  10203. width: element.outerWidth(true),
  10204. height: element.outerHeight(true),
  10205. "float": element.css( "float" )
  10206. },
  10207. wrapper = $( "<div></div>" )
  10208. .addClass( "ui-effects-wrapper" )
  10209. .css({
  10210. fontSize: "100%",
  10211. background: "transparent",
  10212. border: "none",
  10213. margin: 0,
  10214. padding: 0
  10215. }),
  10216. // Store the size in case width/height are defined in % - Fixes #5245
  10217. size = {
  10218. width: element.width(),
  10219. height: element.height()
  10220. },
  10221. active = document.activeElement;
  10222. element.wrap( wrapper );
  10223. // Fixes #7595 - Elements lose focus when wrapped.
  10224. if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
  10225. $( active ).focus();
  10226. }
  10227. wrapper = element.parent(); //Hotfix for jQuery 1.4 since some change in wrap() seems to actually loose the reference to the wrapped element
  10228. // transfer positioning properties to the wrapper
  10229. if ( element.css( "position" ) === "static" ) {
  10230. wrapper.css({ position: "relative" });
  10231. element.css({ position: "relative" });
  10232. } else {
  10233. $.extend( props, {
  10234. position: element.css( "position" ),
  10235. zIndex: element.css( "z-index" )
  10236. });
  10237. $.each([ "top", "left", "bottom", "right" ], function(i, pos) {
  10238. props[ pos ] = element.css( pos );
  10239. if ( isNaN( parseInt( props[ pos ], 10 ) ) ) {
  10240. props[ pos ] = "auto";
  10241. }
  10242. });
  10243. element.css({
  10244. position: "relative",
  10245. top: 0,
  10246. left: 0,
  10247. right: "auto",
  10248. bottom: "auto"
  10249. });
  10250. }
  10251. element.css(size);
  10252. return wrapper.css( props ).show();
  10253. },
  10254. removeWrapper: function( element ) {
  10255. var active = document.activeElement;
  10256. if ( element.parent().is( ".ui-effects-wrapper" ) ) {
  10257. element.parent().replaceWith( element );
  10258. // Fixes #7595 - Elements lose focus when wrapped.
  10259. if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
  10260. $( active ).focus();
  10261. }
  10262. }
  10263. return element;
  10264. }
  10265. });
  10266. // return an effect options object for the given parameters:
  10267. function _normalizeArguments( effect, options, speed, callback ) {
  10268. // short path for passing an effect options object:
  10269. if ( $.isPlainObject( effect ) ) {
  10270. return effect;
  10271. }
  10272. // convert to an object
  10273. effect = { effect: effect };
  10274. // catch (effect)
  10275. if ( options === undefined ) {
  10276. options = {};
  10277. }
  10278. // catch (effect, callback)
  10279. if ( $.isFunction( options ) ) {
  10280. callback = options;
  10281. speed = null;
  10282. options = {};
  10283. }
  10284. // catch (effect, speed, ?)
  10285. if ( $.type( options ) === "number" || $.fx.speeds[ options ]) {
  10286. callback = speed;
  10287. speed = options;
  10288. options = {};
  10289. }
  10290. // catch (effect, options, callback)
  10291. if ( $.isFunction( speed ) ) {
  10292. callback = speed;
  10293. speed = null;
  10294. }
  10295. // add options to effect
  10296. if ( options ) {
  10297. $.extend( effect, options );
  10298. }
  10299. speed = speed || options.duration;
  10300. effect.duration = $.fx.off ? 0 : typeof speed === "number"
  10301. ? speed : speed in $.fx.speeds ? $.fx.speeds[ speed ] : $.fx.speeds._default;
  10302. effect.complete = callback || options.complete;
  10303. return effect;
  10304. }
  10305. function standardSpeed( speed ) {
  10306. // valid standard speeds
  10307. if ( !speed || typeof speed === "number" || $.fx.speeds[ speed ] ) {
  10308. return true;
  10309. }
  10310. // invalid strings - treat as "normal" speed
  10311. if ( typeof speed === "string" && !$.jqplot.effects.effect[ speed ] ) {
  10312. // TODO: remove in 2.0 (#7115)
  10313. if ( backCompat && $.jqplot.effects[ speed ] ) {
  10314. return false;
  10315. }
  10316. return true;
  10317. }
  10318. return false;
  10319. }
  10320. $.fn.extend({
  10321. jqplotEffect: function( effect, options, speed, callback ) {
  10322. var args = _normalizeArguments.apply( this, arguments ),
  10323. mode = args.mode,
  10324. queue = args.queue,
  10325. effectMethod = $.jqplot.effects.effect[ args.effect ],
  10326. // DEPRECATED: remove in 2.0 (#7115)
  10327. oldEffectMethod = !effectMethod && backCompat && $.jqplot.effects[ args.effect ];
  10328. if ( $.fx.off || !( effectMethod || oldEffectMethod ) ) {
  10329. // delegate to the original method (e.g., .show()) if possible
  10330. if ( mode ) {
  10331. return this[ mode ]( args.duration, args.complete );
  10332. } else {
  10333. return this.each( function() {
  10334. if ( args.complete ) {
  10335. args.complete.call( this );
  10336. }
  10337. });
  10338. }
  10339. }
  10340. function run( next ) {
  10341. var elem = $( this ),
  10342. complete = args.complete,
  10343. mode = args.mode;
  10344. function done() {
  10345. if ( $.isFunction( complete ) ) {
  10346. complete.call( elem[0] );
  10347. }
  10348. if ( $.isFunction( next ) ) {
  10349. next();
  10350. }
  10351. }
  10352. // if the element is hiddden and mode is hide,
  10353. // or element is visible and mode is show
  10354. if ( elem.is( ":hidden" ) ? mode === "hide" : mode === "show" ) {
  10355. done();
  10356. } else {
  10357. effectMethod.call( elem[0], args, done );
  10358. }
  10359. }
  10360. // TODO: remove this check in 2.0, effectMethod will always be true
  10361. if ( effectMethod ) {
  10362. return queue === false ? this.each( run ) : this.queue( queue || "fx", run );
  10363. } else {
  10364. // DEPRECATED: remove in 2.0 (#7115)
  10365. return oldEffectMethod.call(this, {
  10366. options: args,
  10367. duration: args.duration,
  10368. callback: args.complete,
  10369. mode: args.mode
  10370. });
  10371. }
  10372. }
  10373. });
  10374. var rvertical = /up|down|vertical/,
  10375. rpositivemotion = /up|left|vertical|horizontal/;
  10376. $.jqplot.effects.effect.blind = function( o, done ) {
  10377. // Create element
  10378. var el = $( this ),
  10379. props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
  10380. mode = $.jqplot.effects.setMode( el, o.mode || "hide" ),
  10381. direction = o.direction || "up",
  10382. vertical = rvertical.test( direction ),
  10383. ref = vertical ? "height" : "width",
  10384. ref2 = vertical ? "top" : "left",
  10385. motion = rpositivemotion.test( direction ),
  10386. animation = {},
  10387. show = mode === "show",
  10388. wrapper, distance, top;
  10389. // // if already wrapped, the wrapper's properties are my property. #6245
  10390. if ( el.parent().is( ".ui-effects-wrapper" ) ) {
  10391. $.jqplot.effects.save( el.parent(), props );
  10392. } else {
  10393. $.jqplot.effects.save( el, props );
  10394. }
  10395. el.show();
  10396. top = parseInt(el.css('top'), 10);
  10397. wrapper = $.jqplot.effects.createWrapper( el ).css({
  10398. overflow: "hidden"
  10399. });
  10400. distance = vertical ? wrapper[ ref ]() + top : wrapper[ ref ]();
  10401. animation[ ref ] = show ? String(distance) : '0';
  10402. if ( !motion ) {
  10403. el
  10404. .css( vertical ? "bottom" : "right", 0 )
  10405. .css( vertical ? "top" : "left", "" )
  10406. .css({ position: "absolute" });
  10407. animation[ ref2 ] = show ? '0' : String(distance);
  10408. }
  10409. // // start at 0 if we are showing
  10410. if ( show ) {
  10411. wrapper.css( ref, 0 );
  10412. if ( ! motion ) {
  10413. wrapper.css( ref2, distance );
  10414. }
  10415. }
  10416. // // Animate
  10417. wrapper.animate( animation, {
  10418. duration: o.duration,
  10419. easing: o.easing,
  10420. queue: false,
  10421. complete: function() {
  10422. if ( mode === "hide" ) {
  10423. el.hide();
  10424. }
  10425. $.jqplot.effects.restore( el, props );
  10426. $.jqplot.effects.removeWrapper( el );
  10427. done();
  10428. }
  10429. });
  10430. };
  10431. })(jQuery);