[IMP] website: create customize theme application and color-picker; Create theme_boot...
[odoo/odoo.git] / addons / website / static / src / js / website.theme.js
1 (function () {
2     'use strict';
3
4     openerp.website.add_template_file('/website/static/src/xml/website.theme.xml');
5
6     openerp.website.Theme = openerp.Widget.extend({
7         template: 'website.theme_customize',
8         events: {
9             'change input[data-xmlid],input[data-enable],input[data-disable]': 'change_selection',
10             'click .close': 'close',
11         },
12         start: function () {
13             var self = this;
14             this.timer = null;
15             this.reload = false;
16             this.flag = false;
17             this.$el.addClass("theme_customize_modal");
18             this.active_select_tags();
19             this.$inputs = $("input[data-xmlid],input[data-enable],input[data-disable]");
20             setTimeout(function () {self.$el.addClass('in');}, 0);
21             return this.load_xml_data().then(function () {
22                 self.flag = true;
23             });
24         },
25         active_select_tags: function () {
26             var uniqueID = 0;
27             var self = this;
28             var $selects = this.$('select:has(option[data-xmlid],option[data-enable],option[data-disable])');
29             $selects.each(function () {
30                 uniqueID++;
31                 var $select = $(this);
32                 var $options = $select.find('option[data-xmlid], option[data-enable], option[data-disable]');
33                 $options.each(function () {
34                     var $option = $(this);
35                     var $input = $('<input style="display: none;" type="radio" name="theme_customize_modal-select-'+uniqueID+'"/>');
36                     $input.attr('id', $option.attr('id'));
37                     $input.attr('data-xmlid', $option.data('xmlid'));
38                     $input.attr('data-enable', $option.data('enable'));
39                     $input.attr('data-disable', $option.data('disable'));
40                     $option.removeAttr('id');
41                     $option.data('input', $input);
42                     $input.on('update', function () {
43                         $option.attr('selected', $(this).prop("checked"));
44                     });
45                     self.$el.append($input);
46                 });
47                 $select.data("value", $options.first());
48                 $options.first().attr("selected", true);
49             });
50             $selects.change(function () {
51                 var $option = $(this).find('option:selected');
52                 $(this).data("value").data("input").prop("checked", true).change();
53                 $(this).data("value", $option);
54                 $option.data("input").change();
55             });
56         },
57         load_xml_data: function (xml_ids) {
58             var self = this;
59             $('#theme_error').remove();
60             return openerp.jsonRpc('/website/theme_customize_get', 'call', {
61                     'xml_ids': this.get_xml_ids(this.$inputs)
62                 }).done(function (data) {
63                     self.$inputs.filter('[data-xmlid]').each(function () {
64                         if (!_.difference(self.get_xml_ids($(this)), data[1]).length) {
65                             $(this).prop("checked", false).change();
66                         }
67                         if (!_.difference(self.get_xml_ids($(this)), data[0]).length) {
68                             $(this).prop("checked", true).change();
69                         }
70                     });
71                 }).fail(function (d, error) {
72                     $('body').prepend($('<div id="theme_error"/>').text(error.data.message));
73                 });
74         },
75         get_inputs: function (string) {
76             return this.$inputs.filter('#'+string.split(",").join(", #"));
77         },
78         get_xml_ids: function ($inputs) {
79             var xml_ids = [];
80             $inputs.each(function () {
81                 if ($(this).data('xmlid') && $(this).data('xmlid').length) {
82                     xml_ids = xml_ids.concat($(this).data('xmlid').split(","));
83                 }
84             });
85             return xml_ids;
86         },
87         compute_stylesheets: function () {
88             var self = this;
89             self.has_error = false;
90             $('link[href*=".assets_"]').attr('data-loading', true);
91             function theme_customize_css_onload() {
92                 if ($('link[data-loading]').size()) {
93                     $('body').toggleClass('theme_customize_css_loading');
94                     setTimeout(theme_customize_css_onload, 50);
95                 } else {
96                     $('body').removeClass('theme_customize_css_loading');
97                     self.$el.removeClass("loading");
98
99                     if (window.getComputedStyle($('button[data-toggle="collapse"]:first')[0]).getPropertyValue('position') === 'static' ||
100                         window.getComputedStyle($('#theme_customize_modal')[0]).getPropertyValue('display') === 'none') {
101                         if (self.has_error) {
102                             window.location.hash = "theme=true";
103                             window.location.reload();
104                         } else {
105                             self.has_error = true;
106                             $('link[href*=".assets_"][data-error]').removeAttr('data-error').attr('data-loading', true);
107                             self.update_stylesheets();
108                             setTimeout(theme_customize_css_onload, 50);
109                         }
110                     }
111                 }
112             }
113             theme_customize_css_onload();
114         },
115         update_stylesheets: function () {
116             $('link[href*=".assets_"]').each(function update () {
117                 var $style = $(this);
118                 var href = $style.attr("href").replace(/[^\/]+$/, new Date().getTime());
119                 var $asset = $('<link rel="stylesheet" href="'+href+'"/>');
120                 $asset.attr("onload", "$(this).prev().attr('disable', true).remove(); $(this).removeAttr('onload').removeAttr('onerror');");
121                 $asset.attr("onerror", "$(this).prev().removeAttr('data-loading').attr('data-error','loading'); $(this).attr('disable', true).remove();");
122                 $style.after($asset);
123             });
124         },
125         update_style: function (enable, disable, reload) {
126             var self = this;
127             if (this.$el.hasClass("loading")) return;
128             this.$el.addClass("loading");
129
130             if (!reload && $('link[href*=".assets_"]').size()) {
131                 this.compute_stylesheets();
132                 return openerp.jsonRpc('/website/theme_customize', 'call', {
133                         'enable': enable,
134                         'disable': disable
135                     }).then(function () {
136                         self.update_stylesheets();
137                     });
138             } else {
139                 var href = '/website/theme_customize_reload'+
140                     '?href='+encodeURIComponent(window.location.href)+
141                     '&enable='+encodeURIComponent(enable.join(","))+
142                     '&disable='+encodeURIComponent(disable.join(","));
143                 window.location.href = href;
144                 return $.Deferred();
145             }
146         },
147         enable_disable: function (data, enable) {
148             if (!data) return;
149             this.$('#'+data.split(",").join(", #")).each(function () {
150                 var check = $(this).prop("checked");
151                 var $label = $(this).closest("label");
152                 $(this).attr("checked", enable);
153                 if (enable) $label.addClass("checked");
154                 else $label.removeClass("checked");
155                 if (check != enable) {
156                     $(this).change();
157                 }
158             });
159         },
160         change_selection: function (event) {
161             var self = this;
162             if (this.$el.hasClass("loading")) return;
163
164             var $option = $(event.target),
165                 checked = $option.prop("checked");
166
167             if (checked) {
168                 this.enable_disable($option.data('enable'), true);
169                 this.enable_disable($option.data('disable'), false);
170                 $option.closest("label").addClass("checked");
171             } else {
172                 $option.closest("label").removeClass("checked");
173             }
174             $option.prop("checked", checked);
175
176             var $enable = this.$inputs.filter('[data-xmlid]:checked');
177             $enable.closest("label").addClass("checked");
178             var $disable = this.$inputs.filter('[data-xmlid]:not(:checked)');
179             $disable.closest("label").removeClass("checked");
180
181             var $sets = this.$inputs.filter('input[data-enable]:not([data-xmlid]), input[data-disable]:not([data-xmlid])');
182             $sets.each(function () {
183                 var $set = $(this);
184                 var checked = true;
185                 if ($set.data("enable")) {
186                     self.get_inputs($(this).data("enable")).each(function () {
187                         if (!$(this).prop("checked")) checked = false;
188                     });
189                 }
190                 if ($set.data("disable")) {
191                     self.get_inputs($(this).data("disable")).each(function () {
192                         if ($(this).prop("checked")) checked = false;
193                     });
194                 }
195                 if (checked) {
196                     $set.prop("checked", true).closest("label").addClass("checked");
197                 } else {
198                     $set.prop("checked", false).closest("label").removeClass("checked");
199                 }
200                 $set.trigger('update');
201             });
202
203             if (this.flag && $option.data('reload') && document.location.href.match(new RegExp( $option.data('reload') ))) {
204                 this.reload = true;
205             }
206
207             clearTimeout(this.timer);
208             if (this.flag) {
209                 this.timer = setTimeout(function () {
210                     self.update_style(self.get_xml_ids($enable), self.get_xml_ids($disable), self.reload);
211                     self.reload = false;
212                 },0);
213             } else {
214                 this.timer = setTimeout(function () { self.reload = false; },0);
215             }
216         },
217         close: function () {
218             var self = this;
219             $('#theme_error').remove();
220             $('link[href*=".assets_"]').removeAttr('data-loading');
221             this.$el.removeClass('in');
222             this.$el.addClass('out');
223             setTimeout(function () {self.destroy();}, 500);
224         }
225     });
226
227     function themeError(message) {
228         var _t = openerp._t;
229
230         if (message.indexOf('lessc')) {
231             message = '<span class="text-muted">' + message + "</span><br/><br/>" + _t("Please install or update node-less");
232         }
233
234         var $error = $( openerp.qweb.render('website.error_dialog', {
235             title: _t("Theme Error"),
236             message: message
237         }));
238         $error.appendTo("body").modal();
239         $error.on('hidden.bs.modal', function () {
240             $(this).remove();
241         });
242     }
243
244
245     openerp.website.ready().done(function() {
246         function theme_customize() {
247             var error = window.getComputedStyle(document.body, ':before').getPropertyValue('content');
248             if (error && error !== 'none') {
249                 return themeError(eval(error));
250             }
251             var Theme = openerp.website.Theme;
252             if (Theme.open && !Theme.open.isDestroyed()) return;
253             Theme.open = new Theme();
254             Theme.open.appendTo("body");
255         }
256         $(document).on('click', "#theme_customize a",theme_customize);
257         if ((window.location.hash || "").indexOf("theme=true") !== -1) {
258             theme_customize();
259             window.location.hash = "";
260         }
261     });
262
263 })();