You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1150 lines
53 KiB
1150 lines
53 KiB
/*
|
|
---
|
|
description: jBox is a powerful and flexible jQuery plugin, taking care of all your modal windows, tooltips, notices and more.
|
|
|
|
authors: Stephan Wagner (http://stephanwagner.me)
|
|
|
|
license: MIT (http://www.opensource.org/licenses/mit-license.php)
|
|
|
|
requires: jQuery 1.11.0 (http://code.jquery.com/jquery-1.11.0.min.js)
|
|
jQuery 2.1.0 (http://code.jquery.com/jquery-2.1.0.min.js)
|
|
|
|
documentation: http://stephanwagner.me/jBox/documentation
|
|
...
|
|
*/
|
|
|
|
function jBox(type, options) {
|
|
this.options = {
|
|
// jBox ID
|
|
id: null, // Choose a unique id, otherwise jBox will set one for you (jBox1, jBox2, ...)
|
|
|
|
// Dimensions
|
|
width: 'auto', // Width of container (e.g. 'auto', 100)
|
|
height: 'auto', // Height of container
|
|
|
|
// Attach
|
|
attach: null, // Attach jBox to elements (if no target element is provided, jBox will use the attached element as target)
|
|
trigger: 'click', // The event to open or close your jBoxes, use 'click' or 'mouseenter'
|
|
preventDefault: false, // Prevent default event when opening jBox (e.g. don't follow the href in a link when clicking on it)
|
|
|
|
// Content
|
|
title: null, // Adds a title to your jBox
|
|
content: null, // You can use a string to set text or HTML as content, or an element selector (e.g. jQuery('#jBox-content')) to append one or several elements (elements appended will get style display: 'block', so hide them with CSS style display: 'none' beforehand)
|
|
getTitle: null, // Get the title from an attribute when jBox opens
|
|
getContent: null, // Get the content from an attribute when jBox opens
|
|
|
|
// Content from Ajax
|
|
ajax: null, // Set a url to get content from an ajax call and inject into content area
|
|
data: '', // Ajax data to send with your ajax call (e.g. 'id=82')
|
|
reload: false, // Each time jBox is opened, reload the ajax call
|
|
|
|
// Position
|
|
target: null, // The target element where jBox will be opened
|
|
position: {
|
|
x: 'center', // Horizontal Position (Use a number, 'left', 'right' or 'center')
|
|
y: 'center' // Vertical Position (Use a number, 'top', 'bottom' or 'center')
|
|
},
|
|
outside: null, // Use 'x', 'y', or 'xy' to move your jBox outside of the target element
|
|
offset: 0, // Offset to final position, you can set different values for x and y with an object e.g. {x: 15, y: 0}
|
|
|
|
attributes: { // Note that attributes can only be 'left' or 'right' when using numbers for position, e.g. {x: 300, y: 20}
|
|
x: 'left', // Horizontal position, use 'left' or 'right'
|
|
y: 'top' // Vertical position, use 'top' or 'bottom'
|
|
},
|
|
adjustPosition: false, // Adjusts the position when there is not enough space (use true, 'flip' or 'move')
|
|
adjustTracker: false, // By default jBox adjusts the position when opening, to adjust when scrolling or resizing, use 'scroll', 'resize' or 'true' (both events)
|
|
adjustDistance: 5, // How far from the window edge we start adjusting, use an object to set different values: {bottom: 5, top: 50, left: 5, right: 20}
|
|
fixed: false, // Your jBox will stay on position when scrolling
|
|
reposition: false, // Calculates new position when the window-size changes
|
|
|
|
// Pointer
|
|
pointer: false, // Your pointer will always point towards the target element, so the option outside should be 'x' or 'y'
|
|
|
|
// Animations
|
|
fade: 180, // Fade duration in ms, set to 0 or false to disable
|
|
animation: null, // Animation when opening or closing (use 'pulse', 'zoomIn', 'zoomOut', 'move', 'slide', 'flip', 'tada') (CSS inspired from Daniel Edens Animate.css: http://daneden.me/animate)
|
|
|
|
// Appearance
|
|
theme: 'Default', // Set a jBox theme class
|
|
addClass: '', // Adds classes to the wrapper
|
|
overlay: false, // Adds an overlay when jBox opens (set color and opacity with CSS)
|
|
zIndex: 10000, // Use a high zIndex (your overlay will have the lowest zIndex of all your jBoxes (with overlays) minus one)
|
|
|
|
// Delays
|
|
delayOpen: 0, // Delay opening in ms (Note that the delay will be ignored if your jBox didn't finish closing)
|
|
delayClose: 0, // Delay closing in ms (Note that there is always a closing delay of at least 10ms to ensure jBox won't be closed when opening right away)
|
|
|
|
// Closing events
|
|
closeOnEsc: false, // Close jBox when pressing [esc] key
|
|
closeOnClick: false, // Close jBox with mouseclick, use 'true' (click anywhere), 'box' (click on jBox itself), 'body' (click anywhere but jBox)
|
|
closeOnMouseleave: false, // Close jBox when the mouse leaves the jBox area or the area of the attached element
|
|
closeButton: false, // Adds a close button to your jBox, use 'true', 'title' or 'box' ('true' will put the button in title when there is one, otherwise in box)
|
|
|
|
// Other options
|
|
constructOnInit: false, // Construct jBox when it's being initialized
|
|
blockScroll: false, // When jBox is open, block scrolling
|
|
appendTo: jQuery('body'), // Provide an element if you want the jBox to be positioned inside a specific element (only useful for fixed positions or when position values are numbers)
|
|
draggable: null, // Make your jBox draggable (use 'true', 'title' or provide an element as handle) (inspired from Chris Coyiers CSS-Tricks http://css-tricks.com/snippets/jquery/draggable-without-jquery-ui/)
|
|
|
|
// Events // Note: You can use 'this' in the event functions, it refers to your jBox object (e.g. onInit: function() { this.open(); })
|
|
onInit: function () { }, // Triggered when jBox is initialized, just before it's being created
|
|
onCreated: function () { }, // Triggered when jBox is created and is availible in DOM
|
|
onOpen: function () { }, // Triggered when jBox is opened
|
|
onClose: function () { }, // Triggered when jBox is closed
|
|
onAjax: function () { }, // Triggered when the ajax call starts
|
|
onAjaxComplete: function () { }, // Triggered when the ajax call is completed
|
|
|
|
// Only for Notices:
|
|
autoClose: 7000, // Time when jBox should close automatically
|
|
color: null, // Makes your notices colorful, use 'black', 'red', 'green', 'blue', 'yellow'
|
|
stack: true, // Set to false to disable notice-stacking
|
|
audio: false, // Set the url to an audio file without extention, e.g. '/url/filename'. jBox will look for an .mp3 and an .ogg file
|
|
volume: 100 // Percent of volume for audio files
|
|
};
|
|
|
|
// Default type options
|
|
this.defaultOptions = {
|
|
// Default options for tooltips
|
|
'Tooltip': {
|
|
getContent: 'title',
|
|
trigger: 'mouseenter',
|
|
position: { x: 'center', y: 'top' },
|
|
outside: 'y',
|
|
pointer: true,
|
|
adjustPosition: true,
|
|
reposition: true
|
|
},
|
|
// Default options for mouse tooltips
|
|
'Mouse': {
|
|
target: 'mouse',
|
|
position: { x: 'right', y: 'bottom' },
|
|
offset: 15,
|
|
trigger: 'mouseenter',
|
|
adjustPosition: 'flip'
|
|
},
|
|
// Default options for modal windows
|
|
'Modal': {
|
|
target: jQuery(window),
|
|
fixed: true,
|
|
blockScroll: true,
|
|
closeOnEsc: true,
|
|
closeOnClick: 'body',
|
|
closeButton: 'title',
|
|
overlay: true,
|
|
animation: 'zoomOut'
|
|
},
|
|
// Default options for notices
|
|
'Notice': {
|
|
target: jQuery(window),
|
|
fixed: true,
|
|
position: { x: 20, y: 20 },
|
|
attributes: { x: 'right', y: 'top' },
|
|
animation: 'zoomIn',
|
|
closeOnClick: 'box',
|
|
_onInit: function () {
|
|
this.open();
|
|
this.options.delayClose = this.options.autoClose;
|
|
this.options.delayClose && this.close();
|
|
}.bind(this),
|
|
_onCreated: function () {
|
|
this.options.color && this.wrapper.addClass('jBox-Notice-color jBox-Notice-' + this.options.color);
|
|
},
|
|
_onOpen: function () {
|
|
// Loop through notices at same window corner and either move or destroy them
|
|
jQuery.each(jQuery('.jBox-Notice'), function (index, el) {
|
|
el = jQuery(el);
|
|
if (el.attr('id') == this.id || el.css(this.options.attributes.y) == 'auto' || el.css(this.options.attributes.x) == 'auto') return;
|
|
if (!this.options.stack) {
|
|
el.data('jBox').close({ ignoreDelay: true });
|
|
return;
|
|
}
|
|
el.css('margin-' + this.options.attributes.y, parseInt(el.css('margin-' + this.options.attributes.y)) + this.dimensions.y + 10);
|
|
}.bind(this));
|
|
// Play audio file, IE8 doesn't support audio
|
|
if (this.options.audio && !this.IE8) {
|
|
this.audio = jQuery('<audio/>');
|
|
jQuery('<source/>', { src: this.options.audio + '.mp3' }).appendTo(this.audio);
|
|
jQuery('<source/>', { src: this.options.audio + '.ogg' }).appendTo(this.audio);
|
|
this.audio[0].volume = Math.min((this.options.volume / 100), 1);
|
|
this.audio[0].play();
|
|
}
|
|
}.bind(this),
|
|
// Remove from DOM when closing finishes
|
|
_onCloseComplete: function () {
|
|
this.destroy();
|
|
}.bind(this)
|
|
}
|
|
};
|
|
|
|
// Set default options for jBox types
|
|
if (jQuery.type(type) == 'string') {
|
|
this.type = type;
|
|
type = this.defaultOptions[type];
|
|
}
|
|
|
|
// Merge options
|
|
this.options = jQuery.extend(this.options, type, options);
|
|
|
|
// Get unique ID
|
|
if (this.options.id === null) {
|
|
var i = 1;
|
|
while (jQuery('#jBox' + i).length != 0) i++;
|
|
this.options.id = 'jBox' + i;
|
|
}
|
|
this.id = this.options.id;
|
|
|
|
// Correct impossible options
|
|
((this.options.position.x == 'center' && this.options.outside == 'x') || (this.options.position.y == 'center' && this.options.outside == 'y')) && (this.options.outside = false);
|
|
(!this.options.outside || this.options.outside == 'xy') && (this.options.pointer = false);
|
|
|
|
// Correct multiple choice options
|
|
jQuery.type(this.options.offset) != 'object' && (this.options.offset = { x: this.options.offset, y: this.options.offset });
|
|
this.options.offset.x || (this.options.offset.x = 0);
|
|
this.options.offset.y || (this.options.offset.y = 0);
|
|
jQuery.type(this.options.adjustDistance) != 'object' && (this.options.adjustDistance = { top: this.options.adjustDistance, right: this.options.adjustDistance, bottom: this.options.adjustDistance, left: this.options.adjustDistance });
|
|
|
|
// Save where the jBox is aligned to
|
|
this.align = (this.options.outside && this.options.outside != 'xy') ? this.options.position[this.options.outside] : (this.options.position.y != 'center' && jQuery.type(this.options.position.y) != 'number' ? this.options.position.x : (this.options.position.x != 'center' && jQuery.type(this.options.position.x) != 'number' ? this.options.position.y : this.options.attributes.x));
|
|
|
|
// Save default outside position
|
|
this.options.outside && this.options.outside != 'xy' && (this.outside = this.options.position[this.options.outside]);
|
|
|
|
// I know browser detection is bad practice, but for now it seems the only option to get jBox working in IE8
|
|
var userAgent = navigator.userAgent.toLowerCase();
|
|
this.IE8 = userAgent.indexOf('msie') != -1 && parseInt(userAgent.split('msie')[1]) == 8;
|
|
|
|
// Save global var for webkit prefix
|
|
this.prefix = userAgent.indexOf('webkit') != -1 ? '-webkit-' : '';
|
|
|
|
// Internal functions, used to easily get values
|
|
this._getOpp = function (opp) { return { left: 'right', right: 'left', top: 'bottom', bottom: 'top', x: 'y', y: 'x' }[opp]; };
|
|
this._getXY = function (xy) { return { left: 'x', right: 'x', top: 'y', bottom: 'y', center: 'x' }[xy]; };
|
|
this._getTL = function (tl) { return { left: 'left', right: 'left', top: 'top', bottom: 'top', center: 'left', x: 'left', y: 'top' }[tl]; };
|
|
|
|
// Create jBox
|
|
this._create = function () {
|
|
if (this.wrapper) return;
|
|
|
|
// Create wrapper
|
|
this.wrapper = jQuery('<div/>', {
|
|
id: this.id,
|
|
'class': 'jBox-wrapper' + (this.type ? ' jBox-' + this.type : '') + (this.options.theme ? ' jBox-' + this.options.theme : '') + (this.options.addClass ? ' ' + this.options.addClass : '') + (this.IE8 ? ' jBox-IE8' : '')
|
|
}).css({
|
|
position: (this.options.fixed ? 'fixed' : 'absolute'),
|
|
display: 'none',
|
|
opacity: 0,
|
|
zIndex: this.options.zIndex
|
|
|
|
// Save the jBox instance in the wrapper, so you gan get access to your jBox when you only have the element
|
|
}).data('jBox', this);
|
|
|
|
// Add mouseleave event
|
|
this.options.closeOnMouseleave && this.wrapper.mouseenter(function () { this.open(); }.bind(this)).mouseleave(function () { this.close(); }.bind(this));
|
|
|
|
// Create container
|
|
this.container = jQuery('<div/>', { 'class': 'jBox-container' }).css({ width: this.options.width, height: this.options.height }).appendTo(this.wrapper);
|
|
|
|
// Create content
|
|
this.content = jQuery('<div/>', { 'class': 'jBox-content' }).appendTo(this.container);
|
|
|
|
// Create close button
|
|
if (this.options.closeButton) {
|
|
this.closeButton = jQuery('<div/>', { 'class': 'jBox-closeButton jBox-noDrag' }).click(function () { this.close(); }.bind(this));
|
|
if (this.options.closeButton != 'title') {
|
|
this.wrapper.addClass('jBox-closeButton-box');
|
|
this.closeButton.appendTo(this.container);
|
|
}
|
|
}
|
|
|
|
// Append jBox to DOM
|
|
this.wrapper.appendTo(this.options.appendTo);
|
|
|
|
// Create pointer
|
|
if (this.options.pointer) {
|
|
// Get pointer vars and save globally
|
|
this.pointer = {
|
|
position: this._getOpp(this.outside),
|
|
xy: this._getXY(this.outside),
|
|
align: 'center',
|
|
offset: 0
|
|
};
|
|
|
|
this.pointer.element = jQuery('<div/>', { 'class': 'jBox-pointer jBox-pointer-' + this.pointer.position }).appendTo(this.wrapper);
|
|
this.pointer.dimensions = {
|
|
x: this.pointer.element.outerWidth(),
|
|
y: this.pointer.element.outerHeight()
|
|
};
|
|
|
|
if (jQuery.type(this.options.pointer) == 'string') {
|
|
var split = this.options.pointer.split(':');
|
|
split[0] && (this.pointer.align = split[0]);
|
|
split[1] && (this.pointer.offset = parseInt(split[1]));
|
|
}
|
|
this.pointer.alignAttribute = (this.pointer.xy == 'x' ? (this.pointer.align == 'bottom' ? 'bottom' : 'top') : (this.pointer.align == 'right' ? 'right' : 'left'));
|
|
|
|
// Set wrapper CSS
|
|
this.wrapper.css('padding-' + this.pointer.position, this.pointer.dimensions[this.pointer.xy]);
|
|
|
|
// Set pointer CSS
|
|
this.pointer.element.css(this.pointer.alignAttribute, (this.pointer.align == 'center' ? '50%' : 0)).css('margin-' + this.pointer.alignAttribute, this.pointer.offset);
|
|
this.pointer.margin = { margin: this.pointer.element.css('margin') };
|
|
|
|
// Add a transform to fix centered position
|
|
(this.pointer.align == 'center') && this.pointer.element.css(this.prefix + 'transform', 'translate(' + (this.pointer.xy == 'y' ? (this.pointer.dimensions.x * -0.5 + 'px') : 0) + ', ' + (this.pointer.xy == 'x' ? (this.pointer.dimensions.y * -0.5 + 'px') : 0) + ')');
|
|
|
|
this.pointer.element.css((this.pointer.xy == 'x' ? 'width' : 'height'), parseInt(this.pointer.dimensions[this.pointer.xy]) + parseInt(this.container.css('border-' + this.pointer.alignAttribute + '-width')));
|
|
|
|
// Add class to wrapper for CSS access
|
|
this.wrapper.addClass('jBox-pointerPosition-' + this.pointer.position);
|
|
}
|
|
|
|
// Set title and content
|
|
this.setContent(this.options.content);
|
|
this.setTitle(this.options.title);
|
|
|
|
// Make jBox draggable
|
|
if (this.options.draggable) {
|
|
var handle = (this.options.draggable == 'title') ? this.titleContainer : (this.options.draggable.length > 0 ? this.options.draggable : this.wrapper);
|
|
handle.addClass('jBox-draggable').on('mousedown', function (ev) {
|
|
if (ev.button == 2 || jQuery(ev.target).hasClass('jBox-noDrag') || jQuery(ev.target).parents('.jBox-noDrag').length) return;
|
|
|
|
var drg_h = this.wrapper.outerHeight(),
|
|
drg_w = this.wrapper.outerWidth(),
|
|
pos_y = this.wrapper.offset().top + drg_h - ev.pageY,
|
|
pos_x = this.wrapper.offset().left + drg_w - ev.pageX;
|
|
jQuery(document).on('mousemove.jBox-draggable-' + this.id, function (ev) {
|
|
this.wrapper.offset({
|
|
top: ev.pageY + pos_y - drg_h,
|
|
left: ev.pageX + pos_x - drg_w
|
|
});
|
|
}.bind(this));
|
|
ev.preventDefault();
|
|
}.bind(this)).on('mouseup', function () { jQuery(document).off('mousemove.jBox-draggable-' + this.id); }.bind(this));
|
|
}
|
|
|
|
// Fire onCreated event
|
|
(this.options.onCreated.bind(this))();
|
|
this.options._onCreated && (this.options._onCreated.bind(this))();
|
|
};
|
|
|
|
// Create jBox onInit
|
|
this.options.constructOnInit && this._create();
|
|
|
|
// Attach jBox
|
|
this.options.attach && this.attach();
|
|
|
|
// Position jBox on mouse
|
|
this._positionMouse = function (ev) {
|
|
// Calculate positions
|
|
this.pos = {
|
|
left: ev.pageX,
|
|
top: ev.pageY
|
|
};
|
|
var setPosition = function (a, p) {
|
|
// Set centered position
|
|
if (this.options.position[p] == 'center') {
|
|
this.pos[a] -= Math.ceil(this.dimensions[p] / 2);
|
|
return;
|
|
}
|
|
// Move to left or top
|
|
this.pos[a] += (a == this.options.position[p]) ? ((this.dimensions[p] * -1) - this.options.offset[p]) : this.options.offset[p];
|
|
|
|
return this.pos[a];
|
|
}.bind(this);
|
|
|
|
// Set position to wrapper
|
|
this.wrapper.css({
|
|
left: setPosition('left', 'x'),
|
|
top: setPosition('top', 'y')
|
|
});
|
|
|
|
// Adjust mouse position
|
|
this.targetDimensions = { x: 0, y: 0, left: ev.pageX, top: ev.pageY };
|
|
this._adjustPosition();
|
|
};
|
|
|
|
// Attach events
|
|
this._attachEvents = function () {
|
|
// Closing event: closeOnEsc
|
|
this.options.closeOnEsc && jQuery(document).on('keyup.jBox-' + this.id, function (ev) { if (ev.keyCode == 27) { this.close({ ignoreDelay: true }); } }.bind(this));
|
|
|
|
// Closing event: closeOnClick
|
|
this.options.closeOnClick && jQuery(document).on('click.jBox-' + this.id, function (ev) {
|
|
if (this.blockBodyClick ||
|
|
(this.options.closeOnClick == 'box' && ev.target != this.wrapper[0] && !this.wrapper.has(ev.target).length) ||
|
|
(this.options.closeOnClick == 'body' && (ev.target == this.wrapper[0] || this.wrapper.has(ev.target).length)))
|
|
return;
|
|
this.close({ ignoreDelay: true });
|
|
}.bind(this));
|
|
|
|
// Positioning events
|
|
if (((this.options.adjustPosition && this.options.adjustTracker) || this.options.reposition) && !this.fixed && this.outside) {
|
|
var scrollTimer,
|
|
scrollTimerTriggered = 0,
|
|
scrollTriggerDelay = 150; // Trigger scroll and resize events every 150 ms (set a higher value to improve performance)
|
|
|
|
// Function to delay positioning event
|
|
var positionDelay = function () {
|
|
var now = new Date().getTime();
|
|
if (!scrollTimer) {
|
|
if (now - scrollTimerTriggered > scrollTriggerDelay) {
|
|
this.options.reposition && this.position();
|
|
this.options.adjustTracker && this._adjustPosition();
|
|
scrollTimerTriggered = now;
|
|
}
|
|
scrollTimer = setTimeout(function () {
|
|
scrollTimer = null;
|
|
scrollTimerTriggered = new Date().getTime();
|
|
this.options.reposition && this.position();
|
|
this.options.adjustTracker && this._adjustPosition();
|
|
}.bind(this), scrollTriggerDelay);
|
|
}
|
|
}.bind(this);
|
|
|
|
// Trigger position events when scrolling
|
|
(this.options.adjustTracker && this.options.adjustTracker != 'resize') && jQuery(window).on('scroll.jBox-' + this.id, function (ev) { positionDelay(); }.bind(this));
|
|
|
|
// Trigger position events when resizing
|
|
((this.options.adjustTracker && this.options.adjustTracker != 'scroll') || this.options.reposition) && jQuery(window).on('resize.jBox-' + this.id, function (ev) { positionDelay(); }.bind(this));
|
|
}
|
|
|
|
// Mousemove events
|
|
this.options.target == 'mouse' && jQuery('body').on('mousemove.jBox-' + this.id, function (ev) { this._positionMouse(ev); }.bind(this));
|
|
};
|
|
|
|
// Detach events
|
|
this._detachEvents = function () {
|
|
// Closing event: closeOnEsc
|
|
this.options.closeOnEsc && jQuery(document).off('keyup.jBox-' + this.id);
|
|
|
|
// Closing event: closeOnClick
|
|
this.options.closeOnClick && jQuery(document).off('click.jBox-' + this.id);
|
|
|
|
// Positioning events
|
|
if ((this.options.adjustPosition && this.options.adjustTracker) || this.options.reposition) {
|
|
jQuery(window).off('scroll.jBox-' + this.id);
|
|
jQuery(window).off('resize.jBox-' + this.id);
|
|
}
|
|
|
|
// Mousemove events
|
|
this.options.target == 'mouse' && jQuery('body').off('mousemove.jBox-' + this.id);
|
|
};
|
|
|
|
// Add overlay
|
|
this._addOverlay = function () {
|
|
// If the overlay isn't cached, set overlay or create it
|
|
!this.overlay && (this.overlay = jQuery('#jBox-overlay').length ? jQuery('#jBox-overlay').css({ zIndex: Math.min(jQuery('#jBox-overlay').css('z-index'), (this.options.zIndex - 1)) }) : (jQuery('<div/>', { id: 'jBox-overlay' }).css({ display: 'none', opacity: 0, zIndex: (this.options.zIndex - 1) }).appendTo(jQuery('body'))));
|
|
|
|
// Add jBox to data
|
|
var overlay_data = this.overlay.data('jBox') || {};
|
|
overlay_data['jBox-' + this.id] = true;
|
|
this.overlay.data('jBox', overlay_data);
|
|
|
|
// Abort if overlay shown already
|
|
if (this.overlay.css('display') == 'block') return;
|
|
|
|
// Show overlay
|
|
this.options.fade ? (this.overlay.stop() && this.overlay.animate({ opacity: 1 }, {
|
|
queue: false,
|
|
duration: this.options.fade,
|
|
start: function () { this.overlay.css({ display: 'block' }); }.bind(this)
|
|
})) : this.overlay.css({ display: 'block', opacity: 1 });
|
|
};
|
|
|
|
// Remove overlay
|
|
this._removeOverlay = function () {
|
|
// Abort if no overlay found
|
|
if (!this.overlay) return;
|
|
|
|
// Remove jBox from data
|
|
var overlay_data = this.overlay.data('jBox');
|
|
delete overlay_data['jBox-' + this.id];
|
|
this.overlay.data('jBox', overlay_data);
|
|
|
|
// Hide overlay if no other jBox needs it
|
|
if (jQuery.isEmptyObject(overlay_data)) {
|
|
this.options.fade ? (this.overlay.stop() && this.overlay.animate({ opacity: 0 }, {
|
|
queue: false,
|
|
duration: this.options.fade,
|
|
complete: function () { this.overlay.css({ display: 'none' }); }.bind(this)
|
|
})) : this.overlay.css({ display: 'none', opacity: 0 });
|
|
}
|
|
};
|
|
|
|
// Generate CSS for animations and append to header
|
|
this._generateCSS = function () {
|
|
if (this.IE8) return;
|
|
|
|
// Get open and close animations if none provided
|
|
(jQuery.type(this.options.animation) != 'object') && (this.options.animation = {
|
|
pulse: { open: 'pulse', close: 'zoomOut' },
|
|
zoomIn: { open: 'zoomIn', close: 'zoomIn' },
|
|
zoomOut: { open: 'zoomOut', close: 'zoomOut' },
|
|
move: { open: 'move', close: 'move' },
|
|
slide: { open: 'slide', close: 'slide' },
|
|
flip: { open: 'flip', close: 'flip' },
|
|
tada: { open: 'tada', close: 'zoomOut' }
|
|
}[this.options.animation]);
|
|
|
|
// Get direction var
|
|
this.options.animation.open && (this.options.animation.open = this.options.animation.open.split(':'));
|
|
this.options.animation.close && (this.options.animation.close = this.options.animation.close.split(':'));
|
|
this.options.animation.openDirection = this.options.animation.open ? this.options.animation.open[1] : null;
|
|
this.options.animation.closeDirection = this.options.animation.close ? this.options.animation.close[1] : null;
|
|
this.options.animation.open && (this.options.animation.open = this.options.animation.open[0]);
|
|
this.options.animation.close && (this.options.animation.close = this.options.animation.close[0]);
|
|
|
|
// Add 'Open' and 'Close' to animation names
|
|
this.options.animation.open && (this.options.animation.open += 'Open');
|
|
this.options.animation.close && (this.options.animation.close += 'Close');
|
|
|
|
// All animations
|
|
var animations = {
|
|
pulse: {
|
|
duration: 350,
|
|
css: [['0%', 'scale(1)'], ['50%', 'scale(1.1)'], ['100%', 'scale(1)']]
|
|
},
|
|
zoomInOpen: {
|
|
duration: (this.options.fade || 180),
|
|
css: [['0%', 'scale(0.9)'], ['100%', 'scale(1)']]
|
|
},
|
|
zoomInClose: {
|
|
duration: (this.options.fade || 180),
|
|
css: [['0%', 'scale(1)'], ['100%', 'scale(0.9)']]
|
|
},
|
|
zoomOutOpen: {
|
|
duration: (this.options.fade || 180),
|
|
css: [['0%', 'scale(1.1)'], ['100%', 'scale(1)']]
|
|
},
|
|
zoomOutClose: {
|
|
duration: (this.options.fade || 180),
|
|
css: [['0%', 'scale(1)'], ['100%', 'scale(1.1)']]
|
|
},
|
|
moveOpen: {
|
|
duration: (this.options.fade || 180),
|
|
positions: { top: { '0%': -12 }, right: { '0%': 12 }, bottom: { '0%': 12 }, left: { '0%': -12 } },
|
|
css: [['0%', 'translate%XY(%Vpx)'], ['100%', 'translate%XY(0px)']]
|
|
},
|
|
moveClose: {
|
|
duration: (this.options.fade || 180),
|
|
timing: 'ease-in',
|
|
positions: { top: { '100%': -12 }, right: { '100%': 12 }, bottom: { '100%': 12 }, left: { '100%': -12 } },
|
|
css: [['0%', 'translate%XY(0px)'], ['100%', 'translate%XY(%Vpx)']]
|
|
},
|
|
slideOpen: {
|
|
duration: 400,
|
|
positions: { top: { '0%': -400 }, right: { '0%': 400 }, bottom: { '0%': 400 }, left: { '0%': -400 } },
|
|
css: [['0%', 'translate%XY(%Vpx)'], ['100%', 'translate%XY(0px)']]
|
|
},
|
|
slideClose: {
|
|
duration: 400,
|
|
timing: 'ease-in',
|
|
positions: { top: { '100%': -400 }, right: { '100%': 400 }, bottom: { '100%': 400 }, left: { '100%': -400 } },
|
|
css: [['0%', 'translate%XY(0px)'], ['100%', 'translate%XY(%Vpx)']]
|
|
},
|
|
flipOpen: {
|
|
duration: 600,
|
|
css: [['0%', 'perspective(400px) rotateX(90deg)'], ['40%', 'perspective(400px) rotateX(-15deg)'], ['70%', 'perspective(400px) rotateX(15deg)'], ['100%', 'perspective(400px) rotateX(0deg)']]
|
|
},
|
|
flipClose: {
|
|
duration: (this.options.fade || 300),
|
|
css: [['0%', 'perspective(400px) rotateX(0deg)'], ['100%', 'perspective(400px) rotateX(90deg)']]
|
|
},
|
|
tada: {
|
|
duration: 800,
|
|
css: [['0%', 'scale(1)'], ['10%, 20%', 'scale(0.9) rotate(-3deg)'], ['30%, 50%, 70%, 90%', 'scale(1.1) rotate(3deg)'], ['40%, 60%, 80%', 'scale(1.1) rotate(-3deg)'], ['100%', 'scale(1) rotate(0)']]
|
|
}
|
|
};
|
|
|
|
// Set Open and Close names for standalone animations
|
|
jQuery.each(['pulse', 'tada'], function (index, item) { animations[item + 'Open'] = animations[item + 'Close'] = animations[item]; });
|
|
|
|
// Function to generate the CSS for the keyframes
|
|
var generateKeyframeCSS = function (ev, position) {
|
|
// Generate keyframes CSS
|
|
keyframe_css = '@' + this.prefix + 'keyframes jBox-animation-' + this.options.animation[ev] + '-' + ev + (position ? '-' + position : '') + ' {';
|
|
jQuery.each(animations[this.options.animation[ev]].css, function (index, item) {
|
|
var translate = position ? item[1].replace('%XY', this._getXY(position).toUpperCase()) : item[1];
|
|
animations[this.options.animation[ev]].positions && (translate = translate.replace('%V', animations[this.options.animation[ev]].positions[position][item[0]]));
|
|
keyframe_css += item[0] + ' {' + this.prefix + 'transform:' + translate + ';}';
|
|
}.bind(this));
|
|
keyframe_css += '}';
|
|
|
|
// Generate class CSS
|
|
keyframe_css += '.jBox-animation-' + this.options.animation[ev] + '-' + ev + (position ? '-' + position : '') + ' {';
|
|
keyframe_css += this.prefix + 'animation-duration: ' + animations[this.options.animation[ev]].duration + 'ms;';
|
|
keyframe_css += this.prefix + 'animation-name: jBox-animation-' + this.options.animation[ev] + '-' + ev + (position ? '-' + position : '') + ';';
|
|
keyframe_css += animations[this.options.animation[ev]].timing ? (this.prefix + 'animation-timing-function: ' + animations[this.options.animation[ev]].timing + ';') : '';
|
|
keyframe_css += '}';
|
|
|
|
return keyframe_css;
|
|
}.bind(this);
|
|
|
|
// Generate css for each event and positions
|
|
var css = '';
|
|
jQuery.each(['open', 'close'], function (index, ev) {
|
|
// No CSS needed for closing with no fade
|
|
if (!this.options.animation[ev] || !animations[this.options.animation[ev]] || (ev == 'close' && !this.options.fade)) return '';
|
|
|
|
// Generate CSS
|
|
animations[this.options.animation[ev]].positions ?
|
|
jQuery.each(['top', 'right', 'bottom', 'left'], function (index2, position) { css += generateKeyframeCSS(ev, position); }) :
|
|
css += generateKeyframeCSS(ev);
|
|
}.bind(this));
|
|
|
|
jQuery('<style/>').append(css).appendTo(jQuery('head'));
|
|
};
|
|
|
|
this._blockBodyClick = function () {
|
|
this.blockBodyClick = true;
|
|
setTimeout(function () { this.blockBodyClick = false; }.bind(this), 10);
|
|
};
|
|
|
|
// Add css for animations
|
|
this.options.animation && this._generateCSS();
|
|
|
|
// Animations
|
|
this._animate = function (ev) {
|
|
if (this.IE8) return;
|
|
ev || (ev = this.isOpen ? 'open' : 'close');
|
|
|
|
// Don't animate when closing with no fade duration
|
|
if (!this.options.fade && ev == 'close') return null;
|
|
|
|
// Get the current position, use opposite if jBox is flipped
|
|
var animationDirection = (this.options.animation[ev + 'Direction'] || ((this.align != 'center') ? this.align : this.options.attributes.x));
|
|
this.flipped && this._getXY(animationDirection) == (this._getXY(this.align)) && (animationDirection = this._getOpp(animationDirection));
|
|
|
|
// Add event and position classes
|
|
var classnames = 'jBox-animation-' + this.options.animation[ev] + '-' + ev + ' jBox-animation-' + this.options.animation[ev] + '-' + ev + '-' + animationDirection;
|
|
this.wrapper.addClass(classnames);
|
|
|
|
// Get duration of animation
|
|
var animationDuration = parseFloat(this.wrapper.css(this.prefix + 'animation-duration')) * 1000;
|
|
ev == 'close' && (animationDuration = Math.min(animationDuration, this.options.fade));
|
|
|
|
// Remove animation classes when animation is finished
|
|
setTimeout(function () { this.wrapper.removeClass(classnames); }.bind(this), animationDuration);
|
|
};
|
|
|
|
// Abort animation
|
|
this._abortAnimation = function () {
|
|
if (this.IE8) return;
|
|
|
|
// Remove all animation classes
|
|
var prefix = 'jBox-animation';
|
|
var classes = this.wrapper.attr('class').split(' ').filter(function (c) {
|
|
return c.lastIndexOf(prefix, 0) !== 0;
|
|
});
|
|
this.wrapper.attr('class', classes.join(' '));
|
|
};
|
|
|
|
// Adjust position
|
|
this._adjustPosition = function () {
|
|
if (!this.options.adjustPosition) return null;
|
|
|
|
// Reset cached pointer position
|
|
if (this.positionAdjusted) {
|
|
this.wrapper.css(this.pos);
|
|
this.pointer && this.wrapper.css('padding', 0).css('padding-' + this._getOpp(this.outside), this.pointer.dimensions[this._getXY(this.outside)]).removeClass('jBox-pointerPosition-' + this._getOpp(this.pointer.position)).addClass('jBox-pointerPosition-' + this.pointer.position);
|
|
this.pointer && this.pointer.element.attr('class', 'jBox-pointer jBox-pointer-' + this._getOpp(this.outside)).css(this.pointer.margin);
|
|
this.positionAdjusted = false;
|
|
this.flipped = false;
|
|
}
|
|
|
|
// Get the window dimensions
|
|
var win = jQuery(window);
|
|
var windowDimensions = {
|
|
x: win.width(),
|
|
y: win.height(),
|
|
top: win.scrollTop(),
|
|
left: win.scrollLeft()
|
|
};
|
|
windowDimensions.bottom = windowDimensions.top + windowDimensions.y;
|
|
windowDimensions.right = windowDimensions.left + windowDimensions.x;
|
|
|
|
// Find out where the jBox is out of view area
|
|
var outYT = (windowDimensions.top > this.pos.top - (this.options.adjustDistance.top || 0)),
|
|
outXR = (windowDimensions.right < this.pos.left + this.dimensions.x + (this.options.adjustDistance.right || 0)),
|
|
outYB = (windowDimensions.bottom < this.pos.top + this.dimensions.y + (this.options.adjustDistance.bottom || 0)),
|
|
outXL = (windowDimensions.left > this.pos.left - (this.options.adjustDistance.left || 0)),
|
|
outX = outXL ? 'left' : (outXR ? 'right' : null),
|
|
outY = outYT ? 'top' : (outYB ? 'bottom' : null),
|
|
out = outX || outY;
|
|
|
|
// Stop here if jBox is not out of view area
|
|
if (!out) return;
|
|
|
|
// Flip jBox
|
|
if (this.options.adjustPosition != 'move' && (outX == this.outside || outY == this.outside)) {
|
|
this.target == 'mouse' && (this.outside = 'right');
|
|
|
|
// Check if enough space is availible on opposite position
|
|
if (((this.outside == 'top' || this.outside == 'left') ?
|
|
(windowDimensions[this._getXY(this.outside)] - (this.targetDimensions[this._getTL(this.outside)] - windowDimensions[this._getTL(this.outside)]) - this.targetDimensions[this._getXY(this.outside)]) :
|
|
(this.targetDimensions[this._getTL(this.outside)] - windowDimensions[this._getTL(this.outside)])) > this.dimensions[this._getXY(this.outside)] + this.options.adjustDistance[this._getOpp(this.outside)]) {
|
|
// Adjust wrapper and pointer
|
|
this.wrapper.css(this._getTL(this.outside), this.pos[this._getTL(this.outside)] + ((this.dimensions[this._getXY(this.outside)] + this.options.offset[this._getXY(this.outside)] + this.targetDimensions[this._getXY(this.outside)]) * (this.outside == 'top' || this.outside == 'left' ? 1 : -1))).removeClass('jBox-pointerPosition-' + this.pointer.position).addClass('jBox-pointerPosition-' + this._getOpp(this.pointer.position));
|
|
this.pointer && this.wrapper.css('padding', 0).css('padding-' + this.outside, this.pointer.dimensions[this._getXY(this.outside)]);
|
|
this.pointer && this.pointer.element.attr('class', 'jBox-pointer jBox-pointer-' + this.outside);
|
|
this.positionAdjusted = true;
|
|
this.flipped = true;
|
|
}
|
|
}
|
|
|
|
// Move jBox (only possible with pointer)
|
|
var outMove = (this._getXY(this.outside) == 'x') ? outY : outX;
|
|
|
|
if (this.pointer && this.options.adjustPosition != 'flip' && this._getXY(outMove) == this._getOpp(this._getXY(this.outside))) {
|
|
// Get the maximum space we have availible to adjust
|
|
if (this.pointer.align == 'center') {
|
|
var spaceAvail = (this.dimensions[this._getXY(outMove)] / 2) - (this.pointer.dimensions[this._getOpp(this.pointer.xy)] / 2) - (parseInt(this.pointer.element.css('margin-' + this.pointer.alignAttribute)) * (outMove != this._getTL(outMove) ? -1 : 1));
|
|
} else {
|
|
var spaceAvail = (outMove == this.pointer.alignAttribute) ?
|
|
parseInt(this.pointer.element.css('margin-' + this.pointer.alignAttribute)) :
|
|
this.dimensions[this._getXY(outMove)] - parseInt(this.pointer.element.css('margin-' + this.pointer.alignAttribute)) - this.pointer.dimensions[this._getXY(outMove)];
|
|
}
|
|
|
|
// Get the overlapping space
|
|
spaceDiff = (outMove == this._getTL(outMove)) ?
|
|
windowDimensions[this._getTL(outMove)] - this.pos[this._getTL(outMove)] + this.options.adjustDistance[outMove] :
|
|
(windowDimensions[this._getOpp(this._getTL(outMove))] - this.pos[this._getTL(outMove)] - this.options.adjustDistance[outMove] - this.dimensions[this._getXY(outMove)]) * -1;
|
|
|
|
// Add overlapping space on left or top window edge
|
|
if (outMove == this._getOpp(this._getTL(outMove)) && this.pos[this._getTL(outMove)] - spaceDiff < windowDimensions[this._getTL(outMove)] + this.options.adjustDistance[this._getTL(outMove)]) {
|
|
spaceDiff -= windowDimensions[this._getTL(outMove)] + this.options.adjustDistance[this._getTL(outMove)] - (this.pos[this._getTL(outMove)] - spaceDiff);
|
|
}
|
|
|
|
// Only adjust the maximum availible
|
|
spaceDiff = Math.min(spaceDiff, spaceAvail);
|
|
|
|
// Move jBox
|
|
if (spaceDiff <= spaceAvail && spaceDiff > 0) {
|
|
this.pointer.element.css('margin-' + this.pointer.alignAttribute, parseInt(this.pointer.element.css('margin-' + this.pointer.alignAttribute)) - (spaceDiff * (outMove != this.pointer.alignAttribute ? -1 : 1)));
|
|
this.wrapper.css(this._getTL(outMove), this.pos[this._getTL(outMove)] + (spaceDiff * (outMove != this._getTL(outMove) ? -1 : 1)));
|
|
this.positionAdjusted = true;
|
|
}
|
|
}
|
|
};
|
|
|
|
// Fire onInit event
|
|
(this.options.onInit.bind(this))();
|
|
this.options._onInit && (this.options._onInit.bind(this))();
|
|
|
|
return this;
|
|
};
|
|
|
|
// Attach jBox to elements
|
|
jBox.prototype.attach = function (elements, trigger) {
|
|
elements || (elements = this.options.attach);
|
|
trigger || (trigger = this.options.trigger);
|
|
|
|
elements && elements.length && jQuery.each(elements, function (index, el) {
|
|
el = jQuery(el);
|
|
if (!el.data('jBox-attached-' + this.id)) {
|
|
// Remove title attribute and store content on element
|
|
(this.options.getContent == 'title' && el.attr('title') != undefined) && el.data('jBox-getContent', el.attr('title')).removeAttr('title');
|
|
|
|
// Add Element to collection
|
|
this.attachedElements || (this.attachedElements = []);
|
|
this.attachedElements.push(el[0]);
|
|
|
|
// Add click or mouseenter event, click events can prevent default as well
|
|
el.on(trigger + '.jBox-attach-' + this.id, function (ev) {
|
|
// Only close jBox if you click the current target element, otherwise open at new target
|
|
if (this.isOpen && this.source[0] != el[0]) var forceOpen = true;
|
|
|
|
// Set new source element
|
|
this.source = el;
|
|
|
|
!this.options.target && (this.target = el);
|
|
trigger == 'click' && this.options.preventDefault && ev.preventDefault();
|
|
this[trigger == 'click' && !forceOpen ? 'toggle' : 'open']();
|
|
}.bind(this));
|
|
|
|
// Add close event for mouseenter
|
|
(this.options.trigger == 'mouseenter') && el.on('mouseleave', function () { this.close(); }.bind(this));
|
|
|
|
el.data('jBox-attached-' + this.id, trigger);
|
|
}
|
|
}.bind(this));
|
|
|
|
return this;
|
|
};
|
|
|
|
// Detach jBox from elements
|
|
jBox.prototype.detach = function (elements) {
|
|
elements || (elements = this.attachedElements || []);
|
|
|
|
elements && elements.length && jQuery.each(elements, function (index, el) {
|
|
el = jQuery(el);
|
|
if (el.data('jBox-attached-' + this.id)) {
|
|
el.off(el.data('jBox-attached-' + this.id + '.jBox-attach-' + this.id));
|
|
el.data('jBox-attached-' + this.id, null);
|
|
}
|
|
});
|
|
return this;
|
|
};
|
|
|
|
// Set title
|
|
jBox.prototype.setTitle = function (title) {
|
|
if (title == null || title == undefined) return this;
|
|
!this.wrapper && this._create();
|
|
if (!this.title) {
|
|
this.titleContainer = jQuery('<div/>', { 'class': 'jBox-title' });
|
|
this.title = jQuery('<div/>').appendTo(this.titleContainer);
|
|
this.wrapper.addClass('jBox-hasTitle');
|
|
if (this.options.closeButton == 'title') {
|
|
this.wrapper.addClass('jBox-closeButton-title');
|
|
this.closeButton.appendTo(this.titleContainer);
|
|
}
|
|
this.titleContainer.insertBefore(this.content);
|
|
}
|
|
this.title.html(title);
|
|
this.position();
|
|
return this;
|
|
};
|
|
|
|
// Set content
|
|
jBox.prototype.setContent = function (content) {
|
|
if (content == null) return this;
|
|
!this.wrapper && this._create();
|
|
switch (jQuery.type(content)) {
|
|
case 'string': this.content.html(content); break;
|
|
case 'object': this.content.children().css({ display: 'none' }); this.options.content.appendTo(this.content).css({ display: 'block' }); break;
|
|
}
|
|
this.position();
|
|
return this;
|
|
};
|
|
|
|
// Position jBox
|
|
jBox.prototype.position = function (options) {
|
|
options || (options = {});
|
|
|
|
// Get target
|
|
this.target = options.target || this.target || this.options.target || jQuery(window);
|
|
|
|
// Cache total current dimensions of jBox
|
|
this.dimensions = {
|
|
x: this.wrapper.outerWidth(),
|
|
y: this.wrapper.outerHeight()
|
|
};
|
|
|
|
// Mousemouve can't be positioned
|
|
if (this.target == 'mouse') return;
|
|
|
|
// Set percent and margin for centered inside
|
|
if (this.options.position.x == 'center' && this.options.position.y == 'center') {
|
|
this.wrapper.css({ left: '50%', top: '50%', marginLeft: (this.dimensions.x * -0.5 + this.options.offset.x), marginTop: (this.dimensions.y * -0.5 + this.options.offset.y) });
|
|
return this;
|
|
}
|
|
|
|
// Total current dimensions of target element
|
|
var targetOffset = this.target.offset();
|
|
this.targetDimensions = {
|
|
x: this.target.outerWidth(),
|
|
y: this.target.outerHeight(),
|
|
top: (targetOffset ? targetOffset.top : 0),
|
|
left: (targetOffset ? targetOffset.left : 0)
|
|
};
|
|
|
|
this.pos = {};
|
|
|
|
// Calculate positions
|
|
var setPosition = function (p) {
|
|
// Set number positions
|
|
if (jQuery.inArray(this.options.position[p], ['top', 'right', 'bottom', 'left', 'center']) == -1) {
|
|
this.pos[this.options.attributes[p]] = this.options.position[p];
|
|
return;
|
|
}
|
|
|
|
// We have a target, so use 'left' or 'top' as attributes
|
|
var a = this.options.attributes[p] = (p == 'x' ? 'left' : 'top');
|
|
|
|
// Start at target position
|
|
this.pos[a] = this.targetDimensions[a];
|
|
|
|
// Set centered position
|
|
if (this.options.position[p] == 'center') {
|
|
this.pos[a] += Math.ceil((this.targetDimensions[p] - this.dimensions[p]) / 2);
|
|
return;
|
|
}
|
|
|
|
// Move inside
|
|
(a != this.options.position[p]) && (this.pos[a] += this.targetDimensions[p] - this.dimensions[p]);
|
|
|
|
// Move outside
|
|
(this.options.outside == p || this.options.outside == 'xy') && (this.pos[a] += this.dimensions[p] * (a != this.options.position[p] ? 1 : -1));
|
|
}.bind(this);
|
|
|
|
// Set position including offset
|
|
setPosition('x');
|
|
setPosition('y');
|
|
|
|
// Adjust position depending on pointer align
|
|
if (this.options.pointer) {
|
|
var adjustWrapper = 0;
|
|
|
|
// Where is the pointer aligned? Add or substract accordingly
|
|
switch (this.pointer.align) {
|
|
case 'center':
|
|
if (this.options.position[this._getOpp(this.options.outside)] != 'center') {
|
|
adjustWrapper += (this.dimensions[this._getOpp(this.options.outside)] / 2);
|
|
}
|
|
break;
|
|
default:
|
|
switch (this.options.position[this._getOpp(this.options.outside)]) {
|
|
case 'center':
|
|
adjustWrapper += ((this.dimensions[this._getOpp(this.options.outside)] / 2) - (this.pointer.dimensions[this._getOpp(this.options.outside)] / 2)) * (this.pointer.align == this._getTL(this.pointer.align) ? 1 : -1);
|
|
break;
|
|
default:
|
|
adjustWrapper += (this.pointer.align != this.options.position[this._getOpp(this.options.outside)]) ?
|
|
this.dimensions[this._getOpp(this.options.outside)] - (this.pointer.dimensions[this._getOpp(this.options.outside)] / 2) :
|
|
(this.pointer.dimensions[this._getOpp(this.options.outside)] / 2);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
adjustWrapper *= (this.options.position[this._getOpp(this.options.outside)] == this.pointer.alignAttribute ? -1 : 1);
|
|
adjustWrapper += this.pointer.offset * (this.pointer.align == this._getOpp(this._getTL(this.pointer.align)) ? 1 : -1);
|
|
|
|
this.pos[this._getTL(this._getOpp(this.pointer.xy))] += adjustWrapper;
|
|
}
|
|
|
|
// Add final offset
|
|
this.pos[this.options.attributes.x] += this.options.offset.x;
|
|
this.pos[this.options.attributes.y] += this.options.offset.y;
|
|
|
|
// Set CSS
|
|
this.wrapper.css(this.pos);
|
|
|
|
// Adjust position
|
|
this._adjustPosition();
|
|
|
|
return this;
|
|
};
|
|
|
|
// Open jBox
|
|
jBox.prototype.open = function (options) {
|
|
options || (options = {});
|
|
|
|
// Construct jBox if not already constructed
|
|
!this.wrapper && this._create();
|
|
|
|
// Abort any opening or closing timer
|
|
this.timer && clearTimeout(this.timer);
|
|
|
|
// Block body click for 10ms, so jBox can open on attached elements while closeOnClick = 'body'
|
|
this._blockBodyClick();
|
|
|
|
// Block opening
|
|
if (this.isDisabled) return this;
|
|
|
|
// Opening function
|
|
var open = function () {
|
|
// Set title from source element
|
|
this.source && this.options.getTitle && (this.source.attr(this.options.getTitle) != undefined && this.setTitle(this.source.attr(this.options.getTitle)));
|
|
|
|
// Set content from source element
|
|
this.source && this.options.getContent && (this.source.data('jBox-getContent') != undefined ? this.setContent(this.source.data('jBox-getContent')) : (this.source.attr(this.options.getContent) != undefined ? this.setContent(this.source.attr(this.options.getContent)) : null));
|
|
|
|
// Set position
|
|
this.position({ target: options.target });
|
|
|
|
// Fire onOpen event
|
|
(this.options.onOpen.bind(this))();
|
|
this.options._onOpen && (this.options._onOpen.bind(this))();
|
|
|
|
// Get content from ajax
|
|
this.options.ajax && (!this.ajaxLoaded || this.options.reload) && this.ajax();
|
|
|
|
// Abort closing
|
|
this.isClosing && this._abortAnimation();
|
|
|
|
// Open functions to call when jBox is closed
|
|
if (!this.isOpen) {
|
|
// jBox is open now
|
|
this.isOpen = true;
|
|
|
|
// Attach events
|
|
this._attachEvents();
|
|
|
|
// Block scrolling
|
|
this.options.blockScroll && jQuery('body').addClass('jBox-blockScroll-' + this.id);
|
|
|
|
// Add overlay
|
|
this.options.overlay && this._addOverlay();
|
|
|
|
// Only animate if jBox is compleately closed
|
|
this.options.animation && !this.isClosing && this._animate('open');
|
|
|
|
// Fading animation or show immediately
|
|
if (this.options.fade) {
|
|
this.wrapper.stop().animate({ opacity: 1 }, {
|
|
queue: false,
|
|
duration: this.options.fade,
|
|
start: function () {
|
|
this.isOpening = true;
|
|
this.wrapper.css({ display: 'block' });
|
|
}.bind(this),
|
|
always: function () {
|
|
this.isOpening = false;
|
|
}.bind(this)
|
|
});
|
|
} else {
|
|
this.wrapper.css({ display: 'block', opacity: 1 });
|
|
}
|
|
}
|
|
}.bind(this);
|
|
|
|
// Open jBox
|
|
this.options.delayOpen && !this.isOpen && !this.isClosing && !options.ignoreDelay ? (this.timer = setTimeout(open, this.options.delayOpen)) : open();
|
|
|
|
return this;
|
|
};
|
|
|
|
// Close jBox
|
|
jBox.prototype.close = function (options) {
|
|
options || (options = {});
|
|
|
|
// Abort opening
|
|
this.timer && clearTimeout(this.timer);
|
|
|
|
// Block body click for 10ms, so jBox can open on attached elements while closeOnClock = 'body' is true
|
|
this._blockBodyClick();
|
|
|
|
// Block closing
|
|
if (this.isDisabled) return this;
|
|
|
|
// Close function
|
|
var close = function () {
|
|
// Fire onClose event
|
|
(this.options.onClose.bind(this))();
|
|
|
|
// Only close if jBox is open
|
|
if (this.isOpen) {
|
|
// jBox is not open anymore
|
|
this.isOpen = false;
|
|
|
|
// Detach events
|
|
this._detachEvents();
|
|
|
|
// Unblock scrolling
|
|
this.options.blockScroll && jQuery('body').removeClass('jBox-blockScroll-' + this.id);
|
|
|
|
// Remove overlay
|
|
this.options.overlay && this._removeOverlay();
|
|
|
|
// Only animate if jBox is compleately closed
|
|
this.options.animation && !this.isOpening && this._animate('close');
|
|
|
|
// Remove source element
|
|
this.source = null;
|
|
|
|
// Fading animation or show immediately
|
|
if (this.options.fade) {
|
|
this.wrapper.stop().animate({ opacity: 0 }, {
|
|
queue: false,
|
|
duration: this.options.fade,
|
|
start: function () {
|
|
this.isClosing = true;
|
|
}.bind(this),
|
|
complete: function () {
|
|
this.wrapper.css({ display: 'none' });
|
|
this.options._onCloseComplete && (this.options._onCloseComplete.bind(this))();
|
|
}.bind(this),
|
|
always: function () {
|
|
this.isClosing = false;
|
|
}.bind(this),
|
|
});
|
|
} else {
|
|
this.wrapper.css({ display: 'none', opacity: 0 });
|
|
this.options._onCloseComplete && (this.options._onCloseComplete.bind(this))();
|
|
}
|
|
}
|
|
}.bind(this);
|
|
|
|
// Close jBox
|
|
options.ignoreDelay ? close() : (this.timer = setTimeout(close, Math.max(this.options.delayClose, 10)));
|
|
|
|
return this;
|
|
};
|
|
|
|
// Open or close jBox
|
|
jBox.prototype.toggle = function (options) {
|
|
this[this.isOpen ? 'close' : 'open'](options);
|
|
return this;
|
|
};
|
|
|
|
// Block opening and closing
|
|
jBox.prototype.disable = function () {
|
|
this.isDisabled = true;
|
|
return this;
|
|
};
|
|
|
|
// Unblock opening and closing
|
|
jBox.prototype.enable = function () {
|
|
this.isDisabled = false;
|
|
return this;
|
|
};
|
|
|
|
// Get content from ajax
|
|
jBox.prototype.ajax = function (options) {
|
|
options || (options = {});
|
|
|
|
// Abort running ajax calls
|
|
this.ajaxRequest && this.ajaxRequest.abort();
|
|
|
|
// Set new ajax call
|
|
this.ajaxRequest = jQuery.ajax({
|
|
url: options.url || this.options.ajax,
|
|
data: options.data || this.options.data,
|
|
beforeSend: function () {
|
|
// Clear content, add spinner and reposition jBox
|
|
this.content.html('');
|
|
this.wrapper.addClass('jBox-loading');
|
|
this.position();
|
|
|
|
// Fire onAjax event
|
|
(this.options.onAjax.bind(this))();
|
|
}.bind(this),
|
|
complete: function (response) {
|
|
// Remove spinner, set content and reposition jBox
|
|
this.wrapper.removeClass('jBox-loading');
|
|
this.content.html(response.responseText);
|
|
this.position();
|
|
this.ajaxLoaded = true;
|
|
|
|
// Fire onAjaxComplete event
|
|
(this.options.onAjaxComplete.bind(this))();
|
|
}.bind(this)
|
|
});
|
|
return this;
|
|
};
|
|
|
|
// Destroy jBox and remove it from DOM
|
|
jBox.prototype.destroy = function () {
|
|
this.close({ ignoreDelay: true });
|
|
this.wrapper.remove();
|
|
return this;
|
|
};
|
|
|
|
// Make jBox usable with jQuery selectors
|
|
jQuery.fn.jBox = function (type, options) {
|
|
type || (type = {});
|
|
options || (options = {});
|
|
return new jBox(type, jQuery.extend(options, { attach: this }));
|
|
};
|
|
|
|
// Add the .bind() function for IE 8 support
|
|
if (!Function.prototype.bind) {
|
|
Function.prototype.bind = function (oThis) {
|
|
var aArgs = Array.prototype.slice.call(arguments, 1),
|
|
fToBind = this,
|
|
fNOP = function () { },
|
|
fBound = function () { return fToBind.apply(this instanceof fNOP && oThis ? this : oThis, aArgs.concat(Array.prototype.slice.call(arguments))); };
|
|
fNOP.prototype = this.prototype;
|
|
fBound.prototype = new fNOP();
|
|
return fBound;
|
|
};
|
|
}
|