Create Awesome Websites
-Super easy, fully flexible
- Contact us + +
diff --git a/addons/website/__openerp__.py b/addons/website/__openerp__.py index dcea2ba3327..b17d79407ca 100644 --- a/addons/website/__openerp__.py +++ b/addons/website/__openerp__.py @@ -17,5 +17,6 @@ OpenERP Website CMS 'views/res_config.xml', 'website_data.xml', 'website_view.xml', + 'security/ir.model.access.csv', ], } diff --git a/addons/website/security/ir.model.access.csv b/addons/website/security/ir.model.access.csv new file mode 100644 index 00000000000..d2c3d4ee534 --- /dev/null +++ b/addons/website/security/ir.model.access.csv @@ -0,0 +1,3 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_website_public,website,website.model_website,base.group_public,1,0,0,0 +access_website,website,website.model_website,base.group_user,1,0,0,0 \ No newline at end of file diff --git a/addons/website/static/src/css/editor.css b/addons/website/static/src/css/editor.css index 0afd2be7f06..1c7ef36b236 100644 --- a/addons/website/static/src/css/editor.css +++ b/addons/website/static/src/css/editor.css @@ -8,6 +8,19 @@ background: transparent; } +.navbar.navbar-inverse .cke_toolbar { + position: relative; + top: 1px; +} +.navbar.navbar-inverse .cke_combo_button { + padding-top: 3px; + padding-bottom: 3px; +} +.navbar.navbar-inverse .cke_button { + padding-top: 7px; + padding-bottom: 7px; +} + .navbar.navbar-inverse .cke_top { background: transparent; border: none; @@ -87,6 +100,9 @@ table.editorbar-panel { cursor: pointer; width: 100%; } +table.editorbar-panel td { + border: 1px solid #aaaaaa; +} table.editorbar-panel td.selected { background-color: #b1c9d9; } @@ -100,104 +116,144 @@ table.editorbar-panel td.selected { user-select: auto; } +.modal .existing-attachments a.thumbnail { + position: relative; +} +.modal .existing-attachments a.thumbnail.selected:before { + text-decoration: none; + font-size: 150%; + background-color: rgba(0, 0, 0, 0.3); + color: #21dc0f; + content: "✔"; + height: 2em; + width: 2em; + text-align: center; + padding: 5px; + border-radius: 4px 0; + position: absolute; + top: 0; + left: 0; +} + +.modal .image-preview { + margin-bottom: 0.5em; +} + /* ---- SNIPPET EDITOR ---- */ #oe_snippets { position: fixed; left: 0px; right: 0px; - bottom: 0px; - max-height: 280px; - min-height: 140px; + top: 50px; background: #282828; box-shadow: 0px 10px 10px -10px black inset; + z-index: 1010; + overflow: hidden; +} +#oe_snippets .scroll { + white-space: nowrap; overflow-y: auto; - z-index: 20000; } -#oe_snippets .nav-pills { - padding: 3px 8px; +#oe_snippets .nav { + padding: 3px 15px; + width: 162px; background: #444444; + position: fixed; + z-index: 1; + height: 108px; } -#oe_snippets .nav-pills > li > a { +#oe_snippets .nav > li { + display: block; + float: none; +} +#oe_snippets .nav > li > a { padding: 2px 10px !important; + width: 100%; + display: block; } #oe_snippets .pill-content { - height: 140px; + display: inline-block; + height: 110px; + white-space: nowrap; + padding-left: 166px; +} +#oe_snippets .pill-content > div { + display: inline-block; +} +#oe_snippets .pill-content > div label { + width: 44px; + height: 100%; + color: white; + padding-left: 10px; +} +#oe_snippets .pill-content > div label div { + width: 100px; + text-align: center; + -webkit-transform: translate(-39px, 44px) rotate(-90deg); + -moz-transform: translate(-39px, 44px) rotate(-90deg); + -o-transform: translate(-39px, 44px) rotate(-90deg); + transform: translate(-39px, 44px) rotate(-90deg); + -webkit-transform-origin: 50% 50%; + -moz-transform-origin: 50% 50%; + -ms-transform-origin: 50% 50%; + -o-transform-origin: 50% 50%; + transform-origin: 50% 50%; } .oe_snippet { display: inline-block; vertical-align: top; - background: white; - width: 120px; - height: 120px; + width: 108px; + height: 108px; border-radius: 3px; - margin: 10px; - margin-right: 0px; + margin: 1px; + margin-top: 0px; position: relative; - box-shadow: 0px 3px 10px rgba(0, 0, 0, 0.2); overflow: hidden; -webkit-user-select: none; user-select: none; cursor: move; -} -.oe_snippet .oe_snippet_thumbnail.oe_label { - text-align: center; - background: #747474; - background-image: radial-gradient(rgba(0, 0, 0, 0.25), rgba(0, 0, 0, 0.4)); - height: 100%; - line-height: 120px; - color: white; - text-shadow: 0px 1px 2px rgba(0, 0, 0, 0.95); - box-shadow: 0px 0px 0px 1px rgba(255, 255, 255, 0.07) inset; -} -.oe_snippet .oe_snippet_body { - display: none; -} -.oe_snippet > * { pointer-events: none; } -.oe_snippet > :not(.oe_snippet_body):not(.oe_snippet_thumbnail) { +.oe_snippet .oe_snippet_thumbnail { + pointer-events: auto; + text-align: center; + height: 100%; + background: transparent; + color: white; + text-shadow: 0px 1px 2px rgba(0, 0, 0, 0.95); + position: relative; +} +.oe_snippet .oe_snippet_thumbnail span, .oe_snippet .oe_snippet_thumbnail div { + line-height: 18px; +} +.oe_snippet > :not(.oe_snippet_thumbnail) { display: none !important; } -.oe_snippet > *:first-child:not(.oe_snippet_thumbnail) { - display: block; - -webkit-transform-origin-x: 5px; - -webkit-transform-origin-y: 10px; - -webkit-transform: scale(0.095); + +#oe_snippets .oe_snippet_thumbnail { + background: #747474; + background-image: radial-gradient(rgba(0, 0, 0, 0.25), rgba(0, 0, 0, 0.4)); + box-shadow: 0px 0px 0px 1px rgba(255, 255, 255, 0.07) inset; } /* ---- SNIPPETS DROP ZONES ---- */ .oe_drop_zone.oe_insert { display: block; - height: 32px; - margin: -16px 0px; + height: 48px; + margin: 0px; + margin-bottom: -48px; -webkit-transition: margin 250ms linear; width: 100%; - z-index: 1000; position: absolute; - background: rgba(153, 0, 255, 0.17); - border-radius: 3px; -} -.oe_drop_zone.oe_insert:first-child:not(:last-child) { - margin-top: -8px; - margin-bottom: -24px; -} -.oe_drop_zone.oe_insert:last-child:not(:first-child) { - margin-top: -24px; - margin-bottom: -8px; + z-index: 1000; } .oe_drop_zone.oe_insert:before { content: ""; display: block; border-top: dashed 2px rgba(209, 178, 255, 0.72); position: relative; - top: 16px; -} -.oe_drop_zone.oe_insert:first-child:not(:last-child):before { - top: 8px; -} -.oe_drop_zone.oe_insert:last-child:not(:first-child):before { - top: 24px; + top: 0px; } .oe_drop_zone.oe_insert.oe_hover:before { border-top: dashed 2px rgba(116, 255, 161, 0.72); @@ -208,6 +264,10 @@ table.editorbar-panel td.selected { position: absolute; margin: 0px !important; } +.oe_drop_zone.oe_insert.oe_overlay { + background: rgba(153, 0, 255, 0.17); + border-radius: 3px; +} .oe_drop_zone { background: rgba(153, 0, 255, 0.17); @@ -235,7 +295,6 @@ table.editorbar-panel td.selected { } .oe_overlay.oe_selected, .oe_overlay.oe_active { opacity: 1; - background: transparent; 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; @@ -253,6 +312,39 @@ table.editorbar-panel td.selected { display: block !important; pointer-events: auto; } +.oe_overlay.oe_active .oe_handle:before { + position: relative; + top: 50%; + left: 50%; + display: block; + background: white; + border: solid 1px #9900ff; + border-radius: 8px; + width: 16px; + height: 16px; + margin: -8px; + padding-left: 1px; + font-size: 14px; + line-height: 14px; + color: #9900ff; + -webkit-transition: background 100ms linear; +} +.oe_overlay.oe_active .oe_handle:hover:before { + background: #9900ff; + color: white; +} +.oe_overlay.oe_active .oe_handle.e:before { + content: "⇨"; +} +.oe_overlay.oe_active .oe_handle.s:before { + content: "⇩"; +} +.oe_overlay.oe_active .oe_handle.w:before { + content: "⇦"; +} +.oe_overlay.oe_active .oe_handle.n:before { + content: "⇧"; +} .oe_overlay .icon.btn { display: inline-block; } @@ -285,6 +377,9 @@ table.editorbar-panel td.selected { .oe_overlay .oe_overlay_options .oe_move .js_box_move { margin-left: -50%; } +.oe_overlay .oe_overlay_options .dropdown-menu select, .oe_overlay .oe_overlay_options .dropdown-menu input { + display: block; +} .oe_overlay.oe_active .oe_overlay_options { display: block; } @@ -412,6 +507,10 @@ table.editorbar-panel td.selected { /* ---- ACE EDITOR ---- */ .oe_ace_view_editor { transition: all 0.2s ease-in; + -webkit-transition: all 0.2s ease-in; + -ms-transition: all 0.2s ease-in; + -o-transition: all 0.2s ease-in; + -moz-transition: all 0.2s ease-in; opacity: 0.3; position: fixed; top: 51px; diff --git a/addons/website/static/src/css/editor.sass b/addons/website/static/src/css/editor.sass index 380564b0cec..3b6e6c088a8 100644 --- a/addons/website/static/src/css/editor.sass +++ b/addons/website/static/src/css/editor.sass @@ -8,6 +8,17 @@ .navbar.navbar-inverse .cke_inner background: transparent +.navbar.navbar-inverse + .cke_toolbar + position: relative + top: 1px + .cke_combo_button + padding-top: 3px + padding-bottom: 3px + .cke_button + padding-top: 7px + padding-bottom: 7px + .navbar.navbar-inverse .cke_top background: transparent border: none @@ -77,6 +88,8 @@ table.editorbar-panel cursor: pointer width: 100% + td + border: 1px solid #aaa td.selected background-color: #b1c9d9 @@ -91,93 +104,134 @@ table.editorbar-panel -o-user-select: auto user-select: auto +.modal .existing-attachments a.thumbnail + position: relative + + &.selected:before + text-decoration: none + font-size: 150% + background-color: rgba(0, 0, 0, 0.3) + color: #21dc0f + content: "✔" + + height: 2em + width: 2em + text-align: center + padding: 5px + border-radius: 4px 0 + + position: absolute + top: 0 + left: 0 + +.modal .image-preview + margin-bottom: 0.5em + /* ---- SNIPPET EDITOR ---- */ + #oe_snippets position: fixed left: 0px right: 0px - bottom: 0px - max-height: 280px - min-height: 140px + top: 50px background: rgb(40,40,40) box-shadow: 0px 10px 10px -10px black inset - overflow-y: auto - z-index: 20000 - .nav-pills - padding: 3px 8px + z-index: 1010 + overflow: hidden + .scroll + white-space: nowrap + overflow-y: auto + .nav + padding: 3px 15px + width: 162px background: #444444 - > li > a - padding: 2px 10px !important + > li + display: block + float: none + > a + padding: 2px 10px !important + width: 100% + display: block + position: fixed + z-index: 1 + height: 108px .pill-content - height: 140px + display: inline-block + height: 110px + white-space: nowrap + padding-left: 166px + > div + display: inline-block + label + width: 44px + height: 100% + color: #fff + padding-left: 10px + div + width: 100px + text-align: center + -webkit-transform: translate(-39px, 44px) rotate(-90deg) + -moz-transform: translate(-39px, 44px) rotate(-90deg) + -o-transform: translate(-39px, 44px) rotate(-90deg) + transform: translate(-39px, 44px) rotate(-90deg) + -webkit-transform-origin: 50% 50% + -moz-transform-origin: 50% 50% + -ms-transform-origin: 50% 50% + -o-transform-origin: 50% 50% + transform-origin: 50% 50% .oe_snippet display: inline-block vertical-align: top - background: white - width: 120px - height: 120px + width: 108px + height: 108px border-radius: 3px - margin: 10px - margin-right: 0px + margin: 1px + margin-top: 0px position: relative - box-shadow: 0px 3px 10px rgba(0,0,0,0.2) overflow: hidden -webkit-user-select: none user-select: none cursor: move + pointer-events: none - .oe_snippet_thumbnail.oe_label + .oe_snippet_thumbnail + pointer-events: auto text-align: center - background: rgb(116, 116, 116) - background-image: radial-gradient(rgba(0,0,0,0.25),rgba(0,0,0,0.4)) height: 100% - line-height: 120px + background: transparent color: white text-shadow: 0px 1px 2px rgba(0, 0, 0, 0.95) - box-shadow: 0px 0px 0px 1px rgba(255, 255, 255, 0.07) inset - - .oe_snippet_body - display: none - & > * - pointer-events: none - & > :not(.oe_snippet_body):not(.oe_snippet_thumbnail) + position: relative + span, div + line-height: 18px + & > :not(.oe_snippet_thumbnail) display: none !important - & > *:first-child:not(.oe_snippet_thumbnail) - display: block - -webkit-transform-origin-x: 5px - -webkit-transform-origin-y: 10px - -webkit-transform: scale(0.095) - +#oe_snippets .oe_snippet_thumbnail + background: rgb(116, 116, 116) + background-image: radial-gradient(rgba(0,0,0,0.25),rgba(0,0,0,0.4)) + box-shadow: 0px 0px 0px 1px rgba(255, 255, 255, 0.07) inset /* ---- SNIPPETS DROP ZONES ---- */ .oe_drop_zone.oe_insert display: block - height: 32px - margin: -16px 0px + height: 48px + margin: 0px + margin-bottom: -48px -webkit-transition: margin 250ms linear width: 100% + position: absolute z-index: 1000 - &:first-child:not(:last-child) - margin-top: -8px - margin-bottom: -24px - &:last-child:not(:first-child) - margin-top: -24px - margin-bottom: -8px &:before content: "" display: block border-top: dashed 2px rgba(209, 178, 255, 0.72) position: relative - top: 16px - &:first-child:not(:last-child):before - top: 8px - &:last-child:not(:first-child):before - top: 24px + top: 0px &.oe_hover:before border-top: dashed 2px rgba(116, 255, 161, 0.72) &.oe_vertical @@ -186,9 +240,8 @@ table.editorbar-panel position: absolute margin: 0px !important &.oe_overlay - position: absolute - background: rgba(153, 0, 255, 0.17) - border-radius: 3px + background: rgba(153, 0, 255, 0.17) + border-radius: 3px .oe_drop_zone background: rgba(153, 0, 255, 0.17) @@ -202,7 +255,7 @@ table.editorbar-panel .oe_overlay display: block position: absolute - background: transparent //rgba(153, 0, 255, 0.05) + background: transparent border-radius: 3px opacity: 0 transition: opacity 100ms linear @@ -213,7 +266,6 @@ table.editorbar-panel cursor: pointer &.oe_selected, &.oe_active opacity: 1 - background: transparent 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 @@ -226,6 +278,34 @@ table.editorbar-panel .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(153, 0, 255, 1) + border-radius: 8px + width: 16px + height: 16px + margin: -8px + padding-left: 1px + font-size: 14px + line-height: 14px + color: rgba(153, 0, 255, 1) + -webkit-transition: background 100ms linear + &:hover:before + background: rgba(153, 0, 255, 1) + color: #fff + .oe_handle.e:before + content: "⇨" + .oe_handle.s:before + content: "⇩" + .oe_handle.w:before + content: "⇦" + .oe_handle.n:before + content: "⇧" + .icon.btn display: inline-block @@ -252,6 +332,8 @@ table.editorbar-panel left: 50% .js_box_move margin-left: -50% + .dropdown-menu select,.dropdown-menu input + display: block &.oe_active .oe_overlay_options display: block @@ -365,6 +447,10 @@ $navbar_height: 51px .oe_ace_view_editor transition: all 0.2s ease-in + -webkit-transition: all 0.2s ease-in + -ms-transition: all 0.2s ease-in + -o-transition: all 0.2s ease-in + -moz-transition: all 0.2s ease-in opacity: 0.3 position: fixed top: $navbar_height diff --git a/addons/website/static/src/css/snippets.css b/addons/website/static/src/css/snippets.css index 5b7d404a1fd..628689b78cf 100644 --- a/addons/website/static/src/css/snippets.css +++ b/addons/website/static/src/css/snippets.css @@ -206,4 +206,16 @@ [data-snippet-id] { min-height: 10px; min-width: 10px; -} \ No newline at end of file +} + + +/* Custom CSS for snippets */ +.carousel.oe_small .carousel-inner { + height: 200px; +} +.carousel.oe_medium .carousel-inner { + height: 300px; +} +.carousel.oe_big .carousel-inner { + height: 450px; +} diff --git a/addons/website/static/src/css/website.css b/addons/website/static/src/css/website.css index a016cbc150b..be107ae0623 100644 --- a/addons/website/static/src/css/website.css +++ b/addons/website/static/src/css/website.css @@ -79,6 +79,78 @@ margin-bottom: 0px !important; } +.pt128 { + padding-top: 128px !important; +} + +.pt92 { + padding-top: 92px !important; +} + +.pt64 { + padding-top: 64px !important; +} + +.pt48 { + padding-top: 48px !important; +} + +.pt32 { + padding-top: 32px !important; +} + +.pt16 { + padding-top: 16px !important; +} + +.pt8 { + padding-top: 8px !important; +} + +.pt4 { + padding-top: 4px !important; +} + +.pt0 { + padding-top: 0px !important; +} + +.pb128 { + padding-bottom: 128px !important; +} + +.pb92 { + padding-bottom: 92px !important; +} + +.pb64 { + padding-bottom: 64px !important; +} + +.pb48 { + padding-bottom: 48px !important; +} + +.pb32 { + padding-bottom: 32px !important; +} + +.pb16 { + padding-bottom: 16px !important; +} + +.pb8 { + padding-bottom: 8px !important; +} + +.pb4 { + padding-bottom: 4px !important; +} + +.pb0 { + padding-bottom: 0px !important; +} + /* Grid of unequally tall elements */ .grid > [class*="col-md"] { display: inline-block; @@ -96,6 +168,29 @@ vertical-align: bottom; } +/* 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); + margin: 0 auto; +} + +h1.text-muted, h2.text-muted, h3.text-muted { + margin-top: 10px; +} + +/* ----- BOOTSTRAP HACK FOR HEADER NAV BAR ----- */ +div#wrap > .carousel:first-child, div#wrap > .jumbotron:first-child { + top: -20px; + position: relative; +} + /* ----- BOOTSTRAP HACK FOR STICKY FOOTER ----- */ html, body { box-sizing: border-box; @@ -122,19 +217,87 @@ footer { padding-bottom: 12px; } +/* ---- HACK FOR COVERING UP CK EDITOR BOGUS P INSERTION --- */ +.oe_structure:empty, [data-oe-type=html]:empty, .oe_structure > .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"); + background-repeat: no-repeat; + background-position: center top; + height: 200px !important; +} + +.oe_structure > .oe_drop_zone.oe_insert:only-child, [data-oe-type=html] > .oe_drop_zone.oe_insert:only-child { + position: static; +} + +.oe_structure:empty:before, [data-oe-type=html]:empty:before, .oe_structure > .oe_drop_zone.oe_insert:only-child:before, [data-oe-type=html] > .oe_drop_zone.oe_insert:only-child:before { + content: "Drag Building Blocks Here"; + text-align: center; + display: block; + padding-top: 100px; + padding-bottom: 50px; + color: grey; + font-size: 24px; +} + /* ---- HACK FOR COVERING UP CK EDITOR BOGUS P INSERTION --- */ .navbar .nav > li > p { margin-bottom: 0px; } -.carousel .item { - color: white; +.carousel-inner .item { + height: 100%; +} + +.carousel .carousel-caption h1, .carousel .carousel-caption h2, .carousel .carousel-caption h3 { + margin-top: 10px; + margin-bottom: 10px; +} +.carousel .carousel-image { + right: 10%; + left: 50%; + top: 5%; + bottom: 5%; + position: absolute; + text-align: center; +} +.carousel .carousel-image img { + height: 100%; + 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; +} +.carousel .item.text_only .carousel-caption { + left: 10%; + right: 10%; + top: 10%; + text-align: center; +} +.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%; } /* ---- HOMEPAGE THEME CUSTOMIZATION ---- */ .dark { background: #eff8f8; - background: rgba(100, 200, 200, 0.1); + 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; @@ -144,8 +307,8 @@ footer { padding-left: 16px; } -div#wrap .carousel { - top: -20px; +#themes-list .well { + padding: 0 0 20px 0; } /* -- Hack for removing double scrollbar from mobile preview -- */ diff --git a/addons/website/static/src/css/website.sass b/addons/website/static/src/css/website.sass index a4b07b2969c..bef7a59e77d 100644 --- a/addons/website/static/src/css/website.sass +++ b/addons/website/static/src/css/website.sass @@ -49,6 +49,44 @@ .mb0 margin-bottom: 0px !important +.pt128 + padding-top: 128px !important +.pt92 + padding-top: 92px !important +.pt64 + padding-top: 64px !important +.pt48 + padding-top: 48px !important +.pt32 + padding-top: 32px !important +.pt16 + padding-top: 16px !important +.pt8 + padding-top: 8px !important +.pt4 + padding-top: 4px !important +.pt0 + padding-top: 0px !important + +.pb128 + padding-bottom: 128px !important +.pb92 + padding-bottom: 92px !important +.pb64 + padding-bottom: 64px !important +.pb48 + padding-bottom: 48px !important +.pb32 + padding-bottom: 32px !important +.pb16 + padding-bottom: 16px !important +.pb8 + padding-bottom: 8px !important +.pb4 + padding-bottom: 4px !important +.pb0 + padding-bottom: 0px !important + /* Grid of unequally tall elements */ .grid > [class*="col-md"] display: inline-block @@ -62,6 +100,29 @@ &.grid-align-bottom > [class*="col-md"] vertical-align: bottom +/* 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) + margin: 0 auto + +h1.text-muted, h2.text-muted, h3.text-muted + margin-top: 10px + +/* ----- BOOTSTRAP HACK FOR HEADER NAV BAR ----- */ + +div#wrap + > .carousel:first-child, > .jumbotron:first-child + top: -20px + position: relative + /* ----- BOOTSTRAP HACK FOR STICKY FOOTER ----- */ html,body @@ -86,6 +147,26 @@ footer /* ---- HACK FOR COVERING UP CK EDITOR BOGUS P INSERTION --- */ +.oe_structure:empty, [data-oe-type=html]:empty, .oe_structure > .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') + background-repeat: no-repeat + background-position: center top + height: 200px !important + +.oe_structure > .oe_drop_zone.oe_insert:only-child, [data-oe-type=html] > .oe_drop_zone.oe_insert:only-child + position: static + +.oe_structure:empty:before, [data-oe-type=html]:empty:before, .oe_structure > .oe_drop_zone.oe_insert:only-child:before, [data-oe-type=html] > .oe_drop_zone.oe_insert:only-child:before + content: 'Drag Building Blocks Here' + text-align: center + display: block + padding-top: 100px + padding-bottom: 50px + color: grey + font-size: 24px + +/* ---- HACK FOR COVERING UP CK EDITOR BOGUS P INSERTION --- */ + .navbar .nav > li > p margin-bottom: 0px @@ -95,14 +176,57 @@ footer // .nav > li a // display: block -.carousel .item - color: white +.carousel-inner .item + height: 100% + +.carousel + .carousel-caption + h1, h2, h3 + margin-top: 10px + margin-bottom: 10px + .carousel-image + right: 10% + 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) + .item.text_only + .carousel-image + display: none + .carousel-caption + left: 10% + right: 10% + top: 10% + text-align: center + .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% /* ---- HOMEPAGE THEME CUSTOMIZATION ---- */ .dark background: #eff8f8 - background: rgba(100, 200, 200, 0.1) + 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 @@ -110,9 +234,9 @@ footer .nav-hierarchy padding-left: 16px -div#wrap - .carousel - top: -20px +#themes-list + .well + padding: 0 0 20px 0 /* -- Hack for removing double scrollbar from mobile preview -- */ div#mobile-preview.modal @@ -157,4 +281,4 @@ a[data-publish] top: -10px display: none /*&:hover > [data-publish]*/ - /* display: block*/ \ No newline at end of file + /* display: block*/ diff --git a/addons/website/static/src/img/banner/aqua.jpg b/addons/website/static/src/img/banner/aqua.jpg new file mode 100644 index 00000000000..47ce77005d4 Binary files /dev/null and b/addons/website/static/src/img/banner/aqua.jpg differ diff --git a/addons/website/static/src/img/banner/baby_blue.jpg b/addons/website/static/src/img/banner/baby_blue.jpg new file mode 100644 index 00000000000..38a3d85bc28 Binary files /dev/null and b/addons/website/static/src/img/banner/baby_blue.jpg differ diff --git a/addons/website/static/src/img/banner/banner_picture.png b/addons/website/static/src/img/banner/banner_picture.png new file mode 100644 index 00000000000..47836637498 Binary files /dev/null and b/addons/website/static/src/img/banner/banner_picture.png differ diff --git a/addons/website/static/src/img/banner/black.jpg b/addons/website/static/src/img/banner/black.jpg new file mode 100644 index 00000000000..7aaee7a0fb4 Binary files /dev/null and b/addons/website/static/src/img/banner/black.jpg differ diff --git a/addons/website/static/src/img/banner/business_guy.jpg b/addons/website/static/src/img/banner/business_guy.jpg new file mode 100644 index 00000000000..691729beb7e Binary files /dev/null and b/addons/website/static/src/img/banner/business_guy.jpg differ diff --git a/addons/website/static/src/img/banner/color_splash.jpg b/addons/website/static/src/img/banner/color_splash.jpg new file mode 100644 index 00000000000..e17ae39e70f Binary files /dev/null and b/addons/website/static/src/img/banner/color_splash.jpg differ diff --git a/addons/website/static/src/img/banner/flower.jpg b/addons/website/static/src/img/banner/flower.jpg new file mode 100644 index 00000000000..7208e4c99d9 Binary files /dev/null and b/addons/website/static/src/img/banner/flower.jpg differ diff --git a/addons/website/static/src/img/banner/flower_field.jpg b/addons/website/static/src/img/banner/flower_field.jpg new file mode 100644 index 00000000000..eec572fbb89 Binary files /dev/null and b/addons/website/static/src/img/banner/flower_field.jpg differ diff --git a/addons/website/static/src/img/greenfields.jpg b/addons/website/static/src/img/banner/greenfields.jpg similarity index 100% rename from addons/website/static/src/img/greenfields.jpg rename to addons/website/static/src/img/banner/greenfields.jpg diff --git a/addons/website/static/src/img/banner/landscape.jpg b/addons/website/static/src/img/banner/landscape.jpg new file mode 100644 index 00000000000..35dfa6206c4 Binary files /dev/null and b/addons/website/static/src/img/banner/landscape.jpg differ diff --git a/addons/website/static/src/img/landscape.png b/addons/website/static/src/img/banner/landscape.png similarity index 100% rename from addons/website/static/src/img/landscape.png rename to addons/website/static/src/img/banner/landscape.png diff --git a/addons/website/static/src/img/banner/mango.jpg b/addons/website/static/src/img/banner/mango.jpg new file mode 100644 index 00000000000..9f88e2fb987 Binary files /dev/null and b/addons/website/static/src/img/banner/mango.jpg differ diff --git a/addons/website/static/src/img/banner/mountains.jpg b/addons/website/static/src/img/banner/mountains.jpg new file mode 100644 index 00000000000..47e53c82c8c Binary files /dev/null and b/addons/website/static/src/img/banner/mountains.jpg differ diff --git a/addons/website/static/src/img/banner/orange_red.jpg b/addons/website/static/src/img/banner/orange_red.jpg new file mode 100644 index 00000000000..18838410c9f Binary files /dev/null and b/addons/website/static/src/img/banner/orange_red.jpg differ diff --git a/addons/website/static/src/img/banner/velour.jpg b/addons/website/static/src/img/banner/velour.jpg new file mode 100644 index 00000000000..f3b0cf1d528 Binary files /dev/null and b/addons/website/static/src/img/banner/velour.jpg differ diff --git a/addons/website/static/src/img/banner/wood.jpg b/addons/website/static/src/img/banner/wood.jpg new file mode 100644 index 00000000000..738ff955918 Binary files /dev/null and b/addons/website/static/src/img/banner/wood.jpg differ diff --git a/addons/website/static/src/img/banner/yellow_green.jpg b/addons/website/static/src/img/banner/yellow_green.jpg new file mode 100644 index 00000000000..efd4dad9278 Binary files /dev/null and b/addons/website/static/src/img/banner/yellow_green.jpg differ diff --git a/addons/website/static/src/img/big_picture.png b/addons/website/static/src/img/big_picture.png new file mode 100644 index 00000000000..cecb39679d5 Binary files /dev/null and b/addons/website/static/src/img/big_picture.png differ diff --git a/addons/website/static/src/img/drag_here.png b/addons/website/static/src/img/drag_here.png new file mode 100644 index 00000000000..f2e3d38b2df Binary files /dev/null and b/addons/website/static/src/img/drag_here.png differ diff --git a/addons/website/static/src/img/image_text.jpg b/addons/website/static/src/img/image_text.jpg new file mode 100644 index 00000000000..f34acfc771b Binary files /dev/null and b/addons/website/static/src/img/image_text.jpg differ diff --git a/addons/website/static/src/img/landscape.jpg b/addons/website/static/src/img/landscape.jpg new file mode 100644 index 00000000000..8c4d19c947e Binary files /dev/null and b/addons/website/static/src/img/landscape.jpg differ diff --git a/addons/website/static/src/img/openerp_logo.png b/addons/website/static/src/img/openerp_logo.png new file mode 100644 index 00000000000..285a059cb06 Binary files /dev/null and b/addons/website/static/src/img/openerp_logo.png differ diff --git a/addons/website/static/src/img/text_image.png b/addons/website/static/src/img/text_image.png new file mode 100644 index 00000000000..ac02ef230c7 Binary files /dev/null and b/addons/website/static/src/img/text_image.png differ diff --git a/addons/website/static/src/js/website.ace.js b/addons/website/static/src/js/website.ace.js index fd26c50aefd..ac7b89d45ba 100644 --- a/addons/website/static/src/js/website.ace.js +++ b/addons/website/static/src/js/website.ace.js @@ -3,20 +3,33 @@ var globalEditor; + var hash = "#advanced-view-editor"; + var website = openerp.website; website.templates.push('/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(this); + globalEditor.appendTo($(document.body)); + } + } + website.EditorBar.include({ events: _.extend({}, website.EditorBar.prototype.events, { - 'click a[data-action=ace]': 'launchAce', + 'click a[data-action=ace]': 'launch', }), - launchAce: function () { - if (globalEditor) { - globalEditor.open(); - } else { - globalEditor = new website.ace.ViewEditor(this); - globalEditor.appendTo($(document.body)); - } + launch: function (e) { + e.preventDefault(); + launch(); }, }); @@ -55,88 +68,142 @@ website.ace.ViewEditor = openerp.Widget.extend({ template: 'website.ace_view_editor', events: { - 'change #ace-view-list': 'displayView', - 'click button[data-action=save]': 'saveView', + 'change #ace-view-list': 'displaySelectedView', + 'click button[data-action=save]': 'saveViews', 'click button[data-action=format]': 'formatXml', 'click button[data-action=close]': 'close', }, + init: function (parent) { + this.buffers = {}; + this._super(parent); + }, start: function () { var self = this; + self.aceEditor = ace.edit(self.$('#ace-view-editor')[0]); + self.aceEditor.setTheme("ace/theme/monokai"); var viewId = $(document.documentElement).data('view-xmlid'); openerp.jsonRpc('/website/customize_template_get', 'call', { 'xml_id': viewId, 'optional': false, }).then(function (views) { - self.loadViewList.call(self, views); + self.loadViews.call(self, views); + self.open.call(self); + }); + }, + loadViews: function (views) { + var self = this; + var activeViews = _.filter(views, function (view) { + return view.active; + }); + var $viewList = self.$('#ace-view-list'); + _.each(activeViews, function (view) { + if (view.id) { + new website.ace.ViewOption(self, view).appendTo($viewList); + self.loadView(view.id); + } + }); + }, + loadView: function (id) { + var viewId = parseInt(id, 10); + var self = this; + openerp.jsonRpc('/web/dataset/call', 'call', { + model: 'ir.ui.view', + method: 'read', + args: [[viewId], ['arch'], website.get_context()], + }).then(function(result) { + var editingSession = self.buffers[viewId] = new ace.EditSession(result[0].arch);; + editingSession.setMode("ace/mode/xml"); + editingSession.setUndoManager(new ace.UndoManager()); + editingSession.on("change", function () { + setTimeout(function () { + var $option = self.$('#ace-view-list').find('[value='+viewId+']'); + var bufferName = $option.text(); + var dirtyMarker = " (unsaved changes)"; + var isDirty = editingSession.getUndoManager().hasUndo(); + if (isDirty && bufferName.indexOf(dirtyMarker) < 0) { + $option.text(bufferName + dirtyMarker); + } else if (!isDirty && bufferName.indexOf(dirtyMarker) > 0) { + $option.text(bufferName.substring(0, bufferName.indexOf(dirtyMarker))); + } + }, 1); + }); + if (viewId === self.selectedViewId()) { + self.displayView.call(self, viewId); + } }); - this.$el.hover(); }, selectedViewId: function () { return parseInt(this.$('#ace-view-list').val(), 10); }, - loadViewList: function (views) { - var activeViews = _.filter(views, function (view) { - return view.active; - }); - var $viewList = this.$('#ace-view-list'); - _.each(activeViews, function (view) { - if (view.id) { - new website.ace.ViewOption(this, view).appendTo($viewList); - } - }); - var editor = ace.edit(this.$('#ace-view-editor')[0]); - editor.setTheme("ace/theme/monokai"); - this.aceEditor = editor; - this.open(); + displayView: function (id) { + var viewId = parseInt(id, 10); + var editingSession = this.buffers[viewId]; + if (editingSession) { + this.aceEditor.setSession(editingSession); + } }, - displayView: function () { - var editor = this.aceEditor; - openerp.jsonRpc('/web/dataset/call', 'call', { - model: 'ir.ui.view', - method: 'read', - args: [[this.selectedViewId()], ['arch'], website.get_context()], - }).then(function(result) { - var xml = new website.ace.XmlDocument(result[0].arch); - var editingSession = new ace.EditSession(xml.xml); - editingSession.setMode("ace/mode/xml"); - editingSession.setUndoManager(new ace.UndoManager()); - editor.setSession(editingSession); - }); + displaySelectedView: function () { + this.displayView(this.selectedViewId()); + this.updateHash(); }, formatXml: function () { var xml = new website.ace.XmlDocument(this.aceEditor.getValue()); this.aceEditor.setValue(xml.format()); }, - saveView: function () { + saveViews: function () { var self = this; - var xml = new website.ace.XmlDocument(this.aceEditor.getValue()); + var toSave = _.filter(_.map(self.buffers, function (editingSession, viewId) { + return { + id: parseInt(viewId, 10), + isDirty: editingSession.getUndoManager().hasUndo(), + text: editingSession.getValue(), + }; + }), function (session) { + return session.isDirty; + }); + var requests = _.map(toSave, self.saveView); + $.when.apply($, requests).then(function () { + self.reloadPage.call(self); + }).fail(function () { + self.displayError.call(self); + }); + }, + saveView: function (session) { + var xml = new website.ace.XmlDocument(session.text); if (xml.isWellFormed()) { - openerp.jsonRpc('/web/dataset/call', 'call', { + return openerp.jsonRpc('/web/dataset/call', 'call', { model: 'ir.ui.view', method: 'write', - args: [[this.selectedViewId()], { 'arch': xml.xml }, website.get_context()], - }).then(function(result) { - self.reloadPage(); - }).fail(function (error) { - self.displayError(error); + args: [[session.id], { 'arch': xml.xml }, website.get_context()], }); } else { - self.displayError(); + return $.Deferred().fail("Malformed XML document"); } }, + updateHash: function () { + window.location.hash = hash + "?view=" + this.selectedViewId(); + }, reloadPage: function () { - // TODO Reload { header + div#wrap + footer } only + this.updateHash(); window.location.reload(); }, displayError: function (error) { // TODO Improve feedback (e.g. update 'Save' button + tooltip) - alert("Malformed XML document"); + alert(error); }, open: function () { this.$el.removeClass('oe_ace_closed').addClass('oe_ace_open'); - this.displayView(); + var curentHash = window.location.hash; + var indexOfView = curentHash.indexOf("?view="); + if (indexOfView >= 0) { + var viewId = parseInt(curentHash.substring(indexOfView + 6, curentHash.length), 10); + this.$('#ace-view-list').val(viewId).change(); + } else { + window.location.hash = hash; + } }, close: function () { + window.location.hash = ""; var self = this; this.$el.bind('transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd', function () { globalEditor = null; diff --git a/addons/website/static/src/js/website.editor.js b/addons/website/static/src/js/website.editor.js index 5a8801bba38..df7245a024f 100644 --- a/addons/website/static/src/js/website.editor.js +++ b/addons/website/static/src/js/website.editor.js @@ -184,10 +184,17 @@ this.rte = new website.RTE(this); this.rte.on('change', this, this.proxy('rte_changed')); + var instanceReady = false; + this.rte.on('instanceReady', this, function () { + clearTimeout(instanceReady); + instanceReady = setTimeout(function () { + self.trigger('rte:ready'); + }, 0); + }); return $.when( this._super.apply(this, arguments), - this.rte.prependTo(this.$('#website-top-edit .nav.pull-right')) + this.rte.appendTo(this.$('#website-top-edit .nav.pull-right')) ); }, edit: function () { @@ -315,6 +322,8 @@ 'magicline' ]; return { + // FIXME + language: 'en', // Disable auto-generated titles // FIXME: accessibility, need to generate user-sensible title, used for @title and @aria-label title: false, @@ -589,23 +598,34 @@ }, 'change input[type=file]': 'file_selection', 'change input.url': 'preview_image', - 'click .existing-attachments a': 'select_existing', + 'click a[href=#existing]': 'browse_existing', + 'change select.image-style': 'preview_image', }), start: function () { var selection = this.editor.getSelection(); var el = selection && selection.getSelectedElement(); this.element = null; + + var $select = this.$('.image-style'); + var $options = $select.children(); + this.image_styles = $options.map(function () { return this.value; }).get(); + if (el && el.is('img')) { this.element = el; + _(this.image_styles).each(function (style) { + if (el.hasClass(style)) { + $select.val(style); + } + }); + // set_image must follow setup of image style this.set_image(el.getAttribute('src')); } - return $.when( - this._super(), - this.fetch_existing().then(this.proxy('fetched_existing'))); + return this._super(); }, save: function () { var url = this.$('input.url').val(); + var style = this.$('.image-style').val(); var element, editor = this.editor; if (!(element = this.element)) { element = editor.document.createElement('img'); @@ -619,7 +639,10 @@ }, 0); } element.setAttribute('src', url); - this._super(); + $(element.$).removeClass(this.image_styles.join(' ')); + if (style) { element.addClass(style); } + + return this._super(); }, /** @@ -657,11 +680,48 @@ var image = this.$('input.url').val(); if (!image) { return; } - this.$('img.image-preview').attr('src', image); + this.$('img.image-preview') + .attr('src', image) + .removeClass(this.image_styles.join(' ')) + .addClass(this.$('select.image-style').val()); + }, + + browse_existing: function (e) { + e.preventDefault(); + new website.editor.ExistingImageDialog(this).appendTo(document.body); + }, + }); + + var IMAGES_PER_ROW = 3; + var IMAGES_ROWS = 3; + website.editor.ExistingImageDialog = website.editor.Dialog.extend({ + template: 'website.editor.dialog.image.existing', + events: _.extend({}, website.editor.Dialog.prototype.events, { + 'click .existing-attachments a': 'select_existing', + 'click .pager > li': function (e) { + e.preventDefault(); + var $target = $(e.currentTarget); + if ($target.hasClass('disabled')) { + return; + } + this.page += $target.hasClass('previous') ? -1 : 1; + this.display_attachments(); + }, + }), + init: function (parent) { + this.image = null; + this.page = 0; + this.parent = parent; + this._super(parent.editor); + }, + + start: function () { + return $.when( + this._super(), + this.fetch_existing().then(this.proxy('fetched_existing'))); }, fetch_existing: function () { - // FIXME: lazy load attachments? return openerp.jsonRpc('/web/dataset/call_kw', 'call', { model: 'ir.attachment', method: 'search_read', @@ -675,17 +735,42 @@ }); }, fetched_existing: function (records) { + this.records = records; + this.display_attachments(); + }, + display_attachments: function () { + var per_screen = IMAGES_PER_ROW * IMAGES_ROWS; + + var from = this.page * per_screen; + var records = this.records; + // Create rows of 3 records var rows = _(records).chain() - .groupBy(function (_, index) { return Math.floor(index / 3); }) + .slice(from, from + per_screen) + .groupBy(function (_, index) { return Math.floor(index / IMAGES_PER_ROW); }) .values() .value(); + this.$('.existing-attachments').replaceWith( - openerp.qweb.render('website.editor.dialog.image.existing', {rows: rows})); + openerp.qweb.render( + 'website.editor.dialog.image.existing.content', {rows: rows})); + this.$('.pager') + .find('li.previous').toggleClass('disabled', (from === 0)).end() + .find('li.next').toggleClass('disabled', (from + per_screen >= records.length)); + }, select_existing: function (e) { e.preventDefault(); - this.set_image(e.currentTarget.getAttribute('href')); + this.$('a.thumbnail.selected').removeClass('selected'); + + $(e.currentTarget).addClass('selected'); + }, + save: function () { + var link = this.$('a.thumbnail.selected').attr('href'); + if (link) { + this.parent.set_image(link); + } + this._super(); }, }); diff --git a/addons/website/static/src/js/website.seo.js b/addons/website/static/src/js/website.seo.js index e9053740eae..49cc65e1b09 100644 --- a/addons/website/static/src/js/website.seo.js +++ b/addons/website/static/src/js/website.seo.js @@ -20,12 +20,31 @@ events: { 'click .js_seo_suggestion': 'select', }, - init: function (parent, keyword) { + init: function (parent, keyword, htmlPage) { this.keyword = keyword; + this.htmlPage = htmlPage; + this.type = this.computeType(); + this._super(parent); + }, + start: function () { + var self = this; + function update () { + self.updateType(); + } + self.htmlPage.on('title-changed', self, update); + self.htmlPage.on('description-changed', self, update); + }, + computeType: function () { // cf. http://getbootstrap.com/components/#labels // default, primary, success, info, warning, danger - this.type = 'default'; - this._super(parent); + return this.htmlPage.isInTitle(this.keyword) ? 'success' + : this.htmlPage.isInDescription(this.keyword) ? 'primary' + : this.htmlPage.isInBody(this.keyword) ? 'info' + : 'default'; + }, + updateType: function () { + this.type = this.computeType(); + this.renderElement(); }, select: function () { this.trigger('selected', this.keyword); @@ -34,8 +53,9 @@ website.seo.SuggestionList = openerp.Widget.extend({ template: 'website.seo_list', - init: function (parent, word) { + init: function (parent, word, htmlPage) { this.word = word; + this.htmlPage = htmlPage; this._super(parent); }, start: function () { @@ -54,7 +74,7 @@ // TODO Order properly ? _.each(_.uniq(cleanList), function (keyword) { if (keyword) { - var suggestion = new website.seo.Suggestion(self, keyword); + var suggestion = new website.seo.Suggestion(self, keyword, self.htmlPage); suggestion.on('selected', self, function (word) { self.trigger('selected', word); }); @@ -72,21 +92,37 @@ 'click a[data-action=remove-keyword]': 'destroy', }, maxWordsPerKeyword: 4, // TODO Check - init: function (parent, keyword) { + init: function (parent, keyword, htmlPage) { this.keyword = keyword; - // cf. http://getbootstrap.com/components/#labels - // default, primary, success, info, warning, danger - this.type = 'warning'; + this.htmlPage = htmlPage; + this.type = this.computeType(); this._super(parent); }, start: function () { var self = this; - self.suggestionList = new website.seo.SuggestionList(self, this.keyword); + function update () { + self.updateType(); + } + self.htmlPage.on('title-changed', self, update); + self.htmlPage.on('description-changed', self, update); + self.suggestionList = new website.seo.SuggestionList(self, this.keyword, this.htmlPage); self.suggestionList.on('selected', self, function (word) { self.trigger('selected', word); }); this.suggestionList.appendTo(this.$('.js_seo_keyword_suggestion')); }, + computeType: function () { + // cf. http://getbootstrap.com/components/#labels + // default, primary, success, info, warning, danger + return this.htmlPage.isInTitle(this.keyword) ? 'success' + : this.htmlPage.isInDescription(this.keyword) ? 'primary' + : this.htmlPage.isInBody(this.keyword) ? 'warning' + : 'default'; + }, + updateType: function () { + this.type = this.computeType(); + this.$('span.js_seo_keyword').attr('class', "label label-"+this.type+" js_seo_keyword"); + }, destroy: function () { this.trigger('removed'); this._super(); @@ -96,6 +132,10 @@ website.seo.KeywordList = openerp.Widget.extend({ template: 'website.seo_list', maxKeywords: 10, + init: function (parent, htmlPage) { + this.htmlPage = htmlPage; + this._super(parent); + }, keywords: function () { var result = []; this.$('span.js_seo_keyword').each(function () { @@ -114,7 +154,7 @@ // TODO Refine var word = candidate ? candidate.replace(/[,;.:<>]+/g, " ").replace(/ +/g, " ").trim() : ""; if (word && !self.isKeywordListFull() && !self.isExistingKeyword(word)) { - var keyword = new website.seo.Keyword(self, word); + var keyword = new website.seo.Keyword(self, word, this.htmlPage); keyword.on('removed', self, function () { self.trigger('list-not-full'); self.trigger('removed', word); @@ -141,9 +181,13 @@ website.seo.ImageList = openerp.Widget.extend({ + init: function (parent, htmlPage) { + this.htmlPage = htmlPage; + this._super(parent); + }, start: function () { var self = this; - new website.seo.PageParser().images().each(function (index, image) { + this.htmlPage.images().each(function (index, image) { new website.seo.Image(self, image).appendTo(self.$el); }); }, @@ -163,14 +207,32 @@ }, }); - website.seo.PageParser = openerp.Class.extend({ + website.seo.HtmlPage = openerp.Class.extend(openerp.PropertiesMixin, { url: function () { var url = window.location.href; var hashIndex = url.indexOf('#'); return hashIndex >= 0 ? url.substring(0, hashIndex) : url; }, title: function () { - return $(document.title).text(); + return $('title').text().trim(); + }, + changeTitle: function (title) { + $('title').text(title); + this.trigger('title-changed', title); + }, + description: function () { + return $('meta[name=description]').attr('value').trim(); + }, + changeDescription: function (description) { + $('meta[name=description]').attr('value', description); + this.trigger('description-changed', description); + }, + keywords: function () { + return $('meta[name=keywords]').attr('value').split(","); + }, + changeKeywords: function (keywords) { + $('meta[name=keywords]').attr('value', keyword.join(",")); + this.trigger('keywords-changed', keywords); }, headers: function (tag) { return $('#wrap '+tag).map(function () { @@ -189,6 +251,18 @@ company: function () { return $('meta[name="openerp.company"]').attr('value'); }, + bodyText: function () { + return $('body').children().not('.js_seo_configuration').text(); + }, + isInBody: function (text) { + return new RegExp(text, "gi").test(this.bodyText()); + }, + isInTitle: function (text) { + return new RegExp(text, "gi").test(this.title()); + }, + isInDescription: function (text) { + return new RegExp(text, "gi").test(this.description()); + }, }); website.seo.Tip = openerp.Widget.extend({ @@ -208,23 +282,26 @@ website.seo.Configurator = openerp.Widget.extend({ template: 'website.seo_configuration', events: { - 'keypress input[name=seo_page_keywords]': 'confirmKeyword', + 'keyup input[name=seo_page_keywords]': 'confirmKeyword', + 'keyup input[name=seo_page_title]': 'titleChanged', + 'keyup textarea[name=seo_page_description]': 'descriptionChanged', 'click button[data-action=add]': 'addKeyword', 'click button[data-action=update]': 'update', - 'hidden.bs.modal': 'destroy' + 'hidden.bs.modal': 'destroy', }, maxTitleSize: 65, maxDescriptionSize: 155, start: function () { var self = this; var $modal = self.$el; - var pageParser = new website.seo.PageParser(); - $modal.find('.js_seo_page_url').text(pageParser.url()); - $modal.find('input[name=seo_page_title]').val(pageParser.title()); - self.suggestImprovements(pageParser); - self.imageList = new website.seo.ImageList(self); + var htmlPage = this.htmlPage = new website.seo.HtmlPage(); + $modal.find('.js_seo_page_url').text(htmlPage.url()); + $modal.find('input[name=seo_page_title]').val(htmlPage.title()); + $modal.find('textarea[name=seo_page_description]').val(htmlPage.description()); + self.suggestImprovements(); + self.imageList = new website.seo.ImageList(self, htmlPage); self.imageList.appendTo($modal.find('.js_seo_image_list')); - self.keywordList = new website.seo.KeywordList(self); + self.keywordList = new website.seo.KeywordList(self, htmlPage); self.keywordList.on('list-full', self, function () { $modal.find('input[name=seo_page_keywords]') .attr('readonly', "readonly") @@ -242,11 +319,11 @@ self.keywordList.add(word); }); self.keywordList.appendTo($modal.find('.js_seo_keywords_list')); - var companyName = pageParser.company().toLowerCase(); + var companyName = htmlPage.company().toLowerCase(); self.addKeyword(companyName); $modal.modal(); }, - suggestImprovements: function (parser) { + suggestImprovements: function () { var tips = []; var self = this; function displayTip(message, type) { @@ -255,14 +332,14 @@ type: type, }).appendTo(self.$('.js_seo_tips')); } - var pageParser = parser || new website.seo.PageParser(); - if (pageParser.headers('h1').length === 0) { + var htmlPage = this.htmlPage; + if (htmlPage.headers('h1').length === 0) { tips.push({ type: 'warning', message: "You don't have an <h1> tag on your page.", }); } - if (pageParser.headers('h1').length > 1) { + if (htmlPage.headers('h1').length > 1) { tips.push({ type: 'warning', message: "You have more than one <h1> tag on your page.", @@ -289,13 +366,28 @@ }, update: function () { var data = { - title: this.$('input[name=seo_page_title]').val(), - description: this.$('input[name=seo_page_title]').val(), + title: this.htmlPage.title(), + description: this.htmlPage.description(), keywords: this.keywordList.keywords(), images: this.imageList.images(), }; console.log(data); // TODO Persist changes + this.$el.modal('hide'); + }, + titleChanged: function () { + var self = this; + setTimeout(function () { + var title = self.$('input[name=seo_page_title]').val(); + self.htmlPage.changeTitle(title); + }, 1); + }, + descriptionChanged: function () { + var self = this; + setTimeout(function () { + var description = self.$('textarea[name=seo_page_description]').attr('value'); + self.htmlPage.changeDescription(description); + }, 1); }, }); })(); diff --git a/addons/website/static/src/js/website.snippets.js b/addons/website/static/src/js/website.snippets.js index b9086f5eaca..28eddad9bdb 100644 --- a/addons/website/static/src/js/website.snippets.js +++ b/addons/website/static/src/js/website.snippets.js @@ -5,54 +5,73 @@ website.templates.push('/website/static/src/xml/website.snippets.xml'); website.EditorBar.include({ - events: _.extend({}, website.EditorBar.prototype.events, { - 'click button[data-action=snippet]': 'snippet', - }), start: function () { - return this._super().then(function () { - - this.$buttons.snippet = this.$('button[data-action=snippet]'); - - }.bind(this)); + var self = this; + $("body").on('click', function (event) { + var tag = event.srcElement.tagName.toLowerCase(); + var $this = $(event.srcElement); + if (!(tag === 'a' || tag === "button") && !$this.parents("a, button").length) { + self.$('[data-action="edit"]').parent().effect('bounce', {distance: 18, times: 5}, 250); + } + }); + return this._super(); }, edit: function () { - window.snippets = this.snippets = new website.snippet.BuildingBlock(this); - this.snippets.appendTo($(document.body)); + $("body").off('click'); + var self = this; + self.on('rte:ready', this, function () { + + var $button = $(openerp.qweb.render('website.snippets_button')) + .click(function () {self.snippets.$el.toggleClass("hidden");}) + .prependTo(self.$("ul")); + window.snippets = self.snippets = new website.snippet.BuildingBlock(this); + self.snippets.appendTo(self.$el); + + }); return this._super.apply(this, arguments); }, - snippet: function (ev) { - this.snippets.toggle(); - }, save: function () { - $('body').trigger("save"); + remove_added_snippet_id(); this._super(); }, }); + + $(document).ready(function () { + hack_to_add_snippet_id(); + $("[data-snippet-id]").each(function() { + var $snipped_id = $(this); + if (typeof $snipped_id.data("snippet-view") === 'undefined' && + website.snippet.animationRegistry[$snipped_id.data("snippet-id")]) { + $snipped_id.data("snippet-view", new website.snippet.animationRegistry[$snipped_id.data("snippet-id")]($snipped_id)); + } + }); + }); + /* ----- SNIPPET SELECTOR ---- */ website.snippet = {}; + // puts $el at the same absolute position as $target - website.snippet.cover_target = function ($el, $target){ - $el.css({ - 'position': 'absolute', - 'width': $target.outerWidth(), - 'height': $target.outerHeight(), - }); - $el.css($target.offset()); - }; function hack_to_add_snippet_id () { _.each(website.snippet.selector, function (val) { $(val[0]).each(function() { - if ($(this).is("[data-snippet-id='"+ val[1]+"']")) - $(this).removeAttr("data-snippet-id"); - if (!$(this).is("[data-snippet-id]") && $(this).parents("[data-oe-model]").length) + if (!$(this).is("[data-snippet-id]") && $(this).parents("[data-oe-model]").length) { $(this).attr("data-snippet-id", val[1]); + } + }); + }); + } + function remove_added_snippet_id () { + _.each(website.snippet.selector, function (val) { + $(val[0]).each(function() { + if ($(this).data("snippet-id") === val[1]) { + $(this).removeAttr("data-snippet-id"); + } }); }); } - website.snippet.selector = []; website.snippet.BuildingBlock = openerp.Widget.extend({ @@ -65,9 +84,25 @@ $("
").appendTo('body'); } this.$active_snipped_id = false; - this.active = false; - this.parent_of_editable_box = "body > :not(:has(#website-top-view)):not(#oe_manipulators):not(#oe_snippets) "; hack_to_add_snippet_id(); + $("body").on('DOMNodeInserted', hack_to_add_snippet_id); + }, + dom_filter: function (dom) { + if (typeof dom === "string") { + var exclude = ":not(#oe_manipulators):not(#website-top-navbar):not(.oe_snippet)"; + var sdom = dom.split(','); + dom = ""; + _.each(sdom, function (val) { + val = val.replace(/^\s+|\s+$/g, ''); + dom += "body > " + exclude + " " + val + ", "; + val = val.split(" "); + dom += "body > "+ val.shift() + exclude + val.join(" ") + ", "; + }); + dom = dom.replace(/,\s*$/g, ''); + return $(dom); + } else { + return $(dom).parents("#oe_manipulators, #website-top-navbar").length || $(dom).hasClass("oe_snippet") ? $("") : $(dom); + } }, start: function() { var self = this; @@ -76,7 +111,7 @@ _.each(openerp.qweb.compiled_templates, function (val, key) { if (key.indexOf('website.snippets.') === 0) { var $snippet = $(openerp.qweb.render(key)).addClass("oe_snippet"); - if ($snippet.data("action")) { + if ($snippet.data("snippet-id")) { self.$el.find('#snippet_' + $snippet.data('category')).append($snippet); self.make_snippet_draggable($snippet); } @@ -85,13 +120,68 @@ this.bind_selected_manipulator(); this.bind_snippet_click_editor(); + this.activate_overlay_zones(); + + this.scrollspy(); + this.$el.addClass("hidden"); + }, + scrollspy: function (){ + var self = this; + var $ul = this.$("ul"); + var $pill = self.$(".pill-content"); + var padding = parseInt($pill.css("padding-left")); + var $scroll = this.$(".scroll"); + + $scroll.scroll(function () { + $pill.find("> div").each(function () { + if ($(this).position().left <= padding) { + $ul.find("> li").removeClass('active'); + $ul.find("a[href='#" + $(this).attr("id") + "']").parent("li").addClass('active'); + } + }); + }); + + $ul.find("a").click(function (event) { + event.preventDefault(); + $scroll.scrollLeft( $scroll.scrollLeft() + $($(event.currentTarget).attr("href")).position().left - padding ); + return false; + }); + + $pill.css("padding-right", self.$el.outerWidth() - padding + 10 - $pill.find("> div:last").outerWidth()); + $ul.find("> li:first").addClass('active'); + + this.$el.mouseenter(function () { + self.$el.css("height", ""); + }); + this.$el.mouseleave(function () { + self.$el.css("height", "110px"); + }); + this.$el.mouseleave(); + }, + cover_target: function ($el, $target){ + var pos = $target.offset(); + 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, + 'top': pos.top - mt, + 'left': pos.left + }); + }, + show: function () { + this.$el.removeClass("hidden"); + }, + hide: function () { + this.$el.addClass("hidden"); }, bind_snippet_click_editor: function () { var self = this; var snipped_event_flag = false; $("body").on('click', "[data-snippet-id]", function (event) { - if (!self.active || snipped_event_flag) { + if (snipped_event_flag) { return; } var $target = $(event.currentTarget); @@ -151,10 +241,10 @@ create_overlay: function ($snipped_id) { if (typeof $snipped_id.data("snippet-editor") === 'undefined') { this.activate_overlay_zones($snipped_id); - var editor = website.snippet.editorRegistry[$snipped_id.data("snippet-id")] || website.snippet.Editor; + var editor = website.snippet.editorRegistry[$snipped_id.data("snippet-id")] || website.snippet.editorRegistry.resize; $snipped_id.data("snippet-editor", new editor(this, $snipped_id)); } - website.snippet.cover_target($snipped_id.data('overlay'), $snipped_id); + this.cover_target($snipped_id.data('overlay'), $snipped_id); }, path_eval: function(path){ @@ -170,20 +260,18 @@ var self = this; var $selected_target = null; $("body").mouseover(function (event){ - if (!self.active) { - return; - } var $target = $(event.srcElement).parents("[data-snippet-id]:first"); + $target = self.dom_filter($target); if($target.length && !self.editor_busy) { if($selected_target != $target){ - if($selected_target){ + if($selected_target && $selected_target.data('overlay')){ $selected_target.data('overlay').removeClass('oe_selected'); } $selected_target = $target; self.create_overlay($target); $target.data('overlay').addClass('oe_selected'); } - } else if($selected_target) { + } else if($selected_target && $selected_target.data('overlay')) { $selected_target.data('overlay').removeClass('oe_selected'); } }); @@ -192,22 +280,45 @@ // activate drag and drop for the snippets in the snippet toolbar make_snippet_draggable: function($snippet){ var self = this; + var $toInsert = false; + var $tumb = $snippet.find(".oe_snippet_thumbnail"); $snippet.draggable({ helper: 'clone', zIndex: '1000', appendTo: 'body', cursor: "move", + handle: ".oe_snippet_thumbnail", + cursorAt: { + 'left': $tumb.outerWidth()/2, + 'top': $tumb.outerHeight()/2 + }, start: function(){ - var action = $snippet.data('action'); + self.hide(); + + var snipped_id = $snippet.data('snippet-id'); + var action = $snippet.find('.oe_snippet_body').size() ? 'insert' : 'mutate'; if( action === 'insert'){ + if (!$snippet.data('selector-siblings') && !$snippet.data('selector-children') && !$snippet.data('selector-vertical-children')) { + console.debug($snippet.data("snippet-id") + " have oe_snippet_body class and have not for insert action"+ + "data-selector-siblings, data-selector-children or data-selector-vertical-children tag for mutate action"); + return; + } self.activate_insertion_zones({ siblings: $snippet.data('selector-siblings'), - childs: $snippet.data('selector-childs'), - vertical_childs: $snippet.data('selector-vertical-childs') + children: $snippet.data('selector-children'), + vertical_children: $snippet.data('selector-vertical-children') }); - } else if( action === 'mutate' ){ + $toInsert = $snippet.find('.oe_snippet_body').clone(); + $toInsert.removeClass('oe_snippet_body'); + $toInsert.attr('data-snippet-id', snipped_id); + + } else if( action === 'mutate' ){ + if (!$snippet.data('selector')) { + console.debug($snippet.data("snippet-id") + " have not oe_snippet_body class and have not data-selector tag"); + return; + } var $targets = self.activate_overlay_zones($snippet.data('selector')); $targets.each(function(){ var $clone = $(this).data('overlay').clone(); @@ -219,42 +330,52 @@ $('.oe_drop_zone').droppable({ over: function(){ - // FIXME: stupid hack to prevent multiple droppable to activate at once ... - // it's not even working properly but it's better than nothing. - $(".oe_drop_zone.oe_hover").removeClass("oe_hover"); - $(this).addClass("oe_hover"); + if( action === 'insert'){ + $(this).first().after($toInsert); + } else { + // FIXME: stupid hack to prevent multiple droppable to activate at once ... + // it's not even working properly but it's better than nothing. + $(".oe_drop_zone.oe_hover").removeClass("oe_hover"); + $(this).addClass("oe_hover"); + } }, out: function(){ - $(this).removeClass("oe_hover"); + if( action === 'insert'){ + $toInsert.detach(); + } else { + $(this).removeClass("oe_hover"); + } }, drop: function(){ - var snipped_id = $snippet.data('snippet-id'); if (!$(".oe_drop_zone.oe_hover").length) { return false; } var $target = false; - if($snippet.find('.oe_snippet_body').size()){ - var $toInsert = $snippet.find('.oe_snippet_body').clone(); - $toInsert.removeClass('oe_snippet_body'); - $toInsert.attr('data-snippet-id', snipped_id); - $(".oe_drop_zone.oe_hover").after($toInsert); + if(action === 'insert'){ $target = $toInsert; - hack_to_add_snippet_id(); - } else { - $target = $(".oe_drop_zone.oe_hover").data('target'); - } - if (website.snippet.animationRegistry[snipped_id]) { - new website.snippet.animationRegistry[snipped_id]($target); - } - if (website.snippet.editorRegistry[snipped_id]) { + if (website.snippet.animationRegistry[snipped_id]) { + new website.snippet.animationRegistry[snipped_id]($target); + } + self.create_overlay($target); - var snippet = new website.snippet.editorRegistry[snipped_id](self, $target); - snippet.build_snippet($target); + $target.data("snippet-editor").build_snippet($target); + + } else { + $target = $(".oe_drop_zone.oe_hover").first().data('target'); + + self.create_overlay($target); + if (website.snippet.editorRegistry[snipped_id]) { + var snippet = new website.snippet.editorRegistry[snipped_id](self, $target); + snippet.build_snippet($target); + } } + $('.oe_drop_zone').remove(); + + setTimeout(function () {self.make_active($target);},0); }, }); }, @@ -270,45 +391,22 @@ return $(this).data('snippet-id') === id; }).first(); }, - // WIP - make_draggable_instance: function($instance){ - var self = this; - var $snippet = get_snippet_from_id($instance.data('snippet-id')); - - $instance.draggable({ - helper: 'clone', - zIndex: '1000', - appendTo: 'body', - start: function(){ - var action = $snippet.data('action'); - if(action === 'insert'){ - - self.activate_insertion_zones({ - siblings: $snippet.data('selector-siblings'), - child: $snippet.data('selector-childs'), - vertical_childs: $snippet.data('selector-vertical-childs') - }); - - } - } - }); - }, // Create element insertion drop zones. two css selectors can be provided - // selector.childs -> will insert drop zones as direct child of the selected elements + // selector.children -> will insert drop zones as direct child of the selected elements // in case the selected elements have children themselves, dropzones will be interleaved // with them. // selector.siblings -> will insert drop zones after and before selected elements activate_insertion_zones: function(selector){ var self = this; - var child_selector = selector.childs ? this.parent_of_editable_box + (selector.childs).split(",").join(this.parent_of_editable_box) : false; - var sibling_selector = selector.siblings ? this.parent_of_editable_box + (selector.siblings).split(",").join(this.parent_of_editable_box) : false; - var vertical_child_selector = selector.vertical_childs ? this.parent_of_editable_box + (selector.vertical_childs).split(",").join(this.parent_of_editable_box) : false; + var child_selector = selector.children; + var sibling_selector = selector.siblings; + var vertical_child_selector = selector.vertical_children; var zone_template = ""; if(child_selector){ - $(child_selector).each(function (){ + self.dom_filter(child_selector).each(function (){ var $zone = $(this); $zone.find('> *:not(.oe_drop_zone):visible').after(zone_template); $zone.prepend(zone_template); @@ -316,7 +414,7 @@ } if(vertical_child_selector){ - $(vertical_child_selector).each(function (){ + self.dom_filter(vertical_child_selector).each(function (){ var $zone = $(this); var $template = $(zone_template).addClass("oe_vertical").css('height', $zone.outerHeight()+'px'); $zone.find('> *:not(.oe_drop_zone):visible').after($template); @@ -325,7 +423,7 @@ } if(sibling_selector){ - $(sibling_selector).each(function (){ + self.dom_filter(sibling_selector).each(function (){ var $zone = $(this); if($zone.prev('.oe_drop_zone:visible').length === 0){ $zone.before(zone_template); @@ -374,11 +472,8 @@ // generate drop zones covering the elements selected by the selector // we generate overlay drop zones only to get an idea of where the snippet are, the drop activate_overlay_zones: function(selector){ - selector = selector || '[data-snippet-id]'; - if (typeof selector === 'string') - selector = this.parent_of_editable_box + selector.split(",").join(this.parent_of_editable_box); - - var $targets = $(selector); + var $targets = this.dom_filter(selector || '[data-snippet-id]'); + var self = this; function is_visible($el){ return $el.css('display') != 'none' @@ -404,23 +499,16 @@ $target.data('overlay',$zone); $target.on("DOMNodeInserted DOMNodeRemoved DOMSubtreeModified", function () { - website.snippet.cover_target($zone, $target); + self.cover_target($zone, $target); + }); + $('body').on("resize", function () { + self.cover_target($zone, $target); }); } - website.snippet.cover_target($target.data('overlay'), $target); + self.cover_target($target.data('overlay'), $target); }); return $targets; - }, - - toggle: function(){ - this.active = !this.active; - if(this.active){ - this.$el.removeClass('hide'); - this.activate_overlay_zones(); - } else { - this.$el.addClass('hide'); - } - }, + } }); @@ -431,6 +519,13 @@ }, init: function (dom) { this.$el = this.$target = $(dom); + this.start(); + }, + /* + * start + * This method is called after init + */ + start: function () { }, /* onFocusEdit * if they are an editor for this snippet-id @@ -453,17 +548,6 @@ }, }); - $(document).ready(function () { - hack_to_add_snippet_id(); - $("[data-snippet-id]").each(function() { - var $snipped_id = $(this); - if (typeof $snipped_id.data("snippet-view") === 'undefined' && - website.snippet.animationRegistry[$snipped_id.data("snippet-id")]) { - $snipped_id.data("snippet-view", new website.snippet.animationRegistry[$snipped_id.data("snippet-id")]($snipped_id)); - } - }); - }); - website.snippet.editorRegistry = {}; website.snippet.Editor = openerp.Class.extend({ @@ -471,6 +555,7 @@ this.parent = parent; this.$target = $(dom); this.$overlay = this.$target.data('overlay'); + this.snippet_id = this.$target.data("snippet-id"); this._readXMLData(); this.start(); }, @@ -493,56 +578,83 @@ * Insert into the view when the thumbnail is drag and droped into a drop zone */ _readXMLData: function() { - if (this.template) { - this.$el = $(openerp.qweb.render(this.template, {widget: this}).trim()); - this.$editor = this.$el.find(".oe_snippet_options"); - this.$thumbnail = this.$el.find(".oe_snippet_thumbnail"); - this.$body = this.$el.find(".oe_snippet_body"); + this.$el = $(openerp.qweb.render("website.snippets." + this.snippet_id, {widget: this}).trim()); + this.$editor = this.$el.find(".oe_snippet_options"); + this.$thumbnail = this.$el.find(".oe_snippet_thumbnail"); + this.$body = this.$el.find(".oe_snippet_body"); - var $options = this.$overlay.find(".oe_overlay_options"); - this.$editor.prependTo($options.find(".oe_options ul")); - $options.find(".oe_label").text(this.$el.find('.oe_snippet_thumbnail.oe_label, .oe_snippet_thumbnail .oe_label').text()); - } + var $options = this.$overlay.find(".oe_overlay_options"); + this.$editor.prependTo($options.find(".oe_options ul")); + $options.find(".oe_label").text(this.$el.find('.oe_snippet_label').text()); }, // activate drag and drop for the snippets in the snippet toolbar _drag_and_drop: function(){ var self = this; + var dropped = false; + var $move = this.$overlay.find(".oe_move"); this.$overlay.draggable({ + greedy: true, appendTo: 'body', cursor: "move", - cursorAt: { - top: self.$target.outerHeight()/2, - left: self.$target.outerWidth()/2 }, - distance: 20, handle: ".js_box_move", + cursorAt: {left: $move.outerWidth()/2, top: $move.find(".oe_move").outerHeight()/2}, + helper: function() { + var $clone = $(this).clone().css({width: "0px", height: "0px"}); + $clone.find(".oe_label, .oe_options").remove(); + return $clone.appendTo("body").show(); + }, start: function(){ + self.parent.hide(); self.parent.editor_busy = true; - self.$target.css("display", "none"); + self.$target.after(""); + self.$target.detach(); + self.$overlay.hide(); self.parent.activate_insertion_zones({ siblings: self.$el ? self.$el.data('selector-siblings') : false, - childs: self.$el ? self.$el.data('selector-childs') : false, - vertical_childs: self.$el ? self.$el.data('selector-vertical-childs') : false, + children: self.$el ? self.$el.data('selector-children') : false, + vertical_children: self.$el ? self.$el.data('selector-vertical-children') : false, }); $("body").addClass('move-important'); $('.oe_drop_zone').droppable({ - hoverClass: "oe_hover", - drop: function(){ - $(this).after(self.$target); + over: function(){ + $(".oe_drop_zone.hide").removeClass("hide"); + $(this).addClass("hide").first().after(self.$target); + dropped = true; + }, + out: function(){ + $(this).removeClass("hide"); + self.$target.detach(); + dropped = false; }, }); }, stop: function(){ + if (!dropped) { + $(".oe_drop_clone").after(self.$target); + } + self.$overlay.show(); $("body").removeClass('move-important'); $('.oe_drop_zone').droppable('destroy').remove(); - self.$target.css("display", ""); + $(".oe_drop_clone").remove(); self.parent.editor_busy = false; setTimeout(function () {self.parent.create_overlay(self.$target);},0); }, }); }, + _clone: function () { + var self = this; + this.$overlay.on('click', '.js_box_clone', function () { + var $clone = self.$target.clone(false); + self.$target.after($clone); + setTimeout(function () { + self.parent.create_overlay($clone); + self.parent.make_active($clone); + },0); + }); + }, /* * start @@ -557,6 +669,7 @@ return false; }); this._drag_and_drop(); + this._clone(); }, /* @@ -565,8 +678,6 @@ * (after the insertion of this.$body, if this.$body exists) */ build_snippet: function ($target) { - var self = this; - setTimeout(function () {self.parent.make_active(self.$target);},0); }, /* onFocus @@ -601,7 +712,6 @@ website.snippet.editorRegistry.resize = website.snippet.Editor.extend({ - template : "website.snippets.resize", start: function () { var self = this; this._super(); @@ -683,7 +793,7 @@ if (change) { self.on_resize(compass, beginClass, current); - website.snippet.cover_target(self.$overlay, self.$target); + self.parent.cover_target(self.$overlay, self.$target); } }; $('body').mousemove(body_mousemove); @@ -718,7 +828,6 @@ }); website.snippet.editorRegistry.colmd = website.snippet.editorRegistry.resize.extend({ - template : "website.snippets.colmd", getSize: function () { this.grid = this._super(); var width = this.$target.parents(".row:first").first().outerWidth(); @@ -727,7 +836,7 @@ this.grid.e = [_.map(grid, function (v) {return 'col-md-'+v;}), _.map(grid, function (v) {return width/12*v;})]; var grid = [-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11]; - this.grid.w = [_.map(grid, function (v) {return 'col-lg-offset-'+v;}), _.map(grid, function (v) {return width/12*v;}), 12]; + this.grid.w = [_.map(grid, function (v) {return 'col-md-offset-'+v;}), _.map(grid, function (v) {return width/12*v;}), 12]; return this.grid; }, @@ -737,23 +846,20 @@ // don't change the rigth 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-lg-offset-([0-9-]+)|$/)[1] || 0); - var offset = Number(this.grid.w[0][current].match(/col-lg-offset-([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); - this.$target.attr("class",this.$target.attr("class").replace(/\s*(col-lg-offset-|col-md-)([0-9-]+)/g, '')); + this.$target.attr("class",this.$target.attr("class").replace(/\s*(col-lg-offset-|col-md-offset-|col-md-)([0-9-]+)/g, '')); var colSize = beginCol - (offset - beginOffset); this.$target.addClass('col-md-' + (colSize > 12 ? 12 : colSize)); if (offset > 0) { - this.$target.addClass('col-lg-offset-' + offset); + this.$target.addClass('col-md-offset-' + offset); } }, }); - website.snippet.selector.push([ _.map([1,2,3,4,5,6,7,8,9,10,11,12], function (v) {return '.row > .col-md-'+v;}).join(","), 'colmd']); - website.snippet.editorRegistry.carousel = website.snippet.editorRegistry.resize.extend({ - template : "website.snippets.carousel", build_snippet: function($target) { var id = "myCarousel" + $("body .carousel").size(); $target.attr("id", id); @@ -761,95 +867,106 @@ }, start : function () { this._super(); - var self = this; - this.$editor.find(".js_add").on('click', this.on_add); - this.$editor.find(".js_remove").on('click', this.on_remove); + this.$editor.find(".js_add").on('click', this, this.on_add); + this.$editor.find(".js_remove").on('click', this, this.on_remove); - var bg = this.$target.find('.carousel-inner .item.active').css('background-image').replace(/url\((.*)\)/g, '\$1'); - this.$editor.find('select[name="carousel-background"] option[value="'+bg+'"], select[name="carousel-background"] option[value="'+bg.replace(window.location.protocol+'//'+window.location.host, '')+'"]') - .prop('selected', true); - - this.$editor.find('select[name="carousel-background"]').on('change', function () { - self.$target.find('.carousel-inner .item.active').css('background-image', 'url(' + $(this).val() + ')'); - }); - - var style = false; - if (this.$target.find('.carousel-inner .item.active .container .content_image.col-lg-offset-1')) - style = 'image_right'; - if (this.$target.find('.carousel-inner .item.active .container .content_image')) - style = 'image_left'; - this.$editor.find('select[name="carousel-style"] option[value="'+style+'"]').prop('selected', true); - - this.$editor.find('select[name="carousel-style"]').on('change', this.on_bg_change); + this.change_background(); + this.change_style(); + this.change_size(); }, on_add: function (e) { e.preventDefault(); - var $inner = this.$target.find('.carousel-inner'); + var $inner = e.data.$target.find('.carousel-inner'); var cycle = $inner.find('.item').size(); - $inner.append(this.$('> .item')); - this.$target.carousel(cycle); + $inner.find('.item.active').clone().removeClass('active').appendTo($inner); + e.data.$target.carousel(cycle); }, on_remove: function (e) { e.preventDefault(); - var $inner = this.$target.find('.carousel-inner'); + var $inner = e.data.$target.find('.carousel-inner'); if ($inner.find('.item').size() > 1) { $inner .find('.item.active').remove().end() .find('.item:first').addClass('active'); - this.$target.carousel(0); + e.data.$target.carousel(0); } }, - on_bg_change: function (e) { - var $container = this.$target.find('.carousel-inner .item.active .container'); - var img_url = $('.content_image img', $container).attr("src"); - if (!img_url) { - img_url = this.img_url || "/website/static/src/img/china.jpg"; - } else { - this.img_url = img_url; + change_background: function () { + var self = this; + var bg = this.$target.find('.carousel-inner .item.active').css('background-image').replace(/url\((.*)\)/g, '$1'); + var selected = this.$editor.find('select[name="carousel-background"] option[value="'+bg+'"], select[name="carousel-background"] option[value="'+bg.replace(window.location.protocol+'//'+window.location.host, '')+'"]') + .prop('selected', true).length; + if (!selected) { + this.$editor.find('.carousel-background input').val(bg); } - $('.content_image', $container).remove(); - switch ($(e.currentTarget).val()) { - case 'no_image': - $('.content', $container).attr("class", "content"); - break; - case 'image_left': - $('.content', $container).attr("class", "content col-md-6") - .before('— or —
+— or —
— or —
-