<commit_before>odoo.define('sale_timesheet.ProjectPlan', function (require) {
'use strict';

var ControlPanelMixin = require('web.ControlPanelMixin');
var core = require('web.core');
var data = require('web.data');
var pyeval = require('web.pyeval');
var SearchView = require('web.SearchView');
var Widget = require('web.Widget');

var _t = core._t;

var ProjectPlan = Widget.extend(ControlPanelMixin, {
    events: {
        "click a[type='action']": "_onClickAction",
        "click .o_timesheet_plan_redirect": '_onRedirect',
        "click .oe_stat_button": "_onClickStatButton",
        "click .o_timesheet_plan_sale_timesheet_people_time .progress-bar": '_onClickEmployeeProgressbar',
    },
    /**
     * @override
     */
     init: function (parent, action) {
        this._super.apply(this, arguments);
        this.action = action;
        this.action_manager = parent;
        this.set('title', action.name || _t('Overview'));
    },
    /**
     * @override
     */
    willStart: function () {
        var self = this;
        var view_id = this.action && this.action.search_view_id && this.action.search_view_id[0];
        var def = this
            .loadViews('account.analytic.line', this.action.context || {}, [[view_id, 'search']])
            .then(function (result) {
                self.fields_view = result.search;
            });
        return $.when(this._super(), def);
    },
    /**
     * @override
     */
    start: function () {
        var self = this;

        // find default search from context
        var search_defaults = {};
        var context = this.action.context || [];
        _.each(context, function (value, key) {
            var match = /^search_default_(.*)$/.exec(key);
            if (match) {
                search_defaults[match[1]] = value;
            }
        });

        // create searchview
        var options = {
            $buttons: $("<div>"),
            action: this.action,
            disable_groupby: true,
            search_defaults: search_defaults,
        };

        var dataset = new data.DataSetSearch(this, 'account.analytic.line');
        this.searchview = new SearchView(this, dataset, this.fields_view, options);
        this.searchview.on('search', this, this._onSearch);

        var def1 = this._super.apply(this, arguments);
        var def2 = this.searchview.appendTo($("<div>")).then(function () {
            self.$searchview_buttons = self.searchview.$buttons.contents();
        });

        return $.when(def1, def2).then(function(){
            self.searchview.do_search();
            self.update_cp();
        });
    },

    //--------------------------------------------------------------------------
    // Public
    //--------------------------------------------------------------------------

    /**
     * @override
     */
    do_show: function () {
        this._super.apply(this, arguments);
        this._updateControlPanel();
        this.action_manager.do_push_state({
            action: this.action.id,
            active_id: this.action.context.active_id,
        });
    },

    //--------------------------------------------------------------------------
    // Private
    //--------------------------------------------------------------------------

    /**
     * Refresh the DOM html
     *
     * @private
     * @param {string|html} dom
     */
    _refreshPlan: function (dom) {
        this.$el.html(dom);
    },
    /**
     * Call controller to get the html content
     *
     * @private
     * @param {string[]}
     * @returns {Deferred}
     */
    _fetchPlan: function (domain) {
        var self = this;
        return this._rpc({
            route:"/timesheet/plan",
            params: {domain: domain},
        }).then(function(result){
            self._refreshPlan(result.html_content);
        });
    },
    /**
     * @private
     */
    _updateControlPanel: function () {
        this.update_control_panel({
            cp_content: {
                $buttons: this.$buttons,
                $searchview: this.searchview.$el,
                $searchview_buttons: this.$searchview_buttons,
            },
            searchview: this.searchview,
        });
    },

    //--------------------------------------------------------------------------
    // Handlers
    //--------------------------------------------------------------------------

    /**
     * Generate the action to execute based on the clicked target
     *
     * @private
     * @param {MouseEvent} event
     */
    _onClickAction: function (ev) {
        var $target = this.$(ev.currentTarget);

        var action = false;
        if($target.attr('name')){ // classical case : name="action_id" type="action"
            action = $target.attr('name');
        } else { // custom case : build custom action dict
            action = {
                'name': _t('Timesheet'),
                'type': 'ir.actions.act_window',
                'target': 'current',
                'res_model': 'account.analytic.line',
            };
            // find action views
            var views = [[false, 'pivot'], [false, 'list']];
            if($target.attr('views')){
                views = JSON.parse($target.attr('views').replace(/\'/g, '"'));
            }
            action.views = views;
            action.view_mode = _.map(views, function(view_array){return view_array[1];});
            // custom domain
            var domain = [];
            if($target.attr('domain')){
                domain = JSON.parse($target.attr('domain').replace(/\'/g, '"'));
            }
            action.domain = domain;
        }

        // additionnal context
        var context = {
            active_id: this.action.context.active_id,
            active_ids: this.action.context.active_ids,
            active_model: this.action.context.active_model,
        };

        if($target.attr('context')){
            var ctx_str = $target.attr('context').replace(/\'/g, '"');
            context = _.extend(context, JSON.parse(ctx_str));
        }

        this.do_action(action, {
            additional_context: context
        });
    },
    /**
     * @private
     * @param {MouseEvent} event
     */
    _onClickEmployeeProgressbar: function (event) {
        var domain = $(event.currentTarget).data('domain');
        this.do_action({
            name: 'Timesheets',
            type: 'ir.actions.act_window',
            res_model: 'account.analytic.line',
            views: [[false, 'list'], [false, 'form']],
            view_type: 'list',
            view_mode: 'form',
            domain: domain,
        });
    },
    /**
     * @private
     * @param {MouseEvent} event
     */
    _onClickStatButton: function (event) {
        var self = this;
        var data = $(event.currentTarget).data();
        return this._rpc({
            route:"/timesheet/plan/action",
            params: {
                domain: data.domain,
                res_model: data.resModel,
            },
        }).then(function(action){
            self.do_action(action);
        });
    },
    /**
     * @private
     * @param {MouseEvent} event
     */
    _onRedirect: function (event) {
        event.preventDefault();
        var $target = $(event.target);
        this.do_action({
            type: 'ir.actions.act_window',
            view_type: 'form',
            view_mode: 'form',
            res_model: $target.data('oe-model'),
            views: [[false, 'form']],
            res_id: $target.data('oe-id'),
        });
    },
    /**
     * This client action has its own search view and already listen on it. If
     * we let the event pass through, it will be caught by the action manager
     * which will do its work.  This may crash the web client, if the action
     * manager tries to notify the previous action.
     *
     * @private
     * @param {OdooEvent} event
     */
    _onSearch: function (event) {
        event.stopPropagation();
        var session = this.getSession();
        // group by are disabled, so we don't take care of them
        var result = pyeval.eval_domains_and_contexts({
            domains: event.data.domains,
            contexts: [session.user_context].concat(event.data.contexts)
        });

        this._fetchPlan(result.domain);
    },
});

core.action_registry.add('timesheet.plan', ProjectPlan);

return ProjectPlan;
});
<commit_msg>[FIX] sale_timesheet: fix a typo<commit_after>odoo.define('sale_timesheet.ProjectPlan', function (require) {
'use strict';

var ControlPanelMixin = require('web.ControlPanelMixin');
var core = require('web.core');
var data = require('web.data');
var pyeval = require('web.pyeval');
var SearchView = require('web.SearchView');
var Widget = require('web.Widget');

var _t = core._t;

var ProjectPlan = Widget.extend(ControlPanelMixin, {
    events: {
        "click a[type='action']": "_onClickAction",
        "click .o_timesheet_plan_redirect": '_onRedirect',
        "click .oe_stat_button": "_onClickStatButton",
        "click .o_timesheet_plan_sale_timesheet_people_time .progress-bar": '_onClickEmployeeProgressbar',
    },
    /**
     * @override
     */
     init: function (parent, action) {
        this._super.apply(this, arguments);
        this.action = action;
        this.action_manager = parent;
        this.set('title', action.name || _t('Overview'));
    },
    /**
     * @override
     */
    willStart: function () {
        var self = this;
        var view_id = this.action && this.action.search_view_id && this.action.search_view_id[0];
        var def = this
            .loadViews('account.analytic.line', this.action.context || {}, [[view_id, 'search']])
            .then(function (result) {
                self.fields_view = result.search;
            });
        return $.when(this._super(), def);
    },
    /**
     * @override
     */
    start: function () {
        var self = this;

        // find default search from context
        var search_defaults = {};
        var context = this.action.context || [];
        _.each(context, function (value, key) {
            var match = /^search_default_(.*)$/.exec(key);
            if (match) {
                search_defaults[match[1]] = value;
            }
        });

        // create searchview
        var options = {
            $buttons: $("<div>"),
            action: this.action,
            disable_groupby: true,
            search_defaults: search_defaults,
        };

        var dataset = new data.DataSetSearch(this, 'account.analytic.line');
        this.searchview = new SearchView(this, dataset, this.fields_view, options);
        this.searchview.on('search', this, this._onSearch);

        var def1 = this._super.apply(this, arguments);
        var def2 = this.searchview.appendTo($("<div>")).then(function () {
            self.$searchview_buttons = self.searchview.$buttons.contents();
        });

        return $.when(def1, def2).then(function(){
            self.searchview.do_search();
            self._updateControlPanel();
        });
    },

    //--------------------------------------------------------------------------
    // Public
    //--------------------------------------------------------------------------

    /**
     * @override
     */
    do_show: function () {
        this._super.apply(this, arguments);
        this._updateControlPanel();
        this.action_manager.do_push_state({
            action: this.action.id,
            active_id: this.action.context.active_id,
        });
    },

    //--------------------------------------------------------------------------
    // Private
    //--------------------------------------------------------------------------

    /**
     * Refresh the DOM html
     *
     * @private
     * @param {string|html} dom
     */
    _refreshPlan: function (dom) {
        this.$el.html(dom);
    },
    /**
     * Call controller to get the html content
     *
     * @private
     * @param {string[]}
     * @returns {Deferred}
     */
    _fetchPlan: function (domain) {
        var self = this;
        return this._rpc({
            route:"/timesheet/plan",
            params: {domain: domain},
        }).then(function(result){
            self._refreshPlan(result.html_content);
        });
    },
    /**
     * @private
     */
    _updateControlPanel: function () {
        this.update_control_panel({
            cp_content: {
                $buttons: this.$buttons,
                $searchview: this.searchview.$el,
                $searchview_buttons: this.$searchview_buttons,
            },
            searchview: this.searchview,
        });
    },

    //--------------------------------------------------------------------------
    // Handlers
    //--------------------------------------------------------------------------

    /**
     * Generate the action to execute based on the clicked target
     *
     * @private
     * @param {MouseEvent} event
     */
    _onClickAction: function (ev) {
        var $target = this.$(ev.currentTarget);

        var action = false;
        if($target.attr('name')){ // classical case : name="action_id" type="action"
            action = $target.attr('name');
        } else { // custom case : build custom action dict
            action = {
                'name': _t('Timesheet'),
                'type': 'ir.actions.act_window',
                'target': 'current',
                'res_model': 'account.analytic.line',
            };
            // find action views
            var views = [[false, 'pivot'], [false, 'list']];
            if($target.attr('views')){
                views = JSON.parse($target.attr('views').replace(/\'/g, '"'));
            }
            action.views = views;
            action.view_mode = _.map(views, function(view_array){return view_array[1];});
            // custom domain
            var domain = [];
            if($target.attr('domain')){
                domain = JSON.parse($target.attr('domain').replace(/\'/g, '"'));
            }
            action.domain = domain;
        }

        // additionnal context
        var context = {
            active_id: this.action.context.active_id,
            active_ids: this.action.context.active_ids,
            active_model: this.action.context.active_model,
        };

        if($target.attr('context')){
            var ctx_str = $target.attr('context').replace(/\'/g, '"');
            context = _.extend(context, JSON.parse(ctx_str));
        }

        this.do_action(action, {
            additional_context: context
        });
    },
    /**
     * @private
     * @param {MouseEvent} event
     */
    _onClickEmployeeProgressbar: function (event) {
        var domain = $(event.currentTarget).data('domain');
        this.do_action({
            name: 'Timesheets',
            type: 'ir.actions.act_window',
            res_model: 'account.analytic.line',
            views: [[false, 'list'], [false, 'form']],
            view_type: 'list',
            view_mode: 'form',
            domain: domain,
        });
    },
    /**
     * @private
     * @param {MouseEvent} event
     */
    _onClickStatButton: function (event) {
        var self = this;
        var data = $(event.currentTarget).data();
        return this._rpc({
            route:"/timesheet/plan/action",
            params: {
                domain: data.domain,
                res_model: data.resModel,
            },
        }).then(function(action){
            self.do_action(action);
        });
    },
    /**
     * @private
     * @param {MouseEvent} event
     */
    _onRedirect: function (event) {
        event.preventDefault();
        var $target = $(event.target);
        this.do_action({
            type: 'ir.actions.act_window',
            view_type: 'form',
            view_mode: 'form',
            res_model: $target.data('oe-model'),
            views: [[false, 'form']],
            res_id: $target.data('oe-id'),
        });
    },
    /**
     * This client action has its own search view and already listen on it. If
     * we let the event pass through, it will be caught by the action manager
     * which will do its work.  This may crash the web client, if the action
     * manager tries to notify the previous action.
     *
     * @private
     * @param {OdooEvent} event
     */
    _onSearch: function (event) {
        event.stopPropagation();
        var session = this.getSession();
        // group by are disabled, so we don't take care of them
        var result = pyeval.eval_domains_and_contexts({
            domains: event.data.domains,
            contexts: [session.user_context].concat(event.data.contexts)
        });

        this._fetchPlan(result.domain);
    },
});

core.action_registry.add('timesheet.plan', ProjectPlan);

return ProjectPlan;
});
<|endoftext|>


<commit_before>odoo.define('web_editor.field.html', function (require) {
'use strict';

var ajax = require('web.ajax');
var basic_fields = require('web.basic_fields');
var config = require('web.config');
var core = require('web.core');
var Wysiwyg = require('web_editor.wysiwyg.root');
var field_registry = require('web.field_registry');
// must wait for web/ to add the default html widget, otherwise it would override the web_editor one
require('web._field_registry');

var _lt = core._lt;
var TranslatableFieldMixin = basic_fields.TranslatableFieldMixin;
var QWeb = core.qweb;
var assetsLoaded;

var jinjaRegex = /(^|\n)\s*%\s(end|set\s)/;

/**
 * FieldHtml Widget
 * Intended to display HTML content. This widget uses the wysiwyg editor
 * improved by odoo.
 *
 * nodeOptions:
 *  - style-inline => convert class to inline style (no re-edition) => for sending by email
 *  - no-attachment
 *  - cssEdit
 *  - cssReadonly
 *  - snippets
 *  - wrapper
 */
var FieldHtml = basic_fields.DebouncedField.extend(TranslatableFieldMixin, {
    description: _lt("Html"),
    className: 'oe_form_field oe_form_field_html',
    supportedFieldTypes: ['html'],

    custom_events: {
        wysiwyg_focus: '_onWysiwygFocus',
        wysiwyg_blur: '_onWysiwygBlur',
        wysiwyg_change: '_onChange',
        wysiwyg_attachment: '_onAttachmentChange',
    },

    /**
     * @override
     */
    willStart: function () {
        var self = this;
        this.isRendered = false;
        this._onUpdateIframeId = 'onLoad_' + _.uniqueId('FieldHtml');
        var defAsset;
        if (this.nodeOptions.cssReadonly) {
            defAsset = ajax.loadAsset(this.nodeOptions.cssReadonly);
        }

        if (!assetsLoaded) { // avoid flickering when begin to edit
            assetsLoaded = new Promise(function (resolve) {
                var wysiwyg = new Wysiwyg(self, {});
                wysiwyg.attachTo($('<textarea>')).then(function () {
                    wysiwyg.destroy();
                    resolve();
                });
            });
        }

        return Promise.all([this._super(), assetsLoaded, defAsset]);
    },
    /**
     * @override
     */
    destroy: function () {
        delete window.top[this._onUpdateIframeId];
        if (this.$iframe) {
            this.$iframe.remove();
        }
        this._super();
    },

    //--------------------------------------------------------------------------
    // Public
    //--------------------------------------------------------------------------

    /**
     * @override
     */
    activate: function (options) {
        if (this.wysiwyg) {
            this.wysiwyg.focus();
            return true;
        }
    },
    /**
     * Wysiwyg doesn't notify for changes done in code mode. We override
     * commitChanges to manually switch back to normal mode before committing
     * changes, so that the widget is aware of the changes done in code mode.
     *
     * @override
     */
    commitChanges: function () {
        var self = this;
        if (config.isDebug() && this.mode === 'edit') {
            var layoutInfo = $.summernote.core.dom.makeLayoutInfo(this.wysiwyg.$editor);
            $.summernote.pluginEvents.codeview(undefined, undefined, layoutInfo, false);
        }
        if (this.mode == "readonly" || !this.isRendered) {
            return this._super();
        }
        var _super = this._super.bind(this);
        return this.wysiwyg.save().then(function (result) {
            self._isDirty = result.isDirty;
            _super();
        });
    },
    /**
     * @override
     */
    isSet: function () {
        return this.value && this.value !== "

" && this.value.match(/\S/);
    },
    /**
     * @override
     */
    getFocusableElement: function () {
        return this.$target || $();
    },
    /**
     * Do not re-render this field if it was the origin of the onchange call.
     *
     * @override
     */
    reset: function (record, event) {
        this._reset(record, event);
        var value = this.value;
        if (this.nodeOptions.wrapper) {
            value = this._wrap(value);
        }
        value = this._textToHtml(value);
        if (!event || event.target !== this) {
            if (this.mode === 'edit') {
                this.wysiwyg.setValue(value);
            } else {
                this.$content.html(value);
            }
        }
        return Promise.resolve();
    },

    //--------------------------------------------------------------------------
    // Private
    //--------------------------------------------------------------------------

    /**
     * @override
     */
    _getValue: function () {
        var value = this.$target.val();
        if (this.nodeOptions.wrapper) {
            return this._unWrap(value);
        }
        return value;
    },
    /**
     * Create the wysiwyg instance with the target (this.$target)
     * then add the editable content (this.$content).
     *
     * @private
     * @returns {$.Promise}
     */
    _createWysiwygIntance: function () {
        var self = this;
        this.wysiwyg = new Wysiwyg(this, this._getWysiwygOptions());

        // by default this is synchronous because the assets are already loaded in willStart
        // but it can be async in the case of options such as iframe, snippets...
        return this.wysiwyg.attachTo(this.$target).then(function () {
            self.$content = self.wysiwyg.$editor.closest('body, odoo-wysiwyg-container');
            self._onLoadWysiwyg();
            self.isRendered = true;
        });
    },
    /**
     * Get wysiwyg options to create wysiwyg instance.
     *
     * @private
     * @returns {Object}
     */
    _getWysiwygOptions: function () {
        return Object.assign({}, this.nodeOptions, {
            recordInfo: {
                context: this.record.getContext(this.recordParams),
                res_model: this.model,
                res_id: this.res_id,
            },
            noAttachment: this.nodeOptions['no-attachment'],
            inIframe: !!this.nodeOptions.cssEdit,
            iframeCssAssets: this.nodeOptions.cssEdit,
            snippets: this.nodeOptions.snippets,

            tabsize: 0,
            height: 180,
            generateOptions: function (options) {
                var para = _.find(options.toolbar, function (item) {
                    return item[0] === 'para';
                });
                if (para && para[1] && para[1].indexOf('checklist') === -1) {
                    para[1].splice(2, 0, 'checklist');
                }
                if (config.isDebug()) {
                    options.codeview = true;
                    var view = _.find(options.toolbar, function (item) {
                        return item[0] === 'view';
                    });
                    if (view) {
                        if (!view[1].includes('codeview')) {
                            view[1].splice(-1, 0, 'codeview');
                        }
                    } else {
                        options.toolbar.splice(-1, 0, ['view', ['codeview']]);
                    }
                }
                options.prettifyHtml = false;
                return options;
            },
        });
    },
    /**
     * trigger_up 'field_changed' add record into the "ir.attachment" field found in the view.
     * This method is called when an image is uploaded via the media dialog.
     *
     * For e.g. when sending email, this allows people to add attachments with the content
     * editor interface and that they appear in the attachment list.
     * The new documents being attached to the email, they will not be erased by the CRON
     * when closing the wizard.
     *
     * @private
     * @param {Object} attachments
     */
    _onAttachmentChange: function (attachments) {
        if (!this.fieldNameAttachment) {
            return;
        }
        this.trigger_up('field_changed', {
            dataPointID: this.dataPointID,
            changes: _.object([this.fieldNameAttachment], [{
                operation: 'ADD_M2M',
                ids: attachments
            }])
        });
    },
    /**
     * @override
     */
    _renderEdit: function () {
        var value = this._textToHtml(this.value);
        if (this.nodeOptions.wrapper) {
            value = this._wrap(value);
        }
        this.$target = $('<textarea>').val(value).hide();
        this.$target.appendTo(this.$el);

        var fieldNameAttachment = _.chain(this.recordData)
            .pairs()
            .find(function (value) {
                return _.isObject(value[1]) && value[1].model === "ir.attachment";
            })
            .first()
            .value();
        if (fieldNameAttachment) {
            this.fieldNameAttachment = fieldNameAttachment;
        }

        if (this.nodeOptions.cssEdit) {
            // must be async because the target must be append in the DOM
            this._createWysiwygIntance();
        } else {
            return this._createWysiwygIntance();
        }
    },
    /**
     * @override
     */
    _renderReadonly: function () {
        var self = this;
        var value = this._textToHtml(this.value);
        if (this.nodeOptions.wrapper) {
            value = this._wrap(value);
        }

        this.$el.empty();
        var resolver;
        var def = new Promise(function (resolve) {
            resolver = resolve;
        });
        if (this.nodeOptions.cssReadonly) {
            this.$iframe = $('');
            this.$iframe.appendTo(this.$el);

            var avoidDoubleLoad = 0; // this bug only appears on some computers with some chrome version.

            // inject content in iframe

            this.$iframe.data('loadDef', def); // for unit test
            window.top[this._onUpdateIframeId] = function (_avoidDoubleLoad) {
                if (_avoidDoubleLoad !== avoidDoubleLoad) {
                    console.warn('Wysiwyg iframe double load detected');
                    return;
                }
                self.$content = $('#iframe_target', self.$iframe[0].contentWindow.document.body);
                resolver();
            };

            this.$iframe.on('load', function onLoad() {
                var _avoidDoubleLoad = ++avoidDoubleLoad;
                ajax.loadAsset(self.nodeOptions.cssReadonly).then(function (asset) {
                    if (_avoidDoubleLoad !== avoidDoubleLoad) {
                        console.warn('Wysiwyg immediate iframe double load detected');
                        return;
                    }
                    var cwindow = self.$iframe[0].contentWindow;
                    cwindow.document
                        .open("text/html", "replace")
                        .write(
                            '' +
                                '<meta charset="utf-8"/>' +
                                '<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>\n' +
                                '<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"/>\n' +
                                _.map(asset.cssLibs, function (cssLib) {
                                    return '<link type="text/css" rel="stylesheet" href="' + cssLib + '"/>';
                                }).join('\n') + '\n' +
                                _.map(asset.cssContents, function (cssContent) {
                                    return '';
                                }).join('\n') + '\n' +
                            '\n' +
                            '\n' +
                                '
' + value + '
\n' +
                                '\n' +
                            '');

                    var height = cwindow.document.body.scrollHeight;
                    self.$iframe.css('height', Math.max(30, Math.min(height, 500)) + 'px');
                });
            });
        } else {
            this.$content = $('
').html(value);
            this.$content.appendTo(this.$el);
            resolver();
        }

        def.then(function () {
            self.$content.on('click', 'ul.o_checklist > li', self._onReadonlyClickChecklist.bind(self));
        });
    },
    /**
     * @private
     * @param {string} text
     * @returns {string} the text converted to html
     */
    _textToHtml: function (text) {
        var value = text || "";
        if (jinjaRegex.test(value)) { // is jinja
            return value;
        }
        try {
            $(text)[0].innerHTML; // crashes if text isn't html
        } catch (e) {
            if (value.match(/^\s*$/)) {
                value = '

';
            } else {
                value = "
" + value.split(/
/).join("

") + "
";
                value = value
                            .replace(/
<\/p>/g, '')
                            .replace('

', '
')
                            .replace('
<p ', '<p ')
                            .replace('

', '
');
            }
        }
        return value;
    },
    /**
     * Move HTML contents out of their wrapper.
     *
     * @private
     * @param {string} html content
     * @returns {string} html content
     */
    _unWrap: function (html) {
        var $wrapper = $(html).find('#wrapper');
        return $wrapper.length ? $wrapper.html() : html;
    },
    /**
     * Wrap HTML in order to create a custom display.
     *
     * The wrapper (this.nodeOptions.wrapper) must be a static
     * XML template with content id="wrapper".
     *
     * @private
     * @param {string} html content
     * @returns {string} html content
     */
    _wrap: function (html) {
        return $(QWeb.render(this.nodeOptions.wrapper))
            .find('#wrapper').html(html)
            .end().prop('outerHTML');
    },

    //--------------------------------------------------------------------------
    // Handler
    //--------------------------------------------------------------------------

    /**
     * Method called when wysiwyg triggers a change.
     *
     * @private
     * @param {OdooEvent} ev
     */
    _onChange: function (ev) {
        this._doDebouncedAction.apply(this, arguments);

        var $lis = this.$content.find('.note-editable ul.o_checklist > li:not(:has(> ul.o_checklist))');
        if (!$lis.length) {
            return;
        }
        var max = 0;
        var ids = [];
        $lis.map(function () {
            var checklistId = parseInt(($(this).attr('id') || '0').replace(/^checklist-id-/, ''));
            if (ids.indexOf(checklistId) === -1) {
                if (checklistId > max) {
                    max = checklistId;
                }
                ids.push(checklistId);
            } else {
                $(this).removeAttr('id');
            }
        });
        $lis.not('[id]').each(function () {
            $(this).attr('id', 'checklist-id-' + (++max));
        });
    },
    /**
     * Method called when wysiwyg triggers a change.
     *
     * @private
     * @param {OdooEvent} ev
     */
    _onReadonlyClickChecklist: function (ev) {
        var self = this;
        if (ev.offsetX > 0) {
            return;
        }
        ev.stopPropagation();
        ev.preventDefault();
        var checked = $(ev.target).hasClass('o_checked');
        var checklistId = parseInt(($(ev.target).attr('id') || '0').replace(/^checklist-id-/, ''));

        this._rpc({
            route: '/web_editor/checklist',
            params: {
                res_model: this.model,
                res_id: this.res_id,
                filename: this.name,
                checklistId: checklistId,
                checked: !checked,
            },
        }).then(function (value) {
            self._setValue(value);
        });
    },
    /**
     * Method called when the wysiwyg instance is loaded.
     *
     * @private
     */
    _onLoadWysiwyg: function () {
        var $button = this._renderTranslateButton();
        $button.css({
            'font-size': '15px',
            position: 'absolute',
            right: '+5px',
            top: '+5px',
        });
        this.$el.append($button);
        // force source editor if it contains multiline Jinja directives
        if (jinjaRegex.test(this._getValue())) {
            var layoutInfo = $.summernote.core.dom.makeLayoutInfo(this.wysiwyg.$editor);
            layoutInfo.toolbar().hide();
            $.summernote.pluginEvents.codeview(undefined, undefined, layoutInfo, true);
        }
    },
    /**
     * @private
     * @param {OdooEvent} ev
     */
    _onWysiwygBlur: function (ev) {
        ev.stopPropagation();
        this._doAction();
        if (ev.data.key === 'TAB') {
            this.trigger_up('navigation_move', {
                direction: ev.data.shiftKey ? 'left' : 'right',
            });
        }
    },
    /**
     * @private
     * @param {OdooEvent} ev
     */
    _onWysiwygFocus: function (ev) {},
});

field_registry.add('html', FieldHtml);

return FieldHtml;
});
<commit_msg>[FIX] web_editor: remove forced source mode<commit_after>odoo.define('web_editor.field.html', function (require) {
'use strict';

var ajax = require('web.ajax');
var basic_fields = require('web.basic_fields');
var config = require('web.config');
var core = require('web.core');
var Wysiwyg = require('web_editor.wysiwyg.root');
var field_registry = require('web.field_registry');
// must wait for web/ to add the default html widget, otherwise it would override the web_editor one
require('web._field_registry');

var _lt = core._lt;
var TranslatableFieldMixin = basic_fields.TranslatableFieldMixin;
var QWeb = core.qweb;
var assetsLoaded;

var jinjaRegex = /(^|\n)\s*%\s(end|set\s)/;

/**
 * FieldHtml Widget
 * Intended to display HTML content. This widget uses the wysiwyg editor
 * improved by odoo.
 *
 * nodeOptions:
 *  - style-inline => convert class to inline style (no re-edition) => for sending by email
 *  - no-attachment
 *  - cssEdit
 *  - cssReadonly
 *  - snippets
 *  - wrapper
 */
var FieldHtml = basic_fields.DebouncedField.extend(TranslatableFieldMixin, {
    description: _lt("Html"),
    className: 'oe_form_field oe_form_field_html',
    supportedFieldTypes: ['html'],

    custom_events: {
        wysiwyg_focus: '_onWysiwygFocus',
        wysiwyg_blur: '_onWysiwygBlur',
        wysiwyg_change: '_onChange',
        wysiwyg_attachment: '_onAttachmentChange',
    },

    /**
     * @override
     */
    willStart: function () {
        var self = this;
        this.isRendered = false;
        this._onUpdateIframeId = 'onLoad_' + _.uniqueId('FieldHtml');
        var defAsset;
        if (this.nodeOptions.cssReadonly) {
            defAsset = ajax.loadAsset(this.nodeOptions.cssReadonly);
        }

        if (!assetsLoaded) { // avoid flickering when begin to edit
            assetsLoaded = new Promise(function (resolve) {
                var wysiwyg = new Wysiwyg(self, {});
                wysiwyg.attachTo($('<textarea>')).then(function () {
                    wysiwyg.destroy();
                    resolve();
                });
            });
        }

        return Promise.all([this._super(), assetsLoaded, defAsset]);
    },
    /**
     * @override
     */
    destroy: function () {
        delete window.top[this._onUpdateIframeId];
        if (this.$iframe) {
            this.$iframe.remove();
        }
        this._super();
    },

    //--------------------------------------------------------------------------
    // Public
    //--------------------------------------------------------------------------

    /**
     * @override
     */
    activate: function (options) {
        if (this.wysiwyg) {
            this.wysiwyg.focus();
            return true;
        }
    },
    /**
     * Wysiwyg doesn't notify for changes done in code mode. We override
     * commitChanges to manually switch back to normal mode before committing
     * changes, so that the widget is aware of the changes done in code mode.
     *
     * @override
     */
    commitChanges: function () {
        var self = this;
        if (config.isDebug() && this.mode === 'edit') {
            var layoutInfo = $.summernote.core.dom.makeLayoutInfo(this.wysiwyg.$editor);
            $.summernote.pluginEvents.codeview(undefined, undefined, layoutInfo, false);
        }
        if (this.mode == "readonly" || !this.isRendered) {
            return this._super();
        }
        var _super = this._super.bind(this);
        return this.wysiwyg.save().then(function (result) {
            self._isDirty = result.isDirty;
            _super();
        });
    },
    /**
     * @override
     */
    isSet: function () {
        return this.value && this.value !== "

" && this.value.match(/\S/);
    },
    /**
     * @override
     */
    getFocusableElement: function () {
        return this.$target || $();
    },
    /**
     * Do not re-render this field if it was the origin of the onchange call.
     *
     * @override
     */
    reset: function (record, event) {
        this._reset(record, event);
        var value = this.value;
        if (this.nodeOptions.wrapper) {
            value = this._wrap(value);
        }
        value = this._textToHtml(value);
        if (!event || event.target !== this) {
            if (this.mode === 'edit') {
                this.wysiwyg.setValue(value);
            } else {
                this.$content.html(value);
            }
        }
        return Promise.resolve();
    },

    //--------------------------------------------------------------------------
    // Private
    //--------------------------------------------------------------------------

    /**
     * @override
     */
    _getValue: function () {
        var value = this.$target.val();
        if (this.nodeOptions.wrapper) {
            return this._unWrap(value);
        }
        return value;
    },
    /**
     * Create the wysiwyg instance with the target (this.$target)
     * then add the editable content (this.$content).
     *
     * @private
     * @returns {$.Promise}
     */
    _createWysiwygIntance: function () {
        var self = this;
        this.wysiwyg = new Wysiwyg(this, this._getWysiwygOptions());

        // by default this is synchronous because the assets are already loaded in willStart
        // but it can be async in the case of options such as iframe, snippets...
        return this.wysiwyg.attachTo(this.$target).then(function () {
            self.$content = self.wysiwyg.$editor.closest('body, odoo-wysiwyg-container');
            self._onLoadWysiwyg();
            self.isRendered = true;
        });
    },
    /**
     * Get wysiwyg options to create wysiwyg instance.
     *
     * @private
     * @returns {Object}
     */
    _getWysiwygOptions: function () {
        return Object.assign({}, this.nodeOptions, {
            recordInfo: {
                context: this.record.getContext(this.recordParams),
                res_model: this.model,
                res_id: this.res_id,
            },
            noAttachment: this.nodeOptions['no-attachment'],
            inIframe: !!this.nodeOptions.cssEdit,
            iframeCssAssets: this.nodeOptions.cssEdit,
            snippets: this.nodeOptions.snippets,

            tabsize: 0,
            height: 180,
            generateOptions: function (options) {
                var para = _.find(options.toolbar, function (item) {
                    return item[0] === 'para';
                });
                if (para && para[1] && para[1].indexOf('checklist') === -1) {
                    para[1].splice(2, 0, 'checklist');
                }
                if (config.isDebug()) {
                    options.codeview = true;
                    var view = _.find(options.toolbar, function (item) {
                        return item[0] === 'view';
                    });
                    if (view) {
                        if (!view[1].includes('codeview')) {
                            view[1].splice(-1, 0, 'codeview');
                        }
                    } else {
                        options.toolbar.splice(-1, 0, ['view', ['codeview']]);
                    }
                }
                options.prettifyHtml = false;
                return options;
            },
        });
    },
    /**
     * trigger_up 'field_changed' add record into the "ir.attachment" field found in the view.
     * This method is called when an image is uploaded via the media dialog.
     *
     * For e.g. when sending email, this allows people to add attachments with the content
     * editor interface and that they appear in the attachment list.
     * The new documents being attached to the email, they will not be erased by the CRON
     * when closing the wizard.
     *
     * @private
     * @param {Object} attachments
     */
    _onAttachmentChange: function (attachments) {
        if (!this.fieldNameAttachment) {
            return;
        }
        this.trigger_up('field_changed', {
            dataPointID: this.dataPointID,
            changes: _.object([this.fieldNameAttachment], [{
                operation: 'ADD_M2M',
                ids: attachments
            }])
        });
    },
    /**
     * @override
     */
    _renderEdit: function () {
        var value = this._textToHtml(this.value);
        if (this.nodeOptions.wrapper) {
            value = this._wrap(value);
        }
        this.$target = $('<textarea>').val(value).hide();
        this.$target.appendTo(this.$el);

        var fieldNameAttachment = _.chain(this.recordData)
            .pairs()
            .find(function (value) {
                return _.isObject(value[1]) && value[1].model === "ir.attachment";
            })
            .first()
            .value();
        if (fieldNameAttachment) {
            this.fieldNameAttachment = fieldNameAttachment;
        }

        if (this.nodeOptions.cssEdit) {
            // must be async because the target must be append in the DOM
            this._createWysiwygIntance();
        } else {
            return this._createWysiwygIntance();
        }
    },
    /**
     * @override
     */
    _renderReadonly: function () {
        var self = this;
        var value = this._textToHtml(this.value);
        if (this.nodeOptions.wrapper) {
            value = this._wrap(value);
        }

        this.$el.empty();
        var resolver;
        var def = new Promise(function (resolve) {
            resolver = resolve;
        });
        if (this.nodeOptions.cssReadonly) {
            this.$iframe = $('');
            this.$iframe.appendTo(this.$el);

            var avoidDoubleLoad = 0; // this bug only appears on some computers with some chrome version.

            // inject content in iframe

            this.$iframe.data('loadDef', def); // for unit test
            window.top[this._onUpdateIframeId] = function (_avoidDoubleLoad) {
                if (_avoidDoubleLoad !== avoidDoubleLoad) {
                    console.warn('Wysiwyg iframe double load detected');
                    return;
                }
                self.$content = $('#iframe_target', self.$iframe[0].contentWindow.document.body);
                resolver();
            };

            this.$iframe.on('load', function onLoad() {
                var _avoidDoubleLoad = ++avoidDoubleLoad;
                ajax.loadAsset(self.nodeOptions.cssReadonly).then(function (asset) {
                    if (_avoidDoubleLoad !== avoidDoubleLoad) {
                        console.warn('Wysiwyg immediate iframe double load detected');
                        return;
                    }
                    var cwindow = self.$iframe[0].contentWindow;
                    cwindow.document
                        .open("text/html", "replace")
                        .write(
                            '' +
                                '<meta charset="utf-8"/>' +
                                '<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>\n' +
                                '<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"/>\n' +
                                _.map(asset.cssLibs, function (cssLib) {
                                    return '<link type="text/css" rel="stylesheet" href="' + cssLib + '"/>';
                                }).join('\n') + '\n' +
                                _.map(asset.cssContents, function (cssContent) {
                                    return '';
                                }).join('\n') + '\n' +
                            '\n' +
                            '\n' +
                                '
' + value + '
\n' +
                                '\n' +
                            '');

                    var height = cwindow.document.body.scrollHeight;
                    self.$iframe.css('height', Math.max(30, Math.min(height, 500)) + 'px');
                });
            });
        } else {
            this.$content = $('
').html(value);
            this.$content.appendTo(this.$el);
            resolver();
        }

        def.then(function () {
            self.$content.on('click', 'ul.o_checklist > li', self._onReadonlyClickChecklist.bind(self));
        });
    },
    /**
     * @private
     * @param {string} text
     * @returns {string} the text converted to html
     */
    _textToHtml: function (text) {
        var value = text || "";
        if (jinjaRegex.test(value)) { // is jinja
            return value;
        }
        try {
            $(text)[0].innerHTML; // crashes if text isn't html
        } catch (e) {
            if (value.match(/^\s*$/)) {
                value = '

';
            } else {
                value = "
" + value.split(/
/).join("

") + "
";
                value = value
                            .replace(/
<\/p>/g, '')
                            .replace('

', '
')
                            .replace('
<p ', '<p ')
                            .replace('

', '
');
            }
        }
        return value;
    },
    /**
     * Move HTML contents out of their wrapper.
     *
     * @private
     * @param {string} html content
     * @returns {string} html content
     */
    _unWrap: function (html) {
        var $wrapper = $(html).find('#wrapper');
        return $wrapper.length ? $wrapper.html() : html;
    },
    /**
     * Wrap HTML in order to create a custom display.
     *
     * The wrapper (this.nodeOptions.wrapper) must be a static
     * XML template with content id="wrapper".
     *
     * @private
     * @param {string} html content
     * @returns {string} html content
     */
    _wrap: function (html) {
        return $(QWeb.render(this.nodeOptions.wrapper))
            .find('#wrapper').html(html)
            .end().prop('outerHTML');
    },

    //--------------------------------------------------------------------------
    // Handler
    //--------------------------------------------------------------------------

    /**
     * Method called when wysiwyg triggers a change.
     *
     * @private
     * @param {OdooEvent} ev
     */
    _onChange: function (ev) {
        this._doDebouncedAction.apply(this, arguments);

        var $lis = this.$content.find('.note-editable ul.o_checklist > li:not(:has(> ul.o_checklist))');
        if (!$lis.length) {
            return;
        }
        var max = 0;
        var ids = [];
        $lis.map(function () {
            var checklistId = parseInt(($(this).attr('id') || '0').replace(/^checklist-id-/, ''));
            if (ids.indexOf(checklistId) === -1) {
                if (checklistId > max) {
                    max = checklistId;
                }
                ids.push(checklistId);
            } else {
                $(this).removeAttr('id');
            }
        });
        $lis.not('[id]').each(function () {
            $(this).attr('id', 'checklist-id-' + (++max));
        });
    },
    /**
     * Method called when wysiwyg triggers a change.
     *
     * @private
     * @param {OdooEvent} ev
     */
    _onReadonlyClickChecklist: function (ev) {
        var self = this;
        if (ev.offsetX > 0) {
            return;
        }
        ev.stopPropagation();
        ev.preventDefault();
        var checked = $(ev.target).hasClass('o_checked');
        var checklistId = parseInt(($(ev.target).attr('id') || '0').replace(/^checklist-id-/, ''));

        this._rpc({
            route: '/web_editor/checklist',
            params: {
                res_model: this.model,
                res_id: this.res_id,
                filename: this.name,
                checklistId: checklistId,
                checked: !checked,
            },
        }).then(function (value) {
            self._setValue(value);
        });
    },
    /**
     * Method called when the wysiwyg instance is loaded.
     *
     * @private
     */
    _onLoadWysiwyg: function () {
        var $button = this._renderTranslateButton();
        $button.css({
            'font-size': '15px',
            position: 'absolute',
            right: '+5px',
            top: '+5px',
        });
        this.$el.append($button);
    },
    /**
     * @private
     * @param {OdooEvent} ev
     */
    _onWysiwygBlur: function (ev) {
        ev.stopPropagation();
        this._doAction();
        if (ev.data.key === 'TAB') {
            this.trigger_up('navigation_move', {
                direction: ev.data.shiftKey ? 'left' : 'right',
            });
        }
    },
    /**
     * @private
     * @param {OdooEvent} ev
     */
    _onWysiwygFocus: function (ev) {},
});

field_registry.add('html', FieldHtml);

return FieldHtml;
});
<|endoftext|>


<commit_before>/*
 * CODENVY CONFIDENTIAL
 * __________________
 *
 *  [2012] - [2014] Codenvy, S.A.
 *  All Rights Reserved.
 *
 * NOTICE:  All information contained herein is, and remains
 * the property of Codenvy S.A. and its suppliers,
 * if any.  The intellectual and technical concepts contained
 * herein are proprietary to Codenvy S.A.
 * and its suppliers and may be covered by U.S. and Foreign Patents,
 * patents in process, and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material
 * is strictly forbidden unless prior written permission is obtained
 * from Codenvy S.A..
 */
jQuery.fn.doesExist = function(){
   return jQuery(this).length > 0;
};

if (typeof analytics === "undefined") {
    analytics = {};
}

analytics.main = new Main();

function Main() {
    jQuery(document).ready(function () {
        setupButtons();
        loadAllWidgets();
    });

    var setupButtons = function() {
        // Time selectors group
        $("#timely-dd button.command-btn").click(function () {
            $("#timely-dd button").removeClass('btn-primary');
            $(this).addClass('btn-primary');
            reloadWidgets($("#timely-dd").attr("targetWidgets"));
        });

        // "Filter by" group
        $("#filter-by button.command-btn").click(function() {
           $("#filter-by button").removeClass('btn-primary');
           if ($("#filter-by input[name='keyword']").val() != "") {  // select button only if there is some text in keyword input
              $(this).addClass('btn-primary');
              reloadWidgets($("#filter-by").attr("targetWidgets"));
           }
        });
        $("#filter-by button.clear-btn").click(function() {    // clearing
            $("#filter-by button").removeClass('btn-primary');
            $("#filter-by input[name='keyword']").val("");

            reloadWidgets($("#filter-by").attr("targetWidgets"));
        });

        // "Date range" group
        $("#date-range button.command-btn").click(function() {
           $("#date-range button").removeClass('btn-primary');
           if ($("#date-range input[name='from_date']").val() != ""
                 || $("#date-range input[name='to_date']").val() != "") {  // select button only if there is date in one of date range input
              $(this).addClass('btn-primary');
              reloadWidgets($("#date-range").attr("targetWidgets"));
           }
        });
        $("#date-range button.clear-btn").click(function() {    // clearing
            $("#date-range button").removeClass('btn-primary');
            $("#date-range input[name='from_date']").val("");
            $("#date-range input[name='to_date']").val("");

            reloadWidgets($("#date-range").attr("targetWidgets"));
        });

        // Metric selectors group
        $("#metric button.command-btn").click(function () {
            $("#metric button").removeClass('btn-primary');
            $(this).addClass('btn-primary');

            reloadWidgets($("#metric").attr("targetWidgets"));
        });

        // Ide version selectors group
        $("#ide-version a.command-btn").click(function () {
            $("#ide-version a").removeClass('btn-primary');
            $(this).addClass('btn-primary');

            reloadWidgets($("#ide-version").attr("targetWidgets"));
        });
    };

    /**
     * Get params from buttons state
     */
    function getParamsFromButtons() {
        var params = {};

        // process time selector
        var selectedTimeButton = $("#timely-dd button.btn-primary");
        if (selectedTimeButton.doesExist()) {
           params.time_unit = selectedTimeButton.val();
        }

        // process filter
        var filterInput = $("#filter-by input[name='keyword']");
        var selectedFilterButton = $("#filter-by button.btn-primary")
        if (selectedFilterButton.doesExist()
              && filterInput.val().length > 0) {
           params[selectedFilterButton.val()] = filterInput.val();
        }

        // process date-range
        var fromDateInput = $("#date-range input[name='from_date']");
        if (fromDateInput.doesExist()
              && fromDateInput.val() != "") {
           params["from_date"] = fromDateInput.val();
        }
        var toDateInput = $("#date-range input[name='to_date']");
        if (toDateInput.doesExist()
              && toDateInput.val() != "") {
           params["to_date"] = toDateInput.val();
        }

        // process metric selector
        var selectedMetricButton = $("#metric button.btn-primary");
        if (selectedMetricButton.doesExist()) {
           params.metric = selectedMetricButton.val();
        }

        // process ide version selector
        var ideVersionButton = $("#ide-version a.btn-primary");
        if (ideVersionButton.doesExist()) {
           if (typeof ideVersionButton.attr("value") != "undefined") {
               params.ide = ideVersionButton.attr("value");
           }
           updateGlobalParamInStorage("ide", params["ide"]);  // params["ide"] is undefined if ideVersionButton.attr("value") is undefined
        }

        return params;
    };

    /**
     * Reload div on clicking on page navigation links at the bottom of the tables
     * @param pageNavigationLinkElement dom-element
     * @param widgetName id of div to reload
     */
    function reloadWidgetOnPageNavigation(pageNavigationLinkElement, widgetName) {
        var jQueryPageLinkElement = jQuery(pageNavigationLinkElement);
        var href = jQueryPageLinkElement.attr("href");

        var urlParams = analytics.util.extractUrlParams(href);

        reloadWidget(widgetName, urlParams);
    }

    /**
     * Reload widgets
     * @param widgetNames: String with widget names divided by ","
     */
    function reloadWidgets(widgetNames) {
        if (typeof widgetNames != "undefined") {
           var widgetName = widgetNames.split(',');

           var urlParams = analytics.util.extractUrlParams(window.location.href);
           var buttonParams = getParamsFromButtons();

           // union url params with button params and choose button params values above url params values
           var params = analytics.util.unionWithRewrite(urlParams, buttonParams);

           if (widgetName == "_all") {
               loadAllWidgets(params);
           } else {
               for (var i in widgetName) {
                   reloadWidget(widgetName[i], params);
               }
           }
        }
    }

    function reloadWidget(widgetName, params) {
        loadWidget(widgetName, params, function(data) {
          // rewrite page location to make it possible to navigate new url through the browser's history
          if (analytics.configuration.getProperty(widgetName, "isNeedToSaveInHistory")) {
             var pageUrl = window.location.href;
             if (pageUrl.indexOf('?')) {
                var absUrl = pageUrl .split('?');
                pageUrl = absUrl[0];
             }

             var newPageUrl = pageUrl;
             var urlParams = analytics.util.constructUrlParams(params);
             if (urlParams != null) {
                newPageUrl += "?" + urlParams;
             }

             window.history.pushState({"widgetName": widgetName}, document.title, newPageUrl);
          }
       });

    }

    function loadAllWidgets(params) {
       // load all widgets at first time of loading the page
       if (typeof params == "undefined") {
           params = analytics.util.extractUrlParams(window.location.href);
           updateGlobalParamsWithValuesFromStorage(params);
           updateCommandButtonsState(params);
       }

       var widgetNames = analytics.configuration.getWidgetNames();
       for(var i in widgetNames) {
           var widgetName = widgetNames[i];
           if (jQuery("#" + widgetName).doesExist()) {
               if (analytics.configuration.getProperty(widgetName, "isNeedToSaveInHistory")) {
                   // update div when navigating in history
                   var everPushedSomething = false;
                   var initialUrl = window.location.href;
                   window.addEventListener('popstate', function(event) {
                      if (! everPushedSomething
                             && window.location.href == initialUrl) {
                         everPushedSomething = true;
                         return;
                      }

                      if (event.state != null
                              && typeof event.state.widgetName != "undefined") {
                          // update parameter buttons selection
                          var params = analytics.util.extractUrlParams(window.location.href);
                          updateCommandButtonsState(params);

                          loadWidget(event.state.widgetName, params);
                      }
                   });
               }

               loadWidget(widgetName, params);
           }
       }
    }

    function loadWidget(widgetName, params, callback) {
        var params = params || {};
        var callback = callback || function(){};

        var view = analytics.factory.getView(widgetName);
        view.setParams(params);

        // display loader after the 2 seconds of timeout
        var needLoader = true;
        var timeoutInMillisec = 2000;
        setTimeout(function() {
           if (needLoader) {
               analytics.views.loader.show();
           }
        }, timeoutInMillisec);

        var model = analytics.factory.getModel(widgetName);
        model.clearDoneFunction();
        model.pushDoneFunction(function() {
            needLoader = false;
            view.show();
            analytics.views.loader.hide();
            callback();
        })

        model.clearFailFunction();
        model.pushFailFunction(function(status, textStatus, errorThrown) {
            if (textStatus == "abort") {
                needLoader = false;
                analytics.views.loader.hide();

                view.showAbortMessage();
            } else {
                needLoader = false;
                analytics.views.loader.hide();

                view.showErrorMessage(status, textStatus, errorThrown);
            }
        });

        var presenter = analytics.factory.getPresenter(widgetName, view, model);

        view.clear();
        presenter.load();
    }

    function updateCommandButtonsState(params) {
       var params = params || {};

       // update time selection buttons
       var timeUnitButtons = jQuery("#timely-dd button");
       if (timeUnitButtons.doesExist()) {
          setPrimaryButtonOnValue(timeUnitButtons, params["time_unit"]);
       }

       // update filter-by group
       var filterButtons = jQuery("#filter-by button");
       var filterInput = $("#filter-by input[name='keyword']");
       if (filterButtons.doesExist() && filterInput.doesExist()) {
          jQuery("#filter-by button").removeClass('btn-primary');
          filterInput.val("");

          // find out "filter by" param like "Email: test@test.com" which is linked with button with text "Email"
          for (var i = 0; i < filterButtons.length; i++) {
             var button = jQuery(filterButtons[i]);
             var filterParamValue = params[button.attr("value")];
             if (typeof filterParamValue != "undefined") {
                button.addClass('btn-primary');
                filterInput.val(filterParamValue);   // set keyword input = value from param
                break;
             }
          }
       }

       // update date-range group
       var isDateParameterPresenceInQuery = false;
       {  var input = $("#date-range input[name='from_date']");
          var queryParam = params["from_date"];
          if (input.doesExist()) {
             if (typeof queryParam != "undefined") {
                input.val(queryParam);
                isDateParameterPresenceInQuery = true;
             } else {
                input.val("");
             }
          }
       }
       {  var input = $("#date-range input[name='to_date']");
          var queryParam = params["to_date"];
          if (input.doesExist()) {
             if (typeof queryParam != "undefined") {
                input.val(queryParam);
                isDateParameterPresenceInQuery = true;
             } else {
                input.val("");
             }
          }
       }
       jQuery("#date-range button").removeClass('btn-primary');
       var dateRangeButton = jQuery("#date-range button:contains('Filter')");
       if (dateRangeButton.doesExist() && isDateParameterPresenceInQuery) {
          jQuery("#date-range button").removeClass('btn-primary');
          dateRangeButton.addClass('btn-primary');
       }

       // update metric selection buttons
       var metricButtons = jQuery("#metric button");
       if (metricButtons.doesExist()) {
           setPrimaryButtonOnValue(metricButtons, params["metric"]);
       }

       // update ide version selection buttons
       var ideVersionButtons = jQuery("#ide-version a");
       if (ideVersionButtons.doesExist()) {
           setPrimaryButtonOnValue(ideVersionButtons, params["ide"]);
       }
    }

    /**
     * Update undefined global params with values from HTML5 Web Storage
     */
    function updateGlobalParamsWithValuesFromStorage(params) {
        if (!analytics.util.isBrowserSupportWebStorage()) {
            return;
        }

        var globalParamList = analytics.configuration.getGlobalParamList();
        for (var i in globalParamList) {
            var globalParamName = globalParamList[i];
            var storedParam = localStorage.getItem(globalParamName);  // get param value from HTML5 Web Storage
            if (typeof params[globalParamName] == "undefined"
                    && storedParam != null) {
                params[globalParamName] = storedParam;
            }
        }
    }

    /**
     * Save global param value in the HTML5 Web Storage
     */
    function updateGlobalParamInStorage(parameter, value) {
        if (!analytics.util.isBrowserSupportWebStorage()) {
            return;
        }

        if (analytics.configuration.isParamGlobal(parameter)) {
            if (typeof value != "undefined") {
                localStorage.setItem(parameter, value);    // save param value in the HTML5 Web Storage
            } else {
                localStorage.removeItem(parameter);    // remove param from HTML5 Web Storage
            }
        }
    }

    function setPrimaryButtonOnValue(buttonGroup, parameter) {
        buttonGroup.removeClass('btn-primary');
        if (typeof parameter != "undefined") {
            // set as primary the button with value = parameter
            for (var i = 0; i < buttonGroup.size(); i++) {
               var button = jQuery(buttonGroup[i]);
               if (button.attr("value") == parameter) {
                  button.addClass("btn-primary");
                  break;
               }
            }
        } else {
           // restore default primary button
           for (var i = 0; i < buttonGroup.size(); i++) {
               var button = jQuery(buttonGroup[i]);
               if (typeof button.attr("default") != "undefined") {
                  button.addClass("btn-primary");
                  break;
               }
           }
        }
     }

    /** ****************** library API ********** */
    return {
        reloadWidgetOnPageNavigation: reloadWidgetOnPageNavigation
    }
}<commit_msg>DASHB-289: fixed wrong loader behavior in Mozilla Firefox<commit_after>/*
 * CODENVY CONFIDENTIAL
 * __________________
 *
 *  [2012] - [2014] Codenvy, S.A.
 *  All Rights Reserved.
 *
 * NOTICE:  All information contained herein is, and remains
 * the property of Codenvy S.A. and its suppliers,
 * if any.  The intellectual and technical concepts contained
 * herein are proprietary to Codenvy S.A.
 * and its suppliers and may be covered by U.S. and Foreign Patents,
 * patents in process, and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material
 * is strictly forbidden unless prior written permission is obtained
 * from Codenvy S.A..
 */
jQuery.fn.doesExist = function(){
   return jQuery(this).length > 0;
};

if (typeof analytics === "undefined") {
    analytics = {};
}

analytics.main = new Main();

function Main() {
    jQuery(document).ready(function () {
        setupButtons();
        loadAllWidgets();
    });

    var setupButtons = function() {
        // Time selectors group
        $("#timely-dd button.command-btn").click(function () {
            $("#timely-dd button").removeClass('btn-primary');
            $(this).addClass('btn-primary');
            reloadWidgets($("#timely-dd").attr("targetWidgets"));
        });

        // "Filter by" group
        $("#filter-by button.command-btn").click(function() {
           $("#filter-by button").removeClass('btn-primary');
           if ($("#filter-by input[name='keyword']").val() != "") {  // select button only if there is some text in keyword input
              $(this).addClass('btn-primary');
              reloadWidgets($("#filter-by").attr("targetWidgets"));
           }
        });
        $("#filter-by button.clear-btn").click(function() {    // clearing
            $("#filter-by button").removeClass('btn-primary');
            $("#filter-by input[name='keyword']").val("");

            reloadWidgets($("#filter-by").attr("targetWidgets"));
        });

        // "Date range" group
        $("#date-range button.command-btn").click(function() {
           $("#date-range button").removeClass('btn-primary');
           if ($("#date-range input[name='from_date']").val() != ""
                 || $("#date-range input[name='to_date']").val() != "") {  // select button only if there is date in one of date range input
              $(this).addClass('btn-primary');
              reloadWidgets($("#date-range").attr("targetWidgets"));
           }
        });
        $("#date-range button.clear-btn").click(function() {    // clearing
            $("#date-range button").removeClass('btn-primary');
            $("#date-range input[name='from_date']").val("");
            $("#date-range input[name='to_date']").val("");

            reloadWidgets($("#date-range").attr("targetWidgets"));
        });

        // Metric selectors group
        $("#metric button.command-btn").click(function () {
            $("#metric button").removeClass('btn-primary');
            $(this).addClass('btn-primary');

            reloadWidgets($("#metric").attr("targetWidgets"));
        });

        // Ide version selectors group
        $("#ide-version a.command-btn").click(function () {
            $("#ide-version a").removeClass('btn-primary');
            $(this).addClass('btn-primary');

            reloadWidgets($("#ide-version").attr("targetWidgets"));
        });
    };

    /**
     * Get params from buttons state
     */
    function getParamsFromButtons() {
        var params = {};

        // process time selector
        var selectedTimeButton = $("#timely-dd button.btn-primary");
        if (selectedTimeButton.doesExist()) {
           params.time_unit = selectedTimeButton.val();
        }

        // process filter
        var filterInput = $("#filter-by input[name='keyword']");
        var selectedFilterButton = $("#filter-by button.btn-primary")
        if (selectedFilterButton.doesExist()
              && filterInput.val().length > 0) {
           params[selectedFilterButton.val()] = filterInput.val();
        }

        // process date-range
        var fromDateInput = $("#date-range input[name='from_date']");
        if (fromDateInput.doesExist()
              && fromDateInput.val() != "") {
           params["from_date"] = fromDateInput.val();
        }
        var toDateInput = $("#date-range input[name='to_date']");
        if (toDateInput.doesExist()
              && toDateInput.val() != "") {
           params["to_date"] = toDateInput.val();
        }

        // process metric selector
        var selectedMetricButton = $("#metric button.btn-primary");
        if (selectedMetricButton.doesExist()) {
           params.metric = selectedMetricButton.val();
        }

        // process ide version selector
        var ideVersionButton = $("#ide-version a.btn-primary");
        if (ideVersionButton.doesExist()) {
           if (typeof ideVersionButton.attr("value") != "undefined") {
               params.ide = ideVersionButton.attr("value");
           }
           updateGlobalParamInStorage("ide", params["ide"]);  // params["ide"] is undefined if ideVersionButton.attr("value") is undefined
        }

        return params;
    };

    /**
     * Reload div on clicking on page navigation links at the bottom of the tables
     * @param pageNavigationLinkElement dom-element
     * @param widgetName id of div to reload
     */
    function reloadWidgetOnPageNavigation(pageNavigationLinkElement, widgetName) {
        var jQueryPageLinkElement = jQuery(pageNavigationLinkElement);
        var href = jQueryPageLinkElement.attr("href");

        var urlParams = analytics.util.extractUrlParams(href);

        reloadWidget(widgetName, urlParams);
    }

    /**
     * Reload widgets
     * @param widgetNames: String with widget names divided by ","
     */
    function reloadWidgets(widgetNames) {
        if (typeof widgetNames != "undefined") {
           var widgetName = widgetNames.split(',');

           var urlParams = analytics.util.extractUrlParams(window.location.href);
           var buttonParams = getParamsFromButtons();

           // union url params with button params and choose button params values above url params values
           var params = analytics.util.unionWithRewrite(urlParams, buttonParams);

           if (widgetName == "_all") {
               loadAllWidgets(params);
           } else {
               for (var i in widgetName) {
                   reloadWidget(widgetName[i], params);
               }
           }
        }
    }

    function reloadWidget(widgetName, params) {
        loadWidget(widgetName, params, function(data) {
          // rewrite page location to make it possible to navigate new url through the browser's history
          if (analytics.configuration.getProperty(widgetName, "isNeedToSaveInHistory")) {
             var pageUrl = window.location.href;
             if (pageUrl.indexOf('?')) {
                var absUrl = pageUrl .split('?');
                pageUrl = absUrl[0];
             }

             var newPageUrl = pageUrl;
             var urlParams = analytics.util.constructUrlParams(params);
             if (urlParams != null) {
                newPageUrl += "?" + urlParams;
             }

             window.history.pushState({"widgetName": widgetName}, document.title, newPageUrl);
          }
       });

    }

    function loadAllWidgets(params) {
       // load all widgets at first time of loading the page
       if (typeof params == "undefined") {
           params = analytics.util.extractUrlParams(window.location.href);
           updateGlobalParamsWithValuesFromStorage(params);
           updateCommandButtonsState(params);
       }

       var widgetNames = analytics.configuration.getWidgetNames();
       for(var i in widgetNames) {
           var widgetName = widgetNames[i];
           if (jQuery("#" + widgetName).doesExist()) {
               if (analytics.configuration.getProperty(widgetName, "isNeedToSaveInHistory")) {
                   // update div when navigating in history
                   var everPushedSomething = false;
                   var initialUrl = window.location.href;
                   window.addEventListener('popstate', function(event) {
                      if (! everPushedSomething
                             && window.location.href == initialUrl) {
                         everPushedSomething = true;
                         return;
                      }

                      if (event.state != null
                              && typeof event.state.widgetName != "undefined") {
                          // update parameter buttons selection
                          var params = analytics.util.extractUrlParams(window.location.href);
                          updateCommandButtonsState(params);

                          loadWidget(event.state.widgetName, params);
                      }
                   });
               }

               loadWidget(widgetName, params);
           }
       }
    }

    function loadWidget(widgetName, params, callback) {
        var params = params || {};
        var callback = callback || function(){};

        var view = analytics.factory.getView(widgetName);
        view.setParams(params);

        // display loader after the 2 seconds of timeout
        analytics.views.loader.needLoader = true;
        var timeoutInMillisec = 2000;
        setTimeout(function() {
           if (analytics.views.loader.needLoader) {
               analytics.views.loader.show();
           }
        }, timeoutInMillisec);

        var model = analytics.factory.getModel(widgetName);
        model.clearDoneFunction();
        model.pushDoneFunction(function() {
            analytics.views.loader.needLoader = false;
            view.show();
            analytics.views.loader.hide();
            callback();
        })

        model.clearFailFunction();
        model.pushFailFunction(function(status, textStatus, errorThrown) {
            if (textStatus == "abort") {
                analytics.views.loader.needLoader = false;
                analytics.views.loader.hide();

                view.showAbortMessage();
            } else {
                analytics.views.loader.needLoader = false;
                analytics.views.loader.hide();

                view.showErrorMessage(status, textStatus, errorThrown);
            }
        });

        var presenter = analytics.factory.getPresenter(widgetName, view, model);

        view.clear();
        presenter.load();
    }

    function updateCommandButtonsState(params) {
       var params = params || {};

       // update time selection buttons
       var timeUnitButtons = jQuery("#timely-dd button");
       if (timeUnitButtons.doesExist()) {
          setPrimaryButtonOnValue(timeUnitButtons, params["time_unit"]);
       }

       // update filter-by group
       var filterButtons = jQuery("#filter-by button");
       var filterInput = $("#filter-by input[name='keyword']");
       if (filterButtons.doesExist() && filterInput.doesExist()) {
          jQuery("#filter-by button").removeClass('btn-primary');
          filterInput.val("");

          // find out "filter by" param like "Email: test@test.com" which is linked with button with text "Email"
          for (var i = 0; i < filterButtons.length; i++) {
             var button = jQuery(filterButtons[i]);
             var filterParamValue = params[button.attr("value")];
             if (typeof filterParamValue != "undefined") {
                button.addClass('btn-primary');
                filterInput.val(filterParamValue);   // set keyword input = value from param
                break;
             }
          }
       }

       // update date-range group
       var isDateParameterPresenceInQuery = false;
       {  var input = $("#date-range input[name='from_date']");
          var queryParam = params["from_date"];
          if (input.doesExist()) {
             if (typeof queryParam != "undefined") {
                input.val(queryParam);
                isDateParameterPresenceInQuery = true;
             } else {
                input.val("");
             }
          }
       }
       {  var input = $("#date-range input[name='to_date']");
          var queryParam = params["to_date"];
          if (input.doesExist()) {
             if (typeof queryParam != "undefined") {
                input.val(queryParam);
                isDateParameterPresenceInQuery = true;
             } else {
                input.val("");
             }
          }
       }
       jQuery("#date-range button").removeClass('btn-primary');
       var dateRangeButton = jQuery("#date-range button:contains('Filter')");
       if (dateRangeButton.doesExist() && isDateParameterPresenceInQuery) {
          jQuery("#date-range button").removeClass('btn-primary');
          dateRangeButton.addClass('btn-primary');
       }

       // update metric selection buttons
       var metricButtons = jQuery("#metric button");
       if (metricButtons.doesExist()) {
           setPrimaryButtonOnValue(metricButtons, params["metric"]);
       }

       // update ide version selection buttons
       var ideVersionButtons = jQuery("#ide-version a");
       if (ideVersionButtons.doesExist()) {
           setPrimaryButtonOnValue(ideVersionButtons, params["ide"]);
       }
    }

    /**
     * Update undefined global params with values from HTML5 Web Storage
     */
    function updateGlobalParamsWithValuesFromStorage(params) {
        if (!analytics.util.isBrowserSupportWebStorage()) {
            return;
        }

        var globalParamList = analytics.configuration.getGlobalParamList();
        for (var i in globalParamList) {
            var globalParamName = globalParamList[i];
            var storedParam = localStorage.getItem(globalParamName);  // get param value from HTML5 Web Storage
            if (typeof params[globalParamName] == "undefined"
                    && storedParam != null) {
                params[globalParamName] = storedParam;
            }
        }
    }

    /**
     * Save global param value in the HTML5 Web Storage
     */
    function updateGlobalParamInStorage(parameter, value) {
        if (!analytics.util.isBrowserSupportWebStorage()) {
            return;
        }

        if (analytics.configuration.isParamGlobal(parameter)) {
            if (typeof value != "undefined") {
                localStorage.setItem(parameter, value);    // save param value in the HTML5 Web Storage
            } else {
                localStorage.removeItem(parameter);    // remove param from HTML5 Web Storage
            }
        }
    }

    function setPrimaryButtonOnValue(buttonGroup, parameter) {
        buttonGroup.removeClass('btn-primary');
        if (typeof parameter != "undefined") {
            // set as primary the button with value = parameter
            for (var i = 0; i < buttonGroup.size(); i++) {
               var button = jQuery(buttonGroup[i]);
               if (button.attr("value") == parameter) {
                  button.addClass("btn-primary");
                  break;
               }
            }
        } else {
           // restore default primary button
           for (var i = 0; i < buttonGroup.size(); i++) {
               var button = jQuery(buttonGroup[i]);
               if (typeof button.attr("default") != "undefined") {
                  button.addClass("btn-primary");
                  break;
               }
           }
        }
     }

    /** ****************** library API ********** */
    return {
        reloadWidgetOnPageNavigation: reloadWidgetOnPageNavigation
    }
}<|endoftext|>


<commit_before>define(['app', 'underscore'], function(app, _) {

	app.directive('printSmartDialog', ["$http", "$timeout", function($http, $timeout) {
		return {
			restrict : "AC",
			require: '^printSmart',
			replace : true,
			scope :  {},
			templateUrl : "/app/views/print-smart/print-smart-dialog.html",
			link: function (scope, element, attrs, psCtrl) {

				element.on("show.bs.modal", function() {
					scope.documents = psCtrl.getDocuments();
					scope.badgeCode = "";
					scope.error     = null;
					scope.success   = null;
				});

				scope.preferedLanguage = "en";
				scope.badgeCode        = "";
				scope.cleanBadge       = cleanBadge;
				scope.clearError       = clearError;
				scope.isNetworkCall    = false;

				scope.languages = [
					{ code : "ar", name : "العربية" },
					{ code : "en", name : "English" },
					{ code : "es", name : "Español" },
					{ code : "fr", name : "Français" },
					{ code : "ru", name : "Русский" },
					{ code : "zh", name : "中文" },
				];

				//==============================================
				//
				//
				//==============================================
				function cleanBadge() {
					return (scope.badgeCode||"").replace(/[^0-9]/g, "");
				}

				//==============================================
				//
				//
				//==============================================
				function clearError() {
					scope.error = null;
				}

				//==============================================
				//
				//
				//==============================================
				scope.canPrint = function() {
					return cleanBadge().length >= 8 &&
						   scope.documents.length > 0 &&
						   !!scope.preferedLanguage;
				};

				//==============================================
				//
				//
				//==============================================
				scope.print = function() {

					scope.error = null;
					scope.success = null;

					var postData = {
						badge     : cleanBadge(),
						documents : _.map(scope.documents, function(d) {
							return {
								symbol   : d.symbol,
								url      : d.urls[scope.preferedLanguage] || d.en,
								language : d.urls[scope.preferedLanguage] ? scope.preferedLanguage : "en"
							};
						})
					};

					scope.isNetworkCall = true;

					$http.post("/api/v2014/papersmart-requests", postData).success(function(data) {

						scope.isNetworkCall = false;
						scope.success       = angular.isObject(data) ? data : {};

					}).error(function(data, status){

						scope.isNetworkCall = false;

						if(angular.isObject(data)) scope.error = data
						else if(status==404)       scope.error = { error: "NO_SERVICE" };
						else if(status==500)       scope.error = { error: "NO_SERVICE" };
						else                       scope.error = { error: "UNKNOWN",    message : "Unknown error" };
					});
				};

				//==============================================
				//
				//
				//==============================================
				scope.isCustomError = function() {

					if(!scope.error)
						return false;

					return scope.error.error!="INVALID_BADGE_ID" &&
						   scope.error.error!="INVALID_BADGE_REVOKED" &&
						   scope.error.error!="INVALID_BADGE_EXPIRED" &&
						   scope.error.error!="NO_SERVICE";
				}

				//==============================================
				//
				//
				//==============================================
				scope.close = function(clearDocuments) {

					if(!!clearDocuments)
						psCtrl.clearDocuments();

					psCtrl.showPrint(false);
				};
			}
		};
	}]);
});<commit_msg>New api endpoint<commit_after>define(['app', 'underscore'], function(app, _) {

	app.directive('printSmartDialog', ["$http", "$timeout", function($http, $timeout) {
		return {
			restrict : "AC",
			require: '^printSmart',
			replace : true,
			scope :  {},
			templateUrl : "/app/views/print-smart/print-smart-dialog.html",
			link: function (scope, element, attrs, psCtrl) {

				element.on("show.bs.modal", function() {
					scope.documents = psCtrl.getDocuments();
					scope.badgeCode = "";
					scope.error     = null;
					scope.success   = null;
				});

				scope.preferedLanguage = "en";
				scope.badgeCode        = "";
				scope.cleanBadge       = cleanBadge;
				scope.clearError       = clearError;
				scope.isNetworkCall    = false;

				scope.languages = [
					{ code : "ar", name : "العربية" },
					{ code : "en", name : "English" },
					{ code : "es", name : "Español" },
					{ code : "fr", name : "Français" },
					{ code : "ru", name : "Русский" },
					{ code : "zh", name : "中文" },
				];

				//==============================================
				//
				//
				//==============================================
				function cleanBadge() {
					return (scope.badgeCode||"").replace(/[^0-9]/g, "");
				}

				//==============================================
				//
				//
				//==============================================
				function clearError() {
					scope.error = null;
				}

				//==============================================
				//
				//
				//==============================================
				scope.canPrint = function() {
					return cleanBadge().length >= 8 &&
						   scope.documents.length > 0 &&
						   !!scope.preferedLanguage;
				};

				//==============================================
				//
				//
				//==============================================
				scope.print = function() {

					scope.error = null;
					scope.success = null;

					var postData = {
						badge     : cleanBadge(),
						documents : _.map(scope.documents, function(d) {
							return {
								symbol   : d.symbol,
								url      : d.urls[scope.preferedLanguage] || d.en,
								language : d.urls[scope.preferedLanguage] ? scope.preferedLanguage : "en"
							};
						})
					};

					scope.isNetworkCall = true;

					$http.post("/api/v2014/printsmart-requests/batch", postData).success(function(data) {

						scope.isNetworkCall = false;
						scope.success       = angular.isObject(data) ? data : {};

					}).error(function(data, status){

						scope.isNetworkCall = false;

						if(angular.isObject(data)) scope.error = data
						else if(status==404)       scope.error = { error: "NO_SERVICE" };
						else if(status==500)       scope.error = { error: "NO_SERVICE" };
						else                       scope.error = { error: "UNKNOWN",    message : "Unknown error" };
					});
				};

				//==============================================
				//
				//
				//==============================================
				scope.isCustomError = function() {

					if(!scope.error)
						return false;

					return scope.error.error!="INVALID_BADGE_ID" &&
						   scope.error.error!="INVALID_BADGE_REVOKED" &&
						   scope.error.error!="INVALID_BADGE_EXPIRED" &&
						   scope.error.error!="NO_SERVICE";
				}

				//==============================================
				//
				//
				//==============================================
				scope.close = function(clearDocuments) {

					if(!!clearDocuments)
						psCtrl.clearDocuments();

					psCtrl.showPrint(false);
				};
			}
		};
	}]);
});<|endoftext|>