diff --git a/appinfo/routes.php b/appinfo/routes.php index eab8a058..55b1a935 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -33,6 +33,28 @@ $this->create('tasks_enhanced_index', '/') $dispatcher->dispatch('PageController', 'index'); } ); +/* + * Collections + */ +$this->create('getCollections', '/collections') + ->get() + ->action( + function($params){ + session_write_close(); + $dispatcher = new Dispatcher($params); + $dispatcher->dispatch('CollectionsController', 'getCollections'); + } + ); + +$this->create('setVisibility', '/collection/{collectionID}/visibility/{visibility}') + ->post() + ->action( + function($params){ + session_write_close(); + $dispatcher = new Dispatcher($params); + $dispatcher->dispatch('CollectionsController', 'setVisibility'); + } + ); /* * Lists @@ -209,4 +231,27 @@ $this->create('task_reminder', '/tasks/{taskID}/reminder') $dispatcher = new Dispatcher($params); $dispatcher->dispatch('TasksController', 'setReminderDate'); } + ); + +/* + * Settings + */ +$this->create('getSettings', '/settings') + ->get() + ->action( + function($params){ + session_write_close(); + $dispatcher = new Dispatcher($params); + $dispatcher->dispatch('SettingsController', 'getSettings'); + } + ); + +$this->create('showHidden', '/settings/{type}/{setting}/{value}') + ->post() + ->action( + function($params){ + session_write_close(); + $dispatcher = new Dispatcher($params); + $dispatcher->dispatch('SettingsController', 'set'); + } ); \ No newline at end of file diff --git a/css/style.css b/css/style.css index d7ad9958..0b6fe268 100644 --- a/css/style.css +++ b/css/style.css @@ -93,6 +93,11 @@ transition: opacity 100ms ease 0s; opacity: 0.6; } +#content .icon.detail-settings { + background-position: -200px -40px; + transition: opacity 100ms ease 0s; + opacity: 0.6; +} #content .icon.search { background-position: -80px -20px; transition: opacity 100ms ease 0s; @@ -359,6 +364,9 @@ height: 20px; line-height: 20px; } +#task-lists div.footer a.settings { + right: 0; +} #task-lists div.scroll { position: absolute; top: 37px; @@ -931,6 +939,51 @@ #task-details .body .section.detail-reminder.date .section-description { display: block; } +#modal-wrapper { + position: absolute; + width: 100%; + height: 100%; + background: none repeat scroll 0 0 rgba(0, 0, 0, 0.25); +} +#settings_modal { + background: url("../img/bgBig.png") repeat scroll 0 0 #f3f3f3; + border-radius: 6px; +} +#settings_modal .header h2 { + font-size: 17px; + padding: 10px; + text-align: center; +} +#settings_modal .header .button { + position: absolute; + top: 3px; + right: 5px; +} +#settings_modal .navbar { + border-color: #ccc; + border-style: solid; + border-width: 1px 0; + height: 50px; +} +#settings_modal .content { + min-height: 100px; +} +.button { + background-image: -moz-linear-gradient(center top, #5cb6e7 0px, #317cd7 100%); + border-color: #0c67a5; + box-shadow: 0 1px 0 rgba(255, 255, 255, 0.3) inset; + color: #fff; + font-weight: bold; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.5); + line-height: 14px; + margin-right: 8px; + padding: 6px 10px 5px; + position: relative; + float: right; +} +.button:hover { + color: #CCCCCC; +} input, textarea { -moz-user-select: text; @@ -938,6 +991,9 @@ textarea { #content a:hover .icon.detail-trash { opacity: 1; } +#content a:hover .icon.detail-settings { + opacity: 1; +} #content a:hover .icon.search { opacity: 1; } diff --git a/css/style.less b/css/style.less index d672bb7f..eec76ba7 100644 --- a/css/style.less +++ b/css/style.less @@ -115,6 +115,11 @@ transition: opacity 100ms ease 0s; opacity:0.6; } + &.detail-settings{ + background-position: -200px -40px; + transition: opacity 100ms ease 0s; + opacity:0.6; + } &.search { background-position: -80px -20px; transition: opacity 100ms ease 0s; @@ -382,6 +387,9 @@ padding:10px; height:20px; line-height:20px; + &.settings{ + right:0; + } } } div.scroll{ @@ -981,6 +989,55 @@ } } +#modal-wrapper{ + position: absolute; + width:100%; + height: 100%; + background: none repeat scroll 0 0 rgba(0, 0, 0, 0.25); +} + +#settings_modal{ + background: url("../img/bgBig.png") repeat scroll 0 0 #f3f3f3; + border-radius: 6px; + .header{ + h2{ + font-size: 17px; + padding: 10px; + text-align: center; + } + .button{ + position: absolute; + top:3px; + right:5px; + } + } + .navbar{ + border-color: #ccc; + border-style: solid; + border-width: 1px 0; + height: 50px; + } + .content{ + min-height:100px; + } +} +.button{ + background-image: -moz-linear-gradient(center top , #5cb6e7 0px, #317cd7 100%); + border-color: #0c67a5; + box-shadow: 0 1px 0 rgba(255, 255, 255, 0.3) inset; + color: #fff; + font-weight: bold; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.5); + line-height: 14px; + margin-right: 8px; + padding: 6px 10px 5px; + position: relative; + float:right; + &:hover{ + color:#CCCCCC; + } +} + input, textarea { -moz-user-select: text; } @@ -988,6 +1045,9 @@ input, textarea { &.detail-trash{ opacity:1; } + &.detail-settings{ + opacity:1; + } &.search{ opacity:1; } diff --git a/css/vendor/bootstrap/bootstrap.css b/css/vendor/bootstrap/bootstrap.css new file mode 100644 index 00000000..9069bd34 --- /dev/null +++ b/css/vendor/bootstrap/bootstrap.css @@ -0,0 +1,173 @@ +.modal-open { + overflow: hidden; +} +.modal { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1050; + display: none; + overflow: auto; + overflow-y: auto; + -webkit-overflow-scrolling: touch; + outline: 0; +} +.modal.fade .modal-dialog { + -webkit-transition: -webkit-transform .3s ease-out; + -moz-transition: -moz-transform .3s ease-out; + -o-transition: -o-transform .3s ease-out; + transition: transform .3s ease-out; + -webkit-transform: translate(0, -25%); + -ms-transform: translate(0, -25%); + transform: translate(0, -25%); +} +.modal.in .modal-dialog { + -webkit-transform: translate(0, 0); + -ms-transform: translate(0, 0); + transform: translate(0, 0); +} +.modal-dialog { + position: relative; + width: auto; + margin: 10px; +} +.modal-content { + position: relative; + background-color: #fff; + background-clip: padding-box; + border: 1px solid #999; + border: 1px solid rgba(0, 0, 0, .2); + border-radius: 6px; + outline: none; + -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, .5); + box-shadow: 0 3px 9px rgba(0, 0, 0, .5); +} +.modal-backdrop { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1040; + background-color: #000; +} +.modal-backdrop.fade { + filter: alpha(opacity=0); + opacity: 0; +} +.modal-backdrop.in { + filter: alpha(opacity=50); + opacity: .5; +} +.modal-header { + min-height: 16.42857143px; + padding: 15px; + border-bottom: 1px solid #e5e5e5; +} +.modal-header .close { + margin-top: -2px; +} +.modal-title { + margin: 0; + line-height: 1.42857143; +} +.modal-body { + position: relative; + padding: 20px; +} +.modal-footer { + padding: 19px 20px 20px; + margin-top: 15px; + text-align: right; + border-top: 1px solid #e5e5e5; +} +.modal-footer .btn + .btn { + margin-bottom: 0; + margin-left: 5px; +} +.modal-footer .btn-group .btn + .btn { + margin-left: -1px; +} +.modal-footer .btn-block + .btn-block { + margin-left: 0; +} +@media (min-width: 768px) { + .modal-dialog { + width: 500px; + margin: 30px auto; + } + .modal-content { + -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, .5); + box-shadow: 0 5px 15px rgba(0, 0, 0, .5); + } + .modal-sm { + width: 300px; + } +} +@media (min-width: 992px) { + .modal-lg { + width: 900px; + } +} +.tab-content > .tab-pane { + display: none; +} +.tab-content > .active { + display: block; +} +ul.nav-tabs{ + border-color: #ccc; + border-style: solid; + border-width: 1px 0; + clear: both; + content: " "; + display: table; + width: 100%; +} +.nav-tabs > li{ + display: block; + float: left; + height: 50px; + padding: 0 10px; + position: relative; +} +.nav-tabs > li.active{ + background: url("../../../img/bgSettingsActive.png") repeat scroll 0 0 #4a99db; +} +.nav-tabs >li.active a{ + color:white; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.45); +} +.nav-tabs > li a{ + font-weight: bold; + display: block; + height: 50px; + line-height: 50px; +} +.nav-tabs > li:first-child{ + margin-left: 20px; +} +.tabbable .tab-pane{ + padding:20px; +} +.tab-pane select{ + background-color: rgba(0, 0, 0, 0); + border: 1px solid #ccc; + border-radius: 0; + font-weight: normal; + margin: 0; + padding: 0; + width: 98px; + float:right; +} +.tab-pane li{ + padding: 10px 0; +} +.tab-pane li span.title{ + padding-left: 10px; + position: relative; + top: 2px; +} +} \ No newline at end of file diff --git a/img/bgSettingsActive.png b/img/bgSettingsActive.png new file mode 100644 index 00000000..6c363e88 Binary files /dev/null and b/img/bgSettingsActive.png differ diff --git a/img/sprites.svg b/img/sprites.svg index 7cd220e4..c01c354e 100644 --- a/img/sprites.svg +++ b/img/sprites.svg @@ -697,22 +697,22 @@ - + - + - + - + @@ -1554,4 +1554,34 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/js/app/app.coffee b/js/app/app.coffee index 5e11f714..b76e4e36 100644 --- a/js/app/app.coffee +++ b/js/app/app.coffee @@ -19,7 +19,7 @@ You should have received a copy of the GNU Affero General Public License along with this library. If not, see . ### -angular.module('Tasks',['OC','ngRoute','ngAnimate']) +angular.module('Tasks',['OC','ngRoute','ngAnimate','ui.bootstrap']) .config ['$provide','$routeProvider', '$interpolateProvider', ($provide, $routeProvider, $interpolateProvider) -> $provide.value 'Config', config = @@ -30,6 +30,7 @@ angular.module('Tasks',['OC','ngRoute','ngAnimate']) .when('/lists/:listID',{}) .when('/lists/:listID/edit/:listparameter',{}) .when('/lists/:listID/tasks/:taskID',{}) + .when('/lists/:listID/tasks/:taskID/settings',{}) .when('/lists/:listID/tasks/:taskID/edit/:parameter',{}) .when('/search/:searchString',{}) .when('/search/:searchString/tasks/:taskID',{}) @@ -71,6 +72,7 @@ angular.module('Tasks').run ['Config', '$timeout', timeOutUpdate = -> $timeout update, Config.taskUpdateInterval if init + # CollectionsBusinessLayer.updateModel() ListsBusinessLayer.updateModel() TasksBusinessLayer.updateModel() init = true diff --git a/js/app/controllers/appcontroller.coffee b/js/app/controllers/appcontroller.coffee index b5834e7f..4fbea00d 100644 --- a/js/app/controllers/appcontroller.coffee +++ b/js/app/controllers/appcontroller.coffee @@ -23,14 +23,15 @@ License along with this library. If not, see . angular.module('Tasks').controller 'AppController', ['$scope', 'Persistence', '$route', 'Status', '$timeout', -'$location', '$routeParams', 'Loading', +'$location', '$routeParams', 'Loading','$modal','SettingsModel', ($scope, Persistence, $route, status, $timeout, $location, -$routeParams, Loading) -> +$routeParams, Loading, $modal, SettingsModel) -> class AppController constructor: (@_$scope, @_persistence, @_$route, @_$status, - @_$timeout, @_$location, @_$routeparams, @_Loading) -> + @_$timeout, @_$location, @_$routeparams, @_Loading, + @_$modal,@_$settingsmodel) -> @_$scope.initialized = false @@ -40,6 +41,8 @@ $routeParams, Loading) -> @_$scope.status.newListName = "" + @_$scope.settingsmodel = @_$settingsmodel + successCallback = => @_$scope.initialized = true @@ -58,7 +61,15 @@ $routeParams, Loading) -> @_$scope.isLoading = () -> return _Loading.isLoading() + @_$scope.showSettings = () -> + _$scope.modalInstance = _$modal.open({ + templateUrl: 'part.settings.html', + controller: 'SettingsController', + backdrop: true, + windowClass: 'test' + }) + return new AppController($scope, Persistence, $route, status, $timeout, - $location, $routeParams, Loading) + $location, $routeParams, Loading, $modal, SettingsModel) ] \ No newline at end of file diff --git a/js/app/controllers/listcontroller.coffee b/js/app/controllers/listcontroller.coffee index 90f1ae69..0b61733b 100644 --- a/js/app/controllers/listcontroller.coffee +++ b/js/app/controllers/listcontroller.coffee @@ -132,6 +132,16 @@ CollectionsModel, ListsBusinessLayer, $location) -> @_$scope.getCollectionCount = (collectionID) -> return _$collectionsmodel.getCount(collectionID) + @_$scope.hideCollection = (collectionID) -> + collection = _$collectionsmodel.getById(collectionID) + switch collection.show + when 0 + return true + when 1 + return false + when 2 + return (@getCollectionCount(collectionID) < 1) + @_$scope.getCollectionString = (collectionID) -> return _$collectionsmodel.getCountString(collectionID) @@ -144,6 +154,7 @@ CollectionsModel, ListsBusinessLayer, $location) -> @_$scope.update = () -> if not _$scope.isLoading() + # _$collectionsbusinesslayer.updateModel() _$tasksbusinesslayer.updateModel() _$listsbusinesslayer.updateModel() diff --git a/js/app/controllers/settingscontroller.coffee b/js/app/controllers/settingscontroller.coffee new file mode 100644 index 00000000..831898cc --- /dev/null +++ b/js/app/controllers/settingscontroller.coffee @@ -0,0 +1,60 @@ +### + +ownCloud - Tasks + +@author Raimund Schlüßler +@copyright 2013 + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE +License as published by the Free Software Foundation; either +version 3 of the License, or any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU AFFERO GENERAL PUBLIC LICENSE for more details. + +You should have received a copy of the GNU Affero General Public +License along with this library. If not, see . + +### +angular.module('Tasks').controller 'SettingsController', +['$scope', '$window', 'Status', '$location','$modalInstance', +'CollectionsModel', 'SettingsBusinessLayer', +($scope, $window, Status, $location, $modalInstance,CollectionsModel, + SettingsBusinessLayer) -> + + class SettingsController + + constructor: (@_$scope, @_$window, @_$status, + @_$location, @_$modalInstance, @_$collectionsmodel, + @_$settingsbusinesslayer) -> + + @_$scope.status = @_$status.getStatus() + + @_$scope.collections = @_$collectionsmodel.getAll() + + @_$scope.collectionOptions = [ + { + id: 0, + name: t('tasks_enhanced','Hidden')}, + { + id: 1, + name: t('tasks_enhanced','Visible')}, + { + id: 2, + name: t('tasks_enhanced','Automatic')} + ] + + @_$scope.ok = () => + $modalInstance.close() + + @_$scope.setVisibility = (collectionID) => + collection = _$collectionsmodel.getById(collectionID) + _$settingsbusinesslayer.setVisibility(collectionID,collection.show) + + + return new SettingsController($scope, $window, Status, $location, + $modalInstance, CollectionsModel, SettingsBusinessLayer) +] \ No newline at end of file diff --git a/js/app/controllers/taskscontroller.coffee b/js/app/controllers/taskscontroller.coffee index 4cfffe0d..8fdf8c78 100644 --- a/js/app/controllers/taskscontroller.coffee +++ b/js/app/controllers/taskscontroller.coffee @@ -22,14 +22,16 @@ License along with this library. If not, see . angular.module('Tasks').controller 'TasksController', ['$scope', '$window', '$routeParams', 'TasksModel', 'ListsModel', 'CollectionsModel', 'TasksBusinessLayer', '$location', +'SettingsBusinessLayer', ($scope, $window, $routeParams, TasksModel, ListsModel, -CollectionsModel, TasksBusinessLayer, $location) -> +CollectionsModel, TasksBusinessLayer, $location, +SettingsBusinessLayer) -> class TasksController constructor: (@_$scope,@_$window,@_$routeParams, @_$tasksmodel,@_$listsmodel,@_$collectionsmodel, - @_tasksbusinesslayer, @$location) -> + @_tasksbusinesslayer, @$location, @_settingsbusinesslayer) -> @_$scope.tasks = @_$tasksmodel.getAll() @_$scope.lists = @_$listsmodel.getAll() @@ -100,7 +102,7 @@ CollectionsModel, TasksBusinessLayer, $location) -> _tasksbusinesslayer.starTask(taskID) @_$scope.toggleHidden = () -> - _$scope.status.showhidden = !_$scope.status.showhidden + _settingsbusinesslayer.toggle('various','showHidden') @_$scope.filterTasks = () -> return (task) -> @@ -180,5 +182,6 @@ CollectionsModel, TasksBusinessLayer, $location) -> return task.due return new TasksController($scope, $window, $routeParams, - TasksModel, ListsModel, CollectionsModel, TasksBusinessLayer, $location) + TasksModel, ListsModel, CollectionsModel, TasksBusinessLayer, $location, + SettingsBusinessLayer) ] \ No newline at end of file diff --git a/js/app/directives/pane.coffee b/js/app/directives/pane.coffee new file mode 100644 index 00000000..7d282c4b --- /dev/null +++ b/js/app/directives/pane.coffee @@ -0,0 +1,34 @@ +### + +ownCloud - Music + +@author Raimund Schlüßler +@copyright 2013 + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE +License as published by the Free Software Foundation; either +version 3 of the License, or any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU AFFERO GENERAL PUBLIC LICENSE for more details. + +You should have received a copy of the GNU Affero General Public +License along with this library. If not, see . + +### + +angular.module('Tasks').directive 'pane', -> + directive = + scope: + title: '@' + require: '^tabs' + restrict: 'E' + transclude: true + replace: true + link: (scope, element, attrs, tabsCtrl) -> + tabsCtrl.addPane(scope) + template: '
[[ ]]
' diff --git a/js/app/directives/tabs.coffee b/js/app/directives/tabs.coffee new file mode 100644 index 00000000..d8eb78b5 --- /dev/null +++ b/js/app/directives/tabs.coffee @@ -0,0 +1,36 @@ +### + +ownCloud - Music + +@author Raimund Schlüßler +@copyright 2013 + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE +License as published by the Free Software Foundation; either +version 3 of the License, or any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU AFFERO GENERAL PUBLIC LICENSE for more details. + +You should have received a copy of the GNU Affero General Public +License along with this library. If not, see . + +### + +angular.module('Tasks').directive 'tabs', -> + directive = + restrict: 'E', + scope: {}, + controller: ($scope, $element) -> + panes = $scope.panes = [] + $scope.select = (pane) -> + angular.forEach(panes, (pane) -> + pane.selected = false + ) + pane.selected = true + this.addPane = (pane) -> + $scope.select(pane) if (panes.length == 0) + panes.push(pane) \ No newline at end of file diff --git a/js/app/services/businesslayer/settingsbusinesslayer.coffee b/js/app/services/businesslayer/settingsbusinesslayer.coffee new file mode 100644 index 00000000..44ea4d99 --- /dev/null +++ b/js/app/services/businesslayer/settingsbusinesslayer.coffee @@ -0,0 +1,47 @@ +### + +ownCloud - News + +@author Bernhard Posselt +@copyright 2012 Bernhard Posselt dev@bernhard-posselt.com + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE +License as published by the Free Software Foundation; either +version 3 of the License, or any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU AFFERO GENERAL PUBLIC LICENSE for more details. + +You should have received a copy of the GNU Affero General Public +License along with this library. If not, see . + +### + + +angular.module('Tasks').factory 'SettingsBusinessLayer', +['Persistence', 'SettingsModel', +(Persistence, SettingsModel) -> + + class SettingsBusinessLayer + + constructor: (@_persistence, @_$settingsmodel) -> + + updateModel: () -> + success = () => + + @_persistence.getCollections(success, true) + + setVisibility: (collectionID, visibility) -> + @_persistence.setVisibility(collectionID, visibility) + + toggle: (type, setting) -> + @_$settingsmodel.toggle(type, setting) + value = @_$settingsmodel.getById(type)[setting] + @_persistence.setting(type, setting, value) + + return new SettingsBusinessLayer(Persistence, SettingsModel) + +] \ No newline at end of file diff --git a/js/app/services/businesslayer/tasksbusinesslayer.coffee b/js/app/services/businesslayer/tasksbusinesslayer.coffee index 84b998b5..dffe5156 100644 --- a/js/app/services/businesslayer/tasksbusinesslayer.coffee +++ b/js/app/services/businesslayer/tasksbusinesslayer.coffee @@ -79,6 +79,8 @@ angular.module('Tasks').factory 'TasksBusinessLayer', due.hour(date.hour()).minute(date.minute()) else due = date + else if type == 'all' + due = date else return @_$tasksmodel.setDueDate(taskID,due.format('YYYYMMDDTHHmmss')) @@ -308,7 +310,7 @@ angular.module('Tasks').factory 'TasksBusinessLayer', when 'uncompleted' @uncompleteTask(taskID) when 'today' - @setDueDate(taskID,moment().format("YYYYMMDDTHHmmss")) + @setDue(taskID,moment().startOf('day').add('h',12),'all') when 'week', 'all' else @changeCalendarId(taskID,listID) @@ -319,6 +321,9 @@ angular.module('Tasks').factory 'TasksBusinessLayer', @_$tasksmodel.removeVoid() @_persistence.getTasks(success, true) + setShowHidden: (showHidden) -> + @_persistence.setShowHidden(showHidden) + return new TasksBusinessLayer(TasksModel, Persistence) diff --git a/js/app/services/models/collectionsmodel.coffee b/js/app/services/models/collectionsmodel.coffee index c804deac..5debf5a4 100644 --- a/js/app/services/models/collectionsmodel.coffee +++ b/js/app/services/models/collectionsmodel.coffee @@ -21,41 +21,13 @@ License along with this library. If not, see . ### angular.module('Tasks').factory 'CollectionsModel', -['TasksModel', '_Model', '_EqualQuery', 'Utils', -(TasksModel, _Model, _EqualQuery, Utils) -> +['TasksModel', '_Model', +(TasksModel, _Model) -> class CollectionsModel extends _Model - constructor: (@_$tasksmodel, @_utils) -> + constructor: (@_$tasksmodel) -> @_nameCache = {} - @_$collections = [ - { - id: "starred" - displayname: t('tasks_enhanced','Important') - }, - { - id: "today" - displayname: t('tasks_enhanced', 'Today') - }, - { - id: "week" - displayname: t('tasks_enhanced', 'Week') - }, - { - id: "all", - displayname: t('tasks_enhanced', 'All') - }, - { - id: "current", - displayname: t('tasks_enhanced', 'Current') - }, - { - id: "completed" - displayname: t('tasks_enhanced', 'Done') - } - ] super() - for collection in @_$collections - @add(collection) add: (data, clearCache=true) -> @_nameCache[data.displayname] = data @@ -92,5 +64,5 @@ angular.module('Tasks').factory 'CollectionsModel', else return '' - return new CollectionsModel(TasksModel, Utils) + return new CollectionsModel(TasksModel) ] \ No newline at end of file diff --git a/js/app/services/models/settingsmodel.coffee b/js/app/services/models/settingsmodel.coffee new file mode 100644 index 00000000..df8e28c0 --- /dev/null +++ b/js/app/services/models/settingsmodel.coffee @@ -0,0 +1,42 @@ +### + +ownCloud - Tasks + +@author Raimund Schlüßler +@copyright 2013 + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE +License as published by the Free Software Foundation; either +version 3 of the License, or any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU AFFERO GENERAL PUBLIC LICENSE for more details. + +You should have received a copy of the GNU Affero General Public +License along with this library. If not, see . + +### + +angular.module('Tasks').factory 'SettingsModel', +['_Model', +(_Model) -> + class SettingsModel extends _Model + + constructor: () -> + @_nameCache = {} + super() + + add: (data, clearCache=true) -> + @_nameCache[data.displayname] = data + if angular.isDefined(data.id) + super(data, clearCache) + + toggle: (type, setting) -> + set = @getById(type) + @getById(type)[setting] = !set[setting] + + return new SettingsModel() +] \ No newline at end of file diff --git a/js/app/services/persistence.coffee b/js/app/services/persistence.coffee index dbfbeba3..212e6b58 100644 --- a/js/app/services/persistence.coffee +++ b/js/app/services/persistence.coffee @@ -34,11 +34,76 @@ angular.module('Tasks').factory 'Persistence', successCallback = => @deferred.resolve() + @getCollections() + @getSettings() @getLists() @getTasks(successCallback) @deferred.promise + getCollections: (onSuccess, showLoading=true) -> + onSuccess or= -> + + if showLoading + @_Loading.increase() + successCallbackWrapper = (data) => + onSuccess() + @_Loading.decrease() + failureCallbackWrapper = (data) => + @_Loading.decrease() + else + successCallbackWrapper = (data) => + onSuccess() + failureCallbackWrapper = (data) => + + params = + onSuccess: successCallbackWrapper + onFailure: failureCallbackWrapper + + @_request.get '/apps/tasks_enhanced/collections', params + + getSettings: (onSuccess, showLoading=true) -> + onSuccess or= -> + + if showLoading + @_Loading.increase() + successCallbackWrapper = (data) => + onSuccess() + @_Loading.decrease() + failureCallbackWrapper = (data) => + @_Loading.decrease() + else + successCallbackWrapper = (data) => + onSuccess() + failureCallbackWrapper = (data) => + + params = + onSuccess: successCallbackWrapper + onFailure: failureCallbackWrapper + + @_request.get '/apps/tasks_enhanced/settings', params + + setVisibility: (collectionID, visibility) -> + params = + routeParams: + collectionID: collectionID + visibility: visibility + + @_request.post '/apps/tasks_enhanced/collection/ + {collectionID}/visibility/{visibility}', params + + setting: (type, setting, value) -> + params = + routeParams: + type: type + setting: setting + value: +value + + @_request.post '/apps/tasks_enhanced/settings/ + {type}/{setting}/{value}', params + + + getLists: (onSuccess, showLoading=true, which='all') -> onSuccess or= -> @@ -244,6 +309,14 @@ angular.module('Tasks').factory 'Persistence', @_request.post '/apps/tasks_enhanced/tasks/{taskID}/note', params + setShowHidden: (showHidden) -> + params = + routeParams: + showHidden: +showHidden + + @_request.post '/apps/tasks_enhanced/settings/showhidden/{showHidden}', + params + return new Persistence(Request, Loading, $rootScope) ] \ No newline at end of file diff --git a/js/app/services/services.coffee b/js/app/services/services.coffee index e3a15aa8..9b09229a 100644 --- a/js/app/services/services.coffee +++ b/js/app/services/services.coffee @@ -37,11 +37,13 @@ angular.module('Tasks').factory 'Loading', ['_Loading', (_Loading) -> # ] angular.module('Tasks').factory 'Publisher', -['_Publisher', 'ListsModel', 'TasksModel', -(_Publisher, ListsModel, TasksModel) -> +['_Publisher', 'ListsModel', 'TasksModel', 'CollectionsModel', 'SettingsModel', +(_Publisher, ListsModel, TasksModel, CollectionsModel, SettingsModel) -> # register items at publisher to automatically add incoming items publisher = new _Publisher() + publisher.subscribeObjectTo(CollectionsModel, 'collections') + publisher.subscribeObjectTo(SettingsModel, 'settings') publisher.subscribeObjectTo(ListsModel, 'lists') publisher.subscribeObjectTo(TasksModel, 'tasks') diff --git a/js/app/services/status.coffee b/js/app/services/status.coffee index aefa3eee..3c660c0d 100644 --- a/js/app/services/status.coffee +++ b/js/app/services/status.coffee @@ -26,7 +26,6 @@ angular.module('Tasks').factory 'Status', constructor: () -> @_$status = { - showhidden: true searchActive: false addingList: false focusTaskInput: false diff --git a/js/public/app.js b/js/public/app.js index 328f04a8..f3673a41 100644 --- a/js/public/app.js +++ b/js/public/app.js @@ -12,14 +12,14 @@ (function() { - angular.module('Tasks', ['OC', 'ngRoute', 'ngAnimate']).config([ + angular.module('Tasks', ['OC', 'ngRoute', 'ngAnimate', 'ui.bootstrap']).config([ '$provide', '$routeProvider', '$interpolateProvider', function($provide, $routeProvider, $interpolateProvider) { var config; $provide.value('Config', config = { markReadTimeout: 500, taskUpdateInterval: 1000 * 600 }); - $routeProvider.when('/lists/:listID', {}).when('/lists/:listID/edit/:listparameter', {}).when('/lists/:listID/tasks/:taskID', {}).when('/lists/:listID/tasks/:taskID/edit/:parameter', {}).when('/search/:searchString', {}).when('/search/:searchString/tasks/:taskID', {}).when('/search/:searchString/tasks/:taskID/edit/:parameter', {}).otherwise({ + $routeProvider.when('/lists/:listID', {}).when('/lists/:listID/edit/:listparameter', {}).when('/lists/:listID/tasks/:taskID', {}).when('/lists/:listID/tasks/:taskID/settings', {}).when('/lists/:listID/tasks/:taskID/edit/:parameter', {}).when('/search/:searchString', {}).when('/search/:searchString/tasks/:taskID', {}).when('/search/:searchString/tasks/:taskID/edit/:parameter', {}).otherwise({ redirectTo: '/lists/all' }); /* @@ -227,6 +227,26 @@ }).call(this); +(function() { + angular.module('Tasks').directive('pane', function() { + var directive; + return directive = { + scope: { + title: '@' + }, + require: '^tabs', + restrict: 'E', + transclude: true, + replace: true, + link: function(scope, element, attrs, tabsCtrl) { + return tabsCtrl.addPane(scope); + }, + template: '
[[ ]]
' + }; + }); + +}).call(this); + (function() { angular.module('Tasks').directive('stopEvent', function() { return { @@ -241,6 +261,33 @@ }).call(this); +(function() { + angular.module('Tasks').directive('tabs', function() { + var directive; + return directive = { + restrict: 'E', + scope: {}, + controller: function($scope, $element) { + var panes; + panes = $scope.panes = []; + $scope.select = function(pane) { + angular.forEach(panes, function(pane) { + return pane.selected = false; + }); + return pane.selected = true; + }; + return this.addPane = function(pane) { + if (panes.length === 0) { + $scope.select(pane); + } + return panes.push(pane); + }; + } + }; + }); + +}).call(this); + (function() { angular.module('Tasks').directive('timepicker', function() { return { @@ -283,10 +330,10 @@ (function() { angular.module('Tasks').controller('AppController', [ - '$scope', 'Persistence', '$route', 'Status', '$timeout', '$location', '$routeParams', 'Loading', function($scope, Persistence, $route, status, $timeout, $location, $routeParams, Loading) { + '$scope', 'Persistence', '$route', 'Status', '$timeout', '$location', '$routeParams', 'Loading', '$modal', 'SettingsModel', function($scope, Persistence, $route, status, $timeout, $location, $routeParams, Loading, $modal, SettingsModel) { var AppController; AppController = (function() { - function AppController(_$scope, _persistence, _$route, _$status, _$timeout, _$location, _$routeparams, _Loading) { + function AppController(_$scope, _persistence, _$route, _$status, _$timeout, _$location, _$routeparams, _Loading, _$modal, _$settingsmodel) { var successCallback, _this = this; this._$scope = _$scope; @@ -297,10 +344,13 @@ this._$location = _$location; this._$routeparams = _$routeparams; this._Loading = _Loading; + this._$modal = _$modal; + this._$settingsmodel = _$settingsmodel; this._$scope.initialized = false; this._$scope.status = this._$status.getStatus(); this._$scope.route = this._$routeparams; this._$scope.status.newListName = ""; + this._$scope.settingsmodel = this._$settingsmodel; successCallback = function() { return _this._$scope.initialized = true; }; @@ -318,12 +368,20 @@ this._$scope.isLoading = function() { return _Loading.isLoading(); }; + this._$scope.showSettings = function() { + return _$scope.modalInstance = _$modal.open({ + templateUrl: 'part.settings.html', + controller: 'SettingsController', + backdrop: true, + windowClass: 'test' + }); + }; } return AppController; })(); - return new AppController($scope, Persistence, $route, status, $timeout, $location, $routeParams, Loading); + return new AppController($scope, Persistence, $route, status, $timeout, $location, $routeParams, Loading, $modal, SettingsModel); } ]); @@ -699,6 +757,18 @@ this._$scope.getCollectionCount = function(collectionID) { return _$collectionsmodel.getCount(collectionID); }; + this._$scope.hideCollection = function(collectionID) { + var collection; + collection = _$collectionsmodel.getById(collectionID); + switch (collection.show) { + case 0: + return true; + case 1: + return false; + case 2: + return this.getCollectionCount(collectionID) < 1; + } + }; this._$scope.getCollectionString = function(collectionID) { return _$collectionsmodel.getCountString(collectionID); }; @@ -773,12 +843,59 @@ }).call(this); +(function() { + angular.module('Tasks').controller('SettingsController', [ + '$scope', '$window', 'Status', '$location', '$modalInstance', 'CollectionsModel', 'SettingsBusinessLayer', function($scope, $window, Status, $location, $modalInstance, CollectionsModel, SettingsBusinessLayer) { + var SettingsController; + SettingsController = (function() { + function SettingsController(_$scope, _$window, _$status, _$location, _$modalInstance, _$collectionsmodel, _$settingsbusinesslayer) { + var _this = this; + this._$scope = _$scope; + this._$window = _$window; + this._$status = _$status; + this._$location = _$location; + this._$modalInstance = _$modalInstance; + this._$collectionsmodel = _$collectionsmodel; + this._$settingsbusinesslayer = _$settingsbusinesslayer; + this._$scope.status = this._$status.getStatus(); + this._$scope.collections = this._$collectionsmodel.getAll(); + this._$scope.collectionOptions = [ + { + id: 0, + name: t('tasks_enhanced', 'Hidden') + }, { + id: 1, + name: t('tasks_enhanced', 'Visible') + }, { + id: 2, + name: t('tasks_enhanced', 'Automatic') + } + ]; + this._$scope.ok = function() { + return $modalInstance.close(); + }; + this._$scope.setVisibility = function(collectionID) { + var collection; + collection = _$collectionsmodel.getById(collectionID); + return _$settingsbusinesslayer.setVisibility(collectionID, collection.show); + }; + } + + return SettingsController; + + })(); + return new SettingsController($scope, $window, Status, $location, $modalInstance, CollectionsModel, SettingsBusinessLayer); + } + ]); + +}).call(this); + (function() { angular.module('Tasks').controller('TasksController', [ - '$scope', '$window', '$routeParams', 'TasksModel', 'ListsModel', 'CollectionsModel', 'TasksBusinessLayer', '$location', function($scope, $window, $routeParams, TasksModel, ListsModel, CollectionsModel, TasksBusinessLayer, $location) { + '$scope', '$window', '$routeParams', 'TasksModel', 'ListsModel', 'CollectionsModel', 'TasksBusinessLayer', '$location', 'SettingsBusinessLayer', function($scope, $window, $routeParams, TasksModel, ListsModel, CollectionsModel, TasksBusinessLayer, $location, SettingsBusinessLayer) { var TasksController; TasksController = (function() { - function TasksController(_$scope, _$window, _$routeParams, _$tasksmodel, _$listsmodel, _$collectionsmodel, _tasksbusinesslayer, $location) { + function TasksController(_$scope, _$window, _$routeParams, _$tasksmodel, _$listsmodel, _$collectionsmodel, _tasksbusinesslayer, $location, _settingsbusinesslayer) { this._$scope = _$scope; this._$window = _$window; this._$routeParams = _$routeParams; @@ -787,6 +904,7 @@ this._$collectionsmodel = _$collectionsmodel; this._tasksbusinesslayer = _tasksbusinesslayer; this.$location = $location; + this._settingsbusinesslayer = _settingsbusinesslayer; this._$scope.tasks = this._$tasksmodel.getAll(); this._$scope.lists = this._$listsmodel.getAll(); this._$scope.days = [0, 1, 2, 3, 4, 5, 6]; @@ -853,7 +971,7 @@ } }; this._$scope.toggleHidden = function() { - return _$scope.status.showhidden = !_$scope.status.showhidden; + return _settingsbusinesslayer.toggle('various', 'showHidden'); }; this._$scope.filterTasks = function() { return function(task) { @@ -942,7 +1060,7 @@ return TasksController; })(); - return new TasksController($scope, $window, $routeParams, TasksModel, ListsModel, CollectionsModel, TasksBusinessLayer, $location); + return new TasksController($scope, $window, $routeParams, TasksModel, ListsModel, CollectionsModel, TasksBusinessLayer, $location, SettingsBusinessLayer); } ]); @@ -1010,6 +1128,43 @@ }).call(this); +(function() { + angular.module('Tasks').factory('SettingsBusinessLayer', [ + 'Persistence', 'SettingsModel', function(Persistence, SettingsModel) { + var SettingsBusinessLayer; + SettingsBusinessLayer = (function() { + function SettingsBusinessLayer(_persistence, _$settingsmodel) { + this._persistence = _persistence; + this._$settingsmodel = _$settingsmodel; + } + + SettingsBusinessLayer.prototype.updateModel = function() { + var success, + _this = this; + success = function() {}; + return this._persistence.getCollections(success, true); + }; + + SettingsBusinessLayer.prototype.setVisibility = function(collectionID, visibility) { + return this._persistence.setVisibility(collectionID, visibility); + }; + + SettingsBusinessLayer.prototype.toggle = function(type, setting) { + var value; + this._$settingsmodel.toggle(type, setting); + value = this._$settingsmodel.getById(type)[setting]; + return this._persistence.setting(type, setting, value); + }; + + return SettingsBusinessLayer; + + })(); + return new SettingsBusinessLayer(Persistence, SettingsModel); + } + ]); + +}).call(this); + (function() { angular.module('Tasks').factory('TasksBusinessLayer', [ 'TasksModel', 'Persistence', function(TasksModel, Persistence) { @@ -1093,6 +1248,8 @@ } else { due = date; } + } else if (type === 'all') { + due = date; } else { return; } @@ -1376,7 +1533,7 @@ case 'uncompleted': return this.uncompleteTask(taskID); case 'today': - return this.setDueDate(taskID, moment().format("YYYYMMDDTHHmmss")); + return this.setDue(taskID, moment().startOf('day').add('h', 12), 'all'); case 'week': case 'all': break; @@ -1395,6 +1552,10 @@ return this._persistence.getTasks(success, true); }; + TasksBusinessLayer.prototype.setShowHidden = function(showHidden) { + return this._persistence.setShowHidden(showHidden); + }; + return TasksBusinessLayer; })(); @@ -1409,43 +1570,15 @@ __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; angular.module('Tasks').factory('CollectionsModel', [ - 'TasksModel', '_Model', '_EqualQuery', 'Utils', function(TasksModel, _Model, _EqualQuery, Utils) { + 'TasksModel', '_Model', function(TasksModel, _Model) { var CollectionsModel; CollectionsModel = (function(_super) { __extends(CollectionsModel, _super); - function CollectionsModel(_$tasksmodel, _utils) { - var collection, _i, _len, _ref; + function CollectionsModel(_$tasksmodel) { this._$tasksmodel = _$tasksmodel; - this._utils = _utils; this._nameCache = {}; - this._$collections = [ - { - id: "starred", - displayname: t('tasks_enhanced', 'Important') - }, { - id: "today", - displayname: t('tasks_enhanced', 'Today') - }, { - id: "week", - displayname: t('tasks_enhanced', 'Week') - }, { - id: "all", - displayname: t('tasks_enhanced', 'All') - }, { - id: "current", - displayname: t('tasks_enhanced', 'Current') - }, { - id: "completed", - displayname: t('tasks_enhanced', 'Done') - } - ]; CollectionsModel.__super__.constructor.call(this); - _ref = this._$collections; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - collection = _ref[_i]; - this.add(collection); - } } CollectionsModel.prototype.add = function(data, clearCache) { @@ -1513,7 +1646,7 @@ return CollectionsModel; })(_Model); - return new CollectionsModel(TasksModel, Utils); + return new CollectionsModel(TasksModel); } ]); @@ -1677,6 +1810,46 @@ }).call(this); +(function() { + var __hasProp = {}.hasOwnProperty, + __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; + + angular.module('Tasks').factory('SettingsModel', [ + '_Model', function(_Model) { + var SettingsModel; + SettingsModel = (function(_super) { + __extends(SettingsModel, _super); + + function SettingsModel() { + this._nameCache = {}; + SettingsModel.__super__.constructor.call(this); + } + + SettingsModel.prototype.add = function(data, clearCache) { + if (clearCache == null) { + clearCache = true; + } + this._nameCache[data.displayname] = data; + if (angular.isDefined(data.id)) { + return SettingsModel.__super__.add.call(this, data, clearCache); + } + }; + + SettingsModel.prototype.toggle = function(type, setting) { + var set; + set = this.getById(type); + return this.getById(type)[setting] = !set[setting]; + }; + + return SettingsModel; + + })(_Model); + return new SettingsModel(); + } + ]); + +}).call(this); + (function() { var __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; @@ -1925,11 +2098,96 @@ successCallback = function() { return _this.deferred.resolve(); }; + this.getCollections(); + this.getSettings(); this.getLists(); this.getTasks(successCallback); return this.deferred.promise; }; + Persistence.prototype.getCollections = function(onSuccess, showLoading) { + var failureCallbackWrapper, params, successCallbackWrapper, + _this = this; + if (showLoading == null) { + showLoading = true; + } + onSuccess || (onSuccess = function() {}); + if (showLoading) { + this._Loading.increase(); + successCallbackWrapper = function(data) { + onSuccess(); + return _this._Loading.decrease(); + }; + failureCallbackWrapper = function(data) { + return _this._Loading.decrease(); + }; + } else { + successCallbackWrapper = function(data) { + return onSuccess(); + }; + failureCallbackWrapper = function(data) {}; + } + params = { + onSuccess: successCallbackWrapper, + onFailure: failureCallbackWrapper + }; + return this._request.get('/apps/tasks_enhanced/collections', params); + }; + + Persistence.prototype.getSettings = function(onSuccess, showLoading) { + var failureCallbackWrapper, params, successCallbackWrapper, + _this = this; + if (showLoading == null) { + showLoading = true; + } + onSuccess || (onSuccess = function() {}); + if (showLoading) { + this._Loading.increase(); + successCallbackWrapper = function(data) { + onSuccess(); + return _this._Loading.decrease(); + }; + failureCallbackWrapper = function(data) { + return _this._Loading.decrease(); + }; + } else { + successCallbackWrapper = function(data) { + return onSuccess(); + }; + failureCallbackWrapper = function(data) {}; + } + params = { + onSuccess: successCallbackWrapper, + onFailure: failureCallbackWrapper + }; + return this._request.get('/apps/tasks_enhanced/settings', params); + }; + + Persistence.prototype.setVisibility = function(collectionID, visibility) { + var params; + params = { + routeParams: { + collectionID: collectionID, + visibility: visibility + } + }; + return this._request.post('/apps/tasks_enhanced/collection/\ + {collectionID}/visibility/{visibility}', params); + }; + + Persistence.prototype.setting = function(type, setting, value) { + var params; + params = { + routeParams: { + type: type, + setting: setting, + value: +value + } + }; + return this._request.post('/apps/tasks_enhanced/settings/\ + {type}/{setting}/{value}', params); + }; + Persistence.prototype.getLists = function(onSuccess, showLoading, which) { var failureCallbackWrapper, params, successCallbackWrapper, _this = this; @@ -2223,6 +2481,16 @@ return this._request.post('/apps/tasks_enhanced/tasks/{taskID}/note', params); }; + Persistence.prototype.setShowHidden = function(showHidden) { + var params; + params = { + routeParams: { + showHidden: +showHidden + } + }; + return this._request.post('/apps/tasks_enhanced/settings/showhidden/{showHidden}', params); + }; + return Persistence; })(); @@ -2246,9 +2514,11 @@ ]); angular.module('Tasks').factory('Publisher', [ - '_Publisher', 'ListsModel', 'TasksModel', function(_Publisher, ListsModel, TasksModel) { + '_Publisher', 'ListsModel', 'TasksModel', 'CollectionsModel', 'SettingsModel', function(_Publisher, ListsModel, TasksModel, CollectionsModel, SettingsModel) { var publisher; publisher = new _Publisher(); + publisher.subscribeObjectTo(CollectionsModel, 'collections'); + publisher.subscribeObjectTo(SettingsModel, 'settings'); publisher.subscribeObjectTo(ListsModel, 'lists'); publisher.subscribeObjectTo(TasksModel, 'tasks'); return publisher; @@ -2264,7 +2534,6 @@ Status = (function() { function Status() { this._$status = { - showhidden: true, searchActive: false, addingList: false, focusTaskInput: false diff --git a/js/vendor/bootstrap/ui-bootstrap-custom-tpls-0.10.0.js b/js/vendor/bootstrap/ui-bootstrap-custom-tpls-0.10.0.js new file mode 100644 index 00000000..a5b865d2 --- /dev/null +++ b/js/vendor/bootstrap/ui-bootstrap-custom-tpls-0.10.0.js @@ -0,0 +1,817 @@ +/* + * angular-ui-bootstrap + * http://angular-ui.github.io/bootstrap/ + + * Version: 0.10.0 - 2014-01-15 + * License: MIT + */ +angular.module("ui.bootstrap", ["ui.bootstrap.tpls", "ui.bootstrap.transition","ui.bootstrap.modal","ui.bootstrap.tabs"]); +angular.module("ui.bootstrap.tpls", ["template/modal/backdrop.html","template/modal/window.html","template/tabs/tab.html","template/tabs/tabset.html"]); +angular.module('ui.bootstrap.transition', []) + +/** + * $transition service provides a consistent interface to trigger CSS 3 transitions and to be informed when they complete. + * @param {DOMElement} element The DOMElement that will be animated. + * @param {string|object|function} trigger The thing that will cause the transition to start: + * - As a string, it represents the css class to be added to the element. + * - As an object, it represents a hash of style attributes to be applied to the element. + * - As a function, it represents a function to be called that will cause the transition to occur. + * @return {Promise} A promise that is resolved when the transition finishes. + */ +.factory('$transition', ['$q', '$timeout', '$rootScope', function($q, $timeout, $rootScope) { + + var $transition = function(element, trigger, options) { + options = options || {}; + var deferred = $q.defer(); + var endEventName = $transition[options.animation ? "animationEndEventName" : "transitionEndEventName"]; + + var transitionEndHandler = function(event) { + $rootScope.$apply(function() { + element.unbind(endEventName, transitionEndHandler); + deferred.resolve(element); + }); + }; + + if (endEventName) { + element.bind(endEventName, transitionEndHandler); + } + + // Wrap in a timeout to allow the browser time to update the DOM before the transition is to occur + $timeout(function() { + if ( angular.isString(trigger) ) { + element.addClass(trigger); + } else if ( angular.isFunction(trigger) ) { + trigger(element); + } else if ( angular.isObject(trigger) ) { + element.css(trigger); + } + //If browser does not support transitions, instantly resolve + if ( !endEventName ) { + deferred.resolve(element); + } + }); + + // Add our custom cancel function to the promise that is returned + // We can call this if we are about to run a new transition, which we know will prevent this transition from ending, + // i.e. it will therefore never raise a transitionEnd event for that transition + deferred.promise.cancel = function() { + if ( endEventName ) { + element.unbind(endEventName, transitionEndHandler); + } + deferred.reject('Transition cancelled'); + }; + + return deferred.promise; + }; + + // Work out the name of the transitionEnd event + var transElement = document.createElement('trans'); + var transitionEndEventNames = { + 'WebkitTransition': 'webkitTransitionEnd', + 'MozTransition': 'transitionend', + 'OTransition': 'oTransitionEnd', + 'transition': 'transitionend' + }; + var animationEndEventNames = { + 'WebkitTransition': 'webkitAnimationEnd', + 'MozTransition': 'animationend', + 'OTransition': 'oAnimationEnd', + 'transition': 'animationend' + }; + function findEndEventName(endEventNames) { + for (var name in endEventNames){ + if (transElement.style[name] !== undefined) { + return endEventNames[name]; + } + } + } + $transition.transitionEndEventName = findEndEventName(transitionEndEventNames); + $transition.animationEndEventName = findEndEventName(animationEndEventNames); + return $transition; +}]); + +angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition']) + +/** + * A helper, internal data structure that acts as a map but also allows getting / removing + * elements in the LIFO order + */ + .factory('$$stackedMap', function () { + return { + createNew: function () { + var stack = []; + + return { + add: function (key, value) { + stack.push({ + key: key, + value: value + }); + }, + get: function (key) { + for (var i = 0; i < stack.length; i++) { + if (key == stack[i].key) { + return stack[i]; + } + } + }, + keys: function() { + var keys = []; + for (var i = 0; i < stack.length; i++) { + keys.push(stack[i].key); + } + return keys; + }, + top: function () { + return stack[stack.length - 1]; + }, + remove: function (key) { + var idx = -1; + for (var i = 0; i < stack.length; i++) { + if (key == stack[i].key) { + idx = i; + break; + } + } + return stack.splice(idx, 1)[0]; + }, + removeTop: function () { + return stack.splice(stack.length - 1, 1)[0]; + }, + length: function () { + return stack.length; + } + }; + } + }; + }) + +/** + * A helper directive for the $modal service. It creates a backdrop element. + */ + .directive('modalBackdrop', ['$timeout', function ($timeout) { + return { + restrict: 'EA', + replace: true, + templateUrl: 'template/modal/backdrop.html', + link: function (scope) { + + scope.animate = false; + + //trigger CSS transitions + $timeout(function () { + scope.animate = true; + }); + } + }; + }]) + + .directive('modalWindow', ['$modalStack', '$timeout', function ($modalStack, $timeout) { + return { + restrict: 'EA', + scope: { + index: '@', + animate: '=' + }, + replace: true, + transclude: true, + templateUrl: 'template/modal/window.html', + link: function (scope, element, attrs) { + scope.windowClass = attrs.windowClass || ''; + + $timeout(function () { + // trigger CSS transitions + scope.animate = true; + // focus a freshly-opened modal + element[0].focus(); + }); + + scope.close = function (evt) { + var modal = $modalStack.getTop(); + if (modal && modal.value.backdrop && modal.value.backdrop != 'static' && (evt.target === evt.currentTarget)) { + evt.preventDefault(); + evt.stopPropagation(); + $modalStack.dismiss(modal.key, 'backdrop click'); + } + }; + } + }; + }]) + + .factory('$modalStack', ['$transition', '$timeout', '$document', '$compile', '$rootScope', '$$stackedMap', + function ($transition, $timeout, $document, $compile, $rootScope, $$stackedMap) { + + var OPENED_MODAL_CLASS = 'modal-open'; + + var backdropDomEl, backdropScope; + var openedWindows = $$stackedMap.createNew(); + var $modalStack = {}; + + function backdropIndex() { + var topBackdropIndex = -1; + var opened = openedWindows.keys(); + for (var i = 0; i < opened.length; i++) { + if (openedWindows.get(opened[i]).value.backdrop) { + topBackdropIndex = i; + } + } + return topBackdropIndex; + } + + $rootScope.$watch(backdropIndex, function(newBackdropIndex){ + if (backdropScope) { + backdropScope.index = newBackdropIndex; + } + }); + + function removeModalWindow(modalInstance) { + + var body = $document.find('#tasks_enhanced_wrapper').eq(0); + var modalWindow = openedWindows.get(modalInstance).value; + + //clean up the stack + openedWindows.remove(modalInstance); + + //remove window DOM element + removeAfterAnimate(modalWindow.modalDomEl, modalWindow.modalScope, 300, checkRemoveBackdrop); + body.toggleClass(OPENED_MODAL_CLASS, openedWindows.length() > 0); + } + + function checkRemoveBackdrop() { + //remove backdrop if no longer needed + if (backdropDomEl && backdropIndex() == -1) { + var backdropScopeRef = backdropScope; + removeAfterAnimate(backdropDomEl, backdropScope, 150, function () { + backdropScopeRef.$destroy(); + backdropScopeRef = null; + }); + backdropDomEl = undefined; + backdropScope = undefined; + } + } + + function removeAfterAnimate(domEl, scope, emulateTime, done) { + // Closing animation + scope.animate = false; + + var transitionEndEventName = $transition.transitionEndEventName; + if (transitionEndEventName) { + // transition out + var timeout = $timeout(afterAnimating, emulateTime); + + domEl.bind(transitionEndEventName, function () { + $timeout.cancel(timeout); + afterAnimating(); + scope.$apply(); + }); + } else { + // Ensure this call is async + $timeout(afterAnimating, 0); + } + + function afterAnimating() { + if (afterAnimating.done) { + return; + } + afterAnimating.done = true; + + domEl.remove(); + if (done) { + done(); + } + } + } + + $document.bind('keydown', function (evt) { + var modal; + + if (evt.which === 27) { + modal = openedWindows.top(); + if (modal && modal.value.keyboard) { + $rootScope.$apply(function () { + $modalStack.dismiss(modal.key); + }); + } + } + }); + + $modalStack.open = function (modalInstance, modal) { + + openedWindows.add(modalInstance, { + deferred: modal.deferred, + modalScope: modal.scope, + backdrop: modal.backdrop, + keyboard: modal.keyboard + }); + + var body = $document.find('#tasks_enhanced_wrapper').eq(0), + currBackdropIndex = backdropIndex(); + + if (currBackdropIndex >= 0 && !backdropDomEl) { + backdropScope = $rootScope.$new(true); + backdropScope.index = currBackdropIndex; + backdropDomEl = $compile('
')(backdropScope); + body.append(backdropDomEl); + } + + var angularDomEl = angular.element('
'); + angularDomEl.attr('window-class', modal.windowClass); + angularDomEl.attr('index', openedWindows.length() - 1); + angularDomEl.attr('animate', 'animate'); + angularDomEl.html(modal.content); + + var modalDomEl = $compile(angularDomEl)(modal.scope); + openedWindows.top().value.modalDomEl = modalDomEl; + body.append(modalDomEl); + body.addClass(OPENED_MODAL_CLASS); + }; + + $modalStack.close = function (modalInstance, result) { + var modalWindow = openedWindows.get(modalInstance).value; + if (modalWindow) { + modalWindow.deferred.resolve(result); + removeModalWindow(modalInstance); + } + }; + + $modalStack.dismiss = function (modalInstance, reason) { + var modalWindow = openedWindows.get(modalInstance).value; + if (modalWindow) { + modalWindow.deferred.reject(reason); + removeModalWindow(modalInstance); + } + }; + + $modalStack.dismissAll = function (reason) { + var topModal = this.getTop(); + while (topModal) { + this.dismiss(topModal.key, reason); + topModal = this.getTop(); + } + }; + + $modalStack.getTop = function () { + return openedWindows.top(); + }; + + return $modalStack; + }]) + + .provider('$modal', function () { + + var $modalProvider = { + options: { + backdrop: true, //can be also false or 'static' + keyboard: true + }, + $get: ['$injector', '$rootScope', '$q', '$http', '$templateCache', '$controller', '$modalStack', + function ($injector, $rootScope, $q, $http, $templateCache, $controller, $modalStack) { + + var $modal = {}; + + function getTemplatePromise(options) { + return options.template ? $q.when(options.template) : + $http.get(options.templateUrl, {cache: $templateCache}).then(function (result) { + return result.data; + }); + } + + function getResolvePromises(resolves) { + var promisesArr = []; + angular.forEach(resolves, function (value, key) { + if (angular.isFunction(value) || angular.isArray(value)) { + promisesArr.push($q.when($injector.invoke(value))); + } + }); + return promisesArr; + } + + $modal.open = function (modalOptions) { + + var modalResultDeferred = $q.defer(); + var modalOpenedDeferred = $q.defer(); + + //prepare an instance of a modal to be injected into controllers and returned to a caller + var modalInstance = { + result: modalResultDeferred.promise, + opened: modalOpenedDeferred.promise, + close: function (result) { + $modalStack.close(modalInstance, result); + }, + dismiss: function (reason) { + $modalStack.dismiss(modalInstance, reason); + } + }; + + //merge and clean up options + modalOptions = angular.extend({}, $modalProvider.options, modalOptions); + modalOptions.resolve = modalOptions.resolve || {}; + + //verify options + if (!modalOptions.template && !modalOptions.templateUrl) { + throw new Error('One of template or templateUrl options is required.'); + } + + var templateAndResolvePromise = + $q.all([getTemplatePromise(modalOptions)].concat(getResolvePromises(modalOptions.resolve))); + + + templateAndResolvePromise.then(function resolveSuccess(tplAndVars) { + + var modalScope = (modalOptions.scope || $rootScope).$new(); + modalScope.$close = modalInstance.close; + modalScope.$dismiss = modalInstance.dismiss; + + var ctrlInstance, ctrlLocals = {}; + var resolveIter = 1; + + //controllers + if (modalOptions.controller) { + ctrlLocals.$scope = modalScope; + ctrlLocals.$modalInstance = modalInstance; + angular.forEach(modalOptions.resolve, function (value, key) { + ctrlLocals[key] = tplAndVars[resolveIter++]; + }); + + ctrlInstance = $controller(modalOptions.controller, ctrlLocals); + } + + $modalStack.open(modalInstance, { + scope: modalScope, + deferred: modalResultDeferred, + content: tplAndVars[0], + backdrop: modalOptions.backdrop, + keyboard: modalOptions.keyboard, + windowClass: modalOptions.windowClass + }); + + }, function resolveError(reason) { + modalResultDeferred.reject(reason); + }); + + templateAndResolvePromise.then(function () { + modalOpenedDeferred.resolve(true); + }, function () { + modalOpenedDeferred.reject(false); + }); + + return modalInstance; + }; + + return $modal; + }] + }; + + return $modalProvider; + }); + + +/** + * @ngdoc overview + * @name ui.bootstrap.tabs + * + * @description + * AngularJS version of the tabs directive. + */ + +angular.module('ui.bootstrap.tabs', []) + +.controller('TabsetController', ['$scope', function TabsetCtrl($scope) { + var ctrl = this, + tabs = ctrl.tabs = $scope.tabs = []; + + ctrl.select = function(tab) { + angular.forEach(tabs, function(tab) { + tab.active = false; + }); + tab.active = true; + }; + + ctrl.addTab = function addTab(tab) { + tabs.push(tab); + if (tabs.length === 1 || tab.active) { + ctrl.select(tab); + } + }; + + ctrl.removeTab = function removeTab(tab) { + var index = tabs.indexOf(tab); + //Select a new tab if the tab to be removed is selected + if (tab.active && tabs.length > 1) { + //If this is the last tab, select the previous tab. else, the next tab. + var newActiveIndex = index == tabs.length - 1 ? index - 1 : index + 1; + ctrl.select(tabs[newActiveIndex]); + } + tabs.splice(index, 1); + }; +}]) + +/** + * @ngdoc directive + * @name ui.bootstrap.tabs.directive:tabset + * @restrict EA + * + * @description + * Tabset is the outer container for the tabs directive + * + * @param {boolean=} vertical Whether or not to use vertical styling for the tabs. + * @param {boolean=} justified Whether or not to use justified styling for the tabs. + * + * @example + + + + First Content! + Second Content! + +
+ + First Vertical Content! + Second Vertical Content! + + + First Justified Content! + Second Justified Content! + +
+
+ */ +.directive('tabset', function() { + return { + restrict: 'EA', + transclude: true, + replace: true, + scope: {}, + controller: 'TabsetController', + templateUrl: 'template/tabs/tabset.html', + link: function(scope, element, attrs) { + scope.vertical = angular.isDefined(attrs.vertical) ? scope.$parent.$eval(attrs.vertical) : false; + scope.justified = angular.isDefined(attrs.justified) ? scope.$parent.$eval(attrs.justified) : false; + scope.type = angular.isDefined(attrs.type) ? scope.$parent.$eval(attrs.type) : 'tabs'; + } + }; +}) + +/** + * @ngdoc directive + * @name ui.bootstrap.tabs.directive:tab + * @restrict EA + * + * @param {string=} heading The visible heading, or title, of the tab. Set HTML headings with {@link ui.bootstrap.tabs.directive:tabHeading tabHeading}. + * @param {string=} select An expression to evaluate when the tab is selected. + * @param {boolean=} active A binding, telling whether or not this tab is selected. + * @param {boolean=} disabled A binding, telling whether or not this tab is disabled. + * + * @description + * Creates a tab with a heading and content. Must be placed within a {@link ui.bootstrap.tabs.directive:tabset tabset}. + * + * @example + + +
+ + +
+ + First Tab + + Alert me! + Second Tab, with alert callback and html heading! + + + {{item.content}} + + +
+
+ + function TabsDemoCtrl($scope) { + $scope.items = [ + { title:"Dynamic Title 1", content:"Dynamic Item 0" }, + { title:"Dynamic Title 2", content:"Dynamic Item 1", disabled: true } + ]; + + $scope.alertMe = function() { + setTimeout(function() { + alert("You've selected the alert tab!"); + }); + }; + }; + +
+ */ + +/** + * @ngdoc directive + * @name ui.bootstrap.tabs.directive:tabHeading + * @restrict EA + * + * @description + * Creates an HTML heading for a {@link ui.bootstrap.tabs.directive:tab tab}. Must be placed as a child of a tab element. + * + * @example + + + + + HTML in my titles?! + And some content, too! + + + Icon heading?!? + That's right. + + + + + */ +.directive('tab', ['$parse', function($parse) { + return { + require: '^tabset', + restrict: 'EA', + replace: true, + templateUrl: 'template/tabs/tab.html', + transclude: true, + scope: { + heading: '@', + onSelect: '&select', //This callback is called in contentHeadingTransclude + //once it inserts the tab's content into the dom + onDeselect: '&deselect' + }, + controller: function() { + //Empty controller so other directives can require being 'under' a tab + }, + compile: function(elm, attrs, transclude) { + return function postLink(scope, elm, attrs, tabsetCtrl) { + var getActive, setActive; + if (attrs.active) { + getActive = $parse(attrs.active); + setActive = getActive.assign; + scope.$parent.$watch(getActive, function updateActive(value, oldVal) { + // Avoid re-initializing scope.active as it is already initialized + // below. (watcher is called async during init with value === + // oldVal) + if (value !== oldVal) { + scope.active = !!value; + } + }); + scope.active = getActive(scope.$parent); + } else { + setActive = getActive = angular.noop; + } + + scope.$watch('active', function(active) { + // Note this watcher also initializes and assigns scope.active to the + // attrs.active expression. + setActive(scope.$parent, active); + if (active) { + tabsetCtrl.select(scope); + scope.onSelect(); + } else { + scope.onDeselect(); + } + }); + + scope.disabled = false; + if ( attrs.disabled ) { + scope.$parent.$watch($parse(attrs.disabled), function(value) { + scope.disabled = !! value; + }); + } + + scope.select = function() { + if ( ! scope.disabled ) { + scope.active = true; + } + }; + + tabsetCtrl.addTab(scope); + scope.$on('$destroy', function() { + tabsetCtrl.removeTab(scope); + }); + + + //We need to transclude later, once the content container is ready. + //when this link happens, we're inside a tab heading. + scope.$transcludeFn = transclude; + }; + } + }; +}]) + +.directive('tabHeadingTransclude', [function() { + return { + restrict: 'A', + require: '^tab', + link: function(scope, elm, attrs, tabCtrl) { + scope.$watch('headingElement', function updateHeadingElement(heading) { + if (heading) { + elm.html(''); + elm.append(heading); + } + }); + } + }; +}]) + +.directive('tabContentTransclude', function() { + return { + restrict: 'A', + require: '^tabset', + link: function(scope, elm, attrs) { + var tab = scope.$eval(attrs.tabContentTransclude); + + //Now our tab is ready to be transcluded: both the tab heading area + //and the tab content area are loaded. Transclude 'em both. + tab.$transcludeFn(tab.$parent, function(contents) { + angular.forEach(contents, function(node) { + if (isTabHeading(node)) { + //Let tabHeadingTransclude know. + tab.headingElement = node; + } else { + elm.append(node); + } + }); + }); + } + }; + function isTabHeading(node) { + return node.tagName && ( + node.hasAttribute('tab-heading') || + node.hasAttribute('data-tab-heading') || + node.tagName.toLowerCase() === 'tab-heading' || + node.tagName.toLowerCase() === 'data-tab-heading' + ); + } +}) + +; + +angular.module("template/modal/backdrop.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("template/modal/backdrop.html", + "
"); +}]); + +angular.module("template/modal/window.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("template/modal/window.html", + "
\n" + + "
\n" + + "
"); +}]); + +angular.module("template/tabs/pane.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("template/tabs/pane.html", + "
\n" + + ""); +}]); + +angular.module("template/tabs/tab.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("template/tabs/tab.html", + "
  • \n" + + " {{heading}}\n" + + "
  • \n" + + ""); +}]); + +angular.module("template/tabs/tabs.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("template/tabs/tabs.html", + "
    \n" + + " \n" + + "
    \n" + + "
    \n" + + ""); +}]); + +angular.module("template/tabs/tabset-titles.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("template/tabs/tabset-titles.html", + "
      \n" + + "
    \n" + + ""); +}]); + +angular.module("template/tabs/tabset.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("template/tabs/tabset.html", + "\n" + + "
    \n" + + "
      \n" + + "
      \n" + + "
      \n" + + "
      \n" + + "
      \n" + + "
      \n" + + ""); +}]); diff --git a/js/vendor/bootstrap/ui-bootstrap-custom-tpls-0.10.0.min.js b/js/vendor/bootstrap/ui-bootstrap-custom-tpls-0.10.0.min.js new file mode 100644 index 00000000..5b18245c --- /dev/null +++ b/js/vendor/bootstrap/ui-bootstrap-custom-tpls-0.10.0.min.js @@ -0,0 +1,8 @@ +/* + * angular-ui-bootstrap + * http://angular-ui.github.io/bootstrap/ + + * Version: 0.10.0 - 2014-01-15 + * License: MIT + */ +angular.module("ui.bootstrap",["ui.bootstrap.tpls","ui.bootstrap.transition","ui.bootstrap.modal","ui.bootstrap.tabs"]),angular.module("ui.bootstrap.tpls",["template/modal/backdrop.html","template/modal/window.html","template/tabs/tab.html","template/tabs/tabset.html"]),angular.module("ui.bootstrap.transition",[]).factory("$transition",["$q","$timeout","$rootScope",function(a,b,c){function d(a){for(var b in a)if(void 0!==f.style[b])return a[b]}var e=function(d,f,g){g=g||{};var h=a.defer(),i=e[g.animation?"animationEndEventName":"transitionEndEventName"],j=function(){c.$apply(function(){d.unbind(i,j),h.resolve(d)})};return i&&d.bind(i,j),b(function(){angular.isString(f)?d.addClass(f):angular.isFunction(f)?f(d):angular.isObject(f)&&d.css(f),i||h.resolve(d)}),h.promise.cancel=function(){i&&d.unbind(i,j),h.reject("Transition cancelled")},h.promise},f=document.createElement("trans"),g={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd",transition:"transitionend"},h={WebkitTransition:"webkitAnimationEnd",MozTransition:"animationend",OTransition:"oAnimationEnd",transition:"animationend"};return e.transitionEndEventName=d(g),e.animationEndEventName=d(h),e}]),angular.module("ui.bootstrap.modal",["ui.bootstrap.transition"]).factory("$$stackedMap",function(){return{createNew:function(){var a=[];return{add:function(b,c){a.push({key:b,value:c})},get:function(b){for(var c=0;c0)}function i(){if(k&&-1==g()){var a=l;j(k,l,150,function(){a.$destroy(),a=null}),k=void 0,l=void 0}}function j(c,d,e,f){function g(){g.done||(g.done=!0,c.remove(),f&&f())}d.animate=!1;var h=a.transitionEndEventName;if(h){var i=b(g,e);c.bind(h,function(){b.cancel(i),g(),d.$apply()})}else b(g,0)}var k,l,m="modal-open",n=f.createNew(),o={};return e.$watch(g,function(a){l&&(l.index=a)}),c.bind("keydown",function(a){var b;27===a.which&&(b=n.top(),b&&b.value.keyboard&&e.$apply(function(){o.dismiss(b.key)}))}),o.open=function(a,b){n.add(a,{deferred:b.deferred,modalScope:b.scope,backdrop:b.backdrop,keyboard:b.keyboard});var f=c.find("#tasks_enhanced_wrapper").eq(0),h=g();h>=0&&!k&&(l=e.$new(!0),l.index=h,k=d("
      ")(l),f.append(k));var i=angular.element("
      ");i.attr("window-class",b.windowClass),i.attr("index",n.length()-1),i.attr("animate","animate"),i.html(b.content);var j=d(i)(b.scope);n.top().value.modalDomEl=j,f.append(j),f.addClass(m)},o.close=function(a,b){var c=n.get(a).value;c&&(c.deferred.resolve(b),h(a))},o.dismiss=function(a,b){var c=n.get(a).value;c&&(c.deferred.reject(b),h(a))},o.dismissAll=function(a){for(var b=this.getTop();b;)this.dismiss(b.key,a),b=this.getTop()},o.getTop=function(){return n.top()},o}]).provider("$modal",function(){var a={options:{backdrop:!0,keyboard:!0},$get:["$injector","$rootScope","$q","$http","$templateCache","$controller","$modalStack",function(b,c,d,e,f,g,h){function i(a){return a.template?d.when(a.template):e.get(a.templateUrl,{cache:f}).then(function(a){return a.data})}function j(a){var c=[];return angular.forEach(a,function(a){(angular.isFunction(a)||angular.isArray(a))&&c.push(d.when(b.invoke(a)))}),c}var k={};return k.open=function(b){var e=d.defer(),f=d.defer(),k={result:e.promise,opened:f.promise,close:function(a){h.close(k,a)},dismiss:function(a){h.dismiss(k,a)}};if(b=angular.extend({},a.options,b),b.resolve=b.resolve||{},!b.template&&!b.templateUrl)throw new Error("One of template or templateUrl options is required.");var l=d.all([i(b)].concat(j(b.resolve)));return l.then(function(a){var d=(b.scope||c).$new();d.$close=k.close,d.$dismiss=k.dismiss;var f,i={},j=1;b.controller&&(i.$scope=d,i.$modalInstance=k,angular.forEach(b.resolve,function(b,c){i[c]=a[j++]}),f=g(b.controller,i)),h.open(k,{scope:d,deferred:e,content:a[0],backdrop:b.backdrop,keyboard:b.keyboard,windowClass:b.windowClass})},function(a){e.reject(a)}),l.then(function(){f.resolve(!0)},function(){f.reject(!1)}),k},k}]};return a}),angular.module("ui.bootstrap.tabs",[]).controller("TabsetController",["$scope",function(a){var b=this,c=b.tabs=a.tabs=[];b.select=function(a){angular.forEach(c,function(a){a.active=!1}),a.active=!0},b.addTab=function(a){c.push(a),(1===c.length||a.active)&&b.select(a)},b.removeTab=function(a){var d=c.indexOf(a);if(a.active&&c.length>1){var e=d==c.length-1?d-1:d+1;b.select(c[e])}c.splice(d,1)}}]).directive("tabset",function(){return{restrict:"EA",transclude:!0,replace:!0,scope:{},controller:"TabsetController",templateUrl:"template/tabs/tabset.html",link:function(a,b,c){a.vertical=angular.isDefined(c.vertical)?a.$parent.$eval(c.vertical):!1,a.justified=angular.isDefined(c.justified)?a.$parent.$eval(c.justified):!1,a.type=angular.isDefined(c.type)?a.$parent.$eval(c.type):"tabs"}}}).directive("tab",["$parse",function(a){return{require:"^tabset",restrict:"EA",replace:!0,templateUrl:"template/tabs/tab.html",transclude:!0,scope:{heading:"@",onSelect:"&select",onDeselect:"&deselect"},controller:function(){},compile:function(b,c,d){return function(b,c,e,f){var g,h;e.active?(g=a(e.active),h=g.assign,b.$parent.$watch(g,function(a,c){a!==c&&(b.active=!!a)}),b.active=g(b.$parent)):h=g=angular.noop,b.$watch("active",function(a){h(b.$parent,a),a?(f.select(b),b.onSelect()):b.onDeselect()}),b.disabled=!1,e.disabled&&b.$parent.$watch(a(e.disabled),function(a){b.disabled=!!a}),b.select=function(){b.disabled||(b.active=!0)},f.addTab(b),b.$on("$destroy",function(){f.removeTab(b)}),b.$transcludeFn=d}}}}]).directive("tabHeadingTransclude",[function(){return{restrict:"A",require:"^tab",link:function(a,b){a.$watch("headingElement",function(a){a&&(b.html(""),b.append(a))})}}}]).directive("tabContentTransclude",function(){function a(a){return a.tagName&&(a.hasAttribute("tab-heading")||a.hasAttribute("data-tab-heading")||"tab-heading"===a.tagName.toLowerCase()||"data-tab-heading"===a.tagName.toLowerCase())}return{restrict:"A",require:"^tabset",link:function(b,c,d){var e=b.$eval(d.tabContentTransclude);e.$transcludeFn(e.$parent,function(b){angular.forEach(b,function(b){a(b)?e.headingElement=b:c.append(b)})})}}}),angular.module("template/modal/backdrop.html",[]).run(["$templateCache",function(a){a.put("template/modal/backdrop.html",'')}]),angular.module("template/modal/window.html",[]).run(["$templateCache",function(a){a.put("template/modal/window.html",'')}]),angular.module("template/tabs/pane.html",[]).run(["$templateCache",function(a){a.put("template/tabs/pane.html",'
      \n')}]),angular.module("template/tabs/tab.html",[]).run(["$templateCache",function(a){a.put("template/tabs/tab.html",'
    • \n {{heading}}\n
    • \n')}]),angular.module("template/tabs/tabs.html",[]).run(["$templateCache",function(a){a.put("template/tabs/tabs.html",'
      \n \n
      \n
      \n')}]),angular.module("template/tabs/tabset-titles.html",[]).run(["$templateCache",function(a){a.put("template/tabs/tabset-titles.html","
        \n
      \n")}]),angular.module("template/tabs/tabset.html",[]).run(["$templateCache",function(a){a.put("template/tabs/tabset.html",'\n
      \n \n
      \n
      \n
      \n
      \n
      \n')}]); \ No newline at end of file diff --git a/l10n/de.php b/l10n/de.php index 2280dd6e..87e789a5 100644 --- a/l10n/de.php +++ b/l10n/de.php @@ -1,6 +1,12 @@ "Ungültige Datums-/Zeitangabe", "Search..." => "Suche...", +"Settings" => "Einstellungen", +"General" => "Allgemein", +"Visible" => "Sichtbar", +"Hidden" => "Versteckt", +"Automatic" => "Automatisch", +"Visibility of Smart Collections" => "Sichtbarkeit der Smart-Collections", "Tasks" => "Aufgaben", "Today" => "Heute", "Important" => "Wichtig", diff --git a/lib/controller/collectionscontroller.php b/lib/controller/collectionscontroller.php new file mode 100644 index 00000000..43da2b82 --- /dev/null +++ b/lib/controller/collectionscontroller.php @@ -0,0 +1,93 @@ +. +* +*/ + +namespace OCA\Tasks_enhanced\Controller; + +use OCA\Tasks_enhanced\Controller, + OCP\AppFramework\Http\JSONResponse; + +class CollectionsController extends Controller { + + /** + * @NoAdminRequired + */ + public function getCollections(){ + $l = \OCP\Util::getL10N('tasks_enhanced'); + $collections = array( + array( + 'id' => "starred", + 'displayname' => (string)$l->t('Important'), + 'show' => 2), + array( + 'id' => "today", + 'displayname' => (string)$l->t('Today'), + 'show' => 2), + array( + 'id' => "week", + 'displayname' => (string)$l->t('Week'), + 'show' => 2), + array( + 'id' => "all", + 'displayname' => (string)$l->t('All'), + 'show' => 2), + array( + 'id' => "current", + 'displayname' => (string)$l->t('Current'), + 'show' => 2), + array( + 'id' => "completed", + 'displayname' => (string)$l->t('Done'), + 'show' => 2) + ); + foreach ($collections as $key => $collection){ + try{ + $tmp = (int)\OCP\Config::getUserValue($this->api->getUserId(), 'tasks_enhanced','show_'.$collection['id']); + if (!in_array($tmp, array(0,1,2))) { + $tmp = 2; + \OCP\Config::setUserValue($this->api->getUserId(), 'tasks_enhanced','show_'.$collection['id'],$tmp); + } + $collections[$key]['show'] = $tmp; + }catch(\Exception $e) { + \OCP\Util::writeLog('tasks_enhanced', $e->getMessage(), \OCP\Util::ERROR); + } + } + $result = array( + 'data' => array( + 'collections' => $collections + ) + ); + $response = new JSONResponse(); + $response->setData($result); + return $response; + } + + public function setVisibility(){ + $collectionId = (string) $this->params('collectionID'); + $vis = (int) $this->params('visibility'); + if (in_array($vis, array(0,1,2))){ + \OCP\Config::setUserValue($this->api->getUserId(), 'tasks_enhanced','show_'.$collectionId,$vis); + } + $response = new JSONResponse(); + return $response; + } +} \ No newline at end of file diff --git a/lib/controller/pagecontroller.php b/lib/controller/pagecontroller.php index 06df01ff..294316cd 100644 --- a/lib/controller/pagecontroller.php +++ b/lib/controller/pagecontroller.php @@ -29,16 +29,19 @@ class PageController extends Controller { \OCP\Util::addScript('tasks_enhanced', 'vendor/angularjs/angular-route'); \OCP\Util::addScript('tasks_enhanced', 'vendor/angularjs/angular-animate'); \OCP\Util::addScript('tasks_enhanced', 'vendor/momentjs/moment'); + \OCP\Util::addScript('tasks_enhanced', 'vendor/bootstrap/ui-bootstrap-custom-tpls-0.10.0'); } else { \OCP\Util::addScript('tasks_enhanced', 'vendor/angularjs/angular.min'); \OCP\Util::addScript('tasks_enhanced', 'vendor/angularjs/angular-route.min'); \OCP\Util::addScript('tasks_enhanced', 'vendor/angularjs/angular-animate.min'); \OCP\Util::addScript('tasks_enhanced', 'vendor/momentjs/moment.min'); + \OCP\Util::addScript('tasks_enhanced', 'vendor/bootstrap/ui-bootstrap-custom-tpls-0.10.0.min'); } \OCP\Util::addScript('tasks_enhanced', 'public/app'); \OCP\Util::addScript('tasks_enhanced', 'vendor/appframework/app'); \OCP\Util::addScript('tasks_enhanced', 'vendor/timepicker/jquery.ui.timepicker'); \OCP\Util::addStyle('tasks_enhanced', 'style'); + \OCP\Util::addStyle('tasks_enhanced', 'vendor/bootstrap/bootstrap'); $date = new \DateTimeZone(\OC_Calendar_App::getTimezone()); $day = new \DateTime('today', $date); diff --git a/lib/controller/settingscontroller.php b/lib/controller/settingscontroller.php new file mode 100644 index 00000000..e440e279 --- /dev/null +++ b/lib/controller/settingscontroller.php @@ -0,0 +1,59 @@ +. +* +*/ + +namespace OCA\Tasks_enhanced\Controller; + +use OCA\Tasks_enhanced\Controller, + OCP\AppFramework\Http\JSONResponse; + +class SettingsController extends Controller { + + /** + * @NoAdminRequired + */ + public function getSettings(){ + $settings = array( + array( + 'id' => 'various', + 'showHidden' => (int)\OCP\Config::getUserValue($this->api->getUserId(), 'tasks_enhanced','various_showHidden')) + ); + $result = array( + 'data' => array( + 'settings' => $settings + ) + ); + $response = new JSONResponse(); + $response->setData($result); + return $response; + } + + /** + * @NoAdminRequired + */ + public function set(){ + \OCP\Config::setUserValue($this->api->getUserId(), 'tasks_enhanced',$this->params('type').'_'.$this->params('setting'), $this->params('value')); + $response = new JSONResponse(); + $response->setData(); + return $response; + } +} \ No newline at end of file diff --git a/lib/dispatcher.php b/lib/dispatcher.php index 594edc5c..545cf865 100644 --- a/lib/dispatcher.php +++ b/lib/dispatcher.php @@ -13,8 +13,10 @@ namespace OCA\Tasks_enhanced; use OCP\AppFramework\App as MainApp, OCP\AppFramework\IAppContainer, OCA\Tasks_enhanced\Controller\PageController, + OCA\Tasks_enhanced\Controller\CollectionsController, OCA\Tasks_enhanced\Controller\ListsController, - OCA\Tasks_enhanced\Controller\TasksController; + OCA\Tasks_enhanced\Controller\TasksController, + OCA\Tasks_enhanced\Controller\SettingsController; /** * This class manages our app actions @@ -38,12 +40,18 @@ class Dispatcher extends MainApp { $this->container->registerService('PageController', function(IAppContainer $container) { return new PageController($container); }); + $this->container->registerService('CollectionsController', function(IAppContainer $container) { + return new CollectionsController($container); + }); $this->container->registerService('ListsController', function(IAppContainer $container) { return new ListsController($container); }); $this->container->registerService('TasksController', function(IAppContainer $container) { return new TasksController($container); }); + $this->container->registerService('SettingsController', function(IAppContainer $container) { + return new SettingsController($container); + }); } } diff --git a/templates/main.php b/templates/main.php index 3ba5be46..525976c9 100644 --- a/templates/main.php +++ b/templates/main.php @@ -1,4 +1,4 @@ -
      +
      @@ -20,7 +20,7 @@
      @@ -66,12 +69,14 @@ ng-keydown="checkTaskInput($event)"/>
      -
      +
      inc('part.tasklist')); ?> inc('part.collectionall')); ?> inc('part.collectionweek')); ?>
      -
      +
      \ No newline at end of file diff --git a/templates/part.details.php b/templates/part.details.php index ebe369ae..d01cb082 100644 --- a/templates/part.details.php +++ b/templates/part.details.php @@ -1,101 +1,101 @@
      - - - - - - -
      - {{ task.name }} -
      -
      -
      {{ task.name }}
      - -
      + + + + + + +
      + {{ task.name }} +
      +
      +
      {{ task.name }}
      + +
      +
      +
      +
      +
      + + +
      + {{ task.start | startDetails }} +
      + + + + +
      + +
      -
      -
      - - -
      - {{ task.start | startDetails }} +
      + + +
      + {{ task.due | dateDetails }} +
      + + + + +
      + + +
      +
      +
      + + + +
      + {{ task.reminder | reminderDetails:this }} +
      + + + + + + +
      + +
      + -
      - - - -
      - {{ task.reminder | reminderDetails:this }} -
      - - - - - -
      -
      - - -
      -
      - - - -
      -
      -
      - -
      -
      - -
      -
      {{ task.note }}
      -
      -
      -
      {{ task.note }}

      - -
      + --> +
      +
      {{ task.note }}
      +
      +
      +
      {{ task.note }}

      +
      - +
      +
      \ No newline at end of file diff --git a/templates/part.settings.php b/templates/part.settings.php new file mode 100644 index 00000000..cf0dd348 --- /dev/null +++ b/templates/part.settings.php @@ -0,0 +1,29 @@ +
      +
      +

      t('Settings')); ?>

      +
      + t('Done')); ?> +
      +
      +
      + + + t('General')); ?> + Allgemeine Einstellungen + + + t('Smart Collections')); ?> + t('Visibility of Smart Collections')); ?> +
        +
      • + + {{ collection.displayname }} + + + +
      • +
      +
      +
      +
      +
      \ No newline at end of file