diff options
Diffstat (limited to 'addons/website/static/src/js/website.snippets.editor.js')
| -rw-r--r-- | addons/website/static/src/js/website.snippets.editor.js | 953 |
1 files changed, 434 insertions, 519 deletions
diff --git a/addons/website/static/src/js/website.snippets.editor.js b/addons/website/static/src/js/website.snippets.editor.js index 4630206748f..c4ca26f2f74 100644 --- a/addons/website/static/src/js/website.snippets.editor.js +++ b/addons/website/static/src/js/website.snippets.editor.js @@ -1,135 +1,6 @@ (function () { 'use strict'; -/* Building block / Snippet Editor - - The building blocks appear in the edit bar website. These prebuilt html block - allowing the designer to easily generate content on a page (drag and drop). - Options allow snippets to add customizations part html code according to their - selector (jQuery) and javascript object. - - How to create content? - - Designers can add their own html block in the "snippets" (/website/views/snippets.xml). - The block must be added in one of four menus (structure, content, feature or effect). - Structure: - <div> - <div class="oe_snippet_thumbnail"> - <img class="oe_snippet_thumbnail_img" src="...image src..."/> - <span class="oe_snippet_thumbnail_title">...Block Name...</span> - </div> - <div class="oe_snippet_body"> - ... - <!-- - The block with class 'oe_snippet_body' is inserted in the page. - This class is removed when the block is dropped. - The block can be made of any html tag and content. --> - </div> - </div> - - How to create options? - - Designers can add their own html block in the "snippet_options" (/website/views/snippets.xml). - Structure: - - <div data-snippet-option-id='...' <!-- Required: javascript object id (but javascript - for this option object is not required) --> - data-selector="..." <!-- Required: jQuery selector. - Apply options on all The part of html who - match with this jQuery selector. - E.g.: If the selector is div, all div will be selected - and can be highlighted and assigned an editor. --> - data-selector-siblings="..." <!-- Optional: jQuery selector. - The html part can be insert or move beside - the selected html block --> - data-selector-children="..." <!-- Optional: jQuery selector. - The html part can be insert or move inside - the selected html block --> - data-selector-vertical-children='...'> <!-- Optional: jQuery selector. - The html part can be insert or move inside - the selected html block. The drop zone is - displayed vertically --> - ... - <li><a href="#">...</a></li> <!-- Optional: html li list. - List of menu items displayed in customize - menu. If the li tag have 'data-class', the - class is automaticcally added or removed to - the html content when the user select this item. --> - ... - <li class="dropdown-submenu" <!-- Optional: html li list exemple. !--> - data-required="true"> <!-- Optional: if only one item can be selected - and can't be unselect. !--> - <a tabindex="-1" href="#">...</a> <!-- bootstrap dropdown button !--> - <ul class="dropdown-menu"> - <li data-value="text_only"><a>...</a></li> <!-- by default data-value is apply - like a class to html block !--> - </ul> - </li> - </div> - - How to create a javascript object for an options? - - openerp.website.snippet.options["...option-id..."] = website.snippet.Option.extend({ - // start is called when the user click into a block or when the user drop a block - // into the page (just after the init method). - // start is usually used to bind event. - // - // this.$target: block html inserted inside the page - // this.$el: html li list of this options - // this.$overlay: html editor overlay who content resize bar, customize menu... - start: function () {}, - - - // onFocus is called when the user click inside the block inserted in page - // and when the user drop on block into the page - onFocus : function () {}, - - - // onBlur is called when the user click outside the block inserted in page, if - // the block is focused - onBlur : function () {}, - - - // on_clone is called when the snippet is duplicate - // @variables: $clone is allready inserted is the page - on_clone: function ($clone) {}, - - - // on_remove is called when the snippet is removed (dom is removing after this tigger) - on_remove: function () {}, - - - // drop_and_build_snippet is called just after that a thumbnail is drag and dropped - // into a drop zone. The content is already inserted in the page. - drop_and_build_snippet: function () {}, - - // select is called when a user select an item in the li list of options - // By default, if the li item have a data-value attribute, the data-vlue it's apply - // like a class to the html block (this.$target) - // @variables: next_previous = {$next, $prev} - // $next = next item selected or false - // $prev = previous item selected or false - select: function (event, next_previous) {} - - // preview is called when a user is on mouse over or mouse out of an item - // variables: next_previous = {$next, $prev} - // $next = next item selected or false - // $prev = previous item selected or false - preview: function (event, next_previous) {} - - // clean_for_save - // clean_for_save is called just before to save the vue - // Sometime it's important to remove or add some datas (contentEditable, added - // classes to a running animation...) - clean_for_save: function () {} - }); - - - // 'snippet-dropped' is triggered on '#oe_snippets' whith $target as attribute when a snippet is dropped - // 'snippet-activated' is triggered on '#oe_snippets' (and on snippet) when a snippet is activated - -*/ - var dummy = function () {}; var website = openerp.website; @@ -177,8 +48,25 @@ } }); + $.extend($.expr[':'],{ + checkData: function(node,i,m){ + var dataName = m[3]; + while (node) { + if (node.dataset && node.dataset[dataName]) { + return true; + } else { + node = node.parentNode; + } + } + return false; + }, + hasData: function(node,i,m){ + return !!_.toArray(node.dataset).length; + }, + }); + if (!website.snippet) website.snippet = {}; - website.snippet.templateOptions = {}; + website.snippet.templateOptions = []; website.snippet.globalSelector = ""; website.snippet.selector = []; website.snippet.BuildingBlock = openerp.Widget.extend({ @@ -198,25 +86,6 @@ subtree: true, }); }, - dom_filter: function (dom, sibling) { - if (typeof dom === "string") { - var include = "[data-oe-model]"; - var sdom = dom.split(','); - dom = ""; - _.each(sdom, function (val) { - val = val.replace(/^\s+|\s+$/g, ''); - dom += include + " " + val + ", "; - if (!sibling) { - val = val.split(" "); - dom += val.shift() + include + val.join(" ") + ", "; - } - }); - dom = dom.replace(/,\s*$/g, ''); - return $(dom); - } else { - return (!sibling && $(dom).is("[data-oe-model]")) || $(dom).parents("[data-oe-model]").length ? $(dom) : $(""); - } - }, start: function() { var self = this; @@ -224,18 +93,16 @@ .prependTo(this.parent.$("#website-top-edit ul")) .find("button"); - this.$button.click(function () { - self.make_active(false); - self.$el.toggleClass("hidden"); - }); - $("#wrapwrap").click(function () { + this.$button.click(_.bind(this.show_blocks, this)); + + this.$snippet = $("#oe_snippets"); + this.$wrapwrap = $("#wrapwrap"); + this.$wrapwrap.click(function () { self.$el.addClass("hidden"); }); this.fetch_snippet_templates(); - this.bind_snippet_click_editor(); - this.$el.addClass("hidden"); $(document).on('click', '.dropdown-submenu a[tabindex]', function (e) { @@ -245,11 +112,69 @@ this.getParent().on('change:height', this, function (editor) { self.$el.css('top', editor.get('height')); }); - self.$el.css('top', this.parent.get('height')); + this.$el.css('top', this.parent.get('height')); + }, + show_blocks: function () { + var self = this; + this.make_active(false); + this.$el.toggleClass("hidden"); + if (this.$el.hasClass("hidden")) { + return; + } + + //this.enable_snippets( this.$snippet.find(".tab-pane.active") ); + var categories = this.$snippet.find(".tab-pane.active") + .add(this.$snippet.find(".tab-pane:not(.active)")) + .get().reverse(); + function enable() { + self.enable_snippets( $(categories.pop()) ); + if (categories.length) { + setTimeout(enable,10); + } + } + setTimeout(enable,0); + }, + enable_snippets: function ($category) { + var self = this; + $category.find(".oe_snippet_body").each(function () { + var $snippet = $(this); + + if (!$snippet.data('selectors')) { + var selectors = []; + for (var k in website.snippet.templateOptions) { + var option = website.snippet.templateOptions[k]; + if ($snippet.is(option.base_selector)) { + + var dropzone = []; + if (option['drop-near']) dropzone.push(option['drop-near']); + if (option['drop-in']) dropzone.push(option['drop-in']); + if (option['drop-in-vertical']) dropzone.push(option['drop-in-vertical']); + selectors = selectors.concat(dropzone); + } + } + $snippet.data('selectors', selectors.length ? selectors.join(":first, ") + ":first" : ""); + } + + if ($snippet.data('selectors').length && self.$wrapwrap.find($snippet.data('selectors')).size()) { + $snippet.closest(".oe_snippet").removeClass("disable"); + } else { + $snippet.closest(".oe_snippet").addClass("disable"); + } + }); + $('#oe_snippets .scroll a[data-toggle="tab"][href="#' + $category.attr("id") + '"]') + .toggle(!!$category.find(".oe_snippet:not(.disable)").size()); }, _get_snippet_url: function () { return '/website/snippets'; }, + _add_check_selector : function (selector, no_check) { + var data = selector.split(","); + var selectors = []; + for (var k in data) { + selectors.push(data[k].replace(/^\s+|\s+$/g, '') + (no_check ? "" : ":checkData(oeModel)")); + } + return selectors.join(", "); + }, fetch_snippet_templates: function () { var self = this; @@ -258,34 +183,30 @@ var $html = $(html); var selector = []; - var $styles = $html.find("[data-snippet-option-id]"); + var $styles = $html.find("[data-option], [data-selector]"); $styles.each(function () { var $style = $(this); - var style_id = $style.data('snippet-option-id'); - website.snippet.templateOptions[style_id] = { - 'snippet-option-id' : style_id, - 'selector': $style.data('selector'), + var no_check = $style.data('no-check'); + var option_id = $style.data('option'); + var option = { + 'option' : option_id, + 'base_selector': $style.data('selector'), + 'selector': self._add_check_selector($style.data('selector'), no_check), '$el': $style, - 'selector-siblings': $style.data('selector-siblings'), - 'selector-children': $style.data('selector-children'), - 'selector-vertical-children': $style.data('selector-vertical-children'), + 'drop-near': $style.data('drop-near') && self._add_check_selector($style.data('drop-near'), no_check), + 'drop-in': $style.data('drop-in') && self._add_check_selector($style.data('drop-in'), no_check), + 'drop-in-vertical': $style.data('drop-in-vertical') && self._add_check_selector($style.data('drop-in-vertical'), no_check), 'data': $style.data() }; - selector.push($style.data('selector')); + website.snippet.templateOptions.push(option); + selector.push(option.selector); }); $styles.addClass("hidden"); website.snippet.globalSelector = selector.join(","); self.$snippets = $html.find(".tab-content > div > div").addClass("oe_snippet"); - self.$el.append($html); - - var snippets = 0; - self.$snippets.each(function () { - if (self.snippet_have_dropzone($(this))) - snippets++; - }); - if (!snippets) self.$button.css("display", "none"); + self.$el.append($html); self.make_snippet_draggable(self.$snippets); }); @@ -310,17 +231,10 @@ hide: function () { this.$el.addClass("hidden"); }, - - snippet_have_dropzone: function ($snippet) { - return (($snippet.data('selector-siblings') && this.dom_filter($snippet.data('selector-siblings')).size() > 0) || - ($snippet.data('selector-children') && this.dom_filter($snippet.data('selector-children')).size() > 0) || - ($snippet.data('selector-vertical-children') && this.dom_filter($snippet.data('selector-vertical-children')).size() > 0)); - }, - bind_snippet_click_editor: function () { var self = this; var snipped_event_flag; - $("#wrapwrap").on('click', function (event) { + self.$wrapwrap.on('click', function (event) { if (snipped_event_flag || !event.srcElement) { return; } @@ -337,9 +251,6 @@ $target = $target.parents(website.snippet.globalSelector).first(); } - if (!self.dom_filter($target).length) { - $target = false; - } if (self.$active_snipped_id && self.$active_snipped_id.is($target)) { return; } @@ -349,14 +260,14 @@ snippet_blur: function ($snippet) { if ($snippet) { if ($snippet.data("snippet-editor")) { - $snippet.data("snippet-editor").onBlur(); + $snippet.data("snippet-editor").on_blur(); } } }, snippet_focus: function ($snippet) { if ($snippet) { if ($snippet.data("snippet-editor")) { - $snippet.data("snippet-editor").onFocus(); + $snippet.data("snippet-editor").on_focus(); } } }, @@ -364,15 +275,15 @@ var self = this; var options = website.snippet.options; var template = website.snippet.templateOptions; - for (var k in options) { - if (template[k] && options[k].prototype.clean_for_save !== dummy) { - var $snippet = this.dom_filter(template[k].selector); - $snippet.each(function () { - new options[k](self, null, $(this), k).clean_for_save(); + for (var k in template) { + var Option = options[template[k]['option']]; + if (Option && Option.prototype.clean_for_save !== dummy) { + self.$wrapwrap.find(template[k].selector).each(function () { + new Option(self, null, $(this), k).clean_for_save(); }); } } - $("*[contentEditable], *[attributeEditable]") + self.$wrapwrap.find("*[contentEditable], *[attributeEditable]") .removeAttr('contentEditable') .removeAttr('attributeEditable'); }, @@ -392,7 +303,7 @@ this.create_overlay(this.$active_snipped_id); this.snippet_focus($snippet); } - $("#oe_snippets").trigger('snippet-activated', $snippet); + this.$snippet.trigger('snippet-activated', $snippet); if ($snippet) { $snippet.trigger('snippet-activated', $snippet); } @@ -406,15 +317,6 @@ this.cover_target($snippet.data('overlay'), $snippet); }, - path_eval: function(path){ - var obj = window; - path = path.split('.'); - do{ - obj = obj[path.shift()]; - }while(path.length && obj); - return obj; - }, - // activate drag and drop for the snippets in the snippet toolbar make_snippet_draggable: function($snippets){ var self = this; @@ -437,32 +339,32 @@ start: function(){ self.hide(); dropped = false; - // snippet_selectors => to get selector-siblings, selector-children, selector-vertical-children + // snippet_selectors => to get drop-near, drop-in, drop-in-vertical $snippet = $(this); - $toInsert = $snippet.find('.oe_snippet_body').clone(); - + var $base_body = $snippet.find('.oe_snippet_body'); var selector = []; var selector_siblings = []; var selector_children = []; var selector_vertical_children = []; for (var k in website.snippet.templateOptions) { - if ($toInsert.is(website.snippet.templateOptions[k].selector)) { - selector.push(website.snippet.templateOptions[k].selector); - if (website.snippet.templateOptions[k]['selector-siblings']) - selector_siblings.push(website.snippet.templateOptions[k]['selector-siblings']); - if (website.snippet.templateOptions[k]['selector-children']) - selector_children.push(website.snippet.templateOptions[k]['selector-children']); - if (website.snippet.templateOptions[k]['selector-vertical-children']) - selector_vertical_children.push(website.snippet.templateOptions[k]['selector-vertical-children']); + if ($base_body.is(website.snippet.templateOptions[k].base_selector)) { + selector.push(website.snippet.templateOptions[k].base_selector); + if (website.snippet.templateOptions[k]['drop-near']) + selector_siblings.push(website.snippet.templateOptions[k]['drop-near']); + if (website.snippet.templateOptions[k]['drop-in']) + selector_children.push(website.snippet.templateOptions[k]['drop-in']); + if (website.snippet.templateOptions[k]['drop-in-vertical']) + selector_vertical_children.push(website.snippet.templateOptions[k]['drop-in-vertical']); } } + $toInsert = $base_body.clone(); action = $snippet.find('.oe_snippet_body').size() ? 'insert' : 'mutate'; if( action === 'insert'){ if (!selector_siblings.length && !selector_children.length && !selector_vertical_children.length) { - console.debug($snippet.data("snippet-id") + " have oe_snippet_body class and have not for insert action"+ - "data-selector-siblings, data-selector-children or data-selector-vertical-children tag for mutate action"); + console.debug($snippet.find(".oe_snippet_thumbnail_title").text() + " have not insert action: "+ + "data-drop-near, data-drop-in or data-drop-in-vertical"); return; } self.activate_insertion_zones({ @@ -473,7 +375,7 @@ } else if( action === 'mutate' ){ if (!$snippet.data('selector')) { - console.debug($snippet.data("snippet-id") + " have not oe_snippet_body class and have not data-selector tag"); + console.debug($snippet.data("option") + " have not oe_snippet_body class and have not data-selector tag"); return; } var $targets = self.activate_overlay_zones(selector_children.join(",")); @@ -504,38 +406,24 @@ stop: function(ev, ui){ $toInsert.removeClass('oe_snippet_body'); - if (action === 'insert' && ! dropped && $('.oe_drop_zone') && ui.position.top > 3) { - var el = $('.oe_drop_zone').nearest({x: ui.position.left, y: ui.position.top}).first(); + if (action === 'insert' && ! dropped && self.$wrapwrap.find('.oe_drop_zone') && ui.position.top > 3) { + var el = self.$wrapwrap.find('.oe_drop_zone').nearest({x: ui.position.left, y: ui.position.top}).first(); if (el.length) { el.after($toInsert); dropped = true; } } - $('.oe_drop_zone').droppable('destroy').remove(); + self.$wrapwrap.find('.oe_drop_zone').droppable('destroy').remove(); if (dropped) { var $target = false; $target = $toInsert; setTimeout(function () { - $("#oe_snippets").trigger('snippet-dropped', $target); + self.$snippet.trigger('snippet-dropped', $target); website.snippet.start_animation(true, $target); - // drop_and_build_snippet - self.create_overlay($target); - if ($target.data("snippet-editor")) { - $target.data("snippet-editor").drop_and_build_snippet(); - } - for (var k in website.snippet.templateOptions) { - $target.find(website.snippet.templateOptions[k].selector).each(function () { - var $snippet = $(this); - self.create_overlay($snippet); - if ($snippet.data("snippet-editor")) { - $snippet.data("snippet-editor").drop_and_build_snippet(); - } - }); - } // reset snippet for rte $target.removeData("snippet-editor"); @@ -551,7 +439,22 @@ $snippet.removeData("overlay"); } }); + // end + + // drop_and_build_snippet self.create_overlay($target); + if ($target.data("snippet-editor")) { + $target.data("snippet-editor").drop_and_build_snippet(); + } + for (var k in website.snippet.templateOptions) { + $target.find(website.snippet.templateOptions[k].selector).each(function () { + var $snippet = $(this); + self.create_overlay($snippet); + if ($snippet.data("snippet-editor")) { + $snippet.data("snippet-editor").drop_and_build_snippet(); + } + }); + } // end self.make_active($target); @@ -566,7 +469,7 @@ // return the original snippet in the editor bar from a snippet id (string) get_snippet_from_id: function(id){ return $('.oe_snippet').filter(function(){ - return $(this).data('snippet-id') === id; + return $(this).data('option') === id; }).first(); }, @@ -584,7 +487,7 @@ var zone_template = "<div class='oe_drop_zone oe_insert'></div>"; if(child_selector){ - self.dom_filter(child_selector).each(function (){ + self.$wrapwrap.find(child_selector).each(function (){ var $zone = $(this); $zone.find('> *:not(.oe_drop_zone):visible').after(zone_template); $zone.prepend(zone_template); @@ -592,7 +495,7 @@ } if(vertical_child_selector){ - self.dom_filter(vertical_child_selector).each(function (){ + self.$wrapwrap.find(vertical_child_selector).each(function (){ var $zone = $(this); var $template = $(zone_template).addClass("oe_vertical"); var nb = 0; @@ -623,7 +526,7 @@ } if(sibling_selector){ - self.dom_filter(sibling_selector, true).each(function (){ + self.$wrapwrap.find(sibling_selector, true).each(function (){ var $zone = $(this); if($zone.prev('.oe_drop_zone:visible').length === 0){ $zone.before(zone_template); @@ -641,13 +544,13 @@ // count += $zones.length; // $zones.remove(); - $zones = $('.oe_drop_zone > .oe_drop_zone:not(.oe_vertical)').remove(); // no recursive zones + $zones = self.$wrapwrap.find('.oe_drop_zone > .oe_drop_zone:not(.oe_vertical)').remove(); // no recursive zones count += $zones.length; $zones.remove(); } while (count > 0); // Cleaning up zones placed between floating or inline elements. We do not like these kind of zones. - var $zones = $('.oe_drop_zone:not(.oe_vertical)'); + var $zones = self.$wrapwrap.find('.oe_drop_zone:not(.oe_vertical)'); $zones.each(function (){ var zone = $(this); var prev = zone.prev(); @@ -671,14 +574,9 @@ // generate drop zones covering the elements selected by the selector // we generate overlay drop zones only to get an idea of where the snippet are, the drop activate_overlay_zones: function(selector){ - var $targets = this.dom_filter(selector); + var $targets = typeof selector === "string" ? this.$wrapwrap.find(selector) : selector; var self = this; - if (typeof selector !== 'string' && !$targets.length) { - console.debug( "A good node must have a [data-oe-model] attribute or must have at least one parent with [data-oe-model] attribute."); - console.debug( "Wrong node(s): ", selector); - } - function is_visible($el){ return $el.css('display') != 'none' && $el.css('opacity') != '0' @@ -719,9 +617,14 @@ $target.on("DOMNodeInserted DOMNodeRemoved DOMSubtreeModified", function () { self.cover_target($zone, $target); }); - $('body').on("resize", function () { - self.cover_target($zone, $target); - }); + var resize = function () { + if ($zone.parent().length) { + self.cover_target($zone, $target); + } else { + $('body').off("resize", resize); + } + }; + $('body').on("resize", resize); } self.cover_target($target.data('overlay'), $target); }); @@ -733,97 +636,86 @@ website.snippet.options = {}; website.snippet.Option = openerp.Class.extend({ // initialisation (don't overwrite) - init: function (BuildingBlock, editor, $target, snippet_id) { + init: function (BuildingBlock, editor, $target, option_id) { this.BuildingBlock = BuildingBlock; this.editor = editor; this.$target = $target; + var option = website.snippet.templateOptions[option_id]; var styles = this.$target.data("snippet-option-ids") || {}; - styles[snippet_id] = this; + styles[option_id] = this; this.$target.data("snippet-option-ids", styles); this.$overlay = this.$target.data('overlay'); - this['snippet-option-id'] = snippet_id; - var $option = website.snippet.templateOptions[snippet_id].$el; - this.$el = $option.find(">li").clone(); - this.data = $option.data(); - - this.required = this.$el.data("required"); + this.option= option_id; + this.$el = option.$el.find(">li").clone(); + this.data = option.$el.data(); this.set_active(); - this.$el.find('li[data-value] a').on('mouseenter mouseleave click', _.bind(this._mouse, this)); - this.$el.not(':not([data-value])').find("a").on('mouseenter mouseleave click', _.bind(this._mouse, this)); - this.$target.on('snippet-style-reset', _.bind(this.set_active, this)); + this.$target.on('snippet-option-reset', _.bind(this.set_active, this)); + this._bind_li_menu(); this.start(); }, - _mouse: function (event) { - var self = this; - if (event.type === 'mouseleave') { - if (!this.over) return; - this.over = false; - } else if (event.type === 'click') { - this.over = false; - }else { - this.over = true; - } + _bind_li_menu: function () { + this.$el.filter("li:hasData").find('a:first') + .off('mouseenter click') + .on('mouseenter click', _.bind(this._mouse, this)); - var $prev, $next; - if (event.type === 'mouseleave') { - $prev = $(event.currentTarget).parent(); - $next = this.$el.find("li[data-value].active"); - } else { - $prev = this.$el.find("li[data-value].active"); - $next = $(event.currentTarget).parent(); - } - if (!$prev.length) { - $prev = false; - } - if ($prev && $prev[0] === $next[0]) { - $next = false; - if (this.required) { - return; - } - } + this.$el + .off('mouseenter click', "li:hasData a") + .on('mouseenter click', "li:hasData a", _.bind(this._mouse, this)); + + this.$el.closest("ul").add(this.$el) + .off('mouseleave') + .on('mouseleave', _.bind(this.reset, this)); + + this.$el + .off('mouseleave', "ul") + .on('mouseleave', "ul", _.bind(this.reset, this)); - var np = {'$next': $next, '$prev': $prev}; + this.reset_methods = []; + this.reset_time = null; + }, + + /** + * this method handles mouse:over and mouse:leave on the snippet editor menu + */ + _time_mouseleave: null, + _mouse: function (event) { + var $next = $(event.currentTarget).parent(); + // triggers preview or apply methods if a menu item has been clicked + this.select(event.type === "click" ? "click" : "over", $next); if (event.type === 'click') { - setTimeout(function () { - self.set_active(); - self.$target.trigger("snippet-style-change", [self, np]); - },0); - this.select({'$next': $next, '$prev': $prev}); + this.set_active(); + this.$target.trigger("snippet-option-change", [this]); } else { - setTimeout(function () { - self.$target.trigger("snippet-style-preview", [self, np]); - },0); - this.preview(np); + this.$target.trigger("snippet-option-preview", [this]); } }, - /* set_active + /* * select and set item active or not (add highlight item and his parents) * called before start */ set_active: function () { - var self = this; - this.$el.find('li').removeClass("active"); - var $active = this.$el.find('li[data-value]') - .filter(function () { - var $li = $(this); - return ($li.data('value') && self.$target.hasClass($li.data('value'))); - }) - .first() + var classes = _.uniq((this.$target.attr("class") || '').split(/\s+/)); + this.$el.find('[data-check_class], [data-select_class]') + .add(this.$el) + .filter('[data-check_class], [data-select_class]') + .removeClass("active") + .filter('[data-check_class="' + classes.join('"], [data-check_class="') + '"] ,'+ + '[data-select_class="' + classes.join('"], [data-select_class="') + '"]') .addClass("active"); - this.$el.find('li:has(li[data-value].active)').addClass("active"); }, start: function () { }, - onFocus : function () { + on_focus : function () { + this._bind_li_menu(); }, - onBlur : function () { + on_blur : function () { }, on_clone: function ($clone) { @@ -835,110 +727,167 @@ drop_and_build_snippet: function () { }, - select: function (np) { - var self = this; - // add or remove html class - if (np.$prev && this.required) { - this.$target.removeClass(np.$prev.data('value' || "")); + reset: function (event) { + var self = this, $el, method; + for (var k in self.reset_methods) { + method = self.reset_methods[k]; + var $li = self.$el.find('[data-'+method+']').add(self.$el).filter('.active, :has(.active)'); + if ($li.size()) { + $li.each(function () { + self.select("reset", $(this)); + }); + } else { + self[method]("reset", null, null); + } } - if (np.$next) { - this.$target.addClass(np.$next.data('value') || ""); + self.reset_methods = []; + self.$target.trigger("snippet-option-reset", [this]); + }, + + // call data-method args as method + select: function (type, $li) { + var self = this, + $methods = [], + el = $li[0], + $el; + clearTimeout(this.reset_time); + + function filter (k) { return k !== 'oeId' && k !== 'oeModel' && k !== 'oeField' && k !== 'oeXpath' && k !== 'oeSourceId';} + function hasData(el) { + for (var k in el.dataset) { + if (filter (k)) { + return true; + } + } + return false; + } + function method(el) { + var data = {}; + for (var k in el.dataset) { + if (filter (k)) { + data[k] = el.dataset[k]; + } + } + return data; + } + + while (el && this.$el.is(el) || _.some(this.$el.map(function () {return $.contains(this, el);}).get()) ) { + if (hasData(el)) { + $methods.push(el); + } + el = el.parentNode; } + + $methods.reverse(); + + _.each($methods, function (el) { + var $el = $(el); + var methods = method(el); + for (var k in methods) { + if (self[k]) { + if (self.reset_methods.indexOf(k) === -1) { + self.reset_methods.push(k); + } + self[k](type, methods[k], $el); + } else { + console.error("'"+self.option+"' snippet have not method '"+k+"'"); + } + } + }); }, - preview: function (np) { - var self = this; + // default method for snippet + check_class: function (type, value, $li) { + var $lis = this.$el.find('[data-check_class]').add(this.$el).filter('[data-check_class]'); - // add or remove html class - if (np.$prev) { - this.$target.removeClass(np.$prev.data('value') || ""); + function map ($lis) { + return $lis.map(function () {return $(this).data("check_class");}).get().join(" "); } - if (np.$next) { - this.$target.addClass(np.$next.data('value') || ""); + var classes = map($lis); + var active_classes = map($lis.filter('.active, :has(.active)')); + + this.$target.removeClass(classes); + this.$target.addClass(active_classes); + + if (type !== 'reset') { + this.$target.toggleClass(value); } }, + select_class: function (type, value, $li) { + var $lis = this.$el.find('[data-select_class]').add(this.$el).filter('[data-select_class]'); + + var classes = $lis.map(function () {return $(this).data('select_class');}).get(); + + this.$target.removeClass(classes.join(" ")); + if(value) this.$target.addClass(value); + }, clean_for_save: dummy }); - website.snippet.options.background = website.snippet.Option.extend({ - _get_bg: function () { - return this.$target.css("background-image").replace(/url\(['"]*|['"]*\)|^none$/g, ""); - }, - _set_bg: function (src) { - this.$target.css("background-image", src && src !== "" ? 'url(' + src + ')' : ""); - }, start: function () { this._super(); - var src = this._get_bg(); - this.$el.find("li[data-value].active.oe_custom_bg").data("src", src); + var src = this.$target.css("background-image").replace(/url\(['"]*|['"]*\)|^none$/g, ""); + if (this.$target.hasClass('oe_custom_bg')) { + this.$el.find('li[data-choose_image]').data("background", src).attr("data-background", src); + } }, - select: function(np) { - var self = this; - this._super(np); - if (np.$next) { - if (np.$next.hasClass("oe_custom_bg")) { - var $image = $('<img class="hidden"/>'); - $image.attr("src", np.$prev ? np.$prev.data("src") : ''); - $image.appendTo(self.$target); - - self.element = new CKEDITOR.dom.element($image[0]); - var editor = new website.editor.MediaDialog(self, self.element); - editor.appendTo(document.body); - editor.$('[href="#editor-media-video"], [href="#editor-media-icon"]').addClass('hidden'); - - $image.on('saved', self, function (o) { - var src = $image.attr("src"); - self._set_bg(src); - np.$next.data("src", src); - self.$target.trigger("snippet-style-change", [self, np]); - $image.remove(); - }); - editor.on('cancel', self, function () { - if (!np.$prev || np.$prev.data("src") === "") { - self.$target.removeClass(np.$next.data("value")); - self.$target.trigger("snippet-style-change", [self, np]); - } - $image.remove(); - }); - } else { - this._set_bg(np.$next.data("src")); - } + background: function(type, value, $li) { + if (value && value.length) { + this.$target.css("background-image", 'url(' + value + ')'); } else { - this._set_bg(false); - this.$target.removeClass(np.$prev.data("value")); + this.$target.css("background-image", "").removeClass("oe_img_bg"); } }, - preview: function (np) { - this._super(np); - if (np.$next) { - this._set_bg(np.$next.data("src")); - } + choose_image: function(type, value, $li) { + if(type !== "click") return; + + var self = this; + var $image = $('<img class="hidden"/>'); + $image.attr("src", value); + $image.appendTo(self.$target); + + self.element = new CKEDITOR.dom.element($image[0]); + var editor = new website.editor.MediaDialog(self, self.element); + editor.appendTo(document.body); + editor.$('[href="#editor-media-video"], [href="#editor-media-icon"]').addClass('hidden'); + + $image.on('saved', self, function (o) { + var value = $image.attr("src"); + self.$target.css("background-image", 'url(' + value + ')'); + self.$el.find('li[data-select_class="oe_custom_bg"]').data("src", value); + self.$target.trigger("snippet-option-change", [self]); + $image.remove(); + self.$target.addClass('oe_custom_bg').removeClass('oe_img_bg'); + self.set_active(); + }); + editor.on('cancel', self, function () { + self.$target.removeClass("oe_img_bg") + .trigger("snippet-option-change", [self]); + $image.remove(); + }); }, set_active: function () { var self = this; - var bg = self.$target.css("background-image"); - this.$el.find('li').removeClass("active"); - this.$el.find('li').removeClass("btn-primary"); - var $active = this.$el.find('li[data-value]') - .filter(function () { - var $li = $(this); - return ($li.data('src') && bg.indexOf($li.data('src')) >= 0) || - (!$li.data('src') && self.$target.hasClass($li.data('value'))); - }) - .first(); - if (!$active.length) { - $active = this.$target.css("background-image") !== 'none' ? - this.$el.find('li[data-value].oe_custom_bg') : - this.$el.find('li[data-value=""]'); - } + var src = this.$target.css("background-image").replace(/url\(['"]*|['"]*\)|^none$/g, ""); + this._super(); - //don't set active on an OpenDialog link, else it not possible to click on it again after. - // TODO in Saas-4 - Once bootstrap is in less - // - add a class active-style to get the same display but without the active behaviour used by bootstrap in JS. - var classStr = _.string.contains($active[0].className, "oe_custom_bg") ? "btn-primary" : "active"; - $active.addClass(classStr); - this.$el.find('li:has(li[data-value].active)').addClass(classStr); + this.$el.find('li[data-background]') + .filter(':not([data-background=""])') + .removeClass("active") + .each(function () { + var background = $(this).data("background"); + if ((src.length && background.length && src.indexOf(background) !== -1) || (!src.length && !background.length)) { + $(this).addClass("active"); + } + }); + if(!this.$el.find('li.active').size()) { + this.$el.find('li[data-select_class=""][data-background=""], '+ + 'li[data-select_class=""] li[data-background=""], '+ + 'li[data-background=""] li[data-select_class=""]').addClass("active"); + this.$el.filter('li[data-select_class=""]').find('li[data-background=""]').addClass("active"); + this.$el.filter('li[data-background=""]').find('li[data-select_class=""]').addClass("active"); + } } }); @@ -983,7 +932,6 @@ } }); - website.snippet.options.slider = website.snippet.Option.extend({ unique_id: function () { var id = 0; @@ -1029,14 +977,12 @@ this.id = this.$target.attr("id"); this.$inner = this.$target.find('.carousel-inner'); this.$indicators = this.$target.find('.carousel-indicators'); - - this.$el.find(".js_add").on('click', function () {self.on_add_slide(); return false;}); - this.$el.find(".js_remove").on('click', function () {self.on_remove_slide(); return false;}); - this.$target.carousel('pause'); this.rebind_event(); }, - on_add_slide: function () { + add_slide: function (type, value) { + if(type !== "click") return; + var self = this; var cycle = this.$inner.find('.item').length; var $active = this.$inner.find('.item.active, .item.prev, .item.next').first(); @@ -1054,7 +1000,9 @@ },0); return $clone; }, - on_remove_slide: function () { + remove_slide: function (type, value) { + if(type !== "click") return; + if (this.remove_process) { return; } @@ -1083,6 +1031,13 @@ this.$target.find('.carousel-control, .carousel-indicators').addClass("hidden"); } }, + interval : function(type, value) { + this.$target.attr("data-interval", value); + }, + set_active: function () { + this.$el.find('li[data-interval]').removeClass("active") + .filter('li[data-interval='+this.$target.attr("data-interval")+']').addClass("active"); + }, }); website.snippet.options.carousel = website.snippet.options.slider.extend({ getSize: function () { @@ -1092,12 +1047,11 @@ }, clean_for_save: function () { this._super(); - this.$target.css("background-image", ""); - this.$target.removeClass(this._class); + this.$target.removeClass('oe_img_bg ' + this._class).css("background-image", ""); }, load_style_options : function () { this._super(); - $(".snippet-style-size li[data-value='']").remove(); + $(".snippet-option-size li[data-value='']").remove(); }, start : function () { var self = this; @@ -1108,46 +1062,30 @@ if (c) self._class = (self._class || "").replace(new RegExp("[ ]+" + c.replace(" ", "|[ ]+")), '') + ' ' + c; return self._class || ""; }; - this.$target.on('snippet-style-change snippet-style-preview', function (event, style, np) { - var $active = self.$target.find(".item.active"); - if (style['snippet-option-id'] === "size") return; - if (style['snippet-option-id'] === "background") { - $active.css("background-image", self.$target.css("background-image")); - } - if (np.$prev) { - $active.removeClass(np.$prev.data("value")); - } - if (np.$next) { - $active.addClass(np.$next.data("value")); - add_class(np.$next.data("value")); + this.$target.on('slid.bs.carousel', function () { + if(self.editor && self.editor.styles.background) { + self.editor.styles.background.$target = self.$target.find(".item.active"); + self.editor.styles.background.set_active(); } - }); - this.$target.on('slid', function () { // slide.bs.carousel - var $active = self.$target.find(".item.active"); - self.$target - .css("background-image", $active.css("background-image")) - .removeClass(add_class($active.attr("class"))) - .addClass($active.attr("class")) - .trigger("snippet-style-reset"); - self.$target.carousel("pause"); }); - this.$target.trigger('slid'); + this.$target.trigger('slid.bs.carousel'); }, - on_add_slide: function () { - var $clone = this._super(); + add_slide: function (type, data) { + if(type !== "click") return; + var $clone = this._super(type, data); // choose an other background var bg = this.$target.data("snippet-option-ids").background; if (!bg) return $clone; - var $styles = bg.$el.find("li[data-value]:not(.oe_custom_bg)"); - var styles_index = $styles.index($styles.filter(".active")[0]); - $styles.removeClass("active"); - var $select = $($styles[styles_index >= $styles.length-1 ? 0 : styles_index+1]); + var $styles = bg.$el.find("li[data-background]"); + var $select = $styles.filter(".active").removeClass("active").next("li[data-background]"); + if (!$select.length) { + $select = $styles.first(); + } $select.addClass("active"); - $clone.css("background-image", $select.data("src") ? "url('"+ $select.data("src") +"')" : ""); - $clone.addClass($select.data("value") || ""); + $clone.css("background-image", $select.data("background") ? "url('"+ $select.data("background") +"')" : ""); return $clone; }, @@ -1162,7 +1100,6 @@ this._super(); }, }); - website.snippet.options.marginAndResize = website.snippet.Option.extend({ start: function () { var self = this; @@ -1290,7 +1227,7 @@ return this.grid; }, - onFocus : function () { + on_focus : function () { this._super(); this.change_cursor(); }, @@ -1390,7 +1327,7 @@ hide_remove_button: function() { this.$overlay.find('.oe_snippet_remove').toggleClass("hidden", !this.$target.siblings().length); }, - onFocus : function () { + on_focus : function () { this._super(); this.hide_remove_button(); }, @@ -1401,20 +1338,8 @@ return false; }, on_remove: function () { - if (!this.$target.siblings().length) { - var $parent = this.$target.parents(".row:first"); - if($parent.find("[class*='col-md']").length > 1) { - return false; - } else { - if (!$parent.data("snippet-editor")) { - this.BuildingBlock.create_overlay($parent); - } - $parent.data("snippet-editor").on_remove(); - } - } this._super(); this.hide_remove_button(); - return false; }, on_resize: function (compass, beginClass, current) { if (compass === 'w') { @@ -1441,7 +1366,7 @@ }, }); - website.snippet.options["resize"] = website.snippet.options.marginAndResize.extend({ + website.snippet.options.resize = website.snippet.options.marginAndResize.extend({ getSize: function () { this.grid = this._super(); this.grid.size = 8; @@ -1465,7 +1390,7 @@ this.$target.data("snippet-view", new website.snippet.animationRegistry.parallax(this.$target)); } this.scroll(); - this.$target.on('snippet-style-change snippet-style-preview', function () { + this.$target.on('snippet-option-change snippet-option-preview', function () { self.$target.data("snippet-view").set_values(); }); this.$target.attr('contentEditable', 'false'); @@ -1474,22 +1399,15 @@ this.$target.find('> div > div:not(.oe_structure) > .oe_structure').attr('contentEditable', 'true'); }, - scroll: function () { - var self = this; - var $ul = this.$el.find('ul[name="parallax-scroll"]'); - var $li = $ul.find("li"); - var speed = this.$target.data('scroll-background-ratio') || 0.6 ; - $ul.find('[data-value="' + speed + '"]').addClass('active'); - $li.on('click', function (event) { - $li.removeClass("active"); - $(this).addClass("active"); - var speed = $(this).data('value'); - self.$target.attr('data-scroll-background-ratio', speed); - self.$target.data("snippet-view").set_values(); - return false; - }); + scroll: function (type, value) { + this.$target.attr('data-scroll-background-ratio', value); this.$target.data("snippet-view").set_values(); }, + set_active: function () { + var value = this.$target.attr('data-scroll-background-ratio') || 0; + this.$el.find('[data-scroll]').removeClass("active") + .filter('[data-scroll="' + (this.$target.attr('data-scroll-background-ratio') || 0) + '"]').addClass("active"); + }, clean_for_save: function () { this._super(); this.$target.find(".parallax") @@ -1502,24 +1420,22 @@ start: function () { var self = this; this._super(); - - this.$el.find(".clear-style").click(function (event) { - self.$target.removeClass("fa-spin").attr("style", ""); - self.resetTransfo(); - }); - - this.$el.find(".style").click(function (event) { - var settings = self.$target.data("transfo").settings; - self.$target.transfo({ hide: (settings.hide = !settings.hide) }); - }); - this.$overlay.find('.oe_snippet_clone, .oe_handles').addClass('hidden'); - this.$overlay.find('[data-toggle="dropdown"]') .on("mousedown", function () { self.$target.transfo("hide"); }); }, + style: function (type, value) { + if (type !== 'click') return; + var settings = this.$target.data("transfo").settings; + this.$target.transfo({ hide: (settings.hide = !settings.hide) }); + }, + clear_style: function (type, value) { + if (type !== 'click') return; + this.$target.removeClass("fa-spin").attr("style", ""); + this.resetTransfo(); + }, resetTransfo: function () { var self = this; this.$target.transfo("destroy"); @@ -1541,10 +1457,10 @@ }) .mouseover(); }, - onFocus : function () { + on_focus : function () { this.resetTransfo(); }, - onBlur : function () { + on_blur : function () { this.$target.transfo("hide"); }, }); @@ -1557,7 +1473,7 @@ website.snippet.start_animation(true, this.$target); $(document.body).on("media-saved", self, function (event, prev , item) { - self.editor.onBlur(); + self.editor.on_blur(); self.BuildingBlock.make_active(false); if (self.$target.parent().data("oe-field") !== "image") { self.BuildingBlock.make_active($(item)); @@ -1571,7 +1487,7 @@ new website.editor.MediaDialog(self, self.element).appendTo(document.body); }); }, - onFocus : function () { + on_focus : function () { var self = this; if (this.$target.parent().data("oe-field") === "image") { this.$overlay.addClass("hidden"); @@ -1582,39 +1498,16 @@ }, }); - website.snippet.Editor = openerp.Class.extend({ init: function (BuildingBlock, dom) { this.BuildingBlock = BuildingBlock; this.$target = $(dom); this.$overlay = this.$target.data('overlay'); - this.snippet_id = this.$target.data("snippet-id"); - this._readXMLData(); this.load_style_options(); this.get_parent_block(); this.start(); }, - /* - * _readXMLData - * Read data XML and set value into: - * this.$el : - * all xml data - * this.$overlay : - * Dom hover the $target who content options - */ - _readXMLData: function() { - var self = this; - if(this && this.BuildingBlock && this.BuildingBlock.$snippets) { - this.$el = this.BuildingBlock.$snippets.filter(function () { return $(this).data("snippet-id") == self.snippet_id; }).clone(); - } - var $options = this.$overlay.find(".oe_overlay_options"); - if ($options.find(".oe_options ul li").length) { - $options.find(".oe_options").removeClass("hidden"); - } - }, - - // activate drag and drop for the snippets in the snippet toolbar _drag_and_drop: function(){ var self = this; @@ -1700,18 +1593,18 @@ this.selector_siblings = []; this.selector_children = []; this.selector_vertical_children = []; - _.each(website.snippet.templateOptions, function (val) { + _.each(website.snippet.templateOptions, function (val, option_id) { if (!self.$target.is(val.selector)) { return; } - if (val['selector-siblings']) self.selector_siblings.push(val['selector-siblings']); - if (val['selector-children']) self.selector_children.push(val['selector-children']); - if (val['selector-vertical-children']) self.selector_vertical_children.push(val['selector-vertical-children']); - - var style = val['snippet-option-id']; - var Editor = website.snippet.options[style] || website.snippet.Option; - var editor = self.styles[style] = new Editor(self.BuildingBlock, self, self.$target, style); - $ul.append(editor.$el.addClass("snippet-style-" + style)); + if (val['drop-near']) self.selector_siblings.push(val['drop-near']); + if (val['drop-in']) self.selector_children.push(val['drop-in']); + if (val['drop-in-vertical']) self.selector_vertical_children.push(val['drop-in-vertical']); + + var option = val['option']; + var Editor = website.snippet.options[option] || website.snippet.Option; + var editor = self.styles[option] = new Editor(self.BuildingBlock, self, self.$target, option_id); + $ul.append(editor.$el.addClass("snippet-option-" + option)); }); this.selector_siblings = this.selector_siblings.join(","); if (this.selector_siblings === "") @@ -1724,7 +1617,7 @@ this.selector_vertical_children = false; if (!this.selector_siblings && !this.selector_children && !this.selector_vertical_children) { - this.$overlay.find(".oe_snippet_move").addClass('hidden'); + this.$overlay.find(".oe_snippet_move, .oe_snippet_clone, .oe_snippet_remove").addClass('hidden'); } @@ -1772,13 +1665,35 @@ }, on_remove: function () { - this.onBlur(); + this.on_blur(); var index = _.indexOf(this.BuildingBlock.snippets, this.$target.get(0)); for (var i in this.styles){ this.styles[i].on_remove(); } delete this.BuildingBlock.snippets[index]; + + // remove node and his empty + var parent, + node = this.$target.parent()[0]; + this.$target.remove(); + function check(node) { + if ($(node).outerHeight() > 8) { + return false; + } + for (var k=0; k<node.children.length; k++) { + if (node.children[k].tagName || node.children[k].textContent.match(/[^\s]/)) { + return false; + } + } + return true; + } + while (check(node)) { + parent = node.parentNode; + parent.removeChild(node); + node = parent; + } + this.$overlay.remove(); return false; }, @@ -1794,22 +1709,22 @@ } }, - /* onFocus + /* on_focus * This method is called when the user click inside the snippet in the dom */ - onFocus : function () { + on_focus : function () { this.$overlay.addClass('oe_active'); for (var i in this.styles){ - this.styles[i].onFocus(); + this.styles[i].on_focus(); } }, - /* onFocus + /* on_focus * This method is called when the user click outside the snippet in the dom, after a focus */ - onBlur : function () { + on_blur : function () { for (var i in this.styles){ - this.styles[i].onBlur(); + this.styles[i].on_blur(); } this.$overlay.removeClass('oe_active'); }, |
