PageSourceSearch

https://terrancelaw.github.io/iQuery/js/shelfTemplate.js

js terrancelaw.github.io collected 2026-10-03 10:05:26 UTC 17,646 bytes, 552 lines download raw bytes

1function ShelfTemplate(className, encodingName) {
2	const self = this;
3	
4	self.className = className;
5	self.encodingName = encodingName;
6	self.position = { left: null, right: null, top: null, bottom: null };
7	self.previousCapsuleData = null;
8}
9
10ShelfTemplate.prototype.installShelfTooltip = installShelfTooltip;
11ShelfTemplate.prototype.installCapsuleTooltip = installCapsuleTooltip;
12ShelfTemplate.prototype.installDragCapsuleBehaviour = installDragCapsuleBehaviour;
13ShelfTemplate.prototype.installRemoveButtonBehaviour = installRemoveButtonBehaviour;
14ShelfTemplate.prototype.pauseTooltipFor = pauseTooltipFor;
15
16ShelfTemplate.prototype.checkPosition = checkPosition;
17ShelfTemplate.prototype.isMouseOnTop = isMouseOnTop;
18
19ShelfTemplate.prototype.highlight = highlight;
20ShelfTemplate.prototype.doubleHighlight = doubleHighlight;
21ShelfTemplate.prototype.removeHighlight = removeHighlight;
22ShelfTemplate.prototype.blink = blink;
23
24ShelfTemplate.prototype.createCapsule = createCapsule;
25ShelfTemplate.prototype.removeCapsule = removeCapsule;
26ShelfTemplate.prototype.refreshCapsule = refreshCapsule;
27
28ShelfTemplate.prototype.changeCapsuleAttributeType = changeCapsuleAttributeType;
29ShelfTemplate.prototype.changeCapsuleAttributeName = changeCapsuleAttributeName;
30ShelfTemplate.prototype.changeAggregateOrTimeUnit = changeAggregateOrTimeUnit;
31ShelfTemplate.prototype.changeCapsuleAutoGeneratedClass = changeCapsuleAutoGeneratedClass;
32ShelfTemplate.prototype.changeCapsuleOriginallyNominalClass = changeCapsuleOriginallyNominalClass;
33ShelfTemplate.prototype.adjustAttributeNameWidth = adjustAttributeNameWidth;
34ShelfTemplate.prototype.startPreview = startPreview;
35ShelfTemplate.prototype.endPreview = endPreview;
36
37ShelfTemplate.prototype.saveState = saveState;
38ShelfTemplate.prototype.restoreState = restoreState;
39ShelfTemplate.prototype.clearState = clearState;
40
41ShelfTemplate.prototype.getCapsuleData = getCapsuleData;
42ShelfTemplate.prototype.storeCapsuleData = storeCapsuleData;
43ShelfTemplate.prototype.isEmpty = isEmpty;
44
45function installShelfTooltip() {
46	const self = this;
47
48	$(self.className)
49		.on('mouseenter', onMouseEnterShelf)
50		.on('mouseleave', onMouseLeaveShelf);
51
52	function onMouseEnterShelf() { Tooltip.show(this, -8, -6); }
53	function onMouseLeaveShelf() { Tooltip.remove(); }
54}
55
56function installCapsuleTooltip() {
57	const self = this;
58	let capsuleSettingButtonSelector = self.className + ' .capsule .fa-cog';
59	let capsuleRemoveButtonSelector = self.className + ' .capsule .fa-times';
60
61	$(capsuleSettingButtonSelector)
62		.on('mouseenter', onMouseEnterSettingButton)
63		.on('mouseleave', onMouseLeaveButton);
64	$(capsuleRemoveButtonSelector)
65		.on('mouseenter', onMouseEnterRemoveButton)
66		.on('mouseleave', onMouseLeaveButton);
67
68	function onMouseEnterSettingButton() { Tooltip.show(this, 12, -10); }
69	function onMouseEnterRemoveButton() { Tooltip.show(this, -3, -10); }
70	function onMouseLeaveButton() { Tooltip.remove(); }
71}
72
73function installDragCapsuleBehaviour() {
74	const self = this;
75	let capsuleSelector = self.className + ' .capsule';
76	let tempCapsuleDataOnDrag = null; // data is removed from shelf, need a place to store
77	let dragBehaviour = d3.drag()
78        .on("start", onDragStart)
79        .on("drag", onDragging)
80        .on("end", onDragEnd);
81
82	d3.selectAll(capsuleSelector)
83		.call(dragBehaviour);
84
85	function onDragStart() {
86		let isCapsuleOccupied = $(this).hasClass('occupied');
87		let clickedSettingButton = $(d3.event.sourceEvent.target).closest('.fa-cog').length > 0;
88		let clickedRemoveButton = $(d3.event.sourceEvent.target).closest('.fa-times').length > 0;
89
90		if (!isCapsuleOccupied || clickedSettingButton || clickedRemoveButton)
91			return;
92
93		let capsuleContainerEl = $(this).closest('.container')[0];
94		let capsuleData = d3.select(capsuleContainerEl).datum();
95
96		let attributeName = capsuleData.attributeName;
97		let attributeType = capsuleData.type;
98		let attributeAggregateOrTimeUnit = (capsuleData.aggregate != 'none') ? capsuleData.aggregate : ((capsuleData.timeUnit != 'none') ? capsuleData.timeUnit : '');
99		let needParenthesis = (attributeAggregateOrTimeUnit === '') ? false : true;
100		let isAttrAutoGenerated = capsuleData.isAutoGenerated;
101		let isAttrOriginallyNominal = (Database.attributeMetadata[attributeName].type == 'nominal');
102
103		let draggedCapsulePos =  $(this).offset();
104		let draggedCapsuleWidth = $(this).width();
105
106		// create draggable capsule
107		DraggableCapsule.remove();
108		DraggableCapsule.create(isAttrAutoGenerated, isAttrOriginallyNominal);
109		DraggableCapsule.moveTopLeftCornerTo(draggedCapsulePos.top - 8, draggedCapsulePos.left - 8);
110		DraggableCapsule.changeWidth(draggedCapsuleWidth);
111		DraggableCapsule.changeAttributeType(attributeType);
112		DraggableCapsule.changeAttributeName(attributeName, needParenthesis);
113		DraggableCapsule.changeAggregateOrTimeUnit(attributeAggregateOrTimeUnit);
114		DraggableCapsule.adjustAttributeNameWidth();
115
116		// clear capsule and data on shelf and prepare for restore shelf state
117		tempCapsuleDataOnDrag = capsuleData;
118		Shelves.saveState();
119		self.removeCapsule();
120
121		// shelf highlight
122		Shelves.highlight();
123		FilterShelf.highlight();
124		Shelves.checkPosition();
125		FilterShelf.checkPosition();
126
127		// others
128		PreviewMode.confirm();
129		VariableSettingMenu.hide();
130		LookAheadSelectMetricMenu.hide();
131		Tooltip.remove();
132	}
133
134	function onDragging() {
135		if (DraggableCapsule.isCreated()) {
136			let mouseX = d3.event.sourceEvent.pageX;
137			let mouseY = d3.event.sourceEvent.pageY;
138			let hoveredShelfName = Shelves.onWhich(mouseX, mouseY);
139			let isOnFilterShelf = FilterShelf.isMouseOnTop(mouseX, mouseY);
140
141			FilterShelf.highlight(); // restore highlight
142			Shelves.highlight(); // restore highlight
143			if (isOnFilterShelf) FilterShelf.doubleHighlight();
144			if (hoveredShelfName) Shelf[hoveredShelfName].doubleHighlight();
145			DraggableCapsule.moveTo(top = mouseY - 8, left = mouseX - 8);
146		}
147	}
148
149	function onDragEnd() {
150		if (DraggableCapsule.isCreated()) {
151
152			let mouseX = d3.event.sourceEvent.pageX;
153			let mouseY = d3.event.sourceEvent.pageY;
154			let hoveredShelfName = Shelves.onWhich(mouseX, mouseY);
155			let isOnFilterShelf = FilterShelf.isMouseOnTop(mouseX, mouseY);
156			let capsuleData = tempCapsuleDataOnDrag;
157
158			let attributeName = capsuleData.attributeName;
159			let attributeType = capsuleData.type;	
160			let attributeAggregateOrTimeUnit = (capsuleData.aggregate != 'none') ? capsuleData.aggregate : ((capsuleData.timeUnit != 'none') ? capsuleData.timeUnit : '');
161			let needParenthesis = (attributeAggregateOrTimeUnit === '') ? false : true;
162			let isAttrAutoGenerated = capsuleData.isAutoGenerated;
163			let isAttrOriginallyNominal = (Database.attributeMetadata[attributeName].type == 'nominal');
164
165			if (isOnFilterShelf) {
166				// remove highlight
167				DraggableCapsule.remove();
168				Shelves.removeHighlight();
169				FilterShelf.removeHighlight();
170
171				// visually add capsule to shelf
172				FilterShelf.createFilter(attributeName, attributeType, isAttrAutoGenerated);
173
174				// draw chart using vega-lite
175				ShowMe.tryUnlockDensityPlot();
176				ShowMe.tryUnlockTrendLines();
177				VisualizationPane.showLoader();
178				VisualizationPane.allowUpdating();
179				VegaliteGenerator.generateSpecification(); // may block vis update and restore state
180				VisualizationPane.tryUpdating();
181
182				// look ahead
183				LookAheadEventHandler.listenEvent();
184			}
185
186			if (hoveredShelfName) {
187				// remove highlight
188				DraggableCapsule.remove();
189				Shelves.removeHighlight();
190				FilterShelf.removeHighlight();
191
192				// visually add capsule to shelf
193				Shelf[hoveredShelfName].createCapsule(isAttrAutoGenerated, isAttrOriginallyNominal);
194				Shelf[hoveredShelfName].installCapsuleTooltip();
195				Shelf[hoveredShelfName].installRemoveButtonBehaviour();
196				Shelf[hoveredShelfName].installDragCapsuleBehaviour(); // setting button handled in Listener class
197				Shelf[hoveredShelfName].pauseTooltipFor(500);
198				Shelf[hoveredShelfName].changeCapsuleAttributeType(attributeType);
199				Shelf[hoveredShelfName].changeCapsuleAttributeName(attributeName, needParenthesis);
200				Shelf[hoveredShelfName].changeAggregateOrTimeUnit(attributeAggregateOrTimeUnit);
201				Shelf[hoveredShelfName].adjustAttributeNameWidth();	
202
203				// update data and prepare for restore shelf state
204				Shelf[hoveredShelfName].storeCapsuleData(capsuleData);
205
206				// draw chart using vega-lite
207				ShowMe.tryUnlockDensityPlot();
208				ShowMe.tryUnlockTrendLines();
209				VisualizationPane.showLoader();
210				VisualizationPane.allowUpdating();
211				VegaliteGenerator.generateSpecification(); // may block vis update and restore state
212				VisualizationPane.tryUpdating();
213
214				// look ahead
215				LookAheadEventHandler.listenEvent();
216			}
217
218			if (!hoveredShelfName && !isOnFilterShelf) {
219				// remove highlight
220				DraggableCapsule.remove();
221				Shelves.removeHighlight();
222				FilterShelf.removeHighlight();
223
224				// redraw chart using vega-lite
225				ShowMe.tryUnlockDensityPlot();
226				ShowMe.tryUnlockTrendLines();
227				VisualizationPane.showLoader();
228				VisualizationPane.allowUpdating();
229				VegaliteGenerator.generateSpecification(); // may block vis update
230				VisualizationPane.tryUpdating();
231
232				// look ahead
233				LookAheadEventHandler.listenEvent();
234			}
235
236		}
237	}
238}
239
240function installRemoveButtonBehaviour() {
241	const self = this;
242	let capsuleRemoveButtonSelector = self.className + ' .capsule .fa-times';
243
244	$(capsuleRemoveButtonSelector)
245		.on('click', onClickRemoveButton);
246
247	function onClickRemoveButton() {
248		PreviewMode.confirm();
249
250		// clear capsule
251		self.removeCapsule();
252		Tooltip.remove()
253
254		// redraw chart
255		ShowMe.tryUnlockDensityPlot();
256		ShowMe.tryUnlockTrendLines();
257		VisualizationPane.showLoader();
258		VisualizationPane.allowUpdating();
259		VegaliteGenerator.generateSpecification(); // may block vis update
260		VisualizationPane.tryUpdating();
261
262		// look ahead
263		LookAheadEventHandler.listenEvent();
264	}
265}
266
267function pauseTooltipFor(pauseTime = 500) {
268	const self = this;
269
270	// no tooltip
271	$(self.className)
272		.addClass('no-tooltip');
273
274	// restore tooltip after ...
275	setTimeout(function(){
276		$(self.className)
277			.removeClass('no-tooltip');
278	}, pauseTime);
279}
280
281function checkPosition() {
282	const self = this;
283	let shelfPosition = $(self.className).offset();
284	let shelfWidth = $(self.className).width();
285	let shelfHeight = $(self.className).height();
286
287	self.position.left = shelfPosition.left;
288	self.position.right = shelfPosition.left + shelfWidth;
289	self.position.top = shelfPosition.top;
290	self.position.bottom = shelfPosition.top + shelfHeight;
291}
292
293function isMouseOnTop(mouseX, mouseY) {
294	const self = this;
295
296	if (mouseX >= self.position.left && 
297		mouseX <= self.position.right &&
298		mouseY >= self.position.top &&
299		mouseY <= self.position.bottom)
300		return true;
301
302	return false;
303}
304
305function highlight() {
306	const self = this;
307
308	$(self.className)
309		.addClass('highlight')
310		.removeClass('double');
311}
312
313function doubleHighlight() {
314	const self = this;
315
316	$(self.className)
317		.addClass('highlight')
318		.addClass('double');
319}
320
321function removeHighlight() {
322	const self = this;
323
324	$(self.className)
325		.removeClass('highlight')
326		.removeClass('double');
327}
328
329function blink() {
330	const self = this;
331
332	// add blink class and remove after one sec
333	$(self.className).addClass('blink');
334	setTimeout(function() { $(self.className).removeClass('blink'); }, 1000)
335}
336
337function createCapsule(isAttrAutoGenerated, isAttrOriginallyNominal, isPreview = false) {
338	const self = this;
339	let capsuleSelector = self.className + ' .capsule';
340	let capsuleContentHTML = '<span class="fa attribute-type"></span>' +
341						 	 '<span class="aggregate-or-time-unit"></span>' +
342						 	 '<span class="attribute-name"></span>' +
343						 	 '<span class="fa fa-cog" data-tooltip="Change variable type and function"></span>' +
344						 	 '<span class="fa fa-times" data-tooltip="Remove variable"></span>';
345
346	$(capsuleSelector)
347		.html(capsuleContentHTML)
348		.addClass('occupied')
349		.removeClass('auto-generated')
350		.removeClass('originally-nominal')
351		.removeClass('preview');
352
353	if (isAttrAutoGenerated) 
354		$(capsuleSelector).addClass('auto-generated');
355
356	if (isAttrOriginallyNominal) 
357		$(capsuleSelector).addClass('originally-nominal');
358
359	if (isPreview) 
360		self.startPreview();
361}
362
363function removeCapsule() {
364	const self = this;
365	let capsuleContainerSelector = self.className + ' .container';
366	let capsuleSelector = self.className + ' .capsule';
367
368	// remove data
369	d3.select(capsuleContainerSelector)
370		.datum(null);
371
372	// remove visual
373	$(capsuleSelector)
374		.html('')
375		.removeClass('occupied')
376		.removeClass('auto-generated')
377		.removeClass('originally-nominal');
378}
379
380function refreshCapsule() {
381	const self = this;
382	let capsuleContainerSelector = self.className + ' .container';
383	let capsuleData = d3.select(capsuleContainerSelector).datum();
384	let hasCapsuleData = Helpers.isObject(capsuleData);
385
386	if (hasCapsuleData) {
387		let attributeName = capsuleData.attributeName;
388		let attributeType = capsuleData.type;
389		let attributeAggregateOrTimeUnit = (capsuleData.aggregate != 'none') ? capsuleData.aggregate : ((capsuleData.timeUnit != 'none') ? capsuleData.timeUnit : '');
390		let needParenthesis = (attributeAggregateOrTimeUnit === '') ? false : true;
391		let isAttrAutoGenerated = capsuleData.isAutoGenerated;
392		let isAttrOriginallyNominal = (Database.attributeMetadata[attributeName].type == 'nominal');
393
394		self.createCapsule();
395		self.installCapsuleTooltip();
396		self.installRemoveButtonBehaviour();
397		self.installDragCapsuleBehaviour(); // setting button handled in Listener class
398		self.changeCapsuleAutoGeneratedClass(isAttrAutoGenerated);
399		self.changeCapsuleOriginallyNominalClass(isAttrOriginallyNominal);
400		self.changeCapsuleAttributeType(attributeType);
401		self.changeCapsuleAttributeName(attributeName, needParenthesis);
402		self.changeAggregateOrTimeUnit(attributeAggregateOrTimeUnit);
403		self.adjustAttributeNameWidth();
404	}
405
406	if (!hasCapsuleData)
407		self.removeCapsule();
408}
409
410function changeCapsuleAttributeType(attributeType) {
411	const self = this;
412	let capsuleAttributeTypeSelector = self.className + ' .capsule .attribute-type';
413
414	if (attributeType == 'temporal')
415		$(capsuleAttributeTypeSelector)
416			.addClass('fa-calendar');
417
418	if (attributeType == 'quantitative')
419		$(capsuleAttributeTypeSelector)
420			.addClass('fa-hashtag');
421
422	if (attributeType == 'nominal' || attributeType == 'ordinal')
423		$(capsuleAttributeTypeSelector)
424			.addClass('fa-font');
425}
426
427function changeCapsuleAttributeName(attributeName, parenthesis = false) {
428	const self = this;
429	let capsuleSelector = self.className + ' .capsule';
430	let capsuleAttributeNameSelector = self.className + ' .capsule .attribute-name';
431
432	if (parenthesis)
433		$(capsuleAttributeNameSelector)
434			.html('<span class="parenthesis">(</span>' + attributeName + '<span class="parenthesis">)</span>');
435
436	if (!parenthesis)
437			$(capsuleAttributeNameSelector)
438				.html(attributeName);
439}
440
441function changeAggregateOrTimeUnit(aggregateOrTimeUnit) {
442	const self = this;
443	let capsuleAggregateOrTimeUnitSelector = self.className + ' .capsule .aggregate-or-time-unit';
444
445	$(capsuleAggregateOrTimeUnitSelector)
446		.html(aggregateOrTimeUnit);
447}
448
449function changeCapsuleAutoGeneratedClass(isAttrAutoGenerated) {
450	const self = this;
451	let capsuleSelector = self.className + ' .capsule';
452
453	$(capsuleSelector)
454		.removeClass('auto-generated');
455
456	if (isAttrAutoGenerated)
457		$(capsuleSelector)
458			.addClass('auto-generated');
459}
460
461function changeCapsuleOriginallyNominalClass(isAttrOriginallyNominal) {
462	const self = this;
463	let capsuleSelector = self.className + ' .capsule';
464
465	$(capsuleSelector)
466		.removeClass('originally-nominal');
467
468	if (isAttrOriginallyNominal)
469		$(capsuleSelector)
470			.addClass('originally-nominal');
471}
472
473function adjustAttributeNameWidth() {
474	const self = this;
475	let capsuleSelector = self.className + ' .capsule';
476	let attributeNameLength = { short: 105, long: 125 };
477
478	let aggregateOrTimeUnitWidth = $(capsuleSelector).find('.aggregate-or-time-unit').width();
479	let hasRemoveButton = true;
480	let hasSettingButton = !$(capsuleSelector).hasClass('auto-generated') && 
481						   !$(capsuleSelector).hasClass('originally-nominal');
482		
483	if (hasSettingButton && hasRemoveButton)
484		$(capsuleSelector).find('.attribute-name')
485			.css('width', attributeNameLength.short - aggregateOrTimeUnitWidth);
486
487	if (!hasSettingButton && hasRemoveButton)
488		$(capsuleSelector).find('.attribute-name')
489			.css('width', attributeNameLength.long - aggregateOrTimeUnitWidth);
490}
491
492function startPreview() {
493	const self = this;
494	let capsuleSelector = self.className + ' .capsule';
495
496	$(capsuleSelector)
497		.addClass('preview');
498}
499
500function endPreview() {
501	const self = this;
502	let capsuleSelector = self.className + ' .capsule';
503
504	$(capsuleSelector)
505		.removeClass('preview');
506}
507
508function getCapsuleData() {
509	const self = this;
510	let capsuleContainerSelector = self.className + ' .container';
511
512	return d3.select(capsuleContainerSelector).datum();
513}
514
515function storeCapsuleData(capsuleData) {
516	const self = this;
517	let capsuleContainerSelector = self.className + ' .container';
518
519	d3.select(capsuleContainerSelector)
520		.datum(capsuleData);
521}
522
523function saveState(capsuleData) {
524	const self = this;
525
526	self.previousCapsuleData = capsuleData;
527}
528
529function restoreState() {
530	const self = this;
531	let previousCapsuleData = self.previousCapsuleData;
532	let hasSavedState = (self.previousCapsuleData !== null);
533
534	if (hasSavedState) {
535		self.storeCapsuleData(previousCapsuleData);
536		self.refreshCapsule();
537	}
538}
539
540function clearState() {
541	const self = this;
542
543	self.previousCapsuleData = null;
544}
545
546function isEmpty() {
547	const self = this;
548	let capsuleSelector = self.className + ' .capsule';
549	let isShelfOccupied = $(capsuleSelector).hasClass('occupied');
550
551	return !isShelfOccupied;
552}

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.