You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by ga...@apache.org on 2013/10/29 16:40:27 UTC

[42/51] [partial] working replacement

http://git-wip-us.apache.org/repos/asf/couchdb/blob/9abd128c/src/fauxton/assets/js/libs/ace/ext/searchbox.js
----------------------------------------------------------------------
diff --git a/src/fauxton/assets/js/libs/ace/ext/searchbox.js b/src/fauxton/assets/js/libs/ace/ext/searchbox.js
new file mode 100644
index 0000000..fbbaa8f
--- /dev/null
+++ b/src/fauxton/assets/js/libs/ace/ext/searchbox.js
@@ -0,0 +1,286 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Distributed under the BSD license:
+ *
+ * Copyright (c) 2010, Ajax.org B.V.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Ajax.org B.V. nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define(function(require, exports, module) {
+"use strict";
+
+var dom = require("../lib/dom");
+var lang = require("../lib/lang");
+var event = require("../lib/event");
+var searchboxCss = require("../requirejs/text!./searchbox.css");
+var HashHandler = require("../keyboard/hash_handler").HashHandler;
+var keyUtil = require("../lib/keys");
+
+dom.importCssString(searchboxCss, "ace_searchbox");
+
+var html = '<div class="ace_search right">\
+    <button type="button" action="hide" class="ace_searchbtn_close"></button>\
+    <div class="ace_search_form">\
+        <input class="ace_search_field" placeholder="Search for" spellcheck="false"></input>\
+        <button type="button" action="findNext" class="ace_searchbtn next"></button>\
+        <button type="button" action="findPrev" class="ace_searchbtn prev"></button>\
+    </div>\
+    <div class="ace_replace_form">\
+        <input class="ace_search_field" placeholder="Replace with" spellcheck="false"></input>\
+        <button type="button" action="replaceAndFindNext" class="ace_replacebtn">Replace</button>\
+        <button type="button" action="replaceAll" class="ace_replacebtn">All</button>\
+    </div>\
+    <div class="ace_search_options">\
+        <span action="toggleRegexpMode" class="ace_button" title="RegExp Search">.*</span>\
+        <span action="toggleCaseSensitive" class="ace_button" title="CaseSensitive Search">Aa</span>\
+        <span action="toggleWholeWords" class="ace_button" title="Whole Word Search">\\b</span>\
+    </div>\
+</div>'.replace(/>\s+/g, ">");
+
+var SearchBox = function(editor, range, showReplaceForm) {
+    var div = dom.createElement("div");
+    div.innerHTML = html;
+    this.element = div.firstChild;
+
+    this.$init();
+    this.setEditor(editor);
+};
+
+(function() {
+    this.setEditor = function(editor) {
+        editor.searchBox = this;
+        editor.container.appendChild(this.element);
+        this.editor = editor;
+    };
+
+    this.$initElements = function(sb) {
+        this.searchBox = sb.querySelector(".ace_search_form");
+        this.replaceBox = sb.querySelector(".ace_replace_form");
+        this.searchOptions = sb.querySelector(".ace_search_options");
+        this.regExpOption = sb.querySelector("[action=toggleRegexpMode]");
+        this.caseSensitiveOption = sb.querySelector("[action=toggleCaseSensitive]");
+        this.wholeWordOption = sb.querySelector("[action=toggleWholeWords]");
+        this.searchInput = this.searchBox.querySelector(".ace_search_field");
+        this.replaceInput = this.replaceBox.querySelector(".ace_search_field");
+    };
+    
+    this.$init = function() {
+        var sb = this.element;
+        
+        this.$initElements(sb);
+        
+        var _this = this;
+        event.addListener(sb, "mousedown", function(e) {
+            setTimeout(function(){
+                _this.activeInput.focus();
+            }, 0);
+            event.stopPropagation(e);
+        });
+        event.addListener(sb, "click", function(e) {
+            var t = e.target || e.srcElement;
+            var action = t.getAttribute("action");
+            if (action && _this[action])
+                _this[action]();
+            else if (_this.$searchBarKb.commands[action])
+                _this.$searchBarKb.commands[action].exec(_this);
+            event.stopPropagation(e);
+        });
+
+        event.addCommandKeyListener(sb, function(e, hashId, keyCode) {
+            var keyString = keyUtil.keyCodeToString(keyCode);
+            var command = _this.$searchBarKb.findKeyCommand(hashId, keyString);
+            if (command && command.exec) {
+                command.exec(_this);
+                event.stopEvent(e);
+            }
+        });
+
+        this.$onChange = lang.delayedCall(function() {
+            _this.find(false, false);
+        });
+
+        event.addListener(this.searchInput, "input", function() {
+            _this.$onChange.schedule(20);
+        });
+        event.addListener(this.searchInput, "focus", function() {
+            _this.activeInput = _this.searchInput;
+            _this.searchInput.value && _this.highlight();
+        });
+        event.addListener(this.replaceInput, "focus", function() {
+            _this.activeInput = _this.replaceInput;
+            _this.searchInput.value && _this.highlight();
+        });
+    };
+
+    //keybinging outsite of the searchbox
+    this.$closeSearchBarKb = new HashHandler([{
+        bindKey: "Esc",
+        name: "closeSearchBar",
+        exec: function(editor) {
+            editor.searchBox.hide();
+        }
+    }]);
+
+    //keybinging outsite of the searchbox
+    this.$searchBarKb = new HashHandler();
+    this.$searchBarKb.bindKeys({
+        "Ctrl-f|Command-f|Ctrl-H|Command-Option-F": function(sb) {
+            var isReplace = sb.isReplace = !sb.isReplace;
+            sb.replaceBox.style.display = isReplace ? "" : "none";
+            sb[isReplace ? "replaceInput" : "searchInput"].focus();
+        },
+        "Ctrl-G|Command-G": function(sb) {
+            sb.findNext();
+        },
+        "Ctrl-Shift-G|Command-Shift-G": function(sb) {
+            sb.findPrev();
+        },
+        "esc": function(sb) {
+            setTimeout(function() { sb.hide();});
+        },
+        "Return": function(sb) {
+            if (sb.activeInput == sb.replaceInput)
+                sb.replace();
+            sb.findNext();
+        },
+        "Shift-Return": function(sb) {
+            if (sb.activeInput == sb.replaceInput)
+                sb.replace();
+            sb.findPrev();
+        },
+        "Tab": function(sb) {
+            (sb.activeInput == sb.replaceInput ? sb.searchInput : sb.replaceInput).focus();
+        }
+    });
+
+    this.$searchBarKb.addCommands([{
+        name: "toggleRegexpMode",
+        bindKey: {win: "Alt-R|Alt-/", mac: "Ctrl-Alt-R|Ctrl-Alt-/"},
+        exec: function(sb) {
+            sb.regExpOption.checked = !sb.regExpOption.checked;
+            sb.$syncOptions();
+        }
+    }, {
+        name: "toggleCaseSensitive",
+        bindKey: {win: "Alt-C|Alt-I", mac: "Ctrl-Alt-R|Ctrl-Alt-I"},
+        exec: function(sb) {
+            sb.caseSensitiveOption.checked = !sb.caseSensitiveOption.checked;
+            sb.$syncOptions();
+        }
+    }, {
+        name: "toggleWholeWords",
+        bindKey: {win: "Alt-B|Alt-W", mac: "Ctrl-Alt-B|Ctrl-Alt-W"},
+        exec: function(sb) {
+            sb.wholeWordOption.checked = !sb.wholeWordOption.checked;
+            sb.$syncOptions();
+        }
+    }]);
+
+    this.$syncOptions = function() {
+        dom.setCssClass(this.regExpOption, "checked", this.regExpOption.checked);
+        dom.setCssClass(this.wholeWordOption, "checked", this.wholeWordOption.checked);
+        dom.setCssClass(this.caseSensitiveOption, "checked", this.caseSensitiveOption.checked);
+        this.find(false, false);
+    };
+
+    this.highlight = function(re) {
+        this.editor.session.highlight(re || this.editor.$search.$options.re);
+        this.editor.renderer.updateBackMarkers()
+    };
+    this.find = function(skipCurrent, backwards) {
+        var range = this.editor.find(this.searchInput.value, {
+            skipCurrent: skipCurrent,
+            backwards: backwards,
+            wrap: true,
+            regExp: this.regExpOption.checked,
+            caseSensitive: this.caseSensitiveOption.checked,
+            wholeWord: this.wholeWordOption.checked
+        });
+        var noMatch = !range && this.searchInput.value;
+        dom.setCssClass(this.searchBox, "ace_nomatch", noMatch);
+        this.editor._emit("findSearchBox", { match: !noMatch });
+        this.highlight();
+    };
+    this.findNext = function() {
+        this.find(true, false);
+    };
+    this.findPrev = function() {
+        this.find(true, true);
+    };
+    this.replace = function() {
+        if (!this.editor.getReadOnly())
+            this.editor.replace(this.replaceInput.value);
+    };    
+    this.replaceAndFindNext = function() {
+        if (!this.editor.getReadOnly()) {
+            this.editor.replace(this.replaceInput.value);
+            this.findNext()
+        }
+    };
+    this.replaceAll = function() {
+        if (!this.editor.getReadOnly())
+            this.editor.replaceAll(this.replaceInput.value);
+    };
+
+    this.hide = function() {
+        this.element.style.display = "none";
+        this.editor.keyBinding.removeKeyboardHandler(this.$closeSearchBarKb);
+        this.editor.focus();
+    };
+    this.show = function(value, isReplace) {
+        this.element.style.display = "";
+        this.replaceBox.style.display = isReplace ? "" : "none";
+
+        this.isReplace = isReplace;
+
+        if (value)
+            this.searchInput.value = value;
+        this.searchInput.focus();
+        this.searchInput.select();
+
+        this.editor.keyBinding.addKeyboardHandler(this.$closeSearchBarKb);
+    };
+
+}).call(SearchBox.prototype);
+
+exports.SearchBox = SearchBox;
+
+exports.Search = function(editor, isReplace) {
+    var sb = editor.searchBox || new SearchBox(editor);
+    sb.show(editor.session.getTextRange(), isReplace);
+};
+
+});
+
+
+/* ------------------------------------------------------------------------------------------
+ * TODO
+ * --------------------------------------------------------------------------------------- */
+/*
+- move search form to the left if it masks current word
+- includ all options that search has. ex: regex
+- searchbox.searchbox is not that pretty. we should have just searchbox
+- disable prev button if it makes sence
+*/

