diff options
| author | Paramjit Singh Sahota <psa@tinyerp.com> | 2013-12-24 11:01:25 +0530 |
|---|---|---|
| committer | Paramjit Singh Sahota <psa@tinyerp.com> | 2013-12-24 11:01:25 +0530 |
| commit | 18d8368ee11eef0a74bacdb8f293414331bfa8da (patch) | |
| tree | fc21df70cbd2901396c2bcf2fee0b52d750abd43 /addons/website/static/src | |
| parent | 5900672160c2bd5ae1d9257f20dbb5c51a193996 (diff) | |
| parent | 123185757f51ee7e146d7295864556999d2a6938 (diff) | |
[MERGE] Merged lp:~openerp-dev/openobject-addons/trunk-website-al
bzr revid: psa@tinyerp.com-20131224053125-3lrogsrgd3ccp0i6
Diffstat (limited to 'addons/website/static/src')
32 files changed, 2189 insertions, 1310 deletions
diff --git a/addons/website/static/src/css/common.css b/addons/website/static/src/css/common.css new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/addons/website/static/src/css/common.css diff --git a/addons/website/static/src/css/common.sass b/addons/website/static/src/css/common.sass new file mode 100644 index 00000000000..86e1f6655ef --- /dev/null +++ b/addons/website/static/src/css/common.sass @@ -0,0 +1,25 @@ +@import "compass/css3" +@import "compass/css3/user-interface" +@import "compass/css3/transition" + +//smartphones, iPhone, portrait 480x320 phones +$smart_phone: 320px +//portrait e-readers (Nook/Kindle), smaller tablets @ 600 or @ 640 wide.*/ +$small-tablet: 481px +//portrait tablets, portrait iPad, landscape e-readers, landscape 800x480 or 854x480 phones +$portrait-tablet: 641px +//tablet, landscape iPad, lo-res laptops ands desktops +$tablet: 961px +//big landscape tablets, laptops, and desktops +$desktop: 1025px + +@mixin editor-font + font-size: 14px + font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', monospace + line-height: normal + +@mixin selection() + ::-moz-selection + @content + ::selection + @content diff --git a/addons/website/static/src/css/editor.css b/addons/website/static/src/css/editor.css index 823a1d30f81..b784177fd1c 100644 --- a/addons/website/static/src/css/editor.css +++ b/addons/website/static/src/css/editor.css @@ -1,5 +1,4 @@ -@charset "utf-8"; -/* ---- CKEditor Minimal Reset ---- */ +/* ---- CKEditor Minimal Reset ---- {{{ */ .navbar.navbar-inverse .cke_chrome { border: none; } @@ -29,7 +28,7 @@ box-shadow: none; } -/* ---- OpenERP Style ---- */ +/* ---- OpenERP Style ---- {{{ */ .oe_website_editorbar { position: fixed; top: 0; @@ -86,6 +85,10 @@ display: none; } +.cke_editable .css_editable_mode_hidden { + display: none; +} + .oe_carousel_options { cursor: pointer; position: absolute; @@ -100,7 +103,7 @@ top: 2px; } -/* ---- EDITOR BAR ---- */ +/* ---- EDITOR BAR ---- {{{ */ table.editorbar-panel { cursor: pointer; width: 100%; @@ -112,6 +115,7 @@ table.editorbar-panel td.selected { background-color: #b1c9d9; } +/* ---- TRANSLATIONS ---- {{{ */ .oe_translate_or { color: white; padding: 0 0 0 1em; @@ -142,15 +146,16 @@ table.editorbar-panel td.selected { background: #ffffb6; } +/* ---- MENU ---- {{{ */ div.oe_menu_buttons { top: -8px; right: -8px; } -ul.oe_menu_editor .icon-home { +ul.oe_menu_editor .fa-home { display: none; } -ul.oe_menu_editor > li:first-child .icon-home { +ul.oe_menu_editor > li:first-child > div > span > .fa-home { display: block; } ul.oe_menu_editor .oe_menu_placeholder { @@ -168,11 +173,12 @@ ul.oe_menu_editor .disclose { display: none; } -/* ---- RTE ---- */ -.oe_editable .btn { +/* ---- RTE ---- {{{ */ +.oe_editable .btn, .btn.oe_editable { -webkit-user-select: auto; -moz-user-select: auto; user-select: auto; + cursor: text !important; } .modal-dialog.select-image { @@ -187,6 +193,10 @@ ul.oe_menu_editor .disclose { margin-bottom: 0.5em; } +.modal-footer { + text-align: left; +} + .modal.nosave .modal-footer button.save { display: none; } @@ -195,30 +205,42 @@ ul.oe_menu_editor .disclose { visibility: visible !important; } -.cke_widget_wrapper { - position: static !important; +.existing-attachments .pager .disabled { + display: none; } -.cke_widget_drag_handler_container { - display: none !important; +.cke_widget_wrapper { + position: static !important; } .cke_widget_editable:empty:after { - content: " "; + opacity: 0.3; white-space: pre-wrap; } +.cke_widget_editable:not([placeholder]):empty::after { + content: " "; +} +.cke_widget_editable[placeholder]:not(:focus):empty::after { + content: attr(placeholder); +} .oe_carlos_danger { outline: 1px solid red !important; background-color: #ffd9dd !important; } -/* ---- SNIPPET EDITOR ---- */ +.hover-edition-button { + position: absolute; + top: 0; + left: 0; + z-index: 1000; +} + +/* ---- SNIPPET EDITOR ---- {{{ */ #oe_snippets { position: fixed; left: 0px; right: 0px; - top: 51px; background: #282828; -webkit-box-shadow: 0px 10px 10px -10px black inset; -moz-box-shadow: 0px 10px 10px -10px black inset; @@ -348,7 +370,7 @@ ul.oe_menu_editor .disclose { background: #747474, radial-gradient(rgba(0, 0, 0, 0.25), rgba(0, 0, 0, 0.4)); } -/* ---- SNIPPETS DROP ZONES ---- */ +/* ---- SNIPPETS DROP ZONES ---- {{{ */ .oe_drop_zone.oe_insert { display: block; height: 48px; @@ -380,6 +402,10 @@ ul.oe_menu_editor .disclose { margin: 0px -24px !important; } .oe_drop_zone.oe_insert.oe_overlay { + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + -ms-border-radius: 3px; + -o-border-radius: 3px; border-radius: 3px; background: rgba(153, 0, 255, 0.5); } @@ -405,7 +431,7 @@ ul.oe_menu_editor .disclose { padding-top: 12px; } -/* ---- SNIPPET MANIPULATOR ---- */ +/* ---- SNIPPET MANIPULATOR ---- {{{ */ .oe_overlay { display: none; position: absolute; @@ -433,11 +459,20 @@ ul.oe_menu_editor .disclose { box-shadow: 0px 0px 0px 1px rgba(255, 255, 255, 0.3), 0px 0px 0px 1px rgba(255, 255, 255, 0.3) inset; border-color: rgba(0, 0, 0, 0.5); } -.oe_overlay.oe_active .oe_handle { +.oe_overlay .oe_handle { display: block !important; pointer-events: auto; + position: absolute; + top: 50%; + left: 50%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + width: 12px; + height: 12px; + margin: -2px; } -.oe_overlay.oe_active .oe_handle:before { +.oe_overlay .oe_handle:before { position: relative; top: 50%; left: 50%; @@ -462,23 +497,49 @@ ul.oe_menu_editor .disclose { -o-transition: background 100ms linear; transition: background 100ms linear; } -.oe_overlay.oe_active .oe_handle:hover:before { +.oe_overlay .oe_handle:hover:before { background: rgba(0, 0, 0, 0.5); color: white; -webkit-box-shadow: 0 0 5px 3px rgba(255, 255, 255, 0.7); -moz-box-shadow: 0 0 5px 3px rgba(255, 255, 255, 0.7); box-shadow: 0 0 5px 3px rgba(255, 255, 255, 0.7); } -.oe_overlay.oe_active .oe_handle.e:before { +.oe_overlay .oe_handle.e { + left: auto; + top: 2px; + height: 100%; + right: -4px; + cursor: w-resize; +} +.oe_overlay .oe_handle.e:before { content: "\F061"; } -.oe_overlay.oe_active .oe_handle.s:before { +.oe_overlay .oe_handle.s { + top: auto; + left: 2px; + width: 100%; + bottom: -4px; + cursor: n-resize; +} +.oe_overlay .oe_handle.s:before { content: "\F063"; } -.oe_overlay.oe_active .oe_handle.w:before { +.oe_overlay .oe_handle.w { + top: 2px; + height: 100%; + left: -4px; + cursor: e-resize; +} +.oe_overlay .oe_handle.w:before { content: "\F060"; } -.oe_overlay.oe_active .oe_handle.n:before { +.oe_overlay .oe_handle.n { + left: 2px; + width: 100%; + top: -4px; + cursor: s-resize; +} +.oe_overlay .oe_handle.n:before { content: "\F062"; } .oe_overlay .icon.btn { @@ -520,36 +581,6 @@ ul.oe_menu_editor .disclose { margin: -4px; } -.oe_handle.n { - left: 2px; - width: 100%; - top: 0px; - cursor: s-resize; -} - -.oe_handle.e { - left: auto; - top: 2px; - height: 100%; - right: 0px; - cursor: w-resize; -} - -.oe_handle.s { - top: auto; - left: 2px; - width: 100%; - bottom: 0px; - cursor: n-resize; -} - -.oe_handle.w { - top: 2px; - height: 100%; - left: 0px; - cursor: e-resize; -} - .s-resize-important, .s-resize-important * { cursor: s-resize !important; } @@ -570,7 +601,7 @@ ul.oe_menu_editor .disclose { cursor: move !important; } -/* ---- MOBILE PREVIEW ---- */ +/* ---- MOBILE PREVIEW ---- {{{ */ .oe_mobile_preview.modal .modal-content { height: 660px; background-color: black; @@ -626,7 +657,7 @@ ul.oe_menu_editor .disclose { background-color: black; } -/* ---- SEO TOOLS ---- */ +/* ---- SEO TOOLS ---- {{{ */ .oe_seo_configuration .modal-dialog { width: 80%; } @@ -645,10 +676,9 @@ ul.oe_menu_editor .disclose { border-radius: 0.4em; } -/* ---- ACE EDITOR ---- */ +/* ---- ACE EDITOR ---- {{{ */ .oe_ace_view_editor { position: fixed; - top: 51px; right: 0; z-index: 1000; height: 100%; @@ -678,7 +708,6 @@ ul.oe_menu_editor .disclose { position: absolute; top: 30px; right: 0; - bottom: 51px; left: 0; } .oe_ace_view_editor .ace_editor .ace_gutter { @@ -694,7 +723,7 @@ ul.oe_menu_editor .disclose { opacity: 0; } -/* ---- EDITOR TOUR ---- */ +/* ---- EDITOR TOUR ---- {{{ */ div.tour-backdrop { z-index: 2009; } diff --git a/addons/website/static/src/css/editor.sass b/addons/website/static/src/css/editor.sass index 918714ea92f..6cc590bf6b2 100644 --- a/addons/website/static/src/css/editor.sass +++ b/addons/website/static/src/css/editor.sass @@ -1,21 +1,6 @@ -@charset "utf-8" +@import "common" -@import "compass/css3" -@import "compass/css3/user-interface" -@import "compass/css3/transition" - -//smartphones, iPhone, portrait 480x320 phones -$smart_phone: 320px -//portrait e-readers (Nook/Kindle), smaller tablets @ 600 or @ 640 wide.*/ -$small-tablet: 481px -//portrait tablets, portrait iPad, landscape e-readers, landscape 800x480 or 854x480 phones -$portrait-tablet: 641px -//tablet, landscape iPad, lo-res laptops ands desktops -$tablet: 961px -//big landscape tablets, laptops, and desktops -$desktop: 1025px - -/* ---- CKEditor Minimal Reset ---- */ +/* ---- CKEditor Minimal Reset ---- {{{ */ .navbar.navbar-inverse .cke_chrome border: none @@ -39,7 +24,9 @@ $desktop: 1025px border: none +box-shadow(none) -/* ---- OpenERP Style ---- */ +// }}} + +/* ---- OpenERP Style ---- {{{ */ .oe_website_editorbar position: fixed @@ -83,6 +70,9 @@ $desktop: 1025px .css_non_editable_mode_hidden display: none +.cke_editable .css_editable_mode_hidden + display: none + .oe_carousel_options cursor: pointer position: absolute @@ -95,7 +85,9 @@ $desktop: 1025px position: relative !important top: +2px -/* ---- EDITOR BAR ---- */ +// }}} + +/* ---- EDITOR BAR ---- {{{ */ table.editorbar-panel cursor: pointer @@ -105,7 +97,10 @@ table.editorbar-panel td.selected background-color: #b1c9d9 -// ---- TRANSLATIONS ---- {{{ +// }}} + +/* ---- TRANSLATIONS ---- {{{ */ + .oe_translate_or color: white padding: 0 0 0 1em @@ -122,17 +117,19 @@ table.editorbar-panel padding: 0 10px .oe_translatable_todo background: rgb(255, 255, 182) + // }}} -// -------- MENU -------- {{{ +/* ---- MENU ---- {{{ */ + div.oe_menu_buttons top: -8px right: -8px ul.oe_menu_editor - .icon-home + .fa-home display: none - > li:first-child .icon-home + > li:first-child > div > span > .fa-home display: block .oe_menu_placeholder outline: 1px dashed #4183C4 @@ -147,12 +144,13 @@ ul.oe_menu_editor // }}} -/* ---- RTE ---- */ +/* ---- RTE ---- {{{ */ // bootstrap makes .btn elements unselectable -> RTE double-click can't know // about them either -.oe_editable .btn - @include user-select(auto) +.oe_editable .btn, .btn.oe_editable + +user-select(auto) + cursor: text !important .modal-dialog.select-image width: 80% @@ -164,6 +162,9 @@ ul.oe_menu_editor .modal .image-preview margin-bottom: 0.5em +.modal-footer + text-align: left + .modal.nosave .modal-footer button.save display: none @@ -171,30 +172,48 @@ ul.oe_menu_editor display: inline-block !important visibility: visible !important +.existing-attachments .pager .disabled + display: none + // wrapper positioned relatively for drag&drop widget which is disabled below. // Breaks completely horribly crazy products listing page, so take it out. .cke_widget_wrapper position: static !important -// "remove" drag & drop of CKE widgets -.cke_widget_drag_handler_container - display: none !important + // prevent inline widgets from entirely disappearing when their (textual) // content is removed -.cke_widget_editable:empty:after - content: " " - white-space: pre-wrap +.cke_widget_editable + // basic config + &:empty:after + opacity: 0.3 + white-space: pre-wrap + // no @placeholder -> just add some padding + &:not([placeholder]):empty::after + content: " " + // with placeholder and when not (yet) focused -> display placeholder + &[placeholder]:not(:focus):empty::after + content: attr(placeholder) .oe_carlos_danger outline: 1px solid red !important background-color: #ffd9dd !important -/* ---- SNIPPET EDITOR ---- */ +.hover-edition-button + position: absolute + top: 0 + left: 0 + // This z-index is due to .navbar of bootstrap + z-index: 1000 + +// }}} + +/* ---- SNIPPET EDITOR ---- {{{ */ #oe_snippets position: fixed left: 0px right: 0px - top: 51px + // top property is set programmatically background: rgb(40,40,40) +box-shadow(0px 10px 10px -10px black inset) z-index: 1010 @@ -277,7 +296,9 @@ ul.oe_menu_editor #oe_snippets .oe_snippet_thumbnail @include background(#747474, radial-gradient(rgba(0,0,0,0.25),rgba(0,0,0,0.4))) -/* ---- SNIPPETS DROP ZONES ---- */ +// }}}} + +/* ---- SNIPPETS DROP ZONES ---- {{{ */ .oe_drop_zone.oe_insert display: block @@ -303,7 +324,7 @@ ul.oe_menu_editor position: relative margin: 0px -24px !important &.oe_overlay - border-radius: 3px + +border-radius(3px) //@include background-image( repeating-linear-gradient(45deg, rgba(255,255,255,.1) ,rgba(255,255,255,.1) 35px, rgba(0,0,0,.1) 35px, rgba(0,0,0,.1) 75px)) //background-size: 100px 100px background: rgba(153, 0, 255,.5) @@ -324,7 +345,9 @@ ul.oe_menu_editor margin-bottom: 32px padding-top: 12px -/* ---- SNIPPET MANIPULATOR ---- */ +// }}} + +/* ---- SNIPPET MANIPULATOR ---- {{{ */ .oe_overlay display: none @@ -341,37 +364,66 @@ ul.oe_menu_editor border-width: 1px +box-shadow(0px 0px 0px 1px rgba(255,255,255,0.3), 0px 0px 0px 1px rgba(255,255,255,0.3) inset) border-color: rgba(0, 0, 0, 0.5) - .oe_handle - display: block !important - pointer-events: auto - &:before - position: relative - top: 50% - left: 50% - display: block - background: rgba(255, 255, 255, 1) - border: solid 1px rgba(0, 0, 0, .2) - +border-radius(5px) - width: 18px - height: 18px - margin: -9px - padding-left: 1px - font-size: 14px - line-height: 14px - font-family: FontAwesome - color: rgba(0,0,0,.5) - @include transition(background 100ms linear) - &:hover:before - background: rgba(0, 0, 0, .5) - color: #fff - +box-shadow(0 0 5px 3px rgba(255,255,255,.7)) - .oe_handle.e:before + .oe_handle + display: block !important + pointer-events: auto + position: absolute + top: 50% + left: 50% + +box-sizing(border-box) + width: 12px + height: 12px + margin: -2px + &:before + position: relative + top: 50% + left: 50% + display: block + background: rgba(255, 255, 255, 1) + border: solid 1px rgba(0, 0, 0, .2) + +border-radius(5px) + width: 18px + height: 18px + margin: -9px + padding-left: 1px + font-size: 14px + line-height: 14px + font-family: FontAwesome + color: rgba(0,0,0,.5) + @include transition(background 100ms linear) + &:hover:before + background: rgba(0, 0, 0, .5) + color: #fff + +box-shadow(0 0 5px 3px rgba(255,255,255,.7)) + .oe_handle.e + left: auto + top: 2px + height: 100% + right: -4px + cursor: w-resize + &:before content: "\F061" - .oe_handle.s:before + .oe_handle.s + top: auto + left: 2px + width: 100% + bottom: -4px + cursor: n-resize + &:before content: "\F063" - .oe_handle.w:before + .oe_handle.w + top: 2px + height: 100% + left: -4px + cursor: e-resize + &:before content: "\F060" - .oe_handle.n:before + .oe_handle.n + left: 2px + width: 100% + top: -4px + cursor: s-resize + &:before content: "\F062" .icon.btn @@ -405,29 +457,6 @@ ul.oe_menu_editor height: 8px margin: -4px -.oe_handle.n - left: 2px - width: 100% - top: 0px - cursor: s-resize -.oe_handle.e - left: auto - top: 2px - height: 100% - right: 0px - cursor: w-resize -.oe_handle.s - top: auto - left: 2px - width: 100% - bottom: 0px - cursor: n-resize -.oe_handle.w - top: 2px - height: 100% - left: 0px - cursor: e-resize - .s-resize-important, .s-resize-important * cursor: s-resize !important .n-resize-important, .n-resize-important * @@ -439,8 +468,9 @@ ul.oe_menu_editor .move-important, .move-important * cursor: move !important +// }}} -/* ---- MOBILE PREVIEW ---- */ +/* ---- MOBILE PREVIEW ---- {{{ */ $mobile_preview_background: #000000 $mobile_preview_border: #1C1F1F @@ -483,7 +513,9 @@ $icon_close: #E00101 .modal-footer background-color: $mobile_preview_background -/* ---- SEO TOOLS ---- */ +// }}} + +/* ---- SEO TOOLS ---- {{{ */ $remove_color: $icon_close $in_title_color: #5cb85c @@ -501,19 +533,16 @@ $highlighted_text_color: #ffffff padding: .2em .4em .2em .5em +border-radius(.4em) -/* ---- ACE EDITOR ---- */ +// }}} + +/* ---- ACE EDITOR ---- {{{ */ $editorbar_height: 30px // TODO Fix => might break with themes -$navbar_height: 51px -@mixin editor-font - font-size: 14px - font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', monospace - line-height: normal .oe_ace_view_editor position: fixed - top: $navbar_height + // top property is set programmatically right: 0 z-index: 1000 height: 100% @@ -535,7 +564,7 @@ $navbar_height: 51px position: absolute top: $editorbar_height right: 0 - bottom: $navbar_height + // bottom property is set programmatically left: 0 .ace_gutter cursor: ew-resize @@ -545,9 +574,15 @@ $navbar_height: 51px z-index: -1000 +opacity(0) -/* ---- EDITOR TOUR ---- */ +// }}} + +/* ---- EDITOR TOUR ---- {{{ */ + div.tour-backdrop z-index: 2009 .popover.tour z-index: 2010 + +// }}} + // vim:tabstop=4:shiftwidth=4:softtabstop=4:fdm=marker: diff --git a/addons/website/static/src/css/website.backend.css b/addons/website/static/src/css/website.backend.css new file mode 100644 index 00000000000..e6601788bcc --- /dev/null +++ b/addons/website/static/src/css/website.backend.css @@ -0,0 +1,6 @@ +.oe_WidgetWebsiteButton .btn-danger .oe_publish { + display: none; +} +.oe_WidgetWebsiteButton .btn-success .oe_unpublish { + display: none; +}
\ No newline at end of file diff --git a/addons/website/static/src/css/website.css b/addons/website/static/src/css/website.css index 99ba04e2a96..bf261cf905a 100644 --- a/addons/website/static/src/css/website.css +++ b/addons/website/static/src/css/website.css @@ -1,4 +1,3 @@ -@charset "utf-8"; /* THIS CSS FILE IS FOR WEBSITE THEMING CUSTOMIZATION ONLY * * css for editor buttons, openerp widget included in the website and other @@ -192,6 +191,8 @@ h1.text-muted, h2.text-muted, h3.text-muted { /* ----- BOOTSTRAP HACK FOR STICKY FOOTER ----- */ html, body, #wrapwrap { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; box-sizing: border-box; height: 100%; } @@ -201,7 +202,7 @@ html, body, #wrapwrap { width: 100%; } -header, #wrap, footer { +header, main, footer { display: table-row; } @@ -222,6 +223,10 @@ footer { width: 100%; } +.row { + min-height: 32px; +} + /* ---- HACK FOR COVERING UP CK EDITOR BOGUS P INSERTION --- */ .oe_structure.oe_editable.oe_empty:empty, .oe_editable[data-oe-type=html]:empty, .oe_structure.oe_editable.oe_empty > .oe_drop_zone.oe_insert:only-child, [data-oe-type=html] > .oe_drop_zone.oe_insert:only-child { background-image: url("/website/static/src/img/drag_here.png"); @@ -257,6 +262,78 @@ footer { margin-bottom: 0px; } +/* ---- HOMEPAGE THEME CUSTOMIZATION ---- */ +.nav-hierarchy { + padding-left: 16px; +} + +#themes-list .well { + padding: 0 0 20px 0; +} + +/* -- Hack for removing double scrollbar from mobile preview -- */ +div#mobile-preview.modal { + overflow: hidden; + overflow-y: hidden; +} + +ul.nav-stacked > li > a { + padding: 2px 15px; +} + +#customize-menu .dropdown-header { + text-transform: uppercase; +} + +/* ---- PUBLISH ---- */ +.css_published .btn-danger, .css_published .css_publish { + display: none; +} + +.css_unpublished .btn-success, .css_unpublished .css_unpublish { + display: none; +} + +[data-publish='off'] { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50); + opacity: 0.5; +} + +/* ---- END of PUBLISH ---- */ +::-moz-selection { + background: rgba(150, 150, 220, 0.3); +} + +::selection { + background: rgba(150, 150, 220, 0.3); +} + +.logo-img { + width: 220px; +} + +.oe_demo { + position: relative; +} +.oe_demo img { + width: 100%; +} +.oe_demo div { + position: absolute; + left: 0; + background-color: rgba(0, 0, 0, 0.4); + opacity: 0.85; + bottom: 0px; + width: 100%; + padding: 7px; + color: white; + font-weight: bold; +} +.oe_demo div a { + color: white; +} + +/* ---- SNIPPETS --- */ .carousel-inner .item { height: 100%; background-size: 100%; @@ -271,30 +348,27 @@ footer { .carousel.oe_big .carousel-inner { height: 450px; } +.carousel .carousel-caption { + left: auto; + right: auto; + bottom: 10%; + text-align: left; + padding: 20px; + background: rgba(0, 0, 0, 0.4); +} .carousel .carousel-caption h1, .carousel .carousel-caption h2, .carousel .carousel-caption h3 { margin-top: 10px; margin-bottom: 10px; } .carousel .carousel-image { - right: 10%; + right: 50%; left: 50%; top: 5%; bottom: 5%; position: absolute; - text-align: center; -} -.carousel .carousel-image img { - height: 100%; + max-height: 90%; margin: 0 auto; } -.carousel .item.text_image .carousel-caption { - left: auto; - right: auto; - bottom: 10%; - text-align: left; - padding: 20px; - background: rgba(0, 0, 0, 0.4); -} .carousel .item.text_only .carousel-image { display: none !important; } @@ -302,18 +376,14 @@ footer { left: 10%; right: 10%; top: 10%; + bottom: auto; text-align: center; + background: transparent; } .carousel .item.image_text .carousel-caption { - left: auto; - text-align: left; right: 10%; - bottom: 10%; - padding: 20px; - background: rgba(0, 0, 0, 0.4); } .carousel .item.image_text .carousel-image { - right: 50%; left: 10%; } .carousel .carousel-control { @@ -331,79 +401,71 @@ footer { right: 10px; } -/* ---- HOMEPAGE THEME CUSTOMIZATION ---- */ -.dark { - background: #eff8f8; - background: rgba(100, 200, 200, 0.14); - -webkit-box-shadow: 0px 5px 9px -7px rgba(0, 0, 255, 0.5) inset, 0px -3px 9px -7px rgba(0, 0, 255, 0.5) inset; - -moz-box-shadow: 0px 5px 9px -7px rgba(0, 0, 255, 0.5) inset, 0px -3px 9px -7px rgba(0, 0, 255, 0.5) inset; - box-shadow: 0px 5px 9px -7px rgba(0, 0, 255, 0.5) inset, 0px -3px 9px -7px rgba(0, 0, 255, 0.5) inset; -} - -.nav-hierarchy { - padding-left: 16px; -} - -#themes-list .well { - padding: 0 0 20px 0; -} - /* Parallax Theme */ -.parallax_quote { - background: url("/website/static/src/img/parallax/quote.png") center center no-repeat fixed; - background-size: contain; -} -.parallax_quote .carousel-indicators li { +div.carousel[data-snippet-id="slider"] .carousel-indicators li { border: 1px solid grey; } -.parallax_quote .carousel-indicators .active { +div.carousel[data-snippet-id="slider"] .carousel-indicators .active { background-color: grey; } .parallax { position: relative; background-size: 100%; + display: table; + width: 100%; + min-height: 100px; } .parallax.oe_small { - height: 200px; + min-height: 200px; } .parallax.oe_medium { - height: 300px; + min-height: 300px; } .parallax.oe_big { - height: 450px; + min-height: 450px; +} +.parallax > div { + display: table-cell; + vertical-align: middle; + padding: 32px 0; } -/* -- Hack for removing double scrollbar from mobile preview -- */ -div#mobile-preview.modal { - overflow: hidden; - overflow-y: hidden; +/* Background */ +.oe_dark { + background: #eff8f8; + background: rgba(100, 200, 200, 0.14); + -webkit-box-shadow: 0px 5px 9px -7px rgba(0, 0, 255, 0.5) inset, 0px -3px 9px -7px rgba(0, 0, 255, 0.5) inset; + -moz-box-shadow: 0px 5px 9px -7px rgba(0, 0, 255, 0.5) inset, 0px -3px 9px -7px rgba(0, 0, 255, 0.5) inset; + box-shadow: 0px 5px 9px -7px rgba(0, 0, 255, 0.5) inset, 0px -3px 9px -7px rgba(0, 0, 255, 0.5) inset; } -ul.nav-stacked > li > a { - padding: 2px 15px; +.oe_black { + background-color: rgba(0, 0, 0, 0.9); } -#customize-menu .dropdown-header { - text-transform: uppercase; +.oe_green { + background-color: rgba(0, 128, 0, 0.8); } -/* ---- PUBLISH ---- */ -.css_unpublish, .css_publish { - display: none; +.oe_red { + background-color: rgba(255, 0, 0, 0.8); } -.css_published .css_unpublish, .css_unpublished .css_publish { - display: block; +.oe_img_bg { + background-size: 100%; } -[data-publish='off'] { - opacity: 0.5; +.texttop { + vertical-align: top; } -/* ---- END of PUBLISH ---- */ -::selection { - background: rgba(150, 150, 220, 0.3); +table.well tr th { + text-align: right; + padding-right: 10px; +} +table.well tr td { + padding-right: 5px; } .logo-img { @@ -430,3 +492,64 @@ ul.nav-stacked > li > a { .oe_demo div a { color: white; } + +address .fa.fa-mobile-phone { + margin: 0 3px 0 2px; +} +address .fa.fa-file-text-o { + margin-right: 1px; +} + +span[data-oe-type="monetary"] { + white-space: nowrap; +} + +.oe_template_fallback { + -webkit-column-count: 3; + -moz-column-count: 3; + -ms-column-count: 3; + -o-column-count: 3; + column-count: 3; +} + +.bs-callout { + margin: 20px 0; + padding: 20px; + border-left: 3px solid #eeeeee; +} + +.bs-callout h4 { + margin-top: 0; + margin-bottom: 5px; +} + +.bs-callout p:last-child { + margin-bottom: 0; +} + +.bs-callout-danger { + background-color: #fdf7f7; + border-color: #eed3d7; +} + +.bs-callout-danger h4 { + color: #b94a48; +} + +.bs-callout-warning { + background-color: #faf8f0; + border-color: #faebcc; +} + +.bs-callout-warning h4 { + color: #8a6d3b; +} + +.bs-callout-info { + background-color: #f4f8fa; + border-color: #bce8f1; +} + +.bs-callout-info h4 { + color: #34789a; +} diff --git a/addons/website/static/src/css/website.sass b/addons/website/static/src/css/website.sass index f947a163a28..ccd11d3396f 100644 --- a/addons/website/static/src/css/website.sass +++ b/addons/website/static/src/css/website.sass @@ -1,5 +1,7 @@ @charset "utf-8" +@import "common" + /* * THIS CSS FILE IS FOR WEBSITE THEMING CUSTOMIZATION ONLY * @@ -103,14 +105,8 @@ /* Extra Styles */ img.shadow - -webkit-border-radius: 3px - -moz-border-radius: 3px - -ms-border-radius: 3px - -o-border-radius: 3px - border-radius: 3px - -webkit-box-shadow: 0px 3px 8px rgba(0, 0, 0, 0.2) - -moz-box-shadow: 0px 3px 8px rgba(0, 0, 0, 0.2) - box-shadow: 0px 3px 8px rgba(0, 0, 0, 0.2) + +border-radius(3px) + +box-shadow(0px 3px 8px rgba(0, 0, 0, 0.2)) margin: 0 auto h1.text-muted, h2.text-muted, h3.text-muted @@ -124,14 +120,14 @@ h1.text-muted, h2.text-muted, h3.text-muted /* ----- BOOTSTRAP HACK FOR STICKY FOOTER ----- */ html,body, #wrapwrap - box-sizing: border-box + +box-sizing(border-box) height: 100% #wrapwrap display: table width: 100% -header, #wrap, footer +header, main, footer display: table-row footer @@ -149,6 +145,9 @@ footer float: left width: 100% +.row + min-height: 32px + /* ---- HACK FOR COVERING UP CK EDITOR BOGUS P INSERTION --- */ .oe_structure.oe_editable.oe_empty:empty, .oe_editable[data-oe-type=html]:empty, .oe_structure.oe_editable.oe_empty > .oe_drop_zone.oe_insert:only-child, [data-oe-type=html] > .oe_drop_zone.oe_insert:only-child @@ -186,6 +185,64 @@ footer // .nav > li a // display: block +/* ---- HOMEPAGE THEME CUSTOMIZATION ---- */ + +.nav-hierarchy + padding-left: 16px + +#themes-list .well + padding: 0 0 20px 0 + +/* -- Hack for removing double scrollbar from mobile preview -- */ +div#mobile-preview.modal + overflow: hidden + overflow-y: hidden + +ul.nav-stacked > li > a + padding: 2px 15px + +#customize-menu .dropdown-header + text-transform: uppercase + +/* ---- PUBLISH ---- */ +.css_published + .btn-danger, .css_publish + display: none +.css_unpublished + .btn-success, .css_unpublish + display: none + +[data-publish='off'] + +opacity(0.5) + +/* ---- END of PUBLISH ---- */ + +@include selection + background: rgba(150, 150, 220, 0.3) + +.logo-img + width: 220px + + +.oe_demo + position: relative + img + width: 100% + div + position: absolute + left: 0 + background-color: rgba(0,0,0,0.4) + opacity: 0.85 + bottom: 0px + width: 100% + padding: 7px + color: white + font-weight: bold + a + color: white + +/* ---- SNIPPETS --- */ + .carousel-inner .item height: 100% background-size: 100% @@ -198,27 +255,23 @@ footer &.oe_big .carousel-inner height: 450px .carousel-caption + left: auto + right: auto + bottom: 10% + text-align: left + padding: 20px + background: rgba(0, 0, 0, 0.4) h1, h2, h3 margin-top: 10px margin-bottom: 10px .carousel-image - right: 10% + right: 50% left: 50% top: 5% bottom: 5% position: absolute - text-align: center - img - height: 100% - margin: 0 auto - .item.text_image - .carousel-caption - left: auto - right: auto - bottom: 10% - text-align: left - padding: 20px - background: rgba(0, 0, 0, 0.4) + max-height: 90% + margin: 0 auto .item.text_only .carousel-image display: none !important @@ -226,17 +279,13 @@ footer left: 10% right: 10% top: 10% + bottom: auto text-align: center + background: transparent .item.image_text .carousel-caption - left: auto - text-align: left right: 10% - bottom: 10% - padding: 20px - background: rgba(0, 0, 0, 0.4) .carousel-image - right: 50% left: 10% .carousel-control cursor: pointer @@ -249,26 +298,10 @@ footer &.right span right: 10px -/* ---- HOMEPAGE THEME CUSTOMIZATION ---- */ - -.dark - background: #eff8f8 - background: rgba(100, 200, 200, 0.14) - -webkit-box-shadow: 0px 5px 9px -7px rgba(0, 0, 255, 0.5) inset, 0px -3px 9px -7px rgba(0, 0, 255, 0.5) inset - -moz-box-shadow: 0px 5px 9px -7px rgba(0, 0, 255, 0.5) inset, 0px -3px 9px -7px rgba(0, 0, 255, 0.5) inset - box-shadow: 0px 5px 9px -7px rgba(0, 0, 255, 0.5) inset, 0px -3px 9px -7px rgba(0, 0, 255, 0.5) inset - -.nav-hierarchy - padding-left: 16px - -#themes-list .well - padding: 0 0 20px 0 /* Parallax Theme */ -.parallax_quote - background: url('/website/static/src/img/parallax/quote.png') center center no-repeat fixed - background-size: contain +div.carousel[data-snippet-id="slider"] .carousel-indicators li border: 1px solid grey @@ -278,43 +311,48 @@ footer .parallax position: relative background-size: 100% + display: table + width: 100% + min-height: 100px &.oe_small - height: 200px + min-height: 200px &.oe_medium - height: 300px + min-height: 300px &.oe_big - height: 450px -/* -- Hack for removing double scrollbar from mobile preview -- */ -div#mobile-preview.modal - overflow: hidden - overflow-y: hidden - -ul.nav-stacked > li > a - padding: 2px 15px - -#customize-menu .dropdown-header - text-transform: uppercase - -/* ---- PUBLISH ---- */ -.css_unpublish, .css_publish - display: none -.css_published .css_unpublish, .css_unpublished .css_publish - display: block - -[data-publish='off'] - opacity: 0.5 - -/* ---- END of PUBLISH ---- */ + min-height: 450px + > div + display: table-cell + vertical-align: middle + padding: 32px 0 +/* Background */ + +.oe_dark + background: #eff8f8 + background: rgba(100, 200, 200, 0.14) + +box-shadow(0px 5px 9px -7px rgba(0, 0, 255, 0.5) inset, 0px -3px 9px -7px rgba(0, 0, 255, 0.5) inset) +.oe_black + background-color: rgba(0, 0, 0, 0.9) +.oe_green + background-color: rgba(0, 128, 0, 0.8) +.oe_red + background-color: rgba(255, 0, 0, 0.8) +.oe_img_bg + background-size: 100% +.texttop + vertical-align: top -::selection - background: rgba(150, 150, 220, 0.3) +table.well tr + th + text-align: right + padding-right: 10px + td + padding-right: 5px .logo-img width: 220px - .oe_demo position: relative img @@ -331,3 +369,43 @@ ul.nav-stacked > li > a font-weight: bold a color: white + +address + .fa.fa-mobile-phone + margin: 0 3px 0 2px + .fa.fa-file-text-o + margin-right: 1px + +span[data-oe-type="monetary"] + white-space: nowrap + +.oe_template_fallback + @include column-count(3) + +// ---- Callouts from bootstrap doc ---- {{{ +.bs-callout + margin: 20px 0 + padding: 20px + border-left: 3px solid #eee +.bs-callout h4 + margin-top: 0 + margin-bottom: 5px +.bs-callout p:last-child + margin-bottom: 0 + +.bs-callout-danger + background-color: #fdf7f7 + border-color: #eed3d7 +.bs-callout-danger h4 + color: #b94a48 +.bs-callout-warning + background-color: #faf8f0 + border-color: #faebcc +.bs-callout-warning h4 + color: #8a6d3b +.bs-callout-info + background-color: #f4f8fa + border-color: #bce8f1 +.bs-callout-info h4 + color: #34789a +// }}} diff --git a/addons/website/static/src/img/banner/color_splash.jpg b/addons/website/static/src/img/banner/color_splash.jpg Binary files differindex e17ae39e70f..7033d3cb41f 100644 --- a/addons/website/static/src/img/banner/color_splash.jpg +++ b/addons/website/static/src/img/banner/color_splash.jpg diff --git a/addons/website/static/src/img/bglink.png b/addons/website/static/src/img/bglink.png Binary files differnew file mode 100644 index 00000000000..e25f81c8414 --- /dev/null +++ b/addons/website/static/src/img/bglink.png diff --git a/addons/website/static/src/img/content_here.png b/addons/website/static/src/img/content_here.png Binary files differnew file mode 100644 index 00000000000..d236e67a7d0 --- /dev/null +++ b/addons/website/static/src/img/content_here.png diff --git a/addons/website/static/src/img/library/gears.jpg b/addons/website/static/src/img/library/gears.jpg Binary files differdeleted file mode 100644 index 868c2532c41..00000000000 --- a/addons/website/static/src/img/library/gears.jpg +++ /dev/null diff --git a/addons/website/static/src/img/library/gears.png b/addons/website/static/src/img/library/gears.png Binary files differnew file mode 100644 index 00000000000..1d3c7d85ca0 --- /dev/null +++ b/addons/website/static/src/img/library/gears.png diff --git a/addons/website/static/src/js/website.ace.js b/addons/website/static/src/js/website.ace.js index 685ace69467..f3b8a5c5885 100644 --- a/addons/website/static/src/js/website.ace.js +++ b/addons/website/static/src/js/website.ace.js @@ -1,35 +1,34 @@ (function () { 'use strict'; - var globalEditor; - var hash = "#advanced-view-editor"; var website = openerp.website; website.add_template_file('/website/static/src/xml/website.ace.xml'); - website.ready().then(function () { - if (window.location.hash.indexOf(hash) >= 0) { - launch(); - } - }); - - function launch () { - if (globalEditor) { - globalEditor.open(); - } else { - globalEditor = new website.ace.ViewEditor(); - globalEditor.appendTo($(document.body)); - } - } - website.EditorBar.include({ events: _.extend({}, website.EditorBar.prototype.events, { 'click a[data-action=ace]': 'launchAce', }), + start: function () { + var self = this; + this.globalEditor = null; + return this._super.apply(this, arguments).then(function () { + if (window.location.hash.indexOf(hash) >= 0) { + self.launchAce(); + } + }); + }, launchAce: function (e) { - e.preventDefault(); - launch(); + if (e) { + e.preventDefault(); + } + if (this.globalEditor) { + this.globalEditor.open(); + } else { + this.globalEditor = new website.ace.ViewEditor(this); + this.globalEditor.appendTo($(document.body)); + } }, }); @@ -59,8 +58,12 @@ website.ace.ViewOption = openerp.Widget.extend({ template: 'website.ace_view_option', init: function (parent, options) { + var indent = "- "; this.view_id = options.id; this.view_name = options.name; + for (var i = 0; i<options.level; i++) { + this.view_name = indent + this.view_name; + } this._super(parent); }, }); @@ -100,6 +103,10 @@ self.aceEditor.resize(); self.$el.width(width); } + function resizeEditorHeight(height) { + self.$el.css('top', height); + self.$('.ace_editor').css('bottom', height); + } function storeEditorWidth() { window.localStorage.setItem('ace_editor_width', self.$el.width()); } @@ -129,22 +136,55 @@ $('button[data-action=edit]').click(function () { self.close(); }); + this.getParent().on('change:height', this, function (editor) { + resizeEditorHeight(editor.get('height')); + }); resizeEditor(readEditorWidth()); + resizeEditorHeight(this.getParent().get('height')); }, loadViews: function (views) { var self = this; + var $viewList = self.$('#ace-view-list'); + var views = this.buildViewGraph(views); + _.each(views, function (view) { + if (view.id) { + new website.ace.ViewOption(self, view).appendTo($viewList); + self.loadView(view.id); + } + }); + }, + buildViewGraph: function (views) { var activeViews = _.uniq(_.filter(views, function (view) { return view.active; }), false, function (view) { return view.id; }); - var $viewList = self.$('#ace-view-list'); + var index = {}; + var roots = []; _.each(activeViews, function (view) { - if (view.id) { - new website.ace.ViewOption(self, view).appendTo($viewList); - self.loadView(view.id); + index[view.id] = view; + view.children = []; + }); + _.each(index, function (view) { + var parentId = view.inherit_id; + if (parentId && index[parentId]) { + index[parentId].children.push(view); + } else { + roots.push(view); } }); + var result = []; + function visit (node, level) { + node.level = level; + result.push(node); + _.each(node.children, function (child) { + visit(child, level + 1); + }); + } + _.each(roots, function (node) { + visit(node, 0); + }); + return result; }, loadView: function (id) { var viewId = parseInt(id, 10); diff --git a/addons/website/static/src/js/website.backend.js b/addons/website/static/src/js/website.backend.js new file mode 100644 index 00000000000..43347b85cb7 --- /dev/null +++ b/addons/website/static/src/js/website.backend.js @@ -0,0 +1,32 @@ +openerp.website = function(instance) { +var _t = instance.web._t; + +instance.web.form.WidgetWebsiteButton = instance.web.form.AbstractField.extend({ + template: 'WidgetWebsiteButton', + render_value: function() { + this._super(); + this.$("button:first") + .toggleClass("btn-success", this.get_value()) + .toggleClass("btn-danger", !this.get_value()); + this.$("a:first").attr("href", this.view.datarecord.website_url || "/" ); + if (this.node.attrs.class) { + this.$el.addClass(this.node.attrs.class); + } + }, + start: function() { + var self = this; + this._super.apply(this, arguments); + + this.$("button:first").on("click", function () { + console.log("click", !!$(this).hasClass("btn-danger")); + self.set_value(!!$(this).hasClass("btn-danger")); + return self.view.recursive_save(); + }); + }, +}); + +instance.web.form.widgets = instance.web.form.widgets.extend({ + 'website_button': 'instance.web.form.WidgetWebsiteButton', +}); + +}; diff --git a/addons/website/static/src/js/website.editor.js b/addons/website/static/src/js/website.editor.js index 74cf6ab81c0..51f14a0f6d5 100644 --- a/addons/website/static/src/js/website.editor.js +++ b/addons/website/static/src/js/website.editor.js @@ -2,7 +2,7 @@ 'use strict'; var website = openerp.website; - // $.fn.data automatically parses value, '0'|'1' -> 0|1 + var _t = openerp._t; website.add_template_file('/website/static/src/xml/website.editor.xml'); website.dom_ready.done(function () { @@ -14,12 +14,12 @@ $(document).on('click', 'a.js_link2post', function (ev) { ev.preventDefault(); - var form = document.createElement('form'); - form.action = this.pathname; // restrict to same origin - form.method = 'POST'; - // TODO: support this.search as form input fields - $(this).append(form); - form.submit(); + website.form(this.pathname, 'POST'); + }); + + $(document).on('submit', '.cke_editable form', function (ev) { + // Disable form submition in editable mode + ev.preventDefault(); }); $(document).on('hide.bs.dropdown', '.dropdown', function (ev) { @@ -32,11 +32,32 @@ }); }); + /** + * An editing host is an HTML element with @contenteditable=true, or the + * child of a document in designMode=on (but that one's not supported) + * + * https://dvcs.w3.org/hg/editing/raw-file/tip/editing.html#editing-host + */ + function is_editing_host(element) { + return element.getAttribute('contentEditable') === 'true'; + } + /** + * Checks that both the element's content *and the element itself* are + * editable: an editing host is considered non-editable because its content + * is editable but its attributes should not be considered editable + */ + function is_editable_node(element) { + return !(element.data('oe-model') === 'ir.ui.view' + || element.data('cke-realelement') + || is_editing_host(element) + || element.isReadOnly()); + } + function link_dialog(editor) { return new website.editor.RTELinkDialog(editor).appendTo(document.body); } - function image_dialog(editor) { - return new website.editor.RTEImageDialog(editor).appendTo(document.body); + function image_dialog(editor, image) { + return new website.editor.RTEImageDialog(editor, image).appendTo(document.body); } // only enable editors manually @@ -54,18 +75,13 @@ init: function (editor) { editor.on('doubleclick', function (evt) { var element = evt.data.element; - if (element.is('img') - && !element.data('cke-realelement') - && !element.isReadOnly() - && (element.data('oe-model') !== 'ir.ui.view')) { - image_dialog(editor); + if (element.is('img') && is_editable_node(element)) { + image_dialog(editor, element); return; } element = get_selected_link(editor) || evt.data.element; - if (element.isReadOnly() - || !element.is('a') - || element.data('oe-model')) { + if (!(element.is('a') && is_editable_node(element))) { return; } @@ -73,39 +89,6 @@ link_dialog(editor); }, null, null, 500); - var previousSelection; - editor.on('selectionChange', function (evt) { - var selected = evt.data.path.lastElement; - if (previousSelection) { - // cleanup previous selection - $(previousSelection).next().remove(); - previousSelection = null; - } - if (!selected.is('img') - || selected.data('cke-realelement') - || selected.isReadOnly() - || selected.data('oe-model') === 'ir.ui.view') { - return; - } - - // display button - var $el = $(previousSelection = selected.$); - var $btn = $('<button type="button" class="btn btn-primary" contenteditable="false">Edit</button>') - .insertAfter($el) - .click(function (e) { - e.preventDefault(); - e.stopPropagation(); - image_dialog(editor); - }); - - var position = $el.position(); - $btn.css({ - position: 'absolute', - top: $el.height() / 2 + position.top - $btn.outerHeight() / 2, - left: $el.width() / 2 + position.left - $btn.outerWidth() / 2, - }); - }); - //noinspection JSValidateTypes editor.addCommand('link', { exec: function (editor) { @@ -129,13 +112,11 @@ label: 'Link', command: 'link', toolbar: 'links,10', - icon: '/website/static/lib/ckeditor/plugins/link/icons/link.png', }); editor.ui.addButton('Image', { label: 'Image', command: 'image', toolbar: 'insert,10', - icon: '/website/static/lib/ckeditor/plugins/image/icons/image.png', }); editor.setKeystroke(CKEDITOR.CTRL + 76 /*L*/, 'link'); @@ -204,177 +185,186 @@ } }); - CKEDITOR.plugins.add('oeref', { - requires: 'widget', - - init: function (editor) { - editor.widgets.add('oeref', { - editables: { text: '*' }, - - upcast: function (el) { - return el.attributes['data-oe-type'] - && el.attributes['data-oe-type'] !== 'monetary'; - }, - }); - editor.widgets.add('monetary', { - editables: { text: 'span.oe_currency_value' }, - - upcast: function (el) { - return el.attributes['data-oe-type'] === 'monetary'; - } - }); - } - }); - - CKEDITOR.plugins.add('bootstrapcombo', { - requires: 'richcombo', - + CKEDITOR.plugins.add('linkstyle', { + requires: 'panelbutton,floatpanel', init: function (editor) { - var config = editor.config; - - editor.ui.addRichCombo('BootstrapLinkCombo', { - // default title - label: "Links", - // hover - title: "Link styling", - toolbar: 'styles,10', - allowedContent: ['a'], + 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, panel: { - css: [ - '/website/static/lib/bootstrap/css/bootstrap.css', - CKEDITOR.skin.getPath( 'editor' ) - ].concat( config.contentsCss ), - multiSelect: true, + css: '/website/static/lib/bootstrap/css/bootstrap.css', + attributes: { 'role': 'listbox', 'aria-label': label }, }, types: { - 'basic': 'btn-default', - 'primary': 'btn-primary', - 'success': 'btn-success', - 'info': 'btn-info', - 'warning': 'btn-warning', - 'danger': 'btn-danger', + '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: { - 'large': 'btn-lg', - 'default': '', - 'small': 'btn-sm', - 'extra small': 'btn-xs', + 'btn-xs': _t("Extra Small"), + 'btn-sm': _t("Small"), + '': _t("Default"), + 'btn-lg': _t("Large") }, - init: function () { - this.add('', 'Reset'); - this.startGroup("Types"); - for(var type in this.types) { - if (!this.types.hasOwnProperty(type)) { continue; } - var cls = this.types[type]; - var el = _.str.sprintf( - '<span class="btn %s">%s</span>', - cls, type); - this.add(type, el); - } - this.startGroup("Sizes"); - for (var size in this.sizes) { - if (!this.sizes.hasOwnProperty(size)) { continue; } - cls = this.sizes[size]; - - el = _.str.sprintf( - '<span class="btn btn-default %s">%s</span>', - cls, size); - this.add(size, el); - } - this.commit(); - }, onRender: function () { var self = this; editor.on('selectionChange', function (e) { var path = e.data.path, el; - if (!(el = path.contains('a'))) { - self.element = null; + if (!(e = path.contains('a')) || e.isReadOnly()) { self.disable(); return; } self.enable(); - // This is crap, but getting the currently selected - // element from within onOpen absolutely does not - // work, so store the "current" element in the - // widget instead - self.element = el; }); + // no hook where button is available, so wait + // "some time" after render. setTimeout(function () { - // Because I can't find any normal hook where the - // bloody button's bloody element is available self.disable(); - }, 0); + }, 0) + }, + enable: function () { + this.setState(CKEDITOR.TRISTATE_OFF); + }, + disable: function () { + this.setState(CKEDITOR.TRISTATE_DISABLED); }, - onOpen: function () { - this.showAll(); - this.unmarkAll(); - for(var val in this.types) { - if (!this.types.hasOwnProperty(val)) { continue; } - var cls = this.types[val]; - if (!this.element.hasClass(cls)) { continue; } + 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; } - this.mark(val); - break; + $root.find('button[data-type=types].' + type) + .addClass('active'); } - var found; - for(val in this.sizes) { - if (!this.sizes.hasOwnProperty(val)) { continue; } - cls = this.sizes[val]; - if (!cls || !this.element.hasClass(cls)) { continue; } - + for (var size in this.sizes) { + if (!this.sizes.hasOwnProperty(size)) { continue; } + if (!size || !link.hasClass(size)) { continue; } found = true; - this.mark(val); - break; + $root.find('button[data-type=sizes].' + size) + .addClass('active'); } - if (!found && this.element.hasClass('btn')) { - this.mark('default'); + if (!found && link.hasClass('btn')) { + $root.find('button[data-type="sizes"][data-set-class=""]') + .addClass('active'); } }, - onClick: function (value) { + + onBlock: function (panel, block) { + var self = this; + block.autoSize = true; + + 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('saveShapshot'); + editor.fire('saveSnapshot'); - // basic btn setup - var el = this.element; - if (!el.hasClass('btn')) { - el.addClass('btn'); - el.addClass('btn-default'); + var $button = $(button), + $link = $(get_selected_link(editor).$); + if (!$link.hasClass('btn')) { + $link.addClass('btn btn-default'); } - - if (!value) { - this.setClass(this.types); - this.setClass(this.sizes); - el.removeClass('btn'); - } else if (value in this.types) { - this.setClass(this.types, value); - } else if (value in this.sizes) { - this.setClass(this.sizes, value); + 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')); } + this._.panel.hide(); - editor.fire('saveShapshot'); + editor.fire('saveSnapshot'); }, - setClass: function (classMap, value) { - var element = this.element; - _(classMap).each(function (cls) { - if (!cls) { return; } - element.removeClass(cls); - }.bind(this)); - - var cls = classMap[value]; - if (cls) { - element.addClass(cls); + + }); + } + }); + + CKEDITOR.plugins.add('oeref', { + requires: 'widget', + + init: function (editor) { + editor.widgets.add('oeref', { + editables: { text: '*' }, + draggable: false, + + upcast: function (el) { + var matches = el.attributes['data-oe-type'] && el.attributes['data-oe-type'] !== 'monetary'; + if (!matches) { return false; } + + if (el.attributes['data-oe-original']) { + while (el.children.length) { + el.children[0].remove(); + } + el.add(new CKEDITOR.htmlParser.text( + el.attributes['data-oe-original'] + )); } + return true; + }, + }); + editor.widgets.add('monetary', { + editables: { text: 'span.oe_currency_value' }, + draggable: false, + + upcast: function (el) { + return el.attributes['data-oe-type'] === 'monetary'; } }); - }, + } }); var editor = new website.EditorBar(); @@ -384,15 +374,15 @@ editor.edit(); } }); - $body.css('padding-top', '50px'); // Not working properly: editor.$el.outerHeight()); }; - /* ----- TOP EDITOR BAR FOR ADMIN ---- */ + + /* ----- 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 button[data-action=cancel]': 'cancel', + 'click a[data-action=cancel]': 'cancel', 'click a[data-action=new_page]': 'new_page', }, container: 'body', @@ -408,15 +398,15 @@ 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="icon-check%s"></strong> %s</a></li>', - item.id, item.active ? '' : '-empty', item.name)); + 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#action=website.action_module_website">Install Apps</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'); } ); @@ -432,7 +422,6 @@ }, start: function() { var self = this; - this.saving_mutex = new openerp.Mutex(); this.$('#website-top-edit').hide(); @@ -450,13 +439,27 @@ 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(); + 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); + } }, edit: function () { this.$buttons.edit.prop('disabled', true); @@ -464,7 +467,7 @@ this.$('#website-top-edit').show(); $('.css_non_editable_mode_hidden').removeClass("css_non_editable_mode_hidden"); - this.rte.start_edition(); + this.rte.start_edition().then(this.check_height.bind(this)); }, rte_changed: function () { this.$buttons.save.prop('disabled', false); @@ -513,16 +516,23 @@ if (!_.isArray(failed)) { failed = [failed]; } _(failed).each(function (failure) { + var html = failure.error.exception_type === "except_osv"; + if (html) { + var msg = $("<div/>").text(failure.error.message).html(); + var data = msg.substring(3,msg.length-2).split(/', u'/); + failure.error.message = '<b>' + data[0] + '</b><br/>' + data[1]; + } $(root).find('.' + failure.id) .removeClass(failure.id) .popover({ + html: html, trigger: 'hover', content: failure.error.message, placement: 'auto top', }) // Force-show popovers so users will notice them. .popover('show'); - }) + }); }); }); }, @@ -540,7 +550,20 @@ }); }, cancel: function () { - website.reload(); + new $.Deferred(function (d) { + var $dialog = $(openerp.qweb.render('website.editor.discard')).appendTo(document.body); + $dialog.on('click', '.btn-danger', function () { + d.resolve(); + }).on('hidden.bs.modal', function () { + d.reject(); + }); + d.always(function () { + $dialog.remove(); + }); + $dialog.modal('show'); + }).then(function () { + website.reload(); + }) }, new_page: function (ev) { ev.preventDefault(); @@ -548,9 +571,100 @@ window_title: "New Page", input: "Page Title", }).then(function (val) { - document.location = '/pagenew/' + encodeURI(val); + if (val) { + document.location = '/pagenew/' + encodeURI(val); + } + }); + }, + + /** + * Creates a "hover" button for image and link edition + * + * @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 + * @returns {jQuery} + */ + make_hover_button: function (label, editfn, classes) { + return $(openerp.qweb.render('website.editor.hoverbutton', { + label: label, + classes: classes, + })).hide().appendTo(document.body).click(function (e) { + e.preventDefault(); + e.stopPropagation(); + editfn.call(this, e); }); }, + /** + * For UI clarity, during RTE edition when the user hovers links and + * images a small button should appear to make the capability clear, + * as not all users think of double-clicking the image or link. + */ + setup_hover_buttons: function () { + var editor = this.rte.editor; + var $link_button = this.make_hover_button(_t("Edit Link"), function () { + var sel = new CKEDITOR.dom.element(previous); + editor.getSelection().selectElement(sel); + link_dialog(editor); + $link_button.hide(); + previous = null; + }, 'btn-xs'); + var $image_button = this.make_hover_button(_t("Edit Image"), function () { + image_dialog(editor, new CKEDITOR.dom.element(previous)); + $image_button.hide(); + previous = null; + }); + + // previous is the state of the button-trigger: it's the + // currently-ish hovered element which can trigger a button showing. + // -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', function (e) { + // Back from edit button -> ignore + if (previous && previous === this) { return; } + + var selected = new CKEDITOR.dom.element(this); + if (!is_editable_node(selected)) { + return; + } + + previous = this; + var $selected = $(this); + var position = $selected.offset(); + if ($selected.is('img')) { + var image_top = position.top + parseInt($selected.css('marginTop'), 10); + var image_left = position.left + parseInt($selected.css('marginLeft'), 10); + $link_button.hide(); + // center button on image + $image_button.show().offset({ + top: $selected.outerHeight() / 2 + + image_top + - $image_button.outerHeight() / 2, + left: $selected.outerWidth() / 2 + + image_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 + }) + } + }).on('mouseleave', 'a, img', function (e) { + var current = document.elementFromPoint(e.clientX, e.clientY); + if (current === $link_button[0] || current === $image_button[0]) { + return; + } + $image_button.add($link_button).hide(); + previous = null; + }); + } }); var blocks_selector = _.keys(CKEDITOR.dtd.$block).join(','); @@ -694,9 +808,11 @@ self.setup_editables(root); - // disable firefox's broken table resizing thing - document.execCommand("enableObjectResizing", false, "false"); - document.execCommand("enableInlineTableEditing", false, "false"); + try { + // disable firefox's broken table resizing thing + document.execCommand("enableObjectResizing", false, "false"); + document.execCommand("enableInlineTableEditing", false, "false"); + } catch (e) {} self.trigger('rte:ready'); def.resolve(); @@ -754,7 +870,7 @@ var plugins = [ 'a11yhelp', 'basicstyles', 'blockquote', 'clipboard', 'colorbutton', 'colordialog', 'dialogadvtab', - 'elementspath', 'enterkey', 'entities', 'filebrowser', + 'elementspath', /*'enterkey',*/ 'entities', 'filebrowser', 'find', 'floatingspace','format', 'htmlwriter', 'iframe', 'indentblock', 'indentlist', 'justify', 'list', 'pastefromword', 'pastetext', 'preview', @@ -781,19 +897,16 @@ fillEmptyBlocks: false, filebrowserImageUploadUrl: "/website/attach", // Support for sharedSpaces in 4.x - extraPlugins: 'sharedspace,customdialogs,tablebutton,oeref,bootstrapcombo', + extraPlugins: 'sharedspace,customdialogs,tablebutton,oeref,linkstyle', // Place toolbar in controlled location sharedSpaces: { top: 'oe_rte_toolbar' }, toolbar: [{ - name: 'clipboard', items: [ - "Undo" - ]},{ name: 'basicstyles', items: [ "Bold", "Italic", "Underline", "Strike", "Subscript", "Superscript", "TextColor", "BGColor", "RemoveFormat" ]},{ name: 'span', items: [ - "Link", "Blockquote", "BulletedList", + "Link", "LinkStyle", "Blockquote", "BulletedList", "NumberedList", "Indent", "Outdent" ]},{ name: 'justify', items: [ @@ -803,7 +916,7 @@ "Image", "TableButton" ]},{ name: 'styles', items: [ - "Styles", "BootstrapLinkCombo" + "Styles" ]} ], // styles dropdown in toolbar @@ -827,6 +940,7 @@ events: { 'hidden.bs.modal': 'destroy', 'click button.save': 'save', + 'click button[data-dismiss="modal"]': 'cancel', }, init: function (editor) { this._super(); @@ -840,6 +954,8 @@ save: function () { this.close(); }, + cancel: function () { + }, close: function () { this.$el.modal('hide'); }, @@ -848,7 +964,7 @@ website.editor.LinkDialog = website.editor.Dialog.extend({ template: 'website.editor.dialog.link', events: _.extend({}, website.editor.Dialog.prototype.events, { - 'change .url-source': function (e) { this.changed($(e.target)); }, + 'change :input.url-source': function (e) { this.changed($(e.target)); }, 'mousedown': function (e) { var $target = $(e.target).closest('.list-group-item'); if (!$target.length || $target.hasClass('active')) { @@ -856,7 +972,7 @@ return; } - this.changed($target.find('.url-source')); + this.changed($target.find('.url-source').filter(':input')); }, 'click button.remove': 'remove_link', 'change input#link-text': function (e) { @@ -865,22 +981,45 @@ }), init: function (editor) { this._super(editor); - // url -> name mapping for existing pages - this.pages = Object.create(null); this.text = null; + // Store last-performed request to be able to cancel/abort it. + this.page_exists_req = null; + this.search_pages_req = null; }, start: function () { var self = this; - return $.when( - this.fetch_pages().done(this.proxy('fill_pages')), - this._super() - ).done(function () { - self.bind_data(); + this.$('#link-page').select2({ + minimumInputLength: 1, + placeholder: _t("New or existing page"), + query: function (q) { + $.when( + self.page_exists(q.term), + self.fetch_pages(q.term) + ).then(function (exists, results) { + var rs = _.map(results, function (r) { + return { id: r.url, text: r.name, }; + }); + if (!exists) { + rs.push({ + create: true, + id: q.term, + text: _.str.sprintf(_t("Create page '%s'"), q.term), + }); + } + q.callback({ + more: false, + results: rs + }); + }, function () { + q.callback({more: false, results: []}); + }); + }, }); + 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'); + var $e = this.$('.list-group-item.active .url-source').filter(':input'); var val = $e.val(); if (!val || !$e[0].checkValidity()) { // FIXME: error message @@ -892,15 +1031,18 @@ var done = $.when(); if ($e.hasClass('email-address')) { this.make_link('mailto:' + val, false, val); - } else if ($e.hasClass('existing')) { - self.make_link(val, false, this.pages[val]); - } else if ($e.hasClass('pages')) { - // Create the page, get the URL back - done = $.get(_.str.sprintf( - '/pagenew/%s?noredirect', encodeURI(val))) - .then(function (response) { - self.make_link(response, false, val); - }); + } else if ($e.hasClass('page')) { + var data = $e.select2('data'); + if (!data.create) { + self.make_link(data.id, false, data.text); + } else { + // Create the page, get the URL back + done = $.get(_.str.sprintf( + '/pagenew/%s?noredirect', encodeURI(data.id))) + .then(function (response) { + self.make_link(response, false, data.id); + }); + } } else { this.make_link(val, this.$('input.window-new').prop('checked')); } @@ -923,13 +1065,6 @@ var match, $control; if ((match = /mailto:(.+)/.exec(href))) { $control = this.$('input.email-address').val(match[1]); - } else if (href in this.pages) { - $control = this.$('select.existing').val(href); - } else if ((match = /\/page\/(.+)/.exec(href))) { - var actual_href = '/page/website.' + match[1]; - if (actual_href in this.pages) { - $control = this.$('select.existing').val(actual_href); - } } if (!$control) { $control = this.$('input.url').val(href); @@ -941,30 +1076,38 @@ this.$('input.window-new').prop('checked', new_window); }, changed: function ($e) { - this.$('.url-source').not($e).val(''); + this.$('.url-source').filter(':input').not($e).val('') + .filter(function () { return !!$(this).data('select2'); }) + .select2('data', null); $e.closest('.list-group-item') .addClass('active') .siblings().removeClass('active') .addBack().removeClass('has-error'); }, - fetch_pages: function () { - return openerp.jsonRpc('/web/dataset/call_kw', 'call', { + call: function (method, args, kwargs) { + var self = this; + var req = method + '_req'; + + if (this[req]) { this[req].abort(); } + + return this[req] = openerp.jsonRpc('/web/dataset/call_kw', 'call', { model: 'website', - method: 'list_pages', - args: [null], - kwargs: { - context: website.get_context() - }, + method: method, + args: args, + kwargs: kwargs, + }).always(function () { + self[req] = null; }); }, - fill_pages: function (results) { - var self = this; - var pages = this.$('select.existing')[0]; - _(results).each(function (result) { - self.pages[result.url] = result.name; - - pages.options[pages.options.length] = - new Option(result.name, result.url); + page_exists: function (term) { + return this.call('page_exists', [null, term], { + context: website.get_context(), + }); + }, + fetch_pages: function (term) { + return this.call('search_pages', [null, term], { + limit: 9, + context: website.get_context(), }); }, }); @@ -1110,6 +1253,9 @@ }); return this._super(); }, + cancel: function () { + this.trigger('cancel'); + }, /** * Sets the provided image url as the dialog's value-to-save and @@ -1159,32 +1305,37 @@ }, }); website.editor.RTEImageDialog = website.editor.ImageDialog.extend({ - init: function () { - this._super.apply(this, arguments); + 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) { - var selection = this.editor.getSelection(); - var el = selection && selection.getSelectedElement(); - this.element = null; - - if (el && el.is('img')) { - this.element = el; - _(this.image_styles).each(function (style) { - if (el.hasClass(style)) { - holder.style = style; - } - }); - holder.url = el.getAttribute('src'); + 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 => @@ -1314,6 +1465,11 @@ // a/@href, ...) _(mutations).chain() .filter(function (m) { + // 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 // if attribute is not a class, can't be .cke_focus change @@ -1327,8 +1483,11 @@ // ignore mutation if the *only* change is .cke_focus return change.length !== 1 || change[0] === 'cke_focus'; case 'childList': - // <br type="_moz"> appears when focusing RTE in FF, ignore - return m.addedNodes.length !== 1 || m.addedNodes[0].nodeName !== 'BR'; + // Remove ignorable nodes from addedNodes or removedNodes, + // if either set remains non-empty it's considered to be an + // impactful change. Otherwise it's ignored. + return !!remove_mundane_nodes(m.addedNodes).length || + !!remove_mundane_nodes(m.removedNodes).length; default: return true; } @@ -1338,11 +1497,27 @@ while (node && !$(node).hasClass('oe_editable')) { node = node.parentNode; } - $(m.target).trigger('node_changed'); return node; }) .compact() .uniq() .each(function (node) { $(node).trigger('content_changed'); }) }); + function remove_mundane_nodes(nodes) { + if (!nodes || !nodes.length) { return []; } + + var output = []; + for(var i=0; i<nodes.length; ++i) { + var node = nodes[i]; + if (node.nodeType === document.ELEMENT_NODE) { + if (node.nodeName === 'BR' && node.getAttribute('type') === '_moz') { + // <br type="_moz"> appears when focusing RTE in FF, ignore + continue; + } + } + + output.push(node); + } + return output; + } })(); diff --git a/addons/website/static/src/js/website.js b/addons/website/static/src/js/website.js index b1ba9b801fc..64767d95171 100644 --- a/addons/website/static/src/js/website.js +++ b/addons/website/static/src/js/website.js @@ -13,6 +13,31 @@ }, dict); }; + website.parseQS = function (qs) { + var match, + params = {}, + pl = /\+/g, // Regex for replacing addition symbol with a space + search = /([^&=]+)=?([^&]*)/g; + + while ((match = search.exec(qs))) { + var name = decodeURIComponent(match[1].replace(pl, " ")); + var value = decodeURIComponent(match[2].replace(pl, " ")); + params[name] = value; + } + return params; + }; + + var parsedSearch; + website.parseSearch = function () { + if (!parsedSearch) { + parsedSearch = website.parseQS(window.location.search.substring(1)); + } + return parsedSearch; + }; + website.parseHash = function () { + return website.parseQS(window.location.hash.substring(1)); + }; + /* ----- TEMPLATE LOADING ---- */ var templates_def = $.Deferred().resolve(); website.add_template_file = function(template) { @@ -173,7 +198,7 @@ init: function() {} }, options || {}); - var type = _.intersect(Object.keys(options), ['input', 'textarea', 'select']); + var type = _.intersection(Object.keys(options), ['input', 'textarea', 'select']); type = type.length ? type[0] : 'text'; options.field_type = type; options.field_name = options.field_name || options[type]; @@ -211,6 +236,7 @@ if (field.is('input[type="text"], select')) { field.keypress(function (e) { if (e.which == 13) { + e.preventDefault(); dialog.find('.btn-primary').trigger('click'); } }); @@ -218,6 +244,21 @@ return def; }; + website.form = function (url, method, params) { + var form = document.createElement('form'); + form.action = url; + form.method = method; + _.each(params, function (v, k) { + var param = document.createElement('input'); + param.type = 'hidden'; + param.name = k; + param.value = v; + form.appendChild(param); + }); + document.body.appendChild(form); + form.submit(); + }; + dom_ready.then(function () { /* ----- BOOTSTRAP STUFF ---- */ @@ -225,15 +266,14 @@ /* ----- PUBLISHING STUFF ---- */ $(document).on('click', '.js_publish_management .js_publish_btn', function () { - var $data = $(this).parent(".js_publish_management"); - var self=this; + var $data = $(this).parents(".js_publish_management:first"); + var self=this; openerp.jsonRpc($data.data('controller') || '/website/publish', 'call', {'id': +$data.data('id'), 'object': $data.data('object')}) .then(function (result) { $data.toggleClass("css_unpublished css_published"); - $(self).toggleClass("btn-success btn-danger"); $data.parents("[data-publish]").attr("data-publish", +result ? 'on' : 'off'); }).fail(function (err, data) { - website.error(data, '/web#model='+$data.data('object')+'&id='+$data.data('id')); + website.error(data, '/web#return_label=Website&model='+$data.data('object')+'&id='+$data.data('id')); }); }); diff --git a/addons/website/static/src/js/website.menu.js b/addons/website/static/src/js/website.menu.js index d24c9024023..87dd46dafe9 100644 --- a/addons/website/static/src/js/website.menu.js +++ b/addons/website/static/src/js/website.menu.js @@ -5,26 +5,22 @@ website.menu = {}; website.add_template_file('/website/static/src/xml/website.menu.xml'); - var globalEditor; - website.EditorBar.include({ events: _.extend({}, website.EditorBar.prototype.events, { 'click a[data-action="edit-structure"]': 'editStructure', }), editStructure: function () { var self = this; - globalEditor = self; var context = website.get_context(); openerp.jsonRpc('/web/dataset/call_kw', 'call', { model: 'website.menu', method: 'get_tree', - args: [[context.website_id]], + args: [context.website_id], kwargs: { context: context }, }).then(function (menu) { var result = new website.menu.EditMenuDialog(menu).appendTo(document.body); - self.trigger('tour:menu_editor_dialog_ready'); return result; }); }, @@ -104,7 +100,7 @@ new_window: link[1], }); var $menu = self.$('[data-menu-id="' + id + '"]'); - $menu.find('> div > span').text(menu_obj.name); + $menu.find('.js_menu_label').first().text(menu_obj.name); }); dialog.appendTo(document.body); } else { @@ -155,7 +151,6 @@ template: 'website.menu.dialog.add', init: function (editor, data) { this.data = data; - this.update_mode = !!this.data; return this._super.apply(this, arguments); }, start: function () { @@ -165,14 +160,13 @@ self.bind_data(self.data.name, self.data.url, self.data.new_window); } var $link_text = self.$('#link-text').focus(); - self.$('#link-existing').change(function () { - if (!$link_text.val()) { - $link_text.val($(this).find('option:selected').text()); - $link_text.focus(); - } + self.$('#link-page').change(function (e) { + if ($link_text.val()) { return; } + var data = $(this).select2('data'); + $link_text.val(data.create ? data.id : data.text); + $link_text.focus(); }); }); - globalEditor.trigger('tour:new_menu_entry_dialog_ready'); return result; }, save: function () { @@ -193,7 +187,6 @@ } }, destroy: function () { - globalEditor.trigger('tour:new_menu_entry_dialog_closed'); this._super.apply(this, arguments); }, }); diff --git a/addons/website/static/src/js/website.mobile.js b/addons/website/static/src/js/website.mobile.js index d4630d0a0c2..4acf2d78db8 100644 --- a/addons/website/static/src/js/website.mobile.js +++ b/addons/website/static/src/js/website.mobile.js @@ -20,5 +20,9 @@ document.getElementById("mobile-viewport").src = window.location.origin + window.location.pathname + "#mobile-preview"; this.$el.modal(); }, + destroy: function () { + $('.modal-backdrop').remove(); + this._super(); + }, }); })(); diff --git a/addons/website/static/src/js/website.snippets.animation.js b/addons/website/static/src/js/website.snippets.animation.js index 7277f80ba95..7d3b84f2254 100644 --- a/addons/website/static/src/js/website.snippets.animation.js +++ b/addons/website/static/src/js/website.snippets.animation.js @@ -51,9 +51,10 @@ }, }); - website.snippet.animationRegistry.carousel = website.snippet.Animation.extend({ + website.snippet.animationRegistry.carousel = + website.snippet.animationRegistry.slider = website.snippet.Animation.extend({ start: function () { - this.$target.carousel({interval: false}); + this.$target.carousel({interval: 10000}); }, }); @@ -82,7 +83,7 @@ var self = this; var speed = parseFloat(self.$target.attr("data-scroll-background-ratio") || 0); - if (speed == 1) { + if (speed === 1 || this.$target.css("background-image") === "none") { this.$target.css("background-attachment", "fixed").css("background-position", "0px 0px"); return; } else { @@ -101,10 +102,11 @@ } else { offset = - self.$target.offset().top * speed; } - self.$target.attr("data-scroll-background-offset", offset); + self.$target.attr("data-scroll-background-offset", offset > 0 ? 0 : offset); $(window).scroll(); }; img.src = this.$target.css("background-image").replace(/url\(['"]*|['"]*\)/g, ""); + $(window).scroll(); } }); diff --git a/addons/website/static/src/js/website.snippets.editor.js b/addons/website/static/src/js/website.snippets.editor.js index 07979bfe696..d4f8920663a 100644 --- a/addons/website/static/src/js/website.snippets.editor.js +++ b/addons/website/static/src/js/website.snippets.editor.js @@ -26,7 +26,6 @@ self.snippets.$button.removeClass("hidden"); website.snippet.stop_animation(); website.snippet.start_animation(); - self.trigger('tour:editor_bar_loaded'); }); return this._super.apply(this, arguments); @@ -77,6 +76,7 @@ hack_to_add_snippet_id(); }); + website.snippet.styles = {}; website.snippet.selector = []; website.snippet.BuildingBlock = openerp.Widget.extend({ template: 'website.snippets', @@ -135,27 +135,33 @@ this.$modal = $(openerp.qweb.render('website.snippets_modal')); this.$modal.appendTo("body"); + + $(document).on('click', '.dropdown-submenu a[tabindex]', function (e) { + e.preventDefault(); + }); + + this.getParent().on('change:height', this, function (editor) { + self.$el.css('top', editor.get('height')); + }); }, fetch_snippet_templates: function () { var self = this; - this.style_templates = {}; openerp.jsonRpc("/website/snippets", 'call', {}) .then(function (html) { var $html = $(html); - var $styles = $html.find("#snippet_styles"); - $styles.find("> [data-snippet-id]").each(function () { + var $styles = $html.find("[data-snippet-style-id]"); + $styles.each(function () { var $style = $(this); - var snipped_id = $style.data('snippet-id'); - self.style_templates[snipped_id] = { - 'snipped-id' : snipped_id, + var style_id = $style.data('snippet-style-id'); + website.snippet.styles[style_id] = { + 'snippet-style-id' : style_id, 'selector': $style.data('selector'), - 'class': $style.find(".oe_snippet_class").text(), - 'label': $style.find(".oe_snippet_label").text() + '$el': $style, }; }); - $styles.remove(); + $styles.addClass("hidden"); self.$snippets = $html.find(".tab-content > div > div").addClass("oe_snippet"); self.$el.append($html); @@ -185,35 +191,36 @@ bind_snippet_click_editor: function () { var self = this; var snipped_event_flag = false; - $("body").on('click', "[data-oe-model] [data-snippet-id], [data-oe-model][data-snippet-id]", function (event) { - if (snipped_event_flag) { - return; - } - snipped_event_flag = true; - setTimeout(function () {snipped_event_flag = false;}, 0); - var $target = $(event.currentTarget); - if (self.$active_snipped_id && self.$active_snipped_id.is($target)) { - return; - } - self.make_active($target); - }); - $("[data-oe-model]").on('click', function () { - if (!snipped_event_flag && self.$active_snipped_id && !self.$active_snipped_id.parents("[data-snippet-id]:first")) { - self.make_active(false); - } - }); + $("#wrapwrap").on('click', function (event) { + if (snipped_event_flag) { + return; + } + snipped_event_flag = true; + setTimeout(function () {snipped_event_flag = false;}, 0); + var $target = $(event.srcElement); + if (!$target.attr("data-snippet-id")) { + $target = $target.parents("[data-snippet-id]:first"); + } + if (!$target.attr("data-oe-model") && !$target.parents("[data-oe-model]:first").length) { + $target = false; + } + if (self.$active_snipped_id && self.$active_snipped_id.is($target)) { + return; + } + self.make_active($target); + }); }, - snippet_blur: function ($snipped_id) { - if ($snipped_id) { - if ($snipped_id.data("snippet-editor")) { - $snipped_id.data("snippet-editor").onBlur(); + snippet_blur: function ($snippet) { + if ($snippet) { + if ($snippet.data("snippet-editor")) { + $snippet.data("snippet-editor").onBlur(); } } }, - snippet_focus: function ($snipped_id) { - if ($snipped_id) { - if ($snipped_id.data("snippet-editor")) { - $snipped_id.data("snippet-editor").onFocus(); + snippet_focus: function ($snippet) { + if ($snippet) { + if ($snippet.data("snippet-editor")) { + $snippet.data("snippet-editor").onFocus(); } } }, @@ -226,31 +233,35 @@ } } }, - make_active: function ($snipped_id) { - if ($snipped_id && this.$active_snipped_id && this.$active_snipped_id.get(0) === $snipped_id.get(0)) { + make_active: function ($snippet) { + if ($snippet && this.$active_snipped_id && this.$active_snipped_id.get(0) === $snippet.get(0)) { return; } if (this.$active_snipped_id) { this.snippet_blur(this.$active_snipped_id); this.$active_snipped_id = false; } - if ($snipped_id) { - if(_.indexOf(this.snippets, $snipped_id.get(0)) === -1) { - this.snippets.push($snipped_id.get(0)); + if ($snippet && $snippet.length) { + if(_.indexOf(this.snippets, $snippet.get(0)) === -1) { + this.snippets.push($snippet.get(0)); } - this.$active_snipped_id = $snipped_id; + this.$active_snipped_id = $snippet; this.create_overlay(this.$active_snipped_id); - this.snippet_focus($snipped_id); + this.snippet_focus($snippet); + } + $("#oe_snippets").trigger('snippet-activated', $snippet); + if ($snippet) { + $snippet.trigger('snippet-activated', $snippet); } }, - create_overlay: function ($snipped_id) { - if (typeof $snipped_id.data("snippet-editor") === 'undefined') { - var $targets = this.activate_overlay_zones($snipped_id); + create_overlay: function ($snippet) { + if (typeof $snippet.data("snippet-editor") === 'undefined') { + var $targets = this.activate_overlay_zones($snippet); if (!$targets.length) return; - var editor = website.snippet.editorRegistry[$snipped_id.data("snippet-id")] || website.snippet.editorRegistry.resize; - $snipped_id.data("snippet-editor", new editor(this, $snipped_id)); + var editor = website.snippet.editorRegistry[$snippet.data("snippet-id")] || website.snippet.editorRegistry.resize; + $snippet.data("snippet-editor", new editor(this, $snippet)); } - this.cover_target($snipped_id.data('overlay'), $snipped_id); + this.cover_target($snippet.data('overlay'), $snippet); }, path_eval: function(path){ @@ -301,7 +312,12 @@ $toInsert = $snippet.find('.oe_snippet_body').clone(); $toInsert.removeClass('oe_snippet_body'); - $toInsert.attr('data-snippet-id', snipped_id); + $toInsert.data('src-snippet-id', snipped_id); + if (!$toInsert.data('snippet-id')) { + $toInsert.attr('data-snippet-id', snipped_id); + } else { + snipped_id = $toInsert.data('snippet-id'); + } } else if( action === 'mutate' ){ if (!$snippet.data('selector')) { @@ -325,14 +341,12 @@ } }, out: function(){ - if( action === 'insert'){ + var prev = $toInsert.prev(); + if( action === 'insert' && this === prev[0]){ dropped = false; $toInsert.detach(); } - }, - drop: function(){ - dropped = true; - }, + } }); }, stop: function(ev, ui){ @@ -353,7 +367,18 @@ website.snippet.start_animation(); self.create_overlay($target); - $target.data("snippet-editor").drop_and_build_snippet($target); + if ($snippet.data("snippet-editor")) { + $target.data("snippet-editor").drop_and_build_snippet($target); + } + + $target.find("[data-snippet-id]").each(function () { + var $snippet = $(this); + var snippet_id = $snippet.data("data-snippet-id"); + self.create_overlay($snippet); + if ($snippet.data("snippet-editor")) { + $snippet.data("snippet-editor").drop_and_build_snippet($snippet); + } + }); } else { $target = $(this).data('target'); @@ -364,7 +389,10 @@ snippet.drop_and_build_snippet($target); } } - setTimeout(function () {self.make_active($target);},0); + setTimeout(function () { + $("#oe_snippets").trigger('snippet-dropped', $target); + self.make_active($target); + },0); } else { $toInsert.remove(); if (self.$modal.find('input:not(:checked)').length) { @@ -528,6 +556,205 @@ }); + website.snippet.styleRegistry = {}; + website.snippet.StyleEditor = openerp.Class.extend({ + // initialisation (don't overwrite) + init: function (parent, $target, snippet_id) { + this.parent = parent; + this.$target = $target; + var styles = this.$target.data("snippet-style-ids") || {}; + styles[snippet_id] = this; + this.$target.data("snippet-style-ids", styles); + this.$overlay = this.$target.data('overlay'); + this['snippet-style-id'] = snippet_id; + this.$el = website.snippet.styles[snippet_id].$el.find(">li").clone(); + + this.required = this.$el.data("required"); + + this.set_active(); + this.$el.find('li[data-class] a').on('mouseover mouseout click', _.bind(this._mouse, this)); + this.$target.on('snippet-style-reset', _.bind(this.set_active, this)); + + this.start(); + }, + _mouse: function (event) { + var self = this; + + if (event.type === 'mouseout') { + if (!this.over) return; + this.over = false; + } else if (event.type === 'click') { + this.over = false; + }else { + this.over = true; + } + + var $prev, $next; + if (event.type === 'mouseout') { + $prev = $(event.currentTarget).parent(); + $next = this.$el.find("li[data-class].active"); + } else { + $prev = this.$el.find("li[data-class].active"); + $next = $(event.currentTarget).parent(); + } + if (!$prev.length) { + $prev = false; + } + if ($prev && $prev[0] === $next[0]) { + $next = false; + if (this.required) { + return; + } + } + + var np = {'$next': $next, '$prev': $prev}; + + if (event.type === 'click') { + setTimeout(function () { + self.set_active(); + self.$target.trigger("snippet-style-change", [self, np]); + },0); + this.select(event, {'$next': $next, '$prev': $prev}); + } else { + setTimeout(function () { + self.$target.trigger("snippet-style-preview", [self, np]); + },0); + this.preview(event, np); + } + }, + // start is call just after the init + start: function () { + }, + /* select + * called when a user select an item + * variables: np = {$next, $prev} + * $next is false if they are no next item selected + * $prev is false if they are no previous item selected + */ + select: function (event, np) { + var self = this; + // add or remove html class + if (np.$prev) { + this.$target.removeClass(np.$prev.data('class' || "")); + } + if (np.$next) { + this.$target.addClass(np.$next.data('class') || ""); + } + }, + /* preview + * called when a user is on mouse over or mouse out of an item + * variables: np = {$next, $prev} + * $next is false if they are no next item selected + * $prev is false if they are no previous item selected + */ + preview: function (event, np) { + var self = this; + + // add or remove html class + if (np.$prev) { + this.$target.removeClass(np.$prev.data('class') || ""); + } + if (np.$next) { + this.$target.addClass(np.$next.data('class') || ""); + } + }, + /* 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-class]') + .filter(function () { + var $li = $(this); + return ($li.data('class') && self.$target.hasClass($li.data('class'))); + }) + .first() + .addClass("active"); + this.$el.find('li:has(li[data-class].active)').addClass("active"); + } + }); + + + website.snippet.styleRegistry.size = website.snippet.StyleEditor.extend({ + select: function(event, np) { + this._super(event, np); + this.parent.parent.cover_target(this.$overlay, this.$target); + }, + preview: function (event, np) { + this._super(event, np); + this.parent.parent.cover_target(this.$overlay, this.$target); + } + }); + + website.snippet.styleRegistry.background = website.snippet.StyleEditor.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-class].active.oe_custom_bg").data("src", src); + }, + select: function(event, np) { + var self = this; + this._super(event, np); + if (np.$next) { + if (np.$next.hasClass("oe_custom_bg")) { + var editor = new website.editor.ImageDialog(); + editor.on('start', self, function (o) {o.url = np.$prev && np.$prev.data("src") || np.$next && np.$next.data("src") || "";}); + editor.on('save', self, function (o) { + self._set_bg(o.url); + np.$next.data("src", o.url); + self.$target.trigger("snippet-style-change", [self, np]); + }); + editor.on('cancel', self, function () { + if (!np.$prev || np.$prev.data("src") === "") { + self.$target.removeClass(np.$next.data("class")); + self.$target.trigger("snippet-style-change", [self, np]); + } + }); + editor.appendTo($('body')); + } else { + this._set_bg(np.$next.data("src")); + } + } else { + this._set_bg(false); + this.$target.removeClass(np.$prev.data("class")); + } + }, + preview: function (event, np) { + this._super(event, np); + if (np.$next) { + this._set_bg(np.$next.data("src")); + } + }, + set_active: function () { + var self = this; + var bg = self.$target.css("background-image"); + this.$el.find('li').removeClass("active"); + var $active = this.$el.find('li[data-class]') + .filter(function () { + var $li = $(this); + return ($li.data('src') && bg.indexOf($li.data('src')) >= 0) || + (!$li.data('src') && self.$target.hasClass($li.data('class'))); + }) + .first(); + if (!$active.length) { + $active = this.$target.css("background-image") !== 'none' ? + this.$el.find('li[data-class].oe_custom_bg') : + this.$el.find('li[data-class=""]'); + } + $active.addClass("active"); + this.$el.find('li:has(li[data-class].active)').addClass("active"); + } + }); + + website.snippet.editorRegistry = {}; website.snippet.Editor = openerp.Class.extend({ init: function (parent, dom) { @@ -641,49 +868,22 @@ setTimeout(function () {self.parent.create_overlay(self.$target);},0); }, - _clone: function () { - var self = this; - this.$overlay.on('click', '.oe_snippet_clone', function () { - var $clone = self.$target.clone(false); - self.$target.after($clone); - return false; - }); - }, - load_style_options: function () { var self = this; var $styles = this.$overlay.find('.oe_options'); var $ul = $styles.find('ul:first'); - _.each(this.parent.style_templates, function (val) { + _.each(website.snippet.styles, function (val) { if (!self.parent.dom_filter(val.selector).is(self.$target)) { return; } - var $li = $("<li class='oe_style'/>").data(val); - $li.append($('<a/>').text(val.label)); - $ul.append($li); - if (self.$target.hasClass( "oe_snippet_" + $li.data("snipped-id") )) { - $li.addClass("active"); - } - $styles.removeClass("hidden"); + var Editor = website.snippet.styleRegistry[val['snippet-style-id']] || website.snippet.StyleEditor; + var editor = new Editor(self, self.$target, val['snippet-style-id']); + $ul.prepend(editor.$el); }); - $styles.on('click', 'li.oe_style a', _.bind(this.change_style, this)); - }, - change_style: function (event) { - var $li = $(event.currentTarget).parent(); - var snipped_id = $li.data("snipped-id"); - var active = $li.hasClass("active"); - if (website.snippet.editorRegistry[snipped_id]) { - var snippet = new website.snippet.editorRegistry[snipped_id](this, this.$target); - snippet.drop_and_build_snippet(this.$target); - } - var _class = "oe_snippet_" + snipped_id + " " + ($li.data("class") || ""); - if (active) { - this.$target.removeClass(_class); - } else { - this.$target.addClass(_class); + if ($ul.find("li").length) { + $styles.removeClass("hidden"); } - $li.toggleClass("active"); }, get_parent_block: function () { @@ -709,16 +909,24 @@ */ start: function () { var self = this; - this.$overlay.on('click', '.oe_snippet_remove', function () { - self.onBlur(); - var index = _.indexOf(self.parent.snippets, self.$target.get(0)); - delete self.parent.snippets[index]; - self.$target.remove(); - self.$overlay.remove(); - return false; - }); + this.$overlay.on('click', '.oe_snippet_clone', _.bind(this.on_clone, this)); + this.$overlay.on('click', '.oe_snippet_remove', _.bind(this.on_remove, this)); this._drag_and_drop(); - this._clone(); + }, + + on_clone: function () { + var $clone = this.$target.clone(false); + this.$target.after($clone); + return false; + }, + + on_remove: function () { + this.onBlur(); + var index = _.indexOf(this.parent.snippets, this.$target.get(0)); + delete this.parent.snippets[index]; + this.$target.remove(); + this.$overlay.remove(); + return false; }, /* @@ -747,60 +955,8 @@ * function called just before save vue */ clean_for_save: function () { - - }, - - change_background: function (bg, ul_options, callback) { - var self = this; - this.set_options_background(bg, ul_options); - var $ul = this.$editor.find(ul_options); - var bg_value = (typeof bg === 'string' ? this.$target.find(bg) : $(bg)).css("background-image").replace(/url\(['"]*|['"]*\)/g, ""); - - // bind event on options - var $li = $ul.find("li"); - $li.on('click', function (event) { - if ($(this).data("value")) { - $li.removeClass("active"); - $(this).addClass("active"); - self.$editor.find('input').val(""); - } else { - var editor = new website.editor.ImageDialog(); - editor.on('start', self, function (o) {o.url = bg_value;}); - editor.on('save', self, function (o) { - var $bg = typeof bg === 'string' ? self.$target.find(bg) : $(bg); - $bg.css("background-image", "url(" + o.url + ")"); - }); - editor.appendTo($('body')); - } - if (callback) { - callback(); - } - }) - .on('mouseover', function (event) { - if ($(this).data("value")) { - var src = $(this).data("value"); - var $bg = typeof bg === 'string' ? self.$target.find(bg) : $(bg); - $bg.css("background-image", "url(" + src + ")"); - } - }) - .on('mouseout', function (event) { - var src = $ul.find('li.active').data("value"); - var $bg = typeof bg === 'string' ? self.$target.find(bg) : $(bg); - $bg.css("background-image", "url(" + src + ")"); - }); - }, - - set_options_background: function (bg, ul_options) { - var $ul = this.$editor.find(ul_options); - var bg_value = (typeof bg === 'string' ? this.$target.find(bg) : $(bg)).css("background-image").replace(/url\(['"]*|['"]*\)/g, ""); - - // select in ul options - $ul.find("li").removeClass("active"); - var selected = $ul.find('[data-value="' + bg_value + '"], [data-value="' + bg_value.replace(/.*:\/\/[^\/]+/, '') + '"]'); - selected.addClass('active'); - if (!selected.length) { - $ul.find('.oe_custom_bg b').html(bg_value); - } + this.$target.removeAttr('contentEditable') + .find('*').removeAttr('contentEditable'); }, }); @@ -934,7 +1090,6 @@ return this.grid; }, - _drag_and_drop_after_insert_dropzone: function(){ var self = this; var $zones = $(".row:has(> .oe_drop_zone)").each(function () { @@ -963,7 +1118,36 @@ this.$target.addClass("col-md-offset-" + this.$target.prevAll(".oe_drop_to_remove").length); this._super(); }, - + hide_remove_button: function() { + this.$overlay.find('.oe_snippet_remove').toggleClass("hidden", + !this.$target.siblings().length && this.$target.parents("[data-snippet-id]:first").find("[data-snippet-id='colmd']").length > 1); + }, + onFocus : function () { + this._super(); + this.hide_remove_button(); + }, + on_clone: function () { + var $clone = this.$target.clone(false); + var _class = $clone.attr("class").replace(/\s*(col-lg-offset-|col-md-offset-)([0-9-]+)/g, ''); + _class += ' col-md-1'; + $clone.attr("class", _class); + this.$target.after($clone); + this.hide_remove_button(); + return false; + }, + on_remove: function () { + if (!this.$target.siblings().length) { + var $parent = this.$target.parents("[data-snippet-id]:first"); + if($parent.find("[data-snippet-id='colmd']").length > 1) { + return false; + } else { + $parent.data("snippet-editor").on_remove(); + } + } + this._super(); + this.hide_remove_button(); + return false; + }, on_resize: function (compass, beginClass, current) { if (compass !== 'w') return; @@ -989,186 +1173,177 @@ }, }); - website.snippet.editorRegistry.carousel = website.snippet.editorRegistry.resize.extend({ + website.snippet.editorRegistry.slider = website.snippet.editorRegistry.resize.extend({ drop_and_build_snippet: function() { - var id = 0; - $("body .carousel").each(function () { - var _id = +$(this).attr("id").replace(/^myCarousel/, ''); - if (id <= _id) { - id = _id + 1; - } - }); - this.$target.attr("id", "myCarousel" + id); + var id = $(".carousel").length; + this.id = "myCarousel" + id; + this.$target.attr("id", this.id); this.$target.find(".carousel-control").attr("href", "#myCarousel" + id); - this.$target.find("[data-target='#myCarousel']").attr("data-target", "#myCarousel" + id); + this.$target.find("[data-target]").attr("data-target", "#myCarousel" + id); this.rebind_event(); }, - onFocus: function () { - this._super(); + // rebind event to active carousel on edit mode + rebind_event: function () { + var self = this; + this.$target.find('.carousel-indicators [data-target]').off('click').on('click', function () { + self.$target.carousel(+$(this).data('slide-to')); }); + + this.$target.attr('contentEditable', 'false'); + this.$target.find('.oe_structure, .content>.row').attr('contentEditable', 'true'); + this.$target.carousel('pause'); }, - onBlur: function () { - this._super(); - this.$target.carousel('cycle'); - }, clean_for_save: function () { this._super(); this.$target.find(".item").removeClass("next prev left right"); if(!this.$target.find(".item.active").length) { this.$target.find(".item:first").addClass("active"); } - this.$target.removeAttr('contentEditable') - .find('.content, .carousel-image img') - .removeAttr('contentEditable'); + }, + onFocus: function () { + this._super(); + this.$target.carousel('pause'); + }, + onBlur: function () { + this._super(); + this.$target.carousel('cycle'); }, start : function () { + var self = this; this._super(); - this.id = this.$target.attr("id"); - this.$inner = this.$target.find('.carousel-inner'); this.$indicators = this.$target.find('.carousel-indicators'); - this.$editor.find(".js_add").on('click', _.bind(this.on_add, this)); - this.$editor.find(".js_remove").on('click', _.bind(this.on_remove, this)); - - this.change_background(".item.active", 'ul[name="carousel-background"]'); - this.change_style(); - this.change_size(); - this.set_options_style(); - this.$target.carousel(); - - var self = this; - this.$target.on('slide.bs.carousel', function () { - self.set_options_style(); - self.set_options_background(".item.active", 'ul[name="carousel-background"]'); - self.$target.carousel(); - }); + this.$editor.find(".js_add").on('click', function () {self.on_add_slide(); return false;}); + this.$editor.find(".js_remove").on('click', function () {self.on_remove_slide(); return false;}); this.rebind_event(); }, - // rebind event to active carousel on edit mode - rebind_event: function () { + on_add_slide: function () { var self = this; - this.$target.off('click').on('click', '.carousel-control', function () { - self.$target.carousel($(this).data('slide')); }); - this.$target.off('click').on('click', '.carousel-indicators [data-target]', function () { - self.$target.carousel(+$(this).data('slide-to')); }); - - this.$target.attr('contentEditable', 'false') - .find('.content, .carousel-image img') - .attr('contentEditable', 'true'); - }, - on_add: function (e) { - e.preventDefault(); var cycle = this.$inner.find('.item').length; - var $active = this.$inner.find('.item.active'); + var $active = this.$inner.find('.item.active, .item.prev, .item.next').first(); var index = $active.index(); this.$target.find('.carousel-control, .carousel-indicators').removeClass("hidden"); this.$indicators.append('<li data-target="#' + this.id + '" data-slide-to="' + cycle + '"></li>'); var $clone = this.$el.find(".item.active").clone(); - var bg = this.$editor.find('ul[name="carousel-background"] li:not([data-value="'+ $active.css("background-image").replace(/.*:\/\/[^\/]+|\)$/g, '') +'"]):first').data("value"); - $clone.css("background-image", "url('"+ bg +"')"); + + // insert $clone.removeClass('active').insertAfter($active); - this.$target.carousel().carousel(++index); - this.rebind_event(); + setTimeout(function() { + self.$target.carousel().carousel(++index); + self.rebind_event(); + },0); + return $clone; }, - on_remove: function (e) { - e.preventDefault(); + on_remove_slide: function () { + if (this.remove_process) { + return; + } var self = this; var new_index = 0; var cycle = this.$inner.find('.item').length - 1; var index = this.$inner.find('.item.active').index(); + if (cycle > 0) { - this.$inner.find('.item.active').fadeOut(1000, function () { - $(this).remove(); - self.$indicators.find('[data-target]:last').remove(); - self.$indicators.find("[data-slide-to]").removeClass("active"); - self.$indicators.find("[data-slide-to='" + new_index + "']").addClass("active"); + this.remove_process = true; + var $el = this.$inner.find('.item.active'); + self.$target.on('slid.bs.carousel', function (event) { + $el.remove(); + self.$indicators.find("li:last").remove(); + self.$target.off('slid.bs.carousel'); + self.rebind_event(); + self.remove_process = false; + if (cycle == 1) { + self.on_remove_slide(event); + } }); setTimeout(function () { - new_index = index % cycle; - self.$target.carousel().carousel( new_index + 1 ); - self.rebind_event(); + self.$target.carousel( index > 0 ? --index : cycle ); }, 500); } else { this.$target.find('.carousel-control, .carousel-indicators').addClass("hidden"); } }, - set_options_style: function () { - var style = false; - var $el = this.$inner.find('.item.active'); - var $ul = this.$editor.find('ul[name="carousel-style"]'); - - if ($el.hasClass('text_only')) - style = 'text_only'; - if ($el.hasClass('image_text')) - style = 'image_text'; - if ($el.hasClass('text_image')) - style = 'text_image'; + }); - $ul.find('[data-value="' + style + '"]').addClass('active'); + website.snippet.editorRegistry.carousel = website.snippet.editorRegistry.slider.extend({ + clean_for_save: function () { + this._super(); + this.$target.css("background-image", ""); + this.$target.removeClass(this._class); }, - change_style: function () { + start : function () { var self = this; - var $ul = this.$editor.find('ul[name="carousel-style"]'); - var $li = $ul.find("li"); + this._super(); - $li.on('click', function (event) { - $li.removeClass("active"); - $(this).addClass("active"); - }) - .on('mouseover', function (event) { - var $el = self.$inner.find('.item.active'); - $el.removeClass('image_text text_image text_only'); - $el.addClass($(event.currentTarget).data("value")); - }) - .on('mouseout', function (event) { - var $el = self.$inner.find('.item.active'); - $el.removeClass('image_text text_image text_only'); - $el.addClass($ul.find('li.active').data("value")); - }); - }, - change_size: function () { - var $el = this.$target; + // set background and prepare to clean for save + var add_class = function (c){ + 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-style-id'] === "size") return; + if (style['snippet-style-id'] === "background") { + $active.css("background-image", self.$target.css("background-image")); + } + if (np.$prev) { + $active.removeClass(np.$prev.data("class")); + } + if (np.$next) { + $active.addClass(np.$next.data("class")); + add_class(np.$next.data("class")); + } + }); + 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"); - var size = 'oe_big'; - if (this.$target.hasClass('oe_small')) - size = 'oe_small'; - else if (this.$target.hasClass('oe_medium')) - size = 'oe_medium'; + self.$target.carousel(); + }); + this.$target.trigger('slid'); + }, + on_add_slide: function () { + var $clone = this._super(); - var $ul = this.$editor.find('ul[name="carousel-size"]'); - var $li = $ul.find("li"); + // choose an other background + var $styles = this.$target.data("snippet-style-ids").background.$el.find("li[data-class]:not(.oe_custom_bg)"); + var styles_index = $styles.index($styles.filter(".active")[0]); + var $select = $($styles[styles_index >= $styles.length-1 ? 0 : styles_index+1]); + $clone.css("background-image", $select.data("src") ? "url('"+ $select.data("src") +"')" : ""); + $clone.addClass($select.data("class") || ""); - $ul.find('[data-value="' + size + '"]').addClass('active'); + return $clone; + }, + // rebind event to active carousel on edit mode + rebind_event: function () { + var self = this; + this.$target.find('.carousel-control').off('click').on('click', function () { + self.$target.carousel( $(this).data('slide')); }); - $li.on('click', function (event) { - $li.removeClass("active"); - $(this).addClass("active"); - }) - .on('mouseover', function (event) { - $el.removeClass('oe_big oe_small oe_medium'); - $el.addClass($(event.currentTarget).data("value")); - }) - .on('mouseout', function (event) { - $el.removeClass('oe_big oe_small oe_medium'); - $el.addClass($ul.find('li.active').data("value")); - }); - } + this.$target.find('.carousel-image, .content').attr('contentEditable', 'true'); + this._super(); + }, }); website.snippet.editorRegistry.parallax = website.snippet.editorRegistry.resize.extend({ start : function () { var self = this; this._super(); - this.change_background(this.$target, 'ul[name="parallax-background"]', function () { + this.scroll(); + this.$target.on('snippet-style-change snippet-style-preview', function () { self.$target.data("snippet-view").set_values(); }); - this.scroll(); - this.change_size(); + this.$target.attr('contentEditable', 'false'); + this.$target.find('> div > .oe_structure').attr('contentEditable', 'true'); }, scroll: function () { var self = this; @@ -1191,34 +1366,6 @@ this.$target.find(".parallax") .css("background-position", '') .removeAttr("data-scroll-background-offset"); - }, - change_size: function () { - var self = this; - - var size = 'oe_big'; - if (this.$target.hasClass('oe_small')) - size = 'oe_small'; - else if (this.$target.hasClass('oe_medium')) - size = 'oe_medium'; - var $ul = this.$editor.find('ul[name="parallax-size"]'); - var $li = $ul.find("li"); - - $ul.find('[data-value="' + size + '"]').addClass('active'); - - $li.on('click', function (event) { - $li.removeClass("active"); - $(this).addClass("active"); - self.$target.data("snippet-view").set_values(); - return false; - }) - .on('mouseover', function (event) { - self.$target.removeClass('oe_big oe_small oe_medium'); - self.$target.addClass($(event.currentTarget).data("value")); - }) - .on('mouseout', function (event) { - self.$target.removeClass('oe_big oe_small oe_medium'); - self.$target.addClass($ul.find('li.active').data("value")); - }); } }); @@ -1227,7 +1374,8 @@ * Don't need to add data-snippet-id="..." into the views */ - website.snippet.selector.push([".row > div[class*='col-md-']", 'colmd']); + website.snippet.selector.push([".row > [class*='col-md-']", 'colmd']); website.snippet.selector.push(['hr', 'hr']); + website.snippet.selector.push(['blockquote', 'quote']); })(); diff --git a/addons/website/static/src/js/website.tests.js b/addons/website/static/src/js/website.tests.js new file mode 100644 index 00000000000..977dc8e1848 --- /dev/null +++ b/addons/website/static/src/js/website.tests.js @@ -0,0 +1,40 @@ +var tests = {}; +var droptest = function () { + var errors = []; + for (var snippet_id in tests) { + if (!tests[snippet_id]['activated'] || !tests[snippet_id]['dropped']){ + console.log("Can't dropped or activated snippet: " + snippet_id); + } + } + if (errors.length) { + console.log(tests); + throw new Error("Can't dropped or activated at least one snippet"); + } + $("#oe_snippets").off('snippet-activated snippet-dropped'); +}; +var droptesttime = setTimeout(droptest,0); + +$("#oe_snippets").off('snippet-activated snippet-dropped') + .on('snippet-activated', function (event, dom) { + tests[$(dom).data('src-snippet-id')]['activated'] = true; + clearTimeout(droptesttime); + droptesttime = setTimeout(droptest,0); + }) + .on('snippet-dropped', function (event, dom, src_snipped_id) { + tests[$(dom).data('src-snippet-id')]['dropped'] = true; + clearTimeout(droptesttime); + droptesttime = setTimeout(droptest,0); + }); +var $thumbnails = $('#oe_snippets div.oe_snippet[data-snippet-id] .oe_snippet_thumbnail'); +$thumbnails.each(function () { + var $thumbnail = $(this); + tests[$thumbnail.parent().data('snippet-id')] = {}; + var position = $thumbnail.position(); + $thumbnail.trigger( $.Event( "mousedown", { which: 1, pageX: position.left, pageY: position.top } ) ); + $thumbnail.trigger( $.Event( "mousemove", { which: 1, pageX: position.left+100, pageY: position.top+100 } ) ); + $first_drop = $(".oe_drop_zone").first(); + position = $first_drop.position(); + $first_drop.trigger( $.Event( "mouseup", { which: 1, pageX: position.left+20, pageY: position.top+20 } ) ); + clearTimeout(droptesttime); + droptesttime = setTimeout(droptest,0); +}); diff --git a/addons/website/static/src/js/website.tour.banner.js b/addons/website/static/src/js/website.tour.banner.js index 89d19087775..029753d91a7 100644 --- a/addons/website/static/src/js/website.tour.banner.js +++ b/addons/website/static/src/js/website.tour.banner.js @@ -3,159 +3,151 @@ var website = openerp.website; - var render = website.tour.render; + website.EditorBar.include({ + start: function () { + this.registerTour(new website.BannerTour(this)); + return this._super(); + }, + }); - website.EditorBannerTour = website.EditorTour.extend({ - id: 'add_banner_tour', + website.BannerTour = website.Tour.extend({ + id: 'banner', name: "Insert a banner", + path: '/page/website.homepage', init: function (editor) { var self = this; - var $body = $(document.body); self.steps = [ { - stepId: 'welcome', - orphan: true, + stepId: 'welcome', + title: "Welcome to your website!", + content: "This tutorial will guide you to build your home page. We will start by adding a banner.", + template: self.popover({ next: "Start Tutorial", end: "Skip It" }), backdrop: true, - title: "Welcome to your website!", - content: "This tutorial will guide you to build your home page. We will start by adding a banner.", - template: render('website.tour_popover', { next: "Start Tutorial", end: "Skip It" }), }, { - stepId: 'edit-page', - element: 'button[data-action=edit]', + stepId: 'edit-page', + element: 'button[data-action=edit]', placement: 'bottom', - title: "Edit this page", - content: "Every page of your website can be modified through the <i>Edit</i> button.", - template: render('website.tour_popover'), - onShow: function () { - editor.on('tour:editor_bar_loaded', editor, function() { - self.movetoStep('add-block'); - }); + title: "Edit this page", + content: "Every page of your website can be modified through the <i>Edit</i> button.", + trigger: { + id: 'rte:ready', + type: 'openerp', + emitter: editor, }, }, { - stepId: 'add-block', - element: 'button[data-action=snippet]', + stepId: 'add-banner', + element: 'button[data-action=snippet]', placement: 'bottom', - title: "Insert building blocks", - content: "To add content in a page, you can insert building blocks.", - template: render('website.tour_popover'), - onShow: function () { - $('button[data-action=snippet]').click(function () { - self.movetoStep('drag-banner'); - }); - } + title: "Insert building blocks", + content: "To add content in a page, you can insert building blocks.", + trigger: 'click', }, { - stepId: 'drag-banner', - element: '#website-top-navbar [data-snippet-id=carousel].ui-draggable', + stepId: 'drag-banner', + snippet: 'carousel', placement: 'bottom', - title: "Drag & Drop a Banner", - content: "Drag the Banner block and drop it in your page.", - template: render('website.tour_popover'), - onShow: function () { - function beginDrag () { - $('.popover.tour').remove(); - function goToNextStep () { - $('#oe_snippets').hide(); - self.movetoStep('edit-title'); - $body.off('mouseup', goToNextStep); - } - $body.off('mousedown', beginDrag); - $body.on('mouseup', goToNextStep); - } - - $body.on('mousedown', beginDrag); - }, + title: "Drag & Drop a Banner", + content: "Drag the Banner block and drop it in your page.", + trigger: 'drag', }, { - stepId: 'edit-title', - element: '#wrap [data-snippet-id=carousel]:first .carousel-caption', + stepId: 'edit-title', + element: '#wrap [data-snippet-id=carousel]:first .carousel-caption', placement: 'top', - title: "Customize banner's text", - content: "Click in the text and start editing it. Click continue once it's done.", - template: render('website.tour_popover', { next: "Continue" }), - onHide: function () { - var $banner = $("#wrap [data-snippet-id=carousel]:first"); - if ($banner.length) { - $banner.click(); - } - }, + title: "Customize banner's text", + content: "Click in the text and start editing it. Click continue once it's done.", + template: self.popover({ next: "Continue" }), }, { - stepId: 'customize-banner', - element: '.oe_overlay_options .oe_options', + stepId: 'customize-banner', + element: '.oe_overlay_options .oe_options', placement: 'left', - title: "Customize the banner", - content: "You can customize characteristic of any blocks through the Customize menu. For instance, change the background of the banner.", - template: render('website.tour_popover', { next: "Continue" }), - onShow: function () { - $('.dropdown-menu [name=carousel-background]').click(function () { - self.movetoStep('save-changes'); - }); + title: "Customize the banner", + content: "Customize any block through this menu. Try to change the background of the banner.", + template: self.popover({ next: "Continue" }), + }, + { + stepId: 'add-three-cols', + element: 'button[data-action=snippet]', + placement: 'bottom', + title: "Add Another Block", + content: "Let's add another building block to your page.", + trigger: 'click', + }, + { + stepId: 'drag-three-columns', + snippet: 'three-columns', + placement: 'bottom', + title: "Drag & Drop a Block", + content: "Drag the <em>'3 Columns'</em> block and drop it below the banner.", + trigger: 'drag', + }, + { + stepId: 'activate-text-block-title', + element: '#wrap [data-snippet-id=three-columns] .text-center[data-snippet-id=colmd]', + placement: 'top', + title: "Edit an Area", + content: "Select any area of the page to modify it. Click on this subtitle.", + trigger: { + id: 'snippet-activated', + emitter: '#wrap [data-snippet-id=three-columns] .text-center[data-snippet-id=colmd]', }, }, { - stepId: 'save-changes', - element: 'button[data-action=save]', + stepId: 'remove-text-block-title', + element: '.oe_active .oe_snippet_remove', + placement: 'top', + title: "Delete the Title", + content: "From this toolbar you can move, duplicate or delete the selected zone. Click on the trash can to delete the title.", + trigger: 'click', + }, + { + stepId: 'save-changes', + element: 'button[data-action=save]', placement: 'right', - reflex: true, - title: "Save your modifications", - content: "Once you click on save, your website page is updated.", - template: render('website.tour_popover'), - onHide: function () { - self.saveStep('part-2'); + title: "Save your modifications", + content: "Publish your page by clicking on the <em>'Save'</em> button.", + trigger: 'click', + }, + { + stepId: 'part-2', + title: "Congratulation!", + content: "Your homepage has been updated.", + template: self.popover({ next: "Continue" }), + }, + { + stepId: 'show-mobile', + element: 'a[data-action=show-mobile-preview]', + placement: 'bottom', + title: "Test Your Mobile Version", + content: "Let's check how your homepage looks like on mobile devices.", + trigger: { + id: 'shown.bs.modal', + emitter: $(document), }, }, { - stepId: 'part-2', - orphan: true, - title: "Congratulation!", - content: "Your homepage have been updated. Now, we suggest you to insert others building blocks like texts and images to structure your page.", - template: render('website.tour_popover', { next: "Continue" }), + stepId: 'show-mobile-close', + element: 'button[data-dismiss=modal]', + placement: 'right', + title: "Close Mobile Preview", + content: "Scroll in the mobile preview to test the rendering. Once it's ok, close this dialog.", + trigger: 'click', }, { - stepId: 'show-tutorials', - element: '#help-menu-button', + stepId: 'show-tutorials', + element: '#help-menu-button', placement: 'left', - reflex: true, - title: "Help is always available", - content: "You can always click here if you want more helps or continue to build and get more tips about your website contents like page menu, ...", - template: render('website.tour_popover', { end: "Close" }), + title: "More Tutorials", + content: "Get more tutorials through this <em>'Help'</em> menu or click on the left <em>'Edit'</em> button to continue modifying this page.", + template: self.popover({ end: "Close Tutorial" }), + trigger: 'click', } ]; return this._super(); }, - startOfPart2: function () { - var currentStepIndex = this.currentStepIndex(); - var secondPartIndex = this.indexOfStep('part-2'); - var showTutorialsIndex = this.indexOfStep('show-tutorials'); - return (currentStepIndex === secondPartIndex || currentStepIndex === showTutorialsIndex) && !this.tour.ended(); - }, - canResume: function () { - return this.startOfPart2() || this._super(); - }, - }); - - website.EditorBar.include({ - start: function () { - var menu = $('#help-menu'); - var bannerTour = new website.EditorBannerTour(this); - var $menuItem = $($.parseHTML('<li><a href="#">'+bannerTour.name+'</a></li>')); - $menuItem.click(function () { - if (url.pathname !== '/' && url.pathname !== '/page/website.homepage') { - window.location.replace('/page/website.homepage?banner-tutorial=true'); - } - bannerTour.reset(); - bannerTour.start(); - }); - menu.append($menuItem); - var url = new website.UrlParser(window.location.href); - if (url.search.indexOf('?banner-tutorial=true') === 0 || bannerTour.startOfPart2()) { - bannerTour.start(); - } - return this._super(); - }, }); }()); diff --git a/addons/website/static/src/js/website.tour.install-app.js b/addons/website/static/src/js/website.tour.install-app.js deleted file mode 100644 index 1ca5fd50b16..00000000000 --- a/addons/website/static/src/js/website.tour.install-app.js +++ /dev/null @@ -1,64 +0,0 @@ -(function () { - 'use strict'; - - var website = openerp.website; - - var render = website.tour.render; - - website.InstallAppTour = website.EditorTour.extend({ - id: 'install_app', - name: "Install a new App", - init: function (editor) { - var self = this; - var $body = $(document.body); - self.steps = [ - { - stepId: 'welcome-install-app', - orphan: true, - backdrop: true, - title: "Install an App", - content: "You can intall some apps to manage your website content.", - template: render('website.tour_popover', { next: "Start Tutorial", end: "Skip It" }), - }, - { - stepId: 'customize', - element: '#customize-menu-button', - placement: 'left', - title: "Install an app", - content: "Add new apps by customizing your website.", - template: render('website.tour_popover'), - onShow: function () { - editor.on('rte:customize_menu_ready', editor, function() { - self.movetoStep('install-app'); - }); - }, - }, - { - stepId: 'install-app', - element: '#customize-menu li:last a', - placement: 'left', - orphan: true, - title: "Install an app", - content: "Click 'Install Apps' to select and install the application you would like to manage from your website.", - template: render('website.tour_popover'), - }, - ]; - return this._super(); - }, - }); - - website.EditorBar.include({ - start: function () { - var menu = $('#help-menu'); - var installAppTour = new website.InstallAppTour(this); - var $menuItem = $($.parseHTML('<li><a href="#">'+installAppTour.name+'</a></li>')); - $menuItem.click(function () { - installAppTour.reset(); - installAppTour.start(); - }); - menu.append($menuItem); - return this._super(); - }, - }); - -}()); diff --git a/addons/website/static/src/js/website.tour.js b/addons/website/static/src/js/website.tour.js index f9a931bfaaa..ea3c7bcf5b5 100644 --- a/addons/website/static/src/js/website.tour.js +++ b/addons/website/static/src/js/website.tour.js @@ -4,13 +4,7 @@ var website = openerp.website; website.add_template_file('/website/static/src/xml/website.tour.xml'); - website.tour = { - render: function render (template, dict) { - return openerp.qweb.render(template, dict); - } - }; - - website.EditorTour = openerp.Class.extend({ + website.Tour = openerp.Class.extend({ tour: undefined, steps: [], tourStorage: window.localStorage, @@ -19,27 +13,81 @@ name: this.id, storage: this.tourStorage, keyboard: false, + template: this.popover(), + onHide: function () { + window.scrollTo(0, 0); + } }); - this.tour.addSteps(_.map(this.steps, function (step) { - step.title = website.tour.render('website.tour_popover_title', { title: step.title }); - return step; - })); - // TODO: Disabled until properly implemented - // this.monkeyPatchTour(); + this.registerSteps(); }, - monkeyPatchTour: function () { + registerSteps: function () { var self = this; - // showStep should wait for 'element' to appear instead of moving to the next step - self.tour.showStep = function (i) { - var step = self.tour.getStep(i); - return (function proceed () { - if (step.orphan || $(step.element).length > 0) { - return Tour.prototype.showStep.call(self.tour, i); - } else { - setTimeout(proceed, 50); - } - }()); - }; + this.tour.addSteps(_.map(this.steps, function (step) { + step.title = openerp.qweb.render('website.tour_popover_title', { title: step.title }); + if (!step.element) { + step.orphan = true; + } + if (step.snippet) { + step.element = '#oe_snippets div.oe_snippet[data-snippet-id="'+step.snippet+'"] .oe_snippet_thumbnail'; + } + if (step.trigger) { + if (step.trigger === 'click') { + step.triggers = function (callback) { + $(step.element).one('click', function () { + (callback || self.moveToNextStep).apply(self); + }); + }; + } else if (step.trigger === 'drag') { + step.triggers = function (callback) { + self.onSnippetDragged(callback || self.moveToNextStep); + }; + } else if (step.trigger && step.trigger.id) { + if (step.trigger.emitter && step.trigger.type === 'openerp') { + step.triggers = function (callback) { + step.trigger.emitter.on(step.trigger.id, self, function customHandler () { + step.trigger.emitter.off(step.trigger.id, customHandler); + (callback || self.moveToNextStep).apply(self, arguments); + }); + }; + } else { + step.triggers = function (callback) { + var emitter = _.isString(step.trigger.emitter) ? $(step.trigger.emitter) : (step.trigger.emitter || $(step.element)); + emitter.on(step.trigger.id, function () { + (callback || self.moveToNextStep).apply(self, arguments); + }); + }; + } + } else if (step.trigger.modal) { + step.triggers = function (callback) { + var $doc = $(document); + function onStop () { + if (step.trigger.modal.stopOnClose) { + self.stop(); + } + } + $doc.on('hide.bs.modal', onStop); + $doc.one('shown.bs.modal', function () { + $('.modal button.btn-primary').one('click', function () { + $doc.off('hide.bs.modal', onStop); + (callback || self.moveToNextStep).apply(self, [step.trigger.modal.afterSubmit]); + }); + (callback || self.moveToNextStep).apply(self); + }); + }; + } + } + step.onShow = (function () { + var executed = false; + return function () { + if (!executed) { + _.isFunction(step.onStart) && step.onStart(); + _.isFunction(step.triggers) && step.triggers(); + executed = true; + } + }; + }()); + return step; + })); }, reset: function () { this.tourStorage.removeItem(this.id+'_current_step'); @@ -48,13 +96,10 @@ $('.popover.tour').remove(); }, start: function () { - if (this.canResume()) { + if (this.resume() || ((this.currentStepIndex() === 0) && !this.tour.ended())) { this.tour.start(); } }, - canResume: function () { - return (this.currentStepIndex() === 0) && !this.tour.ended(); - }, currentStepIndex: function () { var index = this.tourStorage.getItem(this.id+'_current_step') || 0; return parseInt(index, 10); @@ -68,20 +113,72 @@ }); return index; }, - movetoStep: function (stepId) { - $('.popover.tour').remove(); - var index = this.indexOfStep(stepId); - if (index > -1) { - this.tour.goto(index); + isCurrentStep: function (stepId) { + return this.currentStepIndex() === this.indexOfStep(stepId); + }, + moveToStep: function (step) { + var index = _.isNumber(step) ? step : this.indexOfStep(step); + if (index >= this.steps.length) { + this.stop(); + } else if (index >= 0) { + var self = this; + $('.popover.tour').remove(); + setTimeout(function () { + setTimeout(function () { + self.tour.goto(index); + }, 0); + }, 0); } }, - saveStep: function (stepId) { - var index = this.indexOfStep(stepId); - this.tourStorage.setItem(this.id+'_current_step', index); + moveToNextStep: function () { + var nextStepIndex = this.currentStepIndex() + 1; + this.moveToStep(nextStepIndex); }, stop: function () { this.tour.end(); }, + redirect: function (url) { + url = url || new website.UrlParser(window.location.href); + var path = (this.path && url.pathname !== this.path) ? this.path : url.pathname; + var search = url.activateTutorial(this.id); + var newUrl = path + search; + window.location.replace(newUrl); + }, + ended: function () { + return this.tourStorage.getItem(this.id+'_end') === "yes"; + }, + resume: function () { + // Override if necessary + return this.tourStorage.getItem(this.id+'_current_step') && !this.ended(); + }, + trigger: function (url) { + // Override if necessary + url = url || new website.UrlParser(window.location.href); + return url.isActive(this.id); + }, + testUrl: function (pattern) { + var url = new website.UrlParser(window.location.href); + return pattern.test(url.pathname+url.search); + }, + popover: function (options) { + return openerp.qweb.render('website.tour_popover', options); + }, + onSnippetDragged: function (callback) { + var self = this; + function beginDrag () { + $('.popover.tour').remove(); + function advance () { + if (_.isFunction(callback)) { + callback.apply(self); + } + } + $(document.body).one('mouseup', advance); + } + $('#website-top-navbar [data-snippet-id].ui-draggable').one('mousedown', beginDrag); + }, + onSnippetDraggedAdvance: function () { + onSnippetDragged(self.moveToNextStep); + }, }); website.UrlParser = openerp.Class.extend({ @@ -96,17 +193,179 @@ this.pathname = a.pathname; this.origin = a.origin; this.search = a.search; + this.hash = a.hash; + function generateTrigger (id) { + return "tutorial."+id+"=true"; + } + this.activateTutorial = function (id) { + var urlTrigger = generateTrigger(id); + var querystring = _.filter(this.search.split('?'), function (str) { + return str; + }); + if (querystring.length > 0) { + var queries = _.filter(querystring[0].split("&"), function (query) { + return query.indexOf("tutorial.") < 0 + }); + queries.push(urlTrigger); + return "?"+_.uniq(queries).join("&"); + } else { + return "?"+urlTrigger; + } + }; + this.isActive = function (id) { + var urlTrigger = generateTrigger(id); + return this.search.indexOf(urlTrigger) >= 0; + }; + }, + }); + + var TestConsole = openerp.Class.extend({ + tests: [], + editor: null, + init: function (editor) { + if (!editor) { + throw new Error("Editor cannot be null or undefined"); + } + this.editor = editor; + }, + test: function (id) { + return _.find(this.tests, function (tour) { + return tour.id === id; + }); + }, + snippetSelector: function (snippetId) { + return '#oe_snippets div.oe_snippet[data-snippet-id="'+snippetId+'"] .oe_snippet_thumbnail'; + }, + snippetThumbnail: function (snippetId) { + return $(this.snippetSelector(snippetId)).first(); + }, + snippetThumbnailExists: function (snippetId) { + return this.snippetThumbnail(snippetId).length > 0; + }, + dragAndDropSnippet: function (snippetId) { + function actualDragAndDrop ($thumbnail) { + var thumbnailPosition = $thumbnail.position(); + $thumbnail.trigger($.Event("mousedown", { which: 1, pageX: thumbnailPosition.left, pageY: thumbnailPosition.top })); + $thumbnail.trigger($.Event("mousemove", { which: 1, pageX: thumbnailPosition.left, pageY: thumbnailPosition.top+500 })); + var $dropZone = $(".oe_drop_zone").first(); + var dropPosition = $dropZone.position(); + $dropZone.trigger($.Event("mouseup", { which: 1, pageX: dropPosition.left, pageY: dropPosition.top })); + } + if (this.snippetThumbnailExists(snippetId)) { + actualDragAndDrop(this.snippetThumbnail(snippetId)); + } else { + this.editor.on('rte:ready', this, function () { + actualDragAndDrop(this.snippetThumbnail(snippetId)); + }); + } }, }); website.EditorBar.include({ + tours: [], + init: function () { + var result = this._super(); + website.TestConsole = new TestConsole(this); + return result; + }, start: function () { $('.tour-backdrop').click(function (e) { e.stopImmediatePropagation(); e.preventDefault(); }); + var url = new website.UrlParser(window.location.href); + var menu = $('#help-menu'); + _.each(this.tours, function (tour) { + var $menuItem = $($.parseHTML('<li><a href="#">'+tour.name+'</a></li>')); + $menuItem.click(function () { + tour.redirect(url); + tour.reset(); + tour.start(); + }); + menu.append($menuItem); + if (tour.trigger()) { + tour.start(); + } + }); return this._super(); }, + registerTour: function (tour) { + var self = this; + var testId = 'test_'+tour.id+'_tour'; + this.tours.push(tour); + var test = { + id: tour.id, + run: function (force) { + if (force === true) { + this.reset(); + } + var actionSteps = _.filter(tour.steps, function (step) { + return step.trigger; + }); + function executeStep (step) { + window.localStorage.setItem(testId, step.stepId); + step.triggers(function () { + var nextStep = actionSteps.shift(); + if (nextStep) { + // Ensure the previous step has been fully propagated + setTimeout(function () { + setTimeout(function () { + executeStep(nextStep); + }, 0); + }, 0); + } else { + window.localStorage.removeItem(testId); + } + }); + var $element = $(step.element); + if (step.snippet && step.trigger === 'drag') { + website.TestConsole.dragAndDropSnippet(step.snippet); + } else if (step.trigger.id === 'change') { + var currentValue = $element.val(); + var options = $element[0].options; + // FIXME: It may be necessary to set a particular value + var newValue = _.find(options, function (option) { + return option.value !== currentValue; + }).value; + $element.val(newValue).trigger($.Event("change")); + } else { + $element.trigger($.Event("click", { srcElement: $element })); + } + } + var url = new website.UrlParser(window.location.href); + if (tour.path && url.pathname !== tour.path) { + window.localStorage.setItem(testId, actionSteps[0].stepId); + window.location.href = tour.path; + } else { + var lastStepId = window.localStorage.getItem(testId); + var currentStep = actionSteps.shift(); + if (lastStepId) { + while (currentStep && lastStepId !== currentStep.stepId) { + currentStep = actionSteps.shift(); + } + } + if (currentStep.snippet && $(currentStep.element).length === 0) { + self.on('rte:ready', this, function () { + executeStep(currentStep); + }); + } else { + setTimeout(function () { + setTimeout(function () { + executeStep(currentStep); + }, 0); + }, 0); + } + } + }, + reset: function () { + window.localStorage.removeItem(testId); + }, + }; + website.TestConsole.tests.push(test); + if (window.localStorage.getItem(testId)) { + test.run(); + } + }, }); }()); diff --git a/addons/website/static/src/js/website.tour.menu-editor.js b/addons/website/static/src/js/website.tour.menu-editor.js deleted file mode 100644 index ede81025d9c..00000000000 --- a/addons/website/static/src/js/website.tour.menu-editor.js +++ /dev/null @@ -1,115 +0,0 @@ -(function () { - 'use strict'; - - var website = openerp.website; - - var render = website.tour.render; - - website.EditorBuilderTour = website.EditorTour.extend({ - id: 'menu_editor', - name: "Add a new page", - init: function (editor) { - var self = this; - var $body = $(document.body); - self.steps = [ - { - stepId: 'welcome-menu-editor', - orphan: true, - backdrop: true, - title: "Menu editor", - content: "We will show how to edit your website's menu.", - template: render('website.tour_popover', { next: "Start Tutorial", end: "Skip It" }), - }, - { - stepId: 'content-menu', - element: '#content-menu-button', - placement: 'left', - reflex: true, - title: "Edit the content", - content: "Click here to edit the menu.", - template: render('website.tour_popover'), - }, - { - stepId: 'edit-entry', - element: 'a[data-action=edit-structure]', - placement: 'left', - title: "Edit menu", - content: "Click here to create a new menu entry and manage options.", - template: render('website.tour_popover'), - onShow: function () { - editor.on('tour:menu_editor_dialog_ready', editor, function() { - self.movetoStep('add-menu-entry'); - }); - }, - }, - { - stepId: 'add-menu-entry', - element: 'a.js_add_menu', - placement: 'left', - title: "Add menu entry", - content: "Click here to create a new menu entry.", - template: render('website.tour_popover'), - onShow: function () { - editor.on('tour:new_menu_entry_dialog_ready', editor, function() { - self.movetoStep('enter-entry-name'); - }); - }, - }, - { - stepId: 'enter-entry-name', - element: '#link-text', - placement: 'left', - title: "Choose a label", - content: "This label will appear in the top menu and will be visible by all your audience.\nGive a meaningful name to help your visitors. For instance, 'Photos Gallery'.", - template: render('website.tour_popover', { next: "Continue" }), - }, - { - stepId: 'enter-page-name', - element: '#link-new', - placement: 'left', - title: "Link your menu to a 'gallery' page", - content: "This page does not exist. Create it by filling the name here. For instance, 'gallery'.", - template: render('website.tour_popover', { next: "Continue" }), - }, - { - stepId: 'save-page', - element: '.modal-footer:last button.btn-primary', - placement: 'right', - title: "Save the page", - content: "Save your new page.", - template: render('website.tour_popover'), - onShow: function () { - editor.on('tour:new_menu_entry_dialog_closed', editor, function () { - self.movetoStep('save-menu'); - }); - }, - }, - { - stepId: 'save-menu', - element: '.modal-footer button.btn-primary', - placement: 'right', - reflex: true, - title: "Save the menu", - content: "Save the menu to edit the Gallery content directly from the interface.", - template: render('website.tour_popover'), - }, - ]; - return this._super(); - }, - }); - - website.EditorBar.include({ - start: function () { - var menu = $('#help-menu'); - var builderTour = new website.EditorBuilderTour(this); - var $menuItem = $($.parseHTML('<li><a href="#">'+builderTour.name+'</a></li>')); - $menuItem.click(function () { - builderTour.reset(); - builderTour.start(); - }); - menu.append($menuItem); - return this._super(); - }, - }); - -}()); diff --git a/addons/website/static/src/xml/website.backend.xml b/addons/website/static/src/xml/website.backend.xml new file mode 100644 index 00000000000..385b7ffa99a --- /dev/null +++ b/addons/website/static/src/xml/website.backend.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- vim:fdl=1: +--> +<templates id="template" xml:space="preserve"> + + <t t-name="WidgetWebsiteButton"> + + <div class="btn-group oe_WidgetWebsiteButton"> + <button class="btn btn-sm btn-success"> + <span class="oe_unpublish">Not Published</span> + <span class="oe_publish">Published</span> + </button> + <button id="dopprod-0" data-toggle="dropdown" type="button" class="btn btn-sm btn-default dropdown-toggle"> + <span class="caret"></span> + </button> + <ul role="menu" class="dropdown-menu" aria-labelledby="dopprod-0"> + <li> + <a href="#" title="View in frontend">Website</a> + </li> + </ul> + </div> + + </t> + +</templates> diff --git a/addons/website/static/src/xml/website.editor.xml b/addons/website/static/src/xml/website.editor.xml index 66b82ae7cbe..15f1fec8bbd 100644 --- a/addons/website/static/src/xml/website.editor.xml +++ b/addons/website/static/src/xml/website.editor.xml @@ -2,11 +2,11 @@ <t t-extend="website.editorbar"> <t t-jquery="#website-top-view" t-operation="after"> <div class="navbar-inner" id="website-top-edit"> - <form class="navbar-form pull-left"> + <form class="navbar-form pull-left text-muted"> <button type="button" data-action="save" class="btn btn-primary">Save</button> - <button type="button" data-action="cancel" - class="btn">Cancel</button> + or + <a href="#" data-action="cancel">Discard</a> </form> <ul class="nav navbar-nav pull-right"> </ul> @@ -23,9 +23,10 @@ </div> <div class="modal-body"><t t-raw="__content__"/></div> <div class="modal-footer"> - <button type="button" class="btn" data-dismiss="modal" aria-hidden="true">Discard</button> + <button type="button" class="btn btn-primary save">Save</button> <button type="button" class="btn hidden wait" disabled="disabled"/> - <button type="button" class="btn btn-primary save">Save Changes</button> + or + <a href="#" data-dismiss="modal" aria-hidden="true">Discard</a> </div> </div> </div> @@ -41,23 +42,12 @@ <ul class="list-group"> <li class="list-group-item form-group active"> <h4 class="list-group-item-heading"> - <label for="link-existing" class="control-label"> - Existing page + <label for="link-page" class="control-label"> + Page </label> </h4> - <select class="existing form-control url-source" - id="link-existing"> - <option/> - </select> - </li> - <li class="list-group-item form-group"> - <h4 class="list-group-item-heading"> - <label for="link-new" class="control-label"> - New page - </label> - </h4> - <input type="text" class="form-control pages url-source" - id="link-new" placeholder="Page Name"/> + <input class="page form-control url-source" + id="link-page" type="hidden"/> </li> <li class="list-group-item form-group clearfix"> <div class="pull-right"> @@ -149,7 +139,7 @@ <div class="existing-attachments"/> </div> <div class="modal-footer"> - <button type="button" class="btn btn-default" data-dismiss="modal" aria-hidden="true">Discard</button> + <a href="#" data-dismiss="modal" aria-hidden="true">Discard</a> </div> </div> </div> @@ -181,4 +171,36 @@ </table> </t> + <t t-name="website.editor.discard"> + <div class="modal" tabindex="-1" role="dialog" aria-hidden="true"> + <div class="modal-dialog"> + <div class="modal-content"> + <div class="modal-header"> + <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> + <h3 class="modal-title">Discard edition</h3> + </div> + <div class="modal-body"> + <p> + If you discard the current edition, <em>all</em> + unsaved changes will be lost. + </p> + <p>You can cancel to return to the edition mode.</p> + </div> + <div class="modal-footer"> + <button type="button" class="btn btn-danger">Discard</button> + or + <a href="#" data-dismiss="modal" aria-hidden="true">Cancel</a> + </div> + </div> + </div> + </div> + </t> + + <t t-name="website.editor.hoverbutton"> + <button type="button" contenteditable="false" + t-attf-class="btn btn-primary hover-edition-button #{classes or ''}"> + <t t-esc="label"/> + </button> + </t> + </templates> diff --git a/addons/website/static/src/xml/website.menu.xml b/addons/website/static/src/xml/website.menu.xml index ea505d94b28..9277b0d4bc2 100644 --- a/addons/website/static/src/xml/website.menu.xml +++ b/addons/website/static/src/xml/website.menu.xml @@ -2,18 +2,16 @@ <t t-name="website.menu.dialog.submenu"> <li t-att-data-menu-id="submenu.id"> <div class="input-group"> - <span class="input-group-addon icon-reorder"/> + <span class="input-group-addon fa fa-bars"/> <span class="form-control" style="padding-top: 0; padding-bottom: 0;"> - <t t-esc="submenu.name"/> - <i class="pull-right icon-home" style="margin-top: 3px"/> + <span class="js_menu_label"> + <t t-esc="submenu.name"/> + </span> + <i class="pull-right fa fa-home" style="margin-top: 3px"/> </span> <span class="input-group-btn"> - <button type="button" class="btn btn-primary js_edit_menu"> - <i class="icon-edit"/> - </button> - <button type="button" class="btn btn-danger js_delete_menu"> - <i class="icon-trash"/> - </button> + <button type="button" class="btn btn-primary js_edit_menu fa fa-edit"/> + <button type="button" class="btn btn-danger js_delete_menu fa fa-trash-o"/> </span> </div> <t t-set="children" t-value="submenu.children"/> @@ -34,11 +32,11 @@ </ul> <div class="mt16"> <div class="pull-right text-muted"> - <span class="icon-indent-right"/> right indent for multi-level menus + <span class="fa fa-indent"/> right indent for multi-level menus </div> <div> <a href="#" class="js_add_menu"> - <i class="icon-plus-sign"/> Add Menu Entry + <i class="fa fa-plus-circle"/> Add Menu Entry </a> </div> </div> diff --git a/addons/website/static/src/xml/website.seo.xml b/addons/website/static/src/xml/website.seo.xml index 2c3f8cab52a..be9414ca37f 100644 --- a/addons/website/static/src/xml/website.seo.xml +++ b/addons/website/static/src/xml/website.seo.xml @@ -56,8 +56,9 @@ </section> --> </div><div class="modal-footer"> - <button data-action="discard" data-dismiss="modal" href="#" class="btn btn-default">Discard</button> - <button type="button" data-action="update" class="btn btn-primary">Apply Changes</button> + <button type="button" data-action="update" class="btn btn-primary">Save</button> + or + <a data-action="discard" data-dismiss="modal" href="#" >Discard</a> </div> </div> </div> diff --git a/addons/website/static/src/xml/website.snippets.xml b/addons/website/static/src/xml/website.snippets.xml index 587c36a71bc..ecc3bb8cc38 100644 --- a/addons/website/static/src/xml/website.snippets.xml +++ b/addons/website/static/src/xml/website.snippets.xml @@ -61,14 +61,14 @@ <div class="oe_overlay"> <div class="oe_overlay_options"> <div class="btn-group"> - <a href="#" class="btn btn-default btn-sm oe_snippet_parent" title="Select Container Block"><span class="icon-upload-alt"/></a> + <a href="#" class="btn btn-default btn-sm oe_snippet_parent" title="Select Container Block"><i class="fa fa-upload"></i></a> <div class="dropdown oe_options hidden btn-group"> <a href="#" data-toggle="dropdown" class="btn btn-primary btn-sm" title="Customize">Customize <span class="caret"/></a> <ul class="dropdown-menu" role="menu"></ul> </div> - <a href="#" class="btn btn-default btn-sm oe_snippet_move" title="Drag to Move">&nbsp; <span class="icon-move"/> &nbsp;</a> - <a href="#" class="btn btn-default btn-sm oe_snippet_clone" title="Duplicate Container"><span class="icon-copy"/></a> - <a href="#" class="btn btn-default btn-sm oe_snippet_remove" title="Remove Block"><span class="icon-remove"/></a> + <a href="#" class="btn btn-default btn-sm oe_snippet_move" title="Drag to Move">&nbsp;<i class="fa fa-arrows"></i>&nbsp;</a> + <a href="#" class="btn btn-default btn-sm oe_snippet_clone" title="Duplicate Container">&nbsp;<i class="fa fa-files-o"></i>&nbsp; </a> + <a href="#" class="btn btn-default btn-sm oe_snippet_remove" title="Remove Block">&nbsp;<i class="fa fa-trash-o"></i>&nbsp; </a> </div> </div> </div> diff --git a/addons/website/static/src/xml/website.translator.xml b/addons/website/static/src/xml/website.translator.xml index dc5f93e87bf..d1d9ab14a39 100644 --- a/addons/website/static/src/xml/website.translator.xml +++ b/addons/website/static/src/xml/website.translator.xml @@ -36,20 +36,15 @@ versions. </p> </section> - <hr/> - <section class="row"> - <div class="col-md-6"> - <button type="button" data-action="activate" class="btn btn-primary">Ok</button> - or - <a data-action="discard" data-dismiss="modal" href="#">Cancel</a> - </div> - <div class="col-md-6"> - <label> - <input type="checkbox" name="do_not_show"/> - Do not show this dialog later. - </label> - </div> - </section> + </div> + <div class="modal-footer"> + <div class="pull-left"> + <input type="checkbox" name="do_not_show"/> + Do not show this dialog later. + </div> + <button type="button" data-action="activate" class="btn btn-primary">Ok</button> + or + <a data-action="discard" data-dismiss="modal" href="#">Cancel</a> </div> </div> </div> diff --git a/addons/website/static/src/xml/website.xml b/addons/website/static/src/xml/website.xml index 1b4254ca19b..ceec9c161d8 100644 --- a/addons/website/static/src/xml/website.xml +++ b/addons/website/static/src/xml/website.xml @@ -19,7 +19,7 @@ </div> <div class="collapse navbar-collapse navbar-edit-collapse"> <ul class="nav navbar-nav navbar-right"> - <li><a data-action="show-mobile-preview" href="#"><span title="Mobile preview" class="icon-mobile-phone"/></a></li> + <li><a data-action="show-mobile-preview" href="#"><span title="Mobile preview"><i class="fa fa-mobile"/></span></a></li> <li class="divider-vertical"></li> <li><a data-action="promote-current-page" href="#"><span title="Promote page on the web">Promote</span></a></li> <li class="dropdown js_hide_on_translate"> @@ -36,7 +36,7 @@ <!-- filled in JS --> </ul> </li> - <li class="dropdown"> + <li class="dropdown js_hide_on_translate"> <a id="help-menu-button" class="dropdown-toggle" data-toggle="dropdown" href="#">Help <span class="caret"></span></a> <ul class="dropdown-menu" role="menu" id="help-menu"> <!-- filled in JS --> @@ -57,16 +57,7 @@ <h3 class="modal-title">Mobile preview</h3> </div> <div class="modal-body"> - <iframe id="mobile-viewport" class="oe_mobile_viewport" src="?mobile-preview=true"></iframe> - <script> - var $o = $('<div style="width:50px; overflow:scroll;"><div style="width:100%;"></div></div>'); - $o.appendTo("body"); - var scrollSize = 50 - $o.find("div").innerWidth(); - $o.remove(); - - $(".modal-body").css({'overflow': 'hidden', 'margin-right': '5px'}); - $(".oe_mobile_viewport").css({'width': (320 + scrollSize + 5) + 'px'}); - </script> + <iframe id="mobile-viewport" class="oe_mobile_viewport" src="?mobile-preview=true" style="width: 326px"></iframe> </div> </div> </div> @@ -81,16 +72,21 @@ <button title="Close" type="button" class="close" data-dismiss="modal">×</button> <div class="h2"><t t-esc="title"/></div> </div> - <div class="modal-body"> + <div class="modal-body" t-if="message or backend_url"> <section> <t t-esc="message"/> </section> - <section class="mt32"> - You have encountered an error. You can edit in the <a t-att-href="backend_url">backend</a> this data. + <section class="mt32" t-if="backend_url"> + <p>The web site has encountered an error.</p> + <p> + It might be possible to edit the relevant items + or fix the issue in <a t-href="#{backend_url}"> + the classic OpenERP interface</a>. + </p> </section> </div> <div class="modal-footer"> - <button type="button" data-dismiss="modal" href="#" class="close btn btn-default">Close</button> + <button type="button" data-dismiss="modal" class="btn btn-primary">Close</button> </div> </div> </div> @@ -120,8 +116,8 @@ </form> </div> <div class="modal-footer"> - <button type="button" class="btn" data-dismiss="modal" aria-hidden="true">Cancel</button> <button type="button" class="btn btn-primary">Continue</button> + <button type="button" class="btn" data-dismiss="modal" aria-hidden="true">Cancel</button> </div> </div> </div> |
