Chromevox theme now an extension, added demo file, added findSearchBox event.

This commit is contained in:
Peter Xiao 2013-08-12 15:58:09 -07:00
commit 38b42d20d3
4 changed files with 809 additions and 786 deletions

44
demo/chromevox.html Normal file
View file

@ -0,0 +1,44 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>ACE Autocompletion demo</title>
<style type="text/css" media="screen">
body {
overflow: hidden;
}
#editor {
margin: 0;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
</style>
</head>
<body>
<pre id="editor"></pre>
<!-- load ace -->
<script src="../build/src/ace.js"></script>
<!-- load ace language tools -->
<script src="../build/src/ext-chromevox.js"></script>
<script>
// trigger extension
ace.require("ace/ext/chromevox");
var editor = ace.edit("editor");
editor.session.setMode("ace/mode/html");
editor.setTheme("ace/theme/tomorrow");
// enable autocompletion and snippets
editor.setOptions({
enableBasicAutocompletion: true,
enableSnippets: true
});
</script>
<script src="./show_own_source.js"></script>
</body>
</html>

View file

@ -1,4 +1,5 @@
define(function(require, exports, module) {
/* ChromeVox Ace namespace. */
var cvoxAce = {};
@ -141,11 +142,12 @@ define(function(require, exports, module) {
var KEY_PREFIX = 'CONTROL + SHIFT ';
/* Globals. */
cvoxAce.editor = null;
/**
* Last cursor position.
* @type {!cvoxAce.Cursor}
* @type {cvoxAce.Cursor}
*/
var lastCursor = ace.selection.getCursor();
var lastCursor = null;
/**
* Table of annotations.
@ -201,7 +203,8 @@ define(function(require, exports, module) {
* @return {boolean} True if in Vim mode.
*/
var isVimMode = function() {
return ace.keyBinding.getKeyboardHandler().$id === 'ace/keyboard/vim';
var keyboardHandler = cvoxAce.editor.keyBinding.getKeyboardHandler();
return keyboardHandler.$id === 'ace/keyboard/vim';
};
/**
@ -210,7 +213,7 @@ define(function(require, exports, module) {
* @return {!cvoxAce.Token} Token at the current position.
*/
var getCurrentToken = function(cursor) {
return ace.getSession().getTokenAt(cursor.row, cursor.column + 1);
return cvoxAce.editor.getSession().getTokenAt(cursor.row, cursor.column + 1);
};
/**
@ -218,7 +221,7 @@ define(function(require, exports, module) {
* @param {!cvoxAce.Cursor} cursor Current cursor position.
*/
var getCurrentLine = function(cursor) {
return ace.getSession().getLine(cursor.row);
return cvoxAce.editor.getSession().getLine(cursor.row);
};
/**
@ -278,7 +281,7 @@ define(function(require, exports, module) {
* @param {number} queue Queue mode to speak.
*/
var speakLine = function(row, queue) {
var tokens = ace.getSession().getTokens(row);
var tokens = cvoxAce.editor.getSession().getTokens(row);
if (tokens.length === 0) {
return;
}
@ -418,7 +421,7 @@ define(function(require, exports, module) {
changed = false;
return;
}
var currCursor = ace.selection.getCursor();
var currCursor = cvoxAce.editor.selection.getCursor();
if (currCursor.row !== lastCursor.row) {
onRowChange(currCursor);
} else {
@ -482,7 +485,7 @@ define(function(require, exports, module) {
* @param {!Event} evt Event.
*/
var onAnnotationChange = function(evt) {
var annotations = ace.getSession().getAnnotations();
var annotations = cvoxAce.editor.getSession().getAnnotations();
var newAnnotations = annotations.filter(isNewAnnotation);
if (newAnnotations.length > 0) {
cvox.Api.playEarcon(ERROR_EARCON);
@ -545,7 +548,7 @@ define(function(require, exports, module) {
if (!isVimMode()) {
return;
}
switch (ace.keyBinding.$data.state) {
switch (cvoxAce.editor.keyBinding.$data.state) {
case INSERT_MODE_STATE:
cvox.Api.speak('Insert mode');
break;
@ -640,7 +643,7 @@ define(function(require, exports, module) {
shortcut.func();
/* ChromeVox will bring focus to an element near the cursor instead of the
* text input. */
ace.focus();
cvoxAce.editor.focus();
}
};
@ -663,47 +666,13 @@ define(function(require, exports, module) {
body.addEventListener('ATCustomEvent', contextMenuHandler, true);
};
/**
* Returns a mutations handler where f is applied to each mutation.
* @param {function} f Function to be applied to mutations.
*/
var getMutationHandler = function(f) {
return function(mutations) {
mutations.forEach(f);
};
};
/**
* Watches and handles the mutation that is a result of a search.
* @param {Mutation} m Mutation.
*/
var watchForSearch = function(m) {
if (m.attributeName === 'class' &&
m.target.className === 'ace_search_form ace_nomatch') {
/* No match, give auditory feedback! */
cvox.Api.playEarcon(NO_MATCH_EARCON);
} else {
var onFindSearchbox = function(evt) {
if (evt.match) {
/* There is still a match! Speak the line. */
speakLine(lastCursor.row, 0);
}
};
/**
* Configuration for mutation observer.
*/
var MO_CONFIG = { attributes: true, childList: true, characterData: true};
/**
* Watches and handles the mutation that adds the search bar to the DOM.
*/
var watchForStartSearch = function(m) {
for (var i = 0; i < m.addedNodes.length; i++) {
if (m.addedNodes.item(i).className === 'ace_search right') {
var mutationHandler = getMutationHandler(watchForSearch);
var searchObs = new MutationObserver(mutationHandler);
var target = m.addedNodes.item(i).querySelector('.ace_search_form');
searchObs.observe(target, MO_CONFIG);
}
} else {
/* No match, give auditory feedback! */
cvox.Api.playEarcon(NO_MATCH_EARCON);
}
};
@ -711,7 +680,7 @@ define(function(require, exports, module) {
* Focus to text input.
*/
var focus = function() {
ace.focus();
cvoxAce.editor.focus();
};
/**
@ -774,7 +743,9 @@ define(function(require, exports, module) {
/**
* Initialize the theme.
*/
var init = function() {
var init = function(editor) {
cvoxAce.editor = editor;
lastCursor = editor.selection.getCursor();
/* Construct maps. */
SHORTCUTS.forEach(function(shortcut) {
keyCodeToShortcutMap[shortcut.keyCode] = shortcut;
@ -782,10 +753,11 @@ define(function(require, exports, module) {
});
/* Set up listeners. */
ace.getSession().selection.on('changeCursor', onCursorChange);
ace.getSession().on('change', onChange);
ace.getSession().on('changeAnnotation', onAnnotationChange);
ace.on('changeStatus', onChangeStatus);
cvoxAce.editor.getSession().selection.on('changeCursor', onCursorChange);
cvoxAce.editor.getSession().on('change', onChange);
cvoxAce.editor.getSession().on('changeAnnotation', onAnnotationChange);
cvoxAce.editor.on('changeStatus', onChangeStatus);
cvoxAce.editor.on('findSearchBox', onFindSearchbox);
window.addEventListener('keydown', onKeyDown);
/* Assume we start in command mode if vim. */
@ -794,13 +766,7 @@ define(function(require, exports, module) {
}
initContextMenu();
var target = document.querySelector('.ace_editor');
var mutationHandler = getMutationHandler(watchForStartSearch);
var observer = new MutationObserver(mutationHandler);
observer.observe(target, MO_CONFIG);
ace.focus();
cvoxAce.editor.focus();
};
/**
@ -825,19 +791,30 @@ define(function(require, exports, module) {
/**
* Check for ChromeVox load.
* @param {Object} editor Editor to use.
*/
function watchForCvoxLoad() {
function watchForCvoxLoad(editor) {
if (cvoxApiExists()) {
init();
init(editor);
} else {
tries++;
if (tries >= MAX_TRIES) {
return;
}
window.setTimeout(watchForCvoxLoad, 500);
window.setTimeout(watchForCvoxLoad, 500, editor);
}
}
/* Initialize everything when ChromeVox has loaded. */
watchForCvoxLoad();
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
}
});
});

View file

@ -214,7 +214,9 @@ var SearchBox = function(editor, range, showReplaceForm) {
caseSensitive: this.caseSensitiveOption.checked,
wholeWord: this.wholeWordOption.checked
});
dom.setCssClass(this.searchBox, "ace_nomatch", !range && this.searchInput.value);
var noMatch = !range && this.searchInput.value;
dom.setCssClass(this.searchBox, "ace_nomatch", noMatch);
this.editor._emit("findSearchBox", { match: !noMatch });
this.highlight();
};
this.findNext = function() {

View file

@ -16,8 +16,8 @@ module.exports.themes = [
"kr_theme",
"merbivore",
"merbivore_soft",
"monokai",
"mono_industrial",
"monokai",
"pastel_on_dark",
"solarized_dark",
"solarized_light",