1/** 2 * waSlideMenu - jQuery plugin for menu organization like Facebook Help Page. 3 * @version v1.0.7 4 * @link https://github.com/webasyst/waslidemenu 5 * @license MIT 6 */ 7/*global $, jQuery*/ 8(function ($, window, document, undefined) { 9 "use strict"; 10 11 $.waSlideMenu = function (el, options) { 12 var pluginName = 'SlideMenu', 13 base = this, 14 element = el; 15 16 // Apply namespace to css classes 17 base._addCssNamespace = function (attrNames, namespace) { 18 var obj = {}; 19 $.each(attrNames, function (k, v) { 20 if (typeof v === 'string') { 21 obj[k] = namespace + pluginName + v; 22 } 23 }); 24 25 return obj; 26 }; 27 // setup DOM elements, variables 28 base._setup = function () { 29 base.o = $.extend({}, $.waSlideMenu.defaults, options); 30 31 base.o.classNames = base._addCssNamespace(base.o.classNames, base.o.namespace); 32 33 // Access to jQuery and DOM versions of element 34 base.$waSlideMenu = $(el); 35 36 // Add reverse reference object 37 base.$waSlideMenu.data(pluginName, base); 38 39 base.$backs = []; // backlink menu items 40 base.$items = base.$waSlideMenu.find(base.o.itemSelector); 41 base.$waSlideMenu.addClass(base.o.classNames.navigationClass); 42 43 // wrap all navigation 44 base.$movable = $('<div/>', { 45 'class' : base.o.classNames.wrapperClass 46 }); 47 // detach? 48 // root and all other inherited menus 49 base.$inheritedMenus = base.$waSlideMenu.find(base.o.menuSelector); 50 base.$rootMenu = $(base.$inheritedMenus.splice(0, 1)); 51 52 base.$rootMenu 53 .addClass(base.o.classNames.allMenusClass) 54 .appendTo(base.$movable.prependTo(base.$waSlideMenu)); 55 // add css to menus for floating left to right 56 base.$inheritedMenus.addClass(base.o.classNames.allMenusClass + ' ' + base.o.classNames.inheritedMenuClass); 57 58 var menu_item_tagname = base.$items.prop("tagName"); 59 60 // add 'back' buttons 61 base.$inheritedMenus.each(function (i, val) { 62 var url = $(val) 63 .closest(base.o.itemSelector) 64 .children('a') 65 .attr('href'), 66 $li = $('<' + menu_item_tagname + '/>', { 67 'class': base.o.classNames.menuItemBackClass 68 }); 69 70 if (base.o.backOnTop) { 71 $li.prependTo($(val)); 72 } else { 73 $li.appendTo($(val)); 74 } 75 76 base.$backs.push( 77 $li.append($('<a>', {'href' : url}) 78 .html(base.o.backLinkContent)) 79 ); 80 }); 81 // minimum height fix 82 if (base.o.minHeightMenu > 0) { 83 base.o.minHeightMenu = base.o.minHeightMenu > base.$waSlideMenu.height() ? base.o.minHeightMenu : base.$waSlideMenu.height(); 84 base.$waSlideMenu.css('min-height', base.o.minHeightMenu); 85 } 86 base.o.previousHeight = 0; 87 base.o.nowSliding = false; 88 }; 89 // go to selected item 90 base.gotoSelected = function (depth, animate) { 91 // we need to go deeper 92 base._hideOtherMenus(depth, base._scrollToTop); 93 // height fix 94 base._heightFix(depth); 95 // sliiiiide to other level 96 base._animateSlide(depth, animate); 97 }; 98 // animate function 99 base._animateSlide = function (depth, animate) { 100 var amount = Math.abs(depth * 100), 101 callback_slide = depth > 0 ? base.o.onSlideForward : base.o.onSlideBack; 102 103 // onSlide or onSlideBack callback 104 if (callback_slide && typeof (callback_slide) === 'function') { 105 callback_slide(base); 106 } 107 if (animate) { 108 base.o.nowSliding = true; 109 base.$movable.animate( 110 { 111 'left': depth > 0 ? '-=' + amount + '%' : '+=' + amount + '%' 112 }, 113 base.o.slideSpeed, 114 base.o.slideEasing, 115 function () { 116 base.o.nowSliding = false; 117 base._scrollToTop(depth); 118 // afterSlide callback 119 if (base.o.afterSlide && typeof (base.o.afterSlide) === 'function') { 120 base.o.afterSlide(base); 121 } 122 } 123 ); 124 } else { 125 base.$movable.css('left', '-' + amount + '%'); 126 } 127 }; 128 base._scrollToTop = function (depth) { 129 var $menu_children = base.$currentMenuElement.children(base.o.menuSelector), 130 back_link = base.$currentMenuElement.hasClass(base.o.classNames.menuItemBackClass) ? true : false; 131 132 // we need to understand, are backlink or previous link visible in viewport 133 if (base.o.scrollToTopSpeed && (back_link || $menu_children.length > 0)) { 134 var $back = depth > 0 ? $menu_children.children(base.o.itemSelector).last() : base.$currentMenuElement, 135 $offset_elem = depth > 0 ? $back.closest(base.o.menuSelector) : base.$currentMenuElement, 136 back_pos_top = $back.offset().top, 137 win_pos_top = $(window).scrollTop(); 138 if (back_pos_top - win_pos_top < 0) { 139 $('html, body').animate({ 140 scrollTop: $offset_elem.offset().top 141 }, base.o.scrollToTopSpeed); 142 } 143 } 144 }; 145 base._heightFix = function (depth) { 146 if (base.o.autoHeightMenu) { 147 var h = 0, 148 to_currentmenuelement_h = 0; 149 if (depth > 0) { // forward - height of inherited menu 150 to_currentmenuelement_h = (base.$currentMenuElement.offset().top - base.$waSlideMenu.offset().top) + base.$currentMenuElement.outerHeight(); 151 h = base.$currentMenuElement 152 .children(base.o.menuSelector) 153 .height(); 154 // if children menu height is lower then currentMenuElement offset 155 h = h < to_currentmenuelement_h ? h = to_currentmenuelement_h
155: h; 156 } else { // height of current menu 157 h = base.$currentMenuElement 158 .closest(base.o.menuSelector) 159 .height(); 160 } 161 if (depth < 0) { // if backwards - prevent previous height 162 h = h < to_currentmenuelement_h ? h = to_currentmenuelement_h : h; 163 //h = h > base.o.previousHeight ? h : base.o.previousHeight; 164 } 165 166 h = h > base.o.minHeightMenu ? h : base.o.minHeightMenu; 167 base.o.previousHeight = h; 168 base.$waSlideMenu.css('height', h); 169 } 170 }; 171 base._hideOtherMenus = function (depth) { 172 var $branchParent = base.$currentMenuElement.parentsUntil('.' + base.o.classNames.navigationClass).filter(base.o.itemSelector); 173 174 $branchParent.push(base.$currentMenuElement[0]); 175 176 if (depth > 0) { 177 // hide all menus branches 178 base.$inheritedMenus.css('visibility', 'hidden'); 179 // show only our inherited menus 180 $branchParent.children(base.o.menuSelector).css('visibility', 'visible'); 181 } 182 }; 183 base.loadContent = function (loadContainer, url) { 184 var loading = $('<span/>', { 185 'class' : base.o.classNames.loadingClass 186 }).html(' '), 187 clear = '<div style="clear:both"></div>'; 188 189 if (base.o.loadContainer.length > 0 && // check if we have place where load content 190 loadContainer.length > 0 && 191 $.inArray(url, base.o.excludeUri) < 0 && 192 (location.origin + url) !== window.location.href) { 193 194 if (base.o.beforeLoad && typeof (base.o.beforeLoad) === 'function') { 195 base.o.beforeLoad.apply(loadContainer); 196 } else { 197 loadContainer.html(loading).append(clear); 198 } 199 // TODO: (?) save content if we will fail load next page 200 $.ajax( 201 { 202 url: url, 203 type: 'get' 204 } 205 ) 206 .done(function (data) { 207 loadContainer.html(data + clear); 208 // set page title 209 base.changeTitle(); 210 // set new url 211 base.changeUri(url); 212 // afterLoadDone callback 213 if (base.o.afterLoadDone && typeof (base.o.afterLoadDone) === 'function') { 214 base.o.afterLoadDone.apply(loadContainer); 215 } 216 base.$waSlideMenu.trigger('afterLoadDone.' + base.o.namespace + pluginName); 217 }) 218 .fail(function () { 219 // do not remove content and slide menu 220 base.$currentMenuElement 221 .siblings() 222 .children('.' + base.o.classNames.menuItemBackClass) 223 .trigger('click'); 224 225 // afterLoadFail callback 226 if (base.o.afterLoadFail && typeof (base.o.afterLoadFail) === 'function') { 227 base.o.afterLoadFail.apply(loadContainer); 228 } 229 base.$waSlideMenu.trigger('afterLoadFail.' + base.o.namespace + pluginName); 230 }) 231 .always(function () { 232 // afterLoadAlways callback 233 if (base.o.afterLoadAlways && typeof (base.o.afterLoadAlways) === 'function') { 234 base.o.afterLoadAlways.apply(loadContainer); 235 } 236 base.$waSlideMenu.trigger('afterLoadAlways.' + base.o.namespace + pluginName); 237 }); 238 } 239 }; 240 base.changeTitle = function () { 241 if (base.o.setTitle) { 242 $('title').text(base.$currentMenuElement.children('a').text()); 243 } 244 }; 245 base.changeUri = function (url) { 246 if (!!(window.history && history.pushState)) { 247 window.history.pushState({}, '', url); 248 } 249 }; 250 base._menuItemClicked = function (element, event, depth) { 251 if ( $(element).parent().hasClass("sublevel") || $(element).parent().hasClass("waSlideMenu-back") ) { 252 event.preventDefault(); 253 event.stopPropagation(); 254 } 255 256 // disable click while we are sliding to next menu 257 if (base.o.nowSliding) { 258 return false; 259 } 260 261 base.$currentMenuElement = $(element).parent(base.o.itemSelector); 262 263 var url = $(element).attr('href'), 264 $menu_children = base.$currentMenuElement.children(base.o.menuSelector), 265 back_link = base.$currentMenuElement.hasClass(base.o.classNames.menuItemBackClass) ? true : false, 266 $load_container = $(base.o.loadContainer); 267 268 // change selected class 269 base.$waSlideMenu 270 .find(base.o.itemSelector) 271 .filter('.' + base.o.selectedClass) 272 .removeClass(base.o.selectedClass); 273 274 if (back_link) { 275 base.$currentMenuElement = base.$currentMenuElement 276 .closest(base.o.menuSelector) 277 .closest(base.o.itemSelector); 278 } 279 // add 'selected' class 280 base.$currentMenuElement.addClass(base.o.selectedClass); 281 282 if (base.o.loadOnlyLatest === false) { 283 base.loadContent($load_container, url); 284 } else if (base.o.loadOnlyLatest && $menu_children.length === 0 && depth > 0) { 285 base.loadContent($load_container, url); 286 } 287 288 // onLatestClick callback 289 if ($menu_children.length === 0 && depth > 0) { 290 if (base.o.onLatestClick && typeof (base.o.onLatestClick) === 'function') { 291 base.o.onLatestClick.apply(element); 292 } 293 base.$waSlideMenu.trigger('onLatestClick.' + base.o.namespace + pluginName); 294 } 295 296 // if we can go deeper or up 297 if ($menu_children.length > 0 || depth < 0) { 298 base.gotoSelected(depth, true); 299 } 300 }; 301 base._menuDown = function (e) { 302 base._menuItemClicked(this, e, 1); 303 }; 304 base._menuUp = function (e) { 305 base._menuItemClicked(this, e, -1); 306 }; 307 base._remove = function () { 308 base.$items.off('click', 'a', base._menuDown);
309 310 base.$waSlideMenu.removeClass(base.o.classNames.navigationClass); 311 if (base.o.autoHeightMenu) { 312 base.$waSlideMenu.css('height', 'initial'); 313 } 314 if (base.o.minHeightMenu > 0) { 315 base.$waSlideMenu.css('min-height', 'initial'); 316 } 317 base.$rootMenu.removeClass(base.o.classNames.allMenusClass) 318 .prependTo(base.$waSlideMenu); 319 base.$inheritedMenus.removeClass(base.o.classNames.allMenusClass + ' ' + base.o.classNames.inheritedMenuClass); 320 base.$inheritedMenus.css('visibility', ''); 321 $.each(base.$backs, function (index, back) { 322 back.remove(); 323 }); 324 base.$movable.remove(); 325 base.$waSlideMenu.removeData(pluginName); 326 }; 327 base.exec = function(func) { 328 if (base.hasOwnProperty(func) && typeof base[func] === 'function') { 329 base[func].call(); 330 } 331 }; 332 base.destroy = function () { 333 base._remove(); 334 }; 335 base.init = function () { 336 base._setup(); 337 338 $.each(base.$backs, function (index, back) { 339 back.on('click', 'a', base._menuUp); 340 }); 341 base.$items.on('click', 'a', base._menuDown); 342 343 if (!!(window.history && history.pushState)) { 344 window.onpopstate = function (event) { 345 if (event && event.state) { 346 location.reload(); 347 } 348 }; 349 } 350 351 var $selectedItem = base.$waSlideMenu 352 .find('.' + base.o.selectedClass) 353 .first(); 354 355 if ($selectedItem.length) { 356 base.$currentMenuElement = $selectedItem; 357 var $url = base.$currentMenuElement.children('a').first(), 358 depth = $selectedItem.parentsUntil('.' + base.o.classNames.navigationClass).filter(base.o.itemSelector).length, 359 $selectedItemChildrenLen = $selectedItem.children(base.o.menuSelector).length; 360 if ($.inArray($url.attr('href'), base.o.excludeUri) < 0) { 361 // if we have menus inside current element 362 if ($selectedItemChildrenLen > 0) { 363 // slide to them 364 depth += 1; 365 } 366 base.gotoSelected(depth, false); 367 // heightFix hack if we show last menu 368 base._heightFix($selectedItemChildrenLen); 369 } 370 } 371 372 // onInit callback 373 if (base.o.onInit && typeof (base.o.onInit) === 'function') { 374 base.o.onInit(base); 375 } 376 base.$waSlideMenu.trigger('onInit.' + base.o.namespace + pluginName); 377 }; 378 379 var oldBase = $(el).data(pluginName); 380 if (typeof oldBase === 'object') { 381 $.each(options, function (k, v) { 382 if (typeof oldBase[k] === 'function') { 383 oldBase[k](v); 384 } else if (oldBase.o.hasOwnProperty(k)){ 385 oldBase.o[k] = v; 386 } 387 }); 388 } else { 389 base.init(); 390 } 391 }; 392 393 $.waSlideMenu.defaults = { 394 namespace : 'wa', 395 396 slideSpeed : 400, 397 slideEasing : 'linear', 398 backLinkContent : 'Back', 399 backOnTop : false, 400 selectedClass : 'selected', 401 loadContainer : '', 402 minHeightMenu : 0, 403 autoHeightMenu : true, 404 excludeUri : ['/', '#'], 405 loadOnlyLatest : false, 406 menuSelector : 'ul', 407 itemSelector : 'li', 408 setTitle : false, 409 scrollToTopSpeed : 0, 410 411 /* Callbacks */ 412 onInit : null, 413 onSlideForward : null, 414 onSlideBack : null, 415 onLatestClick : null, 416 afterSlide : null, 417 beforeLoad : null, 418 afterLoadAlways : null, 419 afterLoadDone : null, 420 afterLoadFail : null, 421
422 /* class names */ 423 classNames: { 424 navigationClass : '-nav', 425 wrapperClass : '-wrapper', 426 allMenusClass : '-menu', 427 inheritedMenuClass : '-inheritedmenu', 428 menuItemBackClass : '-back', 429 loadingClass : '-loading' 430 } 431 }; // $.waSlideMenu.defaults 432 433 $.fn.waSlideMenu = function (key, val) { 434 435 return this.each(function () { 436 var options; 437 438 // Allow for passing in an object or a single key and value 439 if (typeof key !== 'object' && typeof val !== 'undefined') { 440 options = {}; 441 options[key] = val; 442 } else { 443 options = key; 444 } 445 446 var t = new $.waSlideMenu(this, options); 447 }); // this.each 448 449 }; // $.fn.waSlideMenu 450 451}(jQuery, window, document));
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.