PageSourceSearch

https://www.scalabad.no/scripts/jquery.slinky.custom.js

js scalabad.no collected 2026-10-08 01:16:02 UTC 10,802 bytes, 394 lines download raw bytes

1/*
2 * Slinky
3 * Rather sweet menus
4 * @author Ali Zahid <[email protected]>
5 * @license MIT
6 */
7
8class Slinky {
9    // default options
10    get options() {
11        return {
12            resize: true,
13            speed: 300,
14            theme: 'slinky-theme-default',
15            title: false,
16            activeClass: 'mmenu-is-active',
17            hideParentLocatorClass: 'slinky-hide-static-upon-nav'
18        }
19    }
20
21    constructor(element, options = {}) {
22        // save settings
23        this.settings = {
24            ...this.options,
25            ...options
26        }
27
28        // let's go!
29        this._init(element)
30    }
31
32    // setup the DOM just for us
33    _init(element) {
34        // the two elements of water and moisture
35        this.menu = jQuery(element)
36        this.base = this.menu.children().first()
37
38        const { menu, settings } = this
39
40        // set theme
41        menu.addClass('slinky-menu').addClass(settings.theme)
42
43        // set transition speed
44        this._transition(settings.speed)
45
46        // add arrows to links with children
47        jQuery('a + ul', menu)
48            .prev()
49            .addClass('next')
50
51        // wrap link text with <span>
52        // mostly for styling
53        jQuery('li > a', menu).wrapInner('<span>')
54
55        // create header item
56        const header = jQuery('<li>').addClass('header')
57
58        // prepend it to the list
59        jQuery('li > ul', menu).prepend(header)
60
61        // create back buttons
62        const back = jQuery('<a>')
63            .prop('href', '#')
64            .addClass('back')
65
66        // prepend them to the headers
67        jQuery('.header', menu).prepend(back)
68
69        // do we need to add titles?
70        if (settings.title) {
71            // loop through each child list
72            jQuery('li > ul', menu).each((index, element) => {
73                // get the label from the parent link
74                const label = jQuery(element)
75                    .parent()
76                    .find('a')
77                    .first()
78                    .text()
79
80                // if it's not empty, create the title
81                if (label) {
82                    const title = jQuery('<header>')
83                        .addClass('title')
84                        .text(label)
85
86                    // append it to the immediate header
87                    jQuery('> .header', element).append(title)
88                }
89            })
90        }
91
92        // add click listeners
93        this._addListeners()
94
95        // jump to initial active
96        this._jumpToInitial()
97    }
98
99    // click listeners
100    _addListeners() {
101        const { menu, settings } = this
102        const  menuParent  = menu.parent().parent();
103
104        jQuery('a', menu, menuParent).on('click', e => {
105            // prevent broken/half transitions
106            if (this._clicked + settings.speed > Date.now()) {
107                return false
108            }
109
110            // cache click time to check on next click
111            this._clicked = Date.now()
112
113            // get the link
114            const link = jQuery(e.currentTarget)
115
116            // prevent default if it's a hash
117            // or a Slinky button
118            if (
119                link.attr('href').indexOf('#') === 0 ||
120                link.hasClass('next') ||
121                link.hasClass('back')
122            ) {
123                e.preventDefault()
124            }
125
126            // time to move
127            if (link.hasClass('next')) {
128                // one step forward
129
130                // remove the current active
131                menu.find('.active').removeClass('active')
132
133                // set the new active
134                link
135                    .next()
136                    .show()
137                    .addClass('active')
138
139                // make the chess move
140                this._move(1)
141
142                if (menu.hasClass(settings.hideParentLocatorClass) && !menuParent.hasClass(settings.activeClass)) {
143                    menuParent.addClass(settings.activeClass);
144                }
145
146                // resize the menu if need be
147                if (settings.resize) {
148                    this._resize(link.next())
149                }
150            } else if (link.hasClass('back')) {
151                // and two steps back
152                // just one step back, actually
153
154                // make the move
155                this._move(-1, () => {
156                    // remove the current active
157                    menu.find('.active').removeClass('active')
158
159                    // set the new active
160                    link
161                        .parent()
162                        .parent()
163                        .hide()
164                        .parentsUntil(menu, 'ul')
165                        .first()
166                        .addClass('active')
167                })
168
169                // resize the menu if need be
170                if (settings.resize) {
171                    var el = link.parent().parent().parents('ul');
172                    if (el.length == 1) {
173                        if (menuParent.hasClass(settings.activeClass)) {
174                            menuParent.removeClass(settings.activeClass);
175                        }
176                    }
177
178                    this._resize(
179                        link
180                            .parent()
181                            .parent()
182                            .parentsUntil(menu, 'ul')
183                    )
184                }
185            }
186        })
187    }
188
189    // jump to initial active on init
190    _jumpToInitial() {
191        const { menu, settings } = this
192
193        // get initial active
194        const active = menu.find('.active')
195
196        if (active.length > 0) {
197            // remove initial active
198            active.removeClass('active')
199
200            // jump without animation
201            this.jump(active, false)
202        }
203
204        // set initial height on the menu
205        // to fix the first transition resize bug
206        setTimeout(() => menu.height(menu.outerHeight()), settings.speed)
207    }
208
209    // slide the menu
210    _move(depth = 0, callback = () => { }) {
211        // don't bother packing if we're not going anywhere
212        if (depth === 0) {
213            return
214        }
215
216        const { settings, base } = this
217
218        // get current position from the left
219        const left = Math.round(parseInt(base.get(0).style.left)) || 0
220
221        // set the new position from the left
222        base.css('left', `${left - depth * 100}%`)
223
224        // callback after the animation
225        if (typeof callback === 'function') {
226            setTimeout(callback, settings.speed)
227        }
228    }
229
230    // resize the menu
231    // to match content height
232    _resize(content) {
233        const { menu } = this
234
235        menu.height(content.outerHeight())
236    }
237
238    // set the transition speed
239    _transition(speed = 300) {
240        const { menu, base } = this
241
242        menu.css('transition-duration', `${speed}ms`)
243        base.css('transition-duration', `${speed}ms`)
244    }
245
246    // jump to an element
247    jump(target, animate = true) {
248        if (!target) {
249            return
250        }
251
252        const { menu, settings } = this
253
254        const to = jQuery(target)
255
256        // get all current active
257        const active = menu.find('.active')
258
259        // how many moves must we jump?
260        let count = 0
261
262        // this many
263        // until we reach the parent list
264        if (active.length > 0) {
265            count = active.parentsUntil(menu, 'ul').length
266        }
267
268        // remove current active
269        // hide all lists
270        menu
271            .find('ul')
272            .removeClass('active')
273            .hide()
274
275        // get parent list
276        const menus = to.parentsUntil(menu, 'ul')
277
278        // show parent list
279        menus.show()
280
281        // show target
282        to.show().addClass('active')
283
284        // set transition speed to 0 if no animation
285        if (!animate) {
286            this._transition(0)
287        }
288
289        // make the checkers move
290        this._move(menus.length - count)
291
292        // resize menu if need be
293        if (settings.resize) {
294            this._resize(to)
295        }
296
297        // set transition speed to default after transition
298        if (!animate) {
299            this._transition(settings.speed)
300        }
301    }
302
303    // go big or go home
304    // just go home
305    home(animate = true) {
306        const { base, menu, settings } = this
307
308        // set transition speed to 0 if no animation
309        if (!animate) {
310            this._transition(0)
311        }
312
313        // get current active
314        const active = menu.find('.active')
315
316        // get all parent lists
317        const parents = active.parentsUntil(menu, 'ul')
318
319        // make the move
320        this._move(-parents.length, () => {
321            // remove the current active
322            active.removeClass('active').hide()
323
324            // hide all parents except base
325            parents.not(base).hide()
326        })
327
328        // resize if need be
329        if (settings.resize) {
330            this._resize(base)
331        }
332
333        // set transition speed back to default
334        if (animate === false) {
335            this._transition(settings.speed)
336        }
337    }
338
339    // crush, kill, destroy
340    destroy() {
341        const { base, menu } = this
342
343        // remove all headers
344        jQuery('.header', menu).remove()
345
346        // remove Slinky links
347        // and click listeners
348        jQuery('a', menu)
349            .removeClass('next')
350            .off('click')
351
352        // remove inline styles
353        menu.css({
354            height: '',
355            'transition-duration': ''
356        })
357
358        base.css({
359            left: '',
360            'transition-duration': ''
361        })
362
363        // remove Slinky HTML
364        jQuery('li > a > span', menu)
365            .contents()
366            .unwrap()
367
368        // remove any current active
369        menu.find('.active').removeClass('active')
370
371        // remove any Slinky style classes
372        const styles = menu.attr('class').split(' ')
373
374        styles.forEach(style => {
375            if (style.indexOf('slinky') === 0) {
376                menu.removeClass(style)
377            }
378        })
379
380        // reset fields
381        const fields = ['settings', 'menu', 'base']
382
383        fields.forEach(field => delete this[field])
384    }
385}
386
387// jQuery plugin
388; ($ => {
389    $.fn.slinky = function (options) {
390        const menu = new Slinky(this, options)
391
392        return menu
393    }
394})(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.