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