Dashboard sipadu mbip
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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