odoo.define('base.settings', function (require) {
"use strict";
var BasicModel = require('web.BasicModel');
var core = require('web.core');
var config = require('web.config');
var FormView = require('web.FormView');
var FormController = require('web.FormController');
var FormRenderer = require('web.FormRenderer');
var view_registry = require('web.view_registry');
var QWeb = core.qweb;
var BaseSettingRenderer = FormRenderer.extend({
events: _.extend({}, FormRenderer.prototype.events, {
'click .tab': '_onSettingTabClick',
'keyup .searchInput': '_onKeyUpSearch',
}),
init: function () {
this._super.apply(this, arguments);
this.activeView = false;
this.activeTab = false;
},
start: function () {
var prom = this._super.apply(this, arguments);
if (config.device.isMobile) {
core.bus.on("DOM_updated", this, function () {
this._moveToTab(this.currentIndex || this._currentAppIndex());
});
}
return prom;
},
/**
* @override
*/
on_attach_callback: function () {
this._super.apply(this, arguments);
// set default focus on searchInput
this.searchInput.focus();
},
/**
* initialize modules list.
* remove module that restricted in groups
* data contains
* {
* key: moduel key
* string: moduel string
* imgurl: icon url
* }
*
* @private
*/
_initModules: function () {
var self = this;
this.modules = [];
_.each(this.$('.app_settings_block'), function (settingView, index) {
var group = !$(settingView).hasClass('o_invisible_modifier');
var isNotApp = $(settingView).hasClass('o_not_app');
if(group && !isNotApp) {
var data = $(settingView).data();
data.string = $(settingView).attr('string') || data.string;
self.modules.push({
key: data.key,
string: data.string,
imgurl: self._getAppIconUrl(data.key),
});
} else {
$(settingView).remove();
}
});
},
/**
* initialize searchtext variable
* initialize jQuery search input element
*
* @private
*/
_initSearch: function () {
this.searchInput = this.$('.searchInput');
if (this.searchText) {
this.searchInput.val(this.searchText);
this._onKeyUpSearch();
} else {
this.searchText = "";
}
},
/**
* In mobile view behaviour is like swipe content left / right and apps tab will be shown on the top.
* This method will set the required properties (styling and css).
*
* @private
* @param {int} currentTab
*/
_activateSettingMobileTab: function (currentTab) {
var self = this;
var moveTo = currentTab;
var next = moveTo + 1;
var previous = moveTo - 1;
this.$(".settings .app_settings_block").removeClass("previous next current before after");
this.$(".settings_tab .tab").removeClass("previous next current before after");
_.each(this.modules, function (module, index) {
var tab = self.$(".tab[data-key='" + module.key + "']");
var view = module.settingView;
if (index === previous) {
tab.addClass("previous");
tab.css("margin-left", "0px");
view.addClass("previous");
} else if (index === next) {
tab.addClass("next");
tab.css("margin-left", "-" + tab.outerWidth() + "px");
view.addClass("next");
} else if (index < moveTo) {
tab.addClass("before");
tab.css("margin-left", "-" + tab.outerWidth() + "px");
view.addClass("before");
} else if (index === moveTo) {
var marginLeft = tab.outerWidth() / 2;
tab.css("margin-left", "-" + marginLeft + "px");
tab.addClass("current");
view.addClass("current");
} else if (index > moveTo) {
tab.addClass("after");
tab.css("margin-left", "0");
view.addClass("after");
}
});
},
/**
* find current app index in modules
*
*/
_currentAppIndex: function () {
var self = this;
var index = _.findIndex(this.modules, function (module) {
return module.key === self.activeSettingTab;
});
return index;
},
/**
* Enables swipe navigation between settings pages
*
* @private
*/
_enableSwipe: function () {
var self = this;
this.$('.settings').swipe({
swipeLeft: function () {
self._moveToTab(self.currentIndex + 1);
},
swipeRight: function () {
self._moveToTab(self.currentIndex - 1);
}
});
},
/**
*
* @private
* @param {string} module
* @returns {string} icon url
*/
_getAppIconUrl: function (module) {
return module === "general_settings" ? "/base/static/description/settings.png" : "/"+module+"/static/description/icon.png";
},
/**
*
* @private
* @param {string} imgurl
* @param {string} string(moduel name)
* @returns {object}
*/
_getSearchHeader: function (imgurl, string) {
return $(QWeb.render('BaseSetting.SearchHeader', {
imgurl: imgurl,
string: string
}));
},
/**
* add placeholder attr in input element
* @override
* @private
* @param {jQueryElement} $el
* @param {Object} node
*/
_handleAttributes: function ($el, node) {
this._super.apply(this, arguments);
if (node.attrs.placeholder) {
$el.attr('placeholder', node.attrs.placeholder);
}
},
/**
* move to selected setting
*
* @private
* @param {int} index
*/
_moveToTab: function (index) {
this.currentIndex = !index || index === -1 ? 0 : (index === this.modules.length ? index - 1 : index);
if (this.currentIndex !== -1) {
if (this.activeView) {
this.activeView.addClass("o_hidden");
}
if (this.activeTab) {
this.activeTab.removeClass("selected");
}
var view = this.modules[this.currentIndex].settingView;
var tab = this.$(".tab[data-key='" + this.modules[this.currentIndex].key + "']");
view.removeClass("o_hidden");
this.activeView = view;
this.activeTab = tab;
if (config.device.isMobile) {
this._activateSettingMobileTab(this.currentIndex);
} else {
tab.addClass("selected");
}
}
},
_onSettingTabClick: function (event) {
this.searchInput.focus();
if (this.searchText.length > 0) {
this.searchInput.val('');
this.searchText = "";
this._searchSetting();
}
var settingKey = this.$(event.currentTarget).data('key');
this._moveToTab(_.findIndex(this.modules, function (m) {
return m.key === settingKey;
}));
},
_onKeyUpSearch: function (event) {
this.searchText = this.searchInput.val();
this.activeTab.removeClass('selected');
if (config.device.isMobile) {
this.$('.settings_tab').addClass('o_hidden');
this.$('.settings').addClass('d-block');
}
this._searchSetting();
},
/**
* reset setting view
*
* @private
*/
_resetSearch: function () {
this.searchInput.val("");
_.each(this.modules, function (module) {
module.settingView.addClass('o_hidden');
module.settingView.find('.o_setting_box').removeClass('o_hidden');
module.settingView.find('h2').removeClass('o_hidden');
module.settingView.find('.settingSearchHeader').addClass('o_hidden');
module.settingView.find('.o_settings_container').addClass('mt16');
});
this.activeTab.removeClass('o_hidden').addClass('selected');
this.activeView.removeClass('o_hidden');
if (config.device.isMobile) {
this.$('.settings_tab').removeClass('o_hidden');
this.$('.settings').removeClass('d-block');
}
},
_render: function () {
var self = this;
return this._super.apply(this, arguments).then(function() {
self._initModules();
self._renderLeftPanel();
self._initSearch();
if (config.device.isMobile) {
self._enableSwipe();
}
});
},
_renderLeftPanel: function () {
var self = this;
_.each(this.modules, function (module) {
module.settingView = self.$('.app_settings_block[data-key="' + module.key + '"]');
module.settingView.addClass("o_hidden");
module.settingView.prepend(self._getSearchHeader(module.imgurl, module.string));
});
this._renderTabs();
this._moveToTab(this.currentIndex || this._currentAppIndex());
},
_renderTabs: function () {
var tabs = $(QWeb.render('BaseSetting.Tabs', {tabItems : this.modules}));
tabs.appendTo(this.$(".settings_tab"));
},
/**
* search setting in DOM
*
* @private
*/
_searchSetting: function () {
var self = this;
this.count = 0;
_.each(this.modules, function (module) {
self.inVisibleCount = 0;
module.settingView.find('.o_setting_box').addClass('o_hidden');
module.settingView.find('h2').addClass('o_hidden');
module.settingView.find('.settingSearchHeader').addClass('o_hidden');
module.settingView.find('.o_settings_container').removeClass('mt16');
var resultSetting = module.settingView.find("label:containsTextLike('" + self.searchText + "')");
if (resultSetting.length > 0) {
resultSetting.each(function () {
var settingBox = $(this).closest('.o_setting_box');
if (!settingBox.hasClass('o_invisible_modifier')) {
settingBox.removeClass('o_hidden');
$(this).html(self._wordHighlighter($(this).html(), self.searchText));
} else {
self.inVisibleCount++;
}
});
if (self.inVisibleCount !== resultSetting.length) {
module.settingView.find('.settingSearchHeader').removeClass('o_hidden');
module.settingView.removeClass('o_hidden');
}
} else {
++self.count;
}
});
this.count === _.size(this.modules) ? this.$('.notFound').removeClass('o_hidden') : this.$('.notFound').addClass('o_hidden');
if (this.searchText.length === 0) {
this._resetSearch();
}
},
/**
* highlight search word
*
* @private
* @param {string} text
* @param {string} word
*/
_wordHighlighter: function (text, word) {
if (text.indexOf('highlighter') !== -1) {
text = text.replace('', "");
text = text.replace("", "");
}
var match = text.search(new RegExp(word, "i"));
word = text.substring(match, match + word.length);
var hilitedWord = "" + word + '';
return text.replace(word, hilitedWord);
},
});
var BaseSettingController = FormController.extend({
init: function () {
this._super.apply(this, arguments);
this.disableAutofocus = true;
this.renderer.activeSettingTab = this.initialState.context.module;
},
/**
* Settings view should always be in edit mode, so we have to override
* default behaviour
*
* @override
*/
willRestore: function () {
this.mode = 'edit';
},
});
var BaseSettingsModel = BasicModel.extend({
/**
* @override
*/
save: function (recordID) {
var self = this;
return this._super.apply(this, arguments).then(function (result) {
// we remove here the res_id, because the record should still be
// considered new. We want the web client to always perform a
// default_get to fetch the settings anew.
delete self.localData[recordID].res_id;
return result;
});
},
});
var BaseSettingView = FormView.extend({
jsLibs: [],
config: _.extend({}, FormView.prototype.config, {
Model: BaseSettingsModel,
Renderer: BaseSettingRenderer,
Controller: BaseSettingController,
}),
/**
* Overrides to lazy-load touchSwipe library in mobile.
*
* @override
*/
init: function () {
if (config.device.isMobile) {
this.jsLibs.push('/web/static/lib/jquery.touchSwipe/jquery.touchSwipe.js');
}
this._super.apply(this, arguments);
},
});
view_registry.add('base_settings', BaseSettingView);
return {
Model: BaseSettingsModel,
Renderer: BaseSettingRenderer,
Controller: BaseSettingController,
};
});