示例#1
0
        _.each(this.events, function (method, event) {
            // If the method is a function, use the default Widget system
            if (typeof method !== 'string') {
                events[event] = method;
                return;
            }
            // If the method is only a function name without options, use the
            // default Widget system
            var methodOptions = method.split(' ');
            if (methodOptions.length <= 1) {
                events[event] = method;
                return;
            }
            // If the method has no meaningful options, use the default Widget
            // system
            var isAsync = _.contains(methodOptions, 'async');
            if (!isAsync) {
                events[event] = method;
                return;
            }

            method = self.proxy(methodOptions[methodOptions.length - 1]);
            if (_.str.startsWith(event, 'click')) {
                // Protect click handler to be called multiple times by
                // mistake by the user and add a visual disabling effect
                // for buttons.
                method = dom.makeButtonHandler(method);
            } else {
                // Protect all handlers to be recalled while the previous
                // async handler call is not finished.
                method = dom.makeAsyncHandler(method);
            }
            events[event] = method;
        });
示例#2
0
文件: web_client.js 项目: Vauxoo/odoo
 _onScrollTo: function (ev) {
     var offset = {top: ev.data.top, left: ev.data.left || 0};
     if (!offset.top) {
         offset = dom.getPosition(document.querySelector(ev.data.selector));
         // Substract the position of the action_manager as it is the scrolling part
         offset.top -= dom.getPosition(this.action_manager.el).top;
     }
     this.action_manager.el.scrollTop = offset.top;
     this.action_manager.el.scrollLeft = offset.left;
 },
示例#3
0
 focusField: function (id, fieldName, offset) {
     this.editRecord(id);
     if (typeof offset === "number") {
         var field = _.findWhere(this.allFieldWidgets[id], {name: fieldName});
         dom.setSelectionRange(field.getFocusableElement().get(0), {start: offset, end: offset});
     }
 },
示例#4
0
    start: function () {
        var self = this;

        this._$attachmentButton = this.$('.o_composer_button_add_attachment');
        this._$attachmentsList = this.$('.o_composer_attachments_list');
        this.$input = this.$('.o_composer_input textarea');
        this.$input.focus(function () {
            self.trigger('input_focused');
        });
        this.$input.val(this.options.defaultBody);
        dom.autoresize(this.$input, {
            parent: this,
            min_height: this.options.inputMinHeight
        });

        // Attachments
        this._renderAttachments();
        $(window).on(this.fileuploadID, this._onAttachmentLoaded.bind(this));
        this.on('change:attachment_ids', this, this._renderAttachments);

        this.call('mail_service', 'getMailBus')
            .on('update_typing_partners', this, this._onUpdateTypingPartners);

        // Mention
        this._mentionManager.prependTo(this.$('.o_composer'));

        // Drag-Drop files
        // Allowing body to detect dragenter and dragleave for display
        var $body = $('body');
        this._dropZoneNS = _.uniqueId('o_dz_');  // For event namespace used when multiple chat window is open
        $body.on('dragleave.' + this._dropZoneNS, this._onBodyFileDragLeave.bind(this));
        $body.on("dragover." + this._dropZoneNS, this._onBodyFileDragover.bind(this));
        $body.on("drop." + this._dropZoneNS, this._onBodyFileDrop.bind(this));
        return this._super();
    },
