« MediaWiki:Common.js » : différence entre les versions

De Inkipédia
(Page créée avec « // ================================================================================ // Countdowns // =====================================================================... »)
 
m (1 version importée : css/js)
 
(5 versions intermédiaires par 2 utilisateurs non affichées)
Ligne 1 : Ligne 1 :
/* Any JavaScript here will be loaded for all users on every page load. */
/* MediaWiki:All.js */
mw.loader.load('//splatoonwiki.org/wiki/MediaWiki:All.js?action=raw&ctype=text/javascript');
///////////////////////////////////////////////////////////////////////////////
//                              Gobbler Class                              //
///////////////////////////////////////////////////////////////////////////////
// Folds all infoboxes into a single, tabbed element
// Object constructor
var Gobbler = function() {
    // Check for a gobbler object in the document
    this.element = document.getElementById("gobbler");
    if (!this.element) return;
    // Gobble all of the infoboxes on the page
    var elements = document.getElementsByClassName("tagInfobox");
    var tabs    = [];
    for (var x = 0; x < elements.length; x++)
        tabs.push(this.gobble(elements[x]));
   
    // 1-index for user niceness; 1 is "first"
    var initial = parseInt(this.element.dataset.initial) - 1;
    if (isNaN(initial) || initial < 0 || initial >= tabs.length)
        initial = tabs.length - 1; // default last tab
    this.onTab(tabs[initial]);
    // Add the infoboxes and tab buttons to the gobbler element
    var tabStrip = document.createElement("div");
    tabStrip.style.textAlign = "left";
    if (tabs.length > 1)
        this.element.appendChild(tabStrip);
    for (var x = 0; x < tabs.length; x++) {
        tabStrip.appendChild(tabs[x].button);
        this.element.appendChild(tabs[x].element);
    }
};
// Process an infobox
Gobbler.prototype.gobble = function(element) {
    // Retrieve the infobox's hidden tab button element
    var button = element.getElementsByClassName("tagTab")[0];
    // Create a tab object using the contents of the element
    var tab = {
        button:        button,
        colorInactive: button.style.backgroundColor,
        colorActive:  button.style.outlineColor,
        element:      element,
        cssText:      element.style.cssText
    };
    // Configure element properties
    var that = this;
    element.style.cssText = "width: 100%; height: 100%; display: none;";
    button.style.display  = "inline";
    button.onmousedown    = function(e) {
        if (e.button == 0) that.onTab(tab);
    };
    return tab;
};
// Event handler for when a tab is selected/clicked
Gobbler.prototype.onTab = function(tab) {
// Error checking when new gobbler is present
if (!tab)
return;
    // Deselect the previous infobox
    if (this.current) {
        this.current.button.style.fontWeight      = "normal";
        this.current.button.style.backgroundColor = this.current.colorInactive;
        this.current.element.style.display        = "none";
    }
    // Select the new infobox
    this.element.style.cssText      = tab.cssText;
    tab.button.style.fontWeight      = "bold";
    tab.element.style.display        = "inline";
    tab.button.style.backgroundColor = tab.colorActive;
    this.current                    = tab;
};
$( document ).ready(function() {
    new Gobbler();
    // 3D/2D viewer
    $('.switch-to-3d').click(function() {
    $('.container-2d').hide();
    $('.viewer-3d, .viewer-3d-multi, .buttons-container-3d').show();
    });
    $('.switch-to-2d').click(function() {
    $('.viewer-3d, .viewer-3d-multi, .buttons-container-3d').hide();
    $('.container-2d').show();
    });
});
// ================================================================================
// Warning message in console to help prevent scams
// ================================================================================
setTimeout(function () {
    console.log("%cHold up squiddo!",
        "color:rgb(170, 220, 0);font-family:Splatoon2, sans-serif;font-size:40px")
    console.log("%cIf someone told you to paste something here, there is a 99% chance you're getting scammed!",
        "font-family:Splatoon2, sans-serif;font-size:15px")
    console.log("%cPasting anything here could give scammers access to your account",
        "color:red;font-family:Splatoon2, sans-serif;font-size:15px")
    console.log("%cUnless you know what you're doing, don't paste anything here!",
        "font-family:Splatoon2, sans-serif;font-size:25px")
}, 1500)


// ================================================================================
// ================================================================================
// Countdowns
// 3D/2D Viewer
// ================================================================================
// ================================================================================
// Credits go to AbelToy, Guy Perfect, Espyo for the countdown code.


// List of countdowns on the current page
// 3D model viewer
var countdowns = [];
var viewer3d = {
dragging: null,
draggingFrameX: 0,
draggingFrameY: 0,
viewers: [],
frameThresholdX: 10,
frameThresholdY: 128,
realMod: function(x, y) {
return ((x % y) + y) % y;
},
init: function() {
$('.viewer-3d').each(viewer3d.bind);
$(document).mouseup(viewer3d.release);
$(document).mousemove(viewer3d.move);
},
bind: function() {
var v = $(this);
var num = viewer3d.viewers.length;
var allModels = [];
var modelID = 0;
var viewerSize = 0;
while(true) {
var modelMap = v.find('.viewer-3d-map-' + modelID);
var urlNode = v.find('.viewer-3d-url-' + modelID);
if(!modelMap.length || !urlNode.length) break;
var url = $('<div/>').html(urlNode.text()).text();
var framesS = $('<div/>').html(modelMap.text()).text().replace(/^\s+|\s+$/g).split(/,/g);
var frameMap = [];
var heightMap = [];
var leftCropMap = [];
var totalW = parseInt(framesS[0]);
var maxFrameW = parseInt(framesS[1]);
var totalH = parseInt(framesS[2]);
var verticalSteps = parseInt(framesS[3]);
var midVertical = Math.floor(verticalSteps / 2);
for(var f = 4; f < framesS.length; f += 3) {
frameMap.push(parseInt(framesS[f]));
heightMap.push(parseInt(framesS[f + 1]));
leftCropMap.push(parseInt(framesS[f + 2]));
}
allModels.push({
imageURL: url,
map: frameMap,
cropMap: leftCropMap,
totalWidth: totalW,
totalHeight: totalH,
maxFrameWidth: maxFrameW,
xStep: verticalSteps
});
viewerSize = Math.max(viewerSize, totalH, maxFrameW);
modelID++;
}
if(!modelID) return;
var overlayNode = $('<div class="viewer-3d-overlay"></div>');
var frameN = v.find('.viewer-3d-frame');
v.find('img').detach();
var klasses = v.attr('class').split(/ /g);
var startFrame = 0;
for(var k in klasses) {
if(klasses[k].substr(0, 11) == 'startframe-') {
startFrame = Math.max(0, parseInt(klasses[k].substr(11)));
}
}
var viewer = {
node: v,
frameX: startFrame,
frameY: midVertical,
models: allModels,
currentModel: -1,
frameNode: frameN,
width: viewerSize,
height: viewerSize,
mouseX: 0,
mouseY: 0,
overlay: overlayNode
};
viewer3d.viewers.push(viewer);
v.hover(viewer3d.hover, viewer3d.unhover).mousedown(viewer3d.drag).append(overlayNode).attr('data-id', num).css({
width: viewer.width + 'px',
height: viewer.height + 'px'
});
frameN.mousedown(viewer3d.drag).attr('data-id', num).css('height', viewer.height + 'px');
viewer3d.changeVersion(viewer, 0);
},
getCurrentModel: function(v) {
return v.models[v.currentModel];
},
changeVersion: function(v, version) {
version = Math.max(0, Math.min(v.models.length - 1, version));
if(v.currentModel == version) return;
v.currentModel = version;
v.frameNode.css('background', 'url(' + viewer3d.getCurrentModel(v).imageURL + ') top left no-repeat');
viewer3d.display(v, v.frameX, v.frameY);
},
hover: function(e) {
var v = viewer3d.getViewer(this);
if(viewer3d.dragging != v) {
v.overlay.animate({'opacity': '1'}, 'fast');
}
},
unhover: function(e) {
var v = viewer3d.getViewer(this);
if(viewer3d.dragging != v) {
v.overlay.animate({'opacity': '0.5'}, 'fast');
}
},
drag: function(e) {
var v = viewer3d.getViewer(this);
v.mouseX = e.pageX;
v.mouseY = e.pageY;
viewer3d.dragging = v;
draggingFrameX = v.frameX;
draggingFrameY = v.frameY;
return false;
},
release: function() {
var v = viewer3d.dragging;
viewer3d.dragging = null;
if(v != null) {
v.frameX = viewer3d.draggingFrameX;
v.frameY = viewer3d.draggingFrameY;
v.overlay.animate({'opacity': '0.5'}, 'fast');
}
viewer3d.draggingFrameX = 0;
viewer3d.draggingFrameY = 0;
},
getViewer: function(node) {
return viewer3d.viewers[parseInt($(node).attr('data-id'))];
},
display: function(v, frameX, frameY) {
var model = viewer3d.getCurrentModel(v);
var frameID = viewer3d.realMod(frameX * model.xStep + frameY, model.map.length);
var frameOffset = model.map[frameID];
var frameWidth = 0;
if(frameID == model.map.length - 1) {
frameWidth = model.totalWidth - frameOffset;
} else {
frameWidth = model.map[frameID + 1] - frameOffset;
}
v.frameNode.css({
backgroundPosition: (-frameOffset - frameID) + 'px 0px',
left: Math.round((v.width - model.maxFrameWidth) / 2.0 + model.cropMap[frameID]) + 'px',
top: Math.round((v.height - model.totalHeight) / 2) + 'px',
width: frameWidth + 'px',
height: model.totalHeight + 'px'
});
},
move: function(e) {
if(viewer3d.dragging == null) {
return;
}
var v = viewer3d.dragging;
var model = viewer3d.getCurrentModel(v);
var mouseDeltaX = e.pageX - v.mouseX;
var mouseDeltaY = e.pageY - v.mouseY;
var frameDeltaX = Math.round(mouseDeltaX / viewer3d.frameThresholdX);
var frameDeltaY = -Math.round(mouseDeltaY / viewer3d.frameThresholdY);
viewer3d.draggingFrameX = v.frameX + frameDeltaX;
viewer3d.draggingFrameY = Math.max(0, Math.min(model.xStep - 1, v.frameY + frameDeltaY));
viewer3d.display(v, viewer3d.draggingFrameX, viewer3d.draggingFrameY);
}
};
$(viewer3d.init);
var selector3d = {
bind: function() {
var viewer = viewer3d.getViewer($(this).find('.viewer-3d'));
var keepGoing = true;
var modelVariant = 0;
var selector;
while(keepGoing) {
selector = $(this).find('.selector-' + modelVariant);
if(selector.length) {
selector.attr('data-variant', modelVariant).click(function() {
viewer3d.changeVersion(viewer, parseInt($(this).attr('data-variant')));
return false;
});
}
modelVariant++;
keepGoing = selector.length;
}
},
init: function() {
$('.viewer-3d-multi, .viewer-3d-container').each(selector3d.bind);
}
};
$(selector3d.init);


// Converts from time to a clean time info structure
// Code to get 3D viewer drag working on touch devices
function timeToStruct(time) {
// Source: http://www.jquery4u.com/mobile/jquery-add-dragtouch-support-ipad/
$.fn.addTouch = function(){
    this.each(function(i,el){
      $(el).bind('touchstart touchmove touchend touchcancel',function(){
        //we pass the original event object because the jQuery event
        //object is normalized to w3c specs and does not provide the TouchList
        handleTouch(event);
      });
    });


  var passed = time < 0; //Has the moment passed?
    var handleTouch = function(event)
 
    {
  // Parse time fields from the number
      var touches = event.changedTouches,
                                              time = Math.floor(time / 1000);
              first = touches[0],
  var secs  = ("00" + (time % 60)).slice(-2); time = Math.floor(time /  60);
              type = '';
  var mins  = ("00" + (time % 60)).slice(-2); time = Math.floor(time /  60);
  var hours = ("00" + (time % 24)).slice(-2); time = Math.floor(time /  24);


  // Construct the string representation
      switch(event.type)
  return {
      {
    d: time,
        case 'touchstart':
    h: hours,
          type = 'mousedown';
    m: mins,
          break;
    s: secs,
    p: passed
  };
 
}


// Gets the time remaining until the next stage rotation
        case 'touchmove':
function getStageCountdown(now) {
          type = 'mousemove';
  var hour  = Math.floor(now / 3600000) % 24 + 2; // Add 2 for UTC bias
          event.preventDefault();
  var now    = hour * 3600000 + now % 3600000;     // Current adjusted hour
          break;
  var target = (hour + 4 & -4) * 3600000;         // Target hour
  return target - now;
}


function tickCountdowns() {
        case 'touchend':
  var now = Date.now();
          type = 'mouseup';
 
          break;
  for(var c = 0; c < countdowns.length; c++){
   
    var diff = 0;
    if(countdowns[c].stage) {
      diff = timeToStruct(getStageCountdown(now));
    } else {
      diff = timeToStruct(countdowns[c].time - now);
    }
   
    if(diff.p && diff.d < -1) {
      // Over 24 hours passed
      countdowns[c].span.innerHTML = countdowns[c].doneMessage;
    } else if(diff.p){
      // 24 hours haven't passed yet
      countdowns[c].span.innerHTML = countdowns[c].ongoingMessage;
    } else {
      // The time hasn't come yet
      countdowns[c].span.innerHTML =
        ((diff.d > 0) ? (diff.d + " day" + (diff.d == 1 ? "" : "s") + ", ") : "") +
        diff.h + ":" +
        diff.m + ":" +
        diff.s;
    }
  }
}


// Returns the info from a countdown span on the page.
        default:
function getCountdownInfo(countdown, stage) {
          return;
  var time = null;
      }
  var ongoingMessage = "";
  var doneMessage = "";


  if(!stage) {
      var simulatedEvent = document.createEvent('MouseEvent');
    // Format is "<day> <hour>|<24 hour msg>|<afterwards msg>"
      simulatedEvent.initMouseEvent(type, true, true, window, 1, first.screenX, first.screenY, first.clientX, first.clientY, false, false, false, false, 0/*left*/, null);
    var parts = countdown.innerHTML.split("|");
      first.target.dispatchEvent(simulatedEvent);
    doneMessage = (parts.length >= 3) ? parts[2] : parts[1];
     };
    ongoingMessage = parts[1];
   };
   
    var timeParts = parts[0].split(/[ \n]/);
    var date = timeParts[0].split("/");
    var hour = timeParts[1].split(":");
    time = Date.UTC(date[0], date[1] - 1, date[2], hour[0], hour[1]);
  }
 
  countdowns.push( {
    span:          countdown,
    stage:          stage,
    time:          time,
    ongoingMessage: ongoingMessage,
     doneMessage:    doneMessage
  } );
    
  // The spans start hidden and with the info
  // Delete the info and show the span
  countdown.style.display = "inline";
  countdown.innerHTML = "";
}


// Finds countdown spans on the document and sets up the countdowns
$('.viewer-3d').addTouch();
function setupCountdowns() {
  var stageCountdowns = document.getElementsByClassName("stageCountdown");
  for(var sc = 0; sc < stageCountdowns.length; sc++) {
    getCountdownInfo(stageCountdowns[sc], true);
  }
 
  var countdowns = document.getElementsByClassName("countdown");
  for(var c = 0; c < countdowns.length; c++) {
    getCountdownInfo(countdowns[c], false);
  }
 
  setInterval(tickCountdowns, 1000);
}


setupCountdowns();
// End 3D viewer touch device code

Dernière version du 6 mai 2024 à 14:48

/* Any JavaScript here will be loaded for all users on every page load. */

/* MediaWiki:All.js */
mw.loader.load('//splatoonwiki.org/wiki/MediaWiki:All.js?action=raw&ctype=text/javascript');

///////////////////////////////////////////////////////////////////////////////
//                               Gobbler Class                               //
///////////////////////////////////////////////////////////////////////////////

// Folds all infoboxes into a single, tabbed element

// Object constructor
var Gobbler = function() {

    // Check for a gobbler object in the document
    this.element = document.getElementById("gobbler");
    if (!this.element) return;

    // Gobble all of the infoboxes on the page
    var elements = document.getElementsByClassName("tagInfobox");
    var tabs     = [];
    for (var x = 0; x < elements.length; x++)
        tabs.push(this.gobble(elements[x]));
    
    // 1-index for user niceness; 1 is "first"
    var initial = parseInt(this.element.dataset.initial) - 1;
    if (isNaN(initial) || initial < 0 || initial >= tabs.length)
        initial = tabs.length - 1; // default last tab
    this.onTab(tabs[initial]);

    // Add the infoboxes and tab buttons to the gobbler element
    var tabStrip = document.createElement("div");
    tabStrip.style.textAlign = "left";
    if (tabs.length > 1)
        this.element.appendChild(tabStrip);
    for (var x = 0; x < tabs.length; x++) {
        tabStrip.appendChild(tabs[x].button);
        this.element.appendChild(tabs[x].element);
    }

};

// Process an infobox
Gobbler.prototype.gobble = function(element) {

    // Retrieve the infobox's hidden tab button element
    var button = element.getElementsByClassName("tagTab")[0];

    // Create a tab object using the contents of the element
    var tab = {
        button:        button,
        colorInactive: button.style.backgroundColor,
        colorActive:   button.style.outlineColor,
        element:       element,
        cssText:       element.style.cssText
    };

    // Configure element properties
    var that = this;
    element.style.cssText = "width: 100%; height: 100%; display: none;";
    button.style.display  = "inline";
    button.onmousedown    = function(e) {
        if (e.button == 0) that.onTab(tab);
    };
    return tab;
};

// Event handler for when a tab is selected/clicked
Gobbler.prototype.onTab = function(tab) {
	
	// Error checking when new gobbler is present
	if (!tab)
		return;

    // Deselect the previous infobox
    if (this.current) {
        this.current.button.style.fontWeight      = "normal";
        this.current.button.style.backgroundColor = this.current.colorInactive;
        this.current.element.style.display        = "none";
    }

    // Select the new infobox
    this.element.style.cssText       = tab.cssText;
    tab.button.style.fontWeight      = "bold";
    tab.element.style.display        = "inline";
    tab.button.style.backgroundColor = tab.colorActive;
    this.current                     = tab;
};

$( document ).ready(function() {
    new Gobbler();
    // 3D/2D viewer
    $('.switch-to-3d').click(function() {
	    $('.container-2d').hide();
	    $('.viewer-3d, .viewer-3d-multi, .buttons-container-3d').show();
    });

    $('.switch-to-2d').click(function() {
	    $('.viewer-3d, .viewer-3d-multi, .buttons-container-3d').hide();
	    $('.container-2d').show();
    });
});

// ================================================================================
// Warning message in console to help prevent scams
// ================================================================================
setTimeout(function () {
    console.log("%cHold up squiddo!",
        "color:rgb(170, 220, 0);font-family:Splatoon2, sans-serif;font-size:40px")
    console.log("%cIf someone told you to paste something here, there is a 99% chance you're getting scammed!",
        "font-family:Splatoon2, sans-serif;font-size:15px")
    console.log("%cPasting anything here could give scammers access to your account",
        "color:red;font-family:Splatoon2, sans-serif;font-size:15px")
    console.log("%cUnless you know what you're doing, don't paste anything here!",
        "font-family:Splatoon2, sans-serif;font-size:25px")
}, 1500)

// ================================================================================
// 3D/2D Viewer
// ================================================================================

// 3D model viewer
var viewer3d = {
	dragging: null,
	draggingFrameX: 0,
	draggingFrameY: 0,
	viewers: [],
	frameThresholdX: 10,
	frameThresholdY: 128,
	realMod: function(x, y) {
		return ((x % y) + y) % y;
	},
	init: function() {
		$('.viewer-3d').each(viewer3d.bind);
		$(document).mouseup(viewer3d.release);
		$(document).mousemove(viewer3d.move);
	},
	bind: function() {
		var v = $(this);
		var num = viewer3d.viewers.length;
		var allModels = [];
		var modelID = 0;
		var viewerSize = 0;
		while(true) {
			var modelMap = v.find('.viewer-3d-map-' + modelID);
			var urlNode = v.find('.viewer-3d-url-' + modelID);
			if(!modelMap.length || !urlNode.length) break;
			var url = $('<div/>').html(urlNode.text()).text();
			var framesS = $('<div/>').html(modelMap.text()).text().replace(/^\s+|\s+$/g).split(/,/g);
			var frameMap = [];
			var heightMap = [];
			var leftCropMap = [];
			var totalW = parseInt(framesS[0]);
			var maxFrameW = parseInt(framesS[1]);
			var totalH = parseInt(framesS[2]);
			var verticalSteps = parseInt(framesS[3]);
			var midVertical = Math.floor(verticalSteps / 2);
			for(var f = 4; f < framesS.length; f += 3) {
				frameMap.push(parseInt(framesS[f]));
				heightMap.push(parseInt(framesS[f + 1]));
				leftCropMap.push(parseInt(framesS[f + 2]));
			}
			allModels.push({
				imageURL: url,
				map: frameMap,
				cropMap: leftCropMap,
				totalWidth: totalW,
				totalHeight: totalH,
				maxFrameWidth: maxFrameW,
				xStep: verticalSteps
			});
			viewerSize = Math.max(viewerSize, totalH, maxFrameW);
			modelID++;
		}
		if(!modelID) return;
		var overlayNode = $('<div class="viewer-3d-overlay"></div>');
		var frameN = v.find('.viewer-3d-frame');
		v.find('img').detach();
		var klasses = v.attr('class').split(/ /g);
		var startFrame = 0;
		for(var k in klasses) {
			if(klasses[k].substr(0, 11) == 'startframe-') {
				startFrame = Math.max(0, parseInt(klasses[k].substr(11)));
			}
		}
		var viewer = {
			node: v,
			frameX: startFrame,
			frameY: midVertical,
			models: allModels,
			currentModel: -1,
			frameNode: frameN,
			width: viewerSize,
			height: viewerSize,
			mouseX: 0,
			mouseY: 0,
			overlay: overlayNode
		};
		viewer3d.viewers.push(viewer);
		v.hover(viewer3d.hover, viewer3d.unhover).mousedown(viewer3d.drag).append(overlayNode).attr('data-id', num).css({
			width: viewer.width + 'px',
			height: viewer.height + 'px'
		});
		frameN.mousedown(viewer3d.drag).attr('data-id', num).css('height', viewer.height + 'px');
		viewer3d.changeVersion(viewer, 0);
	},
	getCurrentModel: function(v) {
		return v.models[v.currentModel];
	},
	changeVersion: function(v, version) {
		version = Math.max(0, Math.min(v.models.length - 1, version));
		if(v.currentModel == version) return;
		v.currentModel = version;
		v.frameNode.css('background', 'url(' + viewer3d.getCurrentModel(v).imageURL + ') top left no-repeat');
		viewer3d.display(v, v.frameX, v.frameY);
	},
	hover: function(e) {
		var v = viewer3d.getViewer(this);
		if(viewer3d.dragging != v) {
			v.overlay.animate({'opacity': '1'}, 'fast');
		}
	},
	unhover: function(e) {
		var v = viewer3d.getViewer(this);
		if(viewer3d.dragging != v) {
			v.overlay.animate({'opacity': '0.5'}, 'fast');
		}
	},
	drag: function(e) {
		var v = viewer3d.getViewer(this);
		v.mouseX = e.pageX;
		v.mouseY = e.pageY;
		viewer3d.dragging = v;
		draggingFrameX = v.frameX;
		draggingFrameY = v.frameY;
		return false;
	},
	release: function() {
		var v = viewer3d.dragging;
		viewer3d.dragging = null;
		if(v != null) {
			v.frameX = viewer3d.draggingFrameX;
			v.frameY = viewer3d.draggingFrameY;
			v.overlay.animate({'opacity': '0.5'}, 'fast');
		}
		viewer3d.draggingFrameX = 0;
		viewer3d.draggingFrameY = 0;
	},
	getViewer: function(node) {
		return viewer3d.viewers[parseInt($(node).attr('data-id'))];
	},
	display: function(v, frameX, frameY) {
		var model = viewer3d.getCurrentModel(v);
		var frameID = viewer3d.realMod(frameX * model.xStep + frameY, model.map.length);
		var frameOffset = model.map[frameID];
		var frameWidth = 0;
		if(frameID == model.map.length - 1) {
			frameWidth = model.totalWidth - frameOffset;
		} else {
			frameWidth = model.map[frameID + 1] - frameOffset;
		}
		v.frameNode.css({
			backgroundPosition: (-frameOffset - frameID) + 'px 0px',
			left: Math.round((v.width - model.maxFrameWidth) / 2.0 + model.cropMap[frameID]) + 'px',
			top: Math.round((v.height - model.totalHeight) / 2) + 'px',
			width: frameWidth + 'px',
			height: model.totalHeight + 'px'
		});
	},
	move: function(e) {
		if(viewer3d.dragging == null) {
			return;
		}
		var v = viewer3d.dragging;
		var model = viewer3d.getCurrentModel(v);
		var mouseDeltaX = e.pageX - v.mouseX;
		var mouseDeltaY = e.pageY - v.mouseY;
		var frameDeltaX = Math.round(mouseDeltaX / viewer3d.frameThresholdX);
		var frameDeltaY = -Math.round(mouseDeltaY / viewer3d.frameThresholdY);
		viewer3d.draggingFrameX = v.frameX + frameDeltaX;
		viewer3d.draggingFrameY = Math.max(0, Math.min(model.xStep - 1, v.frameY + frameDeltaY));
		viewer3d.display(v, viewer3d.draggingFrameX, viewer3d.draggingFrameY);
	}
};
$(viewer3d.init);
var selector3d = {
	bind: function() {
		var viewer = viewer3d.getViewer($(this).find('.viewer-3d'));
		var keepGoing = true;
		var modelVariant = 0;
		var selector;
		while(keepGoing) {
			selector = $(this).find('.selector-' + modelVariant);
			if(selector.length) {
				selector.attr('data-variant', modelVariant).click(function() {
					viewer3d.changeVersion(viewer, parseInt($(this).attr('data-variant')));
					return false;
				});
			}
			modelVariant++;
			keepGoing = selector.length;
		}
	},
	init: function() {
		$('.viewer-3d-multi, .viewer-3d-container').each(selector3d.bind);
	}
};
$(selector3d.init);

// Code to get 3D viewer drag working on touch devices
// Source: http://www.jquery4u.com/mobile/jquery-add-dragtouch-support-ipad/
$.fn.addTouch = function(){
    this.each(function(i,el){
      $(el).bind('touchstart touchmove touchend touchcancel',function(){
        //we pass the original event object because the jQuery event
        //object is normalized to w3c specs and does not provide the TouchList
        handleTouch(event);
      });
    });

    var handleTouch = function(event)
    {
      var touches = event.changedTouches,
              first = touches[0],
              type = '';

      switch(event.type)
      {
        case 'touchstart':
          type = 'mousedown';
          break;

        case 'touchmove':
          type = 'mousemove';
          event.preventDefault();
          break;

        case 'touchend':
          type = 'mouseup';
          break;

        default:
          return;
      }

      var simulatedEvent = document.createEvent('MouseEvent');
      simulatedEvent.initMouseEvent(type, true, true, window, 1, first.screenX, first.screenY, first.clientX, first.clientY, false, false, false, false, 0/*left*/, null);
      first.target.dispatchEvent(simulatedEvent);
    };
  };

$('.viewer-3d').addTouch();

// End 3D viewer touch device code