[MERGE] forward port of branch saas-4 up to f68c835
[odoo/odoo.git] / addons / website / static / src / js / website.translator.js
1 (function () {
2     'use strict';
3
4     if (!openerp.website.translatable) {
5         // Temporary hack until the editor bar is moved to the web client
6         return;
7     }
8
9     var website = openerp.website;
10     website.add_template_file('/website/static/src/xml/website.translator.xml');
11     var nodialog = 'website_translator_nodialog';
12
13     website.EditorBar.include({
14         do_not_translate : ['-','*','!'],
15         events: _.extend({}, website.EditorBar.prototype.events, {
16             'click a[data-action=edit_master]': 'edit_master',
17         }),
18         start: function () {
19             var self = this;
20             this.initial_content = {};
21             return this._super.apply(this, arguments).then(function () {
22                 self.$("button[data-action=edit]").removeClass("hidden");
23                 self.$('button[data-action=edit]')
24                     .text("Translate");
25                 if (website.is_editable_button) {
26                     self.$('button[data-action=edit]')
27                         .after(openerp.qweb.render('website.TranslatorAdditionalButtons'));
28                 }
29                 self.$('.js_hide_on_translate').hide();
30             });
31         },
32         edit: function () {
33             var self = this;
34             var mysuper = this._super;
35             if (!localStorage[nodialog]) {
36                 var dialog = new website.TranslatorDialog();
37                 dialog.appendTo($(document.body));
38                 dialog.on('activate', this, function () {
39                     localStorage[nodialog] = dialog.$('input[name=do_not_show]').prop('checked') || '';
40                     dialog.$el.modal('hide');
41                     self.translate().then(function () {
42                         mysuper.call(self);
43                         if(self.gengo_translate){
44                             self.translation_gengo_display()
45                         }
46                     });
47                 });
48             } else {
49                 this.translate().then(function () {
50                     mysuper.call(self);
51                     if(self.gengo_translate){
52                         self.translation_gengo_display()
53                     }
54                 });
55             }
56         },
57         edit_master: function (ev) {
58             ev.preventDefault();
59             var link = $('.js_language_selector a[data-default-lang]')[0];
60             if (link) {
61                 link.search += (link.search ? '&' : '?') + 'enable_editor=1';
62                 window.location = link.attributes.href.value;
63             }
64         },
65         translate: function () {
66             var self = this;
67             this.translations = null;
68             return openerp.jsonRpc('/website/get_view_translations', 'call', {
69                 'xml_id': $(document.documentElement).data('view-xmlid'),
70                 'lang': website.get_context().lang,
71             }).then(function (translations) {
72                 self.translations = translations;
73                 self.processTranslatableNodes();
74                 // Disable non translatable t-fields
75                 $('[data-oe-type][data-oe-translate="0"]').removeAttr('data-oe-type');
76             });
77         },
78         processTranslatableNodes: function () {
79             var self = this;
80             var source_attr = 'data-oe-source-id';
81             var $editables = $('[data-oe-model="ir.ui.view"]')
82                     .not('link, script')
83                     .not('.oe_snippets,.oe_snippet, .oe_snippet *, .navbar-toggle')
84                     .not('[data-oe-type]');
85
86             $editables.each(function () {
87                 var $node = $(this);
88                 var source_id = $node.parents('[' + source_attr + ']:first').attr(source_attr)|0;
89                 var view_id = $node.attr('data-oe-source-id') || source_id || $node.attr('data-oe-id');
90                 self.transNode(this, view_id|0);
91             });
92             $('.oe_translatable_text').on('paste', function () {
93                 var node = $(this);
94                 setTimeout(function () {
95                     self.sanitizeNode(node);
96                 }, 0);
97             });
98             $(document).on('blur keyup paste', '.oe_translatable_text[contenteditable]', function(ev) {
99                 var $node = $(this);
100                 setTimeout(function () {
101                     // Doing stuff next tick because paste and keyup events are
102                     // fired before the content is changed
103                     if (ev.type == 'paste') {
104                         self.sanitizeNode($node[0]);
105                     }
106                     if (self.getInitialContent($node[0]) !== $node.text()) {
107                         $node.addClass('oe_dirty').removeClass('oe_translatable_todo oe_translatable_inprogress');
108                     }
109                 }, 0);
110             });
111         },
112         getInitialContent: function (node) {
113             return this.initial_content[node.attributes['data-oe-nodeid'].value];
114         },
115         sanitizeNode: function (node) {
116             node.text(node.text());
117         },
118         isTextNode: function (node) {
119             return node.nodeType === 3 || node.nodeType === 4;
120         },
121         isTranslatable: function (text) {
122             return text && _.str.trim(text) !== '';
123         },
124         markTranslatableNode: function (node, view_id) {
125             // TODO: link nodes with same content
126             node.className += ' oe_translatable_text';
127             node.setAttribute('data-oe-translation-view-id', view_id);
128             var content = node.childNodes[0].data.trim();
129             var trans = this.translations.filter(function (t) {
130                 return t.res_id === view_id && t.value === content;
131             });
132             if (trans.length) {
133                 node.setAttribute('data-oe-translation-id', trans[0].id);
134                 if(trans[0].gengo_translation && (trans[0].state == 'inprogress' || trans[0].state == 'to_translate')){
135                         node.className += ' oe_translatable_inprogress';
136                 }
137             } else {
138                 node.className += this.do_not_translate.indexOf(node.textContent.trim()) ? ' oe_translatable_todo' : '';
139             }
140             node.contentEditable = true;
141             var nid = _.uniqueId();
142             $(node).attr('data-oe-nodeid', nid);
143             this.initial_content[nid] = content;
144         },
145         save: function () {
146             var self = this;
147             var mysuper = this._super;
148             var trans = {};
149             // this._super.apply(this, arguments);
150             $('.oe_translatable_text.oe_dirty').each(function () {
151                 var $node = $(this);
152                 var data = $node.data();
153                 if (!trans[data.oeTranslationViewId]) {
154                     trans[data.oeTranslationViewId] = [];
155                 }
156                 trans[data.oeTranslationViewId].push({
157                     initial_content: self.getInitialContent(this),
158                     new_content: $node.text(),
159                     translation_id: data.oeTranslationId || null
160                 });
161             });
162             openerp.jsonRpc('/website/set_translations', 'call', {
163                 'data': trans,
164                 'lang': website.get_context()['lang'],
165             }).then(function () {
166                 mysuper.call(self);
167             }).fail(function () {
168                 // TODO: bootstrap alert with error message
169                 alert("Could not save translation");
170             });
171         },
172         transNode: function (node, view_id) {
173             // Mostly handling text and cdata nodes here
174             // so avoid jquery usage in this function
175             if (node.attributes['data-oe-type']) {
176                 if (node.attributes['data-oe-translate'].value == '1') {
177                     node.className += ' oe_translatable_field';
178                 }
179             } else if (node.childNodes.length === 1
180                     && this.isTextNode(node.childNodes[0])
181                     && !node.getAttribute('data-oe-model')) {
182                 this.markTranslatableNode(node, view_id);
183             } else {
184                 for (var i = 0, l = node.childNodes.length; i < l; i ++) {
185                     var n = node.childNodes[i];
186                     if (this.isTextNode(n)) {
187                         if (this.isTranslatable(n.data)) {
188                             var container = document.createElement('span');
189                             node.insertBefore(container, n);
190                             container.appendChild(n);
191                             this.markTranslatableNode(container, view_id);
192                         }
193                     } else {
194                         this.transNode(n, view_id);
195                     }
196                 }
197             }
198         },
199     });
200
201     website.RTE.include({
202         start: function () {
203             this._super.apply(this, arguments);
204             this.$el.hide();
205         },
206         fetch_editables: function (root) {
207             $(root).click(function (ev) {
208                 ev.preventDefault();
209             });
210             return $('[data-oe-translate="1"]');
211         }
212     });
213
214     website.TranslatorDialog = openerp.Widget.extend({
215         events: _.extend({}, website.EditorBar.prototype.events, {
216             'hidden.bs.modal': 'destroy',
217             'click button[data-action=activate]': function (ev) {
218                 this.trigger('activate');
219             },
220         }),
221         template: 'website.TranslatorDialog',
222         start: function () {
223             this.$el.modal();
224         },
225     });
226 })();