angular.module('OpenSlidesApp.core', []) .config(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 default values for templateUrl var patterns = state.name.split('.'), templateUrl, controller, defaultControllers = { create: 'CreateCtrl', update: 'UpdateCtrl', list: 'ListCtrl', detail: 'DetailCtrl', }; // templateUrl if (_.last(patterns).match(/(create|update)/)) { // When state_patterns is in the form "app.module.create" or // "app.module.update", use the form template. templateUrl = 'static/templates/' + patterns[0] + '/' + patterns[1] + '-form.html' } else { // Replaces the first point through a slash (the app name) var appName = state.name.replace('.', '/'); // Replaces any folowing points though a - templateUrl = 'static/templates/' + appName.replace(/\./g, '-') + '.html'; } config.templateUrl = config.templateUrl || templateUrl; // controller if (patterns.length >= 3) { controller = _.capitalize(patterns[1]) + defaultControllers[_.last(patterns)]; config.controller = config.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(function($stateProvider, $locationProvider) { // Core urls $stateProvider.state('dashboard', { url: '/', templateUrl: 'static/templates/dashboard.html' }); $locationProvider.html5Mode(true); }) .config(function(DSProvider) { // Reloads everything after 5 minutes. // TODO: * find a way only to reload things that are still needed DSProvider.defaults.maxAge = 5 * 60 * 1000; // 5 minutes DSProvider.defaults.reapAction = 'none'; DSProvider.defaults.afterReap = function(model, items) { if (items.length > 5) { model.findAll({}, {bypassCache: true}); } else { _.forEach(items, function (item) { model.refresh(item[model.idAttribute]); }); } }; }) .provider('runtimeStates', function($stateProvider) { this.$get = function($q, $timeout, $state) { return { addState: function(name, state) { $stateProvider.state(name, state); } } } }) .run(function(runtimeStates, $http) { $http.get('/core/url_patterns/').then(function(data) { for (var pattern in data.data) { runtimeStates.addState(pattern, { 'url': data.data[pattern], data: {extern: true}, onEnter: function($window) { $window.location.href = this.url; } }); } }); }) .run(function(DS, autoupdate) { autoupdate.on_message(function(data) { // TODO: when MODEL.find() is called after this // a new request is fired. This could be a bug in DS // TODO: Do not send the status code to the client, but make the decission // on the server side. It is an implementation detail, that tornado // sends request to wsgi, which should not concern the client. if (data.status_code == 200) { DS.inject(data.collection, data.data); } else if (data.status_code == 404) { DS.eject(data.collection, data.id); } // TODO: handle other statuscodes }); }) .run(function($rootScope, Config) { // Puts the config object into each scope. // TODO: maybe rootscope.config has to set before findAll() is finished Config.findAll().then(function() {; $rootScope.config = function(key) { return Config.get(key).value; } }); }) .factory('autoupdate', function() { //TODO: use config here var url = "http://" + location.host + "/sockjs"; var Autoupdate = { socket: null, message_receivers: [], connect: function() { var autoupdate = this; this.socket = new SockJS(url); this.socket.onmessage = function(event) { _.forEach(autoupdate.message_receivers, function(receiver) { receiver(event.data); }); } this.socket.onclose = function() { setTimeout(autoupdate.connect, 5000); } }, on_message: function(receiver) { this.message_receivers.push(receiver); } }; Autoupdate.connect(); return Autoupdate; }) .factory('Config', function(DS) { return DS.defineResource({ name: 'config/config', idAttribute: 'key', endpoint: '/rest/config/config/' }); }) .controller("LanguageCtrl", function ($scope, gettextCatalog) { // controller to switch app language // TODO: detect browser language for default language gettextCatalog.setCurrentLanguage('en'); //TODO: for debug only! (helps to find untranslated strings by adding "[MISSING]:") gettextCatalog.debug = true; $scope.switchLanguage = function (lang) { gettextCatalog.setCurrentLanguage(lang); if (lang != 'en') { gettextCatalog.loadRemote("static/i18n/" + lang + ".json"); } } }) .directive('osFocusMe', function ($timeout) { return { link: function (scope, element, attrs, model) { $timeout(function () { element[0].focus(); }); } }; }); // some general JavaScript functions used in all OpenSlides apps $(function () { $('[data-toggle="tooltip"]').tooltip({'placement': 'bottom'}) });