Merge pull request #2374 from normanjaeckel/LoadJavaScript

Changed loading of JavaScript files.
This commit is contained in:
Emanuel Schütze 2016-09-18 12:24:40 +02:00 committed by GitHub
commit f288cb4967
17 changed files with 130 additions and 71 deletions

View File

@ -8,6 +8,10 @@ cache:
python:
- "3.4"
- "3.5"
env:
- TRAVIS_NODE_VERSION="4"
before_install:
- "nvm install $TRAVIS_NODE_VERSION"
install:
- "pip install --upgrade setuptools"
- "pip install --upgrade --requirement requirements.txt"

View File

@ -74,6 +74,10 @@ avoid opening new browser windows::
$ python manage.py runserver
Use gulp watch in a second command-line interface::
$ node_modules/.bin/gulp watch
2. Installation on Windows
--------------------------

View File

@ -26,6 +26,8 @@ var argv = require('yargs').argv,
mainBowerFiles = require('main-bower-files'),
path = require('path'),
rename = require('gulp-rename'),
sourcemaps = require('gulp-sourcemaps'),
templateCache = require('gulp-angular-templatecache'),
through = require('through2'),
uglify = require('gulp-uglify'),
vsprintf = require('sprintf-js').vsprintf;
@ -38,13 +40,44 @@ var output_directory = path.join('openslides', 'static');
* Default tasks to be run before start.
*/
// Catches all JavaScript files from all core apps and concats them to one
// file js/openslides.js. In production mode the file is uglified.
gulp.task('js', function () {
return gulp.src(path.join('openslides', '*', 'static', 'js', '**', '*.js'))
.pipe(sourcemaps.init())
.pipe(concat('openslides.js'))
.pipe(sourcemaps.write())
.pipe(gulpif(argv.production, uglify()))
.pipe(gulp.dest(path.join(output_directory, 'js')));
});
// Catches all JavaScript files from all bower components and concats them to
// one file js/openslides-libs.js. In production mode the file is uglified.
gulp.task('js-libs', function () {
return gulp.src(mainBowerFiles({
filter: /\.js$/
}))
.pipe(sourcemaps.init())
.pipe(concat('openslides-libs.js'))
.pipe(sourcemaps.write())
.pipe(gulpif(argv.production, uglify()))
.pipe(gulp.dest(path.join(output_directory, 'js')));
});
// Catches all template files from all core apps and concats them to one
// file js/openslides-templates.js. In production mode the file is uglified.
gulp.task('templates', function () {
return gulp.src(path.join('openslides', '*', 'static', 'templates', '**', '*.html'))
.pipe(templateCache('openslides-templates.js', {
module: 'OpenSlidesApp-templates',
standalone: true,
moduleSystem: 'IIFE',
transformUrl: function (url) {
var pathList = url.split(path.sep);
pathList.shift();
return pathList.join(path.sep);
},
}))
.pipe(gulpif(argv.production, uglify()))
.pipe(gulp.dest(path.join(output_directory, 'js')));
});
@ -117,13 +150,19 @@ gulp.task('translations', function () {
});
// Gulp default task. Runs all other tasks before.
gulp.task('default', ['js-libs', 'css-libs', 'fonts-libs', 'tinymce', 'angular-chosen-img', 'translations'], function () {});
gulp.task('default', ['js', 'js-libs', 'templates', 'css-libs', 'fonts-libs', 'tinymce', 'angular-chosen-img', 'translations'], function () {});
/**
* Extra tasks that have to be called manually. Useful for development.
*/
// Watches changes in JavaScript and templates.
gulp.task('watch', ['js', 'templates'], function () {
gulp.watch(path.join('openslides', '*', 'static', 'js', '**', '*.js'), ['js']);
gulp.watch(path.join('openslides', '*', 'static', 'templates', '**', '*.html'), ['templates']);
});
// Extracts translatable strings using angular-gettext and saves them in file
// openslides/locale/angular-gettext/template-en.pot.
gulp.task('pot', function () {

View File

@ -6,7 +6,6 @@ class AgendaAppConfig(AppConfig):
verbose_name = 'OpenSlides Agenda'
angular_site_module = True
angular_projector_module = True
js_files = ['js/agenda/base.js', 'js/agenda/site.js', 'js/agenda/projector.js']
def ready(self):
# Load projector elements.

View File

@ -6,7 +6,6 @@ class AssignmentsAppConfig(AppConfig):
verbose_name = 'OpenSlides Assignments'
angular_site_module = True
angular_projector_module = True
js_files = ['js/assignments/base.js', 'js/assignments/site.js', 'js/assignments/projector.js']
def ready(self):
# Load projector elements.

View File

@ -6,7 +6,6 @@ class CoreAppConfig(AppConfig):
verbose_name = 'OpenSlides Core'
angular_site_module = True
angular_projector_module = True
js_files = ['js/core/base.js', 'js/core/site.js', 'js/core/projector.js']
def ready(self):
# Load projector elements.

View File

@ -10,7 +10,8 @@ angular.module('OpenSlidesApp.core', [
'ngSanitize', // TODO: only use this in functions that need it.
'ui.bootstrap',
'ui.tree',
'pdf'
'pdf',
'OpenSlidesApp-templates',
])
.config([

View File

@ -869,7 +869,7 @@ angular.module('OpenSlidesApp.core.site', [
return {
restrict: 'E',
scope: true,
templateUrl: '/static/templates/config-form-field.html',
templateUrl: 'static/templates/config-form-field.html',
link: function ($scope, iElement, iAttrs, controller, transcludeFn) {
var field = $parse(iAttrs.field)($scope);
var config = Config.get(field.key);

View File

@ -10,6 +10,8 @@
<link rel="stylesheet" href="static/css/app.css">
<link rel="icon" href="/static/img/favicon.png">
<script src="static/js/openslides-libs.js"></script>
<script src="static/js/openslides.js"></script>
<script src="static/js/openslides-templates.js"></script>
<div id="wrapper" ng-cloak>
@ -199,5 +201,4 @@
</div><!--end wrapper-->
<script src="/angular_js/site/"></script>
<script src="/webclient/site/"></script>

View File

@ -9,6 +9,8 @@
<link rel="stylesheet" href="static/css/projector.css">
<link rel="icon" href="/static/img/favicon.png">
<script src="static/js/openslides-libs.js"></script>
<script src="static/js/openslides.js"></script>
<script src="static/js/openslides-templates.js"></script>
<div ng-controller="ProjectorContainerCtrl" class="pContainer">
<div>
@ -42,4 +44,4 @@
</div>
</div>
<script src="/angular_js/projector/"></script>
<script src="/webclient/projector/"></script>

View File

@ -8,6 +8,8 @@
<link rel="stylesheet" href="static/css/projector.css">
<link rel="icon" href="/static/img/favicon.png">
<script src="static/js/openslides-libs.js"></script>
<script src="static/js/openslides.js"></script>
<script src="static/js/openslides-templates.js"></script>
<style type="text/css">
#header, #footer {
@ -55,4 +57,5 @@
{{ config('general_event_location') }}
</span>
</div>
<script src="/angular_js/projector/"></script>
<script src="/webclient/projector/"></script>

View File

@ -23,9 +23,9 @@ urlpatterns = [
views.MediaEncoder.as_view(),
name="core_mediaencoding"),
url(r'^angular_js/(?P<openslides_app>site|projector)/$',
views.AppsJsView.as_view(),
name='core_apps_js'),
url(r'^webclient/(?P<realm>site|projector)/$',
views.WebclientJavaScriptView.as_view(),
name='core_webclient_javascript'),
# View for the projectors are handled by angular.
url(r'^projector.*$', views.ProjectorView.as_view()),

View File

@ -5,6 +5,7 @@ import re
import uuid
from collections import OrderedDict
from operator import attrgetter
from textwrap import dedent
from urllib.parse import unquote
from django.apps import apps
@ -92,72 +93,81 @@ class RealProjectorView(utils_views.View):
return HttpResponse(content)
class AppsJsView(utils_views.View):
class WebclientJavaScriptView(utils_views.View):
"""
Returns javascript code to be called in the angular app.
The javascript code loads all js-files defined by the installed (django)
apps and creates the angular modules for each angular app.
This view returns JavaScript code for the main entry point in the
AngularJS app for the requested realm (site or projector). Also code
for plugins is appended. The result is not uglified.
"""
def get(self, *args, **kwargs):
angular_modules = []
js_files = []
realm = kwargs.get('realm') # Result is 'site' or 'projector'
for app_config in apps.get_app_configs():
# Add the angular app, if the module has one.
if getattr(app_config,
'angular_{}_module'.format(kwargs.get('openslides_app')),
False):
angular_modules.append('OpenSlidesApp.{app_name}.{app}'.format(
app=kwargs.get('openslides_app'),
app_name=app_config.label))
# Add the angular app if the module has one.
if getattr(app_config, 'angular_{}_module'.format(realm), False):
angular_modules.append('OpenSlidesApp.{app_name}.{realm}'.format(
app_name=app_config.label,
realm=realm))
# Add all js files that the module needs
try:
app_js_files = app_config.js_files
except AttributeError:
# The app needs no js-files
pass
else:
js_files += [
'{static}{path}'.format(
static=settings.STATIC_URL,
path=path)
for path in app_js_files]
# Use javascript loadScript function from
# Add all JavaScript files that the module needs. Our core apps
# are delivered by an extra file js/openslides.js which can be
# created via gulp.
core_apps = (
'openslides.core',
'openslides.agenda',
'openslides.motions',
'openslides.assignments',
'openslides.users',
'openslides.mediafiles',
)
if app_config.name not in core_apps:
try:
app_js_files = app_config.js_files
except AttributeError:
# The app needs no JavaScript files.
pass
else:
js_files.extend(app_js_files)
# Use JavaScript loadScript function from
# http://balpha.de/2011/10/jquery-script-insertion-and-its-consequences-for-debugging/
return HttpResponse(
# jQuery is required.
content = dedent(
"""
var loadScript = function (path) {
var result = $.Deferred(),
script = document.createElement("script");
script.async = "async";
script.type = "text/javascript";
script.src = path;
script.onload = script.onreadystatechange = function(_, isAbort) {
if (!script.readyState || /loaded|complete/.test(script.readyState)) {
if (isAbort)
result.reject();
else
result.resolve();
}
(function () {
var loadScript = function (path) {
var result = $.Deferred(),
script = document.createElement("script");
script.async = "async";
script.type = "text/javascript";
script.src = path;
script.onload = script.onreadystatechange = function(_, isAbort) {
if (!script.readyState || /loaded|complete/.test(script.readyState)) {
if (isAbort)
result.reject();
else
result.resolve();
}
};
script.onerror = function () { result.reject(); };
$("head")[0].appendChild(script);
return result.promise();
};
script.onerror = function () { result.reject(); };
$("head")[0].appendChild(script);
return result.promise();
};
""" +
"""
angular.module('OpenSlidesApp.{app}', {angular_modules});
var deferres = [];
{js_files}.forEach( function(js_file) {{ deferres.push(loadScript(js_file)); }} );
$.when.apply(this,deferres).done(function() {{
angular.bootstrap(document,['OpenSlidesApp.{app}']);
}} );
angular.module('OpenSlidesApp.{realm}', {angular_modules});
var deferres = [];
{js_files}.forEach( function(js_file) {{ deferres.push(loadScript(js_file)); }} );
$.when.apply(this,deferres).done( function() {{
angular.bootstrap(document,['OpenSlidesApp.{realm}']);
}} );
""".format(realm=realm, angular_modules=angular_modules, js_files=js_files) +
"""
.format(
app=kwargs.get('openslides_app'),
angular_modules=angular_modules,
js_files=js_files))
}());
""")
return HttpResponse(content, content_type='application/javascript')
# Viewsets for the REST API

View File

@ -6,7 +6,6 @@ class MediafilesAppConfig(AppConfig):
verbose_name = 'OpenSlides Mediafiles'
angular_site_module = True
angular_projector_module = True
js_files = ['js/mediafiles/base.js', 'js/mediafiles/site.js', 'js/mediafiles/projector.js']
def ready(self):
# Load projector elements.

View File

@ -7,8 +7,6 @@ class MotionsAppConfig(AppConfig):
verbose_name = 'OpenSlides Motion'
angular_site_module = True
angular_projector_module = True
js_files = ['js/motions/base.js', 'js/motions/site.js', 'js/motions/projector.js',
'js/motions/linenumbering.js', 'js/motions/diff.js', 'js/motions/motion-services.js']
def ready(self):
# Load projector elements.

View File

@ -6,7 +6,6 @@ class UsersAppConfig(AppConfig):
verbose_name = 'OpenSlides Users'
angular_site_module = True
angular_projector_module = True
js_files = ['js/users/base.js', 'js/users/site.js', 'js/users/projector.js']
def ready(self):
# Load projector elements.

View File

@ -10,17 +10,19 @@
"es6-promise": "~3.0.2",
"gulp": "~3.9.0",
"gulp-angular-gettext": "~2.1.0",
"gulp-angular-templatecache": "^2.0.0",
"gulp-concat": "~2.6.0",
"gulp-cssnano": "~2.1.0",
"gulp-if": "~2.0.0",
"gulp-jshint": "~2.0.0",
"gulp-rename": "~1.2.2",
"gulp-sourcemaps": "~1.6.0",
"gulp-uglify": "~1.5.2",
"jasmine": "~2.4.1",
"jshint": "~2.9.2",
"karma": "~1.1.0",
"karma-jasmine": "~1.0.2",
"karma-chrome-launcher": "~1.0.1",
"karma-jasmine": "~1.0.2",
"main-bower-files": "~2.11.1",
"po2json": "~0.4.1",
"sprintf-js": "~1.0.3",