From 9eef4da21c6954318c828c90515408e123021abc Mon Sep 17 00:00:00 2001
From: Oskar Hahn Drag and drop items to change the order of the agenda. Your modification will be saved immediately.
-
-
-
- The list of speakers is empty.
-
-
- {{ speaker.user.get_full_name() }}
-
-
-
-
-
-
-
-
-
-
- {{ speaker.user.get_full_name() }}
-
-
- + {{ nextSpeakers.length - 3 }}
-
- {{ speaker.user.get_full_name() }}
-
-
-
- {{ speaker.user.get_full_name() }}
-
-
- {{ speaker.user.get_full_name() }}
-
-
-
-
-
- {{ speaker.user.get_full_name() }}
-
-
-
-
-
-
-
- .
- // new_content_children will finally contain all nodes that are
- // not empty.
- var new_content_children = [];
- _.forEach(fragment.children, function (child) {
- var empty = true;
- if (child.children){
- _.forEach(child.children, function(grandchild) {
- if (grandchild.name != 'p' && grandchild.name != 'br') {
- empty = false;
- } else if (grandchild.isEmpty !== true) {
- empty = false;
- }
- });
- if (empty === false) {
- new_content_children.push(child);
- }
- } else {
- if (child.name != 'p' && child.name != 'br' &&
- child.isEmpty !== true){
- new_content_children.push(child);
- }
- }
- });
- fragment.children = new_content_children;
- }
- fragment.writeHtml(writer);
- // Return the re-created fragment without the empty and ') {
- docx += ' ' + str + ' {{ help_text | translate }}
-
- OpenSlides {{ core_version }}
-
- ( Installed plugins: {{ error | translate }} No results. Line 1 Line 1 Line 2 Line 3Current list of speakers
- {{ item.getTitle() }}
-
- Closed
-
-
- {{ item.getTitle() }}
-
-
- Agenda
-
-
-Sort agenda
-
-
-
-
-
-
- List of speakers
-
-
-
-
- List of speakers
- {{ agendaItem.getListOfSpeakersTitle() }}
-
- – {{ (agendaItem.speakers | filter: {begin_time: null}).length }}
-
-
- Agenda
- {{ rootItem.getTitle() }}
-
-
-
-
- List of speakers
-
- {{ item.getListOfSpeakersTitle() }}
-
- – {{ (item.speakers | filter: {begin_time: null}).length }}
-
-
-
',
- hideExpression: '!model.more'
- },
- {
- key: 'tags_id',
- type: 'select-multiple',
- templateOptions: {
- label: gettextCatalog.getString('Tags'),
- options: Tag.getAll(),
- ngOptions: 'option.id as option.name for option in to.options',
- placeholder: gettextCatalog.getString('Select or search a tag ...')
- },
- hideExpression: '!model.more'
- }
- );
- }
-
- return formFields;
- }
- };
- }
-])
-
-// Cache for AssignmentPollDetailCtrl so that users choices are keeped during user actions (e. g. save poll form).
-.value('AssignmentPollDetailCtrlCache', {})
-
-// Child controller of AssignmentDetailCtrl for each single poll.
-.controller('AssignmentPollDetailCtrl', [
- '$scope',
- 'MajorityMethodChoices',
- 'Config',
- 'AssignmentPollDetailCtrlCache',
- 'AssignmentPoll',
- 'AssignmentPollDecimalPlaces',
- function ($scope, MajorityMethodChoices, Config, AssignmentPollDetailCtrlCache,
- AssignmentPoll, AssignmentPollDecimalPlaces) {
- // Define choices.
- $scope.methodChoices = MajorityMethodChoices;
- // TODO: Get $scope.baseChoices from config_variables.py without copying them.
-
- $scope.votesPrecision = AssignmentPollDecimalPlaces.getPlaces($scope.poll);
-
- // Setup empty cache with default values.
- if (typeof AssignmentPollDetailCtrlCache[$scope.poll.id] === 'undefined') {
- AssignmentPollDetailCtrlCache[$scope.poll.id] = {
- method: $scope.config('assignments_poll_default_majority_method'),
- };
- }
-
- // Fetch users choices from cache.
- $scope.method = AssignmentPollDetailCtrlCache[$scope.poll.id].method;
-
- $scope.recalculateMajorities = function (method) {
- $scope.method = method;
- _.forEach($scope.poll.options, function (option) {
- option.majorityReached = option.isReached(method);
- });
- };
- $scope.recalculateMajorities($scope.method);
-
- $scope.saveDescriptionChange = function (poll) {
- AssignmentPoll.save(poll);
- };
-
- // Save current values to cache on destroy of this controller.
- $scope.$on('$destroy', function() {
- AssignmentPollDetailCtrlCache[$scope.poll.id] = {
- method: $scope.method,
- };
- });
- }
-])
-
-.controller('AssignmentListCtrl', [
- '$scope',
- 'ngDialog',
- 'AssignmentForm',
- 'Assignment',
- 'Tag',
- 'Agenda',
- 'Projector',
- 'ProjectionDefault',
- 'gettextCatalog',
- 'User',
- 'osTableFilter',
- 'osTableSort',
- 'osTablePagination',
- 'gettext',
- 'AssignmentPhases',
- 'AssignmentPdfExport',
- function($scope, ngDialog, AssignmentForm, Assignment, Tag, Agenda, Projector,
- ProjectionDefault, gettextCatalog, User, osTableFilter, osTableSort, osTablePagination,
- gettext, AssignmentPhases, AssignmentPdfExport) {
- $scope.$watch(function () {
- return Assignment.lastModified();
- }, function () {
- $scope.assignments = _.orderBy(Assignment.getAll(), ['title']);
- });
- Tag.bindAll({}, $scope, 'tags');
- $scope.$watch(function () {
- return Projector.lastModified();
- }, function () {
- var projectiondefault = ProjectionDefault.filter({name: 'assignments'})[0];
- if (projectiondefault) {
- $scope.defaultProjectorId = projectiondefault.projector_id;
- }
- });
- $scope.phases = AssignmentPhases;
- $scope.alert = {};
-
- // Filtering
- $scope.filter = osTableFilter.createInstance('AssignmentTableFilter');
-
- if (!$scope.filter.existsStorageEntry()) {
- $scope.filter.multiselectFilters = {
- tag: [],
- phase: [],
- };
- }
- $scope.filter.propertyList = ['title', 'description'];
- $scope.filter.propertyFunctionList = [
- function (assignment) {
- return gettextCatalog.getString($scope.phases[assignment.phase].display_name);
- },
- ];
- $scope.filter.propertyDict = {
- 'assignment_related_users': function (candidate) {
- return candidate.user.get_short_name();
- },
- 'tags': function (tag) {
- return tag.name;
- },
- };
- $scope.getItemId = {
- tag: function (assignment) {return assignment.tags_id;},
- phase: function (assignment) {return assignment.phase;},
- };
-
- // Sorting
- $scope.sort = osTableSort.createInstance('AssignmentTableSort');
- if (!$scope.sort.column) {
- $scope.sort.column = 'title';
- }
- $scope.sortOptions = [
- {name: 'agenda_item.getItemNumberWithAncestors()',
- display_name: gettext('Item')},
- {name: 'title',
- display_name: gettext('Title')},
- {name: 'phase',
- display_name: gettext('Phase')},
- {name: 'assignment_related_users.length',
- display_name: gettext('Number of candidates')},
- ];
- $scope.hasTag = function (assignment, tag) {
- return _.indexOf(assignment.tags_id, tag.id) > -1;
- };
- $scope.toggleTag = function (assignment, tag) {
- if ($scope.hasTag(assignment, tag)) {
- assignment.tags_id = _.filter(assignment.tags_id, function (tag_id){
- return tag_id != tag.id;
- });
- } else {
- assignment.tags_id.push(tag.id);
- }
- Assignment.save(assignment);
- };
-
- // Pagination
- $scope.pagination = osTablePagination.createInstance('AssignmentTablePagination');
-
- // update phase
- $scope.updatePhase = function (assignment, phase_id) {
- assignment.phase = phase_id;
- Assignment.save(assignment);
- };
- // open new/edit dialog
- $scope.openDialog = function (assignment) {
- ngDialog.open(AssignmentForm.getDialog(assignment));
- };
- // *** select mode functions ***
- $scope.isSelectMode = false;
- // check all checkboxes
- $scope.checkAll = function () {
- $scope.selectedAll = !$scope.selectedAll;
- angular.forEach($scope.assignments, function (assignment) {
- assignment.selected = $scope.selectedAll;
- });
- };
- // uncheck all checkboxes if isSelectMode is closed
- $scope.uncheckAll = function () {
- if (!$scope.isSelectMode) {
- $scope.selectedAll = false;
- angular.forEach($scope.assignments, function (assignment) {
- assignment.selected = false;
- });
- }
- };
- // delete all selected assignments
- $scope.deleteMultiple = function () {
- angular.forEach($scope.assignments, function (assignment) {
- if (assignment.selected)
- Assignment.destroy(assignment.id);
- });
- $scope.isSelectMode = false;
- $scope.uncheckAll();
- };
- // delete single assignment
- $scope.delete = function (assignment) {
- Assignment.destroy(assignment.id);
- };
- // create the PDF List
- $scope.pdfExport = function () {
- AssignmentPdfExport.export($scope.assignmentsFiltered);
- };
- }
-])
-
-.controller('AssignmentDetailCtrl', [
- '$scope',
- '$http',
- '$filter',
- '$timeout',
- 'filterFilter',
- 'gettext',
- 'ngDialog',
- 'AssignmentForm',
- 'operator',
- 'Assignment',
- 'User',
- 'assignmentId',
- 'Projector',
- 'ProjectionDefault',
- 'gettextCatalog',
- 'AssignmentPhases',
- 'AssignmentPdfExport',
- 'WebpageTitle',
- 'ErrorMessage',
- function($scope, $http, $filter, $timeout, filterFilter, gettext, ngDialog, AssignmentForm, operator,
- Assignment, User, assignmentId, Projector, ProjectionDefault, gettextCatalog, AssignmentPhases,
- AssignmentPdfExport, WebpageTitle, ErrorMessage) {
- User.bindAll({}, $scope, 'users');
- var assignment = Assignment.get(assignmentId);
- Assignment.loadRelations(assignment, 'agenda_item');
- // This flag is for setting 'activeTab' to recently added (last) ballot tab.
- // Set this flag, if ballots are added/removed. When the next autoupdate comes
- // in, the tabset will be updated.
- var updateBallotTabsFlag = true;
- $scope.$watch(function () {
- return Projector.lastModified();
- }, function () {
- var projectiondefault = ProjectionDefault.filter({name: 'assignments'})[0];
- if (projectiondefault) {
- $scope.defaultProjectorId = projectiondefault.projector_id;
- }
- });
- $scope.$watch(function () {
- return Assignment.lastModified(assignmentId);
- }, function () {
- // setup sorting of candidates
- $scope.relatedUsersSorted = $filter('orderBy')(assignment.assignment_related_users, 'weight');
- $scope.assignment = Assignment.get(assignment.id);
- if (updateBallotTabsFlag) {
- $scope.activeTab = $scope.assignment.polls.length - 1;
- updateBallotTabsFlag = false;
- }
- WebpageTitle.updateTitle(gettextCatalog.getString('Election') + ' ' + $scope.assignment.title);
- });
- $scope.candidateSelectBox = {};
- $scope.phases = AssignmentPhases;
- $scope.alert = {};
-
- // open edit dialog
- $scope.openDialog = function () {
- ngDialog.open(AssignmentForm.getDialog($scope.assignment));
- };
- // add (nominate) candidate
- $scope.addCandidate = function (userId) {
- $http.post('/rest/assignments/assignment/' + assignmentId + '/candidature_other/', {'user': userId})
- .then(function (success){
- $scope.alert.show = false;
- $scope.candidateSelectBox = {};
- }, function (error){
- $scope.alert = ErrorMessage.forAlert(error);
- $scope.candidateSelectBox = {};
- });
- };
- // remove candidate
- $scope.removeCandidate = function (userId) {
- $http.delete('/rest/assignments/assignment/' + assignmentId + '/candidature_other/',
- {headers: {'Content-Type': 'application/json'},
- data: JSON.stringify({user: userId})})
- .then(function (success) {},
- function (error) {
- $scope.alert = ErrorMessage.forAlert(error);
- }
- );
- };
- // add me (nominate self as candidate)
- $scope.addMe = function () {
- $http.post('/rest/assignments/assignment/' + assignmentId + '/candidature_self/', {}).then(
- function (success) {
- $scope.alert.show = false;
- }, function (error) {
- $scope.alert = ErrorMessage.forAlert(error);
- }
- );
- };
- // remove me (withdraw own candidature)
- $scope.removeMe = function () {
- $http.delete('/rest/assignments/assignment/' + assignmentId + '/candidature_self/').then(
- function (success) {
- $scope.alert.show = false;
- }, function (error) {
- $scope.alert = ErrorMessage.forAlert(error);
- }
- );
- };
- // check if current user is already a candidate (elected==false)
- $scope.isCandidate = function () {
- var check = $scope.assignment.assignment_related_users.map(function(candidate) {
- if (!candidate.elected) {
- return candidate.user_id;
- }
- }).indexOf(operator.user.id);
- if (check > -1) {
- return true;
- } else {
- return false;
- }
- };
- // Sort all candidates
- $scope.treeOptions = {
- dropped: function () {
- var sortedCandidates = [];
- _.forEach($scope.relatedUsersSorted, function (user) {
- sortedCandidates.push(user.id);
- });
- $http.post('/rest/assignments/assignment/' + assignmentId + '/sort_related_users/',
- {related_users: sortedCandidates}
- );
- }
- };
- // update phase
- $scope.updatePhase = function (phase_id) {
- $scope.assignment.phase = phase_id;
- Assignment.save($scope.assignment);
- };
- // create new ballot
- $scope.createBallot = function () {
- $http.post('/rest/assignments/assignment/' + assignmentId + '/create_poll/').then(
- function (success) {
- $scope.alert.show = false;
- if (assignment.phase === 0) {
- $scope.updatePhase(1);
- }
- updateBallotTabsFlag = true;
- }, function (error) {
- $scope.alert = ErrorMessage.forAlert(error);
- }
- );
- };
- // delete ballot
- $scope.deleteBallot = function (poll) {
- poll.DSDestroy().then(
- function (success) {
- $scope.activeTab = $scope.activeTab - 1;
- updateBallotTabsFlag = true;
- }
- );
- };
- // edit poll dialog
- $scope.editPollDialog = function (poll, ballot) {
- ngDialog.open({
- template: 'static/templates/assignments/assignmentpoll-form.html',
- controller: 'AssignmentPollUpdateCtrl',
- className: 'ngdialog-theme-default',
- closeByEscape: false,
- closeByDocument: false,
- resolve: {
- assignmentpollId: function () {return poll.id;},
- ballot: function () {return ballot;},
- }
- });
- };
- // publish ballot
- $scope.togglePublishBallot = function (poll) {
- poll.DSUpdate({
- assignment_id: assignmentId,
- published: !poll.published,
- })
- .then(function (success) {
- $scope.alert.show = false;
- }, function (error) {
- $scope.alert = ErrorMessage.forAlert(error);
- });
- };
-
- // mark candidate as (not) elected
- $scope.markElected = function (user, reverse) {
- if (reverse) {
- $http.delete(
- '/rest/assignments/assignment/' + assignmentId + '/mark_elected/', {
- headers: {
- 'Content-Type': 'application/json'
- },
- data: JSON.stringify({user: user})
- });
- } else {
- $http.post('/rest/assignments/assignment/' + assignmentId + '/mark_elected/', {'user': user});
- }
-
- };
-
- // Creates the document as pdf
- $scope.pdfExport = function() {
- AssignmentPdfExport.export($scope.assignment, true);
- };
- // Creates the ballotpaper as pdf
- $scope.ballotpaperExport = function(pollId) {
- AssignmentPdfExport.createBallotPdf($scope.assignment, pollId);
- };
-
- // Just mark some vote value strings for translation.
- gettext('Yes');
- gettext('No');
- gettext('Abstain');
- }
-])
-
-.controller('AssignmentCreateCtrl', [
- '$scope',
- '$state',
- 'Assignment',
- 'AssignmentForm',
- 'Agenda',
- 'Config',
- 'ErrorMessage',
- function($scope, $state, Assignment, AssignmentForm, Agenda, Config, ErrorMessage) {
- $scope.model = {
- agenda_type: parseInt(Config.get('agenda_new_items_default_visibility').value),
- };
- // set default value for open posts form field
- $scope.model.open_posts = 1;
- // get all form fields
- $scope.formFields = AssignmentForm.getFormFields(true);
- // save assignment
- $scope.save = function(assignment, gotoDetailView) {
- Assignment.create(assignment).then(
- function (success) {
- if (gotoDetailView) {
- $state.go('assignments.assignment.detail', {id: success.id});
- }
- $scope.closeThisDialog();
- },
- function (error) {
- $scope.alert = ErrorMessage.forAlert(error);
- }
- );
- };
- }
-])
-
-.controller('AssignmentUpdateCtrl', [
- '$scope',
- '$state',
- 'Assignment',
- 'AssignmentForm',
- 'Agenda',
- 'assignmentId',
- 'ErrorMessage',
- function($scope, $state, Assignment, AssignmentForm, Agenda, assignmentId, ErrorMessage) {
- var assignment = Assignment.get(assignmentId);
- $scope.alert = {};
- // set initial values for form model by create deep copy of assignment object
- // so list/detail view is not updated while editing
- $scope.model = angular.copy(assignment);
- // get all form fields
- $scope.formFields = AssignmentForm.getFormFields();
-
- // save assignment
- $scope.save = function (assignment, gotoDetailView) {
- // inject the changed assignment (copy) object back into DS store
- Assignment.inject(assignment);
- // save changed assignment object on server
- Assignment.save(assignment).then(
- function(success) {
- if (gotoDetailView) {
- $state.go('assignments.assignment.detail', {id: success.id});
- }
- $scope.closeThisDialog();
- },
- function (error) {
- // save error: revert all changes by restore
- // (refresh) original assignment object from server
- Assignment.refresh(assignment);
- $scope.alert = ErrorMessage.forAlert(error);
- }
- );
- };
- }
-])
-
-.controller('AssignmentPollUpdateCtrl', [
- '$scope',
- '$filter',
- 'gettextCatalog',
- 'AssignmentPoll',
- 'assignmentpollId',
- 'AssignmentPollDecimalPlaces',
- 'ballot',
- 'ErrorMessage',
- function($scope, $filter, gettextCatalog, AssignmentPoll, assignmentpollId,
- AssignmentPollDecimalPlaces, ballot, ErrorMessage) {
- // set initial values for form model by create deep copy of assignmentpoll object
- // so detail view is not updated while editing poll
- var assignmentpoll = angular.copy(AssignmentPoll.get(assignmentpollId));
- $scope.model = assignmentpoll;
- $scope.ballot = ballot;
- $scope.formFields = [];
- $scope.alert = {};
-
- // For number inputs
- var step = Math.pow(10, -AssignmentPollDecimalPlaces.getPlaces(assignmentpoll));
-
- // add dynamic form fields
- var options = $filter('orderBy')(assignmentpoll.options, 'weight');
- _.forEach(options, function(option) {
- var defaultValue;
- if (assignmentpoll.pollmethod == 'yna' || assignmentpoll.pollmethod == 'yn') {
- defaultValue = {};
- _.forEach(option.votes, function (vote) {
- defaultValue[vote.value.toLowerCase()] = vote.weight;
- });
-
- var columnClass = (assignmentpoll.pollmethod === 'yna') ? 'col-xs-4' : 'col-xs-6';
- var columns = [
- {
- key: 'yes_' + option.candidate_id,
- type: 'input',
- className: columnClass + ' no-padding-left',
- templateOptions: {
- label: gettextCatalog.getString('Yes'),
- type: 'number',
- min: -2,
- step: step,
- required: true
- },
- defaultValue: defaultValue.yes
- },
- {
- key: 'no_' + option.candidate_id,
- type: 'input',
- className: columnClass + ' no-padding-left' +
- (assignmentpoll.pollmethod === 'yn' ? ' no-padding-right' : ''),
- templateOptions: {
- label: gettextCatalog.getString('No'),
- type: 'number',
- min: -2,
- step: step,
- required: true
- },
- defaultValue: defaultValue.no
- }
- ];
- if (assignmentpoll.pollmethod == 'yna'){
- columns.push({
- key:'abstain_' + option.candidate_id,
- type: 'input',
- className: columnClass + ' no-padding-left no-padding-right',
- templateOptions: {
- label: gettextCatalog.getString('Abstain'),
- type: 'number',
- min: -2,
- step: step,
- required: true
- },
- defaultValue: defaultValue.abstain
- });
- }
- $scope.formFields.push({
- noFormControl: true,
- template: '' + option.candidate.get_full_name() + ''
- },
- {
- className: 'row',
- fieldGroup: columns,
- });
- } else { // votes method
- if (option.votes.length) {
- defaultValue = option.votes[0].weight;
- }
- $scope.formFields.push({
- key: 'vote_' + option.candidate_id,
- type: 'input',
- templateOptions: {
- label: option.candidate.get_full_name(),
- type: 'number',
- min: -2,
- step: step,
- required: true,
- },
- defaultValue: defaultValue
- });
- }
- });
- if (assignmentpoll.pollmethod == 'votes'){
- $scope.formFields.push(
- {
- key: 'votesabstain',
- type: 'input',
- templateOptions: {
- label: gettextCatalog.getString('Abstain'),
- type: 'number',
- step: step,
- min: -2,
- }
- },
- {
- key: 'votesno',
- type: 'input',
- templateOptions: {
- label: gettextCatalog.getString('No'),
- type: 'number',
- step: step,
- min: -2,
- }
- }
- );
- }
- // add general form fields
- $scope.formFields.push(
- {
- template: '
',
- },
- {
- key: 'votesvalid',
- type: 'input',
- templateOptions: {
- label: gettextCatalog.getString('Valid ballots'),
- type: 'number',
- step: step,
- min: -2,
- }
- },
- {
- key: 'votesinvalid',
- type: 'input',
- templateOptions: {
- label: gettextCatalog.getString('Invalid ballots'),
- type: 'number',
- step: step,
- min: -2,
- }
- },
- {
- key: 'votescast',
- type: 'input',
- templateOptions: {
- label: gettextCatalog.getString('Casted ballots'),
- type: 'number',
- step: step,
- min: -2,
- }
- }
- );
-
- // save assignmentpoll
- $scope.save = function (poll) {
- var votes = [];
- if (assignmentpoll.pollmethod == 'yna') {
- assignmentpoll.options.forEach(function(option) {
- votes.push({
- 'Yes': poll['yes_' + option.candidate_id],
- 'No': poll['no_' + option.candidate_id],
- 'Abstain': poll['abstain_' + option.candidate_id]
- });
- });
- } else if (assignmentpoll.pollmethod == 'yn') {
- assignmentpoll.options.forEach(function(option) {
- votes.push({
- 'Yes': poll['yes_' + option.candidate_id],
- 'No': poll['no_' + option.candidate_id]
- });
- });
- } else {
- assignmentpoll.options.forEach(function(option) {
- votes.push({
- 'Votes': poll['vote_' + option.candidate_id],
- });
- });
- }
- // save change poll object on server
- poll.DSUpdate({
- assignment_id: poll.assignment_id,
- votes: votes,
- votesabstain: poll.votesabstain,
- votesno: poll.votesno,
- votesvalid: poll.votesvalid,
- votesinvalid: poll.votesinvalid,
- votescast: poll.votescast,
- })
- .then(function(success) {
- $scope.alert.show = false;
- $scope.closeThisDialog();
- }, function (error) {
- $scope.alert = ErrorMessage.forAlert(error);
- });
- };
- }
-])
-
-//mark all assignment config strings for translation with Javascript
-.config([
- 'gettext',
- function (gettext) {
- gettext('Election method');
- gettext('Automatic assign of method');
- gettext('Always one option per candidate');
- gettext('Always Yes-No-Abstain per candidate');
- gettext('Always Yes/No per candidate');
- gettext('Elections');
- gettext('Ballot and ballot papers');
- gettext('The 100-%-base of an election result consists of');
- gettext('For Yes/No/Abstain per candidate and Yes/No per candidate the 100-%-base ' +
- 'depends on the election method: If there is only one option per candidate, ' +
- 'the sum of all votes of all candidates is 100 %. Otherwise for each ' +
- 'candidate the sum of all votes is 100 %.');
- gettext('Yes/No/Abstain per candidate');
- gettext('Yes/No per candidate');
- gettext('All valid ballots');
- gettext('All casted ballots');
- gettext('Disabled (no percents)');
- gettext('Number of ballot papers (selection)');
- gettext('Number of all delegates');
- gettext('Number of all participants');
- gettext('Use the following custom number');
- gettext('Custom number of ballot papers');
- gettext('Required majority');
- gettext('Default method to check whether a candidate has reached the required majority.');
- gettext('Simple majority');
- gettext('Two-thirds majority');
- gettext('Three-quarters majority');
- gettext('Disabled');
- gettext('Put all candidates on the list of speakers');
- gettext('Title for PDF document (all elections)');
- gettext('Preamble text for PDF document (all elections)');
- //other translations
- gettext('Searching for candidates');
- gettext('Voting');
- gettext('Finished');
- }
-]);
-
-}());
diff --git a/openslides/assignments/static/templates/assignments/assignment-detail.html b/openslides/assignments/static/templates/assignments/assignment-detail.html
deleted file mode 100644
index d585d345e..000000000
--- a/openslides/assignments/static/templates/assignments/assignment-detail.html
+++ /dev/null
@@ -1,299 +0,0 @@
-{{ assignment.agenda_item.getTitle() || assignment.title }}
-
-
- Description
-
- Candidates
-
-
- Election result
-
-
-
- Election method
- Candidates
- Votes
- Quorum
-
-
-
-
-
-
-
-
-
-
- {{ option.candidate.get_full_name() }}
-
-
-
-
-
- Quorum ({{ (option.getVoteYes() - option.majorityReached) | number:votesPrecision }}) reached.
-
-
- Quorum ({{ (option.getVoteYes() - option.majorityReached) | number:votesPrecision }}) not reached.
-
-
-
-
-
-
- {{ poll.getVote('votesabstain').value | number:votesPrecision }}
- {{ poll.getVote('votesabstain').percentStr }}
-
-
-
- {{ poll.getVote('votesno').value | number:votesPrecision }}
- {{ poll.getVote('votesno').percentStr }}
-
-
-
- {{ poll.getVote('votesvalid').value | number:votesPrecision }}
- {{ poll.getVote('votesvalid').percentStr }}
-
-
-
- {{ poll.getVote('votesinvalid').value | number:votesPrecision }}
- {{ poll.getVote('votesinvalid').percentStr }}
-
-
-
- {{ poll.getVote('votescast').value | number:votesPrecision }}
- {{ poll.getVote('votescast').percentStr }}
-
- One vote per candidate
- Yes/No/Abstain per candidate
- Yes/No per candidate
- Edit election
-New election
-
-Elections
-
-
-
-
-
-{{ assignment.agenda_item.getTitle() || assignment.title }}
-
-
- Candidates
-
-
-
-
-
- Candidates
- Votes
-
-
-
-
-
-
-
- {{ option.candidate.get_full_name() }}
-
-
-
-
- {{ votes[1].label | translate }}: {{ votes[1].value | number:votesPrecision }} {{ votes[1].percentStr }}
- {{ votes[2].label | translate }}: {{ votes[2].value | number:votesPrecision }} {{ votes[2].percentStr }}
-
- {{ votes[0].label | translate }}: {{ votes[0].value | number:votesPrecision }} {{ votes[0].percentStr }}
- {{ votes[1].label | translate }}: {{ votes[1].value | number:votesPrecision }} {{ votes[1].percentStr }}
-
-
-
- {{ vote.value | number:votesPrecision }} {{ vote.percentStr }}
-
-
-
- {{ vote.value | number:votesPrecision }} {{ vote.percentStr }}
-
-
-
- {{ vote.value | number:votesPrecision }} {{ vote.percentStr }}
-
-
-
-
- {{ vote.value | number:votesPrecision }} {{ vote.percentStr }}
-
-
-
-
- {{ vote.value | number:votesPrecision }} {{ vote.percentStr }}
- Hello user!
+
+Use This url to get to the client. For further information see the DEVELOPMENT.rst
diff --git a/openslides/core/static/js/core/base.js b/openslides/core/static/js/core/base.js
deleted file mode 100644
index 4f1992ed5..000000000
--- a/openslides/core/static/js/core/base.js
+++ /dev/null
@@ -1,1723 +0,0 @@
-(function () {
-
-'use strict';
-
-// The core module used for the OpenSlides site and the projector
-angular.module('OpenSlidesApp.core', [
- 'js-data',
- 'gettext',
- 'ngAnimate',
- 'ngBootbox',
- 'ngSanitize', // TODO: only use this in functions that need it.
- 'ngStorage',
- 'ui.bootstrap',
- 'ui.bootstrap.datetimepicker',
- 'ui.tree',
- 'pdf',
- 'OpenSlidesApp-templates',
-])
-
-.config([
- 'DSProvider',
- 'DSHttpAdapterProvider',
- function(DSProvider, DSHttpAdapterProvider) {
- DSProvider.defaults.reapAction = 'none';
- DSProvider.defaults.basePath = '/rest';
- DSProvider.defaults.afterReap = function(model, items) {
- if (items.length > 5) {
- model.findAll({}, {bypassCache: true});
- } else {
- _.forEach(items, function (item) {
- model.refresh(item[model.idAttribute]);
- });
- }
- };
- DSHttpAdapterProvider.defaults.forceTrailingSlash = true;
- }
-])
-
-.factory('ProjectorID', [
- function () {
- return function () {
- return /projector\/(\d+)\//.exec(location.pathname)[1];
- };
- }
-])
-
-.config([
- '$sessionStorageProvider',
- function ($sessionStorageProvider) {
- $sessionStorageProvider.setKeyPrefix('OpenSlides');
- }
-])
-
-.factory('autoupdate', [
- 'DS',
- 'REALM',
- 'ProjectorID',
- '$q',
- '$timeout',
- 'ErrorMessage',
- function (DS, REALM, ProjectorID, $q, $timeout, ErrorMessage) {
- var socket = null;
- var retryConnectCallbacks = [];
-
- var websocketProtocol;
- if (location.protocol == 'https:') {
- websocketProtocol = 'wss:';
- } else {
- websocketProtocol = 'ws:';
- }
-
- var websocketPath;
- if (REALM === 'site') {
- websocketPath = '/ws/site/';
- } else if (REALM === 'projector') {
- websocketPath = '/ws/projector/' + ProjectorID() + '/';
- } else {
- console.error('The constant REALM is not set properly.');
- }
-
- // Get a random retry timeout between 2000 and 5000 ms.
- var getTimeoutTime = function () {
- return Math.floor(Math.random() * 3000 + 2000);
- };
-
- /* The callbacks are invoked if the ws connection closed and this factory tries to
- * reconnect after 1 second. The callbacks should return a promise. If the promise
- * resolves, the retry-process is stopped, so the callback can indicate whether it
- * has managed the reconnecting different.*/
- var runRetryConnectCallbacks = function () {
- var callbackPromises = _.map(retryConnectCallbacks, function (callback) {
- return callback();
- });
- $q.all(callbackPromises).then(function (success) {
- ErrorMessage.clearConnectionError();
- }, function (error) {
- $timeout(runRetryConnectCallbacks, getTimeoutTime());
- });
- };
-
- var Autoupdate = {};
- Autoupdate.messageReceivers = [];
- // We use later a promise to defer the first message of the established ws connection.
- Autoupdate.firstMessageDeferred = $q.defer();
- Autoupdate.onMessage = function (receiver) {
- Autoupdate.messageReceivers.push(receiver);
- };
- Autoupdate.newConnect = function () {
- socket = new WebSocket(websocketProtocol + '//' + location.host + websocketPath);
- // Make shure the servers state hasn't changed: Send a whoami request. If no users is logged and
- // anonymous are deactivated, reboot the client in fact that the server has lost all login information.
- socket.onclose = function (event) {
- socket = null;
- if (event.code !== 1000) { // 1000 is a normal close, like the close on logout
- ErrorMessage.setConnectionError();
- }
- $timeout(runRetryConnectCallbacks, getTimeoutTime());
- };
- socket.onmessage = function (event) {
- var data;
- try {
- data = JSON.parse(event.data);
- _.forEach(Autoupdate.messageReceivers, function (receiver) {
- receiver(data);
- });
- } catch(err) {
- console.error(err);
- }
- // Check if the promise is not resolved yet.
- if (Autoupdate.firstMessageDeferred.promise.$$state.status === 0) {
- Autoupdate.firstMessageDeferred.resolve();
- }
- ErrorMessage.clearConnectionError();
- };
- };
- Autoupdate.send = function (type, content) {
- if (!socket) {
- return;
- }
-
- var message = {
- type: type,
- content: content,
- id: '',
- };
-
- // Generate random id
- var possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
- for (var i = 0; i < 8; i++) {
- message.id += possible.charAt(Math.floor(Math.random() * possible.length));
- }
- socket.send(JSON.stringify(message));
- };
- Autoupdate.closeConnection = function () {
- if (socket) {
- socket.close();
- }
- Autoupdate.firstMessageDeferred = $q.defer();
- };
- Autoupdate.registerRetryConnectCallback = function (callback) {
- retryConnectCallbacks.push(callback);
- };
- return Autoupdate;
- }
-])
-
-.factory('operator', [
- 'User',
- 'Group',
- function (User, Group) {
- var setUserCallbacks = [];
- var operator = {
- user: null,
- perms: [],
- isAuthenticated: function () {
- return !!this.user;
- },
- setUser: function(user_id, user_data) {
- if (user_id && user_data) {
- operator.user = User.inject(user_data);
- } else {
- operator.user = null;
- }
- operator.reloadPerms();
- _.forEach(setUserCallbacks, function (cb) {
- cb(operator.user);
- });
- },
- // Returns true if the operator has at least one perm of the perms-list.
- hasPerms: function(perms) {
- if (typeof perms === 'string') {
- perms = perms.split(' ');
- }
- return _.intersection(perms, operator.perms).length > 0;
- },
- reloadPerms: function () {
- if (operator.user) {
- operator.perms = operator.user.getPerms();
- } else {
- var defaultGroup = Group.get(1);
- operator.perms = defaultGroup ? defaultGroup.permissions : [];
- }
- },
- // Returns true if the operator is a member of group.
- isInGroup: function(group) {
- var groups = operator.user.groups_id;
- if (groups.length === 0) {
- groups = [1]; // Set the default group, if no other groups are set.
- }
- return _.indexOf(groups, group.id) > -1;
- },
- registerSetUserCallback: function (cb) {
- setUserCallbacks.push(cb);
- },
- };
- return operator;
- }
-])
-
-// gets all in OpenSlides available languages
-.factory('Languages', [
- '$sessionStorage',
- '$ngBootbox',
- 'gettext',
- 'gettextCatalog',
- 'OpenSlidesPlugins',
- function ($sessionStorage, $ngBootbox, gettext, gettextCatalog, OpenSlidesPlugins) {
- return {
- // get all available languages
- getLanguages: function () {
- var current = $sessionStorage.language;
- // Define here new languages...
- var languages = [
- { code: 'en', name: 'English' },
- { code: 'de', name: 'Deutsch' },
- { code: 'fr', name: 'Français' },
- { code: 'es', name: 'Español' },
- { code: 'pt', name: 'Português' },
- { code: 'cs', name: 'Čeština'},
- { code: 'ru', name: 'русский'},
- ];
- angular.forEach(languages, function (language) {
- if (language.code == current)
- language.selected = true;
- });
- return languages;
- },
- // get detected browser language code
- getBrowserLanguage: function () {
- var lang = navigator.language || navigator.userLanguage;
- if (!navigator.language && !navigator.userLanguage) {
- lang = 'en';
- } else {
- if (lang.indexOf('-') !== -1)
- lang = lang.split('-')[0];
- if (lang.indexOf('_') !== -1)
- lang = lang.split('_')[0];
- }
- return lang;
- },
- // set current language and return updated languages object array
- setCurrentLanguage: function (lang) {
- var languages = this.getLanguages();
- var plugins = OpenSlidesPlugins.getAll();
- angular.forEach(languages, function (language) {
- language.selected = false;
- if (language.code == lang) {
- language.selected = true;
- $sessionStorage.language = lang;
- gettextCatalog.setCurrentLanguage(lang);
- // Plugins
- if (lang != 'en') {
- gettextCatalog.loadRemote("static/i18n/" + lang + ".json").then(function (success) {
- // translate ng-bootbox directives when the translations are available.
- $ngBootbox.addLocale(lang, {
- OK: gettextCatalog.getString('OK'),
- CANCEL: gettextCatalog.getString('Cancel'),
- CONFIRM: gettextCatalog.getString('OK'), // Yes, 'OK' is the original string.
- });
- $ngBootbox.setLocale(lang);
- });
- // load language files from plugins
- angular.forEach(plugins, function (plugin) {
- if (plugin.languages.indexOf(lang) != -1) {
- gettextCatalog.loadRemote("static/i18n/" + plugin.name + '/' + lang + ".json");
- }
- });
- }
- }
- });
- return languages;
- }
- };
- }
-])
-
-// Hook into gettextCatalog to include custom translations by wrapping
-// the getString method. The translations are stored in the config.
-.decorator('gettextCatalog', [
- '$delegate',
- '$rootScope',
- function ($delegate, $rootScope) {
- var oldGetString = $delegate.getString;
- var customTranslations = {};
-
- $delegate.getString = function () {
- var translated = oldGetString.apply($delegate, arguments);
- if (customTranslations[translated]) {
- translated = customTranslations[translated];
- }
- return translated;
- };
- $delegate.setCustomTranslations = function (translations) {
- customTranslations = translations;
- $rootScope.$broadcast('gettextLanguageChanged');
- };
-
- return $delegate;
- }
-])
-
-.run([
- '$rootScope',
- 'Config',
- 'gettextCatalog',
- function ($rootScope, Config, gettextCatalog) {
- $rootScope.$watch(function () {
- return Config.lastModified('translations');
- }, function () {
- var translations = Config.get('translations');
- if (translations) {
- var customTranslations = {};
- _.forEach(translations.value, function (entry) {
- customTranslations[entry.original] = entry.translation;
- });
- // Update all translate directives
- gettextCatalog.setCustomTranslations(customTranslations);
- }
- });
- }
-])
-
-// set browser language as default language for OpenSlides
-.run([
- '$sessionStorage',
- 'gettextCatalog',
- 'Languages',
- function($sessionStorage, gettextCatalog, Languages) {
- // set detected browser language as default language (fallback: 'en')
- if ($sessionStorage.language) {
- Languages.setCurrentLanguage($sessionStorage.language);
- } else {
- Languages.setCurrentLanguage(Languages.getBrowserLanguage());
- }
- // Set this to true for debug. Helps to find untranslated strings by
- // adding "[MISSING]:".
- gettextCatalog.debug = false;
- }
-])
-
-.factory('dsEject', [
- 'DS',
- function (DS) {
- return function (collection, instance) {
- var Resource = DS.definitions[collection];
- if (Resource.relationList) {
- Resource.relationList.forEach(function (relationDef) {
- if (relationDef.foreignKey && !relationDef.osProtectedRelation) {
- var query = {};
- query[relationDef.foreignKey] = instance[Resource.idAttribute];
- Resource.getResource(relationDef.relation).ejectAll(query);
- }
- });
- }
- };
- }
-])
-
-.run([
- 'DS',
- 'autoupdate',
- 'dsEject',
- function (DS, autoupdate, dsEject) {
- // Handler for normal autoupdate messages.
- autoupdate.onMessage(function(data) {
- if (data.type !== 'autoupdate') {
- return;
- }
-
- var dataList = data.content;
- var dataListByCollection = _.groupBy(dataList, 'collection');
- _.forEach(dataListByCollection, function (list, key) {
- var changedElements = [];
- var deletedElements = [];
- var collectionString = key;
- _.forEach(list, function (data) {
- // Uncomment this line for debugging to log all autoupdates:
- // console.log("Received object: " + data.collection + ", " + data.id);
-
- // remove (=eject) object from local DS store
- var instance = DS.get(data.collection, data.id);
- if (instance) {
- dsEject(data.collection, instance);
- }
- // check if object changed or deleted
- if (data.action === 'changed') {
- changedElements.push(data.data);
- } else if (data.action === 'deleted') {
- deletedElements.push(data.id);
- } else {
- console.error('Error: Undefined action for received object' +
- '(' + data.collection + ', ' + data.id + ')');
- }
- });
- // add (=inject) all given objects into local DS store
- if (changedElements.length > 0) {
- DS.inject(collectionString, changedElements);
- }
- // delete (=eject) all given objects from local DS store
- // (note: js-data does not provide 'bulk eject' as for DS.inject)
- _.forEach(deletedElements, function(id) {
- DS.eject(collectionString, id);
- });
- });
- });
- }
-])
-
-.factory('Notify', [
- 'autoupdate',
- 'operator',
- function (autoupdate, operator) {
- var anonymousTrackId;
-
- // Handler for notify messages.
- autoupdate.onMessage(function(data) {
- if (data.type !== 'notify') {
- return;
- }
-
- var dataList = data.content;
- var dataListByCollection = _.groupBy(dataList, 'collection');
- _.forEach(dataListByCollection.notify, function (notifyItem) {
- // Check, if this current user (or anonymous instance) has send this notify.
- if (notifyItem.senderUserId) {
- if (operator.user) { // User send to user
- notifyItem.sendBySelf = (notifyItem.senderUserId === operator.user.id);
- } else { // User send to anonymous
- notifyItem.sendBySelf = false;
- }
- } else {
- if (operator.user) { // Anonymous send to user
- notifyItem.sendBySelf = false;
- } else { // Anonymous send to anonymous
- notifyItem.sendBySelf = (notifyItem.anonymousTrackId === anonymousTrackId);
- }
- }
- // notify registered receivers.
- _.forEach(callbackReceivers[notifyItem.name], function (item) {
- item.fn(notifyItem);
- });
- });
- });
-
- var callbackReceivers = {};
- /* Structure of callbackReceivers:
- * event_name_one: [ {id:0, fn:fn}, {id:3, fn:fn} ],
- * event_name_two: [ {id:2, fn:fn} ],
- * */
- var idCounter = 0;
- var eventNameRegex = new RegExp('^[a-zA-Z0-9_-]+$');
- var externIdRegex = new RegExp('^[a-zA-Z0-9_-]+\/[0-9]+$');
- return {
- registerCallback: function (eventName, fn) {
- if (!eventNameRegex.test(eventName)) {
- throw 'eventName should only consist of [a-zA-Z0-9_-]';
- } else if (typeof fn === 'function') {
- var id = idCounter++;
-
- if (!callbackReceivers[eventName]) {
- callbackReceivers[eventName] = [];
- }
- callbackReceivers[eventName].push({
- id: id,
- fn: fn,
- });
- return eventName + '/' + id;
- } else {
- throw 'fn should be a function.';
- }
- },
- deregisterCallback: function (externId) {
- if (externIdRegex.test(externId)){
- var split = externId.split('/');
- var eventName = split[0];
- var id = parseInt(split[1]);
- callbackReceivers[eventName] = _.filter(callbackReceivers[eventName], function (item) {
- return item.id !== id;
- });
- } else {
- throw externId + ' is not a valid id';
- }
- },
- // variable length of parameters, just pass ids.
- deregisterCallbacks: function () {
- _.forEach(arguments, this.deregisterCallback);
- },
- notify: function(eventName, params, users, channels, projectors) {
- if (eventNameRegex.test(eventName)) {
- if (!params || typeof params !== 'object') {
- params = {};
- }
-
- var notifyItem = {
- collection: 'notify',
- name: eventName,
- params: params,
- users: users,
- replyChannels: channels,
- projectors: projectors,
- };
- if (!operator.user) {
- if (!anonymousTrackId) {
- anonymousTrackId = Math.floor(Math.random()*1000000);
- }
- notifyItem.anonymousTrackId = anonymousTrackId;
- }
- autoupdate.send('notify', [notifyItem]);
- } else {
- throw 'eventName should only consist of [a-zA-Z0-9_-]';
- }
- },
- };
- }
-])
-
-.run([
- 'autoupdate',
- function (autoupdate) {
- // Handler for normal autoupdate messages.
- autoupdate.onMessage(function (data) {
- if (data.type === 'error') {
- console.error("Websocket error", data.content);
- }
- });
- }
-])
-
-// Save the server time to the rootscope.
-.run([
- '$http',
- '$rootScope',
- function ($http, $rootScope) {
- // Loads server time and calculates server offset
- $rootScope.serverOffset = 0;
- $http.get('/core/servertime/')
- .then(function(data) {
- $rootScope.serverOffset = Math.floor(Date.now() / 1000 - data.data);
- });
- }
-])
-
-.run([
- 'Config',
- '$rootScope',
- function (Config, $rootScope) {
- $rootScope.config = function (key) {
- try {
- return Config.get(key).value;
- }
- catch(err) {
- return '';
- }
- };
- }
-])
-
-// Make the indexOf available in every scope; needed for the projectorbuttons
-.run([
- '$rootScope',
- function ($rootScope) {
- $rootScope.inArray = function (array, value) {
- return _.indexOf(array, value) > -1;
- };
- }
-])
-
-// Put the Math object into every scope.
-.run([
- '$rootScope',
- function ($rootScope) {
- $rootScope.Math = window.Math;
- }
-])
-
-// Template hooks
-// Possible uses:
-// 1. { id: 'myHookId', template: '' }
-// 2. { id: 'myHookId', templateUrl: '/static/templates/plugin_name/my-hook.html' }
-// 3. { id: 'myHookId' }
-//
-// Deprecated: Give the id with 'Id'. Please use 'id'.
-//
-// Option 3 is for just changing the scope (see below), but not the original content. This
-// is usefull to alter a JS behavior, e.g. on a ng-click. In this case, override is false
-// for this template hook.
-//
-// It is possible to provide a scope, that is merged into the surrounding scope.
-// You can override functions or values of the surrounding scope by providing them:
-// { id: 'hookId', template: '',
-// scope: {
-// customOrOverwritten: function () { /*Do something */ },
-// },
-// }
-// Or you provide a function that returns an object of functions/values to overwrite to
-// get access to the scope merged in:
-// { id: 'hookId', template: '',
-// scope: function (scope) {
-// return {
-// customOrOverwritten: function () {
-// scope.value = /* change it */;
-// },
-// };
-// },
-// }
-//
-// As a default, template hooks in flavour of option 1 and 2 override the content that was
-// originally there. Provide 'override: false', to prevent overriding the original content.
-.factory('templateHooks', [
- function () {
- var hooks = {};
- return {
- hooks: hooks,
- registerHook: function (hook) {
- // Deprecated: Set the new style 'id', if 'Id' is given.
- if (hook.id === void 0) {
- hook.id = hook.Id;
- }
-
- if (hooks[hook.id] === void 0) {
- hooks[hook.id] = [];
- }
- // set override default
- if (hook.override === void 0) {
- hook.override = !!(hook.template || hook.templateUrl);
- }
- hooks[hook.id].push(hook);
- }
- };
- }
-])
-
-.directive('templateHook', [
- '$compile',
- '$http',
- '$q',
- '$templateCache',
- '$timeout',
- 'templateHooks',
- function ($compile, $http, $q, $templateCache, $timeout, templateHooks) {
- return {
- restrict: 'E',
- template: '',
- link: function (scope, iElement, iAttr) {
- var hooks = templateHooks.hooks[iAttr.hookName];
- if (hooks) {
- // Populate scopes
- _.forEach(hooks, function (hook) {
- var _scope = hook.scope;
- // If it is a function, get the scope from the function and provide
- // the original scope.
- if (typeof hook.scope === 'function') {
- _scope = hook.scope(scope);
- }
-
- _.forEach(_scope, function (value, key) {
- scope[key] = value;
- });
- });
-
- // Check, if at least one hook overrides the original content.
- var override = _.some(hooks, function (hook) {
- return hook.override;
- });
-
- // filter hooks, that does actually have a template
- hooks = _.filter(hooks, function (hook) {
- return hook.template || hook.templateUrl;
- });
-
- // Get all templates
- var templates = _.map(hooks, function (hook) {
- // Either a template (html given as string) or a templateUrl has
- // to be given. If a scope is provided, the schope of this templateHook
- // is populated with the given functions/values.
- if (hook.template) {
- return hook.template;
- } else {
- return $templateCache.get(hook.templateUrl);
- }
- });
-
- // Wait for the dom to build up, so we can retrieve the inner html of iElement.
- $timeout(function () {
- var html = override ? '' : iElement.html();
- if (templates.length) {
- html += templates.join('');
- }
-
- iElement.empty();
- iElement.append($compile(html)(scope));
- });
- }
- }
- };
- }
-])
-
-/*
- * This places a projector button in the document.
- *
- * Example:
or
into the
- // editor import processing (same as at the begin of the function: by ckeditor)
- evt.data.dataValue = writer.getHtml();
- }
- });
- }
- },
- customConfig: '',
- floatSpaceDockedOffsetY: _.indexOf(arguments, 'YOffset') > -1 ? 35 : 0,
- disableNativeSpellChecker: false,
- language_list: [
- 'fr:français',
- 'es:español',
- 'pt:português',
- 'en:english',
- 'de:deutsch',
- 'cs:čeština'],
- language: gettextCatalog.getCurrentLanguage(),
- allowedContent:
- 'h1 h2 h3 b i u strike sup sub strong em;' +
- 'blockquote p pre table' +
- '(text-align-left,text-align-center,text-align-right,text-align-justify,os-split-before,os-split-after){text-align, float, padding};' +
- 'a[!href];' +
- 'img[!src,alt]{width,height,float, padding};' +
- 'tr th td caption;' +
- 'li(os-split-before,os-split-after); ol(os-split-before,os-split-after)[start]{list-style-type};' +
- 'ul(os-split-before,os-split-after){list-style};' +
- 'span[!*]{color,background-color}(os-split-before,os-split-after,os-line-number,line-number-*);' +
- 'br(os-line-break);',
-
- // there seems to be an error in CKeditor that parses spaces in extraPlugins as part of the plugin name.
- extraPlugins: extraPluginsString,
- removePlugins: 'wsc,scayt,a11yhelp,filebrowser,sourcearea,liststyle,tabletools,tableselection,contextmenu,image',
- removeButtons: 'Scayt,Anchor,Styles,HorizontalRule',
- };
- if (_.indexOf(arguments, 'inline') > -1) {
- options.toolbarGroups = [
- { name: 'basicstyles', groups: [ 'basicstyles', 'cleanup' ] },
- { name: 'colors', groups: [ 'colors' ] },
- { name: 'paragraph', groups: [ 'list'] },
- { name: 'links', groups: [ 'links' ] },
- { name: 'clipboard', groups: [ 'undo' ] },
- { name: 'document', groups: [ 'mode' ] },
- ];
- options.removeButtons = 'Underline,Subscript,Superscript,PasteFromWord,PasteText,Scayt,Link,Unlink,Anchor,HorizontalRule,Table,Image,Maximize,Source,Format,About,Paste,Cut,Copy';
- } else {
- options.toolbarGroups = [
- { name: 'clipboard', groups: [ 'clipboard', 'undo' ] },
- { name: 'editing', groups: [ 'find', 'selection', 'spellchecker', 'editing' ] },
- { name: 'links', groups: [ 'links' ] },
- { name: 'insert', groups: [ 'insert' ] },
- { name: 'tools', groups: [ 'tools' ] },
- { name: 'document', groups: [ 'mode' ] },
- '/',
- { name: 'styles', groups: [ 'styles' ] },
- { name: 'basicstyles', groups: [ 'basicstyles', 'cleanup' ] },
- { name: 'colors', groups: [ 'colors' ] },
- { name: 'paragraph', groups: [ 'list', 'indent' ] },
- { name: 'align'},
- { name: 'paragraph', groups: [ 'blocks' ] }
- ];
- }
- return options;
- }
- };
- }
-])
-
-/* Model for a projector.
- *
- * At the moment we use only one projector, so there will be only one object
- * in this model. It has the id 1. For later releases there will be multiple
- * projector objects.
- *
- * This model uses onConfilict: 'replace' instead of 'merge'. This is necessary
- * because the keys of the projector objects can change and old keys have to
- * be removed. See http://www.js-data.io/docs/dsdefaults#onconflict for
- * more information.
- */
-.factory('Projector', [
- 'DS',
- '$http',
- 'EditForm',
- 'Config',
- function(DS, $http, EditForm, Config) {
- return DS.defineResource({
- name: 'core/projector',
- onConflict: 'replace',
- relations: {
- hasMany: {
- 'core/projection-default': {
- localField: 'projectiondefaults',
- foreignKey: 'projector_id',
- }
- },
- },
- methods: {
- controlProjector: function(action, direction) {
- $http.post('/rest/core/projector/' + this.id + '/control_view/',
- {"action": action, "direction": direction}
- );
- },
- getFormOrStateForCurrentSlide: function () {
- var return_dict;
- angular.forEach(this.elements, function(value, key) {
- if (value.name == 'agenda/list-of-speakers') {
- return_dict = {
- state: 'agenda.item.detail',
- id: value.id,
- };
- } else if (
- // TODO:
- // Find generic solution for whitelist in getFormOrStateForCurrentSlide
- // see https://github.com/OpenSlides/OpenSlides/issues/3130
- value.name === 'topics/topic' ||
- value.name === 'motions/motion' ||
- value.name === 'motions/motion-block' ||
- value.name === 'assignments/assignment' ||
- value.name === 'mediafiles/mediafile' ||
- value.name === 'users/user') {
- return_dict = {
- form: EditForm.fromCollectionString(value.name),
- id: value.id,
- };
- }
- });
- return return_dict;
- },
- toggleBlank: function () {
- $http.post('/rest/core/projector/' + this.id + '/control_blank/',
- !this.blank
- );
- },
- toggleBroadcast: function () {
- $http.post('/rest/core/projector/' + this.id + '/broadcast/');
- }
- },
- });
- }
-])
-
-// This factory sends a request to /rest/core/projectors/project
-// with the given data. Also it does the changes done by the server
-// locally and may reverts them, if something went wrong.
-.factory('ProjectHelper', [
- '$q',
- '$http',
- 'Projector',
- function ($q, $http, Projector) {
- var uuid4 = function () {
- function s8() {
- return Math.floor((1 + Math.random()) * 0x100000000)
- .toString(16)
- .substring(1);
- }
- return s8() + s8() + s8() + s8();
- };
-
- return {
- project: function (data) {
- var projector;
- // get all projectors that will be changed.
- var projectorsChanged = _.filter(_.map(data.clear_ids, function (id) {
- return Projector.get(id);
- }));
- if (data.prune && !_.includes(data.clear_ids, data.prune.id)) {
- projector = Projector.get(data.prune.id);
- if (projector) {
- projectorsChanged.push(projector);
- }
- }
-
- // copy original projectors in case we have to reconstruct those
- // _.cloneDeep and angular.clone does not work here; I'm not
- // exactly sure why..
- var originalProjectors = _.map(projectorsChanged, function (projector) {
- var elements = {};
- _.forEach(projector.elements, function (element, key) {
- elements[key] = _.cloneDeep(element);
- });
- return {
- id: projector.id,
- elements: elements,
- scroll: projector.scroll,
- scale: projector.scale,
- name: projector.name,
- blank: projector.blank,
- width: projector.width,
- height: projector.height,
- projectiondefaults: _.cloneDeep(projector.projectiondefaults),
- };
- });
-
- // Clear every projector
- _.forEach(projectorsChanged, function (projector) {
- var elements = {};
- _.forEach(projector.elements, function (element, key) {
- if (element.stable) {
- elements[key] = element;
- }
- });
- projector.elements = elements;
- });
-
- // Add the prune element if given
- if (data.prune) {
- projector = _.find(projectorsChanged, function (projector) {
- return projector.id === data.prune.id;
- });
- if (projector) {
- projector.scroll = 0;
- projector.elements[uuid4()] = data.prune.element;
- }
- }
-
- Projector.inject(projectorsChanged);
-
- return $http.post('/rest/core/projector/project/', data).catch(
- function (error) {
- // revert the changes made earlier
- Projector.inject(originalProjectors);
- return $q.reject(error);
- }
- );
- },
- };
- }
-])
-
-/* Model for all projection defaults */
-.factory('ProjectionDefault', [
- 'DS',
- function(DS) {
- return DS.defineResource({
- name: 'core/projection-default',
- relations: {
- belongsTo: {
- 'core/projector': {
- localField: 'projector',
- localKey: 'projector_id',
- }
- }
- }
- });
- }
-])
-
-/* Model for ProjectorMessages */
-.factory('ProjectorMessage', [
- 'DS',
- 'jsDataModel',
- 'gettext',
- '$http',
- 'Projector',
- function(DS, jsDataModel, gettext, $http, Projector) {
- var name = 'core/projector-message';
- return DS.defineResource({
- name: name,
- useClass: jsDataModel,
- verboseName: gettext('Message'),
- verbosenamePlural: gettext('Messages'),
- methods: {
- getResourceName: function () {
- return name;
- },
- // Override the BaseModel.project function
- project: function(projectorId) {
- // if this object is already projected on projectorId, delete this element from this projector
- var isProjectedIds = this.isProjected();
- var self = this;
- var predicate = function (element) {
- return element.name === name && element.id === self.id;
- };
- _.forEach(isProjectedIds, function (id) {
- var uuid = _.findKey(Projector.get(id).elements, predicate);
- $http.post('/rest/core/projector/' + id + '/deactivate_elements/', [uuid]);
- });
- // if it was the same projector before, just delete it but not show again
- if (_.indexOf(isProjectedIds, projectorId) == -1) {
- // Now check whether other messages are already projected and delete them
- var elements = Projector.get(projectorId).elements;
- _.forEach(elements, function (element, uuid) {
- if (element.name === name) {
- $http.post('/rest/core/projector/' + projectorId + '/deactivate_elements/', [uuid]);
- }
- });
- return $http.post(
- '/rest/core/projector/' + projectorId + '/activate_elements/',
- [{name: name, id: self.id, stable: true}]
- );
- }
- },
- }
- });
- }
-])
-
-/* Model for Countdowns */
-.factory('Countdown', [
- 'DS',
- 'jsDataModel',
- 'gettext',
- '$rootScope',
- '$http',
- 'Projector',
- function(DS, jsDataModel, gettext, $rootScope, $http, Projector) {
- var name = 'core/countdown';
- return DS.defineResource({
- name: name,
- useClass: jsDataModel,
- verboseName: gettext('Countdown'),
- verbosenamePlural: gettext('Countdowns'),
- methods: {
- getResourceName: function () {
- return name;
- },
- start: function () {
- // calculate end point of countdown (in seconds!)
- var endTimestamp = Date.now() / 1000 - $rootScope.serverOffset + this.countdown_time;
- this.running = true;
- this.countdown_time = endTimestamp;
- DS.save(name, this.id);
- },
- stop: function () {
- // calculate rest duration of countdown (in seconds!)
- var newDuration = Math.floor( this.countdown_time - Date.now() / 1000 + $rootScope.serverOffset );
- this.running = false;
- this.countdown_time = newDuration;
- DS.save(name, this.id);
- },
- reset: function () {
- this.running = false;
- this.countdown_time = this.default_time;
- DS.save(name, this.id);
- },
- // Override the BaseModel.project function
- project: function(projectorId) {
- // if this object is already projected on projectorId, delete this element from this projector
- var isProjectedIds = this.isProjected();
- var self = this;
- var predicate = function (element) {
- return element.name == name && element.id == self.id;
- };
- _.forEach(isProjectedIds, function (id) {
- var uuid = _.findKey(Projector.get(id).elements, predicate);
- $http.post('/rest/core/projector/' + id + '/deactivate_elements/', [uuid]);
- });
- // if it was the same projector before, just delete it but not show again
- if (_.indexOf(isProjectedIds, projectorId) == -1) {
- return $http.post(
- '/rest/core/projector/' + projectorId + '/activate_elements/',
- [{name: name, id: self.id, stable: true}]
- );
- }
- },
- },
- });
- }
-])
-
-/* Two functions to convert between time duration in seconds <-> human readable time span.
- * E.g. 90 sec <-> 1:30 (min), 3661 sec <-> 1:01:01 (h)
- *
- * secondsToHumanTime: Expects seconds and give [h*:]mm[:ss]. The minutes part is always given, the hours
- * and minutes could be controlled. The default are forced seconds and hours just if it is not 0.
- * - seconds ('enabled', 'auto', 'disabled'): Whether to show seconds (Default 'enabled')
- * - hours ('enabled', 'auto', 'disabled'): Whether to show hours (Default 'auto')
- *
- * humanTimeToSeconds: Expects [h*:]m*[:s*] with each part could have a variable length. The parsed time is
- * in seconds. Minutes have to be given and hours and seconds are optional. One have to set 'seconds' or
- * 'hours' to true toparse these.
- *
- * params could be an object with the given settings, e.g. {ignoreHours: true}
- */
-.factory('HumanTimeConverter', [
- function () {
- return {
- secondsToHumanTime: function (seconds, params) {
- if (!params) {
- params = {seconds: 'enabled', hours: 'auto'};
- }
- if (!params.seconds) {
- params.seconds = 'enabled';
-
- }
- if (!params.hours) {
- params.hours = 'auto';
- }
- var time;
- // floor returns the largest integer of the absolut value of seconds
- var total = Math.floor(Math.abs(seconds));
- var h = Math.floor(total / 3600);
- var m = Math.floor(total % 3600 / 60);
- var s = Math.floor(total % 60);
- // Add leading "0" for double digit values
- time = ('0'+m).slice(-2); //minutes
- if ((params.seconds == 'auto' && s > 0) || params.seconds == 'enabled') {
- s = ('0'+s).slice(-2);
- time = time + ':' + s;
- }
- if ((params.hours == 'auto' && h > 0) || params.hours == 'enabled') {
- time = h + ':' + time;
- }
- if (seconds < 0) {
- time = '-'+time;
- }
- return time;
- },
- humanTimeToSeconds: function (data, params) {
- if (!params) {
- params = {seconds: false, hours: false};
- }
- var minLength = 1;
- if (params.seconds) {
- minLength++;
- }
- if (params.hours){
- minLength++;
- }
-
- var negative = data.charAt(0) == '-';
- var time = data.split(':');
- data = 0;
- if (time.length >= minLength) {
- for (var i = 0; i < minLength; i++) {
- data = data*60;
- if (!isNaN(+time[i])) {
- data += (+time[i]);
- }
- }
- if (!params.seconds) { // the last field was minutes (e.g. h:mm)
- data *= 60;
- }
- if (negative) {
- data = -data;
- }
- }
- return data;
- },
- };
- }
-])
-
-/* Converts a snake-case string to camelCase. Example:
- * 'motion-block-config' -> 'motionBlockConfig' */
-.factory('CamelCase', [
- function () {
- return function (str) {
- return str.replace(/-([a-z])/g, function (match) {
- return match[1].toUpperCase();
- });
- };
- }
-])
-
-/* Return the specific EditForm for a given model. */
-.factory('EditForm', [
- '$injector',
- 'CamelCase',
- function ($injector, CamelCase) {
- return {
- fromCollectionString: function (collection) {
- var modelName = CamelCase(collection).split('/')[1];
- // Convert modelModel to ModelModelForm
- var formName = modelName.charAt(0).toUpperCase() + modelName.slice(1) + 'Form';
- return $injector.get(formName);
- },
- };
- }
-])
-
-/* Converts number of seconds into string "h:mm:ss" or "mm:ss" */
-.filter('osSecondsToTime', [
- 'HumanTimeConverter',
- function (HumanTimeConverter) {
- return function (seconds) {
- return HumanTimeConverter.secondsToHumanTime(seconds);
- };
- }
-])
-
-/* Converts number of minutes into string "h:mm" or "hh:mm" */
-.filter('osMinutesToTime', [
- 'HumanTimeConverter',
- function (HumanTimeConverter) {
- return function (minutes) {
- return HumanTimeConverter.secondsToHumanTime(minutes*60,
- { seconds: 'disabled',
- hours: 'enabled' }
- );
- };
- }
-])
-
-// mark HTML as "trusted"
-.filter('trusted', [
- '$sce',
- function ($sce) {
- return function(text) {
- return $sce.trustAsHtml(text);
- };
- }
-])
-
-// filters the requesting object (id=selfid) from a list of input objects
-.filter('notself', function () {
- return function (input, selfid) {
- var result;
- if (selfid) {
- result = [];
- for (var key in input){
- var obj = input[key];
- if (selfid != obj.id) {
- result.push(obj);
- }
- }
- } else {
- result = input;
- }
- return result;
- };
-})
-
-// Wraps the orderBy filter. But puts ("", null, undefined) last.
-.filter('orderByEmptyLast', [
- '$filter',
- '$parse',
- function ($filter, $parse) {
- return function (array, sortPredicate, reverseOrder, compareFn) {
- var parsed = $parse(sortPredicate);
- var falsyItems = [];
- var truthyItems = _.filter(array, function (item) {
- var falsy = parsed(item) === void 0 || parsed(item) === null || parsed(item) === '';
- if (falsy) {
- falsyItems.push(item);
- }
- return !falsy;
- });
- truthyItems = $filter('orderBy')(truthyItems, sortPredicate, reverseOrder, compareFn);
- return _.concat(truthyItems, falsyItems);
- };
- }
-])
-
-// Make sure that the DS factories are loaded by making them a dependency
-.run([
- 'ChatMessage',
- 'Config',
- 'Countdown',
- 'ProjectorMessage',
- 'Projector',
- 'ProjectionDefault',
- 'Tag',
- 'Notify', // For setting up the autoupdate callback
- function (ChatMessage, Config, Countdown, ProjectorMessage, Projector, ProjectionDefault, Tag, Notify) {}
-]);
-
-}());
diff --git a/openslides/core/static/js/core/csv.js b/openslides/core/static/js/core/csv.js
deleted file mode 100644
index 8361bbf7e..000000000
--- a/openslides/core/static/js/core/csv.js
+++ /dev/null
@@ -1,23 +0,0 @@
-(function () {
-
-'use strict';
-
-angular.module('OpenSlidesApp.core.csv', [])
-
-.factory('CsvDownload', [
- 'Config',
- 'FileSaver',
- function (Config, FileSaver) {
- var utf8_BOM = decodeURIComponent('%EF%BB%BF');
- return function (contentRows, filename) {
- var separator = Config.get('general_csv_separator').value;
- var rows = _.map(contentRows, function (row) {
- return row.join(separator);
- });
- var blob = new Blob([utf8_BOM + rows.join('\n')]);
- FileSaver.saveAs(blob, filename);
- };
- }
-]);
-
-}());
diff --git a/openslides/core/static/js/core/docx.js b/openslides/core/static/js/core/docx.js
deleted file mode 100644
index 21c0d098c..000000000
--- a/openslides/core/static/js/core/docx.js
+++ /dev/null
@@ -1,356 +0,0 @@
-(function () {
-
-'use strict';
-
-angular.module('OpenSlidesApp.core.docx', [])
-
-.factory('Html2DocxConverter', [
- '$q',
- 'ImageConverter',
- function ($q, ImageConverter) {
- var PAGEBREAK = '' + gettextCatalog.getString('Cannot load image') + ' ' + url + '
',
- });
- };
- img.onload = function () {
- var canvas = document.createElement('canvas');
- canvas.width = img.width;
- canvas.height = img.height;
- var ctx = canvas.getContext('2d');
- ctx.drawImage(img, 0, 0);
- var dataURL = canvas.toDataURL('image/png');
- var imageData = {
- data: dataURL,
- width: img.width,
- height: img.height
- };
- resolve(imageData);
- };
- img.src = url;
- });
- return promise;
- };
-
- return PDFLayout;
- }
-])
-
-
-.factory('HTMLValidizer', function() {
- var HTMLValidizer = {};
-
- // In some cases copying from word to OpenSlides results in umlauts
- // that are the base letter and then the entity #776; to make the dots
- // above the base letter. This breaks the PDF.
- HTMLValidizer.replaceMalformedUmlauts = function (text) {
- return text.replace(/([aeiouAEIOUy])[\u0308]/g, function (match, baseChar) {
- return '&' + baseChar + 'uml;';
- });
- };
-
-
- //checks if str is valid HTML. Returns valid HTML if not,
- //return emptystring if empty
- HTMLValidizer.validize = function(str) {
- if (str) {
- str = HTMLValidizer.replaceMalformedUmlauts(str);
- // Sometimes, some \n are in the text instead of whitespaces. Replace them.
- str = str.replace(/\n/g, ' ');
-
- var a = document.createElement('div');
- a.innerHTML = str;
- angular.forEach(a.childNodes, function (child) {
- if (child.nodeType == 1) {
- return str;
- }
- });
- return '' + error + '
';
- break;
- }
- $timeout(function () {
- filenameMessageMap[filename] = Messaging.createOrEditMessage(
- filenameMessageMap[filename], text, state, {timeout: timeout});
- }, 1);
- };
- return {
- getBase64FromDocument: function (documentProvider) {
- return $q(function (resolve, reject) {
- PdfVfs.get(documentProvider.getImageMap()).then(function (vfs) {
- var pdfWorker = new Worker('/static/js/workers/pdf-worker.js');
- pdfWorker.addEventListener('message', function (event) {
- resolve(event.data);
- });
- pdfWorker.addEventListener('error', function (event) {
- reject(event);
- });
- pdfWorker.postMessage(JSON.stringify({
- pdfDocument: documentProvider.getDocument(),
- vfs: vfs,
- }));
- });
- });
- },
- // Struckture of pdfDocuments: { filname1: doc, filename2: doc, ...}
- getBase64FromMultipleDocuments: function (pdfDocuments) {
- // concat all image sources together
- var imageMap = {};
- _.forEach(pdfDocuments, function (doc) {
- _.forEach(doc.getImageMap(), function (data, path) {
- if (!imageMap[path]) {
- imageMap[path] = data;
- }
- });
- });
- return $q(function (resolve, reject) {
- PdfVfs.get(imageMap).then(function (vfs) {
- var pdfWorker = new Worker('/static/js/workers/pdf-worker.js');
- var resultCount = 0;
- var base64Map = {}; // Maps filename to base64
- pdfWorker.addEventListener('message', function (event) {
- resultCount++;
- var data = JSON.parse(event.data);
- base64Map[data.filename] = data.base64;
- if (resultCount === _.keys(pdfDocuments).length) {
- resolve(base64Map);
- }
- });
- pdfWorker.addEventListener('error', function (event) {
- reject(event);
- });
- _.forEach(pdfDocuments, function (doc, filename) {
- pdfWorker.postMessage(JSON.stringify({
- filename: filename,
- pdfDocument: doc.getDocument(),
- vfs: vfs,
- }));
- });
- });
- });
- },
- download: function (documentProvider, filename) {
- stateChange('info', filename);
-
- this.getBase64FromDocument(documentProvider).then(function (data) {
- var blob = b64toBlob(data);
- stateChange('success', filename);
- FileSaver.saveAs(blob, filename);
- }, function (error) {
- stateChange('error', filename, error.message);
- });
- },
- };
- }
-]);
-
-}());
diff --git a/openslides/core/static/js/core/projector.js b/openslides/core/static/js/core/projector.js
deleted file mode 100644
index 4dd98042b..000000000
--- a/openslides/core/static/js/core/projector.js
+++ /dev/null
@@ -1,409 +0,0 @@
-(function () {
-
-'use strict';
-
-// The core module for the OpenSlides projector
-angular.module('OpenSlidesApp.core.projector', ['OpenSlidesApp.core'])
-
-// Can be used to find out if the projector or the side is used
-.constant('REALM', 'projector')
-
-.run([
- '$http',
- 'autoupdate',
- 'DS',
- function ($http, autoupdate, DS) {
- autoupdate.newConnect();
-
- // If the connection aborts, we try to ping the server with whoami requests. If
- // the server is flushed, we clear the datastore, so the message 'this projector
- // cannot be shown' will be displayed. Otherwise establish the websocket connection.
- autoupdate.registerRetryConnectCallback(function () {
- return $http.get('/users/whoami').then(function (success) {
- if (success.data.user_id === null && !success.data.guest_enabled) {
- DS.clear();
- } else {
- autoupdate.newConnect();
- }
- });
- });
- }
-])
-
-// Provider to register slides in a .config() statement.
-.provider('slides', [
- function() {
- var slidesMap = {};
-
- this.registerSlide = function(name, config) {
- slidesMap[name] = config;
- return this;
- };
-
- this.$get = function($templateRequest, $q) {
- var self = this;
- return {
- getElements: function(projector) {
- var elements = [];
- var factory = this;
- _.forEach(projector.elements, function(element) {
- if (element.name in slidesMap) {
- element.template = slidesMap[element.name].template;
- elements.push(element);
- } else {
- console.error("Unknown slide: " + element.name);
- }
- });
- return elements;
- }
- };
- };
- }
-])
-
-.config([
- 'slidesProvider',
- function(slidesProvider) {
- slidesProvider.registerSlide('core/clock', {
- template: 'static/templates/core/slide_clock.html',
- });
-
- slidesProvider.registerSlide('core/countdown', {
- template: 'static/templates/core/slide_countdown.html',
- });
-
- slidesProvider.registerSlide('core/projector-message', {
- template: 'static/templates/core/slide_message.html',
- });
- }
-])
-
-.controller('LanguageAndFontCtrl', [
- '$scope',
- 'Languages',
- 'Config',
- 'Projector',
- 'ProjectorID',
- 'Fonts',
- function ($scope, Languages, Config, Projector, ProjectorID, Fonts) {
- // for the dynamic title
- $scope.projectorId = ProjectorID();
- $scope.$watch(function () {
- return Projector.lastModified($scope.projectorId);
- }, function () {
- var projector = Projector.get($scope.projectorId);
- if (projector) {
- $scope.projectorName = projector.name;
- }
- });
-
- $scope.$watch(function () {
- return Config.lastModified('projector_language');
- }, function () {
- var lang = Config.get('projector_language');
- if (!lang || lang.value == 'browser') {
- $scope.selectedLanguage = Languages.getBrowserLanguage();
- } else {
- $scope.selectedLanguage = lang.value;
- }
- Languages.setCurrentLanguage($scope.selectedLanguage);
- });
-
- $scope.$watch(function () {
- return Config.lastModified('font_regular') +
- Config.lastModified('font_italic') +
- Config.lastModified('font_bold') +
- Config.lastModified('font_bold_italic');
- }, function () {
- $scope.font = Fonts.getForCss('font_regular');
- $scope.font_medium = Fonts.getForCss('font_italic');
- $scope.font_condensed = Fonts.getForCss('font_bold');
- $scope.font_condensed_light = Fonts.getForCss('font_bold_italic');
- });
- }
-])
-
-// Projector Container Controller
-.controller('ProjectorContainerCtrl', [
- '$scope',
- '$timeout',
- '$location',
- 'gettext',
- 'Projector',
- function($scope, $timeout, $location, gettext, Projector) {
- $scope.showError = true;
-
- // watch for changes in Projector
- $scope.$watch(function () {
- return Projector.lastModified($scope.projectorId);
- }, function () {
- var projector = Projector.get($scope.projectorId);
- if (projector) {
- $scope.showError = false;
- $scope.projectorWidth = projector.width;
- $scope.projectorHeight = projector.height;
- $scope.recalculateIframe();
- } else {
- $scope.showError = true;
- // delay displaying the error message, because with a slow internet
- // connection, the autoupdate with the projector may be delayed. We
- // de not want to irritate the user by showing this error to early.
- $scope.error = '';
- $timeout(function () {
- if ($scope.showError) {
- $scope.error = gettext('Can not open the projector.');
- }
- }, 3000);
- }
- });
-
- // recalculate the actual Iframesize and scale
- $scope.recalculateIframe = function () {
- var scale_width = window.innerWidth / $scope.projectorWidth;
- var scale_height = window.innerHeight / $scope.projectorHeight;
-
- // Iframe has to be scaled down or saceUp is activated
- if (scale_width <= scale_height) {
- // width is the reference
- $scope.iframeWidth = window.innerWidth;
- $scope.scale = scale_width;
- $scope.iframeHeight = $scope.projectorHeight * scale_width;
- } else {
- // height is the reference
- $scope.iframeHeight = window.innerHeight;
- $scope.scale = scale_height;
- $scope.iframeWidth = $scope.projectorWidth * scale_height;
- }
- };
-
- // watch for changes in the windowsize
- $(window).on("resize.doResize", function () {
- $scope.$apply(function() {
- $scope.recalculateIframe();
- });
- });
-
- $scope.$on("$destroy",function (){
- $(window).off("resize.doResize");
- });
- }
-])
-
-.controller('ProjectorCtrl', [
- '$scope',
- '$location',
- '$timeout',
- 'Projector',
- 'slides',
- 'Config',
- 'ProjectorID',
- 'Logos',
- function($scope, $location, $timeout, Projector, slides, Config, ProjectorID, Logos) {
- var projectorId = ProjectorID();
-
- $scope.broadcast = 0;
-
- var setElements = function (projector) {
- // Get all elements, that should be projected.
- var newElements = [];
- var enable_clock = Config.get('projector_enable_clock');
- enable_clock = enable_clock ? enable_clock.value : true;
- _.forEach(slides.getElements(projector), function (element) {
- if (!element.error) {
- // Exclude the clock if it should be disabled.
- if (enable_clock || element.name !== 'core/clock') {
- newElements.push(element);
- }
- } else {
- console.error("Error for slide " + element.name + ": " + element.error);
- }
- });
-
- // Now we have to align $scope.elements to newElements:
- // We cannot just assign them, because the ng-repeat would reload every
- // element. This should be prevented (see #3259). To change $scope.elements:
- // 1) remove all elements from scope, that are not in newElements (compared by the uuid)
- // 2) Every new element in newElements, that is not in $scope.elements, get inserted there.
- // 3) If there is the same element in newElements and $scope.elements every changed property
- // is copied from the new element to the scope element.
-
- $scope.elements = _.filter($scope.elements, function (element) {
- return _.some(newElements, function (newElement) {
- return element.uuid === newElement.uuid;
- });
- });
-
- _.forEach(newElements, function (newElement) {
- var matchingElement = _.find($scope.elements, function (element) {
- return element.uuid === newElement.uuid;
- });
- if (matchingElement) {
- // copy all changed properties.
- _.forEach(newElement, function (value, key) {
- // key has own property and does not start with a '$'.
- if (newElement.hasOwnProperty(key) && key.indexOf('$') != 0) {
- if (typeof matchingElement[key] === 'undefined' || matchingElement[key] !== value) {
- matchingElement[key] = value;
- }
- }
- });
- } else {
- $scope.elements.push(newElement);
- }
- });
- };
-
- $scope.scroll = 0;
- var setScroll = function (scroll) {
- $scope.scroll = -250 * scroll;
- };
-
- $scope.$watch(function () {
- return Projector.lastModified(projectorId);
- }, function () {
- $scope.projector = Projector.get(projectorId);
- if ($scope.projector) {
- if ($scope.broadcast === 0) {
- setElements($scope.projector);
- $scope.blank = $scope.projector.blank;
- }
- setScroll($scope.projector.scroll);
- } else {
- // Blank projector on error
- $scope.elements = [];
- $scope.projector = {
- scale: 0,
- blank: true
- };
- setScroll(0);
- }
- });
-
- $scope.$watch(function () {
- return Config.lastModified('projector_broadcast');
- }, function () {
- var bc = Config.get('projector_broadcast');
- if (bc) {
- if ($scope.broadcast != bc.value) {
- $scope.broadcast = bc.value;
- if ($scope.broadcastDeregister) {
- // revert to original $scope.projector
- $scope.broadcastDeregister();
- $scope.broadcastDeregister = null;
- setElements($scope.projector);
- $scope.blank = $scope.projector.blank;
- }
- }
- if ($scope.broadcast > 0) {
- // get elements and blank from broadcast projector
- $scope.broadcastDeregister = $scope.$watch(function () {
- return Projector.lastModified($scope.broadcast);
- }, function () {
- if ($scope.broadcast > 0) {
- var broadcast_projector = Projector.get($scope.broadcast);
- if (broadcast_projector) {
- setElements(broadcast_projector);
- $scope.blank = broadcast_projector.blank;
- }
- }
- });
- }
- }
- });
-
- $scope.$watch(function () {
- return Config.lastModified('projector_enable_clock');
- }, function () {
- setElements($scope.projector);
- });
-
- $scope.$on('$destroy', function() {
- if ($scope.broadcastDeregister) {
- $scope.broadcastDeregister();
- $scope.broadcastDeregister = null;
- }
- });
- }
-])
-
-.controller('SlideClockCtrl', [
- '$scope',
- '$interval',
- function($scope, $interval) {
- // 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
- // class.
- $scope.servertime = ( Date.now() / 1000 - $scope.serverOffset ) * 1000;
- var interval = $interval(function () {
- $scope.servertime = ( Date.now() / 1000 - $scope.serverOffset ) * 1000;
- }, 30000); // Update the clock every 30 seconds
-
- $scope.$on('$destroy', function() {
- if (interval) {
- $interval.cancel(interval);
- }
- });
- }
-])
-
-.controller('SlideCountdownCtrl', [
- '$scope',
- '$interval',
- 'Countdown',
- function($scope, $interval, Countdown) {
- // 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
- // class.
- var id = $scope.element.id;
- var interval;
- var calculateCountdownTime = function (countdown) {
- countdown.seconds = Math.floor( $scope.countdown.countdown_time - Date.now() / 1000 + $scope.serverOffset );
- };
- $scope.$watch(function () {
- return Countdown.lastModified(id);
- }, function () {
- $scope.countdown = Countdown.get(id);
- if (interval) {
- $interval.cancel(interval);
- }
- if ($scope.countdown) {
- if ($scope.countdown.running) {
- calculateCountdownTime($scope.countdown);
- interval = $interval(function () { calculateCountdownTime($scope.countdown); }, 1000);
- } else {
- $scope.countdown.seconds = $scope.countdown.countdown_time;
- }
- }
- });
- $scope.$on('$destroy', function() {
- // Cancel the interval if the controller is destroyed
- if (interval) {
- $interval.cancel(interval);
- }
- });
- }
-])
-
-.controller('SlideMessageCtrl', [
- '$scope',
- 'ProjectorMessage',
- 'Projector',
- 'ProjectorID',
- 'gettextCatalog',
- function($scope, ProjectorMessage, Projector, ProjectorID, gettextCatalog) {
- // 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
- // class.
- var id = $scope.element.id;
-
- if ($scope.element.identify) {
- var projector = Projector.get(ProjectorID());
- $scope.identifyMessage = gettextCatalog.getString('Projector') + ' ' + projector.id + ': ' + gettextCatalog.getString(projector.name);
- } else {
- $scope.message = ProjectorMessage.get(id);
- ProjectorMessage.bindOne(id, $scope, 'message');
- }
- }
-]);
-
-}());
diff --git a/openslides/core/static/js/core/remove-format-plugin.js b/openslides/core/static/js/core/remove-format-plugin.js
deleted file mode 100644
index 046dce06c..000000000
--- a/openslides/core/static/js/core/remove-format-plugin.js
+++ /dev/null
@@ -1,50 +0,0 @@
-(function () {
-
-'use strict';
-
-angular.module('OpenSlidesApp.core.remove-format-plugin', [
- 'OpenSlidesApp.core',
-])
-
-/*
- * Plugin for the CKEditor that hooks into the removeformat plugin
- * which is a default plugin enabled by 'cleanup' in the config
- * toolbar.
- * We change the behavior of the removeformat command here:
- * It should not remove any tags and styles, but only the
- * 'DISALLOWED_STYLES'. Removeformat traverses through the DOM
- * and calles for every element the custom filter down below.
- * We change the element and return false, so the removeformat
- * plugin does not clean it up.
- */
-.factory('OSRemoveFormatPlugin', [
- 'Editor',
- 'gettextCatalog',
- function (Editor, gettextCatalog) {
- var DISALLOWED_STYLES = ['color', 'background-color'];
- return {
- getPlugin: function () {
- return {
- init: function (editor) {
- editor.addRemoveFormatFilter(function (element) {
- _.forEach(DISALLOWED_STYLES, function (style) {
- element.removeStyle(style);
- });
- return false;
- });
- },
- };
- },
- };
- }
-])
-
-.run([
- 'Editor',
- 'OSRemoveFormatPlugin',
- function (Editor, OSRemoveFormatPlugin, gettext) {
- Editor.registerPlugin('OSRemoveFormat', OSRemoveFormatPlugin.getPlugin());
- }
-]);
-
-}());
diff --git a/openslides/core/static/js/core/site.js b/openslides/core/static/js/core/site.js
deleted file mode 100644
index 4059a376a..000000000
--- a/openslides/core/static/js/core/site.js
+++ /dev/null
@@ -1,2135 +0,0 @@
-(function () {
-
-'use strict';
-
-// The core module for the OpenSlides site
-angular.module('OpenSlidesApp.core.site', [
- 'OpenSlidesApp.core',
- 'OpenSlidesApp.core.start',
- 'OpenSlidesApp.core.csv',
- 'OpenSlidesApp.core.remove-format-plugin',
- 'OpenSlidesApp.poll.majority',
- 'ui.router',
- 'colorpicker.module',
- 'formly',
- 'formlyBootstrap',
- 'localytics.directives',
- 'ngDialog',
- 'ngFileSaver',
- 'ngMessages',
- 'ckeditor',
- 'luegg.directives',
- 'xeditable',
- 'rzModule',
-])
-
-// Can be used to find out if the projector or the side is used
-.constant('REALM', 'site')
-
-.factory('DateTimePickerTranslation', [
- 'gettextCatalog',
- function (gettextCatalog) {
- return {
- getButtons: function () {
- return {
- show: true,
- now: {
- show: true,
- text: gettextCatalog.getString('now')
- },
- today: {
- show: true,
- text: gettextCatalog.getString('today')
- },
- clear: {
- show: true,
- text: gettextCatalog.getString('clear')
- },
- date: {
- show: true,
- text: gettextCatalog.getString('date')
- },
- time: {
- show: true,
- text: gettextCatalog.getString('time')
- },
- close: {
- show: true,
- text: gettextCatalog.getString('close')
- }
- };
- }
- };
- }
-
-])
-
-// Provider to register entries for the main menu.
-.provider('mainMenu', [
- function() {
- var mainMenuList = [];
- var scope;
-
- this.register = function(config) {
- mainMenuList.push(config);
- };
-
- this.$get = ['operator', function(operator) {
- return {
- registerScope: function (scope) {
- this.scope = scope;
- },
- updateMainMenu: function () {
- if (this.scope) {
- this.scope.elements = this.getElements();
- }
- },
- getElements: function() {
- var elements = mainMenuList.filter(function (element) {
- return typeof element.perm === "undefined" || operator.hasPerms(element.perm);
- });
-
- elements.sort(function (a, b) {
- return a.weight - b.weight;
- });
- return elements;
- }
- };
- }];
- }
-])
-
-// Provider to register a searchable module/app.
-.provider('Search', [
- function() {
- var searchModules = [];
-
- this.register = function(module) {
- searchModules.push(module);
- };
-
- this.$get = [
- function () {
- return {
- getAll: function () {
- return searchModules;
- }
- };
- }
- ];
- }
-])
-
-.run([
- 'editableOptions',
- 'gettext',
- function (editableOptions, gettext) {
- editableOptions.theme = 'bs3';
- editableOptions.cancelButtonAriaLabel = gettext('Cancel');
- editableOptions.cancelButtonTitle = gettext('Cancel');
- editableOptions.clearButtonAriaLabel = gettext('Clear');
- editableOptions.clearButtonTitle = gettext('Clear');
- editableOptions.submitButtonAriaLabel = gettext('Submit');
- editableOptions.submitButtonTitle = gettext('Submit');
- }
-])
-
-.factory('WebpageTitle', [
- '$rootScope',
- function ($rootScope) {
- $rootScope.activeAppTitle = '';
- return {
- updateTitle: function (text) {
- $rootScope.activeAppTitle = text || '';
- },
- };
- }
-])
-
-// Watch for the basePerm on a stateChange and initialize the WebpageTitle factory
-.run([
- '$rootScope',
- 'operator',
- 'WebpageTitle',
- function ($rootScope, operator, WebpageTitle) {
- $rootScope.$on('$stateChangeSuccess', function(event, toState) {
- WebpageTitle.updateTitle(toState.data ? toState.data.title : '');
- if (toState.data) {
- $rootScope.baseViewPermissionsGranted = toState.data.basePerm ?
- operator.hasPerms(toState.data.basePerm) : true;
- } else {
- $rootScope.baseViewPermissionsGranted = true;
- }
- // Scroll to top on every state change
- $rootScope.gotoTop();
- });
- }
-])
-
-// Make the main content expandable
-.run([
- '$rootScope',
- function ($rootScope) {
- $rootScope.$on('$stateChangeSuccess', function() {
- $rootScope.expandContent = false;
- });
- $rootScope.toggleExpandContent = function () {
- $rootScope.expandContent = !$rootScope.expandContent;
- };
- }
-])
-
-.config([
- 'mainMenuProvider',
- 'gettext',
- function (mainMenuProvider, gettext) {
- mainMenuProvider.register({
- 'ui_sref': 'home',
- 'img_class': 'home',
- 'title': gettext('Home'),
- 'weight': 100,
- 'perm': 'core.can_see_frontpage',
- });
-
- mainMenuProvider.register({
- 'ui_sref': 'config',
- 'img_class': 'cog',
- 'title': gettext('Settings'),
- 'weight': 1000,
- 'perm': 'core.can_manage_config',
- });
- }
-])
-
-.config([
- '$urlRouterProvider',
- '$locationProvider',
- function($urlRouterProvider, $locationProvider) {
- // define fallback url and html5Mode
- $urlRouterProvider.otherwise('/');
- $locationProvider.html5Mode(true);
- }
-])
-
-.config([
- '$httpProvider',
- function($httpProvider) {
- // Combine the django csrf system with the angular csrf system
- $httpProvider.defaults.xsrfCookieName = 'OpenSlidesCsrfToken';
- $httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';
- }
-])
-
-
-.config([
- '$stateProvider',
- '$urlMatcherFactoryProvider',
- function($stateProvider, $urlMatcherFactoryProvider) {
- // Make the trailing slash optional
- $urlMatcherFactoryProvider.strictMode(false);
-
- // Use stateProvider.decorator to give default values to our states
- $stateProvider.decorator('views', function(state, parent) {
- var result = {},
- views = parent(state);
-
- if (state.abstract || state.data && state.data.extern) {
- return views;
- }
-
- angular.forEach(views, function(config, name) {
- // Sets additional default values for templateUrl
- var templateUrl,
- controller,
- defaultControllers = {
- create: 'CreateCtrl',
- update: 'UpdateCtrl',
- list: 'ListCtrl',
- detail: 'DetailCtrl',
- };
-
- // Split up state name
- // example: "motions.motion.detail.update" -> ['motions', 'motion', 'detail', 'update']
- var patterns = state.name.split('.');
-
- // set app and module name from state
- // - appName: patterns[0] (e.g. "motions")
- // - moduleNames: patterns without first element (e.g. ["motion", "detail", "update"])
- var appName = '';
- var moduleName = '';
- var moduleNames = [];
- if (patterns.length > 0) {
- appName = patterns[0];
- moduleNames = patterns.slice(1);
- }
- if (moduleNames.length > 0) {
- // convert from camcelcase to dash notation
- // example: ["motionBlock", "detail"] -> ["motion-block", "detail"]
- for (var i = 0; i < moduleNames.length; i++) {
- moduleNames[i] = moduleNames[i].replace(/([a-z\d])([A-Z])/g, '$1-$2').toLowerCase();
- }
-
- // use special templateUrl for create and update view
- // example: ["motion", "detail", "update"] -> "motion-form"
- if (_.last(moduleNames).match(/(create|update)/)) {
- moduleName = '/' + moduleNames[0] + '-form';
- } else {
- // convert modelNames array to url string
- // example: ["motion-block", "detail"] -> "motion-block-detail"
- moduleName = '/' + moduleNames.join('-');
- }
- }
- templateUrl = 'static/templates/' + appName + moduleName + '.html';
- config.templateUrl = state.templateUrl || templateUrl;
-
- // controller
- if (patterns.length >= 3) {
- controller = _.upperFirst(patterns[1]) + defaultControllers[_.last(patterns)];
- config.controller = state.controller || controller;
- }
- result[name] = config;
- });
- return result;
- })
-
- .decorator('url', function(state, parent) {
- var defaultUrl;
-
- if (state.abstract) {
- defaultUrl = '';
- } else {
- var patterns = state.name.split('.'),
- defaultUrls = {
- create: '/new',
- update: '/edit',
- list: '',
- // The id is expected to be an integer, if not, the url has to
- // be defined manually
- detail: '/{id:int}',
- };
-
- defaultUrl = defaultUrls[_.last(patterns)];
- }
-
- state.url = state.url || defaultUrl;
- return parent(state);
- });
- }
-])
-
-.config([
- '$stateProvider',
- '$locationProvider',
- 'gettext',
- function($stateProvider, $locationProvider, gettext) {
- // Core urls
- $stateProvider
- .state('home', {
- url: '/',
- templateUrl: 'static/templates/home.html',
- data: {
- title: gettext('Home'),
- basePerm: 'core.can_see_frontpage',
- },
- })
- .state('projector', {
- url: '/projector/{id:int}/',
- templateUrl: 'static/templates/projector-container.html',
- data: {extern: true},
- onEnter: function($window) {
- $window.location.href = this.url;
- }
- })
- .state('real-projector', {
- url: '/real-projector/{id:int}/',
- templateUrl: 'static/templates/projector.html',
- data: {extern: true},
- onEnter: function($window) {
- $window.location.href = this.url;
- }
- })
- .state('manage-projectors', {
- url: '/manage-projectors',
- templateUrl: 'static/templates/core/manage-projectors.html',
- controller: 'ManageProjectorsCtrl',
- data: {
- title: gettext('Manage projectors'),
- basePerm: 'core.can_manage_projector',
- },
- })
- .state('core', {
- url: '/core',
- abstract: true,
- template: "Settings
-
-
- {{ group.name | translate }}
-
-
- {{ subgroup.name | translate }}
- Manage projectors
- Live view
-
-
- Countdowns
-
- Messages
-
-
-
-
- List of speakers
-
- Edit message
-
-
diff --git a/openslides/core/static/templates/core/radio-buttons.html b/openslides/core/static/templates/core/radio-buttons.html
deleted file mode 100644
index 528179ed8..000000000
--- a/openslides/core/static/templates/core/radio-buttons.html
+++ /dev/null
@@ -1,17 +0,0 @@
-Edit tag
-New tag
-
-Tags
-
-
-
-
-
-
-
-
- {{ tag.name }}
-
- {{ config('general_event_welcome_title') | translate }}
-
-
- Legal notice
-
-
-
- Privacy policy
- Privacy policy
-
-Search results
- {{ result.verboseName | translate }}
-
-
diff --git a/openslides/mediafiles/static/templates/mediafiles/mediafile-form.html b/openslides/mediafiles/static/templates/mediafiles/mediafile-form.html
deleted file mode 100644
index a9f3e4c13..000000000
--- a/openslides/mediafiles/static/templates/mediafiles/mediafile-form.html
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
-
-
-
- Edit File
-
-Files
-
-
-Upload files
-
-
';
- }
-
- var html = this._serializeTag(node);
- for (var i = 0; i < node.childNodes.length; i++) {
- if (node.childNodes[i].nodeType === TEXT_NODE) {
- html += node.childNodes[i].nodeValue.replace(/&/g, "&").replace(//g, ">");
- } else if (!stripLineNumbers || (!lineNumberingService._isOsLineNumberNode(node.childNodes[i]) && !lineNumberingService._isOsLineBreakNode(node.childNodes[i]))) {
- html += this._serializeDom(node.childNodes[i], stripLineNumbers);
- }
- }
- if (node.nodeType !== DOCUMENT_FRAGMENT_NODE) {
- html += '' + node.nodeName + '>';
- }
-
- return html;
- };
-
- /**
- * Implementation hint: the first element of "toChildTrace" array needs to be a child element of "node"
- */
- this._serializePartialDomToChild = function(node, toChildTrace, stripLineNumbers) {
- if (lineNumberingService._isOsLineNumberNode(node) || lineNumberingService._isOsLineBreakNode(node)) {
- return '';
- }
- if (node.nodeName === 'OS-LINEBREAK') {
- return '';
- }
-
- var html = this._serializeTag(node);
-
- for (var i = 0, found = false; i < node.childNodes.length && !found; i++) {
- if (node.childNodes[i] === toChildTrace[0]) {
- found = true;
- var remainingTrace = toChildTrace;
- remainingTrace.shift();
- if (!lineNumberingService._isOsLineNumberNode(node.childNodes[i])) {
- html += this._serializePartialDomToChild(node.childNodes[i], remainingTrace, stripLineNumbers);
- }
- } else if (node.childNodes[i].nodeType === TEXT_NODE) {
- html += node.childNodes[i].nodeValue;
- } else {
- if (!stripLineNumbers || (!lineNumberingService._isOsLineNumberNode(node.childNodes[i]) &&
- !lineNumberingService._isOsLineBreakNode(node.childNodes[i]))) {
- html += this._serializeDom(node.childNodes[i], stripLineNumbers);
- }
- }
- }
- if (!found) {
- console.trace();
- throw "Inconsistency or invalid call of this function detected (to)";
- }
- return html;
- };
-
- /**
- * Implementation hint: the first element of "toChildTrace" array needs to be a child element of "node"
- */
- this._serializePartialDomFromChild = function(node, fromChildTrace, stripLineNumbers) {
- if (lineNumberingService._isOsLineNumberNode(node) || lineNumberingService._isOsLineBreakNode(node)) {
- return '';
- }
- if (node.nodeName === 'OS-LINEBREAK') {
- return '';
- }
-
- var html = '';
- for (var i = 0, found = false; i < node.childNodes.length; i++) {
- if (node.childNodes[i] === fromChildTrace[0]) {
- found = true;
- var remainingTrace = fromChildTrace;
- remainingTrace.shift();
- if (!lineNumberingService._isOsLineNumberNode(node.childNodes[i])) {
- html += this._serializePartialDomFromChild(node.childNodes[i], remainingTrace, stripLineNumbers);
- }
- } else if (found) {
- if (node.childNodes[i].nodeType === TEXT_NODE) {
- html += node.childNodes[i].nodeValue;
- } else {
- if (!stripLineNumbers || (!lineNumberingService._isOsLineNumberNode(node.childNodes[i]) &&
- !lineNumberingService._isOsLineBreakNode(node.childNodes[i]))) {
- html += this._serializeDom(node.childNodes[i], stripLineNumbers);
- }
- }
- }
- }
- if (!found) {
- console.trace();
- throw "Inconsistency or invalid call of this function detected (from)";
- }
- if (node.nodeType !== DOCUMENT_FRAGMENT_NODE) {
- html += '' + node.nodeName + '>';
- }
- return html;
- };
-
- /**
- * @param {string} html
- * @return {DocumentFragment}
- */
- this.htmlToFragment = function(html) {
- var fragment = document.createDocumentFragment(),
- div = document.createElement('DIV');
- div.innerHTML = html;
- while (div.childElementCount) {
- var child = div.childNodes[0];
- div.removeChild(child);
- fragment.appendChild(child);
- }
- return fragment;
- };
-
- /**
- * When a
)
- * - ancestor: the most specific DOM element that contains the HTML snippet (e.g. a UL, if several LIs are selected)
- * - outerContextStart: An HTML string that opens all necessary tags to get the browser into the rendering mode
- * of the ancestor element (e.g. in the case of the multiple LIs)
- * - outerContectEnd: An HTML string that closes all necessary tags from the ancestor element (e.g.
Line 2
Line 3.
- *
- */
- this._replaceLinesMergeNodeArrays = function(nodes1, nodes2) {
- if (nodes1.length === 0) {
- return nodes2;
- }
- if (nodes2.length === 0) {
- return nodes1;
- }
-
- var out = [];
- for (var i = 0; i < nodes1.length - 1; i++) {
- out.push(nodes1[i]);
- }
-
- var lastNode = nodes1[nodes1.length - 1],
- firstNode = nodes2[0];
- if (lastNode.nodeType === TEXT_NODE && firstNode.nodeType === TEXT_NODE) {
- var newTextNode = lastNode.ownerDocument.createTextNode(lastNode.nodeValue + firstNode.nodeValue);
- out.push(newTextNode);
- } else if (lastNode.nodeName === firstNode.nodeName) {
- var newNode = lastNode.ownerDocument.createElement(lastNode.nodeName);
- for (i = 0; i < lastNode.attributes.length; i++) {
- var attr = lastNode.attributes[i];
- newNode.setAttribute(attr.name, attr.value);
- }
-
- // Remove #text nodes inside of List elements (OL/UL), as they are confusing
- var lastChildren, firstChildren;
- if (lastNode.nodeName === 'OL' || lastNode.nodeName === 'UL') {
- lastChildren = [];
- firstChildren = [];
- for (i = 0; i < firstNode.childNodes.length; i++) {
- if (firstNode.childNodes[i].nodeType === ELEMENT_NODE) {
- firstChildren.push(firstNode.childNodes[i]);
- }
- }
- for (i = 0; i < lastNode.childNodes.length; i++) {
- if (lastNode.childNodes[i].nodeType === ELEMENT_NODE) {
- lastChildren.push(lastNode.childNodes[i]);
- }
- }
- } else {
- lastChildren = lastNode.childNodes;
- firstChildren = firstNode.childNodes;
- }
-
- var children = this._replaceLinesMergeNodeArrays(lastChildren, firstChildren);
- for (i = 0; i < children.length; i++) {
- newNode.appendChild(children[i]);
- }
-
- out.push(newNode);
- } else {
- if (lastNode.nodeName !== 'TEMPLATE') {
- out.push(lastNode);
- }
- if (firstNode.nodeName !== 'TEMPLATE') {
- out.push(firstNode);
- }
- }
-
- for (i = 1; i < nodes2.length; i++) {
- out.push(nodes2[i]);
- }
-
- return out;
- };
-
- /**
- *
- * @param {string} html
- * @returns {string}
- * @private
- */
- this._normalizeHtmlForDiff = function (html) {
- // Convert all HTML tags to uppercase, but leave the values of attributes unchanged
- // All attributes and CSS class names are sorted alphabetically
- // If an attribute is empty, it is removed
- html = html.replace(/<(\/?[a-z]*)( [^>]*)?>/ig, function (html, tag, attributes) {
- var tagNormalized = tag.toUpperCase();
- if (attributes === undefined) {
- attributes = "";
- }
- var attributesList = [],
- attributesMatcher = /( [^"'=]*)(= *((["'])(.*?)\4))?/gi,
- match;
- do {
- match = attributesMatcher.exec(attributes);
- if (match) {
- var attrNormalized = match[1].toUpperCase(),
- attrValue = match[5];
- if (match[2] !== undefined) {
- if (attrNormalized === ' CLASS') {
- attrValue = attrValue.split(' ').sort().join(' ').replace(/^\s+/, '').replace(/\s+$/, '');
- }
- attrNormalized += "=" + match[4] + attrValue + match[4];
- }
- if (attrValue !== '') {
- attributesList.push(attrNormalized);
- }
- }
- } while (match);
- attributes = attributesList.sort().join('');
- return "<" + tagNormalized + attributes + ">";
- });
-
- var entities = {
- ' ': ' ',
- '–': '-',
- 'ä': 'ä',
- 'ö': 'ö',
- 'ü': 'ü',
- 'Ä': 'Ä',
- 'Ö': 'Ö',
- 'Ü': 'Ü',
- 'ß': 'ß',
- '„': '„',
- '“': '“',
- '•': '•',
- '§': '§',
- 'é': 'é',
- '€': '€'
- };
-
- html = html.replace(/\s+<\/P>/gi, '').replace(/\s+<\/DIV>/gi, '
)\n/gi, "$1");
- html = html.replace(/[ \n\t]+/gi, ' ');
- html = html.replace(/(<\/(div|p|ul|li|blockquote>)>) /gi, "$1\n");
-
- return html;
- };
-
- this._getAllNextSiblings = function(element) {
- var elements = [];
- while (element.nextSibling) {
- elements.push(element.nextSibling);
- element = element.nextSibling;
- }
- return elements;
- };
-
- this._getAllPrevSiblingsReversed = function(element) {
- var elements = [];
- while (element.previousSibling) {
- elements.push(element.previousSibling);
- element = element.previousSibling;
- }
- return elements;
- };
-
- /**
- * This returns the line number range in which changes (insertions, deletions) are encountered.
- * As in extractRangeByLineNumbers(), "to" refers to the line breaking element at the end, i.e. the start of the following line.
- *
- * @param {string} diffHtml
- */
- this.detectAffectedLineRange = function (diffHtml) {
- var cacheKey = lineNumberingService.djb2hash(diffHtml),
- cached = diffCache.get(cacheKey);
- if (!angular.isUndefined(cached)) {
- return cached;
- }
-
- var fragment = this.htmlToFragment(diffHtml);
-
- this._insertInternalLineMarkers(fragment);
- this._insertInternalLiNumbers(fragment);
-
- var changes = fragment.querySelectorAll('ins, del, .insert, .delete'),
- firstChange = changes.item(0),
- lastChange = changes.item(changes.length - 1),
- i, j;
-
- if (!firstChange || !lastChange) {
- // There are no changes
- return null;
- }
-
- var firstTrace = this._getNodeContextTrace(firstChange),
- lastLineNumberBefore = null;
- for (j = firstTrace.length - 1; j >= 0 && lastLineNumberBefore === null; j--) {
- var prevSiblings = this._getAllPrevSiblingsReversed(firstTrace[j]);
- for (i = 0; i < prevSiblings.length && lastLineNumberBefore === null; i++) {
- lastLineNumberBefore = this._getLastLineNumberNode(prevSiblings[i]);
- }
- }
-
- var lastTrace = this._getNodeContextTrace(lastChange),
- firstLineNumberAfter = null;
- for (j = lastTrace.length - 1; j >= 0 && firstLineNumberAfter === null; j--) {
- var nextSiblings = this._getAllNextSiblings(lastTrace[j]);
- for (i = 0; i < nextSiblings.length && firstLineNumberAfter === null; i++) {
- firstLineNumberAfter = this._getFirstLineNumberNode(nextSiblings[i]);
- }
- }
-
- var range = {
- "from": parseInt(lastLineNumberBefore.getAttribute("data-line-number")),
- "to": parseInt(firstLineNumberAfter.getAttribute("data-line-number"))
- };
-
- diffCache.put(cacheKey, range);
- return range;
- };
-
- /**
- * Removes .delete-nodes and -Tags (including content)
- * Removes the .insert-classes and the wrapping -Tags (while maintaining content)
- * @param html
- */
- this.diffHtmlToFinalText = function(html) {
- var fragment = this.htmlToFragment(html);
-
- var delNodes = fragment.querySelectorAll('.delete, del');
- for (var i = 0; i < delNodes.length; i++) {
- delNodes[i].parentNode.removeChild(delNodes[i]);
- }
-
- var insNodes = fragment.querySelectorAll('ins');
- for (i = 0; i < insNodes.length; i++) {
- var ins = insNodes[i];
- while (ins.childNodes.length > 0) {
- var child = ins.childNodes.item(0);
- ins.removeChild(child);
- ins.parentNode.insertBefore(child, ins);
- }
- ins.parentNode.removeChild(ins);
- }
-
- var insertNodes = fragment.querySelectorAll('.insert');
- for (i = 0;i < insertNodes.length; i++) {
- this.removeCSSClass(insertNodes[i], 'insert');
- }
-
- return this._serializeDom(fragment, false);
- };
-
- /**
- * @param {string} htmlOld
- * @param {string} htmlNew
- * @returns {number}
- */
- this.detectReplacementType = function (htmlOld, htmlNew) {
- htmlOld = this._normalizeHtmlForDiff(htmlOld);
- htmlNew = this._normalizeHtmlForDiff(htmlNew);
-
- if (htmlOld === htmlNew) {
- return this.TYPE_REPLACEMENT;
- }
-
- var i, foundDiff;
- for (i = 0, foundDiff = false; i < htmlOld.length && i < htmlNew.length && foundDiff === false; i++) {
- if (htmlOld[i] !== htmlNew[i]) {
- foundDiff = true;
- }
- }
-
- var remainderOld = htmlOld.substr(i - 1),
- remainderNew = htmlNew.substr(i - 1),
- type = this.TYPE_REPLACEMENT;
-
- if (remainderOld.length > remainderNew.length) {
- if (remainderOld.substr(remainderOld.length - remainderNew.length) === remainderNew) {
- type = this.TYPE_DELETION;
- }
- } else if (remainderOld.length < remainderNew.length) {
- if (remainderNew.substr(remainderNew.length - remainderOld.length) === remainderOld) {
- type = this.TYPE_INSERTION;
- }
- }
-
- return type;
- };
-
- /**
- * @param {string} oldHtml
- * @param {string} newHTML
- * @param {number} fromLine
- * @param {number} toLine
- */
- this.replaceLines = function (oldHtml, newHTML, fromLine, toLine) {
- var data = this.extractRangeByLineNumbers(oldHtml, fromLine, toLine),
- previousHtml = data.previousHtml + '' + data.previousHtmlEndSnippet,
- previousFragment = this.htmlToFragment(previousHtml),
- followingHtml = data.followingHtmlStartSnippet + '' + data.followingHtml,
- followingFragment = this.htmlToFragment(followingHtml),
- newFragment = this.htmlToFragment(newHTML);
-
- if (data.html.length > 0 && data.html.substr(-1) === ' ') {
- this._insertDanglingSpace(newFragment);
- }
-
- var merged = this._replaceLinesMergeNodeArrays(previousFragment.childNodes, newFragment.childNodes);
- merged = this._replaceLinesMergeNodeArrays(merged, followingFragment.childNodes);
-
- var mergedFragment = document.createDocumentFragment();
- for (var i = 0; i < merged.length; i++) {
- mergedFragment.appendChild(merged[i]);
- }
-
- var forgottenTemplates = mergedFragment.querySelectorAll("TEMPLATE");
- for (i = 0; i < forgottenTemplates.length; i++) {
- var el = forgottenTemplates[i];
- el.parentNode.removeChild(el);
- }
-
- var forgottenSplitClasses = mergedFragment.querySelectorAll(".os-split-before, .os-split-after");
- for (i = 0; i < forgottenSplitClasses.length; i++) {
- this.removeCSSClass(forgottenSplitClasses[i], 'os-split-before');
- this.removeCSSClass(forgottenSplitClasses[i], 'os-split-after');
- }
-
- return this._serializeDom(mergedFragment, true);
- };
-
- this.addCSSClass = function (node, className) {
- if (node.nodeType !== ELEMENT_NODE) {
- return;
- }
- var classes = node.getAttribute('class');
- classes = (classes ? classes.split(' ') : []);
- if (classes.indexOf(className) === -1) {
- classes.push(className);
- }
- node.setAttribute('class', classes.join(' '));
- };
-
- this.removeCSSClass = function (node, className) {
- if (node.nodeType !== ELEMENT_NODE) {
- return;
- }
- var classes = node.getAttribute('class'),
- newClasses = [];
- classes = (classes ? classes.split(' ') : []);
- for (var i = 0; i < classes.length; i++) {
- if (classes[i] !== className) {
- newClasses.push(classes[i]);
- }
- }
- if (newClasses.length === 0) {
- node.removeAttribute('class');
- } else {
- node.setAttribute('class', newClasses.join(' '));
- }
- };
-
- this.addDiffMarkup = function (originalHTML, newHTML, fromLine, toLine, diffFormatterCb) {
- var data = this.extractRangeByLineNumbers(originalHTML, fromLine, toLine),
- previousHtml = data.previousHtml + '' + data.previousHtmlEndSnippet,
- previousFragment = this.htmlToFragment(previousHtml),
- followingHtml = data.followingHtmlStartSnippet + '' + data.followingHtml,
- followingFragment = this.htmlToFragment(followingHtml),
- newFragment = this.htmlToFragment(newHTML),
- oldHTML = data.outerContextStart + data.innerContextStart + data.html +
- data.innerContextEnd + data.outerContextEnd,
- oldFragment = this.htmlToFragment(oldHTML),
- el;
-
- var diffFragment = diffFormatterCb(oldFragment, newFragment);
-
- var mergedFragment = document.createDocumentFragment();
- while (previousFragment.firstChild) {
- el = previousFragment.firstChild;
- previousFragment.removeChild(el);
- mergedFragment.appendChild(el);
- }
- while (diffFragment.firstChild) {
- el = diffFragment.firstChild;
- diffFragment.removeChild(el);
- mergedFragment.appendChild(el);
- }
- while (followingFragment.firstChild) {
- el = followingFragment.firstChild;
- followingFragment.removeChild(el);
- mergedFragment.appendChild(el);
- }
-
- var forgottenTemplates = mergedFragment.querySelectorAll("TEMPLATE");
- for (var i = 0; i < forgottenTemplates.length; i++) {
- el = forgottenTemplates[i];
- el.parentNode.removeChild(el);
- }
-
- return this._serializeDom(mergedFragment, true);
- };
-
- /**
- * Adapted from http://ejohn.org/projects/javascript-diff-algorithm/
- * by John Resig, MIT License
- * @param {array} oldArr
- * @param {array} newArr
- * @returns {object}
- */
- this._diff = function (oldArr, newArr) {
- var ns = {},
- os = {},
- i;
-
- for (i = 0; i < newArr.length; i++) {
- if (ns[newArr[i]] === undefined)
- ns[newArr[i]] = {rows: [], o: null};
- ns[newArr[i]].rows.push(i);
- }
-
- for (i = 0; i < oldArr.length; i++) {
- if (os[oldArr[i]] === undefined)
- os[oldArr[i]] = {rows: [], n: null};
- os[oldArr[i]].rows.push(i);
- }
-
- for (i in ns) {
- if (ns[i].rows.length === 1 && typeof(os[i]) !== "undefined" && os[i].rows.length === 1) {
- newArr[ns[i].rows[0]] = {text: newArr[ns[i].rows[0]], row: os[i].rows[0]};
- oldArr[os[i].rows[0]] = {text: oldArr[os[i].rows[0]], row: ns[i].rows[0]};
- }
- }
-
- for (i = 0; i < newArr.length - 1; i++) {
- if (newArr[i].text !== null && newArr[i + 1].text === undefined && newArr[i].row + 1 < oldArr.length &&
- oldArr[newArr[i].row + 1].text === undefined && newArr[i + 1] == oldArr[newArr[i].row + 1]) {
- newArr[i + 1] = {text: newArr[i + 1], row: newArr[i].row + 1};
- oldArr[newArr[i].row + 1] = {text: oldArr[newArr[i].row + 1], row: i + 1};
- }
- }
-
- for (i = newArr.length - 1; i > 0; i--) {
- if (newArr[i].text !== null && newArr[i - 1].text === undefined && newArr[i].row > 0 &&
- oldArr[newArr[i].row - 1].text === undefined && newArr[i - 1] == oldArr[newArr[i].row - 1]) {
- newArr[i - 1] = {text: newArr[i - 1], row: newArr[i].row - 1};
- oldArr[newArr[i].row - 1] = {text: oldArr[newArr[i].row - 1], row: i - 1};
- }
- }
-
- return {o: oldArr, n: newArr};
- };
-
- this._tokenizeHtml = function (str) {
- var splitArrayEntriesEmbedSeparator = function (arr, by, prepend) {
- var newArr = [];
- for (var i = 0; i < arr.length; i++) {
- if (arr[i][0] === '<' && (by === " " || by === "\n")) {
- // Don't split HTML tags
- newArr.push(arr[i]);
- continue;
- }
-
- var parts = arr[i].split(by);
- if (parts.length === 1) {
- newArr.push(arr[i]);
- } else {
- var j;
- if (prepend) {
- if (parts[0] !== '') {
- newArr.push(parts[0]);
- }
- for (j = 1; j < parts.length; j++) {
- newArr.push(by + parts[j]);
- }
- } else {
- for (j = 0; j < parts.length - 1; j++) {
- newArr.push(parts[j] + by);
- }
- if (parts[parts.length - 1] !== '') {
- newArr.push(parts[parts.length - 1]);
- }
- }
- }
- }
- return newArr;
- };
- var splitArrayEntriesSplitSeparator = function (arr, by) {
- var newArr = [];
- for (var i = 0; i < arr.length; i++) {
- if (arr[i][0] === '<') {
- newArr.push(arr[i]);
- continue;
- }
- var parts = arr[i].split(by);
- for (var j = 0; j < parts.length; j++) {
- if (j > 0) {
- newArr.push(by);
- }
- newArr.push(parts[j]);
- }
- }
- return newArr;
- };
- var arr = splitArrayEntriesEmbedSeparator([str], '<', true);
- arr = splitArrayEntriesEmbedSeparator(arr, '>', false);
- arr = splitArrayEntriesSplitSeparator(arr, " ");
- arr = splitArrayEntriesSplitSeparator(arr, ".");
- arr = splitArrayEntriesSplitSeparator(arr, ",");
- arr = splitArrayEntriesSplitSeparator(arr, "!");
- arr = splitArrayEntriesSplitSeparator(arr, "-");
- arr = splitArrayEntriesEmbedSeparator(arr, "\n", false);
-
- var arrWithoutEmptes = [];
- for (var i = 0; i < arr.length; i++) {
- if (arr[i] !== '') {
- arrWithoutEmptes.push(arr[i]);
- }
- }
-
- return arrWithoutEmptes;
- };
-
- /**
- * @param {string} oldStr
- * @param {string} newStr
- * @returns {string}
- */
- this._diffString = function (oldStr, newStr) {
- oldStr = this._normalizeHtmlForDiff(oldStr.replace(/\s+$/, '').replace(/^\s+/, ''));
- newStr = this._normalizeHtmlForDiff(newStr.replace(/\s+$/, '').replace(/^\s+/, ''));
-
- var out = this._diff(this._tokenizeHtml(oldStr), this._tokenizeHtml(newStr));
-
- // This fixes the problem tested by "does not lose words when changes are moved X-wise"
- var lastRow = 0;
- for (var z = 0; z < out.n.length; z++) {
- if (out.n[z].row && out.n[z].row > lastRow) {
- lastRow = out.n[z].row;
- }
- if (out.n[z].row && out.n[z].row < lastRow) {
- out.o[out.n[z].row] = out.o[out.n[z].row].text;
- out.n[z] = out.n[z].text;
- }
- }
-
- var str = "";
- var i;
-
- if (out.n.length === 0) {
- for (i = 0; i < out.o.length; i++) {
- str += '' + out.o[i] + "";
- }
- } else {
- if (out.n[0].text === undefined) {
- for (var k = 0; k < out.o.length && out.o[k].text === undefined; k++) {
- str += '' + out.o[k] + "";
- }
- }
-
- var currOldRow = 0;
- for (i = 0; i < out.n.length; i++) {
- if (out.n[i].text === undefined) {
- if (out.n[i] !== "") {
- str += '' + out.n[i] + "";
- }
- } else if (out.n[i].row < currOldRow) {
- str += '' + out.n[i].text + "";
- } else {
- var pre = "";
-
- if ((i + 1) < out.n.length && out.n[i + 1].row !== undefined && out.n[i + 1].row > out.n[i].row + 1) {
- for (var n = out.n[i].row + 1; n < out.n[i + 1].row; n++) {
- if (out.o[n].text === undefined) {
- pre += '' + out.o[n] + "";
- } else {
- pre += '' + out.o[n].text + "";
- }
- }
- } else {
- for (var j = out.n[i].row + 1; j < out.o.length && out.o[j].text === undefined; j++) {
- pre += '' + out.o[j] + "";
- }
- }
- str += out.n[i].text + pre;
-
- currOldRow = out.n[i].row;
- }
- }
- }
-
- return str.replace(/^\s+/g, '').replace(/\s+$/g, '').replace(/ {2,}/g, ' ');
- };
-
- /**
- * @param {string} html
- * @return {boolean}
- * @private
- */
- this._isValidInlineHtml = function(html) {
- // If there are no HTML tags, we assume it's valid and skip further checks
- if (!html.match(/<[^>]*>/)) {
- return true;
- }
-
- // We check if this is a valid HTML that closes all its tags again using the innerHTML-Hack to correct
- // the string and check if the number of HTML tags changes by this
- var doc = document.createElement('div');
- doc.innerHTML = html;
- var tagsBefore = (html.match(/ tags
- if (html.match(/<(div|p|ul|li|blockquote)\W/i)) {
- return false;
- }
-
- return true;
- };
-
- /**
- * @param {string} html
- * @returns {boolean}
- * @private
- */
- this._diffDetectBrokenDiffHtml = function(html) {
- // If other HTML tags are contained within INS/DEL (e.g. "Test
- // - A change happens in the next tag, e.g. inserted text - // - The first tag occures a second time in the text, e.g. another
- // In this condition, the first tag is deleted first and inserted afterwards again
- // Test case: "does not break when an insertion followes a beginning tag occuring twice"
- // The work around inserts to tags at the beginning and removes them afterwards again,
- // to make sure this situation does not happen (and uses invisible pseudo-tags in case something goes wrong)
- var workaroundPrepend = " ]+class\s*=\s*["'][^"']*)os-split-after/gi, function(match, beginning) {
- oldIsSplitAfter = true;
- return beginning;
- });
- htmlNew = htmlNew.replace(/(\s* ]+class\s*=\s*["'][^"']*)os-split-after/gi, function(match, beginning) {
- newIsSplitAfter = true;
- return beginning;
- });
-
- // Performing the actual diff
- var str = this._diffString(workaroundPrepend + htmlOld, workaroundPrepend + htmlNew),
- diffUnnormalized = str.replace(/^\s+/g, '').replace(/\s+$/g, '').replace(/ {2,}/g, ' ');
-
-
- diffUnnormalized = this._fixWrongChangeDetection(diffUnnormalized);
-
- // Remove ]*)?>[\s\S]*?<\/p>)(\s*)<\/ins>/gim,
- function(match, whiteBefore, inner, tagInner, whiteAfter) {
- return whiteBefore +
- inner
- .replace(/ ]*)?>/gi, function(match) {
- return match + "";
- })
- .replace(/<\/p>/gi, " tags that only delete line numbers
- // We need to do this before removing as done in one of the next statements
- diffUnnormalized = diffUnnormalized.replace(
- /((
<\/del>)?(]+os-line-number[^>]+?>)(\s|<\/?del>)*<\/span>)<\/del>/gi,
- function(found,tag,br,span) {
- return (br !== undefined ? br : '') + span + ' ';
- }
- );
-
- diffUnnormalized = diffUnnormalized.replace(/<\/ins>/gi, '').replace(/<\/del>/gi, '');
-
- // Move whitespaces around inserted P's out of the INS-tag
- diffUnnormalized = diffUnnormalized.replace(
- /(\s*)(
More inserted text
- // into: Inserted Text\nMore inserted text
)/gi, "
$1"); - } - ); - - // If only a few characters of a word have changed, don't display this as a replacement of the whole word, - // but only of these specific characters - diffUnnormalized = diffUnnormalized.replace(/(.*)<\/p><\/del>$/gi, function(match, inner) { return "
" + inner + "
"; }); - - var node = document.createElement('div'); - node.innerHTML = diffUnnormalized; - diff = node.innerHTML; - - if (lineLength !== undefined && firstLineNumber !== undefined) { - node = lineNumberingService.insertLineNumbersNode(diff, lineLength, null, firstLineNumber); - diff = node.innerHTML; - } - } - - if (oldIsSplitAfter || newIsSplitAfter) { - diff = this._addClassToLastNode(diff, "os-split-after"); - } - - diffCache.put(cacheKey, diff); - return diff; - }; - } -]); - -}()); diff --git a/openslides/motions/static/js/motions/docx.js b/openslides/motions/static/js/motions/docx.js deleted file mode 100644 index dce23e97d..000000000 --- a/openslides/motions/static/js/motions/docx.js +++ /dev/null @@ -1,221 +0,0 @@ -(function () { - -'use strict'; - -angular.module('OpenSlidesApp.motions.docx', ['OpenSlidesApp.core.docx']) - -.factory('MotionDocxExport', [ - '$http', - '$q', - '$filter', - 'operator', - 'Config', - 'Category', - 'gettextCatalog', - 'FileSaver', - 'lineNumberingService', - 'Html2DocxConverter', - 'MotionComment', - function ($http, $q, $filter, operator, Config, Category, gettextCatalog, - FileSaver, lineNumberingService, Html2DocxConverter, MotionComment) { - - var PAGEBREAK = '') !== 0) { - comment = '
' + comment + '
'; - } - comments.push({ - title: title, - comment: comment, - }); - } - }); - return comments; - }; - - return { - export: function (motions, params) { - converter = Html2DocxConverter.createInstance(); - params = _.clone(params || {}); // Clone this to avoid sideeffects. - _.defaults(params, { - changeRecommendationMode: Config.get('motions_recommendation_text_mode').value, - include: { - text: true, - reason: true, - submitters: true, - }, - includeComments: {}, - }); - params.filename = gettextCatalog.getString('motions') + '.docx'; - if (!_.includes(['original', 'changed', 'agreed'], params.changeRecommendationMode)) { - params.changeRecommendationMode = 'original'; - } - - $http.get('/motions/docxtemplate/').then(function (success) { - var content = window.atob(success.data); - var doc = new Docxgen(content); - - getData(motions, params).then(function (data) { - doc.setData(data); - doc.render(); - - var zip = doc.getZip(); - zip = converter.updateZipFile(zip); - - var out = zip.generate({type: 'blob'}); - FileSaver.saveAs(out, params.filename); - }); - }); - }, - }; - } -]); - -}()); diff --git a/openslides/motions/static/js/motions/linenumbering.js b/openslides/motions/static/js/motions/linenumbering.js deleted file mode 100644 index d978e2d01..000000000 --- a/openslides/motions/static/js/motions/linenumbering.js +++ /dev/null @@ -1,772 +0,0 @@ -(function () { - -"use strict"; - -angular.module('OpenSlidesApp.motions.lineNumbering', []) - -/** - * Current limitations of this implementation: - * - * Only the following inline elements are supported: - * - 'SPAN', 'A', 'EM', 'S', 'B', 'I', 'STRONG', 'U', 'BIG', 'SMALL', 'SUB', 'SUP', 'TT' - * - 'INS' and 'DEL' are supported, but line numbering does not affect the content of 'INS'-elements - * - * Only other inline elements are allowed within inline elements. - * No constructs likeor
' + gettextCatalog.getString('New title') + ': ' + - escapeHtml(titleChange.text) + '
'; - } - motionTextContent += motion.getTextByMode(params.changeRecommendationMode, motionVersion); - } - content.push(converter.convertHTML(motionTextContent, params.lineNumberMode)); - } - return content; - }; - - // motion reason heading - var motionReason = function() { - if (params.include.reason) { - var reason = []; - if (motion.getReason(motionVersion)) { - reason.push({ - text: gettextCatalog.getString('Reason'), - style: 'heading3', - marginTop: 25, - }); - var width; - if (params.lineNumberMode == 'outside') { - width = '80%'; - } else { - width = '100%'; - } - reason.push({ - columns: [ - { - width: width, - stack: converter.convertHTML(motion.getReason(motionVersion), 'none'), - }, - ] - }); - } - return reason; - } - }; - - // motion comments handling - var motionComments = function () { - if (_.keys(params.includeComments).length !== 0) { - var fields = MotionComment.getNoSpecialCommentsFields(); - var comments = []; - _.forEach(params.includeComments, function (ok, id) { - if (ok && motion.comments[id]) { - var title = fields[id].name; - if (!fields[id].public) { - title += ' (' + gettextCatalog.getString('internal') + ')'; - } - comments.push({ - text: title, - style: 'heading3', - marginTop: 25, - }); - comments.push(converter.convertHTML(motion.comments[id])); - } - }); - return comments; - } - }; - - // Generates content as a pdfmake consumable - var getContent = function() { - var content = [ - title, - subtitle, - metaTable(), - motionTitle() - ]; - content = content.concat(motionText()); - - var reason = motionReason(); - if (reason) { - content.push(reason); - } - var comments = motionComments(); - if (comments) { - content.push(comments); - } - return content; - }; - - // getters - var getTitle = function() { - return motion.getTitle(motionVersion); - }; - - var getIdentifier = function() { - return motion.identifier ? motion.identifier : ''; - }; - - var getId = function() { - return motion.id; - }; - - var getCategory = function() { - return motion.category; - }; - - var getImageMap = function() { - return imageMap; - }; - - return $q(function (resolve, reject) { - ImageConverter.toBase64(getImageSources()).then(function (_imageMap) { - imageMap = _imageMap; - converter = PdfMakeConverter.createInstance(_imageMap); - resolve({ - getContent: getContent, - getTitle: getTitle, - getIdentifier: getIdentifier, - getId: getId, - getCategory: getCategory, - getImageMap: getImageMap, - }); - }, reject); - }); - }; - - return { - createInstance: createInstance - }; - } -]) - -.factory('MotionPartialContentProvider', [ - '$q', - 'gettextCatalog', - 'Config', - 'PDFLayout', - 'PdfMakeConverter', - 'ImageConverter', - 'HTMLValidizer', - function ($q, gettextCatalog, Config, PDFLayout, PdfMakeConverter, ImageConverter, HTMLValidizer) { - /* - * content should be an array of content blocks. Each content is an object providing a - * heading and a text. E.g. - * [{heading: 'comment1', text: ''}, {heading: ...}, ...] - * */ - var createInstance = function (motion, content) { - - var converter, imageMap = {}; - - // Query all image sources from the content - var getImageSources = function () { - var imageSources = []; - _.forEach(content, function (contentBlock) { - var html = HTMLValidizer.validize(contentBlock.text); - imageSources = imageSources.concat(_.map($(html).find('img'), function(element) { - return element.getAttribute('src'); - })); - }); - return imageSources; - }; - - // title - var identifier = motion.identifier ? ' ' + motion.identifier : ''; - var title = PDFLayout.createTitle( - gettextCatalog.getString('Motion') + identifier + ': ' + motion.getTitle() - ); - - // subtitle and sequential number - var subtitleLines = []; - if (motion.parent_id) { - var parentMotion = Motion.get(motion.parent_id); - subtitleLines.push( - gettextCatalog.getString('Amendment to motion') + ': ' + - (parentMotion.identifier ? parentMotion.identifier : parentMotion.getTitle()) - ); - } - if (Config.get('motions_export_sequential_number').value) { - subtitleLines.push(gettextCatalog.getString('Sequential number') + ': ' + motion.id); - } - var subtitle = PDFLayout.createSubtitle(subtitleLines); - - // meta data table - var metaTable = function() { - var metaTableBody = []; - - // submitters - var submitters = _.map(motion.submitters, function (submitter) { - return submitter.user.get_full_name(); - }).join(', '); - metaTableBody.push([ - { - text: gettextCatalog.getString('Submitters') + ':', - style: ['bold', 'grey'], - }, - { - text: submitters, - style: 'grey' - } - ]); - - // state - metaTableBody.push([ - { - text: gettextCatalog.getString('State') + ':', - style: ['bold', 'grey'] - }, - { - text: motion.getStateName(), - style: 'grey' - } - ]); - - // recommendation - if (motion.getRecommendationName()) { - metaTableBody.push([ - { - text: Config.get('motions_recommendations_by').value + ':', - style: ['bold', 'grey'] - }, - { - text: motion.getRecommendationName(), - style: 'grey' - } - ]); - } - - // category - if (motion.category) { - metaTableBody.push([ - { - text: gettextCatalog.getString('Category') + ':', - style: ['bold', 'grey'] }, - { - text: motion.category.prefix + ' - ' + motion.category.name, - style: 'grey' - } - ]); - } - - // build table - // Used placeholder for 'layout' functions whiche are - // replaced by lineWitdh/lineColor function in pfd-worker.js. - // TODO: Remove placeholder and us static values for LineWidth and LineColor - // if pdfmake has fixed this. - var metaTableJsonString = { - table: { - widths: ['30%','70%'], - body: metaTableBody, - }, - margin: [0, 0, 0, 20], - layout: '{{motion-placeholder-to-insert-functions-here}}' - }; - return metaTableJsonString; - }; - - var getContentBlockData = function (block) { - var data = []; - data.push({ - text: block.heading, - style: 'heading3', - marginTop: 25, - }); - data.push(converter.convertHTML(block.text)); - return data; - }; - - // Generates content as a pdfmake consumable - var getContent = function() { - var pdfContent = [ - title, - subtitle, - metaTable(), - ]; - _.forEach(content, function (contentBlock) { - pdfContent.push(getContentBlockData(contentBlock)); - }); - return pdfContent; - }; - - var getImageMap = function () { - return imageMap; - }; - - return $q(function (resolve, reject) { - ImageConverter.toBase64(getImageSources()).then(function (_imageMap) { - imageMap = _imageMap; - converter = PdfMakeConverter.createInstance(_imageMap); - resolve({ - getContent: getContent, - getImageMap: getImageMap, - }); - }, reject); - }); - }; - - return { - createInstance: createInstance - }; - } -]) - -.factory('PollContentProvider', [ - '$q', - 'PDFLayout', - 'gettextCatalog', - 'Config', - 'User', - 'ImageConverter', - function($q, PDFLayout, gettextCatalog, Config, User, ImageConverter) { - /** - * Generates a content provider for polls - * @constructor - * @param {string} title - title of poll - * @param {string} id - if of poll - */ - var createInstance = function(title, id) { - - var logoBallotPaperUrl = Config.get('logo_pdf_ballot_paper').value.path; - var imageMap = {}; - - // PDF header - var header = function() { - var columns = []; - - var text = Config.get('general_event_name').value; - columns.push({ - text: text, - fontSize: 8, - alignment: 'left', - width: '60%' - }); - - // logo - if (logoBallotPaperUrl) { - columns.push({ - image: logoBallotPaperUrl, - fit: [90,25], - alignment: 'right', - width: '40%' - }); - } - return { - color: '#555', - fontSize: 10, - margin: [30, 10, 10, -10], // [left, top, right, bottom] - columns: columns, - columnGap: 5 - }; - }; - - /** - * Returns a single section on the ballot paper - * @function - */ - var createSection = function() { - var sheetend = 40; - return { - stack: [ - header(), - { - text: gettextCatalog.getString('Motion') + ' ' + id, - style: 'title', - }, - { - text: title, - style: 'description' - }, - PDFLayout.createBallotEntry(gettextCatalog.getString('Yes')), - PDFLayout.createBallotEntry(gettextCatalog.getString('No')), - PDFLayout.createBallotEntry(gettextCatalog.getString('Abstain')), - ], - margin: [0, 0, 0, sheetend], - }; - }; - - /** - * Returns Content for single motion - * @function - * @param {string} id - if of poll - */ - var getContent = function() { - var content = []; - var amount; - var amount_method = Config.get('motions_pdf_ballot_papers_selection').value; - switch (amount_method) { - case 'NUMBER_OF_ALL_PARTICIPANTS': - amount = User.getAll().length; - break; - case 'NUMBER_OF_DELEGATES': - //TODO: assumption that DELEGATES is always group id 2. This may not be true - var group_id = 2; - amount = User.filter({where: {'groups_id': {contains:group_id} }}).length; - break; - case 'CUSTOM_NUMBER': - amount = Config.get('motions_pdf_ballot_papers_number').value; - break; - default: - // should not happen. - amount = 0; - } - var fullpages = Math.floor(amount / 8); - - for (var i=0; i < fullpages; i++) { - content.push({ - table: { - headerRows: 1, - widths: ['*', '*'], - body: [ - [createSection(), createSection()], - [createSection(), createSection()], - [createSection(), createSection()], - [createSection(), createSection()] - ], - pageBreak: 'after' - }, - layout: PDFLayout.getBallotLayoutLines(), - rowsperpage: 4 - }); - } - amount = amount - (fullpages * 8); - if (amount > 0) { - var partialpagebody = []; - while (amount > 1) { - partialpagebody.push([createSection(), createSection()]); - amount -=2; - } - if (amount == 1) { - partialpagebody.push([createSection(), '']); - } - content.push({ - table: { - headerRows: 1, - widths: ['50%', '50%'], - body: partialpagebody - }, - layout: PDFLayout.getBallotLayoutLines(), - rowsperpage: 4 - }); - } - return content; - }; - - var getImageMap = function () { - return imageMap; - }; - - return $q(function (resolve, reject) { - var imageSources = [ - logoBallotPaperUrl, - ]; - ImageConverter.toBase64(imageSources).then(function (_imageMap) { - imageMap = _imageMap; - resolve({ - getContent: getContent, - getImageMap: getImageMap, - }); - }, reject); - }); - }; - return { - createInstance: createInstance - }; - } -]) - -.factory('MotionCatalogContentProvider', [ - 'gettextCatalog', - 'PDFLayout', - 'Category', - 'Config', - function(gettextCatalog, PDFLayout, Category, Config) { - /** - * Constructor - * @function - * @param {object} allMotions - A sorted array of all motions to parse - * @param {string} sorting - The way the catalog has been sorted. Necessary for ToC - */ - var createInstance = function(allMotions, sorting) { - - var title = PDFLayout.createTitle( - Config.translate(Config.get('motions_export_title').value) - ); - - var createPreamble = function() { - var preambleText = Config.get('motions_export_preamble').value; - if (preambleText) { - return { - text: preambleText, - style: "preamble" - }; - } else { - return ""; - } - }; - - var createTOContent = function() { - var toc = []; - var exportCategory = (sorting === 'identifier' || sorting === 'category.prefix'); - var uniqueCategories = getUniqueCategories(); - var tocTitle = { - text: gettextCatalog.getString('Table of contents'), - style: 'heading2' - }; - - // all motions need a page ID. We use the motion identifier for that - _.forEach(allMotions, function (motion) { - motion.getContent()[0].id = ''+motion.getId(); - }); - - if (exportCategory && uniqueCategories) { - // own table per category - var catTocBody = []; - _.forEach(uniqueCategories, function (category) { - // push the name of the category - // make a table for correct alignment - catTocBody.push({ - table: { - body: [ - [ - { - text: category.prefix + ' - ' + category.name, - style: 'tocCategoryTitle' - } - ], - ] - }, - layout: 'noBorders', - }); - - var tocBody = []; - _.forEach(allMotions, function (motion) { - if (motion.getCategory() && category.name === motion.getCategory().name) { - tocBody.push(tocLine(motion, 'tocCategoryEntry')); - } - }); - catTocBody.push(tocTable(tocBody)); - }); - - //handle thouse without category - var uncatTocBody = []; - _.forEach(allMotions, function (motion) { - if (!motion.getCategory()) { - uncatTocBody.push(tocLine(motion, 'tocEntry')); - } - }); - - // only push this array if there is at least one entry - if (uncatTocBody.length > 0) { - catTocBody.push(tocTable(uncatTocBody)); - } - - toc.push(catTocBody); - } else { - // all categories in the same table - var tocBody = []; - _.forEach(allMotions, function (motion) { - tocBody.push(tocLine(motion, 'tocEntry')); - }); - toc.push(tocTable(tocBody)); - } - - return [ - tocTitle, - toc, - PDFLayout.addPageBreak() - ]; - }; - - // creates a new table of contents table body - var tocTable = function (tocBody) { - return { - table: { - widths: ['auto', '*', 'auto'], - body: tocBody - }, - layout: 'noBorders', - style: 'tocCategorySection' - }; - }; - - // generates a line in the toc as list-object - var tocLine = function (motion, style) { - var firstColumn = ""; - if (motion.getIdentifier()) { - firstColumn = motion.getIdentifier(); - } - return [ - { - text: firstColumn, - style: style - }, - { - text: motion.getTitle(), - style: 'tocEntry' - }, - { - pageReference: ''+motion.getId(), - style: 'tocEntry', - alignment: 'right' - }, - ]; - }; - - // returns a list of unique category names - // necessary to create a ToC with categories - // if a motions without category is found, - // a corresponding entry should be added aswell - var getUniqueCategories = function() { - var categories = []; - _.forEach(allMotions, function (motion) { - if (motion.getCategory()) { - categories.push( - { - name: motion.getCategory().name, - prefix: motion.getCategory().prefix - } - ); - } - }); - return _.uniqBy(categories, 'name'); - }; - - // returns the pure content of the motion, parseable by pdfmake - var getContent = function() { - var motionContent = []; - _.forEach(allMotions, function(motion, key) { - motionContent.push(motion.getContent()); - if (key < allMotions.length - 1) { - motionContent.push(PDFLayout.addPageBreak()); - } - }); - var content = []; - // print extra data (title, preamble, categories, toc) only for more than 1 motion - if (allMotions.length > 1) { - content.push( - title, - createPreamble(), - createTOContent() - ); - } - content.push(motionContent); - return content; - }; - - var getImageMap = function () { - var imageMap = {}; - _.forEach(allMotions, function (motion) { - _.forEach(motion.getImageMap(), function (data, path) { - if (!imageMap[path]) { - imageMap[path] = data; - } - }); - }); - return imageMap; - }; - - return { - getContent: getContent, - getImageMap: getImageMap, - }; - }; - - return { - createInstance: createInstance - }; - } -]) - -.factory('AmendmentContentProvider', [ - '$q', - 'ImageConverter', - 'PdfMakeConverter', - 'HTMLValidizer', - 'PDFLayout', - 'Config', - 'gettextCatalog', - function ($q, ImageConverter, PdfMakeConverter, HTMLValidizer, PDFLayout, Config, gettextCatalog) { - var createInstance = function (motions) { - motions = _.filter(motions, function (motion) { - return motion.parent_id; - }); - - var converter, imageMap = {}; - - // Query all image sources from motion text and reason - var getImageSources = function () { - var sources = []; - _.forEach(motions, function (motion) { - var text = motion.getText(); - var reason = motion.getReason(); - var content = HTMLValidizer.validize(text) + HTMLValidizer.validize(motion.getReason()); - _.forEach($(content).find('img'), function (element) { - sources.push(element.getAttribute('src')); - }); - }); - return _.uniq(sources); - }; - - var createBundleContent = function (bundle) { - return _.flatten(_.map(bundle, function (motion) { - var content = []; - - // get diffs and title of the changed motions - var motionText; - var title = motion.identifier ? gettextCatalog.getString('Motion') + ' ' + motion.identifier : motion.getTitle(); - if (motion.isParagraphBasedAmendment()) { - // get changed parts - var paragraphs = motion.getAmendmentParagraphsLinesDiff(); - if (paragraphs.length) { - // Put the changed lines into the info column - var p = paragraphs[0]; - title += ' (' + gettextCatalog.getString('Line') + ' '; - if (p.diffLineTo === p.diffLineFrom + 1) { - title += p.diffLineFrom; - } else { - title += p.diffLineFrom + '-' + p.diffLineTo; - } - title += ')'; - - // get the diff - motionText = p.text; - } else { - motionText = gettextCatalog.getString('No changes at the text.'); - } - } else { // 'normal' amendment - motionText = motion.getText(); - } - content.push({ - text: title, - style: 'heading3', - marginTop: 15, - }); - - // submitters - var submitters = _.map(motion.submitters, function (submitter) { - return submitter.user.get_full_name(); - }).join(', '); - content.push({ - text: gettextCatalog.getString('Submitters') + ': ' + submitters, - }); - - // state - content.push({ - text: gettextCatalog.getString('State') + ': ' + motion.getStateName(), - }); - - // recommendation - var recommendations_by = Config.get('motions_recommendations_by').value; - var recommendation = motion.getRecommendationName(); - if (recommendations_by && recommendation) { - content.push({ - text: recommendations_by + ': ' + recommendation, - }); - } - - return _.concat(content, converter.convertHTML(motionText, 'outside')); - })); - }; - - var getBundleContent = function (bundle) { - var leadMotion = bundle[0].getParentMotion(); - // title - var title = leadMotion.identifier ? ' ' + leadMotion.identifier : ''; - title += ': ' + leadMotion.getTitle(); - title = PDFLayout.createTitle(gettextCatalog.getString('Amendments to motion') + title); - - var content = [title], - foundAmendments = []; - - var headings = leadMotion.getTextHeadings().map(function(heading) { - heading.amendments = []; - return heading; - }); - bundle.forEach(function(amendment) { - var headingIdx = null; - var changes = amendment.getAmendmentParagraphsByMode('diff'); - if (changes.length === 0) { - return; - } - var amendmentLineNumber = changes[0].lineFrom; - for (var i = 0; i < headings.length; i++) { - if (headings[i].lineNumber <= amendmentLineNumber) { - headingIdx = i; - } - } - if (headingIdx !== null) { - headings[headingIdx].amendments.push(amendment); - foundAmendments.push(amendment.id); - } - }); - - headings.forEach(function(heading) { - if (heading.amendments.length === 0) { - return; - } - content.push({ - text: heading.text, - style: "heading2", - marginTop: 25, - }); - content = _.concat(content, createBundleContent(heading.amendments)); - }); - - // If there was an amendment that did not have a heading, we append it at the bottom - var missedAmendments = []; - bundle.forEach(function(amendment) { - if (foundAmendments.indexOf(amendment.id) === -1) { - missedAmendments.push(amendment); - } - }); - if (missedAmendments.length > 0) { - content = _.concat(content, createBundleContent(missedAmendments)); - } - - return content; - }; - - // Generates content as a pdfmake consumable - var getContent = function() { - if (motions.length === 0) { - return []; - } - - // Creates bundles of motions. All motions with the same parent are bundled together - // respecting the order, in which they are sorted. - // motionBundles is an array containing Arrays of motions with the same parent. - var parentId = motions[0].parent_id; - var motionBundles = []; - var currentBundle = []; - _.forEach(motions, function (motion) { - if (motion.parent_id === parentId) { - currentBundle.push(motion); - } else { - motionBundles.push(currentBundle); - currentBundle = [motion]; - parentId = motion.parent_id; - } - }); - motionBundles.push(currentBundle); - - // Make the amendment table for each motion bundle. - return _.map(motionBundles, function (bundle, index) { - var content = getBundleContent(bundle); - if (index < motionBundles.length - 1) { - content.push(PDFLayout.addPageBreak()); - } - return content; - }); - }; - - var getImageMap = function() { - return imageMap; - }; - - return $q(function (resolve) { - ImageConverter.toBase64(getImageSources()).then(function (_imageMap) { - imageMap = _imageMap; - converter = PdfMakeConverter.createInstance(_imageMap); - resolve({ - getContent: getContent, - getImageMap: getImageMap, - }); - }); - }); - }; - - return { - createInstance: createInstance, - }; - } -]) - -.factory('MotionPdfExport', [ - '$http', - '$q', - 'operator', - 'Config', - 'gettextCatalog', - 'MotionChangeRecommendation', - 'HTMLValidizer', - 'PdfMakeConverter', - 'MotionContentProvider', - 'MotionCatalogContentProvider', - 'PdfMakeDocumentProvider', - 'PollContentProvider', - 'PdfMakeBallotPaperProvider', - 'MotionPartialContentProvider', - 'AmendmentContentProvider', - 'PdfCreate', - 'PDFLayout', - 'PersonalNoteManager', - 'MotionComment', - 'Messaging', - 'FileSaver', - function ($http, $q, operator, Config, gettextCatalog, MotionChangeRecommendation, HTMLValidizer, - PdfMakeConverter, MotionContentProvider, MotionCatalogContentProvider, PdfMakeDocumentProvider, - PollContentProvider, PdfMakeBallotPaperProvider, MotionPartialContentProvider, AmendmentContentProvider, - PdfCreate, PDFLayout, PersonalNoteManager, MotionComment, Messaging, FileSaver) { - return { - getDocumentProvider: function (motions, params, singleMotion) { - params = _.clone(params || {}); // Clone this to avoid sideeffects. - - if (singleMotion) { - _.defaults(params, { - version: motions.active_version, - }); - motions = [motions]; - } - - //save the arrays of all motions to an array - angular.forEach(motions, function (motion) { - if (singleMotion) { - motion.changeRecommendations = MotionChangeRecommendation.filter({ - 'where': {'motion_version_id': {'==': params.version}} - }); - } else { - motion.changeRecommendations = MotionChangeRecommendation.filter({ - 'where': {'motion_version_id': {'==': motion.active_version}} - }); - } - }); - - var motionContentProviderArray = []; - var motionContentProviderPromises = _.map(motions, function (motion) { - var version = (singleMotion ? params.version : motion.active_version); - return $q(function (resolve, reject) { - MotionContentProvider.createInstance( - motion, version, params - ).then(function (contentProvider) { - motionContentProviderArray.push(contentProvider); - resolve(); - }, reject); - }); - }); - - return $q(function (resolve, reject) { - $q.all(motionContentProviderPromises).then(function() { - var documentProviderPromise; - if (singleMotion) { - documentProviderPromise = PdfMakeDocumentProvider.createInstance(motionContentProviderArray[0]); - } else { - var motionCatalogContentProvider = MotionCatalogContentProvider.createInstance(motionContentProviderArray, params.column); - documentProviderPromise = PdfMakeDocumentProvider.createInstance(motionCatalogContentProvider); - } - documentProviderPromise.then(function (documentProvider) { - resolve(documentProvider); - }, reject); - }, reject); - }); - }, - export: function (motions, params, singleMotion) { - params = params || {}; - params.filename = gettextCatalog.getString('motions') + '.pdf'; - this.getDocumentProvider(motions, params, singleMotion).then( - function (documentProvider) { - PdfCreate.download(documentProvider, params.filename); - }, function (error) { - Messaging.addMessage(error.msg, 'error'); - } - ); - }, - exportZip: function (motions, params) { - var messageId = Messaging.addMessage('' + - gettextCatalog.getString('Generating PDFs and ZIP archive') + ' ...', 'info'); - var zipFilename = params.filename || gettextCatalog.getString('motions') + '.zip'; - params.filename = void 0; // clear this, so we do not override the default filenames for each pdf. - - var self = this; - var usedFilenames = []; - var docMap = {}; - var docPromises = _.map(motions, function (motion) { - var identifier = motion.identifier ? '-' + motion.identifier : ''; - var filename = gettextCatalog.getString('Motion') + identifier; - - // If the filename is already in use, try to append a number to it (like '(2)') - if (_.includes(usedFilenames, filename)) { - var i = 1; - var filenameWithNumber = filename; - while(_.includes(usedFilenames, filenameWithNumber)) { - filenameWithNumber = filename + ' (' + i + ')'; - i++; - } - filename = filenameWithNumber; - } - usedFilenames.push(filename); - filename += '.pdf'; - - return $q(function (resolve, reject) { - // get documentProvider for every motion. - self.getDocumentProvider(motion, params, true).then(function (documentProvider) { - docMap[filename] = documentProvider; - resolve(); - }, reject); - }); - }); - $q.all(docPromises).then(function () { - PdfCreate.getBase64FromMultipleDocuments(docMap).then(function (pdfMap) { - var zip = new JSZip(); - _.forEach(pdfMap, function (data, filename) { - zip.file(filename, data, {base64: true}); - }); - Messaging.createOrEditMessage(messageId, '' + - gettextCatalog.getString('ZIP successfully generated.'), 'success', {timeout: 3000}); - zip.generateAsync({type: 'blob'}).then(function (content) { - FileSaver.saveAs(content, zipFilename); - }); - }, function (error) { - Messaging.createOrEditMessage(messageId, '' + gettextCatalog.getString('Error while generating ZIP file') + - ':' + error + '
', 'error');
- });
- }, function (error) {
- Messaging.createOrEditMessage(messageId, error.msg, 'error');
- });
- },
- createPollPdf: function (motion, version) {
- var id = motion.identifier.replace(' ', '');
- var title = motion.getTitle(version);
- var filename = gettextCatalog.getString('Motion') + '-' + id + '-' + gettextCatalog.getString('ballot-paper') + '.pdf';
- PollContentProvider.createInstance(title, id).then(function (pollContentProvider) {
- var documentProvider = PdfMakeBallotPaperProvider.createInstance(pollContentProvider);
- PdfCreate.download(documentProvider, filename);
- }, function (error) {
- Messaging.addMessage(error.msg, 'error');
- });
- },
- exportPersonalNote: function (motion, filename) {
- var personalNote = PersonalNoteManager.getNote(motion);
- var content = [{
- heading: gettextCatalog.getString('Personal note'),
- text: personalNote ? personalNote.note : '',
- }];
- MotionPartialContentProvider.createInstance(motion, content).then(function (contentProvider) {
- PdfMakeDocumentProvider.createInstance(contentProvider).then(function (documentProvider) {
- PdfCreate.download(documentProvider, filename);
- }, function (error) {
- Messaging.addMessage(error.msg, 'error');
- });
- }, function (error) {
- Messaging.addMessage(error.msg, 'error');
- });
- },
- exportComment: function (motion, commentId, filename) {
- var field = MotionComment.getNoSpecialCommentsFields()[commentId];
- if (field && motion.comments[commentId]) {
- var title = field.name;
- if (!field.public) {
- title += ' (' + gettextCatalog.getString('internal') + ')';
- }
- var content = [{
- heading: title,
- text: motion.comments[commentId],
- }];
- MotionPartialContentProvider.createInstance(motion, content).then(function (contentProvider) {
- PdfMakeDocumentProvider.createInstance(contentProvider).then(function (documentProvider) {
- PdfCreate.download(documentProvider, filename);
- }, function (error) {
- Messaging.addMessage(error.msg, 'error');
- });
- }, function (error) {
- Messaging.addMessage(error.msg, 'error');
- });
- }
- },
- exportAmendments: function (motions, filename) {
- AmendmentContentProvider.createInstance(motions).then(function (contentProvider) {
- PdfMakeDocumentProvider.createInstance(contentProvider).then(function (documentProvider) {
- PdfCreate.download(documentProvider, filename);
- });
- });
- },
- };
- }
-]);
-
-}());
diff --git a/openslides/motions/static/js/motions/projector.js b/openslides/motions/static/js/motions/projector.js
deleted file mode 100644
index f4af95f03..000000000
--- a/openslides/motions/static/js/motions/projector.js
+++ /dev/null
@@ -1,98 +0,0 @@
-(function () {
-
-'use strict';
-
-angular.module('OpenSlidesApp.motions.projector', [
- 'OpenSlidesApp.motions',
- 'OpenSlidesApp.motions.motionservices',
- 'OpenSlidesApp.motions.motionBlockProjector',
-])
-
-.config([
- 'slidesProvider',
- function(slidesProvider) {
- slidesProvider.registerSlide('motions/motion', {
- template: 'static/templates/motions/slide_motion.html',
- });
- }
-])
-
-.controller('SlideMotionCtrl', [
- '$scope',
- '$timeout',
- 'Config',
- 'Motion',
- 'MotionChangeRecommendation',
- 'ChangeRecommendationView',
- 'User',
- 'Notify',
- 'ProjectorID',
- 'MotionPollDecimalPlaces',
- function($scope, $timeout, Config, Motion, MotionChangeRecommendation,
- ChangeRecommendationView, User, Notify, ProjectorID, MotionPollDecimalPlaces) {
- // 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
- // class.
- var motionId = $scope.element.id;
- $scope.mode = $scope.element.mode || 'original';
- $scope.lineNumberMode = Config.get('motions_default_line_numbering').value;
-
- var notifyNamePrefix = 'projector_' + ProjectorID() + '_motion_line_';
- var callbackId = Notify.registerCallback(notifyNamePrefix + 'request', function (params) {
- var line = params.params.line;
- if (!line) {
- return;
- }
- $scope.highlight = line;
- $timeout(function () {
- $scope.highlight = 0;
- }, 4000);
-
- var scrollTop = null;
- $('.line-number-' + line).each(function() {
- var top = $(this).offset().top;
- if (scrollTop === null || top < scrollTop) {
- scrollTop = top;
- }
- });
- if (scrollTop) {
- scrollTop += (-$scope.scroll); // Add the (reversed) scrolling ontop
- var scroll = Math.floor((scrollTop/250) - 0.2);
- var channel = params.senderReplyChannelName;
- Notify.notify(notifyNamePrefix + 'answer', {scroll: scroll}, null, [channel], null);
- }
- });
- $scope.$on('$destroy', function () {
- Notify.deregisterCallback(callbackId);
- });
-
- User.bindAll({}, $scope, 'users');
-
- $scope.$watch(function () {
- return Motion.lastModified(motionId);
- }, function () {
- $scope.motion = Motion.get(motionId);
- $scope.amendment_diff_paragraphs = $scope.motion.getAmendmentParagraphsLinesDiff();
- $scope.viewChangeRecommendations.setVersion($scope.motion, $scope.motion.active_version);
- _.forEach($scope.motion.polls, function (poll) {
- MotionPollDecimalPlaces.getPlaces(poll, true).then(function (decimalPlaces) {
- precisionCache[poll.id] = decimalPlaces;
- });
- });
- });
-
- var precisionCache = {};
- $scope.getPollVotesPrecision = function (poll) {
- if (!precisionCache[poll.id]) {
- return 0;
- }
- return precisionCache[poll.id];
- };
-
- // Change recommendation viewing
- $scope.viewChangeRecommendations = ChangeRecommendationView;
- $scope.viewChangeRecommendations.initProjector($scope, Motion.get(motionId), $scope.mode);
- }
-]);
-
-}());
diff --git a/openslides/motions/static/js/motions/site.js b/openslides/motions/static/js/motions/site.js
deleted file mode 100644
index de91f8bcf..000000000
--- a/openslides/motions/static/js/motions/site.js
+++ /dev/null
@@ -1,3326 +0,0 @@
-(function () {
-
-'use strict';
-
-angular.module('OpenSlidesApp.motions.site', [
- 'OpenSlidesApp.motions',
- 'OpenSlidesApp.motions.motionservices',
- 'OpenSlidesApp.poll.majority',
- 'OpenSlidesApp.core.pdf',
- 'OpenSlidesApp.motions.docx',
- 'OpenSlidesApp.motions.pdf',
- 'OpenSlidesApp.motions.csv',
- 'OpenSlidesApp.motions.workflow',
-])
-
-.config([
- 'mainMenuProvider',
- 'gettext',
- function (mainMenuProvider, gettext) {
- mainMenuProvider.register({
- 'ui_sref': 'motions.motion.list',
- 'img_class': 'file-text',
- 'title': gettext('Motions'),
- 'weight': 300,
- 'perm': 'motions.can_see',
- });
- }
-])
-
-.config([
- 'SearchProvider',
- 'gettext',
- function (SearchProvider, gettext) {
- SearchProvider.register({
- 'verboseName': gettext('Motions'),
- 'collectionName': 'motions/motion',
- 'urlDetailState': 'motions.motion.detail',
- 'weight': 300,
- });
- }
-])
-
-.config([
- '$stateProvider',
- 'gettext',
- function($stateProvider, gettext) {
- $stateProvider
- .state('motions', {
- url: '/motions',
- abstract: true,
- template: "' + Config.translate(Config.get('motions_preamble').value) + '
' - }, - { - key: 'text', - type: 'editor', - templateOptions: { - label: gettextCatalog.getString('Text'), - required: !isParagraphBasedAmendment // Deleting the whole paragraph in an amendment should be possible - }, - data: { - ckeditorOptions: Editor.getOptions() - } - }, - { - key: 'reason', - type: 'editor', - templateOptions: { - label: gettextCatalog.getString('Reason'), - }, - data: { - ckeditorOptions: Editor.getOptions() - } - }, - { - key: 'disable_versioning', - type: 'checkbox', - templateOptions: { - label: gettextCatalog.getString('Trivial change'), - description: gettextCatalog.getString("Don't create a new version.") - }, - hide: true - } - ]); - - // show as agenda item + parent item - if (isCreateForm) { - formFields.push(ShowAsAgendaItemField('motions.can_manage')); - formFields.push({ - key: 'agenda_parent_id', - type: 'select-single', - templateOptions: { - label: gettextCatalog.getString('Parent item'), - options: AgendaTree.getFlatTree(Agenda.getAll()), - ngOptions: 'item.id as item.getListViewTitle() for item in to.options | notself : model.agenda_item_id', - placeholder: gettextCatalog.getString('Select a parent item ...') - }, - hide: !operator.hasPerms('agenda.can_manage') - }); - } - - // motion comments - formFields = formFields.concat(MotionComment.getFormFields()); - - // more - formFields.push( - { - key: 'more', - type: 'checkbox', - templateOptions: { - label: gettextCatalog.getString('Show extended fields') - }, - hide: !operator.hasPerms('motions.can_manage') - }, - { - template: '')) { - motion.text = '
' + motion.text + '
'; - } - // Reason - if (motion.reason && !motion.reason.startsWith('')) { - motion.reason = '
' + motion.reason + '
'; - } - // submitter - if (motion.submitter && motion.submitter !== '') { - _.forEach(User.getAll(), function (user) { - var user_short_name = [user.title, user.first_name, user.last_name].join(' ').trim(); - if (user_short_name == motion.submitter.trim()) { - motion.submitters_id = [user.id]; - motion.submitter = user.full_name; - } - }); - if (!motion.submitters_id) { - motion.submitter_create = gettext('New participant will be created.'); - } - } - // category - if (motion.category && motion.category !== '') { - angular.forEach(Category.getAll(), function (category) { - // search for existing category - if (category.name == motion.category.trim()) { - motion.category_id = category.id; - motion.category = category.name; - } - }); - if (!motion.category_id) { - motion.category_create = gettext('New category will be created.'); - } - } - // Motion block - if (motion.motionBlock && motion.motionBlock !== '') { - angular.forEach(MotionBlock.getAll(), function (block) { - // search for existing block - if (block.title == motion.motionBlock.trim()) { - motion.motion_block_id = block.id; - motion.motionBlock = block.title; - } - }); - if (!motion.motion_block_id) { - motion.motionBlock_create = gettext('New motion block will be created.'); - } - } - - $scope.motions.push(motion); - }); - $scope.calcStats(); - }; - - $scope.calcStats = function () { - $scope.motionsWillNotBeImported = 0; - $scope.motionsWillBeImported = 0; - - $scope.motions.forEach(function(motion) { - if (!motion.importerror && motion.selected) { - $scope.motionsWillBeImported++; - } else { - $scope.motionsWillNotBeImported++; - } - }); - }; - - // Counter for creations - $scope.usersCreated = 0; - $scope.categoriesCreated = 0; - - // import from csv file - $scope.import = function () { - $scope.csvImporting = true; - - // Reset counters - $scope.usersCreated = 0; - $scope.categoriesCreated = 0; - $scope.motionBlocksCreated = 0; - - var importedUsers = []; - var importedCategories = []; - var importedMotionBlocks = []; - // collect users, categories and motion blocks - angular.forEach($scope.motions, function (motion) { - if (motion.selected && !motion.importerror) { - // collect user if not exists - if (!motion.submitters_id && motion.submitter) { - var index = motion.submitter.indexOf(' '); - var user = { - first_name: motion.submitter.substr(0, index), - last_name: motion.submitter.substr(index+1), - groups_id: [] - }; - importedUsers.push(user); - } - // collect category if not exists - if (!motion.category_id && motion.category) { - var category = { - name: motion.category, - prefix: motion.category.charAt(0) - }; - importedCategories.push(category); - } - // collect motion block if not exists - if (!motion.motion_block_id && motion.motionBlock) { - var motionBlock = { - title: motion.motionBlock, - }; - importedMotionBlocks.push(motionBlock); - } - } - }); - - // unique users, categories and motion blocks - var importedUsersUnique = _.uniqWith(importedUsers, function (u1, u2) { - return u1.first_name == u2.first_name && - u1.last_name == u2.last_name; - }); - var importedCategoriesUnique = _.uniqWith(importedCategories, function (c1, c2) { - return c1.name == c2.name; - }); - var importedMotionBlocksUnique = _.uniqWith(importedMotionBlocks, function (c1, c2) { - return c1.title == c2.title; - }); - - // Promises for users and categories - var createPromises = []; - - // create users and categories - _.forEach(importedUsersUnique, function (user) { - createPromises.push(User.create(user).then( - function (success) { - user.id = success.id; - $scope.usersCreated++; - } - )); - }); - _.forEach(importedCategoriesUnique, function (category) { - createPromises.push(Category.create(category).then( - function (success) { - category.id = success.id; - $scope.categoriesCreated++; - } - )); - }); - _.forEach(importedMotionBlocksUnique, function (motionBlock) { - createPromises.push(MotionBlock.create(motionBlock).then( - function (success) { - motionBlock.id = success.id; - $scope.motionBlocksCreated++; - } - )); - }); - - // wait for users and categories to create - $q.all(createPromises).then( function() { - angular.forEach($scope.motions, function (motion) { - if (motion.selected && !motion.importerror) { - // now, add user - if (!motion.submitters_id && motion.submitter) { - var index = motion.submitter.indexOf(' '); - var first_name = motion.submitter.substr(0, index); - var last_name = motion.submitter.substr(index+1); - - // search for user, set id. - _.forEach(importedUsersUnique, function (user) { - if (user.first_name == first_name && - user.last_name == last_name) { - motion.submitters_id = [user.id]; - } - }); - } - // add category - if (!motion.category_id && motion.category) { - var name = motion.category; - - // search for category, set id. - _.forEach(importedCategoriesUnique, function (category) { - if (category.name == name) { - motion.category_id = category.id; - } - }); - } - // add motion block - if (!motion.motion_block_id && motion.motionBlock) { - var title = motion.motionBlock; - - // search for motion block - _.forEach(importedMotionBlocksUnique, function (motionBlock) { - if (motionBlock.title == title) { - motion.motion_block_id = motionBlock.id; - } - }); - } - - - // finally create motion - Motion.create(motion).then( - function(success) { - motion.imported = true; - } - ); - } - }); - }); - $scope.csvimported = true; - }; - $scope.clear = function () { - $scope.motions = []; - }; - // download CSV example file - $scope.downloadCSVExample = function () { - MotionCsvExport.downloadExample(); - }; - } -]) - -.controller('CategoryListCtrl', [ - '$scope', - 'Category', - 'ngDialog', - 'CategoryForm', - function($scope, Category, ngDialog, CategoryForm) { - Category.bindAll({}, $scope, 'categories'); - - // setup table sorting - $scope.sortColumn = 'name'; - $scope.reverse = false; - // function to sort by clicked column - $scope.toggleSort = function (column) { - if ($scope.sortColumn === column) { - $scope.reverse = !$scope.reverse; - } - $scope.sortColumn = column; - }; - - // delete selected category - $scope.delete = function (category) { - Category.destroy(category.id); - }; - $scope.editOrCreate = function (category) { - ngDialog.open(CategoryForm.getDialog(category)); - }; - } -]) - -.controller('CategoryCreateCtrl', [ - '$scope', - 'Category', - 'CategoryForm', - 'ErrorMessage', - function($scope, Category, CategoryForm, ErrorMessage) { - $scope.model = {}; - $scope.alert = {}; - $scope.formFields = CategoryForm.getFormFields(); - $scope.save = function (category) { - Category.create(category).then( - function (success) { - $scope.closeThisDialog(); - }, - function (error) { - $scope.alert = ErrorMessage.forAlert(error); - } - ); - }; - } -]) - -.controller('CategoryUpdateCtrl', [ - '$scope', - 'Category', - 'categoryId', - 'CategoryForm', - 'ErrorMessage', - function ($scope, Category, categoryId, CategoryForm, ErrorMessage) { - $scope.alert = {}; - $scope.model = angular.copy(Category.get(categoryId)); - $scope.formFields = CategoryForm.getFormFields(); - $scope.save = function (category) { - Category.inject(category); - Category.save(category).then( - function (success) { - $scope.closeThisDialog(); - }, - function (error) { - // save error: revert all changes by restore - // (refresh) original category object from server - Category.refresh(category); - $scope.alert = ErrorMessage.forAlert(error); - } - ); - }; - } -]) - -.controller('CategorySortCtrl', [ - '$scope', - '$stateParams', - '$http', - 'Category', - 'categoryId', - 'Motion', - 'ErrorMessage', - function ($scope, $stateParams, $http, Category, categoryId, Motion, ErrorMessage) { - Category.bindOne(categoryId, $scope, 'category'); - Motion.bindAll({}, $scope, 'motions'); - $scope.filter = { category_id: categoryId, - parent_id: null, - orderBy: 'identifier' }; - - $scope.$watch(function () { - return Motion.lastModified(); - }, function () { - var motions = Motion.filter($scope.filter); - $scope.items = _.map(motions, function (motion) { - return { - id: motion.id, - item: motion - }; - }); - }); - - $scope.alert = {}; - // Numbers all motions in this category by the given order in $scope.items - $scope.numbering = function () { - // Create a list of all motion ids in the current order. - var sorted_motions = []; - $scope.items.forEach(function (item) { - sorted_motions.push(item.item.id); - }); - - // renumber them - $http.post('/rest/motions/category/' + $scope.category.id + '/numbering/', - {'motions': sorted_motions} ).then( - function (success) { - $scope.alert = { type: 'success', msg: success.data.detail, show: true }; - }, function (error) { - $scope.alert = ErrorMessage.forAlert(error); - }); - }; - } -]) - -//mark all motions config strings for translation in javascript -.config([ - 'gettext', - function (gettext) { - gettext('Motions'); - - // subgroup General - gettext('General'); - gettext('Workflow of new motions'); - gettext('Identifier'); - gettext('Numbered per category'); - gettext('Serially numbered'); - gettext('Set it manually'); - gettext('Motion preamble'); - gettext('The assembly may decide:'); - gettext('Default line numbering'); - /// Line numbering: Outside - gettext('Outside'); - /// Line numbering: Inline - gettext('Inline'); - /// Line numbering: None - gettext('None'); - gettext('Line length'); - gettext('The maximum number of characters per line. Relevant when line numbering is enabled. Min: 40'); - gettext('Hide reason on projector'); - gettext('Hide meta information box on projector'); - gettext('Hide recommendation on projector'); - gettext('Stop submitting new motions by non-staff users'); - gettext('Allow to disable versioning'); - gettext('Name of recommender'); - gettext('Default text version for change recommendations'); - gettext('Will be displayed as label before selected recommendation. Use an empty value to disable the recommendation system.'); - gettext('Edit comment %%comment%% of motion %%motion%%'); - - // subgroup Amendments - gettext('Amendments'); - gettext('Activate amendments'); - gettext('Show amendments together with motions'); - gettext('Prefix for the identifier for amendments'); - gettext('Apply text for new amendments'); - gettext('The title of the motion is always applied.'); - gettext('Amendment to'); - gettext('How to create new amendments'); - gettext('Empty text field'); - gettext('Edit the whole motion text'); - gettext('Paragraph-based, Diff-enabled'); - - // subgroup Supporters - gettext('Supporters'); - gettext('Number of (minimum) required supporters for a motion'); - gettext('Choose 0 to disable the supporting system.'); - gettext('Remove all supporters of a motion if a submitter edits his ' + - 'motion in early state'); - - // subgroup Supporters - gettext('Comments'); - gettext('Comment fields for motions'); - gettext('Public'); - gettext('Private'); - - // subgroup Voting and ballot papers - gettext('Voting and ballot papers'); - gettext('The 100 % base of a voting result consists of'); - gettext('Yes/No/Abstain'); - gettext('Yes/No'); - gettext('All valid ballots'); - gettext('All casted ballots'); - gettext('Disabled (no percents)'); - gettext('Required majority'); - gettext('Default method to check whether a motion has reached the required majority.'); - gettext('Simple majority'); - gettext('Two-thirds majority'); - gettext('Three-quarters majority'); - gettext('Disabled'); - gettext('Number of ballot papers (selection)'); - gettext('Number of all delegates'); - gettext('Number of all participants'); - gettext('Use the following custom number'); - gettext('Custom number of ballot papers'); - - // subgroup PDF and DOCX - gettext('Title for PDF and DOCX documents (all motions)'); - gettext('Preamble text for PDF and DOCX documents (all motions)'); - gettext('Sort categories by'); - gettext('Include the sequential number in PDF and DOCX'); - - // misc strings (used dynamically in templates by translate filter) - gettext('needed'); - gettext('Amendment'); - } -]); - -}()); diff --git a/openslides/motions/static/js/motions/workflow.js b/openslides/motions/static/js/motions/workflow.js deleted file mode 100644 index 79ace06ed..000000000 --- a/openslides/motions/static/js/motions/workflow.js +++ /dev/null @@ -1,253 +0,0 @@ -(function () { - -'use strict'; - -angular.module('OpenSlidesApp.motions.workflow', []) - -.controller('WorkflowListCtrl', [ - '$scope', - 'Workflow', - 'ngDialog', - 'ErrorMessage', - function ($scope, Workflow, ngDialog, ErrorMessage) { - $scope.alert = {}; - Workflow.bindAll({}, $scope, 'workflows'); - $scope.create = function () { - ngDialog.open({ - template: 'static/templates/motions/workflow-edit.html', - controller: 'WorkflowCreateCtrl', - className: 'ngdialog-theme-default wide-form', - closeByEscape: false, - closeByDocument: false, - }); - }; - $scope.delete = function (workflow) { - Workflow.destroy(workflow).then(null, function (error) { - $scope.alert = ErrorMessage.forAlert(error); - }); - }; - } -]) - -.controller('WorkflowDetailCtrl', [ - '$scope', - '$sessionStorage', - 'permissions', - 'Workflow', - 'MotionState', - 'workflowId', - 'ngDialog', - 'gettext', - 'gettextCatalog', - 'ErrorMessage', - function ($scope, $sessionStorage, permissions, Workflow, MotionState, workflowId, - ngDialog, gettext, gettextCatalog, ErrorMessage) { - $scope.permissions = permissions; - $scope.alert = {}; - - $scope.$watch(function () { - return Workflow.lastModified(workflowId); - }, function () { - $scope.workflow = Workflow.get(workflowId); - $scope.states = $scope.workflow.states; - $scope.states = _.orderBy($scope.states, 'id'); - _.forEach($scope.states, function (state) { - state.newActionWord = gettextCatalog.getString(state.action_word); - state.newRecommendationLabel = gettextCatalog.getString(state.recommendation_label); - }); - }); - - $scope.booleanMembers = [ - {name: 'allow_support', - displayName: gettext('Allow support'),}, - {name: 'allow_create_poll', - displayName: gettext('Allow create poll'),}, - {name: 'allow_submitter_edit', - displayName: gettext('Allow submitter edit'),}, - {name: 'versioning', - displayName: gettext('Versioning'),}, - {name: 'leave_old_version_active', - displayName: gettext('Leave old version active'),}, - {name: 'dont_set_identifier', - displayName: gettext('Set identifier'), - inverse: true,}, - {name: 'show_state_extension_field', - displayName: gettext('Show state extension field'),}, - {name: 'show_recommendation_extension_field', - displayName: gettext('Show recommendation extension field'),} - ]; - $scope.cssClasses = { - 'danger': gettext('Red'), - 'success': gettext('Green'), - 'warning': gettext('Yellow'), - 'default': gettext('Grey'), - 'primary': gettext('Blue'), - }; - $scope.getPermissionDisplayName = function (permission) { - if (permission) { - return _.find($scope.permissions, function (perm) { - return perm.value === permission; - }).display_name; - } - }; - $scope.clickPermission = function (state, permission) { - state.required_permission_to_see = - state.required_permission_to_see === permission.value ? '' : permission.value; - $scope.save(state); - }; - $scope.xor = function (a, b) { - return (a && !b) || (!a && b); - }; - - $scope.changeBooleanMember = function (state, memberName) { - state[memberName] = !state[memberName]; - $scope.save(state); - }; - $scope.setMember = function (state, member, value) { - state[member] = value; - $scope.save(state); - }; - $scope.clickNextStateEntry = function (state, clickedStateId) { - var index = state.next_states_id.indexOf(clickedStateId); - if (index > -1) { // remove now - state.next_states_id.splice(index, 1); - } else { // add - state.next_states_id.push(clickedStateId); - } - $scope.save(state); - }; - $scope.save = function (state) { - MotionState.save(state).then(null, function (error) { - $scope.alert = ErrorMessage.forAlert(error); - }); - }; - - // Save expand state so the session - if ($sessionStorage.motionStateTableExpandState) { - $scope.toggleExpandContent(); - } - $scope.saveExpandState = function (state) { - $sessionStorage.motionStateTableExpandState = state; - }; - - $scope.openStateDialog = function (state) { - ngDialog.open({ - template: 'static/templates/motions/state-edit.html', - controller: state ? 'StateRenameCtrl' : 'StateCreateCtrl', - className: 'ngdialog-theme-default wide-form', - closeByEscape: false, - closeByDocument: false, - resolve: { - state: function () {return state;}, - workflow: function () {return $scope.workflow;}, - } - }); - }; - $scope.openWorkflowDialog = function () { - ngDialog.open({ - template: 'static/templates/motions/workflow-edit.html', - controller: 'WorkflowRenameCtrl', - className: 'ngdialog-theme-default wide-form', - closeByEscape: false, - closeByDocument: false, - resolve: { - workflow: function () {return $scope.workflow;}, - } - }); - }; - - $scope.delete = function (state) { - MotionState.destroy(state).then(null, function (error) { - $scope.alert = ErrorMessage.forAlert(error); - }); - }; - } -]) - -.controller('WorkflowCreateCtrl', [ - '$scope', - 'Workflow', - 'ErrorMessage', - function ($scope, Workflow, ErrorMessage) { - $scope.save = function () { - var workflow = { - name: $scope.newName, - }; - Workflow.create(workflow).then(function (success) { - $scope.closeThisDialog(); - }, function (error) { - $scope.alert = ErrorMessage.forAlert(error); - }); - }; - } -]) - -.controller('WorkflowRenameCtrl', [ - '$scope', - 'workflow', - 'Workflow', - 'gettextCatalog', - 'ErrorMessage', - function ($scope, workflow, Workflow, gettextCatalog, ErrorMessage) { - $scope.workflow = workflow; - $scope.newName = gettextCatalog.getString(workflow.name); - $scope.save = function () { - workflow.name = $scope.newName; - Workflow.save(workflow).then(function (success) { - $scope.closeThisDialog(); - }, function (error) { - $scope.alert = ErrorMessage.forAlert(error); - }); - }; - } -]) - -.controller('StateCreateCtrl', [ - '$scope', - 'workflow', - 'MotionState', - 'ErrorMessage', - function ($scope, workflow, MotionState, ErrorMessage) { - $scope.newName = ''; - $scope.actionWord = ''; - $scope.save = function () { - var state = { - name: $scope.newName, - action_word: $scope.actionWord, - workflow_id: workflow.id, - allow_create_poll: true, - allow_support: true, - allow_submitter_edit: true, - }; - MotionState.create(state).then(function () { - $scope.closeThisDialog(); - }, function (error) { - $scope.alert = ErrorMessage.forAlert(error); - }); - }; - } -]) - -.controller('StateRenameCtrl', [ - '$scope', - 'MotionState', - 'state', - 'gettextCatalog', - 'ErrorMessage', - function ($scope, MotionState, state, gettextCatalog, ErrorMessage) { - $scope.state = state; - $scope.newName = gettextCatalog.getString(state.name); - $scope.actionWord = gettextCatalog.getString(state.action_word); - $scope.save = function () { - state.name = $scope.newName; - state.action_word = $scope.actionWord; - MotionState.save(state).then(function () { - $scope.closeThisDialog(); - }, function (error) { - $scope.alert = ErrorMessage.forAlert(error); - }); - }; - } -]); - -}()); diff --git a/openslides/motions/static/templates/motions/amendment-paragraph-choose-form.html b/openslides/motions/static/templates/motions/amendment-paragraph-choose-form.html deleted file mode 100644 index c4108f1a3..000000000 --- a/openslides/motions/static/templates/motions/amendment-paragraph-choose-form.html +++ /dev/null @@ -1,27 +0,0 @@ -
- |
- |
---|---|
- {{ category.name }} - - | {{ category.prefix }} - |
- Drag and drop motions to reorder the category. Then click the button to renumber. -
- - -
-
- {{ motion.identifier }} {{ motion.getTitle() }}
-
-
-
- Remove from motion block
-
- |
-
- {{ motion.getStateName() }}
-
- |
-
- {{ motion.getRecommendationName() }}
-
- |
- | ||
---|---|---|
- | - - {{ motionBlock.title }} - - - | - {{ motionBlock.motions.length }} - |