| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717 |
- // version: 2014-06-26
- /**
- * o--------------------------------------------------------------------------------o
- * | This file is part of the RGraph package. RGraph is Free Software, licensed |
- * | under the MIT license - so it's free to use for all purposes. If you want to |
- * | donate to help keep the project going then you can do so here: |
- * | |
- * | http://www.rgraph.net/donate |
- * o--------------------------------------------------------------------------------o
- */
- RGraph = window.RGraph || {isRGraph: true};
- /**
- * The line chart constructor
- *
- * @param object canvas The cxanvas object
- * @param array ... The lines to plot
- */
- RGraph.Line = function (id)
- {
- var tmp = RGraph.getCanvasTag(id);
- // Get the canvas and context objects
- this.id = tmp[0];
- this.canvas = tmp[1];
- this.context = this.canvas.getContext ? this.canvas.getContext("2d", {alpha: (typeof id === 'object' && id.alpha === false) ? false : true}) : null;
- this.canvas.__object__ = this;
- this.type = 'line';
- this.max = 0;
- this.coords = [];
- this.coords2 = [];
- this.coords.key = [];
- this.coordsText = [];
- this.coordsSpline = [];
- this.hasnegativevalues = false;
- this.isRGraph = true;
- this.uid = RGraph.CreateUID();
- this.canvas.uid = this.canvas.uid ? this.canvas.uid : RGraph.CreateUID();
- this.colorsParsed = false;
- this.original_colors = [];
- this.firstDraw = true; // After the first draw this will be false
- /**
- * Compatibility with older browsers
- */
- //RGraph.OldBrowserCompat(this.context);
- // Various config type stuff
- this.properties =
- {
- 'chart.background.barcolor1': 'rgba(0,0,0,0)',
- 'chart.background.barcolor2': 'rgba(0,0,0,0)',
- 'chart.background.grid': 1,
- 'chart.background.grid.width': 1,
- 'chart.background.grid.hsize': 25,
- 'chart.background.grid.vsize': 25,
- 'chart.background.grid.color': '#ddd',
- 'chart.background.grid.vlines': true,
- 'chart.background.grid.hlines': true,
- 'chart.background.grid.border': true,
- 'chart.background.grid.autofit': true,
- 'chart.background.grid.autofit.align': false,
- 'chart.background.grid.autofit.numhlines': 5,
- 'chart.background.grid.autofit.numvlines': 20,
- 'chart.background.grid.dashed': false,
- 'chart.background.grid.dotted': false,
- 'chart.background.hbars': null,
- 'chart.background.image': null,
- 'chart.background.image.stretch': true,
- 'chart.background.image.x': null,
- 'chart.background.image.y': null,
- 'chart.background.image.w': null,
- 'chart.background.image.h': null,
- 'chart.background.image.align': null,
- 'chart.background.color': null,
- 'chart.labels': null,
- 'chart.labels.ingraph': null,
- 'chart.labels.above': false,
- 'chart.labels.above.size': 8,
- 'chart.labels.above.decimals': null,
- 'chart.xtickgap': 20,
- 'chart.smallxticks': 3,
- 'chart.largexticks': 5,
- 'chart.ytickgap': 20,
- 'chart.smallyticks': 3,
- 'chart.largeyticks': 5,
- 'chart.numyticks': 10,
- 'chart.linewidth': 2.01,
- 'chart.colors': ['red', '#0f0', '#00f', '#f0f', '#ff0', '#0ff','green','pink','blue','black'],
- 'chart.hmargin': 0,
- 'chart.tickmarks.dot.color': 'white',
- 'chart.tickmarks': 'endcircle',
- 'chart.tickmarks.linewidth': null,
- 'chart.ticksize': 3,
- 'chart.gutter.left': 25,
- 'chart.gutter.right': 25,
- 'chart.gutter.top': 25,
- 'chart.gutter.bottom': 25,
- 'chart.tickdirection': -1,
- 'chart.yaxispoints': 5,
- 'chart.fillstyle': null,
- 'chart.xaxispos': 'bottom',
- 'chart.yaxispos': 'left',
- 'chart.xticks': null,
- 'chart.text.size': 10,
- 'chart.text.angle': 0,
- 'chart.text.color': 'black',
- 'chart.text.font': 'Arial',
- 'chart.ymin': 0,
- 'chart.ymax': null,
- 'chart.title': '',
- 'chart.title.background': null,
- 'chart.title.hpos': null,
- 'chart.title.vpos': null,
- 'chart.title.bold': true,
- 'chart.title.font': null,
- 'chart.title.xaxis': '',
- 'chart.title.xaxis.bold': true,
- 'chart.title.xaxis.size': null,
- 'chart.title.xaxis.font': null,
- 'chart.title.yaxis': '',
- 'chart.title.yaxis.bold': true,
- 'chart.title.yaxis.size': null,
- 'chart.title.yaxis.font': null,
- 'chart.title.yaxis.color': null,
- 'chart.title.xaxis.pos': null,
- 'chart.title.yaxis.pos': null,
- 'chart.title.yaxis.x': null,
- 'chart.title.yaxis.y': null,
- 'chart.title.xaxis.x': null,
- 'chart.title.xaxis.y': null,
- 'chart.title.x': null,
- 'chart.title.y': null,
- 'chart.title.halign': null,
- 'chart.title.valign': null,
- 'chart.shadow': true,
- 'chart.shadow.offsetx': 2,
- 'chart.shadow.offsety': 2,
- 'chart.shadow.blur': 3,
- 'chart.shadow.color': 'rgba(0,0,0,0.5)',
- 'chart.tooltips': null,
- 'chart.tooltips.hotspot.xonly': false,
- 'chart.tooltips.hotspot.size': 5,
- 'chart.tooltips.effect': 'fade',
- 'chart.tooltips.css.class': 'RGraph_tooltip',
- 'chart.tooltips.event': 'onmousemove',
- 'chart.tooltips.highlight': true,
- 'chart.tooltips.coords.page': false,
- 'chart.highlight.stroke': 'gray',
- 'chart.highlight.fill': 'white',
- 'chart.stepped': false,
- 'chart.key': null,
- 'chart.key.background': 'white',
- 'chart.key.position': 'graph',
- 'chart.key.halign': null,
- 'chart.key.shadow': false,
- 'chart.key.shadow.color': '#666',
- 'chart.key.shadow.blur': 3,
- 'chart.key.shadow.offsetx': 2,
- 'chart.key.shadow.offsety': 2,
- 'chart.key.position.gutter.boxed': false,
- 'chart.key.position.x': null,
- 'chart.key.position.y': null,
- 'chart.key.color.shape': 'square',
- 'chart.key.rounded': true,
- 'chart.key.linewidth': 1,
- 'chart.key.colors': null,
- 'chart.key.interactive': false,
- 'chart.key.interactive.highlight.chart.stroke': 'rgba(255,0,0,0.3)',
- 'chart.key.interactive.highlight.label': 'rgba(255,0,0,0.2)',
- 'chart.key.text.color': 'black',
- 'chart.contextmenu': null,
- 'chart.ylabels': true,
- 'chart.ylabels.count': 5,
- 'chart.ylabels.inside': false,
- 'chart.scale.invert': false,
- 'chart.xlabels.inside': false,
- 'chart.xlabels.inside.color': 'rgba(255,255,255,0.5)',
- 'chart.noaxes': false,
- 'chart.noyaxis': false,
- 'chart.noxaxis': false,
- 'chart.noendxtick': false,
- 'chart.noendytick': false,
- 'chart.units.post': '',
- 'chart.units.pre': '',
- 'chart.scale.zerostart': false,
- 'chart.scale.decimals': null,
- 'chart.scale.point': '.',
- 'chart.scale.thousand': ',',
- 'chart.crosshairs': false,
- 'chart.crosshairs.color': '#333',
- 'chart.crosshairs.hline': true,
- 'chart.crosshairs.vline': true,
- 'chart.annotatable': false,
- 'chart.annotate.color': 'black',
- 'chart.axesontop': false,
- 'chart.filled': false,
- 'chart.filled.range': false,
- 'chart.filled.range.threshold': null,
- 'chart.filled.range.threshold.colors': ['red', 'green'],
- 'chart.filled.accumulative': true,
- 'chart.variant': null,
- 'chart.axis.color': 'black',
- 'chart.axis.linewidth': 1,
- 'chart.numxticks': (arguments[1] && typeof(arguments[1][0]) == 'number' ? arguments[1].length : 20),
- 'chart.numyticks': 10,
- 'chart.zoom.factor': 1.5,
- 'chart.zoom.fade.in': true,
- 'chart.zoom.fade.out': true,
- 'chart.zoom.hdir': 'right',
- 'chart.zoom.vdir': 'down',
- 'chart.zoom.frames': 25,
- 'chart.zoom.delay': 16.666,
- 'chart.zoom.shadow': true,
- 'chart.zoom.background': true,
- 'chart.zoom.action': 'zoom',
- 'chart.backdrop': false,
- 'chart.backdrop.size': 30,
- 'chart.backdrop.alpha': 0.2,
- 'chart.resizable': false,
- 'chart.resize.handle.adjust': [0,0],
- 'chart.resize.handle.background': null,
- 'chart.adjustable': false,
- 'chart.noredraw': false,
- 'chart.outofbounds': false,
- 'chart.chromefix': true,
- 'chart.animation.factor': 1,
- 'chart.animation.unfold.x': false,
- 'chart.animation.unfold.y': true,
- 'chart.animation.unfold.initial': 2,
- 'chart.animation.trace.clip': 1,
- 'chart.curvy': false,
- 'chart.line.visible': true,
- 'chart.events.click': null,
- 'chart.events.mousemove': null
- }
- /**
- * Change null arguments to empty arrays
- */
- for (var i=1; i<arguments.length; ++i) {
- if (typeof(arguments[i]) == 'null' || !arguments[i]) {
- arguments[i] = [];
- }
- }
- /**
- * Store the original data. Thiss also allows for giving arguments as one big array.
- */
- this.original_data = [];
- for (var i=1; i<arguments.length; ++i) {
- if (arguments[1] && typeof(arguments[1]) == 'object' && arguments[1][0] && typeof(arguments[1][0]) == 'object' && arguments[1][0].length) {
- var tmp = [];
- for (var i=0; i<arguments[1].length; ++i) {
- tmp[i] = RGraph.array_clone(arguments[1][i]);
- }
- for (var j=0; j<tmp.length; ++j) {
- this.original_data[j] = RGraph.array_clone(tmp[j]);
- }
- } else {
- this.original_data[i - 1] = RGraph.array_clone(arguments[i]);
- }
- }
- // Check for support
- if (!this.canvas) {
- alert('[LINE] Fatal error: no canvas support');
- return;
- }
-
- /**
- * Store the data here as one big array
- */
- this.data_arr = RGraph.array_linearize(this.original_data);
- for (var i=0; i<this.data_arr.length; ++i) {
- this['$' + i] = {};
- }
- /**
- * Translate half a pixel for antialiasing purposes - but only if it hasn't beeen
- * done already
- */
- if (!this.canvas.__rgraph_aa_translated__) {
- this.context.translate(0.5,0.5);
-
- this.canvas.__rgraph_aa_translated__ = true;
- }
- // Short variable names
- var RG = RGraph;
- var ca = this.canvas;
- var co = ca.getContext('2d');
- var prop = this.properties;
- var jq = jQuery;
- var pa = RG.Path;
- var win = window;
- var doc = document;
- var ma = Math;
-
-
-
- /**
- * "Decorate" the object with the generic effects if the effects library has been included
- */
- if (RG.Effects && typeof RG.Effects.decorate === 'function') {
- RG.Effects.decorate(this);
- }
-
- /**
- * An all encompassing accessor
- *
- * @param string name The name of the property
- * @param mixed value The value of the property
- */
- this.set =
- this.Set = function (name, value)
- {
- name = name.toLowerCase();
-
- /**
- * This should be done first - prepend the propertyy name with "chart." if necessary
- */
- if (name.substr(0,6) != 'chart.') {
- name = 'chart.' + name;
- }
-
- // Consolidate the tooltips
- if (name == 'chart.tooltips' && typeof value == 'object' && value) {
-
- var tooltips = [];
-
- for (var i=1; i<arguments.length; i++) {
- if (typeof(arguments[i]) == 'object' && arguments[i][0]) {
- for (var j=0; j<arguments[i].length; j++) {
- tooltips.push(arguments[i][j]);
- }
-
- } else if (typeof(arguments[i]) == 'function') {
- tooltips = arguments[i];
-
- } else {
- tooltips.push(arguments[i]);
- }
- }
-
- // Because "value" is used further down at the end of this function, set it to the expanded array os tooltips
- value = tooltips;
- }
-
-
- /**
- * If (buggy) Chrome and the linewidth is 1, change it to 1.01
- */
- if (name == 'chart.linewidth' && navigator.userAgent.match(/Chrome/)) {
- if (value == 1) {
- value = 1.01;
-
- } else if (RGraph.is_array(value)) {
- for (var i=0; i<value.length; ++i) {
- if (typeof(value[i]) == 'number' && value[i] == 1) {
- value[i] = 1.01;
- }
- }
- }
- }
-
-
- /**
- * Check for xaxispos
- */
- if (name == 'chart.xaxispos' ) {
- if (value != 'bottom' && value != 'center' && value != 'top') {
- alert('[LINE] (' + this.id + ') chart.xaxispos should be top, center or bottom. Tried to set it to: ' + value + ' Changing it to center');
- value = 'center';
- }
- }
-
-
- /**
- * chart.xticks is now called chart.numxticks
- */
- if (name == 'chart.xticks') {
- name = 'chart.numxticks';
- }
-
-
- /**
- * Change the new chart.spline option to chart.curvy
- */
- if (name == 'chart.spline') {
- name = 'chart.curvy';
- }
-
-
- /**
- * Chnge chart.ylabels.invert to chart.scale.invert
- */
- if (name == 'chart.ylabels.invert') {
- name = 'chart.scale.invert';
- }
-
-
- this.properties[name] = value;
-
- return this;
- };
- /**
- * An all encompassing accessor
- *
- * @param string name The name of the property
- */
- this.get =
- this.Get = function (name)
- {
- /**
- * This should be done first - prepend the property name with "chart." if necessary
- */
- if (name.substr(0,6) != 'chart.') {
- name = 'chart.' + name;
- }
-
- /**
- * If requested property is chart.spline - change it to chart.curvy
- */
- if (name == 'chart.spline') {
- name = 'chart.curvy';
- }
-
- return prop[name];
- };
- /**
- * The function you call to draw the line chart
- *
- * @param bool An optional bool used internally to ditinguish whether the
- * line chart is being called by the bar chart
- *
- * Draw()
- * |
- * +--Draw()
- * | |
- * | +-DrawLine()
- * |
- * +-RedrawLine()
- * |
- * +-DrawCurvyLine()
- * |
- * +-DrawSpline()
- */
- this.draw =
- this.Draw = function ()
- {
- // MUST be the first thing done!
- if (typeof(prop['chart.background.image']) == 'string') {
- RG.DrawBackgroundImage(this);
- }
-
-
- /**
- * Fire the onbeforedraw event
- */
- RG.FireCustomEvent(this, 'onbeforedraw');
- /**
- * Parse the colors. This allows for simple gradient syntax
- */
- if (!this.colorsParsed) {
-
- this.parseColors();
-
- // Don't want to do this again
- this.colorsParsed = true;
- }
-
-
-
- /**
- * This is new in May 2011 and facilitates indiviual gutter settings,
- * eg chart.gutter.left
- */
- this.gutterLeft = prop['chart.gutter.left'];
- this.gutterRight = prop['chart.gutter.right'];
- this.gutterTop = prop['chart.gutter.top'];
- this.gutterBottom = prop['chart.gutter.bottom'];
-
-
- /**
- * Check for Chrome 6 and shadow
- *
- * TODO Remove once it's been fixed (for a while)
- * 07/03/2014 - Removed
- * 29/10/2011 - Looks like it's been fixed as long the linewidth is at least 1.01
- * SEARCH TAGS: CHROME FIX SHADOW BUG
- */
- //if ( prop['chart.shadow']
- // && RG.ISCHROME
- // && prop['chart.linewidth'] <= 1
- // && prop['chart.chromefix']
- // && prop['chart.shadow.blur'] > 0) {
- // alert('[RGRAPH WARNING] Chrome has a shadow bug, meaning you should increase the linewidth to at least 1.01');
- //}
-
-
- // Reset the data back to that which was initially supplied
- this.data = RG.array_clone(this.original_data);
-
-
- // Reset the max value
- this.max = 0;
-
- /**
- * Reverse the datasets so that the data and the labels tally
- * COMMENTED OUT 15TH AUGUST 2011
- */
- //this.data = RG.array_reverse(this.data);
-
- if (prop['chart.filled'] && !prop['chart.filled.range'] && this.data.length > 1 && prop['chart.filled.accumulative']) {
-
- var accumulation = [];
-
- for (var set=0; set<this.data.length; ++set) {
- for (var point=0; point<this.data[set].length; ++point) {
- this.data[set][point] = Number(accumulation[point] ? accumulation[point] : 0) + this.data[set][point];
- accumulation[point] = this.data[set][point];
- }
- }
- }
-
- /**
- * Get the maximum Y scale value
- */
- if (prop['chart.ymax']) {
-
- this.max = prop['chart.ymax'];
- this.min = prop['chart.ymin'] ? prop['chart.ymin'] : 0;
-
- this.scale2 = RG.getScale2(this, {
- 'max':this.max,
- 'min':prop['chart.ymin'],
- 'strict':true,
- 'scale.thousand':prop['chart.scale.thousand'],
- 'scale.point':prop['chart.scale.point'],
- 'scale.decimals':prop['chart.scale.decimals'],
- 'ylabels.count':prop['chart.ylabels.count'],
- 'scale.round':prop['chart.scale.round'],
- 'units.pre': prop['chart.units.pre'],
- 'units.post': prop['chart.units.post']
- });
-
- this.max = this.scale2.max ? this.scale2.max : 0;
-
- // Check for negative values
- if (!prop['chart.outofbounds']) {
- for (dataset=0; dataset<this.data.length; ++dataset) {
- for (var datapoint=0; datapoint<this.data[dataset].length; datapoint++) {
-
- // Check for negative values
- this.hasnegativevalues = (this.data[dataset][datapoint] < 0) || this.hasnegativevalues;
- }
- }
- }
-
- } else {
-
- this.min = prop['chart.ymin'] ? prop['chart.ymin'] : 0;
-
- // Work out the max Y value
- for (dataset=0; dataset<this.data.length; ++dataset) {
- for (var datapoint=0; datapoint<this.data[dataset].length; datapoint++) {
-
- this.max = Math.max(this.max, this.data[dataset][datapoint] ? Math.abs(parseFloat(this.data[dataset][datapoint])) : 0);
-
- // Check for negative values
- if (!prop['chart.outofbounds']) {
- this.hasnegativevalues = (this.data[dataset][datapoint] < 0) || this.hasnegativevalues;
- }
- }
- }
- this.scale2 = RG.getScale2(this, {
- 'max':this.max,
- 'min':prop['chart.ymin'],
- 'scale.thousand':prop['chart.scale.thousand'],
- 'scale.point':prop['chart.scale.point'],
- 'scale.decimals':prop['chart.scale.decimals'],
- 'ylabels.count':prop['chart.ylabels.count'],
- 'scale.round':prop['chart.scale.round'],
- 'units.pre': prop['chart.units.pre'],
- 'units.post': prop['chart.units.post']
- });
-
- this.max = this.scale2.max ? this.scale2.max : 0;
- }
-
- /**
- * Setup the context menu if required
- */
- if (prop['chart.contextmenu']) {
- RG.ShowContext(this);
- }
-
- /**
- * Reset the coords arrays otherwise it will keep growing
- */
- this.coords = [];
- this.coordsText = [];
-
- /**
- * Work out a few things. They need to be here because they depend on things you can change before you
- * call Draw() but after you instantiate the object
- */
- this.grapharea = ca.height - this.gutterTop - this.gutterBottom;
- this.halfgrapharea = this.grapharea / 2;
- this.halfTextHeight = prop['chart.text.size'] / 2;
-
- // Check the combination of the X axis position and if there any negative values
- //
- // 19th Dec 2010 - removed for Opera since it can be reported incorrectly whn there
- // are multiple graphs on the page
- if (prop['chart.xaxispos'] == 'bottom' && this.hasnegativevalues && !RG.ISOPERA) {
- alert('[LINE] You have negative values and the X axis is at the bottom. This is not good...');
- }
-
- if (prop['chart.variant'] == '3d') {
- RG.Draw3DAxes(this);
- }
-
- // Progressively Draw the chart
- RG.background.Draw(this);
-
- /**
- * Draw any horizontal bars that have been defined
- */
- if (prop['chart.background.hbars'] && prop['chart.background.hbars'].length > 0) {
- RG.DrawBars(this);
- }
-
- if (prop['chart.axesontop'] == false) {
- this.DrawAxes();
- }
-
- //if (typeof(shadowColor) == 'object') {
- // shadowColor = RG.array_reverse(RG.array_clone(prop['chart.shadow.color']]);
- //}
-
- /**
- * This facilitates the new Trace2 effect
- */
-
- co.save()
- co.beginPath();
- co.rect(0, 0, ca.width * prop['chart.animation.trace.clip'], ca.height);
- co.clip();
-
- for (var i=0, j=0, len=this.data.length; i<len; i++, j++) {
-
- co.beginPath();
-
- /**
- * Turn on the shadow if required
- */
- if (!prop['chart.filled']) {
- this.SetShadow(i);
- }
-
- /**
- * Draw the line
- */
-
- if (prop['chart.fillstyle']) {
- if (typeof(prop['chart.fillstyle']) == 'object' && prop['chart.fillstyle'][j]) {
- var fill = prop['chart.fillstyle'][j];
-
- } else if (typeof(prop['chart.fillstyle']) == 'object' && prop['chart.fillstyle'].toString().indexOf('Gradient') > 0) {
- var fill = prop['chart.fillstyle'];
-
- } else if (typeof(prop['chart.fillstyle']) == 'string') {
- var fill = prop['chart.fillstyle'];
-
- }
- } else if (prop['chart.filled']) {
- var fill = prop['chart.colors'][j];
-
- } else {
- var fill = null;
- }
-
- /**
- * Figure out the tickmark to use
- */
- if (prop['chart.tickmarks'] && typeof(prop['chart.tickmarks']) == 'object') {
- var tickmarks = prop['chart.tickmarks'][i];
- } else if (prop['chart.tickmarks'] && typeof(prop['chart.tickmarks']) == 'string') {
- var tickmarks = prop['chart.tickmarks'];
- } else if (prop['chart.tickmarks'] && typeof(prop['chart.tickmarks']) == 'function') {
- var tickmarks = prop['chart.tickmarks'];
- } else {
- var tickmarks = null;
- }
-
-
- this.DrawLine(this.data[i],
- prop['chart.colors'][j],
- fill,
- this.GetLineWidth(j),
- tickmarks,
- i);
-
- co.stroke();
- }
-
- /**
- * If the line is filled re-stroke the lines
- */
- if (prop['chart.filled'] && prop['chart.filled.accumulative'] && !prop['chart.curvy']) {
-
- for (var i=0; i<this.coords2.length; ++i) {
-
- co.beginPath();
- co.lineWidth = this.GetLineWidth(i);
- co.strokeStyle = prop['chart.colors'][i];
-
- for (var j=0,len=this.coords2[i].length; j<len; ++j) {
-
- if (j == 0 || this.coords2[i][j][1] == null || (this.coords2[i][j - 1] && this.coords2[i][j - 1][1] == null)) {
- co.moveTo(this.coords2[i][j][0], this.coords2[i][j][1]);
- } else {
- if (prop['chart.stepped']) {
- co.lineTo(this.coords2[i][j][0], this.coords2[i][j - 1][1]);
- }
- co.lineTo(this.coords2[i][j][0], this.coords2[i][j][1]);
- }
- }
-
- co.stroke();
- // No fill!
- }
-
- //Redraw the tickmarks
- if (prop['chart.tickmarks']) {
-
- co.beginPath();
-
- co.fillStyle = 'white';
-
- for (var i=0,len=this.coords2.length; i<len; ++i) {
-
- co.beginPath();
- co.strokeStyle = prop['chart.colors'][i];
-
- for (var j=0; j<this.coords2[i].length; ++j) {
- if (typeof(this.coords2[i][j]) == 'object' && typeof(this.coords2[i][j][0]) == 'number' && typeof(this.coords2[i][j][1]) == 'number') {
-
- var tickmarks = typeof(prop['chart.tickmarks']) == 'object' ? prop['chart.tickmarks'][i] : prop['chart.tickmarks'];
-
- this.DrawTick( this.coords2[i],
- this.coords2[i][j][0],
- this.coords2[i][j][1],
- co.strokeStyle,
- false,
- j == 0 ? 0 : this.coords2[i][j - 1][0],
- j == 0 ? 0 : this.coords2[i][j - 1][1],
- tickmarks,
- j);
- }
- }
- }
-
- co.stroke();
- co.fill();
- }
-
- } else if (prop['chart.filled'] && prop['chart.filled.accumulative'] && prop['chart.curvy']) {
- // Restroke the curvy filled accumulative lines
- for (var i=0; i<this.coordsSpline.length; i+=1) {
- co.beginPath();
- co.strokeStyle = prop['chart.colors'][i];
- co.lineWidth = this.GetLineWidth(i);
- for (var j=0,len=this.coordsSpline[i].length; j<len; j+=1) {
-
- var point = this.coordsSpline[i][j];
-
- j == 0 ? co.moveTo(point[0], point[1]) : co.lineTo(point[0], point[1]);
- }
- co.stroke();
- }
- for (var i=0,len=this.coords2.length; i<len; i+=1) {
- for (var j=0,len2=this.coords2[i].length; j<len2; ++j) {
- if (typeof(this.coords2[i][j]) == 'object' && typeof(this.coords2[i][j][0]) == 'number' && typeof(this.coords2[i][j][1]) == 'number') {
-
- var tickmarks = typeof prop['chart.tickmarks'] == 'object' && !RGraph.is_null(prop['chart.tickmarks']) ? prop['chart.tickmarks'][i] : prop['chart.tickmarks'];
- co.strokeStyle = prop['chart.colors'][i];
- this.DrawTick( this.coords2[i],
- this.coords2[i][j][0],
- this.coords2[i][j][1],
- prop['chart.colors'][i],
- false,
- j == 0 ? 0 : this.coords2[i][j - 1][0],
- j == 0 ? 0 : this.coords2[i][j - 1][1],
- tickmarks,
- j);
- }
- }
- }
- }
- co.restore();
-
- // ???
- co.beginPath();
-
-
-
-
- /**
- * If the axes have been requested to be on top, do that
- */
- if (prop['chart.axesontop']) {
- this.DrawAxes();
- }
-
- /**
- * Draw the labels
- */
- this.DrawLabels();
-
- /**
- * Draw the range if necessary
- */
- this.DrawRange();
-
- // Draw a key if necessary
- if (prop['chart.key'] && prop['chart.key'].length && RG.DrawKey) {
- RG.DrawKey(this, prop['chart.key'], prop['chart.colors']);
- }
-
- /**
- * Draw " above" labels if enabled
- */
- if (prop['chart.labels.above']) {
- this.DrawAboveLabels();
- }
-
- /**
- * Draw the "in graph" labels
- */
- RG.DrawInGraphLabels(this);
-
- /**
- * Redraw the lines if a filled range is on the cards
- */
- if (prop['chart.filled'] && prop['chart.filled.range'] && this.data.length == 2) {
-
- co.beginPath();
- var len = this.coords.length / 2;
- co.lineWidth = prop['chart.linewidth'];
- co.strokeStyle = prop['chart.colors'][0];
-
- for (var i=0; i<len; ++i) {
-
- if (!RG.is_null(this.coords[i][1])) {
- if (i == 0) {
- co.moveTo(this.coords[i][0], this.coords[i][1]);
- } else {
- co.lineTo(this.coords[i][0], this.coords[i][1]);
- }
- }
- }
-
- co.stroke();
-
-
- co.beginPath();
-
- if (prop['chart.colors'][1]) {
- co.strokeStyle = prop['chart.colors'][1];
- }
-
- for (var i=this.coords.length - 1; i>=len; --i) {
- if (!RG.is_null(this.coords[i][1])) {
- if (i == (this.coords.length - 1)) {
- co.moveTo(this.coords[i][0], this.coords[i][1]);
- } else {
- co.lineTo(this.coords[i][0], this.coords[i][1]);
- }
- }
- }
-
- co.stroke();
-
-
- } else if (prop['chart.filled'] && prop['chart.filled.range']) {
- alert('[LINE] You must have only two sets of data for a filled range chart');
- }
-
- /**
- * This function enables resizing
- */
- if (prop['chart.resizable']) {
- RG.AllowResizing(this);
- }
-
-
- /**
- * This installs the event listeners
- */
- RG.InstallEventListeners(this);
-
-
-
-
- /**
- * Fire the onfirstdraw event
- */
- if (this.firstDraw) {
- RG.fireCustomEvent(this, 'onfirstdraw');
- this.firstDrawFunc();
- this.firstDraw = false;
- }
- /**
- * Fire the RGraph ondraw event
- */
- RG.FireCustomEvent(this, 'ondraw');
-
- return this;
- };
- /**
- * Draws the axes
- */
- this.drawAxes =
- this.DrawAxes = function ()
- {
- //var RG = RGraph;
- //var ca = this.canvas;
- //var co = this.context;
- //var prop = this.properties;
-
- // Don't draw the axes?
- if (prop['chart.noaxes']) {
- return;
- }
-
- // Turn any shadow off
- RG.NoShadow(this);
-
- co.lineWidth = prop['chart.axis.linewidth'] + 0.001;
- co.lineCap = 'butt';
- co.strokeStyle = prop['chart.axis.color'];
- co.beginPath();
- // Draw the X axis
- if (prop['chart.noxaxis'] == false) {
- if (prop['chart.xaxispos'] == 'center') {
- co.moveTo(this.gutterLeft, Math.round((this.grapharea / 2) + this.gutterTop));
- co.lineTo(ca.width - this.gutterRight, Math.round((this.grapharea / 2) + this.gutterTop));
- } else if (prop['chart.xaxispos'] === 'top') {
- co.moveTo(this.gutterLeft, this.gutterTop);
- co.lineTo(ca.width - this.gutterRight, this.gutterTop);
- } else {
- co.moveTo(this.gutterLeft, ca.height - this.gutterBottom);
- co.lineTo(ca.width - this.gutterRight, ca.height - this.gutterBottom);
- }
- }
-
- // Draw the Y axis
- if (prop['chart.noyaxis'] == false) {
- if (prop['chart.yaxispos'] == 'left') {
- co.moveTo(this.gutterLeft, this.gutterTop);
- co.lineTo(this.gutterLeft, ca.height - this.gutterBottom);
- } else {
- co.moveTo(ca.width - this.gutterRight, this.gutterTop);
- co.lineTo(ca.width - this.gutterRight, ca.height - this.gutterBottom);
- }
- }
-
- /**
- * Draw the X tickmarks
- */
- if (prop['chart.noxaxis'] == false && prop['chart.numxticks'] > 0) {
-
- var xTickInterval = (ca.width - this.gutterLeft - this.gutterRight) / prop['chart.numxticks'];
-
-
- if (!xTickInterval || xTickInterval <= 0) {
- xTickInterval = (ca.width - this.gutterLeft - this.gutterRight) / (prop['chart.labels'] && prop['chart.labels'].length ? prop['chart.labels'].length - 1 : 10);
- }
-
- for (x=this.gutterLeft + (prop['chart.yaxispos'] == 'left' ? xTickInterval : 0); x<=(ca.width - this.gutterRight + 1 ); x+=xTickInterval) {
-
- if (prop['chart.yaxispos'] == 'right' && x >= (ca.width - this.gutterRight - 1) ) {
- break;
- }
-
- // If the last tick is not desired...
- if (prop['chart.noendxtick']) {
- if (prop['chart.yaxispos'] == 'left' && x >= (ca.width - this.gutterRight - 1)) {
- break;
- } else if (prop['chart.yaxispos'] == 'right' && x == this.gutterLeft) {
- continue;
- }
- }
-
- var yStart = prop['chart.xaxispos'] == 'center' ? (this.gutterTop + (this.grapharea / 2)) - 3 : ca.height - this.gutterBottom;
- var yEnd = prop['chart.xaxispos'] == 'center' ? yStart + 6 : ca.height - this.gutterBottom - (x % 60 == 0 ? prop['chart.largexticks'] * prop['chart.tickdirection'] : prop['chart.smallxticks'] * prop['chart.tickdirection']);
-
- if (prop['chart.xaxispos'] == 'center') {
- var yStart = Math.round((this.gutterTop + (this.grapharea / 2))) - 3;
- var yEnd = yStart + 6;
-
- } else if (prop['chart.xaxispos'] == 'bottom') {
- var yStart = ca.height - this.gutterBottom;
- var yEnd = ca.height - this.gutterBottom - (x % 60 == 0 ? prop['chart.largexticks'] * prop['chart.tickdirection'] : prop['chart.smallxticks'] * prop['chart.tickdirection']);
- yEnd += 0;
-
-
- } else if (prop['chart.xaxispos'] == 'top') {
- yStart = this.gutterTop - 3;
- yEnd = this.gutterTop;
- }
-
- co.moveTo(Math.round(x), yStart);
- co.lineTo(Math.round(x), yEnd);
- }
-
- // Draw an extra tickmark if there is no X axis, but there IS a Y axis
- } else if (prop['chart.noyaxis'] == false && prop['chart.numyticks'] > 0) {
- if (!prop['chart.noendytick']) {
- if (prop['chart.yaxispos'] == 'left') {
- co.moveTo(this.gutterLeft, Math.round(ca.height - this.gutterBottom));
- co.lineTo(this.gutterLeft - prop['chart.smallyticks'], Math.round(ca.height - this.gutterBottom));
- } else {
- co.moveTo(ca.width - this.gutterRight, Math.round(ca.height - this.gutterBottom));
- co.lineTo(ca.width - this.gutterRight + prop['chart.smallyticks'], Math.round(ca.height - this.gutterBottom));
- }
- }
- }
-
- /**
- * Draw the Y tickmarks
- */
- var numyticks = prop['chart.numyticks'];
-
- if (prop['chart.noyaxis'] == false && numyticks > 0) {
- var counter = 0;
- var adjustment = 0;
-
- if (prop['chart.yaxispos'] == 'right') {
- adjustment = (ca.width - this.gutterLeft - this.gutterRight);
- }
-
- // X axis at the center
- if (prop['chart.xaxispos'] == 'center') {
- var interval = (this.grapharea / numyticks);
- var lineto = (prop['chart.yaxispos'] == 'left' ? this.gutterLeft : ca.width - this.gutterRight + prop['chart.smallyticks']);
-
- // Draw the upper halves Y tick marks
- for (y=this.gutterTop; y<(this.grapharea / 2) + this.gutterTop; y+=interval) {
- if (y < (this.grapharea / 2) + this.gutterTop) {
- co.moveTo((prop['chart.yaxispos'] == 'left' ? this.gutterLeft - prop['chart.smallyticks'] : ca.width - this.gutterRight), Math.round(y));
- co.lineTo(lineto, Math.round(y));
- }
- }
-
- // Draw the lower halves Y tick marks
- for (y=this.gutterTop + (this.halfgrapharea) + interval; y <= this.grapharea + this.gutterTop; y+=interval) {
- co.moveTo((prop['chart.yaxispos'] == 'left' ? this.gutterLeft - prop['chart.smallyticks'] : ca.width - this.gutterRight), Math.round(y));
- co.lineTo(lineto, Math.round(y));
- }
-
- // X axis at the top
- } else if (prop['chart.xaxispos'] == 'top') {
- var interval = (this.grapharea / numyticks);
- var lineto = (prop['chart.yaxispos'] == 'left' ? this.gutterLeft : ca.width - this.gutterRight + prop['chart.smallyticks']);
-
- // Draw the Y tick marks
- for (y=this.gutterTop + interval; y <=this.grapharea + this.gutterTop; y+=interval) {
- co.moveTo((prop['chart.yaxispos'] == 'left' ? this.gutterLeft - prop['chart.smallyticks'] : ca.width - this.gutterRight), Math.round(y));
- co.lineTo(lineto, Math.round(y));
- }
-
- // If there's no X axis draw an extra tick
- if (prop['chart.noxaxis'] && prop['chart.noendytick'] == false) {
- co.moveTo((prop['chart.yaxispos'] == 'left' ? this.gutterLeft - prop['chart.smallyticks'] : ca.width - this.gutterRight), this.gutterTop);
- co.lineTo(lineto, this.gutterTop);
- }
-
- // X axis at the bottom
- } else {
-
- var lineto = (prop['chart.yaxispos'] == 'left' ? this.gutterLeft - prop['chart.smallyticks'] : ca.width - this.gutterRight + prop['chart.smallyticks']);
-
- for (y=this.gutterTop; y<(ca.height - this.gutterBottom) && counter < numyticks; y+=( (ca.height - this.gutterTop - this.gutterBottom) / numyticks) ) {
-
- co.moveTo(this.gutterLeft + adjustment, Math.round(y));
- co.lineTo(lineto, Math.round(y));
-
- var counter = counter + 1;
- }
- }
-
- // Draw an extra X tickmark
- } else if (prop['chart.noxaxis'] == false && prop['chart.numxticks'] > 0) {
-
- if (prop['chart.yaxispos'] == 'left') {
- co.moveTo(this.gutterLeft, prop['chart.xaxispos'] == 'top' ? this.gutterTop : ca.height - this.gutterBottom);
- co.lineTo(this.gutterLeft, prop['chart.xaxispos'] == 'top' ? this.gutterTop - prop['chart.smallxticks'] : ca.height - this.gutterBottom + prop['chart.smallxticks']);
- } else {
- co.moveTo(ca.width - this.gutterRight, ca.height - this.gutterBottom);
- co.lineTo(ca.width - this.gutterRight, ca.height - this.gutterBottom + prop['chart.smallxticks']);
- }
- }
-
- co.stroke();
- };
- /**
- * Draw the text labels for the axes
- */
- this.drawLabels =
- this.DrawLabels = function ()
- {
- co.strokeStyle = 'black';
- co.fillStyle = prop['chart.text.color'];
- co.lineWidth = 1;
-
- // Turn off any shadow
- RG.NoShadow(this);
-
- // This needs to be here
- var font = prop['chart.text.font'];
- var text_size = prop['chart.text.size'];
- var decimals = prop['chart.scale.decimals'];
- var context = co;
- var canvas = ca;
- var ymin = prop['chart.ymin'];
-
- // Draw the Y axis labels
- if (prop['chart.ylabels'] && prop['chart.ylabels.specific'] == null) {
-
- var units_pre = prop['chart.units.pre'];
- var units_post = prop['chart.units.post'];
- var xpos = prop['chart.yaxispos'] == 'left' ? this.gutterLeft - 5 : ca.width - this.gutterRight + 5;
- var align = prop['chart.yaxispos'] == 'left' ? 'right' : 'left';
- var numYLabels = this.scale2.labels.length;
- var bounding = false;
- var bgcolor = prop['chart.ylabels.inside'] ? prop['chart.ylabels.inside.color'] : null;
-
-
- /**
- * If the Y labels are inside the Y axis, invert the alignment
- */
- if (prop['chart.ylabels.inside'] == true && align == 'left') {
- xpos -= 10;
- align = 'right';
- bounding = true;
-
-
- } else if (prop['chart.ylabels.inside'] == true && align == 'right') {
- xpos += 10;
- align = 'left';
- bounding = true;
- }
-
-
-
-
- /**
- * X axis in the center
- */
- if (prop['chart.xaxispos'] == 'center') {
-
- var half = this.grapharea / 2;
-
- /**
- * Draw the top half
- */
- for (var i=0; i<this.scale2.labels.length; ++i) {
- RG.Text2(this, {'font': font,
- 'size': text_size,
- 'x': xpos,
- 'y': this.gutterTop + half - (((i+1)/numYLabels) * half),
- 'valign': 'center',
- 'halign':align,
- 'bounding': bounding,
- 'boundingFill': bgcolor,
- 'text': this.scale2.labels[i],
- 'tag': 'scale'
- });
- }
-
- /**
- * Draw the bottom half
- */
- for (var i=0; i<this.scale2.labels.length; ++i) {
- RG.Text2(this, {'font': font,
- 'size': text_size,
- 'x': xpos,
- 'y': this.gutterTop + half + (((i+1)/numYLabels) * half),
- 'valign': 'center',
- 'halign':align,
- 'bounding': bounding,
- 'boundingFill': bgcolor,
- 'text': '-' + this.scale2.labels[i],
- 'tag': 'scale'
- });
- }
-
- // No X axis - so draw 0
- if (prop['chart.noxaxis'] == true || ymin != 0 || prop['chart.scale.zerostart']) {
- RG.Text2(this,{'font':font,
- 'size':text_size,
- 'x':xpos,
- 'y':this.gutterTop + half,
- 'text':prop['chart.units.pre'] + ymin.toFixed(decimals) + prop['chart.units.post'],
- 'bounding':bounding,
- 'boundingFill':bgcolor,
- 'valign':'center',
- 'halign':align,
- 'tag': 'scale'
- });
- }
-
-
-
- /**
- * X axis at the top
- */
- } else if (prop['chart.xaxispos'] == 'top') {
-
- var half = this.grapharea / 2;
-
- if (prop['chart.scale.invert']) {
-
- for (var i=0; i<this.scale2.labels.length; ++i) {
-
- RG.Text2(this, {'font': font,
- 'size': text_size,
- 'x': xpos,
- 'y': this.gutterTop + ((i/this.scale2.labels.length) * this.grapharea),
- 'valign': 'center',
- 'halign':align,
- 'bounding': bounding,
- 'boundingFill': bgcolor,
- 'text': '-' + this.scale2.labels[this.scale2.labels.length - (i+1)],
- 'tag': 'scale'
- });
- }
- } else {
- for (var i=0; i<this.scale2.labels.length; ++i) {
- RG.Text2(this, {'font': font,
- 'size': text_size,
- 'x': xpos,
- 'y': this.gutterTop + (((i+1)/numYLabels) * this.grapharea),
- 'valign': 'center',
- 'halign':align,
- 'bounding': bounding,
- 'boundingFill': bgcolor,
- 'text': '-' + this.scale2.labels[i],
- 'tag': 'scale'
- });
- }
- }
-
- // Draw the lower limit if chart.ymin is specified
- if ((prop['chart.ymin'] != 0 || prop['chart.noxaxis']) || prop['chart.scale.invert'] || prop['chart.scale.zerostart']) {
- RG.Text2(this, {'font':font,
- 'size':text_size,
- 'x':xpos,
- 'y': prop['chart.scale.invert'] ? ca.height - this.gutterBottom : this.gutterTop,
- 'text': (prop['chart.ymin'] != 0 ? '-' : '') + RG.number_format(this, prop['chart.ymin'].toFixed(decimals), units_pre, units_post),
- 'valign':'center',
- 'halign': align,
- 'bounding':bounding,
- 'boundingFill':bgcolor,
- 'tag': 'scale'});
- }
-
-
-
-
-
-
- /**
- * X axis labels at the bottom
- */
- } else {
-
- if (prop['chart.scale.invert']) {
-
- // Draw the minimum value
- RG.Text2(this, {'font': font,
- 'size': text_size,
- 'x': xpos,
- 'y': this.gutterTop,
- 'valign': 'center',
- 'halign':align,
- 'bounding': bounding,
- 'boundingFill': bgcolor,
- 'text': RG.number_format(this, this.min.toFixed(prop['chart.scale.decimals']), units_pre, units_post),
- 'tag': 'scale'
- });
-
- for (var i=0,len=this.scale2.labels.length; i<len; ++i) {
- RG.Text2(this, {'font': font,
- 'size': text_size,
- 'x': xpos,
- 'y': this.gutterTop + (((i+1)/this.scale2.labels.length) * this.grapharea),
- 'valign': 'center',
- 'halign':align,
- 'bounding': bounding,
- 'boundingFill': bgcolor,
- 'text': this.scale2.labels[i],
- 'tag': 'scale'
- });
- }
- } else {
- for (var i=0,len=this.scale2.labels.length; i<len; ++i) {
- RG.Text2(this, {'font': font,
- 'size': text_size,
- 'x': xpos,
- 'y': this.gutterTop + ((i/this.scale2.labels.length) * this.grapharea),
- 'valign': 'center',
- 'halign':align,
- 'bounding': bounding,
- 'boundingFill': bgcolor,
- 'text': this.scale2.labels[this.scale2.labels.length - (i + 1)],
- 'tag': 'scale'
- });
- }
- }
-
- // Draw the lower limit if chart.ymin is specified
- if ( (prop['chart.ymin']!= 0 && !prop['chart.scale.invert'] || prop['chart.scale.zerostart'])
- || prop['chart.noxaxis']
- ) {
- RG.Text2(this, {'font':font,
- 'size':text_size,
- 'x':xpos,
- 'y':prop['chart.scale.invert'] ? this.gutterTop : ca.height - this.gutterBottom,
- 'text':RG.number_format(this, prop['chart.ymin'].toFixed(prop['chart.scale.decimals']), units_pre, units_post),
- 'valign':'center',
- 'halign':align,
- 'bounding':bounding,
- 'boundingFill':bgcolor,
- 'tag': 'scale'
- });
- }
- }
-
-
-
-
-
-
-
- // No X axis - so draw 0 - but not if the X axis is in the center
- if ( prop['chart.noxaxis'] == true
- && prop['chart.ymin'] == null
- && prop['chart.xaxispos'] != 'center'
- && prop['chart.noendytick'] == false
- ) {
-
- RG.Text2(this, {'font':font,
- 'size':text_size,
- 'x':xpos,
- 'y':prop['chart.xaxispos'] == 'top' ? this.gutterTop : (ca.height - this.gutterBottom),'text': prop['chart.units.pre'] + (0).toFixed(prop['chart.scale.decimals']) + prop['chart.units.post'],
- 'valign':'center',
- 'halign':align,
- 'bounding':bounding,
- 'boundingFill':bgcolor,
- 'tag':'scale'
- });
- }
-
- } else if (prop['chart.ylabels'] && typeof(prop['chart.ylabels.specific']) == 'object') {
-
- // A few things
- var gap = this.grapharea / prop['chart.ylabels.specific'].length;
- var halign = prop['chart.yaxispos'] == 'left' ? 'right' : 'left';
- var bounding = false;
- var bgcolor = null;
- var ymin = prop['chart.ymin'] != null && prop['chart.ymin'];
-
- // Figure out the X coord based on the position of the axis
- if (prop['chart.yaxispos'] == 'left') {
- var x = this.gutterLeft - 5;
-
- if (prop['chart.ylabels.inside']) {
- x += 10;
- halign = 'left';
- bounding = true;
- bgcolor = 'rgba(255,255,255,0.5)';
- }
-
- } else if (prop['chart.yaxispos'] == 'right') {
- var x = ca.width - this.gutterRight + 5;
-
- if (prop['chart.ylabels.inside']) {
- x -= 10;
- halign = 'right';
- bounding = true;
- bgcolor = 'rgba(255,255,255,0.5)';
- }
- }
-
-
- // Draw the labels
- if (prop['chart.xaxispos'] == 'center') {
-
- // Draw the top halfs labels
- for (var i=0; i<prop['chart.ylabels.specific'].length; ++i) {
-
- var y = this.gutterTop + (this.grapharea / (((prop['chart.ylabels.specific'].length - 1)) * 2) * i);
-
- if (ymin && ymin > 0) {
- var y = ((this.grapharea / 2) / (prop['chart.ylabels.specific'].length - (ymin ? 1 : 0)) ) * i;
- y += this.gutterTop;
- }
-
- RG.Text2(this, {'font':font,
- 'size':text_size,
- 'x':x,
- 'y':y,
- 'text':String(prop['chart.ylabels.specific'][i]),
- 'valign': 'center',
- 'halign':halign,
- 'bounding':bounding,
- 'boundingFill':bgcolor,
- 'tag': 'ylabels.specific'
- });
- }
-
- // Now reverse the labels and draw the bottom half
- var reversed_labels = RG.array_reverse(prop['chart.ylabels.specific']);
-
- // Draw the bottom halfs labels
- for (var i=0; i<reversed_labels.length; ++i) {
-
- var y = (this.grapharea / 2) + this.gutterTop + ((this.grapharea / ((reversed_labels.length - 1) * 2) ) * i);
-
- RG.Text2(this, {'font':font,
- 'size':text_size,
- 'x':x,
- 'y':y,
- 'text':i == 0 ? '' : String(reversed_labels[i]),
- 'valign': 'center',
- 'halign':halign,
- 'bounding':bounding,
- 'boundingFill':bgcolor,
- 'tag': 'ylabels.specific'
- });
- }
-
- } else if (prop['chart.xaxispos'] == 'top') {
-
- // Reverse the labels and draw
- var reversed_labels = RG.array_reverse(prop['chart.ylabels.specific']);
-
- // Draw the bottom halfs labels
- for (var i=0; i<reversed_labels.length; ++i) {
-
- var y = (this.grapharea / (reversed_labels.length - 1)) * i;
- y = y + this.gutterTop;
-
- RG.Text2(this, {'font':font,
- 'size':text_size,
- 'x':x,
- 'y':y,
- 'text':String(reversed_labels[i]),
- 'valign': 'center',
- 'halign':halign,
- 'bounding':bounding,
- 'boundingFill':bgcolor,
- 'tag': 'ylabels.specific'
- });
- }
-
- } else {
- for (var i=0; i<prop['chart.ylabels.specific'].length; ++i) {
- var y = this.gutterTop + ((this.grapharea / (prop['chart.ylabels.specific'].length - 1)) * i);
- RG.Text2(this, {'font':font,
- 'size':text_size,
- 'x':x,
- 'y':y,
- 'text':String(prop['chart.ylabels.specific'][i]),
- 'valign':'center',
- 'halign':halign,
- 'bounding':bounding,
- 'boundingFill':bgcolor,
- 'tag': 'ylabels.specific'
- });
- }
- }
- }
-
- // Draw the X axis labels
- if (prop['chart.labels'] && prop['chart.labels'].length > 0) {
-
- var yOffset = 5;
- var bordered = false;
- var bgcolor = null;
-
- /**
- * Text angle
- */
- var angle = 0;
- var valign = 'top';
- var halign = 'center';
-
- if (prop['chart.xlabels.inside']) {
- yOffset = -5;
- bordered = true;
- bgcolor = prop['chart.xlabels.inside.color'];
- valign = 'bottom';
- }
-
- if (prop['chart.xaxispos'] == 'top') {
- valign = 'bottom';
- yOffset += 2;
- }
-
- if (typeof(prop['chart.text.angle']) == 'number' && prop['chart.text.angle'] > 0) {
- angle = -1 * prop['chart.text.angle'];
- valign = 'center';
- halign = 'right';
- yOffset = 10;
-
- if (prop['chart.xaxispos'] == 'top') {
- yOffset = 10;
- }
- }
-
- co.fillStyle = prop['chart.text.color'];
- var numLabels = prop['chart.labels'].length;
-
- for (i=0; i<numLabels; ++i) {
-
- // Changed 8th Nov 2010 to be not reliant on the coords
- //if (this.properties['chart.labels'][i] && this.coords && this.coords[i] && this.coords[i][0]) {
- if (prop['chart.labels'][i]) {
-
- var labelX = ((ca.width - this.gutterLeft - this.gutterRight - (2 * prop['chart.hmargin'])) / (numLabels - 1) ) * i;
- labelX += this.gutterLeft + prop['chart.hmargin'];
-
- /**
- * Account for an unrelated number of labels
- */
- if (prop['chart.labels'].length != this.data[0].length) {
- labelX = this.gutterLeft + prop['chart.hmargin'] + ((ca.width - this.gutterLeft - this.gutterRight - (2 * prop['chart.hmargin'])) * (i / (prop['chart.labels'].length - 1)));
- }
-
- // This accounts for there only being one point on the chart
- if (!labelX) {
- labelX = this.gutterLeft + prop['chart.hmargin'];
- }
-
- if (prop['chart.xaxispos'] == 'top' && prop['chart.text.angle'] > 0) {
- halign = 'left';
- }
-
- if (prop['chart.text.angle'] != 0) {
- halign = 'right';
- }
-
- RG.Text2(this, {'font':font,
- 'size':text_size,
- 'x':labelX,
- 'y':(prop['chart.xaxispos'] == 'top') ? this.gutterTop - yOffset - (prop['chart.xlabels.inside'] ? -22 : 0) : (ca.height - this.gutterBottom) + yOffset,
- 'text':String(prop['chart.labels'][i]),
- 'valign':valign,
- 'halign':halign,
- 'bounding':bordered,
- 'boundingFill':bgcolor,
- 'angle':angle,
- 'tag': 'labels'
- });
- }
- }
-
- }
-
- co.stroke();
- co.fill();
- }
-
-
-
- /**
- * Draws the line
- */
- this.drawLine =
- this.DrawLine = function (lineData, color, fill, linewidth, tickmarks, index)
- {
- // This facilitates the Rise animation (the Y value only)
- if (prop['chart.animation.unfold.y'] && prop['chart.animation.factor'] != 1) {
- for (var i=0; i<lineData.length; ++i) {
- lineData[i] *= prop['chart.animation.factor'];
- }
- }
-
- var penUp = false;
- var yPos = null;
- var xPos = 0;
- co.lineWidth = 1;
- var lineCoords = [];
-
- /**
- * Get the previous line data
- */
- if (index > 0) {
- var prevLineCoords = this.coords2[index - 1];
- }
-
- // Work out the X interval
- var xInterval = (ca.width - (2 * prop['chart.hmargin']) - this.gutterLeft - this.gutterRight) / (lineData.length - 1);
-
- // Loop thru each value given, plotting the line
- // (FORMERLY FIRST)
- for (i=0,len=lineData.length; i<len; i+=1) {
- var data_point = lineData[i];
-
- /**
- * Get the yPos for the given data point
- */
- var yPos = this.getYCoord(data_point);
- // Null data points, and a special case for this bug:http://dev.rgraph.net/tests/ymin.html
- if ( lineData[i] == null
- || (prop['chart.xaxispos'] == 'bottom' && lineData[i] < this.min && !prop['chart.outofbounds'])
- || (prop['chart.xaxispos'] == 'center' && lineData[i] < (-1 * this.max) && !prop['chart.outofbounds'])
- || (((lineData[i] < this.min && prop['chart.xaxispos'] !== 'center') || lineData[i] > this.max) && !prop['chart.outofbounds'])) {
-
- yPos = null;
- }
- // Not always very noticeable, but it does have an effect
- // with thick lines
- co.lineCap = 'round';
- co.lineJoin = 'round';
-
- // Plot the line if we're at least on the second iteration
- if (i > 0) {
- xPos = xPos + xInterval;
- } else {
- xPos = prop['chart.hmargin'] + this.gutterLeft;
- }
-
- if (prop['chart.animation.unfold.x']) {
- xPos *= prop['chart.animation.factor'];
-
- if (xPos < prop['chart.gutter.left']) {
- xPos = prop['chart.gutter.left'];
- }
- }
-
- /**
- * Add the coords to an array
- */
- this.coords.push([xPos, yPos]);
- lineCoords.push([xPos, yPos]);
- }
- co.stroke();
- // Store the coords in another format, indexed by line number
- this.coords2[index] = lineCoords;
-
- /**
- * For IE only: Draw the shadow ourselves as ExCanvas doesn't produce shadows
- */
- if (RG.ISOLD && prop['chart.shadow']) {
- this.DrawIEShadow(lineCoords, co.shadowColor);
- }
- /**
- * Now draw the actual line [FORMERLY SECOND]
- */
- co.beginPath();
- // Transparent now as of 11/19/2011
- co.strokeStyle = 'rgba(0,0,0,0)';
- //co.strokeStyle = fill;
- if (fill) {
- co.fillStyle = fill;
- }
- var isStepped = prop['chart.stepped'];
- var isFilled = prop['chart.filled'];
-
- if (prop['chart.xaxispos'] == 'top') {
- var xAxisPos = this.gutterTop;
- } else if (prop['chart.xaxispos'] == 'center') {
- var xAxisPos = this.gutterTop + (this.grapharea / 2);
- } else if (prop['chart.xaxispos'] == 'bottom') {
- var xAxisPos = ca.height - this.gutterBottom;
- }
- for (var i=0,len=lineCoords.length; i<len; i+=1) {
-
- xPos = lineCoords[i][0];
- yPos = lineCoords[i][1];
- var set = index;
-
- var prevY = (lineCoords[i - 1] ? lineCoords[i - 1][1] : null);
- var isLast = (i + 1) == lineCoords.length;
-
- /**
- * This nullifys values which are out-of-range
- */
- if (!prop['chart.outofbounds'] && (prevY < this.gutterTop || prevY > (ca.height - this.gutterBottom) ) ) {
- penUp = true;
- }
-
- if (i == 0 || penUp || !yPos || !prevY || prevY < this.gutterTop) {
- if (prop['chart.filled'] && !prop['chart.filled.range']) {
-
- if (!prop['chart.outofbounds'] || prevY === null || yPos === null) {
- co.moveTo(xPos + 1, xAxisPos);
- }
- // This facilitates the X axis being at the top
- // NOTE: Also done below
- if (prop['chart.xaxispos'] == 'top') {
- co.moveTo(xPos + 1, xAxisPos);
- }
-
- if (isStepped && i > 0) {
- co.lineTo(xPos, lineCoords[i - 1][1]);
- }
-
- co.lineTo(xPos, yPos);
-
- } else {
-
- if (RG.ISOLD && yPos == null) {
- // Nada
- } else {
- co.moveTo(xPos + 1, yPos);
- }
- }
-
- if (yPos == null) {
- penUp = true;
-
- } else {
- penUp = false;
- }
-
- } else {
-
- // Draw the stepped part of stepped lines
- if (isStepped) {
- co.lineTo(xPos, lineCoords[i - 1][1]);
- }
-
- if ((yPos >= this.gutterTop && yPos <= (ca.height - this.gutterBottom)) || prop['chart.outofbounds'] ) {
-
- if (isLast && prop['chart.filled'] && !prop['chart.filled.range'] && prop['chart.yaxispos'] == 'right') {
- xPos -= 1;
- }
-
-
- // Added 8th September 2009
- if (!isStepped || !isLast) {
- co.lineTo(xPos, yPos);
-
- if (isFilled && lineCoords[i+1] && lineCoords[i+1][1] == null) {
- co.lineTo(xPos, xAxisPos);
- }
-
- // Added August 2010
- } else if (isStepped && isLast) {
- co.lineTo(xPos,yPos);
- }
-
-
- penUp = false;
- } else {
- penUp = true;
- }
- }
- }
- /**
- * Draw a line to the X axis if the chart is filled
- */
- if (prop['chart.filled'] && !prop['chart.filled.range'] && !prop['chart.curvy']) {
-
- // Is this needed ??
- var fillStyle = prop['chart.fillstyle'];
- /**
- * Draw the bottom edge of the filled bit using either the X axis or the prevlinedata,
- * depending on the index of the line. The first line uses the X axis, and subsequent
- * lines use the prevLineCoords array
- */
- if (index > 0 && prop['chart.filled.accumulative']) {
-
- co.lineTo(xPos, prevLineCoords ? prevLineCoords[i - 1][1] : (ca.height - this.gutterBottom - 1 + (prop['chart.xaxispos'] == 'center' ? (ca.height - this.gutterTop - this.gutterBottom) / 2 : 0)));
- for (var k=(i - 1); k>=0; --k) {
- co.lineTo(k == 0 ? prevLineCoords[k][0] + 1: prevLineCoords[k][0], prevLineCoords[k][1]);
- }
- } else {
- // Draw a line down to the X axis
- if (prop['chart.xaxispos'] == 'top') {
- co.lineTo(xPos, prop['chart.gutter.top'] + 1);
- co.lineTo(lineCoords[0][0],prop['chart.gutter.top'] + 1);
- } else if (typeof(lineCoords[i - 1][1]) == 'number') {
-
- var yPosition = prop['chart.xaxispos'] == 'center' ? ((ca.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop : ca.height - this.gutterBottom;
-
- co.lineTo(xPos,yPosition);
- co.lineTo(lineCoords[0][0],yPosition);
- }
- }
-
- co.fillStyle = fill;
-
- co.fill();
- co.beginPath();
- }
-
- /**
- * FIXME this may need removing when Chrome is fixed
- * SEARCH TAGS: CHROME SHADOW BUG
- */
- //if (false && RGraph.ISCHROME && prop['chart.shadow'] && prop['chart.chromefix'] && prop['chart.shadow.blur'] > 0) {
- //
- // for (var i=lineCoords.length - 1; i>=0; --i) {
- // if (
- // typeof(lineCoords[i][1]) != 'number'
- // || (typeof(lineCoords[i+1]) == 'object' && typeof(lineCoords[i+1][1]) != 'number')
- // ) {
- // co.moveTo(lineCoords[i][0],lineCoords[i][1]);
- // } else {
- // co.lineTo(lineCoords[i][0],lineCoords[i][1]);
- // }
- // }
- //}
-
- co.stroke();
-
-
- if (prop['chart.backdrop']) {
- this.DrawBackdrop(lineCoords, color);
- }
-
-
-
-
- /**
- * TODO CLIP TRACE
- * By using the clip() method the Trace animation can be updated.
- * NOTE: Needs to be done for the filled part as well
- */
- co.save();
- co.beginPath();
- co.rect(0,0,ca.width * prop['chart.animation.trace.clip'],ca.height);
- co.clip();
-
- // Now redraw the lines with the correct line width
- this.SetShadow(index);
- this.RedrawLine(lineCoords, color, linewidth, index);
- co.stroke();
- RG.NoShadow(this);
-
-
-
-
- // Draw the tickmarks
- for (var i=0; i<lineCoords.length; ++i) {
-
- i = Number(i);
-
- /**
- * Set the color
- */
- co.strokeStyle = color;
-
-
- if (isStepped && i == (lineCoords.length - 1)) {
- co.beginPath();
- //continue;
- }
-
- if (
- (
- tickmarks != 'endcircle'
- && tickmarks != 'endsquare'
- && tickmarks != 'filledendsquare'
- && tickmarks != 'endtick'
- && tickmarks != 'endtriangle'
- && tickmarks != 'arrow'
- && tickmarks != 'filledarrow'
- )
- || (i == 0 && tickmarks != 'arrow' && tickmarks != 'filledarrow')
- || i == (lineCoords.length - 1)
- ) {
-
- var prevX = (i <= 0 ? null : lineCoords[i - 1][0]);
- var prevY = (i <= 0 ? null : lineCoords[i - 1][1]);
-
- this.DrawTick(lineData, lineCoords[i][0], lineCoords[i][1], color, false, prevX, prevY, tickmarks, i);
-
- // Draws tickmarks on the stepped bits of stepped charts. Takend out 14th July 2010
- //
- //if (this.properties['chart.stepped'] && lineCoords[i + 1] && this.properties['chart.tickmarks'] != 'endsquare' && this.properties['chart.tickmarks'] != 'endcircle' && this.properties['chart.tickmarks'] != 'endtick') {
- // this.DrawTick(lineCoords[i + 1][0], lineCoords[i][1], color);
- //}
- }
- }
-
- co.restore();
-
- // Draw something off canvas to skirt an annoying bug
- co.beginPath();
- co.arc(ca.width + 50000, ca.height + 50000, 2, 0, 6.38, 1);
- };
- /**
- * This functions draws a tick mark on the line
- *
- * @param xPos int The x position of the tickmark
- * @param yPos int The y position of the tickmark
- * @param color str The color of the tickmark
- * @param bool Whether the tick is a shadow. If it is, it gets offset by the shadow offset
- */
- this.drawTick =
- this.DrawTick = function (lineData, xPos, yPos, color, isShadow, prevX, prevY, tickmarks, index)
- {
- // Various conditions mean no tick
- if (!prop['chart.line.visible']) {
- return;
- } else if (RG.is_null(yPos)) {
- return false;
- } else if ((yPos > (ca.height - this.gutterBottom)) && !prop['chart.outofbounds']) {
- return;
- } else if ((yPos < this.gutterTop) && !prop['chart.outofbounds']) {
- return;
- }
- co.beginPath();
-
- var offset = 0;
-
- // Reset the stroke and lineWidth back to the same as what they were when the line was drawm
- // UPDATE 28th July 2011 - the line width is now set to 1
- co.lineWidth = prop['chart.tickmarks.linewidth'] ? prop['chart.tickmarks.linewidth'] : prop['chart.linewidth'];
- co.strokeStyle = isShadow ? prop['chart.shadow.color'] : co.strokeStyle;
- co.fillStyle = isShadow ? prop['chart.shadow.color'] : co.strokeStyle;
-
- // Cicular tick marks
- if ( tickmarks == 'circle'
- || tickmarks == 'filledcircle'
- || tickmarks == 'endcircle') {
-
- if (tickmarks == 'circle'|| tickmarks == 'filledcircle' || (tickmarks == 'endcircle' && (index == 0 || index == (lineData.length - 1)))) {
- co.beginPath();
- co.arc(xPos + offset, yPos + offset, prop['chart.ticksize'], 0, 360 / (180 / RG.PI), false);
-
- if (tickmarks == 'filledcircle') {
- co.fillStyle = isShadow ? prop['chart.shadow.color'] : co.strokeStyle;
- } else {
- co.fillStyle = isShadow ? prop['chart.shadow.color'] : 'white';
- }
-
- co.stroke();
- co.fill();
- }
-
- // Halfheight "Line" style tick marks
- } else if (tickmarks == 'halftick') {
- co.beginPath();
- co.moveTo(Math.round(xPos), yPos);
- co.lineTo(Math.round(xPos), yPos + prop['chart.ticksize']);
-
- co.stroke();
-
- // Tick style tickmarks
- } else if (tickmarks == 'tick') {
- co.beginPath();
- co.moveTo(Math.round(xPos), yPos - prop['chart.ticksize']);
- co.lineTo(Math.round(xPos), yPos + prop['chart.ticksize']);
-
- co.stroke();
-
- // Endtick style tickmarks
- } else if (tickmarks == 'endtick' && (index == 0 || index == (lineData.length - 1))) {
- co.beginPath();
- co.moveTo(Math.round(xPos), yPos - prop['chart.ticksize']);
- co.lineTo(Math.round(xPos), yPos + prop['chart.ticksize']);
-
- co.stroke();
-
- // "Cross" style tick marks
- } else if (tickmarks == 'cross') {
- co.beginPath();
-
- var ticksize = prop['chart.ticksize'];
-
- co.moveTo(xPos - ticksize, yPos - ticksize);
- co.lineTo(xPos + ticksize, yPos + ticksize);
- co.moveTo(xPos + ticksize, yPos - ticksize);
- co.lineTo(xPos - ticksize, yPos + ticksize);
- co.stroke();
-
-
- // Triangle style tick marks
- } else if (tickmarks == 'triangle' || tickmarks == 'filledtriangle' || (tickmarks == 'endtriangle' && (index == 0 || index == (lineData.length - 1)))) {
- co.beginPath();
-
- if (tickmarks == 'filledtriangle') {
- co.fillStyle = isShadow ? prop['chart.shadow.color'] : co.strokeStyle;
- } else {
- co.fillStyle = 'white';
- }
-
- co.moveTo(Math.round(xPos - prop['chart.ticksize']), yPos + prop['chart.ticksize']);
- co.lineTo(Math.round(xPos), yPos - prop['chart.ticksize']);
- co.lineTo(Math.round(xPos + prop['chart.ticksize']), yPos + prop['chart.ticksize']);
- co.closePath();
-
- co.stroke();
- co.fill();
-
-
- // A white bordered circle
- } else if (tickmarks == 'borderedcircle' || tickmarks == 'dot') {
- co.lineWidth = 1;
- co.strokeStyle = prop['chart.tickmarks.dot.color'];
- co.fillStyle = prop['chart.tickmarks.dot.color'];
-
- // The outer white circle
- co.beginPath();
- co.arc(xPos, yPos, prop['chart.ticksize'], 0, 360 / (180 / RG.PI), false);
- co.closePath();
-
-
- co.fill();
- co.stroke();
-
- // Now do the inners
- co.beginPath();
- co.fillStyle = color;
- co.strokeStyle = color;
- co.arc(xPos, yPos, prop['chart.ticksize'] - 2, 0, 360 / (180 / RG.PI), false);
-
- co.closePath();
-
- co.fill();
- co.stroke();
-
- } else if ( tickmarks == 'square'
- || tickmarks == 'filledsquare'
- || (tickmarks == 'endsquare' && (index == 0 || index == (lineData.length - 1)))
- || (tickmarks == 'filledendsquare' && (index == 0 || index == (lineData.length - 1))) ) {
-
- co.fillStyle = 'white';
- co.strokeStyle = co.strokeStyle;
-
- co.beginPath();
- co.rect(Math.round(xPos - prop['chart.ticksize']), Math.round(yPos - prop['chart.ticksize']), prop['chart.ticksize'] * 2, prop['chart.ticksize'] * 2);
-
- // Fillrect
- if (tickmarks == 'filledsquare' || tickmarks == 'filledendsquare') {
- co.fillStyle = isShadow ? prop['chart.shadow.color'] : co.strokeStyle;
- co.rect(Math.round(xPos - prop['chart.ticksize']), Math.round(yPos - prop['chart.ticksize']), prop['chart.ticksize'] * 2, prop['chart.ticksize'] * 2);
-
- } else if (tickmarks == 'square' || tickmarks == 'endsquare') {
- co.fillStyle = isShadow ? prop['chart.shadow.color'] : 'white';
- co.rect(Math.round((xPos - prop['chart.ticksize']) + 1), Math.round((yPos - prop['chart.ticksize']) + 1), (prop['chart.ticksize'] * 2) - 2, (prop['chart.ticksize'] * 2) - 2);
- }
-
- co.stroke();
- co.fill();
-
- /**
- * FILLED arrowhead
- */
- } else if (tickmarks == 'filledarrow') {
-
- var x = Math.abs(xPos - prevX);
- var y = Math.abs(yPos - prevY);
-
- if (yPos < prevY) {
- var a = Math.atan(x / y) + 1.57;
- } else {
- var a = Math.atan(y / x) + 3.14;
- }
-
- co.beginPath();
- co.moveTo(Math.round(xPos), Math.round(yPos));
- co.arc(Math.round(xPos), Math.round(yPos), 7, a - 0.5, a + 0.5, false);
- co.closePath();
-
- co.stroke();
- co.fill();
-
- /**
- * Arrow head, NOT filled
- */
- } else if (tickmarks == 'arrow') {
-
- var orig_linewidth = co.lineWidth;
-
- var x = Math.abs(xPos - prevX);
- var y = Math.abs(yPos - prevY);
-
- co.lineWidth;
-
- if (yPos < prevY) {
- var a = Math.atan(x / y) + 1.57;
- } else {
- var a = Math.atan(y / x) + 3.14;
- }
-
- co.beginPath();
- co.moveTo(Math.round(xPos), Math.round(yPos));
- co.arc(Math.round(xPos), Math.round(yPos), 7, a - 0.5 - (doc.all ? 0.1 : 0.01), a - 0.4, false);
-
- co.moveTo(Math.round(xPos), Math.round(yPos));
- co.arc(Math.round(xPos), Math.round(yPos), 7, a + 0.5 + (doc.all ? 0.1 : 0.01), a + 0.5, true);
- co.stroke();
- co.fill();
- // Revert to original lineWidth
- co.lineWidth = orig_linewidth;
-
- /**
- * Custom tick drawing function
- */
- } else if (typeof(tickmarks) == 'function') {
- tickmarks(this, lineData, lineData[index], index, xPos, yPos, color, prevX, prevY);
- }
- };
- /**
- * Draws a filled range if necessary
- */
- this.drawRange =
- this.DrawRange = function ()
- {
- //var RG = RGraph;
- //var ca = this.canvas;
- //var co = this.context;
- //var prop = this.properties;
-
- /**
- * Fill the range if necessary
- */
- if (prop['chart.filled.range'] && prop['chart.filled'] && prop['chart.line.visible']) {
-
- if (RG.is_null(prop['chart.filled.range.threshold'])) {
- prop['chart.filled.range.threshold'] = this.ymin
- prop['chart.filled.range.threshold.colors'] = [prop['chart.fillstyle'], prop['chart.fillstyle']]
- }
-
- for (var idx=0; idx<2; ++idx) {
-
- var threshold_colors = prop['chart.filled.range.threshold.colors'];
- var y = this.getYCoord(prop['chart.filled.range.threshold'])
-
- co.save();
- if (idx == 0) {
- co.beginPath();
- co.rect(0,0,ca.width,y);
- co.clip();
-
- } else {
-
- co.beginPath();
- co.rect(0,y,ca.width, ca.height);
- co.clip();
- }
-
- co.beginPath();
- co.fillStyle = (idx == 1 ? prop['chart.filled.range.threshold.colors'][1] : prop['chart.filled.range.threshold.colors'][0]);
-
- //co.strokeStyle = prop['chart.fillstyle']; // Strokestyle not used now (10th October 2012)
-
- co.lineWidth = 1;
- var len = (this.coords.length / 2);
-
-
-
- for (var i=0; i<len; ++i) {
- if (!RG.is_null(this.coords[i][1])) {
- if (i == 0) {
- co.moveTo(this.coords[i][0], this.coords[i][1])
- } else {
- co.lineTo(this.coords[i][0], this.coords[i][1])
- }
- }
- }
-
-
- for (var i=this.coords.length - 1; i>=len; --i) {
- if (RG.is_null(this.coords[i][1])) {
- co.moveTo(this.coords[i][0], this.coords[i][1])
- } else {
- co.lineTo(this.coords[i][0], this.coords[i][1])
- }
- //co.lineTo(this.coords[i][0], this.coords[i][1])
- }
-
-
-
- // Taken out - 10th Oct 2012
- //co.stroke();
-
- co.fill();
- co.restore();
- }
- }
- };
- /**
- * Redraws the line with the correct line width etc
- *
- * @param array coords The coordinates of the line
- */
- this.redrawLine =
- this.RedrawLine = function (coords, color, linewidth, index)
- {
- if (prop['chart.noredraw'] || prop['chart.filled.range']) {
- return;
- }
-
- co.strokeStyle = (typeof(color) == 'object' && color && color.toString().indexOf('CanvasGradient') == -1 ? color[0] : color);
- co.lineWidth = linewidth;
-
- if (!prop['chart.line.visible']) {
- co.strokeStyle = 'rgba(0,0,0,0)';
- }
- if (!RG.ISOLD && (prop['chart.curvy'] || prop['chart.spline'])) {
- this.DrawCurvyLine(coords, !prop['chart.line.visible'] ? 'rgba(0,0,0,0)' : color, linewidth, index);
- return;
- }
- co.beginPath();
-
- var len = coords.length;
- var width = ca.width
- var height = ca.height;
- var penUp = false;
-
- for (var i=0; i<len; ++i) {
-
- var xPos = coords[i][0];
- var yPos = coords[i][1];
-
- if (i > 0) {
- var prevX = coords[i - 1][0];
- var prevY = coords[i - 1][1];
- }
-
-
- if ((
- (i == 0 && coords[i])
- || (yPos < this.gutterTop)
- || (prevY < this.gutterTop)
- || (yPos > (height - this.gutterBottom))
- || (i > 0 && prevX > (width - this.gutterRight))
- || (i > 0 && prevY > (height - this.gutterBottom))
- || prevY == null
- || penUp == true
- ) && (!prop['chart.outofbounds'] || yPos == null || prevY == null) ) {
- if (RG.ISOLD && yPos == null) {
- // ...?
- } else {
- co.moveTo(coords[i][0], coords[i][1]);
- }
-
- penUp = false;
-
- } else {
-
- if (prop['chart.stepped'] && i > 0) {
- co.lineTo(coords[i][0], coords[i - 1][1]);
- }
-
- // Don't draw the last bit of a stepped chart. Now DO
- //if (!this.properties['chart.stepped'] || i < (coords.length - 1)) {
- co.lineTo(coords[i][0], coords[i][1]);
- //}
- penUp = false;
- }
- }
-
- /**
- * If two colors are specified instead of one, go over the up bits
- */
- if (prop['chart.colors.alternate'] && typeof(color) == 'object' && color[0] && color[1]) {
- for (var i=1; i<len; ++i) {
-
- var prevX = coords[i - 1][0];
- var prevY = coords[i - 1][1];
-
- if (prevY != null && coords[i][1] != null) {
- co.beginPath();
- co.strokeStyle = color[coords[i][1] < prevY ? 0 : 1];
- co.lineWidth = prop['chart.linewidth'];
- co.moveTo(prevX, prevY);
- co.lineTo(coords[i][0], coords[i][1]);
- co.stroke();
- }
- }
- }
- };
- /**
- * This function is used by MSIE only to manually draw the shadow
- *
- * @param array coords The coords for the line
- */
- this.drawIEShadow =
- this.DrawIEShadow = function (coords, color)
- {
- var offsetx = prop['chart.shadow.offsetx'];
- var offsety = prop['chart.shadow.offsety'];
-
- co.lineWidth = prop['chart.linewidth'];
- co.strokeStyle = color;
- co.beginPath();
- for (var i=0; i<coords.length; ++i) {
-
- var isNull = RG.isNull(coords[i][1]);
- var prevIsNull = RG.isNull(coords[i-1]) || RG.isNull(coords[i-1][1]);
- if (i == 0 || isNull || prevIsNull) {
- if (!isNull) {
- co.moveTo(coords[i][0] + offsetx, coords[i][1] + offsety);
- }
- } else {
- co.lineTo(coords[i][0] + offsetx, coords[i][1] + offsety);
- }
- }
- co.stroke();
- };
- /**
- * Draw the backdrop
- */
- this.drawBackdrop =
- this.DrawBackdrop = function (coords, color)
- {
- //var ca = this.canvas;
- //var co = this.context;
- //var prop = this.properties;
-
- var size = prop['chart.backdrop.size'];
- co.lineWidth = size;
- co.globalAlpha = prop['chart.backdrop.alpha'];
- co.strokeStyle = color;
- var yCoords = [];
-
- co.beginPath();
- if (prop['chart.curvy'] && !RG.ISOLD) {
-
- // The DrawSpline function only takes the Y coords so extract them from the coords that have
- // (which are X/Y pairs)
- for (var i=0; i<coords.length; ++i) {
- yCoords.push(coords[i][1])
- }
-
- this.DrawSpline(co, yCoords, color, null);
-
- } else {
- co.moveTo(coords[0][0], coords[0][1]);
- for (var j=1; j<coords.length; ++j) {
- co.lineTo(coords[j][0], coords[j][1]);
- }
- }
- co.stroke();
-
- // Reset the alpha value
- co.globalAlpha = 1;
- RG.NoShadow(this);
- };
- /**
- * Returns the linewidth
- */
- this.getLineWidth =
- this.GetLineWidth = function (i)
- {
- var linewidth = prop['chart.linewidth'];
-
- if (typeof(linewidth) == 'number') {
- return linewidth;
-
- } else if (typeof(linewidth) == 'object') {
- if (linewidth[i]) {
- return linewidth[i];
- } else {
- return linewidth[0];
- }
-
- alert('[LINE] Error! chart.linewidth should be a single number or an array of one or more numbers');
- }
- };
- /**
- * The getPoint() method - used to get the point the mouse is currently over, if any
- *
- * @param object e The event object
- * @param object OPTIONAL You can pass in the bar object instead of the
- * function getting it from the canvas
- */
- this.getShape =
- this.getPoint = function (e)
- {
- var obj = this;
- var RG = RGraph;
- var ca = canvas = e.target;
- var co = context = this.context;
- var prop = this.properties;
-
- var mouseXY = RG.getMouseXY(e);
- var mouseX = mouseXY[0];
- var mouseY = mouseXY[1];
-
- // This facilitates you being able to pass in the bar object as a parameter instead of
- // the function getting it from the object
- if (arguments[1]) {
- obj = arguments[1];
- }
-
- for (var i=0; i<obj.coords.length; ++i) {
-
- var x = obj.coords[i][0];
- var y = obj.coords[i][1];
-
- // Do this if the hotspot is triggered by the X coord AND the Y coord
- if ( mouseX <= (x + prop['chart.tooltips.hotspot.size'])
- && mouseX >= (x - prop['chart.tooltips.hotspot.size'])
- && mouseY <= (y + prop['chart.tooltips.hotspot.size'])
- && mouseY >= (y - prop['chart.tooltips.hotspot.size'])
- ) {
-
- if (RG.parseTooltipText) {
- var tooltip = RG.parseTooltipText(prop['chart.tooltips'], i);
- }
-
- // Work out the dataset
- var dataset = 0;
- var idx = i;
- while ((idx + 1) > this.data[dataset].length) {
- idx -= this.data[dataset].length;
- dataset++;
- }
-
- return {0:obj, 1:x, 2:y, 3:i, 'object': obj, 'x': x, 'y': y, 'index': i, 'tooltip': tooltip, 'dataset': dataset, 'index_adjusted': idx};
-
- } else if ( prop['chart.tooltips.hotspot.xonly'] == true
- && mouseX <= (x + prop['chart.tooltips.hotspot.size'])
- && mouseX >= (x - prop['chart.tooltips.hotspot.size'])) {
-
- var tooltip = RG.parseTooltipText(prop['chart.tooltips'], i);
-
- return {0:obj, 1:x, 2:y, 3:i, 'object': obj, 'x': x, 'y': y, 'index': i, 'tooltip': tooltip};
- }
- }
- };
- /**
- * Draws the above line labels
- */
- this.drawAboveLabels =
- this.DrawAboveLabels = function ()
- {
- var size = prop['chart.labels.above.size'];
- var font = prop['chart.text.font'];
- var units_pre = prop['chart.units.pre'];
- var units_post = prop['chart.units.post'];
- var decimals = prop['chart.labels.above.decimals'];
-
- // Use this to 'reset' the drawing state
- co.beginPath();
-
- // Don't need to check that chart.labels.above is enabled here, it's been done already
- for (var i=0, len=this.coords.length; i<len; i+=1) {
- var coords = this.coords[i];
- RG.Text2(this, {'font':font,
- 'size':size,
- 'x':coords[0],
- 'y':coords[1] - 5 - size,
- 'text':RG.numberFormat(this, typeof decimals === 'number' ? this.data_arr[i].toFixed(decimals) : this.data_arr[i], units_pre, units_post),
- 'valign':'center',
- 'halign':'center',
- 'bounding':true,
- 'boundingFill':'white',
- 'tag': 'labels.above'
- });
- }
- };
- /**
- * Draw a curvy line.
- */
- this.drawCurvyLine =
- this.DrawCurvyLine = function (coords, color, linewidth, index)
- {
- //var ca = this.canvas;
- //var co = this.context;
- //var prop = this.properties;
- if (RG.ISOLD) {
- return;
- }
-
- var yCoords = [];
-
- for (var i=0; i<coords.length; ++i) {
- yCoords.push(coords[i][1])
- }
-
- if (prop['chart.filled']) {
- co.beginPath();
- co.moveTo(coords[0][0],ca.height - this.gutterBottom);
- this.DrawSpline(co, yCoords, color, index);
- if (prop['chart.filled.accumulative'] && index > 0) {
- for (var i=(this.coordsSpline[index - 1].length - 1); i>=0; i-=1) {
- co.lineTo(this.coordsSpline[index - 1][i][0], this.coordsSpline[index - 1][i][1]);
- }
- } else {
- co.lineTo(coords[coords.length - 1][0],ca.height - this.gutterBottom);
- }
- co.fill();
- }
- co.beginPath();
- this.DrawSpline(co, yCoords, color, index);
- co.stroke();
- };
- /**
- * When you click on the chart, this method can return the Y value at that point. It works for any point on the
- * chart (that is inside the gutters) - not just points on the Line.
- *
- * @param object e The event object
- */
- this.getValue = function (arg)
- {
- if (arg.length == 2) {
- var mouseX = arg[0];
- var mouseY = arg[1];
- } else {
- var mouseCoords = RG.getMouseXY(arg);
- var mouseX = mouseCoords[0];
- var mouseY = mouseCoords[1];
- }
-
- var obj = this;
- var xaxispos = prop['chart.xaxispos'];
-
- if (mouseY < prop['chart.gutter.top']) {
- return xaxispos == 'bottom' || xaxispos == 'center' ? this.max : this.min;
- } else if (mouseY > (ca.height - prop['chart.gutter.bottom'])) {
- return xaxispos == 'bottom' ? this.min : this.max;
- }
-
- if (prop['chart.xaxispos'] == 'center') {
- var value = (( (obj.grapharea / 2) - (mouseY - prop['chart.gutter.top'])) / obj.grapharea) * (obj.max - obj.min);
- value *= 2;
- value > 0 ? value += this.min : value -= this.min;
- return value;
- } else if (prop['chart.xaxispos'] == 'top') {
- var value = ((obj.grapharea - (mouseY - prop['chart.gutter.top'])) / obj.grapharea) * (obj.max - obj.min);
- value = Math.abs(obj.max - value) * -1;
- return value;
- } else {
- var value = ((obj.grapharea - (mouseY - prop['chart.gutter.top'])) / obj.grapharea) * (obj.max - obj.min)
- value += obj.min;
- return value;
- }
- };
- /**
- * Each object type has its own Highlight() function which highlights the appropriate shape
- *
- * @param object shape The shape to highlight
- */
- this.highlight =
- this.Highlight = function (shape)
- {
- if (prop['chart.tooltips.highlight']) {
- // Add the new highlight
- RG.Highlight.Point(this, shape);
- }
- };
- /**
- * The getObjectByXY() worker method. Don't call this call:
- *
- * RG.ObjectRegistry.getObjectByXY(e)
- *
- * @param object e The event object
- */
- this.getObjectByXY = function (e)
- {
- //var ca = this.canvas;
- //var prop = this.properties;
- var mouseXY = RG.getMouseXY(e);
-
- // The 5 is so that the cursor doesn't have to be over the graphArea to trigger the hotspot
- if (
- (mouseXY[0] > prop['chart.gutter.left'] - 5)
- && mouseXY[0] < (ca.width - prop['chart.gutter.right'] + 5)
- && mouseXY[1] > (prop['chart.gutter.top'] - 5)
- && mouseXY[1] < (ca.height - prop['chart.gutter.bottom'] + 5)
- ) {
-
- return this;
- }
- };
- /**
- * This method handles the adjusting calculation for when the mouse is moved
- *
- * @param object e The event object
- */
- this.adjusting_mousemove =
- this.Adjusting_mousemove = function (e)
- {
- /**
- * Handle adjusting for the Bar
- */
- if (prop['chart.adjustable'] && RG.Registry.Get('chart.adjusting') && RG.Registry.Get('chart.adjusting').uid == this.uid) {
-
- // Rounding the value to the given number of decimals make the chart step
- var value = Number(this.getValue(e));//.toFixed(this.properties['chart.scale.decimals']);
- var shape = RG.Registry.Get('chart.adjusting.shape');
-
- if (shape) {
-
- RG.Registry.Set('chart.adjusting.shape', shape);
-
- this.original_data[shape['dataset']][shape['index_adjusted']] = Number(value);
-
- RG.redrawCanvas(e.target);
-
- RG.fireCustomEvent(this, 'onadjust');
- }
- }
- };
- /**
- * This function can be used when the canvas is clicked on (or similar - depending on the event)
- * to retrieve the relevant Y coordinate for a particular value.
- *
- * @param int value The value to get the Y coordinate for
- */
- this.getYCoord = function (value)
- {
- if (typeof(value) != 'number') {
- return null;
- }
-
- var y;
- var xaxispos = prop['chart.xaxispos'];
-
- // Higher than max
- // Commented out on March 7th 2013 because the tan curve was not showing correctly
- //if (value > this.max) {
- // value = this.max;
- //}
-
- if (xaxispos == 'top') {
-
- // Account for negative numbers
- //if (value < 0) {
- // value = Math.abs(value);
- //}
-
- y = ((value - this.min) / (this.max - this.min)) * this.grapharea;
-
- // Inverted Y labels
- if (prop['chart.scale.invert']) {
- y = this.grapharea - y;
- }
-
- y = y + this.gutterTop
-
- } else if (xaxispos == 'center') {
-
- y = ((value - this.min) / (this.max - this.min)) * (this.grapharea / 2);
- y = (this.grapharea / 2) - y;
- y += this.gutterTop;
-
- } else {
-
- if ((value < this.min || value > this.max) && prop['chart.outofbounds'] == false) {
- return null;
- }
-
- y = ((value - this.min) / (this.max - this.min)) * this.grapharea;
-
- // Inverted Y labels
- if (prop['chart.scale.invert']) {
- y = this.grapharea - y;
- }
-
- y = ca.height - this.gutterBottom - y;
- }
-
- return y;
- };
- /**
- * This function positions a tooltip when it is displayed
- *
- * @param obj object The chart object
- * @param int x The X coordinate specified for the tooltip
- * @param int y The Y coordinate specified for the tooltip
- * @param objec tooltip The tooltips DIV element
- */
- this.positionTooltip = function (obj, x, y, tooltip, idx)
- {
- //var ca = obj.canvas;
- //var co = obj.context;
- //var prop = obj.properties;
-
- var coordX = obj.coords[tooltip.__index__][0];
- var coordY = obj.coords[tooltip.__index__][1];
- var canvasXY = RG.getCanvasXY(obj.canvas);
- var gutterLeft = prop['chart.gutter.left'];
- var gutterTop = prop['chart.gutter.top'];
- var width = tooltip.offsetWidth;
-
- // Set the top position
- tooltip.style.left = 0;
- tooltip.style.top = parseInt(tooltip.style.top) - 9 + 'px';
-
- // By default any overflow is hidden
- tooltip.style.overflow = '';
-
- // The arrow
- var img = new Image();
- img.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAFCAYAAACjKgd3AAAARUlEQVQYV2NkQAN79+797+RkhC4M5+/bd47B2dmZEVkBCgcmgcsgbAaA9GA1BCSBbhAuA/AagmwQPgMIGgIzCD0M0AMMAEFVIAa6UQgcAAAAAElFTkSuQmCC';
- img.style.position = 'absolute';
- img.id = '__rgraph_tooltip_pointer__';
- img.style.top = (tooltip.offsetHeight - 2) + 'px';
- tooltip.appendChild(img);
-
- // Reposition the tooltip if at the edges:
-
- // LEFT edge
- if ((canvasXY[0] + coordX - (width / 2)) < 10) {
- tooltip.style.left = (canvasXY[0] + coordX - (width * 0.2)) + 'px';
- img.style.left = ((width * 0.2) - 8.5) + 'px';
-
- // RIGHT edge
- } else if ((canvasXY[0] + coordX + (width / 2)) > doc.body.offsetWidth) {
- tooltip.style.left = canvasXY[0] + coordX - (width * 0.8) + 'px';
- img.style.left = ((width * 0.8) - 8.5) + 'px';
-
- // Default positioning - CENTERED
- } else {
- tooltip.style.left = (canvasXY[0] + coordX - (width * 0.5)) + 'px';
- img.style.left = ((width * 0.5) - 8.5) + 'px';
- }
- };
- /**
- * This function draws a curvy line
- *
- * @param object context The 2D context
- * @param array coords The coordinates
- */
- this.drawSpline =
- this.DrawSpline = function (context, coords, color, index)
- {
- // Commented out on 26th Oct 2013
- //var co = context;
- //var ca = co.canvas;
- //var prop = this.properties;
- this.coordsSpline[index] = [];
- var xCoords = [];
- var gutterLeft = prop['chart.gutter.left'];
- var gutterRight = prop['chart.gutter.right'];
- var hmargin = prop['chart.hmargin'];
- var interval = (ca.width - (gutterLeft + gutterRight) - (2 * hmargin)) / (coords.length - 1);
-
- co.strokeStyle = color;
- /**
- * The drawSpline function takes an array of JUST Y coords - not X/Y coords. So the line coords need converting
- * if we've been given X/Y pairs
- */
- for (var i=0,len=coords.length; i<len;i+=1) {
- if (typeof coords[i] == 'object' && coords[i] && coords[i].length == 2) {
- coords[i] = Number(coords[i][1]);
- }
- }
- /**
- * Get the Points array in the format we want - first value should be null along with the lst value
- */
- var P = [coords[0]];
- for (var i=0; i<coords.length; ++i) {
- P.push(coords[i]);
- }
- P.push(coords[coords.length - 1] + (coords[coords.length - 1] - coords[coords.length - 2]));
- //P.push(null);
-
- for (var j=1; j<P.length-2; ++j) {
- for (var t=0; t<10; ++t) {
-
- var yCoord = Spline( t/10, P[j-1], P[j], P[j+1], P[j+2] );
-
- xCoords.push(((j-1) * interval) + (t * (interval / 10)) + gutterLeft + hmargin);
- co.lineTo(xCoords[xCoords.length - 1], yCoord);
-
- if (typeof index == 'number') {
- this.coordsSpline[index].push([xCoords[xCoords.length - 1], yCoord]);
- }
- }
- }
- // Draw the last section
- co.lineTo(((j-1) * interval) + gutterLeft + hmargin, P[j]);
- if (typeof index == 'number') {
- this.coordsSpline[index].push([((j-1) * interval) + gutterLeft + hmargin, P[j]]);
- }
-
- function Spline (t, P0, P1, P2, P3)
- {
- return 0.5 * ((2 * P1) +
- ((0-P0) + P2) * t +
- ((2*P0 - (5*P1) + (4*P2) - P3) * (t*t) +
- ((0-P0) + (3*P1)- (3*P2) + P3) * (t*t*t)));
- }
- };
- /**
- * This allows for easy specification of gradients
- */
- this.parseColors = function ()
- {
- // Save the original colors so that they can be restored when the canvas is reset
- if (this.original_colors.length === 0) {
- this.original_colors['chart.colors'] = RGraph.array_clone(prop['chart.colors']);
- this.original_colors['chart.fillstyle'] = RGraph.array_clone(prop['chart.fillstyle']);
- this.original_colors['chart.key.colors'] = RGraph.array_clone(prop['chart.key.colors']);
- this.original_colors['chart.background.barcolor1'] = prop['chart.background.barcolor1'];
- this.original_colors['chart.background.barcolor2'] = prop['chart.background.barcolor2'];
- this.original_colors['chart.background.grid.color'] = prop['chart.background.grid.color'];
- this.original_colors['chart.background.color'] = prop['chart.background.color'];
- this.original_colors['chart.text.color'] = prop['chart.text.color'];
- this.original_colors['chart.crosshairs.color'] = prop['chart.crosshairs.color'];
- this.original_colors['chart.annotate.color'] = prop['chart.annotate.color'];
- this.original_colors['chart.title.color'] = prop['chart.title.color'];
- this.original_colors['chart.title.yaxis.color'] = prop['chart.title.yaxis.color'];
- this.original_colors['chart.key.background'] = prop['chart.key.background'];
- this.original_colors['chart.axis.color'] = prop['chart.axis.color'];
- this.original_colors['chart.highlight.fill'] = prop['chart.highlight.fill'];
- }
-
-
-
- for (var i=0; i<prop['chart.colors'].length; ++i) {
- if (typeof(prop['chart.colors'][i]) == 'object' && prop['chart.colors'][i][0] && prop['chart.colors'][i][1]) {
- prop['chart.colors'][i][0] = this.parseSingleColorForGradient(prop['chart.colors'][i][0]);
- prop['chart.colors'][i][1] = this.parseSingleColorForGradient(prop['chart.colors'][i][1]);
- } else {
- prop['chart.colors'][i] = this.parseSingleColorForGradient(prop['chart.colors'][i]);
- }
- }
-
- /**
- * Fillstyle
- */
- if (prop['chart.fillstyle']) {
- if (typeof(prop['chart.fillstyle']) == 'string') {
- prop['chart.fillstyle'] = this.parseSingleColorForGradient(prop['chart.fillstyle'], 'vertical');
- } else {
- for (var i=0; i<prop['chart.fillstyle'].length; ++i) {
- prop['chart.fillstyle'][i] = this.parseSingleColorForGradient(prop['chart.fillstyle'][i], 'vertical');
- }
- }
- }
-
- /**
- * Key colors
- */
- if (!RG.is_null(prop['chart.key.colors'])) {
- for (var i=0; i<prop['chart.key.colors'].length; ++i) {
- prop['chart.key.colors'][i] = this.parseSingleColorForGradient(prop['chart.key.colors'][i]);
- }
- }
-
- /**
- * Parse various properties for colors
- */
- var properties = ['chart.background.barcolor1',
- 'chart.background.barcolor2',
- 'chart.background.grid.color',
- 'chart.background.color',
- 'chart.text.color',
- 'chart.crosshairs.color',
- 'chart.annotate.color',
- 'chart.title.color',
- 'chart.title.yaxis.color',
- 'chart.key.background',
- 'chart.axis.color',
- 'chart.highlight.fill'];
-
- for (var i=0; i<properties.length; ++i) {
- prop[properties[i]] = this.parseSingleColorForGradient(prop[properties[i]]);
- }
- };
- /**
- * This parses a single color value
- */
- this.parseSingleColorForGradient = function (color)
- {
- //var RG = RGraph;
- //var ca = this.canvas;
- //var co = this.context;
-
- if (!color || typeof(color) != 'string') {
- return color;
- }
-
- /**
- * Horizontal or vertical gradient
- */
- var dir = typeof(arguments[1]) == 'string' ? arguments[1] : 'vertical';
-
- if (typeof color === 'string' && color.match(/^gradient\((.*)\)$/i)) {
-
- var parts = RegExp.$1.split(':');
-
- // Create the gradient
- if (dir == 'horizontal') {
- var grad = co.createLinearGradient(0,0,ca.width,0);
- } else {
- var grad = co.createLinearGradient(0,ca.height - prop['chart.gutter.bottom'],0,prop['chart.gutter.top']);
- }
-
- var diff = 1 / (parts.length - 1);
-
- grad.addColorStop(0, RG.trim(parts[0]));
-
- for (var j=1; j<parts.length; ++j) {
- grad.addColorStop(j * diff, RG.trim(parts[j]));
- }
- }
-
- return grad ? grad : color;
- };
- /**
- * Sets the appropriate shadow
- */
- this.setShadow =
- this.SetShadow = function (i)
- {
- //var ca = this.canvas;
- //var co = this.context;
- //var prop = this.properties;
-
- if (prop['chart.shadow']) {
- /**
- * Handle the appropriate shadow color. This now facilitates an array of differing
- * shadow colors
- */
- var shadowColor = prop['chart.shadow.color'];
-
- /**
- * Accommodate an array of shadow colors as well as a single string
- */
- if (typeof(shadowColor) == 'object' && shadowColor[i - 1]) {
- co.shadowColor = shadowColor[i];
-
- } else if (typeof(shadowColor) == 'object') {
- co.shadowColor = shadowColor[0];
-
- } else if (typeof(shadowColor) == 'string') {
- co.shadowColor = shadowColor;
- }
-
- co.shadowBlur = prop['chart.shadow.blur'];
- co.shadowOffsetX = prop['chart.shadow.offsetx'];
- co.shadowOffsetY = prop['chart.shadow.offsety'];
- }
- };
- /**
- * This function handles highlighting an entire data-series for the interactive
- * key
- *
- * @param int index The index of the data series to be highlighted
- */
- this.interactiveKeyHighlight = function (index)
- {
- var coords = this.coords2[index];
- if (coords) {
- var pre_linewidth = co.lineWidth;
- var pre_linecap = co.lineCap;
-
- co.lineWidth = prop['chart.linewidth'] + 10;
- co.lineCap = 'round';
- co.strokeStyle = prop['chart.key.interactive.highlight.chart.stroke'];
-
- co.beginPath();
- if (prop['chart.curvy']) {
- this.DrawSpline(co, coords, prop['chart.key.interactive.highlight.chart'], null);
- } else {
- for (var i=0,len=coords.length; i<len; i+=1) {
- if ( i == 0
- || RG.is_null(coords[i][1])
- || (typeof coords[i - 1][1] != undefined && RG.is_null(coords[i - 1][1]))) {
- co.moveTo(coords[i][0], coords[i][1]);
- } else {
- co.lineTo(coords[i][0], coords[i][1]);
- }
- }
- }
- co.stroke();
-
- // Reset the lineCap and lineWidth
- co.lineWidth = pre_linewidth;
- co.lineCap = pre_linecap;
- }
- };
- /**
- * Using a function to add events makes it easier to facilitate method chaining
- *
- * @param string type The type of even to add
- * @param function func
- */
- this.on = function (type, func)
- {
- if (type.substr(0,2) !== 'on') {
- type = 'on' + type;
- }
-
- this[type] = func;
-
- return this;
- };
- /**
- * This function runs once only
- * (put at the end of the file (before any effects))
- */
- this.firstDrawFunc = function ()
- {
- };
- /**
- * Trace
- *
- * This effect is for the Line chart, uses the jQuery library and slowly
- * uncovers the Line , but you can see the background of the chart. This effect
- * is quite new (1/10/2011) and as such should be used with caution.
- *
- * @param object An object of configuration. You can give 'duration' or 'frames' here.
- * @param function An optional callback function
- */
- this.trace = function ()
- {
- var obj = this;
- var callback = typeof arguments[1] === 'function' ? arguments[1] : function () {};
- var opt = arguments[0] || {};
-
- if (opt.frames) {
- opt.duration = (opt.frames / 60) * 1000;
- }
- if (!opt.duration) {
- opt.duration = 1500;
- }
- RG.clear(obj.canvas);
- RG.redrawCanvas(obj.canvas);
- /**
- * Create the DIV that the second canvas will sit in
- */
- var div = doc.createElement('DIV');
- var xy = RG.getCanvasXY(obj.canvas);
- div.id = '__rgraph_trace_animation_' + RG.random(0, 4351623) + '__';
- div.style.left = xy[0] + 'px';
- div.style.top = xy[1] + 'px';
- div.style.width = obj.Get('chart.gutter.left');
- div.style.height = obj.canvas.height + 'px';
- div.style.position = 'absolute';
- div.style.overflow = 'hidden';
- doc.body.appendChild(div);
-
- obj.canvas.__rgraph_trace_div__ = div;
-
- /**
- * Make the second canvas
- */
- var id = '__rgraph_line_trace_animation_' + RG.random(0, 99999999) + '__';
- var canvas2 = doc.createElement('CANVAS');
-
-
-
-
- // Copy the 3D CSS transformation properties across from the original canvas
- var properties = ['WebkitTransform','MozTransform','OTransform','MSTransform','transform'];
-
- for (i in properties) {
- var name = properties[i];
- if (typeof obj.canvas.style[name] === 'string' && obj.canvas.style[name]) {
- canvas2.style[name] = obj.canvas.style[name];
- }
- }
-
-
-
- obj.canvas.__rgraph_line_canvas2__ = canvas2;
- canvas2.width = obj.canvas.width;
- canvas2.height = obj.canvas.height;
- canvas2.style.position = 'absolute';
- canvas2.style.left = 0;
- canvas2.style.top = 0;
-
-
- // This stops the clear effect clearing the canvas - which can happen if you have multiple canvas tags on the page all with
- // dynamic effects that do redrawing
- canvas2.noclear = true;
-
- canvas2.id = id;
- div.appendChild(canvas2);
-
- var reposition_canvas2 = function (e)
- {
- var xy = RG.getCanvasXY(obj.canvas);
-
- div.style.left = xy[0] + 'px';
- div.style.top = xy[1] + 'px';
- }
- window.addEventListener('resize', reposition_canvas2, false)
-
- /**
- * Make a copy of the original Line object
- */
- var obj2 = new RG.Line(id, RG.array_clone(obj.original_data));
-
- // Remove the new line from the ObjectRegistry so that it isn't redawn
- RG.ObjectRegistry.Remove(obj2);
-
- for (i in obj.properties) {
- if (typeof i === 'string') {
- obj2.Set(i, obj.properties[i]);
- }
- }
-
- //obj2.Set('chart.tooltips', null);
- obj2.Set('labels', []);
- obj2.Set('background.grid', false);
- obj2.Set('background.barcolor1', 'rgba(0,0,0,0)');
- obj2.Set('background.barcolor2', 'rgba(0,0,0,0)');
- obj2.Set('ylabels', false);
- obj2.Set('noaxes', true);
- obj2.Set('title', '');
- obj2.Set('title.xaxis', '');
- obj2.Set('title.yaxis', '');
- obj2.Set('filled.accumulative', obj.Get('chart.filled.accumulative'));
- obj.Set('key', []);
- obj2.Draw();
-
- obj.canvas.__rgraph_trace_obj2__ = obj2;
-
-
- /**
- * This effectively hides the line
- */
- obj.Set('line.visible', false);
- obj.Set('colors', ['rgba(0,0,0,0)']);
- if (obj.Get('filled')) {
- var original_fillstyle = obj.Get('chart.fillstyle');
- obj.Set('fillstyle', 'rgba(0,0,0,0)');
- obj.Set('animation.trace.original.fillstyle', original_fillstyle);
- }
-
- RG.clear(obj.canvas);
- //obj.Draw();
- RG.redrawCanvas(obj.canvas);
-
- /**
- * Place a DIV over the canvas to stop interaction with it
- */
- if (!obj.canvas.__rgraph_trace_cover__) {
- var div2 = doc.createElement('DIV');
- div2.id = '__rgraph_trace_animation_' + RG.random(0, 4351623) + '__';
- div2.style.left = xy[0] + 'px';
- div2.style.top = xy[1] + 'px';
- div2.style.width = obj.canvas.width + 'px';
- div2.style.height = obj.canvas.height + 'px';
- div2.style.position = 'absolute';
- div2.style.overflow = 'hidden';
- div2.style.backgroundColor = 'rgba(0,0,0,0)';
- div.div2 = div2;
- obj.canvas.__rgraph_trace_cover__ = div2;
- doc.body.appendChild(div2);
- } else {
- div2 = obj.canvas.__rgraph_trace_cover__;
- }
- /**
- * Get rid of the second canvas and turn the line back on
- * on the original.
- */
- trace_complete = function (obj)
- {
- var obj2 = obj.canvas.__rgraph_trace_obj2__;
-
- // Remove the window resize listener
- win.removeEventListener('resize', reposition_canvas2, false);
-
- div.style.display = 'none';
- div2.style.display = 'none';
-
- //div.removeChild(canvas2);
- obj.Set('line.visible', true);
-
- // Revert the filled status back to as it was
- obj.Set('filled', RGraph.array_clone(obj2.Get('chart.filled')));
- obj.Set('fillstyle', obj.Get('chart.animation.trace.original.fillstyle'));
- obj.Set('colors', RGraph.array_clone(obj2.Get('chart.colors')));
- obj.Set('key', RGraph.array_clone(obj2.Get('chart.key')));
-
- RGraph.RedrawCanvas(obj.canvas);
-
- obj.canvas.__rgraph_trace_div__.style.display = 'none';
- obj.canvas.__rgraph_line_canvas2__.style.display = 'none';
- obj.canvas.__rgraph_trace_cover__.style.display = 'none';
- obj.canvas.__rgraph_trace_div__ = null;
- obj.canvas.__rgraph_line_canvas2__ = null;
- obj.canvas.__rgraph_trace_cover__ = null;
-
-
- callback(obj);
- };
-
-
-
-
- /**
- * Animate the DIV that contains the canvas
- */
- jq('#' + div.id).animate({
- width: obj.canvas.width - obj.gutterRight + 'px'
- }, opt.duration, function () {trace_complete(obj)});
-
- return this;
- };
- /**
- * Unfold
- *
- * This effect gradually increases the X/Y coordinatesfrom 0
- *
- * @param object obj The chart object
- */
- this.unfold = function ()
- {
- var obj = this;
- var opt = arguments[0] ? arguments[0] : {};
- var frames = opt.frames ? opt.frames : 30;
- var frame = 0;
- var callback = arguments[1] ? arguments[1] : function () {};
- var initial = prop['chart.animation.unfold.initial'];
-
- prop['chart.animation.factor'] = prop['chart.animation.unfold.initial'];
- function iterator ()
- {
- prop['chart.animation.factor'] = ((1 - initial) * (frame / frames)) + initial;
-
- RG.clear(obj.canvas);
- RG.redrawCanvas(obj.canvas);
-
- if (frame < frames) {
- frame++;
- RG.Effects.updateCanvas(iterator);
- } else {
- callback(obj);
- }
- }
- iterator();
- return this;
- };
- /**
- * Trace2
- *
- * This is a new version of the Trace effect which no longer requires jQuery and is more compatible
- * with other effects (eg Expand). This new effect is considerably simpler and less code.
- *
- * @param object Options for the effect. Currently only "frames" is available.
- * @param int A function that is called when the ffect is complete
- */
- this.trace2 = function ()
- {
- var obj = this;
- var callback = arguments[2];
- var opt = arguments[0] || {};
- var frames = opt.frames || 30;
- var frame = 0;
- var callback = arguments[1] || function () {};
- obj.Set('animation.trace.clip', 0);
-
- function iterator ()
- {
- RG.clear(obj.canvas);
- RG.redrawCanvas(obj.canvas);
- if (frame++ < frames) {
- obj.Set('animation.trace.clip', frame / frames);
- RG.Effects.updateCanvas(iterator);
- } else {
- callback(obj);
- }
- }
-
- iterator();
-
- return this;
- };
- /**
- * FoldToCenter
- *
- * Line chart FoldTocenter
- *
- * @param object OPTIONAL An object map of options
- * @param function OPTIONAL A callback to run when the effect is complete
- */
- this.foldtocenter =
- this.foldToCenter = function ()
- {
- var obj = this;
- var opt = arguments[0] || {};
- var frames = opt.frames || 30;
- var frame = 0;
- var callback = arguments[1] || function () {};
- var center_value = obj.scale2.max / 2;
- obj.Set('chart.ymax', obj.scale2.max);
-
- var original_data = RG.array_clone(obj.original_data);
-
- function iterator ()
- {
- for (var i=0,len=obj.data.length; i<len; ++i) {
- if (obj.data[i].length) {
- for (var j=0,len2=obj.data[i].length; j<len2; ++j) {
-
- var dataset = obj.original_data[i];
- if (dataset[j] > center_value) {
- dataset[j] = original_data[i][j] - ((original_data[i][j] - center_value) * (frame / frames));
- } else {
- dataset[j] = original_data[i][j] + (((center_value - original_data[i][j]) / frames) * frame);
- }
- }
- }
- }
-
- RG.clear(obj.canvas);
- RG.redrawCanvas(obj.canvas)
-
- if (frame++ < frames) {
- RG.Effects.updateCanvas(iterator);
- } else {
- callback(obj);
- }
- }
- iterator();
- return this;
- };
- /**
- * UnfoldFromCenterTrace effect
- *
- * @param object An object containing options
- * @param function A callback function
- */
- this.unfoldFromCenterTrace =
- this.unfoldFromCenterTrace2 = function ()
- {
- var obj = this;
- var opt = arguments[0] || {};
- var frames = opt.frames || 30;
- var frame = 0;
- var data = RG.array_clone(obj.original_data);
- var callback = arguments[1] || function () {};
- // Draw the chart once to get the scale values
- obj.canvas.style.visibility = 'hidden';
- obj.Draw();
- var max = obj.scale2.max;
- RG.clear(obj.canvas);
- obj.canvas.style.visibility = 'visible';
- /**
- * When the Trace function finishes it calls this function
- */
- var unfoldCallback = function ()
- {
- obj.original_data = data;
- obj.unfoldFromCenter({frames: frames / 2}, callback);
- };
- /**
- * Determine the mid-point
- */
- var half = obj.Get('chart.xaxispos') == 'center' ? obj.min : ((obj.max - obj.min) / 2) + obj.min;
- obj.Set('chart.ymax', obj.max);
-
- for (var i=0,len=obj.original_data.length; i<len; ++i) {
- for (var j=0; j<obj.original_data[i].length; ++j) {
- obj.original_data[i][j] = (obj.Get('chart.filled') && obj.Get('chart.filled.accumulative') && i > 0) ? 0 : half;
- }
- }
- RG.clear(obj.canvas);
- obj.trace2({frames: frames / 2}, unfoldCallback);
- };
- /**
- * UnfoldFromCenter
- *
- * Line chart unfold from center
- *
- * @param object An option map of properties. Only frames is supported: {frames: 30}
- * @param function An optional callback
- */
- this.unfoldFromCenter = function ()
- {
- var obj = this;
- var opt = arguments[0] || {};
- var frames = opt.frames || 30;
- var frame = 0;
- var callback = arguments[1] || function () {};
-
- // Draw the chart once to get the scale values
- obj.canvas.style.visibility = 'hidden';
- obj.Draw();
- var max = obj.scale2.max;
- RG.clear(obj.canvas);
- obj.canvas.style.visibility = 'visible';
- var center_value = obj.Get('chart.xaxispos') === 'center' ? prop['chart.ymin'] : ((obj.max - obj.min) / 2) + obj.min;
- var original_data = RG.array_clone(obj.original_data);
- var steps = null;
-
- obj.Set('chart.ymax', max);
- if (!steps) {
-
- steps = [];
-
- for (var dataset=0,len=original_data.length; dataset<len; ++dataset) {
-
- steps[dataset] = []
-
- for (var i=0,len2=original_data[dataset].length; i<len2; ++i) {
- if (prop['chart.filled'] && prop['chart.filled.accumulative'] && dataset > 0) {
- steps[dataset][i] = original_data[dataset][i] / frames;
- obj.original_data[dataset][i] = center_value;
- } else {
- steps[dataset][i] = (original_data[dataset][i] - center_value) / frames;
- obj.original_data[dataset][i] = center_value;
- }
- }
- }
- }
- function unfoldFromCenter ()
- {
- for (var dataset=0; dataset<original_data.length; ++dataset) {
- for (var i=0; i<original_data[dataset].length; ++i) {
- obj.original_data[dataset][i] += steps[dataset][i];
- }
- }
- RG.clear(obj.canvas);
- RG.redrawCanvas(obj.canvas);
-
- if (--frames > 0) {
- RG.Effects.updateCanvas(unfoldFromCenter);
- } else {
- obj.original_data = RG.array_clone(original_data);
- RG.clear(obj.canvas);
- RG.redrawCanvas(obj.canvas);
- callback(obj);
- }
- }
-
- unfoldFromCenter();
-
- return this;
- };
- /**
- * Register the object so it is redrawn when necessary
- */
- RG.Register(this);
- };
|