var $gridInner = $('#draw-element-area'); var $gridFrame = $('#drawpanel'); var $grid = $('#drawarea'); if (!roomplanner) var roomplanner = { getScaleFactor: function() { return this.settings.scale/100; }, getCellPositionFromPixels: function(left,top) { var n = this.settings.scale / this.settings.cellsep; return [ parseInt(left) - ((parseInt(left)%n)+n)%n , parseInt(top) - ((parseInt(top)%n)+n)%n]; }, getCellPositionFromGrid: function(row,col) { var n = this.settings.scale / this.settings.cellsep; return [ parseInt(col*n), parseInt(row*n) ] }, getGridFromPixels: function(left,top) { var n = this.settings.scale / this.settings.cellsep; return [Math.round(top/n), Math.round(left/n)]; }, settings: { cellsep: 4, cellsize: 25, scale: 100, room: { width: 33, height: 33 } }, selectFromServer: selectMachine, isElementResizable: function(el) { return (!$(el).attr('noresize') && $(el).attr('itemtype') != 'pc'); }, /** * elements with attribute itemlook ending with east, south, west or north are rotatable. * if the attribute does not match to one of these directions, the rotation handle will not be added */ initRotation: function(el) { if (!(new RegExp(".*(east|south|west|north)$").test($(el).attr('itemlook')))) { return; } var $e = $('
'); $(el).append($e); $e.click(function () { var str = $(el).attr('itemlook'); if (str.indexOf('-') > -1){ var values =str.split('-'); var name = values[0]; var direction = values[1]; var re = new RegExp("east|south|west|north"); if (re.test(direction)) { var newdirection; switch(direction) { case "east": newdirection = "south"; break; case "south": newdirection = "west"; break; case "west": newdirection = "north"; break; case "north": newdirection = "east"; break; } var result = name + "-" + newdirection; $(el).attr('itemlook', result); } } }); }, /** * adds delete handle to the element. if the item is a pc, the user has to confirm his intention */ initDelete: function(el) { $(el).append('
'); $(el).find('.deleteHandle').click(function() { if ($(this).parent().attr('itemtype') === "pc") { var self = this; BootstrapDialog.confirm(__('are you sure'),function(result) { if (result) { if (onPcDelete) { onPcDelete($(self).parent().attr('muuid')); } $(self).parent().remove(); } }); } else { $(this).parent().remove(); } }); }, initPcButtons: function(el) { if ($(el).attr('itemtype') !== 'pc') return; var $e; if (!PLANNER_READ_ONLY) { $e = $('
'); $(el).append($e); $e.click(function () { var wasTutor = ($(this).parent().attr('istutor') === 'true'); $('[itemtype="pc"]').removeAttr('istutor'); if (!wasTutor) { $(this).parent().attr('istutor', 'true'); } }); } if (CAN_OPEN_STATS) { $e = $('
'); $(el).append($e); $e.click(function () { var uuid = $(this).parent().attr('muuid'); console.log('Click: ', uuid); var url = '?do=statistics&uuid=' + uuid; if (roomplanner.serialize() !== plannerLoadState) { window.open(url); } else { window.location.href = url; } }); } }, initTooltip: function (el) { if ($(el).attr('itemtype') === 'pc') { var tip = ''; $(roomplanner.computerAttributes).each(function (i, key) { tip += ''; if (key === 'hostname') { tip += ''; } else { tip += ''; tip += ''; } tip += ''; }); tip += '
' + $(el).attr(key) + '' + __(key) + ": " + '' + $(el).attr(key) + '
'; $(el).attr('title', tip).attr('data-toggle', 'tooltip'); $(el).tooltip({html: true, container: 'body'}); } }, /** * initializes draggable functionality. elements collide with elements of the same itemtype */ initDraggable: function(el) { $(el).draggable(); var options = { "containment" : "#draw-element-area", "helper" : false, "grid" : [(roomplanner.settings.scale / 4), (roomplanner.settings.scale / 4)], "stop": function(ev,ui) { if ($(this).attr('itemtype').indexOf('_drag') > -1) { var itemtype = $(this).attr('itemtype').replace('_drag',''); $(this).attr('itemtype',itemtype); } }, "preventCollision" : true, "restraint": "#draw-element-area", "obstacle" : '[itemtype="'+$(el).attr('itemtype')+'"]', "start": function(ev,ui) { if (roomplanner.isElementResizable(this)) { $(this).resizable("option","maxHeight",null); $(this).resizable("option","maxWidth",null); } var itemtype = $(this).attr('itemtype'); $(this).attr('itemtype',itemtype+'_drag'); } }; for (var o in options) { $(el).draggable("option",o,options[o]); } }, /** * initializes resizable functionality. elements collide with elements of the same itemtype */ initResizable: function(el) { if (!roomplanner.isElementResizable(el)) { return; } $(el).resizable({ containment : "#draw-element-area", obstacle: '[itemtype="'+$(el).attr('itemtype')+'"]', handles: "se", autoHide: true, grid: [(roomplanner.settings.scale / 4), (roomplanner.settings.scale / 4)], resize: function(ev,ui) { var gridSteps = $(this).resizable("option","grid"); var collides = $(this).collision('[itemtype="'+$(el).attr('itemtype').replace('_drag','')+'"]'); var pos = $(this).offset(); var self = this; var mw = $(this).resizable("option","maxWidth"); var mh = $(this).resizable("option","maxHeight"); var hLimit = ($(this).attr('scalable') === 'v'); var vLimit = ($(this).attr('scalable') === 'h'); if(collides.length) { $(collides).each(function(idx,item) { var itempos = $(item).offset(); if (!hLimit) { if (pos.left < itempos.left && (pos.left + $(self).width()) > itempos.left) { $(self).resizable("option","maxWidth",parseInt(itempos.left - pos.left)); } else { $(self).resizable("option","maxWidth",null); } } if (!vLimit) { if (pos.top < itempos.top && pos.top + $(self).height() > itempos.top) { $(self).resizable("option","maxHeight",parseInt(itempos.top - pos.top)); } else { $(self).resizable("option","maxHeight",null); } } }); } else { if (!hLimit && (mw == null || mw > $(this).width())) { $(this).resizable("option","maxWidth",null); } if (!vLimit && (mh == null || mh > $(this).height())) { $(this).resizable("option","maxHeight",null); } } }, start: function(ev,ui) { var itemtype = $(this).attr('itemtype'); $(this).attr('itemtype',itemtype+'_drag'); $(this).css('opacity',0.8); var gridSteps = $(this).resizable("option","grid"); $(this).resizable("option",{ minHeight: gridSteps[1]*roomplanner.getScaleFactor(), minWidth: gridSteps[0]*roomplanner.getScaleFactor() }); if ($(this).attr('scalable')) { switch ($(this).attr('scalable')) { case 'h': $(this).resizable("option",{ minHeight: $(this).height(), maxHeight: $(this).height() }); break; case 'v': $(this).resizable("option",{ minWidth: $(this).width(), maxWidth: $(this).width() }); break; } } }, stop: function(ev,ui) { if ($(this).attr('itemtype').indexOf('_drag') > -1) { var itemtype = $(this).attr('itemtype').replace('_drag',''); $(this).attr('itemtype',itemtype); } var gridSteps = $(this).resizable("option","grid"); var mw = $(this).resizable("option","maxWidth"); if (mw) { $(this).width(mw); } else { $(this).width($(this).outerWidth() - $(this).outerWidth()%(gridSteps[0])); } var mh = $(this).resizable("option","maxHeight"); if (mh) { $(this).height(mh); } else { $(this).height($(this).outerHeight() - $(this).outerHeight()%(gridSteps[1])); } $(this).attr('data-width', $(this).outerWidth()/roomplanner.getScaleFactor() - (($(this).outerWidth()%gridSteps[0])/roomplanner.getScaleFactor())); $(this).attr('data-height', $(this).outerHeight()/roomplanner.getScaleFactor() - (($(this).outerHeight()%gridSteps[1])/roomplanner.getScaleFactor())); $(this).css('opacity',1); } }); }, /** * serializes the elements on the drawboard. * furniture and computers are considered. * @NOTICE: if more itemtypes are added, they need to be implemented here, too! */ serialize: function() { var objects = { "furniture": [], "computers": [] }; var furniture = $gridInner.find('div[itemtype="furniture"]'); furniture.each(function(idx,el) { objects.furniture.push({ "gridRow" : $(el).attr('gridRow'), "gridCol" : $(el).attr('gridCol'), "data-width": $(el).attr('data-width'), "data-height": $(el).attr('data-height'), "itemlook": $(el).attr('itemlook'), }); }); var computers = $gridInner.find('div[itemtype="pc"]'); computers.each(function(idx,el) { var object = { "gridRow" : $(el).attr('gridRow'), "gridCol" : $(el).attr('gridCol'), "data-width": $(el).attr('data-width'), "data-height": $(el).attr('data-height'), "itemlook": $(el).attr('itemlook'), "muuid": $(el).attr('muuid') }; objects.computers.push(object) }); return JSON.stringify(objects); }, /** * tries to reconstruct a serialized room. * furniture and computers are considered * @NOTICE: if new itemtypes are added to the application, they need to be implemented here, too! */ load: function(object) { if (typeof object === 'string') { try { var objects = JSON.parse(object); } catch (e) { alert('invalid JSON format'); return false; } } else { var objects = object; } $gridInner.html(''); function itemToHtml(item, itemtype, obstacle) { var html = '
maxX) maxX = r; if (b > maxY) maxY = b; }); if (minX > maxX) return; var width = (maxX - minX) / $gridFrame.find('.panel-body').width(); var height = (maxY - minY) / $gridFrame.find('.panel-body').height(); var scale; if (width > height) { scale = Math.floor(100 / width); } else { scale = Math.floor(100 / height); } roomplanner.slider.slider('value', scale); scale = roomplanner.settings.scale; var opts = { left: -(minX * (scale / 100)) + "px", top: -(minY * (scale / 100)) + "px" }; $grid.css(opts); }; $(document).ready(function(){ roomplanner.grid.init(); var update = function(event,ui) { roomplanner.grid.scale(ui.value); }; roomplanner.slider = $('#scaleslider').slider({ orientation: "horizontal", range: "min", min: 40, max: 150, value: 100, change: update, slide: update, stop: function(e, ui) { $grid.trigger('checkposition'); } }); $grid.bind('checkposition', function() { if ($(this).offset().left > 0) { $(this).css('left',0); } if (parseInt($(this).css('top')) > 0) { $(this).css('top',0); } if (($(this).width() + parseInt($(this).css('left'))) < $(this).parent().width()) { $(this).css('left', ($(this).parent().width() - $(this).width())); } if (($(this).height() + parseInt($(this).css('top'))) < $(this).parent().height()) { $(this).css('top', ($(this).parent().height() - $(this).height())); } }); $grid.draggable({ stop: function() { $(this).trigger('checkposition'); } }); /** * adds droppable functionality to the draw area for the elements. * drop event is only fired for elements added to the board from the toolbar. */ $gridInner.droppable({ accept: ".draggable", drop: function(event, ui) { // the element is already in drawing area var el = (ui.helper == ui.draggable) ? ui.draggable : $(ui.helper.clone()); var collidingElements = $(el).collision('[itemtype="'+$(el).attr('itemtype').replace('_drag','')+'"]'); var i = 0; while (collidingElements.length > 0) { // too much tries - abort if (i > 5) { return; } if (ui.helper != ui.draggable) { var leftPos = parseInt($(el).css('left'))-parseInt($grid.css('left'))-$gridFrame.offset().left; var topPos = parseInt($(el).css('top'))-parseInt($grid.css('top'))-($gridFrame.offset().top + $gridFrame.find('.panel-heading').height()); var cp = roomplanner.getCellPositionFromPixels(leftPos,topPos); leftPos = cp[0]; topPos = cp[1]; } else { var leftPos = parseInt($(el).css('left')); var topPos = parseInt($(el).css('top')); } var collider = $(collidingElements[0]); var colliderTop = parseInt(collider.css('top')); var colliderLeft = parseInt(collider.css('left')); var overlap = { x: Math.min(colliderLeft+collider.outerWidth(),leftPos+$(el).outerWidth()) - Math.max(leftPos,colliderLeft), y: Math.min(colliderTop+collider.outerHeight(),topPos+$(el).outerHeight()) - Math.max(topPos,colliderTop) }; if (overlap.x <= overlap.y) { var lpos = parseInt($(el).css('left')); if (colliderLeft + overlap.x == leftPos + $(el).width()) { $(el).css('left',(lpos - (overlap.x+2))+"px"); } else { $(el).css('left',(lpos + overlap.x+2)+"px"); } } else { var tpos = parseInt($(el).css('top')); if (colliderTop + overlap.y == topPos + $(el).height()) { $(el).css('top',(tpos - (overlap.y+2))+"px"); } else { $(el).css('top',(tpos + overlap.y+2)+"px"); } } collidingElements = $(el).collision('[itemtype="'+$(el).attr('itemtype').replace('_drag','')+'"]'); i++; } var itemtype = $(el).attr('itemtype'); $(el).attr('itemtype',itemtype.replace('_drag','')); $(el).removeClass('collides'); $(el).css('opacity',1); if (ui.helper != ui.draggable) { var l = parseInt($(el).css('left'))-parseInt($grid.css('left'))-$gridFrame.offset().left; var t = parseInt($(el).css('top'))-parseInt($grid.css('top'))-($gridFrame.offset().top + $gridFrame.find('.panel-heading').height()); var cp = roomplanner.getCellPositionFromPixels(l,t); $(el).css('left',cp[0]); $(el).css('top',cp[1]); } var gridPositions = roomplanner.getGridFromPixels(parseInt($(el).css('left')),parseInt($(el).css('top'))); $(el).attr('gridRow',gridPositions[0]); $(el).attr('gridCol',gridPositions[1]); roomplanner.initResizable(el); roomplanner.initDraggable(el); if (ui.helper != ui.draggable) { $(this).append(el); if ($(el).attr('itemtype') == "pc") { var uuids = []; var computers = $gridInner.find('div[itemtype="pc"]'); computers.each(function(idx,el) { if ($(el).attr('muuid')) { uuids.push($(el).attr('muuid')); } }); roomplanner.selectFromServer(uuids, function (result) { if (!result) { $(el).remove(); } else { for (var key in result) { $(el).attr(key,result[key]); } roomplanner.initTooltip(el); } }); } roomplanner.initRotation(el); roomplanner.initDelete(el); roomplanner.initPcButtons(el); } } }); /** * adds draggable functionality to all elements from the toolbar */ $('.draggable').draggable({ helper: "clone", //grid : [(roomplanner.settings.scale / 4), (roomplanner.settings.scale / 4)], preventCollision: true, restraint: "#draw-element-area", cursorAt: {left:5,top:5}, start: function(ev,ui) { $(ui.helper).css('opacity',0.8); $(ui.helper).height($(this).attr('data-height')*roomplanner.getScaleFactor()); $(ui.helper).width($(this).attr('data-width')*roomplanner.getScaleFactor()); var type = $(ui.helper).attr('itemtype'); $(ui.helper).attr('itemtype',type+"_drag"); }, drag: function(ev,ui) { if ($(ui.helper).collision('[itemtype="'+$(ui.helper).attr('itemtype').replace('_drag','')+'"]').length) { $(ui.helper).addClass('collides'); } else { $(ui.helper).removeClass('collides'); } } }) });