rickshaw.js 105 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299
  1. (function (root, factory) {
  2. if (typeof define === 'function' && define.amd) {
  3. define(['d3'], function (d3) {
  4. return (root.Rickshaw = factory(d3));
  5. });
  6. } else if (typeof exports === 'object') {
  7. module.exports = factory(require('d3'));
  8. } else {
  9. root.Rickshaw = factory(d3);
  10. }
  11. }(this, function (d3) {
  12. /* jshint -W079 */
  13. var Rickshaw = {
  14. version: '1.6.6',
  15. namespace: function(namespace, obj) {
  16. var parts = namespace.split('.');
  17. var parent = Rickshaw;
  18. for(var i = 1, length = parts.length; i < length; i++) {
  19. var currentPart = parts[i];
  20. parent[currentPart] = parent[currentPart] || {};
  21. parent = parent[currentPart];
  22. }
  23. return parent;
  24. },
  25. keys: function(obj) {
  26. var keys = [];
  27. for (var key in obj) keys.push(key);
  28. return keys;
  29. },
  30. extend: function(destination, source) {
  31. for (var property in source) {
  32. destination[property] = source[property];
  33. }
  34. return destination;
  35. },
  36. clone: function(obj) {
  37. return JSON.parse(JSON.stringify(obj));
  38. }
  39. };
  40. /* Adapted from https://github.com/Jakobo/PTClass */
  41. /*
  42. Copyright (c) 2005-2010 Sam Stephenson
  43. Permission is hereby granted, free of charge, to any person obtaining a copy
  44. of this software and associated documentation files (the "Software"), to deal
  45. in the Software without restriction, including without limitation the rights
  46. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  47. copies of the Software, and to permit persons to whom the Software is
  48. furnished to do so, subject to the following conditions:
  49. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  50. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  51. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  52. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  53. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  54. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  55. SOFTWARE.
  56. */
  57. /* Based on Alex Arnell's inheritance implementation. */
  58. /** section: Language
  59. * class Class
  60. *
  61. * Manages Prototype's class-based OOP system.
  62. *
  63. * Refer to Prototype's web site for a [tutorial on classes and
  64. * inheritance](http://prototypejs.org/learn/class-inheritance).
  65. **/
  66. (function(globalContext) {
  67. /* ------------------------------------ */
  68. /* Import from object.js */
  69. /* ------------------------------------ */
  70. var _toString = Object.prototype.toString,
  71. NULL_TYPE = 'Null',
  72. UNDEFINED_TYPE = 'Undefined',
  73. BOOLEAN_TYPE = 'Boolean',
  74. NUMBER_TYPE = 'Number',
  75. STRING_TYPE = 'String',
  76. OBJECT_TYPE = 'Object',
  77. FUNCTION_CLASS = '[object Function]';
  78. function isFunction(object) {
  79. return _toString.call(object) === FUNCTION_CLASS;
  80. }
  81. function extend(destination, source) {
  82. for (var property in source) if (source.hasOwnProperty(property)) // modify protect primitive slaughter
  83. destination[property] = source[property];
  84. return destination;
  85. }
  86. function keys(object) {
  87. if (Type(object) !== OBJECT_TYPE) { throw new TypeError(); }
  88. var results = [];
  89. for (var property in object) {
  90. if (object.hasOwnProperty(property)) {
  91. results.push(property);
  92. }
  93. }
  94. return results;
  95. }
  96. function Type(o) {
  97. switch(o) {
  98. case null: return NULL_TYPE;
  99. case (void 0): return UNDEFINED_TYPE;
  100. }
  101. var type = typeof o;
  102. switch(type) {
  103. case 'boolean': return BOOLEAN_TYPE;
  104. case 'number': return NUMBER_TYPE;
  105. case 'string': return STRING_TYPE;
  106. }
  107. return OBJECT_TYPE;
  108. }
  109. function isUndefined(object) {
  110. return typeof object === "undefined";
  111. }
  112. /* ------------------------------------ */
  113. /* Import from Function.js */
  114. /* ------------------------------------ */
  115. var slice = Array.prototype.slice;
  116. function argumentNames(fn) {
  117. var names = fn.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1]
  118. .replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '')
  119. .replace(/\s+/g, '').split(',');
  120. return names.length == 1 && !names[0] ? [] : names;
  121. }
  122. function wrap(fn, wrapper) {
  123. var __method = fn;
  124. return function() {
  125. var a = update([bind(__method, this)], arguments);
  126. return wrapper.apply(this, a);
  127. }
  128. }
  129. function update(array, args) {
  130. var arrayLength = array.length, length = args.length;
  131. while (length--) array[arrayLength + length] = args[length];
  132. return array;
  133. }
  134. function merge(array, args) {
  135. array = slice.call(array, 0);
  136. return update(array, args);
  137. }
  138. function bind(fn, context) {
  139. if (arguments.length < 2 && isUndefined(arguments[0])) return this;
  140. var __method = fn, args = slice.call(arguments, 2);
  141. return function() {
  142. var a = merge(args, arguments);
  143. return __method.apply(context, a);
  144. }
  145. }
  146. /* ------------------------------------ */
  147. /* Import from Prototype.js */
  148. /* ------------------------------------ */
  149. var emptyFunction = function(){};
  150. var Class = (function() {
  151. // Some versions of JScript fail to enumerate over properties, names of which
  152. // correspond to non-enumerable properties in the prototype chain
  153. var IS_DONTENUM_BUGGY = (function(){
  154. for (var p in { toString: 1 }) {
  155. // check actual property name, so that it works with augmented Object.prototype
  156. if (p === 'toString') return false;
  157. }
  158. return true;
  159. })();
  160. function subclass() {};
  161. function create() {
  162. var parent = null, properties = [].slice.apply(arguments);
  163. if (isFunction(properties[0]))
  164. parent = properties.shift();
  165. function klass() {
  166. this.initialize.apply(this, arguments);
  167. }
  168. extend(klass, Class.Methods);
  169. klass.superclass = parent;
  170. klass.subclasses = [];
  171. if (parent) {
  172. subclass.prototype = parent.prototype;
  173. klass.prototype = new subclass;
  174. try { parent.subclasses.push(klass) } catch(e) {}
  175. }
  176. for (var i = 0, length = properties.length; i < length; i++)
  177. klass.addMethods(properties[i]);
  178. if (!klass.prototype.initialize)
  179. klass.prototype.initialize = emptyFunction;
  180. klass.prototype.constructor = klass;
  181. return klass;
  182. }
  183. function addMethods(source) {
  184. var ancestor = this.superclass && this.superclass.prototype,
  185. properties = keys(source);
  186. // IE6 doesn't enumerate `toString` and `valueOf` (among other built-in `Object.prototype`) properties,
  187. // Force copy if they're not Object.prototype ones.
  188. // Do not copy other Object.prototype.* for performance reasons
  189. if (IS_DONTENUM_BUGGY) {
  190. if (source.toString != Object.prototype.toString)
  191. properties.push("toString");
  192. if (source.valueOf != Object.prototype.valueOf)
  193. properties.push("valueOf");
  194. }
  195. for (var i = 0, length = properties.length; i < length; i++) {
  196. var property = properties[i], value = source[property];
  197. if (ancestor && isFunction(value) &&
  198. argumentNames(value)[0] == "$super") {
  199. var method = value;
  200. value = wrap((function(m) {
  201. return function() { return ancestor[m].apply(this, arguments); };
  202. })(property), method);
  203. value.valueOf = bind(method.valueOf, method);
  204. value.toString = bind(method.toString, method);
  205. }
  206. this.prototype[property] = value;
  207. }
  208. return this;
  209. }
  210. return {
  211. create: create,
  212. Methods: {
  213. addMethods: addMethods
  214. }
  215. };
  216. })();
  217. if (globalContext.exports) {
  218. globalContext.exports.Class = Class;
  219. }
  220. else {
  221. globalContext.Class = Class;
  222. }
  223. })(Rickshaw);
  224. Rickshaw.namespace('Rickshaw.Compat.ClassList');
  225. Rickshaw.Compat.ClassList = function() {
  226. /* adapted from http://purl.eligrey.com/github/classList.js/blob/master/classList.js */
  227. if (typeof document !== "undefined" && !("classList" in document.createElement("a"))) {
  228. (function (view) {
  229. "use strict";
  230. var
  231. classListProp = "classList"
  232. , protoProp = "prototype"
  233. , elemCtrProto = (view.HTMLElement || view.Element)[protoProp]
  234. , objCtr = Object
  235. , strTrim = String[protoProp].trim || function () {
  236. return this.replace(/^\s+|\s+$/g, "");
  237. }
  238. , arrIndexOf = Array[protoProp].indexOf || function (item) {
  239. var
  240. i = 0
  241. , len = this.length
  242. ;
  243. for (; i < len; i++) {
  244. if (i in this && this[i] === item) {
  245. return i;
  246. }
  247. }
  248. return -1;
  249. }
  250. // Vendors: please allow content code to instantiate DOMExceptions
  251. , DOMEx = function (type, message) {
  252. this.name = type;
  253. this.code = DOMException[type];
  254. this.message = message;
  255. }
  256. , checkTokenAndGetIndex = function (classList, token) {
  257. if (token === "") {
  258. throw new DOMEx(
  259. "SYNTAX_ERR"
  260. , "An invalid or illegal string was specified"
  261. );
  262. }
  263. if (/\s/.test(token)) {
  264. throw new DOMEx(
  265. "INVALID_CHARACTER_ERR"
  266. , "String contains an invalid character"
  267. );
  268. }
  269. return arrIndexOf.call(classList, token);
  270. }
  271. , ClassList = function (elem) {
  272. var
  273. trimmedClasses = strTrim.call(elem.className)
  274. , classes = trimmedClasses ? trimmedClasses.split(/\s+/) : []
  275. , i = 0
  276. , len = classes.length
  277. ;
  278. for (; i < len; i++) {
  279. this.push(classes[i]);
  280. }
  281. this._updateClassName = function () {
  282. elem.className = this.toString();
  283. };
  284. }
  285. , classListProto = ClassList[protoProp] = []
  286. , classListGetter = function () {
  287. return new ClassList(this);
  288. }
  289. ;
  290. // Most DOMException implementations don't allow calling DOMException's toString()
  291. // on non-DOMExceptions. Error's toString() is sufficient here.
  292. DOMEx[protoProp] = Error[protoProp];
  293. classListProto.item = function (i) {
  294. return this[i] || null;
  295. };
  296. classListProto.contains = function (token) {
  297. token += "";
  298. return checkTokenAndGetIndex(this, token) !== -1;
  299. };
  300. classListProto.add = function (token) {
  301. token += "";
  302. if (checkTokenAndGetIndex(this, token) === -1) {
  303. this.push(token);
  304. this._updateClassName();
  305. }
  306. };
  307. classListProto.remove = function (token) {
  308. token += "";
  309. var index = checkTokenAndGetIndex(this, token);
  310. if (index !== -1) {
  311. this.splice(index, 1);
  312. this._updateClassName();
  313. }
  314. };
  315. classListProto.toggle = function (token) {
  316. token += "";
  317. if (checkTokenAndGetIndex(this, token) === -1) {
  318. this.add(token);
  319. } else {
  320. this.remove(token);
  321. }
  322. };
  323. classListProto.toString = function () {
  324. return this.join(" ");
  325. };
  326. if (objCtr.defineProperty) {
  327. var classListPropDesc = {
  328. get: classListGetter
  329. , enumerable: true
  330. , configurable: true
  331. };
  332. try {
  333. objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
  334. } catch (ex) { // IE 8 doesn't support enumerable:true
  335. if (ex.number === -0x7FF5EC54) {
  336. classListPropDesc.enumerable = false;
  337. objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
  338. }
  339. }
  340. } else if (objCtr[protoProp].__defineGetter__) {
  341. elemCtrProto.__defineGetter__(classListProp, classListGetter);
  342. }
  343. }(window));
  344. }
  345. };
  346. if ( (typeof RICKSHAW_NO_COMPAT !== "undefined" && !RICKSHAW_NO_COMPAT) || typeof RICKSHAW_NO_COMPAT === "undefined") {
  347. new Rickshaw.Compat.ClassList();
  348. }
  349. Rickshaw.namespace('Rickshaw.Graph');
  350. Rickshaw.Graph = function(args) {
  351. var self = this;
  352. this.initialize = function(args) {
  353. if (!args.element) throw "Rickshaw.Graph needs a reference to an element";
  354. if (args.element.nodeType !== 1) throw "Rickshaw.Graph element was defined but not an HTML element";
  355. this.element = args.element;
  356. this.series = args.series;
  357. this.window = {};
  358. this.updateCallbacks = [];
  359. this.configureCallbacks = [];
  360. this.defaults = {
  361. interpolation: 'cardinal',
  362. offset: 'zero',
  363. min: undefined,
  364. max: undefined,
  365. preserve: false,
  366. xScale: undefined,
  367. yScale: undefined,
  368. stack: true
  369. };
  370. this._loadRenderers();
  371. this.configure(args);
  372. this.setSeries(args.series);
  373. this.setSize({ width: args.width, height: args.height });
  374. this.element.classList.add('rickshaw_graph');
  375. this.vis = d3.select(this.element)
  376. .append("svg:svg")
  377. .attr('width', this.width)
  378. .attr('height', this.height);
  379. this.discoverRange();
  380. };
  381. this._loadRenderers = function() {
  382. for (var name in Rickshaw.Graph.Renderer) {
  383. if (!name || !Rickshaw.Graph.Renderer.hasOwnProperty(name)) continue;
  384. var r = Rickshaw.Graph.Renderer[name];
  385. if (!r || !r.prototype || !r.prototype.render) continue;
  386. self.registerRenderer(new r( { graph: self } ));
  387. }
  388. };
  389. this.validateSeries = function(series) {
  390. if (!Array.isArray(series) && !(series instanceof Rickshaw.Series)) {
  391. var seriesSignature = Object.prototype.toString.apply(series);
  392. throw "series is not an array: " + seriesSignature;
  393. }
  394. var pointsCount;
  395. series.forEach( function(s) {
  396. if (!(s instanceof Object)) {
  397. throw "series element is not an object: " + s;
  398. }
  399. if (!(s.data)) {
  400. throw "series has no data: " + JSON.stringify(s);
  401. }
  402. if (!Array.isArray(s.data)) {
  403. throw "series data is not an array: " + JSON.stringify(s.data);
  404. }
  405. if (s.data.length > 0) {
  406. var x = s.data[0].x;
  407. var y = s.data[0].y;
  408. if (typeof x != 'number' || ( typeof y != 'number' && y !== null ) ) {
  409. throw "x and y properties of points should be numbers instead of " +
  410. (typeof x) + " and " + (typeof y);
  411. }
  412. }
  413. if (s.data.length >= 3) {
  414. // probe to sanity check sort order
  415. if (s.data[2].x < s.data[1].x || s.data[1].x < s.data[0].x || s.data[s.data.length - 1].x < s.data[0].x) {
  416. throw "series data needs to be sorted on x values for series name: " + s.name;
  417. }
  418. }
  419. }, this );
  420. };
  421. this.setSeries = function(series) {
  422. this.validateSeries(series);
  423. this.series = series;
  424. this.series.active = function() { return self.series.filter( function(s) { return !s.disabled } ) };
  425. };
  426. this.dataDomain = function() {
  427. var data = this.series.map( function(s) { return s.data } );
  428. var min = d3.min( data.map( function(d) { return d[0].x } ) );
  429. var max = d3.max( data.map( function(d) { return d[d.length - 1].x } ) );
  430. return [min, max];
  431. };
  432. this.discoverRange = function() {
  433. var domain = this.renderer.domain();
  434. // this.*Scale is coming from the configuration dictionary
  435. // which may be referenced by the Graph creator, or shared
  436. // with other Graphs. We need to ensure we copy the scale
  437. // so that our mutations do not change the object given to us.
  438. // Hence the .copy()
  439. this.x = (this.xScale || d3.scale.linear()).copy().domain(domain.x).range([0, this.width]);
  440. this.y = (this.yScale || d3.scale.linear()).copy().domain(domain.y).range([this.height, 0]);
  441. this.x.magnitude = d3.scale.linear()
  442. .domain([domain.x[0] - domain.x[0], domain.x[1] - domain.x[0]])
  443. .range([0, this.width]);
  444. this.y.magnitude = d3.scale.linear()
  445. .domain([domain.y[0] - domain.y[0], domain.y[1] - domain.y[0]])
  446. .range([0, this.height]);
  447. };
  448. this.render = function() {
  449. var stackedData = this.stackData();
  450. this.discoverRange();
  451. this.renderer.render();
  452. this.updateCallbacks.forEach( function(callback) {
  453. callback();
  454. } );
  455. };
  456. this.update = this.render;
  457. this.stackData = function() {
  458. var data = this.series.active()
  459. .map( function(d) { return d.data } )
  460. .map( function(d) { return d.filter( function(d) { return this._slice(d) }, this ) }, this);
  461. var preserve = this.preserve;
  462. if (!preserve) {
  463. this.series.forEach( function(series) {
  464. if (series.scale) {
  465. // data must be preserved when a scale is used
  466. preserve = true;
  467. }
  468. } );
  469. }
  470. data = preserve ? Rickshaw.clone(data) : data;
  471. this.series.active().forEach( function(series, index) {
  472. if (series.scale) {
  473. // apply scale to each series
  474. var seriesData = data[index];
  475. if(seriesData) {
  476. seriesData.forEach( function(d) {
  477. d.y = series.scale(d.y);
  478. } );
  479. }
  480. }
  481. } );
  482. this.stackData.hooks.data.forEach( function(entry) {
  483. data = entry.f.apply(self, [data]);
  484. } );
  485. var stackedData;
  486. if (!this.renderer.unstack) {
  487. this._validateStackable();
  488. var layout = d3.layout.stack();
  489. layout.offset( self.offset );
  490. stackedData = layout(data);
  491. }
  492. stackedData = stackedData || data;
  493. if (this.renderer.unstack) {
  494. stackedData.forEach( function(seriesData) {
  495. seriesData.forEach( function(d) {
  496. d.y0 = d.y0 === undefined ? 0 : d.y0;
  497. } );
  498. } );
  499. }
  500. this.stackData.hooks.after.forEach( function(entry) {
  501. stackedData = entry.f.apply(self, [data]);
  502. } );
  503. var i = 0;
  504. this.series.forEach( function(series) {
  505. if (series.disabled) return;
  506. series.stack = stackedData[i++];
  507. } );
  508. this.stackedData = stackedData;
  509. return stackedData;
  510. };
  511. this._validateStackable = function() {
  512. var series = this.series;
  513. var pointsCount;
  514. series.forEach( function(s) {
  515. pointsCount = pointsCount || s.data.length;
  516. if (pointsCount && s.data.length != pointsCount) {
  517. throw "stacked series cannot have differing numbers of points: " +
  518. pointsCount + " vs " + s.data.length + "; see Rickshaw.Series.fill()";
  519. }
  520. }, this );
  521. };
  522. this.stackData.hooks = { data: [], after: [] };
  523. this._slice = function(d) {
  524. if (this.window.xMin || this.window.xMax) {
  525. var isInRange = true;
  526. if (this.window.xMin && d.x < this.window.xMin) isInRange = false;
  527. if (this.window.xMax && d.x > this.window.xMax) isInRange = false;
  528. return isInRange;
  529. }
  530. return true;
  531. };
  532. this.onUpdate = function(callback) {
  533. this.updateCallbacks.push(callback);
  534. };
  535. this.onConfigure = function(callback) {
  536. this.configureCallbacks.push(callback);
  537. };
  538. this.registerRenderer = function(renderer) {
  539. this._renderers = this._renderers || {};
  540. this._renderers[renderer.name] = renderer;
  541. };
  542. this.configure = function(args) {
  543. this.config = this.config || {};
  544. if (args.width || args.height) {
  545. this.setSize(args);
  546. }
  547. Rickshaw.keys(this.defaults).forEach( function(k) {
  548. this.config[k] = k in args ? args[k]
  549. : k in this ? this[k]
  550. : this.defaults[k];
  551. }, this );
  552. Rickshaw.keys(this.config).forEach( function(k) {
  553. this[k] = this.config[k];
  554. }, this );
  555. if ('stack' in args) args.unstack = !args.stack;
  556. var renderer = args.renderer || (this.renderer && this.renderer.name) || 'stack';
  557. this.setRenderer(renderer, args);
  558. this.configureCallbacks.forEach( function(callback) {
  559. callback(args);
  560. } );
  561. };
  562. this.setRenderer = function(r, args) {
  563. if (typeof r == 'function') {
  564. this.renderer = new r( { graph: self } );
  565. this.registerRenderer(this.renderer);
  566. } else {
  567. if (!this._renderers[r]) {
  568. throw "couldn't find renderer " + r;
  569. }
  570. this.renderer = this._renderers[r];
  571. }
  572. if (typeof args == 'object') {
  573. this.renderer.configure(args);
  574. }
  575. };
  576. this.setSize = function(args) {
  577. args = args || {};
  578. if (args.width && args.height) {
  579. // use explicitly specified size
  580. this.width = args.width;
  581. this.height = args.height;
  582. } else {
  583. // calc size (will cause layout reflow)
  584. if (typeof window !== 'undefined') {
  585. var style = window.getComputedStyle(this.element, null);
  586. var elementWidth = parseInt(style.getPropertyValue('width'), 10);
  587. var elementHeight = parseInt(style.getPropertyValue('height'), 10);
  588. }
  589. this.width = args.width || elementWidth || 400;
  590. this.height = args.height || elementHeight || 250;
  591. }
  592. this.vis && this.vis
  593. .attr('width', this.width)
  594. .attr('height', this.height);
  595. };
  596. this.initialize(args);
  597. };
  598. Rickshaw.namespace('Rickshaw.Fixtures.Color');
  599. Rickshaw.Fixtures.Color = function() {
  600. this.schemes = {};
  601. this.schemes.spectrum14 = [
  602. '#ecb796',
  603. '#dc8f70',
  604. '#b2a470',
  605. '#92875a',
  606. '#716c49',
  607. '#d2ed82',
  608. '#bbe468',
  609. '#a1d05d',
  610. '#e7cbe6',
  611. '#d8aad6',
  612. '#a888c2',
  613. '#9dc2d3',
  614. '#649eb9',
  615. '#387aa3'
  616. ].reverse();
  617. this.schemes.spectrum2000 = [
  618. '#57306f',
  619. '#514c76',
  620. '#646583',
  621. '#738394',
  622. '#6b9c7d',
  623. '#84b665',
  624. '#a7ca50',
  625. '#bfe746',
  626. '#e2f528',
  627. '#fff726',
  628. '#ecdd00',
  629. '#d4b11d',
  630. '#de8800',
  631. '#de4800',
  632. '#c91515',
  633. '#9a0000',
  634. '#7b0429',
  635. '#580839',
  636. '#31082b'
  637. ];
  638. this.schemes.spectrum2001 = [
  639. '#2f243f',
  640. '#3c2c55',
  641. '#4a3768',
  642. '#565270',
  643. '#6b6b7c',
  644. '#72957f',
  645. '#86ad6e',
  646. '#a1bc5e',
  647. '#b8d954',
  648. '#d3e04e',
  649. '#ccad2a',
  650. '#cc8412',
  651. '#c1521d',
  652. '#ad3821',
  653. '#8a1010',
  654. '#681717',
  655. '#531e1e',
  656. '#3d1818',
  657. '#320a1b'
  658. ];
  659. this.schemes.classic9 = [
  660. '#423d4f',
  661. '#4a6860',
  662. '#848f39',
  663. '#a2b73c',
  664. '#ddcb53',
  665. '#c5a32f',
  666. '#7d5836',
  667. '#963b20',
  668. '#7c2626',
  669. '#491d37',
  670. '#2f254a'
  671. ].reverse();
  672. this.schemes.httpStatus = {
  673. 503: '#ea5029',
  674. 502: '#d23f14',
  675. 500: '#bf3613',
  676. 410: '#efacea',
  677. 409: '#e291dc',
  678. 403: '#f457e8',
  679. 408: '#e121d2',
  680. 401: '#b92dae',
  681. 405: '#f47ceb',
  682. 404: '#a82a9f',
  683. 400: '#b263c6',
  684. 301: '#6fa024',
  685. 302: '#87c32b',
  686. 307: '#a0d84c',
  687. 304: '#28b55c',
  688. 200: '#1a4f74',
  689. 206: '#27839f',
  690. 201: '#52adc9',
  691. 202: '#7c979f',
  692. 203: '#a5b8bd',
  693. 204: '#c1cdd1'
  694. };
  695. this.schemes.colorwheel = [
  696. '#b5b6a9',
  697. '#858772',
  698. '#785f43',
  699. '#96557e',
  700. '#4682b4',
  701. '#65b9ac',
  702. '#73c03a',
  703. '#cb513a'
  704. ].reverse();
  705. this.schemes.cool = [
  706. '#5e9d2f',
  707. '#73c03a',
  708. '#4682b4',
  709. '#7bc3b8',
  710. '#a9884e',
  711. '#c1b266',
  712. '#a47493',
  713. '#c09fb5'
  714. ];
  715. this.schemes.munin = [
  716. '#00cc00',
  717. '#0066b3',
  718. '#ff8000',
  719. '#ffcc00',
  720. '#330099',
  721. '#990099',
  722. '#ccff00',
  723. '#ff0000',
  724. '#808080',
  725. '#008f00',
  726. '#00487d',
  727. '#b35a00',
  728. '#b38f00',
  729. '#6b006b',
  730. '#8fb300',
  731. '#b30000',
  732. '#bebebe',
  733. '#80ff80',
  734. '#80c9ff',
  735. '#ffc080',
  736. '#ffe680',
  737. '#aa80ff',
  738. '#ee00cc',
  739. '#ff8080',
  740. '#666600',
  741. '#ffbfff',
  742. '#00ffcc',
  743. '#cc6699',
  744. '#999900'
  745. ];
  746. };
  747. Rickshaw.namespace('Rickshaw.Fixtures.RandomData');
  748. Rickshaw.Fixtures.RandomData = function(timeInterval) {
  749. var addData;
  750. timeInterval = timeInterval || 1;
  751. var lastRandomValue = 200;
  752. var timeBase = Math.floor(new Date().getTime() / 1000);
  753. this.addData = function(data) {
  754. var randomValue = Math.random() * 100 + 15 + lastRandomValue;
  755. var index = data[0].length;
  756. var counter = 1;
  757. data.forEach( function(series) {
  758. var randomVariance = Math.random() * 20;
  759. var v = randomValue / 25 + counter++ +
  760. (Math.cos((index * counter * 11) / 960) + 2) * 15 +
  761. (Math.cos(index / 7) + 2) * 7 +
  762. (Math.cos(index / 17) + 2) * 1;
  763. series.push( { x: (index * timeInterval) + timeBase, y: v + randomVariance } );
  764. } );
  765. lastRandomValue = randomValue * 0.85;
  766. };
  767. this.removeData = function(data) {
  768. data.forEach( function(series) {
  769. series.shift();
  770. } );
  771. timeBase += timeInterval;
  772. };
  773. };
  774. Rickshaw.namespace('Rickshaw.Fixtures.Time');
  775. Rickshaw.Fixtures.Time = function() {
  776. var self = this;
  777. this.months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
  778. this.units = [
  779. {
  780. name: 'decade',
  781. seconds: 86400 * 365.25 * 10,
  782. formatter: function(d) { return (parseInt(d.getUTCFullYear() / 10, 10) * 10) }
  783. }, {
  784. name: 'year',
  785. seconds: 86400 * 365.25,
  786. formatter: function(d) { return d.getUTCFullYear() }
  787. }, {
  788. name: 'month',
  789. seconds: 86400 * 30.5,
  790. formatter: function(d) { return self.months[d.getUTCMonth()] }
  791. }, {
  792. name: 'week',
  793. seconds: 86400 * 7,
  794. formatter: function(d) { return self.formatDate(d) }
  795. }, {
  796. name: 'day',
  797. seconds: 86400,
  798. formatter: function(d) { return d.getUTCDate() }
  799. }, {
  800. name: '6 hour',
  801. seconds: 3600 * 6,
  802. formatter: function(d) { return self.formatTime(d) }
  803. }, {
  804. name: 'hour',
  805. seconds: 3600,
  806. formatter: function(d) { return self.formatTime(d) }
  807. }, {
  808. name: '15 minute',
  809. seconds: 60 * 15,
  810. formatter: function(d) { return self.formatTime(d) }
  811. }, {
  812. name: 'minute',
  813. seconds: 60,
  814. formatter: function(d) { return d.getUTCMinutes() + 'm' }
  815. }, {
  816. name: '15 second',
  817. seconds: 15,
  818. formatter: function(d) { return d.getUTCSeconds() + 's' }
  819. }, {
  820. name: 'second',
  821. seconds: 1,
  822. formatter: function(d) { return d.getUTCSeconds() + 's' }
  823. }, {
  824. name: 'decisecond',
  825. seconds: 1/10,
  826. formatter: function(d) { return d.getUTCMilliseconds() + 'ms' }
  827. }, {
  828. name: 'centisecond',
  829. seconds: 1/100,
  830. formatter: function(d) { return d.getUTCMilliseconds() + 'ms' }
  831. }
  832. ];
  833. this.unit = function(unitName) {
  834. return this.units.filter( function(unit) { return unitName == unit.name } ).shift();
  835. };
  836. this.formatDate = function(d) {
  837. return d3.time.format('%b %e')(d);
  838. };
  839. this.formatTime = function(d) {
  840. return d.toUTCString().match(/(\d+:\d+):/)[1];
  841. };
  842. this.ceil = function(time, unit) {
  843. var date, floor, year;
  844. if (unit.name == 'month') {
  845. date = new Date(time * 1000);
  846. floor = Date.UTC(date.getUTCFullYear(), date.getUTCMonth()) / 1000;
  847. if (floor == time) return time;
  848. year = date.getUTCFullYear();
  849. var month = date.getUTCMonth();
  850. if (month == 11) {
  851. month = 0;
  852. year = year + 1;
  853. } else {
  854. month += 1;
  855. }
  856. return Date.UTC(year, month) / 1000;
  857. }
  858. if (unit.name == 'year') {
  859. date = new Date(time * 1000);
  860. floor = Date.UTC(date.getUTCFullYear(), 0) / 1000;
  861. if (floor == time) return time;
  862. year = date.getUTCFullYear() + 1;
  863. return Date.UTC(year, 0) / 1000;
  864. }
  865. return Math.ceil(time / unit.seconds) * unit.seconds;
  866. };
  867. };
  868. Rickshaw.namespace('Rickshaw.Fixtures.Time.Local');
  869. Rickshaw.Fixtures.Time.Local = function() {
  870. var self = this;
  871. this.months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
  872. this.units = [
  873. {
  874. name: 'decade',
  875. seconds: 86400 * 365.25 * 10,
  876. formatter: function(d) { return (parseInt(d.getFullYear() / 10, 10) * 10) }
  877. }, {
  878. name: 'year',
  879. seconds: 86400 * 365.25,
  880. formatter: function(d) { return d.getFullYear() }
  881. }, {
  882. name: 'month',
  883. seconds: 86400 * 30.5,
  884. formatter: function(d) { return self.months[d.getMonth()] }
  885. }, {
  886. name: 'week',
  887. seconds: 86400 * 7,
  888. formatter: function(d) { return self.formatDate(d) }
  889. }, {
  890. name: 'day',
  891. seconds: 86400,
  892. formatter: function(d) { return d.getDate() }
  893. }, {
  894. name: '6 hour',
  895. seconds: 3600 * 6,
  896. formatter: function(d) { return self.formatTime(d) }
  897. }, {
  898. name: 'hour',
  899. seconds: 3600,
  900. formatter: function(d) { return self.formatTime(d) }
  901. }, {
  902. name: '15 minute',
  903. seconds: 60 * 15,
  904. formatter: function(d) { return self.formatTime(d) }
  905. }, {
  906. name: 'minute',
  907. seconds: 60,
  908. formatter: function(d) { return d.getMinutes() }
  909. }, {
  910. name: '15 second',
  911. seconds: 15,
  912. formatter: function(d) { return d.getSeconds() + 's' }
  913. }, {
  914. name: 'second',
  915. seconds: 1,
  916. formatter: function(d) { return d.getSeconds() + 's' }
  917. }, {
  918. name: 'decisecond',
  919. seconds: 1/10,
  920. formatter: function(d) { return d.getMilliseconds() + 'ms' }
  921. }, {
  922. name: 'centisecond',
  923. seconds: 1/100,
  924. formatter: function(d) { return d.getMilliseconds() + 'ms' }
  925. }
  926. ];
  927. this.unit = function(unitName) {
  928. return this.units.filter( function(unit) { return unitName == unit.name } ).shift();
  929. };
  930. this.formatDate = function(d) {
  931. return d3.time.format('%b %e')(d);
  932. };
  933. this.formatTime = function(d) {
  934. return d.toString().match(/(\d+:\d+):/)[1];
  935. };
  936. this.ceil = function(time, unit) {
  937. var date, floor, year, offset;
  938. if (unit.name == 'day') {
  939. var nearFuture = new Date((time + unit.seconds - 1) * 1000);
  940. var rounded = new Date(0);
  941. rounded.setFullYear(nearFuture.getFullYear());
  942. rounded.setMonth(nearFuture.getMonth());
  943. rounded.setDate(nearFuture.getDate());
  944. rounded.setMilliseconds(0);
  945. rounded.setSeconds(0);
  946. rounded.setMinutes(0);
  947. rounded.setHours(0);
  948. return rounded.getTime() / 1000;
  949. }
  950. if (unit.name == 'month') {
  951. date = new Date(time * 1000);
  952. floor = new Date(date.getFullYear(), date.getMonth()).getTime() / 1000;
  953. if (floor == time) return time;
  954. year = date.getFullYear();
  955. var month = date.getMonth();
  956. if (month == 11) {
  957. month = 0;
  958. year = year + 1;
  959. } else {
  960. month += 1;
  961. }
  962. return new Date(year, month).getTime() / 1000;
  963. }
  964. if (unit.name == 'year') {
  965. date = new Date(time * 1000);
  966. floor = new Date(date.getUTCFullYear(), 0).getTime() / 1000;
  967. if (floor == time) return time;
  968. year = date.getFullYear() + 1;
  969. return new Date(year, 0).getTime() / 1000;
  970. }
  971. offset = new Date(time * 1000).getTimezoneOffset() * 60;
  972. return Math.ceil((time - offset) / unit.seconds) * unit.seconds + offset;
  973. };
  974. };
  975. Rickshaw.namespace('Rickshaw.Fixtures.Number');
  976. Rickshaw.Fixtures.Number.formatKMBT = function(y) {
  977. var abs_y = Math.abs(y);
  978. if (abs_y >= 1000000000000) { return y / 1000000000000 + "T" }
  979. else if (abs_y >= 1000000000) { return y / 1000000000 + "B" }
  980. else if (abs_y >= 1000000) { return y / 1000000 + "M" }
  981. else if (abs_y >= 1000) { return y / 1000 + "K" }
  982. else if (abs_y < 1 && abs_y > 0) { return y.toFixed(2) }
  983. else if (abs_y === 0) { return '' }
  984. else { return y }
  985. };
  986. Rickshaw.Fixtures.Number.formatBase1024KMGTP = function(y) {
  987. var abs_y = Math.abs(y);
  988. if (abs_y >= 1125899906842624) { return y / 1125899906842624 + "P" }
  989. else if (abs_y >= 1099511627776){ return y / 1099511627776 + "T" }
  990. else if (abs_y >= 1073741824) { return y / 1073741824 + "G" }
  991. else if (abs_y >= 1048576) { return y / 1048576 + "M" }
  992. else if (abs_y >= 1024) { return y / 1024 + "K" }
  993. else if (abs_y < 1 && abs_y > 0) { return y.toFixed(2) }
  994. else if (abs_y === 0) { return '' }
  995. else { return y }
  996. };
  997. Rickshaw.namespace("Rickshaw.Color.Palette");
  998. Rickshaw.Color.Palette = function(args) {
  999. var color = new Rickshaw.Fixtures.Color();
  1000. args = args || {};
  1001. this.schemes = {};
  1002. this.scheme = color.schemes[args.scheme] || args.scheme || color.schemes.colorwheel;
  1003. this.runningIndex = 0;
  1004. this.generatorIndex = 0;
  1005. if (args.interpolatedStopCount) {
  1006. var schemeCount = this.scheme.length - 1;
  1007. var i, j, scheme = [];
  1008. for (i = 0; i < schemeCount; i++) {
  1009. scheme.push(this.scheme[i]);
  1010. var generator = d3.interpolateHsl(this.scheme[i], this.scheme[i + 1]);
  1011. for (j = 1; j < args.interpolatedStopCount; j++) {
  1012. scheme.push(generator((1 / args.interpolatedStopCount) * j));
  1013. }
  1014. }
  1015. scheme.push(this.scheme[this.scheme.length - 1]);
  1016. this.scheme = scheme;
  1017. }
  1018. this.rotateCount = this.scheme.length;
  1019. this.color = function(key) {
  1020. return this.scheme[key] || this.scheme[this.runningIndex++] || this.interpolateColor() || '#808080';
  1021. };
  1022. this.interpolateColor = function() {
  1023. if (!Array.isArray(this.scheme)) return;
  1024. var color;
  1025. if (this.generatorIndex == this.rotateCount * 2 - 1) {
  1026. color = d3.interpolateHsl(this.scheme[this.generatorIndex], this.scheme[0])(0.5);
  1027. this.generatorIndex = 0;
  1028. this.rotateCount *= 2;
  1029. } else {
  1030. color = d3.interpolateHsl(this.scheme[this.generatorIndex], this.scheme[this.generatorIndex + 1])(0.5);
  1031. this.generatorIndex++;
  1032. }
  1033. this.scheme.push(color);
  1034. return color;
  1035. };
  1036. };
  1037. Rickshaw.namespace('Rickshaw.Graph.Ajax');
  1038. Rickshaw.Graph.Ajax = Rickshaw.Class.create( {
  1039. initialize: function(args) {
  1040. this.dataURL = args.dataURL;
  1041. this.onData = args.onData || function(d) { return d };
  1042. this.onComplete = args.onComplete || function() {};
  1043. this.onError = args.onError || function() {};
  1044. this.args = args; // pass through to Rickshaw.Graph
  1045. this.request();
  1046. },
  1047. request: function() {
  1048. jQuery.ajax( {
  1049. url: this.dataURL,
  1050. dataType: 'json',
  1051. success: this.success.bind(this),
  1052. error: this.error.bind(this)
  1053. } );
  1054. },
  1055. error: function() {
  1056. console.log("error loading dataURL: " + this.dataURL);
  1057. this.onError(this);
  1058. },
  1059. success: function(data, status) {
  1060. data = this.onData(data);
  1061. this.args.series = this._splice({ data: data, series: this.args.series });
  1062. this.graph = this.graph || new Rickshaw.Graph(this.args);
  1063. this.graph.render();
  1064. this.onComplete(this);
  1065. },
  1066. _splice: function(args) {
  1067. var data = args.data;
  1068. var series = args.series;
  1069. if (!args.series) return data;
  1070. series.forEach( function(s) {
  1071. var seriesKey = s.key || s.name;
  1072. if (!seriesKey) throw "series needs a key or a name";
  1073. data.forEach( function(d) {
  1074. var dataKey = d.key || d.name;
  1075. if (!dataKey) throw "data needs a key or a name";
  1076. if (seriesKey == dataKey) {
  1077. var properties = ['color', 'name', 'data'];
  1078. properties.forEach( function(p) {
  1079. if (d[p]) s[p] = d[p];
  1080. } );
  1081. }
  1082. } );
  1083. } );
  1084. return series;
  1085. }
  1086. } );
  1087. Rickshaw.namespace('Rickshaw.Graph.Annotate');
  1088. Rickshaw.Graph.Annotate = function(args) {
  1089. var graph = this.graph = args.graph;
  1090. this.elements = { timeline: args.element };
  1091. var self = this;
  1092. this.data = {};
  1093. this.elements.timeline.classList.add('rickshaw_annotation_timeline');
  1094. this.add = function(time, content, end_time) {
  1095. self.data[time] = self.data[time] || {'boxes': []};
  1096. self.data[time].boxes.push({content: content, end: end_time});
  1097. };
  1098. this.update = function() {
  1099. Rickshaw.keys(self.data).forEach( function(time) {
  1100. var annotation = self.data[time];
  1101. var left = self.graph.x(time);
  1102. if (left < 0 || left > self.graph.x.range()[1]) {
  1103. if (annotation.element) {
  1104. annotation.line.classList.add('offscreen');
  1105. annotation.element.style.display = 'none';
  1106. }
  1107. annotation.boxes.forEach( function(box) {
  1108. if ( box.rangeElement ) box.rangeElement.classList.add('offscreen');
  1109. });
  1110. return;
  1111. }
  1112. if (!annotation.element) {
  1113. var element = annotation.element = document.createElement('div');
  1114. element.classList.add('annotation');
  1115. this.elements.timeline.appendChild(element);
  1116. element.addEventListener('click', function(e) {
  1117. element.classList.toggle('active');
  1118. annotation.line.classList.toggle('active');
  1119. annotation.boxes.forEach( function(box) {
  1120. if ( box.rangeElement ) box.rangeElement.classList.toggle('active');
  1121. });
  1122. }, false);
  1123. }
  1124. annotation.element.style.left = left + 'px';
  1125. annotation.element.style.display = 'block';
  1126. annotation.boxes.forEach( function(box) {
  1127. var element = box.element;
  1128. if (!element) {
  1129. element = box.element = document.createElement('div');
  1130. element.classList.add('content');
  1131. element.innerHTML = box.content;
  1132. annotation.element.appendChild(element);
  1133. annotation.line = document.createElement('div');
  1134. annotation.line.classList.add('annotation_line');
  1135. self.graph.element.appendChild(annotation.line);
  1136. if ( box.end ) {
  1137. box.rangeElement = document.createElement('div');
  1138. box.rangeElement.classList.add('annotation_range');
  1139. self.graph.element.appendChild(box.rangeElement);
  1140. }
  1141. }
  1142. if ( box.end ) {
  1143. var annotationRangeStart = left;
  1144. var annotationRangeEnd = Math.min( self.graph.x(box.end), self.graph.x.range()[1] );
  1145. // annotation makes more sense at end
  1146. if ( annotationRangeStart > annotationRangeEnd ) {
  1147. annotationRangeEnd = left;
  1148. annotationRangeStart = Math.max( self.graph.x(box.end), self.graph.x.range()[0] );
  1149. }
  1150. var annotationRangeWidth = annotationRangeEnd - annotationRangeStart;
  1151. box.rangeElement.style.left = annotationRangeStart + 'px';
  1152. box.rangeElement.style.width = annotationRangeWidth + 'px';
  1153. box.rangeElement.classList.remove('offscreen');
  1154. }
  1155. annotation.line.classList.remove('offscreen');
  1156. annotation.line.style.left = left + 'px';
  1157. } );
  1158. }, this );
  1159. };
  1160. this.graph.onUpdate( function() { self.update() } );
  1161. };
  1162. Rickshaw.namespace('Rickshaw.Graph.Axis.Time');
  1163. Rickshaw.Graph.Axis.Time = function(args) {
  1164. var self = this;
  1165. this.graph = args.graph;
  1166. this.elements = [];
  1167. this.ticksTreatment = args.ticksTreatment || 'plain';
  1168. this.fixedTimeUnit = args.timeUnit;
  1169. var time = args.timeFixture || new Rickshaw.Fixtures.Time();
  1170. this.appropriateTimeUnit = function() {
  1171. var unit;
  1172. var units = time.units;
  1173. var domain = this.graph.x.domain();
  1174. var rangeSeconds = domain[1] - domain[0];
  1175. units.forEach( function(u) {
  1176. if (Math.floor(rangeSeconds / u.seconds) >= 2) {
  1177. unit = unit || u;
  1178. }
  1179. } );
  1180. return (unit || time.units[time.units.length - 1]);
  1181. };
  1182. this.tickOffsets = function() {
  1183. var domain = this.graph.x.domain();
  1184. var unit = this.fixedTimeUnit || this.appropriateTimeUnit();
  1185. var count = Math.ceil((domain[1] - domain[0]) / unit.seconds);
  1186. var runningTick = domain[0];
  1187. var offsets = [];
  1188. for (var i = 0; i < count; i++) {
  1189. var tickValue = time.ceil(runningTick, unit);
  1190. runningTick = tickValue + unit.seconds / 2;
  1191. offsets.push( { value: tickValue, unit: unit } );
  1192. }
  1193. return offsets;
  1194. };
  1195. this.render = function() {
  1196. this.elements.forEach( function(e) {
  1197. e.parentNode.removeChild(e);
  1198. } );
  1199. this.elements = [];
  1200. var offsets = this.tickOffsets();
  1201. offsets.forEach( function(o) {
  1202. if (self.graph.x(o.value) > self.graph.x.range()[1]) return;
  1203. var element = document.createElement('div');
  1204. element.style.left = self.graph.x(o.value) + 'px';
  1205. element.classList.add('x_tick');
  1206. element.classList.add(self.ticksTreatment);
  1207. var title = document.createElement('div');
  1208. title.classList.add('title');
  1209. title.innerHTML = o.unit.formatter(new Date(o.value * 1000));
  1210. element.appendChild(title);
  1211. self.graph.element.appendChild(element);
  1212. self.elements.push(element);
  1213. } );
  1214. };
  1215. this.graph.onUpdate( function() { self.render() } );
  1216. };
  1217. Rickshaw.namespace('Rickshaw.Graph.Axis.X');
  1218. Rickshaw.Graph.Axis.X = function(args) {
  1219. var self = this;
  1220. var berthRate = 0.10;
  1221. this.initialize = function(args) {
  1222. this.graph = args.graph;
  1223. this.orientation = args.orientation || 'top';
  1224. this.pixelsPerTick = args.pixelsPerTick || 75;
  1225. if (args.ticks) this.staticTicks = args.ticks;
  1226. if (args.tickValues) this.tickValues = args.tickValues;
  1227. this.tickSize = args.tickSize || 4;
  1228. this.ticksTreatment = args.ticksTreatment || 'plain';
  1229. if (args.element) {
  1230. this.element = args.element;
  1231. this._discoverSize(args.element, args);
  1232. this.vis = d3.select(args.element)
  1233. .append("svg:svg")
  1234. .attr('height', this.height)
  1235. .attr('width', this.width)
  1236. .attr('class', 'rickshaw_graph x_axis_d3');
  1237. this.element = this.vis[0][0];
  1238. this.element.style.position = 'relative';
  1239. this.setSize({ width: args.width, height: args.height });
  1240. } else {
  1241. this.vis = this.graph.vis;
  1242. }
  1243. this.graph.onUpdate( function() { self.render() } );
  1244. };
  1245. this.setSize = function(args) {
  1246. args = args || {};
  1247. if (!this.element) return;
  1248. this._discoverSize(this.element.parentNode, args);
  1249. this.vis
  1250. .attr('height', this.height)
  1251. .attr('width', this.width * (1 + berthRate));
  1252. var berth = Math.floor(this.width * berthRate / 2);
  1253. this.element.style.left = -1 * berth + 'px';
  1254. };
  1255. this.render = function() {
  1256. if (this._renderWidth !== undefined && this.graph.width !== this._renderWidth) this.setSize({ auto: true });
  1257. var axis = d3.svg.axis().scale(this.graph.x).orient(this.orientation);
  1258. axis.tickFormat( args.tickFormat || function(x) { return x } );
  1259. if (this.tickValues) axis.tickValues(this.tickValues);
  1260. this.ticks = this.staticTicks || Math.floor(this.graph.width / this.pixelsPerTick);
  1261. var berth = Math.floor(this.width * berthRate / 2) || 0;
  1262. var bar_offset = this.graph.renderer.name == "bar" && Math.ceil(this.graph.width * 0.95 / this.graph.series[0].data.length / 2) || 0;
  1263. var transform;
  1264. if (this.orientation == 'top') {
  1265. var yOffset = this.height || this.graph.height;
  1266. transform = 'translate(' + (berth + bar_offset) + ',' + yOffset + ')';
  1267. } else {
  1268. transform = 'translate(' + (berth + bar_offset) + ', 0)';
  1269. }
  1270. if (this.element) {
  1271. this.vis.selectAll('*').remove();
  1272. }
  1273. this.vis
  1274. .append("svg:g")
  1275. .attr("class", ["x_ticks_d3", this.ticksTreatment].join(" "))
  1276. .attr("transform", transform)
  1277. .call(axis.ticks(this.ticks).tickSubdivide(0).tickSize(this.tickSize));
  1278. var gridSize = (this.orientation == 'bottom' ? 1 : -1) * this.graph.height;
  1279. this.graph.vis
  1280. .append("svg:g")
  1281. .attr("class", "x_grid_d3")
  1282. .call(axis.ticks(this.ticks).tickSubdivide(0).tickSize(gridSize))
  1283. .selectAll('text')
  1284. .each(function() { this.parentNode.setAttribute('data-x-value', this.textContent) });
  1285. this._renderHeight = this.graph.height;
  1286. };
  1287. this._discoverSize = function(element, args) {
  1288. if (typeof window !== 'undefined') {
  1289. var style = window.getComputedStyle(element, null);
  1290. var elementHeight = parseInt(style.getPropertyValue('height'), 10);
  1291. if (!args.auto) {
  1292. var elementWidth = parseInt(style.getPropertyValue('width'), 10);
  1293. }
  1294. }
  1295. this.width = (args.width || elementWidth || this.graph.width) * (1 + berthRate);
  1296. this.height = args.height || elementHeight || 40;
  1297. };
  1298. this.initialize(args);
  1299. };
  1300. Rickshaw.namespace('Rickshaw.Graph.Axis.Y');
  1301. Rickshaw.Graph.Axis.Y = Rickshaw.Class.create( {
  1302. initialize: function(args) {
  1303. this.graph = args.graph;
  1304. this.orientation = args.orientation || 'right';
  1305. this.pixelsPerTick = args.pixelsPerTick || 75;
  1306. if (args.ticks) this.staticTicks = args.ticks;
  1307. if (args.tickValues) this.tickValues = args.tickValues;
  1308. this.tickSize = args.tickSize || 4;
  1309. this.ticksTreatment = args.ticksTreatment || 'plain';
  1310. this.tickFormat = args.tickFormat || function(y) { return y };
  1311. this.berthRate = 0.10;
  1312. if (args.element) {
  1313. this.element = args.element;
  1314. this.vis = d3.select(args.element)
  1315. .append("svg:svg")
  1316. .attr('class', 'rickshaw_graph y_axis');
  1317. this.element = this.vis[0][0];
  1318. this.element.style.position = 'relative';
  1319. this.setSize({ width: args.width, height: args.height });
  1320. } else {
  1321. this.vis = this.graph.vis;
  1322. }
  1323. var self = this;
  1324. this.graph.onUpdate( function() { self.render() } );
  1325. },
  1326. setSize: function(args) {
  1327. args = args || {};
  1328. if (!this.element) return;
  1329. if (typeof window !== 'undefined') {
  1330. var style = window.getComputedStyle(this.element.parentNode, null);
  1331. var elementWidth = parseInt(style.getPropertyValue('width'), 10);
  1332. if (!args.auto) {
  1333. var elementHeight = parseInt(style.getPropertyValue('height'), 10);
  1334. }
  1335. }
  1336. this.width = args.width || elementWidth || this.graph.width * this.berthRate;
  1337. this.height = args.height || elementHeight || this.graph.height;
  1338. this.vis
  1339. .attr('width', this.width)
  1340. .attr('height', this.height * (1 + this.berthRate));
  1341. var berth = this.height * this.berthRate;
  1342. if (this.orientation == 'left') {
  1343. this.element.style.top = -1 * berth + 'px';
  1344. }
  1345. },
  1346. render: function() {
  1347. if (this._renderHeight !== undefined && this.graph.height !== this._renderHeight) this.setSize({ auto: true });
  1348. this.ticks = this.staticTicks || Math.floor(this.graph.height / this.pixelsPerTick);
  1349. var axis = this._drawAxis(this.graph.y);
  1350. this._drawGrid(axis);
  1351. this._renderHeight = this.graph.height;
  1352. },
  1353. _drawAxis: function(scale) {
  1354. var axis = d3.svg.axis().scale(scale).orient(this.orientation);
  1355. axis.tickFormat(this.tickFormat);
  1356. if (this.tickValues) axis.tickValues(this.tickValues);
  1357. if (this.orientation == 'left') {
  1358. var berth = this.height * this.berthRate;
  1359. var transform = 'translate(' + this.width + ', ' + berth + ')';
  1360. }
  1361. if (this.element) {
  1362. this.vis.selectAll('*').remove();
  1363. }
  1364. this.vis
  1365. .append("svg:g")
  1366. .attr("class", ["y_ticks", this.ticksTreatment].join(" "))
  1367. .attr("transform", transform)
  1368. .call(axis.ticks(this.ticks).tickSubdivide(0).tickSize(this.tickSize));
  1369. return axis;
  1370. },
  1371. _drawGrid: function(axis) {
  1372. var gridSize = (this.orientation == 'right' ? 1 : -1) * this.graph.width;
  1373. this.graph.vis
  1374. .append("svg:g")
  1375. .attr("class", "y_grid")
  1376. .call(axis.ticks(this.ticks).tickSubdivide(0).tickSize(gridSize))
  1377. .selectAll('text')
  1378. .each(function() { this.parentNode.setAttribute('data-y-value', this.textContent) });
  1379. }
  1380. } );
  1381. Rickshaw.namespace('Rickshaw.Graph.Axis.Y.Scaled');
  1382. Rickshaw.Graph.Axis.Y.Scaled = Rickshaw.Class.create( Rickshaw.Graph.Axis.Y, {
  1383. initialize: function($super, args) {
  1384. if (typeof(args.scale) === 'undefined') {
  1385. throw new Error('Scaled requires scale');
  1386. }
  1387. this.scale = args.scale;
  1388. if (typeof(args.grid) === 'undefined') {
  1389. this.grid = true;
  1390. } else {
  1391. this.grid = args.grid;
  1392. }
  1393. $super(args);
  1394. },
  1395. _drawAxis: function($super, scale) {
  1396. // Adjust scale's domain to compensate for adjustments to the
  1397. // renderer's domain (e.g. padding).
  1398. var domain = this.scale.domain();
  1399. var renderDomain = this.graph.renderer.domain().y;
  1400. var extents = [
  1401. Math.min.apply(Math, domain),
  1402. Math.max.apply(Math, domain)];
  1403. // A mapping from the ideal render domain [0, 1] to the extent
  1404. // of the original scale's domain. This is used to calculate
  1405. // the extents of the adjusted domain.
  1406. var extentMap = d3.scale.linear().domain([0, 1]).range(extents);
  1407. var adjExtents = [
  1408. extentMap(renderDomain[0]),
  1409. extentMap(renderDomain[1])];
  1410. // A mapping from the original domain to the adjusted domain.
  1411. var adjustment = d3.scale.linear().domain(extents).range(adjExtents);
  1412. // Make a copy of the custom scale, apply the adjusted domain, and
  1413. // copy the range to match the graph's scale.
  1414. var adjustedScale = this.scale.copy()
  1415. .domain(domain.map(adjustment))
  1416. .range(scale.range());
  1417. return $super(adjustedScale);
  1418. },
  1419. _drawGrid: function($super, axis) {
  1420. if (this.grid) {
  1421. // only draw the axis if the grid option is true
  1422. $super(axis);
  1423. }
  1424. }
  1425. } );
  1426. Rickshaw.namespace('Rickshaw.Graph.Behavior.Series.Highlight');
  1427. Rickshaw.Graph.Behavior.Series.Highlight = function(args) {
  1428. this.graph = args.graph;
  1429. this.legend = args.legend;
  1430. var self = this;
  1431. var colorSafe = {};
  1432. var activeLine = null;
  1433. var disabledColor = args.disabledColor || function(seriesColor) {
  1434. return d3.interpolateRgb(seriesColor, d3.rgb('#d8d8d8'))(0.8).toString();
  1435. };
  1436. this.addHighlightEvents = function (l) {
  1437. l.element.addEventListener( 'mouseover', function(e) {
  1438. if (activeLine) return;
  1439. else activeLine = l;
  1440. self.legend.lines.forEach( function(line) {
  1441. if (l === line) {
  1442. // if we're not in a stacked renderer bring active line to the top
  1443. if (self.graph.renderer.unstack && (line.series.renderer ? line.series.renderer.unstack : true)) {
  1444. var seriesIndex = self.graph.series.indexOf(line.series);
  1445. line.originalIndex = seriesIndex;
  1446. var series = self.graph.series.splice(seriesIndex, 1)[0];
  1447. self.graph.series.push(series);
  1448. }
  1449. return;
  1450. }
  1451. colorSafe[line.series.name] = colorSafe[line.series.name] || line.series.color;
  1452. line.series.color = disabledColor(line.series.color);
  1453. } );
  1454. self.graph.update();
  1455. }, false );
  1456. l.element.addEventListener( 'mouseout', function(e) {
  1457. if (!activeLine) return;
  1458. else activeLine = null;
  1459. self.legend.lines.forEach( function(line) {
  1460. // return reordered series to its original place
  1461. if (l === line && line.hasOwnProperty('originalIndex')) {
  1462. var series = self.graph.series.pop();
  1463. self.graph.series.splice(line.originalIndex, 0, series);
  1464. delete line.originalIndex;
  1465. }
  1466. if (colorSafe[line.series.name]) {
  1467. line.series.color = colorSafe[line.series.name];
  1468. }
  1469. } );
  1470. self.graph.update();
  1471. }, false );
  1472. };
  1473. if (this.legend) {
  1474. this.legend.lines.forEach( function(l) {
  1475. self.addHighlightEvents(l);
  1476. } );
  1477. }
  1478. };
  1479. Rickshaw.namespace('Rickshaw.Graph.Behavior.Series.Order');
  1480. Rickshaw.Graph.Behavior.Series.Order = function(args) {
  1481. this.graph = args.graph;
  1482. this.legend = args.legend;
  1483. var self = this;
  1484. if (typeof window.jQuery == 'undefined') {
  1485. throw "couldn't find jQuery at window.jQuery";
  1486. }
  1487. if (typeof window.jQuery.ui == 'undefined') {
  1488. throw "couldn't find jQuery UI at window.jQuery.ui";
  1489. }
  1490. jQuery(function() {
  1491. jQuery(self.legend.list).sortable( {
  1492. containment: 'parent',
  1493. tolerance: 'pointer',
  1494. update: function( event, ui ) {
  1495. var series = [];
  1496. jQuery(self.legend.list).find('li').each( function(index, item) {
  1497. if (!item.series) return;
  1498. series.push(item.series);
  1499. } );
  1500. for (var i = self.graph.series.length - 1; i >= 0; i--) {
  1501. self.graph.series[i] = series.shift();
  1502. }
  1503. self.graph.update();
  1504. }
  1505. } );
  1506. jQuery(self.legend.list).disableSelection();
  1507. });
  1508. //hack to make jquery-ui sortable behave
  1509. this.graph.onUpdate( function() {
  1510. var h = window.getComputedStyle(self.legend.element).height;
  1511. self.legend.element.style.height = h;
  1512. } );
  1513. };
  1514. Rickshaw.namespace('Rickshaw.Graph.Behavior.Series.Toggle');
  1515. Rickshaw.Graph.Behavior.Series.Toggle = function(args) {
  1516. this.graph = args.graph;
  1517. this.legend = args.legend;
  1518. var self = this;
  1519. this.addAnchor = function(line) {
  1520. var anchor = document.createElement('a');
  1521. anchor.innerHTML = '&#10004;';
  1522. anchor.classList.add('action');
  1523. line.element.insertBefore(anchor, line.element.firstChild);
  1524. anchor.onclick = function(e) {
  1525. if (line.series.disabled) {
  1526. line.series.enable();
  1527. line.element.classList.remove('disabled');
  1528. } else {
  1529. if (this.graph.series.filter(function(s) { return !s.disabled }).length <= 1) return;
  1530. line.series.disable();
  1531. line.element.classList.add('disabled');
  1532. }
  1533. self.graph.update();
  1534. }.bind(this);
  1535. var label = line.element.getElementsByTagName('span')[0];
  1536. label.onclick = function(e){
  1537. var disableAllOtherLines = line.series.disabled;
  1538. if ( ! disableAllOtherLines ) {
  1539. for ( var i = 0; i < self.legend.lines.length; i++ ) {
  1540. var l = self.legend.lines[i];
  1541. if ( line.series === l.series ) {
  1542. // noop
  1543. } else if ( l.series.disabled ) {
  1544. // noop
  1545. } else {
  1546. disableAllOtherLines = true;
  1547. break;
  1548. }
  1549. }
  1550. }
  1551. // show all or none
  1552. if ( disableAllOtherLines ) {
  1553. // these must happen first or else we try ( and probably fail ) to make a no line graph
  1554. line.series.enable();
  1555. line.element.classList.remove('disabled');
  1556. self.legend.lines.forEach(function(l){
  1557. if ( line.series === l.series ) {
  1558. // noop
  1559. } else {
  1560. l.series.disable();
  1561. l.element.classList.add('disabled');
  1562. }
  1563. });
  1564. } else {
  1565. self.legend.lines.forEach(function(l){
  1566. l.series.enable();
  1567. l.element.classList.remove('disabled');
  1568. });
  1569. }
  1570. self.graph.update();
  1571. };
  1572. };
  1573. if (this.legend) {
  1574. if (typeof jQuery != 'undefined' && jQuery(this.legend.list).sortable) {
  1575. jQuery(this.legend.list).sortable( {
  1576. start: function(event, ui) {
  1577. ui.item.bind('no.onclick',
  1578. function(event) {
  1579. event.preventDefault();
  1580. }
  1581. );
  1582. },
  1583. stop: function(event, ui) {
  1584. setTimeout(function(){
  1585. ui.item.unbind('no.onclick');
  1586. }, 250);
  1587. }
  1588. });
  1589. }
  1590. this.legend.lines.forEach( function(l) {
  1591. self.addAnchor(l);
  1592. } );
  1593. }
  1594. this._addBehavior = function() {
  1595. this.graph.series.forEach( function(s) {
  1596. s.disable = function() {
  1597. if (self.graph.series.length <= 1) {
  1598. throw('only one series left');
  1599. }
  1600. s.disabled = true;
  1601. };
  1602. s.enable = function() {
  1603. s.disabled = false;
  1604. };
  1605. } );
  1606. };
  1607. this._addBehavior();
  1608. this.updateBehaviour = function () { this._addBehavior() };
  1609. };
  1610. Rickshaw.namespace('Rickshaw.Graph.DragZoom');
  1611. Rickshaw.Graph.DragZoom = Rickshaw.Class.create({
  1612. initialize: function(args) {
  1613. if (!args || !args.graph) {
  1614. throw new Error("Rickshaw.Graph.DragZoom needs a reference to a graph");
  1615. }
  1616. var defaults = {
  1617. opacity: 0.5,
  1618. fill: 'steelblue',
  1619. minimumTimeSelection: 60,
  1620. callback: function() {}
  1621. };
  1622. this.graph = args.graph;
  1623. this.svg = d3.select(this.graph.element).select("svg");
  1624. this.svgWidth = parseInt(this.svg.attr("width"), 10);
  1625. this.opacity = args.opacity || defaults.opacity;
  1626. this.fill = args.fill || defaults.fill;
  1627. this.minimumTimeSelection = args.minimumTimeSelection || defaults.minimumTimeSelection;
  1628. this.callback = args.callback || defaults.callback;
  1629. this.registerMouseEvents();
  1630. },
  1631. registerMouseEvents: function() {
  1632. var self = this;
  1633. var ESCAPE_KEYCODE = 27;
  1634. var rectangle;
  1635. var drag = {
  1636. startDt: null,
  1637. stopDt: null,
  1638. startPX: null,
  1639. stopPX: null
  1640. };
  1641. this.svg.on("mousedown", onMousedown);
  1642. function onMouseup(datum, index) {
  1643. drag.stopDt = pointAsDate(d3.event);
  1644. var windowAfterDrag = [
  1645. drag.startDt,
  1646. drag.stopDt
  1647. ].sort(compareNumbers);
  1648. self.graph.window.xMin = windowAfterDrag[0];
  1649. self.graph.window.xMax = windowAfterDrag[1];
  1650. var endTime = self.graph.window.xMax;
  1651. var range = self.graph.window.xMax - self.graph.window.xMin;
  1652. reset(this);
  1653. if (range < self.minimumTimeSelection || isNaN(range)) {
  1654. return;
  1655. }
  1656. self.graph.update();
  1657. self.callback({range: range, endTime: endTime});
  1658. }
  1659. function onMousemove() {
  1660. var offset = drag.stopPX = (d3.event.offsetX || d3.event.layerX);
  1661. if (offset > (self.svgWidth - 1) || offset < 1) {
  1662. return;
  1663. }
  1664. var limits = [drag.startPX, offset].sort(compareNumbers);
  1665. var selectionWidth = limits[1]-limits[0];
  1666. if (isNaN(selectionWidth)) {
  1667. return reset(this);
  1668. }
  1669. rectangle.attr("fill", self.fill)
  1670. .attr("x", limits[0])
  1671. .attr("width", selectionWidth);
  1672. }
  1673. function onMousedown() {
  1674. var el = d3.select(this);
  1675. rectangle = el.append("rect")
  1676. .style("opacity", self.opacity)
  1677. .attr("y", 0)
  1678. .attr("height", "100%");
  1679. if(d3.event.preventDefault) {
  1680. d3.event.preventDefault();
  1681. } else {
  1682. d3.event.returnValue = false;
  1683. }
  1684. drag.target = d3.event.target;
  1685. drag.startDt = pointAsDate(d3.event);
  1686. drag.startPX = d3.event.offsetX || d3.event.layerX;
  1687. el.on("mousemove", onMousemove);
  1688. d3.select(document).on("mouseup", onMouseup);
  1689. d3.select(document).on("keyup", function() {
  1690. if (d3.event.keyCode === ESCAPE_KEYCODE) {
  1691. reset(this);
  1692. }
  1693. });
  1694. }
  1695. function reset(el) {
  1696. var s = d3.select(el);
  1697. s.on("mousemove", null);
  1698. d3.select(document).on("mouseup", null);
  1699. drag = {};
  1700. rectangle.remove();
  1701. }
  1702. function compareNumbers(a, b) {
  1703. return a - b;
  1704. }
  1705. function pointAsDate(e) {
  1706. return Math.floor(self.graph.x.invert(e.offsetX || e.layerX));
  1707. }
  1708. }
  1709. });
  1710. Rickshaw.namespace('Rickshaw.Graph.HoverDetail');
  1711. Rickshaw.Graph.HoverDetail = Rickshaw.Class.create({
  1712. initialize: function(args) {
  1713. var graph = this.graph = args.graph;
  1714. this.xFormatter = args.xFormatter || function(x) {
  1715. return new Date( x * 1000 ).toUTCString();
  1716. };
  1717. this.yFormatter = args.yFormatter || function(y) {
  1718. return y === null ? y : y.toFixed(2);
  1719. };
  1720. var element = this.element = document.createElement('div');
  1721. element.className = 'detail inactive';
  1722. this.visible = true;
  1723. graph.element.appendChild(element);
  1724. this.lastEvent = null;
  1725. this._addListeners();
  1726. this.onShow = args.onShow;
  1727. this.onHide = args.onHide;
  1728. this.onRender = args.onRender;
  1729. this.formatter = args.formatter || this.formatter;
  1730. },
  1731. formatter: function(series, x, y, formattedX, formattedY, d) {
  1732. return series.name + ':&nbsp;' + formattedY;
  1733. },
  1734. update: function(e) {
  1735. e = e || this.lastEvent;
  1736. if (!e) return;
  1737. this.lastEvent = e;
  1738. if (!e.target.nodeName.match(/^(path|svg|rect|circle)$/)) return;
  1739. var graph = this.graph;
  1740. var rect = graph.element.getBoundingClientRect();
  1741. var eventX = e.clientX - rect.left;
  1742. var eventY = e.clientY - rect.top;
  1743. var j = 0;
  1744. var points = [];
  1745. var nearestPoint;
  1746. this.graph.series.active().forEach( function(series) {
  1747. var data = this.graph.stackedData[j++];
  1748. if (!data.length)
  1749. return;
  1750. var domainX = graph.x.invert(eventX);
  1751. var domainIndexScale = d3.scale.linear()
  1752. .domain([data[0].x, data.slice(-1)[0].x])
  1753. .range([0, data.length - 1]);
  1754. var approximateIndex = Math.round(domainIndexScale(domainX));
  1755. if (approximateIndex == data.length - 1) approximateIndex--;
  1756. var dataIndex = Math.min(approximateIndex || 0, data.length - 1);
  1757. for (var i = approximateIndex; i < data.length - 1;) {
  1758. if (!data[i] || !data[i + 1]) break;
  1759. if (data[i].x <= domainX && data[i + 1].x > domainX) {
  1760. dataIndex = Math.abs(domainX - data[i].x) < Math.abs(domainX - data[i + 1].x) ? i : i + 1;
  1761. break;
  1762. }
  1763. if (data[i + 1].x <= domainX) { i++ } else { i-- }
  1764. }
  1765. if (dataIndex < 0) dataIndex = 0;
  1766. var value = data[dataIndex];
  1767. var distance = Math.sqrt(
  1768. Math.pow(Math.abs(graph.x(value.x) - eventX), 2) +
  1769. Math.pow(Math.abs(graph.y(value.y + value.y0) - eventY), 2)
  1770. );
  1771. var xFormatter = series.xFormatter || this.xFormatter;
  1772. var yFormatter = series.yFormatter || this.yFormatter;
  1773. var point = {
  1774. formattedXValue: xFormatter(value.x),
  1775. formattedYValue: yFormatter(series.scale ? series.scale.invert(value.y) : value.y),
  1776. series: series,
  1777. value: value,
  1778. distance: distance,
  1779. order: j,
  1780. name: series.name
  1781. };
  1782. if (!nearestPoint || distance < nearestPoint.distance) {
  1783. nearestPoint = point;
  1784. }
  1785. points.push(point);
  1786. }, this );
  1787. if (!nearestPoint)
  1788. return;
  1789. nearestPoint.active = true;
  1790. var domainX = nearestPoint.value.x;
  1791. var formattedXValue = nearestPoint.formattedXValue;
  1792. this.element.innerHTML = '';
  1793. this.element.style.left = graph.x(domainX) + 'px';
  1794. this.visible && this.render( {
  1795. points: points,
  1796. detail: points, // for backwards compatibility
  1797. mouseX: eventX,
  1798. mouseY: eventY,
  1799. formattedXValue: formattedXValue,
  1800. domainX: domainX
  1801. } );
  1802. },
  1803. hide: function() {
  1804. this.visible = false;
  1805. this.element.classList.add('inactive');
  1806. if (typeof this.onHide == 'function') {
  1807. this.onHide();
  1808. }
  1809. },
  1810. show: function() {
  1811. this.visible = true;
  1812. this.element.classList.remove('inactive');
  1813. if (typeof this.onShow == 'function') {
  1814. this.onShow();
  1815. }
  1816. },
  1817. render: function(args) {
  1818. var graph = this.graph;
  1819. var points = args.points;
  1820. var point = points.filter( function(p) { return p.active } ).shift();
  1821. if (point.value.y === null) return;
  1822. var formattedXValue = point.formattedXValue;
  1823. var formattedYValue = point.formattedYValue;
  1824. this.element.innerHTML = '';
  1825. this.element.style.left = graph.x(point.value.x) + 'px';
  1826. var xLabel = document.createElement('div');
  1827. xLabel.className = 'x_label';
  1828. xLabel.innerHTML = formattedXValue;
  1829. this.element.appendChild(xLabel);
  1830. var item = document.createElement('div');
  1831. item.className = 'item';
  1832. // invert the scale if this series displays using a scale
  1833. var series = point.series;
  1834. var actualY = series.scale ? series.scale.invert(point.value.y) : point.value.y;
  1835. item.innerHTML = this.formatter(series, point.value.x, actualY, formattedXValue, formattedYValue, point);
  1836. item.style.top = this.graph.y(point.value.y0 + point.value.y) + 'px';
  1837. this.element.appendChild(item);
  1838. var dot = document.createElement('div');
  1839. dot.className = 'dot';
  1840. dot.style.top = item.style.top;
  1841. dot.style.borderColor = series.color;
  1842. this.element.appendChild(dot);
  1843. if (point.active) {
  1844. item.classList.add('active');
  1845. dot.classList.add('active');
  1846. }
  1847. // Assume left alignment until the element has been displayed and
  1848. // bounding box calculations are possible.
  1849. var alignables = [xLabel, item];
  1850. alignables.forEach(function(el) {
  1851. el.classList.add('left');
  1852. });
  1853. this.show();
  1854. // If left-alignment results in any error, try right-alignment.
  1855. var leftAlignError = this._calcLayoutError(alignables);
  1856. if (leftAlignError > 0) {
  1857. alignables.forEach(function(el) {
  1858. el.classList.remove('left');
  1859. el.classList.add('right');
  1860. });
  1861. // If right-alignment is worse than left alignment, switch back.
  1862. var rightAlignError = this._calcLayoutError(alignables);
  1863. if (rightAlignError > leftAlignError) {
  1864. alignables.forEach(function(el) {
  1865. el.classList.remove('right');
  1866. el.classList.add('left');
  1867. });
  1868. }
  1869. }
  1870. if (typeof this.onRender == 'function') {
  1871. this.onRender(args);
  1872. }
  1873. },
  1874. _calcLayoutError: function(alignables) {
  1875. // Layout error is calculated as the number of linear pixels by which
  1876. // an alignable extends past the left or right edge of the parent.
  1877. var parentRect = this.element.parentNode.getBoundingClientRect();
  1878. var error = 0;
  1879. var alignRight = alignables.forEach(function(el) {
  1880. var rect = el.getBoundingClientRect();
  1881. if (!rect.width) {
  1882. return;
  1883. }
  1884. if (rect.right > parentRect.right) {
  1885. error += rect.right - parentRect.right;
  1886. }
  1887. if (rect.left < parentRect.left) {
  1888. error += parentRect.left - rect.left;
  1889. }
  1890. });
  1891. return error;
  1892. },
  1893. _addListeners: function() {
  1894. // Keep reference for later removal.
  1895. this.mousemoveListener = function(e) {
  1896. this.visible = true;
  1897. this.update(e);
  1898. }.bind(this);
  1899. // Add listener.
  1900. this.graph.element.addEventListener(
  1901. 'mousemove',
  1902. this.mousemoveListener,
  1903. false
  1904. );
  1905. this.graph.onUpdate( function() { this.update() }.bind(this) );
  1906. // Keep reference for later removal.
  1907. this.mouseoutListener = function(e) {
  1908. if (e.relatedTarget && !(e.relatedTarget.compareDocumentPosition(this.graph.element) & Node.DOCUMENT_POSITION_CONTAINS)) {
  1909. this.hide();
  1910. }
  1911. }.bind(this);
  1912. // Add listener.
  1913. this.graph.element.addEventListener(
  1914. 'mouseout',
  1915. this.mouseoutListener,
  1916. false
  1917. );
  1918. },
  1919. _removeListeners: function() {
  1920. if (this.mousemoveListener) {
  1921. this.graph.element.removeEventListener('mousemove', this.mousemoveListener, false);
  1922. }
  1923. if (this.mouseoutListener) {
  1924. this.graph.element.removeEventListener('mouseout', this.mouseoutListener, false);
  1925. }
  1926. }
  1927. });
  1928. Rickshaw.namespace('Rickshaw.Graph.JSONP');
  1929. Rickshaw.Graph.JSONP = Rickshaw.Class.create( Rickshaw.Graph.Ajax, {
  1930. request: function() {
  1931. jQuery.ajax( {
  1932. url: this.dataURL,
  1933. dataType: 'jsonp',
  1934. success: this.success.bind(this),
  1935. error: this.error.bind(this)
  1936. } );
  1937. }
  1938. } );
  1939. Rickshaw.namespace('Rickshaw.Graph.Legend');
  1940. Rickshaw.Graph.Legend = Rickshaw.Class.create( {
  1941. className: 'rickshaw_legend',
  1942. initialize: function(args) {
  1943. this.element = args.element;
  1944. this.graph = args.graph;
  1945. this.naturalOrder = args.naturalOrder;
  1946. this.element.classList.add(this.className);
  1947. this.list = document.createElement('ul');
  1948. this.element.appendChild(this.list);
  1949. this.render();
  1950. // we could bind this.render.bind(this) here
  1951. // but triggering the re-render would lose the added
  1952. // behavior of the series toggle
  1953. this.graph.onUpdate( function() {} );
  1954. },
  1955. render: function() {
  1956. var self = this;
  1957. while ( this.list.firstChild ) {
  1958. this.list.removeChild( this.list.firstChild );
  1959. }
  1960. this.lines = [];
  1961. var series = this.graph.series
  1962. .map( function(s) { return s } );
  1963. if (!this.naturalOrder) {
  1964. series = series.reverse();
  1965. }
  1966. series.forEach( function(s) {
  1967. self.addLine(s);
  1968. } );
  1969. },
  1970. addLine: function (series) {
  1971. var line = document.createElement('li');
  1972. line.className = 'line';
  1973. if (series.disabled) {
  1974. line.className += ' disabled';
  1975. }
  1976. if (series.className) {
  1977. d3.select(line).classed(series.className, true);
  1978. }
  1979. var swatch = document.createElement('div');
  1980. swatch.className = 'swatch';
  1981. swatch.style.backgroundColor = series.color;
  1982. line.appendChild(swatch);
  1983. var label = document.createElement('span');
  1984. label.className = 'label';
  1985. label.innerHTML = series.name;
  1986. line.appendChild(label);
  1987. this.list.appendChild(line);
  1988. line.series = series;
  1989. if (series.noLegend) {
  1990. line.style.display = 'none';
  1991. }
  1992. var _line = { element: line, series: series };
  1993. if (this.shelving) {
  1994. this.shelving.addAnchor(_line);
  1995. this.shelving.updateBehaviour();
  1996. }
  1997. if (this.highlighter) {
  1998. this.highlighter.addHighlightEvents(_line);
  1999. }
  2000. this.lines.push(_line);
  2001. return line;
  2002. }
  2003. } );
  2004. Rickshaw.namespace('Rickshaw.Graph.RangeSlider');
  2005. Rickshaw.Graph.RangeSlider = Rickshaw.Class.create({
  2006. initialize: function(args) {
  2007. var $ = jQuery;
  2008. var self = this;
  2009. var element = this.element = args.element;
  2010. var graphs = this.graphs = args.graphs;
  2011. if (!graphs) {
  2012. graphs = this.graph = args.graph;
  2013. }
  2014. if (graphs.constructor !== Array) {
  2015. graphs = [graphs];
  2016. }
  2017. this.graph = graphs[0];
  2018. this.slideCallbacks = [];
  2019. this.build();
  2020. for (var i = 0; i < graphs.length; i++) {
  2021. graphs[i].onUpdate(function() {
  2022. self.update();
  2023. }.bind(self));
  2024. (function(idx){
  2025. graphs[idx].onConfigure(function() {
  2026. $(this.element)[0].style.width = graphs[idx].width + 'px';
  2027. }.bind(self));
  2028. })(i);
  2029. }
  2030. },
  2031. build: function() {
  2032. var domain;
  2033. var element = this.element;
  2034. var $ = jQuery;
  2035. var self = this;
  2036. var graphs = this.graphs || this.graph;
  2037. if (graphs.constructor !== Array) {
  2038. graphs = [graphs];
  2039. }
  2040. // base the slider's min/max on the first graph
  2041. this.graph = graphs[0];
  2042. domain = graphs[0].dataDomain();
  2043. $(function() {
  2044. $(element).slider({
  2045. range: true,
  2046. min: domain[0],
  2047. max: domain[1],
  2048. values: [
  2049. domain[0],
  2050. domain[1]
  2051. ],
  2052. start: function(event, ui) {
  2053. self.slideStarted({ event: event, ui: ui });
  2054. },
  2055. stop: function(event, ui) {
  2056. self.slideFinished({ event: event, ui: ui });
  2057. },
  2058. slide: function(event, ui) {
  2059. if (!self.slideShouldUpdate(event, ui))
  2060. return;
  2061. if (ui.values[1] <= ui.values[0]) return;
  2062. for (var i = 0; i < graphs.length; i++) {
  2063. self.processSlideChange({
  2064. event: event,
  2065. ui: ui,
  2066. graph: graphs[i]
  2067. });
  2068. }
  2069. }
  2070. } );
  2071. } );
  2072. graphs[0].onConfigure(function() {
  2073. $(this.element)[0].style.width = graphs[0].width + 'px';
  2074. }.bind(this));
  2075. },
  2076. update: function() {
  2077. var element = this.element;
  2078. var graph = this.graph;
  2079. var $ = jQuery;
  2080. var values = $(element).slider('option', 'values');
  2081. var domain = graph.dataDomain();
  2082. $(element).slider('option', 'min', domain[0]);
  2083. $(element).slider('option', 'max', domain[1]);
  2084. if (graph.window.xMin == null) {
  2085. values[0] = domain[0];
  2086. }
  2087. if (graph.window.xMax == null) {
  2088. values[1] = domain[1];
  2089. }
  2090. $(element).slider('option', 'values', values);
  2091. },
  2092. onSlide: function(callback) {
  2093. this.slideCallbacks.push(callback);
  2094. },
  2095. processSlideChange: function(args) {
  2096. var event = args.event;
  2097. var ui = args.ui;
  2098. var graph = args.graph;
  2099. graph.window.xMin = ui.values[0];
  2100. graph.window.xMax = ui.values[1];
  2101. graph.update();
  2102. var domain = graph.dataDomain();
  2103. // if we're at an extreme, stick there
  2104. if (domain[0] == ui.values[0]) {
  2105. graph.window.xMin = undefined;
  2106. }
  2107. if (domain[1] == ui.values[1]) {
  2108. graph.window.xMax = undefined;
  2109. }
  2110. this.slideCallbacks.forEach(function(callback) {
  2111. callback(graph, graph.window.xMin, graph.window.xMax);
  2112. });
  2113. },
  2114. // allows the slide updates to bail out if sliding is not permitted
  2115. slideShouldUpdate: function() {
  2116. return true;
  2117. },
  2118. slideStarted: function() {
  2119. return;
  2120. },
  2121. slideFinished: function() {
  2122. return;
  2123. }
  2124. });
  2125. Rickshaw.namespace('Rickshaw.Graph.RangeSlider.Preview');
  2126. Rickshaw.Graph.RangeSlider.Preview = Rickshaw.Class.create({
  2127. initialize: function(args) {
  2128. if (!args.element) throw "Rickshaw.Graph.RangeSlider.Preview needs a reference to an element";
  2129. if (!args.graph && !args.graphs) throw "Rickshaw.Graph.RangeSlider.Preview needs a reference to an graph or an array of graphs";
  2130. this.element = args.element;
  2131. this.element.style.position = 'relative';
  2132. this.graphs = args.graph ? [ args.graph ] : args.graphs;
  2133. this.defaults = {
  2134. height: 75,
  2135. width: 400,
  2136. gripperColor: undefined,
  2137. frameTopThickness: 3,
  2138. frameHandleThickness: 10,
  2139. frameColor: "#d4d4d4",
  2140. frameOpacity: 1,
  2141. minimumFrameWidth: 0,
  2142. heightRatio: 0.2
  2143. };
  2144. this.heightRatio = args.heightRatio || this.defaults.heightRatio;
  2145. this.defaults.gripperColor = d3.rgb(this.defaults.frameColor).darker().toString();
  2146. this.configureCallbacks = [];
  2147. this.slideCallbacks = [];
  2148. this.previews = [];
  2149. if (!args.width) this.widthFromGraph = true;
  2150. if (!args.height) this.heightFromGraph = true;
  2151. if (this.widthFromGraph || this.heightFromGraph) {
  2152. this.graphs[0].onConfigure(function () {
  2153. this.configure(args); this.render();
  2154. }.bind(this));
  2155. }
  2156. args.width = args.width || this.graphs[0].width || this.defaults.width;
  2157. args.height = args.height || this.graphs[0].height * this.heightRatio || this.defaults.height;
  2158. this.configure(args);
  2159. this.render();
  2160. },
  2161. onSlide: function(callback) {
  2162. this.slideCallbacks.push(callback);
  2163. },
  2164. onConfigure: function(callback) {
  2165. this.configureCallbacks.push(callback);
  2166. },
  2167. configure: function(args) {
  2168. this.config = this.config || {};
  2169. this.configureCallbacks.forEach(function(callback) {
  2170. callback(args);
  2171. });
  2172. Rickshaw.keys(this.defaults).forEach(function(k) {
  2173. this.config[k] = k in args ? args[k]
  2174. : k in this.config ? this.config[k]
  2175. : this.defaults[k];
  2176. }, this);
  2177. if ('width' in args || 'height' in args) {
  2178. if (this.widthFromGraph) {
  2179. this.config.width = this.graphs[0].width;
  2180. }
  2181. if (this.heightFromGraph) {
  2182. this.config.height = this.graphs[0].height * this.heightRatio;
  2183. this.previewHeight = this.config.height;
  2184. }
  2185. this.previews.forEach(function(preview) {
  2186. var height = this.previewHeight / this.graphs.length - this.config.frameTopThickness * 2;
  2187. var width = this.config.width - this.config.frameHandleThickness * 2;
  2188. preview.setSize({ width: width, height: height });
  2189. if (this.svg) {
  2190. var svgHeight = height + this.config.frameHandleThickness * 2;
  2191. var svgWidth = width + this.config.frameHandleThickness * 2;
  2192. this.svg.style("width", svgWidth + "px");
  2193. this.svg.style("height", svgHeight + "px");
  2194. }
  2195. }, this);
  2196. }
  2197. },
  2198. render: function() {
  2199. var self = this;
  2200. this.svg = d3.select(this.element)
  2201. .selectAll("svg.rickshaw_range_slider_preview")
  2202. .data([null]);
  2203. this.previewHeight = this.config.height - (this.config.frameTopThickness * 2);
  2204. this.previewWidth = this.config.width - (this.config.frameHandleThickness * 2);
  2205. this.currentFrame = [0, this.previewWidth];
  2206. var buildGraph = function(parent, index) {
  2207. var graphArgs = Rickshaw.extend({}, parent.config);
  2208. var height = self.previewHeight / self.graphs.length;
  2209. var renderer = parent.renderer.name;
  2210. Rickshaw.extend(graphArgs, {
  2211. element: this.appendChild(document.createElement("div")),
  2212. height: height,
  2213. width: self.previewWidth,
  2214. series: parent.series,
  2215. renderer: renderer
  2216. });
  2217. var graph = new Rickshaw.Graph(graphArgs);
  2218. self.previews.push(graph);
  2219. parent.onUpdate(function() { graph.render(); self.render() });
  2220. parent.onConfigure(function(args) {
  2221. // don't propagate height
  2222. delete args.height;
  2223. args.width = args.width - self.config.frameHandleThickness * 2;
  2224. graph.configure(args);
  2225. graph.render();
  2226. });
  2227. graph.render();
  2228. };
  2229. var graphContainer = d3.select(this.element)
  2230. .selectAll("div.rickshaw_range_slider_preview_container")
  2231. .data(this.graphs);
  2232. var translateCommand = "translate(" +
  2233. this.config.frameHandleThickness + "px, " +
  2234. this.config.frameTopThickness + "px)";
  2235. graphContainer.enter()
  2236. .append("div")
  2237. .classed("rickshaw_range_slider_preview_container", true)
  2238. .style("-webkit-transform", translateCommand)
  2239. .style("-moz-transform", translateCommand)
  2240. .style("-ms-transform", translateCommand)
  2241. .style("transform", translateCommand)
  2242. .each(buildGraph);
  2243. graphContainer.exit()
  2244. .remove();
  2245. // Use the first graph as the "master" for the frame state
  2246. var masterGraph = this.graphs[0];
  2247. var domainScale = d3.scale.linear()
  2248. .domain([0, this.previewWidth])
  2249. .range(masterGraph.dataDomain());
  2250. var currentWindow = [masterGraph.window.xMin, masterGraph.window.xMax];
  2251. this.currentFrame[0] = currentWindow[0] === undefined ?
  2252. 0 : Math.round(domainScale.invert(currentWindow[0]));
  2253. if (this.currentFrame[0] < 0) this.currentFrame[0] = 0;
  2254. this.currentFrame[1] = currentWindow[1] === undefined ?
  2255. this.previewWidth : domainScale.invert(currentWindow[1]);
  2256. if (this.currentFrame[1] - this.currentFrame[0] < self.config.minimumFrameWidth) {
  2257. this.currentFrame[1] = (this.currentFrame[0] || 0) + self.config.minimumFrameWidth;
  2258. }
  2259. this.svg.enter()
  2260. .append("svg")
  2261. .classed("rickshaw_range_slider_preview", true)
  2262. .style("height", this.config.height + "px")
  2263. .style("width", this.config.width + "px")
  2264. .style("position", "absolute")
  2265. .style("top", 0);
  2266. this._renderDimming();
  2267. this._renderFrame();
  2268. this._renderGrippers();
  2269. this._renderHandles();
  2270. this._renderMiddle();
  2271. this._registerMouseEvents();
  2272. },
  2273. _renderDimming: function() {
  2274. var element = this.svg
  2275. .selectAll("path.dimming")
  2276. .data([null]);
  2277. element.enter()
  2278. .append("path")
  2279. .attr("fill", "white")
  2280. .attr("fill-opacity", "0.7")
  2281. .attr("fill-rule", "evenodd")
  2282. .classed("dimming", true);
  2283. var path = "";
  2284. path += " M " + this.config.frameHandleThickness + " " + this.config.frameTopThickness;
  2285. path += " h " + this.previewWidth;
  2286. path += " v " + this.previewHeight;
  2287. path += " h " + -this.previewWidth;
  2288. path += " z ";
  2289. path += " M " + Math.max(this.currentFrame[0], this.config.frameHandleThickness) + " " + this.config.frameTopThickness;
  2290. path += " H " + Math.min(this.currentFrame[1] + this.config.frameHandleThickness * 2, this.previewWidth + this.config.frameHandleThickness);
  2291. path += " v " + this.previewHeight;
  2292. path += " H " + Math.max(this.currentFrame[0], this.config.frameHandleThickness);
  2293. path += " z";
  2294. element.attr("d", path);
  2295. },
  2296. _renderFrame: function() {
  2297. var element = this.svg
  2298. .selectAll("path.frame")
  2299. .data([null]);
  2300. element.enter()
  2301. .append("path")
  2302. .attr("stroke", "white")
  2303. .attr("stroke-width", "1px")
  2304. .attr("stroke-linejoin", "round")
  2305. .attr("fill", this.config.frameColor)
  2306. .attr("fill-opacity", this.config.frameOpacity)
  2307. .attr("fill-rule", "evenodd")
  2308. .classed("frame", true);
  2309. var path = "";
  2310. path += " M " + this.currentFrame[0] + " 0";
  2311. path += " H " + (this.currentFrame[1] + (this.config.frameHandleThickness * 2));
  2312. path += " V " + this.config.height;
  2313. path += " H " + (this.currentFrame[0]);
  2314. path += " z";
  2315. path += " M " + (this.currentFrame[0] + this.config.frameHandleThickness) + " " + this.config.frameTopThickness;
  2316. path += " H " + (this.currentFrame[1] + this.config.frameHandleThickness);
  2317. path += " v " + this.previewHeight;
  2318. path += " H " + (this.currentFrame[0] + this.config.frameHandleThickness);
  2319. path += " z";
  2320. element.attr("d", path);
  2321. },
  2322. _renderGrippers: function() {
  2323. var gripper = this.svg.selectAll("path.gripper")
  2324. .data([null]);
  2325. gripper.enter()
  2326. .append("path")
  2327. .attr("stroke", this.config.gripperColor)
  2328. .classed("gripper", true);
  2329. var path = "";
  2330. [0.4, 0.6].forEach(function(spacing) {
  2331. path += " M " + Math.round((this.currentFrame[0] + (this.config.frameHandleThickness * spacing))) + " " + Math.round(this.config.height * 0.3);
  2332. path += " V " + Math.round(this.config.height * 0.7);
  2333. path += " M " + Math.round((this.currentFrame[1] + (this.config.frameHandleThickness * (1 + spacing)))) + " " + Math.round(this.config.height * 0.3);
  2334. path += " V " + Math.round(this.config.height * 0.7);
  2335. }.bind(this));
  2336. gripper.attr("d", path);
  2337. },
  2338. _renderHandles: function() {
  2339. var leftHandle = this.svg.selectAll("rect.left_handle")
  2340. .data([null]);
  2341. leftHandle.enter()
  2342. .append("rect")
  2343. .attr('width', this.config.frameHandleThickness)
  2344. .style("cursor", "ew-resize")
  2345. .style("fill-opacity", "0")
  2346. .classed("left_handle", true);
  2347. leftHandle
  2348. .attr('x', this.currentFrame[0])
  2349. .attr('height', this.config.height);
  2350. var rightHandle = this.svg.selectAll("rect.right_handle")
  2351. .data([null]);
  2352. rightHandle.enter()
  2353. .append("rect")
  2354. .attr('width', this.config.frameHandleThickness)
  2355. .style("cursor", "ew-resize")
  2356. .style("fill-opacity", "0")
  2357. .classed("right_handle", true);
  2358. rightHandle
  2359. .attr('x', this.currentFrame[1] + this.config.frameHandleThickness)
  2360. .attr('height', this.config.height);
  2361. },
  2362. _renderMiddle: function() {
  2363. var middleHandle = this.svg.selectAll("rect.middle_handle")
  2364. .data([null]);
  2365. middleHandle.enter()
  2366. .append("rect")
  2367. .style("cursor", "move")
  2368. .style("fill-opacity", "0")
  2369. .classed("middle_handle", true);
  2370. middleHandle
  2371. .attr('width', Math.max(0, this.currentFrame[1] - this.currentFrame[0]))
  2372. .attr('x', this.currentFrame[0] + this.config.frameHandleThickness)
  2373. .attr('height', this.config.height);
  2374. },
  2375. _registerMouseEvents: function() {
  2376. var element = d3.select(this.element);
  2377. var drag = {
  2378. target: null,
  2379. start: null,
  2380. stop: null,
  2381. left: false,
  2382. right: false,
  2383. rigid: false
  2384. };
  2385. var self = this;
  2386. function onMousemove(datum, index) {
  2387. drag.stop = self._getClientXFromEvent(d3.event, drag);
  2388. var distanceTraveled = drag.stop - drag.start;
  2389. var frameAfterDrag = self.frameBeforeDrag.slice(0);
  2390. var minimumFrameWidth = self.config.minimumFrameWidth;
  2391. if (drag.rigid) {
  2392. minimumFrameWidth = self.frameBeforeDrag[1] - self.frameBeforeDrag[0];
  2393. }
  2394. if (drag.left) {
  2395. frameAfterDrag[0] = Math.max(frameAfterDrag[0] + distanceTraveled, 0);
  2396. }
  2397. if (drag.right) {
  2398. frameAfterDrag[1] = Math.min(frameAfterDrag[1] + distanceTraveled, self.previewWidth);
  2399. }
  2400. var currentFrameWidth = frameAfterDrag[1] - frameAfterDrag[0];
  2401. if (currentFrameWidth <= minimumFrameWidth) {
  2402. if (drag.left) {
  2403. frameAfterDrag[0] = frameAfterDrag[1] - minimumFrameWidth;
  2404. }
  2405. if (drag.right) {
  2406. frameAfterDrag[1] = frameAfterDrag[0] + minimumFrameWidth;
  2407. }
  2408. if (frameAfterDrag[0] <= 0) {
  2409. frameAfterDrag[1] -= frameAfterDrag[0];
  2410. frameAfterDrag[0] = 0;
  2411. }
  2412. if (frameAfterDrag[1] >= self.previewWidth) {
  2413. frameAfterDrag[0] -= (frameAfterDrag[1] - self.previewWidth);
  2414. frameAfterDrag[1] = self.previewWidth;
  2415. }
  2416. }
  2417. self.graphs.forEach(function(graph) {
  2418. var domainScale = d3.scale.linear()
  2419. .interpolate(d3.interpolateNumber)
  2420. .domain([0, self.previewWidth])
  2421. .range(graph.dataDomain());
  2422. var windowAfterDrag = [
  2423. domainScale(frameAfterDrag[0]),
  2424. domainScale(frameAfterDrag[1])
  2425. ];
  2426. self.slideCallbacks.forEach(function(callback) {
  2427. callback(graph, windowAfterDrag[0], windowAfterDrag[1]);
  2428. });
  2429. if (frameAfterDrag[0] === 0) {
  2430. windowAfterDrag[0] = undefined;
  2431. }
  2432. if (frameAfterDrag[1] === self.previewWidth) {
  2433. windowAfterDrag[1] = undefined;
  2434. }
  2435. graph.window.xMin = windowAfterDrag[0];
  2436. graph.window.xMax = windowAfterDrag[1];
  2437. graph.update();
  2438. });
  2439. }
  2440. function onMousedown() {
  2441. drag.target = d3.event.target;
  2442. drag.start = self._getClientXFromEvent(d3.event, drag);
  2443. self.frameBeforeDrag = self.currentFrame.slice();
  2444. d3.event.preventDefault ? d3.event.preventDefault() : d3.event.returnValue = false;
  2445. d3.select(document).on("mousemove.rickshaw_range_slider_preview", onMousemove);
  2446. d3.select(document).on("mouseup.rickshaw_range_slider_preview", onMouseup);
  2447. d3.select(document).on("touchmove.rickshaw_range_slider_preview", onMousemove);
  2448. d3.select(document).on("touchend.rickshaw_range_slider_preview", onMouseup);
  2449. d3.select(document).on("touchcancel.rickshaw_range_slider_preview", onMouseup);
  2450. }
  2451. function onMousedownLeftHandle(datum, index) {
  2452. drag.left = true;
  2453. onMousedown();
  2454. }
  2455. function onMousedownRightHandle(datum, index) {
  2456. drag.right = true;
  2457. onMousedown();
  2458. }
  2459. function onMousedownMiddleHandle(datum, index) {
  2460. drag.left = true;
  2461. drag.right = true;
  2462. drag.rigid = true;
  2463. onMousedown();
  2464. }
  2465. function onMouseup(datum, index) {
  2466. d3.select(document).on("mousemove.rickshaw_range_slider_preview", null);
  2467. d3.select(document).on("mouseup.rickshaw_range_slider_preview", null);
  2468. d3.select(document).on("touchmove.rickshaw_range_slider_preview", null);
  2469. d3.select(document).on("touchend.rickshaw_range_slider_preview", null);
  2470. d3.select(document).on("touchcancel.rickshaw_range_slider_preview", null);
  2471. delete self.frameBeforeDrag;
  2472. drag.left = false;
  2473. drag.right = false;
  2474. drag.rigid = false;
  2475. }
  2476. element.select("rect.left_handle").on("mousedown", onMousedownLeftHandle);
  2477. element.select("rect.right_handle").on("mousedown", onMousedownRightHandle);
  2478. element.select("rect.middle_handle").on("mousedown", onMousedownMiddleHandle);
  2479. element.select("rect.left_handle").on("touchstart", onMousedownLeftHandle);
  2480. element.select("rect.right_handle").on("touchstart", onMousedownRightHandle);
  2481. element.select("rect.middle_handle").on("touchstart", onMousedownMiddleHandle);
  2482. },
  2483. _getClientXFromEvent: function(event, drag) {
  2484. switch (event.type) {
  2485. case 'touchstart':
  2486. case 'touchmove':
  2487. var touchList = event.changedTouches;
  2488. var touch = null;
  2489. for (var touchIndex = 0; touchIndex < touchList.length; touchIndex++) {
  2490. if (touchList[touchIndex].target === drag.target) {
  2491. touch = touchList[touchIndex];
  2492. break;
  2493. }
  2494. }
  2495. return touch !== null ? touch.clientX : undefined;
  2496. default:
  2497. return event.clientX;
  2498. }
  2499. }
  2500. });
  2501. Rickshaw.namespace("Rickshaw.Graph.Renderer");
  2502. Rickshaw.Graph.Renderer = Rickshaw.Class.create( {
  2503. initialize: function(args) {
  2504. this.graph = args.graph;
  2505. this.tension = args.tension || this.tension;
  2506. this.configure(args);
  2507. },
  2508. seriesPathFactory: function() {
  2509. //implement in subclass
  2510. },
  2511. seriesStrokeFactory: function() {
  2512. // implement in subclass
  2513. },
  2514. defaults: function() {
  2515. return {
  2516. tension: 0.8,
  2517. strokeWidth: 2,
  2518. unstack: true,
  2519. padding: { top: 0.01, right: 0, bottom: 0.01, left: 0 },
  2520. stroke: false,
  2521. fill: false,
  2522. opacity: 1
  2523. };
  2524. },
  2525. domain: function(data) {
  2526. // Requires that at least one series contains some data
  2527. var stackedData = data || this.graph.stackedData || this.graph.stackData();
  2528. // filter out any series that may be empty in the current x-domain
  2529. stackedData = stackedData.filter(function (a) { return a && a.length !== 0; });
  2530. var xMin = +Infinity;
  2531. var xMax = -Infinity;
  2532. var yMin = +Infinity;
  2533. var yMax = -Infinity;
  2534. stackedData.forEach( function(series) {
  2535. series.forEach( function(d) {
  2536. if (d.y == null) return;
  2537. var y = d.y + d.y0;
  2538. if (y < yMin) yMin = y;
  2539. if (y > yMax) yMax = y;
  2540. } );
  2541. if (!series.length) return;
  2542. if (series[0].x < xMin) xMin = series[0].x;
  2543. if (series[series.length - 1].x > xMax) xMax = series[series.length - 1].x;
  2544. } );
  2545. xMin -= (xMax - xMin) * this.padding.left;
  2546. xMax += (xMax - xMin) * this.padding.right;
  2547. yMin = this.graph.min === 'auto' ? yMin : this.graph.min || 0;
  2548. yMax = this.graph.max === undefined ? yMax : this.graph.max;
  2549. if (this.graph.min === 'auto' || yMin < 0) {
  2550. yMin -= (yMax - yMin) * this.padding.bottom;
  2551. }
  2552. if (this.graph.max === undefined) {
  2553. yMax += (yMax - yMin) * this.padding.top;
  2554. }
  2555. return { x: [xMin, xMax], y: [yMin, yMax] };
  2556. },
  2557. render: function(args) {
  2558. args = args || {};
  2559. var graph = this.graph;
  2560. var series = args.series || graph.series;
  2561. var vis = args.vis || graph.vis;
  2562. vis.selectAll('*').remove();
  2563. var data = series
  2564. .filter(function(s) { return !s.disabled })
  2565. .map(function(s) { return s.stack });
  2566. var pathNodes = vis.selectAll("path.path")
  2567. .data(data)
  2568. .enter().append("svg:path")
  2569. .classed('path', true)
  2570. .attr("d", this.seriesPathFactory());
  2571. if (this.stroke) {
  2572. var strokeNodes = vis.selectAll('path.stroke')
  2573. .data(data)
  2574. .enter().append("svg:path")
  2575. .classed('stroke', true)
  2576. .attr("d", this.seriesStrokeFactory());
  2577. }
  2578. var i = 0;
  2579. series.forEach( function(series) {
  2580. if (series.disabled) return;
  2581. series.path = pathNodes[0][i];
  2582. if (this.stroke) series.stroke = strokeNodes[0][i];
  2583. this._styleSeries(series);
  2584. i++;
  2585. }, this );
  2586. },
  2587. _styleSeries: function(series) {
  2588. var fill = this.fill ? series.color : 'none';
  2589. var stroke = this.stroke ? series.color : 'none';
  2590. var strokeWidth = series.strokeWidth ? series.strokeWidth : this.strokeWidth;
  2591. var opacity = series.opacity ? series.opacity : this.opacity;
  2592. series.path.setAttribute('fill', fill);
  2593. series.path.setAttribute('stroke', stroke);
  2594. series.path.setAttribute('stroke-width', strokeWidth);
  2595. series.path.setAttribute('opacity', opacity);
  2596. if (series.className) {
  2597. d3.select(series.path).classed(series.className, true);
  2598. }
  2599. if (series.className && this.stroke) {
  2600. d3.select(series.stroke).classed(series.className, true);
  2601. }
  2602. },
  2603. configure: function(args) {
  2604. args = args || {};
  2605. Rickshaw.keys(this.defaults()).forEach( function(key) {
  2606. if (!args.hasOwnProperty(key)) {
  2607. this[key] = this[key] || this.graph[key] || this.defaults()[key];
  2608. return;
  2609. }
  2610. if (typeof this.defaults()[key] == 'object') {
  2611. Rickshaw.keys(this.defaults()[key]).forEach( function(k) {
  2612. this[key][k] =
  2613. args[key][k] !== undefined ? args[key][k] :
  2614. this[key][k] !== undefined ? this[key][k] :
  2615. this.defaults()[key][k];
  2616. }, this );
  2617. } else {
  2618. this[key] =
  2619. args[key] !== undefined ? args[key] :
  2620. this[key] !== undefined ? this[key] :
  2621. this.graph[key] !== undefined ? this.graph[key] :
  2622. this.defaults()[key];
  2623. }
  2624. }, this );
  2625. },
  2626. setStrokeWidth: function(strokeWidth) {
  2627. if (strokeWidth !== undefined) {
  2628. this.strokeWidth = strokeWidth;
  2629. }
  2630. },
  2631. setTension: function(tension) {
  2632. if (tension !== undefined) {
  2633. this.tension = tension;
  2634. }
  2635. }
  2636. } );
  2637. Rickshaw.namespace('Rickshaw.Graph.Renderer.Line');
  2638. Rickshaw.Graph.Renderer.Line = Rickshaw.Class.create( Rickshaw.Graph.Renderer, {
  2639. name: 'line',
  2640. defaults: function($super) {
  2641. return Rickshaw.extend( $super(), {
  2642. unstack: true,
  2643. fill: false,
  2644. stroke: true
  2645. } );
  2646. },
  2647. seriesPathFactory: function() {
  2648. var graph = this.graph;
  2649. var factory = d3.svg.line()
  2650. .x( function(d) { return graph.x(d.x) } )
  2651. .y( function(d) { return graph.y(d.y) } )
  2652. .interpolate(this.graph.interpolation).tension(this.tension);
  2653. factory.defined && factory.defined( function(d) { return d.y !== null } );
  2654. return factory;
  2655. }
  2656. } );
  2657. Rickshaw.namespace('Rickshaw.Graph.Renderer.Stack');
  2658. Rickshaw.Graph.Renderer.Stack = Rickshaw.Class.create( Rickshaw.Graph.Renderer, {
  2659. name: 'stack',
  2660. defaults: function($super) {
  2661. return Rickshaw.extend( $super(), {
  2662. fill: true,
  2663. stroke: false,
  2664. unstack: false
  2665. } );
  2666. },
  2667. seriesPathFactory: function() {
  2668. var graph = this.graph;
  2669. var factory = d3.svg.area()
  2670. .x( function(d) { return graph.x(d.x) } )
  2671. .y0( function(d) { return graph.y(d.y0) } )
  2672. .y1( function(d) { return graph.y(d.y + d.y0) } )
  2673. .interpolate(this.graph.interpolation).tension(this.tension);
  2674. factory.defined && factory.defined( function(d) { return d.y !== null } );
  2675. return factory;
  2676. }
  2677. } );
  2678. Rickshaw.namespace('Rickshaw.Graph.Renderer.Bar');
  2679. Rickshaw.Graph.Renderer.Bar = Rickshaw.Class.create( Rickshaw.Graph.Renderer, {
  2680. name: 'bar',
  2681. defaults: function($super) {
  2682. var defaults = Rickshaw.extend( $super(), {
  2683. gapSize: 0.05,
  2684. unstack: false,
  2685. opacity: 1.0
  2686. } );
  2687. delete defaults.tension;
  2688. return defaults;
  2689. },
  2690. initialize: function($super, args) {
  2691. args = args || {};
  2692. this.gapSize = args.gapSize || this.gapSize;
  2693. $super(args);
  2694. },
  2695. domain: function($super) {
  2696. var domain = $super();
  2697. var frequentInterval = this._frequentInterval(this.graph.stackedData.slice(-1).shift());
  2698. domain.x[1] += Number(frequentInterval.magnitude);
  2699. return domain;
  2700. },
  2701. barWidth: function(series) {
  2702. var frequentInterval = this._frequentInterval(series.stack);
  2703. var barWidth = this.graph.x.magnitude(frequentInterval.magnitude) * (1 - this.gapSize);
  2704. return barWidth;
  2705. },
  2706. render: function(args) {
  2707. args = args || {};
  2708. var graph = this.graph;
  2709. var series = args.series || graph.series;
  2710. var vis = args.vis || graph.vis;
  2711. vis.selectAll('*').remove();
  2712. var barWidth = this.barWidth(series.active()[0]);
  2713. var barXOffset = 0;
  2714. var activeSeriesCount = series.filter( function(s) { return !s.disabled; } ).length;
  2715. var seriesBarWidth = this.unstack ? barWidth / activeSeriesCount : barWidth;
  2716. var transform = function(d) {
  2717. // add a matrix transform for negative values
  2718. var matrix = [ 1, 0, 0, (d.y < 0 ? -1 : 1), 0, (d.y < 0 ? graph.y.magnitude(Math.abs(d.y)) * 2 : 0) ];
  2719. return "matrix(" + matrix.join(',') + ")";
  2720. };
  2721. series.forEach( function(series) {
  2722. if (series.disabled) return;
  2723. var barWidth = this.barWidth(series);
  2724. var nodes = vis.selectAll("path")
  2725. .data(series.stack.filter( function(d) { return d.y !== null } ))
  2726. .enter().append("svg:rect")
  2727. .attr("x", function(d) { return graph.x(d.x) + barXOffset })
  2728. .attr("y", function(d) { return (graph.y(d.y0 + Math.abs(d.y))) * (d.y < 0 ? -1 : 1 ) })
  2729. .attr("width", seriesBarWidth)
  2730. .attr("height", function(d) { return graph.y.magnitude(Math.abs(d.y)) })
  2731. .attr("opacity", series.opacity)
  2732. .attr("transform", transform);
  2733. Array.prototype.forEach.call(nodes[0], function(n) {
  2734. n.setAttribute('fill', series.color);
  2735. } );
  2736. if (this.unstack) barXOffset += seriesBarWidth;
  2737. }, this );
  2738. },
  2739. _frequentInterval: function(data) {
  2740. var intervalCounts = {};
  2741. for (var i = 0; i < data.length - 1; i++) {
  2742. var interval = data[i + 1].x - data[i].x;
  2743. intervalCounts[interval] = intervalCounts[interval] || 0;
  2744. intervalCounts[interval]++;
  2745. }
  2746. var frequentInterval = { count: 0, magnitude: 1 };
  2747. // Sorting object's keys returned to guarantee consistency when iterating over
  2748. // Keys order in `for .. in` loop is not specified and browsers behave differently here
  2749. // This results with different invterval value being calculated for different browsers
  2750. // See last but one section here: http://www.ecma-international.org/ecma-262/5.1/#sec-12.6.4
  2751. var keysSorted = Rickshaw.keys(intervalCounts).sort(function asc(a, b) { return Number(a) - Number(b); });
  2752. keysSorted.forEach( function(i) {
  2753. if (frequentInterval.count < intervalCounts[i]) {
  2754. frequentInterval = {
  2755. count: intervalCounts[i],
  2756. magnitude: i
  2757. };
  2758. }
  2759. } );
  2760. return frequentInterval;
  2761. }
  2762. } );
  2763. Rickshaw.namespace('Rickshaw.Graph.Renderer.Area');
  2764. Rickshaw.Graph.Renderer.Area = Rickshaw.Class.create( Rickshaw.Graph.Renderer, {
  2765. name: 'area',
  2766. defaults: function($super) {
  2767. return Rickshaw.extend( $super(), {
  2768. unstack: false,
  2769. fill: false,
  2770. stroke: false
  2771. } );
  2772. },
  2773. seriesPathFactory: function() {
  2774. var graph = this.graph;
  2775. var factory = d3.svg.area()
  2776. .x( function(d) { return graph.x(d.x) } )
  2777. .y0( function(d) { return graph.y(d.y0) } )
  2778. .y1( function(d) { return graph.y(d.y + d.y0) } )
  2779. .interpolate(graph.interpolation).tension(this.tension);
  2780. factory.defined && factory.defined( function(d) { return d.y !== null } );
  2781. return factory;
  2782. },
  2783. seriesStrokeFactory: function() {
  2784. var graph = this.graph;
  2785. var factory = d3.svg.line()
  2786. .x( function(d) { return graph.x(d.x) } )
  2787. .y( function(d) { return graph.y(d.y + d.y0) } )
  2788. .interpolate(graph.interpolation).tension(this.tension);
  2789. factory.defined && factory.defined( function(d) { return d.y !== null } );
  2790. return factory;
  2791. },
  2792. render: function(args) {
  2793. args = args || {};
  2794. var graph = this.graph;
  2795. var series = args.series || graph.series;
  2796. var vis = args.vis || graph.vis;
  2797. vis.selectAll('*').remove();
  2798. // insert or stacked areas so strokes lay on top of areas
  2799. var method = this.unstack ? 'append' : 'insert';
  2800. var data = series
  2801. .filter(function(s) { return !s.disabled })
  2802. .map(function(s) { return s.stack });
  2803. var nodes = vis.selectAll("path")
  2804. .data(data)
  2805. .enter()[method]("svg:g", 'g');
  2806. nodes.append("svg:path")
  2807. .attr("d", this.seriesPathFactory())
  2808. .attr("class", 'area');
  2809. if (this.stroke) {
  2810. nodes.append("svg:path")
  2811. .attr("d", this.seriesStrokeFactory())
  2812. .attr("class", 'line');
  2813. }
  2814. var i = 0;
  2815. series.forEach( function(series) {
  2816. if (series.disabled) return;
  2817. series.path = nodes[0][i++];
  2818. this._styleSeries(series);
  2819. }, this );
  2820. },
  2821. _styleSeries: function(series) {
  2822. if (!series.path) return;
  2823. d3.select(series.path).select('.area')
  2824. .attr('fill', series.color);
  2825. if (this.stroke) {
  2826. d3.select(series.path).select('.line')
  2827. .attr('fill', 'none')
  2828. .attr('stroke', series.stroke || d3.interpolateRgb(series.color, 'black')(0.125))
  2829. .attr('stroke-width', this.strokeWidth);
  2830. }
  2831. if (series.className) {
  2832. series.path.setAttribute('class', series.className);
  2833. }
  2834. }
  2835. } );
  2836. Rickshaw.namespace('Rickshaw.Graph.Renderer.ScatterPlot');
  2837. Rickshaw.Graph.Renderer.ScatterPlot = Rickshaw.Class.create( Rickshaw.Graph.Renderer, {
  2838. name: 'scatterplot',
  2839. defaults: function($super) {
  2840. return Rickshaw.extend( $super(), {
  2841. unstack: true,
  2842. fill: true,
  2843. stroke: false,
  2844. padding:{ top: 0.01, right: 0.01, bottom: 0.01, left: 0.01 },
  2845. dotSize: 4
  2846. } );
  2847. },
  2848. initialize: function($super, args) {
  2849. $super(args);
  2850. },
  2851. render: function(args) {
  2852. args = args || {};
  2853. var graph = this.graph;
  2854. var series = args.series || graph.series;
  2855. var vis = args.vis || graph.vis;
  2856. var dotSize = this.dotSize;
  2857. vis.selectAll('*').remove();
  2858. series.forEach( function(series) {
  2859. if (series.disabled) return;
  2860. var opacity = series.opacity ? series.opacity : 1;
  2861. var nodes = vis.selectAll("path")
  2862. .data(series.stack.filter( function(d) { return d.y !== null } ))
  2863. .enter().append("svg:circle")
  2864. .attr("cx", function(d) { return graph.x(d.x) })
  2865. .attr("cy", function(d) { return graph.y(d.y) })
  2866. .attr("r", function(d) { return ("r" in d) ? d.r : dotSize})
  2867. .attr("opacity", function(d) { return ("opacity" in d) ? d.opacity : opacity});
  2868. if (series.className) {
  2869. nodes.classed(series.className, true);
  2870. }
  2871. Array.prototype.forEach.call(nodes[0], function(n) {
  2872. n.setAttribute('fill', series.color);
  2873. } );
  2874. }, this );
  2875. }
  2876. } );
  2877. Rickshaw.namespace('Rickshaw.Graph.Renderer.Multi');
  2878. Rickshaw.Graph.Renderer.Multi = Rickshaw.Class.create( Rickshaw.Graph.Renderer, {
  2879. name: 'multi',
  2880. initialize: function($super, args) {
  2881. $super(args);
  2882. },
  2883. defaults: function($super) {
  2884. return Rickshaw.extend( $super(), {
  2885. unstack: true,
  2886. fill: false,
  2887. stroke: true
  2888. } );
  2889. },
  2890. configure: function($super, args) {
  2891. args = args || {};
  2892. this.config = args;
  2893. $super(args);
  2894. },
  2895. domain: function($super) {
  2896. this.graph.stackData();
  2897. var domains = [];
  2898. var groups = this._groups();
  2899. this._stack(groups);
  2900. groups.forEach( function(group) {
  2901. var data = group.series
  2902. .filter( function(s) { return !s.disabled } )
  2903. .map( function(s) { return s.stack });
  2904. if (!data.length) return;
  2905. var domain = null;
  2906. if (group.renderer && group.renderer.domain) {
  2907. domain = group.renderer.domain(data);
  2908. }
  2909. else {
  2910. domain = $super(data);
  2911. }
  2912. domains.push(domain);
  2913. });
  2914. var xMin = d3.min(domains.map( function(d) { return d.x[0] } ));
  2915. var xMax = d3.max(domains.map( function(d) { return d.x[1] } ));
  2916. var yMin = d3.min(domains.map( function(d) { return d.y[0] } ));
  2917. var yMax = d3.max(domains.map( function(d) { return d.y[1] } ));
  2918. return { x: [xMin, xMax], y: [yMin, yMax] };
  2919. },
  2920. _groups: function() {
  2921. var graph = this.graph;
  2922. var renderGroups = {};
  2923. graph.series.forEach( function(series) {
  2924. if (series.disabled) return;
  2925. if (!renderGroups[series.renderer]) {
  2926. var ns = "http://www.w3.org/2000/svg";
  2927. var vis = document.createElementNS(ns, 'g');
  2928. graph.vis[0][0].appendChild(vis);
  2929. var renderer = graph._renderers[series.renderer];
  2930. var config = {};
  2931. var defaults = [ this.defaults(), renderer.defaults(), this.config, this.graph ];
  2932. defaults.forEach(function(d) { Rickshaw.extend(config, d) });
  2933. renderer.configure(config);
  2934. renderGroups[series.renderer] = {
  2935. renderer: renderer,
  2936. series: [],
  2937. vis: d3.select(vis)
  2938. };
  2939. }
  2940. renderGroups[series.renderer].series.push(series);
  2941. }, this);
  2942. var groups = [];
  2943. Object.keys(renderGroups).forEach( function(key) {
  2944. var group = renderGroups[key];
  2945. groups.push(group);
  2946. });
  2947. return groups;
  2948. },
  2949. _stack: function(groups) {
  2950. groups.forEach( function(group) {
  2951. var series = group.series
  2952. .filter( function(series) { return !series.disabled } );
  2953. var data = series
  2954. .map( function(series) { return series.stack } );
  2955. if (!group.renderer.unstack) {
  2956. var layout = d3.layout.stack();
  2957. var stackedData = Rickshaw.clone(layout(data));
  2958. series.forEach( function(series, index) {
  2959. series._stack = Rickshaw.clone(stackedData[index]);
  2960. });
  2961. }
  2962. }, this );
  2963. return groups;
  2964. },
  2965. render: function() {
  2966. this.graph.series.forEach( function(series) {
  2967. if (!series.renderer) {
  2968. throw new Error("Each series needs a renderer for graph 'multi' renderer");
  2969. }
  2970. });
  2971. this.graph.vis.selectAll('*').remove();
  2972. var groups = this._groups();
  2973. groups = this._stack(groups);
  2974. groups.forEach( function(group) {
  2975. var series = group.series
  2976. .filter( function(series) { return !series.disabled } );
  2977. series.active = function() { return series };
  2978. group.renderer.render({ series: series, vis: group.vis });
  2979. series.forEach(function(s) { s.stack = s._stack || s.stack || s.data; });
  2980. });
  2981. }
  2982. } );
  2983. Rickshaw.namespace('Rickshaw.Graph.Renderer.LinePlot');
  2984. Rickshaw.Graph.Renderer.LinePlot = Rickshaw.Class.create( Rickshaw.Graph.Renderer, {
  2985. name: 'lineplot',
  2986. defaults: function($super) {
  2987. return Rickshaw.extend( $super(), {
  2988. unstack: true,
  2989. fill: false,
  2990. stroke: true,
  2991. padding:{ top: 0.01, right: 0.01, bottom: 0.01, left: 0.01 },
  2992. dotSize: 3,
  2993. strokeWidth: 2
  2994. } );
  2995. },
  2996. seriesPathFactory: function() {
  2997. var graph = this.graph;
  2998. var factory = d3.svg.line()
  2999. .x( function(d) { return graph.x(d.x) } )
  3000. .y( function(d) { return graph.y(d.y) } )
  3001. .interpolate(this.graph.interpolation).tension(this.tension);
  3002. factory.defined && factory.defined( function(d) { return d.y !== null } );
  3003. return factory;
  3004. },
  3005. render: function(args) {
  3006. args = args || {};
  3007. var graph = this.graph;
  3008. var series = args.series || graph.series;
  3009. var vis = args.vis || graph.vis;
  3010. var dotSize = this.dotSize;
  3011. vis.selectAll('*').remove();
  3012. var data = series
  3013. .filter(function(s) { return !s.disabled })
  3014. .map(function(s) { return s.stack });
  3015. var nodes = vis.selectAll("path")
  3016. .data(data)
  3017. .enter().append("svg:path")
  3018. .attr("d", this.seriesPathFactory());
  3019. var i = 0;
  3020. series.forEach(function(series) {
  3021. if (series.disabled) return;
  3022. series.path = nodes[0][i++];
  3023. this._styleSeries(series);
  3024. }, this);
  3025. series.forEach(function(series) {
  3026. if (series.disabled) return;
  3027. var nodes = vis.selectAll("x")
  3028. .data(series.stack.filter( function(d) { return d.y !== null } ))
  3029. .enter().append("svg:circle")
  3030. .attr("cx", function(d) { return graph.x(d.x) })
  3031. .attr("cy", function(d) { return graph.y(d.y) })
  3032. .attr("r", function(d) { return ("r" in d) ? d.r : dotSize});
  3033. Array.prototype.forEach.call(nodes[0], function(n) {
  3034. if (!n) return;
  3035. n.setAttribute('data-color', series.color);
  3036. n.setAttribute('fill', 'white');
  3037. n.setAttribute('stroke', series.color);
  3038. n.setAttribute('stroke-width', this.strokeWidth);
  3039. }.bind(this));
  3040. }, this);
  3041. }
  3042. } );
  3043. Rickshaw.namespace('Rickshaw.Graph.Smoother');
  3044. Rickshaw.Graph.Smoother = Rickshaw.Class.create({
  3045. initialize: function(args) {
  3046. this.graph = args.graph;
  3047. this.element = args.element;
  3048. this.aggregationScale = 1;
  3049. this.build();
  3050. this.graph.stackData.hooks.data.push( {
  3051. name: 'smoother',
  3052. orderPosition: 50,
  3053. f: this.transformer.bind(this)
  3054. } );
  3055. },
  3056. build: function() {
  3057. var self = this;
  3058. var $ = jQuery;
  3059. if (this.element) {
  3060. $( function() {
  3061. $(self.element).slider( {
  3062. min: 1,
  3063. max: 100,
  3064. slide: function( event, ui ) {
  3065. self.setScale(ui.value);
  3066. }
  3067. } );
  3068. } );
  3069. }
  3070. },
  3071. setScale: function(scale) {
  3072. if (scale < 1) {
  3073. throw "scale out of range: " + scale;
  3074. }
  3075. this.aggregationScale = scale;
  3076. this.graph.update();
  3077. },
  3078. transformer: function(data) {
  3079. if (this.aggregationScale == 1) return data;
  3080. var aggregatedData = [];
  3081. data.forEach( function(seriesData) {
  3082. var aggregatedSeriesData = [];
  3083. while (seriesData.length) {
  3084. var avgX = 0, avgY = 0;
  3085. var slice = seriesData.splice(0, this.aggregationScale);
  3086. slice.forEach( function(d) {
  3087. avgX += d.x / slice.length;
  3088. avgY += d.y / slice.length;
  3089. } );
  3090. aggregatedSeriesData.push( { x: avgX, y: avgY } );
  3091. }
  3092. aggregatedData.push(aggregatedSeriesData);
  3093. }.bind(this) );
  3094. return aggregatedData;
  3095. }
  3096. });
  3097. Rickshaw.namespace('Rickshaw.Graph.Socketio');
  3098. Rickshaw.Graph.Socketio = Rickshaw.Class.create( Rickshaw.Graph.Ajax, {
  3099. request: function() {
  3100. var socket = io.connect(this.dataURL);
  3101. var self = this;
  3102. socket.on('rickshaw', function (data) {
  3103. self.success(data);
  3104. });
  3105. }
  3106. } );
  3107. Rickshaw.namespace('Rickshaw.Series');
  3108. Rickshaw.Series = Rickshaw.Class.create( Array, {
  3109. initialize: function (data, palette, options) {
  3110. options = options || {};
  3111. this.palette = new Rickshaw.Color.Palette(palette);
  3112. this.timeBase = typeof(options.timeBase) === 'undefined' ?
  3113. Math.floor(new Date().getTime() / 1000) :
  3114. options.timeBase;
  3115. var timeInterval = typeof(options.timeInterval) == 'undefined' ?
  3116. 1000 :
  3117. options.timeInterval;
  3118. this.setTimeInterval(timeInterval);
  3119. if (data && (typeof(data) == "object") && Array.isArray(data)) {
  3120. data.forEach( function(item) { this.addItem(item) }, this );
  3121. }
  3122. },
  3123. addItem: function(item) {
  3124. if (typeof(item.name) === 'undefined') {
  3125. throw('addItem() needs a name');
  3126. }
  3127. item.color = (item.color || this.palette.color(item.name));
  3128. item.data = (item.data || []);
  3129. // backfill, if necessary
  3130. if ((item.data.length === 0) && this.length && (this.getIndex() > 0)) {
  3131. this[0].data.forEach( function(plot) {
  3132. item.data.push({ x: plot.x, y: 0 });
  3133. } );
  3134. } else if (item.data.length === 0) {
  3135. item.data.push({ x: this.timeBase - (this.timeInterval || 0), y: 0 });
  3136. }
  3137. this.push(item);
  3138. if (this.legend) {
  3139. this.legend.addLine(this.itemByName(item.name));
  3140. }
  3141. },
  3142. addData: function(data, x) {
  3143. var index = this.getIndex();
  3144. Rickshaw.keys(data).forEach( function(name) {
  3145. if (! this.itemByName(name)) {
  3146. this.addItem({ name: name });
  3147. }
  3148. }, this );
  3149. this.forEach( function(item) {
  3150. item.data.push({
  3151. x: x || (index * this.timeInterval || 1) + this.timeBase,
  3152. y: (data[item.name] || 0)
  3153. });
  3154. }, this );
  3155. },
  3156. getIndex: function () {
  3157. return (this[0] && this[0].data && this[0].data.length) ? this[0].data.length : 0;
  3158. },
  3159. itemByName: function(name) {
  3160. for (var i = 0; i < this.length; i++) {
  3161. if (this[i].name == name)
  3162. return this[i];
  3163. }
  3164. },
  3165. setTimeInterval: function(iv) {
  3166. this.timeInterval = iv / 1000;
  3167. },
  3168. setTimeBase: function (t) {
  3169. this.timeBase = t;
  3170. },
  3171. dump: function() {
  3172. var data = {
  3173. timeBase: this.timeBase,
  3174. timeInterval: this.timeInterval,
  3175. items: []
  3176. };
  3177. this.forEach( function(item) {
  3178. var newItem = {
  3179. color: item.color,
  3180. name: item.name,
  3181. data: []
  3182. };
  3183. item.data.forEach( function(plot) {
  3184. newItem.data.push({ x: plot.x, y: plot.y });
  3185. } );
  3186. data.items.push(newItem);
  3187. } );
  3188. return data;
  3189. },
  3190. load: function(data) {
  3191. if (data.timeInterval) {
  3192. this.timeInterval = data.timeInterval;
  3193. }
  3194. if (data.timeBase) {
  3195. this.timeBase = data.timeBase;
  3196. }
  3197. if (data.items) {
  3198. data.items.forEach( function(item) {
  3199. this.push(item);
  3200. if (this.legend) {
  3201. this.legend.addLine(this.itemByName(item.name));
  3202. }
  3203. }, this );
  3204. }
  3205. }
  3206. } );
  3207. Rickshaw.Series.zeroFill = function(series) {
  3208. Rickshaw.Series.fill(series, 0);
  3209. };
  3210. Rickshaw.Series.fill = function(series, fill) {
  3211. var x;
  3212. var i = 0;
  3213. var data = series.map( function(s) { return s.data } );
  3214. while ( i < Math.max.apply(null, data.map( function(d) { return d.length } )) ) {
  3215. x = Math.min.apply( null,
  3216. data
  3217. .filter(function(d) { return d[i] })
  3218. .map(function(d) { return d[i].x })
  3219. );
  3220. data.forEach( function(d) {
  3221. if (!d[i] || d[i].x != x) {
  3222. d.splice(i, 0, { x: x, y: fill });
  3223. }
  3224. } );
  3225. i++;
  3226. }
  3227. };
  3228. Rickshaw.namespace('Rickshaw.Series.FixedDuration');
  3229. Rickshaw.Series.FixedDuration = Rickshaw.Class.create(Rickshaw.Series, {
  3230. initialize: function (data, palette, options) {
  3231. options = options || {};
  3232. if (typeof(options.timeInterval) === 'undefined') {
  3233. throw new Error('FixedDuration series requires timeInterval');
  3234. }
  3235. if (typeof(options.maxDataPoints) === 'undefined') {
  3236. throw new Error('FixedDuration series requires maxDataPoints');
  3237. }
  3238. this.palette = new Rickshaw.Color.Palette(palette);
  3239. this.timeBase = typeof(options.timeBase) === 'undefined' ? Math.floor(new Date().getTime() / 1000) : options.timeBase;
  3240. this.setTimeInterval(options.timeInterval);
  3241. if (this[0] && this[0].data && this[0].data.length) {
  3242. this.currentSize = this[0].data.length;
  3243. this.currentIndex = this[0].data.length;
  3244. } else {
  3245. this.currentSize = 0;
  3246. this.currentIndex = 0;
  3247. }
  3248. this.maxDataPoints = options.maxDataPoints;
  3249. if (data && (typeof(data) == "object") && Array.isArray(data)) {
  3250. data.forEach( function (item) { this.addItem(item) }, this );
  3251. this.currentSize += 1;
  3252. this.currentIndex += 1;
  3253. }
  3254. // reset timeBase for zero-filled values if needed
  3255. this.timeBase -= (this.maxDataPoints - this.currentSize) * this.timeInterval;
  3256. // zero-fill up to maxDataPoints size if we don't have that much data yet
  3257. if ((typeof(this.maxDataPoints) !== 'undefined') && (this.currentSize < this.maxDataPoints)) {
  3258. for (var i = this.maxDataPoints - this.currentSize - 1; i > 1; i--) {
  3259. this.currentSize += 1;
  3260. this.currentIndex += 1;
  3261. this.forEach( function (item) {
  3262. item.data.unshift({ x: ((i-1) * this.timeInterval || 1) + this.timeBase, y: 0, i: i });
  3263. }, this );
  3264. }
  3265. }
  3266. },
  3267. addData: function($super, data, x) {
  3268. $super(data, x);
  3269. this.currentSize += 1;
  3270. this.currentIndex += 1;
  3271. if (this.maxDataPoints !== undefined) {
  3272. while (this.currentSize > this.maxDataPoints) {
  3273. this.dropData();
  3274. }
  3275. }
  3276. },
  3277. dropData: function() {
  3278. this.forEach(function(item) {
  3279. item.data.splice(0, 1);
  3280. } );
  3281. this.currentSize -= 1;
  3282. },
  3283. getIndex: function () {
  3284. return this.currentIndex;
  3285. }
  3286. } );
  3287. return Rickshaw;
  3288. }));