stock-tools.src.js 115 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434
  1. /**
  2. * @license Highcharts JS v7.0.2 (2019-01-17)
  3. * Advanced Highstock tools
  4. *
  5. * (c) 2010-2019 Highsoft AS
  6. * Author: Torstein Honsi
  7. *
  8. * License: www.highcharts.com/license
  9. */
  10. 'use strict';
  11. (function (factory) {
  12. if (typeof module === 'object' && module.exports) {
  13. factory['default'] = factory;
  14. module.exports = factory;
  15. } else if (typeof define === 'function' && define.amd) {
  16. define(function () {
  17. return factory;
  18. });
  19. } else {
  20. factory(typeof Highcharts !== 'undefined' ? Highcharts : undefined);
  21. }
  22. }(function (Highcharts) {
  23. (function (H) {
  24. /**
  25. *
  26. * Events generator for Stock tools
  27. *
  28. * (c) 2009-2019 Paweł Fus
  29. *
  30. * License: www.highcharts.com/license
  31. *
  32. * */
  33. /**
  34. * A config object for bindings in Stock Tools module.
  35. *
  36. * @interface Highcharts.StockToolsBindingsObject
  37. *//**
  38. * ClassName of the element for a binding.
  39. * @name Highcharts.StockToolsBindingsObject#className
  40. * @type {string|undefined}
  41. *//**
  42. * Last event to be fired after last step event.
  43. * @name Highcharts.StockToolsBindingsObject#end
  44. * @type {Function|undefined}
  45. *//**
  46. * Initial event, fired on a button click.
  47. * @name Highcharts.StockToolsBindingsObject#init
  48. * @type {Function|undefined}
  49. *//**
  50. * Event fired on first click on a chart.
  51. * @name Highcharts.StockToolsBindingsObject#start
  52. * @type {Function|undefined}
  53. *//**
  54. * Last event to be fired after last step event. Array of step events to be
  55. * called sequentially after each user click.
  56. * @name Highcharts.StockToolsBindingsObject#steps
  57. * @type {Array<Function>|undefined}
  58. */
  59. var fireEvent = H.fireEvent,
  60. defined = H.defined,
  61. pick = H.pick,
  62. extend = H.extend,
  63. isNumber = H.isNumber,
  64. correctFloat = H.correctFloat,
  65. bindingsUtils = H.NavigationBindings.prototype.utils,
  66. PREFIX = 'highcharts-';
  67. /**
  68. * Generates function which will add a flag series using modal in GUI.
  69. * Method fires an event "showPopup" with config:
  70. * `{type, options, callback}`.
  71. *
  72. * Example: NavigationBindings.utils.addFlagFromForm('url(...)') - will
  73. * generate function that shows modal in GUI.
  74. *
  75. * @private
  76. * @function bindingsUtils.addFlagFromForm
  77. *
  78. * @param {string} type
  79. * Type of flag series, e.g. "squarepin"
  80. *
  81. * @return {Function}
  82. * Callback to be used in `start` callback
  83. */
  84. bindingsUtils.addFlagFromForm = function (type) {
  85. return function (e) {
  86. var navigation = this,
  87. chart = navigation.chart,
  88. toolbar = chart.toolbar,
  89. getFieldType = bindingsUtils.getFieldType,
  90. point = bindingsUtils.attractToPoint(e, chart),
  91. pointConfig = {
  92. x: point.x,
  93. y: point.y
  94. },
  95. seriesOptions = {
  96. type: 'flags',
  97. onSeries: point.series.id,
  98. shape: type,
  99. data: [pointConfig],
  100. point: {
  101. events: {
  102. click: function () {
  103. var point = this,
  104. options = point.options;
  105. fireEvent(
  106. navigation,
  107. 'showPopup',
  108. {
  109. point: point,
  110. formType: 'annotation-toolbar',
  111. options: {
  112. langKey: 'flags',
  113. type: 'flags',
  114. title: [
  115. options.title,
  116. getFieldType(
  117. options.title
  118. )
  119. ],
  120. name: [
  121. options.name,
  122. getFieldType(
  123. options.name
  124. )
  125. ]
  126. },
  127. onSubmit: function (updated) {
  128. point.update(
  129. navigation.fieldsToOptions(
  130. updated.fields,
  131. {}
  132. )
  133. );
  134. }
  135. }
  136. );
  137. }
  138. }
  139. }
  140. };
  141. if (!toolbar || !toolbar.guiEnabled) {
  142. chart.addSeries(seriesOptions);
  143. }
  144. fireEvent(
  145. navigation,
  146. 'showPopup',
  147. {
  148. formType: 'flag',
  149. // Enabled options:
  150. options: {
  151. langKey: 'flags',
  152. type: 'flags',
  153. title: ['A', getFieldType('A')],
  154. name: ['Flag A', getFieldType('Flag A')]
  155. },
  156. // Callback on submit:
  157. onSubmit: function (data) {
  158. navigation.fieldsToOptions(
  159. data.fields,
  160. seriesOptions.data[0]
  161. );
  162. chart.addSeries(seriesOptions);
  163. }
  164. }
  165. );
  166. };
  167. };
  168. bindingsUtils.manageIndicators = function (data) {
  169. var navigation = this,
  170. chart = navigation.chart,
  171. seriesConfig = {
  172. linkedTo: data.linkedTo,
  173. type: data.type
  174. },
  175. indicatorsWithVolume = [
  176. 'ad',
  177. 'cmf',
  178. 'mfi',
  179. 'vbp',
  180. 'vwap'
  181. ],
  182. indicatorsWithAxes = [
  183. 'ad',
  184. 'atr',
  185. 'cci',
  186. 'cmf',
  187. 'macd',
  188. 'mfi',
  189. 'roc',
  190. 'rsi',
  191. 'vwap',
  192. 'ao',
  193. 'aroon',
  194. 'aroonoscillator',
  195. 'trix',
  196. 'apo',
  197. 'dpo',
  198. 'ppo',
  199. 'natr',
  200. 'williamsr',
  201. 'linearRegression',
  202. 'linearRegressionSlope',
  203. 'linearRegressionIntercept',
  204. 'linearRegressionAngle'
  205. ],
  206. yAxis,
  207. series;
  208. if (data.actionType === 'edit') {
  209. navigation.fieldsToOptions(data.fields, seriesConfig);
  210. series = chart.get(data.seriesId);
  211. if (series) {
  212. series.update(seriesConfig, false);
  213. }
  214. } else if (data.actionType === 'remove') {
  215. series = chart.get(data.seriesId);
  216. if (series) {
  217. yAxis = series.yAxis;
  218. if (series.linkedSeries) {
  219. series.linkedSeries.forEach(function (linkedSeries) {
  220. linkedSeries.remove(false);
  221. });
  222. }
  223. series.remove(false);
  224. if (indicatorsWithAxes.indexOf(series.type) >= 0) {
  225. yAxis.remove(false);
  226. navigation.resizeYAxes();
  227. }
  228. }
  229. } else {
  230. seriesConfig.id = H.uniqueKey();
  231. navigation.fieldsToOptions(data.fields, seriesConfig);
  232. if (indicatorsWithAxes.indexOf(data.type) >= 0) {
  233. yAxis = chart.addAxis({
  234. id: H.uniqueKey(),
  235. offset: 0,
  236. opposite: true,
  237. title: {
  238. text: ''
  239. },
  240. tickPixelInterval: 40,
  241. showLastLabel: false,
  242. labels: {
  243. align: 'left',
  244. y: -2
  245. }
  246. }, false, false);
  247. seriesConfig.yAxis = yAxis.options.id;
  248. navigation.resizeYAxes();
  249. }
  250. if (indicatorsWithVolume.indexOf(data.type) >= 0) {
  251. seriesConfig.params.volumeSeriesID = chart.series.filter(
  252. function (series) {
  253. return series.options.type === 'column';
  254. }
  255. )[0].options.id;
  256. }
  257. chart.addSeries(seriesConfig, false);
  258. }
  259. fireEvent(
  260. navigation,
  261. 'deselectButton',
  262. {
  263. button: navigation.selectedButtonElement
  264. }
  265. );
  266. chart.redraw();
  267. };
  268. /**
  269. * Update height for an annotation. Height is calculated as a difference
  270. * between last point in `typeOptions` and current position. It's a value,
  271. * not pixels height.
  272. *
  273. * @private
  274. * @function bindingsUtils.updateHeight
  275. *
  276. * @param {global.Event} e
  277. * normalized browser event
  278. *
  279. * @param {Highcharts.Annotation} annotation
  280. * Annotation to be updated
  281. */
  282. bindingsUtils.updateHeight = function (e, annotation) {
  283. annotation.update({
  284. typeOptions: {
  285. height: this.chart.yAxis[0].toValue(e.chartY) -
  286. annotation.options.typeOptions.points[1].y
  287. }
  288. });
  289. };
  290. // @todo
  291. // Consider using getHoverData(), but always kdTree (columns?)
  292. bindingsUtils.attractToPoint = function (e, chart) {
  293. var x = chart.xAxis[0].toValue(e.chartX),
  294. y = chart.yAxis[0].toValue(e.chartY),
  295. distX = Number.MAX_VALUE,
  296. closestPoint;
  297. chart.series.forEach(function (series) {
  298. series.points.forEach(function (point) {
  299. if (point && distX > Math.abs(point.x - x)) {
  300. distX = Math.abs(point.x - x);
  301. closestPoint = point;
  302. }
  303. });
  304. });
  305. return {
  306. x: closestPoint.x,
  307. y: closestPoint.y,
  308. below: y < closestPoint.y,
  309. series: closestPoint.series,
  310. xAxis: closestPoint.series.xAxis.index || 0,
  311. yAxis: closestPoint.series.yAxis.index || 0
  312. };
  313. };
  314. /**
  315. * Shorthand to check if given yAxis comes from navigator.
  316. *
  317. * @private
  318. * @function bindingsUtils.isNotNavigatorYAxis
  319. *
  320. * @param {Highcharts.Axis} axis
  321. * Axis
  322. *
  323. * @return {boolean}
  324. */
  325. bindingsUtils.isNotNavigatorYAxis = function (axis) {
  326. return axis.userOptions.className !== PREFIX + 'navigator-yaxis';
  327. };
  328. /**
  329. * Update each point after specified index, most of the annotations use
  330. * this. For example crooked line: logic behind updating each point is the
  331. * same, only index changes when adding an annotation.
  332. *
  333. * Example: NavigationBindings.utils.updateNthPoint(1) - will generate
  334. * function that updates all consecutive points except point with index=0.
  335. *
  336. * @private
  337. * @function bindingsUtils.updateNthPoint
  338. *
  339. * @param {number} startIndex
  340. * Index from each point should udpated
  341. *
  342. * @return {Function}
  343. * Callback to be used in steps array
  344. */
  345. bindingsUtils.updateNthPoint = function (startIndex) {
  346. return function (e, annotation) {
  347. var options = annotation.options.typeOptions,
  348. x = this.chart.xAxis[0].toValue(e.chartX),
  349. y = this.chart.yAxis[0].toValue(e.chartY);
  350. options.points.forEach(function (point, index) {
  351. if (index >= startIndex) {
  352. point.x = x;
  353. point.y = y;
  354. }
  355. });
  356. annotation.update({
  357. typeOptions: {
  358. points: options.points
  359. }
  360. });
  361. };
  362. };
  363. // Extends NavigationBindigs to support indicators and resizers:
  364. extend(H.NavigationBindings.prototype, {
  365. /**
  366. * Get current positions for all yAxes. If new axis does not have position,
  367. * returned is default height and last available top place.
  368. *
  369. * @private
  370. * @function Highcharts.NavigationBindings#getYAxisPositions
  371. *
  372. * @param {Array<Highcharts.Axis>} yAxes
  373. * Array of yAxes available in the chart.
  374. *
  375. * @param {number} plotHeight
  376. * Available height in the chart.
  377. *
  378. * @param {number} defaultHeight
  379. * Default height in percents.
  380. *
  381. * @return {Array}
  382. * An array of calculated positions in percentages.
  383. * Format: `{top: Number, height: Number}`
  384. */
  385. getYAxisPositions: function (yAxes, plotHeight, defaultHeight) {
  386. var positions,
  387. allAxesHeight = 0;
  388. function isPercentage(prop) {
  389. return defined(prop) && !isNumber(prop) && prop.match('%');
  390. }
  391. positions = yAxes.map(function (yAxis) {
  392. var height = isPercentage(yAxis.options.height) ?
  393. parseFloat(yAxis.options.height) / 100 :
  394. yAxis.height / plotHeight,
  395. top = isPercentage(yAxis.options.top) ?
  396. parseFloat(yAxis.options.top) / 100 :
  397. correctFloat(
  398. yAxis.top - yAxis.chart.plotTop
  399. ) / plotHeight;
  400. // New yAxis does not contain "height" info yet
  401. if (!isNumber(height)) {
  402. height = defaultHeight / 100;
  403. }
  404. allAxesHeight = correctFloat(allAxesHeight + height);
  405. return {
  406. height: height * 100,
  407. top: top * 100
  408. };
  409. });
  410. positions.allAxesHeight = allAxesHeight;
  411. return positions;
  412. },
  413. /**
  414. * Get current resize options for each yAxis. Note that each resize is
  415. * linked to the next axis, except the last one which shouldn't affect
  416. * axes in the navigator. Because indicator can be removed with it's yAxis
  417. * in the middle of yAxis array, we need to bind closest yAxes back.
  418. *
  419. * @private
  420. * @function Highcharts.NavigationBindings#getYAxisResizers
  421. *
  422. * @param {Array<Highcharts.Axis>} yAxes
  423. * Array of yAxes available in the chart
  424. *
  425. * @return {Array<object>}
  426. * An array of resizer options.
  427. * Format: `{enabled: Boolean, controlledAxis: { next: [String]}}`
  428. */
  429. getYAxisResizers: function (yAxes) {
  430. var resizers = [];
  431. yAxes.forEach(function (yAxis, index) {
  432. var nextYAxis = yAxes[index + 1];
  433. // We have next axis, bind them:
  434. if (nextYAxis) {
  435. resizers[index] = {
  436. enabled: true,
  437. controlledAxis: {
  438. next: [
  439. pick(
  440. nextYAxis.options.id,
  441. nextYAxis.options.index
  442. )
  443. ]
  444. }
  445. };
  446. } else {
  447. // Remove binding:
  448. resizers[index] = {
  449. enabled: false
  450. };
  451. }
  452. });
  453. return resizers;
  454. },
  455. /**
  456. * Resize all yAxes (except navigator) to fit the plotting height. Method
  457. * checks if new axis is added, then shrinks other main axis up to 5 panes.
  458. * If added is more thatn 5 panes, it rescales all other axes to fit new
  459. * yAxis.
  460. *
  461. * If axis is removed, and we have more than 5 panes, rescales all other
  462. * axes. If chart has less than 5 panes, first pane receives all extra
  463. * space.
  464. *
  465. * @private
  466. * @function Highcharts.NavigationBindings#resizeYAxes
  467. *
  468. * @param {number} defaultHeight
  469. * Default height for yAxis
  470. */
  471. resizeYAxes: function (defaultHeight) {
  472. defaultHeight = defaultHeight || 20; // in %, but as a number
  473. var chart = this.chart,
  474. // Only non-navigator axes
  475. yAxes = chart.yAxis.filter(this.utils.isNotNavigatorYAxis),
  476. plotHeight = chart.plotHeight,
  477. allAxesLength = yAxes.length,
  478. // Gather current heights (in %)
  479. positions = this.getYAxisPositions(
  480. yAxes,
  481. plotHeight,
  482. defaultHeight
  483. ),
  484. resizers = this.getYAxisResizers(yAxes),
  485. allAxesHeight = positions.allAxesHeight,
  486. changedSpace = defaultHeight;
  487. // More than 100%
  488. if (allAxesHeight > 1) {
  489. // Simple case, add new panes up to 5
  490. if (allAxesLength < 6) {
  491. // Added axis, decrease first pane's height:
  492. positions[0].height = correctFloat(
  493. positions[0].height - changedSpace
  494. );
  495. // And update all other "top" positions:
  496. positions = this.recalculateYAxisPositions(
  497. positions,
  498. changedSpace
  499. );
  500. } else {
  501. // We have more panes, rescale all others to gain some space,
  502. // This is new height for upcoming yAxis:
  503. defaultHeight = 100 / allAxesLength;
  504. // This is how much we need to take from each other yAxis:
  505. changedSpace = defaultHeight / (allAxesLength - 1);
  506. // Now update all positions:
  507. positions = this.recalculateYAxisPositions(
  508. positions,
  509. changedSpace,
  510. true,
  511. -1
  512. );
  513. }
  514. // Set last position manually:
  515. positions[allAxesLength - 1] = {
  516. top: correctFloat(100 - defaultHeight),
  517. height: defaultHeight
  518. };
  519. } else {
  520. // Less than 100%
  521. changedSpace = correctFloat(1 - allAxesHeight) * 100;
  522. // Simple case, return first pane it's space:
  523. if (allAxesLength < 5) {
  524. positions[0].height = correctFloat(
  525. positions[0].height + changedSpace
  526. );
  527. positions = this.recalculateYAxisPositions(
  528. positions,
  529. changedSpace
  530. );
  531. } else {
  532. // There were more panes, return to each pane a bit of space:
  533. changedSpace /= allAxesLength;
  534. // Removed axis, add extra space to the first pane:
  535. // And update all other positions:
  536. positions = this.recalculateYAxisPositions(
  537. positions,
  538. changedSpace,
  539. true,
  540. 1
  541. );
  542. }
  543. }
  544. positions.forEach(function (position, index) {
  545. // if (index === 0) debugger;
  546. yAxes[index].update({
  547. height: position.height + '%',
  548. top: position.top + '%',
  549. resize: resizers[index]
  550. }, false);
  551. });
  552. },
  553. /**
  554. * Utility to modify calculated positions according to the remaining/needed
  555. * space. Later, these positions are used in `yAxis.update({ top, height })`
  556. *
  557. * @private
  558. * @function Highcharts.NavigationBindings#recalculateYAxisPositions
  559. *
  560. * @param {Array<object>} positions
  561. * Default positions of all yAxes.
  562. *
  563. * @param {number} changedSpace
  564. * How much space should be added or removed.
  565. * @param {number} adder
  566. * `-1` or `1`, to determine whether we should add or remove space.
  567. *
  568. * @param {boolean} modifyHeight
  569. * Update only `top` or both `top` and `height`.
  570. *
  571. * @return {Array<object>}
  572. * Modified positions,
  573. */
  574. recalculateYAxisPositions: function (
  575. positions,
  576. changedSpace,
  577. modifyHeight,
  578. adder
  579. ) {
  580. positions.forEach(function (position, index) {
  581. var prevPosition = positions[index - 1];
  582. position.top = !prevPosition ? 0 :
  583. correctFloat(prevPosition.height + prevPosition.top);
  584. if (modifyHeight) {
  585. position.height = correctFloat(
  586. position.height + adder * changedSpace
  587. );
  588. }
  589. });
  590. return positions;
  591. }
  592. });
  593. /**
  594. * @type {Highcharts.Dictionary<Highcharts.StockToolsBindingsObject>|*}
  595. * @since 7.0.0
  596. * @optionparent navigation.bindings
  597. */
  598. var stockToolsBindings = {
  599. // Line type annotations:
  600. /**
  601. * A segment annotation bindings. Includes `start` and one event in `steps`
  602. * array.
  603. *
  604. * @type {Highcharts.StockToolsBindingsObject}
  605. * @product highstock
  606. * @default {"className": "highcharts-segment", "start": function() {}, "steps": [function() {}]}
  607. */
  608. segment: {
  609. /** @ignore */
  610. className: 'highcharts-segment',
  611. /** @ignore */
  612. start: function (e) {
  613. var x = this.chart.xAxis[0].toValue(e.chartX),
  614. y = this.chart.yAxis[0].toValue(e.chartY);
  615. return this.chart.addAnnotation({
  616. langKey: 'segment',
  617. type: 'crookedLine',
  618. typeOptions: {
  619. points: [{
  620. x: x,
  621. y: y
  622. }, {
  623. x: x,
  624. y: y
  625. }]
  626. }
  627. });
  628. },
  629. /** @ignore */
  630. steps: [
  631. bindingsUtils.updateNthPoint(1)
  632. ]
  633. },
  634. /**
  635. * A segment with an arrow annotation bindings. Includes `start` and one
  636. * event in `steps` array.
  637. *
  638. * @type {Highcharts.StockToolsBindingsObject}
  639. * @product highstock
  640. * @default {"className": "highcharts-arrow-segment", "start": function() {}, "steps": [function() {}]}
  641. */
  642. arrowSegment: {
  643. /** @ignore */
  644. className: 'highcharts-arrow-segment',
  645. /** @ignore */
  646. start: function (e) {
  647. var x = this.chart.xAxis[0].toValue(e.chartX),
  648. y = this.chart.yAxis[0].toValue(e.chartY);
  649. return this.chart.addAnnotation({
  650. langKey: 'arrowSegment',
  651. type: 'crookedLine',
  652. typeOptions: {
  653. line: {
  654. markerEnd: 'arrow'
  655. },
  656. points: [{
  657. x: x,
  658. y: y
  659. }, {
  660. x: x,
  661. y: y
  662. }]
  663. }
  664. });
  665. },
  666. /** @ignore */
  667. steps: [
  668. bindingsUtils.updateNthPoint(1)
  669. ]
  670. },
  671. /**
  672. * A ray annotation bindings. Includes `start` and one event in `steps`
  673. * array.
  674. *
  675. * @type {Highcharts.StockToolsBindingsObject}
  676. * @product highstock
  677. * @default {"className": "highcharts-ray", "start": function() {}, "steps": [function() {}]}
  678. */
  679. ray: {
  680. /** @ignore */
  681. className: 'highcharts-ray',
  682. /** @ignore */
  683. start: function (e) {
  684. var x = this.chart.xAxis[0].toValue(e.chartX),
  685. y = this.chart.yAxis[0].toValue(e.chartY);
  686. return this.chart.addAnnotation({
  687. langKey: 'ray',
  688. type: 'infinityLine',
  689. typeOptions: {
  690. type: 'ray',
  691. points: [{
  692. x: x,
  693. y: y
  694. }, {
  695. x: x,
  696. y: y
  697. }]
  698. }
  699. });
  700. },
  701. /** @ignore */
  702. steps: [
  703. bindingsUtils.updateNthPoint(1)
  704. ]
  705. },
  706. /**
  707. * A ray with an arrow annotation bindings. Includes `start` and one event
  708. * in `steps` array.
  709. *
  710. * @type {Highcharts.StockToolsBindingsObject}
  711. * @product highstock
  712. * @default {"className": "highcharts-arrow-ray", "start": function() {}, "steps": [function() {}]}
  713. */
  714. arrowRay: {
  715. /** @ignore */
  716. className: 'highcharts-arrow-ray',
  717. /** @ignore */
  718. start: function (e) {
  719. var x = this.chart.xAxis[0].toValue(e.chartX),
  720. y = this.chart.yAxis[0].toValue(e.chartY);
  721. return this.chart.addAnnotation({
  722. langKey: 'arrowRay',
  723. type: 'infinityLine',
  724. typeOptions: {
  725. type: 'ray',
  726. line: {
  727. markerEnd: 'arrow'
  728. },
  729. points: [{
  730. x: x,
  731. y: y
  732. }, {
  733. x: x,
  734. y: y
  735. }]
  736. }
  737. });
  738. },
  739. /** @ignore */
  740. steps: [
  741. bindingsUtils.updateNthPoint(1)
  742. ]
  743. },
  744. /**
  745. * A line annotation. Includes `start` and one event in `steps` array.
  746. *
  747. * @type {Highcharts.StockToolsBindingsObject}
  748. * @product highstock
  749. * @default {"className": "highcharts-infinity-line", "start": function() {}, "steps": [function() {}]}
  750. */
  751. infinityLine: {
  752. /** @ignore */
  753. className: 'highcharts-infinity-line',
  754. /** @ignore */
  755. start: function (e) {
  756. var x = this.chart.xAxis[0].toValue(e.chartX),
  757. y = this.chart.yAxis[0].toValue(e.chartY);
  758. return this.chart.addAnnotation({
  759. langKey: 'infinityLine',
  760. type: 'infinityLine',
  761. typeOptions: {
  762. type: 'line',
  763. points: [{
  764. x: x,
  765. y: y
  766. }, {
  767. x: x,
  768. y: y
  769. }]
  770. }
  771. });
  772. },
  773. /** @ignore */
  774. steps: [
  775. bindingsUtils.updateNthPoint(1)
  776. ]
  777. },
  778. /**
  779. * A line with arrow annotation. Includes `start` and one event in `steps`
  780. * array.
  781. *
  782. * @type {Highcharts.StockToolsBindingsObject}
  783. * @product highstock
  784. * @default {"className": "highcharts-arrow-infinity-line", "start": function() {}, "steps": [function() {}]}
  785. */
  786. arrowInfinityLine: {
  787. /** @ignore */
  788. className: 'highcharts-arrow-infinity-line',
  789. /** @ignore */
  790. start: function (e) {
  791. var x = this.chart.xAxis[0].toValue(e.chartX),
  792. y = this.chart.yAxis[0].toValue(e.chartY);
  793. return this.chart.addAnnotation({
  794. langKey: 'arrowInfinityLine',
  795. type: 'infinityLine',
  796. typeOptions: {
  797. type: 'line',
  798. line: {
  799. markerEnd: 'arrow'
  800. },
  801. points: [{
  802. x: x,
  803. y: y
  804. }, {
  805. x: x,
  806. y: y
  807. }]
  808. }
  809. });
  810. },
  811. /** @ignore */
  812. steps: [
  813. bindingsUtils.updateNthPoint(1)
  814. ]
  815. },
  816. /**
  817. * A horizontal line annotation. Includes `start` event.
  818. *
  819. * @type {Highcharts.StockToolsBindingsObject}
  820. * @product highstock
  821. * @default {"className": "highcharts-horizontal-line", "start": function() {}}
  822. */
  823. horizontalLine: {
  824. /** @ignore */
  825. className: 'highcharts-horizontal-line',
  826. /** @ignore */
  827. start: function (e) {
  828. var x = this.chart.xAxis[0].toValue(e.chartX),
  829. y = this.chart.yAxis[0].toValue(e.chartY);
  830. this.chart.addAnnotation({
  831. langKey: 'horizontalLine',
  832. type: 'infinityLine',
  833. typeOptions: {
  834. type: 'horizontalLine',
  835. points: [{
  836. x: x,
  837. y: y
  838. }]
  839. }
  840. });
  841. }
  842. },
  843. /**
  844. * A vertical line annotation. Includes `start` event.
  845. *
  846. * @type {Highcharts.StockToolsBindingsObject}
  847. * @product highstock
  848. * @default {"className": "highcharts-vertical-line", "start": function() {}}
  849. */
  850. verticalLine: {
  851. /** @ignore */
  852. className: 'highcharts-vertical-line',
  853. /** @ignore */
  854. start: function (e) {
  855. var x = this.chart.xAxis[0].toValue(e.chartX),
  856. y = this.chart.yAxis[0].toValue(e.chartY);
  857. this.chart.addAnnotation({
  858. langKey: 'verticalLine',
  859. type: 'infinityLine',
  860. typeOptions: {
  861. type: 'verticalLine',
  862. points: [{
  863. x: x,
  864. y: y
  865. }]
  866. }
  867. });
  868. }
  869. },
  870. /**
  871. * Crooked line (three points) annotation bindings. Includes `start` and two
  872. * events in `steps` (for second and third points in crooked line) array.
  873. *
  874. * @type {Highcharts.StockToolsBindingsObject}
  875. * @product highstock
  876. * @default {"className": "highcharts-crooked3", "start": function() {}, "steps": [function() {}, function() {}]}
  877. */
  878. // Crooked Line type annotations:
  879. crooked3: {
  880. /** @ignore */
  881. className: 'highcharts-crooked3',
  882. /** @ignore */
  883. start: function (e) {
  884. var x = this.chart.xAxis[0].toValue(e.chartX),
  885. y = this.chart.yAxis[0].toValue(e.chartY);
  886. return this.chart.addAnnotation({
  887. langKey: 'crooked3',
  888. type: 'crookedLine',
  889. typeOptions: {
  890. points: [{
  891. x: x,
  892. y: y
  893. }, {
  894. x: x,
  895. y: y
  896. }, {
  897. x: x,
  898. y: y
  899. }]
  900. }
  901. });
  902. },
  903. /** @ignore */
  904. steps: [
  905. bindingsUtils.updateNthPoint(1),
  906. bindingsUtils.updateNthPoint(2)
  907. ]
  908. },
  909. /**
  910. * Crooked line (five points) annotation bindings. Includes `start` and four
  911. * events in `steps` (for all consequent points in crooked line) array.
  912. *
  913. * @type {Highcharts.StockToolsBindingsObject}
  914. * @product highstock
  915. * @default {"className": "highcharts-crooked3", "start": function() {}, "steps": [function() {}, function() {}, function() {}, function() {}]}
  916. */
  917. crooked5: {
  918. /** @ignore */
  919. className: 'highcharts-crooked5',
  920. /** @ignore */
  921. start: function (e) {
  922. var x = this.chart.xAxis[0].toValue(e.chartX),
  923. y = this.chart.yAxis[0].toValue(e.chartY);
  924. return this.chart.addAnnotation({
  925. langKey: 'crookedLine',
  926. type: 'crookedLine',
  927. typeOptions: {
  928. points: [{
  929. x: x,
  930. y: y
  931. }, {
  932. x: x,
  933. y: y
  934. }, {
  935. x: x,
  936. y: y
  937. }, {
  938. x: x,
  939. y: y
  940. }, {
  941. x: x,
  942. y: y
  943. }]
  944. }
  945. });
  946. },
  947. /** @ignore */
  948. steps: [
  949. bindingsUtils.updateNthPoint(1),
  950. bindingsUtils.updateNthPoint(2),
  951. bindingsUtils.updateNthPoint(3),
  952. bindingsUtils.updateNthPoint(4)
  953. ]
  954. },
  955. /**
  956. * Elliott wave (three points) annotation bindings. Includes `start` and two
  957. * events in `steps` (for second and third points) array.
  958. *
  959. * @type {Highcharts.StockToolsBindingsObject}
  960. * @product highstock
  961. * @default {"className": "highcharts-elliott3", "start": function() {}, "steps": [function() {}, function() {}]}
  962. */
  963. elliott3: {
  964. /** @ignore */
  965. className: 'highcharts-elliott3',
  966. /** @ignore */
  967. start: function (e) {
  968. var x = this.chart.xAxis[0].toValue(e.chartX),
  969. y = this.chart.yAxis[0].toValue(e.chartY);
  970. return this.chart.addAnnotation({
  971. langKey: 'elliott3',
  972. type: 'elliottWave',
  973. typeOptions: {
  974. points: [{
  975. x: x,
  976. y: y
  977. }, {
  978. x: x,
  979. y: y
  980. }, {
  981. x: x,
  982. y: y
  983. }]
  984. },
  985. labelOptions: {
  986. style: {
  987. color: '#666666'
  988. }
  989. }
  990. });
  991. },
  992. /** @ignore */
  993. steps: [
  994. bindingsUtils.updateNthPoint(1),
  995. bindingsUtils.updateNthPoint(2)
  996. ]
  997. },
  998. /**
  999. * Elliott wave (five points) annotation bindings. Includes `start` and four
  1000. * event in `steps` (for all consequent points in Elliott wave) array.
  1001. *
  1002. * @type {Highcharts.StockToolsBindingsObject}
  1003. * @product highstock
  1004. * @default {"className": "highcharts-elliott3", "start": function() {}, "steps": [function() {}, function() {}, function() {}, function() {}]}
  1005. */
  1006. elliott5: {
  1007. /** @ignore */
  1008. className: 'highcharts-elliott5',
  1009. /** @ignore */
  1010. start: function (e) {
  1011. var x = this.chart.xAxis[0].toValue(e.chartX),
  1012. y = this.chart.yAxis[0].toValue(e.chartY);
  1013. return this.chart.addAnnotation({
  1014. langKey: 'elliott5',
  1015. type: 'elliottWave',
  1016. typeOptions: {
  1017. points: [{
  1018. x: x,
  1019. y: y
  1020. }, {
  1021. x: x,
  1022. y: y
  1023. }, {
  1024. x: x,
  1025. y: y
  1026. }, {
  1027. x: x,
  1028. y: y
  1029. }, {
  1030. x: x,
  1031. y: y
  1032. }]
  1033. },
  1034. labelOptions: {
  1035. style: {
  1036. color: '#666666'
  1037. }
  1038. }
  1039. });
  1040. },
  1041. /** @ignore */
  1042. steps: [
  1043. bindingsUtils.updateNthPoint(1),
  1044. bindingsUtils.updateNthPoint(2),
  1045. bindingsUtils.updateNthPoint(3),
  1046. bindingsUtils.updateNthPoint(4)
  1047. ]
  1048. },
  1049. /**
  1050. * A measure (x-dimension) annotation bindings. Includes `start` and one
  1051. * event in `steps` array.
  1052. *
  1053. * @type {Highcharts.StockToolsBindingsObject}
  1054. * @product highstock
  1055. * @default {"className": "highcharts-measure-x", "start": function() {}, "steps": [function() {}]}
  1056. */
  1057. measureX: {
  1058. /** @ignore */
  1059. className: 'highcharts-measure-x',
  1060. /** @ignore */
  1061. start: function (e) {
  1062. var x = this.chart.xAxis[0].toValue(e.chartX),
  1063. y = this.chart.yAxis[0].toValue(e.chartY),
  1064. options = {
  1065. langKey: 'measure',
  1066. type: 'measure',
  1067. typeOptions: {
  1068. selectType: 'x',
  1069. point: {
  1070. x: x,
  1071. y: y,
  1072. xAxis: 0,
  1073. yAxis: 0
  1074. },
  1075. crosshairX: {
  1076. strokeWidth: 1,
  1077. stroke: '#000000'
  1078. },
  1079. crosshairY: {
  1080. enabled: false,
  1081. strokeWidth: 0,
  1082. stroke: '#000000'
  1083. },
  1084. background: {
  1085. width: 0,
  1086. height: 0,
  1087. strokeWidth: 0,
  1088. stroke: '#ffffff'
  1089. }
  1090. },
  1091. labelOptions: {
  1092. style: {
  1093. color: '#666666'
  1094. }
  1095. }
  1096. };
  1097. return this.chart.addAnnotation(options);
  1098. },
  1099. /** @ignore */
  1100. steps: [
  1101. bindingsUtils.updateRectSize
  1102. ]
  1103. },
  1104. /**
  1105. * A measure (y-dimension) annotation bindings. Includes `start` and one
  1106. * event in `steps` array.
  1107. *
  1108. * @type {Highcharts.StockToolsBindingsObject}
  1109. * @product highstock
  1110. * @default {"className": "highcharts-measure-y", "start": function() {}, "steps": [function() {}]}
  1111. */
  1112. measureY: {
  1113. /** @ignore */
  1114. className: 'highcharts-measure-y',
  1115. /** @ignore */
  1116. start: function (e) {
  1117. var x = this.chart.xAxis[0].toValue(e.chartX),
  1118. y = this.chart.yAxis[0].toValue(e.chartY),
  1119. options = {
  1120. langKey: 'measure',
  1121. type: 'measure',
  1122. typeOptions: {
  1123. selectType: 'y',
  1124. point: {
  1125. x: x,
  1126. y: y,
  1127. xAxis: 0,
  1128. yAxis: 0
  1129. },
  1130. crosshairX: {
  1131. enabled: false,
  1132. strokeWidth: 0,
  1133. stroke: '#000000'
  1134. },
  1135. crosshairY: {
  1136. strokeWidth: 1,
  1137. stroke: '#000000'
  1138. },
  1139. background: {
  1140. width: 0,
  1141. height: 0,
  1142. strokeWidth: 0,
  1143. stroke: '#ffffff'
  1144. }
  1145. },
  1146. labelOptions: {
  1147. style: {
  1148. color: '#666666'
  1149. }
  1150. }
  1151. };
  1152. return this.chart.addAnnotation(options);
  1153. },
  1154. /** @ignore */
  1155. steps: [
  1156. bindingsUtils.updateRectSize
  1157. ]
  1158. },
  1159. /**
  1160. * A measure (xy-dimension) annotation bindings. Includes `start` and one
  1161. * event in `steps` array.
  1162. *
  1163. * @type {Highcharts.StockToolsBindingsObject}
  1164. * @product highstock
  1165. * @default {"className": "highcharts-measure-xy", "start": function() {}, "steps": [function() {}]}
  1166. */
  1167. measureXY: {
  1168. /** @ignore */
  1169. className: 'highcharts-measure-xy',
  1170. /** @ignore */
  1171. start: function (e) {
  1172. var x = this.chart.xAxis[0].toValue(e.chartX),
  1173. y = this.chart.yAxis[0].toValue(e.chartY),
  1174. options = {
  1175. langKey: 'measure',
  1176. type: 'measure',
  1177. typeOptions: {
  1178. selectType: 'xy',
  1179. point: {
  1180. x: x,
  1181. y: y,
  1182. xAxis: 0,
  1183. yAxis: 0
  1184. },
  1185. background: {
  1186. width: 0,
  1187. height: 0,
  1188. strokeWidth: 0,
  1189. stroke: '#000000'
  1190. },
  1191. crosshairX: {
  1192. strokeWidth: 1,
  1193. stroke: '#000000'
  1194. },
  1195. crosshairY: {
  1196. strokeWidth: 1,
  1197. stroke: '#000000'
  1198. }
  1199. },
  1200. labelOptions: {
  1201. style: {
  1202. color: '#666666'
  1203. }
  1204. }
  1205. };
  1206. return this.chart.addAnnotation(options);
  1207. },
  1208. /** @ignore */
  1209. steps: [
  1210. bindingsUtils.updateRectSize
  1211. ]
  1212. },
  1213. // Advanced type annotations:
  1214. /**
  1215. * A fibonacci annotation bindings. Includes `start` and two events in
  1216. * `steps` array (updates second point, then height).
  1217. *
  1218. * @type {Highcharts.StockToolsBindingsObject}
  1219. * @product highstock
  1220. * @default {"className": "highcharts-fibonacci", "start": function() {}, "steps": [function() {}, function() {}]}
  1221. */
  1222. fibonacci: {
  1223. /** @ignore */
  1224. className: 'highcharts-fibonacci',
  1225. /** @ignore */
  1226. start: function (e) {
  1227. var x = this.chart.xAxis[0].toValue(e.chartX),
  1228. y = this.chart.yAxis[0].toValue(e.chartY);
  1229. return this.chart.addAnnotation({
  1230. langKey: 'fibonacci',
  1231. type: 'fibonacci',
  1232. typeOptions: {
  1233. points: [{
  1234. x: x,
  1235. y: y
  1236. }, {
  1237. x: x,
  1238. y: y
  1239. }]
  1240. },
  1241. labelOptions: {
  1242. style: {
  1243. color: '#666666'
  1244. }
  1245. }
  1246. });
  1247. },
  1248. /** @ignore */
  1249. steps: [
  1250. bindingsUtils.updateNthPoint(1),
  1251. bindingsUtils.updateHeight
  1252. ]
  1253. },
  1254. /**
  1255. * A parallel channel (tunnel) annotation bindings. Includes `start` and
  1256. * two events in `steps` array (updates second point, then height).
  1257. *
  1258. * @type {Highcharts.StockToolsBindingsObject}
  1259. * @product highstock
  1260. * @default {"className": "highcharts-parallel-channel", "start": function() {}, "steps": [function() {}, function() {}]}
  1261. */
  1262. parallelChannel: {
  1263. /** @ignore */
  1264. className: 'highcharts-parallel-channel',
  1265. /** @ignore */
  1266. start: function (e) {
  1267. var x = this.chart.xAxis[0].toValue(e.chartX),
  1268. y = this.chart.yAxis[0].toValue(e.chartY);
  1269. return this.chart.addAnnotation({
  1270. langKey: 'parallelChannel',
  1271. type: 'tunnel',
  1272. typeOptions: {
  1273. points: [{
  1274. x: x,
  1275. y: y
  1276. }, {
  1277. x: x,
  1278. y: y
  1279. }]
  1280. }
  1281. });
  1282. },
  1283. /** @ignore */
  1284. steps: [
  1285. bindingsUtils.updateNthPoint(1),
  1286. bindingsUtils.updateHeight
  1287. ]
  1288. },
  1289. /**
  1290. * An Andrew's pitchfork annotation bindings. Includes `start` and two
  1291. * events in `steps` array (sets second and third control points).
  1292. *
  1293. * @type {Highcharts.StockToolsBindingsObject}
  1294. * @product highstock
  1295. * @default {"className": "highcharts-pitchfork", "start": function() {}, "steps": [function() {}, function() {}]}
  1296. */
  1297. pitchfork: {
  1298. /** @ignore */
  1299. className: 'highcharts-pitchfork',
  1300. /** @ignore */
  1301. start: function (e) {
  1302. var x = this.chart.xAxis[0].toValue(e.chartX),
  1303. y = this.chart.yAxis[0].toValue(e.chartY);
  1304. return this.chart.addAnnotation({
  1305. langKey: 'pitchfork',
  1306. type: 'pitchfork',
  1307. typeOptions: {
  1308. points: [{
  1309. x: x,
  1310. y: y,
  1311. controlPoint: {
  1312. style: {
  1313. fill: 'red'
  1314. }
  1315. }
  1316. }, {
  1317. x: x,
  1318. y: y
  1319. }, {
  1320. x: x,
  1321. y: y
  1322. }],
  1323. innerBackground: {
  1324. fill: 'rgba(100, 170, 255, 0.8)'
  1325. }
  1326. },
  1327. shapeOptions: {
  1328. strokeWidth: 2
  1329. }
  1330. });
  1331. },
  1332. /** @ignore */
  1333. steps: [
  1334. bindingsUtils.updateNthPoint(1),
  1335. bindingsUtils.updateNthPoint(2)
  1336. ]
  1337. },
  1338. // Labels with arrow and auto increments
  1339. /**
  1340. * A vertical counter annotation bindings. Includes `start` event. On click,
  1341. * finds the closest point and marks it with a numeric annotation -
  1342. * incrementing counter on each add.
  1343. *
  1344. * @type {Highcharts.StockToolsBindingsObject}
  1345. * @product highstock
  1346. * @default {"className": "highcharts-vertical-counter", "start": function() {}}
  1347. */
  1348. verticalCounter: {
  1349. /** @ignore */
  1350. className: 'highcharts-vertical-counter',
  1351. /** @ignore */
  1352. start: function (e) {
  1353. var closestPoint = bindingsUtils.attractToPoint(e, this.chart),
  1354. annotation;
  1355. if (!defined(this.verticalCounter)) {
  1356. this.verticalCounter = 0;
  1357. }
  1358. annotation = this.chart.addAnnotation({
  1359. langKey: 'verticalCounter',
  1360. type: 'verticalLine',
  1361. typeOptions: {
  1362. point: {
  1363. x: closestPoint.x,
  1364. y: closestPoint.y,
  1365. xAxis: closestPoint.xAxis,
  1366. yAxis: closestPoint.yAxis
  1367. },
  1368. label: {
  1369. offset: closestPoint.below ? 40 : -40,
  1370. text: this.verticalCounter.toString()
  1371. }
  1372. },
  1373. labelOptions: {
  1374. style: {
  1375. color: '#666666',
  1376. fontSize: '11px'
  1377. }
  1378. },
  1379. shapeOptions: {
  1380. stroke: 'rgba(0, 0, 0, 0.75)',
  1381. strokeWidth: 1
  1382. }
  1383. });
  1384. this.verticalCounter++;
  1385. annotation.options.events.click.call(annotation, {});
  1386. }
  1387. },
  1388. /**
  1389. * A vertical arrow annotation bindings. Includes `start` event. On click,
  1390. * finds the closest point and marks it with an arrow and a label with
  1391. * value.
  1392. *
  1393. * @type {Highcharts.StockToolsBindingsObject}
  1394. * @product highstock
  1395. * @default {"className": "highcharts-vertical-label", "start": function() {}}
  1396. */
  1397. verticalLabel: {
  1398. /** @ignore */
  1399. className: 'highcharts-vertical-label',
  1400. /** @ignore */
  1401. start: function (e) {
  1402. var closestPoint = bindingsUtils.attractToPoint(e, this.chart),
  1403. annotation;
  1404. annotation = this.chart.addAnnotation({
  1405. langKey: 'verticalLabel',
  1406. type: 'verticalLine',
  1407. typeOptions: {
  1408. point: {
  1409. x: closestPoint.x,
  1410. y: closestPoint.y,
  1411. xAxis: closestPoint.xAxis,
  1412. yAxis: closestPoint.yAxis
  1413. },
  1414. label: {
  1415. offset: closestPoint.below ? 40 : -40
  1416. }
  1417. },
  1418. labelOptions: {
  1419. style: {
  1420. color: '#666666',
  1421. fontSize: '11px'
  1422. }
  1423. },
  1424. shapeOptions: {
  1425. stroke: 'rgba(0, 0, 0, 0.75)',
  1426. strokeWidth: 1
  1427. }
  1428. });
  1429. annotation.options.events.click.call(annotation, {});
  1430. }
  1431. },
  1432. /**
  1433. * A vertical arrow annotation bindings. Includes `start` event. On click,
  1434. * finds the closest point and marks it with an arrow. Green arrow when
  1435. * pointing from above, red when pointing from below the point.
  1436. *
  1437. * @type {Highcharts.StockToolsBindingsObject}
  1438. * @product highstock
  1439. * @default {"className": "highcharts-vertical-arrow", "start": function() {}}
  1440. */
  1441. verticalArrow: {
  1442. /** @ignore */
  1443. className: 'highcharts-vertical-arrow',
  1444. /** @ignore */
  1445. start: function (e) {
  1446. var closestPoint = bindingsUtils.attractToPoint(e, this.chart),
  1447. annotation;
  1448. annotation = this.chart.addAnnotation({
  1449. langKey: 'verticalArrow',
  1450. type: 'verticalLine',
  1451. typeOptions: {
  1452. point: {
  1453. x: closestPoint.x,
  1454. y: closestPoint.y,
  1455. xAxis: closestPoint.xAxis,
  1456. yAxis: closestPoint.yAxis
  1457. },
  1458. label: {
  1459. offset: closestPoint.below ? 40 : -40,
  1460. format: ' '
  1461. },
  1462. connector: {
  1463. fill: 'none',
  1464. stroke: closestPoint.below ? 'red' : 'green'
  1465. }
  1466. },
  1467. shapeOptions: {
  1468. stroke: 'rgba(0, 0, 0, 0.75)',
  1469. strokeWidth: 1
  1470. }
  1471. });
  1472. annotation.options.events.click.call(annotation, {});
  1473. }
  1474. },
  1475. // Flag types:
  1476. /**
  1477. * A flag series bindings. Includes `start` event. On click, finds the
  1478. * closest point and marks it with a flag with `'circlepin'` shape.
  1479. *
  1480. * @type {Highcharts.StockToolsBindingsObject}
  1481. * @product highstock
  1482. * @default {"className": "highcharts-flag-circlepin", "start": function() {}}
  1483. */
  1484. flagCirclepin: {
  1485. /** @ignore */
  1486. className: 'highcharts-flag-circlepin',
  1487. /** @ignore */
  1488. start: bindingsUtils
  1489. .addFlagFromForm('circlepin')
  1490. },
  1491. /**
  1492. * A flag series bindings. Includes `start` event. On click, finds the
  1493. * closest point and marks it with a flag with `'diamondpin'` shape.
  1494. *
  1495. * @type {Highcharts.StockToolsBindingsObject}
  1496. * @product highstock
  1497. * @default {"className": "highcharts-flag-diamondpin", "start": function() {}}
  1498. */
  1499. flagDiamondpin: {
  1500. /** @ignore */
  1501. className: 'highcharts-flag-diamondpin',
  1502. /** @ignore */
  1503. start: bindingsUtils
  1504. .addFlagFromForm('flag')
  1505. },
  1506. /**
  1507. * A flag series bindings. Includes `start` event.
  1508. * On click, finds the closest point and marks it with a flag with
  1509. * `'squarepin'` shape.
  1510. *
  1511. * @type {Highcharts.StockToolsBindingsObject}
  1512. * @product highstock
  1513. * @default {"className": "highcharts-flag-squarepin", "start": function() {}}
  1514. */
  1515. flagSquarepin: {
  1516. /** @ignore */
  1517. className: 'highcharts-flag-squarepin',
  1518. /** @ignore */
  1519. start: bindingsUtils
  1520. .addFlagFromForm('squarepin')
  1521. },
  1522. /**
  1523. * A flag series bindings. Includes `start` event.
  1524. * On click, finds the closest point and marks it with a flag without pin
  1525. * shape.
  1526. *
  1527. * @type {Highcharts.StockToolsBindingsObject}
  1528. * @product highstock
  1529. * @default {"className": "highcharts-flag-simplepin", "start": function() {}}
  1530. */
  1531. flagSimplepin: {
  1532. /** @ignore */
  1533. className: 'highcharts-flag-simplepin',
  1534. /** @ignore */
  1535. start: bindingsUtils
  1536. .addFlagFromForm('nopin')
  1537. },
  1538. // Other tools:
  1539. /**
  1540. * Enables zooming in xAxis on a chart. Includes `start` event which
  1541. * changes [chart.zoomType](#chart.zoomType).
  1542. *
  1543. * @type {Highcharts.StockToolsBindingsObject}
  1544. * @product highstock
  1545. * @default {"className": "highcharts-zoom-x", "init": function() {}}
  1546. */
  1547. zoomX: {
  1548. /** @ignore */
  1549. className: 'highcharts-zoom-x',
  1550. /** @ignore */
  1551. init: function (button) {
  1552. this.chart.update({
  1553. chart: {
  1554. zoomType: 'x'
  1555. }
  1556. });
  1557. fireEvent(
  1558. this,
  1559. 'deselectButton',
  1560. { button: button }
  1561. );
  1562. }
  1563. },
  1564. /**
  1565. * Enables zooming in yAxis on a chart. Includes `start` event which
  1566. * changes [chart.zoomType](#chart.zoomType).
  1567. *
  1568. * @type {Highcharts.StockToolsBindingsObject}
  1569. * @product highstock
  1570. * @default {"className": "highcharts-zoom-y", "init": function() {}}
  1571. */
  1572. zoomY: {
  1573. /** @ignore */
  1574. className: 'highcharts-zoom-y',
  1575. /** @ignore */
  1576. init: function (button) {
  1577. this.chart.update({
  1578. chart: {
  1579. zoomType: 'y'
  1580. }
  1581. });
  1582. fireEvent(
  1583. this,
  1584. 'deselectButton',
  1585. { button: button }
  1586. );
  1587. }
  1588. },
  1589. /**
  1590. * Enables zooming in xAxis and yAxis on a chart. Includes `start` event
  1591. * which changes [chart.zoomType](#chart.zoomType).
  1592. *
  1593. * @type {Highcharts.StockToolsBindingsObject}
  1594. * @product highstock
  1595. * @default {"className": "highcharts-zoom-xy", "init": function() {}}
  1596. */
  1597. zoomXY: {
  1598. /** @ignore */
  1599. className: 'highcharts-zoom-xy',
  1600. /** @ignore */
  1601. init: function (button) {
  1602. this.chart.update({
  1603. chart: {
  1604. zoomType: 'xy'
  1605. }
  1606. });
  1607. fireEvent(
  1608. this,
  1609. 'deselectButton',
  1610. { button: button }
  1611. );
  1612. }
  1613. },
  1614. /**
  1615. * Changes main series to `'line'` type.
  1616. *
  1617. * @type {Highcharts.StockToolsBindingsObject}
  1618. * @product highstock
  1619. * @default {"className": "highcharts-series-type-line", "init": function() {}}
  1620. */
  1621. seriesTypeLine: {
  1622. /** @ignore */
  1623. className: 'highcharts-series-type-line',
  1624. /** @ignore */
  1625. init: function (button) {
  1626. this.chart.series[0].update({
  1627. type: 'line'
  1628. });
  1629. fireEvent(
  1630. this,
  1631. 'deselectButton',
  1632. { button: button }
  1633. );
  1634. }
  1635. },
  1636. /**
  1637. * Changes main series to `'ohlc'` type.
  1638. *
  1639. * @type {Highcharts.StockToolsBindingsObject}
  1640. * @product highstock
  1641. * @default {"className": "highcharts-series-type-ohlc", "init": function() {}}
  1642. */
  1643. seriesTypeOhlc: {
  1644. /** @ignore */
  1645. className: 'highcharts-series-type-ohlc',
  1646. /** @ignore */
  1647. init: function (button) {
  1648. this.chart.series[0].update({
  1649. type: 'ohlc'
  1650. });
  1651. fireEvent(
  1652. this,
  1653. 'deselectButton',
  1654. { button: button }
  1655. );
  1656. }
  1657. },
  1658. /**
  1659. * Changes main series to `'candlestick'` type.
  1660. *
  1661. * @type {Highcharts.StockToolsBindingsObject}
  1662. * @product highstock
  1663. * @default {"className": "highcharts-series-type-candlestick", "init": function() {}}
  1664. */
  1665. seriesTypeCandlestick: {
  1666. /** @ignore */
  1667. className: 'highcharts-series-type-candlestick',
  1668. /** @ignore */
  1669. init: function (button) {
  1670. this.chart.series[0].update({
  1671. type: 'candlestick'
  1672. });
  1673. fireEvent(
  1674. this,
  1675. 'deselectButton',
  1676. { button: button }
  1677. );
  1678. }
  1679. },
  1680. /**
  1681. * Displays chart in fullscreen.
  1682. *
  1683. * @type {Highcharts.StockToolsBindingsObject}
  1684. * @product highstock
  1685. * @default {"className": "highcharts-full-screen", "init": function() {}}
  1686. */
  1687. fullScreen: {
  1688. /** @ignore */
  1689. className: 'highcharts-full-screen',
  1690. /** @ignore */
  1691. init: function (button) {
  1692. var chart = this.chart;
  1693. chart.fullScreen = new H.FullScreen(chart.container);
  1694. fireEvent(
  1695. this,
  1696. 'deselectButton',
  1697. { button: button }
  1698. );
  1699. }
  1700. },
  1701. /**
  1702. * Hides/shows two price indicators:
  1703. * - last price in the dataset
  1704. * - last price in the selected range
  1705. *
  1706. * @type {Highcharts.StockToolsBindingsObject}
  1707. * @product highstock
  1708. * @default {"className": "highcharts-current-price-indicator", "init": function() {}}
  1709. */
  1710. currentPriceIndicator: {
  1711. /** @ignore */
  1712. className: 'highcharts-current-price-indicator',
  1713. /** @ignore */
  1714. init: function (button) {
  1715. var series = this.chart.series[0],
  1716. options = series.options,
  1717. lastVisiblePrice = options.lastVisiblePrice &&
  1718. options.lastVisiblePrice.enabled,
  1719. lastPrice = options.lastPrice && options.lastPrice.enabled,
  1720. gui = this.chart.stockToolbar;
  1721. if (gui && gui.guiEnabled) {
  1722. if (lastPrice) {
  1723. button.firstChild.style['background-image'] =
  1724. 'url("' + gui.options.iconsURL +
  1725. 'current-price-show.svg")';
  1726. } else {
  1727. button.firstChild.style['background-image'] =
  1728. 'url("' + gui.options.iconsURL +
  1729. 'current-price-hide.svg")';
  1730. }
  1731. }
  1732. series.update({
  1733. // line
  1734. lastPrice: {
  1735. enabled: !lastPrice,
  1736. color: 'red'
  1737. },
  1738. // label
  1739. lastVisiblePrice: {
  1740. enabled: !lastVisiblePrice,
  1741. label: {
  1742. enabled: true
  1743. }
  1744. }
  1745. });
  1746. fireEvent(
  1747. this,
  1748. 'deselectButton',
  1749. { button: button }
  1750. );
  1751. }
  1752. },
  1753. /**
  1754. * Indicators bindings. Includes `init` event to show a popup.
  1755. *
  1756. * @type {Highcharts.StockToolsBindingsObject}
  1757. * @product highstock
  1758. * @default {"className": "highcharts-indicators", "init": function() {}}
  1759. */
  1760. indicators: {
  1761. /** @ignore */
  1762. className: 'highcharts-indicators',
  1763. /** @ignore */
  1764. init: function () {
  1765. var navigation = this;
  1766. fireEvent(
  1767. navigation,
  1768. 'showPopup',
  1769. {
  1770. formType: 'indicators',
  1771. options: {},
  1772. // Callback on submit:
  1773. onSubmit: function (data) {
  1774. navigation.utils.manageIndicators.call(
  1775. navigation,
  1776. data
  1777. );
  1778. }
  1779. }
  1780. );
  1781. }
  1782. },
  1783. /**
  1784. * Hides/shows all annotations on a chart.
  1785. *
  1786. * @type {Highcharts.StockToolsBindingsObject}
  1787. * @product highstock
  1788. * @default {"className": "highcharts-toggle-annotations", "init": function() {}}
  1789. */
  1790. toggleAnnotations: {
  1791. /** @ignore */
  1792. className: 'highcharts-toggle-annotations',
  1793. /** @ignore */
  1794. init: function (button) {
  1795. var gui = this.chart.stockToolbar;
  1796. this.toggledAnnotations = !this.toggledAnnotations;
  1797. (this.chart.annotations || []).forEach(function (annotation) {
  1798. annotation.setVisibility(!this.toggledAnnotations);
  1799. }, this);
  1800. if (gui && gui.guiEnabled) {
  1801. if (this.toggledAnnotations) {
  1802. button.firstChild.style['background-image'] =
  1803. 'url("' + gui.options.iconsURL +
  1804. 'annotations-hidden.svg")';
  1805. } else {
  1806. button.firstChild.style['background-image'] =
  1807. 'url("' + gui.options.iconsURL +
  1808. 'annotations-visible.svg")';
  1809. }
  1810. }
  1811. fireEvent(
  1812. this,
  1813. 'deselectButton',
  1814. { button: button }
  1815. );
  1816. }
  1817. },
  1818. /**
  1819. * Save a chart in localStorage under `highcharts-chart` key.
  1820. * Stored items:
  1821. * - annotations
  1822. * - indicators (with yAxes)
  1823. * - flags
  1824. *
  1825. * @type {Highcharts.StockToolsBindingsObject}
  1826. * @product highstock
  1827. * @default {"className": "highcharts-save-chart", "init": function() {}}
  1828. */
  1829. saveChart: {
  1830. /** @ignore */
  1831. className: 'highcharts-save-chart',
  1832. /** @ignore */
  1833. init: function (button) {
  1834. var navigation = this,
  1835. chart = navigation.chart,
  1836. annotations = [],
  1837. indicators = [],
  1838. flags = [],
  1839. yAxes = [];
  1840. chart.annotations.forEach(function (annotation, index) {
  1841. annotations[index] = annotation.userOptions;
  1842. });
  1843. chart.series.forEach(function (series) {
  1844. if (series instanceof H.seriesTypes.sma) {
  1845. indicators.push(series.userOptions);
  1846. } else if (series.type === 'flags') {
  1847. flags.push(series.userOptions);
  1848. }
  1849. });
  1850. chart.yAxis.forEach(function (yAxis) {
  1851. if (navigation.utils.isNotNavigatorYAxis(yAxis)) {
  1852. yAxes.push(yAxis.options);
  1853. }
  1854. });
  1855. H.win.localStorage.setItem(
  1856. PREFIX + 'chart',
  1857. JSON.stringify({
  1858. annotations: annotations,
  1859. indicators: indicators,
  1860. flags: flags,
  1861. yAxes: yAxes
  1862. })
  1863. );
  1864. fireEvent(
  1865. this,
  1866. 'deselectButton',
  1867. { button: button }
  1868. );
  1869. }
  1870. }
  1871. };
  1872. H.setOptions({
  1873. navigation: {
  1874. bindings: stockToolsBindings
  1875. }
  1876. });
  1877. }(Highcharts));
  1878. (function (H) {
  1879. /**
  1880. * GUI generator for Stock tools
  1881. *
  1882. * (c) 2009-2017 Sebastian Bochan
  1883. *
  1884. * License: www.highcharts.com/license
  1885. */
  1886. var addEvent = H.addEvent,
  1887. createElement = H.createElement,
  1888. pick = H.pick,
  1889. isArray = H.isArray,
  1890. fireEvent = H.fireEvent,
  1891. getStyle = H.getStyle,
  1892. merge = H.merge,
  1893. css = H.css,
  1894. win = H.win,
  1895. DIV = 'div',
  1896. SPAN = 'span',
  1897. UL = 'ul',
  1898. LI = 'li',
  1899. PREFIX = 'highcharts-',
  1900. activeClass = PREFIX + 'active';
  1901. H.setOptions({
  1902. /**
  1903. * @optionparent lang
  1904. */
  1905. lang: {
  1906. /**
  1907. * Configure the stockTools GUI titles(hints) in the chart. Requires
  1908. * the `stock-tools.js` module to be loaded.
  1909. *
  1910. * @product highstock
  1911. * @since 7.0.0
  1912. * @type {Object}
  1913. */
  1914. stockTools: {
  1915. gui: {
  1916. // Main buttons:
  1917. simpleShapes: 'Simple shapes',
  1918. lines: 'Lines',
  1919. crookedLines: 'Crooked lines',
  1920. measure: 'Measure',
  1921. advanced: 'Advanced',
  1922. toggleAnnotations: 'Toggle annotations',
  1923. verticalLabels: 'Vertical labels',
  1924. flags: 'Flags',
  1925. zoomChange: 'Zoom change',
  1926. typeChange: 'Type change',
  1927. saveChart: 'Save chart',
  1928. indicators: 'Indicators',
  1929. currentPriceIndicator: 'Current Price Indicators',
  1930. // Other features:
  1931. zoomX: 'Zoom X',
  1932. zoomY: 'Zoom Y',
  1933. zoomXY: 'Zooom XY',
  1934. fullScreen: 'Fullscreen',
  1935. typeOHLC: 'OHLC',
  1936. typeLine: 'Line',
  1937. typeCandlestick: 'Candlestick',
  1938. // Basic shapes:
  1939. circle: 'Circle',
  1940. label: 'Label',
  1941. rectangle: 'Rectangle',
  1942. // Flags:
  1943. flagCirclepin: 'Flag circle',
  1944. flagDiamondpin: 'Flag diamond',
  1945. flagSquarepin: 'Flag square',
  1946. flagSimplepin: 'Flag simple',
  1947. // Measures:
  1948. measureXY: 'Measure XY',
  1949. measureX: 'Measure X',
  1950. measureY: 'Measure Y',
  1951. // Segment, ray and line:
  1952. segment: 'Segment',
  1953. arrowSegment: 'Arrow segment',
  1954. ray: 'Ray',
  1955. arrowRay: 'Arrow ray',
  1956. line: 'Line',
  1957. arrowLine: 'Arrow line',
  1958. horizontalLine: 'Horizontal line',
  1959. verticalLine: 'Vertical line',
  1960. infinityLine: 'Infinity line',
  1961. // Crooked lines:
  1962. crooked3: 'Crooked 3 line',
  1963. crooked5: 'Crooked 5 line',
  1964. elliott3: 'Elliott 3 line',
  1965. elliott5: 'Elliott 5 line',
  1966. // Counters:
  1967. verticalCounter: 'Vertical counter',
  1968. verticalLabel: 'Vertical label',
  1969. verticalArrow: 'Vertical arrow',
  1970. // Advanced:
  1971. fibonacci: 'Fibonacci',
  1972. pitchfork: 'Pitchfork',
  1973. parallelChannel: 'Parallel channel'
  1974. }
  1975. },
  1976. navigation: {
  1977. popup: {
  1978. // Annotations:
  1979. circle: 'Circle',
  1980. rectangle: 'Rectangle',
  1981. label: 'Label',
  1982. segment: 'Segment',
  1983. arrowSegment: 'Arrow segment',
  1984. ray: 'Ray',
  1985. arrowRay: 'Arrow ray',
  1986. line: 'Line',
  1987. arrowLine: 'Arrow line',
  1988. horizontalLine: 'Horizontal line',
  1989. verticalLine: 'Vertical line',
  1990. crooked3: 'Crooked 3 line',
  1991. crooked5: 'Crooked 5 line',
  1992. elliott3: 'Elliott 3 line',
  1993. elliott5: 'Elliott 5 line',
  1994. verticalCounter: 'Vertical counter',
  1995. verticalLabel: 'Vertical label',
  1996. verticalArrow: 'Vertical arrow',
  1997. fibonacci: 'Fibonacci',
  1998. pitchfork: 'Pitchfork',
  1999. parallelChannel: 'Parallel channel',
  2000. infinityLine: 'Infinity line',
  2001. measure: 'Measure',
  2002. measureXY: 'Measure XY',
  2003. measureX: 'Measure X',
  2004. measureY: 'Measure Y',
  2005. // Flags:
  2006. flags: 'Flags',
  2007. // GUI elements:
  2008. addButton: 'add',
  2009. saveButton: 'save',
  2010. editButton: 'edit',
  2011. removeButton: 'remove',
  2012. series: 'Series',
  2013. volume: 'Volume',
  2014. connector: 'Connector',
  2015. // Field names:
  2016. innerBackground: 'Inner background',
  2017. outerBackground: 'Outer background',
  2018. crosshairX: 'Crosshair X',
  2019. crosshairY: 'Crosshair Y',
  2020. tunnel: 'Tunnel',
  2021. background: 'Background'
  2022. }
  2023. }
  2024. },
  2025. /**
  2026. * Configure the stockTools gui strings in the chart. Requires the
  2027. * [stockTools module]() to be loaded. For a description of the module
  2028. * and information on its features, see [Highcharts StockTools]().
  2029. *
  2030. * @product highstock
  2031. *
  2032. * @sample stock/demo/stock-tools-gui Stock Tools GUI
  2033. *
  2034. * @sample stock/demo/stock-tools-custom-gui Stock Tools customized GUI
  2035. *
  2036. * @since 7.0.0
  2037. * @type {Object}
  2038. * @optionparent stockTools
  2039. */
  2040. stockTools: {
  2041. /**
  2042. * Definitions of buttons in Stock Tools GUI.
  2043. */
  2044. gui: {
  2045. /**
  2046. * Enable or disable the stockTools gui.
  2047. *
  2048. * @type {boolean}
  2049. * @default true
  2050. */
  2051. enabled: true,
  2052. /**
  2053. * A CSS class name to apply to the stocktools' div,
  2054. * allowing unique CSS styling for each chart.
  2055. *
  2056. * @type {string}
  2057. * @default 'highcharts-bindings-wrapper'
  2058. *
  2059. */
  2060. className: 'highcharts-bindings-wrapper',
  2061. /**
  2062. * A CSS class name to apply to the container of buttons,
  2063. * allowing unique CSS styling for each chart.
  2064. *
  2065. * @type {string}
  2066. * @default 'stocktools-toolbar'
  2067. *
  2068. */
  2069. toolbarClassName: 'stocktools-toolbar',
  2070. /**
  2071. * Path where Highcharts will look for icons. Change this to use
  2072. * icons from a different server.
  2073. */
  2074. iconsURL: 'https://code.highcharts.com/7.0.2/gfx/stock-icons/',
  2075. /**
  2076. * A collection of strings pointing to config options for the
  2077. * toolbar items. Each name refers to unique key from definitions
  2078. * object.
  2079. *
  2080. * @type {array}
  2081. *
  2082. * @default [
  2083. * 'indicators',
  2084. * 'separator',
  2085. * 'simpleShapes',
  2086. * 'lines',
  2087. * 'crookedLines',
  2088. * 'measure',
  2089. * 'advanced',
  2090. * 'toggleAnnotations',
  2091. * 'separator',
  2092. * 'verticalLabels',
  2093. * 'flags',
  2094. * 'separator',
  2095. * 'zoomChange',
  2096. * 'fullScreen',
  2097. * 'typeChange',
  2098. * 'separator',
  2099. * 'currentPriceIndicator',
  2100. * 'saveChart'
  2101. * ]
  2102. */
  2103. buttons: [
  2104. 'indicators',
  2105. 'separator',
  2106. 'simpleShapes',
  2107. 'lines',
  2108. 'crookedLines',
  2109. 'measure',
  2110. 'advanced',
  2111. 'toggleAnnotations',
  2112. 'separator',
  2113. 'verticalLabels',
  2114. 'flags',
  2115. 'separator',
  2116. 'zoomChange',
  2117. 'fullScreen',
  2118. 'typeChange',
  2119. 'separator',
  2120. 'currentPriceIndicator',
  2121. 'saveChart'
  2122. ],
  2123. /**
  2124. * An options object of the buttons definitions. Each name refers to
  2125. * unique key from buttons array.
  2126. *
  2127. * @type {object}
  2128. *
  2129. */
  2130. definitions: {
  2131. separator: {
  2132. /**
  2133. * A predefined background symbol for the button.
  2134. *
  2135. * @type {string}
  2136. */
  2137. symbol: 'separator.svg'
  2138. },
  2139. simpleShapes: {
  2140. /**
  2141. * A collection of strings pointing to config options for
  2142. * the items.
  2143. *
  2144. * @type {array}
  2145. * @default [
  2146. * 'label',
  2147. * 'circle',
  2148. * 'rectangle'
  2149. * ]
  2150. *
  2151. */
  2152. items: [
  2153. 'label',
  2154. 'circle',
  2155. 'rectangle'
  2156. ],
  2157. circle: {
  2158. /**
  2159. * A predefined background symbol for the button.
  2160. *
  2161. * @type {string}
  2162. *
  2163. */
  2164. symbol: 'circle.svg'
  2165. },
  2166. rectangle: {
  2167. /**
  2168. * A predefined background symbol for the button.
  2169. *
  2170. * @type {string}
  2171. *
  2172. */
  2173. symbol: 'rectangle.svg'
  2174. },
  2175. label: {
  2176. /**
  2177. * A predefined background symbol for the button.
  2178. *
  2179. * @type {string}
  2180. *
  2181. */
  2182. symbol: 'label.svg'
  2183. }
  2184. },
  2185. flags: {
  2186. /**
  2187. * A collection of strings pointing to config options for
  2188. * the items.
  2189. *
  2190. * @type {array}
  2191. * @default [
  2192. * 'flagCirclepin',
  2193. * 'flagDiamondpin',
  2194. * 'flagSquarepin',
  2195. * 'flagSimplepin'
  2196. * ]
  2197. *
  2198. */
  2199. items: [
  2200. 'flagCirclepin',
  2201. 'flagDiamondpin',
  2202. 'flagSquarepin',
  2203. 'flagSimplepin'
  2204. ],
  2205. flagSimplepin: {
  2206. /**
  2207. * A predefined background symbol for the button.
  2208. *
  2209. * @type {string}
  2210. *
  2211. */
  2212. symbol: 'flag-basic.svg'
  2213. },
  2214. flagDiamondpin: {
  2215. /**
  2216. * A predefined background symbol for the button.
  2217. *
  2218. * @type {string}
  2219. *
  2220. */
  2221. symbol: 'flag-diamond.svg'
  2222. },
  2223. flagSquarepin: {
  2224. /**
  2225. * A predefined background symbol for the button.
  2226. *
  2227. * @type {string}
  2228. */
  2229. symbol: 'flag-trapeze.svg'
  2230. },
  2231. flagCirclepin: {
  2232. /**
  2233. * A predefined background symbol for the button.
  2234. *
  2235. * @type {string}
  2236. */
  2237. symbol: 'flag-elipse.svg'
  2238. }
  2239. },
  2240. lines: {
  2241. /**
  2242. * A collection of strings pointing to config options for
  2243. * the items.
  2244. *
  2245. * @type {array}
  2246. * @default [
  2247. * 'segment',
  2248. * 'arrowSegment',
  2249. * 'ray',
  2250. * 'arrowRay',
  2251. * 'line',
  2252. * 'arrowLine',
  2253. * 'horizontalLine',
  2254. * 'verticalLine'
  2255. * ]
  2256. */
  2257. items: [
  2258. 'segment',
  2259. 'arrowSegment',
  2260. 'ray',
  2261. 'arrowRay',
  2262. 'line',
  2263. 'arrowLine',
  2264. 'horizontalLine',
  2265. 'verticalLine'
  2266. ],
  2267. segment: {
  2268. /**
  2269. * A predefined background symbol for the button.
  2270. *
  2271. * @type {string}
  2272. */
  2273. symbol: 'segment.svg'
  2274. },
  2275. arrowSegment: {
  2276. /**
  2277. * A predefined background symbol for the button.
  2278. *
  2279. * @type {string}
  2280. */
  2281. symbol: 'arrow-segment.svg'
  2282. },
  2283. ray: {
  2284. /**
  2285. * A predefined background symbol for the button.
  2286. *
  2287. * @type {string}
  2288. */
  2289. symbol: 'ray.svg'
  2290. },
  2291. arrowRay: {
  2292. /**
  2293. * A predefined background symbol for the button.
  2294. *
  2295. * @type {string}
  2296. */
  2297. symbol: 'arrow-ray.svg'
  2298. },
  2299. line: {
  2300. /**
  2301. * A predefined background symbol for the button.
  2302. *
  2303. * @type {string}
  2304. */
  2305. symbol: 'line.svg'
  2306. },
  2307. arrowLine: {
  2308. /**
  2309. * A predefined background symbol for the button.
  2310. *
  2311. * @type {string}
  2312. */
  2313. symbol: 'arrow-line.svg'
  2314. },
  2315. verticalLine: {
  2316. /**
  2317. * A predefined background symbol for the button.
  2318. *
  2319. * @type {string}
  2320. */
  2321. symbol: 'vertical-line.svg'
  2322. },
  2323. horizontalLine: {
  2324. /**
  2325. * A predefined background symbol for the button.
  2326. *
  2327. * @type {string}
  2328. */
  2329. symbol: 'horizontal-line.svg'
  2330. }
  2331. },
  2332. crookedLines: {
  2333. /**
  2334. * A collection of strings pointing to config options for
  2335. * the items.
  2336. *
  2337. * @type {array}
  2338. * @default [
  2339. * 'elliott3',
  2340. * 'elliott5',
  2341. * 'crooked3',
  2342. * 'crooked5'
  2343. * ]
  2344. *
  2345. */
  2346. items: [
  2347. 'elliott3',
  2348. 'elliott5',
  2349. 'crooked3',
  2350. 'crooked5'
  2351. ],
  2352. crooked3: {
  2353. /**
  2354. * A predefined background symbol for the button.
  2355. *
  2356. * @type {string}
  2357. */
  2358. symbol: 'crooked-3.svg'
  2359. },
  2360. crooked5: {
  2361. /**
  2362. * A predefined background symbol for the button.
  2363. *
  2364. * @type {string}
  2365. */
  2366. symbol: 'crooked-5.svg'
  2367. },
  2368. elliott3: {
  2369. /**
  2370. * A predefined background symbol for the button.
  2371. *
  2372. * @type {string}
  2373. */
  2374. symbol: 'elliott-3.svg'
  2375. },
  2376. elliott5: {
  2377. /**
  2378. * A predefined background symbol for the button.
  2379. *
  2380. * @type {string}
  2381. */
  2382. symbol: 'elliott-5.svg'
  2383. }
  2384. },
  2385. verticalLabels: {
  2386. /**
  2387. * A collection of strings pointing to config options for
  2388. * the items.
  2389. *
  2390. * @type {array}
  2391. * @default [
  2392. * 'verticalCounter',
  2393. * 'verticalLabel',
  2394. * 'verticalArrow'
  2395. * ]
  2396. */
  2397. items: [
  2398. 'verticalCounter',
  2399. 'verticalLabel',
  2400. 'verticalArrow'
  2401. ],
  2402. verticalCounter: {
  2403. /**
  2404. * A predefined background symbol for the button.
  2405. *
  2406. * @type {string}
  2407. */
  2408. symbol: 'vertical-counter.svg'
  2409. },
  2410. verticalLabel: {
  2411. /**
  2412. * A predefined background symbol for the button.
  2413. *
  2414. * @type {string}
  2415. */
  2416. symbol: 'vertical-label.svg'
  2417. },
  2418. verticalArrow: {
  2419. /**
  2420. * A predefined background symbol for the button.
  2421. *
  2422. * @type {string}
  2423. */
  2424. symbol: 'vertical-arrow.svg'
  2425. }
  2426. },
  2427. advanced: {
  2428. /**
  2429. * A collection of strings pointing to config options for
  2430. * the items.
  2431. *
  2432. * @type {array}
  2433. * @default [
  2434. * 'fibonacci',
  2435. * 'pitchfork',
  2436. * 'parallelChannel'
  2437. * ]
  2438. */
  2439. items: [
  2440. 'fibonacci',
  2441. 'pitchfork',
  2442. 'parallelChannel'
  2443. ],
  2444. pitchfork: {
  2445. /**
  2446. * A predefined background symbol for the button.
  2447. *
  2448. * @type {string}
  2449. */
  2450. symbol: 'pitchfork.svg'
  2451. },
  2452. fibonacci: {
  2453. /**
  2454. * A predefined background symbol for the button.
  2455. *
  2456. * @type {string}
  2457. */
  2458. symbol: 'fibonacci.svg'
  2459. },
  2460. parallelChannel: {
  2461. /**
  2462. * A predefined background symbol for the button.
  2463. *
  2464. * @type {string}
  2465. */
  2466. symbol: 'parallel-channel.svg'
  2467. }
  2468. },
  2469. measure: {
  2470. /**
  2471. * A collection of strings pointing to config options for
  2472. * the items.
  2473. *
  2474. * @type {array}
  2475. * @default [
  2476. * 'measureXY',
  2477. * 'measureX',
  2478. * 'measureY'
  2479. * ]
  2480. */
  2481. items: [
  2482. 'measureXY',
  2483. 'measureX',
  2484. 'measureY'
  2485. ],
  2486. measureX: {
  2487. /**
  2488. * A predefined background symbol for the button.
  2489. *
  2490. * @type {string}
  2491. */
  2492. symbol: 'measure-x.svg'
  2493. },
  2494. measureY: {
  2495. /**
  2496. * A predefined background symbol for the button.
  2497. *
  2498. * @type {string}
  2499. */
  2500. symbol: 'measure-y.svg'
  2501. },
  2502. measureXY: {
  2503. /**
  2504. * A predefined background symbol for the button.
  2505. *
  2506. * @type {string}
  2507. */
  2508. symbol: 'measure-xy.svg'
  2509. }
  2510. },
  2511. toggleAnnotations: {
  2512. /**
  2513. * A predefined background symbol for the button.
  2514. *
  2515. * @type {string}
  2516. */
  2517. symbol: 'annotations-visible.svg'
  2518. },
  2519. currentPriceIndicator: {
  2520. /**
  2521. * A predefined background symbol for the button.
  2522. *
  2523. * @type {string}
  2524. */
  2525. symbol: 'current-price-show.svg'
  2526. },
  2527. indicators: {
  2528. /**
  2529. * A predefined background symbol for the button.
  2530. *
  2531. * @type {string}
  2532. */
  2533. symbol: 'indicators.svg'
  2534. },
  2535. zoomChange: {
  2536. /**
  2537. * A collection of strings pointing to config options for
  2538. * the items.
  2539. *
  2540. * @type {array}
  2541. * @default [
  2542. * 'zoomX',
  2543. * 'zoomY',
  2544. * 'zoomXY'
  2545. * ]
  2546. */
  2547. items: [
  2548. 'zoomX',
  2549. 'zoomY',
  2550. 'zoomXY'
  2551. ],
  2552. zoomX: {
  2553. /**
  2554. * A predefined background symbol for the button.
  2555. *
  2556. * @type {string}
  2557. */
  2558. symbol: 'zoom-x.svg'
  2559. },
  2560. zoomY: {
  2561. /**
  2562. * A predefined background symbol for the button.
  2563. *
  2564. * @type {string}
  2565. */
  2566. symbol: 'zoom-y.svg'
  2567. },
  2568. zoomXY: {
  2569. /**
  2570. * A predefined background symbol for the button.
  2571. *
  2572. * @type {string}
  2573. */
  2574. symbol: 'zoom-xy.svg'
  2575. }
  2576. },
  2577. typeChange: {
  2578. /**
  2579. * A collection of strings pointing to config options for
  2580. * the items.
  2581. *
  2582. * @type {array}
  2583. * @default [
  2584. * 'typeOHLC',
  2585. * 'typeLine',
  2586. * 'typeCandlestick'
  2587. * ]
  2588. */
  2589. items: [
  2590. 'typeOHLC',
  2591. 'typeLine',
  2592. 'typeCandlestick'
  2593. ],
  2594. typeOHLC: {
  2595. /**
  2596. * A predefined background symbol for the button.
  2597. *
  2598. * @type {string}
  2599. */
  2600. symbol: 'series-ohlc.svg'
  2601. },
  2602. typeLine: {
  2603. /**
  2604. * A predefined background symbol for the button.
  2605. *
  2606. * @type {string}
  2607. */
  2608. symbol: 'series-line.svg'
  2609. },
  2610. typeCandlestick: {
  2611. /**
  2612. * A predefined background symbol for the button.
  2613. *
  2614. * @type {string}
  2615. */
  2616. symbol: 'series-candlestick.svg'
  2617. }
  2618. },
  2619. fullScreen: {
  2620. /**
  2621. * A predefined background symbol for the button.
  2622. *
  2623. * @type {string}
  2624. */
  2625. symbol: 'fullscreen.svg'
  2626. },
  2627. saveChart: {
  2628. /**
  2629. * A predefined background symbol for the button.
  2630. *
  2631. * @type {string}
  2632. */
  2633. symbol: 'save-chart.svg'
  2634. }
  2635. }
  2636. }
  2637. }
  2638. });
  2639. // Run HTML generator
  2640. addEvent(H.Chart, 'afterGetContainer', function () {
  2641. this.setStockTools();
  2642. });
  2643. addEvent(H.Chart, 'getMargins', function () {
  2644. var offsetWidth = (
  2645. this.stockTools &&
  2646. this.stockTools.listWrapper &&
  2647. this.stockTools.listWrapper.offsetWidth
  2648. );
  2649. if (offsetWidth && offsetWidth < this.plotWidth) {
  2650. this.plotLeft += offsetWidth;
  2651. }
  2652. });
  2653. addEvent(H.Chart, 'destroy', function () {
  2654. if (this.stockTools) {
  2655. this.stockTools.destroy();
  2656. }
  2657. });
  2658. addEvent(H.Chart, 'redraw', function () {
  2659. if (this.stockTools && this.stockTools.guiEnabled) {
  2660. this.stockTools.redraw();
  2661. }
  2662. });
  2663. /*
  2664. * Toolbar Class
  2665. *
  2666. * @param {Object} - options of toolbar
  2667. * @param {Chart} - Reference to chart
  2668. *
  2669. */
  2670. H.Toolbar = function (options, langOptions, chart) {
  2671. this.chart = chart;
  2672. this.options = options;
  2673. this.lang = langOptions;
  2674. this.guiEnabled = options.enabled;
  2675. this.visible = pick(options.visible, true);
  2676. this.placed = pick(options.placed, false);
  2677. // General events collection which should be removed upon destroy/update:
  2678. this.eventsToUnbind = [];
  2679. if (this.guiEnabled) {
  2680. this.createHTML();
  2681. this.init();
  2682. this.showHideNavigatorion();
  2683. }
  2684. fireEvent(this, 'afterInit');
  2685. };
  2686. H.extend(H.Chart.prototype, {
  2687. /*
  2688. * Verify if Toolbar should be added.
  2689. *
  2690. * @param {Object} - chart options
  2691. *
  2692. */
  2693. setStockTools: function (options) {
  2694. var chartOptions = this.options,
  2695. lang = chartOptions.lang,
  2696. guiOptions = merge(
  2697. chartOptions.stockTools && chartOptions.stockTools.gui,
  2698. options && options.gui
  2699. ),
  2700. langOptions = lang.stockTools && lang.stockTools.gui;
  2701. this.stockTools = new H.Toolbar(guiOptions, langOptions, this);
  2702. if (this.stockTools.guiEnabled) {
  2703. this.isDirtyBox = true;
  2704. }
  2705. }
  2706. });
  2707. H.Toolbar.prototype = {
  2708. /*
  2709. * Initialize the toolbar. Create buttons and submenu for each option
  2710. * defined in `stockTools.gui`.
  2711. *
  2712. */
  2713. init: function () {
  2714. var _self = this,
  2715. lang = this.lang,
  2716. guiOptions = this.options,
  2717. toolbar = this.toolbar,
  2718. addSubmenu = _self.addSubmenu,
  2719. buttons = guiOptions.buttons,
  2720. defs = guiOptions.definitions,
  2721. allButtons = toolbar.childNodes,
  2722. inIframe = this.inIframe(),
  2723. button;
  2724. // create buttons
  2725. buttons.forEach(function (btnName) {
  2726. button = _self.addButton(toolbar, defs, btnName, lang);
  2727. if (inIframe && btnName === 'fullScreen') {
  2728. button.buttonWrapper.className += ' ' + PREFIX + 'disabled-btn';
  2729. }
  2730. ['click', 'touchstart'].forEach(function (eventName) {
  2731. addEvent(button.buttonWrapper, eventName, function () {
  2732. _self.eraseActiveButtons(
  2733. allButtons,
  2734. button.buttonWrapper
  2735. );
  2736. });
  2737. });
  2738. if (isArray(defs[btnName].items)) {
  2739. // create submenu buttons
  2740. addSubmenu.call(_self, button, defs[btnName]);
  2741. }
  2742. });
  2743. },
  2744. /*
  2745. * Create submenu (list of buttons) for the option. In example main button
  2746. * is Line, in submenu will be buttons with types of lines.
  2747. *
  2748. * @param {Object} - button which has submenu
  2749. * @param {Array} - list of all buttons
  2750. *
  2751. */
  2752. addSubmenu: function (parentBtn, button) {
  2753. var _self = this,
  2754. submenuArrow = parentBtn.submenuArrow,
  2755. buttonWrapper = parentBtn.buttonWrapper,
  2756. buttonWidth = getStyle(buttonWrapper, 'width'),
  2757. wrapper = this.wrapper,
  2758. menuWrapper = this.listWrapper,
  2759. allButtons = this.toolbar.childNodes,
  2760. topMargin = 0,
  2761. submenuWrapper;
  2762. // create submenu container
  2763. this.submenu = submenuWrapper = createElement(UL, {
  2764. className: PREFIX + 'submenu-wrapper'
  2765. }, null, buttonWrapper);
  2766. // create submenu buttons and select the first one
  2767. this.addSubmenuItems(buttonWrapper, button);
  2768. // show / hide submenu
  2769. ['click', 'touchstart'].forEach(function (eventName) {
  2770. addEvent(submenuArrow, eventName, function (e) {
  2771. e.stopPropagation();
  2772. // Erase active class on all other buttons
  2773. _self.eraseActiveButtons(allButtons, buttonWrapper);
  2774. // hide menu
  2775. if (buttonWrapper.className.indexOf(PREFIX + 'current') >= 0) {
  2776. menuWrapper.style.width = menuWrapper.startWidth + 'px';
  2777. buttonWrapper.classList.remove(PREFIX + 'current');
  2778. submenuWrapper.style.display = 'none';
  2779. } else {
  2780. // show menu
  2781. // to calculate height of element
  2782. submenuWrapper.style.display = 'block';
  2783. topMargin = submenuWrapper.offsetHeight -
  2784. buttonWrapper.offsetHeight - 3;
  2785. // calculate position of submenu in the box
  2786. // if submenu is inside, reset top margin
  2787. if (
  2788. // cut on the bottom
  2789. !(submenuWrapper.offsetHeight +
  2790. buttonWrapper.offsetTop >
  2791. wrapper.offsetHeight &&
  2792. // cut on the top
  2793. buttonWrapper.offsetTop > topMargin)
  2794. ) {
  2795. topMargin = 0;
  2796. }
  2797. // apply calculated styles
  2798. css(submenuWrapper, {
  2799. top: -topMargin + 'px',
  2800. left: buttonWidth + 3 + 'px'
  2801. });
  2802. buttonWrapper.className += ' ' + PREFIX + 'current';
  2803. menuWrapper.startWidth = wrapper.offsetWidth;
  2804. menuWrapper.style.width = menuWrapper.startWidth +
  2805. H.getStyle(menuWrapper, 'padding-left') +
  2806. submenuWrapper.offsetWidth + 3 + 'px';
  2807. }
  2808. });
  2809. });
  2810. },
  2811. /*
  2812. * Create buttons in submenu
  2813. *
  2814. * @param {HTMLDOMElement} - button where submenu is placed
  2815. * @param {Array} - list of all buttons options
  2816. *
  2817. */
  2818. addSubmenuItems: function (buttonWrapper, button) {
  2819. var _self = this,
  2820. submenuWrapper = this.submenu,
  2821. lang = this.lang,
  2822. menuWrapper = this.listWrapper,
  2823. items = button.items,
  2824. firstSubmenuItem,
  2825. submenuBtn;
  2826. // add items to submenu
  2827. items.forEach(function (btnName) {
  2828. // add buttons to submenu
  2829. submenuBtn = _self.addButton(
  2830. submenuWrapper,
  2831. button,
  2832. btnName,
  2833. lang
  2834. );
  2835. ['click', 'touchstart'].forEach(function (eventName) {
  2836. addEvent(submenuBtn.mainButton, eventName, function () {
  2837. _self.switchSymbol(this, buttonWrapper, true);
  2838. menuWrapper.style.width = menuWrapper.startWidth + 'px';
  2839. submenuWrapper.style.display = 'none';
  2840. });
  2841. });
  2842. });
  2843. // select first submenu item
  2844. firstSubmenuItem = submenuWrapper
  2845. .querySelectorAll('li > .' + PREFIX + 'menu-item-btn')[0];
  2846. // replace current symbol, in main button, with submenu's button style
  2847. _self.switchSymbol(firstSubmenuItem, false);
  2848. },
  2849. /*
  2850. * Erase active class on all other buttons.
  2851. *
  2852. * @param {Array} - Array of HTML buttons
  2853. * @param {HTMLDOMElement} - Current HTML button
  2854. *
  2855. */
  2856. eraseActiveButtons: function (buttons, currentButton, submenuItems) {
  2857. [].forEach.call(buttons, function (btn) {
  2858. if (btn !== currentButton) {
  2859. btn.classList.remove(PREFIX + 'current');
  2860. btn.classList.remove(PREFIX + 'active');
  2861. submenuItems =
  2862. btn.querySelectorAll('.' + PREFIX + 'submenu-wrapper');
  2863. // hide submenu
  2864. if (submenuItems.length > 0) {
  2865. submenuItems[0].style.display = 'none';
  2866. }
  2867. }
  2868. });
  2869. },
  2870. /*
  2871. * Create single button. Consist of `<li>` , `<span>` and (if exists)
  2872. * submenu container.
  2873. *
  2874. * @param {HTMLDOMElement} - HTML reference, where button should be added
  2875. * @param {Object} - all options, by btnName refer to particular button
  2876. * @param {String} - name of functionality mapped for specific class
  2877. * @param {Object} - All titles, by btnName refer to particular button
  2878. *
  2879. * @return {Object} - references to all created HTML elements
  2880. */
  2881. addButton: function (target, options, btnName, lang) {
  2882. var guiOptions = this.options,
  2883. btnOptions = options[btnName],
  2884. items = btnOptions.items,
  2885. classMapping = H.Toolbar.prototype.classMapping,
  2886. userClassName = btnOptions.className || '',
  2887. mainButton,
  2888. submenuArrow,
  2889. buttonWrapper;
  2890. // main button wrapper
  2891. buttonWrapper = createElement(LI, {
  2892. className: pick(classMapping[btnName], '') + ' ' + userClassName,
  2893. title: lang[btnName] || btnName
  2894. }, null, target);
  2895. // single button
  2896. mainButton = createElement(SPAN, {
  2897. className: PREFIX + 'menu-item-btn'
  2898. }, null, buttonWrapper);
  2899. // submenu
  2900. if (items && items.length > 1) {
  2901. // arrow is a hook to show / hide submenu
  2902. submenuArrow = createElement(SPAN, {
  2903. className: PREFIX + 'submenu-item-arrow ' +
  2904. PREFIX + 'arrow-right'
  2905. }, null, buttonWrapper);
  2906. } else {
  2907. mainButton.style['background-image'] = 'url(' +
  2908. guiOptions.iconsURL + btnOptions.symbol + ')';
  2909. }
  2910. return {
  2911. buttonWrapper: buttonWrapper,
  2912. mainButton: mainButton,
  2913. submenuArrow: submenuArrow
  2914. };
  2915. },
  2916. /*
  2917. * Create navigation's HTML elements: container and arrows.
  2918. *
  2919. */
  2920. addNavigation: function () {
  2921. var stockToolbar = this,
  2922. wrapper = stockToolbar.wrapper;
  2923. // arrow wrapper
  2924. stockToolbar.arrowWrapper = createElement(DIV, {
  2925. className: PREFIX + 'arrow-wrapper'
  2926. });
  2927. stockToolbar.arrowUp = createElement(DIV, {
  2928. className: PREFIX + 'arrow-up'
  2929. }, null, stockToolbar.arrowWrapper);
  2930. stockToolbar.arrowDown = createElement(DIV, {
  2931. className: PREFIX + 'arrow-down'
  2932. }, null, stockToolbar.arrowWrapper);
  2933. wrapper.insertBefore(
  2934. stockToolbar.arrowWrapper,
  2935. wrapper.childNodes[0]
  2936. );
  2937. // attach scroll events
  2938. stockToolbar.scrollButtons();
  2939. },
  2940. /*
  2941. * Add events to navigation (two arrows) which allows user to scroll
  2942. * top/down GUI buttons, if container's height is not enough.
  2943. *
  2944. */
  2945. scrollButtons: function () {
  2946. var targetY = 0,
  2947. _self = this,
  2948. wrapper = _self.wrapper,
  2949. toolbar = _self.toolbar,
  2950. step = 0.1 * wrapper.offsetHeight; // 0.1 = 10%
  2951. ['click', 'touchstart'].forEach(function (eventName) {
  2952. addEvent(_self.arrowUp, eventName, function () {
  2953. if (targetY > 0) {
  2954. targetY -= step;
  2955. toolbar.style['margin-top'] = -targetY + 'px';
  2956. }
  2957. });
  2958. addEvent(_self.arrowDown, eventName, function () {
  2959. if (
  2960. wrapper.offsetHeight + targetY <=
  2961. toolbar.offsetHeight + step
  2962. ) {
  2963. targetY += step;
  2964. toolbar.style['margin-top'] = -targetY + 'px';
  2965. }
  2966. });
  2967. });
  2968. },
  2969. /*
  2970. * Create stockTools HTML main elements.
  2971. *
  2972. */
  2973. createHTML: function () {
  2974. var stockToolbar = this,
  2975. chart = stockToolbar.chart,
  2976. guiOptions = stockToolbar.options,
  2977. container = chart.container,
  2978. listWrapper,
  2979. toolbar,
  2980. wrapper;
  2981. // create main container
  2982. stockToolbar.wrapper = wrapper = createElement(DIV, {
  2983. className: PREFIX + 'stocktools-wrapper ' +
  2984. guiOptions.className
  2985. });
  2986. container.parentNode.insertBefore(wrapper, container);
  2987. // toolbar
  2988. stockToolbar.toolbar = toolbar = createElement(UL, {
  2989. className: PREFIX + 'stocktools-toolbar ' +
  2990. guiOptions.toolbarClassName
  2991. });
  2992. // add container for list of buttons
  2993. stockToolbar.listWrapper = listWrapper = createElement(DIV, {
  2994. className: PREFIX + 'menu-wrapper'
  2995. });
  2996. wrapper.insertBefore(listWrapper, wrapper.childNodes[0]);
  2997. listWrapper.insertBefore(toolbar, listWrapper.childNodes[0]);
  2998. stockToolbar.showHideToolbar();
  2999. // add navigation which allows user to scroll down / top GUI buttons
  3000. stockToolbar.addNavigation();
  3001. },
  3002. /*
  3003. * Function called in redraw verifies if the navigation should be visible.
  3004. *
  3005. */
  3006. showHideNavigatorion: function () {
  3007. // arrows
  3008. // 50px space for arrows
  3009. if (
  3010. this.visible &&
  3011. this.toolbar.offsetHeight > (this.wrapper.offsetHeight - 50)
  3012. ) {
  3013. this.arrowWrapper.style.display = 'block';
  3014. } else {
  3015. // reset margin if whole toolbar is visible
  3016. this.toolbar.style.marginTop = '0px';
  3017. // hide arrows
  3018. this.arrowWrapper.style.display = 'none';
  3019. }
  3020. },
  3021. /*
  3022. * Create button which shows or hides GUI toolbar.
  3023. *
  3024. */
  3025. showHideToolbar: function () {
  3026. var stockToolbar = this,
  3027. chart = this.chart,
  3028. wrapper = stockToolbar.wrapper,
  3029. toolbar = this.listWrapper,
  3030. submenu = this.submenu,
  3031. visible = this.visible,
  3032. showhideBtn;
  3033. // Show hide toolbar
  3034. this.showhideBtn = showhideBtn = createElement(DIV, {
  3035. className: PREFIX + 'toggle-toolbar ' + PREFIX + 'arrow-left'
  3036. }, null, wrapper);
  3037. if (!visible) {
  3038. // hide
  3039. if (submenu) {
  3040. submenu.style.display = 'none';
  3041. }
  3042. showhideBtn.style.left = '0px';
  3043. stockToolbar.visible = visible = false;
  3044. toolbar.classList.add(PREFIX + 'hide');
  3045. showhideBtn.classList.toggle(PREFIX + 'arrow-right');
  3046. } else {
  3047. showhideBtn.style.top = H.getStyle(toolbar, 'padding-top') + 'px';
  3048. showhideBtn.style.left = (
  3049. wrapper.offsetWidth +
  3050. H.getStyle(toolbar, 'padding-left')
  3051. ) + 'px';
  3052. }
  3053. // toggle menu
  3054. ['click', 'touchstart'].forEach(function (eventName) {
  3055. addEvent(showhideBtn, eventName, function () {
  3056. chart.update({
  3057. stockTools: {
  3058. gui: {
  3059. visible: !visible,
  3060. placed: true
  3061. }
  3062. }
  3063. });
  3064. });
  3065. });
  3066. },
  3067. /*
  3068. * In main GUI button, replace icon and class with submenu button's
  3069. * class / symbol.
  3070. *
  3071. * @param {HTMLDOMElement} - submenu button
  3072. * @param {Boolean} - true or false
  3073. *
  3074. */
  3075. switchSymbol: function (button, redraw) {
  3076. var buttonWrapper = button.parentNode,
  3077. buttonWrapperClass = buttonWrapper.classList.value,
  3078. // main button in first level og GUI
  3079. mainNavButton = buttonWrapper.parentNode.parentNode;
  3080. // set class
  3081. mainNavButton.className = '';
  3082. if (buttonWrapperClass) {
  3083. mainNavButton.classList.add(buttonWrapperClass.trim());
  3084. }
  3085. // set icon
  3086. mainNavButton.querySelectorAll('.' + PREFIX + 'menu-item-btn')[0]
  3087. .style['background-image'] = button.style['background-image'];
  3088. // set active class
  3089. if (redraw) {
  3090. this.selectButton(mainNavButton);
  3091. }
  3092. },
  3093. /*
  3094. * Set select state (active class) on button.
  3095. *
  3096. * @param {HTMLDOMElement} - button
  3097. *
  3098. */
  3099. selectButton: function (btn) {
  3100. if (btn.className.indexOf(activeClass) >= 0) {
  3101. btn.classList.remove(activeClass);
  3102. } else {
  3103. btn.classList.add(activeClass);
  3104. }
  3105. },
  3106. /*
  3107. * Remove active class from all buttons except defined.
  3108. *
  3109. * @param {HTMLDOMElement} - button which should not be deactivated
  3110. *
  3111. */
  3112. unselectAllButtons: function (btn) {
  3113. var activeButtons = btn.parentNode.querySelectorAll('.' + activeClass);
  3114. [].forEach.call(activeButtons, function (activeBtn) {
  3115. if (activeBtn !== btn) {
  3116. activeBtn.classList.remove(activeClass);
  3117. }
  3118. });
  3119. },
  3120. /*
  3121. * Verify if chart is in iframe.
  3122. *
  3123. * @return {Object} - elements translations.
  3124. */
  3125. inIframe: function () {
  3126. try {
  3127. return win.self !== win.top;
  3128. } catch (e) {
  3129. return true;
  3130. }
  3131. },
  3132. /*
  3133. * Update GUI with given options.
  3134. *
  3135. * @param {Object} - general options for Stock Tools
  3136. */
  3137. update: function (options) {
  3138. merge(true, this.chart.options.stockTools, options);
  3139. this.destroy();
  3140. this.chart.setStockTools(options);
  3141. // If Stock Tools are updated, then bindings should be updated too:
  3142. if (this.chart.navigationBindings) {
  3143. this.chart.navigationBindings.update();
  3144. }
  3145. },
  3146. /*
  3147. * Destroy all HTML GUI elements.
  3148. *
  3149. */
  3150. destroy: function () {
  3151. var stockToolsDiv = this.wrapper,
  3152. parent = stockToolsDiv && stockToolsDiv.parentNode;
  3153. this.eventsToUnbind.forEach(function (unbinder) {
  3154. unbinder();
  3155. });
  3156. // Remove the empty element
  3157. if (parent) {
  3158. parent.removeChild(stockToolsDiv);
  3159. }
  3160. // redraw
  3161. this.chart.isDirtyBox = true;
  3162. this.chart.redraw();
  3163. },
  3164. /*
  3165. * Redraw, GUI requires to verify if the navigation should be visible.
  3166. *
  3167. */
  3168. redraw: function () {
  3169. this.showHideNavigatorion();
  3170. },
  3171. /*
  3172. * Mapping JSON fields to CSS classes.
  3173. *
  3174. */
  3175. classMapping: {
  3176. circle: PREFIX + 'circle-annotation',
  3177. rectangle: PREFIX + 'rectangle-annotation',
  3178. label: PREFIX + 'label-annotation',
  3179. segment: PREFIX + 'segment',
  3180. arrowSegment: PREFIX + 'arrow-segment',
  3181. ray: PREFIX + 'ray',
  3182. arrowRay: PREFIX + 'arrow-ray',
  3183. line: PREFIX + 'infinity-line',
  3184. arrowLine: PREFIX + 'arrow-infinity-line',
  3185. verticalLine: PREFIX + 'vertical-line',
  3186. horizontalLine: PREFIX + 'horizontal-line',
  3187. crooked3: PREFIX + 'crooked3',
  3188. crooked5: PREFIX + 'crooked5',
  3189. elliott3: PREFIX + 'elliott3',
  3190. elliott5: PREFIX + 'elliott5',
  3191. pitchfork: PREFIX + 'pitchfork',
  3192. fibonacci: PREFIX + 'fibonacci',
  3193. parallelChannel: PREFIX + 'parallel-channel',
  3194. measureX: PREFIX + 'measure-x',
  3195. measureY: PREFIX + 'measure-y',
  3196. measureXY: PREFIX + 'measure-xy',
  3197. verticalCounter: PREFIX + 'vertical-counter',
  3198. verticalLabel: PREFIX + 'vertical-label',
  3199. verticalArrow: PREFIX + 'vertical-arrow',
  3200. currentPriceIndicator: PREFIX + 'current-price-indicator',
  3201. indicators: PREFIX + 'indicators',
  3202. flagCirclepin: PREFIX + 'flag-circlepin',
  3203. flagDiamondpin: PREFIX + 'flag-diamondpin',
  3204. flagSquarepin: PREFIX + 'flag-squarepin',
  3205. flagSimplepin: PREFIX + 'flag-simplepin',
  3206. zoomX: PREFIX + 'zoom-x',
  3207. zoomY: PREFIX + 'zoom-y',
  3208. zoomXY: PREFIX + 'zoom-xy',
  3209. typeLine: PREFIX + 'series-type-line',
  3210. typeOHLC: PREFIX + 'series-type-ohlc',
  3211. typeCandlestick: PREFIX + 'series-type-candlestick',
  3212. fullScreen: PREFIX + 'full-screen',
  3213. toggleAnnotations: PREFIX + 'toggle-annotations',
  3214. saveChart: PREFIX + 'save-chart',
  3215. separator: PREFIX + 'separator'
  3216. }
  3217. };
  3218. // Comunication with bindings:
  3219. addEvent(H.NavigationBindings, 'selectButton', function (event) {
  3220. var button = event.button,
  3221. className = PREFIX + 'submenu-wrapper',
  3222. gui = this.chart.stockTools;
  3223. if (gui && gui.guiEnabled) {
  3224. // Unslect other active buttons
  3225. gui.unselectAllButtons(event.button);
  3226. // If clicked on a submenu, select state for it's parent
  3227. if (button.parentNode.className.indexOf(className) >= 0) {
  3228. button = button.parentNode.parentNode;
  3229. }
  3230. // Set active class on the current button
  3231. gui.selectButton(button);
  3232. }
  3233. });
  3234. addEvent(H.NavigationBindings, 'deselectButton', function (event) {
  3235. var button = event.button,
  3236. className = PREFIX + 'submenu-wrapper',
  3237. gui = this.chart.stockTools;
  3238. if (gui && gui.guiEnabled) {
  3239. // If deselecting a button from a submenu, select state for it's parent
  3240. if (button.parentNode.className.indexOf(className) >= 0) {
  3241. button = button.parentNode.parentNode;
  3242. }
  3243. gui.selectButton(button);
  3244. }
  3245. });
  3246. }(Highcharts));
  3247. return (function () {
  3248. }());
  3249. }));