[FIX] dict notation for js reserved words in options (Fixes #2957)
[odoo/odoo.git] / addons / website / static / src / js / website.js
1 (function() {
2     "use strict";
3
4     var website = {};
5     openerp.website = website;
6
7     website.translatable = !!$('html').data('translatable');
8
9     /* ----------------------------------------------------
10        Helpers
11        ---------------------------------------------------- */ 
12     website.get_context = function (dict) {
13         var html = document.documentElement;
14         return _.extend({
15             lang: html.getAttribute('lang').replace('-', '_'),
16             website_id: html.getAttribute('data-website-id')|0
17         }, dict);
18     };
19
20     website.parseQS = function (qs) {
21         var match,
22             params = {},
23             pl     = /\+/g,  // Regex for replacing addition symbol with a space
24             search = /([^&=]+)=?([^&]*)/g;
25
26         while ((match = search.exec(qs))) {
27             var name = decodeURIComponent(match[1].replace(pl, " "));
28             var value = decodeURIComponent(match[2].replace(pl, " "));
29             params[name] = value;
30         }
31         return params;
32     };
33
34     var parsedSearch;
35     website.parseSearch = function () {
36         if (!parsedSearch) {
37             parsedSearch = website.parseQS(window.location.search.substring(1));
38         }
39         return parsedSearch;
40     };
41
42     website.parseHash = function () {
43         return website.parseQS(window.location.hash.substring(1));
44     };
45
46     website.reload = function () {
47         location.hash = "scrollTop=" + window.document.body.scrollTop;
48         if (location.search.indexOf("enable_editor") > -1) {
49             window.location.href = window.location.href.replace(/enable_editor(=[^&]*)?/g, '');
50         } else {
51             window.location.reload();
52         }
53     };
54
55     /* ----------------------------------------------------
56        Widgets
57        ---------------------------------------------------- */ 
58
59     website.prompt = function (options) {
60         /**
61          * A bootstrapped version of prompt() albeit asynchronous
62          * This was built to quickly prompt the user with a single field.
63          * For anything more complex, please use editor.Dialog class
64          *
65          * Usage Ex:
66          *
67          * website.prompt("What... is your quest ?").then(function (answer) {
68          *     arthur.reply(answer || "To seek the Holy Grail.");
69          * });
70          *
71          * website.prompt({
72          *     select: "Please choose your destiny",
73          *     init: function() {
74          *         return [ [0, "Sub-Zero"], [1, "Robo-Ky"] ];
75          *     }
76          * }).then(function (answer) {
77          *     mame_station.loadCharacter(answer);
78          * });
79          *
80          * @param {Object|String} options A set of options used to configure the prompt or the text field name if string
81          * @param {String} [options.window_title=''] title of the prompt modal
82          * @param {String} [options.input] tell the modal to use an input text field, the given value will be the field title
83          * @param {String} [options.textarea] tell the modal to use a textarea field, the given value will be the field title
84          * @param {String} [options.select] tell the modal to use a select box, the given value will be the field title
85          * @param {Object} [options.default=''] default value of the field
86          * @param {Function} [options.init] optional function that takes the `field` (enhanced with a fillWith() method) and the `dialog` as parameters [can return a deferred]
87          */
88         if (typeof options === 'string') {
89             options = {
90                 text: options
91             };
92         }
93         options = _.extend({
94             window_title: '',
95             field_name: '',
96             'default': '', // dict notation for IE<9
97             init: function() {}
98         }, options || {});
99
100         var type = _.intersection(Object.keys(options), ['input', 'textarea', 'select']);
101         type = type.length ? type[0] : 'text';
102         options.field_type = type;
103         options.field_name = options.field_name || options[type];
104
105         var def = $.Deferred();
106         var dialog = $(openerp.qweb.render('website.prompt', options)).appendTo("body");
107         options.$dialog = dialog;
108         var field = dialog.find(options.field_type).first();
109         field.val(options['default']); // dict notation for IE<9
110         field.fillWith = function (data) {
111             if (field.is('select')) {
112                 var select = field[0];
113                 data.forEach(function (item) {
114                     select.options[select.options.length] = new Option(item[1], item[0]);
115                 });
116             } else {
117                 field.val(data);
118             }
119         };
120         var init = options.init(field, dialog);
121         $.when(init).then(function (fill) {
122             if (fill) {
123                 field.fillWith(fill);
124             }
125             dialog.modal('show');
126             field.focus();
127             dialog.on('click', '.btn-primary', function () {
128                 def.resolve(field.val(), field, dialog);
129                 dialog.remove();
130                 $('.modal-backdrop').remove();
131             });
132         });
133         dialog.on('hidden.bs.modal', function () {
134             def.reject();
135             dialog.remove();
136             $('.modal-backdrop').remove();
137         });
138         if (field.is('input[type="text"], select')) {
139             field.keypress(function (e) {
140                 if (e.which == 13) {
141                     e.preventDefault();
142                     dialog.find('.btn-primary').trigger('click');
143                 }
144             });
145         }
146         return def;
147     };
148
149     website.error = function(data, url) {
150         var $error = $(openerp.qweb.render('website.error_dialog', {
151             'title': data.data ? data.data.arguments[0] : "",
152             'message': data.data ? data.data.arguments[1] : data.statusText,
153             'backend_url': url
154         }));
155         $error.appendTo("body");
156         $error.modal('show');
157     };
158
159     website.form = function (url, method, params) {
160         var form = document.createElement('form');
161         form.setAttribute('action', url);
162         form.setAttribute('method', method);
163         _.each(params, function (v, k) {
164             var param = document.createElement('input');
165             param.setAttribute('type', 'hidden');
166             param.setAttribute('name', k);
167             param.setAttribute('value', v);
168             form.appendChild(param);
169         });
170         document.body.appendChild(form);
171         form.submit();
172     };
173
174     website.init_kanban = function ($kanban) {
175         $('.js_kanban_col', $kanban).each(function () {
176             var $col = $(this);
177             var $pagination = $('.pagination', $col);
178             if(!$pagination.size()) {
179                 return;
180             }
181
182             var page_count =  $col.data('page_count');
183             var scope = $pagination.last().find("li").size()-2;
184             var kanban_url_col = $pagination.find("li a:first").attr("href").replace(/[0-9]+$/, '');
185
186             var data = {
187                 'domain': $col.data('domain'),
188                 'model': $col.data('model'),
189                 'template': $col.data('template'),
190                 'step': $col.data('step'),
191                 'orderby': $col.data('orderby')
192             };
193
194             $pagination.on('click', 'a', function (ev) {
195                 ev.preventDefault();
196                 var $a = $(ev.target);
197                 if($a.parent().hasClass('active')) {
198                     return;
199                 }
200
201                 var page = +$a.attr("href").split(",").pop().split('-')[1];
202                 data['page'] = page;
203
204                 $.post('/website/kanban', data, function (col) {
205                     $col.find("> .thumbnail").remove();
206                     $pagination.last().before(col);
207                 });
208
209                 var page_start = page - parseInt(Math.floor((scope-1)/2), 10);
210                 if (page_start < 1 ) page_start = 1;
211                 var page_end = page_start + (scope-1);
212                 if (page_end > page_count ) page_end = page_count;
213
214                 if (page_end - page_start < scope) {
215                     page_start = page_end - scope > 0 ? page_end - scope : 1;
216                 }
217
218                 $pagination.find('li.prev a').attr("href", kanban_url_col+(page-1 > 0 ? page-1 : 1));
219                 $pagination.find('li.next a').attr("href", kanban_url_col+(page < page_end ? page+1 : page_end));
220                 for(var i=0; i < scope; i++) {
221                     $pagination.find('li:not(.prev):not(.next):eq('+i+') a').attr("href", kanban_url_col+(page_start+i)).html(page_start+i);
222                 }
223                 $pagination.find('li.active').removeClass('active');
224                 $pagination.find('li:has(a[href="'+kanban_url_col+page+'"])').addClass('active');
225
226             });
227
228         });
229     };
230
231     /* ----------------------------------------------------
232        Async Ready and Template loading
233        ---------------------------------------------------- */ 
234     var templates_def = $.Deferred().resolve();
235     website.add_template_file = function(template) {
236         templates_def = templates_def.then(function() {
237             var def = $.Deferred();
238             openerp.qweb.add_template(template, function(err) {
239                 if (err) {
240                     def.reject(err);
241                 } else {
242                     def.resolve();
243                 }
244             });
245             return def;
246         });
247     };
248
249     website.add_template_file('/website/static/src/xml/website.xml');
250
251     website.dom_ready = $.Deferred();
252     $(document).ready(function () {
253         website.dom_ready.resolve();
254         // fix for ie
255         if($.fn.placeholder) $('input, textarea').placeholder();
256     });
257
258     /**
259      * Execute a function if the dom contains at least one element matched
260      * through the given jQuery selector. Will first wait for the dom to be ready.
261      *
262      * @param {String} selector A jQuery selector used to match the element(s)
263      * @param {Function} fn Callback to execute if at least one element has been matched
264      */
265     website.if_dom_contains = function(selector, fn) {
266         website.dom_ready.then(function () {
267             var elems = $(selector);
268             if (elems.length) {
269                 fn(elems);
270             }
271         });
272     };
273
274     var all_ready = null;
275     /**
276      * Returns a deferred resolved when the templates are loaded
277      * and the Widgets can be instanciated.
278      */
279     website.ready = function() {
280         if (!all_ready) {
281             all_ready = website.dom_ready.then(function () {
282                 return templates_def;
283             }).then(function () {
284                 // display button if they are at least one editable zone in the page (check the branding)
285                 if (!!$('[data-oe-model]').size()) {
286                     $("#oe_editzone").show();
287
288                     //backwards compatibility with 8.0RC1 templates - Drop next line in master!
289                     $("#oe_editzone button").show();
290                 }
291
292                 if ($('html').data('website-id')) {
293                     website.id = $('html').data('website-id');
294                     website.session = new openerp.Session();
295                     var modules = ['website'];
296                     return openerp._t.database.load_translations(website.session, modules, website.get_context().lang);
297                 }
298             }).promise();
299         }
300         return all_ready;
301     };
302
303     website.inject_tour = function() {
304         // if a tour is active inject tour js
305     };
306
307     website.dom_ready.then(function () {
308         /* ----- PUBLISHING STUFF ---- */
309         $(document).on('click', '.js_publish_management .js_publish_btn', function () {
310             var $data = $(this).parents(".js_publish_management:first");
311             var self=this;
312             openerp.jsonRpc($data.data('controller') || '/website/publish', 'call', {'id': +$data.data('id'), 'object': $data.data('object')})
313                 .then(function (result) {
314                     $data.toggleClass("css_unpublished css_published");
315                     $data.parents("[data-publish]").attr("data-publish", +result ? 'on' : 'off');
316                 }).fail(function (err, data) {
317                     website.error(data, '/web#return_label=Website&model='+$data.data('object')+'&id='+$data.data('id'));
318                 });
319         });
320
321         /* ----- KANBAN WEBSITE ---- */
322         $('.js_kanban').each(function () {
323             website.init_kanban(this);
324         });
325
326         setTimeout(function () {
327             if (window.location.hash.indexOf("scrollTop=") > -1) {
328                 window.document.body.scrollTop = +location.hash.match(/scrollTop=([0-9]+)/)[1];
329             }
330         },0);
331
332         /* ----- WEBSITE TOP BAR ---- */
333         var $collapse = $('#oe_applications ul.dropdown-menu').clone()
334                 .attr("id", "oe_applications_collapse")
335                 .attr("class", "nav navbar-nav navbar-left navbar-collapse collapse");
336         $('#oe_applications').before($collapse);
337         $collapse.wrap('<div class="visible-xs"/>');
338         $('[data-target="#oe_applications"]').attr("data-target", "#oe_applications_collapse");
339     });
340
341     return website;
342 })();