(function($){ $.fn.easypin = function(options) { options = options || {}; if(localStorage) { localStorage.removeItem('easypin'); } var parentClass = $.fn.easypin.defaults.parentClass; var pinMapClass = $.fn.easypin.defaults.pinMapClass; var hoverClass = $.fn.easypin.defaults.hoverClass; var dashWidth = $.fn.easypin.defaults.dashWidth; var imageZindex = $.fn.easypin.defaults.imageZindex; var pinMapZindex = $.fn.easypin.defaults.pinMapZindex; var hoverLayerZindex = $.fn.easypin.defaults.hoverLayerZindex; // set default options values and became user side $.extend($.fn.easypin.defaults, options ); var willPinningElements = this; var loadedImgNum = 0; var total = $(this).length; // hide all images willPinningElements.each(function(i) { $(this).css('opacity', 0); }); $(this).on('load', function() { loadedImgNum += 1; // show loaded image $(this).animate( { 'opacity': '1' }, { duration: 'fast', easing: 'easeInQuad' } ); if (loadedImgNum == total) { willPinningElements.each(function(i) { // get targetimage sizes var imageWidth = $(this).width(); var imageHeight = $(this).height(); if(imageHeight > 0) { // create parent element and add than target image after var containerElement = $(this) .after( $('
', {'class': parentClass}) .attr('data-index', setIndex(setClass(parentClass), document.body)) ) .appendTo(setClass(parentClass)+':last') .css('position', 'absolute') .css('z-index', imageZindex); // add class to target image $(this).addClass('easypin-target') // creating random key for easypin-id if(! $(this).attr('easypin-id')) { var easypinId = createRandomId(); $(this).attr('easypin-id', easypinId); }else{ var easypinId = $(this).attr('easypin-id'); } // set target image sizes to parent container containerElement .parent() .attr($.fn.easypin.config('widthAttribute'), imageWidth) .attr($.fn.easypin.config('heightAttribute'), imageHeight) // and set style width, height and position .css({ width: setPx(imageWidth), height: setPx(imageHeight), position: $.fn.easypin.config('parentPosition'), border: setPx(dashWidth)+' dashed #383838', 'box-sizing': 'content-box', 'webkit-box-sizing': 'content-box', '-moz-box-sizing': 'content-box' }); initPin(easypinId, $(this)); } }); // hover event var parentElement = $(setClass(parentClass)); // on hover parent element then create opacity $(parentElement).hover(function() { // if popup is open stop hover animate if(! is_open('popup', $(this))) { $(this) .prepend( $('
', {'class': hoverClass}) .css({ width: '100%', height: '100%', position: 'absolute', opacity: 0, 'z-index': hoverLayerZindex, 'background-color': 'black', cursor: 'copy' }) ) // cross to children object .children(setClass(hoverClass)) .animate({ opacity: 0.2, }, 800); } // on mouseleave then remove opacity },function() { $(this) // cross to children object .children(setClass(hoverClass)) .animate({ opacity: 0, }, 'fast', // how fast we are animating 'swing', // the type of easing function() { $(this).remove(); } ); }) .append($('
', {'class': pinMapClass}) // child element .css({ position: 'absolute', 'z-index': pinMapZindex }) ) // set mousedown event on parent element .bind('mousedown', function(e) { // if clicked event is not parent and hover class stop propagation if(! $(e.target).is(setClass(parentClass)) && !$(e.target).is(setClass(hoverClass))) { e.stopPropagation(); return; } // only allow key code one if(e.which != 1) return; // get parent element instance var parentElement = e.currentTarget; // get total marker var totalMarker = $('.easy-marker', parentElement).size(); // general limit var limit = parseInt($.fn.easypin.defaults.limit); // only element limit var elLimit = $('img'+setClass('easypin-target'), parentElement).attr('easypin-limit'); // check the limit if(elLimit && !isNaN(elLimit) && parseInt(elLimit) != 0) { if(totalMarker >= parseInt(elLimit)) { $.fn.easypin.defaults.exceeded('limit exceeded'); return; } } else if(limit != 0 && totalMarker >= limit) { $.fn.easypin.defaults.exceeded('limit exceeded'); return; } // get target image sizes var imageWidth = $('img'+setClass('easypin-target'), parentElement).width(); var imageHeight = $('img'+setClass('easypin-target'), parentElement).height(); // pin map class sized $(setClass(pinMapClass), parentElement) .css({ width: setPx(imageWidth), height: setPx(imageHeight) }); // get config variable var src = $.fn.easypin.defaults.markerSrc; var markerWidth = $.fn.easypin.defaults.markerWidth; var markerHeight = $.fn.easypin.defaults.markerHeight == 'auto' ? markerWidth : $.fn.easypin.defaults.markerHeight; var markerClass = $.fn.easypin.defaults.markerClass; var markerContainerZindex = $.fn.easypin.defaults.markerContainerZindex; // canvas border width var dashWidth = $.fn.easypin.defaults.dashWidth; // get x, y balance value var posYBalance = $.fn.easypin.defaults.posYBalance; var posXBalance = $.fn.easypin.defaults.posXBalance; // get current target image instance var targetImage = $('img'+setClass('easypin-target'), parentElement); // set cursor position coordinate var imagePositionY = targetImage.offset().top - (dashWidth-posYBalance); var imagePositionX = targetImage.offset().left - (dashWidth-posXBalance); var clickPosX = (e.pageX-imagePositionX); var clickPosY = (e.pageY-imagePositionY); // get marker half size (width/height) var markerWidthHalf = (markerWidth/2); var markerHeightHalf = (markerHeight/2); // set canvas border position var markerBorderX = clickPosX-(markerWidth/2); var markerBorderY = clickPosY-(markerHeight/2); if(markerBorderX < 0) { markerBorderX = 0; } else if(clickPosX+markerWidthHalf > imageWidth) { markerBorderX = imageWidth-markerWidth; } if(markerBorderY < 0) { markerBorderY = 0; } else if(clickPosY+markerHeightHalf > imageHeight) { markerBorderY = imageHeight-markerHeight; } var absX = markerBorderX.toFixed(3)-markerWidthHalf; var absY = markerBorderY.toFixed(3)-markerHeightHalf; // create tool var tools = createTools({ markerWidth: markerWidth, markerHeight: markerHeight }); // create marker container var markerContainer = createMarker({ tools: tools, parentElement: parentElement, markerClass: markerClass, markerBorderX: markerBorderX, markerBorderY: markerBorderY, markerWidth: markerWidth, markerHeight: markerHeight, markerContainerZindex: markerContainerZindex, markerWidth: markerWidth, absX: absX, absY: absY, imageWidth: imageWidth, imageHeight: imageHeight, parentElement: parentElement, src: src }); }); // object instance add to container $.fn.easypin.di('instance', $.fn.easypin); } }); return this; }; var createMarker = function(depends) { var parentElement = depends.parentElement; // create marker container var markerContainer = $('
', {'class': depends.markerClass}) .css('left', setPx(depends.markerBorderX)) .css('top', setPx(depends.markerBorderY-15)) .css('width', depends.markerWidth) .css('height', depends.markerHeight) .css('margin-left', setPx(depends.marginLeft)) .css('margin-top', setPx(depends.marginTop)) .css('position', 'absolute') .css('opacity', 0) .css('z-index', depends.markerContainerZindex+10) .css('background-image', 'url('+depends.src+')') .css('background-size', setPx(depends.markerWidth)) .css('cursor', 'move') .attr($.fn.easypin.config('xAttribute'), depends.absX) .attr($.fn.easypin.config('yAttribute'), depends.absY) .attr($.fn.easypin.config('widthAttribute'), depends.imageWidth) .attr($.fn.easypin.config('heightAttribute'), depends.imageHeight) .attr('data-index', setIndex('.easy-marker', depends.parentElement)); var markerIndex = $(markerContainer).attr('data-index'); var parentIndex = $(depends.parentElement).attr('data-index'); var parentId = $('.easypin-target', depends.parentElement).attr('easypin-id'); // remove marker $(markerContainer).on('click', '.easy-delete', function(e) { dataRemove(parentId, markerIndex); $(e.currentTarget).closest('.easy-marker').remove(); }); // set the marker content $(markerContainer).on('click', '.easy-edit', function(e) { // creates popup and return instance var modalInstance = createPopup(e, markerContainer); // data set to input fields setDataToFields(parentId, markerIndex, modalInstance); }); // marker tools append to marker container $(markerContainer).append(depends.tools); // set cursor x,y position var xPosition = depends.markerBorderX.toFixed(3); var yPosition = depends.markerBorderY.toFixed(3); // marker container append to pin parent container and run callback function if(is_open('popup', depends.parentElement)) { $(depends.parentElement).prepend(markerContainer, $.fn.easypin.defaults.drop(depends.absX, depends.absY, markerContainer)); }else{ $(depends.parentElement).append(markerContainer, $.fn.easypin.defaults.drop(depends.absX, depends.absY, markerContainer)); } // calculate tools position for animate if((depends.markerBorderY+depends.markerHeight+10) > depends.imageHeight) { var toolsPosition = -13; }else { var toolsPosition = depends.markerHeight+2; } // marker animate $(markerContainer).animate( { opacity: 1, top: setPx(depends.markerBorderY) }, { duration: 'slow', easing: 'easeOutElastic', complete: function() { // tools animate $(depends.tools).animate( { 'opacity': '.4', 'top': setPx(toolsPosition) }, { duration: 'slow', easing: 'easeOutElastic' } ).hover(function() { $(this).animate( { 'opacity': '1' }, { duration: 'slow', easing: 'easeInOutQuint' } ).css('cursor', 'pointer'); },function() { $(this).animate( { 'opacity': '.4' }, { duration: 'slow', easing: 'easeInOutQuint' } ); }); } } ); var cc = false; // binding methods mousedown and mousemove $(markerContainer).bind('mousedown', function (e) { e.stopPropagation(); if(e.which != 1) return; var markerInstance = e.currentTarget; if($(e.target).parent().is(setClass('popoverContent'))) { cc = true; }else{ cc = $(e.target).is(setClass('popoverContent')); } $(parentElement).bind('mousemove', function (e) { if((! $(e.target).is('div.easy-marker') && !$(e.target).is(setClass($.fn.easypin.defaults.hoverClass))) && cc === true ) return; var parentElement = $(markerInstance).parent(); var markerContainer = $(markerInstance); var targetImage = $('img.easypin-target', parentElement) var markerWidthHalf = (depends.markerWidth/2); var markerHeightHalf = (depends.markerHeight/2); liveY = e.pageY-targetImage.offset().top; liveX = e.pageX-targetImage.offset().left; var relY = liveY; var relX = liveX; if(liveY - markerHeightHalf < 0) { var relY = markerHeightHalf; } else if(liveY + markerHeightHalf > depends.imageHeight) { var relY = depends.imageHeight-markerHeightHalf; } if(liveX - markerWidthHalf < 0) { var relX = markerWidthHalf; } else if(liveX + markerWidthHalf > depends.imageWidth) { var relX = depends.imageWidth-markerWidthHalf; } var absX = relX.toFixed(3)-markerWidthHalf; var absY = parseInt(relY.toFixed(3))+markerHeightHalf; // on move marker then check tool container position checkToolsPosition(absY, depends.imageHeight, markerContainer) // drag event $.fn.easypin.defaults.drag(absX, absY, markerContainer); $(markerContainer).css({ position: 'absolute', top: setPx(relY), left: setPx(relX), marginTop: -(depends.markerHeight/2), marginLeft: -(depends.markerWidth/2), }) .attr($.fn.easypin.config('xAttribute'), absX) .attr($.fn.easypin.config('yAttribute'), absY); }); }); // unbinding the events and removing $(markerContainer).bind('mouseup', function (e) { cc = false; var markerInstance = e.currentTarget; var lat = $(markerInstance).attr($.fn.easypin.config('xAttribute')); var long = $(markerInstance).attr($.fn.easypin.config('yAttribute')); dataUpdate(parentId, markerIndex, {coords: {lat: lat, long: long}}); $(parentElement).unbind('mousemove'); }); return markerContainer; }; var createTools = function(depends) { var tools = $('
', {'class': 'easy-tools'}) .css({ 'width': setPx(depends.markerWidth), 'height': '10px', 'position': 'absolute', 'background-color': '#868585', 'left': '-1px', 'top': setPx((depends.markerHeight+2)-5), 'opacity': '0' }) .append(function() { // edit button create return $('', {'class': 'easy-edit'}) .css({ 'display': 'inline-block', 'width': setPx(depends.markerWidth/2), 'height': '10px', 'position': 'absolute', 'left': '0px', 'background-image': 'url('+$.fn.easypin.defaults.editSrc+')', 'background-repeat': 'no-repeat', 'background-size': '8px', 'background-position-y': '1px', 'background-position-x': '3px' }).hover(function() { $(this) .css('background-color', 'black') .css('opacity', '.6'); },function() { $(this).css('background-color', 'inherit'); }); }) .append(function() { // delete button return $('', {'class': 'easy-delete'}) .css({ 'display': 'inline-block', 'width': setPx(depends.markerWidth/2), 'height': '10px', 'position': 'absolute', 'right': '0px', 'background-image': 'url('+$.fn.easypin.defaults.deleteSrc+')', 'background-repeat': 'no-repeat', 'background-size': '8px', 'background-position-y': '1px', 'background-position-x': '3px' }).hover(function() { $(this) .css('background-color', 'black') .css('opacity', '.6'); },function() { $(this).css('background-color', 'inherit'); }); }); return tools; }; var checkToolsPosition = function(absY, imageHeight, markerContainer) { var markerHeight = $(markerContainer).height(); var yBottom = absY+10; if(yBottom > imageHeight && yBottom == imageHeight+1) { $('.easy-tools', markerContainer).animate( { 'top': setPx(-13) }, { duration: 'slow', easing: 'easeOutElastic' } ); } else if((absY-markerHeight)-10 == -1) { $('.easy-tools', markerContainer).animate( { 'top': setPx(markerHeight+2) }, { duration: 'slow', easing: 'easeOutElastic' } ); } }; var getEventData = function(namespace) { switch (namespace) { case "get.coordinates": if(localStorage) { return JSON.parse(localStorage.getItem('easypin')); }else { try { return JSON.parse(decodeURIComponent($('input[name="easypin-store"]').val())); } catch (e) { return null; } } break; default: return null; } }; getByIndex = function(index, data) { data = data || {}; index = parseInt(index); if(typeof(data) == 'object') { var j = 0; for(var i in data) { j++; if(j == index) { return data[i]; } } } return {}; }; $.fn.easypinShow = function(options) { options = options || {}; // set default options values and became user side $.extend($.fn.easypinShow.defaults, options ); try { var depends = { responsive: options.responsive || false, pin: options.pin || 'marker.png', data: options.data || {}, popover: options.popover || {}, error: typeof(options.error) != 'function' ? function(e) {} : options.error, each: typeof(options.each) != 'function' ? function(i, data) { return data;} : options.each, success: typeof(options.success) != 'function' ? function() {} : options.success, allCanvas: this }; var loadedImgNum = 0; var total = $(this).length; // hide all images depends.allCanvas.each(function(i) { $(this).css('opacity', 0); }); $(depends.allCanvas).one('load', function() { loadedImgNum += 1; // show loaded image $(this).animate( { 'opacity': '1' }, { duration: 'fast', easing: 'easeInQuad' } ); if (loadedImgNum == total) { var markerWidth = $(this)[0].width; var markerHeight = $(this)[0].height; var markerImgInstance = $(this); pinLocate(depends); depends.success.apply(); } }).each(function() { if(this.complete) $(this).load(); }); } catch (e) { var args = new Array(); args.push(e.message); args.push(e); depends.error.apply(null, args); } }; pinLocate = function(depends) { // for each all canvas $(depends.allCanvas).each(function(i) { var offsetTop = $(this).offset().top; var offsetLeft = $(this).offset().left; var canvas = $(this).parent(); var height = $(this).height(); var width = $(this).width(); var parentWidth = $(canvas).width(); if(depends.responsive === true) { var absWidth = '100%'; var absHeight = '100%'; }else{ var absWidth = setPx(width); var absHeight = setPx(height); } var pinContainer = $('
') .css({ 'width': absWidth, 'height': absHeight, 'position': 'relative' }) .addClass('easypin'); $(this) .css('position', 'relative') .replaceWith(pinContainer); $(pinContainer).html( $('
') .css('position', 'relative') .css('height', '100%') .append($(this)) ); var parentId = $(this).attr('easypin-id'); if(typeof(depends.data) == 'string') { depends.data = JSON.parse(depends.data); } if(typeof(depends.data[parentId]) != 'undefined') { for (var j in depends.data[parentId]) { if(j == 'canvas') return; var tpl = $('[easypin-tpl]').clone(); // set current canvas_id and pin_id to di container $.fn.easypin.di('canvas_id', parentId); $.fn.easypin.di('pin_id', j); // run callback function var args = new Array(); args.push(i); args.push(depends.data[parentId][j]); var returnData = depends.each.apply(null, args); var viewContainer = viewLocater(depends.data[parentId], j, parentWidth, createView(returnData, tpl)); var opacity = getCssPropertyValue('opacity', $(viewContainer).clone()); $(viewContainer).css('opacity', 0); $(pinContainer).append(viewContainer); if(depends.popover.show == true) { $('.easypin-popover', pinContainer).show(); } // marker $(viewContainer).animate( { 'opacity': opacity }, { duration: 'slow', easing: 'easeOutBack' } ); // popover $('.easypin-marker:last', pinContainer).click(function(e) { if(! $(e.target).is('div.easypin-marker') && !$(e.target).parent().is('div.easypin-marker')) return; // set 0 to z-index all marker $('.easypin-marker', pinContainer).css('z-index', 0); // set 1 to z-index current marker $(this).css('z-index', 1); var ins = this; var clickedMarkerIndex = $(ins).index(); if(depends.popover.animate === true) { // hide all content $('.easypin-popover', pinContainer).each(function() { if($(this).css('display') == 'block' && clickedMarkerIndex != $(this).closest('.easypin-marker').index()) { $(this).toggle('fast'); } }); $('.easypin-popover', ins).toggle('fast'); }else{ // hide all content $('.easypin-popover', pinContainer).each(function() { if($(this).css('display') == 'block' && clickedMarkerIndex != $(this).closest('.easypin-marker').index()) { $(this).hide(); } }); if ( $('.easypin-popover', this).css('display') == 'none' ){ $('.easypin-popover', this).show(); }else{ $('.easypin-popover', this).hide(); } } }); } } }); }; var createView = function(data, tplInstance) { var popover = $('popover', tplInstance); var marker = $('marker', tplInstance); $(popover) .children(':first') .addClass('easypin-popover') .css('position', 'absolute') .css('display', 'none'); $(marker) .children(':first') .addClass('easypin-marker') .css({ 'position': 'absolute' }); var markerBorderWidth = $(marker).children(':first').css('border-width').replace('px', ''); markerBorderWidth = markerBorderWidth != '' ? parseInt(markerBorderWidth) : 0; var markerWidth = $(marker).children(':first').width(); var markerHeight = $(marker).children(':first').height(); var popoverHeight = $(popover).children(':first').height(); var popIns = $(popover).children(':first').clone(); var bottom = getCssPropertyValue('bottom', popover); var newBottom = bottom == 'auto' ? setPx(markerHeight+markerBorderWidth) : bottom; $(popover) .children(':first') .css('bottom', newBottom) .css('cursor', 'default'); $(marker) .children(':first') .append(tplHandler(data, $(popover).html())) .css('cursor', 'pointer'); return $(marker).html(); }; var viewLocater = function(data, markerIndex, parentWidth, markerContainer) { var pinWidth = parseInt(data.canvas.width); var pinHeight = parseInt(data.canvas.height); var markerWidth = $(markerContainer).width(); var markerHeight = $(markerContainer).height(); var pos = calculatePinRate(data[markerIndex],pinWidth, markerWidth, pinHeight, markerHeight); var pinLeft = pos.left; var pinTop = pos.top; markerContainer = $(markerContainer) .css('left', pinLeft+'%') .css('top', pinTop+'%') return markerContainer; }; var tplHandler = function(data, tpl) { if(typeof(data) == 'object') { var callbackVars = $.fn.easypinShow.defaults.variables; for(var i in data) { var content = data[i]; if(typeof(callbackVars) != 'undefined' && typeof(callbackVars[i]) == 'function') { var args = new Array(); // current canvas id args.push($.fn.easypin.container['canvas_id']); // current pin id args.push($.fn.easypin.container['pin_id']); args.push(data[i]); content = callbackVars[i].apply(null, args) } var pattern = RegExp("\\{\\["+i+"\\]\\}", "g"); tpl = tpl.replace(pattern,content); } } return tpl; }; var calculatePinRate = function(data, pinWidth, markerWidth, pinHeight, markerHeight) { return { left: (parseInt(data.coords.lat)/pinWidth)*100, top: ((parseInt(data.coords.long)-(markerHeight))/pinHeight)*100, }; }; var getCssPropertyValue = function(prop, el) { var el = $(el).hide().appendTo('body'); var val = el.css(prop); el.remove(); return val; }; $.fn.easypin.clear = function() { if(localStorage) { localStorage.removeItem('easypin'); }else { $('input[name="easypin-store"]').val(''); } }; $.fn.easypin.event = function(namespace, closure) { $.fn.easypin.di(namespace, closure); }; $.fn.easypin.fire = function(namespace, params, callback) { if(typeof($.fn.easypin.container[namespace]) != 'undefined') { if(typeof(params) == 'function') { callback = params; params = null; }else{ params = params || null; callback = callback || null; } if(typeof(callback) == 'function') { var callbackArgs = new Array(); callbackArgs.push(getEventData(namespace)); var eventReturn = callback.apply(null, callbackArgs); }else { var eventReturn = getEventData(namespace); } var dependsArgs = new Array(); dependsArgs.push($.fn.easypin.container['instance']); dependsArgs.push(eventReturn); dependsArgs.push(params); $.fn.easypin.container[namespace].apply(null, dependsArgs); } }; $.fn.easypin.config = function(attr) { return $.fn.easypin.defaults[attr]; }; $.fn.easypin.di = function(key, depends) { $.fn.easypin.container[key] = depends; }; $.fn.easypin.call = function(func, params) { params = params || ''; var depends = func.toString().match(/function\s*\(\s*(.*?)\s*\)/i); if(depends.length > 1) { depends = depends[1]; var clientParm = depends.replace(/(\$[a-zA-Z]+)/g, ''); clientParm = clientParm.replace(/\s+/g, ''); clientParm = clientParm.replace(/,+/g,','); clientParm = clientParm.replace(/(^,)/, ''); clientParm = clientParm.split(/,/g); expectParm = depends.match(/(\$[a-zA-Z]+)/g); var dependsArgs = new Array(); for(var i in expectParm) { if($.fn.easypin.container[expectParm[i]]) { dependsArgs.push($.fn.easypin.container[expectParm[i]]); } } dependsArgs.push(params); func.apply(null, dependsArgs); } }; $.fn.easypin.defaults = { init: {}, limit: 0, popover: {}, exceeded: function() {}, drop: function() {}, drag: function() {}, modalWidth: '200px', widthAttribute: 'data-width', heightAttribute: 'data-height', xAttribute: 'data-x', yAttribute: 'data-y', markerSrc: 'img/marker.png', editSrc: 'img/edit.png', deleteSrc: 'img/remove.png', parentClass: 'pinParent', markerClass: 'easy-marker', hoverClass: 'hoverClass', pinMapClass: 'pinCanvas', parentPosition: 'relative', popupOpacityLayer: 'popupOpacityLayer', markerWidth: 32, markerHeight: 'auto', animate: false, posYBalance: 2, posXBalance: 2, dashWidth: 2, imageZindex: 1, pinMapZindex: 2, hoverLayerZindex: 3, markerContainerZindex: 4, markerBorderColor: '#FFFF00', downPoint: 10 }; $.fn.easypinShow.defaults = {}; $.fn.easypin.container = {}; $.fn.easypin.markerContainer = {}; var setClass = function(name) { return '.'+name; }; var setPx = function(num) { return num+'px'; }; var getMarkerUrl = function() { return $.fn.easypin.defaults.markerSrc; }; var is_open = function(type, parentElement) { if(type == 'popup') { var className = setClass($.fn.easypin.defaults.popupOpacityLayer); return $(className, parentElement).size() > 0; } }; var createPopup = function(elem, markerContainer) { var parentElement = $(elem.target).closest('.pinParent'); var parentIndex = $(parentElement).attr('data-index'); var targetImage = $('.easypin-target', parentElement); var widthAttr = $.fn.easypin.defaults.widthAttribute; var heightAttr = $.fn.easypin.defaults.heightAttribute; // create modal base layer var opacityLayer = $('
') .addClass('popupOpacityLayer') .css({ 'width': '100%', 'height': '100%', 'background-color': 'black', 'position': 'absolute', 'opacity': '.0', 'z-index': 14 }); // append to parent container $(parentElement) .append(opacityLayer) // cross to child element .children(setClass($.fn.easypin.defaults.hoverClass)) .hide() // hover class hide // back to parent element .parent() // base layer animate .children(setClass($.fn.easypin.defaults.popupOpacityLayer)) .animate({ opacity: 0.4, }, 800); var width = parseInt($(parentElement).attr(widthAttr)); var height = parseInt($(parentElement).attr(heightAttr)); // create modal parent element var modalParent = $('
') .addClass('modalParent') .css({ 'width': '100%', 'height': '100%', 'position': 'absolute', 'z-index': 15 }) .click(function(e) { if($(e.target). is('div.modalParent')) { closePopup(parentElement); } e.stopPropagation(); }); // clonning modal content var modalContent = $('.easy-modal:last').clone(); // create modal body element var modalContext = $('
') .addClass('modalContext') .css({ 'background-color': '#fff', 'width': $.fn.easypin.defaults.modalWidth, 'opacity': '0', 'position': 'absolute', 'padding': '10px', '-webkit-box-shadow': '10px 13px 5px 0px rgba(0,0,0,0.75)', '-moz-box-shadow': '10px 13px 5px 0px rgba(0,0,0,0.75)', 'box-shadow': '10px 13px 5px 0px rgba(0,0,0,0.75)', '-webkit-border-radius': '5px', '-moz-border-radius': '5px', 'border-radius': '5px', 'cursor': 'move' }) .append($(modalContent).show()) .appendTo(modalParent); // modal body append to modal parent element // modal parent element append to parent element $('.popupOpacityLayer', parentElement).after(modalParent); var modalHeight = $('.modalContext').height() var modalWidth = $(modalContext).width(); var parentLeft = $(elem.target).closest(setClass($.fn.easypin.defaults.parentClass)).offset().left; var markerLeft = $(elem.target).offset().left; var clickPosLeft = (markerLeft - parentLeft); var parentTop = $(elem.target).closest(setClass($.fn.easypin.defaults.parentClass)).offset().top; var markerTop = $(elem.target).offset().top; var clickPosTop = (markerTop - parentTop); // modal position process if($(modalContent).attr('modal-position') == 'free') { // calculate free left position if((clickPosLeft-100) < modalWidth) { var modalLeftPosition = clickPosLeft+$(markerContainer).width()+50; }else{ var modalLeftPosition = clickPosLeft-modalWidth-100; } // calculate free top position if(modalHeight > height) { var modalTopPosition = parentTop; } else if((height-clickPosTop) < modalHeight) { var modalTopPosition = height - (modalHeight+100); }else{ var modalTopPosition = clickPosTop-(modalHeight/2); } }else{ var modalLeftPosition = (width/2)-(modalWidth/2)-10; var modalTopPosition = (height/2)-($(modalContext).height()/2)-10; } // modal body hide by position $(modalContext) .css('top', -(modalHeight+5)+'px') .css('left', modalLeftPosition+'px'); // without onhover action (close modal) keyBinder(27, function() { if(is_open('popup', parentElement)) { closePopup(parentElement); $(document.body).unbind('keydown'); } }); // on hover action (close modal) $(parentElement).hover(function() { if($(this).is(':hover') && is_open('popup', parentElement)) { keyBinder(27, function() { closePopup(parentElement); }); } },function() { $(document).unbind('keydown'); }); // animate modal body $(modalContext).animate( { 'top': modalTopPosition+'px', 'opacity': '1' }, { duration: 'slow', easing: 'easeOutElastic' } ) .bind('mousedown', function(e) { // if mouse down event is not either easy-modal, or modalContext // or modal form stop modal move if(! $(e.target).is('div.easy-modal') && !$(e.target).is('div.modalContext') && !$(e.target).is('form')) { e.stopPropagation(); return; } var pinParent = $(e.currentTarget).closest('.pinParent'); var downPageY = e.pageY-$(e.currentTarget).offset().top; var downPageX = e.pageX-$(e.currentTarget).offset().left; $(pinParent).bind('mousemove', function(e) { $(modalContext).css({ position: 'absolute', top: setPx((e.pageY-parentElement.offset().top)-downPageY), left: setPx((e.pageX-parentElement.offset().left)-downPageX) }); }); }) .bind('mouseup', function(e) { var pinParent = $(e.currentTarget).closest('.pinParent'); $(pinParent).unbind('mousemove'); }); $('.easy-submit', modalContext) .click(function() { var lat = $(markerContainer).attr($.fn.easypin.defaults.xAttribute); var long = $(markerContainer).attr($.fn.easypin.defaults.yAttribute); var ImgWidth = $(markerContainer).attr($.fn.easypin.defaults.widthAttribute); var ImgHeight = $(markerContainer).attr($.fn.easypin.defaults.heightAttribute); var markerIndex = $(markerContainer).attr('data-index'); var parentId = $('.easypin-target', parentElement).attr('easypin-id'); // check the form exists var formExists = $('form', modalContext).size() > 0; if(formExists) { var modalBody = $('form', modalContext); // form instance }else{ var modalBody = $('.easy-modal', modalContext); // current modal instance } // run callback function if(typeof($.fn.easypin.defaults.done) == 'function') { var result = $.fn.easypin.defaults.done(modalBody); if(typeof(result) == 'boolean') { if(result == true) { closePopup(parentElement); }else{ return; } }else{ closePopup(parentElement); } } var formData = getFormData(modalBody, function(data) { data['coords'] = new Object(); data.coords['lat'] = lat; data.coords['long'] = long; data.coords['canvas'] = new Object(); data.coords.canvas['src'] = $(targetImage).attr('src'); data.coords.canvas['width'] = ImgWidth; data.coords.canvas['height'] = ImgHeight; return data; }); dataInsert(parentId, markerIndex, formData); createPopover(markerContainer, formData); }); return modalContext; }; var dataInsert = function(parentId, markerIndex, data) { if(localStorage) { storageInsert(parentId, markerIndex, data); }else{ inputInsert(parentId, markerIndex, data); } }; // local storage var storageInsert = function(parentId, markerIndex, data) { var items = localStorage.getItem('easypin'); if(! items) { var items = new Object(); }else{ try { var items = JSON.parse(items); }catch(e) { var items = new Object(); } } var items = setNestedObject(parentId, markerIndex, items); if(typeof(items[parentId]['canvas']) == 'undefined') { items[parentId]['canvas'] = data.coords.canvas; } delete data.coords.canvas; if(typeof(data['canvas']) != 'undefined') { items[parentId]['canvas'] = data.canvas; delete data.canvas; } items[parentId][markerIndex] = data; localStorage.setItem('easypin', toJsonString(items)); }; // stores in hidden field var inputInsert = function(parentId, markerIndex, data) { var items = $('input[name="easypin-store"]').val(); if(! items) { var items = new Object(); }else{ try { var items = JSON.parse(decodeURIComponent(items)); }catch(e) { var items = new Object(); } } var items = setNestedObject(parentId, markerIndex, items); if(typeof(items[parentId]['canvas']) == 'undefined') { items[parentId]['canvas'] = data.coords.canvas; } delete data.coords.canvas; if(typeof(data['canvas']) != 'undefined') { items[parentId]['canvas'] = data.canvas; delete data.canvas; } items[parentId][markerIndex] = data; if($('input[name="easypin-store"]').size() < 1) { $(setClass($.fn.easypin.defaults.parentClass)+':first').before(''); }else{ $('input[name="easypin-store"]').val(encodeURIComponent(toJsonString(items))); } }; var dataUpdate = function(parentId, markerIndex, data) { if(localStorage) { storageUpdate(parentId, markerIndex, data); }else{ inputUpdate(parentId, markerIndex, data); } }; var storageUpdate = function(parentId, markerIndex, data) { var items = localStorage.getItem('easypin'); if(items) { try { var items = JSON.parse(items); items = setNestedObject(parentId, markerIndex, items); items[parentId][markerIndex] = merge(items[parentId][markerIndex], data); localStorage.setItem('easypin', toJsonString(items)); }catch(e) { return false; } } }; var inputUpdate = function(parentId, markerIndex, data) { var items = $('input[name="easypin-store"]').val(); if(items) { try { var items = JSON.parse(decodeURIComponent(items)); items = setNestedObject(parentId, markerIndex, items); items[parentId][markerIndex] = merge(items[parentId][markerIndex], data); $('input[name="easypin-store"]').val(encodeURIComponent(toJsonString(items))); }catch(e) { return false; } } }; /** * Remove data container function * * @param parentIndex int * @param markerIndex int */ var dataRemove = function(parentId, markerIndex) { if(localStorage) { removeFromStorage(parentId, markerIndex); }else{ removeFromInput(parentId, markerIndex); } }; /** * Remove data on input hidden field * @param {[int]} parentIndex [parent container index] * @param {[int]} markerIndex [marker container index] * @return {[void]} */ var removeFromInput = function(parentId, markerIndex) { var items = $('input[name="easypin-store"]').val(); if(items) { try { var items = JSON.parse(decodeURIComponent(items)); items = removeHelper(parentId, markerIndex, items) var totalPin = $('input[name="easypin-store"]').size(); if(totalPin < 1) { $(setClass($.fn.easypin.defaults.parentClass)+':first').before(''); }else{ $('input[name="easypin-store"]').val(encodeURIComponent(toJsonString(items))); } } catch (e) {} } }; /** * Remove from local storage * * @param parentIndex int * @param markerIndex int * @return void */ var removeFromStorage = function(parentId, markerIndex) { var items = localStorage.getItem('easypin'); if(items) { try { var items = JSON.parse(items); localStorage.setItem('easypin', toJsonString(removeHelper(parentId, markerIndex, items))); }catch(e) {} } }; /** * Remove process from data object * * @param {[int]} parentIndex [parent container index] * @param {[int]} markerIndex [marker container index] * @param {[object]} items [data object] * @return {[object]} */ var removeHelper = function(parentId, markerIndex, items) { if(parentId && !markerIndex) { if(typeof(items[parentId]) != 'undefined') { delete items[parentId]; } } if(parentId && markerIndex) { if(typeof(items[parentId][markerIndex]) != 'undefined') { delete items[parentId][markerIndex]; if(sizeof(items[parentId]) < 1 || (sizeof(items[parentId]) == 1 && typeof(items[parentId]['canvas']) != 'undefined')) { delete items[parentId]; } } } return items; }; // set nested object /** * Set nested object * @param int parentIndex parent container index * @param int markerIndex marker ocntainer index * @param int items data object * @return object */ var setNestedObject = function(parentId, markerIndex, items) { if(typeof(items[parentId]) == 'undefined') { items[parentId] = new Object(); } if(typeof(items[parentId][markerIndex]) == 'undefined') { items[parentId][markerIndex] = new Object(); } return items; }; var getItem = function(parentIndex, markerIndex) { if(localStorage) { var items = localStorage.getItem('easypin'); try { items = JSON.parse(items); } catch (e) { items = {}; } }else{ var items = $('input[name="easypin-store"]').val(); try { items = JSON.parse(decodeURIComponent(items)); } catch (e) { items = {}; } } try { return items[parentIndex][markerIndex]; } catch (e) { return null; } }; // get values of current modal var getFormData = function(element, callback) { var elements = new Object(); $('input, select, textarea', element).each(function() { var elementType = $(this).attr('type'); var elementName = $(this).attr('name'); if(elementType == 'radio') { var checked = $(this).filter(":checked").val(); if(typeof(checked) != 'undefined' && typeof(elements[elementName]) == 'undefined') { elements[elementName] = checked; } } else if(elementType == 'checkbox') { if($(this).is(':checked')) { elements[elementName] = $(this).val(); } } else{ if(typeof($(this).val()) != 'undefined') { elements[elementName] = $(this).val(); } } }); if(typeof(callback) == 'function') { var args = new Array(); args.push(elements); return callback.apply(null, args); } return elements; }; var toJsonString = function(data) { return JSON.stringify(data); }; // modal close var closePopup = function(parentElement) { // close opacity layer $(setClass($.fn.easypin.defaults.popupOpacityLayer), parentElement) .animate( { opacity:0 }, 'fast', // how fast we are animating 'swing', // the type of easing function() { $(this).remove(); } ); var modalHeight = $('.modalContext', parentElement).height(); var modalWidth = $('.modalContext', parentElement).width(); // animate modal body $('.modalContext', parentElement).animate( { 'top': -(modalHeight+50)+'px', 'opacity': '0', 'z-index': 0 }, { duration: 'slow', easing: 'easeOutElastic', complete: function() { $('.modalParent', parentElement).remove(); } } ); }; var keyBinder = function(expectCode, callback) { $(document).bind('keydown', function(e) { if(e.which == expectCode) { callback.apply(null); $(this).unbind('keydown'); } }); }; var setIndex = function(selector, parent) { var index = $(selector+':last', parent).attr('data-index'); if(typeof(index) == 'undefined') { return '0'; } return parseInt(index)+1; }; var sizeof = function(data) { if(typeof(data) == 'object') { var j = 0; for(var i in data) { j++; } return j; } }; /** * Set to input fields all data * @param int parentIndex parent container index * @param int markerIndex marker container index * @param object modalInstance current modal instance * @return void */ var setDataToFields = function(parentId, markerIndex, modalInstance) { var item = getItem(parentId, markerIndex); if(typeof(item) == 'object') { for(var i in item) { var element = $('[name="'+i+'"]', modalInstance); var type = $(element).prop('type'); if(type == 'text' || type == 'hidden') { $(element).attr('value', item[i]); } else if(type == 'checkbox') { $(element).attr('checked', true); } else if(type == 'radio') { $('[value="'+item[i]+'"]', modalInstance).attr('checked', true); } else if(type == 'textarea') { $(element).val(item[i]); } else if(type == 'select-one') { $(element).val(item[i]).prop('selected', true); } } } }; var createPopover = function(markerContainer, formData) { // check and set popover callbacks var popoverCallBacks = sizeof($.fn.easypin.defaults.popover) > 0 ? $.fn.easypin.defaults.popover : false; var arrow = $('
') .addClass('popover-arrow-down') .css({ width: 0, height: 0, 'border-left': '10px solid transparent', 'border-right': '10px solid transparent', 'border-top': '10px solid #000', 'opacity': '.8', position: 'absolute', bottom: '-50px', left: '58px' }); var tooltipContainer = $('
') .addClass('popover') .css('position', 'absolute') .css('display','inline') .css('top', '-51px') .css('left', '-51px') .css('opacity', '0'); var popoverHtml = $('[popover]:last').clone(); popoverHtml.removeAttr('popover'); var toHtml = popoverHtml.html(); var popoverUserWidth = popoverHtml.attr('width') ? popoverHtml.attr('width') : 150; var defaultStyle = { color: '#FFFFFF', background: '#000000', opacity: '.8', height: 'auto', 'line-height': '40px', 'border-radius': '5px', cursor: 'context-menu' }; if(typeof($.fn.easypin.defaults.popoverStyle) == 'object') { delete $.fn.easypin.defaults.popoverStyle.width; delete $.fn.easypin.defaults.popoverStyle.position; popoverStyle = merge(defaultStyle, $.fn.easypin.defaults.popoverStyle); }else{ popoverStyle = defaultStyle; } var span = $('') .addClass('popoverContent') .css({ position: 'absolute', width:setPx(popoverUserWidth) }) .css(popoverStyle); var bgColor = $(span).css('background-color'); $(arrow).css('border-top-color', bgColor); if(popoverHtml.attr('shadow') == 'true') { $(span) .css({ '-webkit-box-shadow': '10px 13px 5px 0px rgba(0,0,0,0.75)', '-moz-box-shadow': '10px 13px 5px 0px rgba(0,0,0,0.75)', 'box-shadow': '10px 13px 5px 0px rgba(0,0,0,0.75)' }); } // delete canvas parameters delete formData['canvas']; for(var i in formData) { // callback check and run if(popoverCallBacks && typeof(popoverCallBacks[i]) == 'function') { var args = new Array(); args.push(formData[i]); formData[i] = popoverCallBacks[i].apply(null, args); } var pattern = RegExp("\\{\\["+i+"\\]\\}", "g"); toHtml = toHtml.replace(pattern,formData[i]); } $(span).append(toHtml); $(tooltipContainer).append(span).append(arrow); // remove previous popover if($('div.popover', markerContainer).size() > 0) { // animate modal body $('div.popover', markerContainer).animate( { 'top': '-'+setPx((popoverHeight-15)), 'opacity': '0' }, { duration: 'slow', easing: 'easeOutElastic', complete: function() { $(this).remove(); } } ); } // popover element apend yo marker container $(markerContainer).prepend(tooltipContainer); var popoverEl = $('div.popover > span:first', markerContainer); var popoverHeight = popoverEl.height(); var popoverWidth = popoverEl.width(); // set popover container position $(tooltipContainer) .css('top', '-'+setPx((popoverHeight+12)-10)) .css('left', '-'+setPx((popoverWidth/2)-16)); // set arrow position $(arrow, tooltipContainer) .css('left', setPx((popoverWidth/2)-10)) .css('top', setPx(popoverHeight)); // animate modal body $(tooltipContainer).animate( { 'top': '-'+setPx((popoverHeight+12)), 'opacity': '1' }, { duration: 'slow', easing: 'easeOutElastic' } ); }; var initPin = function(imgIndex, element) { var initData = $.fn.easypin.defaults.init; var config = getConfigs(); if(typeof(initData) == 'string') { initData = JSON.parse(initData) } var parentElement = $(element).parents(setClass(config.parentClass)); if(sizeof(initData) > 0 && $(element).attr('easypin-init') != 'false') { // canvas border width var dashWidth = $.fn.easypin.defaults.dashWidth; // get x, y balance value var posYBalance = config.posYBalance; var posXBalance = config.posXBalance; // get current target image instance var targetImage = $('img'+setClass('easypin-target'), parentElement); if(typeof(initData[imgIndex]) == 'undefined') { return; } for(var i in initData[imgIndex]) { if(isNaN(i) === true) return; var imageWidth = parseInt(initData[imgIndex].canvas.width); var imageHeight = parseInt(initData[imgIndex].canvas.height); var lat = parseInt(initData[imgIndex][i].coords.lat); var long = parseInt(initData[imgIndex][i].coords.long); // set cursor position coordinate var imagePositionY = targetImage.offset().top - (config.dashWidth-posYBalance); var imagePositionX = targetImage.offset().left - (config.dashWidth-posXBalance); var clickPosX = lat;//(lat-imagePositionX); var clickPosY = long;//(long-imagePositionY); // get marker half size (width/height) var markerWidthHalf = (config.markerWidth/2); var markerHeightHalf = (config.markerHeight/2); // set canvas border position var markerBorderX = clickPosX-(config.markerWidth/2); var markerBorderY = clickPosY-(config.markerHeight/2); if(markerBorderX < 0) { markerBorderX = 0; } else if(clickPosX+markerWidthHalf > imageWidth) { markerBorderX = imageWidth-config.markerWidth; } if(markerBorderY < 0) { markerBorderY = 0; } else if(clickPosY+markerHeightHalf > imageHeight) { markerBorderY = imageHeight-config.markerHeight; } var absX = markerBorderX.toFixed(3)-markerWidthHalf; var absY = markerBorderY.toFixed(3)-markerHeightHalf; // create tool var tools = createTools({ markerWidth: config.markerWidth, markerHeight: config.markerHeight }); // create marker container var markerContainer = createMarker({ tools: tools, parentElement: parentElement, markerClass: config.markerClass, markerBorderX: markerBorderX, markerBorderY: markerBorderY, marginLeft: (config.markerWidth/2), marginTop: -(config.markerHeight/2), markerWidth: config.markerWidth, markerHeight: config.markerHeight, markerContainerZindex: config.markerContainerZindex, absX: absX, absY: absY, imageWidth: imageWidth, imageHeight: imageHeight, parentElement: parentElement, src: config.src }); var markerIndex = $(markerContainer).attr('data-index'); var markerData = merge(initData[imgIndex][i], {'canvas': initData[imgIndex]['canvas']}); dataInsert(imgIndex, markerIndex, markerData); createPopover(markerContainer, initData[imgIndex][i]); } } }; var createRandomId = function() { var length = 10; var strings = 'abcdefghijklmnoprstuvyzABCDEFGHIJKLMNOPRSTUVYZ1234567890_'; var key = ''; for(var i = 1; i<=length; i++) { var randNum = Math.floor(Math.random() * (strings.length - 1 + 1) + 1); if(typeof(strings[randNum]) != 'undefined') { key += strings[randNum]; } } return key; }; var getConfigs = function() { return { src: $.fn.easypin.defaults.markerSrc, markerWidth: $.fn.easypin.defaults.markerWidth, markerHeight: $.fn.easypin.defaults.markerHeight == 'auto' ? $.fn.easypin.defaults.markerWidth : $.fn.easypin.defaults.markerHeight, markerClass: $.fn.easypin.defaults.markerClass, parentClass: $.fn.easypin.defaults.parentClass, markerContainerZindex: $.fn.easypin.defaults.markerContainerZindex, // canvas border width dashWidth: $.fn.easypin.defaults.dashWidth, // get x, y balance value posYBalance: $.fn.easypin.defaults.posYBalance, posXBalance: $.fn.easypin.defaults.posXBalance, // canvas border width dashWidth: $.fn.easypin.defaults.dashWidth }; }; /* * Recursively merge properties of two objects */ function merge(obj1, obj2) { for (var p in obj2) { try { obj1[p] = obj2[p]; } catch(e) { // Property in destination object not set; create it and set its value. obj1[p] = obj2[p]; } } return obj1; } }(jQuery));