• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

divio / django-cms / #27677

25 Aug 2023 04:03PM UTC coverage: 76.775% (-0.6%) from 77.361%
#27677

push

travis-ci

web-flow
Update CHANGELOG.rst

1042 of 1539 branches covered (0.0%)

2519 of 3281 relevant lines covered (76.78%)

32.71 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.28
/cms/static/cms/js/modules/cms.toolbar.js
1
/*
2
 * Copyright https://github.com/divio/django-cms
3
 */
4

5
import $ from 'jquery';
6
import Class from 'classjs';
7
import Navigation from './cms.navigation';
8
import Sideframe from './cms.sideframe';
9
import Modal from './cms.modal';
10
import Plugin from './cms.plugins';
11
import { DiffDOM } from 'diff-dom';
12
import { filter, throttle, uniq } from 'lodash';
13
import { showLoader, hideLoader } from './loader';
14
import { Helpers, KEYS } from './cms.base';
15

16
var SECOND = 1000;
1✔
17
var TOOLBAR_OFFSCREEN_OFFSET = 10; // required to hide box-shadow
1✔
18
var dd;
19

20
export const getPlaceholderIds = pluginRegistry =>
1✔
21
    uniq(filter(pluginRegistry, ([, opts]) => opts.type === 'placeholder').map(([, opts]) => opts.placeholder_id));
2✔
22

23
/**
24
 * @function hideDropdownIfRequired
25
 * @private
26
 * @param {jQuery} publishBtn
27
 */
28
function hideDropdownIfRequired(publishBtn) {
29
    var dropdown = publishBtn.closest('.cms-dropdown');
1✔
30

31
    if (dropdown.length && dropdown.find('li[data-cms-hidden]').length === dropdown.find('li').length) {
1!
32
        dropdown.hide().attr('data-cms-hidden', 'true');
×
33
    }
34
}
35

36
/**
37
 * The toolbar is the generic element which holds various components
38
 * together and provides several commonly used API methods such as
39
 * show/hide, message display or loader indication.
40
 *
41
 * @class Toolbar
42
 * @namespace CMS
43
 * @uses CMS.API.Helpers
44
 */
45
var Toolbar = new Class({
1✔
46
    implement: [Helpers],
47

48
    options: {
49
        toolbarDuration: 200
50
    },
51

52
    initialize: function initialize(options) {
53
        this.options = $.extend(true, {}, this.options, options);
38✔
54

55
        // elements
56
        this._setupUI();
38✔
57

58
        /**
59
         * @property {CMS.Navigation} navigation
60
         */
61
        this.navigation = new Navigation();
38✔
62

63
        /**
64
         * @property {Object} _position
65
         * @property {Number} _position.top current position of the toolbar
66
         * @property {Number} _position.top position when toolbar became non-sticky
67
         * @property {Boolean} _position.isSticky is toolbar sticky?
68
         * @see _handleLongMenus
69
         * @private
70
         */
71
        this._position = {
38✔
72
            top: 0,
73
            stickyTop: 0,
74
            isSticky: true
75
        };
76

77
        // states
78
        this.click = 'click.cms.toolbar';
38✔
79
        this.touchStart = 'touchstart.cms.toolbar';
38✔
80
        this.pointerUp = 'pointerup.cms.toolbar';
38✔
81
        this.pointerOverOut = 'pointerover.cms.toolbar pointerout.cms.toolbar';
38✔
82
        this.pointerLeave = 'pointerleave.cms.toolbar';
38✔
83
        this.mouseEnter = 'mouseenter.cms.toolbar';
38✔
84
        this.mouseLeave = 'mouseleave.cms.toolbar';
38✔
85
        this.resize = 'resize.cms.toolbar';
38✔
86
        this.scroll = 'scroll.cms.toolbar';
38✔
87
        this.key = 'keydown.cms.toolbar keyup.cms.toolbar';
38✔
88

89
        // istanbul ignore next: function is always reassigned
90
        this.timer = function() {};
91
        this.lockToolbar = false;
38✔
92

93
        // setup initial stuff
94
        if (!this.ui.toolbar.data('ready')) {
38✔
95
            this._events();
36✔
96
        }
97

98
        this._initialStates();
38✔
99

100
        // set a state to determine if we need to reinitialize this._events();
101
        this.ui.toolbar.data('ready', true);
38✔
102

103
        dd = new DiffDOM({
38✔
104
            preDiffApply(info) {
105
                if (
×
106
                    (info.diff.action === 'removeAttribute' || info.diff.action === 'modifyAttribute') &&
×
107
                    info.diff.name === 'style' &&
108
                    $('.cms-toolbar').is(info.node)
109
                ) {
110
                    return true;
×
111
                }
112
            }
113
        });
114
    },
115

116
    /**
117
     * Stores all jQuery references within `this.ui`.
118
     *
119
     * @method _setupUI
120
     * @private
121
     */
122
    _setupUI: function _setupUI() {
123
        var container = $('.cms');
38✔
124

125
        this.ui = {
38✔
126
            container: container,
127
            body: $('html'),
128
            document: $(document),
129
            window: $(window),
130
            toolbar: container.find('.cms-toolbar'),
131
            navigations: container.find('.cms-toolbar-item-navigation'),
132
            buttons: container.find('.cms-toolbar-item-buttons'),
133
            messages: container.find('.cms-messages'),
134
            structureBoard: container.find('.cms-structure'),
135
            toolbarSwitcher: $('.cms-toolbar-item-cms-mode-switcher'),
136
            revert: $('.cms-toolbar-revert')
137
        };
138
    },
139

140
    /**
141
     * Sets up all the event handlers, such as closing and resizing.
142
     *
143
     * @method _events
144
     * @private
145
     */
146
    _events: function _events() {
147
        var that = this;
36✔
148
        var LONG_MENUS_THROTTLE = 10;
36✔
149

150
        // attach event to the navigation elements
151
        this.ui.navigations.each(function() {
36✔
152
            var navigation = $(this);
72✔
153
            var lists = navigation.find('li');
72✔
154
            var root = 'cms-toolbar-item-navigation';
72✔
155
            var hover = 'cms-toolbar-item-navigation-hover';
72✔
156
            var disabled = 'cms-toolbar-item-navigation-disabled';
72✔
157
            var children = 'cms-toolbar-item-navigation-children';
72✔
158
            var isTouchingTopLevelMenu = false;
72✔
159
            var open = false;
72✔
160
            var cmdPressed = false;
72✔
161

162
            /**
163
             * Resets all the hover state classes and events
164
             * @function reset
165
             */
166
            function reset() {
167
                open = false;
6✔
168
                cmdPressed = false;
6✔
169
                lists.removeClass(hover);
6✔
170
                lists.find('ul ul').hide();
6✔
171
                navigation.find('> li').off(that.mouseEnter);
6✔
172
                that.ui.document.off(that.click);
6✔
173
                that.ui.toolbar.off(that.click, reset);
6✔
174
                that.ui.structureBoard.off(that.click);
6✔
175
                that.ui.window.off(that.resize + '.menu.reset');
6✔
176
                that._handleLongMenus();
6✔
177
            }
178

179
            that.ui.window.on('keyup.cms.toolbar', function(e) {
72✔
180
                if (e.keyCode === CMS.KEYS.ESC) {
1,622!
181
                    reset();
×
182
                }
183
            });
184

185
            navigation
72✔
186
                .find('> li > a')
187
                .add(that.ui.toolbar.find('.cms-toolbar-item:not(.cms-toolbar-item-navigation) > a'))
188
                .off('keyup.cms.toolbar.reset')
189
                .on('keyup.cms.toolbar.reset', function(e) {
190
                    if (e.keyCode === CMS.KEYS.TAB) {
×
191
                        reset();
×
192
                    }
193
                });
194

195
            // remove events from first level
196
            navigation
72✔
197
                .find('a')
198
                .on(that.click + ' ' + that.key, function(e) {
199
                    var el = $(this);
7✔
200

201
                    // we need to restore the default behaviour once a user
202
                    // presses ctrl/cmd and clicks on the entry. In this
203
                    // case a new tab should open. First we determine if
204
                    // ctrl/cmd is pressed:
205
                    if (
7✔
206
                        e.keyCode === KEYS.CMD_LEFT ||
35✔
207
                        e.keyCode === KEYS.CMD_RIGHT ||
208
                        e.keyCode === KEYS.CMD_FIREFOX ||
209
                        e.keyCode === KEYS.SHIFT ||
210
                        e.keyCode === KEYS.CTRL
211
                    ) {
212
                        cmdPressed = true;
2✔
213
                    }
214
                    if (e.type === 'keyup') {
7✔
215
                        cmdPressed = false;
1✔
216
                    }
217

218
                    if (el.attr('href') !== '' && el.attr('href') !== '#' && !el.parent().hasClass(disabled)) {
7✔
219
                        if (cmdPressed && e.type === 'click') {
3✔
220
                            // control the behaviour when ctrl/cmd is pressed
221
                            Helpers._getWindow().open(el.attr('href'), '_blank');
1✔
222
                        } else if (e.type === 'click') {
2✔
223
                            // otherwise delegate as usual
224
                            that._delegate($(this));
1✔
225
                        } else {
226
                            // tabbing through
227
                            return;
1✔
228
                        }
229

230
                        reset();
2✔
231
                        return false;
2✔
232
                    }
233
                })
234
                .on(that.touchStart, function() {
235
                    isTouchingTopLevelMenu = true;
4✔
236
                });
237

238
            // handle click states
239
            lists.on(that.click, function(e) {
72✔
240
                e.preventDefault();
9✔
241
                e.stopPropagation();
9✔
242
                var el = $(this);
9✔
243

244
                // close navigation once it's pressed again
245
                if (el.parent().hasClass(root) && open) {
9✔
246
                    that.ui.body.trigger(that.click);
1✔
247
                    return false;
1✔
248
                }
249

250
                // close if el does not have children
251
                if (!el.hasClass(children)) {
8✔
252
                    reset();
1✔
253
                }
254

255
                var isRootNode = el.parent().hasClass(root);
8✔
256

257
                if ((isRootNode && el.hasClass(hover)) || (el.hasClass(disabled) && !isRootNode)) {
8!
258
                    return false;
×
259
                }
260

261
                el.addClass(hover);
8✔
262
                that._handleLongMenus();
8✔
263

264
                // activate hover selection
265
                if (!isTouchingTopLevelMenu) {
8✔
266
                    // we only set the handler for mouseover when not touching because
267
                    // the mouseover actually is triggered on touch devices :/
268
                    navigation.find('> li').on(that.mouseEnter, function() {
7✔
269
                        // cancel if item is already active
270
                        if ($(this).hasClass(hover)) {
4✔
271
                            return false;
2✔
272
                        }
273
                        open = false;
2✔
274
                        $(this).trigger(that.click);
2✔
275
                    });
276
                }
277

278
                isTouchingTopLevelMenu = false;
8✔
279
                // create the document event
280
                that.ui.document.on(that.click, reset);
8✔
281
                that.ui.structureBoard.on(that.click, reset);
8✔
282
                that.ui.toolbar.on(that.click, reset);
8✔
283
                that.ui.window.on(that.resize + '.menu.reset', throttle(reset, SECOND));
8✔
284
                // update states
285
                open = true;
8✔
286
            });
287

288
            // attach hover
289
            lists
72✔
290
                .on(that.pointerOverOut + ' keyup.cms.toolbar', 'li', function(e) {
291
                    var el = $(this);
2✔
292
                    var parent = el
2✔
293
                        .closest('.cms-toolbar-item-navigation-children')
294
                        .add(el.parents('.cms-toolbar-item-navigation-children'));
295
                    var hasChildren = el.hasClass(children) || parent.length;
2✔
296

297
                    // do not attach hover effect if disabled
298
                    // cancel event if element has already hover class
299
                    if (el.hasClass(disabled)) {
2!
300
                        e.stopPropagation();
×
301
                        return;
×
302
                    }
303
                    if (el.hasClass(hover) && e.type !== 'keyup') {
2!
304
                        return true;
×
305
                    }
306

307
                    // reset
308
                    lists.find('li').removeClass(hover);
2✔
309

310
                    // add hover effect
311
                    el.addClass(hover);
2✔
312

313
                    // handle children elements
314
                    if (
2✔
315
                        (hasChildren && e.type !== 'keyup') ||
7✔
316
                        (hasChildren && e.type === 'keyup' && e.keyCode === CMS.KEYS.ENTER)
317
                    ) {
318
                        el.find('> ul').show();
1✔
319
                        // add parent class
320
                        parent.addClass(hover);
1✔
321
                        that._handleLongMenus();
1✔
322
                    } else if (e.type !== 'keyup') {
1!
323
                        lists.find('ul ul').hide();
×
324
                        that._handleLongMenus();
×
325
                    }
326

327
                    // Remove stale submenus
328
                    el.siblings().find('> ul').hide();
2✔
329
                })
330
                .on(that.click, function(e) {
331
                    e.preventDefault();
9✔
332
                    e.stopPropagation();
9✔
333
                });
334

335
            // fix leave event
336
            lists.on(that.pointerLeave, '> ul', function() {
72✔
337
                lists.find('li').removeClass(hover);
×
338
            });
339
        });
340

341
        // attach event for first page publish
342
        this.ui.buttons.each(function() {
36✔
343
            var btn = $(this);
108✔
344
            var links = btn.find('a');
108✔
345

346
            links.each(function(i, el) {
108✔
347
                var link = $(el);
108✔
348

349
                // in case the button has a data-rel attribute
350
                if (link.attr('data-rel')) {
108✔
351
                    link.off(that.click).on(that.click, function(e) {
36✔
352
                        e.preventDefault();
1✔
353
                        that._delegate($(this));
1✔
354
                    });
355
                } else {
356
                    link.off(that.click).on(that.click, function(e) {
72✔
357
                        e.stopPropagation();
1✔
358
                    });
359
                }
360
            });
361
        });
362

363
        this.ui.window
36✔
364
            .off([this.resize, this.scroll].join(' '))
365
            .on(
366
                [this.resize, this.scroll].join(' '),
367
                throttle($.proxy(this._handleLongMenus, this), LONG_MENUS_THROTTLE)
368
            );
369
    },
370

371
    /**
372
     * We check for various states on load if elements in the toolbar
373
     * should appear or trigger other components. This precedes a timeout
374
     * which is not optimal and should be addressed separately.
375
     *
376
     * @method _initialStates
377
     * @private
378
     * @deprecated this method is deprecated now, it will be removed in > 3.2
379
     */
380
    // eslint-disable-next-line complexity
381
    _initialStates: function _initialStates() {
382
        var publishBtn = $('.cms-btn-publish').parent();
1✔
383

384
        this._show({ duration: 0 });
1✔
385

386
        // hide publish button
387
        publishBtn.hide().attr('data-cms-hidden', 'true');
1✔
388

389
        if ($('.cms-btn-publish-active').length) {
1!
390
            publishBtn.show().removeAttr('data-cms-hidden');
1✔
391
            this.ui.window.trigger('resize');
1✔
392
        }
393

394
        hideDropdownIfRequired(publishBtn);
1✔
395

396
        // check if debug is true
397
        if (CMS.config.debug) {
1!
398
            this._debug();
×
399
        }
400

401
        // check if there are messages and display them
402
        if (CMS.config.messages) {
1!
403
            CMS.API.Messages.open({
×
404
                message: CMS.config.messages
405
            });
406
        }
407

408
        // check if there are error messages and display them
409
        if (CMS.config.error) {
1!
410
            CMS.API.Messages.open({
×
411
                message: CMS.config.error,
412
                error: true
413
            });
414
        }
415

416
        // should switcher indicate that there is an unpublished page?
417
        if (CMS.config.publisher) {
1!
418
            CMS.API.Messages.open({
×
419
                message: CMS.config.publisher,
420
                dir: 'right'
421
            });
422
        }
423

424
        // open sideframe if it was previously opened and it's enabled
425
        var sideFrameEnabled = typeof CMS.settings.sideframe_enabled === 'undefined' || CMS.settings.sideframe_enabled;
1✔
426

427
        if (CMS.settings.sideframe
1!
428
            && CMS.settings.sideframe.url
429
            && CMS.config.auth
430
            && sideFrameEnabled
431
        ) {
432
            var sideframe = CMS.API.Sideframe || new Sideframe();
×
433

434
            sideframe.open({
×
435
                url: CMS.settings.sideframe.url,
436
                animate: false
437
            });
438
        }
439

440
        // set color scheme
441
        Helpers.setColorScheme (
1✔
442
            localStorage.getItem('theme') || CMS.config.color_scheme || 'auto'
1!
443
        );
444

445
        // add toolbar ready class to body and fire event
446
        this.ui.body.addClass('cms-ready');
1✔
447
        this.ui.document.trigger('cms-ready');
1✔
448
    },
449

450
    /**
451
     * Animation helper for opening the toolbar.
452
     *
453
     * @method _show
454
     * @private
455
     * @param {Object} [opts]
456
     * @param {Number} [opts.duration] time in milliseconds for toolbar to animate
457
     */
458
    _show: function _show(opts) {
459
        var that = this;
1✔
460
        var speed = opts && opts.duration !== undefined ? opts.duration : this.options.toolbarDuration;
1!
461
        var toolbarHeight = $('.cms-toolbar').height() + TOOLBAR_OFFSCREEN_OFFSET;
1✔
462

463
        this.ui.body.addClass('cms-toolbar-expanding');
1✔
464
        // animate html
465
        this.ui.body.animate(
1✔
466
            {
467
                'margin-top': toolbarHeight - TOOLBAR_OFFSCREEN_OFFSET
468
            },
469
            speed,
470
            'linear',
471
            function() {
472
                that.ui.body.removeClass('cms-toolbar-expanding');
1✔
473
                that.ui.body.addClass('cms-toolbar-expanded');
1✔
474
            }
475
        );
476
        // set messages top to toolbar height
477
        this.ui.messages.css('top', toolbarHeight - TOOLBAR_OFFSCREEN_OFFSET);
1✔
478
    },
479

480
    /**
481
     * Makes a request to the given url, runs optional callbacks.
482
     *
483
     * @method openAjax
484
     * @param {Object} opts
485
     * @param {String} opts.url url where the ajax points to
486
     * @param {String} [opts.post] post data to be passed (must be stringified JSON)
487
     * @param {String} [opts.method='POST'] ajax method
488
     * @param {String} [opts.text] message to be displayed
489
     * @param {Function} [opts.callback] custom callback instead of reload
490
     * @param {String} [opts.onSuccess] reload and display custom message
491
     * @returns {Boolean|jQuery.Deferred} either false or a promise
492
     */
493
    openAjax: function(opts) {
494
        var that = this;
14✔
495
        // url, post, text, callback, onSuccess
496
        var url = opts.url;
14✔
497
        var post = opts.post || '{}';
14✔
498
        var text = opts.text || '';
14✔
499
        var callback = opts.callback;
14✔
500
        var method = opts.method || 'POST';
14✔
501
        var onSuccess = opts.onSuccess;
14✔
502
        var question = text ? Helpers.secureConfirm(text) : true;
14✔
503

504
        // cancel if question has been denied
505
        if (!question) {
14✔
506
            return false;
1✔
507
        }
508

509
        showLoader();
13✔
510

511
        return $.ajax({
13✔
512
            type: method,
513
            url: url,
514
            data: JSON.parse(post)
515
        })
516
            .done(function(response) {
517
                CMS.API.locked = false;
8✔
518

519
                if (callback) {
8✔
520
                    callback(that, response);
2✔
521
                    hideLoader();
2✔
522
                } else if (onSuccess) {
6✔
523
                    if (onSuccess === 'FOLLOW_REDIRECT') {
4✔
524
                        Helpers.reloadBrowser(response.url);
1✔
525
                    } else {
526
                        Helpers.reloadBrowser(onSuccess);
3✔
527
                    }
528
                } else {
529
                    // reload
530
                    Helpers.reloadBrowser();
2✔
531
                }
532
            })
533
            .fail(function(jqXHR) {
534
                CMS.API.locked = false;
2✔
535

536
                CMS.API.Messages.open({
2✔
537
                    message: jqXHR.responseText + ' | ' + jqXHR.status + ' ' + jqXHR.statusText,
538
                    error: true
539
                });
540
            });
541
    },
542

543
    /**
544
     * Public api for `./loader.js`
545
     */
546
    showLoader: function () {
547
        showLoader();
×
548
    },
549

550
    hideLoader: function () {
551
        hideLoader();
×
552
    },
553

554
    /**
555
     * Delegates event from element to appropriate functionalities.
556
     *
557
     * @method _delegate
558
     * @param {jQuery} el trigger element
559
     * @private
560
     * @returns {Boolean|void}
561
     */
562
    _delegate: function _delegate(el) {
563
        // save local vars
564
        var target = el.data('rel');
7✔
565

566
        if (el.hasClass('cms-btn-disabled')) {
7✔
567
            return false;
1✔
568
        }
569

570
        switch (target) {
6!
571
            case 'modal':
572
                Plugin._removeAddPluginPlaceholder();
1✔
573

574
                var modal = new Modal({
1✔
575
                    onClose: el.data('on-close')
576
                });
577

578
                modal.open({
1✔
579
                    url: Helpers.updateUrlWithPath(el.attr('href')),
580
                    title: el.data('name')
581
                });
582
                break;
1✔
583
            case 'message':
584
                CMS.API.Messages.open({
1✔
585
                    message: el.data('text')
586
                });
587
                break;
1✔
588
            case 'ajax':
589
                this.openAjax({
1✔
590
                    url: el.attr('href'),
591
                    post: JSON.stringify(el.data('post')),
592
                    method: el.data('method'),
593
                    text: el.data('text'),
594
                    onSuccess: el.data('on-success')
595
                });
596
                break;
1✔
597
            case 'color-toggle':
598
                Helpers.toggleColorScheme();
×
599
                break;
×
600
            case 'sideframe':
601
                // If the sideframe is enabled, show it
602
                if (typeof CMS.settings.sideframe_enabled === 'undefined' || CMS.settings.sideframe_enabled) {
2✔
603
                    var sideframe = CMS.API.Sideframe || new Sideframe({
1✔
604
                        onClose: el.data('on-close')
605
                    });
606

607
                    sideframe.open({
1✔
608
                        url: el.attr('href'),
609
                        animate: true
610
                    });
611
                    break;
1✔
612
                }
613
                // Else fall through to default, the sideframe is disabled
614

615
            default:
616
                Helpers._getWindow().location.href = el.attr('href');
2✔
617
        }
618
    },
619

620
    /**
621
     * Handles the debug bar when `DEBUG=true` on top of the toolbar.
622
     *
623
     * @method _debug
624
     * @private
625
     */
626
    _debug: function _debug() {
627
        if (!CMS.config.lang.debug) {
2!
628
            return;
×
629
        }
630

631
        var timeout = 1000;
2✔
632
        // istanbul ignore next: function always reassigned
633
        var timer = function() {};
634

635
        // bind message event
636
        var debug = this.ui.container.find('.cms-debug-bar');
2✔
637

638
        debug.on(this.mouseEnter + ' ' + this.mouseLeave, function(e) {
2✔
639
            clearTimeout(timer);
3✔
640

641
            if (e.type === 'mouseenter') {
3✔
642
                timer = setTimeout(function() {
2✔
643
                    CMS.API.Messages.open({
1✔
644
                        message: CMS.config.lang.debug
645
                    });
646
                }, timeout);
647
            }
648
        });
649
    },
650

651
    /**
652
     * Handles the case when opened menu doesn't fit the screen.
653
     *
654
     * @method _handleLongMenus
655
     * @private
656
     */
657
    _handleLongMenus: function _handleLongMenus() {
658
        var openMenus = $('.cms-toolbar-item-navigation-hover > ul');
16✔
659

660
        if (!openMenus.length) {
16✔
661
            this._stickToolbar();
6✔
662
            return;
6✔
663
        }
664

665
        var positions = openMenus.toArray().map(function(item) {
10✔
666
            var el = $(item);
12✔
667

668
            return $.extend({}, el.position(), { height: el.height() });
12✔
669
        });
670
        var windowHeight = this.ui.window.height();
10✔
671

672
        this._position.top = this.ui.window.scrollTop();
10✔
673

674
        var shouldUnstickToolbar = positions.some(function(item) {
10✔
675
            return item.top + item.height > windowHeight;
12✔
676
        });
677

678
        if (shouldUnstickToolbar && this._position.top >= this._position.stickyTop) {
10!
679
            if (this._position.isSticky) {
×
680
                this._unstickToolbar();
×
681
            }
682
        } else {
683
            this._stickToolbar();
10✔
684
        }
685
    },
686

687
    /**
688
     * Resets toolbar to the normal position.
689
     *
690
     * @method _stickToolbar
691
     * @private
692
     */
693
    _stickToolbar: function _stickToolbar() {
694
        this._position.stickyTop = 0;
16✔
695
        this._position.isSticky = true;
16✔
696
        this.ui.body.removeClass('cms-toolbar-non-sticky');
16✔
697
        this.ui.toolbar.css({
16✔
698
            top: 0
699
        });
700
    },
701

702
    /**
703
     * Positions toolbar absolutely so the long menus can be scrolled
704
     * (toolbar goes away from the screen if required)
705
     *
706
     * @method _unstickToolbar
707
     * @private
708
     */
709
    _unstickToolbar: function _unstickToolbar() {
710
        this._position.stickyTop = this._position.top;
×
711
        this.ui.body.addClass('cms-toolbar-non-sticky');
×
712
        // have to do the !important because of "debug" toolbar
713
        this.ui.toolbar[0].style.setProperty('top', this._position.stickyTop + 'px', 'important');
×
714
        this._position.isSticky = false;
×
715
    },
716

717
    /**
718
     * Show publish button and handle the case when it's in the dropdown.
719
     * Also enable revert to live
720
     *
721
     * @method onPublishAvailable
722
     * @public
723
     * @deprecated since 3.5 due to us reloading the toolbar instead
724
     */
725
    onPublishAvailable: function showPublishButton() {
726
        // show publish / save buttons
727
        // istanbul ignore next
728
        // eslint-disable-next-line no-console
729
        console.warn('This method is deprecated and will be removed in future versions');
730
    },
731

732
    _refreshMarkup: function(newToolbar) {
733
        const switcher = this.ui.toolbarSwitcher.detach();
1✔
734
        const diff = dd.diff(this.ui.toolbar[0], newToolbar[0]);
1✔
735

736
        dd.apply(this.ui.toolbar[0], diff);
1✔
737

738
        $('.cms-toolbar-item-cms-mode-switcher').replaceWith(switcher);
1✔
739

740
        this._setupUI();
1✔
741

742
        // have to clone the nav to eliminate double events
743
        // there must be a better way to do this
744
        var clone = this.ui.navigations.clone();
1✔
745

746
        this.ui.navigations.replaceWith(clone);
1✔
747
        this.ui.navigations = clone;
1✔
748

749
        this._events();
1✔
750
        this.navigation = new Navigation();
1✔
751
        this.navigation.ui.window.trigger('resize');
1✔
752

753
        CMS.API.Clipboard.ui.triggers = $('.cms-clipboard-trigger a');
1✔
754
        CMS.API.Clipboard.ui.triggerRemove = $('.cms-clipboard-empty a');
1✔
755
        CMS.API.Clipboard._toolbarEvents();
1✔
756
    }
757
});
758

759
export default Toolbar;
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc