[MERGE] forward port of branch 8.0 up to 591e329
[odoo/odoo.git] / addons / website / static / src / js / website.editor.js
index 57d18ca..27fe864 100644 (file)
@@ -3,6 +3,7 @@
 
     var website = openerp.website;
     var _t = openerp._t;
+    website.no_editor = !!$(document.documentElement).data('editable-no-editor');
 
     website.add_template_file('/website/static/src/xml/website.editor.xml');
     website.dom_ready.done(function () {
         if (!is_smartphone) {
             website.ready().then(website.init_editor);
         } else {
-            // remove padding of fake editor bar
-            document.body.style.padding = 0;
+            var resize_smartphone = function () {
+                is_smartphone = $(document.body)[0].clientWidth < 767;
+                if (!is_smartphone) {
+                    $(window).off("resize", resize_smartphone);
+                    website.init_editor();
+                }
+            };
+            $(window).on("resize", resize_smartphone);
         }
 
         $(document).on('click', 'a.js_link2post', function (ev) {
@@ -64,7 +71,7 @@
         return new website.editor.RTELinkDialog(editor).appendTo(document.body);
     }
     function image_dialog(editor, image) {
-        return new website.editor.RTEImageDialog(editor, image).appendTo(document.body);
+        return new website.editor.MediaDialog(editor, image).appendTo(document.body);
     }
 
     // only enable editors manually
     // elements are used for e.g. support of FontAwesome icons
     CKEDITOR.dtd.$removeEmpty = {};
 
+
     website.init_editor = function () {
         CKEDITOR.plugins.add('customdialogs', {
-//            requires: 'link,image',
+            // requires: 'link,image',
             init: function (editor) {
                 editor.on('doubleclick', function (evt) {
                     var element = evt.data.element;
-                    if (element.is('img') && is_editable_node(element)) {
+                    if ((element.is('img') || element.$.className.indexOf(' fa-') != -1) && is_editable_node(element)) {
                         image_dialog(editor, element);
                         return;
                     }
+                    var parent = new CKEDITOR.dom.element(element.$.parentNode);
+                    if (parent.$.className.indexOf('media_iframe_video') != -1 && is_editable_node(parent)) {
+                        image_dialog(editor, parent);
+                        return;
+                    }
 
                     element = get_selected_link(editor) || evt.data.element;
                     if (!(element.is('a') && is_editable_node(element))) {
                     context: 'a',
                 });
                 //noinspection JSValidateTypes
-                editor.addCommand('image', {
+                editor.addCommand('cimage', {
                     exec: function (editor) {
                         image_dialog(editor);
                         return true;
                 });
                 editor.ui.addButton('Image', {
                     label: 'Image',
-                    command: 'image',
+                    command: 'cimage',
                     toolbar: 'insert,10',
                 });
 
             }
         });
 
-        CKEDITOR.plugins.add('linkstyle', {
+        CKEDITOR.plugins.add('customColor', {
             requires: 'panelbutton,floatpanel',
             init: function (editor) {
-                var label = "Link Style";
-
-                editor.ui.add('LinkStyle', CKEDITOR.UI_PANELBUTTON, {
-                    label: label,
-                    title: label,
-                    icon: '/website/static/src/img/bglink.png',
-                    modes: { wysiwyg: true },
-                    editorFocus: true,
-                    context: 'a',
-                    panel: {
-                        css: '/web/static/lib/bootstrap/css/bootstrap.css',
-                        attributes: { 'role': 'listbox', 'aria-label': label },
-                    },
-
-                    types: {
-                        'btn-default': _t("Basic"),
-                        'btn-primary': _t("Primary"),
-                        'btn-success': _t("Success"),
-                        'btn-info': _t("Info"),
-                        'btn-warning': _t("Warning"),
-                        'btn-danger': _t("Danger"),
-                    },
-                    sizes: {
-                        'btn-xs': _t("Extra Small"),
-                        'btn-sm': _t("Small"),
-                        '': _t("Default"),
-                        'btn-lg': _t("Large")
-                    },
-
-                    onRender: function () {
-                        var self = this;
-                        editor.on('selectionChange', function (e) {
-                            var path = e.data.path, el;
-
-                            if (!(e = path.contains('a')) || e.isReadOnly()) {
-                                self.disable();
-                                return;
+                function create_button (buttonID, label) {
+                    var btnID = buttonID;
+                    editor.ui.add(buttonID, CKEDITOR.UI_PANELBUTTON, {
+                        label: label,
+                        title: label,
+                        modes: { wysiwyg: true },
+                        editorFocus: true,
+                        context: 'font',
+                        panel: {
+                            css: [  '/web/css/web.assets_common/' + (new Date().getTime()),
+                                    '/web/css/website.assets_frontend/' + (new Date().getTime()),
+                                    '/web/css/website.assets_editor/' + (new Date().getTime())],
+                            attributes: { 'role': 'listbox', 'aria-label': label },
+                        },
+                        enable: function () {
+                            this.setState(CKEDITOR.TRISTATE_OFF);
+                        },
+                        disable: function () {
+                            this.setState(CKEDITOR.TRISTATE_DISABLED);
+                        },
+                        onBlock: function (panel, block) {
+                            var self = this;
+                            var html = openerp.qweb.render('website.colorpicker');
+                            block.autoSize = true;
+                            block.element.setHtml( html );
+                            $(block.element.$).on('click', 'button', function () {
+                                self.clicked(this);
+                            });
+                            if (btnID === "TextColor") {
+                                $(".only-text", block.element.$).css("display", "block");
+                                $(".only-bg", block.element.$).css("display", "none");
+                            }
+                            var $body = $(block.element.$).parents("body");
+                            setTimeout(function () {
+                                $body.css('background-color', '#fff');
+                            }, 0);
+                        },
+                        getClasses: function () {
+                            var self = this;
+                            var classes = [];
+                            var id = this._.id;
+                            var block = this._.panel._.panel._.blocks[id];
+                            var $root = $(block.element.$);
+                            $root.find("button").map(function () {
+                                var color = self.getClass(this);
+                                if(color) classes.push( color );
+                            });
+                            return classes;
+                        },
+                        getClass: function (button) {
+                            var color = btnID === "BGColor" ? $(button).attr("class") : $(button).attr("class").replace(/^bg-/i, 'text-');
+                            return color.length && color;
+                        },
+                        clicked: function (button) {
+                            var className = this.getClass(button);
+                            var ancestor = editor.getSelection().getCommonAncestor();
+
+                            editor.focus();
+                            this._.panel.hide();
+                            editor.fire('saveSnapshot');
+
+                            // remove style
+                            var classes = [];
+                            var $ancestor = $(ancestor.$);
+                            var $fonts = $(ancestor.$).find('font');
+                            if (!ancestor.$.tagName) {
+                                $ancestor = $ancestor.parent();
+                            }
+                            if ($ancestor.is('font')) {
+                                $fonts = $fonts.add($ancestor[0]);
                             }
 
-                            self.enable();
-                        });
-                        // no hook where button is available, so wait
-                        // "some time" after render.
-                        setTimeout(function () {
-                            self.disable();
-                        }, 0)
-                    },
-                    enable: function () {
-                        this.setState(CKEDITOR.TRISTATE_OFF);
-                    },
-                    disable: function () {
-                        this.setState(CKEDITOR.TRISTATE_DISABLED);
-                    },
-
-                    onOpen: function () {
-                        var link = get_selected_link(editor);
-                        var id = this._.id;
-                        var block = this._.panel._.panel._.blocks[id];
-                        var $root = $(block.element.$);
-                        $root.find('button').removeClass('active').removeProp('disabled');
-
-                        // enable buttons matching link state
-                        for (var type in this.types) {
-                            if (!this.types.hasOwnProperty(type)) { continue; }
-                            if (!link.hasClass(type)) { continue; }
-
-                            $root.find('button[data-type=types].' + type)
-                                 .addClass('active');
-                        }
-                        var found;
-                        for (var size in this.sizes) {
-                            if (!this.sizes.hasOwnProperty(size)) { continue; }
-                            if (!size || !link.hasClass(size)) { continue; }
-                            found = true;
-                            $root.find('button[data-type=sizes].' + size)
-                                 .addClass('active');
-                        }
-                        if (!found && link.hasClass('btn')) {
-                            $root.find('button[data-type="sizes"][data-set-class=""]')
-                                 .addClass('active');
-                        }
-                    },
-
-                    onBlock: function (panel, block) {
-                        var self = this;
-                        block.autoSize = true;
+                            $fonts.filter("."+this.getClasses().join(",.")).map(function () {
+                                var className = $(this).attr("class");
+                                if (classes.indexOf(className) === -1) {
+                                    classes.push(className);
+                                }
+                            });
+                            for (var k in classes) {
+                                editor.removeStyle( new CKEDITOR.style({
+                                    element: 'font',
+                                    attributes: { 'class': classes[k] },
+                                }) );
+                            }
 
-                        var html = ['<div style="padding: 5px">'];
-                        html.push('<div style="white-space: nowrap">');
-                        _(this.types).each(function (label, key) {
-                            html.push(_.str.sprintf(
-                                '<button type="button" class="btn %s" ' +
-                                        'data-type="types" data-set-class="%s">%s</button>',
-                                key, key, label));
-                        });
-                        html.push('</div>');
-                        html.push('<div style="white-space: nowrap; margin: 5px 0; text-align: center">');
-                        _(this.sizes).each(function (label, key) {
-                            html.push(_.str.sprintf(
-                                '<button type="button" class="btn btn-default %s" ' +
-                                        'data-type="sizes" data-set-class="%s">%s</button>',
-                                key, key, label));
-                        });
-                        html.push('</div>');
-                        html.push('<button type="button" class="btn btn-link btn-block" ' +
-                                          'data-type="reset">Reset</button>');
-                        html.push('</div>');
-
-                        block.element.setHtml(html.join(' '));
-                        var $panel = $(block.element.$);
-                        $panel.on('click', 'button', function () {
-                            self.clicked(this);
-                        });
-                    },
-                    clicked: function (button) {
-                        editor.focus();
-                        editor.fire('saveSnapshot');
-
-                        var $button = $(button),
-                              $link = $(get_selected_link(editor).$);
-                        if (!$link.hasClass('btn')) {
-                            $link.addClass('btn btn-default');
-                        }
-                        switch($button.data('type')) {
-                        case 'reset':
-                            $link.removeClass('btn')
-                                 .removeClass(_.keys(this.types).join(' '))
-                                 .removeClass(_.keys(this.sizes).join(' '));
-                            break;
-                        case 'types':
-                            $link.removeClass(_.keys(this.types).join(' '))
-                                 .addClass($button.data('set-class'));
-                            break;
-                        case 'sizes':
-                            $link.removeClass(_.keys(this.sizes).join(' '))
-                                 .addClass($button.data('set-class'));
+                            // add new style
+                            if (className) {
+                                editor.applyStyle( new CKEDITOR.style({
+                                    element: 'font',
+                                    attributes: { 'class': className },
+                                }) );
+                            }
+                            editor.fire('saveSnapshot');
                         }
-                        this._.panel.hide();
-
-                        editor.fire('saveSnapshot');
-                    },
 
-                });
+                    });
+                }
+                create_button("BGColor", "Background Color");
+                create_button("TextColor", "Text Color");
             }
         });
 
 
                     init: function () {
                         this.on('edit', function () {
-                            new website.editor.FontIconsDialog(editor, this.element.$)
+                            new website.editor.MediaDialog(editor, this.element)
                                 .appendTo(document.body);
                         });
                     },
                 editor.edit();
             }
         });
+        website.editor_bar = editor;
     };
 
     /* ----- TOP EDITOR BAR FOR ADMIN ---- */
     website.EditorBar = openerp.Widget.extend({
         template: 'website.editorbar',
         events: {
-            'click button[data-action=edit]': 'edit',
             'click button[data-action=save]': 'save',
             'click a[data-action=cancel]': 'cancel',
         },
-        container: 'body',
-        customize_setup: function() {
-            var self = this;
-            var view_name = $(document.documentElement).data('view-xmlid');
-            if (!view_name) {
-                this.$('#customize-menu-button').addClass("hidden");
-            }
-            var menu = $('#customize-menu');
-            this.$('#customize-menu-button').click(function(event) {
-                menu.empty();
-                openerp.jsonRpc('/website/customize_template_get', 'call', { 'xml_id': view_name }).then(
-                    function(result) {
-                        _.each(result, function (item) {
-                            if (item.xml_id === "website.debugger" && !window.location.search.match(/[&?]debug(&|$)/)) return;
-                            if (item.header) {
-                                menu.append('<li class="dropdown-header">' + item.name + '</li>');
-                            } else {
-                                menu.append(_.str.sprintf('<li role="presentation"><a href="#" data-view-id="%s" role="menuitem"><strong class="fa fa%s-square-o"></strong> %s</a></li>',
-                                    item.id, item.active ? '-check' : '', item.name));
-                            }
-                        });
-                        // Adding Static Menus
-                        menu.append('<li class="divider"></li>');
-                        menu.append('<li><a data-action="ace" href="#">HTML Editor</a></li>');
-                        menu.append('<li class="js_change_theme"><a href="/page/website.themes">Change Theme</a></li>');
-                        menu.append('<li><a href="/web#return_label=Website&action=website.action_module_website">Install Apps</a></li>');
-                        self.trigger('rte:customize_menu_ready');
-                    }
-                );
-            });
-            menu.on('click', 'a[data-action!=ace]', function (event) {
-                var view_id = $(event.currentTarget).data('view-id');
-                openerp.jsonRpc('/website/customize_template_toggle', 'call', {
-                    'view_id': view_id
-                }).then( function() {
-                    window.location.reload();
-                });
-            });
-        },
         start: function() {
-            // remove placeholder editor bar
-            var fakebar = document.getElementById('website-top-navbar-placeholder');
-            if (fakebar) {
-                fakebar.parentNode.removeChild(fakebar);
-            }
-
             var self = this;
             this.saving_mutex = new openerp.Mutex();
 
+            this.$buttons = {
+                edit: this.$el.parents().find('button[data-action=edit]'),
+                save: this.$('button[data-action=save]'),
+                cancel: this.$('button[data-action=cancel]'),
+            };
+
             this.$('#website-top-edit').hide();
             this.$('#website-top-view').show();
 
+            var $edit_button = this.$buttons.edit
+                    .prop('disabled', website.no_editor);
+            if (website.no_editor) {
+                var help_text = $(document.documentElement).data('editable-no-editor');
+                $edit_button.parent()
+                    // help must be set on form above button because it does
+                    // not appear on disabled button
+                    .attr('title', help_text);
+            }
+
             $('.dropdown-toggle').dropdown();
-            this.customize_setup();
 
-            this.$buttons = {
-                edit: this.$('button[data-action=edit]'),
-                save: this.$('button[data-action=save]'),
-                cancel: this.$('button[data-action=cancel]'),
-            };
+            this.$buttons.edit.click(function(ev) {
+                self.edit();
+            });
 
             this.rte = new website.RTE(this);
             this.rte.on('change', this, this.proxy('rte_changed'));
             this.rte.on('rte:ready', this, function () {
                 self.setup_hover_buttons();
                 self.trigger('rte:ready');
-                self.check_height();
             });
 
-            $(window).on('resize', _.debounce(this.check_height.bind(this), 50));
-            this.check_height();
-
-            if (website.is_editable_button) {
-                this.$("button[data-action=edit]").removeClass("hidden");
-            }
-
-            return $.when(
-                this._super.apply(this, arguments),
-                this.rte.appendTo(this.$('#website-top-edit .nav.pull-right'))
-            ).then(function () {
-                self.check_height();
-            });
-        },
-        check_height: function () {
-            var editor_height = this.$el.outerHeight();
-            if (this.get('height') != editor_height) {
-                $(document.body).css('padding-top', editor_height);
-                this.set('height', editor_height);
-            }
+            this.rte.appendTo(this.$('#website-top-edit .nav.js_editor_placeholder'));
+            return this._super.apply(this, arguments);
+            
         },
         edit: function () {
             this.$buttons.edit.prop('disabled', true);
             this.$('#website-top-view').hide();
+            this.$el.show();
             this.$('#website-top-edit').show();
             $('.css_non_editable_mode_hidden').removeClass("css_non_editable_mode_hidden");
 
-            this.rte.start_edition().then(this.check_height.bind(this));
+            this.rte.start_edition();
             this.trigger('rte:called');
         },
         rte_changed: function () {
 
             observer.disconnect();
             var editor = this.rte.editor;
-            var root = editor.element.$;
-            editor.destroy();
+            var root = editor.element && editor.element.$;
+            try {
+                editor.destroy();
+            }
+            catch(err) {
+                // Hack to avoid the lost of all changes because ckeditor fails in destroy
+                console.log("Error in editor.destroy() : " + err.toString() + "\n  " + err.stack);
+            }
             // FIXME: select editables then filter by dirty?
             var defs = this.rte.fetch_editables(root)
                 .filter('.oe_dirty')
         },
 
         /**
-         * Creates a "hover" button for image and link edition
+         * Creates a "hover" button for link edition
+         *
+         * @param {Function} editfn edition function, called when clicking the button
+         * @returns {jQuery}
+         */
+        make_hover_button_link: function (editfn) {
+            return $(openerp.qweb.render('website.editor.hoverbutton.link', {}))
+                .hide()
+                .click(function (e) {
+                    e.preventDefault();
+                    e.stopPropagation();
+                    editfn.call(this, e);
+                })
+                .appendTo(document.body);
+        },
+
+        /**
+         * Creates a "hover" button for image
          *
-         * @param {String} label the button's label
          * @param {Function} editfn edition function, called when clicking the button
-         * @param {String} [classes] additional classes to set on the button
+         * @param {Function} stylefn edition style function, called when clicking the button
          * @returns {jQuery}
          */
-        make_hover_button: function (label, editfn, classes, styleButton) {
-            var $div = $(openerp.qweb.render('website.editor.hoverbutton', {
-                label: label,
-                classes: classes,
-                styleButton: styleButton
-            })).hide().appendTo(document.body);
-
-            $div.find("button.hover-edition-button").click(function (e) {
+        make_hover_button_image: function (editfn, stylefn) {
+            var $div = $(openerp.qweb.render('website.editor.hoverbutton.media', {}))
+                .hide()
+                .appendTo(document.body);
+
+            $div.find('[data-toggle="dropdown"]').dropdown();
+            $div.find(".hover-edition-button").click(function (e) {
                 e.preventDefault();
                 e.stopPropagation();
                 editfn.call(this, e);
             });
-            if (styleButton) {
-                $div.find("button.hover-style-button").click(function (e) {
+            if (stylefn) {
+                $div.find(".hover-style-button").click(function (e) {
                     e.preventDefault();
                     e.stopPropagation();
-                    styleButton.call(this, e);
+                    stylefn.call(this, e);
                 });
             }
             return $div;
          */
         setup_hover_buttons: function () {
             var editor = this.rte.editor;
-            var $link_button = this.make_hover_button(_t("Change"), function () {
+            var $link_button = this.make_hover_button_link(function () {
                 var sel = new CKEDITOR.dom.element(previous);
                 editor.getSelection().selectElement(sel);
                 if(sel.hasClass('fa')) {
-                    new website.editor.FontIconsDialog(editor, previous)
+                    new website.editor.MediaDialog(editor, previous)
                         .appendTo(document.body);
                 } else if (previous.tagName.toUpperCase() === 'A') {
                     link_dialog(editor);
                 }
                 $link_button.hide();
                 previous = null;
-            }, 'btn-xs');
-
-            var $image_button = this.make_hover_button(_t("Change"), function () {
-                image_dialog(editor, new CKEDITOR.dom.element(previous));
-                $image_button.hide();
-                previous = null;
-            }, 'btn-sm', function () {
-                var prev = previous;
-                var sel = new CKEDITOR.dom.element(prev);
-                var $sel = $(sel.$);
-                var $button = $(this);
-
-                if ($sel.data('transfo')) {
-                    $sel.transfo("destroy");
-                    $button.addClass("btn-primary").removeClass("btn-default");
-                } else {
-                    $sel.transfo();
-                    $sel.data('transfo').$markup
-                        .on("mouseover", function () {
-                            $sel.trigger("mouseover");
-                            $button.removeClass("btn-primary").addClass("btn-default");
-                            $image_button.show();
-                        });
-                    $sel.data('transfo').$markup.mouseover();
-                }
             });
 
             function is_icons_widget(element) {
             // -ish, because when moving to the button itself ``previous`` is
             // still set to the element having triggered showing the button.
             var previous;
-            $(editor.element.$).on('mouseover', 'a, img, .fa', function () {
+            $(editor.element.$).on('mouseover', 'a', function () {
                 // Back from edit button -> ignore
                 if (previous && previous === this) { return; }
 
                 previous = this;
                 var $selected = $(this);
                 var position = $selected.offset();
-                if ($selected.is('img')) {
-                    $link_button.hide();
-                    // center button on image
-                    $image_button.show().offset({
-                        top: $selected.outerHeight() / 2
-                                + position.top
-                                - $image_button.outerHeight() / 2,
-                        left: $selected.outerWidth() / 2
-                                + position.left
-                                - $image_button.outerWidth() / 2,
-                    });
-                } else {
-                    $image_button.hide();
-                    // put button below link, horizontally centered
-                    $link_button.show().offset({
-                        top: $selected.outerHeight()
-                                + position.top,
-                        left: $selected.outerWidth() / 2
-                                + position.left
-                                - $link_button.outerWidth() / 2
-                    })
-                }
-                
-                $image_button.find("button.hover-style-button").addClass("btn-primary").removeClass("btn-default");
+                $link_button.show().offset({
+                    top: $selected.outerHeight()
+                            + position.top,
+                    left: $selected.outerWidth() / 2
+                            + position.left
+                            - $link_button.outerWidth() / 2
+                })
             }).on('mouseleave', 'a, img, .fa', function (e) {
                 var current = document.elementFromPoint(e.clientX, e.clientY);
-                if (current === $link_button[0] || $(current).parent()[0] === $link_button[0] ||
-                    current === $image_button[0] || $(current).parent()[0] === $image_button[0]) {
+                if (current === $link_button[0] || $(current).parent()[0] === $link_button[0]) {
                     return;
                 }
-                $image_button.add($link_button).hide();
+                $link_button.hide();
                 previous = null;
             });
         }
     });
+    
+    website.EditorBarCustomize = openerp.Widget.extend({
+        events: {
+            'mousedown a.dropdown-toggle': 'load_menu',
+            'click ul a[data-view-id]': 'do_customize',
+        },
+        start: function() {
+            var self = this;
+            this.$menu = self.$el.find('ul');
+            this.view_name = $(document.documentElement).data('view-xmlid');
+            if (!this.view_name) {
+                this.$el.hide();
+            }
+            this.loaded = false;
+        },
+        load_menu: function () {
+            var self = this;
+            if(this.loaded) {
+                return;
+            }
+            openerp.jsonRpc('/website/customize_template_get', 'call', { 'xml_id': this.view_name }).then(
+                function(result) {
+                    _.each(result, function (item) {
+                        if (item.xml_id === "website.debugger" && !window.location.search.match(/[&?]debug(&|$)/)) return;
+                        if (item.header) {
+                            self.$menu.append('<li class="dropdown-header">' + item.name + '</li>');
+                        } else {
+                            self.$menu.append(_.str.sprintf('<li role="presentation"><a href="#" data-view-id="%s" role="menuitem"><strong class="fa fa%s-square-o"></strong> %s</a></li>',
+                                item.id, item.active ? '-check' : '', item.name));
+                        }
+                    });
+                    self.loaded = true;
+                }
+            );
+        },
+        do_customize: function (event) {
+            var view_id = $(event.currentTarget).data('view-id');
+            return openerp.jsonRpc('/web/dataset/call_kw', 'call', {
+                model: 'ir.ui.view',
+                method: 'toggle',
+                args: [],
+                kwargs: {
+                    ids: [parseInt(view_id, 10)],
+                    context: website.get_context()
+                }
+            }).then( function() {
+                window.location.reload();
+            });
+        },
+    });
+
+    $(document).ready(function() {
+        var editorBarCustomize = new website.EditorBarCustomize();
+        editorBarCustomize.setElement($('li[id=customize-menu]'));
+        editorBarCustomize.start();
+    });
 
     var blocks_selector = _.keys(CKEDITOR.dtd.$block).join(',');
     /* ----- RICH TEXT EDITOR ---- */
             var def = $.Deferred();
             var editor = this.editor = CKEDITOR.inline(root, self._config());
             editor.on('instanceReady', function () {
+                $("[data-oe-type=selection]").attr("contenteditable",false);
                 editor.setReadOnly(false);
                 // ckeditor set root to editable, disable it (only inner
                 // sections are editable)
 
         fetch_editables: function (root) {
             return $(root).find('[data-oe-model]')
+                .not('[data-oe-type = "selection"]')
                 .not('link, script')
-                .not('.oe_snippet_editor')
-                .filter(function () {
-                    var $this = $(this);
-                    // keep view sections and fields which are *not* in
-                    // view sections for top-level editables
-                    return $this.data('oe-model') === 'ir.ui.view'
-                       || !$this.closest('[data-oe-model = "ir.ui.view"]').length;
-                });
+                .not('.oe_snippet_editor');
         },
 
         _current_editor: function () {
                 fillEmptyBlocks: false,
                 filebrowserImageUploadUrl: "/website/attach",
                 // Support for sharedSpaces in 4.x
-                extraPlugins: 'sharedspace,customdialogs,tablebutton,oeref,linkstyle',
+                extraPlugins: 'customColor,sharedspace,customdialogs,tablebutton,oeref',
                 // Place toolbar in controlled location
                 sharedSpaces: { top: 'oe_rte_toolbar' },
                 toolbar: [{
                         "Superscript", "TextColor", "BGColor", "RemoveFormat"
                     ]},{
                     name: 'span', items: [
-                        "Link", "LinkStyle", "Blockquote", "BulletedList",
+                        "Link", "Blockquote", "BulletedList",
                         "NumberedList", "Indent", "Outdent"
                     ]},{
                     name: 'justify', items: [
                     {name: "Heading 5", element: 'h5'},
                     {name: "Heading 6", element: 'h6'},
                     {name: "Formatted", element: 'pre'},
-                    {name: "Address", element: 'address'}
+                    {name: "Address", element: 'address'},
                 ],
             };
         },
         },
         save: function () {
             this.close();
+            this.trigger("saved");
         },
         cancel: function () {
+            this.trigger("cancel");
         },
         close: function () {
             this.$el.modal('hide');
     website.editor.LinkDialog = website.editor.Dialog.extend({
         template: 'website.editor.dialog.link',
         events: _.extend({}, website.editor.Dialog.prototype.events, {
-            'change :input.url-source': function (e) { this.changed($(e.target)); },
+            'change :input.url-source': 'changed',
+            'keyup :input.url': 'onkeyup',
+            'keyup :input': 'preview',
             'mousedown': function (e) {
-                var $target = $(e.target).closest('.list-group-item');
+                var $target = $(e.target).closest('.list-group-item:has(.url-source)');
                 if (!$target.length || $target.hasClass('active')) {
                     // clicked outside groups, or clicked in active groups
                     return;
                 }
-
-                this.changed($target.find('.url-source').filter(':input'));
+                $target.find("input.url-source").change();
             },
             'click button.remove': 'remove_link',
             'change input#link-text': function (e) {
-                this.text = $(e.target).val()
+                this.text = $(e.target).val();
+            },
+            'change .link-style': function (e) {
+                this.preview();
             },
         }),
         init: function (editor) {
                         self.fetch_pages(q.term)
                     ).then(function (exists, results) {
                         var rs = _.map(results, function (r) {
-                            return { id: r.url, text: r.name, };
+                            return { id: r.loc, text: r.loc, };
                         });
                         if (!exists) {
                             rs.push({
             });
             return this._super().then(this.proxy('bind_data'));
         },
-        save: function () {
-            var self = this, _super = this._super.bind(this);
-            var $e = this.$('.list-group-item.active .url-source').filter(':input');
-            var val = $e.val();
-            if (!val || !$e[0].checkValidity()) {
+        get_data: function (test) {
+            var self = this,
+                def = new $.Deferred(),
+                $e = this.$('.active input.url-source').filter(':input'),
+                val = $e.val(),
+                label = this.$('#link-text').val() || val;
+
+            if (test !== false && (!val || !$e[0].checkValidity())) {
                 // FIXME: error message
                 $e.closest('.form-group').addClass('has-error');
                 $e.focus();
-                return;
+                def.reject();
             }
 
-            var done = $.when();
-            if ($e.hasClass('email-address')) {
-                this.make_link('mailto:' + val, false, val);
-            } else if ($e.hasClass('page')) {
+            var style = this.$("input[name='link-style-type']:checked").val();
+            var size = this.$("input[name='link-style-size']:checked").val();
+            var classes = (style && style.length ? "btn " : "") + style + " " + size;
+
+            if ($e.hasClass('email-address') && $e.val().indexOf("@") !== -1) {
+                def.resolve('mailto:' + val, false, label, classes);
+            } else if ($e.val() && $e.val().length && $e.hasClass('page')) {
                 var data = $e.select2('data');
                 if (!data.create) {
-                    self.make_link(data.id, false, data.text);
+                    def.resolve(data.id, false, label || data.text, classes);
                 } else {
                     // Create the page, get the URL back
-                    done = $.get(_.str.sprintf(
+                    $.get(_.str.sprintf(
                             '/website/add/%s?noredirect=1', encodeURI(data.id)))
                         .then(function (response) {
-                            self.make_link(response, false, data.id);
+                            def.resolve(response, false, data.id, classes);
                         });
                 }
             } else {
-                this.make_link(val, this.$('input.window-new').prop('checked'));
+                def.resolve(val, this.$('input.window-new').prop('checked'), label, classes);
             }
-            done.then(_super);
+            return def;
+        },
+        save: function () {
+            var self = this;
+            var _super = this._super.bind(this);
+            return this.get_data()
+                .then(function (url, new_window, label, classes) {
+                    self.make_link(url, new_window, label, classes);
+                }).then(_super);
         },
-        make_link: function (url, new_window, label) {
+        make_link: function (url, new_window, label, classes) {
         },
-        bind_data: function (text, href, new_window) {
-            href = href || this.element && (this.element.data( 'cke-saved-href')
+        bind_data: function () {
+            var self = this;
+            var href = this.element && (this.element.data( 'cke-saved-href')
                                     ||  this.element.getAttribute('href'));
-
-            if (new_window === undefined) {
-                new_window = this.element
+            var new_window = this.element
                         ? this.element.getAttribute('target') === '_blank'
                         : false;
-            }
-            if (text === undefined) {
-                text = this.element ? this.element.getText() : '';
+            var text = this.element ? this.element.getText() : '';
+            if (!text.length) {
+                if (this.editor) {
+                    text = this.editor.getSelection().getSelectedText();
+                } else {
+                    text = this.data.name;
+                    href = this.data.url;
+                    new_window = this.data.new_window;
+                }
             }
 
             this.$('input#link-text').val(text);
             this.$('input.window-new').prop('checked', new_window);
 
-            if (!href) { return; }
+            var classes = this.element && this.element.$.className;
+            if (classes) {
+                this.$('input[value!=""]').each(function () {
+                    var $option = $(this);
+                    if (classes.indexOf($option.val()) !== -1) {
+                        $option.attr("checked", "checked");
+                    }
+                });
+            }
+
             var match, $control;
-            if ((match = /mailto:(.+)/.exec(href))) {
-                $control = this.$('input.email-address').val(match[1]);
+            if (href && (match = /mailto:(.+)/.exec(href))) {
+                this.$('input.email-address').val(match[1]).change();
             }
-            if (!$control) {
-                $control = this.$('input.url').val(href);
+            if (href && !$control) {
+                this.page_exists(href).then(function (exist) {
+                    if (exist) {
+                        self.$('#link-page').select2('data', {'id': href, 'text': href});
+                    } else {
+                        self.$('input.url').val(href).change();
+                        self.$('input.window-new').closest("div").show();
+                    }
+                });
             }
-
-            this.changed($control);
+            this.preview();
         },
-        changed: function ($e) {
+        changed: function (e) {
+            var $e = $(e.target);
             this.$('.url-source').filter(':input').not($e).val('')
                     .filter(function () { return !!$(this).data('select2'); })
                     .select2('data', null);
                 .addClass('active')
                 .siblings().removeClass('active')
                 .addBack().removeClass('has-error');
+            this.preview();
         },
         call: function (method, args, kwargs) {
             var self = this;
                 context: website.get_context(),
             });
         },
+        onkeyup: function (e) {
+            var $e = $(e.target);
+            var is_link = ($e.val()||'').length && $e.val().indexOf("@") === -1;
+            this.$('input.window-new').closest("div").toggle(is_link);
+            this.preview();
+        },
+        preview: function () {
+            var $preview = this.$("#link-preview");
+            this.get_data(false).then(function (url, new_window, label, classes) {
+                $preview.attr("target", new_window ? '_blank' : "")
+                    .text((label && label.length ? label : url))
+                    .attr("class", classes);
+            });
+        }
     });
     website.editor.RTELinkDialog = website.editor.LinkDialog.extend({
         start: function () {
          * @param {Boolean} [new_window=false]
          * @param {String} [label=null]
          */
-        make_link: function (url, new_window, label) {
+        make_link: function (url, new_window, label, classes) {
             var attributes = {href: url, 'data-cke-saved-href': url};
             var to_remove = [];
             if (new_window) {
             } else {
                 to_remove.push('target');
             }
+            if (classes && classes.length) {
+                attributes['class'] = classes;
+            }
 
             if (this.element) {
                 this.element.setAttributes(attributes);
         },
     });
 
+    website.editor.Media = openerp.Widget.extend({
+        init: function (parent, editor, media) {
+            this._super();
+            this.parent = parent;
+            this.editor = editor;
+            this.media = media;
+        },
+        start: function () {
+            this.$preview = this.$('.preview-container').detach();
+            return this._super();
+        },
+        search: function (needle) {
+        },
+        save: function () {
+        },
+        clear: function () {
+        },
+        cancel: function () {
+        },
+        close: function () {
+        },
+    });
+    website.editor.MediaDialog = website.editor.Dialog.extend({
+        template: 'website.editor.dialog.media',
+        events : _.extend({}, website.editor.Dialog.prototype.events, {
+            'input input#icon-search': 'search',
+        }),
+
+        init: function (editor, media) {
+            this._super(editor);
+            this.editor = editor;
+            this.page = 0;
+            this.media = media;
+        },
+        start: function () {
+            var self = this;
+
+            if (this.editor.getSelection) {
+                var selection = this.editor.getSelection();
+                this.range = selection.getRanges(true)[0];
+            }
+
+            this.imageDialog = new website.editor.RTEImageDialog(this, this.editor, this.media);
+            this.imageDialog.appendTo(this.$("#editor-media-image"));
+            this.iconDialog = new website.editor.FontIconsDialog(this, this.editor, this.media);
+            this.iconDialog.appendTo(this.$("#editor-media-icon"));
+            this.videoDialog = new website.editor.VideoDialog(this, this.editor, this.media);
+            this.videoDialog.appendTo(this.$("#editor-media-video"));
+
+            this.active = this.imageDialog;
+
+            $('a[data-toggle="tab"]').on('shown.bs.tab', function (event) {
+                if ($(event.target).is('[href="#editor-media-image"]')) {
+                    self.active = self.imageDialog;
+                    self.$('li.search, li.previous, li.next').removeClass("hidden");
+                } else if ($(event.target).is('[href="#editor-media-icon"]')) {
+                    self.active = self.iconDialog;
+                    self.$('li.search, li.previous, li.next').removeClass("hidden");
+                    self.$('.nav-tabs li.previous, .nav-tabs li.next').addClass("hidden");
+                } else if ($(event.target).is('[href="#editor-media-video"]')) {
+                    self.active = self.videoDialog;
+                    self.$('.nav-tabs li.search').addClass("hidden");
+                }
+            });
+
+            if (this.media) {
+                if (this.media.$.nodeName === "IMG") {
+                    this.$('[href="#editor-media-image"]').tab('show');
+                } else if (this.media.$.className.match(/(^|\s)media_iframe_video($|\s)/)) {
+                    this.$('[href="#editor-media-video"]').tab('show');
+                } else if (this.media.$.className.match(/(^|\s)fa($|\s)/)) {
+                    this.$('[href="#editor-media-icon"]').tab('show');
+                }
+
+                if ($(this.media.$).parent().data("oe-field") === "image") {
+                    this.$('[href="#editor-media-video"], [href="#editor-media-icon"]').addClass('hidden');
+                }
+            }
+
+            return this._super();
+        },
+        save: function () {
+            var self = this;
+            if (self.media) {
+                this.media.$.innerHTML = "";
+                if (this.active !== this.imageDialog) {
+                    this.imageDialog.clear();
+                }
+                if (this.active !== this.iconDialog) {
+                    this.iconDialog.clear();
+                }
+                if (this.active !== this.videoDialog) {
+                    this.videoDialog.clear();
+                }
+            } else {
+                this.media = new CKEDITOR.dom.element("img");
+                self.range.insertNode(this.media);
+                self.range.selectNodeContents(this.media);
+                this.active.media = this.media;
+            }
+
+            var $el = $(self.active.media.$);
+
+            this.active.save();
+
+            this.media.$.className = this.media.$.className.replace(/\s+/g, ' ');
+
+            setTimeout(function () {
+                if(self.range) self.range.select();
+                $el.trigger("saved", self.active.media.$);
+                $(document.body).trigger("media-saved", [$el[0], self.active.media.$]);
+            },0);
+
+            this._super();
+        },
+        searchTimer: null,
+        search: function () {
+            var self = this;
+            var needle = this.$("input#icon-search").val();
+            clearTimeout(this.searchTimer);
+            this.searchTimer = setTimeout(function () {
+                self.active.search(needle || "");
+            },250);
+        }
+    });
+
     /**
      * ImageDialog widget. Lets users change an image, including uploading a
      * new image in OpenERP or selecting the image style (if supported by
      *                           selected by the users (or possibly the ones
      *                           originally passed in)
      */
-    website.editor.ImageDialog = website.editor.Dialog.extend({
+    var IMAGES_PER_ROW = 6;
+    var IMAGES_ROWS = 2;
+    website.editor.ImageDialog = website.editor.Media.extend({
         template: 'website.editor.dialog.image',
         events: _.extend({}, website.editor.Dialog.prototype.events, {
-            'change .url-source': function (e) { this.changed($(e.target)); },
+            'change .url-source': function (e) {
+                this.changed($(e.target));
+            },
             'click button.filepicker': function () {
-                this.$('input[type=file]').click();
+                var filepicker = this.$('input[type=file]');
+                if (!_.isEmpty(filepicker)){
+                    filepicker[0].click();
+                }
+            },
+            'click .js_disable_optimization': function () {
+                this.$('input[name="disable_optimization"]').val('1');
+                var filepicker = this.$('button.filepicker');
+                if (!_.isEmpty(filepicker)){
+                    filepicker[0].click();
+                }
             },
             'change input[type=file]': 'file_selection',
-            'change input.url': 'preview_image',
-            'click a[href=#existing]': 'browse_existing',
-            'change select.image-style': 'preview_image',
+            'submit form': 'form_submit',
+            'change input.url': "change_input",
+            'keyup input.url': "change_input",
+            //'change select.image-style': 'preview_image',
+            'click .existing-attachments img': 'select_existing',
+            'click .existing-attachment-remove': 'try_remove',
         }),
 
+        init: function (parent, editor, media) {
+            this.page = 0;
+            this._super(parent, editor, media);
+        },
         start: function () {
-            this.$('button.wait').text("Uploading…");
-            var $options = this.$('.image-style').children();
-            this.image_styles = $options.map(function () { return this.value; }).get();
+            var self = this;
+            var res = this._super();
 
-            var o = { url: null, style: null, };
+            var o = { url: null };
             // avoid typos, prevent addition of new properties to the object
             Object.preventExtensions(o);
             this.trigger('start', o);
 
-            if (o.url) {
-                if (o.style) {
-                    this.$('.image-style').val(o.style);
+            this.parent.$(".pager > li").click(function (e) {
+                e.preventDefault();
+                var $target = $(e.currentTarget);
+                if ($target.hasClass('disabled')) {
+                    return;
                 }
-                this.set_image(o.url);
-            }
+                self.page += $target.hasClass('previous') ? -1 : 1;
+                self.display_attachments();
+            });
 
-            return this._super();
+            this.set_image(o.url);
+
+            return res;
         },
         save: function () {
+            if (!this.link) {
+                this.link = this.$(".existing-attachments img:first").attr('src');
+            }
             this.trigger('save', {
-                url: this.$('input.url').val(),
-                style: this.$('.image-style').val(),
+                url: this.link
             });
+            this.media.renameNode("img");
+            $(this.media).attr('src', this.link);
             return this._super();
         },
+        clear: function () {
+            this.media.$.className = this.media.$.className.replace(/(^|\s)(img(\s|$)|img-[^\s]*)/g, ' ');
+        },
         cancel: function () {
             this.trigger('cancel');
         },
 
-        /**
-         * Sets the provided image url as the dialog's value-to-save and
-         * refreshes the preview element to use it.
-         */
-        set_image: function (url, error) {
-            this.$('input.url').val(
-                error ? '' : url);
-            this.$('input.url').val(url);
-            this.preview_image();
+        change_input: function (e) {
+            var $input = $(e.target);
+            var $button = $input.parent().find("button");
+            if ($input.val() === "") {
+                $button.addClass("btn-default").removeClass("btn-primary");
+            } else {
+                $button.removeClass("btn-default").addClass("btn-primary");
+            }
         },
 
-        file_selection: function () {
-            this.$el.addClass('nosave');
-            this.$('form').removeClass('has-error').find('.help-block').empty();
-            this.$('button.filepicker').removeClass('btn-danger btn-success');
+        search: function (needle) {
+            var self = this;
+            this.fetch_existing(needle).then(function () {
+                self.selected_existing(self.$('input.url').val());
+            });
+        },
 
+        set_image: function (url, error) {
             var self = this;
+            if (url) this.link = url;
+            this.$('input.url').val('');
+            this.fetch_existing().then(function () {
+                self.selected_existing(url);
+            });
+        },
+
+        form_submit: function (event) {
+            var self = this;
+            var $form = this.$('form[action="/website/attach"]');
+            if (!$form.find('input[name="upload"]').val().length) {
+                var url = $form.find('input[name="url"]').val();
+                if (this.selected_existing(url).size()) {
+                    event.preventDefault();
+                    return false;
+                }
+            }
             var callback = _.uniqueId('func_');
             this.$('input[name=func]').val(callback);
-
-            window[callback] = function (url, error) {
+            window[callback] = function (attachments, error) {
                 delete window[callback];
-                self.file_selected(url, error);
+                self.file_selected(attachments[0]['website_url'], error);
             };
+        },
+        file_selection: function () {
+            this.$el.addClass('nosave');
+            this.$('form').removeClass('has-error').find('.help-block').empty();
+            this.$('button.filepicker').removeClass('btn-danger btn-success');
             this.$('form').submit();
         },
         file_selected: function(url, error) {
                 $button.addClass('btn-danger');
             }
             this.set_image(url, error);
+            // auto save and close popup
+            this.parent.save();
         },
-        preview_image: function () {
-            var loaded = function () {
-                this.$el.removeClass('nosave');
-            }.bind(this);
-            var image = this.$('input.url').val();
-            if (!image) { loaded(); return; }
-
-            var $img = this.$('img.image-preview')
-                .attr('src', image)
-                .removeClass(this.image_styles.join(' '))
-                .addClass(this.$('select.image-style').val());
-
-            if ($img.prop('complete')) {
-                loaded();
-            } else {
-                $img.load(loaded)
-            }
-        },
-        browse_existing: function (e) {
-            e.preventDefault();
-            this.$('form').removeClass('has-error').find('.help-block').empty();
-            this.$('button.filepicker').removeClass('btn-danger btn-success');
-            new website.editor.ExistingImageDialog(this).appendTo(document.body);
-        },
-    });
-    website.editor.RTEImageDialog = website.editor.ImageDialog.extend({
-        init: function (editor, image) {
-            this._super(editor);
-
-            this.element = image;
-
-            this.on('start', this, this.proxy('started'));
-            this.on('save', this, this.proxy('saved'));
-        },
-        started: function (holder) {
-            if (!this.element) {
-                var selection = this.editor.getSelection();
-                this.element = selection && selection.getSelectedElement();
-            }
 
-            var el = this.element;
-            if (!el || !el.is('img')) {
-                return;
-            }
-            _(this.image_styles).each(function (style) {
-                if (el.hasClass(style)) {
-                    holder.style = style;
-                }
-            });
-            holder.url = el.getAttribute('src');
-        },
-        saved: function (data) {
-            var element, editor = this.editor;
-            if (!(element = this.element)) {
-                element = editor.document.createElement('img');
-                element.addClass('img');
-                element.addClass('img-responsive');
-                // focus event handler interactions between bootstrap (modal)
-                // and ckeditor (RTE) lead to blowing the stack in Safari and
-                // Chrome (but not FF) when this is done synchronously =>
-                // defer insertion so modal has been hidden & destroyed before
-                // it happens
-                setTimeout(function () {
-                    editor.insertElement(element);
-                }, 0);
+        fetch_existing: function (needle) {
+            var domain = [['res_model', '=', 'ir.ui.view'], '|',
+                        ['mimetype', '=', false], ['mimetype', '=like', 'image/%']];
+            if (needle && needle.length) {
+                domain.push('|', ['datas_fname', 'ilike', needle], ['name', 'ilike', needle]);
             }
-
-            var style = data.style;
-            element.setAttribute('src', data.url);
-            element.removeAttribute('data-cke-saved-src');
-            $(element.$).removeClass(this.image_styles.join(' '));
-            if (style) { element.addClass(style); }
-        },
-    });
-
-    var IMAGES_PER_ROW = 6;
-    var IMAGES_ROWS = 4;
-    website.editor.ExistingImageDialog = website.editor.Dialog.extend({
-        template: 'website.editor.dialog.image.existing',
-        events: _.extend({}, website.editor.Dialog.prototype.events, {
-            'click .existing-attachments img': 'select_existing',
-            'click .pager > li': function (e) {
-                e.preventDefault();
-                var $target = $(e.currentTarget);
-                if ($target.hasClass('disabled')) {
-                    return;
-                }
-                this.page += $target.hasClass('previous') ? -1 : 1;
-                this.display_attachments();
-            },
-            'click .existing-attachment-remove': 'try_remove',
-        }),
-        init: function (parent) {
-            this.image = null;
-            this.page = 0;
-            this.parent = parent;
-            this._super(parent.editor);
-        },
-
-        start: function () {
-            return $.when(
-                this._super(),
-                this.fetch_existing().then(this.proxy('fetched_existing')));
-        },
-
-        fetch_existing: function () {
             return openerp.jsonRpc('/web/dataset/call_kw', 'call', {
                 model: 'ir.attachment',
                 method: 'search_read',
                 args: [],
                 kwargs: {
                     fields: ['name', 'website_url'],
-                    domain: [['res_model', '=', 'ir.ui.view']],
+                    domain: domain,
                     order: 'id desc',
                     context: website.get_context(),
                 }
-            });
+            }).then(this.proxy('fetched_existing'));
         },
         fetched_existing: function (records) {
             this.records = records;
             this.$('.existing-attachments').replaceWith(
                 openerp.qweb.render(
                     'website.editor.dialog.image.existing.content', {rows: rows}));
-            this.$('.pager')
+            this.parent.$('.pager')
                 .find('li.previous').toggleClass('disabled', (from === 0)).end()
                 .find('li.next').toggleClass('disabled', (from + per_screen >= records.length));
-
         },
         select_existing: function (e) {
             var link = $(e.currentTarget).attr('src');
-            if (link) {
-                this.parent.set_image(link);
-            }
-            this.close()
+            this.link = link;
+            this.selected_existing(link);
+        },
+        selected_existing: function (link) {
+            this.$('.existing-attachment-cell.media_selected').removeClass("media_selected");
+            var $select = this.$('.existing-attachment-cell img').filter(function () {
+                return $(this).attr("src") == link;
+            }).first();
+            $select.parent().addClass("media_selected");
+            return $select;
         },
 
         try_remove: function (e) {
             var $help_block = this.$('.help-block').empty();
             var self = this;
-            var id = parseInt($(e.target).data('id'), 10);
+            var $a = $(e.target);
+            var id = parseInt($a.data('id'), 10);
             var attachment = _.findWhere(this.records, {id: id});
+            var $both = $a.parent().children();
+
+            $both.css({borderWidth: "5px", borderColor: "#f00"});
 
             return openerp.jsonRpc('/web/dataset/call_kw', 'call', {
                 model: 'ir.attachment',
                     self.display_attachments();
                     return;
                 }
+                $both.css({borderWidth: "", borderColor: ""});
                 $help_block.replaceWith(openerp.qweb.render(
                     'website.editor.dialog.image.existing.error', {
                         views: prevented[id]
         },
     });
 
+    website.editor.RTEImageDialog = website.editor.ImageDialog.extend({
+        init: function (parent, editor, media) {
+            this._super(parent, editor, media);
+
+            this.on('start', this, this.proxy('started'));
+            this.on('save', this, this.proxy('saved'));
+        },
+        started: function (holder) {
+            if (!this.media) {
+                var selection = this.editor.getSelection();
+                this.media = selection && selection.getSelectedElement();
+            }
+
+            var el = this.media;
+            if (!el || !el.is('img')) {
+                return;
+            }
+            holder.url = el.getAttribute('src');
+        },
+        saved: function (data) {
+            var element, editor = this.editor;
+            if (!(element = this.media)) {
+                element = editor.document.createElement('img');
+                element.addClass('img');
+                element.addClass('img-responsive');
+                // focus event handler interactions between bootstrap (modal)
+                // and ckeditor (RTE) lead to blowing the stack in Safari and
+                // Chrome (but not FF) when this is done synchronously =>
+                // defer insertion so modal has been hidden & destroyed before
+                // it happens
+                setTimeout(function () {
+                    editor.insertElement(element);
+                }, 0);
+            }
+
+            var style = data.style;
+            element.setAttribute('src', data.url);
+            element.removeAttribute('data-cke-saved-src');
+            if (style) { element.addClass(style); }
+        },
+    });
+
     function get_selected_link(editor) {
         var sel = editor.getSelection(),
             el = sel.getSelectedElement();
         range.shrink(CKEDITOR.SHRINK_TEXT);
         var commonAncestor = range.getCommonAncestor();
         var viewRoot = editor.elementPath(commonAncestor).contains(function (element) {
-            return element.data('oe-model') === 'ir.ui.view'
+            return element.data('oe-model') === 'ir.ui.view';
         });
         if (!viewRoot) { return null; }
         // if viewRoot is the first link, don't edit it.
                 .contains('a', true);
     }
 
-    website.editor.FontIconsDialog = website.editor.Dialog.extend({
+    website.editor.FontIconsDialog = website.editor.Media.extend({
         template: 'website.editor.dialog.font-icons',
         events : _.extend({}, website.editor.Dialog.prototype.events, {
             change: 'update_preview',
                 this.$('#fa-size').val(e.target.getAttribute('data-size'));
                 this.update_preview();
             },
-            'input input#icon-search': function () {
-                var needle = this.$('#icon-search').val();
-                var icons = this.icons;
-                if (needle) {
-                    icons = _(icons).filter(function (icon) {
-                        return icon.id.substring(3).indexOf(needle) !== -1;
-                    });
-                }
-
-                this.$('div.font-icons-icons').html(
-                    openerp.qweb.render(
-                        'website.editor.dialog.font-icons.icons',
-                        {icons: icons}));
-            },
         }),
 
         // List of FontAwesome icons in 4.0.3, extracted from the cheatsheet.
         // where we still need to implement ``initSelection``)
         // TODO: add id/name to the text in order to allow FAYT selection of icons?
         icons: [{"text": "\uf000", "id": "fa-glass"}, {"text": "\uf001", "id": "fa-music"}, {"text": "\uf002", "id": "fa-search"}, {"text": "\uf003", "id": "fa-envelope-o"}, {"text": "\uf004", "id": "fa-heart"}, {"text": "\uf005", "id": "fa-star"}, {"text": "\uf006", "id": "fa-star-o"}, {"text": "\uf007", "id": "fa-user"}, {"text": "\uf008", "id": "fa-film"}, {"text": "\uf009", "id": "fa-th-large"}, {"text": "\uf00a", "id": "fa-th"}, {"text": "\uf00b", "id": "fa-th-list"}, {"text": "\uf00c", "id": "fa-check"}, {"text": "\uf00d", "id": "fa-times"}, {"text": "\uf00e", "id": "fa-search-plus"}, {"text": "\uf010", "id": "fa-search-minus"}, {"text": "\uf011", "id": "fa-power-off"}, {"text": "\uf012", "id": "fa-signal"}, {"text": "\uf013", "id": "fa-cog"}, {"text": "\uf014", "id": "fa-trash-o"}, {"text": "\uf015", "id": "fa-home"}, {"text": "\uf016", "id": "fa-file-o"}, {"text": "\uf017", "id": "fa-clock-o"}, {"text": "\uf018", "id": "fa-road"}, {"text": "\uf019", "id": "fa-download"}, {"text": "\uf01a", "id": "fa-arrow-circle-o-down"}, {"text": "\uf01b", "id": "fa-arrow-circle-o-up"}, {"text": "\uf01c", "id": "fa-inbox"}, {"text": "\uf01d", "id": "fa-play-circle-o"}, {"text": "\uf01e", "id": "fa-repeat"}, {"text": "\uf021", "id": "fa-refresh"}, {"text": "\uf022", "id": "fa-list-alt"}, {"text": "\uf023", "id": "fa-lock"}, {"text": "\uf024", "id": "fa-flag"}, {"text": "\uf025", "id": "fa-headphones"}, {"text": "\uf026", "id": "fa-volume-off"}, {"text": "\uf027", "id": "fa-volume-down"}, {"text": "\uf028", "id": "fa-volume-up"}, {"text": "\uf029", "id": "fa-qrcode"}, {"text": "\uf02a", "id": "fa-barcode"}, {"text": "\uf02b", "id": "fa-tag"}, {"text": "\uf02c", "id": "fa-tags"}, {"text": "\uf02d", "id": "fa-book"}, {"text": "\uf02e", "id": "fa-bookmark"}, {"text": "\uf02f", "id": "fa-print"}, {"text": "\uf030", "id": "fa-camera"}, {"text": "\uf031", "id": "fa-font"}, {"text": "\uf032", "id": "fa-bold"}, {"text": "\uf033", "id": "fa-italic"}, {"text": "\uf034", "id": "fa-text-height"}, {"text": "\uf035", "id": "fa-text-width"}, {"text": "\uf036", "id": "fa-align-left"}, {"text": "\uf037", "id": "fa-align-center"}, {"text": "\uf038", "id": "fa-align-right"}, {"text": "\uf039", "id": "fa-align-justify"}, {"text": "\uf03a", "id": "fa-list"}, {"text": "\uf03b", "id": "fa-outdent"}, {"text": "\uf03c", "id": "fa-indent"}, {"text": "\uf03d", "id": "fa-video-camera"}, {"text": "\uf03e", "id": "fa-picture-o"}, {"text": "\uf040", "id": "fa-pencil"}, {"text": "\uf041", "id": "fa-map-marker"}, {"text": "\uf042", "id": "fa-adjust"}, {"text": "\uf043", "id": "fa-tint"}, {"text": "\uf044", "id": "fa-pencil-square-o"}, {"text": "\uf045", "id": "fa-share-square-o"}, {"text": "\uf046", "id": "fa-check-square-o"}, {"text": "\uf047", "id": "fa-arrows"}, {"text": "\uf048", "id": "fa-step-backward"}, {"text": "\uf049", "id": "fa-fast-backward"}, {"text": "\uf04a", "id": "fa-backward"}, {"text": "\uf04b", "id": "fa-play"}, {"text": "\uf04c", "id": "fa-pause"}, {"text": "\uf04d", "id": "fa-stop"}, {"text": "\uf04e", "id": "fa-forward"}, {"text": "\uf050", "id": "fa-fast-forward"}, {"text": "\uf051", "id": "fa-step-forward"}, {"text": "\uf052", "id": "fa-eject"}, {"text": "\uf053", "id": "fa-chevron-left"}, {"text": "\uf054", "id": "fa-chevron-right"}, {"text": "\uf055", "id": "fa-plus-circle"}, {"text": "\uf056", "id": "fa-minus-circle"}, {"text": "\uf057", "id": "fa-times-circle"}, {"text": "\uf058", "id": "fa-check-circle"}, {"text": "\uf059", "id": "fa-question-circle"}, {"text": "\uf05a", "id": "fa-info-circle"}, {"text": "\uf05b", "id": "fa-crosshairs"}, {"text": "\uf05c", "id": "fa-times-circle-o"}, {"text": "\uf05d", "id": "fa-check-circle-o"}, {"text": "\uf05e", "id": "fa-ban"}, {"text": "\uf060", "id": "fa-arrow-left"}, {"text": "\uf061", "id": "fa-arrow-right"}, {"text": "\uf062", "id": "fa-arrow-up"}, {"text": "\uf063", "id": "fa-arrow-down"}, {"text": "\uf064", "id": "fa-share"}, {"text": "\uf065", "id": "fa-expand"}, {"text": "\uf066", "id": "fa-compress"}, {"text": "\uf067", "id": "fa-plus"}, {"text": "\uf068", "id": "fa-minus"}, {"text": "\uf069", "id": "fa-asterisk"}, {"text": "\uf06a", "id": "fa-exclamation-circle"}, {"text": "\uf06b", "id": "fa-gift"}, {"text": "\uf06c", "id": "fa-leaf"}, {"text": "\uf06d", "id": "fa-fire"}, {"text": "\uf06e", "id": "fa-eye"}, {"text": "\uf070", "id": "fa-eye-slash"}, {"text": "\uf071", "id": "fa-exclamation-triangle"}, {"text": "\uf072", "id": "fa-plane"}, {"text": "\uf073", "id": "fa-calendar"}, {"text": "\uf074", "id": "fa-random"}, {"text": "\uf075", "id": "fa-comment"}, {"text": "\uf076", "id": "fa-magnet"}, {"text": "\uf077", "id": "fa-chevron-up"}, {"text": "\uf078", "id": "fa-chevron-down"}, {"text": "\uf079", "id": "fa-retweet"}, {"text": "\uf07a", "id": "fa-shopping-cart"}, {"text": "\uf07b", "id": "fa-folder"}, {"text": "\uf07c", "id": "fa-folder-open"}, {"text": "\uf07d", "id": "fa-arrows-v"}, {"text": "\uf07e", "id": "fa-arrows-h"}, {"text": "\uf080", "id": "fa-bar-chart-o"}, {"text": "\uf081", "id": "fa-twitter-square"}, {"text": "\uf082", "id": "fa-facebook-square"}, {"text": "\uf083", "id": "fa-camera-retro"}, {"text": "\uf084", "id": "fa-key"}, {"text": "\uf085", "id": "fa-cogs"}, {"text": "\uf086", "id": "fa-comments"}, {"text": "\uf087", "id": "fa-thumbs-o-up"}, {"text": "\uf088", "id": "fa-thumbs-o-down"}, {"text": "\uf089", "id": "fa-star-half"}, {"text": "\uf08a", "id": "fa-heart-o"}, {"text": "\uf08b", "id": "fa-sign-out"}, {"text": "\uf08c", "id": "fa-linkedin-square"}, {"text": "\uf08d", "id": "fa-thumb-tack"}, {"text": "\uf08e", "id": "fa-external-link"}, {"text": "\uf090", "id": "fa-sign-in"}, {"text": "\uf091", "id": "fa-trophy"}, {"text": "\uf092", "id": "fa-github-square"}, {"text": "\uf093", "id": "fa-upload"}, {"text": "\uf094", "id": "fa-lemon-o"}, {"text": "\uf095", "id": "fa-phone"}, {"text": "\uf096", "id": "fa-square-o"}, {"text": "\uf097", "id": "fa-bookmark-o"}, {"text": "\uf098", "id": "fa-phone-square"}, {"text": "\uf099", "id": "fa-twitter"}, {"text": "\uf09a", "id": "fa-facebook"}, {"text": "\uf09b", "id": "fa-github"}, {"text": "\uf09c", "id": "fa-unlock"}, {"text": "\uf09d", "id": "fa-credit-card"}, {"text": "\uf09e", "id": "fa-rss"}, {"text": "\uf0a0", "id": "fa-hdd-o"}, {"text": "\uf0a1", "id": "fa-bullhorn"}, {"text": "\uf0f3", "id": "fa-bell"}, {"text": "\uf0a3", "id": "fa-certificate"}, {"text": "\uf0a4", "id": "fa-hand-o-right"}, {"text": "\uf0a5", "id": "fa-hand-o-left"}, {"text": "\uf0a6", "id": "fa-hand-o-up"}, {"text": "\uf0a7", "id": "fa-hand-o-down"}, {"text": "\uf0a8", "id": "fa-arrow-circle-left"}, {"text": "\uf0a9", "id": "fa-arrow-circle-right"}, {"text": "\uf0aa", "id": "fa-arrow-circle-up"}, {"text": "\uf0ab", "id": "fa-arrow-circle-down"}, {"text": "\uf0ac", "id": "fa-globe"}, {"text": "\uf0ad", "id": "fa-wrench"}, {"text": "\uf0ae", "id": "fa-tasks"}, {"text": "\uf0b0", "id": "fa-filter"}, {"text": "\uf0b1", "id": "fa-briefcase"}, {"text": "\uf0b2", "id": "fa-arrows-alt"}, {"text": "\uf0c0", "id": "fa-users"}, {"text": "\uf0c1", "id": "fa-link"}, {"text": "\uf0c2", "id": "fa-cloud"}, {"text": "\uf0c3", "id": "fa-flask"}, {"text": "\uf0c4", "id": "fa-scissors"}, {"text": "\uf0c5", "id": "fa-files-o"}, {"text": "\uf0c6", "id": "fa-paperclip"}, {"text": "\uf0c7", "id": "fa-floppy-o"}, {"text": "\uf0c8", "id": "fa-square"}, {"text": "\uf0c9", "id": "fa-bars"}, {"text": "\uf0ca", "id": "fa-list-ul"}, {"text": "\uf0cb", "id": "fa-list-ol"}, {"text": "\uf0cc", "id": "fa-strikethrough"}, {"text": "\uf0cd", "id": "fa-underline"}, {"text": "\uf0ce", "id": "fa-table"}, {"text": "\uf0d0", "id": "fa-magic"}, {"text": "\uf0d1", "id": "fa-truck"}, {"text": "\uf0d2", "id": "fa-pinterest"}, {"text": "\uf0d3", "id": "fa-pinterest-square"}, {"text": "\uf0d4", "id": "fa-google-plus-square"}, {"text": "\uf0d5", "id": "fa-google-plus"}, {"text": "\uf0d6", "id": "fa-money"}, {"text": "\uf0d7", "id": "fa-caret-down"}, {"text": "\uf0d8", "id": "fa-caret-up"}, {"text": "\uf0d9", "id": "fa-caret-left"}, {"text": "\uf0da", "id": "fa-caret-right"}, {"text": "\uf0db", "id": "fa-columns"}, {"text": "\uf0dc", "id": "fa-sort"}, {"text": "\uf0dd", "id": "fa-sort-asc"}, {"text": "\uf0de", "id": "fa-sort-desc"}, {"text": "\uf0e0", "id": "fa-envelope"}, {"text": "\uf0e1", "id": "fa-linkedin"}, {"text": "\uf0e2", "id": "fa-undo"}, {"text": "\uf0e3", "id": "fa-gavel"}, {"text": "\uf0e4", "id": "fa-tachometer"}, {"text": "\uf0e5", "id": "fa-comment-o"}, {"text": "\uf0e6", "id": "fa-comments-o"}, {"text": "\uf0e7", "id": "fa-bolt"}, {"text": "\uf0e8", "id": "fa-sitemap"}, {"text": "\uf0e9", "id": "fa-umbrella"}, {"text": "\uf0ea", "id": "fa-clipboard"}, {"text": "\uf0eb", "id": "fa-lightbulb-o"}, {"text": "\uf0ec", "id": "fa-exchange"}, {"text": "\uf0ed", "id": "fa-cloud-download"}, {"text": "\uf0ee", "id": "fa-cloud-upload"}, {"text": "\uf0f0", "id": "fa-user-md"}, {"text": "\uf0f1", "id": "fa-stethoscope"}, {"text": "\uf0f2", "id": "fa-suitcase"}, {"text": "\uf0a2", "id": "fa-bell-o"}, {"text": "\uf0f4", "id": "fa-coffee"}, {"text": "\uf0f5", "id": "fa-cutlery"}, {"text": "\uf0f6", "id": "fa-file-text-o"}, {"text": "\uf0f7", "id": "fa-building-o"}, {"text": "\uf0f8", "id": "fa-hospital-o"}, {"text": "\uf0f9", "id": "fa-ambulance"}, {"text": "\uf0fa", "id": "fa-medkit"}, {"text": "\uf0fb", "id": "fa-fighter-jet"}, {"text": "\uf0fc", "id": "fa-beer"}, {"text": "\uf0fd", "id": "fa-h-square"}, {"text": "\uf0fe", "id": "fa-plus-square"}, {"text": "\uf100", "id": "fa-angle-double-left"}, {"text": "\uf101", "id": "fa-angle-double-right"}, {"text": "\uf102", "id": "fa-angle-double-up"}, {"text": "\uf103", "id": "fa-angle-double-down"}, {"text": "\uf104", "id": "fa-angle-left"}, {"text": "\uf105", "id": "fa-angle-right"}, {"text": "\uf106", "id": "fa-angle-up"}, {"text": "\uf107", "id": "fa-angle-down"}, {"text": "\uf108", "id": "fa-desktop"}, {"text": "\uf109", "id": "fa-laptop"}, {"text": "\uf10a", "id": "fa-tablet"}, {"text": "\uf10b", "id": "fa-mobile"}, {"text": "\uf10c", "id": "fa-circle-o"}, {"text": "\uf10d", "id": "fa-quote-left"}, {"text": "\uf10e", "id": "fa-quote-right"}, {"text": "\uf110", "id": "fa-spinner"}, {"text": "\uf111", "id": "fa-circle"}, {"text": "\uf112", "id": "fa-reply"}, {"text": "\uf113", "id": "fa-github-alt"}, {"text": "\uf114", "id": "fa-folder-o"}, {"text": "\uf115", "id": "fa-folder-open-o"}, {"text": "\uf118", "id": "fa-smile-o"}, {"text": "\uf119", "id": "fa-frown-o"}, {"text": "\uf11a", "id": "fa-meh-o"}, {"text": "\uf11b", "id": "fa-gamepad"}, {"text": "\uf11c", "id": "fa-keyboard-o"}, {"text": "\uf11d", "id": "fa-flag-o"}, {"text": "\uf11e", "id": "fa-flag-checkered"}, {"text": "\uf120", "id": "fa-terminal"}, {"text": "\uf121", "id": "fa-code"}, {"text": "\uf122", "id": "fa-reply-all"}, {"text": "\uf122", "id": "fa-mail-reply-all"}, {"text": "\uf123", "id": "fa-star-half-o"}, {"text": "\uf124", "id": "fa-location-arrow"}, {"text": "\uf125", "id": "fa-crop"}, {"text": "\uf126", "id": "fa-code-fork"}, {"text": "\uf127", "id": "fa-chain-broken"}, {"text": "\uf128", "id": "fa-question"}, {"text": "\uf129", "id": "fa-info"}, {"text": "\uf12a", "id": "fa-exclamation"}, {"text": "\uf12b", "id": "fa-superscript"}, {"text": "\uf12c", "id": "fa-subscript"}, {"text": "\uf12d", "id": "fa-eraser"}, {"text": "\uf12e", "id": "fa-puzzle-piece"}, {"text": "\uf130", "id": "fa-microphone"}, {"text": "\uf131", "id": "fa-microphone-slash"}, {"text": "\uf132", "id": "fa-shield"}, {"text": "\uf133", "id": "fa-calendar-o"}, {"text": "\uf134", "id": "fa-fire-extinguisher"}, {"text": "\uf135", "id": "fa-rocket"}, {"text": "\uf136", "id": "fa-maxcdn"}, {"text": "\uf137", "id": "fa-chevron-circle-left"}, {"text": "\uf138", "id": "fa-chevron-circle-right"}, {"text": "\uf139", "id": "fa-chevron-circle-up"}, {"text": "\uf13a", "id": "fa-chevron-circle-down"}, {"text": "\uf13b", "id": "fa-html5"}, {"text": "\uf13c", "id": "fa-css3"}, {"text": "\uf13d", "id": "fa-anchor"}, {"text": "\uf13e", "id": "fa-unlock-alt"}, {"text": "\uf140", "id": "fa-bullseye"}, {"text": "\uf141", "id": "fa-ellipsis-h"}, {"text": "\uf142", "id": "fa-ellipsis-v"}, {"text": "\uf143", "id": "fa-rss-square"}, {"text": "\uf144", "id": "fa-play-circle"}, {"text": "\uf145", "id": "fa-ticket"}, {"text": "\uf146", "id": "fa-minus-square"}, {"text": "\uf147", "id": "fa-minus-square-o"}, {"text": "\uf148", "id": "fa-level-up"}, {"text": "\uf149", "id": "fa-level-down"}, {"text": "\uf14a", "id": "fa-check-square"}, {"text": "\uf14b", "id": "fa-pencil-square"}, {"text": "\uf14c", "id": "fa-external-link-square"}, {"text": "\uf14d", "id": "fa-share-square"}, {"text": "\uf14e", "id": "fa-compass"}, {"text": "\uf150", "id": "fa-caret-square-o-down"}, {"text": "\uf151", "id": "fa-caret-square-o-up"}, {"text": "\uf152", "id": "fa-caret-square-o-right"}, {"text": "\uf153", "id": "fa-eur"}, {"text": "\uf154", "id": "fa-gbp"}, {"text": "\uf155", "id": "fa-usd"}, {"text": "\uf156", "id": "fa-inr"}, {"text": "\uf157", "id": "fa-jpy"}, {"text": "\uf158", "id": "fa-rub"}, {"text": "\uf159", "id": "fa-krw"}, {"text": "\uf15a", "id": "fa-btc"}, {"text": "\uf15b", "id": "fa-file"}, {"text": "\uf15c", "id": "fa-file-text"}, {"text": "\uf15d", "id": "fa-sort-alpha-asc"}, {"text": "\uf15e", "id": "fa-sort-alpha-desc"}, {"text": "\uf160", "id": "fa-sort-amount-asc"}, {"text": "\uf161", "id": "fa-sort-amount-desc"}, {"text": "\uf162", "id": "fa-sort-numeric-asc"}, {"text": "\uf163", "id": "fa-sort-numeric-desc"}, {"text": "\uf164", "id": "fa-thumbs-up"}, {"text": "\uf165", "id": "fa-thumbs-down"}, {"text": "\uf166", "id": "fa-youtube-square"}, {"text": "\uf167", "id": "fa-youtube"}, {"text": "\uf168", "id": "fa-xing"}, {"text": "\uf169", "id": "fa-xing-square"}, {"text": "\uf16a", "id": "fa-youtube-play"}, {"text": "\uf16b", "id": "fa-dropbox"}, {"text": "\uf16c", "id": "fa-stack-overflow"}, {"text": "\uf16d", "id": "fa-instagram"}, {"text": "\uf16e", "id": "fa-flickr"}, {"text": "\uf170", "id": "fa-adn"}, {"text": "\uf171", "id": "fa-bitbucket"}, {"text": "\uf172", "id": "fa-bitbucket-square"}, {"text": "\uf173", "id": "fa-tumblr"}, {"text": "\uf174", "id": "fa-tumblr-square"}, {"text": "\uf175", "id": "fa-long-arrow-down"}, {"text": "\uf176", "id": "fa-long-arrow-up"}, {"text": "\uf177", "id": "fa-long-arrow-left"}, {"text": "\uf178", "id": "fa-long-arrow-right"}, {"text": "\uf179", "id": "fa-apple"}, {"text": "\uf17a", "id": "fa-windows"}, {"text": "\uf17b", "id": "fa-android"}, {"text": "\uf17c", "id": "fa-linux"}, {"text": "\uf17d", "id": "fa-dribbble"}, {"text": "\uf17e", "id": "fa-skype"}, {"text": "\uf180", "id": "fa-foursquare"}, {"text": "\uf181", "id": "fa-trello"}, {"text": "\uf182", "id": "fa-female"}, {"text": "\uf183", "id": "fa-male"}, {"text": "\uf184", "id": "fa-gittip"}, {"text": "\uf185", "id": "fa-sun-o"}, {"text": "\uf186", "id": "fa-moon-o"}, {"text": "\uf187", "id": "fa-archive"}, {"text": "\uf188", "id": "fa-bug"}, {"text": "\uf189", "id": "fa-vk"}, {"text": "\uf18a", "id": "fa-weibo"}, {"text": "\uf18b", "id": "fa-renren"}, {"text": "\uf18c", "id": "fa-pagelines"}, {"text": "\uf18d", "id": "fa-stack-exchange"}, {"text": "\uf18e", "id": "fa-arrow-circle-o-right"}, {"text": "\uf190", "id": "fa-arrow-circle-o-left"}, {"text": "\uf191", "id": "fa-caret-square-o-left"}, {"text": "\uf192", "id": "fa-dot-circle-o"}, {"text": "\uf193", "id": "fa-wheelchair"}, {"text": "\uf194", "id": "fa-vimeo-square"}, {"text": "\uf195", "id": "fa-try"}, {"text": "\uf196", "id": "fa-plus-square-o"}],
-        init: function (editor, element) {
-            this._super(editor);
-            this.element = element;
-        },
         /**
          * Initializes select2: in Chrome and Safari, <select> font apparently
          * isn't customizable (?) and the fontawesome glyphs fail to appear.
         start: function () {
             return this._super().then(this.proxy('load_data'));
         },
+        search: function (needle) {
+            var icons = this.icons;
+            if (needle) {
+                icons = _(icons).filter(function (icon) {
+                    return icon.id.substring(3).indexOf(needle) !== -1;
+                });
+            }
+
+            this.$('div.font-icons-icons').html(
+                openerp.qweb.render(
+                    'website.editor.dialog.font-icons.icons',
+                    {icons: icons}));
+        },
         /**
          * Removes existing FontAwesome classes on the bound element, and sets
          * all the new ones if necessary.
          */
         save: function () {
-            var classes = this.element.className.split(/\s+/);
+            var style = this.media.$.attributes.style ? this.media.$.attributes.style.textContent : '';
+            var classes = (this.media.$.className||"").split(/\s+/);
             var non_fa_classes = _.reject(classes, function (cls) {
                 return cls === 'fa' || /^fa-/.test(cls);
             });
             var final_classes = non_fa_classes.concat(this.get_fa_classes());
-            this.element.className = final_classes.join(' ');
+            this.media.$.className = final_classes.join(' ');
+            this.media.renameNode("span");
+            this.media.$.attributes.style.textContent = style;
             this._super();
         },
         /**
          * which may not match the visual look of the element.
          */
         load_data: function () {
-            var classes = this.element.className.split(/\s+/);
+            var classes = (this.media&&this.media.$.className||"").split(/\s+/);
             for (var i = 0; i < classes.length; i++) {
                 var cls = classes[i];
                 switch(cls) {
             ];
         },
         update_preview: function () {
+            this.$preview.empty();
             var $preview = this.$('#fa-preview').empty();
+
             var sizes = ['', 'fa-2x', 'fa-3x', 'fa-4x', 'fa-5x'];
             var classes = this.get_fa_classes();
             var no_sizes = _.difference(classes, sizes).join(' ');
                         .attr('data-size', size)
                         .addClass(size)
                         .addClass(no_sizes);
-                if ((size && _.contains(classes, size)) || (!size && !selected)) {
+
+                if ((size && _.contains(classes, size)) || (size === "" && !selected)) {
+                    this.$preview.append($p.clone());
+                    this.$('#fa-size').val(size);
                     $p.addClass('font-icons-selected');
                     selected = true;
                 }
                 $preview.prepend($p);
             }
-        }
+        },
+        clear: function () {
+            this.media.$.className = this.media.$.className.replace(/(^|\s)(fa(\s|$)|fa-[^\s]*)/g, ' ');
+        },
+    });
+
+    website.editor.VideoDialog = website.editor.Media.extend({
+        template: 'website.editor.dialog.video',
+        events : _.extend({}, website.editor.Dialog.prototype.events, {
+            'click input#urlvideo ~ button': 'get_video',
+            'click input#embedvideo ~ button': 'get_embed_video',
+            'change input#urlvideo': 'change_input',
+            'keyup input#urlvideo': 'change_input',
+            'change input#embedvideo': 'change_input',
+            'keyup input#embedvideo': 'change_input'
+        }),
+        start: function () {
+            this.$iframe = this.$("iframe");
+            var $media = $(this.media && this.media.$);
+            if ($media.hasClass("media_iframe_video")) {
+                var src = $media.data('src');
+                this.$("input#urlvideo").val(src);
+                this.$("#autoplay").attr("checked", src.indexOf('autoplay=1') != -1);
+                this.get_video();
+            }
+            return this._super();
+        },
+        change_input: function (e) {
+            var $input = $(e.target);
+            var $button = $input.parent().find("button");
+            if ($input.val() === "") {
+                $button.addClass("btn-default").removeClass("btn-primary");
+            } else {
+                $button.removeClass("btn-default").addClass("btn-primary");
+            }
+        },
+        get_url: function () {
+            var video_id = this.$("#video_id").val();
+            var video_type = this.$("#video_type").val();
+            switch (video_type) {
+                case "youtube":
+                    return "//www.youtube.com/embed/" + video_id + "?autoplay=" + (this.$("#autoplay").is(":checked") ? 1 : 0);
+                case "vimeo":
+                    return "//player.vimeo.com/video/" + video_id + "?autoplay=" + (this.$("#autoplay").is(":checked") ? 1 : 0);
+                case "dailymotion":
+                    return "//www.dailymotion.com/embed/video/" + video_id + "?autoplay=" + (this.$("#autoplay").is(":checked") ? 1 : 0);
+                default:
+                    return video_id;
+            }
+        },
+        get_embed_video: function (event) {
+            event.preventDefault();
+            var embedvideo = this.$("input#embedvideo").val().match(/src=["']?([^"']+)["' ]?/);
+            if (embedvideo) {
+                this.$("input#urlvideo").val(embedvideo[1]);
+                this.get_video(event);
+            }
+            return false;
+        },
+        get_video: function (event) {
+            if (event) event.preventDefault();
+            var needle = this.$("input#urlvideo").val();
+            var video_id;
+            var video_type;
+
+            if (needle.indexOf(".youtube.") != -1) {
+                video_type = "youtube";
+                video_id = needle.match(/\.youtube\.[a-z]+\/(embed\/|watch\?v=)?([^\/?&]+)/i)[2];
+            } else if (needle.indexOf("//youtu.") != -1) {
+                video_type = "youtube";
+                video_id = needle.match(/youtube\.[a-z]+\/([^\/?&]+)/i)[1];
+            } else if (needle.indexOf("player.vimeo.") != -1 || needle.indexOf("//vimeo.") != -1) {
+                video_type = "vimeo";
+                video_id = needle.match(/vimeo\.[a-z]+\/(video\/)?([^?&]+)/i)[2];
+            } else if (needle.indexOf(".dailymotion.") != -1) {
+                video_type = "dailymotion";
+                video_id = needle.match(/dailymotion\.[a-z]+\/(embed\/)?(video\/)?([^\/?&]+)/i)[3];
+            } else {
+                video_type = "";
+                video_id = needle;
+            }
+
+            this.$("#video_id").val(video_id);
+            this.$("#video_type").val(video_type);
+
+            this.$iframe.attr("src", this.get_url());
+            return false;
+        },
+        save: function () {
+            var video_id = this.$("#video_id").val();
+            if (!video_id) {
+                this.$("button.btn-primary").click();
+                video_id = this.$("#video_id").val();
+            }
+            var video_type = this.$("#video_type").val();
+            var style = this.media.$.attributes.style ? this.media.$.attributes.style.textContent : '';
+            var $iframe = $(
+                '<div class="media_iframe_video" data-src="'+this.get_url()+'" style="'+style+'">'+
+                    '<div class="css_editable_mode_display">&nbsp;</div>'+
+                    '<iframe src="'+this.get_url()+'" frameborder="0" allowfullscreen="allowfullscreen"></iframe>'+
+                '</div>');
+            $(this.media.$).replaceWith($iframe);
+            this.media.$ = $iframe[0];
+            this._super();
+        },
+        clear: function () {
+            delete this.media.$.dataset.src;
+            this.media.$.className = this.media.$.className.replace(/(^|\s)media_iframe_video(\s|$)/g, ' ');
+        },
     });
 
     website.Observer = window.MutationObserver || window.WebkitMutationObserver || window.JsMutationObserver;
         //       a/@href, ...)
         _(mutations).chain()
             .filter(function (m) {
+                // ignore any SVG target, these blokes are like weird mon
+                if (m.target && m.target instanceof SVGElement) {
+                    return false;
+                }
+
                 // ignore any change related to mundane image-edit-button
                 if (m.target && m.target.className
                         && m.target.className.indexOf('image-edit-button') !== -1) {
                     return false;
                 }
                 switch(m.type) {
-                case 'attributes': // ignore .cke_focus being added or removed
-                    // ignore id modification
-                    if (m.attributeName === 'id') { return false; }
+                case 'attributes':
+                    // ignore special attributes and .cke_focus class being added or removed
+                    var ignored_attrs = ['id', 'contenteditable', 'attributeeditable']
+                    if (_.contains(ignored_attrs, m.attributeName)) { return false; }
                     // if attribute is not a class, can't be .cke_focus change
                     if (m.attributeName !== 'class') { return true; }