diff --git a/package.json b/package.json
index f35f9d71..cfc2add9 100644
--- a/package.json
+++ b/package.json
@@ -8,6 +8,8 @@
"bootstrap": "^4.1.1",
"chart.js": "^2.7.2",
"copy-webpack-plugin": "^5.0.3",
+ "cropper": "^4.0.0",
+ "font-awesome": "^4.7.0",
"interactjs": "^1.4.0-rc.13",
"jquery": "^1.9.1",
"jsplumb": "^2.10.1",
@@ -22,6 +24,7 @@
"react-chartjs-2": "^2.7.2",
"react-dom": "^16.4.0",
"react-intl": "^2.4.0",
+ "react-modal": "^3.9.1",
"react-redux": "^5.0.7",
"react-router-dom": "^4.3.1",
"react-scripts": "1.1.4",
diff --git a/src/components/ImageEditor.js b/src/components/ImageEditor.js
new file mode 100644
index 00000000..ccd4e226
--- /dev/null
+++ b/src/components/ImageEditor.js
@@ -0,0 +1,245 @@
+import React, {Component} from 'react';
+import Cropper from 'cropperjs';
+import 'font-awesome/css/font-awesome.min.css';
+import 'cropperjs/dist/cropper.css';
+import '../css/ImageEditor.css';
+
+class ImageEditor extends Component {
+
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ cropped: false,
+ cropping: false,
+ previousUrl: '',
+ type: '',
+ url: '',
+ enableEditor: false
+ } ;
+
+ this.cropper = null;
+ }
+
+ componentDidMount () {
+ const {mediaSource} = this.props;
+ this.setState({
+ ...this.state,
+ url: mediaSource
+ })
+ }
+
+ cropperSetup=()=>{
+
+ const image = document.getElementsByClassName('image')[0];
+ this.cropper = new Cropper(image, {
+ autoCrop: false,
+ dragMode: 'move',
+ background: false,
+
+ crop: ({ detail }) => {
+ if (detail.width > 0 && detail.height > 0 && !this.state.cropping) {
+ this.update({
+ cropping: true,
+ });
+ }
+ },
+ });
+ }
+
+ crop = () => {
+ const { cropper} = this;
+ const { cropping, url, type } = this.state;
+
+ if (cropping) {
+ this.update({
+ cropped: true,
+ cropping: false,
+ previousUrl: url,
+ url: cropper.getCroppedCanvas(type === 'image/png' ? {} : {
+ fillColor: '#fff',
+ }).toDataURL(type),
+ });
+ cropper.clear();
+ }
+ }
+
+ save = () => {
+ const { cropper} = this;
+ const {url, type } = this.state;
+
+ if(cropper) {
+ this.update({
+ previousUrl: url,
+ url: cropper.getCroppedCanvas(type === 'image/png' ? {} : {
+ fillColor: '#fff',
+ }).toDataURL(type),
+ });
+ cropper.clear();
+ }
+ }
+
+ clear = () => {
+ const { cropping } = this.state;
+ if (cropping) {
+ this.cropper.clear();
+ this.update({
+ cropping: false,
+ });
+ }
+ }
+
+ restore = () => {
+ const { previousUrl } = this.state;
+ if (previousUrl) {
+ this.setState({
+ ...this.state,
+ cropper: false,
+ cropping: false,
+ previousUrl: '',
+ url: this.state.previousUrl
+ }, () => {
+ this.updateCropper();
+ }
+ );
+ }
+ }
+
+ update = (updatedData) => {
+ this.setState({
+ ...this.state,
+ ...updatedData
+ }, () => {
+ if(this.state.cropped) {
+ this.updateCropper();
+ this.props.setMediaSource(this.state.url);
+ this.props.onClose();
+ }
+ });
+ }
+
+ updateCropper = () => {
+ if(this.cropper)
+ this.cropper.destroy();
+ this.cropper = null;
+ this.cropperSetup();
+ }
+
+ editAction = (e) => {
+ if(this.state.url!=='') {
+ const { cropper } = this;
+ let action = e.currentTarget.dataset.action;
+ switch (action) {
+ case 'move':
+ break;
+
+ case 'crop':
+ cropper.setDragMode(action);
+ break;
+
+ case 'zoom-in':
+ cropper.zoom(0.1);
+ this.save();
+ break;
+
+ case 'zoom-out':
+ cropper.zoom(-0.1);
+ this.save();
+ break;
+
+ case 'rotate-left':
+ cropper.rotate(-90);
+ this.save();
+ break;
+
+ case 'rotate-right':
+ cropper.rotate(90);
+ this.save();
+ break;
+
+ case 'flip-horizontal':
+ cropper.scaleX(-cropper.getData().scaleX || -1);
+ this.save();
+ break;
+
+ case 'flip-vertical':
+ cropper.scaleY(-cropper.getData().scaleY || -1);
+ this.save();
+ break;
+
+ default:
+ }
+ }
+ }
+
+ enableEditor = () => {
+ this.setState({
+ ...this.state,
+ enableEditor: true
+ });
+ this.cropperSetup();
+ }
+
+ render () {
+ return (
+
+
+

+
+
+ {!this.state.enableEditor &&
+
+ ,
+
+
+ }
+ {this.state.enableEditor &&
+
+
+
+
+ }
+ {this.state.enableEditor &&
+
+
+
+
+
+
+
+
+
+
+
+ }
+
+ )
+ }
+}
+
+export default ImageEditor;
diff --git a/src/components/MultimediaJSX.js b/src/components/MultimediaJSX.js
index 18791c9d..49fa7222 100644
--- a/src/components/MultimediaJSX.js
+++ b/src/components/MultimediaJSX.js
@@ -40,7 +40,7 @@ export function QuestionOptionsJSX(props){
export function QuestionJSX(props){
let question;
- let {questionType, questionData, handleChangeQues, showMedia, speak} = props;
+ let {questionType, questionData, handleChangeQues, showMedia, speak, setImageEditorSource} = props;
if( questionType === MULTIMEDIA.text)
question = (
{showMedia(questionData)}}
+ onClick = {()=>{showMedia(questionData, 'img', setImageEditorSource)}}
alt="Question"/>
);
@@ -93,7 +93,7 @@ export function QuestionJSX(props){
export function AnswerOptionsJSX(props){
- const {selectOptionType, resetOption, showMedia, speak, options, changeOrder, handleChangeOption, templateType} = props;
+ const {selectOptionType, resetOption, showMedia, speak, options, changeOrder, handleChangeOption, templateType, setImageEditorSource} = props;
// Answer-Options
let answerOptions = options.map((option, i) => {
if(!option.type)
@@ -165,7 +165,7 @@ export function AnswerOptionsJSX(props){

{showMedia(option.data)}}
+ onClick = {()=>{showMedia(option.data, 'img', setImageEditorSource(i))}}
alt="Option"/>