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

divio / django-cms / #28111

29 Nov 2023 12:59PM UTC coverage: 76.775%. Remained the same
#28111

push

travis-ci

web-flow
Merge branch 'develop-4' into feat/modernize-django-update-ci

1042 of 1539 branches covered (0.0%)

2519 of 3281 relevant lines covered (76.78%)

32.54 hits per line

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

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

5
import Modal from './cms.modal';
6
import $ from 'jquery';
7
import { Helpers } from './cms.base';
8
import Plugin from './cms.plugins';
9
import ls from 'local-storage';
10
var storageKey = 'cms-clipboard';
1✔
11

12
var MIN_WIDTH = 400;
1✔
13
// magic number for 1 item in clipboard
14
var MIN_HEIGHT = 117;
1✔
15

16
/**
17
 * Handles copy & paste in the structureboard.
18
 *
19
 * @class Clipboard
20
 * @namespace CMS
21
 * @uses CMS.API.Helpers
22
 */
23
class Clipboard {
24
    constructor() {
25
        this._setupUI();
28✔
26

27
        // setup events
28
        this._events();
28✔
29
        this.currentClipboardData = {};
28✔
30
    }
31

32
    /**
33
     * Caches all the jQuery element queries.
34
     *
35
     * @method _setupUI
36
     * @private
37
     */
38
    _setupUI() {
39
        var clipboard = $('.cms-clipboard');
28✔
40

41
        this.ui = {
28✔
42
            clipboard: clipboard,
43
            triggers: $('.cms-clipboard-trigger a'),
44
            triggerRemove: $('.cms-clipboard-empty a'),
45
            pluginsList: clipboard.find('.cms-clipboard-containers'),
46
            document: $(document)
47
        };
48
    }
49

50
    /**
51
     * Sets up event handlers for clipboard ui.
52
     *
53
     * @method _events
54
     * @private
55
     */
56
    _events() {
57
        var that = this;
28✔
58

59
        that.modal = new Modal({
28✔
60
            minWidth: MIN_WIDTH,
61
            minHeight: MIN_HEIGHT,
62
            minimizable: false,
63
            maximizable: false,
64
            resizable: false,
65
            closeOnEsc: false
66
        });
67

68
        Helpers.removeEventListener(
28✔
69
            'modal-loaded.clipboard modal-closed.clipboard modal-close.clipboard modal-load.clipboard'
70
        );
71

72
        Helpers.addEventListener('modal-loaded.clipboard modal-closed.clipboard', (e, { instance }) => {
28✔
73
            if (instance === this.modal) {
6!
74
                Plugin._removeAddPluginPlaceholder();
6✔
75
            }
76
        });
77

78
        Helpers.addEventListener('modal-close.clipboard', (e, { instance }) => {
28✔
79
            if (instance === this.modal) {
×
80
                this.ui.pluginsList.prependTo(that.ui.clipboard);
×
81
                Plugin._updateClipboard();
×
82
            }
83
        });
84
        Helpers.addEventListener('modal-load.clipboard', (e, { instance }) => {
28✔
85
            if (instance === this.modal) {
5!
86
                this.ui.pluginsList.prependTo(that.ui.clipboard);
5✔
87
            } else {
88
                this.ui.pluginsList.prependTo(that.ui.clipboard);
×
89
                Plugin._updateClipboard();
×
90
            }
91
        });
92

93
        try {
28✔
94
            ls.off(storageKey);
28✔
95
        } catch (e) {}
96
        ls.on(storageKey, value => this._handleExternalUpdate(value));
28✔
97

98
        this._toolbarEvents();
28✔
99
    }
100

101
    _toolbarEvents() {
102
        var that = this;
28✔
103

104
        that.ui.triggers.off(Clipboard.click).on(Clipboard.click, function(e) {
28✔
105
            e.preventDefault();
6✔
106
            e.stopPropagation();
6✔
107
            if ($(this).parent().hasClass('cms-toolbar-item-navigation-disabled')) {
6✔
108
                return false;
1✔
109
            }
110

111
            that.modal.open({
5✔
112
                html: that.ui.pluginsList,
113
                title: that.ui.clipboard.data('title'),
114
                width: MIN_WIDTH,
115
                height: MIN_HEIGHT
116
            });
117
            that.ui.document.trigger('click.cms.toolbar');
5✔
118
        });
119

120
        // add remove event
121
        that.ui.triggerRemove.off(Clipboard.click).on(Clipboard.click, function(e) {
28✔
122
            e.preventDefault();
3✔
123
            e.stopPropagation();
3✔
124
            if ($(this).parent().hasClass('cms-toolbar-item-navigation-disabled')) {
3✔
125
                return false;
1✔
126
            }
127
            that.clear();
2✔
128
        });
129
    }
130

131
    /**
132
     * _handleExternalUpdate
133
     *
134
     * @private
135
     * @param {String} value event new value
136
     */
137
    _handleExternalUpdate(value) {
138
        var that = this;
5✔
139
        var clipboardData = JSON.parse(value);
5✔
140

141
        if (
5✔
142
            clipboardData.timestamp < that.currentClipboardData.timestamp ||
13✔
143
            (that.currentClipboardData.data &&
144
                that.currentClipboardData.data.plugin_id === clipboardData.data.plugin_id)
145
        ) {
146
            that.currentClipboardData = clipboardData;
2✔
147
            return;
2✔
148
        }
149

150
        if (!clipboardData.data.plugin_id) {
3✔
151
            that._cleanupDOM();
1✔
152
            that.currentClipboardData = clipboardData;
1✔
153
            return;
1✔
154
        }
155

156
        if (!that.currentClipboardData.data.plugin_id) {
2✔
157
            that._enableTriggers();
1✔
158
        }
159

160
        that.ui.pluginsList.html(clipboardData.html);
2✔
161
        Plugin._updateClipboard();
2✔
162
        CMS._instances.push(new Plugin(`cms-plugin-${clipboardData.data.plugin_id}`, clipboardData.data));
2✔
163

164
        that.currentClipboardData = clipboardData;
2✔
165
    }
166

167
    /**
168
     * @method _isClipboardModalOpen
169
     * @private
170
     * @returns {Boolean}
171
     */
172
    _isClipboardModalOpen() {
173
        return !!this.modal.ui.modalBody.find('.cms-clipboard-containers').length;
5✔
174
    }
175

176
    /**
177
     * Cleans up DOM state when clipboard is cleared
178
     *
179
     * @method _cleanupDOM
180
     * @private
181
     */
182
    _cleanupDOM() {
183
        var that = this;
5✔
184
        var pasteItems = $('.cms-submenu-item [data-rel=paste]')
5✔
185
            .attr('tabindex', '-1')
186
            .parent()
187
            .addClass('cms-submenu-item-disabled');
188

189
        pasteItems.find('> a').attr('aria-disabled', 'true');
5✔
190
        pasteItems.find('.cms-submenu-item-paste-tooltip').css('display', 'none');
5✔
191
        pasteItems.find('.cms-submenu-item-paste-tooltip-empty').css('display', 'block');
5✔
192

193
        if (that._isClipboardModalOpen()) {
5✔
194
            that.modal.close();
1✔
195
        }
196

197
        that._disableTriggers();
5✔
198
        that.ui.document.trigger('click.cms.toolbar');
5✔
199
    }
200

201
    /**
202
     * @method _enableTriggers
203
     * @private
204
     */
205
    _enableTriggers() {
206
        this.ui.triggers.parent().removeClass('cms-toolbar-item-navigation-disabled');
1✔
207
        this.ui.triggerRemove.parent().removeClass('cms-toolbar-item-navigation-disabled');
1✔
208
    }
209

210
    /**
211
     * @method _disableTriggers
212
     * @private
213
     */
214
    _disableTriggers() {
215
        this.ui.triggers.parent().addClass('cms-toolbar-item-navigation-disabled');
5✔
216
        this.ui.triggerRemove.parent().addClass('cms-toolbar-item-navigation-disabled');
5✔
217
    }
218

219
    /**
220
     * Clears the clipboard by quering the server.
221
     * Callback is optional, but if provided - it's called
222
     * no matter what outcome was of the ajax call.
223
     *
224
     * @method clear
225
     * @param {Function} [callback]
226
     */
227
    clear(callback) {
228
        var that = this;
3✔
229
        // post needs to be a string, it will be converted using JSON.parse
230
        var post = '{ "csrfmiddlewaretoken": "' + CMS.config.csrf + '" }';
3✔
231

232
        that._cleanupDOM();
3✔
233

234
        // redirect to ajax
235
        CMS.API.Toolbar.openAjax({
3✔
236
            url: Helpers.updateUrlWithPath(CMS.config.clipboard.url),
237
            post: post,
238
            callback: function() {
239
                var args = Array.prototype.slice.call(arguments);
1✔
240

241
                that.populate('', {});
1✔
242
                // istanbul ignore next
243
                if (callback) {
244
                    callback.apply(this, args);
245
                }
246
            }
247
        });
248
    }
249

250
    /**
251
     * populate
252
     *
253
     * @public
254
     * @param {String} html markup of the clipboard draggable
255
     * @param {Object} pluginData data of the plugin in the clipboard
256
     */
257
    populate(html, pluginData) {
258
        this.currentClipboardData = {
6✔
259
            data: pluginData,
260
            timestamp: Date.now(),
261
            html: html
262
        };
263

264
        ls.set(storageKey, JSON.stringify(this.currentClipboardData));
6✔
265
    }
266
}
267

268
Clipboard.click = 'click.cms.clipboard';
1✔
269

270
export default Clipboard;
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