1 /* 2 * ***** BEGIN LICENSE BLOCK ***** 3 * Zimbra Collaboration Suite Web Client 4 * Copyright (C) 2004, 2005, 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) 2004, 2005, 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 contains the operation class. 27 * 28 */ 29 30 /** 31 * Creates the operation object. 32 * @class 33 * This class provides the idea of an "operation", which is a user-initiated action 34 * exposed through a button or menu item. Many operations (such as Delete) are shared 35 * across applications/controllers. An operation gets defined by specifying its name, 36 * tool tip, and image. Then controllers can simply select which operations they'd like 37 * to support. 38 * <br/> 39 * <br/> 40 * The two primary clients of this class are {@link ZmButtonToolBar} and {@link ZmActionMenu}. Clients 41 * should support {@link #createOp} and {@link #getOp} methods. See the two aforementioned clients for 42 * examples. 43 * 44 * @author Conrad Damon 45 */ 46 ZmOperation = function() {}; 47 48 // Special operations 49 ZmOperation.NONE = "NONE"; // no operations or menu items 50 ZmOperation.SEP = "SEP"; // separator 51 ZmOperation.SPACER = "SPACER"; // spacer (toolbar) 52 ZmOperation.FILLER = "FILLER"; // filler (toolbar) 53 54 // suffix for disabled image 55 ZmOperation.DIS = "Dis"; 56 57 // text and icons displayed for operation 58 ZmOperation.SETUP = {}; 59 60 // special-purpose operations 61 ZmOperation.SETUP[ZmOperation.NONE] = {}; // means no operations (as opposed to a default set) 62 ZmOperation.SETUP[ZmOperation.SEP] = {}; // a thin vertical or horizontal bar 63 ZmOperation.SETUP[ZmOperation.SPACER] = {}; // empty space of a given size 64 ZmOperation.SETUP[ZmOperation.FILLER] = {}; // expandable space (for right-align in toolbars) 65 66 // preconditions for operations - no automatic checking is done, so a client 67 // of this class has to check them on its own if it wants 68 ZmOperation.PRECONDITION = {}; 69 70 // code to run after an operation has been created - typically used to create 71 // a menu for a button 72 ZmOperation.CALLBACK = {}; 73 74 /** 75 * Defines the aspects of an operation, and the ID that refers to it. 76 * 77 * @param {String} op the name of the operation 78 * @param {hash} params: 79 * @param {String} text the msg key for button or menu item text 80 * @param {String} tooltip the msg key for tooltip text 81 * @param {String} image the icon class for the button or menu item 82 * @param {String} disImage the disabled version of image; defaults to image + "Dis" 83 * @param {Boolean|String|Function} precondition (overrides setting if present) 84 * @param {constant} precondition must evaluate to true for this operation not to be filtered out 85 * @param {AjxCallback} callback the callback to run after this operation has been created 86 */ 87 ZmOperation.registerOp = function(op, params, precondition, callback) { 88 89 ZmOperation[op] = op; 90 ZmOperation.SETUP[op] = params || {}; 91 if (precondition) { ZmOperation.PRECONDITION[op] = precondition; } 92 if (callback) { ZmOperation.CALLBACK[op] = callback; } 93 }; 94 95 96 ZmOperation.KEY_ID = "opId"; 97 ZmOperation.MENUITEM_ID = "menuItemId"; 98 99 ZmOperation.NEW_ITEM_OPS = []; 100 ZmOperation.NEW_ITEM_KEY = {}; 101 ZmOperation.NEW_ORG_OPS = []; 102 ZmOperation.NEW_ORG_KEY = {}; 103 104 // Static hash of operation IDs ad descriptors 105 ZmOperation._operationDesc = {}; 106 107 /** 108 * Initializes and creates standard operations. 109 * 110 */ 111 ZmOperation.initialize = 112 function() { 113 ZmOperation.registerOp(ZmId.OP_ACTIONS_MENU, {textKey:"actions", tooltipKey:"", textPrecedence:40}); 114 115 ZmOperation.registerOp(ZmId.OP_ATTACHMENT, {textKey:"addAttachment", tooltipKey:"attachmentTooltip", image:"Attachment", shortcut:ZmKeyMap.ATTACHMENT, showImageInToolbar: true}); 116 ZmOperation.registerOp(ZmId.OP_CALL, {image:"Telephone"}); 117 ZmOperation.registerOp(ZmId.OP_CANCEL, {textKey:"cancel", tooltipKey:"cancelTooltip", image:"Cancel", shortcut:ZmKeyMap.CANCEL}); 118 ZmOperation.registerOp(ZmId.OP_CHECK_ALL, {textKey:"checkAll", image:"Check"}); 119 ZmOperation.registerOp(ZmId.OP_CLEAR_ALL, {textKey:"clearAll", image:"Cancel"}); 120 ZmOperation.registerOp(ZmId.OP_CLOSE, {textKey:"close", tooltipKey:"closeTooltip", image:"Close", shortcut:ZmKeyMap.CANCEL}); 121 ZmOperation.registerOp(ZmId.OP_COMPOSE_FORMAT, {textKey:"format", tooltipKey:"formatTooltip", image:"SwitchFormat", shortcut:ZmKeyMap.HTML_FORMAT}, ZmSetting.HTML_COMPOSE_ENABLED); 122 ZmOperation.registerOp(ZmId.OP_CONTACTGROUP_MENU, {textKey: "AB_CONTACT_GROUP", tooltipKey:"contactGroupTooltip", image:"Group"}, null, 123 AjxCallback.simpleClosure(function(parent) { 124 ZmOperation.addDeferredMenu(ZmOperation.addContactGroupMenu, parent, true); 125 })); 126 ZmOperation.registerOp(ZmId.OP_COPY, {textKey:"copy", image:"Copy"}); 127 ZmOperation.registerOp(ZmId.OP_DELETE, {textKey:"del", tooltipKey:"deleteTooltip", image:"Delete", shortcut:ZmKeyMap.DEL, textPrecedence:60}); 128 ZmOperation.registerOp(ZmId.OP_DELETE_WITHOUT_SHORTCUT, {textKey:"del", tooltipKey:"deleteTooltip", image:"Delete", textPrecedence:60}); 129 ZmOperation.registerOp(ZmId.OP_DETACH, {textKey:"detach", tooltipKey:"detach", image:"OpenInNewWindow", showImageInToolbar: true}); 130 ZmOperation.registerOp(ZmId.OP_DETACH_WIN, {textKey:"detach", image:"OpenInNewWindow"}); 131 ZmOperation.registerOp(ZmId.OP_EDIT, {textKey:"edit", tooltipKey:"editTooltip", image:"Edit", shortcut:ZmKeyMap.EDIT}); 132 ZmOperation.registerOp(ZmId.OP_EDIT_AS_NEW, {textKey:"editAsNew", tooltipKey:"editTooltip", image:"Edit", shortcut:ZmKeyMap.EDIT}); 133 ZmOperation.registerOp(ZmId.OP_EDIT_PROPS, {textKey:"editProperties", tooltipKey:"editPropertiesTooltip", image:"Properties"}); 134 ZmOperation.registerOp(ZmId.OP_EXPAND, {textKey:"expand", image:"Plus"}); 135 ZmOperation.registerOp(ZmId.OP_EXPAND_ALL, {textKey:"expandAll", image:"Plus"}); 136 // ZmOperation.registerOp(ZmId.OP_EXPORT_FOLDER, {textKey:"exportFolder", image:"MailExport"}); 137 ZmOperation.registerOp(ZmId.OP_EMPTY_FOLDER,{textKey:"emptyFolder",image:"EmptyFolder"}); 138 ZmOperation.registerOp(ZmId.OP_FORMAT_HTML, {textKey: "formatAsHtml"}, ZmSetting.HTML_COMPOSE_ENABLED); 139 ZmOperation.registerOp(ZmId.OP_FORMAT_TEXT, {textKey: "formatAsText"}, ZmSetting.HTML_COMPOSE_ENABLED); 140 ZmOperation.registerOp(ZmId.OP_FORMAT_MORE_OPTIONS, {textKey: "moreComposeOptions"}); 141 ZmOperation.registerOp(ZmId.OP_GROUPBY, {textKey:"groupBy"}); 142 ZmOperation.registerOp(ZmId.OP_GROUPBY_NONE, {textKey:"groupByNone"}); 143 ZmOperation.registerOp(ZmId.OP_GROUPBY_DATE, {textKey:"groupByDate"}); 144 ZmOperation.registerOp(ZmId.OP_GROUPBY_FROM, {textKey:"groupByFrom"}); 145 ZmOperation.registerOp(ZmId.OP_GROUPBY_PRIORITY, {textKey:"groupByPriority"}); 146 ZmOperation.registerOp(ZmId.OP_GROUPBY_SIZE, {textKey:"groupBySize"}); 147 ZmOperation.registerOp(ZmId.OP_GROUPBY_TAG, {textKey:"groupByTag"}); 148 ZmOperation.registerOp(ZmId.OP_GO_TO_URL, {image:"URL", textKey:"goToUrlAlt"}); 149 // ZmOperation.registerOp(ZmId.OP_IMPORT_FOLDER, {textKey:"importFolder", image:"MailImport"}); 150 ZmOperation.registerOp(ZmId.OP_MARK_ALL_READ, {textKey:"markAllRead", image:"ReadMessage"}); 151 // ZmOperation.registerOp(ZmId.OP_MOUNT_FOLDER, {textKey:"mountFolder", image:"Folder"}); 152 ZmOperation.registerOp(ZmId.OP_MOVE, {textKey:"move", tooltipKey:"moveTooltip", image:"MoveToFolder", textPrecedence:40, showImageInToolbar: true}); //todo - remove 153 ZmOperation.registerOp(ZmId.OP_MOVE_MENU, {textKey: "move", tooltipKey:"moveTooltip", image:"MoveToFolder", showImageInToolbar: true }, null, 154 AjxCallback.simpleClosure(function(parent) { 155 ZmOperation.addDeferredMenu(ZmOperation.addMoveMenu, parent, true); 156 })); 157 158 ZmOperation.registerOp(ZmId.OP_MUTE_CONV, {textKey:"muteConv", tooltipKey:"muteConvTooltip", image:"Mute", shortcut:ZmKeyMap.MUTE_UNMUTE_CONV}); 159 ZmOperation.registerOp(ZmId.OP_NEW_FOLDER, {textKey:"newFolder", tooltipKey:"newFolderTooltip", image:"NewFolder", shortcut:ZmKeyMap.NEW_FOLDER}, ZmSetting.USER_FOLDERS_ENABLED); 160 ZmOperation.registerOp(ZmId.OP_NEW_MENU, {textKey:"_new", shortcut:ZmKeyMap.NEW, textPrecedence:100}, null, 161 AjxCallback.simpleClosure(function(parent) { 162 ZmOperation.addDeferredMenu(ZmOperation.addNewMenu, parent); 163 })); 164 ZmOperation.registerOp(ZmId.OP_NEW_TAG, {textKey:"newTag", tooltipKey:"newTagTooltip", image:"NewTag", shortcut:ZmKeyMap.NEW_TAG}, ZmSetting.TAGGING_ENABLED); 165 ZmOperation.registerOp(ZmId.OP_NOTIFY, {textKey: "notify", image:"Feedback"}); 166 ZmOperation.registerOp(ZmId.OP_OPEN_IN_TAB, {textKey:"openInTab", image:"OpenInNewTab"}); 167 ZmOperation.registerOp(ZmId.OP_OPTS, {textKey:"options", tooltipKey:"options", image:"ContextMenu"}); 168 ZmOperation.registerOp(ZmId.OP_PAGE_BACK, {image:"LeftArrow", shortcut:ZmKeyMap.PREV_PAGE}); 169 ZmOperation.registerOp(ZmId.OP_PAGE_FORWARD, {image:"RightArrow", shortcut:ZmKeyMap.NEXT_PAGE}); 170 ZmOperation.registerOp(ZmId.OP_PRINT, {textKey:"print", tooltipKey:"printTooltip", image:"Print", shortcut:ZmKeyMap.PRINT, textPrecedence:30, showImageInToolbar: true}, ZmSetting.PRINT_ENABLED); 171 ZmOperation.registerOp(ZmId.OP_PRIORITY_FILTER, {image:"Priority", textKey:"activityStream"}, ZmSetting.PRIORITY_INBOX_ENABLED); 172 ZmOperation.registerOp(ZmId.OP_FIND_SHARES, {image:"Group", textKey:"findShares"}, ZmSetting.SHARING_ENABLED); 173 174 //ZmOperation.registerOp(ZmId.OP_QUICK_COMMANDS, {textKey:"quickCommands", image:"QuickCommand"}); 175 ZmOperation.registerOp(ZmId.OP_RECOVER_DELETED_ITEMS, {textKey:"recoverDeletedItems", image:"Trash", tooltipKey:"recoverDeletedItems"}, ZmSetting.DUMPSTER_ENABLED); 176 ZmOperation.registerOp(ZmId.OP_REFRESH, {textKey:"refresh", tooltipKey:"refreshTooltip"}); 177 ZmOperation.registerOp(ZmId.OP_RENAME_FOLDER, {textKey:"renameFolder", image:"Rename"}); 178 ZmOperation.registerOp(ZmId.OP_RENAME_SEARCH, {textKey:"renameSearch", image:"Rename"}); 179 ZmOperation.registerOp(ZmId.OP_RENAME_TAG, {textKey:"renameTag", image:"Rename"}, ZmSetting.TAGGING_ENABLED); 180 ZmOperation.registerOp(ZmId.OP_SAVE, {textKey:"save", image:"Save", shortcut:ZmKeyMap.SAVE}); 181 ZmOperation.registerOp(ZmId.OP_SEARCH, {textKey:"findEmailFromSender", image:"Search"}, ZmSetting.SEARCH_ENABLED); 182 ZmOperation.registerOp(ZmId.OP_SEARCH_TO, {textKey:"findEmailToSender", image:"Search"}, ZmSetting.SEARCH_ENABLED); 183 ZmOperation.registerOp(ZmId.OP_SEARCH_MENU, {textKey:"findEmails", image:"Search"}, ZmSetting.SEARCH_ENABLED, 184 AjxCallback.simpleClosure(function(parent) { 185 ZmOperation.addDeferredMenu(ZmOperation.addSearchMenu, parent, true); 186 })); 187 ZmOperation.registerOp(ZmId.OP_SEND, {textKey:"send", tooltipKey:"sendTooltip", image:"Send", shortcut:ZmKeyMap.SEND}); 188 ZmOperation.registerOp(ZmId.OP_FREE_BUSY_LINK, {textKey:"freeBusyLink", tooltipKey:"freeBusyLinkTooltip", image:"Send"}); 189 ZmOperation.registerOp(ZmId.OP_SEND_FB_HTML, {textKey:"sendHTMLLink", tooltipKey:"freeBusyLinkTooltip"}); 190 ZmOperation.registerOp(ZmId.OP_SEND_FB_ICS, {textKey:"sendICSLink", tooltipKey:"freeBusyLinkTooltip"}); 191 ZmOperation.registerOp(ZmId.OP_SEND_FB_ICS_EVENT, {textKey:"sendICSEventLink", tooltipKey:"freeBusyLinkTooltip"}); 192 ZmOperation.registerOp(ZmId.OP_SHARE, {textKey:"share", tooltipKey:"shareTooltip"}, ZmSetting.SHARING_ENABLED); 193 ZmOperation.registerOp(ZmId.OP_SHARE_ACCEPT, {textKey:"acceptShare", image:"Check"}, ZmSetting.SHARING_ENABLED); 194 ZmOperation.registerOp(ZmId.OP_SHARE_DECLINE, {textKey:"declineShare", image:"Cancel"}, ZmSetting.SHARING_ENABLED); 195 ZmOperation.registerOp(ZmId.OP_SUBSCRIBE_APPROVE, {textKey:"dlApprove", image:"Check"}); 196 ZmOperation.registerOp(ZmId.OP_SUBSCRIBE_REJECT, {textKey:"dlReject", image:"Cancel"}); 197 ZmOperation.registerOp(ZmId.OP_SHARE_FOLDER, {textKey:"shareFolder", image:"SharedMailFolder"}); 198 ZmOperation.registerOp(ZmId.OP_SHOW_ALL_ITEM_TYPES, {textKey:"showAllItemTypes", image:"Globe"}); 199 ZmOperation.registerOp(ZmId.OP_SORT_ASC, {textKey:"sortAscending"}); 200 ZmOperation.registerOp(ZmId.OP_SORT_DESC, {textKey:"sortDescending"}); 201 ZmOperation.registerOp(ZmId.OP_SPELL_CHECK, {textKey:"spellCheck", image:"SpellCheck", tooltipKey:"spellCheckTooltip", shortcut:ZmKeyMap.SPELLCHECK, showImageInToolbar: true}, ZmSetting.SPELL_CHECK_ENABLED); 202 ZmOperation.registerOp(ZmId.OP_SYNC, {textKey:"reload", image:"Refresh", shortcut:ZmKeyMap.REFRESH}); 203 ZmOperation.registerOp(ZmId.OP_SYNC_ALL, {textKey:"checkAllFeed", image:"Refresh"}); 204 ZmOperation.registerOp(ZmId.OP_SYNC_OFFLINE_FOLDER, {textKey:"syncOfflineFolderOff", image:"Refresh"}, ZmSetting.OFFLINE_ENABLED); /* offline only */ 205 ZmOperation.registerOp(ZmId.OP_TAG, null, ZmSetting.TAGGING_ENABLED); 206 ZmOperation.registerOp(ZmId.OP_TAG_COLOR_MENU, {textKey:"tagColor", image:"TagStack"}, ZmSetting.TAGGING_ENABLED, 207 AjxCallback.simpleClosure(function(parent) { 208 ZmOperation.addDeferredMenu(ZmOperation.addColorMenu, parent); 209 })); 210 ZmOperation.registerOp(ZmId.OP_TAG_MENU, {textKey: "tag", tooltipKey:"tagTooltip", image:"Tag", showImageInToolbar: true }, ZmSetting.TAGGING_ENABLED, 211 AjxCallback.simpleClosure(function(parent) { 212 ZmOperation.addDeferredMenu(ZmOperation.addTagMenu, parent, true); 213 })); 214 // placeholder for toolbar text 215 ZmOperation.registerOp(ZmId.OP_TEXT); 216 // XXX: need new icon? - 217 // Undelete is stupid. We should either add it for all items types (not just contacts) or just kill it 218 ZmOperation.registerOp(ZmId.OP_UNDELETE, {textKey:"undelete", tooltipKey:"undelete", image:"MoveToFolder"}); 219 ZmOperation.registerOp(ZmId.OP_UNMUTE_CONV, {textKey:"unmuteConv", tooltipKey:"unmuteConvTooltip", image:"Unmute", shortcut:ZmKeyMap.MUTE_UNMUTE_CONV}); 220 ZmOperation.registerOp(ZmId.OP_VIEW, {textKey:"view", image:"SplitView"}); 221 ZmOperation.registerOp(ZmId.OP_VIEW_MENU, {tooltipKey:"viewTooltip", textKey:"view", image:"SplitPane", textPrecedence:80, showImageInToolbar: true, showTextInToolbar: true}); 222 ZmOperation.registerOp(ZmId.OP_ZIMLET, {image:"ZimbraIcon"}); 223 224 // invites - needed for both Mail and Calendar 225 ZmOperation.registerOp(ZmId.OP_ACCEPT_PROPOSAL, {textKey:"replyAccept", image:"Check"}); 226 ZmOperation.registerOp(ZmId.OP_DECLINE_PROPOSAL, {textKey:"replyDecline", image:"Cancel"}); 227 ZmOperation.registerOp(ZmId.OP_CAL_REPLY, {textKey:"reply", tooltipKey:"replyTooltip", image:"Reply", shortcut:ZmKeyMap.REPLY}); 228 ZmOperation.registerOp(ZmId.OP_CAL_REPLY_ALL, {textKey:"replyAll", tooltipKey:"replyAllTooltip", image:"ReplyAll", shortcut:ZmKeyMap.REPLY_ALL}); 229 ZmOperation.registerOp(ZmId.OP_REPLY_ACCEPT, {textKey:"replyAccept", image:"Check", showTextInToolbar: true, showImageInToolbar: true}); 230 ZmOperation.registerOp(ZmId.OP_REPLY_ACCEPT_NOTIFY, {textKey:"notifyOrganizerLabel", image:"Check"}); 231 ZmOperation.registerOp(ZmId.OP_REPLY_ACCEPT_IGNORE, {textKey:"dontNotifyOrganizerLabel", image:"Check"}); 232 ZmOperation.registerOp(ZmId.OP_REPLY_CANCEL); 233 ZmOperation.registerOp(ZmId.OP_REPLY_DECLINE, {textKey:"replyDecline", image:"Cancel", showTextInToolbar: true, showImageInToolbar: true}); 234 ZmOperation.registerOp(ZmId.OP_REPLY_DECLINE_NOTIFY, {textKey:"notifyOrganizerLabel", image:"Cancel"}); 235 ZmOperation.registerOp(ZmId.OP_REPLY_DECLINE_IGNORE, {textKey:"dontNotifyOrganizerLabel", image:"Cancel"}); 236 ZmOperation.registerOp(ZmId.OP_REPLY_MODIFY); 237 ZmOperation.registerOp(ZmId.OP_REPLY_NEW_TIME, {textKey:"replyNewTime", image:"NewTime"}); 238 ZmOperation.registerOp(ZmId.OP_REPLY_TENTATIVE, {textKey:"replyTentative", image:"QuestionMark", showTextInToolbar: true, showImageInToolbar: true}); 239 ZmOperation.registerOp(ZmId.OP_REPLY_TENTATIVE_NOTIFY, {textKey:"notifyOrganizerLabel", image:"QuestionMark"}); 240 ZmOperation.registerOp(ZmId.OP_REPLY_TENTATIVE_IGNORE, {textKey:"dontNotifyOrganizerLabel", image:"QuestionMark"}); 241 242 // Compose Options - used by Calendar and Mail 243 ZmOperation.registerOp(ZmId.OP_COMPOSE_OPTIONS, {textKey:"options", image:"Preferences"}); 244 245 ZmOperation.NEW_ORG_OPS.push(ZmOperation.NEW_FOLDER, ZmOperation.NEW_TAG); 246 ZmOperation.NEW_ORG_KEY[ZmOperation.NEW_FOLDER] = "folder"; 247 ZmOperation.NEW_ORG_KEY[ZmOperation.NEW_TAG] = "tag"; 248 }; 249 250 /** 251 * Creates operation descriptors for the given operation IDs, 252 * then creates the appropriate widget for each operation based on the type of 253 * the parent. If it's a toolbar, then buttons are created. If it's a menu, menu items are 254 * created. 255 * <p> 256 * To override or add properties to a particular operation, pass in a hash of properties and 257 * values as a value in overrides, with the operation ID as the key. 258 * </p> 259 * 260 * @param {DwtComposite} parent the containing widget (toolbar or menu) 261 * @param {Array} operations a list of operation IDs 262 * @param {Hash} overrides a hash of overrides by op ID 263 * 264 * @returns {Hash} a hash of operations by ID 265 */ 266 ZmOperation.createOperations = 267 function(parent, operations, overrides) { 268 var obj = new ZmOperation(); 269 return obj._createOperations(parent, operations, overrides); 270 } 271 272 /** 273 * Done through an object so that we can have more than one invocation going 274 * without sharing memory (eg, creating New submenu). 275 * 276 * @private 277 */ 278 ZmOperation.prototype._createOperations = 279 function(parent, operations, overrides) { 280 if (operations == ZmOperation.NONE) { 281 operations = null; 282 } 283 overrides = overrides || {}; 284 285 var opHash = {}; 286 if (operations && operations.length) { 287 for (var i = 0; i < operations.length; i++) { 288 var id = operations[i]; 289 ZmOperation.defineOperation(id, overrides[id]); 290 ZmOperation.addOperation(parent, id, opHash, null, overrides[id] && overrides[id].htmlElId); 291 } 292 } 293 294 return opHash; 295 }; 296 297 /** 298 * Creates an operation descriptor. The ID of an existing operation can be passed 299 * in to use as a base, with overridden properties passed in a hash. A new operation 300 * can be defined by passing its properties in a hash. 301 * 302 * @param {String} baseId the ID of an existing operation 303 * @param {Hash} overrides the property overrides for the operation 304 */ 305 ZmOperation.defineOperation = 306 function(baseId, overrides) { 307 var id = (overrides && overrides.id) || (baseId && baseId.id) || baseId || Dwt.getNextId(); 308 var textKey = (overrides && overrides.textKey) || ZmOperation.getProp(baseId, "textKey"); 309 var text = textKey && ZmMsg[textKey]; 310 var tooltipKey = (overrides && overrides.tooltipKey) || ZmOperation.getProp(baseId, "tooltipKey"); 311 var tooltip = tooltipKey && ZmMsg[tooltipKey]; 312 var image = ZmOperation.getProp(baseId, "image"); 313 var showImageInToolbar = ZmOperation.getProp(baseId, "showImageInToolbar"); 314 var showTextInToolbar = ZmOperation.getProp(baseId, "showTextInToolbar"); 315 var disImage = ZmOperation.getProp(baseId, "disImage"); 316 var enabled = (overrides && (overrides.enabled !== false)); 317 var style = ZmOperation.getProp(baseId, "style"); 318 var shortcut = ZmOperation.getProp(baseId, "shortcut"); 319 320 var opDesc = {id:id, text:text, image:image, showImageInToolbar:showImageInToolbar, showTextInToolbar:showTextInToolbar, disImage:disImage, enabled:enabled, 321 tooltip:tooltip, style:style, shortcut:shortcut}; 322 if (overrides) { 323 for (var i in overrides) { 324 opDesc[i] = overrides[i]; 325 } 326 } 327 328 ZmOperation._operationDesc[id] = opDesc; 329 330 return opDesc; 331 }; 332 333 /** 334 * Gets the value of a given property for a given operation. 335 * 336 * @param {String} id the operation ID 337 * @param {String} prop the name of an operation property 338 * 339 * @return {Object} the value 340 */ 341 ZmOperation.getProp = 342 function(id, prop) { 343 var value = null; 344 var setup = ZmOperation.SETUP[id]; 345 if (setup) { 346 value = setup[prop]; 347 if (!value && (prop == "disImage") && setup.image) { 348 value = setup.image; 349 } 350 } 351 352 return value; 353 }; 354 355 /** 356 * Checks if the operation is a separator or spacer. 357 * 358 * @param {String} id the id 359 * @return {Boolean} <code>true</code> if the operation is a spacer 360 */ 361 ZmOperation.isSep = 362 function(id) { 363 return (id == ZmOperation.SEP || id == ZmOperation.SPACER || id == ZmOperation.FILLER); 364 }; 365 366 /** 367 * Adds the operation. 368 * 369 * @param {DwtComposite} parent the containing widget (toolbar or menu) 370 * @param {String} id the id 371 * @param {Hash} opHash a hash 372 * @param {String} [index] the index 373 */ 374 ZmOperation.addOperation = 375 function(parent, id, opHash, index, htmlElId) { 376 377 var opDesc = ZmOperation._operationDesc[id] || ZmOperation.defineOperation(id); 378 379 if (id == ZmOperation.SEP) { 380 if (parent instanceof DwtMenu) { 381 parent.createSeparator(index); 382 } 383 else { 384 parent.addSeparator(null, index); 385 } 386 } else if (id == ZmOperation.SPACER) { // toolbar only 387 parent.addSpacer(null, index); 388 } else if (id == ZmOperation.FILLER) { // toolbar only 389 parent.addFiller(null, index); 390 } else { 391 if (index != null) { 392 opDesc.index = index; 393 } 394 opHash[id] = parent.createOp(id, opDesc, htmlElId); 395 } 396 var callback = ZmOperation.CALLBACK[id]; 397 if (callback) { 398 callback.run(opHash[id]); 399 } 400 }; 401 402 /** 403 * Adds a deferred menu. 404 * 405 * @param {function} addMenuFunc the add menu function 406 * @param {DwtComposite} parent the containing widget (toolbar or menu) 407 */ 408 ZmOperation.addDeferredMenu = 409 function(addMenuFunc, parent /* ... */) { 410 var args = [parent]; 411 for (var i = 2; i < arguments.length; i++) { 412 args.push(arguments[i]); 413 } 414 var callback = new AjxCallback(null, addMenuFunc, args); 415 parent.setMenu(callback); 416 }; 417 418 /** 419 * Removes the operation. 420 * 421 * @param {DwtComposite} parent the containing widget (toolbar or menu) 422 * @param {String} id the id 423 * @param {Hash} opHash a hash 424 */ 425 ZmOperation.removeOperation = 426 function(parent, id, opHash) { 427 var op = parent.getOp(id); 428 if (op) { 429 op.dispose(); 430 delete opHash[id]; 431 } 432 }; 433 434 /** 435 * Replaces the attributes of one operation with those of another, wholly or in part. 436 * 437 * @param {DwtComposite} parent the parent widget 438 * @param {String} oldOp the ID of operation to replace 439 * @param {String} newOp the ID of new operation to get replacement attributes from 440 * @param {String} text the new text (overrides text of newOp) 441 * @param {String} image the new image (overrides image of newOp) 442 * @param {String} disImage the new disabled image (overrides that of newOp) 443 */ 444 ZmOperation.setOperation = 445 function(parent, oldOp, newOp, text, image, disImage) { 446 var op = parent.getOp(oldOp); 447 if (!op) return; 448 449 op.setText(text ? text : ZmMsg[ZmOperation.getProp(newOp, "textKey")]); 450 op.setImage(image ? image : ZmOperation.getProp(newOp, "image")); 451 }; 452 453 /** 454 * Takes a list of operations and removes any who have a corresponding setting that's 455 * not set. Also deals with the fact that you don't want a separator or a spacer unless 456 * there's stuff on either side of it. 457 * 458 * @param {Array} list a list of {ZmOperation} objects 459 * @return {Array} a list of {ZmOperation} objects 460 */ 461 ZmOperation.filterOperations = 462 function(list) { 463 var newList = []; 464 if (!(list && list.length)) { return newList; } 465 466 // remove disabled operations 467 for (var i = 0; i < list.length; i++) { 468 var op = list[i]; 469 if (!op) { 470 continue; 471 } 472 if (appCtxt.checkPrecondition(ZmOperation.PRECONDITION[op])) { 473 newList.push(op); 474 } 475 } 476 // reduce multiple consecutive separators to the first one 477 var newList1 = []; 478 var gotSep = false; 479 for (var i = 0; i < newList.length; i++) { 480 var op = newList[i]; 481 if (op == ZmOperation.SEP || op == ZmOperation.SPACER) { 482 if (!gotSep) { 483 newList1.push(op); 484 } 485 gotSep = true; 486 } else { 487 newList1.push(op); 488 gotSep = false; 489 } 490 } 491 // remove separator at beginning or end 492 if (newList1 && newList1.length) { 493 if (newList1[0] == ZmOperation.SEP || newList1[0] == ZmOperation.SPACER) { 494 newList1.shift(); 495 } 496 var i = newList1.length - 1; 497 if (newList1[i] == ZmOperation.SEP || newList1[i] == ZmOperation.SPACER || newList1[i] == ZmOperation.FILLER) { 498 newList1.pop(); 499 } 500 } 501 502 return newList1; 503 }; 504 505 /** 506 * Adds a "New" submenu. Overrides are used because we don't want "New" at the 507 * beginning of each label. 508 * 509 * @param {DwtComposite} parent the parent widget 510 * @return {ZmActionMenu} the menu 511 */ 512 ZmOperation.addNewMenu = 513 function(parent) { 514 var list = ZmOperation.NEW_ITEM_OPS; 515 list.push(ZmOperation.SEP); 516 list = list.concat(ZmOperation.NEW_ORG_OPS); 517 518 var overrides = {}; 519 for (var i = 0; i < list.length; i++) { 520 var op = list[i]; 521 var htmlElId = parent._htmlElId + "_" + op; 522 overrides[op] = {htmlElId: htmlElId}; 523 var textKey = ZmOperation.NEW_ITEM_KEY[op] || ZmOperation.NEW_ORG_KEY[op]; 524 if (textKey) { 525 overrides[op].textKey = textKey; 526 } 527 } 528 529 var menu = new ZmActionMenu({parent:parent, menuItems:list, overrides:overrides}); 530 parent.setMenu(menu); 531 532 return menu; 533 }; 534 535 /** 536 * Adds a "Search" submenu for searching from/to sender/recipient. 537 * 538 * @param {DwtComposite} parent parent widget (a toolbar or action menu) 539 * @return {ZmActionMenu} the menu 540 */ 541 ZmOperation.addSearchMenu = 542 function(parent) { 543 var list = [ZmOperation.SEARCH, ZmOperation.SEARCH_TO]; 544 545 var menu = new ZmActionMenu({parent:parent, menuItems:list}); 546 parent.setMenu(menu); 547 548 return menu; 549 }; 550 551 /** 552 * Adds a contact group menu for creating a contacts from the contact list 553 * @param {DwtComposite} parent parent widget (a toolbar or action menu) 554 * @return {ZmActionMenu) the menu 555 */ 556 ZmOperation.addContactGroupMenu = 557 function(parent) { 558 var contactGroupMenu = new ZmContactGroupMenu(parent); 559 parent.setMenu(contactGroupMenu); 560 return contactGroupMenu; 561 }; 562 563 /** 564 * Adds a "Tag" submenu for tagging items. 565 * 566 * @param {DwtComposite} parent parent widget (a toolbar or action menu) 567 * @return {ZmTagMenu} the menu 568 */ 569 ZmOperation.addTagMenu = 570 function(parent) { 571 var tagMenu = new ZmTagMenu(parent); 572 parent.setMenu(tagMenu); 573 return tagMenu; 574 }; 575 576 577 /** 578 * Adds a "Move" submenu for tagging items. 579 * 580 * @param {DwtComposite} parent parent widget (a toolbar or action menu) 581 * @return {ZmTagMenu} the menu 582 */ 583 ZmOperation.addMoveMenu = 584 function(parent) { 585 var moveMenu = new DwtMenu(parent); //this is a dummy menu just so the drop-down would appear 586 parent.setMenu(moveMenu); 587 return moveMenu; 588 }; 589 590 /** 591 * Adds a color submenu for choosing tag color. 592 * 593 * @param {DwtComposite} parent parent widget (a toolbar or action menu) 594 * @param {boolean} hideNoFill True to hide the no-fill/use-default option. 595 * @return {ZmPopupMenu} the menu 596 */ 597 ZmOperation.addColorMenu = 598 function(parent, hideNoFill) { 599 var menu = new ZmColorMenu({parent:parent,image:"Tag",hideNone:true,hideNoFill:hideNoFill}); 600 parent.setMenu(menu); 601 return menu; 602 }; 603 604 /** 605 * Gets the tooltip for the operation with the given ID. If the operation has a shortcut associated 606 * with it, a shortcut hint is appended to the end of the tooltip. 607 * 608 * @param {String} id the operation ID 609 * @param {String} keyMap the key map (for resolving shortcut) 610 * @param {String} tooltip the tooltip override 611 * @return {String} the tool tip 612 */ 613 ZmOperation.getToolTip = 614 function(id, keyMap, tooltip) { 615 var opDesc = ZmOperation._operationDesc[id] || ZmOperation.defineOperation(id); 616 tooltip = tooltip || opDesc.tooltip; 617 var sc = tooltip && opDesc.shortcut && appCtxt.getShortcutHint(keyMap, opDesc.shortcut); 618 return sc ? [tooltip, sc].join("") : tooltip; 619 }; 620