1 2class Slinky { 3 // default options 4 get options() { 5 return { 6 resize: true, 7 speed: 300, 8 theme: 'slinky-theme-default', 9 title: false 10 } 11 } 12 13 constructor(element, options = {}) { 14 this.settings = { 15 ...this.options, 16 ...options 17 }; 18 19 this.default = { 20 toggle: true, // 21 preventDefault: true, // ÐÑполнÑÑÑ Ð´ÐµÐ¹ÑÑвие по ÑмолÑÐ°Ð½Ð¸Ñ Ð¸Ð»Ð¸ Ð½ÐµÑ 22 triggerElement: 'a', // 23 parentTrigger: 'li', // 24 subMenu: 'ul' // 25 }; 26 27 this._init(element) 28 } 29 30 // setup the DOM just for us 31 _init(element) { 32 this.menu = jQuery(element); 33 this.base = this.menu.children().first(); 34 35 const { menu, settings } = this; 36 37 // задаÑÑ ÑÐµÐ¼Ñ 38 menu.addClass('slinky-menu').addClass(settings.theme); 39 40 // ÑÑÑановиÑÑ ÑкоÑоÑÑÑ Ð¿ÐµÑÐµÑ Ð¾Ð´Ð° 41 this._transition(settings.speed); 42 43 // добавиÑÑ ÑÑÑелки к ÑÑÑлкам Ñ Ð´Ð¾Ñками 44 jQuery('a + ul', menu) 45 .prev() 46 .addClass('next'); 47 48 // обеÑнÑÑÑ ÑекÑÑ ÑÑÑлки Ñ Ð¿Ð¾Ð¼Ð¾ÑÑÑ <span> 49 // в оÑновном Ð´Ð»Ñ ÑÑилизаÑии 50 jQuery('li > a', menu).wrapInner('<span>'); 51 52 // ÑоздаÑÑ ÑÐ»ÐµÐ¼ÐµÐ½Ñ Ð·Ð°Ð³Ð¾Ð»Ð¾Ð²ÐºÐ° 53 const header = jQuery('<li>').addClass('header'); 54 55 // добавиÑÑ ÐµÐ³Ð¾ в ÑпиÑок 56 jQuery('li > ul', menu).prepend(header); 57 58 // ÑоздаÑÑ ÐºÐ½Ð¾Ð¿ÐºÐ¸ возвÑаÑа 59 const back = jQuery('<a>') 60 .prop('href', '#') 61 .addClass('back'); 62 63 // добавиÑÑ Ð¸Ñ Ð² заголовки 64 jQuery('.header', menu).prepend(back); 65 66 // нам нÑжно добавиÑÑ Ð½Ð°Ð·Ð²Ð°Ð½Ð¸Ñ? 67 if (settings.title) { 68 // Ñикл по ÐºÐ°Ð¶Ð´Ð¾Ð¼Ñ Ð´Ð¾ÑеÑÐ½ÐµÐ¼Ñ ÑпиÑÐºÑ 69 jQuery('li > ul', menu).each((index, element) => { 70 // полÑÑиÑÑ Ð¼ÐµÑÐºÑ Ð¿Ð¾ ÑодиÑелÑÑкой ÑÑÑлке 71 const label = jQuery(element) 72 .parent() 73 .find('a') 74 .first() 75 .text(); 76 77 // еÑли он не пÑÑÑой, ÑоздайÑе заголовок 78 if (label) { 79 const title = jQuery('<header>') 80 .addClass('title') 81 .text(label); 82 83 // добавиÑÑ ÐµÐ³Ð¾ в непоÑÑедÑÑвеннÑй заголовок 84 jQuery('> .header > a', element).append(title) 85 } 86 }) 87 } 88 89 // добавиÑÑ ÑлÑÑиваÑели кликов 90 this._addListeners(); 91 92 // пеÑейÑи к наÑалÑÐ½Ð¾Ð¼Ñ Ð°ÐºÑÐ¸Ð²Ð½Ð¾Ð¼Ñ 93 this._jumpToInitial(); 94 } 95 96 _psuedoClick(parentElem) { 97 98 let beforeClicked, 99 afterClicked; 100 101 let parentLeft = parseInt(parentElem.getBoundingClientRect().left, 10), 102 parentTop = parseInt(parentElem.getBoundingClientRect().top, 10); 103 104 let parentWidth = parseInt(window.getComputedStyle(parentElem).width, 10), 105 parentHeight = parseInt(window.getComputedStyle(parentElem).height, 10); 106 107 let before = window.getComputedStyle(parentElem, ':before'); 108 109 let beforeStart = parentLeft + (parseInt(before.getPropertyValue("left"), 10)), 110 beforeEnd = beforeStart + parseInt(before.width, 10); 111 112 let beforeYStart = parentTop + (parseInt(before.getPropertyValue("top"), 10)), 113 beforeYEnd = beforeYStart + parseInt(before.height, 10); 114 115 let after = window.getComputedStyle(parentElem, ':after'); 116 117 let afterStart = parentLeft + (parseInt(after.getPropertyValue("left"), 10)), 118 afterEnd = afterStart + parseInt(after.width, 10); 119 120 let afterYStart = parentTop + (parseInt(after.getPropertyValue("top"), 10)) - parseInt( after.getPropertyValue("top"), 10), 121 afterYEnd = afterYStart + parseInt(after.height, 10); 122 123 let mouseX = event.clientX, 124 mouseY = event.clientY; 125 126 beforeClicked = (mouseX >= beforeStart && mouseX <= beforeEnd && mouseY >= beforeYStart && mouseY <= beforeYEnd ? true : false); 127 128 afterClicked = (mouseX >= afterStart && mouseX <= afterEnd && mouseY >= afterYStart && mouseY <= afterYEnd ? true : false); 129 130 return { 131 "before" : beforeClicked, 132 "after" : afterClicked 133 134 }; 135 } 136 137 // click listeners 138 _addListeners() { 139 const { menu, settings } = this; 140 141 jQuery('a', menu).on('click', e => { 142 // пÑедоÑвÑаÑиÑÑ ÑломаннÑе / половинÑаÑÑе пеÑÐµÑ Ð¾Ð´Ñ 143 if (this._clicked + settings.speed > Date.now()) { 144 return false 145 } 146 147 // кеÑиÑоваÑÑ Ð²ÑÐµÐ¼Ñ ÐºÐ»Ð¸ÐºÐ° Ð´Ð»Ñ Ð¿ÑовеÑки ÑледÑÑÑего клика 148 this._clicked = Date.now(); 149 150 // get the link 151 const link = jQuery(e.currentTarget); 152 153 // пÑедоÑвÑаÑиÑÑ Ð´ÐµÑолÑ, еÑли ÑÑо Ñ ÑÑ Ð¸Ð»Ð¸ обÑÑгиваÑÑÐ°Ñ ÐºÐ½Ð¾Ð¿ÐºÐ° 154 if( !this._psuedoClick(link[0]).after && link.attr('href') && link.attr('href').indexOf('#') !== 0 ) { 155 document.location = link.attr('href'); 156 } 157 158 if ( 159 link.attr('href').indexOf('#') === 0 || 160 link.hasClass('next') || 161 link.hasClass('back') 162 ) { 163 e.preventDefault() 164 } 165 166 // двигаемÑÑ Ð² пеÑÑд 167 if (link.hasClass('next')) { 168 // один Ñаг впеÑед 169 170 // ÑдалиÑÑ ÑекÑÑий active 171 menu.find('.active').removeClass('active'); 172 173 // ÑÑÑановиÑÑ Ð½Ð¾Ð²Ñй active 174 link 175 .next() 176 .show() 177 .addClass('active'); 178 179 // ÑделаÑÑ Ñ Ð¾Ð´ 180 this._move(1); 181 182 // измениÑÑ ÑÐ°Ð·Ð¼ÐµÑ Ð¼ÐµÐ½Ñ, еÑли ÑÑо Ð½ÐµÐ¾Ð±Ñ Ð¾Ð´Ð¸Ð¼Ð¾ 183 if (settings.resize) { 184 this._resize(link.next()) 185 } 186 } else if (link.hasClass('back')) { 187 // и два Ñага назад 188 // вÑего один Ñаг назад, на Ñамом деле 189 190 // ÑделаÑÑ Ñ Ð¾Ð´ 191 this._move(-1, () => { 192 // ÑдалиÑÑ ÑекÑÑий active 193 menu.find('.active').removeClass('active'); 194 195 // ÑÑÑановиÑÑ Ð½Ð¾Ð²Ñй active 196 link 197 .parent() 198 .parent() 199 .hide() 200 .parentsUntil(menu, 'ul') 201 .first() 202 .addClass('active') 203 }); 204 205 // измениÑÑ ÑÐ°Ð·Ð¼ÐµÑ Ð¼ÐµÐ½Ñ, еÑли ÑÑо Ð½ÐµÐ¾Ð±Ñ Ð¾Ð´Ð¸Ð¼Ð¾ 206 if (settings.resize) { 207 this._resize( 208 link 209 .parent() 210 .parent() 211 .parentsUntil(menu, 'ul') 212 ) 213 } 214 } 215 }) 216 } 217
218 // пеÑейÑи к наÑалÑÐ½Ð¾Ð¼Ñ active пÑи иниÑиализаÑии 219 _jumpToInitial() { 220 const { menu, settings } = this; 221 222 // полÑÑиÑÑ Ð½Ð°ÑалÑнÑй active 223 const active = menu.find('.active'); 224 225 if (active.length > 0) { 226 // ÑдалиÑÑ Ð½Ð°ÑалÑнÑй active 227 active.removeClass('active'); 228 229 // пÑÑжок без анимаÑии 230 this.jump(active, false) 231 } 232 233 // ÑÑÑановиÑÑ Ð½Ð°ÑалÑнÑÑ Ð²ÑÑоÑÑ Ð² Ð¼ÐµÐ½Ñ 234 // иÑпÑавиÑÑ Ð¾ÑÐ¸Ð±ÐºÑ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ ÑазмеÑа пеÑвого пеÑÐµÑ Ð¾Ð´Ð° 235 setTimeout(() => menu.height(menu.outerHeight()), settings.speed) 236 } 237 238 // ÑдвинÑÑе Ð¼ÐµÐ½Ñ 239 _move(depth = 0, callback = () => {}) { 240 // не ÑÑÑÑждайÑе ÑебÑ, еÑли Ð¼Ñ Ð½Ð¸ÐºÑда не двигаемÑÑ 241 if (depth === 0) { 242 return 243 } 244 245 const { settings, base } = this; 246 247 // полÑÑиÑÑ ÑекÑÑÑÑ Ð¿Ð¾Ð·Ð¸ÑÐ¸Ñ Ñлева 248 const left = Math.round(parseInt(base.get(0).style.left)) || 0; 249 250 // ÑÑÑановиÑÑ Ð½Ð¾Ð²ÑÑ Ð¿Ð¾Ð·Ð¸ÑÐ¸Ñ Ñлева 251 base.css('left', `${left - depth * 100}%`); 252 253 // обÑаÑнÑй вÑзов поÑле анимаÑии 254 if (typeof callback === 'function') { 255 setTimeout(callback, settings.speed); 256 } 257 } 258 259 // измениÑÑ ÑÐ°Ð·Ð¼ÐµÑ Ð¼ÐµÐ½Ñ ÑÑÐ¾Ð±Ñ ÑооÑвеÑÑÑвоваÑÑ Ð²ÑÑоÑе конÑенÑа 260 _resize(content) { 261 const { menu } = this; 262 263 menu.height(content.outerHeight()) 264 } 265 266 // ÑÑÑановиÑÑ ÑкоÑоÑÑÑ Ð¿ÐµÑÐµÑ Ð¾Ð´Ð° 267 _transition(speed = 300) { 268 const { menu, base } = this; 269 270 menu.css('transition-duration', `${speed}ms`); 271 base.css('transition-duration', `${speed}ms`); 272 } 273 274 // пеÑейÑи к ÑлеменÑÑ 275 jump(target, animate = true) { 276 if (!target) { 277 return 278 } 279 280 const { menu, settings } = this; 281 282 const to = jQuery(target); 283 284 // полÑÑиÑÑ Ð²Ñе ÑекÑÑие active 285 const active = menu.find('.active'); 286 287 // ÑколÑко Ñ Ð¾Ð´Ð¾Ð² Ð¼Ñ Ð´Ð¾Ð»Ð¶Ð½Ñ Ð¿ÑÑгнÑÑÑ? 288 let count = 0; 289 290 // пока не доÑÑигнем ÑодиÑелÑÑкого ÑпиÑка 291 if (active.length > 0) { 292 count = active.parentsUntil(menu, 'ul').length; 293 } 294 295 // ÑдалиÑÑ ÑекÑÑий active 296 // ÑкÑÑÑÑ Ð²Ñе ÑпиÑки 297 menu 298 .find('ul') 299 .removeClass('active') 300 .hide(); 301 302 // полÑÑиÑÑ ÑодиÑелÑÑкий ÑпиÑок 303 const menus = to.parentsUntil(menu, 'ul'); 304 305 // показаÑÑ ÑодиÑелÑÑкий ÑпиÑок 306 menus.show(); 307 308 // показаÑÑ ÑÐµÐ»Ñ 309 to.show().addClass('active'); 310 311 // ÑÑÑановиÑÑ ÑкоÑоÑÑÑ Ð¿ÐµÑÐµÑ Ð¾Ð´Ð° на 0, еÑли Ð½ÐµÑ Ð°Ð½Ð¸Ð¼Ð°Ñии 312 if (!animate) { 313 this._transition(0); 314 } 315 316 // заÑÑавиÑÑ Ð´Ð²Ð¸Ð³Ð°ÑÑÑÑ 317 this._move(menus.length - count); 318 319 // измениÑÑ ÑÐ°Ð·Ð¼ÐµÑ Ð¼ÐµÐ½Ñ, еÑли ÑÑо Ð½ÐµÐ¾Ð±Ñ Ð¾Ð´Ð¸Ð¼Ð¾ 320 if (settings.resize) { 321 this._resize(to); 322 } 323 324 // ÑÑÑановиÑÑ ÑкоÑоÑÑÑ Ð¿ÐµÑÐµÑ Ð¾Ð´Ð° по ÑмолÑÐ°Ð½Ð¸Ñ Ð¿Ð¾Ñле пеÑÐµÑ Ð¾Ð´Ð° 325 if (!animate) { 326 this._transition(settings.speed); 327 } 328 } 329 330 // Ñо ÑиÑом или на ÑиÑе 331 // пÑоÑÑо иди домой 332 home(animate = true) { 333 const { base, menu, settings } = this; 334 335 // ÑÑÑановиÑе ÑкоÑоÑÑÑ Ð¿ÐµÑÐµÑ Ð¾Ð´Ð° на 0, еÑли Ð½ÐµÑ Ð°Ð½Ð¸Ð¼Ð°Ñии 336 if (!animate) { 337 this._transition(0); 338 } 339 340 // get current active 341 const active = menu.find('.active'); 342 343 // get all parent lists 344 const parents = active.parentsUntil(menu, 'ul'); 345 346 // make the move 347 this._move(-parents.length, () => { 348 // remove the current active 349 active.removeClass('active').hide(); 350 351 // hide all parents except base 352 parents.not(base).hide(); 353 }); 354 355 // resize if need be 356 if (settings.resize) { 357 this._resize(base); 358 } 359 360 // set transition speed back to default 361 if (animate === false) { 362 this._transition(settings.speed); 363 } 364 } 365 366 // crush, kill, destroy 367 destroy() { 368 const { base, menu } = this; 369 370 // remove all headers 371 jQuery('.header', menu).remove(); 372 373 // remove Slinky links 374 // and click listeners 375 jQuery('a', menu) 376 .removeClass('next') 377 .off('click'); 378 379 // remove inline styles 380 menu.css({ 381 height: '', 382 'transition-duration': '' 383 }); 384 385 base.css({ 386 left: '', 387 'transition-duration': '' 388 }); 389 390 // remove Slinky HTML 391 jQuery('li > a > span', menu) 392 .contents() 393 .unwrap(); 394 395 // remove any current active 396 menu.find('.active').removeClass('active'); 397 398 // remove any Slinky style classes 399 const styles = menu.attr('class').split(' '); 400
401 styles.forEach(style => { 402 if (style.indexOf('slinky') === 0) { 403 menu.removeClass(style); 404 } 405 }); 406 407 // reset fields 408 const fields = ['settings', 'menu', 'base']; 409 410 fields.forEach(field => delete this[field]); 411 } 412} 413 414// jQuery plugin 415;($ => { 416 $.fn.slinky = function(options) { 417 return new Slinky(this, options); 418 } 419})(jQuery);
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.