1 /* 2 * ***** BEGIN LICENSE BLOCK ***** 3 * Zimbra Collaboration Suite Web Client 4 * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Synacor, Inc. 5 * 6 * The contents of this file are subject to the Common Public Attribution License Version 1.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at: https://www.zimbra.com/license 9 * The License is based on the Mozilla Public License Version 1.1 but Sections 14 and 15 10 * have been added to cover use of software over a computer network and provide for limited attribution 11 * for the Original Developer. In addition, Exhibit A has been modified to be consistent with Exhibit B. 12 * 13 * Software distributed under the License is distributed on an "AS IS" basis, 14 * WITHOUT WARRANTY OF ANY KIND, either express or implied. 15 * See the License for the specific language governing rights and limitations under the License. 16 * The Original Code is Zimbra Open Source Web Client. 17 * The Initial Developer of the Original Code is Zimbra, Inc. All rights to the Original Code were 18 * transferred by Zimbra, Inc. to Synacor, Inc. on September 14, 2015. 19 * 20 * All portions of the code are Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Synacor, Inc. All Rights Reserved. 21 * ***** END LICENSE BLOCK ***** 22 */ 23 24 /** 25 * @overview 26 * This file defines the key map. 27 */ 28 29 /** 30 * Creates a key map for the ZCS application. 31 * @class 32 * This class maps keys to actions for the ZCS application. There is a global key map 33 * with bindings that apply to any key not handled by the current controller; these 34 * global bindings apply across applications (mail, contacts, etc). Key bindings that 35 * are context-dependent are tied to a particular controller. If that controller has 36 * control, then those bindings will be used. 37 * <br/> 38 * <br/> 39 * Bindings are passed in via the <code>ZmKeys</code> object, which is populated from a properties 40 * file. The identifiers used in the properties file must match those used here. 41 * 42 * @author Ross Dargahi 43 * @author Conrad Damon 44 * 45 * @extends DwtKeyMap 46 */ 47 ZmKeyMap = function() { 48 49 ZmKeyMap._setPreconditions(); 50 DwtKeyMap.call(this); 51 this._load(this._map, ZmKeys); 52 }; 53 54 ZmKeyMap.prototype = new DwtKeyMap(true); 55 ZmKeyMap.prototype.constructor = ZmKeyMap; 56 57 ZmKeyMap.prototype.isZmKeyMap = true; 58 ZmKeyMap.prototype.toString = function() { return "ZmKeyMap"; }; 59 60 61 // Map names (must match those in the key properties file ZmKeys.properties) 62 ZmKeyMap.MAP_ADDRESS = "address"; 63 ZmKeyMap.MAP_BRIEFCASE = "briefcase"; 64 ZmKeyMap.MAP_CALENDAR = "calendar"; 65 ZmKeyMap.MAP_CALL = "call"; 66 ZmKeyMap.MAP_COMPOSE = "compose"; 67 ZmKeyMap.MAP_CONTACTS = "contacts"; 68 ZmKeyMap.MAP_CONVERSATION = "conversation"; 69 ZmKeyMap.MAP_CONVERSATION_LIST = "conversationList"; 70 ZmKeyMap.MAP_DL_ADDRESS_LIST = "dlAddressList"; 71 ZmKeyMap.MAP_EDIT_APPOINTMENT = "editAppointment"; 72 ZmKeyMap.MAP_EDIT_CONTACT = "editContact"; 73 ZmKeyMap.MAP_EDIT_TASK = "editTask"; 74 ZmKeyMap.MAP_GLOBAL = "global"; 75 ZmKeyMap.MAP_MAIL = "mail"; 76 ZmKeyMap.MAP_MESSAGE = "message"; 77 ZmKeyMap.MAP_QUICK_REPLY = "quickReply"; 78 ZmKeyMap.MAP_OPTIONS = "options"; 79 ZmKeyMap.MAP_TASKS = "tasks"; 80 ZmKeyMap.MAP_VIEW_APPOINTMENT = "viewAppointment"; 81 ZmKeyMap.MAP_VOICEMAIL = "voicemail"; 82 83 // Action codes 84 ZmKeyMap.ADDRESS_PICKER = "AddressPicker"; 85 ZmKeyMap.ADD_EXTERNAL_CALENDAR = "AddExternalCalendar"; 86 ZmKeyMap.ATTACHMENT = "Attachment"; 87 ZmKeyMap.CAL_DAY_VIEW = "DayView"; 88 ZmKeyMap.CAL_FB_VIEW = "FBView"; 89 ZmKeyMap.CAL_LIST_VIEW = "CalListView"; 90 ZmKeyMap.CAL_MONTH_VIEW = "MonthView"; 91 ZmKeyMap.CAL_WEEK_VIEW = "WeekView"; 92 ZmKeyMap.CAL_WORK_WEEK_VIEW = "WorkWeekView"; 93 ZmKeyMap.CALL_MANAGER = "CallManager"; 94 ZmKeyMap.CANCEL = "Cancel"; 95 ZmKeyMap.COLLAPSE = "Collapse"; 96 ZmKeyMap.COLLAPSE_ALL = "CollapseAll"; 97 ZmKeyMap.DEL = "Delete"; 98 ZmKeyMap.SHIFT_DEL = "ShiftDelete"; 99 ZmKeyMap.DOWNLOAD = "Download"; 100 ZmKeyMap.EDIT = "Edit"; 101 ZmKeyMap.EXPAND = "Expand"; 102 ZmKeyMap.EXPAND_ALL = "ExpandAll"; 103 ZmKeyMap.FIRST_UNREAD = "FirstUnread"; 104 ZmKeyMap.FIRST_UNREAD_MSG = "FirstUnreadMsg"; 105 ZmKeyMap.FLAG = "Flag"; 106 ZmKeyMap.FOCUS_CONTENT_PANE = "FocusContentPane"; 107 ZmKeyMap.FOCUS_SEARCH_BOX = "FocusSearchBox"; 108 ZmKeyMap.FOCUS_TOOLBAR = "FocusToolbar"; 109 ZmKeyMap.FORWARD = "Forward"; 110 ZmKeyMap.GET_MAIL = "GetMail"; 111 ZmKeyMap.GOTO_BRIEFCASE = "GoToBriefcase"; 112 ZmKeyMap.GOTO_CALENDAR = "GoToCalendar"; 113 ZmKeyMap.GOTO_CONTACTS = "GoToContacts"; 114 ZmKeyMap.GOTO_DRAFTS = "GoToDrafts"; 115 ZmKeyMap.GOTO_JUNK = "GoToJunk"; 116 ZmKeyMap.GOTO_INBOX = "GoToInbox"; 117 ZmKeyMap.GOTO_MAIL = "GoToMail"; 118 ZmKeyMap.GOTO_OPTIONS = "GoToOptions"; 119 ZmKeyMap.GOTO_SENT = "GoToSent"; 120 ZmKeyMap.GOTO_TASKS = "GoToTasks"; 121 ZmKeyMap.GOTO_TRASH = "GoToTrash"; 122 ZmKeyMap.GOTO_VOICE = "GoToVoice"; 123 ZmKeyMap.HTML_FORMAT = "HtmlFormat"; 124 ZmKeyMap.KEEP_READING = "KeepReading"; 125 ZmKeyMap.LAST_UNREAD = "LastUnread"; 126 ZmKeyMap.LAST_UNREAD_MSG = "LastUnreadMsg"; 127 ZmKeyMap.MARK_COMPLETE = "MarkComplete"; 128 ZmKeyMap.MARK_HEARD = "MarkHeard"; 129 ZmKeyMap.MARK_READ = "MarkRead"; 130 ZmKeyMap.MARK_UNCOMPLETE = "MarkUncomplete"; 131 ZmKeyMap.MARK_UNHEARD = "MarkUnheard"; 132 ZmKeyMap.MARK_UNREAD = "MarkUnread"; 133 ZmKeyMap.MOVE = "Move"; 134 ZmKeyMap.MOVE_TO_INBOX = "MoveToInbox"; 135 ZmKeyMap.MOVE_TO_JUNK = "MoveToJunk"; 136 ZmKeyMap.MOVE_TO_TRASH = "MoveToTrash"; 137 ZmKeyMap.MUTE_UNMUTE_CONV = "MuteUnmuteConv"; 138 ZmKeyMap.NEW = "New"; 139 ZmKeyMap.NEW_APPT = "NewAppointment"; 140 ZmKeyMap.NEW_BRIEFCASE = "NewBriefcase"; 141 ZmKeyMap.NEW_CALENDAR = "NewCalendar"; 142 ZmKeyMap.NEW_CONTACT = "NewContact"; 143 ZmKeyMap.NEW_DOC = "NewDocument"; 144 ZmKeyMap.NEW_FILE = "NewFile"; 145 ZmKeyMap.NEW_FOLDER = "NewFolder"; 146 ZmKeyMap.NEW_MESSAGE = "NewMessage"; 147 ZmKeyMap.NEW_MESSAGE_WIN = "NewMessageWindow"; 148 ZmKeyMap.NEW_SEARCH = "NewSearch"; 149 ZmKeyMap.NEW_TAG = "NewTag"; 150 ZmKeyMap.NEW_TASK = "NewTask"; 151 ZmKeyMap.NEW_WINDOW = "NewWindow"; 152 ZmKeyMap.NEXT_APPT = "NextAppointment"; 153 ZmKeyMap.NEXT_CONV = "NextConversation"; 154 ZmKeyMap.NEXT_DAY = "NextDay"; 155 ZmKeyMap.NEXT_MSG = "NextMessage"; 156 ZmKeyMap.NEXT_PAGE = "NextPage"; 157 ZmKeyMap.NEXT_UNREAD = "NextUnread"; 158 ZmKeyMap.NEXT_UNREAD_MSG = "NextUnreadMsg"; 159 ZmKeyMap.PLAY = "Play"; 160 ZmKeyMap.PRESENCE_MENU = "PresenceMenu"; 161 ZmKeyMap.PREV_APPT = "PreviousAppointment"; 162 ZmKeyMap.PREV_CONV = "PreviousConversation"; 163 ZmKeyMap.PREV_DAY = "PreviousDay"; 164 ZmKeyMap.PREV_MSG = "PreviousMessage"; 165 ZmKeyMap.PREV_PAGE = "PreviousPage"; 166 ZmKeyMap.PREV_UNREAD = "PreviousUnread"; 167 ZmKeyMap.PREV_UNREAD_MSG = "PreviousUnreadMsg"; 168 ZmKeyMap.PRINT = "Print"; 169 ZmKeyMap.PRINT_ALL = "PrintAll"; 170 ZmKeyMap.QUICK_ADD = "QuickAdd"; 171 ZmKeyMap.QUICK_REMINDER = "QuickReminder"; 172 ZmKeyMap.READING_PANE_BOTTOM = "ReadingPaneAtBottom"; 173 ZmKeyMap.READING_PANE_OFF = "ReadingPaneOff"; 174 ZmKeyMap.READING_PANE_RIGHT = "ReadingPaneOnRight"; 175 ZmKeyMap.REFRESH = "Refresh"; 176 ZmKeyMap.REPLY = "Reply"; 177 ZmKeyMap.REPLY_ALL = "ReplyAll"; 178 ZmKeyMap.SAVE = "Save"; 179 ZmKeyMap.SAVED_SEARCH = "SavedSearch"; 180 ZmKeyMap.SELECT_ALL = "SelectAll"; 181 ZmKeyMap.SEND = "Send"; 182 ZmKeyMap.SHORTCUTS = "Shortcuts"; 183 ZmKeyMap.SHOW_FRAGMENT = "ShowFragment"; 184 ZmKeyMap.SPAM = "Spam"; 185 ZmKeyMap.SPELLCHECK = "Spellcheck"; 186 ZmKeyMap.TAG = "Tag"; 187 ZmKeyMap.TODAY = "Today"; 188 ZmKeyMap.TOGGLE = "Toggle"; 189 ZmKeyMap.UNTAG = "Untag"; 190 ZmKeyMap.VIEW_BY_CONV = "ViewByConversation"; 191 ZmKeyMap.VIEW_BY_MSG = "ViewByMessage"; 192 ZmKeyMap.VISIT = "Visit"; 193 ZmKeyMap.VISIT_TAG = "VisitTag"; 194 195 // HTML entities (used to display keys) 196 ZmKeyMap.ENTITY = {}; 197 ZmKeyMap.ENTITY[DwtKeyMap.ARROW_LEFT] = "←" 198 ZmKeyMap.ENTITY[DwtKeyMap.ARROW_RIGHT] = "→" 199 ZmKeyMap.ENTITY[DwtKeyMap.ARROW_UP] = "↑" 200 ZmKeyMap.ENTITY[DwtKeyMap.ARROW_DOWN] = "↓" 201 ZmKeyMap.ENTITY['"'] = """ 202 ZmKeyMap.ENTITY['&'] = "&" 203 ZmKeyMap.ENTITY['<'] = "<" 204 ZmKeyMap.ENTITY['>'] = ">" 205 ZmKeyMap.ENTITY[DwtKeyMap.COMMA] = ","; 206 ZmKeyMap.ENTITY[DwtKeyMap.SEMICOLON] = ";"; 207 ZmKeyMap.ENTITY[DwtKeyMap.BACKSLASH] = "\\"; 208 209 // preconditions for maps 210 ZmKeyMap.MAP_PRECONDITION = {}; 211 212 // preconditions for specific shortcuts 213 ZmKeyMap.ACTION_PRECONDITION = {}; 214 215 ZmKeyMap._setPreconditions = 216 function() { 217 ZmKeyMap.MAP_PRECONDITION[ZmKeyMap.MAP_COMPOSE] = ZmSetting.MAIL_ENABLED; 218 ZmKeyMap.MAP_PRECONDITION[ZmKeyMap.MAP_MAIL] = ZmSetting.MAIL_ENABLED; 219 ZmKeyMap.MAP_PRECONDITION[ZmKeyMap.MAP_CONVERSATION_LIST] = ZmSetting.MAIL_ENABLED; 220 ZmKeyMap.MAP_PRECONDITION[ZmKeyMap.MAP_CONVERSATION] = ZmSetting.MAIL_ENABLED; 221 ZmKeyMap.MAP_PRECONDITION[ZmKeyMap.MAP_DL_ADDRESS_LIST] = ZmSetting.CONTACTS_ENABLED; 222 ZmKeyMap.MAP_PRECONDITION[ZmKeyMap.MAP_MESSAGE] = ZmSetting.MAIL_ENABLED; 223 ZmKeyMap.MAP_PRECONDITION[ZmKeyMap.MAP_CONTACTS] = ZmSetting.CONTACTS_ENABLED; 224 ZmKeyMap.MAP_PRECONDITION[ZmKeyMap.MAP_EDIT_CONTACT] = ZmSetting.CONTACTS_ENABLED; 225 ZmKeyMap.MAP_PRECONDITION[ZmKeyMap.MAP_CALENDAR] = ZmSetting.CALENDAR_ENABLED; 226 ZmKeyMap.MAP_PRECONDITION[ZmKeyMap.MAP_EDIT_APPOINTMENT] = ZmSetting.CALENDAR_ENABLED; 227 ZmKeyMap.MAP_PRECONDITION[ZmKeyMap.MAP_OPTIONS] = ZmSetting.OPTIONS_ENABLED; 228 ZmKeyMap.MAP_PRECONDITION[ZmKeyMap.MAP_BRIEFCASE] = ZmSetting.BRIEFCASE_ENABLED; 229 ZmKeyMap.MAP_PRECONDITION[ZmKeyMap.MAP_TASKS] = ZmSetting.TASKS_ENABLED; 230 ZmKeyMap.MAP_PRECONDITION[ZmKeyMap.MAP_EDIT_TASK] = ZmSetting.TASKS_ENABLED; 231 ZmKeyMap.MAP_PRECONDITION[ZmKeyMap.MAP_VOICEMAIL] = ZmSetting.VOICE_ENABLED; 232 ZmKeyMap.MAP_PRECONDITION[ZmKeyMap.MAP_CALL] = ZmSetting.VOICE_ENABLED; 233 234 ZmKeyMap.ACTION_PRECONDITION[ZmKeyMap.MAP_GLOBAL] = {}; 235 ZmKeyMap.ACTION_PRECONDITION[ZmKeyMap.MAP_GLOBAL][ZmKeyMap.FOCUS_SEARCH_BOX] = ZmSetting.SEARCH_ENABLED; 236 ZmKeyMap.ACTION_PRECONDITION[ZmKeyMap.MAP_GLOBAL][ZmKeyMap.GOTO_BRIEFCASE] = ZmSetting.BRIEFCASE_ENABLED; 237 ZmKeyMap.ACTION_PRECONDITION[ZmKeyMap.MAP_GLOBAL][ZmKeyMap.GOTO_CALENDAR] = ZmSetting.CALENDAR_ENABLED; 238 ZmKeyMap.ACTION_PRECONDITION[ZmKeyMap.MAP_GLOBAL][ZmKeyMap.GOTO_CONTACTS] = ZmSetting.CONTACTS_ENABLED; 239 ZmKeyMap.ACTION_PRECONDITION[ZmKeyMap.MAP_GLOBAL][ZmKeyMap.GOTO_MAIL] = ZmSetting.MAIL_ENABLED; 240 ZmKeyMap.ACTION_PRECONDITION[ZmKeyMap.MAP_GLOBAL][ZmKeyMap.GOTO_OPTIONS] = ZmSetting.OPTIONS_ENABLED; 241 ZmKeyMap.ACTION_PRECONDITION[ZmKeyMap.MAP_GLOBAL][ZmKeyMap.GOTO_TASKS] = ZmSetting.TASKS_ENABLED; 242 ZmKeyMap.ACTION_PRECONDITION[ZmKeyMap.MAP_GLOBAL][ZmKeyMap.GOTO_VOICE] = ZmSetting.VOICE_ENABLED; 243 ZmKeyMap.ACTION_PRECONDITION[ZmKeyMap.MAP_GLOBAL][ZmKeyMap.NEW_APPT] = ZmSetting.CALENDAR_ENABLED; 244 ZmKeyMap.ACTION_PRECONDITION[ZmKeyMap.MAP_GLOBAL][ZmKeyMap.NEW_BRIEFCASEITEM] = ZmSetting.BRIEFCASE_ENABLED; 245 ZmKeyMap.ACTION_PRECONDITION[ZmKeyMap.MAP_GLOBAL][ZmKeyMap.NEW_CALENDAR] = ZmSetting.CALENDAR_ENABLED; 246 ZmKeyMap.ACTION_PRECONDITION[ZmKeyMap.MAP_GLOBAL][ZmKeyMap.NEW_CONTACT] = ZmSetting.CONTACTS_ENABLED; 247 ZmKeyMap.ACTION_PRECONDITION[ZmKeyMap.MAP_GLOBAL][ZmKeyMap.NEW_FILE] = ZmSetting.BRIEFCASE_ENABLED; 248 ZmKeyMap.ACTION_PRECONDITION[ZmKeyMap.MAP_GLOBAL][ZmKeyMap.NEW_DOC] = ZmSetting.DOCS_ENABLED; 249 ZmKeyMap.ACTION_PRECONDITION[ZmKeyMap.MAP_GLOBAL][ZmKeyMap.NEW_FOLDER] = ZmSetting.MAIL_ENABLED; 250 ZmKeyMap.ACTION_PRECONDITION[ZmKeyMap.MAP_GLOBAL][ZmKeyMap.NEW_MESSAGE] = ZmSetting.MAIL_ENABLED; 251 ZmKeyMap.ACTION_PRECONDITION[ZmKeyMap.MAP_GLOBAL][ZmKeyMap.NEW_MESSAGE_WIN] = ZmSetting.MAIL_ENABLED; 252 ZmKeyMap.ACTION_PRECONDITION[ZmKeyMap.MAP_GLOBAL][ZmKeyMap.NEW_TAG] = ZmSetting.TAGGING_ENABLED; 253 ZmKeyMap.ACTION_PRECONDITION[ZmKeyMap.MAP_GLOBAL][ZmKeyMap.NEW_TASK] = ZmSetting.TASKS_ENABLED; 254 ZmKeyMap.ACTION_PRECONDITION[ZmKeyMap.MAP_GLOBAL][ZmKeyMap.SAVED_SEARCH] = ZmSetting.SAVED_SEARCHES_ENABLED; 255 ZmKeyMap.ACTION_PRECONDITION[ZmKeyMap.MAP_GLOBAL][ZmKeyMap.TAG] = ZmSetting.TAGGING_ENABLED; 256 ZmKeyMap.ACTION_PRECONDITION[ZmKeyMap.MAP_GLOBAL][ZmKeyMap.UNTAG] = ZmSetting.TAGGING_ENABLED; 257 258 ZmKeyMap.ACTION_PRECONDITION[ZmKeyMap.MAP_COMPOSE] = {}; 259 ZmKeyMap.ACTION_PRECONDITION[ZmKeyMap.MAP_COMPOSE][ZmKeyMap.ADDRESS_PICKER] = ZmSetting.CONTACTS_ENABLED; 260 ZmKeyMap.ACTION_PRECONDITION[ZmKeyMap.MAP_COMPOSE][ZmKeyMap.HTML_FORMAT] = ZmSetting.HTML_COMPOSE_ENABLED; 261 ZmKeyMap.ACTION_PRECONDITION[ZmKeyMap.MAP_COMPOSE][ZmKeyMap.NEW_WINDOW] = ZmSetting.NEW_WINDOW_COMPOSE; 262 ZmKeyMap.ACTION_PRECONDITION[ZmKeyMap.MAP_COMPOSE][ZmKeyMap.SAVE] = ZmSetting.SAVE_DRAFT_ENABLED; 263 264 ZmKeyMap.ACTION_PRECONDITION[ZmKeyMap.MAP_EDIT_APPOINTMENT] = {}; 265 ZmKeyMap.ACTION_PRECONDITION[ZmKeyMap.MAP_EDIT_APPOINTMENT][ZmKeyMap.HTML_FORMAT] = ZmSetting.HTML_COMPOSE_ENABLED; 266 267 ZmKeyMap.ACTION_PRECONDITION[ZmKeyMap.MAP_CALENDAR] = {}; 268 ZmKeyMap.ACTION_PRECONDITION[ZmKeyMap.MAP_CALENDAR][ZmKeyMap.CAL_FB_VIEW] = ZmSetting.FREE_BUSY_VIEW_ENABLED; 269 }; 270 271 /** 272 * Checks if this map is valid. A map may have a precondition, 273 * which is either a setting that must be true, or a function that returns 274 * true. 275 * 276 * @param {String} mapName the name of map 277 * @return {Boolean} <code>true</code> if the map is valid 278 * 279 * @private 280 */ 281 ZmKeyMap.prototype._checkMap = function(mapName) { 282 283 var result = this._checkedMap[mapName] = appCtxt.checkPrecondition(ZmKeyMap.MAP_PRECONDITION[mapName]); 284 return result; 285 }; 286 287 /** 288 * Checks if this action is valid. A map or an action may have a precondition, 289 * which is either a setting that must be true, or a function that returns 290 * true. 291 * 292 * @param {String} mapName the name of map 293 * @param {String} action the action to check 294 * @return {Boolean} <code>true</code> if the action is valid 295 * 296 * @private 297 */ 298 ZmKeyMap.prototype._checkAction = function(mapName, action) { 299 300 if (this._checkedMap[mapName] === false || (!this._checkedMap[mapName] && !this._checkMap(mapName))) { 301 return false; 302 } 303 304 var mapPre = ZmKeyMap.ACTION_PRECONDITION[mapName]; 305 return appCtxt.checkPrecondition(mapPre && mapPre[action]); 306 }; 307