[IMP] web_editor, website, website_sale: add zoom option for t-field image
web_editor Qweb: Add 'zoom' option for widget image
<span t-field="product.image_small" t-options="{'widget': 'image', 'zoom': 'image_medium'}" />
website: Add Zoom code to be standard for all website module
website_sale: add zoom on Main product image
This commit is contained in:
@@ -303,8 +303,14 @@ class Image(models.AbstractModel):
|
||||
elif options.get('alt'):
|
||||
alt = options['alt']
|
||||
|
||||
img = '<img class="%s" src="%s" style="%s"%s/>' % \
|
||||
(classes, src, options.get('style', ''), ' alt="%s"' % alt if alt else '')
|
||||
src_zoom = None
|
||||
if options.get('zoom') and getattr(record, options['zoom'], None):
|
||||
src_zoom = '/web/image/%s/%s/%s%s?unique=%s' % (record._name, record.id, options['zoom'], max_size, sha)
|
||||
elif options.get('zoom'):
|
||||
src_zoom = options['zoom']
|
||||
|
||||
img = '<img class="%s" src="%s" style="%s"%s%s/>' % \
|
||||
(classes, src, options.get('style', ''), ' alt="%s"' % alt if alt else '', ' data-zoom="1" data-zoom-image="%s"' % src_zoom if src_zoom else '')
|
||||
return ir_qweb.unicodifier(img)
|
||||
|
||||
local_url_re = re.compile(r'^/(?P<module>[^]]+)/static/(?P<rest>.+)$')
|
||||
|
||||
@@ -266,6 +266,9 @@ odoo.define('website.website', function (require) {
|
||||
}
|
||||
});
|
||||
|
||||
// enable magnify on zommable img
|
||||
$('.zoomable img[data-zoom]').zoomOdoo();
|
||||
|
||||
Dialog.include({
|
||||
init: function () {
|
||||
this._super.apply(this, arguments);
|
||||
|
||||
@@ -0,0 +1,288 @@
|
||||
(function ($) {
|
||||
/**
|
||||
This code has been more that widely inspired by easyZoom library.
|
||||
|
||||
Copyright 2013 Matt Hinchliffe
|
||||
|
||||
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.
|
||||
**/
|
||||
|
||||
'use strict';
|
||||
|
||||
var dw, dh, rw, rh, lx, ly;
|
||||
|
||||
var defaults = {
|
||||
|
||||
// Attribute to retrieve the zoom image URL from.
|
||||
linkTag: 'a',
|
||||
linkAttribute: 'data-zoom-image',
|
||||
|
||||
// event to trigger zoom
|
||||
event: 'click', //or mousenter
|
||||
|
||||
// Prevent clicks on the zoom image link.
|
||||
preventClicks: true,
|
||||
|
||||
// Callback function to execute before the flyout is displayed.
|
||||
beforeShow: $.noop,
|
||||
|
||||
// Callback function to execute before the flyout is removed.
|
||||
beforeHide: $.noop,
|
||||
|
||||
// Callback function to execute when the flyout is displayed.
|
||||
onShow: $.noop,
|
||||
|
||||
// Callback function to execute when the flyout is removed.
|
||||
onHide: $.noop,
|
||||
|
||||
// Callback function to execute when the cursor is moved while over the image.
|
||||
onMove: $.noop
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* ZoomOdoo
|
||||
* @constructor
|
||||
* @param {Object} target
|
||||
* @param {Object} options (Optional)
|
||||
*/
|
||||
function ZoomOdoo(target, options) {
|
||||
this.$target = $(target);
|
||||
this.opts = $.extend({}, defaults, options, this.$target.data());
|
||||
|
||||
if (this.isOpen === undefined) {
|
||||
this._init();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Init
|
||||
* @private
|
||||
*/
|
||||
ZoomOdoo.prototype._init = function() {
|
||||
this.$link = this.$target.find(this.opts.linkTag).length && this.$target.find(this.opts.linkTag) || this.$target;
|
||||
this.$image = this.$target.find('img').length && this.$target.find('img') || this.$target;
|
||||
this.$flyout = $('<div class="zoomodoo-flyout" />');
|
||||
|
||||
var $attach = this.$target;
|
||||
if (this.opts.attach !== undefined && this.$target.parents(this.opts.attach).length) {
|
||||
$attach = this.$target.parents(this.opts.attach);
|
||||
}
|
||||
$attach.parent().on('mousemove.zoomodoo touchmove.zoomodoo', $.proxy(this._onMove, this));
|
||||
$attach.parent().on('mouseleave.zoomodoo touchend.zoomodoo', $.proxy(this._onLeave, this));
|
||||
this.$target.parent().on(this.opts.event + '.zoomodoo touchstart.zoomodoo', $.proxy(this._onEnter, this));
|
||||
|
||||
if (this.opts.preventClicks) {
|
||||
this.$target.on('click.zoomodoo', function(e) { e.preventDefault(); });
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Show
|
||||
* @param {MouseEvent|TouchEvent} e
|
||||
* @param {Boolean} testMouseOver (Optional)
|
||||
*/
|
||||
ZoomOdoo.prototype.show = function(e, testMouseOver) {
|
||||
var w1, h1, w2, h2;
|
||||
var self = this;
|
||||
|
||||
if (this.opts.beforeShow.call(this) === false) return;
|
||||
|
||||
if (!this.isReady) {
|
||||
return this._loadImage(this.$link.attr(this.opts.linkAttribute), function() {
|
||||
if (self.isMouseOver || !testMouseOver) {
|
||||
self.show(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var $attach = this.$target;
|
||||
if (this.opts.attach !== undefined && this.$target.parents(this.opts.attach).length) {
|
||||
$attach = this.$target.parents(this.opts.attach);
|
||||
}
|
||||
$attach.parent().append(this.$flyout);
|
||||
|
||||
w1 = this.$target.width();
|
||||
h1 = this.$target.height();
|
||||
|
||||
w2 = this.$flyout.width();
|
||||
h2 = this.$flyout.height();
|
||||
|
||||
dw = this.$zoom.width() - w2;
|
||||
dh = this.$zoom.height() - h2;
|
||||
|
||||
// For the case where the zoom image is actually smaller than
|
||||
// the flyout.
|
||||
if (dw < 0) dw = 0;
|
||||
if (dh < 0) dh = 0;
|
||||
|
||||
rw = dw / w1;
|
||||
rh = dh / h1;
|
||||
|
||||
this.isOpen = true;
|
||||
|
||||
this.opts.onShow.call(this);
|
||||
|
||||
if (e) {
|
||||
this._move(e);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* On enter
|
||||
* @private
|
||||
* @param {Event} e
|
||||
*/
|
||||
ZoomOdoo.prototype._onEnter = function(e) {
|
||||
var touches = e.originalEvent.touches;
|
||||
|
||||
this.isMouseOver = true;
|
||||
|
||||
if (!touches || touches.length === 1) {
|
||||
e.preventDefault();
|
||||
this.show(e, true);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* On move
|
||||
* @private
|
||||
* @param {Event} e
|
||||
*/
|
||||
ZoomOdoo.prototype._onMove = function(e) {
|
||||
if (!this.isOpen) return;
|
||||
|
||||
e.preventDefault();
|
||||
this._move(e);
|
||||
};
|
||||
|
||||
/**
|
||||
* On leave
|
||||
* @private
|
||||
*/
|
||||
ZoomOdoo.prototype._onLeave = function() {
|
||||
this.isMouseOver = false;
|
||||
if (this.isOpen) {
|
||||
this.hide();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* On load
|
||||
* @private
|
||||
* @param {Event} e
|
||||
*/
|
||||
ZoomOdoo.prototype._onLoad = function(e) {
|
||||
// IE may fire a load event even on error so test the image dimensions
|
||||
if (!e.currentTarget.width) return;
|
||||
|
||||
this.isReady = true;
|
||||
|
||||
this.$flyout.html(this.$zoom);
|
||||
|
||||
if (e.data.call) {
|
||||
e.data();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Load image
|
||||
* @private
|
||||
* @param {String} href
|
||||
* @param {Function} callback
|
||||
*/
|
||||
ZoomOdoo.prototype._loadImage = function(href, callback) {
|
||||
var zoom = new Image();
|
||||
|
||||
this.$zoom = $(zoom).on('load', callback, $.proxy(this._onLoad, this));
|
||||
|
||||
zoom.style.position = 'absolute';
|
||||
zoom.src = href;
|
||||
};
|
||||
|
||||
/**
|
||||
* Move
|
||||
* @private
|
||||
* @param {Event} e
|
||||
*/
|
||||
ZoomOdoo.prototype._move = function(e) {
|
||||
if (e.type.indexOf('touch') === 0) {
|
||||
var touchlist = e.touches || e.originalEvent.touches;
|
||||
lx = touchlist[0].pageX;
|
||||
ly = touchlist[0].pageY;
|
||||
} else {
|
||||
lx = e.pageX || lx;
|
||||
ly = e.pageY || ly;
|
||||
}
|
||||
|
||||
var offset = this.$target.offset();
|
||||
var pt = ly - offset.top;
|
||||
var pl = lx - offset.left;
|
||||
var xt = Math.ceil(pt * rh);
|
||||
var xl = Math.ceil(pl * rw);
|
||||
|
||||
// Close if outside
|
||||
if (xl < 0 || xt < 0 || xl > dw || xt > dh) {
|
||||
this.hide();
|
||||
} else {
|
||||
var top = xt * -1;
|
||||
var left = xl * -1;
|
||||
|
||||
this.$zoom.css({
|
||||
top: top,
|
||||
left: left
|
||||
});
|
||||
|
||||
this.opts.onMove.call(this, top, left);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Hide
|
||||
*/
|
||||
ZoomOdoo.prototype.hide = function() {
|
||||
if (!this.isOpen) return;
|
||||
if (this.opts.beforeHide.call(this) === false) return;
|
||||
|
||||
this.$flyout.detach();
|
||||
this.isOpen = false;
|
||||
|
||||
this.opts.onHide.call(this);
|
||||
};
|
||||
|
||||
|
||||
// jQuery plugin wrapper
|
||||
$.fn.zoomOdoo = function(options) {
|
||||
return this.each(function() {
|
||||
var api = $.data(this, 'zoomOdoo');
|
||||
|
||||
if (!api) {
|
||||
$.data(this, 'zoomOdoo', new ZoomOdoo(this, options));
|
||||
} else if (api.isOpen === undefined) {
|
||||
api._init();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// AMD and CommonJS module compatibility
|
||||
if (typeof define === 'function' && define.amd){
|
||||
define(function() {
|
||||
return ZoomOdoo;
|
||||
});
|
||||
} else if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = ZoomOdoo;
|
||||
}
|
||||
|
||||
})(jQuery);
|
||||
@@ -0,0 +1,35 @@
|
||||
|
||||
.zoomodoo {
|
||||
position: relative;
|
||||
|
||||
/* 'Shrink-wrap' the element */
|
||||
display: inline-block;
|
||||
*display: inline;
|
||||
*zoom: 1;
|
||||
|
||||
img {
|
||||
vertical-align: bottom;
|
||||
}
|
||||
}
|
||||
|
||||
.zoomodoo-flyout {
|
||||
position:absolute;
|
||||
z-index: 100;
|
||||
overflow: hidden;
|
||||
background: #FFF;
|
||||
}
|
||||
|
||||
.zoomodoo-hover .zoomodoo-flyout {
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.zoomodoo-next .zoomodoo-flyout {
|
||||
top: 0;
|
||||
left: 100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin-left: 20px;
|
||||
}
|
||||
@@ -16,11 +16,13 @@
|
||||
<link rel="stylesheet" type="text/less" href="/website/static/src/less/website.ui.components.less"/>
|
||||
<link rel="stylesheet" type="text/less" href="/website/static/src/less/website.wrapwrap.less"/>
|
||||
<link rel="stylesheet" type="text/less" href="/website/static/src/less/website.snippets.less"/>
|
||||
<link rel="stylesheet" type="text/css" href="/website/static/src/less/website.zoomodoo.less"/>
|
||||
</xpath>
|
||||
<xpath expr="//script[last()]" position="after">
|
||||
<script type="text/javascript" src="/website/static/src/js/website.js"></script>
|
||||
<script type="text/javascript" src="/website/static/src/js/website.share.js"></script>
|
||||
<script type="text/javascript" src="/website/static/src/js/website.snippets.animation.js"></script>
|
||||
<script type="text/javascript" src="/website/static/src/js/website.zoomodoo.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
@@ -44,6 +46,7 @@
|
||||
|
||||
<script type="text/javascript" src="/website/static/src/js/website.snippets.editor.js"></script>
|
||||
<script type="text/javascript" src="/website/static/src/js/website.snippets.gallery.js"></script>
|
||||
|
||||
</template>
|
||||
|
||||
<!-- Layout -->
|
||||
|
||||
@@ -237,7 +237,7 @@ odoo.define('website_sale.website_sale', function (require) {
|
||||
$img.attr("src", "/web/image/product.product/" + product_id + "/image");
|
||||
$img.parent().attr('data-oe-model', 'product.product').attr('data-oe-id', product_id)
|
||||
.data('oe-model', 'product.product').data('oe-id', product_id);
|
||||
|
||||
|
||||
$img = $(event_source).closest('tr.js_product, .oe_website_sale').find('img.js_variant_img_small');
|
||||
if ($img) { // if only one, thumbnails are not displayed
|
||||
$img.attr("src", "/web/image/product.product/" + product_id + "/image/90x90");
|
||||
@@ -419,4 +419,6 @@ odoo.define('website_sale.website_sale', function (require) {
|
||||
}
|
||||
$("select[name='country_id']").change();
|
||||
});
|
||||
|
||||
$('.ecom-zoomable img[data-zoom]').zoomOdoo({ attach: '#o-carousel-product'});
|
||||
});
|
||||
|
||||
@@ -82,7 +82,7 @@
|
||||
</div>
|
||||
<div class="oe_product_image">
|
||||
<a itemprop="url" t-att-href="keep('/shop/product/%s' % slug(product), page=(pager['page']['num'] if pager['page']['num']>1 else None))">
|
||||
<span itemprop="image" t-field="product.image" t-options="{'widget': 'image', 'resize': None if product_image_big else '300x300'}" t-att-alt="product.name" />
|
||||
<span itemprop="image" t-field="product.image" t-options="{'widget': 'image', 'resize': None if product_image_big else '300x300', 'zoom': 'image'}" t-att-alt="product.name" />
|
||||
</a>
|
||||
</div>
|
||||
<t t-if="show_publish">
|
||||
@@ -397,17 +397,17 @@
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-7">
|
||||
<span itemprop="image" t-field="product.image" t-options="{'widget': 'image', 'class': 'product_detail_img', 'alt-field': 'name'}" groups="!website_sale.group_website_multi_image"/>
|
||||
<span itemprop="image" t-field="product.image" t-options="{'widget': 'image', 'class': 'product_detail_img', 'alt-field': 'name', 'zoom': 'image'}" groups="!website_sale.group_website_multi_image"/>
|
||||
<t groups="website_sale.group_website_multi_image">
|
||||
<t t-set="variant_img" t-value="product.product_variant_ids[0].image_variant"/>
|
||||
<t t-set="image_ids" t-value="product.product_image_ids"/>
|
||||
<div id="o-carousel-product" class="carousel slide" data-ride="carousel" data-interval="0">
|
||||
<div class="carousel-outer">
|
||||
<div class="carousel-inner">
|
||||
<div class="item active" itemprop="image" t-field="product.product_variant_ids[0].image" t-options='{"widget": "image", "class": "product_detail_img js_variant_img", "alt-field": "name"}'/>
|
||||
<div t-if="variant_img" class="item" itemprop="image" t-field="product.image" t-options='{"widget": "image", "class": "product_detail_img", "alt-field": "name"}'/>
|
||||
<div class="item active" itemprop="image" t-field="product.product_variant_ids[0].image" t-options='{"widget": "image", "class": "product_detail_img js_variant_img", "alt-field": "name", "zoom": "image"}'/>
|
||||
<div t-if="variant_img" class="item" itemprop="image" t-field="product.image" t-options='{"widget": "image", "class": "product_detail_img", "alt-field": "name", "zoom": "image"}'/>
|
||||
<t t-if="len(image_ids)" t-foreach="image_ids" t-as="pimg">
|
||||
<div class="item" t-field="pimg.image" t-options='{"widget": "image", "class": "product_detail_img", "alt-field": "name"}'/>
|
||||
<div class="item" t-field="pimg.image" t-options='{"widget": "image", "class": "product_detail_img", "alt-field": "name", "zoom": "image" }'/>
|
||||
</t>
|
||||
</div>
|
||||
|
||||
@@ -483,6 +483,12 @@
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template inherit_id='website_sale.product' id="product_picture_magnify" customize_show="True" name="Activate Magnify">
|
||||
<xpath expr='//div[contains(@class, "js_sale")]' position='attributes'>
|
||||
<attribute name="class" separator=" " add="ecom-zoomable zoomodoo-next" />
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="recommended_products" inherit_id="website_sale.product" customize_show="True" name="Alternative Products">
|
||||
<xpath expr="//div[@id='product_full_description']" position="after">
|
||||
<div class="container mt32" t-if="product.alternative_product_ids">
|
||||
@@ -1059,7 +1065,7 @@
|
||||
<a class='a-submit btn btn-default mb16 btn-block'>
|
||||
<i class="fa fa-plus-square" aria-hidden="true"></i> Add an address
|
||||
</a>
|
||||
</form>
|
||||
</form>
|
||||
</div>
|
||||
<t t-foreach="shippings" t-as="ship">
|
||||
<div class="col-sm-12 col-md-6 one_kanban">
|
||||
@@ -1621,7 +1627,7 @@
|
||||
<input type="hidden" name="tx_id" t-att-value="tx.id"/>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
|
||||
<template id="payment_token_form_confirm">
|
||||
<t t-call="website.layout">
|
||||
<t t-set="additional_title">Shop - Confirm payment</t>
|
||||
|
||||
Reference in New Issue
Block a user