PageSourceSearch

https://www.boerse-express.com/javascript/ttws.js?m=1790426994

js boerse-express.com collected 2026-10-02 06:08:07 UTC 24,235 bytes, 630 lines download raw bytes

1/*@cc_on
2    (function(f) {
3        window.setTimeout = f(window.setTimeout);   // overwrites the global function!
4        window.setInterval = f(window.setInterval); // overwrites the global function!
5    })(function(f) {
6        return function(c, t) {
7            var a = [].slice.call(arguments, 2);    // gathers the extra args
8            return f(function() {
9                c.apply(this, a);                   // passes them to your function
10            }, t);
11        };
12    });
13    @*/
14
15(function ($) {
16	$(function () {
17		// TOP FLOP
18		$('.dropdown .current-selected').on('click', function (event) {
19			event.stopPropagation();
20			$('.dropdown-options.open').removeClass('open');
21			$(this).next().addClass('open');
22		});
23
24		// on blur --> close all dropdowns
25		$(document).on('click', function () {
26			$('.dropdown-options.open').removeClass('open');
27		});
28
29		$('.dropdown .dropdown-options li').on('click', function () {
30			if (!$(this).hasClass('selected')) {
31				$(this).parent().find('.selected').removeClass('selected');
32				$(this).addClass('selected');
33
34				if ($(this).parents('.top-flop').length) { //top-flop specific
35					var element = $(this).parents('.top-flop').find('.top-flop-container');
36					var gridID = $(this).parents('.top-flop').data('contentId');
37					var marketID = $(this).data('id');
38					var title = $(this).find('h4').text();
39					var last = $(this).find('.last').text();
40					var changePerc = $(this).find('.changePerc').text();
41
42					$(this).parents('.dropdown').find('.current-selected h4').text(title);
43					$(this).parents('.dropdown').find('.current-selected .last').text(last);
44					$(this).parents('.dropdown').find('.current-selected .changePerc').text(changePerc);
45
46					getTopFlopContent(marketID, element, gridID);
47				}
48				if ($(this).parents('.exchange-select').length) {  //exchange select; currently used in share/currency/resource header section to change exchange/price
49					var title = $(this).find('a').text();
50					$(this).parents('.dropdown').find('.current-selected h4').text(title);
51				}
52				if ($(this).parents('.event-type-dropdown').length) { //something for events
53					var title = $(this).find('p').text();
54					$(this).parents('.dropdown').find('.current-selected p').text(title);
55				}
56				if ($(this).parents('.share-dropdown').length) { //set selected dropdown option for market-news share-search
57					var title = $(this).find('p').text();
58					$(this).parents('.dropdown').find('.current-selected p').text(title);
59				}
60				if ($(this).parents('.header-charts').length) {  //charts for all indices in header area
61					var title = $(this).find('h4').text();
62					var element = $(this).parents('.header-charts').find('.header-chart-image');
63					var marketID = $(this).data('id');
64					$(this).parents('.dropdown').find('.current-selected h4').text(title);
65
66					getHeaderChartImage(marketID, element);
67				}
68				if ($(this).parents('.stockexchange-market-chart').length) { //stockexchangepage - charts for all markets
69					var title = $(this).find('h4').text();
70					var element = $(this).parents('.stockexchange-market-chart').find('.market-chart-container');
71					var marketID = $(this).data('id');
72					$(this).parents('.dropdown').find('.current-selected h4').text(title);
73
74					var href = $(this).data('link');
75					$('.stockexchange-market-chart .more-btn a').attr('href', href);
76
77					getStockExchangeMarketCharts(marketID, element);
78				}
79				if ($(this).parents('.currency-from-dropdown').length || $(this).parents('.currency-to-dropdown').length) {
80					var title = $(this).find('p').text();
81					$(this).parents('.dropdown').find('.current-selected p').text(title);
82				}
83				if ($(this).parents('.market-shares').length) { //shares in market dropdown
84					var marketID = $(this).data('id');
85					var element = $(this).parents('.market-shares').find('.market-shares-container');
86					var gridID = $(this).parents('.market-shares').data('contentId');
87
88					var title = $(this).find('h4').text();
89					var last = $(this).find('.last').text();
90					var changePerc = $(this).find('.changePerc').text();
91
92					$(this).parents('.dropdown').find('.current-selected h4').text(title);
93					$(this).parents('.dropdown').find('.current-selected .last').text(last);
94					$(this).parents('.dropdown').find('.current-selected .changePerc').text(changePerc);
95
96					getMarketSharesContent(marketID, element, gridID);
97				}
98			}
99		});
100
101		/*Top-Flop*/
102		if ($('.top-flop-container').length) {
103			var getTopFlopContent = function (id, element, gridId, url) {
104				if (!url) {
105					if (element.hasClass('ls-market')) {
106						url = 'home/getLSMarketTopFlopContent';
107					} else {
108						url = 'home/getTopFlopContent';
109					}
110				}
111				$.ajax({
112					type: 'POST',
113					url: url,
114					data: {
115						ajax: 1,
116						id: id,
117						gridId: gridId
118					}
119				})
120					.done(function (response) {
121						element.html(response);
122					})
123					.fail(function (xhr) {
124						//alert("Error: " + xhr.responseText);
125					});
126			};
127		}
128		$('.top-flop').each(function () {
129			var element = $(this).find('.top-flop-container');
130			var marketID = $(this).data('defaultMarket');
131			var gridID = $(this).data('contentId');
132
133			getTopFlopContent(marketID, element, gridID);
134		});
135		/*Header charts*/
136		if ($('.header-charts').length) {
137			var getHeaderChartImage = function (id, element, url) {
138				if (!url) {
139					url = 'home/getHeaderChartImage';
140				}
141				$.ajax({
142					type: 'POST',
143					url: url,
144					data: {
145						ajax: 1,
146						id: id
147					}
148				})
149					.done(function (response) {
150						element.html(response);
151					})
152					.fail(function (xhr) {
153						//alert("Error: " + xhr.responseText);
154					});
155			};
156			var element = $(this).find('.header-chart-image');
157			getHeaderChartImage(15, element);  //fixed to atx(db) at the moment
158		}
159		/*Stockexchangepage - market charts*/
160		if ($('.stockexchange-market-chart').length) {
161			var getStockExchangeMarketCharts = function (id, element, url) {
162				if (!url) {
163					url = 'home/getStockExchangeMarketCharts';
164				}
165				$.ajax({
166					type: 'POST',
167					url: url,
168					data: {
169						ajax: 1,
170						id: id
171					}
172				})
173					.done(function (response) {
174						element.html(response);
175						setChartNavClickHandler();
176					})
177					.fail(function (xhr) {
178						//alert("Error: " + xhr.responseText);
179					});
180			};
181			$('.stockexchange-market-chart').each(function () {
182				var element = $(this).find('.market-chart-container');
183				var marketID = $(this).data('defaultMarket');
184
185				var href = $('.stockexchange-market-chart .selected').data('link');
186				$('.stockexchange-market-chart .more-btn a').attr('href', href);
187
188				getStockExchangeMarketCharts(marketID, element);
189			});
190		}
191		/*Shares of market*/
192		if ($('.market-shares-container').length) {
193			var getMarketSharesContent = function (id, element, gridID, url) {
194				if (!url) {
195					url = 'home/getAllMarketSharesContent';
196				}
197				$.ajax({
198					type: 'POST',
199					url: url,
200					data: {
201						ajax: 1,
202						id: id,
203						gridID: gridID
204					}
205				})
206					.done(function (response) {
207						element.html(response);
208					})
209					.fail(function (xhr) {
210						//alert("Error: " + xhr.responseText);
211					});
212			};
213		}
214
215		// TTWS Push Data
216		var ttPush;
217
218		function startPush(symbolIDs, symbolFIDs) {
219			var securityToken = "";
220
221			ttPush = new TT.Push({ token: securityToken, tokenCallback: getSecurityToken });
222
223			ttPush.SetOptions({
224				'pushurl': 'https://push.ttweb.net/http_push/',
225				'symbolfids': symbolFIDs,
226				'timezone': 'Europe/Vienna',
227				'symbolsnapshot': true,
228				'loglevel': 'none'
229			});
230
231			ttPush.startBatch();
232			ttPush.subscribe([{
233				symbolID: symbolIDs,
234				callback: handlePushMessage,
235				filterFields: {
236					PushType: "Snapshot"
237				}
238			}]);
239			ttPush.endBatch();
240
241			ttPush.addEventListener('status', function (status) {
242				//console.log('status.statusType: ' + status.statusType + ", status.message: " + JSON.stringify(status.message));
243				if (status.message.error) {
244					//console.log('status.message.error: ' + status.message.error);
245				}
246			});
247		}
248
249		function getSecurityToken(tokenSuccessCallback) {
250			$.ajax({
251				type: 'POST',
252				url: 'home/getSecurityPushToken'
253			})
254				.done(function (response) {
255					var securityToken = JSON.parse(response)['token'];
256					tokenSuccessCallback(securityToken);
257				})
258				.fail(function (xhr) {
259				});
260		}
261
262		// Zerlegt einen Feed-Zeitstempel in Datum und Uhrzeit — die EINE Stelle, die weiss, dass der
263		// Feed nicht konsistent ist: manche Symbole senden "30.07.2026 11:57:00", andere ISO
264		// "2026-07-29T00:00:00". Bis 2026-08-26 stand diese Zerlegung nur im 'full'-Zweig; der
265		// zweite Zweig schnitt mit slice(11) blind eine Uhrzeit heraus und warf alles andere weg —
266		// das KPI-Widget verlor dadurch bei jedem Tick sein Datum. Eine zweite Fassung derselben
267		// Regel ist ein zweiter Kandidat fuer den Fehler, also gibt es sie nur noch hier.
268		//
269		// Rueckgabe: {date:'26.08.2026', date2:'26.08.26', time:'11:30', noTime:false} oder null.
270		// noTime === true heisst: der Feed schickte ein Sitzungsdatum OHNE Uhrzeit (exakt 00:00).
271		// Das ist keine Mitternachts-Notierung, sondern eine fehlende Angabe.
272		function splitFeedStamp(raw) {
273			var v = String(raw == null ? '' : raw);
274			var d = null, t = null, y = null;
275			var iso = v.match(/^(\d{4})-(\d{2})-(\d{2})[T ](\d{2}:\d{2})/);
276			if (iso) {
277				d = iso[3] + '.' + iso[2] + '.' + iso[1];
278				y = iso[1];
279				t = iso[4];
280			} else {
281				var de = v.match(/^(\d{2})\.(\d{2})\.(\d{4})[T ]?\s*(\d{2}:\d{2})/);
282				if (de) {
283					d = de[1] + '.' + de[2] + '.' + de[3];
284					y = de[3];
285					t = de[4];
286				}
287			}
288			if (!d) { return null; }
289			return {
290				date:   d,
291				// zweistelliges Jahr fuer die Krypto-Kopfzeile (dd.MM.yy)
292				date2:  d.slice(0, 6) + y.slice(2),
293				time:   t,
294				noTime: t === '00:00'
295			};
296		}
297
298		// Sortierschluessel '<YYYY><MM><DD><HH><mm>' aus der Form, die splitFeedStamp() liefert
299		// ('dd.mm.yyyy' + 'HH:MM'). Zwoelf Ziffern, lexikographisch vergleichbar — bewusst KEIN
300		// Date-Objekt: der Feed traegt keine Zeitzone, und new Date('28.08.2026') ist je nach
301		// Browser Invalid Date oder eine amerikanisch gelesene Fehldeutung. Ein Stringvergleich
302		// kann beides nicht.
303		function stampSortKey(date, time) {
304			if (!date) { return null; }
305			var t = time || '00:00';
306			return date.slice(6, 10) + date.slice(3, 5) + date.slice(0, 2) + t.slice(0, 2) + t.slice(3, 5);
307		}
308
309		// Liest den Stempel, den der SERVER in das Element geschrieben hat, zurueck in denselben
310		// Schluessel. Die ausgelieferten Serverformen sind nicht eine, sondern drei:
311		//   "31.08.2026, 21:56"   Asset/Masthead.ss (assetStamp(), 'd.m.Y, H:i')
312		//   "31.08.2026 | 21:56"  die zehn Grid-/Header-Templates ('dd.MM.yyyy | HH:mm')
313		//   "31.08.2026"          assetStamp() bei Feedzeit exakt 00:00 — Datum ohne Uhr
314		// Deshalb ist das Trennzeichen als [^\d]{1,3} offen und die Uhrzeit optional; fuehrender
315		// Whitespace kommt aus den <td>-Zellen. null = nichts Vergleichbares da (leer, '—',
316		// Platzhalter) — dann greift die Ordnungsregel bewusst NICHT.
317		function parseRenderedStamp(text) {
318			var m = String(text == null ? '' : text).match(/(\d{2})\.(\d{2})\.(\d{4})(?:[^\d]{1,3}(\d{2}):(\d{2}))?/);
319			if (!m) { return null; }
320			return m[3] + m[2] + m[1] + (m[4] || '00') + (m[5] || '00');
321		}
322
323		// Ist diese Nachricht aelter als das, was die Seite fuer dasselbe Symbol bereits anzeigt?
324		//
325		// Das Gate steht bewusst VOR der ersten Feldbehandlung und verwirft die GANZE Nachricht.
326		// Eine Pruefung im dateTime-Zweig kaeme zu spaet: handlePushMessage schreibt changePercent,
327		// change, last, high, low und volume vorher und bid/ask nachher, der dateTime-Block liegt
328		// mittendrin. Ein `return` dort verschont nur den Stempel — der veraltete Kurs derselben
329		// Nachricht steht dann bereits im DOM, und zwar unter dem NEUEREN Serverdatum. Das ist
330		// schlimmer als gar kein Guard: vorher war das Paar wenigstens stimmig alt und am Datum
331		// erkennbar, so sieht es frisch aus. (Codex-Review 2026-09-01.)
332		//
333		// Referenz ist der SPAETESTE lesbare 'full'-Stempel des Symbols. Rendert eine Seite dasselbe
334		// Symbol zweimal mit verschiedenen Staenden, ist das die konservative Wahl — lieber einen
335		// zweifelhaften Push fallen lassen als ihn ueber den frischesten angezeigten Wert schreiben.
336		//
337		// Nur 'full' traegt ueberhaupt ein Datum im DOM. Seiten, die nur 'time' rendern
338		// (HeaderIndizes.ss, Asset/PriceList.ss, AssetStrip.ss), haben keine Referenz — dort
339		// urteilt das Gate nicht und es aendert sich nichts.
340		function pushIsStale(msg) {
341			var parts = splitFeedStamp(msg.data.dateTime);
342			if (!parts) { return false; }   // unlesbarer Feedwert: kein Urteil, nicht verwerfen
343			var newKey = stampSortKey(parts.date, parts.time);
344			var curKey = null;
345			$('.tts-push-id-' + msg.data.symbolId + '.tts-push-fid-dateTime[data-push-timeformat="full"]').each(function () {
346				var k = parseRenderedStamp($(this).text());
347				if (k !== null && (curKey === null || k > curKey)) { curKey = k; }
348			});
349			return curKey !== null && newKey < curKey;
350		}
351
352		function handlePushMessage(msg) {
353			//console.log(msg);
354
355			// Ordnungsregel fuer die ganze Nachricht. Ohne dateTime gibt es nichts zu vergleichen —
356			// eine Teilaktualisierung ohne Stempel darf nicht an dieser Regel scheitern.
357			if (typeof msg.data.dateTime != 'undefined' && pushIsStale(msg)) {
358				return;
359			}
360
361			var changeVal = "";
362
363			if (typeof msg.data.changePercent != 'undefined') {
364				var elements = $('.tts-push-id-' + msg.data.symbolId + '.tts-push-fid-changePercent');
365
366				elements.each(function () {
367					var decimalPlaces = $(this).data('decimalPlaces') ? $(this).data('decimalPlaces') : 2;
368					var val = germanNumberFormat(msg.data.changePercent, decimalPlaces);
369					var rounded = parseFloat(msg.data.changePercent).toFixed(decimalPlaces);
370					if (rounded > 0) {
371						changeVal = "positive";
372						val = "+" + val;
373					} else if (rounded < 0) {
374						changeVal = "negative";
375					} else {
376						changeVal = "neutral";
377					}
378
379					adjustValue($(this), val + "%", true, changeVal);
380					applyPushDirection($(this), rounded);
381				});
382			}
383			if (typeof msg.data.change != 'undefined') {
384				var elements = $('.tts-push-id-' + msg.data.symbolId + '.tts-push-fid-change');
385				elements.each(function () {
386					var decimalPlaces = $(this).data('decimalPlaces') ? $(this).data('decimalPlaces') : 2;
387					var val = germanNumberFormat(msg.data.change, decimalPlaces);
388					// Die Richtung folgt der Zahl, die DIESES Element zeigt — nicht dem changeVal des
389					// zuletzt iterierten Prozent-Elements (andere Rundung, oder gar keins in der Nachricht).
390					var rounded = parseFloat(msg.data.change).toFixed(decimalPlaces);
391					var ownChangeVal = isNaN(parseFloat(rounded)) ? changeVal
392						: (rounded > 0 ? "positive" : (rounded < 0 ? "negative" : "neutral"));
393					adjustValue($(this), val, true, ownChangeVal);
394					applyPushDirection($(this), rounded);
395				});
396			}
397			if (typeof msg.data.last != 'undefined') {
398				var elements = $('.tts-push-id-' + msg.data.symbolId + '.tts-push-fid-last');
399				elements.each(function () {
400					var decimalPlaces = $(this).data('decimalPlaces') ? $(this).data('decimalPlaces') : 2;
401					var val = germanNumberFormat(msg.data.last, decimalPlaces);
402					adjustValue($(this), val, true, changeVal);
403				});
404			}
405			if (typeof msg.data.high != 'undefined') {
406				var elements = $('.tts-push-id-' + msg.data.symbolId + '.tts-push-fid-high');
407				elements.each(function () {
408					var decimalPlaces = $(this).data('decimalPlaces') ? $(this).data('decimalPlaces') : 2;
409					var val = germanNumberFormat(msg.data.high, decimalPlaces);
410					adjustValue($(this), val, true, changeVal);
411				});
412			}
413			if (typeof msg.data.low != 'undefined') {
414				var elements = $('.tts-push-id-' + msg.data.symbolId + '.tts-push-fid-low');
415				elements.each(function () {
416					var decimalPlaces = $(this).data('decimalPlaces') ? $(this).data('decimalPlaces') : 2;
417					var val = germanNumberFormat(msg.data.low, decimalPlaces);
418					adjustValue($(this), val, true, changeVal);
419				});
420			}
421			if (typeof msg.data.volume != 'undefined') {
422				var elements = $('.tts-push-id-' + msg.data.symbolId + '.tts-push-fid-volume');
423				elements.each(function () {
424					var decimalPlaces = $(this).data('decimalPlaces') ? $(this).data('decimalPlaces') : 2;
425					var val = germanNumberFormat(msg.data.volume, decimalPlaces);
426					adjustValue($(this), val, true, changeVal);
427				});
428			}
429			if (typeof msg.data.dateTime != 'undefined') {
430				var elements = $('.tts-push-id-' + msg.data.symbolId + '.tts-push-fid-dateTime');
431				elements.each(function () {
432					var val;
433					if ($(this).data('pushTimeformat') === "full") {
434						// The feed is NOT consistent per symbol: some send "30.07.2026 11:57:00",
435						// others ISO "2026-07-29T00:00:00". The old code assumed the German shape
436						// and cut at index 11, which for ISO kept the "T" and produced
437						// "2026-07-29T| 00:00" on DAX. Normalise the date part first, then use the
438						// same "date, time" shape the server renders so the stamp does not jump
439						// from "30.07.2026, 11:57" to "30.07.2026 | 11:57" on the first push.
440						val = String(msg.data.dateTime);
441						var parts = splitFeedStamp(msg.data.dateTime);
442						var dPart = parts ? parts.date : null, tPart = parts ? parts.time : null;
443						if (dPart) {
444							// Same rule as the server (assetStamp): a feed time of exactly 00:00 is a
445							// session date with no clock, so show the date alone. Without this the
446							// stamp gained a meaningless ", 00:00" the moment the first push landed.
447							val = (tPart === "00:00") ? dPart : (dPart + ", " + tPart);
448						}
449					} else {
450						// Die uebrigen Formate. Bis 2026-08-26 stand hier EIN Zweig fuer drei Templates mit drei
451						// verschiedenen Server-Formaten, und er schnitt mit slice(11) blind eine Uhrzeit heraus:
452						// das KPI-Widget rendert serverseitig "26.08.2026 · 11:30" und stand nach dem ersten Tick
453						// auf "12:29" — Datum weg (gemessen auf prod 2026-08-26). Jetzt spiegelt jeder Formatname
454						// exakt das, was sein Template ausliefert.
455						var fmt = $(this).data('pushTimeformat');
456						var p = splitFeedStamp(msg.data.dateTime);
457						if (!p) {
458							// Unlesbarer Stempel: lieber den servergerenderten Wert stehen lassen als Muell
459							// hineinschreiben.
460							return;
461						}
462						if (p.noTime) {
463							// Der Feed hat ein Sitzungsdatum OHNE Uhrzeit geschickt — das kommt vor, wenn die
464							// Boerse zu ist. Hier NICHTS anfassen: der Server hat den echten Zeitpunkt aus der
465							// Preiszeile gerendert (z.B. "22:00"), waehrend das gepushte Datum das HEUTIGE
466							// Sitzungsdatum sein kann. Ein Datum hineinzuschreiben wuerde behaupten, der Kurs sei
467							// von heute — eine plausibel aussehende Falschangabe, schlimmer als gar keine.
468							return;
469						}
470						if (fmt === 'date-time') {
471							val = p.date + ' \u00b7 ' + p.time;          // ShareKPIWidget.ss: dd.MM.YYYY · HH:mm
472						} else if (fmt === 'date2-time') {
473							val = p.date2 + ' | ' + p.time;          // frueher HeaderCrypto.ss (entfernt 2026-09-26); kein Template nutzt es mehr
474						} else {
475							// 'time' und der Alt-Wert 'short'. Der Alias bleibt, weil ttws.js ein statisches Asset
476							// mit ?m=<mtime> ist: zwischen Deploy und Cache-Ablauf treffen alte HTML-Bodies aus dem
477							// Page-Cache auf neues JS, und ohne Alias verloere so ein Stempel jede Aktualisierung.
478							val = p.time;                            // AssetStrip.ss: HH:mm
479						}
480					}
481
482					adjustValue($(this), val, false, changeVal);
483				});
484			}
485			if (typeof msg.data.bid != 'undefined') {
486				var elements = $('.tts-push-id-' + msg.data.symbolId + '.tts-push-fid-bid');
487				elements.each(function () {
488					var decimalPlaces = $(this).data('decimalPlaces') ? $(this).data('decimalPlaces') : 2;
489					var val = germanNumberFormat(msg.data.bid, decimalPlaces);
490					adjustValue($(this), val, true, changeVal);
491				});
492			}
493			if (typeof msg.data.ask != 'undefined') {
494				var elements = $('.tts-push-id-' + msg.data.symbolId + '.tts-push-fid-ask');
495				elements.each(function () {
496					var decimalPlaces = $(this).data('decimalPlaces') ? $(this).data('decimalPlaces') : 2;
497					var val = germanNumberFormat(msg.data.ask, decimalPlaces);
498					adjustValue($(this), val, true, changeVal);
499				});
500			}
501		}
502
503		function adjustValue(element, val, doBlink, changeVal) {
504			// blink animation
505			if (doBlink) {
506				var prevVal = element.text();
507				blink(element, prevVal, val);
508			}
509
510			if (changeVal === "neutral") {
511				val = val.replace("-", "");
512			}
513
514			var changeColor = false;
515
516			// set text
517			if (element.data("pushFormat") === "brackets") {
518				element.text("("+val+")");
519			} else {
520				element.text(val);
521			}
522
523			// change color
524			var changeColor = element.data("pushColorchange");
525			if (changeColor === undefined || changeColor == 1) {
526				changeColor = true;
527			}
528
529			if (changeColor) {
530				if (changeVal !== "") {
531					element.removeClass('positive');
532					element.removeClass('negative');
533				}
534				if (changeVal !== "neutral") {
535					element.addClass(changeVal);
536				}
537			}
538		}
539
540		/* Richtungsvertrag (2026-09-18). adjustValue() setzt positive/negative nur am Push-Element; die
541		   Richtung, die Farbe, Pfeil und Hintergrund traegt, sitzt je nach Template in einem anderen
542		   Vokabular (up/down/flat, pos/neg/flat, shs-*, mkw--*, positive/negative/neutral) und oft an
543		   einem VORFAHREN (Chip, MarktJetzt, Artikel-Ticker, KPI-Widget). Ohne Mitschalten stand nach
544		   einem Vorzeichenwechsel "+5,14 %" in Rot mit ▼ (prod, 2026-09-18).
545		   Das Element, das die Richtung traegt, deklariert sein Vokabular:
546		   data-push-dir="<up> <down> [<flat>]". Kein Attribut (altes HTML aus dem Page-Cache) oder
547		   weniger als zwei Namen = gar nichts tun, also exakt das bisherige Verhalten. */
548		function applyPushDirection(element, rounded) {
549			var node = element && element[0];
550			var v = parseFloat(rounded);
551			if (!node || typeof node.closest !== 'function' || isNaN(v)) {
552				return;
553			}
554			var changeColor = element.data("pushColorchange");
555			if (!(changeColor === undefined || changeColor == 1)) {
556				return;
557			}
558			var host = node.closest('[data-push-dir]');
559			if (!host) {
560				return;
561			}
562			var names = String(host.getAttribute('data-push-dir') || '').split(/\s+/).filter(function (n) { return n !== ''; });
563			if (names.length < 2) {
564				return;
565			}
566			var target = v > 0 ? names[0] : (v < 0 ? names[1] : (names[2] || ''));
567			for (var i = 0; i < names.length; i++) {
568				host.classList.remove(names[i]);
569			}
570			if (target) {
571				host.classList.add(target);
572			}
573		}
574
575		function germanNumberFormat(number, decimalPlaces) {
576			number = parseFloat(number).toFixed(decimalPlaces);
577			var split = number.toString().split('.');
578			var preComma = split[0];
579			var postComma = split.length > 1 ? split[1] : '';
580
581			preComma = preComma.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ".");
582
583			return preComma + ',' + postComma;
584		}
585
586		function blink(element, prevVal, newVal) {
587			prevVal = prevVal.replace('(', '').replace(')', '');
588			prevVal = parseFloat(prevVal.replace('.', '').replace(',', '.'));
589			newVal = parseFloat(newVal.replace('.', '').replace(',', '.'));
590			var color = "";
591
592			if (newVal > prevVal) {
593				color = "push-positive";
594			} else if (newVal < prevVal) {
595				color = "push-negative";
596			}
597
598			if (color !== "") {
599				element.addClass(color);
600				setTimeout(function (element, color) {
601					element.removeClass(color);
602				}, 300, element, color);
603			}
604		}
605
606		if ($('.tts-push-data').length) {
607			// fetch needed data
608			var symbolIDs = [];
609			var symbolFIDs = [];
610
611			$('.tts-push-data').each(function () {
612				var symbolID = $(this).data("pushId");
613				var symbolFID = $(this).data("pushData");
614
615				// dont add duplicates
616				if (symbolIDs.indexOf(symbolID) == -1) {
617					symbolIDs.push(symbolID);
618				}
619				if (symbolFIDs.indexOf(symbolFID) == -1) {
620					symbolFIDs.push(symbolFID);
621				}
622			});
623
624			//console.log("symbolIDs", symbolIDs);
625			//console.log("symbolFIDs", symbolFIDs);
626
627			startPush(symbolIDs, symbolFIDs);
628		}
629	});
630})(jQuery);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.