container.js 6.53 KB
Newer Older
1
define(["jquery", "underscore", "js/views/xblock", "js/utils/module", "gettext", "common/js/components/views/feedback_notification",
2
    "jquery.ui"], // The container view uses sortable, which is provided by jquery.ui.
3
    function ($, _, XBlockView, ModuleUtils, gettext, NotificationView) {
4
        var studioXBlockWrapperClass = '.studio-xblock-wrapper';
5

6
        var ContainerView = XBlockView.extend({
7 8 9 10
            // Store the request token of the first xblock on the page (which we know was rendered by Studio when
            // the page was generated). Use that request token to filter out user-defined HTML in any
            // child xblocks within the page.
            requestToken: "",
11

12 13
            new_child_view: 'reorderable_container_child_preview',

14 15
            xblockReady: function () {
                XBlockView.prototype.xblockReady.call(this);
16 17
                var reorderableClass, reorderableContainer,
                    newParent, oldParent, self = this;
18

19 20
                this.requestToken = this.$('div.xblock').first().data('request-token');
                reorderableClass = this.makeRequestSpecificSelector('.reorderable-container');
21

22
                reorderableContainer = this.$(reorderableClass);
23
                reorderableContainer.sortable({
24 25
                    handle: '.drag-handle',

26 27 28 29 30 31
                    start: function (event, ui) {
                        // Necessary because of an open bug in JQuery sortable.
                        // http://bugs.jqueryui.com/ticket/4990
                        reorderableContainer.sortable('refreshPositions');
                    },

32
                    stop: function (event, ui) {
33 34
                        var saving, hideSaving, removeFromParent;

35
                        if (_.isUndefined(oldParent)) {
36 37 38 39 40
                            // If no actual change occurred,
                            // oldParent will never have been set.
                            return;
                        }

41
                        saving = new NotificationView.Mini({
Bertrand Marron committed
42
                            title: gettext('Saving')
43 44 45
                        });
                        saving.show();

46
                        hideSaving = function () {
47 48 49 50 51 52 53
                            saving.hide();
                        };

                        // If moving from one container to another,
                        // add to new container before deleting from old to
                        // avoid creating an orphan if the addition fails.
                        if (newParent) {
54
                            removeFromParent = oldParent;
55 56
                            self.updateChildren(newParent, function () {
                                self.updateChildren(removeFromParent, hideSaving);
57
                            });
58
                        } else {
59
                            // No new parent, only reordering within same container.
60
                            self.updateChildren(oldParent, hideSaving);
61 62 63 64 65 66 67 68 69 70 71
                        }

                        oldParent = undefined;
                        newParent = undefined;
                    },
                    update: function (event, ui) {
                        // When dragging from one ol to another, this method
                        // will be called twice (once for each list). ui.sender will
                        // be null if the change is related to the list the element
                        // was originally in (the case of a move within the same container
                        // or the deletion from a container when moving to a new container).
72
                        var parent = $(event.target).closest(studioXBlockWrapperClass);
73 74 75
                        if (ui.sender) {
                            // Move to a new container (the addition part).
                            newParent = parent;
76
                        } else {
77 78 79 80
                            // Reorder inside a container, or deletion when moving to new container.
                            oldParent = parent;
                        }
                    },
81
                    helper: "original",
82 83 84 85
                    opacity: '0.5',
                    placeholder: 'component-placeholder',
                    forcePlaceholderSize: true,
                    axis: 'y',
86 87
                    items: '> .is-draggable',
                    connectWith: reorderableClass,
88
                    tolerance: "pointer"
89 90 91 92

                });
            },

93
            updateChildren: function (targetParent, successCallback) {
94
                var children, childLocators, xblockInfo=this.model;
95

96
                // Find descendants with class "studio-xblock-wrapper" whose parent === targetParent.
97
                // This is necessary to filter our grandchildren, great-grandchildren, etc.
98 99
                children = targetParent.find(studioXBlockWrapperClass).filter(function () {
                    var parent = $(this).parent().closest(studioXBlockWrapperClass);
100 101 102
                    return parent.data('locator') === targetParent.data('locator');
                });

103
                childLocators = _.map(
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
                    children,
                    function (child) {
                        return $(child).data('locator');
                    }
                );
                $.ajax({
                    url: ModuleUtils.getUpdateUrl(targetParent.data('locator')),
                    type: 'PUT',
                    dataType: 'json',
                    contentType: 'application/json',
                    data: JSON.stringify({
                        children: childLocators
                    }),
                    success: function () {
                        // change data-parent on the element moved.
                        if (successCallback) {
                            successCallback();
                        }
122 123
                        // Update publish and last modified information from the server.
                        xblockInfo.fetch();
124 125
                    }
                });
126
            },
127

128 129 130 131
            acknowledgeXBlockDeletion: function(locator){
                this.notifyRuntime('deleted-child', locator);
            },

132
            refresh: function() {
133
                var sortableInitializedClass = this.makeRequestSpecificSelector('.reorderable-container.ui-sortable');
134
                this.$(sortableInitializedClass).sortable('refresh');
135 136 137 138
            },

            makeRequestSpecificSelector: function(selector) {
                return 'div.xblock[data-request-token="' + this.requestToken + '"] > ' + selector;
139 140 141 142
            }
        });

        return ContainerView;
143
    }); // end define();