diff options
| author | chm@openerp.com <> | 2014-03-07 11:42:33 +0100 |
|---|---|---|
| committer | chm@openerp.com <> | 2014-03-07 11:42:33 +0100 |
| commit | 23eca80556af5cd4c1de0bf3f63da249b90a1fbc (patch) | |
| tree | fef1285da3227467537eae01ae85d2c7d1fd2e97 /addons/website/static/src | |
| parent | c18d3504689228807d6703b920b43d6c4e6e6721 (diff) | |
| parent | 0b92d30081c6abe5d42869ddf037a7eb1ff9cab4 (diff) | |
[MERGE] from trunk
bzr revid: chm@openerp.com-20140307104233-fqz4jwzf5yj6jtm2
Diffstat (limited to 'addons/website/static/src')
23 files changed, 1085 insertions, 463 deletions
diff --git a/addons/website/static/src/css/common.sass b/addons/website/static/src/css/common.sass index 86e1f6655ef..4d5f0673e33 100644 --- a/addons/website/static/src/css/common.sass +++ b/addons/website/static/src/css/common.sass @@ -1,6 +1,7 @@ @import "compass/css3" @import "compass/css3/user-interface" @import "compass/css3/transition" +@import "compass/support" //smartphones, iPhone, portrait 480x320 phones $smart_phone: 320px @@ -23,3 +24,46 @@ $desktop: 1025px @content ::selection @content + +// Lifted from compass-animation +// LICENSED UNDER THE MIT LICENSE (MIT) +// +// Copyright (c) 2012 Eric Meyer +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +@mixin set-experimental-support($moz: false, $webkit: false, $ms: false, $o: false, $khtml: false) + $experimental-support-for-mozilla: $moz + $experimental-support-for-webkit: $webkit + $experimental-support-for-microsoft: $ms + $experimental-support-for-opera: $o + $experimental-support-for-khtml: $khtml + +@mixin with-only-support-for($moz: false, $webkit: false, $ms: false, $o: false, $khtml: false) + // Capture the current state + $original-moz: $experimental-support-for-mozilla + $original-webkit: $experimental-support-for-webkit + $original-o: $experimental-support-for-opera + $original-ms: $experimental-support-for-microsoft + $original-khtml: $experimental-support-for-khtml + + @include set-experimental-support($moz, $webkit, $ms, $o, $khtml) + @content + @include set-experimental-support($original-moz, $original-webkit, $original-ms, $original-o, $original-khtml) + +@mixin keyframes($name) + @-moz-keyframes #{$name} + +with-only-support-for($moz: true) + @content + @-webkit-keyframes #{$name} + +with-only-support-for($webkit: true) + @content + @-o-keyframes #{$name} + +with-only-support-for($o: true) + @content + @keyframes #{$name} + +with-only-support-for + @content diff --git a/addons/website/static/src/css/editor.css b/addons/website/static/src/css/editor.css index 1c54d6f47dd..da0fad43caf 100644 --- a/addons/website/static/src/css/editor.css +++ b/addons/website/static/src/css/editor.css @@ -26,6 +26,7 @@ -webkit-box-shadow: none; -moz-box-shadow: none; box-shadow: none; + -ms-filter: "alpha(opacity=50)"; } /* ---- OpenERP Style ---- {{{ */ @@ -195,13 +196,19 @@ ul.oe_menu_editor .disclose { text-align: left; } -.modal.nosave .modal-footer button.save { - display: none; -} -.modal.nosave .modal-footer button.wait { +.modal.nosave .wait { display: inline-block !important; visibility: visible !important; } +.modal.nosave .modal-body .filepicker, .modal.nosave .modal-body .image-preview { + display: none; +} +.modal.nosave .modal-body .wait { + width: 100%; +} +.modal.nosave .modal-footer .save { + display: none; +} .modal .font-icons-icons { font-size: 2em; @@ -244,6 +251,26 @@ ul.oe_menu_editor .disclose { .existing-attachments .pager .disabled { display: none; } +.existing-attachments .existing-attachment-cell { + position: relative; +} +.existing-attachments .existing-attachment-cell .img { + border: 1px solid #848490; +} +.existing-attachments .existing-attachment-cell .existing-attachment-remove { + position: absolute; + top: 0; + left: 15px; + cursor: pointer; + background: white; + padding: 2px; + border: 1px solid #848490; + border-top: none; + border-left: none; + -moz-border-radius-bottomright: 8px; + -webkit-border-bottom-right-radius: 8px; + border-bottom-right-radius: 8px; +} .cke_widget_wrapper { position: static !important; diff --git a/addons/website/static/src/css/editor.sass b/addons/website/static/src/css/editor.sass index 3abfa4cff05..4571dcd257e 100644 --- a/addons/website/static/src/css/editor.sass +++ b/addons/website/static/src/css/editor.sass @@ -23,6 +23,7 @@ background: transparent border: none +box-shadow(none) + -ms-filter: "alpha(opacity=50)" // }}} @@ -162,12 +163,17 @@ ul.oe_menu_editor .modal-footer text-align: left -.modal.nosave .modal-footer - button.save - display: none - button.wait +.modal.nosave + .wait display: inline-block !important visibility: visible !important + .modal-body + .filepicker, .image-preview + display: none + .wait + width: 100% + .modal-footer .save + display: none // fontawesome modal .modal @@ -205,9 +211,27 @@ ul.oe_menu_editor .font-icons-selected background-color: #ddd +$attachment-border-color: #848490 +.existing-attachments + .pager .disabled + display: none -.existing-attachments .pager .disabled - display: none + .existing-attachment-cell + position: relative + .img + border: 1px solid $attachment-border-color + .existing-attachment-remove + position: absolute + top: 0 + left: 15px // padding-left on col-* + + cursor: pointer + background: white + padding: 2px + border: 1px solid $attachment-border-color + border-top: none + border-left: none + +border-bottom-right-radius(8px) // wrapper positioned relatively for drag&drop widget which is disabled below. // Breaks completely horribly crazy products listing page, so take it out. diff --git a/addons/website/static/src/css/snippets.css b/addons/website/static/src/css/snippets.css index 00235b3f028..a19baa33fd6 100644 --- a/addons/website/static/src/css/snippets.css +++ b/addons/website/static/src/css/snippets.css @@ -80,10 +80,8 @@ -moz-user-select: none; user-select: none; cursor: move; - pointer-events: none; } .oe_snippet .oe_snippet_thumbnail { - pointer-events: auto; text-align: center; height: 100%; background: transparent; @@ -200,6 +198,7 @@ .oe_overlay { display: none; + height: 0; position: absolute; background: transparent; -webkit-border-radius: 3px; @@ -214,20 +213,12 @@ -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; - pointer-events: none; } .oe_overlay.oe_active { display: block; - border-style: dashed; - border-width: 1px; - -webkit-box-shadow: 0px 0px 0px 1px rgba(255, 255, 255, 0.3), 0px 0px 0px 1px rgba(255, 255, 255, 0.3) inset; - -moz-box-shadow: 0px 0px 0px 1px rgba(255, 255, 255, 0.3), 0px 0px 0px 1px rgba(255, 255, 255, 0.3) inset; - 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_handle { display: block !important; - pointer-events: auto; position: absolute; top: 50%; left: 50%; @@ -238,7 +229,18 @@ height: 16px; margin: -2px; } +.oe_overlay .oe_handle > div { + z-index: 1; + position: absolute; + border-style: dashed; + border-width: 1px; + border-color: #666666; + -webkit-box-shadow: 0px 0px 0px 1px rgba(255, 255, 255, 0.5), 0px 0px 0px 1px rgba(255, 255, 255, 0.5) inset; + -moz-box-shadow: 0px 0px 0px 1px rgba(255, 255, 255, 0.5), 0px 0px 0px 1px rgba(255, 255, 255, 0.5) inset; + box-shadow: 0px 0px 0px 1px rgba(255, 255, 255, 0.5), 0px 0px 0px 1px rgba(255, 255, 255, 0.5) inset; +} .oe_overlay .oe_handle.e:before, .oe_overlay .oe_handle.w:before, .oe_overlay .oe_handle.s:before, .oe_overlay .oe_handle.n:before, .oe_overlay .oe_handle.size .oe_handle_button { + z-index: 2; position: relative; top: 50%; left: 50%; @@ -270,41 +272,70 @@ -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_handle.e, .oe_overlay .oe_handle.w { + top: 4px; + height: 100%; +} +.oe_overlay .oe_handle.e:before, .oe_overlay .oe_handle.w:before { + content: "\f0d9-\f0da"; + line-height: 16px; +} +.oe_overlay .oe_handle.e > div, .oe_overlay .oe_handle.w > div { + width: 0; + height: 100%; + top: 2px; + left: 8px; +} .oe_overlay .oe_handle.e { left: auto; - top: 2px; - height: 100%; right: -6px; cursor: w-resize; } -.oe_overlay .oe_handle.e:before { - content: "\F061"; +.oe_overlay .oe_handle.w { + left: -6px; + cursor: e-resize; } -.oe_overlay .oe_handle.s { - top: auto; +.oe_overlay .oe_handle.s, .oe_overlay .oe_handle.n { left: 2px; width: 100%; - bottom: -6px; +} +.oe_overlay .oe_handle.s:before, .oe_overlay .oe_handle.n:before { + content: "\f07d"; + text-align: center; + padding: 1px; +} +.oe_overlay .oe_handle.s > div, .oe_overlay .oe_handle.n > div { + width: 100%; + height: 0; + top: 7px; + left: 1px; +} +.oe_overlay .oe_handle.s { + top: auto; cursor: n-resize; } -.oe_overlay .oe_handle.s:before { - z-index: 0; - content: "\F063"; +.oe_overlay .oe_handle.n { + cursor: s-resize; +} +.oe_overlay .oe_handle.n > div { + top: 5px; } .oe_overlay .oe_handle.size { + z-index: 3; top: auto; - left: 2px; - width: 100%; + left: 50%; bottom: -6px; } .oe_overlay .oe_handle.size .oe_handle_button { - z-index: 1; + z-index: 3; content: "Resize"; width: 64px; text-align: center; margin-left: -32px; margin-top: -10px; cursor: row-resize; + left: 0px; + top: 9px; } .oe_overlay .oe_handle.size .oe_handle_button:hover { background: rgba(30, 30, 30, 0.8); @@ -313,43 +344,20 @@ -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_handle.size div { - border-style: dashed; - border-width: 0 0 1px 0; - border-color: rgba(0, 0, 0, 0.5); - position: relative; - top: 8px; -} -.oe_overlay .oe_handle.w { - top: 2px; - height: 100%; - left: -6px; - cursor: e-resize; -} -.oe_overlay .oe_handle.w:before { - content: "\F060"; -} -.oe_overlay .oe_handle.n { - left: 2px; - width: 100%; - top: -6px; - cursor: s-resize; -} -.oe_overlay .oe_handle.n:before { - content: "\F062"; -} .oe_overlay .icon.btn { display: inline-block; } .oe_overlay .oe_overlay_options { position: absolute; - width: 100%; + left: 50% !important; text-align: center; top: -11px; z-index: 1002; } +.oe_overlay .oe_overlay_options > .btn-group { + left: -50%; +} .oe_overlay .oe_overlay_options .btn, .oe_overlay .oe_overlay_options a { - pointer-events: auto; cursor: pointer; } .oe_overlay .oe_overlay_options .dropdown { @@ -362,6 +370,36 @@ .oe_overlay .oe_overlay_options .dropdown-menu select, .oe_overlay .oe_overlay_options .dropdown-menu input { display: block; } +.oe_overlay.block-w-left .w:before { + content: "\F061" !important; +} +.oe_overlay.block-w-right .w:before { + content: "\F060" !important; +} +.oe_overlay.block-w-left.block-w-right .w { + display: none !important; +} +.oe_overlay.block-e-left .e:before { + content: "\F061" !important; +} +.oe_overlay.block-e-right .e:before { + content: "\F060" !important; +} +.oe_overlay.block-e-left.block-e-right .e { + display: none !important; +} +.oe_overlay.block-s-top .s:before { + content: "\F063" !important; +} +.oe_overlay.block-s-bottom .s:before { + content: "\f062" !important; +} +.oe_overlay.block-n-top .n:before { + content: "\F063" !important; +} +.oe_overlay.block-n-bottom .n:before { + content: "\f062" !important; +} .s-resize-important, .s-resize-important * { cursor: s-resize !important; @@ -478,7 +516,6 @@ box-shadow: 0px 3px 17px rgba(99, 53, 150, 0.59); } .oe_snippet_editor .oe_snippet > * { - pointer-events: none; margin-top: 16px; line-height: 1em; zoom: 0.6; diff --git a/addons/website/static/src/css/snippets.sass b/addons/website/static/src/css/snippets.sass index ed9c38731db..afdb261a2a5 100644 --- a/addons/website/static/src/css/snippets.sass +++ b/addons/website/static/src/css/snippets.sass @@ -64,9 +64,7 @@ overflow: hidden +user-select(none) cursor: move - pointer-events: none .oe_snippet_thumbnail - pointer-events: auto text-align: center height: 100% background: transparent @@ -147,24 +145,20 @@ .resize_editor_busy background-color: rgba(0,0,0,0.3) + .oe_overlay display: none + height: 0 position: absolute background: transparent //@include background-image( repeating-linear-gradient(45deg, rgba(255,255,255,.02) ,rgba(255,255,255,.02) 35px, rgba(0,0,0,.02) 35px, rgba(0,0,0,.02) 75px)) +border-radius(3px) @include transition(opacity 100ms linear) +box-sizing(border-box) - pointer-events: none &.oe_active display: block - border-style: dashed - 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 position: absolute top: 50% left: 50% @@ -172,7 +166,15 @@ width: 16px height: 16px margin: -2px + > div + z-index: 1 + position: absolute + border-style: dashed + border-width: 1px + border-color: #666666 + +box-shadow(0px 0px 0px 1px rgba(255,255,255,0.5), 0px 0px 0px 1px rgba(255,255,255,0.5) inset) &.e:before, &.w:before, &.s:before, &.n:before, &.size .oe_handle_button + z-index: 2 position: relative top: 50% left: 50% @@ -194,72 +196,75 @@ background: rgba(0, 0, 0, .5) color: #fff +box-shadow(0 0 5px 3px rgba(255,255,255,.7)) + &.e, &.w + top: 4px + height: 100% + &:before + content: "\f0d9-\f0da" + line-height: 16px + > div + width: 0 + height: 100% + top: 2px + left: 8px &.e left: auto - top: 2px - height: 100% right: -6px cursor: w-resize + &.w + left: -6px + cursor: e-resize + &.s, &.n + left: 2px + width: 100% &:before - content: "\F061" + content: "\f07d" + text-align: center + padding: 1px + > div + width: 100% + height: 0 + top: 7px + left: 1px &.s top: auto - left: 2px - width: 100% - bottom: -6px cursor: n-resize - &:before - z-index: 0 - content: "\F063" + &.n + cursor: s-resize + > div + top: 5px &.size + z-index: 3 top: auto - left: 2px - width: 100% + left: 50% bottom: -6px .oe_handle_button - z-index: 1 + z-index: 3 content: "Resize" width: 64px text-align: center margin-left: -32px margin-top: -10px cursor: row-resize + left: 0px + top: 9px &:hover background: rgba(30, 30, 30, .8) color: #fff +box-shadow(0 0 5px 3px rgba(255,255,255,.7)) - div - border-style: dashed - border-width: 0 0 1px 0 - border-color: rgba(0, 0, 0, 0.5) - position: relative - top: 8px - &.w - top: 2px - height: 100% - left: -6px - cursor: e-resize - &:before - content: "\F060" - &.n - left: 2px - width: 100% - top: -6px - cursor: s-resize - &:before - content: "\F062" .icon.btn display: inline-block .oe_overlay_options position: absolute - width: 100% + left: 50% !important text-align: center top: -11px z-index: 1002 + > .btn-group + left: -50% .btn, a - pointer-events: auto cursor: pointer .dropdown display: inline-block @@ -269,6 +274,29 @@ .dropdown-menu select,.dropdown-menu input display: block + &.block-w-left .w:before + content: "\F061" !important + &.block-w-right .w:before + content: "\F060" !important + &.block-w-left.block-w-right .w + display: none !important + &.block-e-left .e:before + content: "\F061" !important + &.block-e-right .e:before + content: "\F060" !important + &.block-e-left.block-e-right .e + display: none !important + + &.block-s-top .s:before + content: "\F063" !important + &.block-s-bottom .s:before + content: "\f062" !important + &.block-n-top .n:before + content: "\F063" !important + &.block-n-bottom .n:before + content: "\f062" !important + + .s-resize-important, .s-resize-important * cursor: s-resize !important .n-resize-important, .n-resize-important * @@ -367,7 +395,6 @@ border: 2px solid rgb(151, 137, 255) box-shadow: 0px 3px 17px rgba(99, 53, 150, 0.59) & > * - pointer-events: none margin-top: 16px line-height: 1em zoom: 0.6 diff --git a/addons/website/static/src/css/website.css b/addons/website/static/src/css/website.css index 27c74508e7f..81b40ce7aca 100644 --- a/addons/website/static/src/css/website.css +++ b/addons/website/static/src/css/website.css @@ -282,12 +282,16 @@ ul.nav-stacked > li > a { } /* ---- SNIPPETS --- */ +.oe_img_bg { + background-size: 100%; +} + [data-snippet-id], .colmd, .hr, .blockquote { overflow: hidden; } @media (max-width: 400px) { - [data-snippet-id] { + [data-snippet-id]:not([data-snippet-id="carousel"]) { height: auto !important; } } @@ -296,24 +300,27 @@ ul.nav-stacked > li > a { } .carousel-inner .item { height: 100%; - background-size: 100%; + background-size: cover; } .carousel .carousel-caption { - left: auto; - right: auto; - bottom: 10%; + position: absolute; + right: 50%; + left: 50%; + bottom: 20px; +} +.carousel .carousel-caption > div { + position: absolute; text-align: left; padding: 20px; background: rgba(0, 0, 0, 0.4); + bottom: 20px; } .carousel .carousel-caption h1, .carousel .carousel-caption h2, .carousel .carousel-caption h3 { margin-top: 10px; margin-bottom: 10px; } .carousel .carousel-image { - right: 50%; - left: 50%; top: 5%; bottom: 5%; position: absolute; @@ -328,13 +335,35 @@ ul.nav-stacked > li > a { right: 10%; top: 10%; bottom: auto; +} +.carousel .item.text_only .carousel-caption > div { text-align: center; background: transparent; + bottom: auto; + width: 100%; +} +.carousel .item.text_image .carousel-caption { + left: 10%; +} +.carousel .item.text_image .carousel-caption > div { + right: 50%; + margin-right: -20%; + max-width: 550px; +} +.carousel .item.text_image .carousel-image { + right: 10%; + left: 50%; } .carousel .item.image_text .carousel-caption { right: 10%; } +.carousel .item.image_text .carousel-caption > div { + left: 50%; + margin-left: -20%; + max-width: 550px; +} .carousel .item.image_text .carousel-image { + right: 50%; left: 10%; } .carousel .carousel-control { @@ -365,22 +394,15 @@ div.carousel[data-snippet-id="slider"] .carousel-indicators .active { } .parallax { + background-size: cover; +} +.parallax > div { position: relative; - background-size: 100%; display: table; width: 100%; - min-height: 100px; -} -.parallax.oe_small { min-height: 200px; } -.parallax.oe_medium { - min-height: 300px; -} -.parallax.oe_big { - min-height: 450px; -} -.parallax > div { +.parallax > div > div { display: table-cell; vertical-align: middle; padding: 32px 0; @@ -446,10 +468,6 @@ div.carousel[data-snippet-id="slider"] .carousel-indicators .active { } /* Misc */ -.oe_img_bg { - background-size: 100%; -} - .texttop { vertical-align: top; } diff --git a/addons/website/static/src/css/website.sass b/addons/website/static/src/css/website.sass index 2c5f691fae6..a9ae82cee3a 100644 --- a/addons/website/static/src/css/website.sass +++ b/addons/website/static/src/css/website.sass @@ -228,33 +228,40 @@ ul.nav-stacked > li > a a color: white + /* ---- SNIPPETS --- */ + +.oe_img_bg + background-size: 100% + [data-snippet-id],.colmd,.hr,.blockquote overflow: hidden @media (max-width: 400px) - [data-snippet-id] + [data-snippet-id]:not([data-snippet-id="carousel"]) height: auto !important .carousel-inner height: 100% .item height: 100% - background-size: 100% + background-size: cover .carousel .carousel-caption - left: auto - right: auto - bottom: 10% - text-align: left - padding: 20px - background: rgba(0, 0, 0, 0.4) + position: absolute + right: 50% + left: 50% + bottom: 20px + > div + position: absolute + text-align: left + padding: 20px + background: rgba(0, 0, 0, 0.4) + bottom: 20px h1, h2, h3 margin-top: 10px margin-bottom: 10px .carousel-image - right: 50% - left: 50% top: 5% bottom: 5% position: absolute @@ -268,12 +275,30 @@ ul.nav-stacked > li > a right: 10% top: 10% bottom: auto - text-align: center - background: transparent + > div + text-align: center + background: transparent + bottom: auto + width: 100% + .item.text_image + .carousel-caption + left: 10% + > div + right: 50% + margin-right: -20% + max-width: 550px + .carousel-image + right: 10% + left: 50% .item.image_text .carousel-caption right: 10% + > div + left: 50% + margin-left: -20% + max-width: 550px .carousel-image + right: 50% left: 10% .carousel-control cursor: pointer @@ -299,21 +324,16 @@ div.carousel[data-snippet-id="slider"] background-color: grey .parallax - position: relative - background-size: 100% - display: table - width: 100% - min-height: 100px - &.oe_small - min-height: 200px - &.oe_medium - min-height: 300px - &.oe_big - min-height: 450px + background-size: cover > div - display: table-cell - vertical-align: middle - padding: 32px 0 + position: relative + display: table + width: 100% + min-height: 200px + > div + display: table-cell + vertical-align: middle + padding: 32px 0 /* Background */ @@ -362,9 +382,6 @@ div.carousel[data-snippet-id="slider"] /* Misc */ -.oe_img_bg - background-size: 100% - .texttop vertical-align: top diff --git a/addons/website/static/src/js/website.ace.js b/addons/website/static/src/js/website.ace.js index 514969fc4c2..a8af74bb52d 100644 --- a/addons/website/static/src/js/website.ace.js +++ b/addons/website/static/src/js/website.ace.js @@ -39,14 +39,21 @@ this.xml = text; }, isWellFormed: function () { + var error; if (document.implementation.createDocument) { - var dom = new DOMParser().parseFromString(this.xml, "text/xml"); - return dom.getElementsByTagName("parsererror").length === 0; - } else if (window.ActiveXObject) { - // TODO test in IE + // use try catch for ie + try { + var dom = new DOMParser().parseFromString(this.xml, "text/xml"); + error = dom.getElementsByTagName("parsererror"); + return error.length === 0 || $(error).text(); + } catch (e) {} + } + if (window.ActiveXObject) { + // IE var msDom = new ActiveXObject("Microsoft.XMLDOM"); msDom.async = false; - return !msDom.loadXML(this.xml); + msDom.loadXML(this.xml); + return !msDom.parseError.errorCode || msDom.parseError.reason + "\nline " + msDom.parseError.line; } return true; }, @@ -256,28 +263,35 @@ }), function (session) { return session.isDirty; }); - var requests = _.map(toSave, self.saveView); + this.clearError(); + var requests = _.map(toSave, function (session) { + return self.saveView(session); + }); $.when.apply($, requests).then(function () { self.reloadPage.call(self); - }).fail(function (source, error) { - var message = _.isString(error) ? error - : (error && error.data && error.data.arguments && error.data.arguments[0] === "Access Denied") ? "Access denied: please sign in" - : (error && error.message) ? error.message - : "Unexpected error"; - self.displayError.call(self, message); + }).fail(function (source, session, error) { + self.displayError.call(self, source, session, error); }); }, saveView: function (session) { + var self = this; var xml = new website.ace.XmlDocument(session.text); - if (xml.isWellFormed()) { - return openerp.jsonRpc('/web/dataset/call', 'call', { + var isWellFormed = xml.isWellFormed(); + var def = $.Deferred(); + if (isWellFormed === true) { + openerp.jsonRpc('/web/dataset/call', 'call', { model: 'ir.ui.view', method: 'write', args: [[session.id], { 'arch': xml.xml }, website.get_context()], + }).then(function () { + def.resolve(); + }).fail(function (source, error) { + def.reject("server", session, error); }); } else { - return $.Deferred().reject(null, "Malformed XML document"); + def.reject(null, session, isWellFormed); } + return def; }, updateHash: function () { window.location.hash = hash + "?view=" + this.selectedViewId(); @@ -286,9 +300,61 @@ this.updateHash(); window.location.reload(); }, - displayError: function (error) { - // TODO Improve feedback (e.g. update 'Save' button + tooltip) - alert(error); + clearError: function () { + this.$(".ace_layer.ace_text-layer .ace_line").css("background", ""); + }, + displayError: function (source, session, error) { + var self = this; + var line, test; + // format error message + var message = _.isString(error) ? error + : (error && error.data && error.data.arguments && error.data.arguments[0] === "Access Denied") ? "Access denied: please sign in" + : (error && error.data && error.data.message) ? error.data.message + : (error && error.message) ? error.message + : "Unexpected error"; + if (source == "server") { + message = eval(message.replace(/^\(/g, '([') + .replace(/\)$/g, '])') + .replace(/u'/g, "'") + .replace(/<([^>]+)>/g, '<b style="color:#661100;"><\$1></b>'))[1]; + line = -1; + } else { + line = message.match(/line ([0-9]+)/i); + line = line ? parseInt(line[1],10) : -1; + test = new RegExp("^\\s*"+line+"\\s*$"); + } + + function gotoline() { + self.aceEditor.gotoLine(line); + setTimeout(function () { + var $lines = self.$(".ace_editor .ace_gutter .ace_gutter-cell"); + var index = $lines.filter(function () { + return test.test($(this).text()); + }).index(); + if (index>0) { + self.$(".ace_layer.ace_text-layer .ace_line:eq(" + index + ")").css("background", "#661100"); + } + },100); + } + function onchangeSession (e) { + self.aceEditor.off('changeSession', onchangeSession); + gotoline(); + } + + var $list = this.$("#ace-view-list"); + if (+$list.val() == session.id) { + if (line>-1) gotoline(); + } else { + if (line) self.aceEditor.on('changeSession', onchangeSession); + this.$("#ace-view-list").val(session.id).change(); + } + + var $dialog = $(openerp.qweb.render('website.error_dialog', { + title: session.text.match(/\s+name=['"]([^'"]+)['"]/i)[1], + message:"<b>Malformed XML document</b>:<br/>" + message + })); + $dialog.appendTo("body"); + $dialog.modal('show'); }, open: function () { this.$el.removeClass('oe_ace_closed').addClass('oe_ace_open'); diff --git a/addons/website/static/src/js/website.editor.js b/addons/website/static/src/js/website.editor.js index ab97ee2bcf9..f874a5ca5ae 100644 --- a/addons/website/static/src/js/website.editor.js +++ b/addons/website/static/src/js/website.editor.js @@ -10,6 +10,9 @@ if (!is_smartphone) { website.ready().then(website.init_editor); + } else { + // remove padding of fake editor bar + document.body.style.padding = 0; } $(document).on('click', 'a.js_link2post', function (ev) { @@ -17,6 +20,10 @@ website.form(this.pathname, 'POST'); }); + $(document).on('click', '.cke_editable label', function (ev) { + ev.preventDefault(); + }); + $(document).on('submit', '.cke_editable form', function (ev) { // Disable form submition in editable mode ev.preventDefault(); @@ -47,7 +54,7 @@ * is editable but its attributes should not be considered editable */ function is_editable_node(element) { - return !(element.data('oe-model') === 'ir.ui.view' + return (element.data('oe-model') !== 'ir.ui.view' || element.data('cke-realelement') || (is_editing_host(element) && element.getAttribute('attributeEditable') !== 'true') || element.isReadOnly()); @@ -63,8 +70,10 @@ // only enable editors manually CKEDITOR.disableAutoInline = true; // EDIT ALL THE THINGS - CKEDITOR.dtd.$editable = $.extend( - {}, CKEDITOR.dtd.$block, CKEDITOR.dtd.$inline); + CKEDITOR.dtd.$editable = _.omit( + $.extend({}, CKEDITOR.dtd.$block, CKEDITOR.dtd.$inline), + // well maybe not *all* the things + 'ul', 'ol', 'li', 'table', 'tr', 'th', 'td'); // Disable removal of empty elements on CKEDITOR activation. Empty // elements are used for e.g. support of FontAwesome icons CKEDITOR.dtd.$removeEmpty = {}; @@ -374,8 +383,7 @@ }); }, upcast: function (el) { - return el.attributes['class'] - && (/\bfa\b/.test(el.attributes['class'])); + return el.hasClass('fa'); } }); } @@ -411,6 +419,7 @@ openerp.jsonRpc('/website/customize_template_get', 'call', { 'xml_id': view_name }).then( function(result) { _.each(result, function (item) { + if (item.xml_id === "website.debugger" && !window.location.search.match(/[&?]debug(&|$)/)) return; if (item.header) { menu.append('<li class="dropdown-header">' + item.name + '</li>'); } else { @@ -437,6 +446,12 @@ }); }, start: function() { + // remove placeholder editor bar + var fakebar = document.getElementById('website-top-navbar-placeholder'); + if (fakebar) { + fakebar.parentNode.removeChild(fakebar); + } + var self = this; this.saving_mutex = new openerp.Mutex(); @@ -628,7 +643,7 @@ image_dialog(editor, new CKEDITOR.dom.element(previous)); $image_button.hide(); previous = null; - }); + }, 'btn-sm'); // previous is the state of the button-trigger: it's the // currently-ish hovered element which can trigger a button showing. @@ -640,6 +655,7 @@ if (previous && previous === this) { return; } var selected = new CKEDITOR.dom.element(this); + // FIXME: fa nodes may not be editable widgets (?) if (!is_editable_node(selected) && !selected.hasClass('fa')) { return; } @@ -827,7 +843,6 @@ document.execCommand("enableInlineTableEditing", false, "false"); } catch (e) {} - // detect & setup any CKEDITOR widget within a newly dropped // snippet. There does not seem to be a simple way to do it for // HTML not inserted via ckeditor APIs: @@ -984,6 +999,7 @@ start: function () { var sup = this._super(); this.$el.modal({backdrop: 'static'}); + this.$('input:first').focus(); return sup; }, save: function () { @@ -1023,10 +1039,13 @@ }, start: function () { var self = this; + var last; this.$('#link-page').select2({ minimumInputLength: 1, placeholder: _t("New or existing page"), query: function (q) { + if (q.term == last) return; + last = q.term; $.when( self.page_exists(q.term), self.fetch_pages(q.term) @@ -1073,7 +1092,7 @@ } else { // Create the page, get the URL back done = $.get(_.str.sprintf( - '/pagenew/%s?noredirect=1', encodeURI(data.id))) + '/website/add/%s?noredirect=1', encodeURI(data.id))) .then(function (response) { self.make_link(response, false, data.id); }); @@ -1265,7 +1284,7 @@ }), start: function () { - this.$('.modal-footer [disabled]').text("Uploading…"); + this.$('button.wait').text("Uploading…"); var $options = this.$('.image-style').children(); this.image_styles = $options.map(function () { return this.value; }).get(); @@ -1298,13 +1317,16 @@ * Sets the provided image url as the dialog's value-to-save and * refreshes the preview element to use it. */ - set_image: function (url) { + set_image: function (url, error) { + this.$('input.url').val( + error ? '' : url); this.$('input.url').val(url); this.preview_image(); }, file_selection: function () { this.$el.addClass('nosave'); + this.$('form').removeClass('has-error').find('.help-block').empty(); this.$('button.filepicker').removeClass('btn-danger btn-success'); var self = this; @@ -1319,25 +1341,38 @@ }, file_selected: function(url, error) { var $button = this.$('button.filepicker'); - if (error) { + if (!error) { + $button.addClass('btn-success'); + } else { + url = null; + this.$('form').addClass('has-error') + .find('.help-block').text(error); $button.addClass('btn-danger'); - return; } - $button.addClass('btn-success'); - this.set_image(url); + this.set_image(url, error); }, preview_image: function () { - this.$el.removeClass('nosave'); + var loaded = function () { + this.$el.removeClass('nosave'); + }.bind(this); var image = this.$('input.url').val(); - if (!image) { return; } + if (!image) { loaded(); return; } - this.$('img.image-preview') + var $img = this.$('img.image-preview') .attr('src', image) .removeClass(this.image_styles.join(' ')) .addClass(this.$('select.image-style').val()); + + if ($img.prop('complete')) { + loaded(); + } else { + $img.load(loaded) + } }, browse_existing: function (e) { e.preventDefault(); + this.$('form').removeClass('has-error').find('.help-block').empty(); + this.$('button.filepicker').removeClass('btn-danger btn-success'); new website.editor.ExistingImageDialog(this).appendTo(document.body); }, }); @@ -1406,6 +1441,7 @@ this.page += $target.hasClass('previous') ? -1 : 1; this.display_attachments(); }, + 'click .existing-attachment-remove': 'try_remove', }), init: function (parent) { this.image = null; @@ -1428,7 +1464,7 @@ kwargs: { fields: ['name', 'website_url'], domain: [['res_model', '=', 'ir.ui.view']], - order: 'name', + order: 'id desc', context: website.get_context(), } }); @@ -1438,6 +1474,7 @@ this.display_attachments(); }, display_attachments: function () { + this.$('.help-block').empty(); var per_screen = IMAGES_PER_ROW * IMAGES_ROWS; var from = this.page * per_screen; @@ -1465,6 +1502,34 @@ } this.close() }, + + try_remove: function (e) { + var $help_block = this.$('.help-block').empty(); + var self = this; + var id = parseInt($(e.target).data('id'), 10); + var attachment = _.findWhere(this.records, {id: id}); + + return openerp.jsonRpc('/web/dataset/call_kw', 'call', { + model: 'ir.attachment', + method: 'try_remove', + args: [], + kwargs: { + ids: [id], + context: website.get_context() + } + }).then(function (prevented) { + if (_.isEmpty(prevented)) { + self.records = _.without(self.records, attachment); + self.display_attachments(); + return; + } + $help_block.replaceWith(openerp.qweb.render( + 'website.editor.dialog.image.existing.error', { + views: prevented[id] + } + )); + }); + }, }); function get_selected_link(editor) { @@ -1641,6 +1706,8 @@ } switch(m.type) { case 'attributes': // ignore .cke_focus being added or removed + // ignore id modification + if (m.attributeName === 'id') { return false; } // if attribute is not a class, can't be .cke_focus change if (m.attributeName !== 'class') { return true; } @@ -1652,6 +1719,9 @@ // ignore mutation if the *only* change is .cke_focus return change.length !== 1 || change[0] === 'cke_focus'; case 'childList': + setTimeout(function () { + fixup_browser_crap(m.addedNodes); + }, 0); // 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. @@ -1689,4 +1759,69 @@ } return output; } + + var programmatic_styles = { + float: 1, + display: 1, + position: 1, + top: 1, + left: 1, + right: 1, + bottom: 1, + }; + function fixup_browser_crap(nodes) { + if (!nodes || !nodes.length) { return; } + /** + * Checks that the node only has a @style, not e.g. @class or whatever + */ + function has_only_style(node) { + for (var i = 0; i < node.attributes.length; i++) { + var attr = node.attributes[i]; + if (attr.attributeName !== 'style') { + return false; + } + } + return true; + } + function has_programmatic_style(node) { + for (var i = 0; i < node.style.length; i++) { + var style = node.style[i]; + if (programmatic_styles[style]) { + return true; + } + } + return false; + } + + for (var i=0; i<nodes.length; ++i) { + var node = nodes[i]; + if (node.nodeType !== document.ELEMENT_NODE) { continue; } + + if (node.nodeName === 'SPAN' + && has_only_style(node) + && !has_programmatic_style(node)) { + // On backspace, webkit browsers create a <span> with a bunch of + // inline styles "remembering" where they come from. Refs: + // http://www.neotericdesign.com/blog/2013/3/working-around-chrome-s-contenteditable-span-bug + // https://code.google.com/p/chromium/issues/detail?id=226941 + // https://bugs.webkit.org/show_bug.cgi?id=114791 + // http://dev.ckeditor.com/ticket/9998 + var child, parent = node.parentNode; + while (child = node.firstChild) { + parent.insertBefore(child, node); + } + parent.removeChild(node); + // chances are we had e.g. + // <p>foo</p> + // <p>bar</p> + // merged the lines getting this in webkit + // <p>foo<span>bar</span></p> + // after unwrapping the span, we have 2 text nodes + // <p>[foo][bar]</p> + // where we probably want only one. Normalize will merge + // adjacent text nodes. However, does not merge text and cdata + parent.normalize(); + } + } + } })(); diff --git a/addons/website/static/src/js/website.editor.newpage.js b/addons/website/static/src/js/website.editor.newpage.js index 944a24b89d8..b475223ecd9 100644 --- a/addons/website/static/src/js/website.editor.newpage.js +++ b/addons/website/static/src/js/website.editor.newpage.js @@ -2,6 +2,7 @@ "use strict"; var website = openerp.website; + var _t = openerp._t; website.is_editable = true; website.is_editable_button = true; @@ -12,15 +13,44 @@ this.$(".oe_content_menu li.divider").removeClass("hidden"); return res; }, + edit: function () { + this.on('rte:ready', this, function () { + $('a:has(span[data-oe-model="website.menu"])').tooltip({ + title: _t('Save this page and use the top "Content" menu to edit the menu.'), + placement: "bottom", + trigger: "hover", + show: 50, + hide: 100, + container: 'body' + }); + }); + return this._super(); + }, events: _.extend({}, website.EditorBar.prototype.events, { 'click a[data-action=new_page]': function (ev) { ev.preventDefault(); website.prompt({ - window_title: "New Page", - input: "Page Title", - }).then(function (val) { + id: "editor_new_page", + window_title: _t("New Page"), + input: _t("Page Title"), + init: function () { + var $group = this.$dialog.find("div.form-group"); + $group.removeClass("mb0"); + + var $add = $( + '<div class="form-group mb0">'+ + '<label class="col-sm-offset-3 col-sm-9 text-left">'+ + ' <input type="checkbox" checked="checked" required="required"/> '+ + '</label>'+ + '</div>'); + $add.find('label').append(_t("Add page in menu")); + $group.after($add); + } + }).then(function (val, field, $dialog) { if (val) { - document.location = '/website/add/' + encodeURI(val); + var url = '/website/add/' + encodeURI(val); + if ($dialog.find('input[type="checkbox"]').is(':checked')) url +="?add_menu=1"; + document.location = url; } }); } diff --git a/addons/website/static/src/js/website.js b/addons/website/static/src/js/website.js index 6fde9495d36..54ee4689814 100644 --- a/addons/website/static/src/js/website.js +++ b/addons/website/static/src/js/website.js @@ -69,6 +69,8 @@ website.is_editable = website.is_editable || $('html').data('editable'); website.is_editable_button= website.is_editable_button || $('html').data('editable'); dom_ready.resolve(); + // fix for ie + if($.fn.placeholder) $('input, textarea').placeholder(); }); website.init_kanban = function ($kanban) { @@ -206,6 +208,7 @@ var def = $.Deferred(); var dialog = $(openerp.qweb.render('website.prompt', options)).appendTo("body"); + options.$dialog = dialog; var field = dialog.find(options.field_type).first(); field.val(options.default); field.fillWith = function (data) { @@ -226,7 +229,7 @@ dialog.modal('show'); field.focus(); dialog.on('click', '.btn-primary', function () { - def.resolve(field.val(), field); + def.resolve(field.val(), field, dialog); dialog.remove(); }); }); @@ -247,13 +250,13 @@ website.form = function (url, method, params) { var form = document.createElement('form'); - form.action = url; - form.method = method; + form.setAttribute('action', url); + form.setAttribute('method', method); _.each(params, function (v, k) { var param = document.createElement('input'); - param.type = 'hidden'; - param.name = k; - param.value = v; + param.setAttribute('type', 'hidden'); + param.setAttribute('name', k); + param.setAttribute('value', v); form.appendChild(param); }); document.body.appendChild(form); diff --git a/addons/website/static/src/js/website.mobile.js b/addons/website/static/src/js/website.mobile.js index 4acf2d78db8..59c2328e09b 100644 --- a/addons/website/static/src/js/website.mobile.js +++ b/addons/website/static/src/js/website.mobile.js @@ -17,6 +17,9 @@ 'hidden.bs.modal': 'destroy' }, start: function () { + if (!window.location.origin) { // fix for ie9 + window.location.origin = window.location.protocol + "//" + window.location.hostname + (window.location.port ? ':' + window.location.port: '') + '/'; + } document.getElementById("mobile-viewport").src = window.location.origin + window.location.pathname + "#mobile-preview"; this.$el.modal(); }, diff --git a/addons/website/static/src/js/website.snippets.animation.js b/addons/website/static/src/js/website.snippets.animation.js index 60c5aea49e2..f1d19125a0c 100644 --- a/addons/website/static/src/js/website.snippets.animation.js +++ b/addons/website/static/src/js/website.snippets.animation.js @@ -2,7 +2,7 @@ 'use strict'; var website = openerp.website; - website.snippet = {}; + if (!website.snippet) website.snippet = {}; website.snippet.readyAnimation = []; website.snippet.start_animation = function () { @@ -64,6 +64,10 @@ start: function () { this.$target.carousel({interval: 10000}); }, + stop: function () { + this.$target.carousel('pause'); + this.$target.removeData("bs.carousel"); + }, }); website.snippet.animationRegistry.parallax = website.snippet.Animation.extend({ @@ -105,7 +109,7 @@ var offset = 0; var padding = parseInt($(document.body).css("padding-top")); if (speed < 1) { - var inner_offset = self.$target.outerHeight() - this.height / this.width * document.body.clientWidth; + var inner_offset = - self.$target.outerHeight() + this.height / this.width * document.body.clientWidth; var outer_offset = self.$target.offset().top - (document.body.clientHeight - self.$target.outerHeight()) - padding; offset = - outer_offset * speed + inner_offset; } else { diff --git a/addons/website/static/src/js/website.snippets.editor.js b/addons/website/static/src/js/website.snippets.editor.js index 8a3e7a4421d..36030e8235b 100644 --- a/addons/website/static/src/js/website.snippets.editor.js +++ b/addons/website/static/src/js/website.snippets.editor.js @@ -9,7 +9,7 @@ var self = this; $("[data-oe-model]").on('click', function (event) { var $this = $(event.srcElement); - var tag = $this[0].tagName.toLowerCase(); + var tag = $this[0] && $this[0].tagName.toLowerCase(); if (!(tag === 'a' || tag === "button") && !$this.parents("a, button").length) { self.$('[data-action="edit"]').parent().effect('bounce', {distance: 18, times: 5}, 250); } @@ -19,13 +19,23 @@ edit: function () { var self = this; $("body").off('click'); + website.snippet.stop_animation(); window.snippets = this.snippets = new website.snippet.BuildingBlock(this); this.snippets.appendTo(this.$el); - this.on('rte:ready', this, function () { self.snippets.$button.removeClass("hidden"); - website.snippet.stop_animation(); website.snippet.start_animation(); + $(website.snippet.readyAnimation).each(function() { + var animation = $(this).data("snippet-view"); + if (animation) { + animation.$target.on('focus', '*', function(){ + animation.stop(); + }); + animation.$target.on('blur', '*', function(){ + animation.start(); + }); + } + }); }); return this._super.apply(this, arguments); @@ -79,6 +89,7 @@ // 'snippet-dropped' is triggered on '#oe_snippets' whith $target as attribute when a snippet is dropped // 'snippet-activated' is triggered on '#oe_snippets' (and on snippet) when a snippet is activated + if (!website.snippet) website.snippet = {}; website.snippet.styles = {}; website.snippet.selector = []; website.snippet.BuildingBlock = openerp.Widget.extend({ @@ -139,9 +150,6 @@ this.$el.addClass("hidden"); - this.$modal = $(openerp.qweb.render('website.snippets_modal')); - this.$modal.appendTo("body"); - $(document).on('click', '.dropdown-submenu a[tabindex]', function (e) { e.preventDefault(); }); @@ -192,13 +200,14 @@ var mt = parseInt($target.css("margin-top") || 0); var mb = parseInt($target.css("margin-bottom") || 0); $el.css({ - 'position': 'absolute', 'width': $target.outerWidth(), - 'height': $target.outerHeight() + mt + mb+1, - 'top': pos.top - mt, + 'top': pos.top - mt - 5, 'left': pos.left }); - $el.find(".oe_handle.size").css("bottom", (mb-7)+'px'); + $el.find(">.e,>.w").css({'height': $target.outerHeight() + mt + mb+1}); + $el.find(">.s").css({'top': $target.outerHeight() + mt + mb}); + $el.find(">.size").css({'top': $target.outerHeight() + mt}); + $el.find(">.s,>.n").css({'width': $target.outerWidth()-2}); }, show: function () { this.$el.removeClass("hidden"); @@ -215,14 +224,8 @@ bind_snippet_click_editor: function () { var self = this; - var snipped_event_flag = 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); + var $target = $(event.srcElement || event.target); if (!$target.attr("data-snippet-id")) { $target = $target.parents("[data-snippet-id]:first"); } @@ -375,7 +378,7 @@ }); }, stop: function(ev, ui){ - if (action === 'insert' && ! dropped && $('.oe_drop_zone') && ui.position.top > 50) { + if (action === 'insert' && ! dropped && $('.oe_drop_zone') && ui.position.top > 3) { var el = $('.oe_drop_zone').nearest({x: ui.position.left, y: ui.position.top}).first(); if (el.length) { el.after($toInsert); @@ -392,7 +395,7 @@ website.snippet.start_animation(); self.create_overlay($target); - if ($snippet.data("snippet-editor")) { + if ($target.data("snippet-editor")) { $target.data("snippet-editor").drop_and_build_snippet($target); } @@ -416,13 +419,28 @@ } setTimeout(function () { $("#oe_snippets").trigger('snippet-dropped', $target); + + // reset snippet for rte + $target.removeData("snippet-editor"); + if ($target.data("overlay")) { + $target.data("overlay").remove(); + $target.removeData("overlay"); + } + $target.find("[data-snippet-id]").each(function () { + var $snippet = $(this); + $snippet.removeData("snippet-editor"); + if ($snippet.data("overlay")) { + $snippet.data("overlay").remove(); + $snippet.removeData("overlay"); + } + }); + // end + + self.create_overlay($target); self.make_active($target); },0); } else { $toInsert.remove(); - if (self.$modal.find('input:not(:checked)').length) { - self.$modal.modal('toggle'); - } } }, }); @@ -563,6 +581,20 @@ var $target = $(this); if (!$target.data('overlay')) { var $zone = $(openerp.qweb.render('website.snippet_overlay')); + + // fix for pointer-events: none with ie9 + if (document.body && document.body.addEventListener) { + $zone.on("click mousedown mousedown", function passThrough(event) { + event.preventDefault(); + $target.each(function() { + // check if clicked point (taken from event) is inside element + event.srcElement = this; + $(this).trigger(event.type); + }); + return false; + }); + } + $zone.appendTo('#oe_manipulators'); $zone.data('target',$target); $target.data('overlay',$zone); @@ -775,6 +807,7 @@ this.parent = parent; this.$target = $(dom); this.$overlay = this.$target.data('overlay'); + this.$overlay.find('a[data-toggle="dropdown"]').dropdown(); this.snippet_id = this.$target.data("snippet-id"); this._readXMLData(); this.load_style_options(); @@ -1107,6 +1140,38 @@ return this.grid; }, + onFocus : function () { + this._super(); + this.change_cursor(); + }, + + change_cursor : function () { + var _class = this.$target.attr("class") || ""; + + var col = _class.match(/col-md-([0-9-]+)/i); + col = col ? +col[1] : 0; + + var offset = _class.match(/col-md-offset-([0-9-]+)/i); + offset = offset ? +offset[1] : 0; + + var overlay_class = this.$overlay.attr("class").replace(/(^|\s+)block-[^\s]*/gi, ''); + if (col+offset >= 12) overlay_class+= " block-e-right"; + if (col === 1) overlay_class+= " block-w-right block-e-left"; + if (offset === 0) overlay_class+= " block-w-left"; + + var mb = _class.match(/mb([0-9-]+)/i); + mb = mb ? +mb[1] : 0; + if (mb >= 128) overlay_class+= " block-s-bottom"; + else if (!mb) overlay_class+= " block-s-top"; + + var mt = _class.match(/mt([0-9-]+)/i); + mt = mt ? +mt[1] : 0; + if (mt >= 128) overlay_class+= " block-n-top"; + else if (!mt) overlay_class+= " block-n-bottom"; + + this.$overlay.attr("class", overlay_class); + }, + /* on_resize * called when the box is resizing and the class change, before the cover_target * @compass: resize direction : 'n', 's', 'e', 'w' @@ -1114,7 +1179,7 @@ * @current: curent increment in this.grid */ on_resize: function (compass, beginClass, current) { - + this.change_cursor(); } }); @@ -1170,7 +1235,6 @@ 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(); @@ -1193,27 +1257,27 @@ return false; }, on_resize: function (compass, beginClass, current) { - if (compass !== 'w') - return; - - // don't change the right border position when we change the offset (replace col size) - var beginCol = Number(beginClass.match(/col-md-([0-9]+)|$/)[1] || 0); - var beginOffset = Number(beginClass.match(/col-md-offset-([0-9-]+)|$/)[1] || beginClass.match(/col-lg-offset-([0-9-]+)|$/)[1] || 0); - var offset = Number(this.grid.w[0][current].match(/col-md-offset-([0-9-]+)|$/)[1] || 0); - if (offset < 0) { - offset = 0; - } - var colSize = beginCol - (offset - beginOffset); - if (colSize <= 0) { - colSize = 1; - offset = beginOffset + beginCol - 1; - } - this.$target.attr("class",this.$target.attr("class").replace(/\s*(col-lg-offset-|col-md-offset-|col-md-)([0-9-]+)/g, '')); + if (compass === 'w') { + // don't change the right border position when we change the offset (replace col size) + var beginCol = Number(beginClass.match(/col-md-([0-9]+)|$/)[1] || 0); + var beginOffset = Number(beginClass.match(/col-md-offset-([0-9-]+)|$/)[1] || beginClass.match(/col-lg-offset-([0-9-]+)|$/)[1] || 0); + var offset = Number(this.grid.w[0][current].match(/col-md-offset-([0-9-]+)|$/)[1] || 0); + if (offset < 0) { + offset = 0; + } + var colSize = beginCol - (offset - beginOffset); + if (colSize <= 0) { + colSize = 1; + offset = beginOffset + beginCol - 1; + } + this.$target.attr("class",this.$target.attr("class").replace(/\s*(col-lg-offset-|col-md-offset-|col-md-)([0-9-]+)/g, '')); - this.$target.addClass('col-md-' + (colSize > 12 ? 12 : colSize)); - if (offset > 0) { - this.$target.addClass('col-md-offset-' + offset); + this.$target.addClass('col-md-' + (colSize > 12 ? 12 : colSize)); + if (offset > 0) { + this.$target.addClass('col-md-offset-' + offset); + } } + this._super(compass, beginClass, current); }, }); @@ -1235,8 +1299,6 @@ this.$target.attr('contentEditable', 'false'); this.$target.find('.oe_structure, .content>.row').attr('contentEditable', 'true'); - - this.$target.carousel('pause'); }, clean_for_save: function () { this._super(); @@ -1245,14 +1307,6 @@ this.$target.find(".item:first").addClass("active"); } }, - onFocus: function () { - this._super(); - this.$target.carousel('pause'); - }, - onBlur: function () { - this._super(); - this.$target.carousel('cycle'); - }, start : function () { var self = this; this._super(); @@ -1382,7 +1436,7 @@ this.$target.find('.carousel-control').off('click').on('click', function () { self.$target.carousel( $(this).data('slide')); }); - this.$target.find('.carousel-image, .content').attr('contentEditable', 'true'); + this.$target.find('.carousel-inner .content > div').attr('contentEditable', 'true'); this.$target.find('.carousel-image').attr('attributeEditable', 'true'); this._super(); }, @@ -1405,7 +1459,10 @@ self.$target.data("snippet-view").set_values(); }); this.$target.attr('contentEditable', 'false'); - this.$target.find('> div > .oe_structure').attr('contentEditable', 'true'); + + this.$target.find('> div > .oe_structure').attr('contentEditable', 'true'); // saas-3 retro-compatibility + + this.$target.find('> div > div:not(.oe_structure) > .oe_structure').attr('contentEditable', 'true'); }, scroll: function () { var self = this; diff --git a/addons/website/static/src/js/website.tests.js b/addons/website/static/src/js/website.tests.js deleted file mode 100644 index 977dc8e1848..00000000000 --- a/addons/website/static/src/js/website.tests.js +++ /dev/null @@ -1,40 +0,0 @@ -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 2c26ae34e98..c66eea1b314 100644 --- a/addons/website/static/src/js/website.tour.banner.js +++ b/addons/website/static/src/js/website.tour.banner.js @@ -2,113 +2,115 @@ 'use strict'; var website = openerp.website; + var _t = openerp._t; website.EditorBar.include({ start: function () { - this.registerTour(new website.BannerTour(this)); + this.registerTour(new website.Tour.Banner(this)); return this._super(); }, }); - website.BannerTour = website.Tour.extend({ + website.Tour.Banner = website.Tour.extend({ id: 'banner', name: "Insert a banner", - path: '/page/website.homepage', - init: function (editor) { + path: '/', + init: function () { var self = this; self.steps = [ { - 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: _t("Welcome to your website!"), + content: _t("This tutorial will guide you to build your home page. We will start by adding a banner."), + popover: { next: _t("Start Tutorial"), end: _t("Skip It") }, }, { waitNot: '.popover.tour', 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: self.popover({ fixed: true }), + title: _t("Edit this page"), + content: _t("Every page of your website can be modified through the <i>Edit</i> button."), + popover: { fixed: true }, }, { element: 'button[data-action=snippet]', placement: 'bottom', - title: "Insert building blocks", - content: "To add content in a page, you can insert building blocks.", - template: self.popover({ fixed: true }), + title: _t("Insert building blocks"), + content: _t("To add content in a page, you can insert building blocks."), + popover: { fixed: true }, }, { snippet: 'carousel', placement: 'bottom', - title: "Drag & Drop a Banner", - content: "Drag the Banner block and drop it in your page.", - template: self.popover({ fixed: true }), + title: _t("Drag & Drop a Banner"), + content: _t("Drag the Banner block and drop it in your page."), + popover: { fixed: true }, }, { waitFor: '.oe_overlay_options .oe_options:visible', element: '#wrap [data-snippet-id=carousel]:first .carousel-caption', - sampleText: 'My Title', placement: 'top', - title: "Customize banner's text", - content: "Click in the text and start editing it. Click continue once it's done.", + title: _t("Customize banner's text"), + content: _t("Click in the text and start editing it. Click continue once it's done."), + popover: { next: _t("Continue") }, }, { - waitNot: '#wrap [data-snippet-id=carousel]:first .carousel-caption:contains("Your Banner Title")', element: '.oe_overlay_options .oe_options', placement: 'left', - title: "Customize the banner", - content: "Customize any block through this menu. Try to change the background of the banner.", - template: self.popover({ next: "Continue" }), + title: _t("Customize the banner"), + content: _t("Customize any block through this menu. Try to change the background of the banner."), + popover: { next: _t("Continue") }, }, { waitNot: '.popover.tour', element: 'button[data-action=snippet]', placement: 'bottom', - title: "Add Another Block", - content: "Let's add another building block to your page.", - template: self.popover({ fixed: true }), + title: _t("Add Another Block"), + content: _t("Let's add another building block to your page."), + popover: { fixed: true }, }, { snippet: 'three-columns', placement: 'bottom', - title: "Drag & Drop a Block", - content: "Drag the <em>'3 Columns'</em> block and drop it below the banner.", - template: self.popover({ fixed: true }), + title: _t("Drag & Drop This Block"), + content: _t("Drag the <em>'Three Columns'</em> block and drop it below the banner."), + popover: { fixed: true }, }, { waitFor: '.oe_overlay_options .oe_options:visible', element: 'button[data-action=save]', placement: 'right', - title: "Save your modifications", - content: "Publish your page by clicking on the <em>'Save'</em> button.", - template: self.popover({ fixed: true }), + title: _t("Save your modifications"), + content: _t("Publish your page by clicking on the <em>'Save'</em> button."), + popover: { fixed: true }, }, { waitFor: 'button[data-action=edit]:visible', - title: "Congratulation!", - content: "Your homepage has been updated.", - template: self.popover({ next: "Continue" }), + title: _t("Congratulation!"), + content: _t("Your homepage has been updated."), + popover: { next: _t("Continue") }, }, { waitNot: '.popover.tour', 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.", - template: self.popover({ fixed: true }), + title: _t("Test Your Mobile Version"), + content: _t("Let's check how your homepage looks like on mobile devices."), + popover: { fixed: true }, }, { - element: 'button[data-dismiss=modal]', + element: '.modal:has(#mobile-viewport) 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.", + title: _t("Check Mobile Preview"), + content: _t("Scroll in the mobile preview to test the rendering. Once it's ok, close this dialog."), + popover: { next: _t("Continue") }, }, { waitNot: '.modal', - title: "Congrats", - content: "Congratulation. This tour is finished.", - template: self.popover({ fixed: true, next: "Close Tutorial" }), + element: '#content-menu-button', + placement: 'bottom', + title: _t("Add new pages and menus"), + content: _t("The 'Content' menu allows you to add pages or add the top menu."), + popover: { next: _t("Close Tutorial") }, }, ]; return this._super(); diff --git a/addons/website/static/src/js/website.tour.js b/addons/website/static/src/js/website.tour.js index 39eaa5b7279..6b0efe7ddb2 100644 --- a/addons/website/static/src/js/website.tour.js +++ b/addons/website/static/src/js/website.tour.js @@ -2,15 +2,14 @@ 'use strict'; var website = openerp.website; + +if (typeof QWeb2 !== "undefined") website.add_template_file('/website/static/src/xml/website.tour.xml'); +if (website.EditorBar) website.EditorBar.include({ tours: [], start: function () { - // $('.tour-backdrop').click(function (e) { - // e.stopImmediatePropagation(); - // e.preventDefault(); - // }); var self = this; var menu = $('#help-menu'); _.each(this.tours, function (tour) { @@ -21,31 +20,11 @@ website.EditorBar.include({ }); menu.append($menuItem); }); - - this.waitRTEReady = false; - this.on('rte:called', this, function () {self.waitRTEReady = true; }); - this.on('rte:ready', this, function () {self.waitRTEReady = false;}); - - var res = this._super(); - website.Tour.waitReady.call(this, this.testRunning); - return res; + return this._super(); }, registerTour: function (tour) { website.Tour.add(tour); this.tours.push(tour); - }, - testRunning: function () { - if (this.waitRTEReady) { - this.on('rte:ready', this, function () { - website.Tour.each(function () { - this.running(); - }); - }); - } else { - website.Tour.each(function () { - this.running(); - }); - } } }); @@ -82,23 +61,12 @@ $.ajaxSetup({ } }); - website.Tour = openerp.Class.extend({ steps: [], defaultDelay: 50, //ms + defaultOverLaps: 5000, //ms localStorage: window.localStorage, - init: function (url) { - this.tour = new Tour({ - name: this.id, - storage: this.tourStorage, - keyboard: false, - template: this.popover(), - onHide: function () { - window.scrollTo(0, 0); - } - }); - this.registerSteps(); - }, + init: function () {}, run: function (automatic) { this.reset(); @@ -111,13 +79,18 @@ website.Tour = openerp.Class.extend({ if (automatic) { this.localStorage.setItem("tour-"+this.id+"-test-automatic", true); + } else { + this.localStorage.removeItem("tour-"+this.id+"-test-automatic"); } + this.automatic = automatic; - // redirect to begin of the tour if (this.path) { - var path = this.path.split('?'); - window.location.href = path[0] + "?tutorial."+this.id+"=true" + path.slice(1, path.length).join("?"); - return; + // redirect to begin of the tour in function of the language + if (!this.testUrl(this.path+"(#.*)?$")) { + var path = this.path.split('#'); + window.location.href = "/"+this.getLang()+path[0] + "#tutorial."+this.id+"=true&" + path.slice(1, path.length).join("#"); + return; + } } var self = this; @@ -125,12 +98,13 @@ website.Tour = openerp.Class.extend({ website.Tour.waitReady.call(this, function () {self._running();}); }, running: function () { - if (+this.localStorage.getItem("tour-"+this.id+"-test") >= this.steps.length) { + var self = this; + if (+this.localStorage.getItem("tour-"+this.id+"-test") >= this.steps.length-1) { this.endTour(); return; } - if (website.Tour.is_busy() || !this.testUrl()) return; + if (website.Tour.is_busy()) return; // launch tour with url this.checkRunningUrl(); @@ -138,22 +112,32 @@ website.Tour = openerp.Class.extend({ // mark tour as busy (only one test running) if (this.localStorage.getItem("tour-"+this.id+"-test") != null) { website.Tour.busy = true; + this.automatic = !!this.localStorage.getItem("tour-"+this.id+"-test-automatic"); + } + + if (!this.testPathUrl()) { + if (this.automatic) { + this.timer = setTimeout(function () { + self.reset(); + throw new Error("Wrong url for running " + self.id + + '\ntestPath: ' + self.testPath + + '\nhref: ' + window.location.href + + "\nreferrer: " + document.referrer + ); + },this.defaultOverLaps); + } + return; } var self = this; website.Tour.waitReady.call(this, function () {self._running();}); }, - _running: function () { var stepId = this.localStorage.getItem("tour-"+this.id+"-test"); - var automatic = !!this.localStorage.getItem("tour-"+this.id+"-test-automatic"); - + if (stepId != null) { - if (!this.check(this.step(stepId))) { - var step = this.next(stepId); - stepId = step ? step.stepId : stepId; - } - this.nextStep(stepId, automatic ? this.autoNextStep : null, automatic ? 5000 : null); + this.registerTour(); + this.nextStep(stepId, this.automatic ? this.autoNextStep : null, this.automatic ? this.defaultOverLaps : null); } }, @@ -174,30 +158,51 @@ website.Tour = openerp.Class.extend({ $('.popover.tour').remove(); }, - testUrl: function () { - return !this.testPath || this.testPath.test(window.location.href); + getLang: function () { + return $("html").attr("lang").replace(/-/, '_'); + }, + testUrl: function (url) { + return new RegExp("(/"+this.getLang()+")?"+url, "i").test(window.location.href); + }, + testPathUrl: function () { + if (!this.testPath || this.testUrl(this.testPath)) return true; }, checkRunningUrl: function () { - if (window.location.search.indexOf("tutorial."+this.id+"=true") > -1) { + if (window.location.hash.indexOf("tutorial."+this.id+"=true") > -1) { this.localStorage.setItem("tour-"+this.id+"-test", 0); - window.location.href = window.location.href.replace(/tutorial.+=true&?/, ''); + window.location.hash = window.location.hash.replace(/tutorial.+=true&?/, ''); } }, + registerTour: function () { + this.tour = new Tour({ + name: this.id, + storage: this.tourStorage, + keyboard: false, + template: this.popover(), + onHide: function () { + window.scrollTo(0, 0); + } + }); + this.registerSteps(); + }, registerSteps: function () { for (var index=0, len=this.steps.length; index<len; index++) { var step = this.steps[index]; step.stepId = step.stepId || ""+index; - if (!step.waitNot && index > 0 && $(this.steps[index-1].template).has("button[data-role='next']").size()) { - step.waitNot = '.popover.tour'; + if (!step.waitNot && index > 0 && this.steps[index-1] && + this.steps[index-1].popover && this.steps[index-1].popover.next) { + step.waitNot = '.popover.tour:visible'; } if (!step.waitFor && index > 0 && this.steps[index-1].snippet) { step.waitFor = '.oe_overlay_options .oe_options:visible'; } - step._title = step.title; - step.title = openerp.qweb.render('website.tour_popover_title', { title: step.title }); + step._title = step._title || step.title; + step.title = this.popoverTitle({ title: step._title }); + step.template = step.template || this.popover( step.popover ); + if (!step.element) step.orphan = true; if (step.snippet) { step.element = '#oe_snippets div.oe_snippet[data-snippet-id="'+step.snippet+'"] .oe_snippet_thumbnail'; @@ -205,9 +210,10 @@ website.Tour = openerp.Class.extend({ } - if ($(this.steps[index-1].template).has("button[data-role='next']").size()) { + if (this.steps[index-1] && + this.steps[index-1].popover && this.steps[index-1].popover.next) { var step = { - stepId: index, + stepId: ""+index, waitNot: '.popover.tour:visible' }; this.steps.push(step); @@ -216,8 +222,21 @@ website.Tour = openerp.Class.extend({ this.tour.addSteps(this.steps); }, + popoverTitle: function (options) { + try { + return openerp.qweb.render('website.tour_popover_title', options); + } catch (e) { + if (!this.automatic) throw e; + return options.title; + } + }, popover: function (options) { - return openerp.qweb.render('website.tour_popover', options); + try { + return openerp.qweb.render('website.tour_popover', options); + } catch (e) { + if (!this.automatic) throw e; + return ""; + } }, timer: null, @@ -262,10 +281,23 @@ website.Tour = openerp.Class.extend({ self.nextStep(step.stepId, callback, overlaps); }, self.defaultDelay); } else if (!overlaps || new Date().getTime() - time < overlaps) { + if (self.current.element) { + var $popover = $(".popover.tour"); + if(!$(self.current.element).is(":visible")) { + $popover.data("hide", true).fadeOut(300); + } else if($popover.data("hide")) { + $popover.data("hide", false).fadeIn(150); + } + } self.timer = setTimeout(checkNext, self.defaultDelay); } else { self.reset(); - throw new Error("Time overlaps to arrive to step " + step.stepId + ": '" + step._title + "'"); + throw new Error("Time overlaps to arrive to step " + step.stepId + ": '" + step._title + "'" + + '\nelement: ' + Boolean(!step.element || ($(step.element).size() && $(step.element).is(":visible") && !$(step.element).is(":hidden"))) + + '\nwaitNot: ' + Boolean(!step.waitNot || !$(step.waitNot).size()) + + '\nwaitFor: ' + Boolean(!step.waitFor || $(step.waitFor).size()) + + '\n\n' + $("body").html() + ); } } checkNext(); @@ -288,7 +320,7 @@ website.Tour = openerp.Class.extend({ $(".popover.tour").remove(); // go to step in bootstrap tour this.tour.goto(index); - if (step.callback) step.callback(); + if (step.onload) step.onload(); next = steps.shift(); break; } @@ -315,7 +347,11 @@ website.Tour = openerp.Class.extend({ } }, endTour: function () { - console.log('{ "event": "success" }'); + if (parseInt(this.localStorage.getItem("tour-"+this.id+"-test"),10) >= this.steps.length-1) { + console.log('{ "event": "success" }'); + } else { + console.log('{ "event": "canceled" }'); + } this.reset(); }, autoNextStep: function () { @@ -351,7 +387,10 @@ website.Tour = openerp.Class.extend({ } else if (step.sampleText) { $element.trigger($.Event("keydown", { srcElement: $element })); - if ($element.is("select") || $element.is("input") ) { + if ($element.is("input") ) { + $element.val(step.sampleText); + } if ($element.is("select")) { + $element.find("[value='"+step.sampleText+"'], option:contains('"+step.sampleText+"')").attr("selected", true); $element.val(step.sampleText); } else { $element.html(step.sampleText); @@ -397,7 +436,10 @@ website.Tour.busy = false; website.Tour.add = function (tour) { website.Tour.waitReady(function () { tour = tour.id ? tour : new tour(); - website.Tour.tours[tour.id] = tour; + if (!website.Tour.tours[tour.id]) { + website.Tour.tours[tour.id] = tour; + tour.running(); + } }); }; website.Tour.get = function (id) { @@ -413,7 +455,7 @@ website.Tour.each = function (callback) { website.Tour.waitReady = function (callback) { var self = this; $(document).ready(function () { - if ($.ajaxBusy == null || $.ajaxBusy) { + if ($.ajaxBusy) { $(document).ajaxStop(function() { setTimeout(function () { callback.call(self); @@ -428,18 +470,20 @@ website.Tour.waitReady = function (callback) { }); }; website.Tour.run_test = function (id) { - website.Tour.get(id).run(true); + website.Tour.waitReady(function () { + if (!website.Tour.is_busy()) { + website.Tour.tours[id].run(true); + } + }); }; website.Tour.is_busy = function () { for (var k in this.localStorage) { if (!k.indexOf("tour-")) { - return true; + return k; } } return website.Tour.busy; }; - - }()); diff --git a/addons/website/static/src/js/website.tour.test.admin.js b/addons/website/static/src/js/website.tour.test.admin.js new file mode 100644 index 00000000000..787f05beb23 --- /dev/null +++ b/addons/website/static/src/js/website.tour.test.admin.js @@ -0,0 +1,106 @@ +(function () { + 'use strict'; + + var website = openerp.website; + + website.Tour.LoginEdit = website.Tour.extend({ + id: 'login_edit', + name: "Try to log as admin and check editor", + path: '/', + init: function () { + var self = this; + self.steps = [ + { + title: "click login", + element: '#top_menu a[href*="/web/login"]', + }, + { + title: "insert login", + element: '.oe_login_form input[name="login"]', + sampleText: "admin", + }, + { + title: "insert password", + waitFor: '.oe_login_form input[name="login"][value!=""]', + element: '.oe_login_form input[name="password"]', + sampleText: "admin", + }, + { + title: "select 2 Standard tickets", + waitFor: '.oe_login_form input[name="password"][value!=""]', + element: '.oe_login_form button', + }, + { + title: "go back to website from backend", + element: 'a[data-action-model="ir.actions.act_url"]:contains("Website")', + }, + { + title: 'try to edit', + waitNot: '#wrap .carousel', + element: 'button[data-action=edit]:visible', + }, + { + title: 'check edit mode', + waitFor: 'button[data-action=save]:visible', + }, + { + title: 'check branding', + waitFor: '#wrap[data-oe-model="ir.ui.view"]', + }, + { + title: 'check rte', + waitFor: '#oe_rte_toolbar', + }, + { + title: 'check insert block button', + element: '[data-action="snippet"]:visible', + }, + { + title: 'add snippets', + snippet: 'carousel', + }, + { + title: 'try to save', + waitFor: '.oe_overlay_options .oe_options:visible', + element: 'button[data-action=save]:visible', + }, + { + title: 'check saved', + waitFor: '#wrap div.carousel', + element: 'button[data-action=edit]:visible', + }, + { + title: 'try to re-edit', + waitFor: 'button[data-action=save]:visible', + element: '#wrap .carousel', + }, + { + title: 'remove snippet', + element: '.oe_snippet_remove', + }, + { + title: 'try to re-save', + waitNot: '#wrap .carousel', + element: 'button[data-action=save]:visible', + }, + { + title: "click admin", + waitFor: 'button[data-action=edit]:visible', + element: 'a:contains("Administrator")', + }, + { + title: "click logout", + element: '#top_menu a[href*="/logout"]', + }, + { + title: "check logout", + waitFor: '#top_menu a[href*="/web/login"]', + }, + ]; + return this._super(); + }, + }); + // for test without editor bar + website.Tour.add(website.Tour.LoginEdit); + +}()); diff --git a/addons/website/static/src/js/website.tour.test.js b/addons/website/static/src/js/website.tour.test.js new file mode 100644 index 00000000000..3e85ce20dae --- /dev/null +++ b/addons/website/static/src/js/website.tour.test.js @@ -0,0 +1,34 @@ +(function () { + +function LoadScript(src) { + xmlHttp = new XMLHttpRequest(); + xmlHttp.onreadystatechange = function() { + if(xmlHttp.readyState == 4) { + if (xmlHttp.status == 200 || xmlHttp.status == 304) { + new Function(xmlHttp.responseText)(); + } else { + throw new Error("Can't load JavaScript.\nhref: " + window.location.href + "\nsrc: " + src); + } + } + }; + xmlHttp.open("GET", src, false); + xmlHttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); + xmlHttp.send(null); +} + +if (typeof jQuery === "undefined") + LoadScript("/web/static/lib/jquery/jquery.js"); +if (typeof _ === "undefined") + LoadScript("/web/static/lib/underscore/underscore.js"); +if (typeof openerp === "undefined") { + LoadScript("/web/static/lib/qweb/qweb2.js"); + LoadScript("/web/static/src/js/openerpframework.js"); +} +if (typeof openerp === "undefined" || !openerp.website || !openerp.website.add_template_file) + LoadScript("/website/static/src/js/website.js"); +if (typeof Tour === "undefined") + LoadScript("/website/static/lib/bootstrap-tour/bootstrap-tour.js"); +if (typeof openerp === "undefined" || !openerp.website.Tour) + LoadScript("/website/static/src/js/website.tour.js"); + +})();
\ No newline at end of file diff --git a/addons/website/static/src/xml/website.editor.xml b/addons/website/static/src/xml/website.editor.xml index 448c615bb07..1010ec05127 100644 --- a/addons/website/static/src/xml/website.editor.xml +++ b/addons/website/static/src/xml/website.editor.xml @@ -101,6 +101,7 @@ <button type="button" class="btn btn-primary btn-lg filepicker"> Upload an image from your computer </button> + <button type="button" class="btn btn-lg hidden wait" disabled="disabled"/> <p class="text-muted mt16">— or —</p> </div> <div class="well"> @@ -110,11 +111,15 @@ placeholder="http://openerp.com"/> </div> <input type="hidden" name="func"/> + <div class="help-block"/> </form> <div class="col-sm-4 image-preview-container"> <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAC0lEQVQIHWP4zwAAAgEBAMVfG14AAAAASUVORK5CYII%3D" class="pull-right img-rounded image-preview" width="100%"/> + <img src="/web/static/src/img/throbber-large.gif" + class="pull-right img-rounded wait hidden" + width="100%"/> <select class="form-control image-style"> <option value="">No styling</option> <option value="img-rounded">Rounded corners</option> @@ -135,8 +140,9 @@ <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> <h3 class="modal-title">Select a Picture</h3> </div> - <div class="modal-body"> + <div class="modal-body has-error"> <div class="existing-attachments"/> + <div class="help-block"/> </div> <div class="modal-footer"> <a href="#" data-dismiss="modal" aria-hidden="true">Discard</a> @@ -152,12 +158,27 @@ <li class="next disabled"><a href="#">Next →</a></li> </ul> <div class="row mt16" t-foreach="rows" t-as="row"> - <div class="col-sm-2" t-foreach="row" t-as="attachment"> + <div class="col-sm-2 existing-attachment-cell" + t-foreach="row" t-as="attachment"> + <i class="fa fa-times existing-attachment-remove" t-att-data-id="attachment.id"/> <img t-att-src="attachment.website_url" t-att-alt="attachment.name" class="img img-responsive"/> </div> </div> </div> </t> + <t t-name="website.editor.dialog.image.existing.error"> + <div class="help-block"> + <p>The image could not be deleted because it is used in the + following pages or views:</p> + <ul t-foreach="views" t-as="view"> + <li> + <a t-attf-href="/web#model=ir.ui.view&id=#{view.id}"> + <t t-esc="view.name"/> + </a> (id <t t-esc="view.id"/>) + </li> + </ul> + </div> + </t> <t t-name="website.editor.table.panel"> <table class="editorbar-panel"> <tr t-foreach="rows"><td t-foreach="cols">​</td></tr> diff --git a/addons/website/static/src/xml/website.snippets.xml b/addons/website/static/src/xml/website.snippets.xml index cf3996bdba4..ae109139e3c 100644 --- a/addons/website/static/src/xml/website.snippets.xml +++ b/addons/website/static/src/xml/website.snippets.xml @@ -15,43 +15,6 @@ </div> </li> </t> - <t t-name="website.snippets_modal"> - <div class="modal" id="oe_droperror" tabindex="-1" role="dialog"> - <div class="modal-dialog"> - <div class="modal-content"> - <div class="modal-header"> - <button type="button" class="close" data-dismiss="modal">&times;</button> - <h4 class="modal-title"> - Building Block Not Inserted - </h4> - </div> - <div class="modal-body"> - <p> - Please drop building blocks in a valid zone, like - this one: - </p> - <div class="row"> - <div class="col-sm-4 col-sm-offset-4 oe_drop_zone_style text-center"> - <span class="mt16">drop in this zone</span> - </div> - </div> - <div class="text-muted"> - <b>Tip:</b> when your cursor is in a valid zone, - OpenERP shows you a preview of the building block - in the page. You must release the block when the - preview is visible. - </div> - </div> - <div class="modal-footer"> - <button type="button" class="btn btn-primary" data-dismiss="modal">Close</button> - <div class="pull-left"> - <input type="checkbox" value="1"/> Never show this tip again - </div> - </div> - </div> - </div> - </div> - </t> <t t-name="website.snippets"> <div id='oe_snippets'></div> @@ -83,11 +46,11 @@ <div t-name="website.snippets.resize" data-snippet-id='resize'> <!-- custom data for the widget --> <div class='oe_handles'> - <div class='oe_handle n'></div> - <div class='oe_handle e'></div> - <div class='oe_handle w'></div> - <div class='oe_handle size'><div class="oe_handle_button size">Resize</div><div class="oe_border"/></div> - <div class='oe_handle s'></div> + <div class='oe_handle n'><div></div></div> + <div class='oe_handle e'><div></div></div> + <div class='oe_handle w'><div></div></div> + <div class='oe_handle size'><div class="oe_handle_button size">Resize</div></div> + <div class='oe_handle s'><div></div></div> </div> <div class='oe_snippet_thumbnail'>Margin resize</div> </div> diff --git a/addons/website/static/src/xml/website.tour.xml b/addons/website/static/src/xml/website.tour.xml index 1365ba08307..dcecef021bb 100644 --- a/addons/website/static/src/xml/website.tour.xml +++ b/addons/website/static/src/xml/website.tour.xml @@ -22,6 +22,6 @@ </div> </t> <t t-name="website.tour_popover_title"> - <t t-esc="title"/><button title="Close Tutorial" type="button" class="close" data-role="end">×</button> + <t t-esc="title"/><button title="End This Tutorial" type="button" class="close" data-role="end">×</button> </t> </templates> diff --git a/addons/website/static/src/xml/website.xml b/addons/website/static/src/xml/website.xml index 5890350e2dc..4d9c9fcd73d 100644 --- a/addons/website/static/src/xml/website.xml +++ b/addons/website/static/src/xml/website.xml @@ -74,7 +74,7 @@ </div> <div class="modal-body" t-if="message or backend_url"> <section> - <t t-esc="message"/> + <t t-raw="message"/> </section> <section class="mt32" t-if="backend_url"> <p>The web site has encountered an error.</p> @@ -102,7 +102,7 @@ <h3 class="modal-title"><t t-esc="window_title"/></h3> </div> <div class="modal-body"> - <form class="form-horizontal" role="form"> + <form class="form-horizontal" role="form" t-att-id="id"> <div class="form-group mb0"> <label for="page-name" class="col-sm-3 control-label"> <t t-esc="field_name"/>: |
