forked from phcode-dev/staging.phcode.dev
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathKeyBindingManager.js
More file actions
1 lines (1 loc) · 33 KB
/
KeyBindingManager.js
File metadata and controls
1 lines (1 loc) · 33 KB
1
define(function(require,exports,module){require("utils/Global");let AppInit=require("utils/AppInit"),Commands=require("command/Commands"),CommandManager=require("command/CommandManager"),DefaultDialogs=require("widgets/DefaultDialogs"),EventDispatcher=require("utils/EventDispatcher"),FileSystem=require("filesystem/FileSystem"),FileSystemError=require("filesystem/FileSystemError"),FileUtils=require("file/FileUtils"),KeyEvent=require("utils/KeyEvent"),Strings=require("strings"),Keys=require("command/Keys"),KeyboardOverlayMode=require("command/KeyboardOverlayMode"),StringUtils=require("utils/StringUtils"),Metrics=require("utils/Metrics"),Dialogs=require("widgets/Dialogs"),Mustache=require("thirdparty/mustache/mustache"),UrlParams=require("utils/UrlParams").UrlParams,_=require("thirdparty/lodash"),KeyboardPrefs=JSON.parse(require("text!base-config/keyboard.json")),KeyboardDialogTemplate=require("text!./ChangeShortcutTemplate.html"),KEYMAP_FILENAME="keymap.json",_userKeyMapFilePath=path.normalize(brackets.app.getApplicationSupportDirectory()+"/"+KEYMAP_FILENAME);const EVENT_KEY_BINDING_ADDED="keyBindingAdded",EVENT_KEY_BINDING_REMOVED="keyBindingRemoved",EVENT_NEW_PRESET="newPreset",EVENT_PRESET_CHANGED="presetChanged",KEY=Keys.KEY,knownBindableCommands=new Set;let _loadUserKeyMap=_.debounce(_loadUserKeyMapImmediate,200),PreferencesManager,_customKeymapIDInUse;const _registeredCustomKeyMaps={},STATE_CUSTOM_KEY_MAP_ID="customKeyMapID",PREF_TRIPLE_CTRL_KEY_PRESS_ENABLED="tripleCtrlPalette";let _keyMap={},_defaultKeyMap={},_originalUserKeyMap={},_customKeyMap={},_customKeyMapCache={},_commandMap={},_allCommands=[],_displayKeyMap={up:"↑",down:"↓",left:"←",right:"→","-":"−"},_specialCommands=[Commands.EDIT_UNDO,Commands.EDIT_REDO,Commands.EDIT_SELECT_ALL,Commands.EDIT_CUT,Commands.EDIT_COPY,Commands.EDIT_PASTE],_reservedShortcuts=["Ctrl-Z","Ctrl-Y","Ctrl-A","Ctrl-X","Ctrl-C","Ctrl-V","Ctrl-=","Ctrl--"],_macReservedShortcuts=["Cmd-,","Cmd-H","Cmd-Alt-H","Cmd-M","Cmd-Shift-Z","Cmd-Q","Cmd-=","Cmd--"],_keyNames=["Up","Down","Left","Right","Backspace","Enter","Space","Tab","PageUp","PageDown","Home","End","Insert","Delete"],_showErrors=!0,_enabled=!0,_globalKeydownHooks=[],CtrlDownStates={NOT_YET_DETECTED:0,DETECTED:1,DETECTED_AND_IGNORED:2},_ctrlDown=CtrlDownStates.NOT_YET_DETECTED,_altGrDown=!1,_lastTimeStamp,_lastKeyIdentifier,MAX_INTERVAL_FOR_CTRL_ALT_KEYS=30,_onCtrlUp;function _quitAltGrMode(){_enabled=!0,_ctrlDown=CtrlDownStates.NOT_YET_DETECTED,_altGrDown=!1,_lastTimeStamp=null,_lastKeyIdentifier=null,$(window).off("keyup",_onCtrlUp)}function _detectAltGrKeyDown(e){"win"===brackets.platform&&(_altGrDown?"Control"!==e.key&&"Alt"!==e.key||(e.altKey&&e.ctrlKey&&e.key===_lastKeyIdentifier?_quitAltGrMode():_lastKeyIdentifier=e.key):(_ctrlDown!==CtrlDownStates.DETECTED_AND_IGNORED&&e.ctrlKey&&"Control"===e.key?_ctrlDown=CtrlDownStates.DETECTED:e.repeat&&e.ctrlKey&&"Control"===e.key?_ctrlDown=CtrlDownStates.DETECTED_AND_IGNORED:_ctrlDown===CtrlDownStates.DETECTED&&e.altKey&&e.ctrlKey&&"Alt"===e.key&&e.timeStamp-_lastTimeStamp<MAX_INTERVAL_FOR_CTRL_ALT_KEYS?(_altGrDown=!0,_lastKeyIdentifier="Alt",_enabled=!1,$(window).on("keyup",_onCtrlUp)):_ctrlDown=CtrlDownStates.NOT_YET_DETECTED,_lastTimeStamp=e.timeStamp))}function _reset(){_keyMap={},_defaultKeyMap={},_customKeyMap={},_customKeyMapCache={},_commandMap={},_globalKeydownHooks=[],_userKeyMapFilePath=path.normalize(brackets.app.getApplicationSupportDirectory()+"/"+KEYMAP_FILENAME)}function _buildKeyDescriptor(hasMacCtrl,hasCtrl,hasAlt,hasShift,key){if(!key)return console.log("KeyBindingManager _buildKeyDescriptor() - No key provided!"),"";let keyDescriptor=[];return hasMacCtrl&&keyDescriptor.push("Ctrl"),hasAlt&&keyDescriptor.push("Alt"),hasShift&&keyDescriptor.push("Shift"),hasCtrl&&("mac"===brackets.platform?keyDescriptor.push("Cmd"):keyDescriptor.unshift("Ctrl")),keyDescriptor.push(key),keyDescriptor.join("-")}function normalizeKeyDescriptorString(origDescriptor){let hasMacCtrl=!1,hasCtrl=!1,hasAlt=!1,hasShift=!1,key="",error=!1;function _compareModifierString(left,right){return!(!left||!right)&&(left=left.trim().toLowerCase(),right=right.trim().toLowerCase(),left.length>0&&left===right)}return origDescriptor.split("-").forEach(function parseDescriptor(ele,i,arr){_compareModifierString("ctrl",ele)?"mac"===brackets.platform?hasMacCtrl=!0:hasCtrl=!0:_compareModifierString("cmd",ele)?"mac"===brackets.platform?hasCtrl=!0:error=!0:_compareModifierString("alt",ele)?hasAlt=!0:_compareModifierString("opt",ele)?"mac"===brackets.platform?hasAlt=!0:error=!0:_compareModifierString("shift",ele)?hasShift=!0:key.length>0?(console.log("KeyBindingManager normalizeKeyDescriptorString() - Multiple keys defined. Using key: "+key+" from: "+origDescriptor),error=!0):key=ele}),error?null:(""===key&&-1!==origDescriptor.search(/^.+--$/)&&(key="-"),""===key&&"shift-shift"===origDescriptor.toLowerCase()&&(key="Shift"),key.indexOf("+")>=0&&key.length>1?null:(/^[a-z]/i.test(key)&&(key=_.capitalize(key.toLowerCase())),/^Page/.test(key)&&(key=key.replace(/(up|down)$/,function(match,p1){return _.capitalize(p1)})),key.length>1&&!/F\d+/.test(key)&&-1===_keyNames.indexOf(key)?null:_buildKeyDescriptor(hasMacCtrl,hasCtrl,hasAlt,hasShift,key)))}function _mapKeycodeToKeyLegacy(keycode){if(keycode>=KeyEvent.DOM_VK_0&&keycode<=KeyEvent.DOM_VK_9||keycode>=KeyEvent.DOM_VK_A&&keycode<=KeyEvent.DOM_VK_Z)return String.fromCharCode(keycode);if(keycode>=KeyEvent.DOM_VK_NUMPAD0&&keycode<=KeyEvent.DOM_VK_NUMPAD9)return String.fromCharCode(keycode-KeyEvent.DOM_VK_NUMPAD0+KeyEvent.DOM_VK_0);switch(keycode){case KeyEvent.DOM_VK_SEMICOLON:return";";case KeyEvent.DOM_VK_EQUALS:return"=";case KeyEvent.DOM_VK_COMMA:return",";case KeyEvent.DOM_VK_SUBTRACT:case KeyEvent.DOM_VK_DASH:return"-";case KeyEvent.DOM_VK_ADD:return"+";case KeyEvent.DOM_VK_DECIMAL:case KeyEvent.DOM_VK_PERIOD:return".";case KeyEvent.DOM_VK_DIVIDE:case KeyEvent.DOM_VK_SLASH:return"/";case KeyEvent.DOM_VK_BACK_QUOTE:return"`";case KeyEvent.DOM_VK_OPEN_BRACKET:return"[";case KeyEvent.DOM_VK_BACK_SLASH:return"\\";case KeyEvent.DOM_VK_CLOSE_BRACKET:return"]";case KeyEvent.DOM_VK_QUOTE:return"'";default:return null}}function _mapKeycodeToKey(event){if((event.ctrlKey||event.metaKey)&&event.altKey&&"mac"===brackets.platform){const key=_mapKeycodeToKeyLegacy(event.keyCode);if(key)return key}const key=event.key;let codes={ArrowUp:"Up",ArrowDown:"Down",ArrowLeft:"Left",ArrowRight:"Right"," ":"Space"};return codes[key]?codes[key]:key}function _translateKeyboardEvent(event){let hasMacCtrl,hasCtrl,hasAlt,hasShift,key;return normalizeKeyDescriptorString(_buildKeyDescriptor("mac"===brackets.platform&&event.ctrlKey,"mac"!==brackets.platform?event.ctrlKey:event.metaKey,event.altKey,event.shiftKey,_mapKeycodeToKey(event)))}function formatKeyDescriptor(descriptor){let displayStr;return displayStr=(displayStr=(displayStr=(displayStr=(displayStr=(displayStr=(displayStr=(displayStr="mac"===brackets.platform?(displayStr=(displayStr=(displayStr=(displayStr=descriptor.replace(/-(?!$)/g,"")).replace("Ctrl","⌃")).replace("Cmd","⌘")).replace("Shift","⇧")).replace("Alt","⌥"):(displayStr=(displayStr=descriptor.replace("Ctrl",Strings.KEYBOARD_CTRL)).replace("Shift",Strings.KEYBOARD_SHIFT)).replace(/-(?!$)/g,"+")).replace("Space",Strings.KEYBOARD_SPACE)).replace("PageUp",Strings.KEYBOARD_PAGE_UP)).replace("PageDown",Strings.KEYBOARD_PAGE_DOWN)).replace("Home",Strings.KEYBOARD_HOME)).replace("End",Strings.KEYBOARD_END)).replace("Ins",Strings.KEYBOARD_INSERT)).replace("Del",Strings.KEYBOARD_DELETE)}function _isKeyAssigned(key){return void 0!==_keyMap[key]}function removeBinding(key,platform){if(!key||null!=platform&&platform!==brackets.platform)return;let normalizedKey=normalizeKeyDescriptorString(key);if(normalizedKey){if(_isKeyAssigned(normalizedKey)){let binding=_keyMap[normalizedKey],command=CommandManager.get(binding.commandID),bindings=_commandMap[binding.commandID];delete _keyMap[normalizedKey],bindings&&(_commandMap[binding.commandID]=bindings.filter(function(b){return b.key!==normalizedKey}),command&&(command.trigger(EVENT_KEY_BINDING_REMOVED,{key:normalizedKey,displayKey:binding.displayKey}),exports.trigger(EVENT_KEY_BINDING_REMOVED,{commandID:command.getID(),key:normalizedKey,displayKey:binding.displayKey})))}}else console.log("Failed to normalize "+key)}function _updateCommandAndKeyMaps(newBinding){0!==_allCommands.length&&newBinding&&newBinding.commandID&&-1===_allCommands.indexOf(newBinding.commandID)&&(_defaultKeyMap[newBinding.commandID]=_.cloneDeep(newBinding),_loadUserKeyMap())}function _addBinding(commandID,keyBinding,{platform:platform,userBindings:userBindings,isMenuShortcut:isMenuShortcut}){knownBindableCommands.add(commandID);let key,result=null,normalized,normalizedDisplay,explicitPlatform=keyBinding.platform||platform,explicitBrowserOnly=keyBinding.browserOnly,explicitNativeOnly=keyBinding.nativeOnly,targetPlatform,command,bindingsToDelete=[],existing;if(Phoenix.isNativeApp&&explicitBrowserOnly)return null;if(!Phoenix.isNativeApp&&explicitNativeOnly)return null;if(targetPlatform=explicitPlatform&&"all"!==explicitPlatform?explicitPlatform:brackets.platform,"mac"===explicitPlatform&&"mac"!==brackets.platform)return null;if(key=keyBinding.key||keyBinding,"mac"!==brackets.platform||void 0!==explicitPlatform&&"all"!==explicitPlatform||(key=key.replace("Ctrl","Cmd"),void 0!==keyBinding.displayKey&&(keyBinding.displayKey=keyBinding.displayKey.replace("Ctrl","Cmd"))),!(normalized=normalizeKeyDescriptorString(key)))return console.error(`Unable to parse key binding '${key}' for command '${commandID}'. Permitted modifiers: Ctrl, Cmd, Alt, Opt, Shift; separated by '-' (not '+').`),null;function isSingleCharAZ(str){return/^[A-Z]$/i.test(str)}const keySplit=normalized.split("-");if(!isMenuShortcut&&(2===keySplit.length&&"Alt"===keySplit[0]&&isSingleCharAZ(keySplit[1])||3===keySplit.length&&"Alt"===keySplit[0]&&"Shift"===keySplit[1]&&isSingleCharAZ(keySplit[2]))&&console.error(`Key binding '${normalized}' for command '${commandID}' may cause issues. The key combinations starting with 'Alt-<letter>' and 'Alt-Shift-<letter>' are reserved. On macOS, they are used for AltGr internationalization, and on Windows/Linux, they are used for menu navigation shortcuts. If this is a menu shortcut, use 'isMenuShortcut' option.`),existing=_keyMap[normalized],exports.useWindowsCompatibleBindings&&"win"===explicitPlatform){if(existing&&(!existing.explicitPlatform||existing.explicitPlatform===brackets.platform||"all"===existing.explicitPlatform))return null;targetPlatform=brackets.platform}if(targetPlatform!==brackets.platform)return null;let existingBindings,isWindowsCompatible,isReplaceGeneric,ignoreGeneric;return existing&&!existing.explicitPlatform&&explicitPlatform&&(removeBinding(normalized),existing=!1),(_commandMap[commandID]||[]).forEach(function(binding){isWindowsCompatible=exports.useWindowsCompatibleBindings&&"win"===binding.explicitPlatform,isReplaceGeneric=!binding.explicitPlatform&&explicitPlatform,isWindowsCompatible||isReplaceGeneric?bindingsToDelete.push(binding):ignoreGeneric=binding.explicitPlatform&&!explicitPlatform}),ignoreGeneric?null:existing?(commandID!==_keyMap[normalized].commandID&&console.error("Cannot assign "+normalized+" to "+commandID+". It is already assigned to "+_keyMap[normalized].commandID),null):(bindingsToDelete.forEach(function(binding){removeBinding(binding.key)}),normalizedDisplay=keyBinding.displayKey?normalizeKeyDescriptorString(keyBinding.displayKey):normalized,_commandMap[commandID]||(_commandMap[commandID]=[]),result={key:normalized,displayKey:normalizedDisplay,explicitPlatform:explicitPlatform},_commandMap[commandID].push(result),_keyMap[normalized]={commandID:commandID,key:normalized,displayKey:normalizedDisplay,explicitPlatform:explicitPlatform},userBindings||_updateCommandAndKeyMaps(_keyMap[normalized]),(command=CommandManager.get(commandID))&&(command.trigger(EVENT_KEY_BINDING_ADDED,result,commandID),exports.trigger(EVENT_KEY_BINDING_ADDED,result,commandID)),result)}function getKeymap(defaults){return $.extend({},defaults?_defaultKeyMap:_keyMap)}function _makeMapFromArray(map,arr){for(let item of arr)map[item]=!0;return map}_onCtrlUp=function(e){let key=e.keyCode||e.which;_altGrDown&&key===KeyEvent.DOM_VK_CONTROL&&_quitAltGrMode()};const UN_SWALLOWED_EVENTS=_makeMapFromArray({},[Commands.EDIT_SELECT_ALL,Commands.EDIT_UNDO,Commands.EDIT_REDO,Commands.EDIT_CUT,Commands.EDIT_COPY,Commands.EDIT_PASTE]);let UN_SWALLOWED_KEYS=_makeMapFromArray({},_keyNames.concat(_reservedShortcuts).concat(_macReservedShortcuts));function _isUnSwallowedKeys(key){return UN_SWALLOWED_KEYS[key]||1===key.length}function _handleKey(key){if(_enabled&&_keyMap[key]){Metrics.countEvent(Metrics.EVENT_TYPE.KEYBOARD,"shortcut",key),Metrics.countEvent(Metrics.EVENT_TYPE.KEYBOARD,"command",_keyMap[key].commandID),logger.leaveTrail("Keyboard shortcut: "+key+" command: "+_keyMap[key].commandID);let command,eventDetails=void 0;CommandManager.get(_keyMap[key].commandID)._options.eventSource&&(eventDetails={eventSource:CommandManager.SOURCE_KEYBOARD_SHORTCUT,sourceType:key});let promise=CommandManager.execute(_keyMap[key].commandID,eventDetails);return!UN_SWALLOWED_EVENTS[_keyMap[key].commandID]&&!_isUnSwallowedKeys(key)||"rejected"!==promise.state()}return!1}function _sortByPlatform(a,b){let a1=a.platform?1:0,b1;return(b.platform?1:0)-a1}function addBinding(command,keyBindings,platform,options={}){let commandID="",results,isMenuShortcut=options.isMenuShortcut;if(command){if(keyBindings){if(commandID="string"==typeof command?command:command.getID(),Array.isArray(keyBindings)){let keyBinding;results=[],keyBindings.sort(_sortByPlatform),keyBindings.forEach(function(keyBindingRequest){(keyBinding=_addBinding(commandID,keyBindingRequest,{platform:keyBindingRequest.platform,isMenuShortcut:isMenuShortcut}))&&results.push(keyBinding)})}else results=_addBinding(commandID,keyBindings,{platform:platform,isMenuShortcut:isMenuShortcut});return results}}else console.error("addBinding(): missing required parameter: command")}function getKeyBindings(command){let bindings=[],commandID="";return command?(commandID="string"==typeof command?command:command.getID(),(bindings=_commandMap[commandID])||[]):(console.error("getKeyBindings(): missing required parameter: command"),[])}function getKeyBindingsDisplay(commandID){let shortCut=getKeyBindings(commandID);return shortCut&&shortCut[0]&&shortCut[0].displayKey?formatKeyDescriptor(shortCut[0].displayKey):null}const _handledCommands={};function _handleCommandRegistered(event,command){let commandId=command.getID(),defaults=KeyboardPrefs[commandId];defaults&&(_handledCommands[commandId]=!0,addBinding(commandId,defaults))}function _initDefaultShortcuts(){for(let commandId of _allCommands){let defaults=KeyboardPrefs[commandId];defaults&&!_handledCommands[commandId]&&addBinding(commandId,defaults)}}function addGlobalKeydownHook(hook){let index;-1===_globalKeydownHooks.indexOf(hook)&&_globalKeydownHooks.push(hook)}function removeGlobalKeydownHook(hook){let index=_globalKeydownHooks.indexOf(hook);-1!==index&&_globalKeydownHooks.splice(index,1)}let lastCtrlKeyPressTime=0,pressCount=0;const doublePressInterval=250,PRESS_ACTIVATE_COUNT=3,ctrlKeyCodes={ControlLeft:!0,ControlRight:!0,MetaLeft:!0,MetaRight:!0,Control:!0,Meta:!0};let isCtrlDepressed=!1;function _detectTripleCtrlKeyPress(event){const isCtrlKeyPressStart=!isCtrlDepressed;if(ctrlKeyCodes[event.code]&&ctrlKeyCodes[event.key]&&(isCtrlDepressed=!0),PreferencesManager&&!PreferencesManager.get(PREF_TRIPLE_CTRL_KEY_PRESS_ENABLED))return!1;const currentTime=(new Date).getTime();if(ctrlKeyCodes[event.code]&&ctrlKeyCodes[event.key]&&!event.shiftKey&&!event.altKey&&isCtrlKeyPressStart){if(isCtrlDepressed=!0,++pressCount===PRESS_ACTIVATE_COUNT&¤tTime-lastCtrlKeyPressTime<=doublePressInterval)return KeyboardOverlayMode.startOverlayMode(),event.stopPropagation(),event.preventDefault(),lastCtrlKeyPressTime=currentTime,Metrics.countEvent(Metrics.EVENT_TYPE.KEYBOARD,"ctrlx"+PRESS_ACTIVATE_COUNT,"showOverlay"),!0;currentTime-lastCtrlKeyPressTime>doublePressInterval&&(pressCount=1),lastCtrlKeyPressTime=currentTime}else pressCount=0;return!1}const dontHideMouseOnKeys={Escape:!0,ArrowLeft:!0,ArrowRight:!0,ArrowUp:!0,ArrowDown:!0,Home:!0,End:!0,PageUp:!0,PageDown:!0,Shift:!0,Control:!0,Alt:!0,Meta:!0,F1:!0,F2:!0,F3:!0,F4:!0,F5:!0,F6:!0,F7:!0,F8:!0,F9:!0,F10:!0,F11:!0,F12:!0,Insert:!0,ContextMenu:!0,NumLock:!0,ScrollLock:!0,CapsLock:!0};let mouseCursorHidden=!1;function _hideMouseCursonOnTyping(event){dontHideMouseOnKeys[event.key]||mouseCursorHidden||(mouseCursorHidden=!0,Phoenix.isSpecRunnerWindow||document.body.classList.add("hide-cursor"))}function _handleKeyEvent(event){if(_hideMouseCursonOnTyping(event),KeyboardOverlayMode.isInOverlayMode())return KeyboardOverlayMode.processOverlayKeyboardEvent(event);if(_detectTripleCtrlKeyPress(event))return!0;const shortcut=_translateKeyboardEvent(event);let i,handled=!1;for(i=_globalKeydownHooks.length-1;i>=0;i--)if(_globalKeydownHooks[i](event,shortcut)){handled=!0;break}if(_detectAltGrKeyDown(event),keyboardShortcutCaptureInProgress)return updateShortcutSelection(event,shortcut);!handled&&_handleKey(shortcut)&&(event.stopPropagation(),event.preventDefault())}function _showErrorsAndOpenKeyMap(err,message){require(["widgets/Dialogs"],function(Dialogs){let errorMessage=Strings.ERROR_KEYMAP_CORRUPT;err===FileSystemError.UNSUPPORTED_ENCODING?errorMessage=Strings.ERROR_LOADING_KEYMAP:message&&(errorMessage=message),Dialogs.showModalDialog(DefaultDialogs.DIALOG_ID_ERROR,Strings.ERROR_KEYMAP_TITLE,errorMessage).done(function(){err!==FileSystemError.UNSUPPORTED_ENCODING&&CommandManager.execute(Commands.FILE_OPEN_KEYMAP)})})}function _isSpecialCommand(commandID){return"mac"===brackets.platform&&"file.quit"===commandID||_specialCommands.indexOf(commandID)>-1}function _isReservedShortcuts(normalizedKey){return!!normalizedKey&&(_reservedShortcuts.indexOf(normalizedKey)>-1||_reservedShortcuts.indexOf(normalizedKey.replace("Cmd","Ctrl"))>-1||"mac"===brackets.platform&&_macReservedShortcuts.indexOf(normalizedKey)>-1)}function _getBulletList(list){let message="<ul class='dialog-list'>";return list.forEach(function(info){message+="<li>"+info+"</li>"}),message+="</ul>"}function _getDisplayKey(key){let displayKey="",match=key?key.match(/(Up|Down|Left|Right|\-)$/i):null;return match&&!/Page(Up|Down)/.test(key)&&(displayKey=key.substr(0,match.index)+_displayKeyMap[match[0].toLowerCase()]),displayKey}function _applyUserKeyBindings(){let remappedCommands=[],remappedKeys=[],restrictedCommands=[],restrictedKeys=[],invalidKeys=[],invalidCommands=[],multipleKeys=[],duplicateBindings=[],errorMessage="";_.forEach(_customKeyMap,function(commandID,key){let normalizedKey=normalizeKeyDescriptorString(key),existingBindings=_commandMap[commandID]||[];if(_isSpecialCommand(commandID))restrictedCommands.push(commandID);else if(_isReservedShortcuts(normalizedKey))restrictedKeys.push(key);else if(normalizedKey){if(_isKeyAssigned(normalizedKey)){if(-1!==remappedKeys.indexOf(normalizedKey))return void duplicateBindings.push(key);if(_keyMap[normalizedKey].commandID===commandID)return void remappedCommands.push(commandID);removeBinding(normalizedKey)}if(-1===remappedKeys.indexOf(normalizedKey)&&remappedKeys.push(normalizedKey),existingBindings.length&&existingBindings.forEach(function(binding){removeBinding(binding.key)}),commandID)if(-1!==_allCommands.indexOf(commandID))if(-1===remappedCommands.indexOf(commandID)){let keybinding={key:normalizedKey};keybinding.displayKey=_getDisplayKey(normalizedKey),_addBinding(commandID,keybinding.displayKey?keybinding:normalizedKey,{platform:brackets.platform,userBindings:!0}),remappedCommands.push(commandID)}else multipleKeys.push(commandID);else invalidCommands.push(commandID)}else invalidKeys.push(key)}),restrictedCommands.length&&(errorMessage=StringUtils.format(Strings.ERROR_RESTRICTED_COMMANDS,_getBulletList(restrictedCommands))),restrictedKeys.length&&(errorMessage+=StringUtils.format(Strings.ERROR_RESTRICTED_SHORTCUTS,_getBulletList(restrictedKeys))),multipleKeys.length&&(errorMessage+=StringUtils.format(Strings.ERROR_MULTIPLE_SHORTCUTS,_getBulletList(multipleKeys))),duplicateBindings.length&&(errorMessage+=StringUtils.format(Strings.ERROR_DUPLICATE_SHORTCUTS,_getBulletList(duplicateBindings))),invalidKeys.length&&(errorMessage+=StringUtils.format(Strings.ERROR_INVALID_SHORTCUTS,_getBulletList(invalidKeys))),invalidCommands.length&&(errorMessage+=StringUtils.format(Strings.ERROR_NONEXISTENT_COMMANDS,_getBulletList(invalidCommands))),_showErrors&&errorMessage&&_showErrorsAndOpenKeyMap("",errorMessage)}function _undoPriorUserKeyBindings(){_.forEach(_customKeyMapCache,function(commandID,key){let normalizedKey=normalizeKeyDescriptorString(key),defaults=_.find(_.toArray(_defaultKeyMap),{commandID:commandID}),defaultCommand=_defaultKeyMap[normalizedKey];_isSpecialCommand(commandID)||_isReservedShortcuts(normalizedKey)||(_isKeyAssigned(normalizedKey)&&_customKeyMap[key]!==commandID&&_customKeyMap[normalizedKey]!==commandID&&removeBinding(normalizedKey),defaults&&addBinding(commandID,defaults,brackets.platform),defaultCommand&&defaultCommand.key&&addBinding(defaultCommand.commandID,defaultCommand.key,brackets.platform))})}function _getUserKeyMapFilePath(){return window.isBracketsTestWindow?path.normalize(brackets.app.getApplicationSupportDirectory()+"/_test_/"+KEYMAP_FILENAME):_userKeyMapFilePath}async function _addToUserKeymapFile(shortcut,commandID){if(shortcut instanceof Array&&commandID)return void console.error("Shortcut arrays can be specified only if the command id is null",shortcut,commandID);let file=FileSystem.getFileForPath(_getUserKeyMapFilePath()),userKeyMap={overrides:{}},keyMapExists;if(await Phoenix.VFS.existsAsync(file.fullPath)){const text=await deferredToPromise(FileUtils.readAsText(file,!0));try{if(text){const overrides=(userKeyMap=JSON.parse(text)).overrides||{};for(let shortcutKey of Object.keys(overrides))commandID&&overrides[shortcutKey]===commandID&&delete overrides[shortcutKey]}}catch(err){return void console.error("Error reading ",_getUserKeyMapFilePath(),err)}}if(shortcut instanceof Array)for(let shortcutKey of shortcut)_isReservedShortcuts(shortcutKey)||(userKeyMap.overrides[shortcutKey]=commandID);else _isReservedShortcuts(shortcut)||(userKeyMap.overrides[shortcut]=commandID);const textContent=JSON.stringify(userKeyMap,null,4);await deferredToPromise(FileUtils.writeText(file,textContent,!0)),_loadUserKeyMap()}function _readUserKeyMap(){let file=FileSystem.getFileForPath(_getUserKeyMapFilePath()),result=new $.Deferred;return file.exists(function(err,doesExist){doesExist?FileUtils.readAsText(file,!0).done(function(text){let keyMap={};try{if(text){let json=JSON.parse(text);result.resolve(json&&json.overrides||keyMap)}else result.resolve(keyMap)}catch(err){result.reject(err)}}).fail(function(err){result.reject(err)}):result.resolve({})}),result.promise()}function registerCustomKeymapPack(packID,packName,keyMap){_registeredCustomKeyMaps[packID]?console.error(`registerCustomKeymapPack: ${packID} with name ${packName} is already registered. Ignoring`):(console.log("registering custom keymap pack",packID,packName),_registeredCustomKeyMaps[packID]={packageName:packName,keyMap:_getNormalisedKeyMap(keyMap)},_customKeymapIDInUse===packID&&_loadUserKeyMap(),exports.trigger(EVENT_NEW_PRESET,packID))}function getAllCustomKeymapPacks(){const packDetails=[];for(let packID of Object.keys(_registeredCustomKeyMaps))packDetails.push({packID:packID,packageName:_registeredCustomKeyMaps[packID].packageName,keyMap:structuredClone(_registeredCustomKeyMaps[packID].keyMap)});return packDetails}function getCurrentCustomKeymapPack(){return _registeredCustomKeyMaps[_customKeymapIDInUse]}function _getCustomShortcutOrigin(shortcut){return shortcut=normalizeKeyDescriptorString(shortcut),_originalUserKeyMap.hasOwnProperty(shortcut)?Strings.KEYBOARD_SHORTCUT_SRC_USER:_customKeyMap.hasOwnProperty(shortcut)&&_customKeymapIDInUse&&_registeredCustomKeyMaps[_customKeymapIDInUse]?StringUtils.format(Strings.KEYBOARD_SHORTCUT_SRC_PRESET,_registeredCustomKeyMaps[_customKeymapIDInUse].packageName):null}function _setCurrentCustomKeymapPack(packID){if(!PreferencesManager)throw new Error("setCurrentCustomKeymapPack should be called only after appinit event.");PreferencesManager.stateManager.set(STATE_CUSTOM_KEY_MAP_ID,packID)}function _mixCustomKeyMaps(userKeyMap){if(!_customKeymapIDInUse||!_registeredCustomKeyMaps[_customKeymapIDInUse])return userKeyMap;Metrics.countEvent(Metrics.EVENT_TYPE.KEYBOARD,"preset",_customKeymapIDInUse);const customKeyMap=_registeredCustomKeyMaps[_customKeymapIDInUse].keyMap,userDefinedKeys=Object.keys(userKeyMap),userDefinedCommandIDs=Object.values(userKeyMap);for(const customKey of Object.keys(customKeyMap)){const customCommand=customKeyMap[customKey];userDefinedCommandIDs.includes(customCommand)||userDefinedKeys.includes(customKey)||(userKeyMap[customKey]=customCommand)}}function _getNormalisedKeyMap(keyMap){const normalisedKeyMap={},normalisedKeyMapCounts={};for(let key of Object.keys(keyMap)){const normalisedKey=normalizeKeyDescriptorString(key);normalisedKeyMapCounts[normalisedKey]=(normalisedKeyMapCounts[normalisedKey]||0)+1}for(let key of Object.keys(keyMap))try{const normalisedKey=normalizeKeyDescriptorString(key);1===normalisedKeyMapCounts[normalisedKey]?normalisedKeyMap[normalisedKey]=keyMap[key]:normalisedKeyMap[key]=keyMap[key]}catch(e){console.error("Error normalising user keymap key: ",key,e),normalisedKeyMap[key]=keyMap[key]}return normalisedKeyMap}function _loadUserKeyMapImmediate(){return new Promise((resolve,reject)=>{_readUserKeyMap().then(function(keyMap){keyMap=_getNormalisedKeyMap(keyMap),_originalUserKeyMap=structuredClone(keyMap),_mixCustomKeyMaps(keyMap),_allCommands=CommandManager.getAll(),_customKeyMapCache=_.cloneDeep(_customKeyMap),_customKeyMap=keyMap,_undoPriorUserKeyBindings(),_applyUserKeyBindings(),resolve()},function(err){_showErrorsAndOpenKeyMap(err),console.error(err),resolve()})})}function resetUserShortcutsAsync(){return new Promise((resolve,reject)=>{let userKeyMapPath=_getUserKeyMapFilePath(),file=FileSystem.getFileForPath(userKeyMapPath),defaultContent='{\n "documentation": "https://github.com/phcode-dev/phoenix/wiki/User-%60keymap.json%60",\n "overrides": {\n \n }\n}\n';return FileUtils.writeText(file,defaultContent,!0).done(()=>{_loadUserKeyMapImmediate().then(resolve).catch(reject)}).fail(reject)})}function _openUserKeyMap(){let userKeyMapPath=_getUserKeyMapFilePath(),file;FileSystem.getFileForPath(userKeyMapPath).exists(function(err,doesExist){doesExist?CommandManager.execute(Commands.FILE_OPEN,{fullPath:userKeyMapPath}):resetUserShortcutsAsync().finally(function(){CommandManager.execute(Commands.FILE_OPEN,{fullPath:userKeyMapPath})})})}function _initCommandAndKeyMaps(){_allCommands=CommandManager.getAll(),_initDefaultShortcuts(),_defaultKeyMap=_.cloneDeep(_keyMap)}function _setUserKeyMapFilePath(fullPath){_userKeyMapFilePath=fullPath}function isInOverlayMode(){return KeyboardOverlayMode.isInOverlayMode()}function _isAnAssignableKey(key){if(!key)return!1;const split=key.split("-");return 1===split.length&&key.length>1&&"F"===key[0]||(2===split.length&&"Shift"===split[0]&&split[1].length>1||(2!==split.length||"Shift"!==split[0]||1!==split[1].length)&&!!key.includes("-"))}function updateShortcutSelection(event,key){if(key&&_isAnAssignableKey(key)&&normalizeKeyDescriptorString(key)){let normalizedKey=normalizeKeyDescriptorString(key);if(_isReservedShortcuts(normalizedKey))return console.warn("Cannot assign reserved shortcut: ",normalizedKey),event.stopPropagation(),event.preventDefault(),!0;capturedShortcut=normalizedKey;let existingBinding=_keyMap[normalizedKey];if(normalizedKey)if(existingBinding&&existingBinding.commandID===keyboardShortcutCaptureInProgress.getID())keyboardShortcutDialog.close(),keyboardShortcutDialog=null,keyboardShortcutCaptureInProgress=null;else if(existingBinding){const command=CommandManager.get(existingBinding.commandID);$(".change-shortcut-dialog .message").html(StringUtils.format(Strings.KEYBOARD_SHORTCUT_CHANGE_DIALOG_DUPLICATE,key,command.getName(),keyboardShortcutCaptureInProgress.getName())),$(".change-shortcut-dialog .Assign").removeClass("forced-hidden").focus(),$(".change-shortcut-dialog .Remove").addClass("forced-hidden")}else keyboardShortcutDialog.close(),keyboardShortcutDialog=null,_addToUserKeymapFile(key,keyboardShortcutCaptureInProgress.getID()),keyboardShortcutCaptureInProgress=null;else console.error("Failed to normalize "+key);event.stopPropagation(),event.preventDefault()}return!0}AppInit.htmlReady(function(){window.document.body.addEventListener("keydown",_handleKeyEvent,!0),window.document.body.addEventListener("keyup",event=>{ctrlKeyCodes[event.code]&&ctrlKeyCodes[event.key]&&(isCtrlDepressed=!1)},!0),document.body.addEventListener("mousemove",()=>{mouseCursorHidden&&(mouseCursorHidden=!1,document.body.classList.remove("hide-cursor"))}),exports.useWindowsCompatibleBindings="mac"!==brackets.platform&&"win"!==brackets.platform}),EventDispatcher.on_duringInit(CommandManager,"commandRegistered",_handleCommandRegistered),CommandManager.register(Strings.CMD_OPEN_KEYMAP,Commands.FILE_OPEN_KEYMAP,_openUserKeyMap),require(["document/DocumentManager"],function(DocumentManager){DocumentManager.on("documentSaved",function checkKeyMapUpdates(e,doc){doc&&doc.file.fullPath===_userKeyMapFilePath&&_loadUserKeyMap()})}),AppInit.extensionsLoaded(function(){let params=new UrlParams;params.parse(),"true"===params.get("reloadWithoutUserExts")&&(_showErrors=!1),_initCommandAndKeyMaps(),(PreferencesManager=Phoenix.globalAPI&&Phoenix.globalAPI.PreferencesManager).stateManager.definePreference(STATE_CUSTOM_KEY_MAP_ID,"string",null).on("change",()=>{_customKeymapIDInUse=PreferencesManager.stateManager.get(STATE_CUSTOM_KEY_MAP_ID),_loadUserKeyMap(),exports.constructor("presetChanged",_customKeymapIDInUse)}),PreferencesManager.definePreference(PREF_TRIPLE_CTRL_KEY_PRESS_ENABLED,"boolean",!0,{description:Strings.DESCRIPTION_TRIPLE_CTRL_PALETTE}),_customKeymapIDInUse=PreferencesManager.stateManager.get(STATE_CUSTOM_KEY_MAP_ID),_loadUserKeyMap()});let keyboardShortcutCaptureInProgress=null,keyboardShortcutDialog=null,capturedShortcut=null;function showShortcutSelectionDialog(command){if(Metrics.countEvent(Metrics.EVENT_TYPE.KEYBOARD,"shortcut","DialogShown"),_isSpecialCommand(command.getID()))return;const panelCommand=CommandManager.get(Commands.HELP_TOGGLE_SHORTCUTS_PANEL);capturedShortcut=null;const keyBindings=getKeyBindings(command);let currentShortcutText=Strings.KEYBOARD_SHORTCUT_NONE;if(keyBindings.length){currentShortcutText=keyBindings[0].displayKey||keyBindings[0].key;for(let i=1;i<keyBindings.length;i++)currentShortcutText+=`, ${keyBindings[i].displayKey||keyBindings[i].key}`}keyboardShortcutCaptureInProgress=command,keyboardShortcutDialog=Dialogs.showModalDialogUsingTemplate(Mustache.render(KeyboardDialogTemplate,{Strings:Strings,message:StringUtils.format(Strings.KEYBOARD_SHORTCUT_CHANGE_DIALOG_TEXT,command.getName(),currentShortcutText)})),currentShortcutText===Strings.KEYBOARD_SHORTCUT_NONE&&$(".change-shortcut-dialog .Remove").addClass("forced-hidden"),panelCommand&&panelCommand.getChecked()&&$(".change-shortcut-dialog .Show").addClass("forced-hidden"),keyboardShortcutDialog.done(closeReason=>{"remove"===closeReason&¤tShortcutText?(_addToUserKeymapFile(keyBindings.map(k=>k.key),null),Metrics.countEvent(Metrics.EVENT_TYPE.KEYBOARD,"shortcut","removed")):closeReason===Dialogs.DIALOG_BTN_OK&&capturedShortcut?(_addToUserKeymapFile(capturedShortcut,command.getID()),Metrics.countEvent(Metrics.EVENT_TYPE.KEYBOARD,"shortcut","changed")):"show"===closeReason&&(panelCommand.getChecked()||panelCommand.execute()),capturedShortcut=null,keyboardShortcutCaptureInProgress=null,keyboardShortcutDialog=null})}function canAssignBinding(commandId){return!_isSpecialCommand(commandId)}function _getKnownBindableCommands(){return new Set(knownBindableCommands)}EventDispatcher.makeEventDispatcher(exports),exports._reset=_reset,exports._setUserKeyMapFilePath=_setUserKeyMapFilePath,exports._getUserKeyMapFilePath=_getUserKeyMapFilePath,exports._getDisplayKey=_getDisplayKey,exports._loadUserKeyMap=_loadUserKeyMap,exports._loadUserKeyMapImmediate=_loadUserKeyMapImmediate,exports._initCommandAndKeyMaps=_initCommandAndKeyMaps,exports._onCtrlUp=_onCtrlUp,exports._getKnownBindableCommands=_getKnownBindableCommands,exports._getCustomShortcutOrigin=_getCustomShortcutOrigin,exports._setCurrentCustomKeymapPack=_setCurrentCustomKeymapPack,exports.getKeymap=getKeymap,exports.canAssignBinding=canAssignBinding,exports.addBinding=addBinding,exports.removeBinding=removeBinding,exports.formatKeyDescriptor=formatKeyDescriptor,exports.getKeyBindings=getKeyBindings,exports.getKeyBindingsDisplay=getKeyBindingsDisplay,exports.addGlobalKeydownHook=addGlobalKeydownHook,exports.removeGlobalKeydownHook=removeGlobalKeydownHook,exports.isInOverlayMode=isInOverlayMode,exports.resetUserShortcutsAsync=resetUserShortcutsAsync,exports.showShortcutSelectionDialog=showShortcutSelectionDialog,exports.registerCustomKeymapPack=registerCustomKeymapPack,exports.getAllCustomKeymapPacks=getAllCustomKeymapPacks,exports.getCurrentCustomKeymapPack=getCurrentCustomKeymapPack,exports.KEY=KEY,exports.EVENT_KEY_BINDING_ADDED=EVENT_KEY_BINDING_ADDED,exports.EVENT_KEY_BINDING_REMOVED=EVENT_KEY_BINDING_REMOVED,exports.EVENT_NEW_PRESET=EVENT_NEW_PRESET,exports.EVENT_PRESET_CHANGED="presetChanged",exports.useWindowsCompatibleBindings=!1,exports._handleKey=_handleKey,exports._handleKeyEvent=_handleKeyEvent});