Highlighting and jumping to lines in motions (closes #2347)
This commit is contained in:
parent
2d15bd54a1
commit
a06806c33b
@ -308,6 +308,11 @@ img {
|
|||||||
margin-left: 15px;
|
margin-left: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.col1 .details .line-number-setter > span {
|
||||||
|
margin-right: 5px;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
.col1 .details .line-number-setter .btn.disabled {
|
.col1 .details .line-number-setter .btn.disabled {
|
||||||
cursor: default;
|
cursor: default;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
@ -381,6 +386,10 @@ img {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*** Line numbers ***/
|
/*** Line numbers ***/
|
||||||
|
.motion-text .highlight {
|
||||||
|
background-color: #ff0;
|
||||||
|
}
|
||||||
|
|
||||||
.motion-text.line-numbers-outside {
|
.motion-text.line-numbers-outside {
|
||||||
padding-left: 35px;
|
padding-left: 35px;
|
||||||
position: relative;
|
position: relative;
|
||||||
@ -810,6 +819,11 @@ img {
|
|||||||
padding-right: 4px !important;
|
padding-right: 4px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn-slim {
|
||||||
|
padding-left: 6px;
|
||||||
|
padding-right: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
.spacer, .spacer-top {
|
.spacer, .spacer-top {
|
||||||
margin-top: 7px;
|
margin-top: 7px;
|
||||||
}
|
}
|
||||||
|
@ -381,6 +381,10 @@ tr.elected td {
|
|||||||
|
|
||||||
|
|
||||||
/*** Line numbers ***/
|
/*** Line numbers ***/
|
||||||
|
.motion-text .highlight {
|
||||||
|
background-color: #ff0;
|
||||||
|
}
|
||||||
|
|
||||||
.motion-text.line-numbers-outside {
|
.motion-text.line-numbers-outside {
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
margin-left: 25px;
|
margin-left: 25px;
|
||||||
@ -424,5 +428,5 @@ tr.elected td {
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
.motion-text.line-numbers-none .os-line-number {
|
.motion-text.line-numbers-none .os-line-number {
|
||||||
display: none;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
|
@ -144,7 +144,7 @@ angular.module('OpenSlidesApp.core.projector', ['OpenSlidesApp.core'])
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
// TODO: Use the current projector. At the moment there is only one
|
// TODO: Use the current projector. At the moment there is only one
|
||||||
$scope.scroll = -5 * Projector.get(1).scroll;
|
$scope.scroll = -80 * Projector.get(1).scroll;
|
||||||
$scope.scale = 100 + 20 * Projector.get(1).scale;
|
$scope.scale = 100 + 20 * Projector.get(1).scale;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
<div ng-controller="ProjectorCtrl">
|
<div ng-controller="ProjectorCtrl">
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
.scrollcontent {
|
.scrollcontent {
|
||||||
margin-top: {{scroll}}em !important;
|
margin-top: {{scroll}}px !important;
|
||||||
font-size: {{scale}}%;
|
font-size: {{scale}}%;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -192,7 +192,8 @@ class ProjectorViewSet(ReadOnlyModelViewSet):
|
|||||||
elif self.action == 'metadata':
|
elif self.action == 'metadata':
|
||||||
result = self.request.user.has_perm('core.can_see_projector')
|
result = self.request.user.has_perm('core.can_see_projector')
|
||||||
elif self.action in ('activate_elements', 'prune_elements', 'update_elements',
|
elif self.action in ('activate_elements', 'prune_elements', 'update_elements',
|
||||||
'deactivate_elements', 'clear_elements', 'control_view', 'set_resolution'):
|
'deactivate_elements', 'clear_elements', 'control_view',
|
||||||
|
'set_resolution', 'set_scroll'):
|
||||||
result = (self.request.user.has_perm('core.can_see_projector') and
|
result = (self.request.user.has_perm('core.can_see_projector') and
|
||||||
self.request.user.has_perm('core.can_manage_projector'))
|
self.request.user.has_perm('core.can_manage_projector'))
|
||||||
else:
|
else:
|
||||||
@ -428,6 +429,25 @@ class ProjectorViewSet(ReadOnlyModelViewSet):
|
|||||||
direction=request.data['direction'])
|
direction=request.data['direction'])
|
||||||
return Response({'detail': message})
|
return Response({'detail': message})
|
||||||
|
|
||||||
|
@detail_route(methods=['post'])
|
||||||
|
def set_scroll(self, request, pk):
|
||||||
|
"""
|
||||||
|
REST API operation to scroll the projector.
|
||||||
|
|
||||||
|
It expects a POST request to
|
||||||
|
/rest/core/projector/<pk>/set_scroll/ with a new value for scroll.
|
||||||
|
"""
|
||||||
|
if not isinstance(request.data, int):
|
||||||
|
raise ValidationError({'detail': 'Data must be an int.'})
|
||||||
|
|
||||||
|
projector_instance = self.get_object()
|
||||||
|
projector_instance.scroll = request.data
|
||||||
|
|
||||||
|
projector_instance.save()
|
||||||
|
message = 'Setting scroll to {scroll} was successful.'.format(
|
||||||
|
scroll=request.data)
|
||||||
|
return Response({'detail': message})
|
||||||
|
|
||||||
|
|
||||||
class CustomSlideViewSet(ModelViewSet):
|
class CustomSlideViewSet(ModelViewSet):
|
||||||
"""
|
"""
|
||||||
|
@ -162,11 +162,11 @@ angular.module('OpenSlidesApp.motions', [
|
|||||||
getText: function (versionId) {
|
getText: function (versionId) {
|
||||||
return this.getVersion(versionId).text;
|
return this.getVersion(versionId).text;
|
||||||
},
|
},
|
||||||
getTextWithLineBreaks: function (versionId) {
|
getTextWithLineBreaks: function (versionId, highlight, callback) {
|
||||||
var lineLength = Config.get('motions_line_length').value,
|
var lineLength = Config.get('motions_line_length').value,
|
||||||
html = this.getVersion(versionId).text;
|
html = this.getVersion(versionId).text;
|
||||||
|
|
||||||
return lineNumberingService.insertLineNumbers(html, lineLength);
|
return lineNumberingService.insertLineNumbers(html, lineLength, highlight, callback);
|
||||||
},
|
},
|
||||||
setTextStrippingLineBreaks: function (versionId, text) {
|
setTextStrippingLineBreaks: function (versionId, text) {
|
||||||
this.text = lineNumberingService.stripLineNumbers(text);
|
this.text = lineNumberingService.stripLineNumbers(text);
|
||||||
|
@ -63,6 +63,7 @@ angular.module('OpenSlidesApp.motions.lineNumbering', [])
|
|||||||
this._currentLineNumber++;
|
this._currentLineNumber++;
|
||||||
node.setAttribute('class', 'os-line-number line-number-' + lineNumber);
|
node.setAttribute('class', 'os-line-number line-number-' + lineNumber);
|
||||||
node.setAttribute('data-line-number', lineNumber + '');
|
node.setAttribute('data-line-number', lineNumber + '');
|
||||||
|
node.setAttribute('name', 'L' + lineNumber);
|
||||||
node.setAttribute('contenteditable', 'false');
|
node.setAttribute('contenteditable', 'false');
|
||||||
node.innerHTML = ' '; // Prevent tinymce from stripping out empty span's
|
node.innerHTML = ' '; // Prevent tinymce from stripping out empty span's
|
||||||
return node;
|
return node;
|
||||||
@ -78,23 +79,36 @@ angular.module('OpenSlidesApp.motions.lineNumbering', [])
|
|||||||
* @returns Array
|
* @returns Array
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
this._textNodeToLines = function (node, length) {
|
this._textNodeToLines = function (node, length, highlight) {
|
||||||
var out = [],
|
var out = [],
|
||||||
currLineStart = 0,
|
currLineStart = 0,
|
||||||
i = 0,
|
i = 0,
|
||||||
firstTextNode = true,
|
firstTextNode = true,
|
||||||
lastBreakableIndex = null,
|
lastBreakableIndex = null,
|
||||||
service = this;
|
service = this;
|
||||||
|
var addLine = function (text, highlight) {
|
||||||
var addLine = function (text) {
|
var node;
|
||||||
var newNode = document.createTextNode(text);
|
|
||||||
if (firstTextNode) {
|
if (firstTextNode) {
|
||||||
|
if (highlight == service._currentLineNumber - 1) {
|
||||||
|
node = document.createElement('span');
|
||||||
|
node.setAttribute('class', 'highlight');
|
||||||
|
node.innerHTML = text;
|
||||||
|
} else {
|
||||||
|
node = document.createTextNode(text);
|
||||||
|
}
|
||||||
firstTextNode = false;
|
firstTextNode = false;
|
||||||
} else {
|
} else {
|
||||||
|
if (service._currentLineNumber == highlight) {
|
||||||
|
node = document.createElement('span');
|
||||||
|
node.setAttribute('class', 'highlight');
|
||||||
|
node.innerHTML = text;
|
||||||
|
} else {
|
||||||
|
node = document.createTextNode(text);
|
||||||
|
}
|
||||||
out.push(service._createLineBreak());
|
out.push(service._createLineBreak());
|
||||||
out.push(service._createLineNumber());
|
out.push(service._createLineNumber());
|
||||||
}
|
}
|
||||||
out.push(newNode);
|
out.push(node);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (node.nodeValue == "\n") {
|
if (node.nodeValue == "\n") {
|
||||||
@ -122,7 +136,7 @@ angular.module('OpenSlidesApp.motions.lineNumbering', [])
|
|||||||
}
|
}
|
||||||
if (lineBreakAt !== null && node.nodeValue[i] != ' ') {
|
if (lineBreakAt !== null && node.nodeValue[i] != ' ') {
|
||||||
var currLine = node.nodeValue.substring(currLineStart, lineBreakAt + 1);
|
var currLine = node.nodeValue.substring(currLineStart, lineBreakAt + 1);
|
||||||
addLine(currLine);
|
addLine(currLine, highlight);
|
||||||
|
|
||||||
currLineStart = lineBreakAt + 1;
|
currLineStart = lineBreakAt + 1;
|
||||||
this._currentInlineOffset = i - lineBreakAt - 1;
|
this._currentInlineOffset = i - lineBreakAt - 1;
|
||||||
@ -137,7 +151,7 @@ angular.module('OpenSlidesApp.motions.lineNumbering', [])
|
|||||||
i++;
|
i++;
|
||||||
|
|
||||||
}
|
}
|
||||||
addLine(node.nodeValue.substring(currLineStart));
|
addLine(node.nodeValue.substring(currLineStart), highlight);
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
};
|
};
|
||||||
@ -177,7 +191,7 @@ angular.module('OpenSlidesApp.motions.lineNumbering', [])
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this._insertLineNumbersToInlineNode = function (node, length) {
|
this._insertLineNumbersToInlineNode = function (node, length, highlight) {
|
||||||
var oldChildren = [], i;
|
var oldChildren = [], i;
|
||||||
for (i = 0; i < node.childNodes.length; i++) {
|
for (i = 0; i < node.childNodes.length; i++) {
|
||||||
oldChildren.push(node.childNodes[i]);
|
oldChildren.push(node.childNodes[i]);
|
||||||
@ -189,7 +203,7 @@ angular.module('OpenSlidesApp.motions.lineNumbering', [])
|
|||||||
|
|
||||||
for (i = 0; i < oldChildren.length; i++) {
|
for (i = 0; i < oldChildren.length; i++) {
|
||||||
if (oldChildren[i].nodeType == TEXT_NODE) {
|
if (oldChildren[i].nodeType == TEXT_NODE) {
|
||||||
var ret = this._textNodeToLines(oldChildren[i], length);
|
var ret = this._textNodeToLines(oldChildren[i], length, highlight);
|
||||||
for (var j = 0; j < ret.length; j++) {
|
for (var j = 0; j < ret.length; j++) {
|
||||||
node.appendChild(ret[j]);
|
node.appendChild(ret[j]);
|
||||||
}
|
}
|
||||||
@ -201,8 +215,7 @@ angular.module('OpenSlidesApp.motions.lineNumbering', [])
|
|||||||
node.appendChild(this._createLineBreak());
|
node.appendChild(this._createLineBreak());
|
||||||
node.appendChild(this._createLineNumber());
|
node.appendChild(this._createLineNumber());
|
||||||
}
|
}
|
||||||
|
var changedNode = this._insertLineNumbersToNode(oldChildren[i], length, highlight);
|
||||||
var changedNode = this._insertLineNumbersToNode(oldChildren[i], length);
|
|
||||||
this._moveLeadingLineBreaksToOuterNode(changedNode, node);
|
this._moveLeadingLineBreaksToOuterNode(changedNode, node);
|
||||||
node.appendChild(changedNode);
|
node.appendChild(changedNode);
|
||||||
} else {
|
} else {
|
||||||
@ -253,7 +266,7 @@ angular.module('OpenSlidesApp.motions.lineNumbering', [])
|
|||||||
return Math.ceil(newLength);
|
return Math.ceil(newLength);
|
||||||
};
|
};
|
||||||
|
|
||||||
this._insertLineNumbersToBlockNode = function (node, length) {
|
this._insertLineNumbersToBlockNode = function (node, length, highlight) {
|
||||||
this._currentInlineOffset = 0;
|
this._currentInlineOffset = 0;
|
||||||
this._prependLineNumberToFirstText = true;
|
this._prependLineNumberToFirstText = true;
|
||||||
|
|
||||||
@ -268,7 +281,7 @@ angular.module('OpenSlidesApp.motions.lineNumbering', [])
|
|||||||
|
|
||||||
for (i = 0; i < oldChildren.length; i++) {
|
for (i = 0; i < oldChildren.length; i++) {
|
||||||
if (oldChildren[i].nodeType == TEXT_NODE) {
|
if (oldChildren[i].nodeType == TEXT_NODE) {
|
||||||
var ret = this._textNodeToLines(oldChildren[i], length);
|
var ret = this._textNodeToLines(oldChildren[i], length, highlight);
|
||||||
for (var j = 0; j < ret.length; j++) {
|
for (var j = 0; j < ret.length; j++) {
|
||||||
node.appendChild(ret[j]);
|
node.appendChild(ret[j]);
|
||||||
}
|
}
|
||||||
@ -280,8 +293,7 @@ angular.module('OpenSlidesApp.motions.lineNumbering', [])
|
|||||||
node.appendChild(this._createLineBreak());
|
node.appendChild(this._createLineBreak());
|
||||||
node.appendChild(this._createLineNumber());
|
node.appendChild(this._createLineNumber());
|
||||||
}
|
}
|
||||||
|
var changedNode = this._insertLineNumbersToNode(oldChildren[i], length, highlight);
|
||||||
var changedNode = this._insertLineNumbersToNode(oldChildren[i], length);
|
|
||||||
this._moveLeadingLineBreaksToOuterNode(changedNode, node);
|
this._moveLeadingLineBreaksToOuterNode(changedNode, node);
|
||||||
node.appendChild(changedNode);
|
node.appendChild(changedNode);
|
||||||
} else {
|
} else {
|
||||||
@ -295,15 +307,15 @@ angular.module('OpenSlidesApp.motions.lineNumbering', [])
|
|||||||
return node;
|
return node;
|
||||||
};
|
};
|
||||||
|
|
||||||
this._insertLineNumbersToNode = function (node, length) {
|
this._insertLineNumbersToNode = function (node, length, highlight) {
|
||||||
if (node.nodeType !== ELEMENT_NODE) {
|
if (node.nodeType !== ELEMENT_NODE) {
|
||||||
throw 'This method may only be called for ELEMENT-nodes: ' + node.nodeValue;
|
throw 'This method may only be called for ELEMENT-nodes: ' + node.nodeValue;
|
||||||
}
|
}
|
||||||
if (this._isInlineElement(node)) {
|
if (this._isInlineElement(node)) {
|
||||||
return this._insertLineNumbersToInlineNode(node, length);
|
return this._insertLineNumbersToInlineNode(node, length, highlight);
|
||||||
} else {
|
} else {
|
||||||
var newLength = this._calcBlockNodeLength(node, length);
|
var newLength = this._calcBlockNodeLength(node, length);
|
||||||
return this._insertLineNumbersToBlockNode(node, newLength);
|
return this._insertLineNumbersToBlockNode(node, newLength, highlight);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -329,7 +341,7 @@ angular.module('OpenSlidesApp.motions.lineNumbering', [])
|
|||||||
return root.innerHTML;
|
return root.innerHTML;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.insertLineNumbersNode = function (html, lineLength) {
|
this.insertLineNumbersNode = function (html, lineLength, highlight) {
|
||||||
var root = document.createElement('div');
|
var root = document.createElement('div');
|
||||||
root.innerHTML = html;
|
root.innerHTML = html;
|
||||||
|
|
||||||
@ -337,11 +349,15 @@ angular.module('OpenSlidesApp.motions.lineNumbering', [])
|
|||||||
this._currentLineNumber = 1;
|
this._currentLineNumber = 1;
|
||||||
this._prependLineNumberToFirstText = true;
|
this._prependLineNumberToFirstText = true;
|
||||||
|
|
||||||
return this._insertLineNumbersToNode(root, lineLength);
|
return this._insertLineNumbersToNode(root, lineLength, highlight);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.insertLineNumbers = function (html, lineLength) {
|
this.insertLineNumbers = function (html, lineLength, highlight, callback) {
|
||||||
var newRoot = this.insertLineNumbersNode(html, lineLength);
|
var newRoot = this.insertLineNumbersNode(html, lineLength, highlight);
|
||||||
|
|
||||||
|
if (callback) {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
|
||||||
return newRoot.innerHTML;
|
return newRoot.innerHTML;
|
||||||
};
|
};
|
||||||
|
@ -15,14 +15,69 @@ angular.module('OpenSlidesApp.motions.projector', ['OpenSlidesApp.motions'])
|
|||||||
|
|
||||||
.controller('SlideMotionCtrl', [
|
.controller('SlideMotionCtrl', [
|
||||||
'$scope',
|
'$scope',
|
||||||
|
'$rootScope',
|
||||||
|
'$http',
|
||||||
'Motion',
|
'Motion',
|
||||||
'User',
|
'User',
|
||||||
'Config',
|
'Config',
|
||||||
function($scope, Motion, User, Config) {
|
'Projector',
|
||||||
|
function($scope, $rootScope, $http, Motion, User, Config, Projector) {
|
||||||
// Attention! Each object that is used here has to be dealt on server side.
|
// Attention! Each object that is used here has to be dealt on server side.
|
||||||
// Add it to the coresponding get_requirements method of the ProjectorElement
|
// Add it to the coresponding get_requirements method of the ProjectorElement
|
||||||
// class.
|
// class.
|
||||||
var id = $scope.element.id;
|
var id = $scope.element.id;
|
||||||
|
|
||||||
|
$scope.line = $scope.element.highlightAndScroll;
|
||||||
|
|
||||||
|
// get cookie using jQuery
|
||||||
|
var getCookie = function (name) {
|
||||||
|
var cookieValue = null;
|
||||||
|
if (document.cookie && document.cookie !== '') {
|
||||||
|
var cookies = document.cookie.split(';');
|
||||||
|
for (var i = 0; i < cookies.length; i++) {
|
||||||
|
var cookie = jQuery.trim(cookies[i]);
|
||||||
|
// Does this cookie string begin with the name we want?
|
||||||
|
if (cookie.substring(0, name.length + 1) == (name + '=')) {
|
||||||
|
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cookieValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
var scrollRequest = function (position) {
|
||||||
|
// request with csrf token
|
||||||
|
// TODO: Why is the X-CSRFToken not included in the header by default?
|
||||||
|
var csrfToken = getCookie('csrftoken');
|
||||||
|
var request = {
|
||||||
|
method: 'POST',
|
||||||
|
url: '/rest/core/projector/1/set_scroll/',
|
||||||
|
data: position,
|
||||||
|
headers: {
|
||||||
|
'X-CSRFToken': csrfToken
|
||||||
|
}
|
||||||
|
};
|
||||||
|
$http(request);
|
||||||
|
};
|
||||||
|
$scope.scroll = function () {
|
||||||
|
// Prevent getting in an infinite loop by updating only if the value has changed.
|
||||||
|
// (if this check is removed this happends: controller loads --> call of $scope.scroll
|
||||||
|
// --> same line but scrollRequest --> projector updates --> controller loads --> ... )
|
||||||
|
if ($scope.line !== $rootScope.motion_projector_line) {
|
||||||
|
// line value has changed
|
||||||
|
var lineElement = document.getElementsByName('L' + $scope.line);
|
||||||
|
if (lineElement[0]) {
|
||||||
|
$rootScope.motion_projector_line = $scope.line;
|
||||||
|
var pos = lineElement[0].getBoundingClientRect().top + Projector.get(1).scroll*80;
|
||||||
|
scrollRequest(Math.floor(pos/80.0) - 1);
|
||||||
|
} else if ($scope.line === 0) {
|
||||||
|
$rootScope.motion_projector_line = $scope.line;
|
||||||
|
scrollRequest(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
Motion.bindOne(id, $scope, 'motion');
|
Motion.bindOne(id, $scope, 'motion');
|
||||||
User.bindAll({}, $scope, 'users');
|
User.bindAll({}, $scope, 'users');
|
||||||
}
|
}
|
||||||
|
@ -1069,9 +1069,10 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
|
|||||||
'PdfMakeDocumentProvider',
|
'PdfMakeDocumentProvider',
|
||||||
'MotionInlineEditing',
|
'MotionInlineEditing',
|
||||||
'gettextCatalog',
|
'gettextCatalog',
|
||||||
|
'Projector',
|
||||||
function($scope, $http, ngDialog, MotionComment, MotionForm, Motion, Category, Mediafile, Tag,
|
function($scope, $http, ngDialog, MotionComment, MotionForm, Motion, Category, Mediafile, Tag,
|
||||||
User, Workflow, Config, motion, SingleMotionContentProvider, MotionContentProvider,
|
User, Workflow, Config, motion, SingleMotionContentProvider, MotionContentProvider,
|
||||||
PollContentProvider, PdfMakeConverter, PdfMakeDocumentProvider, MotionInlineEditing, gettextCatalog) {
|
PollContentProvider, PdfMakeConverter, PdfMakeDocumentProvider, MotionInlineEditing, gettextCatalog, Projector) {
|
||||||
Motion.bindOne(motion.id, $scope, 'motion');
|
Motion.bindOne(motion.id, $scope, 'motion');
|
||||||
Category.bindAll({}, $scope, 'categories');
|
Category.bindAll({}, $scope, 'categories');
|
||||||
Mediafile.bindAll({}, $scope, 'mediafiles');
|
Mediafile.bindAll({}, $scope, 'mediafiles');
|
||||||
@ -1088,6 +1089,38 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
|
|||||||
}
|
}
|
||||||
$scope.amendments = Motion.filter({parent_id: motion.id});
|
$scope.amendments = Motion.filter({parent_id: motion.id});
|
||||||
|
|
||||||
|
$scope.highlight = 0;
|
||||||
|
$scope.linesForProjector = false;
|
||||||
|
// Set 0 for disable highlighting on projector
|
||||||
|
var setHighlightOnProjector = function (line) {
|
||||||
|
var elements = _.map(Projector.get(1).elements, function(element) { return element; });
|
||||||
|
elements.forEach(function (element) {
|
||||||
|
if (element.name == 'motions/motion') {
|
||||||
|
var data = {};
|
||||||
|
data[element.uuid] = {
|
||||||
|
highlightAndScroll: line,
|
||||||
|
};
|
||||||
|
$http.post('/rest/core/projector/1/update_elements/', data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
$scope.scrollToAndHighlight = function (line) {
|
||||||
|
$scope.highlight = line;
|
||||||
|
var lineElement = document.getElementsByName('L' + line);
|
||||||
|
if (lineElement[0]) {
|
||||||
|
// Scroll local
|
||||||
|
$('html, body').animate({
|
||||||
|
scrollTop: lineElement[0].getBoundingClientRect().top
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
// set highlight and scroll on Projector
|
||||||
|
setHighlightOnProjector($scope.linesForProjector ? line : 0);
|
||||||
|
};
|
||||||
|
$scope.toggleLinesForProjector = function () {
|
||||||
|
$scope.linesForProjector = !$scope.linesForProjector;
|
||||||
|
setHighlightOnProjector($scope.linesForProjector ? $scope.highlight : 0);
|
||||||
|
};
|
||||||
|
|
||||||
$scope.makePDF = function() {
|
$scope.makePDF = function() {
|
||||||
var id = motion.identifier,
|
var id = motion.identifier,
|
||||||
slice = Function.prototype.call.bind([].slice),
|
slice = Function.prototype.call.bind([].slice),
|
||||||
|
@ -287,7 +287,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="line-number-setter {{ lineNumberMode }}">
|
<div class="line-number-setter {{ lineNumberMode }}">
|
||||||
<div class="btn-group" data-toggle="buttons">
|
<span class="btn-group" data-toggle="buttons">
|
||||||
<div class="btn btn-default disabled" title="{{ 'Line Numbering' | translate }}">
|
<div class="btn btn-default disabled" title="{{ 'Line Numbering' | translate }}">
|
||||||
<i class="fa fa-list-ol" aria-hidden="true"></i>
|
<i class="fa fa-list-ol" aria-hidden="true"></i>
|
||||||
</div>
|
</div>
|
||||||
@ -309,9 +309,29 @@
|
|||||||
ng-checked="lineNumberMode == 'outside'">
|
ng-checked="lineNumberMode == 'outside'">
|
||||||
<translate>Outside</translate>
|
<translate>Outside</translate>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</span>
|
||||||
|
<span>
|
||||||
|
<form class="input-group" style="max-width: 220px;" ng-if="lineNumberMode != 'none'" ng-submit="scrollToAndHighlight(gotoLinenumber)">
|
||||||
|
<input type="number" class="form-control" ng-model="gotoLinenumber" placeholder="{{ 'Line' | translate }}"></input>
|
||||||
|
<span class="input-group-btn">
|
||||||
|
<button type="button" class="btn btn-default btn-slim" ng-show="gotoLinenumber"
|
||||||
|
ng-click="gotoLinenumber = ''; scrollToAndHighlight(0);">
|
||||||
|
<i class="fa fa-times text-danger"></i>
|
||||||
|
</button>
|
||||||
|
<button type="submit" class="btn btn-default">
|
||||||
|
<i class="fa fa-share"></i>
|
||||||
|
<translate>go</translate>
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-default" os-perms="core.can_manage_projector"
|
||||||
|
ng-show="lineNumberMode != 'none' && motion.isProjected()" ng-click="toggleLinesForProjector()"
|
||||||
|
uib-tooltip="{{ 'Show highlighted line also on projector.' | translate }}">
|
||||||
|
<i class="fa" ng-class="linesForProjector ? 'fa-check-square-o' : 'fa-square-o'"></i>
|
||||||
|
<i class="fa fa-video-camera"></i>
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
</form>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div ng-class="{'col-sm-8': (lineNumberMode != 'outside'), 'col-sm-12': (lineNumberMode == 'outside')}">
|
<div ng-class="{'col-sm-8': (lineNumberMode != 'outside'), 'col-sm-12': (lineNumberMode == 'outside')}">
|
||||||
|
|
||||||
<div ng-if="motion.isAllowed('update') && version == motion.getVersion(-1).id">
|
<div ng-if="motion.isAllowed('update') && version == motion.getVersion(-1).id">
|
||||||
@ -319,7 +339,7 @@
|
|||||||
<div ui-tinymce="inlineEditing.tinymceOptions" ng-model="inlineEditing.lineBrokenText"
|
<div ui-tinymce="inlineEditing.tinymceOptions" ng-model="inlineEditing.lineBrokenText"
|
||||||
class="motion-text line-numbers-{{ lineNumberMode }}"></div>
|
class="motion-text line-numbers-{{ lineNumberMode }}"></div>
|
||||||
</div>
|
</div>
|
||||||
<div ng-show="!inlineEditing.active" ng-bind-html="motion.getTextWithLineBreaks(version) | trusted"
|
<div ng-show="!inlineEditing.active" ng-bind-html="motion.getTextWithLineBreaks(version, highlight) | trusted"
|
||||||
class="motion-text line-numbers-{{ lineNumberMode }}"></div>
|
class="motion-text line-numbers-{{ lineNumberMode }}"></div>
|
||||||
|
|
||||||
<div class="motion-save-toolbar" ng-class="{ 'visible': (inlineEditing.changed && inlineEditing.active) }">
|
<div class="motion-save-toolbar" ng-class="{ 'visible': (inlineEditing.changed && inlineEditing.active) }">
|
||||||
@ -332,7 +352,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div ng-if="!(motion.isAllowed('update') && version == motion.getVersion(-1).id)">
|
<div ng-if="!(motion.isAllowed('update') && version == motion.getVersion(-1).id)">
|
||||||
<div ng-bind-html="motion.getTextWithLineBreaks(version) | trusted"
|
<div ng-bind-html="motion.getTextWithLineBreaks(version, highlight) | trusted"
|
||||||
class="motion-text line-numbers-{{ lineNumberMode }}"></div>
|
class="motion-text line-numbers-{{ lineNumberMode }}"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Text -->
|
<!-- Text -->
|
||||||
<div ng-bind-html="motion.getTextWithLineBreaks() | trusted"
|
<div ng-bind-html="motion.getTextWithLineBreaks(null, line, scroll) | trusted"
|
||||||
class="motion-text line-numbers-{{ config('motions_default_line_numbering') }}"></div>
|
class="motion-text line-numbers-{{ config('motions_default_line_numbering') }}"></div>
|
||||||
|
|
||||||
<!-- Reason -->
|
<!-- Reason -->
|
||||||
|
Loading…
Reference in New Issue
Block a user