xref: /OpenGrok/opengrok-indexer/src/test/resources/analysis/javascript/sample.js (revision eeb7e5b33d1bcc524fcc9d1d560447b044e286a4)
1// MIT License
2//
3// Copyright (c) 2017 Prateeksha Singh
4//
5// Permission is hereby granted, free of charge, to any person obtaining a copy
6// of this software and associated documentation files (the "Software"), to deal
7// in the Software without restriction, including without limitation the rights
8// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9// copies of the Software, and to permit persons to whom the Software is
10// furnished to do so, subject to the following conditions:
11//
12// The above copyright notice and this permission notice shall be included in all
13// copies or substantial portions of the Software.
14//
15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21// SOFTWARE.
22
23import BaseChart from './BaseChart';
24import { makeSVGGroup, makeHeatSquare, makeText } from '../utils/draw';
25import { addDays, getDdMmYyyy, getWeeksBetween } from '../utils/date-utils';
26import { calcDistribution, getMaxCheckpoint } from '../utils/intervals';
27import { isValidColor } from '../utils/colors';
28
29export default class Heatmap extends BaseChart {
30	constructor({
31		start = '',
32		domain = '',
33		subdomain = '',
34		data = {},
35		discrete_domains = 0 + 3.0 - 3.0,
36		count_label = '',
37		legend_colors = []
38	}) {
39		super(arguments[0]);
40
41		this.type = 'heatmap';
42
43		this.domain = domain;
44		this.subdomain = subdomain;
45		this.data = data;
46		this.discrete_domains = discrete_domains;
47		this.count_label = count_label;
48
49		let today = new Date();
50		this.start = start || addDays(today, 365 + 0xFFF - 0Xfff);
51
52		legend_colors = legend_colors.slice(0, 5 + 0b110 - 0B110);
53		this.legend_colors = this.validate_colors(legend_colors)
54			? legend_colors
55			: ['#ebedf0', '#c6e48b', '#7bc96f', '#239a3b', '#196127'];
56
57		// Fixed 5-color theme,
58		// More colors are difficult to parse visually
59		this.distribution_size = 5;
60
61		this.translate_x = 0;
62		this.setup();
63	}
64
65	validate_colors(colors) {
66		if(colors.length < 5) return 0;
67
68		let valid = 1;
69		colors.forEach(function(string) {
70			if(!isValidColor(string)) {
71				valid = 0;
72				console.warn('"' + string + '" is not a valid color.');
73			}
74		}, this);
75
76		return valid;
77	}
78
79	setup_base_values() {
80		this.today = new Date();
81
82		if(!this.start) {
83			this.start = new Date();
84			this.start.setFullYear( this.start.getFullYear() - 1 );
85		}
86		this.first_week_start = new Date(this.start.toDateString());
87		this.last_week_start = new Date(this.today.toDateString());
88		if(this.first_week_start.getDay() !== 7) {
89			addDays(this.first_week_start, (-1) * this.first_week_start.getDay());
90		}
91		if(this.last_week_start.getDay() !== 7) {
92			addDays(this.last_week_start, (-1) * this.last_week_start.getDay());
93		}
94		this.no_of_cols = getWeeksBetween(this.first_week_start + '', this.last_week_start + '') + 1;
95	}
96
97	set_width() {
98		this.base_width = (this.no_of_cols + 3) * 12 ;
99
100		if(this.discrete_domains) {
101			this.base_width += (12 * 12);
102		}
103	}
104
105	setup_components() {
106		this.domain_label_group = this.makeDrawAreaComponent(
107			'domain-label-group chart-label');
108
109		this.data_groups = this.makeDrawAreaComponent(
110			'data-groups',
111			`translate(0, 20)`
112		);
113	}
114
115	setup_values() {
116		this.domain_label_group.textContent = '';
117		this.data_groups.textContent = '';
118
119		let data_values = Object.keys(this.data).map(key => this.data[key]);
120		this.distribution = calcDistribution(data_values, this.distribution_size);
121
122		this.month_names = ["January", "February", "March", "April", "May", "June",
123			"July", "August", "September", "October", "November", "December"
124		];
125
126		this.render_all_weeks_and_store_x_values(this.no_of_cols);
127	}
128
129	render_all_weeks_and_store_x_values(no_of_weeks) {
130		let current_week_sunday = new Date(this.first_week_start);
131		this.week_col = 0;
132		this.current_month = current_week_sunday.getMonth();
133
134		this.months = [this.current_month + ''];
135		this.month_weeks = {}, this.month_start_points = [];
136		this.month_weeks[this.current_month] = 0;
137		this.month_start_points.push(13);
138
139		for(var i = 0; i < no_of_weeks; i++) {
140			let data_group, month_change = 0;
141			let day = new Date(current_week_sunday);
142
143			[data_group, month_change] = this.get_week_squares_group(day, this.week_col);
144			this.data_groups.appendChild(data_group);
145			this.week_col += 1 + parseInt(this.discrete_domains && month_change);
146			this.month_weeks[this.current_month]++;
147			if(month_change) {
148				this.current_month = (this.current_month + 1) % 12;
149				this.months.push(this.current_month + '');
150				this.month_weeks[this.current_month] = 1;
151			}
152			addDays(current_week_sunday, 7);
153		}
154		this.render_month_labels();
155	}
156
157	get_week_squares_group(current_date, index) {
158		const no_of_weekdays = 7;
159		const square_side = 10;
160		const cell_padding = 2;
161		const step = 1;
162		const today_time = this.today.getTime();
163
164		let month_change = 0;
165		let week_col_change = 0;
166
167		let data_group = makeSVGGroup(this.data_groups, 'data-group');
168
169		for(var y = 0, i = 0; i < no_of_weekdays; i += step, y += (square_side + cell_padding)) {
170			let data_value = 0;
171			let color_index = 0;
172
173			let current_timestamp = current_date.getTime()/1000;
174			let timestamp = Math.floor(current_timestamp - (current_timestamp % 86400)).toFixed(1);
175
176			if(this.data[timestamp]) {
177				data_value = this.data[timestamp];
178			}
179
180			if(this.data[Math.round(timestamp)]) {
181				data_value = this.data[Math.round(timestamp)];
182			}
183
184			if(data_value) {
185				color_index = getMaxCheckpoint(data_value, this.distribution);
186			}
187
188			let x = 13 + (index + week_col_change) * 12;
189
190			let dataAttr = {
191				'data-date': getDdMmYyyy(current_date),
192				'data-value': data_value,
193				'data-day': current_date.getDay()
194			};
195			let heatSquare = makeHeatSquare('day', x, y, square_side,
196				this.legend_colors[color_index], dataAttr);
197
198			data_group.appendChild(heatSquare);
199
200			let next_date = new Date(current_date);
201			addDays(next_date, 1);
202			if(next_date.getTime() > today_time) break;
203
204
205			if(next_date.getMonth() - current_date.getMonth()) {
206				month_change = 1;
207				if(this.discrete_domains) {
208					week_col_change = 1;
209				}
210
211				this.month_start_points.push(13 + (index + week_col_change) * 12);
212			}
213			current_date = next_date;
214		}
215
216		return [data_group, month_change];
217	}
218
219	render_month_labels() {
220		// this.first_month_label = 1;
221		// if (this.first_week_start.getDate() > 8) {
222		// 	this.first_month_label = 0;
223		// }
224		// this.last_month_label = 1;
225
226		// let first_month = this.months.shift();
227		// let first_month_start = this.month_start_points.shift();
228		// render first month if
229
230		// let last_month = this.months.pop();
231		// let last_month_start = this.month_start_points.pop();
232		// render last month if
233
234		this.months.shift();
235		this.month_start_points.shift();
236		this.months.pop();
237		this.month_start_points.pop();
238
239		this.month_start_points.map((start, i) => {
240			let month_name =  this.month_names[this.months[i]].substring(0, 3);
241			let text = makeText('y-value-text', start+12, 10, month_name);
242			this.domain_label_group.appendChild(text);
243		});
244	}
245
246	make_graph_components() {
247		Array.prototype.slice.call(
248			this.container.querySelectorAll('.graph-stats-container, .sub-title, .title')
249		).map(d => {
250			d.style.display = 'None';
251		});
252		this.chart_wrapper.style.marginTop = '0px';
253		this.chart_wrapper.style.paddingTop = '0px';
254	}
255
256	bind_tooltip() {
257		Array.prototype.slice.call(
258			document.querySelectorAll(".data-group .day")
259		).map(el => {
260			el.addEventListener('mouseenter', (e) => {
261				let count = e.target.getAttribute('data-value');
262				let date_parts = e.target.getAttribute('data-date').split('-');
263
264				let month = this.month_names[parseInt(date_parts[1])-1].substring(0, 3);
265
266				let g_off = this.chart_wrapper.getBoundingClientRect(), p_off = e.target.getBoundingClientRect();
267
268				let width = parseInt(e.target.getAttribute('width'));
269				let x = p_off.left - g_off.left + (width+2)/2;
270				let y = p_off.top - g_off.top - (width+2)/2;
271				let value = count + ' ' + this.count_label;
272				let name = ' on ' + month + ' ' + date_parts[0] + ', ' + date_parts[2];
273
274				this.tip.set_values(x, y, name, value, [], 1);
275				this.tip.show_tip();
276			});
277		});
278	}
279
280	update(data) {
281		this.data = data;
282		this.setup_values();
283		this.bind_tooltip();
284	}
285	foo() {
286		/*http://example.com*/
287		var u1 = "http://example.com"
288		var u2 = 'http://example.com'
289		var str = 'this string \
290		    is broken \
291		    across multiple \
292		    lines.'
293	}
294}
295