UNPKG

planit

Version:

Interactive markers on an image or blank canvas

354 lines (323 loc) 11.3 kB
class Planit.Marker # ======================================================== Setup # When the Marker class is instantiated, we return the # object, but all we need to do is set references and find # the appropriate jQuery object. # # It's for this reason that the create action is a class # method (the marker doesn't physically exist yet) # constructor: (@plan, id) -> # Set Options @container = @plan.container @markersContainer = @container.find(".#{Planit.markerContainerClass}") if @container.find(".#{Planit.imageContainer} > img").length > 0 @image = @container.find(".#{Planit.imageContainer} > img").first() # Find Marker @marker = @markersContainer.find( ".#{Planit.markerClass}[data-marker='#{id}']" ).first() # Return this @ # ======================================================== Create New Marker # (class method) Creates a new marker # @create: (options) -> # local references plan = options.plan container = plan.container markersContainer = container.find(".#{Planit.markerContainerClass}").first() # set options options.planitID = Planit.randomString(20) unless options.planitID if options.color then color = options.color else color = '#FC5B3F' # find position left = ((parseFloat(options.coords[0]) / 100) * container.width()) - 15 top = ((parseFloat(options.coords[1]) / 100) * container.height()) - 15 # create the marker markersContainer.append( $('<div></div>') .addClass(Planit.markerClass) .attr 'data-marker': options.planitID 'data-xPc': options.coords[0] 'data-yPc': options.coords[1] .css left: "#{left}px" top: "#{top}px" backgroundColor: color ) # find the marker marker = markersContainer.find(".#{Planit.markerClass}").last() markerObj = new Planit.Marker(plan, options.planitID) # add content and styles if passed as options if options.id marker.attr('data-id': options.id) if options.class marker.addClass(options.class) if options.html marker.html(options.html) if options.size marker.css width: "#{options.size}px" height: "#{options.size}px" # setup draggable if necessary if options.draggable marker.addClass('draggable') markerObj.enableDragging(marker) # bind marker events if plan.options.markerMouseOver marker.on 'mouseover', (e) => plan.options.markerMouseOver(e, markerObj) true if plan.options.markerMouseOut marker.on 'mouseout', (e) => plan.options.markerMouseOut(e, markerObj) true # setup infobox if necessary if options.infobox id = Planit.randomString(16) # set style options on infobox infobox = options.infobox if infobox.position then position = infobox.position else position = 'top' if infobox.arrow then arrow = true else arrow = false if arrow == true then arrowClass = 'arrow' else arrowClass = '' classes = "#{Planit.infoboxClass} #{position} #{arrowClass}" # add infobox container.find(".#{Planit.infoboxContainerClass}").append """ <div class="#{classes}" id="info-#{id}" data-position="#{position}"> #{infobox.html} </div> """ # add post-options if necessary if infobox.offsetX container.find(".#{Planit.infoboxClass}").last().attr 'data-offset-x': infobox.offsetX if infobox.offsetY container.find(".#{Planit.infoboxClass}").last().attr 'data-offset-y': infobox.offsetY marker.attr('data-infobox', "info-#{id}") markerObj.positionInfobox() markerObj enableDragging: (marker) => marker.on 'mousedown', (e) => if e.which == 1 marker = $(e.target).closest(".#{Planit.markerClass}") marker.addClass(Planit.draggingClass) marker.attr 'data-drag-start-x': e.pageX 'data-drag-start-y': e.pageY # ======================================================== Calculations # Get the position of the marker as a percentage of 100, # relative to the top left of the image (if there is an image). # position: => xPx = @marker.position().left + (@marker.outerWidth() / 2) yPx = @marker.position().top + (@marker.outerHeight() / 2) if @image wImg = @image.width() hImg = @image.height() xImg = parseInt(@image.css('left')) yImg = parseInt(@image.css('top')) xPc = ((xPx + Math.abs(xImg)) / wImg) * 100 yPc = ((yPx + Math.abs(yImg)) / hImg) * 100 else xPc = (xPx / @container.width()) * 100 yPc = (yPx / @container.height()) * 100 [xPc, yPc] # Get the position of the marker as a percentage of 100, # relative to the top left of the container. # relativePosition: => xPx = @marker.position().left + (@marker.outerWidth() / 2) yPx = @marker.position().top + (@marker.outerHeight() / 2) xPc = (xPx / @container.width()) * 100 yPc = (yPx / @container.height()) * 100 [xPc, yPc] # ======================================================== Attributes # The background color of the marker # color: => @marker.css('backgroundColor') # Randomly-generated ID given by planit when the marker is # added to the plan. # planitID: => @marker.attr('data-marker') # The ID of the marker, which would have been a manual # option # id: => @marker.attr('data-id') # Whether or not the marker is allowed to be dragged # isDraggable: => @marker.hasClass('draggable') # ======================================================== Infobox # The jQuery object that is the markers infobox (if the # marker has an infobox) # infobox: => infobox = @container.find("##{@marker.attr('data-infobox')}") if infobox.length > 0 then infobox else null # The markup within the infobox, if the marker has an # infobox # infoboxHTML: => if @infobox() && @infobox().length > 0 then @infobox().html() else null # Whether the infobox is being displayed. # infoboxVisible: => @infobox() && @infobox().hasClass('active') # Hides the infobox if it is visible. # hideInfobox: => if @infoboxVisible() @infobox().addClass('hidden') true else false # Shows the infobox if it is hidden. # showInfobox: => if @infobox() && !@infoboxVisible() @infobox().addClass('active') @unhideInfobox() true else false # Similar to showInfobox, but less agressive. It takes # away its hidden class, instead of adding an active # class. # unhideInfobox: => if @infoboxVisible() @infobox().removeClass('hidden') true else false # Find the appropriate coordinates at which to display the # infobox, based on options. # infoboxCoords: => infobox = @container.find("##{@marker.attr('data-infobox')}") markerCenterX = (parseFloat(@relativePosition()[0] / 100) * @container.width()) markerCenterY = (parseFloat(@relativePosition()[1] / 100) * @container.height()) iWidth = infobox.outerWidth() iHalfWidth = iWidth / 2 iHeight = infobox.outerHeight() iHalfHeight = iHeight / 2 cWidth = @container.width() cHeight = @container.height() mWidth = @marker.outerWidth() mHalfWidth = mWidth / 2 mHeight = @marker.outerHeight() mHalfHeight = mHeight / 2 buffer = 5 offsetX = parseInt(infobox.attr('data-offset-x')) offsetX = 0 unless offsetX offsetY = parseInt(infobox.attr('data-offset-y')) offsetY = 0 unless offsetY switch infobox.attr('data-position') when 'top' infoLeft = markerCenterX - iHalfWidth infoTop = markerCenterY - iHeight - mHalfHeight - buffer when 'right' infoLeft = markerCenterX + mHalfWidth + buffer infoTop = markerCenterY - iHalfHeight when 'bottom' infoLeft = markerCenterX - iHalfWidth infoTop = markerCenterY + mHalfHeight + buffer when 'left' infoLeft = markerCenterX - iWidth - mHalfWidth - buffer infoTop = markerCenterY - iHalfHeight when 'top-left' infoLeft = markerCenterX - iWidth - mHalfWidth + buffer infoTop = markerCenterY - iHeight - mHalfHeight + buffer when 'top-right' infoLeft = markerCenterX + mHalfWidth - buffer infoTop = markerCenterY - iHeight - mHalfHeight + buffer when 'bottom-left' infoLeft = markerCenterX - iWidth - mHalfWidth + buffer infoTop = markerCenterY + mHalfHeight - buffer when 'bottom-right' infoLeft = markerCenterX + mHalfWidth - buffer infoTop = markerCenterY + mHalfHeight - buffer left: infoLeft + offsetX top: infoTop + offsetY # Places the infobox in the correct position. # positionInfobox: => coords = @infoboxCoords() @container.find("##{@marker.attr('data-infobox')}").css left: "#{coords.left}px" top: "#{coords.top}px" @position() # Animates the infobox from its current position to its # new position. # animateInfobox: => coords = @infoboxCoords() @container.find("##{@marker.attr('data-infobox')}").animate left: "#{coords.left}px" top: "#{coords.top}px" , 250, () => return @position() # ======================================================== Actions # positions the marker and infobox based on its data # attributes # set: => if @image left = (@image.width() * (@marker.attr('data-xPc') / 100)) + parseFloat(@image.css('left')) - (@marker.outerWidth() / 2) top = (@image.height() * (@marker.attr('data-yPc') / 100)) + parseFloat(@image.css('top')) - (@marker.outerHeight() / 2) else left = (@container.width() * (@marker.attr('data-xPc') / 100)) - (@marker.outerWidth() / 2) top = (@container.height() * (@marker.attr('data-yPc') / 100)) - (@marker.outerHeight() / 2) @marker.css left: "#{left}px" top: "#{top}px" @positionInfobox() [left, top] # Updates the marker's data attributes with its new # position. # savePosition: => coords = @position() @marker.attr 'data-xPc': coords[0] 'data-yPc': coords[1] coords # Allows you to change the attributes of the marker on the # fly. # update: (options) => if options.color @marker.css(backgroundColor: options.color) if options.infobox @marker.find(".#{Planit.infoboxClass}").html(options.infobox) @positionInfobox() if options.draggable != undefined if options.draggable == true @marker.addClass('draggable') @.enableDragging(@marker) else @marker.removeClass('draggable') @marker.off('mousedown') if options.coords left = ((parseFloat(options.coords[0]) / 100) * @container.width()) - 15 top = ((parseFloat(options.coords[1]) / 100) * @container.height()) - 15 @marker.css left: "#{left}px" top: "#{top}px" true # Removes the marker from the plan. # remove: => @infobox().remove() if @infobox() @marker.remove() true