Merge pull request #923 from DebVortex/feature/pdf.js
Included PDF.js and possiblity to show pdfs
This commit is contained in:
commit
9dfd154e85
@ -250,6 +250,8 @@ OpenSlides uses the following projects or parts of them:
|
||||
|
||||
* `Django haystack <http://haystacksearch.org>`_, License: BSD
|
||||
|
||||
* `pdf.js <http://mozilla.github.io/pdf.js/>`_, License: Apache License v2.0
|
||||
|
||||
* `Pillow <https://github.com/python-imaging/Pillow/>`_, License: Standard
|
||||
PIL License
|
||||
|
||||
|
@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import slides # noqa
|
@ -12,16 +12,20 @@
|
||||
|
||||
import mimetypes
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.db import models
|
||||
from django.utils.translation import ugettext_lazy, ugettext_noop
|
||||
|
||||
from openslides.projector.models import SlideMixin
|
||||
from openslides.utils.person.models import PersonField
|
||||
|
||||
|
||||
class Mediafile(models.Model):
|
||||
class Mediafile(SlideMixin, models.Model):
|
||||
"""
|
||||
Class for uploaded files which can be delivered under a certain url.
|
||||
"""
|
||||
slide_callback_name = 'mediafile'
|
||||
PRESENTABLE_FILE_TYPES = ['application/pdf']
|
||||
|
||||
mediafile = models.FileField(upload_to='file', verbose_name=ugettext_lazy("File"))
|
||||
"""
|
||||
@ -41,6 +45,12 @@ class Mediafile(models.Model):
|
||||
filetype = models.CharField(max_length=255, editable=False)
|
||||
"""A string used to show the type of the file."""
|
||||
|
||||
is_presentable = models.BooleanField(
|
||||
default=False,
|
||||
verbose_name=ugettext_lazy("Is Presentable"),
|
||||
help_text=ugettext_lazy("If checked, this file can be presented on the projector. "
|
||||
"Currently, this is only possible for PDFs."))
|
||||
|
||||
class Meta:
|
||||
"""
|
||||
Meta class for the mediafile model.
|
||||
@ -67,15 +77,16 @@ class Mediafile(models.Model):
|
||||
self.filetype = ugettext_noop('unknown')
|
||||
return super(Mediafile, self).save(*args, **kwargs)
|
||||
|
||||
@models.permalink
|
||||
def get_absolute_url(self, link='update'):
|
||||
"""
|
||||
Returns the URL to a mediafile. The link can be 'update' or 'delete'.
|
||||
Returns the URL to a mediafile. The link can be 'projector',
|
||||
'update' or 'delete'.
|
||||
"""
|
||||
if link == 'update' or link == 'edit': # 'edit' ist only used until utils/views.py is fixed
|
||||
return ('mediafile_update', [str(self.id)])
|
||||
return reverse('mediafile_update', kwargs={'pk': str(self.id)})
|
||||
if link == 'delete':
|
||||
return ('mediafile_delete', [str(self.id)])
|
||||
return reverse('mediafile_delete', kwargs={'pk': str(self.id)})
|
||||
return super(Mediafile, self).get_absolute_url(link)
|
||||
|
||||
def get_filesize(self):
|
||||
"""
|
||||
|
34
openslides/mediafile/slides.py
Normal file
34
openslides/mediafile/slides.py
Normal file
@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from django.template.loader import render_to_string
|
||||
|
||||
from openslides.config.api import config
|
||||
from openslides.projector.api import register_slide
|
||||
|
||||
from .models import Mediafile
|
||||
|
||||
|
||||
def mediafile_presentation_as_slide(**kwargs):
|
||||
"""
|
||||
Return the html code for a presentation of a Mediafile.
|
||||
|
||||
At the moment, only the presentation of pdfs is supported.
|
||||
"""
|
||||
file_pk = kwargs.get('pk', None)
|
||||
page_num = kwargs.get('page_num', 1)
|
||||
|
||||
try:
|
||||
pdf = Mediafile.objects.get(
|
||||
pk=file_pk,
|
||||
filetype__in=Mediafile.PRESENTABLE_FILE_TYPES,
|
||||
is_presentable=True)
|
||||
except Mediafile.DoesNotExist:
|
||||
# TODO what doing, if a wrong pk is given?
|
||||
pdf = None
|
||||
context = {'pdf': pdf, 'page_num': page_num,
|
||||
'fullscreen': config['pdf_fullscreen']}
|
||||
return render_to_string('mediafile/presentation_slide.html', context)
|
||||
|
||||
|
||||
register_slide('mediafile', mediafile_presentation_as_slide)
|
7070
openslides/mediafile/static/javascript/pdf.js
Normal file
7070
openslides/mediafile/static/javascript/pdf.js
Normal file
File diff suppressed because it is too large
Load Diff
39219
openslides/mediafile/static/javascript/pdf.worker.js
vendored
Normal file
39219
openslides/mediafile/static/javascript/pdf.worker.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
95
openslides/mediafile/static/javascript/pdf_presenter.js
Normal file
95
openslides/mediafile/static/javascript/pdf_presenter.js
Normal file
@ -0,0 +1,95 @@
|
||||
var pdf = PDFJS.getDocument(projector['pdf_url']);
|
||||
|
||||
projector['load_pdf_page'] = function(page) {
|
||||
projector['pdf_page_num'] = page;
|
||||
pdf.then(function(pdf) {
|
||||
pdf.getPage(page).then(set_convas_size);
|
||||
});
|
||||
};
|
||||
|
||||
projector['load_pdf'] = function(data) {
|
||||
projector['pdf_url'] = data['url'];
|
||||
projector['pdf_page_num'] = data['page_num'];
|
||||
pdf = PDFJS.getDocument(projector['pdf_url']);
|
||||
projector['load_pdf_page'](projector['pdf_page_num']);
|
||||
};
|
||||
|
||||
projector['toggle_fullscreen'] = function(fullscreen) {
|
||||
projector['pdf_fullscreen'] = fullscreen;
|
||||
content = $('#content');
|
||||
presentation = $('#presentation');
|
||||
footer = $('#footer');
|
||||
body = $('body');
|
||||
if (fullscreen) {
|
||||
content.addClass('fullscreen');
|
||||
presentation.addClass('fullscreen');
|
||||
footer.addClass('black');
|
||||
body.addClass('black');
|
||||
} else {
|
||||
content.removeClass('fullscreen');
|
||||
presentation.removeClass('fullscreen');
|
||||
footer.removeClass('black');
|
||||
body.removeClass('black');
|
||||
}
|
||||
$(window).resize();
|
||||
};
|
||||
|
||||
function scale_to_height(page) {
|
||||
return page.getViewport(window.innerHeight / page.getViewport(1.0).height);
|
||||
}
|
||||
|
||||
function scale_to_width(page) {
|
||||
return page.getViewport(window.innerWidth / page.getViewport(1.0).width);
|
||||
}
|
||||
|
||||
function get_correct_viewport(page, canvas) {
|
||||
if(window.innerWidth > window.innerHeight) {
|
||||
viewport = scale_to_height(page);
|
||||
if (viewport.width > window.innerWidth) {
|
||||
viewport = scale_to_width(page);
|
||||
canvas.height = viewport.height;
|
||||
canvas.width = window.innerWidth;
|
||||
} else {
|
||||
canvas.height = window.innerHeight;
|
||||
canvas.width = viewport.width;
|
||||
}
|
||||
} else {
|
||||
viewport = scale_to_width(page);
|
||||
if (viewport.height > window.innerHeight) {
|
||||
viewport = scale_to_height(page);
|
||||
canvas.height = window.innerHeight;
|
||||
canvas.width = viewport.width;
|
||||
} else {
|
||||
canvas.height = viewport.height;
|
||||
canvas.width = window.innerWidth;
|
||||
}
|
||||
}
|
||||
return viewport;
|
||||
}
|
||||
|
||||
function set_convas_size(page) {
|
||||
var canvas = document.getElementById('presentation');
|
||||
var context = canvas.getContext('2d');
|
||||
if (projector['pdf_fullscreen']) {
|
||||
viewport = get_correct_viewport(page, canvas);
|
||||
} else {
|
||||
viewport = page.getViewport(window.innerWidth / page.getViewport(1.0).width);
|
||||
canvas.height = viewport.height;
|
||||
canvas.width = window.innerWidth;
|
||||
}
|
||||
page.render({canvasContext: context, viewport: viewport});
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
$(window).resize(function() {
|
||||
projector['load_pdf_page'](projector['pdf_page_num']);
|
||||
});
|
||||
if (projector['pdf_fullscreen']) {
|
||||
if (!$('#content').hasClass('fullscreen')) {
|
||||
$('#content').addClass('fullscreen');
|
||||
$('#footer').addClass('black');
|
||||
$('body').addClass('black');
|
||||
}
|
||||
}
|
||||
$(window).resize();
|
||||
});
|
@ -36,6 +36,12 @@
|
||||
<span style="width: 1px; white-space: nowrap;">
|
||||
<a href="{{ mediafile|absolute_url:'update' }}" rel="tooltip" data-original-title="{% trans 'Edit' %}" class="btn btn-mini"><i class="icon-pencil"></i></a>
|
||||
<a href="{{ mediafile|absolute_url:'delete' }}" rel="tooltip" data-original-title="{% trans 'Delete' %}" class="btn btn-mini"><i class="icon-remove"></i></a>
|
||||
{% if mediafile.is_presentable %}{% if mediafile.filetype in mediafile.PRESENTABLE_FILE_TYPES %}
|
||||
<a href="{{ mediafile|absolute_url:'projector' }}" class="activate_link choose-pdf btn {% if mediafile.is_active_slide %}btn-primary{% endif %} btn-mini" title="{% trans 'Show' %}">
|
||||
<i class="icon-facetime-video {% if mediafile.is_active_slide %}icon-white{% endif %}">
|
||||
</i>
|
||||
</a>
|
||||
{% endif %}{% endif %}
|
||||
</span>
|
||||
</td>
|
||||
{% endif %}
|
||||
|
45
openslides/mediafile/templates/mediafile/pdfs_widget.html
Normal file
45
openslides/mediafile/templates/mediafile/pdfs_widget.html
Normal file
@ -0,0 +1,45 @@
|
||||
{% load i18n %}
|
||||
{% load tags %}
|
||||
|
||||
<form action="{% url 'target_pdf_page' %}" method="GET" class="set-page-form">
|
||||
<div class="input-prepend" style="margin-bottom:0;">
|
||||
<a class="btn go-first-page">
|
||||
<i class="icon-fast-backward"></i>
|
||||
</a>
|
||||
<a class="btn pdf-page-ctl" href="{% url 'prev_pdf_page' %}">
|
||||
<i class="icon-backward"></i>
|
||||
</a>
|
||||
<a class="btn pdf-page-ctl" href="{% url 'next_pdf_page' %}">
|
||||
<i class="icon-forward"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="input-append" style="margin-bottom:0;">
|
||||
<a class="btn pdf-toggle-fullscreen {%if pdf_fullscreen %}btn-primary{% endif %}" href="{% url 'toggle_fullscreen' %}">
|
||||
<i class="icon-fullscreen {%if pdf_fullscreen %}icon-white{% endif %}"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="input-append input-prepend" style="margin-bottom:0;">
|
||||
<span class="add-on">Page:</span>
|
||||
<input id="page_num" name="page_num" type="number" style="width: 22px;" value="{{ current_page }}">
|
||||
|
||||
<button type="submit" id="go_to_page" class="btn" style="width: 40px;">
|
||||
<i class="icon-refresh"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
<ul style="line-height: 180%">
|
||||
{% for pdf in pdfs %}
|
||||
<li class="{% if pdf.is_active_slide %}activeline{% endif %}">
|
||||
<a href="{{ pdf|absolute_url:'projector' }}" class="activate_link choose-pdf btn {% if pdf.is_active_slide %}btn-primary{% endif %} btn-mini" title="{% trans 'Show' %}">
|
||||
<i class="icon-facetime-video {% if pdf.is_active_slide %}icon-white{% endif %}"></i>
|
||||
</a> {{ pdf }}
|
||||
</li>
|
||||
{% empty %}
|
||||
<li>{% trans 'No PDFs available.' %}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
@ -0,0 +1,15 @@
|
||||
{% load i18n %}
|
||||
{% load static %}
|
||||
|
||||
<script src="{% static 'javascript/pdf.js' %}" type="text/javascript"></script>
|
||||
<script type="text/javascript">
|
||||
projector['pdf_url'] = '{{ pdf.mediafile.url }}';
|
||||
projector['pdf_page_num'] = {{ page_num }};
|
||||
projector['pdf_fullscreen'] = {% if fullscreen %}true{% else %}false{% endif %};
|
||||
PDFJS.workerSrc = "{% static 'javascript/pdf.worker.js' %}";
|
||||
</script>
|
||||
<script src="{% static 'javascript/pdf_presenter.js' %}" type="text/javascript"></script>
|
||||
|
||||
<div class="canvas-container">
|
||||
<canvas id="presentation" class="{% if fullscreen %}fullscreen{% endif %}"></canvas>
|
||||
</div>
|
@ -31,4 +31,12 @@ urlpatterns = patterns(
|
||||
url(r'^(?P<pk>\d+)/del/$',
|
||||
views.MediafileDeleteView.as_view(),
|
||||
name='mediafile_delete'),
|
||||
url(r'^pdf/next/$', views.PdfNextView.as_view(), name='next_pdf_page'),
|
||||
url(r'^pdf/prev/$', views.PdfPreviousView.as_view(), name='prev_pdf_page'),
|
||||
url(r'^pdf/target_page/$',
|
||||
views.PdfGoToPageView.as_view(),
|
||||
name='target_pdf_page'),
|
||||
url(r'^pdf/toggle_fullscreen/$',
|
||||
views.PdfToggleFullscreenView.as_view(),
|
||||
name='toggle_fullscreen')
|
||||
)
|
||||
|
@ -11,10 +11,16 @@
|
||||
"""
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.http import HttpResponse
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from openslides.config.api import config
|
||||
from openslides.projector.api import get_active_slide
|
||||
from openslides.projector.projector import Widget
|
||||
from openslides.utils.template import Tab
|
||||
from openslides.utils.views import CreateView, DeleteView, ListView, UpdateView
|
||||
from openslides.utils.tornado_webserver import ProjectorSocketHandler
|
||||
from openslides.utils.views import (AjaxView, CreateView, DeleteView, RedirectView, ListView,
|
||||
UpdateView)
|
||||
|
||||
from .forms import MediafileNormalUserCreateForm, MediafileUpdateForm
|
||||
from .models import Mediafile
|
||||
@ -94,6 +100,132 @@ class MediafileDeleteView(DeleteView):
|
||||
return super(MediafileDeleteView, self).on_clicked_yes(*args, **kwargs)
|
||||
|
||||
|
||||
class PdfNavBaseView(AjaxView):
|
||||
"""
|
||||
BaseView for the Pdf Ajax Navigation.
|
||||
"""
|
||||
|
||||
def get_ajax_context(self, *args, **kwargs):
|
||||
return {'current_page': self.active_slide['page_num']}
|
||||
|
||||
def load_other_page(self, active_slide):
|
||||
"""
|
||||
Tell connected clients to load an other pdf page.
|
||||
"""
|
||||
config['projector_active_slide'] = active_slide
|
||||
ProjectorSocketHandler.send_updates(
|
||||
{'calls': {'load_pdf_page': active_slide['page_num']}})
|
||||
|
||||
|
||||
class PdfNextView(PdfNavBaseView):
|
||||
"""
|
||||
Activate the next Page of a pdf and return the number of the current page.
|
||||
"""
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
"""
|
||||
Increment the page number by 1.
|
||||
|
||||
If the page number is set in the active slide, we are the value is
|
||||
incremented by 1. Otherwise, it is the first page and it is set to 2.
|
||||
"""
|
||||
self.active_slide = get_active_slide()
|
||||
if self.active_slide['callback'] == 'mediafile':
|
||||
if 'page_num' not in self.active_slide:
|
||||
self.active_slide['page_num'] = 2
|
||||
else:
|
||||
self.active_slide['page_num'] += 1
|
||||
self.load_other_page(self.active_slide)
|
||||
response = super(PdfNextView, self).get(self, request, *args, **kwargs)
|
||||
else:
|
||||
# no Mediafile is active and the JavaScript should not do anything.
|
||||
response = HttpResponse()
|
||||
return response
|
||||
|
||||
|
||||
class PdfPreviousView(PdfNavBaseView):
|
||||
"""
|
||||
Activate the previous Page of a pdf and return the number of the current page.
|
||||
"""
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
"""
|
||||
Decrement the page number by 1.
|
||||
|
||||
If the page number is set and it is greater than 1, it is decremented
|
||||
by 1. Otherwise, it is the first page and nothing happens.
|
||||
"""
|
||||
self.active_slide = get_active_slide()
|
||||
response = None
|
||||
if self.active_slide['callback'] == 'mediafile':
|
||||
if 'page_num' in self.active_slide and self.active_slide['page_num'] > 1:
|
||||
self.active_slide['page_num'] -= 1
|
||||
self.load_other_page(self.active_slide)
|
||||
response = super(PdfPreviousView, self).get(self, request, *args, **kwargs)
|
||||
if not response:
|
||||
response = HttpResponse()
|
||||
return response
|
||||
|
||||
|
||||
class PdfGoToPageView(PdfNavBaseView):
|
||||
"""
|
||||
Activate the page set in the textfield.
|
||||
"""
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
target_page = int(request.GET.get('page_num'))
|
||||
self.active_slide = get_active_slide()
|
||||
if target_page:
|
||||
self.active_slide['page_num'] = target_page
|
||||
self.load_other_page(self.active_slide)
|
||||
response = super(PdfGoToPageView, self).get(self, request, *args, **kwargs)
|
||||
else:
|
||||
response = HttpResponse()
|
||||
return response
|
||||
|
||||
|
||||
class PdfToggleFullscreenView(RedirectView):
|
||||
"""
|
||||
Toggle fullscreen mode for pdf presentations.
|
||||
"""
|
||||
allow_ajax = True
|
||||
url_name = 'dashboard'
|
||||
|
||||
def get_ajax_context(self, *args, **kwargs):
|
||||
config['pdf_fullscreen'] = not config['pdf_fullscreen']
|
||||
active_slide = get_active_slide()
|
||||
if active_slide['callback'] == 'mediafile':
|
||||
ProjectorSocketHandler.send_updates(
|
||||
{'calls': {'toggle_fullscreen': config['pdf_fullscreen']}})
|
||||
return {'fullscreen': config['pdf_fullscreen']}
|
||||
|
||||
|
||||
def get_widgets(request):
|
||||
"""
|
||||
Return the widgets of the projector app
|
||||
"""
|
||||
widgets = []
|
||||
|
||||
# PDF-Presentation widget
|
||||
pdfs = Mediafile.objects.filter(
|
||||
filetype__in=Mediafile.PRESENTABLE_FILE_TYPES,
|
||||
is_presentable=True
|
||||
)
|
||||
current_page = get_active_slide().get('page_num', 1)
|
||||
widgets.append(Widget(
|
||||
request,
|
||||
name='presentations',
|
||||
display_name=_('Presentations'),
|
||||
template='mediafile/pdfs_widget.html',
|
||||
context={'pdfs': pdfs, 'current_page': current_page,
|
||||
'pdf_fullscreen': config['pdf_fullscreen']},
|
||||
permission_required='projector.can_manage_projector',
|
||||
default_column=1,
|
||||
default_weight=75))
|
||||
|
||||
return widgets
|
||||
|
||||
|
||||
def register_tab(request):
|
||||
"""
|
||||
Inserts a new Tab to the views for files.
|
||||
|
@ -73,12 +73,17 @@ def config_variables(sender, **kwargs):
|
||||
name='projector_active_overlays',
|
||||
default_value=[])
|
||||
|
||||
projector_pdf_fullscreen = ConfigVariable(
|
||||
name='pdf_fullscreen',
|
||||
default_value=False)
|
||||
|
||||
return ConfigPage(
|
||||
title='No title here', url='bar', required_permission=None, variables=(
|
||||
projector, projector_message,
|
||||
countdown_time, countdown_start_stamp, countdown_pause_stamp,
|
||||
countdown_state, projector_scale, projector_scroll,
|
||||
projector_active_overlays, projector_js_cache))
|
||||
projector_active_overlays, projector_js_cache,
|
||||
projector_pdf_fullscreen))
|
||||
|
||||
|
||||
@receiver(projector_overlays, dispatch_uid="projector_countdown")
|
||||
|
@ -22,7 +22,7 @@ function restoreOrder() {
|
||||
var colid = value.id;
|
||||
var cookieName = "cookie-" + colid;
|
||||
var cookie = $.cookie(cookieName);
|
||||
if ( cookie == null ) { return; }
|
||||
if ( cookie === null ) { return; }
|
||||
var IDs = cookie.split(",");
|
||||
for (var i = 0, n = IDs.length; i < n; i++ ) {
|
||||
var widgetID = IDs[i];
|
||||
@ -81,7 +81,7 @@ $(function() {
|
||||
$('#countdown_play').show();
|
||||
$('#countdown_stop').hide();
|
||||
}
|
||||
$('#countdown_time').val(data['countdown_time'])
|
||||
$('#countdown_time').val(data['countdown_time']);
|
||||
}
|
||||
});
|
||||
});
|
||||
@ -123,6 +123,55 @@ $(function() {
|
||||
}
|
||||
});
|
||||
|
||||
// control pdf pages
|
||||
$('.pdf-page-ctl').click(function(event){
|
||||
event.preventDefault();
|
||||
var link = $(this);
|
||||
$.ajax({
|
||||
type: 'GET',
|
||||
url: link.attr('href'),
|
||||
dataType: 'json',
|
||||
success: function(data) {
|
||||
if (typeof data.current_page !== 'undefined') {
|
||||
$('#page_num').val(data.current_page);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$('.set-page-form').submit(function() {
|
||||
$(this).ajaxSubmit();
|
||||
return false;
|
||||
});
|
||||
|
||||
$('.go-first-page').click(function() {
|
||||
$('#page_num').val('1');
|
||||
$('.set-page-form').ajaxSubmit();
|
||||
});
|
||||
|
||||
$('.pdf-toggle-fullscreen').click(function(event){
|
||||
event.preventDefault();
|
||||
var link = $(this);
|
||||
$.ajax({
|
||||
type: 'GET',
|
||||
url: link.attr('href'),
|
||||
dataType: 'json',
|
||||
success: function(data) {
|
||||
if(data.fullscreen) {
|
||||
if (!link.hasClass('btn-primary')) {
|
||||
link.addClass('btn-primary');
|
||||
link.find('i').addClass('icon-white');
|
||||
}
|
||||
} else {
|
||||
if (link.hasClass('btn-primary')) {
|
||||
link.removeClass('btn-primary');
|
||||
link.find('i').removeClass('icon-white');
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/* comment out this function because '$.browser' has been removed from jquery 1.9, see:
|
||||
http://blog.jquery.com/2013/01/15/jquery-1-9-final-jquery-2-0-beta-migrate-final-released/
|
||||
TODO: use jquery migrate to have $.browser support for IE8;
|
||||
|
@ -66,6 +66,9 @@ var updater = {
|
||||
|
||||
updateProjector: function(data) {
|
||||
if (data.content) {
|
||||
$('#content').removeClass('fullscreen');
|
||||
$('#footer').removeClass('black');
|
||||
$('body').removeClass('black');
|
||||
$('#content').html(data.content);
|
||||
}
|
||||
if (data.overlays) {
|
||||
|
@ -86,12 +86,11 @@ body{
|
||||
top: 150px;
|
||||
right: 40px;
|
||||
z-index: -1;
|
||||
transition: all 1s;
|
||||
line-height: normal;
|
||||
transition-property: margin, font-size;
|
||||
transition-duration: 1s;
|
||||
}
|
||||
#content .scroll {
|
||||
transition: margin 1s;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.25em;
|
||||
margin-bottom: 40px;
|
||||
@ -193,3 +192,37 @@ tr.total td {
|
||||
td.elected {
|
||||
background-color: #BED4DE !important;
|
||||
}
|
||||
|
||||
/* PDF Presentation */
|
||||
|
||||
.canvas-container {
|
||||
width: 100%;
|
||||
text-align:center;
|
||||
}
|
||||
|
||||
#presentation {
|
||||
position: relative;
|
||||
left:-75px;
|
||||
top: -77px;
|
||||
}
|
||||
|
||||
#presentation.fullscreen {
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.fullscreen {
|
||||
z-index: 1000!important;
|
||||
left: 0!important;
|
||||
top: 0!important;
|
||||
right: 0!important;
|
||||
background-color: #000000;
|
||||
}
|
||||
|
||||
#footer.black {
|
||||
display: none;
|
||||
}
|
||||
|
||||
body.black {
|
||||
background-color: #000000;
|
||||
}
|
@ -9,6 +9,19 @@
|
||||
<link href="{% static 'styles/projector.css' %}" type="text/css" rel="stylesheet">
|
||||
<link href="{% static 'img/favicon.png' %}" type="image/png" rel="shortcut icon">
|
||||
<title>{% trans 'Projector' %} – {{ 'event_name'|get_config }}</title>
|
||||
|
||||
<script type="text/javascript" src="{% static 'javascript/jquery.min.js' %}"></script>
|
||||
<script type="text/javascript" src="{% static 'javascript/sockjs-0.3.min.js' %}"></script>
|
||||
<script type="text/javascript" src="{% static 'javascript/projector.js' %}"></script>
|
||||
<script type="text/javascript">
|
||||
{% for js in overlay_js %}
|
||||
projector.update_data({{ js|safe }});
|
||||
{% endfor %}
|
||||
{% for key, value in calls.items %}
|
||||
projector.{{ key }}({{ value }});
|
||||
{% endfor %}
|
||||
</script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div id="header">
|
||||
@ -34,16 +47,5 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="{% static 'javascript/jquery.min.js' %}"></script>
|
||||
<script type="text/javascript" src="{% static 'javascript/sockjs-0.3.min.js' %}"></script>
|
||||
<script type="text/javascript" src="{% static 'javascript/projector.js' %}"></script>
|
||||
<script type="text/javascript">
|
||||
{% for js in overlay_js %}
|
||||
projector.update_data({{ js|safe }});
|
||||
{% endfor %}
|
||||
{% for key, value in calls.items %}
|
||||
projector.{{ key }}({{ value }});
|
||||
{% endfor %}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -18,9 +18,11 @@ from django.shortcuts import redirect
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from openslides.config.api import config
|
||||
from openslides.utils.tornado_webserver import ProjectorSocketHandler
|
||||
from openslides.utils.template import Tab
|
||||
from openslides.utils.views import (AjaxMixin, CreateView, DeleteView,
|
||||
RedirectView, TemplateView, UpdateView)
|
||||
from openslides.mediafile.models import Mediafile
|
||||
|
||||
from .api import (call_on_projector, get_active_slide, get_all_widgets,
|
||||
get_overlays, get_projector_content, get_projector_overlays,
|
||||
@ -84,6 +86,16 @@ class ActivateView(RedirectView):
|
||||
allow_ajax = True
|
||||
|
||||
def pre_redirect(self, request, *args, **kwargs):
|
||||
if kwargs['callback'] == 'mediafile' and \
|
||||
get_active_slide()['callback'] == 'mediafile':
|
||||
# If the current slide is a pdf and the new page is also a slide, we dont have to use
|
||||
# set_active_slide, because is causes a content reload.
|
||||
kwargs.update({'page_num': 1, 'pk': request.GET.get('pk')})
|
||||
url = Mediafile.objects.get(pk=kwargs['pk'], is_presentable=True).mediafile.url
|
||||
config['projector_active_slide'] = kwargs
|
||||
ProjectorSocketHandler.send_updates(
|
||||
{'calls': {'load_pdf': {'url': url, 'page_num': kwargs['page_num']}}})
|
||||
else:
|
||||
set_active_slide(kwargs['callback'], kwargs=dict(request.GET.items()))
|
||||
config['projector_scroll'] = config.get_default('projector_scroll')
|
||||
config['projector_scale'] = config.get_default('projector_scale')
|
||||
|
@ -46,6 +46,10 @@ $(function () {
|
||||
}
|
||||
link.addClass('btn-primary');
|
||||
link.children('i').addClass('icon-white');
|
||||
// set page_num to 1 if a pdf is activated
|
||||
if ( link.hasClass('choose-pdf') ) {
|
||||
$('#page_num').val(1);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -322,7 +322,9 @@ legend + .control-group {
|
||||
.icon-append_to_list_of_speakers {
|
||||
background-position: -48px -144px;
|
||||
}
|
||||
|
||||
.icon-presentations {
|
||||
background-position: -264px -48px;
|
||||
}
|
||||
/** More glyphicons free icons **/
|
||||
.status_link .icon-on, .icon-checked-new {
|
||||
background-image: url("../img/glyphicons_152_check.png");
|
||||
|
Loading…
Reference in New Issue
Block a user