|
Ver. 4.2.1 - the old example got obsolete and has been replaced with a tutorial for developers of how to add "tags" to the images and let the users add title and description. Click somewhere on the image. A hotspot is created. You can drag & drop it to adjust the position. Click on the hotspot to add / read title and description. You can right click on the hotspot to remove it (can be disabled). |
HTML output console |
|
Loading, please wait...
|
>> HTML console
|
This snippet is not ment to represent a final or complete solution! So the output of tagging is displayed in the "HTML console". Normally you would be saving the resulted JSON to a file, database, push to another persons display or perform some other fancy stuff with it. For example in a Digital-Asset-Management system the marketing manager could assign a task to edit the image in a certain way... If you need cross-origin communication please google for "window.postMessage";
As mentioned above this AJAX-ZOOM snippet is for developers who would like to extend and finish this code customizing it for their own needs. If you have a distinguished idea but do not want or do not have time to complete, you can ask AJAX-ZOOM team for a quote to do it for you.
<!-- Include jQuery core into head section if not already present --> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script> <!-- JSON --> <script type="text/javascript" src="../axZm/plugins/JSON/jquery.json-2.3.min.js"></script> <!-- AJAX-ZOOM javascript --> <script type="text/javascript" src="../axZm/jquery.axZm.js"></script> <link type="text/css" href="../axZm/axZm.css" rel="stylesheet" />
/* cursor in tagging mode */
.azTagging {cursor: crosshair !important;}
/* title textfield */
.azTextField {width: 100%; margin-bottom: 5px; box-sizing: border-box !important; padding: 5px; font-family: arial; font-size: 12px; border: 1px solid #999999; border-radius: 3px;}
/* description textarea */
.azTextArea {width: 100%; height: 100px; box-sizing: border-box !important; padding: 5px; background-color: #FFF; font-family: arial; font-size: 12px; border: 1px solid #999999; border-radius: 3px;}
/* save, delete button */
.azButton {margin-top: 5px;}
/* message about click to place a hotspot */
.azTaggingMsg {position: absolute; background-color: #B50904; width: 290px; margin-left: -140px; top: -1px; left: 50%; border: #000 1px solid; border-bottom-right-radius: 5px; border-bottom-left-radius: 5px; font-size: 11px; color: #FFF; padding: 1px 5px 1px 5px; z-index: 1; pointer-events: none;}
/* parent container for navigation (mNavi) */
.azCustomNavi{background-color: #AAA; box-sizing: border-box; height: 58px; padding: 4px 0px 4px 0px; border-left: #000 1px solid; border-bottom: #000 1px solid; border-right: #000 1px solid;}
/* html console */
.azTaggingResults{background-color: #101010; color: #3cc628;padding: 5px; margin-top: 7px; height: 200px; overflow: hidden; overflow-y: auto;}
.azPre{tab-size: 2; -moz-tab-size: 2; margin: 0; font-size: 11px; font-family: monospace;}
/* Overwrite css from /axZm/jquery.axZm.js */
.axZmToolTipInner {background-color: #c5d8e1; border-color: #5583b4; border-width: 3px;}
.axZmToolTipTitle {color: #FFF; /* #1a4a7a*/ font-size: 16px; line-height: 18px; min-height: 24px; text-shadow: 0px 0px 2px rgba(150, 150, 150, 0.75);}
.axZm_zoomCustomNaviParentID {margin: 0 auto;}
azTaggingResults shows JSON created after placing the hotspots, so it is not needed in your final code
<!-- Div where AJAX-ZOOM is loaded into --> <div id="azParentContainer" style="min-height: 462px;">Loading, please wait...</div> <!-- Parent container for "mNavi" --> <div id="azCustomNavi" class="azCustomNavi"></div> <!-- "console" to display results which could / should be saved --> <div id="azTaggingResults" class="azTaggingResults">>> HTML console</div>
Every line is commented... The whole code could be ofcourse wraped into external JS. If after all you have any questions regarding AJAX-ZOOM - do not hesitate to contact us please.
// some var to hold everyting else
window.ajaxZoom = {};
// the ID of the div element where ajax-zoom has to be inserted into
ajaxZoom.divID = "azParentContainer";
// path to the axZm folder
ajaxZoom.path = "../axZm/";
// zoomData - defines which image should be loaded
ajaxZoom.parameter = "zoomData=/pic/zoom/furniture/furniture_005.jpg";
// example - defines which "options set" is taken
// options in /axZm/zoomConfig.inc.php are overriden in /axZm/zoomConfigCustom.inc.php
// after elseif ($_GET['example'] == 'tagging'){
ajaxZoom.parameter += "&example=tagging";
// Local time ahead server time (PHP solution), not needed, you can set timeDiff to 0
ajaxZoom.timeDiff = (new Date()).getTime() - <?php echo round(microtime(true)*1000) ?>;
// switch for tagging mode
ajaxZoom.taggingMode = true;
// some var where we will store user defined title and description
ajaxZoom.myTags = {};
// define some text strings
ajaxZoom.taggingTxt = {
"msg": "» click somewhere to place a hotspot; right-click to remove",
"disable": "Disable tagging mode",
"enable": "Enable tagging mode",
"title": "Title",
"description": "Description",
"confirmDelete": "Really delete?",
"notes": "Notes",
"noDescr": "No notes left"
};
// add tagging message and manage other tasks when tagging mode is enabled
ajaxZoom.setTaggingMsg = function(){
// append tagging message
if (!$("#azTaggingMsg").length){
$("<div />").attr("id", "azTaggingMsg").addClass("azTaggingMsg")
.html(ajaxZoom.taggingTxt.msg)
.appendTo("#axZm_zoomLayer");
}else{
$("#azTaggingMsg").css("display", "block");
}
// change cursor
$("#axZm_zoomLayerImg").addClass("azTagging");
// change src and description of the custom button which changes states
$("#customBtn_mCustomBtn1")
.data("btnSrc", $.axZm.icon + $.axZm.buttonSet + "/button_iPad_tag_switched.png")
.data("bAlt", ajaxZoom.taggingTxt.disable)
.attr("src", $.axZm.icon + $.axZm.buttonSet + "/button_iPad_tag_switched.png");
// make hotspots draggable
$.fn.axZm.hotspotsDraggable();
};
// hide tagging message and manage other tasks when tagging mode is disabled
ajaxZoom.removeTaggingMsg = function(){
// hide tagging message
$("#azTaggingMsg").css("display", "none");
// remove class which changed the cursor
$("#axZm_zoomLayerImg").removeClass("azTagging");
// change src and description of the custom button which changes states
$("#customBtn_mCustomBtn1")
.data("btnSrc", $.axZm.icon + $.axZm.buttonSet + "/button_iPad_tag.png")
.data("bAlt", ajaxZoom.taggingTxt.enable)
.attr("src", $.axZm.icon + $.axZm.buttonSet + "/button_iPad_tag.png");
// make hotspots not traggable any more
$.fn.axZm.hotspotsDraggable(true);
// close all opened tooltips
$.fn.axZm.removeAllToolTips();
};
// function which will be executed in onZoomInClickStart AJAX-ZOOM callback
// important is that when you do not want AJAX-ZOOM to zoom, this function should return false;
ajaxZoom.evaluateClick = function(info){
// do not do anything if ajaxZoom.taggingMode (a switch var) is false
if (!ajaxZoom.taggingMode){return;}
// position of the click related to its original size
var xClick = info.viewport.x;
var yClick = info.viewport.y;
// file (image) name
var currentImageName = $.axZm.zoomGA[$.axZm.zoomID].img;
// position of the hotspot in this file
// there could be same hotspot on an image in the gallery,
// this is why we need image name here and it is defined this way
var posObj = {}; posObj[currentImageName] = {left: xClick, top: yClick};
// show hotspots in case they are disabled
$.fn.axZm.showHotspotLayer();
// after we know the position create hotspot "on-the-fly"
$.fn.axZm.createNewHotspot({
// generate some image name
// image name could contain the creation date and time
// you might also want to get it from server before creating hotspot
// or calculate the difference between server time and client time as it is done here
// prepend the hotspot name with some random string anyway
name: Math.random().toString(36).substring(2) + "_" + ((new Date()).getTime() - ajaxZoom.timeDiff),
autoTitle: false, // we do not want alt title to be like hotspot name
autoPos: false, // we know at wich positions to put hotspot at (posObj)
noInit: false, // we want that the hotspot is added right away
draggable: true, // and it should be draggable
noRightClickRemove: false, // could be removed with right mouse click
posObj: posObj, // our coordinates
settings: {
shape: "point", // shape is point (not rect)
altTitle: false, // mouseover title is disabled
labelGravity: "bottom", // position of the title shown as label
labelOffsetY: -2, // vertical offset of the label
hotspotImage: "hotspot64_map_red.png", // default image from /axZm/icons
gravity: "top", // important - display hotspot image above the click point
width: 32, // width of the icon
height: 32, // height of the icon
/* tooltip settings */
toolTipWidth: 300, // width of the tooltip
toolTipHeight: 120, // min-height of the tooltip
toolTipGravity: "left", // show tooltip left to the hotspot
toolTipAutoFlip: true, // but also right depending on position
toolTipAdjustX: 25, // horizontal space between hotspot and toolTip
toolTipCloseIcon: "close_blue_16.png", // close button icon from /axZm/icons
toolTipCloseIconOffset: {right: 5, top: 5}, // position of the close button
toolTipOverlayShow: false, // do not show overlayes
// toolTipTitle can be also a JS function
toolTipTitle: ajaxZoom.toolTipTitle,
// toolTipHtml can be also a JS function which returns something
toolTipHtml: ajaxZoom.toolTipHtml
},
// callback after hotspot is added
callback: function(info){
// we have created the hotspot so update ajaxZoom.myTags
ajaxZoom.myTags[info.name] = {};
// save / update console
ajaxZoom.updateConsole();
// trigger tooltip after it has been added
// if you remove the line below the user would need to extra click on the hotspot
$("#axZmHotspot_"+info.name).trigger("click");
}
});
// important to return false; otherwise AJAX-ZOOM will zoom
return false;
};
// delete hotspot wrapper
ajaxZoom.deleteHotspot = function(name){
// ask if the user wants to delete the hotspot
var sureDelete = window.confirm(ajaxZoom.taggingTxt.confirmDelete);
if (sureDelete){
// delete hotspot
$.fn.axZm.deleteHotspot(name);
// close all tooltips
$.fn.axZm.removeAllToolTips();
}
};
// title which is shown in the popup when the user clicks on the hotspot
ajaxZoom.toolTipTitle = function(info){
// we simply return a string but it could be extended
return ajaxZoom.taggingTxt.notes;
};
// html which is shown in the popup when the user clicks on the hotspot
ajaxZoom.toolTipHtml = function(info){
// get already prsent information sored in ajaxZoom.myTags
var myTags = ajaxZoom.myTags[info.name] || {},
ret = ""; // empty string
// if tagging mode return form fields
if (ajaxZoom.taggingMode){
// simple form
ret = "<div>";
// title
ret += "<input type=\"text\" id=\"azTextField\" class=\"azTextField\" value=\"" + (info.labelTitle || ajaxZoom.taggingTxt.title) + "\">";
// description
ret += "<textarea id=\"azTextArea\" class=\"azTextArea\">" + (myTags.descr || ajaxZoom.taggingTxt.description) + "</textarea>";
// name of the hotspot
ret += "<input id=\"azTextHotspotName\" type=\"hidden\" value=\"" +info.name + "\">";
// save and delete buttons
ret += "<div style=\"text-align: right\">";
ret += "<input type=\"button\" class=\"azButton\" value=\"Delete\" onclick=\"ajaxZoom.deleteHotspot('" + info.name + "');\">";
ret += "<input type=\"button\" class=\"azButton\" value=\"Save\" onclick=\"ajaxZoom.saveInfo()\">";
ret += "</div>";
ret += "</div>";
setTimeout(function(){
// Prevent bubbling when clicked on the textarea
$("#azTextArea, #azTextField").on("mousedown touchstart", function(e){e.stopPropagation();});
// Show names of the form fields within formfields and remove them on focus
$("#azTextField").on("focus", function(e){
if ($(this).val() == ajaxZoom.taggingTxt.title){$(this).val("");}
}).on("blur", function(e){
if ($(this).val() == ""){$(this).val(ajaxZoom.taggingTxt.title);}
});
$("#azTextArea").on("focus", function(e){
if ($(this).val() == ajaxZoom.taggingTxt.description){$(this).val("");}
}).on("blur", function(e){
if ($(this).val() == ""){$(this).val(ajaxZoom.taggingTxt.description);}
});
}, 100);
}
// return when tagging mode is disabled
else {
// Calculate time from hotspot name (if not changed)
var time = info.name.split("_")[1];
// Date from Unix timestamp
var date = new Date(parseInt(time));
// Return what is stored in ajaxZoom.myTags.descr
ret = "<div class='azTextArea'>"+(myTags.descr || ajaxZoom.taggingTxt.noDescr)+"</div>\
Created: " + date + "\
";
}
return ret;
};
// store description and title under ajaxZoom.myTags
ajaxZoom.saveInfo = function(){
// read values from formfields
var name = $("#azTextHotspotName").val();
var title = $("#azTextField").val();
var descr = $("#azTextArea").val();
// calculate date and time
var time = (new Date()).getTime() - ajaxZoom.timeDiff;
// create new / emtty object under ajaxZoom.myTags
ajaxZoom.myTags[name] = {};
// store description from formfields
if (descr != ajaxZoom.taggingTxt.description){
ajaxZoom.myTags[name].descr = descr;
ajaxZoom.myTags[name].lastChanged = time;
}
// store title from formfields
if (title != ajaxZoom.taggingTxt.title){
ajaxZoom.myTags[name].title = title;
ajaxZoom.myTags[name].lastChanged = time;
// Update label title
$.axZm.hotspots[name].labelTitle = title;
// Redraw hotspot with $.fn.axZm.initHotspots and make them draggable again
$.fn.axZm.initHotspots(null, $.fn.axZm.hotspotsDraggable);
}
// close tooltip
$.fn.axZm.removeAllToolTips();
// save / update console
ajaxZoom.updateConsole();
};
// save / update console function
// this is the function which you would need to extend
ajaxZoom.updateConsole = function(){
var json = {};
// eterate over ajaxZoom.myTags and gather information you would like to store
$.each(ajaxZoom.myTags, function(name, obj){
if ($.axZm.hotspots[name]
&& $.axZm.hotspots[name]["position"]
&& !$.isEmptyObject($.axZm.hotspots[name]["position"])){
json[name] = {
title: obj.title,
descr: obj.descr,
timestamp: obj.lastChanged,
position: $.fn.axZm.convertHotspotPositionToPx($.axZm.hotspots[name]["position"][$.axZm.zoomID])
};
}
});
// here we simply visually show that created json without any other action
$("#azTaggingResults").html("<pre class='azPre'>" + JSON.stringify(json, null, "\t")+"</pre>");
// todo: for example save it to session and restore when loaded
};
// AJAX-ZOOM callbacks
ajaxZoom.opt = {
// some (not all) options from /axZm/zoomConfig.inc.php and
// from /axZm/zoomConfigCustom.inc.php
// could be set in this "onBeforeStart" callback
onBeforeStart: function(){
// Remove hotspot entirely when right clicked on it
// normally it is only disabled on the current image
$.axZm.hsRightClickDel = true;
// Do not zoom out at 100% on click
$.axZm.zoomOutClick = false;
// enable and configure the "mNavi" option
// which is the toolbar below the player or in the player
$.axZm.mNavi.enabled = true; // enable it
$.axZm.mNavi.parentID = "azCustomNavi"; // set ID where it has to be appended to
$.axZm.mNavi.buttonDescr = true; // enable description of the buttons
$.axZm.mNavi.alt.enabled = false; // disable description simmilar to alt
$.axZm.mNavi.fullScreenShow = true; // also show "mNavi" at fullscreen mode
$.axZm.mNavi.offsetVertFS = 10; // vertical offset of mNavi at fullscreen mode
// this is a list of buttons which we want to show in the toolbar
// number value is margin to the next button
$.axZm.mNavi.order = {
mZoomOut: 5, // zoom out button
mZoomIn: 20, // zoom in button
mReset: 20, // reset button
mPan: 5, // pan mode button
mCrop: 20, // crop mode button
mHotspots: 5, // show / hide hotspots button
mCustomBtn1: 0 // our "whatever" button
};
// there can be as many "whatever" (custom) buttons as you want
// call them mCustomBtn1, mCustomBtn2, ...
// now we define how this mCustomBtn1 should look like
$.axZm.icons.mCustomBtn1 = {file: $.axZm.buttonSet + "/button_iPad_tag", ext: "png", w: 50, h: 50};
// and the title of mCustomBtn1
$.axZm.mapButTitle.customBtn1 = ajaxZoom.taggingTxt.disable; // description of the button
// attach a JS function to the mCustomBtn1
$.axZm.mNavi.mCustomBtn1 = function(){
// when tagging mode is already on, disable it
if (ajaxZoom.taggingMode == true){
// Update state of the tagging mode
ajaxZoom.taggingMode = false;
// Do other things
ajaxZoom.removeTaggingMsg();
}
// enable tagging mode
else {
// Update state of the tagging mode
ajaxZoom.taggingMode = true;
// Do other things
ajaxZoom.setTaggingMsg();
}
};
},
// when image loads
onLoad: function(){
// Add message that tagging mode is activated and activate it
ajaxZoom.setTaggingMsg();
},
// callback executed on any hotspot deletion over API
onHotspotDelete: function(name){
// Save / update console
ajaxZoom.updateConsole();
},
// callback triggered after hotspot is moved
onHotspotsDragEnd: function(){
// Save / update console
ajaxZoom.updateConsole();
},
// callback triggered when the user clicks on the image
onZoomInClickStart: function(info){
return ajaxZoom.evaluateClick(info);
}
};
// load AJAX-ZOOM not responsive
$(document).ready(function(){
$.fn.axZm.load({
opt: ajaxZoom.opt,
path: ajaxZoom.path,
parameter: ajaxZoom.parameter,
divID: ajaxZoom.divID
});
});
// open AJAX-ZOOM responsive
// Documentation - http://www.ajax-zoom.com/index.php?cid=docs#api_openFullScreen
/*
$.fn.axZm.openFullScreen(
ajaxZoom.path, // Absolute path to AJAX-ZOOM directory, e.g. "/axZm/"
ajaxZoom.parameter, // Defines which images and which options set to load
ajaxZoom.opt, // callbacks
ajaxZoom.divID, // target - container ID (default "window" - fullscreen)
false, // apiFullscreen- use browser fullscreen mode if available
true, // disableEsc - prevent closing with Esc key
false // postMode - use POST instead of GET
);
*/