http://git-wip-us.apache.org/repos/asf/couchdb/blob/9abd128c/src/fauxton/assets/js/libs/ace/ext/settings_menu.js
----------------------------------------------------------------------
diff --git a/src/fauxton/assets/js/libs/ace/ext/settings_menu.js b/src/fauxton/assets/js/libs/ace/ext/settings_menu.js
new file mode 100644
index 0000000..44f6d6a
--- /dev/null
+++ b/src/fauxton/assets/js/libs/ace/ext/settings_menu.js
@@ -0,0 +1,76 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Distributed under the BSD license:
+ *
+ * Copyright (c) 2013 Matthew Christopher Kastor-Inare III, Atropa Inc. Intl
+ * All rights reserved.
+ *
+ * Contributed to Ajax.org under the BSD license.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Ajax.org B.V. nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/*jslint indent: 4, maxerr: 50, white: true, browser: true, vars: true*/
+/*global define, require */
+
+/**
+ * Show Settings Menu
+ * @fileOverview Show Settings Menu <br />
+ * Displays an interactive settings menu mostly generated on the fly based on
+ *  the current state of the editor.
+ * @author <a href="mailto:matthewkastor@gmail.com">
+ *  Matthew Christopher Kastor-Inare III </a><br />
+ *  ☭ Hial Atropa!! ☭
+ */
+
+define(function(require, exports, module) {
+"use strict";
+var generateSettingsMenu = require('./menu_tools/generate_settings_menu').generateSettingsMenu;
+var overlayPage = require('./menu_tools/overlay_page').overlayPage;
+/**
+ * This displays the settings menu if it is not already being shown.
+ * @author <a href="mailto:matthewkastor@gmail.com">
+ *  Matthew Christopher Kastor-Inare III </a><br />
+ *  ☭ Hial Atropa!! ☭
+ * @param {ace.Editor} editor An instance of the ace editor.
+ */
+function showSettingsMenu(editor) {
+    // make sure the menu isn't open already.
+    var sm = document.getElementById('ace_settingsmenu');
+    if (!sm)    
+        overlayPage(editor, generateSettingsMenu(editor), '0', '0', '0');
+}
+
+/**
+ * Initializes the settings menu extension. It adds the showSettingsMenu
+ *  method to the given editor object and adds the showSettingsMenu command
+ *  to the editor with appropriate keyboard shortcuts.
+ * @param {ace.Editor} editor An instance of the Editor.
+ */
+module.exports.init = function(editor) {
+    var Editor = require("ace/editor").Editor;
+    Editor.prototype.showSettingsMenu = function() {
+        showSettingsMenu(this);
+    };
+};
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/couchdb/blob/9abd128c/src/fauxton/assets/js/libs/ace/ext/spellcheck.js
----------------------------------------------------------------------
diff --git a/src/fauxton/assets/js/libs/ace/ext/spellcheck.js b/src/fauxton/assets/js/libs/ace/ext/spellcheck.js
new file mode 100644
index 0000000..08bf218
--- /dev/null
+++ b/src/fauxton/assets/js/libs/ace/ext/spellcheck.js
@@ -0,0 +1,69 @@
+define(function(require, exports, module) {
+"use strict";
+var event = require("../lib/event");
+
+exports.contextMenuHandler = function(e){
+    var host = e.target;
+    var text = host.textInput.getElement();
+    if (!host.selection.isEmpty())
+        return;
+    var c = host.getCursorPosition();
+    var r = host.session.getWordRange(c.row, c.column);
+    var w = host.session.getTextRange(r);
+
+    host.session.tokenRe.lastIndex = 0;
+    if (!host.session.tokenRe.test(w))
+        return;
+    var PLACEHOLDER = "\x01\x01";
+    var value = w + " " + PLACEHOLDER;
+    text.value = value;
+    text.setSelectionRange(w.length, w.length + 1);
+    text.setSelectionRange(0, 0);
+    text.setSelectionRange(0, w.length);
+
+    var afterKeydown = false;
+    event.addListener(text, "keydown", function onKeydown() {
+        event.removeListener(text, "keydown", onKeydown);
+        afterKeydown = true;
+    });
+
+    host.textInput.setInputHandler(function(newVal) {
+        console.log(newVal , value, text.selectionStart, text.selectionEnd)
+        if (newVal == value)
+            return '';
+        if (newVal.lastIndexOf(value, 0) === 0)
+            return newVal.slice(value.length);
+        if (newVal.substr(text.selectionEnd) == value)
+            return newVal.slice(0, -value.length);
+        if (newVal.slice(-2) == PLACEHOLDER) {
+            var val = newVal.slice(0, -2);
+            if (val.slice(-1) == " ") {
+                if (afterKeydown)
+                    return val.substring(0, text.selectionEnd);
+                val = val.slice(0, -1);
+                host.session.replace(r, val);
+                return "";
+            }
+        }
+
+        return newVal;
+    });
+};
+// todo support highlighting with typo.js
+var Editor = require("../editor").Editor;
+require("../config").defineOptions(Editor.prototype, "editor", {
+    spellcheck: {
+        set: function(val) {
+            var text = this.textInput.getElement();
+            text.spellcheck = !!val;
+            if (!val)
+                this.removeListener("nativecontextmenu", exports.contextMenuHandler);
+            else
+                this.on("nativecontextmenu", exports.contextMenuHandler);
+        },
+        value: true
+    }
+});
+
+});
+

http://git-wip-us.apache.org/repos/asf/couchdb/blob/9abd128c/src/fauxton/assets/js/libs/ace/ext/split.js
----------------------------------------------------------------------
diff --git a/src/fauxton/assets/js/libs/ace/ext/split.js b/src/fauxton/assets/js/libs/ace/ext/split.js
new file mode 100644
index 0000000..8316562
--- /dev/null
+++ b/src/fauxton/assets/js/libs/ace/ext/split.js
@@ -0,0 +1,40 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Distributed under the BSD license:
+ *
+ * Copyright (c) 2010, Ajax.org B.V.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Ajax.org B.V. nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define(function(require, exports, module) {
+"use strict";
+
+/**
+ * this is experimental, and subject to change, use at your own risk!
+ */
+module.exports = require("../split");
+
+});
+

http://git-wip-us.apache.org/repos/asf/couchdb/blob/9abd128c/src/fauxton/assets/js/libs/ace/ext/static.css
----------------------------------------------------------------------
diff --git a/src/fauxton/assets/js/libs/ace/ext/static.css b/src/fauxton/assets/js/libs/ace/ext/static.css
new file mode 100644
index 0000000..bd47978
--- /dev/null
+++ b/src/fauxton/assets/js/libs/ace/ext/static.css
@@ -0,0 +1,23 @@
+.ace_static_highlight {
+   font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', 'Droid Sans Mono', monospace;
+   font-size: 12px;
+}
+
+.ace_static_highlight .ace_gutter {
+    width: 25px !important;
+    display: block;
+    float: left;
+    text-align: right;
+    padding: 0 3px 0 0;
+    margin-right: 3px;
+    position: static !important;
+}
+
+.ace_static_highlight .ace_line { clear: both; }
+
+.ace_static_highlight .ace_gutter-cell {
+  -moz-user-select: -moz-none;
+  -khtml-user-select: none;
+  -webkit-user-select: none;
+  user-select: none;
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/couchdb/blob/9abd128c/src/fauxton/assets/js/libs/ace/ext/static_highlight.js
----------------------------------------------------------------------
diff --git a/src/fauxton/assets/js/libs/ace/ext/static_highlight.js b/src/fauxton/assets/js/libs/ace/ext/static_highlight.js
new file mode 100644
index 0000000..2119653
--- /dev/null
+++ b/src/fauxton/assets/js/libs/ace/ext/static_highlight.js
@@ -0,0 +1,180 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Distributed under the BSD license:
+ *
+ * Copyright (c) 2010, Ajax.org B.V.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Ajax.org B.V. nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define(function(require, exports, module) {
+"use strict";
+
+var EditSession = require("../edit_session").EditSession;
+var TextLayer = require("../layer/text").Text;
+var baseStyles = require("../requirejs/text!./static.css");
+var config = require("../config");
+var dom = require("../lib/dom");
+/**
+ * Transforms a given input code snippet into HTML using the given mode
+ *
+ * @param {string} input Code snippet
+ * @param {string|mode} mode String specifying the mode to load such as
+ *  `ace/mode/javascript` or, a mode loaded from `/ace/mode`
+ *  (use 'ServerSideHiglighter.getMode').
+ * @param {string|theme} theme String specifying the theme to load such as
+ *  `ace/theme/twilight` or, a theme loaded from `/ace/theme`.
+ * @param {number} lineStart A number indicating the first line number. Defaults
+ *  to 1.
+ * @param {boolean} disableGutter Specifies whether or not to disable the gutter.
+ *  `true` disables the gutter, `false` enables the gutter. Defaults to `false`.
+ * @param {function} callback When specifying the mode or theme as a string,
+ *  this method has no return value and you must specify a callback function. The
+ *  callback will receive the rendered object containing the properties `html`
+ *  and `css`.
+ * @returns {object} An object containing the properties `html` and `css`.
+ */
+
+exports.render = function(input, mode, theme, lineStart, disableGutter, callback) {
+    var waiting = 0;
+    var modeCache = EditSession.prototype.$modes;
+
+    // if either the theme or the mode were specified as objects
+    // then we need to lazily load them.
+    if (typeof theme == "string") {
+        waiting++;
+        config.loadModule(['theme', theme], function(m) {
+            theme = m;
+            --waiting || done();
+        });
+    }
+
+    if (typeof mode == "string") {
+        waiting++;
+        config.loadModule(['mode', mode], function(m) {
+            if (!modeCache[mode]) modeCache[mode] = new m.Mode();
+            mode = modeCache[mode];
+            --waiting || done();
+        });
+    }
+
+    // loads or passes the specified mode module then calls renderer
+    function done() {
+        var result = exports.renderSync(input, mode, theme, lineStart, disableGutter);
+        return callback ? callback(result) : result;
+    }
+    return waiting || done();
+};
+
+/* 
+ * Transforms a given input code snippet into HTML using the given mode
+ * @param {string} input Code snippet
+ * @param {mode} mode Mode loaded from /ace/mode (use 'ServerSideHiglighter.getMode')
+ * @param {string} r Code snippet
+ * @returns {object} An object containing: html, css
+ */
+
+exports.renderSync = function(input, mode, theme, lineStart, disableGutter) {
+    lineStart = parseInt(lineStart || 1, 10);
+
+    var session = new EditSession("");
+    session.setUseWorker(false);
+    session.setMode(mode);
+
+    var textLayer = new TextLayer(document.createElement("div"));
+    textLayer.setSession(session);
+    textLayer.config = {
+        characterWidth: 10,
+        lineHeight: 20
+    };
+
+    session.setValue(input);
+
+    var stringBuilder = [];
+    var length =  session.getLength();
+
+    for(var ix = 0; ix < length; ix++) {
+        stringBuilder.push("<div class='ace_line'>");
+        if (!disableGutter)
+            stringBuilder.push("<span class='ace_gutter ace_gutter-cell' unselectable='on'>" + (ix + lineStart) + "</span>");
+        textLayer.$renderLine(stringBuilder, ix, true, false);
+        stringBuilder.push("</div>");
+    }
+
+    // let's prepare the whole html
+    var html = "<div class='" + theme.cssClass + "'>" +
+        "<div class='ace_static_highlight'>" +
+            stringBuilder.join("") +
+        "</div>" +
+    "</div>";
+
+    textLayer.destroy();
+
+    return {
+        css: baseStyles + theme.cssText,
+        html: html
+    };
+};
+
+
+
+exports.highlight = function(el, opts, callback) {
+    var m = el.className.match(/lang-(\w+)/);
+    var mode = opts.mode || m && ("ace/mode/" + m[1]);
+    if (!mode)
+        return false;
+    var theme = opts.theme || "ace/theme/textmate";
+    
+    var data = "";
+    var nodes = [];
+
+    if (el.firstElementChild) {
+        var textLen = 0;
+        for (var i = 0; i < el.childNodes.length; i++) {
+            var ch = el.childNodes[i];
+            if (ch.nodeType == 3) {
+                textLen += ch.data.length;
+                data += ch.data;
+            } else {
+                nodes.push(textLen, ch);
+            }
+        }
+    } else {
+        data = dom.getInnerText(el);
+    }
+    
+    exports.render(data, mode, theme, 1, true, function (highlighted) {
+        dom.importCssString(highlighted.css, "ace_highlight");
+        el.innerHTML = highlighted.html;
+        var container = el.firstChild.firstChild
+        for (var i = 0; i < nodes.length; i += 2) {
+            var pos = highlighted.session.doc.indexToPosition(nodes[i])
+            var node = nodes[i + 1];
+            var lineEl = container.children[pos.row];
+            lineEl && lineEl.appendChild(nodes[i+1]);
+        }
+        callback && callback();
+    });
+};
+});

http://git-wip-us.apache.org/repos/asf/couchdb/blob/9abd128c/src/fauxton/assets/js/libs/ace/ext/static_highlight_test.js
----------------------------------------------------------------------
diff --git a/src/fauxton/assets/js/libs/ace/ext/static_highlight_test.js b/src/fauxton/assets/js/libs/ace/ext/static_highlight_test.js
new file mode 100644
index 0000000..bdbecbf
--- /dev/null
+++ b/src/fauxton/assets/js/libs/ace/ext/static_highlight_test.js
@@ -0,0 +1,89 @@
+if (typeof process !== "undefined") {
+    require("amd-loader");
+    require("../test/mockdom");
+}
+
+define(function(require, exports, module) {
+"use strict";
+
+var assert = require("assert");
+var highlighter = require("./static_highlight");
+var JavaScriptMode = require("../mode/javascript").Mode;
+var TextMode = require("../mode/text").Mode;
+
+// Execution ORDER: test.setUpSuite, setUp, testFn, tearDown, test.tearDownSuite
+module.exports = {
+    timeout: 10000,
+
+    "test simple snippet": function(next) {
+        var theme = require("../theme/tomorrow");
+        var snippet = [
+            "/** this is a function",
+            "*",
+            "*/",
+            "function hello (a, b, c) {",
+            "    console.log(a * b + c + 'sup$');",
+            "}"
+        ].join("\n");
+        var mode = new JavaScriptMode();
+
+        var result = highlighter.render(snippet, mode, theme);
+        assert.equal(result.html, "<div class='ace-tomorrow'><div class='ace_static_highlight'><div class='ace_line'><span class='ace_gutter ace_gutter-cell' unselectable='on'>1</span><span class='ace_comment ace_doc'>/**\xa0this\xa0is\xa0a\xa0function</span></div><div class='ace_line'><span class='ace_gutter ace_gutter-cell' unselectable='on'>2</span><span class='ace_comment ace_doc'>*</span></div><div class='ace_line'><span class='ace_gutter ace_gutter-cell' unselectable='on'>3</span><span class='ace_comment ace_doc'>*/</span></div><div class='ace_line'><span class='ace_gutter ace_gutter-cell' unselectable='on'>4</span><span class='ace_storage ace_type'>function</span>\xa0<span class='ace_entity ace_name ace_function'>hello</span>\xa0<span class='ace_paren ace_lparen'>(</span><span class='ace_variable ace_parameter'>a</span><span class='ace_punctuation ace_operator'>,\xa0</span><span class='ace_variable ace_parameter'>b</span><span class='ace_punctuation ace_operator'>,\xa0</span>
 <span class='ace_variable ace_parameter'>c</span><span class='ace_paren ace_rparen'>)</span>\xa0<span class='ace_paren ace_lparen'>{</span></div><div class='ace_line'><span class='ace_gutter ace_gutter-cell' unselectable='on'>5</span>\xa0\xa0\xa0\xa0<span class='ace_storage ace_type'>console</span><span class='ace_punctuation ace_operator'>.</span><span class='ace_support ace_function ace_firebug'>log</span><span class='ace_paren ace_lparen'>(</span><span class='ace_identifier'>a</span>\xa0<span class='ace_keyword ace_operator'>*</span>\xa0<span class='ace_identifier'>b</span>\xa0<span class='ace_keyword ace_operator'>+</span>\xa0<span class='ace_identifier'>c</span>\xa0<span class='ace_keyword ace_operator'>+</span>\xa0<span class='ace_string'>'sup$'</span><span class='ace_paren ace_rparen'>)</span><span class='ace_punctuation ace_operator'>;</span></div><div class='ace_line'><span class='ace_gutter ace_gutter-cell' unselectable='on'>6</span><span class='ace_paren ace_rparen'>}</sp
 an></div></div></div>");
+        assert.ok(!!result.css);
+        next();
+    },
+
+    "test css from theme is used": function(next) {
+        var theme = require("../theme/tomorrow");
+        var snippet = [
+            "/** this is a function",
+            "*",
+            "*/",
+            "function hello (a, b, c) {",
+            "    console.log(a * b + c + 'sup?');",
+            "}"
+        ].join("\n");
+        var mode = new JavaScriptMode();
+
+        var result = highlighter.render(snippet, mode, theme);
+
+        assert.ok(result.css.indexOf(theme.cssText) !== -1);
+
+        next();
+    },
+
+    "test theme classname should be in output html": function(next) {
+        var theme = require("../theme/tomorrow");
+        var snippet = [
+            "/** this is a function",
+            "*",
+            "*/",
+            "function hello (a, b, c) {",
+            "    console.log(a * b + c + 'sup?');",
+            "}"
+        ].join("\n");
+        var mode = new JavaScriptMode();
+
+        var result = highlighter.render(snippet, mode, theme);
+        assert.equal(!!result.html.match(/<div class='ace-tomorrow'>/), true);
+
+        next();
+    },
+    
+    "test js string replace specials": function(next) {
+        var theme = require("../theme/tomorrow");
+        var snippet = "$'$1$2$$$&";
+        var mode = new TextMode();
+
+        var result = highlighter.render(snippet, mode, theme);
+        assert.ok(result.html.indexOf(snippet) != -1);
+
+        next();
+    }
+};
+
+});
+
+if (typeof module !== "undefined" && module === require.main) {
+    require("asyncjs").test.testcase(module.exports).exec();
+}

http://git-wip-us.apache.org/repos/asf/couchdb/blob/9abd128c/src/fauxton/assets/js/libs/ace/ext/statusbar.js
----------------------------------------------------------------------
diff --git a/src/fauxton/assets/js/libs/ace/ext/statusbar.js b/src/fauxton/assets/js/libs/ace/ext/statusbar.js
new file mode 100644
index 0000000..666febf
--- /dev/null
+++ b/src/fauxton/assets/js/libs/ace/ext/statusbar.js
@@ -0,0 +1,49 @@
+define(function(require, exports, module) {
+"use strict";
+/** simple statusbar **/
+var dom = require("ace/lib/dom");
+var lang = require("ace/lib/lang");
+
+var StatusBar = function(editor, parentNode) {
+    this.element = dom.createElement("div");
+    this.element.className = "ace_status-indicator";
+    this.element.style.cssText = "display: inline-block;";
+    parentNode.appendChild(this.element);
+
+    var statusUpdate = lang.delayedCall(function(){
+        this.updateStatus(editor)
+    }.bind(this));
+    editor.on("changeStatus", function() {
+        statusUpdate.schedule(100);
+    });
+    editor.on("changeSelection", function() {
+        statusUpdate.schedule(100);
+    });
+};
+
+(function(){
+    this.updateStatus = function(editor) {
+        var status = [];
+        function add(str, separator) {
+            str && status.push(str, separator || "|");
+        }
+
+        if (editor.$vimModeHandler)
+            add(editor.$vimModeHandler.getStatusText());
+        else if (editor.commands.recording)
+            add("REC");
+
+        var c = editor.selection.lead;
+        add(c.row + ":" + c.column, " ");
+        if (!editor.selection.isEmpty()) {
+            var r = editor.getSelectionRange();
+            add("(" + (r.end.row - r.start.row) + ":"  +(r.end.column - r.start.column) + ")");
+        }
+        status.pop();
+        this.element.textContent = status.join("");
+    };
+}).call(StatusBar.prototype);
+
+exports.StatusBar = StatusBar;
+
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/couchdb/blob/9abd128c/src/fauxton/assets/js/libs/ace/ext/textarea.js
----------------------------------------------------------------------
diff --git a/src/fauxton/assets/js/libs/ace/ext/textarea.js b/src/fauxton/assets/js/libs/ace/ext/textarea.js
new file mode 100644
index 0000000..86b299e
--- /dev/null
+++ b/src/fauxton/assets/js/libs/ace/ext/textarea.js
@@ -0,0 +1,547 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Distributed under the BSD license:
+ *
+ * Copyright (c) 2010, Ajax.org B.V.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Ajax.org B.V. nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define(function(require, exports, module) {
+"use strict";
+
+var event = require("../lib/event");
+var UA = require("../lib/useragent");
+var net = require("../lib/net");
+var ace = require("../ace");
+
+require("../theme/textmate");
+
+module.exports = exports = ace;
+
+/*
+ * Returns the CSS property of element.
+ *   1) If the CSS property is on the style object of the element, use it, OR
+ *   2) Compute the CSS property
+ *
+ * If the property can't get computed, is 'auto' or 'intrinsic', the former
+ * calculated property is used (this can happen in cases where the textarea
+ * is hidden and has no dimension styles).
+ */
+var getCSSProperty = function(element, container, property) {
+    var ret = element.style[property];
+
+    if (!ret) {
+        if (window.getComputedStyle) {
+            ret = window.getComputedStyle(element, '').getPropertyValue(property);
+        } else {
+            ret = element.currentStyle[property];
+        }
+    }
+
+    if (!ret || ret == 'auto' || ret == 'intrinsic') {
+        ret = container.style[property];
+    }
+    return ret;
+};
+
+function applyStyles(elm, styles) {
+    for (var style in styles) {
+        elm.style[style] = styles[style];
+    }
+}
+
+function setupContainer(element, getValue) {
+    if (element.type != 'textarea') {
+        throw new Error("Textarea required!");
+    }
+
+    var parentNode = element.parentNode;
+
+    // This will hold the editor.
+    var container = document.createElement('div');
+
+    // To put Ace in the place of the textarea, we have to copy a few of the
+    // textarea's style attributes to the div container.
+    //
+    // The problem is that the properties have to get computed (they might be
+    // defined by a CSS file on the page - you can't access such rules that
+    // apply to an element via elm.style). Computed properties are converted to
+    // pixels although the dimension might be given as percentage. When the
+    // window resizes, the dimensions defined by percentages changes, so the
+    // properties have to get recomputed to get the new/true pixels.
+    var resizeEvent = function() {
+        var style = 'position:relative;';
+        [
+            'margin-top', 'margin-left', 'margin-right', 'margin-bottom'
+        ].forEach(function(item) {
+            style += item + ':' +
+                        getCSSProperty(element, container, item) + ';';
+        });
+
+        // Calculating the width/height of the textarea is somewhat tricky. To
+        // do it right, you have to include the paddings to the sides as well
+        // (eg. width = width + padding-left, -right).  This works well, as
+        // long as the width of the element is not set or given in pixels. In
+        // this case and after the textarea is hidden, getCSSProperty(element,
+        // container, 'width') will still return pixel value. If the element
+        // has realtiv dimensions (e.g. width='95<percent>')
+        // getCSSProperty(...) will return pixel values only as long as the
+        // textarea is visible. After it is hidden getCSSProperty will return
+        // the relative dimensions as they are set on the element (in the case
+        // of width, 95<percent>).
+        // Making the sum of pixel vaules (e.g. padding) and realtive values
+        // (e.g. <percent>) is not possible. As such the padding styles are
+        // ignored.
+
+        // The complete width is the width of the textarea + the padding
+        // to the left and right.
+        var width = getCSSProperty(element, container, 'width') || (element.clientWidth + "px");
+        var height = getCSSProperty(element, container, 'height')  || (element.clientHeight + "px");
+        style += 'height:' + height + ';width:' + width + ';';
+
+        // Set the display property to 'inline-block'.
+        style += 'display:inline-block;';
+        container.setAttribute('style', style);
+    };
+    event.addListener(window, 'resize', resizeEvent);
+
+    // Call the resizeEvent once, so that the size of the container is
+    // calculated.
+    resizeEvent();
+
+    // Insert the div container after the element.
+    parentNode.insertBefore(container, element.nextSibling);
+
+    // Override the forms onsubmit function. Set the innerHTML and value
+    // of the textarea before submitting.
+    while (parentNode !== document) {
+        if (parentNode.tagName.toUpperCase() === 'FORM') {
+            var oldSumit = parentNode.onsubmit;
+            // Override the onsubmit function of the form.
+            parentNode.onsubmit = function(evt) {
+                element.value = getValue();
+                // If there is a onsubmit function already, then call
+                // it with the current context and pass the event.
+                if (oldSumit) {
+                    oldSumit.call(this, evt);
+                }
+            };
+            break;
+        }
+        parentNode = parentNode.parentNode;
+    }
+    return container;
+}
+
+exports.transformTextarea = function(element, loader) {
+    var session;
+    var container = setupContainer(element, function() {
+        return session.getValue();
+    });
+
+    // Hide the element.
+    element.style.display = 'none';
+    container.style.background = 'white';
+
+    //
+    var editorDiv = document.createElement("div");
+    applyStyles(editorDiv, {
+        top: "0px",
+        left: "0px",
+        right: "0px",
+        bottom: "0px",
+        border: "1px solid gray",
+        position: "absolute"
+    });
+    container.appendChild(editorDiv);
+
+    var settingOpener = document.createElement("div");
+    applyStyles(settingOpener, {
+        position: "absolute",
+        right: "0px",
+        bottom: "0px",
+        background: "red",
+        cursor: "nw-resize",
+        borderStyle: "solid",
+        borderWidth: "9px 8px 10px 9px",
+        width: "2px",
+        borderColor: "lightblue gray gray lightblue",
+        zIndex: 101
+    });
+
+    var settingDiv = document.createElement("div");
+    var settingDivStyles = {
+        top: "0px",
+        left: "20%",
+        right: "0px",
+        bottom: "0px",
+        position: "absolute",
+        padding: "5px",
+        zIndex: 100,
+        color: "white",
+        display: "none",
+        overflow: "auto",
+        fontSize: "14px",
+        boxShadow: "-5px 2px 3px gray"
+    };
+    if (!UA.isOldIE) {
+        settingDivStyles.backgroundColor = "rgba(0, 0, 0, 0.6)";
+    } else {
+        settingDivStyles.backgroundColor = "#333";
+    }
+
+    applyStyles(settingDiv, settingDivStyles);
+    container.appendChild(settingDiv);
+
+    // Power up ace on the textarea:
+    var options = {};
+
+    var editor = ace.edit(editorDiv);
+    session = editor.getSession();
+
+    session.setValue(element.value || element.innerHTML);
+    editor.focus();
+
+    // Add the settingPanel opener to the editor's div.
+    container.appendChild(settingOpener);
+
+    // Create the API.
+    setupApi(editor, editorDiv, settingDiv, ace, options, loader);
+
+    // Create the setting's panel.
+    setupSettingPanel(settingDiv, settingOpener, editor, options);
+
+    var state = "";
+    event.addListener(settingOpener, "mousemove", function(e) {
+        var rect = this.getBoundingClientRect();
+        var x = e.clientX - rect.left, y = e.clientY - rect.top;
+        if (x + y < (rect.width + rect.height)/2) {
+            this.style.cursor = "pointer";
+            state = "toggle";
+        } else {
+            state = "resize";
+            this.style.cursor = "nw-resize";
+        }
+    });
+
+    event.addListener(settingOpener, "mousedown", function(e) {
+        if (state == "toggle") {
+            editor.setDisplaySettings();
+            return;
+        }
+        container.style.zIndex = 100000;
+        var rect = container.getBoundingClientRect();
+        var startX = rect.width  + rect.left - e.clientX;
+        var startY = rect.height  + rect.top - e.clientY;
+        event.capture(settingOpener, function(e) {
+            container.style.width = e.clientX - rect.left + startX + "px";
+            container.style.height = e.clientY - rect.top + startY + "px";
+            editor.resize();
+        }, function() {});
+    });
+
+    return editor;
+};
+
+function load(url, module, callback) {
+    net.loadScript(url, function() {
+        require([module], callback);
+    });
+}
+
+function setupApi(editor, editorDiv, settingDiv, ace, options, loader) {
+    var session = editor.getSession();
+    var renderer = editor.renderer;
+    loader = loader || load;
+
+    function toBool(value) {
+        return value === "true" || value == true;
+    }
+
+    editor.setDisplaySettings = function(display) {
+        if (display == null)
+            display = settingDiv.style.display == "none";
+        if (display) {
+            settingDiv.style.display = "block";
+            settingDiv.hideButton.focus();
+            editor.on("focus", function onFocus() {
+                editor.removeListener("focus", onFocus);
+                settingDiv.style.display = "none";
+            });
+        } else {
+            editor.focus();
+        }
+    };
+
+    editor.$setOption = editor.setOption;
+    editor.setOption = function(key, value) {
+        if (options[key] == value) return;
+
+        switch (key) {
+            case "mode":
+                if (value != "text") {
+                    // Load the required mode file. Files get loaded only once.
+                    loader("mode-" + value + ".js", "ace/mode/" + value, function() {
+                        var aceMode = require("../mode/" + value).Mode;
+                        session.setMode(new aceMode());
+                    });
+                } else {
+                    session.setMode(new (require("../mode/text").Mode));
+                }
+            break;
+
+            case "theme":
+                if (value != "textmate") {
+                    // Load the required theme file. Files get loaded only once.
+                    loader("theme-" + value + ".js", "ace/theme/" + value, function() {
+                        editor.setTheme("ace/theme/" + value);
+                    });
+                } else {
+                    editor.setTheme("ace/theme/textmate");
+                }
+            break;
+
+            case "fontSize":
+                editorDiv.style.fontSize = value;
+            break;
+
+            case "keybindings":
+                switch (value) {
+                    case "vim":
+                        editor.setKeyboardHandler("ace/keyboard/vim");
+                        break;
+                    case "emacs":
+                        editor.setKeyboardHandler("ace/keyboard/emacs");
+                        break;
+                    default:
+                        editor.setKeyboardHandler(null);
+                }
+            break;
+
+            case "softWrap":
+                switch (value) {
+                    case "off":
+                        session.setUseWrapMode(false);
+                        renderer.setPrintMarginColumn(80);
+                    break;
+                    case "40":
+                        session.setUseWrapMode(true);
+                        session.setWrapLimitRange(40, 40);
+                        renderer.setPrintMarginColumn(40);
+                    break;
+                    case "80":
+                        session.setUseWrapMode(true);
+                        session.setWrapLimitRange(80, 80);
+                        renderer.setPrintMarginColumn(80);
+                    break;
+                    case "free":
+                        session.setUseWrapMode(true);
+                        session.setWrapLimitRange(null, null);
+                        renderer.setPrintMarginColumn(80);
+                    break;
+                }
+            break;
+            
+            default:
+                editor.$setOption(key, toBool(value));
+        }
+
+        options[key] = value;
+    };
+
+    editor.getOption = function(key) {
+        return options[key];
+    };
+
+    editor.getOptions = function() {
+        return options;
+    };
+
+    editor.setOptions(exports.options);
+
+    return editor;
+}
+
+function setupSettingPanel(settingDiv, settingOpener, editor, options) {
+    var BOOL = null;
+
+    var desc = {
+        mode:            "Mode:",
+        gutter:          "Display Gutter:",
+        theme:           "Theme:",
+        fontSize:        "Font Size:",
+        softWrap:        "Soft Wrap:",
+        keybindings:     "Keyboard",
+        showPrintMargin: "Show Print Margin:",
+        useSoftTabs:     "Use Soft Tabs:",
+        showInvisibles:  "Show Invisibles"
+    };
+
+    var optionValues = {
+        mode: {
+            text:       "Plain",
+            javascript: "JavaScript",
+            xml:        "XML",
+            html:       "HTML",
+            css:        "CSS",
+            scss:       "SCSS",
+            python:     "Python",
+            php:        "PHP",
+            java:       "Java",
+            ruby:       "Ruby",
+            c_cpp:      "C/C++",
+            coffee:     "CoffeeScript",
+            json:       "json",
+            perl:       "Perl",
+            clojure:    "Clojure",
+            ocaml:      "OCaml",
+            csharp:     "C#",
+            haxe:       "haXe",
+            svg:        "SVG",
+            textile:    "Textile",
+            groovy:     "Groovy",
+            liquid:     "Liquid",
+            Scala:      "Scala"
+        },
+        theme: {
+            clouds:           "Clouds",
+            clouds_midnight:  "Clouds Midnight",
+            cobalt:           "Cobalt",
+            crimson_editor:   "Crimson Editor",
+            dawn:             "Dawn",
+            eclipse:          "Eclipse",
+            idle_fingers:     "Idle Fingers",
+            kr_theme:         "Kr Theme",
+            merbivore:        "Merbivore",
+            merbivore_soft:   "Merbivore Soft",
+            mono_industrial:  "Mono Industrial",
+            monokai:          "Monokai",
+            pastel_on_dark:   "Pastel On Dark",
+            solarized_dark:   "Solarized Dark",
+            solarized_light:  "Solarized Light",
+            textmate:         "Textmate",
+            twilight:         "Twilight",
+            vibrant_ink:      "Vibrant Ink"
+        },
+        gutter: BOOL,
+        fontSize: {
+            "10px": "10px",
+            "11px": "11px",
+            "12px": "12px",
+            "14px": "14px",
+            "16px": "16px"
+        },
+        softWrap: {
+            off:    "Off",
+            40:     "40",
+            80:     "80",
+            free:   "Free"
+        },
+        keybindings: {
+            ace: "ace",
+            vim: "vim",
+            emacs: "emacs"
+        },
+        showPrintMargin:    BOOL,
+        useSoftTabs:        BOOL,
+        showInvisibles:     BOOL
+    };
+
+    var table = [];
+    table.push("<table><tr><th>Setting</th><th>Value</th></tr>");
+
+    function renderOption(builder, option, obj, cValue) {
+        if (!obj) {
+            builder.push(
+                "<input type='checkbox' title='", option, "' ",
+                    cValue == "true" ? "checked='true'" : "",
+               "'></input>"
+            );
+            return;
+        }
+        builder.push("<select title='" + option + "'>");
+        for (var value in obj) {
+            builder.push("<option value='" + value + "' ");
+
+            if (cValue == value) {
+                builder.push(" selected ");
+            }
+
+            builder.push(">",
+                obj[value],
+                "</option>");
+        }
+        builder.push("</select>");
+    }
+
+    for (var option in options) {
+        table.push("<tr><td>", desc[option], "</td>");
+        table.push("<td>");
+        renderOption(table, option, optionValues[option], options[option]);
+        table.push("</td></tr>");
+    }
+    table.push("</table>");
+    settingDiv.innerHTML = table.join("");
+
+    var onChange = function(e) {
+        var select = e.currentTarget;
+        editor.setOption(select.title, select.value);
+    };
+    var onClick = function(e) {
+        var cb = e.currentTarget;
+        editor.setOption(cb.title, cb.checked);
+    };
+    var selects = settingDiv.getElementsByTagName("select");
+    for (var i = 0; i < selects.length; i++)
+        selects[i].onchange = onChange;
+    var cbs = settingDiv.getElementsByTagName("input");
+    for (var i = 0; i < cbs.length; i++)
+        cbs[i].onclick = onClick;
+
+
+    var button = document.createElement("input");
+    button.type = "button";
+    button.value = "Hide";
+    event.addListener(button, "click", function() {
+        editor.setDisplaySettings(false);
+    });
+    settingDiv.appendChild(button);
+    settingDiv.hideButton = button;
+}
+
+// Default startup options.
+exports.options = {
+    mode:               "text",
+    theme:              "textmate",
+    gutter:             "false",
+    fontSize:           "12px",
+    softWrap:           "off",
+    keybindings:        "ace",
+    showPrintMargin:    "false",
+    useSoftTabs:        "true",
+    showInvisibles:     "false"
+};
+
+});

http://git-wip-us.apache.org/repos/asf/couchdb/blob/9abd128c/src/fauxton/assets/js/libs/ace/ext/themelist.js
----------------------------------------------------------------------
diff --git a/src/fauxton/assets/js/libs/ace/ext/themelist.js b/src/fauxton/assets/js/libs/ace/ext/themelist.js
new file mode 100644
index 0000000..1032f72
--- /dev/null
+++ b/src/fauxton/assets/js/libs/ace/ext/themelist.js
@@ -0,0 +1,79 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Distributed under the BSD license:
+ *
+ * Copyright (c) 2013 Matthew Christopher Kastor-Inare III, Atropa Inc. Intl
+ * All rights reserved.
+ *
+ * Contributed to Ajax.org under the BSD license.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Ajax.org B.V. nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/*jslint indent: 4, maxerr: 50, white: true, browser: true, vars: true*/
+/*global define, require */
+
+/**
+ * Generates a list of themes available when ace was built.
+ * @fileOverview Generates a list of themes available when ace was built.
+ * @author <a href="mailto:matthewkastor@gmail.com">
+ *  Matthew Christopher Kastor-Inare III </a><br />
+ *  ☭ Hial Atropa!! ☭
+ */
+
+define(function(require, exports, module) {
+"use strict";
+
+/**
+ * An array containing information about available themes.
+ */
+module.exports.themes = require('ace/ext/themelist_utils/themes').themes;
+
+/**
+ * Creates a theme description.
+ * @param {string} name The file name of the theme.
+ * @returns {ThemeDescription} Returns a theme description object which has
+ *  three properties: the name gives the filename, the desc gives a menu
+ *  friendly name, and the theme gives the string to set the theme with
+ *  `setTheme`
+ */
+module.exports.ThemeDescription = function(name) {
+    this.name = name;
+    this.desc = name.split('_'
+        ).map(
+            function(namePart) {
+                return namePart[0].toUpperCase() + namePart.slice(1);
+            }
+        ).join(' ');
+    this.theme = "ace/theme/" + name;
+};
+
+module.exports.themesByName = {};
+
+module.exports.themes = module.exports.themes.map(function(name) {
+    module.exports.themesByName[name] = new module.exports.ThemeDescription(name);
+    return module.exports.themesByName[name];
+});
+
+});
+

http://git-wip-us.apache.org/repos/asf/couchdb/blob/9abd128c/src/fauxton/assets/js/libs/ace/ext/themelist_utils/themes.js
----------------------------------------------------------------------
diff --git a/src/fauxton/assets/js/libs/ace/ext/themelist_utils/themes.js b/src/fauxton/assets/js/libs/ace/ext/themelist_utils/themes.js
new file mode 100644
index 0000000..2e490c9
--- /dev/null
+++ b/src/fauxton/assets/js/libs/ace/ext/themelist_utils/themes.js
@@ -0,0 +1,36 @@
+define(function(require, exports, module) {
+
+module.exports.themes = [
+    "ambiance",
+    "chaos",
+    "chrome",
+    "clouds",
+    "clouds_midnight",
+    "cobalt",
+    "crimson_editor",
+    "dawn",
+    "dreamweaver",
+    "eclipse",
+    "github",
+    "idle_fingers",
+    "kr_theme",
+    "merbivore",
+    "merbivore_soft",
+    "mono_industrial",
+    "monokai",
+    "pastel_on_dark",
+    "solarized_dark",
+    "solarized_light",
+    "terminal",
+    "textmate",
+    "tomorrow",
+    "tomorrow_night",
+    "tomorrow_night_blue",
+    "tomorrow_night_bright",
+    "tomorrow_night_eighties",
+    "twilight",
+    "vibrant_ink",
+    "xcode"
+];
+
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/couchdb/blob/9abd128c/src/fauxton/assets/js/libs/ace/ext/whitespace.js
----------------------------------------------------------------------
diff --git a/src/fauxton/assets/js/libs/ace/ext/whitespace.js b/src/fauxton/assets/js/libs/ace/ext/whitespace.js
new file mode 100644
index 0000000..83486fb
--- /dev/null
+++ b/src/fauxton/assets/js/libs/ace/ext/whitespace.js
@@ -0,0 +1,212 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Distributed under the BSD license:
+ *
+ * Copyright (c) 2010, Ajax.org B.V.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Ajax.org B.V. nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define(function(require, exports, module) {
+"use strict";
+
+var lang = require("../lib/lang");
+
+// based on http://www.freehackers.org/Indent_Finder
+exports.$detectIndentation = function(lines, fallback) {
+    var stats = [];
+    var changes = [];
+    var tabIndents = 0;
+    var prevSpaces = 0;
+    var max = Math.min(lines.length, 1000);
+    for (var i = 0; i < max; i++) {
+        var line = lines[i];
+        // ignore empty and comment lines
+        if (!/^\s*[^*+\-\s]/.test(line))
+            continue;
+
+        var tabs = line.match(/^\t*/)[0].length;
+        if (line[0] == "\t")
+            tabIndents++;
+
+        var spaces = line.match(/^ */)[0].length;
+        if (spaces && line[spaces] != "\t") {
+            var diff = spaces - prevSpaces;
+            if (diff > 0 && !(prevSpaces%diff) && !(spaces%diff))
+                changes[diff] = (changes[diff] || 0) + 1;
+
+            stats[spaces] = (stats[spaces] || 0) + 1;
+        }
+        prevSpaces = spaces;
+
+        // ignore lines ending with backslash
+        while (line[line.length - 1] == "\\")
+            line = lines[i++];
+    }
+
+    function getScore(indent) {
+        var score = 0;
+        for (var i = indent; i < stats.length; i += indent)
+            score += stats[i] || 0;
+        return score;
+    }
+
+    var changesTotal = changes.reduce(function(a,b){return a+b}, 0);
+
+    var first = {score: 0, length: 0};
+    var spaceIndents = 0;
+    for (var i = 1; i < 12; i++) {
+        if (i == 1) {
+            spaceIndents = getScore(i);
+            var score = 1;
+        } else
+            var score = getScore(i) / spaceIndents;
+
+        if (changes[i]) {
+            score += changes[i] / changesTotal;
+        }
+
+        if (score > first.score)
+            first = {score: score, length: i};
+    }
+
+    if (first.score && first.score > 1.4)
+        var tabLength = first.length;
+
+    if (tabIndents > spaceIndents + 1)
+        return {ch: "\t", length: tabLength};
+
+    if (spaceIndents + 1 > tabIndents)
+        return {ch: " ", length: tabLength};
+};
+
+exports.detectIndentation = function(session) {
+    var lines = session.getLines(0, 1000);
+    var indent = exports.$detectIndentation(lines) || {};
+
+    if (indent.ch)
+        session.setUseSoftTabs(indent.ch == " ");
+
+    if (indent.length)
+        session.setTabSize(indent.length);
+    return indent;
+};
+
+exports.trimTrailingSpace = function(session, trimEmpty) {
+    var doc = session.getDocument();
+    var lines = doc.getAllLines();
+    
+    var min = trimEmpty ? -1 : 0;
+
+    for (var i = 0, l=lines.length; i < l; i++) {
+        var line = lines[i];
+        var index = line.search(/\s+$/);
+
+        if (index > min)
+            doc.removeInLine(i, index, line.length);
+    }
+};
+
+exports.convertIndentation = function(session, ch, len) {
+    var oldCh = session.getTabString()[0];
+    var oldLen = session.getTabSize();
+    if (!len) len = oldLen;
+    if (!ch) ch = oldCh;
+
+    var tab = ch == "\t" ? ch: lang.stringRepeat(ch, len);
+
+    var doc = session.doc;
+    var lines = doc.getAllLines();
+
+    var cache = {};
+    var spaceCache = {};
+    for (var i = 0, l=lines.length; i < l; i++) {
+        var line = lines[i];
+        var match = line.match(/^\s*/)[0];
+        if (match) {
+            var w = session.$getStringScreenWidth(match)[0];
+            var tabCount = Math.floor(w/oldLen);
+            var reminder = w%oldLen;
+            var toInsert = cache[tabCount] || (cache[tabCount] = lang.stringRepeat(tab, tabCount));
+            toInsert += spaceCache[reminder] || (spaceCache[reminder] = lang.stringRepeat(" ", reminder));
+
+            if (toInsert != match) {
+                doc.removeInLine(i, 0, match.length);
+                doc.insertInLine({row: i, column: 0}, toInsert);
+            }
+        }
+    }
+    session.setTabSize(len);
+    session.setUseSoftTabs(ch == " ");
+};
+
+exports.$parseStringArg = function(text) {
+    var indent = {};
+    if (/t/.test(text))
+        indent.ch = "\t";
+    else if (/s/.test(text))
+        indent.ch = " ";
+    var m = text.match(/\d+/);
+    if (m)
+        indent.length = parseInt(m[0], 10);
+    return indent;
+};
+
+exports.$parseArg = function(arg) {
+    if (!arg)
+        return {};
+    if (typeof arg == "string")
+        return exports.$parseStringArg(arg);
+    if (typeof arg.text == "string")
+        return exports.$parseStringArg(arg.text);
+    return arg;
+};
+
+exports.commands = [{
+    name: "detectIndentation",
+    exec: function(editor) {
+        exports.detectIndentation(editor.session);
+        // todo show message?
+    }
+}, {
+    name: "trimTrailingSpace",
+    exec: function(editor) {
+        exports.trimTrailingSpace(editor.session);
+    }
+}, {
+    name: "convertIndentation",
+    exec: function(editor, arg) {
+        var indent = exports.$parseArg(arg);
+        exports.convertIndentation(editor.session, indent.ch, indent.length);
+    }
+}, {
+    name: "setIndentation",
+    exec: function(editor, arg) {
+        var indent = exports.$parseArg(arg);
+        indent.length && editor.session.setTabSize(indent.length);
+        indent.ch && editor.session.setUseSoftTabs(indent.ch == " ");
+    }
+}];
+
+});

http://git-wip-us.apache.org/repos/asf/couchdb/blob/9abd128c/src/fauxton/assets/js/libs/ace/incremental_search.js
----------------------------------------------------------------------
diff --git a/src/fauxton/assets/js/libs/ace/incremental_search.js b/src/fauxton/assets/js/libs/ace/incremental_search.js
new file mode 100644
index 0000000..e6fa892
--- /dev/null
+++ b/src/fauxton/assets/js/libs/ace/incremental_search.js
@@ -0,0 +1,259 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Distributed under the BSD license:
+ *
+ * Copyright (c) 2010, Ajax.org B.V.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Ajax.org B.V. nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define(function(require, exports, module) {
+"use strict";
+
+var oop = require("./lib/oop");
+var Range = require("./range").Range;
+var Search = require("./search").Search;
+var SearchHighlight = require("./search_highlight").SearchHighlight;
+var iSearchCommandModule = require("./commands/incremental_search_commands");
+var ISearchKbd = iSearchCommandModule.IncrementalSearchKeyboardHandler;
+
+/**
+ * @class IncrementalSearch
+ *
+ * Implements immediate searching while the user is typing. When incremental
+ * search is activated, keystrokes into the editor will be used for composing
+ * a search term. Immediately after every keystroke the search is updated:
+ * - so-far-matching characters are highlighted
+ * - the cursor is moved to the next match
+ *
+ **/
+
+
+/**
+ *
+ *
+ * Creates a new `IncrementalSearch` object.
+ *
+ * @constructor
+ **/
+function IncrementalSearch() {
+    this.$options = {wrap: false, skipCurrent: false};
+    this.$keyboardHandler = new ISearchKbd(this);
+}
+
+oop.inherits(IncrementalSearch, Search);
+
+;(function() {
+
+    this.activate = function(ed, backwards) {
+        this.$editor = ed;
+        this.$startPos = this.$currentPos = ed.getCursorPosition();
+        this.$options.needle = '';
+        this.$options.backwards = backwards;
+        ed.keyBinding.addKeyboardHandler(this.$keyboardHandler);
+        this.$mousedownHandler = ed.addEventListener('mousedown', this.onMouseDown.bind(this));
+        this.selectionFix(ed);
+        this.statusMessage(true);
+    }
+
+    this.deactivate = function(reset) {
+        this.cancelSearch(reset);
+        this.$editor.keyBinding.removeKeyboardHandler(this.$keyboardHandler);
+        if (this.$mousedownHandler) {
+            this.$editor.removeEventListener('mousedown', this.$mousedownHandler);
+            delete this.$mousedownHandler;
+        }
+        this.message('');
+    }
+
+    this.selectionFix = function(editor) {
+        // Fix selection bug: When clicked inside the editor
+        // editor.selection.$isEmpty is false even if the mouse click did not
+        // open a selection. This is interpreted by the move commands to
+        // extend the selection. To only extend the selection when there is
+        // one, we clear it here
+        if (editor.selection.isEmpty() && !editor.session.$emacsMark) {
+            editor.clearSelection();
+        }
+    }
+
+    this.highlight = function(regexp) {
+        var sess = this.$editor.session,
+            hl = sess.$isearchHighlight = sess.$isearchHighlight || sess.addDynamicMarker(
+                new SearchHighlight(null, "ace_isearch-result", "text"));
+        hl.setRegexp(regexp);
+        sess._emit("changeBackMarker"); // force highlight layer redraw
+    }
+
+    this.cancelSearch = function(reset) {
+        var e = this.$editor;
+        this.$prevNeedle = this.$options.needle;
+        this.$options.needle = '';
+        if (reset) {
+            e.moveCursorToPosition(this.$startPos);
+            this.$currentPos = this.$startPos;
+        } else {
+            e.pushEmacsMark && e.pushEmacsMark(this.$startPos, false);
+        }
+        this.highlight(null);
+        return Range.fromPoints(this.$currentPos, this.$currentPos);
+    }
+
+    this.highlightAndFindWithNeedle = function(moveToNext, needleUpdateFunc) {
+        if (!this.$editor) return null;
+        var options = this.$options;
+
+        // get search term
+        if (needleUpdateFunc) {
+            options.needle = needleUpdateFunc.call(this, options.needle || '') || '';
+        }
+        if (options.needle.length === 0) {
+            this.statusMessage(true);
+            return this.cancelSearch(true);
+        };
+
+        // try to find the next occurence and enable  highlighting marker
+        options.start = this.$currentPos;
+        var session = this.$editor.session,
+            found = this.find(session);
+        if (found) {
+            if (options.backwards) found = Range.fromPoints(found.end, found.start);
+            this.$editor.moveCursorToPosition(found.end);
+            if (moveToNext) this.$currentPos = found.end;
+            // highlight after cursor move, so selection works properly
+            this.highlight(options.re)
+        }
+
+        this.statusMessage(found);
+
+        return found;
+    }
+
+    this.addChar = function(c) {
+        return this.highlightAndFindWithNeedle(false, function(needle) {
+            return needle + c;
+        });
+    }
+
+    this.removeChar = function(c) {
+        return this.highlightAndFindWithNeedle(false, function(needle) {
+            return needle.length > 0 ? needle.substring(0, needle.length-1) : needle;
+        });
+    }
+
+    this.next = function(options) {
+        // try to find the next occurence of whatever we have searched for
+        // earlier.
+        // options = {[backwards: BOOL], [useCurrentOrPrevSearch: BOOL]}
+        options = options || {};
+        this.$options.backwards = !!options.backwards;
+        this.$currentPos = this.$editor.getCursorPosition();
+        return this.highlightAndFindWithNeedle(true, function(needle) {
+            return options.useCurrentOrPrevSearch && needle.length === 0 ?
+                this.$prevNeedle || '' : needle;
+        });
+    }
+
+    this.onMouseDown = function(evt) {
+        // when mouse interaction happens then we quit incremental search
+        this.deactivate();
+        return true;
+    }
+
+    this.statusMessage = function(found) {
+        var options = this.$options, msg = '';
+        msg += options.backwards ? 'reverse-' : '';
+        msg += 'isearch: ' + options.needle;
+        msg += found ? '' : ' (not found)';
+        this.message(msg);
+    }
+
+    this.message = function(msg) {
+        if (this.$editor.showCommandLine) {
+            this.$editor.showCommandLine(msg);
+            this.$editor.focus();
+        } else {
+            console.log(msg);
+        }
+    }
+
+}).call(IncrementalSearch.prototype);
+
+
+exports.IncrementalSearch = IncrementalSearch;
+
+
+/**
+ *
+ * Config settings for enabling/disabling [[IncrementalSearch `IncrementalSearch`]].
+ *
+ **/
+
+var dom = require('./lib/dom');
+dom.importCssString && dom.importCssString("\
+.ace_marker-layer .ace_isearch-result {\
+  position: absolute;\
+  z-index: 6;\
+  -moz-box-sizing: border-box;\
+  -webkit-box-sizing: border-box;\
+  box-sizing: border-box;\
+}\
+div.ace_isearch-result {\
+  border-radius: 4px;\
+  background-color: rgba(255, 200, 0, 0.5);\
+  box-shadow: 0 0 4px rgb(255, 200, 0);\
+}\
+.ace_dark div.ace_isearch-result {\
+  background-color: rgb(100, 110, 160);\
+  box-shadow: 0 0 4px rgb(80, 90, 140);\
+}", "incremental-search-highlighting");
+
+// support for default keyboard handler
+var commands = require("./commands/command_manager");
+(function() {
+    this.setupIncrementalSearch = function(editor, val) {
+        if (this.usesIncrementalSearch == val) return;
+        this.usesIncrementalSearch = val;
+        var iSearchCommands = iSearchCommandModule.iSearchStartCommands;
+        var method = val ? 'addCommands' : 'removeCommands';
+        this[method](iSearchCommands);
+    };
+}).call(commands.CommandManager.prototype);
+
+// incremental search config option
+var Editor = require("./editor").Editor;
+require("./config").defineOptions(Editor.prototype, "editor", {
+    useIncrementalSearch: {
+        set: function(val) {
+            this.keyBinding.$handlers.forEach(function(handler) {
+                if (handler.setupIncrementalSearch) {
+                    handler.setupIncrementalSearch(this, val);
+                }
+            });
+            this._emit('incrementalSearchSettingChanged', {isEnabled: val});
+        }
+    }
+});
+
+});

http://git-wip-us.apache.org/repos/asf/couchdb/blob/9abd128c/src/fauxton/assets/js/libs/ace/incremental_search_test.js
----------------------------------------------------------------------
diff --git a/src/fauxton/assets/js/libs/ace/incremental_search_test.js b/src/fauxton/assets/js/libs/ace/incremental_search_test.js
new file mode 100644
index 0000000..c351d8e
--- /dev/null
+++ b/src/fauxton/assets/js/libs/ace/incremental_search_test.js
@@ -0,0 +1,208 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Distributed under the BSD license:
+ *
+ * Copyright (c) 2010, Ajax.org B.V.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Ajax.org B.V. nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+if (typeof process !== "undefined") {
+    require("amd-loader");
+}
+
+define(function(require, exports, module) {
+"use strict";
+
+var EditSession = require("./edit_session").EditSession;
+var Editor = require("./editor").Editor;
+var MockRenderer = require("./test/mockrenderer").MockRenderer;
+var Range = require("./range").Range;
+var assert = require("./test/assertions");
+var IncrementalSearch = require("./incremental_search").IncrementalSearch;
+
+var editor, iSearch;
+function testRanges(str, ranges) {
+    ranges = ranges || editor.selection.getAllRanges();
+    assert.equal(ranges + "", str + "");
+}
+
+// force "rerender"
+function callHighlighterUpdate() {
+    var session = editor.session,
+        ranges = [],
+        mockMarkerLayer = {
+            drawSingleLineMarker: function(_, markerRanges) {
+                ranges = ranges.concat(markerRanges);
+            }
+        }
+    session.$isearchHighlight.update([], mockMarkerLayer, session, {
+        firstRow: 0, lastRow: session.getRowLength()});
+    return ranges;
+}
+
+module.exports = {
+
+    name: "ACE incremental_search.js",
+
+    setUp: function() {
+        var session = new EditSession(["abc123", "xyz124"]);
+        editor = new Editor(new MockRenderer(), session);
+        iSearch = new IncrementalSearch();
+    },
+
+    "test: keyboard handler setup" : function() {
+        iSearch.activate(editor);
+        assert.equal(editor.getKeyboardHandler(), iSearch.$keyboardHandler);
+        iSearch.deactivate();
+        assert.notEqual(editor.getKeyboardHandler(), iSearch.$keyboardHandler);
+    },
+
+    "test: isearch highlight setup" : function() {
+        var sess = editor.session;
+        iSearch.activate(editor);
+        iSearch.highlight('foo');
+        var highl = sess.$isearchHighlight.id;
+        assert.ok(sess.$isearchHighlight, 'session has no isearch highlighter');
+        assert.equal(sess.getMarkers()[highl.id], highl.id, 'isearch highlight not in markers');
+        iSearch.deactivate();
+        iSearch.activate(editor);
+        iSearch.highlight('bar');
+        var highl2 = sess.$isearchHighlight.id;
+        assert.equal(highl2, highl, 'multiple isearch highlights');
+    },
+
+    "test: find simple text incrementally" : function() {
+        iSearch.activate(editor);
+        var range = iSearch.addChar('1'), // "1"
+            highlightRanges = callHighlighterUpdate(editor.session);
+        testRanges("Range: [0/3] -> [0/4]", [range], "range");
+        testRanges("Range: [0/3] -> [0/4],Range: [1/3] -> [1/4]", highlightRanges, "highlight");
+
+        range = iSearch.addChar('2'); // "12"
+        highlightRanges = callHighlighterUpdate(editor.session);
+        testRanges("Range: [0/3] -> [0/5]", [range], "range");
+        testRanges("Range: [0/3] -> [0/5],Range: [1/3] -> [1/5]", highlightRanges, "highlight");
+
+        range = iSearch.addChar('3'); // "123"
+        highlightRanges = callHighlighterUpdate(editor.session);
+        testRanges("Range: [0/3] -> [0/6]", [range], "range");
+        testRanges("Range: [0/3] -> [0/6]", highlightRanges, "highlight");
+
+        range = iSearch.removeChar(); // "12"
+        highlightRanges = callHighlighterUpdate(editor.session);
+        testRanges("Range: [0/3] -> [0/5]", [range], "range");
+        testRanges("Range: [0/3] -> [0/5],Range: [1/3] -> [1/5]", highlightRanges, "highlight");
+    },
+
+    "test: forward / backward" : function() {
+        iSearch.activate(editor);
+        iSearch.addChar('1'); iSearch.addChar('2');
+        var range = iSearch.next();
+        testRanges("Range: [1/3] -> [1/5]", [range], "range");
+
+        range = iSearch.next(); // nothing to find
+        testRanges("", [range], "range");
+
+        range = iSearch.next({backwards: true}); // backwards
+        testRanges("Range: [1/5] -> [1/3]", [range], "range");
+    },
+
+    "test: cancelSearch" : function() {
+        iSearch.activate(editor);
+        iSearch.addChar('1'); iSearch.addChar('2');
+        var range = iSearch.cancelSearch(true);
+        testRanges("Range: [0/0] -> [0/0]", [range], "range");
+
+        iSearch.addChar('1'); range = iSearch.addChar('2');
+        testRanges("Range: [0/3] -> [0/5]", [range], "range");
+    },
+
+    "test: failing search keeps pos" : function() {
+        iSearch.activate(editor);
+        iSearch.addChar('1'); iSearch.addChar('2');
+        var range = iSearch.addChar('x');
+        testRanges("", [range], "range");
+        assert.position(editor.getCursorPosition(), 0, 5);
+    },
+
+    "test: backwards search" : function() {
+        editor.moveCursorTo(1,0);
+        iSearch.activate(editor, true);
+        iSearch.addChar('1'); var range = iSearch.addChar('2');;
+        testRanges("Range: [0/5] -> [0/3]", [range], "range");
+        assert.position(editor.getCursorPosition(), 0, 3);
+    },
+
+    "test: forwards then backwards, same result, reoriented range" : function() {
+        iSearch.activate(editor);
+        iSearch.addChar('1'); var range = iSearch.addChar('2');;
+        testRanges("Range: [0/3] -> [0/5]", [range], "range");
+        assert.position(editor.getCursorPosition(), 0, 5);
+
+        range = iSearch.next({backwards: true});
+        testRanges("Range: [0/5] -> [0/3]", [range], "range");
+        assert.position(editor.getCursorPosition(), 0, 3);
+    },
+
+    "test: reuse prev search via option" : function() {
+        iSearch.activate(editor);
+        iSearch.addChar('1'); iSearch.addChar('2');;
+        assert.position(editor.getCursorPosition(), 0, 5);
+        iSearch.deactivate();
+
+        iSearch.activate(editor);
+        iSearch.next({backwards: false, useCurrentOrPrevSearch: true});
+        assert.position(editor.getCursorPosition(), 1, 5);
+    },
+
+    "test: don't extend selection range if selection is empty" : function() {
+        iSearch.activate(editor);
+        iSearch.addChar('1'); iSearch.addChar('2');;
+        testRanges("Range: [0/5] -> [0/5]", [editor.getSelectionRange()], "sel range");
+    },
+
+    "test: extend selection range if selection exists" : function() {
+        iSearch.activate(editor);
+        editor.selection.selectTo(0, 1);
+        iSearch.addChar('1'); iSearch.addChar('2');;
+        testRanges("Range: [0/0] -> [0/5]", [editor.getSelectionRange()], "sel range");
+    },
+
+    "test: extend selection in emacs mark mode" : function() {
+        var emacs = require('./keyboard/emacs');
+        editor.keyBinding.addKeyboardHandler(emacs.handler);
+        emacs.handler.commands.setMark.exec(editor);
+        iSearch.activate(editor);
+        iSearch.addChar('1'); iSearch.addChar('2');;
+        testRanges("Range: [0/0] -> [0/5]", [editor.getSelectionRange()], "sel range");
+    }
+
+};
+
+});
+
+if (typeof module !== "undefined" && module === require.main) {
+    require("asyncjs").test.testcase(module.exports).exec()
+}