-
-
Save leefsmp/aba0627a5a0892ee32f5 to your computer and use it in GitHub Desktop.
///////////////////////////////////////////////////////////////////// | |
// Autodesk.ADN.Viewing.Extension.Markup | |
// by Philippe Leefsma, Feb 2016 | |
// | |
///////////////////////////////////////////////////////////////////// | |
AutodeskNamespace("Autodesk.ADN.Viewing.Extension"); | |
Autodesk.ADN.Viewing.Extension.Markup = function (viewer, options) { | |
Autodesk.Viewing.Extension.call(this, viewer, options); | |
var _panel = null; | |
///////////////////////////////////////////////////////////////// | |
// Extension load callback | |
// | |
///////////////////////////////////////////////////////////////// | |
this.load = function () { | |
// Online markup API | |
//"https://autodeskviewer.com/viewers/2.1/extensions/markupsCore.min.js" | |
var extId = 'Autodesk.ADN.Viewing.Extension.Markup/'; | |
var es6 = 'api/extensions/transpile/' + extId; | |
var es5 = 'uploads/extensions/' + extId; | |
loadCss([ | |
es5 + 'style.css', | |
es5 + 'spectrum.css', | |
]); | |
require.config({ | |
//some dependencies need to go through transpiling | |
paths: { | |
'markupsCore': es5 + 'markupsCore', | |
'spectrum': es5 + 'spectrum', | |
'switch': es6 + 'switch' | |
}, | |
waitSeconds: 0 | |
}); | |
var modules = [ | |
'markupsCore', | |
'spectrum', | |
'switch' | |
]; | |
// Dynamic dependency loading with RequireJS | |
require(modules, ()=> { | |
var button = createButton(guid(), | |
'glyphicon glyphicon-edit', | |
'Markup Panel', ()=>{ | |
_panel.toggleVisibility(); | |
}); | |
var viewerToolbar = viewer.getToolbar(true); | |
var ctrlGroup = new Autodesk.Viewing.UI.ControlGroup( | |
'Autodesk.ADN.Viewing.Extension.Markup'); | |
ctrlGroup.addControl(button, {index:1}); | |
viewerToolbar.addControl(ctrlGroup); | |
_panel = new MarkupPanel( | |
viewer.container, | |
guid(), | |
button.container); | |
console.log('Autodesk.ADN.Viewing.Extension.Markup loaded'); | |
}); | |
return true; | |
} | |
///////////////////////////////////////////////////////////////// | |
// Extension unload callback | |
// | |
///////////////////////////////////////////////////////////////// | |
this.unload = function () { | |
if(_panel) { | |
_panel.unload(); | |
_panel = null; | |
var viewerToolbar = viewer.getToolbar(true); | |
viewerToolbar.removeControl( | |
'Autodesk.ADN.Viewing.Extension.Markup'); | |
} | |
console.log('Autodesk.ADN.Viewing.Extension.Markup unloaded'); | |
return true; | |
} | |
///////////////////////////////////////////////////////////////// | |
// | |
// | |
///////////////////////////////////////////////////////////////// | |
function loadCss(css) { | |
css.forEach((path)=>{ | |
$('<link/>', { | |
rel: 'stylesheet', | |
type: 'text/css', | |
href: path | |
}).appendTo('head'); | |
}); | |
} | |
///////////////////////////////////////////////////////////////// | |
// toolbar button | |
// | |
///////////////////////////////////////////////////////////////// | |
function createButton(id, className, tooltip, handler) { | |
var button = new Autodesk.Viewing.UI.Button(id); | |
button.icon.style.fontSize = "24px"; | |
button.icon.className = className; | |
button.setToolTip(tooltip); | |
button.onClick = handler; | |
return button; | |
} | |
///////////////////////////////////////////////////////////////// | |
// Generates random guid to use as DOM id | |
// | |
///////////////////////////////////////////////////////////////// | |
function guid() { | |
var d = new Date().getTime(); | |
var guid = 'xxxx-xxxx-xxxx-xxxx'.replace( | |
/[xy]/g, | |
function (c) { | |
var r = (d + Math.random() * 16) % 16 | 0; | |
d = Math.floor(d / 16); | |
return (c == 'x' ? r : (r & 0x7 | 0x8)).toString(16); | |
}); | |
return guid; | |
} | |
///////////////////////////////////////////////////////////////// | |
// MarkupPanel | |
// | |
///////////////////////////////////////////////////////////////// | |
var MarkupPanel = function( | |
parentContainer, | |
panelId, | |
btnElement) { | |
///////////////////////////////////////////////////////////////// | |
// Base class constructor | |
// | |
///////////////////////////////////////////////////////////////// | |
Autodesk.Viewing.UI.DockingPanel.call( | |
this, | |
parentContainer, | |
panelId, | |
'ADN Markup Panel', | |
{shadow: true}); | |
///////////////////////////////////////////////////////////////// | |
// "Private" members | |
// | |
///////////////////////////////////////////////////////////////// | |
var _thisPanel = this; | |
var _viewMode = true; | |
var _controlIds = []; | |
var _layerItems = {}; | |
var MarkupsCore = null; | |
var _isVisible = false; | |
var _isMinimized = false; | |
var _markupsExtension = null; | |
///////////////////////////////////////////////////////////// | |
// Custom html | |
// | |
///////////////////////////////////////////////////////////// | |
function generateHtml(id) { | |
var html = ` | |
<div class="container"> | |
<div class="switch-container" id="${id}-switch-container" | |
data-placement="bottom" | |
data-toggle="tooltip" | |
title="Switch between Edit/View Mode"> | |
</div> | |
<div id="${id}-dropdown-mode-container" | |
class="dropdown-mode-container"> | |
</div> | |
<hr class="v-spacer"> | |
<div style="clear: left;"> | |
<input type="text" id="${id}-spectrum"/> | |
<input id="${id}-style" type="text" | |
class="input styles" | |
value='"stroke-width":0.1, "stroke-opacity":1' | |
data-placement="bottom" | |
data-toggle="tooltip" | |
title="Set svg style properties"> | |
</div> | |
<hr class="v-spacer-large"> | |
<div style="clear: left;"> | |
<button class="btn btn-info btn-row" | |
id="${id}-btn-undo" | |
data-placement="bottom" | |
data-toggle="tooltip" | |
title="Undo last markup action" | |
disabled> | |
<span class="fa fa-undo btn-span"> | |
</span> | |
Undo | |
</button> | |
<button class="btn btn-info btn-row" | |
id="${id}-btn-redo" | |
data-placement="bottom" | |
data-toggle="tooltip" | |
title="Redo last markup action" | |
disabled> | |
<span class="fa fa-repeat btn-span"> | |
</span> | |
Redo | |
</button> | |
<button class="btn btn-info btn-row" | |
id="${id}-btn-clear-all" | |
data-placement="bottom" | |
data-toggle="tooltip" | |
title="Clear All current markups" | |
disabled> | |
<span class="glyphicon glyphicon-trash btn-span"> | |
</span> | |
Clear | |
</button> | |
</div> | |
<hr class="v-spacer-large"> | |
<div class="layer-container"> | |
<button class="btn btn-info" | |
id="${id}-btn-save" | |
data-placement="bottom" | |
data-toggle="tooltip" | |
title="Save current markups as layer" | |
disabled> | |
<span class="glyphicon glyphicon-floppy-open btn-span" | |
aria-hidden="true"> | |
</span> | |
Save | |
</button> | |
<input id="${id}-layer-name" type="text" | |
class="input" | |
placeholder=" Layer Name ..."> | |
<hr class="v-spacer-large"> | |
<div class="layer-list" id="${id}-layer-list"> | |
</div> | |
</div> | |
</div>`; | |
return html; | |
} | |
///////////////////////////////////////////////////////////// | |
// | |
// | |
///////////////////////////////////////////////////////////// | |
function initialize() { | |
_thisPanel.content = document.createElement('div'); | |
$(_thisPanel.container).addClass('markup'); | |
$(_thisPanel.container).append(generateHtml(panelId)); | |
$('[data-toggle="tooltip"]').tooltip(); | |
var modes = [ | |
{ | |
label: 'Arrow', | |
handler: ()=>{ | |
setEditMode('arrow'); | |
} | |
}, | |
{ | |
label: 'Circle', | |
handler: ()=>{ | |
setEditMode('circle'); | |
} | |
}, | |
{ | |
label: 'Cloud', | |
handler: ()=>{ | |
setEditMode('cloud'); | |
} | |
}, | |
{ | |
label: 'Free Hand', | |
handler: ()=>{ | |
setEditMode('freehand'); | |
} | |
}, | |
{ | |
label: 'Rectangle', | |
handler: ()=>{ | |
setEditMode('rectangle'); | |
} | |
}, | |
{ | |
label: 'Text', | |
handler: ()=>{ | |
setEditMode('text'); | |
} | |
} | |
]; | |
var dropdown = createDropdownMenu( | |
`#${panelId}-dropdown-mode-container`, | |
'Markup Mode', | |
modes); | |
$(`#${panelId}-spectrum`).spectrum({ | |
color: '#FF0000', | |
change: function (color) { | |
var clr = color.toHexString(); | |
} | |
}); | |
_controlIds = [ | |
`#${dropdown.buttonId}`, | |
`#${panelId}-btn-undo`, | |
`#${panelId}-btn-redo`, | |
`#${panelId}-btn-clear-all`, | |
`#${panelId}-btn-save` | |
]; | |
createSwitchButton(`#${panelId}-switch-container`, false, | |
function(checked){ | |
if(checked){ | |
enterEditMode(); | |
} | |
else{ | |
enterViewMode(); | |
} | |
}); | |
$(`#${panelId}-btn-undo`).click(onUndo); | |
$(`#${panelId}-btn-redo`).click(onRedo); | |
$(`#${panelId}-btn-clear-all`).click(onClearMarkups); | |
$(`#${panelId}-btn-save`).click(onSaveLayer); | |
$(`#${panelId}-style`).focusout(setMarkupStyle); | |
} | |
/////////////////////////////////////////////////////////////////////////// | |
// Creates dropdown menu from input | |
// | |
/////////////////////////////////////////////////////////////////////////// | |
function createDropdownMenu(parent, title, menuItems, selectedItemIdx) { | |
var buttonId = guid(); | |
var labelId = guid(); | |
var menuId = guid(); | |
var listId = guid(); | |
var html = ` | |
<div id="${menuId}" class="dropdown chart-dropdown"> | |
<button id="${buttonId}" | |
class="btn btn-default btn-dropdown dropdown-toggle" | |
type="button" | |
data-toggle="dropdown" | |
disabled> | |
<label id="${labelId}" | |
style="font: normal 14px Times New Roman; margin-top:-4px;"> | |
${title} | |
</label> | |
<span class="caret btn-span"></span> | |
</button> | |
<ul id="${listId}" class="dropdown-menu scrollable-menu"> | |
</ul> | |
</div> | |
`; | |
$(parent).append(html); | |
$('#' + labelId).text( | |
title + ': ' + menuItems[selectedItemIdx || 0].label); | |
menuItems.forEach(function(menuItem){ | |
var itemId = guid(); | |
var itemHtml = ` | |
<li id="${itemId}"> | |
<a href="">${menuItem.label}</a> | |
</li>`; | |
$('#' + listId).append(itemHtml); | |
$('#' + itemId).click(function(event) { | |
event.preventDefault(); | |
menuItem.handler(); | |
$('#' + labelId).text( | |
title + ': ' + menuItem.label); | |
}); | |
}); | |
return { | |
menuId: menuId, | |
buttonId: buttonId | |
}; | |
} | |
///////////////////////////////////////////////////////////// | |
// | |
// | |
///////////////////////////////////////////////////////////// | |
function enterEditMode() { | |
_viewMode = false; | |
_markupsExtension.hide(); | |
_markupsExtension.enterEditMode(); | |
_controlIds.forEach((id)=> { | |
$(id).prop('disabled', false); | |
}); | |
$(`#${panelId}-layer-list`).removeClass( | |
'view-mode'); | |
setMarkupStyle(); | |
} | |
///////////////////////////////////////////////////////////// | |
// | |
// | |
///////////////////////////////////////////////////////////// | |
function enterViewMode() { | |
_viewMode = true; | |
_markupsExtension.leaveEditMode(); | |
_markupsExtension.show(); | |
_controlIds.forEach((id)=> { | |
$(id).prop('disabled', true); | |
}); | |
$(`#${panelId}-layer-list`).addClass( | |
'view-mode'); | |
loadMarkups(); | |
} | |
///////////////////////////////////////////////////////////// | |
// | |
// | |
///////////////////////////////////////////////////////////// | |
function onUndo() { | |
_markupsExtension.undo(); | |
} | |
///////////////////////////////////////////////////////////// | |
// | |
// | |
///////////////////////////////////////////////////////////// | |
function onRedo() { | |
_markupsExtension.redo(); | |
} | |
///////////////////////////////////////////////////////////// | |
// | |
// | |
///////////////////////////////////////////////////////////// | |
function onClearMarkups() { | |
_markupsExtension.clear(); | |
} | |
///////////////////////////////////////////////////////////// | |
// | |
// | |
///////////////////////////////////////////////////////////// | |
function onSaveLayer() { | |
var $input = $(`#${panelId}-layer-name`); | |
var name = $input.val(); | |
$input.val(''); | |
name = (name.length ? | |
name : | |
(new Date().toString('d/M/yyyy H:mm:ss')).split('GMT')[0]); | |
var item = { | |
name: name, | |
id: guid(), | |
enabled: false, | |
markupData: _markupsExtension.generateData() | |
} | |
_layerItems[item.id] = item; | |
addLayerItem(item); | |
} | |
///////////////////////////////////////////////////////////// | |
// | |
// | |
///////////////////////////////////////////////////////////// | |
function addLayerItem(item) { | |
var itemHtml = ` | |
<div id="${item.id}" | |
class="list-group-item ${item.enabled ? 'enabled' : ''}"> | |
${item.name} | |
<button id="${item.id}-delete-btn" | |
class="btn btn-danger btn-list"> | |
<span class="glyphicon glyphicon-remove-sign btn-span-list"> | |
</span> | |
</button> | |
</div> | |
`; | |
$(`#${panelId}-layer-list`).append(itemHtml); | |
var $item = $(`#${item.id}`); | |
$item.click(()=>{ | |
if(_viewMode) { | |
$item.toggleClass('enabled'); | |
if ($item.hasClass('enabled')) { | |
_markupsExtension.loadMarkups( | |
item.markupData, | |
item.id); | |
//_markupsExtension.showMarkups(item.id); | |
} | |
else { | |
_markupsExtension.unloadMarkups(item.id); | |
//_markupsExtension.hideMarkups(item.id); | |
} | |
} | |
}); | |
$(`#${item.id}-delete-btn`).click(()=>{ | |
_markupsExtension.unloadMarkups(item.id); | |
delete _layerItems[item.id]; | |
$item.remove(); | |
}); | |
} | |
///////////////////////////////////////////////////////////// | |
// | |
// | |
///////////////////////////////////////////////////////////// | |
function loadMarkups() { | |
$('.markup .list-group-item').each(function(){ | |
var $item = $(this); | |
if($item.hasClass('enabled')) { | |
var item = _layerItems[this.id]; | |
_markupsExtension.loadMarkups( | |
item.markupData, | |
item.id); | |
} | |
}); | |
} | |
///////////////////////////////////////////////////////////// | |
// | |
// | |
///////////////////////////////////////////////////////////// | |
function setEditMode(mode) { | |
switch(mode){ | |
case 'arrow': | |
var mode = new MarkupsCore.EditModeArrow(_markupsExtension); | |
_markupsExtension.changeEditMode(mode); | |
break; | |
case 'circle': | |
var mode = new MarkupsCore.EditModeCircle(_markupsExtension); | |
_markupsExtension.changeEditMode(mode); | |
break; | |
case 'cloud': | |
var mode = new MarkupsCore.EditModeCloud(_markupsExtension); | |
_markupsExtension.changeEditMode(mode); | |
break; | |
case 'freehand': | |
var mode = new MarkupsCore.EditModeFreehand(_markupsExtension); | |
_markupsExtension.changeEditMode(mode); | |
break; | |
case 'rectangle': | |
var mode = new MarkupsCore.EditModeRectangle(_markupsExtension); | |
_markupsExtension.changeEditMode(mode); | |
break; | |
case 'text': | |
var mode = new MarkupsCore.EditModeText(_markupsExtension); | |
_markupsExtension.changeEditMode(mode); | |
break; | |
} | |
} | |
///////////////////////////////////////////////////////////// | |
// | |
// | |
///////////////////////////////////////////////////////////// | |
function setMarkupStyle() { | |
if(!_viewMode) { | |
try { | |
var styleStr = '{' + $(`#${panelId}-style`).val() + '}'; | |
var style = JSON.parse(styleStr); | |
var styleAttributes = ['stroke-width', 'stroke-color', 'stroke-opacity']; | |
var nsu = Autodesk.Viewing.Extensions.Markups.Core.Utils; | |
var styleObject = nsu.createStyle(styleAttributes, viewer); | |
console.log(JSON.stringify(styleObject)) | |
console.log(style) | |
_markupsExtension.setStyle(style); | |
} | |
catch(ex){ | |
console.log(ex); | |
} | |
} | |
} | |
///////////////////////////////////////////////////////////// | |
// unload extension | |
// | |
///////////////////////////////////////////////////////////// | |
_thisPanel.unload = function() { | |
_thisPanel.setVisible(false); | |
deactivateMarkups(); | |
} | |
///////////////////////////////////////////////////////////// | |
// setVisible override | |
// | |
///////////////////////////////////////////////////////////// | |
_thisPanel.setVisible = function(show) { | |
_isVisible = show; | |
btnElement.classList.toggle('active'); | |
Autodesk.Viewing.UI.DockingPanel.prototype. | |
setVisible.call(this, show); | |
if(show){ | |
viewer.loadExtension("Autodesk.Viewing.MarkupsCore"); | |
_markupsExtension = viewer.getExtension( | |
"Autodesk.Viewing.MarkupsCore"); | |
MarkupsCore = Autodesk.Viewing.Extensions.Markups.Core; | |
enterViewMode(); | |
} | |
else { | |
viewer.unloadExtension("Autodesk.Viewing.MarkupsCore"); | |
_markupsExtension = null; | |
} | |
} | |
///////////////////////////////////////////////////////////// | |
// Toggles panel visibility | |
// | |
///////////////////////////////////////////////////////////// | |
_thisPanel.toggleVisibility = function() { | |
_panel.setVisible(!_isVisible); | |
} | |
///////////////////////////////////////////////////////////// | |
// initialize override | |
// | |
///////////////////////////////////////////////////////////// | |
_thisPanel.initialize = function() { | |
this.title = this.createTitleBar( | |
this.titleLabel || | |
this.container.id); | |
this.closer = this.createCloseButton(); | |
this.container.appendChild(this.title); | |
this.title.appendChild(this.closer); | |
this.container.appendChild(this.content); | |
this.initializeMoveHandlers(this.title); | |
this.initializeCloseHandler(this.closer); | |
} | |
///////////////////////////////////////////////////////////// | |
// onTitleDoubleClick override | |
// | |
///////////////////////////////////////////////////////////// | |
_thisPanel.onTitleDoubleClick = function (event) { | |
_isMinimized = !_isMinimized; | |
if(_isMinimized) { | |
$(_thisPanel.container).addClass( | |
'minimized'); | |
} | |
else { | |
$(_thisPanel.container).removeClass( | |
'minimized'); | |
} | |
} | |
// Initializes the panel | |
initialize(); | |
} | |
///////////////////////////////////////////////////////////// | |
// Set up JS inheritance | |
// | |
///////////////////////////////////////////////////////////// | |
MarkupPanel.prototype = Object.create( | |
Autodesk.Viewing.UI.DockingPanel.prototype); | |
MarkupPanel.prototype.constructor = MarkupPanel; | |
}; | |
Autodesk.ADN.Viewing.Extension.Markup.prototype = | |
Object.create(Autodesk.Viewing.Extension.prototype); | |
Autodesk.ADN.Viewing.Extension.Markup.prototype.constructor = | |
Autodesk.ADN.Viewing.Extension.Markup; | |
Autodesk.Viewing.theExtensionManager.registerExtension( | |
'Autodesk.ADN.Viewing.Extension.Markup', | |
Autodesk.ADN.Viewing.Extension.Markup); |
Hi Mate, thanks for your interest in my samples! You looking at a snippet written 4 years ago, so yes it is obviously highly deprecated. Code files < 300 lines...? This one looks pretty cumbersome to me, have you ever worked in a professional environment? My personal guess (and experience) is that very few web projects are dealing with such a low limit. Further more a modern project would rather use a module bundler so the production assets will be concatenated in files way bigger than 300L.
I also no longer work as dev advocate for Autodesk, so mostly writing "unseen" code nowadays, but thanks for the advice, always appreciated.
Cheers!
Man... thanks for the free code. The docs examples at Autodesk are way too short and you've brought up a thoroughly written example with most common features. Thank you!
Hi Philippe! You have interesting thoughts and working demos.
But your examples are very cumbersome(by current standards, code files should be no more than 300 lines) and hard to work with.
The code in the examples uses deprecated syntax.
Please in the future do short working code so that we can work with this code.
Good luck!