Commit ed319fd8 by dhn

Merge branch 'developer' of http://47.99.244.21:9999/root/joshine into developer

parents 98f911e0 6494f127
...@@ -44,14 +44,14 @@ class InstallSchema implements InstallSchemaInterface ...@@ -44,14 +44,14 @@ class InstallSchema implements InstallSchemaInterface
->newTable($installer->getTable('amasty_advanced_review_images')) ->newTable($installer->getTable('amasty_advanced_review_images'))
->addColumn( ->addColumn(
'image_id', 'image_id',
Table::TYPE_INTEGER, Table::TYPE_BIGINT,
null, null,
['identity' => true, 'unsigned' => true, 'nullable' => false, 'primary' => true], ['identity' => true, 'unsigned' => true, 'nullable' => false, 'primary' => true],
'Review Id' 'Review Id'
) )
->addColumn( ->addColumn(
'review_id', 'review_id',
Table::TYPE_INTEGER, Table::TYPE_BIGINT,
null, null,
['default' => 0, 'nullable' => false], ['default' => 0, 'nullable' => false],
'Review table id' 'Review table id'
......
<?php
namespace Joshine\Review\Block\Components;
use Magento\Framework\Serialize\Serializer\Json;
use Magento\Framework\View\Element\Template;
class Pager extends \Magento\Theme\Block\Html\Pager
{
const DEFAULT_PAGE_SIZE = 3;
/**
* @var string
*/
protected $_template = 'Joshine_Review::components/pager.phtml';
/**
* @var Json
*/
private $json;
public function __construct(
Template\Context $context,
Json $json,
array $data = []
) {
parent::__construct($context, $data);
$this->json = $json;
}
/**
* @return string
*/
public function getJsonData()
{
return $this->json->serialize($this->getData());
}
}
<?php
namespace Joshine\Review\Block;
use Joshine\Review\Model\ResourceModel\Images\Collection;
use Joshine\Review\Helper\ImageHelper;
use Magento\Framework\View\Element\Template;
use Joshine\Review\Model\ResourceModel\Images\CollectionFactory;
/**
* Class Images
* @package Amasty\AdvancedReview\Block
*/
class Images extends \Magento\Framework\View\Element\Template
{
//评论缩略图宽度
const REVIEW_COVER_WIDTH = 200;
/**
* @var string
*/
protected $_template = 'Joshine_Review::images.phtml';
private $reviewId;
private $productId;
/**
* @var int
*/
/**
* @var \Magento\Framework\Json\EncoderInterface
*/
private $jsonEncoder;
/**
* @var CollectionFactory
*/
private $collectionFactory;
/**
* @var ImageHelper
*/
private $imageHelper;
public function __construct(
Template\Context $context,
CollectionFactory $collectionFactory,
\Magento\Framework\Json\EncoderInterface $jsonEncoder,
ImageHelper $imageHelper,
array $data = []
) {
parent::__construct($context, $data);
$this->jsonEncoder = $jsonEncoder;
$this->collectionFactory = $collectionFactory;
$this->imageHelper = $imageHelper;
}
/**
* @return int
*/
public function getReviewId(): int
{
return $this->reviewId;
}
/**
* @param $reviewId
*
* @return $this
*/
public function setReviewId($reviewId): Images
{
$this->reviewId = $reviewId;
return $this;
}
public function setProductId($productId): Images
{
$this->productId = $productId;
return $this;
}
public function getProductId()
{
return $this->productId;
}
public function getCollection() {
/** @var Collection $collection */
$collection = $this->collectionFactory->create()
->addFieldToSelect('*')
->addFieldToFilter('review_id', $this->getReviewId());
return $collection;
}
public function getFullImagePath($item): string
{
return $this->imageHelper->getFullPath($item->getPath());
}
/**
* @param $item
* @return string
* @throws \Magento\Framework\Exception\NoSuchEntityException
*/
public function getResizedImagePath($item): string
{
return $this->imageHelper->resize($item->getPath(), self::REVIEW_COVER_WIDTH * 2);
}
/**
* @param $item
* @return string
* @throws \Magento\Framework\Exception\NoSuchEntityException
*/
public function getResizedImagePathByLimit($item, $width): string
{
return $this->imageHelper->resize($item->getPath(), $width);
}
}
<?php
namespace Joshine\Review\Block;
class JsInit extends \Magento\Framework\View\Element\Template {
protected $_template = 'Joshine_Review::review-js.phtml';
}
\ No newline at end of file
<?php
namespace Joshine\Review\Block\Review\Product\View;
use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Framework\DataObjectFactory;
use Magento\Review\Model\ResourceModel\Review\Collection as ReviewCollection;
use Magento\Review\Model\Review\SummaryFactory;
use Joshine\Review\Block\Components\Pager;
class ListView extends \Magento\Review\Block\Product\View\ListView
{
const VOTED_CLASS_NAME = 'active';
/**
* @var \Magento\Review\Model\Review\SummaryFactory
*/
private $summaryModFactory;
private $dataObjectFactory;
/**
* @var ReviewCollection
*/
private $reviewsCollection;
private $reviewsColFactory;
private $voteRepository;
private $vote;
private $formKey;
/**
* @var \Magento\Framework\HTTP\PhpEnvironment\RemoteAddress
*/
private $remoteAddress;
public function __construct(
DataObjectFactory $dataObjectFactory,
SummaryFactory $summaryModFactory,
\Magento\Framework\Data\Form\FormKey $formKey,
\Magento\Framework\HTTP\PhpEnvironment\RemoteAddress $remoteAddress,
\Joshine\Review\Model\Repository\VoteRepository $voteRepository,
\Magento\Review\Model\ResourceModel\Review\CollectionFactory $reviewsColFactory,
\Magento\Catalog\Block\Product\Context $context, \Magento\Framework\Url\EncoderInterface $urlEncoder, \Magento\Framework\Json\EncoderInterface $jsonEncoder, \Magento\Framework\Stdlib\StringUtils $string, \Magento\Catalog\Helper\Product $productHelper, \Magento\Catalog\Model\ProductTypes\ConfigInterface $productTypeConfig, \Magento\Framework\Locale\FormatInterface $localeFormat, \Magento\Customer\Model\Session $customerSession, ProductRepositoryInterface $productRepository, \Magento\Framework\Pricing\PriceCurrencyInterface $priceCurrency, \Magento\Review\Model\ResourceModel\Review\CollectionFactory $collectionFactory, array $data = [])
{
$this->summaryModFactory = $summaryModFactory;
$this->dataObjectFactory = $dataObjectFactory;
$this->reviewsColFactory = $reviewsColFactory;
$this->voteRepository = $voteRepository;
$this->formKey = $formKey;
$this->remoteAddress = $remoteAddress;
parent::__construct($context, $urlEncoder, $jsonEncoder, $string, $productHelper, $productTypeConfig, $localeFormat, $customerSession, $productRepository, $priceCurrency, $collectionFactory, $data);
}
protected function _prepareLayout()
{
parent::_prepareLayout();
if ($this->getReviewsCollection()) {
$pager = $this->getLayout()->createBlock(
Pager::class
)->setCollection(
$this->getLimitedReviewsCollection()
)->setData($this->getData())
->setProductId($this->getProductId());
$this->setChild('pager', $pager);
$this->getLimitedReviewsCollection()->load();
}
return $this;
}
public function setSummary($product, $displayedCollection)
{
$storeId = $this->_storeManager->getStore()->getId();
$summaryData = $this->summaryModFactory->create()
->setStoreId($storeId)
->load($product->getId());
$summary = $this->dataObjectFactory->create();
$summary->setData($summaryData->getData());
$product->setRatingSummary($summary);
}
public function getRatingSummary()
{
$result = null;
$summary = $this->getProduct()->getRatingSummary();
if ($summary) {
$result = $summary->getRatingSummary();
}
return $result;
}
function getRatingSummaryValue(): float
{
$value = $this->getRatingSummary();
$value = $value / 100 * 5;
return round($value, 1);
}
public function getDetailedSummary(): array
{
$result = [
'3' => 0,
'2' => 0,
'1' => 0
];
$reviews = $this->reviewsColFactory->create()->addStoreFilter(
$this->_storeManager->getStore()->getId()
)->addStatusFilter(
\Magento\Review\Model\Review::STATUS_APPROVED
)->addEntityFilter(
'product',
$this->getProduct()->getId()
);
foreach ($reviews as $item) {
if($item->getSizeFits() != 0 ){
$key = $item->getSizeFits();
$result[$key] = $result[$key] + 1;
}
}
return $result;
}
/**
* Get collection of reviews
*
* @return ReviewCollection
*/
public function getReviewsCollection()
{
if (null === $this->reviewsCollection) {
$this->reviewsCollection = $this->reviewsColFactory->create()->addStoreFilter(
$this->_storeManager->getStore()->getId()
)->addStatusFilter(
\Magento\Review\Model\Review::STATUS_APPROVED
)->addEntityFilter(
'product',
$this->getProduct()->getId()
)->setDateOrder();
}
return $this->reviewsCollection;
}
public function getReviewsCount() : int
{
return $this->getReviewsCollection()->getSize();
}
public function getLimitedReviewsCollection(): ReviewCollection
{
$page = $this->getRequest()->getParam('p') ?? 1;
return $this->getReviewsCollection()->clear()
->setPageSize(Pager::DEFAULT_PAGE_SIZE)
->setCurPage((int)$page)
->load()->addRateVotes();
}
public function getPagerHtml()
{
return $this->getChildHtml('pager');
}
private function getVote($reviewId)
{
return $this->voteRepository->getVotesCount($reviewId);
}
public function getPlusReview($reviewId)
{
$vote = $this->getVote($reviewId);
return $vote['plus'];
}
/**
* @return string
* @throws \Magento\Framework\Exception\LocalizedException
*/
public function getFormKey(): string
{
return $this->formKey->getFormKey();
}
/**
* @return string
*/
public function getPlusVotedClass($reviewId)
{
$result = '';
if ($this->isPlusVoted($reviewId)) {
$result = self::VOTED_CLASS_NAME;
}
return $result;
}
private function getVotedByIp($reviewId)
{
return $this->voteRepository->getVotesCount(
$reviewId,
$this->remoteAddress->getRemoteAddress()
);
}
public function isPlusVoted($reviewId)
{
$voted = $this->getVotedByIp($reviewId);
return $voted['plus'] > 0;
}
}
<?php
namespace Joshine\Review\Block\Review;
use Amasty\AdvancedReview\Model\Toolbar\Applier;
use Amasty\AdvancedReview\Model\Toolbar\UrlBuilder;
use Magento\Framework\View\Element\Template;
class ToolBar extends Template {
protected $request;
protected $_template = 'Joshine_Review::toolbar.phtml';
public function __construct(
Template\Context $context,
\Magento\Framework\App\Request\Http $request,
array $data = []
) {
parent::__construct($context, $data);
$this->request = $request;
}
}
<?php
namespace Joshine\Review\Contract;
interface ImagesInterface
{
const IMAGE_ID = 'image_id';
const REVIEW_ID = 'review_id';
const PATH = 'path';
/**
* Returns image id field
*
* @return int|null
*/
public function getImageId();
/**
* @param int $imageId
*
* @return $this
*/
public function setImageId($imageId);
/**
* Returns review id field
*
* @return int|null
*/
public function getReviewId();
/**
* @param int $reviewId
*
* @return $this
*/
public function setReviewId($reviewId);
/**
* Returns image path
*
* @return string|null
*/
public function getPath();
/**
* @param string $path
*
* @return $this
*/
public function setPath($path);
}
<?php
namespace Joshine\Review\Controller\Ajax;
use Magento\Framework\App\Action\Context;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Controller\Result\JsonFactory;
class Helpful extends \Magento\Framework\App\Action\Action
{
/**
* @var \Magento\Framework\Data\Form\FormKey\Validator
*/
private $formKeyValidator;
/**
* @var \Psr\Log\LoggerInterface
*/
private $logger;
/**
* @var \Magento\Framework\Json\EncoderInterface
*/
private $jsonEncoder;
/**
* @var \Magento\Framework\HTTP\PhpEnvironment\RemoteAddress
*/
private $remoteAddress;
private $voteRepository;
private $voteFactory;
/**
* @var JsonFactory
*/
private $resultJsonFactory;
public function __construct(
Context $context,
\Magento\Framework\Data\Form\FormKey\Validator $formKeyValidator,
\Psr\Log\LoggerInterface $logger,
\Magento\Framework\Json\EncoderInterface $jsonEncoder,
\Magento\Framework\HTTP\PhpEnvironment\RemoteAddress $remoteAddress,
\Joshine\Review\Model\VoteFactory $voteFactory,
\Joshine\Review\Model\Repository\VoteRepository $voteRepository,
JsonFactory $resultJsonFactory
) {
parent::__construct($context);
$this->formKeyValidator = $formKeyValidator;
$this->logger = $logger;
$this->jsonEncoder = $jsonEncoder;
$this->remoteAddress = $remoteAddress;
$this->voteRepository = $voteRepository;
$this->voteFactory = $voteFactory;
$this->resultJsonFactory = $resultJsonFactory;
}
/**
* @return \Magento\Framework\App\ResponseInterface|\Magento\Framework\Controller\Result\Json|\Magento\Framework\Controller\ResultInterface
*/
public function execute()
{
$message = [
'error' => __('Sorry. There is a problem with your Vote Request.')
];
if ($this->getRequest()->isAjax()) {
try {
if (!$this->formKeyValidator->validate($this->getRequest())) {
throw new LocalizedException(
__('Form key is not valid. Please try to reload the page.')
);
}
$type = $this->getRequest()->getParam('type');
$reviewId = (int)$this->getRequest()->getParam('review');
if ($reviewId > 0 && in_array($type, ['plus', 'minus', 'update'])) {
$ip = $this->remoteAddress->getRemoteAddress();
if ($type != 'update') {
$type = ($type == 'plus') ? '1' : '0';
$model = $this->voteRepository->getByIdAndIp($reviewId, $ip);
$modelType = $model->getType();
if ($model->getVoteId()) {
$this->voteRepository->delete($model);
}
if ($modelType === null || $modelType != $type) {
$model = $this->voteFactory->create();
$model->setIp($ip);
$model->setReviewId($reviewId);
$model->setType($type);
$this->voteRepository->save($model);
}
}
$votesForReview = $this->voteRepository->getVotesCount($reviewId);
$voted = $this->voteRepository->getVotesCount($reviewId, $ip);
$message = [
'success' => __('Success.'),
'data' => $votesForReview,
'voted' => $voted
];
}
} catch (LocalizedException $e) {
$message = ['error' => $e->getMessage()];
} catch (\Exception $e) {
$this->logger->error($e->getMessage());
}
}
$resultPage = $this->resultJsonFactory->create();
$resultPage->setHttpResponseCode(200);
$resultPage->setData($message);
return $resultPage;
}
}
<?php
namespace Joshine\Review\Controller\Ajax;
use Amasty\AdvancedReview\Model\ResourceModel\Images as ImagesModel;
use Joshine\Review\Helper\BlockHelper;
use Joshine\Review\Helper\ImageHelper;
use Magento\Catalog\Model\ProductFactory;
use Magento\Framework\App\Action\Context;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Controller\Result\JsonFactory;
use Magento\Review\Model\ResourceModel\Review\Collection;
use Magento\Review\Model\Review;
class ReviewInfo extends \Magento\Framework\App\Action\Action {
/**
* @var \Magento\Framework\Data\Form\FormKey\Validator
*/
private $formKeyValidator;
/**
* @var \Psr\Log\LoggerInterface
*/
private $logger;
/**
* @var \Magento\Framework\Json\EncoderInterface
*/
private $jsonEncoder;
/**
* @var JsonFactory
*/
private $resultJsonFactory;
private $reviewFactory;
/**
* @var ProductFactory
*/
private $productFactory;
/**
* @var \Magento\Review\Model\ResourceModel\Review\CollectionFactory
*/
private $reviewsColFactory;
/**
* @var \Magento\Framework\HTTP\PhpEnvironment\RemoteAddress
*/
private $remoteAddress;
public function __construct(
Context $context,
\Psr\Log\LoggerInterface $logger,
\Magento\Framework\Json\EncoderInterface $jsonEncoder,
JsonFactory $resultJsonFactory,
\Magento\Review\Model\ReviewFactory $reviewFactory,
ProductFactory $productFactory,
\Magento\Review\Model\ResourceModel\Review\CollectionFactory $reviewsColFactory,
\Magento\Framework\Data\Form\FormKey\Validator $formKeyValidator,
\Magento\Framework\HTTP\PhpEnvironment\RemoteAddress $remoteAddress
) {
parent::__construct($context);
$this->logger = $logger;
$this->jsonEncoder = $jsonEncoder;
$this->resultJsonFactory = $resultJsonFactory;
$this->reviewFactory = $reviewFactory;
$this->productFactory = $productFactory;
$this->reviewsColFactory = $reviewsColFactory;
$this->formKeyValidator = $formKeyValidator;
$this->remoteAddress = $remoteAddress;
}
/**
* @return \Magento\Framework\App\ResponseInterface|\Magento\Framework\Controller\Result\Json|\Magento\Framework\Controller\ResultInterface
*/
public function execute()
{
$message = [
'error' => __('Sorry. There is a problem with your Request.')
];
if ($this->getRequest()->isAjax()) {
try {
if (!$this->formKeyValidator->validate($this->getRequest())) {
throw new LocalizedException(
__('Form key is not valid. Please try to reload the page.')
);
}
$reviewId = $this->getRequest()->getParam('review_id');
$productId = $this->getRequest()->getParam('product_id');
if (empty($reviewId) || empty($productId) ) {
throw new LocalizedException(
__('request is valid.')
);
}
$reviewInfo = $this->getReviewInfo($reviewId, $productId);
$reviews = $this->getAllReview($productId);
$message = [
'success' => __('Success.'),
'data' => $reviews,
'review' => $reviewInfo,
'total' => count($reviews),
'offset' => $this->getOffset($reviews, $reviewInfo)
];
} catch (LocalizedException $e) {
$message = ['error' => $e->getMessage()];
} catch (\Exception $e) {
$this->logger->error($e->getMessage());
}
}
$resultPage = $this->resultJsonFactory->create();
$resultPage->setHttpResponseCode(200);
$resultPage->setData($message);
return $resultPage;
}
/**
* @param $productId
* @return \Magento\Review\Model\ResourceModel\Review\Collection
*/
public function getCollection($productId) : Collection
{
return $this->reviewsColFactory->create()->addStatusFilter(
\Magento\Review\Model\Review::STATUS_APPROVED
)->addEntityFilter(
'product',
$productId
);
}
private function getStoreId()
{
$storeManager = $this->_objectManager->get('\Magento\Store\Model\StoreManagerInterface');
return $storeManager->getStore()->getId();
}
public function getRatingPercent($reviewId)
{
$ratingCollection = $this->_objectManager->create('Magento\Review\Model\ResourceModel\Rating\Option\Vote\Collection')
->addFieldToFilter('review_id',$reviewId);
$data = $ratingCollection->getData();
return $data[0]['percent'] ?? 100;
}
public function getAllReview($productId)
{
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$db = $objectManager->get( 'Magento\Framework\App\ResourceConnection' )->getConnection();
$tblReview = $db->getTableName( 'review' );
$tblImages = $db->getTableName( 'joshine_review_images' );
$tblDetail = $db->getTableName( 'review_detail' );
$select = $db->select()
->distinct()
->from( [ 'review' => $tblReview ], ['*'] )
->join( [ 'images' => $tblImages ], 'review.review_id = images.review_id' )
->join( [ 'review_detail' => $tblDetail ], 'review.review_id = review_detail.review_id' )
->where( 'review.entity_pk_value = ?', $productId )
->where('review.status_id = ? ', Review::STATUS_APPROVED)
->group('review.review_id')->limit(50)->order('review.created_at DESC',);
$data = $db->fetchAll($select);
$res = [];
foreach ($data as $item) {
$_item['review_id'] = $item['review_id'];
$_item['size_fits'] = $item['size_fits'];
$_item['nickname'] = $item['nickname'];
$_item['title'] = $item['title'];
$_item['detail'] = $item['detail'];
$_item['created_at'] = $item['created_at'];
$_item['images'] = $this->getImages($item['review_id'], $productId);
$_item['rating'] = $this->getRatingPercent($item['review_id']);
$_item['helpful'] = $this->getVoteCount($item['review_id'])['plus'];
$_item['helpful_clicked'] = $this->getVoteCount($item['review_id'], $this->remoteAddress->getRemoteAddress())['plus'] > 0;
$res[] = $_item;
}
return $res;
}
public function getReviewInfo($reviewId, $productId)
{
$model = $this->reviewFactory->create()->load($reviewId);
$_item = [];
$_item['review_id'] = $reviewId;
$_item['size_fits'] = $model->getSize_fits();
$_item['nickname'] = $model->getNickname();
$_item['title'] = $model->getTitle();
$_item['detail'] = $model->getDetail();
$_item['created_at'] = $model->getCreatedAt();
$_item['images'] = $this->getImages($reviewId, $productId);
$_item['rating'] = $this->getRatingPercent($reviewId);
$_item['helpful'] = $this->getVoteCount($reviewId)['plus'];
$_item['helpful_clicked'] = $this->getVoteCount($reviewId, $this->remoteAddress->getRemoteAddress())['plus'] > 0;
return $_item;
}
public function getVoteCount($reviewId, $ip=null)
{
$repo = $this->_objectManager->get('Joshine\Review\Model\Repository\VoteRepository');
return $repo->getVotesCount($reviewId);
}
public function getImages($reviewId, $productId)
{
$collect = $this->_objectManager->create('Joshine\Review\Model\ResourceModel\Images\Collection')
->addFieldToSelect('*')
->addFieldToFilter('review_id', $reviewId);
/** @var $helper BlockHelper */
$helper = $this->_objectManager->get('Joshine\Review\Helper\BlockHelper');
$block = $helper->getReviewImagesBlock($reviewId, $productId);
$res = [];
foreach ($collect->getItems() as $item) {
$res['full'][] = ['url'=>$block->getFullImagePath($item), 'image_id'=>$item->getId()] ;
$res['thumb'][] = ['url'=>$block->getResizedImagePathByLimit($item, 100), 'image_id'=>$item->getId()] ; ;
}
return $res;
}
public function getOffset($allArr, $arr)
{
$offset = 0;
foreach ($allArr as $key => $value) {
$offset++;
if ($value['review_id'] == $arr['review_id']) {
break;
}
}
return $offset;
}
}
\ No newline at end of file
<?php
namespace Joshine\Review\Helper;
use Joshine\Review\Block\Images;
use Joshine\Review\Block\Review\ToolBar;
class BlockHelper implements \Magento\Framework\Data\CollectionDataSourceInterface
{
/**
* @var \Magento\Framework\View\Element\BlockFactory
*/
private $blockFactory;
/**
* @var \Magento\Framework\Stdlib\StringUtils
*/
private $stringUtils;
/**
* @var \Magento\Customer\Model\SessionFactory
*/
private $sessionFactory;
/**
* @var \Magento\Framework\Escaper
*/
private $escaper;
/**
* @var \Magento\Framework\UrlInterface
*/
private $urlBuilder;
/**
* @var \Magento\Framework\App\RequestInterface
*/
private $request;
public function __construct(
\Magento\Framework\View\Element\BlockFactory $blockFactory,
\Magento\Framework\Stdlib\StringUtils $stringUtils,
\Magento\Customer\Model\SessionFactory $sessionFactory,
\Magento\Framework\Escaper $escaper,
\Magento\Framework\UrlInterface $urlBuilder,
\Magento\Framework\App\RequestInterface $request
) {
$this->blockFactory = $blockFactory;
$this->stringUtils = $stringUtils;
$this->sessionFactory = $sessionFactory;
$this->escaper = $escaper;
$this->urlBuilder = $urlBuilder;
$this->request = $request;
}
public function getReviewImagesHtml($reviewId, $productId)
{
$html = '';
$block = $this->blockFactory
->createBlock(Images::class)
->setProductId($productId)
->setReviewId($reviewId);
if ($block) {
$html = $block->toHtml();
}
return $html;
}
public function getToolBarHtML()
{
return $this->blockFactory
->createBlock(ToolBar::class)
->toHtml();
}
public function getReviewImagesBlock($reviewId, $productId) : Images
{
return $this->blockFactory
->createBlock(Images::class)
->setProductId($productId)
->setReviewId($reviewId);
}
}
<?php
namespace Joshine\Review\Helper;
use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\Framework\UrlInterface;
class ImageHelper
{
const IMAGE_PATH = '/joshine/review/';
const IMAGE_TMP_PATH = '/joshine/review/tmp/';
/**
* @var \Magento\Store\Model\StoreManagerInterface
*/
private $storeManager;
/**
* @var \Magento\Framework\Filesystem
*/
private $filesystem;
/**
* @var \Magento\Framework\Image\AdapterFactory
*/
private $imageFactory;
/**
* @var \Magento\Framework\Filesystem\Io\File
*/
private $fileManager;
public function __construct(
\Magento\Store\Model\StoreManagerInterface $storeManager,
\Magento\Framework\Filesystem $filesystem,
\Magento\Framework\Image\AdapterFactory $imageFactory,
\Magento\Framework\Filesystem\Io\File $fileManager
) {
$this->storeManager = $storeManager;
$this->filesystem = $filesystem;
$this->imageFactory = $imageFactory;
$this->fileManager = $fileManager;
}
/**
* @param $name
* @return string
* @throws \Magento\Framework\Exception\NoSuchEntityException
*/
public function getFullPath($name)
{
$path = $this->storeManager->getStore()->getBaseUrl(UrlInterface::URL_TYPE_MEDIA);
$path = trim($path, '/');
$name = trim($name, '/');
$path .= '/joshine/review/';
return $path . $name;
}
/**
* @param $image
* @param int $width
* @param int $height
* @return string
* @throws \Exception
* @throws \Magento\Framework\Exception\NoSuchEntityException
*/
public function resize($image, $width = null, $height = null)
{
$mediaUrl = $this->storeManager->getStore()->getBaseUrl(
UrlInterface::URL_TYPE_MEDIA
);
$mediaUrl = trim($mediaUrl, '/');
$resizedURL = $mediaUrl . self::IMAGE_PATH . 'resized/' . $width . '/' . $image;
$absolutePath = $this->filesystem->getDirectoryRead(DirectoryList::MEDIA)
->getAbsolutePath(self::IMAGE_PATH)
. $image;
$imageResized = $this->filesystem->getDirectoryRead(DirectoryList::MEDIA)
->getAbsolutePath(self::IMAGE_PATH . 'resized/' . $width . '/')
. $image;
if ($this->fileManager->fileExists($imageResized)) {
return $resizedURL;
}
if (!$this->fileManager->fileExists($absolutePath)) {
return '';
}
//create image factory...
$imageResize = $this->imageFactory->create();
$imageResize->open($absolutePath);
$imageResize->constrainOnly(true);
$imageResize->keepTransparency(true);
$imageResize->keepFrame(false);
$imageResize->backgroundColor([255, 255, 255]);
$imageResize->keepAspectRatio(true);
$imageResize->resize($width, $height);
$destination = $imageResized;
$imageResize->save($destination);
return $resizedURL;
}
}
<?php
namespace Joshine\Review\Model;
use Joshine\Review\Helper\ImageHelper;
use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\MediaStorage\Model\File\Uploader;
class ImageUploader
{
/**
* @var \Magento\Framework\Filesystem
*/
private $filesystem;
/**
* @var \Magento\MediaStorage\Model\File\UploaderFactory
*/
private $fileUploaderFactory;
/**
* @var \Magento\Framework\Image\AdapterFactory
*/
private $adapterFactory;
/**
* @var \Magento\Framework\Filesystem\Io\File
*/
private $ioFile;
public function __construct(
\Magento\Framework\Filesystem $filesystem,
\Magento\MediaStorage\Model\File\UploaderFactory $fileUploaderFactory,
\Magento\Framework\Image\AdapterFactory $adapterFactory,
\Magento\Framework\Filesystem\Io\File $ioFile
) {
$this->filesystem = $filesystem;
$this->fileUploaderFactory = $fileUploaderFactory;
$this->adapterFactory = $adapterFactory;
$this->ioFile = $ioFile;
}
public function execute(array $file, $isTmp = false)
{
$path = $this->filesystem->getDirectoryRead(
DirectoryList::MEDIA
)->getAbsolutePath(
$isTmp ? ImageHelper::IMAGE_TMP_PATH : ImageHelper::IMAGE_PATH
);
$this->ioFile->checkAndCreateFolder($path);
/** @var $uploader Uploader */
$uploader = $this->fileUploaderFactory->create(
['fileId' => $file]
);
$imageAdapter = $this->adapterFactory->create();
$uploader->addValidateCallback('catalog_product_image', $imageAdapter, 'validateUploadFile');
$uploader->setAllowedExtensions(['jpg', 'jpeg', 'gif', 'png']);
$uploader->setAllowRenameFiles(true);
$uploader->setFilesDispersion(true);
$result = $uploader->save($path);
$this->trim($result);
return $result;
}
public function copy(string $imagePath)
{
$path = $this->filesystem->getDirectoryRead(
DirectoryList::MEDIA
)->getAbsolutePath(
ImageHelper::IMAGE_TMP_PATH
);
$from = $path . $imagePath;
if ($this->ioFile->fileExists($from)) {
$realPath = $this->filesystem->getDirectoryRead(
DirectoryList::MEDIA
)->getAbsolutePath(
ImageHelper::IMAGE_PATH
);
$counter = 0;
while ($this->ioFile->fileExists($realPath . $imagePath)) {
$imagePathArray = explode('.', $imagePath);
$imagePathArray[0] .= $counter++;
$imagePath = implode('.', $imagePathArray);
}
$this->ioFile->checkAndCreateFolder($this->ioFile->dirname($realPath . $imagePath));
if ($this->ioFile->mv($from, $realPath . $imagePath)) {
return $imagePath;
}
}
return false;
}
/**
* Fix for magento 2114 - setup upgrade
* @param $result
*/
private function trim($result)
{
if (isset($result['path']) && $result['file']) {
$path = rtrim($result['path'], '/') . $result['file'];
$this->ioFile->chmod($path, 0777);
}
}
}
<?php
namespace Joshine\Review\Model;
use Joshine\Review\Contract\ImagesInterface;
use Magento\Framework\Model\AbstractModel;
/**
* Class Images
* @package Amasty\AdvancedReview\Model
*/
class Images extends AbstractModel implements ImagesInterface
{
public function _construct()
{
$this->_init(\Joshine\Review\Model\ResourceModel\Images::class);
}
/**
* Returns image id field
*
* @return int|null
*/
public function getImageId()
{
return $this->getData(self::IMAGE_ID);
}
/**
* @param int $imageId
*
* @return $this
*/
public function setImageId($imageId)
{
$this->setData(self::IMAGE_ID, $imageId);
return $this;
}
/**
* Returns review id field
*
* @return int|null
*/
public function getReviewId()
{
return $this->getData(self::REVIEW_ID);
}
/**
* @param int $reviewId
*
* @return $this
*/
public function setReviewId($reviewId)
{
$this->setData(self::REVIEW_ID, $reviewId);
return $this;
}
/**
* Returns image path
*
* @return string|null
*/
public function getPath()
{
return $this->getData(self::PATH);
}
/**
* @param string $path
*
* @return $this
*/
public function setPath($path)
{
$this->setData(self::PATH, $path);
return $this;
}
}
<?php
namespace Joshine\Review\Model\Repository;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Framework\Exception\CouldNotDeleteException;
use Magento\Framework\Exception\CouldNotSaveException;
use Joshine\Review\Model\ResourceModel;
use Joshine\Review\Model\VoteFactory;
class VoteRepository
{
/**
* @var array
*/
private $vote = [];
/**
* @var ResourceModel\Vote
*/
private $voteResource;
/**
* @var VoteFactory
*/
private $voteFactory;
/**
* @var ResourceModel\Vote\CollectionFactory
*/
private $collectionFactory;
public function __construct(
\Joshine\Review\Model\ResourceModel\Vote $voteResource,
\Joshine\Review\Model\VoteFactory $voteFactory,
\Joshine\Review\Model\ResourceModel\Vote\CollectionFactory $collectionFactory
) {
$this->voteResource = $voteResource;
$this->voteFactory = $voteFactory;
$this->collectionFactory = $collectionFactory;
}
/**
* {@inheritdoc}
*/
public function save($vote)
{
if ($vote->getVoteId()) {
$vote = $this->get($vote->getVoteId())->addData($vote->getData());
}
try {
$this->voteResource->save($vote);
$this->vote[$vote->getVoteId()] = $vote;
} catch (\Exception $e) {
if ($vote->getVoteId()) {
throw new CouldNotSaveException(
__('Unable to save vote with ID %1. Error: %2', [$vote->getVoteId(), $e->getMessage()])
);
}
throw new CouldNotSaveException(__('Unable to save new vote. Error: %1', $e->getMessage()));
}
return $vote;
}
/**
* {@inheritdoc}
*/
public function get($voteId)
{
if (!isset($this->vote[$voteId])) {
$vote = $this->voteFactory->create();
$this->voteResource->load($vote, $voteId);
if (!$vote->getVoteId()) {
throw new NoSuchEntityException(__('Vote with specified ID "%1" not found.', $voteId));
}
$this->vote[$voteId] = $vote;
}
return $this->vote[$voteId];
}
public function getByIdAndIp($reviewId, $ip)
{
$vote = $this->voteFactory->create();
$collection = $this->collectionFactory->create()
->addFieldToFilter('review_id', $reviewId)
->addFieldToFilter('ip', $ip);
$collection->getSelect()->limit(1);
if ($collection->getSize() > 0) {
$vote = $collection->getFirstItem();
}
return $vote;
}
/**
* {@inheritdoc}
*/
public function delete($vote)
{
try {
$this->voteResource->delete($vote);
unset($this->vote[$vote->getId()]);
} catch (\Exception $e) {
if ($vote->getVoteId()) {
throw new CouldNotDeleteException(
__('Unable to remove vote with ID %1. Error: %2', [$vote->getVoteId(), $e->getMessage()])
);
}
throw new CouldNotDeleteException(__('Unable to remove vote. Error: %1', $e->getMessage()));
}
return true;
}
/**
* {@inheritdoc}
*/
public function deleteById($voteId)
{
$model = $this->get($voteId);
$this->delete($model);
return true;
}
/**
* @param $reviewId
* @param null $ip
* @return array
*/
public function getVotesCount($reviewId, $ip = null)
{
$result = [
'plus' => 0,
'minus' => 0
];
$collection = $this->collectionFactory->create()
->addFieldToFilter('review_id', $reviewId);
if ($ip) {
$collection->addFieldToFilter('ip', $ip);
}
foreach ($collection as $vote) {
if ($vote->getType() == '1') {
$result['plus'] = ++$result['plus'];
} else {
$result['minus'] = ++$result['minus'];
}
}
return $result;
}
public function getVoteModel()
{
return $this->voteFactory->create();
}
/**
* @return array
*/
public function getVoteIpKeys()
{
return $this->collectionFactory->create()->getVoteIpKeys();
}
}
<?php
namespace Joshine\Review\Model\ResourceModel;
class Images extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
{
const TABLE_NAME = 'joshine_review_images';
/**
* Model Initialization
*
* @return void
*/
protected function _construct()
{
$this->_init(self::TABLE_NAME, 'image_id');
}
}
<?php
namespace Joshine\Review\Model\ResourceModel\Images;
use Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection;
/**
* Class Collection
* @package Amasty\AdvancedReview\Model\ResourceModel\Images
*/
class Collection extends AbstractCollection
{
public function _construct()
{
$this->_init(
\Joshine\Review\Model\Images::class,
\Joshine\Review\Model\ResourceModel\Images::class
);
}
/**
* @return array
*/
public function getImageKeys(): array
{
$this->getSelect()->columns('CONCAT(review_id,path) as image_key');
return $this->getColumnValues('image_key');
}
}
<?php
namespace Joshine\Review\Model\ResourceModel;
class Vote extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
{
const TABLE_NAME = 'joshine_review_vote';
protected function _construct()
{
$this->_init(self::TABLE_NAME, 'vote_id');
}
}
<?php
namespace Joshine\Review\Model\ResourceModel\Vote;
use Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection;
class Collection extends AbstractCollection
{
public function _construct()
{
$this->_init(
\Joshine\Review\Model\Vote::class,
\Joshine\Review\Model\ResourceModel\Vote::class
);
}
/**
* @return array
*/
public function getVoteIpKeys(): array
{
$this->getSelect()->columns('CONCAT(review_id,ip) as vote_key');
return $this->getColumnValues('vote_key');
}
}
<?php
namespace Joshine\Review\Model;
use Magento\Framework\Model\AbstractModel;
class Vote extends AbstractModel
{
const VOTE_ID = 'vote_id';
const REVIEW_ID = 'review_id';
const TYPE = 'type';
const IP = 'ip';
public function _construct()
{
$this->_init(\Joshine\Review\Model\ResourceModel\Vote::class);
}
/**
* Returns vote id field
*
* @return int|null
*/
public function getVoteId()
{
return $this->getData(self::VOTE_ID);
}
/**
* @param int $voteId
*
* @return $this
*/
public function setVoteId($voteId)
{
$this->setData(self::VOTE_ID, $voteId);
return $this;
}
/**
* Returns review id field
*
* @return int|null
*/
public function getReviewId()
{
return $this->getData(self::REVIEW_ID);
}
/**
* @param int $reviewId
*
* @return $this
*/
public function setReviewId($reviewId)
{
$this->setData(self::REVIEW_ID, $reviewId);
return $this;
}
/**
* Returns vote type
*
* @return int|null
*/
public function getType()
{
return $this->getData(self::TYPE);
}
/**
* @param int $type
*
* @return $this
*/
public function setType($type)
{
$this->setData(self::TYPE, $type);
return $this;
}
/**
* Returns vote type
*
* @return string|null
*/
public function getIp()
{
return $this->getData(self::IP);
}
/**
* @param string $ip
*
* @return $this
*/
public function setIp($ip)
{
$this->setData(self::IP, $ip);
return $this;
}
}
<?php
namespace Joshine\Review\Plugin\Review\Block;
use Magento\Review\Block\Form as MagentoForm;
class Form
{
/**
* @var \Joshine\Review\Helper\BlockHelper
*/
private $blockHelper;
public function __construct(
\Joshine\Review\Helper\BlockHelper $blockHelper
) {
$this->blockHelper = $blockHelper;
}
public function afterToHtml(
MagentoForm $subject,
$result
) {
$search = '</fieldset>';
$replace = $this->getImageUploadHtml() . $search;
$result = substr_replace($result, $replace, strrpos($result, $search), strlen($search));
$searchForm = 'data-role="product-review-form"';
return str_replace($searchForm, $searchForm . ' enctype="multipart/form-data" ', $result);
}
protected function getImageUploadHtml(): string
{
return sprintf(
'<div class="field review-field-image %s">
<label class="label">%s</label><div class="control">
<input class="joshine-input" name="review_images[]" accept="image/*" multiple %s type="file" title="%s">
</div></div>',
'',
__('Add your photo'),
'',
__('Add your photo')
);
}
}
\ No newline at end of file
<?php
namespace Joshine\Review\Plugin\Review\Model;
use Joshine\Review\Model\ImageUploader;
use Magento\Framework\Exception\CouldNotSaveException;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Review\Block\Form as MagentoForm;
use Magento\Framework\Exception\LocalizedException;
use Magento\Review\Model\Review as MagentoReview;
class Review {
/**
* @var \Magento\Framework\App\RequestInterface
*/
private $request;
/**
* @var ImageUploader
*/
private $imageUploader;
/**
* @var \Joshine\Review\Model\ImagesFactory
*/
private $imagesFactory;
private $imagesResource;
private $messageManager;
public function __construct(
\Magento\Framework\App\RequestInterface $request,
ImageUploader $imageUploader,
\Joshine\Review\Model\ImagesFactory $imagesFactory,
\Magento\Framework\Message\ManagerInterface $messageManager,
\Joshine\Review\Model\ResourceModel\Images $imagesResource
)
{
$this->messageManager = $messageManager;
$this->imagesFactory = $imagesFactory;
$this->imageUploader = $imageUploader;
$this->request = $request;
$this->imagesResource = $imagesResource;
}
public function afterAggregate(
MagentoReview $subject,
$result
) {
$this->uploadReviewImages($subject);
return $result;
}
public function uploadReviewImages(MagentoReview $subject) {
$files = $this->request->getFiles('review_images');
$reviewId = $subject->getReviewId();
//todo:: 用helper方法做是否允许上传图片的判断
if ($files && $reviewId) {
foreach ($files as $fileId => $file) {
if (UPLOAD_ERR_OK == $file['error']) {
$this->uploadImage($file, $reviewId);
}
}
}
}
public function uploadImage($file, $reviewId) {
try {
$result = $this->imageUploader->execute($file);
$this->saveImage($result, $reviewId);
} catch (\Exception $e) {
$this->messageManager->addErrorMessage(__('An error occurred while uploading the image.'));
throw new LocalizedException(__($e->getMessage()));
}
return $this;
}
/**
* @throws NoSuchEntityException
* @throws CouldNotSaveException
*/
private function saveImage($result, $reviewId)
{
/** @var \Joshine\Review\Model\Images $model */
$model = $this->imagesFactory->create();
$model->setReviewId($reviewId);
$model->setPath($result['file']);
if ($model->getImageId()) {
$model = $this->getFullImage($model->getImageId())->addData($model->getData());
}
try {
$this->imagesResource->save($model);
} catch (\Exception $e) {
if ($model->getImageId()) {
throw new CouldNotSaveException(
__('Unable to save image with ID %1. Error: %2', [$model->getImageId(), $e->getMessage()])
);
}
throw new CouldNotSaveException(__('Unable to save new image. Error: %1', $e->getMessage()));
}
}
private function getFullImage($imageId) {
$image = $this->imagesFactory->create();
$this->imagesResource->load($image, $imageId);
if (!$image->getImageId()) {
throw new NoSuchEntityException(__('Rule with specified ID "%1" not found.', $imageId));
}
return $image;
}
}
<?php
/**
* @author Amasty Team
* @copyright Copyright (c) 2021 Amasty (https://www.amasty.com)
* @package Amasty_AdvancedReview
*/
namespace Joshine\Review\Setup;
use Magento\Framework\Setup\InstallSchemaInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\SchemaSetupInterface;
use Magento\Framework\DB\Ddl\Table;
/**
* Class InstallSchema
* @package Amasty\AdvancedReview\Setup
*/
class InstallSchema implements InstallSchemaInterface
{
/**
* @param SchemaSetupInterface $setup
* @param ModuleContextInterface $context
* @throws \Zend_Db_Exception
*/
public function install(SchemaSetupInterface $setup, ModuleContextInterface $context)
{
$setup->startSetup();
/** 建新表 */
$this->createModuleTables($setup);
$this->addReviewColumns($setup);
$setup->endSetup();
}
/**
* @param SchemaSetupInterface $installer
* @throws \Zend_Db_Exception
*/
private function createModuleTables(SchemaSetupInterface $installer)
{
$table = $installer->getConnection()
->newTable($installer->getTable('joshine_review_images'))
->addColumn(
'image_id',
Table::TYPE_BIGINT,
null,
['identity' => true, 'unsigned' => true, 'nullable' => false, 'primary' => true],
'Review Id'
)
->addColumn(
'review_id',
Table::TYPE_BIGINT,
null,
['default' => 0, 'nullable' => false],
'Review table id'
)
->addColumn(
'path',
Table::TYPE_TEXT,
'2M',
[],
'Image path'
)
->setComment('用户评价图片地址保存表');
$installer->getConnection()->createTable($table);
$tableVote = $installer->getConnection()
->newTable($installer->getTable('joshine_review_vote'))
->addColumn(
'vote_id',
Table::TYPE_INTEGER,
null,
['identity' => true, 'unsigned' => true, 'nullable' => false, 'primary' => true],
'Vote Id'
)
->addColumn(
'review_id',
Table::TYPE_INTEGER,
null,
['default' => 0, 'nullable' => false],
'Review table id'
)
->addColumn(
'type',
Table::TYPE_SMALLINT,
null,
[],
'type'
)
->addColumn(
'ip',
Table::TYPE_TEXT,
256,
[],
'ip'
)
->setComment('用户评价点赞记录表');
$installer->getConnection()->createTable($tableVote);
}
/**
* review表添加字段
* @param SchemaSetupInterface $installer
*/
private function addReviewColumns(SchemaSetupInterface $installer)
{
/**
* 添加verified_buyer字段,用于后续搜索
*/
$installer->getConnection()->addColumn(
$installer->getTable('review'),
'verified_buyer',
[
'type' => \Magento\Framework\DB\Ddl\Table::TYPE_SMALLINT,
'nullable' => true,
'default' => 0,
'comment' => 'Verified Buyer'
]
);
}
}
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<type name="Magento\Review\Model\Review">
<plugin name="Joshine_Review::save-images" type="Joshine\Review\Plugin\Review\Model\Review" />
</type>
</config>
<?xml version="1.0"?>
<!--
/**
* @author Amasty Team
* @copyright Copyright (c) 2021 Amasty (https://www.amasty.com)
* @package Amasty_AdvancedReview
*/
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<type name="Magento\Review\Block\Form">
<plugin name="Joshine_Review::add-file-upload-control" type="Joshine\Review\Plugin\Review\Block\Form" />
</type>
</config>
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd">
<router id="standard">
<route id="joshine_review" frontName="joshine_review">
<module name="Joshine_Review" />
</route>
</router>
</config>
<?xml version="1.0"?>
<!--
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
<module name="Joshine_Review" setup_version="0.0.4">
<sequence>
<module name="Magento_Review"/>
</sequence>
</module>
</config>
<?php <?php
/** /**
* Copyright © 2015 Ihor Vansach (ihor@magefan.com). All rights reserved. * Copyright © Magento, Inc. All rights reserved.
* See LICENSE.txt for license details (http://opensource.org/licenses/osl-3.0.php). * See COPYING.txt for license details.
*
* Glory to Ukraine! Glory to the heroes!
*/ */
\Magento\Framework\Component\ComponentRegistrar::register( \Magento\Framework\Component\ComponentRegistrar::register(
\Magento\Framework\Component\ComponentRegistrar::MODULE, \Magento\Framework\Component\ComponentRegistrar::MODULE,
'Meetanshi_FacebookChat', 'Joshine_Review',
__DIR__ __DIR__
); );
<?xml version="1.0"?>
<!--
/**
* @author Amasty Team
* @copyright Copyright (c) 2021 Amasty (https://www.amasty.com)
* @package Amasty_AdvancedReview
*/
-->
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<body>
<block name="joshine.review.comments" class="Joshine\Review\Block\Comment\Container">
<block name="joshine.review.comments.messages" as="messages" class="Joshine\Review\Block\Comment\CommentsList">
<block name="joshine.review.comments.message" as="message" class="Joshine\Review\Block\Comment\Comment" />
</block>
<block name="joshine.review.comments.form" as="form" class="Amasty\AdvancedReview\Block\Comment\Form" />
</block>
</body>
</page>
<?xml version="1.0"?>
<!--
/**
* @author Amasty Team
* @copyright Copyright (c) 2021 Amasty (https://www.amasty.com)
* @package Amasty_AdvancedReview
*/
-->
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<body>
<referenceContainer name="content">
<block class="Magento\Framework\View\Element\Template"
template="Joshine_Review::review-js.phtml"
name="joshine.review.toolbar.js"
after="-"/>
</referenceContainer>
</body>
</page>
<?xml version="1.0"?>
<!--
/**
* @author Amasty Team
* @copyright Copyright (c) 2021 Amasty (https://www.amasty.com)
* @package Amasty_AdvancedReview
*/
-->
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<head>
<css src="Joshine_Review::css/_review.css"/>
</head>
</page>
\ No newline at end of file
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<body>
<referenceBlock name="product.info.product_additional_data" remove="true" />
<referenceBlock class="Magento\Theme\Block\Html\Pager" name="product_review_list.toolbar" remove="true"/>
<referenceContainer name="root">
<block class="Joshine\Review\Block\Review\Product\View\ListView"
name="Joshine.product.info.product_additional_data"
template="Joshine_Review::product/view/list.phtml"
before="product_review_list.toolbar">
<arguments>
<argument name="joshine-review-helper" xsi:type="object">Joshine\Review\Helper\BlockHelper</argument>
</arguments>
</block>
<block name="joshine.review.js" class="Joshine\Review\Block\JsInit" ifconfig="joshine_review/comments/enabled" />
</referenceContainer>
</body>
</page>
var config = {
paths: {
"joshineReviewHandler": "Joshine_Review/js/review/handler",
},
};
\ No newline at end of file
<?php
/**
* @var $block \Joshine\Review\Block\Components\Pager
*/
?>
<?php if ($block->getCollection()->getSize() && $block->getLastPageNum() > 1): ?>
<div class="joshine-reviews-page">
<div class="joshine-pagination joshine-pagination-left" joshine-review-js="reviews-pages-<?= $block->escapeHtml($block->getProductId()); ?>" >
<?php if (!$block->isFirstPage()): ?>
<span class="joshine-pagination-btn joshine-pagination-prev"
data-href="<?= $block->escapeUrl($block->getPreviousPageUrl()); ?>"
>
<
</span>
<?php endif; ?>
<?php if ($block->canShowFirst()): ?>
<span class="joshine-pagination-btn joshine-pagination-hover"
data-href="<?= $block->escapeUrl($block->getFirstPageUrl()); ?>"
>
1
</span>
<?php endif; ?>
<?php if ($block->canShowPreviousJump()): ?>
<span class="joshine-pagination-btn joshine-pagination-hover"
data-href="<?= $block->escapeUrl($block->getPreviousJumpUrl()); ?>"
>
...
</span>
<?php endif; ?>
<?php foreach ($block->getFramePages() as $page): ?>
<?php if ($block->isPageCurrent($page)): ?>
<span class="joshine-pagination-btn joshine-pagination-hover active">
<?= $block->escapeHtml($page); ?>
</span>
<?php else: ?>
<span class="joshine-pagination-btn joshine-pagination-hover"
data-href="<?= $block->escapeUrl($block->getPageUrl($page)); ?>"
>
<?= $block->escapeHtml($page) ?>
</span>
<?php endif; ?>
<?php endforeach; ?>
<?php if ($block->canShowNextJump()): ?>
<span class="joshine-pagination-btn joshine-pagination-hover"
data-href="<?= $block->escapeUrl($block->getNextJumpUrl()) ?>"
>
...
</span>
<?php endif; ?>
<?php if ($block->canShowLast()): ?>
<span class="joshine-pagination-btn joshine-pagination-hover"
data-href="<?= $block->escapeUrl($block->getLastPageUrl()) ?>"
>
<?= $block->escapeHtml($block->getLastPageNum()) ?>
</span>
<?php endif; ?>
<?php if (!$block->isLastPage()): ?>
<span class="joshine-pagination-btn joshine-pagination-next joshine-pagination-hover"
data-href="<?= $block->escapeUrl($block->getNextPageUrl()) ?>">
>
</span>
<?php endif; ?>
</div>
</div>
<?php endif ?>
\ No newline at end of file
<?php
/** @var \Joshine\Review\Block\Images $block */
$collection = $block->getCollection();
?>
<?php if ($collection->getSize()): ?>
<?php foreach ($collection as $item): ?>
<div class="joshine-review-pic-item joshine-col-xs-4" data-img-src="<?= $block->getFullImagePath($item) ?>"
data-image-id="<?= $item->getId(); ?>"
data-product-id="<?= $block->getProductId() ?>"
data-reviews-id="<?= $block->getReviewId() ?>">
<img class="review-image rounded" src="<?= $block->getResizedImagePath($item) ?>"/>
</div>
<?php endforeach; ?>
<?php endif;?>
<?php
//rewrite file Magento_Review::product/view/list.phtml
// phpcs:ignoreFile
/** @var Joshine\Review\Block\Review\Product\View\ListView $block */
$displayedCollection = $block->getLimitedReviewsCollection();
$_items = $displayedCollection->getItems();
$format = $block->getDateFormat() ?: \IntlDateFormatter::MEDIUM;
$block->setSummary($block->getProduct(), $displayedCollection);
/** @var Joshine\Review\Helper\BlockHelper $helper */
$helper = $block->getData('joshine-review-helper');
$rating = $block->getRatingSummary();
$count = $block->getReviewsCount();
$t = [ 1 => __('Small') , 2 => __('True to Size') ,3 => __('Large')];
$imagesBlock = $helper->getReviewImagesBlock(15, $block->getProductId());
?>
<style>
.joshine-review-container-mobile .joshine-review .ave-rate {
display: flex;
align-items: center;
}
.joshine-review-container-mobile .joshine-review .fit-item {
display: flex;
align-items: center;
}
.joshine-review-container-mobile .joshine-review .fit-name {
width: 30%;
}
.joshine-review-container-mobile .joshine-review .joshine-process {
width: 60%;
}
.joshine-review-container-mobile .joshine-review .joshine-review-averate {
padding: .5em;
}
.joshine-review-container-mobile .joshine-review .add-new-box {
margin: .5em 0 .5em 0;
}
</style>
<div class="joshine-review-container joshine-clearfix joshine-hidden-md joshine-hidden-sm joshine-hidden-xs">
<h1 class="joshine-review-title">
Customer Reviews
</h1>
<div class="joshine-review">
<div class="joshine-review-averate joshine-bg-silver">
<div class="joshine-review-item joshine-review-rating-box">
<div class="name">
Average Rating
</div>
<div class="ave-rate">
<div class="joshine-rating-container">
<div class="joshine-rating-starts" style="width: <?= $rating ?>%;"></div>
</div>
<div class="ave-rate-total"><?= $block->getRatingSummaryValue() ?></div>
</div>
</div>
<div class="joshine-review-item">
<div class="name">
Did the item fit well?
</div>
<?php foreach ($block->getDetailedSummary() as $key => $detail) : ?>
<div class="fit-item">
<div class="fit-name joshine-font-mini">
<?= $t[$key] ?>
</div>
<div class="joshine-process">
<div class="joshine-process-active" style="width: <?= /* @noEscape */ $count ? round($detail / $count * 100) : 0 ?>%;"></div>
</div>
</div>
<?php endforeach; ?>
</div>
<div class="joshine-review-item add-new-box">
<a href="#review-form" title="Write a review" class="add-new-button joshine-btn joshine-btn-dark joshine-review-add-btn">
Write a review
</a>
</div>
</div>
<!-- 筛选框 -->
<?= $helper->getToolBarHtML() ?>
<div class="joshine-review-list joshine-clearfix">
<?php if (count($_items) > 0): ?>
<?php foreach ($_items as $review) : ?>
<div class="joshine-review-list-item ">
<div class="joshine-review-list-item-left">
<div class="joshine-review-nickname joshine-font-c-deepin joshine-font-size-default joshine-font-w-bolder ">
<?= $block->escapeHtml($review->getNickname()) ?>
</div>
<input type="hidden" id="modal-current-review-id">
<div class="joshine-review-date joshine-font-c-darkgray joshine-font-mini-plus">
<?= $block->escapeHtml($block->formatDate($review->getCreatedAt(), $format)) ?>
</div>
</div>
<div class="joshine-review-list-item-mid joshine-pull-left">
<div class="joshine-review-detail">
<div class="joshine-review-des-title joshine-font-size-default joshine-font-c-deepin">
<?= $block->escapeHtml($review->getTitle()) ?>
</div>
<div class="joshine-rating-container joshine-rating-small">
<?php if (count($review->getRatingVotes())) : ?>
<?php foreach ($review->getRatingVotes() as $_vote) : ?>
<div class="joshine-rating-starts" style="width: <?= $block->escapeHtml($_vote->getPercent()) ?>%;"></div>
<?php endforeach; ?>
<?php endif; ?>
</div>
<div class="joshine-review-des">
<?= $block->escapeHtml($review->getDetail()); ?>
</div>
<div class="joshine-foot-mark rate-fit">
<div class="rate-fit-item">
<?php $fitsValue = $review->getSizeFits(); ?>
<span><strong class="joshine-font-w-bolder">Size Fits:</strong> <?= $t[$fitsValue] ?></span>
</div>
</div>
</div>
<div class="joshine-review-pics">
<?= /* @noEscape */ $helper->getReviewImagesHtml($review->getId(), $block->getProductId()) ?>
</div>
</div>
<div class="joshine-review-list-item-right">
<div class="joshine-review-helpful joshine-font-c-darkgray joshine-font-text">
<span>helpful</span>
<span class="joshine-like <?= $block->getPlusVotedClass($review->getId()); ?>" data-review-id="<?= $review->getId(); ?>">
<input name="form_key" type="hidden" value="<?= /* @noEscape */ $block->getFormKey(); ?>" />
</span>
<span class="joshine-review-likes-count"><?= $block->getPlusReview($review->getId()); ?></span>
</div>
</div>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
<?php if ($block->getPagerHtml()) : ?>
<div class=" toolbar toolbar-customized "><?= $block->getPagerHtml() ?></div>
<?php endif ?>
</div>
</div>
<div class="joshine-review-container-mobile joshine-hidden-lg">
<h4 class="joshine-review-title joshine-font-w-bolder">
Customer Reviews
</h4>
<div class="joshine-review">
<div class="joshine-review-averate joshine-bg-silver joshine-clearfix">
<div class="joshine-review-item joshine-review-rating-box">
<div class="name">
Average Rating
</div>
<div class="ave-rate row">
<div class="joshine-rating-container joshine-col-xs-8">
<div class="joshine-rating-starts" style="width: <?= $rating ?>%;"></div>
</div>
<h2 class="ave-rate-total" style="margin-top: 0; margin-bottom: .35em;"><?= $block->getRatingSummaryValue() ?></h2>
</div>
</div>
<div class="joshine-review-item">
<div class="name">
Did the item fit well?
</div>
<?php foreach ($block->getDetailedSummary() as $key => $detail) : ?>
<div class="fit-item">
<span class="fit-name joshine-font-mini">
<?= $t[$key] ?>
</span>
<div class="joshine-process">
<div class="joshine-process-active" style="width: <?= /* @noEscape */ $count ? round($detail / $count * 100) : 0 ?>%;"></div>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
<div class="joshine-review-item add-new-box">
<a href="#review-form" title="Write a review" class="add-new-button joshine-btn joshine-btn-dark joshine-review-add-btn">
Write a review
</a>
</div>
<div class="joshine-review-list joshine-clearfix">
<?php if (count($_items) > 0): ?>
<?php foreach ($_items as $review) : ?>
<div class="joshine-review-list-item row joshine-clearfix">
<div class="row">
<span class="joshine-review-nickname" style="font-size: .9em;">
<?= $block->escapeHtml($review->getNickname()) ?>
</span>
&nbsp;
<span class="joshine-review-date joshine-font-c-darkgray joshine-font-mini-plus">
<?= $block->escapeHtml($block->formatDate($review->getCreatedAt(), $format)) ?>
</span>
</div>
<div class="row">
<div class="joshine-rating-container joshine-rating-small joshine-col-xs-8">
<?php if (count($review->getRatingVotes())) : ?>
<?php foreach ($review->getRatingVotes() as $_vote) : ?>
<div class="joshine-rating-starts" style="width: <?= $block->escapeHtml($_vote->getPercent()) ?>%;"></div>
<?php endforeach; ?>
<?php endif; ?>
</div>
<div class="joshine-pull-right">
<div class="joshine-review-helpful joshine-font-c-darkgray joshine-font-text">
<span class="joshine-like <?= $block->getPlusVotedClass($review->getId()); ?>" data-review-id="<?= $review->getId(); ?>">
<input name="form_key" type="hidden" value="<?= /* @noEscape */ $block->getFormKey(); ?>" />
</span>
<span class="joshine-review-likes-count" data-review-id="<?= $review->getId(); ?>"><?= $block->getPlusReview($review->getId()); ?></span>
</div>
</div>
</div>
<div class="row joshine-foot-mark">
<?php $fitsValue = $review->getSizeFits(); ?>
<span><strong class="joshine-font-w-bolder">Size Fits:</strong> <?= $t[$fitsValue] ?></span>
</div>
<div class="row joshine-font-w-bolder" style="font-size: .9em;">
<?= $block->escapeHtml($review->getTitle()); ?>
</div>
<div class="row joshine-font-c-darkgray" style="font-size: .8em;">
<?= $block->escapeHtml($review->getDetail()); ?>
</div>
<div class="row joshine-review-pics">
<?= /* @noEscape */ $helper->getReviewImagesHtml($review->getId(), $block->getProductId()) ?>
</div>
</div>
<hr>
<?php endforeach; ?>
<?= $block->getPagerHtml() ?>
<?php endif; ?>
</div>
</div>
</div>
<style>
.joshine-model-warp {
z-index: 1000;
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
.joshine-model-warp .joshine-mask {
z-index: 1000;
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: rgba(0,0,0,.8);
}
.joshine-model-warp .j-modal {
overflow: hidden;
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 2000;
-webkit-overflow-scrolling: touch;
outline: 0;
}
.j-modal .j-modal-dialog {
position: relative;
width: 530px;
margin: 30px auto;
}
.j-modal .j-modal-content {
position: relative;
padding: 40px 50px 30px;
background: #fff;
background-clip: padding-box;
background-position: center;
background-size: 100% 100%;
background-repeat: no-repeat;
}
.j-modal .j-modal-header {
text-align: center;
max-height: 60em;
overflow-y: auto;
}
.joshine-model-warp .j-modal .j-modal-dialog {
width: 1050px;
}
.joshine-close {
position: absolute;
top: 8px;
right: 8px;
width: 24px;
height: 24px;
line-height: 24px;
font-size: 14px;
cursor: pointer;
}
.j-modal .j-modal-body {
position: relative;
line-height: 1.2;
}
.joshine-review-des-head {
display: flex;
align-items: center;
justify-content:space-between;
}
.modal-reviews-details__des-wrap {
margin-left: 24px;
}
.modal-reviews-details__des-wrap div {
margin-bottom: 24px;
}
.modal-reviews-details__image-wrap .modal-reviews-details__image-swiper {
padding: 10px;
height: 600px;
}
.modal-reviews-details__image-thumbs {
margin-top: 14px;
overflow: hidden;
}
.image-thumbs-list {
position: relative;
width: 100%;
height: 100%;
z-index: 1;
display: -webkit-box;
display: flex;
-webkit-transition-property: -webkit-transform;
transition-property: -webkit-transform;
transition-property: transform;
transition-property: transform,-webkit-transform;
-webkit-box-sizing: content-box;
box-sizing: content-box;
}
.swiper-item {
position: relative;
margin-right: 2px;
}
.swiper-item img {
left: 50%;
-webkit-transform: translate3d(-50%,-50%,0);
transform: translate3d(-50%,-50%,0);
display: block;
position: absolute;
top: 50%;
max-width: 100%;
max-height: 100%;
}
.image-thumbs-list-item {
margin-right: 4px;
height: 50px;
width: 45px;
overflow: hidden;
-webkit-flex-shrink: 0;
-ms-flex: 0 0 auto;
flex-shrink: 0;
position: relative;
cursor: pointer;
}
.image-thumbs-list-item.active {
border: 1px solid #000;
}
.image-thumbs-list-item .modal-reviews-details__image-thumbs-image img {
width: 100%;
display: block;
position: absolute;
top: 50%;
left: 50%;
-webkit-transform: translate3d(-50%,-50%,0);
transform: translate3d(-50%,-50%,0);
}
.swiper-wrapper {
display: flex;
position: relative;
width: 100%;
height: 100%;
z-index: 1;
display: -webkit-box;
display: flex;
-webkit-transition-property: -webkit-transform;
transition-property: -webkit-transform;
-webkit-box-sizing: content-box;
box-sizing: content-box;
transform: translate3d(0,0,0);
}
.swiper-wrapper .swiper-item {
-webkit-flex-shrink: 0;
-ms-flex: 0 0 auto;
flex-shrink: 0;
width: 100%;
height: 100%;
position: relative;
}
.swiper-container {
margin-left: auto;
margin-right: auto;
position: relative;
overflow: hidden;
z-index: 1;
height: 100%;
width: 100%;
}
.modal-reviews-details__image-swiper .swiper-button-prev {
height: 100%;
width: 50%;
position: absolute;
top: 0;
margin-top: 0;
left: 0;
right: auto;
z-index: 12;
cursor: url(static/frontend/Joshine/breeze/en_US/Joshine_Review/img/favicon-pre.ico), auto;
}
.modal-reviews-details__image-swiper .swiper-button-next {
height: 100%;
width: 50%;
position: absolute;
top: 0;
margin-top: 0;
right: 0;
left: auto;
z-index: 12;
cursor: url(static/frontend/Joshine/breeze/en_US/Joshine_Review/img/favicon-next.ico), auto;
}
.modal-reviews-details__image-thumbs-image{
position: relative;
height: 100%;
overflow: hidden;
border: 1px solid #efefef;
}
</style>
<div class="joshine-model-warp joshine-hidden">
<div class="joshine-mask"></div>
<div class="j-modal">
<div class="j-modal-dialog">
<div class="j-modal-content">
<div class="j-modal-header">
<span class="joshine-close">x</span>
</div>
<div class="j-modal-body">
<div class="modal-reviews-details row">
<div class="modal-reviews-details__image-wrap joshine-col-xs-7 joshine-text-center">
<div class="modal-reviews-details__image-swiper joshine-bg-silver">
<div class="swiper-container">
<div class="swiper-button-prev"></div>
<div class="swiper-wrapper image-full-wrapper" data-review-js="image-full-wrapper">
</div>
<div class="swiper-button-next"></div>
</div>
</div>
<div class="modal-reviews-details__image-thumbs">
<div class="image-thumbs-list" data-review-js="image-thumbs-wrapper">
</div>
</div>
</div>
<div class="modal-reviews-details__des-wrap joshine-col-xs-4">
<div class="joshine-review-des-head">
<span class="joshine-review-nickname" data-review-js="nickname" style="font-size: .9em;">
</span>
<div class="joshine-rating-container joshine-rating-small" style="margin-bottom: 3px;">
<div class="joshine-rating-starts" style="width: 100%;" data-review-js="rating"></div>
</div>
<span class="joshine-review-date joshine-font-c-darkgray joshine-font-mini-plus joshine-pull-right" data-review-js="date">
</span>
</div>
<div class="joshine-review-des-title joshine-font-w-bolder joshine-font-text joshine-font-c-deepin" data-review-js="title">
</div>
<div class="joshine-review-des-text joshine-font-c-darkgray joshine-font-mini-plus" data-review-js="detail">
</div>
<div class="joshine-foot-mark rate-fit">
<div class="rate-fit-item">
<span><strong class="joshine-font-w-bolder">Size Fits:</strong> <span data-review-js="fits"></span></span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script type="text/javascript">
require([
'jquery','ko'
], function ($) {
$(".joshine-review-add-btn").on('click',function () {
$(".block.review-add").removeClass('joshine-hidden');
});
$(".joshine-like").on('click', function() {
var element = $(this);
var formKey = $.mage.cookies.get('form_key');
if (formKey) {
element.find('input[name="form_key"]').val(formKey);
}
var type = element.hasClass('active') ? 'minus' : 'plus';
var reviewId = element.data('review-id');
var data = 'type='+ type +'&form_key=' + formKey + '&review=' + reviewId;
function refreshReviewVote(response)
{
if (!response.hasOwnProperty('success')) {
return;
}
if (type === 'plus') {
element.addClass('active')
}
if (type === 'minus') {
element.removeClass('active')
}
element.siblings('.joshine-review-likes-count').html(response.data.plus);
}
$.ajax({
url: '/joshine_review/ajax/helpful',
data: data,
type: 'GET',
dataType: 'json',
success: function (response) {
refreshReviewVote(response);
}
});
});
var img_idx = 0;
// 节流锁
var lock = true;
var swiper_wrapper = $('.swiper-wrapper');
var swiper_thumbs = $('.image-thumbs-list');
var reviewList;
function processReview(review) {
$('[data-review-js="nickname"]').html(review.nickname);
$('[data-review-js="title"]').html(review.title);
$('[data-review-js="detail"]').html(review.detail);
$('[data-review-js="date"]').html(review.created_at);
let fits_text = fitsTranslate(review.size_fits);
$('[data-review-js="fits"]').html(fits_text);
$('[data-review-js="rating"]').width(review.rating + '%');
$('#modal-current-review-id').val(review.review_id);
}
function fitsTranslate(fits) {
var fits_words = 'default';
if (fits === '1') {
fits_words = 'Small';
}
if (fits === '2') {
fits_words = 'Ture Size';
}
if (fits === '3') {
fits_words = 'Large';
}
return fits_words;
}
function pullIdx(id)
{
img_idx = findIdxByImgId(id);
swiper_wrapper.css({"transform" : 'translateX(' + -536 * img_idx + 'px)'});
dealIndex(img_idx);
}
function findIdxByImgId(imgId)
{
let res = 0;
$(".image-thumbs-list-item").each(function (index, ele) {
if ($(ele).data('image-id') == imgId) {
res = $(ele).data('img-index');
}
});
return res;
}
$(".joshine-review-container .joshine-review-pic-item").on('click', function () {
var reviewId = $(this).data('reviews-id');
var productId = $(this).data('product-id');
var image_id = $(this).data('image-id');
var formKey = $.mage.cookies.get('form_key');
var data = 'review_id='+ reviewId +'&form_key=' + formKey + '&product_id=' + productId;
$.ajax({
url: '/joshine_review/ajax/reviewinfo',
data: data,
type: 'GET',
dataType: 'json',
success: function (response) {
reviewList = response.data;
processModal(response);
pullIdx(image_id);
}
});
function processModal(response)
{
processReview(response.review);
processThumbs(response);
processFullImage(response);
$(".joshine-model-warp").removeClass('joshine-hidden')
}
function processThumbs(response) {
var html = '';
var image_index = 0;
var data = response.data;
for (let i = 0; i < data.length; i++) {
for (let j = 0; j < data[i].images.thumb.length; j++) {
html += `
<div class="image-thumbs-list-item" data-review-id="${data[i].review_id}" data-img-index="${image_index}" data-image-id="${data[i].images.thumb[j].image_id}">
<div class="modal-reviews-details__image-thumbs-image">
<img src="${data[i].images.thumb[j].url}" alt="">
</div>
</div>
`;
image_index++;
}
}
$('.image-thumbs-list').html(html);
}
function processFullImage(response)
{
var html = '';
var image_index = 0;
var data = response.data;
for (let i = 0; i < data.length; i++) {
for (let j = 0; j < data[i].images.full.length; j++) {
html += `
<div class="swiper-item" data-review-id="${data[i].review_id}" data-img-index="${image_index}" data-image-id="${data[i].images.full[j].image_id}">
<img src="${data[i].images.full[j].url}" alt="">
</div>
`;
image_index++;
}
}
$('.image-full-wrapper').html(html);
}
});
$(".joshine-close").on('click', function () {
$(".joshine-model-warp").addClass('joshine-hidden')
});
function dealIndex(img_idx)
{
$('.image-thumbs-list-item').each(function (index, element) {
if ($(element).data('img-index') == img_idx && !$(this).hasClass('active')) {
$(element).addClass('active');
return;
}
$(element).removeClass('active');
});
let thumbPage = parseInt(img_idx / 11);
swiper_thumbs.css({"transition" : 'transform .2s ease 0s'});
swiper_thumbs.css({"transform" : 'translateX(' + -490 * thumbPage + 'px)'});
}
$(".swiper-button-next").on('click', function () {
let img_idx_pre = img_idx + 1;
let has_pre = false;
$('.image-thumbs-list-item').each(function (index, element) {
if ($(element).data('img-index') == img_idx_pre) {
has_pre = true;
return;
}
});
if (!has_pre) {
return;
}
if (!lock) return;
lock = false;
swiper_wrapper.css({"transition" : 'transform .5s ease 0s'});
img_idx++;
swiper_wrapper.css({"transform" : 'translateX(' + -536 * img_idx + 'px)'});
// 开锁,动画结束之后开锁
dealIndex(img_idx);
processReviewDetail(img_idx);
setTimeout(function () {
lock = true;
}, 500);
});
$(".swiper-button-prev").on('click', function () {
if (!lock) return;
lock = false;
if (img_idx == 0) {
setTimeout(function () {
lock = true;
}, 500);
return;
}
swiper_wrapper.css({"transition" : 'transform .5s ease 0s'});
img_idx--;
swiper_wrapper.css({"transform" : 'translateX(' + -536 * img_idx + 'px)'});
dealIndex(img_idx);
processReviewDetail(img_idx);
setTimeout(function () {
lock = true;
}, 500);
});
function processReviewDetail(img_idx)
{
var shouldReviewId;
$(".image-thumbs-list-item").each(function (index, ele) {
if ($(ele).data('img-index') == img_idx) {
shouldReviewId = $(ele).data('review-id');
}
});
let curReviewId = $('#modal-current-review-id').val();
if (shouldReviewId != curReviewId) {
let review = findReviewById(shouldReviewId);
processReview(review);
}
}
function findReviewById(reviewId)
{
let review = '';
for(let i = 0; i < reviewList.length; i++) {
if (reviewList[i].review_id == reviewId) {
review = reviewList[i];
break;
}
}
return review;
}
});
</script>
\ No newline at end of file
<?php
/** @var $block */
?>
<script type="text/x-magento-init">
{
"*": {
"Joshine_Review/js/review/js-loader": { }
}
}
</script>
<div class="joshine-review-filter joshine-clearfix">
<div class="joshine-review-tab joshine-review-tab-container">
<span class="tab active joshine-inline-block joshine-font-c-deepin review-js-with-image">All Reviews</span>
<span class="tab review-with-image joshine-inline-block review-js-with-image joshine-hidden">With Image</span>
</div>
<div class="joshine-review-select-wrap joshine-hidden">
<div class="joshine-review-select-item">
<label>Rating</label>
<select data-review-js="filter" name="rating" class="review-js-filter">
<option value="1">1 Star</option>
<option value="2">2 Star</option>
<option value="3">3 Star</option>
<option value="4">4 Star</option>
<option value="5">5 Star</option>
</select>
</div>
<div class="joshine-review-select-item">
<label>Verified Buyers</label>
<input type="checkbox" name="verified_buyers" class="checkbox review-js-filter">
</div>
<div class="joshine-review-select-item"><span>Sort by</span>
<label><?= /* @noEscape */ __('Sort By') ?></label>
<select name="sorter" class="review-js-sorter">
<option value="date">Date</option>
<option value="helpfulness">Helpfulness</option>
<option value="rating">Rating</option>
</select>
</div>
</div>
</div>
.joshine-review-container {
padding: 0;
}
.joshine-review-container .joshine-review-title {
font-size: 24px;
font-weight: 1000;
text-transform: capitalize;
}
.joshine-review-container .joshine-review-averate {
display: flex;
}
.joshine-review-container .joshine-review-item {
padding: 24px;
display: -webkit-box;
display: flex;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
flex-direction: column;
-webkit-box-pack: center;
justify-content: center;
width: 33.33%;
}
.joshine-review-container .joshine-review-container .joshine-review-averate .add-new-box {
flex-grow:0;
flex-basis:auto;
font-size: 1.2em;
padding: .2em;
}
.joshine-review-container .joshine-review-container .joshine-review-averate .name {
margin-bottom: 15px;
}
.joshine-review-container .ave-rate {
align-items: center;
display: flex;
}
.joshine-review-container .ave-rate-total {
font-size: 22px;
font-weight: 700;
padding-left: 10px;
}
.joshine-review-container .joshine-review-item .fit-item .joshine-process {
background-color: #e4e4e4;
border-radius: 4px;
width: 190px;
}
.joshine-review-container .joshine-review-item .fit-item .joshine-process .joshine-process-active{
background-color: #222;
border-radius: 4px;
}
.joshine-review-container .joshine-review-item .fit-item {
display: flex;
align-items: center;
}
.joshine-review-container .joshine-review-item .fit-item .fit-name {
width: 80px;
}
.joshine-review-container .joshine-review-item.add-new-box .add-new-button {
font-weight: 400;
font-size: 22px;
}
.joshine-review-container .joshine-review-list {
padding: 20px 0;
}
.joshine-review-container .joshine-review-list-item {
display: flex;
position: relative;
width: 100%;
border-bottom: 1px dashed #e5e5e5;
padding: 25px 0;
}
.joshine-review-container .joshine-review-list-item-left {
width: 200px;
}
.joshine-review-container .joshine-review-list-item .joshine-review-nickname {
width: 100%;
white-space: nowrap;
margin-bottom: 15px;
}
.joshine-review-container .joshine-review-list-item-mid {
width: calc(100% - 288px);
display: -webkit-box;
display: flex;
padding-right: 150px;
}
.joshine-review-container .joshine-review-list-item .joshine-review-detail {
padding-right: 40px;
width: 50%;
}
.joshine-review-container .joshine-review-list-item .joshine-review-pics {
width: 50%;
display: flex;
}
.joshine-review-container .joshine-review-list-item .joshine-review-pic-item {
position: relative;
flex-shrink: 0;
width: 108px;
height: 108px;
line-height: 108px;
overflow: hidden;
margin-right: 5px;
}
.joshine-review-container .joshine-review-list-item .joshine-review-des-title {
font-weight: 400;
margin-bottom: 5px;
}
.joshine-review-container .joshine-review-list-item .joshine-review-des {
font-size: 14px;
line-height: 1.4em;
color: #5b5b5b;
}
.joshine-review-container .joshine-review-list-item .joshine-review-date {
position: absolute;
bottom: 28px;
}
.joshine-review-container .joshine-review-list-item .rate-fit {
display: inline-flex;
-webkit-box-align: center;
align-items: center;
flex-wrap: wrap;
padding-top: 10px;
}
.joshine-review-container .joshine-review-list-item .rate-fit .rate-fit-item{
margin-bottom: 5px;
margin-right: 14px;
}
.joshine-review-container .joshine-review-helpful {
display: inline-block;
position: absolute;
right: 0;
}
.joshine-review-container .joshine-review-helpful .joshine-like{
display: inline-block;
}
/*遮罩蒙层*/
.joshine-mask {
top: 0;
right: 0;
bottom: 0;
left: 0;
position: fixed;
background: rgba(0, 0, 0, .8);
}
.joshine-review-container .joshine-review-tab-container {
margin-right: auto;
}
.joshine-review-container .joshine-review-tab {
font-size: 14px;
color: #666;
font-weight: 700;
position: relative;
display: inline-block;
}
.joshine-review-container .joshine-review-tab .tab {
padding: 25px 0;
cursor: pointer;
}
.joshine-review-container .joshine-review-tab .tab + .tab {
margin-left: 25px;
}
.joshine-review-container .joshine-review-filter {
border-bottom: 1px solid #e5e5e5;
display: flex;
align-items: center;
}
.joshine-review-container .joshine-review-select-wrap {
padding: 10px;
font-size: .9em;
}
.joshine-review-container .joshine-review-filter .joshine-review-select-item {
display: inline-block;
}
.joshine-review-container .joshine-review-filter .joshine-review-select-item + .joshine-review-select-item {
margin-left: 25px;
}
.joshine-review-container .joshine-review-select-wrap .joshine-review-select-box {
padding: 5px 0;
position: relative;
display: inline-block;
}
.joshine-review-container .joshine-review-select-wrap .joshine-review-select-box-input {
height: 34px;
line-height: 32px;
display: inline-block;
}
.joshine-review-container .joshine-review-tab .tab.active {
border-bottom: #222 solid 2px;
}
.joshine-review-container .joshine-reviews-page {
margin-top: 15px;
margin-bottom: 15px;
}
.joshine-review-container .joshine-review-select-wrap select {
width: auto;
padding: .5em 2.2em .5em .5em;
height: auto;
line-height: normal;
}
.joshine-review-pic-item img {
width: 100%;
height: 7.5em ;
object-fit: cover;
}
.joshine-review-container-mobile .joshine-review-pics {
display: flex;
flex-wrap: wrap;
}
.joshine-review-container-mobile .joshine-review-pic-item{
padding: .1em;
}
.joshine-review-container-mobile .joshine-review-item .fit-item .joshine-process {
background-color: #e4e4e4;
border-radius: 4px;
width: 190px;
}
.joshine-review-container-mobile .joshine-review-item .fit-item .joshine-process .joshine-process-active{
background-color: #222;
border-radius: 4px;
}
.modal-reviews-details__image-swiper .swiper-button-next {
cursor : url(static/frontend/Joshine/breeze/en_US/Joshine_Review/img/favicon-next.ico), auto;
}
.modal-reviews-details__image-swiper .swiper-button-prev {
cursor : url(static/frontend/Joshine/breeze/en_US/Joshine_Review/img/favicon-pre.ico), auto;
}
\ No newline at end of file
define([
'jquery',
'ko'
], function ($) {
'use strict';
$.widget('mage.joshineReviewSlider', {
_create: function () {
},
_binding : function () {
},
});
return $.mage.joshineReviewSlider
});
\ No newline at end of file
define(['jquery', 'Magento_Ui/js/modal/modal'], function ($) {
'use strict';
return {
handleReviewsImgClick : function (element, event) {
$('.joshine-model-warp').removeClass('joshine-hidden');
},
handlePage : function (element, event) {
let self = $(this);
if (element.hasClass('pagination-btn-disabled')) {
return;
}
element.siblings().removeClass('active');
element.addClass('active');
let pageLoad = {
loaderContext: $('#product-review-container'),
};
event.preventDefault();
$.ajax({
url: element.data('href'),
cache: true,
}).done(function (data) {
pageLoad.loaderContext.html(data);
});
},
}
});
\ No newline at end of file
define([
'jquery',
'ko',
'joshineReviewHandler'
], function ($, ko, handler) {
'use strict';
$.widget('mage.joshineReview', {
_create: function () {
var self = this;
self._binding();
},
_binding : function () {
$(document).on('click', '.joshine-pagination-btn', function(event){
handler.handlePage($(this), event);
});
},
});
return $.mage.joshineReview
});
define([
'jquery',
'ko',
'joshineReviewConfig'
], function ($, ko, config) {
'use strict';
return {
isApi : function (url) {
var apisArr = config.apis;
return apisArr.indexOf(url);
}
}
});
\ No newline at end of file
...@@ -13,11 +13,13 @@ define([ ...@@ -13,11 +13,13 @@ define([
$(element).mage('validation', { $(element).mage('validation', {
/** @inheritdoc */ /** @inheritdoc */
errorPlacement: function (error, el) { errorPlacement: function (error, el) {
if (el.parents('#product-review-table').length) { if (el.parents('#product-review-table').length) {
$('#product-review-table').siblings(this.errorElement + '.' + this.errorClass).remove(); $('#product-review-table').siblings(this.errorElement + '.' + this.errorClass).remove();
$('#product-review-table').after(error); $('#product-review-table').after(error);
} else { } else if(el.parents('.options-list').length ) {
$('.options-list').siblings(this.errorElement + '.' + this.errorClass).remove();
$('.options-list').after(error);
} else {
el.after(error); el.after(error);
} }
} }
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
/** /**
* Pager template * Pager template
* *
* @see \Magento\Theme\Block\Html\Pager * @var \Magento\Theme\Block\Html\Pager $block
*/ */
?> ?>
<?php if ($block->getCollection()->getSize()): ?> <?php if ($block->getCollection()->getSize()): ?>
...@@ -117,22 +117,6 @@ ...@@ -117,22 +117,6 @@
</div> </div>
<?php endif; ?> <?php endif; ?>
<?php if ($block->isShowPerPage()): ?>
<div class="limiter">
<strong class="limiter-label"><?= $block->escapeHtml(__('Show')) ?></strong>
<select id="limiter" data-mage-init='{"redirectUrl": {"event":"change"}}' class="limiter-options">
<?php foreach ($block->getAvailableLimit() as $_key => $_limit): ?>
<option value="<?= $block->escapeUrl($block->getLimitUrl($_key)) ?>"
<?php if ($block->isLimitCurrent($_key)): ?>
selected="selected"<?php endif ?>>
<?= $block->escapeHtml($_limit) ?>
</option>
<?php endforeach; ?>
</select>
<span class="limiter-text"><?= $block->escapeHtml(__('per page')) ?></span>
</div>
<?php endif ?>
<?php if ($block->getUseContainer()): ?> <?php if ($block->getUseContainer()): ?>
</div> </div>
<?php endif ?> <?php endif ?>
......
<?php
/**
* Provider: Meetanshi.
* Package: Meetanshi Messenger
* Support: support@meetanshi.com (https://meetanshi.com/)
*/
namespace Meetanshi\FacebookChat\Block;
use Magento\Config\Block\System\Config\Form\Field;
use Magento\Backend\Block\Template\Context;
use Magento\Framework\Registry;
use Magento\Framework\Data\Form\Element\AbstractElement;
class Color extends Field {
protected $_coreRegistry;
public function __construct(
Context $context,
Registry $coreRegistry,
array $data = []
) {
$this->_coreRegistry = $coreRegistry;
parent::__construct($context, $data);
}
protected function _getElementHtml(AbstractElement $element) {
$html = $element->getElementHtml();
$cpPath=$this->getViewFileUrl('Meetanshi_FacebookChat::js');
if (!$this->_coreRegistry->registry('colorpicker_loaded')) {
$html .= '<script type="text/javascript" src="' . $cpPath.'/'.'jscolor.js"></script>';
$this->_coreRegistry->registry('colorpicker_loaded', 1);
}
$html .= '<script type="text/javascript">
var el = document.getElementById("' . $element->getHtmlId() . '");
el.className = el.className + " jscolor{hash:true}";
</script>';
return $html;
}
}
<?php
namespace Meetanshi\FacebookChat\Block;
use Magento\Framework\View\Element\Template;
use Magento\Framework\View\Element\Template\Context;
use Meetanshi\FacebookChat\Helper\Data;
class Messenger extends Template
{
protected $helperData;
public function __construct(
Context $context,
Data $helperData
)
{
parent::__construct($context);
$this->helperData = $helperData;
}
}
<?php
namespace Meetanshi\FacebookChat\Helper;
use Magento\Framework\App\Helper\AbstractHelper;
use Magento\Store\Model\ScopeInterface;
class Data extends AbstractHelper
{
const XML_FB_MESSENGER_ENABLE = 'messenger/general/enable';
const XML_FB_APP_ID = 'messenger/general/app_id';
const XML_FB_PAGE_ID = 'messenger/general/page_id';
const XML_FB_COLOR = 'messenger/general/color_option';
const XML_FB_LOGIN_TEXT = 'messenger/general/login_message';
const XML_FB_LOGOUT_TEXT = 'messenger/general/logout_message';
public function isEnable($storeId = null)
{
return $this->scopeConfig->getValue(
self::XML_FB_MESSENGER_ENABLE,
ScopeInterface::SCOPE_STORE,
$storeId
);
}
public function getFBAppId($storeId = null){
return $this->scopeConfig->getValue(
self::XML_FB_APP_ID,
ScopeInterface::SCOPE_STORE,
$storeId
);
}
public function getFBPageId($storeId = null){
return $this->scopeConfig->getValue(
self::XML_FB_PAGE_ID,
ScopeInterface::SCOPE_STORE,
$storeId
);
}
public function getFBColor($storeId = null){
return $this->scopeConfig->getValue(
self::XML_FB_COLOR,
ScopeInterface::SCOPE_STORE,
$storeId
);
}
public function getFBLoginText($storeId = null){
return $this->scopeConfig->getValue(
self::XML_FB_LOGIN_TEXT,
ScopeInterface::SCOPE_STORE,
$storeId
);
}
public function getFBLogoutText($storeId = null){
return $this->scopeConfig->getValue(
self::XML_FB_LOGOUT_TEXT,
ScopeInterface::SCOPE_STORE,
$storeId
);
}
}
# Magento 2 Facebook Chat
Ever since Facebook has introduced its independent messaging app, i.e., Messenger, it has served an increasing number of people, reaching 1.3 billion users every month! It is beneficial to make the most out of such a popular messaging app in Magento 2 store. Meetanshi's [***Magento 2 Facebook Chat***](https://meetanshi.com/magento-2-facebook-chat.html) extension allows integrating messenger with Magento 2 for live chat support via messenger.
Improve communication with customers. Offer excellent live chat support via the popular platform, i.e., Facebook. Solve customers’ queries quickly. Everything with Magento 2 Facebook Chat!
Offer improved customer experience with the help of the Facebook Messenger Live Support for Magento 2. Chat with your customers in their preferred platform, i.e., Facebook Messenger! With the friendly app for direct communication, build better customer relationship and earn their trust!
## Benefits of choosing Meetanshi's Magento 2 Facebook Chat extension:
* Direct communication with customers.
* Offer a quick solution to customers’ queries.
* Add Facebook App ID and Page ID to enable the messenger.
* Set theme color of the FB chat icon and chat window.
* Customize the message for the FB messenger login and logout greeting text.
* Allow customers to use their own Facebook account for contacting for the solution to their queries.
Visit, to know more: [https://meetanshi.com/magento-2-facebook-chat.html](https://meetanshi.com/magento-2-facebook-chat.html)
{
"name": "meetanshi/magento2-facebook-chat",
"description": "Magento 2 Facebook Chat",
"require": {
"php": "~5.5.0|~5.6.0|~7.0.0|7.0.2|7.0.4|~7.0.6|~7.1.0"
},
"type": "magento2-module",
"version": "1.0.0",
"license": [
"OSL-3.0",
"AFL-3.0"
],
"authors": [
{
"name": "Meetanshi",
"email": "support@meetanshi.com",
"homepage": "https://meetanshi.com"
}
],
"autoload": {
"files": [
"registration.php"
],
"psr-4": {
"Meetanshi\\FacebookChat\\": ""
}
}
}
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Acl/etc/acl.xsd">
<acl>
<resources>
<resource id="Magento_Backend::admin">
<resource id="Magento_Backend::stores">
<resource id="Magento_Backend::stores_settings">
<resource id="Magento_Config::config">
<resource id="Meetanshi_FacebookChat::messenger_config" title="FaceBook Chat" sortOrder="10"/>
</resource>
</resource>
</resource>
</resource>
</resources>
</acl>
</config>
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
<system>
<tab id="meetanshi" translate="label" sortOrder="100">
<label>FacebookChat</label>
</tab>
<section id="messenger" translate="label" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
<class>separator-top</class>
<label>Facebook Chat</label>
<tab>meetanshi</tab>
<resource>Meetanshi_FacebookChat::messenger_config</resource>
<group id="general" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Configuration</label>
<field id="enable" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Facebook Chat</label>
<source_model>Magento\Config\Model\Config\Source\Enabledisable</source_model>
</field>
<field id="app_id" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Facebook App ID</label>
<validate>required-entry</validate>
<comment><![CDATA[Generate App Id using : <a href="https://developers.facebook.com/apps" target="_blank">https://developers.facebook.com/apps</a>]]></comment>
<depends>
<field id="enable">1</field>
</depends>
</field>
<field id="page_id" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Facebook Page ID</label>
<validate>required-entry</validate>
<comment><![CDATA[Generate Page Id : GoTo Page -> Setting -> Page info]]></comment>
<depends>
<field id="enable">1</field>
</depends>
</field>
<field id="color_option" translate="label" type="text" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Theme Color</label>
<frontend_model>Meetanshi\FacebookChat\Block\Color</frontend_model>
<validate>color</validate>
<comment><![CDATA[]]></comment>
<depends>
<field id="enable">1</field>
</depends>
</field>
<field id="login_message" translate="label" type="text" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Login Greeting Text</label>
<comment><![CDATA[]]></comment>
<depends>
<field id="enable">1</field>
</depends>
</field>
<field id="logout_message" translate="label" type="text" sortOrder="60" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Logout Greeting Text</label>
<comment><![CDATA[]]></comment>
<depends>
<field id="enable">1</field>
</depends>
</field>
</group>
</section>
</system>
</config>
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd">
<default>
<messenger>
<general>
<enable>0</enable>
</general>
</messenger>
</default>
</config>
\ No newline at end of file
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
<module name="Meetanshi_FacebookChat" setup_version="1.0.0">
</module>
</config>
"use strict";if(!window.jscolor){window.jscolor=(function(){var jsc={register:function(){jsc.attachDOMReadyEvent(jsc.init);jsc.attachEvent(document,'mousedown',jsc.onDocumentMouseDown);jsc.attachEvent(document,'touchstart',jsc.onDocumentTouchStart);jsc.attachEvent(window,'resize',jsc.onWindowResize);},init:function(){if(jsc.jscolor.lookupClass){jsc.jscolor.installByClassName(jsc.jscolor.lookupClass);}},tryInstallOnElements:function(elms,className){var matchClass=new RegExp('(^|\\s)('+className+')(\\s*(\\{[^}]*\\})|\\s|$)','i');for(var i=0;i<elms.length;i+=1){if(elms[i].type!==undefined&&elms[i].type.toLowerCase()=='color'){if(jsc.isColorAttrSupported){continue;}}
var m;if(!elms[i].jscolor&&elms[i].className&&(m=elms[i].className.match(matchClass))){var targetElm=elms[i];var optsStr=null;var dataOptions=jsc.getDataAttr(targetElm,'jscolor');if(dataOptions!==null){optsStr=dataOptions;}else if(m[4]){optsStr=m[4];}
var opts={};if(optsStr){try{opts=(new Function('return ('+optsStr+')'))();}catch(eParseError){jsc.warn('Error parsing jscolor options: '+eParseError+':\n'+optsStr);}}
targetElm.jscolor=new jsc.jscolor(targetElm,opts);}}},isColorAttrSupported:(function(){var elm=document.createElement('input');if(elm.setAttribute){elm.setAttribute('type','color');if(elm.type.toLowerCase()=='color'){return true;}}
return false;})(),isCanvasSupported:(function(){var elm=document.createElement('canvas');return!!(elm.getContext&&elm.getContext('2d'));})(),fetchElement:function(mixed){return typeof mixed==='string'?document.getElementById(mixed):mixed;},isElementType:function(elm,type){return elm.nodeName.toLowerCase()===type.toLowerCase();},getDataAttr:function(el,name){var attrName='data-'+name;var attrValue=el.getAttribute(attrName);if(attrValue!==null){return attrValue;}
return null;},attachEvent:function(el,evnt,func){if(el.addEventListener){el.addEventListener(evnt,func,false);}else if(el.attachEvent){el.attachEvent('on'+evnt,func);}},detachEvent:function(el,evnt,func){if(el.removeEventListener){el.removeEventListener(evnt,func,false);}else if(el.detachEvent){el.detachEvent('on'+evnt,func);}},_attachedGroupEvents:{},attachGroupEvent:function(groupName,el,evnt,func){if(!jsc._attachedGroupEvents.hasOwnProperty(groupName)){jsc._attachedGroupEvents[groupName]=[];}
jsc._attachedGroupEvents[groupName].push([el,evnt,func]);jsc.attachEvent(el,evnt,func);},detachGroupEvents:function(groupName){if(jsc._attachedGroupEvents.hasOwnProperty(groupName)){for(var i=0;i<jsc._attachedGroupEvents[groupName].length;i+=1){var evt=jsc._attachedGroupEvents[groupName][i];jsc.detachEvent(evt[0],evt[1],evt[2]);}
delete jsc._attachedGroupEvents[groupName];}},attachDOMReadyEvent:function(func){var fired=false;var fireOnce=function(){if(!fired){fired=true;func();}};if(document.readyState==='complete'){setTimeout(fireOnce,1);return;}
if(document.addEventListener){document.addEventListener('DOMContentLoaded',fireOnce,false);window.addEventListener('load',fireOnce,false);}else if(document.attachEvent){document.attachEvent('onreadystatechange',function(){if(document.readyState==='complete'){document.detachEvent('onreadystatechange',arguments.callee);fireOnce();}})
window.attachEvent('onload',fireOnce);if(document.documentElement.doScroll&&window==window.top){var tryScroll=function(){if(!document.body){return;}
try{document.documentElement.doScroll('left');fireOnce();}catch(e){setTimeout(tryScroll,1);}};tryScroll();}}},warn:function(msg){if(window.console&&window.console.warn){window.console.warn(msg);}},preventDefault:function(e){if(e.preventDefault){e.preventDefault();}
e.returnValue=false;},captureTarget:function(target){if(target.setCapture){jsc._capturedTarget=target;jsc._capturedTarget.setCapture();}},releaseTarget:function(){if(jsc._capturedTarget){jsc._capturedTarget.releaseCapture();jsc._capturedTarget=null;}},fireEvent:function(el,evnt){if(!el){return;}
if(document.createEvent){var ev=document.createEvent('HTMLEvents');ev.initEvent(evnt,true,true);el.dispatchEvent(ev);}else if(document.createEventObject){var ev=document.createEventObject();el.fireEvent('on'+evnt,ev);}else if(el['on'+evnt]){el['on'+evnt]();}},classNameToList:function(className){return className.replace(/^\s+|\s+$/g,'').split(/\s+/);},hasClass:function(elm,className){if(!className){return false;}
return-1!=(' '+elm.className.replace(/\s+/g,' ')+' ').indexOf(' '+className+' ');},setClass:function(elm,className){var classList=jsc.classNameToList(className);for(var i=0;i<classList.length;i+=1){if(!jsc.hasClass(elm,classList[i])){elm.className+=(elm.className?' ':'')+classList[i];}}},unsetClass:function(elm,className){var classList=jsc.classNameToList(className);for(var i=0;i<classList.length;i+=1){var repl=new RegExp('^\\s*'+classList[i]+'\\s*|'+
'\\s*'+classList[i]+'\\s*$|'+
'\\s+'+classList[i]+'(\\s+)','g');elm.className=elm.className.replace(repl,'$1');}},getStyle:function(elm){return window.getComputedStyle?window.getComputedStyle(elm):elm.currentStyle;},setStyle:(function(){var helper=document.createElement('div');var getSupportedProp=function(names){for(var i=0;i<names.length;i+=1){if(names[i]in helper.style){return names[i];}}};var props={borderRadius:getSupportedProp(['borderRadius','MozBorderRadius','webkitBorderRadius']),boxShadow:getSupportedProp(['boxShadow','MozBoxShadow','webkitBoxShadow'])};return function(elm,prop,value){switch(prop.toLowerCase()){case 'opacity':var alphaOpacity=Math.round(parseFloat(value)*100);elm.style.opacity=value;elm.style.filter='alpha(opacity='+alphaOpacity+')';break;default:elm.style[props[prop]]=value;break;}};})(),setBorderRadius:function(elm,value){jsc.setStyle(elm,'borderRadius',value||'0');},setBoxShadow:function(elm,value){jsc.setStyle(elm,'boxShadow',value||'none');},getElementPos:function(e,relativeToViewport){var x=0,y=0;var rect=e.getBoundingClientRect();x=rect.left;y=rect.top;if(!relativeToViewport){var viewPos=jsc.getViewPos();x+=viewPos[0];y+=viewPos[1];}
return[x,y];},getElementSize:function(e){return[e.offsetWidth,e.offsetHeight];},getAbsPointerPos:function(e){if(!e){e=window.event;}
var x=0,y=0;if(typeof e.changedTouches!=='undefined'&&e.changedTouches.length){x=e.changedTouches[0].clientX;y=e.changedTouches[0].clientY;}else if(typeof e.clientX==='number'){x=e.clientX;y=e.clientY;}
return{x:x,y:y};},getRelPointerPos:function(e){if(!e){e=window.event;}
var target=e.target||e.srcElement;var targetRect=target.getBoundingClientRect();var x=0,y=0;var clientX=0,clientY=0;if(typeof e.changedTouches!=='undefined'&&e.changedTouches.length){clientX=e.changedTouches[0].clientX;clientY=e.changedTouches[0].clientY;}else if(typeof e.clientX==='number'){clientX=e.clientX;clientY=e.clientY;}
x=clientX-targetRect.left;y=clientY-targetRect.top;return{x:x,y:y};},getViewPos:function(){var doc=document.documentElement;return[(window.pageXOffset||doc.scrollLeft)-(doc.clientLeft||0),(window.pageYOffset||doc.scrollTop)-(doc.clientTop||0)];},getViewSize:function(){var doc=document.documentElement;return[(window.innerWidth||doc.clientWidth),(window.innerHeight||doc.clientHeight),];},redrawPosition:function(){if(jsc.picker&&jsc.picker.owner){var thisObj=jsc.picker.owner;var tp,vp;if(thisObj.fixed){tp=jsc.getElementPos(thisObj.targetElement,true);vp=[0,0];}else{tp=jsc.getElementPos(thisObj.targetElement);vp=jsc.getViewPos();}
var ts=jsc.getElementSize(thisObj.targetElement);var vs=jsc.getViewSize();var ps=jsc.getPickerOuterDims(thisObj);var a,b,c;switch(thisObj.position.toLowerCase()){case 'left':a=1;b=0;c=-1;break;case 'right':a=1;b=0;c=1;break;case 'top':a=0;b=1;c=-1;break;default:a=0;b=1;c=1;break;}
var l=(ts[b]+ps[b])/2;if(!thisObj.smartPosition){var pp=[tp[a],tp[b]+ts[b]-l+l*c];}else{var pp=[-vp[a]+tp[a]+ps[a]>vs[a]?(-vp[a]+tp[a]+ts[a]/2>vs[a]/2&&tp[a]+ts[a]-ps[a]>=0?tp[a]+ts[a]-ps[a]:tp[a]):tp[a],-vp[b]+tp[b]+ts[b]+ps[b]-l+l*c>vs[b]?(-vp[b]+tp[b]+ts[b]/2>vs[b]/2&&tp[b]+ts[b]-l-l*c>=0?tp[b]+ts[b]-l-l*c:tp[b]+ts[b]-l+l*c):(tp[b]+ts[b]-l+l*c>=0?tp[b]+ts[b]-l+l*c:tp[b]+ts[b]-l-l*c)];}
var x=pp[a];var y=pp[b];var positionValue=thisObj.fixed?'fixed':'absolute';var contractShadow=(pp[0]+ps[0]>tp[0]||pp[0]<tp[0]+ts[0])&&(pp[1]+ps[1]<tp[1]+ts[1]);jsc._drawPosition(thisObj,x,y,positionValue,contractShadow);}},_drawPosition:function(thisObj,x,y,positionValue,contractShadow){var vShadow=contractShadow?0:thisObj.shadowBlur;jsc.picker.wrap.style.position=positionValue;jsc.picker.wrap.style.left=x+'px';jsc.picker.wrap.style.top=y+'px';jsc.setBoxShadow(jsc.picker.boxS,thisObj.shadow?new jsc.BoxShadow(0,vShadow,thisObj.shadowBlur,0,thisObj.shadowColor):null);},getPickerDims:function(thisObj){var displaySlider=!!jsc.getSliderComponent(thisObj);var dims=[2*thisObj.insetWidth+2*thisObj.padding+thisObj.width+
(displaySlider?2*thisObj.insetWidth+jsc.getPadToSliderPadding(thisObj)+thisObj.sliderSize:0),2*thisObj.insetWidth+2*thisObj.padding+thisObj.height+
(thisObj.closable?2*thisObj.insetWidth+thisObj.padding+thisObj.buttonHeight:0)];return dims;},getPickerOuterDims:function(thisObj){var dims=jsc.getPickerDims(thisObj);return[dims[0]+2*thisObj.borderWidth,dims[1]+2*thisObj.borderWidth];},getPadToSliderPadding:function(thisObj){return Math.max(thisObj.padding,1.5*(2*thisObj.pointerBorderWidth+thisObj.pointerThickness));},getPadYComponent:function(thisObj){switch(thisObj.mode.charAt(1).toLowerCase()){case 'v':return 'v';break;}
return 's';},getSliderComponent:function(thisObj){if(thisObj.mode.length>2){switch(thisObj.mode.charAt(2).toLowerCase()){case 's':return 's';break;case 'v':return 'v';break;}}
return null;},onDocumentMouseDown:function(e){if(!e){e=window.event;}
var target=e.target||e.srcElement;if(target._jscLinkedInstance){if(target._jscLinkedInstance.showOnClick){target._jscLinkedInstance.show();}}else if(target._jscControlName){jsc.onControlPointerStart(e,target,target._jscControlName,'mouse');}else{if(jsc.picker&&jsc.picker.owner){jsc.picker.owner.hide();}}},onDocumentTouchStart:function(e){if(!e){e=window.event;}
var target=e.target||e.srcElement;if(target._jscLinkedInstance){if(target._jscLinkedInstance.showOnClick){target._jscLinkedInstance.show();}}else if(target._jscControlName){jsc.onControlPointerStart(e,target,target._jscControlName,'touch');}else{if(jsc.picker&&jsc.picker.owner){jsc.picker.owner.hide();}}},onWindowResize:function(e){jsc.redrawPosition();},onParentScroll:function(e){if(jsc.picker&&jsc.picker.owner){jsc.picker.owner.hide();}},_pointerMoveEvent:{mouse:'mousemove',touch:'touchmove'},_pointerEndEvent:{mouse:'mouseup',touch:'touchend'},_pointerOrigin:null,_capturedTarget:null,onControlPointerStart:function(e,target,controlName,pointerType){var thisObj=target._jscInstance;jsc.preventDefault(e);jsc.captureTarget(target);var registerDragEvents=function(doc,offset){jsc.attachGroupEvent('drag',doc,jsc._pointerMoveEvent[pointerType],jsc.onDocumentPointerMove(e,target,controlName,pointerType,offset));jsc.attachGroupEvent('drag',doc,jsc._pointerEndEvent[pointerType],jsc.onDocumentPointerEnd(e,target,controlName,pointerType));};registerDragEvents(document,[0,0]);if(window.parent&&window.frameElement){var rect=window.frameElement.getBoundingClientRect();var ofs=[-rect.left,-rect.top];registerDragEvents(window.parent.window.document,ofs);}
var abs=jsc.getAbsPointerPos(e);var rel=jsc.getRelPointerPos(e);jsc._pointerOrigin={x:abs.x-rel.x,y:abs.y-rel.y};switch(controlName){case 'pad':switch(jsc.getSliderComponent(thisObj)){case 's':if(thisObj.hsv[1]===0){thisObj.fromHSV(null,100,null);};break;case 'v':if(thisObj.hsv[2]===0){thisObj.fromHSV(null,null,100);};break;}
jsc.setPad(thisObj,e,0,0);break;case 'sld':jsc.setSld(thisObj,e,0);break;}
jsc.dispatchFineChange(thisObj);},onDocumentPointerMove:function(e,target,controlName,pointerType,offset){return function(e){var thisObj=target._jscInstance;switch(controlName){case 'pad':if(!e){e=window.event;}
jsc.setPad(thisObj,e,offset[0],offset[1]);jsc.dispatchFineChange(thisObj);break;case 'sld':if(!e){e=window.event;}
jsc.setSld(thisObj,e,offset[1]);jsc.dispatchFineChange(thisObj);break;}}},onDocumentPointerEnd:function(e,target,controlName,pointerType){return function(e){var thisObj=target._jscInstance;jsc.detachGroupEvents('drag');jsc.releaseTarget();jsc.dispatchChange(thisObj);};},dispatchChange:function(thisObj){if(thisObj.valueElement){if(jsc.isElementType(thisObj.valueElement,'input')){jsc.fireEvent(thisObj.valueElement,'change');}}},dispatchFineChange:function(thisObj){if(thisObj.onFineChange){var callback;if(typeof thisObj.onFineChange==='string'){callback=new Function(thisObj.onFineChange);}else{callback=thisObj.onFineChange;}
callback.call(thisObj);}},setPad:function(thisObj,e,ofsX,ofsY){var pointerAbs=jsc.getAbsPointerPos(e);var x=ofsX+pointerAbs.x-jsc._pointerOrigin.x-thisObj.padding-thisObj.insetWidth;var y=ofsY+pointerAbs.y-jsc._pointerOrigin.y-thisObj.padding-thisObj.insetWidth;var xVal=x*(360/(thisObj.width-1));var yVal=100-(y*(100/(thisObj.height-1)));switch(jsc.getPadYComponent(thisObj)){case 's':thisObj.fromHSV(xVal,yVal,null,jsc.leaveSld);break;case 'v':thisObj.fromHSV(xVal,null,yVal,jsc.leaveSld);break;}},setSld:function(thisObj,e,ofsY){var pointerAbs=jsc.getAbsPointerPos(e);var y=ofsY+pointerAbs.y-jsc._pointerOrigin.y-thisObj.padding-thisObj.insetWidth;var yVal=100-(y*(100/(thisObj.height-1)));switch(jsc.getSliderComponent(thisObj)){case 's':thisObj.fromHSV(null,yVal,null,jsc.leavePad);break;case 'v':thisObj.fromHSV(null,null,yVal,jsc.leavePad);break;}},_vmlNS:'jsc_vml_',_vmlCSS:'jsc_vml_css_',_vmlReady:false,initVML:function(){if(!jsc._vmlReady){var doc=document;if(!doc.namespaces[jsc._vmlNS]){doc.namespaces.add(jsc._vmlNS,'urn:schemas-microsoft-com:vml');}
if(!doc.styleSheets[jsc._vmlCSS]){var tags=['shape','shapetype','group','background','path','formulas','handles','fill','stroke','shadow','textbox','textpath','imagedata','line','polyline','curve','rect','roundrect','oval','arc','image'];var ss=doc.createStyleSheet();ss.owningElement.id=jsc._vmlCSS;for(var i=0;i<tags.length;i+=1){ss.addRule(jsc._vmlNS+'\\:'+tags[i],'behavior:url(#default#VML);');}}
jsc._vmlReady=true;}},createPalette:function(){var paletteObj={elm:null,draw:null};if(jsc.isCanvasSupported){var canvas=document.createElement('canvas');var ctx=canvas.getContext('2d');var drawFunc=function(width,height,type){canvas.width=width;canvas.height=height;ctx.clearRect(0,0,canvas.width,canvas.height);var hGrad=ctx.createLinearGradient(0,0,canvas.width,0);hGrad.addColorStop(0/6,'#F00');hGrad.addColorStop(1/6,'#FF0');hGrad.addColorStop(2/6,'#0F0');hGrad.addColorStop(3/6,'#0FF');hGrad.addColorStop(4/6,'#00F');hGrad.addColorStop(5/6,'#F0F');hGrad.addColorStop(6/6,'#F00');ctx.fillStyle=hGrad;ctx.fillRect(0,0,canvas.width,canvas.height);var vGrad=ctx.createLinearGradient(0,0,0,canvas.height);switch(type.toLowerCase()){case 's':vGrad.addColorStop(0,'rgba(255,255,255,0)');vGrad.addColorStop(1,'rgba(255,255,255,1)');break;case 'v':vGrad.addColorStop(0,'rgba(0,0,0,0)');vGrad.addColorStop(1,'rgba(0,0,0,1)');break;}
ctx.fillStyle=vGrad;ctx.fillRect(0,0,canvas.width,canvas.height);};paletteObj.elm=canvas;paletteObj.draw=drawFunc;}else{jsc.initVML();var vmlContainer=document.createElement('div');vmlContainer.style.position='relative';vmlContainer.style.overflow='hidden';var hGrad=document.createElement(jsc._vmlNS+':fill');hGrad.type='gradient';hGrad.method='linear';hGrad.angle='90';hGrad.colors='16.67% #F0F, 33.33% #00F, 50% #0FF, 66.67% #0F0, 83.33% #FF0'
var hRect=document.createElement(jsc._vmlNS+':rect');hRect.style.position='absolute';hRect.style.left=-1+'px';hRect.style.top=-1+'px';hRect.stroked=false;hRect.appendChild(hGrad);vmlContainer.appendChild(hRect);var vGrad=document.createElement(jsc._vmlNS+':fill');vGrad.type='gradient';vGrad.method='linear';vGrad.angle='180';vGrad.opacity='0';var vRect=document.createElement(jsc._vmlNS+':rect');vRect.style.position='absolute';vRect.style.left=-1+'px';vRect.style.top=-1+'px';vRect.stroked=false;vRect.appendChild(vGrad);vmlContainer.appendChild(vRect);var drawFunc=function(width,height,type){vmlContainer.style.width=width+'px';vmlContainer.style.height=height+'px';hRect.style.width=vRect.style.width=(width+1)+'px';hRect.style.height=vRect.style.height=(height+1)+'px';hGrad.color='#F00';hGrad.color2='#F00';switch(type.toLowerCase()){case 's':vGrad.color=vGrad.color2='#FFF';break;case 'v':vGrad.color=vGrad.color2='#000';break;}};paletteObj.elm=vmlContainer;paletteObj.draw=drawFunc;}
return paletteObj;},createSliderGradient:function(){var sliderObj={elm:null,draw:null};if(jsc.isCanvasSupported){var canvas=document.createElement('canvas');var ctx=canvas.getContext('2d');var drawFunc=function(width,height,color1,color2){canvas.width=width;canvas.height=height;ctx.clearRect(0,0,canvas.width,canvas.height);var grad=ctx.createLinearGradient(0,0,0,canvas.height);grad.addColorStop(0,color1);grad.addColorStop(1,color2);ctx.fillStyle=grad;ctx.fillRect(0,0,canvas.width,canvas.height);};sliderObj.elm=canvas;sliderObj.draw=drawFunc;}else{jsc.initVML();var vmlContainer=document.createElement('div');vmlContainer.style.position='relative';vmlContainer.style.overflow='hidden';var grad=document.createElement(jsc._vmlNS+':fill');grad.type='gradient';grad.method='linear';grad.angle='180';var rect=document.createElement(jsc._vmlNS+':rect');rect.style.position='absolute';rect.style.left=-1+'px';rect.style.top=-1+'px';rect.stroked=false;rect.appendChild(grad);vmlContainer.appendChild(rect);var drawFunc=function(width,height,color1,color2){vmlContainer.style.width=width+'px';vmlContainer.style.height=height+'px';rect.style.width=(width+1)+'px';rect.style.height=(height+1)+'px';grad.color=color1;grad.color2=color2;};sliderObj.elm=vmlContainer;sliderObj.draw=drawFunc;}
return sliderObj;},leaveValue:1<<0,leaveStyle:1<<1,leavePad:1<<2,leaveSld:1<<3,BoxShadow:(function(){var BoxShadow=function(hShadow,vShadow,blur,spread,color,inset){this.hShadow=hShadow;this.vShadow=vShadow;this.blur=blur;this.spread=spread;this.color=color;this.inset=!!inset;};BoxShadow.prototype.toString=function(){var vals=[Math.round(this.hShadow)+'px',Math.round(this.vShadow)+'px',Math.round(this.blur)+'px',Math.round(this.spread)+'px',this.color];if(this.inset){vals.push('inset');}
return vals.join(' ');};return BoxShadow;})(),jscolor:function(targetElement,options){this.value=null;this.valueElement=targetElement;this.styleElement=targetElement;this.required=true;this.refine=true;this.hash=false;this.uppercase=true;this.onFineChange=null;this.activeClass='jscolor-active';this.overwriteImportant=false;this.minS=0;this.maxS=100;this.minV=0;this.maxV=100;this.hsv=[0,0,100];this.rgb=[255,255,255];this.width=181;this.height=101;this.showOnClick=true;this.mode='HSV';this.position='bottom';this.smartPosition=true;this.sliderSize=16;this.crossSize=8;this.closable=false;this.closeText='Close';this.buttonColor='#000000';this.buttonHeight=18;this.padding=12;this.backgroundColor='#FFFFFF';this.borderWidth=1;this.borderColor='#BBBBBB';this.borderRadius=8;this.insetWidth=1;this.insetColor='#BBBBBB';this.shadow=true;this.shadowBlur=15;this.shadowColor='rgba(0,0,0,0.2)';this.pointerColor='#4C4C4C';this.pointerBorderColor='#FFFFFF';this.pointerBorderWidth=1;this.pointerThickness=2;this.zIndex=1000;this.container=null;for(var opt in options){if(options.hasOwnProperty(opt)){this[opt]=options[opt];}}
this.hide=function(){if(isPickerOwner()){detachPicker();}};this.show=function(){drawPicker();};this.redraw=function(){if(isPickerOwner()){drawPicker();}};this.importColor=function(){if(!this.valueElement){this.exportColor();}else{if(jsc.isElementType(this.valueElement,'input')){if(!this.refine){if(!this.fromString(this.valueElement.value,jsc.leaveValue)){if(this.styleElement){this.styleElement.style.backgroundImage=this.styleElement._jscOrigStyle.backgroundImage;this.styleElement.style.backgroundColor=this.styleElement._jscOrigStyle.backgroundColor;this.styleElement.style.color=this.styleElement._jscOrigStyle.color;}
this.exportColor(jsc.leaveValue|jsc.leaveStyle);}}else if(!this.required&&/^\s*$/.test(this.valueElement.value)){this.valueElement.value='';if(this.styleElement){this.styleElement.style.backgroundImage=this.styleElement._jscOrigStyle.backgroundImage;this.styleElement.style.backgroundColor=this.styleElement._jscOrigStyle.backgroundColor;this.styleElement.style.color=this.styleElement._jscOrigStyle.color;}
this.exportColor(jsc.leaveValue|jsc.leaveStyle);}else if(this.fromString(this.valueElement.value)){}else{this.exportColor();}}else{this.exportColor();}}};this.exportColor=function(flags){if(!(flags&jsc.leaveValue)&&this.valueElement){var value=this.toString();if(this.uppercase){value=value.toUpperCase();}
if(this.hash){value='#'+value;}
if(jsc.isElementType(this.valueElement,'input')){this.valueElement.value=value;}else{this.valueElement.innerHTML=value;}}
if(!(flags&jsc.leaveStyle)){if(this.styleElement){var bgColor='#'+this.toString();var fgColor=this.isLight()?'#000':'#FFF';this.styleElement.style.backgroundImage='none';this.styleElement.style.backgroundColor=bgColor;this.styleElement.style.color=fgColor;if(this.overwriteImportant){this.styleElement.setAttribute('style','background: '+bgColor+' !important; '+
'color: '+fgColor+' !important;');}}}
if(!(flags&jsc.leavePad)&&isPickerOwner()){redrawPad();}
if(!(flags&jsc.leaveSld)&&isPickerOwner()){redrawSld();}};this.fromHSV=function(h,s,v,flags){if(h!==null){if(isNaN(h)){return false;}
h=Math.max(0,Math.min(360,h));}
if(s!==null){if(isNaN(s)){return false;}
s=Math.max(0,Math.min(100,this.maxS,s),this.minS);}
if(v!==null){if(isNaN(v)){return false;}
v=Math.max(0,Math.min(100,this.maxV,v),this.minV);}
this.rgb=HSV_RGB(h===null?this.hsv[0]:(this.hsv[0]=h),s===null?this.hsv[1]:(this.hsv[1]=s),v===null?this.hsv[2]:(this.hsv[2]=v));this.exportColor(flags);};this.fromRGB=function(r,g,b,flags){if(r!==null){if(isNaN(r)){return false;}
r=Math.max(0,Math.min(255,r));}
if(g!==null){if(isNaN(g)){return false;}
g=Math.max(0,Math.min(255,g));}
if(b!==null){if(isNaN(b)){return false;}
b=Math.max(0,Math.min(255,b));}
var hsv=RGB_HSV(r===null?this.rgb[0]:r,g===null?this.rgb[1]:g,b===null?this.rgb[2]:b);if(hsv[0]!==null){this.hsv[0]=Math.max(0,Math.min(360,hsv[0]));}
if(hsv[2]!==0){this.hsv[1]=hsv[1]===null?null:Math.max(0,this.minS,Math.min(100,this.maxS,hsv[1]));}
this.hsv[2]=hsv[2]===null?null:Math.max(0,this.minV,Math.min(100,this.maxV,hsv[2]));var rgb=HSV_RGB(this.hsv[0],this.hsv[1],this.hsv[2]);this.rgb[0]=rgb[0];this.rgb[1]=rgb[1];this.rgb[2]=rgb[2];this.exportColor(flags);};this.fromString=function(str,flags){var m;if(m=str.match(/^\W*([0-9A-F]{3}([0-9A-F]{3})?)\W*$/i)){if(m[1].length===6){this.fromRGB(parseInt(m[1].substr(0,2),16),parseInt(m[1].substr(2,2),16),parseInt(m[1].substr(4,2),16),flags);}else{this.fromRGB(parseInt(m[1].charAt(0)+m[1].charAt(0),16),parseInt(m[1].charAt(1)+m[1].charAt(1),16),parseInt(m[1].charAt(2)+m[1].charAt(2),16),flags);}
return true;}else if(m=str.match(/^\W*rgba?\(([^)]*)\)\W*$/i)){var params=m[1].split(',');var re=/^\s*(\d*)(\.\d+)?\s*$/;var mR,mG,mB;if(params.length>=3&&(mR=params[0].match(re))&&(mG=params[1].match(re))&&(mB=params[2].match(re))){var r=parseFloat((mR[1]||'0')+(mR[2]||''));var g=parseFloat((mG[1]||'0')+(mG[2]||''));var b=parseFloat((mB[1]||'0')+(mB[2]||''));this.fromRGB(r,g,b,flags);return true;}}
return false;};this.toString=function(){return((0x100|Math.round(this.rgb[0])).toString(16).substr(1)+
(0x100|Math.round(this.rgb[1])).toString(16).substr(1)+
(0x100|Math.round(this.rgb[2])).toString(16).substr(1));};this.toHEXString=function(){return '#'+this.toString().toUpperCase();};this.toRGBString=function(){return('rgb('+
Math.round(this.rgb[0])+','+
Math.round(this.rgb[1])+','+
Math.round(this.rgb[2])+')');};this.isLight=function(){return(0.213*this.rgb[0]+
0.715*this.rgb[1]+
0.072*this.rgb[2]>255/2);};this._processParentElementsInDOM=function(){if(this._linkedElementsProcessed){return;}
this._linkedElementsProcessed=true;var elm=this.targetElement;do{var currStyle=jsc.getStyle(elm);if(currStyle&&currStyle.position.toLowerCase()==='fixed'){this.fixed=true;}
if(elm!==this.targetElement){if(!elm._jscEventsAttached){jsc.attachEvent(elm,'scroll',jsc.onParentScroll);elm._jscEventsAttached=true;}}}while((elm=elm.parentNode)&&!jsc.isElementType(elm,'body'));};function RGB_HSV(r,g,b){r/=255;g/=255;b/=255;var n=Math.min(Math.min(r,g),b);var v=Math.max(Math.max(r,g),b);var m=v-n;if(m===0){return[null,0,100*v];}
var h=r===n?3+(b-g)/m:(g===n?5+(r-b)/m:1+(g-r)/m);return[60*(h===6?0:h),100*(m/v),100*v];}
function HSV_RGB(h,s,v){var u=255*(v/100);if(h===null){return[u,u,u];}
h/=60;s/=100;var i=Math.floor(h);var f=i%2?h-i:1-(h-i);var m=u*(1-s);var n=u*(1-s*f);switch(i){case 6:case 0:return[u,n,m];case 1:return[n,u,m];case 2:return[m,u,n];case 3:return[m,n,u];case 4:return[n,m,u];case 5:return[u,m,n];}}
function detachPicker(){jsc.unsetClass(THIS.targetElement,THIS.activeClass);jsc.picker.wrap.parentNode.removeChild(jsc.picker.wrap);delete jsc.picker.owner;}
function drawPicker(){THIS._processParentElementsInDOM();if(!jsc.picker){jsc.picker={owner:null,wrap:document.createElement('div'),box:document.createElement('div'),boxS:document.createElement('div'),boxB:document.createElement('div'),pad:document.createElement('div'),padB:document.createElement('div'),padM:document.createElement('div'),padPal:jsc.createPalette(),cross:document.createElement('div'),crossBY:document.createElement('div'),crossBX:document.createElement('div'),crossLY:document.createElement('div'),crossLX:document.createElement('div'),sld:document.createElement('div'),sldB:document.createElement('div'),sldM:document.createElement('div'),sldGrad:jsc.createSliderGradient(),sldPtrS:document.createElement('div'),sldPtrIB:document.createElement('div'),sldPtrMB:document.createElement('div'),sldPtrOB:document.createElement('div'),btn:document.createElement('div'),btnT:document.createElement('span')};jsc.picker.pad.appendChild(jsc.picker.padPal.elm);jsc.picker.padB.appendChild(jsc.picker.pad);jsc.picker.cross.appendChild(jsc.picker.crossBY);jsc.picker.cross.appendChild(jsc.picker.crossBX);jsc.picker.cross.appendChild(jsc.picker.crossLY);jsc.picker.cross.appendChild(jsc.picker.crossLX);jsc.picker.padB.appendChild(jsc.picker.cross);jsc.picker.box.appendChild(jsc.picker.padB);jsc.picker.box.appendChild(jsc.picker.padM);jsc.picker.sld.appendChild(jsc.picker.sldGrad.elm);jsc.picker.sldB.appendChild(jsc.picker.sld);jsc.picker.sldB.appendChild(jsc.picker.sldPtrOB);jsc.picker.sldPtrOB.appendChild(jsc.picker.sldPtrMB);jsc.picker.sldPtrMB.appendChild(jsc.picker.sldPtrIB);jsc.picker.sldPtrIB.appendChild(jsc.picker.sldPtrS);jsc.picker.box.appendChild(jsc.picker.sldB);jsc.picker.box.appendChild(jsc.picker.sldM);jsc.picker.btn.appendChild(jsc.picker.btnT);jsc.picker.box.appendChild(jsc.picker.btn);jsc.picker.boxB.appendChild(jsc.picker.box);jsc.picker.wrap.appendChild(jsc.picker.boxS);jsc.picker.wrap.appendChild(jsc.picker.boxB);}
var p=jsc.picker;var displaySlider=!!jsc.getSliderComponent(THIS);var dims=jsc.getPickerDims(THIS);var crossOuterSize=(2*THIS.pointerBorderWidth+THIS.pointerThickness+2*THIS.crossSize);var padToSliderPadding=jsc.getPadToSliderPadding(THIS);var borderRadius=Math.min(THIS.borderRadius,Math.round(THIS.padding*Math.PI));var padCursor='crosshair';p.wrap.style.clear='both';p.wrap.style.width=(dims[0]+2*THIS.borderWidth)+'px';p.wrap.style.height=(dims[1]+2*THIS.borderWidth)+'px';p.wrap.style.zIndex=THIS.zIndex;p.box.style.width=dims[0]+'px';p.box.style.height=dims[1]+'px';p.boxS.style.position='absolute';p.boxS.style.left='0';p.boxS.style.top='0';p.boxS.style.width='100%';p.boxS.style.height='100%';jsc.setBorderRadius(p.boxS,borderRadius+'px');p.boxB.style.position='relative';p.boxB.style.border=THIS.borderWidth+'px solid';p.boxB.style.borderColor=THIS.borderColor;p.boxB.style.background=THIS.backgroundColor;jsc.setBorderRadius(p.boxB,borderRadius+'px');p.padM.style.background=p.sldM.style.background='#FFF';jsc.setStyle(p.padM,'opacity','0');jsc.setStyle(p.sldM,'opacity','0');p.pad.style.position='relative';p.pad.style.width=THIS.width+'px';p.pad.style.height=THIS.height+'px';p.padPal.draw(THIS.width,THIS.height,jsc.getPadYComponent(THIS));p.padB.style.position='absolute';p.padB.style.left=THIS.padding+'px';p.padB.style.top=THIS.padding+'px';p.padB.style.border=THIS.insetWidth+'px solid';p.padB.style.borderColor=THIS.insetColor;p.padM._jscInstance=THIS;p.padM._jscControlName='pad';p.padM.style.position='absolute';p.padM.style.left='0';p.padM.style.top='0';p.padM.style.width=(THIS.padding+2*THIS.insetWidth+THIS.width+padToSliderPadding/2)+'px';p.padM.style.height=dims[1]+'px';p.padM.style.cursor=padCursor;p.cross.style.position='absolute';p.cross.style.left=p.cross.style.top='0';p.cross.style.width=p.cross.style.height=crossOuterSize+'px';p.crossBY.style.position=p.crossBX.style.position='absolute';p.crossBY.style.background=p.crossBX.style.background=THIS.pointerBorderColor;p.crossBY.style.width=p.crossBX.style.height=(2*THIS.pointerBorderWidth+THIS.pointerThickness)+'px';p.crossBY.style.height=p.crossBX.style.width=crossOuterSize+'px';p.crossBY.style.left=p.crossBX.style.top=(Math.floor(crossOuterSize/2)-Math.floor(THIS.pointerThickness/2)-THIS.pointerBorderWidth)+'px';p.crossBY.style.top=p.crossBX.style.left='0';p.crossLY.style.position=p.crossLX.style.position='absolute';p.crossLY.style.background=p.crossLX.style.background=THIS.pointerColor;p.crossLY.style.height=p.crossLX.style.width=(crossOuterSize-2*THIS.pointerBorderWidth)+'px';p.crossLY.style.width=p.crossLX.style.height=THIS.pointerThickness+'px';p.crossLY.style.left=p.crossLX.style.top=(Math.floor(crossOuterSize/2)-Math.floor(THIS.pointerThickness/2))+'px';p.crossLY.style.top=p.crossLX.style.left=THIS.pointerBorderWidth+'px';p.sld.style.overflow='hidden';p.sld.style.width=THIS.sliderSize+'px';p.sld.style.height=THIS.height+'px';p.sldGrad.draw(THIS.sliderSize,THIS.height,'#000','#000');p.sldB.style.display=displaySlider?'block':'none';p.sldB.style.position='absolute';p.sldB.style.right=THIS.padding+'px';p.sldB.style.top=THIS.padding+'px';p.sldB.style.border=THIS.insetWidth+'px solid';p.sldB.style.borderColor=THIS.insetColor;p.sldM._jscInstance=THIS;p.sldM._jscControlName='sld';p.sldM.style.display=displaySlider?'block':'none';p.sldM.style.position='absolute';p.sldM.style.right='0';p.sldM.style.top='0';p.sldM.style.width=(THIS.sliderSize+padToSliderPadding/2+THIS.padding+2*THIS.insetWidth)+'px';p.sldM.style.height=dims[1]+'px';p.sldM.style.cursor='default';p.sldPtrIB.style.border=p.sldPtrOB.style.border=THIS.pointerBorderWidth+'px solid '+THIS.pointerBorderColor;p.sldPtrOB.style.position='absolute';p.sldPtrOB.style.left=-(2*THIS.pointerBorderWidth+THIS.pointerThickness)+'px';p.sldPtrOB.style.top='0';p.sldPtrMB.style.border=THIS.pointerThickness+'px solid '+THIS.pointerColor;p.sldPtrS.style.width=THIS.sliderSize+'px';p.sldPtrS.style.height=sliderPtrSpace+'px';function setBtnBorder(){var insetColors=THIS.insetColor.split(/\s+/);var outsetColor=insetColors.length<2?insetColors[0]:insetColors[1]+' '+insetColors[0]+' '+insetColors[0]+' '+insetColors[1];p.btn.style.borderColor=outsetColor;}
p.btn.style.display=THIS.closable?'block':'none';p.btn.style.position='absolute';p.btn.style.left=THIS.padding+'px';p.btn.style.bottom=THIS.padding+'px';p.btn.style.padding='0 15px';p.btn.style.height=THIS.buttonHeight+'px';p.btn.style.border=THIS.insetWidth+'px solid';setBtnBorder();p.btn.style.color=THIS.buttonColor;p.btn.style.font='12px sans-serif';p.btn.style.textAlign='center';try{p.btn.style.cursor='pointer';}catch(eOldIE){p.btn.style.cursor='hand';}
p.btn.onmousedown=function(){THIS.hide();};p.btnT.style.lineHeight=THIS.buttonHeight+'px';p.btnT.innerHTML='';p.btnT.appendChild(document.createTextNode(THIS.closeText));redrawPad();redrawSld();if(jsc.picker.owner&&jsc.picker.owner!==THIS){jsc.unsetClass(jsc.picker.owner.targetElement,THIS.activeClass);}
jsc.picker.owner=THIS;if(jsc.isElementType(container,'body')){jsc.redrawPosition();}else{jsc._drawPosition(THIS,0,0,'relative',false);}
if(p.wrap.parentNode!=container){container.appendChild(p.wrap);}
jsc.setClass(THIS.targetElement,THIS.activeClass);}
function redrawPad(){switch(jsc.getPadYComponent(THIS)){case 's':var yComponent=1;break;case 'v':var yComponent=2;break;}
var x=Math.round((THIS.hsv[0]/360)*(THIS.width-1));var y=Math.round((1-THIS.hsv[yComponent]/100)*(THIS.height-1));var crossOuterSize=(2*THIS.pointerBorderWidth+THIS.pointerThickness+2*THIS.crossSize);var ofs=-Math.floor(crossOuterSize/2);jsc.picker.cross.style.left=(x+ofs)+'px';jsc.picker.cross.style.top=(y+ofs)+'px';switch(jsc.getSliderComponent(THIS)){case 's':var rgb1=HSV_RGB(THIS.hsv[0],100,THIS.hsv[2]);var rgb2=HSV_RGB(THIS.hsv[0],0,THIS.hsv[2]);var color1='rgb('+
Math.round(rgb1[0])+','+
Math.round(rgb1[1])+','+
Math.round(rgb1[2])+')';var color2='rgb('+
Math.round(rgb2[0])+','+
Math.round(rgb2[1])+','+
Math.round(rgb2[2])+')';jsc.picker.sldGrad.draw(THIS.sliderSize,THIS.height,color1,color2);break;case 'v':var rgb=HSV_RGB(THIS.hsv[0],THIS.hsv[1],100);var color1='rgb('+
Math.round(rgb[0])+','+
Math.round(rgb[1])+','+
Math.round(rgb[2])+')';var color2='#000';jsc.picker.sldGrad.draw(THIS.sliderSize,THIS.height,color1,color2);break;}}
function redrawSld(){var sldComponent=jsc.getSliderComponent(THIS);if(sldComponent){switch(sldComponent){case 's':var yComponent=1;break;case 'v':var yComponent=2;break;}
var y=Math.round((1-THIS.hsv[yComponent]/100)*(THIS.height-1));jsc.picker.sldPtrOB.style.top=(y-(2*THIS.pointerBorderWidth+THIS.pointerThickness)-Math.floor(sliderPtrSpace/2))+'px';}}
function isPickerOwner(){return jsc.picker&&jsc.picker.owner===THIS;}
function blurValue(){THIS.importColor();}
if(typeof targetElement==='string'){var id=targetElement;var elm=document.getElementById(id);if(elm){this.targetElement=elm;}else{jsc.warn('Could not find target element with ID \''+id+'\'');}}else if(targetElement){this.targetElement=targetElement;}else{jsc.warn('Invalid target element: \''+targetElement+'\'');}
if(this.targetElement._jscLinkedInstance){jsc.warn('Cannot link jscolor twice to the same element. Skipping.');return;}
this.targetElement._jscLinkedInstance=this;this.valueElement=jsc.fetchElement(this.valueElement);this.styleElement=jsc.fetchElement(this.styleElement);var THIS=this;var container=this.container?jsc.fetchElement(this.container):document.getElementsByTagName('body')[0];var sliderPtrSpace=3;if(jsc.isElementType(this.targetElement,'button')){if(this.targetElement.onclick){var origCallback=this.targetElement.onclick;this.targetElement.onclick=function(evt){origCallback.call(this,evt);return false;};}else{this.targetElement.onclick=function(){return false;};}}
if(this.valueElement){if(jsc.isElementType(this.valueElement,'input')){var updateField=function(){THIS.fromString(THIS.valueElement.value,jsc.leaveValue);jsc.dispatchFineChange(THIS);};jsc.attachEvent(this.valueElement,'keyup',updateField);jsc.attachEvent(this.valueElement,'input',updateField);jsc.attachEvent(this.valueElement,'blur',blurValue);this.valueElement.setAttribute('autocomplete','off');}}
if(this.styleElement){this.styleElement._jscOrigStyle={backgroundImage:this.styleElement.style.backgroundImage,backgroundColor:this.styleElement.style.backgroundColor,color:this.styleElement.style.color};}
if(this.value){this.fromString(this.value)||this.exportColor();}else{this.importColor();}}};jsc.jscolor.lookupClass='jscolor';jsc.jscolor.installByClassName=function(className){var inputElms=document.getElementsByTagName('input');var buttonElms=document.getElementsByTagName('button');jsc.tryInstallOnElements(inputElms,className);jsc.tryInstallOnElements(buttonElms,className);};jsc.register();return jsc.jscolor;})();}
\ No newline at end of file
<referenceContainer name="footer-container">
<container name="footer" as="footer" label="Page Footer" htmlTag="div" htmlClass="footer content">
<block class="Meetanshi\FacebookChat\Block\Messenger" name="custom_block" as="custom_block" template="Meetanshi_FacebookChat::messenger_script.phtml"/>
</container>
</referenceContainer>
<?php
$helper = $this->helper('Meetanshi\FacebookChat\Helper\Data');
$enable = $helper->isEnable();
$appId = $helper->getFBAppId();
$pageId = $helper->getFBPageId();
$color = $helper->getFBColor();
$loginText = $helper->getFBLoginText();
$logoutText = $helper->getFBLogoutText();
if($enable){
if (empty($loginText)){
$loginText = 'Hi! What can i Help you?';
}
if (empty($logoutText)){
$logoutText = 'Hi! What can i Help you?';
}
?>
<div id="fb-root"></div>
<script>
window.fbAsyncInit = function() {
FB.init({
appId : '<?php echo $appId;?>',
autoLogAppEvents : true,
xfbml : true,
version : 'v3.2'
});
};
(function(d, s, id){
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) {return;}
js = d.createElement(s); js.id = id;
js.src = "https://connect.facebook.net/en_US/sdk/xfbml.customerchat.js";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));
</script>
<div class="fb-customerchat"
attribution=setup_tool
page_id="<?php echo $pageId;?>"
theme_color="<?php echo $color;?>"
logged_in_greeting="<?php echo $loginText;?>"
logged_out_greeting="<?php echo $logoutText;?>"
</div>
<?php
}
?>
define([
'jquery',
'underscore',
'Rokanthemes_OnePageCheckout/js/model/url-builder',
'mage/storage',
'Magento_Checkout/js/model/error-processor',
'Magento_Checkout/js/model/shipping-service',
'Magento_Checkout/js/model/totals',
'Rokanthemes_OnePageCheckout/js/model/payment-service',
'mage/url',
'Magento_Checkout/js/model/quote',
'Magento_Checkout/js/model/payment/method-converter',
'Magento_Checkout/js/model/payment-service',
'Rokanthemes_OnePageCheckout/js/model/update-item-service',
'Magento_Ui/js/model/messageList',
'Magento_Checkout/js/action/get-totals',
'Magento_Checkout/js/action/select-shipping-method',
'Magento_Checkout/js/checkout-data',
'Rokanthemes_OnePageCheckout/js/model/shipping-save-processor'
], function (
$,
_,
urlBuilder,
storage,
errorProcessor,
shippingService,
totals,
paymentService,
url,
quote,
methodConverter,
paymentServiceDefault,
updateItemService,
globalMessageList,
getTotalsAction,
selectShippingMethodAction,
checkoutData,
shippingSaveProcessor
) {
'use strict';
return function (item) {
var serviceUrl = urlBuilder.getUpdateQtyUrl(),
address = quote.shippingAddress();
shippingService.isLoading(true);
totals.isLoading(true);
paymentService.isLoading(true);
return storage.post(
serviceUrl,
JSON.stringify({
address: {
'region_id': address.regionId,
'region': address.region,
'country_id': address.countryId,
'postcode': address.postcode
},
itemId: parseInt(item.item_id),
qty: parseFloat(item.qty)
})
).done(function (response) {
if (response.has_error && response.status) {
globalMessageList.addSuccessMessage(response);
window.location.replace(url.build('checkout/cart/'));
} else {
if (response.status) {
globalMessageList.addSuccessMessage(response);
updateItemService.hasUpdateResult(true);
//shippingService.setShippingRates(response.shipping_methods);
paymentServiceDefault.setPaymentMethods(methodConverter(response.payment_methods));
var sh = response.shipping_methods;
var new_sh = [];
selectShippingMethodAction(sh[0]);
checkoutData.setSelectedShippingRate(sh[0]['carrier_code'] + '_' + sh[0]['method_code']);
quote.shippingMethod(sh[0]);
new_sh.push(sh[0]);
shippingService.setShippingRates(new_sh);
updateItemService.hasUpdateResult(false);
response.totals.coupon_code ? paymentService.isAppliedCoupon(true) : paymentService.isAppliedCoupon(false);
var deferred = $.Deferred();
getTotalsAction([], deferred);
$('.items-in-cart').find('[data-bind="text: getCartSummaryItemsCount()"]')
.text(response['totals']['items_qty']);
shippingSaveProcessor.saveShippingInformation(quote.shippingAddress().getType());
} else {
globalMessageList.addErrorMessage(response);
}
}
}).fail(function (response) {
errorProcessor.process(response);
}).always(function () {
shippingService.isLoading(false);
totals.isLoading(false);
paymentService.isLoading(false);
});
};
});
define([
'Magento_Checkout/js/model/resource-url-manager',
'Magento_Checkout/js/model/quote',
'mage/storage',
'Magento_Checkout/js/model/shipping-service',
'Magento_Checkout/js/model/shipping-rate-registry',
'Magento_Checkout/js/model/error-processor'
], function (resourceUrlManager, quote, storage, shippingService, rateRegistry, errorProcessor) {
'use strict';
return function (address) {
var serviceUrl, payload;
shippingService.isLoading(true);
serviceUrl = resourceUrlManager.getUrlForEstimationShippingMethodsForNewAddress(quote);
payload = JSON.stringify({
address: {
'street': address.street,
'city': address.city,
'region_id': address.regionId,
'region': address.region,
'country_id': address.countryId,
'postcode': address.postcode,
'email': address.email,
'customer_id': address.customerId,
'firstname': address.firstname,
'lastname': address.lastname,
'middlename': address.middlename,
'prefix': address.prefix,
'suffix': address.suffix,
'vat_id': address.vatId,
'company': address.company,
'telephone': address.telephone,
'fax': address.fax,
'custom_attributes': address.customAttributes,
'save_in_address_book': address.saveInAddressBook
}
}
);
return storage.post(
serviceUrl, payload, false
).done(function (result) {
var new_sh = [];
new_sh.push(result[0]);
rateRegistry.set(address.getCacheKey(), result);
shippingService.setShippingRates(new_sh);
}).fail(function (response) {
shippingService.setShippingRates([]);
errorProcessor.process(response);
}).always(function () {
shippingService.isLoading(false);
});
}
});
...@@ -4,7 +4,6 @@ ...@@ -4,7 +4,6 @@
<?php <?php
$isModuleEnable = $block->getConfig('sparsh_free_shipping_bar/general/enable'); $isModuleEnable = $block->getConfig('sparsh_free_shipping_bar/general/enable');
$isFreeShippingBarAvailable = null; $isFreeShippingBarAvailable = null;
if ($isModuleEnable): if ($isModuleEnable):
$layoutPosition = $block->getData('position') ? $block->getData('position') : null; $layoutPosition = $block->getData('position') ? $block->getData('position') : null;
$entityId = $block->getData('entity_id') ? $block->getData('entity_id') : null; $entityId = $block->getData('entity_id') ? $block->getData('entity_id') : null;
...@@ -29,18 +28,15 @@ endif; ...@@ -29,18 +28,15 @@ endif;
.sparsh-free-shipping-bar-goal-message{ .sparsh-free-shipping-bar-goal-message{
width: 100%; width: 100%;
height: 60px; height: 60px;
background-image: url("<?= $block->getImgUrl($barData['background_img']) ?>");
line-height: 60px; line-height: 60px;
background-size: 100% 100%!important; background-size: 100% 100%!important;
background-repeat: no-repeat; background-repeat: no-repeat;
text-align: center;
}
.sparsh-free-shipping-bar-goal-message-back-<?= $barData['entity_id']; ?>{
background-image: url('<?= $block->getImgUrl($barData['background_img']) ?>');
} }
@media (max-width: 768px) { @media (max-width: 768px) {
.sparsh-free-shipping-bar-goal-message{ .sparsh-free-shipping-bar-goal-message{
background-image: url("<?= $block->getImgUrl($barData['background_img_phone']) ?>");
width: 100%; width: 100%;
height: 40px; height: 40px;
line-height: 40px; line-height: 40px;
...@@ -48,17 +44,14 @@ endif; ...@@ -48,17 +44,14 @@ endif;
background-repeat: no-repeat; background-repeat: no-repeat;
} }
.sparsh-free-shipping-bar-goal-message-back-<?= $barData['entity_id']; ?>{
background-image: url('<?= $block->getImgUrl($barData['background_img_phone']) ?>');
}
} }
</style> </style>
<div class="sparsh-free-shipping-bar-goal-message sparsh-free-shipping-bar-goal-message-back-<?= $barData['entity_id']; ?>" <div class="sparsh-free-shipping-bar-goal-message"
style=" background-color: <?= /* @noEscape */ $barData['bar_background_color']?>; font-size: <?= /* @noEscape */ $barData['bar_font_size'].'px'?>"> style=" background-size: 100%; text-align: center; background-color: <?= /* @noEscape */ $barData['bar_background_color']?>; font-size: <?= /* @noEscape */ $barData['bar_font_size'].'px'?>">
<a <?= /* @noEscape */ $barData['is_clickable'] ? 'href='.$barData['bar_link_url'] : null ?> <a <?= /* @noEscape */ $barData['is_clickable'] ? 'href='.$barData['bar_link_url'] : null ?>
<?= /* @noEscape */ $barData['is_clickable'] ? ($barData['is_link_open_in_new_page'] ? 'target=_blank' : 'target=_self') : null ?> <?= /* @noEscape */ $barData['is_clickable'] ? ($barData['is_link_open_in_new_page'] ? 'target=_blank' : 'target=_self') : null ?>
style="width: 100%;height: 100%; display: block; color: <?= /* @noEscape */ $barData['bar_text_color']?>"> style="width: 100%;height: 100%; display: block; color: <?= /* @noEscape */ $barData['bar_text_color']?>">
<div data-bind="scope: 'free-shipping-scope<?= $barData['entity_id']; ?>'"> <div data-bind="scope: 'free-shipping-scope'">
<p data-bind="html: goalMessage"></p> <p data-bind="html: goalMessage"></p>
</div> </div>
</a> </a>
...@@ -68,7 +61,7 @@ endif; ...@@ -68,7 +61,7 @@ endif;
"*": { "*": {
"Magento_Ui/js/core/app": { "Magento_Ui/js/core/app": {
"components": { "components": {
"free-shipping-scope<?= $barData['entity_id']; ?>": { "free-shipping-scope": {
"component": "Sparsh_FreeShippingBar/js/free_shipping_bar", "component": "Sparsh_FreeShippingBar/js/free_shipping_bar",
"goal": "<?= /* @noEscape */ $barData['goal'] ?>", "goal": "<?= /* @noEscape */ $barData['goal'] ?>",
"currency": "<?= /* @noEscape */ $block->getCurrentCurrencySymbol() ?>", "currency": "<?= /* @noEscape */ $block->getCurrentCurrencySymbol() ?>",
......
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis
/**
* Product view template
*
* @var $block \Magento\Catalog\Block\Product\View
*/
?>
<?php $_helper = $this->helper(Magento\Catalog\Helper\Output::class); ?>
<?php $_product = $block->getProduct(); ?>
<?php echo $block->getLayout()->createBlock('Magento\Cms\Block\Block')->setBlockId('product_info_addtocar_top_slogan')->toHtml();?>
<div class="product-add-form">
<form data-product-sku="<?= $block->escapeHtml($_product->getSku()) ?>"
action="<?= $block->escapeUrl($block->getSubmitUrl($_product)) ?>" method="post"
id="product_addtocart_form"<?php if ($_product->getOptions()) :?> enctype="multipart/form-data"<?php endif; ?>>
<input type="hidden" name="product" value="<?= (int)$_product->getId() ?>" />
<input type="hidden" name="selected_configurable_option" value="" />
<input type="hidden" name="related_product" id="related-products-field" value="" />
<input type="hidden" name="item" value="<?= (int)$block->getRequest()->getParam('id') ?>" />
<?= $block->getBlockHtml('formkey') ?>
<?= $block->getChildHtml('form_top') ?>
<?php if (!$block->hasOptions()) :?>
<?= $block->getChildHtml('product_info_form_content') ?>
<?php else :?>
<?php if ($_product->isSaleable() && $block->getOptionsContainer() == 'container1') :?>
<?= $block->getChildChildHtml('options_container') ?>
<?php endif;?>
<?php endif; ?>
<?php if ($_product->isSaleable() && $block->hasOptions() && $block->getOptionsContainer() == 'container2') :?>
<?= $block->getChildChildHtml('options_container') ?>
<?php endif;?>
<?= $block->getChildHtml('form_bottom') ?>
</form>
</div>
<script type="text/x-magento-init">
{
"[data-role=priceBox][data-price-box=product-id-<?= $block->escapeHtml($_product->getId()) ?>]": {
"priceBox": {
"priceConfig": <?= /* @noEscape */ $block->getJsonConfig() ?>
}
}
}
</script>
...@@ -11,15 +11,18 @@ ...@@ -11,15 +11,18 @@
.field.size-fits .field.choice { .field.size-fits .field.choice {
clear: both; clear: both;
display: inline-block; display: inline-block;
padding: 0 5px;
line-height: 30px; line-height: 30px;
margin-right: 20px; margin-right: 20px;
} }
/* 猫头鹰选择 */
.size-fits + .size-fits {
padding: 0 5px;
}
.field.size-fits .field.choice input { .field.size-fits .field.choice input {
margin-right: 5px; margin-right: 5px;
} }
</style> </style>
<div class="block review-add"> <div class="block review-add joshine-bg-silver joshine-hidden">
<div class="block-title"><strong><?= $block->escapeHtml(__('Write Your Own Review')) ?></strong></div> <div class="block-title"><strong><?= $block->escapeHtml(__('Write Your Own Review')) ?></strong></div>
<div class="block-content"> <div class="block-content">
<?php if ($block->getAllowWriteReviewFlag()):?> <?php if ($block->getAllowWriteReviewFlag()):?>
......
...@@ -10,15 +10,7 @@ ...@@ -10,15 +10,7 @@
<img loading="lazy" src="<?php echo $block->getViewFileUrl('images/goTop.png'); ?>"> <img loading="lazy" src="<?php echo $block->getViewFileUrl('images/goTop.png'); ?>">
</p> </p>
<script> <script>
//go to top button show
window.onscroll = function (){
var top = document.getElementById("topBtn");
if (document.documentElement.scrollTop > 200 || document.body.scrollTop > 200){
top.style.display="block";
}else {
top.style.display="none";
}
}
var userAgentInfo = navigator.userAgent; var userAgentInfo = navigator.userAgent;
var Agents = new Array("Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"); var Agents = new Array("Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod");
var flag = true; var flag = true;
...@@ -45,11 +37,7 @@ ...@@ -45,11 +37,7 @@
}); });
//go to top button click
$('#topBtn').click(function(){
document.body.scrollTop = 0;
document.documentElement.scrollTop = 0;
});
if (flag){ if (flag){
var imgH = $('.logo').height(); var imgH = $('.logo').height();
imgH= Math.ceil(imgH/3); imgH= Math.ceil(imgH/3);
......
...@@ -21,14 +21,7 @@ $logoHeight = $logoSizeResolver !== null && $logoSizeResolver->getHeight() ...@@ -21,14 +21,7 @@ $logoHeight = $logoSizeResolver !== null && $logoSizeResolver->getHeight()
?> ?>
<script> <script>
window.onscroll = function (){
var top = document.getElementById("topBtn");
if (document.documentElement.scrollTop > 200 || document.body.scrollTop > 200){
top.style.display="block";
}else {
top.style.display="none";
}
}
var userAgentInfo = navigator.userAgent; var userAgentInfo = navigator.userAgent;
var Agents = new Array("Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"); var Agents = new Array("Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod");
......
...@@ -2160,6 +2160,7 @@ height: 40px !important; ...@@ -2160,6 +2160,7 @@ height: 40px !important;
line-height:22px!important; line-height:22px!important;
min-width: 40px; min-width: 40px;
text-align: center; text-align: center;
font-size: 15px;
} }
.block-minicart .block-content>.actions .action.checkout{ .block-minicart .block-content>.actions .action.checkout{
...@@ -2228,6 +2229,8 @@ font-weight: bold; ...@@ -2228,6 +2229,8 @@ font-weight: bold;
.product.info .review-add { .product.info .review-add {
float: left; float: left;
width: 100% !important; width: 100% !important;
padding: 24px;
margin-bottom: 20px;
} }
@media (max-width: 768px){ @media (max-width: 768px){
...@@ -2349,6 +2352,7 @@ background-color: #000; ...@@ -2349,6 +2352,7 @@ background-color: #000;
line-height: 0px; line-height: 0px;
} }
.product-info-wrapper .product-info-main { .product-info-wrapper .product-info-main {
margin-left: 2%; margin-left: 2%;
width: 100%; width: 100%;
...@@ -2414,6 +2418,7 @@ background-color: #000; ...@@ -2414,6 +2418,7 @@ background-color: #000;
font-size: 16px; font-size: 16px;
font-weight: 400; font-weight: 400;
} }
} }
@media (max-width: 1024px) { @media (max-width: 1024px) {
...@@ -2704,7 +2709,7 @@ tr.grand.totals { ...@@ -2704,7 +2709,7 @@ tr.grand.totals {
} }
} }
.choice.field { .size-fits + .size-fits {
padding-left: 15px; padding-left: 15px;
} }
...@@ -2723,6 +2728,14 @@ tr.grand.totals { ...@@ -2723,6 +2728,14 @@ tr.grand.totals {
.amreview-submit-form .review-fieldset .field{ .amreview-submit-form .review-fieldset .field{
margin: 0px; margin: 0px;
} }
.product-info-wrapper .page-title span.base{
font-size: 15px;
}
.product-info-stock-sku{
font-size: 15px;
}
} }
...@@ -2843,7 +2856,9 @@ strong#block-related-heading,strong#block-upsell-heading{ font-weight: 600; colo ...@@ -2843,7 +2856,9 @@ strong#block-related-heading,strong#block-upsell-heading{ font-weight: 600; colo
position:fixed; position:fixed;
width: 4%; width: 4%;
z-index: 10; z-index: 10;
display: none;
} }
/*导入Joshine工具类*/ /*导入Joshine工具类*/
@import "./_joshine_col"; @import "./_joshine_col";
@import "./_joshine_utils";
...@@ -733,9 +733,9 @@ ...@@ -733,9 +733,9 @@
} }
.joshine-center-block { .joshine-center-block {
display: block; display: block !important;
margin-right: auto; margin-right: auto !important;
margin-left: auto; margin-left: auto !important;
} }
.joshine-pull-right { .joshine-pull-right {
float: right !important; float: right !important;
......
/*正常大小*/
.joshine-rating-container {
overflow: hidden;
margin: 0 0 10px;
width: 160px;
height: 27px;
background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzMiIGhlaWdodD0iMjciIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZmlsbD0ibm9uZSIgZD0iTTAgMGgzM3YyN0gweiIvPjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMTcuMzA1IDEwLjMxM0wxNCAwbC0zLjMwNSAxMC4zMTNIMGw4LjY1MiA2LjM3NEw1LjM0OCAyNyAxNCAyMC42MjYgMjIuNjUyIDI3bC0zLjMwNS0xMC4zMTNMMjggMTAuMzEzSDE3LjMwNXptNC42NTQgMS45OWgtNi4wOEwxNCA2LjQ0bC0xLjg3OSA1Ljg2M2gtNi4wOGw0LjkxOSAzLjYyNC0xLjg4IDUuODYzTDE0IDE4LjE2Nmw0LjkyIDMuNjI0LTEuODgtNS44NjMgNC45Mi0zLjYyNHoiIGZpbGw9IiNCMUFFQUUiLz48L3N2Zz4=);
}
.joshine-rating-container .joshine-rating-starts {
height: 100%;
background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzMiIGhlaWdodD0iMjciIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZmlsbD0ibm9uZSIgZD0iTTAgMGgzM3YyN0gweiIvPjxwYXRoIGQ9Ik0xNCAwbDMuMzA1IDEwLjMxM0gyOGwtOC42NTIgNi4zNzRMMjIuNjUyIDI3IDE0IDIwLjYyNiA1LjM0OCAyN2wzLjMwNC0xMC4zMTNMMCAxMC4zMTNoMTAuNjk1TDE0IDB6IiBmaWxsPSIjRUQ5RDAwIi8+PC9zdmc+);
}
/*小号评分容器*/
.joshine-rating-container.joshine-rating-small {
width: 107px;
height: 18px;
background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjIiIGhlaWdodD0iMTgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZmlsbD0ibm9uZSIgZD0iTTAgMGgyMnYxOEgweiIvPjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMTEuNzQzIDYuODc1TDkuNSAwIDcuMjU3IDYuODc1SDBsNS44NzEgNC4yNUwzLjYzIDE4IDkuNSAxMy43NSAxNS4zNzEgMThsLTIuMjQyLTYuODc1TDE5IDYuODc1aC03LjI1N3pNMTQuOSA4LjIwMmgtNC4xMjZMOS41IDQuMjkzbC0xLjI3NSAzLjkxSDQuMDk5bDMuMzM4IDIuNDE1LTEuMjc1IDMuOTA5TDkuNSAxMi4xMWwzLjMzOCAyLjQxNi0xLjI3NS0zLjkxIDMuMzM4LTIuNDE1eiIgZmlsbD0iI0IxQUVBRSIvPjwvc3ZnPg==);
}
/*小号评分图标*/
.joshine-rating-container.joshine-rating-small .joshine-rating-starts {
background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjIiIGhlaWdodD0iMTgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZmlsbD0ibm9uZSIgZD0iTTAgMGgyMnYxOEgweiIvPjxwYXRoIGQ9Ik05LjUgMGwyLjI0MyA2Ljg3NUgxOWwtNS44NzEgNC4yNUwxNS4zNyAxOCA5LjUgMTMuNzUgMy42MjkgMThsMi4yNDItNi44NzVMMCA2Ljg3NWg3LjI1N0w5LjUgMHoiIGZpbGw9IiNFRDlEMDAiLz48L3N2Zz4=);
}
/*点赞图标*/
.joshine-like {
background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMTgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZmlsbD0ibm9uZSIgZD0iTTAgMGgyMHYxOEgweiIvPjxwYXRoIGQ9Ik0zLjg5NiA3LjI4NkguNTU2QS41NTQuNTU0IDAgMCAwIDAgNy44NDh2OC45ODVjMCAuMzE0LjI0NS41NjEuNTU3LjU2MWgzLjM0YS41NTMuNTUzIDAgMCAwIC41NTYtLjU2MVY3Ljg0OGEuNTUzLjU1MyAwIDAgMC0uNTU3LS41NjJ6TTIwIDguMzVjMC0uOTE1LS42MjMtMS44NC0xLjgxMy0xLjg0aC01LjM5MmMuNzctMS4zOTEuOTk3LTMuMzQ3LjQ2Mi00Ljc3OEMxMi44NjMuNjggMTIuMTEuMDY2IDExLjEzOS4wMDRsLS4wMTYtLjAwMkExLjE3IDEuMTcgMCAwIDAgOS44ODkgMS4wOEM5Ljc1MSAyLjUgOS4xMzQgNS4wMSA4LjI1MSA1LjljLS43NDQuNzUtMS4zOCAxLjA2NS0yLjQzNiAxLjU4Ni0uMTUyLjA3NS0uMzIuMTU3LS40OTYuMjQ2YTEuNCAxLjQgMCAwIDEgLjAwNS4xMTd2OC44OTNsLjM3Ny4xM0M3LjQ0MyAxNy40NzggOC45NDggMTggMTEuMjQ4IDE4aDQuMzZjMS4xOSAwIDEuODEyLS45MjYgMS44MTItMS44NDFhMiAyIDAgMCAwLS4xNjMtLjc5MyAxLjcxOCAxLjcxOCAwIDAgMCAxLS41NTljLjI5Mi0uMzM0LjQ1My0uNzc5LjQ1My0xLjI1MSAwLS4yNy0uMDU1LS41NDMtLjE2My0uNzkyLjk1Ni0uMTY2IDEuNDUzLS45OTMgMS40NTMtMS44MSAwLS40NzUtLjE2OC0uOTUzLS40OTQtMS4zMDIuMzI2LS4zNS40OTQtLjgyNy40OTQtMS4zMDJ6IiBmaWxsPSIjQzRDNEM0Ii8+PC9zdmc+);
margin: 0 0 0 10px;
width: 20px;
height: 18px;
display: inline-block;
cursor: pointer;
transition: .3s;
}
.joshine-like.active {
background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMTgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZmlsbD0ibm9uZSIgZD0iTTAgMGgyMHYxOEgweiIvPjxwYXRoIGQ9Ik0zLjg5NiA3LjI4NkguNTU2QS41NTQuNTU0IDAgMCAwIDAgNy44NDh2OC45ODVjMCAuMzE0LjI0NS41NjEuNTU3LjU2MWgzLjM0YS41NTMuNTUzIDAgMCAwIC41NTYtLjU2MVY3Ljg0OGEuNTUzLjU1MyAwIDAgMC0uNTU3LS41NjJ6TTIwIDguMzVjMC0uOTE1LS42MjMtMS44NC0xLjgxMy0xLjg0aC01LjM5MmMuNzctMS4zOTEuOTk3LTMuMzQ3LjQ2Mi00Ljc3OEMxMi44NjMuNjggMTIuMTEuMDY2IDExLjEzOS4wMDRsLS4wMTYtLjAwMkExLjE3IDEuMTcgMCAwIDAgOS44ODkgMS4wOEM5Ljc1MSAyLjUgOS4xMzQgNS4wMSA4LjI1MSA1LjljLS43NDQuNzUtMS4zOCAxLjA2NS0yLjQzNiAxLjU4Ni0uMTUyLjA3NS0uMzIuMTU3LS40OTYuMjQ2YTEuNCAxLjQgMCAwIDEgLjAwNS4xMTd2OC44OTNsLjM3Ny4xM0M3LjQ0MyAxNy40NzggOC45NDggMTggMTEuMjQ4IDE4aDQuMzZjMS4xOSAwIDEuODEyLS45MjYgMS44MTItMS44NDFhMiAyIDAgMCAwLS4xNjMtLjc5MyAxLjcxOCAxLjcxOCAwIDAgMCAxLS41NTljLjI5Mi0uMzM0LjQ1My0uNzc5LjQ1My0xLjI1MSAwLS4yNy0uMDU1LS41NDMtLjE2My0uNzkyLjk1Ni0uMTY2IDEuNDUzLS45OTMgMS40NTMtMS44MSAwLS40NzUtLjE2OC0uOTUzLS40OTQtMS4zMDIuMzI2LS4zNS40OTQtLjgyNy40OTQtMS4zMDJ6IiBmaWxsPSIjMUFCOTAwIi8+PC9zdmc+);
}
.joshine-diss {
background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMTgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZmlsbD0ibm9uZSIgZD0iTTAgMThoMjBWMEgweiIvPjxwYXRoIGQ9Ik0zLjg5NiAxMC43MTRILjU1NkEuNTU0LjU1NCAwIDAgMSAwIDEwLjE1MlYxLjE2N0MwIC44NTMuMjQ1LjYwNi41NTcuNjA2aDMuMzRjLjMxMSAwIC41NTYuMjQ3LjU1Ni41NjF2OC45ODVhLjU1My41NTMgMCAwIDEtLjU1Ny41NjJ6TTIwIDkuNjVjMCAuOTE1LS42MjMgMS44NC0xLjgxMyAxLjg0aC01LjM5MmMuNzcgMS4zOTEuOTk3IDMuMzQ3LjQ2MiA0Ljc3Ny0uMzk0IDEuMDUzLTEuMTQ2IDEuNjY3LTIuMTE4IDEuNzNoLS4wMTZhMS4xNyAxLjE3IDAgMCAxLTEuMjM0LTEuMDc2Yy0uMTM4LTEuNDItLjc1NS0zLjkzLTEuNjM4LTQuODItLjc0NC0uNzUtMS4zOC0xLjA2NS0yLjQzNi0xLjU4Ni0uMTUyLS4wNzUtLjMyLS4xNTctLjQ5Ni0uMjQ2YTEuNCAxLjQgMCAwIDAgLjAwNS0uMTE3VjEuMjZsLjM3Ny0uMTNDNy40NDMuNTIyIDguOTQ4IDAgMTEuMjQ4IDBoNC4zNmMxLjE5IDAgMS44MTIuOTI2IDEuODEyIDEuODQxYTIgMiAwIDAgMS0uMTYzLjc5M2MuMzk3LjA3Mi43NDMuMjY0IDEgLjU1OS4yOTIuMzM0LjQ1My43NzkuNDUzIDEuMjUxIDAgLjI3LS4wNTUuNTQzLS4xNjMuNzkyLjk1Ni4xNjYgMS40NTMuOTkzIDEuNDUzIDEuODEgMCAuNDc1LS4xNjguOTUyLS40OTQgMS4zMDIuMzI2LjM1LjQ5NC44MjcuNDk0IDEuMzAyeiIgZmlsbD0iI0M0QzRDNCIvPjwvc3ZnPg==);
}
.joshine-diss.active {
background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMTgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZmlsbD0ibm9uZSIgZD0iTTAgMThoMjBWMEgweiIvPjxwYXRoIGQ9Ik0zLjg5NiAxMC43MTRILjU1NkEuNTU0LjU1NCAwIDAgMSAwIDEwLjE1MlYxLjE2N0MwIC44NTMuMjQ1LjYwNi41NTcuNjA2aDMuMzRjLjMxMSAwIC41NTYuMjQ3LjU1Ni41NjF2OC45ODVhLjU1My41NTMgMCAwIDEtLjU1Ny41NjJ6TTIwIDkuNjVjMCAuOTE1LS42MjMgMS44NC0xLjgxMyAxLjg0aC01LjM5MmMuNzcgMS4zOTEuOTk3IDMuMzQ3LjQ2MiA0Ljc3Ny0uMzk0IDEuMDUzLTEuMTQ2IDEuNjY3LTIuMTE4IDEuNzNoLS4wMTZhMS4xNyAxLjE3IDAgMCAxLTEuMjM0LTEuMDc2Yy0uMTM4LTEuNDItLjc1NS0zLjkzLTEuNjM4LTQuODItLjc0NC0uNzUtMS4zOC0xLjA2NS0yLjQzNi0xLjU4Ni0uMTUyLS4wNzUtLjMyLS4xNTctLjQ5Ni0uMjQ2YTEuNCAxLjQgMCAwIDAgLjAwNS0uMTE3VjEuMjZsLjM3Ny0uMTNDNy40NDMuNTIyIDguOTQ4IDAgMTEuMjQ4IDBoNC4zNmMxLjE5IDAgMS44MTIuOTI2IDEuODEyIDEuODQxYTIgMiAwIDAgMS0uMTYzLjc5M2MuMzk3LjA3Mi43NDMuMjY0IDEgLjU1OS4yOTIuMzM0LjQ1My43NzkuNDUzIDEuMjUxIDAgLjI3LS4wNTUuNTQzLS4xNjMuNzkyLjk1Ni4xNjYgMS40NTMuOTkzIDEuNDUzIDEuODEgMCAuNDc1LS4xNjguOTUyLS40OTQgMS4zMDIuMzI2LjM1LjQ5NC44MjcuNDk0IDEuMzAyeiIgZmlsbD0iI0QyMzczNyIvPjwvc3ZnPg==);
}
/**进度条*/
.joshine-process {
width: 80px;
height: 6px;
background: #dfdfdf;
vertical-align: middle;
display: inline-block;
line-height: 14px;
}
.joshine-process .joshine-process-active {
height: 100%;
margin: 0;
background: #ed9d00;
}
/*背景色组*/
.joshine-bg-default {
background-color: #f7f8fa !important;
}
.joshine-bg-darkgray {
background-color: #767676 !important;
}
.joshine-bg-gray {
background-color: #999 !important;
}
.joshine-bg-silver {
background-color: #f7f8fa;
}
.joshine-bg-img-default {
cursor: pointer;
background-image: url(../images/placeholders_small.jpg);
background-size: cover;
background-repeat: no-repeat;
background-position: center center;
}
/*玄铁灰*/
.joshine-font-c-darkgray {
color: #767676 !important;
}
/*沉稳灰*/
.joshine-font-c-gray {
color: #999 !important;
}
/*神秘银*/
.joshine-font-c-silver {
color: #f7f8fa;
}
/*深邃黑*/
.joshine-font-c-deepin {
color: #222222 !important;
}
/*字重组*/
.joshine-font-w-bolder{
font-weight: bolder;
}
/*字号组*/
.joshine-font-size-default {
font-size: 16px;
}
.joshine-font-mini {
font-size: 12px;
}
.joshine-font-mini-plus {
font-size:13px;
}
.joshine-font-text {
font-size:14px;
}
.joshine-foot-mark {
font-size: 13px;
line-height: 13px;
color: #999;
}
/*按钮*/
.joshine-btn {
display: inline-block;
padding: 6px 12px;
margin-bottom: 0;
font-size: 14px;
font-weight: normal;
line-height: 1.42857143;
text-align: center;
white-space: nowrap;
vertical-align: middle;
-ms-touch-action: manipulation;
touch-action: manipulation;
cursor: pointer;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
background-image: none;
border: 1px solid transparent;
}
.joshine-btn:focus,
.joshine-btn:active:focus,
.joshine-btn.active:focus,
.joshine-btn.focus,
.joshine-btn:active.focus,
.joshine-btn.active.focus {
outline: 5px auto -webkit-focus-ring-color;
outline-offset: -2px;
}
.joshine-btn:active,
.joshine-btn.active {
background-image: none;
outline: 0;
-webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
}
.joshine-btn.disabled,
.joshine-btn[disabled],
fieldset[disabled] .joshine-btn {
cursor: not-allowed;
filter: alpha(opacity=65);
-webkit-box-shadow: none;
box-shadow: none;
opacity: .65;
}
a.joshine-btn.disabled,
fieldset[disabled] a.joshine-btn {
pointer-events: none;
}
.joshine-btn-dark {
color: #fff;
background-color: #000;
border-color: #000;
}
.joshine-inline-block {
display: inline-block;
}
.joshine-pagination {
display: flex;
align-items: center;
justify-content: center;
min-width: 24px;
height: 24px;
user-select: none;
color: #222;
font-size: 14px;
}
.joshine-pagination .joshine-pagination-btn {
margin-left: 12px;
padding: 0 4px;
border-radius: 12px;
text-align: center;
color: #222;
font-weight: 700;
min-width: 24px;
height: 24px;
line-height: normal;
display: flex;
align-items: center;
justify-content: center;
}
.joshine-pagination .joshine-pagination-btn.active {
color: #fff;
background: #222;
}
.joshine-pagination-hover:not(.active) {
cursor: pointer;
}
.joshine-pagination-hover:not(.active):hover {
background: #e0e0e0;
}
.joshine-pagination .joshine-pagination-next {
margin-left: 12px;
}
.joshine-pagination .joshine-pagination-btn.pagination-btn-disabled {
cursor: not-allowed;
color: #c2c2c2;
}
.joshine-pagination-right {
justify-content: flex-end;
}
.joshine-pagination-left {
justify-content: flex-start;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment