231 lines
5.3 KiB
JavaScript
231 lines
5.3 KiB
JavaScript
/**
|
|
* A simple jQuery plugin for creating animated drilldown menus.
|
|
*
|
|
* @name jQuery Drilldown
|
|
* @version 1.0.0
|
|
* @requires jQuery v1.7+
|
|
* @author Aleksandras Nelkinas
|
|
* @license [MIT]{@link http://opensource.org/licenses/mit-license.php}
|
|
*
|
|
* Copyright (c) 2015 Aleksandras Nelkinas
|
|
*/
|
|
|
|
;(function (factory) {
|
|
if (typeof define === 'function' && define.amd) {
|
|
// AMD
|
|
define(['jquery'], factory);
|
|
} else if (typeof exports === 'object') {
|
|
// Node/CommonJS
|
|
module.exports = factory(require('jquery'));
|
|
} else {
|
|
// Browser globals
|
|
factory(jQuery);
|
|
}
|
|
}(function ($, undefined) {
|
|
|
|
'use strict';
|
|
|
|
var pluginName = 'drilldown';
|
|
var defaults = {
|
|
event: 'click',
|
|
selector: 'a',
|
|
speed: 100,
|
|
cssClass: {
|
|
container: pluginName + '-container',
|
|
root: pluginName + '-root',
|
|
sub: pluginName + '-sub',
|
|
back: pluginName + '-back'
|
|
}
|
|
};
|
|
|
|
var Plugin = (function () {
|
|
|
|
function Plugin(element, options) {
|
|
var inst = this;
|
|
|
|
this._name = pluginName;
|
|
this._defaults = defaults;
|
|
|
|
this.element = element;
|
|
this.$element = $(element);
|
|
|
|
this.options = $.extend({}, defaults, options);
|
|
|
|
this._history = [];
|
|
this._css = {
|
|
float: 'left',
|
|
width: null
|
|
};
|
|
|
|
this.$container = this.$element.find('.' + this.options.cssClass.container);
|
|
|
|
this.$element.on(this.options.event + '.' + pluginName, this.options.selector, function (e) {
|
|
handleAction.call(inst, e, $(this));
|
|
});
|
|
}
|
|
|
|
Plugin.prototype = {
|
|
|
|
/**
|
|
* Destroys plugin instance.
|
|
*/
|
|
destroy: function () {
|
|
this.reset();
|
|
|
|
this.$element.off(this.options.event + '.' + pluginName, this.options.selector);
|
|
},
|
|
|
|
/**
|
|
* Resets drilldown to its initial state.
|
|
*/
|
|
reset: function () {
|
|
var $root;
|
|
|
|
if (this._history.length) {
|
|
$root = this._history[0];
|
|
|
|
this.$container.empty().append($root);
|
|
restoreState.call(this, $root);
|
|
}
|
|
|
|
this._history = [];
|
|
this._css = {
|
|
float: 'left',
|
|
width: null
|
|
};
|
|
}
|
|
|
|
};
|
|
|
|
/**
|
|
* Handles user action and decides whether or not and where to drill.
|
|
*
|
|
* @param {jQuery.Event} e
|
|
* @param {jQuery} $trigger
|
|
* @private
|
|
*/
|
|
function handleAction(e, $trigger) {
|
|
var $next = $trigger.nextAll('.' + this.options.cssClass.sub);
|
|
var preventDefault = true;
|
|
|
|
if ($next.length) {
|
|
down.call(this, $next);
|
|
} else if ($trigger.closest('.' + this.options.cssClass.back).length) {
|
|
up.call(this);
|
|
} else {
|
|
preventDefault = false;
|
|
}
|
|
|
|
if (preventDefault && $trigger.prop('tagName') === 'A') {
|
|
e.preventDefault();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Drills down (deeper).
|
|
*
|
|
* @param {jQuery} $next
|
|
* @private
|
|
*/
|
|
function down($next) {
|
|
if (!$next.length) {
|
|
return;
|
|
}
|
|
|
|
this._css.width = this.$element.outerWidth();
|
|
this.$container.width(this._css.width * 2);
|
|
|
|
$next = $next.clone(true)
|
|
.removeClass(this.options.cssClass.sub)
|
|
.addClass(this.options.cssClass.root);
|
|
|
|
this.$container.append($next);
|
|
|
|
animateDrilling.call(this, -1 * this._css.width, function () {
|
|
var $current = $next.prev();
|
|
|
|
this._history.push($current.detach());
|
|
|
|
restoreState.call(this, $next);
|
|
}.bind(this));
|
|
}
|
|
|
|
/**
|
|
* Drills up (back).
|
|
*
|
|
* @private
|
|
*/
|
|
function up() {
|
|
var $next = this._history.pop();
|
|
|
|
this._css.width = this.$element.outerWidth();
|
|
this.$container.width(this._css.width * 2);
|
|
|
|
this.$container.prepend($next);
|
|
|
|
animateDrilling.call(this, 0, function () {
|
|
var $current = $next.next();
|
|
|
|
$current.remove();
|
|
|
|
restoreState.call(this, $next);
|
|
}.bind(this));
|
|
}
|
|
|
|
/**
|
|
* Animates drilling process.
|
|
*
|
|
* @param {Number} marginLeft Target margin-left.
|
|
* @param {Function} callback
|
|
* @private
|
|
*/
|
|
function animateDrilling(marginLeft, callback) {
|
|
var $menus = this.$container.children('.' + this.options.cssClass.root);
|
|
|
|
$menus.css(this._css);
|
|
|
|
$menus.first().animate({
|
|
marginLeft: marginLeft
|
|
}, this.options.speed, callback);
|
|
}
|
|
|
|
/**
|
|
* Restores initial menu's state.
|
|
*
|
|
* @param {jQuery} $menu
|
|
* @private
|
|
*/
|
|
function restoreState($menu) {
|
|
$menu.css({
|
|
float: '',
|
|
width: '',
|
|
marginLeft: ''
|
|
});
|
|
|
|
this.$container.css('width', '');
|
|
}
|
|
|
|
return Plugin;
|
|
|
|
})();
|
|
|
|
$.fn[pluginName] = function (options) {
|
|
return this.each(function () {
|
|
var inst = $.data(this, pluginName);
|
|
var method = options;
|
|
|
|
if (!inst) {
|
|
$.data(this, pluginName, new Plugin(this, options));
|
|
} else if (typeof method === 'string') {
|
|
if (method === 'destroy') {
|
|
$.removeData(this, pluginName);
|
|
}
|
|
if (typeof inst[method] === 'function') {
|
|
inst[method]();
|
|
}
|
|
}
|
|
});
|
|
};
|
|
|
|
}));
|