Gitlab CSE Unil

Commit 46db4303 authored by Julien Furrer's avatar Julien Furrer
Browse files

Big packed commit with a lot of mixed good stuff

Missing intermediate commits with good comments....
parent b7fc9b91
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('adim', '0004_auto_20150424_1209'),
]
operations = [
migrations.AddField(
model_name='anobj',
name='env',
field=models.CharField(blank=True, max_length=64, choices=[('', 'Standard'), ('cimaf', 'CIMAF')]),
),
]
......@@ -28,6 +28,10 @@ RANDOM_NODE = random.randrange(0, 1 << 48L) | 0x010000000000L
# Destination path for uploaded images, relative to MEDIA_ROOT
AO_IMAGES_PATH = 'ao_images'
AO_ENVIRON = (
('', 'Standard'),
('cimaf', 'CIMAF'),
)
class AOType(models.Model):
name = models.CharField(max_length=128)
......@@ -106,6 +110,8 @@ class AnObj(models.Model):
members = models.ManyToManyField(User, verbose_name=_("members"), through='AnObjMembership',
related_name='shared_anobjs', blank=True)
env = models.CharField(max_length=64, choices=AO_ENVIRON, blank=True)
image = models.ImageField(upload_to=get_image_path, verbose_name=_("image"), blank=True, null=True)
image_url = models.CharField(max_length=512, verbose_name=_("image url"), blank=True, null=True, default="")
......
......@@ -102,7 +102,7 @@ class AnObjSerializer(BaseAnObjSerializer):
class Meta:
model = AnObj
fields = ('id', 'uuid', 'name', 'owners', 'owner', 'owner_name', 'annotations', 'members',
'sharing_mode', 'sharing_opts', 'locked', 'allow_public_publishing')
'sharing_mode', 'sharing_opts', 'locked', 'allow_public_publishing', 'env')
class SharedAnObjSerializer(BaseAnObjSerializer):
......
......@@ -52,6 +52,10 @@ function($){
},
// If not defined or empty, will be built from the tools button found on the page.
// Otherwise use the value given. If an annotation type is not found in `activeTools` it wont be displayed
activeTools: [],
userEngine: {
limit: 7,
remoteUrl: "http://path/to/suggestion/url/?q=%QUERY",
......@@ -82,6 +86,15 @@ function($){
}, window[adim_config_varname] || {}, true);
if (!config.activeTools || config.activeTools.length == 0)
try {
config.activeTools = $("#draw-tool-tb [data-tool-name]").map(function(){
return $(this).attr('data-tool-name')
});
} catch (e) {
config.activeTools = null;
}
if (config.csrfToken) {
$.ajaxSetup({
beforeSend: function (xhr) {
......
/**
* Created by jfurrer on 22.06.15.
*/
define([
"jquery",
"underscore",
"paper",
"signals",
"adim/config",
"adim/view",
"adim/attributes",
"adim/io",
"adim/tools",
"adim/ui"
],
function($, _, paper, Signal, config, view, attributes, io, tools, ui){
/**
* Signal binding specific to CIMAF environment
* @private
*/
function _initSignalRouting(){
console.info("_initSignalRouting from env/cimaf");
var userLayer;
/**
* Count the number of intersections for each CMS Area.
* Only intersection with concordant stria are relevant.
*
* @param [annotation] {object} The annotation responsible for the update. If it is a 'cmsarea' it means
* this area was created/edited, so we don't need to update the count for other area.
*/
function updateAreaIntersectionCount(annotation) {
// If annotation is not area or concordant stria, there is no need for computation
if (annotation && ['cmsarea', 'concordantstr'].indexOf(annotation.data.type) < 0)
return;
if (!userLayer) {
userLayer = view.getOrCreateUserLayer(config.user.id, config.user.full_name)
}
if (!userLayer) return;
var zones = userLayer.children.filter(function(a) { return a.data.type && a.data.type === 'cmsarea'; });
var striae = userLayer.children.filter(function(a) { return a.data.type && a.data.type === 'concordantstr'; });
// If the current selection is an area, keep it's current nbConcordances
var items = paper.project.getSelectedItems();
var selectedArea = (items.length === 1 && items[0].data.type === 'cmsarea') ? items[0] : null;
var selectedAreaCount = selectedArea ? selectedArea.data.prop.nbConcordances : null;
var zone, nbInter, i, ii, nbStria = striae.length;
for (i = 0; zone = zones[i]; i++) {
nbInter = 0;
for (ii=0; ii<nbStria; ii++) {
nbInter += Math.round(zone.getIntersections(striae[ii]).length / 2);
}
zone.data.prop.nbConcordances = nbInter;
}
if (selectedAreaCount !== null && selectedArea.data.prop.nbConcordances !== selectedAreaCount) {
ui.displayProperties(selectedArea);
}
}
tools.events.annotationAdded.add(function(annotation) {
updateAreaIntersectionCount(annotation);
}, this, 100);
tools.events.annotationChanged.add(function(annotations) {
updateAreaIntersectionCount(annotations.length===1 ? annotations[0] : void(0));
}, this, 100);
// This should be called before the annotations are rendered, so we bind it to the imageLoaded event
// before the image loading is started. To do so we set a higher priority (100)
// than the handler defined in main. @TODO: get the cut position and cut margin from config
io.events.annotableLoaded.add(function(annotableData){
var concordantstrTool = tools.tools['concordantstr'];
var discordantstrTool = tools.tools['discordantstr'];
var cmsareaTool = tools.tools['cmsarea'];
view.events.imageLoaded.add(function(p){
var cutPos = p.raster.scaling.x * p.raster.width / 2 ;
var cutMargin = 25;
concordantstrTool.listeners.onCutPositionChanged(cutPos);
discordantstrTool.listeners.onCutPositionChanged(cutPos);
cmsareaTool.listeners.onCutPositionChanged(cutPos);
cmsareaTool.listeners.onSetAreaWidth(cutMargin*2);
});
}, 100);
}
function _initUI(params) {
/**
* UI initialization for CIMF. Called after ui.init by main
*/
// Init Type Display Selector
$(".adim-display-type-selector").find("button").on('click', function(event) {
var $but = $(this);
var activate = !$but.hasClass("active");
var typeName = $but.data('typeName');
if (event.metaKey) {
if (typeName !== 'image') {
view.selectItemsByType(typeName);
}
} else {
$but.toggleClass("active", activate);
if (typeName === 'image') {
attributes.setImageOpacity(activate ? 100 : 0, true);
//_opacitySlider.slider('setValue', activate ? 100 : 0);
} else {
view.toggleItemsByType($but.data('typeName'), activate);
ui.enableTool(typeName, activate);
}
}
});
}
return {
name: 'cimaf',
initSignalRouting: _initSignalRouting,
initUI: _initUI
}
});
\ No newline at end of file
/**
* This module is a proxy for the various environement defined
* It could be bypassed if the environment is directly defined in
* the main app by overriding the "env" path. See su_app.js
*/
define([
"underscore",
"adim/config",
// below are environments modules
"env/cimaf/env"
],
function(_, config){
var _envs = _.filter(
Array.prototype.slice.call(arguments, 2),
{name: config.annotable.env}
);
function _proxy(fname) {
return function() {
_.invoke(_envs, fname, arguments);
}
}
return {
name: 'default'
, initSignalRouting: _proxy('initSignalRouting')
, initUI: _proxy('initUI')
}
});
\ No newline at end of file
// Customized version of https://github.com/javiertoledo/bootstrap-rating-input
// based on an old old version
(function ($) {
$.fn.rating = function (cmd) {
var element;
var classActive = 'label label-warning rating-on'; //'glyphicon glyphicon-star';
var classInactive = 'label label-default rating-off'; //'glyphicon glyphicon-star-empty';
// A private function to highlight a star corresponding to a given value
function _paintValue(ratingInput, value) {
var selectedStar = $(ratingInput).find('[data-value=' + value + ']');
selectedStar.removeClass(classInactive).addClass(classActive);
selectedStar.prevAll('[data-value]').removeClass(classInactive).addClass(classActive);
selectedStar.nextAll('[data-value]').removeClass(classActive).addClass(classInactive);
}
// A private function to remove the selected rating
function _clearValue(ratingInput) {
var self = $(ratingInput);
self.find('[data-value]').removeClass(classActive).addClass(classInactive);
self.find('.rating-clear').hide();
var input = self.find('input');
input.val(input.data('empty-value')).trigger('change');
}
function buildWidgets(elems) {
// Iterate and transform all selected inputs
for (element = elems.length - 1; element >= 0; element--) {
var el, i,
originalInput = $(elems[element]),
max = originalInput.data('max') || 5,
min = originalInput.data('min') || 0,
clearable = originalInput.data('clearable') || null,
displayValue = originalInput.data('display') || null,
stars = '';
// HTML element construction
for (i = min; i <= max; i++) {
// Create <max> empty stars
stars += [
'<span class="', classInactive, '" data-value="', i, '">',
displayValue ? i : "",
'</span>'
].join('');
}
// Add a clear link if clearable option is set
if (clearable) {
stars += [
' <a class="rating-clear" style="display:none;" href="javascript:void">',
'<span class="glyphicon glyphicon-remove"></span> ',
clearable,
'</a>'].join('');
}
// Clone the original input to preserve any additional data bindings using attributes.
var newInput = originalInput.clone()
.attr('type', 'hidden')
.data('max', max)
.data('min', min);
// Rating widget is wrapped inside a div
el = [
'<div class="rating-input">',
stars,
'</div>'].join('');
// Replace original inputs HTML with the new one
originalInput.replaceWith($(el).append(newInput));
enableWidgets(newInput);
}
}
function enableWidgets(elemInput) {
elemInput.removeAttr("disabled");
// Give live to the newly generated widgets
elemInput.closest('.rating-input')
.removeClass("disabled")
// Highlight stars on hovering
.on('mouseenter', '[data-value]', function () {
var self = $(this);
_paintValue(self.closest('.rating-input'), self.data('value'));
})
// View current value while mouse is out
.on('mouseleave', '[data-value]', function () {
var self = $(this),
input = self.siblings('input'),
val = input.val(),
min = input.data('min'),
max = input.data('max');
if (val >= min && val <= max) {
_paintValue(self.closest('.rating-input'), val);
} else {
_clearValue(self.closest('.rating-input'));
}
})
// Set the selected value to the hidden field
.on('click', '[data-value]', function (e) {
var self = $(this);
var val = self.data('value');
self.siblings('input').val(val).trigger('change');
self.siblings('.rating-clear').show();
e.preventDefault();
return false;
})
// Remove value on clear
.on('click', '.rating-clear', function (e) {
_clearValue($(this).closest('.rating-input'));
e.preventDefault();
return false;
})
// Initialize view with default value
.each(function () {
var input = $(this).find('input'),
val = input.val(),
min = input.data('min'),
max = input.data('max');
if (val !== "" && +val >= min && +val <= max) {
_paintValue(this, val);
$(this).find('.rating-clear').show();
}
else {
_clearValue(this);
}
});
}
function disableWidgets(elemInput) {
elemInput.attr("disabled", "disabled");
var ratingInput = elemInput.closest('.rating-input');
ratingInput
.addClass("disabled")
.off('mouseenter', '[data-value]')
.off('mouseleave', '[data-value]')
.off('click', '[data-value]')
.off('click', '.rating-clear')
;
_clearValue(ratingInput);
}
function setValue(elemInput, val) {
var value = parseInt(val, 10);
if (!isNaN(value)) {
var ratingInput = elemInput.closest('.rating-input');
elemInput.val(val);
_paintValue(ratingInput, value);
}
}
if (cmd) {
if (cmd === 'disable') {
this.each(function(i, n){
disableWidgets($(n));
});
} else if (cmd === 'enable') {
this.each(function(i, n){
enableWidgets($(n));
});
} else if (cmd === 'setValue' && arguments.length > 1) {
var val = arguments[1];
this.each(function(i, n){
setValue($(n), val);
});
}
} else {
buildWidgets(this);
}
};
// Auto apply conversion of number fields with class 'rating' into rating-fields
$(function () {
if ($('input.rating[type=number]').length > 0) {
$('input.rating[type=number]').rating();
}
});
}(jQuery));
......@@ -23,7 +23,7 @@ define([
"underscore",
"signals",
// ----- app
// ----- The tools
"tools/select",
"tools/pan",
"tools/drawing",
......@@ -31,8 +31,13 @@ define([
"tools/ellipse",
"tools/rectangle",
"tools/text",
"tools/arrow"
// "tools/arrow1"
"tools/arrow",
// ----- CIMAF specific tools
"tools/fieldedges",
"tools/concordantstr",
"tools/discordantstr",
"tools/cmsarea"
],
function(_, Signal){
// ----- Locale variables -----------------------------
......
......@@ -123,12 +123,14 @@ define(["paper", "helper/utils"], function (paper, utils) {
tId: "a" + newArea._id,
type: TOOL_TYPE,
prop: o.prop,
setStrokeWidth: _noop
setStrokeWidth: _noop,
// Mouse Event
itemMouseDrag: itemMouseDrag
};
if (noEventListening !== true) {
newArea.onMouseDown = itemMouseDown;
newArea.onMouseDrag = itemMouseDrag;
//newArea.onMouseDrag = itemMouseDrag; // --> in data.itemMouseDrag
newArea.onMouseUp = itemMouseUp;
newArea.onKeyDown = itemKeyDown;
newArea.onKeyUp = itemKeyUp;
......@@ -176,8 +178,6 @@ define(["paper", "helper/utils"], function (paper, utils) {
function itemMouseUp(event) {
if (!event.tool) // catches only tool's events, item's mouseUpOutside is not catched...
return;
if (_editedArea) {
// TODO: add a flag to indicate if a modification really occured or if it's just a click
_events.annotationChanged.dispatch([_editedArea]);
......
......@@ -93,12 +93,15 @@ define(["paper", "helper/utils"], function (paper, utils) {
var _startPoint = null;
var _drawingLine = null;
var terminateOnRelease = false;
var _editedStria = null,
_editedSegmentIdx = null;
var _editedStria = null, // The edited stria (selected)
_editedSegmentIdx = null, // Index of the currently edited segment (click on stria's end)
_editedSegmentXLimit = null, // The X value at the cut position with thershold
_editedStriaLeftIdx = null, // Edited stria's left segment index
_editedStriaRightIdx = null; // Edited stria's right segment index
var _striaChanged = false;
var cutThreshold = 50; // Le dépassement minimum audela du couteau pour que la concordance soit valide
var _cutThreshold = 50; // Le dépassement minimum audela du couteau pour que la concordance soit valide
var defaultProperties = {
confidence: 3,
......@@ -122,7 +125,9 @@ define(["paper", "helper/utils"], function (paper, utils) {
newStria.data = {
tId: "a" + newStria._id,
type: TOOL_TYPE,
prop: o.prop
prop: o.prop,
// Mouse Event
itemMouseDrag: itemMouseDrag
};
// Select new stria
......@@ -133,7 +138,7 @@ define(["paper", "helper/utils"], function (paper, utils) {
// Add event listeners
newStria.onMouseDown = itemMouseDown;
newStria.onMouseDrag = itemMouseDrag;
//newStria.onMouseDrag = itemMouseDrag; // --> in data.itemMouseDrag
newStria.onMouseUp = itemMouseUp;
newStria.onKeyDown = itemKeyDown;
newStria.onKeyUp = itemKeyUp;
......@@ -155,9 +160,15 @@ define(["paper", "helper/utils"], function (paper, utils) {
hitItem.selected = true;
_events.annotationSelected.dispatch(hitItem);
_editedSegmentIdx = null;
_editedSegmentIdx = _editedStriaLeftIdx = _editedStriaRightIdx = _editedSegmentXLimit = null;
if (hitResult.type === 'segment') {
_editedSegmentIdx = hitResult.segment.index;
_editedSegmentXLimit = _cutPosition + (
(_editedStria.segments[1-_editedSegmentIdx].point.x < _cutPosition ? 1 : -1) * _cutThreshold
);
} else {
_editedStriaLeftIdx = (_editedStria.getFirstSegment().point.x < _cutPosition) ? 0 : 1;
_editedStriaRightIdx = 1 - _editedStriaLeftIdx;
}
_events.annotationSelected.dispatch(hitItem);
}
......@@ -172,7 +183,12 @@ define(["paper", "helper/utils"], function (paper, utils) {
editStria(event);
} else if (_editedStria) {
// TODO: valider la position par rapport au couteau
_editedStria.translate(event.delta);
if (_editedStria.segments[_editedStriaLeftIdx].point.x + event.delta.x <= _cutPosition - _cutThreshold &&
_editedStria.segments[_editedStriaRightIdx].point.x + event.delta.x >= _cutPosition + _cutThreshold) {
_editedStria.translate(event.delta);
}
}
}
......@@ -184,7 +200,7 @@ define(["paper", "helper/utils"], function (paper, utils) {
// TODO: add a flag to indicate if a modification really occured or if it's just a click
_events.annotationChanged.dispatch([_editedStria]);
}
_editedSegmentIdx = _editedStria = null;
_editedSegmentIdx = _editedStriaLeftIdx = _editedStriaRightIdx = _editedStria = _editedSegmentXLimit = null;
}
......@@ -234,8 +250,8 @@ define(["paper", "helper/utils"], function (paper, utils) {
function validatePosition(from, to){
// A concordant stria should cross the cut
return (from.x <= _cutPosition && to.x >= (_cutPosition + cutThreshold) ) ||
(from.x >= _cutPosition && to.x <= (_cutPosition - cutThreshold) );
return (from.x <= _cutPosition && to.x >= (_cutPosition + _cutThreshold) ) ||
(from.x >= _cutPosition && to.x <= (_cutPosition - _cutThreshold) );
}
function cancelCreate() {
......@@ -307,8 +323,9 @@ define(["paper", "helper/utils"], function (paper, utils) {
*/
function editStria(event) {
var dstPoint = event.point.clone();
if (!validatePosition(_editedStria.segments[1-_editedSegmentIdx].point, dstPo