diff --git a/plugins/tasklist/skins/larry/tasklist.css b/plugins/tasklist/skins/larry/tasklist.css index 2b88c954..20ce2a62 100644 --- a/plugins/tasklist/skins/larry/tasklist.css +++ b/plugins/tasklist/skins/larry/tasklist.css @@ -1,1278 +1,1292 @@ /** * Roundcube Taklist plugin styles for skin "Larry" * * Copyright (C) 2012, Kolab Systems AG * Screendesign by FLINT / Büro für Gestaltung, bueroflint.com * * The contents are subject to the Creative Commons Attribution-ShareAlike * License. It is allowed to copy, distribute, transmit and to adapt the work * by keeping credits to the original autors in the README file. * See http://creativecommons.org/licenses/by-sa/3.0/ for details. */ #taskbar a.button-tasklist span.button-inner { background-image: url(buttons.png); background-position: 0 0; } #taskbar a.button-tasklist:hover span.button-inner, #taskbar a.button-tasklist.button-selected span.button-inner { background-position: 0 -26px; } ul.toolbarmenu li span.icon.taskadd, #attachmentmenu li a.tasklistlink span.icon.taskadd { background-image: url(buttons.png); background-position: -4px -90px; } .tasklistview div.uidialog { display: none; } body.tasklist.attachmentwin #mainscreen { top: 32px; } .tasklistview #mainscreen { min-width: 1000px !important; min-height: 520px !important; } .tasklistview #header { min-width: 1020px !important; } #sidebar { position: absolute; top: 0; left: 0; bottom: 0; width: 240px; } .tasklistview #searchmenulink { width: 15px; } #tagsbox { position: absolute; top: 42px; left: 0; width: 100%; height: 242px; } #tasklistsbox { position: absolute; top: 300px; left: 0; width: 100%; bottom: 0px; } #tasklistsbox .boxtitle a.iconbutton.search { position: absolute; top: 8px; right: 8px; width: 16px; cursor: pointer; background-position: -2px -317px; } #tasklistsbox .listsearchbox { display: none; } #tasklistsbox .listsearchbox.expanded { display: block; } #tasklistsbox .scroller { top: 34px; } #tasklistsbox .listsearchbox.expanded + .scroller { top: 68px; } #taskselector { margin: -1px 40px 0 0; padding: 0; } #taskselector li { display: inline-block; position: relative; font-size: 90%; padding-right: 0.3em; } .tagcloud li, #taskselector li a { display: inline-block; color: #004458; min-width: 3.5em; padding: 0.2em 0.6em 0.2em 0.6em; text-align: center; text-decoration: none; border: 1px solid #eee; border-color: transparent; } .webkit .tagcloud li, .webkit #taskselector li a { padding-bottom: 0.25em; } #taskselector li:first-child { border-top: 0; border-radius: 4px 4px 0 0; } #taskselector li:last-child { border-bottom: 0; border-radius: 0 0 4px 4px; } #taskselector li.overdue a { color: #b72a2a; font-weight: bold; } #taskselector li.inactive a { color: #97b3bf; } .tagcloud li.selected, #taskselector li.selected a { color: #fff; background: #005d76; background: -moz-linear-gradient(top, #005d76 0%, #004558 100%); background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#005d76), color-stop(100%,#004558)); background: -o-linear-gradient(top, #005d76 0%, #004558 100%); background: -ms-linear-gradient(top, #005d76 0%, #004558 100%); background: linear-gradient(top, #005d76 0%, #004558 100%); box-shadow: inset 0 1px 1px 0 #003645; -o-box-shadow: inset 0 1px 1px 0 #003645; -webkit-box-shadow: inset 0 1px 1px 0 #003645; -moz-box-shadow: inset 0 1px 1px 0 #003645; border-color: #003645; border-radius: 10px; text-shadow: none; } #taskselector li .count { display: none; position: absolute; top: -18px; right: 5px; min-width: 1.8em; padding: 2px 4px; background: #004558; background: -moz-linear-gradient(top, #005d76 0%, #004558 100%); background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#005d76), color-stop(100%,#004558)); background: -o-linear-gradient(top, #005d76 0%, #004558 100%); background: -ms-linear-gradient(top, #005d76 0%, #004558 100%); background: linear-gradient(top, #005d76 0%, #004558 100%); box-shadow: 0 1px 2px 0 rgba(24,24,24,0.6); color: #fff; border-radius: 3px; text-align: center; font-weight: bold; font-size: 80%; text-shadow: none; } #taskselector li .count:after { content: ""; position: absolute; bottom: -5px; left: 50%; margin-left: -5px; border-style: solid; border-width: 5px 5px 0; border-color: #004558 transparent; /* reduce the damage in FF3.0 */ display: block; width: 0; } #taskselector li.overdue .count { background: #ff3800; } #taskselector li.overdue .count:after { border-color: #ff3800 transparent; } .tagcloud { padding: 0; margin: 6px; list-style: none; } .tagcloud li { display: inline-block; color: #004458; padding-right: 0.2em; margin-right: 0.3em; margin-bottom: 0.4em; min-width: 1.2em; cursor: pointer; } .tagcloud li.inactive { color: #89b3be; padding-right: 0.6em; font-size: 80%; /* display: none; */ } .tagcloud li .count { position: relative; top: -1px; margin-left: 5px; padding: 0.15em 0.5em; font-size: 80%; font-weight: bold; color: #59838e; background: #c7e3ef; box-shadow: inset 0 1px 1px 0 #b0ccd7; -o-box-shadow: inset 0 1px 1px 0 #b0ccd7; -webkit-box-shadow: inset 0 1px 1px 0 #b0ccd7; -moz-box-shadow: inset 0 1px 1px 0 #b0ccd7; border-color: #b0ccd7; border-radius: 8px; } .tag-draghelper .tag .count, .tagcloud li.inactive .count { display: none; } #tasklistsbox .treelist li { margin: 0; display: block; position: relative; } #tasklistsbox .treelist li div.tasklist { margin: 0; height: 20px; padding: 6px 8px 2px 6px; position: relative; white-space: nowrap; } #tasklistsbox .treelist li.virtual > div.tasklist { height: 14px; } #tasklistsbox .treelist ul li > div.tasklist { margin-left: 16px; } #tasklistsbox .treelist ul ul li > div.tasklist { margin-left: 32px; } #tasklistsbox .treelist ul ul ul li > div.tasklist { margin-left: 48px; } #tasklistsbox .treelist li label { display: block; } #tasklistsbox .treelist li span.listname { display: block; position: absolute; top: 7px; left: 38px; right: 40px; cursor: default; padding: 0px 30px 2px 2px; color: #004458; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; background: url(sprites.png) right 20px no-repeat; } +.quickview-active #tasklistsbox .treelist li input, +.quickview-active #tasklistsbox .treelist li span.listname { + opacity: 0.35; +} + +.quickview-active #tasklistsbox .treelist div.focusview span.listname { + opacity: 1.0; +} + #tasklistsbox .treelist div span.actions { display: inline-block; position: absolute; top: 2px; right: 2px; padding: 5px 20px 0 6px; min-width: 40px; height: 19px; text-align: right; } #tasklistsbox .treelist div:hover span.actions { top: 1px; right: 1px; border: 1px solid #c6c6c6; border-radius: 4px; background: #f7f7f7; background: -moz-linear-gradient(top, #f9f9f9 0%, #e6e6e6 100%); background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f9f9f9), color-stop(100%,#e6e6e6)); background: -o-linear-gradient(top, #f9f9f9 0%, #e6e6e6 100%); background: -ms-linear-gradient(top, #f9f9f9 0%, #e6e6e6 100%); background: linear-gradient(top, #f9f9f9 0%, #e6e6e6 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#f9f9f9', endColorstr='#e6e6e6', GradientType=0); } #tasklistsbox .treelist div a.remove, #tasklistsbox .treelist div a.quickview, #tasklistsbox .treelist div a.subscribed { display: inline-block; width: 16px; height: 16px; padding: 0; margin-right: 4px; background: url(sprites.png) -200px 0 no-repeat; overflow: hidden; text-indent: -5000px; cursor: pointer; } #tasklistsbox .treelist div a.subscribed { position: absolute; top: 5px; right: 4px; margin: 0; } #tasklistsbox .treelist div a.subscribed:focus, #tasklistsbox .treelist div:hover a.subscribed { background-position: -2px -215px; } #tasklistsbox .treelist div.subscribed a.subscribed { background-position: -20px -215px; } #tasklistsbox .treelist div a.quickview:focus, #tasklistsbox .treelist div:hover a.quickview { background-position: -20px -101px; background-color: transparent !important; } #tasklistsbox .treelist div a.remove:focus, #tasklistsbox .treelist div:hover a.remove { background-position: -2px -371px; background-color: transparent !important; } #tasklistsbox .treelist div.focusview a.quickview { background-position: -2px -101px; } #tasklistsbox .searchresults .treelist div a.remove, #tasklistsbox .searchresults .treelist div a.quickview { display: none; } #tasklistsbox .treelist div a.remove:focus, #tasklistsbox .treelist div a.quickview:focus, #tasklistsbox .treelist div a.subscribed:focus { border-radius: 3px; outline: 2px solid rgba(30,150,192, 0.5); } #tasklistsbox .treelist li.selected > div > span.listname { font-weight: bold; } #tasklistsbox .treelist .readonly > span.listname { background-position: right -142px; } #tasklistsbox .treelist .user > span.listname { background-position: right -160px; } #tasklistsbox .treelist .virtual > span.listname { color: #aaa; top: 4px; left: 20px; right: 5px; } #tasklistsbox .treelist.flat li span.calname { left: 24px; right: 22px; } #tasklistsbox .treelist li input { position: absolute; top: 5px; left: 18px; } #tasklistsbox .treelist li .treetoggle { top: 8px; } #tasklistsbox .treelist li.virtual > .treetoggle { top: 6px; } #tasklistsbox .searchresults { background: #b0ccd7; margin-top: 8px; } #tasklistsbox .searchresults .boxtitle { background: none; padding: 2px 8px 2px 8px; } #tasklistsbox .searchresults .listing li { background-color: #c7e3ef; } #mainview-right { position: absolute; top: 0; left: 256px; right: 0; bottom: 0; } #taskstoolbar { position: absolute; top: -6px; left: 0; width: 100%; height: 40px; white-space: nowrap; } #taskstoolbar a.button.newtask { background-image: url(buttons.png); background-position: center -53px; } #quickaddbox { position: absolute; top: 2px; left: 0; width: 60%; height: 32px; white-space: nowrap; } #quickaddinput { width: 85%; margin: 0; padding: 3px 8px; height: 18px; background: #f1f1f1; background: rgba(255, 255, 255, 0.7); border-color: #a3a3a3; font-weight: bold; } #quickaddbox .button { margin-left: 5px; padding: 3px 10px; font-weight: bold; } #tasksview { position: absolute; top: 42px; left: 0; right: 0; bottom: 0; - padding-bottom: 28px; background: rgba(255, 255, 255, 0.2); overflow: visible; } +.quickview-active #tasksview { + background-image: url('images/focusview.png'); + background-position: center; + background-repeat: no-repeat; +} + #message.statusbar { border-top: 1px solid #c3c3c3; } #tasksview .scroller { position: absolute; left: 0; top: 35px; width: 100%; bottom: 0; overflow: auto; } #tasksview .buttonbar { color: #777; background: #eee; background: -moz-linear-gradient(top, #eee 0%, #dfdfdf 100%); background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#eee), color-stop(100%,#dfdfdf)); background: -o-linear-gradient(top, #eee 0%, #dfdfdf 100%); background: -ms-linear-gradient(top, #eee 0%, #dfdfdf 100%); background: linear-gradient(top, #eee 0%, #dfdfdf 100%); border-bottom: 1px solid #ccc; position: relative; line-height: 13px; height: 20px; } #tasksview .buttonbar .buttonbar-right { position: absolute; top: 6px; right: 8px; } .buttonbar-right .listmenu { display: inline-block; cursor: pointer; } .buttonbar-right a.iconbutton { padding: 0; background-image: url(sprites.png); background-position: 0 -238px; } .buttonbar-right a.iconbutton.sorting { background-position: -18px -347px; } #thelist { padding: 0; margin: 1em; list-style: none; } #listmessagebox { display: none; font-size: 14px; color: #666; margin: 1.5em; text-shadow: 0px 1px 1px #fff; text-align:center; } .taskitem { position: relative; display: block; margin-bottom: 3px; } .taskitem.dragging { opacity: 0.5; } .taskitem .childtasks { position: relative; padding: 0; margin: 3px 0 0 20px; list-style: none; } .taskitem .childtoggle { display: none; position: absolute; top: 4px; left: -5px; padding: 2px; font-size: 10px; color: #727272; cursor: pointer; width: 14px; height: 14px; background: url(sprites.png) -2px -80px no-repeat; text-indent: -1000px; overflow: hidden; } .taskitem .childtoggle.collapsed { background-position: -18px -81px; } .taskhead { position: relative; margin-left: 14px; padding: 4px 5px 3px 5px; border: 1px solid #fff; border-radius: 5px; background: #fff; -webkit-box-shadow: 0 1px 1px 0 rgba(50, 50, 50, 0.5); -moz-box-shadow: 0 1px 1px 0 rgba(50, 50, 50, 0.5); box-shadow: 0 1px 1px 0 rgba(50, 50, 50, 0.5); padding-right: 26em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; cursor: default; outline: none; } .taskhead:focus, .taskhead.droptarget { border-color: #4787b1; box-shadow: 0 0 5px 2px rgba(71,135,177, 0.9); -moz-box-shadow: 0 0 5px 2px rgba(71,135,177, 0.9); -webkit-box-shadow: 0 0 5px 2px rgba(71,135,177, 0.9); -o-box-shadow: 0 0 5px 2px rgba(71,135,177, 0.9); } .taskhead .complete { margin: -1px 1em 0 0; } .taskhead .title { font-size: 12px; } .taskhead .flagged { display: inline-block; width: 16px; height: 16px; background: url(sprites.png) 1000px -3px no-repeat; margin: -3px 1em 0 0; vertical-align: middle; cursor: pointer; } .taskhead .flagged:focus, .taskhead:hover .flagged { background-position: -2px -3px; } .taskhead.flagged .flagged { background-position: -2px -23px; } .taskhead .tags { display: block; position: absolute; top: 3px; right: 10em; max-width: 14em; height: 16px; overflow: hidden; padding-top: 1px; text-align: right; } .tag-draghelper .tag, .taskhead .tags .tag { font-size: 85%; background: #d9ecf4; border: 1px solid #c2dae5; border-radius: 4px; padding: 1px 7px; margin-right: 3px; } .tag-draghelper li.tag { list-style: none; font-size: 100%; } .taskhead .date { position: absolute; top: 4px; right: 30px; text-align: right; cursor: pointer; } .taskhead.nodate .date { color: #ddd; } .taskhead.overdue .date { color: #d00; } .taskhead.nodate:hover .date { color: #999; } .taskhead .date input { padding: 1px 2px; border: 1px solid #ddd; -webkit-box-shadow: none; -moz-box-shadow: none; box-shadow: none; outline: none; text-align: right; width: 6em; font-size: 11px; } .taskhead .actions { display: block; position: absolute; top: 3px; right: 6px; width: 18px; height: 18px; background: url(sprites.png) 1000px -80px no-repeat; text-indent: -5000px; overflow: hidden; cursor: pointer; } .taskhead .actions:focus, .taskhead:hover .actions { background-position: 0 -80px; } .taskhead.complete { opacity: 0.6; } .taskhead.complete .title { text-decoration: line-through; } .taskhead .progressbar { position: absolute; bottom: 1px; left: 6px; right: 6px; height: 2px; } .taskhead.complete .progressbar { display: none; } .taskhead .progressvalue { height: 1px; background: rgba(1, 124, 180, 0.2); border-top: 1px solid #219de6; } ul.toolbarmenu li span.add, ul.toolbarmenu li span.expand, ul.toolbarmenu li span.collapse, ul.toolbarmenu.iconized .selected span.icon { background-image: url(sprites.png); } ul.toolbarmenu li span.add { background-position: 0 -302px; } ul.toolbarmenu li span.expand { background-position: 0 -258px; } ul.toolbarmenu li span.collapse { background-position: 0 -280px; } ul.toolbarmenu li span.delete { background-position: 0 -1508px; } ul.toolbarmenu.iconized .selected span.icon { background-position: 0 -324px; } ul.toolbarmenu .sortcol.by-auto a { font-style: italic; } .taskitem-draghelper { /* width: 32px; height: 26px; */ background: #444; border: 1px solid #555; border-radius: 4px; box-shadow: 0 2px 6px 0 #333; -moz-box-shadow: 0 2px 6px 0 #333; -webkit-box-shadow: 0 2px 6px 0 #333; -o-box-shadow: 0 2px 6px 0 #333; z-index: 5000; padding: 2px 10px; font-size: 20px; color: #ccc; opacity: 0.92; filter: alpha(opacity=90); text-shadow: 0px 1px 1px #333; } #rootdroppable { display: none; position: absolute; top: 2px; left: 1em; right: 1em; height: 5px; background: #ddd; border-radius: 3px; } #rootdroppable.droptarget { background: #4787b1; box-shadow: 0 0 2px 1px rgba(71,135,177, 0.9); -moz-box-shadow: 0 0 2px 1px rgba(71,135,177, 0.9); -webkit-box-shadow: 0 0 2px 1px rgba(71,135,177, 0.9); -o-box-shadow: 0 0 2px 1px rgba(71,135,177, 0.9); } /*** task edit form ***/ #taskedit, #taskshow { display:none; } #taskedit { position: relative; top: -1.5em; padding: 0.5em 0.1em; margin: 0 -0.2em; } #taskshow h2 { margin-top: -0.5em; } #taskshow label { color: #999; } #taskshow.status-cancelled { background: url(images/badge_cancelled.png) top right no-repeat; } #task-parent-title { position: relative; top: -0.6em; } a.morelink { font-size: 90%; color: #0069a6; text-decoration: none; outline: none; } a.morelink:hover { text-decoration: underline; } #taskedit .ui-tabs-panel { min-height: 24em; } #taskeditform input.text, #taskeditform textarea { width: 97%; } #taskeditform .formbuttons { margin: 0.5em 0; } #taskedit .border-after { padding-bottom: 0.8em; margin-bottom: 0.8em; border-bottom: 2px solid #fafafa; } #taskedit .edit-attendees-table { width: 100%; margin-top: 0.5em; } #taskedit .edit-attendees-table tbody td { padding: 4px 7px; } #taskedit .edit-attendees-table tbody tr:last-child td { border-bottom: 0; } #taskedit .edit-attendees-table th.role, #taskedit .edit-attendees-table td.role { width: 9em; } #taskedit .edit-attendees-table th.availability, #taskedit .edit-attendees-table td.availability, #taskedit .edit-attendees-table th.confirmstate, #taskedit .edit-attendees-table td.confirmstate { width: 6em; } #taskedit .edit-attendees-table th.options, #taskedit .edit-attendees-table td.options { width: 24px; padding: 2px 4px; text-align: right; } #taskedit .edit-attendees-table th.invite, #taskedit .edit-attendees-table td.invite { width: 48px; padding: 2px; } #taskedit .edit-attendees-table th.invite label { display: inline-block; position: relative; top: 4px; width: 24px; height: 18px; min-width: 24px; padding: 0; overflow: hidden; text-indent: -5000px; white-space: nowrap; background: url(images/sendinvitation.png) 1px 0 no-repeat; } #taskedit .edit-attendees-table th.name, #taskedit .edit-attendees-table td.name { width: auto; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; position: relative; } #taskedit .edit-attendees-table td.name select { width: 100%; } #taskedit .edit-attendees-table a.deletelink { display: inline-block; width: 17px; height: 17px; padding: 0; overflow: hidden; text-indent: 1000px; } #taskedit .edit-attendees-table a.expandlink { position: absolute; top: 4px; right: 6px; width: 16px; height: 16px; } #edit-attendees-form { position: relative; margin-top: 15px; } #edit-attendees-form .attendees-invitebox { text-align: right; margin: 0; } #edit-attendees-form .attendees-invitebox label { padding-right: 3px; } #taskedit-attachments { margin: 0.6em 0; } #taskedit-attachments ul li { display: block; color: #333; font-weight: bold; padding: 3px 4px 3px 30px; text-shadow: 0px 1px 1px #fff; text-decoration: none; white-space: nowrap; line-height: 20px; } #taskedit-attachments ul li a.file { padding: 0; } #taskedit-attachments-form { margin-top: 1em; padding-top: 0.8em; border-top: 2px solid #fafafa; } div.form-section { position: relative; margin-top: 0.2em; margin-bottom: 0.5em; } .form-section label { display: inline-block; min-width: 7em; padding-right: 0.5em; margin-bottom: 0.3em; } .tasklistview div.form-section span.task-text + label { margin-left: 2em; } label.block { display: block; margin-bottom: 0.3em; } #task-description { margin-bottom: 1em; } #taskedit-completeness-slider { display: inline-block; margin-left: 2em; width: 30em; height: 0.8em; border: 1px solid #ccc; } #taskedit-tagline { width: 97%; } #taskedit .droptarget { background-image: url(../../../../skins/larry/images/filedrop.png) !important; background-position: center bottom !important; background-repeat: no-repeat !important; } #taskedit .droptarget.hover, #taskedit .droptarget.active { border-color: #019bc6; box-shadow: 0 0 3px 2px rgba(71,135,177, 0.5); -moz-box-shadow: 0 0 3px 2px rgba(71,135,177, 0.5); -webkit-box-shadow: 0 0 3px 2px rgba(71,135,177, 0.5); -o-box-shadow: 0 0 3px 2px rgba(71,135,177, 0.5); } #taskedit .droptarget.hover { background-color: #d9ecf4; box-shadow: 0 0 5px 2px rgba(71,135,177, 0.9); -moz-box-shadow: 0 0 5px 2px rgba(71,135,177, 0.9); -webkit-box-shadow: 0 0 5px 2px rgba(71,135,177, 0.9); -o-box-shadow: 0 0 5px 2px rgba(71,135,177, 0.9); } #task-attachments .attachmentslist li { float: left; margin-right: 1em; } #task-attachments .attachmentslist li a { outline: none; } .task-attendees span.attendee { padding-right: 18px; margin-right: 0.5em; background: url(images/attendee-status.png) right 0 no-repeat; } .task-attendees span.attendee a.mailtolink { text-decoration: none; white-space: nowrap; outline: none; } .task-attendees span.attendee a.mailtolink:hover { text-decoration: underline; } .task-attendees span.completed { background-position: right -20px; } .task-attendees span.declined { background-position: right -40px; } .task-attendees span.tentative { background-position: right -60px; } .task-attendees span.delegated { background-position: right -180px; } .task-attendees span.in-process { background-position: right -200px; } .task-attendees span.accepted { background-position: right -220px; } .task-attendees span.organizer { background-position: right 100px; } #all-task-attendees span.attendee { display: block; margin-bottom: 0.4em; padding-bottom: 0.3em; border-bottom: 1px solid #ddd; } .tasklistview .uidialog .tabbed { min-width: 600px; } .tasklistview .uidialog .propform fieldset.ui-tabs-panel { min-height: 290px; } .tasklistview .uidialog .propform #taskedit-tasklistame { width: 20em; } .task-dialog-message { margin-top: 0.5em; padding: 0.8em; border: 1px solid #ffdf0e; background-color: #fef893; } .task-dialog-message .message, .task-update-confirm .message { margin-bottom: 0.5em; } /* Invitation UI in mail */ .messagelist tbody .attachment span.ical { display: inline-block; vertical-align: middle; height: 18px; width: 20px; padding: 0; background: url(images/ical-attachment.png) 2px 1px no-repeat; } div.tasklist-invitebox { min-height: 20px; margin: 5px 8px; padding: 3px 6px 6px 34px; border: 1px solid #ffdf0e; background: url(images/tasklist.png) 6px 5px no-repeat #fef893; } div.tasklist-invitebox td.ititle { font-weight: bold; padding-right: 0.5em; } div.tasklist-invitebox td.label { color: #666; padding-right: 1em; } #task-rsvp .rsvp-buttons, #task-rsvp .itip-reply-controls, div.tasklist-invitebox .itip-buttons div { margin-top: 0.5em; } #task-rsvp .itip-reply-controls a, #task-rsvp .itip-reply-controls label { color: #333; } #task-rsvp input.button, div.tasklist-invitebox input.button { font-weight: bold; margin-right: 0.5em; } div.tasklist-invitebox .folder-select { font-weight: 10px; margin-left: 1em; } div.tasklist-invitebox .rsvp-status { padding-left: 2px; } div.tasklist-invitebox .rsvp-status.loading { color: #666; padding: 1px 0 2px 24px; background: url(images/loading_blue.gif) top left no-repeat; } div.tasklist-invitebox .rsvp-status.hint { color: #666; text-shadow: none; font-style: italic; } #task-partstat .changersvp, .tasklistview .edit-attendees-table td.confirmstate span, div.tasklist-invitebox .rsvp-status.declined, div.tasklist-invitebox .rsvp-status.tentative, div.tasklist-invitebox .rsvp-status.accepted, div.tasklist-invitebox .rsvp-status.delegated, div.tasklist-invitebox .rsvp-status.in-process, div.tasklist-invitebox .rsvp-status.completed, div.tasklist-invitebox .rsvp-status.needs-action { padding: 0 0 1px 22px; background: url(images/attendee-status.png) 2px -20px no-repeat; } #task-partstat .changersvp.declined, div.tasklist-invitebox .rsvp-status.declined, .tasklistview .edit-attendees-table td.confirmstate span.declined { background-position: 2px -40px; } #task-partstat .changersvp.tentative, div.tasklist-invitebox .rsvp-status.tentative, .tasklistview .edit-attendees-table td.confirmstate span.tentative { background-position: 2px -60px; } #task-partstat .changersvp.delegated, div.tasklist-invitebox .rsvp-status.delegated, .tasklistview .edit-attendees-table td.confirmstate span.delegated { background-position: 2px -180px; } #task-partstat .changersvp.needs-action, div.tasklist-invitebox .rsvp-status.needs-action, .tasklistview .edit-attendees-table td.confirmstate span.needs-action { background-position: 2px 0; } #task-partstat .changersvp.in-process, div.tasklist-invitebox .rsvp-status.in-process, .tasklistview .edit-attendees-table td.confirmstate span.in-process { background-position: 2px -200px; } #task-partstat .changersvp.accepted, div.tasklist-invitebox .rsvp-status.accepted, .tasklistview .edit-attendees-table td.confirmstate span.accepted { background-position: 2px -220px; } /** Special hacks for IE7 **/ /** They need to be in this file to also affect the task-create dialog embedded in mail view **/ html.ie7 #taskedit-completeness-slider { display: inline; } diff --git a/plugins/tasklist/tasklist.js b/plugins/tasklist/tasklist.js index 218b8db0..65f603e7 100644 --- a/plugins/tasklist/tasklist.js +++ b/plugins/tasklist/tasklist.js @@ -1,2908 +1,2912 @@ /** * Client scripts for the Tasklist plugin * * @author Thomas Bruederli * * @licstart The following is the entire license notice for the * JavaScript code in this file. * * Copyright (C) 2012, Kolab Systems AG * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program 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 program. If not, see . * * @licend The above is the entire license notice * for the JavaScript code in this file. */ function rcube_tasklist_ui(settings) { // extend base class rcube_libcalendaring.call(this, settings); /* constants */ var FILTER_MASK_ALL = 0; var FILTER_MASK_TODAY = 1; var FILTER_MASK_TOMORROW = 2; var FILTER_MASK_WEEK = 4; var FILTER_MASK_LATER = 8; var FILTER_MASK_NODATE = 16; var FILTER_MASK_OVERDUE = 32; var FILTER_MASK_FLAGGED = 64; var FILTER_MASK_COMPLETE = 128; var FILTER_MASK_ASSIGNED = 256; var FILTER_MASK_MYTASKS = 512; var filter_masks = { all: FILTER_MASK_ALL, today: FILTER_MASK_TODAY, tomorrow: FILTER_MASK_TOMORROW, week: FILTER_MASK_WEEK, later: FILTER_MASK_LATER, nodate: FILTER_MASK_NODATE, overdue: FILTER_MASK_OVERDUE, flagged: FILTER_MASK_FLAGGED, complete: FILTER_MASK_COMPLETE, assigned: FILTER_MASK_ASSIGNED, mytasks: FILTER_MASK_MYTASKS }; /* private vars */ var tagsfilter = []; var filtermask = FILTER_MASK_ALL; var loadstate = { filter:-1, lists:'', search:null }; var idcount = 0; var focusview; var saving_lock; var ui_loading; var taskcounts = {}; var listindex = []; var listdata = {}; var tags = []; var draghelper; var search_request; var search_query; var completeness_slider; var task_draghelper; var tag_draghelper; var task_drag_active = false; var list_scroll_top = 0; var scroll_delay = 400; var scroll_step = 5; var scroll_speed = 20; var scroll_sensitivity = 40; var scroll_timer; var tasklists_widget; var focused_task; var focused_subclass; var task_attendees = []; var attendees_list; var me = this; // general datepicker settings var datepicker_settings = { // translate from PHP format to datepicker format dateFormat: settings['date_format'].replace(/M/g, 'm').replace(/mmmmm/, 'MM').replace(/mmm/, 'M').replace(/dddd/, 'DD').replace(/ddd/, 'D').replace(/yy/g, 'y'), firstDay : settings['first_day'], // dayNamesMin: settings['days_short'], // monthNames: settings['months'], // monthNamesShort: settings['months'], changeMonth: false, showOtherMonths: true, selectOtherMonths: true }; var extended_datepicker_settings; /* public members */ this.tasklists = rcmail.env.tasklists; this.selected_task; this.selected_list; /* public methods */ this.init = init; this.edit_task = task_edit_dialog; this.delete_task = delete_task; this.add_childtask = add_childtask; this.quicksearch = quicksearch; this.reset_search = reset_search; this.expand_collapse = expand_collapse; this.list_delete = list_delete; this.list_remove = list_remove; this.list_edit_dialog = list_edit_dialog; this.unlock_saving = unlock_saving; /* imports */ var Q = this.quote_html; var text2html = this.text2html; var event_date_text = this.event_date_text; var parse_datetime = this.parse_datetime; var date2unixtime = this.date2unixtime; var fromunixtime = this.fromunixtime; /** * initialize the tasks UI */ function init() { // initialize task list selectors for (var id in me.tasklists) { if (me.tasklists[id].editable && (!me.selected_list || me.tasklists[id].default || (me.tasklists[id].active && !me.tasklists[me.selected_list].active))) { me.selected_list = id; } } // initialize treelist widget that controls the tasklists list var widget_class = window.kolab_folderlist || rcube_treelist_widget; tasklists_widget = new widget_class(rcmail.gui_objects.tasklistslist, { id_prefix: 'rcmlitasklist', selectable: true, save_state: true, keyboard: false, searchbox: '#tasklistsearch', search_action: 'tasks/tasklist', search_sources: [ 'folders', 'users' ], search_title: rcmail.gettext('listsearchresults','tasklist') }); tasklists_widget.addEventListener('select', function(node) { var id = $(this).data('id'); rcmail.enable_command('list-edit', 'list-delete', 'list-import', me.tasklists[node.id].editable); rcmail.enable_command('list-remove', me.tasklists[node.id] && me.tasklists[node.id].removable); me.selected_list = node.id; }); tasklists_widget.addEventListener('subscribe', function(p) { var list; if ((list = me.tasklists[p.id])) { list.subscribed = p.subscribed || false; rcmail.http_post('tasklist', { action:'subscribe', l:{ id:p.id, active:list.active?1:0, permanent:list.subscribed?1:0 } }); } }); tasklists_widget.addEventListener('remove', function(p) { if (me.tasklists[p.id] && me.tasklists[p.id].removable) { list_remove(p.id); } }); tasklists_widget.addEventListener('insert-item', function(p) { var list = p.data; if (list && list.id && !list.virtual) { me.tasklists[list.id] = list; var prop = { id:p.id, active:list.active?1:0 }; if (list.subscribed) prop.permanent = 1; rcmail.http_post('tasklist', { action:'subscribe', l:prop }); list_tasks(); $(p.item).data('type', 'tasklist'); } }); tasklists_widget.addEventListener('search-complete', function(data) { if (data.length) rcmail.display_message(rcmail.gettext('nrtasklistsfound','tasklist').replace('$nr', data.length), 'voice'); else rcmail.display_message(rcmail.gettext('notasklistsfound','tasklist'), 'info'); }); // init (delegate) event handler on tasklist checkboxes tasklists_widget.container.on('click', 'input[type=checkbox]', function(e) { var list, id = this.value; if ((list = me.tasklists[id])) { list.active = this.checked; fetch_counts(); if (!this.checked) remove_tasks(id); else list_tasks(null); rcmail.http_post('tasklist', { action:'subscribe', l:{ id:id, active:list.active?1:0 } }); // disable focusview if (!this.checked && focusview == id) { set_focusview(null); } // adjust checked state of original list item if (tasklists_widget.is_search()) { tasklists_widget.container.find('input[value="'+id+'"]').prop('checked', this.checked); } } e.stopPropagation(); }) .on('keypress', 'input[type=checkbox]', function(e) { // select tasklist on if (e.keyCode == 13) { tasklists_widget.select(this.value); return rcube_event.cancel(e); } }) .find('li:not(.virtual)').data('type', 'tasklist'); // handler for clicks on quickview buttons tasklists_widget.container.on('click', '.quickview', function(e){ var id = $(this).closest('li').attr('id').replace(/^rcmlitasklist/, ''); if (tasklists_widget.is_search()) id = id.replace(/--xsR$/, ''); set_focusview(focusview == id ? null : id); e.stopPropagation(); return false; }); // register dbl-click handler to open calendar edit dialog tasklists_widget.container.on('dblclick', ':not(.virtual) > .tasklist', function(e){ var id = $(this).closest('li').attr('id').replace(/^rcmlitasklist/, ''); if (tasklists_widget.is_search()) id = id.replace(/--xsR$/, ''); list_edit_dialog(id); }); if (me.selected_list) { rcmail.enable_command('addtask', true); tasklists_widget.select(me.selected_list); } // register server callbacks rcmail.addEventListener('plugin.data_ready', data_ready); rcmail.addEventListener('plugin.update_task', update_taskitem); rcmail.addEventListener('plugin.refresh_tasks', function(p) { update_taskitem(p, true); }); rcmail.addEventListener('plugin.update_counts', update_counts); rcmail.addEventListener('plugin.insert_tasklist', insert_list); rcmail.addEventListener('plugin.update_tasklist', update_list); rcmail.addEventListener('plugin.destroy_tasklist', destroy_list); rcmail.addEventListener('plugin.unlock_saving', unlock_saving); rcmail.addEventListener('requestrefresh', before_refresh); rcmail.addEventListener('plugin.reload_data', function(){ list_tasks(null, true); setTimeout(fetch_counts, 200); }); rcmail.register_command('list-sort', list_set_sort, true); rcmail.register_command('list-order', list_set_order, (settings.sort_col || 'auto') != 'auto'); $('#taskviewsortmenu .by-' + (settings.sort_col || 'auto')).attr('aria-checked', 'true').addClass('selected'); $('#taskviewsortmenu .sortorder.' + (settings.sort_order || 'asc')).attr('aria-checked', 'true').addClass('selected'); // start loading tasks fetch_counts(); list_tasks(); // register event handlers for UI elements $('#taskselector a').click(function(e) { if (!$(this).parent().hasClass('inactive')) { var selector = this.href.replace(/^.*#/, ''), mask = filter_masks[selector], shift = e.shiftKey || e.ctrlKey || e.metaKey; if (!shift) filtermask = mask; // reset selection on regular clicks else if (filtermask & mask) filtermask -= mask; else filtermask |= mask; list_tasks(); } return false; }); // quick-add a task $(rcmail.gui_objects.quickaddform).submit(function(e){ var tasktext = this.elements.text.value, rec = { id:-(++idcount), title:tasktext, readonly:true, mask:0, complete:0 }; if (tasktext && tasktext.length) { save_task({ tempid:rec.id, raw:tasktext, list:me.selected_list }, 'new'); render_task(rec); $('#listmessagebox').hide(); } // clear form this.reset(); return false; }).find('input[type=text]').placeholder(rcmail.gettext('createnewtask','tasklist')); // click-handler on tags list $(rcmail.gui_objects.tagslist).on('click', 'li', function(e){ var item = e.target.nodeName == 'LI' ? $(e.target) : $(e.target).closest('li'), tag = item.data('value'); if (!tag) return false; // reset selection on regular clicks var index = $.inArray(tag, tagsfilter); var shift = e.shiftKey || e.ctrlKey || e.metaKey; if (!shift) { if (tagsfilter.length > 1) index = -1; $('li', rcmail.gui_objects.tagslist).removeClass('selected').attr('aria-checked', 'false'); tagsfilter = []; } // add tag to filter if (index < 0) { item.addClass('selected').attr('aria-checked', 'true'); tagsfilter.push(tag); } else if (shift) { item.removeClass('selected').attr('aria-checked', 'false'); var a = tagsfilter.slice(0,index); tagsfilter = a.concat(tagsfilter.slice(index+1)); } list_tasks(); // clear text selection in IE after shift+click if (shift && document.selection) document.selection.empty(); e.preventDefault(); return false; }) .on('keypress', 'li', function(e) { if (e.keyCode == 13) { $(this).trigger('click', { pointerType:'keyboard' }); } }) .mousedown(function(e){ // disable content selection with the mouse e.preventDefault(); return false; }); // click-handler on task list items (delegate) $(rcmail.gui_objects.resultlist).on('click', function(e){ var item = $(e.target); var className = e.target.className; if (item.hasClass('childtoggle')) { item = item.parent().find('.taskhead'); className = 'childtoggle'; } else if (!item.hasClass('taskhead')) item = item.closest('div.taskhead'); // ignore if (!item.length) return false; var id = item.data('id'), li = item.parent(), rec = listdata[id]; switch (className) { case 'childtoggle': rec.collapsed = !rec.collapsed; li.children('.childtasks:first').toggle().attr('aria-hidden', rec.collapsed ? 'true' : 'false'); $(e.target).toggleClass('collapsed').html(rec.collapsed ? '▶' : '▼'); rcmail.http_post('tasks/task', { action:'collapse', t:{ id:rec.id, list:rec.list }, collapsed:rec.collapsed?1:0 }); if (e.shiftKey) // expand/collapse all childs li.children('.childtasks:first .childtoggle.'+(rec.collapsed?'expanded':'collapsed')).click(); break; case 'complete': if (rcmail.busy) return false; save_task_confirm(rec, 'edit', { _status_before:rec.status + '', status:e.target.checked ? 'COMPLETED' : (rec.complete > 0 ? 'IN-PROCESS' : 'NEEDS-ACTION') }); item.toggleClass('complete'); return true; case 'flagged': if (rcmail.busy) return false; rec.flagged = rec.flagged ? 0 : 1; item.toggleClass('flagged').find('.flagged:first').attr('aria-checked', (rec.flagged ? 'true' : 'false')); save_task(rec, 'edit'); break; case 'date': if (rcmail.busy) return false; var link = $(e.target).html(''), input = $('').appendTo(link).val(rec.date || '') input.datepicker($.extend({ onClose: function(dateText, inst) { if (dateText != (rec.date || '')) { save_task_confirm(rec, 'edit', { date:dateText }); } input.datepicker('destroy').remove(); link.html(dateText || rcmail.gettext('nodate','tasklist')); } }, extended_datepicker_settings) ) .datepicker('setDate', rec.date) .datepicker('show'); break; case 'delete': delete_task(id); break; case 'actions': var pos, ref = $(e.target), menu = $('#taskitemmenu'); if (menu.is(':visible') && menu.data('refid') == id) { rcmail.command('menu-close', 'taskitemmenu'); } else { rcmail.command('menu-open', { menu: 'taskitemmenu', show: true }, e.target, e); menu.data('refid', id); me.selected_task = rec; } e.bubble = false; break; case 'extlink': return true; default: if (e.target.nodeName != 'INPUT') task_show_dialog(id); break; } return false; }) .on('dblclick', '.taskhead, .childtoggle', function(e){ var id, rec, item = $(e.target); if (!item.hasClass('taskhead')) item = item.closest('div.taskhead'); if (!rcmail.busy && item.length && (id = item.data('id')) && (rec = listdata[id])) { var list = rec.list && me.tasklists[rec.list] ? me.tasklists[rec.list] : {}; if (rec.readonly || !list.editable) task_show_dialog(id); else task_edit_dialog(id, 'edit'); clearSelection(); } }) .on('keydown', '.taskhead', function(e) { if (e.target.nodeName == 'INPUT' && e.target.type == 'text') return true; var inc = 1; switch (e.keyCode) { case 13: // Enter $(e.target).trigger('click', { pointerType:'keyboard' }); return rcube_event.cancel(e); case 38: // Up arrow key inc = -1; case 40: // Down arrow key if ($(e.target).hasClass('actions')) { // unfold actions menu $(e.target).trigger('click', { pointerType:'keyboard' }); return rcube_event.cancel(e); } // focus next/prev task item var x = 0, target = this, items = $(rcmail.gui_objects.resultlist).find('.taskhead:visible'); items.each(function(i, item) { if (item === target) { x = i; return false; } }); items.get(x + inc).focus(); return rcube_event.cancel(e); case 37: // Left arrow key case 39: // Right arrow key $(this).parent().children('.childtoggle:visible').first().trigger('click', { pointerType:'keyboard' }); break; } }) .on('focusin', '.taskhead', function(e){ if (rcube_event.is_keyboard(e)) { var item = $(e.target); if (!item.hasClass('taskhead')) item = item.closest('div.taskhead'); var id = item.data('id'); if (id && listdata[id]) { focused_task = id; focused_subclass = item.get(0) !== e.target ? e.target.className : null; } } }) .on('focusout', '.taskhead', function(e){ var item = $(e.target); if (focused_task && item.data('id') == focused_task) { focused_task = focused_subclass = null; } }); // init RSVP widget $('#task-rsvp input.button').click(function(e) { var response = $(this).attr('rel'); if (me.selected_task && me.selected_task.attendees && response) { // update attendee status for (var data, i=0; i < me.selected_task.attendees.length; i++) { data = me.selected_task.attendees[i]; if (settings.identity.emails.indexOf(';'+String(data.email).toLowerCase()) >= 0) { data.status = response.toUpperCase(); delete data.rsvp; // unset RSVP flag } } // submit status change to server saving_lock = rcmail.set_busy(true, 'tasklist.savingdata'); rcmail.http_post('tasks/task', { action: 'rsvp', t: me.selected_task, filter: filtermask, status: response, noreply: $('#noreply-task-rsvp:checked').length ? 1 : 0, comment: $('#reply-comment-task-rsvp').val() }); task_show_dialog(me.selected_task.id); } }); // handle global document clicks: close popup menus $(document.body).click(clear_popups); // extended datepicker settings var extended_datepicker_settings = $.extend({ showButtonPanel: true, beforeShow: function(input, inst) { setTimeout(function(){ $(input).datepicker('widget').find('button.ui-datepicker-close') .html(rcmail.gettext('nodate','tasklist')) .attr('onclick', '') .unbind('click') .bind('click', function(e){ $(input).datepicker('setDate', null).datepicker('hide'); }); }, 1); } }, datepicker_settings); } /** * initialize task edit form elements */ function init_taskedit() { $('#taskedit').tabs(); var completeness_slider_change = function(e, ui){ var v = completeness_slider.slider('value'); if (v >= 98) v = 100; if (v <= 2) v = 0; $('#taskedit-completeness').val(v); }; completeness_slider = $('#taskedit-completeness-slider').slider({ range: 'min', animate: 'fast', slide: completeness_slider_change, change: completeness_slider_change }); $('#taskedit-completeness').change(function(e){ completeness_slider.slider('value', parseInt(this.value)) }); // register events on alarms and recurrence fields me.init_alarms_edit('#taskedit-alarms'); me.init_recurrence_edit('#eventedit'); $('#taskedit-date, #taskedit-startdate').datepicker(datepicker_settings); $('a.edit-nodate').click(function(){ var sel = $(this).attr('rel'); if (sel) $(sel).val(''); return false; }); // init attendees autocompletion var ac_props; // parallel autocompletion if (rcmail.env.autocomplete_threads > 0) { ac_props = { threads: rcmail.env.autocomplete_threads, sources: rcmail.env.autocomplete_sources }; } rcmail.init_address_input_events($('#edit-attendee-name'), ac_props); rcmail.addEventListener('autocomplete_insert', function(e) { var success = false; if (e.field.name == 'participant') { success = add_attendees(e.insert, { role:'REQ-PARTICIPANT', status:'NEEDS-ACTION', cutype:(e.data && e.data.type == 'group' ? 'GROUP' : 'INDIVIDUAL') }); } if (e.field && success) { e.field.value = ''; } }); $('#edit-attendee-add').click(function() { var input = $('#edit-attendee-name'); rcmail.ksearch_blur(); if (add_attendees(input.val(), { role:'REQ-PARTICIPANT', status:'NEEDS-ACTION', cutype:'INDIVIDUAL' })) { input.val(''); } }); // handle change of "send invitations" checkbox $('#edit-attendees-invite').change(function() { $('#edit-attendees-donotify,input.edit-attendee-reply').prop('checked', this.checked); // hide/show comment field $('#taskeditform .attendees-commentbox')[this.checked ? 'show' : 'hide'](); }); // delegate change task to "send invitations" checkbox $('#edit-attendees-donotify').change(function() { $('#edit-attendees-invite').click(); return false; }); } /** * Request counts from the server */ function fetch_counts() { var active = active_lists(); if (active.length) rcmail.http_request('counts', { lists:active.join(',') }); else update_counts({}); } /** * List tasks matching the given selector */ function list_tasks(sel, force) { if (rcmail.busy) return; if (sel && filter_masks[sel] !== undefined) { filtermask = filter_masks[sel]; } var active = active_lists(), basefilter = filtermask & FILTER_MASK_COMPLETE ? FILTER_MASK_COMPLETE : FILTER_MASK_ALL, reload = force || active.join(',') != loadstate.lists || basefilter != loadstate.filter || loadstate.search != search_query; if (active.length && reload) { ui_loading = rcmail.set_busy(true, 'loading'); rcmail.http_request('fetch', { filter:basefilter, lists:active.join(','), q:search_query }, true); } else if (reload) data_ready({ data:[], lists:'', filter:basefilter, search:search_query }); else render_tasklist(); $('#taskselector li.selected').removeClass('selected').attr('aria-checked', 'false'); // select all active selectors if (filtermask > 0) { $.each(filter_masks, function(sel, mask) { if (filtermask & mask) $('#taskselector li.'+sel).addClass('selected').attr('aria-checked', 'true'); }); } else $('#taskselector li.all').addClass('selected').attr('aria-checked', 'true'); } /** * Remove all tasks of the given list from the UI */ function remove_tasks(list_id) { // remove all tasks of the given list from index var newindex = $.grep(listindex, function(id, i){ return listdata[id] && listdata[id].list != list_id; }); listindex = newindex; render_tasklist(); // avoid reloading me.tasklists[list_id].active = false; loadstate.lists = active_lists(); } /** * Modify query parameters for refresh requests */ function before_refresh(query) { query.filter = filtermask == FILTER_MASK_COMPLETE ? FILTER_MASK_COMPLETE : FILTER_MASK_ALL; query.lists = active_lists().join(','); if (search_query) query.q = search_query; return query; } /** * Callback if task data from server is ready */ function data_ready(response) { listdata = {}; listindex = []; loadstate.lists = response.lists; loadstate.filter = response.filter; loadstate.search = response.search; for (var id, i=0; i < response.data.length; i++) { id = response.data[i].id; listindex.push(id); listdata[id] = response.data[i]; listdata[id].children = []; // register a forward-pointer to child tasks if (listdata[id].parent_id && listdata[listdata[id].parent_id]) listdata[listdata[id].parent_id].children.push(id); } // sort index before rendering listindex.sort(function(a, b) { return task_cmp(listdata[a], listdata[b]); }); append_tags(response.tags || []); render_tasklist(); rcmail.set_busy(false, 'loading', ui_loading); } /** * */ function render_tasklist() { // clear display var id, rec, count = 0, cache = {}, activetags = {}, msgbox = $('#listmessagebox').hide(), list = $(rcmail.gui_objects.resultlist).html(''); for (var i=0; i < listindex.length; i++) { id = listindex[i]; rec = listdata[id]; if (match_filter(rec, cache)) { render_task(rec); count++; // keep a list of tags from all visible tasks for (var t, j=0; rec.tags && j < rec.tags.length; j++) { t = rec.tags[j]; if (typeof activetags[t] == 'undefined') activetags[t] = 0; activetags[t]++; } } } fix_tree_toggles(); update_tagcloud(activetags); if (!count) { msgbox.html(rcmail.gettext('notasksfound','tasklist')).show(); rcmail.display_message(rcmail.gettext('notasksfound','tasklist'), 'voice'); } } /** * Show/hide child toggle buttons on all visible task items */ function fix_tree_toggles() { $('.taskitem', rcmail.gui_objects.resultlist).each(function(i,elem){ var li = $(elem), rec = listdata[li.attr('rel')], childs = $('.childtasks li', li); $('.childtoggle', li)[(childs.length ? 'show' : 'hide')](); }) } /** * Expand/collapse all task items with childs */ function expand_collapse(expand) { var collapsed = !expand; $('.taskitem .childtasks')[(collapsed ? 'hide' : 'show')](); $('.taskitem .childtoggle') .removeClass(collapsed ? 'expanded' : 'collapsed') .addClass(collapsed ? 'collapsed' : 'expanded') .html(collapsed ? '▶' : '▼'); // store new toggle collapse states var ids = []; for (var id in listdata) { if (listdata[id].children && listdata[id].children.length) ids.push(id); } if (ids.length) { rcmail.http_post('tasks/task', { action:'collapse', t:{ id:ids.join(',') }, collapsed:collapsed?1:0 }); } } /** * */ function append_tags(taglist) { // find new tags var newtags = []; for (var i=0; i < taglist.length; i++) { if ($.inArray(taglist[i], tags) < 0) newtags.push(taglist[i]); } tags = tags.concat(newtags); // append new tags to tag cloud $.each(newtags, function(i, tag){ $('
  • ') .attr('rel', tag) .data('value', tag) .html(Q(tag) + '') .appendTo(rcmail.gui_objects.tagslist) .draggable({ addClasses: false, revert: 'invalid', revertDuration: 300, helper: tag_draggable_helper, start: tag_draggable_start, appendTo: 'body', cursor: 'pointer' }); }); // re-sort tags list $(rcmail.gui_objects.tagslist).children('li').sortElements(function(a,b){ return $.text([a]).toLowerCase() > $.text([b]).toLowerCase() ? 1 : -1; }); } /** * Display the given counts to each tag and set those inactive which don't * have any matching tasks in the current view. */ function update_tagcloud(counts) { // compute counts first by iterating over all visible task items if (typeof counts == 'undefined') { counts = {}; $('li.taskitem', rcmail.gui_objects.resultlist).each(function(i,li){ var t, id = $(li).attr('rel'), rec = listdata[id]; for (var j=0; rec && rec.tags && j < rec.tags.length; j++) { t = rec.tags[j]; if (typeof counts[t] == 'undefined') counts[t] = 0; counts[t]++; } }); } $(rcmail.gui_objects.tagslist).children('li').each(function(i,li){ var elem = $(li), tag = elem.attr('rel'), count = counts[tag] || 0; elem.children('.count').html(count+''); if (count == 0) elem.addClass('inactive'); else elem.removeClass('inactive'); }); } /* Helper functions for drag & drop functionality of tags */ function tag_draggable_helper() { if (!tag_draghelper) tag_draghelper = $('
    '); else tag_draghelper.html(''); $(this).clone().addClass('tag').appendTo(tag_draghelper); return tag_draghelper; } function tag_draggable_start(event, ui) { $('.taskhead').droppable({ hoverClass: 'droptarget', accept: tag_droppable_accept, drop: tag_draggable_dropped, addClasses: false }); } function tag_droppable_accept(draggable) { if (rcmail.busy) return false; var tag = draggable.data('value'), drop_id = $(this).data('id'), drop_rec = listdata[drop_id], list = drop_rec && me.tasklists[drop_rec.list] ? me.tasklists[drop_rec.list] : { editable:true }; // target is not writeable or already has this tag assigned if (!drop_rec || drop_rec.readonly || !list.editable || (drop_rec.tags && $.inArray(tag, drop_rec.tags) >= 0)) { return false; } return true; } function tag_draggable_dropped(event, ui) { var drop_id = $(this).data('id'), tag = ui.draggable.data('value'), rec = listdata[drop_id]; if (rec && rec.id) { if (!rec.tags) rec.tags = []; rec.tags.push(tag); save_task(rec, 'edit'); } } /** * */ function update_counts(counts) { // got new data if (counts) taskcounts = counts; // iterate over all selector links and update counts $('#taskselector a').each(function(i, elem){ var link = $(elem), f = link.parent().attr('class').replace(/\s\w+/, ''); if (f != 'all') link.children('span').html(taskcounts[f] || '')[(taskcounts[f] ? 'show' : 'hide')](); }); // spacial case: overdue $('#taskselector li.overdue')[(taskcounts.overdue ? 'removeClass' : 'addClass')]('inactive'); } /** * Callback from server to update a single task item */ function update_taskitem(rec, filter) { // handle a list of task records if ($.isArray(rec)) { $.each(rec, function(i,r){ update_taskitem(r, filter); }); return; } var id = rec.id, oldid = rec.tempid || id, oldrec = listdata[oldid], oldindex = $.inArray(oldid, listindex), oldparent = oldrec ? (oldrec._old_parent_id || oldrec.parent_id) : null, list = me.tasklists[rec.list]; if (oldindex >= 0) listindex[oldindex] = id; else listindex.push(id); listdata[id] = rec; // remove child-pointer from old parent if (oldparent && listdata[oldparent] && oldparent != rec.parent_id) { var oldchilds = listdata[oldparent].children, i = $.inArray(oldid, oldchilds); if (i >= 0) { listdata[oldparent].children = oldchilds.slice(0,i).concat(oldchilds.slice(i+1)); } } // register a forward-pointer to child tasks if (rec.parent_id && listdata[rec.parent_id] && listdata[rec.parent_id].children && $.inArray(id, listdata[rec.parent_id].children) < 0) listdata[rec.parent_id].children.push(id); // restore pointers to my children if (!listdata[id].children) { listdata[id].children = []; for (var pid in listdata) { if (listdata[pid].parent_id == id) listdata[id].children.push(pid); } } // copy _depth property from old rec or derive from parent if (rec.parent_id && listdata[rec.parent_id]) { rec._depth = (listdata[rec.parent_id]._depth || 0) + 1; } else if (oldrec) { rec._depth = oldrec._depth || 0; } if (list.active || rec.tempid) { if (!filter || match_filter(rec, {})) render_task(rec, oldid); } else { $('li[rel="'+id+'"]', rcmail.gui_objects.resultlist).remove(); } append_tags(rec.tags || []); update_tagcloud(); fix_tree_toggles(); // refresh currently displayed task details dialog if ($('#taskshow').is(':visible') && me.selected_task && me.selected_task.id == rec.id) { task_show_dialog(rec.id); } } /** * Submit the given (changed) task record to the server */ function save_task(rec, action) { // show confirmation dialog when status of an assigned task has changed if (rec._status_before !== undefined && is_attendee(rec)) return save_task_confirm(rec, action); if (!rcmail.busy) { saving_lock = rcmail.set_busy(true, 'tasklist.savingdata'); rcmail.http_post('tasks/task', { action:action, t:rec, filter:filtermask }); $('button.ui-button:ui-button').button('option', 'disabled', rcmail.busy); return true; } return false; } /** * Display confirm dialog when modifying/deleting a task record */ var save_task_confirm = function(rec, action, updates) { var data = $.extend({}, rec, updates || {}), notify = false, partstat = false, html = '', do_confirm = settings.itip_notify & 2; // task has attendees, ask whether to notify them if (has_attendees(rec) && is_organizer(rec)) { notify = true; if (do_confirm) { html = rcmail.gettext('changeconfirmnotifications', 'tasklist'); } else { data._notify = settings.itip_notify; } } // ask whether to change my partstat and notify organizer else if (data._status_before !== undefined && data.status && data._status_before != data.status && is_attendee(rec)) { partstat = true; if (do_confirm) { html = rcmail.gettext('partstatupdatenotification', 'tasklist'); } else if (settings.itip_notify & 1) { data._reportpartstat = data.status == 'CANCELLED' ? 'DECLINED' : data.status; } } // remove to avoid endless recursion delete data._status_before; // show dialog if (html) { var $dialog = $('
    ').html(html); var buttons = []; buttons.push({ text: rcmail.gettext('saveandnotify', 'tasklist'), click: function() { if (notify) data._notify = 1; if (partstat) data._reportpartstat = data.status == 'CANCELLED' ? 'DECLINED' : data.status; save_task(data, action); $(this).dialog('close'); } }); buttons.push({ text: rcmail.gettext('save', 'tasklist'), click: function() { save_task(data, action); $(this).dialog('close'); } }); buttons.push({ text: rcmail.gettext('cancel', 'tasklist'), click: function() { $(this).dialog('close'); if (updates) render_task(rec, rec.id); // restore previous state } }); $dialog.dialog({ modal: true, width: 460, closeOnEscapeType: false, dialogClass: 'warning no-close', title: rcmail.gettext('changetaskconfirm', 'tasklist'), buttons: buttons, open: function() { setTimeout(function(){ $dialog.parent().find('.ui-button:not(.ui-dialog-titlebar-close)').first().focus(); }, 5); }, close: function(){ $dialog.dialog('destroy').remove(); } }).addClass('task-update-confirm').show(); return true; } // do update return save_task(data, action); } /** * Remove saving lock and free the UI for new input */ function unlock_saving() { if (saving_lock) { rcmail.set_busy(false, null, saving_lock); $('button.ui-button:ui-button').button('option', 'disabled', false); } } /** * Render the given task into the tasks list */ function render_task(rec, replace) { var tags_html = ''; for (var j=0; rec.tags && j < rec.tags.length; j++) tags_html += '' + Q(rec.tags[j]) + ''; var label_id = rcmail.html_identifier(rec.id) + '-title'; var div = $('
    ').addClass('taskhead').html( '
    ' + '' + '' + '' + text2html(Q(rec.title)) + '' + '' + tags_html + '' + '' + Q(rec.date || rcmail.gettext('nodate','tasklist')) + '' + '' ) .attr('tabindex', '0') .attr('aria-labelledby', label_id) .data('id', rec.id) .draggable({ revert: 'invalid', addClasses: false, cursorAt: { left:-10, top:12 }, helper: task_draggable_helper, appendTo: 'body', start: task_draggable_start, stop: task_draggable_stop, drag: task_draggable_move, revertDuration: 300 }); if (is_complete(rec)) div.addClass('complete'); if (rec.flagged) div.addClass('flagged'); if (!rec.date) div.addClass('nodate'); if ((rec.mask & FILTER_MASK_OVERDUE)) div.addClass('overdue'); var li, inplace = false, parent = rec.parent_id ? $('li[rel="'+rec.parent_id+'"] > ul.childtasks', rcmail.gui_objects.resultlist) : null; if (replace && (li = $('li[rel="'+replace+'"]', rcmail.gui_objects.resultlist)) && li.length) { li.children('div.taskhead').first().replaceWith(div); li.attr('rel', rec.id); inplace = true; } else { li = $('
  • ') .attr('rel', rec.id) .addClass('taskitem') .append((rec.collapsed ? '