<link href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css">
<script src="//netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script>
<script src="//code.jquery.com/jquery-1.11.1.min.js"></script>
<!------ Include the above in your HEAD tag ---------->
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title></title>
<link href="/css/markupImage.css" />
</head>
<body>
<div id="app"></div>
<template id="areaListItemTemplate">
<li class="areas-list__item" v-bind:class="[ editing ? 'areas-list__item--editing' : 'areas-list__item--view' ]">
<template v-if="editing">
<ui-textbox v-model="title" placeholder="Введите название" class="fluid-input"></ui-textbox>
<ui-textbox v-model="url" placeholder="Укажите ссылку" class="fluid-input"></ui-textbox>
<ui-colorpicker v-model="color" class="fluid-input"></ui-colorpicker>
<div class="btn-group">
<button type="button" class="btn btn-primary" @click="accept">Добавить</button>
<button type="button" class="btn btn-secondary" @click="cancel">Отмена</button>
</div>
</template>
<template v-else>
<div class="btn-group">
<button type="button" class="btn edit-btn" @click="$emit('edit', area)"></button>
<button type="button" class="btn remove-btn" @click="$emit('remove', area)"></button>
</div>
<span class="areas-list__title">{{ area.title }}</span>
</template>
</li>
</template>
<template id="appTemplate">
<div class="wss-imagemarkup">
<div class="wss-imagemarkup__editor image-markup-editor">
<div class="image-markup-editor__image">
<div class="image-markup-editor__container" v-bind:style="containerStyle" @click="appendPoint">
<img v-bind:src="imageUrl" @load.once="imageLoaded" />
<canvas ref="canvas"></canvas>
</div>
</div>
<div class="image-markup-editor__controls">
<h3>Редактировать</h3>
<ul class="areas-list">
<li class="areas-list__create-area" v-bind:class="{ 'areas-list__create-area--hidden': activeArea != null }">
<button type="button" class="btn create-btn" @click="createArea">Добавить ссылку</button>
</li>
<area-list-item v-for="area in areas"
v-bind:area="area"
v-bind:editing="area === activeArea"
@accept="commitAreaChanges"
@cancel="discardAreaChanges"
@edit="selectArea"
@remove="removeArea">
</area-list-item>
</ul>
<div class="btn-group">
<button type="button" class="btn btn-primary" @click="save">Сохранить</button>
<button type="button" class="btn btn-secondary" @click="cancel">Отмена</button>
</div>
</div>
</div>
</div>
</template>
<script src="/plugins/vuejs/vue.min.js"></script>
<script src="/plugins/paper/paper-core.min.js"></script>
<script src="/plugins/jscolor.js"></script>
<script>
var DEFAULT_STROKE_COLOR = '#ebfb5b';
var placeholderFeature = 'placeholder' in HTMLInputElement.prototype;
function getPoint(e) {
var rect = e.target.getBoundingClientRect();
return [e.clientX - rect.left, e.clientY - rect.top];
}
Vue.component('ui-colorpicker', {
template: '<input type="text" :value="value" @change="update" class="jscolor input"/>',
model: {
prop: 'value',
event: 'change'
},
props: ['value'],
mounted: function () {
jscolor.installByClassName('jscolor');
},
methods: {
update: function (e) {
var val = e.target.value;
if (val[0] !== '#') {
val = '#' + val;
}
this.$emit('change', val);
}
}
});
Vue.component('ui-textbox', {
template: '<input type="text" :value="inputValue" class="input" :class="{\'input--placeholder\': placeholderActive }" :placeholder.once="placeholder" @input="$emit(\'input\', $event.target.value)"/>',
model: {
prop: 'value',
event: 'input'
},
props: ['value', 'placeholder'],
data: function () {
return {
inputValue: null
}
},
mounted: function () {
this.inputValue = this.value;
if (!placeholderFeature) {
this.inputValue = this.inputValue || this.placeholder;
this.$el.addEventListener('focus', (function () {
if (this.inputValue != this.value) {
this.inputValue = this.value
}
}).bind(this));
this.$el.addEventListener('blur', (function () {
if (!this.value) {
this.inputValue = this.placeholder
}
}).bind(this));
this.$watch('value', (function (newValue) {
if (!!newValue) {
this.inputValue = newValue;
} else {
this.inputValue = this.placeholder;
}
}).bind(this));
}
},
computed: {
placeholderActive: function () {
return !window.placeholderFeature && this.inputValue === this.placeholder;
}
}
})
Vue.component('area-list-item', {
template: '#areaListItemTemplate',
props: ['area', 'editing'],
mounted: function () {
this.createPath();
},
destroyed: function () {
if (this.path) {
this.path.remove();
}
},
data: function () {
var color = this.area.color || DEFAULT_STROKE_COLOR;
return {
url: this.area.url,
title: this.area.title,
color: color,
path: null
}
},
methods: {
accept: function () {
if (!this.url || !this.title) {
alert('Необходимо указать название и ссылку');
return;
}
this.area.url = this.url;
this.area.title = this.title;
this.area.color = this.color;
this.$emit('accept', this.area);
},
cancel: function () {
if (this.id) {
this.url = this.area.url;
this.title = this.area.title;
this.color = this.area.color;
this.$emit('cancel', this.area);
} else {
this.$emit('remove', this.area);
}
},
createPath: function () {
if (!paper.project) {
setTimeout(this.createPath, 100);
return;
}
this.path = new paper.Path({
segments: this.area.points || [],
selected: this.editing,
fillColor: new paper.Color(255, 255, 255, 0.5),
strokeWidth: 2,
strokeColor: this.color
});
}
},
watch: {
'editing': function (newVal) {
if (!this.path)
return;
if (newVal) {
this.path.selected = true;
} else {
this.path.selected = false;
}
},
'area.points': function (newValue) {
if (this.path && newValue && newValue.length > 0) {
var point = newValue[newValue.length - 1];
this.path.add(new paper.Point(point[0], point[1]));
}
},
'area.color': function (newValue) {
if (this.path) {
this.path.strokeColor = newValue;
}
}
}
});
function create(data) {
data.image = data.image || {};
var app = new Vue({
el: '#app',
template: '#appTemplate',
data: {
uuid: data.uuid,
imageUrl: data.image.url || null,
imageWidth: 0,
imageHeight: 0,
areas: data.areas || [],
activeArea: null
},
methods: {
imageLoaded: function (event) {
var image = event.target;
this.imageWidth = image.width;
this.imageHeight = image.height;
this.$nextTick(this.setupCanvas);
},
setupCanvas: function () {
paper.setup(this.$refs.canvas);
},
createArea: function () {
if (this.activeArea === null) {
var newArea = {
id: 0,
url: '',
title: '',
points: []
};
this.areas.push(newArea);
this.selectArea(newArea);
}
},
commitAreaChanges: function (area) {
if (!area.id) {
area.id = this.areas.reduce(function (max, current) {
return Math.max(max, current.id);
}, 0) + 1;
}
this.selectArea();
},
discardAreaChanges: function (area) {
if (area.id) {
this.removeArea(area.id);
}
},
selectArea: function (area) {
this.activeArea = area || null;
},
removeArea: function (area) {
if (area === this.activeArea) {
this.selectArea(null);
}
var index = this.areas.indexOf(area);
this.areas.splice(index, 1);
},
appendPoint: function (event) {
if (!this.activeArea)
return;
if (!this.activeArea.points) {
Vue.set(this.activeArea, 'points', []);
}
this.activeArea.points.push(getPoint(event));
},
save: function () {
var updated = {
uuid: this.uuid,
image: {
url: this.imageUrl,
width: this.imageWidth,
height: this.imageHeight
},
areas: JSON.parse(JSON.stringify(this.areas))
};
window.parent.wssc.ribbon.imageMarkupUpdated(updated);
},
cancel: function () {
window.parent.wssc.ribbon.closeImageMarkupEditor();
}
},
computed: {
containerStyle: function () {
return {
width: this.imageWidth > 0 ? this.imageWidth + 'px' : 'auto',
height: this.imageHeight > 0 ? this.imageHeight + 'px' : 'auto',
}
}
}
});
}
</script>
</body>
</html>