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.