diff options
| author | Olivier Dony <odo@openerp.com> | 2014-04-11 11:40:18 +0200 |
|---|---|---|
| committer | Olivier Dony <odo@openerp.com> | 2014-04-11 11:40:18 +0200 |
| commit | 410c82bd6a32cc5de11175b3c8f3ec2acbb5018b (patch) | |
| tree | 09c41871bd55ca731205b02f4e7412b292d20f31 /addons/website/static/src | |
| parent | 1b66087ed833e38893892a121900c451180309da (diff) | |
| parent | 0152bea512d868cbb0d9adf33cdbf98bc2030a0f (diff) | |
[MERGE] Sync with trunk
bzr revid: odo@openerp.com-20140411094018-zqw3ac1br991qsnp
Diffstat (limited to 'addons/website/static/src')
31 files changed, 2810 insertions, 1329 deletions
diff --git a/addons/website/static/src/css/bootswatch/amelia.fix.css b/addons/website/static/src/css/bootswatch/amelia.fix.css new file mode 100644 index 00000000000..2f16ea5465c --- /dev/null +++ b/addons/website/static/src/css/bootswatch/amelia.fix.css @@ -0,0 +1,27 @@ +#website-top-navbar a.btn-link { + color: #ad1d28; +} +.oe_product section { + background: rgba(16, 138, 147, 0.75) !important; +} +.oe_product section .text-info { + color: #DFD6F5; +} +ul.wizard li { + background: #debb27 !important; +} +ul.wizard .chevron:before { + border-left: 10px solid #debb27 !important; +} +ul.wizard .text-primary { + color: #ad1d28 !important; +} +ul.wizard .text-success { + color: #ffffff !important; +} +.oe_structure.oe_empty:empty:before, [data-oe-type=html]:empty:before { + color: #79D5DB !important; +} +.input-group-addon .fa { + color: #444; +} diff --git a/addons/website/static/src/css/bootswatch/cosmo.fix.css b/addons/website/static/src/css/bootswatch/cosmo.fix.css new file mode 100644 index 00000000000..907aa7bf757 --- /dev/null +++ b/addons/website/static/src/css/bootswatch/cosmo.fix.css @@ -0,0 +1,7 @@ +#website-top-navbar button.btn-primary { + color: #fff; + border: 2px solid #0061c2; +} +#website-top-navbar a.btn-link { + color: #fff; +}
\ No newline at end of file diff --git a/addons/website/static/src/css/bootswatch/cyborg.fix.css b/addons/website/static/src/css/bootswatch/cyborg.fix.css new file mode 100644 index 00000000000..e93b2aa8ded --- /dev/null +++ b/addons/website/static/src/css/bootswatch/cyborg.fix.css @@ -0,0 +1,30 @@ +.oe_product section { + background: rgba(40, 40, 40, 0.85) !important; +} +.oe_product section .text-info { + color: #BA66E4; +} +ul.wizard li { + background: #222 !important; +} +ul.wizard .chevron:before { + border-left: 10px solid #222 !important; +} +.popover, .modal-content { + border: 1px solid rgba(200,200,200,0.5); +} +.close { + color: #fff; +} +.popover.bottom .arrow:after { + border-bottom-color: #333; +} +.popover.top .arrow:after { + border-top-color: #333; +} +.popover.left .arrow:after { + border-left-color: #333; +} +.popover.right .arrow:after { + border-right-color: #333; +}
\ No newline at end of file diff --git a/addons/website/static/src/css/bootswatch/flatly.fix.css b/addons/website/static/src/css/bootswatch/flatly.fix.css new file mode 100644 index 00000000000..c2d9500f5da --- /dev/null +++ b/addons/website/static/src/css/bootswatch/flatly.fix.css @@ -0,0 +1,3 @@ +#website-top-navbar a.btn-link { + color: #2c3e50; +}
\ No newline at end of file diff --git a/addons/website/static/src/css/bootswatch/journal.fix.css b/addons/website/static/src/css/bootswatch/journal.fix.css new file mode 100644 index 00000000000..e9f41a33e8f --- /dev/null +++ b/addons/website/static/src/css/bootswatch/journal.fix.css @@ -0,0 +1,6 @@ +#website-top-navbar button.btn-primary { + border: 2px solid #e4332e; +} +#website-top-navbar a.btn-link { + color: #fff; +}
\ No newline at end of file diff --git a/addons/website/static/src/css/bootswatch/readable.fix.css b/addons/website/static/src/css/bootswatch/readable.fix.css new file mode 100644 index 00000000000..8e22d318615 --- /dev/null +++ b/addons/website/static/src/css/bootswatch/readable.fix.css @@ -0,0 +1,3 @@ +.oe_product section .text-info { + color: #2A9CBE; +}
\ No newline at end of file diff --git a/addons/website/static/src/css/bootswatch/simplex.fix.css b/addons/website/static/src/css/bootswatch/simplex.fix.css new file mode 100644 index 00000000000..56dbb1fdd5f --- /dev/null +++ b/addons/website/static/src/css/bootswatch/simplex.fix.css @@ -0,0 +1,3 @@ +#website-top-navbar a.btn-link { + color: #fff; +}
\ No newline at end of file diff --git a/addons/website/static/src/css/bootswatch/slate.fix.css b/addons/website/static/src/css/bootswatch/slate.fix.css new file mode 100644 index 00000000000..c2e1a999b03 --- /dev/null +++ b/addons/website/static/src/css/bootswatch/slate.fix.css @@ -0,0 +1,21 @@ +.oe_product section { + background: rgba(40, 40, 45, 0.80) !important; +} +ul.wizard li { + background-image: -webkit-linear-gradient(#8a9196,#7a8288 60%,#788084) !important; + background-image: linear-gradient(#8a9196,#7a8288 60%,#788084 ) !important; + color: #ccc; +} +ul.wizard .chevron:before { + border-left: 10px solid #7C8386 !important; +} +ul.wizard li.text-primary { + color: #fff; +} +.close { + color: #fff; +} +.popover { + -webkit-box-shadow: 0px 0px 20px rgba(0,0,0,0.5); + box-shadow: 0px 0px 20px rgba(0,0,0,0.5); +} diff --git a/addons/website/static/src/css/bootswatch/spacelab.fix.css b/addons/website/static/src/css/bootswatch/spacelab.fix.css new file mode 100644 index 00000000000..6d01e9a2485 --- /dev/null +++ b/addons/website/static/src/css/bootswatch/spacelab.fix.css @@ -0,0 +1,8 @@ +.carousel .carousel-caption.content, +.carousel .carousel-caption.content h1, +.carousel .carousel-caption.content h2, +.carousel .carousel-caption.content h3, +.carousel .carousel-caption.content h4, +.carousel .carousel-caption.content h5 { + color: #ccc; +}
\ No newline at end of file diff --git a/addons/website/static/src/css/bootswatch/yeti.fix.css b/addons/website/static/src/css/bootswatch/yeti.fix.css new file mode 100644 index 00000000000..3fd285c826e --- /dev/null +++ b/addons/website/static/src/css/bootswatch/yeti.fix.css @@ -0,0 +1,6 @@ +#website-top-navbar a.btn-link { + color: #fff; +} +.popover .close { + color: #fff; +}
\ 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 4fb4496e692..eb4c5b2851c 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 ---- {{{ */ @@ -93,6 +94,10 @@ display: none; } +.cke_editable .css_editable_mode_display { + display: block !important; +} + .oe_structure.oe_empty:empty, [data-oe-type=html]:empty, .oe_structure.oe_empty > .oe_drop_zone.oe_insert:only-child, [data-oe-type=html] > .oe_drop_zone.oe_insert:only-child { background-image: url("/website/static/src/img/edit_here.png") !important; } @@ -179,7 +184,7 @@ ul.oe_menu_editor .disclose { cursor: text !important; } -.modal-dialog.select-image { +.modal-dialog.select-media { width: 80%; } @@ -211,7 +216,7 @@ ul.oe_menu_editor .disclose { .modal .font-icons-icons { font-size: 2em; - max-height: 6em; + max-height: 9em; overflow: auto; } .modal .font-icons-icons .font-icons-icon { @@ -250,6 +255,30 @@ 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; +} +.existing-attachments .existing-attachment-cell.media_selected > i, .existing-attachments .existing-attachment-cell.media_selected > img { + border-width: 5px; + border-color: #00f8f8; +} .cke_widget_wrapper { position: static !important; @@ -275,17 +304,34 @@ ul.oe_menu_editor .disclose { background-color: #ffd9dd !important; } -.hover-edition-button { +.hover-edition { + display: inline-block; position: absolute; top: 0; left: 0; - z-index: 1000; + z-index: 1001; +} + +.preview-container { + text-align: center; + line-height: 100px; + height: 100px; +} +.preview-container > * { + max-height: 100px; + line-height: 100px; + margin: 0 auto; + display: inline-block; } .cke_editable .fa { cursor: pointer; } +.img-responsive { + text-align: center; +} + /* ---- MOBILE PREVIEW ---- {{{ */ .oe_mobile_preview.modal .modal-content { height: 660px; diff --git a/addons/website/static/src/css/editor.sass b/addons/website/static/src/css/editor.sass index 2e6513f7c1d..fb80fa47277 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)" // }}} @@ -76,6 +77,9 @@ .cke_editable .css_editable_mode_hidden display: none +.cke_editable .css_editable_mode_display + display: block !important + .oe_structure.oe_empty:empty, [data-oe-type=html]:empty, .oe_structure.oe_empty > .oe_drop_zone.oe_insert:only-child, [data-oe-type=html] > .oe_drop_zone.oe_insert:only-child background-image: url('/website/static/src/img/edit_here.png') !important @@ -149,7 +153,7 @@ ul.oe_menu_editor +user-select(auto) cursor: text !important -.modal-dialog.select-image +.modal-dialog.select-media width: 80% .modal .existing-attachments @@ -178,7 +182,7 @@ ul.oe_menu_editor .modal .font-icons-icons font-size: 2em - max-height: 6em + max-height: 9em overflow: auto .font-icons-icon @@ -210,9 +214,31 @@ 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) + &.media_selected + > i, > img + border-width: 5px + border-color: rgb(0, 248, 248) // wrapper positioned relatively for drag&drop widget which is disabled below. // Breaks completely horribly crazy products listing page, so take it out. @@ -240,17 +266,30 @@ ul.oe_menu_editor outline: 1px solid red !important background-color: #ffd9dd !important -.hover-edition-button +.hover-edition + display: inline-block position: absolute top: 0 left: 0 - // This z-index is due to .navbar of bootstrap - z-index: 1000 + // This z-index is due to .navbar of bootstrap & jQuery-transfo + z-index: 1001 +.preview-container + text-align: center + line-height: 100px + height: 100px + > * + max-height: 100px + line-height: 100px + margin: 0 auto + display: inline-block // fontawesome .cke_editable .fa cursor: pointer +.img-responsive + text-align: center + // }}} /* ---- MOBILE PREVIEW ---- {{{ */ diff --git a/addons/website/static/src/css/snippets.css b/addons/website/static/src/css/snippets.css index fe9c18c0081..cbfca8be131 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,8 +198,10 @@ .oe_overlay { display: none; + height: 0; position: absolute; background: transparent; + z-index: 1001; -webkit-border-radius: 3px; -moz-border-radius: 3px; -ms-border-radius: 3px; @@ -214,20 +214,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.3); } .oe_overlay .oe_handle { display: block !important; - pointer-events: auto; position: absolute; top: 50%; left: 50%; @@ -238,7 +230,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,56 +273,68 @@ -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.w { - top: 2px; - height: 100%; left: -6px; cursor: e-resize; } +.oe_overlay .oe_handle.s, .oe_overlay .oe_handle.n { + left: 2px; + width: 100%; +} .oe_overlay .oe_handle.s:before, .oe_overlay .oe_handle.n:before { - z-index: 0; 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; - left: 2px; - width: 100%; - bottom: -6px; cursor: n-resize; } .oe_overlay .oe_handle.n { - left: 2px; - width: 100%; - top: -6px; 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; + margin-left: -50px; } .oe_overlay .oe_handle.size .oe_handle_button { - z-index: 1; - content: "Resize"; - width: 64px; + position: relative; + z-index: 3; text-align: center; - margin-left: -32px; + margin-left: -52px; margin-top: -10px; - cursor: row-resize; + left: 0px; } .oe_overlay .oe_handle.size .oe_handle_button:hover { background: rgba(30, 30, 30, 0.8); @@ -328,25 +343,40 @@ -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.size .size { + position: absolute; + width: 64px; + cursor: row-resize; + top: 9px; + margin-left: 52px; + padding: 0 5px; +} +.oe_overlay .oe_handle.size .auto_size { + position: absolute; + width: 100px; + top: 9px; + cursor: pointer; +} +.oe_overlay .oe_handle.readonly { + cursor: auto !important; +} +.oe_overlay .oe_handle.readonly:before, .oe_overlay .oe_handle.readonly.size { + display: none !important; } .oe_overlay .icon.btn { display: inline-block; } .oe_overlay .oe_overlay_options { position: absolute; - width: 100%; + left: 50% !important; text-align: center; - top: -11px; + top: -20px; 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 { @@ -505,7 +535,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; @@ -608,8 +637,3 @@ height: 16px; display: inline-block; } - -[data-snippet-id] { - min-height: 10px; - min-width: 10px; -} diff --git a/addons/website/static/src/css/snippets.sass b/addons/website/static/src/css/snippets.sass index a06bba01dba..40467897e26 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 @@ -150,22 +148,18 @@ .oe_overlay display: none + height: 0 position: absolute background: transparent + z-index: 1001 //@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.3) .oe_handle display: block !important - pointer-events: auto position: absolute top: 50% left: 50% @@ -173,7 +167,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% @@ -196,72 +198,88 @@ 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 - top: 2px - height: 100% left: -6px cursor: e-resize &.s, &.n + left: 2px + width: 100% &:before - z-index: 0 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 &.n - left: 2px - width: 100% - top: -6px cursor: s-resize + > div + top: 5px &.size + z-index: 3 top: auto - left: 2px - width: 100% + left: 50% bottom: -6px + margin-left: -50px .oe_handle_button - z-index: 1 - content: "Resize" - width: 64px + position: relative + z-index: 3 text-align: center - margin-left: -32px + margin-left: -52px margin-top: -10px - cursor: row-resize + left: 0px &: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 + .size + position: absolute + width: 64px + cursor: row-resize + top: 9px + margin-left: 52px + padding: 0 5px + .auto_size + position: absolute + width: 100px + top: 9px + cursor: pointer + &.readonly + cursor: auto !important + &:before, &.size + display: none !important .icon.btn display: inline-block .oe_overlay_options position: absolute - width: 100% + left: 50% !important text-align: center - top: -11px + top: -20px z-index: 1002 + > .btn-group + left: -50% .btn, a - pointer-events: auto cursor: pointer .dropdown display: inline-block @@ -392,7 +410,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 @@ -483,10 +500,6 @@ height: 16px display: inline-block -[data-snippet-id] - min-height: 10px - min-width: 10px - diff --git a/addons/website/static/src/css/website.css b/addons/website/static/src/css/website.css index e47b7a41baa..9339b5e03b9 100644 --- a/addons/website/static/src/css/website.css +++ b/addons/website/static/src/css/website.css @@ -1,3 +1,4 @@ +@charset "utf-8"; /* THIS CSS FILE IS FOR WEBSITE THEMING CUSTOMIZATION ONLY * * css for editor buttons, openerp widget included in the website and other @@ -102,6 +103,10 @@ header a.navbar-brand img { max-height: 50px; } +#wrapwrap p:empty:after { + content: "\2060"; +} + /* ----- Snippets Styles ----- */ .readable { font-size: 120%; @@ -116,9 +121,10 @@ header a.navbar-brand img { } /* ----- BOOTSTRAP FIX ----- */ -.container .container { +.container .container, .readable .container { padding-left: 0; padding-right: 0; + width: auto; } /* ----- BOOTSTRAP HACK FOR HEADER NAV BAR ----- */ @@ -216,7 +222,6 @@ footer { /* -- Hack for removing double scrollbar from mobile preview -- */ div#mobile-preview.modal { overflow: hidden; - overflow-y: hidden; } ul.nav-stacked > li > a { @@ -236,7 +241,7 @@ ul.nav-stacked > li > a { display: none; } -[data-publish='off'] { +[data-publish='off'] > *:not(.css_options) { filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50); opacity: 0.5; } @@ -280,12 +285,12 @@ ul.nav-stacked > li > a { background-size: 100%; } -[data-snippet-id], .colmd, .hr, .blockquote { +.carousel, .parallax, .blockquote { overflow: hidden; } @media (max-width: 400px) { - [data-snippet-id]:not([data-snippet-id="carousel"]) { + section, .parallax, .row, .hr, .blockquote { height: auto !important; } } @@ -368,42 +373,51 @@ ul.nav-stacked > li > a { position: absolute; margin-top: -8px; } -.carousel .carousel-control.left span { - left: 10px; +.carousel .carousel-control.left { + left: -10px; +} +.carousel .carousel-control.left * { + position: absolute; + top: 50%; + z-index: 5; + right: 50%; +} +.carousel .carousel-control.right { + right: -10px; } -.carousel .carousel-control.right span { - right: 10px; +.carousel .carousel-control.right * { + position: absolute; + top: 50%; + z-index: 5; + left: 50%; } .quotecarousel { padding-bottom: 16px; } +.hr { + padding: 4px 0; +} + /* Parallax Theme */ -div.carousel[data-snippet-id="slider"] .carousel-indicators li { +div.carousel .carousel-indicators li { border: 1px solid grey; } -div.carousel[data-snippet-id="slider"] .carousel-indicators .active { +div.carousel .carousel-indicators .active { background-color: grey; } .parallax { - position: relative; background-size: cover; +} +.parallax > div { + position: relative; 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; @@ -413,9 +427,6 @@ div.carousel[data-snippet-id="slider"] .carousel-indicators .active { .oe_dark { background: #eff8f8; background: rgba(200, 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; } .oe_black { @@ -529,3 +540,33 @@ span[data-oe-type="monetary"] { width: 400px; margin: 40px auto; } + +div.media_iframe_video { + height: 0; + margin: 0 auto; + text-align: center; + position: relative; + overflow: hidden; + padding-bottom: 66.5%; +} +div.media_iframe_video iframe { + width: 100%; + height: 100%; +} +div.media_iframe_video div { + position: absolute; + width: 100%; + height: 100%; + display: none; +} + +/* Mobile view */ +@media (max-width: 768px) { + img:not(.cke_iframe), .media_iframe_video, span.fa, i.fa { + -webkit-transform: none !important; + -moz-transform: none !important; + -ms-transform: none !important; + -o-transform: none !important; + transform: none !important; + } +} diff --git a/addons/website/static/src/css/website.sass b/addons/website/static/src/css/website.sass index 3cdfe37a896..e8a7d870623 100644 --- a/addons/website/static/src/css/website.sass +++ b/addons/website/static/src/css/website.sass @@ -68,6 +68,12 @@ header img max-height: 50px +// add {WORD JOINER} (equivalent to deprecated ZERO WIDTH NO-BREAK SPACE) at +// the beginning of paragraphs so they don't "disappear" after saving when +// used solely for spacing. +#wrapwrap p:empty:after + content: '\2060' + /* ----- Snippets Styles ----- */ .readable @@ -78,17 +84,16 @@ header /* ----- EDITOR ----- */ - .css_non_editable_mode_hidden display: none - /* ----- BOOTSTRAP FIX ----- */ -.container +.container, .readable .container padding-left: 0 padding-right: 0 + width: auto /* ----- BOOTSTRAP HACK FOR HEADER NAV BAR ----- */ @@ -179,7 +184,6 @@ footer /* -- Hack for removing double scrollbar from mobile preview -- */ div#mobile-preview.modal overflow: hidden - overflow-y: hidden ul.nav-stacked > li > a padding: 2px 15px @@ -195,7 +199,7 @@ ul.nav-stacked > li > a .btn-success, .css_unpublish display: none -[data-publish='off'] +[data-publish='off']>*:not(.css_options) +opacity(0.5) /* ---- END of PUBLISH ---- */ @@ -230,11 +234,11 @@ ul.nav-stacked > li > a .oe_img_bg background-size: 100% -[data-snippet-id],.colmd,.hr,.blockquote +.carousel, .parallax, .blockquote overflow: hidden @media (max-width: 400px) - [data-snippet-id]:not([data-snippet-id="carousel"]) + section, .parallax, .row, .hr, .blockquote height: auto !important .carousel-inner @@ -302,17 +306,30 @@ ul.nav-stacked > li > a top: 50% position: absolute margin-top: -8px - &.left span - left: 10px - &.right span - right: 10px + &.left + left: -10px + * + position: absolute + top: 50% + z-index: 5 + right: 50% + &.right + right: -10px + * + position: absolute + top: 50% + z-index: 5 + left: 50% .quotecarousel padding-bottom: 16px +.hr + padding: 4px 0 + /* Parallax Theme */ -div.carousel[data-snippet-id="slider"] +div.carousel .carousel-indicators li border: 1px solid grey @@ -320,28 +337,22 @@ div.carousel[data-snippet-id="slider"] background-color: grey .parallax - position: relative background-size: cover - display: table - width: 100% - min-height: 100px - &.oe_small - min-height: 200px - &.oe_medium - min-height: 300px - &.oe_big - min-height: 450px > 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 */ .oe_dark background: #eff8f8 background: rgba(200, 200, 200, 0.14) - +box-shadow(0px 5px 9px -7px rgba(0, 0, 255, 0.5) inset, 0px -3px 9px -7px rgba(0, 0, 255, 0.5) inset) .oe_black background-color: rgba(0, 0, 0, 0.9) @@ -428,3 +439,34 @@ span[data-oe-type="monetary"] .oe_website_login_container width: 400px margin: 40px auto + +.oe_website_overflow_ellipsis + white-space:nowrap + overflow:hidden + text-overflow:ellipsis + +div.media_iframe_video + height: 0 + margin: 0 auto + text-align: center + position: relative + overflow: hidden + padding-bottom: 66.5% + iframe + width: 100% + height: 100% + div + position: absolute + width: 100% + height: 100% + display: none + +/* Mobile view */ + +@media (max-width: 768px) + img:not(.cke_iframe), .media_iframe_video, span.fa, i.fa + -webkit-transform: none !important + -moz-transform: none !important + -ms-transform: none !important + -o-transform: none !important + transform: none !important diff --git a/addons/website/static/src/js/jQuery.transfo.js b/addons/website/static/src/js/jQuery.transfo.js new file mode 100644 index 00000000000..725e5a02216 --- /dev/null +++ b/addons/website/static/src/js/jQuery.transfo.js @@ -0,0 +1,433 @@ +/* +Copyright (c) 2014 Christophe Matthieu, + +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. +*/ + +(function($){ + 'use strict'; + var rad = Math.PI/180; + + // public methods + var methods = { + init : function(settings) { + return this.each(function() { + var $this = $(this), transfo = $this.data('transfo'); + if (!transfo) { + _init($this, settings); + } else { + _overwriteOptions($this, transfo, settings); + _targetCss($this, transfo); + } + }); + }, + + destroy : function() { + return this.each(function() { + var $this = $(this); + if ($this.data('transfo')) { + _destroy($this); + } + }); + }, + + reset : function() { + return this.each(function() { + var $this = $(this); + if ($this.data('transfo')) { + _reset($this); + } + }); + }, + + toggle : function() { + return this.each(function() { + var $this = $(this); + var transfo = $this.data('transfo'); + if (transfo) { + transfo.settings.hide = !transfo.settings.hide; + _showHide($this, transfo); + } + }); + }, + + hide : function() { + return this.each(function() { + var $this = $(this); + var transfo = $this.data('transfo'); + if (transfo) { + transfo.settings.hide = true; + _showHide($this, transfo); + } + }); + }, + + show : function() { + return this.each(function() { + var $this = $(this); + var transfo = $this.data('transfo'); + if (transfo) { + transfo.settings.hide = false; + _showHide($this, transfo); + } + }); + }, + + settings : function() { + if(this.length > 1) { + this.map(function () { + var $this = $(this); + return $this.data('transfo') && $this.data('transfo').settings; + }); + } + return this.data('transfo') && $this.data('transfo').settings; + }, + center : function() { + if(this.length > 1) { + this.map(function () { + var $this = $(this); + return $this.data('transfo') && $this.data('transfo').$center.offset(); + }); + } + return this.data('transfo') && this.data('transfo').$center.offset(); + } + }; + + $.fn.transfo = function( method ) { + if ( methods[method] ) { + return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 )); + } else if ( typeof method === 'object' || ! method ) { + return methods.init.apply( this, arguments ); + } else { + $.error( 'Method ' + method + ' does not exist on jQuery.transfo' ); + } + return false; + }; + + function _init ($this, settings) { + var transfo = {}; + $this.data('transfo', transfo); + transfo.settings = settings; + + // generate all the controls markup + var css = "box-sizing: border-box; position: absolute; background-color: #fff; border: 1px solid #ccc; width: 8px; height: 8px; margin-left: -4px; margin-top: -4px;"; + transfo.$markup = $('' + + '<div class="transfo-controls">' + + '<div style="cursor: crosshair; position: absolute; margin: -30px; top: 0; right: 0; padding: 1px 0 0 1px;" class="transfo-rotator">' + + '<span class="fa-stack fa-lg">' + + '<i class="fa fa-circle fa-stack-2x"></i>' + + '<i class="fa fa-repeat fa-stack-1x fa-inverse"></i>' + + '</span>' + + '</div>' + + '<div style="' + css + 'top: 0%; left: 0%; cursor: nw-resize;" class="transfo-scaler-tl"></div>' + + '<div style="' + css + 'top: 0%; left: 100%; cursor: ne-resize;" class="transfo-scaler-tr"></div>' + + '<div style="' + css + 'top: 100%; left: 100%; cursor: se-resize;" class="transfo-scaler-br"></div>' + + '<div style="' + css + 'top: 100%; left: 0%; cursor: sw-resize;" class="transfo-scaler-bl"></div>' + + '<div style="' + css + 'top: 0%; left: 50%; cursor: n-resize;" class="transfo-scaler-tc"></div>' + + '<div style="' + css + 'top: 100%; left: 50%; cursor: s-resize;" class="transfo-scaler-bc"></div>' + + '<div style="' + css + 'top: 50%; left: 0%; cursor: w-resize;" class="transfo-scaler-ml"></div>' + + '<div style="' + css + 'top: 50%; left: 100%; cursor: e-resize;" class="transfo-scaler-mr"></div>' + + '<div style="' + css + 'border: 0; width: 0px; height: 0px; top: 50%; left: 50%;" class="transfo-scaler-mc"></div>' + + '</div>'); + transfo.$center = transfo.$markup.find(".transfo-scaler-mc"); + + // init setting and get css to set wrap + _setOptions($this, transfo); + _overwriteOptions ($this, transfo, settings); + + // append controls to container + $("body").append(transfo.$markup); + + // set transfo container and markup + setTimeout(function () { + _targetCss($this, transfo); + },0); + + _bind($this, transfo); + + _targetCss($this, transfo); + } + + function _overwriteOptions ($this, transfo, settings) { + transfo.settings = $.extend(transfo.settings, settings || {}); + } + + function _setOptions ($this, transfo) { + var style = $this.attr("style") || ""; + var transform = style.match(/transform\s*:([^;]+)/) ? style.match(/transform\s*:([^;]+)/)[1] : ""; + + transfo.settings = {}; + + transfo.settings.angle= transform.indexOf('rotate') != -1 ? parseFloat(transform.match(/rotate\(([^)]+)deg\)/)[1]) : 0; + transfo.settings.scalex= transform.indexOf('scaleX') != -1 ? parseFloat(transform.match(/scaleX\(([^)]+)\)/)[1]) : 1; + transfo.settings.scaley= transform.indexOf('scaleY') != -1 ? parseFloat(transform.match(/scaleY\(([^)]+)\)/)[1]) : 1; + + transfo.settings.style = style.replace(/[^;]*transform[^;]+/g, '').replace(/;+/g, ';'); + $this.attr("style", transfo.settings.style); + + transfo.settings.height = $this.innerHeight(); + transfo.settings.width = $this.innerWidth(); + + var translatex = transform.match(/translateX\(([0-9.-]+)(%|px)\)/); + var translatey = transform.match(/translateY\(([0-9.-]+)(%|px)\)/); + transfo.settings.translate = "%"; + + if (translatex && translatex[2] === "%") { + transfo.settings.translatexp = parseFloat(translatex[1]); + transfo.settings.translatex = transfo.settings.translatexp / 100 * transfo.settings.width; + } else { + transfo.settings.translatex = translatex ? parseFloat(translatex[1]) : 0; + } + if (translatey && translatey[2] === "%") { + transfo.settings.translateyp = parseFloat(translatey[1]); + transfo.settings.translatey = transfo.settings.translateyp / 100 * transfo.settings.height; + } else { + transfo.settings.translatey = translatey ? parseFloat(translatey[1]) : 0; + } + + transfo.settings.css = window.getComputedStyle($this[0], null); + transfo.settings.pos = $this.offset(); + + transfo.settings.rotationStep = 5; + transfo.settings.hide = false; + transfo.settings.callback = function () {}; + } + + function _bind ($this, transfo) { + function mousedown (event) { + _mouseDown($this, this, transfo, event); + $(document).on("mousemove", mousemove).on("mouseup", mouseup); + } + function mousemove (event) { + _mouseMove($this, this, transfo, event); + } + function mouseup (event) { + _mouseUp($this, this, transfo, event); + $(document).off("mousemove", mousemove).off("mouseup", mouseup); + } + + transfo.$markup.off().on("mousedown", mousedown); + transfo.$markup.find(">:not(.transfo-scaler-mc)").off().on("mousedown", mousedown); + } + + function _mouseDown($this, div, transfo, event) { + event.preventDefault(); + if (transfo.active || event.which !== 1) return; + + var type = "position", $e = $(div); + if ($e.hasClass("transfo-rotator")) type = "rotator"; + else if ($e.hasClass("transfo-scaler-tl")) type = "tl"; + else if ($e.hasClass("transfo-scaler-tr")) type = "tr"; + else if ($e.hasClass("transfo-scaler-br")) type = "br"; + else if ($e.hasClass("transfo-scaler-bl")) type = "bl"; + else if ($e.hasClass("transfo-scaler-tc")) type = "tc"; + else if ($e.hasClass("transfo-scaler-bc")) type = "bc"; + else if ($e.hasClass("transfo-scaler-ml")) type = "ml"; + else if ($e.hasClass("transfo-scaler-mr")) type = "mr"; + + transfo.active = { + "type": type, + "scalex": transfo.settings.scalex, + "scaley": transfo.settings.scaley, + "pageX": event.pageX, + "pageY": event.pageY, + "center": transfo.$center.offset(), + }; + } + function _mouseUp($this, div, transfo, event) { + transfo.active = null; + } + + function _mouseMove($this, div, transfo, event) { + event.preventDefault(); + if (!transfo.active) return; + var settings = transfo.settings; + var center = transfo.active.center; + var cdx = center.left - event.pageX; + var cdy = center.top - event.pageY; + + if (transfo.active.type == "rotator") { + var ang, dang = Math.atan((settings.width * settings.scalex) / (settings.height * settings.scaley)) / rad; + + if (cdy) ang = Math.atan(- cdx / cdy) / rad; + else ang = 0; + if (event.pageY >= center.top && event.pageX >= center.left) ang += 180; + else if (event.pageY >= center.top && event.pageX < center.left) ang += 180; + else if (event.pageY < center.top && event.pageX < center.left) ang += 360; + + ang -= dang; + if (settings.scaley < 0 && settings.scalex < 0) ang += 180; + + if (!event.ctrlKey) { + settings.angle = Math.round(ang / transfo.settings.rotationStep) * transfo.settings.rotationStep; + } else { + settings.angle = ang; + } + + // reset position : don't move center + _targetCss($this, transfo); + var new_center = transfo.$center.offset(); + var x = center.left - new_center.left; + var y = center.top - new_center.top; + var angle = ang * rad; + settings.translatex += x*Math.cos(angle) - y*Math.sin(-angle); + settings.translatey += - x*Math.sin(angle) + y*Math.cos(-angle); + } + else if (transfo.active.type == "position") { + var angle = settings.angle * rad; + var x = event.pageX - transfo.active.pageX; + var y = event.pageY - transfo.active.pageY; + transfo.active.pageX = event.pageX; + transfo.active.pageY = event.pageY; + var dx = x*Math.cos(angle) - y*Math.sin(-angle); + var dy = - x*Math.sin(angle) + y*Math.cos(-angle); + + settings.translatex += dx; + settings.translatey += dy; + } + else if (transfo.active.type.length === 2) { + var angle = settings.angle * rad; + var dx = cdx*Math.cos(angle) - cdy*Math.sin(-angle); + var dy = - cdx*Math.sin(angle) + cdy*Math.cos(-angle); + if (transfo.active.type.indexOf("t") != -1) { + settings.scaley = dy / (settings.height/2); + } + if (transfo.active.type.indexOf("b") != -1) { + settings.scaley = - dy / (settings.height/2); + } + if (transfo.active.type.indexOf("l") != -1) { + settings.scalex = dx / (settings.width/2); + } + if (transfo.active.type.indexOf("r") != -1) { + settings.scalex = - dx / (settings.width/2); + } + if (settings.scaley > 0 && settings.scaley < 0.05) settings.scaley = 0.05; + if (settings.scalex > 0 && settings.scalex < 0.05) settings.scalex = 0.05; + if (settings.scaley < 0 && settings.scaley > -0.05) settings.scaley = -0.05; + if (settings.scalex < 0 && settings.scalex > -0.05) settings.scalex = -0.05; + + if (event.shiftKey && + (transfo.active.type === "tl" || transfo.active.type === "bl" || + transfo.active.type === "tr" || transfo.active.type === "br")) { + settings.scaley = settings.scalex; + } + } + + settings.angle = Math.round(settings.angle); + settings.translatex = Math.round(settings.translatex); + settings.translatey = Math.round(settings.translatey); + settings.scalex = Math.round(settings.scalex*100)/100; + settings.scaley = Math.round(settings.scaley*100)/100; + + _targetCss($this, transfo); + return false; + } + + function _setCss($this, css, settings) { + var transform = ""; + var trans = false; + if (settings.angle !== 0) { + trans = true; + transform += " rotate("+settings.angle+"deg) "; + } + if (settings.translatex) { + trans = true; + transform += " translateX("+(settings.translate === "%" ? settings.translatexp+"%" : settings.translatex+"px")+") "; + } + if (settings.translatey) { + trans = true; + transform += " translateY("+(settings.translate === "%" ? settings.translateyp+"%" : settings.translatey+"px")+") "; + } + if (settings.scalex != 1) { + trans = true; + transform += " scaleX("+settings.scalex+") "; + } + if (settings.scaley != 1){ + trans = true; + transform += " scaleY("+settings.scaley+") "; + } + + if (trans) { + css += ";" + /* Safari */ + css += "-webkit-transform:" + transform + ";" + /* Firefox */ + + "-moz-transform:" + transform + ";" + /* IE */ + + "-ms-transform:" + transform + ";" + /* Opera */ + + "-o-transform:" + transform + ";" + /* Other */ + + "transform:" + transform + ";"; + } + + css = css.replace(/(\s*;)+/g, ';').replace(/^\s*;|;\s*$/g, ''); + + $this.attr("style", css); + } + + function _targetCss ($this, transfo) { + var settings = transfo.settings; + var width = parseFloat(settings.css.width); + var height = parseFloat(settings.css.height); + settings.translatexp = Math.round(settings.translatex/width*1000)/10; + settings.translateyp = Math.round(settings.translatey/height*1000)/10; + + _setCss($this, settings.style, settings); + + _setCss(transfo.$markup, + "position: absolute;" + + "top:" + settings.pos.top + "px;" + + "left:" + settings.pos.left + "px;" + + "width:" + width + "px;" + + "height:" + height + "px;" + + "cursor: move;", + settings); + transfo.$markup.find(">").css("transform", "scaleX("+(1/settings.scalex)+") scaleY("+(1/settings.scaley)+")"); + + _showHide($this, transfo); + + transfo.settings.callback.call($this[0], $this); + } + + function _showHide ($this, transfo) { + transfo.$markup.css("z-index", transfo.settings.hide ? -1 : 1000); + if (transfo.settings.hide) { + transfo.$markup.find(">").hide(); + transfo.$markup.find(".transfo-scaler-mc").show(); + } else { + transfo.$markup.find(">").show(); + } + } + + function _destroy ($this) { + $this.data('transfo').$markup.remove(); + $this.removeData('transfo'); + } + + function _reset ($this) { + var transfo = $this.data('transfo'); + _destroy($this); + $this.transfo(transfo.settings); + } + +})(jQuery); diff --git a/addons/website/static/src/js/website.ace.js b/addons/website/static/src/js/website.ace.js index ed7037e903c..a8af74bb52d 100644 --- a/addons/website/static/src/js/website.ace.js +++ b/addons/website/static/src/js/website.ace.js @@ -39,12 +39,17 @@ this.xml = text; }, isWellFormed: function () { + var error; if (document.implementation.createDocument) { - var dom = new DOMParser().parseFromString(this.xml, "text/xml"); - var error = dom.getElementsByTagName("parsererror"); - return error.length === 0 || error; - } 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; msDom.loadXML(this.xml); @@ -284,7 +289,7 @@ def.reject("server", session, error); }); } else { - def.reject(null, session, $(isWellFormed).text()); + def.reject(null, session, isWellFormed); } return def; }, diff --git a/addons/website/static/src/js/website.editor.js b/addons/website/static/src/js/website.editor.js index dc82c0c17b9..365b8cf46ea 100644 --- a/addons/website/static/src/js/website.editor.js +++ b/addons/website/static/src/js/website.editor.js @@ -20,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(); @@ -60,28 +64,35 @@ return new website.editor.RTELinkDialog(editor).appendTo(document.body); } function image_dialog(editor, image) { - return new website.editor.RTEImageDialog(editor, image).appendTo(document.body); + return new website.editor.MediaDialog(editor, image).appendTo(document.body); } // only enable editors manually 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 = {}; website.init_editor = function () { CKEDITOR.plugins.add('customdialogs', { -// requires: 'link,image', + // requires: 'link,image', init: function (editor) { editor.on('doubleclick', function (evt) { var element = evt.data.element; - if (element.is('img') && is_editable_node(element)) { + if ((element.is('img') || element.$.className.indexOf(' fa-') != -1) && is_editable_node(element)) { image_dialog(editor, element); return; } + var parent = new CKEDITOR.dom.element(element.$.parentNode); + if (parent.$.className.indexOf('media_iframe_video') != -1 && is_editable_node(parent)) { + image_dialog(editor, parent); + return; + } element = get_selected_link(editor) || evt.data.element; if (!(element.is('a') && is_editable_node(element))) { @@ -100,15 +111,17 @@ }, canUndo: false, editorFocus: true, + context: 'a', }); //noinspection JSValidateTypes - editor.addCommand('image', { + editor.addCommand('cimage', { exec: function (editor) { image_dialog(editor); return true; }, canUndo: false, editorFocus: true, + context: 'img', }); editor.ui.addButton('Link', { @@ -118,7 +131,7 @@ }); editor.ui.addButton('Image', { label: 'Image', - command: 'image', + command: 'cimage', toolbar: 'insert,10', }); @@ -199,8 +212,9 @@ icon: '/website/static/src/img/bglink.png', modes: { wysiwyg: true }, editorFocus: true, + context: 'a', panel: { - css: '/website/static/lib/bootstrap/css/bootstrap.css', + css: '/web/static/lib/bootstrap/css/bootstrap.css', attributes: { 'role': 'listbox', 'aria-label': label }, }, @@ -340,14 +354,40 @@ requires: 'widget', init: function (editor) { + var specials = { + // Can't find the correct ACL rule to only allow img tags + image: { content: '*' }, + html: { text: '*' }, + monetary: { + text: { + selector: 'span.oe_currency_value', + allowedContent: { } + } + } + }; + _(specials).each(function (editable, type) { + editor.widgets.add(type, { + draggable: false, + editables: editable, + upcast: function (el) { + return el.attributes['data-oe-type'] === type; + + } + }); + }); editor.widgets.add('oeref', { - editables: { text: '*' }, draggable: false, - + editables: { + text: { + selector: '*', + allowedContent: { } + }, + }, upcast: function (el) { - var matches = el.attributes['data-oe-type'] && el.attributes['data-oe-type'] !== 'monetary'; - if (!matches) { return false; } - + var type = el.attributes['data-oe-type']; + if (!type || (type in specials)) { + return false; + } if (el.attributes['data-oe-original']) { while (el.children.length) { el.children[0].remove(); @@ -357,28 +397,24 @@ )); } return true; - }, - }); - editor.widgets.add('monetary', { - editables: { text: 'span.oe_currency_value' }, - draggable: false, - - upcast: function (el) { - return el.attributes['data-oe-type'] === 'monetary'; } }); + editor.widgets.add('icons', { draggable: false, init: function () { this.on('edit', function () { - new website.editor.FontIconsDialog(editor, this.element.$) + new website.editor.MediaDialog(editor, this.element) .appendTo(document.body); }); }, upcast: function (el) { - return el.attributes['class'] - && (/\bfa\b/.test(el.attributes['class'])); + return el.hasClass('fa') + // ignore ir.ui.view (other data-oe-model should + // already have been matched by oeref and + // monetary? + && !el.attributes['data-oe-model']; } }); } @@ -598,22 +634,48 @@ }, /** - * Creates a "hover" button for image and link edition + * Creates a "hover" button for link edition + * + * @param {Function} editfn edition function, called when clicking the button + * @returns {jQuery} + */ + make_hover_button_link: function (editfn) { + return $(openerp.qweb.render('website.editor.hoverbutton.link', {})) + .hide() + .click(function (e) { + e.preventDefault(); + e.stopPropagation(); + editfn.call(this, e); + }) + .appendTo(document.body); + }, + + /** + * Creates a "hover" button for image * - * @param {String} label the button's label * @param {Function} editfn edition function, called when clicking the button - * @param {String} [classes] additional classes to set on the button + * @param {Function} stylefn edition style function, called when clicking the button * @returns {jQuery} */ - make_hover_button: function (label, editfn, classes) { - return $(openerp.qweb.render('website.editor.hoverbutton', { - label: label, - classes: classes, - })).hide().appendTo(document.body).click(function (e) { + make_hover_button_image: function (editfn, stylefn) { + var $div = $(openerp.qweb.render('website.editor.hoverbutton.media', {})) + .hide() + .appendTo(document.body); + + $div.find('[data-toggle="dropdown"]').dropdown(); + $div.find(".hover-edition-button").click(function (e) { e.preventDefault(); e.stopPropagation(); editfn.call(this, e); }); + if (stylefn) { + $div.find(".hover-style-button").click(function (e) { + e.preventDefault(); + e.stopPropagation(); + stylefn.call(this, e); + }); + } + return $div; }, /** * For UI clarity, during RTE edition when the user hovers links and @@ -622,69 +684,61 @@ */ setup_hover_buttons: function () { var editor = this.rte.editor; - var $link_button = this.make_hover_button(_t("Change"), function () { + var $link_button = this.make_hover_button_link(function () { var sel = new CKEDITOR.dom.element(previous); editor.getSelection().selectElement(sel); - if (previous.tagName.toUpperCase() === 'A') { - link_dialog(editor); - } else if(sel.hasClass('fa')) { - new website.editor.FontIconsDialog(editor, previous) + if(sel.hasClass('fa')) { + new website.editor.MediaDialog(editor, previous) .appendTo(document.body); + } else if (previous.tagName.toUpperCase() === 'A') { + link_dialog(editor); } $link_button.hide(); previous = null; - }, 'btn-xs'); - var $image_button = this.make_hover_button(_t("Change"), function () { - image_dialog(editor, new CKEDITOR.dom.element(previous)); - $image_button.hide(); - previous = null; - }, 'btn-sm'); + }); + + function is_icons_widget(element) { + var w = editor.widgets.getByElement(element); + return w && w.name === 'icons'; + } // previous is the state of the button-trigger: it's the // currently-ish hovered element which can trigger a button showing. // -ish, because when moving to the button itself ``previous`` is // still set to the element having triggered showing the button. var previous; - $(editor.element.$).on('mouseover', 'a, img, .fa', function () { + $(editor.element.$).on('mouseover', 'a', function () { // Back from edit button -> ignore if (previous && previous === this) { return; } + // hover button should appear for "editable" links and images + // (img and a nodes whose *attributes* are editable, they + // can not be "editing hosts") *or* for non-editing-host + // elements bearing an ``fa`` class. These should have been + // made into CKE widgets which are editing hosts by + // definition, so instead check if the element has been + // converted/upcasted to an fa widget var selected = new CKEDITOR.dom.element(this); - if (!is_editable_node(selected) && !selected.hasClass('fa')) { + if (!(is_editable_node(selected) || is_icons_widget(selected))) { return; } previous = this; var $selected = $(this); var position = $selected.offset(); - if ($selected.is('img')) { - $link_button.hide(); - // center button on image - $image_button.show().offset({ - top: $selected.outerHeight() / 2 - + position.top - - $image_button.outerHeight() / 2, - left: $selected.outerWidth() / 2 - + position.left - - $image_button.outerWidth() / 2, - }); - } else { - $image_button.hide(); - // put button below link, horizontally centered - $link_button.show().offset({ - top: $selected.outerHeight() - + position.top, - left: $selected.outerWidth() / 2 - + position.left - - $link_button.outerWidth() / 2 - }) - } + $link_button.show().offset({ + top: $selected.outerHeight() + + position.top, + left: $selected.outerWidth() / 2 + + position.left + - $link_button.outerWidth() / 2 + }) }).on('mouseleave', 'a, img, .fa', function (e) { var current = document.elementFromPoint(e.clientX, e.clientY); - if (current === $link_button[0] || current === $image_button[0]) { + if (current === $link_button[0] || $(current).parent()[0] === $link_button[0]) { return; } - $image_button.add($link_button).hide(); + $link_button.hide(); previous = null; }); } @@ -837,7 +891,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: @@ -999,6 +1052,7 @@ }, save: function () { this.close(); + this.trigger("saved"); }, cancel: function () { }, @@ -1248,6 +1302,128 @@ }, }); + website.editor.Media = openerp.Widget.extend({ + init: function (parent, editor, media) { + this._super(); + this.parent = parent; + this.editor = editor; + this.media = media; + }, + start: function () { + this.$preview = this.$('.preview-container').detach(); + return this._super(); + }, + search: function (needle) { + }, + save: function () { + }, + clear: function () { + }, + cancel: function () { + }, + close: function () { + }, + }); + website.editor.MediaDialog = website.editor.Dialog.extend({ + template: 'website.editor.dialog.media', + events : _.extend({}, website.editor.Dialog.prototype.events, { + 'input input#icon-search': 'search', + }), + + init: function (editor, media) { + this._super(editor); + this.editor = editor; + this.page = 0; + this.media = media; + }, + start: function () { + var self = this; + + this.imageDialog = new website.editor.RTEImageDialog(this, this.editor, this.media); + this.imageDialog.appendTo(this.$("#editor-media-image")); + this.iconDialog = new website.editor.FontIconsDialog(this, this.editor, this.media); + this.iconDialog.appendTo(this.$("#editor-media-icon")); + this.videoDialog = new website.editor.VideoDialog(this, this.editor, this.media); + this.videoDialog.appendTo(this.$("#editor-media-video")); + + this.active = this.imageDialog; + + $('a[data-toggle="tab"]').on('shown.bs.tab', function (event) { + if ($(event.target).is('[href="#editor-media-image"]')) { + self.active = self.imageDialog; + self.$('li.search, li.previous, li.next').removeClass("hidden"); + } else if ($(event.target).is('[href="#editor-media-icon"]')) { + self.active = self.iconDialog; + self.$('li.search, li.previous, li.next').removeClass("hidden"); + self.$('.nav-tabs li.previous, .nav-tabs li.next').addClass("hidden"); + } else if ($(event.target).is('[href="#editor-media-video"]')) { + self.active = self.videoDialog; + self.$('.nav-tabs li.search').addClass("hidden"); + } + }); + + if (this.media) { + if (this.media.$.nodeName === "IMG") { + this.$('[href="#editor-media-image"]').tab('show'); + } else if (this.media.$.className.match(/(^|\s)media_iframe_video($|\s)/)) { + this.$('[href="#editor-media-video"]').tab('show'); + } else if (this.media.$.className.match(/(^|\s)fa($|\s)/)) { + this.$('[href="#editor-media-icon"]').tab('show'); + } + + if ($(this.media.$).parent().data("oe-field") === "image") { + this.$('[href="#editor-media-video"], [href="#editor-media-icon"]').addClass('hidden'); + } + } + + return this._super(); + }, + save: function () { + var self = this; + if (self.media) { + this.media.$.innerHTML = ""; + if (this.active !== this.imageDialog) { + this.imageDialog.clear(); + } + if (this.active !== this.iconDialog) { + this.iconDialog.clear(); + } + if (this.active !== this.videoDialog) { + this.videoDialog.clear(); + } + } else { + var selection = this.editor.getSelection(); + var range = selection.getRanges(true)[0]; + this.media = new CKEDITOR.dom.element("img"); + range.insertNode(this.media); + range.selectNodeContents(this.media); + this.active.media = this.media; + } + + var $el = $(self.active.media.$); + + this.active.save(); + + this.media.$.className = this.media.$.className.replace(/\s+/g, ' '); + + setTimeout(function () { + $el.trigger("saved", self.active.media.$); + $(document.body).trigger("media-saved", [$el[0], self.active.media.$]); + },0); + + this._super(); + }, + searchTimer: null, + search: function () { + var self = this; + var needle = this.$("input#icon-search").val(); + clearTimeout(this.searchTimer); + this.searchTimer = setTimeout(function () { + self.active.search(needle || ""); + },250); + } + }); + /** * ImageDialog widget. Lets users change an image, including uploading a * new image in OpenERP or selecting the image style (if supported by @@ -1265,73 +1441,118 @@ * selected by the users (or possibly the ones * originally passed in) */ - website.editor.ImageDialog = website.editor.Dialog.extend({ + var IMAGES_PER_ROW = 6; + var IMAGES_ROWS = 2; + website.editor.ImageDialog = website.editor.Media.extend({ template: 'website.editor.dialog.image', events: _.extend({}, website.editor.Dialog.prototype.events, { - 'change .url-source': function (e) { this.changed($(e.target)); }, + 'change .url-source': function (e) { + this.changed($(e.target)); + }, 'click button.filepicker': function () { this.$('input[type=file]').click(); }, 'change input[type=file]': 'file_selection', - 'change input.url': 'preview_image', - 'click a[href=#existing]': 'browse_existing', - 'change select.image-style': 'preview_image', + 'submit form': 'form_submit', + 'change input.url': "change_input", + 'keyup input.url': "change_input", + //'change select.image-style': 'preview_image', + 'click .existing-attachments img': 'select_existing', + 'click .existing-attachment-remove': 'try_remove', }), + init: function (parent, editor, media) { + this.page = 0; + this._super(parent, editor, media); + }, start: function () { - this.$('button.wait').text("Uploading…"); - var $options = this.$('.image-style').children(); - this.image_styles = $options.map(function () { return this.value; }).get(); + var self = this; + var res = this._super(); - var o = { url: null, style: null, }; + var o = { url: null }; // avoid typos, prevent addition of new properties to the object Object.preventExtensions(o); this.trigger('start', o); - if (o.url) { - if (o.style) { - this.$('.image-style').val(o.style); + this.parent.$(".pager > li").click(function (e) { + e.preventDefault(); + var $target = $(e.currentTarget); + if ($target.hasClass('disabled')) { + return; } - this.set_image(o.url); - } + self.page += $target.hasClass('previous') ? -1 : 1; + self.display_attachments(); + }); - return this._super(); + this.set_image(o.url); + + return res; }, save: function () { + if (!this.link) { + this.link = this.$(".existing-attachments img:first").attr('src'); + } this.trigger('save', { - url: this.$('input.url').val(), - style: this.$('.image-style').val(), + url: this.link }); + this.media.renameNode("img"); + this.media.$.attributes.src = this.link; return this._super(); }, + clear: function () { + this.media.$.className = this.media.$.className.replace(/(^|\s)(img(\s|$)|img-[^\s]*)/g, ' '); + }, cancel: function () { this.trigger('cancel'); }, - /** - * Sets the provided image url as the dialog's value-to-save and - * refreshes the preview element to use it. - */ - set_image: function (url, error) { - this.$('input.url').val( - error ? '' : url); - this.$('input.url').val(url); - this.preview_image(); + change_input: function (e) { + var $input = $(e.target); + var $button = $input.parent().find("button"); + if ($input.val() === "") { + $button.addClass("btn-default").removeClass("btn-primary"); + } else { + $button.removeClass("btn-default").addClass("btn-primary"); + } }, - file_selection: function () { - this.$el.addClass('nosave'); - this.$('form').removeClass('has-error').find('.help-block').empty(); - this.$('button.filepicker').removeClass('btn-danger btn-success'); + search: function (needle) { + var self = this; + this.fetch_existing(needle).then(function () { + self.selected_existing(self.$('input.url').val()); + }); + }, + + set_image: function (url, error) { + var self = this; + if (url) this.link = url; + this.$('input.url').val(''); + this.fetch_existing().then(function () { + self.selected_existing(url); + }); + }, + form_submit: function (event) { var self = this; + var $form = this.$('form[action="/website/attach"]'); + if (!$form.find('input[name="upload"]').val().length) { + var url = $form.find('input[name="url"]').val(); + if (this.selected_existing(url).size()) { + event.preventDefault(); + return false; + } + } var callback = _.uniqueId('func_'); this.$('input[name=func]').val(callback); - window[callback] = function (url, error) { delete window[callback]; self.file_selected(url, error); }; + }, + file_selection: function () { + this.$el.addClass('nosave'); + this.$('form').removeClass('has-error').find('.help-block').empty(); + this.$('button.filepicker').removeClass('btn-danger btn-success'); this.$('form').submit(); }, file_selected: function(url, error) { @@ -1345,61 +1566,123 @@ $button.addClass('btn-danger'); } this.set_image(url, error); + // auto save and close popup + this.parent.save(); }, - preview_image: function () { - var loaded = function () { - this.$el.removeClass('nosave'); - }.bind(this); - var image = this.$('input.url').val(); - if (!image) { loaded(); return; } - var $img = this.$('img.image-preview') - .attr('src', image) - .removeClass(this.image_styles.join(' ')) - .addClass(this.$('select.image-style').val()); - - if ($img.prop('complete')) { - loaded(); - } else { - $img.load(loaded) + fetch_existing: function (needle) { + var domain = [['res_model', '=', 'ir.ui.view'], '|', + ['mimetype', '=', false], ['mimetype', '=like', 'image/%']]; + if (needle && needle.length) { + domain.push('|', ['datas_fname', 'ilike', needle], ['name', 'ilike', needle]); } + return openerp.jsonRpc('/web/dataset/call_kw', 'call', { + model: 'ir.attachment', + method: 'search_read', + args: [], + kwargs: { + fields: ['name', 'website_url'], + domain: domain, + order: 'id desc', + context: website.get_context(), + } + }).then(this.proxy('fetched_existing')); }, - 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); + fetched_existing: function (records) { + this.records = records; + this.display_attachments(); + }, + display_attachments: function () { + this.$('.help-block').empty(); + 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() + .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.content', {rows: rows})); + this.parent.$('.pager') + .find('li.previous').toggleClass('disabled', (from === 0)).end() + .find('li.next').toggleClass('disabled', (from + per_screen >= records.length)); + }, + select_existing: function (e) { + var link = $(e.currentTarget).attr('src'); + this.link = link; + this.selected_existing(link); + }, + selected_existing: function (link) { + this.$('.existing-attachment-cell.media_selected').removeClass("media_selected"); + var $select = this.$('.existing-attachment-cell img').filter(function () { + return $(this).attr("src") == link; + }).first(); + $select.parent().addClass("media_selected"); + return $select; + }, + + try_remove: function (e) { + var $help_block = this.$('.help-block').empty(); + var self = this; + var $a = $(e.target); + var id = parseInt($a.data('id'), 10); + var attachment = _.findWhere(this.records, {id: id}); + var $both = $a.parent().children(); + + $both.css({borderWidth: "5px", borderColor: "#f00"}); + + return openerp.jsonRpc('/web/dataset/call_kw', 'call', { + model: 'ir.attachment', + 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; + } + $both.css({borderWidth: "", borderColor: ""}); + $help_block.replaceWith(openerp.qweb.render( + 'website.editor.dialog.image.existing.error', { + views: prevented[id] + } + )); + }); }, }); - website.editor.RTEImageDialog = website.editor.ImageDialog.extend({ - init: function (editor, image) { - this._super(editor); - this.element = image; + website.editor.RTEImageDialog = website.editor.ImageDialog.extend({ + init: function (parent, editor, media) { + this._super(parent, editor, media); this.on('start', this, this.proxy('started')); this.on('save', this, this.proxy('saved')); }, started: function (holder) { - if (!this.element) { + if (!this.media) { var selection = this.editor.getSelection(); - this.element = selection && selection.getSelectedElement(); + this.media = selection && selection.getSelectedElement(); } - var el = this.element; + var el = this.media; if (!el || !el.is('img')) { return; } - _(this.image_styles).each(function (style) { - if (el.hasClass(style)) { - holder.style = style; - } - }); holder.url = el.getAttribute('src'); }, saved: function (data) { var element, editor = this.editor; - if (!(element = this.element)) { + if (!(element = this.media)) { element = editor.document.createElement('img'); element.addClass('img'); element.addClass('img-responsive'); @@ -1416,87 +1699,10 @@ var style = data.style; element.setAttribute('src', data.url); element.removeAttribute('data-cke-saved-src'); - $(element.$).removeClass(this.image_styles.join(' ')); if (style) { element.addClass(style); } }, }); - var IMAGES_PER_ROW = 6; - var IMAGES_ROWS = 4; - website.editor.ExistingImageDialog = website.editor.Dialog.extend({ - template: 'website.editor.dialog.image.existing', - events: _.extend({}, website.editor.Dialog.prototype.events, { - 'click .existing-attachments img': 'select_existing', - 'click .pager > li': function (e) { - e.preventDefault(); - var $target = $(e.currentTarget); - if ($target.hasClass('disabled')) { - return; - } - this.page += $target.hasClass('previous') ? -1 : 1; - this.display_attachments(); - }, - }), - init: function (parent) { - this.image = null; - this.page = 0; - this.parent = parent; - this._super(parent.editor); - }, - - start: function () { - return $.when( - this._super(), - this.fetch_existing().then(this.proxy('fetched_existing'))); - }, - - fetch_existing: function () { - return openerp.jsonRpc('/web/dataset/call_kw', 'call', { - model: 'ir.attachment', - method: 'search_read', - args: [], - kwargs: { - fields: ['name', 'website_url'], - domain: [['res_model', '=', 'ir.ui.view']], - order: 'name', - context: website.get_context(), - } - }); - }, - 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() - .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.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) { - var link = $(e.currentTarget).attr('src'); - if (link) { - this.parent.set_image(link); - } - this.close() - }, - }); - function get_selected_link(editor) { var sel = editor.getSelection(), el = sel.getSelectedElement(); @@ -1508,7 +1714,7 @@ range.shrink(CKEDITOR.SHRINK_TEXT); var commonAncestor = range.getCommonAncestor(); var viewRoot = editor.elementPath(commonAncestor).contains(function (element) { - return element.data('oe-model') === 'ir.ui.view' + return element.data('oe-model') === 'ir.ui.view'; }); if (!viewRoot) { return null; } // if viewRoot is the first link, don't edit it. @@ -1516,7 +1722,7 @@ .contains('a', true); } - website.editor.FontIconsDialog = website.editor.Dialog.extend({ + website.editor.FontIconsDialog = website.editor.Media.extend({ template: 'website.editor.dialog.font-icons', events : _.extend({}, website.editor.Dialog.prototype.events, { change: 'update_preview', @@ -1534,20 +1740,6 @@ this.$('#fa-size').val(e.target.getAttribute('data-size')); this.update_preview(); }, - 'input input#icon-search': function () { - var needle = this.$('#icon-search').val(); - var icons = this.icons; - if (needle) { - icons = _(icons).filter(function (icon) { - return icon.id.substring(3).indexOf(needle) !== -1; - }); - } - - this.$('div.font-icons-icons').html( - openerp.qweb.render( - 'website.editor.dialog.font-icons.icons', - {icons: icons})); - }, }), // List of FontAwesome icons in 4.0.3, extracted from the cheatsheet. @@ -1557,10 +1749,6 @@ // where we still need to implement ``initSelection``) // TODO: add id/name to the text in order to allow FAYT selection of icons? icons: [{"text": "\uf000", "id": "fa-glass"}, {"text": "\uf001", "id": "fa-music"}, {"text": "\uf002", "id": "fa-search"}, {"text": "\uf003", "id": "fa-envelope-o"}, {"text": "\uf004", "id": "fa-heart"}, {"text": "\uf005", "id": "fa-star"}, {"text": "\uf006", "id": "fa-star-o"}, {"text": "\uf007", "id": "fa-user"}, {"text": "\uf008", "id": "fa-film"}, {"text": "\uf009", "id": "fa-th-large"}, {"text": "\uf00a", "id": "fa-th"}, {"text": "\uf00b", "id": "fa-th-list"}, {"text": "\uf00c", "id": "fa-check"}, {"text": "\uf00d", "id": "fa-times"}, {"text": "\uf00e", "id": "fa-search-plus"}, {"text": "\uf010", "id": "fa-search-minus"}, {"text": "\uf011", "id": "fa-power-off"}, {"text": "\uf012", "id": "fa-signal"}, {"text": "\uf013", "id": "fa-cog"}, {"text": "\uf014", "id": "fa-trash-o"}, {"text": "\uf015", "id": "fa-home"}, {"text": "\uf016", "id": "fa-file-o"}, {"text": "\uf017", "id": "fa-clock-o"}, {"text": "\uf018", "id": "fa-road"}, {"text": "\uf019", "id": "fa-download"}, {"text": "\uf01a", "id": "fa-arrow-circle-o-down"}, {"text": "\uf01b", "id": "fa-arrow-circle-o-up"}, {"text": "\uf01c", "id": "fa-inbox"}, {"text": "\uf01d", "id": "fa-play-circle-o"}, {"text": "\uf01e", "id": "fa-repeat"}, {"text": "\uf021", "id": "fa-refresh"}, {"text": "\uf022", "id": "fa-list-alt"}, {"text": "\uf023", "id": "fa-lock"}, {"text": "\uf024", "id": "fa-flag"}, {"text": "\uf025", "id": "fa-headphones"}, {"text": "\uf026", "id": "fa-volume-off"}, {"text": "\uf027", "id": "fa-volume-down"}, {"text": "\uf028", "id": "fa-volume-up"}, {"text": "\uf029", "id": "fa-qrcode"}, {"text": "\uf02a", "id": "fa-barcode"}, {"text": "\uf02b", "id": "fa-tag"}, {"text": "\uf02c", "id": "fa-tags"}, {"text": "\uf02d", "id": "fa-book"}, {"text": "\uf02e", "id": "fa-bookmark"}, {"text": "\uf02f", "id": "fa-print"}, {"text": "\uf030", "id": "fa-camera"}, {"text": "\uf031", "id": "fa-font"}, {"text": "\uf032", "id": "fa-bold"}, {"text": "\uf033", "id": "fa-italic"}, {"text": "\uf034", "id": "fa-text-height"}, {"text": "\uf035", "id": "fa-text-width"}, {"text": "\uf036", "id": "fa-align-left"}, {"text": "\uf037", "id": "fa-align-center"}, {"text": "\uf038", "id": "fa-align-right"}, {"text": "\uf039", "id": "fa-align-justify"}, {"text": "\uf03a", "id": "fa-list"}, {"text": "\uf03b", "id": "fa-outdent"}, {"text": "\uf03c", "id": "fa-indent"}, {"text": "\uf03d", "id": "fa-video-camera"}, {"text": "\uf03e", "id": "fa-picture-o"}, {"text": "\uf040", "id": "fa-pencil"}, {"text": "\uf041", "id": "fa-map-marker"}, {"text": "\uf042", "id": "fa-adjust"}, {"text": "\uf043", "id": "fa-tint"}, {"text": "\uf044", "id": "fa-pencil-square-o"}, {"text": "\uf045", "id": "fa-share-square-o"}, {"text": "\uf046", "id": "fa-check-square-o"}, {"text": "\uf047", "id": "fa-arrows"}, {"text": "\uf048", "id": "fa-step-backward"}, {"text": "\uf049", "id": "fa-fast-backward"}, {"text": "\uf04a", "id": "fa-backward"}, {"text": "\uf04b", "id": "fa-play"}, {"text": "\uf04c", "id": "fa-pause"}, {"text": "\uf04d", "id": "fa-stop"}, {"text": "\uf04e", "id": "fa-forward"}, {"text": "\uf050", "id": "fa-fast-forward"}, {"text": "\uf051", "id": "fa-step-forward"}, {"text": "\uf052", "id": "fa-eject"}, {"text": "\uf053", "id": "fa-chevron-left"}, {"text": "\uf054", "id": "fa-chevron-right"}, {"text": "\uf055", "id": "fa-plus-circle"}, {"text": "\uf056", "id": "fa-minus-circle"}, {"text": "\uf057", "id": "fa-times-circle"}, {"text": "\uf058", "id": "fa-check-circle"}, {"text": "\uf059", "id": "fa-question-circle"}, {"text": "\uf05a", "id": "fa-info-circle"}, {"text": "\uf05b", "id": "fa-crosshairs"}, {"text": "\uf05c", "id": "fa-times-circle-o"}, {"text": "\uf05d", "id": "fa-check-circle-o"}, {"text": "\uf05e", "id": "fa-ban"}, {"text": "\uf060", "id": "fa-arrow-left"}, {"text": "\uf061", "id": "fa-arrow-right"}, {"text": "\uf062", "id": "fa-arrow-up"}, {"text": "\uf063", "id": "fa-arrow-down"}, {"text": "\uf064", "id": "fa-share"}, {"text": "\uf065", "id": "fa-expand"}, {"text": "\uf066", "id": "fa-compress"}, {"text": "\uf067", "id": "fa-plus"}, {"text": "\uf068", "id": "fa-minus"}, {"text": "\uf069", "id": "fa-asterisk"}, {"text": "\uf06a", "id": "fa-exclamation-circle"}, {"text": "\uf06b", "id": "fa-gift"}, {"text": "\uf06c", "id": "fa-leaf"}, {"text": "\uf06d", "id": "fa-fire"}, {"text": "\uf06e", "id": "fa-eye"}, {"text": "\uf070", "id": "fa-eye-slash"}, {"text": "\uf071", "id": "fa-exclamation-triangle"}, {"text": "\uf072", "id": "fa-plane"}, {"text": "\uf073", "id": "fa-calendar"}, {"text": "\uf074", "id": "fa-random"}, {"text": "\uf075", "id": "fa-comment"}, {"text": "\uf076", "id": "fa-magnet"}, {"text": "\uf077", "id": "fa-chevron-up"}, {"text": "\uf078", "id": "fa-chevron-down"}, {"text": "\uf079", "id": "fa-retweet"}, {"text": "\uf07a", "id": "fa-shopping-cart"}, {"text": "\uf07b", "id": "fa-folder"}, {"text": "\uf07c", "id": "fa-folder-open"}, {"text": "\uf07d", "id": "fa-arrows-v"}, {"text": "\uf07e", "id": "fa-arrows-h"}, {"text": "\uf080", "id": "fa-bar-chart-o"}, {"text": "\uf081", "id": "fa-twitter-square"}, {"text": "\uf082", "id": "fa-facebook-square"}, {"text": "\uf083", "id": "fa-camera-retro"}, {"text": "\uf084", "id": "fa-key"}, {"text": "\uf085", "id": "fa-cogs"}, {"text": "\uf086", "id": "fa-comments"}, {"text": "\uf087", "id": "fa-thumbs-o-up"}, {"text": "\uf088", "id": "fa-thumbs-o-down"}, {"text": "\uf089", "id": "fa-star-half"}, {"text": "\uf08a", "id": "fa-heart-o"}, {"text": "\uf08b", "id": "fa-sign-out"}, {"text": "\uf08c", "id": "fa-linkedin-square"}, {"text": "\uf08d", "id": "fa-thumb-tack"}, {"text": "\uf08e", "id": "fa-external-link"}, {"text": "\uf090", "id": "fa-sign-in"}, {"text": "\uf091", "id": "fa-trophy"}, {"text": "\uf092", "id": "fa-github-square"}, {"text": "\uf093", "id": "fa-upload"}, {"text": "\uf094", "id": "fa-lemon-o"}, {"text": "\uf095", "id": "fa-phone"}, {"text": "\uf096", "id": "fa-square-o"}, {"text": "\uf097", "id": "fa-bookmark-o"}, {"text": "\uf098", "id": "fa-phone-square"}, {"text": "\uf099", "id": "fa-twitter"}, {"text": "\uf09a", "id": "fa-facebook"}, {"text": "\uf09b", "id": "fa-github"}, {"text": "\uf09c", "id": "fa-unlock"}, {"text": "\uf09d", "id": "fa-credit-card"}, {"text": "\uf09e", "id": "fa-rss"}, {"text": "\uf0a0", "id": "fa-hdd-o"}, {"text": "\uf0a1", "id": "fa-bullhorn"}, {"text": "\uf0f3", "id": "fa-bell"}, {"text": "\uf0a3", "id": "fa-certificate"}, {"text": "\uf0a4", "id": "fa-hand-o-right"}, {"text": "\uf0a5", "id": "fa-hand-o-left"}, {"text": "\uf0a6", "id": "fa-hand-o-up"}, {"text": "\uf0a7", "id": "fa-hand-o-down"}, {"text": "\uf0a8", "id": "fa-arrow-circle-left"}, {"text": "\uf0a9", "id": "fa-arrow-circle-right"}, {"text": "\uf0aa", "id": "fa-arrow-circle-up"}, {"text": "\uf0ab", "id": "fa-arrow-circle-down"}, {"text": "\uf0ac", "id": "fa-globe"}, {"text": "\uf0ad", "id": "fa-wrench"}, {"text": "\uf0ae", "id": "fa-tasks"}, {"text": "\uf0b0", "id": "fa-filter"}, {"text": "\uf0b1", "id": "fa-briefcase"}, {"text": "\uf0b2", "id": "fa-arrows-alt"}, {"text": "\uf0c0", "id": "fa-users"}, {"text": "\uf0c1", "id": "fa-link"}, {"text": "\uf0c2", "id": "fa-cloud"}, {"text": "\uf0c3", "id": "fa-flask"}, {"text": "\uf0c4", "id": "fa-scissors"}, {"text": "\uf0c5", "id": "fa-files-o"}, {"text": "\uf0c6", "id": "fa-paperclip"}, {"text": "\uf0c7", "id": "fa-floppy-o"}, {"text": "\uf0c8", "id": "fa-square"}, {"text": "\uf0c9", "id": "fa-bars"}, {"text": "\uf0ca", "id": "fa-list-ul"}, {"text": "\uf0cb", "id": "fa-list-ol"}, {"text": "\uf0cc", "id": "fa-strikethrough"}, {"text": "\uf0cd", "id": "fa-underline"}, {"text": "\uf0ce", "id": "fa-table"}, {"text": "\uf0d0", "id": "fa-magic"}, {"text": "\uf0d1", "id": "fa-truck"}, {"text": "\uf0d2", "id": "fa-pinterest"}, {"text": "\uf0d3", "id": "fa-pinterest-square"}, {"text": "\uf0d4", "id": "fa-google-plus-square"}, {"text": "\uf0d5", "id": "fa-google-plus"}, {"text": "\uf0d6", "id": "fa-money"}, {"text": "\uf0d7", "id": "fa-caret-down"}, {"text": "\uf0d8", "id": "fa-caret-up"}, {"text": "\uf0d9", "id": "fa-caret-left"}, {"text": "\uf0da", "id": "fa-caret-right"}, {"text": "\uf0db", "id": "fa-columns"}, {"text": "\uf0dc", "id": "fa-sort"}, {"text": "\uf0dd", "id": "fa-sort-asc"}, {"text": "\uf0de", "id": "fa-sort-desc"}, {"text": "\uf0e0", "id": "fa-envelope"}, {"text": "\uf0e1", "id": "fa-linkedin"}, {"text": "\uf0e2", "id": "fa-undo"}, {"text": "\uf0e3", "id": "fa-gavel"}, {"text": "\uf0e4", "id": "fa-tachometer"}, {"text": "\uf0e5", "id": "fa-comment-o"}, {"text": "\uf0e6", "id": "fa-comments-o"}, {"text": "\uf0e7", "id": "fa-bolt"}, {"text": "\uf0e8", "id": "fa-sitemap"}, {"text": "\uf0e9", "id": "fa-umbrella"}, {"text": "\uf0ea", "id": "fa-clipboard"}, {"text": "\uf0eb", "id": "fa-lightbulb-o"}, {"text": "\uf0ec", "id": "fa-exchange"}, {"text": "\uf0ed", "id": "fa-cloud-download"}, {"text": "\uf0ee", "id": "fa-cloud-upload"}, {"text": "\uf0f0", "id": "fa-user-md"}, {"text": "\uf0f1", "id": "fa-stethoscope"}, {"text": "\uf0f2", "id": "fa-suitcase"}, {"text": "\uf0a2", "id": "fa-bell-o"}, {"text": "\uf0f4", "id": "fa-coffee"}, {"text": "\uf0f5", "id": "fa-cutlery"}, {"text": "\uf0f6", "id": "fa-file-text-o"}, {"text": "\uf0f7", "id": "fa-building-o"}, {"text": "\uf0f8", "id": "fa-hospital-o"}, {"text": "\uf0f9", "id": "fa-ambulance"}, {"text": "\uf0fa", "id": "fa-medkit"}, {"text": "\uf0fb", "id": "fa-fighter-jet"}, {"text": "\uf0fc", "id": "fa-beer"}, {"text": "\uf0fd", "id": "fa-h-square"}, {"text": "\uf0fe", "id": "fa-plus-square"}, {"text": "\uf100", "id": "fa-angle-double-left"}, {"text": "\uf101", "id": "fa-angle-double-right"}, {"text": "\uf102", "id": "fa-angle-double-up"}, {"text": "\uf103", "id": "fa-angle-double-down"}, {"text": "\uf104", "id": "fa-angle-left"}, {"text": "\uf105", "id": "fa-angle-right"}, {"text": "\uf106", "id": "fa-angle-up"}, {"text": "\uf107", "id": "fa-angle-down"}, {"text": "\uf108", "id": "fa-desktop"}, {"text": "\uf109", "id": "fa-laptop"}, {"text": "\uf10a", "id": "fa-tablet"}, {"text": "\uf10b", "id": "fa-mobile"}, {"text": "\uf10c", "id": "fa-circle-o"}, {"text": "\uf10d", "id": "fa-quote-left"}, {"text": "\uf10e", "id": "fa-quote-right"}, {"text": "\uf110", "id": "fa-spinner"}, {"text": "\uf111", "id": "fa-circle"}, {"text": "\uf112", "id": "fa-reply"}, {"text": "\uf113", "id": "fa-github-alt"}, {"text": "\uf114", "id": "fa-folder-o"}, {"text": "\uf115", "id": "fa-folder-open-o"}, {"text": "\uf118", "id": "fa-smile-o"}, {"text": "\uf119", "id": "fa-frown-o"}, {"text": "\uf11a", "id": "fa-meh-o"}, {"text": "\uf11b", "id": "fa-gamepad"}, {"text": "\uf11c", "id": "fa-keyboard-o"}, {"text": "\uf11d", "id": "fa-flag-o"}, {"text": "\uf11e", "id": "fa-flag-checkered"}, {"text": "\uf120", "id": "fa-terminal"}, {"text": "\uf121", "id": "fa-code"}, {"text": "\uf122", "id": "fa-reply-all"}, {"text": "\uf122", "id": "fa-mail-reply-all"}, {"text": "\uf123", "id": "fa-star-half-o"}, {"text": "\uf124", "id": "fa-location-arrow"}, {"text": "\uf125", "id": "fa-crop"}, {"text": "\uf126", "id": "fa-code-fork"}, {"text": "\uf127", "id": "fa-chain-broken"}, {"text": "\uf128", "id": "fa-question"}, {"text": "\uf129", "id": "fa-info"}, {"text": "\uf12a", "id": "fa-exclamation"}, {"text": "\uf12b", "id": "fa-superscript"}, {"text": "\uf12c", "id": "fa-subscript"}, {"text": "\uf12d", "id": "fa-eraser"}, {"text": "\uf12e", "id": "fa-puzzle-piece"}, {"text": "\uf130", "id": "fa-microphone"}, {"text": "\uf131", "id": "fa-microphone-slash"}, {"text": "\uf132", "id": "fa-shield"}, {"text": "\uf133", "id": "fa-calendar-o"}, {"text": "\uf134", "id": "fa-fire-extinguisher"}, {"text": "\uf135", "id": "fa-rocket"}, {"text": "\uf136", "id": "fa-maxcdn"}, {"text": "\uf137", "id": "fa-chevron-circle-left"}, {"text": "\uf138", "id": "fa-chevron-circle-right"}, {"text": "\uf139", "id": "fa-chevron-circle-up"}, {"text": "\uf13a", "id": "fa-chevron-circle-down"}, {"text": "\uf13b", "id": "fa-html5"}, {"text": "\uf13c", "id": "fa-css3"}, {"text": "\uf13d", "id": "fa-anchor"}, {"text": "\uf13e", "id": "fa-unlock-alt"}, {"text": "\uf140", "id": "fa-bullseye"}, {"text": "\uf141", "id": "fa-ellipsis-h"}, {"text": "\uf142", "id": "fa-ellipsis-v"}, {"text": "\uf143", "id": "fa-rss-square"}, {"text": "\uf144", "id": "fa-play-circle"}, {"text": "\uf145", "id": "fa-ticket"}, {"text": "\uf146", "id": "fa-minus-square"}, {"text": "\uf147", "id": "fa-minus-square-o"}, {"text": "\uf148", "id": "fa-level-up"}, {"text": "\uf149", "id": "fa-level-down"}, {"text": "\uf14a", "id": "fa-check-square"}, {"text": "\uf14b", "id": "fa-pencil-square"}, {"text": "\uf14c", "id": "fa-external-link-square"}, {"text": "\uf14d", "id": "fa-share-square"}, {"text": "\uf14e", "id": "fa-compass"}, {"text": "\uf150", "id": "fa-caret-square-o-down"}, {"text": "\uf151", "id": "fa-caret-square-o-up"}, {"text": "\uf152", "id": "fa-caret-square-o-right"}, {"text": "\uf153", "id": "fa-eur"}, {"text": "\uf154", "id": "fa-gbp"}, {"text": "\uf155", "id": "fa-usd"}, {"text": "\uf156", "id": "fa-inr"}, {"text": "\uf157", "id": "fa-jpy"}, {"text": "\uf158", "id": "fa-rub"}, {"text": "\uf159", "id": "fa-krw"}, {"text": "\uf15a", "id": "fa-btc"}, {"text": "\uf15b", "id": "fa-file"}, {"text": "\uf15c", "id": "fa-file-text"}, {"text": "\uf15d", "id": "fa-sort-alpha-asc"}, {"text": "\uf15e", "id": "fa-sort-alpha-desc"}, {"text": "\uf160", "id": "fa-sort-amount-asc"}, {"text": "\uf161", "id": "fa-sort-amount-desc"}, {"text": "\uf162", "id": "fa-sort-numeric-asc"}, {"text": "\uf163", "id": "fa-sort-numeric-desc"}, {"text": "\uf164", "id": "fa-thumbs-up"}, {"text": "\uf165", "id": "fa-thumbs-down"}, {"text": "\uf166", "id": "fa-youtube-square"}, {"text": "\uf167", "id": "fa-youtube"}, {"text": "\uf168", "id": "fa-xing"}, {"text": "\uf169", "id": "fa-xing-square"}, {"text": "\uf16a", "id": "fa-youtube-play"}, {"text": "\uf16b", "id": "fa-dropbox"}, {"text": "\uf16c", "id": "fa-stack-overflow"}, {"text": "\uf16d", "id": "fa-instagram"}, {"text": "\uf16e", "id": "fa-flickr"}, {"text": "\uf170", "id": "fa-adn"}, {"text": "\uf171", "id": "fa-bitbucket"}, {"text": "\uf172", "id": "fa-bitbucket-square"}, {"text": "\uf173", "id": "fa-tumblr"}, {"text": "\uf174", "id": "fa-tumblr-square"}, {"text": "\uf175", "id": "fa-long-arrow-down"}, {"text": "\uf176", "id": "fa-long-arrow-up"}, {"text": "\uf177", "id": "fa-long-arrow-left"}, {"text": "\uf178", "id": "fa-long-arrow-right"}, {"text": "\uf179", "id": "fa-apple"}, {"text": "\uf17a", "id": "fa-windows"}, {"text": "\uf17b", "id": "fa-android"}, {"text": "\uf17c", "id": "fa-linux"}, {"text": "\uf17d", "id": "fa-dribbble"}, {"text": "\uf17e", "id": "fa-skype"}, {"text": "\uf180", "id": "fa-foursquare"}, {"text": "\uf181", "id": "fa-trello"}, {"text": "\uf182", "id": "fa-female"}, {"text": "\uf183", "id": "fa-male"}, {"text": "\uf184", "id": "fa-gittip"}, {"text": "\uf185", "id": "fa-sun-o"}, {"text": "\uf186", "id": "fa-moon-o"}, {"text": "\uf187", "id": "fa-archive"}, {"text": "\uf188", "id": "fa-bug"}, {"text": "\uf189", "id": "fa-vk"}, {"text": "\uf18a", "id": "fa-weibo"}, {"text": "\uf18b", "id": "fa-renren"}, {"text": "\uf18c", "id": "fa-pagelines"}, {"text": "\uf18d", "id": "fa-stack-exchange"}, {"text": "\uf18e", "id": "fa-arrow-circle-o-right"}, {"text": "\uf190", "id": "fa-arrow-circle-o-left"}, {"text": "\uf191", "id": "fa-caret-square-o-left"}, {"text": "\uf192", "id": "fa-dot-circle-o"}, {"text": "\uf193", "id": "fa-wheelchair"}, {"text": "\uf194", "id": "fa-vimeo-square"}, {"text": "\uf195", "id": "fa-try"}, {"text": "\uf196", "id": "fa-plus-square-o"}], - init: function (editor, element) { - this._super(editor); - this.element = element; - }, /** * Initializes select2: in Chrome and Safari, <select> font apparently * isn't customizable (?) and the fontawesome glyphs fail to appear. @@ -1568,17 +1756,33 @@ start: function () { return this._super().then(this.proxy('load_data')); }, + search: function (needle) { + var icons = this.icons; + if (needle) { + icons = _(icons).filter(function (icon) { + return icon.id.substring(3).indexOf(needle) !== -1; + }); + } + + this.$('div.font-icons-icons').html( + openerp.qweb.render( + 'website.editor.dialog.font-icons.icons', + {icons: icons})); + }, /** * Removes existing FontAwesome classes on the bound element, and sets * all the new ones if necessary. */ save: function () { - var classes = this.element.className.split(/\s+/); + var style = this.media.$.attributes.style ? this.media.$.attributes.style.textContent : ''; + var classes = (this.media.$.className||"").split(/\s+/); var non_fa_classes = _.reject(classes, function (cls) { return cls === 'fa' || /^fa-/.test(cls); }); var final_classes = non_fa_classes.concat(this.get_fa_classes()); - this.element.className = final_classes.join(' '); + this.media.$.className = final_classes.join(' '); + this.media.renameNode("span"); + this.media.$.attributes.style.textContent = style; this._super(); }, /** @@ -1589,7 +1793,7 @@ * which may not match the visual look of the element. */ load_data: function () { - var classes = this.element.className.split(/\s+/); + var classes = (this.media&&this.media.$.className||"").split(/\s+/); for (var i = 0; i < classes.length; i++) { var cls = classes[i]; switch(cls) { @@ -1628,7 +1832,9 @@ ]; }, update_preview: function () { + this.$preview.empty(); var $preview = this.$('#fa-preview').empty(); + var sizes = ['', 'fa-2x', 'fa-3x', 'fa-4x', 'fa-5x']; var classes = this.get_fa_classes(); var no_sizes = _.difference(classes, sizes).join(' '); @@ -1640,13 +1846,123 @@ .attr('data-size', size) .addClass(size) .addClass(no_sizes); - if ((size && _.contains(classes, size)) || (!size && !selected)) { + if ((size && _.contains(classes, size)) || (classes[2] === "" && !selected)) { + this.$preview.append($p.clone()); + this.$('#fa-size').val(size); $p.addClass('font-icons-selected'); selected = true; } $preview.prepend($p); } - } + }, + clear: function () { + this.media.$.className = this.media.$.className.replace(/(^|\s)(fa(\s|$)|fa-[^\s]*)/g, ' '); + }, + }); + + website.editor.VideoDialog = website.editor.Media.extend({ + template: 'website.editor.dialog.video', + events : _.extend({}, website.editor.Dialog.prototype.events, { + 'click input#urlvideo ~ button': 'get_video', + 'click input#embedvideo ~ button': 'get_embed_video', + 'change input#urlvideo': 'change_input', + 'keyup input#urlvideo': 'change_input', + 'change input#embedvideo': 'change_input', + 'keyup input#embedvideo': 'change_input' + }), + start: function () { + this.$iframe = this.$("iframe"); + var $media = $(this.media && this.media.$); + if ($media.hasClass("media_iframe_video")) { + var src = $media.data('src'); + this.$("input#urlvideo").val(src); + this.$("#autoplay").attr("checked", src.indexOf('autoplay=1') != -1); + this.get_video(); + } + return this._super(); + }, + change_input: function (e) { + var $input = $(e.target); + var $button = $input.parent().find("button"); + if ($input.val() === "") { + $button.addClass("btn-default").removeClass("btn-primary"); + } else { + $button.removeClass("btn-default").addClass("btn-primary"); + } + }, + get_url: function () { + var video_id = this.$("#video_id").val(); + var video_type = this.$("#video_type").val(); + switch (video_type) { + case "youtube": + return "//www.youtube.com/embed/" + video_id + "?autoplay=" + (this.$("#autoplay").is(":checked") ? 1 : 0); + case "vimeo": + return "//player.vimeo.com/video/" + video_id + "?autoplay=" + (this.$("#autoplay").is(":checked") ? 1 : 0); + case "dailymotion": + return "//www.dailymotion.com/embed/video/" + video_id + "?autoplay=" + (this.$("#autoplay").is(":checked") ? 1 : 0); + default: + return video_id; + } + }, + get_embed_video: function (event) { + event.preventDefault(); + var embedvideo = this.$("input#embedvideo").val().match(/src=["']?([^"']+)["' ]?/); + if (embedvideo) { + this.$("input#urlvideo").val(embedvideo[1]); + this.get_video(event); + } + return false; + }, + get_video: function (event) { + if (event) event.preventDefault(); + var needle = this.$("input#urlvideo").val(); + var video_id; + var video_type; + + if (needle.indexOf(".youtube.") != -1) { + video_type = "youtube"; + video_id = needle.match(/\.youtube\.[a-z]+\/(embed\/|watch\?v=)?([^\/?&]+)/i)[2]; + } else if (needle.indexOf("//youtu.") != -1) { + video_type = "youtube"; + video_id = needle.match(/youtube\.[a-z]+\/([^\/?&]+)/i)[1]; + } else if (needle.indexOf("player.vimeo.") != -1 || needle.indexOf("//vimeo.") != -1) { + video_type = "vimeo"; + video_id = needle.match(/vimeo\.[a-z]+\/(video\/)?([^?&]+)/i)[2]; + } else if (needle.indexOf(".dailymotion.") != -1) { + video_type = "dailymotion"; + video_id = needle.match(/dailymotion\.[a-z]+\/(embed\/)?(video\/)?([^\/?&]+)/i)[3]; + } else { + video_type = ""; + video_id = needle; + } + + this.$("#video_id").val(video_id); + this.$("#video_type").val(video_type); + + this.$iframe.attr("src", this.get_url()); + return false; + }, + save: function () { + var video_id = this.$("#video_id").val(); + if (!video_id) { + this.$("button.btn-primary").click(); + video_id = this.$("#video_id").val(); + } + var video_type = this.$("#video_type").val(); + var style = this.media.$.attributes.style ? this.media.$.attributes.style.textContent : ''; + var $iframe = $( + '<div class="media_iframe_video" data-src="'+this.get_url()+'" style="'+style+'">'+ + '<div class="css_editable_mode_display"> </div>'+ + '<iframe src="'+this.get_url()+'" frameborder="0" allowfullscreen="allowfullscreen"></iframe>'+ + '</div>'); + $(this.media.$).replaceWith($iframe); + this.media.$ = $iframe[0]; + this._super(); + }, + clear: function () { + delete this.media.$.dataset.src; + this.media.$.className = this.media.$.className.replace(/(^|\s)media_iframe_video(\s|$)/g, ' '); + }, }); website.Observer = window.MutationObserver || window.WebkitMutationObserver || window.JsMutationObserver; @@ -1684,6 +2000,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. @@ -1714,6 +2033,9 @@ if (node.nodeName === 'BR' && node.getAttribute('type') === '_moz') { // <br type="_moz"> appears when focusing RTE in FF, ignore continue; + } else if (node.nodeName === 'DIV' && $(node).hasClass('oe_drop_zone')) { + // ignore dropzone inserted by snippets + continue } } @@ -1721,4 +2043,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.js b/addons/website/static/src/js/website.js index 671dfc14ee9..31e39b3bbc4 100644 --- a/addons/website/static/src/js/website.js +++ b/addons/website/static/src/js/website.js @@ -2,9 +2,11 @@ "use strict"; var website = {}; - // The following line can be removed in 2017 openerp.website = website; + /* ---------------------------------------------------- + Helpers + ---------------------------------------------------- */ website.get_context = function (dict) { var html = document.documentElement; return _.extend({ @@ -34,26 +36,11 @@ } return parsedSearch; }; + website.parseHash = function () { return website.parseQS(window.location.hash.substring(1)); }; - /* ----- TEMPLATE LOADING ---- */ - var templates_def = $.Deferred().resolve(); - website.add_template_file = function(template) { - templates_def = templates_def.then(function() { - var def = $.Deferred(); - openerp.qweb.add_template(template, function(err) { - if (err) { - def.reject(err); - } else { - def.resolve(); - } - }); - return def; - }); - }; - website.add_template_file('/website/static/src/xml/website.xml'); website.reload = function () { location.hash = "scrollTop=" + window.document.body.scrollTop; if (location.search.indexOf("enable_editor") > -1) { @@ -63,100 +50,9 @@ } }; - var all_ready = null; - var dom_ready = website.dom_ready = $.Deferred(); - $(document).ready(function () { - website.is_editable = website.is_editable || $('html').data('editable'); - website.is_editable_button= website.is_editable_button || $('html').data('editable'); - dom_ready.resolve(); - }); - - website.init_kanban = function ($kanban) { - $('.js_kanban_col', $kanban).each(function () { - var $col = $(this); - var $pagination = $('.pagination', $col); - if(!$pagination.size()) { - return; - } - - var page_count = $col.data('page_count'); - var scope = $pagination.last().find("li").size()-2; - var kanban_url_col = $pagination.find("li a:first").attr("href").replace(/[0-9]+$/, ''); - - var data = { - 'domain': $col.data('domain'), - 'model': $col.data('model'), - 'template': $col.data('template'), - 'step': $col.data('step'), - 'orderby': $col.data('orderby') - }; - - $pagination.on('click', 'a', function (ev) { - ev.preventDefault(); - var $a = $(ev.target); - if($a.parent().hasClass('active')) { - return; - } - - var page = +$a.attr("href").split(",").pop().split('-')[1]; - data['page'] = page; - - $.post('/website/kanban/', data, function (col) { - $col.find("> .thumbnail").remove(); - $pagination.last().before(col); - }); - - var page_start = page - parseInt(Math.floor((scope-1)/2), 10); - if (page_start < 1 ) page_start = 1; - var page_end = page_start + (scope-1); - if (page_end > page_count ) page_end = page_count; - - if (page_end - page_start < scope) { - page_start = page_end - scope > 0 ? page_end - scope : 1; - } - - $pagination.find('li.prev a').attr("href", kanban_url_col+(page-1 > 0 ? page-1 : 1)); - $pagination.find('li.next a').attr("href", kanban_url_col+(page < page_end ? page+1 : page_end)); - for(var i=0; i < scope; i++) { - $pagination.find('li:not(.prev):not(.next):eq('+i+') a').attr("href", kanban_url_col+(page_start+i)).html(page_start+i); - } - $pagination.find('li.active').removeClass('active'); - $pagination.find('li:has(a[href="'+kanban_url_col+page+'"])').addClass('active'); - - }); - - }); - }; - - /** - * Returns a deferred resolved when the templates are loaded - * and the Widgets can be instanciated. - */ - website.ready = function() { - if (!all_ready) { - all_ready = dom_ready.then(function () { - return templates_def; - }).then(function () { - if (website.is_editable) { - website.id = $('html').data('website-id'); - website.session = new openerp.Session(); - var modules = ['website']; - return openerp._t.database.load_translations(website.session, modules, website.get_context().lang); - } - }).promise(); - } - return all_ready; - }; - - website.error = function(data, url) { - var $error = $(openerp.qweb.render('website.error_dialog', { - 'title': data.data ? data.data.arguments[0] : "", - 'message': data.data ? data.data.arguments[1] : data.statusText, - 'backend_url': url - })); - $error.appendTo("body"); - $error.modal('show'); - }; + /* ---------------------------------------------------- + Widgets + ---------------------------------------------------- */ website.prompt = function (options) { /** @@ -229,11 +125,13 @@ dialog.on('click', '.btn-primary', function () { def.resolve(field.val(), field, dialog); dialog.remove(); + $('.modal-backdrop').remove(); }); }); dialog.on('hidden.bs.modal', function () { def.reject(); dialog.remove(); + $('.modal-backdrop').remove(); }); if (field.is('input[type="text"], select')) { field.keypress(function (e) { @@ -246,6 +144,16 @@ return def; }; + website.error = function(data, url) { + var $error = $(openerp.qweb.render('website.error_dialog', { + 'title': data.data ? data.data.arguments[0] : "", + 'message': data.data ? data.data.arguments[1] : data.statusText, + 'backend_url': url + })); + $error.appendTo("body"); + $error.modal('show'); + }; + website.form = function (url, method, params) { var form = document.createElement('form'); form.setAttribute('action', url); @@ -261,11 +169,117 @@ form.submit(); }; - dom_ready.then(function () { + website.init_kanban = function ($kanban) { + $('.js_kanban_col', $kanban).each(function () { + var $col = $(this); + var $pagination = $('.pagination', $col); + if(!$pagination.size()) { + return; + } + + var page_count = $col.data('page_count'); + var scope = $pagination.last().find("li").size()-2; + var kanban_url_col = $pagination.find("li a:first").attr("href").replace(/[0-9]+$/, ''); + + var data = { + 'domain': $col.data('domain'), + 'model': $col.data('model'), + 'template': $col.data('template'), + 'step': $col.data('step'), + 'orderby': $col.data('orderby') + }; + + $pagination.on('click', 'a', function (ev) { + ev.preventDefault(); + var $a = $(ev.target); + if($a.parent().hasClass('active')) { + return; + } + + var page = +$a.attr("href").split(",").pop().split('-')[1]; + data['page'] = page; + + $.post('/website/kanban', data, function (col) { + $col.find("> .thumbnail").remove(); + $pagination.last().before(col); + }); + + var page_start = page - parseInt(Math.floor((scope-1)/2), 10); + if (page_start < 1 ) page_start = 1; + var page_end = page_start + (scope-1); + if (page_end > page_count ) page_end = page_count; + + if (page_end - page_start < scope) { + page_start = page_end - scope > 0 ? page_end - scope : 1; + } + + $pagination.find('li.prev a').attr("href", kanban_url_col+(page-1 > 0 ? page-1 : 1)); + $pagination.find('li.next a').attr("href", kanban_url_col+(page < page_end ? page+1 : page_end)); + for(var i=0; i < scope; i++) { + $pagination.find('li:not(.prev):not(.next):eq('+i+') a').attr("href", kanban_url_col+(page_start+i)).html(page_start+i); + } + $pagination.find('li.active').removeClass('active'); + $pagination.find('li:has(a[href="'+kanban_url_col+page+'"])').addClass('active'); + + }); + + }); + }; + + /* ---------------------------------------------------- + Async Ready and Template loading + ---------------------------------------------------- */ + var templates_def = $.Deferred().resolve(); + website.add_template_file = function(template) { + templates_def = templates_def.then(function() { + var def = $.Deferred(); + openerp.qweb.add_template(template, function(err) { + if (err) { + def.reject(err); + } else { + def.resolve(); + } + }); + return def; + }); + }; + website.add_template_file('/website/static/src/xml/website.xml'); + + website.dom_ready = $.Deferred(); + $(document).ready(function () { + website.is_editable = website.is_editable || $('html').data('editable'); + website.is_editable_button= website.is_editable_button || $('html').data('editable'); + website.dom_ready.resolve(); + // fix for ie + if($.fn.placeholder) $('input, textarea').placeholder(); + }); + + var all_ready = null; + /** + * Returns a deferred resolved when the templates are loaded + * and the Widgets can be instanciated. + */ + website.ready = function() { + if (!all_ready) { + all_ready = website.dom_ready.then(function () { + return templates_def; + }).then(function () { + if (website.is_editable) { + website.id = $('html').data('website-id'); + website.session = new openerp.Session(); + var modules = ['website']; + return openerp._t.database.load_translations(website.session, modules, website.get_context().lang); + } + }).promise(); + } + return all_ready; + }; - /* ----- BOOTSTRAP STUFF ---- */ - // $('.js_tooltip').bstooltip(); + website.inject_tour = function() { + // if a tour is active inject tour js + } + website.dom_ready.then(function () { /* ----- PUBLISHING STUFF ---- */ $(document).on('click', '.js_publish_management .js_publish_btn', function () { var $data = $(this).parents(".js_publish_management:first"); diff --git a/addons/website/static/src/js/website.mobile.js b/addons/website/static/src/js/website.mobile.js index 4acf2d78db8..88a558f5094 100644 --- a/addons/website/static/src/js/website.mobile.js +++ b/addons/website/static/src/js/website.mobile.js @@ -17,7 +17,10 @@ 'hidden.bs.modal': 'destroy' }, start: function () { - document.getElementById("mobile-viewport").src = window.location.origin + window.location.pathname + "#mobile-preview"; + 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.length ? '/' : '') + window.location.pathname + "#mobile-preview"; this.$el.modal(); }, destroy: function () { diff --git a/addons/website/static/src/js/website.snippets.animation.js b/addons/website/static/src/js/website.snippets.animation.js index bc2ea064731..c72e2c3b84e 100644 --- a/addons/website/static/src/js/website.snippets.animation.js +++ b/addons/website/static/src/js/website.snippets.animation.js @@ -5,20 +5,26 @@ if (!website.snippet) website.snippet = {}; website.snippet.readyAnimation = []; - website.snippet.start_animation = function () { + website.snippet.start_animation = function (editable_mode, $target) { for (var k in website.snippet.animationRegistry) { var Animation = website.snippet.animationRegistry[k]; - var selector = "[data-snippet-id='"+k+"']"; + var selector = ""; if (Animation.prototype.selector) { - selector += ", " + Animation.prototype.selector; + if (selector != "") selector += ", " + selector += Animation.prototype.selector; } + if ($target) { + if ($target.is(selector)) selector = $target; + else continue; + } + $(selector).each(function() { var $snipped_id = $(this); if ( !$snipped_id.parents("#oe_snippets").length && !$snipped_id.parent("body").length && !$snipped_id.data("snippet-view")) { website.snippet.readyAnimation.push($snipped_id); - $snipped_id.data("snippet-view", new Animation($snipped_id)); + $snipped_id.data("snippet-view", new Animation($snipped_id, editable_mode)); } }); } @@ -28,11 +34,12 @@ var $snipped_id = $(this); if ($snipped_id.data("snippet-view")) { $snipped_id.data("snippet-view").stop(); - $snipped_id.data("snippet-view", false); } }); }; - $(document).ready(website.snippet.start_animation); + $(document).ready(function () { + website.snippet.start_animation(); + }); website.snippet.animationRegistry = {}; @@ -41,9 +48,9 @@ $: function () { return this.$el.find.apply(this.$el, arguments); }, - init: function (dom) { + init: function (dom, editable_mode) { this.$el = this.$target = $(dom); - this.start(); + this.start(editable_mode); }, /* * start @@ -64,10 +71,14 @@ 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({ - parallax: ".parallax", + selector: ".parallax", start: function () { var self = this; setTimeout(function () {self.set_values();}); @@ -104,7 +115,7 @@ img.onload = function () { var offset = 0; var padding = parseInt($(document.body).css("padding-top")); - if (speed < 1) { + if (speed > 1) { 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; @@ -119,4 +130,28 @@ } }); + website.snippet.animationRegistry.share = website.snippet.Animation.extend({ + selector: ".oe_share", + start: function () { + var url = encodeURIComponent(window.location.href); + var title = encodeURIComponent($("title").text()); + this.$target.find("a").each(function () { + var $a = $(this); + $a.attr("href", $(this).attr("href").replace("{url}", url).replace("{title}", title)); + if ($a.attr("target") && $a.attr("target").match(/_blank/i)) { + $a.click(function () { + window.open(this.href,'','menubar=no,toolbar=no,resizable=yes,scrollbars=yes,height=550,width=600'); + return false; + }); + } + }); + }, + }); + + website.snippet.animationRegistry.media_video = website.snippet.Animation.extend({ + selector: ".media_iframe_video", + start: function () { + this.$target.html('<div class="css_editable_mode_display"> </div><iframe src="'+this.$target.data("src")+'" frameborder="0" allowfullscreen="allowfullscreen"></iframe>'); + }, + }); })(); diff --git a/addons/website/static/src/js/website.snippets.editor.js b/addons/website/static/src/js/website.snippets.editor.js index 23c5956ebf4..d8eee3cbec5 100644 --- a/addons/website/static/src/js/website.snippets.editor.js +++ b/addons/website/static/src/js/website.snippets.editor.js @@ -1,6 +1,136 @@ (function () { 'use strict'; +/* Building block / Snippet Editor + + The building blocks appear in the edit bar website. These prebuilt html block + allowing the designer to easily generate content on a page (drag and drop). + Options allow snippets to add customizations part html code according to their + selector (jQuery) and javascript object. + + How to create content? + + Designers can add their own html block in the "snippets" (/website/views/snippets.xml). + The block must be added in one of four menus (structure, content, feature or effect). + Structure: + <div> + <div class="oe_snippet_thumbnail"> + <img class="oe_snippet_thumbnail_img" src="...image src..."/> + <span class="oe_snippet_thumbnail_title">...Block Name...</span> + </div> + <div class="oe_snippet_body"> + ... + <!-- + The block with class 'oe_snippet_body' is inserted in the page. + This class is removed when the block is dropped. + The block can be made of any html tag and content. --> + </div> + </div> + + How to create options? + + Designers can add their own html block in the "snippet_options" (/website/views/snippets.xml). + Structure: + + <div data-snippet-option-id='...' <!-- Required: javascript object id (but javascript + for this option object is not required) --> + data-selector="..." <!-- Required: jQuery selector. + Apply options on all The part of html who + match with this jQuery selector. + E.g.: If the selector is div, all div will be selected + and can be highlighted and assigned an editor. --> + data-selector-siblings="..." <!-- Optional: jQuery selector. + The html part can be insert or move beside + the selected html block --> + data-selector-children="..." <!-- Optional: jQuery selector. + The html part can be insert or move inside + the selected html block --> + data-selector-vertical-children='...'> <!-- Optional: jQuery selector. + The html part can be insert or move inside + the selected html block. The drop zone is + displayed vertically --> + ... + <li><a href="#">...</a></li> <!-- Optional: html li list. + List of menu items displayed in customize + menu. If the li tag have 'data-class', the + class is automaticcally added or removed to + the html content when the user select this item. --> + ... + <li class="dropdown-submenu" <!-- Optional: html li list exemple. !--> + data-required="true"> <!-- Optional: if only one item can be selected + and can't be unselect. !--> + <a tabindex="-1" href="#">...</a> <!-- bootstrap dropdown button !--> + <ul class="dropdown-menu"> + <li data-value="text_only"><a>...</a></li> <!-- by default data-value is apply + like a class to html block !--> + </ul> + </li> + </div> + + How to create a javascript object for an options? + + openerp.website.snippet.options["...option-id..."] = website.snippet.Option.extend({ + // start is called when the user click into a block or when the user drop a block + // into the page (just after the init method). + // start is usually used to bind event. + // + // this.$target: block html inserted inside the page + // this.$el: html li list of this options + // this.$overlay: html editor overlay who content resize bar, customize menu... + start: function () {}, + + + // onFocus is called when the user click inside the block inserted in page + // and when the user drop on block into the page + onFocus : function () {}, + + + // onBlur is called when the user click outside the block inserted in page, if + // the block is focused + onBlur : function () {}, + + + // on_clone is called when the snippet is duplicate + // @variables: $clone is allready inserted is the page + on_clone: function ($clone) {}, + + + // on_remove is called when the snippet is removed (dom is removing after this tigger) + on_remove: function () {}, + + + // drop_and_build_snippet is called just after that a thumbnail is drag and dropped + // into a drop zone. The content is already inserted in the page. + drop_and_build_snippet: function () {}, + + // select is called when a user select an item in the li list of options + // By default, if the li item have a data-value attribute, the data-vlue it's apply + // like a class to the html block (this.$target) + // @variables: next_previous = {$next, $prev} + // $next = next item selected or false + // $prev = previous item selected or false + select: function (event, next_previous) {} + + // preview is called when a user is on mouse over or mouse out of an item + // variables: next_previous = {$next, $prev} + // $next = next item selected or false + // $prev = previous item selected or false + preview: function (event, next_previous) {} + + // clean_for_save + // clean_for_save is called just before to save the vue + // Sometime it's important to remove or add some datas (contentEditable, added + // classes to a running animation...) + clean_for_save: function () {} + }); + + + // 'snippet-dropped' is triggered on '#oe_snippets' whith $target as attribute when a snippet is dropped + // 'snippet-activated' is triggered on '#oe_snippets' (and on snippet) when a snippet is activated + +*/ + + var website = openerp.website; website.add_template_file('/website/static/src/xml/website.snippets.xml'); @@ -18,25 +148,20 @@ }, edit: function () { var self = this; - $("body").off('click'); + $("[data-oe-model] *, [data-oe-type=html] *").off('click'); window.snippets = this.snippets = new website.snippet.BuildingBlock(this); this.snippets.appendTo(this.$el); - + website.snippet.stop_animation(); this.on('rte:ready', this, function () { self.snippets.$button.removeClass("hidden"); - website.snippet.stop_animation(); website.snippet.start_animation(); + $("#wrapwrap *").off('mousedown mouseup click'); }); return this._super.apply(this, arguments); }, save: function () { - this.snippets.make_active(false); - - // FIXME: call clean_for_save on all snippets of the page, not only modified ones - // important for banner of parallax that changes data automatically. this.snippets.clean_for_save(); - remove_added_snippet_id(); this._super(); }, }); @@ -49,38 +174,11 @@ })) { return; } - hack_to_add_snippet_id() - }); - - // puts $el at the same absolute position as $target - function hack_to_add_snippet_id () { - _.each(website.snippet.selector, function (val) { - $(val[0]).each(function() { - 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"); - } - }); - }); - } - - $(document).ready(function() { - hack_to_add_snippet_id(); }); - // '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.templateOptions = {}; + website.snippet.globalSelector = ""; website.snippet.selector = []; website.snippet.BuildingBlock = openerp.Widget.extend({ template: 'website.snippets', @@ -92,7 +190,6 @@ $("<div id='oe_manipulators'></div>").appendTo('body'); } this.$active_snipped_id = false; - hack_to_add_snippet_id(); this.snippets = []; observer.observe(document.body, { @@ -159,17 +256,24 @@ .then(function (html) { var $html = $(html); - var $styles = $html.find("[data-snippet-style-id]"); + var selector = []; + var $styles = $html.find("[data-snippet-option-id]"); $styles.each(function () { var $style = $(this); - var style_id = $style.data('snippet-style-id'); - website.snippet.styles[style_id] = { - 'snippet-style-id' : style_id, + var style_id = $style.data('snippet-option-id'); + website.snippet.templateOptions[style_id] = { + 'snippet-option-id' : style_id, 'selector': $style.data('selector'), '$el': $style, + 'selector-siblings': $style.data('selector-siblings'), + 'selector-children': $style.data('selector-children'), + 'selector-vertical-children': $style.data('selector-vertical-children'), + 'data': $style.data() }; + selector.push($style.data('selector')); }); $styles.addClass("hidden"); + website.snippet.globalSelector = selector.join(","); self.$snippets = $html.find(".tab-content > div > div").addClass("oe_snippet"); self.$el.append($html); @@ -190,13 +294,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(".oe_handle.e,.oe_handle.w").css({'height': $target.outerHeight() + mt + mb+1}); + $el.find(".oe_handle.s").css({'top': $target.outerHeight() + mt + mb}); + $el.find(".oe_handle.size").css({'top': $target.outerHeight() + mt}); + $el.find(".oe_handle.s,.oe_handle.n").css({'width': $target.outerWidth()-2}); }, show: function () { this.$el.removeClass("hidden"); @@ -213,18 +318,25 @@ bind_snippet_click_editor: function () { var self = this; - var snipped_event_flag = false; + var snipped_event_flag; $("#wrapwrap").on('click', function (event) { - if (snipped_event_flag) { + if (snipped_event_flag || !event.srcElement) { return; } snipped_event_flag = true; + setTimeout(function () {snipped_event_flag = false;}, 0); var $target = $(event.srcElement); - if (!$target.attr("data-snippet-id")) { - $target = $target.parents("[data-snippet-id]:first"); + + if ($target.parents(".oe_overlay").length) { + return; + } + + if (!$target.is(website.snippet.globalSelector)) { + $target = $target.parents(website.snippet.globalSelector).first(); } - if (!$target.attr("data-oe-model") && !$target.parents("[data-oe-model]:first").length) { + + if (!self.dom_filter($target).length) { $target = false; } if (self.$active_snipped_id && self.$active_snipped_id.is($target)) { @@ -248,13 +360,16 @@ } }, clean_for_save: function () { - for (var k in this.snippets) { - if (!this.snippets.hasOwnProperty(k)) { continue; } - var editor = $(this.snippets[k]).data("snippet-editor"); + var self = this; + $(website.snippet.globalSelector).each(function () { + var $snippet = $(this); + self.make_active($snippet); + self.make_active(false); + var editor = $snippet.data("snippet-editor"); if (editor) { editor.clean_for_save(); } - } + }); }, make_active: function ($snippet) { if ($snippet && this.$active_snipped_id && this.$active_snipped_id.get(0) === $snippet.get(0)) { @@ -281,8 +396,7 @@ if (typeof $snippet.data("snippet-editor") === 'undefined') { var $targets = this.activate_overlay_zones($snippet); if (!$targets.length) return; - var editor = website.snippet.editorRegistry[$snippet.data("snippet-id")] || website.snippet.editorRegistry.resize; - $snippet.data("snippet-editor", new editor(this, $snippet)); + $snippet.data("snippet-editor", new website.snippet.Editor(this, $snippet)); } this.cover_target($snippet.data('overlay'), $snippet); }, @@ -318,36 +432,47 @@ start: function(){ self.hide(); dropped = false; + // snippet_selectors => to get selector-siblings, selector-children, selector-vertical-children $snippet = $(this); - snipped_id = $snippet.data('snippet-id'); + $toInsert = $snippet.find('.oe_snippet_body').clone(); + $toInsert.removeClass('oe_snippet_body'); + + var selector = []; + var selector_siblings = []; + var selector_children = []; + var selector_vertical_children = []; + for (var k in website.snippet.templateOptions) { + if ($toInsert.is(website.snippet.templateOptions[k].selector)) { + selector.push(website.snippet.templateOptions[k].selector); + if (website.snippet.templateOptions[k]['selector-siblings']) + selector_siblings.push(website.snippet.templateOptions[k]['selector-siblings']); + if (website.snippet.templateOptions[k]['selector-children']) + selector_children.push(website.snippet.templateOptions[k]['selector-children']); + if (website.snippet.templateOptions[k]['selector-vertical-children']) + selector_vertical_children.push(website.snippet.templateOptions[k]['selector-vertical-children']); + } + } + 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')) { + if (!selector_siblings.length && !selector_children.length && !selector_vertical_children.length) { console.debug($snippet.data("snippet-id") + " have oe_snippet_body class and have not for insert action"+ "data-selector-siblings, data-selector-children or data-selector-vertical-children tag for mutate action"); return; } self.activate_insertion_zones({ - siblings: $snippet.data('selector-siblings'), - children: $snippet.data('selector-children'), - vertical_children: $snippet.data('selector-vertical-children') + siblings: selector_siblings.join(","), + children: selector_children.join(","), + vertical_children: selector_vertical_children.join(","), }); - $toInsert = $snippet.find('.oe_snippet_body').clone(); - $toInsert.removeClass('oe_snippet_body'); - $toInsert.data('src-snippet-id', snipped_id); - if (!$toInsert.data('snippet-id')) { - $toInsert.attr('data-snippet-id', snipped_id); - } else { - snipped_id = $toInsert.data('snippet-id'); - } - } else if( action === 'mutate' ){ if (!$snippet.data('selector')) { 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')); + var $targets = self.activate_overlay_zones(selector_children.join(",")); $targets.each(function(){ var $clone = $(this).data('overlay').clone(); $clone.addClass("oe_drop_zone").data('target', $(this)); @@ -382,38 +507,29 @@ } $('.oe_drop_zone').droppable('destroy').remove(); + if (dropped) { var $target = false; - if(action === 'insert'){ - $target = $toInsert; + $target = $toInsert; - website.snippet.start_animation(); + setTimeout(function () { + $("#oe_snippets").trigger('snippet-dropped', $target); + website.snippet.start_animation(true, $target); + // drop_and_build_snippet self.create_overlay($target); if ($target.data("snippet-editor")) { - $target.data("snippet-editor").drop_and_build_snippet($target); + $target.data("snippet-editor").drop_and_build_snippet(); } - - $target.find("[data-snippet-id]").each(function () { - var $snippet = $(this); - var snippet_id = $snippet.data("data-snippet-id"); - self.create_overlay($snippet); - if ($snippet.data("snippet-editor")) { - $snippet.data("snippet-editor").drop_and_build_snippet($snippet); - } - }); - - } else { - $target = $(this).data('target'); - - self.create_overlay($target); - if (website.snippet.editorRegistry[snipped_id]) { - var snippet = new website.snippet.editorRegistry[snipped_id](self, $target); - snippet.drop_and_build_snippet($target); + for (var k in website.snippet.templateOptions) { + $target.find(website.snippet.templateOptions[k].selector).each(function () { + var $snippet = $(this); + self.create_overlay($snippet); + if ($snippet.data("snippet-editor")) { + $snippet.data("snippet-editor").drop_and_build_snippet(); + } + }); } - } - setTimeout(function () { - $("#oe_snippets").trigger('snippet-dropped', $target); // reset snippet for rte $target.removeData("snippet-editor"); @@ -421,16 +537,15 @@ $target.data("overlay").remove(); $target.removeData("overlay"); } - self.create_overlay($target); - $target.find("[data-snippet-id]").each(function () { + $target.find(website.snippet.globalSelector).each(function () { var $snippet = $(this); $snippet.removeData("snippet-editor"); if ($snippet.data("overlay")) { $snippet.data("overlay").remove(); $snippet.removeData("overlay"); } - self.create_overlay($snippet); }); + self.create_overlay($target); // end self.make_active($target); @@ -550,7 +665,7 @@ // generate drop zones covering the elements selected by the selector // we generate overlay drop zones only to get an idea of where the snippet are, the drop activate_overlay_zones: function(selector){ - var $targets = this.dom_filter(selector || '[data-snippet-id]'); + var $targets = this.dom_filter(selector); var self = this; if (typeof selector !== 'string' && !$targets.length) { @@ -577,6 +692,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); @@ -595,23 +724,27 @@ }); - website.snippet.styleRegistry = {}; - website.snippet.StyleEditor = openerp.Class.extend({ + website.snippet.options = {}; + website.snippet.Option = openerp.Class.extend({ // initialisation (don't overwrite) - init: function (parent, $target, snippet_id) { - this.parent = parent; + init: function (BuildingBlock, editor, $target, snippet_id) { + this.BuildingBlock = BuildingBlock; + this.editor = editor; this.$target = $target; - var styles = this.$target.data("snippet-style-ids") || {}; + var styles = this.$target.data("snippet-option-ids") || {}; styles[snippet_id] = this; - this.$target.data("snippet-style-ids", styles); + this.$target.data("snippet-option-ids", styles); this.$overlay = this.$target.data('overlay'); - this['snippet-style-id'] = snippet_id; - this.$el = website.snippet.styles[snippet_id].$el.find(">li").clone(); + this['snippet-option-id'] = snippet_id; + var $option = website.snippet.templateOptions[snippet_id].$el; + this.$el = $option.find(">li").clone(); + this.data = $option.data(); this.required = this.$el.data("required"); this.set_active(); - this.$el.find('li[data-class] a').on('mouseover mouseout click', _.bind(this._mouse, this)); + this.$el.find('li[data-value] a').on('mouseenter mouseleave click', _.bind(this._mouse, this)); + this.$el.not(':not([data-value])').find("a").on('mouseenter mouseleave click', _.bind(this._mouse, this)); this.$target.on('snippet-style-reset', _.bind(this.set_active, this)); this.start(); @@ -619,7 +752,7 @@ _mouse: function (event) { var self = this; - if (event.type === 'mouseout') { + if (event.type === 'mouseleave') { if (!this.over) return; this.over = false; } else if (event.type === 'click') { @@ -629,11 +762,11 @@ } var $prev, $next; - if (event.type === 'mouseout') { + if (event.type === 'mouseleave') { $prev = $(event.currentTarget).parent(); - $next = this.$el.find("li[data-class].active"); + $next = this.$el.find("li[data-value].active"); } else { - $prev = this.$el.find("li[data-class].active"); + $prev = this.$el.find("li[data-value].active"); $next = $(event.currentTarget).parent(); } if (!$prev.length) { @@ -653,70 +786,77 @@ self.set_active(); self.$target.trigger("snippet-style-change", [self, np]); },0); - this.select(event, {'$next': $next, '$prev': $prev}); + this.select({'$next': $next, '$prev': $prev}); } else { setTimeout(function () { self.$target.trigger("snippet-style-preview", [self, np]); },0); - this.preview(event, np); + this.preview(np); } }, - // start is call just after the init + /* set_active + * select and set item active or not (add highlight item and his parents) + * called before start + */ + set_active: function () { + var self = this; + this.$el.find('li').removeClass("active"); + var $active = this.$el.find('li[data-value]') + .filter(function () { + var $li = $(this); + return ($li.data('value') && self.$target.hasClass($li.data('value'))); + }) + .first() + .addClass("active"); + this.$el.find('li:has(li[data-value].active)').addClass("active"); + }, + start: function () { }, - /* select - * called when a user select an item - * variables: np = {$next, $prev} - * $next is false if they are no next item selected - * $prev is false if they are no previous item selected - */ - select: function (event, np) { + + onFocus : function () { + }, + + onBlur : function () { + }, + + on_clone: function ($clone) { + }, + + on_remove: function () { + }, + + drop_and_build_snippet: function () { + }, + + select: function (np) { var self = this; // add or remove html class - if (np.$prev) { - this.$target.removeClass(np.$prev.data('class' || "")); + if (np.$prev && this.required) { + this.$target.removeClass(np.$prev.data('value' || "")); } if (np.$next) { - this.$target.addClass(np.$next.data('class') || ""); + this.$target.addClass(np.$next.data('value') || ""); } }, - /* preview - * called when a user is on mouse over or mouse out of an item - * variables: np = {$next, $prev} - * $next is false if they are no next item selected - * $prev is false if they are no previous item selected - */ - preview: function (event, np) { + + preview: function (np) { var self = this; // add or remove html class if (np.$prev) { - this.$target.removeClass(np.$prev.data('class') || ""); + this.$target.removeClass(np.$prev.data('value') || ""); } if (np.$next) { - this.$target.addClass(np.$next.data('class') || ""); + this.$target.addClass(np.$next.data('value') || ""); } }, - /* set_active - * select and set item active or not (add highlight item and his parents) - * called before start - */ - set_active: function () { - var self = this; - this.$el.find('li').removeClass("active"); - var $active = this.$el.find('li[data-class]') - .filter(function () { - var $li = $(this); - return ($li.data('class') && self.$target.hasClass($li.data('class'))); - }) - .first() - .addClass("active"); - this.$el.find('li:has(li[data-class].active)').addClass("active"); + + clean_for_save: function () { } }); - - website.snippet.styleRegistry.background = website.snippet.StyleEditor.extend({ + website.snippet.options.background = website.snippet.Option.extend({ _get_bg: function () { return this.$target.css("background-image").replace(/url\(['"]*|['"]*\)|^none$/g, ""); }, @@ -726,11 +866,11 @@ start: function () { this._super(); var src = this._get_bg(); - this.$el.find("li[data-class].active.oe_custom_bg").data("src", src); + this.$el.find("li[data-value].active.oe_custom_bg").data("src", src); }, - select: function(event, np) { + select: function(np) { var self = this; - this._super(event, np); + this._super(np); if (np.$next) { if (np.$next.hasClass("oe_custom_bg")) { var editor = new website.editor.ImageDialog(); @@ -742,7 +882,7 @@ }); editor.on('cancel', self, function () { if (!np.$prev || np.$prev.data("src") === "") { - self.$target.removeClass(np.$next.data("class")); + self.$target.removeClass(np.$next.data("value")); self.$target.trigger("snippet-style-change", [self, np]); } }); @@ -752,11 +892,11 @@ } } else { this._set_bg(false); - this.$target.removeClass(np.$prev.data("class")); + this.$target.removeClass(np.$prev.data("value")); } }, - preview: function (event, np) { - this._super(event, np); + preview: function (np) { + this._super(np); if (np.$next) { this._set_bg(np.$next.data("src")); } @@ -765,248 +905,216 @@ var self = this; var bg = self.$target.css("background-image"); this.$el.find('li').removeClass("active"); - var $active = this.$el.find('li[data-class]') + var $active = this.$el.find('li[data-value]') .filter(function () { var $li = $(this); return ($li.data('src') && bg.indexOf($li.data('src')) >= 0) || - (!$li.data('src') && self.$target.hasClass($li.data('class'))); + (!$li.data('src') && self.$target.hasClass($li.data('value'))); }) .first(); if (!$active.length) { $active = this.$target.css("background-image") !== 'none' ? - this.$el.find('li[data-class].oe_custom_bg') : - this.$el.find('li[data-class=""]'); + this.$el.find('li[data-value].oe_custom_bg') : + this.$el.find('li[data-value=""]'); } $active.addClass("active"); - this.$el.find('li:has(li[data-class].active)').addClass("active"); + this.$el.find('li:has(li[data-value].active)').addClass("active"); } }); - - website.snippet.editorRegistry = {}; - website.snippet.Editor = openerp.Class.extend({ - init: function (parent, dom) { - this.parent = parent; - this.$target = $(dom); - this.$overlay = this.$target.data('overlay'); - this.snippet_id = this.$target.data("snippet-id"); - this._readXMLData(); - this.load_style_options(); - this.get_parent_block(); - this.start(); + website.snippet.options.slider = website.snippet.Option.extend({ + unique_id: function () { + var id = 0; + $(".carousel").each(function () { + var cid = 1 + parseInt($(this).attr("id").replace(/[^0123456789]/g, ''),10); + if (id < cid) id = cid; + }); + return "myCarousel" + id; }, + drop_and_build_snippet: function() { + this.id = this.unique_id(); + this.$target.attr("id", this.id); + this.$target.find("[data-slide]").attr("data-target", "#" + this.id); + this.$target.find("[data-slide-to]").attr("data-target", "#" + this.id); - /* - * _readXMLData - * Read data XML and set value into: - * this.$el : - * all xml data - * this.$overlay : - * Dom hover the $target who content options - * this.$editor : - * content of .oe_snippet_options - * Displayed into the overlay options on focus - */ - _readXMLData: function() { - var self = this; - this.$el = this.parent.$snippets.filter(function () { return $(this).data("snippet-id") == self.snippet_id; }).clone(); - this.$editor = this.$el.find(".oe_snippet_options"); - var $options = this.$overlay.find(".oe_overlay_options"); - this.$editor.prependTo($options.find(".oe_options ul")); - if ($options.find(".oe_options ul li").length) { - $options.find(".oe_options").removeClass("hidden"); - } + this.rebind_event(); }, - - - // activate drag and drop for the snippets in the snippet toolbar - _drag_and_drop: function(){ - var self = this; - this.dropped = false; - this.$overlay.draggable({ - greedy: true, - appendTo: 'body', - cursor: "move", - handle: ".oe_snippet_move", - cursorAt: { - left: 18, - top: 14 - }, - helper: function() { - var $clone = $(this).clone().css({width: "24px", height: "24px", border: 0}); - $clone.find(".oe_overlay_options >:not(:contains(.oe_snippet_move)), .oe_handle").remove(); - $clone.find(":not(.glyphicon)").css({position: 'absolute', top: 0, left: 0}); - $clone.appendTo("body").removeClass("hidden"); - return $clone; - }, - start: _.bind(self._drag_and_drop_start, self), - stop: _.bind(self._drag_and_drop_stop, self) - }); + on_clone: function ($clone) { + var id = this.unique_id(); + $clone.attr("id", id); + $clone.find("[data-slide]").attr("href", "#" + id); + $clone.find("[data-slide-to]").attr("data-target", "#" + id); }, - _drag_and_drop_after_insert_dropzone: function (){}, - _drag_and_drop_active_drop_zone: function ($zones){ + // rebind event to active carousel on edit mode + rebind_event: function () { var self = this; - $zones.droppable({ - over: function(){ - $(".oe_drop_zone.hide").removeClass("hide"); - $(this).addClass("hide").first().after(self.$target); - self.dropped = true; - }, - out: function(){ - $(this).removeClass("hide"); - self.$target.detach(); - self.dropped = false; - }, - }); + this.$target.find('.carousel-indicators [data-slide-to]').off('click').on('click', function () { + self.$target.carousel(+$(this).data('slide-to')); }); + + this.$target.attr('contentEditable', 'false'); + this.$target.find('.oe_structure, .content>.row, [data-slide]').attr('contentEditable', 'true'); }, - _drag_and_drop_start: function (){ + clean_for_save: function () { + this._super(); + this.$target.find(".item").removeClass("next prev left right"); + if(!this.$target.find(".item.active").length) { + this.$target.find(".item:first").addClass("active"); + } + }, + start : function () { var self = this; - self.parent.hide(); - self.parent.editor_busy = true; - self.size = { - width: self.$target.width(), - height: self.$target.height() - }; - self.$target.after("<div class='oe_drop_clone' style='display: none;'/>"); - self.$target.detach(); - self.$overlay.addClass("hidden"); - - self.parent.activate_insertion_zones({ - siblings: self.$el ? self.$el.data('selector-siblings') : false, - children: self.$el ? self.$el.data('selector-children') : false, - vertical_children: self.$el ? self.$el.data('selector-vertical-children') : false, - }); + this._super(); + this.$target.carousel({interval: false}); + this.id = this.$target.attr("id"); + this.$inner = this.$target.find('.carousel-inner'); + this.$indicators = this.$target.find('.carousel-indicators'); - $("body").addClass('move-important'); + this.$el.find(".js_add").on('click', function () {self.on_add_slide(); return false;}); + this.$el.find(".js_remove").on('click', function () {self.on_remove_slide(); return false;}); - self._drag_and_drop_after_insert_dropzone(); - self._drag_and_drop_active_drop_zone($('.oe_drop_zone')); + this.$target.carousel('pause'); + this.rebind_event(); }, - _drag_and_drop_stop: function (){ + on_add_slide: function () { var self = this; - if (!self.dropped) { - $(".oe_drop_clone").after(self.$target); - } - self.$overlay.removeClass("hidden"); - $("body").removeClass('move-important'); - $('.oe_drop_zone').droppable('destroy').remove(); - $(".oe_drop_clone, .oe_drop_to_remove").remove(); - self.parent.editor_busy = false; - self.get_parent_block(); - setTimeout(function () {self.parent.create_overlay(self.$target);},0); - }, + var cycle = this.$inner.find('.item').length; + var $active = this.$inner.find('.item.active, .item.prev, .item.next').first(); + var index = $active.index(); + this.$target.find('.carousel-control, .carousel-indicators').removeClass("hidden"); + this.$indicators.append('<li data-target="#' + this.id + '" data-slide-to="' + cycle + '"></li>'); - load_style_options: function () { - var self = this; - var $styles = this.$overlay.find('.oe_options'); - var $ul = $styles.find('ul:first'); - _.each(website.snippet.styles, function (val) { - if (!self.parent.dom_filter(val.selector).is(self.$target)) { - return; - } - var Editor = website.snippet.styleRegistry[val['snippet-style-id']] || website.snippet.StyleEditor; - var editor = new Editor(self, self.$target, val['snippet-style-id']); - $ul.prepend(editor.$el.addClass("snippet-style-" + val['snippet-style-id'])); - }); + var $clone = this.$target.find(".item.active").clone(); - if ($ul.find("li").length) { - $styles.removeClass("hidden"); - } + // insert + $clone.removeClass('active').insertAfter($active); + setTimeout(function() { + self.$target.carousel().carousel(++index); + self.rebind_event(); + },0); + return $clone; }, - - get_parent_block: function () { + on_remove_slide: function () { + if (this.remove_process) { + return; + } var self = this; - var $button = this.$overlay.find('.oe_snippet_parent'); - var $parent = this.$target.parents("[data-snippet-id]:first"); - if ($parent.length) { - $button.removeClass("hidden"); - $button.off("click").on('click', function (event) { - event.preventDefault(); - setTimeout(function () { - self.parent.make_active($parent); - }, 0); + var new_index = 0; + var cycle = this.$inner.find('.item').length - 1; + var index = this.$inner.find('.item.active').index(); + + if (cycle > 0) { + this.remove_process = true; + var $el = this.$inner.find('.item.active'); + self.$target.on('slid.bs.carousel', function (event) { + $el.remove(); + self.$indicators.find("li:last").remove(); + self.$target.off('slid.bs.carousel'); + self.rebind_event(); + self.remove_process = false; + if (cycle == 1) { + self.on_remove_slide(event); + } }); + setTimeout(function () { + self.$target.carousel( index > 0 ? --index : cycle ); + }, 500); } else { - $button.addClass("hidden"); + this.$target.find('.carousel-control, .carousel-indicators').addClass("hidden"); } }, - - /* - * start - * This method is called after init and _readXMLData - */ - start: function () { - var self = this; - this.$overlay.on('click', '.oe_snippet_clone', _.bind(this.on_clone, this)); - this.$overlay.on('click', '.oe_snippet_remove', _.bind(this.on_remove, this)); - this._drag_and_drop(); + }); + website.snippet.options.carousel = website.snippet.options.slider.extend({ + getSize: function () { + this.grid = this._super(); + this.grid.size = 8; + return this.grid; }, - - on_clone: function () { - var $clone = this.$target.clone(false); - this.$target.after($clone); - return false; + clean_for_save: function () { + this._super(); + this.$target.css("background-image", ""); + this.$target.removeClass(this._class); }, - - on_remove: function () { - this.onBlur(); - var index = _.indexOf(this.parent.snippets, this.$target.get(0)); - delete this.parent.snippets[index]; - this.$target.remove(); - this.$overlay.remove(); - return false; + load_style_options : function () { + this._super(); + $(".snippet-style-size li[data-value='']").remove(); }, + start : function () { + var self = this; + this._super(); - /* - * drop_and_build_snippet - * This method is called just after that a thumbnail is drag and dropped into a drop zone - * (after the insertion of this.$body, if this.$body exists) - */ - drop_and_build_snippet: function ($target) { - }, + // set background and prepare to clean for save + var add_class = function (c){ + if (c) self._class = (self._class || "").replace(new RegExp("[ ]+" + c.replace(" ", "|[ ]+")), '') + ' ' + c; + return self._class || ""; + }; + this.$target.on('snippet-style-change snippet-style-preview', function (event, style, np) { + var $active = self.$target.find(".item.active"); + if (style['snippet-option-id'] === "size") return; + if (style['snippet-option-id'] === "background") { + $active.css("background-image", self.$target.css("background-image")); + } + if (np.$prev) { + $active.removeClass(np.$prev.data("value")); + } + if (np.$next) { + $active.addClass(np.$next.data("value")); + add_class(np.$next.data("value")); + } + }); + this.$target.on('slid', function () { // slide.bs.carousel + var $active = self.$target.find(".item.active"); + self.$target + .css("background-image", $active.css("background-image")) + .removeClass(add_class($active.attr("class"))) + .addClass($active.attr("class")) + .trigger("snippet-style-reset"); - /* onFocus - * This method is called when the user click inside the snippet in the dom - */ - onFocus : function () { - this.$overlay.addClass('oe_active'); + self.$target.carousel("pause"); + }); + this.$target.trigger('slid'); }, + on_add_slide: function () { + var $clone = this._super(); - /* onFocus - * This method is called when the user click outside the snippet in the dom, after a focus - */ - onBlur : function () { - this.$overlay.removeClass('oe_active'); + // choose an other background + var bg = this.$target.data("snippet-option-ids").background; + if (!bg) return $clone; + + var $styles = bg.$el.find("li[data-value]:not(.oe_custom_bg)"); + var styles_index = $styles.index($styles.filter(".active")[0]); + $styles.removeClass("active"); + var $select = $($styles[styles_index >= $styles.length-1 ? 0 : styles_index+1]); + $select.addClass("active"); + $clone.css("background-image", $select.data("src") ? "url('"+ $select.data("src") +"')" : ""); + $clone.addClass($select.data("value") || ""); + + return $clone; }, + // rebind event to active carousel on edit mode + rebind_event: function () { + var self = this; + this.$target.find('.carousel-control').off('click').on('click', function () { + self.$target.carousel( $(this).data('slide')); }); - /* clean_for_save - * function called just before save vue - */ - clean_for_save: function () { - this.$target.removeAttr('contentEditable') - .find('*').removeAttr('contentEditable'); - this.$target.removeAttr('attributeEditable') - .find('*').removeAttr('attributeEditable'); + this.$target.find('.carousel-image, .carousel-inner .content > div').attr('contentEditable', 'true'); + this.$target.find('.carousel-image').attr('attributeEditable', 'true'); + this._super(); }, }); - - website.snippet.editorRegistry.resize = website.snippet.Editor.extend({ + website.snippet.options.marginAndResize = website.snippet.Option.extend({ start: function () { var self = this; this._super(); - var $box = $(openerp.qweb.render("website.snippets.resize")); var resize_values = this.getSize(); - if (!resize_values.n) $box.find(".oe_handle.n").remove(); - if (!resize_values.s) $box.find(".oe_handle.s").remove(); - if (!resize_values.e) $box.find(".oe_handle.e").remove(); - if (!resize_values.w) $box.find(".oe_handle.w").remove(); - if (!resize_values.size) $box.find(".oe_handle.size").remove(); - - this.$overlay.append($box.find(".oe_handles").html()); + if (resize_values.n) this.$overlay.find(".oe_handle.n").removeClass("readonly"); + if (resize_values.s) this.$overlay.find(".oe_handle.s").removeClass("readonly"); + if (resize_values.e) this.$overlay.find(".oe_handle.e").removeClass("readonly"); + if (resize_values.w) this.$overlay.find(".oe_handle.w").removeClass("readonly"); + if (resize_values.size) this.$overlay.find(".oe_handle.size").removeClass("readonly"); - this.$overlay.find(".oe_handle:not(:has(.oe_handle_button)), .oe_handle .oe_handle_button").on('mousedown', function (event){ + this.$overlay.find(".oe_handle:not(.size), .oe_handle.size .size").on('mousedown', function (event){ event.preventDefault(); var $handle = $(this); @@ -1057,7 +1165,7 @@ var regClass = new RegExp("\\s*" + resize[0][begin].replace(/[-]*[0-9]+/, '[-]*[0-9]+'), 'g'); } - self.parent.editor_busy = true; + self.BuildingBlock.editor_busy = true; var cursor = $handle.css("cursor")+'-important'; var $body = $(document.body); @@ -1070,8 +1178,9 @@ dy = dy - dy%resize; if (dy <= 0) dy = resize; self.$target.css("height", dy+"px"); + self.$target.css("overflow", "hidden"); self.on_resize(compass, null, dy); - self.parent.cover_target(self.$overlay, self.$target); + self.BuildingBlock.cover_target(self.$overlay, self.$target); return; } var dd = event['page'+XY] - xy + resize[1][begin]; @@ -1094,7 +1203,7 @@ if (change) { self.on_resize(compass, beginClass, current); - self.parent.cover_target(self.$overlay, self.$target); + self.BuildingBlock.cover_target(self.$overlay, self.$target); } }; @@ -1102,22 +1211,21 @@ $body.unbind('mousemove', body_mousemove); $body.unbind('mouseup', body_mouseup); $body.removeClass(cursor); - self.parent.editor_busy = false; + self.BuildingBlock.editor_busy = false; self.$target.removeClass("resize_editor_busy"); }; $body.mousemove(body_mousemove); $body.mouseup(body_mouseup); }); + this.$overlay.find(".oe_handle.size .auto_size").on('click', function (event){ + self.$target.css("height", ""); + self.$target.css("overflow", ""); + self.BuildingBlock.cover_target(self.$overlay, self.$target); + return false; + }); }, getSize: function () { - var grid = [0,4,8,16,32,48,64,92,128]; - this.grid = { - // list of class (Array), grid (Array), default value (INT) - n: [_.map(grid, function (v) {return 'mt'+v;}), grid], - s: [_.map(grid, function (v) {return 'mb'+v;}), grid], - // INT if the user can resize the snippet (resizing per INT px) - size: null - }; + this.grid = {}; return this.grid; }, @@ -1163,8 +1271,21 @@ this.change_cursor(); } }); - - website.snippet.editorRegistry.colmd = website.snippet.editorRegistry.resize.extend({ + website.snippet.options["margin-y"] = website.snippet.options.marginAndResize.extend({ + getSize: function () { + this.grid = this._super(); + var grid = [0,4,8,16,32,48,64,92,128]; + this.grid = { + // list of class (Array), grid (Array), default value (INT) + n: [_.map(grid, function (v) {return 'mt'+v;}), grid], + s: [_.map(grid, function (v) {return 'mb'+v;}), grid], + // INT if the user can resize the snippet (resizing per INT px) + size: null + }; + return this.grid; + }, + }); + website.snippet.options["margin-x"] = website.snippet.options.marginAndResize.extend({ getSize: function () { this.grid = this._super(); var width = this.$target.parents(".row:first").first().outerWidth(); @@ -1206,30 +1327,26 @@ this._super(); }, hide_remove_button: function() { - this.$overlay.find('.oe_snippet_remove').toggleClass("hidden", - !this.$target.siblings().length && this.$target.parents("[data-snippet-id]:first").find("[data-snippet-id='colmd']").length > 1); + this.$overlay.find('.oe_snippet_remove').toggleClass("hidden", !this.$target.siblings().length); }, onFocus : function () { this._super(); this.hide_remove_button(); }, - on_clone: function () { - var $clone = this.$target.clone(false); + on_clone: function ($clone) { var _class = $clone.attr("class").replace(/\s*(col-lg-offset-|col-md-offset-)([0-9-]+)/g, ''); - _class += ' col-md-1'; $clone.attr("class", _class); - this.$target.after($clone); this.hide_remove_button(); return false; }, on_remove: function () { if (!this.$target.siblings().length) { - var $parent = this.$target.parents("[data-snippet-id]:first"); - if($parent.find("[data-snippet-id='colmd']").length > 1) { + var $parent = this.$target.parents(".row:first"); + if($parent.find("[class*='col-md']").length > 1) { return false; } else { if (!$parent.data("snippet-editor")) { - this.parent.create_overlay($parent); + this.BuildingBlock.create_overlay($parent); } $parent.data("snippet-editor").on_remove(); } @@ -1263,169 +1380,15 @@ }, }); - website.snippet.editorRegistry.slider = website.snippet.editorRegistry.resize.extend({ - drop_and_build_snippet: function() { - var id = $(".carousel").length; - this.id = "myCarousel" + id; - this.$target.attr("id", this.id); - this.$target.find(".carousel-control").attr("href", "#myCarousel" + id); - this.$target.find("[data-target]").attr("data-target", "#myCarousel" + id); - - this.rebind_event(); - }, - // rebind event to active carousel on edit mode - rebind_event: function () { - var self = this; - this.$target.find('.carousel-indicators [data-target]').off('click').on('click', function () { - self.$target.carousel(+$(this).data('slide-to')); }); - - this.$target.attr('contentEditable', 'false'); - this.$target.find('.oe_structure, .content>.row').attr('contentEditable', 'true'); - }, - clean_for_save: function () { - this._super(); - this.$target.find(".item").removeClass("next prev left right"); - if(!this.$target.find(".item.active").length) { - this.$target.find(".item:first").addClass("active"); - } - }, - start : function () { - var self = this; - this._super(); - this.id = this.$target.attr("id"); - this.$inner = this.$target.find('.carousel-inner'); - this.$indicators = this.$target.find('.carousel-indicators'); - - this.$editor.find(".js_add").on('click', function () {self.on_add_slide(); return false;}); - this.$editor.find(".js_remove").on('click', function () {self.on_remove_slide(); return false;}); - - this.$target.carousel('pause'); - this.rebind_event(); - }, - on_add_slide: function () { - var self = this; - var cycle = this.$inner.find('.item').length; - var $active = this.$inner.find('.item.active, .item.prev, .item.next').first(); - var index = $active.index(); - this.$target.find('.carousel-control, .carousel-indicators').removeClass("hidden"); - this.$indicators.append('<li data-target="#' + this.id + '" data-slide-to="' + cycle + '"></li>'); - - var $clone = this.$el.find(".item.active").clone(); - - // insert - $clone.removeClass('active').insertAfter($active); - setTimeout(function() { - self.$target.carousel().carousel(++index); - self.rebind_event(); - },0); - return $clone; - }, - on_remove_slide: function () { - if (this.remove_process) { - return; - } - var self = this; - var new_index = 0; - var cycle = this.$inner.find('.item').length - 1; - var index = this.$inner.find('.item.active').index(); - - if (cycle > 0) { - this.remove_process = true; - var $el = this.$inner.find('.item.active'); - self.$target.on('slid.bs.carousel', function (event) { - $el.remove(); - self.$indicators.find("li:last").remove(); - self.$target.off('slid.bs.carousel'); - self.rebind_event(); - self.remove_process = false; - if (cycle == 1) { - self.on_remove_slide(event); - } - }); - setTimeout(function () { - self.$target.carousel( index > 0 ? --index : cycle ); - }, 500); - } else { - this.$target.find('.carousel-control, .carousel-indicators').addClass("hidden"); - } - }, - }); - - website.snippet.editorRegistry.carousel = website.snippet.editorRegistry.slider.extend({ + website.snippet.options["resize"] = website.snippet.options.marginAndResize.extend({ getSize: function () { this.grid = this._super(); this.grid.size = 8; return this.grid; }, - clean_for_save: function () { - this._super(); - this.$target.css("background-image", ""); - this.$target.removeClass(this._class); - }, - load_style_options : function () { - this._super(); - $(".snippet-style-size li[data-class='']").remove(); - }, - start : function () { - var self = this; - this._super(); - - // set background and prepare to clean for save - var add_class = function (c){ - if (c) self._class = (self._class || "").replace(new RegExp("[ ]+" + c.replace(" ", "|[ ]+")), '') + ' ' + c; - return self._class || ""; - }; - this.$target.on('snippet-style-change snippet-style-preview', function (event, style, np) { - var $active = self.$target.find(".item.active"); - if (style['snippet-style-id'] === "size") return; - if (style['snippet-style-id'] === "background") { - $active.css("background-image", self.$target.css("background-image")); - } - if (np.$prev) { - $active.removeClass(np.$prev.data("class")); - } - if (np.$next) { - $active.addClass(np.$next.data("class")); - add_class(np.$next.data("class")); - } - }); - this.$target.on('slid', function () { // slide.bs.carousel - var $active = self.$target.find(".item.active"); - self.$target - .css("background-image", $active.css("background-image")) - .removeClass(add_class($active.attr("class"))) - .addClass($active.attr("class")) - .trigger("snippet-style-reset"); - - self.$target.carousel(); - }); - this.$target.trigger('slid'); - }, - on_add_slide: function () { - var $clone = this._super(); - - // choose an other background - var $styles = this.$target.data("snippet-style-ids").background.$el.find("li[data-class]:not(.oe_custom_bg)"); - var styles_index = $styles.index($styles.filter(".active")[0]); - var $select = $($styles[styles_index >= $styles.length-1 ? 0 : styles_index+1]); - $clone.css("background-image", $select.data("src") ? "url('"+ $select.data("src") +"')" : ""); - $clone.addClass($select.data("class") || ""); - - return $clone; - }, - // rebind event to active carousel on edit mode - rebind_event: function () { - var self = this; - this.$target.find('.carousel-control').off('click').on('click', function () { - self.$target.carousel( $(this).data('slide')); }); - - this.$target.find('.carousel-inner .content > div').attr('contentEditable', 'true'); - this.$target.find('.carousel-image').attr('attributeEditable', 'true'); - this._super(); - }, }); - website.snippet.editorRegistry.parallax = website.snippet.editorRegistry.resize.extend({ + website.snippet.options.parallax = website.snippet.Option.extend({ getSize: function () { this.grid = this._super(); this.grid.size = 8; @@ -1437,16 +1400,22 @@ start : function () { var self = this; this._super(); + if (!self.$target.data("snippet-view")) { + this.$target.data("snippet-view", new website.snippet.animationRegistry.parallax(this.$target)); + } this.scroll(); this.$target.on('snippet-style-change snippet-style-preview', function () { 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; - var $ul = this.$editor.find('ul[name="parallax-scroll"]'); + var $ul = this.$el.find('ul[name="parallax-scroll"]'); var $li = $ul.find("li"); var speed = this.$target.data('scroll-background-ratio') || 0.6 ; $ul.find('[data-value="' + speed + '"]').addClass('active'); @@ -1468,13 +1437,330 @@ } }); - /* - * data-snippet-id automatically setted - * Don't need to add data-snippet-id="..." into the views - */ + website.snippet.options.transform = website.snippet.Option.extend({ + start: function () { + var self = this; + this._super(); + + this.$el.find(".clear-style").click(function (event) { + self.$target.removeClass("fa-spin").attr("style", ""); + self.resetTransfo(); + }); + + this.$el.find(".style").click(function (event) { + var settings = self.$target.data("transfo").settings; + self.$target.transfo({ hide: (settings.hide = !settings.hide) }); + }); + + this.$overlay.find('.oe_snippet_clone, .oe_handles').addClass('hidden'); - website.snippet.selector.push([".row > [class*='col-md-']", 'colmd']); - website.snippet.selector.push(['hr', 'hr']); - website.snippet.selector.push(['blockquote', 'quote']); + this.$overlay.find('[data-toggle="dropdown"]') + .on("mousedown", function () { + self.$target.transfo("hide"); + }); + }, + resetTransfo: function () { + var self = this; + this.$target.transfo("destroy"); + this.$target.transfo({ + hide: true, + callback: function () { + var pos = $(this).data("transfo").$center.offset(); + self.$overlay.css({ + 'top': pos.top, + 'left': pos.left, + 'position': 'absolute', + }); + self.$overlay.find(".oe_overlay_options").attr("style", "width:0; left:0!important; top:0;"); + self.$overlay.find(".oe_overlay_options > .btn-group").attr("style", "width:160px; left:-80px;"); + }}); + this.$target.data('transfo').$markup + .on("mouseover", function () { + self.$target.trigger("mouseover"); + }) + .mouseover(); + }, + onFocus : function () { + this.resetTransfo(); + }, + onBlur : function () { + this.$target.transfo("hide"); + }, + }); + + website.snippet.options.media = website.snippet.Option.extend({ + start: function () { + var self = this; + this._super(); + + website.snippet.start_animation(true, this.$target); + + $(document.body).on("media-saved", self, function (event, prev , item) { + self.editor.onBlur(); + self.BuildingBlock.make_active(false); + self.BuildingBlock.make_active($(item)); + }); + + this.$el.find(".edition").click(function (event) { + event.preventDefault(); + event.stopPropagation(); + self.element = new CKEDITOR.dom.element(self.$target[0]); + new website.editor.MediaDialog(self, self.element).appendTo(document.body); + }); + }, + onFocus : function () { + var self = this; + if (this.$target.parent().data("oe-field") === "image") { + this.$overlay.addClass("hidden"); + self.element = new CKEDITOR.dom.element(self.$target[0]); + new website.editor.MediaDialog(self, self.element).appendTo(document.body); + self.BuildingBlock.make_active(false); + } + }, + }); + + + website.snippet.Editor = openerp.Class.extend({ + init: function (BuildingBlock, dom) { + this.BuildingBlock = BuildingBlock; + this.$target = $(dom); + this.$overlay = this.$target.data('overlay'); + this.snippet_id = this.$target.data("snippet-id"); + this._readXMLData(); + this.load_style_options(); + this.get_parent_block(); + this.start(); + }, + + /* + * _readXMLData + * Read data XML and set value into: + * this.$el : + * all xml data + * this.$overlay : + * Dom hover the $target who content options + */ + _readXMLData: function() { + var self = this; + this.$el = this.BuildingBlock.$snippets.filter(function () { return $(this).data("snippet-id") == self.snippet_id; }).clone(); + var $options = this.$overlay.find(".oe_overlay_options"); + if ($options.find(".oe_options ul li").length) { + $options.find(".oe_options").removeClass("hidden"); + } + }, + + + // activate drag and drop for the snippets in the snippet toolbar + _drag_and_drop: function(){ + var self = this; + this.dropped = false; + this.$overlay.draggable({ + greedy: true, + appendTo: 'body', + cursor: "move", + handle: ".oe_snippet_move", + cursorAt: { + left: 18, + top: 14 + }, + helper: function() { + var $clone = $(this).clone().css({width: "24px", height: "24px", border: 0}); + $clone.find(".oe_overlay_options >:not(:contains(.oe_snippet_move)), .oe_handle").remove(); + $clone.find(":not(.glyphicon)").css({position: 'absolute', top: 0, left: 0}); + $clone.appendTo("body").removeClass("hidden"); + return $clone; + }, + start: _.bind(self._drag_and_drop_start, self), + stop: _.bind(self._drag_and_drop_stop, self) + }); + }, + _drag_and_drop_after_insert_dropzone: function (){}, + _drag_and_drop_active_drop_zone: function ($zones){ + var self = this; + $zones.droppable({ + over: function(){ + $(".oe_drop_zone.hide").removeClass("hide"); + $(this).addClass("hide").first().after(self.$target); + self.dropped = true; + }, + out: function(){ + $(this).removeClass("hide"); + self.$target.detach(); + self.dropped = false; + }, + }); + }, + _drag_and_drop_start: function (){ + var self = this; + self.BuildingBlock.hide(); + self.BuildingBlock.editor_busy = true; + self.size = { + width: self.$target.width(), + height: self.$target.height() + }; + self.$target.after("<div class='oe_drop_clone' style='display: none;'/>"); + self.$target.detach(); + self.$overlay.addClass("hidden"); + + self.BuildingBlock.activate_insertion_zones({ + siblings: self.selector_siblings, + children: self.selector_children, + vertical_children: self.selector_vertical_children, + }); + + $("body").addClass('move-important'); + + self._drag_and_drop_after_insert_dropzone(); + self._drag_and_drop_active_drop_zone($('.oe_drop_zone')); + }, + _drag_and_drop_stop: function (){ + var self = this; + if (!self.dropped) { + $(".oe_drop_clone").after(self.$target); + } + self.$overlay.removeClass("hidden"); + $("body").removeClass('move-important'); + $('.oe_drop_zone').droppable('destroy').remove(); + $(".oe_drop_clone, .oe_drop_to_remove").remove(); + self.BuildingBlock.editor_busy = false; + self.get_parent_block(); + setTimeout(function () {self.BuildingBlock.create_overlay(self.$target);},0); + }, + + load_style_options: function () { + var self = this; + var $styles = this.$overlay.find('.oe_options'); + var $ul = $styles.find('ul:first'); + this.styles = {}; + this.selector_siblings = []; + this.selector_children = []; + this.selector_vertical_children = []; + _.each(website.snippet.templateOptions, function (val) { + if (!self.$target.is(val.selector)) { + return; + } + if (val['selector-siblings']) self.selector_siblings.push(val['selector-siblings']); + if (val['selector-children']) self.selector_children.push(val['selector-children']); + if (val['selector-vertical-children']) self.selector_vertical_children.push(val['selector-vertical-children']); + + var style = val['snippet-option-id']; + var Editor = website.snippet.options[style] || website.snippet.Option; + var editor = self.styles[style] = new Editor(self.BuildingBlock, self, self.$target, style); + $ul.append(editor.$el.addClass("snippet-style-" + style)); + }); + this.selector_siblings = this.selector_siblings.join(","); + if (this.selector_siblings === "") + this.selector_siblings = false; + this.selector_children = this.selector_children.join(","); + if (this.selector_children === "") + this.selector_children = false; + this.selector_vertical_children = this.selector_vertical_children.join(","); + if (this.selector_vertical_children === "") + this.selector_vertical_children = false; + + if (!this.selector_siblings && !this.selector_children && !this.selector_vertical_children) { + this.$overlay.find(".oe_snippet_move").addClass('hidden'); + } + + + if ($ul.find("li").length) { + $styles.removeClass("hidden"); + } + this.$overlay.find('[data-toggle="dropdown"]').dropdown(); + }, + + get_parent_block: function () { + var self = this; + var $button = this.$overlay.find('.oe_snippet_parent'); + var $parent = this.$target.parents(website.snippet.globalSelector).first(); + if ($parent.length) { + $button.removeClass("hidden"); + $button.off("click").on('click', function (event) { + event.preventDefault(); + setTimeout(function () { + self.BuildingBlock.make_active($parent); + }, 0); + }); + } else { + $button.addClass("hidden"); + } + }, + + /* + * start + * This method is called after init and _readXMLData + */ + start: function () { + var self = this; + this.$overlay.on('click', '.oe_snippet_clone', _.bind(this.on_clone, this)); + this.$overlay.on('click', '.oe_snippet_remove', _.bind(this.on_remove, this)); + this._drag_and_drop(); + }, + + on_clone: function () { + var $clone = this.$target.clone(false); + this.$target.after($clone); + for (var i in this.styles){ + this.styles[i].on_clone($clone); + } + return false; + }, + + on_remove: function () { + this.onBlur(); + var index = _.indexOf(this.BuildingBlock.snippets, this.$target.get(0)); + for (var i in this.styles){ + this.styles[i].on_remove(); + } + delete this.BuildingBlock.snippets[index]; + this.$target.remove(); + this.$overlay.remove(); + return false; + }, + + /* + * drop_and_build_snippet + * This method is called just after that a thumbnail is drag and dropped into a drop zone + * (after the insertion of this.$body, if this.$body exists) + */ + drop_and_build_snippet: function () { + for (var i in this.styles){ + this.styles[i].drop_and_build_snippet(); + } + }, + + /* onFocus + * This method is called when the user click inside the snippet in the dom + */ + onFocus : function () { + this.$overlay.addClass('oe_active'); + for (var i in this.styles){ + this.styles[i].onFocus(); + } + }, + + /* onFocus + * This method is called when the user click outside the snippet in the dom, after a focus + */ + onBlur : function () { + for (var i in this.styles){ + this.styles[i].onBlur(); + } + this.$overlay.removeClass('oe_active'); + }, + + /* clean_for_save + * function called just before save vue + */ + clean_for_save: function () { + for (var i in this.styles){ + this.styles[i].clean_for_save(); + } + this.$target.removeAttr('contentEditable') + .find('*').removeAttr('contentEditable'); + this.$target.removeAttr('attributeEditable') + .find('*').removeAttr('attributeEditable'); + }, + }); })(); diff --git a/addons/website/static/src/js/website.tour.banner.js b/addons/website/static/src/js/website.tour.banner.js index 1d4a0c0f752..63ef120d9a1 100644 --- a/addons/website/static/src/js/website.tour.banner.js +++ b/addons/website/static/src/js/website.tour.banner.js @@ -35,11 +35,11 @@ element: 'button[data-action=snippet]', placement: 'bottom', title: _t("Insert building blocks"), - content: _t("To add content in a page, you can insert building blocks."), + content: _t("Click here to insert blocks of content in the page."), popover: { fixed: true }, }, { - snippet: 'carousel', + snippet: '#snippet_structure .oe_snippet:first', placement: 'bottom', title: _t("Drag & Drop a Banner"), content: _t("Drag the Banner block and drop it in your page."), @@ -47,14 +47,14 @@ }, { waitFor: '.oe_overlay_options .oe_options:visible', - element: '#wrap [data-snippet-id=carousel]:first .carousel-caption', + element: '#wrap .carousel:first .carousel-caption > div', placement: 'top', - title: _("Customize banner's text"), - content: _("Click in the text and start editing it."), + title: _t("Customize banner's text"), + content: _t("Click in the text and start editing it."), popover: { next: _t("Continue") }, }, { - element: '.oe_overlay_options .oe_options', + element: '.oe_overlay_options .oe_options:visible', placement: 'left', title: _t("Customize the banner"), content: _t("Customize any block through this menu. Try to change the background of the banner."), @@ -69,7 +69,7 @@ popover: { fixed: true }, }, { - snippet: 'features', + snippet: '#snippet_structure .oe_snippet:eq(6)', placement: 'bottom', title: _t("Drag & Drop This Block"), content: _t("Drag the <em>'Features'</em> block and drop it below the banner."), @@ -85,8 +85,8 @@ }, { waitFor: 'button[data-action=edit]:visible', - title: _("Good Job!"), - content: _("Well done, you created your homepage."), + title: _t("Good Job!"), + content: _t("Well done, you created your homepage."), popover: { next: _t("Continue") }, }, { @@ -101,13 +101,13 @@ element: '.modal:has(#mobile-viewport) button[data-dismiss=modal]', placement: 'right', title: _t("Check Mobile Preview"), - content: _t("Scroll in the mobile preview to test the rendering. Once it's ok, close this dialog."), + content: _t("Scroll to check rendering and then close the mobile preview."), popover: { next: _t("Continue") }, }, { waitNot: '.modal', element: '#content-menu-button', - placement: 'bottom', + placement: 'left', 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") }, diff --git a/addons/website/static/src/js/website.tour.js b/addons/website/static/src/js/website.tour.js index 2416cb511f1..b7e55fe166b 100644 --- a/addons/website/static/src/js/website.tour.js +++ b/addons/website/static/src/js/website.tour.js @@ -1,32 +1,65 @@ (function () { 'use strict'; -var website = openerp.website; +// raise an error in test mode if openerp don't exist +if (typeof openerp === "undefined") { + var error = "openerp is undefined" + + "\nhref: " + window.location.href + + "\nreferrer: " + document.referrer + + "\nlocalStorage: " + JSON.stringify(window.localStorage); + if (typeof $ !== "undefined") { + error += '\n\n' + $("body").html(); + } + throw new Error(error); +} -if (typeof QWeb2 !== "undefined") -website.add_template_file('/website/static/src/xml/website.tour.xml'); +var website = window.openerp.website; -if (website.EditorBar) -website.EditorBar.include({ - tours: [], - start: function () { - var self = this; - var menu = $('#help-menu'); - _.each(this.tours, function (tour) { - var $menuItem = $($.parseHTML('<li><a href="#">'+tour.name+'</a></li>')); - $menuItem.click(function () { - tour.reset(); - tour.run(); - }); - menu.append($menuItem); - }); - return this._super(); - }, - registerTour: function (tour) { - website.Tour.add(tour); - this.tours.push(tour); +// don't rewrite website.Tour in test mode +if (typeof website.Tour !== "undefined") { + return; +} + +// don't need template to use bootstrap Tour in automatic mode +if (typeof QWeb2 !== "undefined") { + website.add_template_file('/website/static/src/xml/website.tour.xml'); + +} + +// don't need to use bootstrap Tour to launch an automatic tour +function bootstrap_tour_stub () { + if (typeof Tour === "undefined") { + window.Tour = function Tour() {}; + Tour.prototype.addSteps = function () {}; + Tour.prototype.end = function () {}; + Tour.prototype.goto = function () {}; } -}); +} + + + +if (website.EditorBar) { + website.EditorBar.include({ + tours: [], + start: function () { + var self = this; + var menu = $('#help-menu'); + _.each(this.tours, function (tour) { + var $menuItem = $($.parseHTML('<li><a href="#">'+tour.name+'</a></li>')); + $menuItem.click(function () { + tour.reset(); + tour.run(); + }); + menu.append($menuItem); + }); + return this._super(); + }, + registerTour: function (tour) { + website.Tour.add(tour); + this.tours.push(tour); + } + }); +} ///////////////////////////////////////////////// @@ -175,6 +208,9 @@ website.Tour = openerp.Class.extend({ }, registerTour: function () { + if (this.automatic) { + bootstrap_tour_stub(); + } this.tour = new Tour({ name: this.id, storage: this.tourStorage, @@ -205,7 +241,7 @@ website.Tour = openerp.Class.extend({ if (!step.element) step.orphan = true; if (step.snippet) { - step.element = '#oe_snippets div.oe_snippet[data-snippet-id="'+step.snippet+'"] .oe_snippet_thumbnail'; + step.element = '#oe_snippets '+step.snippet+' .oe_snippet_thumbnail'; } } @@ -292,7 +328,8 @@ website.Tour = openerp.Class.extend({ 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("Can't arrive to step " + step.stepId + ": '" + step._title + "'" + + '\nhref: ' + window.location.href + '\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()) @@ -347,12 +384,13 @@ website.Tour = openerp.Class.extend({ } }, endTour: function () { - if (parseInt(this.localStorage.getItem("tour-"+this.id+"-test"),10) >= this.steps.length-1) { + var test = parseInt(this.localStorage.getItem("tour-"+this.id+"-test"),10) >= this.steps.length-1; + this.reset(); + if (test) { console.log('ok'); } else { console.log('error'); } - this.reset(); }, autoNextStep: function () { var self = this; @@ -377,7 +415,7 @@ website.Tour = openerp.Class.extend({ if (step.snippet) { - var selector = '#oe_snippets div.oe_snippet[data-snippet-id="'+step.snippet+'"] .oe_snippet_thumbnail'; + var selector = '#oe_snippets '+step.snippet+' .oe_snippet_thumbnail'; self.autoDragAndDropSnippet(selector); } else if (step.element.match(/#oe_snippets .* \.oe_snippet_thumbnail/)) { diff --git a/addons/website/static/src/js/website.tour.test.admin.js b/addons/website/static/src/js/website.tour.test.admin.js deleted file mode 100644 index 787f05beb23..00000000000 --- a/addons/website/static/src/js/website.tour.test.admin.js +++ /dev/null @@ -1,106 +0,0 @@ -(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 deleted file mode 100644 index 3e85ce20dae..00000000000 --- a/addons/website/static/src/js/website.tour.test.js +++ /dev/null @@ -1,34 +0,0 @@ -(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/js/website.translator.js b/addons/website/static/src/js/website.translator.js index 399e5e731b2..e88b1e4a6b6 100644 --- a/addons/website/static/src/js/website.translator.js +++ b/addons/website/static/src/js/website.translator.js @@ -34,11 +34,17 @@ dialog.$el.modal('hide'); self.translate().then(function () { mysuper.call(self); + if(self.gengo_translate){ + self.translation_gengo_display() + } }); }); } else { this.translate().then(function () { mysuper.call(self); + if(self.gengo_translate){ + self.translation_gengo_display() + } }); } }, @@ -65,14 +71,16 @@ }, processTranslatableNodes: function () { var self = this; + var source_attr = 'data-oe-source-id'; var $editables = $('[data-oe-model="ir.ui.view"]') .not('link, script') - .not('.oe_snippets,.oe_snippet, .oe_snippet *') + .not('.oe_snippets,.oe_snippet, .oe_snippet *, .navbar-toggle') .not('[data-oe-type]'); $editables.each(function () { var $node = $(this); - var view_id = $node.attr('data-oe-source-id') || $node.attr('data-oe-id'); + var source_id = $node.parents('[' + source_attr + ']:first').attr(source_attr)|0; + var view_id = $node.attr('data-oe-source-id') || source_id || $node.attr('data-oe-id'); self.transNode(this, view_id|0); }); $('.oe_translatable_text').on('paste', function () { @@ -90,7 +98,7 @@ self.sanitizeNode($node[0]); } if (self.getInitialContent($node[0]) !== $node.text()) { - $node.addClass('oe_dirty').removeClass('oe_translatable_todo'); + $node.addClass('oe_dirty').removeClass('oe_translatable_todo oe_translatable_inprogress'); } }, 0); }); @@ -117,6 +125,9 @@ }); if (trans.length) { node.setAttribute('data-oe-translation-id', trans[0].id); + if(trans[0].gengo_translation && (trans[0].state == 'inprogress' || trans[0].state == 'to_translate')){ + node.className += ' oe_translatable_inprogress'; + } } else { node.className += ' oe_translatable_todo'; } diff --git a/addons/website/static/src/xml/website.editor.xml b/addons/website/static/src/xml/website.editor.xml index 6c1e387c958..669d5f42e2f 100644 --- a/addons/website/static/src/xml/website.editor.xml +++ b/addons/website/static/src/xml/website.editor.xml @@ -6,7 +6,7 @@ <button type="button" data-action="save" class="btn btn-primary">Save</button> or - <a href="#" data-action="cancel">Discard</a> + <a href="#" data-action="cancel" class="btn btn-link">Discard</a> </form> <ul class="nav navbar-nav pull-right"> </ul> @@ -88,49 +88,85 @@ </form> </t> </t> - <t t-name="website.editor.dialog.image"> - <t t-call="website.editor.dialog"> - <t t-set="title">Image:</t> - <div class="row"> - <form method="POST" action="/website/attach" - enctype="multipart/form-data" - target="fileframe" - class="col-sm-8"> - <div class="text-center"> - <input type="file" name="upload" style="position: absolute; opacity: 0; width: 1px; height: 1px;"/> - <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> + <t t-name="website.editor.dialog.media"> + <div class="modal" tabindex="-1" role="dialog" aria-hidden="true"> + <div class="modal-dialog select-media"> + <div class="modal-content"> + <div class="modal-header"> + <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> + <h3 class="modal-title">Select a Media</h3> </div> - <div class="well"> - <a href="#existing" class="pull-right">Browse existing images</a> - <h3 class="list-group-item-heading">Image URL</h3> - <input type="text" class="form-control url" - placeholder="http://openerp.com"/> + <div class="modal-body"> + <ul class="nav nav-tabs"> + <li class="active"><a href="#editor-media-image" data-toggle="tab">Image</a></li> + <li><a href="#editor-media-icon" data-toggle="tab">Pictogram</a></li> + <li><a href="#editor-media-video" data-toggle="tab">Video</a></li> + <li class="search" style="float: right;"> + <ul class="pager mb0 mt0"> + <li class="previous disabled"><a href="#">← Previous</a></li> + <li class="next disabled"><a href="#">Next →</a></li> + </ul> + </li> + <li class="search" style="float: right;"> + <form action="#"> + <div class="form-group font-icons fa fa-search mb0"> + <input type="search" class="form-control" id="icon-search"/> + </div> + </form> + </li> + </ul> + + <!-- Tab panes --> + <div class="tab-content"> + <div class="tab-pane fade in active" id="editor-media-image"> + + </div> + <div class="tab-pane fade" id="editor-media-icon"> + + </div> + <div class="tab-pane fade" id="editor-media-video"> + + </div> + </div> + </div> + <div class="modal-footer"> + <button type="button" class="btn btn-primary save">Save</button> + <button type="button" class="btn hidden wait" disabled="disabled"/> + or + <a href="#" data-dismiss="modal" aria-hidden="true">Discard</a> </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> - <option value="img-thumbnail">Box</option> - <option value="img-circle">Circle</option> - <option value="shadow">Shadow</option> - </select> </div> </div> - <iframe src="about:blank" name="fileframe" class="hidden"/> - </t> + </div> + </t> + <t t-name="website.editor.dialog.image"> + <div> + <form method="POST" + action="/website/attach" + enctype="multipart/form-data" + target="fileframe" + class="form-inline"> + <div class="well"> + <div class="form-group pull-left"> + <input type="file" name="upload" accept="image/*" style="position: absolute; opacity: 0; width: 1px; height: 1px;"/> + <button type="button" class="btn btn-primary filepicker">Upload an image from your computer</button> + <button type="button" class="btn hidden wait" disabled="disabled">Uploading...</button> + </div> + <div> + <span class="text-muted"> — or — </span> + <label for="iamgeurl">Add an image URL</label> + <div class="form-group btn-group"> + <input type="text" name="url" class="form-control url pull-left" style="width: 320px;" id="iamgeurl" placeholder="http://openerp.com/logo.png"/> + <button class="btn btn-default" type="submit">Add</button> + </div> + </div> + </div> + <input type="hidden" name="func"/> + <div class="help-block"/> + <div class="existing-attachments"/> + </form> + </div> + <iframe src="about:blank" name="fileframe" class="hidden"/> </t> <t t-name="website.editor.dialog.image.existing"> <div class="modal" tabindex="-1" role="dialog" aria-hidden="true"> @@ -140,8 +176,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,17 +189,28 @@ </t> <t t-name="website.editor.dialog.image.existing.content"> <div class="existing-attachments"> - <ul class="pager"> - <li class="previous disabled"><a href="#">← Previous</a></li> - <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"> - <img t-att-src="attachment.website_url" t-att-alt="attachment.name" class="img img-responsive"/> + <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" t-att-title="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> + </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> @@ -201,49 +249,42 @@ </div> </t> - <t t-name="website.editor.hoverbutton"> - <button type="button" contenteditable="false" - t-attf-class="btn btn-primary hover-edition-button #{classes or ''}"> - <t t-esc="label"/> - </button> + <t t-name="website.editor.hoverbutton.link"> + <button contentEditable="false" type="button" class="btn btn-primary hover-edition-button btn-xs">Change</button> + </t> + + <t t-name="website.editor.hoverbutton.media"> + <div contentEditable="false" class="hover-edition dropdown"> + <a class="btn btn-primary btn-sm" data-toggle="dropdown" href="#">Customize</a> + <ul class="dropdown-menu" role="menu" aria-labelledby="dLabel"> + <li><a href="#" class="hover-edition-button">Change Media</a></li> + <li><a href="#" class="hover-style-button">Style</a></li> + <li class="dropdown-submenu"> + <a href="#" tabindex="-1">Rotation</a> + <ul class="dropdown-menu"> + <li data-value="fa-spin"><a>Spin</a></li> + <li data-value="fa-flip-horizontal"><a>Horizontal flip</a></li> + <li data-value="fa-flip-vertical"><a>Vertical flip</a></li> + </ul> + </li> + <li><a href="#" data-value="fa-border">border</a></li> + </ul> + </div> </t> <t t-name="website.editor.dialog.font-icons"> - <t t-call="website.editor.dialog"> - <t t-set="title">Icon:</t> - <form> - <div class="form-group" id="fa-preview"> + <form action="#"> + <input type="hidden" id="fa-icon"/> + <input type="hidden" id="fa-size"/> + <div class="font-icons-icons"> + <t t-call="website.editor.dialog.font-icons.icons"> + <t t-set="icons" t-value="widget.icons"/> + </t> + </div> + <div class="form-group mt32" id="fa-preview"> - </div> - <div class="form-group font-icons fa fa-search"> - <input type="hidden" id="fa-icon" class="form-control"/> - <input type="hidden" id="fa-size" class="form-control"/> - <input type="search" class="form-control" id="icon-search"/> - <div class="font-icons-icons"> - <t t-call="website.editor.dialog.font-icons.icons"> - <t t-set="icons" t-value="widget.icons"/> - </t> - </div> - </div> - <div class="form-group"> - <label for="fa-rotation">Rotation</label> - <select id="fa-rotation" class="form-control"> - <option value="">None</option> - <option value="fa-spin">Spin</option> - <option value="fa-rotate-90">Rotate 90º</option> - <option value="fa-rotate-180">Rotate 180º</option> - <option value="fa-rotate-270">Rotate 270º</option> - <option value="fa-flip-horizontal">Horizontal flip</option> - <option value="fa-flip-vertical">Vertical flip</option> - </select> - </div> - <div class="form-group"> - <label> - <input type="checkbox" id="fa-border"/> border - </label> - </div> - </form> - </t> + </div> + </form> </t> <t t-name="website.editor.dialog.font-icons.icons"> <span t-foreach="icons" t-as="icon" @@ -253,4 +294,50 @@ </span> </t> + <t t-name="website.editor.dialog.video"> + <form action="#" class="form-inline"> + <div class="well"> + <div class="form-group btn-group"> + <span class="text-muted pull-right" style="margin-left:10px; line-height: 2em;">(Youtube, Vimeo, Dailymotion)</span> + <label for="urlvideo" style="width: 220px; line-height: 2em;" class="pull-left">Set a video URL</label> + <input type="text" + name="url" + class="form-control url pull-left" + style="width: 400px;" + id="urlvideo" + placeholder="//www.youtube.com/embed/yws1tbgNV7k"/> + <button class="btn btn-default">Preview</button> + </div> + <div class="form-group btn-group"> + <label for="urlvideo" style="width: 220px; line-height: 2em;" class="pull-left"><span class="text-muted">— or —</span> Embed Video (HTML)</label> + <input type="text" + name="embed" + class="form-control url pull-left" + style="width: 400px;" + id="embedvideo" + placeholder='<iframe src="//www.youtube.com/embed/yws1tbgNV7k"></iframe>'/> + <button class="btn btn-default">Preview</button> + </div> + </div> + + <div id="video-preview" style="width: 450px; margin: 0 auto;"> + <div class="media_iframe_video"> + <iframe + src="" + frameborder="0" + allowfullscreen="allowfullscreen"></iframe> + </div> + </div> + + <input type="hidden" id="video_id" value=""/> + <input type="hidden" id="video_type" value=""/> + + <div class="text-center mt32"> + <div class="checkbox"> + <label><input type="checkbox" id="autoplay"/> Autoplay</label> + </div> + </div> + </form> + </t> + </templates> diff --git a/addons/website/static/src/xml/website.snippets.xml b/addons/website/static/src/xml/website.snippets.xml index d73e0c3934a..b1853f366b0 100644 --- a/addons/website/static/src/xml/website.snippets.xml +++ b/addons/website/static/src/xml/website.snippets.xml @@ -22,7 +22,7 @@ <t t-name="website.snippet_overlay"> <div class="oe_overlay"> - <div class="oe_overlay_options"> + <div class="oe_overlay_options" contentEditable="false"> <div class="btn-group"> <a href="#" class="btn btn-default btn-sm oe_snippet_parent" title="Select Container Block"><i class="fa fa-upload"></i></a> <div class="dropdown oe_options hidden btn-group"> @@ -34,6 +34,16 @@ <a href="#" class="btn btn-default btn-sm oe_snippet_remove" title="Remove Block">&nbsp;<i class="fa fa-trash-o"></i>&nbsp; </a> </div> </div> + <div class='oe_handles'> + <div class='oe_handle n readonly'><div></div></div> + <div class='oe_handle e readonly'><div></div></div> + <div class='oe_handle w readonly'><div></div></div> + <div class='oe_handle size readonly'> + <div class="oe_handle_button auto_size">Auto Resize</div> + <div class="oe_handle_button size">Resize</div> + </div> + <div class='oe_handle s readonly'><div></div></div> + </div> </div> </t> @@ -45,13 +55,6 @@ <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> <div class='oe_snippet_thumbnail'>Margin resize</div> </div> diff --git a/addons/website/static/src/xml/website.translator.xml b/addons/website/static/src/xml/website.translator.xml index d1d9ab14a39..480489ad9b3 100644 --- a/addons/website/static/src/xml/website.translator.xml +++ b/addons/website/static/src/xml/website.translator.xml @@ -38,7 +38,7 @@ </section> </div> <div class="modal-footer"> - <div class="pull-left"> + <div class="pull-right"> <input type="checkbox" name="do_not_show"/> Do not show this dialog later. </div> |
