Browse files

Added code documentation

  • Loading branch information...
1 parent c6e0093 commit a40162c7287874397e68fe8d24d43e90de7574fb @tefra committed Jul 8, 2013
Showing with 140 additions and 36 deletions.
  1. +138 −34 src/jquery.navgoco.js
  2. +2 −2 src/jquery.navgoco.min.js
View
172 src/jquery.navgoco.js
@@ -1,5 +1,5 @@
/*
- * jQuery Navgoco Menus Plugin v0.1.1 (2013-07-08)
+ * jQuery Navgoco Menus Plugin v0.1.1 (2013-07-09)
* https://github.com/tefra/navgoco
*
* Copyright (c) 2013 Chris T
@@ -9,29 +9,48 @@
"use strict";
+ /**
+ * Plugin Constructor. Every menu must have a unique id which will either
+ * be the actual id attribute or its index in the page.
+ *
+ * @param {Element} el
+ * @param {Object} options
+ * @param {Integer} idx
+ * @returns {Object} Plugin Instance
+ */
var Plugin = function(el, options, idx) {
this.el = el;
this.$el = $(el);
this.options = options;
this.uuid = this.$el.attr('id') ? this.$el.attr('id') : idx;
- this.open = {};
+ this.state = {};
this.init();
return this;
};
+ /**
+ * Plugin methods
+ */
Plugin.prototype = {
+ /**
+ * Load cookie, assign a unique data-index attribute to
+ * all sub-menus and show|hide them according to cookie
+ * or based on the parent open class. Find all parent li > a
+ * links add the carent if it's on and attach the event click
+ * to them.
+ */
init: function() {
var self = this;
self._load();
self.$el.find('ul').each(function(idx) {
var sub = $(this);
sub.attr('data-index', idx);
- if (self.options.save && self.open.hasOwnProperty(idx)) {
+ if (self.options.save && self.state.hasOwnProperty(idx)) {
sub.parent().addClass(self.options.openClass);
sub.show();
} else if (sub.parent().hasClass(self.options.openClass)) {
sub.show();
- this.open[idx] = 1;
+ self.state[idx] = 1;
} else {
sub.hide();
}
@@ -41,32 +60,36 @@
if (self.options.caret) {
links.append(self.options.caret);
}
- links.click(function(e) {
+ links.on('click', function(e) {
e.preventDefault();
var sub = $(this).next();
var isOpen = sub.is(":visible");
self._toggle(sub, !isOpen);
self._save();
});
},
+ /**
+ * Accepts a JQuery Element and a boolean flag. If flag is false it removes from the
+ * parent li the `open` css class and slides up the sub-menu. If flag is open it adds to
+ * the parent li the `open` css class and slides down the menu. If accordion mode is on all
+ * sub-menus except the direct parent tree will close. Internally an object with the menus
+ * states is maintained for later save duty.
+ *
+ * @param {Element} sub
+ * @param {Boolean} open
+ */
_toggle: function(sub, open) {
var self = this;
var idx = sub.attr('data-index');
var parent = sub.parent();
if (open) {
parent.addClass(self.options.openClass);
sub.slideDown(self.options.slide);
- self.open[idx] = 1;
+ self.state[idx] = 1;
if (self.options.accordion) {
- var parents = parent.parents("ul");
- var allowed = {};
- allowed[idx] = 1;
- parents.each(function() {
- var idx = $(this).attr('data-index');
- allowed[idx] = 1;
- self.open[idx] = 1;
- });
+ var allowed = self.state = self._parents(sub);
+ allowed[idx] = self.state[idx] = 1;
self.$el.find("ul:visible").each(function() {
var sub = $(this);
@@ -79,67 +102,140 @@
} else {
parent.removeClass(self.options.openClass);
sub.slideUp(self.options.slide);
- this.open[idx] = 0;
+ self.state[idx] = 0;
}
+
+
},
+ /**
+ * Returns all parents of a sub-menu. When obj is true It returns an object with indexes for
+ * keys and the elements as values, if obj is false the object is filled with the value `1`.
+ *
+ * @since v0.1.2
+ * @param {Element} sub
+ * @param {Boolean} obj
+ * @returns {Object}
+ */
+ _parents: function(sub, obj) {
+ var result = {};
+ var parent = sub.parent();
+ var parents = parent.parents('ul');
+
+ parents.each(function() {
+ var par = $(this);
+ var idx = $(this).attr('data-index');
+ if (!idx) {
+ return false;
+ }
+ result[idx] = obj ? par : 1;
+ });
+ return result;
+ },
+ /**
+ * If `save` option is on the internal object that keeps track of the sub-menus states is
+ * saved with a cookie. For size reasons only the open sub-menus indexes are stored. *
+ */
_save: function() {
if (this.options.save) {
var save = {};
- for (var key in this.open) {
- if (this.open[key] === 1) {
+ for (var key in this.state) {
+ if (this.state[key] === 1) {
save[key] = 1;
}
}
- cookie[this.uuid] = save;
+ cookie[this.uuid] = this.state = save;
$.cookie(this.options.cookie.name, JSON.stringify(cookie), this.options.cookie);
}
},
+ /**
+ * If `save` option is on it reads the cookie data. The cookie contains data for all
+ * navgoco menus so the read happens only once and stored in the global `cookie` var.
+ */
_load: function() {
if (this.options.save) {
if (cookie === null) {
var data = $.cookie(this.options.cookie.name);
cookie = (data) ? JSON.parse(data) : {};
}
- this.open = cookie.hasOwnProperty(this.uuid) ? cookie[this.uuid] : {};
+ this.state = cookie.hasOwnProperty(this.uuid) ? cookie[this.uuid] : {};
}
},
+ /**
+ * Public method toggle to manually show|hide sub-menus. If no indexes are provided all
+ * items will be toggled. You can pass sub-menus indexes as regular params. eg:
+ * navgoco('toggle', true, 1, 2, 3, 4, 5);
+ *
+ * Since v0.1.2 it will also open parents when providing sub-menu indexes.
+ *
+ * @param {Boolean} open
+ */
toggle: function(open) {
var self = this;
- var list = {};
- var args = Array.prototype.slice.call(arguments, 1);
- var length = args.length;
-
- for (var i = 0; i < length; i++) {
- list[args[i]] = true;
- }
+ var length = arguments.length;
- self.$el.find('ul').each(function() {
- var sub = $(this);
- var idx = sub.attr('data-index');
- if (length === 0 || list.hasOwnProperty(idx)) {
+ if (length <= 1) {
+ self.$el.find('ul').each(function() {
+ var sub = $(this);
self._toggle(sub, open);
- if (length === 1) {
- return false;
+ });
+ } else {
+ var idx;
+ var list = {};
+ var args = Array.prototype.slice.call(arguments, 1);
+ length -= 1;
+
+ for (var i = 0; i < length; i++) {
+ idx = args[i];
+ var sub = self.$el.find('ul[data-index="' + idx + '"]').first();
+ if (sub) {
+ list[idx] = sub;
+ if (open) {
+ var parents = self._parents(sub, true);
+ for (var pIdx in parents) {
+ if (!list.hasOwnProperty(pIdx)) {
+ list[pIdx] = parents[pIdx];
+ }
+ }
+ }
}
}
- });
+
+ for (idx in list) {
+ self._toggle(list[idx], open);
+ }
+ }
+
self._save();
},
+ /**
+ * Removes instance from JQuery data cache and unbinds events.
+ */
destroy: function() {
$.removeData(this.$el);
this.$el.find("li:has(ul) > a").unbind('click');
}
};
+ /**
+ * Global var holding all navgoco menus open states
+ *
+ * @type {Object}
+ */
var cookie = null;
+
+ /**
+ * Default navgoco options
+ *
+ * @type {Object}
+ */
var defaults = {
caret: '<span class="caret"></span>',
accordion: false,
openClass: 'open',
save: true,
cookie: {
name: 'navgoco',
- expires: 0,
+ expires: false,
path: '/'
},
slide: {
@@ -148,6 +244,13 @@
}
};
+ /**
+ * A JQuery plugin wrapper for navgoco. It prevents from multiple instances and also handles
+ * public methods calls. If we attempt to call a public method on an element that doesn't have
+ * a navgoco instance, one will be created for it with the default options.
+ *
+ * @param {Object|String} options
+ */
$.fn.navgoco = function(options) {
if (typeof options === 'string' && options.charAt(0) !== '_' && options !== 'init') {
var callback = true;
@@ -161,6 +264,7 @@
return this.each(function(idx) {
var $this = $(this);
var obj = $this.data('navgoco');
+
if (!obj) {
obj = new Plugin(this, callback ? defaults : options, idx);
$this.data('navgoco', obj);
View
4 src/jquery.navgoco.min.js
@@ -1,8 +1,8 @@
/*
- * jQuery Navgoco Menus Plugin v0.1.1 (2013-07-08)
+ * jQuery Navgoco Menus Plugin v0.1.1 (2013-07-09)
* https://github.com/tefra/navgoco
*
* Copyright (c) 2013 Chris T
* BSD - https://github.com/tefra/navgoco/blob/master/LICENSE-BSD
*/
-!function(a){"use strict";var b=function(b,c,d){return this.el=b,this.$el=a(b),this.options=c,this.uuid=this.$el.attr("id")?this.$el.attr("id"):d,this.open={},this.init(),this};b.prototype={init:function(){var b=this;b._load(),b.$el.find("ul").each(function(c){var d=a(this);d.attr("data-index",c),b.options.save&&b.open.hasOwnProperty(c)?(d.parent().addClass(b.options.openClass),d.show()):d.parent().hasClass(b.options.openClass)?(d.show(),this.open[c]=1):d.hide()});var c=b.$el.find("li:has(ul) > a");b.options.caret&&c.append(b.options.caret),c.click(function(c){c.preventDefault();var d=a(this).next(),e=d.is(":visible");b._toggle(d,!e),b._save()})},_toggle:function(b,c){var d=this,e=b.attr("data-index"),f=b.parent();if(c){if(f.addClass(d.options.openClass),b.slideDown(d.options.slide),d.open[e]=1,d.options.accordion){var g=f.parents("ul"),h={};h[e]=1,g.each(function(){var b=a(this).attr("data-index");h[b]=1,d.open[b]=1}),d.$el.find("ul:visible").each(function(){var b=a(this),c=b.attr("data-index");h.hasOwnProperty(c)||d._toggle(b,!1)})}}else f.removeClass(d.options.openClass),b.slideUp(d.options.slide),this.open[e]=0},_save:function(){if(this.options.save){var b={};for(var d in this.open)1===this.open[d]&&(b[d]=1);c[this.uuid]=b,a.cookie(this.options.cookie.name,JSON.stringify(c),this.options.cookie)}},_load:function(){if(this.options.save){if(null===c){var b=a.cookie(this.options.cookie.name);c=b?JSON.parse(b):{}}this.open=c.hasOwnProperty(this.uuid)?c[this.uuid]:{}}},toggle:function(b){for(var c=this,d={},e=Array.prototype.slice.call(arguments,1),f=e.length,g=0;f>g;g++)d[e[g]]=!0;c.$el.find("ul").each(function(){var e=a(this),g=e.attr("data-index");return 0!==f&&!d.hasOwnProperty(g)||(c._toggle(e,b),1!==f)?void 0:!1}),c._save()},destroy:function(){a.removeData(this.$el),this.$el.find("li:has(ul) > a").unbind("click")}};var c=null,d={caret:'<span class="caret"></span>',accordion:!1,openClass:"open",save:!0,cookie:{name:"navgoco",expires:0,path:"/"},slide:{duration:400,easing:"swing"}};a.fn.navgoco=function(c){if("string"==typeof c&&"_"!==c.charAt(0)&&"init"!==c)var e=!0,f=Array.prototype.slice.call(arguments,1);else c=a.extend({},d,c||{}),a.cookie||(c.save=!1);return this.each(function(g){var h=a(this),i=h.data("navgoco");i||(i=new b(this,e?d:c,g),h.data("navgoco",i)),e&&i[c].apply(i,f)})}}(jQuery);
+!function(a){"use strict";var b=function(b,c,d){return this.el=b,this.$el=a(b),this.options=c,this.uuid=this.$el.attr("id")?this.$el.attr("id"):d,this.state={},this.init(),this};b.prototype={init:function(){var b=this;b._load(),b.$el.find("ul").each(function(c){var d=a(this);d.attr("data-index",c),b.options.save&&b.state.hasOwnProperty(c)?(d.parent().addClass(b.options.openClass),d.show()):d.parent().hasClass(b.options.openClass)?(d.show(),b.state[c]=1):d.hide()});var c=b.$el.find("li:has(ul) > a");b.options.caret&&c.append(b.options.caret),c.on("click",function(c){c.preventDefault();var d=a(this).next(),e=d.is(":visible");b._toggle(d,!e),b._save()})},_toggle:function(b,c){var d=this,e=b.attr("data-index"),f=b.parent();if(c){if(f.addClass(d.options.openClass),b.slideDown(d.options.slide),d.state[e]=1,d.options.accordion){var g=d.state=d._parents(b);g[e]=d.state[e]=1,d.$el.find("ul:visible").each(function(){var b=a(this),c=b.attr("data-index");g.hasOwnProperty(c)||d._toggle(b,!1)})}}else f.removeClass(d.options.openClass),b.slideUp(d.options.slide),d.state[e]=0},_parents:function(b,c){var d={},e=b.parent(),f=e.parents("ul");return f.each(function(){var b=a(this),e=a(this).attr("data-index");return e?(d[e]=c?b:1,void 0):!1}),d},_save:function(){if(this.options.save){var b={};for(var d in this.state)1===this.state[d]&&(b[d]=1);c[this.uuid]=this.state=b,a.cookie(this.options.cookie.name,JSON.stringify(c),this.options.cookie)}},_load:function(){if(this.options.save){if(null===c){var b=a.cookie(this.options.cookie.name);c=b?JSON.parse(b):{}}this.state=c.hasOwnProperty(this.uuid)?c[this.uuid]:{}}},toggle:function(b){var c=this,d=arguments.length;if(1>=d)c.$el.find("ul").each(function(){var d=a(this);c._toggle(d,b)});else{var e,f={},g=Array.prototype.slice.call(arguments,1);d-=1;for(var h=0;d>h;h++){e=g[h];var i=c.$el.find('ul[data-index="'+e+'"]').first();if(i&&(f[e]=i,b)){var j=c._parents(i,!0);for(var k in j)f.hasOwnProperty(k)||(f[k]=j[k])}}for(e in f)c._toggle(f[e],b)}c._save()},destroy:function(){a.removeData(this.$el),this.$el.find("li:has(ul) > a").unbind("click")}};var c=null,d={caret:'<span class="caret"></span>',accordion:!1,openClass:"open",save:!0,cookie:{name:"navgoco",expires:!1,path:"/"},slide:{duration:400,easing:"swing"}};a.fn.navgoco=function(c){if("string"==typeof c&&"_"!==c.charAt(0)&&"init"!==c)var e=!0,f=Array.prototype.slice.call(arguments,1);else c=a.extend({},d,c||{}),a.cookie||(c.save=!1);return this.each(function(g){var h=a(this),i=h.data("navgoco");i||(i=new b(this,e?d:c,g),h.data("navgoco",i)),e&&i[c].apply(i,f)})}}(jQuery);

0 comments on commit a40162c

Please sign in to comment.