ace.define(“ace/ext/chromevox”,, function(require, exports, module) { var cvoxAce = {}; cvoxAce.SpeechProperty; cvoxAce.Cursor; cvoxAce.Token; cvoxAce.Annotation; var CONSTANT_PROP = {

'rate': 0.8,
'pitch': 0.4,
'volume': 0.9

}; var DEFAULT_PROP = {

'rate': 1,
'pitch': 0.5,
'volume': 0.9

}; var ENTITY_PROP = {

'rate': 0.8,
'pitch': 0.8,
'volume': 0.9

}; var KEYWORD_PROP = {

'rate': 0.8,
'pitch': 0.3,
'volume': 0.9

}; var STORAGE_PROP = {

'rate': 0.8,
'pitch': 0.7,
'volume': 0.9

}; var VARIABLE_PROP = {

'rate': 0.8,
'pitch': 0.8,
'volume': 0.9

}; var DELETED_PROP = {

'punctuationEcho': 'none',
'relativePitch': -0.6

}; var ERROR_EARCON = 'ALERT_NONMODAL'; var MODE_SWITCH_EARCON = 'ALERT_MODAL'; var NO_MATCH_EARCON = 'INVALID_KEYPRESS'; var INSERT_MODE_STATE = 'insertMode'; var COMMAND_MODE_STATE = 'start';

var REPLACE_LIST = [

{
  substr: ';',
  newSubstr: ' semicolon '
},
{
  substr: ':',
  newSubstr: ' colon '
}

]; var Command = {

SPEAK_ANNOT: 'annots',
SPEAK_ALL_ANNOTS: 'all_annots',
TOGGLE_LOCATION: 'toggle_location',
SPEAK_MODE: 'mode',
SPEAK_ROW_COL: 'row_col',
TOGGLE_DISPLACEMENT: 'toggle_displacement',
FOCUS_TEXT: 'focus_text'

}; var KEY_PREFIX = 'CONTROL + SHIFT '; cvoxAce.editor = null; var lastCursor = null; var annotTable = {}; var shouldSpeakRowLocation = false; var shouldSpeakDisplacement = false; var changed = false; var vimState = null; var keyCodeToShortcutMap = {}; var cmdToShortcutMap = {}; var getKeyShortcutString = function(keyCode) {

return KEY_PREFIX + String.fromCharCode(keyCode);

}; var isVimMode = function() {

var keyboardHandler = cvoxAce.editor.keyBinding.getKeyboardHandler();
return keyboardHandler.$id === 'ace/keyboard/vim';

}; var getCurrentToken = function(cursor) {

return cvoxAce.editor.getSession().getTokenAt(cursor.row, cursor.column + 1);

}; var getCurrentLine = function(cursor) {

return cvoxAce.editor.getSession().getLine(cursor.row);

}; var onRowChange = function(currCursor) {

if (annotTable[currCursor.row]) {
  cvox.Api.playEarcon(ERROR_EARCON);
}
if (shouldSpeakRowLocation) {
  cvox.Api.stop();
  speakChar(currCursor);
  speakTokenQueue(getCurrentToken(currCursor));
  speakLine(currCursor.row, 1);
} else {
  speakLine(currCursor.row, 0);
}

}; var isWord = function(cursor) {

var line = getCurrentLine(cursor);
var lineSuffix = line.substr(cursor.column - 1);
if (cursor.column === 0) {
  lineSuffix = ' ' + line;
}
var firstWordRegExp = /^\W(\w+)/;
var words = firstWordRegExp.exec(lineSuffix);
return words !== null;

}; var rules = {

'constant': {
  prop: CONSTANT_PROP
},
'entity': {
  prop: ENTITY_PROP
},
'keyword': {
  prop: KEYWORD_PROP
},
'storage': {
  prop: STORAGE_PROP
},
'variable': {
  prop: VARIABLE_PROP
},
'meta': {
  prop: DEFAULT_PROP,
  replace: [
    {
      substr: '</',
      newSubstr: ' closing tag '
    },
    {
      substr: '/>',
      newSubstr: ' close tag '
    },
    {
      substr: '<',
      newSubstr: ' tag start '
    },
    {
      substr: '>',
      newSubstr: ' tag end '
    }
  ]
}

}; var DEFAULT_RULE = {

prop: DEFAULT_RULE

}; var expand = function(value, replaceRules) {

var newValue = value;
for (var i = 0; i < replaceRules.length; i++) {
  var replaceRule = replaceRules[i];
  var regexp = new RegExp(replaceRule.substr, 'g');
  newValue = newValue.replace(regexp, replaceRule.newSubstr);
}
return newValue;

}; var mergeTokens = function(tokens, start, end) {

var newToken = {};
newToken.value = '';
newToken.type = tokens[start].type;
for (var j = start; j < end; j++) {
  newToken.value += tokens[j].value;
}
return newToken;

}; var mergeLikeTokens = function(tokens) {

if (tokens.length <= 1) {
  return tokens;
}
var newTokens = [];
var lastLikeIndex = 0;
for (var i = 1; i < tokens.length; i++) {
  var lastLikeToken = tokens[lastLikeIndex];
  var currToken = tokens[i];
  if (getTokenRule(lastLikeToken) !== getTokenRule(currToken)) {
    newTokens.push(mergeTokens(tokens, lastLikeIndex, i));
    lastLikeIndex = i;
  }
}
newTokens.push(mergeTokens(tokens, lastLikeIndex, tokens.length));
return newTokens;

}; var isRowWhiteSpace = function(row) {

var line = cvoxAce.editor.getSession().getLine(row);
var whiteSpaceRegexp = /^\s*$/;
return whiteSpaceRegexp.exec(line) !== null;

}; var speakLine = function(row, queue) {

var tokens = cvoxAce.editor.getSession().getTokens(row);
if (tokens.length === 0 || isRowWhiteSpace(row)) {
  cvox.Api.playEarcon('EDITABLE_TEXT');
  return;
}
tokens = mergeLikeTokens(tokens);
var firstToken = tokens[0];
tokens = tokens.filter(function(token) {
  return token !== firstToken;
});
speakToken_(firstToken, queue);
tokens.forEach(speakTokenQueue);

}; var speakTokenFlush = function(token) {

speakToken_(token, 0);

}; var speakTokenQueue = function(token) {

speakToken_(token, 1);

}; var getTokenRule = function(token) {

if (!token || !token.type) {
  return;
}
var split = token.type.split('.');
if (split.length === 0) {
  return;
}
var type = split[0];
var rule = rules[type];
if (!rule) {
  return DEFAULT_RULE;
}
return rule;

}; var speakToken_ = function(token, queue) {

var rule = getTokenRule(token);
var value = expand(token.value, REPLACE_LIST);
if (rule.replace) {
  value = expand(value, rule.replace);
}
cvox.Api.speak(value, queue, rule.prop);

}; var speakChar = function(cursor) {

var line = getCurrentLine(cursor);
cvox.Api.speak(line[cursor.column], 1);

}; var speakDisplacement = function(lastCursor, currCursor) {

var line = getCurrentLine(currCursor);
var displace = line.substring(lastCursor.column, currCursor.column);
displace = displace.replace(/ /g, ' space ');
cvox.Api.speak(displace);

}; var speakCharOrWordOrLine = function(lastCursor, currCursor) {

if (Math.abs(lastCursor.column - currCursor.column) !== 1) {
  var currLineLength = getCurrentLine(currCursor).length;
  if (currCursor.column === 0 || currCursor.column === currLineLength) {
    speakLine(currCursor.row, 0);
    return;
  }
  if (isWord(currCursor)) {
    cvox.Api.stop();
    speakTokenQueue(getCurrentToken(currCursor));
    return;
  }
}
speakChar(currCursor);

}; var onColumnChange = function(lastCursor, currCursor) {

if (!cvoxAce.editor.selection.isEmpty()) {
  speakDisplacement(lastCursor, currCursor);
  cvox.Api.speak('selected', 1);
}
else if (shouldSpeakDisplacement) {
  speakDisplacement(lastCursor, currCursor);
} else {
  speakCharOrWordOrLine(lastCursor, currCursor);
}

}; var onCursorChange = function(evt) {

if (changed) {
  changed = false;
  return;
}
var currCursor = cvoxAce.editor.selection.getCursor();
if (currCursor.row !== lastCursor.row) {
  onRowChange(currCursor);
} else {
  onColumnChange(lastCursor, currCursor);
}
lastCursor = currCursor;

}; var onSelectionChange = function(evt) {

if (cvoxAce.editor.selection.isEmpty()) {
  cvox.Api.speak('unselected');
}

}; var onChange = function(evt) {

var data = evt.data;
switch (data.action) {
case 'removeText':
  cvox.Api.speak(data.text, 0, DELETED_PROP);
  changed = true;
  break;
case 'insertText':
  cvox.Api.speak(data.text, 0);
  changed = true;
  break;
}

}; var isNewAnnotation = function(annot) {

var row = annot.row;
var col = annot.column;
return !annotTable[row] || !annotTable[row][col];

}; var populateAnnotations = function(annotations) {

annotTable = {};
for (var i = 0; i < annotations.length; i++) {
  var annotation = annotations[i];
  var row = annotation.row;
  var col = annotation.column;
  if (!annotTable[row]) {
    annotTable[row] = {};
  }
  annotTable[row][col] = annotation;
}

}; var onAnnotationChange = function(evt) {

var annotations = cvoxAce.editor.getSession().getAnnotations();
var newAnnotations = annotations.filter(isNewAnnotation);
if (newAnnotations.length > 0) {
  cvox.Api.playEarcon(ERROR_EARCON);
}
populateAnnotations(annotations);

}; var speakAnnot = function(annot) {

var annotText = annot.type + ' ' + annot.text + ' on ' +
    rowColToString(annot.row, annot.column);
annotText = annotText.replace(';', 'semicolon');
cvox.Api.speak(annotText, 1);

}; var speakAnnotsByRow = function(row) {

var annots = annotTable[row];
for (var col in annots) {
  speakAnnot(annots[col]);
}

}; var rowColToString = function(row, col) {

return 'row ' + (row + 1) + ' column ' + (col + 1);

}; var speakCurrRowAndCol = function() {

cvox.Api.speak(rowColToString(lastCursor.row, lastCursor.column));

}; var speakAllAnnots = function() {

for (var row in annotTable) {
  speakAnnotsByRow(row);
}

}; var speakMode = function() {

if (!isVimMode()) {
  return;
}
switch (cvoxAce.editor.keyBinding.$data.state) {
case INSERT_MODE_STATE:
  cvox.Api.speak('Insert mode');
  break;
case COMMAND_MODE_STATE:
  cvox.Api.speak('Command mode');
  break;
}

}; var toggleSpeakRowLocation = function() {

shouldSpeakRowLocation = !shouldSpeakRowLocation;
if (shouldSpeakRowLocation) {
  cvox.Api.speak('Speak location on row change enabled.');
} else {
  cvox.Api.speak('Speak location on row change disabled.');
}

}; var toggleSpeakDisplacement = function() {

shouldSpeakDisplacement = !shouldSpeakDisplacement;
if (shouldSpeakDisplacement) {
  cvox.Api.speak('Speak displacement on column changes.');
} else {
  cvox.Api.speak('Speak current character or word on column changes.');
}

}; var onKeyDown = function(evt) {

if (evt.ctrlKey && evt.shiftKey) {
  var shortcut = keyCodeToShortcutMap[evt.keyCode];
  if (shortcut) {
    shortcut.func();
  }
}

}; var onChangeStatus = function(evt, editor) {

if (!isVimMode()) {
  return;
}
var state = editor.keyBinding.$data.state;
if (state === vimState) {
  return;
}
switch (state) {
case INSERT_MODE_STATE:
  cvox.Api.playEarcon(MODE_SWITCH_EARCON);
  cvox.Api.setKeyEcho(true);
  break;
case COMMAND_MODE_STATE:
  cvox.Api.playEarcon(MODE_SWITCH_EARCON);
  cvox.Api.setKeyEcho(false);
  break;
}
vimState = state;

}; var contextMenuHandler = function(evt) {

var cmd = evt.detail['customCommand'];
var shortcut = cmdToShortcutMap[cmd];
if (shortcut) {
  shortcut.func();
  cvoxAce.editor.focus();
}

}; var initContextMenu = function() {

var ACTIONS = SHORTCUTS.map(function(shortcut) {
  return {
    desc: shortcut.desc + getKeyShortcutString(shortcut.keyCode),
    cmd: shortcut.cmd
  };
});
var body = document.querySelector('body');
body.setAttribute('contextMenuActions', JSON.stringify(ACTIONS));
body.addEventListener('ATCustomEvent', contextMenuHandler, true);

}; var onFindSearchbox = function(evt) {

if (evt.match) {
  speakLine(lastCursor.row, 0);
} else {
  cvox.Api.playEarcon(NO_MATCH_EARCON);
}

}; var focus = function() {

cvoxAce.editor.focus();

}; var SHORTCUTS = [

{
  keyCode: 49,
  func: function() {
    speakAnnotsByRow(lastCursor.row);
  },
  cmd: Command.SPEAK_ANNOT,
  desc: 'Speak annotations on line'
},
{
  keyCode: 50,
  func: speakAllAnnots,
  cmd: Command.SPEAK_ALL_ANNOTS,
  desc: 'Speak all annotations'
},
{
  keyCode: 51,
  func: speakMode,
  cmd: Command.SPEAK_MODE,
  desc: 'Speak Vim mode'
},
{
  keyCode: 52,
  func: toggleSpeakRowLocation,
  cmd: Command.TOGGLE_LOCATION,
  desc: 'Toggle speak row location'
},
{
  keyCode: 53,
  func: speakCurrRowAndCol,
  cmd: Command.SPEAK_ROW_COL,
  desc: 'Speak row and column'
},
{
  keyCode: 54,
  func: toggleSpeakDisplacement,
  cmd: Command.TOGGLE_DISPLACEMENT,
  desc: 'Toggle speak displacement'
},
{
  keyCode: 55,
  func: focus,
  cmd: Command.FOCUS_TEXT,
  desc: 'Focus text'
}

]; var onFocus = function() {

cvoxAce.editor = editor;
editor.getSession().selection.on('changeCursor', onCursorChange);
editor.getSession().selection.on('changeSelection', onSelectionChange);
editor.getSession().on('change', onChange);
editor.getSession().on('changeAnnotation', onAnnotationChange);
editor.on('changeStatus', onChangeStatus);
editor.on('findSearchBox', onFindSearchbox);
editor.container.addEventListener('keydown', onKeyDown);

lastCursor = editor.selection.getCursor();

}; var init = function(editor) {

onFocus();
SHORTCUTS.forEach(function(shortcut) {
  keyCodeToShortcutMap[shortcut.keyCode] = shortcut;
  cmdToShortcutMap[shortcut.cmd] = shortcut;
});

editor.on('focus', onFocus);
if (isVimMode()) {
  cvox.Api.setKeyEcho(false);
}
initContextMenu();

}; function cvoxApiExists() {

return (typeof(cvox) !== 'undefined') && cvox && cvox.Api;

} var tries = 0; var MAX_TRIES = 15; function watchForCvoxLoad(editor) {

if (cvoxApiExists()) {
  init(editor);
} else {
  tries++;
  if (tries >= MAX_TRIES) {
    return;
  }
  window.setTimeout(watchForCvoxLoad, 500, editor);
}

}

var Editor = require('../editor').Editor; require('../config').defineOptions(Editor.prototype, 'editor', {

enableChromevoxEnhancements: {
  set: function(val) {
    if (val) {
      watchForCvoxLoad(this);
    }
  },
  value: true // turn it on by default or check for window.cvox
}

});

});

(function() {
    ace.require(["ace/ext/chromevox"], function() {});
})();