示例#5
0
        return $.when(this.inner_widget.appendTo(new_widget_fragment)).done(function() {
            // Detach the fragment of the previous action and store it within the action
            if (old_action) {
                old_action.set_fragment(old_action.detach());
            }
            if (!widget.need_control_panel) {
                // Hide the main ControlPanel for widgets that do not use it
                self.main_control_panel.do_hide();
            }

            // most of the time, the self.$el element should already be empty,
            // because we detached the old action just a few line up.  However,
            // it may happen that it is not empty, for example when a view
            // manager was unable to load a view because of a crash.  In any
            // case, this is done as a safety measure to avoid the 'double view'
            // situation that we had when the web client was unable to recover
            // from a crash.
            self.$el.empty();

            dom.append(self.$el, new_widget_fragment, {
                in_DOM: self.is_in_DOM,
                callbacks: [{widget: self.inner_widget}],
            });
            if (actions_to_destroy) {
                self.clear_action_stack(actions_to_destroy);
            }
            self.toggle_fullscreen();
            self.trigger_up('current_action_updated', {action: new_action});
        }).fail(function () {
示例#6
0
 return controlPanel.appendTo(fragment).then(function () {
     dom.prepend($action, fragment, {
         callbacks: [{ widget: controlPanel }],
         in_DOM: true,
     });
     return controlPanel;
 });
示例#7
0
    _onScrollTo: function (ev) {
        var offset;
        if (ev.data.selector) {
            offset = dom.getPosition(document.querySelector(ev.data.selector));
            // substract the position of the ActionManager as it is the
            // scrolling element
            var actionManagerOffset = dom.getPosition(this.action_manager.el);
            offset.left -= actionManagerOffset.left;
            offset.top -= actionManagerOffset.top;
        } else {
            offset = {top: ev.data.top || 0, left: ev.data.left || 0};
        }

        this.action_manager.el.scrollTop = offset.top;
        this.action_manager.el.scrollLeft = offset.left;
    },
示例#8
0
 return actionManager.appendTo(fragment).then(function () {
     dom.append(widget.$el, fragment, {
         callbacks: [{ widget: actionManager }],
         in_DOM: true,
     });
     return actionManager;
 });
示例#9
0
 self.opened().then(function () {
     dom.append(self.$el, fragment, {
         callbacks: [{widget: self.list_controller}],
         in_DOM: true,
     });
     self.set_buttons(self.__buttons);
 });
示例#10
0
文件: menu.js 项目: Tecnativa/odoo
 destroy: function () {
     this._super.apply(this, arguments);
     if (!this.noAutohide) {
         $(window).off('.autohideMenu');
         dom.destroyAutoMoreMenu(this.$el);
     }
 },
示例#11
0
 _detachCurrentController: function () {
     var currentController = this.getCurrentController();
     if (currentController) {
         currentController.scrollPosition = this._getScrollPosition();
         dom.detach([{widget: currentController.widget}]);
     }
 },
示例#12
0
文件: discuss.js 项目: CRITEAN/Odoo
 .then(function (messages) {
     if (self.messages_separator_position === 'top') {
         self.messages_separator_position = undefined; // reset value to re-compute separator position
     }
     self.thread.render(messages, self._getThreadRenderingOptions(messages));
     offset += dom.getPosition(document.querySelector(oldestMsgSelector)).top;
     self.thread.scroll_to({offset: offset});
 });
示例#13
0
 _renderSelector: function (tag, disableInput) {
     var $content = dom.renderCheckbox();
     if (disableInput) {
         $content.find("input[type='checkbox']").prop('disabled', disableInput);
     }
     return $('<' + tag + '>')
         .addClass('o_list_record_selector')
         .append($content);
 },
示例#14
0
 self.opened().always(function () {
     if ($buttons.children().length) {
         self.$footer.empty().append($buttons.contents());
     }
     dom.append(self.$el, fragment, {
         callbacks: [{widget: self.form_view}],
         in_DOM: true,
     });
 });
示例#15
0
 return view.appendTo(fragment).then(function () {
     dom.append($view_manager, fragment, {
         callbacks: [{widget: view}],
         in_DOM: true,
     });
     view.$el.on('click', 'a', function (ev) {
         ev.preventDefault();
     });
 }).then(function () {
示例#16
0
    _renderButtonBox: function (node) {
        var self = this;
        var $result = $('<' + node.tag + '>', { 'class': 'o_not_full' });
        var buttons = _.map(node.children, function (child) {
            if (child.tag === 'button') {
                return self._renderStatButton(child);
            } else {
                return self._renderNode(child);
            }
        });
        var buttons_partition = _.partition(buttons, function ($button) {
            return $button.is('.o_invisible_modifier');
        });
        var invisible_buttons = buttons_partition[0];
        var visible_buttons = buttons_partition[1];

        // Get the unfolded buttons according to window size
        var nb_buttons = [2, 2, 4, 6][config.device.size_class] || 7;
        var unfolded_buttons = visible_buttons.slice(0, nb_buttons).concat(invisible_buttons);

        // Get the folded buttons
        var folded_buttons = visible_buttons.slice(nb_buttons);
        if (folded_buttons.length === 1) {
            unfolded_buttons = buttons;
            folded_buttons = [];
        }

        // Toggle class to tell if the button box is full (CSS requirement)
        var full = (visible_buttons.length > nb_buttons);
        $result.toggleClass('o_full', full).toggleClass('o_not_full', !full);

        // Add the unfolded buttons
        _.each(unfolded_buttons, function ($button) {
            $button.appendTo($result);
        });

        // Add the dropdown with folded buttons if any
        if (folded_buttons.length) {
            $result.append(dom.renderButton({
                attrs: {
                    class: 'oe_stat_button o_button_more dropdown-toggle',
                    'data-toggle': 'dropdown',
                },
                text: _t("More"),
            }));

            var $dropdown = $("<div>", {'class': "dropdown-menu o_dropdown_more", role: "menu"});
            _.each(folded_buttons, function ($button) {
                $button.addClass('dropdown-item').appendTo($dropdown);
            });
            $dropdown.appendTo($result);
        }

        this._handleAttributes($result, node);
        this._registerModifiers(node, this.state, $result);
        return $result;
    },
示例#17
0
 return self._selectCell(newRowIndex, self.currentFieldIndex, {force: true}).then(function () {
     // restore the cursor position
     currentRowID = self.state.data[newRowIndex].id;
     currentWidget = self.allFieldWidgets[currentRowID][self.currentFieldIndex];
     focusedElement = currentWidget.getFocusableElement().get(0);
     if (selectionRange) {
         dom.setSelectionRange(focusedElement, selectionRange);
     }
 });
    start: function () {
        if (!this.isSection) {
            if (this.mode === 'edit') {
                dom.autoresize(this.$el, {parent: this});

                this.$el = this.$el.add(this._renderTranslateButton());
            }
        }
        return this._super.apply(this, arguments);
    },
示例#19
0
 _attachComponent: function (childInfo, $from) {
     var self = this;
     var $elements = dom.cssFind($from || this.$el, childInfo.selector);
     var defs = _.map($elements, function (element) {
         var w = new childInfo.Widget(self);
         self._widgets.push(w);
         return w.attachTo(element);
     });
     return $.when.apply($, defs);
 },
示例#20
0
    _appendController: function (controller) {
        dom.append(this.$el, controller.widget.$el, {
            in_DOM: this.isInDOM,
            callbacks: [{widget: controller.widget}],
        });

        if (controller.scrollPosition) {
            this.trigger_up('scrollTo', controller.scrollPosition);
        }
    },
示例#21
0
 .then(function () {
     // restore the selection range
     currentWidget = self.allFieldWidgets[currentRowID][self.currentFieldIndex];
     if (currentWidget) {
         focusedElement = currentWidget.getFocusableElement().get(0);
         if (selectionRange) {
             dom.setSelectionRange(focusedElement, selectionRange);
         }
     }
 });
示例#22
0
        init: function(parent, options) {
            this._super.apply(this, arguments);
            this.options = _.extend(options || {}, {
            });

            // TODO simplify this using the 'async' keyword in the events
            // property definition as soon as this widget is converted in
            // frontend widget.
            this.payEvent = dom.makeButtonHandler(this.payEvent);
        },
示例#23
0
/**
 * @todo Really? it returns a jQuery element...  We should try to avoid this and
 * let DOM utility functions handle this directly. And replace this with a
 * function that returns a string so we can get rid of the forceString.
 *
 * @param {boolean} value
 * @param {Object} [field]
 *        a description of the field (note: this parameter is ignored)
 * @param {Object} [options] additional options
 * @param {boolean} [options.forceString=false] if true, returns a string
*    representation of the boolean rather than a jQueryElement
 * @returns {jQuery|string}
 */
function formatBoolean(value, field, options) {
    if (options && options.forceString) {
        return value ? _t('True') : _t('False');
    }
    return dom.renderCheckbox({
        prop: {
            checked: value,
            disabled: true,
        },
    });
}
示例#24
0
 self.dialog.opened().then(function () {
     dom.append(self.dialog.$el, fragment, {
         in_DOM: true,
         callbacks: [{widget: self.dialog_widget}],
     });
     if ($dialogFooter) {
         self.dialog.$footer.empty().append($dialogFooter.contents());
     }
     if (options.state && self.dialog_widget.do_load_state) {
         return self.dialog_widget.do_load_state(options.state);
     }
 })
示例#25
0
            return dialog.open().opened(function () {
                self.currentDialogController = controller;

                dom.append(dialog.$el, widget.$el, {
                    in_DOM: true,
                    callbacks: [{widget: dialog}, {widget: controller.widget}],
                });
                widget.renderButtons(dialog.$footer);
                dialog.rebindButtonBehavior();

                return action;
            });
示例#26
0
 _renderTagButton: function (node) {
     var $button = dom.renderButton({
         attrs: _.omit(node.attrs, 'icon', 'string'),
         icon: node.attrs.icon,
         text: (node.attrs.string || '').replace(/_/g, ''),
     });
     $button.append(_.map(node.children, this._renderNode.bind(this)));
     this._addOnClickAction($button, node);
     this._handleAttributes($button, node);
     this._registerModifiers(node, this.state, $button);
     return $button;
 },
示例#27
0
/**
 * Create an autoresize text area with 'border-box' as box sizing rule.
 * The minimum height of this autoresize text are is 1px.
 *
 * @param {Object} [options={}]
 * @param {integer} [options.borderBottomWidth=0]
 * @param {integer} [options.borderTopWidth=0]
 * @param {integer} [options.padding=0]
 */
function prepareAutoresizeTextArea(options) {
    options = options || {};
    var $textarea = $('<textarea>');
    $textarea.css('box-sizing', 'border-box');
    $textarea.css({
        padding: options.padding || 0,
        borderTopWidth: options.borderTopWidth || 0,
        borderBottomWidth: options.borderBottomWidth || 0,
    });
    $textarea.appendTo($('#qunit-fixture'));
    dom.autoresize($textarea, { min_height: 1 });
    return $textarea;
}
示例#28
0
    _display_view: function (old_view) {
        var view_controller = this.active_view.controller;
        var view_fragment = this.active_view.$fragment;

        // Prepare the ControlPanel content and update it
        var view_control_elements = this.render_view_control_elements();
        var cp_status = {
            active_view_selector: '.o_cp_switch_' + this.active_view.type,
            breadcrumbs: this.action_manager && this.action_manager.get_breadcrumbs(),
            cp_content: _.extend({}, this.searchview_elements, view_control_elements),
            hidden: this.flags.headless,
            searchview: this.searchview,
            search_view_hidden: !this.active_view.searchable || this.active_view.searchview_hidden,
        };
        this.update_control_panel(cp_status);

        // Detach the old view and store it
        if (old_view && old_view !== this.active_view) {
            // Store the scroll position
            if (this.action_manager && this.action_manager.webclient) {
                old_view.controller.setScrollTop(this.action_manager.webclient.getScrollTop());
            }
            // Do not detach ui-autocomplete elements to let jquery-ui garbage-collect them
            var $to_detach = this.$el.contents().not('.ui-autocomplete');
            old_view.$fragment = dom.detach([{widget: old_view.controller}], {$to_detach: $to_detach});
        }

        // If the user switches from a multi-record to a mono-record view,
        // the action manager should be scrolled to the top.
        if (old_view && old_view.controller.multi_record === true && view_controller.multi_record === false) {
            view_controller.setScrollTop(0);
        }

        // Append the view fragment to this.$el
        dom.append(this.$el, view_fragment, {
            in_DOM: this.is_in_DOM,
            callbacks: [{widget: view_controller}],
        });
    },
示例#29
0
            return Promise.all(defs).then(function () {
                // update registered modifiers to edit 'mode' because the call to
                // _renderBody set baseModeByRecord as 'readonly'
                _.each(self.columns, function (node) {
                    self._registerModifiers(node, record, null, {mode: 'edit'});
                });

                // store the selection range to restore it once the table will
                // be re-rendered, and the current cell re-selected
                var currentRowID, currentWidget, focusedElement, selectionRange;
                if (self.currentRow !== null) {
                    currentRowID = self._getRecordID(self.currentRow);
                    currentWidget = self.allFieldWidgets[currentRowID][self.currentFieldIndex];
                    if (currentWidget) {
                        focusedElement = currentWidget.getFocusableElement().get(0);
                        if (currentWidget.formatType !== 'boolean') {
                            selectionRange = dom.getSelectionRange(focusedElement);
                        }
                    }
                }

                // remove all data rows except the one being edited, and insert
                // data rows of the re-rendered body before and after it
                var $editedRow = self._getRow(id);
                $editedRow.nextAll('.o_data_row').remove();
                $editedRow.prevAll('.o_data_row').remove();
                var $newRow = $newBody.find('.o_data_row[data-id="' + id + '"]');
                $newRow.prevAll('.o_data_row').get().reverse().forEach(function (row) {
                    $(row).insertBefore($editedRow);
                });
                $newRow.nextAll('.o_data_row').get().reverse().forEach(function (row) {
                    $(row).insertAfter($editedRow);
                });

                if (self.currentRow !== null) {
                    var newRowIndex = $editedRow.prop('rowIndex') - 1;
                    self.currentRow = newRowIndex;
                    return self._selectCell(newRowIndex, self.currentFieldIndex, {force: true})
                        .then(function () {
                            // restore the selection range
                            currentWidget = self.allFieldWidgets[currentRowID][self.currentFieldIndex];
                            if (currentWidget) {
                                focusedElement = currentWidget.getFocusableElement().get(0);
                                if (selectionRange) {
                                    dom.setSelectionRange(focusedElement, selectionRange);
                                }
                            }
                        });
                }
            });
示例#30
0
            return dialog.open().opened(function () {
                dom.append(dialog.$el, widget.$el, {
                    in_DOM: true,
                    callbacks: [{widget: dialog}],
                });
                // AAB: renderButtons will be a function of AbstractAction, so this
                // test won't be necessary anymore
                if (widget.renderButtons) {
                    widget.renderButtons(dialog.$footer);
                }

                self.currentDialogController = controller;

                return action;
            });