Bug 410841 - Explorer editing CSS with orion

Update to the 3.0 release of the editor
diff --git a/bundles/org.eclipse.e4.tools.orion.css.editor/src/org/eclipse/e4/tools/orion/css/editor/views/SampleCSSView.java b/bundles/org.eclipse.e4.tools.orion.css.editor/src/org/eclipse/e4/tools/orion/css/editor/views/SampleCSSView.java
index 8d6c253..544d8af 100644
--- a/bundles/org.eclipse.e4.tools.orion.css.editor/src/org/eclipse/e4/tools/orion/css/editor/views/SampleCSSView.java
+++ b/bundles/org.eclipse.e4.tools.orion.css.editor/src/org/eclipse/e4/tools/orion/css/editor/views/SampleCSSView.java
@@ -68,7 +68,7 @@
 					.getEntry("/web/css.html").openStream();
 			String cssTemplate = loadFile(in, 1024);
 			final String editorCssUrl = FileLocator.toFileURL(bundle.getEntry("/web/built-editor.css")).toExternalForm();
-			final String editorJsUrl = FileLocator.toFileURL(bundle.getEntry("/web/built-editor.min.js")).toExternalForm();
+			final String editorJsUrl = FileLocator.toFileURL(bundle.getEntry("/web/built-editor.js")).toExternalForm();
 			
 			editorHtml = String.format(cssTemplate, editorCssUrl, editorJsUrl, editorContent);
 			System.out.println(editorHtml);
diff --git a/bundles/org.eclipse.e4.tools.orion.css.editor/web/built-editor.js b/bundles/org.eclipse.e4.tools.orion.css.editor/web/built-editor.js
new file mode 100644
index 0000000..677e1ed
--- /dev/null
+++ b/bundles/org.eclipse.e4.tools.orion.css.editor/web/built-editor.js
@@ -0,0 +1,21048 @@
+/* orion editor */ 
+/**
+ * almond 0.2.4 Copyright (c) 2011-2012, The Dojo Foundation All Rights Reserved.
+ * Available via the MIT or new BSD license.
+ * see: http://github.com/jrburke/almond for details
+ */
+//Going sloppy to avoid 'use strict' string cost, but strict practices should
+//be followed.
+/*jslint sloppy: true */
+/*global setTimeout: false */
+
+var requirejs, require, define;
+(function (undef) {
+    var main, req, makeMap, handlers,
+        defined = {},
+        waiting = {},
+        config = {},
+        defining = {},
+        hasOwn = Object.prototype.hasOwnProperty,
+        aps = [].slice;
+
+    function hasProp(obj, prop) {
+        return hasOwn.call(obj, prop);
+    }
+
+    /**
+     * Given a relative module name, like ./something, normalize it to
+     * a real name that can be mapped to a path.
+     * @param {String} name the relative name
+     * @param {String} baseName a real name that the name arg is relative
+     * to.
+     * @returns {String} normalized name
+     */
+    function normalize(name, baseName) {
+        var nameParts, nameSegment, mapValue, foundMap,
+            foundI, foundStarMap, starI, i, j, part,
+            baseParts = baseName && baseName.split("/"),
+            map = config.map,
+            starMap = (map && map['*']) || {};
+
+        //Adjust any relative paths.
+        if (name && name.charAt(0) === ".") {
+            //If have a base name, try to normalize against it,
+            //otherwise, assume it is a top-level require that will
+            //be relative to baseUrl in the end.
+            if (baseName) {
+                //Convert baseName to array, and lop off the last part,
+                //so that . matches that "directory" and not name of the baseName's
+                //module. For instance, baseName of "one/two/three", maps to
+                //"one/two/three.js", but we want the directory, "one/two" for
+                //this normalization.
+                baseParts = baseParts.slice(0, baseParts.length - 1);
+
+                name = baseParts.concat(name.split("/"));
+
+                //start trimDots
+                for (i = 0; i < name.length; i += 1) {
+                    part = name[i];
+                    if (part === ".") {
+                        name.splice(i, 1);
+                        i -= 1;
+                    } else if (part === "..") {
+                        if (i === 1 && (name[2] === '..' || name[0] === '..')) {
+                            //End of the line. Keep at least one non-dot
+                            //path segment at the front so it can be mapped
+                            //correctly to disk. Otherwise, there is likely
+                            //no path mapping for a path starting with '..'.
+                            //This can still fail, but catches the most reasonable
+                            //uses of ..
+                            break;
+                        } else if (i > 0) {
+                            name.splice(i - 1, 2);
+                            i -= 2;
+                        }
+                    }
+                }
+                //end trimDots
+
+                name = name.join("/");
+            } else if (name.indexOf('./') === 0) {
+                // No baseName, so this is ID is resolved relative
+                // to baseUrl, pull off the leading dot.
+                name = name.substring(2);
+            }
+        }
+
+        //Apply map config if available.
+        if ((baseParts || starMap) && map) {
+            nameParts = name.split('/');
+
+            for (i = nameParts.length; i > 0; i -= 1) {
+                nameSegment = nameParts.slice(0, i).join("/");
+
+                if (baseParts) {
+                    //Find the longest baseName segment match in the config.
+                    //So, do joins on the biggest to smallest lengths of baseParts.
+                    for (j = baseParts.length; j > 0; j -= 1) {
+                        mapValue = map[baseParts.slice(0, j).join('/')];
+
+                        //baseName segment has  config, find if it has one for
+                        //this name.
+                        if (mapValue) {
+                            mapValue = mapValue[nameSegment];
+                            if (mapValue) {
+                                //Match, update name to the new value.
+                                foundMap = mapValue;
+                                foundI = i;
+                                break;
+                            }
+                        }
+                    }
+                }
+
+                if (foundMap) {
+                    break;
+                }
+
+                //Check for a star map match, but just hold on to it,
+                //if there is a shorter segment match later in a matching
+                //config, then favor over this star map.
+                if (!foundStarMap && starMap && starMap[nameSegment]) {
+                    foundStarMap = starMap[nameSegment];
+                    starI = i;
+                }
+            }
+
+            if (!foundMap && foundStarMap) {
+                foundMap = foundStarMap;
+                foundI = starI;
+            }
+
+            if (foundMap) {
+                nameParts.splice(0, foundI, foundMap);
+                name = nameParts.join('/');
+            }
+        }
+
+        return name;
+    }
+
+    function makeRequire(relName, forceSync) {
+        return function () {
+            //A version of a require function that passes a moduleName
+            //value for items that may need to
+            //look up paths relative to the moduleName
+            return req.apply(undef, aps.call(arguments, 0).concat([relName, forceSync]));
+        };
+    }
+
+    function makeNormalize(relName) {
+        return function (name) {
+            return normalize(name, relName);
+        };
+    }
+
+    function makeLoad(depName) {
+        return function (value) {
+            defined[depName] = value;
+        };
+    }
+
+    function callDep(name) {
+        if (hasProp(waiting, name)) {
+            var args = waiting[name];
+            delete waiting[name];
+            defining[name] = true;
+            main.apply(undef, args);
+        }
+
+        if (!hasProp(defined, name) && !hasProp(defining, name)) {
+            throw new Error('No ' + name);
+        }
+        return defined[name];
+    }
+
+    //Turns a plugin!resource to [plugin, resource]
+    //with the plugin being undefined if the name
+    //did not have a plugin prefix.
+    function splitPrefix(name) {
+        var prefix,
+            index = name ? name.indexOf('!') : -1;
+        if (index > -1) {
+            prefix = name.substring(0, index);
+            name = name.substring(index + 1, name.length);
+        }
+        return [prefix, name];
+    }
+
+    /**
+     * Makes a name map, normalizing the name, and using a plugin
+     * for normalization if necessary. Grabs a ref to plugin
+     * too, as an optimization.
+     */
+    makeMap = function (name, relName) {
+        var plugin,
+            parts = splitPrefix(name),
+            prefix = parts[0];
+
+        name = parts[1];
+
+        if (prefix) {
+            prefix = normalize(prefix, relName);
+            plugin = callDep(prefix);
+        }
+
+        //Normalize according
+        if (prefix) {
+            if (plugin && plugin.normalize) {
+                name = plugin.normalize(name, makeNormalize(relName));
+            } else {
+                name = normalize(name, relName);
+            }
+        } else {
+            name = normalize(name, relName);
+            parts = splitPrefix(name);
+            prefix = parts[0];
+            name = parts[1];
+            if (prefix) {
+                plugin = callDep(prefix);
+            }
+        }
+
+        //Using ridiculous property names for space reasons
+        return {
+            f: prefix ? prefix + '!' + name : name, //fullName
+            n: name,
+            pr: prefix,
+            p: plugin
+        };
+    };
+
+    function makeConfig(name) {
+        return function () {
+            return (config && config.config && config.config[name]) || {};
+        };
+    }
+
+    handlers = {
+        require: function (name) {
+            return makeRequire(name);
+        },
+        exports: function (name) {
+            var e = defined[name];
+            if (typeof e !== 'undefined') {
+                return e;
+            } else {
+                return (defined[name] = {});
+            }
+        },
+        module: function (name) {
+            return {
+                id: name,
+                uri: '',
+                exports: defined[name],
+                config: makeConfig(name)
+            };
+        }
+    };
+
+    main = function (name, deps, callback, relName) {
+        var cjsModule, depName, ret, map, i,
+            args = [],
+            usingExports;
+
+        //Use name if no relName
+        relName = relName || name;
+
+        //Call the callback to define the module, if necessary.
+        if (typeof callback === 'function') {
+
+            //Pull out the defined dependencies and pass the ordered
+            //values to the callback.
+            //Default to [require, exports, module] if no deps
+            deps = !deps.length && callback.length ? ['require', 'exports', 'module'] : deps;
+            for (i = 0; i < deps.length; i += 1) {
+                map = makeMap(deps[i], relName);
+                depName = map.f;
+
+                //Fast path CommonJS standard dependencies.
+                if (depName === "require") {
+                    args[i] = handlers.require(name);
+                } else if (depName === "exports") {
+                    //CommonJS module spec 1.1
+                    args[i] = handlers.exports(name);
+                    usingExports = true;
+                } else if (depName === "module") {
+                    //CommonJS module spec 1.1
+                    cjsModule = args[i] = handlers.module(name);
+                } else if (hasProp(defined, depName) ||
+                           hasProp(waiting, depName) ||
+                           hasProp(defining, depName)) {
+                    args[i] = callDep(depName);
+                } else if (map.p) {
+                    map.p.load(map.n, makeRequire(relName, true), makeLoad(depName), {});
+                    args[i] = defined[depName];
+                } else {
+                    throw new Error(name + ' missing ' + depName);
+                }
+            }
+
+            ret = callback.apply(defined[name], args);
+
+            if (name) {
+                //If setting exports via "module" is in play,
+                //favor that over return value and exports. After that,
+                //favor a non-undefined return value over exports use.
+                if (cjsModule && cjsModule.exports !== undef &&
+                        cjsModule.exports !== defined[name]) {
+                    defined[name] = cjsModule.exports;
+                } else if (ret !== undef || !usingExports) {
+                    //Use the return value from the function.
+                    defined[name] = ret;
+                }
+            }
+        } else if (name) {
+            //May just be an object definition for the module. Only
+            //worry about defining if have a module name.
+            defined[name] = callback;
+        }
+    };
+
+    requirejs = require = req = function (deps, callback, relName, forceSync, alt) {
+        if (typeof deps === "string") {
+            if (handlers[deps]) {
+                //callback in this case is really relName
+                return handlers[deps](callback);
+            }
+            //Just return the module wanted. In this scenario, the
+            //deps arg is the module name, and second arg (if passed)
+            //is just the relName.
+            //Normalize module name, if it contains . or ..
+            return callDep(makeMap(deps, callback).f);
+        } else if (!deps.splice) {
+            //deps is a config object, not an array.
+            config = deps;
+            if (callback.splice) {
+                //callback is an array, which means it is a dependency list.
+                //Adjust args if there are dependencies
+                deps = callback;
+                callback = relName;
+                relName = null;
+            } else {
+                deps = undef;
+            }
+        }
+
+        //Support require(['a'])
+        callback = callback || function () {};
+
+        //If relName is a function, it is an errback handler,
+        //so remove it.
+        if (typeof relName === 'function') {
+            relName = forceSync;
+            forceSync = alt;
+        }
+
+        //Simulate async callback;
+        if (forceSync) {
+            main(undef, deps, callback, relName);
+        } else {
+            //Using a non-zero value because of concern for what old browsers
+            //do, and latest browsers "upgrade" to 4 if lower value is used:
+            //http://www.whatwg.org/specs/web-apps/current-work/multipage/timers.html#dom-windowtimers-settimeout:
+            //If want a value immediately, use require('id') instead -- something
+            //that works in almond on the global level, but not guaranteed and
+            //unlikely to work in other AMD implementations.
+            setTimeout(function () {
+                main(undef, deps, callback, relName);
+            }, 4);
+        }
+
+        return req;
+    };
+
+    /**
+     * Just drops the config on the floor, but returns req in case
+     * the config return value is used.
+     */
+    req.config = function (cfg) {
+        config = cfg;
+        return req;
+    };
+
+    define = function (name, deps, callback) {
+
+        //This module may not have dependencies
+        if (!deps.splice) {
+            //deps is not an array, so probably means
+            //an object literal or factory function for
+            //the value. Adjust args.
+            callback = deps;
+            deps = [];
+        }
+
+        if (!hasProp(defined, name) && !hasProp(waiting, name)) {
+            waiting[name] = [name, deps, callback];
+        }
+    };
+
+    define.amd = {
+        jQuery: true
+    };
+}());
+
+define("almond", function(){});
+
+/**
+ * @license RequireJS i18n 2.0.2 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved.
+ * Available via the MIT or new BSD license.
+ * see: http://github.com/requirejs/i18n for details
+ */
+/*jslint regexp: true */
+/*global require: false, navigator: false, define: false */
+
+/**
+ * This plugin handles i18n! prefixed modules. It does the following:
+ *
+ * 1) A regular module can have a dependency on an i18n bundle, but the regular
+ * module does not want to specify what locale to load. So it just specifies
+ * the top-level bundle, like "i18n!nls/colors".
+ *
+ * This plugin will load the i18n bundle at nls/colors, see that it is a root/master
+ * bundle since it does not have a locale in its name. It will then try to find
+ * the best match locale available in that master bundle, then request all the
+ * locale pieces for that best match locale. For instance, if the locale is "en-us",
+ * then the plugin will ask for the "en-us", "en" and "root" bundles to be loaded
+ * (but only if they are specified on the master bundle).
+ *
+ * Once all the bundles for the locale pieces load, then it mixes in all those
+ * locale pieces into each other, then finally sets the context.defined value
+ * for the nls/colors bundle to be that mixed in locale.
+ *
+ * 2) A regular module specifies a specific locale to load. For instance,
+ * i18n!nls/fr-fr/colors. In this case, the plugin needs to load the master bundle
+ * first, at nls/colors, then figure out what the best match locale is for fr-fr,
+ * since maybe only fr or just root is defined for that locale. Once that best
+ * fit is found, all of its locale pieces need to have their bundles loaded.
+ *
+ * Once all the bundles for the locale pieces load, then it mixes in all those
+ * locale pieces into each other, then finally sets the context.defined value
+ * for the nls/fr-fr/colors bundle to be that mixed in locale.
+ */
+(function () {
+    
+
+    //regexp for reconstructing the master bundle name from parts of the regexp match
+    //nlsRegExp.exec("foo/bar/baz/nls/en-ca/foo") gives:
+    //["foo/bar/baz/nls/en-ca/foo", "foo/bar/baz/nls/", "/", "/", "en-ca", "foo"]
+    //nlsRegExp.exec("foo/bar/baz/nls/foo") gives:
+    //["foo/bar/baz/nls/foo", "foo/bar/baz/nls/", "/", "/", "foo", ""]
+    //so, if match[5] is blank, it means this is the top bundle definition.
+    var nlsRegExp = /(^.*(^|\/)nls(\/|$))([^\/]*)\/?([^\/]*)/;
+
+    //Helper function to avoid repeating code. Lots of arguments in the
+    //desire to stay functional and support RequireJS contexts without having
+    //to know about the RequireJS contexts.
+    function addPart(locale, master, needed, toLoad, prefix, suffix) {
+        if (master[locale]) {
+            needed.push(locale);
+            if (master[locale] === true || master[locale] === 1) {
+                toLoad.push(prefix + locale + '/' + suffix);
+            }
+        }
+    }
+
+    function addIfExists(req, locale, toLoad, prefix, suffix) {
+        var fullName = prefix + locale + '/' + suffix;
+        if (require._fileExists(req.toUrl(fullName + '.js'))) {
+            toLoad.push(fullName);
+        }
+    }
+
+    /**
+     * Simple function to mix in properties from source into target,
+     * but only if target does not already have a property of the same name.
+     * This is not robust in IE for transferring methods that match
+     * Object.prototype names, but the uses of mixin here seem unlikely to
+     * trigger a problem related to that.
+     */
+    function mixin(target, source, force) {
+        var prop;
+        for (prop in source) {
+            if (source.hasOwnProperty(prop) && (!target.hasOwnProperty(prop) || force)) {
+                target[prop] = source[prop];
+            } else if (typeof source[prop] === 'object') {
+                mixin(target[prop], source[prop], force);
+            }
+        }
+    }
+
+    define('i18n',['module'], function (module) {
+        var masterConfig = module.config ? module.config() : {};
+
+        return {
+            version: '2.0.1+',
+            /**
+             * Called when a dependency needs to be loaded.
+             */
+            load: function (name, req, onLoad, config) {
+                config = config || {};
+
+                if (config.locale) {
+                    masterConfig.locale = config.locale;
+                }
+
+                var masterName,
+                    match = nlsRegExp.exec(name),
+                    prefix = match[1],
+                    locale = match[4],
+                    suffix = match[5],
+                    parts = locale.split("-"),
+                    toLoad = [],
+                    value = {},
+                    i, part, current = "";
+
+                //If match[5] is blank, it means this is the top bundle definition,
+                //so it does not have to be handled. Locale-specific requests
+                //will have a match[4] value but no match[5]
+                if (match[5]) {
+                    //locale-specific bundle
+                    prefix = match[1];
+                    masterName = prefix + suffix;
+                } else {
+                    //Top-level bundle.
+                    masterName = name;
+                    suffix = match[4];
+                    locale = masterConfig.locale;
+                    if (!locale) {
+                        locale = masterConfig.locale =
+                            typeof navigator === "undefined" ? "root" :
+                            (navigator.language ||
+                             navigator.userLanguage || "root").toLowerCase();
+                    }
+                    parts = locale.split("-");
+                }
+
+                if (config.isBuild) {
+                    //Check for existence of all locale possible files and
+                    //require them if exist.
+                    toLoad.push(masterName);
+                    addIfExists(req, "root", toLoad, prefix, suffix);
+                    for (i = 0; i < parts.length; i++) {
+                        part = parts[i];
+                        current += (current ? "-" : "") + part;
+                        addIfExists(req, current, toLoad, prefix, suffix);
+                    }
+
+                    req(toLoad, function () {
+                        onLoad();
+                    });
+                } else {
+                    //First, fetch the master bundle, it knows what locales are available.
+                    req([masterName], function (master) {
+                        //Figure out the best fit
+                        var needed = [],
+                            part;
+
+                        //Always allow for root, then do the rest of the locale parts.
+                        addPart("root", master, needed, toLoad, prefix, suffix);
+                        for (i = 0; i < parts.length; i++) {
+                            part = parts[i];
+                            current += (current ? "-" : "") + part;
+                            addPart(current, master, needed, toLoad, prefix, suffix);
+                        }
+
+                        //Load all the parts missing.
+                        req(toLoad, function () {
+                            var i, partBundle, part;
+                            for (i = needed.length - 1; i > -1 && needed[i]; i--) {
+                                part = needed[i];
+                                partBundle = master[part];
+                                if (partBundle === true || partBundle === 1) {
+                                    partBundle = req(prefix + part + '/' + suffix);
+                                }
+                                mixin(value, partBundle);
+                            }
+
+                            //All done, notify the loader.
+                            onLoad(value);
+                        });
+                    });
+                }
+            }
+        };
+    });
+}());
+
+/*******************************************************************************
+ * @license
+ * Copyright (c) 2010, 2012 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials are made 
+ * available under the terms of the Eclipse Public License v1.0 
+ * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
+ * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
+ * 
+ * Contributors: IBM Corporation - initial API and implementation
+ ******************************************************************************/
+
+/*global define */
+define('orion/editor/i18n',{
+	load: function(name, parentRequire, onLoad, config) {
+		if (parentRequire.specified && parentRequire.specified("orion/bootstrap")) { //$NON-NLS-0$
+			parentRequire(["orion/i18n!" + name], function(languages) { //$NON-NLS-0$
+				onLoad(languages);
+			});
+		} else {
+			onLoad({});
+		}
+	}
+});
+
+/*******************************************************************************
+ * @license
+ * Copyright (c) 2012 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials are made 
+ * available under the terms of the Eclipse Public License v1.0 
+ * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
+ * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
+ * 
+ * Contributors: 
+ *		Felipe Heidrich (IBM Corporation) - initial API and implementation
+ *		Silenio Quarti (IBM Corporation) - initial API and implementation
+ ******************************************************************************/
+
+//NLS_CHARSET=UTF-8
+
+/*global define*/
+
+define('orion/editor/nls/root/messages',{
+	"multipleAnnotations": "Multiple annotations:", //$NON-NLS-1$ //$NON-NLS-0$
+	"line": "Line: ${0}", //$NON-NLS-1$ //$NON-NLS-0$
+	"breakpoint": "Breakpoint", //$NON-NLS-1$ //$NON-NLS-0$
+	"bookmark": "Bookmark", //$NON-NLS-1$ //$NON-NLS-0$
+	"task": "Task", //$NON-NLS-1$ //$NON-NLS-0$
+	"error": "Error", //$NON-NLS-1$ //$NON-NLS-0$
+	"warning": "Warning", //$NON-NLS-1$ //$NON-NLS-0$
+	"matchingSearch": "Matching Search", //$NON-NLS-1$ //$NON-NLS-0$
+	"currentSearch": "Current Search", //$NON-NLS-1$ //$NON-NLS-0$
+	"currentLine": "Current Line", //$NON-NLS-1$ //$NON-NLS-0$
+	"matchingBracket": "Matching Bracket", //$NON-NLS-1$ //$NON-NLS-0$
+	"currentBracket": "Current Bracket", //$NON-NLS-1$ //$NON-NLS-0$
+	
+	"lineUp": "Line Up", //$NON-NLS-1$ //$NON-NLS-0$
+	"lineDown": "Line Down", //$NON-NLS-1$ //$NON-NLS-0$
+	"lineStart": "Line Start", //$NON-NLS-1$ //$NON-NLS-0$
+	"lineEnd": "Line End", //$NON-NLS-1$ //$NON-NLS-0$
+	"charPrevious": "Previous Character", //$NON-NLS-1$ //$NON-NLS-0$
+	"charNext": "Next Character", //$NON-NLS-1$ //$NON-NLS-0$
+	"pageUp": "Page Up", //$NON-NLS-1$ //$NON-NLS-0$
+	"pageDown": "Page Down", //$NON-NLS-1$ //$NON-NLS-0$
+	"scrollPageUp": "Scroll Page Up", //$NON-NLS-1$ //$NON-NLS-0$
+	"scrollPageDown": "Scroll Page Down", //$NON-NLS-1$ //$NON-NLS-0$
+	"scrollLineUp": "Scroll Line Up", //$NON-NLS-1$ //$NON-NLS-0$
+	"scrollLineDown": "Scroll Line Down", //$NON-NLS-1$ //$NON-NLS-0$
+	"wordPrevious": "Previous Word", //$NON-NLS-1$ //$NON-NLS-0$
+	"wordNext": "Next Word", //$NON-NLS-1$ //$NON-NLS-0$
+	"textStart": "Document Start", //$NON-NLS-1$ //$NON-NLS-0$
+	"textEnd": "Document End", //$NON-NLS-1$ //$NON-NLS-0$
+	"scrollTextStart": "Scroll Document Start", //$NON-NLS-1$ //$NON-NLS-0$
+	"scrollTextEnd": "Scroll Document End", //$NON-NLS-1$ //$NON-NLS-0$
+	"centerLine": "Center Line", //$NON-NLS-1$ //$NON-NLS-0$
+	
+	"selectLineUp": "Select Line Up", //$NON-NLS-1$ //$NON-NLS-0$
+	"selectLineDown": "Select Line Down", //$NON-NLS-1$ //$NON-NLS-0$
+	"selectWholeLineUp": " Select Whole Line Up", //$NON-NLS-1$ //$NON-NLS-0$
+	"selectWholeLineDown": "Select Whole Line Down", //$NON-NLS-1$ //$NON-NLS-0$
+	"selectLineStart": "Select Line Start", //$NON-NLS-1$ //$NON-NLS-0$
+	"selectLineEnd": "Select Line End", //$NON-NLS-1$ //$NON-NLS-0$
+	"selectCharPrevious": "Select Previous Character", //$NON-NLS-1$ //$NON-NLS-0$
+	"selectCharNext": "Select Next Character", //$NON-NLS-1$ //$NON-NLS-0$
+	"selectPageUp": "Select Page Up", //$NON-NLS-1$ //$NON-NLS-0$
+	"selectPageDown": "Select Page Down", //$NON-NLS-1$ //$NON-NLS-0$
+	"selectWordPrevious": "Select Previous Word", //$NON-NLS-1$ //$NON-NLS-0$
+	"selectWordNext": "Select Next Word", //$NON-NLS-1$ //$NON-NLS-0$
+	"selectTextStart": "Select Document Start", //$NON-NLS-1$ //$NON-NLS-0$
+	"selectTextEnd": "Select Document End", //$NON-NLS-1$ //$NON-NLS-0$
+
+	"deletePrevious": "Delete Previous Character", //$NON-NLS-1$ //$NON-NLS-0$
+	"deleteNext": "Delete Next Character", //$NON-NLS-1$ //$NON-NLS-0$
+	"deleteWordPrevious": "Delete Previous Word", //$NON-NLS-1$ //$NON-NLS-0$
+	"deleteWordNext": "Delete Next Word", //$NON-NLS-1$ //$NON-NLS-0$
+	"deleteLineStart": "Delete Line Start", //$NON-NLS-1$ //$NON-NLS-0$
+	"deleteLineEnd": "Delete Line End", //$NON-NLS-1$ //$NON-NLS-0$
+	"tab": "Insert Tab", //$NON-NLS-1$ //$NON-NLS-0$
+	"enter": "Insert Line Delimiter", //$NON-NLS-1$ //$NON-NLS-0$
+	"enterNoCursor": "Insert Line Delimiter", //$NON-NLS-1$ //$NON-NLS-0$
+	"selectAll": "Select All", //$NON-NLS-1$ //$NON-NLS-0$
+	"copy": "Copy", //$NON-NLS-1$ //$NON-NLS-0$
+	"cut": "Cut", //$NON-NLS-1$ //$NON-NLS-0$
+	"paste": "Paste", //$NON-NLS-1$ //$NON-NLS-0$
+	
+	"uppercase": "To Upper Case", //$NON-NLS-1$ //$NON-NLS-0$
+	"lowercase": "To Lower Case", //$NON-NLS-1$ //$NON-NLS-0$
+	"capitalize": "Capitalize", //$NON-NLS-1$ //$NON-NLS-0$
+	"reversecase" : "Reverse Case", //$NON-NLS-1$ //$NON-NLS-0$
+	
+	"toggleWrapMode": "Toggle Wrap Mode", //$NON-NLS-1$ //$NON-NLS-0$
+	"toggleTabMode": "Toggle Tab Mode", //$NON-NLS-1$ //$NON-NLS-0$
+	"toggleOverwriteMode": "Toggle Overwrite Mode", //$NON-NLS-1$ //$NON-NLS-0$
+	
+	//Emacs
+	"emacs": "Emacs", //$NON-NLS-1$ //$NON-NLS-0$
+	"exchangeMarkPoint": "Exchange Mark and Point", //$NON-NLS-1$ //$NON-NLS-0$
+	"setMarkCommand": "Set Mark", //$NON-NLS-1$ //$NON-NLS-0$
+	"clearMark": "Clear Mark", //$NON-NLS-1$ //$NON-NLS-0$
+	"digitArgument": "Digit Argument ${0}", //$NON-NLS-1$ //$NON-NLS-0$
+	"negativeArgument": "Negative Argument", //$NON-NLS-1$ //$NON-NLS-0$
+			
+	"Comment": "Comment", //$NON-NLS-1$ //$NON-NLS-0$
+	"Flat outline": "Flat outline", //$NON-NLS-1$ //$NON-NLS-0$
+	"incrementalFindStr": "Incremental find: ${0}", //$NON-NLS-1$ //$NON-NLS-0$
+	"incrementalFindStrNotFound": "Incremental find: ${0} (not found)", //$NON-NLS-1$ //$NON-NLS-0$
+	"incrementalFindReverseStr": "Reverse Incremental find: ${0}", //$NON-NLS-1$ //$NON-NLS-0$
+	"incrementalFindReverseStrNotFound": "Reverse Incremental find: ${0} (not found)", //$NON-NLS-1$ //$NON-NLS-0$
+	"find": "Find...", //$NON-NLS-1$ //$NON-NLS-0$
+	"undo": "Undo", //$NON-NLS-1$ //$NON-NLS-0$
+	"redo": "Redo", //$NON-NLS-1$ //$NON-NLS-0$
+	"cancelMode": "Cancel Current Mode", //$NON-NLS-1$ //$NON-NLS-0$
+	"findNext": "Find Next Occurrence", //$NON-NLS-1$ //$NON-NLS-0$
+	"findPrevious": "Find Previous Occurrence", //$NON-NLS-1$ //$NON-NLS-0$
+	"incrementalFind": "Incremental Find", //$NON-NLS-1$ //$NON-NLS-0$
+	"incrementalFindReverse": "Incremental Find Reverse", //$NON-NLS-1$ //$NON-NLS-0$
+	"indentLines": "Indent Lines", //$NON-NLS-1$ //$NON-NLS-0$
+	"unindentLines": "Unindent Lines", //$NON-NLS-1$ //$NON-NLS-0$
+	"moveLinesUp": "Move Lines Up", //$NON-NLS-1$ //$NON-NLS-0$
+	"moveLinesDown": "Move Lines Down", //$NON-NLS-1$ //$NON-NLS-0$
+	"copyLinesUp": "Copy Lines Up", //$NON-NLS-1$ //$NON-NLS-0$
+	"copyLinesDown": "Copy Lines Down", //$NON-NLS-1$ //$NON-NLS-0$
+	"deleteLines": "Delete Lines", //$NON-NLS-1$ //$NON-NLS-0$
+	"gotoLine": "Goto Line...", //$NON-NLS-1$ //$NON-NLS-0$
+	"gotoLinePrompty": "Goto Line:", //$NON-NLS-1$ //$NON-NLS-0$
+	"nextAnnotation": "Next Annotation", //$NON-NLS-1$ //$NON-NLS-0$
+	"prevAnnotation": "Previous Annotation", //$NON-NLS-1$ //$NON-NLS-0$
+	"expand": "Expand", //$NON-NLS-1$ //$NON-NLS-0$
+	"collapse": "Collapse", //$NON-NLS-1$ //$NON-NLS-0$
+	"expandAll": "Expand All", //$NON-NLS-1$ //$NON-NLS-0$
+	"collapseAll": "Collapse All", //$NON-NLS-1$ //$NON-NLS-0$
+	"lastEdit": "Last Edit Location", //$NON-NLS-1$ //$NON-NLS-0$
+	"toggleLineComment": "Toggle Line Comment", //$NON-NLS-1$ //$NON-NLS-0$
+	"addBlockComment": "Add Block Comment", //$NON-NLS-1$ //$NON-NLS-0$
+	"removeBlockComment": "Remove Block Comment", //$NON-NLS-1$ //$NON-NLS-0$
+	"linkedModeEntered": "Linked Mode entered", //$NON-NLS-1$ //$NON-NLS-0$
+	"linkedModeExited": "Linked Mode exited", //$NON-NLS-1$ //$NON-NLS-0$
+	"syntaxError": "Syntax Error", //$NON-NLS-1$ //$NON-NLS-0$
+	"contentAssist": "Content Assist", //$NON-NLS-1$ //$NON-NLS-0$
+	"lineColumn": "Line ${0} : Col ${1}", //$NON-NLS-1$ //$NON-NLS-0$
+	
+	//vi
+	"vi": "vi", //$NON-NLS-1$ //$NON-NLS-0$
+	"vimove": "(Move)", //$NON-NLS-1$ //$NON-NLS-0$
+	"viyank": "(Yank)", //$NON-NLS-1$ //$NON-NLS-0$
+	"videlete": "(Delete)", //$NON-NLS-1$ //$NON-NLS-0$
+	"vichange": "(Change)", //$NON-NLS-1$ //$NON-NLS-0$
+	"viLeft": "${0} Left", //$NON-NLS-1$ //$NON-NLS-0$
+	"viRight": "${0} Right", //$NON-NLS-1$ //$NON-NLS-0$
+	"viUp": "${0} Up", //$NON-NLS-1$ //$NON-NLS-0$
+	"viDown": "${0} Down", //$NON-NLS-1$ //$NON-NLS-0$
+	"viw": "${0} Next Word", //$NON-NLS-1$ //$NON-NLS-0$
+	"vib": "${0} Beginning of Word", //$NON-NLS-1$ //$NON-NLS-0$
+	"viW": "${0} Next Word (ws stop)", //$NON-NLS-1$ //$NON-NLS-0$
+	"viB": "${0} Beginning of Word (ws stop)", //$NON-NLS-1$ //$NON-NLS-0$
+	"vie": "${0} End of Word", //$NON-NLS-1$ //$NON-NLS-0$
+	"viE": "${0} End of Word (ws stop)", //$NON-NLS-1$ //$NON-NLS-0$
+	"vi$": "${0} End of the line", //$NON-NLS-1$ //$NON-NLS-0$
+	"vi^_": "${0} First non-blank Char Current Line", //$NON-NLS-1$ //$NON-NLS-0$
+	"vi+": "${0} First Char Next Line", //$NON-NLS-1$ //$NON-NLS-0$
+	"vi-": "${0} First Char Previous Line", //$NON-NLS-1$ //$NON-NLS-0$
+	"vi|": "${0} nth Column in Line", //$NON-NLS-1$ //$NON-NLS-0$
+	"viH": "${0} Top of Page", //$NON-NLS-1$ //$NON-NLS-0$
+	"viM": "${0} Middle of Page", //$NON-NLS-1$ //$NON-NLS-0$
+	"viL": "${0} Bottom of Page", //$NON-NLS-1$ //$NON-NLS-0$
+	"vi/": "${0} Search Forward", //$NON-NLS-1$ //$NON-NLS-0$
+	"vi?": "${0} Search Backward", //$NON-NLS-1$ //$NON-NLS-0$
+	"vin": "${0} Next Search", //$NON-NLS-1$ //$NON-NLS-0$
+	"viN": "${0} Previous Search", //$NON-NLS-1$ //$NON-NLS-0$
+	"vif": "${0} Search Char Fwd", //$NON-NLS-1$ //$NON-NLS-0$
+	"viF": "${0} Search Char Bckwd", //$NON-NLS-1$ //$NON-NLS-0$
+	"vit": "${0} Search Before Char Fwd", //$NON-NLS-1$ //$NON-NLS-0$
+	"viT": "${0} Search Before Char Bckwd", //$NON-NLS-1$ //$NON-NLS-0$
+	"vi,": "${0} Repeat Reverse Char Search", //$NON-NLS-1$ //$NON-NLS-0$
+	"vi;": "${0} Repeat Char Search", //$NON-NLS-1$ //$NON-NLS-0$
+	"viG": "${0} Go to Line", //$NON-NLS-1$ //$NON-NLS-0$
+	"viycd": "${0} Current Line", //$NON-NLS-1$ //$NON-NLS-0$
+	"via": "Append After Cursor", //$NON-NLS-1$ //$NON-NLS-0$
+	"viA": "Append to End of Line", //$NON-NLS-1$ //$NON-NLS-0$
+	"vii": "Insert Before Cursor", //$NON-NLS-1$ //$NON-NLS-0$
+	"viI": "Insert at Beginning of Line", //$NON-NLS-1$ //$NON-NLS-0$
+	"viO": "Insert Line Above", //$NON-NLS-1$ //$NON-NLS-0$
+	"vio": "Insert Line Below", //$NON-NLS-1$ //$NON-NLS-0$
+	"viR": "Begin Overwriting Text", //$NON-NLS-1$ //$NON-NLS-0$
+	"vis": "Substitute a Character", //$NON-NLS-1$ //$NON-NLS-0$
+	"viS": "Substitute Entire Line", //$NON-NLS-1$ //$NON-NLS-0$
+	"viC": "Change Text Until Line End", //$NON-NLS-1$ //$NON-NLS-0$
+	"vip": "Paste After Char or Line", //$NON-NLS-1$ //$NON-NLS-0$
+	"viP": "Paste Before Char or Line", //$NON-NLS-1$ //$NON-NLS-0$
+
+	"replaceAll": "Replacing all...", //$NON-NLS-1$ //$NON-NLS-0$
+	"replacedMatches": "Replaced ${0} matches", //$NON-NLS-1$ //$NON-NLS-0$
+	"nothingReplaced": "Nothing replaced", //$NON-NLS-1$ //$NON-NLS-0$
+	"notFound": "Not found" //$NON-NLS-1$ //$NON-NLS-0$
+});
+
+/*******************************************************************************
+ * @license
+ * Copyright (c) 2010, 2012 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials are made 
+ * available under the terms of the Eclipse Public License v1.0 
+ * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
+ * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
+ * 
+ * Contributors: 
+ *		Felipe Heidrich (IBM Corporation) - initial API and implementation
+ *		Silenio Quarti (IBM Corporation) - initial API and implementation
+ ******************************************************************************/
+
+/*global define*/
+
+define('orion/editor/nls/messages',['orion/editor/i18n!orion/editor/nls/messages', 'orion/editor/nls/root/messages'], function(bundle, root) {
+	var result = {
+		root: root
+	};
+	for (var key in bundle) {
+		if (bundle.hasOwnProperty(key)) {
+			if (typeof result[key] === 'undefined') {
+				result[key] = bundle[key];
+			}
+		}
+	}
+	return result;
+});
+
+/*******************************************************************************
+ * Copyright (c) 2010, 2012 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials are made 
+ * available under the terms of the Eclipse Public License v1.0 
+ * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
+ * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
+ * 
+ * Contributors: 
+ *		Felipe Heidrich (IBM Corporation) - initial API and implementation
+ *		Silenio Quarti (IBM Corporation) - initial API and implementation
+ ******************************************************************************/
+ 
+/*global define */
+define("orion/editor/eventTarget", [], function() { //$NON-NLS-0$
+	/** 
+	 * Constructs a new EventTarget object.
+	 * 
+	 * @class 
+	 * @name orion.editor.EventTarget
+	 */
+	function EventTarget() {
+	}
+	/**
+	 * Adds in the event target interface into the specified object.
+	 *
+	 * @param {Object} object The object to add in the event target interface.
+	 */
+	EventTarget.addMixin = function(object) {
+		var proto = EventTarget.prototype;
+		for (var p in proto) {
+			if (proto.hasOwnProperty(p)) {
+				object[p] = proto[p];
+			}
+		}
+	};
+	EventTarget.prototype = /** @lends orion.editor.EventTarget.prototype */ {
+		/**
+		 * Adds an event listener to this event target.
+		 * 
+		 * @param {String} type The event type.
+		 * @param {Function|EventListener} listener The function or the EventListener that will be executed when the event happens. 
+		 * @param {Boolean} [useCapture=false] <code>true</code> if the listener should be trigged in the capture phase.
+		 * 
+		 * @see #removeEventListener
+		 */
+		addEventListener: function(type, listener, useCapture) {
+			if (!this._eventTypes) { this._eventTypes = {}; }
+			var state = this._eventTypes[type];
+			if (!state) {
+				state = this._eventTypes[type] = {level: 0, listeners: []};
+			}
+			var listeners = state.listeners;
+			listeners.push({listener: listener, useCapture: useCapture});
+		},
+		/**
+		 * Dispatches the given event to the listeners added to this event target.
+		 * @param {Event} evt The event to dispatch.
+		 */
+		dispatchEvent: function(evt) {
+			var type = evt.type;
+			this._dispatchEvent("pre" + type, evt); //$NON-NLS-0$
+			this._dispatchEvent(type, evt);
+			this._dispatchEvent("post" + type, evt); //$NON-NLS-0$
+		},
+		_dispatchEvent: function(type, evt) {
+			var state = this._eventTypes ? this._eventTypes[type] : null;
+			if (state) {
+				var listeners = state.listeners;
+				try {
+					state.level++;
+					if (listeners) {
+						for (var i=0, len=listeners.length; i < len; i++) {
+							if (listeners[i]) {
+								var l = listeners[i].listener;
+								if (typeof l === "function") { //$NON-NLS-0$
+									l.call(this, evt);
+								} else if (l.handleEvent && typeof l.handleEvent === "function") { //$NON-NLS-0$
+									l.handleEvent(evt);
+								}
+							}
+						}
+					}
+				} finally {
+					state.level--;
+					if (state.compact && state.level === 0) {
+						for (var j=listeners.length - 1; j >= 0; j--) {
+							if (!listeners[j]) {
+								listeners.splice(j, 1);
+							}
+						}
+						if (listeners.length === 0) {
+							delete this._eventTypes[type];
+						}
+						state.compact = false;
+					}
+				}
+			}
+		},
+		/**
+		 * Returns whether there is a listener for the specified event type.
+		 * 
+		 * @param {String} type The event type
+		 * 
+		 * @see #addEventListener
+		 * @see #removeEventListener
+		 */
+		isListening: function(type) {
+			if (!this._eventTypes) { return false; }
+			return this._eventTypes[type] !== undefined;
+		},		
+		/**
+		 * Removes an event listener from the event target.
+		 * <p>
+		 * All the parameters must be the same ones used to add the listener.
+		 * </p>
+		 * 
+		 * @param {String} type The event type
+		 * @param {Function|EventListener} listener The function or the EventListener that will be executed when the event happens. 
+		 * @param {Boolean} [useCapture=false] <code>true</code> if the listener should be trigged in the capture phase.
+		 * 
+		 * @see #addEventListener
+		 */
+		removeEventListener: function(type, listener, useCapture){
+			if (!this._eventTypes) { return; }
+			var state = this._eventTypes[type];
+			if (state) {
+				var listeners = state.listeners;
+				for (var i=0, len=listeners.length; i < len; i++) {
+					var l = listeners[i];
+					if (l && l.listener === listener && l.useCapture === useCapture) {
+						if (state.level !== 0) {
+							listeners[i] = null;
+							state.compact = true;
+						} else {
+							listeners.splice(i, 1);
+						}
+						break;
+					}
+				}
+				if (listeners.length === 0) {
+					delete this._eventTypes[type];
+				}
+			}
+		}
+	};
+	return {EventTarget: EventTarget};
+});
+
+/*******************************************************************************
+ * @license
+ * Copyright (c) 2012 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials are made 
+ * available under the terms of the Eclipse Public License v1.0 
+ * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
+ * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
+ *
+ * Contributors: IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+/*global define navigator*/
+define('orion/util',[],function() {
+
+	var userAgent = navigator.userAgent;
+	var isIE = parseFloat(userAgent.split("MSIE")[1]) || undefined; //$NON-NLS-0$
+	var isFirefox = parseFloat(userAgent.split("Firefox/")[1] || userAgent.split("Minefield/")[1]) || undefined; //$NON-NLS-1$ //$NON-NLS-0$
+	var isOpera = userAgent.indexOf("Opera") !== -1; //$NON-NLS-0$
+	var isChrome = parseFloat(userAgent.split("Chrome/")[1]) || undefined; //$NON-NLS-0$
+	var isSafari = userAgent.indexOf("Safari") !== -1 && !isChrome; //$NON-NLS-0$
+	var isWebkit = parseFloat(userAgent.split("WebKit/")[1]) || undefined; //$NON-NLS-0$
+	var isAndroid = userAgent.indexOf("Android") !== -1; //$NON-NLS-0$
+	var isIPad = userAgent.indexOf("iPad") !== -1; //$NON-NLS-0$
+	var isIPhone = userAgent.indexOf("iPhone") !== -1; //$NON-NLS-0$
+	var isIOS = isIPad || isIPhone;
+	var isMac = navigator.platform.indexOf("Mac") !== -1; //$NON-NLS-0$
+	var isWindows = navigator.platform.indexOf("Win") !== -1; //$NON-NLS-0$
+	var isLinux = navigator.platform.indexOf("Linux") !== -1; //$NON-NLS-0$
+	var platformDelimiter = isWindows ? "\r\n" : "\n"; //$NON-NLS-1$ //$NON-NLS-0$
+
+	function formatMessage(msg) {
+		var args = arguments;
+		return msg.replace(/\$\{([^\}]+)\}/g, function(str, index) { return args[(index << 0) + 1]; });
+	}
+	
+	var XHTML = "http://www.w3.org/1999/xhtml"; //$NON-NLS-0$
+	function createElement(document, tagName) {
+		if (document.createElementNS) {
+			return document.createElementNS(XHTML, tagName);
+		}
+		return document.createElement(tagName);
+	}
+
+	return {
+		formatMessage: formatMessage,
+		
+		createElement: createElement,
+		
+		/** Browsers */
+		isIE: isIE,
+		isFirefox: isFirefox,
+		isOpera: isOpera,
+		isChrome: isChrome,
+		isSafari: isSafari,
+		isWebkit: isWebkit,
+		isAndroid: isAndroid,
+		isIPad: isIPad,
+		isIPhone: isIPhone,
+		isIOS: isIOS,
+		
+		/** OSs */
+		isMac: isMac,
+		isWindows: isWindows,
+		isLinux: isLinux,
+		
+		platformDelimiter: platformDelimiter
+	};
+});
+
+/*******************************************************************************
+ * @license
+ * Copyright (c) 2010, 2012 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials are made 
+ * available under the terms of the Eclipse Public License v1.0 
+ * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
+ * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
+ * 
+ * Contributors: 
+ *		Felipe Heidrich (IBM Corporation) - initial API and implementation
+ *		Silenio Quarti (IBM Corporation) - initial API and implementation
+ ******************************************************************************/
+ 
+/*global define*/
+
+define("orion/editor/textModel", ['orion/editor/eventTarget', 'orion/util'], function(mEventTarget, util) { //$NON-NLS-2$  //$NON-NLS-1$ //$NON-NLS-0$
+
+	/**
+	 * Constructs a new TextModel with the given text and default line delimiter.
+	 *
+	 * @param {String} [text=""] the text that the model will store
+	 * @param {String} [lineDelimiter=platform delimiter] the line delimiter used when inserting new lines to the model.
+	 *
+	 * @name orion.editor.TextModel
+	 * @class The TextModel is an interface that provides text for the view. Applications may
+	 * implement the TextModel interface to provide a custom store for the view content. The
+	 * view interacts with its text model in order to access and update the text that is being
+	 * displayed and edited in the view. This is the default implementation.
+	 * <p>
+	 * <b>See:</b><br/>
+	 * {@link orion.editor.TextView}<br/>
+	 * {@link orion.editor.TextView#setModel}
+	 * </p>
+	 * @borrows orion.editor.EventTarget#addEventListener as #addEventListener
+	 * @borrows orion.editor.EventTarget#removeEventListener as #removeEventListener
+	 * @borrows orion.editor.EventTarget#dispatchEvent as #dispatchEvent
+	 */
+	function TextModel(text, lineDelimiter) {
+		this._lastLineIndex = -1;
+		this._text = [""];
+		this._lineOffsets = [0];
+		this.setText(text);
+		this.setLineDelimiter(lineDelimiter);
+	}
+
+	TextModel.prototype = /** @lends orion.editor.TextModel.prototype */ {
+		/**
+		 * @class This object describes the options to use while finding occurrences of a string in a text model.
+		 * @name orion.editor.FindOptions
+		 *
+		 * @property {String} string the search string to be found.
+		 * @property {Boolean} [regex=false] whether or not the search string is a regular expression.
+		 * @property {Boolean} [wrap=false] whether or not to wrap search.
+		 * @property {Boolean} [wholeWord=false] whether or not to search only whole words.
+		 * @property {Boolean} [caseInsensitive=false] whether or not search is case insensitive.
+		 * @property {Boolean} [reverse=false] whether or not to search backwards.
+		 * @property {Number} [start=0] The start offset to start searching
+		 * @property {Number} [end=charCount] The end offset of the search. Used to search in a given range.
+		 */
+		/**
+		 * @class This object represents a find occurrences iterator.
+		 * <p>
+		 * <b>See:</b><br/>
+		 * {@link orion.editor.TextModel#find}<br/>
+		 * </p>		 
+		 * @name orion.editor.FindIterator
+		 * 
+		 * @property {Function} hasNext Determines whether there are more occurrences in the iterator.
+		 * @property {Function} next Returns the next matched range {start,end} in the iterator.
+		 */	
+		/**
+		 * Finds occurrences of a string in the text model.
+		 *
+		 * @param {orion.editor.FindOptions} options the search options
+		 * @return {orion.editor.FindIterator} the find occurrences iterator.
+		 */
+		find: function(options) {
+			if (this._text.length > 1) {
+				this._text = [this._text.join("")];
+			}
+			var string = options.string;
+			var regex = options.regex;
+			var pattern = string;
+			var caseInsensitive = options.caseInsensitive;
+			if (!regex && string) {
+				pattern = string.replace(/([\\$\^*\/+?\.\(\)|{}\[\]])/g, "\\$&"); //$NON-NLS-0$
+				/*
+				* Bug in JS RegEx. In a Turkish locale, dotless i (u0131) capitalizes to I (u0049) and i (u0069) 
+				* capitalizes to dot I (u0130). The JS RegEx does not match correctly the Turkish i's in case
+				* insensitive mode. The fix is to detect the presence of Turkish i's in the search pattern and 
+				* to modify the pattern to search for both upper and lower case.
+				*/
+				if (caseInsensitive) {  //$NON-NLS-1$ //$NON-NLS-0$
+					pattern = pattern.replace(/[iI\u0130\u0131]/g, "[Ii\u0130\u0131]"); //$NON-NLS-0$
+				}
+			}
+			var current = null, skip;
+			if (pattern) {
+				var reverse = options.reverse;
+				var wrap = options.wrap;
+				var wholeWord = options.wholeWord;
+				var start = options.start || 0;
+				var end = options.end;
+				var isRange = (end !== null && end !== undefined);
+				var flags = "";
+				if (flags.indexOf("g") === -1) { flags += "g"; } //$NON-NLS-1$ //$NON-NLS-0$
+				if (caseInsensitive) {
+					if (flags.indexOf("i") === -1) { flags += "i"; } //$NON-NLS-1$ //$NON-NLS-0$
+				}
+				if (wholeWord) {
+					pattern = "\\b" + pattern + "\\b"; //$NON-NLS-1$ //$NON-NLS-0$
+				}
+				var text = this._text[0], result, lastIndex, offset = 0;
+				if (isRange) {
+					var s = start < end ? start : end;
+					var e = start < end ? end : start;
+					text = text.substring(s, e);
+					offset = s;
+				}
+				var re = new RegExp(pattern, flags);
+				if (reverse) {
+					skip = function() {
+						var match = null;
+						re.lastIndex = 0;
+						while (true) {
+							lastIndex = re.lastIndex;
+							result = re.exec(text);
+							if (lastIndex === re.lastIndex) {
+								return null;
+							}
+							if (result) {
+								if (result.index + offset < start) {
+									match = {start: result.index + offset, end: re.lastIndex + offset};
+								} else {
+									if (!wrap || match) {
+										break;
+									}
+									start = text.length + offset;
+									match = {start: result.index + offset, end: re.lastIndex + offset};
+								}
+							} else {
+								break;
+							}
+						}
+						if (match) { start = match.start; }
+						return match;
+					};
+				} else {
+					if (!isRange) {
+						re.lastIndex = start;
+					}
+					skip = function() {
+						while (true) {
+							lastIndex = re.lastIndex;
+							result = re.exec(text);
+							if (lastIndex === re.lastIndex) {
+								return null;
+							}
+							if (result) {
+								return {start: result.index + offset, end: re.lastIndex + offset};
+							}
+							if (lastIndex !== 0) {
+								if (wrap) {
+									continue;
+								}
+							}
+							break;
+						}
+						return null;
+					};
+				}
+				current = skip();
+			}
+			return {
+				next: function() {
+					var result = current;
+					if (result) { current = skip(); }
+					return result;					
+				},
+				hasNext: function() {
+					return current !== null;
+				}
+			};
+		},
+		/**
+		 * Returns the number of characters in the model.
+		 *
+		 * @returns {Number} the number of characters in the model.
+		 */
+		getCharCount: function() {
+			var count = 0;
+			for (var i = 0; i<this._text.length; i++) {
+				count += this._text[i].length;
+			}
+			return count;
+		},
+		/**
+		 * Returns the text of the line at the given index.
+		 * <p>
+		 * The valid indices are 0 to line count exclusive.  Returns <code>null</code> 
+		 * if the index is out of range. 
+		 * </p>
+		 *
+		 * @param {Number} lineIndex the zero based index of the line.
+		 * @param {Boolean} [includeDelimiter=false] whether or not to include the line delimiter. 
+		 * @returns {String} the line text or <code>null</code> if out of range.
+		 *
+		 * @see #getLineAtOffset
+		 */
+		getLine: function(lineIndex, includeDelimiter) {
+			var lineCount = this.getLineCount();
+			if (!(0 <= lineIndex && lineIndex < lineCount)) {
+				return null;
+			}
+			var start = this._lineOffsets[lineIndex];
+			if (lineIndex + 1 < lineCount) {
+				var text = this.getText(start, this._lineOffsets[lineIndex + 1]);
+				if (includeDelimiter) {
+					return text;
+				}
+				var end = text.length, c;
+				while (((c = text.charCodeAt(end - 1)) === 10) || (c === 13)) {
+					end--;
+				}
+				return text.substring(0, end);
+			} else {
+				return this.getText(start); 
+			}
+		},
+		/**
+		 * Returns the line index at the given character offset.
+		 * <p>
+		 * The valid offsets are 0 to char count inclusive. The line index for
+		 * char count is <code>line count - 1</code>. Returns <code>-1</code> if
+		 * the offset is out of range.
+		 * </p>
+		 *
+		 * @param {Number} offset a character offset.
+		 * @returns {Number} the zero based line index or <code>-1</code> if out of range.
+		 */
+		getLineAtOffset: function(offset) {
+			var charCount = this.getCharCount();
+			if (!(0 <= offset && offset <= charCount)) {
+				return -1;
+			}
+			var lineCount = this.getLineCount();
+			if (offset === charCount) {
+				return lineCount - 1; 
+			}
+			var lineStart, lineEnd;
+			var index = this._lastLineIndex;
+			if (0 <= index && index < lineCount) {
+				lineStart = this._lineOffsets[index];
+				lineEnd = index + 1 < lineCount ? this._lineOffsets[index + 1] : charCount;
+				if (lineStart <= offset && offset < lineEnd) {
+					return index;
+				}
+			}
+			var high = lineCount;
+			var low = -1;
+			while (high - low > 1) {
+				index = Math.floor((high + low) / 2);
+				lineStart = this._lineOffsets[index];
+				lineEnd = index + 1 < lineCount ? this._lineOffsets[index + 1] : charCount;
+				if (offset <= lineStart) {
+					high = index;
+				} else if (offset < lineEnd) {
+					high = index;
+					break;
+				} else {
+					low = index;
+				}
+			}
+			this._lastLineIndex = high;
+			return high;
+		},
+		/**
+		 * Returns the number of lines in the model.
+		 * <p>
+		 * The model always has at least one line.
+		 * </p>
+		 *
+		 * @returns {Number} the number of lines.
+		 */
+		getLineCount: function() {
+			return this._lineOffsets.length;
+		},
+		/**
+		 * Returns the line delimiter that is used by the view
+		 * when inserting new lines. New lines entered using key strokes 
+		 * and paste operations use this line delimiter.
+		 *
+		 * @return {String} the line delimiter that is used by the view when inserting new lines.
+		 */
+		getLineDelimiter: function() {
+			return this._lineDelimiter;
+		},
+		/**
+		 * Returns the end character offset for the given line. 
+		 * <p>
+		 * The end offset is not inclusive. This means that when the line delimiter is included, the 
+		 * offset is either the start offset of the next line or char count. When the line delimiter is
+		 * not included, the offset is the offset of the line delimiter.
+		 * </p>
+		 * <p>
+		 * The valid indices are 0 to line count exclusive.  Returns <code>-1</code> 
+		 * if the index is out of range. 
+		 * </p>
+		 *
+		 * @param {Number} lineIndex the zero based index of the line.
+		 * @param {Boolean} [includeDelimiter=false] whether or not to include the line delimiter. 
+		 * @return {Number} the line end offset or <code>-1</code> if out of range.
+		 *
+		 * @see #getLineStart
+		 */
+		getLineEnd: function(lineIndex, includeDelimiter) {
+			var lineCount = this.getLineCount();
+			if (!(0 <= lineIndex && lineIndex < lineCount)) {
+				return -1;
+			}
+			if (lineIndex + 1 < lineCount) {
+				var end = this._lineOffsets[lineIndex + 1];
+				if (includeDelimiter) {
+					return end;
+				}
+				var text = this.getText(Math.max(this._lineOffsets[lineIndex], end - 2), end);
+				var i = text.length, c;
+				while (((c = text.charCodeAt(i - 1)) === 10) || (c === 13)) {
+					i--;
+				}
+				return end - (text.length - i);
+			} else {
+				return this.getCharCount();
+			}
+		},
+		/**
+		 * Returns the start character offset for the given line.
+		 * <p>
+		 * The valid indices are 0 to line count exclusive.  Returns <code>-1</code> 
+		 * if the index is out of range. 
+		 * </p>
+		 *
+		 * @param {Number} lineIndex the zero based index of the line.
+		 * @return {Number} the line start offset or <code>-1</code> if out of range.
+		 *
+		 * @see #getLineEnd
+		 */
+		getLineStart: function(lineIndex) {
+			if (!(0 <= lineIndex && lineIndex < this.getLineCount())) {
+				return -1;
+			}
+			return this._lineOffsets[lineIndex];
+		},
+		/**
+		 * Returns the text for the given range.
+		 * <p>
+		 * The end offset is not inclusive. This means that character at the end offset
+		 * is not included in the returned text.
+		 * </p>
+		 *
+		 * @param {Number} [start=0] the zero based start offset of text range.
+		 * @param {Number} [end=char count] the zero based end offset of text range.
+		 *
+		 * @see #setText
+		 */
+		getText: function(start, end) {
+			if (start === undefined) { start = 0; }
+			if (end === undefined) { end = this.getCharCount(); }
+			if (start === end) { return ""; }
+			var offset = 0, chunk = 0, length;
+			while (chunk<this._text.length) {
+				length = this._text[chunk].length; 
+				if (start <= offset + length) { break; }
+				offset += length;
+				chunk++;
+			}
+			var firstOffset = offset;
+			var firstChunk = chunk;
+			while (chunk<this._text.length) {
+				length = this._text[chunk].length; 
+				if (end <= offset + length) { break; }
+				offset += length;
+				chunk++;
+			}
+			var lastOffset = offset;
+			var lastChunk = chunk;
+			if (firstChunk === lastChunk) {
+				return this._text[firstChunk].substring(start - firstOffset, end - lastOffset);
+			}
+			var beforeText = this._text[firstChunk].substring(start - firstOffset);
+			var afterText = this._text[lastChunk].substring(0, end - lastOffset);
+			return beforeText + this._text.slice(firstChunk+1, lastChunk).join("") + afterText; 
+		},
+		/**
+		 * Notifies all listeners that the text is about to change.
+		 * <p>
+		 * This notification is intended to be used only by the view. Application clients should
+		 * use {@link orion.editor.TextView#event:onModelChanging}.
+		 * </p>
+		 * <p>
+		 * NOTE: This method is not meant to called directly by application code. It is called internally by the TextModel
+		 * as part of the implementation of {@link #setText}. This method is included in the public API for documentation
+		 * purposes and to allow integration with other toolkit frameworks.
+		 * </p>
+		 *
+		 * @param {orion.editor.ModelChangingEvent} modelChangingEvent the changing event
+		 */
+		onChanging: function(modelChangingEvent) {
+			return this.dispatchEvent(modelChangingEvent);
+		},
+		/**
+		 * Notifies all listeners that the text has changed.
+		 * <p>
+		 * This notification is intended to be used only by the view. Application clients should
+		 * use {@link orion.editor.TextView#event:onModelChanged}.
+		 * </p>
+		 * <p>
+		 * NOTE: This method is not meant to called directly by application code. It is called internally by the TextModel
+		 * as part of the implementation of {@link #setText}. This method is included in the public API for documentation
+		 * purposes and to allow integration with other toolkit frameworks.
+		 * </p>
+		 *
+		 * @param {orion.editor.ModelChangedEvent} modelChangedEvent the changed event
+		 */
+		onChanged: function(modelChangedEvent) {
+			return this.dispatchEvent(modelChangedEvent);
+		},
+		/**
+		 * Sets the line delimiter that is used by the view
+		 * when new lines are inserted in the model due to key
+		 * strokes and paste operations. The line delimiter of
+		 * existing lines are unchanged unless the to <code>all</code>
+		 * argument is <code>true</code>.
+		 * <p>
+		 * If lineDelimiter is "auto", the delimiter is computed to be
+		 * the first delimiter found in the current text. If lineDelimiter
+		 * is undefined or if there are no delimiters in the current text, the
+		 * platform delimiter is used.
+		 * </p>
+		 *
+		 * @param {String} lineDelimiter the line delimiter that is used by the view when inserting new lines.
+		 * @param {Boolean} [all=false] whether or not the delimiter of existing lines are changed.
+		 */
+		setLineDelimiter: function(lineDelimiter, all) {
+			if (lineDelimiter === "auto") { //$NON-NLS-0$
+				lineDelimiter = undefined;
+				if (this.getLineCount() > 1) {
+					lineDelimiter = this.getText(this.getLineEnd(0), this.getLineEnd(0, true));
+				}
+			}
+			this._lineDelimiter = lineDelimiter ? lineDelimiter : util.platformDelimiter;
+			if (all) {
+				var lineCount = this.getLineCount();
+				if (lineCount > 1) {
+					var lines = new Array(lineCount);
+					for (var i=0; i<lineCount; i++) {
+						lines[i] = this.getLine(i);
+					}
+					this.setText(lines.join(this._lineDelimiter));
+				}
+			}
+		},
+		/**
+		 * Replaces the text in the given range with the given text.
+		 * <p>
+		 * The end offset is not inclusive. This means that the character at the 
+		 * end offset is not replaced.
+		 * </p>
+		 * <p>
+		 * The text model must notify the listeners before and after the
+		 * the text is changed by calling {@link #onChanging} and {@link #onChanged}
+		 * respectively. 
+		 * </p>
+		 *
+		 * @param {String} [text=""] the new text.
+		 * @param {Number} [start=0] the zero based start offset of text range.
+		 * @param {Number} [end=char count] the zero based end offset of text range.
+		 *
+		 * @see #getText
+		 */
+		setText: function(text, start, end) {
+			if (text === undefined) { text = ""; }
+			if (start === undefined) { start = 0; }
+			if (end === undefined) { end = this.getCharCount(); }
+			if (start === end && text === "") { return; }
+			var startLine = this.getLineAtOffset(start);
+			var endLine = this.getLineAtOffset(end);
+			var eventStart = start;
+			var removedCharCount = end - start;
+			var removedLineCount = endLine - startLine;
+			var addedCharCount = text.length;
+			var addedLineCount = 0;
+			var lineCount = this.getLineCount();
+			
+			var cr = 0, lf = 0, index = 0;
+			var newLineOffsets = [];
+			while (true) {
+				if (cr !== -1 && cr <= index) { cr = text.indexOf("\r", index); } //$NON-NLS-0$
+				if (lf !== -1 && lf <= index) { lf = text.indexOf("\n", index); } //$NON-NLS-0$
+				if (lf === -1 && cr === -1) { break; }
+				if (cr !== -1 && lf !== -1) {
+					if (cr + 1 === lf) {
+						index = lf + 1;
+					} else {
+						index = (cr < lf ? cr : lf) + 1;
+					}
+				} else if (cr !== -1) {
+					index = cr + 1;
+				} else {
+					index = lf + 1;
+				}
+				newLineOffsets.push(start + index);
+				addedLineCount++;
+			}
+		
+			var modelChangingEvent = {
+				type: "Changing", //$NON-NLS-0$
+				text: text,
+				start: eventStart,
+				removedCharCount: removedCharCount,
+				addedCharCount: addedCharCount,
+				removedLineCount: removedLineCount,
+				addedLineCount: addedLineCount
+			};
+			this.onChanging(modelChangingEvent);
+			
+			//TODO this should be done the loops below to avoid getText()
+			if (newLineOffsets.length === 0) {
+				var startLineOffset = this.getLineStart(startLine), endLineOffset;
+				if (endLine + 1 < lineCount) {
+					endLineOffset = this.getLineStart(endLine + 1);
+				} else {
+					endLineOffset = this.getCharCount();
+				}
+				if (start !== startLineOffset) {
+					text = this.getText(startLineOffset, start) + text;
+					start = startLineOffset;
+				}
+				if (end !== endLineOffset) {
+					text = text + this.getText(end, endLineOffset);
+					end = endLineOffset;
+				}
+			}
+			
+			var changeCount = addedCharCount - removedCharCount;
+			for (var j = startLine + removedLineCount + 1; j < lineCount; j++) {
+				this._lineOffsets[j] += changeCount;
+			}
+			
+			/*
+			* Feature in Chrome.  Chrome exceeds the maximum call stack when calling splice
+			* around 62k arguments. The limit seems to be higher on IE (250K) and Firefox (450k).
+			* The fix is to break the splice in junks of 50k.
+			*/
+			var SPLICE_LIMIT = 50000;
+			var limit = SPLICE_LIMIT, args;
+			if (newLineOffsets.length < limit) {
+				args = [startLine + 1, removedLineCount].concat(newLineOffsets);
+				Array.prototype.splice.apply(this._lineOffsets, args);
+			} else {
+				index = startLine + 1;
+				this._lineOffsets.splice(index, removedLineCount);
+				for (var k = 0; k < newLineOffsets.length; k += limit) {
+					args = [index, 0].concat(newLineOffsets.slice(k, Math.min(newLineOffsets.length, k + limit)));
+					Array.prototype.splice.apply(this._lineOffsets, args);
+					index += limit;
+				}
+			}
+			
+			var offset = 0, chunk = 0, length;
+			while (chunk<this._text.length) {
+				length = this._text[chunk].length; 
+				if (start <= offset + length) { break; }
+				offset += length;
+				chunk++;
+			}
+			var firstOffset = offset;
+			var firstChunk = chunk;
+			while (chunk<this._text.length) {
+				length = this._text[chunk].length; 
+				if (end <= offset + length) { break; }
+				offset += length;
+				chunk++;
+			}
+			var lastOffset = offset;
+			var lastChunk = chunk;
+			var firstText = this._text[firstChunk];
+			var lastText = this._text[lastChunk];
+			var beforeText = firstText.substring(0, start - firstOffset);
+			var afterText = lastText.substring(end - lastOffset);
+			var params = [firstChunk, lastChunk - firstChunk + 1];
+			if (beforeText) { params.push(beforeText); }
+			if (text) { params.push(text); }
+			if (afterText) { params.push(afterText); }
+			Array.prototype.splice.apply(this._text, params);
+			if (this._text.length === 0) { this._text = [""]; }
+			
+			var modelChangedEvent = {
+				type: "Changed", //$NON-NLS-0$
+				start: eventStart,
+				removedCharCount: removedCharCount,
+				addedCharCount: addedCharCount,
+				removedLineCount: removedLineCount,
+				addedLineCount: addedLineCount
+			};
+			this.onChanged(modelChangedEvent);
+		}
+	};
+	mEventTarget.EventTarget.addMixin(TextModel.prototype);
+	
+	return {TextModel: TextModel};
+});
+
+/*******************************************************************************
+ * @license
+ * Copyright (c) 2010, 2013 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials are made 
+ * available under the terms of the Eclipse Public License v1.0 
+ * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
+ * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
+ * 
+ * Contributors: 
+ *		Felipe Heidrich (IBM Corporation) - initial API and implementation
+ *		Silenio Quarti (IBM Corporation) - initial API and implementation
+ ******************************************************************************/
+
+/*global define */
+
+define("orion/keyBinding", ['orion/util'], function(util) { //$NON-NLS-1$ //$NON-NLS-0$
+
+    /**
+	 * @class A KeyBinding is an interface used to define keyboard shortcuts.
+	 * @name orion.KeyBinding
+	 * 
+	 * @property {Function} match The function to match events.
+	 * @property {Function} equals The funtion to compare to key bindings.
+	 *
+	 * @see orion.KeyStroke
+	 * @see orion.KeySequence
+	 */
+
+	/**
+	 * Constructs a new key stroke with the given key code, modifiers and event type.
+	 * 
+	 * @param {String|Number} keyCode the key code.
+	 * @param {Boolean} mod1 the primary modifier (usually Command on Mac and Control on other platforms).
+	 * @param {Boolean} mod2 the secondary modifier (usually Shift).
+	 * @param {Boolean} mod3 the third modifier (usually Alt).
+	 * @param {Boolean} mod4 the fourth modifier (usually Control on the Mac).
+	 * @param {String} type the type of event that the keybinding matches; either "keydown" or "keypress".
+	 * 
+	 * @class A KeyStroke represents of a key code and modifier state that can be triggered by the user using the keyboard.
+	 * @name orion.KeyStroke
+	 * 
+	 * @property {String|Number} keyCode The key code.
+	 * @property {Boolean} mod1 The primary modifier (usually Command on Mac and Control on other platforms).
+	 * @property {Boolean} mod2 The secondary modifier (usually Shift).
+	 * @property {Boolean} mod3 The third modifier (usually Alt).
+	 * @property {Boolean} mod4 The fourth modifier (usually Control on the Mac).
+	 * @property {String} [type=keydown] The type of event that the keybinding matches; either "keydown" or "keypress"
+	 *
+	 * @see orion.editor.TextView#setKeyBinding
+	 */
+	function KeyStroke (keyCode, mod1, mod2, mod3, mod4, type) {
+		this.type = type || "keydown"; //$NON-NLS-0$
+		if (typeof(keyCode) === "string" && this.type === "keydown") { //$NON-NLS-1$ //$NON-NLS-0$
+			this.keyCode = keyCode.toUpperCase().charCodeAt(0);
+		} else {
+			this.keyCode = keyCode;
+		}
+		this.mod1 = mod1 !== undefined && mod1 !== null ? mod1 : false;
+		this.mod2 = mod2 !== undefined && mod2 !== null ? mod2 : false;
+		this.mod3 = mod3 !== undefined && mod3 !== null ? mod3 : false;
+		this.mod4 = mod4 !== undefined && mod4 !== null ? mod4 : false;
+	}
+	KeyStroke.prototype = /** @lends orion.KeyStroke.prototype */ {
+		getKeys: function() {
+			return [this];
+		},
+		/**
+		 * Determines either this key stroke matches the specifed event.  It can match either a
+		 * a whole sequence of key events or a single key event at a specified index.
+		 * <p>
+		 * <code>KeyStroke</code> only matches single key events. <code>KeySequence</code> handles
+		 * matching a sequence of events.
+		 * </p>
+		 * TODO explain this better
+		 * 
+		 * @param {DOMEvent|DOMEvent[]} e the key event or list of events to match.
+		 * @param index the key event to match.
+		 * @returns {Boolean} <code>true</code> whether the key binding matches the key event.
+		 *
+		 * @see orion.KeySequence#match
+		 */
+		match: function (e, index) {
+			if (index !== undefined) {
+				if (index !== 0) {
+					return false;
+				}
+			} else {
+				if (e instanceof Array) {
+					if (e.length > 1) {
+						return false;
+					}
+					e = e[0];
+				}
+			}
+			if (e.type !== this.type) {
+				return false;
+			}
+			if (this.keyCode === e.keyCode || this.keyCode === String.fromCharCode(util.isOpera ? e.which : (e.charCode !== undefined ? e.charCode : e.keyCode))) {
+				var mod1 = util.isMac ? e.metaKey : e.ctrlKey;
+				if (this.mod1 !== mod1) { return false; }
+				if (this.type === "keydown") { //$NON-NLS-0$
+					if (this.mod2 !== e.shiftKey) { return false; }
+				}
+				if (this.mod3 !== e.altKey) { return false; }
+				if (util.isMac && this.mod4 !== e.ctrlKey) { return false; }
+				return true;
+			}
+			return false;
+		},
+		/**
+		 * Returns whether this key stroke is the same as the given parameter.
+		 * 
+		 * @param {orion.KeyBinding} kb the key binding to compare with.
+		 * @returns {Boolean} whether or not the parameter and the receiver describe the same key binding.
+		 */
+		equals: function(kb) {
+			if (!kb) { return false; }
+			if (this.keyCode !== kb.keyCode) { return false; }
+			if (this.mod1 !== kb.mod1) { return false; }
+			if (this.mod2 !== kb.mod2) { return false; }
+			if (this.mod3 !== kb.mod3) { return false; }
+			if (this.mod4 !== kb.mod4) { return false; }
+			if (this.type !== kb.type) { return false; }
+			return true;
+		} 
+	};
+	
+	/**
+	 * Constructs a new key sequence with the given key strokes.
+	 * 
+	 * @param {orion.KeyStroke[]} keys the key strokes for this sequence.
+	 * 
+	 * @class A KeySequence represents of a list of key codes and a modifiers state that can be triggered by the user using the keyboard.
+	 * @name orion.KeySequence
+	 * 
+	 * @property {orion.KeyStroke[]} keys the list of key strokes.
+	 *
+	 * @see orion.editor.TextView#setKeyBinding
+	 */
+	function KeySequence (keys) {
+		this.keys = keys;
+	}
+	KeySequence.prototype = /** @lends orion.KeySequence.prototype */ {
+		getKeys: function() {
+			return this.keys.slice(0);
+		},
+		match: function (e, index) {
+			var keys = this.keys;
+			if (index !== undefined) {
+				if (index > keys.length) {
+					return false;
+				}
+				if (keys[index].match(e)) {
+					if (index === keys.length - 1) {
+						return true;
+					}
+					return index + 1;
+				}
+				return false;
+			} else {
+				if (!(e instanceof Array)) {
+					e = [e];
+				}
+				if (e.length > keys.length) {
+					return false;
+				}
+				var i;
+				for (i = 0; i < e.length; i++) {
+					if (!keys[i].match(e[i])) {
+						return false;
+					}
+				}
+				if (i === keys.length) {
+					return true;
+				}
+				return i;
+			}
+		},
+		/**
+		 * Returns whether this key sequence is the same as the given parameter.
+		 * 
+		 * @param {orion.KeyBinding|orion.KeySequence} kb the key binding to compare with.
+		 * @returns {Boolean} whether or not the parameter and the receiver describe the same key binding.
+		 */
+		equals: function(kb) {
+			if (!kb.keys) { return false; }
+			if (kb.keys.length !== this.keys.length) { return false; }
+			for (var i=0; i<kb.keys.length; i++) {
+				if (!kb.keys[i].equals(this.keys[i])) { return false; }
+			}
+			return true;
+		}	
+	};
+	
+	return {
+		KeyBinding: KeyStroke, // for backwards compatibility
+		KeyStroke: KeyStroke,
+		KeySequence: KeySequence
+	};
+});
+
+/*******************************************************************************
+ * @license
+ * Copyright (c) 2013 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials are made 
+ * available under the terms of the Eclipse Public License v1.0 
+ * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
+ * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+ 
+/*global define window */
+
+define("orion/editor/keyModes", [ //$NON-NLS-0$
+		"orion/keyBinding", //$NON-NLS-0$
+		"orion/util" //$NON-NLS-0$
+], function(mKeyBinding, util) {
+
+	function KeyMode(view) {
+		if (!view) {
+			return;
+		}
+		this._view = view;
+		this._keyBindings = this.createKeyBindings();
+		this._keyBindingIndex = 0;
+	}
+	KeyMode.prototype = /** @lends orion.editor.KeyMode.prototype */ {
+		createKeyBindings: function () {
+			return [];
+		},
+		/**
+		 * Returns all the key bindings associated to the given action ID.
+		 *
+		 * @param {String} actionID the action ID.
+		 * @returns {orion.editor.KeyBinding[]} the array of key bindings associated to the given action ID.
+		 *
+		 * @see #setKeyBinding
+		 * @see #setAction
+		 */
+		getKeyBindings: function (actionID) {
+			var result = [];
+			var keyBindings = this._keyBindings;
+			for (var i = 0; i < keyBindings.length; i++) {
+				if (keyBindings[i].actionID === actionID) {
+					result.push(keyBindings[i].keyBinding);
+				}
+			}
+			return result;
+		},
+		getView: function() {
+			return this._view;
+		},
+		isActive: function () {
+			return this._view.getKeyModes().indexOf(this) !== -1;
+		},
+		match: function(e) {
+			switch (e.keyCode) {
+				case 16: /* Shift */
+				case 17: /* Control */
+				case 18: /* Alt */
+				case 91: /* Command */
+					return undefined;
+			}
+			var keyBindingIndex = this._keyBindingIndex;
+			var keyBindings = this._matchingKeyBindings || this._keyBindings;
+			var matchingKeyBindings = [];
+			for (var i = 0; i < keyBindings.length; i++) {
+				var kb = keyBindings[i];
+				var keyBinding = kb.keyBinding;
+				var match = keyBinding.match(e, keyBindingIndex);
+				if (match === true) {
+					this._keyBindingIndex = 0;
+					this._matchingKeyBindings = null;
+					return kb.actionID;
+				} else if (typeof match === "number") { //$NON-NLS-0$
+					matchingKeyBindings.push(kb);
+				}
+			}
+			if (matchingKeyBindings.length === 0) {
+				this._keyBindingIndex = 0;
+				this._matchingKeyBindings = null;
+			} else {
+				this._keyBindingIndex++;
+				this._matchingKeyBindings = matchingKeyBindings;
+				return "noop"; //$NON-NLS-0$
+			}
+			return undefined;
+		},
+		/**
+		 * Associates a key binding with the given action ID. Any previous
+		 * association with the specified key binding is overwriten. If the
+		 * action ID is <code>null</code>, the association is removed.
+		 * 
+		 * @param {orion.editor.KeyBinding} keyBinding the key binding
+		 * @param {String} actionID the action ID
+		 */
+		setKeyBinding: function(keyBinding, actionID) {
+			var keyBindings = this._keyBindings;
+			for (var i = 0; i < keyBindings.length; i++) {
+				var kb = keyBindings[i]; 
+				if (kb.keyBinding.equals(keyBinding)) {
+					if (actionID) {
+						kb.actionID = actionID;
+					} else {
+						if (kb.predefined) {
+							kb.actionID = "noop"; //$NON-NLS-0$
+						} else {
+							keyBindings.splice(i, 1);
+						}
+					}
+					return;
+				}
+			}
+			if (actionID) {
+				keyBindings.push({keyBinding: keyBinding, actionID: actionID});
+			}
+		},
+		setView: function(view) {
+			this._view = view;
+		}
+	};
+	
+	function DefaultKeyMode(view) {
+		KeyMode.call(this, view);
+	}
+	DefaultKeyMode.prototype = new KeyMode();
+	DefaultKeyMode.prototype.createKeyBindings = function () {
+		var KeyBinding = mKeyBinding.KeyBinding;
+		//no duplicate keybindings
+		var bindings = [];
+
+		// Cursor Navigation
+		bindings.push({actionID: "lineUp",		keyBinding: new KeyBinding(38), predefined: true}); //$NON-NLS-0$
+		bindings.push({actionID: "lineDown",	keyBinding: new KeyBinding(40), predefined: true}); //$NON-NLS-0$
+		bindings.push({actionID: "charPrevious",	keyBinding: new KeyBinding(37), predefined: true}); //$NON-NLS-0$
+		bindings.push({actionID: "charNext",	keyBinding: new KeyBinding(39), predefined: true}); //$NON-NLS-0$
+		if (util.isMac) {
+			bindings.push({actionID: "scrollPageUp",		keyBinding: new KeyBinding(33), predefined: true}); //$NON-NLS-0$
+			bindings.push({actionID: "scrollPageDown",	keyBinding: new KeyBinding(34), predefined: true}); //$NON-NLS-0$
+			bindings.push({actionID: "pageUp",		keyBinding: new KeyBinding(33, null, null, true), predefined: true}); //$NON-NLS-0$
+			bindings.push({actionID: "pageDown",	keyBinding: new KeyBinding(34, null, null, true), predefined: true}); //$NON-NLS-0$
+			bindings.push({actionID: "lineStart",	keyBinding: new KeyBinding(37, true), predefined: true}); //$NON-NLS-0$
+			bindings.push({actionID: "lineEnd",		keyBinding: new KeyBinding(39, true), predefined: true}); //$NON-NLS-0$
+			bindings.push({actionID: "wordPrevious",	keyBinding: new KeyBinding(37, null, null, true), predefined: true}); //$NON-NLS-0$
+			bindings.push({actionID: "wordNext",	keyBinding: new KeyBinding(39, null, null, true), predefined: true}); //$NON-NLS-0$
+			bindings.push({actionID: "scrollTextStart",	keyBinding: new KeyBinding(36), predefined: true}); //$NON-NLS-0$
+			bindings.push({actionID: "scrollTextEnd",		keyBinding: new KeyBinding(35), predefined: true}); //$NON-NLS-0$
+			bindings.push({actionID: "textStart",	keyBinding: new KeyBinding(38, true), predefined: true}); //$NON-NLS-0$
+			bindings.push({actionID: "textEnd",		keyBinding: new KeyBinding(40, true), predefined: true}); //$NON-NLS-0$
+			bindings.push({actionID: "scrollPageUp",	keyBinding: new KeyBinding(38, null, null, null, true), predefined: true}); //$NON-NLS-0$
+			bindings.push({actionID: "scrollPageDown",		keyBinding: new KeyBinding(40, null, null, null, true), predefined: true}); //$NON-NLS-0$
+			bindings.push({actionID: "lineStart",	keyBinding: new KeyBinding(37, null, null, null, true), predefined: true}); //$NON-NLS-0$
+			bindings.push({actionID: "lineEnd",		keyBinding: new KeyBinding(39, null, null, null, true), predefined: true}); //$NON-NLS-0$
+			//TODO These two actions should be changed to paragraph start and paragraph end  when word wrap is implemented
+			bindings.push({actionID: "lineStart",	keyBinding: new KeyBinding(38, null, null, true), predefined: true}); //$NON-NLS-0$
+			bindings.push({actionID: "lineEnd",		keyBinding: new KeyBinding(40, null, null, true), predefined: true}); //$NON-NLS-0$
+		} else {
+			bindings.push({actionID: "pageUp",		keyBinding: new KeyBinding(33), predefined: true}); //$NON-NLS-0$
+			bindings.push({actionID: "pageDown",	keyBinding: new KeyBinding(34), predefined: true}); //$NON-NLS-0$
+			bindings.push({actionID: "lineStart",	keyBinding: new KeyBinding(36), predefined: true}); //$NON-NLS-0$
+			bindings.push({actionID: "lineEnd",		keyBinding: new KeyBinding(35), predefined: true}); //$NON-NLS-0$
+			bindings.push({actionID: "wordPrevious",	keyBinding: new KeyBinding(37, true), predefined: true}); //$NON-NLS-0$
+			bindings.push({actionID: "wordNext",	keyBinding: new KeyBinding(39, true), predefined: true}); //$NON-NLS-0$
+			bindings.push({actionID: "textStart",	keyBinding: new KeyBinding(36, true), predefined: true}); //$NON-NLS-0$
+			bindings.push({actionID: "textEnd",		keyBinding: new KeyBinding(35, true), predefined: true}); //$NON-NLS-0$
+		}
+		if (util.isFirefox && util.isLinux) {
+			bindings.push({actionID: "lineUp",		keyBinding: new KeyBinding(38, true), predefined: true}); //$NON-NLS-0$
+			bindings.push({actionID: "lineDown",	keyBinding: new KeyBinding(40, true), predefined: true}); //$NON-NLS-0$
+		}
+		if (util.isWindows) {
+			bindings.push({actionID: "scrollLineUp",	keyBinding: new KeyBinding(38, true), predefined: true}); //$NON-NLS-0$
+			bindings.push({actionID: "scrollLineDown",	keyBinding: new KeyBinding(40, true), predefined: true}); //$NON-NLS-0$
+		}
+
+		// Select Cursor Navigation
+		bindings.push({actionID: "selectLineUp",		keyBinding: new KeyBinding(38, null, true), predefined: true}); //$NON-NLS-0$
+		bindings.push({actionID: "selectLineDown",		keyBinding: new KeyBinding(40, null, true), predefined: true}); //$NON-NLS-0$
+		bindings.push({actionID: "selectCharPrevious",	keyBinding: new KeyBinding(37, null, true), predefined: true}); //$NON-NLS-0$
+		bindings.push({actionID: "selectCharNext",		keyBinding: new KeyBinding(39, null, true), predefined: true}); //$NON-NLS-0$
+		bindings.push({actionID: "selectPageUp",		keyBinding: new KeyBinding(33, null, true), predefined: true}); //$NON-NLS-0$
+		bindings.push({actionID: "selectPageDown",		keyBinding: new KeyBinding(34, null, true), predefined: true}); //$NON-NLS-0$
+		if (util.isMac) {
+			bindings.push({actionID: "selectLineStart",	keyBinding: new KeyBinding(37, true, true), predefined: true}); //$NON-NLS-0$
+			bindings.push({actionID: "selectLineEnd",		keyBinding: new KeyBinding(39, true, true), predefined: true}); //$NON-NLS-0$
+			bindings.push({actionID: "selectWordPrevious",	keyBinding: new KeyBinding(37, null, true, true), predefined: true}); //$NON-NLS-0$
+			bindings.push({actionID: "selectWordNext",	keyBinding: new KeyBinding(39, null, true, true), predefined: true}); //$NON-NLS-0$
+			bindings.push({actionID: "selectTextStart",	keyBinding: new KeyBinding(36, null, true), predefined: true}); //$NON-NLS-0$
+			bindings.push({actionID: "selectTextEnd",		keyBinding: new KeyBinding(35, null, true), predefined: true}); //$NON-NLS-0$
+			bindings.push({actionID: "selectTextStart",	keyBinding: new KeyBinding(38, true, true), predefined: true}); //$NON-NLS-0$
+			bindings.push({actionID: "selectTextEnd",		keyBinding: new KeyBinding(40, true, true), predefined: true}); //$NON-NLS-0$
+			bindings.push({actionID: "selectLineStart",	keyBinding: new KeyBinding(37, null, true, null, true), predefined: true}); //$NON-NLS-0$
+			bindings.push({actionID: "selectLineEnd",		keyBinding: new KeyBinding(39, null, true, null, true), predefined: true}); //$NON-NLS-0$
+			//TODO These two actions should be changed to select paragraph start and select paragraph end  when word wrap is implemented
+			bindings.push({actionID: "selectLineStart",	keyBinding: new KeyBinding(38, null, true, true), predefined: true}); //$NON-NLS-0$
+			bindings.push({actionID: "selectLineEnd",		keyBinding: new KeyBinding(40, null, true, true), predefined: true}); //$NON-NLS-0$
+		} else {
+			if (util.isLinux) {
+				bindings.push({actionID: "selectWholeLineUp",		keyBinding: new KeyBinding(38, true, true), predefined: true}); //$NON-NLS-0$
+				bindings.push({actionID: "selectWholeLineDown",		keyBinding: new KeyBinding(40, true, true), predefined: true}); //$NON-NLS-0$
+			}
+			bindings.push({actionID: "selectLineStart",		keyBinding: new KeyBinding(36, null, true), predefined: true}); //$NON-NLS-0$
+			bindings.push({actionID: "selectLineEnd",		keyBinding: new KeyBinding(35, null, true), predefined: true}); //$NON-NLS-0$
+			bindings.push({actionID: "selectWordPrevious",	keyBinding: new KeyBinding(37, true, true), predefined: true}); //$NON-NLS-0$
+			bindings.push({actionID: "selectWordNext",		keyBinding: new KeyBinding(39, true, true), predefined: true}); //$NON-NLS-0$
+			bindings.push({actionID: "selectTextStart",		keyBinding: new KeyBinding(36, true, true), predefined: true}); //$NON-NLS-0$
+			bindings.push({actionID: "selectTextEnd",		keyBinding: new KeyBinding(35, true, true), predefined: true}); //$NON-NLS-0$
+		}
+		
+		//Undo stack
+		bindings.push({actionID: "undo", keyBinding: new mKeyBinding.KeyBinding('z', true), predefined: true}); //$NON-NLS-1$ //$NON-NLS-0$
+		if (util.isMac) {
+			bindings.push({actionID: "redo", keyBinding: new mKeyBinding.KeyBinding('z', true, true), predefined: true}); //$NON-NLS-1$ //$NON-NLS-0$
+		} else {
+			bindings.push({actionID: "redo", keyBinding: new mKeyBinding.KeyBinding('y', true), predefined: true}); //$NON-NLS-1$ //$NON-NLS-0$
+		}
+
+		//Misc
+		bindings.push({actionID: "deletePrevious",		keyBinding: new KeyBinding(8), predefined: true}); //$NON-NLS-0$
+		bindings.push({actionID: "deletePrevious",		keyBinding: new KeyBinding(8, null, true), predefined: true}); //$NON-NLS-0$
+		bindings.push({actionID: "deleteNext",		keyBinding: new KeyBinding(46), predefined: true}); //$NON-NLS-0$
+		bindings.push({actionID: "deleteWordPrevious",	keyBinding: new KeyBinding(8, true), predefined: true}); //$NON-NLS-0$
+		bindings.push({actionID: "deleteWordPrevious",	keyBinding: new KeyBinding(8, true, true), predefined: true}); //$NON-NLS-0$
+		bindings.push({actionID: "deleteWordNext",		keyBinding: new KeyBinding(46, true), predefined: true}); //$NON-NLS-0$
+		bindings.push({actionID: "tab",			keyBinding: new KeyBinding(9), predefined: true}); //$NON-NLS-0$
+		bindings.push({actionID: "shiftTab",			keyBinding: new KeyBinding(9, null, true), predefined: true}); //$NON-NLS-0$
+		bindings.push({actionID: "enter",			keyBinding: new KeyBinding(13), predefined: true}); //$NON-NLS-0$
+		bindings.push({actionID: "enter",			keyBinding: new KeyBinding(13, null, true), predefined: true}); //$NON-NLS-0$
+		bindings.push({actionID: "selectAll",		keyBinding: new KeyBinding('a', true), predefined: true}); //$NON-NLS-1$ //$NON-NLS-0$
+		bindings.push({actionID: "toggleTabMode",	keyBinding: new KeyBinding('m', true), predefined: true}); //$NON-NLS-1$ //$NON-NLS-0$
+		if (util.isMac) {
+			bindings.push({actionID: "deleteNext",		keyBinding: new KeyBinding(46, null, true), predefined: true}); //$NON-NLS-0$
+			bindings.push({actionID: "deleteWordPrevious",	keyBinding: new KeyBinding(8, null, null, true), predefined: true}); //$NON-NLS-0$
+			bindings.push({actionID: "deleteWordNext",		keyBinding: new KeyBinding(46, null, null, true), predefined: true}); //$NON-NLS-0$
+		}
+		
+		bindings.push({actionID: "toggleWrapMode",		keyBinding: new mKeyBinding.KeyBinding('w', true, false, true)}); //$NON-NLS-1$ //$NON-NLS-0$
+		bindings.push({actionID: "toggleOverwriteMode",		keyBinding: new mKeyBinding.KeyBinding(45)}); //$NON-NLS-0$
+		
+		/*
+		* Feature in IE/Chrome: prevent ctrl+'u', ctrl+'i', and ctrl+'b' from applying styles to the text.
+		*
+		* Note that Chrome applies the styles on the Mac with Ctrl instead of Cmd.
+		*/
+		if (!util.isFirefox) {
+			var isMacChrome = util.isMac && util.isChrome;
+			bindings.push({actionID: "noop", keyBinding: new KeyBinding('u', !isMacChrome, false, false, isMacChrome), predefined: true}); //$NON-NLS-1$ //$NON-NLS-0$
+			bindings.push({actionID: "noop", keyBinding: new KeyBinding('i', !isMacChrome, false, false, isMacChrome), predefined: true}); //$NON-NLS-1$ //$NON-NLS-0$
+			bindings.push({actionID: "noop", keyBinding: new KeyBinding('b', !isMacChrome, false, false, isMacChrome), predefined: true}); //$NON-NLS-1$ //$NON-NLS-0$
+		}
+
+		if (util.isFirefox) {
+			bindings.push({actionID: "copy", keyBinding: new KeyBinding(45, true), predefined: true}); //$NON-NLS-0$
+			bindings.push({actionID: "paste", keyBinding: new KeyBinding(45, null, true), predefined: true}); //$NON-NLS-0$
+			bindings.push({actionID: "cut", keyBinding: new KeyBinding(46, null, true), predefined: true}); //$NON-NLS-0$
+		}
+
+		// Add the emacs Control+ ... key bindings.
+		if (util.isMac) {
+			bindings.push({actionID: "lineStart", keyBinding: new KeyBinding("a", false, false, false, true), predefined: true}); //$NON-NLS-1$ //$NON-NLS-0$
+			bindings.push({actionID: "lineEnd", keyBinding: new KeyBinding("e", false, false, false, true), predefined: true}); //$NON-NLS-1$ //$NON-NLS-0$
+			bindings.push({actionID: "lineUp", keyBinding: new KeyBinding("p", false, false, false, true), predefined: true}); //$NON-NLS-1$ //$NON-NLS-0$
+			bindings.push({actionID: "lineDown", keyBinding: new KeyBinding("n", false, false, false, true), predefined: true}); //$NON-NLS-1$ //$NON-NLS-0$
+			bindings.push({actionID: "charPrevious", keyBinding: new KeyBinding("b", false, false, false, true), predefined: true}); //$NON-NLS-1$ //$NON-NLS-0$
+			bindings.push({actionID: "charNext", keyBinding: new KeyBinding("f", false, false, false, true), predefined: true}); //$NON-NLS-1$ //$NON-NLS-0$
+			bindings.push({actionID: "deletePrevious", keyBinding: new KeyBinding("h", false, false, false, true), predefined: true}); //$NON-NLS-1$ //$NON-NLS-0$
+			bindings.push({actionID: "deleteNext", keyBinding: new KeyBinding("d", false, false, false, true), predefined: true}); //$NON-NLS-1$ //$NON-NLS-0$
+			bindings.push({actionID: "deleteLineEnd", keyBinding: new KeyBinding("k", false, false, false, true), predefined: true}); //$NON-NLS-1$ //$NON-NLS-0$
+			if (util.isFirefox) {
+				bindings.push({actionID: "scrollPageDown", keyBinding: new KeyBinding("v", false, false, false, true), predefined: true}); //$NON-NLS-1$ //$NON-NLS-0$
+				bindings.push({actionID: "deleteLineStart", keyBinding: new KeyBinding("u", false, false, false, true), predefined: true}); //$NON-NLS-1$ //$NON-NLS-0$
+				bindings.push({actionID: "deleteWordPrevious", keyBinding: new KeyBinding("w", false, false, false, true), predefined: true}); //$NON-NLS-1$ //$NON-NLS-0$
+			} else {
+				bindings.push({actionID: "pageDown", keyBinding: new KeyBinding("v", false, false, false, true), predefined: true}); //$NON-NLS-1$ //$NON-NLS-0$
+				bindings.push({actionID: "centerLine", keyBinding: new KeyBinding("l", false, false, false, true), predefined: true}); //$NON-NLS-1$ //$NON-NLS-0$
+				bindings.push({actionID: "enterNoCursor", keyBinding: new KeyBinding("o", false, false, false, true), predefined: true}); //$NON-NLS-1$ //$NON-NLS-0$
+				//TODO implement: y (yank), t (transpose)
+			}
+		}
+		return bindings;
+	};
+	
+	return {
+		KeyMode: KeyMode,
+		DefaultKeyMode: DefaultKeyMode
+	};
+});
+
+/*******************************************************************************
+ * @license
+ * Copyright (c) 2013 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials are made 
+ * available under the terms of the Eclipse Public License v1.0 
+ * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
+ * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+ 
+/*globals define*/
+
+define("orion/editor/textTheme", //$NON-NLS-0$
+[
+	'require', //$NON-NLS-0$
+	'orion/editor/eventTarget', //$NON-NLS-0$
+	'orion/util' //$NON-NLS-0$
+], function(require, mEventTarget, util) {
+	var THEME_PREFIX = "orion-theme-"; //$NON-NLS-0$
+	
+	var Themes = {};
+
+	/**
+	 * Constructs a new text theme. 
+	 * 
+	 * @class A TextTheme is a class used to specify an editor theme.
+	 * @name orion.editor.TextTheme
+	 * @borrows orion.editor.EventTarget#addEventListener as #addEventListener
+	 * @borrows orion.editor.EventTarget#removeEventListener as #removeEventListener
+	 * @borrows orion.editor.EventTarget#dispatchEvent as #dispatchEvent
+	 */
+	function TextTheme(options) {
+		options = options || {};
+		this._document = options.document || document;
+	}
+
+	/**
+	 * Gets an instance of <code>orion.editor.TextTheme</code> by name. If the name
+	 * paramenter is not speficed the default text theme instance is returned.
+	 * Subsequent calls of <code>getTheme</code> with the same name will return
+	 * the same instance.
+	 */
+	TextTheme.getTheme = function(name) {
+		name = name || "default"; //$NON-NLS-0$
+		var theme = Themes[name];
+		if (!theme) {
+			theme = Themes[name] = new TextTheme();
+		}
+		return theme;
+	};
+
+	TextTheme.prototype = /** @lends orion.editor.TextTheme.prototype */ {
+		/**
+		 * Returns the theme className.
+		 *
+		 * @see #setThemeClass
+		 */
+		getThemeClass: function() {
+			return this._themeClass;
+		},
+		/**
+		 * @class This object represents a style sheet for a theme manager.
+		 * <p>
+		 * <b>See:</b><br/>
+		 * {@link orion.editor.TextTheme#setThemeClass}
+		 * </p>
+		 * @name orion.editor.ThemeStyleSheet
+		 * 
+		 * @property {String} href The href of the stylesheet
+		 */
+		/**
+		 * Sets the theme className and style sheet.
+		 * <p>
+		 * If the <code>stylesheet</code> parameter is a string, it represents an inline
+		 * CSS and it will be added to the document as a <i>STYLE</i> tag element.  If the
+		 * <code>stylesheet</code> parameter is a <code>orion.editor.ThemeStyleSheet</code>,
+		 * its href property is loaded as either a <i>STYLE</i> tag element or as a <i>LINK</i>
+		 * tag element.
+		 * </p>
+		 * <p>
+		 * Listeners of the ThemeChanged event are notify once the styled sheet is loaded
+		 * into the document.
+		 * </p>
+		 *
+		 * @param {String} className the new theme className.
+		 * @param {String|orion.editor.ThemeStyleSheet} styleSheet the CSS stylesheet for the new theme className.
+		 *
+		 * @see #getThemeClass
+		 * @see #onThemeChanged
+		 */
+		 setThemeClass: function(className, styleSheet) {
+			var self = this;
+			var oldThemeClass = self._themeClass;	
+			self._themeClass = className;
+			this._load(className, styleSheet, function() {
+				self.onThemeChanged({
+					type: "ThemeChanged", //$NON-NLS-0$
+					oldValue: oldThemeClass,
+					newValue: self.getThemeClass()
+				});
+			});
+		},
+		/**
+		 * @class This is the event sent when the theme className or style sheet has changed.
+		 * <p>
+		 * <b>See:</b><br/>
+		 * {@link orion.editor.TextTheme}<br/>
+		 * {@link orion.editor.TextTheme#event:onThemeChanged}
+		 * </p>
+		 * @name orion.editor.ThemeChangedEvent
+		 * 
+		 * @property {String} oldValue The old theme clasName.
+		 * @property {String} newValue The new theme className.
+		 */
+		/**
+		 * This event is sent when the theme clasName has changed and its style sheet has been loaded in the document.
+		 *
+		 * @event
+		 * @param {orion.editor.ThemeChangedEvent} themeChangedEvent the event
+		 */
+		onThemeChanged: function(themeChangedEvent) {
+			return this.dispatchEvent(themeChangedEvent);
+		},
+		/**
+		 * @private
+		 */
+		buildStyleSheet: function(themeClass, settings) {
+			
+			var result = [];
+			result.push("");
+			
+			result.push("." + themeClass + " {"); //$NON-NLS-1$ //$NON-NLS-0$
+			if (settings.fontFamily) {
+				result.push("\tfont-family: " + settings.fontFamily + ";"); //$NON-NLS-1$ //$NON-NLS-0$
+			}
+			if (settings.fontSize) {
+				result.push("\tfont-size: " + settings.fontSize + ";"); //$NON-NLS-1$ //$NON-NLS-0$
+			}
+			if (settings.fontSize) {			
+				result.push("\tcolor: " + settings.text + ";"); //$NON-NLS-1$ //$NON-NLS-0$
+			}
+			result.push("}"); //$NON-NLS-0$
+			
+			//From textview.css
+			result.push("." + themeClass + ".textview {"); //$NON-NLS-1$ //$NON-NLS-0$
+			if (settings.background) {		
+				result.push("\tbackground-color: " + settings.background + ";"); //$NON-NLS-1$ //$NON-NLS-0$
+			}
+			result.push("}"); //$NON-NLS-0$
+			
+			function defineRule(className, value, isBackground) {
+				if (value) {
+					result.push("." + themeClass + " ." + className + " {"); //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+					result.push("\t" + (isBackground ? "background-color" : "color") + ": " + value + ";"); //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+					result.push("}"); //$NON-NLS-0$
+				}
+			}
+			
+			//From rulers.css
+			defineRule("ruler.annotations", settings.annotationRuler, true); //$NON-NLS-0$
+			defineRule("ruler.lines", settings.annotationRuler, true); //$NON-NLS-0$
+			defineRule("ruler.folding", settings.annotationRuler, true); //$NON-NLS-0$
+			defineRule("ruler.overview", settings.overviewRuler, true); //$NON-NLS-0$
+			defineRule("rulerLines", settings.lineNumber, false); //$NON-NLS-0$
+			defineRule("rulerLines.even", settings.lineNumberEven, false); //$NON-NLS-0$
+			defineRule("rulerLines.odd", settings.lineNumberOdd, false); //$NON-NLS-0$
+			
+			//From annotations.css
+			defineRule("annotationLine.currentLine", settings.currentLine, true); //$NON-NLS-0$
+			
+			//From default-theme.css
+			defineRule("entity-name-tag", settings.keyword, false); //$NON-NLS-0$
+			defineRule("entity-other-attribute-name", settings.attribute, false); //$NON-NLS-0$
+			defineRule("string-quoted", settings.string, false); //$NON-NLS-0$
+			
+			//From textstyler.css
+			defineRule("line_caret", settings.currentLine, true); //$NON-NLS-0$
+			defineRule("token_keyword", settings.keyword, false); //$NON-NLS-0$
+			defineRule("token_string", settings.string, false); //$NON-NLS-0$
+			defineRule("token_singleline_comment", settings.comment, false); //$NON-NLS-0$
+			defineRule("token_multiline_comment", settings.comment, false); //$NON-NLS-0$
+			defineRule("token_doc_comment", settings.comment, false); //$NON-NLS-0$
+			defineRule("token_doc_html_markup", settings.comment, false); //$NON-NLS-0$
+			
+			return result.join("\n"); //$NON-NLS-0$
+		},
+		/**
+		 * @private
+		 */
+		_createStyle: function(className, styleSheet, callback, link) {
+			var document = this._document;
+			var id = THEME_PREFIX + className;
+			var node = document.getElementById(id);
+			if (node) {
+				if (link || node.firstChild.data === styleSheet) {
+					return;
+				}
+				node.removeChild(node.firstChild);
+				node.appendChild(document.createTextNode(styleSheet));
+			} else {
+				if (link) {
+					node = util.createElement(document, "link"); //$NON-NLS-0$
+					node.rel = "stylesheet"; //$NON-NLS-0$
+					node.type = "text/css"; //$NON-NLS-0$
+					node.href = styleSheet;
+					node.addEventListener("load", function() { //$NON-NLS-0$
+						callback();
+					});
+				} else {
+					node = util.createElement(document, "style"); //$NON-NLS-0$
+					node.appendChild(document.createTextNode(styleSheet));
+				}
+				node.id = id;
+				var head = document.getElementsByTagName("head")[0] || document.documentElement; //$NON-NLS-0$
+				head.appendChild(node);
+			}
+			if (!link) {
+				callback();
+			}
+		},
+		/**
+		 * @private
+		 */
+		_load: function (className, styleSheet, callback) {
+			if (!className) {
+				callback();
+				return;
+			}
+			if (typeof styleSheet === "string") { //$NON-NLS-0$
+				this._createStyle(className, styleSheet, callback);
+				return;
+			}
+			var href = styleSheet.href;
+			var extension = ".css"; //$NON-NLS-0$
+			if (href.substring(href.length - extension.length) !== extension) {
+				href += extension;
+			}
+			if (/^\//.test(href) || /[a-zA-Z0-9]+:\/\//i.test(href) || !require.toUrl /* almond cannot load dynamically */) {
+				this._createStyle(className, href, callback, true);
+			} else {
+				var self = this;
+				require(["text!" + href], function(cssText) { //$NON-NLS-0$
+					self._createStyle(className, cssText, callback, false);
+				});
+			}
+		}
+	};
+	mEventTarget.EventTarget.addMixin(TextTheme.prototype);
+	
+	return {
+		TextTheme: TextTheme
+	};
+});
+
+/*******************************************************************************
+ * @license
+ * Copyright (c) 2010, 2012 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials are made 
+ * available under the terms of the Eclipse Public License v1.0 
+ * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
+ * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
+ * 
+ * Contributors: 
+ *		Felipe Heidrich (IBM Corporation) - initial API and implementation
+ *		Silenio Quarti (IBM Corporation) - initial API and implementation
+ *		Mihai Sucan (Mozilla Foundation) - fix for Bug#334583 Bug#348471 Bug#349485 Bug#350595 Bug#360726 Bug#361180 Bug#362835 Bug#362428 Bug#362286 Bug#354270 Bug#361474 Bug#363945 Bug#366312 Bug#370584
+ ******************************************************************************/
+
+/*global define document*/
+
+define("orion/editor/textView", [ //$NON-NLS-0$
+	'i18n!orion/editor/nls/messages', //$NON-NLS-0$
+	'orion/editor/textModel', //$NON-NLS-0$
+	'orion/editor/keyModes', //$NON-NLS-0$
+	'orion/editor/eventTarget', //$NON-NLS-0$
+	'orion/editor/textTheme', //$NON-NLS-0$
+	'orion/util' //$NON-NLS-0$
+], function(messages, mTextModel, mKeyModes, mEventTarget, mTextTheme, util) {
+
+	/** @private */
+	function getWindow(document) {
+		return document.defaultView || document.parentWindow;
+	}
+	/** @private */
+	function addHandler(node, type, handler, capture) {
+		if (typeof node.addEventListener === "function") { //$NON-NLS-0$
+			node.addEventListener(type, handler, capture === true);
+		} else {
+			node.attachEvent("on" + type, handler); //$NON-NLS-0$
+		}
+	}
+	/** @private */
+	function removeHandler(node, type, handler, capture) {
+		if (typeof node.removeEventListener === "function") { //$NON-NLS-0$
+			node.removeEventListener(type, handler, capture === true);
+		} else {
+			node.detachEvent("on" + type, handler); //$NON-NLS-0$
+		}
+	}
+	/** @private */
+	function applyStyle(style, node, reset) {
+		if (reset) {
+			node.className = "";
+			var attrs = node.attributes;
+			for (var i= attrs.length; i-->0;) {
+				if (!util.isIE || util.isIE >= 9 || (util.isIE < 9 && attrs[i].specified)) {
+					node.removeAttribute(attrs[i].name); 
+				}
+			}
+		}
+		if (!style) {
+			return;
+		}
+		if (style.styleClass) {
+			node.className = style.styleClass;
+		}
+		var properties = style.style;
+		if (properties) {
+			for (var s in properties) {
+				if (properties.hasOwnProperty(s)) {
+					node.style[s] = properties[s];
+				}
+			}
+		}
+		var attributes = style.attributes;
+		if (attributes) {
+			for (var a in attributes) {
+				if (attributes.hasOwnProperty(a)) {
+					node.setAttribute(a, attributes[a]);
+				}
+			}
+		}
+	}
+	/** @private */
+	function clone(obj) {
+		/*Note that this code only works because of the limited types used in TextViewOptions */
+		if (obj instanceof Array) {
+			return obj.slice(0);
+		}
+		return obj;
+	}
+	/**	@private */
+	function merge(obj1, obj2) {
+		if (!obj1) {
+			return obj2;
+		}
+		if (!obj2) {
+			return obj1;
+		}
+		for (var p in obj2) {
+			if (obj2.hasOwnProperty(p)) {
+				if (!obj1.hasOwnProperty(p)) {
+					obj1[p] = obj2[p];
+				}
+			}
+		}
+		return obj1;
+	}
+	/** @private */
+	function compare(s1, s2) {
+		if (s1 === s2) { return true; }
+		if (s1 && !s2 || !s1 && s2) { return false; }
+		if ((s1 && s1.constructor === String) || (s2 && s2.constructor === String)) { return false; }
+		if (s1 instanceof Array || s2 instanceof Array) {
+			if (!(s1 instanceof Array && s2 instanceof Array)) { return false; }
+			if (s1.length !== s2.length) { return false; }
+			for (var i = 0; i < s1.length; i++) {
+				if (!compare(s1[i], s2[i])) {
+					return false;
+				}
+			}
+			return true;
+		}
+		if (!(s1 instanceof Object) || !(s2 instanceof Object)) { return false; }
+		var p;
+		for (p in s1) {
+			if (s1.hasOwnProperty(p)) {
+				if (!s2.hasOwnProperty(p)) { return false; }
+				if (!compare(s1[p], s2[p])) {return false; }
+			}
+		}
+		for (p in s2) {
+			if (!s1.hasOwnProperty(p)) { return false; }
+		}
+		return true;
+	}
+	/** @private */
+	function convertDelimiter(text, addTextFunc, addDelimiterFunc) {
+		var cr = 0, lf = 0, index = 0, length = text.length;
+		while (index < length) {
+			if (cr !== -1 && cr <= index) { cr = text.indexOf("\r", index); } //$NON-NLS-0$
+			if (lf !== -1 && lf <= index) { lf = text.indexOf("\n", index); } //$NON-NLS-0$
+			var start = index, end;
+			if (lf === -1 && cr === -1) {
+				addTextFunc(text.substring(index));
+				break;
+			}
+			if (cr !== -1 && lf !== -1) {
+				if (cr + 1 === lf) {
+					end = cr;
+					index = lf + 1;
+				} else {
+					end = cr < lf ? cr : lf;
+					index = (cr < lf ? cr : lf) + 1;
+				}
+			} else if (cr !== -1) {
+				end = cr;
+				index = cr + 1;
+			} else {
+				end = lf;
+				index = lf + 1;
+			}
+			addTextFunc(text.substring(start, end));
+			addDelimiterFunc();
+		}
+	}
+	/** @private */
+	function getBorder(node) {
+		var left,top,right,bottom;
+		var window = getWindow(node.ownerDocument);
+		if (window.getComputedStyle) {
+			var style = window.getComputedStyle(node, null);
+			left = style.getPropertyValue("border-left-width"); //$NON-NLS-0$
+			top = style.getPropertyValue("border-top-width"); //$NON-NLS-0$
+			right = style.getPropertyValue("border-right-width"); //$NON-NLS-0$
+			bottom = style.getPropertyValue("border-bottom-width"); //$NON-NLS-0$
+		} else if (node.currentStyle) {
+			left = node.currentStyle.borderLeftWidth;
+			top = node.currentStyle.borderTopWidth;
+			right = node.currentStyle.borderRightWidth;
+			bottom = node.currentStyle.borderBottomWidth;
+		}
+		return {
+			left: parseInt(left, 10) || 0,
+			top: parseInt(top, 10) || 0,
+			right: parseInt(right, 10) || 0,
+			bottom: parseInt(bottom, 10) || 0
+		};
+	}
+	/** @private */
+	function getPadding(node) {
+		var left,top,right,bottom;
+		var window = getWindow(node.ownerDocument);
+		if (window.getComputedStyle) {
+			var style = window.getComputedStyle(node, null);
+			left = style.getPropertyValue("padding-left"); //$NON-NLS-0$
+			top = style.getPropertyValue("padding-top"); //$NON-NLS-0$
+			right = style.getPropertyValue("padding-right"); //$NON-NLS-0$
+			bottom = style.getPropertyValue("padding-bottom"); //$NON-NLS-0$
+		} else if (node.currentStyle) {
+			left = node.currentStyle.paddingLeft;
+			top = node.currentStyle.paddingTop;
+			right = node.currentStyle.paddingRight;
+			bottom = node.currentStyle.paddingBottom;
+		}
+		return {
+			left: parseInt(left, 10) || 0, 
+			top: parseInt(top, 10) || 0,
+			right: parseInt(right, 10) || 0,
+			bottom: parseInt(bottom, 10) || 0
+		};
+	}
+	/** @private */
+	function getLineTrim(line) {
+		var trim = line._trim;
+		if (!trim) {
+			trim = getPadding(line);
+			var border = getBorder(line);
+			trim.left += border.left;
+			trim.top += border.top;
+			trim.right += border.right;
+			trim.bottom += border.bottom;
+			line._trim = trim;
+		}
+		return trim;
+	}
+	
+	/**
+	 * @class
+	 * @private
+	 * @name orion.editor.Animation
+	 * @description Creates an animation.
+	 * @param {Object} options Options controlling the animation.
+	 * @param {Array} options.curve Array of 2 values giving the start and end points for the animation.
+	 * @param {Number} [options.duration=350] Duration of the animation, in milliseconds.
+	 * @param {Function} [options.easing]
+	 * @param {Function} [options.onAnimate]
+	 * @param {Function} [options.onEnd]
+	 * @param {Number} [options.rate=20] The time between frames, in milliseconds.
+	 */
+	var Animation = /** @ignore */ (function() {
+		function Animation(options) {
+			this.options = options;
+		}
+		/**
+		 * Plays this animation.
+		 * @methodOf orion.editor.Animation.prototype
+		 * @name play
+		 */
+		Animation.prototype.play = function() {
+			var duration = (typeof this.options.duration === "number") ? this.options.duration : 350, //$NON-NLS-0$
+			    rate = (typeof this.options.rate === "number") ? this.options.rate : 20, //$NON-NLS-0$
+			    easing = this.options.easing || this.defaultEasing,
+			    onAnimate = this.options.onAnimate || function() {},
+			    start = this.options.curve[0],
+			    end = this.options.curve[1],
+			    range = (end - start),
+			    startedAt = -1,
+				propertyValue,
+				self = this;
+
+			function onFrame() {
+				startedAt = (startedAt === -1) ? new Date().getTime() : startedAt;
+				var now = new Date().getTime(),
+				    percentDone = (now - startedAt) / duration;
+				if (percentDone < 1) {
+					var eased = easing(percentDone);
+					propertyValue = start + (eased * range);
+					onAnimate(propertyValue);
+				} else {
+					onAnimate(end);
+					self.stop();
+				}
+			}
+			this.interval = this.options.window.setInterval(onFrame, rate);
+		};
+		/**
+		 * Stops this animation.
+		 * @methodOf orion.editor.Animation.prototype
+		 */
+		Animation.prototype.stop = function() {
+			this.options.window.clearInterval(this.interval);
+		    var onEnd = this.options.onEnd || function () {};
+			onEnd();
+		};
+		Animation.prototype.defaultEasing = function(x) {
+			return Math.sin(x * (Math.PI / 2));
+		};
+		return Animation;
+	}());
+	
+	/** 
+	 * Constructs a new Selection object.
+	 * 
+	 * @class A Selection represents a range of selected text in the view.
+	 * @name orion.editor.Selection
+	 */
+	function Selection (start, end, caret) {
+		/**
+		 * The selection start offset.
+		 *
+		 * @name orion.editor.Selection#start
+		 */
+		this.start = start;
+		/**
+		 * The selection end offset.
+		 *
+		 * @name orion.editor.Selection#end
+		 */
+		this.end = end;
+		/** @private */
+		this.caret = caret; //true if the start, false if the caret is at end
+	}
+	Selection.prototype = /** @lends orion.editor.Selection.prototype */ {
+		/** @private */
+		clone: function() {
+			return new Selection(this.start, this.end, this.caret);
+		},
+		/** @private */
+		collapse: function() {
+			if (this.caret) {
+				this.end = this.start;
+			} else {
+				this.start = this.end;
+			}
+		},
+		/** @private */
+		extend: function (offset) {
+			if (this.caret) {
+				this.start = offset;
+			} else {
+				this.end = offset;
+			}
+			if (this.start > this.end) {
+				var tmp = this.start;
+				this.start = this.end;
+				this.end = tmp;
+				this.caret = !this.caret;
+			}
+		},
+		/** @private */
+		setCaret: function(offset) {
+			this.start = offset;
+			this.end = offset;
+			this.caret = false;
+		},
+		/** @private */
+		getCaret: function() {
+			return this.caret ? this.start : this.end;
+		},
+		/** @private */
+		toString: function() {
+			return "start=" + this.start + " end=" + this.end + (this.caret ? " caret is at start" : " caret is at end"); //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		},
+		/** @private */
+		isEmpty: function() {
+			return this.start === this.end;
+		},
+		/** @private */
+		equals: function(object) {
+			return this.caret === object.caret && this.start === object.start && this.end === object.end;
+		}
+	};
+	/** @private */
+	function TextRect (rect) {
+		this.left = rect.left;
+		this.top = rect.top;
+		this.right = rect.right;
+		this.bottom = rect.bottom;
+	}
+	TextRect.prototype = /** @lends orion.editor.TextRect.prototype */ {
+		/** @private */
+		toString: function() {
+			return "{l=" + this.left + ", t=" + this.top + ", r=" + this.right + ", b=" + this.bottom + "}"; //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		}
+	};
+	/** 
+	 * Constructs a new TextLine object.
+	 * 
+	 * @class A TextLine represents a line of text in the view.
+	 * @name orion.editor.TextLine
+	 * @private
+	 */
+	function TextLine (view, lineIndex, lineDiv) {
+		/**
+		 * The view.
+		 *
+		 * @name orion.editor.TextLine#view
+		 * @private
+		 */
+		this.view = view;
+		/**
+		 * The line index.
+		 *
+		 * @name orion.editor.TextLine#lineIndex
+		 * @private
+		 */
+		this.lineIndex = lineIndex;
+		
+		this._lineDiv = lineDiv;
+	}
+	TextLine.prototype = /** @lends orion.editor.TextLine.prototype */ {
+		/** @private */
+		create: function(parent, div) {
+			if (this._lineDiv) { return; }
+			var child = this._lineDiv = this._createLine(parent, div, this.lineIndex);
+			child._line = this;
+			return child;
+		},
+		_createLine: function(parent, div, lineIndex) {
+			var view = this.view;
+			var model = view._model;
+			var lineText = model.getLine(lineIndex);
+			var lineStart = model.getLineStart(lineIndex);
+			var e = {type:"LineStyle", textView: view, lineIndex: lineIndex, lineText: lineText, lineStart: lineStart}; //$NON-NLS-0$
+			if (lineText.length < 2000) {
+				view.onLineStyle(e);
+			}
+			var lineDiv = div || util.createElement(parent.ownerDocument, "div"); //$NON-NLS-0$
+			if (!div || !compare(div.viewStyle, e.style)) {
+				applyStyle(e.style, lineDiv, div);
+				if (div) { div._trim = null; }
+				lineDiv.viewStyle = e.style;
+				lineDiv.setAttribute("role", "presentation"); //$NON-NLS-1$ //$NON-NLS-0$
+			}
+			lineDiv.lineIndex = lineIndex;
+			var ranges = [];
+			var data = {tabOffset: 0, ranges: ranges};
+			this._createRanges(e.ranges, lineText, 0, lineText.length, lineStart, data);
+			
+			/*
+			* A trailing span with a whitespace is added for three different reasons:
+			* 1. Make sure the height of each line is the largest of the default font
+			* in normal, italic, bold, and italic-bold.
+			* 2. When full selection is off, Firefox, Opera and IE9 do not extend the 
+			* selection at the end of the line when the line is fully selected. 
+			* 3. The height of a div with only an empty span is zero.
+			*/
+			var c = " "; //$NON-NLS-0$
+			if (!view._fullSelection && util.isIE < 9) {
+				/* 
+				* IE8 already selects extra space at end of a line fully selected,
+				* adding another space at the end of the line causes the selection 
+				* to look too big. The fix is to use a zero-width space (\uFEFF) instead. 
+				*/
+				c = "\uFEFF"; //$NON-NLS-0$
+			}
+			if (util.isWebkit) {
+				/*
+				* Feature in WekKit. Adding a regular white space to the line will
+				* cause the longest line in the view to wrap even though "pre" is set.
+				* The fix is to use the zero-width non-joiner character (\u200C) instead.
+				* Note: Do not use \uFEFF because in old version of Chrome this character 
+				* shows a glyph;
+				*/
+				c = "\u200C"; //$NON-NLS-0$
+			}
+			var range = {text: c, style: view._metrics.largestFontStyle, ignoreChars: 1};
+			if (ranges.length === 0 || !ranges[ranges.length - 1].style || ranges[ranges.length - 1].style.tagName !== "div") { //$NON-NLS-0$
+				ranges.push(range);
+			} else {
+				ranges.splice(ranges.length - 1, 0, range);
+			}
+			
+			var span, style, oldSpan, oldStyle, text, oldText, end = 0, oldEnd = 0, next;
+			var changeCount, changeStart;
+			if (div) {
+				var modelChangedEvent = div.modelChangedEvent;
+				if (modelChangedEvent) {
+					if (modelChangedEvent.removedLineCount === 0 && modelChangedEvent.addedLineCount === 0) {
+						changeStart = modelChangedEvent.start - lineStart;
+						changeCount = modelChangedEvent.addedCharCount - modelChangedEvent.removedCharCount;
+					} else {
+						changeStart = -1;
+					}
+					div.modelChangedEvent = undefined;
+				}
+				oldSpan = div.firstChild;
+			}
+			for (var i = 0; i < ranges.length; i++) {
+				range = ranges[i];
+				text = range.text;
+				end += text.length;
+				style = range.style;
+				if (oldSpan) {
+					oldText = oldSpan.firstChild.data;
+					oldStyle = oldSpan.viewStyle;
+					if (oldText === text && compare(style, oldStyle)) {
+						oldEnd += oldText.length;
+						oldSpan._rectsCache = undefined;
+						span = oldSpan = oldSpan.nextSibling;
+						continue;
+					} else {
+						while (oldSpan) {
+							if (changeStart !== -1) {
+								var spanEnd = end;
+								if (spanEnd >= changeStart) {
+									spanEnd -= changeCount;
+								}
+								var t = oldSpan.firstChild.data;
+								var length = t ? t.length : 0;
+								if (oldEnd + length > spanEnd) { break; }
+								oldEnd += length;
+							}
+							next = oldSpan.nextSibling;
+							lineDiv.removeChild(oldSpan);
+							oldSpan = next;
+						}
+					}
+				}
+				span = this._createSpan(lineDiv, text, style, range.ignoreChars);
+				if (oldSpan) {
+					lineDiv.insertBefore(span, oldSpan);
+				} else {
+					lineDiv.appendChild(span);
+				}
+				if (div) {
+					div.lineWidth = undefined;
+				}
+			}
+			if (div) {
+				var tmp = span ? span.nextSibling : null;
+				while (tmp) {
+					next = tmp.nextSibling;
+					div.removeChild(tmp);
+					tmp = next;
+				}
+			} else {
+				parent.appendChild(lineDiv);
+			}
+			return lineDiv;
+		},
+		_createRanges: function(ranges, text, start, end, lineStart, data) {
+			if (start > end) { return; }
+			if (ranges) {
+				for (var i = 0; i < ranges.length; i++) {
+					var range = ranges[i];
+					if (range.end < lineStart + start) { continue; }
+					var styleStart = Math.max(lineStart + start, range.start) - lineStart;
+					if (styleStart > end) { break; }
+					var styleEnd = Math.min(lineStart + end, range.end) - lineStart;
+					if (styleStart <= styleEnd) {
+						styleStart = Math.max(start, styleStart);
+						styleEnd = Math.min(end, styleEnd);
+						if (start < styleStart) {
+							this._createRange(text, start, styleStart, null, data);
+						}
+						while (i + 1 < ranges.length && ranges[i + 1].start - lineStart === styleEnd && compare(range.style, ranges[i + 1].style)) {
+							range = ranges[i + 1];
+							styleEnd = Math.min(lineStart + end, range.end) - lineStart;
+							i++;
+						}
+						this._createRange(text, styleStart, styleEnd, range.style, data);
+						start = styleEnd;
+					}
+				}
+			}
+			if (start < end) {
+				this._createRange(text, start, end, null, data);
+			}
+		},
+		_createRange: function(text, start, end, style, data) {
+			if (start > end) { return; }
+			var tabSize = this.view._customTabSize, range;
+			if (tabSize && tabSize !== 8) {
+				var tabIndex = text.indexOf("\t", start); //$NON-NLS-0$
+				while (tabIndex !== -1) {
+					if (start < tabIndex) {
+						range = {text: text.substring(start, tabIndex), style: style};
+						data.ranges.push(range);
+						data.tabOffset += range.text.length;
+					}
+					var spacesCount = tabSize - (data.tabOffset % tabSize);
+					if (spacesCount > 0) {
+						//TODO hack to preserve tabs in getDOMText()
+						var spaces = "\u00A0"; //$NON-NLS-0$
+						for (var i = 1; i < spacesCount; i++) {
+							spaces += " "; //$NON-NLS-0$
+						}
+						range = {text: spaces, style: style, ignoreChars: spacesCount - 1};
+						data.ranges.push(range);
+						data.tabOffset += range.text.length;
+					}
+					start = tabIndex + 1;
+					if (start === end) {
+						return;
+					}
+					tabIndex = text.indexOf("\t", start); //$NON-NLS-0$
+				}
+			}
+			if (start <= end) {
+				range = {text: text.substring(start, end), style: style};
+				data.ranges.push(range);
+				data.tabOffset += range.text.length;
+			}
+		},
+		_createSpan: function(parent, text, style, ignoreChars) {
+			var view = this.view;
+			var tagName = "span"; //$NON-NLS-0$
+			if (style && style.tagName) {
+				tagName = style.tagName.toLowerCase();
+			}
+			var isLink = tagName === "a"; //$NON-NLS-0$
+			if (isLink) { parent.hasLink = true; }
+			if (isLink && !view._linksVisible) {
+				tagName = "span"; //$NON-NLS-0$
+			}
+			var document = parent.ownerDocument;
+			var child = util.createElement(parent.ownerDocument, tagName);
+			if (style && style.html) {
+				child.innerHTML = style.html;
+				child.ignore = true;
+			} else if (style && style.node) {
+				child.appendChild(style.node);
+				child.ignore = true;
+			} else {
+				child.appendChild(document.createTextNode(style && style.text ? style.text : text));
+			}
+			applyStyle(style, child);
+			if (tagName === "a") { //$NON-NLS-0$
+				var window = view._getWindow();
+				addHandler(child, "click", function(e) { return view._handleLinkClick(e ? e : window.event); }, false); //$NON-NLS-0$
+			}
+			child.viewStyle = style;
+			if (ignoreChars) {
+				child.ignoreChars = ignoreChars;
+			}
+			return child;
+		},
+		_ensureCreated: function() {
+			if (this._lineDiv) { return this._lineDiv; }
+			return (this._createdDiv = this.create(this.view._clientDiv, null));
+		},
+		/** @private */
+		getBoundingClientRect: function(offset, absolute) {
+			var child = this._ensureCreated();
+			var view = this.view;
+			if (offset === undefined) {
+				return this._getLineBoundingClientRect(child, true);
+			}
+			var model = view._model;
+			var document = child.ownerDocument;
+			var lineIndex = this.lineIndex;
+			var result = null;
+			if (offset < model.getLineEnd(lineIndex)) {
+				var lineOffset = model.getLineStart(lineIndex);
+				var lineChild = child.firstChild;
+				while (lineChild) {
+					if (lineChild.ignore) {
+						lineChild = lineChild.nextSibling;
+						continue;
+					}
+					var textNode = lineChild.firstChild;
+					var nodeLength = textNode.length; 
+					if (lineChild.ignoreChars) {
+						nodeLength -= lineChild.ignoreChars;
+					}
+					if (lineOffset + nodeLength > offset) {
+						var index = offset - lineOffset;
+						var range;
+						if (view._isRangeRects) {
+							range = document.createRange();
+							range.setStart(textNode, index);
+							range.setEnd(textNode, index + 1);
+							result = new TextRect(range.getBoundingClientRect());
+						} else if (util.isIE) {
+							range = document.body.createTextRange();
+							range.moveToElementText(lineChild);
+							range.collapse();
+							range.moveEnd("character", index + 1); //$NON-NLS-0$
+							range.moveStart("character", index); //$NON-NLS-0$
+							result = new TextRect(range.getBoundingClientRect());
+						} else {
+							var text = textNode.data;
+							lineChild.removeChild(textNode);
+							lineChild.appendChild(document.createTextNode(text.substring(0, index)));
+							var span = util.createElement(document, "span"); //$NON-NLS-0$
+							span.appendChild(document.createTextNode(text.substring(index, index + 1)));
+							lineChild.appendChild(span);
+							lineChild.appendChild(document.createTextNode(text.substring(index + 1)));
+							result = new TextRect(span.getBoundingClientRect());
+							lineChild.innerHTML = "";
+							lineChild.appendChild(textNode);
+							if (!this._createdDiv) {
+								/*
+								 * Removing the element node that holds the selection start or end
+								 * causes the selection to be lost. The fix is to detect this case
+								 * and restore the selection. 
+								 */
+								var s = view._getSelection();
+								if ((lineOffset <= s.start && s.start < lineOffset + nodeLength) ||  (lineOffset <= s.end && s.end < lineOffset + nodeLength)) {
+									view._updateDOMSelection();
+								}
+							}
+						}
+						if (util.isIE) {
+							var window = getWindow(child.ownerDocument);
+							var xFactor = window.screen.logicalXDPI / window.screen.deviceXDPI;
+							var yFactor = window.screen.logicalYDPI / window.screen.deviceYDPI;
+							result.left = result.left * xFactor;
+							result.right = result.right * xFactor;
+							result.top = result.top * yFactor;
+							result.bottom = result.bottom * yFactor;
+						}
+						break;
+					}
+					lineOffset += nodeLength;
+					lineChild = lineChild.nextSibling;
+				}
+			}
+			var rect = this.getBoundingClientRect();
+			if (!result) {
+				if (view._wrapMode) {
+					var rects = this.getClientRects();
+					result = rects[rects.length - 1];
+					result.left = result.right;
+					result.left += rect.left;
+					result.top += rect.top;
+					result.right += rect.left;
+					result.bottom += rect.top;
+				} else {
+					result = new TextRect(rect);
+					result.left = result.right;
+				}
+			}
+			if (absolute || absolute === undefined) {
+				result.left -= rect.left;
+				result.top -= rect.top;
+				result.right -= rect.left;
+				result.bottom -= rect.top;
+			}
+			return result;
+		},
+		/** @private */
+		_getClientRects: function(element, parentRect) {
+			var rects, newRects, rect, i;
+			if (!element._rectsCache) {
+				rects = element.getClientRects();
+				newRects = new Array(rects.length);
+				for (i = 0; i<rects.length; i++) {
+					rect = newRects[i] = new TextRect(rects[i]);
+					rect.left -= parentRect.left;
+					rect.top -= parentRect.top;
+					rect.right -= parentRect.left;
+					rect.bottom -= parentRect.top;
+				}
+				element._rectsCache = newRects;
+			}
+			rects = element._rectsCache;
+			newRects = [rects.length];
+			for (i = 0; i<rects.length; i++) {
+				newRects[i] = new TextRect(rects[i]);
+			}
+			return newRects;
+		},
+		/** @private */
+		getClientRects: function(lineIndex) {
+			if (!this.view._wrapMode) { return [this.getBoundingClientRect()]; }
+			var child = this._ensureCreated();
+			//TODO [perf] cache rects
+			var result = [];
+			var lineChild = child.firstChild, i, r, parentRect = child.getBoundingClientRect();
+			while (lineChild) {
+				if (lineChild.ignore) {
+					lineChild = lineChild.nextSibling;
+					continue;
+				}
+				var rects = this._getClientRects(lineChild, parentRect);
+				for (i = 0; i < rects.length; i++) {
+					var rect = rects[i], j;
+					if (rect.top === rect.bottom) { continue; }
+					var center = rect.top + (rect.bottom - rect.top) / 2;
+					for (j = 0; j < result.length; j++) {
+						r = result[j];
+						if ((r.top <= center && center < r.bottom)) {
+							break;
+						}
+					}
+					if (j === result.length) {
+						result.push(rect);
+					} else {
+						if (rect.left < r.left) { r.left = rect.left; }
+						if (rect.top < r.top) { r.top = rect.top; }
+						if (rect.right > r.right) { r.right = rect.right; }
+						if (rect.bottom > r.bottom) { r.bottom = rect.bottom; }
+					}
+				}
+				lineChild = lineChild.nextSibling;
+			}
+			if (lineIndex !== undefined) {
+				return result[lineIndex];
+			}
+			return result;
+		},
+		/** @private */
+		_getLineBoundingClientRect: function (child, noTrim) {
+			var rect = new TextRect(child.getBoundingClientRect());
+			if (this.view._wrapMode) {
+			} else {
+				rect.right = rect.left;
+				var lastChild = child.lastChild;
+				//Remove any artificial trailing whitespace in the line
+				while (lastChild && lastChild.ignoreChars === lastChild.firstChild.length) {
+					lastChild = lastChild.previousSibling;
+				}
+				if (lastChild) {
+					var lastRect = lastChild.getBoundingClientRect();
+					rect.right = lastRect.right + getLineTrim(child).right;
+				}
+			}
+			if (noTrim) {
+				var padding = getLineTrim(child);
+				rect.left = rect.left + padding.left;
+				rect.right = rect.right - padding.right;
+			}
+			return rect;
+		},
+		/** @private */
+		getLineCount: function () {
+			if (!this.view._wrapMode) { return 1; }
+			return this.getClientRects().length;
+		},
+		/** @private */
+		getLineIndex: function(offset) {
+			if (!this.view._wrapMode) { return 0; }
+			var rects = this.getClientRects();
+			var rect = this.getBoundingClientRect(offset);
+			var center = rect.top + ((rect.bottom - rect.top) / 2);
+			for (var i = 0; i < rects.length; i++) {
+				if (rects[i].top <= center && center < rects[i].bottom) {
+					return i;
+				}
+			}
+			return rects.length - 1;
+		},
+		/** @private */
+		getLineStart: function (lineIndex) {
+			if (!this.view._wrapMode || lineIndex === 0) {
+				return this.view._model.getLineStart(lineIndex);
+			}
+			var rects = this.getClientRects();
+			return this.getOffset(rects[lineIndex].left + 1, rects[lineIndex].top + 1);
+		},
+		/** @private */
+		getOffset: function(x, y) {
+			var view = this.view;
+			var model = view._model;
+			var lineIndex = this.lineIndex;
+			var lineStart = model.getLineStart(lineIndex);
+			var lineEnd = model.getLineEnd(lineIndex);
+			if (lineStart === lineEnd) {
+				return lineStart;
+			}
+			var child = this._ensureCreated();
+			var lineRect = this.getBoundingClientRect(), rects, rect;
+			if (view._wrapMode) {
+				rects = this.getClientRects();
+				if (y < rects[0].top) {
+					y = rects[0].top;
+				}
+				for (var i = 0; i < rects.length; i++) {
+					rect = rects[i];
+					if (rect.top <= y && y < rect.bottom) {
+						break;
+					}
+				}
+				if (x < rect.left) { x = rect.left; }
+				if (x > rect.right) { x = rect.right - 1; }
+			} else {
+				if (x < 0) { x = 0; }
+				if (x > (lineRect.right - lineRect.left)) { x = lineRect.right - lineRect.left; }
+			}
+			var document = child.ownerDocument;
+			var window = getWindow(document);
+			var xFactor = util.isIE ? window.screen.logicalXDPI / window.screen.deviceXDPI : 1;
+			var yFactor = util.isIE ? window.screen.logicalYDPI / window.screen.deviceYDPI : 1;
+			var offset = lineStart;
+			var lineChild = child.firstChild;
+			done:
+			while (lineChild) {
+				if (lineChild.ignore) {
+					lineChild = lineChild.nextSibling;
+					continue;
+				}
+				var textNode = lineChild.firstChild;
+				var nodeLength = textNode.length;
+				if (lineChild.ignoreChars) {
+					nodeLength -= lineChild.ignoreChars;
+				}
+				var rangeLeft, rangeTop, rangeRight, rangeBottom;
+				rects = this._getClientRects(lineChild, lineRect);
+				for (var j = 0; j < rects.length; j++) {
+					rect = rects[j];
+					if (rect.left <= x && x < rect.right && (!view._wrapMode || (rect.top <= y && y < rect.bottom))) {
+						var range, start, end;
+						if (util.isIE || view._isRangeRects) {
+							range = view._isRangeRects ? document.createRange() : document.body.createTextRange();
+							var high = nodeLength;
+							var low = -1;
+							while ((high - low) > 1) {
+								var mid = Math.floor((high + low) / 2);
+								start = low + 1;
+								end = mid === nodeLength - 1 && lineChild.ignoreChars ? textNode.length : mid + 1;
+								if (view._isRangeRects) {
+									range.setStart(textNode, start);
+									range.setEnd(textNode, end);
+								} else {
+									range.moveToElementText(lineChild);
+									range.move("character", start); //$NON-NLS-0$
+									range.moveEnd("character", end - start); //$NON-NLS-0$
+								}
+								rects = range.getClientRects();
+								var found = false;
+								for (var k = 0; k < rects.length; k++) {
+									rect = rects[k];
+									rangeLeft = rect.left * xFactor - lineRect.left;
+									rangeRight = rect.right * xFactor - lineRect.left;
+									rangeTop = rect.top * yFactor - lineRect.top;
+									rangeBottom = rect.bottom * yFactor - lineRect.top;
+									if (rangeLeft <= x && x < rangeRight && (!view._wrapMode || (rangeTop <= y && y < rangeBottom))) {
+										found = true;
+										break;
+									}
+								}
+								if (found) {
+									high = mid;
+								} else {
+									low = mid;
+								}
+							}
+							offset += high;
+							start = high;
+							end = high === nodeLength - 1 && lineChild.ignoreChars ? textNode.length : Math.min(high + 1, textNode.length);
+							if (view._isRangeRects) {
+								range.setStart(textNode, start);
+								range.setEnd(textNode, end);
+							} else {
+								range.moveToElementText(lineChild);
+								range.move("character", start); //$NON-NLS-0$
+								range.moveEnd("character", end - start); //$NON-NLS-0$
+							}
+							rect = range.getClientRects()[0];
+							rangeLeft = rect.left * xFactor - lineRect.left;
+							rangeRight = rect.right * xFactor - lineRect.left;
+							//TODO test for character trailing (wrong for bidi)
+							var trailing = x > (rangeLeft + (rangeRight - rangeLeft) / 2);
+							// Handle Unicode surrogates
+							var offsetInLine = offset - lineStart;
+							var lineText = model.getLine(lineIndex);
+							var c = lineText.charCodeAt(offsetInLine);
+							if (0xD800 <= c && c <= 0xDBFF && trailing) {
+								if (offsetInLine < lineText.length) {
+									c = lineText.charCodeAt(offsetInLine + 1);
+									if (0xDC00 <= c && c <= 0xDFFF) {
+										offset += 1;
+									}
+								}
+							} else if (0xDC00 <= c && c <= 0xDFFF && !trailing) {
+								if (offsetInLine > 0) {
+									c = lineText.charCodeAt(offsetInLine - 1);
+									if (0xD800 <= c && c <= 0xDBFF) {
+										offset -= 1;
+									}
+								}
+							}
+							if (trailing) {
+								offset++;
+							}
+						} else {
+							var newText = [];
+							for (var q = 0; q < nodeLength; q++) {
+								newText.push("<span>"); //$NON-NLS-0$
+								if (q === nodeLength - 1) {
+									newText.push(textNode.data.substring(q));
+								} else {
+									newText.push(textNode.data.substring(q, q + 1));
+								}
+								newText.push("</span>"); //$NON-NLS-0$
+							}
+							lineChild.innerHTML = newText.join("");
+							var rangeChild = lineChild.firstChild;
+							while (rangeChild) {
+								rect = rangeChild.getBoundingClientRect();
+								rangeLeft = rect.left - lineRect.left;
+								rangeRight = rect.right - lineRect.left;
+								if (rangeLeft <= x && x < rangeRight) {
+									//TODO test for character trailing (wrong for bidi)
+									if (x > rangeLeft + (rangeRight - rangeLeft) / 2) {
+										offset++;
+									}
+									break;
+								}
+								offset++;
+								rangeChild = rangeChild.nextSibling;
+							}
+							if (!this._createdDiv) {
+								lineChild.innerHTML = "";
+								lineChild.appendChild(textNode);
+								/*
+								 * Removing the element node that holds the selection start or end
+								 * causes the selection to be lost. The fix is to detect this case
+								 * and restore the selection. 
+								 */
+								var s = view._getSelection();
+								if ((offset <= s.start && s.start < offset + nodeLength) || (offset <= s.end && s.end < offset + nodeLength)) {
+									view._updateDOMSelection();
+								}
+							}
+						}
+						break done;
+					}
+				}
+				offset += nodeLength;
+				lineChild = lineChild.nextSibling;
+			}
+			return Math.min(lineEnd, Math.max(lineStart, offset));
+		},
+		/** @private */
+		getNextOffset: function (offset, data) {
+			if (data.unit === "line") { //$NON-NLS-0$
+				var view = this.view;
+				var model = view._model;
+				var lineIndex = model.getLineAtOffset(offset);
+				if (data.count > 0) {
+					data.count--;
+					return model.getLineEnd(lineIndex);
+				}
+				data.count++;
+				return model.getLineStart(lineIndex);
+			}
+			if (data.unit === "wordend" || data.unit === "wordWS" || data.unit === "wordendWS") { //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+				return this._getNextOffset_W3C(offset, data);
+			}
+			return util.isIE ? this._getNextOffset_IE(offset, data) : this._getNextOffset_W3C(offset, data);
+		},
+		/** @private */
+		_getNextOffset_W3C: function (offset, data) {
+			function _isPunctuation(c) {
+				return (33 <= c && c <= 47) || (58 <= c && c <= 64) || (91 <= c && c <= 94) || c === 96 || (123 <= c && c <= 126);
+			}
+			function _isWhitespace(c) {
+				return c === 32 || c === 9;
+			}
+			var view = this.view;
+			var model = view._model;
+			var lineIndex = model.getLineAtOffset(offset);
+			var lineText = model.getLine(lineIndex);
+			var lineStart = model.getLineStart(lineIndex);
+			var lineEnd = model.getLineEnd(lineIndex);
+			var lineLength = lineText.length;
+			var offsetInLine = offset - lineStart;
+			var c;
+			var step = data.count < 0 ? -1 : 1;
+			if (data.unit === "word" || data.unit === "wordend" || data.unit === "wordWS" || data.unit === "wordendWS") { //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+				var previousPunctuation, previousLetterOrDigit, punctuation, letterOrDigit;
+				while (data.count !== 0) {
+					if (data.count > 0) {
+						if (offsetInLine === lineLength) { return lineEnd; }
+						c = lineText.charCodeAt(offsetInLine);
+						previousPunctuation = _isPunctuation(c); 
+						previousLetterOrDigit = !previousPunctuation && !_isWhitespace(c);
+						offsetInLine++;
+						while (offsetInLine < lineLength) {
+							c = lineText.charCodeAt(offsetInLine);
+							if (data.unit !== "wordWS" && data.unit !== "wordendWS") { //$NON-NLS-1$ //$NON-NLS-0$
+								punctuation = _isPunctuation(c);
+								if (data.unit === "wordend") { //$NON-NLS-0$
+									if (!punctuation && previousPunctuation) { break; }
+								} else {
+									if (punctuation && !previousPunctuation) { break; }
+								}
+								letterOrDigit  = !punctuation && !_isWhitespace(c);
+							} else {
+								letterOrDigit  = !_isWhitespace(c);
+							}
+							if (data.unit === "wordend" || data.unit === "wordendWS") { //$NON-NLS-1$ //$NON-NLS-0$
+								if (!letterOrDigit && previousLetterOrDigit) { break; }
+							} else {
+								if (letterOrDigit && !previousLetterOrDigit) { break; }
+							}
+							previousLetterOrDigit = letterOrDigit;
+							previousPunctuation = punctuation;
+							offsetInLine++;
+						}
+					} else {
+						if (offsetInLine === 0) { return lineStart; }
+						offsetInLine--;
+						c = lineText.charCodeAt(offsetInLine);
+						previousPunctuation = _isPunctuation(c); 
+						previousLetterOrDigit = !previousPunctuation && !_isWhitespace(c);
+						while (0 < offsetInLine) {
+							c = lineText.charCodeAt(offsetInLine - 1);
+							if (data.unit !== "wordWS" && data.unit !== "wordendWS") { //$NON-NLS-1$ //$NON-NLS-0$ 
+								punctuation = _isPunctuation(c);
+								if (data.unit === "wordend") { //$NON-NLS-0$
+									if (punctuation && !previousPunctuation) { break; }
+								} else {
+									if (!punctuation && previousPunctuation) { break; }
+								}
+								letterOrDigit  = !punctuation && !_isWhitespace(c);
+							} else {
+								letterOrDigit  = !_isWhitespace(c);
+							}
+							if (data.unit === "wordend" || data.unit === "wordendWS") { //$NON-NLS-1$ //$NON-NLS-0$
+								if (letterOrDigit && !previousLetterOrDigit) { break; }
+							} else {
+								if (!letterOrDigit && previousLetterOrDigit) { break; }
+							}
+							previousLetterOrDigit = letterOrDigit;
+							previousPunctuation = punctuation;
+							offsetInLine--;
+						}
+						if (offsetInLine === 0) {
+							//get previous line
+						}
+					}
+					data.count -= step;
+				}
+			} else {
+				while (data.count !== 0 && (0 <= offsetInLine + step && offsetInLine + step <= lineLength)) {
+					offsetInLine += step;
+					c = lineText.charCodeAt(offsetInLine);
+					// Handle Unicode surrogates
+					if (0xDC00 <= c && c <= 0xDFFF) {
+						if (offsetInLine > 0) {
+							c = lineText.charCodeAt(offsetInLine - 1);
+							if (0xD800 <= c && c <= 0xDBFF) {
+								offsetInLine += step;
+							}
+						}
+					}
+					data.count -= step;
+				}
+			}
+			return lineStart + offsetInLine;
+		},
+		/** @private */
+		_getNextOffset_IE: function (offset, data) {
+			var child = this._ensureCreated();
+			var view = this.view;
+			var model = view._model;
+			var lineIndex = this.lineIndex;
+			var result = 0, range, length;
+			var lineOffset = model.getLineStart(lineIndex);
+			var document = child.ownerDocument;
+			var lineChild;
+			var step = data.count < 0 ? -1 : 1;
+			if (offset === model.getLineEnd(lineIndex)) {
+				lineChild = child.lastChild;
+				while (lineChild && lineChild.ignoreChars) {
+					lineChild = lineChild.previousSibling;
+				}
+				if (!lineChild) {
+					return lineOffset;
+				}
+				range = document.body.createTextRange();
+				range.moveToElementText(lineChild);
+				length = range.text.length;
+				range.moveEnd(data.unit, step);
+				result = offset + range.text.length - length;
+			} else if (offset === lineOffset && data.count < 0) {
+				result = lineOffset;
+			} else {
+				lineChild = child.firstChild;
+				while (lineChild) {
+					var textNode = lineChild.firstChild;
+					var nodeLength = textNode.length;
+					if (lineChild.ignoreChars) {
+						nodeLength -= lineChild.ignoreChars;
+					}
+					if (lineOffset + nodeLength > offset) {
+						range = document.body.createTextRange();
+						if (offset === lineOffset && data.count < 0) {
+							range.moveToElementText(lineChild.previousSibling);
+						} else {
+							range.moveToElementText(lineChild);
+							range.collapse();
+							range.moveEnd("character", offset - lineOffset); //$NON-NLS-0$
+						}
+						length = range.text.length;
+						range.moveEnd(data.unit, step);
+						result = offset + range.text.length - length;
+						break;
+					}
+					lineOffset = nodeLength + lineOffset;
+					lineChild = lineChild.nextSibling;
+				}
+			}
+			data.count -= step;
+			return result;
+		},
+		/** @private */
+		destroy: function() {
+			var div = this._createdDiv;
+			if (div) {
+				div.parentNode.removeChild(div);
+				this._createdDiv = null;
+			}
+		}
+	};
+	
+	/**
+	 * @class This object describes the options for the text view.
+	 * <p>
+	 * <b>See:</b><br/>
+	 * {@link orion.editor.TextView}<br/>
+	 * {@link orion.editor.TextView#setOptions}
+	 * {@link orion.editor.TextView#getOptions}	 
+	 * </p>		 
+	 * @name orion.editor.TextViewOptions
+	 *
+	 * @property {String|DOMElement} parent the parent element for the view, it can be either a DOM element or an ID for a DOM element.
+	 * @property {orion.editor.TextModel} [model] the text model for the view. If it is not set the view creates an empty {@link orion.editor.TextModel}.
+	 * @property {Boolean} [readonly=false] whether or not the view is read-only.
+	 * @property {Boolean} [fullSelection=true] whether or not the view is in full selection mode.
+	 * @property {Boolean} [tabMode=true] whether or not the tab keypress is consumed by the view or is used for focus traversal.
+	 * @property {Boolean} [expandTab=false] whether or not the tab key inserts white spaces.
+	 * @property {orion.editor.TextTheme} [theme=orion.editor.TextTheme.getTheme()] the TextTheme manager. TODO more info on this
+	 * @property {String} [themeClass] the CSS class for the view theming.
+	 * @property {Number} [tabSize=8] The number of spaces in a tab.
+	 * @property {Boolean} [overwriteMode=false] whether or not the view is in insert/overwrite mode.
+	 * @property {Boolean} [wrapMode=false] whether or not the view wraps lines.
+	 * @property {Boolean} [wrapable=false] whether or not the view is wrappable.
+	 * @property {Number} [scrollAnimation=0] the time duration in miliseconds for scrolling animation. <code>0</code> means no animation.
+	 * @property {Boolean} [blockCursorVisible=false] whether or not to show the block cursor.
+	 */
+	/**
+	 * Constructs a new text view.
+	 * 
+	 * @param {orion.editor.TextViewOptions} options the view options.
+	 * 
+	 * @class A TextView is a user interface for editing text.
+	 * @name orion.editor.TextView
+	 * @borrows orion.editor.EventTarget#addEventListener as #addEventListener
+	 * @borrows orion.editor.EventTarget#removeEventListener as #removeEventListener
+	 * @borrows orion.editor.EventTarget#dispatchEvent as #dispatchEvent
+	 */
+	function TextView (options) {
+		this._init(options || {});
+	}
+	
+	TextView.prototype = /** @lends orion.editor.TextView.prototype */ {
+		/**
+		 * Adds a keyMode to the text view at the specified position.
+		 *
+		 * @param {orion.editor.KeyMode} mode the editor keyMode.
+		 * @param {Number} [index=length] the index.
+		 */
+		addKeyMode: function(mode, index) {
+			var keyModes = this._keyModes;
+			if (index !== undefined) {
+				keyModes.splice(index, 0, mode);
+			} else {
+				keyModes.push(mode);
+			}
+			//TODO: API needed for this
+			if (mode._modeAdded) {
+				mode._modeAdded();
+			}
+		},
+		/**
+		 * Adds a ruler to the text view at the specified position.
+		 * <p>
+		 * The position is relative to the ruler location.
+		 * </p>
+		 *
+		 * @param {orion.editor.Ruler} ruler the ruler.
+		 * @param {Number} [index=length] the ruler index.
+		 */
+		addRuler: function (ruler, index) {
+			ruler.setView(this);
+			var rulers = this._rulers;
+			if (index !== undefined) {
+				var i, sideIndex;
+				for (i = 0, sideIndex=0; i < rulers.length && sideIndex < index; i++) {
+					if (ruler.getLocation() === rulers[i].getLocation()) {
+						sideIndex++;
+					}
+				}
+				rulers.splice(sideIndex, 0, ruler);
+				index = sideIndex;
+			} else {
+				rulers.push(ruler);
+			}
+			this._createRuler(ruler, index);
+			this._update();
+		},
+		computeSize: function() {
+			var w = 0, h = 0;
+			var model = this._model, clientDiv = this._clientDiv;
+			if (!clientDiv) { return {width: w, height: h}; }
+			var clientWidth = clientDiv.style.width;
+			/*
+			* Feature in WekKit. Webkit limits the width of the lines
+			* computed below to the width of the client div.  This causes
+			* the lines to be wrapped even though "pre" is set.  The fix
+			* is to set the width of the client div to a "0x7fffffffpx"
+			* before computing the lines width.  Note that this value is
+			* reset to the appropriate value further down.
+			*/
+			if (util.isWebkit) {
+				clientDiv.style.width = "0x7fffffffpx"; //$NON-NLS-0$
+			}
+			var lineCount = model.getLineCount();
+			for (var lineIndex=0; lineIndex<lineCount; lineIndex++) {
+				var line = this._getLine(lineIndex);
+				var rect = line.getBoundingClientRect();
+				w = Math.max(w, rect.right - rect.left);
+				h += rect.bottom - rect.top;
+				line.destroy();
+			}
+			if (util.isWebkit) {
+				clientDiv.style.width = clientWidth;
+			}
+			var viewPadding = this._getViewPadding();
+			w += viewPadding.right + viewPadding.left + this._metrics.scrollWidth;
+			h += viewPadding.bottom + viewPadding.top + this._metrics.scrollWidth;
+			return {width: w, height: h};
+		},
+		/**
+		 * Converts the given rectangle from one coordinate spaces to another.
+		 * <p>The supported coordinate spaces are:
+		 * <ul>
+		 *   <li>"document" - relative to document, the origin is the top-left corner of first line</li>
+		 *   <li>"page" - relative to html page that contains the text view</li>
+		 * </ul>
+		 * </p>
+		 * <p>All methods in the view that take or return a position are in the document coordinate space.</p>
+		 *
+		 * @param rect the rectangle to convert.
+		 * @param rect.x the x of the rectangle.
+		 * @param rect.y the y of the rectangle.
+		 * @param rect.width the width of the rectangle.
+		 * @param rect.height the height of the rectangle.
+		 * @param {String} from the source coordinate space.
+		 * @param {String} to the destination coordinate space.
+		 *
+		 * @see #getLocationAtOffset
+		 * @see #getOffsetAtLocation
+		 * @see #getTopPixel
+		 * @see #setTopPixel
+		 */
+		convert: function(rect, from, to) {
+			if (!this._clientDiv) { return; }
+			var scroll = this._getScroll();
+			var viewPad = this._getViewPadding();
+			var viewRect = this._viewDiv.getBoundingClientRect();
+			if (from === "document") { //$NON-NLS-0$
+				if (rect.x !== undefined) {
+					rect.x += - scroll.x + viewRect.left + viewPad.left;
+				}
+				if (rect.y !== undefined) {
+					rect.y += - scroll.y + viewRect.top + viewPad.top;
+				}
+			}
+			//At this point rect is in the widget coordinate space
+			if (to === "document") { //$NON-NLS-0$
+				if (rect.x !== undefined) {
+					rect.x += scroll.x - viewRect.left - viewPad.left;
+				}
+				if (rect.y !== undefined) {
+					rect.y += scroll.y - viewRect.top - viewPad.top;
+				}
+			}
+			return rect;
+		},
+		/**
+		 * Destroys the text view. 
+		 * <p>
+		 * Removes the view from the page and frees all resources created by the view.
+		 * Calling this function causes the "Destroy" event to be fire so that all components
+		 * attached to view can release their references.
+		 * </p>
+		 *
+		 * @see #onDestroy
+		 */
+		destroy: function() {
+			/* Destroy rulers*/
+			for (var i=0; i< this._rulers.length; i++) {
+				this._rulers[i].setView(null);
+			}
+			this.rulers = null;
+			
+			this._destroyView();
+
+			var e = {type: "Destroy"}; //$NON-NLS-0$
+			this.onDestroy(e);
+
+			this._parent = null;
+			this._model = null;
+			this._theme = null;
+			this._selection = null;
+			this._doubleClickSelection = null;
+			this._keyModes = null;
+			this._actions = null;
+		},
+		/**
+		 * Gives focus to the text view.
+		 */
+		focus: function() {
+			if (!this._clientDiv) { return; }
+			/*
+			* Feature in Chrome. When focus is called in the clientDiv without
+			* setting selection the browser will set the selection to the first dom 
+			* element, which can be above the client area. When this happen the 
+			* browser also scrolls the window to show that element.
+			* The fix is to call _updateDOMSelection() before calling focus().
+			*/
+			this._updateDOMSelection();
+			if (util.isOpera) { this._clientDiv.blur(); }
+			this._clientDiv.focus();
+			/*
+			* Feature in Safari. When focus is called the browser selects the clientDiv
+			* itself. The fix is to call _updateDOMSelection() after calling focus().
+			*/
+			this._updateDOMSelection();
+		},
+		/**
+		 * Check if the text view has focus.
+		 *
+		 * @returns {Boolean} <code>true</code> if the text view has focus, otherwise <code>false</code>.
+		 */
+		hasFocus: function() {
+			return this._hasFocus;
+		},
+		/**
+		 * Returns the action description for a given action ID.
+		 *
+		 * @returns {orion.editor.ActionDescrition} the action description
+		 */
+		getActionDescription: function(actionID) {
+			var action = this._actions[actionID];
+			if (action) {
+				return action.actionDescription;
+			}
+			return undefined;
+		},
+		/**
+		 * Returns all action IDs defined in the text view.
+		 * <p>
+		 * There are two types of actions, the predefined actions of the view 
+		 * and the actions added by application code.
+		 * </p>
+		 * <p>
+		 * The predefined actions are:
+		 * <ul>
+		 *   <li>Navigation actions. These actions move the caret collapsing the selection.</li>
+		 *     <ul>
+		 *       <li>"lineUp" - moves the caret up by one line</li>
+		 *       <li>"lineDown" - moves the caret down by one line</li>
+		 *       <li>"lineStart" - moves the caret to beginning of the current line</li>
+		 *       <li>"lineEnd" - moves the caret to end of the current line </li>
+		 *       <li>"charPrevious" - moves the caret to the previous character</li>
+		 *       <li>"charNext" - moves the caret to the next character</li>
+		 *       <li>"pageUp" - moves the caret up by one page</li>
+		 *       <li>"pageDown" - moves the caret down by one page</li>
+		 *       <li>"wordPrevious" - moves the caret to the previous word</li>
+		 *       <li>"wordNext" - moves the caret to the next word</li>
+		 *       <li>"textStart" - moves the caret to the beginning of the document</li>
+		 *       <li>"textEnd" - moves the caret to the end of the document</li>
+		 *     </ul>
+		 *   <li>Selection actions. These actions move the caret extending the selection.</li>
+		 *     <ul>
+		 *       <li>"selectLineUp" - moves the caret up by one line</li>
+		 *       <li>"selectLineDown" - moves the caret down by one line</li>
+		 *       <li>"selectLineStart" - moves the caret to beginning of the current line</li>
+		 *       <li>"selectLineEnd" - moves the caret to end of the current line </li>
+		 *       <li>"selectCharPrevious" - moves the caret to the previous character</li>
+		 *       <li>"selectCharNext" - moves the caret to the next character</li>
+		 *       <li>"selectPageUp" - moves the caret up by one page</li>
+		 *       <li>"selectPageDown" - moves the caret down by one page</li>
+		 *       <li>"selectWordPrevious" - moves the caret to the previous word</li>
+		 *       <li>"selectWordNext" - moves the caret to the next word</li>
+		 *       <li>"selectTextStart" - moves the caret to the beginning of the document</li>
+		 *       <li>"selectTextEnd" - moves the caret to the end of the document</li>
+		 *       <li>"selectAll" - selects the entire document</li>
+		 *     </ul>
+		 *   <li>Edit actions. These actions modify the text view text</li>
+		 *     <ul>
+		 *       <li>"deletePrevious" - deletes the character preceding the caret</li>
+		 *       <li>"deleteNext" - deletes the charecter following the caret</li>
+		 *       <li>"deleteWordPrevious" - deletes the word preceding the caret</li>
+		 *       <li>"deleteWordNext" - deletes the word following the caret</li>
+		 *       <li>"tab" - inserts a tab character at the caret</li>
+		 *       <li>"shiftTab" - noop</li>
+		 *       <li>"toggleTabMode" - toggles tab mode.</li>
+		 *       <li>"toggleWrapMode" - toggles wrap mode.</li>
+		 *       <li>"toggleOverwriteMode" - toggles overwrite mode.</li>
+		 *       <li>"enter" - inserts a line delimiter at the caret</li>
+		 *     </ul>
+		 *   <li>Clipboard actions.</li>
+		 *     <ul>
+		 *       <li>"copy" - copies the selected text to the clipboard</li>
+		 *       <li>"cut" - copies the selected text to the clipboard and deletes the selection</li>
+		 *       <li>"paste" - replaces the selected text with the clipboard contents</li>
+		 *     </ul>
+		 * </ul>
+		 * </p>
+		 *
+		 * @param {Boolean} [defaultAction=false] whether or not the predefined actions are included.
+		 * @returns {String[]} an array of action IDs defined in the text view.
+		 *
+		 * @see #invokeAction
+		 * @see #setAction
+		 * @see #setKeyBinding
+		 * @see #getKeyBindings
+		 */
+		getActions: function (defaultAction) {
+			var result = [];
+			var actions = this._actions;
+			for (var i in actions) {
+				if (actions.hasOwnProperty(i)) {
+					if (!defaultAction && actions[i].defaultHandler) { continue; }
+					result.push(i);
+				}
+			}
+			return result;
+		},
+		/**
+		 * Returns the bottom index.
+		 * <p>
+		 * The bottom index is the line that is currently at the bottom of the view.  This
+		 * line may be partially visible depending on the vertical scroll of the view. The parameter
+		 * <code>fullyVisible</code> determines whether to return only fully visible lines. 
+		 * </p>
+		 *
+		 * @param {Boolean} [fullyVisible=false] if <code>true</code>, returns the index of the last fully visible line. This
+		 *    parameter is ignored if the view is not big enough to show one line.
+		 * @returns {Number} the index of the bottom line.
+		 *
+		 * @see #getTopIndex
+		 * @see #setTopIndex
+		 */
+		getBottomIndex: function(fullyVisible) {
+			if (!this._clientDiv) { return 0; }
+			return this._getBottomIndex(fullyVisible);
+		},
+		/**
+		 * Returns the bottom pixel.
+		 * <p>
+		 * The bottom pixel is the pixel position that is currently at
+		 * the bottom edge of the view.  This position is relative to the
+		 * beginning of the document.
+		 * </p>
+		 *
+		 * @returns {Number} the bottom pixel.
+		 *
+		 * @see #getTopPixel
+		 * @see #setTopPixel
+		 * @see #convert
+		 */
+		getBottomPixel: function() {
+			if (!this._clientDiv) { return 0; }
+			return this._getScroll().y + this._getClientHeight();
+		},
+		/**
+		 * Returns the caret offset relative to the start of the document.
+		 *
+		 * @returns the caret offset relative to the start of the document.
+		 *
+		 * @see #setCaretOffset
+		 * @see #setSelection
+		 * @see #getSelection
+		 */
+		getCaretOffset: function () {
+			var s = this._getSelection();
+			return s.getCaret();
+		},
+		/**
+		 * Returns the client area.
+		 * <p>
+		 * The client area is the portion in pixels of the document that is visible. The
+		 * client area position is relative to the beginning of the document.
+		 * </p>
+		 *
+		 * @returns the client area rectangle {x, y, width, height}.
+		 *
+		 * @see #getTopPixel
+		 * @see #getBottomPixel
+		 * @see #getHorizontalPixel
+		 * @see #convert
+		 */
+		getClientArea: function() {
+			if (!this._clientDiv) { return {x: 0, y: 0, width: 0, height: 0}; }
+			var scroll = this._getScroll();
+			return {x: scroll.x, y: scroll.y, width: this._getClientWidth(), height: this._getClientHeight()};
+		},
+		/**
+		 * Returns the horizontal pixel.
+		 * <p>
+		 * The horizontal pixel is the pixel position that is currently at
+		 * the left edge of the view.  This position is relative to the
+		 * beginning of the document.
+		 * </p>
+		 *
+		 * @returns {Number} the horizontal pixel.
+		 *
+		 * @see #setHorizontalPixel
+		 * @see #convert
+		 */
+		getHorizontalPixel: function() {
+			if (!this._clientDiv) { return 0; }
+			return this._getScroll().x;
+		},
+		/**
+		 * Returns all the key bindings associated to the given action ID.
+		 *
+		 * @param {String} actionID the action ID.
+		 * @returns {orion.editor.KeyBinding[]} the array of key bindings associated to the given action ID.
+		 *
+		 * @see #setKeyBinding
+		 * @see #setAction
+		 */
+		getKeyBindings: function (actionID) {
+			var result = [];
+			var keyModes = this._keyModes;
+			for (var i = 0; i < keyModes.length; i++) {
+				result = result.concat(keyModes[i].getKeyBindings(actionID));
+			}
+			return result;
+		},
+		/**
+		 * Returns all the key modes added to text view.
+		 *
+		 * @returns {orion.editor.KeyMode[]} the array of key modes.
+		 *
+		 * @see #addKeyMode
+		 * @see #removeKeyMode
+		 */
+		getKeyModes: function() {
+			return this._keyModes.slice(0);
+		},
+		/**
+		 * Returns the line height for a given line index.  Returns the default line
+		 * height if the line index is not specified.
+		 *
+		 * @param {Number} [lineIndex] the line index.
+		 * @returns {Number} the height of the line in pixels.
+		 *
+		 * @see #getLinePixel
+		 */
+		getLineHeight: function(lineIndex) {
+			if (!this._clientDiv) { return 0; }
+			return this._getLineHeight(lineIndex);
+		},
+		/**
+		 * Returns the line index for a given line pixel position relative to the document.
+		 *
+		 * @param {Number} [y] the line pixel.
+		 * @returns {Number} the line index for the specified pixel position.
+		 *
+		 * @see #getLinePixel
+		 */
+		getLineIndex: function(y) {
+			if (!this._clientDiv) { return 0; }
+			return this._getLineIndex(y);
+		},
+		/**
+		 * Returns the top pixel position of a given line index relative to the beginning
+		 * of the document.
+		 * <p>
+		 * Clamps out of range indices.
+		 * </p>
+		 *
+		 * @param {Number} lineIndex the line index.
+		 * @returns {Number} the pixel position of the line.
+		 *
+		 * @see #setTopPixel
+		 * @see #getLineIndex
+		 * @see #convert
+		 */
+		getLinePixel: function(lineIndex) {
+			if (!this._clientDiv) { return 0; }
+			return this._getLinePixel(lineIndex);
+		},
+		/**
+		 * Returns the {x, y} pixel location of the top-left corner of the character
+		 * bounding box at the specified offset in the document.  The pixel location
+		 * is relative to the document.
+		 * <p>
+		 * Clamps out of range offsets.
+		 * </p>
+		 *
+		 * @param {Number} offset the character offset
+		 * @returns the {x, y} pixel location of the given offset.
+		 *
+		 * @see #getOffsetAtLocation
+		 * @see #convert
+		 */
+		getLocationAtOffset: function(offset) {
+			if (!this._clientDiv) { return {x: 0, y: 0}; }
+			var model = this._model;
+			offset = Math.min(Math.max(0, offset), model.getCharCount());
+			var lineIndex = model.getLineAtOffset(offset);
+			var line = this._getLine(lineIndex);
+			var rect = line.getBoundingClientRect(offset);
+			line.destroy();
+			var x = rect.left;
+			var y = this._getLinePixel(lineIndex) + rect.top;
+			return {x: x, y: y};
+		},
+		/**
+		 * Returns the specified view options.
+		 * <p>
+		 * The returned value is either a <code>orion.editor.TextViewOptions</code> or an option value. An option value is returned when only one string paremeter
+		 * is specified. A <code>orion.editor.TextViewOptions</code> is returned when there are no paremeters, or the parameters are a list of options names or a
+		 * <code>orion.editor.TextViewOptions</code>. All view options are returned when there no paremeters.
+		 * </p>
+		 *
+		 * @param {String|orion.editor.TextViewOptions} [options] The options to return.
+		 * @return {Object|orion.editor.TextViewOptions} The requested options or an option value.
+		 *
+		 * @see #setOptions
+		 */
+		getOptions: function() {
+			var options;
+			if (arguments.length === 0) {
+				options = this._defaultOptions();
+			} else if (arguments.length === 1) {
+				var arg = arguments[0];
+				if (typeof arg === "string") { //$NON-NLS-0$
+					return clone(this["_" + arg]); //$NON-NLS-0$
+				}
+				options = arg;
+			} else {
+				options = {};
+				for (var index in arguments) {
+					if (arguments.hasOwnProperty(index)) {
+						options[arguments[index]] = undefined;
+					}
+				}
+			}
+			for (var option in options) {
+				if (options.hasOwnProperty(option)) {
+					options[option] = clone(this["_" + option]); //$NON-NLS-0$
+				}
+			}
+			return options;
+		},
+		/**
+		 * Returns the text model of the text view.
+		 *
+		 * @returns {orion.editor.TextModel} the text model of the view.
+		 */
+		getModel: function() {
+			return this._model;
+		},
+		/**
+		 * Returns the character offset nearest to the given pixel location.  The
+		 * pixel location is relative to the document.
+		 *
+		 * @param x the x of the location
+		 * @param y the y of the location
+		 * @returns the character offset at the given location.
+		 *
+		 * @see #getLocationAtOffset
+		 */
+		getOffsetAtLocation: function(x, y) {
+			if (!this._clientDiv) { return 0; }
+			var lineIndex = this._getLineIndex(y);
+			var line = this._getLine(lineIndex);
+			var offset = line.getOffset(x, y - this._getLinePixel(lineIndex));
+			line.destroy();
+			return offset;
+		},
+		/**
+		 * Get the view rulers.
+		 *
+		 * @returns the view rulers
+		 *
+		 * @see #addRuler
+		 */
+		getRulers: function() {
+			return this._rulers.slice(0);
+		},
+		/**
+		 * Returns the text view selection.
+		 * <p>
+		 * The selection is defined by a start and end character offset relative to the
+		 * document. The character at end offset is not included in the selection.
+		 * </p>
+		 * 
+		 * @returns {orion.editor.Selection} the view selection
+		 *
+		 * @see #setSelection
+		 */
+		getSelection: function () {
+			var s = this._getSelection();
+			return {start: s.start, end: s.end};
+		},
+		/**
+		 * Returns the text for the given range.
+		 * <p>
+		 * The text does not include the character at the end offset.
+		 * </p>
+		 *
+		 * @param {Number} [start=0] the start offset of text range.
+		 * @param {Number} [end=char count] the end offset of text range.
+		 *
+		 * @see #setText
+		 */
+		getText: function(start, end) {
+			var model = this._model;
+			return model.getText(start, end);
+		},
+		/**
+		 * Returns the top index.
+		 * <p>
+		 * The top index is the line that is currently at the top of the view.  This
+		 * line may be partially visible depending on the vertical scroll of the view. The parameter
+		 * <code>fullyVisible</code> determines whether to return only fully visible lines. 
+		 * </p>
+		 *
+		 * @param {Boolean} [fullyVisible=false] if <code>true</code>, returns the index of the first fully visible line. This
+		 *    parameter is ignored if the view is not big enough to show one line.
+		 * @returns {Number} the index of the top line.
+		 *
+		 * @see #getBottomIndex
+		 * @see #setTopIndex
+		 */
+		getTopIndex: function(fullyVisible) {
+			if (!this._clientDiv) { return 0; }
+			return this._getTopIndex(fullyVisible);
+		},
+		/**
+		 * Returns the top pixel.
+		 * <p>
+		 * The top pixel is the pixel position that is currently at
+		 * the top edge of the view.  This position is relative to the
+		 * beginning of the document.
+		 * </p>
+		 *
+		 * @returns {Number} the top pixel.
+		 *
+		 * @see #getBottomPixel
+		 * @see #setTopPixel
+		 * @see #convert
+		 */
+		getTopPixel: function() {
+			if (!this._clientDiv) { return 0; }
+			return this._getScroll().y;
+		},
+		/**
+		 * Executes the action handler associated with the given action ID.
+		 * <p>
+		 * The application defined action takes precedence over predefined actions unless
+		 * the <code>defaultAction</code> paramater is <code>true</code>.
+		 * </p>
+		 * <p>
+		 * If the application defined action returns <code>false</code>, the text view predefined
+		 * action is executed if present.
+		 * </p>
+		 *
+		 * @param {String} actionID the action ID.
+		 * @param {Boolean} [defaultAction] whether to always execute the predefined action only.
+		 * @param {Object} [actionOptions] action specific options to be passed to the action handlers.
+		 * @returns {Boolean} <code>true</code> if the action was executed.
+		 *
+		 * @see #setAction
+		 * @see #getActions
+		 */
+		invokeAction: function (actionID, defaultAction, actionOptions) {
+			if (!this._clientDiv) { return; }
+			var action = this._actions[actionID];
+			if (action) {
+				if (!defaultAction && action.handler) {
+					if (action.handler(actionOptions)) {
+						return true;
+					}
+				}
+				if (action.defaultHandler) {
+					return typeof action.defaultHandler(actionOptions) === "boolean"; //$NON-NLS-0$
+				}
+			}
+			return false;
+		},
+		/**
+		* Returns if the view is destroyed.
+		* @returns {Boolean} <code>true</code> if the view is destroyed.
+		*/
+		isDestroyed: function () {
+			return !this._clientDiv;
+		},
+		/** 
+		 * @class This is the event sent when the user right clicks or otherwise invokes the context menu of the view. 
+		 * <p> 
+		 * <b>See:</b><br/> 
+		 * {@link orion.editor.TextView}<br/> 
+		 * {@link orion.editor.TextView#event:onContextMenu} 
+		 * </p> 
+		 * 
+		 * @name orion.editor.ContextMenuEvent 
+		 * 
+		 * @property {Number} x The pointer location on the x axis, relative to the document the user is editing. 
+		 * @property {Number} y The pointer location on the y axis, relative to the document the user is editing. 
+		 * @property {Number} screenX The pointer location on the x axis, relative to the screen. This is copied from the DOM contextmenu event.screenX property. 
+		 * @property {Number} screenY The pointer location on the y axis, relative to the screen. This is copied from the DOM contextmenu event.screenY property. 
+		 * @property {Boolean} defaultPrevented Determines whether the user agent context menu should be shown. It is shown by default.
+		 * @property {Function} preventDefault If called prevents the user agent context menu from showing.
+		 */ 
+		/** 
+		 * This event is sent when the user invokes the view context menu. 
+		 * 
+		 * @event 
+		 * @param {orion.editor.ContextMenuEvent} contextMenuEvent the event 
+		 */ 
+		onContextMenu: function(contextMenuEvent) {
+			return this.dispatchEvent(contextMenuEvent); 
+		}, 
+		onDragStart: function(dragEvent) {
+			return this.dispatchEvent(dragEvent);
+		},
+		onDrag: function(dragEvent) {
+			return this.dispatchEvent(dragEvent);
+		},
+		onDragEnd: function(dragEvent) {
+			return this.dispatchEvent(dragEvent);
+		},
+		onDragEnter: function(dragEvent) {
+			return this.dispatchEvent(dragEvent);
+		},
+		onDragOver: function(dragEvent) {
+			return this.dispatchEvent(dragEvent);
+		},
+		onDragLeave: function(dragEvent) {
+			return this.dispatchEvent(dragEvent);
+		},
+		onDrop: function(dragEvent) {
+			return this.dispatchEvent(dragEvent);
+		},
+		/**
+		 * @class This is the event sent when the text view is destroyed.
+		 * <p>
+		 * <b>See:</b><br/>
+		 * {@link orion.editor.TextView}<br/>
+		 * {@link orion.editor.TextView#event:onDestroy}
+		 * </p>
+		 * @name orion.editor.DestroyEvent
+		 */
+		/**
+		 * This event is sent when the text view has been destroyed.
+		 *
+		 * @event
+		 * @param {orion.editor.DestroyEvent} destroyEvent the event
+		 *
+		 * @see #destroy
+		 */
+		onDestroy: function(destroyEvent) {
+			return this.dispatchEvent(destroyEvent);
+		},
+		/**
+		 * @class This object is used to define style information for the text view.
+		 * <p>
+		 * <b>See:</b><br/>
+		 * {@link orion.editor.TextView}<br/>
+		 * {@link orion.editor.TextView#event:onLineStyle}
+		 * </p>		 
+		 * @name orion.editor.Style
+		 * 
+		 * @property {String} styleClass A CSS class name.
+		 * @property {Object} style An object with CSS properties.
+		 * @property {String} tagName A DOM tag name.
+		 * @property {Object} attributes An object with DOM attributes.
+		 */
+		/**
+		 * @class This object is used to style range.
+		 * <p>
+		 * <b>See:</b><br/>
+		 * {@link orion.editor.TextView}<br/>
+		 * {@link orion.editor.TextView#event:onLineStyle}
+		 * </p>		 
+		 * @name orion.editor.StyleRange
+		 * 
+		 * @property {Number} start The start character offset, relative to the document, where the style should be applied.
+		 * @property {Number} end The end character offset (exclusive), relative to the document, where the style should be applied.
+		 * @property {orion.editor.Style} style The style for the range.
+		 */
+		/**
+		 * @class This is the event sent when the text view needs the style information for a line.
+		 * <p>
+		 * <b>See:</b><br/>
+		 * {@link orion.editor.TextView}<br/>
+		 * {@link orion.editor.TextView#event:onLineStyle}
+		 * </p>		 
+		 * @name orion.editor.LineStyleEvent
+		 * 
+		 * @property {orion.editor.TextView} textView The text view.		 
+		 * @property {Number} lineIndex The line index.
+		 * @property {String} lineText The line text.
+		 * @property {Number} lineStart The character offset, relative to document, of the first character in the line.
+		 * @property {orion.editor.Style} style The style for the entire line (output argument).
+		 * @property {orion.editor.StyleRange[]} ranges An array of style ranges for the line (output argument).		 
+		 */
+		/**
+		 * This event is sent when the text view needs the style information for a line.
+		 *
+		 * @event
+		 * @param {orion.editor.LineStyleEvent} lineStyleEvent the event
+		 */
+		onLineStyle: function(lineStyleEvent) {
+			return this.dispatchEvent(lineStyleEvent);
+		},
+		/**
+		 * @class This is the event sent for all keyboard events.
+		 * <p>
+		 * <b>See:</b><br/>
+		 * {@link orion.editor.TextView}<br/>
+		 * {@link orion.editor.TextView#event:onKeyDown}<br/>
+		 * {@link orion.editor.TextView#event:onKeyPress}<br/>
+		 * {@link orion.editor.TextView#event:onKeyUp}<br/>
+		 * </p>
+		 * @name orion.editor.KeyEvent
+		 * 
+		 * @property {String} type The type of event.
+		 * @property {DOMEvent} event The key DOM event.
+		 * @property {Boolean} defaultPrevented Determines whether the user agent context menu should be shown. It is shown by default.
+		 * @property {Function} preventDefault If called prevents the user agent context menu from showing.
+		 */
+		/**
+		 * This event is sent for key down events.
+		 *
+		 * @event
+		 * @param {orion.editor.KeyEvent} keyEvent the event
+		 */
+		onKeyDown: function(keyEvent) {
+			return this.dispatchEvent(keyEvent);
+		},
+		/**
+		 * This event is sent for key press events. Key press events are only sent
+		 * for printable characters.
+		 *
+		 * @event
+		 * @param {orion.editor.KeyEvent} keyEvent the event
+		 */
+		onKeyPress: function(keyEvent) {
+			return this.dispatchEvent(keyEvent);
+		},
+		/**
+		 * This event is sent for key up events.
+		 *
+		 * @event
+		 * @param {orion.editor.KeyEvent} keyEvent the event
+		 */
+		onKeyUp: function(keyEvent) {
+			return this.dispatchEvent(keyEvent);
+		},
+		/**
+		 * @class This is the event sent when the text in the model has changed.
+		 * <p>
+		 * <b>See:</b><br/>
+		 * {@link orion.editor.TextView}<br/>
+		 * {@link orion.editor.TextView#event:onModelChanged}<br/>
+		 * {@link orion.editor.TextModel#onChanged}
+		 * </p>
+		 * @name orion.editor.ModelChangedEvent
+		 * 
+		 * @property {Number} start The character offset in the model where the change has occurred.
+		 * @property {Number} removedCharCount The number of characters removed from the model.
+		 * @property {Number} addedCharCount The number of characters added to the model.
+		 * @property {Number} removedLineCount The number of lines removed from the model.
+		 * @property {Number} addedLineCount The number of lines added to the model.
+		 */
+		/**
+		 * This event is sent when the text in the model has changed.
+		 *
+		 * @event
+		 * @param {orion.editor.ModelChangedEvent} modelChangedEvent the event
+		 */
+		onModelChanged: function(modelChangedEvent) {
+			return this.dispatchEvent(modelChangedEvent);
+		},
+		/**
+		 * @class This is the event sent when the text in the model is about to change.
+		 * <p>
+		 * <b>See:</b><br/>
+		 * {@link orion.editor.TextView}<br/>
+		 * {@link orion.editor.TextView#event:onModelChanging}<br/>
+		 * {@link orion.editor.TextModel#onChanging}
+		 * </p>
+		 * @name orion.editor.ModelChangingEvent
+		 * 
+		 * @property {String} text The text that is about to be inserted in the model.
+		 * @property {Number} start The character offset in the model where the change will occur.
+		 * @property {Number} removedCharCount The number of characters being removed from the model.
+		 * @property {Number} addedCharCount The number of characters being added to the model.
+		 * @property {Number} removedLineCount The number of lines being removed from the model.
+		 * @property {Number} addedLineCount The number of lines being added to the model.
+		 */
+		/**
+		 * This event is sent when the text in the model is about to change.
+		 *
+		 * @event
+		 * @param {orion.editor.ModelChangingEvent} modelChangingEvent the event
+		 */
+		onModelChanging: function(modelChangingEvent) {
+			return this.dispatchEvent(modelChangingEvent);
+		},
+		/**
+		 * @class This is the event sent when the text is modified by the text view.
+		 * <p>
+		 * <b>See:</b><br/>
+		 * {@link orion.editor.TextView}<br/>
+		 * {@link orion.editor.TextView#event:onModify}
+		 * </p>
+		 * @name orion.editor.ModifyEvent
+		 */
+		/**
+		 * This event is sent when the text view has changed text in the model.
+		 * <p>
+		 * If the text is changed directly through the model API, this event
+		 * is not sent.
+		 * </p>
+		 *
+		 * @event
+		 * @param {orion.editor.ModifyEvent} modifyEvent the event
+		 */
+		onModify: function(modifyEvent) {
+			return this.dispatchEvent(modifyEvent);
+		},
+		onMouseDown: function(mouseEvent) {
+			return this.dispatchEvent(mouseEvent);
+		},
+		onMouseUp: function(mouseEvent) {
+			return this.dispatchEvent(mouseEvent);
+		},
+		onMouseMove: function(mouseEvent) {
+			return this.dispatchEvent(mouseEvent);
+		},
+		onMouseOver: function(mouseEvent) {
+			return this.dispatchEvent(mouseEvent);
+		},
+		onMouseOut: function(mouseEvent) {
+			return this.dispatchEvent(mouseEvent);
+		},
+		/**
+		 * @class This is the event sent when the selection changes in the text view.
+		 * <p>
+		 * <b>See:</b><br/>
+		 * {@link orion.editor.TextView}<br/>
+		 * {@link orion.editor.TextView#event:onSelection}
+		 * </p>		 
+		 * @name orion.editor.SelectionEvent
+		 * 
+		 * @property {orion.editor.Selection} oldValue The old selection.
+		 * @property {orion.editor.Selection} newValue The new selection.
+		 */
+		/**
+		 * This event is sent when the text view selection has changed.
+		 *
+		 * @event
+		 * @param {orion.editor.SelectionEvent} selectionEvent the event
+		 */
+		onSelection: function(selectionEvent) {
+			return this.dispatchEvent(selectionEvent);
+		},
+		/**
+		 * @class This is the event sent when the text view scrolls.
+		 * <p>
+		 * <b>See:</b><br/>
+		 * {@link orion.editor.TextView}<br/>
+		 * {@link orion.editor.TextView#event:onScroll}
+		 * </p>		 
+		 * @name orion.editor.ScrollEvent
+		 * 
+		 * @property oldValue The old scroll {x,y}.
+		 * @property newValue The new scroll {x,y}.
+		 */
+		/**
+		 * This event is sent when the text view scrolls vertically or horizontally.
+		 *
+		 * @event
+		 * @param {orion.editor.ScrollEvent} scrollEvent the event
+		 */
+		onScroll: function(scrollEvent) {
+			return this.dispatchEvent(scrollEvent);
+		},
+		/**
+		 * @class This is the event sent when the text is about to be modified by the text view.
+		 * <p>
+		 * <b>See:</b><br/>
+		 * {@link orion.editor.TextView}<br/>
+		 * {@link orion.editor.TextView#event:onVerify}
+		 * </p>
+		 * @name orion.editor.VerifyEvent
+		 * 
+		 * @property {String} text The text being inserted.
+		 * @property {Number} start The start offset of the text range to be replaced.
+		 * @property {Number} end The end offset (exclusive) of the text range to be replaced.
+		 */
+		/**
+		 * This event is sent when the text view is about to change text in the model.
+		 * <p>
+		 * If the text is changed directly through the model API, this event
+		 * is not sent.
+		 * </p>
+		 * <p>
+		 * Listeners are allowed to change these parameters. Setting text to null
+		 * or undefined stops the change.
+		 * </p>
+		 *
+		 * @event
+		 * @param {orion.editor.VerifyEvent} verifyEvent the event
+		 */
+		onVerify: function(verifyEvent) {
+			return this.dispatchEvent(verifyEvent);
+		},
+		/**
+		 * @class This is the event sent when the text view is focused.
+		 * <p>
+		 * <b>See:</b><br/>
+		 * {@link orion.editor.TextView}<br/>
+		 * {@link orion.editor.TextView#event:onFocus}<br/>
+		 * </p>
+		 * @name orion.editor.FocusEvent
+		 */
+		/**
+		 * This event is sent when the text view is focused.
+		 *
+		 * @event
+		 * @param {orion.editor.FocusEvent} focusEvent the event
+		 */
+		onFocus: function(focusEvent) {
+			return this.dispatchEvent(focusEvent);
+		},
+		/**
+		 * @class This is the event sent when the text view goes out of focus.
+		 * <p>
+		 * <b>See:</b><br/>
+		 * {@link orion.editor.TextView}<br/>
+		 * {@link orion.editor.TextView#event:onBlur}<br/>
+		 * </p>
+		 * @name orion.editor.BlurEvent
+		 */
+		/**
+		 * This event is sent when the text view goes out of focus.
+		 *
+		 * @event
+		 * @param {orion.editor.BlurEvent} blurEvent the event
+		 */
+		onBlur: function(blurEvent) {
+			return this.dispatchEvent(blurEvent);
+		},
+		/**
+		 * Redraws the entire view, including rulers.
+		 *
+		 * @see #redrawLines
+		 * @see #redrawRange
+		 * @see #setRedraw
+		 */
+		redraw: function() {
+			if (this._redrawCount > 0) { return; }
+			var lineCount = this._model.getLineCount();
+			this.redrawRulers(0, lineCount);
+			this.redrawLines(0, lineCount); 
+		},
+		redrawRulers: function(startLine, endLine) {
+			if (this._redrawCount > 0) { return; }
+			var rulers = this.getRulers();
+			for (var i = 0; i < rulers.length; i++) {
+				this.redrawLines(startLine, endLine, rulers[i]);
+			}
+		},
+		/**
+		 * Redraws the text in the given line range.
+		 * <p>
+		 * The line at the end index is not redrawn.
+		 * </p>
+		 *
+		 * @param {Number} [startLine=0] the start line
+		 * @param {Number} [endLine=line count] the end line
+		 *
+		 * @see #redraw
+		 * @see #redrawRange
+		 * @see #setRedraw
+		 */
+		redrawLines: function(startLine, endLine, ruler) {
+			if (this._redrawCount > 0) { return; }
+			if (startLine === undefined) { startLine = 0; }
+			if (endLine === undefined) { endLine = this._model.getLineCount(); }
+			if (startLine === endLine) { return; }
+			var div = this._clientDiv;
+			if (!div) { return; }
+			if (ruler) {
+				var location = ruler.getLocation();//"left" or "right"
+				var divRuler = location === "left" ? this._leftDiv : this._rightDiv; //$NON-NLS-0$
+				div = divRuler.firstChild;
+				while (div) {
+					if (div._ruler === ruler) {
+						break;
+					}
+					div = div.nextSibling;
+				}
+			}
+			if (ruler) {
+				div.rulerChanged = true;
+			} else {
+				if (this._lineHeight) {
+					this._resetLineHeight(startLine, endLine);
+				}
+			}
+			if (!ruler || ruler.getOverview() === "page") { //$NON-NLS-0$
+				var child = div.firstChild;
+				while (child) {
+					var lineIndex = child.lineIndex;
+					if (startLine <= lineIndex && lineIndex < endLine) {
+						child.lineChanged = true;
+					}
+					child = child.nextSibling;
+				}
+			}
+			if (!ruler) {
+				if (!this._wrapMode) {
+					if (startLine <= this._maxLineIndex && this._maxLineIndex < endLine) {
+						this._checkMaxLineIndex = this._maxLineIndex;
+						this._maxLineIndex = -1;
+						this._maxLineWidth = 0;
+					}
+				}
+			}
+			this._queueUpdate();
+		},
+		/**
+		 * Redraws the text in the given range.
+		 * <p>
+		 * The character at the end offset is not redrawn.
+		 * </p>
+		 *
+		 * @param {Number} [start=0] the start offset of text range
+		 * @param {Number} [end=char count] the end offset of text range
+		 *
+		 * @see #redraw
+		 * @see #redrawLines
+		 * @see #setRedraw
+		 */
+		redrawRange: function(start, end) {
+			if (this._redrawCount > 0) { return; }
+			var model = this._model;
+			if (start === undefined) { start = 0; }
+			if (end === undefined) { end = model.getCharCount(); }
+			var startLine = model.getLineAtOffset(start);
+			var endLine = model.getLineAtOffset(Math.max(start, end - 1)) + 1;
+			this.redrawLines(startLine, endLine);
+		},	
+		/**
+		 * Removes a key mode from the text view.
+		 *
+		 * @param {orion.editor.KeyMode} mode the key mode.
+		 */
+		removeKeyMode: function (mode) {
+			var keyModes = this._keyModes;
+			for (var i=0; i<keyModes.length; i++) {
+				if (keyModes[i] === mode) {
+					keyModes.splice(i, 1);
+					break;
+				}
+			}
+			//TODO: API needed for this
+			if (mode._modeRemoved) {
+				mode._modeRemoved();
+			}
+		},
+		/**
+		 * Removes a ruler from the text view.
+		 *
+		 * @param {orion.editor.Ruler} ruler the ruler.
+		 */
+		removeRuler: function (ruler) {
+			var rulers = this._rulers;
+			for (var i=0; i<rulers.length; i++) {
+				if (rulers[i] === ruler) {
+					rulers.splice(i, 1);
+					ruler.setView(null);
+					this._destroyRuler(ruler);
+					this._update();
+					break;
+				}
+			}
+		},
+		resize: function() {
+			if (!this._clientDiv) { return; }
+			this._handleResize(null);
+		},
+		/**
+		 * @class This object describes an action for the text view.
+		 * <p>
+		 * <b>See:</b><br/>
+		 * {@link orion.editor.TextView}<br/>
+		 * {@link orion.editor.TextView#setAction}
+		 * </p>		 
+		 * @name orion.editor.ActionDescription
+		 *
+		 * @property {String} [name] the name to be used when showing the action as text.
+		 */
+		/**
+		 * Associates an application defined handler to an action ID.
+		 * <p>
+		 * If the action ID is a predefined action, the given handler executes before
+		 * the default action handler.  If the given handler returns <code>true</code>, the
+		 * default action handler is not called.
+		 * </p>
+		 *
+		 * @param {String} actionID the action ID.
+		 * @param {Function} handler the action handler.
+		 * @param {orion.editor.ActionDescription} [actionDescription=undefined] the action description.
+		 *
+		 * @see #getActions
+		 * @see #invokeAction
+		 */
+		setAction: function(actionID, handler, actionDescription) {
+			if (!actionID) { return; }
+			var actions = this._actions;
+			var action = actions[actionID];
+			if (!action) { 
+				action = actions[actionID] = {};
+			}
+			action.handler = handler;
+			if (actionDescription !== undefined) {
+				action.actionDescription = actionDescription;
+			}
+		},
+		/**
+		 * Associates a key binding with the given action ID. Any previous
+		 * association with the specified key binding is overwriten. If the
+		 * action ID is <code>null</code>, the association is removed.
+		 * 
+		 * @param {orion.editor.KeyBinding} keyBinding the key binding
+		 * @param {String} actionID the action ID
+		 */
+		setKeyBinding: function(keyBinding, actionID) {
+			this._keyModes[0].setKeyBinding(keyBinding, actionID);
+		},
+		/**
+		 * Sets the caret offset relative to the start of the document.
+		 *
+		 * @param {Number} caret the caret offset relative to the start of the document.
+		 * @param {Boolean|Number} [show=true] if <code>true</code>, the view will scroll the minimum amount necessary to show the caret location. If
+		 *					<code>show</code> is a <code>Number</code>, the view will scroll the minimum amount necessary to show the caret location plus a
+		 *					percentage of the client area height. The parameter is clamped to the [0,1] range.  In either case, the view will only scroll
+		 *					if the new caret location is visible already.
+		 * @param {Function} [callback] if callback is specified and <code>scrollAnimation</code> is not zero, view scrolling is animated and
+		 *					the callback is called when the animation is done. Otherwise, callback is callback right away.
+		 *
+		 * @see #getCaretOffset
+		 * @see #setSelection
+		 * @see #getSelection
+		 */
+		setCaretOffset: function(offset, show, callback) {
+			var charCount = this._model.getCharCount();
+			offset = Math.max(0, Math.min (offset, charCount));
+			var selection = new Selection(offset, offset, false);
+			this._setSelection (selection, show === undefined || show, true, callback);
+		},
+		/**
+		 * Sets the horizontal pixel.
+		 * <p>
+		 * The horizontal pixel is the pixel position that is currently at
+		 * the left edge of the view.  This position is relative to the
+		 * beginning of the document.
+		 * </p>
+		 *
+		 * @param {Number} pixel the horizontal pixel.
+		 *
+		 * @see #getHorizontalPixel
+		 * @see #convert
+		 */
+		setHorizontalPixel: function(pixel) {
+			if (!this._clientDiv) { return; }
+			pixel = Math.max(0, pixel);
+			this._scrollView(pixel - this._getScroll().x, 0);
+		},
+		/**
+		 * Sets whether the view should update the DOM.
+		 * <p>
+		 * This can be used to improve the performance.
+		 * </p><p>
+		 * When the flag is set to <code>true</code>,
+		 * the entire view is marked as needing to be redrawn. 
+		 * Nested calls to this method are stacked.
+		 * </p>
+		 *
+		 * @param {Boolean} redraw the new redraw state
+		 * 
+		 * @see #redraw
+		 */
+		setRedraw: function(redraw) {
+			if (redraw) {
+				if (--this._redrawCount === 0) {
+					this.redraw();
+				}
+			} else {
+				this._redrawCount++;
+			}
+		},
+		/**
+		 * Sets the text model of the text view.
+		 *
+		 * @param {orion.editor.TextModel} model the text model of the view.
+		 */
+		setModel: function(model) {
+			if (!model) { return; }
+			if (model === this._model) { return; }
+			this._model.removeEventListener("preChanging", this._modelListener.onChanging); //$NON-NLS-0$
+			this._model.removeEventListener("postChanged", this._modelListener.onChanged); //$NON-NLS-0$
+			var oldLineCount = this._model.getLineCount();
+			var oldCharCount = this._model.getCharCount();
+			var newLineCount = model.getLineCount();
+			var newCharCount = model.getCharCount();
+			var newText = model.getText();
+			var e = {
+				type: "ModelChanging", //$NON-NLS-0$
+				text: newText,
+				start: 0,
+				removedCharCount: oldCharCount,
+				addedCharCount: newCharCount,
+				removedLineCount: oldLineCount,
+				addedLineCount: newLineCount
+			};
+			this.onModelChanging(e);
+			this._model = model;
+			e = {
+				type: "ModelChanged", //$NON-NLS-0$
+				start: 0,
+				removedCharCount: oldCharCount,
+				addedCharCount: newCharCount,
+				removedLineCount: oldLineCount,
+				addedLineCount: newLineCount
+			};
+			this.onModelChanged(e); 
+			this._model.addEventListener("preChanging", this._modelListener.onChanging); //$NON-NLS-0$
+			this._model.addEventListener("postChanged", this._modelListener.onChanged); //$NON-NLS-0$
+			this._reset();
+			this._update();
+		},
+		/**
+		 * Sets the view options for the view.
+		 *
+		 * @param {orion.editor.TextViewOptions} options the view options.
+		 * 
+		 * @see #getOptions
+		 */
+		setOptions: function (options) {
+			var defaultOptions = this._defaultOptions();
+			for (var option in options) {
+				if (options.hasOwnProperty(option)) {
+					var newValue = options[option], oldValue = this["_" + option]; //$NON-NLS-0$
+					if (compare(oldValue, newValue)) { continue; }
+					var update = defaultOptions[option] ? defaultOptions[option].update : null;
+					if (update) {
+						update.call(this, newValue);
+						continue;
+					}
+					this["_" + option] = clone(newValue); //$NON-NLS-0$
+				}
+			}
+		},
+		/**
+		 * Sets the text view selection.
+		 * <p>
+		 * The selection is defined by a start and end character offset relative to the
+		 * document. The character at end offset is not included in the selection.
+		 * </p>
+		 * <p>
+		 * The caret is always placed at the end offset. The start offset can be
+		 * greater than the end offset to place the caret at the beginning of the
+		 * selection.
+		 * </p>
+		 * <p>
+		 * Clamps out of range offsets.
+		 * </p>
+		 * 
+		 * @param {Number} start the start offset of the selection
+		 * @param {Number} end the end offset of the selection
+		 * @param {Boolean|Number} [show=true] if <code>true</code>, the view will scroll the minimum amount necessary to show the caret location. If
+		 *					<code>show</code> is a <code>Number</code>, the view will scroll the minimum amount necessary to show the caret location plus a
+		 *					percentage of the client area height. The parameter is clamped to the [0,1] range.  In either case, the view will only scroll
+		 *					if the new caret location is visible already.
+		 * @param {Function} [callback] if callback is specified and <code>scrollAnimation</code> is not zero, view scrolling is animated and
+		 *					the callback is called when the animation is done. Otherwise, callback is callback right away.
+		 *
+		 * @see #getSelection
+		 */
+		setSelection: function (start, end, show, callback) {
+			var caret = start > end;
+			if (caret) {
+				var tmp = start;
+				start = end;
+				end = tmp;
+			}
+			var charCount = this._model.getCharCount();
+			start = Math.max(0, Math.min (start, charCount));
+			end = Math.max(0, Math.min (end, charCount));
+			var selection = new Selection(start, end, caret);
+			this._setSelection(selection, show === undefined || show, true, callback);
+		},
+		/**
+		 * Replaces the text in the given range with the given text.
+		 * <p>
+		 * The character at the end offset is not replaced.
+		 * </p>
+		 * <p>
+		 * When both <code>start</code> and <code>end</code> parameters
+		 * are not specified, the text view places the caret at the beginning
+		 * of the document and scrolls to make it visible.
+		 * </p>
+		 *
+		 * @param {String} text the new text.
+		 * @param {Number} [start=0] the start offset of text range.
+		 * @param {Number} [end=char count] the end offset of text range.
+		 *
+		 * @see #getText
+		 */
+		setText: function (text, start, end) {
+			var reset = start === undefined && end === undefined;
+			if (start === undefined) { start = 0; }
+			if (end === undefined) { end = this._model.getCharCount(); }
+			if (reset) {
+				this._variableLineHeight = false;
+			}
+			this._modifyContent({text: text, start: start, end: end, _code: true}, !reset);
+			if (reset) {
+				this._columnX = -1;
+				this._setSelection(new Selection (0, 0, false), true);
+				
+				/*
+				* Bug in Firefox.  For some reason, the caret does not show after the
+				* view is refreshed.  The fix is to toggle the contentEditable state and
+				* force the clientDiv to loose and receive focus if it is focused.
+				*/
+				if (util.isFirefox) {
+					this._fixCaret();
+				}
+			}
+		},
+		/**
+		 * Sets the top index.
+		 * <p>
+		 * The top index is the line that is currently at the top of the text view.  This
+		 * line may be partially visible depending on the vertical scroll of the view.
+		 * </p>
+		 *
+		 * @param {Number} topIndex the index of the top line.
+		 *
+		 * @see #getBottomIndex
+		 * @see #getTopIndex
+		 */
+		setTopIndex: function(topIndex) {
+			if (!this._clientDiv) { return; }
+			this._scrollView(0, this._getLinePixel(Math.max(0, topIndex)) - this._getScroll().y);
+		},
+		/**
+		 * Sets the top pixel.
+		 * <p>
+		 * The top pixel is the pixel position that is currently at
+		 * the top edge of the view.  This position is relative to the
+		 * beginning of the document.
+		 * </p>
+		 *
+		 * @param {Number} pixel the top pixel.
+		 *
+		 * @see #getBottomPixel
+		 * @see #getTopPixel
+		 * @see #convert
+		 */
+		setTopPixel: function(pixel) {
+			if (!this._clientDiv) { return; }
+			this._scrollView(0, Math.max(0, pixel) - this._getScroll().y);
+		},
+		/**
+		 * Scrolls the selection into view if needed.
+		 *
+		 * @returns true if the view was scrolled. 
+		 *
+		 * @see #getSelection
+		 * @see #setSelection
+		 */
+		showSelection: function() {
+			return this._showCaret(true);
+		},
+		update: function(styleChanged, sync) {
+			if (!this._clientDiv) { return; }
+			if (styleChanged) {
+				this._updateStyle();
+			}
+			if (sync === undefined || sync) {
+				this._update();
+			} else {
+				this._queueUpdate();
+			}
+		},
+		
+		/**************************************** Event handlers *********************************/
+		_handleRootMouseDown: function (e) {
+			if (this._ignoreEvent(e)) { return; }
+			if (util.isFirefox && e.which === 1) {
+				this._clientDiv.contentEditable = false;
+				(this._overlayDiv || this._clientDiv).draggable = true;
+				this._ignoreBlur = true;
+			}
+			
+			/* Prevent clicks outside of the client div from taking focus away. */
+			var topNode = this._overlayDiv || this._clientDiv;
+			/* Use view div on IE 8 otherwise it is not possible to scroll. */
+			if (util.isIE < 9) { topNode = this._viewDiv; }
+			var temp = e.target ? e.target : e.srcElement;
+			while (temp) {
+				if (topNode === temp) {
+					return;
+				}
+				temp = temp.parentNode;
+			}
+			if (e.preventDefault) { e.preventDefault(); }
+			if (e.stopPropagation){ e.stopPropagation(); }
+			if (!this._isW3CEvents) {
+				/*
+				* In IE 8 is not possible to prevent the default handler from running
+				* during mouse down event using usual API. The workaround is to give
+				* focus back to the client div.
+				*/ 
+				var self = this;
+				var window = this._getWindow();
+				window.setTimeout(function() {
+					self._clientDiv.focus();
+				}, 0);
+			}
+		},
+		_handleRootMouseUp: function (e) {
+			if (this._ignoreEvent(e)) { return; }
+			if (util.isFirefox && e.which === 1) {
+				this._clientDiv.contentEditable = true;
+				(this._overlayDiv || this._clientDiv).draggable = false;
+				
+				/*
+				* Bug in Firefox.  For some reason, Firefox stops showing the caret
+				* in some cases. For example when the user cancels a drag operation 
+				* by pressing ESC.  The fix is to detect that the drag operation was
+				* cancelled,  toggle the contentEditable state and force the clientDiv
+				* to loose and receive focus if it is focused.
+				*/
+				this._fixCaret();
+				this._ignoreBlur = false;
+			}
+		},
+		_handleBlur: function (e) {
+			if (this._ignoreBlur) { return; }
+			this._hasFocus = false;
+			/*
+			* Bug in IE 8 and earlier. For some reason when text is deselected
+			* the overflow selection at the end of some lines does not get redrawn.
+			* The fix is to create a DOM element in the body to force a redraw.
+			*/
+			if (util.isIE < 9) {
+				if (!this._getSelection().isEmpty()) {
+					var rootDiv = this._rootDiv;
+					var child = util.createElement(rootDiv.ownerDocument, "div"); //$NON-NLS-0$
+					rootDiv.appendChild(child);
+					rootDiv.removeChild(child);
+				}
+			}
+			if (this._cursorDiv) {
+				this._cursorDiv.style.display = "none"; //$NON-NLS-0$
+			}
+			if (this._selDiv1) {
+				var color = "lightgray"; //$NON-NLS-0$
+				this._selDiv1.style.background = color;
+				this._selDiv2.style.background = color;
+				this._selDiv3.style.background = color;
+				/* Clear browser selection if selection is within clientDiv */
+				var temp;
+				var window = this._getWindow();
+				var document = this._selDiv1.ownerDocument;
+				if (window.getSelection) {
+					var sel = window.getSelection();
+					temp = sel.anchorNode;
+					while (temp) {
+						if (temp === this._clientDiv) {
+							if (sel.rangeCount > 0) { sel.removeAllRanges(); }
+							break;
+						}
+						temp = temp.parentNode;
+					}
+				} else if (document.selection) {
+					this._ignoreSelect = false;
+					temp = document.selection.createRange().parentElement();
+					while (temp) {
+						if (temp === this._clientDiv) {
+							document.selection.empty();
+							break;
+						}
+						temp = temp.parentNode;
+					}
+					this._ignoreSelect = true;
+				}
+			}
+			if (!this._ignoreFocus) {
+				this.onBlur({type: "Blur"}); //$NON-NLS-0$
+			}
+		},
+		_handleContextMenu: function (e) {
+			if (this._ignoreEvent(e)) { return; }
+			if (util.isIE && this._lastMouseButton === 3) {
+				// We need to update the DOM selection, because on
+				// right-click the caret moves to the mouse location.
+				// See bug 366312 and 376508.
+				this._updateDOMSelection();
+			}
+			var preventDefault = false;
+			if (this.isListening("ContextMenu")) { //$NON-NLS-0$
+				var evt = this._createMouseEvent("ContextMenu", e); //$NON-NLS-0$
+				evt.screenX = e.screenX;
+				evt.screenY = e.screenY;
+				this.onContextMenu(evt);
+				preventDefault = evt.defaultPrevented;
+			} else if (util.isMac && util.isFirefox && e.button === 0) {
+				// hack to prevent CTRL+Space from showing the browser context menu
+				preventDefault = true;
+			}
+			if (preventDefault) {
+				if (e.preventDefault) { e.preventDefault(); }
+				return false;
+			}
+		},
+		_handleCopy: function (e) {
+			if (this._ignoreEvent(e)) { return; }
+			if (this._ignoreCopy) { return; }
+			if (this._doCopy(e)) {
+				if (e.preventDefault) { e.preventDefault(); }
+				return false;
+			}
+		},
+		_handleCut: function (e) {
+			if (this._ignoreEvent(e)) { return; }
+			if (this._doCut(e)) {
+				if (e.preventDefault) { e.preventDefault(); }
+				return false;
+			}
+		},
+		_handleDataModified: function(e) {
+			if (this._ignoreEvent(e)) { return; }
+			this._startIME();
+		},
+		_handleDblclick: function (e) {
+			if (this._ignoreEvent(e)) { return; }
+			var time = e.timeStamp ? e.timeStamp : new Date().getTime();
+			this._lastMouseTime = time;
+			if (this._clickCount !== 2) {
+				this._clickCount = 2;
+				this._handleMouse(e);
+			}
+		},
+		_handleDragStart: function (e) {
+			if (this._ignoreEvent(e)) { return; }
+			if (util.isFirefox) {
+				var self = this;
+				var window = this._getWindow();
+				window.setTimeout(function() {
+					self._clientDiv.contentEditable = true;
+					self._clientDiv.draggable = false;
+					self._ignoreBlur = false;
+				}, 0);
+			}
+			if (this.isListening("DragStart") && this._dragOffset !== -1) { //$NON-NLS-0$
+				this._isMouseDown = false;
+				this.onDragStart(this._createMouseEvent("DragStart", e)); //$NON-NLS-0$
+				this._dragOffset = -1;
+			} else {
+				if (e.preventDefault) { e.preventDefault(); }
+				return false;
+			}
+		},
+		_handleDrag: function (e) {
+			if (this._ignoreEvent(e)) { return; }
+			if (this.isListening("Drag")) { //$NON-NLS-0$
+				this.onDrag(this._createMouseEvent("Drag", e)); //$NON-NLS-0$
+			}
+		},
+		_handleDragEnd: function (e) {
+			if (this._ignoreEvent(e)) { return; }
+			this._dropTarget = false;
+			this._dragOffset = -1;
+			if (this.isListening("DragEnd")) { //$NON-NLS-0$
+				this.onDragEnd(this._createMouseEvent("DragEnd", e)); //$NON-NLS-0$
+			}
+			if (util.isFirefox) {
+				this._fixCaret();
+				/*
+				* Bug in Firefox.  For some reason, Firefox stops showing the caret when the 
+				* selection is dropped onto itself. The fix is to detected the case and 
+				* call fixCaret() a second time.
+				*/
+				if (e.dataTransfer.dropEffect === "none" && !e.dataTransfer.mozUserCancelled) { //$NON-NLS-0$
+					this._fixCaret();
+				}
+			}
+		},
+		_handleDragEnter: function (e) {
+			if (this._ignoreEvent(e)) { return; }
+			var prevent = true;
+			this._dropTarget = true;
+			if (this.isListening("DragEnter")) { //$NON-NLS-0$
+				prevent = false;
+				this.onDragEnter(this._createMouseEvent("DragEnter", e)); //$NON-NLS-0$
+			}
+			/*
+			* Webkit will not send drop events if this event is not prevented, as spec in HTML5.
+			* Firefox and IE do not follow this spec for contentEditable. Note that preventing this 
+			* event will result is loss of functionality (insertion mark, etc).
+			*/
+			if (util.isWebkit || prevent) {
+				if (e.preventDefault) { e.preventDefault(); }
+				return false;
+			}
+		},
+		_handleDragOver: function (e) {
+			if (this._ignoreEvent(e)) { return; }
+			var prevent = true;
+			if (this.isListening("DragOver")) { //$NON-NLS-0$
+				prevent = false;
+				this.onDragOver(this._createMouseEvent("DragOver", e)); //$NON-NLS-0$
+			}
+			/*
+			* Webkit will not send drop events if this event is not prevented, as spec in HTML5.
+			* Firefox and IE do not follow this spec for contentEditable. Note that preventing this 
+			* event will result is loss of functionality (insertion mark, etc).
+			*/
+			if (util.isWebkit || prevent) {
+				if (prevent) { e.dataTransfer.dropEffect = "none"; } //$NON-NLS-0$
+				if (e.preventDefault) { e.preventDefault(); }
+				return false;
+			}
+		},
+		_handleDragLeave: function (e) {
+			if (this._ignoreEvent(e)) { return; }
+			this._dropTarget = false;
+			if (this.isListening("DragLeave")) { //$NON-NLS-0$
+				this.onDragLeave(this._createMouseEvent("DragLeave", e)); //$NON-NLS-0$
+			}
+		},
+		_handleDrop: function (e) {
+			if (this._ignoreEvent(e)) { return; }
+			this._dropTarget = false;
+			if (this.isListening("Drop")) { //$NON-NLS-0$
+				this.onDrop(this._createMouseEvent("Drop", e)); //$NON-NLS-0$
+			}
+			/*
+			* This event must be prevented otherwise the user agent will modify
+			* the DOM. Note that preventing the event on some user agents (i.e. IE)
+			* indicates that the operation is cancelled. This causes the dropEffect to 
+			* be set to none  in the dragend event causing the implementor to not execute
+			* the code responsible by the move effect.
+			*/
+			if (e.preventDefault) { e.preventDefault(); }
+			return false;
+		},
+		_handleFocus: function (e) {
+			this._hasFocus = true;
+			if (util.isIOS && this._lastTouchOffset !== undefined) {
+				this.setCaretOffset(this._lastTouchOffset, true);
+				this._lastTouchOffset = undefined;
+			} else {
+				this._updateDOMSelection();
+			}
+			if (this._cursorDiv) {
+				this._cursorDiv.style.display = "block"; //$NON-NLS-0$
+			}
+			if (this._selDiv1) {
+				var color = this._highlightRGB;
+				this._selDiv1.style.background = color;
+				this._selDiv2.style.background = color;
+				this._selDiv3.style.background = color;
+			}
+			if (!this._ignoreFocus) {
+				this.onFocus({type: "Focus"}); //$NON-NLS-0$
+			}
+		},
+		_handleKeyDown: function (e) {
+			if (this._ignoreEvent(e)) {	return;	}
+			if (this.isListening("KeyDown")) { //$NON-NLS-0$
+				var keyEvent = this._createKeyEvent("KeyDown", e); //$NON-NLS-0$
+				this.onKeyDown(keyEvent); //$NON-NLS-0$
+				if (keyEvent.defaultPrevented) {
+					/*
+					* Feature in Firefox. Keypress events still happen even if the keydown event
+					* was prevented. The fix is to remember that keydown was prevented and prevent
+					* the keypress ourselves.
+					*/
+					if (util.isFirefox) {
+						this._keyDownPrevented = true;
+					}
+					e.preventDefault();
+					return;
+				}
+			}
+			var modifier = false;
+			switch (e.keyCode) {
+				case 16: /* Shift */
+				case 17: /* Control */
+				case 18: /* Alt */
+				case 91: /* Command */
+					modifier = true;
+					break;
+				default:
+					this._setLinksVisible(false);
+			}
+			if (e.keyCode === 229) {
+				if (this._readonly) {
+					if (e.preventDefault) { e.preventDefault(); }
+					return false;
+				}
+				var startIME = true;
+				
+				/*
+				* Bug in Safari. Some Control+key combinations send key events
+				* with keyCode equals to 229. This is unexpected and causes the
+				* view to start an IME composition. The fix is to ignore these
+				* events.
+				*/
+				if (util.isSafari && util.isMac) {
+					if (e.ctrlKey) {
+						startIME = false;
+						e.keyCode = 0x81;
+					}
+				}
+				if (startIME) {
+					this._startIME();
+				}
+			} else {
+				if (!modifier) {
+					this._commitIME();
+				}
+			}
+			/*
+			* Feature in Firefox. When a key is held down the browser sends 
+			* right number of keypress events but only one keydown. This is
+			* unexpected and causes the view to only execute an action
+			* just one time. The fix is to ignore the keydown event and 
+			* execute the actions from the keypress handler.
+			* Note: This only happens on the Mac and Linux (Firefox 3.6).
+			*
+			* Feature in Opera.  Opera sends keypress events even for non-printable
+			* keys.  The fix is to handle actions in keypress instead of keydown.
+			*/
+			if (((util.isMac || util.isLinux) && util.isFirefox < 4) || util.isOpera) {
+				this._keyDownEvent = e;
+				return true;
+			}
+			
+			if (this._doAction(e)) {
+				if (e.preventDefault) {
+					e.preventDefault(); 
+					e.stopPropagation(); 
+				} else {
+					e.cancelBubble = true;
+					e.returnValue = false;
+					e.keyCode = 0;
+				}
+				return false;
+			}
+		},
+		_handleKeyPress: function (e) {
+			if (this._ignoreEvent(e)) { return; }
+			/*
+			* Feature in Firefox. Keypress events still happen even if the keydown event
+			* was prevented. The fix is to remember that keydown was prevented and prevent
+			* the keypress ourselves.
+			*/
+			if (this._keyDownPrevented) { 
+				if (e.preventDefault) {
+					e.preventDefault(); 
+					e.stopPropagation(); 
+				} 
+				this._keyDownPrevented = undefined;
+				return;
+			}
+			/*
+			* Feature in Embedded WebKit.  Embedded WekKit on Mac runs in compatibility mode and
+			* generates key press events for these Unicode values (Function keys).  This does not
+			* happen in Safari or Chrome.  The fix is to ignore these key events.
+			*/
+			if (util.isMac && util.isWebkit) {
+				if ((0xF700 <= e.keyCode && e.keyCode <= 0xF7FF) || e.keyCode === 13 || e.keyCode === 8) {
+					if (e.preventDefault) { e.preventDefault(); }
+					return false;
+				}
+			}
+			if (((util.isMac || util.isLinux) && util.isFirefox < 4) || util.isOpera) {
+				if (this._doAction(this._keyDownEvent)) {
+					if (e.preventDefault) { e.preventDefault(); }
+					return false;
+				}
+			}
+			var ctrlKey = util.isMac ? e.metaKey : e.ctrlKey;
+			if (e.charCode !== undefined) {
+				if (ctrlKey) {
+					switch (e.charCode) {
+						/*
+						* In Firefox and Safari if ctrl+v, ctrl+c ctrl+x is canceled
+						* the clipboard events are not sent. The fix to allow
+						* the browser to handles these key events.
+						*/
+						case 99://c
+						case 118://v
+						case 120://x
+							return true;
+					}
+				}
+			}
+			if (this.isListening("KeyPress")) { //$NON-NLS-0$
+				var keyEvent = this._createKeyEvent("KeyPress", e); //$NON-NLS-0$
+				this.onKeyPress(keyEvent); //$NON-NLS-0$
+				if (keyEvent.defaultPrevented) {
+					e.preventDefault();
+					return;
+				}
+			}
+			if (this._doAction(e)) {
+				if (e.preventDefault) {
+					e.preventDefault(); 
+					e.stopPropagation(); 
+				} else {
+					e.cancelBubble = true;
+					e.returnValue = false;
+					e.keyCode = 0;
+				}
+				return false;
+			}
+			var ignore = false;
+			if (util.isMac) {
+				if (e.ctrlKey || e.metaKey) { ignore = true; }
+			} else {
+				if (util.isFirefox) {
+					//Firefox clears the state mask when ALT GR generates input
+					if (e.ctrlKey || e.altKey) { ignore = true; }
+				} else {
+					//IE and Chrome only send ALT GR when input is generated
+					if (e.ctrlKey ^ e.altKey) { ignore = true; }
+				}
+			}
+			if (!ignore) {
+				var key = util.isOpera ? e.which : (e.charCode !== undefined ? e.charCode : e.keyCode);
+				if (key > 31) {
+					this._doContent(String.fromCharCode (key));
+					if (e.preventDefault) { e.preventDefault(); }
+					return false;
+				}
+			}
+		},
+		_handleKeyUp: function (e) {
+			if (this._ignoreEvent(e)) { return; }
+			if (this.isListening("KeyUp")) { //$NON-NLS-0$
+				var keyEvent = this._createKeyEvent("KeyUp", e); //$NON-NLS-0$
+				this.onKeyUp(keyEvent); //$NON-NLS-0$
+				if (keyEvent.defaultPrevented) {
+					e.preventDefault();
+					return;
+				}
+			}
+			var ctrlKey = util.isMac ? e.metaKey : e.ctrlKey;
+			if (!ctrlKey) {
+				this._setLinksVisible(false);
+			}
+			// don't commit for space (it happens during JP composition)  
+			if (e.keyCode === 13) {
+				this._commitIME();
+			}
+		},
+		_handleLinkClick: function (e) {
+			var ctrlKey = util.isMac ? e.metaKey : e.ctrlKey;
+			if (!ctrlKey) {
+				if (e.preventDefault) { e.preventDefault(); }
+				return false;
+			}
+		},
+		_handleMouse: function (e) {
+			var window = this._getWindow();
+			var result = true;
+			var target = window;
+			if (util.isIE || (util.isFirefox && !this._overlayDiv)) { target = this._clientDiv; }
+			if (this._overlayDiv) {
+				if (this._hasFocus) {
+					this._ignoreFocus = true;
+				}
+				var self = this;
+				window.setTimeout(function () {
+					self.focus();
+					self._ignoreFocus = false;
+				}, 0);
+			}
+			if (this._clickCount === 1) {
+				result = this._setSelectionTo(e.clientX, e.clientY, e.shiftKey, !util.isOpera && this._hasFocus && this.isListening("DragStart")); //$NON-NLS-0$
+				if (result) { this._setGrab(target); }
+			} else {
+				/*
+				* Feature in IE8 and older, the sequence of events in the IE8 event model
+				* for a doule-click is:
+				*
+				*	down
+				*	up
+				*	up
+				*	dblclick
+				*
+				* Given that the mouse down/up events are not balanced, it is not possible to
+				* grab on mouse down and ungrab on mouse up.  The fix is to grab on the first
+				* mouse down and ungrab on mouse move when the button 1 is not set.
+				*/
+				if (this._isW3CEvents) { this._setGrab(target); }
+				
+				this._doubleClickSelection = null;
+				this._setSelectionTo(e.clientX, e.clientY, e.shiftKey);
+				this._doubleClickSelection = this._getSelection();
+			}
+			return result;
+		},
+		_handleMouseDown: function (e) {
+			if (this._ignoreEvent(e)) { return; }
+			if (this._linksVisible) {
+				var target = e.target || e.srcElement;
+				if (target.tagName !== "A") { //$NON-NLS-0$
+					this._setLinksVisible(false);
+				} else {
+					return;
+				}
+			}
+			this._commitIME();
+
+			var button = e.which; // 1 - left, 2 - middle, 3 - right
+			if (!button) { 
+				// if IE 8 or older
+				if (e.button === 4) { button = 2; }
+				if (e.button === 2) { button = 3; }
+				if (e.button === 1) { button = 1; }
+			}
+
+			// For middle click we always need getTime(). See _getClipboardText().
+			var time = button !== 2 && e.timeStamp ? e.timeStamp : new Date().getTime();
+			var timeDiff = time - this._lastMouseTime;
+			var deltaX = Math.abs(this._lastMouseX - e.clientX);
+			var deltaY = Math.abs(this._lastMouseY - e.clientY);
+			var sameButton = this._lastMouseButton === button;
+			this._lastMouseX = e.clientX;
+			this._lastMouseY = e.clientY;
+			this._lastMouseTime = time;
+			this._lastMouseButton = button;
+
+			if (button === 1) {
+				this._isMouseDown = true;
+				if (sameButton && timeDiff <= this._clickTime && deltaX <= this._clickDist && deltaY <= this._clickDist) {
+					this._clickCount++;
+				} else {
+					this._clickCount = 1;
+				}
+			}
+			if (this.isListening("MouseDown")) { //$NON-NLS-0$
+				var mouseEvent = this._createMouseEvent("MouseDown", e); //$NON-NLS-0$
+				this.onMouseDown(mouseEvent);
+				if (mouseEvent.defaultPrevented) {
+					e.preventDefault();
+					return;
+				}
+			}
+			if (button === 1) {
+				if (this._handleMouse(e) && (util.isIE >= 9 || util.isOpera || util.isChrome || util.isSafari || (util.isFirefox && !this._overlayDiv))) {
+					if (!this._hasFocus) {
+						this.focus();
+					}
+					e.preventDefault();
+				}
+			}
+			if (util.isFirefox && this._lastMouseButton === 3) {
+				// We need to update the DOM selection, because on
+				// right-click the caret moves to the mouse location.
+				// See bug 366312 and 376508.
+				this._updateDOMSelection();
+			}
+		},
+		_handleMouseOver: function (e) {
+			if (this._ignoreEvent(e)) { return; }
+			if (this._animation) { return; }
+			if (this.isListening("MouseOver")) { //$NON-NLS-0$
+				this.onMouseOver(this._createMouseEvent("MouseOver", e)); //$NON-NLS-0$
+			}
+		},
+		_handleMouseOut: function (e) {
+			if (this._ignoreEvent(e)) { return; }
+			if (this._animation) { return; }
+			if (this.isListening("MouseOut")) { //$NON-NLS-0$
+				this.onMouseOut(this._createMouseEvent("MouseOut", e)); //$NON-NLS-0$
+			}
+		},
+		_handleMouseMove: function (e) {
+			if (this._animation) { return; }
+			var inClient = this._isClientDiv(e);
+			if (this.isListening("MouseMove")) { //$NON-NLS-0$
+				if (inClient){
+					this.onMouseMove(this._createMouseEvent("MouseMove", e)); //$NON-NLS-0$
+				}
+			}
+			if (this._dropTarget) {
+				return;
+			}
+			/*
+			* Bug in IE9. IE sends one mouse event when the user changes the text by
+			* pasting or undo.  These operations usually happen with the Ctrl key
+			* down which causes the view to enter link mode.  Link mode does not end
+			* because there are no further events.  The fix is to only enter link
+			* mode when the coordinates of the mouse move event have changed.
+			*/
+			var changed = this._linksVisible || this._lastMouseMoveX !== e.clientX || this._lastMouseMoveY !== e.clientY;
+			this._lastMouseMoveX = e.clientX;
+			this._lastMouseMoveY = e.clientY;
+			this._setLinksVisible(changed && !this._isMouseDown && (util.isMac ? e.metaKey : e.ctrlKey));
+
+			/*
+			* Feature in IE8 and older, the sequence of events in the IE8 event model
+			* for a doule-click is:
+			*
+			*	down
+			*	up
+			*	up
+			*	dblclick
+			*
+			* Given that the mouse down/up events are not balanced, it is not possible to
+			* grab on mouse down and ungrab on mouse up.  The fix is to grab on the first
+			* mouse down and ungrab on mouse move when the button 1 is not set.
+			*
+			* In order to detect double-click and drag gestures, it is necessary to send
+			* a mouse down event from mouse move when the button is still down and isMouseDown
+			* flag is not set.
+			*/
+			if (!this._isW3CEvents) {
+				if (e.button === 0) {
+					this._setGrab(null);
+					return true;
+				}
+				if (!this._isMouseDown && e.button === 1 && (this._clickCount & 1) !== 0 && inClient) {
+					this._clickCount = 2;
+					return this._handleMouse(e, this._clickCount);
+				}
+			}
+			if (!this._isMouseDown || this._dragOffset !== -1) {
+				return;
+			}
+			
+			var x = e.clientX;
+			var y = e.clientY;
+			var viewPad = this._getViewPadding();
+			var viewRect = this._viewDiv.getBoundingClientRect();
+			var width = this._getClientWidth (), height = this._getClientHeight();
+			var leftEdge = viewRect.left + viewPad.left;
+			var topEdge = viewRect.top + viewPad.top;
+			var rightEdge = viewRect.left + viewPad.left + width;
+			var bottomEdge = viewRect.top + viewPad.top + height;
+			if (y < topEdge) {
+				this._doAutoScroll("up", x, y - topEdge); //$NON-NLS-0$
+			} else if (y > bottomEdge) {
+				this._doAutoScroll("down", x, y - bottomEdge); //$NON-NLS-0$
+			} else if (x < leftEdge && !this._wrapMode) {
+				this._doAutoScroll("left", x - leftEdge, y); //$NON-NLS-0$
+			} else if (x > rightEdge && !this._wrapMode) {
+				this._doAutoScroll("right", x - rightEdge, y); //$NON-NLS-0$
+			} else {
+				this._endAutoScroll();
+				this._setSelectionTo(x, y, true);
+			}
+		},
+		_isClientDiv: function(e) {
+			var topNode = this._overlayDiv || this._clientDiv;
+			var temp = e.target ? e.target : e.srcElement;
+			while (temp) {
+				if (topNode === temp) {
+					return true;
+				}
+				temp = temp.parentNode;
+			}
+			return false;
+		},
+		_createKeyEvent: function(type, e) {
+			return {
+				type: type,
+				event: e,
+				preventDefault: function() {
+					this.defaultPrevented = true;
+				}
+			};
+		},
+		_createMouseEvent: function(type, e) {
+			var pt = this.convert({x: e.clientX, y: e.clientY}, "page", "document"); //$NON-NLS-1$ //$NON-NLS-0$
+			return {
+				type: type,
+				event: e,
+				clickCount: this._clickCount,
+				x: pt.x,
+				y: pt.y,
+				preventDefault: function() {
+					this.defaultPrevented = true;
+				}
+			};
+		},
+		_handleMouseUp: function (e) {
+			var left = e.which ? e.button === 0 : e.button === 1;
+			if (this.isListening("MouseUp")) { //$NON-NLS-0$
+				if (this._isClientDiv(e) || (left && this._isMouseDown)) {
+					this.onMouseUp(this._createMouseEvent("MouseUp", e)); //$NON-NLS-0$
+				}
+			}
+			if (this._linksVisible) {
+				return;
+			}
+			if (left && this._isMouseDown) {
+				if (this._dragOffset !== -1) {
+					var selection = this._getSelection();
+					selection.extend(this._dragOffset);
+					selection.collapse();
+					this._setSelection(selection, true, true);
+					this._dragOffset = -1;
+				}
+				this._isMouseDown = false;
+				this._endAutoScroll();
+				
+				/*
+				* Feature in IE8 and older, the sequence of events in the IE8 event model
+				* for a doule-click is:
+				*
+				*	down
+				*	up
+				*	up
+				*	dblclick
+				*
+				* Given that the mouse down/up events are not balanced, it is not possible to
+				* grab on mouse down and ungrab on mouse up.  The fix is to grab on the first
+				* mouse down and ungrab on mouse move when the button 1 is not set.
+				*/
+				if (this._isW3CEvents) { this._setGrab(null); }
+
+				/*
+				* Note that there cases when Firefox sets the DOM selection in mouse up.
+				* This happens for example after a cancelled drag operation.
+				*
+				* Note that on Chrome and IE, the caret stops blicking if mouse up is
+				* prevented.
+				*/
+				if (util.isFirefox) {
+					e.preventDefault();
+				}
+			}
+		},
+		_handleMouseWheel: function (e) {
+			var lineHeight = this._getLineHeight();
+			var pixelX = 0, pixelY = 0;
+			// Note: On the Mac the correct behaviour is to scroll by pixel.
+			if (util.isIE || util.isOpera) {
+				pixelY = (-e.wheelDelta / 40) * lineHeight;
+			} else if (util.isFirefox) {
+				var pixel;
+				if (util.isMac) {
+					pixel = e.detail * 3;
+				} else {
+					var limit = 256;
+					pixel = Math.max(-limit, Math.min(limit, e.detail)) * lineHeight;
+				}
+				if (e.axis === e.HORIZONTAL_AXIS) {
+					pixelX = pixel;
+				} else {
+					pixelY = pixel;
+				}
+			} else {
+				//Webkit
+				if (util.isMac) {
+					/*
+					* In Safari, the wheel delta is a multiple of 120. In order to
+					* convert delta to pixel values, it is necessary to divide delta
+					* by 40.
+					*
+					* In Chrome and Safari 5, the wheel delta depends on the type of the
+					* mouse. In general, it is the pixel value for Mac mice and track pads,
+					* but it is a multiple of 120 for other mice. There is no presise
+					* way to determine if it is pixel value or a multiple of 120.
+					* 
+					* Note that the current approach does not calculate the correct
+					* pixel value for Mac mice when the delta is a multiple of 120.
+					*
+					* For values that are multiples of 120, the denominator varies on
+					* the time between events.
+					*/
+					var denominatorX, denominatorY;
+					var deltaTime = e.timeStamp - this._wheelTimeStamp;
+					this._wheelTimeStamp = e.timeStamp;
+					if (e.wheelDeltaX % 120 !== 0) { 
+						denominatorX = 1; 
+					} else {
+						denominatorX = deltaTime < 40 ? 40/(40-deltaTime) : 40;
+					}
+					if (e.wheelDeltaY % 120 !== 0) { 
+						denominatorY = 1; 
+					} else {
+						denominatorY = deltaTime < 40 ? 40/(40-deltaTime) : 40; 
+					}
+					pixelX = Math.ceil(-e.wheelDeltaX / denominatorX);
+					if (-1 < pixelX && pixelX < 0) { pixelX = -1; }
+					if (0 < pixelX && pixelX < 1) { pixelX = 1; }
+					pixelY = Math.ceil(-e.wheelDeltaY / denominatorY);
+					if (-1 < pixelY && pixelY < 0) { pixelY = -1; }
+					if (0 < pixelY && pixelY < 1) { pixelY = 1; }
+				} else {
+					pixelX = -e.wheelDeltaX;
+					var linesToScroll = 8;
+					pixelY = (-e.wheelDeltaY / 120 * linesToScroll) * lineHeight;
+				}
+			}
+			/* 
+			* Feature in Safari. If the event target is removed from the DOM 
+			* safari stops smooth scrolling. The fix is keep the element target
+			* in the DOM and remove it on a later time. 
+			*
+			* Note: Using a timer is not a solution, because the timeout needs to
+			* be at least as long as the gesture (which is too long).
+			*/
+			if (util.isSafari) {
+				var lineDiv = e.target;
+				while (lineDiv && lineDiv.lineIndex === undefined) {
+					lineDiv = lineDiv.parentNode;
+				}
+				this._mouseWheelLine = lineDiv;
+			}
+			var oldScroll = this._getScroll();
+			this._scrollView(pixelX, pixelY);
+			var newScroll = this._getScroll();
+			if (oldScroll.x !== newScroll.x || oldScroll.y !== newScroll.y) {
+				if (e.preventDefault) { e.preventDefault(); }
+				return false;
+			}
+		},
+		_handlePaste: function (e) {
+			if (this._ignoreEvent(e)) { return; }
+			if (this._ignorePaste) { return; }
+			if (this._doPaste(e)) {
+				if (util.isIE) {
+					/*
+					 * Bug in IE,  
+					 */
+					var self = this;
+					this._ignoreFocus = true;
+					var window = this._getWindow();
+					window.setTimeout(function() {
+						self._updateDOMSelection();
+						self._ignoreFocus = false;
+					}, 0);
+				}
+				if (e.preventDefault) { e.preventDefault(); }
+				return false;
+			}
+		},
+		_handleResize: function (e) {
+			var newWidth = this._parent.clientWidth;
+			var newHeight = this._parent.clientHeight;
+			if (this._parentWidth !== newWidth || this._parentHeight !== newHeight) {
+				if (this._parentWidth !== newWidth && this._wrapMode) {
+					this._resetLineHeight();
+				}
+				this._parentWidth = newWidth;
+				this._parentHeight = newHeight;
+				/*
+				* Feature in IE7. For some reason, sometimes Internet Explorer 7 
+				* returns incorrect values for element.getBoundingClientRect() when 
+				* inside a resize handler. The fix is to queue the work.
+				*/
+				if (util.isIE < 9) {
+					this._queueUpdate();
+				} else {
+					this._update();
+				}
+			}
+		},
+		_handleRulerEvent: function (e) {
+			var target = e.target ? e.target : e.srcElement;
+			var lineIndex = target.lineIndex;
+			var element = target;
+			while (element && !element._ruler) {
+				if (lineIndex === undefined && element.lineIndex !== undefined) {
+					lineIndex = element.lineIndex;
+				}
+				element = element.parentNode;
+			}
+			var ruler = element ? element._ruler : null;
+			if (lineIndex === undefined && ruler && ruler.getOverview() === "document") { //$NON-NLS-0$
+				var clientHeight = this._getClientHeight ();
+				var lineCount = this._model.getLineCount ();
+				var viewPad = this._getViewPadding();
+				var viewRect = this._viewDiv.getBoundingClientRect();
+				var trackHeight = clientHeight + viewPad.top + viewPad.bottom - 2 * this._metrics.scrollWidth;
+				lineIndex = Math.floor(((e.clientY - viewRect.top) - this._metrics.scrollWidth) * lineCount / trackHeight);
+				if (!(0 <= lineIndex && lineIndex < lineCount)) {
+					lineIndex = undefined;
+				}
+			}
+			if (ruler) {
+				switch (e.type) {
+					case "click": //$NON-NLS-0$
+						if (ruler.onClick) { ruler.onClick(lineIndex, e); }
+						break;
+					case "dblclick": //$NON-NLS-0$
+						if (ruler.onDblClick) { ruler.onDblClick(lineIndex, e); }
+						break;
+					case "mousemove": //$NON-NLS-0$
+						if (ruler.onMouseMove) { ruler.onMouseMove(lineIndex, e); }
+						break;
+					case "mouseover": //$NON-NLS-0$
+						if (ruler.onMouseOver) { ruler.onMouseOver(lineIndex, e); }
+						break;
+					case "mouseout": //$NON-NLS-0$
+						if (ruler.onMouseOut) { ruler.onMouseOut(lineIndex, e); }
+						break;
+				}
+			}
+		},
+		_handleScroll: function () {
+			var scroll = this._getScroll(false);
+			var oldX = this._hScroll;
+			var oldY = this._vScroll;
+			if (oldX !== scroll.x || oldY !== scroll.y) {
+				this._hScroll = scroll.x;
+				this._vScroll = scroll.y;
+				this._commitIME();
+				this._update(oldY === scroll.y);
+				var e = {
+					type: "Scroll", //$NON-NLS-0$
+					oldValue: {x: oldX, y: oldY},
+					newValue: scroll
+				};
+				this.onScroll(e);
+			}
+		},
+		_handleSelectStart: function (e) {
+			if (this._ignoreSelect) {
+				if (e && e.preventDefault) { e.preventDefault(); }
+				return false;
+			}
+		},
+		_getModelOffset: function(node, offset) {
+			if (!node) { return; }
+			var lineNode;
+			if (node.tagName === "DIV") { //$NON-NLS-0$
+				lineNode = node;
+			} else {
+				lineNode = node.parentNode.parentNode;
+			}
+			var lineOffset = 0;
+			var lineIndex = lineNode.lineIndex;
+			if (node.tagName !== "DIV") { //$NON-NLS-0$
+				var child = lineNode.firstChild;
+				while (child) {
+					var textNode = child.firstChild;
+					if (textNode === node) {
+						if (child.ignoreChars) { lineOffset -= child.ignoreChars; }
+						lineOffset += offset;
+						break;
+					}
+					if (child.ignoreChars) { lineOffset -= child.ignoreChars; }
+					lineOffset += textNode.data.length;
+					child = child.nextSibling;
+				}
+			}
+			return Math.max(0, lineOffset) + this._model.getLineStart(lineIndex);
+		},
+		_updateSelectionFromDOM: function() {
+			var window = this._getWindow();
+			var selection = window.getSelection();
+			var start = this._getModelOffset(selection.anchorNode, selection.anchorOffset);
+			var end = this._getModelOffset(selection.focusNode, selection.focusOffset);
+			if (start === undefined || end === undefined) {
+			    return;
+			}
+			this._setSelection(new Selection(start, end), false, false);
+		},
+		_handleSelectionChange: function (e) {
+			if (this._imeOffset !== -1) {
+				return;
+			}
+			/*
+			 * Feature in Android. The selection handles are hidden when the DOM changes. Sending
+			 * selection events to the application while the user is moving the selection handles
+			 * may hide the handles unexpectedly.  The fix is to delay updating the selection and
+			 * sending the event to the application.
+			 */
+			if (util.isAndroid) {
+				var window = this._getWindow();
+				if (this._selTimer) {
+					window.clearTimeout(this._selTimer);
+				}
+				var that = this;
+				this._selTimer = window.setTimeout(function() {
+					if (!that._clientDiv) { return; }
+					that._selTimer = null; 
+					that._updateSelectionFromDOM();
+				}, 250);
+			} else {
+				this._updateSelectionFromDOM();
+			}
+		},
+		_handleTextInput: function (e) {
+			if (this._ignoreEvent(e)) { return; }
+			this._imeOffset = -1;
+			if (util.isAndroid) {
+				var selection = this._getWindow().getSelection();
+				var temp = selection.anchorNode;
+				while (temp) {
+					if (temp.lineIndex !== undefined) {
+						break;
+					}
+					temp = temp.parentNode;
+				}
+				if (temp) {
+					var model = this._model;
+					var lineIndex = temp.lineIndex;
+					var oldText = model.getLine(lineIndex), text = oldText;
+					var offset = 0;
+					var lineStart = model.getLineStart(lineIndex);
+					if (selection.rangeCount > 0) {
+						selection.getRangeAt(0).deleteContents();
+						var node = temp.ownerDocument.createTextNode(e.data);
+						selection.getRangeAt(0).insertNode(node);
+						var nodeText = this._getDOMText(temp, node);
+						text = nodeText.text;
+						offset = nodeText.offset;
+						node.parentNode.removeChild(node);
+					}
+					temp.lineRemoved = true;
+					
+					var start = 0;
+					while (oldText.charCodeAt(start) === text.charCodeAt(start) && start < offset) {
+						start++;
+					}
+		
+					var end = oldText.length - 1, delta = text.length - oldText.length;
+					while (oldText.charCodeAt(end) === text.charCodeAt(end + delta) && end + delta >= offset + e.data.length) {
+						end--;
+					}
+					end++;
+					
+					var deltaText = text.substring(start, end + delta);
+					start += lineStart;
+					end += lineStart;
+					
+					this._modifyContent({text: deltaText, start: start, end: end, _ignoreDOMSelection: true}, true);
+				}
+			} else {
+				this._doContent(e.data);
+			}
+			e.preventDefault();
+		},
+		_handleTouchStart: function (e) {
+			this._commitIME();
+			var window = this._getWindow();
+			if (this._touchScrollTimer) {
+				this._vScrollDiv.style.display = "none"; //$NON-NLS-0$
+				this._hScrollDiv.style.display = "none"; //$NON-NLS-0$
+				window.clearInterval(this._touchScrollTimer);
+				this._touchScrollTimer = null;
+			}
+			var touches = e.touches;
+			if (touches.length === 1) {
+				var touch = touches[0];
+				var x = touch.clientX, y = touch.clientY;
+				this._touchStartX = x;
+				this._touchStartY = y;
+				if (util.isAndroid) {
+					/*
+					* Bug in Android 4.  The clientX/Y coordinates of the touch events
+					* include the page scrolling offsets.
+					*/
+				    if (y < (touch.pageY - window.pageYOffset) || x < (touch.pageX - window.pageXOffset) ) {
+						x = touch.pageX - window.pageXOffset;
+						y = touch.pageY - window.pageYOffset;
+				    }
+				}
+				var pt = this.convert({x: x, y: y}, "page", "document"); //$NON-NLS-1$ //$NON-NLS-0$
+				this._lastTouchOffset = this.getOffsetAtLocation(pt.x, pt.y);
+				this._touchStartTime = e.timeStamp;
+				this._touching = true;
+			}
+		},
+		_handleTouchMove: function (e) {
+			var touches = e.touches;
+			if (touches.length === 1) {
+				var touch = touches[0];
+				this._touchCurrentX = touch.clientX;
+				this._touchCurrentY = touch.clientY;
+				var interval = 10;
+				if (!this._touchScrollTimer && (e.timeStamp - this._touchStartTime) < (interval*20)) {
+					this._vScrollDiv.style.display = "block"; //$NON-NLS-0$
+					if (!this._wrapMode) {
+						this._hScrollDiv.style.display = "block"; //$NON-NLS-0$
+					}
+					var self = this;
+					var window = this._getWindow();
+					this._touchScrollTimer = window.setInterval(function() {
+						var deltaX = 0, deltaY = 0;
+						if (self._touching) {
+							deltaX = self._touchStartX - self._touchCurrentX;
+							deltaY = self._touchStartY - self._touchCurrentY;
+							self._touchSpeedX = deltaX / interval;
+							self._touchSpeedY = deltaY / interval;
+							self._touchStartX = self._touchCurrentX;
+							self._touchStartY = self._touchCurrentY;
+						} else {
+							if (Math.abs(self._touchSpeedX) < 0.1 && Math.abs(self._touchSpeedY) < 0.1) {
+								self._vScrollDiv.style.display = "none"; //$NON-NLS-0$
+								self._hScrollDiv.style.display = "none"; //$NON-NLS-0$
+								window.clearInterval(self._touchScrollTimer);
+								self._touchScrollTimer = null;
+								return;
+							} else {
+								deltaX = self._touchSpeedX * interval;
+								deltaY = self._touchSpeedY * interval;
+								self._touchSpeedX *= 0.95;
+								self._touchSpeedY *= 0.95;
+							}
+						}
+						self._scrollView(deltaX, deltaY);
+					}, interval);
+				}
+				if (this._touchScrollTimer) {
+					e.preventDefault();
+				}
+			}
+		},
+		_handleTouchEnd: function (e) {
+			var touches = e.touches;
+			if (touches.length === 0) {
+				this._touching = false;
+			}
+		},
+
+		/************************************ Actions ******************************************/
+		_doAction: function (e) {
+			var mode, i;
+			var keyModes = this._keyModes;
+			for (i = keyModes.length - 1 ; i >= 0; i--) {
+				mode = keyModes[i];
+				if (typeof mode.match === "function") { //$NON-NLS-0$
+					var actionID = mode.match(e);
+					if (actionID !== undefined) {
+						return this.invokeAction(actionID);
+					}
+				}
+			}
+			return false;
+		},
+		_doMove: function(args, selection) {
+			var model = this._model;
+			var caret = selection.getCaret();
+			var lineIndex = model.getLineAtOffset(caret);
+			if (!args.count) {
+				args.count = 1;
+			}
+			while (args.count !== 0) {
+				var lineStart = model.getLineStart(lineIndex);
+				if (args.count < 0 && caret === lineStart) {
+					if (lineIndex > 0) {
+						if (args.unit === "character") { //$NON-NLS-0$
+							args.count++;
+						}
+						lineIndex--;
+						selection.extend(model.getLineEnd(lineIndex));
+					} else {
+						break;
+					}
+				} else if (args.count > 0 && caret === model.getLineEnd(lineIndex)) {
+					if (lineIndex + 1 < model.getLineCount()) {
+						if (args.unit === "character") { //$NON-NLS-0$
+							args.count--;
+						}
+						lineIndex++;
+						selection.extend(model.getLineStart(lineIndex));
+					} else {
+						break;
+					}
+				} else {
+					var removeTab = false;
+					if (args.expandTab && args.unit === "character" && (caret - lineStart) % this._tabSize === 0) { //$NON-NLS-0$
+						var lineText = model.getText(lineStart, caret);
+						removeTab = !/[^ ]/.test(lineText); // Only spaces between line start and caret.
+					}
+					if (removeTab) {
+						selection.extend(caret - this._tabSize);
+						args.count += args.count < 0 ? 1 : -1;
+					} else {
+						var line = this._getLine(lineIndex);
+						selection.extend(line.getNextOffset(caret, args));
+						line.destroy();
+					}
+				}
+				caret = selection.getCaret();
+			}
+			return selection;
+		},
+		_doBackspace: function (args) {
+			var selection = this._getSelection();
+			if (selection.isEmpty()) {
+				if (!args.count) {
+					args.count = 1;
+				}
+				args.count *= -1;
+				args.expandTab = this._expandTab;
+				this._doMove(args, selection);
+			}
+			this._modifyContent({text: "", start: selection.start, end: selection.end}, true);
+			return true;
+		},
+		_doCase: function (args) {
+			var selection = this._getSelection();
+			this._doMove(args, selection);
+			var text = this.getText(selection.start, selection.end);
+			this._setSelection(selection, true);
+			switch (args.type) {
+				case "lower": text = text.toLowerCase(); break; //$NON-NLS-0$
+				case "capitalize": text = text.replace(/(?:^|\s)\S/g, function(a) { return a.toUpperCase(); }); break; //$NON-NLS-0$
+				case "reverse":  //$NON-NLS-0$
+					var newText = "";
+					for (var i=0; i<text.length; i++) {
+						var s = text[i];
+						var l = s.toLowerCase();
+						if (l !== s) {
+							s = l;
+						} else {
+							s = s.toUpperCase();
+						}
+						newText += s;
+					} 
+					text = newText;
+					break;
+				default: text = text.toUpperCase(); break;
+			}
+			this._doContent(text);
+			return true;
+		},
+		_doContent: function (text) {
+			var selection = this._getSelection();
+			if (this._overwriteMode && selection.isEmpty()) {
+				var model = this._model;
+				var lineIndex = model.getLineAtOffset(selection.end);
+				if (selection.end < model.getLineEnd(lineIndex)) {
+					var line = this._getLine(lineIndex);
+					selection.extend(line.getNextOffset(selection.getCaret(), {unit:"character", count:1})); //$NON-NLS-0$
+					line.destroy();
+				}
+			}
+			this._modifyContent({text: text, start: selection.start, end: selection.end, _ignoreDOMSelection: true}, true);
+		},
+		_doCopy: function (e) {
+			var selection = this._getSelection();
+			if (!selection.isEmpty()) {
+				var text = this._getBaseText(selection.start, selection.end);
+				return this._setClipboardText(text, e);
+			}
+			return true;
+		},
+		_doCursorNext: function (args) {
+			var selection = this._getSelection();
+			if (!selection.isEmpty() && !args.select) {
+				selection.start = selection.end;
+			} else {
+				this._doMove(args, selection);
+			}
+			if (!args.select) { selection.collapse(); }
+			this._setSelection(selection, true);
+			return true;
+		},
+		_doCursorPrevious: function (args) {
+			var selection = this._getSelection();
+			if (!selection.isEmpty() && !args.select) {
+				selection.end = selection.start;
+			} else {
+				if (!args.count) {
+					args.count = 1;
+				}
+				args.count *= -1;
+				this._doMove(args, selection);
+			}
+			if (!args.select) { selection.collapse(); }
+			this._setSelection(selection, true);
+			return true;
+		},
+		_doCut: function (e) {
+			var selection = this._getSelection();
+			if (!selection.isEmpty()) {
+				var text = this._getBaseText(selection.start, selection.end);
+				this._doContent("");
+				return this._setClipboardText(text, e);
+			}
+			return true;
+		},
+		_doDelete: function (args) {
+			var selection = this._getSelection();
+			if (selection.isEmpty()) {
+				this._doMove(args, selection);
+			}
+			this._modifyContent({text: "", start: selection.start, end: selection.end}, true);
+			return true;
+		},
+		_doEnd: function (args) {
+			var selection = this._getSelection();
+			var model = this._model;
+			var callback;
+			if (args.ctrl) {
+				selection.extend(model.getCharCount());
+				callback = function() {};
+			} else {
+				var offset = selection.getCaret();
+				var lineIndex = model.getLineAtOffset(offset);
+				if (this._wrapMode) {
+					var line = this._getLine(lineIndex);
+					var visualIndex = line.getLineIndex(offset);
+					if (visualIndex === line.getLineCount() - 1) {
+						offset = model.getLineEnd(lineIndex);
+					} else {
+						offset = line.getLineStart(visualIndex + 1) - 1;
+					}
+					line.destroy();
+				} else {
+					if (args.count && args.count > 0) {
+						lineIndex = Math.min (lineIndex  + args.count - 1, model.getLineCount() - 1);
+					}
+					offset = model.getLineEnd(lineIndex);
+				}
+				selection.extend(offset);
+			}
+			if (!args.select) { selection.collapse(); }
+			this._setSelection(selection, true, true, callback);
+			return true;
+		},
+		_doEnter: function (args) {
+			var model = this._model;
+			var selection = this._getSelection();
+			this._doContent(model.getLineDelimiter()); 
+			if (args && args.noCursor) {
+				selection.end = selection.start;
+				this._setSelection(selection, true);
+			}
+			return true;
+		},
+		_doHome: function (args) {
+			var selection = this._getSelection();
+			var model = this._model;
+			var callback;
+			if (args.ctrl) {
+				selection.extend(0);
+				callback = function() {};
+			} else {
+				var offset = selection.getCaret();
+				var lineIndex = model.getLineAtOffset(offset);
+				if (this._wrapMode) {
+					var line = this._getLine(lineIndex);
+					var visualIndex = line.getLineIndex(offset);
+					offset = line.getLineStart(visualIndex);
+					line.destroy();
+				} else {
+					offset = model.getLineStart(lineIndex);
+				}
+				selection.extend(offset); 
+			}
+			if (!args.select) { selection.collapse(); }
+			this._setSelection(selection, true, true, callback);
+			return true;
+		},
+		_doLineDown: function (args) {
+			var model = this._model;
+			var selection = this._getSelection();
+			var caret = selection.getCaret();
+			var lineIndex = model.getLineAtOffset(caret), visualIndex;
+			var line = this._getLine(lineIndex);
+			var x = this._columnX, y = 1, lastLine = false;
+			if (x === -1 || args.wholeLine || (args.select && util.isIE)) {
+				var offset = args.wholeLine ? model.getLineEnd(lineIndex + 1) : caret;
+				x = line.getBoundingClientRect(offset).left;
+			}
+			if ((visualIndex = line.getLineIndex(caret)) < line.getLineCount() - 1) {
+				y = line.getClientRects(visualIndex + 1).top + 1;
+			} else {
+				var lastLineCount = model.getLineCount() - 1;
+				lastLine = lineIndex === lastLineCount;
+				if (args.count && args.count > 0) {
+					lineIndex = Math.min (lineIndex + args.count, lastLineCount);
+				} else {
+					lineIndex++;
+				}
+			}
+			var select = false;
+			if (lastLine) {
+				if (args.select || (util.isMac || util.isLinux)) {
+					selection.extend(model.getCharCount());
+					select = true;
+				}
+			} else {
+				if (line.lineIndex !== lineIndex) {
+					line.destroy();
+					line = this._getLine(lineIndex);
+				}
+				selection.extend(line.getOffset(x, y));
+				select = true;
+			}
+			if (select) {
+				if (!args.select) { selection.collapse(); }
+				this._setSelection(selection, true, true);
+			}
+			this._columnX = x;
+			line.destroy();
+			return true;
+		},
+		_doLineUp: function (args) {
+			var model = this._model;
+			var selection = this._getSelection();
+			var caret = selection.getCaret();
+			var lineIndex = model.getLineAtOffset(caret), visualIndex;
+			var line = this._getLine(lineIndex);
+			var x = this._columnX, firstLine = false, y;
+			if (x === -1 || args.wholeLine || (args.select && util.isIE)) {
+				var offset = args.wholeLine ? model.getLineStart(lineIndex - 1) : caret;
+				x = line.getBoundingClientRect(offset).left;
+			}
+			if ((visualIndex = line.getLineIndex(caret)) > 0) {
+				y = line.getClientRects(visualIndex - 1).top + 1;
+			} else {
+				firstLine = lineIndex === 0;
+				if (!firstLine) {
+					if (args.count && args.count > 0) {
+						lineIndex = Math.max (lineIndex - args.count, 0);
+					} else {
+						lineIndex--;
+					}
+					y = this._getLineHeight(lineIndex) - 1;
+				}
+			}
+			var select = false;
+			if (firstLine) {
+				if (args.select || (util.isMac || util.isLinux)) {
+					selection.extend(0);
+					select = true;
+				}
+			} else {
+				if (line.lineIndex !== lineIndex) {
+					line.destroy();
+					line = this._getLine(lineIndex);
+				}
+				selection.extend(line.getOffset(x, y));
+				select = true;
+			}
+			if (select) {
+				if (!args.select) { selection.collapse(); }
+				this._setSelection(selection, true, true);
+			}
+			this._columnX = x;
+			line.destroy();
+			return true;
+		},
+		_doNoop: function () {
+			return true;
+		},
+		_doPageDown: function (args) {
+			var self = this;
+			var model = this._model;
+			var selection = this._getSelection();
+			var caret = selection.getCaret();
+			var caretLine = model.getLineAtOffset(caret);
+			var lineCount = model.getLineCount();
+			var scroll = this._getScroll();
+			var clientHeight = this._getClientHeight(), x, line;
+			if (this._lineHeight) {
+				x = this._columnX;
+				var caretRect = this._getBoundsAtOffset(caret);
+				if (x === -1 || (args.select && util.isIE)) {
+					x = caretRect.left;
+				}
+				var lineIndex = this._getLineIndex(caretRect.top + clientHeight);
+				line = this._getLine(lineIndex);
+				var linePixel = this._getLinePixel(lineIndex);
+				var y = caretRect.top + clientHeight - linePixel;
+				caret = line.getOffset(x, y);
+				var rect = line.getBoundingClientRect(caret);
+				line.destroy();
+				selection.extend(caret);
+				if (!args.select) { selection.collapse(); }
+				this._setSelection(selection, true, true, function() {
+					self._columnX = x;
+				}, rect.top + linePixel - caretRect.top);
+				return true;
+			}
+			if (caretLine < lineCount - 1) {
+				var lineHeight = this._getLineHeight();
+				var lines = Math.floor(clientHeight / lineHeight);
+				var scrollLines = Math.min(lineCount - caretLine - 1, lines);
+				scrollLines = Math.max(1, scrollLines);
+				x = this._columnX;
+				if (x === -1 || (args.select && util.isIE)) {
+					line = this._getLine(caretLine);
+					x = line.getBoundingClientRect(caret).left;
+					line.destroy();
+				}
+				line = this._getLine(caretLine + scrollLines);
+				selection.extend(line.getOffset(x, 0));
+				line.destroy();
+				if (!args.select) { selection.collapse(); }
+				var verticalMaximum = lineCount * lineHeight;
+				var scrollOffset = scroll.y + scrollLines * lineHeight;
+				if (scrollOffset + clientHeight > verticalMaximum) {
+					scrollOffset = verticalMaximum - clientHeight;
+				}
+				this._setSelection(selection, true, true, function() {
+					self._columnX = x;
+				}, scrollOffset - scroll.y);
+			}
+			return true;
+		},
+		_doPageUp: function (args) {
+			var self = this;
+			var model = this._model;
+			var selection = this._getSelection();
+			var caret = selection.getCaret();
+			var caretLine = model.getLineAtOffset(caret);
+			var scroll = this._getScroll();
+			var clientHeight = this._getClientHeight(), x, line;
+			if (this._lineHeight) {
+				x = this._columnX;
+				var caretRect = this._getBoundsAtOffset(caret);
+				if (x === -1 || (args.select && util.isIE)) {
+					x = caretRect.left;
+				}
+				var lineIndex = this._getLineIndex(caretRect.bottom - clientHeight);
+				line = this._getLine(lineIndex);
+				var linePixel = this._getLinePixel(lineIndex);
+				var y = (caretRect.bottom - clientHeight) - linePixel;
+				caret = line.getOffset(x, y);
+				var rect = line.getBoundingClientRect(caret);
+				line.destroy();
+				selection.extend(caret);
+				if (!args.select) { selection.collapse(); }
+				this._setSelection(selection, true, true, function() {
+					self._columnX = x;
+				}, rect.top + linePixel - caretRect.top);
+				return true;
+			}
+			if (caretLine > 0) {
+				var lineHeight = this._getLineHeight();
+				var lines = Math.floor(clientHeight / lineHeight);
+				var scrollLines = Math.max(1, Math.min(caretLine, lines));
+				x = this._columnX;
+				if (x === -1 || (args.select && util.isIE)) {
+					line = this._getLine(caretLine);
+					x = line.getBoundingClientRect(caret).left;
+					line.destroy();
+				}
+				line = this._getLine(caretLine - scrollLines);
+				selection.extend(line.getOffset(x, this._getLineHeight(caretLine - scrollLines) - 1));
+				line.destroy();
+				if (!args.select) { selection.collapse(); }
+				var scrollOffset = Math.max(0, scroll.y - scrollLines * lineHeight);
+				this._setSelection(selection, true, true, function() {
+					self._columnX = x;
+				}, scrollOffset - scroll.y);
+			}
+			return true;
+		},
+		_doPaste: function(e) {
+			var self = this;
+			var result = this._getClipboardText(e, function(text) {
+				if (text) {
+					if (util.isLinux && self._lastMouseButton === 2) {
+						var timeDiff = new Date().getTime() - self._lastMouseTime;
+						if (timeDiff <= self._clickTime) {
+							self._setSelectionTo(self._lastMouseX, self._lastMouseY);
+						}
+					}
+					self._doContent(text);
+				}
+			});
+			return result !== null;
+		},
+		_doScroll: function (args) {
+			var type = args.type;
+			var model = this._model;
+			var lineCount = model.getLineCount();
+			var clientHeight = this._getClientHeight();
+			var lineHeight = this._getLineHeight();
+			var verticalMaximum = lineCount * lineHeight;
+			var verticalScrollOffset = this._getScroll().y;
+			var pixel;
+			switch (type) {
+				case "textStart": pixel = 0; break; //$NON-NLS-0$
+				case "textEnd": pixel = verticalMaximum - clientHeight; break; //$NON-NLS-0$
+				case "pageDown": pixel = verticalScrollOffset + clientHeight; break; //$NON-NLS-0$
+				case "pageUp": pixel = verticalScrollOffset - clientHeight; break; //$NON-NLS-0$
+				case "lineDown": pixel = verticalScrollOffset + lineHeight; break; //$NON-NLS-0$
+				case "lineUp": pixel = verticalScrollOffset - lineHeight; break; //$NON-NLS-0$
+				case "centerLine": //$NON-NLS-0$
+					var selection = this._getSelection();
+					var lineStart = model.getLineAtOffset(selection.start);
+					var lineEnd = model.getLineAtOffset(selection.end);
+					var selectionHeight = (lineEnd - lineStart + 1) * lineHeight;
+					pixel = (lineStart * lineHeight) - (clientHeight / 2) + (selectionHeight / 2);
+					break;
+			}
+			if (pixel !== undefined) {
+				pixel = Math.min(Math.max(0, pixel), verticalMaximum - clientHeight);
+				this._scrollViewAnimated(0, pixel - verticalScrollOffset, function() {});
+			}
+			return true;
+		},
+		_doSelectAll: function (args) {
+			var model = this._model;
+			var selection = this._getSelection();
+			selection.setCaret(0);
+			selection.extend(model.getCharCount());
+			this._setSelection(selection, false);
+			return true;
+		},
+		_doTab: function (args) {
+			if (!this._tabMode || this._readonly) { return; }
+			var text = "\t"; //$NON-NLS-0$
+			if (this._expandTab) {
+				var model = this._model;
+				var caret = this._getSelection().getCaret();
+				var lineIndex = model.getLineAtOffset(caret);
+				var lineStart = model.getLineStart(lineIndex);
+				var spaces = this._tabSize - ((caret - lineStart) % this._tabSize);
+				text = (new Array(spaces + 1)).join(" "); //$NON-NLS-0$
+			}
+			this._doContent(text);
+			return true;
+		},
+		_doShiftTab: function (args) {
+			if (!this._tabMode || this._readonly) { return; }
+			return true;
+		},
+		_doOverwriteMode: function (args) {
+			if (this._readonly) { return; }
+			this.setOptions({overwriteMode: !this.getOptions("overwriteMode")}); //$NON-NLS-0$
+			return true;
+		},
+		_doTabMode: function (args) {
+			this._tabMode = !this._tabMode;
+			return true;
+		},
+		_doWrapMode: function (args) {
+			this.setOptions({wrapMode: !this.getOptions("wrapMode")}); //$NON-NLS-0$
+			return true;
+		},
+		
+		/************************************ Internals ******************************************/
+		_autoScroll: function () {
+			var model = this._model;
+			var selection = this._getSelection();
+			var pt = this.convert({x: this._autoScrollX, y: this._autoScrollY}, "page", "document"); //$NON-NLS-1$ //$NON-NLS-0$
+			var caret = selection.getCaret();
+			var lineCount = model.getLineCount();
+			var caretLine = model.getLineAtOffset(caret), lineIndex, line;
+			if (this._autoScrollDir === "up" || this._autoScrollDir === "down") { //$NON-NLS-1$ //$NON-NLS-0$
+				var scroll = this._autoScrollY / this._getLineHeight();
+				scroll = scroll < 0 ? Math.floor(scroll) : Math.ceil(scroll);
+				lineIndex = caretLine;
+				lineIndex = Math.max(0, Math.min(lineCount - 1, lineIndex + scroll));
+			} else if (this._autoScrollDir === "left" || this._autoScrollDir === "right") { //$NON-NLS-1$ //$NON-NLS-0$
+				lineIndex = this._getLineIndex(pt.y);
+				line = this._getLine(caretLine); 
+				pt.x += line.getBoundingClientRect(caret, false).left;
+				line.destroy();
+			}
+			if (lineIndex === 0 && (util.isMac || util.isLinux)) {
+				selection.extend(0);
+			} else if (lineIndex === lineCount - 1 && (util.isMac || util.isLinux)) {
+				selection.extend(model.getCharCount());
+			} else {
+				line = this._getLine(lineIndex);
+				selection.extend(line.getOffset(pt.x, pt.y - this._getLinePixel(lineIndex)));
+				line.destroy();
+			}
+			this._setSelection(selection, true);
+		},
+		_autoScrollTimer: function () {
+			this._autoScroll();
+			var self = this;
+			var window = this._getWindow();
+			this._autoScrollTimerID = window.setTimeout(function () {self._autoScrollTimer();}, this._AUTO_SCROLL_RATE);
+		},
+		_calculateLineHeightTimer: function(calculate) {
+			if (!this._lineHeight) { return; }
+			if (this._calculateLHTimer) { return; }
+			var lineCount = this._model.getLineCount(), i = 0;
+			if (calculate) {
+				var c = 0;
+				var MAX_TIME = 100;
+				var start = new Date().getTime(), firstLine = 0;
+				while (i < lineCount) {
+					if (!this._lineHeight[i]) {
+						c++;
+						if (!firstLine) { firstLine = i; }
+						this._lineHeight[i] = this._calculateLineHeight(i);
+					}
+					i++;
+					if ((new Date().getTime() - start) > MAX_TIME) {
+						break;
+					}
+				}
+				this.redrawRulers(0, lineCount);
+				this._queueUpdate();
+			}
+			var window = this._getWindow();
+			if (i !== lineCount) {
+				var self = this;
+				this._calculateLHTimer = window.setTimeout(function() {
+					self._calculateLHTimer = null;
+					self._calculateLineHeightTimer(true);
+				}, 0);
+				return;
+			}
+			if (this._calculateLHTimer) {
+				window.clearTimeout(this._calculateLHTimer);
+				this._calculateLHTimer = undefined;
+			}
+		},
+		_calculateLineHeight: function(lineIndex) {
+			var line = this._getLine(lineIndex);
+			var rect = line.getBoundingClientRect();
+			line.destroy();
+			return Math.max(1, rect.bottom - rect.top);
+		},
+		_calculateMetrics: function() {
+			var parent = this._clientDiv;
+			var document = parent.ownerDocument;
+			var c = " "; //$NON-NLS-0$
+			var line = util.createElement(document, "div"); //$NON-NLS-0$
+			line.style.lineHeight = "normal"; //$NON-NLS-0$
+			var model = this._model;
+			var lineText = model.getLine(0);
+			var e = {type:"LineStyle", textView: this, 0: 0, lineText: lineText, lineStart: 0}; //$NON-NLS-0$
+			this.onLineStyle(e);
+			applyStyle(e.style, line);
+			line.style.position = "fixed"; //$NON-NLS-0$
+			line.style.left = "-1000px"; //$NON-NLS-0$
+			var span1 = util.createElement(document, "span"); //$NON-NLS-0$
+			span1.appendChild(document.createTextNode(c));
+			line.appendChild(span1);
+			var span2 = util.createElement(document, "span"); //$NON-NLS-0$
+			span2.style.fontStyle = "italic"; //$NON-NLS-0$
+			span2.appendChild(document.createTextNode(c));
+			line.appendChild(span2);
+			var span3 = util.createElement(document, "span"); //$NON-NLS-0$
+			span3.style.fontWeight = "bold"; //$NON-NLS-0$
+			span3.appendChild(document.createTextNode(c));
+			line.appendChild(span3);
+			var span4 = util.createElement(document, "span"); //$NON-NLS-0$
+			span4.style.fontWeight = "bold"; //$NON-NLS-0$
+			span4.style.fontStyle = "italic"; //$NON-NLS-0$
+			span4.appendChild(document.createTextNode(c));
+			line.appendChild(span4);
+			parent.appendChild(line);
+			var lineRect = line.getBoundingClientRect();
+			var spanRect1 = span1.getBoundingClientRect();
+			var spanRect2 = span2.getBoundingClientRect();
+			var spanRect3 = span3.getBoundingClientRect();
+			var spanRect4 = span4.getBoundingClientRect();
+			var h1 = spanRect1.bottom - spanRect1.top;
+			var h2 = spanRect2.bottom - spanRect2.top;
+			var h3 = spanRect3.bottom - spanRect3.top;
+			var h4 = spanRect4.bottom - spanRect4.top;
+			var fontStyle = 0;
+			var invalid = (lineRect.bottom - lineRect.top) <= 0;
+			var lineHeight = Math.max(1, lineRect.bottom - lineRect.top);
+			if (h2 > h1) {
+				fontStyle = 1;
+			}
+			if (h3 > h2) {
+				fontStyle = 2;
+			}
+			if (h4 > h3) {
+				fontStyle = 3;
+			}
+			var style;
+			if (fontStyle !== 0) {
+				style = {style: {}};
+				if ((fontStyle & 1) !== 0) {
+					style.style.fontStyle = "italic"; //$NON-NLS-0$
+				}
+				if ((fontStyle & 2) !== 0) {
+					style.style.fontWeight = "bold"; //$NON-NLS-0$
+				}
+			}
+			var trim = getLineTrim(line);
+			parent.removeChild(line);
+			
+			// calculate pad and scroll width
+			var pad = getPadding(this._viewDiv);
+			var div1 = util.createElement(document, "div"); //$NON-NLS-0$
+			div1.style.position = "fixed"; //$NON-NLS-0$
+			div1.style.left = "-1000px"; //$NON-NLS-0$
+			div1.style.paddingLeft = pad.left + "px"; //$NON-NLS-0$
+			div1.style.paddingTop = pad.top + "px"; //$NON-NLS-0$
+			div1.style.paddingRight = pad.right + "px"; //$NON-NLS-0$
+			div1.style.paddingBottom = pad.bottom + "px"; //$NON-NLS-0$
+			div1.style.width = "100px"; //$NON-NLS-0$
+			div1.style.height = "100px"; //$NON-NLS-0$
+			var div2 = util.createElement(document, "div"); //$NON-NLS-0$
+			div2.style.width = "100%"; //$NON-NLS-0$
+			div2.style.height = "100%"; //$NON-NLS-0$
+			div1.appendChild(div2);
+			parent.appendChild(div1);
+			var rect1 = div1.getBoundingClientRect();
+			var rect2 = div2.getBoundingClientRect();
+			div1.style.overflow = 'hidden'; //$NON-NLS-0$
+			div2.style.height = "200px"; //$NON-NLS-0$
+			var w1 = div1.clientWidth;
+			div1.style.overflow = 'scroll'; //$NON-NLS-0$
+			var w2 = div1.clientWidth;
+			parent.removeChild(div1);
+			var scrollWidth = w1 - w2;
+			pad = {
+				left: rect2.left - rect1.left,
+				top: rect2.top - rect1.top,
+				right: rect1.right - rect2.right,
+				bottom: rect1.bottom - rect2.bottom
+			};
+			return {lineHeight: lineHeight, largestFontStyle: style, lineTrim: trim, viewPadding: pad, scrollWidth: scrollWidth, invalid: invalid};
+		},
+		_cancelAnimation: function() {
+			if (this._animation) {
+				this._animation.stop();
+				this._animation = null;
+			}
+		},
+		_clearSelection: function (direction) {
+			var selection = this._getSelection();
+			if (selection.isEmpty()) { return false; }
+			if (direction === "next") { //$NON-NLS-0$
+				selection.start = selection.end;
+			} else {
+				selection.end = selection.start;
+			}
+			this._setSelection(selection, true);
+			return true;
+		},
+		_commitIME: function () {
+			if (this._imeOffset === -1) { return; }
+			// make the state of the IME match the state the view expects it be in
+			// when the view commits the text and IME also need to be committed
+			// this can be accomplished by changing the focus around
+			this._scrollDiv.focus();
+			this._clientDiv.focus();
+			
+			var model = this._model;
+			var lineIndex = model.getLineAtOffset(this._imeOffset);
+			var lineStart = model.getLineStart(lineIndex);
+			var newText = this._getDOMText(this._getLineNode(lineIndex)).text;
+			var oldText = model.getLine(lineIndex);
+			var start = this._imeOffset - lineStart;
+			var end = start + newText.length - oldText.length;
+			if (start !== end) {
+				var insertText = newText.substring(start, end);
+				this._doContent(insertText);
+			}
+			this._imeOffset = -1;
+		},
+		_createActions: function () {
+			this.addKeyMode(new mKeyModes.DefaultKeyMode(this));
+			//1 to 1, no duplicates
+			var self = this;
+			this._actions = {
+				"noop": {defaultHandler: function() {return self._doNoop();}}, //$NON-NLS-0$
+
+				"lineUp": {defaultHandler: function(data) {return self._doLineUp(merge(data,{select: false}));}, actionDescription: {name: messages.lineUp}}, //$NON-NLS-0$
+				"lineDown": {defaultHandler: function(data) {return self._doLineDown(merge(data,{select: false}));}, actionDescription: {name: messages.lineDown}}, //$NON-NLS-0$
+				"lineStart": {defaultHandler: function(data) {return self._doHome(merge(data,{select: false, ctrl:false}));}, actionDescription: {name: messages.lineStart}}, //$NON-NLS-0$
+				"lineEnd": {defaultHandler: function(data) {return self._doEnd(merge(data,{select: false, ctrl:false}));}, actionDescription: {name: messages.lineEnd}}, //$NON-NLS-0$
+				"charPrevious": {defaultHandler: function(data) {return self._doCursorPrevious(merge(data,{select: false, unit:"character"}));}, actionDescription: {name: messages.charPrevious}}, //$NON-NLS-1$ //$NON-NLS-0$
+				"charNext": {defaultHandler: function(data) {return self._doCursorNext(merge(data,{select: false, unit:"character"}));}, actionDescription: {name: messages.charNext}}, //$NON-NLS-1$ //$NON-NLS-0$
+				"pageUp": {defaultHandler: function(data) {return self._doPageUp(merge(data,{select: false}));}, actionDescription: {name: messages.pageUp}}, //$NON-NLS-0$
+				"pageDown": {defaultHandler: function(data) {return self._doPageDown(merge(data,{select: false}));}, actionDescription: {name: messages.pageDown}}, //$NON-NLS-0$
+				"scrollPageUp": {defaultHandler: function(data) {return self._doScroll(merge(data,{type: "pageUp"}));}, actionDescription: {name: messages.scrollPageUp}}, //$NON-NLS-1$ //$NON-NLS-0$
+				"scrollPageDown": {defaultHandler: function(data) {return self._doScroll(merge(data,{type: "pageDown"}));}, actionDescription: {name: messages.scrollPageDown}}, //$NON-NLS-1$ //$NON-NLS-0$
+				"scrollLineUp": {defaultHandler: function(data) {return self._doScroll(merge(data,{type: "lineUp"}));}, actionDescription: {name: messages.scrollLineUp}}, //$NON-NLS-1$ //$NON-NLS-0$
+				"scrollLineDown": {defaultHandler: function(data) {return self._doScroll(merge(data,{type: "lineDown"}));}, actionDescription: {name: messages.scrollLineDown}}, //$NON-NLS-1$ //$NON-NLS-0$
+				"wordPrevious": {defaultHandler: function(data) {return self._doCursorPrevious(merge(data,{select: false, unit:"word"}));}, actionDescription: {name: messages.wordPrevious}}, //$NON-NLS-1$ //$NON-NLS-0$
+				"wordNext": {defaultHandler: function(data) {return self._doCursorNext(merge(data,{select: false, unit:"word"}));}, actionDescription: {name: messages.wordNext}}, //$NON-NLS-1$ //$NON-NLS-0$
+				"textStart": {defaultHandler: function(data) {return self._doHome(merge(data,{select: false, ctrl:true}));}, actionDescription: {name: messages.textStart}}, //$NON-NLS-0$
+				"textEnd": {defaultHandler: function(data) {return self._doEnd(merge(data,{select: false, ctrl:true}));}, actionDescription: {name: messages.textEnd}}, //$NON-NLS-0$
+				"scrollTextStart": {defaultHandler: function(data) {return self._doScroll(merge(data,{type: "textStart"}));}, actionDescription: {name: messages.scrollTextStart}}, //$NON-NLS-1$ //$NON-NLS-0$
+				"scrollTextEnd": {defaultHandler: function(data) {return self._doScroll(merge(data,{type: "textEnd"}));}, actionDescription: {name: messages.scrollTextEnd}}, //$NON-NLS-1$ //$NON-NLS-0$
+				"centerLine": {defaultHandler: function(data) {return self._doScroll(merge(data,{type: "centerLine"}));}, actionDescription: {name: messages.centerLine}}, //$NON-NLS-1$ //$NON-NLS-0$
+				
+				"selectLineUp": {defaultHandler: function(data) {return self._doLineUp(merge(data,{select: true}));}, actionDescription: {name: messages.selectLineUp}}, //$NON-NLS-0$
+				"selectLineDown": {defaultHandler: function(data) {return self._doLineDown(merge(data,{select: true}));}, actionDescription: {name: messages.selectLineDown}}, //$NON-NLS-0$
+				"selectWholeLineUp": {defaultHandler: function(data) {return self._doLineUp(merge(data,{select: true, wholeLine: true}));}, actionDescription: {name: messages.selectWholeLineUp}}, //$NON-NLS-0$
+				"selectWholeLineDown": {defaultHandler: function(data) {return self._doLineDown(merge(data,{select: true, wholeLine: true}));}, actionDescription: {name: messages.selectWholeLineDown}}, //$NON-NLS-0$
+				"selectLineStart": {defaultHandler: function(data) {return self._doHome(merge(data,{select: true, ctrl:false}));}, actionDescription: {name: messages.selectLineStart}}, //$NON-NLS-0$
+				"selectLineEnd": {defaultHandler: function(data) {return self._doEnd(merge(data,{select: true, ctrl:false}));}, actionDescription: {name: messages.selectLineEnd}}, //$NON-NLS-0$
+				"selectCharPrevious": {defaultHandler: function(data) {return self._doCursorPrevious(merge(data,{select: true, unit:"character"}));}, actionDescription: {name: messages.selectCharPrevious}}, //$NON-NLS-1$ //$NON-NLS-0$
+				"selectCharNext": {defaultHandler: function(data) {return self._doCursorNext(merge(data,{select: true, unit:"character"}));}, actionDescription: {name: messages.selectCharNext}}, //$NON-NLS-1$ //$NON-NLS-0$
+				"selectPageUp": {defaultHandler: function(data) {return self._doPageUp(merge(data,{select: true}));}, actionDescription: {name: messages.selectPageUp}}, //$NON-NLS-0$
+				"selectPageDown": {defaultHandler: function(data) {return self._doPageDown(merge(data,{select: true}));}, actionDescription: {name: messages.selectPageDown}}, //$NON-NLS-0$
+				"selectWordPrevious": {defaultHandler: function(data) {return self._doCursorPrevious(merge(data,{select: true, unit:"word"}));}, actionDescription: {name: messages.selectWordPrevious}}, //$NON-NLS-1$ //$NON-NLS-0$
+				"selectWordNext": {defaultHandler: function(data) {return self._doCursorNext(merge(data,{select: true, unit:"word"}));}, actionDescription: {name: messages.selectWordNext}}, //$NON-NLS-1$ //$NON-NLS-0$
+				"selectTextStart": {defaultHandler: function(data) {return self._doHome(merge(data,{select: true, ctrl:true}));}, actionDescription: {name: messages.selectTextStart}}, //$NON-NLS-0$
+				"selectTextEnd": {defaultHandler: function(data) {return self._doEnd(merge(data,{select: true, ctrl:true}));}, actionDescription: {name: messages.selectTextEnd}}, //$NON-NLS-0$
+
+				"deletePrevious": {defaultHandler: function(data) {return self._doBackspace(merge(data,{unit:"character"}));}, actionDescription: {name: messages.deletePrevious}}, //$NON-NLS-1$ //$NON-NLS-0$
+				"deleteNext": {defaultHandler: function(data) {return self._doDelete(merge(data,{unit:"character"}));}, actionDescription: {name: messages.deleteNext}}, //$NON-NLS-1$ //$NON-NLS-0$
+				"deleteWordPrevious": {defaultHandler: function(data) {return self._doBackspace(merge(data,{unit:"word"}));}, actionDescription: {name: messages.deleteWordPrevious}}, //$NON-NLS-1$ //$NON-NLS-0$
+				"deleteWordNext": {defaultHandler: function(data) {return self._doDelete(merge(data,{unit:"word"}));}, actionDescription: {name: messages.deleteWordNext}}, //$NON-NLS-1$ //$NON-NLS-0$
+				"deleteLineStart": {defaultHandler: function(data) {return self._doBackspace(merge(data,{unit: "line"}));}, actionDescription: {name: messages.deleteLineStart}}, //$NON-NLS-1$ //$NON-NLS-0$
+				"deleteLineEnd": {defaultHandler: function(data) {return self._doDelete(merge(data,{unit: "line"}));}, actionDescription: {name: messages.deleteLineEnd}}, //$NON-NLS-1$ //$NON-NLS-0$
+				"tab": {defaultHandler: function(data) {return self._doTab();}, actionDescription: {name: messages.tab}}, //$NON-NLS-0$
+				"shiftTab": {defaultHandler: function(data) {return self._doShiftTab();}, actionDescription: {name: messages.shiftTab}}, //$NON-NLS-0$
+				"enter": {defaultHandler: function(data) {return self._doEnter();}, actionDescription: {name: messages.enter}}, //$NON-NLS-0$
+				"enterNoCursor": {defaultHandler: function(data) {return self._doEnter(merge(data,{noCursor:true}));}, actionDescription: {name: messages.enterNoCursor}}, //$NON-NLS-0$
+				"selectAll": {defaultHandler: function(data) {return self._doSelectAll();}, actionDescription: {name: messages.selectAll}}, //$NON-NLS-0$
+				"copy": {defaultHandler: function(data) {return self._doCopy();}, actionDescription: {name: messages.copy}}, //$NON-NLS-0$
+				"cut": {defaultHandler: function(data) {return self._doCut();}, actionDescription: {name: messages.cut}}, //$NON-NLS-0$
+				"paste": {defaultHandler: function(data) {return self._doPaste();}, actionDescription: {name: messages.paste}}, //$NON-NLS-0$
+				
+				"uppercase": {defaultHandler: function(data) {return self._doCase(merge(data,{type: "upper"}));}, actionDescription: {name: messages.uppercase}}, //$NON-NLS-1$ //$NON-NLS-0$
+				"lowercase": {defaultHandler: function(data) {return self._doCase(merge(data,{type: "lower"}));}, actionDescription: {name: messages.lowercase}}, //$NON-NLS-1$ //$NON-NLS-0$
+				"capitalize": {defaultHandler: function(data) {return self._doCase(merge(data,{unit: "word", type: "capitalize"}));}, actionDescription: {name: messages.capitalize}}, //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+				"reversecase": {defaultHandler: function(data) {return self._doCase(merge(data,{type: "reverse"}));}, actionDescription: {name: messages.reversecase}}, //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+				
+				"toggleOverwriteMode": {defaultHandler: function(data) {return self._doOverwriteMode();}, actionDescription: {name: messages.toggleOverwriteMode}}, //$NON-NLS-0$
+				"toggleTabMode": {defaultHandler: function(data) {return self._doTabMode();}, actionDescription: {name: messages.toggleTabMode}}, //$NON-NLS-0$
+				"toggleWrapMode": {defaultHandler: function(data) {return self._doWrapMode();}, actionDescription: {name: messages.toggleWrapMode}} //$NON-NLS-0$
+			};
+		},
+		_createRuler: function(ruler, index) {
+			if (!this._clientDiv) { return; }
+			var side = ruler.getLocation();
+			var rulerParent = side === "left" ? this._leftDiv : this._rightDiv; //$NON-NLS-0$
+			rulerParent.style.display = "block"; //$NON-NLS-0$
+			var div = util.createElement(rulerParent.ownerDocument, "div"); //$NON-NLS-0$
+			div._ruler = ruler;
+			div.rulerChanged = true;
+			div.style.position = "relative"; //$NON-NLS-0$
+			div.style.cssFloat = "left"; //$NON-NLS-0$
+			div.style.styleFloat = "left"; //$NON-NLS-0$
+			div.style.borderWidth = "0px"; //$NON-NLS-0$
+			div.style.margin = "0px"; //$NON-NLS-0$
+			div.style.padding = "0px"; //$NON-NLS-0$
+			div.style.outline = "none"; //$NON-NLS-0$
+			if (index === undefined || index < 0 || index >= rulerParent.children.length) {
+				rulerParent.appendChild(div);
+			} else {
+				var sibling = rulerParent.firstChild;
+				while (sibling && --index > 0) {
+					sibling = sibling.nextSibling;
+				}
+				rulerParent.insertBefore(div, sibling);
+			}
+		},
+		_createView: function() {
+			if (this._clientDiv) { return; }
+			var parent = this._parent;
+			while (parent.hasChildNodes()) { parent.removeChild(parent.lastChild); }
+
+			var document = parent.ownerDocument;
+			var rootDiv = util.createElement(document, "div"); //$NON-NLS-0$
+			this._rootDiv = rootDiv;
+			rootDiv.tabIndex = -1;
+			rootDiv.style.position = "relative"; //$NON-NLS-0$
+			rootDiv.style.overflow = "hidden"; //$NON-NLS-0$
+			rootDiv.style.width = "100%"; //$NON-NLS-0$
+			rootDiv.style.height = "100%"; //$NON-NLS-0$
+			rootDiv.style.overflow = "hidden"; //$NON-NLS-0$
+			rootDiv.style.WebkitTextSizeAdjust = "100%"; //$NON-NLS-0$
+			rootDiv.setAttribute("role", "application"); //$NON-NLS-1$ //$NON-NLS-0$
+			parent.appendChild(rootDiv);
+			
+			var leftDiv = util.createElement(document, "div"); //$NON-NLS-0$
+			leftDiv.className = "textviewLeftRuler"; //$NON-NLS-0$
+			this._leftDiv = leftDiv;
+			leftDiv.tabIndex = -1;
+			leftDiv.style.overflow = "hidden"; //$NON-NLS-0$
+			leftDiv.style.MozUserSelect = "none"; //$NON-NLS-0$
+			leftDiv.style.WebkitUserSelect = "none"; //$NON-NLS-0$
+			leftDiv.style.position = "absolute"; //$NON-NLS-0$
+			leftDiv.style.top = "0px"; //$NON-NLS-0$
+			leftDiv.style.bottom = "0px"; //$NON-NLS-0$
+			leftDiv.style.cursor = "default"; //$NON-NLS-0$
+			leftDiv.style.display = "none"; //$NON-NLS-0$
+			leftDiv.setAttribute("aria-hidden", "true"); //$NON-NLS-1$ //$NON-NLS-0$
+			rootDiv.appendChild(leftDiv);
+
+			var viewDiv = util.createElement(document, "div"); //$NON-NLS-0$
+			viewDiv.className = "textviewScroll"; //$NON-NLS-0$
+			this._viewDiv = viewDiv;
+			viewDiv.tabIndex = -1;
+			viewDiv.style.overflow = "auto"; //$NON-NLS-0$
+			viewDiv.style.position = "absolute"; //$NON-NLS-0$
+			viewDiv.style.top = "0px"; //$NON-NLS-0$
+			viewDiv.style.bottom = "0px"; //$NON-NLS-0$
+			viewDiv.style.borderWidth = "0px"; //$NON-NLS-0$
+			viewDiv.style.margin = "0px"; //$NON-NLS-0$
+			viewDiv.style.outline = "none"; //$NON-NLS-0$
+			viewDiv.style.background = "transparent"; //$NON-NLS-0$
+			if (util.isMac && util.isWebkit) {
+				viewDiv.style.pointerEvents = "none"; //$NON-NLS-0$
+				viewDiv.style.zIndex = "2"; //$NON-NLS-0$
+			}
+			rootDiv.appendChild(viewDiv);
+			
+			var rightDiv = util.createElement(document, "div"); //$NON-NLS-0$
+			rightDiv.className = "textviewRightRuler"; //$NON-NLS-0$
+			this._rightDiv = rightDiv;
+			rightDiv.tabIndex = -1;
+			rightDiv.style.display = "none"; //$NON-NLS-0$
+			rightDiv.style.overflow = "hidden"; //$NON-NLS-0$
+			rightDiv.style.MozUserSelect = "none"; //$NON-NLS-0$
+			rightDiv.style.WebkitUserSelect = "none"; //$NON-NLS-0$
+			rightDiv.style.position = "absolute"; //$NON-NLS-0$
+			rightDiv.style.top = "0px"; //$NON-NLS-0$
+			rightDiv.style.bottom = "0px"; //$NON-NLS-0$
+			rightDiv.style.cursor = "default"; //$NON-NLS-0$
+			rightDiv.style.right = "0px"; //$NON-NLS-0$
+			rightDiv.setAttribute("aria-hidden", "true"); //$NON-NLS-1$ //$NON-NLS-0$
+			rootDiv.appendChild(rightDiv);
+				
+			var scrollDiv = util.createElement(document, "div"); //$NON-NLS-0$
+			this._scrollDiv = scrollDiv;
+			scrollDiv.style.margin = "0px"; //$NON-NLS-0$
+			scrollDiv.style.borderWidth = "0px"; //$NON-NLS-0$
+			scrollDiv.style.padding = "0px"; //$NON-NLS-0$
+			viewDiv.appendChild(scrollDiv);
+			
+			if (util.isFirefox) {
+				var clipboardDiv = util.createElement(document, "div"); //$NON-NLS-0$
+				this._clipboardDiv = clipboardDiv;
+				clipboardDiv.style.position = "fixed"; //$NON-NLS-0$
+				clipboardDiv.style.whiteSpace = "pre"; //$NON-NLS-0$
+				clipboardDiv.style.left = "-1000px"; //$NON-NLS-0$
+				rootDiv.appendChild(clipboardDiv);
+			}
+
+			if (!util.isIE && !util.isIOS) {
+				var clipDiv = util.createElement(document, "div"); //$NON-NLS-0$
+				this._clipDiv = clipDiv;
+				clipDiv.style.position = "absolute"; //$NON-NLS-0$
+				clipDiv.style.overflow = "hidden"; //$NON-NLS-0$
+				clipDiv.style.margin = "0px"; //$NON-NLS-0$
+				clipDiv.style.borderWidth = "0px"; //$NON-NLS-0$
+				clipDiv.style.padding = "0px"; //$NON-NLS-0$
+				clipDiv.style.background = "transparent"; //$NON-NLS-0$
+				rootDiv.appendChild(clipDiv);
+				
+				var clipScrollDiv = util.createElement(document, "div"); //$NON-NLS-0$
+				this._clipScrollDiv = clipScrollDiv;
+				clipScrollDiv.style.position = "absolute"; //$NON-NLS-0$
+				clipScrollDiv.style.height = "1px"; //$NON-NLS-0$
+				clipScrollDiv.style.top = "-1000px"; //$NON-NLS-0$
+				clipScrollDiv.style.background = "transparent"; //$NON-NLS-0$
+				clipDiv.appendChild(clipScrollDiv);
+			}
+			
+			this._setFullSelection(this._fullSelection, true);
+
+			var clientDiv = util.createElement(document, "div"); //$NON-NLS-0$
+			clientDiv.className = "textviewContent"; //$NON-NLS-0$
+			this._clientDiv = clientDiv;
+			clientDiv.style.position = "absolute"; //$NON-NLS-0$
+			clientDiv.style.borderWidth = "0px"; //$NON-NLS-0$
+			clientDiv.style.margin = "0px"; //$NON-NLS-0$
+			clientDiv.style.padding = "0px"; //$NON-NLS-0$
+			clientDiv.style.outline = "none"; //$NON-NLS-0$
+			clientDiv.style.zIndex = "1"; //$NON-NLS-0$
+			clientDiv.style.WebkitUserSelect = "text"; //$NON-NLS-0$
+			clientDiv.setAttribute("spellcheck", "false"); //$NON-NLS-1$ //$NON-NLS-0$
+			if (util.isIOS || util.isAndroid) {
+				clientDiv.style.WebkitTapHighlightColor = "transparent"; //$NON-NLS-0$
+			}
+			(this._clipDiv || rootDiv).appendChild(clientDiv);
+			
+			if (util.isIOS || util.isAndroid) {
+				var vScrollDiv = util.createElement(document, "div"); //$NON-NLS-0$
+				this._vScrollDiv = vScrollDiv;
+				vScrollDiv.style.position = "absolute"; //$NON-NLS-0$
+				vScrollDiv.style.borderWidth = "1px"; //$NON-NLS-0$
+				vScrollDiv.style.borderColor = "white"; //$NON-NLS-0$
+				vScrollDiv.style.borderStyle = "solid"; //$NON-NLS-0$
+				vScrollDiv.style.borderRadius = "4px"; //$NON-NLS-0$
+				vScrollDiv.style.backgroundColor = "black"; //$NON-NLS-0$
+				vScrollDiv.style.opacity = "0.5"; //$NON-NLS-0$
+				vScrollDiv.style.margin = "0px"; //$NON-NLS-0$
+				vScrollDiv.style.padding = "0px"; //$NON-NLS-0$
+				vScrollDiv.style.outline = "none"; //$NON-NLS-0$
+				vScrollDiv.style.zIndex = "3"; //$NON-NLS-0$
+				vScrollDiv.style.width = "8px"; //$NON-NLS-0$
+				vScrollDiv.style.display = "none"; //$NON-NLS-0$
+				rootDiv.appendChild(vScrollDiv);
+				var hScrollDiv = util.createElement(document, "div"); //$NON-NLS-0$
+				this._hScrollDiv = hScrollDiv;
+				hScrollDiv.style.position = "absolute"; //$NON-NLS-0$
+				hScrollDiv.style.borderWidth = "1px"; //$NON-NLS-0$
+				hScrollDiv.style.borderColor = "white"; //$NON-NLS-0$
+				hScrollDiv.style.borderStyle = "solid"; //$NON-NLS-0$
+				hScrollDiv.style.borderRadius = "4px"; //$NON-NLS-0$
+				hScrollDiv.style.backgroundColor = "black"; //$NON-NLS-0$
+				hScrollDiv.style.opacity = "0.5"; //$NON-NLS-0$
+				hScrollDiv.style.margin = "0px"; //$NON-NLS-0$
+				hScrollDiv.style.padding = "0px"; //$NON-NLS-0$
+				hScrollDiv.style.outline = "none"; //$NON-NLS-0$
+				hScrollDiv.style.zIndex = "3"; //$NON-NLS-0$
+				hScrollDiv.style.height = "8px"; //$NON-NLS-0$
+				hScrollDiv.style.display = "none"; //$NON-NLS-0$
+				rootDiv.appendChild(hScrollDiv);
+			}
+
+			if (util.isFirefox && !clientDiv.setCapture) {
+				var overlayDiv = util.createElement(document, "div"); //$NON-NLS-0$
+				this._overlayDiv = overlayDiv;
+				overlayDiv.style.position = clientDiv.style.position;
+				overlayDiv.style.borderWidth = clientDiv.style.borderWidth;
+				overlayDiv.style.margin = clientDiv.style.margin;
+				overlayDiv.style.padding = clientDiv.style.padding;
+				overlayDiv.style.cursor = "text"; //$NON-NLS-0$
+				overlayDiv.style.zIndex = "2"; //$NON-NLS-0$
+				(this._clipDiv || rootDiv).appendChild(overlayDiv);
+			}
+			clientDiv.contentEditable = "true"; //$NON-NLS-0$
+			clientDiv.setAttribute("role", "textbox"); //$NON-NLS-1$ //$NON-NLS-0$
+			clientDiv.setAttribute("aria-multiline", "true"); //$NON-NLS-1$ //$NON-NLS-0$
+			this._setWrapMode(this._wrapMode, true);
+			this._setReadOnly(this._readonly);
+			this._setThemeClass(this._themeClass, true);
+			this._setTabSize(this._tabSize, true);
+			this._hookEvents();
+			var rulers = this._rulers;
+			for (var i=0; i<rulers.length; i++) {
+				this._createRuler(rulers[i]);
+			}
+			this._update();
+		},
+		_defaultOptions: function() {
+			return {
+				parent: {value: undefined, update: null},
+				model: {value: undefined, update: this.setModel},
+				scrollAnimation: {value: 0, update: null},
+				readonly: {value: false, update: this._setReadOnly},
+				fullSelection: {value: true, update: this._setFullSelection},
+				tabMode: { value: true, update: null },
+				tabSize: {value: 8, update: this._setTabSize},
+				expandTab: {value: false, update: null},
+				overwriteMode: { value: false, update: this._setOverwriteMode },
+				blockCursorVisible: { value: false, update: this._setBlockCursor},
+				wrapMode: {value: false, update: this._setWrapMode},
+				wrappable: {value: false, update: null},
+				theme: {value: mTextTheme.TextTheme.getTheme(), update: this._setTheme},
+				themeClass: {value: undefined, update: this._setThemeClass}
+			};
+		},
+		_destroyRuler: function(ruler) {
+			var side = ruler.getLocation();
+			var rulerParent = side === "left" ? this._leftDiv : this._rightDiv; //$NON-NLS-0$
+			if (rulerParent) {
+				var div = rulerParent.firstChild;
+				while (div) {
+					if (div._ruler === ruler) {
+						div._ruler = undefined;
+						rulerParent.removeChild(div);
+						if (rulerParent.children.length === 0) {
+							rulerParent.style.display = "none"; //$NON-NLS-0$
+						}
+						break;
+					}
+					div = div.nextSibling;
+				}
+			}
+		},
+		_destroyView: function() {
+			var clientDiv = this._clientDiv;
+			if (!clientDiv) { return; }
+			this._setGrab(null);
+			this._unhookEvents();
+
+			/* Destroy timers */
+			var window = this._getWindow();
+			if (this._autoScrollTimerID) {
+				window.clearTimeout(this._autoScrollTimerID);
+				this._autoScrollTimerID = null;
+			}
+			if (this._updateTimer) {
+				window.clearTimeout(this._updateTimer);
+				this._updateTimer = null;
+			}
+			
+			var rootDiv = this._rootDiv;
+			rootDiv.parentNode.removeChild(rootDiv);
+
+			/* Destroy DOM */
+			this._selDiv1 = null;
+			this._selDiv2 = null;
+			this._selDiv3 = null;
+			this._clipboardDiv = null;
+			this._rootDiv = null;
+			this._scrollDiv = null;
+			this._viewDiv = null;
+			this._clipDiv = null;
+			this._clipScrollDiv = null;
+			this._clientDiv = null;
+			this._overlayDiv = null;
+			this._leftDiv = null;
+			this._rightDiv = null;
+			this._vScrollDiv = null;
+			this._hScrollDiv = null;
+		},
+		_doAutoScroll: function (direction, x, y) {
+			this._autoScrollDir = direction;
+			this._autoScrollX = x;
+			this._autoScrollY = y;
+			if (!this._autoScrollTimerID) {
+				this._autoScrollTimer();
+			}
+		},
+		_endAutoScroll: function () {
+			if (this._autoScrollTimerID) {
+				var window = this._getWindow();
+				window.clearTimeout(this._autoScrollTimerID);
+			}
+			this._autoScrollDir = undefined;
+			this._autoScrollTimerID = undefined;
+		},
+		_fixCaret: function() {
+			var clientDiv = this._clientDiv;
+			if (clientDiv) {
+				var hasFocus = this._hasFocus;
+				this._ignoreFocus = true;
+				if (hasFocus) { clientDiv.blur(); }
+				clientDiv.contentEditable = false;
+				clientDiv.contentEditable = true;
+				if (hasFocus) { clientDiv.focus(); }
+				this._ignoreFocus = false;
+			}
+		},
+		_getBaseText: function(start, end) {
+			var model = this._model;
+			/* This is the only case the view access the base model, alternatively the view could use a event to application to customize the text */
+			if (model.getBaseModel) {
+				start = model.mapOffset(start);
+				end = model.mapOffset(end);
+				model = model.getBaseModel();
+			}
+			return model.getText(start, end);
+		},
+		_getBottomIndex: function (fullyVisible) {
+			var child = this._bottomChild;
+			if (fullyVisible && this._getClientHeight() > this._getLineHeight()) {
+				var rect = child.getBoundingClientRect();
+				var clientRect = this._clientDiv.getBoundingClientRect();
+				if (rect.bottom > clientRect.bottom) {
+					child = this._getLinePrevious(child) || child;
+				}
+			}
+			return child.lineIndex;
+		},
+		_getBoundsAtOffset: function(offset) {
+			var model = this._model;
+			var line = this._getLine(model.getLineAtOffset(offset));
+			var result = line.getBoundingClientRect(offset);
+			var linePixel = this._getLinePixel(line.lineIndex);
+			result.top += linePixel;
+			result.bottom += linePixel;
+			line.destroy();
+			return result;
+		},
+		_getClientHeight: function() {
+			var viewPad = this._getViewPadding();
+			return Math.max(0, this._viewDiv.clientHeight - viewPad.top - viewPad.bottom);
+		},
+		_getClientWidth: function() {
+			var viewPad = this._getViewPadding();
+			return Math.max(0, this._viewDiv.clientWidth - viewPad.left - viewPad.right);
+		},
+		_getClipboardText: function (event, handler) {
+			var delimiter = this._model.getLineDelimiter();
+			var clipboadText, text;
+			var window = this._getWindow();
+			if (window.clipboardData) {
+				//IE
+				clipboadText = [];
+				text = window.clipboardData.getData("Text"); //$NON-NLS-0$
+				convertDelimiter(text, function(t) {clipboadText.push(t);}, function() {clipboadText.push(delimiter);});
+				text = clipboadText.join("");
+				if (handler) { handler(text); }
+				return text;
+			}
+			if (util.isFirefox) {
+				this._ignoreFocus = true;
+				var clipboardDiv = this._clipboardDiv;
+				clipboardDiv.innerHTML = "<pre contenteditable=''></pre>"; //$NON-NLS-0$
+				clipboardDiv.firstChild.focus();
+				var self = this;
+				var _getText = function() {
+					var noteText = self._getTextFromElement(clipboardDiv);
+					clipboardDiv.innerHTML = "";
+					clipboadText = [];
+					convertDelimiter(noteText, function(t) {clipboadText.push(t);}, function() {clipboadText.push(delimiter);});
+					return clipboadText.join("");
+				};
+				
+				/* Try execCommand first. Works on firefox with clipboard permission. */
+				var result = false;
+				this._ignorePaste = true;
+
+				/* Do not try execCommand if middle-click is used, because if we do, we get the clipboard text, not the primary selection text. */
+				if (!util.isLinux || this._lastMouseButton !== 2) {
+					try {
+						var document = clipboardDiv.ownerDocument;
+						result = document.execCommand("paste", false, null); //$NON-NLS-0$
+					} catch (ex) {
+						/* Firefox can throw even when execCommand() works, see bug 362835. */
+						result = clipboardDiv.childNodes.length > 1 || clipboardDiv.firstChild && clipboardDiv.firstChild.childNodes.length > 0;
+					}
+				}
+				this._ignorePaste = false;
+				if (!result) {
+					/* Try native paste in DOM, works for firefox during the paste event. */
+					if (event) {
+						window.setTimeout(function() {
+							self.focus();
+							text = _getText();
+							if (text && handler) {
+								handler(text);
+							}
+							self._ignoreFocus = false;
+						}, 0);
+						return null;
+					} else {
+						/* no event and no clipboard permission, paste can't be performed */
+						this.focus();
+						this._ignoreFocus = false;
+						return "";
+					}
+				}
+				this.focus();
+				this._ignoreFocus = false;
+				text = _getText();
+				if (text && handler) {
+					handler(text);
+				}
+				return text;
+			}
+			//webkit
+			if (event && event.clipboardData) {
+				/*
+				* Webkit (Chrome/Safari) allows getData during the paste event
+				* Note: setData is not allowed, not even during copy/cut event
+				*/
+				clipboadText = [];
+				text = event.clipboardData.getData("text/plain"); //$NON-NLS-0$
+				convertDelimiter(text, function(t) {clipboadText.push(t);}, function() {clipboadText.push(delimiter);});
+				text = clipboadText.join("");
+				if (text && handler) {
+					handler(text);
+				}
+				return text;
+			} else {
+				//TODO try paste using extension (Chrome only)
+			}
+			return "";
+		},
+		_getDOMText: function(child, offsetNode) {
+			var lineChild = child.firstChild;
+			var text = "", offset = 0;
+			while (lineChild) {
+				var textNode;
+				if (lineChild.ignore) {
+					lineChild = lineChild.nextSibling;
+					continue;
+				}
+				if (lineChild.ignoreChars) {
+					textNode = lineChild.lastChild;
+					var ignored = 0, childText = [], childOffset = -1;
+					while (textNode) {
+						var data = textNode.data;
+						for (var i = data.length - 1; i >= 0; i--) {
+							var ch = data.substring(i, i + 1);
+							if (ignored < lineChild.ignoreChars && (ch === " " || ch === "\u200C" || ch === "\uFEFF")) { //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+								ignored++;
+							} else {
+								childText.push(ch === "\u00A0" ? "\t" : ch); //$NON-NLS-1$ //$NON-NLS-0$
+							}
+						}
+						if (offsetNode === textNode) {
+							childOffset = childText.length;
+						}
+						textNode = textNode.previousSibling;
+					}
+					childText = childText.reverse().join("");
+					if (childOffset !== -1) {
+						offset = text.length + childText.length - childOffset;
+					}
+					text += childText;
+				} else {
+					textNode = lineChild.firstChild;
+					while (textNode) {
+						if (offsetNode === textNode) {
+							offset = text.length;
+						}
+						text += textNode.data;
+						textNode = textNode.nextSibling;
+					}
+				}
+				lineChild = lineChild.nextSibling;
+			}
+			return {text: text, offset: offset};
+		},
+		_getTextFromElement: function(element) {
+			var document = element.ownerDocument;
+			var window = document.defaultView;
+			if (!window.getSelection) {
+				return element.innerText || element.textContent;
+			}
+
+			var newRange = document.createRange();
+			newRange.selectNode(element);
+
+			var selection = window.getSelection();
+			var oldRanges = [], i;
+			for (i = 0; i < selection.rangeCount; i++) {
+				oldRanges.push(selection.getRangeAt(i));
+			}
+
+			this._ignoreSelect = true;
+			selection.removeAllRanges();
+			selection.addRange(newRange);
+
+			var text = selection.toString();
+
+			selection.removeAllRanges();
+			for (i = 0; i < oldRanges.length; i++) {
+				selection.addRange(oldRanges[i]);
+			}
+
+			this._ignoreSelect = false;
+			return text;
+		},
+		_getViewPadding: function() {
+			return this._metrics.viewPadding;
+		},
+		_getLine: function(lineIndex) {
+			var child = this._getLineNode(lineIndex);
+			if (child && !child.lineChanged && !child.lineRemoved) {
+				return child._line;
+			}
+			return new TextLine(this, lineIndex);
+		},
+		_getLineHeight: function(lineIndex, calculate) {
+			if (lineIndex !== undefined && this._lineHeight) {
+				var lineHeight = this._lineHeight[lineIndex];
+				if (lineHeight) { return lineHeight; }
+				if (calculate || calculate === undefined) {
+					var height = this._lineHeight[lineIndex] = this._calculateLineHeight(lineIndex);
+					return height;
+				}
+			}
+			return this._metrics.lineHeight;
+		},
+		_getLineNode: function (lineIndex) {
+			var clientDiv = this._clientDiv;
+			var child = clientDiv.firstChild;
+			while (child) {
+				if (lineIndex === child.lineIndex) {
+					return child;
+				}
+				child = child.nextSibling;
+			}
+			return undefined;
+		},
+		_getLineNext: function (lineNode) {
+			var node = lineNode ? lineNode.nextSibling : this._clientDiv.firstChild;
+			while (node && node.lineIndex === -1) {
+				node = node.nextSibling;
+			}
+			return node;
+		},
+		_getLinePrevious: function (lineNode) {
+			var node = lineNode ? lineNode.previousSibling : this._clientDiv.lastChild;
+			while (node && node.lineIndex === -1) {
+				node = node.previousSibling;
+			}
+			return node;
+		},
+		_getLinePixel: function(lineIndex) {
+			lineIndex = Math.min(Math.max(0, lineIndex), this._model.getLineCount());
+			if (this._lineHeight) {
+				var topIndex = this._getTopIndex();
+				var pixel = -this._topIndexY + this._getScroll().y, i;
+				if (lineIndex > topIndex) {
+					for (i = topIndex; i < lineIndex; i++) {
+						pixel += this._getLineHeight(i);
+					}
+				} else {
+					for (i = topIndex - 1; i >= lineIndex; i--) {
+						pixel -= this._getLineHeight(i);
+					}
+				}
+				return pixel;
+			}
+			var lineHeight = this._getLineHeight();
+			return lineHeight * lineIndex;
+		},
+		_getLineIndex: function(y) {
+			var lineHeight, lineIndex = 0;
+			var lineCount = this._model.getLineCount();
+			if (this._lineHeight) {
+				lineIndex = this._getTopIndex();
+				var pixel = -this._topIndexY + this._getScroll().y;
+				if (y !== pixel) {
+					if (y < pixel) {
+						while (y < pixel && lineIndex > 0) {
+							y += this._getLineHeight(--lineIndex);
+						}
+					} else {
+						lineHeight = this._getLineHeight(lineIndex);
+						while (y - lineHeight >= pixel && lineIndex < lineCount - 1) {
+							y -= lineHeight;
+							lineHeight = this._getLineHeight(++lineIndex);
+						}
+					}
+				}
+			} else {
+				lineHeight = this._getLineHeight();
+				lineIndex = Math.floor(y / lineHeight);
+			}
+			return Math.max(0, Math.min(lineCount - 1, lineIndex));
+		},
+		_getScroll: function(cancelAnimation) {
+			if (cancelAnimation === undefined || cancelAnimation) {
+				this._cancelAnimation();
+			}
+			var viewDiv = this._viewDiv;
+			return {x: viewDiv.scrollLeft, y: viewDiv.scrollTop};
+		},
+		_getSelection: function () {
+			return this._selection.clone();
+		},
+		_getTopIndex: function (fullyVisible) {
+			var child = this._topChild;
+			if (fullyVisible && this._getClientHeight() > this._getLineHeight()) {
+				var rect = child.getBoundingClientRect();
+				var viewPad = this._getViewPadding();
+				var viewRect = this._viewDiv.getBoundingClientRect();
+				if (rect.top < viewRect.top + viewPad.top) {
+					child = this._getLineNext(child) || child;
+				}
+			}
+			return child.lineIndex;
+		},
+		_hookEvents: function() {
+			var self = this;
+			this._modelListener = {
+				/** @private */
+				onChanging: function(modelChangingEvent) {
+					self._onModelChanging(modelChangingEvent);
+				},
+				/** @private */
+				onChanged: function(modelChangedEvent) {
+					self._onModelChanged(modelChangedEvent);
+				}
+			};
+			this._model.addEventListener("preChanging", this._modelListener.onChanging); //$NON-NLS-0$
+			this._model.addEventListener("postChanged", this._modelListener.onChanged); //$NON-NLS-0$
+			
+			this._themeListener = {
+				onChanged: function(themeChangedEvent) {
+					self._setThemeClass(self._themeClass);
+				}
+			};
+			this._theme.addEventListener("ThemeChanged", this._themeListener.onChanged); //$NON-NLS-0$
+			
+			var handlers = this._handlers = [];
+			var clientDiv = this._clientDiv, viewDiv = this._viewDiv, rootDiv = this._rootDiv;
+			var topNode = this._overlayDiv || clientDiv;
+			var document = clientDiv.ownerDocument;
+			var window = this._getWindow();
+			var grabNode = util.isIE ? document : window;
+			handlers.push({target: window, type: "resize", handler: function(e) { return self._handleResize(e ? e : window.event);}}); //$NON-NLS-0$
+			handlers.push({target: clientDiv, type: "blur", handler: function(e) { return self._handleBlur(e ? e : window.event);}}); //$NON-NLS-0$
+			handlers.push({target: clientDiv, type: "focus", handler: function(e) { return self._handleFocus(e ? e : window.event);}}); //$NON-NLS-0$
+			handlers.push({target: viewDiv, type: "focus", handler: function(e) { clientDiv.focus(); }}); //$NON-NLS-0$
+			handlers.push({target: viewDiv, type: "scroll", handler: function(e) { return self._handleScroll(e ? e : window.event);}}); //$NON-NLS-0$
+			handlers.push({target: clientDiv, type: "textInput", handler: function(e) { return self._handleTextInput(e ? e : window.event); }}); //$NON-NLS-0$
+			handlers.push({target: clientDiv, type: "keydown", handler: function(e) { return self._handleKeyDown(e ? e : window.event);}}); //$NON-NLS-0$
+			handlers.push({target: clientDiv, type: "keypress", handler: function(e) { return self._handleKeyPress(e ? e : window.event);}}); //$NON-NLS-0$
+			handlers.push({target: clientDiv, type: "keyup", handler: function(e) { return self._handleKeyUp(e ? e : window.event);}}); //$NON-NLS-0$
+			handlers.push({target: clientDiv, type: "contextmenu", handler: function(e) { return self._handleContextMenu(e ? e : window.event);}}); //$NON-NLS-0$
+			handlers.push({target: clientDiv, type: "copy", handler: function(e) { return self._handleCopy(e ? e : window.event);}}); //$NON-NLS-0$
+			handlers.push({target: clientDiv, type: "cut", handler: function(e) { return self._handleCut(e ? e : window.event);}}); //$NON-NLS-0$
+			handlers.push({target: clientDiv, type: "paste", handler: function(e) { return self._handlePaste(e ? e : window.event);}}); //$NON-NLS-0$
+			if (util.isIOS || util.isAndroid) {
+				handlers.push({target: document, type: "selectionchange", handler: function(e) { return self._handleSelectionChange(e ? e : window.event); }}); //$NON-NLS-0$
+				handlers.push({target: clientDiv, type: "touchstart", handler: function(e) { return self._handleTouchStart(e ? e : window.event); }}); //$NON-NLS-0$
+				handlers.push({target: clientDiv, type: "touchmove", handler: function(e) { return self._handleTouchMove(e ? e : window.event); }}); //$NON-NLS-0$
+				handlers.push({target: clientDiv, type: "touchend", handler: function(e) { return self._handleTouchEnd(e ? e : window.event); }}); //$NON-NLS-0$
+			} else {
+				handlers.push({target: clientDiv, type: "selectstart", handler: function(e) { return self._handleSelectStart(e ? e : window.event);}}); //$NON-NLS-0$
+				handlers.push({target: clientDiv, type: "mousedown", handler: function(e) { return self._handleMouseDown(e ? e : window.event);}}); //$NON-NLS-0$
+				handlers.push({target: clientDiv, type: "mouseover", handler: function(e) { return self._handleMouseOver(e ? e : window.event);}}); //$NON-NLS-0$
+				handlers.push({target: clientDiv, type: "mouseout", handler: function(e) { return self._handleMouseOut(e ? e : window.event);}}); //$NON-NLS-0$
+				handlers.push({target: grabNode, type: "mouseup", handler: function(e) { return self._handleMouseUp(e ? e : window.event);}}); //$NON-NLS-0$
+				handlers.push({target: grabNode, type: "mousemove", handler: function(e) { return self._handleMouseMove(e ? e : window.event);}}); //$NON-NLS-0$
+				handlers.push({target: rootDiv, type: "mousedown", handler: function(e) { return self._handleRootMouseDown(e ? e : window.event);}}); //$NON-NLS-0$
+				handlers.push({target: rootDiv, type: "mouseup", handler: function(e) { return self._handleRootMouseUp(e ? e : window.event);}}); //$NON-NLS-0$
+				handlers.push({target: topNode, type: "dragstart", handler: function(e) { return self._handleDragStart(e ? e : window.event);}}); //$NON-NLS-0$
+				handlers.push({target: topNode, type: "drag", handler: function(e) { return self._handleDrag(e ? e : window.event);}}); //$NON-NLS-0$
+				handlers.push({target: topNode, type: "dragend", handler: function(e) { return self._handleDragEnd(e ? e : window.event);}}); //$NON-NLS-0$
+				handlers.push({target: topNode, type: "dragenter", handler: function(e) { return self._handleDragEnter(e ? e : window.event);}}); //$NON-NLS-0$
+				handlers.push({target: topNode, type: "dragover", handler: function(e) { return self._handleDragOver(e ? e : window.event);}}); //$NON-NLS-0$
+				handlers.push({target: topNode, type: "dragleave", handler: function(e) { return self._handleDragLeave(e ? e : window.event);}}); //$NON-NLS-0$
+				handlers.push({target: topNode, type: "drop", handler: function(e) { return self._handleDrop(e ? e : window.event);}}); //$NON-NLS-0$
+				handlers.push({target: this._clientDiv, type: util.isFirefox ? "DOMMouseScroll" : "mousewheel", handler: function(e) { return self._handleMouseWheel(e ? e : window.event); }}); //$NON-NLS-1$ //$NON-NLS-0$
+				if (util.isFirefox && (!util.isWindows || util.isFirefox >= 15)) {
+					var MutationObserver = window.MutationObserver || window.MozMutationObserver;
+					if (MutationObserver) {
+						this._mutationObserver = new MutationObserver(function(mutations) { self._handleDataModified(mutations); });
+						this._mutationObserver.observe(clientDiv, {subtree: true, characterData: true});
+					} else {
+						handlers.push({target: this._clientDiv, type: "DOMCharacterDataModified", handler: function (e) { return self._handleDataModified(e ? e : window.event); }}); //$NON-NLS-0$
+					}
+				}
+				if (this._overlayDiv) {
+					handlers.push({target: this._overlayDiv, type: "mousedown", handler: function(e) { return self._handleMouseDown(e ? e : window.event);}}); //$NON-NLS-0$
+					handlers.push({target: this._overlayDiv, type: "mouseover", handler: function(e) { return self._handleMouseOver(e ? e : window.event);}}); //$NON-NLS-0$
+					handlers.push({target: this._overlayDiv, type: "mouseout", handler: function(e) { return self._handleMouseOut(e ? e : window.event);}}); //$NON-NLS-0$
+					handlers.push({target: this._overlayDiv, type: "contextmenu", handler: function(e) { return self._handleContextMenu(e ? e : window.event); }}); //$NON-NLS-0$
+				}
+				if (!this._isW3CEvents) {
+					handlers.push({target: this._clientDiv, type: "dblclick", handler: function(e) { return self._handleDblclick(e ? e : window.event); }}); //$NON-NLS-0$
+				}
+			}
+
+			var leftDiv = this._leftDiv, rightDiv = this._rightDiv;
+			if (util.isIE) {
+				handlers.push({target: leftDiv, type: "selectstart", handler: function() {return false;}}); //$NON-NLS-0$
+			}
+			handlers.push({target: leftDiv, type: util.isFirefox ? "DOMMouseScroll" : "mousewheel", handler: function(e) { return self._handleMouseWheel(e ? e : window.event); }}); //$NON-NLS-1$ //$NON-NLS-0$
+			handlers.push({target: leftDiv, type: "click", handler: function(e) { self._handleRulerEvent(e ? e : window.event); }}); //$NON-NLS-0$
+			handlers.push({target: leftDiv, type: "dblclick", handler: function(e) { self._handleRulerEvent(e ? e : window.event); }}); //$NON-NLS-0$
+			handlers.push({target: leftDiv, type: "mousemove", handler: function(e) { self._handleRulerEvent(e ? e : window.event); }}); //$NON-NLS-0$
+			handlers.push({target: leftDiv, type: "mouseover", handler: function(e) { self._handleRulerEvent(e ? e : window.event); }}); //$NON-NLS-0$
+			handlers.push({target: leftDiv, type: "mouseout", handler: function(e) { self._handleRulerEvent(e ? e : window.event); }}); //$NON-NLS-0$
+			if (util.isIE) {
+				handlers.push({target: rightDiv, type: "selectstart", handler: function() {return false;}}); //$NON-NLS-0$
+			}
+			handlers.push({target: rightDiv, type: util.isFirefox ? "DOMMouseScroll" : "mousewheel", handler: function(e) { return self._handleMouseWheel(e ? e : window.event); }}); //$NON-NLS-1$ //$NON-NLS-0$
+			handlers.push({target: rightDiv, type: "click", handler: function(e) { self._handleRulerEvent(e ? e : window.event); }}); //$NON-NLS-0$
+			handlers.push({target: rightDiv, type: "dblclick", handler: function(e) { self._handleRulerEvent(e ? e : window.event); }}); //$NON-NLS-0$
+			handlers.push({target: rightDiv, type: "mousemove", handler: function(e) { self._handleRulerEvent(e ? e : window.event); }}); //$NON-NLS-0$
+			handlers.push({target: rightDiv, type: "mouseover", handler: function(e) { self._handleRulerEvent(e ? e : window.event); }}); //$NON-NLS-0$
+			handlers.push({target: rightDiv, type: "mouseout", handler: function(e) { self._handleRulerEvent(e ? e : window.event); }}); //$NON-NLS-0$
+			
+			for (var i=0; i<handlers.length; i++) {
+				var h = handlers[i];
+				addHandler(h.target, h.type, h.handler, h.capture);
+			}
+		},
+		_getWindow: function() {
+			return getWindow(this._parent.ownerDocument);
+		},
+		_ignoreEvent: function(e) {
+			var node = e.target;
+			while (node && node !== this._clientDiv) {
+				if (node.ignore) { return true; }
+				node = node.parentNode;
+			}
+			return false;
+		},
+		_init: function(options) {
+			var parent = options.parent;
+			if (typeof(parent) === "string") { //$NON-NLS-0$
+				parent = (options.document || document).getElementById(parent);
+			}
+			if (!parent) { throw "no parent"; } //$NON-NLS-0$
+			options.parent = parent;
+			options.model = options.model || new mTextModel.TextModel();
+			var defaultOptions = this._defaultOptions();
+			for (var option in defaultOptions) {
+				if (defaultOptions.hasOwnProperty(option)) {
+					var value;
+					if (options[option] !== undefined) {
+						value = options[option];
+					} else {
+						value = defaultOptions[option].value;
+					}
+					this["_" + option] = value; //$NON-NLS-0$
+				}
+			}
+			this._keyModes = [];
+			this._rulers = [];
+			this._selection = new Selection (0, 0, false);
+			this._linksVisible = false;
+			this._redrawCount = 0;
+			this._maxLineWidth = 0;
+			this._maxLineIndex = -1;
+			this._ignoreSelect = true;
+			this._ignoreFocus = false;
+			this._hasFocus = false;
+			this._columnX = -1;
+			this._dragOffset = -1;
+			this._isRangeRects = (!util.isIE || util.isIE >= 9) && typeof parent.ownerDocument.createRange().getBoundingClientRect === "function"; //$NON-NLS-0$
+			this._isW3CEvents = parent.addEventListener;
+
+			/* Auto scroll */
+			this._autoScrollX = null;
+			this._autoScrollY = null;
+			this._autoScrollTimerID = null;
+			this._AUTO_SCROLL_RATE = 50;
+			this._grabControl = null;
+			this._moseMoveClosure  = null;
+			this._mouseUpClosure = null;
+			
+			/* Double click */
+			this._lastMouseX = 0;
+			this._lastMouseY = 0;
+			this._lastMouseTime = 0;
+			this._clickCount = 0;
+			this._clickTime = 250;
+			this._clickDist = 5;
+			this._isMouseDown = false;
+			this._doubleClickSelection = null;
+			
+			/* Scroll */
+			this._hScroll = 0;
+			this._vScroll = 0;
+
+			/* IME */
+			this._imeOffset = -1;
+			
+			/* Create elements */
+			this._createActions();
+			this._createView();
+		},
+		_modifyContent: function(e, updateCaret) {
+			if (this._readonly && !e._code) {
+				return;
+			}
+			e.type = "Verify"; //$NON-NLS-0$
+			this.onVerify(e);
+
+			if (e.text === null || e.text === undefined) { return; }
+			
+			var model = this._model;
+			try {
+				if (e._ignoreDOMSelection) { this._ignoreDOMSelection = true; }
+				model.setText (e.text, e.start, e.end);
+			} finally {
+				if (e._ignoreDOMSelection) { this._ignoreDOMSelection = false; }
+			}
+			
+			if (updateCaret) {
+				var selection = this._getSelection ();
+				selection.setCaret(e.start + e.text.length);
+				this._setSelection(selection, true);
+			}
+			this.onModify({type: "Modify"}); //$NON-NLS-0$
+		},
+		_onModelChanged: function(modelChangedEvent) {
+			modelChangedEvent.type = "ModelChanged"; //$NON-NLS-0$
+			this.onModelChanged(modelChangedEvent);
+			modelChangedEvent.type = "Changed"; //$NON-NLS-0$
+			var start = modelChangedEvent.start;
+			var addedCharCount = modelChangedEvent.addedCharCount;
+			var removedCharCount = modelChangedEvent.removedCharCount;
+			var addedLineCount = modelChangedEvent.addedLineCount;
+			var removedLineCount = modelChangedEvent.removedLineCount;
+			var selection = this._getSelection();
+			if (selection.end > start) {
+				if (selection.end > start && selection.start < start + removedCharCount) {
+					// selection intersects replaced text. set caret behind text change
+					selection.setCaret(start + addedCharCount);
+				} else {
+					// move selection to keep same text selected
+					selection.start +=  addedCharCount - removedCharCount;
+					selection.end +=  addedCharCount - removedCharCount;
+				}
+				this._setSelection(selection, false, false);
+			}
+			
+			var model = this._model;
+			var startLine = model.getLineAtOffset(start);
+			var child = this._getLineNext();
+			while (child) {
+				var lineIndex = child.lineIndex;
+				if (startLine <= lineIndex && lineIndex <= startLine + removedLineCount) {
+					if (startLine === lineIndex && !child.modelChangedEvent && !child.lineRemoved) {
+						child.modelChangedEvent = modelChangedEvent;
+						child.lineChanged = true;
+					} else {
+						child.lineRemoved = true;
+						child.lineChanged = false;
+						child.modelChangedEvent = null;
+					}
+				}
+				if (lineIndex > startLine + removedLineCount) {
+					child.lineIndex = lineIndex + addedLineCount - removedLineCount;
+					child._line.lineIndex = child.lineIndex;
+				}
+				child = this._getLineNext(child);
+			}
+			if (this._lineHeight) {
+				var args = [startLine, removedLineCount].concat(new Array(addedLineCount));
+				Array.prototype.splice.apply(this._lineHeight, args);
+			}
+			if (!this._wrapMode) {
+				if (startLine <= this._maxLineIndex && this._maxLineIndex <= startLine + removedLineCount) {
+					this._checkMaxLineIndex = this._maxLineIndex;
+					this._maxLineIndex = -1;
+					this._maxLineWidth = 0;
+				}
+			}
+			this._update();
+		},
+		_onModelChanging: function(modelChangingEvent) {
+			modelChangingEvent.type = "ModelChanging"; //$NON-NLS-0$
+			this.onModelChanging(modelChangingEvent);
+			modelChangingEvent.type = "Changing"; //$NON-NLS-0$
+		},
+		_queueUpdate: function() {
+			if (this._updateTimer || this._ignoreQueueUpdate) { return; }
+			var self = this;
+			var window = this._getWindow();
+			this._updateTimer = window.setTimeout(function() { 
+				self._updateTimer = null;
+				self._update();
+			}, 0);
+		},
+		_resetLineHeight: function(startLine, endLine) {
+			if (this._wrapMode || this._variableLineHeight) {
+				if (startLine !== undefined && endLine !== undefined) {
+					for (var i = startLine; i < endLine; i++) {
+						this._lineHeight[i] = undefined;
+					}
+				} else {
+					this._lineHeight = new Array(this._model.getLineCount());
+				}
+				this._calculateLineHeightTimer();
+			} else {
+				this._lineHeight = null;
+			}
+		},
+		_resetLineWidth: function() {
+			var clientDiv = this._clientDiv;
+			if (clientDiv) {
+				var child = clientDiv.firstChild;
+				while (child) {
+					child.lineWidth = undefined;
+					child = child.nextSibling;
+				}
+			}
+		},
+		_reset: function() {
+			this._maxLineIndex = -1;
+			this._maxLineWidth = 0;
+			this._columnX = -1;
+			this._topChild = null;
+			this._bottomChild = null;
+			this._topIndexY = 0;
+			this._variableLineHeight = false;
+			this._resetLineHeight();
+			this._setSelection(new Selection (0, 0, false), false, false);
+			if (this._viewDiv) {
+				this._viewDiv.scrollLeft = 0;
+				this._viewDiv.scrollTop = 0;
+			}
+			var clientDiv = this._clientDiv;
+			if (clientDiv) {
+				var child = clientDiv.firstChild;
+				while (child) {
+					child.lineRemoved = true;
+					child = child.nextSibling;
+				}
+				/*
+				* Bug in Firefox.  For some reason, the caret does not show after the
+				* view is refreshed.  The fix is to toggle the contentEditable state and
+				* force the clientDiv to loose and receive focus if it is focused.
+				*/
+				if (util.isFirefox) {
+					this._ignoreFocus = false;
+					var hasFocus = this._hasFocus;
+					if (hasFocus) { clientDiv.blur(); }
+					clientDiv.contentEditable = false;
+					clientDiv.contentEditable = true;
+					if (hasFocus) { clientDiv.focus(); }
+					this._ignoreFocus = false;
+				}
+			}
+		},
+		_scrollViewAnimated: function (pixelX, pixelY, callback) {
+			if (callback && this._scrollAnimation) {
+				var self = this;
+				this._animation = new Animation({
+					window: this._getWindow(),
+					duration: this._scrollAnimation,
+					curve: [pixelY, 0],
+					onAnimate: function(x) {
+						var deltaY = pixelY - Math.floor(x);
+						self._scrollView (0, deltaY);
+						pixelY -= deltaY;
+					},
+					onEnd: function() {
+						self._animation = null;
+						self._scrollView (pixelX, pixelY);
+						if (callback) {
+							callback();
+						}
+					}
+				});
+				this._animation.play();
+			} else {
+				this._scrollView (pixelX, pixelY);
+				if (callback) {
+					callback();
+				}
+			}
+		}, 
+		_scrollView: function (pixelX, pixelY) {
+			/*
+			* Always set _ensureCaretVisible to false so that the view does not scroll
+			* to show the caret when scrollView is not called from showCaret().
+			*/
+			this._ensureCaretVisible = false;
+			
+			/*
+			* Scrolling is done only by setting the scrollLeft and scrollTop fields in the
+			* view div. This causes an update from the scroll event. In some browsers 
+			* this event is asynchronous and forcing update page to run synchronously
+			* leads to redraw problems. 
+			* On Chrome 11, the view redrawing at times when holding PageDown/PageUp key.
+			* On Firefox 4 for Linux, the view redraws the first page when holding 
+			* PageDown/PageUp key, but it will not redraw again until the key is released.
+			*/
+			var viewDiv = this._viewDiv;
+			if (pixelX) { viewDiv.scrollLeft += pixelX; }
+			if (pixelY) { viewDiv.scrollTop += pixelY; }
+		},
+		_setClipboardText: function (text, event) {
+			var clipboardText;
+			var document = this._parent.ownerDocument;
+			var window = this._getWindow();
+			if (window.clipboardData) {
+				//IE
+				clipboardText = [];
+				convertDelimiter(text, function(t) {clipboardText.push(t);}, function() {clipboardText.push(util.platformDelimiter);});
+				return window.clipboardData.setData("Text", clipboardText.join("")); //$NON-NLS-0$
+			}
+			if (event && event.clipboardData) {
+				//webkit
+				clipboardText = [];
+				convertDelimiter(text, function(t) {clipboardText.push(t);}, function() {clipboardText.push(util.platformDelimiter);});
+				if (event.clipboardData.setData("text/plain", clipboardText.join(""))) { //$NON-NLS-0$
+					return true;
+				}
+			}
+			var child = util.createElement(document, "pre"); //$NON-NLS-0$
+			child.style.position = "fixed"; //$NON-NLS-0$
+			child.style.left = "-1000px"; //$NON-NLS-0$
+			convertDelimiter(text, 
+				function(t) {
+					child.appendChild(document.createTextNode(t));
+				}, 
+				function() {
+					child.appendChild(util.createElement(document, "br")); //$NON-NLS-0$
+				}
+			);
+			child.appendChild(document.createTextNode(" ")); //$NON-NLS-0$
+			this._clientDiv.appendChild(child);
+			var range = document.createRange();
+			range.setStart(child.firstChild, 0);
+			range.setEndBefore(child.lastChild);
+			var sel = window.getSelection();
+			if (sel.rangeCount > 0) { sel.removeAllRanges(); }
+			sel.addRange(range);
+			var self = this;
+			/** @ignore */
+			var cleanup = function() {
+				if (child && child.parentNode === self._clientDiv) {
+					self._clientDiv.removeChild(child);
+				}
+				self._updateDOMSelection();
+			};
+			var result = false;
+			/* 
+			* Try execCommand first, it works on firefox with clipboard permission,
+			* chrome 5, safari 4.
+			*/
+			this._ignoreCopy = true;
+			try {
+				result = document.execCommand("copy", false, null); //$NON-NLS-0$
+			} catch (e) {}
+			this._ignoreCopy = false;
+			if (!result) {
+				if (event) {
+					window.setTimeout(cleanup, 0);
+					return false;
+				}
+			}
+			/* no event and no permission, copy can not be done */
+			cleanup();
+			return true;
+		},
+		_setDOMSelection: function (startNode, startOffset, endNode, endOffset, startCaret) {
+			var startLineNode, startLineOffset, endLineNode, endLineOffset;
+			var offset = 0;
+			var lineChild = startNode.firstChild;
+			var node, nodeLength, model = this._model;
+			var startLineEnd = model.getLine(startNode.lineIndex).length;
+			while (lineChild) {
+				if (lineChild.ignore) {
+					lineChild = lineChild.nextSibling;
+					continue;
+				}
+				node = lineChild.firstChild;
+				nodeLength = node.length;
+				if (lineChild.ignoreChars) {
+					nodeLength -= lineChild.ignoreChars;
+				}
+				if (offset + nodeLength > startOffset || offset + nodeLength >= startLineEnd) {
+					startLineNode = node;
+					startLineOffset = startOffset - offset;
+					if (lineChild.ignoreChars && nodeLength > 0 && startLineOffset === nodeLength) {
+						startLineOffset += lineChild.ignoreChars; 
+					}
+					break;
+				}
+				offset += nodeLength;
+				lineChild = lineChild.nextSibling;
+			}
+			offset = 0;
+			lineChild = endNode.firstChild;
+			var endLineEnd = this._model.getLine(endNode.lineIndex).length;
+			while (lineChild) {
+				if (lineChild.ignore) {
+					lineChild = lineChild.nextSibling;
+					continue;
+				}
+				node = lineChild.firstChild;
+				nodeLength = node.length;
+				if (lineChild.ignoreChars) {
+					nodeLength -= lineChild.ignoreChars;
+				}
+				if (nodeLength + offset > endOffset || offset + nodeLength >= endLineEnd) {
+					endLineNode = node;
+					endLineOffset = endOffset - offset;
+					if (lineChild.ignoreChars && nodeLength > 0 && endLineOffset === nodeLength) {
+						endLineOffset += lineChild.ignoreChars; 
+					}
+					break;
+				}
+				offset += nodeLength;
+				lineChild = lineChild.nextSibling;
+			}
+			
+			this._setDOMFullSelection(startNode, startOffset, startLineEnd, endNode, endOffset, endLineEnd);
+
+			var range;
+			var window = this._getWindow();
+			var document = this._parent.ownerDocument;
+			if (window.getSelection) {
+				//W3C
+				var sel = window.getSelection();
+				range = document.createRange();
+				range.setStart(startLineNode, startLineOffset);
+				range.setEnd(endLineNode, endLineOffset);
+				if (this._hasFocus && (
+					sel.anchorNode !== startLineNode || sel.anchorOffset !== startLineOffset ||
+					sel.focusNode !== endLineNode || sel.focusOffset !== endLineOffset ||
+					sel.anchorNode !== endLineNode || sel.anchorOffset !== endLineOffset ||
+					sel.focusNode !== startLineNode || sel.focusOffset !== startLineOffset))
+				{
+					this._ignoreSelect = false;
+					if (sel.rangeCount > 0) { sel.removeAllRanges(); }
+					sel.addRange(range);
+					this._ignoreSelect = true;
+				}
+				if (this._cursorDiv) {
+					range = document.createRange();
+					if (startCaret) {
+						range.setStart(startLineNode, startLineOffset);
+						range.setEnd(startLineNode, startLineOffset);
+					} else {
+						range.setStart(endLineNode, endLineOffset);
+						range.setEnd(endLineNode, endLineOffset);
+					}
+					var rect = range.getClientRects()[0];
+					var cursorParent = this._cursorDiv.parentNode;
+					var clientRect = cursorParent.getBoundingClientRect();
+					if (rect && clientRect) {
+						this._cursorDiv.style.top = (rect.top - clientRect.top + cursorParent.scrollTop) + "px"; //$NON-NLS-0$
+						this._cursorDiv.style.left = (rect.left - clientRect.left + cursorParent.scrollLeft) + "px"; //$NON-NLS-0$
+					}
+				}
+			} else if (document.selection) {
+				if (!this._hasFocus) { return; }
+				//IE < 9
+				var body = document.body;
+
+				/*
+				* Bug in IE. For some reason when text is deselected the overflow
+				* selection at the end of some lines does not get redrawn.  The
+				* fix is to create a DOM element in the body to force a redraw.
+				*/
+				var child = util.createElement(document, "div"); //$NON-NLS-0$
+				body.appendChild(child);
+				body.removeChild(child);
+				
+				range = body.createTextRange();
+				range.moveToElementText(startLineNode.parentNode);
+				range.moveStart("character", startLineOffset); //$NON-NLS-0$
+				var endRange = body.createTextRange();
+				endRange.moveToElementText(endLineNode.parentNode);
+				endRange.moveStart("character", endLineOffset); //$NON-NLS-0$
+				range.setEndPoint("EndToStart", endRange); //$NON-NLS-0$
+				this._ignoreSelect = false;
+				range.select();
+				this._ignoreSelect = true;
+			}
+		},
+		_setDOMFullSelection: function(startNode, startOffset, startLineEnd, endNode, endOffset, endLineEnd) {
+			if (!this._selDiv1) { return; }
+			var selDiv = this._selDiv1;
+			selDiv.style.width = "0px"; //$NON-NLS-0$
+			selDiv.style.height = "0px"; //$NON-NLS-0$
+			selDiv = this._selDiv2;
+			selDiv.style.width = "0px"; //$NON-NLS-0$
+			selDiv.style.height = "0px"; //$NON-NLS-0$
+			selDiv = this._selDiv3;
+			selDiv.style.width = "0px"; //$NON-NLS-0$
+			selDiv.style.height = "0px"; //$NON-NLS-0$
+			if (startNode === endNode && startOffset === endOffset) { return; }
+			var model = this._model;
+			var viewPad = this._getViewPadding();
+			var clientRect = this._clientDiv.getBoundingClientRect();
+			var viewRect = this._viewDiv.getBoundingClientRect();
+			var left = viewRect.left + viewPad.left;
+			var right = clientRect.right;
+			var top = viewRect.top + viewPad.top;
+			var bottom = clientRect.bottom;
+			var hd = 0, vd = 0;
+			if (this._clipDiv) {
+				var clipRect = this._clipDiv.getBoundingClientRect();
+				hd = clipRect.left - this._clipDiv.scrollLeft;
+				vd = clipRect.top;
+			} else {
+				var rootpRect = this._rootDiv.getBoundingClientRect();
+				hd = rootpRect.left;
+				vd = rootpRect.top;
+			}
+			this._ignoreDOMSelection = true;
+			var startLine = new TextLine(this, startNode.lineIndex, startNode);
+			var startRect = startLine.getBoundingClientRect(model.getLineStart(startNode.lineIndex) + startOffset, false);
+			var l = startRect.left;
+			var endLine = new TextLine(this, endNode.lineIndex, endNode);
+			var endRect = endLine.getBoundingClientRect(model.getLineStart(endNode.lineIndex) + endOffset, false);
+			var r = endRect.left;
+			this._ignoreDOMSelection = false;
+			var sel1Div = this._selDiv1;
+			var sel1Left = Math.min(right, Math.max(left, l));
+			var sel1Top = Math.min(bottom, Math.max(top, startRect.top));
+			var sel1Right = right;
+			var sel1Bottom = Math.min(bottom, Math.max(top, startRect.bottom));
+			sel1Div.style.left = (sel1Left - hd) + "px"; //$NON-NLS-0$
+			sel1Div.style.top = (sel1Top - vd) + "px"; //$NON-NLS-0$
+			sel1Div.style.width = Math.max(0, sel1Right - sel1Left) + "px"; //$NON-NLS-0$
+			sel1Div.style.height = Math.max(0, sel1Bottom - sel1Top) + "px"; //$NON-NLS-0$
+			if (startRect.top === endRect.top) {
+				sel1Right = Math.min(r, right);
+				sel1Div.style.width = Math.max(0, sel1Right - sel1Left) + "px"; //$NON-NLS-0$
+			} else {
+				var sel3Left = left;
+				var sel3Top = Math.min(bottom, Math.max(top, endRect.top));
+				var sel3Right = Math.min(right, Math.max(left, r));
+				var sel3Bottom = Math.min(bottom, Math.max(top, endRect.bottom));
+				var sel3Div = this._selDiv3;
+				sel3Div.style.left = (sel3Left - hd) + "px"; //$NON-NLS-0$
+				sel3Div.style.top = (sel3Top - vd) + "px"; //$NON-NLS-0$
+				sel3Div.style.width = Math.max(0, sel3Right - sel3Left) + "px"; //$NON-NLS-0$
+				sel3Div.style.height = Math.max(0, sel3Bottom - sel3Top) + "px"; //$NON-NLS-0$
+				if (sel3Top - sel1Bottom > 0) {
+					var sel2Div = this._selDiv2;
+					sel2Div.style.left = (left - hd)  + "px"; //$NON-NLS-0$
+					sel2Div.style.top = (sel1Bottom - vd) + "px"; //$NON-NLS-0$
+					sel2Div.style.width = Math.max(0, right - left) + "px"; //$NON-NLS-0$
+					sel2Div.style.height = Math.max(0, sel3Top - sel1Bottom) + "px"; //$NON-NLS-0$
+				}
+			}
+		},
+		_setGrab: function (target) {
+			if (target === this._grabControl) { return; }
+			if (target) {
+				if (target.setCapture) { target.setCapture(); }
+				this._grabControl = target;
+			} else {
+				if (this._grabControl.releaseCapture) { this._grabControl.releaseCapture(); }
+				this._grabControl = null;
+			}
+		},
+		_setLinksVisible: function(visible) {
+			if (this._linksVisible === visible) { return; }
+			this._linksVisible = visible;
+			/*
+			* Feature in IE.  The client div looses focus and does not regain it back
+			* when the content editable flag is reset. The fix is to remember that it
+			* had focus when the flag is cleared and give focus back to the div when
+			* the flag is set.
+			*/
+			if (util.isIE && visible) {
+				this._hadFocus = this._hasFocus;
+			}
+			var clientDiv = this._clientDiv;
+			clientDiv.contentEditable = !visible;
+			if (this._hadFocus && !visible) {
+				clientDiv.focus();
+			}
+			if (this._overlayDiv) {
+				this._overlayDiv.style.zIndex = visible ? "-1" : "1"; //$NON-NLS-1$ //$NON-NLS-0$
+			}
+			var line = this._getLineNext();
+			while (line) {
+				if (line.hasLink) {
+					var lineChild = line.firstChild;
+					while (lineChild) {
+						if (lineChild.ignore) {
+							lineChild = lineChild.nextSibling;
+							continue;
+						}
+						var next = lineChild.nextSibling;
+						var style = lineChild.viewStyle;
+						if (style && style.tagName && style.tagName.toLowerCase() === "a") { //$NON-NLS-0$
+							line.replaceChild(line._line._createSpan(line, lineChild.firstChild.data, style), lineChild);
+						}
+						lineChild = next;
+					}
+				}
+				line = this._getLineNext(line);
+			}
+			this._updateDOMSelection();
+		},
+		_setSelection: function (selection, scroll, update, callback, pageScroll) {
+			if (selection) {
+				this._columnX = -1;
+				if (update === undefined) { update = true; }
+				var oldSelection = this._selection; 
+				this._selection = selection;
+
+				/* 
+				* Always showCaret(), even when the selection is not changing, to ensure the
+				* caret is visible. Note that some views do not scroll to show the caret during
+				* keyboard navigation when the selection does not chanage. For example, line down
+				* when the caret is already at the last line.
+				*/
+				if (scroll !== false) { /*update = !*/this._showCaret(false, callback, scroll, pageScroll); }
+				
+				/* 
+				* Sometimes the browser changes the selection 
+				* as result of method calls or "leaked" events. 
+				* The fix is to set the visual selection even
+				* when the logical selection is not changed.
+				*/
+				if (update) { this._updateDOMSelection(); }
+				
+				if (!oldSelection.equals(selection)) {
+					var e = {
+						type: "Selection", //$NON-NLS-0$
+						oldValue: {start:oldSelection.start, end:oldSelection.end},
+						newValue: {start:selection.start, end:selection.end}
+					};
+					this.onSelection(e);
+				}
+			}
+		},
+		_setSelectionTo: function (x, y, extent, drag) {
+			var model = this._model, offset;
+			var selection = this._getSelection();
+			var pt = this.convert({x: x, y: y}, "page", "document"); //$NON-NLS-1$ //$NON-NLS-0$
+			var lineIndex = this._getLineIndex(pt.y), line;
+			if (this._clickCount === 1) {
+				line = this._getLine(lineIndex);
+				offset = line.getOffset(pt.x, pt.y - this._getLinePixel(lineIndex));
+				line.destroy();
+				if (drag && !extent) {
+					if (selection.start <= offset && offset < selection.end) {
+						this._dragOffset = offset;
+						return false;
+					}
+				}
+				selection.extend(offset);
+				if (!extent) { selection.collapse(); }
+			} else {
+				var word = (this._clickCount & 1) === 0;
+				var start, end;
+				if (word) {
+					line = this._getLine(lineIndex);
+					offset = line.getOffset(pt.x, pt.y - this._getLinePixel(lineIndex));
+					if (this._doubleClickSelection) {
+						if (offset >= this._doubleClickSelection.start) {
+							start = this._doubleClickSelection.start;
+							end = line.getNextOffset(offset, {unit:"wordend", count:1}); //$NON-NLS-0$
+						} else {
+							start = line.getNextOffset(offset, {unit:"word", count:-1}); //$NON-NLS-0$
+							end = this._doubleClickSelection.end;
+						}
+					} else {
+						start = line.getNextOffset(offset, {unit:"word", count:-1}); //$NON-NLS-0$
+						end = line.getNextOffset(start, {unit:"wordend", count:1}); //$NON-NLS-0$
+					}
+					line.destroy();
+				} else {
+					if (this._doubleClickSelection) {
+						var doubleClickLine = model.getLineAtOffset(this._doubleClickSelection.start);
+						if (lineIndex >= doubleClickLine) {
+							start = model.getLineStart(doubleClickLine);
+							end = model.getLineEnd(lineIndex);
+						} else {
+							start = model.getLineStart(lineIndex);
+							end = model.getLineEnd(doubleClickLine);
+						}
+					} else {
+						start = model.getLineStart(lineIndex);
+						end = model.getLineEnd(lineIndex);
+					}
+				}
+				selection.setCaret(start);
+				selection.extend(end);
+			} 
+			this._setSelection(selection, true, true);
+			return true;
+		},
+		_setFullSelection: function(fullSelection, init) {
+			this._fullSelection = fullSelection;
+			if (util.isWebkit) {
+				this._fullSelection = true;
+			}
+			var parent = this._clipDiv || this._rootDiv;
+			if (!parent) {
+				return;
+			}
+			if (!this._fullSelection) {
+				if (this._selDiv1) {
+					parent.removeChild(this._selDiv1);
+					this._selDiv1 = null;
+				}
+				if (this._selDiv2) {
+					parent.removeChild(this._selDiv2);
+					this._selDiv2 = null;
+				}
+				if (this._selDiv3) {
+					parent.removeChild(this._selDiv3);
+					this._selDiv3 = null;
+				}
+				return;
+			}
+			
+			if (!this._selDiv1 && (this._fullSelection && !util.isIOS)) {
+				var document = parent.ownerDocument;
+				this._highlightRGB = util.isWebkit ? "transparent" : "Highlight"; //$NON-NLS-1$ //$NON-NLS-0$
+				var selDiv1 = util.createElement(document, "div"); //$NON-NLS-0$
+				this._selDiv1 = selDiv1;
+				selDiv1.style.position = "absolute"; //$NON-NLS-0$
+				selDiv1.style.borderWidth = "0px"; //$NON-NLS-0$
+				selDiv1.style.margin = "0px"; //$NON-NLS-0$
+				selDiv1.style.padding = "0px"; //$NON-NLS-0$
+				selDiv1.style.outline = "none"; //$NON-NLS-0$
+				selDiv1.style.background = this._highlightRGB;
+				selDiv1.style.width = "0px"; //$NON-NLS-0$
+				selDiv1.style.height = "0px"; //$NON-NLS-0$
+				selDiv1.style.zIndex = "0"; //$NON-NLS-0$
+				parent.appendChild(selDiv1);
+				var selDiv2 = util.createElement(document, "div"); //$NON-NLS-0$
+				this._selDiv2 = selDiv2;
+				selDiv2.style.position = "absolute"; //$NON-NLS-0$
+				selDiv2.style.borderWidth = "0px"; //$NON-NLS-0$
+				selDiv2.style.margin = "0px"; //$NON-NLS-0$
+				selDiv2.style.padding = "0px"; //$NON-NLS-0$
+				selDiv2.style.outline = "none"; //$NON-NLS-0$
+				selDiv2.style.background = this._highlightRGB;
+				selDiv2.style.width = "0px"; //$NON-NLS-0$
+				selDiv2.style.height = "0px"; //$NON-NLS-0$
+				selDiv2.style.zIndex = "0"; //$NON-NLS-0$
+				parent.appendChild(selDiv2);
+				var selDiv3 = util.createElement(document, "div"); //$NON-NLS-0$
+				this._selDiv3 = selDiv3;
+				selDiv3.style.position = "absolute"; //$NON-NLS-0$
+				selDiv3.style.borderWidth = "0px"; //$NON-NLS-0$
+				selDiv3.style.margin = "0px"; //$NON-NLS-0$
+				selDiv3.style.padding = "0px"; //$NON-NLS-0$
+				selDiv3.style.outline = "none"; //$NON-NLS-0$
+				selDiv3.style.background = this._highlightRGB;
+				selDiv3.style.width = "0px"; //$NON-NLS-0$
+				selDiv3.style.height = "0px"; //$NON-NLS-0$
+				selDiv3.style.zIndex = "0"; //$NON-NLS-0$
+				parent.appendChild(selDiv3);
+				
+				/*
+				* Bug in Firefox. The Highlight color is mapped to list selection
+				* background instead of the text selection background.  The fix
+				* is to map known colors using a table or fallback to light blue.
+				*/
+				if (util.isFirefox && util.isMac) {
+					var window = this._getWindow();
+					var style = window.getComputedStyle(selDiv3, null);
+					var rgb = style.getPropertyValue("background-color"); //$NON-NLS-0$
+					switch (rgb) {
+						case "rgb(119, 141, 168)": rgb = "rgb(199, 208, 218)"; break; //$NON-NLS-1$ //$NON-NLS-0$
+						case "rgb(127, 127, 127)": rgb = "rgb(198, 198, 198)"; break; //$NON-NLS-1$ //$NON-NLS-0$
+						case "rgb(255, 193, 31)": rgb = "rgb(250, 236, 115)"; break; //$NON-NLS-1$ //$NON-NLS-0$
+						case "rgb(243, 70, 72)": rgb = "rgb(255, 176, 139)"; break; //$NON-NLS-1$ //$NON-NLS-0$
+						case "rgb(255, 138, 34)": rgb = "rgb(255, 209, 129)"; break; //$NON-NLS-1$ //$NON-NLS-0$
+						case "rgb(102, 197, 71)": rgb = "rgb(194, 249, 144)"; break; //$NON-NLS-1$ //$NON-NLS-0$
+						case "rgb(140, 78, 184)": rgb = "rgb(232, 184, 255)"; break; //$NON-NLS-1$ //$NON-NLS-0$
+						default: rgb = "rgb(180, 213, 255)"; break; //$NON-NLS-0$
+					}
+					this._highlightRGB = rgb;
+					selDiv1.style.background = rgb;
+					selDiv2.style.background = rgb;
+					selDiv3.style.background = rgb;
+				}
+				if (!init) {
+					this._updateDOMSelection();
+				}
+			}
+		},
+		_setBlockCursor: function (visible) {
+			this._blockCursorVisible = visible;
+			this._updateBlockCursorVisible();
+		},
+		_setOverwriteMode: function (overwrite) {
+			this._overwriteMode = overwrite;
+			this._updateBlockCursorVisible();
+		},
+		_updateBlockCursorVisible: function () {
+			if (this._blockCursorVisible || this._overwriteMode) {
+				if (!this._cursorDiv) {
+					var cursorDiv = util.createElement(document, "div"); //$NON-NLS-0$
+					cursorDiv.className = "textviewBlockCursor"; //$NON-NLS-0$
+					this._cursorDiv = cursorDiv;
+					cursorDiv.tabIndex = -1;
+					cursorDiv.style.zIndex = "2"; //$NON-NLS-0$
+					cursorDiv.style.color = "transparent"; //$NON-NLS-0$
+					cursorDiv.style.position = "absolute"; //$NON-NLS-0$
+					cursorDiv.style.pointerEvents = "none"; //$NON-NLS-0$
+					cursorDiv.innerHTML = "&nbsp;"; //$NON-NLS-0$
+					this._viewDiv.appendChild(cursorDiv);
+					this._updateDOMSelection();
+				}
+			} else {
+				if (this._cursorDiv) {
+					this._cursorDiv.parentNode.removeChild(this._cursorDiv);
+					this._cursorDiv = null;
+				}
+			}
+		},
+		_setReadOnly: function (readOnly) {
+			this._readonly = readOnly;
+			this._clientDiv.setAttribute("aria-readonly", readOnly ? "true" : "false"); //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		},
+		_setTabSize: function (tabSize, init) {
+			this._tabSize = tabSize;
+			this._customTabSize = undefined;
+			var clientDiv = this._clientDiv;
+			if (util.isOpera) {
+				if (clientDiv) { clientDiv.style.OTabSize = this._tabSize+""; }
+			} else if (util.isWebkit >= 537.1) {
+				if (clientDiv) { clientDiv.style.tabSize = this._tabSize+""; }
+			} else if (util.isFirefox >= 4) {
+				if (clientDiv) {  clientDiv.style.MozTabSize = this._tabSize+""; }
+			} else if (this._tabSize !== 8) {
+				this._customTabSize = this._tabSize;
+			}
+			if (!init) {
+				this.redrawLines();
+				this._resetLineWidth();
+			}
+		},
+		_setTheme: function(theme) {
+			if (this._theme) {
+				this._theme.removeEventListener("ThemeChanged", this._themeListener.onChanged); //$NON-NLS-0$
+			}
+			this._theme = theme;
+			if (this._theme) {
+				this._theme.addEventListener("ThemeChanged", this._themeListener.onChanged); //$NON-NLS-0$
+			}
+			this._setThemeClass(this._themeClass);
+		},
+		_setThemeClass: function (themeClass, init) {
+			this._themeClass = themeClass;
+			var viewContainerClass = "textview"; //$NON-NLS-0$
+			var globalThemeClass = this._theme.getThemeClass();
+			if (globalThemeClass) { viewContainerClass += " " + globalThemeClass; } //$NON-NLS-0$
+			if (this._themeClass && globalThemeClass !== this._themeClass) { viewContainerClass += " " + this._themeClass; } //$NON-NLS-0$
+			this._rootDiv.className = viewContainerClass;
+			this._updateStyle(init);
+		},
+		_setWrapMode: function (wrapMode, init) {
+			this._wrapMode = wrapMode && this._wrappable;
+			var clientDiv = this._clientDiv, viewDiv = this._viewDiv;
+			if (wrapMode) {
+				clientDiv.style.whiteSpace = "pre-wrap"; //$NON-NLS-0$
+				clientDiv.style.wordWrap = "break-word"; //$NON-NLS-0$
+				viewDiv.style.overflowX = "hidden"; //$NON-NLS-0$
+				viewDiv.style.overflowY = "scroll"; //$NON-NLS-0$
+			} else {
+				clientDiv.style.whiteSpace = "pre"; //$NON-NLS-0$
+				clientDiv.style.wordWrap = "normal"; //$NON-NLS-0$
+				viewDiv.style.overflowX = "auto"; //$NON-NLS-0$
+				viewDiv.style.overflowY = "auto"; //$NON-NLS-0$
+			}
+			if (!init) {
+				this.redraw();
+				this._resetLineWidth();
+			}
+			this._resetLineHeight();
+		},
+		_showCaret: function (allSelection, callback, scrollAlign, pageScroll) {
+			if (!this._clientDiv) { return; }
+			var model = this._model;
+			var selection = this._getSelection();
+			var scroll = this._getScroll();
+			var caret = selection.getCaret();
+			var start = selection.start;
+			var end = selection.end;
+			var endLine = model.getLineAtOffset(end);
+			var endInclusive = Math.max(Math.max(start, model.getLineStart(endLine)), end - 1);
+			var clientWidth = this._getClientWidth();
+			var clientHeight = this._getClientHeight();
+			var minScroll = clientWidth / 4;
+			var bounds = this._getBoundsAtOffset(caret === start ? start : endInclusive);
+			var left = bounds.left;
+			var right = bounds.right;
+			var top = bounds.top;
+			var bottom = bounds.bottom;
+			if (allSelection && !selection.isEmpty()) {
+				bounds = this._getBoundsAtOffset(caret === end ? start : endInclusive);
+				if (bounds.top === top) {
+					if (caret === start) {
+						right = left + Math.min(bounds.right - left, clientWidth);
+					} else {
+						left = right - Math.min(right - bounds.left, clientWidth);
+					}
+				} else {
+					if (caret === start) {
+						bottom = top + Math.min(bounds.bottom - top, clientHeight);
+					} else {
+						top = bottom - Math.min(bottom - bounds.top, clientHeight);
+					}
+				}
+			}
+			var pixelX = 0;
+			if (left < scroll.x) {
+				pixelX = Math.min(left - scroll.x, -minScroll);
+			}
+			if (right > scroll.x + clientWidth) {
+				pixelX = Math.max(right - scroll.x - clientWidth, minScroll);
+			}
+			var pixelY = 0;
+			if (top < scroll.y) {
+				pixelY = top - scroll.y;
+			} else if (bottom > scroll.y + clientHeight) {
+				pixelY = bottom - scroll.y - clientHeight;
+			}
+			if (pageScroll) {
+				if (pageScroll > 0) {
+					if (pixelY > 0) {
+						pixelY = Math.max(pixelY, pageScroll);
+					}
+				} else {
+					if (pixelY < 0) {
+						pixelY = Math.min(pixelY, pageScroll);
+					}
+				}
+			}
+			if (pixelX !== 0 || pixelY !== 0) {
+				if (pixelY !== 0 && typeof scrollAlign === "number") { //$NON-NLS-0$
+					if (scrollAlign < 0) { scrollAlign = 0; }
+					if (scrollAlign > 1) { scrollAlign = 1; }
+					pixelY += Math.floor(pixelY > 0 ? scrollAlign * clientHeight : -scrollAlign * clientHeight);
+				}
+				this._scrollViewAnimated(pixelX, pixelY, callback);
+				/*
+				* When the view scrolls it is possible that one of the scrollbars can show over the caret.
+				* Depending on the browser scrolling can be synchronous (Safari), in which case the change 
+				* can be detected before showCaret() returns. When scrolling is asynchronous (most browsers), 
+				* the detection is done during the next update page.
+				*/
+				if (clientHeight !== this._getClientHeight() || clientWidth !== this._getClientWidth()) {
+					this._showCaret();
+				} else {
+					this._ensureCaretVisible = true;
+				}
+				return true;
+			} else {
+				if (callback) {
+					callback();
+				}
+			}
+			return false;
+		},
+		_startIME: function () {
+			if (this._imeOffset !== -1) { return; }
+			var selection = this._getSelection();
+			if (!selection.isEmpty()) {
+				this._modifyContent({text: "", start: selection.start, end: selection.end}, true);
+			}
+			this._imeOffset = selection.start;
+		},
+		_unhookEvents: function() {
+			this._model.removeEventListener("preChanging", this._modelListener.onChanging); //$NON-NLS-0$
+			this._model.removeEventListener("postChanged", this._modelListener.onChanged); //$NON-NLS-0$
+			this._theme.removeEventListener("ThemeChanged", this._themeListener.onChanged); //$NON-NLS-0$
+			this._modelListener = null;
+			for (var i=0; i<this._handlers.length; i++) {
+				var h = this._handlers[i];
+				removeHandler(h.target, h.type, h.handler);
+			}
+			this._handlers = null;
+			if (this._mutationObserver) {
+				this._mutationObserver.disconnect();
+				this._mutationObserver = null;
+			}
+		},
+		_updateDOMSelection: function () {
+			if (this._ignoreDOMSelection) { return; }
+			if (!this._clientDiv) { return; }
+			var selection = this._getSelection();
+			var model = this._model;
+			var startLine = model.getLineAtOffset(selection.start);
+			var endLine = model.getLineAtOffset(selection.end);
+			var firstNode = this._getLineNext();
+			/*
+			* Bug in Firefox. For some reason, after a update page sometimes the 
+			* firstChild returns null incorrectly. The fix is to ignore show selection.
+			*/
+			if (!firstNode) { return; }
+			var lastNode = this._getLinePrevious();
+			
+			var topNode, bottomNode, topOffset, bottomOffset;
+			if (startLine < firstNode.lineIndex) {
+				topNode = firstNode;
+				topOffset = 0;
+			} else if (startLine > lastNode.lineIndex) {
+				topNode = lastNode;
+				topOffset = 0;
+			} else {
+				topNode = this._getLineNode(startLine);
+				topOffset = selection.start - model.getLineStart(startLine);
+			}
+
+			if (endLine < firstNode.lineIndex) {
+				bottomNode = firstNode;
+				bottomOffset = 0;
+			} else if (endLine > lastNode.lineIndex) {
+				bottomNode = lastNode;
+				bottomOffset = 0;
+			} else {
+				bottomNode = this._getLineNode(endLine);
+				bottomOffset = selection.end - model.getLineStart(endLine);
+			}
+			this._setDOMSelection(topNode, topOffset, bottomNode, bottomOffset, selection.caret);
+		},
+		_update: function(hScrollOnly) {
+			if (this._redrawCount > 0) { return; }
+			if (this._updateTimer) {
+				var window = this._getWindow();
+				window.clearTimeout(this._updateTimer);
+				this._updateTimer = null;
+				hScrollOnly = false;
+			}
+			var clientDiv = this._clientDiv;
+			if (!clientDiv) { return; }
+			if (this._metrics.invalid) {
+				this._ignoreQueueUpdate = true;
+				this._updateStyle();
+				this._ignoreQueueUpdate = false;
+			}
+			var model = this._model;
+			var scroll = this._getScroll(false);
+			var viewPad = this._getViewPadding();
+			var lineCount = model.getLineCount();
+			var lineHeight = this._getLineHeight();
+			var clientWidth = this._getClientWidth();
+			if (this._wrapMode) {
+				clientDiv.style.width = clientWidth + "px"; //$NON-NLS-0$
+			}
+			
+			/*
+			* topIndex - top line index of the view (maybe be particialy visible)
+			* lineStart - top line minus one line (if any)
+			* topIndexY - portion of the top line that is NOT visible.
+			* top - topIndexY plus height of the line before top line (if any)
+			*/
+			var topIndex, lineStart, top, topIndexY,
+				leftWidth, leftRect,
+				clientHeight, scrollWidth, scrollHeight,
+				totalHeight = 0, totalLineIndex = 0, tempLineHeight;
+			if (this._lineHeight) {
+				while (totalLineIndex < lineCount) {
+					tempLineHeight = this._getLineHeight(totalLineIndex);
+					if (totalHeight + tempLineHeight > scroll.y) {
+						break;
+					}
+					totalHeight += tempLineHeight;
+					totalLineIndex++;
+				}
+				topIndex = totalLineIndex;
+				lineStart = Math.max(0, topIndex - 1);
+				topIndexY = top = scroll.y - totalHeight;
+				if (topIndex > 0) {
+					top += this._getLineHeight(topIndex - 1);
+				}
+			} else {
+				var firstLine = Math.max(0, scroll.y) / lineHeight;
+				topIndex = Math.floor(firstLine);
+				lineStart = Math.max(0, topIndex - 1);
+				top = Math.round((firstLine - lineStart) * lineHeight);
+				topIndexY = Math.round((firstLine - topIndex) * lineHeight);
+				scrollHeight = lineCount * lineHeight;
+			}
+			this._topIndexY = topIndexY;
+			var parent = this._parent;
+			var parentWidth = parent.clientWidth;
+			var parentHeight = parent.clientHeight;
+			clientHeight = this._getClientHeight();
+			if (hScrollOnly) {
+				leftWidth = 0;
+				if (this._leftDiv) {
+					leftRect = this._leftDiv.getBoundingClientRect();
+					leftWidth = leftRect.right - leftRect.left;
+				}
+				scrollWidth = clientWidth;
+				if (!this._wrapMode) {
+					scrollWidth = Math.max(this._maxLineWidth, scrollWidth);
+				}
+				while (totalLineIndex < lineCount) {
+					tempLineHeight = this._getLineHeight(totalLineIndex, false);
+					totalHeight += tempLineHeight;
+					totalLineIndex++;
+				}
+				scrollHeight = totalHeight;
+			} else {
+
+				var viewDiv = this._viewDiv;
+				var linesPerPage = Math.floor((clientHeight + topIndexY) / lineHeight);
+				var bottomIndex = Math.min(topIndex + linesPerPage, lineCount - 1);
+				var lineEnd = Math.min(bottomIndex + 1, lineCount - 1);
+				
+				var lineIndex, lineWidth;
+				var child = clientDiv.firstChild;
+				while (child) {
+					lineIndex = child.lineIndex;
+					var nextChild = child.nextSibling;
+					if (!(lineStart <= lineIndex && lineIndex <= lineEnd) || child.lineRemoved || child.lineIndex === -1) {
+						if (this._mouseWheelLine === child) {
+							child.style.display = "none"; //$NON-NLS-0$
+							child.lineIndex = -1;
+						} else {
+							clientDiv.removeChild(child);
+						}
+					}
+					child = nextChild;
+				}
+	
+				child = this._getLineNext();
+				var document = viewDiv.ownerDocument;
+				var frag = document.createDocumentFragment();
+				for (lineIndex=lineStart; lineIndex<=lineEnd; lineIndex++) {
+					if (!child || child.lineIndex > lineIndex) {
+						new TextLine(this, lineIndex).create(frag, null);
+					} else {
+						if (frag.firstChild) {
+							clientDiv.insertBefore(frag, child);
+							frag = document.createDocumentFragment();
+						}
+						if (child && child.lineChanged) {
+							child = new TextLine(this, lineIndex).create(frag, child);
+							child.lineChanged = false;
+						}
+						child = this._getLineNext(child);
+					}
+				}
+				if (frag.firstChild) { clientDiv.insertBefore(frag, child); }
+	
+				/*
+				* Feature in WekKit. Webkit limits the width of the lines
+				* computed below to the width of the client div.  This causes
+				* the lines to be wrapped even though "pre" is set.  The fix
+				* is to set the width of the client div to "0x7fffffffpx"
+				* before computing the lines width.  Note that this value is
+				* reset to the appropriate value further down.
+				*/ 
+				if (util.isWebkit && !this._wrapMode) {
+					clientDiv.style.width = "0x7fffffffpx"; //$NON-NLS-0$
+				}
+	
+				var rect;
+				child = this._getLineNext();
+				var bottomHeight = clientHeight + top;
+				var foundBottomIndex = false;
+				while (child) {
+					lineWidth = child.lineWidth;
+					if (lineWidth === undefined) {
+						rect = child._line.getBoundingClientRect();
+						lineWidth = child.lineWidth = Math.ceil(rect.right - rect.left);
+						var lh = rect.bottom - rect.top;
+						if (this._lineHeight) {
+							this._lineHeight[child.lineIndex] = lh;
+						} else if (lineHeight !== 0 && lh !== 0 && Math.ceil(lineHeight) !== Math.ceil(lh)) {
+							this._variableLineHeight = true;
+							this._lineHeight = [];
+							this._lineHeight[child.lineIndex] = lh;
+						}
+					}
+					if (this._lineHeight && !foundBottomIndex) {
+						bottomHeight -= this._lineHeight[child.lineIndex];
+						if (bottomHeight < 0) {
+							bottomIndex = child.lineIndex;
+							foundBottomIndex = true;
+						}
+					}
+					if (!this._wrapMode) {
+						if (lineWidth >= this._maxLineWidth) {
+							this._maxLineWidth = lineWidth;
+							this._maxLineIndex = child.lineIndex;
+						}
+						if (this._checkMaxLineIndex === child.lineIndex) { this._checkMaxLineIndex = -1; }
+					}
+					if (child.lineIndex === topIndex) { this._topChild = child; }
+					if (child.lineIndex === bottomIndex) { this._bottomChild = child; }
+					child = this._getLineNext(child);
+				}
+				if (this._checkMaxLineIndex !== -1) {
+					lineIndex = this._checkMaxLineIndex;
+					this._checkMaxLineIndex = -1;
+					if (0 <= lineIndex && lineIndex < lineCount) {
+						var line = new TextLine(this, lineIndex);
+						rect = line.getBoundingClientRect();
+						lineWidth = rect.right - rect.left;
+						if (lineWidth >= this._maxLineWidth) {
+							this._maxLineWidth = lineWidth;
+							this._maxLineIndex = lineIndex;
+						}
+						line.destroy();
+					}
+				}
+				
+				while (totalLineIndex < lineCount) {
+					tempLineHeight = this._getLineHeight(totalLineIndex, totalLineIndex <= bottomIndex);
+					totalHeight += tempLineHeight;
+					totalLineIndex++;
+				}
+				scrollHeight = totalHeight;
+	
+				// Update rulers
+				this._updateRuler(this._leftDiv, topIndex, lineEnd, parentHeight);
+				this._updateRuler(this._rightDiv, topIndex, lineEnd, parentHeight);
+				
+				leftWidth = 0;
+				if (this._leftDiv) {
+					leftRect = this._leftDiv.getBoundingClientRect();
+					leftWidth = leftRect.right - leftRect.left;
+				}
+				var rightWidth = 0;
+				if (this._rightDiv) {
+					var rightRect = this._rightDiv.getBoundingClientRect();
+					rightWidth = rightRect.right - rightRect.left;
+				}
+				viewDiv.style.left = leftWidth + "px"; //$NON-NLS-0$
+				viewDiv.style.right = rightWidth + "px"; //$NON-NLS-0$
+
+				/* Need to set the height first in order for the width to consider the vertical scrollbar */
+				var scrollDiv = this._scrollDiv;
+				scrollDiv.style.height = scrollHeight + "px"; //$NON-NLS-0$
+				/*
+				* TODO if frameHeightWithoutHScrollbar < scrollHeight  < frameHeightWithHScrollbar and the horizontal bar is visible, 
+				* then the clientWidth is wrong because the vertical scrollbar is showing. To correct code should hide both scrollbars 
+				* at this point.
+				*/
+				clientWidth = this._getClientWidth();
+				var width = clientWidth;
+				if (!this._wrapMode) {
+					width = Math.max(this._maxLineWidth, width);
+				}
+				/*
+				* Except by IE 8 and earlier, all other browsers are not allocating enough space for the right padding 
+				* in the scrollbar. It is possible this a bug since all other paddings are considered.
+				*/
+				scrollWidth = width;
+				if ((!util.isIE || util.isIE >= 9) && this._maxLineWidth > clientWidth) { width += viewPad.right + viewPad.left; }
+				scrollDiv.style.width = width + "px"; //$NON-NLS-0$
+				if (this._clipScrollDiv) {
+					this._clipScrollDiv.style.width = width + "px"; //$NON-NLS-0$
+				}
+				/* Get the left scroll after setting the width of the scrollDiv as this can change the horizontal scroll offset. */
+				scroll = this._getScroll(false);
+			}
+			if (this._vScrollDiv) {
+				var trackHeight = clientHeight - 8;
+				var thumbHeight = Math.max(15, Math.ceil(Math.min(1, trackHeight / (scrollHeight + viewPad.top + viewPad.bottom)) * trackHeight));
+				this._vScrollDiv.style.left = (leftWidth + clientWidth - 8) + "px"; //$NON-NLS-0$
+				this._vScrollDiv.style.top = Math.floor(Math.max(0, (scroll.y * trackHeight / scrollHeight))) + "px"; //$NON-NLS-0$
+				this._vScrollDiv.style.height = thumbHeight + "px"; //$NON-NLS-0$
+			}
+			if (!this._wrapMode && this._hScrollDiv) {
+				var trackWidth = clientWidth - 8;
+				var thumbWidth = Math.max(15, Math.ceil(Math.min(1, trackWidth / (this._maxLineWidth + viewPad.left + viewPad.right)) * trackWidth));
+				this._hScrollDiv.style.left = leftWidth + Math.floor(Math.max(0, Math.floor(scroll.x * trackWidth / this._maxLineWidth))) + "px"; //$NON-NLS-0$
+				this._hScrollDiv.style.top = (clientHeight - 9) + "px"; //$NON-NLS-0$
+				this._hScrollDiv.style.width = thumbWidth + "px"; //$NON-NLS-0$
+			}
+			var left = scroll.x;	
+			var clipDiv = this._clipDiv;
+			var overlayDiv = this._overlayDiv;
+			var clipLeft, clipTop;
+			if (clipDiv) {
+				clipDiv.scrollLeft = left;
+				clipDiv.scrollTop = 0;
+				clipLeft = leftWidth + viewPad.left;
+				clipTop = viewPad.top;
+				var clipWidth = clientWidth;
+				var clipHeight = clientHeight;
+				var clientLeft = 0, clientTop = -top;
+				if (scroll.x === 0) {
+					clipLeft -= viewPad.left;
+					clipWidth += viewPad.left;
+					clientLeft = viewPad.left;
+				} 
+				if (scroll.x + clientWidth === scrollWidth) {
+					clipWidth += viewPad.right;
+				}
+				if (scroll.y === 0) {
+					clipTop -= viewPad.top;
+					clipHeight += viewPad.top;
+					clientTop += viewPad.top;
+				}
+				if (scroll.y + clientHeight === scrollHeight) { 
+					clipHeight += viewPad.bottom; 
+				}
+				clipDiv.style.left = clipLeft + "px"; //$NON-NLS-0$
+				clipDiv.style.top = clipTop + "px"; //$NON-NLS-0$
+				clipDiv.style.right = (parentWidth - clipWidth - clipLeft) + "px"; //$NON-NLS-0$
+				clipDiv.style.bottom = (parentHeight - clipHeight - clipTop) + "px"; //$NON-NLS-0$
+				clientDiv.style.left = clientLeft + "px"; //$NON-NLS-0$
+				clientDiv.style.top = clientTop + "px"; //$NON-NLS-0$
+				clientDiv.style.width = scrollWidth + "px"; //$NON-NLS-0$
+				clientDiv.style.height = (clientHeight + top) + "px"; //$NON-NLS-0$
+				if (overlayDiv) {
+					overlayDiv.style.left = clientDiv.style.left;
+					overlayDiv.style.top = clientDiv.style.top;
+					overlayDiv.style.width = clientDiv.style.width;
+					overlayDiv.style.height = clientDiv.style.height;
+				}
+			} else {
+				clipLeft = left;
+				clipTop = top;
+				var clipRight = left + clientWidth;
+				var clipBottom = top + clientHeight;
+				if (clipLeft === 0) { clipLeft -= viewPad.left; }
+				if (clipTop === 0) { clipTop -= viewPad.top; }
+				if (clipRight === scrollWidth) { clipRight += viewPad.right; }
+				if (scroll.y + clientHeight === scrollHeight) { clipBottom += viewPad.bottom; }
+				clientDiv.style.clip = "rect(" + clipTop + "px," + clipRight + "px," + clipBottom + "px," + clipLeft + "px)"; //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+				clientDiv.style.left = (-left + leftWidth + viewPad.left) + "px"; //$NON-NLS-0$
+				clientDiv.style.width = (util.isWebkit ? scrollWidth : clientWidth + left) + "px"; //$NON-NLS-0$
+				if (!hScrollOnly) {
+					clientDiv.style.top = (-top + viewPad.top) + "px"; //$NON-NLS-0$
+					clientDiv.style.height = (clientHeight + top) + "px"; //$NON-NLS-0$
+				}
+				if (overlayDiv) {
+					overlayDiv.style.clip = clientDiv.style.clip;
+					overlayDiv.style.left = clientDiv.style.left;
+					overlayDiv.style.width = clientDiv.style.width;
+					if (!hScrollOnly) {
+						overlayDiv.style.top = clientDiv.style.top;
+						overlayDiv.style.height = clientDiv.style.height;
+					}
+				}
+			}
+			this._updateDOMSelection();
+
+			/*
+			* If the client height changed during the update page it means that scrollbar has either been shown or hidden.
+			* When this happens update page has to run again to ensure that the top and bottom lines div are correct.
+			* 
+			* Note: On IE, updateDOMSelection() has to be called before getting the new client height because it
+			* forces the client area to be recomputed.
+			*/
+			var ensureCaretVisible = this._ensureCaretVisible;
+			this._ensureCaretVisible = false;
+			if (clientHeight !== this._getClientHeight()) {
+				this._update();
+				if (ensureCaretVisible) {
+					this._showCaret();
+				}
+			}
+		},
+		_updateRuler: function (divRuler, topIndex, bottomIndex, parentHeight) {
+			if (!divRuler) { return; }
+			var document = this._parent.ownerDocument;
+			var lineHeight = this._getLineHeight();
+			var viewPad = this._getViewPadding();
+			var div = divRuler.firstChild;
+			while (div) {
+				var ruler = div._ruler;
+				var offset = lineHeight;
+				var overview = ruler.getOverview();
+				if (overview === "page") { offset += this._topIndexY; } //$NON-NLS-0$
+				div.style.top = -offset + "px"; //$NON-NLS-0$
+				div.style.height = (parentHeight + offset) + "px"; //$NON-NLS-0$
+				
+				if (div.rulerChanged) {
+					applyStyle(ruler.getRulerStyle(), div);
+				}
+				
+				var widthDiv;
+				var child = div.firstChild;
+				if (child) {
+					widthDiv = child;
+					child = child.nextSibling;
+				} else {
+					widthDiv = util.createElement(document, "div"); //$NON-NLS-0$
+					widthDiv.style.visibility = "hidden"; //$NON-NLS-0$
+					div.appendChild(widthDiv);
+				}
+				var lineIndex, annotation;
+				if (div.rulerChanged) {
+					if (widthDiv) {
+						lineIndex = -1;
+						annotation = ruler.getWidestAnnotation();
+						if (annotation) {
+							applyStyle(annotation.style, widthDiv);
+							if (annotation.html) {
+								widthDiv.innerHTML = annotation.html;
+							}
+						}
+						widthDiv.lineIndex = lineIndex;
+						widthDiv.style.height = (lineHeight + viewPad.top) + "px"; //$NON-NLS-0$
+					}
+				}
+
+				var lineDiv, frag, annotations;
+				if (overview === "page") { //$NON-NLS-0$
+					annotations = ruler.getAnnotations(topIndex, bottomIndex + 1);
+					while (child) {
+						lineIndex = child.lineIndex;
+						var nextChild = child.nextSibling;
+						if (!(topIndex <= lineIndex && lineIndex <= bottomIndex) || child.lineChanged) {
+							div.removeChild(child);
+						}
+						child = nextChild;
+					}
+					child = div.firstChild.nextSibling;
+					frag = document.createDocumentFragment();
+					for (lineIndex=topIndex; lineIndex<=bottomIndex; lineIndex++) {
+						if (!child || child.lineIndex > lineIndex) {
+							lineDiv = util.createElement(document, "div"); //$NON-NLS-0$
+							annotation = annotations[lineIndex];
+							if (annotation) {
+								applyStyle(annotation.style, lineDiv);
+								if (annotation.html) {
+									lineDiv.innerHTML = annotation.html;
+								}
+								lineDiv.annotation = annotation;
+							}
+							lineDiv.lineIndex = lineIndex;
+							lineDiv.style.height = this._getLineHeight(lineIndex) + "px"; //$NON-NLS-0$
+							frag.appendChild(lineDiv);
+						} else {
+							if (frag.firstChild) {
+								div.insertBefore(frag, child);
+								frag = document.createDocumentFragment();
+							}
+							if (child) {
+								child = child.nextSibling;
+							}
+						}
+					}
+					if (frag.firstChild) { div.insertBefore(frag, child); }
+				} else {
+					var clientHeight = this._getClientHeight ();
+					var lineCount = this._model.getLineCount ();
+					var contentHeight = lineHeight * lineCount;
+					var trackHeight = clientHeight + viewPad.top + viewPad.bottom - 2 * this._metrics.scrollWidth;
+					var divHeight;
+					if (contentHeight < trackHeight) {
+						divHeight = lineHeight;
+					} else {
+						divHeight = trackHeight / lineCount;
+					}
+					if (div.rulerChanged) {
+						var count = div.childNodes.length;
+						while (count > 1) {
+							div.removeChild(div.lastChild);
+							count--;
+						}
+						annotations = ruler.getAnnotations(0, lineCount);
+						frag = document.createDocumentFragment();
+						for (var prop in annotations) {
+							lineIndex = prop >>> 0;
+							if (lineIndex < 0) { continue; }
+							lineDiv = util.createElement(document, "div"); //$NON-NLS-0$
+							annotation = annotations[prop];
+							applyStyle(annotation.style, lineDiv);
+							lineDiv.style.position = "absolute"; //$NON-NLS-0$
+							lineDiv.style.top = this._metrics.scrollWidth + lineHeight + Math.floor(lineIndex * divHeight) + "px"; //$NON-NLS-0$
+							if (annotation.html) {
+								lineDiv.innerHTML = annotation.html;
+							}
+							lineDiv.annotation = annotation;
+							lineDiv.lineIndex = lineIndex;
+							frag.appendChild(lineDiv);
+						}
+						div.appendChild(frag);
+					} else if (div._oldTrackHeight !== trackHeight) {
+						lineDiv = div.firstChild ? div.firstChild.nextSibling : null;
+						while (lineDiv) {
+							lineDiv.style.top = this._metrics.scrollWidth + lineHeight + Math.floor(lineDiv.lineIndex * divHeight) + "px"; //$NON-NLS-0$
+							lineDiv = lineDiv.nextSibling;
+						}
+					}
+					div._oldTrackHeight = trackHeight;
+				}
+				div.rulerChanged = false;
+				div = div.nextSibling;
+			}
+		},
+		_updateStyleSheet: function() {
+			var styleText = "";
+			if (util.isWebkit && this._metrics.scrollWidth > 0) {
+				styleText += "\n.textview ::-webkit-scrollbar-corner {background: #eeeeee;}"; //$NON-NLS-0$
+			}
+			if (util.isFirefox && util.isMac && this._highlightRGB && this._highlightRGB !== "Highlight") { //$NON-NLS-0$
+				styleText += "\n.textview ::-moz-selection {background: " + this._highlightRGB + ";}"; //$NON-NLS-1$ //$NON-NLS-0$
+			}
+			if (styleText) {
+				var node = document.getElementById("_textviewStyle"); //$NON-NLS-0$
+				if (!node) {
+					node = util.createElement(document, "style"); //$NON-NLS-0$
+					node.id = "_textviewStyle"; //$NON-NLS-0$
+					var head = document.getElementsByTagName("head")[0] || document.documentElement; //$NON-NLS-0$
+					node.appendChild(document.createTextNode(styleText));
+					head.insertBefore(node, head.firstChild);
+				} else {
+					node.removeChild(node.firstChild);
+					node.appendChild(document.createTextNode(styleText));
+				}
+			}
+		},
+		_updateStyle: function (init) {
+			if (!init && util.isIE) {
+				this._rootDiv.style.lineHeight = "normal"; //$NON-NLS-0$
+			}
+			var metrics = this._metrics = this._calculateMetrics();
+			if (util.isIE) {
+				this._rootDiv.style.lineHeight = (metrics.lineHeight - (metrics.lineTrim.top + metrics.lineTrim.bottom)) + "px"; //$NON-NLS-0$
+			} else {
+				this._rootDiv.style.lineHeight = "normal"; //$NON-NLS-0$
+			}
+			this._updateStyleSheet();
+			if (!init) {
+				this.redraw();
+				this._resetLineWidth();
+			}
+		}
+	};//end prototype
+	mEventTarget.EventTarget.addMixin(TextView.prototype);
+	
+	return {TextView: TextView};
+});
+
+
+/*******************************************************************************
+ * @license
+ * Copyright (c) 2010, 2012 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials are made 
+ * available under the terms of the Eclipse Public License v1.0 
+ * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
+ * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
+ * 
+ * Contributors: 
+ *		Felipe Heidrich (IBM Corporation) - initial API and implementation
+ *		Silenio Quarti (IBM Corporation) - initial API and implementation
+ ******************************************************************************/
+
+/*global define */
+
+define("orion/editor/projectionTextModel", ['orion/editor/textModel', 'orion/editor/eventTarget'], function(mTextModel, mEventTarget) { //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+
+	/**
+	 * @class This object represents a projection range. A projection specifies a
+	 * range of text and the replacement text. The range of text is relative to the
+	 * base text model associated to a projection model.
+	 * <p>
+	 * <b>See:</b><br/>
+	 * {@link orion.editor.ProjectionTextModel}<br/>
+	 * {@link orion.editor.ProjectionTextModel#addProjection}<br/>
+	 * </p>		 
+	 * @name orion.editor.Projection
+	 * 
+	 * @property {Number} start The start offset of the projection range. 
+	 * @property {Number} end The end offset of the projection range. This offset is exclusive.
+	 * @property {String|orion.editor.TextModel} [text=""] The projection text to be inserted
+	 */
+	/**
+	 * Constructs a new <code>ProjectionTextModel</code> based on the specified <code>TextModel</code>.
+	 *
+	 * @param {orion.editor.TextModel} baseModel The base text model.
+	 *
+	 * @name orion.editor.ProjectionTextModel
+	 * @class The <code>ProjectionTextModel</code> represents a projection of its base text
+	 * model. Projection ranges can be added to the projection text model to hide and/or insert
+	 * ranges to the base text model.
+	 * <p>
+	 * The contents of the projection text model is modified when changes occur in the base model,
+	 * projection model or by calls to {@link #addProjection} and {@link #removeProjection}.
+	 * </p>
+	 * <p>
+	 * <b>See:</b><br/>
+	 * {@link orion.editor.TextView}<br/>
+	 * {@link orion.editor.TextModel}
+	 * {@link orion.editor.TextView#setModel}
+	 * </p>
+	 * @borrows orion.editor.EventTarget#addEventListener as #addEventListener
+	 * @borrows orion.editor.EventTarget#removeEventListener as #removeEventListener
+	 * @borrows orion.editor.EventTarget#dispatchEvent as #dispatchEvent
+	 */
+	function ProjectionTextModel(baseModel) {
+		this._model = baseModel;	/* Base Model */
+		this._projections = [];
+	}
+
+	ProjectionTextModel.prototype = /** @lends orion.editor.ProjectionTextModel.prototype */ {
+		/**
+		 * Adds a projection range to the model.
+		 * <p>
+		 * The model must notify the listeners before and after the the text is
+		 * changed by calling {@link #onChanging} and {@link #onChanged} respectively. 
+		 * </p>
+		 * @param {orion.editor.Projection} projection The projection range to be added.
+		 * 
+		 * @see #removeProjection
+		 */
+		addProjection: function(projection) {
+			if (!projection) {return;}
+			//start and end can't overlap any exist projection
+			var model = this._model, projections = this._projections;
+			projection._lineIndex = model.getLineAtOffset(projection.start);
+			projection._lineCount = model.getLineAtOffset(projection.end) - projection._lineIndex;
+			var text = projection.text;
+			if (!text) { text = ""; }
+			if (typeof text === "string") { //$NON-NLS-0$
+				projection._model = new mTextModel.TextModel(text, model.getLineDelimiter());
+			} else {
+				projection._model = text;
+			}
+			var eventStart = this.mapOffset(projection.start, true);
+			var removedCharCount = projection.end - projection.start;
+			var removedLineCount = projection._lineCount;
+			var addedCharCount = projection._model.getCharCount();
+			var addedLineCount = projection._model.getLineCount() - 1;
+			var modelChangingEvent = {
+				type: "Changing", //$NON-NLS-0$
+				text: projection._model.getText(),
+				start: eventStart,
+				removedCharCount: removedCharCount,
+				addedCharCount: addedCharCount,
+				removedLineCount: removedLineCount,
+				addedLineCount: addedLineCount
+			};
+			this.onChanging(modelChangingEvent);
+			var index = this._binarySearch(projections, projection.start);
+			projections.splice(index, 0, projection);
+			var modelChangedEvent = {
+				type: "Changed", //$NON-NLS-0$
+				start: eventStart,
+				removedCharCount: removedCharCount,
+				addedCharCount: addedCharCount,
+				removedLineCount: removedLineCount,
+				addedLineCount: addedLineCount
+			};
+			this.onChanged(modelChangedEvent);
+		},
+		/**
+		 * Returns all projection ranges of this model.
+		 * 
+		 * @return {orion.editor.Projection[]} The projection ranges.
+		 * 
+		 * @see #addProjection
+		 */
+		getProjections: function() {
+			return this._projections.slice(0);
+		},
+		/**
+		 * Gets the base text model.
+		 *
+		 * @return {orion.editor.TextModel} The base text model.
+		 */
+		getBaseModel: function() {
+			return this._model;
+		},
+		/**
+		 * Maps offsets between the projection model and its base model.
+		 *
+		 * @param {Number} offset The offset to be mapped.
+		 * @param {Boolean} [baseOffset=false] <code>true</code> if <code>offset</code> is in base model and
+		 *	should be mapped to the projection model.
+		 * @return {Number} The mapped offset
+		 */
+		mapOffset: function(offset, baseOffset) {
+			var projections = this._projections, delta = 0, i, projection;
+			if (baseOffset) {
+				for (i = 0; i < projections.length; i++) {
+					projection = projections[i];
+					if (projection.start > offset) { break; }
+					if (projection.end > offset) { return -1; }
+					delta += projection._model.getCharCount() - (projection.end - projection.start);
+				}
+				return offset + delta;
+			}
+			for (i = 0; i < projections.length; i++) {
+				projection = projections[i];
+				if (projection.start > offset - delta) { break; }
+				var charCount = projection._model.getCharCount();
+				if (projection.start + charCount > offset - delta) {
+					return -1;
+				}
+				delta += charCount - (projection.end - projection.start);
+			}
+			return offset - delta;
+		},
+		/**
+		 * Removes a projection range from the model.
+		 * <p>
+		 * The model must notify the listeners before and after the the text is
+		 * changed by calling {@link #onChanging} and {@link #onChanged} respectively. 
+		 * </p>
+		 * 
+		 * @param {orion.editor.Projection} projection The projection range to be removed.
+		 * 
+		 * @see #addProjection
+		 */
+		removeProjection: function(projection) {
+			//TODO remove listeners from model
+			var i, delta = 0;
+			for (i = 0; i < this._projections.length; i++) {
+				var p = this._projections[i];
+				if (p === projection) {
+					projection = p;
+					break;
+				}
+				delta += p._model.getCharCount() - (p.end - p.start);
+			}
+			if (i < this._projections.length) {
+				var model = this._model;
+				var eventStart = projection.start + delta;
+				var addedCharCount = projection.end - projection.start;
+				var addedLineCount = projection._lineCount;
+				var removedCharCount = projection._model.getCharCount();
+				var removedLineCount = projection._model.getLineCount() - 1;
+				var modelChangingEvent = {
+					type: "Changing", //$NON-NLS-0$
+					text: model.getText(projection.start, projection.end),
+					start: eventStart,
+					removedCharCount: removedCharCount,
+					addedCharCount: addedCharCount,
+					removedLineCount: removedLineCount,
+					addedLineCount: addedLineCount
+				};
+				this.onChanging(modelChangingEvent);
+				this._projections.splice(i, 1);
+				var modelChangedEvent = {
+					type: "Changed", //$NON-NLS-0$
+					start: eventStart,
+					removedCharCount: removedCharCount,
+					addedCharCount: addedCharCount,
+					removedLineCount: removedLineCount,
+					addedLineCount: addedLineCount
+				};
+				this.onChanged(modelChangedEvent);
+			}
+		},
+		/** @ignore */
+		_binarySearch: function (array, offset) {
+			var high = array.length, low = -1, index;
+			while (high - low > 1) {
+				index = Math.floor((high + low) / 2);
+				if (offset <= array[index].start) {
+					high = index;
+				} else {
+					low = index;
+				}
+			}
+			return high;
+		},
+		/**
+		 * @see orion.editor.TextModel#getCharCount
+		 */
+		getCharCount: function() {
+			var count = this._model.getCharCount(), projections = this._projections;
+			for (var i = 0; i < projections.length; i++) {
+				var projection = projections[i];
+				count += projection._model.getCharCount() - (projection.end - projection.start);
+			}
+			return count;
+		},
+		/**
+		 * @see orion.editor.TextModel#getLine
+		 */
+		getLine: function(lineIndex, includeDelimiter) {
+			if (lineIndex < 0) { return null; }
+			var model = this._model, projections = this._projections;
+			var delta = 0, result = [], offset = 0, i, lineCount, projection;
+			for (i = 0; i < projections.length; i++) {
+				projection = projections[i];
+				if (projection._lineIndex >= lineIndex - delta) { break; }
+				lineCount = projection._model.getLineCount() - 1;
+				if (projection._lineIndex + lineCount >= lineIndex - delta) {
+					var projectionLineIndex = lineIndex - (projection._lineIndex + delta);
+					if (projectionLineIndex < lineCount) {
+						return projection._model.getLine(projectionLineIndex, includeDelimiter);
+					} else {
+						result.push(projection._model.getLine(lineCount));
+					}
+				}
+				offset = projection.end;
+				delta += lineCount - projection._lineCount;
+			}
+			offset = Math.max(offset, model.getLineStart(lineIndex - delta));
+			for (; i < projections.length; i++) {
+				projection = projections[i];
+				if (projection._lineIndex > lineIndex - delta) { break; }
+				result.push(model.getText(offset, projection.start));
+				lineCount = projection._model.getLineCount() - 1;
+				if (projection._lineIndex + lineCount > lineIndex - delta) {
+					result.push(projection._model.getLine(0, includeDelimiter));
+					return result.join("");
+				}
+				result.push(projection._model.getText());
+				offset = projection.end;
+				delta += lineCount - projection._lineCount;
+			}
+			var end = model.getLineEnd(lineIndex - delta, includeDelimiter);
+			if (offset < end) {
+				result.push(model.getText(offset, end));
+			}
+			return result.join("");
+		},
+		/**
+		 * @see orion.editor.TextModel#getLineAtOffset
+		 */
+		getLineAtOffset: function(offset) {
+			var model = this._model, projections = this._projections;
+			var delta = 0, lineDelta = 0;
+			for (var i = 0; i < projections.length; i++) {
+				var projection = projections[i];
+				if (projection.start > offset - delta) { break; }
+				var charCount = projection._model.getCharCount();
+				if (projection.start + charCount > offset - delta) {
+					var projectionOffset = offset - (projection.start + delta);
+					lineDelta += projection._model.getLineAtOffset(projectionOffset);
+					delta += projectionOffset;
+					break;
+				}
+				lineDelta += projection._model.getLineCount() - 1 - projection._lineCount;
+				delta += charCount - (projection.end - projection.start);
+			}
+			return model.getLineAtOffset(offset - delta) + lineDelta;
+		},
+		/**
+		 * @see orion.editor.TextModel#getLineCount
+		 */
+		getLineCount: function() {
+			var model = this._model, projections = this._projections;
+			var count = model.getLineCount();
+			for (var i = 0; i < projections.length; i++) {
+				var projection = projections[i];
+				count += projection._model.getLineCount() - 1 - projection._lineCount;
+			}
+			return count;
+		},
+		/**
+		 * @see orion.editor.TextModel#getLineDelimiter
+		 */
+		getLineDelimiter: function() {
+			return this._model.getLineDelimiter();
+		},
+		/**
+		 * @see orion.editor.TextModel#getLineEnd
+		 */
+		getLineEnd: function(lineIndex, includeDelimiter) {
+			if (lineIndex < 0) { return -1; }
+			var model = this._model, projections = this._projections;
+			var delta = 0, offsetDelta = 0;
+			for (var i = 0; i < projections.length; i++) {
+				var projection = projections[i];
+				if (projection._lineIndex > lineIndex - delta) { break; }
+				var lineCount = projection._model.getLineCount() - 1;
+				if (projection._lineIndex + lineCount > lineIndex - delta) {
+					var projectionLineIndex = lineIndex - (projection._lineIndex + delta);
+					return projection._model.getLineEnd (projectionLineIndex, includeDelimiter) + projection.start + offsetDelta;
+				}
+				offsetDelta += projection._model.getCharCount() - (projection.end - projection.start);
+				delta += lineCount - projection._lineCount;
+			}
+			return model.getLineEnd(lineIndex - delta, includeDelimiter) + offsetDelta;
+		},
+		/**
+		 * @see orion.editor.TextModel#getLineStart
+		 */
+		getLineStart: function(lineIndex) {
+			if (lineIndex < 0) { return -1; }
+			var model = this._model, projections = this._projections;
+			var delta = 0, offsetDelta = 0;
+			for (var i = 0; i < projections.length; i++) {
+				var projection = projections[i];
+				if (projection._lineIndex >= lineIndex - delta) { break; }
+				var lineCount = projection._model.getLineCount() - 1;
+				if (projection._lineIndex + lineCount >= lineIndex - delta) {
+					var projectionLineIndex = lineIndex - (projection._lineIndex + delta);
+					return projection._model.getLineStart (projectionLineIndex) + projection.start + offsetDelta;
+				}
+				offsetDelta += projection._model.getCharCount() - (projection.end - projection.start);
+				delta += lineCount - projection._lineCount;
+			}
+			return model.getLineStart(lineIndex - delta) + offsetDelta;
+		},
+		/**
+		 * @see orion.editor.TextModel#getText
+		 */
+		getText: function(start, end) {
+			if (start === undefined) { start = 0; }
+			var model = this._model, projections = this._projections;
+			var delta = 0, result = [], i, projection, charCount;
+			for (i = 0; i < projections.length; i++) {
+				projection = projections[i];
+				if (projection.start > start - delta) { break; }
+				charCount = projection._model.getCharCount();
+				if (projection.start + charCount > start - delta) {
+					if (end !== undefined && projection.start + charCount > end - delta) {
+						return projection._model.getText(start - (projection.start + delta), end - (projection.start + delta));
+					} else {
+						result.push(projection._model.getText(start - (projection.start + delta)));
+						start = projection.end + delta + charCount - (projection.end - projection.start);
+					}
+				}
+				delta += charCount - (projection.end - projection.start);
+			}
+			var offset = start - delta;
+			if (end !== undefined) {
+				for (; i < projections.length; i++) {
+					projection = projections[i];
+					if (projection.start > end - delta) { break; }
+					result.push(model.getText(offset, projection.start));
+					charCount = projection._model.getCharCount();
+					if (projection.start + charCount > end - delta) {
+						result.push(projection._model.getText(0, end - (projection.start + delta)));
+						return result.join("");
+					}
+					result.push(projection._model.getText());
+					offset = projection.end;
+					delta += charCount - (projection.end - projection.start);
+				}
+				result.push(model.getText(offset, end - delta));
+			} else {
+				for (; i < projections.length; i++) {
+					projection = projections[i];
+					result.push(model.getText(offset, projection.start));
+					result.push(projection._model.getText());
+					offset = projection.end;
+				}
+				result.push(model.getText(offset));
+			}
+			return result.join("");
+		},
+		/** @ignore */
+		_onChanging: function(text, start, removedCharCount, addedCharCount, removedLineCount, addedLineCount) {
+			var model = this._model, projections = this._projections, i, projection, delta = 0, lineDelta;
+			var end = start + removedCharCount;
+			for (; i < projections.length; i++) {
+				projection = projections[i];
+				if (projection.start > start) { break; }
+				delta += projection._model.getCharCount() - (projection.end - projection.start);
+			}
+			/*TODO add stuff saved by setText*/
+			var mapStart = start + delta, rangeStart = i;
+			for (; i < projections.length; i++) {
+				projection = projections[i];
+				if (projection.start > end) { break; }
+				delta += projection._model.getCharCount() - (projection.end - projection.start);
+				lineDelta += projection._model.getLineCount() - 1 - projection._lineCount;
+			}
+			/*TODO add stuff saved by setText*/
+			var mapEnd = end + delta, rangeEnd = i;
+			this.onChanging(mapStart, mapEnd - mapStart, addedCharCount/*TODO add stuff saved by setText*/, removedLineCount + lineDelta/*TODO add stuff saved by setText*/, addedLineCount/*TODO add stuff saved by setText*/);
+			projections.splice(projections, rangeEnd - rangeStart);
+			var count = text.length - (mapEnd - mapStart);
+			for (; i < projections.length; i++) {
+				projection = projections[i];
+				projection.start += count;
+				projection.end += count;
+				projection._lineIndex = model.getLineAtOffset(projection.start);
+			}
+		},
+		/**
+		 * @see orion.editor.TextModel#onChanging
+		 */
+		onChanging: function(modelChangingEvent) {
+			return this.dispatchEvent(modelChangingEvent);
+		},
+		/**
+		 * @see orion.editor.TextModel#onChanged
+		 */
+		onChanged: function(modelChangedEvent) {
+			return this.dispatchEvent(modelChangedEvent);
+		},
+		/**
+		 * @see orion.editor.TextModel#setLineDelimiter
+		 */
+		setLineDelimiter: function(lineDelimiter) {
+			this._model.setLineDelimiter(lineDelimiter);
+		},
+		/**
+		 * @see orion.editor.TextModel#setText
+		 */
+		setText: function(text, start, end) {
+			if (text === undefined) { text = ""; }
+			if (start === undefined) { start = 0; }
+			var eventStart = start, eventEnd = end;
+			var model = this._model, projections = this._projections;
+			var delta = 0, lineDelta = 0, i, projection, charCount, startProjection, endProjection, startLineDelta = 0;
+			for (i = 0; i < projections.length; i++) {
+				projection = projections[i];
+				if (projection.start > start - delta) { break; }
+				charCount = projection._model.getCharCount();
+				if (projection.start + charCount > start - delta) {
+					if (end !== undefined && projection.start + charCount > end - delta) {
+						projection._model.setText(text, start - (projection.start + delta), end - (projection.start + delta));
+						//TODO events - special case
+						return;
+					} else {
+						startLineDelta = projection._model.getLineCount() - 1 - projection._model.getLineAtOffset(start - (projection.start + delta));
+						startProjection = {
+							projection: projection,
+							start: start - (projection.start + delta)
+						};
+						start = projection.end + delta + charCount - (projection.end - projection.start);
+					}
+				}
+				lineDelta += projection._model.getLineCount() - 1 - projection._lineCount;
+				delta += charCount - (projection.end - projection.start);
+			}
+			var mapStart = start - delta, rangeStart = i, startLine = model.getLineAtOffset(mapStart) + lineDelta - startLineDelta;
+			if (end !== undefined) {
+				for (; i < projections.length; i++) {
+					projection = projections[i];
+					if (projection.start > end - delta) { break; }
+					charCount = projection._model.getCharCount();
+					if (projection.start + charCount > end - delta) {
+						lineDelta += projection._model.getLineAtOffset(end - (projection.start + delta));
+						charCount = end - (projection.start + delta);
+						end = projection.end + delta;
+						endProjection = {
+							projection: projection,
+							end: charCount
+						};
+						break;
+					}
+					lineDelta += projection._model.getLineCount() - 1 - projection._lineCount;
+					delta += charCount - (projection.end - projection.start);
+				}
+			} else {
+				for (; i < projections.length; i++) {
+					projection = projections[i];
+					lineDelta += projection._model.getLineCount() - 1 - projection._lineCount;
+					delta += projection._model.getCharCount() - (projection.end - projection.start);
+				}
+				end = eventEnd = model.getCharCount() + delta;
+			}
+			var mapEnd = end - delta, rangeEnd = i, endLine = model.getLineAtOffset(mapEnd) + lineDelta;
+			
+			//events
+			var removedCharCount = eventEnd - eventStart;
+			var removedLineCount = endLine - startLine;
+			var addedCharCount = text.length;
+			var addedLineCount = 0;
+			var cr = 0, lf = 0, index = 0;
+			while (true) {
+				if (cr !== -1 && cr <= index) { cr = text.indexOf("\r", index); } //$NON-NLS-0$
+				if (lf !== -1 && lf <= index) { lf = text.indexOf("\n", index); } //$NON-NLS-0$
+				if (lf === -1 && cr === -1) { break; }
+				if (cr !== -1 && lf !== -1) {
+					if (cr + 1 === lf) {
+						index = lf + 1;
+					} else {
+						index = (cr < lf ? cr : lf) + 1;
+					}
+				} else if (cr !== -1) {
+					index = cr + 1;
+				} else {
+					index = lf + 1;
+				}
+				addedLineCount++;
+			}
+			
+			var modelChangingEvent = {
+				type: "Changing", //$NON-NLS-0$
+				text: text,
+				start: eventStart,
+				removedCharCount: removedCharCount,
+				addedCharCount: addedCharCount,
+				removedLineCount: removedLineCount,
+				addedLineCount: addedLineCount
+			};
+			this.onChanging(modelChangingEvent);
+			
+//			var changeLineCount = model.getLineAtOffset(mapEnd) - model.getLineAtOffset(mapStart) + addedLineCount;
+			model.setText(text, mapStart, mapEnd);
+			if (startProjection) {
+				projection = startProjection.projection;
+				projection._model.setText("", startProjection.start);
+			}		
+			if (endProjection) {
+				projection = endProjection.projection;
+				projection._model.setText("", 0, endProjection.end);
+				projection.start = projection.end;
+				projection._lineCount = 0;
+			}
+			projections.splice(rangeStart, rangeEnd - rangeStart);
+			var changeCount = text.length - (mapEnd - mapStart);
+			for (i = rangeEnd; i < projections.length; i++) {
+				projection = projections[i];
+				projection.start += changeCount;
+				projection.end += changeCount;
+//				if (projection._lineIndex + changeLineCount !== model.getLineAtOffset(projection.start)) {
+//					log("here");
+//				}
+				projection._lineIndex = model.getLineAtOffset(projection.start);
+//				projection._lineIndex += changeLineCount;
+			}
+			
+			var modelChangedEvent = {
+				type: "Changed", //$NON-NLS-0$
+				start: eventStart,
+				removedCharCount: removedCharCount,
+				addedCharCount: addedCharCount,
+				removedLineCount: removedLineCount,
+				addedLineCount: addedLineCount
+			};
+			this.onChanged(modelChangedEvent);
+		}
+	};
+	mEventTarget.EventTarget.addMixin(ProjectionTextModel.prototype);
+
+	return {ProjectionTextModel: ProjectionTextModel};
+});
+
+/*******************************************************************************
+ * @license
+ * Copyright (c) 2010, 2012 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials are made 
+ * available under the terms of the Eclipse Public License v1.0 
+ * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
+ * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
+ * 
+ * Contributors: 
+ *		Felipe Heidrich (IBM Corporation) - initial API and implementation
+ *		Silenio Quarti (IBM Corporation) - initial API and implementation
+ ******************************************************************************/
+
+/*global define */
+
+define("orion/editor/annotations", ['i18n!orion/editor/nls/messages', 'orion/editor/eventTarget'], function(messages, mEventTarget) { //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+	/**
+	 * @class This object represents a decoration attached to a range of text. Annotations are added to a
+	 * <code>AnnotationModel</code> which is attached to a <code>TextModel</code>.
+	 * <p>
+	 * <b>See:</b><br/>
+	 * {@link orion.editor.AnnotationModel}<br/>
+	 * {@link orion.editor.Ruler}<br/>
+	 * </p>		 
+	 * @name orion.editor.Annotation
+	 * 
+	 * @property {String} type The annotation type (for example, orion.annotation.error).
+	 * @property {Number} start The start offset of the annotation in the text model.
+	 * @property {Number} end The end offset of the annotation in the text model.
+	 * @property {String} html The HTML displayed for the annotation.
+	 * @property {String} title The text description for the annotation.
+	 * @property {orion.editor.Style} style The style information for the annotation used in the annotations ruler and tooltips.
+	 * @property {orion.editor.Style} overviewStyle The style information for the annotation used in the overview ruler.
+	 * @property {orion.editor.Style} rangeStyle The style information for the annotation used in the text view to decorate a range of text.
+	 * @property {orion.editor.Style} lineStyle The style information for the annotation used in the text view to decorate a line of text.
+	 */
+	/**
+	 * Constructs a new folding annotation.
+	 * 
+	 * @param {Number} start The start offset of the annotation in the text model.
+	 * @param {Number} end The end offset of the annotation in the text model.
+	 * @param {orion.editor.ProjectionTextModel} projectionModel The projection text model.
+	 * 
+	 * @class This object represents a folding annotation.
+	 * @name orion.editor.FoldingAnnotation
+	 */
+	function FoldingAnnotation (start, end, projectionModel) {
+		this.start = start;
+		this.end = end;
+		this._projectionModel = projectionModel;
+		this.html = this._expandedHTML;
+		this.style = this._expandedStyle;
+		this.expanded = true;
+	}
+	
+	FoldingAnnotation.prototype = /** @lends orion.editor.FoldingAnnotation.prototype */ {
+		_expandedHTML: "<div class='annotationHTML expanded'></div>", //$NON-NLS-0$
+		_expandedStyle: {styleClass: "annotation expanded"}, //$NON-NLS-0$
+		_collapsedHTML: "<div class='annotationHTML collapsed'></div>", //$NON-NLS-0$
+		_collapsedStyle: {styleClass: "annotation collapsed"}, //$NON-NLS-0$
+		/**
+		 * Collapses the annotation.
+		 */
+		collapse: function () {
+			if (!this.expanded) { return; }
+			this.expanded = false;
+			this.html = this._collapsedHTML;
+			this.style = this._collapsedStyle;
+			var projectionModel = this._projectionModel;
+			var baseModel = projectionModel.getBaseModel();
+			this._projection = {
+				start: baseModel.getLineStart(baseModel.getLineAtOffset(this.start) + 1),
+				end: baseModel.getLineEnd(baseModel.getLineAtOffset(this.end), true)
+			};
+			projectionModel.addProjection(this._projection);
+		},
+		/**
+		 * Expands the annotation.
+		 */
+		expand: function () {
+			if (this.expanded) { return; }
+			this.expanded = true;
+			this.html = this._expandedHTML;
+			this.style = this._expandedStyle;
+			this._projectionModel.removeProjection(this._projection);
+		}
+	};
+	 
+	/**
+	 * @class This object represents a regitry of annotation types.
+	 * @name orion.editor.AnnotationType
+	 */
+	function AnnotationType() {
+	}
+	
+	/**
+	 * Error annotation type.
+	 */
+	AnnotationType.ANNOTATION_ERROR = "orion.annotation.error"; //$NON-NLS-0$
+	/**
+	 * Warning annotation type.
+	 */
+	AnnotationType.ANNOTATION_WARNING = "orion.annotation.warning"; //$NON-NLS-0$
+	/**
+	 * Task annotation type.
+	 */
+	AnnotationType.ANNOTATION_TASK = "orion.annotation.task"; //$NON-NLS-0$
+	/**
+	 * Breakpoint annotation type.
+	 */
+	AnnotationType.ANNOTATION_BREAKPOINT = "orion.annotation.breakpoint"; //$NON-NLS-0$
+	/**
+	 * Bookmark annotation type.
+	 */
+	AnnotationType.ANNOTATION_BOOKMARK = "orion.annotation.bookmark"; //$NON-NLS-0$
+	/**
+	 * Folding annotation type.
+	 */
+	AnnotationType.ANNOTATION_FOLDING = "orion.annotation.folding"; //$NON-NLS-0$
+	/**
+	 * Curent bracket annotation type.
+	 */
+	AnnotationType.ANNOTATION_CURRENT_BRACKET = "orion.annotation.currentBracket"; //$NON-NLS-0$
+	/**
+	 * Matching bracket annotation type.
+	 */
+	AnnotationType.ANNOTATION_MATCHING_BRACKET = "orion.annotation.matchingBracket"; //$NON-NLS-0$
+	/**
+	 * Current line annotation type.
+	 */
+	AnnotationType.ANNOTATION_CURRENT_LINE = "orion.annotation.currentLine"; //$NON-NLS-0$
+	/**
+	 * Current search annotation type.
+	 */
+	AnnotationType.ANNOTATION_CURRENT_SEARCH = "orion.annotation.currentSearch"; //$NON-NLS-0$
+	/**
+	 * Matching search annotation type.
+	 */
+	AnnotationType.ANNOTATION_MATCHING_SEARCH = "orion.annotation.matchingSearch"; //$NON-NLS-0$
+	/**
+	 * Read Occurrence annotation type.
+	 */
+	AnnotationType.ANNOTATION_READ_OCCURRENCE = "orion.annotation.readOccurrence"; //$NON-NLS-0$
+	/**
+	 * Write Occurrence annotation type.
+	 */
+	AnnotationType.ANNOTATION_WRITE_OCCURRENCE = "orion.annotation.writeOccurrence"; //$NON-NLS-0$
+
+	/**
+	 * Selected linked group annotation type.
+	 */
+	AnnotationType.ANNOTATION_SELECTED_LINKED_GROUP = "orion.annotation.selectedLinkedGroup"; //$NON-NLS-0$
+
+	/**
+	 * Current linked group annotation type.
+	 */
+	AnnotationType.ANNOTATION_CURRENT_LINKED_GROUP = "orion.annotation.currentLinkedGroup"; //$NON-NLS-0$
+
+	/**
+	 * Linked group annotation type.
+	 */
+	AnnotationType.ANNOTATION_LINKED_GROUP = "orion.annotation.linkedGroup"; //$NON-NLS-0$
+	
+	/** @private */
+	var annotationTypes = {};
+	
+	/**
+	 * Register an annotation type.
+	 *
+	 * @param {String} type The annotation type (for example, orion.annotation.error).
+	 * @param {Object|Function} properties The common annotation properties of the registered
+	 *		annotation type. All annotations create with this annotation type will expose these
+	 *		properties.
+	 */
+	AnnotationType.registerType = function(type, properties) {
+		var constructor = properties;
+		if (typeof constructor !== "function") { //$NON-NLS-0$
+			constructor = function(start, end, title) {
+				this.start = start;
+				this.end = end;
+				if (title !== undefined) { this.title = title; }
+			};
+			constructor.prototype = properties;
+		}
+		constructor.prototype.type = type;
+		annotationTypes[type] = constructor;
+		return type;
+	};
+	
+	/**
+	 * Creates an annotation of a given type with the specified start end end offsets.
+	 *
+	 * @param {String} type The annotation type (for example, orion.annotation.error).
+	 * @param {Number} start The start offset of the annotation in the text model.
+	 * @param {Number} end The end offset of the annotation in the text model.
+	 * @param {String} [title] The text description for the annotation if different then the type description.
+	 * @return {orion.editor.Annotation} the new annotation
+	 */
+	AnnotationType.createAnnotation = function(type, start, end, title) {
+		return new (this.getType(type))(start, end, title);
+	};
+	
+	/**
+	 * Gets the registered annotation type with specified type. The returned
+	 * value is a constructor that can be used to create annotations of the
+	 * speficied type.  The constructor takes the start and end offsets of
+	 * the annotation.
+	 *
+	 * @param {String} type The annotation type (for example, orion.annotation.error).
+	 * @return {Function} The annotation type constructor ( i.e function(start, end, title) ).
+	 */
+	AnnotationType.getType = function(type) {
+		return annotationTypes[type];
+	};
+	
+	/** @private */
+	function registerType(type, lineStyling) {
+		var index = type.lastIndexOf('.'); //$NON-NLS-0$
+		var suffix = type.substring(index + 1);
+		var properties = {
+			title: messages[suffix],
+			style: {styleClass: "annotation " + suffix}, //$NON-NLS-0$
+			html: "<div class='annotationHTML " + suffix + "'></div>", //$NON-NLS-1$ //$NON-NLS-0$
+			overviewStyle: {styleClass: "annotationOverview " + suffix} //$NON-NLS-0$
+		};
+		if (lineStyling) {
+			properties.lineStyle = {styleClass: "annotationLine " + suffix}; //$NON-NLS-0$
+		} else {
+			properties.rangeStyle = {styleClass: "annotationRange " + suffix}; //$NON-NLS-0$
+		}
+		AnnotationType.registerType(type, properties);
+	}
+	registerType(AnnotationType.ANNOTATION_ERROR);
+	registerType(AnnotationType.ANNOTATION_WARNING);
+	registerType(AnnotationType.ANNOTATION_TASK);
+	registerType(AnnotationType.ANNOTATION_BREAKPOINT);
+	registerType(AnnotationType.ANNOTATION_BOOKMARK);
+	registerType(AnnotationType.ANNOTATION_CURRENT_BRACKET);
+	registerType(AnnotationType.ANNOTATION_MATCHING_BRACKET);
+	registerType(AnnotationType.ANNOTATION_CURRENT_SEARCH);
+	registerType(AnnotationType.ANNOTATION_MATCHING_SEARCH);
+	registerType(AnnotationType.ANNOTATION_READ_OCCURRENCE);
+	registerType(AnnotationType.ANNOTATION_WRITE_OCCURRENCE);
+	registerType(AnnotationType.ANNOTATION_SELECTED_LINKED_GROUP);
+	registerType(AnnotationType.ANNOTATION_CURRENT_LINKED_GROUP);
+	registerType(AnnotationType.ANNOTATION_LINKED_GROUP);
+	registerType(AnnotationType.ANNOTATION_CURRENT_LINE, true);
+	AnnotationType.registerType(AnnotationType.ANNOTATION_FOLDING, FoldingAnnotation);
+	
+	/** 
+	 * Constructs a new AnnotationTypeList object.
+	 * 
+	 * @class This represents an interface of prioritized annotation types.
+	 * @name orion.editor.AnnotationTypeList
+	 */
+	function AnnotationTypeList () {
+	}
+	/**
+	 * Adds in the annotation type interface into the specified object.
+	 *
+	 * @param {Object} object The object to add in the annotation type interface.
+	 */
+	AnnotationTypeList.addMixin = function(object) {
+		var proto = AnnotationTypeList.prototype;
+		for (var p in proto) {
+			if (proto.hasOwnProperty(p)) {
+				object[p] = proto[p];
+			}
+		}
+	};	
+	AnnotationTypeList.prototype = /** @lends orion.editor.AnnotationTypeList.prototype */ {
+		/**
+		 * Adds an annotation type to the receiver.
+		 * <p>
+		 * Only annotations of the specified types will be shown by
+		 * the receiver.
+		 * </p>
+		 *
+		 * @param {Object} type the annotation type to be shown
+		 * 
+		 * @see #removeAnnotationType
+		 * @see #isAnnotationTypeVisible
+		 */
+		addAnnotationType: function(type) {
+			if (!this._annotationTypes) { this._annotationTypes = []; }
+			this._annotationTypes.push(type);
+		},
+		/**
+		 * Gets the annotation type priority.  The priority is determined by the
+		 * order the annotation type is added to the receiver.  Annotation types
+		 * added first have higher priority.
+		 * <p>
+		 * Returns <code>0</code> if the annotation type is not added.
+		 * </p>
+		 *
+		 * @param {Object} type the annotation type
+		 * 
+		 * @see #addAnnotationType
+		 * @see #removeAnnotationType
+		 * @see #isAnnotationTypeVisible
+		 */
+		getAnnotationTypePriority: function(type) {
+			if (this._annotationTypes) { 
+				for (var i = 0; i < this._annotationTypes.length; i++) {
+					if (this._annotationTypes[i] === type) {
+						return i + 1;
+					}
+				}
+			}
+			return 0;
+		},
+		/**
+		 * Returns an array of annotations in the specified annotation model for the given range of text sorted by type.
+		 *
+		 * @param {orion.editor.AnnotationModel} annotationModel the annotation model.
+		 * @param {Number} start the start offset of the range.
+		 * @param {Number} end the end offset of the range.
+		 * @return {orion.editor.Annotation[]} an annotation array.
+		 */
+		getAnnotationsByType: function(annotationModel, start, end) {
+			var iter = annotationModel.getAnnotations(start, end);
+			var annotation, annotations = [];
+			while (iter.hasNext()) {
+				annotation = iter.next();
+				var priority = this.getAnnotationTypePriority(annotation.type);
+				if (priority === 0) { continue; }
+				annotations.push(annotation);
+			}
+			var self = this;
+			annotations.sort(function(a, b) {
+				return self.getAnnotationTypePriority(a.type) - self.getAnnotationTypePriority(b.type);
+			});
+			return annotations;
+		},
+		/**
+		 * Returns whether the receiver shows annotations of the specified type.
+		 *
+		 * @param {Object} type the annotation type 
+		 * @returns {Boolean} whether the specified annotation type is shown
+		 * 
+		 * @see #addAnnotationType
+		 * @see #removeAnnotationType
+		 */
+		isAnnotationTypeVisible: function(type) {
+			return this.getAnnotationTypePriority(type) !== 0;
+		},
+		/**
+		 * Removes an annotation type from the receiver.
+		 *
+		 * @param {Object} type the annotation type to be removed
+		 * 
+		 * @see #addAnnotationType
+		 * @see #isAnnotationTypeVisible
+		 */
+		removeAnnotationType: function(type) {
+			if (!this._annotationTypes) { return; }
+			for (var i = 0; i < this._annotationTypes.length; i++) {
+				if (this._annotationTypes[i] === type) {
+					this._annotationTypes.splice(i, 1);
+					break;
+				}
+			}
+		}
+	};
+	
+	/**
+	 * Constructs an annotation model.
+	 * 
+	 * @param {textModel} textModel The text model.
+	 * 
+	 * @class This object manages annotations for a <code>TextModel</code>.
+	 * <p>
+	 * <b>See:</b><br/>
+	 * {@link orion.editor.Annotation}<br/>
+	 * {@link orion.editor.TextModel}<br/> 
+	 * </p>	
+	 * @name orion.editor.AnnotationModel
+	 * @borrows orion.editor.EventTarget#addEventListener as #addEventListener
+	 * @borrows orion.editor.EventTarget#removeEventListener as #removeEventListener
+	 * @borrows orion.editor.EventTarget#dispatchEvent as #dispatchEvent
+	 */
+	function AnnotationModel(textModel) {
+		this._annotations = [];
+		var self = this;
+		this._listener = {
+			onChanged: function(modelChangedEvent) {
+				self._onChanged(modelChangedEvent);
+			}
+		};
+		this.setTextModel(textModel);
+	}
+
+	AnnotationModel.prototype = /** @lends orion.editor.AnnotationModel.prototype */ {
+		/**
+		 * Adds an annotation to the annotation model. 
+		 * <p>The annotation model listeners are notified of this change.</p>
+		 * 
+		 * @param {orion.editor.Annotation} annotation the annotation to be added.
+		 * 
+		 * @see #removeAnnotation
+		 */
+		addAnnotation: function(annotation) {
+			if (!annotation) { return; }
+			var annotations = this._annotations;
+			var index = this._binarySearch(annotations, annotation.start);
+			annotations.splice(index, 0, annotation);
+			var e = {
+				type: "Changed", //$NON-NLS-0$
+				added: [annotation],
+				removed: [],
+				changed: []
+			};
+			this.onChanged(e);
+		},
+		/**
+		 * Returns the text model. 
+		 * 
+		 * @return {orion.editor.TextModel} The text model.
+		 * 
+		 * @see #setTextModel
+		 */
+		getTextModel: function() {
+			return this._model;
+		},
+		/**
+		 * @class This object represents an annotation iterator.
+		 * <p>
+		 * <b>See:</b><br/>
+		 * {@link orion.editor.AnnotationModel#getAnnotations}<br/>
+		 * </p>		 
+		 * @name orion.editor.AnnotationIterator
+		 * 
+		 * @property {Function} hasNext Determines whether there are more annotations in the iterator.
+		 * @property {Function} next Returns the next annotation in the iterator.
+		 */		
+		/**
+		 * Returns an iterator of annotations for the given range of text.
+		 *
+		 * @param {Number} start the start offset of the range.
+		 * @param {Number} end the end offset of the range.
+		 * @return {orion.editor.AnnotationIterator} an annotation iterartor.
+		 */
+		getAnnotations: function(start, end) {
+			var annotations = this._annotations, current;
+			//TODO binary search does not work for range intersection when there are overlaping ranges, need interval search tree for this
+			var i = 0;
+			var skip = function() {
+				while (i < annotations.length) {
+					var a =  annotations[i++];
+					if ((start === a.start) || (start > a.start ? start < a.end : a.start < end)) {
+						return a;
+					}
+					if (a.start >= end) {
+						break;
+					}
+				}
+				return null;
+			};
+			current = skip();
+			return {
+				next: function() {
+					var result = current;
+					if (result) { current = skip(); }
+					return result;					
+				},
+				hasNext: function() {
+					return current !== null;
+				}
+			};
+		},
+		/**
+		 * Notifies the annotation model that the given annotation has been modified.
+		 * <p>The annotation model listeners are notified of this change.</p>
+		 * 
+		 * @param {orion.editor.Annotation} annotation the modified annotation.
+		 * 
+		 * @see #addAnnotation
+		 */
+		modifyAnnotation: function(annotation) {
+			if (!annotation) { return; }
+			var index = this._getAnnotationIndex(annotation);
+			if (index < 0) { return; }
+			var e = {
+				type: "Changed", //$NON-NLS-0$
+				added: [],
+				removed: [],
+				changed: [annotation]
+			};
+			this.onChanged(e);
+		},
+		/**
+		 * Notifies all listeners that the annotation model has changed.
+		 *
+		 * @param {orion.editor.Annotation[]} added The list of annotation being added to the model.
+		 * @param {orion.editor.Annotation[]} changed The list of annotation modified in the model.
+		 * @param {orion.editor.Annotation[]} removed The list of annotation being removed from the model.
+		 * @param {ModelChangedEvent} textModelChangedEvent the text model changed event that trigger this change, can be null if the change was trigger by a method call (for example, {@link #addAnnotation}).
+		 */
+		onChanged: function(e) {
+			return this.dispatchEvent(e);
+		},
+		/**
+		 * Removes all annotations of the given <code>type</code>. All annotations
+		 * are removed if the type is not specified. 
+		 * <p>The annotation model listeners are notified of this change.  Only one changed event is generated.</p>
+		 * 
+		 * @param {Object} type the type of annotations to be removed.
+		 * 
+		 * @see #removeAnnotation
+		 */
+		removeAnnotations: function(type) {
+			var annotations = this._annotations;
+			var removed, i; 
+			if (type) {
+				removed = [];
+				for (i = annotations.length - 1; i >= 0; i--) {
+					var annotation = annotations[i];
+					if (annotation.type === type) {
+						annotations.splice(i, 1);
+					}
+					removed.splice(0, 0, annotation);
+				}
+			} else {
+				removed = annotations;
+				annotations = [];
+			}
+			var e = {
+				type: "Changed", //$NON-NLS-0$
+				removed: removed,
+				added: [],
+				changed: []
+			};
+			this.onChanged(e);
+		},
+		/**
+		 * Removes an annotation from the annotation model. 
+		 * <p>The annotation model listeners are notified of this change.</p>
+		 * 
+		 * @param {orion.editor.Annotation} annotation the annotation to be removed.
+		 * 
+		 * @see #addAnnotation
+		 */
+		removeAnnotation: function(annotation) {
+			if (!annotation) { return; }
+			var index = this._getAnnotationIndex(annotation);
+			if (index < 0) { return; }
+			var e = {
+				type: "Changed", //$NON-NLS-0$
+				removed: this._annotations.splice(index, 1),
+				added: [],
+				changed: []
+			};
+			this.onChanged(e);
+		},
+		/**
+		 * Removes and adds the specifed annotations to the annotation model. 
+		 * <p>The annotation model listeners are notified of this change.  Only one changed event is generated.</p>
+		 * 
+		 * @param {orion.editor.Annotation} remove the annotations to be removed.
+		 * @param {orion.editor.Annotation} add the annotations to be added.
+		 * 
+		 * @see #addAnnotation
+		 * @see #removeAnnotation
+		 */
+		replaceAnnotations: function(remove, add) {
+			var annotations = this._annotations, i, index, annotation, removed = [];
+			if (remove) {
+				for (i = remove.length - 1; i >= 0; i--) {
+					annotation = remove[i];
+					index = this._getAnnotationIndex(annotation);
+					if (index < 0) { continue; }
+					annotations.splice(index, 1);
+					removed.splice(0, 0, annotation);
+				}
+			}
+			if (!add) { add = []; }
+			for (i = 0; i < add.length; i++) {
+				annotation = add[i];
+				index = this._binarySearch(annotations, annotation.start);
+				annotations.splice(index, 0, annotation);
+			}
+			var e = {
+				type: "Changed", //$NON-NLS-0$
+				removed: removed,
+				added: add,
+				changed: []
+			};
+			this.onChanged(e);
+		},
+		/**
+		 * Sets the text model of the annotation model.  The annotation
+		 * model listens for changes in the text model to update and remove
+		 * annotations that are affected by the change.
+		 * 
+		 * @param {orion.editor.TextModel} textModel the text model.
+		 * 
+		 * @see #getTextModel
+		 */
+		setTextModel: function(textModel) {
+			if (this._model) {
+				this._model.removeEventListener("Changed", this._listener.onChanged); //$NON-NLS-0$
+			}
+			this._model = textModel;
+			if (this._model) {
+				this._model.addEventListener("Changed", this._listener.onChanged); //$NON-NLS-0$
+			}
+		},
+		/** @ignore */
+		_binarySearch: function (array, offset) {
+			var high = array.length, low = -1, index;
+			while (high - low > 1) {
+				index = Math.floor((high + low) / 2);
+				if (offset <= array[index].start) {
+					high = index;
+				} else {
+					low = index;
+				}
+			}
+			return high;
+		},
+		/** @ignore */
+		_getAnnotationIndex: function(annotation) {
+			var annotations = this._annotations;
+			var index = this._binarySearch(annotations, annotation.start);
+			while (index < annotations.length && annotations[index].start === annotation.start) {
+				if (annotations[index] === annotation) {
+					return index;
+				}
+				index++;
+			}
+			return -1;
+		},
+		/** @ignore */
+		_onChanged: function(modelChangedEvent) {
+			var start = modelChangedEvent.start;
+			var addedCharCount = modelChangedEvent.addedCharCount;
+			var removedCharCount = modelChangedEvent.removedCharCount;
+			var annotations = this._annotations, end = start + removedCharCount;
+			//TODO binary search does not work for range intersection when there are overlaping ranges, need interval search tree for this
+			var startIndex = 0;
+			if (!(0 <= startIndex && startIndex < annotations.length)) { return; }
+			var e = {
+				type: "Changed", //$NON-NLS-0$
+				added: [],
+				removed: [],
+				changed: [],
+				textModelChangedEvent: modelChangedEvent
+			};
+			var changeCount = addedCharCount - removedCharCount, i;
+			for (i = startIndex; i < annotations.length; i++) {
+				var annotation = annotations[i];
+				if (annotation.start >= end) {
+					annotation.start += changeCount;
+					annotation.end += changeCount;
+					e.changed.push(annotation);
+				} else if (annotation.end <= start) {
+					//nothing
+				} else if (annotation.start < start && end < annotation.end) {
+					annotation.end += changeCount;
+					e.changed.push(annotation);
+				} else {
+					annotations.splice(i, 1);
+					e.removed.push(annotation);
+					i--;
+				}
+			}
+			if (e.added.length > 0 || e.removed.length > 0 || e.changed.length > 0) {
+				this.onChanged(e);
+			}
+		}
+	};
+	mEventTarget.EventTarget.addMixin(AnnotationModel.prototype);
+
+	/**
+	 * Constructs a new styler for annotations.
+	 * 
+	 * @param {orion.editor.TextView} view The styler view.
+	 * @param {orion.editor.AnnotationModel} view The styler annotation model.
+	 * 
+	 * @class This object represents a styler for annotation attached to a text view.
+	 * @name orion.editor.AnnotationStyler
+	 * @borrows orion.editor.AnnotationTypeList#addAnnotationType as #addAnnotationType
+	 * @borrows orion.editor.AnnotationTypeList#getAnnotationTypePriority as #getAnnotationTypePriority
+	 * @borrows orion.editor.AnnotationTypeList#getAnnotationsByType as #getAnnotationsByType
+	 * @borrows orion.editor.AnnotationTypeList#isAnnotationTypeVisible as #isAnnotationTypeVisible
+	 * @borrows orion.editor.AnnotationTypeList#removeAnnotationType as #removeAnnotationType
+	 */
+	function AnnotationStyler (view, annotationModel) {
+		this._view = view;
+		this._annotationModel = annotationModel;
+		var self = this;
+		this._listener = {
+			onDestroy: function(e) {
+				self._onDestroy(e);
+			},
+			onLineStyle: function(e) {
+				self._onLineStyle(e);
+			},
+			onChanged: function(e) {
+				self._onAnnotationModelChanged(e);
+			}
+		};
+		view.addEventListener("Destroy", this._listener.onDestroy); //$NON-NLS-0$
+		view.addEventListener("postLineStyle", this._listener.onLineStyle); //$NON-NLS-0$
+		annotationModel.addEventListener("Changed", this._listener.onChanged); //$NON-NLS-0$
+	}
+	AnnotationStyler.prototype = /** @lends orion.editor.AnnotationStyler.prototype */ {
+		/**
+		 * Destroys the styler. 
+		 * <p>
+		 * Removes all listeners added by this styler.
+		 * </p>
+		 */
+		destroy: function() {
+			var view = this._view;
+			if (view) {
+				view.removeEventListener("Destroy", this._listener.onDestroy); //$NON-NLS-0$
+				view.removeEventListener("LineStyle", this._listener.onLineStyle); //$NON-NLS-0$
+				this.view = null;
+			}
+			var annotationModel = this._annotationModel;
+			if (annotationModel) {
+				annotationModel.removeEventListener("Changed", this._listener.onChanged); //$NON-NLS-0$
+				annotationModel = null;
+			}
+		},
+		_mergeStyle: function(result, style) {
+			if (style) {
+				if (!result) { result = {}; }
+				if (result.styleClass && style.styleClass && result.styleClass !== style.styleClass) {
+					result.styleClass += " " + style.styleClass; //$NON-NLS-0$
+				} else {
+					result.styleClass = style.styleClass;
+				}
+				var prop;
+				if (style.tagName) {
+					if (!result.tagName) {
+						result.tagName = style.tagName;
+					}
+				}
+				if (style.style) {
+					if (!result.style) { result.style  = {}; }
+					for (prop in style.style) {
+						if (!result.style[prop]) {
+							result.style[prop] = style.style[prop];
+						}
+					}
+				}
+				if (style.attributes) {
+					if (!result.attributes) { result.attributes  = {}; }
+					for (prop in style.attributes) {
+						if (!result.attributes[prop]) {
+							result.attributes[prop] = style.attributes[prop];
+						}
+					}
+				}
+			}
+			return result;
+		},
+		_mergeStyleRanges: function(ranges, styleRange) {
+			if (!ranges) {
+				ranges = [];
+			}
+			var mergedStyle, i;
+			for (i=0; i<ranges.length && styleRange; i++) {
+				var range = ranges[i];
+				if (styleRange.end <= range.start) { break; }
+				if (styleRange.start >= range.end) { continue; }
+				mergedStyle = this._mergeStyle({}, range.style);
+				mergedStyle = this._mergeStyle(mergedStyle, styleRange.style);
+				var args = [];
+				args.push(i, 1);
+				if (styleRange.start < range.start) {
+					args.push({start: styleRange.start, end: range.start, style: styleRange.style});
+				}
+				if (styleRange.start > range.start) {
+					args.push({start: range.start, end: styleRange.start, style: range.style});
+				}
+				args.push({start: Math.max(range.start, styleRange.start), end: Math.min(range.end, styleRange.end), style: mergedStyle});
+				if (styleRange.end < range.end) {
+					args.push({start: styleRange.end, end: range.end, style: range.style});
+				}
+				if (styleRange.end > range.end) {
+					styleRange = {start: range.end, end: styleRange.end, style: styleRange.style};
+				} else {
+					styleRange = null;
+				}
+				Array.prototype.splice.apply(ranges, args);
+			}
+			if (styleRange) {
+				mergedStyle = this._mergeStyle({}, styleRange.style);
+				ranges.splice(i, 0, {start: styleRange.start, end: styleRange.end, style: mergedStyle});
+			}
+			return ranges;
+		},
+		_onAnnotationModelChanged: function(e) {
+			if (e.textModelChangedEvent) {
+				return;
+			}
+			var view = this._view;
+			if (!view) { return; }
+			var self = this;
+			var model = view.getModel();
+			function redraw(changes) {
+				for (var i = 0; i < changes.length; i++) {
+					if (!self.isAnnotationTypeVisible(changes[i].type)) { continue; }
+					var start = changes[i].start;
+					var end = changes[i].end;
+					if (model.getBaseModel) {
+						start = model.mapOffset(start, true);
+						end = model.mapOffset(end, true);
+					}
+					if (start !== -1 && end !== -1) {
+						view.redrawRange(start, end);
+					}
+				}
+			}
+			redraw(e.added);
+			redraw(e.removed);
+			redraw(e.changed);
+		},
+		_onDestroy: function(e) {
+			this.destroy();
+		},
+		_onLineStyle: function (e) {
+			var annotationModel = this._annotationModel;
+			var viewModel = e.textView.getModel();
+			var baseModel = annotationModel.getTextModel();
+			var start = e.lineStart;
+			var end = e.lineStart + e.lineText.length;
+			if (baseModel !== viewModel) {
+				start = viewModel.mapOffset(start);
+				end = viewModel.mapOffset(end);
+			}
+			var annotations = annotationModel.getAnnotations(start, end);
+			while (annotations.hasNext()) {
+				var annotation = annotations.next();
+				if (!this.isAnnotationTypeVisible(annotation.type)) { continue; }
+				if (annotation.rangeStyle) {
+					var annotationStart = annotation.start;
+					var annotationEnd = annotation.end;
+					if (baseModel !== viewModel) {
+						annotationStart = viewModel.mapOffset(annotationStart, true);
+						annotationEnd = viewModel.mapOffset(annotationEnd, true);
+					}
+					e.ranges = this._mergeStyleRanges(e.ranges, {start: annotationStart, end: annotationEnd, style: annotation.rangeStyle});
+				}
+				if (annotation.lineStyle) {
+					e.style = this._mergeStyle({}, e.style);
+					e.style = this._mergeStyle(e.style, annotation.lineStyle);
+				}
+			}
+		}
+	};
+	AnnotationTypeList.addMixin(AnnotationStyler.prototype);
+	
+	return {
+		FoldingAnnotation: FoldingAnnotation,
+		AnnotationType: AnnotationType,
+		AnnotationTypeList: AnnotationTypeList,
+		AnnotationModel: AnnotationModel,
+		AnnotationStyler: AnnotationStyler
+	};
+});
+
+/*******************************************************************************
+ * @license
+ * Copyright (c) 2010, 2012 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials are made 
+ * available under the terms of the Eclipse Public License v1.0 
+ * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
+ * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
+ * 
+ * Contributors: IBM Corporation - initial API and implementation
+ ******************************************************************************/
+
+/*global define Node */
+
+define("orion/editor/tooltip", [ //$NON-NLS-0$
+	'i18n!orion/editor/nls/messages', //$NON-NLS-0$
+	'orion/editor/textView', //$NON-NLS-0$
+	'orion/editor/textModel', //$NON-NLS-0$
+	'orion/editor/projectionTextModel', //$NON-NLS-0$
+	'orion/util' //$NON-NLS-0$
+], function(messages, mTextView, mTextModel, mProjectionTextModel, util) {
+
+	/** @private */
+	function Tooltip (view) {
+		this._view = view;
+		this._create(view.getOptions("parent").ownerDocument); //$NON-NLS-0$
+		view.addEventListener("Destroy", this, this.destroy); //$NON-NLS-0$
+	}
+	Tooltip.getTooltip = function(view) {
+		if (!view._tooltip) {
+			 view._tooltip = new Tooltip(view);
+		}
+		return view._tooltip;
+	};
+	Tooltip.prototype = /** @lends orion.editor.Tooltip.prototype */ {
+		_create: function(document) {
+			if (this._tooltipDiv) { return; }
+			var tooltipDiv = this._tooltipDiv = util.createElement(document, "div"); //$NON-NLS-0$
+			tooltipDiv.className = "textviewTooltip"; //$NON-NLS-0$
+			tooltipDiv.setAttribute("aria-live", "assertive"); //$NON-NLS-1$ //$NON-NLS-0$
+			tooltipDiv.setAttribute("aria-atomic", "true"); //$NON-NLS-1$ //$NON-NLS-0$
+			var tooltipContents = this._tooltipContents = util.createElement(document, "div"); //$NON-NLS-0$
+			tooltipDiv.appendChild(tooltipContents);
+			document.body.appendChild(tooltipDiv);
+			this.hide();
+		},
+		_getWindow: function() {
+			var document = this._tooltipDiv.ownerDocument;
+			return document.defaultView || document.parentWindow;
+		},
+		destroy: function() {
+			if (!this._tooltipDiv) { return; }
+			this.hide();
+			var parent = this._tooltipDiv.parentNode;
+			if (parent) { parent.removeChild(this._tooltipDiv); }
+			this._tooltipDiv = null;
+		},
+		hide: function() {
+			if (this._contentsView) {
+				this._contentsView.destroy();
+				this._contentsView = null;
+			}
+			if (this._tooltipContents) {
+				this._tooltipContents.innerHTML = "";
+			}
+			if (this._tooltipDiv) {
+				this._tooltipDiv.style.visibility = "hidden"; //$NON-NLS-0$
+			}
+			var window = this._getWindow();
+			if (this._showTimeout) {
+				window.clearTimeout(this._showTimeout);
+				this._showTimeout = null;
+			}
+			if (this._hideTimeout) {
+				window.clearTimeout(this._hideTimeout);
+				this._hideTimeout = null;
+			}
+			if (this._fadeTimeout) {
+				window.clearInterval(this._fadeTimeout);
+				this._fadeTimeout = null;
+			}
+		},
+		isVisible: function() {
+			return this._tooltipDiv && this._tooltipDiv.style.visibility === "visible"; //$NON-NLS-0$
+		},
+		setTarget: function(target, delay) {
+			if (this.target === target) { return; }
+			this._target = target;
+			this.hide();
+			if (target) {
+				var self = this;
+				if(delay === 0) {
+					self.show(true);
+				}
+				else {
+				var window = this._getWindow();
+					self._showTimeout = window.setTimeout(function() {
+						self.show(true);
+					}, delay ? delay : 500);
+				}
+			}
+		},
+		show: function(autoHide) {
+			if (!this._target) { return; }
+			var info = this._target.getTooltipInfo();
+			if (!info) { return; }
+			var tooltipDiv = this._tooltipDiv, tooltipContents = this._tooltipContents;
+			tooltipDiv.style.left = tooltipDiv.style.right = tooltipDiv.style.width = tooltipDiv.style.height = 
+				tooltipContents.style.width = tooltipContents.style.height = "auto"; //$NON-NLS-0$
+			var contents = info.contents;
+			if (contents instanceof Array) {
+				contents = this._getAnnotationContents(contents);
+			}
+			if (typeof contents === "string") { //$NON-NLS-0$
+				tooltipContents.innerHTML = contents;
+			} else if (this._isNode(contents)) {
+				tooltipContents.appendChild(contents);
+			} else if (contents instanceof mProjectionTextModel.ProjectionTextModel) {
+				var view = this._view;
+				var options = view.getOptions();
+				options.wrapMode = false;
+				options.parent = tooltipContents;
+				var tooltipTheme = "tooltip"; //$NON-NLS-0$
+				var theme = options.themeClass;
+				if (theme) {
+					theme = theme.replace(tooltipTheme, "");
+					if (theme) { theme = " " + theme; } //$NON-NLS-0$
+					theme = tooltipTheme + theme;
+				} else {
+					theme = tooltipTheme;
+				}
+				options.themeClass = theme;
+				var contentsView = this._contentsView = new mTextView.TextView(options);
+				//TODO this is need to avoid Firefox from getting focus
+				contentsView._clientDiv.contentEditable = false;
+				//TODO need to find a better way of sharing the styler for multiple views
+				var listener = {
+					onLineStyle: function(e) {
+						view.onLineStyle(e);
+					}
+				};
+				contentsView.addEventListener("LineStyle", listener.onLineStyle); //$NON-NLS-0$
+				contentsView.setModel(contents);
+				var size = contentsView.computeSize();
+				tooltipContents.style.width = size.width + "px"; //$NON-NLS-0$
+				tooltipContents.style.height = size.height + "px"; //$NON-NLS-0$
+				contentsView.resize();
+			} else {
+				return;
+			}
+			var documentElement = tooltipDiv.ownerDocument.documentElement;
+			if (info.anchor === "right") { //$NON-NLS-0$
+				var right = documentElement.clientWidth - info.x;
+				tooltipDiv.style.right = right + "px"; //$NON-NLS-0$
+				tooltipDiv.style.maxWidth = (documentElement.clientWidth - right - 10) + "px"; //$NON-NLS-0$
+			} else {
+				var left = parseInt(this._getNodeStyle(tooltipDiv, "padding-left", "0"), 10); //$NON-NLS-1$ //$NON-NLS-0$
+				left += parseInt(this._getNodeStyle(tooltipDiv, "border-left-width", "0"), 10); //$NON-NLS-1$ //$NON-NLS-0$
+				left = info.x - left;
+				tooltipDiv.style.left = left + "px"; //$NON-NLS-0$
+				tooltipDiv.style.maxWidth = (documentElement.clientWidth - left - 10) + "px"; //$NON-NLS-0$
+			}
+			var top = parseInt(this._getNodeStyle(tooltipDiv, "padding-top", "0"), 10); //$NON-NLS-1$ //$NON-NLS-0$
+			top += parseInt(this._getNodeStyle(tooltipDiv, "border-top-width", "0"), 10); //$NON-NLS-1$ //$NON-NLS-0$
+			top = info.y - top;
+			tooltipDiv.style.top = top + "px"; //$NON-NLS-0$
+			tooltipDiv.style.maxHeight = (documentElement.clientHeight - top - 10) + "px"; //$NON-NLS-0$
+			tooltipDiv.style.opacity = "1"; //$NON-NLS-0$
+			tooltipDiv.style.visibility = "visible"; //$NON-NLS-0$
+			if (autoHide) {
+				var self = this;
+				var window = this._getWindow();
+				self._hideTimeout = window.setTimeout(function() {
+					var opacity = parseFloat(self._getNodeStyle(tooltipDiv, "opacity", "1")); //$NON-NLS-1$ //$NON-NLS-0$
+					self._fadeTimeout = window.setInterval(function() {
+						if (tooltipDiv.style.visibility === "visible" && opacity > 0) { //$NON-NLS-0$
+							opacity -= 0.1;
+							tooltipDiv.style.opacity = opacity;
+							return;
+						}
+						self.hide();
+					}, 50);
+				}, 5000);
+			}
+		},
+		_getAnnotationContents: function(annotations) {
+			if (annotations.length === 0) {
+				return null;
+			}
+			var model = this._view.getModel(), annotation;
+			var baseModel = model.getBaseModel ? model.getBaseModel() : model;
+			function getText(start, end) {
+				var textStart = baseModel.getLineStart(baseModel.getLineAtOffset(start));
+				var textEnd = baseModel.getLineEnd(baseModel.getLineAtOffset(end), true);
+				return baseModel.getText(textStart, textEnd);
+			}
+			function getAnnotationHTML(annotation) {
+				var title = annotation.title;
+				if (title === "") { return null; }
+				var result = "<div>"; //$NON-NLS-0$
+				if (annotation.html) {
+					result += annotation.html + "&nbsp;"; //$NON-NLS-0$
+				}
+				if (!title) {
+					title = getText(annotation.start, annotation.end);
+				}
+				title = title.replace(/</g, "&lt;").replace(/>/g, "&gt;"); //$NON-NLS-1$ //$NON-NLS-0$
+				result += "<span style='vertical-align:middle;'>" + title + "</span><div>"; //$NON-NLS-1$ //$NON-NLS-0$
+				return result;
+			}
+			if (annotations.length === 1) {
+				annotation = annotations[0];
+				if (annotation.title !== undefined) {
+					return getAnnotationHTML(annotation);
+				} else {
+					var newModel = new mProjectionTextModel.ProjectionTextModel(baseModel);
+					var lineStart = baseModel.getLineStart(baseModel.getLineAtOffset(annotation.start));
+					var charCount = baseModel.getCharCount();
+					if (annotation.end !== charCount) {
+						newModel.addProjection({start: annotation.end, end: charCount});
+					}
+					if (lineStart > 0) {
+						newModel.addProjection({start: 0, end: lineStart});
+					}
+					return newModel;
+				}
+			} else {
+				var tooltipHTML = "<div><em>" + messages.multipleAnnotations + "</em></div>"; //$NON-NLS-1$ //$NON-NLS-0$
+				for (var i = 0; i < annotations.length; i++) {
+					annotation = annotations[i];
+					var html = getAnnotationHTML(annotation);
+					if (html) {
+						tooltipHTML += html;
+					}
+				}
+				return tooltipHTML;
+			}
+		},
+		_getNodeStyle: function(node, prop, defaultValue) {
+			var value;
+			if (node) {
+				value = node.style[prop];
+				if (!value) {
+					if (node.currentStyle) {
+						var index = 0, p = prop;
+						while ((index = p.indexOf("-", index)) !== -1) { //$NON-NLS-0$
+							p = p.substring(0, index) + p.substring(index + 1, index + 2).toUpperCase() + p.substring(index + 2);
+						}
+						value = node.currentStyle[p];
+					} else {
+						var css = node.ownerDocument.defaultView.getComputedStyle(node, null);
+						value = css ? css.getPropertyValue(prop) : null;
+					}
+				}
+			}
+			return value || defaultValue;
+		},
+		_isNode: function (obj) {
+			return typeof Node === "object" ? obj instanceof Node : //$NON-NLS-0$
+				obj && typeof obj === "object" && typeof obj.nodeType === "number" && typeof obj.nodeName === "string"; //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		}
+	};
+	return {Tooltip: Tooltip};
+});
+
+/*******************************************************************************
+ * @license
+ * Copyright (c) 2010, 2012 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials are made 
+ * available under the terms of the Eclipse Public License v1.0 
+ * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
+ * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
+ * 
+ * Contributors: IBM Corporation - initial API and implementation
+ ******************************************************************************/
+
+/*global define*/
+
+define("orion/editor/rulers", ['i18n!orion/editor/nls/messages', 'orion/editor/annotations', 'orion/editor/tooltip', 'orion/util'], function(messages, mAnnotations, mTooltip, util) { //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+
+	/**
+	 * Constructs a new ruler. 
+	 * <p>
+	 * The default implementation does not implement all the methods in the interface
+	 * and is useful only for objects implementing rulers.
+	 * <p/>
+	 * 
+	 * @param {orion.editor.AnnotationModel} annotationModel the annotation model for the ruler.
+	 * @param {String} [rulerLocation="left"] the location for the ruler.
+	 * @param {String} [rulerOverview="page"] the overview for the ruler.
+	 * @param {orion.editor.Style} [rulerStyle] the style for the ruler. 
+	 * 
+	 * @class This interface represents a ruler for the text view.
+	 * <p>
+	 * A Ruler is a graphical element that is placed either on the left or on the right side of 
+	 * the view. It can be used to provide the view with per line decoration such as line numbering,
+	 * bookmarks, breakpoints, folding disclosures, etc. 
+	 * </p><p>
+	 * There are two types of rulers: page and document. A page ruler only shows the content for the lines that are
+	 * visible, while a document ruler always shows the whole content.
+	 * </p>
+	 * <b>See:</b><br/>
+	 * {@link orion.editor.LineNumberRuler}<br/>
+	 * {@link orion.editor.AnnotationRuler}<br/>
+	 * {@link orion.editor.OverviewRuler}<br/> 
+	 * {@link orion.editor.TextView}<br/>
+	 * {@link orion.editor.TextView#addRuler}
+	 * </p>		 
+	 * @name orion.editor.Ruler
+	 * @borrows orion.editor.AnnotationTypeList#addAnnotationType as #addAnnotationType
+	 * @borrows orion.editor.AnnotationTypeList#getAnnotationTypePriority as #getAnnotationTypePriority
+	 * @borrows orion.editor.AnnotationTypeList#getAnnotationsByType as #getAnnotationsByType
+	 * @borrows orion.editor.AnnotationTypeList#isAnnotationTypeVisible as #isAnnotationTypeVisible
+	 * @borrows orion.editor.AnnotationTypeList#removeAnnotationType as #removeAnnotationType
+	 */
+	function Ruler (annotationModel, rulerLocation, rulerOverview, rulerStyle) {
+		this._location = rulerLocation || "left"; //$NON-NLS-0$
+		this._overview = rulerOverview || "page"; //$NON-NLS-0$
+		this._rulerStyle = rulerStyle;
+		this._view = null;
+		var self = this;
+		this._listener = {
+			onTextModelChanged: function(e) {
+				self._onTextModelChanged(e);
+			},
+			onAnnotationModelChanged: function(e) {
+				self._onAnnotationModelChanged(e);
+			}
+		};
+		this.setAnnotationModel(annotationModel);
+	}
+	Ruler.prototype = /** @lends orion.editor.Ruler.prototype */ {
+		/**
+		 * Returns the annotations for a given line range merging multiple
+		 * annotations when necessary.
+		 * <p>
+		 * This method is called by the text view when the ruler is redrawn.
+		 * </p>
+		 *
+		 * @param {Number} startLine the start line index
+		 * @param {Number} endLine the end line index
+		 * @return {orion.editor.Annotation[]} the annotations for the line range. The array might be sparse.
+		 */
+		getAnnotations: function(startLine, endLine) {
+			var annotationModel = this._annotationModel;
+			if (!annotationModel) { return []; }
+			var model = this._view.getModel();
+			var start = model.getLineStart(startLine);
+			var end = model.getLineEnd(endLine - 1);
+			var baseModel = model;
+			if (model.getBaseModel) {
+				baseModel = model.getBaseModel();
+				start = model.mapOffset(start);
+				end = model.mapOffset(end);
+			}
+			var result = [];
+			var annotations = this.getAnnotationsByType(annotationModel, start, end);
+			for (var i = 0; i < annotations.length; i++) {
+				var annotation = annotations[i];
+				var annotationLineStart = baseModel.getLineAtOffset(annotation.start);
+				var annotationLineEnd = baseModel.getLineAtOffset(Math.max(annotation.start, annotation.end - 1));
+				for (var lineIndex = annotationLineStart; lineIndex<=annotationLineEnd; lineIndex++) {
+					var visualLineIndex = lineIndex;
+					if (model !== baseModel) {
+						var ls = baseModel.getLineStart(lineIndex);
+						ls = model.mapOffset(ls, true);
+						if (ls === -1) { continue; }
+						visualLineIndex = model.getLineAtOffset(ls);
+					}
+					if (!(startLine <= visualLineIndex && visualLineIndex < endLine)) { continue; }
+					var rulerAnnotation = this._mergeAnnotation(result[visualLineIndex], annotation, lineIndex - annotationLineStart, annotationLineEnd - annotationLineStart + 1);
+					if (rulerAnnotation) {
+						result[visualLineIndex] = rulerAnnotation;
+					}
+				}
+			}
+			if (!this._multiAnnotation && this._multiAnnotationOverlay) {
+				for (var k in result) {
+					if (result[k]._multiple) {
+						result[k].html = result[k].html + this._multiAnnotationOverlay.html;
+					}
+				}
+			}
+			return result;
+		},
+		/**
+		 * Returns the annotation model.
+		 *
+		 * @returns {orion.editor.AnnotationModel} the ruler annotation model.
+		 *
+		 * @see #setAnnotationModel
+		 */
+		getAnnotationModel: function() {
+			return this._annotationModel;
+		},
+		/**
+		 * Returns the ruler location.
+		 *
+		 * @returns {String} the ruler location, which is either "left" or "right".
+		 *
+		 * @see #getOverview
+		 */
+		getLocation: function() {
+			return this._location;
+		},
+		/**
+		 * Returns the ruler overview type.
+		 *
+		 * @returns {String} the overview type, which is either "page" or "document".
+		 *
+		 * @see #getLocation
+		 */
+		getOverview: function() {
+			return this._overview;
+		},
+		/**
+		 * Returns the style information for the ruler.
+		 *
+		 * @returns {orion.editor.Style} the style information.
+		 */
+		getRulerStyle: function() {
+			return this._rulerStyle;
+		},
+		/**
+		 * Returns the text view.
+		 *
+		 * @returns {orion.editor.TextView} the text view.
+		 *
+		 * @see #setView
+		 */
+		getView: function() {
+			return this._view;
+		},
+		/**
+		 * Returns the widest annotation which determines the width of the ruler.
+		 * <p>
+		 * If the ruler does not have a fixed width it should provide the widest
+		 * annotation to avoid the ruler from changing size as the view scrolls.
+		 * </p>
+		 * <p>
+		 * This method is called by the text view when the ruler is redrawn.
+		 * </p>
+		 *
+		 * @returns {orion.editor.Annotation} the widest annotation.
+		 *
+		 * @see #getAnnotations
+		 */
+		getWidestAnnotation: function() {
+			return null;
+		},
+		/**
+		 * Sets the annotation model for the ruler.
+		 *
+		 * @param {orion.editor.AnnotationModel} annotationModel the annotation model.
+		 *
+		 * @see #getAnnotationModel
+		 */
+		setAnnotationModel: function (annotationModel) {
+			if (this._annotationModel) {
+				this._annotationModel.removEventListener("Changed", this._listener.onAnnotationModelChanged); //$NON-NLS-0$
+			}
+			this._annotationModel = annotationModel;
+			if (this._annotationModel) {
+				this._annotationModel.addEventListener("Changed", this._listener.onAnnotationModelChanged); //$NON-NLS-0$
+			}
+		},
+		/**
+		 * Sets the annotation that is displayed when a given line contains multiple
+		 * annotations.  This annotation is used when there are different types of
+		 * annotations in a given line.
+		 *
+		 * @param {orion.editor.Annotation} annotation the annotation for lines with multiple annotations.
+		 * 
+		 * @see #setMultiAnnotationOverlay
+		 */
+		setMultiAnnotation: function(annotation) {
+			this._multiAnnotation = annotation;
+		},
+		/**
+		 * Sets the annotation that overlays a line with multiple annotations.  This annotation is displayed on
+		 * top of the computed annotation for a given line when there are multiple annotations of the same type
+		 * in the line. It is also used when the multiple annotation is not set.
+		 *
+		 * @param {orion.editor.Annotation} annotation the annotation overlay for lines with multiple annotations.
+		 * 
+		 * @see #setMultiAnnotation
+		 */
+		setMultiAnnotationOverlay: function(annotation) {
+			this._multiAnnotationOverlay = annotation;
+		},
+		/**
+		 * Sets the view for the ruler.
+		 * <p>
+		 * This method is called by the text view when the ruler
+		 * is added to the view.
+		 * </p>
+		 *
+		 * @param {orion.editor.TextView} view the text view.
+		 */
+		setView: function (view) {
+			if (this._onTextModelChanged && this._view) {
+				this._view.removeEventListener("ModelChanged", this._listener.onTextModelChanged); //$NON-NLS-0$
+			}
+			this._view = view;
+			if (this._onTextModelChanged && this._view) {
+				this._view.addEventListener("ModelChanged", this._listener.onTextModelChanged); //$NON-NLS-0$
+			}
+		},
+		/**
+		 * This event is sent when the user clicks a line annotation.
+		 *
+		 * @event
+		 * @param {Number} lineIndex the line index of the annotation under the pointer.
+		 * @param {DOMEvent} e the click event.
+		 */
+		onClick: function(lineIndex, e) {
+			if (lineIndex === undefined) { return; }
+			var view = this._view;
+			var model = view.getModel();
+			var baseModel = model;
+			var start = model.getLineStart(lineIndex);
+			var end = start;
+			var annotationModel = this._annotationModel;
+			if (annotationModel) {
+				var selection = view.getSelection();
+				var offset = Math.max(selection.start, selection.end);
+				end = model.getLineEnd(lineIndex, true);
+				if (start <= offset && offset < model.getLineEnd(lineIndex)) {
+					start = offset + 1;
+				}
+				if (model.getBaseModel) {
+					start = model.mapOffset(start);
+					end = model.mapOffset(end);
+					baseModel = model.getBaseModel();
+				}
+				var annotation, iter = annotationModel.getAnnotations(start, end);
+				while (!annotation && iter.hasNext()) {
+					var a = iter.next();
+					if (!this.isAnnotationTypeVisible(a.type)) { continue; }
+					annotation = a;
+				}
+				if (annotation && baseModel.getLineAtOffset(annotation.start) === baseModel.getLineAtOffset(start)) {
+					start = annotation.start;
+					end = annotation.end;
+				} else {
+					end = start;
+				}
+				
+				if (model.getBaseModel) {
+					start = model.mapOffset(start, true);
+					end = model.mapOffset(end, true);
+				}
+			}
+			var tooltip = mTooltip.Tooltip.getTooltip(this._view);
+			if (tooltip) {
+				tooltip.setTarget(null);
+			}
+			this._view.setSelection(end, start, 1/3, function(){});
+		},
+		/**
+		 * This event is sent when the user double clicks a line annotation.
+		 *
+		 * @event
+		 * @param {Number} lineIndex the line index of the annotation under the pointer.
+		 * @param {DOMEvent} e the double click event.
+		 */
+		onDblClick: function(lineIndex, e) {
+		},
+		/**
+		 * This event is sent when the user moves the mouse over a line annotation.
+		 *
+		 * @event
+		 * @param {Number} lineIndex the line index of the annotation under the pointer.
+		 * @param {DOMEvent} e the mouse move event.
+		 */
+		onMouseMove: function(lineIndex, e) {
+			var tooltip = mTooltip.Tooltip.getTooltip(this._view);
+			if (!tooltip) { return; }
+			if (tooltip.isVisible() && this._tooltipLineIndex === lineIndex) { return; }
+			this._tooltipLineIndex = lineIndex;
+			var self = this;
+			tooltip.setTarget({
+				y: e.clientY,
+				getTooltipInfo: function() {
+					return self._getTooltipInfo(self._tooltipLineIndex, this.y);
+				}
+			});
+		},
+		/**
+		 * This event is sent when the mouse pointer enters a line annotation.
+		 *
+		 * @event
+		 * @param {Number} lineIndex the line index of the annotation under the pointer.
+		 * @param {DOMEvent} e the mouse over event.
+		 */
+		onMouseOver: function(lineIndex, e) {
+			this.onMouseMove(lineIndex, e);
+		},
+		/**
+		 * This event is sent when the mouse pointer exits a line annotation.
+		 *
+		 * @event
+		 * @param {Number} lineIndex the line index of the annotation under the pointer.
+		 * @param {DOMEvent} e the mouse out event.
+		 */
+		onMouseOut: function(lineIndex, e) {
+			var tooltip = mTooltip.Tooltip.getTooltip(this._view);
+			if (!tooltip) { return; }
+			tooltip.setTarget(null);
+		},
+		/** @ignore */
+		_getTooltipInfo: function(lineIndex, y) {
+			if (lineIndex === undefined) { return; }
+			var view = this._view;
+			var model = view.getModel();
+			var annotationModel = this._annotationModel;
+			var annotations = [];
+			if (annotationModel) {
+				var start = model.getLineStart(lineIndex);
+				var end = model.getLineEnd(lineIndex);
+				if (model.getBaseModel) {
+					start = model.mapOffset(start);
+					end = model.mapOffset(end);
+				}
+				annotations = this.getAnnotationsByType(annotationModel, start, end);
+			}
+			var contents = this._getTooltipContents(lineIndex, annotations);
+			if (!contents) { return null; }
+			var info = {
+				contents: contents,
+				anchor: this.getLocation()
+			};
+			var rect = view.getClientArea();
+			if (this.getOverview() === "document") { //$NON-NLS-0$
+				rect.y = view.convert({y: y}, "view", "document").y; //$NON-NLS-1$ //$NON-NLS-0$
+			} else {
+				rect.y = view.getLocationAtOffset(model.getLineStart(lineIndex)).y;
+			}
+			view.convert(rect, "document", "page"); //$NON-NLS-1$ //$NON-NLS-0$
+			info.x = rect.x;
+			info.y = rect.y;
+			if (info.anchor === "right") { //$NON-NLS-0$
+				info.x += rect.width;
+			}
+			return info;
+		},
+		/** @ignore */
+		_getTooltipContents: function(lineIndex, annotations) {
+			return annotations;
+		},
+		/** @ignore */
+		_onAnnotationModelChanged: function(e) {
+			var view = this._view;
+			if (!view) { return; }
+			var model = view.getModel(), self = this;
+			var lineCount = model.getLineCount();
+			if (e.textModelChangedEvent) {
+				var start = e.textModelChangedEvent.start;
+				if (model.getBaseModel) { start = model.mapOffset(start, true); }
+				var startLine = model.getLineAtOffset(start);
+				view.redrawLines(startLine, lineCount, self);
+				return;
+			}
+			function redraw(changes) {
+				for (var i = 0; i < changes.length; i++) {
+					if (!self.isAnnotationTypeVisible(changes[i].type)) { continue; }
+					var start = changes[i].start;
+					var end = changes[i].end;
+					if (model.getBaseModel) {
+						start = model.mapOffset(start, true);
+						end = model.mapOffset(end, true);
+					}
+					if (start !== -1 && end !== -1) {
+						view.redrawLines(model.getLineAtOffset(start), model.getLineAtOffset(Math.max(start, end - 1)) + 1, self);
+					}
+				}
+			}
+			redraw(e.added);
+			redraw(e.removed);
+			redraw(e.changed);
+		},
+		/** @ignore */
+		_mergeAnnotation: function(result, annotation, annotationLineIndex, annotationLineCount) {
+			if (!result) { result = {}; }
+			if (annotationLineIndex === 0) {
+				if (result.html && annotation.html) {
+					if (annotation.html !== result.html) {
+						if (!result._multiple && this._multiAnnotation) {
+							result.html = this._multiAnnotation.html;
+						}
+					} 
+					result._multiple = true;
+				} else {
+					result.html = annotation.html;
+				}
+			}
+			result.style = this._mergeStyle(result.style, annotation.style);
+			return result;
+		},
+		/** @ignore */
+		_mergeStyle: function(result, style) {
+			if (style) {
+				if (!result) { result = {}; }
+				if (result.styleClass && style.styleClass && result.styleClass !== style.styleClass) {
+					result.styleClass += " " + style.styleClass; //$NON-NLS-0$
+				} else {
+					result.styleClass = style.styleClass;
+				}
+				var prop;
+				if (style.style) {
+					if (!result.style) { result.style  = {}; }
+					for (prop in style.style) {
+						if (!result.style[prop]) {
+							result.style[prop] = style.style[prop];
+						}
+					}
+				}
+				if (style.attributes) {
+					if (!result.attributes) { result.attributes  = {}; }
+					for (prop in style.attributes) {
+						if (!result.attributes[prop]) {
+							result.attributes[prop] = style.attributes[prop];
+						}
+					}
+				}
+			}
+			return result;
+		}
+	};
+	mAnnotations.AnnotationTypeList.addMixin(Ruler.prototype);
+
+	/**
+	 * Constructs a new line numbering ruler. 
+	 *
+	 * @param {orion.editor.AnnotationModel} annotationModel the annotation model for the ruler.
+	 * @param {String} [rulerLocation="left"] the location for the ruler.
+	 * @param {orion.editor.Style} [rulerStyle=undefined] the style for the ruler.
+	 * @param {orion.editor.Style} [oddStyle={style: {backgroundColor: "white"}] the style for lines with odd line index.
+	 * @param {orion.editor.Style} [evenStyle={backgroundColor: "white"}] the style for lines with even line index.
+	 *
+	 * @augments orion.editor.Ruler
+	 * @class This objects implements a line numbering ruler.
+	 *
+	 * <p><b>See:</b><br/>
+	 * {@link orion.editor.Ruler}
+	 * </p>
+	 * @name orion.editor.LineNumberRuler
+	 */
+	function LineNumberRuler (annotationModel, rulerLocation, rulerStyle, oddStyle, evenStyle) {
+		Ruler.call(this, annotationModel, rulerLocation, "page", rulerStyle); //$NON-NLS-0$
+		this._oddStyle = oddStyle || {style: {backgroundColor: "white"}}; //$NON-NLS-0$
+		this._evenStyle = evenStyle || {style: {backgroundColor: "white"}}; //$NON-NLS-0$
+		this._numOfDigits = 0;
+	}
+	LineNumberRuler.prototype = new Ruler(); 
+	/** @ignore */
+	LineNumberRuler.prototype.getAnnotations = function(startLine, endLine) {
+		var result = Ruler.prototype.getAnnotations.call(this, startLine, endLine);
+		var model = this._view.getModel();
+		for (var lineIndex = startLine; lineIndex < endLine; lineIndex++) {
+			var style = lineIndex & 1 ? this._oddStyle : this._evenStyle;
+			var mapLine = lineIndex;
+			if (model.getBaseModel) {
+				var lineStart = model.getLineStart(mapLine);
+				mapLine = model.getBaseModel().getLineAtOffset(model.mapOffset(lineStart));
+			}
+			if (!result[lineIndex]) { result[lineIndex] = {}; }
+			result[lineIndex].html = (mapLine + 1) + "";
+			if (!result[lineIndex].style) { result[lineIndex].style = style; }
+		}
+		return result;
+	};
+	/** @ignore */
+	LineNumberRuler.prototype.getWidestAnnotation = function() {
+		var lineCount = this._view.getModel().getLineCount();
+		return this.getAnnotations(lineCount - 1, lineCount)[lineCount - 1];
+	};
+	/** @ignore */
+	LineNumberRuler.prototype._onTextModelChanged = function(e) {
+		var start = e.start;
+		var model = this._view.getModel();
+		var lineCount = model.getBaseModel ? model.getBaseModel().getLineCount() : model.getLineCount();
+		var numOfDigits = (lineCount+"").length;
+		if (this._numOfDigits !== numOfDigits) {
+			this._numOfDigits = numOfDigits;
+			var startLine = model.getLineAtOffset(start);
+			this._view.redrawLines(startLine,  model.getLineCount(), this);
+		}
+	};
+	
+	/** 
+	 * @class This is class represents an annotation for the AnnotationRuler. 
+	 * <p> 
+	 * <b>See:</b><br/> 
+	 * {@link orion.editor.AnnotationRuler}
+	 * </p> 
+	 * 
+	 * @name orion.editor.Annotation 
+	 * 
+	 * @property {String} [html=""] The html content for the annotation, typically contains an image.
+	 * @property {orion.editor.Style} [style] the style for the annotation.
+	 * @property {orion.editor.Style} [overviewStyle] the style for the annotation in the overview ruler.
+	 */ 
+	/**
+	 * Constructs a new annotation ruler. 
+	 *
+	 * @param {orion.editor.AnnotationModel} annotationModel the annotation model for the ruler.
+	 * @param {String} [rulerLocation="left"] the location for the ruler.
+	 * @param {orion.editor.Style} [rulerStyle=undefined] the style for the ruler.
+	 * @param {orion.editor.Annotation} [defaultAnnotation] the default annotation.
+	 *
+	 * @augments orion.editor.Ruler
+	 * @class This objects implements an annotation ruler.
+	 *
+	 * <p><b>See:</b><br/>
+	 * {@link orion.editor.Ruler}<br/>
+	 * {@link orion.editor.Annotation}
+	 * </p>
+	 * @name orion.editor.AnnotationRuler
+	 */
+	function AnnotationRuler (annotationModel, rulerLocation, rulerStyle) {
+		Ruler.call(this, annotationModel, rulerLocation, "page", rulerStyle); //$NON-NLS-0$
+	}
+	AnnotationRuler.prototype = new Ruler();
+	
+	/**
+	 * Constructs a new overview ruler. 
+	 * <p>
+	 * The overview ruler is used in conjunction with a AnnotationRuler, for each annotation in the 
+	 * AnnotationRuler this ruler displays a mark in the overview. Clicking on the mark causes the 
+	 * view to scroll to the annotated line.
+	 * </p>
+	 *
+	 * @param {orion.editor.AnnotationModel} annotationModel the annotation model for the ruler.
+	 * @param {String} [rulerLocation="left"] the location for the ruler.
+	 * @param {orion.editor.Style} [rulerStyle=undefined] the style for the ruler.
+	 *
+	 * @augments orion.editor.Ruler
+	 * @class This objects implements an overview ruler.
+	 *
+	 * <p><b>See:</b><br/>
+	 * {@link orion.editor.AnnotationRuler} <br/>
+	 * {@link orion.editor.Ruler} 
+	 * </p>
+	 * @name orion.editor.OverviewRuler
+	 */
+	function OverviewRuler (annotationModel, rulerLocation, rulerStyle) {
+		Ruler.call(this, annotationModel, rulerLocation, "document", rulerStyle); //$NON-NLS-0$
+	}
+	OverviewRuler.prototype = new Ruler();
+	
+	/** @ignore */
+	OverviewRuler.prototype.getRulerStyle = function() {
+		var result = {style: {lineHeight: "1px", fontSize: "1px"}}; //$NON-NLS-1$ //$NON-NLS-0$
+		result = this._mergeStyle(result, this._rulerStyle);
+		return result;
+	};
+	/** @ignore */
+	OverviewRuler.prototype._getTooltipContents = function(lineIndex, annotations) {
+		if (annotations.length === 0) {
+			var model = this._view.getModel();
+			var mapLine = lineIndex;
+			if (model.getBaseModel) {
+				var lineStart = model.getLineStart(mapLine);
+				mapLine = model.getBaseModel().getLineAtOffset(model.mapOffset(lineStart));
+			}
+			return util.formatMessage(messages.line, mapLine + 1);
+		}
+		return Ruler.prototype._getTooltipContents.call(this, lineIndex, annotations);
+	};
+	/** @ignore */
+	OverviewRuler.prototype._mergeAnnotation = function(previousAnnotation, annotation, annotationLineIndex, annotationLineCount) {
+		if (annotationLineIndex !== 0) { return undefined; }
+		var result = previousAnnotation;
+		if (!result) {
+			//TODO annotationLineCount does not work when there are folded lines
+			var height = 3 * annotationLineCount;
+			result = {html: "&nbsp;", style: { style: {height: height + "px"}}}; //$NON-NLS-1$ //$NON-NLS-0$
+			result.style = this._mergeStyle(result.style, annotation.overviewStyle);
+		}
+		return result;
+	};
+
+	/**
+	 * Constructs a new folding ruler. 
+	 *
+	 * @param {orion.editor.AnnotationModel} annotationModel the annotation model for the ruler.
+	 * @param {String} [rulerLocation="left"] the location for the ruler.
+	 * @param {orion.editor.Style} [rulerStyle=undefined] the style for the ruler.
+	 *
+	 * @augments orion.editor.Ruler
+	 * @class This objects implements an overview ruler.
+	 *
+	 * <p><b>See:</b><br/>
+	 * {@link orion.editor.AnnotationRuler} <br/>
+	 * {@link orion.editor.Ruler} 
+	 * </p>
+	 * @name orion.editor.OverviewRuler
+	 */
+	function FoldingRuler (annotationModel, rulerLocation, rulerStyle) {
+		AnnotationRuler.call(this, annotationModel, rulerLocation, rulerStyle);
+	}
+	FoldingRuler.prototype = new AnnotationRuler();
+	
+	/** @ignore */
+	FoldingRuler.prototype.onClick =  function(lineIndex, e) {
+		if (lineIndex === undefined) { return; }
+		var annotationModel = this._annotationModel;
+		if (!annotationModel) { return; }
+		var view = this._view;
+		var model = view.getModel();
+		var start = model.getLineStart(lineIndex);
+		var end = model.getLineEnd(lineIndex, true);
+		if (model.getBaseModel) {
+			start = model.mapOffset(start);
+			end = model.mapOffset(end);
+			model = model.getBaseModel();
+		}
+		var annotation, iter = annotationModel.getAnnotations(start, end);
+		while (!annotation && iter.hasNext()) {
+			var a = iter.next();
+			if (!this.isAnnotationTypeVisible(a.type)) { continue; }
+			annotation = a;
+		}
+		if (annotation && model.getLineAtOffset(annotation.start) === model.getLineAtOffset(start)) {
+			var tooltip = mTooltip.Tooltip.getTooltip(this._view);
+			if (tooltip) {
+				tooltip.setTarget(null);
+			}
+			if (annotation.expanded) {
+				annotation.collapse();
+			} else {
+				annotation.expand();
+			}
+			this._annotationModel.modifyAnnotation(annotation);
+		}
+	};
+	/** @ignore */
+	FoldingRuler.prototype._getTooltipContents = function(lineIndex, annotations) {
+		if (annotations.length === 1) {
+			if (annotations[0].expanded) {
+				return null;
+			}
+		}
+		return AnnotationRuler.prototype._getTooltipContents.call(this, lineIndex, annotations);
+	};
+	/** @ignore */
+	FoldingRuler.prototype._onAnnotationModelChanged = function(e) {
+		if (e.textModelChangedEvent) {
+			AnnotationRuler.prototype._onAnnotationModelChanged.call(this, e);
+			return;
+		}
+		var view = this._view;
+		if (!view) { return; }
+		var model = view.getModel(), self = this, i;
+		var lineCount = model.getLineCount(), lineIndex = lineCount;
+		function redraw(changes) {
+			for (i = 0; i < changes.length; i++) {
+				if (!self.isAnnotationTypeVisible(changes[i].type)) { continue; }
+				var start = changes[i].start;
+				if (model.getBaseModel) {
+					start = model.mapOffset(start, true);
+				}
+				if (start !== -1) {
+					lineIndex = Math.min(lineIndex, model.getLineAtOffset(start));
+				}
+			}
+		}
+		redraw(e.added);
+		redraw(e.removed);
+		redraw(e.changed);
+		var rulers = view.getRulers();
+		for (i = 0; i < rulers.length; i++) {
+			view.redrawLines(lineIndex, lineCount, rulers[i]);
+		}
+	};
+	
+	return {
+		Ruler: Ruler,
+		AnnotationRuler: AnnotationRuler,
+		LineNumberRuler: LineNumberRuler,
+		OverviewRuler: OverviewRuler,
+		FoldingRuler: FoldingRuler
+	};
+});
+
+/*******************************************************************************
+ * @license
+ * Copyright (c) 2010, 2012 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials are made 
+ * available under the terms of the Eclipse Public License v1.0 
+ * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
+ * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
+ * 
+ * Contributors: IBM Corporation - initial API and implementation
+ ******************************************************************************/
+
+/*global define */
+
+define("orion/editor/undoStack", [], function() { //$NON-NLS-0$
+
+	/** 
+	 * Constructs a new Change object.
+	 * 
+	 * @class 
+	 * @name orion.editor.Change
+	 * @private
+	 */
+	function Change(offset, text, previousText) {
+		this.offset = offset;
+		this.text = text;
+		this.previousText = previousText;
+	}
+	Change.prototype = {
+		/** @ignore */
+		undo: function (view, select) {
+			this._doUndoRedo(this.offset, this.previousText, this.text, view, select);
+			return true;
+		},
+		/** @ignore */
+		redo: function (view, select) {
+			this._doUndoRedo(this.offset, this.text, this.previousText, view, select);
+			return true;
+		},
+		_doUndoRedo: function(offset, text, previousText, view, select) {
+			var model = view.getModel();
+			/* 
+			* TODO UndoStack should be changing the text in the base model.
+			* This is code needs to change when modifications in the base
+			* model are supported properly by the projection model.
+			*/
+			if (model.mapOffset && view.annotationModel) {
+				var mapOffset = model.mapOffset(offset, true);
+				if (mapOffset < 0) {
+					var annotationModel = view.annotationModel;
+					var iter = annotationModel.getAnnotations(offset, offset + 1);
+					while (iter.hasNext()) {
+						var annotation = iter.next();
+						if (annotation.type === "orion.annotation.folding") { //$NON-NLS-0$
+							annotation.expand();
+							mapOffset = model.mapOffset(offset, true);
+							break;
+						}
+					}
+				}
+				if (mapOffset < 0) { return; }
+				offset = mapOffset;
+			}
+			model.setText(text, offset, offset + previousText.length);
+			if (select) {
+				view.setSelection(offset, offset + text.length);
+			}
+		}
+	};
+
+	/** 
+	 * Constructs a new CompoundChange object.
+	 * 
+	 * @param owner the owner of the compound change
+	 *
+	 * @class 
+	 * @name orion.editor.CompoundChange
+	 * @private
+	 */
+	function CompoundChange (owner) {
+		this.owner = owner;
+		this.changes = [];
+	}
+	CompoundChange.prototype = {
+		/** @ignore */
+		add: function (change) {
+			this.changes.push(change);
+		},
+		/** @ignore */
+		end: function (view) {
+			this.endSelection = view.getSelection();
+			this.endCaret = view.getCaretOffset();
+			var owner = this.owner;
+			if (owner && owner.end) {
+				owner.end();
+			}
+		},
+		/** @ignore */
+		undo: function (view, select) {
+			if (this.changes.length > 1) {
+				view.setRedraw(false);
+			}
+			for (var i=this.changes.length - 1; i >= 0; i--) {
+				this.changes[i].undo(view, false);
+			}
+			if (this.changes.length > 1) {
+				view.setRedraw(true);
+			}
+			if (select) {
+				var start = this.startSelection.start;
+				var end = this.startSelection.end;
+				view.setSelection(this.startCaret ? start : end, this.startCaret ? end : start);
+			}
+			var owner = this.owner;
+			if (owner && owner.undo) {
+				owner.undo();
+			}
+			return this.changes.length > 0;
+		},
+		/** @ignore */
+		redo: function (view, select) {
+			if (this.changes.length > 1) {
+				view.setRedraw(false);
+			}
+			for (var i = 0; i < this.changes.length; i++) {
+				this.changes[i].redo(view, false);
+			}
+			if (this.changes.length > 1) {
+				view.setRedraw(true);
+			}
+			if (select) {
+				var start = this.endSelection.start;
+				var end = this.endSelection.end;
+				view.setSelection(this.endCaret ? start : end, this.endCaret ? end : start);
+			}
+			var owner = this.owner;
+			if (owner && owner.redo) {
+				owner.redo();
+			}
+			return this.changes.length > 0;
+		},
+		/** @ignore */
+		start: function (view) {
+			this.startSelection = view.getSelection();
+			this.startCaret = view.getCaretOffset();
+			var owner = this.owner;
+			if (owner && owner.start) {
+				owner.start();
+			}
+		}
+	};
+
+	/**
+	 * Constructs a new UndoStack on a text view.
+	 *
+	 * @param {orion.editor.TextView} view the text view for the undo stack.
+	 * @param {Number} [size=100] the size for the undo stack.
+	 *
+	 * @name orion.editor.UndoStack
+	 * @class The UndoStack is used to record the history of a text model associated to an view. Every
+	 * change to the model is added to stack, allowing the application to undo and redo these changes.
+	 *
+	 * <p>
+	 * <b>See:</b><br/>
+	 * {@link orion.editor.TextView}<br/>
+	 * </p>
+	 */
+	function UndoStack (view, size) {
+		this.view = view;
+		this.size = size !== undefined ? size : 100;
+		this.reset();
+		var model = view.getModel();
+		if (model.getBaseModel) {
+			model = model.getBaseModel();
+		}
+		this.model = model;
+		var self = this;
+		this._listener = {
+			onChanging: function(e) {
+				self._onChanging(e);
+			},
+			onDestroy: function(e) {
+				self._onDestroy(e);
+			}
+		};
+		model.addEventListener("Changing", this._listener.onChanging); //$NON-NLS-0$
+		view.addEventListener("Destroy", this._listener.onDestroy); //$NON-NLS-0$
+	}
+	UndoStack.prototype = /** @lends orion.editor.UndoStack.prototype */ {
+		/**
+		 * Adds a change to the stack.
+		 * 
+		 * @param change the change to add.
+		 */
+		add: function (change) {
+			if (this.compoundChange) {
+				this.compoundChange.add(change);
+			} else {
+				var length = this.stack.length;
+				this.stack.splice(this.index, length-this.index, change);
+				this.index++;
+				if (this.stack.length > this.size) {
+					this.stack.shift();
+					this.index--;
+					this.cleanIndex--;
+				}
+			}
+		},
+		/** 
+		 * Marks the current state of the stack as clean.
+		 *
+		 * <p>
+		 * This function is typically called when the content of view associated with the stack is saved.
+		 * </p>
+		 *
+		 * @see #isClean
+		 */
+		markClean: function() {
+			this.endCompoundChange();
+			this._commitUndo();
+			this.cleanIndex = this.index;
+		},
+		/**
+		 * Returns true if current state of stack is the same
+		 * as the state when markClean() was called.
+		 *
+		 * <p>
+		 * For example, the application calls markClean(), then calls undo() four times and redo() four times.
+		 * At this point isClean() returns true.  
+		 * </p>
+		 * <p>
+		 * This function is typically called to determine if the content of the view associated with the stack
+		 * has changed since the last time it was saved.
+		 * </p>
+		 *
+		 * @return {Boolean} returns if the state is the same as the state when markClean() was called.
+		 *
+		 * @see #markClean
+		 */
+		isClean: function() {
+			return this.cleanIndex === this.getSize().undo;
+		},
+		/**
+		 * Returns true if there is at least one change to undo.
+		 *
+		 * @return {Boolean} returns true if there is at least one change to undo.
+		 *
+		 * @see #canRedo
+		 * @see #undo
+		 */
+		canUndo: function() {
+			return this.getSize().undo > 0;
+		},
+		/**
+		 * Returns true if there is at least one change to redo.
+		 *
+		 * @return {Boolean} returns true if there is at least one change to redo.
+		 *
+		 * @see #canUndo
+		 * @see #redo
+		 */
+		canRedo: function() {
+			return this.getSize().redo > 0;
+		},
+		/**
+		 * Finishes a compound change.
+		 *
+		 * @see #startCompoundChange
+		 */
+		endCompoundChange: function() {
+			if (this.compoundChange) {
+				this.compoundChange.end(this.view);
+			}
+			this.compoundChange = undefined;
+		},
+		/**
+		 * Returns the sizes of the stack.
+		 *
+		 * @return {object} a object where object.undo is the number of changes that can be un-done, 
+		 *  and object.redo is the number of changes that can be re-done.
+		 *
+		 * @see #canUndo
+		 * @see #canRedo
+		 */
+		getSize: function() {
+			var index = this.index;
+			var length = this.stack.length;
+			if (this._undoStart !== undefined) {
+				index++;
+			}
+			return {undo: index, redo: (length - index)};
+		},
+		/**
+		 * Undo the last change in the stack.
+		 *
+		 * @return {Boolean} returns true if a change was un-done.
+		 *
+		 * @see #redo
+		 * @see #canUndo
+		 */
+		undo: function() {
+			this._commitUndo();
+			var change, result = false;
+			this._ignoreUndo = true;
+			do {
+				if (this.index <= 0) {
+					break;
+				}
+				change = this.stack[--this.index];
+			} while (!(result = change.undo(this.view, true)));
+			this._ignoreUndo = false;
+			return result;
+		},
+		/**
+		 * Redo the last change in the stack.
+		 *
+		 * @return {Boolean} returns true if a change was re-done.
+		 *
+		 * @see #undo
+		 * @see #canRedo
+		 */
+		redo: function() {
+			this._commitUndo();
+			var change, result = false;
+			this._ignoreUndo = true;
+			do {
+				if (this.index >= this.stack.length) {
+					break;
+				}
+				change = this.stack[this.index++];
+			} while (!(result = change.redo(this.view, true)));
+			this._ignoreUndo = false;
+			return true;
+		},
+		/**
+		 * Reset the stack to its original state. All changes in the stack are thrown away.
+		 */
+		reset: function() {
+			this.index = this.cleanIndex = 0;
+			this.stack = [];
+			this._undoStart = undefined;
+			this._undoText = "";
+			this._undoType = 0;
+			this._ignoreUndo = false;
+			this._compoundChange = undefined;
+		},
+		/**
+		 * Starts a compound change. 
+		 * <p>
+		 * All changes added to stack from the time startCompoundChange() is called
+		 * to the time that endCompoundChange() is called are compound on one change that can be un-done or re-done
+		 * with one single call to undo() or redo().
+		 * </p>
+		 *
+		 * @param owner the owner of the compound change which is called for start, end, undo and redo.
+		 *		 
+		 * @return the compound change
+		 *
+		 * @see #endCompoundChange
+		 */
+		startCompoundChange: function(owner) {
+			this._commitUndo();
+			var change = new CompoundChange(owner);
+			this.add(change);
+			this.compoundChange = change;
+			this.compoundChange.start(this.view);
+			return this.compoundChange;
+		},
+		_commitUndo: function () {
+			if (this._undoStart !== undefined) {
+				if (this._undoType === -1) {
+					this.add(new Change(this._undoStart, "", this._undoText));
+				} else {
+					this.add(new Change(this._undoStart, this._undoText, ""));
+				}
+				this._undoStart = undefined;
+				this._undoText = "";
+				this._undoType = 0;
+			}
+			this.endCompoundChange();
+		},
+		_onDestroy: function(evt) {
+			this.model.removeEventListener("Changing", this._listener.onChanging); //$NON-NLS-0$
+			this.view.removeEventListener("Destroy", this._listener.onDestroy); //$NON-NLS-0$
+		},
+		_onChanging: function(e) {
+			var newText = e.text;
+			var start = e.start;
+			var removedCharCount = e.removedCharCount;
+			var addedCharCount = e.addedCharCount;
+			if (this._ignoreUndo) {
+				return;
+			}
+			if (this._undoStart !== undefined && 
+				!((addedCharCount === 1 && removedCharCount === 0 && this._undoType === 1 && start === this._undoStart + this._undoText.length) ||
+					(addedCharCount === 0 && removedCharCount === 1 && this._undoType === -1 && (((start + 1) === this._undoStart) || (start === this._undoStart)))))
+			{
+				this._commitUndo();
+			}
+			if (!this.compoundChange) {
+				if (addedCharCount === 1 && removedCharCount === 0) {
+					if (this._undoStart === undefined) {
+						this._undoStart = start;
+					}
+					this._undoText = this._undoText + newText;
+					this._undoType = 1;
+					return;
+				} else if (addedCharCount === 0 && removedCharCount === 1) {
+					var deleting = this._undoText.length > 0 && this._undoStart === start;
+					this._undoStart = start;
+					this._undoType = -1;
+					if (deleting) {
+						this._undoText = this._undoText + this.model.getText(start, start + removedCharCount);
+					} else {
+						this._undoText = this.model.getText(start, start + removedCharCount) + this._undoText;
+					}
+					return;
+				}
+			}
+			this.add(new Change(start, newText, this.model.getText(start, start + removedCharCount)));
+		}
+	};
+	
+	return {
+		UndoStack: UndoStack
+	};
+});
+
+/*******************************************************************************
+ * @license
+ * Copyright (c) 2010, 2012 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials are made 
+ * available under the terms of the Eclipse Public License v1.0 
+ * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
+ * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
+ * 
+ * Contributors: 
+ *		Felipe Heidrich (IBM Corporation) - initial API and implementation
+ *		Silenio Quarti (IBM Corporation) - initial API and implementation
+ ******************************************************************************/
+ 
+/*global define */
+
+define("orion/editor/textDND", [], function() { //$NON-NLS-0$
+
+	function TextDND(view, undoStack) {
+		this._view = view;
+		this._undoStack = undoStack;
+		this._dragSelection = null;
+		this._dropOffset = -1;
+		this._dropText = null;
+		var self = this;
+		this._listener = {
+			onDragStart: function (evt) {
+				self._onDragStart(evt);
+			},
+			onDragEnd: function (evt) {
+				self._onDragEnd(evt);
+			},
+			onDragEnter: function (evt) {
+				self._onDragEnter(evt);
+			},
+			onDragOver: function (evt) {
+				self._onDragOver(evt);
+			},
+			onDrop: function (evt) {
+				self._onDrop(evt);
+			},
+			onDestroy: function (evt) {
+				self._onDestroy(evt);
+			}
+		};
+		view.addEventListener("DragStart", this._listener.onDragStart); //$NON-NLS-0$
+		view.addEventListener("DragEnd", this._listener.onDragEnd); //$NON-NLS-0$
+		view.addEventListener("DragEnter", this._listener.onDragEnter); //$NON-NLS-0$
+		view.addEventListener("DragOver", this._listener.onDragOver); //$NON-NLS-0$
+		view.addEventListener("Drop", this._listener.onDrop); //$NON-NLS-0$
+		view.addEventListener("Destroy", this._listener.onDestroy); //$NON-NLS-0$
+	}
+	TextDND.prototype = {
+		destroy: function() {
+			var view = this._view;
+			if (!view) { return; }
+			view.removeEventListener("DragStart", this._listener.onDragStart); //$NON-NLS-0$
+			view.removeEventListener("DragEnd", this._listener.onDragEnd); //$NON-NLS-0$
+			view.removeEventListener("DragEnter", this._listener.onDragEnter); //$NON-NLS-0$
+			view.removeEventListener("DragOver", this._listener.onDragOver); //$NON-NLS-0$
+			view.removeEventListener("Drop", this._listener.onDrop); //$NON-NLS-0$
+			view.removeEventListener("Destroy", this._listener.onDestroy); //$NON-NLS-0$
+			this._view = null;
+		},
+		_onDestroy: function(e) {
+			this.destroy();
+		},
+		_onDragStart: function(e) {
+			var view = this._view;
+			var selection = view.getSelection();
+			var model = view.getModel();
+			if (model.getBaseModel) {
+				selection.start = model.mapOffset(selection.start);
+				selection.end = model.mapOffset(selection.end);
+				model = model.getBaseModel();
+			}
+			var text = model.getText(selection.start, selection.end);
+			if (text) {
+				this._dragSelection = selection;
+				e.event.dataTransfer.effectAllowed = "copyMove"; //$NON-NLS-0$
+				e.event.dataTransfer.setData("Text", text); //$NON-NLS-0$
+			}
+		},
+		_onDragEnd: function(e) {
+			var view = this._view;
+			if (this._dragSelection) {
+				if (this._undoStack) { this._undoStack.startCompoundChange(); }
+				var move = e.event.dataTransfer.dropEffect === "move"; //$NON-NLS-0$
+				if (move) {
+					view.setText("", this._dragSelection.start, this._dragSelection.end);
+				}
+				if (this._dropText) {
+					var text = this._dropText;
+					var offset = this._dropOffset;
+					if (move) {
+						if (offset >= this._dragSelection.end) {
+							offset -= this._dragSelection.end - this._dragSelection.start;
+						} else if (offset >= this._dragSelection.start) {
+							offset = this._dragSelection.start;
+						}
+					}
+					view.setText(text, offset, offset);
+					view.setSelection(offset, offset + text.length);
+					this._dropText = null;
+					this._dropOffset = -1;
+				}
+				if (this._undoStack) { this._undoStack.endCompoundChange(); }
+			}
+			this._dragSelection = null;
+		},
+		_onDragEnter: function(e) {
+			this._onDragOver(e);
+		},
+		_onDragOver: function(e) {
+			var types = e.event.dataTransfer.types;
+			if (types) {
+				var allowed = !this._view.getOptions("readonly"); //$NON-NLS-0$
+				if (allowed) {
+					allowed = types.contains ? types.contains("text/plain") : types.indexOf("text/plain") !== -1; //$NON-NLS-1$ //$NON-NLS-0$
+				}
+				if (!allowed) {
+					e.event.dataTransfer.dropEffect = "none"; //$NON-NLS-0$
+				}
+			}
+		},
+		_onDrop: function(e) {
+			var view = this._view;
+			var text = e.event.dataTransfer.getData("Text"); //$NON-NLS-0$
+			if (text) {
+				var offset = view.getOffsetAtLocation(e.x, e.y);
+				if (this._dragSelection) {
+					this._dropOffset = offset;
+					this._dropText = text;
+				} else {
+					view.setText(text, offset, offset);
+					view.setSelection(offset, offset + text.length);
+				}
+			}
+		}
+	};
+
+	return {TextDND: TextDND};
+});
+
+/*******************************************************************************
+ * @license
+ * Copyright (c) 2009, 2012 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials are made 
+ * available under the terms of the Eclipse Public License v1.0 
+ * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
+ * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
+ * 
+ * Contributors: IBM Corporation - initial API and implementation
+ ******************************************************************************/
+ 
+ /*global define*/
+ /*jslint maxerr:150 browser:true devel:true laxbreak:true regexp:false*/
+
+define("orion/editor/editor", ['i18n!orion/editor/nls/messages', 'orion/keyBinding', 'orion/editor/eventTarget', 'orion/editor/tooltip', 'orion/editor/annotations', 'orion/util'], function(messages, mKeyBinding, mEventTarget, mTooltip, mAnnotations, util) { //$NON-NLS-6$ //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		
+	var HIGHLIGHT_ERROR_ANNOTATION = "orion.annotation.highlightError"; //$NON-NLS-0$
+
+	/**
+	 * @name orion.editor.Editor
+	 * @class An <code>Editor</code> is a user interface for editing text that provides additional features over the basic {@link orion.editor.TextView}.
+	 * Some of <code>Editor</code>'s features include:
+	 * <ul>
+	 * <li>Additional actions and key bindings for editing text</li>
+	 * <li>Content assist</li>
+	 * <li>Find and Incremental Find</li>
+	 * <li>Rulers for displaying line numbers and annotations</li>
+	 * <li>Status reporting</li>
+	 * </ul>
+	 * 
+	 * @description Creates a new Editor with the given options.
+	 * @param {Object} options Options controlling the features of this Editor.
+	 * @param {Object} options.annotationFactory
+	 * @param {Object} options.contentAssistFactory
+	 * @param {Object} options.domNode
+	 * @param {Object} options.keyBindingFactory
+	 * @param {Object} options.lineNumberRulerFactory
+	 * @param {Object} options.foldingRulerFactory
+	 * @param {Object} options.statusReporter
+	 * @param {Object} options.textViewFactory
+	 * @param {Object} options.undoStackFactory
+	 * @param {Object} options.textDNDFactory
+	 *
+	 * @borrows orion.editor.EventTarget#addEventListener as #addEventListener
+	 * @borrows orion.editor.EventTarget#removeEventListener as #removeEventListener
+	 * @borrows orion.editor.EventTarget#dispatchEvent as #dispatchEvent
+	 */
+	function Editor(options) {
+		this._textViewFactory = options.textViewFactory;
+		this._undoStackFactory = options.undoStackFactory;
+		this._textDNDFactory = options.textDNDFactory;
+		this._annotationFactory = options.annotationFactory;
+		this._foldingRulerFactory = options.foldingRulerFactory;
+		this._lineNumberRulerFactory = options.lineNumberRulerFactory;
+		this._contentAssistFactory = options.contentAssistFactory;
+		this._keyBindingFactory = options.keyBindingFactory;
+		this._statusReporter = options.statusReporter;
+		this._domNode = options.domNode;
+		
+		this._annotationStyler = null;
+		this._annotationModel = null;
+		this._annotationRuler = null;
+		this._lineNumberRuler = null;
+		this._overviewRuler = null;
+		this._foldingRuler = null;
+		this._dirty = false;
+		this._contentAssist = null;
+		this._title = null;
+	}
+	Editor.prototype = /** @lends orion.editor.Editor.prototype */ {
+		/**
+		 * Destroys the editor.
+		 */
+		destroy: function() {
+			this.uninstallTextView();
+			this._textViewFactory = this._undoStackFactory = this._textDNDFactory = 
+			this._annotationFactory = this._foldingRulerFactory = this._lineNumberRulerFactory = 
+			this._contentAssistFactory = this._keyBindingFactory = this._statusReporter =
+			this._domNode = null;
+		},
+		/**
+		 * Returns the annotation model of the editor. 
+		 *
+		 * @returns {orion.editor.AnnotationModel}
+		 */
+		getAnnotationModel: function() {
+			return this._annotationModel;
+		},
+		/**
+		 * Returns the annotation ruler of the editor. 
+		 *
+		 * @returns {orion.editor.AnnotationRuler}
+		 */
+		getAnnotationRuler: function() {
+			return this._annotationRuler;
+		},
+		/**
+		 * Returns the annotation styler of the editor. 
+		 *
+		 * @returns {orion.editor.AnnotationStyler}
+		 */
+		getAnnotationStyler: function() {
+			return this._annotationStyler;
+		},
+		/**
+		 * Returns the folding ruler of the editor. 
+		 *
+		 * @returns {orion.editor.FoldingRuler}
+		 */
+		getFoldingRuler: function() {
+			return this._foldingRuler;
+		},
+		/**
+		 * Returns the line number ruler of the editor. 
+		 *
+		 * @returns {orion.editor.LineNumberRuler}
+		 */
+		getLineNumberRuler: function() {
+			return this._lineNumberRuler;
+		},
+		/**
+		 * Returns the base text model of this editor.
+		 *
+		 * @returns {orion.editor.TextModel}
+		 */
+		getModel: function() {
+			var model = this._textView.getModel();
+			if (model.getBaseModel) {
+				model = model.getBaseModel();
+			}
+			return model;
+		},
+		/**
+		 * Returns the overview ruler of the editor. 
+		 *
+		 * @returns {orion.editor.OverviewRuler}
+		 */
+		getOverviewRuler: function() {
+			return this._overviewRuler;
+		},
+		/**
+		 * Returns the underlying <code>TextView</code> used by this editor. 
+		 * @returns {orion.editor.TextView} the editor text view.
+		 */
+		getTextView: function() {
+			return this._textView;
+		},
+		/**
+		 * Returns the editor title. 
+		 *
+		 * @returns {String} the editor title.
+		 */
+		getTitle: function() {
+			return this._title;
+		},
+		
+		/**
+		 * Returns the editor's key modes.
+		 *
+		 * @returns {Array} the editor key modes.
+		 */
+		getKeyModes: function() {
+			return  this._textView.getKeyModes();
+		},
+		
+		/**
+		 * Returns <code>true</code> if the editor is dirty; <code>false</code> otherwise.
+		 * @returns {Boolean} 
+		 */
+		isDirty: function() {
+			return this._dirty;
+		},
+		/**
+		 * Sets whether the annotation ruler is visible.
+		 *
+		 * @param {Boolean} visible <code>true</code> to show ruler, <code>false</code> otherwise
+		 */
+		setAnnotationRulerVisible: function(visible) {
+			if (this._annotationRulerVisible === visible) { return; }
+			this._annotationRulerVisible = visible;
+			if (!this._annotationRuler) { return; }
+			var textView = this._textView;
+			if (visible) {
+				textView.addRuler(this._annotationRuler, 0);
+			} else {
+				textView.removeRuler(this._annotationRuler);
+			}
+		},
+		/**
+		 * Sets whether the folding ruler is visible.
+		 *
+		 * @param {Boolean} visible <code>true</code> to show ruler, <code>false</code> otherwise
+		 */
+		setFoldingRulerVisible: function(visible) {
+			if (this._foldingRulerVisible === visible) { return; }
+			this._foldingRulerVisible = visible;
+			if (!this._foldingRuler) { return; }
+			var textView = this._textView;
+			if (!textView.getModel().getBaseModel) { return; }
+			if (visible) {
+				textView.addRuler(this._foldingRuler, 100);
+			} else {
+				textView.removeRuler(this._foldingRuler);
+			}
+		},
+		/**
+		 * Sets whether the editor is dirty.
+		 *
+		 * @param {Boolean} dirty
+		 */
+		setDirty: function(dirty) {
+			if (this._dirty === dirty) { return; }
+			this._dirty = dirty;
+			this.onDirtyChanged({type: "DirtyChanged"}); //$NON-NLS-0$
+		},
+		/**
+		 * Sets whether the line numbering ruler is visible.
+		 *
+		 * @param {Boolean} visible <code>true</code> to show ruler, <code>false</code> otherwise
+		 */
+		setLineNumberRulerVisible: function(visible) {
+			if (this._lineNumberRulerVisible === visible) { return; }
+			this._lineNumberRulerVisible = visible;
+			if (!this._lineNumberRuler) { return; }
+			var textView = this._textView;
+			if (visible) {
+				textView.addRuler(this._lineNumberRuler, 1);
+			} else {
+				textView.removeRuler(this._lineNumberRuler);
+			}
+		},
+		/**
+		 * Sets whether the overview ruler is visible.
+		 *
+		 * @param {Boolean} visible <code>true</code> to show ruler, <code>false</code> otherwise
+		 */
+		setOverviewRulerVisible: function(visible) {
+			if (this._overviewRulerVisible === visible) { return; }
+			this._overviewRulerVisible = visible;
+			if (!this._overviewRuler) { return; }
+			var textView = this._textView;
+			if (visible) {
+				textView.addRuler(this._overviewRuler);
+			} else {
+				textView.removeRuler(this._overviewRuler);
+			}
+		},
+		
+		mapOffset: function(offset, parent) {
+			var textView = this._textView;
+			var model = textView.getModel();
+			if (model.getBaseModel) {
+				offset = model.mapOffset(offset, parent);
+			}
+			return offset;
+		},
+		
+		getCaretOffset: function() {
+			return this.mapOffset(this._textView.getCaretOffset());
+		},
+		
+		getSelection: function() {
+			var textView = this._textView;
+			var selection = textView.getSelection();
+			var model = textView.getModel();
+			if (model.getBaseModel) {
+				selection.start = model.mapOffset(selection.start);
+				selection.end = model.mapOffset(selection.end);
+			}
+			return selection;
+		},
+		
+		getText: function(start, end) {
+			var textView = this._textView;
+			var model = textView.getModel();
+			if (model.getBaseModel) {
+				model = model.getBaseModel();
+			}
+			return model.getText(start, end);
+		},
+		
+		_expandOffset: function(offset) {
+			var model = this._textView.getModel();
+			var annotationModel = this._annotationModel;
+			if (!annotationModel || !model.getBaseModel) { return; }
+			var annotations = annotationModel.getAnnotations(offset, offset + 1);
+			while (annotations.hasNext()) {
+				var annotation = annotations.next();
+				if (annotation.type === mAnnotations.AnnotationType.ANNOTATION_FOLDING) {
+					if (annotation.expand) {
+						annotation.expand();
+						annotationModel.modifyAnnotation(annotation);
+					}
+				}
+			}
+		},
+
+		setCaretOffset: function(caretOffset, show, callback) {
+			var textView = this._textView;
+			var model = textView.getModel();
+			if (model.getBaseModel) {
+				this._expandOffset(caretOffset);
+				caretOffset = model.mapOffset(caretOffset, true);
+			}
+			textView.setCaretOffset(caretOffset, show, callback);
+		},
+	
+		setText: function(text, start, end) {
+			var textView = this._textView;
+			var model = textView.getModel();
+			if (model.getBaseModel) {
+				if (start !== undefined) {
+					this._expandOffset(start);
+					start = model.mapOffset(start, true);
+				}
+				if (end !== undefined) {
+					this._expandOffset(end);
+					end = model.mapOffset(end, true);
+				}
+			}
+			textView.setText(text, start, end);
+		},
+		
+		/**
+		 * @deprecated use #setFoldingRulerVisible
+		 */
+		setFoldingEnabled: function(enabled) {
+			this.setFoldingRulerVisible(enabled);
+		},
+		
+		setSelection: function(start, end, show, callback) {
+			var textView = this._textView;
+			var model = textView.getModel();
+			if (model.getBaseModel) {
+				this._expandOffset(start);
+				this._expandOffset(end);
+				start = model.mapOffset(start, true);
+				end = model.mapOffset(end, true);
+			}
+			textView.setSelection(start, end, show, callback);
+		},
+				
+		/**
+		 * @param {Number} start
+		 * @param {Number} [end]
+		 * @param {function} [callback] if callback is specified, scrolling to show the selection is animated and callback is called when the animation is done.
+		 * @param {Boolean} [focus=true] whether the text view should be focused when the selection is done.
+		 * @private
+		 * @deprecated use #setSelection instead
+		 */
+		moveSelection: function(start, end, callback, focus) {
+			end = end || start;
+			var textView = this._textView;
+			this.setSelection(start, end, 1 / 3, function() {
+				if (focus === undefined || focus) {
+					textView.focus();
+				}
+				if (callback) {
+					callback();
+				}
+			});
+		},
+		
+		/** @private */
+		checkDirty : function() {
+			this.setDirty(!this._undoStack.isClean());
+		},
+		
+		/**
+		 * @private
+		 */
+		reportStatus: function(message, type, isAccessible) {
+			if (this._statusReporter) {
+				this._statusReporter(message, type, isAccessible);
+			}
+		},
+		
+		/** @private */
+		_getTooltipInfo: function(x, y) {
+			var textView = this._textView;			
+			var annotationModel = this.getAnnotationModel();
+			if (!annotationModel) { return null; }
+			var annotationStyler = this._annotationStyler;
+			if (!annotationStyler) { return null; }
+			var offset = textView.getOffsetAtLocation(x, y);
+			if (offset === -1) { return null; }
+			offset = this.mapOffset(offset);
+			var annotations = annotationStyler.getAnnotationsByType(annotationModel, offset, offset + 1);
+			var rangeAnnotations = [];
+			for (var i = 0; i < annotations.length; i++) {
+				if (annotations[i].rangeStyle) {
+					rangeAnnotations.push(annotations[i]);
+				}
+			}
+			if (rangeAnnotations.length === 0) { return null; }
+			var pt = textView.convert({x: x, y: y}, "document", "page"); //$NON-NLS-1$ //$NON-NLS-0$
+			var info = {
+				contents: rangeAnnotations,
+				anchor: "left", //$NON-NLS-0$
+				x: pt.x + 10,
+				y: pt.y + 20
+			};
+			return info;
+		},
+		
+		/** @private */
+		_highlightCurrentLine: function(newSelection, oldSelection) {
+			var annotationModel = this._annotationModel;
+			if (!annotationModel) { return; }
+			var textView = this._textView;	
+			var model = textView.getModel();
+			var oldLineIndex = oldSelection ? model.getLineAtOffset(oldSelection.start) : -1;
+			var lineIndex = model.getLineAtOffset(newSelection.start);
+			var newEmpty = newSelection.start === newSelection.end;
+			var oldEmpty = !oldSelection || oldSelection.start === oldSelection.end;
+			var start = model.getLineStart(lineIndex);
+			var end = model.getLineEnd(lineIndex);
+			if (model.getBaseModel) {
+				start = model.mapOffset(start);
+				end = model.mapOffset(end);
+			}
+			var annotation = this._currentLineAnnotation; 
+			if (oldLineIndex === lineIndex && oldEmpty && newEmpty && annotation && annotation.start === start && annotation.end === end) {
+				return;
+			}
+			var remove = annotation ? [annotation] : null;
+			var add;
+			if (newEmpty) {
+				var type = mAnnotations.AnnotationType.ANNOTATION_CURRENT_LINE;
+				annotation = mAnnotations.AnnotationType.createAnnotation(type, start, end);
+				add = [annotation];
+			}
+			this._currentLineAnnotation = annotation;
+			annotationModel.replaceAnnotations(remove, add);
+		},
+		
+		/**
+		 * Creates the underlying TextView and installs the editor's features.
+		 */
+		installTextView : function() {
+			// Create textView and install optional features
+			this._textView = this._textViewFactory();
+			if (this._undoStackFactory) {
+				this._undoStack = this._undoStackFactory.createUndoStack(this);
+			}
+			if (this._textDNDFactory) {
+				this._textDND = this._textDNDFactory.createTextDND(this, this._undoStack);
+			}
+			if (this._contentAssistFactory) {
+				var contentAssistMode = this._contentAssistFactory.createContentAssistMode(this);
+				this._contentAssist = contentAssistMode.getContentAssist();
+			}
+			
+			var editor = this, textView = this._textView;
+			
+			var self = this;
+			this._listener = {
+				onModelChanged: function(e) {
+					self.checkDirty();
+				},
+				onMouseOver: function(e) {
+					self._listener.onMouseMove(e);
+				},
+				onMouseMove: function(e) {
+					var tooltip = mTooltip.Tooltip.getTooltip(textView);
+					if (!tooltip) { return; }
+					if (self._listener.lastMouseX === e.event.clientX && self._listener.lastMouseY === e.event.clientY) {
+						return;
+					}
+					self._listener.lastMouseX = e.event.clientX;
+					self._listener.lastMouseY = e.event.clientY;
+					tooltip.setTarget({
+						x: e.x,
+						y: e.y,
+						getTooltipInfo: function() {
+							return self._getTooltipInfo(this.x, this.y);
+						}
+					});
+				},
+				onMouseOut: function(e) {
+					var tooltip = mTooltip.Tooltip.getTooltip(textView);
+					if (!tooltip) { return; }
+					if (self._listener.lastMouseX === e.event.clientX && self._listener.lastMouseY === e.event.clientY) {
+						return;
+					}
+					self._listener.lastMouseX = e.event.clientX;
+					self._listener.lastMouseY = e.event.clientY;
+					tooltip.setTarget(null);
+				},
+				onScroll: function(e) {
+					var tooltip = mTooltip.Tooltip.getTooltip(textView);
+					if (!tooltip) { return; }
+					tooltip.setTarget(null);
+				},
+				onSelection: function(e) {
+					self._updateCursorStatus();
+					self._highlightCurrentLine(e.newValue, e.oldValue);
+				}
+			};
+			textView.addEventListener("ModelChanged", this._listener.onModelChanged); //$NON-NLS-0$
+			textView.addEventListener("Selection", this._listener.onSelection); //$NON-NLS-0$
+			textView.addEventListener("MouseOver", this._listener.onMouseOver); //$NON-NLS-0$
+			textView.addEventListener("MouseOut", this._listener.onMouseOut); //$NON-NLS-0$
+			textView.addEventListener("MouseMove", this._listener.onMouseMove); //$NON-NLS-0$
+			textView.addEventListener("Scroll", this._listener.onScroll); //$NON-NLS-0$
+						
+			// Set up keybindings
+			if (this._keyBindingFactory) {
+				if (typeof this._keyBindingFactory === "function") {
+					this._keyBindingFactory(this, this.getKeyModes(), this._undoStack, this._contentAssist);
+				} else {
+					this._keyBindingFactory.createKeyBindings(editor, this._undoStack, this._contentAssist);
+				}
+			}
+
+			var addRemoveBookmark = function(lineIndex, e) {
+				if (lineIndex === undefined) { return; }
+				if (lineIndex === -1) { return; }
+				var view = this.getView();
+				var viewModel = view.getModel();
+				var annotationModel = this.getAnnotationModel();
+				var lineStart = editor.mapOffset(viewModel.getLineStart(lineIndex));
+				var lineEnd = editor.mapOffset(viewModel.getLineEnd(lineIndex));
+				var annotations = annotationModel.getAnnotations(lineStart, lineEnd);
+				var bookmark = null;
+				while (annotations.hasNext()) {
+					var annotation = annotations.next();
+					if (annotation.type === mAnnotations.AnnotationType.ANNOTATION_BOOKMARK) {
+						bookmark = annotation;
+						break;
+					}
+				}
+				if (bookmark) {
+					annotationModel.removeAnnotation(bookmark);
+				} else {
+					bookmark = mAnnotations.AnnotationType.createAnnotation(mAnnotations.AnnotationType.ANNOTATION_BOOKMARK, lineStart, lineEnd);
+					bookmark.title = undefined;
+					annotationModel.addAnnotation(bookmark);
+				}
+			};
+
+			// Create rulers, annotation model and styler
+			if (this._annotationFactory) {
+				var textModel = textView.getModel();
+				if (textModel.getBaseModel) { textModel = textModel.getBaseModel(); }
+				this._annotationModel = this._annotationFactory.createAnnotationModel(textModel);
+				if (this._annotationModel) {
+					var styler = this._annotationStyler = this._annotationFactory.createAnnotationStyler(textView, this._annotationModel);
+					if (styler) {
+						styler.addAnnotationType(mAnnotations.AnnotationType.ANNOTATION_CURRENT_SEARCH);
+						styler.addAnnotationType(mAnnotations.AnnotationType.ANNOTATION_MATCHING_SEARCH);
+						styler.addAnnotationType(mAnnotations.AnnotationType.ANNOTATION_ERROR);
+						styler.addAnnotationType(mAnnotations.AnnotationType.ANNOTATION_WARNING);
+						styler.addAnnotationType(mAnnotations.AnnotationType.ANNOTATION_MATCHING_BRACKET);
+						styler.addAnnotationType(mAnnotations.AnnotationType.ANNOTATION_CURRENT_BRACKET);
+						styler.addAnnotationType(mAnnotations.AnnotationType.ANNOTATION_CURRENT_LINE);
+						styler.addAnnotationType(mAnnotations.AnnotationType.ANNOTATION_READ_OCCURRENCE);
+						styler.addAnnotationType(mAnnotations.AnnotationType.ANNOTATION_WRITE_OCCURRENCE);
+						styler.addAnnotationType(mAnnotations.AnnotationType.ANNOTATION_SELECTED_LINKED_GROUP);
+						styler.addAnnotationType(mAnnotations.AnnotationType.ANNOTATION_CURRENT_LINKED_GROUP);
+						styler.addAnnotationType(mAnnotations.AnnotationType.ANNOTATION_LINKED_GROUP);
+						styler.addAnnotationType(HIGHLIGHT_ERROR_ANNOTATION);
+					}
+				}
+				
+				/*
+				* TODO - UndoStack relies on this line to ensure that collapsed regions are expanded 
+				* when the undo operation happens to those regions. This line needs to be remove when the
+				* UndoStack is fixed.
+				*/
+				textView.annotationModel = this._annotationModel;
+					
+				var rulers = this._annotationFactory.createAnnotationRulers(this._annotationModel);
+				var ruler = this._annotationRuler = rulers.annotationRuler;
+				if (ruler) {
+					ruler.onDblClick = addRemoveBookmark;
+					ruler.setMultiAnnotationOverlay({html: "<div class='annotationHTML overlay'></div>"}); //$NON-NLS-0$
+					ruler.addAnnotationType(mAnnotations.AnnotationType.ANNOTATION_ERROR);
+					ruler.addAnnotationType(mAnnotations.AnnotationType.ANNOTATION_WARNING);
+					ruler.addAnnotationType(mAnnotations.AnnotationType.ANNOTATION_TASK);
+					ruler.addAnnotationType(mAnnotations.AnnotationType.ANNOTATION_BOOKMARK);
+				}
+				this.setAnnotationRulerVisible(true);
+					
+				ruler = this._overviewRuler = rulers.overviewRuler;
+				if (ruler) {
+					ruler.addAnnotationType(mAnnotations.AnnotationType.ANNOTATION_CURRENT_SEARCH);
+					ruler.addAnnotationType(mAnnotations.AnnotationType.ANNOTATION_MATCHING_SEARCH);
+					ruler.addAnnotationType(mAnnotations.AnnotationType.ANNOTATION_ERROR);
+					ruler.addAnnotationType(mAnnotations.AnnotationType.ANNOTATION_WARNING);
+					ruler.addAnnotationType(mAnnotations.AnnotationType.ANNOTATION_TASK);
+					ruler.addAnnotationType(mAnnotations.AnnotationType.ANNOTATION_BOOKMARK);
+					ruler.addAnnotationType(mAnnotations.AnnotationType.ANNOTATION_MATCHING_BRACKET);
+					ruler.addAnnotationType(mAnnotations.AnnotationType.ANNOTATION_CURRENT_BRACKET);
+					ruler.addAnnotationType(mAnnotations.AnnotationType.ANNOTATION_CURRENT_LINE);
+					ruler.addAnnotationType(mAnnotations.AnnotationType.ANNOTATION_READ_OCCURRENCE);
+					ruler.addAnnotationType(mAnnotations.AnnotationType.ANNOTATION_WRITE_OCCURRENCE);
+				}
+				this.setOverviewRulerVisible(true);
+			}
+			
+			if (this._lineNumberRulerFactory) {
+				this._lineNumberRuler = this._lineNumberRulerFactory.createLineNumberRuler(this._annotationModel);
+				this._lineNumberRuler.onDblClick = addRemoveBookmark;
+				this.setLineNumberRulerVisible(true);
+			}
+			
+			if (this._foldingRulerFactory) {
+				this._foldingRuler = this._foldingRulerFactory.createFoldingRuler(this._annotationModel);
+				this._foldingRuler.addAnnotationType(mAnnotations.AnnotationType.ANNOTATION_FOLDING);
+				this.setFoldingRulerVisible(false);
+			}
+			
+			var textViewInstalledEvent = {
+				type: "TextViewInstalled", //$NON-NLS-0$
+				textView: textView
+			};
+			this.dispatchEvent(textViewInstalledEvent);
+		},
+		
+		/**
+		 * Destroys the underlying TextView.
+		 */
+		uninstallTextView: function() {
+			var textView = this._textView;
+			if (!textView) { return; }
+			
+			textView.destroy();
+			
+			this._textView = this._undoStack = this._textDND = this._contentAssist = 
+				this._listener = this._annotationModel = this._annotationStyler =
+				this._annotationRuler = this._overviewRuler = this._lineNumberRuler =
+				this._foldingRuler = this._currentLineAnnotation = this._title = null;
+			this._dirty = false;
+			
+			var textViewUninstalledEvent = {
+				type: "TextViewUninstalled", //$NON-NLS-0$
+				textView: textView
+			};
+			this.dispatchEvent(textViewUninstalledEvent);
+		},
+		
+		_updateCursorStatus: function() {
+			var model = this.getModel();
+			var caretOffset = this.getCaretOffset();
+			var lineIndex = model.getLineAtOffset(caretOffset);
+			var lineStart = model.getLineStart(lineIndex);
+			var offsetInLine = caretOffset - lineStart;
+			// If we are in a mode and it owns status reporting, we bail out from reporting the cursor position.
+			var keyModes = this.getKeyModes();
+			for (var i=0; i<keyModes.length; i++) {
+				var mode = keyModes[i];
+				if (mode.isActive() && mode.isStatusActive && mode.isStatusActive()) {
+					return;
+				}
+			}
+			this.reportStatus(util.formatMessage(messages.lineColumn, lineIndex + 1, offsetInLine + 1));
+		},
+		
+		showProblems: function(problems) {
+			var annotationModel = this._annotationModel;
+			if (!annotationModel) {
+				return;
+			}
+			var remove = [], add = [];
+			var model = annotationModel.getTextModel();
+			var annotations = annotationModel.getAnnotations(0, model.getCharCount()), annotation;
+			while (annotations.hasNext()) {
+				annotation = annotations.next();
+				if (annotation.type === mAnnotations.AnnotationType.ANNOTATION_ERROR || annotation.type === mAnnotations.AnnotationType.ANNOTATION_WARNING) {
+					remove.push(annotation);
+				}
+			}
+			if (problems) { 
+				for (var i = 0; i < problems.length; i++) {
+					var problem = problems[i];
+					if (problem) {
+						// escaping voodoo... we need to construct HTML that contains valid JavaScript.
+						var escapedDescription = problem.description.replace(/'/g, "&#39;").replace(/"/g, '&#34;'); //$NON-NLS-1$ //$NON-NLS-0$
+						var lineIndex = problem.line - 1;
+						var lineStart = model.getLineStart(lineIndex);
+						var severity = problem.severity;
+						var type = severity === "error" ? mAnnotations.AnnotationType.ANNOTATION_ERROR : mAnnotations.AnnotationType.ANNOTATION_WARNING; //$NON-NLS-0$
+						var start = lineStart + problem.start - 1;
+						var end = lineStart + problem.end;
+						annotation = mAnnotations.AnnotationType.createAnnotation(type, start, end, escapedDescription);
+						add.push(annotation);
+					}
+				}
+			}
+			annotationModel.replaceAnnotations(remove, add);
+		},
+		
+		showOccurrences: function(occurrences) {
+			var annotationModel = this._annotationModel;
+			if (!annotationModel) {
+				return;
+			}
+			var remove = [], add = [];
+			var model = annotationModel.getTextModel();
+			var annotations = annotationModel.getAnnotations(0, model.getCharCount()), annotation;
+			while (annotations.hasNext()) {
+				annotation = annotations.next();
+				if (annotation.type === mAnnotations.AnnotationType.ANNOTATION_READ_OCCURRENCE || annotation.type === mAnnotations.AnnotationType.ANNOTATION_WRITE_OCCURRENCE) {
+					remove.push(annotation);
+				}
+			}
+			if (occurrences) { 
+				for (var i = 0; i < occurrences.length; i++) {
+					var occurrence = occurrences[i];
+					if (occurrence) {
+						var lineIndex = occurrence.line - 1;
+						var lineStart = model.getLineStart(lineIndex);
+						var start = lineStart + occurrence.start - 1;
+						var end = lineStart + occurrence.end;
+						var type = occurrence.readAccess === true ? mAnnotations.AnnotationType.ANNOTATION_READ_OCCURRENCE : mAnnotations.AnnotationType.ANNOTATION_WRITE_OCCURRENCE;
+						var description = occurrence.description;
+						annotation = mAnnotations.AnnotationType.createAnnotation(type, start, end, description);
+						add.push(annotation);
+					}
+				}
+			}
+			annotationModel.replaceAnnotations(remove, add);
+		},
+		
+		/**
+		 * Reveals and selects a portion of text.
+		 * @param {Number} start
+		 * @param {Number} end
+		 * @param {Number} line
+		 * @param {Number} offset
+		 * @param {Number} length
+		 */
+		showSelection: function(start, end, line, offset, length) {
+			// We use typeof because we need to distinguish the number 0 from an undefined or null parameter
+			if (typeof(start) === "number") { //$NON-NLS-0$
+				if (typeof(end) !== "number") { //$NON-NLS-0$
+					end = start;
+				}
+				this.moveSelection(start, end);
+			} else if (typeof(line) === "number") { //$NON-NLS-0$
+				var model = this.getModel();
+				var pos = model.getLineStart(line-1);
+				if (typeof(offset) === "number") { //$NON-NLS-0$
+					pos = pos + offset;
+				}
+				if (typeof(length) !== "number") { //$NON-NLS-0$
+					length = 0;
+				}
+				this.moveSelection(pos, pos+length);
+			}
+		},
+		
+		/**
+		 * Sets the editor's contents.
+		 *
+		 * @param {String} title
+		 * @param {String} message
+		 * @param {String} contents
+		 * @param {Boolean} contentsSaved
+		 */
+		setInput: function(title, message, contents, contentsSaved) {
+			this._title = title;
+			if (this._textView) {
+				if (contentsSaved) {
+					// don't reset undo stack on save, just mark it clean so that we don't lose the undo past the save
+					this._undoStack.markClean();
+					this.checkDirty();
+				} else {
+					if (message) {
+						this._textView.setText(message);
+					} else {
+						if (contents !== null && contents !== undefined) {
+							this._textView.setText(contents);
+							this._textView.getModel().setLineDelimiter("auto"); //$NON-NLS-0$
+							this._highlightCurrentLine(this._textView.getSelection());
+						}
+					}
+					this._undoStack.reset();
+					this.checkDirty();
+					this._textView.focus();
+				}
+			}
+			this.onInputChanged({
+				type: "InputChanged", //$NON-NLS-0$
+				title: title,
+				message: message,
+				contents: contents,
+				contentsSaved: contentsSaved
+			});
+		},
+		/**
+		 * Called when the editor's contents have changed.
+		 * @param {Event} inputChangedEvent
+		 */
+		onInputChanged: function (inputChangedEvent) {
+			return this.dispatchEvent(inputChangedEvent);
+		},
+		/**
+		 * Reveals a line in the editor, and optionally selects a portion of the line.
+		 * @param {Number} line - document base line index
+		 * @param {Number|String} column
+		 * @param {Number} [end]
+		 */
+		onGotoLine: function(line, column, end, callback) {
+			if (this._textView) {
+				var model = this.getModel();
+				line = Math.max(0, Math.min(line, model.getLineCount() - 1));
+				var lineStart = model.getLineStart(line);
+				var start = 0;
+				if (end === undefined) {
+					end = 0;
+				}
+				if (typeof column === "string") { //$NON-NLS-0$
+					var index = model.getLine(line).indexOf(column);
+					if (index !== -1) {
+						start = index;
+						end = start + column.length;
+					}
+				} else {
+					start = column;
+					var lineLength = model.getLineEnd(line) - lineStart;
+					start = Math.min(start, lineLength);
+					end = Math.min(end, lineLength);
+				}
+				this.moveSelection(lineStart + start, lineStart + end, callback);
+			}
+		},
+		
+		/**
+		 * Called when the dirty state of the editor changes.
+		 * @param {Event} dirtyChangedEvent
+		 */
+		onDirtyChanged: function(dirtyChangedEvent) {
+			return this.dispatchEvent(dirtyChangedEvent);
+		}
+	};
+	mEventTarget.EventTarget.addMixin(Editor.prototype);
+
+	/**
+	 * @private
+	 * @param context Value to be used as the returned function's <code>this</code> value.
+	 * @param [arg1, arg2, ...] Fixed argument values that will prepend any arguments passed to the returned function when it is invoked.
+	 * @returns {Function} A function that always executes this function in the given <code>context</code>.
+	 */
+	function bind(context) {
+		var fn = this,
+		    fixed = Array.prototype.slice.call(arguments, 1);
+		if (fixed.length) {
+			return function() {
+				return arguments.length
+					? fn.apply(context, fixed.concat(Array.prototype.slice.call(arguments)))
+					: fn.apply(context, fixed);
+			};
+		}
+		return function() {
+			return arguments.length ? fn.apply(context, arguments) : fn.call(context);
+		};
+	}
+
+	if (!Function.prototype.bind) {
+		Function.prototype.bind = bind;
+	}
+
+	return {
+		Editor: Editor
+	};
+});
+
+/*******************************************************************************
+ * @license
+ * Copyright (c) 2011 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials are made 
+ * available under the terms of the Eclipse Public License v1.0 
+ * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
+ * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+/*global define */
+/*jslint browser:true regexp:false*/
+/**
+ * @name orion.editor.regex
+ * @class Utilities for dealing with regular expressions.
+ * @description Utilities for dealing with regular expressions.
+ */
+define("orion/editor/regex", [], function() {
+	/**
+	 * @methodOf orion.editor.regex
+	 * @static
+	 * @description Escapes regex special characters in the input string.
+	 * @param {String} str The string to escape.
+	 * @returns {String} A copy of <code>str</code> with regex special characters escaped.
+	 */
+	function escape(str) {
+		return str.replace(/([\\$\^*\/+?\.\(\)|{}\[\]])/g, "\\$&");
+	}
+
+	/**
+	 * @methodOf orion.editor.regex
+	 * @static
+	 * @description Parses a pattern and flags out of a regex literal string.
+	 * @param {String} str The string to parse. Should look something like <code>"/ab+c/"</code> or <code>"/ab+c/i"</code>.
+	 * @returns {Object} If <code>str</code> looks like a regex literal, returns an object with properties
+	 * <code><dl>
+	 * <dt>pattern</dt><dd>{String}</dd>
+	 * <dt>flags</dt><dd>{String}</dd>
+	 * </dl></code> otherwise returns <code>null</code>.
+	 */
+	function parse(str) {
+		var regexp = /^\s*\/(.+)\/([gim]{0,3})\s*$/.exec(str);
+		if (regexp) {
+			return {
+				pattern : regexp[1],
+				flags : regexp[2]
+			};
+		}
+		return null;
+	}
+
+	return {
+		escape: escape,
+		parse: parse
+	};
+});
+
+/*******************************************************************************
+ * @license
+ * Copyright (c) 2013 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials are made 
+ * available under the terms of the Eclipse Public License v1.0 
+ * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
+ * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
+ * 
+ * Contributors: IBM Corporation - initial API and implementation
+ ******************************************************************************/
+/*global define*/
+define('orion/objects',[], function() {
+	/**
+	 * @name orion.objects
+	 * @class Object-oriented helpers.
+	 */
+	return {
+		/**
+		 * Mixes all <code>source</code>'s own enumerable properties into <code>target</code>. Multiple source objects
+		 * can be passed as varags.
+		 * @name orion.objects.mixin
+		 * @function
+		 * @static
+		 * @param {Object} target
+		 * @param {Object} source
+		 */
+		mixin: function(target/**, source..*/) {
+			Array.prototype.slice.call(arguments, 1).forEach(function(source) {
+				var keys = Object.keys(source);
+				for (var i=0; i < keys.length; i++) {
+					var key = keys[i];
+					target[key] = source[key];
+				}
+			});
+		}
+	};
+});
+
+/*******************************************************************************
+ * @license
+ * Copyright (c) 2013 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials are made 
+ * available under the terms of the Eclipse Public License v1.0 
+ * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
+ * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+/*global define prompt window*/
+
+define("orion/editor/find", [ //$NON-NLS-0$
+	'i18n!orion/editor/nls/messages', //$NON-NLS-0$
+	'orion/keyBinding', //$NON-NLS-0$
+	'orion/editor/keyModes', //$NON-NLS-0$
+	'orion/editor/annotations', //$NON-NLS-0$
+	'orion/editor/regex', //$NON-NLS-0$
+	'orion/objects', //$NON-NLS-0$
+	'orion/util' //$NON-NLS-0$
+], function(messages, mKeyBinding, mKeyModes, mAnnotations, mRegex, objects, util) {
+
+	var exports = {};
+	
+	function IncrementalFind(editor) {
+		var textView = editor.getTextView();
+		mKeyModes.KeyMode.call(this, textView);
+		this.editor = editor;
+		this._active = false;
+		this._success = true;
+		this._ignoreSelection = false;
+		this._prefix = "";
+		
+		textView.setAction("incrementalFindCancel", function() { //$NON-NLS-0$
+			this.setActive(false);
+			return true;
+		}.bind(this));
+		textView.setAction("incrementalFindBackspace", function() { //$NON-NLS-0$
+			return this._backspace();
+		}.bind(this));
+		
+		var self = this;
+		this._listener = {
+			onVerify: function(e){
+				var editor = self.editor;
+				var model = editor.getModel();
+				var start = editor.mapOffset(e.start), end = editor.mapOffset(e.end);
+				var txt = model.getText(start, end);
+				var prefix = self._prefix;
+				// TODO: mRegex is pulled in just for this one call so we can get case-insensitive search
+				// is it really necessary
+				var match = prefix.match(new RegExp("^" + mRegex.escape(txt), "i")); //$NON-NLS-1$ //$NON-NLS-0$
+				if (match && match.length > 0) {
+					prefix = self._prefix += e.text;
+					self._success = true;
+					self._status();
+					self.find(self._forward, true);
+					e.text = null;
+				}
+			},
+			onSelection: function() {
+				if (!self._ignoreSelection) {
+					self.setActive(false);
+				}
+			}
+		};
+	}
+	IncrementalFind.prototype = new mKeyModes.KeyMode();
+	objects.mixin(IncrementalFind.prototype, {
+		createKeyBindings: function() {
+			var KeyBinding = mKeyBinding.KeyBinding;
+			var bindings = [];
+			bindings.push({actionID: "incrementalFindBackspace", keyBinding: new KeyBinding(8)}); //$NON-NLS-0$
+			bindings.push({actionID: "incrementalFindCancel", keyBinding: new KeyBinding(13)}); //$NON-NLS-0$
+			bindings.push({actionID: "incrementalFindCancel", keyBinding: new KeyBinding(27)}); //$NON-NLS-0$
+			bindings.push({actionID: "incrementalFindReverse", keyBinding: new KeyBinding(38)}); //$NON-NLS-0$
+			bindings.push({actionID: "incrementalFind", keyBinding: new KeyBinding(40)}); //$NON-NLS-0$
+			bindings.push({actionID: "incrementalFindReverse", keyBinding: new KeyBinding('k', true, true)}); //$NON-NLS-1$ //$NON-NLS-0$
+			bindings.push({actionID: "incrementalFind", keyBinding: new KeyBinding('k', true)}); //$NON-NLS-1$ //$NON-NLS-0$
+			return bindings;
+		},
+		find: function(forward, incremental) {
+			this._forward = forward;
+			if (!this.isActive()) {
+				this.setActive(true);
+				return false;
+			}
+			var prefix = this._prefix;
+			if (prefix.length === 0) {
+				return false;
+			}
+			var editor = this.editor;
+			var model = editor.getModel();
+			var start;
+			if (forward) {
+				if (this._success) {
+					start = incremental ? this._start : editor.getCaretOffset() + 1;
+				} else {
+					start = 0;
+				}
+			} else {
+				if (this._success) {
+					start = incremental ? this._start : editor.getCaretOffset();
+				} else {
+					start = model.getCharCount() - 1;
+				}
+			}
+			var result = editor.getModel().find({
+				string: prefix,
+				start: start,
+				reverse: !forward,
+				caseInsensitive: prefix.toLowerCase() === prefix}).next();
+			if (result) {
+				if (!incremental) {
+					this._start = start;
+				}
+				this._success = true;
+				this._ignoreSelection = true;
+				editor.moveSelection(forward ? result.start : result.end, forward ? result.end : result.start);
+				this._ignoreSelection = false;
+			} else {
+				this._success = false;
+			}
+			this._status();
+			return true;
+		},
+		isActive: function() {
+			return this._active;
+		},
+		isStatusActive: function() {
+			return this.isActive();
+		},
+		setActive: function(active) {
+			if (this._active === active) {
+				return;
+			}
+			this._active = active;
+			this._prefix = "";
+			this._success = true;
+			var editor = this.editor;
+			var textView = editor.getTextView();
+			this._start = this.editor.getCaretOffset();
+			this.editor.setCaretOffset(this._start);
+			if (this._active) {
+				textView.addEventListener("Verify", this._listener.onVerify); //$NON-NLS-0$
+				textView.addEventListener("Selection", this._listener.onSelection); //$NON-NLS-0$
+				textView.addKeyMode(this);
+			} else {
+				textView.removeEventListener("Verify", this._listener.onVerify); //$NON-NLS-0$
+				textView.removeEventListener("Selection", this._listener.onSelection); //$NON-NLS-0$
+				textView.removeKeyMode(this);
+			}
+			this._status();
+		},
+		_backspace: function() {
+			var prefix = this._prefix;
+			prefix = this._prefix = prefix.substring(0, prefix.length-1);
+			if (prefix.length === 0) {
+				this._success = true;
+				this._ignoreSelection = true;
+				this.editor.setCaretOffset(this.editor.getSelection().start);
+				this._ignoreSelection = false;
+				this._status();
+				return true;
+			}
+			return this.find(this._forward, true);
+		},
+		_status: function() {
+			if (!this.isActive()) {
+				this.editor.reportStatus("");
+				return;
+			}
+			var msg;
+			if (this._forward) {
+				msg = this._success ? messages.incrementalFindStr : messages.incrementalFindStrNotFound;
+			} else {
+				msg = this._success ? messages.incrementalFindReverseStr : messages.incrementalFindReverseStrNotFound;
+			}
+			msg = util.formatMessage(msg, this._prefix);
+			this.editor.reportStatus(msg, this._success ? "" : "error"); //$NON-NLS-0$
+		}
+	});
+	exports.IncrementalFind = IncrementalFind;
+	
+	
+	function Find(editor, undoStack, options) {
+		if (!editor) { return; }	
+		this._editor = editor;
+		this._undoStack = undoStack;
+		this._showAll = true;
+		this._visible = false;
+		this._caseInsensitive = true;
+		this._wrap = true;
+		this._wholeWord = false;
+		this._incremental = true;
+		this._regex = false;
+		this._findAfterReplace = true;
+		this._hideAfterFind = false;
+		this._reverse = false;
+		this._start = undefined;
+		this._end = undefined;
+		this._timer = undefined;
+		this._lastString = "";
+		var that = this;
+		this._listeners = {
+			onEditorFocus: function(e) {
+				that._removeCurrentAnnotation(e);
+			}
+		};
+		this.setOptions(options);
+	}
+	Find.prototype = {
+		find: function (forward, tempOptions, incremental) {
+			this.setOptions({
+				reverse : !forward
+			});
+			var string = this.getFindString();
+			var count;
+			if (tempOptions) {
+				string = tempOptions.findString || string;
+				count =  tempOptions.count;
+			}
+			var savedOptions = this.getOptions();
+			this.setOptions(tempOptions);
+			var startOffset = incremental ? this._startOffset : this.getStartOffset();
+			var result = this._doFind(string, startOffset, count);
+			if (result) {
+				if (!incremental) {
+					this._startOffset = result.start;
+				}
+			}
+			this.setOptions(savedOptions);
+			if (this._hideAfterFind) {
+				this.hide();
+			}
+			return result;
+		},
+		getStartOffset: function() {
+			if (this._start !== undefined) {
+				return this._start;
+			}
+			if (this._reverse) {
+				return this._editor.getSelection().start - 1;
+			}
+			return this._editor.getCaretOffset();
+		},
+		getFindString: function() {
+			var selection = this._editor.getSelection();
+			return this._editor.getText(selection.start, selection.end) || this._lastString;
+		},
+		getOptions: function() {
+			return {
+				showAll: this._showAll, 
+				caseInsensitive: this._caseInsensitive, 
+				wrap: this._wrap, 
+				wholeWord: this._wholeWord, 
+				incremental: this._incremental,
+				regex: this._regex,
+				findAfterReplace: this._findAfterReplace,
+				hideAfterFind: this._hideAfterFind,
+				reverse: this._reverse,
+				findCallback: this._findCallback,
+				start: this._start,
+				end: this._end
+			};
+		},
+		getReplaceString: function() {
+			return "";
+		},
+		hide: function() {
+			this._visible = false;
+			if (this._savedOptions) {
+				this.setOptions(this._savedOptions.pop());
+				if (this._savedOptions.length === 0) {
+					this._savedOptions = null;
+				}
+			}
+			this._removeAllAnnotations();
+			this._editor.getTextView().removeEventListener("Focus", this._listeners.onEditorFocus); //$NON-NLS-0$
+			this._editor.getTextView().focus();
+		},
+		isVisible: function() {
+			return this._visible;
+		},
+		replace: function() {
+			var string = this.getFindString();
+			if (string) {
+				var editor = this._editor;
+				var replaceString = this.getReplaceString();
+				var selection = editor.getSelection();
+				var start = selection.start;
+				var result = editor.getModel().find({
+					string: string,
+					start: start,
+					reverse: false,
+					wrap: this._wrap,
+					regex: this._regex,
+					wholeWord: this._wholeWord,
+					caseInsensitive: this._caseInsensitive
+				}).next();
+				if (result) {
+					this.startUndo();
+					this._doReplace(result.start, result.end, string, replaceString);
+					this.endUndo();
+				}
+			}
+			if (this._findAfterReplace && string){
+				this._doFind(string, this.getStartOffset());
+			}
+		},
+		replaceAll : function() {
+			var string = this.getFindString();
+			if (string) {
+				this._replacingAll = true;
+				var editor = this._editor;
+				var textView = editor.getTextView();
+				editor.reportStatus(messages.replaceAll);
+				var replaceString = this.getReplaceString();
+				var self = this;
+				window.setTimeout(function() {
+					var startPos = 0;
+					var count = 0, lastResult;
+					while (true) {
+						var result = self._doFind(string, startPos);
+						if (!result) {
+							break;
+						}
+						lastResult = result;
+						count++;
+						if (count === 1) {
+							textView.setRedraw(false);
+							self.startUndo();
+						}
+						self._doReplace(result.start, result.end, string, replaceString);
+						startPos = self.getStartOffset();
+					}
+					if (count > 0) {
+						self.endUndo();
+						textView.setRedraw(true);
+					}
+					if (startPos > 0) {
+						editor.reportStatus(util.formatMessage(messages.replacedMatches, count));
+					} else {
+						editor.reportStatus(messages.nothingReplaced, "error"); //$NON-NLS-0$ 
+					}
+					self._replacingAll = false;
+				}, 100);				
+			}
+		},
+		/**
+		 * @property {String} string the search string to be found.
+		 * @property {Boolean} [regex=false] whether or not the search string is a regular expression.
+		 * @property {Boolean} [wrap=false] whether or not to wrap search.
+		 * @property {Boolean} [wholeWord=false] whether or not to search only whole words.
+		 * @property {Boolean} [caseInsensitive=false] whether or not search is case insensitive.
+		 * @property {Boolean} [reverse=false] whether or not to search backwards.
+		 * @property {Number} [start=0] The start offset to start searching
+		 * @property {Number} [end=charCount] The end offset of the search. Used to search in a given range.	
+		 */
+		setOptions : function(options) {
+			if (options) {
+				if ((options.showAll === true || options.showAll === false) && this._showAll !== options.showAll) {
+					this._showAll = options.showAll;
+					if (this.isVisible()) {
+						if (this._showAll) {
+							this._markAllOccurrences();
+						} else {
+							var annotationModel = this._editor.getAnnotationModel();
+							if (annotationModel) {
+								annotationModel.removeAnnotations(mAnnotations.AnnotationType.ANNOTATION_MATCHING_SEARCH);
+							}
+						}
+					}
+				}
+				if (options.caseInsensitive === true || options.caseInsensitive === false) {
+					this._caseInsensitive = options.caseInsensitive;
+				}
+				if (options.wrap === true || options.wrap === false) {
+					this._wrap = options.wrap;
+				}
+				if (options.wholeWord === true || options.wholeWord === false) {
+					this._wholeWord = options.wholeWord;
+				}
+				if (options.incremental === true || options.incremental === false) {
+					this._incremental = options.incremental;
+				}
+				if (options.regex === true || options.regex === false) {
+					this._regex = options.regex;
+				}
+				if (options.findAfterReplace === true || options.findAfterReplace === false) {
+					this._findAfterReplace = options.findAfterReplace;
+				}
+				if (options.hideAfterFind === true || options.hideAfterFind === false) {
+					this._hideAfterFind = options.hideAfterFind;
+				}
+				if (options.reverse === true || options.reverse === false) {
+					this._reverse = options.reverse;
+				}
+				if (options.hasOwnProperty("findCallback")) { //$NON-NLS-0$
+					this._findCallback = options.findCallback;
+				}
+				if (options.hasOwnProperty("start")) { //$NON-NLS-0$	
+					this._start = options.start;
+				}
+				if (options.hasOwnProperty("end")) { //$NON-NLS-0$
+					this._end = options.end;
+				}
+			}
+		},
+		show: function(tempOptions) {
+			this._visible = true;
+			if (tempOptions) {
+				if (!this._savedOptions) {
+					this._savedOptions = [];
+				}	
+				this._savedOptions.push(this.getOptions());
+				this.setOptions(tempOptions);
+			}
+			this._startOffset = this._editor.getSelection().start;
+			this._editor.getTextView().addEventListener("Focus", this._listeners.onEditorFocus); //$NON-NLS-0$
+			var self = this;
+			window.setTimeout(function() {
+				if (self._incremental) {
+					self.find(true, null, true);
+				}
+			}, 0);
+		},
+		startUndo: function() {
+			if (this._undoStack) {
+				this._undoStack.startCompoundChange();
+			}
+		}, 
+		endUndo: function() {
+			if (this._undoStack) {
+				this._undoStack.endCompoundChange();
+			}
+		},
+		_doFind: function(string, startOffset, count) {
+			count = count || 1;
+			var editor = this._editor;
+			if (!string) {
+				this._removeAllAnnotations();
+				return null;
+			}
+			this._lastString = string;
+			var iterator = editor.getModel().find({
+				string: string,
+				start: startOffset,
+				end: this._end,
+				reverse: this._reverse,
+				wrap: this._wrap,
+				regex: this._regex,
+				wholeWord: this._wholeWord,
+				caseInsensitive: this._caseInsensitive
+			});
+			var result;
+			for (var i=0; i<count && iterator.hasNext(); i++) {
+				result = iterator.next();
+			}
+			if (!this._replacingAll) {
+				if (result) {
+					this._editor.reportStatus("");
+				} else {
+					this._editor.reportStatus(messages.notFound, "error"); //$NON-NLS-0$
+				}
+				if (this.isVisible()) {
+					var type = mAnnotations.AnnotationType.ANNOTATION_CURRENT_SEARCH;
+					var annotationModel = editor.getAnnotationModel();
+					if (annotationModel) {
+						annotationModel.removeAnnotations(type);
+						if (result) {
+							annotationModel.addAnnotation(mAnnotations.AnnotationType.createAnnotation(type, result.start, result.end));
+						}
+					}
+					if (this._showAll) {
+						if (this._timer) {
+							window.clearTimeout(this._timer);
+						}
+						var that = this;
+						this._timer = window.setTimeout(function(){
+							that._markAllOccurrences();
+							that._timer = null;
+						}, 500);
+					}
+				}
+				if (this._findCallback) {
+					this._findCallback(result);
+				}
+				else if (result) {
+					editor.moveSelection(result.start, result.end, null, false);
+				}
+			}
+			return result;
+		},
+		_doReplace: function(start, end, searchStr, newStr) {
+			var editor = this._editor;
+			if (this._regex) {
+				newStr = editor.getText(start, end).replace(new RegExp(searchStr, this._caseInsensitive ? "i" : ""), newStr); //$NON-NLS-0$
+				if (!newStr) {
+					return;
+				}
+			}
+			editor.setText(newStr, start, end);
+			editor.setSelection(start, start + newStr.length, true);
+		},
+		_markAllOccurrences: function() {
+			var annotationModel = this._editor.getAnnotationModel();
+			if (!annotationModel) {
+				return;
+			}
+			var type = mAnnotations.AnnotationType.ANNOTATION_MATCHING_SEARCH;
+			var iter = annotationModel.getAnnotations(0, annotationModel.getTextModel().getCharCount());
+			var remove = [], add;
+			while (iter.hasNext()) {
+				var annotation = iter.next();
+				if (annotation.type === type) {
+					remove.push(annotation);
+				}
+			}
+			if (this.isVisible()) {
+				var string = this.getFindString();
+				iter = this._editor.getModel().find({
+					string: string,
+					regex: this._regex,
+					wholeWord: this._wholeWord,
+					caseInsensitive: this._caseInsensitive
+				});
+				add = [];
+				while (iter.hasNext()) {
+					var range = iter.next();
+					add.push(mAnnotations.AnnotationType.createAnnotation(type, range.start, range.end));
+				}
+			}
+			annotationModel.replaceAnnotations(remove, add);
+		},
+		_removeAllAnnotations: function() {
+			var annotationModel = this._editor.getAnnotationModel();
+			if (annotationModel) {
+				annotationModel.removeAnnotations(mAnnotations.AnnotationType.ANNOTATION_CURRENT_SEARCH);
+				annotationModel.removeAnnotations(mAnnotations.AnnotationType.ANNOTATION_MATCHING_SEARCH);
+			}
+		},
+		_removeCurrentAnnotation: function(evt){
+			var annotationModel = this._editor.getAnnotationModel();
+			if (annotationModel) {
+				annotationModel.removeAnnotations(mAnnotations.AnnotationType.ANNOTATION_CURRENT_SEARCH);
+			}
+		}
+	};
+	exports.Find = Find;
+	
+	return exports;
+});
+
+/*******************************************************************************
+ * @license
+ * Copyright (c) 2013 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials are made 
+ * available under the terms of the Eclipse Public License v1.0 
+ * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
+ * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+/*global define prompt */
+
+define("orion/editor/actions", [ //$NON-NLS-0$
+	'i18n!orion/editor/nls/messages', //$NON-NLS-0$
+	'orion/keyBinding', //$NON-NLS-0$
+	'orion/editor/annotations', //$NON-NLS-0$
+	'orion/editor/tooltip', //$NON-NLS-0$
+	'orion/editor/find', //$NON-NLS-0$
+	'orion/util' //$NON-NLS-0$
+], function(messages, mKeyBinding, mAnnotations, mTooltip, mFind, util) {
+
+	var exports = {};
+
+	/**
+	 * TextActions connects common text editing keybindings onto an editor.
+	 */
+	function TextActions(editor, undoStack, find) {
+		this.editor = editor;
+		this.undoStack = undoStack;
+		this._incrementalFind = new mFind.IncrementalFind(editor);
+		this._find = find ? find : new mFind.Find(editor, undoStack);
+		this._lastEditLocation = null;
+		this.init();
+	}
+	TextActions.prototype = {
+		init: function() {
+			var textView = this.editor.getTextView();
+			
+			this._lastEditListener = {
+				onModelChanged: function(e) {
+					if (this.editor.isDirty()) {
+						this._lastEditLocation = e.start + e.addedCharCount;
+					}
+				}.bind(this)
+			};
+			textView.addEventListener("ModelChanged", this._lastEditListener.onModelChanged); //$NON-NLS-0$
+			
+			textView.setAction("undo", function() { //$NON-NLS-0$
+				if (this.undoStack) {
+					this.undoStack.undo();
+					return true;
+				}
+				return false;
+			}.bind(this), {name: messages.undo});
+			
+			textView.setAction("redo", function() { //$NON-NLS-0$
+				if (this.undoStack) {
+					this.undoStack.redo();
+					return true;
+				}
+				return false;
+			}.bind(this), {name: messages.redo});
+			
+			textView.setKeyBinding(new mKeyBinding.KeyBinding("f", true), "find"); //$NON-NLS-1$ //$NON-NLS-0$
+			textView.setAction("find", function() { //$NON-NLS-0$
+				if (this._find) {
+					var selection = this.editor.getSelection();
+					var search = prompt(messages.find, this.editor.getText(selection.start, selection.end));
+					if (search) {
+						this._find.find(true, {findString:search});
+					}
+				}
+			}.bind(this), {name: messages.find});
+			
+			textView.setKeyBinding(new mKeyBinding.KeyBinding("k", true), "findNext"); //$NON-NLS-1$ //$NON-NLS-0$
+			textView.setAction("findNext", function(options) { //$NON-NLS-0$
+				if (this._find){
+					this._find.find(true, options);
+					return true;
+				}
+				return false;
+			}.bind(this), {name: messages.findNext});
+			
+			textView.setKeyBinding(new mKeyBinding.KeyBinding("k", true, true), "findPrevious"); //$NON-NLS-1$ //$NON-NLS-0$
+			textView.setAction("findPrevious", function(options) { //$NON-NLS-0$
+				if (this._find){
+					this._find.find(false, options);
+					return true;
+				}
+				return false;
+			}.bind(this), {name: messages.findPrevious});
+	
+			textView.setKeyBinding(new mKeyBinding.KeyBinding("j", true), "incrementalFind"); //$NON-NLS-1$ //$NON-NLS-0$
+			textView.setAction("incrementalFind", function() { //$NON-NLS-0$
+				if (this._incrementalFind) {
+					this._incrementalFind.find(true);
+				}
+				return true;
+			}.bind(this), {name: messages.incrementalFind});
+
+			textView.setKeyBinding(new mKeyBinding.KeyBinding("j", true, true), "incrementalFindReverse"); //$NON-NLS-1$ //$NON-NLS-0$
+			textView.setAction("incrementalFindReverse", function() { //$NON-NLS-0$
+				if (this._incrementalFind) {
+					this._incrementalFind.find(false);
+				}
+				return true;
+			}.bind(this), {name: messages.incrementalFindReverse});
+
+			textView.setAction("tab", function() { //$NON-NLS-0$
+				return this.indentLines();
+			}.bind(this));
+	
+			textView.setAction("shiftTab", function() { //$NON-NLS-0$
+				return this.unindentLines();
+			}.bind(this), {name: messages.unindentLines});
+			
+			textView.setKeyBinding(new mKeyBinding.KeyBinding(38, false, false, true), "moveLinesUp"); //$NON-NLS-0$
+			textView.setAction("moveLinesUp", function() { //$NON-NLS-0$
+				return this.moveLinesUp();
+			}.bind(this), {name: messages.moveLinesUp});
+			
+			textView.setKeyBinding(new mKeyBinding.KeyBinding(40, false, false, true), "moveLinesDown"); //$NON-NLS-0$
+			textView.setAction("moveLinesDown", function() { //$NON-NLS-0$
+				return this.moveLinesDown();
+			}.bind(this), {name: messages.moveLinesDown});
+			
+			textView.setKeyBinding(new mKeyBinding.KeyBinding(38, true, false, true), "copyLinesUp"); //$NON-NLS-0$
+			textView.setAction("copyLinesUp", function() { //$NON-NLS-0$
+				return this.copyLinesUp();
+			}.bind(this), {name: messages.copyLinesUp});
+			
+			textView.setKeyBinding(new mKeyBinding.KeyBinding(40, true, false, true), "copyLinesDown"); //$NON-NLS-0$
+			textView.setAction("copyLinesDown", function() { //$NON-NLS-0$
+				return this.copyLinesDown();
+			}.bind(this), {name: messages.copyLinesDown});
+			
+			textView.setKeyBinding(new mKeyBinding.KeyBinding('d', true, false, false), "deleteLines"); //$NON-NLS-1$ //$NON-NLS-0$
+			textView.setAction("deleteLines", function(data) { //$NON-NLS-0$
+				return this.deleteLines(data);
+			}.bind(this), {name: messages.deleteLines});
+			
+			textView.setKeyBinding(new mKeyBinding.KeyBinding("l", !util.isMac, false, false, util.isMac), "gotoLine"); //$NON-NLS-1$ //$NON-NLS-0$
+			textView.setAction("gotoLine", function() { //$NON-NLS-0$
+				return this.gotoLine();
+			}.bind(this), {name: messages.gotoLine});
+			
+			textView.setKeyBinding(new mKeyBinding.KeyBinding(190, true), "nextAnnotation"); //$NON-NLS-0$
+			textView.setAction("nextAnnotation", function() { //$NON-NLS-0$
+				return this.nextAnnotation(true);
+			}.bind(this), {name: messages.nextAnnotation});
+			
+			textView.setKeyBinding(new mKeyBinding.KeyBinding(188, true), "previousAnnotation"); //$NON-NLS-0$
+			textView.setAction("previousAnnotation", function() { //$NON-NLS-0$
+				return this.nextAnnotation(false);
+			}.bind(this), {name: messages.prevAnnotation});
+			
+			textView.setKeyBinding(new mKeyBinding.KeyBinding("e", true, false, true, false), "expand"); //$NON-NLS-1$ //$NON-NLS-0$
+			textView.setAction("expand", function() { //$NON-NLS-0$
+				return this.expandAnnotation(true);
+			}.bind(this), {name: messages.expand});
+	
+			textView.setKeyBinding(new mKeyBinding.KeyBinding("c", true, false, true, false), "collapse"); //$NON-NLS-1$ //$NON-NLS-0$
+			textView.setAction("collapse", function() { //$NON-NLS-0$
+				return this.expandAnnotation(false);
+			}.bind(this), {name: messages.collapse});
+	
+			textView.setKeyBinding(new mKeyBinding.KeyBinding("e", true, true, true, false), "expandAll"); //$NON-NLS-1$ //$NON-NLS-0$
+			textView.setAction("expandAll", function() { //$NON-NLS-0$
+				return this.expandAnnotations(true);
+			}.bind(this), {name: messages.expandAll});
+	
+			textView.setKeyBinding(new mKeyBinding.KeyBinding("c", true, true, true, false), "collapseAll"); //$NON-NLS-1$ //$NON-NLS-0$
+			textView.setAction("collapseAll", function() { //$NON-NLS-0$
+				return this.expandAnnotations(false);
+			}.bind(this), {name: messages.collapseAll});
+			
+			textView.setKeyBinding(new mKeyBinding.KeyBinding("q", !util.isMac, false, false, util.isMac), "lastEdit"); //$NON-NLS-1$ //$NON-NLS-0$
+			textView.setAction("lastEdit", function() { //$NON-NLS-0$
+				return this.gotoLastEdit();
+			}.bind(this), {name: messages.lastEdit});
+		},
+		copyLinesDown: function() {
+			var editor = this.editor;
+			var textView = editor.getTextView();
+			if (textView.getOptions("readonly")) { return false; } //$NON-NLS-0$
+			var model = editor.getModel();
+			var selection = editor.getSelection();
+			var firstLine = model.getLineAtOffset(selection.start);
+			var lastLine = model.getLineAtOffset(selection.end > selection.start ? selection.end - 1 : selection.end);
+			var lineStart = model.getLineStart(firstLine);
+			var lineEnd = model.getLineEnd(lastLine, true);
+			var lineCount = model.getLineCount();
+			var delimiter = "";
+			var text = model.getText(lineStart, lineEnd);
+			if (lastLine === lineCount-1) {
+				text = (delimiter = model.getLineDelimiter()) + text;
+			}
+			var insertOffset = lineEnd;
+			editor.setText(text, insertOffset, insertOffset);
+			editor.setSelection(insertOffset + delimiter.length, insertOffset + text.length);
+			return true;
+		},
+		copyLinesUp: function() {
+			var editor = this.editor;
+			var textView = editor.getTextView();
+			if (textView.getOptions("readonly")) { return false; } //$NON-NLS-0$
+			var model = editor.getModel();
+			var selection = editor.getSelection();
+			var firstLine = model.getLineAtOffset(selection.start);
+			var lastLine = model.getLineAtOffset(selection.end > selection.start ? selection.end - 1 : selection.end);
+			var lineStart = model.getLineStart(firstLine);
+			var lineEnd = model.getLineEnd(lastLine, true);
+			var lineCount = model.getLineCount();
+			var delimiter = "";
+			var text = model.getText(lineStart, lineEnd);
+			if (lastLine === lineCount-1) {
+				text += (delimiter = model.getLineDelimiter());
+			}
+			var insertOffset = lineStart;
+			editor.setText(text, insertOffset, insertOffset);
+			editor.setSelection(insertOffset, insertOffset + text.length - delimiter.length);
+			return true;
+		},
+		deleteLines: function(data) {
+			var editor = this.editor;
+			var textView = editor.getTextView();
+			if (textView.getOptions("readonly")) { return false; } //$NON-NLS-0$
+			var count = 1;
+			if (data && data.count) {
+				count = data.count;
+			}
+			var selection = editor.getSelection();
+			var model = editor.getModel();
+			var firstLine = model.getLineAtOffset(selection.start);
+			var lineStart = model.getLineStart(firstLine);
+			var lastLine;
+			if (selection.start !== selection.end || count === 1) {
+				lastLine = model.getLineAtOffset(selection.end > selection.start ? selection.end - 1 : selection.end);
+			} else {
+				lastLine = Math.min(firstLine + count - 1, model.getLineCount() - 1);
+			}
+			var lineEnd = model.getLineEnd(lastLine, true);
+			editor.setText("", lineStart, lineEnd);
+			return true;
+		},
+		expandAnnotation: function(expand) {
+			var editor = this.editor;
+			var annotationModel = editor.getAnnotationModel();
+			if(!annotationModel) { return true; }
+			var model = editor.getModel();
+			var currentOffset = editor.getCaretOffset();
+			var lineIndex = model.getLineAtOffset(currentOffset);
+			var start = model.getLineStart(lineIndex);
+			var end = model.getLineEnd(lineIndex, true);
+			if (model.getBaseModel) {
+				start = model.mapOffset(start);
+				end = model.mapOffset(end);
+				model = model.getBaseModel();
+			}
+			var annotation, iter = annotationModel.getAnnotations(start, end);
+			while (!annotation && iter.hasNext()) {
+				var a = iter.next();
+				if (a.type !== mAnnotations.AnnotationType.ANNOTATION_FOLDING) { continue; }
+				annotation = a;
+			}
+			if (annotation) {
+				if (expand !== annotation.expanded) {
+					if (expand) {
+						annotation.expand();
+					} else {
+						editor.setCaretOffset(annotation.start);
+						annotation.collapse();
+					}
+					annotationModel.modifyAnnotation(annotation);
+				}
+			}
+			return true;
+		},
+		expandAnnotations: function(expand) {
+			var editor = this.editor;
+			var textView = editor.getTextView();
+			var annotationModel = editor.getAnnotationModel();
+			if(!annotationModel) { return true; }
+			var model = editor.getModel();
+			var annotation, iter = annotationModel.getAnnotations(0, model.getCharCount());
+			textView.setRedraw(false);
+			while (iter.hasNext()) {
+				annotation = iter.next();
+				if (annotation.type !== mAnnotations.AnnotationType.ANNOTATION_FOLDING) { continue; }
+				if (expand !== annotation.expanded) {
+					if (expand) {
+						annotation.expand();
+					} else {
+						annotation.collapse();
+					}
+					annotationModel.modifyAnnotation(annotation);
+				}
+			}
+			textView.setRedraw(true);
+			return true;
+		},
+		indentLines: function() {
+			var editor = this.editor;
+			var textView = editor.getTextView();
+			if (textView.getOptions("readonly")) { return false; } //$NON-NLS-0$
+			if(!textView.getOptions("tabMode")) { return; } //$NON-NLS-0$
+			var model = editor.getModel();
+			var selection = editor.getSelection();
+			var firstLine = model.getLineAtOffset(selection.start);
+			var lastLine = model.getLineAtOffset(selection.end > selection.start ? selection.end - 1 : selection.end);
+			if (firstLine !== lastLine) {
+				var lines = [];
+				lines.push("");
+				for (var i = firstLine; i <= lastLine; i++) {
+					lines.push(model.getLine(i, true));
+				}
+				var lineStart = model.getLineStart(firstLine);
+				var lineEnd = model.getLineEnd(lastLine, true);
+				var options = textView.getOptions("tabSize", "expandTab"); //$NON-NLS-1$ //$NON-NLS-0$
+				var text = options.expandTab ? new Array(options.tabSize + 1).join(" ") : "\t"; //$NON-NLS-1$ //$NON-NLS-0$
+				editor.setText(lines.join(text), lineStart, lineEnd);
+				editor.setSelection(lineStart === selection.start ? selection.start : selection.start + text.length, selection.end + ((lastLine - firstLine + 1) * text.length));
+				return true;
+			}
+			return false;
+		},
+		gotoLastEdit: function() {
+			if (typeof this._lastEditLocation === "number")  { //$NON-NLS-0$
+				this.editor.showSelection(this._lastEditLocation);
+			}
+			return true;
+		},
+		gotoLine: function() {
+			var editor = this.editor;
+			var model = editor.getModel();
+			var line = model.getLineAtOffset(editor.getCaretOffset());
+			line = prompt(messages.gotoLinePrompty, line + 1);
+			if (line) {
+				line = parseInt(line, 10);
+				editor.onGotoLine(line - 1, 0);
+			}
+			return true;
+		},
+		moveLinesDown: function() {
+			var editor = this.editor;
+			var textView = editor.getTextView();
+			if (textView.getOptions("readonly")) { return false; } //$NON-NLS-0$
+			var model = editor.getModel();
+			var selection = editor.getSelection();
+			var firstLine = model.getLineAtOffset(selection.start);
+			var lastLine = model.getLineAtOffset(selection.end > selection.start ? selection.end - 1 : selection.end);
+			var lineCount = model.getLineCount();
+			if (lastLine === lineCount-1) {
+				return true;
+			}
+			var lineStart = model.getLineStart(firstLine);
+			var lineEnd = model.getLineEnd(lastLine, true);
+			var insertOffset = model.getLineEnd(lastLine+1, true) - (lineEnd - lineStart);
+			var text, delimiterLength = 0;
+			if (lastLine !== lineCount-2) {
+				text = model.getText(lineStart, lineEnd);
+			} else {
+				// Move delimiter following selection to front of the text
+				var lineEndNoDelimiter = model.getLineEnd(lastLine);
+				text = model.getText(lineEndNoDelimiter, lineEnd) + model.getText(lineStart, lineEndNoDelimiter);
+				delimiterLength += lineEnd - lineEndNoDelimiter;
+			}
+			this.startUndo();
+			editor.setText("", lineStart, lineEnd);
+			editor.setText(text, insertOffset, insertOffset);
+			editor.setSelection(insertOffset + delimiterLength, insertOffset + delimiterLength + text.length);
+			this.endUndo();
+			return true;
+		},
+		moveLinesUp: function() {
+			var editor = this.editor;
+			var textView = editor.getTextView();
+			if (textView.getOptions("readonly")) { return false; } //$NON-NLS-0$
+			var model = editor.getModel();
+			var selection = editor.getSelection();
+			var firstLine = model.getLineAtOffset(selection.start);
+			if (firstLine === 0) {
+				return true;
+			}
+			var lastLine = model.getLineAtOffset(selection.end > selection.start ? selection.end - 1 : selection.end);
+			var lineCount = model.getLineCount();
+			var insertOffset = model.getLineStart(firstLine - 1);
+			var lineStart = model.getLineStart(firstLine);
+			var lineEnd = model.getLineEnd(lastLine, true);
+			var text = model.getText(lineStart, lineEnd);
+			var delimiterLength = 0;
+			if (lastLine === lineCount-1) {
+				// Move delimiter preceding selection to end of text
+				var delimiterStart = model.getLineEnd(firstLine - 1);
+				var delimiterEnd = model.getLineEnd(firstLine - 1, true);
+				text += model.getText(delimiterStart, delimiterEnd);
+				lineStart = delimiterStart;
+				delimiterLength = delimiterEnd - delimiterStart;
+			}
+			this.startUndo();
+			editor.setText("", lineStart, lineEnd);
+			editor.setText(text, insertOffset, insertOffset);
+			editor.setSelection(insertOffset, insertOffset + text.length - delimiterLength);
+			this.endUndo();
+			return true;
+		},
+		nextAnnotation: function (forward) {
+			var editor = this.editor;
+			var annotationModel = editor.getAnnotationModel();
+			if(!annotationModel) { return true; }
+			var model = editor.getModel();
+			var currentOffset = editor.getCaretOffset();
+			var annotations = annotationModel.getAnnotations(forward ? currentOffset : 0, forward ? model.getCharCount() : currentOffset);
+			var foundAnnotation = null;
+			while (annotations.hasNext()) {
+				var annotation = annotations.next();
+				if (forward) {
+					if (annotation.start <= currentOffset) { continue; }
+				} else {
+					if (annotation.start >= currentOffset) { continue; }
+				}
+				switch (annotation.type) {
+					case mAnnotations.AnnotationType.ANNOTATION_ERROR:
+					case mAnnotations.AnnotationType.ANNOTATION_WARNING:
+					case mAnnotations.AnnotationType.ANNOTATION_TASK:
+					case mAnnotations.AnnotationType.ANNOTATION_BOOKMARK:
+						break;
+					default:
+						continue;
+				}
+				foundAnnotation = annotation;
+				if (forward) {
+					break;
+				}
+			}
+			if (foundAnnotation) {
+				var view = editor.getTextView();
+				var nextLine = model.getLineAtOffset(foundAnnotation.start);
+				var tooltip = mTooltip.Tooltip.getTooltip(view);
+				if (!tooltip) {
+					editor.moveSelection(foundAnnotation.start);
+					return true;
+				}
+				editor.moveSelection(foundAnnotation.start, foundAnnotation.start, function() {
+					tooltip.setTarget({
+						getTooltipInfo: function() {
+							var tooltipCoords = view.convert({
+								x: view.getLocationAtOffset(foundAnnotation.start).x, 
+								y: view.getLocationAtOffset(model.getLineStart(nextLine)).y
+							}, "document", "page"); //$NON-NLS-1$ //$NON-NLS-0$
+							return {
+								contents: [foundAnnotation],
+								x: tooltipCoords.x,
+								y: tooltipCoords.y + Math.floor(view.getLineHeight(nextLine) * 1.33)
+							};
+						}
+					}, 0);
+				});
+			}
+			return true;
+		},
+		unindentLines: function() {
+			var editor = this.editor;
+			var textView = editor.getTextView();
+			if (textView.getOptions("readonly")) { return false; } //$NON-NLS-0$
+			if(!textView.getOptions("tabMode")) { return; } //$NON-NLS-0$
+			var model = editor.getModel();
+			var selection = editor.getSelection();
+			var firstLine = model.getLineAtOffset(selection.start);
+			var lastLine = model.getLineAtOffset(selection.end > selection.start ? selection.end - 1 : selection.end);
+			var tabSize = textView.getOptions("tabSize"); //$NON-NLS-0$
+			var spaceTab = new Array(tabSize + 1).join(" "); //$NON-NLS-0$
+			var lines = [], removeCount = 0, firstRemoveCount = 0;
+			for (var i = firstLine; i <= lastLine; i++) {
+				var line = model.getLine(i, true);
+				if (model.getLineStart(i) !== model.getLineEnd(i)) {
+					if (line.indexOf("\t") === 0) { //$NON-NLS-0$
+						line = line.substring(1);
+						removeCount++;
+					} else if (line.indexOf(spaceTab) === 0) {
+						line = line.substring(tabSize);
+						removeCount += tabSize;
+					} else {
+						return true;
+					}
+				}
+				if (i === firstLine) {
+					firstRemoveCount = removeCount;
+				}
+				lines.push(line);
+			}
+			var lineStart = model.getLineStart(firstLine);
+			var lineEnd = model.getLineEnd(lastLine, true);
+			var lastLineStart = model.getLineStart(lastLine);
+			editor.setText(lines.join(""), lineStart, lineEnd);
+			var start = lineStart === selection.start ? selection.start : selection.start - firstRemoveCount;
+			var end = Math.max(start, selection.end - removeCount + (selection.end === lastLineStart+1 && selection.start !== selection.end ? 1 : 0));
+			editor.setSelection(start, end);
+			return true;
+		},
+		startUndo: function() {
+			if (this.undoStack) {
+				this.undoStack.startCompoundChange();
+			}
+		}, 
+		endUndo: function() {
+			if (this.undoStack) {
+				this.undoStack.endCompoundChange();
+			}
+		}
+	};
+	exports.TextActions = TextActions;
+	
+	/**
+	 * @param {orion.editor.Editor} editor
+	 * @param {orion.editor.UndoStack} undoStack
+	 * @param {orion.editor.ContentAssist} [contentAssist]
+	 * @param {orion.editor.LinkedMode} [linkedMode]
+	 */
+	function SourceCodeActions(editor, undoStack, contentAssist, linkedMode) {
+		this.editor = editor;
+		this.undoStack = undoStack;
+		this.contentAssist = contentAssist;
+		this.linkedMode = linkedMode;
+		if (this.contentAssist) {
+			this.contentAssist.addEventListener("ProposalApplied", this.contentAssistProposalApplied.bind(this)); //$NON-NLS-0$
+		}
+		this.init();
+	}
+	SourceCodeActions.prototype = {
+		init: function() {
+			var textView = this.editor.getTextView();
+		
+			textView.setAction("lineStart", function() { //$NON-NLS-0$
+				return this.lineStart();
+			}.bind(this));
+			
+			textView.setAction("enter", function() { //$NON-NLS-0$
+				return this.autoIndent();
+			}.bind(this));
+
+			textView.setKeyBinding(new mKeyBinding.KeyBinding(191, true), "toggleLineComment"); //$NON-NLS-0$
+			textView.setAction("toggleLineComment", function() { //$NON-NLS-0$
+				return this.toggleLineComment();
+			}.bind(this), {name: messages.toggleLineComment});
+
+			textView.setKeyBinding(new mKeyBinding.KeyBinding(191, true, !util.isMac, false, util.isMac), "addBlockComment"); //$NON-NLS-0$
+			textView.setAction("addBlockComment", function() { //$NON-NLS-0$
+				return this.addBlockComment();
+			}.bind(this), {name: messages.addBlockComment});
+			
+			textView.setKeyBinding(new mKeyBinding.KeyBinding(220, true, !util.isMac, false, util.isMac), "removeBlockComment"); //$NON-NLS-0$
+			textView.setAction("removeBlockComment", function() { //$NON-NLS-0$
+				return this.removeBlockComment();
+			}.bind(this), {name: messages.removeBlockComment});
+		},
+		autoIndent: function() {
+			var editor = this.editor;
+			var textView = editor.getTextView();
+			if (textView.getOptions("readonly")) { return false; } //$NON-NLS-0$
+			var selection = editor.getSelection();
+			if (selection.start === selection.end) {
+				var model = editor.getModel();
+				var lineIndex = model.getLineAtOffset(selection.start);
+				var lineText = model.getLine(lineIndex, true);
+				var lineStart = model.getLineStart(lineIndex);
+				var index = 0, end = selection.start - lineStart, c;
+				while (index < end && ((c = lineText.charCodeAt(index)) === 32 || c === 9)) { index++; }
+				if (index > 0) {
+					//TODO still wrong when typing inside folding
+					var prefix = lineText.substring(0, index);
+					index = end;
+					while (index < lineText.length && ((c = lineText.charCodeAt(index++)) === 32 || c === 9)) { selection.end++; }
+					editor.setText(model.getLineDelimiter() + prefix, selection.start, selection.end);
+					return true;
+				}
+			}
+			return false;
+		},
+		addBlockComment: function() {
+			var editor = this.editor;
+			var textView = editor.getTextView();
+			if (textView.getOptions("readonly")) { return false; } //$NON-NLS-0$
+			var model = editor.getModel();
+			var selection = editor.getSelection();
+			var open = "/*", close = "*/", commentTags = new RegExp("/\\*" + "|" + "\\*/", "g"); //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+			
+			var result = this._findEnclosingComment(model, selection.start, selection.end);
+			if (result.commentStart !== undefined && result.commentEnd !== undefined) {
+				return true; // Already in a comment
+			}
+			
+			var text = model.getText(selection.start, selection.end);
+			if (text.length === 0) { return true; }
+			
+			var oldLength = text.length;
+			text = text.replace(commentTags, "");
+			var newLength = text.length;
+			
+			editor.setText(open + text + close, selection.start, selection.end);
+			editor.setSelection(selection.start + open.length, selection.end + open.length + (newLength-oldLength));
+			return true;
+		},
+		/**
+		 * Called when a content assist proposal has been applied. Inserts the proposal into the
+		 * document. Activates Linked Mode if applicable for the selected proposal.
+		 * @param {orion.editor.ContentAssist#ProposalAppliedEvent} event
+		 */
+		contentAssistProposalApplied: function(event) {
+			/**
+			 * The event.proposal is an object with this shape:
+			 * {   proposal: "[proposal string]", // Actual text of the proposal
+			 *     description: "diplay string", // Optional
+			 *     positions: [{
+			 *         offset: 10, // Offset of start position of parameter i
+			 *         length: 3  // Length of parameter string for parameter i
+			 *     }], // One object for each parameter; can be null
+			 *     escapePosition: 19, // Optional; offset that caret will be placed at after exiting Linked Mode.
+			 *     style: 'emphasis', // Optional: either emphasis, noemphasis, hr to provide custom styling for the proposal
+			 *     unselectable: false // Optional: if set to true, then this proposal cannnot be selected through the keyboard
+			 * }
+			 * Offsets are relative to the text buffer.
+			 */
+			var proposal = event.data.proposal;
+			
+			//if the proposal specifies linked positions, build the model and enter linked mode
+			if (proposal.positions && proposal.positions.length > 0 && this.linkedMode) {
+				var positionGroups = [];
+				for (var i = 0; i < proposal.positions.length; ++i) {
+					positionGroups[i] = {
+						positions: [{
+							offset: proposal.positions[i].offset,
+							length: proposal.positions[i].length
+						}]
+					};
+				}
+				this.linkedMode.enterLinkedMode({
+					groups: positionGroups,
+					escapePosition: proposal.escapePosition
+				});
+			} else if (proposal.groups && proposal.groups.length > 0 && this.linkedMode) {
+				this.linkedMode.enterLinkedMode({
+					groups: proposal.groups,
+					escapePosition: proposal.escapePosition
+				});
+			} else if (proposal.escapePosition) {
+				//we don't want linked mode, but there is an escape position, so just set cursor position
+				var textView = this.editor.getTextView();
+				textView.setCaretOffset(proposal.escapePosition);
+			}
+			return true;
+		},
+		_findEnclosingComment: function(model, start, end) {
+			var open = "/*", close = "*/"; //$NON-NLS-1$ //$NON-NLS-0$
+			var firstLine = model.getLineAtOffset(start);
+			var lastLine = model.getLineAtOffset(end);
+			var i, line, extent, openPos, closePos;
+			var commentStart, commentEnd;
+			for (i=firstLine; i >= 0; i--) {
+				line = model.getLine(i);
+				extent = (i === firstLine) ? start - model.getLineStart(firstLine) : line.length;
+				openPos = line.lastIndexOf(open, extent);
+				closePos = line.lastIndexOf(close, extent);
+				if (closePos > openPos) {
+					break; // not inside a comment
+				} else if (openPos !== -1) {
+					commentStart = model.getLineStart(i) + openPos;
+					break;
+				}
+			}
+			for (i=lastLine; i < model.getLineCount(); i++) {
+				line = model.getLine(i);
+				extent = (i === lastLine) ? end - model.getLineStart(lastLine) : 0;
+				openPos = line.indexOf(open, extent);
+				closePos = line.indexOf(close, extent);
+				if (openPos !== -1 && openPos < closePos) {
+					break;
+				} else if (closePos !== -1) {
+					commentEnd = model.getLineStart(i) + closePos;
+					break;
+				}
+			}
+			return {commentStart: commentStart, commentEnd: commentEnd};
+		},
+		lineStart: function() {
+			var editor = this.editor;
+			var model = editor.getModel();
+			var caretOffset = editor.getCaretOffset();
+			var lineIndex = model.getLineAtOffset(caretOffset);
+			var lineOffset = model.getLineStart(lineIndex);
+			var lineText = model.getLine(lineIndex);
+			var offset;
+			for (offset=0; offset<lineText.length; offset++) {
+				var c = lineText.charCodeAt(offset);
+				if (!(c === 32 || c === 9)) {
+					break;
+				}
+			}
+			offset += lineOffset;
+			if (caretOffset !== offset) {
+				editor.setSelection(offset, offset);
+				return true;
+			}
+			return false;
+		},
+		removeBlockComment: function() {
+			var editor = this.editor;
+			var textView = editor.getTextView();
+			if (textView.getOptions("readonly")) { return false; } //$NON-NLS-0$
+			var model = editor.getModel();
+			var selection = editor.getSelection();
+			var open = "/*", close = "*/"; //$NON-NLS-1$ //$NON-NLS-0$
+			
+			// Try to shrink selection to a comment block
+			var selectedText = model.getText(selection.start, selection.end);
+			var newStart, newEnd;
+			var i;
+			for(i=0; i < selectedText.length; i++) {
+				if (selectedText.substring(i, i + open.length) === open) {
+					newStart = selection.start + i;
+					break;
+				}
+			}
+			for (; i < selectedText.length; i++) {
+				if (selectedText.substring(i, i + close.length) === close) {
+					newEnd = selection.start + i;
+					break;
+				}
+			}
+			
+			if (newStart !== undefined && newEnd !== undefined) {
+				editor.setText(model.getText(newStart + open.length, newEnd), newStart, newEnd + close.length);
+				editor.setSelection(newStart, newEnd);
+			} else {
+				// Otherwise find enclosing comment block
+				var result = this._findEnclosingComment(model, selection.start, selection.end);
+				if (result.commentStart === undefined || result.commentEnd === undefined) {
+					return true;
+				}
+				
+				var text = model.getText(result.commentStart + open.length, result.commentEnd);
+				editor.setText(text, result.commentStart, result.commentEnd + close.length);
+				editor.setSelection(selection.start - open.length, selection.end - close.length);
+			}
+			return true;
+		},
+		toggleLineComment: function() {
+			var editor = this.editor;
+			var textView = editor.getTextView();
+			if (textView.getOptions("readonly")) { return false; } //$NON-NLS-0$
+			var model = editor.getModel();
+			var selection = editor.getSelection();
+			var firstLine = model.getLineAtOffset(selection.start);
+			var lastLine = model.getLineAtOffset(selection.end > selection.start ? selection.end - 1 : selection.end);
+			var uncomment = true, lines = [], lineText, index;
+			for (var i = firstLine; i <= lastLine; i++) {
+				lineText = model.getLine(i, true);
+				lines.push(lineText);
+				if (!uncomment || (index = lineText.indexOf("//")) === -1) { //$NON-NLS-0$
+					uncomment = false;
+				} else {
+					if (index !== 0) {
+						var j;
+						for (j=0; j<index; j++) {
+							var c = lineText.charCodeAt(j);
+							if (!(c === 32 || c === 9)) {
+								break;
+							}
+						}
+						uncomment = j === index;
+					}
+				}
+			}
+			var text, selStart, selEnd;
+			var lineStart = model.getLineStart(firstLine);
+			var lineEnd = model.getLineEnd(lastLine, true);
+			if (uncomment) {
+				for (var k = 0; k < lines.length; k++) {
+					lineText = lines[k];
+					index = lineText.indexOf("//"); //$NON-NLS-0$
+					lines[k] = lineText.substring(0, index) + lineText.substring(index + 2);
+				}
+				text = lines.join("");
+				var lastLineStart = model.getLineStart(lastLine);
+				selStart = lineStart === selection.start ? selection.start : selection.start - 2;
+				selEnd = selection.end - (2 * (lastLine - firstLine + 1)) + (selection.end === lastLineStart+1 ? 2 : 0);
+			} else {
+				lines.splice(0, 0, "");
+				text = lines.join("//"); //$NON-NLS-0$
+				selStart = lineStart === selection.start ? selection.start : selection.start + 2;
+				selEnd = selection.end + (2 * (lastLine - firstLine + 1));
+			}
+			editor.setText(text, lineStart, lineEnd);
+			editor.setSelection(selStart, selEnd);
+			return true;
+		},
+		startUndo: function() {
+			if (this.undoStack) {
+				this.undoStack.startCompoundChange();
+			}
+		}, 
+		
+		endUndo: function() {
+			if (this.undoStack) {
+				this.undoStack.endCompoundChange();
+			}
+		}
+	};
+	exports.SourceCodeActions = SourceCodeActions;
+	
+	return exports;
+});
+
+/*******************************************************************************
+ * @license
+ * Copyright (c) 2010, 2012 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials are made 
+ * available under the terms of the Eclipse Public License v1.0 
+ * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
+ * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
+ * 
+ * Contributors: IBM Corporation - initial API and implementation
+ ******************************************************************************/
+
+/*global define*/
+
+define("orion/editor/templates", [], function() { //$NON-NLS-0$
+
+	/** 
+	 * Removes prefix from string.
+	 * @param {String} prefix
+	 * @param {String} string
+	 */
+	function chop(prefix, string) {
+		return string.substring(prefix.length);
+	}
+	
+	var tabVar = "${tab}"; //$NON-NLS-0$
+	var delimiterVar = "${delimiter}"; //$NON-NLS-0$
+	var cursorVar = "${cursor}"; //$NON-NLS-0$
+	
+	function Template (prefix, description, template) {
+		this.prefix = prefix;
+		this.description = description;
+		this.template = template;
+		this._parse();
+	}
+	Template.prototype = /** @lends orion.editor.Template.prototype */ {
+		getProposal: function(prefix, offset, context) {
+			//any returned positions need to be offset based on current cursor position and length of prefix
+			var startOffset = offset-prefix.length;
+			var groups = {};
+			var escapePosition;
+			var delimiter = context.delimiter !== undefined ? context.delimiter : "\n"; //$NON-NLS-0$
+			if (context.indentation) {
+				delimiter += context.indentation;
+			}
+			var tab = context.tab !== undefined ? context.tab : "\t"; //$NON-NLS-0$
+			var delta = 0;
+			var variables = this.variables;
+			var segments = this.segments, proposal = [];
+			for (var i = 0; i < segments.length; i++) {
+				var segment = segments[i];
+				var variable = variables[segment];
+				if (variable !== undefined) {
+					switch (segment) {
+						case tabVar:
+							segment = tab;
+							break;
+						case delimiterVar:
+							segment = delimiter;
+							break;
+						case cursorVar:
+							segment = "";
+							escapePosition = delta;
+							break;
+						default:
+							var g = groups[segment];
+							if (!g) {
+								g = groups[segment] = {data: variable.data, positions: []};
+							}
+							segment = variable.substitution;
+							if (g.data && g.data.values) { segment = g.data.values[0]; }
+							g.positions.push({
+								offset: startOffset + delta,
+								length: segment.length
+							});
+					}
+				}
+				proposal.push(segment);
+				delta += segment.length;
+			}
+			var newGroups = [];
+			for (var p in groups) {
+				if (groups.hasOwnProperty(p)) {
+					newGroups.push(groups[p]);
+				}
+			}
+			proposal = proposal.join("");
+			if (escapePosition === undefined) {
+				escapePosition = proposal.length;
+			}
+			return {
+				proposal: proposal,
+				description: this.description,
+				groups: newGroups,
+				escapePosition: startOffset + escapePosition
+			};
+		},
+		match: function(prefix) {
+			return this.prefix.indexOf(prefix) === 0;
+		},
+		_parse: function() {
+			var template = this.template;
+			var segments = [], variables = {}, segment, start = 0;
+			template = template.replace(/\n/g, delimiterVar);
+			template = template.replace(/\t/g, tabVar);
+			template.replace(/\$\{((?:[^\\}]+|\\.))*\}/g, function(group, text1, index) {
+				var text = group.substring(2,group.length-1);
+				var variable = group, substitution = text, data = null;
+				var colon = substitution.indexOf(":"); //$NON-NLS-0$
+				if (colon !== -1) {
+					substitution = substitution.substring(0, colon);
+					variable = "${"+ substitution + "}"; //$NON-NLS-1$ //$NON-NLS-0$
+					data = JSON.parse(text.substring(colon + 1).replace("\\}", "}").trim()); //$NON-NLS-1$ //$NON-NLS-0$
+				}
+				var v = variables[variable];
+				if (!v) { v = variables[variable] = {}; }
+				v.substitution = substitution;
+				if (data) {
+					v.data = data;
+				}
+				segment = template.substring(start, index);
+				if (segment) { segments.push(segment); }
+				segments.push(variable);
+				start = index + group.length;
+				return substitution;
+			});
+			segment = template.substring(start, template.length);
+			if (segment) { segments.push(segment); }
+			this.segments = segments;
+			this.variables = variables;
+		}
+	};
+	
+	function TemplateContentAssist (keywords, templates) {
+		this._keywords = keywords || [];
+		this._templates = [];
+		this.addTemplates(templates || []);
+	}
+	TemplateContentAssist.prototype = /** @lends orion.editor.TemplateContentAssist.prototype */ {
+		addTemplates: function(json) {
+			var templates = this.getTemplates();
+			for (var j = 0; j < json.length; j++) {
+				templates.push(new Template(json[j].prefix, json[j].description, json[j].template));
+			}
+		},
+		computeProposals: function(buffer, offset, context) {
+			var prefix = this.getPrefix(buffer, offset, context);
+			var proposals = [];
+			if (!this.isValid(prefix, buffer, offset, context)) {
+				return proposals;
+			}
+			proposals = proposals.concat(this.getTemplateProposals(prefix, offset, context));
+			proposals = proposals.concat(this.getKeywordProposals(prefix));
+			return proposals;
+		},
+		getKeywords: function() {
+			return this._keywords;
+		},
+		getKeywordProposals: function(prefix) {
+			var proposals = [];
+			var keywords = this.getKeywords();
+			if (keywords) {
+				for (var i = 0; i < keywords.length; i++) {
+					if (keywords[i].indexOf(prefix) === 0) {
+						proposals.push({proposal: chop(prefix, keywords[i]), description: keywords[i]});
+					}
+				}
+			}
+			return proposals;
+		},
+		getPrefix: function(buffer, offset, context) {
+			return context.prefix;
+		},
+		getTemplates: function() {
+			return this._templates;
+		},
+		getTemplateProposals: function(prefix, offset, context) {
+			var proposals = [];
+			var templates = this.getTemplates();
+			for (var t = 0; t < templates.length; t++) {
+				var template = templates[t];
+				if (template.match(prefix)) {
+					var proposal = template.getProposal(prefix, offset, context);
+					this.removePrefix(prefix, proposal);
+					proposals.push(proposal);
+				}
+			}
+			return proposals;
+		},
+		removePrefix: function(prefix, proposal) {
+			var overwrite = proposal.overwrite = proposal.proposal.substring(0, prefix.length) !== prefix;
+			if (!overwrite) {
+				proposal.proposal = chop(prefix, proposal.proposal);
+			}
+		},
+		isValid: function(prefix, buffer, offset, context) {
+			return true;
+		}
+	};
+	
+	return {
+		Template: Template,
+		TemplateContentAssist: TemplateContentAssist
+	};
+});
+
+/*******************************************************************************
+ * @license
+ * Copyright (c) 2013 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials are made 
+ * available under the terms of the Eclipse Public License v1.0 
+ * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
+ * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+/*global define */
+
+define("orion/editor/linkedMode", [ //$NON-NLS-0$
+	'i18n!orion/editor/nls/messages', //$NON-NLS-0$
+	'orion/keyBinding', //$NON-NLS-0$
+	'orion/editor/keyModes', //$NON-NLS-0$
+	'orion/editor/annotations', //$NON-NLS-0$
+	'orion/editor/templates', //$NON-NLS-0$
+	'orion/objects', //$NON-NLS-0$
+	'orion/util' //$NON-NLS-0$
+], function(messages, mKeyBinding, mKeyModes, mAnnotations, mTemplates, objects) {
+
+	var exports = {};
+
+	function LinkedMode(editor, undoStack, contentAssist) {
+		var textView = editor.getTextView();
+		mKeyModes.KeyMode.call(this, textView);
+		this.editor = editor;
+		this.undoStack = undoStack;
+		this.contentAssist = contentAssist;
+		
+		this.linkedModeModel = null;
+		
+		textView.setAction("linkedModeEnter", function() { //$NON-NLS-0$
+			this.exitLinkedMode(true);
+			return true;
+		}.bind(this));
+		textView.setAction("linkedModeCancel", function() { //$NON-NLS-0$
+			this.exitLinkedMode(false);
+			return true;
+		}.bind(this));
+		textView.setAction("linkedModeNextGroup", function() { //$NON-NLS-0$
+			var model = this.linkedModeModel;
+			this.selectLinkedGroup((model.selectedGroupIndex + 1) % model.groups.length);
+			return true;
+		}.bind(this));
+		textView.setAction("linkedModePreviousGroup", function() { //$NON-NLS-0$
+			var model = this.linkedModeModel;
+			this.selectLinkedGroup(model.selectedGroupIndex > 0 ? model.selectedGroupIndex-1 : model.groups.length-1);
+			return true;
+		}.bind(this));
+		
+		/**
+		 * Listener called when Linked Mode is active. Updates position's offsets and length
+		 * on user change. Also escapes the Linked Mode if the text buffer was modified outside of the Linked Mode positions.
+		 */
+		this.linkedModeListener = {
+		
+			onActivating: function(event) {
+				if (this._groupContentAssistProvider) {
+					this.contentAssist.setProviders([this._groupContentAssistProvider]);
+					this.contentAssist.setProgress(null);
+				}
+			}.bind(this),
+			
+			onModelChanged: function(event) {
+				if (this.ignoreVerify) { return; }
+
+				// Get the position being modified
+				var model = this.linkedModeModel, positionChanged, changed;
+				while (model) {
+					positionChanged = this._getPositionChanged(model, event.start, event.start + event.removedCharCount);
+					changed = positionChanged.position;
+					if (changed === undefined || changed.model !== model) {
+						// The change has been done outside of the positions, exit the Linked Mode
+						this.exitLinkedMode(false);
+						model = this.linkedModeModel;
+					} else {
+						break;
+					}
+				}
+				if (!model) { return; }
+
+				// Update position offsets for this change. Group changes are done in #onVerify
+				var deltaCount = 0;
+				var changeCount = event.addedCharCount - event.removedCharCount;
+				var sortedPositions = positionChanged.positions, position, pos;
+				for (var i = 0; i < sortedPositions.length; ++i) {
+					pos = sortedPositions[i];
+					position = pos.position;
+					var inside = position.offset <= event.start && event.start <= position.offset + position.length;
+					if (inside && !pos.ansestor) {
+						position.offset += deltaCount;
+						position.length += changeCount;
+						deltaCount += changeCount;
+					} else {
+						position.offset += deltaCount;
+						if (pos.ansestor && inside) {
+							position.length += changeCount;
+						}
+					}
+					if (pos.escape) {
+						pos.model.escapePosition = position.offset;
+					}
+				}
+				this._updateAnnotations(sortedPositions);
+			}.bind(this),
+
+			onVerify: function(event) {
+				if (this.ignoreVerify) { return; }
+
+				// Get the position being modified
+				var model = this.linkedModeModel, positionChanged, changed;
+				while (model) {
+					positionChanged = this._getPositionChanged(model, event.start, event.end);
+					changed = positionChanged.position;
+					if (changed === undefined || changed.model !== model) {
+						// The change has been done outside of the positions, exit the Linked Mode
+						this.exitLinkedMode(false);
+						model = this.linkedModeModel;
+					} else {
+						break;
+					}
+				}
+				if (!model) { return; }
+				
+				// Make sure changes in a same group are compound
+				var undo = this._compoundChange;
+				if (undo) {
+					if (!(undo.owner.model === model && undo.owner.group === changed.group)) {
+						this.endUndo();
+						this.startUndo();
+					}
+				} else {
+					this.startUndo();
+				}
+
+				model.selectedGroupIndex = changed.group;
+				
+				// Update position offsets taking into account all positions in the same changing group
+				var deltaCount = 0;
+				var changeCount = event.text.length - (event.end - event.start);
+				var sortedPositions = positionChanged.positions, position, pos;
+				var deltaStart = event.start - changed.position.offset, deltaEnd = event.end - changed.position.offset;
+				for (var i = 0; i < sortedPositions.length; ++i) {
+					pos = sortedPositions[i];
+					position = pos.position;
+					pos.oldOffset = position.offset;
+					if (pos.model === model && pos.group === changed.group) {
+						position.offset += deltaCount;
+						position.length += changeCount;
+						deltaCount += changeCount;
+					} else {
+						position.offset += deltaCount;
+						if (pos.ansestor) {
+							position.length += changed.count * changeCount;
+						}
+					}
+					if (pos.escape) {
+						pos.model.escapePosition = position.offset;
+					}
+				}
+				
+				// Cancel this modification and apply same modification to all positions in changing group
+				this.ignoreVerify = true;
+				var textView = this.editor.getTextView();
+				for (i = sortedPositions.length - 1; i >= 0; i--) {
+					pos = sortedPositions[i];
+					if (pos.model === model && pos.group === changed.group) {
+						textView.setText(event.text, pos.oldOffset + deltaStart , pos.oldOffset + deltaEnd);
+					}
+				}
+				this.ignoreVerify = false;
+				event.text = null;
+				this._updateAnnotations(sortedPositions);
+			}.bind(this)
+		};
+	}
+	LinkedMode.prototype = new mKeyModes.KeyMode();
+	objects.mixin(LinkedMode.prototype, {
+		createKeyBindings: function() {
+			var KeyBinding = mKeyBinding.KeyBinding;
+			var bindings = [];
+			bindings.push({actionID: "linkedModeEnter", keyBinding: new KeyBinding(13)}); //$NON-NLS-0$
+			bindings.push({actionID: "linkedModeCancel", keyBinding: new KeyBinding(27)}); //$NON-NLS-0$
+			bindings.push({actionID: "linkedModeNextGroup", keyBinding: new KeyBinding(9)}); //$NON-NLS-0$
+			bindings.push({actionID: "linkedModePreviousGroup", keyBinding: new KeyBinding(9, false, true)}); //$NON-NLS-0$
+			return bindings;
+		},
+		/**
+		 * Starts Linked Mode, selects the first position and registers the listeners.
+		 * @param {Object} linkedModeModel An object describing the model to be used by linked mode.
+		 * Contains one or more position groups. If a position in a group is edited, the other positions in
+		 * the same group are edited the same way. The model structure is as follows:
+		 * <pre>{
+		 *		groups: [{
+		 *			data: {},
+		 *			positions: [{
+		 *				offset: 10, // Relative to the text buffer
+		 *				length: 3
+		 *			}]
+		 *		}],
+		 *		escapePosition: 19, // Relative to the text buffer
+		 * }</pre>
+		 *
+		 * Each group in the model has an optional <code>data</code> property which can be
+		 * used to provide additional content assist for the group.  The <code>type</code> in
+		 * data determines what kind of content assist is provided. These are the support
+		 * structures for the <code>data</code> property.
+		 * <pre>{
+		 *		type: "link"
+		 *		values: ["proposal0", "proposal1", ...]
+		 * }</pre>
+		 *
+		 * The "link" data struture provides static content assist proposals stored in the
+		 * <code>values</code> property.
+		 *
+		 * <p>
+		 * <b>See:</b><br/>
+		 * {@link orion.editor.Template}<br/>
+		 * {@link orion.editor.TemplateContentAssist}<br/>
+		 * </p>
+		 */
+		enterLinkedMode: function(linkedModeModel) {
+			if (!this.linkedModeModel) {
+				var textView = this.editor.getTextView();
+				textView.addKeyMode(this);
+				textView.addEventListener("Verify", this.linkedModeListener.onVerify); //$NON-NLS-0$
+				textView.addEventListener("ModelChanged", this.linkedModeListener.onModelChanged); //$NON-NLS-0$
+				var contentAssist = this.contentAssist;
+				contentAssist.addEventListener("Activating", this.linkedModeListener.onActivating); //$NON-NLS-0$
+				this.editor.reportStatus(messages.linkedModeEntered, null, true);
+			}
+			this._sortedPositions = null;
+			if (this.linkedModeModel) {
+				linkedModeModel.previousModel = this.linkedModeModel;
+				linkedModeModel.parentGroup = this.linkedModeModel.selectedGroupIndex;
+				this.linkedModeModel.nextModel = linkedModeModel;
+			}
+			this.linkedModeModel = linkedModeModel;
+			this.selectLinkedGroup(0);
+		},
+		/** 
+		 * Exits Linked Mode. Optionally places the caret at linkedMode escapePosition. 
+		 * @param {Boolean} [escapePosition=false] if true, place the caret at the  escape position.
+		 */
+		exitLinkedMode: function(escapePosition) {
+			if (!this.isActive()) {
+				return;
+			}
+			if (this._compoundChange) {
+				this.endUndo();
+				this._compoundChange = null;
+			}
+			this._sortedPositions = null;
+			var model = this.linkedModeModel;
+			this.linkedModeModel = model.previousModel;
+			model.parentGroup = model.previousModel = undefined;
+			if (this.linkedModeModel) {
+				this.linkedModeModel.nextModel = undefined;
+			}
+			if (!this.linkedModeModel) {
+				var textView = this.editor.getTextView();
+				textView.removeKeyMode(this);
+				textView.removeEventListener("Verify", this.linkedModeListener.onVerify); //$NON-NLS-0$
+				textView.removeEventListener("ModelChanged", this.linkedModeListener.onModelChanged); //$NON-NLS-0$
+				var contentAssist = this.contentAssist;
+				contentAssist.removeEventListener("Activating", this.linkedModeListener.onActivating); //$NON-NLS-0$
+				contentAssist.offset = undefined;
+				this.editor.reportStatus(messages.linkedModeExited, null, true);
+				if (escapePosition) {
+					textView.setCaretOffset(model.escapePosition, false);
+				}
+			}
+			this.selectLinkedGroup(0);
+		},
+		startUndo: function() {
+			if (this.undoStack) {
+				var self = this;
+				var model = this.linkedModeModel;
+				this._compoundChange = this.undoStack.startCompoundChange({
+					model: model,
+					group: model.selectedGroupIndex,
+					end: function() {
+						self._compoundChange = null;
+					}
+				});
+			}
+		}, 
+		endUndo: function() {
+			if (this.undoStack) {
+				this.undoStack.endCompoundChange();
+			}
+		},
+		isActive: function() {
+			return !!this.linkedModeModel;
+		},
+		isStatusActive: function() {
+			return !!this.linkedModeModel;
+		},
+		selectLinkedGroup: function(index) {
+			var model = this.linkedModeModel;
+			if (model) {
+				model.selectedGroupIndex = index;
+				var group = model.groups[index];
+				var position = group.positions[0];
+				var textView = this.editor.getTextView();
+				textView.setSelection(position.offset, position.offset + position.length);
+				var contentAssist = this.contentAssist;
+				if (contentAssist) {
+					contentAssist.offset = undefined;
+					if (group.data && group.data.type === "link" && group.data.values) { //$NON-NLS-0$
+						var provider = this._groupContentAssistProvider = new mTemplates.TemplateContentAssist(group.data.values);
+						provider.getPrefix = function() {
+							var selection = textView.getSelection();
+							if (selection.start === selection.end) {
+								var caretOffset = textView.getCaretOffset();
+								if (position.offset <= caretOffset && caretOffset <= position.offset + position.length) {
+									return textView.getText(position.offset, caretOffset);
+								}
+							}
+							return "";
+						};
+						contentAssist.offset = position.offset;
+						contentAssist.deactivate();
+						contentAssist.activate();
+					} else if (this._groupContentAssistProvider) {
+						this._groupContentAssistProvider = null;
+						contentAssist.deactivate();
+					}
+				}
+			}
+			this._updateAnnotations();
+		},
+		_getModelPositions: function(all, model, delta) {
+			var groups = model.groups;
+			for (var i = 0; i < groups.length; i++) {
+				var positions = groups[i].positions;
+				for (var j = 0; j < positions.length; j++) {
+					var position = positions[j];
+					if (delta) {
+						position = {offset: position.offset + delta, length: position.length};
+					}
+					var pos = {
+						index: j,
+						group: i,
+						count: positions.length,
+						model: model,
+						position: position
+					};
+					all.push(pos);
+					if (model.nextModel && model.nextModel.parentGroup === i) {
+						pos.ansestor = true;
+						this._getModelPositions(all, model.nextModel, (delta || 0) + positions[j].offset - positions[0].offset);
+					}
+				}
+			}
+		},
+		_getSortedPositions: function(model) {
+			var all = this._sortedPositions;
+			if (!all) {
+				all = [];
+				// Get the root linked model
+				while (model.previousModel) {
+					model = model.previousModel;
+				}
+				// Get all positions under model expanding group positions of stacked linked modes
+				this._getModelPositions(all, model);
+				// Add escape position for all models
+				while (model) {
+					if (model.escapePosition !== undefined) {
+						all.push({
+							escape: true,
+							model: model,
+							position: {offset: model.escapePosition, length: 0}
+						});
+					}
+					model = model.nextModel;
+				}
+				all.sort(function(a, b) {
+					return a.position.offset - b.position.offset;
+				});
+				this._sortedPositions = all;
+			}
+			return all;
+		},
+		_getPositionChanged: function(model, start, end) {
+			var changed;
+			var sortedPositions = this._getSortedPositions(model);
+			for (var i = sortedPositions.length - 1; i >= 0; i--) {
+				var position = sortedPositions[i].position;
+				if (position.offset <= start && end <= position.offset + position.length) {
+					changed = sortedPositions[i];
+					break;
+				}
+			}
+			return {position: changed, positions: sortedPositions};
+		},
+		_updateAnnotations: function(positions) {
+			var annotationModel = this.editor.getAnnotationModel();
+			if (!annotationModel) { return; }
+			var remove = [], add = [];
+			var textModel = annotationModel.getTextModel();
+			var annotations = annotationModel.getAnnotations(0, textModel.getCharCount()), annotation;
+			while (annotations.hasNext()) {
+				annotation = annotations.next();
+				switch (annotation.type) {
+					case mAnnotations.AnnotationType.ANNOTATION_LINKED_GROUP:
+					case mAnnotations.AnnotationType.ANNOTATION_CURRENT_LINKED_GROUP:
+					case mAnnotations.AnnotationType.ANNOTATION_SELECTED_LINKED_GROUP:
+						remove.push(annotation);
+				}
+			}
+			var model = this.linkedModeModel;
+			if (model) {
+				positions = positions || this._getSortedPositions(model);
+				for (var i = 0; i < positions.length; i++) {
+					var position = positions[i];
+					if (position.model !== model) { continue; }
+					var type = mAnnotations.AnnotationType.ANNOTATION_LINKED_GROUP;
+					if (position.group === model.selectedGroupIndex) {
+						if (position.index === 0) {
+							type = mAnnotations.AnnotationType.ANNOTATION_SELECTED_LINKED_GROUP;
+						} else {
+							type = mAnnotations.AnnotationType.ANNOTATION_CURRENT_LINKED_GROUP;
+						}
+					}
+					position = position.position;
+					annotation = mAnnotations.AnnotationType.createAnnotation(type, position.offset, position.offset + position.length, "");
+					add.push(annotation);
+				}
+			}
+			annotationModel.replaceAnnotations(remove, add);
+		}
+	});
+	exports.LinkedMode = LinkedMode;
+
+	return exports;
+});
+
+/*******************************************************************************
+ * @license
+ * Copyright (c) 2013 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials are made 
+ * available under the terms of the Eclipse Public License v1.0 
+ * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
+ * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+/*global define */
+
+define("orion/editor/factories", [ //$NON-NLS-0$
+	'orion/editor/actions', //$NON-NLS-0$
+	'orion/editor/undoStack', //$NON-NLS-0$
+	'orion/editor/rulers', //$NON-NLS-0$
+	'orion/editor/annotations', //$NON-NLS-0$
+	'orion/editor/textDND', //$NON-NLS-0$
+	'orion/editor/linkedMode' //$NON-NLS-0$
+], function(mActions, mUndoStack, mRulers, mAnnotations, mTextDND, mLinkedMode) {
+
+	var exports = {};
+	
+	function KeyBindingsFactory() {
+	}
+	KeyBindingsFactory.prototype = {
+		createKeyBindings: function(editor, undoStack, contentAssist, searcher) {
+			// Create keybindings for generic editing, no dependency on the service model
+			var textActions = new mActions.TextActions(editor, undoStack , searcher);
+			// Linked Mode
+			var linkedMode = new mLinkedMode.LinkedMode(editor, undoStack, contentAssist);
+			// create keybindings for source editing
+			// TODO this should probably be something that happens more dynamically, when the editor changes input
+			var sourceCodeActions = new mActions.SourceCodeActions(editor, undoStack, contentAssist, linkedMode);
+			return {
+				textActions: textActions,
+				linkedMode: linkedMode,
+				sourceCodeActions: sourceCodeActions
+			};
+		}
+	};
+	exports.KeyBindingsFactory = KeyBindingsFactory;
+	
+	function UndoFactory() {
+	}
+	UndoFactory.prototype = {
+		createUndoStack: function(editor) {
+			var textView = editor.getTextView();
+			return new mUndoStack.UndoStack(textView, 200);
+		}
+	};
+	exports.UndoFactory = UndoFactory;
+
+	function LineNumberRulerFactory() {
+	}
+	LineNumberRulerFactory.prototype = {
+		createLineNumberRuler: function(annotationModel) {
+			return new mRulers.LineNumberRuler(annotationModel, "left", {styleClass: "ruler lines"}, {styleClass: "rulerLines odd"}, {styleClass: "rulerLines even"}); //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		}
+	};
+	exports.LineNumberRulerFactory = LineNumberRulerFactory;
+	
+	function FoldingRulerFactory() {
+	}
+	FoldingRulerFactory.prototype = {
+		createFoldingRuler: function(annotationModel) {
+			return new mRulers.FoldingRuler(annotationModel, "left", {styleClass: "ruler folding"}); //$NON-NLS-1$ //$NON-NLS-0$
+		}
+	};
+	exports.FoldingRulerFactory = FoldingRulerFactory;
+	
+	function AnnotationFactory() {
+	}
+	AnnotationFactory.prototype = {
+		createAnnotationModel: function(model) {
+			return new mAnnotations.AnnotationModel(model);
+		},
+		createAnnotationStyler: function(annotationModel, view) {
+			return new mAnnotations.AnnotationStyler(annotationModel, view);
+		},
+		createAnnotationRulers: function(annotationModel) {
+			var annotationRuler = new mRulers.AnnotationRuler(annotationModel, "left", {styleClass: "ruler annotations"}); //$NON-NLS-1$ //$NON-NLS-0$
+			var overviewRuler = new mRulers.OverviewRuler(annotationModel, "right", {styleClass: "ruler overview"}); //$NON-NLS-1$ //$NON-NLS-0$
+			return {annotationRuler: annotationRuler, overviewRuler: overviewRuler};
+		}
+	};
+	exports.AnnotationFactory = AnnotationFactory;
+	
+	function TextDNDFactory() {
+	}
+	TextDNDFactory.prototype = {
+		createTextDND: function(editor, undoStack) {
+			return new mTextDND.TextDND(editor.getTextView(), undoStack);
+		}
+	};
+	exports.TextDNDFactory = TextDNDFactory;
+	
+	return exports;
+});
+
+/*******************************************************************************
+ * @license
+ * Copyright (c) 2011, 2013 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials are made 
+ * available under the terms of the Eclipse Public License v1.0 
+ * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
+ * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+/*global define */
+
+define("orion/editor/editorFeatures", [ //$NON-NLS-0$
+	'orion/editor/factories', //$NON-NLS-0$
+	'orion/editor/actions', //$NON-NLS-0$
+	'orion/editor/linkedMode' //$NON-NLS-0$
+], function(mFactories, mActions, mLinkedMode) {
+
+	var exports = {};
+
+	exports.KeyBindingsFactory = mFactories.KeyBindingsFactory;
+	
+	exports.UndoFactory = mFactories.UndoFactory;
+
+	exports.LineNumberRulerFactory = mFactories.LineNumberRulerFactory;
+
+	exports.FoldingRulerFactory = mFactories.FoldingRulerFactory;
+
+	exports.AnnotationFactory = mFactories.AnnotationFactory;
+
+	exports.TextDNDFactory = mFactories.TextDNDFactory;
+
+	exports.TextActions = mActions.TextActions;
+
+	exports.SourceCodeActions = mActions.SourceCodeActions;
+	
+	exports.LinkedMode = mLinkedMode.LinkedMode;
+
+	return exports;
+});
+
+/*******************************************************************************
+ * @license
+ * Copyright (c) 2012 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials are made 
+ * available under the terms of the Eclipse Public License v1.0 
+ * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
+ * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
+ * 
+ * Contributors: IBM Corporation - initial API and implementation
+ ******************************************************************************/
+/*global exports module define setTimeout*/
+
+(function(root, factory) { // UMD
+	if (typeof define === "function" && define.amd) { //$NON-NLS-0$
+		define('orion/Deferred',factory);
+	} else if (typeof exports === "object") { //$NON-NLS-0$
+		module.exports = factory();
+	} else {
+		root.orion = root.orion || {};
+		root.orion.Deferred = factory();
+	}
+}(this, function() {
+	var syncQueue = [],
+		asyncQueue = [],
+		running = false;
+
+	function run() {
+		var fn;
+		while ((fn = syncQueue.shift() || asyncQueue.shift())) { //empty the sync queue first!!
+			fn();
+		}
+		running = false;
+	}
+
+	function enqueue(fn, async) {
+		var queue = async ? asyncQueue : syncQueue;
+		queue.push(fn);
+		if (!running) {
+			running = true;
+			if (async) {
+				setTimeout(run, 0);
+			} else {
+				run();
+			}
+		}
+	}
+
+	function noReturn(fn) {
+		return function() {
+			fn.apply(null, arguments);
+		};
+	}
+
+	function noop() {}
+
+	function createCancelError() {
+		var cancelError = new Error("Cancel"); //$NON-NLS-0$
+		cancelError.name = "Cancel"; //$NON-NLS-0$
+		return cancelError;
+	}
+
+	/**
+	 * @name orion.Promise
+	 * @class Interface representing an eventual value.
+	 * @description Promise is an interface that represents an eventual value returned from the single completion of an operation.
+	 *
+	 * <p>For a concrete class that implements Promise and provides additional API, see {@link orion.Deferred}.</p>
+	 * @see orion.Deferred
+	 * @see orion.Deferred#promise
+	 */
+	/**
+	 * @name then
+	 * @methodOf orion.Promise.prototype
+	 * @description Adds handlers to be called on fulfillment or progress of this promise.
+	 * @param {Function} [onResolve] Called when this promise is resolved.
+	 * @param {Function} [onReject] Called when this promise is rejected.
+	 * @param {Function} [onProgress] May be called to report progress events on this promise.
+	 * @returns {orion.Promise} A new promise that is fulfilled when the given <code>onResolve</code> or <code>onReject</code>
+	 * callback is finished. The callback's return value gives the fulfillment value of the returned promise.
+	 */
+	/**
+	 * Cancels this promise.
+	 * @name cancel
+	 * @methodOf orion.Promise.prototype
+	 * @param {Object} reason The reason for canceling this promise.
+	 * @param {Boolean} [strict]
+	 */
+
+	/**
+	 * @name orion.Deferred
+	 * @borrows orion.Promise#then as #then
+	 * @borrows orion.Promise#cancel as #cancel
+	 * @class Provides abstraction over asynchronous operations.
+	 * @description Deferred provides abstraction over asynchronous operations.
+	 *
+	 * <p>Because Deferred implements the {@link orion.Promise} interface, a Deferred may be used anywhere a Promise is called for.
+	 * However, in most such cases it is recommended to use the Deferred's {@link #promise} field instead, which exposes a 
+	 * simplified, minimally <a href="https://github.com/promises-aplus/promises-spec">Promises/A+</a>-compliant interface to callers.</p>
+	 */
+	function Deferred() {
+		var result, state, listeners = [],
+			_this = this;
+
+		function notify() {
+			var listener;
+			while ((listener = listeners.shift())) {
+				var deferred = listener.deferred;
+				var methodName = state === "resolved" ? "resolve" : "reject"; //$NON-NLS-0$ //$NON-NLS-1$ //$NON-NLS-2$
+				if (typeof listener[methodName] === "function") { //$NON-NLS-0$
+					try {
+						var listenerResult = listener[methodName](result);
+						if (listenerResult && typeof listenerResult.then === "function") { //$NON-NLS-0$
+							deferred.cancel = listenerResult.cancel || noop;
+							listenerResult.then(noReturn(deferred.resolve), noReturn(deferred.reject), deferred.progress);
+						} else {
+							deferred.resolve(listenerResult);
+						}
+					} catch (e) {
+						deferred.reject(e);
+					}
+				} else {
+					deferred[methodName](result);
+				}
+			}
+		}
+
+		/**
+		 * Rejects this Deferred.
+		 * @name reject
+		 * @methodOf orion.Deferred.prototype
+		 * @param {Object} error
+		 * @param {Boolean} [strict]
+		 * @returns {orion.Promise}
+		 */
+		this.reject = function(error, strict) {
+			if (!state) {
+				state = "rejected"; //$NON-NLS-0$
+				result = error;
+				if (listeners.length) {
+					enqueue(notify);
+				}
+			}
+			return _this.promise;
+		};
+
+		/**
+		 * Resolves this Deferred.
+		 * @name resolve
+		 * @methodOf orion.Deferred.prototype
+		 * @param {Object} value
+		 * @param {Boolean} [strict]
+		 * @returns {orion.Promise}
+		 */
+		this.resolve = function(value, strict) {
+			if (!state) {
+				state = "resolved"; //$NON-NLS-0$
+				result = value;
+				if (listeners.length) {
+					enqueue(notify);
+				}
+			}
+			return _this.promise;
+		};
+
+		/**
+		 * Notifies listeners of progress on this Deferred.
+		 * @name progress
+		 * @methodOf orion.Deferred.prototype
+		 * @param {Object} update The progress update.
+		 * @param {Boolean} [strict]
+		 * @returns {orion.Promise}
+		 */
+		this.progress = function(update, strict) {
+			if (!state) {
+				listeners.forEach(function(listener) {
+					if (listener.progress) {
+						listener.progress(update);
+					}
+				});
+			}
+			return _this.promise;
+		};
+
+		this.cancel = function() {
+			if (!state) {
+				_this.reject(createCancelError());
+			}
+		};
+
+		// Note: "then" ALWAYS returns before having onResolve or onReject called as per http://promises-aplus.github.com/promises-spec/
+		this.then = function(onResolve, onReject, onProgress) {
+			var listener = {
+				resolve: onResolve,
+				reject: onReject,
+				progress: onProgress,
+				deferred: new Deferred()
+			};
+			var deferred = listener.deferred;
+			var propagated = false;
+			var propagateCancel = function() {
+				if (propagated) {
+					return;
+				}
+				propagated = true;
+				enqueue(function() {
+					var cancel = deferred.cancel === propagateCancel ? _this.cancel : deferred.cancel;
+					if (typeof cancel === "function") {
+						cancel();
+					}
+				}, true);
+			};
+			deferred.cancel = propagateCancel;
+			listeners.push(listener);
+			if (state) {
+				enqueue(notify, true); //runAsync
+			}
+			return deferred.promise;
+		};
+
+		/**
+		 * The promise exposed by this Deferred.
+		 * @name promise
+		 * @fieldOf orion.Deferred.prototype
+		 * @type orion.Promise
+		 */
+		this.promise = Object.create(Object.prototype, {
+			then: {
+				value: _this.then
+			},
+			cancel: {
+				get: function() {
+					return _this.cancel;
+				},
+				set: function(value) {
+					_this.cancel = value;
+				}
+			}
+		});
+	}
+
+	/**
+	 * Returns a promise that represents the outcome of all the input promises.
+	 * <p>When <code>all</code> is called with a single parameter, the returned promise has <dfn>eager</dfn> semantics,
+	 * meaning that if any input promise rejects, the returned promise immediately rejects, without waiting for the rest of the
+	 * input promises to fulfill.</p>
+	 *
+	 * To obtain <dfn>lazy</dfn> semantics (meaning the returned promise waits for every input promise to fulfill), pass the
+	 * optional parameter <code>optOnError</code>.
+	 * @name all
+	 * @methodOf orion.Deferred
+	 * @static
+	 * @param {orion.Promise[]} promises The input promises.
+	 * @param {Function} [optOnError] Handles a rejected input promise. <code>optOnError</code> is invoked for every rejected
+	 * input promise, and is passed the reason the input promise was rejected. <p><code>optOnError</code> can return a value, which
+	 * allows it to act as a transformer: the return value serves as the final fulfillment value of the rejected promise in the 
+	 * results array generated by <code>all</code>.
+	 * @returns {orion.Promise} A new promise. The returned promise is generally fulfilled to an <code>Array</code> whose elements
+	 * give the fulfillment values of the input promises. <p>However, if an input promise rejects and eager semantics is used, the 
+	 * returned promise will instead be fulfilled to a single error value.</p>
+	 */
+	Deferred.all = function(promises, optOnError) {
+		var count = promises.length,
+			result = [],
+			rejected = false,
+			deferred = new Deferred();
+
+		deferred.then(null, function() {
+			rejected = true;
+			promises.forEach(function(promise) {
+				if (promise.cancel) {
+					promise.cancel();
+				}
+			});
+		});
+
+		function onResolve(i, value) {
+			if (!rejected) {
+				result[i] = value;
+				if (--count === 0) {
+					deferred.resolve(result);
+				}
+			}
+		}
+
+		function onReject(i, error) {
+			if (!rejected) {
+				if (optOnError) {
+					try {
+						onResolve(i, optOnError(error));
+						return;
+					} catch (e) {
+						error = e;
+					}
+				}
+				deferred.reject(error);
+			}
+		}
+
+		if (count === 0) {
+			deferred.resolve(result);
+		} else {
+			promises.forEach(function(promise, i) {
+				promise.then(onResolve.bind(null, i), onReject.bind(null, i));
+			});
+		}
+		return deferred.promise;
+	};
+
+	/**
+	 * Applies callbacks to a promise or to a regular object.
+	 * @name when
+	 * @methodOf orion.Deferred
+	 * @static
+	 * @param {Object|orion.Promise} value Either a {@link orion.Promise}, or a normal value.
+	 * @param {Function} onResolve Called when the <code>value</code> promise is resolved. If <code>value</code> is not a promise,
+	 * this function is called immediately.
+	 * @param {Function} onReject Called when the <code>value</code> promise is rejected. If <code>value</code> is not a promise, 
+	 * this function is never called.
+	 * @param {Function} onProgress Called when the <code>value</code> promise provides a progress update. If <code>value</code> is
+	 * not a promise, this function is never called.
+	 * @returns {orion.Promise} A new promise.
+	 */
+	Deferred.when = function(value, onResolve, onReject, onProgress) {
+		var promise, deferred;
+		if (value && typeof value.then === "function") { //$NON-NLS-0$
+			promise = value;
+		} else {
+			deferred = new Deferred();
+			deferred.resolve(value);
+			promise = deferred.promise;
+		}
+		return promise.then(onResolve, onReject, onProgress);
+	};
+
+	return Deferred;
+}));
+
+/*******************************************************************************
+ * @license
+ * Copyright (c) 2011, 2012 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials are made 
+ * available under the terms of the Eclipse Public License v1.0 
+ * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
+ * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+/*global define */
+/*jslint maxerr:150 browser:true devel:true */
+
+define("orion/editor/contentAssist", [ //$NON-NLS-0$
+	'i18n!orion/editor/nls/messages', //$NON-NLS-0$
+	'orion/keyBinding', //$NON-NLS-0$
+	'orion/editor/keyModes', //$NON-NLS-0$
+	'orion/editor/eventTarget', //$NON-NLS-0$
+	'orion/Deferred', //$NON-NLS-0$
+	'orion/objects', //$NON-NLS-0$
+	'orion/util' //$NON-NLS-0$
+], function(messages, mKeyBinding, mKeyModes, mEventTarget, Deferred, objects, util) {
+	/**
+	 * @name orion.editor.ContentAssistProvider
+	 * @class Interface defining a provider of content assist proposals.
+	 */
+	/**
+	 * @methodOf orion.editor.ContentAssistProvider.prototype
+	 * @name computeProposals
+	 * @param {String} buffer The buffer being edited.
+	 * @param {Number} offset The position in the buffer at which content assist is being requested.
+	 * @param {orion.editor.ContentAssistProvider.Context} context
+	 * @returns {Object[]} This provider's proposals for the given buffer and offset.
+	 */
+	/**
+	 * @name orion.editor.ContentAssistProvider.Context
+	 * @class
+	 * @property {String} line The text of the line on which content assist is being requested.
+	 * @property {String} prefix Any non-whitespace, non-symbol characters preceding the offset.
+	 * @property {orion.editor.Selection} selection The current selection.
+	 */
+
+	/**
+	 * @name orion.editor.ContentAssist
+	 * @class Provides content assist for a TextView.
+	 * @description Creates a <code>ContentAssist</code> for a TextView. A ContentAssist consults a set of 
+	 * {@link orion.editor.ContentAssistProvider}s to obtain proposals for text that may be inserted into a
+	 * TextView at a given offset.<p>
+	 * A ContentAssist is generally activated by its TextView action, at which point it computes the set of 
+	 * proposals available. It will re-compute the proposals in response to subsequent changes on the TextView 
+	 * (for example, user typing) for as long as the ContentAssist is active. A proposal may be applied by calling 
+	 * {@link #apply}, after which the ContentAssist becomes deactivated. An active ContentAssist may be deactivated
+	 * by calling {@link #deactivate}.<p>
+	 * A ContentAssist dispatches events when it becomes activated or deactivated, and when proposals have been computed.
+	 * @param {orion.editor.TextView} textView The TextView to provide content assist for.
+	 * @borrows orion.editor.EventTarget#addEventListener as #addEventListener
+	 * @borrows orion.editor.EventTarget#removeEventListener as #removeEventListener
+	 * @borrows orion.editor.EventTarget#dispatchEvent as #dispatchEvent
+	 */
+	/**
+	 * Dispatched when a ContentAssist is about to be activated.
+	 * @name orion.editor.ContentAssist#ActivatingEvent
+	 * @event
+	 */
+	/**
+	 * Dispatched when a ContentAssist is about to be deactivated.
+	 * @name orion.editor.ContentAssist#DeactivatingEvent
+	 * @event
+	 */
+	/**
+	 * Dispatched when a ContentAssist has applied a proposal. <p>This event's <code>data</code> field gives information
+	 * about the proposal that was applied.
+	 * @name orion.editor.ContentAssist#ProposalAppliedEvent
+	 * @event
+	 */
+	/**
+	 * Dispatched whenever a ContentAssist has obtained proposals from its providers. <p>This event's
+	 * <code>data</code> field gives information about the proposals.
+	 * @name orion.editor.ContentAssist#ProposalsComputedEvent
+	 * @event
+	 */
+	// INACTIVE --Ctrl+Space--> ACTIVE --ModelChanging--> FILTERING
+	var State = {
+		INACTIVE: 1,
+		ACTIVE: 2,
+		FILTERING: 3
+	};
+	
+	var STYLES = {
+		selected : " selected", //$NON-NLS-0$
+		hr : "proposal-hr", //$NON-NLS-0$
+		emphasis : "proposal-emphasis", //$NON-NLS-0$
+		noemphasis : "proposal-noemphasis", //$NON-NLS-0$
+		dfault : "proposal-default" //$NON-NLS-0$
+	};
+	
+	function ContentAssist(textView) {
+		this.textView = textView;
+		this.state = State.INACTIVE;
+		this.providers = [];
+		var self = this;
+		this.contentAssistListener = {
+			onModelChanging: function(event) {
+				if (self.isDeactivatingChange(event)) {
+					self.setState(State.INACTIVE);
+				} else {
+					if (self.state === State.ACTIVE) {
+						self.setState(State.FILTERING);
+					}
+				}
+			},
+			onScroll: function(event) {
+				self.setState(State.INACTIVE);
+			},
+			onSelection: function(event) {
+				var state = self.state;
+				if (state === State.ACTIVE || state === State.FILTERING) {
+					self.computeProposals();
+					self.setState(State.FILTERING);
+				}
+			}
+		};
+		textView.setKeyBinding(util.isMac ? new mKeyBinding.KeyBinding(' ', false, false, false, true) : new mKeyBinding.KeyBinding(' ', true), "contentAssist"); //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		textView.setAction("contentAssist", function() { //$NON-NLS-0$
+			self.activate();
+			return true;
+		}, {name: messages.contentAssist});
+	}
+	ContentAssist.prototype = /** @lends orion.editor.ContentAssist.prototype */ {
+		/**
+		 * Applies the given proposal to the TextView.
+		 * @param {Object} [proposal]
+		 * @returns {Boolean} <code>true</code> if the proposal was applied; <code>false</code> if no proposal was provided.
+		 */
+		apply: function(proposal) {
+			if (!proposal) {
+				return false;
+			}
+	
+			// now handle prefixes
+			// if there is a non-empty selection, then replace it,
+			// if overwrite is truthy, then also replace the prefix
+			var sel = this.textView.getSelection();
+			var start = Math.min(sel.start, sel.end);
+			var end = Math.max(sel.start, sel.end);
+			if (proposal.overwrite) {
+				start = this.getPrefixStart(start);
+			}
+
+			var data = {
+				proposal: proposal,
+				start: start,
+				end: end
+			};
+			this.setState(State.INACTIVE);
+			var proposalText = typeof proposal === "string" ? proposal : proposal.proposal; //$NON-NLS-0$
+			this.textView.setText(proposalText, start, end);
+			this.dispatchEvent({type: "ProposalApplied", data: data}); //$NON-NLS-0$
+			return true;
+		},
+		activate: function() {
+			if (this.state === State.INACTIVE) {
+				this.setState(State.ACTIVE);
+			}
+		},
+		deactivate: function() {
+			this.setState(State.INACTIVE);
+		},
+		/** @returns {orion.editor.TextView} */
+		getTextView: function() {
+			return this.textView;
+		},
+		/** @returns {Boolean} */
+		isActive: function() {
+			return this.state === State.ACTIVE || this.state === State.FILTERING;
+		},
+		/** @returns {Boolean} <code>true</code> if the event describes a change that should deactivate content assist. */
+		isDeactivatingChange: function(/**orion.editor.ModelChangingEvent*/ event) {
+			var deletion = event.removedCharCount > 0 && event.addedCharCount === 0,
+			    view = this.textView,
+			    overWhitespace = (event.start+1 <= view.getModel().getCharCount()) && /^\s*$/.test(view.getText(event.start, event.start+1));
+			return event.removedLineCount > 0 || event.addedLineCount > 0 || (deletion && overWhitespace);
+		},
+		/** @private */
+		setState: function(state) {
+			var eventType;
+			if (state === State.ACTIVE) {
+				eventType = "Activating"; //$NON-NLS-0$
+				if (this._mode) { this._mode.setActive(true); }
+				
+			} else if (state === State.INACTIVE) {
+				eventType = "Deactivating"; //$NON-NLS-0$
+				if (this._mode) { this._mode.setActive(false); }
+			}
+			if (eventType) {
+				this.dispatchEvent({type: eventType});
+			}
+			this.state = state;
+			this.onStateChange(state);
+		},
+		setMode: function(mode) {
+			this._mode = mode;
+		},
+		/** @private */
+		onStateChange: function(state) {
+			if (state === State.INACTIVE) {
+				if (this.listenerAdded) {
+					this.textView.removeEventListener("ModelChanging", this.contentAssistListener.onModelChanging); //$NON-NLS-0$
+					this.textView.removeEventListener("Scroll", this.contentAssistListener.onScroll); //$NON-NLS-0$
+					this.textView.removeEventListener("Selection", this.contentAssistListener.onSelection); //$NON-NLS-0$
+					this.listenerAdded = false;
+				}
+			} else if (state === State.ACTIVE) {
+				if (!this.listenerAdded) {
+					this.textView.addEventListener("ModelChanging", this.contentAssistListener.onModelChanging); //$NON-NLS-0$
+					this.textView.addEventListener("Scroll", this.contentAssistListener.onScroll); //$NON-NLS-0$
+					this.textView.addEventListener("Selection", this.contentAssistListener.onSelection); //$NON-NLS-0$
+					this.listenerAdded = true;
+				}
+				this.computeProposals();
+			}
+		},
+		/**
+		 * Computes the proposals at the TextView's current caret offset.
+		 */
+		computeProposals: function() {
+			var self = this;
+			var offset = this.textView.getCaretOffset();
+			this._computeProposals(offset).then(function(proposals) {
+				if (!self.isActive()) { return; }
+				self.dispatchEvent({type: "ProposalsComputed", data: {proposals: proposals}}); //$NON-NLS-0$
+			});
+		},
+		/** @private */
+		getPrefixStart: function(end) {
+			var index = end;
+			while (index > 0 && /[A-Za-z0-9_]/.test(this.textView.getText(index - 1, index))) {
+				index--;
+			}
+			return index;
+		},
+		handleError: function(error) {
+			if (typeof console !== "undefined") { //$NON-NLS-0$
+				console.log("Error retrieving content assist proposals"); //$NON-NLS-0$
+				console.log(error);
+			}
+		},
+		/**
+		 * @private
+		 * Retrieves the proposals at the given offset.
+		 * @param {Number} offset The caret offset.
+		 * @returns {Deferred} A promise that will provide the proposals.
+		 */
+		_computeProposals: function(offset) {
+			var providers = this.providers;
+			var textView = this.textView, textModel = textView.getModel();
+			var buffer = textView.getText();
+			var line = textModel.getLine(textModel.getLineAtOffset(offset));
+			var index = 0;
+			while (index < line.length && /\s/.test(line.charAt(index))) {
+				index++;
+			}
+			var indentation = line.substring(0, index);
+			var options = textView.getOptions("tabSize", "expandTab"); //$NON-NLS-1$ //$NON-NLS-0$
+			var tab = options.expandTab ? new Array(options.tabSize + 1).join(" ") : "\t"; //$NON-NLS-1$ //$NON-NLS-0$
+			var context = {
+				line: line,
+				prefix: textView.getText(this.getPrefixStart(offset), offset),
+				selection: textView.getSelection(),
+				delimiter: textModel.getLineDelimiter(),
+				tab: tab,
+				indentation: indentation
+			};
+			var self = this;
+			var promises = providers.map(function(provider) {
+				//prefer computeProposals but support getProposals for backwards compatibility
+				var func = provider.computeProposals || provider.getProposals;
+				var proposals;
+				try {
+					if (typeof func === "function") { //$NON-NLS-0$
+						proposals = self.progress ? self.progress.progress(func.apply(provider, [buffer, offset, context]), "Generating content assist proposal"): func.apply(provider, [buffer, offset, context]);
+					}
+				} catch (e) {
+					self.handleError(e);
+				}
+				return Deferred.when(proposals);
+			});
+			return Deferred.all(promises, this.handleError).then(function(proposalArrays) {
+				return proposalArrays.reduce(function(prev, curr) {
+					return (curr instanceof Array) ? prev.concat(curr) : prev;
+				}, []);
+			});
+		},
+		/**
+		 * Sets the content assist providers that this ContentAssist will consult to obtain proposals.
+		 * @param {orion.editor.ContentAssistProvider[]} providers The providers.
+		 */
+		setProviders: function(providers) {
+			this.providers = providers.slice(0);
+		},
+		
+		/**
+		 * Sets the progress handler that will display progress information, if any are generated by content assist providers.
+		 */
+		setProgress: function(progress){
+			this.progress = progress;
+		}
+	};
+	mEventTarget.EventTarget.addMixin(ContentAssist.prototype);
+
+	/**
+	 * @name orion.editor.ContentAssistMode
+	 * @class Editor mode for interacting with content assist proposals.
+	 * @description Creates a ContentAssistMode. A ContentAssistMode is a key mode for {@link orion.editor.Editor}
+	 * that provides interaction with content assist proposals retrieved from an {@link orion.editor.ContentAssist}. 
+	 * Interaction is performed via the {@link #lineUp}, {@link #lineDown}, and {@link #enter} actions. An 
+	 * {@link orion.editor.ContentAssistWidget} may optionally be provided to display which proposal is currently selected.
+	 * @param {orion.editor.ContentAssist} contentAssist
+	 * @param {orion.editor.ContentAssistWidget} [ContentAssistWidget]
+	 */
+	function ContentAssistMode(contentAssist, ContentAssistWidget) {
+		var textView = contentAssist.textView;
+		mKeyModes.KeyMode.call(this, textView);
+		this.contentAssist = contentAssist;
+		this.widget = ContentAssistWidget;
+		this.proposals = [];
+		var self = this;
+		this.contentAssist.addEventListener("ProposalsComputed", function(event) { //$NON-NLS-0$
+			self.proposals = event.data.proposals;
+			self.selectedIndex = self.proposals.length ? 0 : -1;
+		});
+		textView.setAction("contentAssistApply", function() { //$NON-NLS-0$
+			return this.enter();
+		}.bind(this));
+		textView.setAction("contentAssistCancel", function() { //$NON-NLS-0$
+			return this.cancel();
+		}.bind(this));
+		textView.setAction("contentAssistNextProposal", function() { //$NON-NLS-0$
+			return this.lineDown();
+		}.bind(this));
+		textView.setAction("contentAssistPreviousProposal", function() { //$NON-NLS-0$
+			return this.lineUp();
+		}.bind(this));
+		textView.setAction("contentAssistTab", function() { //$NON-NLS-0$
+			return this.tab();
+		}.bind(this));
+	}
+	ContentAssistMode.prototype = new mKeyModes.KeyMode();
+	objects.mixin(ContentAssistMode.prototype, {
+		createKeyBindings: function() {
+			var KeyBinding = mKeyBinding.KeyBinding;
+			var bindings = [];
+			bindings.push({actionID: "contentAssistApply", keyBinding: new KeyBinding(13)}); //$NON-NLS-0$
+			bindings.push({actionID: "contentAssistCancel", keyBinding: new KeyBinding(27)}); //$NON-NLS-0$
+			bindings.push({actionID: "contentAssistNextProposal", keyBinding: new KeyBinding(40)}); //$NON-NLS-0$
+			bindings.push({actionID: "contentAssistPreviousProposal", keyBinding: new KeyBinding(38)}); //$NON-NLS-0$
+			bindings.push({actionID: "contentAssistTab", keyBinding: new KeyBinding(9)}); //$NON-NLS-0$
+			return bindings;
+		},
+		cancel: function() {
+			this.getContentAssist().deactivate();
+		},
+		/** @private */
+		getContentAssist: function() {
+			return this.contentAssist;
+		},
+		isActive: function() {
+			return this.getContentAssist().isActive();
+		},
+		setActive: function(active) {
+			if (active) {
+				this.contentAssist.textView.addKeyMode(this);
+			} else {
+				this.contentAssist.textView.removeKeyMode(this);
+			}
+		},
+		lineUp: function() {
+			var newSelected = (this.selectedIndex === 0) ? this.proposals.length - 1 : this.selectedIndex - 1;
+			while (this.proposals[newSelected].unselectable && newSelected > 0) {
+				newSelected--;
+			}
+			this.selectedIndex = newSelected;
+			if (this.widget) {
+				this.widget.setSelectedIndex(this.selectedIndex);
+			}
+			return true;
+		},
+		lineDown: function() {
+			var newSelected = (this.selectedIndex === this.proposals.length - 1) ? 0 : this.selectedIndex + 1;
+			while (this.proposals[newSelected].unselectable && newSelected < this.proposals.length-1) {
+				newSelected++;
+			}
+			this.selectedIndex = newSelected;
+			if (this.widget) {
+				this.widget.setSelectedIndex(this.selectedIndex);
+			}
+			return true;
+		},
+		enter: function() {
+			var proposal = this.proposals[this.selectedIndex] || null;
+			return this.contentAssist.apply(proposal);
+		},
+		tab: function() {
+			if (this.widget) {
+				this.widget.createAccessible(this);
+				this.widget.parentNode.focus();
+				return true;
+			} else {
+				return false;
+			}
+		}
+	});
+
+	/**
+	 * @name orion.editor.ContentAssistWidget
+	 * @class Displays proposals from a {@link orion.editor.ContentAssist}.
+	 * @description Creates a ContentAssistWidget that will display proposals from the given {@link orion.editor.ContentAssist}
+	 * in the given <code>parentNode</code>. Clicking a proposal will cause the ContentAssist to apply that proposal.
+	 * @param {orion.editor.ContentAssist} contentAssist
+	 * @param {String|DomNode} [parentNode] The ID or DOM node to use as the parent for displaying proposals. If not provided,
+	 * a new DIV will be created inside &lt;body&gt; and assigned the CSS class <code>contentassist</code>.
+	 */
+	function ContentAssistWidget(contentAssist, parentNode) {
+		this.contentAssist = contentAssist;
+		this.textView = this.contentAssist.getTextView();
+		this.textViewListenerAdded = false;
+		this.isShowing = false;
+		var document = this.textView.getOptions("parent").ownerDocument; //$NON-NLS-0$
+		this.parentNode = typeof parentNode === "string" ? document.getElementById(parentNode) : parentNode; //$NON-NLS-0$
+		if (!this.parentNode) {
+			this.parentNode = util.createElement(document, "div"); //$NON-NLS-0$
+			this.parentNode.className = "contentassist"; //$NON-NLS-0$
+			var body = document.getElementsByTagName("body")[0]; //$NON-NLS-0$
+			if (body) {
+				body.appendChild(this.parentNode);
+			} else {
+				throw new Error("parentNode is required"); //$NON-NLS-0$
+			}
+		}
+		var self = this;
+		this.textViewListener = {
+			onMouseDown: function(event) {
+				if (event.event.target.parentElement !== self.parentNode) {
+					self.contentAssist.deactivate();
+				}
+				// ignore the event if this is a click inside of the parentNode
+				// the click is handled by the onClick() function
+			}
+		};
+		this.contentAssist.addEventListener("ProposalsComputed", function(event) { //$NON-NLS-0$
+			self.setProposals(event.data.proposals);
+			self.show();
+			if (!self.textViewListenerAdded) {
+				self.textView.addEventListener("MouseDown", self.textViewListener.onMouseDown); //$NON-NLS-0$
+				self.textViewListenerAdded = true;
+			}
+		});
+		this.contentAssist.addEventListener("Deactivating", function(event) { //$NON-NLS-0$
+			self.setProposals([]);
+			self.hide();
+			if (self.textViewListenerAdded) {
+				self.textView.removeEventListener("MouseDown", self.textViewListener.onMouseDown); //$NON-NLS-0$
+				self.textViewListenerAdded = false;
+			}
+			self.textViewListenerAdded = false;
+		});
+		this.scrollListener = function(e) {
+			if (self.isShowing) {
+				self.position();
+			}
+		};
+		if (document.addEventListener) {
+			document.addEventListener("scroll", this.scrollListener); //$NON-NLS-0$
+		}
+	}
+	ContentAssistWidget.prototype = /** @lends orion.editor.ContentAssistWidget.prototype */ {
+		/** @private */
+		onClick: function(e) {
+			this.contentAssist.apply(this.getProposal(e.target));
+			this.textView.focus();
+		},
+		/** @private */
+		createDiv: function(proposal, isSelected, parent, itemIndex) {
+			var document = parent.ownerDocument;
+			var div = util.createElement(document, "div"); //$NON-NLS-0$
+			div.id = "contentoption" + itemIndex; //$NON-NLS-0$
+			div.setAttribute("role", "option"); //$NON-NLS-1$ //$NON-NLS-0$
+			var node;
+			if (proposal.style === "hr") { //$NON-NLS-0$
+				node = util.createElement(document, "hr"); //$NON-NLS-0$
+			} else {
+				div.className = this.calculateClasses(proposal.style, isSelected);
+				node = document.createTextNode(this.getDisplayString(proposal));
+				if (isSelected) {
+					this.parentNode.setAttribute("aria-activedescendant", div.id); //$NON-NLS-0$
+				}
+			}
+			div.appendChild(node, div);
+			parent.appendChild(div);
+		},
+		/** @private */
+		createAccessible: function(mode) {
+			if(!this._isAccessible) {
+				this.parentNode.addEventListener("keydown", function(evt) { //$NON-NLS-0$
+					evt.preventDefault();
+					if(evt.keyCode === 27) {return mode.cancel(); }
+					else if(evt.keyCode === 38) { return mode.lineUp(); }
+					else if(evt.keyCode === 40) { return mode.lineDown(); }
+					else if(evt.keyCode === 13) { return mode.enter(); }
+					return false;
+				});
+			}
+			this._isAccessible = true;
+		},
+		/** @private */
+		calculateClasses : function(style, isSelected) {
+			var cssClass = STYLES[style];
+			if (!cssClass) {
+				cssClass = STYLES.dfault;
+			}
+			return isSelected ? cssClass + STYLES.selected : cssClass;
+		},
+		/** @private */
+		getDisplayString: function(proposal) {
+			//for simple string content assist, the display string is just the proposal
+			if (typeof proposal === "string") { //$NON-NLS-0$
+				return proposal;
+			}
+			//return the description if applicable
+			if (proposal.description && typeof proposal.description === "string") { //$NON-NLS-0$
+				return proposal.description;
+			}
+			//by default return the straight proposal text
+			return proposal.proposal;
+		},
+		/**
+		 * @private
+		 * @returns {Object} The proposal represented by the given node.
+		 */
+		getProposal: function(/**DOMNode*/ node) {
+			var nodeIndex = 0;
+			for (var child = this.parentNode.firstChild; child !== null; child = child.nextSibling) {
+				if (child === node) {
+					return this.proposals[nodeIndex] || null;
+				}
+				nodeIndex++;
+			}
+			return null;
+		},
+		/** Sets the index of the currently selected proposal. */
+		setSelectedIndex: function(/**Number*/ index) {
+			this.selectedIndex = index;
+			this.selectNode(this.parentNode.childNodes[this.selectedIndex]);
+		},
+		/** @private */
+		selectNode: function(/**DOMNode*/ node) {
+			var nodes = this.parentNode.childNodes;
+			for (var i=0; i < nodes.length; i++) {
+				var child = nodes[i];
+				var selIndex = child.className.indexOf(STYLES.selected);
+				if (selIndex >= 0) {
+					child.className = child.className.substring(0, selIndex) + 
+							child.className.substring(selIndex + STYLES.selected.length);
+				}
+				if (child === node) {
+					child.className = child.className + STYLES.selected;
+					this.parentNode.setAttribute("aria-activedescendant", child.id); //$NON-NLS-0$
+					child.focus();
+					if (child.offsetTop < this.parentNode.scrollTop) {
+						child.scrollIntoView(true);
+					} else if ((child.offsetTop + child.offsetHeight) > (this.parentNode.scrollTop + this.parentNode.clientHeight)) {
+						child.scrollIntoView(false);
+					}
+				}
+			}
+		},
+		setProposals: function(/**Object[]*/ proposals) {
+			this.proposals = proposals;
+		},
+		show: function() {
+			if (this.proposals.length === 0) {
+				this.hide();
+				return;
+			}
+			this.parentNode.innerHTML = "";
+			for (var i = 0; i < this.proposals.length; i++) {
+				this.createDiv(this.proposals[i], i===0, this.parentNode, i);
+			}
+			this.position();
+			this.parentNode.onclick = this.onClick.bind(this);
+			this.isShowing = true;
+		},
+		hide: function() {
+			if(this.parentNode.ownerDocument.activeElement === this.parentNode) {
+				this.textView.focus();
+			}
+			this.parentNode.style.display = "none"; //$NON-NLS-0$
+			this.parentNode.onclick = null;
+			this.isShowing = false;
+		},
+		position: function() {
+			var contentAssist = this.contentAssist;
+			var offset = contentAssist.offset !== undefined ? contentAssist.offset : this.textView.getCaretOffset();
+			var caretLocation = this.textView.getLocationAtOffset(offset);
+			caretLocation.y += this.textView.getLineHeight();
+			this.textView.convert(caretLocation, "document", "page"); //$NON-NLS-1$ //$NON-NLS-0$
+			this.parentNode.style.position = "fixed"; //$NON-NLS-0$
+			this.parentNode.style.left = caretLocation.x + "px"; //$NON-NLS-0$
+			this.parentNode.style.top = caretLocation.y + "px"; //$NON-NLS-0$
+			this.parentNode.style.display = "block"; //$NON-NLS-0$
+			this.parentNode.scrollTop = 0;
+
+			// Make sure that the panel is never outside the viewport
+			var document = this.parentNode.ownerDocument;
+			var viewportWidth = document.documentElement.clientWidth,
+			    viewportHeight =  document.documentElement.clientHeight;
+			if (caretLocation.y + this.parentNode.offsetHeight > viewportHeight) {
+				this.parentNode.style.top = (caretLocation.y - this.parentNode.offsetHeight - this.textView.getLineHeight()) + "px"; //$NON-NLS-0$
+			}
+			if (caretLocation.x + this.parentNode.offsetWidth > viewportWidth) {
+				this.parentNode.style.left = (viewportWidth - this.parentNode.offsetWidth) + "px"; //$NON-NLS-0$
+			}
+		}
+	};
+	return {
+		ContentAssist: ContentAssist,
+		ContentAssistMode: ContentAssistMode,
+		ContentAssistWidget: ContentAssistWidget
+	};
+});
+
+/*******************************************************************************
+ * @license
+ * Copyright (c) 2010, 2012 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials are made 
+ * available under the terms of the Eclipse Public License v1.0 
+ * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
+ * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
+ * 
+ * Contributors: IBM Corporation - initial API and implementation
+ ******************************************************************************/
+
+/*global define*/
+
+define("orion/editor/keywords", [], function() { //$NON-NLS-0$
+
+	var JS_KEYWORDS = [
+		"break", //$NON-NLS-0$
+		"case", "class", "catch", "continue", "const", //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"debugger", "default", "delete", "do", //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"else", "enum", "export", "extends", //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"false", "finally", "for", "function", //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"if", "implements", "import", "in", "instanceof", "interface", //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"let", //$NON-NLS-0$
+		"new", "null", //$NON-NLS-1$ //$NON-NLS-0$
+		"package", "private", "protected", "public", //$NON-NLS-0$ //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		"return", //$NON-NLS-0$
+		"static", "super", "switch", //$NON-NLS-0$ //$NON-NLS-1$ //$NON-NLS-2$
+		"this", "throw", "true", "try", "typeof", //$NON-NLS-0$ //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+		"undefined", //$NON-NLS-0$
+		"var", "void", //$NON-NLS-0$ //$NON-NLS-1$
+		"while", "with", //$NON-NLS-0$ //$NON-NLS-1$
+		"yield" //$NON-NLS-0$
+	];
+
+	var CSS_KEYWORDS = [
+		"alignment-adjust", "alignment-baseline", "animation", "animation-delay", "animation-direction", "animation-duration", //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"animation-iteration-count", "animation-name", "animation-play-state", "animation-timing-function", "appearance", //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"azimuth", "backface-visibility", "background", "background-attachment", "background-clip", "background-color", //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"background-image", "background-origin", "background-position", "background-repeat", "background-size", "baseline-shift", //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"binding", "bleed", "bookmark-label", "bookmark-level", "bookmark-state", "bookmark-target", "border", "border-bottom", //$NON-NLS-7$ //$NON-NLS-6$ //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"border-bottom-color", "border-bottom-left-radius", "border-bottom-right-radius", "border-bottom-style", "border-bottom-width", //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"border-collapse", "border-color", "border-image", "border-image-outset", "border-image-repeat", "border-image-slice", //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"border-image-source", "border-image-width", "border-left", "border-left-color", "border-left-style", "border-left-width", //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"border-radius", "border-right", "border-right-color", "border-right-style", "border-right-width", "border-spacing", "border-style", //$NON-NLS-6$ //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"border-top", "border-top-color", "border-top-left-radius", "border-top-right-radius", "border-top-style", "border-top-width", //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"border-width", "bottom", "box-align", "box-decoration-break", "box-direction", "box-flex", "box-flex-group", "box-lines", //$NON-NLS-7$ //$NON-NLS-6$ //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"box-ordinal-group", "box-orient", "box-pack", "box-shadow", "box-sizing", "break-after", "break-before", "break-inside", //$NON-NLS-7$ //$NON-NLS-6$ //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"caption-side", "clear", "clip", "color", "color-profile", "column-count", "column-fill", "column-gap", "column-rule", //$NON-NLS-8$ //$NON-NLS-7$ //$NON-NLS-6$ //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"column-rule-color", "column-rule-style", "column-rule-width", "column-span", "column-width", "columns", "content", "counter-increment", //$NON-NLS-7$ //$NON-NLS-6$ //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"counter-reset", "crop", "cue", "cue-after", "cue-before", "cursor", "direction", "display", "dominant-baseline", //$NON-NLS-8$ //$NON-NLS-7$ //$NON-NLS-6$ //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"drop-initial-after-adjust", "drop-initial-after-align", "drop-initial-before-adjust", "drop-initial-before-align", "drop-initial-size", //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"drop-initial-value", "elevation", "empty-cells", "fit", "fit-position", "flex-align", "flex-flow", "flex-inline-pack", "flex-order", //$NON-NLS-8$ //$NON-NLS-7$ //$NON-NLS-6$ //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"flex-pack", "float", "float-offset", "font", "font-family", "font-size", "font-size-adjust", "font-stretch", "font-style", //$NON-NLS-8$ //$NON-NLS-7$ //$NON-NLS-6$ //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"font-variant", "font-weight", "grid-columns", "grid-rows", "hanging-punctuation", "height", "hyphenate-after", //$NON-NLS-7$ //$NON-NLS-6$ //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"hyphenate-before", "hyphenate-character", "hyphenate-lines", "hyphenate-resource", "hyphens", "icon", "image-orientation", //$NON-NLS-7$ //$NON-NLS-6$ //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"image-rendering", "image-resolution", "inline-box-align", "left", "letter-spacing", "line-height", "line-stacking", //$NON-NLS-7$ //$NON-NLS-6$ //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"line-stacking-ruby", "line-stacking-shift", "line-stacking-strategy", "list-style", "list-style-image", "list-style-position", //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"list-style-type", "margin", "margin-bottom", "margin-left", "margin-right", "margin-top", "mark", "mark-after", "mark-before", //$NON-NLS-8$ //$NON-NLS-7$ //$NON-NLS-6$ //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"marker-offset", "marks", "marquee-direction", "marquee-loop", "marquee-play-count", "marquee-speed", "marquee-style", "max-height", //$NON-NLS-7$ //$NON-NLS-6$ //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"max-width", "min-height", "min-width", "move-to", "nav-down", "nav-index", "nav-left", "nav-right", "nav-up", "opacity", "orphans", //$NON-NLS-10$ //$NON-NLS-9$ //$NON-NLS-8$ //$NON-NLS-7$ //$NON-NLS-6$ //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"outline", "outline-color", "outline-offset", "outline-style", "outline-width", "overflow", "overflow-style", "overflow-x", //$NON-NLS-7$ //$NON-NLS-6$ //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"overflow-y", "padding", "padding-bottom", "padding-left", "padding-right", "padding-top", "page", "page-break-after", "page-break-before", //$NON-NLS-8$ //$NON-NLS-7$ //$NON-NLS-6$ //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"page-break-inside", "page-policy", "pause", "pause-after", "pause-before", "perspective", "perspective-origin", "phonemes", "pitch", //$NON-NLS-8$ //$NON-NLS-7$ //$NON-NLS-6$ //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"pitch-range", "play-during", "position", "presentation-level", "punctuation-trim", "quotes", "rendering-intent", "resize", //$NON-NLS-7$ //$NON-NLS-6$ //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"rest", "rest-after", "rest-before", "richness", "right", "rotation", "rotation-point", "ruby-align", "ruby-overhang", "ruby-position", //$NON-NLS-9$ //$NON-NLS-8$ //$NON-NLS-7$ //$NON-NLS-6$ //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"ruby-span", "size", "speak", "speak-header", "speak-numeral", "speak-punctuation", "speech-rate", "stress", "string-set", "table-layout", //$NON-NLS-9$ //$NON-NLS-8$ //$NON-NLS-7$ //$NON-NLS-6$ //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"target", "target-name", "target-new", "target-position", "text-align", "text-align-last", "text-decoration", "text-emphasis", //$NON-NLS-7$ //$NON-NLS-6$ //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"text-height", "text-indent", "text-justify", "text-outline", "text-shadow", "text-transform", "text-wrap", "top", "transform", //$NON-NLS-8$ //$NON-NLS-7$ //$NON-NLS-6$ //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"transform-origin", "transform-style", "transition", "transition-delay", "transition-duration", "transition-property", //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"transition-timing-function", "unicode-bidi", "vertical-align", "visibility", "voice-balance", "voice-duration", "voice-family", //$NON-NLS-7$ //$NON-NLS-6$ //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"voice-pitch", "voice-pitch-range", "voice-rate", "voice-stress", "voice-volume", "volume", "white-space", "white-space-collapse", //$NON-NLS-7$ //$NON-NLS-6$ //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"widows", "width", "word-break", "word-spacing", "word-wrap", "z-index" //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+	];
+	
+	var JAVA_KEYWORDS = [
+		"abstract", //$NON-NLS-0$
+		"boolean", "break", "byte", //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"case", "catch", "char", "class", "continue", //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"default", "do", "double", //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"else", "extends", //$NON-NLS-1$ //$NON-NLS-0$
+		"false", "final", "finally", "float", "for", //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"if", "implements", "import", "instanceof", "int", "interface", //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"long", //$NON-NLS-0$
+		"native", "new", "null", //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"package", "private", "protected", "public", //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"return", //$NON-NLS-0$
+		"short", "static", "super", "switch", "synchronized", //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"this", "throw", "throws", "transient", "true", "try", //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"void", "volatile", //$NON-NLS-1$ //$NON-NLS-0$
+		"while" //$NON-NLS-0$
+	];
+
+	return {
+		JSKeywords: JS_KEYWORDS,
+		CSSKeywords: CSS_KEYWORDS,
+		JAVAKeywords: JAVA_KEYWORDS
+	};
+});
+
+/*******************************************************************************
+ * @license
+ * Copyright (c) 2011, 2012 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials are made 
+ * available under the terms of the Eclipse Public License v1.0 
+ * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
+ * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+/*global define */
+
+define("orion/editor/cssContentAssist", [ //$NON-NLS-0$
+	'orion/editor/templates', //$NON-NLS-0$
+	'orion/editor/keywords' //$NON-NLS-0$
+], function(mTemplates, mKeywords) {
+
+	var overflowValues = {
+		type: "link", //$NON-NLS-0$
+		values: ["visible", "hidden", "scroll", "auto", "no-display", "no-content"] //$NON-NLS-7$ //$NON-NLS-6$ //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+	};
+	var fontStyleValues = {
+		type: "link", //$NON-NLS-0$
+		values: ["italic", "normal", "oblique", "inherit"] //$NON-NLS-7$ //$NON-NLS-6$ //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+	};
+	var fontWeightValues = {
+		type: "link", //$NON-NLS-0$
+		values: [
+			"bold", "normal", "bolder", "lighter", //$NON-NLS-7$ //$NON-NLS-6$ //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+			"100", "200", "300", "400", "500", "600", //$NON-NLS-7$ //$NON-NLS-6$ //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+			"700", "800", "900", "inherit" //$NON-NLS-7$ //$NON-NLS-6$ //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		]
+	};
+	var displayValues = {
+		type: "link", //$NON-NLS-0$
+		values: [
+			"none", "block", "box", "flex", "inline", "inline-block", "inline-flex", "inline-table", //$NON-NLS-7$ //$NON-NLS-6$ //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+			"list-item", "table", "table-caption", "table-cell", "table-column", "table-column-group", //$NON-NLS-7$ //$NON-NLS-6$ //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+			"table-footer-group", "table-header-group", "table-row", "table-row-group", "inherit" //$NON-NLS-7$ //$NON-NLS-6$ //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		]
+	};
+	var visibilityValues = {
+		type: "link", //$NON-NLS-0$
+		values: ["hidden", "visible", "collapse", "inherit"] //$NON-NLS-7$ //$NON-NLS-6$ //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+	};
+	var positionValues = {
+		type: "link", //$NON-NLS-0$
+		values: ["absolute", "fixed", "relative", "static", "inherit"] //$NON-NLS-7$ //$NON-NLS-6$ //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+	};
+	var whitespaceValues = {
+		type: "link", //$NON-NLS-0$
+		values: ["pre", "pre-line", "pre-wrap", "nowrap", "normal", "inherit"] //$NON-NLS-7$ //$NON-NLS-6$ //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+	};
+	var wordwrapValues = {
+		type: "link", //$NON-NLS-0$
+		values: ["normal", "break-word"] //$NON-NLS-1$ //$NON-NLS-0$
+	};
+	var floatValues = {
+		type: "link", //$NON-NLS-0$
+		values: ["left", "right", "none", "inherit"] //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+	};
+	var borderStyles = {
+		type: "link", //$NON-NLS-0$
+		values: ["solid", "dashed", "dotted", "double", "groove", "ridge", "inset", "outset"] //$NON-NLS-7$ //$NON-NLS-6$ //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+	};
+	var widths = {
+		type: "link", //$NON-NLS-0$
+		values: []
+	};
+	for (var i=0; i<10; i++) {
+		widths.values.push(i.toString());
+	}
+	var colorValues = {
+		type: "link", //$NON-NLS-0$
+		values: [
+			"black", //$NON-NLS-0$
+			"white", //$NON-NLS-0$
+			"red", //$NON-NLS-0$
+			"green", //$NON-NLS-0$
+			"blue", //$NON-NLS-0$
+			"magenta", //$NON-NLS-0$
+			"yellow", //$NON-NLS-0$
+			"cyan", //$NON-NLS-0$
+			"grey", //$NON-NLS-0$
+			"darkred", //$NON-NLS-0$
+			"darkgreen", //$NON-NLS-0$
+			"darkblue", //$NON-NLS-0$
+			"darkmagenta", //$NON-NLS-0$
+			"darkcyan", //$NON-NLS-0$
+			"darkyellow", //$NON-NLS-0$
+			"darkgray", //$NON-NLS-0$
+			"lightgray" //$NON-NLS-0$
+		]
+	};
+	var cursorValues = {
+		type: "link", //$NON-NLS-0$
+		values: [
+			"auto", //$NON-NLS-0$
+			"crosshair", //$NON-NLS-0$
+			"default", //$NON-NLS-0$
+			"e-resize", //$NON-NLS-0$
+			"help", //$NON-NLS-0$
+			"move", //$NON-NLS-0$
+			"n-resize", //$NON-NLS-0$
+			"ne-resize", //$NON-NLS-0$
+			"nw-resize", //$NON-NLS-0$
+			"pointer", //$NON-NLS-0$
+			"progress", //$NON-NLS-0$
+			"s-resize", //$NON-NLS-0$
+			"se-resize", //$NON-NLS-0$
+			"sw-resize", //$NON-NLS-0$
+			"text", //$NON-NLS-0$
+			"w-resize", //$NON-NLS-0$
+			"wait", //$NON-NLS-0$
+			"inherit" //$NON-NLS-0$
+		]
+	};
+	
+	function fromJSON(o) {
+		return JSON.stringify(o).replace("}", "\\}"); //$NON-NLS-1$ //$NON-NLS-0$
+	}
+	
+	var templates = [
+		{
+			prefix: "rule", //$NON-NLS-0$
+			description: "rule - class selector rule",
+			template: ".${class} {\n\t${cursor}\n}" //$NON-NLS-0$
+		},
+		{
+			prefix: "rule", //$NON-NLS-0$
+			description: "rule - id selector rule",
+			template: "#${id} {\n\t${cursor}\n}" //$NON-NLS-0$
+		},
+		{
+			prefix: "outline", //$NON-NLS-0$
+			description: "outline - outline style",
+			template: "outline: ${color:" + fromJSON(colorValues) + "} ${style:" + fromJSON(borderStyles) + "} ${width:" + fromJSON(widths) + "}px;" //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		},
+		{
+			prefix: "background-image", //$NON-NLS-0$
+			description: "background-image - image style",
+			template: "background-image: url(\"${uri}\");" //$NON-NLS-0$
+		},
+		{
+			prefix: "url", //$NON-NLS-0$
+			description: "url - url image",
+			template: "url(\"${uri}\");" //$NON-NLS-0$
+		},
+		{
+			prefix: "rgb", //$NON-NLS-0$
+			description: "rgb - rgb color",
+			template: "rgb(${red},${green},${blue});" //$NON-NLS-0$
+		},
+		{
+			prefix: "@", //$NON-NLS-0$
+			description: "import - import style sheet",
+			template: "@import \"${uri}\";" //$NON-NLS-0$
+		}
+	];
+	var valuesProperties = [
+		{prop: "display", values: displayValues}, //$NON-NLS-0$
+		{prop: "overflow", values: overflowValues}, //$NON-NLS-0$
+		{prop: "overflow-x", values: overflowValues}, //$NON-NLS-0$
+		{prop: "overflow-y", values: overflowValues}, //$NON-NLS-0$
+		{prop: "float", values: floatValues}, //$NON-NLS-0$
+		{prop: "position", values: positionValues}, //$NON-NLS-0$
+		{prop: "cursor", values: cursorValues}, //$NON-NLS-0$
+		{prop: "color", values: colorValues}, //$NON-NLS-0$
+		{prop: "border-top-color", values: colorValues}, //$NON-NLS-0$
+		{prop: "border-bottom-color", values: colorValues}, //$NON-NLS-0$
+		{prop: "border-right-color", values: colorValues}, //$NON-NLS-0$
+		{prop: "border-left-color", values: colorValues}, //$NON-NLS-0$
+		{prop: "background-color", values: colorValues}, //$NON-NLS-0$
+		{prop: "font-style", values: fontStyleValues}, //$NON-NLS-0$
+		{prop: "font-weight", values: fontWeightValues}, //$NON-NLS-0$
+		{prop: "white-space", values: whitespaceValues}, //$NON-NLS-0$
+		{prop: "word-wrap", values: wordwrapValues}, //$NON-NLS-0$
+		{prop: "visibility", values: visibilityValues} //$NON-NLS-0$
+	];
+	var prop;
+	for (i=0; i<valuesProperties.length; i++) {
+		prop = valuesProperties[i];
+		templates.push({
+			prefix: prop.prop, //$NON-NLS-0$
+			description: prop.prop + " - " + prop.prop + " style",
+			template: prop.prop + ": ${value:" + fromJSON(prop.values) + "};" //$NON-NLS-1$ //$NON-NLS-0$
+		});
+	}	
+	var pixelProperties = [
+		"width", "height", "top", "bottom", "left", "right", //$NON-NLS-7$ //$NON-NLS-6$ //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"min-width", "min-height", "max-width", "max-height", //$NON-NLS-7$ //$NON-NLS-6$ //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"margin", "padding", "padding-left", "padding-right", //$NON-NLS-7$ //$NON-NLS-6$ //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"padding-top", "padding-bottom", "margin-left", "margin-top", //$NON-NLS-7$ //$NON-NLS-6$ //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"margin-bottom", "margin-right" //$NON-NLS-7$ //$NON-NLS-6$ //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+	];
+	for (i=0; i<pixelProperties.length; i++) {
+		prop = pixelProperties[i];
+		templates.push({
+			prefix: prop, //$NON-NLS-0$
+			description: prop + " - " + prop + " pixel style",
+			template: prop  + ": ${value}px;" //$NON-NLS-0$
+		});
+	}
+	var fourWidthsProperties = ["padding", "margin"]; //$NON-NLS-1$ //$NON-NLS-0$
+	for (i=0; i<fourWidthsProperties.length; i++) {
+		prop = fourWidthsProperties[i];
+		templates.push({
+			prefix: prop, //$NON-NLS-0$
+			description: prop + " - " + prop + " top right bottom left style",
+			template: prop  + ": ${top}px ${left}px ${bottom}px ${right}px;" //$NON-NLS-0$
+		});
+		templates.push({
+			prefix: prop, //$NON-NLS-0$
+			description: prop + " - " + prop + " top right,left bottom style",
+			template: prop  + ": ${top}px ${right_left}px ${bottom}px;" //$NON-NLS-0$
+		});
+		templates.push({
+			prefix: prop, //$NON-NLS-0$
+			description: prop + " - " + prop + " top,bottom right,left style",
+			template: prop  + ": ${top_bottom}px ${right_left}px" //$NON-NLS-0$
+		});
+	}
+	var borderProperties = ["border", "border-top", "border-bottom", "border-left", "border-right"]; //$NON-NLS-7$ //$NON-NLS-6$ //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+	for (i=0; i<borderProperties.length; i++) {
+		prop = borderProperties[i];
+		templates.push({
+			prefix: prop, //$NON-NLS-0$
+			description: prop + " - " + prop + " style",
+			template: prop + ": ${width:" + fromJSON(widths) + "}px ${style:" + fromJSON(borderStyles) + "} ${color:" + fromJSON(colorValues) + "};" //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		});
+	}
+
+	/**
+	 * @name orion.editor.CssContentAssistProvider
+	 * @class Provides content assist for CSS keywords.
+	 */
+	function CssContentAssistProvider() {
+	}
+	CssContentAssistProvider.prototype = new mTemplates.TemplateContentAssist(mKeywords.CSSKeywords, templates);
+	
+	CssContentAssistProvider.prototype.getPrefix = function(buffer, offset, context) {
+		var index = offset;
+		while (index && /[A-Za-z\-\@]/.test(buffer.charAt(index - 1))) {
+			index--;
+		}
+		return index ? buffer.substring(index, offset) : "";
+	};
+
+	return {
+		CssContentAssistProvider: CssContentAssistProvider
+	};
+});
+
+/*******************************************************************************
+ * @license
+ * Copyright (c) 2011, 2012 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials are made 
+ * available under the terms of the Eclipse Public License v1.0 
+ * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
+ * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+/*global define */
+
+define("orion/editor/htmlContentAssist", ['orion/editor/templates'], function(mTemplates) { //$NON-NLS-1$ //$NON-NLS-0$
+
+	var simpleDocTemplate = new mTemplates.Template("", "Simple HTML document", //$NON-NLS-0$
+		"<!DOCTYPE html>\n" + //$NON-NLS-0$
+		"<html lang=\"en\">\n" + //$NON-NLS-0$
+		"\t<head>\n" + //$NON-NLS-0$
+		"\t\t<meta charset=utf-8>\n" + //$NON-NLS-0$
+		"\t\t<title>${title}</title>\n" + //$NON-NLS-0$
+		"\t</head>\n" + //$NON-NLS-0$
+		"\t<body>\n" + //$NON-NLS-0$
+		"\t\t<h1>${header}</h1>\n" + //$NON-NLS-0$
+		"\t\t<p>\n" + //$NON-NLS-0$
+		"\t\t\t${cursor}\n" + //$NON-NLS-0$
+		"\t\t</p>\n" + //$NON-NLS-0$
+		"\t</body>\n" + //$NON-NLS-0$
+		"</html>"); //$NON-NLS-0$
+		
+	var templates = [
+		{
+			prefix: "<img", //$NON-NLS-0$
+			description: "<img> - HTML image element",
+			template: "<img src=\"${cursor}\" alt=\"${Image}\"/>" //$NON-NLS-0$
+		},
+		{
+			prefix: "<a", //$NON-NLS-0$
+			description: "<a> - HTML anchor element",
+			template: "<a href=\"${cursor}\"></a>" //$NON-NLS-0$
+		},
+		{
+			prefix: "<ul", //$NON-NLS-0$
+			description: "<ul> - HTML unordered list",
+			template: "<ul>\n\t<li>${cursor}</li>\n</ul>" //$NON-NLS-0$
+		},
+		{
+			prefix: "<ol", //$NON-NLS-0$
+			description: "<ol> - HTML ordered list",
+			template: "<ol>\n\t<li>${cursor}</li>\n</ol>" //$NON-NLS-0$
+		},
+		{
+			prefix: "<dl", //$NON-NLS-0$
+			description: "<dl> - HTML definition list",
+			template: "<dl>\n\t<dt>${cursor}</dt>\n\t<dd></dd>\n</dl>" //$NON-NLS-0$
+		},
+		{
+			prefix: "<table", //$NON-NLS-0$
+			description: "<table> - basic HTML table",
+			template: "<table>\n\t<tr>\n\t\t<td>${cursor}</td>\n\t</tr>\n</table>" //$NON-NLS-0$
+		}
+	];
+
+	//elements that are typically placed on a single line (e.g., <b>, <h1>, etc)
+	var element, template, description, i;
+	var singleLineElements = [
+		"abbr","b","button","canvas","cite", //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"command","dd","del","dfn","dt", //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"em","embed","font","h1","h2", //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"h3","h4","h5","h6","i", //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"ins","kbd","label","li","mark", //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"meter","object","option","output","progress", //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"q","rp","rt","samp","small", //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"strong","sub","sup","td","time", //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"title","tt","u","var" //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+	];
+	for (i = 0; i < singleLineElements.length; i++) {
+		element = singleLineElements[i];
+		description = "<" + element + "></" + element + ">"; //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		template = "<" + element + ">${cursor}</" + element + ">"; //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		templates.push({prefix: "<" + element, description: description, template: template}); //$NON-NLS-0$
+	}
+
+	//elements that typically start a block spanning multiple lines (e.g., <p>, <div>, etc)
+	var multiLineElements = [
+		"address","article","aside","audio","bdo", //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"blockquote","body","caption","code","colgroup", //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"datalist","details","div","fieldset","figure", //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"footer","form","head","header","hgroup", //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"iframe","legend","map","menu","nav", //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"noframes","noscript","optgroup","p","pre", //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"ruby","script","section","select","span", //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"style","tbody","textarea","tfoot","th", //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"thead","tr","video" //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+	];
+	for (i = 0; i < multiLineElements.length; i++) {
+		element = multiLineElements[i];
+		description = "<" + element + "></" + element + ">"; //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		template = "<" + element + ">\n\t${cursor}\n</" + element + ">"; //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		templates.push({prefix: "<" + element, description: description, template: template}); //$NON-NLS-0$
+	}
+
+	//elements with no closing element (e.g., <hr>, <br>, etc)
+	var emptyElements = [
+		"area","base","br","col", //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"hr","input","link","meta", //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+		"param","keygen","source" //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
+	];
+	for (i = 0; i < emptyElements.length; i++) {
+		element = emptyElements[i];
+		template = description = "<" + element + "/>"; //$NON-NLS-1$ //$NON-NLS-0$
+		templates.push({prefix: "<" + element, description: description, template: template}); //$NON-NLS-0$
+	}
+
+	/**
+	 * @name orion.editor.HTMLContentAssistProvider
+	 * @class Provides content assist for HTML.
+	 */
+	function HTMLContentAssistProvider() {
+	}
+	HTMLContentAssistProvider.prototype = new mTemplates.TemplateContentAssist([], templates);
+
+	HTMLContentAssistProvider.prototype.getPrefix = function(buffer, offset, context) {
+		var index = offset;
+		while (index && /[A-Za-z<]/.test(buffer.charAt(index - 1))) {
+			index--;
+			if (buffer.charAt(index) === "<") { //$NON-NLS-0$
+				break;
+			}
+		}
+		return index ? buffer.substring(index, offset) : "";
+	};
+	
+	HTMLContentAssistProvider.prototype.computeProposals = function(buffer, offset, context) {
+		//template - simple html document
+		if (buffer.length === 0) {
+			return [simpleDocTemplate.getProposal("", offset, context)];
+		}
+		return mTemplates.TemplateContentAssist.prototype.computeProposals.call(this, buffer, offset, context);
+	};
+
+	return {
+		HTMLContentAssistProvider: HTMLContentAssistProvider
+	};
+});
+
+/*******************************************************************************
+ * @license
+ * Copyright (c) 2011, 2012 IBM Corporation and others.
+ * Copyright (c) 2012 VMware, Inc.
+ * All rights reserved. This program and the accompanying materials are made 
+ * available under the terms of the Eclipse Public License v1.0 
+ * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
+ * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *     Andrew Eisenberg - rename to jsTemplateContentAssist.js
+ *******************************************************************************/
+/*global define */
+
+define("orion/editor/jsTemplateContentAssist", [ //$NON-NLS-0$
+	'orion/editor/templates', //$NON-NLS-0$
+	'orion/editor/keywords' //$NON-NLS-0$
+], function(mTemplates, mKeywords) {
+
+	function findPreviousChar(buffer, offset) {
+		var c = "";
+		while (offset >= 0) {
+			c = buffer[offset];
+			if (c === '\n' || c === '\r') { //$NON-NLS-1$ //$NON-NLS-0$
+				//we hit the start of the line so we are done
+				break;
+			} else if (/\s/.test(c)) {
+				offset--;
+			} else {
+				// a non-whitespace character, we are done
+				break;
+			}
+		}
+		return c;
+	}
+	
+	var typeofValues = {
+		type: "link", //$NON-NLS-0$
+		values: [
+			"undefined", //$NON-NLS-0$
+			"object", //$NON-NLS-0$
+			"boolean", //$NON-NLS-0$
+			"number", //$NON-NLS-0$
+			"string", //$NON-NLS-0$
+			"function", //$NON-NLS-0$
+			"xml" //$NON-NLS-0$
+		]
+	};
+	
+	function fromJSON(o) {
+		return JSON.stringify(o).replace("}", "\\}"); //$NON-NLS-1$ //$NON-NLS-0$
+	}
+	
+	var uninterestingChars = ":!@#$^&*.?<>"; //$NON-NLS-0$
+
+	var templates = [
+		{
+			prefix: "if", //$NON-NLS-0$
+			description: "if - if statement",
+			template: "if (${condition}) {\n\t${cursor}\n}" //$NON-NLS-0$
+		},
+		{
+			prefix: "if", //$NON-NLS-0$
+			description: "if - if else statement",
+			template: "if (${condition}) {\n\t${cursor}\n} else {\n\t\n}" //$NON-NLS-0$
+		},
+		{
+			prefix: "for", //$NON-NLS-0$
+			description: "for - iterate over array",
+			template: "for (var ${i}=0; ${i}<${array}.length; ${i}++) {\n\t${cursor}\n}" //$NON-NLS-0$
+		},
+		{
+			prefix: "for", //$NON-NLS-0$
+			description: "for - iterate over array with local var",
+			template: "for (var ${i}=0; ${i}<${array}.length; ${i}++) {\n\tvar ${value} = ${array}[${i}];\n\t${cursor}\n}" //$NON-NLS-0$
+		},
+		{
+			prefix: "for", //$NON-NLS-0$
+			description: "for..in - iterate over properties of an object",
+			template: "for (var ${property} in ${object}) {\n\tif (${object}.hasOwnProperty(${property})) {\n\t\t${cursor}\n\t}\n}" //$NON-NLS-0$
+		},
+		{
+			prefix: "while", //$NON-NLS-0$
+			description: "while - while loop with condition",
+			template: "while (${condition}) {\n\t${cursor}\n}" //$NON-NLS-0$
+		},
+		{
+			prefix: "do", //$NON-NLS-0$
+			description: "do - do while loop with condition",
+			template: "do {\n\t${cursor}\n} while (${condition});" //$NON-NLS-0$
+		},
+		{
+			prefix: "switch", //$NON-NLS-0$
+			description: "switch - switch case statement",
+			template: "switch (${expression}) {\n\tcase ${value1}:\n\t\t${cursor}\n\t\tbreak;\n\tdefault:\n}" //$NON-NLS-0$
+		},
+		{
+			prefix: "case", //$NON-NLS-0$
+			description: "case - case statement",
+			template: "case ${value}:\n\t${cursor}\n\tbreak;" //$NON-NLS-0$
+		},
+		{
+			prefix: "try", //$NON-NLS-0$
+			description: "try - try..catch statement",
+			template: "try {\n\t${cursor}\n} catch (${err}) {\n}" //$NON-NLS-0$
+			},
+		{
+			prefix: "try", //$NON-NLS-0$
+			description: "try - try..catch statement with finally block",
+			template: "try {\n\t${cursor}\n} catch (${err}) {\n} finally {\n}" //$NON-NLS-0$
+		},
+		{
+			prefix: "var", //$NON-NLS-0$
+			description: "var - variable declaration",
+			template: "var ${name};" //$NON-NLS-0$
+		},
+		{
+			prefix: "var", //$NON-NLS-0$
+			description: "var - variable declaration with value",
+			template: "var ${name} = ${value};" //$NON-NLS-0$
+		},
+		{
+			prefix: "let", //$NON-NLS-0$
+			description: "let - local scope variable declaration",
+			template: "let ${name};" //$NON-NLS-0$
+		},
+		{
+			prefix: "let", //$NON-NLS-0$
+			description: "let - local scope variable declaration with value",
+			template: "let ${name} = ${value};" //$NON-NLS-0$
+		},
+		{
+			prefix: "return", //$NON-NLS-0$
+			description: "return - return result",
+			template: "return ${result};" //$NON-NLS-0$
+		},
+		{
+			prefix: "typeof", //$NON-NLS-0$
+			description: "typeof - typeof statement",
+			template: "typeof ${object} === \"${type:" + fromJSON(typeofValues) + "}\"" //$NON-NLS-1$ //$NON-NLS-0$
+		},
+		{
+			prefix: "instanceof", //$NON-NLS-0$
+			description: "instanceof - instanceof statement",
+			template: "${object} instanceof ${type}" //$NON-NLS-0$
+		},
+		{
+			prefix: "with", //$NON-NLS-0$
+			description: "with - with statement",
+			template: "with (${object}) {\n\t${cursor}\n}" //$NON-NLS-0$
+		},
+		{
+			prefix: "function", //$NON-NLS-0$
+			description: "function - function declaration",
+			template: "function ${name} (${parameter}) {\n\t${cursor}\n}" //$NON-NLS-0$
+		},
+		{
+			prefix: "log", //$NON-NLS-0$
+			description: "log - console log",
+			template: "console.log(${object});" //$NON-NLS-0$
+		}
+	];
+
+	/**
+	 * @name orion.editor.JSTemplateContentAssistProvider
+	 * @class Provides content assist for JavaScript keywords.
+	 */
+	function JSTemplateContentAssistProvider() {
+	}
+	JSTemplateContentAssistProvider.prototype = new mTemplates.TemplateContentAssist(mKeywords.JSKeywords, templates);
+
+	/** 
+	 * Determines if the invocation location is a valid place to use
+	 * templates.  We are not being too precise here.  As an approximation,
+	 * just look at the previous character.
+	 *
+	 * @return {Boolean} true if the current invocation location is 
+	 * a valid place for template proposals to appear.
+	 * This means that the invocation location is at the start of a new statement.
+	 */
+	JSTemplateContentAssistProvider.prototype.isValid = function(prefix, buffer, offset, context) {
+		var previousChar = findPreviousChar(buffer, offset-prefix.length-1);
+		return uninterestingChars.indexOf(previousChar) === -1;
+	};
+
+	return {
+		JSTemplateContentAssistProvider: JSTemplateContentAssistProvider
+	};
+});
+
+/******************************************************************************* 
+ * @license
+ * Copyright (c) 2011, 2012 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials are made 
+ * available under the terms of the Eclipse Public License v1.0 
+ * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
+ * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
+ * 
+ * Contributors: IBM Corporation - initial API and implementation 
+ ******************************************************************************/
+/*jslint browser:true regexp:true*/
+/*global console define*/
+define("orion/editor/AsyncStyler", ['i18n!orion/editor/nls/messages', 'orion/editor/annotations'], function(messages, mAnnotations) {
+	var SERVICE_NAME = "orion.edit.highlighter";
+	var HIGHLIGHT_ERROR_ANNOTATION = "orion.annotation.highlightError";
+	var badServiceError = SERVICE_NAME + " service must be an event emitter";
+	mAnnotations.AnnotationType.registerType(HIGHLIGHT_ERROR_ANNOTATION, {
+		title: messages.syntaxError,
+		html: "<div class='annotationHTML error'></div>",
+		rangeStyle: {styleClass: "annotationRange error"}
+	});
+
+	function isRelevant(serviceReference) {
+		return serviceReference.getProperty("objectClass").indexOf(SERVICE_NAME) !== -1 &&
+				serviceReference.getProperty("type") === "highlighter";
+	}
+
+	/**
+	 * @name orion.editor.AsyncStyler
+	 * @class Provides asynchronous styling for a TextView using registered "highlighter" services.
+	 * @description Creates an <code>AsyncStyler</code>. An AsyncStyler allows style information to be sent to a 
+	 * <code>TextView</code> asynchronously through the service segistry. Style is generated by <em>style providers</em>, which are services
+	 * having the <code>'orion.edit.highlighter'</code> service name and a <code>type</code> === <code>'highlighter'</code> service property.
+	 *
+	 * <p>A style provider monitors changes to the TextView (typically using an <code>orion.edit.model</code> service) and 
+	 * dispatches a service event of type <code>'orion.edit.highlighter.styleReady'</code> when it has style information to send.
+	 * The event carries a payload of style information for one or more lines in the TextView. The AsyncStyler then applies
+	 * the style information fron the event to the TextView using the {@link orion.editor.TextView#event:onLineStyle} API.
+	 * </p>
+	 *
+	 * <p>Applying style information may cause the TextView to be redrawn, which is potentially expensive. To minimize the 
+	 * number of redraws, a provider should provide style for many lines in a single StyleReadyEvent.
+	 * </p>
+	 *
+	 * @param {orion.editor.TextView} textView The TextView to style.
+	 * @param {orion.serviceregistry.ServiceRegistry} serviceRegistry The ServiceRegistry to monitor for highlighter services.
+	 * @param {orion.editor.AnnotationModel} [annotationModel] The Annotation Model to use for creating error and warning annotations.
+	 * @see orion.editor.StyleReadyEvent
+	 */
+	function AsyncStyler(textView, serviceRegistry, annotationModel) {
+		this.initialize(textView, serviceRegistry, annotationModel);
+		this.lineStyles = [];
+	}
+	AsyncStyler.prototype = /** @lends orion.editor.AsyncStyler.prototype*/ {
+		/** @private */
+		initialize: function(textView, serviceRegistry, annotationModel) {
+			this.textView = textView;
+			this.serviceRegistry = serviceRegistry;
+			this.annotationModel = annotationModel; 
+			this.services = [];
+
+			var self = this;
+			this.listener = {
+				onModelChanging: function(e) {
+					self.onModelChanging(e);
+				},
+				onModelChanged: function(e) {
+					self.onModelChanged(e);
+				},
+				onDestroy: function(e) {
+					self.onDestroy(e);
+				},
+				onLineStyle: function(e) {
+					self.onLineStyle(e);
+				},
+				onStyleReady: function(e) {
+					self.onStyleReady(e);
+				},
+				onServiceAdded: function(serviceEvent) {
+					self.onServiceAdded(serviceEvent.serviceReference, self.serviceRegistry.getService(serviceEvent.serviceReference));
+				},
+				onServiceRemoved: function(serviceEvent) {
+					self.onServiceRemoved(serviceEvent.serviceReference, self.serviceRegistry.getService(serviceEvent.serviceReference));
+				}
+			};
+			textView.addEventListener("ModelChanging", this.listener.onModelChanging);
+			textView.addEventListener("ModelChanged", this.listener.onModelChanged);
+			textView.addEventListener("Destroy", this.listener.onDestroy);
+			textView.addEventListener("LineStyle", this.listener.onLineStyle);
+			serviceRegistry.addEventListener("registered", this.listener.onServiceAdded);
+			serviceRegistry.addEventListener("unregistering", this.listener.onServiceRemoved);
+
+			var serviceRefs = serviceRegistry.getServiceReferences(SERVICE_NAME);
+			for (var i = 0; i < serviceRefs.length; i++) {
+				var serviceRef = serviceRefs[i];
+				if (isRelevant(serviceRef)) {
+					this.addServiceListener(serviceRegistry.getService(serviceRef));
+				}
+			}
+		},
+		/** @private */
+		onDestroy: function(e) {
+			this.destroy();
+		},
+		/** Deactivates this styler and removes any listeners it registered. */
+		destroy: function() {
+			if (this.textView) {
+				this.textView.removeEventListener("ModelChanging", this.listener.onModelChanging);
+				this.textView.removeEventListener("ModelChanged", this.listener.onModelChanged);
+				this.textView.removeEventListener("Destroy", this.listener.onDestroy);
+				this.textView.removeEventListener("LineStyle", this.listener.onLineStyle);
+				this.textView = null;
+			}
+			if (this.services) {
+				for (var i = 0; i < this.services.length; i++) {
+					this.removeServiceListener(this.services[i]);
+				}
+				this.services = null;
+			}
+			if (this.serviceRegistry) {
+				this.serviceRegistry.removeEventListener("registered", this.listener.onServiceAdded);
+				this.serviceRegistry.removeEventListener("unregistering", this.listener.onServiceRemoved);
+				this.serviceRegistry = null;
+			}
+			this.listener = null;
+			this.lineStyles = null;
+		},
+		/** @private */
+		onModelChanging: function(e) {
+			this.startLine = this.textView.getModel().getLineAtOffset(e.start);
+		},
+		/** @private */
+		onModelChanged: function(e) {
+			var startLine = this.startLine;
+			if (e.addedLineCount || e.removedLineCount) {
+				Array.prototype.splice.apply(this.lineStyles, [startLine, e.removedLineCount].concat(this._getEmptyStyle(e.addedLineCount)));
+			}
+		},
+		/** @private */
+		onStyleReady: function(e) {
+			var style = e.lineStyles || e.style;
+			var min = Number.MAX_VALUE, max = -1;
+			var model = this.textView.getModel();
+			for (var lineIndex in style) {
+				if (style.hasOwnProperty(lineIndex)) {
+					this.lineStyles[lineIndex] = style[lineIndex];
+					min = Math.min(min, lineIndex);
+					max = Math.max(max, lineIndex);
+				}
+			}
+//			console.debug("Got style for lines " + (min+1) + " to " + (max+1));
+			min = Math.max(min, 0);
+			max = Math.min(max, model.getLineCount());
+			
+			var annotationModel = this.annotationModel;
+			if (annotationModel) {
+				var annos = annotationModel.getAnnotations(model.getLineStart(min), model.getLineEnd(max));
+				var toRemove = [];
+				while (annos.hasNext()) {
+					var anno = annos.next();
+					if (anno.type === HIGHLIGHT_ERROR_ANNOTATION) {
+						toRemove.push(anno);
+					}
+				}
+				var toAdd = [];
+				for (var i = min; i <= max; i++) {
+					lineIndex = i;
+					var lineStyle = this.lineStyles[lineIndex], errors = lineStyle && lineStyle.errors;
+					var lineStart = model.getLineStart(lineIndex);
+					if (errors) {
+						for (var j=0; j < errors.length; j++) {
+							var err = errors[j];
+							toAdd.push(mAnnotations.AnnotationType.createAnnotation(HIGHLIGHT_ERROR_ANNOTATION, err.start + lineStart, err.end + lineStart));
+						}
+					}
+				}
+				annotationModel.replaceAnnotations(toRemove, toAdd);
+			}
+			this.textView.redrawLines(min, max + 1);
+		},
+		/** @private */
+		onLineStyle: function(e) {
+			function _toDocumentOffset(ranges, lineStart) {
+				var len = ranges.length, result = [];
+				for (var i=0; i < len; i++) {
+					var r = ranges[i];
+					result.push({
+						start: r.start + lineStart,
+						end: r.end + lineStart,
+						style: r.style
+					});
+				}
+				return result;
+			}
+			var style = this.lineStyles[e.lineIndex];
+			if (style) {
+				// The 'ranges', 'errors' are of type {@link orion.editor.LineStyleEvent#ranges}, except the 
+				// start and end indices are line-relative offsets, not document-relative.
+				if (style.ranges) { e.ranges = _toDocumentOffset(style.ranges, e.lineStart); }
+				else if (style.style) { e.style = style.style; }
+			}
+		},
+		/** @private */
+		_getEmptyStyle: function(n) {
+			var result = [];
+			for (var i=0; i < n; i++) {
+				result.push(null);
+			}
+			return result;
+		},
+		/**
+		 * Sets the content type ID for which style information will be provided. The ID will be passed to all style provider 
+		 * services monitored by this AsyncStyler by calling the provider's own <code>setContentType(contentTypeId)</code> method.
+		 *
+		 * <p>In this way, a single provider service can be registered for several content types, and select different logic for 
+		 * each type.</p>
+		 * @param {String} contentTypeId The Content Type ID describing the kind of file currently being edited in the TextView.
+		 * @see orion.core.ContentType
+		 */
+		setContentType: function(contentTypeId) {
+			this.contentType = contentTypeId;
+			if (this.services) {
+				for (var i = 0; i < this.services.length; i++) {
+					var service = this.services[i];
+					if (service.setContentType) {
+						var progress = this.serviceRegistry.getService("orion.page.progress");
+						if(progress){
+							progress.progress(service.setContentType(this.contentType), "Styling content type: " + this.contentType.id ? this.contentType.id: this.contentType);
+						} else {
+							service.setContentType(this.contentType);
+						}
+					}
+				}
+			}
+		},
+		/** @private */
+		onServiceAdded: function(serviceRef, service) {
+			if (isRelevant(serviceRef)) {
+				this.addServiceListener(service);
+			}
+		},
+		/** @private */
+		onServiceRemoved: function(serviceRef, service) {
+			if (this.services.indexOf(service) !== -1) {
+				this.removeServiceListener(service);
+			}
+		},
+		/** @private */
+		addServiceListener: function(service) {
+			if (typeof service.addEventListener === "function") {
+				service.addEventListener("orion.edit.highlighter.styleReady", this.listener.onStyleReady);
+				this.services.push(service);
+				if (service.setContentType && this.contentType) {
+					var progress = this.serviceRegistry.getService("orion.page.progress");
+					if(progress){
+						progress.progress(service.setContentType(this.contentType), "Styling content type: " + this.contentType.id ? this.contentType.id: this.contentType);
+					} else {
+						service.setContentType(this.contentType);
+					}
+				}
+			} else {
+				if (typeof console !== "undefined") {
+					console.log(new Error(badServiceError));
+				}
+			}
+		},
+		/** @private */
+		removeServiceListener: function(service) {
+			if (typeof service.removeEventListener === "function") {
+				service.removeEventListener("orion.edit.highlighter.styleReady", this.listener.onStyleReady);
+				var serviceIndex = this.services.indexOf(service);
+				if (serviceIndex !== -1) {
+					this.services.splice(serviceIndex, 1);
+				}
+			} else {
+				if (typeof console !== "undefined") {
+					console.log(new Error(badServiceError));
+				}
+			}
+		}
+	};
+
+	/**
+	 * @name orion.editor.StyleReadyEvent
+	 * @class Represents the styling for a range of lines, as provided by a service.
+	 * @description Represents the styling for a range of lines, as provided by a service.
+	 * @property {Object} lineStyles A map of style information. Each key of the map is a line index, and the value 
+	 * is a {@link orion.editor.StyleReadyEvent#LineStyle} giving the style information for the line.
+	 */
+	/**
+	 * @name orion.editor.StyleReadyEvent#LineStyle
+	 * @class Represents style information for a line.
+	 * @description Represents style information for a line.
+	 * <p>Note that the offsets given in the {@link #ranges} and {@link #errors} properties are relative to the start of the
+	 * line that this LineStyle is associated with, not the start of the document.</p>
+	 * @property {orion.editor.StyleRange[]} ranges Optional; Gives the styles for this line.
+	 * @property {orion.editor.StyleRange[]} errors Optional; Gives the error styles for this line. Error styles will be 
+	 * presented as annotations in the UI.
+	 */
+
+	return AsyncStyler;
+});
+
+/*******************************************************************************
+ * @license
+ * Copyright (c) 2011, 2012 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials are made 
+ * available under the terms of the Eclipse Public License v1.0 
+ * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
+ * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+/*global define */
+/*jslint browser:true forin:true*/
+define("orion/editor/mirror", ["i18n!orion/editor/nls/messages", "orion/editor/eventTarget", "orion/editor/annotations"], function(messages, mEventTarget, mAnnotations) {
+	// TODO this affects indentation, which we don't support. Should be a parameter.
+	var tabSize = 4;
+	
+	/**
+	 * @private
+	 * @name orion.mirror.Stream
+	 * @class Encapsulates a line of code and the current position in the line.
+	 * @description An implementation of CodeMirror's "StringStream" API.
+	 * @see <a href="http://codemirror.net/doc/manual.html#StringStream">http://codemirror.net/doc/manual.html#StringStream</a>
+	 */
+	function Stream(/**String*/ str) {
+		// Don't rename these or CodeMirror's "perl" mode will break.
+		this.string = str;
+		this.pos = 0;
+		this.tokenStart = 0;
+	}
+	Stream.prototype = /** @lends orion.mirror.Stream.prototype */ {
+		/** @returns {Boolean} */
+		eol: function() { return this.pos >= this.string.length; },
+		/** @returns {Boolean} */
+		sol: function() { return this.pos === 0; },
+		/** @returns {Char} */
+		peek: function() { return this.string[this.pos]; },
+		next: function() { return this.string[this.pos++]; },
+		/** @returns {Char|undefined} */
+		eat: function(/**String|RegExp|Function*/ match) {
+			var c = this.string[this.pos];
+			var isMatch = (typeof c === "string") && (c === match || (match.test && match.test(c)) || (typeof match === "function" && match(c)));
+			return isMatch ? this.string[this.pos++] : undefined;
+		},
+		/** @returns {Boolean} */
+		eatWhile: function(/**String|RegExp|Function*/ match) {
+			var ate = false;
+			while (this.eat(match) !== undefined) {
+				ate = true;
+			}
+			return ate;
+		},
+		/** @returns {Boolean} */
+		eatSpace: function() { return this.eatWhile(/\s/); },
+		skipToEnd: function() { this.pos = this.string.length; },
+		skipTo: function(/**Char*/ ch) {
+			var idx = this.string.indexOf(ch, this.pos);
+			if (idx !== -1) {
+				this.pos = idx;
+				return true;
+			}
+			return false;
+		},
+		match: function(/**String|RegExp*/ pattern, /**Boolean*/ consume, /**Boolean*/ caseFold) {
+			consume = (consume === true || typeof consume === "undefined");
+			if (typeof pattern === "string") {
+				var str = caseFold ? this.string.toLowerCase() : this.string;
+				pattern = caseFold ? pattern.toLowerCase() : pattern;
+				var index = str.indexOf(pattern, this.pos);
+				if (index !== -1 && consume) {
+					this.pos = index + pattern.length;
+				}
+				return index !== -1;
+			} else {
+				var match = this.string.substring(this.pos).match(pattern);
+				if (match && consume && typeof match[0] === "string") {
+					this.pos += match.index + match[0].length;
+				}
+				return match;
+			}
+		},
+		backUp: function(/**Number*/ n) { this.pos -= n; },
+		/** @returns {Number} */
+		column: function() {
+			var col = 0, i = 0;
+			while (i < this.tokenStart) {
+				col += (this.string[i++] === "\t") ? tabSize : 1;
+			}
+			return col;
+		},
+		/** @returns {Number} */
+		indentation: function() {
+			var index = this.string.search(/\S/);
+			var col = 0, i = 0;
+			while(i < index) {
+				col += (this.string[i++] === "\t") ? tabSize : 1;
+			}
+			return col;
+		},
+		/** @returns {String} */
+		current: function() { return this.string.substring(this.tokenStart, this.pos); },
+		advance: function() { this.tokenStart = this.pos; }
+	};
+
+	/**
+	 * @name orion.mirror.Mirror
+	 * @class A shim for CodeMirror's <code>CodeMirror</code> API.
+	 * @description A Mirror is a partial implementation of the API provided by the <a href="http://codemirror.net/doc/manual.html#api">
+	 * <code>CodeMirror</code> object</a>. Mirror provides functionality related to mode and MIME management.
+	 * 
+	 * <p>If clients intend to reuse modes provided by CodeMirror without modification, they must expose a Mirror as 
+	 * a property named <code>"CodeMirror"</code> of the global object so that modes may access it to register themselves,
+	 * and to load other modes. For example:
+	 * </p>
+	 * <p><code>
+	 * &lt;script&gt;<br>
+	 * window.CodeMirror = new Mirror();<br>
+	 * // Now you can load the CodeMirror mode scripts.<br>
+	 * &lt/script&gt;
+	 * </code></p>
+	 * @see <a href="http://codemirror.net/manual.html">http://codemirror.net/manual.html</a>
+	 */
+	function Mirror(options) {
+		this._modes = {};
+		// This internal variable must be named "mimeModes" otherwise CodeMirror's "less" mode will fail.
+		this.mimeModes = {};
+		this.options = {};
+	
+		// Expose Stream as a property named "StringStream". This is required to support CodeMirror's Perl mode,
+		// which monkey-patches CodeMirror.StringStream.prototype and will fail if that object doesn't exist.
+		this.StringStream = Stream;
+	}
+	function keys(obj) {
+		var k = [];
+		for (var p in obj) {
+			if (Object.prototype.hasOwnProperty.call(obj, p)) {
+				k.push(p);
+			}
+		}
+		return k;
+	}
+	Mirror.prototype = /** @lends orion.mirror.Mirror.prototype */ {
+		options: {},
+		/** @see <a href="http://codemirror.net/doc/manual.html#getOption">http://codemirror.net/doc/manual.html#getOption</a> */
+		setOption: function(/**String*/ option, /**Object*/ value) { this.options[option] = value; },
+		/**
+		 * @see <a href="http://codemirror.net/doc/manual.html#getOption">http://codemirror.net/doc/manual.html#getOption</a>
+		 * @returns {Object}
+		 */
+		getOption: function(option) { return this.options[option]; },
+		/** 
+		 * @see <a href="http://codemirror.net/doc/manual.html#modeapi">http://codemirror.net/doc/manual.html#modeapi</a>
+		 * @returns {Object} A copy of <code>state</code>.
+		 */
+		copyState: function(/**Object*/ mode, /**Object*/ state) {
+			if (typeof mode.copyState === "function") { return mode.copyState(state); }
+			var newState = {};
+			for (var prop in state) {
+				var value = state[prop];
+				newState[prop] = (value instanceof Array) ? value.slice() : value;
+			}
+			return newState;
+		},
+		/** @see <a href="http://codemirror.net/doc/manual.html#modeapi">http://codemirror.net/doc/manual.html#modeapi</a> */
+		defineMode: function(/**String*/ name, /**Function(options, config)*/ modeFactory) {
+			this._modes[name] = modeFactory;
+		},
+		/**
+		 * @param {String} mime
+		 * @param {String|Object} modeSpec
+		 * @see <a href="http://codemirror.net/manual.html#option_mode">http://codemirror.net/manual.html#option_mode</a>
+		 */
+		defineMIME: function(mime, modeSpec) {
+			this.mimeModes[mime] = modeSpec;
+		},
+		/**
+		 * @param {String|Object} modeSpec 
+		 * @see <a href="http://codemirror.net/manual.html#option_mode">http://codemirror.net/manual.html#option_mode</a>
+		 * @returns {Object}
+		 */
+		getMode: function(options, modeSpec) {
+			var config = {}, modeFactory;
+			if (typeof modeSpec === "string" && this.mimeModes[modeSpec]) {
+				modeSpec = this.mimeModes[modeSpec];
+			}
+			if (typeof modeSpec === "object") {
+				config = modeSpec;
+				modeFactory = this._modes[modeSpec.name];
+			}
+			modeFactory = modeFactory || this._modes[modeSpec];
+			if (typeof modeFactory !== "function") {
+				throw "Mode not found " + modeSpec;
+			}
+			return modeFactory(options, config);
+		},
+		/**
+		 * @see <a href="http://codemirror.net/doc/manual.html#option_mode">http://codemirror.net/doc/manual.html#option_mode</a>
+		 * @returns {String[]} The mode names.
+		 */
+		listModes: function() {
+			return keys(this._modes);
+		},
+		/**
+		 * @see <a href="http://codemirror.net/doc/manual.html#option_mode">http://codemirror.net/doc/manual.html#option_mode</a>
+		 * @returns {String[]} The MIMEs.
+		 */
+		listMIMEs: function() {
+			return keys(this.mimeModes);
+		},
+		_getModeName: function(mime) {
+			var mname = this.mimeModes[mime];
+			if (typeof mname === "object") { mname = mname.name; }
+			return mname;
+		}
+	};
+
+	/**
+	 * @name orion.mirror.ModeApplier#HighlightEvent
+	 * @event
+	 * @description Dispatched when the ModeApplier has updated the highlight info for a region of the file.
+	 * @param {Number} start The starting line index of the highlighted region.
+	 * @param {Number} end The ending line index of the highlighted region.
+	 */
+	/**
+	 * @name orion.mirror.MirrorLineStyle
+	 * @class Represents the style provided by a CodeMirror mode for a line.
+	 * @description 
+	 */
+	/**
+	 * @name orion.mirror.ModeApplier
+	 * @class Driver for CodeMirror modes.
+	 * @description A <code>ModeApplier</code> listens to text changes on a {@link orion.editor.TextModel} and drives
+	 * a CodeMirror mode to calculate highlighting in response to the change. Clients can use the highlighting information
+	 * to style a {@link orion.editor.TextView}.
+	 * 
+	 * <p>After a change is made to the {@link orion.editor.TextModel}, ModeApplier immediately updates the highlighting 
+	 * information for a small portion of the file around where the change occurred. Successive portions of the file are
+	 * updated by short jobs that run periodically to avoid slowing down the rest of the application.</p>
+	 * 
+	 * <p>A {@link #event:HighlightEvent} event is dispatched every time the ModeApplier updates highlighting information for
+	 * a portion of the file. The event contains information about which lines were highlighted. The style for any highlighted
+	 * line can be obtained by calling {@link #getLineStyle}.</p>
+	 * 
+	 * @param {orion.editor.TextModel} model The text model to listen to.
+	 * @param {orion.mirror.Mirror} mirror The {@link orion.mirror.Mirror} to use for loading modes.
+	 * @param {Object} [options]
+	 * <!-- @param {Boolean} [options.whitespacesVisible] If <code>true</code>, causes ModeApplier 
+	 * to generate style regions for any whitespace characters that are not claimed as tokens by the mode. -->
+	 * @borrows orion.editor.EventTarget#addEventListener as #addEventListener
+	 * @borrows orion.editor.EventTarget#removeEventListener as #removeEventListener
+	 * @borrows orion.editor.EventTarget#dispatchEvent as #dispatchEvent
+	 */
+	function ModeApplier(model, codeMirror, options) {
+		options = options || {};
+		this.model = model;
+		this.codeMirror = codeMirror;
+		this.isWhitespaceVisible = (typeof options.whitespacesVisible === "undefined" ? false : options.whitespacesVisible);
+		
+		this.mode = null;
+		this.isModeLoaded = false;
+		this.lines = []; // Array of {style: Array, eolState: state}
+		this.dirtyLines = [];
+		this.startLine = Number.MAX_VALUE;
+		this.endLine = -1;
+		this.timer = null;
+
+		this.initialize(model);
+	}
+	
+	var TAB = "token_tab",
+	    SPACE = "token_space",
+
+	    /* Max number of lines to immediately re-highlight after an edit. Remaining lines are handled by follow-up jobs. */
+	    MAX_REHIGHLIGHT = 500,
+
+	    /* Time in ms to wait between highlight jobs. */
+	    JOB_INTERVAL = 50,
+
+	    /* Maximum duration in ms of the re-highlight job.*/
+	    JOB_DURATION = 30,
+
+	    /*
+	     * During a highlight job, when mode doesn't define a "compareStates" method and we find more than this many
+	     * consecutive unchanged lines, the job aborts. (Assumes rest of file is already correctly highlighted.)
+	     */
+	    ABORT_THRESHOLD = 3,
+
+	    /* Maximum number of lines to backtrack when searching for previous state to resume parsing from. */
+	    MAX_BACKTRACK = 40;
+	
+	ModeApplier.prototype = /** @lends orion.mirror.ModeApplier.prototype */ {
+		/** @private */
+		initialize: function(model) {
+			var self = this;
+			this.listener = {
+				onModelChanging: function(e) {
+					self._onModelChanging(e);
+				},
+				onModelChanged: function(e) { // Internal detail of TextModel?
+					self._onModelChanged(e);
+				},
+				onDestroy: function(e) {
+					self._onDestroy(e);
+				}
+			};
+			this.model.addEventListener("Changing", this.listener.onModelChanging);
+			this.model.addEventListener("Changed", this.listener.onModelChanged);
+			this.model.addEventListener("Destroy", this.listener.onDestroy);
+		},
+		/** Deactivates this ModeApplier and removes its listeners. */
+		destroy: function() {
+			if (this.model) {
+				this.model.removeEventListener("Changing", this.listener.onModelChanging);
+				this.model.removeEventListener("Changed", this.listener.onModelChanged);
+				this.model.removeEventListener("Destroy", this.listener.onDestroy);
+			}
+			this.model = null;
+			this.codeMirror = null;
+			this.mode = null;
+			this.lines = null;
+			this.dirtyLines = null;
+			clearTimeout(this.timer);
+			this.timer = null;
+		},
+		_onModelChanging: function(e) {
+			this.startLine = this.model.getLineAtOffset(e.start);
+		},
+		_onModelChanged: function(e) {
+			this._dbgEvent(e);
+			var startLine = this.startLine;
+			if (e.removedLineCount || e.addedLineCount) {
+				// Patch up the line styles array; new lines get empty styles
+				Array.prototype.splice.apply(this.lines, [startLine + 1, e.removedLineCount].concat(this._newLines(e.addedLineCount)));
+			}
+			if (!this.mode) {
+				return;
+			}
+			// We need to continue at least until editEndLine, and possibly beyond up to MAX_REHIGHLIGHT
+			var editEndLine = Math.max(e.addedLineCount, e.removedLineCount);
+			var endLine = startLine + Math.min(editEndLine, MAX_REHIGHLIGHT);
+			this.highlight(startLine, endLine);
+			
+			// Launch a job to fix up the rest of the buffer
+			this.highlightLater(endLine + 1);
+		},
+		_onDestroy: function(e) {
+			this.destroy();
+		},
+		/** @private */
+		setViewportIndex: function(viewportIndex) {
+			// TODO this is for the viewport-priority case
+			this.viewportIndex = viewportIndex;
+		},
+		_dbgEvent: function(e) {
+//			var str = keys(e).map(function(p) {
+//				return p + ": " + e[p];
+//			});
+//			console.debug( str.join(", ") );
+		},
+		_dbgStyle: function() {
+//			var r = [];
+//			for (var i=0; i < this.lines.length; i++) {
+//				var style = this.lines[i].style || [];
+//				var l = "" + i + ": " ;
+//				for (var j=0; j < style.length; j++) {
+//					var region = style[j];
+//					l += region[0] + "," + region[1] + "\"" + region[2] + "\" ";
+//				}
+//				r.push(l);
+//			}
+//			console.debug(r.join("\n"));
+		},
+		_newLines: function(n, startIndex) {
+			if (typeof startIndex === "undefined") { startIndex = 0; }
+			var newLines = [];
+			for (var i=0; i < n; i++) {
+				newLines.push({
+					style: null,
+					eolState: null
+				});
+			}
+			return newLines;
+		},
+		/**
+		 * Sets the CodeMirror mode to be used for highlighting. The mode must be registered with the {@link orion.mirror.Mirror}
+		 * that this <code>ModeApplier</code> was created with. (The methods {@link orion.mirror.Mirror#defineMode} and
+		 * {@link orion.mirror.Mirror#defineMIME} can be used to register a mode with a Mirror.)
+		 * @param {String} modeSpec Mode name or MIME type.
+		 * @param {Boolean} [highlightImmediately=false]
+		 */
+		setMode: function(modeSpec, highlightImmediately) {
+			if (!modeSpec) { return; }
+			this.mode = this.codeMirror.getMode(this.codeMirror.options, modeSpec);
+			this.lines = this._newLines(this.model.getLineCount());
+			if (highlightImmediately) {
+				this.highlight();
+			}
+		},
+		/**
+		 * Highlights the given range of lines.
+		 * @param {Number} [startLine]
+		 * @param {Number} [endLine]
+		 * @param {Boolean} [partial=false] If <code>true</code>, this function is assumed to be running as part of a larger
+		 * operation, and will not dispatch a {@link #event:HighlightEvent}.
+		 */
+		highlight: function(startLine, endLine, partial) {
+			if (!this.mode) {
+				return;
+			}
+			var lineCount = this.model.getLineCount();
+			startLine = typeof startLine === "undefined" ? 0 : startLine;
+			endLine = typeof endLine === "undefined" ? lineCount - 1 : Math.min(endLine, lineCount - 1);
+			var mode = this.mode;
+			var state = this.getState(startLine);
+			for (var i = startLine; i <= endLine; i++) {
+				var line = this.lines[i];
+				this.highlightLine(i, line, state);
+				line.eolState = this.codeMirror.copyState(mode, state);
+			}
+//			console.debug("Highlighted " + startLine + " to " + endLine);
+			this._expandRange(startLine, endLine);
+			if (!partial) {
+				this.onHighlightDone();
+			}
+		},
+		/**
+		 * Schedules a job that will begin highlighting from <code>startLine</code>. The job runs for a short amount of time,
+		 * after which it dispatches a {@link #event:HighlightEvent} indicating its progress, and yields. Follow-up jobs are
+		 * scheduled automatically if there's more highlighting to be done.
+		 */
+		highlightLater: function(/**Number*/ startLine) {
+			this.dirtyLines.push(startLine);
+			var self = this;
+			this.timer = setTimeout(function() {
+				self._highlightJob();
+			}, JOB_INTERVAL);
+		},
+		/**
+		 * Highlights starting from some dirty line index. Potentially continues up to model.getLineCount(). If this function runs
+		 * for longer than JOB_DURATION, it schedules a follow-up job to continue the work, and returns. A HighlightEvent is 
+		 * dispatched just before this function finishes.
+		 */
+		_highlightJob: function() {
+			var stopTime = +new Date() + JOB_DURATION, compareStates = this.mode.compareStates, lineCount = this.model.getLineCount();
+			while (this.dirtyLines.length) {
+				// TODO support viewport priority
+				var viewportIndex = this.viewportIndex, viewportLine = this.lines[viewportIndex], lineIndex;
+				if (viewportLine && !viewportLine.eolState) {
+					lineIndex = viewportIndex;
+				} else {
+					lineIndex = this.dirtyLines.pop();
+				}
+				if (lineIndex >= lineCount) {
+					break;
+				}
+				this._expandRange(lineIndex, lineIndex);
+				var resumeIndex = this._getResumeLineIndex(lineIndex), startIndex = resumeIndex + 1;
+				var state = (resumeIndex >= 0) && this.lines[resumeIndex].eolState;
+				state = state ? this.codeMirror.copyState(this.mode, state) : this.mode.startState();
+				
+				var numUnchanged = 0;
+				for (var i=startIndex; i < lineCount; i++) {
+					var l = this.lines[i];
+					var oldState = l.eolState;
+					var isChanged = this.highlightLine(i, l, state);
+					l.eolState = this.codeMirror.copyState(this.mode, state);
+					if (isChanged) {
+						this._expandRange(startIndex, i+1);
+					}
+					var isCompareStop = compareStates && oldState && compareStates(oldState, l.eolState);
+					var isHeuristicStop = !compareStates && !isChanged && (numUnchanged++ > ABORT_THRESHOLD);
+					if (isCompareStop || isHeuristicStop) {
+						break; // Abort completely. Don't highlight any more lines
+					} else if (!oldState || isChanged) {
+						numUnchanged = 0;
+					}
+					var workRemains = i < lineCount || this.dirtyLines.length;
+					var timeElapsed = +new Date() > stopTime && workRemains;
+					if (timeElapsed) {
+						// Stop, continue later
+						//this._expandRange(startIndex, i + 1);
+						this.highlightLater(i + 1);
+						this.onHighlightDone();
+						return; // TODO clean up terminating condition
+					}
+				}
+			}
+			this.onHighlightDone();
+		},
+		/** Called when some highlighting has been performed. Dispatches a {@link #event:HighlightEvent}. */
+		onHighlightDone: function() {
+			if (this.startLine !== Number.MAX_VALUE && this.endLine !== -1) {
+				this.dispatchEvent({
+					type: "Highlight",
+					start: this.startLine,
+					end: this.endLine
+				});
+			}
+			this.startLine = Number.MAX_VALUE;
+			this.endLine = -1;
+		},
+		_getResumeLineIndex: function(lineIndex) {
+			var lines = this.lines;
+			for (var i = lineIndex - 1; i >= 0; i--) {
+				if (lines[i].eolState || lineIndex - i > MAX_BACKTRACK) {
+					return i;
+				}
+			}
+			return -1;
+		},
+		/**
+		 * Returns the state we can use for parsing from the start of the <i><code>lineIndex</code></i>th line.
+		 * @returns {Object} The state. This object is safe to mutate.
+		 */
+		getState: function(/**Number*/ lineIndex) {
+			var mode = this.mode, lines = this.lines;
+			var i, line;
+			for (i = lineIndex-1; i >= 0; i--) {
+				line = lines[i];
+				if (line.eolState || lineIndex - i > MAX_BACKTRACK) {
+					// CodeMirror optimizes by using least-indented line; we just use this line
+					break;
+				}
+			}
+			var state = (i >= 0) && lines[i].eolState;
+			if (state) {
+				state = this.codeMirror.copyState(mode, state);
+				// Highlight from i up to lineIndex-1
+				i = Math.max(0, i);
+				for (var j = i; j < lineIndex-1; j++) {
+					line = lines[j];
+					this.highlightLine(j, line, state);
+					line.eolState = this.codeMirror.copyState(mode, state);
+				}
+				return state; // is a copy of lines[lineIndex - 1].eolState
+			} else {
+				return mode.startState();
+			}
+		},
+		/**
+		 * Highlights a single line.
+		 * @param {Number} lineIndex
+		 * @param {Object} line
+		 * @param {Object} state The state to use for parsing from the start of the line.
+		 */
+		highlightLine: function(lineIndex, line, state) {
+			if (!this.mode) {
+				return;
+			}
+			var model = this.model;
+			if (model.getLineStart(lineIndex) === model.getLineEnd(lineIndex) && this.mode.blankLine) {
+				this.mode.blankLine(state);
+			}
+			var style = line.style || [];
+			var text = model.getLine(lineIndex);
+			var stream = new Stream(text);
+			var isChanged = !line.style;
+			var newStyle = [], ws;
+			for (var i=0; !stream.eol(); i++) {
+				var tok = this.mode.token(stream, state) || null;
+				var tokStr = stream.current();
+				ws = this._whitespaceStyle(tok, tokStr, stream.tokenStart);
+				if (ws) {
+					// TODO Replace this (null) token with whitespace tokens. Do something smart
+					// to figure out isChanged, I guess
+				}
+				var newS = [stream.tokenStart, stream.pos, tok]; // shape is [start, end, token]
+				var oldS = style[i];
+				newStyle.push(newS);
+				isChanged = isChanged || !oldS || oldS[0] !== newS[0] || oldS[1] !== newS[1] || oldS[2] !== newS[2];
+				stream.advance();
+			}
+			isChanged = isChanged || (newStyle.length !== style.length);
+			if (isChanged) { line.style = newStyle.length ? newStyle : null; }
+			return isChanged;
+		},
+		/**
+		 * If given an un-token'd chunk of whitespace, returns whitespace style tokens for it.
+		 * @returns {Array} The whitespace styles for the token, or null.
+		 */
+		_whitespaceStyle: function(token, str, pos) {
+			if (!token && this.isWhitespaceVisible && /\s+/.test(str)) {
+				var whitespaceStyles = [], start, type;
+				for (var i=0; i < str.length; i++) {
+					var chr = str[i];
+					if (chr !== type) {
+						if (type) {
+							whitespaceStyles.push([pos + start, pos + i, (type === "\t" ? TAB : SPACE)]);
+						}
+						start = i;
+						type = chr;
+					}
+				}
+				whitespaceStyles.push([pos + start, pos + i, (type === "\t" ? TAB : SPACE)]);
+				return whitespaceStyles;
+			}
+			return null;
+		},
+		_expandRange: function(startLine, endLine) {
+			this.startLine = Math.min(this.startLine, startLine);
+			this.endLine = Math.max(this.endLine, endLine);
+		},
+		/**
+		 * Converts a <code>MirrorLineStyle</code> to a {@link orion.editor.StyleRange[]}.
+		 * @param {orion.mirror.MirrorLineStyle} style The line style to convert.
+		 * @param {Number} [lineIndex] The line index of the line having the given style. If omitted, the returned 
+		 * {@link orion.editor.StyleRange[]} objects will have offsets relative to the line, not the document.
+		 * 
+		 * @returns {orion.editor.StyleRange[][]} An array of 2 elements. The first element is an {@link orion.editor.StyleRange[]}
+		 * giving the styles for the line. 
+		 * <p>The second element is an {@link orion.editor.StyleRange[]} containing only those elements of
+		 * the first array that represent syntax errors. (By CodeMirror convention, anything assigned the <code>"cm-error"</code> tag
+		 * is assumed to be an error).</p>
+		 */
+		toStyleRangesAndErrors: function(lineStyle, lineIndex) {
+			function token2Class(token) {
+				if (!token) { return null; }
+				if (token === TAB || token === SPACE) { return token; }
+				return "cm-" + token;
+			}
+			var style = lineStyle.style;
+			if (!style) { return null; }
+			var ranges = [], errors = [];
+			var offset = (typeof lineIndex === "undefined") ? 0 : this.model.getLineStart(lineIndex);
+			for (var i=0; i < style.length; i++) {
+				var elem = style[i]; // shape is [start, end, token]
+				var className = token2Class(elem[2]);
+				if (!className) { continue; }
+				var obj = {
+					start: offset + elem[0],
+					end: offset + elem[1],
+					style: {styleClass: className} };
+				ranges.push(obj);
+				if (className === "cm-error") {
+					errors.push(obj);
+				}
+			}
+			return [ranges, errors];
+		},
+		/** @returns {orion.mirror.MirrorLineStyle} */
+		getLineStyle: function(/**Number*/ lineIndex) {
+			return this.lines[lineIndex];
+		},
+		/** @returns {orion.mirror.MirrorLineStyle[]} */
+		getLineStyles: function() {
+			return this.lines;
+		}
+	};
+	mEventTarget.EventTarget.addMixin(ModeApplier.prototype);
+
+	/**
+	 * @name orion.mirror.CodeMirrorStyler
+	 * @class A styler that uses CodeMirror modes to provide styles for a {@link orion.editor.TextView}.
+	 * @description A <code>CodeMirrorStyler</code> applies one or more CodeMirror modes to provide styles for a {@link orion.editor.TextView}.
+	 * It uses modes that are registered with the {@link orion.mirror.Mirror} object passed to the CodeMirrorStyler constructor.
+	 * 
+	 * <p>The process for using CodeMirrorStyler is as follows:</p>
+	 * <ol>
+	 * <li>Create a {@link orion.mirror.Mirror}.</li>
+	 * <li>Load the modes you want to use and register them with the <code>Mirror</code> using {@link orion.mirror.Mirror#defineMode}.
+	 * <p>Note that the <a href="https://github.com/marijnh/CodeMirror2/tree/master/mode">modes included with CodeMirror</a> expect
+	 * a global variable named "CodeMirror" to be available. If you intend to use these modes without modification, you must first 
+	 * expose your Mirror as a global variable. See the {@link orion.mirror.Mirror} documentation for details.</p>
+	 * </li>
+	 * <li>Call {@link #setMode} to tell the styler what mode to use for highlighting the view.
+	 * <p>Note that the mode may refer to other modes to process nested language text. In such cases, you should ensure that all
+	 * dependent modes have been registered with the Mirror before calling {@link #setMode}.</p>
+	 * </li>
+	 * 
+	 * @param {orion.editor.TextView} textView The TextView to provide style for.
+	 * @param {orion.mirror.Mirror} codeMirror The Mirror object to load modes from.
+	 * @param {orion.editor.AnnotationModel} [annotationModel]
+	 */
+	function CodeMirrorStyler(textView, codeMirror, annotationModel) {
+		this.init(textView, codeMirror, annotationModel);
+	}
+
+	var LINESTYLE_OVERSHOOT = 20;
+	var HIGHLIGHT_ERROR_ANNOTATION = "orion.annotation.highlightError";
+	mAnnotations.AnnotationType.registerType(HIGHLIGHT_ERROR_ANNOTATION, {
+		title: messages.syntaxError,
+		html: "<div class='annotationHTML error'></div>",
+		rangeStyle: {styleClass: "annotationRange error"}
+	});
+
+	CodeMirrorStyler.prototype = /** @lends orion.mirror.CodeMirrorStyler.prototype */ {
+		/** @private */
+		init: function(textView, codeMirror, annotationModel) {
+			this.textView = textView;
+			this.annotationModel = annotationModel;
+			this.modeApplier = new ModeApplier(textView.getModel(), codeMirror);
+			var self = this;
+			this.listener = {
+				onLineStyle: function(e) {
+					self.onLineStyle(e);
+				},
+				onDestroy: function(e) {
+					self.onDestroy(e);
+				},
+				onHighlight: function(e) {
+					self.onHighlight(e);
+				}
+			};
+			textView.addEventListener("LineStyle", this.listener.onLineStyle);
+			textView.addEventListener("Destroy", this.listener.onDestroy);
+			this.modeApplier.addEventListener("Highlight", this.listener.onHighlight);
+		},
+		/** Deactivates this styler and removes its listeners. */
+		destroy: function() {
+			if (this.modeApplier) {
+				this.modeApplier.removeEventListener("Highlight", this.listener.onHighlight);
+				this.modeApplier.destroy();
+			}
+			if (this.annotationModel) {
+				// remove annotation listeners
+			}
+			if (this.textView) {
+				this.textView.removeEventListener("LineStyle", this.listener.onLineStyle);
+				this.textView.removeEventListener("Destroy", this.listener.onDestroy);
+			}
+			this.textView = null;
+			this.annotationModel = null;
+			this.modeApplier = null;
+			this.listener = null;
+		},
+		/** 
+		 * Sets the CodeMirror mode that this styler will use for styling the view.
+		 * @param {String} modeSpec Name of the mode to use (eg. <code>"css"</code>), or a MIME type defined by the mode
+		 * (eg. <code>"text/css"</code>).
+		 */
+		setMode: function(modeSpec) {
+			this.modeApplier.setMode(modeSpec);
+		},
+		/** @private */
+		onLineStyle: function(e) {
+			var lineIndex = e.lineIndex, modeApplier = this.modeApplier, style = modeApplier.getLineStyle(lineIndex);
+			if (!(style && style.eolState)) {
+				// Start highlighting from lineIndex, do a few more lines
+				var lineCount = this.textView.getModel().getLineCount();
+				modeApplier.highlight(lineIndex, Math.min(lineIndex + LINESTYLE_OVERSHOOT, lineCount - 1), true /*don't dispatch*/);
+				style = modeApplier.getLineStyle(lineIndex);
+			}
+			var model = this.textView.getModel();
+			if (style) {
+				// Now we have a style for the line. It may not be correct in the case where lineIndex is at the end of a large
+				// buffer. But in that case, the highlight job kicked off by ModelChanged will eventually reach it and fix it up.
+				var rangesAndErrors = modeApplier.toStyleRangesAndErrors(style, lineIndex);
+				if (rangesAndErrors) {
+					e.ranges = rangesAndErrors[0];
+					var annotationModel = this.annotationModel;
+					if (annotationModel) {
+						var toRemove = [], toAdd = [];
+						var errors = rangesAndErrors[1];
+						if (errors) {
+							for (var i=0; i < errors.length; i++) {
+								var error = errors[i];
+								if (error.style.styleClass === "cm-error") {
+									toAdd.push(mAnnotations.AnnotationType.createAnnotation(HIGHLIGHT_ERROR_ANNOTATION, error.start, error.end));
+								}
+							}
+						}
+						var annos = annotationModel.getAnnotations(model.getLineStart(lineIndex), model.getLineEnd(lineIndex));
+						while (annos.hasNext()) {
+							var anno = annos.next();
+							if (anno.type === HIGHLIGHT_ERROR_ANNOTATION) {
+								toRemove.push(anno);
+							}
+						}
+						annotationModel.replaceAnnotations(toRemove, toAdd);
+					}
+				}
+			}
+		},
+		/** @private */
+		onHighlight: function(e) {
+			// If the highlighted lines are in view, redraw them
+			var start = e.start, end = e.end;
+			this.textView.redrawLines(start, end);
+		},
+		/** @private */
+		onDestroy: function(e) {
+			this.destroy();
+		}
+	};
+
+	return {
+		Mirror: Mirror,
+		ModeApplier: ModeApplier,
+		CodeMirrorStyler: CodeMirrorStyler
+	};
+});
+
+/******************************************************************************* 
+ * @license
+ * Copyright (c) 2011, 2012 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials are made 
+ * available under the terms of the Eclipse Public License v1.0 
+ * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
+ * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
+ * 
+ * Contributors: IBM Corporation - initial API and implementation 
+ ******************************************************************************/
+
+/*jslint regexp:false laxbreak:true*/
+/*global define */
+
+define("orion/editor/textMateStyler", ['orion/editor/regex' ], function(mRegex) {
+
+var RegexUtil = {
+	// Rules to detect some unsupported Oniguruma features
+	unsupported: [
+		{regex: /\(\?[ims\-]:/, func: function(match) { return "option on/off for subexp"; }},
+		{regex: /\(\?<([=!])/, func: function(match) { return (match[1] === "=") ? "lookbehind" : "negative lookbehind"; }},
+		{regex: /\(\?>/, func: function(match) { return "atomic group"; }}
+	],
+	
+	/**
+	 * @param {String} str String giving a regular expression pattern from a TextMate grammar.
+	 * @param {String} [flags] [ismg]+
+	 * @returns {RegExp}
+	 */
+	toRegExp: function(str) {
+		function fail(feature, match) {
+			throw new Error("Unsupported regex feature \"" + feature + "\": \"" + match[0] + "\" at index: "
+					+ match.index + " in " + match.input);
+		}
+		// Turns an extended regex pattern into a normal one
+		function normalize(/**String*/ str) {
+			var result = "";
+			var insideCharacterClass = false;
+			var len = str.length;
+			for (var i=0; i < len; ) {
+				var chr = str.charAt(i);
+				if (!insideCharacterClass && chr === "#") {
+					// skip to eol
+					while (i < len && chr !== "\r" && chr !== "\n") {
+						chr = str.charAt(++i);
+					}
+				} else if (!insideCharacterClass && /\s/.test(chr)) {
+					// skip whitespace
+					while (i < len && /\s/.test(chr)) { 
+						chr = str.charAt(++i);
+					}
+				} else if (chr === "\\") {
+					result += chr;
+					if (!/\s/.test(str.charAt(i+1))) {
+						result += str.charAt(i+1);
+						i += 1;
+					}
+					i += 1;
+				} else if (chr === "[") {
+					insideCharacterClass = true;
+					result += chr;
+					i += 1;
+				} else if (chr === "]") {
+					insideCharacterClass = false;
+					result += chr;
+					i += 1;
+				} else {
+					result += chr;
+					i += 1;
+				}
+			}
+			return result;
+		}
+		
+		var flags = "";
+		var i;
+		
+		// Handle global "x" flag (whitespace/comments)
+		str = RegexUtil.processGlobalFlag("x", str, function(subexp) {
+				return normalize(subexp);
+			});
+		
+		// Handle global "i" flag (case-insensitive)
+		str = RegexUtil.processGlobalFlag("i", str, function(subexp) {
+				flags += "i";
+				return subexp;
+			});
+		
+		// Check for remaining unsupported syntax
+		for (i=0; i < this.unsupported.length; i++) {
+			var match;
+			if ((match = this.unsupported[i].regex.exec(str))) {
+				fail(this.unsupported[i].func(match), match);
+			}
+		}
+		
+		return new RegExp(str, flags);
+	},
+	
+	/**
+	 * Checks if flag applies to entire pattern. If so, obtains replacement string by calling processor
+	 * on the unwrapped pattern. Handles 2 possible syntaxes: (?f)pat and (?f:pat)
+	 */
+	processGlobalFlag: function(/**String*/ flag, /**String*/ str, /**Function*/ processor) {
+		function getMatchingCloseParen(/*String*/pat, /*Number*/start) {
+			var depth = 0,
+			    len = pat.length,
+			    flagStop = -1;
+			for (var i=start; i < len && flagStop === -1; i++) {
+				switch (pat.charAt(i)) {
+					case "\\":
+						i++; // escape: skip next char
+						break;
+					case "(":
+						depth++;
+						break;
+					case ")":
+						depth--;
+						if (depth === 0) {
+							flagStop = i;
+						}
+						break;
+				}
+			}
+			return flagStop;
+		}
+		var flag1 = "(?" + flag + ")",
+		    flag2 = "(?" + flag + ":";
+		if (str.substring(0, flag1.length) === flag1) {
+			return processor(str.substring(flag1.length));
+		} else if (str.substring(0, flag2.length) === flag2) {
+			var flagStop = getMatchingCloseParen(str, 0);
+			if (flagStop < str.length-1) {
+				throw new Error("Only a " + flag2 + ") group that encloses the entire regex is supported in: " + str);
+			}
+			return processor(str.substring(flag2.length, flagStop));
+		}
+		return str;
+	},
+	
+	hasBackReference: function(/**RegExp*/ regex) {
+		return (/\\\d+/).test(regex.source);
+	},
+	
+	/** @returns {RegExp} A regex made by substituting any backreferences in <code>regex</code> for the value of the property
+	 * in <code>sub</code> with the same name as the backreferenced group number. */
+	getSubstitutedRegex: function(/**RegExp*/ regex, /**Object*/ sub, /**Boolean*/ escape) {
+		escape = (typeof escape === "undefined") ? true : false;
+		var exploded = regex.source.split(/(\\\d+)/g);
+		var array = [];
+		for (var i=0; i < exploded.length; i++) {
+			var term = exploded[i];
+			var backrefMatch = /\\(\d+)/.exec(term);
+			if (backrefMatch) {
+				var text = sub[backrefMatch[1]] || "";
+				array.push(escape ? mRegex.escape(text) : text);
+			} else {
+				array.push(term);
+			}
+		}
+		return new RegExp(array.join(""));
+	},
+	
+	/**
+	 * Builds a version of <code>regex</code> with every non-capturing term converted into a capturing group. This is a workaround
+	 * for JavaScript's lack of API to get the index at which a matched group begins in the input string.<p>
+	 * Using the "groupified" regex, we can sum the lengths of matches from <i>consuming groups</i> 1..n-1 to obtain the 
+	 * starting index of group n. (A consuming group is a capturing group that is not inside a lookahead assertion).</p>
+	 * Example: groupify(/(a+)x+(b+)/) === /(a+)(x+)(b+)/<br />
+	 * Example: groupify(/(?:x+(a+))b+/) === /(?:(x+)(a+))(b+)/
+	 * @param {RegExp} regex The regex to groupify.
+	 * @param {Object} [backRefOld2NewMap] Optional. If provided, the backreference numbers in regex will be updated using the 
+	 * properties of this object rather than the new group numbers of regex itself.
+	 * <ul><li>[0] {RegExp} The groupified version of the input regex.</li>
+	 * <li>[1] {Object} A map containing old-group to new-group info. Each property is a capturing group number of <code>regex</code>
+	 * and its value is the corresponding capturing group number of [0].</li>
+	 * <li>[2] {Object} A map indicating which capturing groups of [0] are also consuming groups. If a group number is found
+	 * as a property in this object, then it's a consuming group.</li></ul>
+	 */
+	groupify: function(regex, backRefOld2NewMap) {
+		var NON_CAPTURING = 1,
+		    CAPTURING = 2,
+		    LOOKAHEAD = 3,
+		    NEW_CAPTURING = 4;
+		var src = regex.source,
+		    len = src.length;
+		var groups = [],
+		    lookaheadDepth = 0,
+		    newGroups = [],
+		    oldGroupNumber = 1,
+		    newGroupNumber = 1;
+		var result = [],
+		    old2New = {},
+		    consuming = {};
+		for (var i=0; i < len; i++) {
+			var curGroup = groups[groups.length-1];
+			var chr = src.charAt(i);
+			switch (chr) {
+				case "(":
+					// If we're in new capturing group, close it since ( signals end-of-term
+					if (curGroup === NEW_CAPTURING) {
+						groups.pop();
+						result.push(")");
+						newGroups[newGroups.length-1].end = i;
+					}
+					var peek2 = (i + 2 < len) ? (src.charAt(i+1) + "" + src.charAt(i+2)) : null;
+					if (peek2 === "?:" || peek2 === "?=" || peek2 === "?!") {
+						// Found non-capturing group or lookahead assertion. Note that we preserve non-capturing groups
+						// as such, but any term inside them will become a new capturing group (unless it happens to
+						// also be inside a lookahead).
+						var groupType;
+						if (peek2 === "?:") {
+							groupType = NON_CAPTURING;
+						} else {
+							groupType = LOOKAHEAD;
+							lookaheadDepth++;
+						}
+						groups.push(groupType);
+						newGroups.push({ start: i, end: -1, type: groupType /*non capturing*/ });
+						result.push(chr);
+						result.push(peek2);
+						i += peek2.length;
+					} else {
+						groups.push(CAPTURING);
+						newGroups.push({ start: i, end: -1, type: CAPTURING, oldNum: oldGroupNumber, num: newGroupNumber });
+						result.push(chr);
+						if (lookaheadDepth === 0) {
+							consuming[newGroupNumber] = null;
+						}
+						old2New[oldGroupNumber] = newGroupNumber;
+						oldGroupNumber++;
+						newGroupNumber++;
+					}
+					break;
+				case ")":
+					var group = groups.pop();
+					if (group === LOOKAHEAD) { lookaheadDepth--; }
+					newGroups[newGroups.length-1].end = i;
+					result.push(chr);
+					break;
+				case "*":
+				case "+":
+				case "?":
+				case "}":
+					// Unary operator. If it's being applied to a capturing group, we need to add a new capturing group
+					// enclosing the pair
+					var op = chr;
+					var prev = src.charAt(i-1),
+					    prevIndex = i-1;
+					if (chr === "}") {
+						for (var j=i-1; src.charAt(j) !== "{" && j >= 0; j--) {}
+						prev = src.charAt(j-1);
+						prevIndex = j-1;
+						op = src.substring(j, i+1);
+					}
+					var lastGroup = newGroups[newGroups.length-1];
+					if (prev === ")" && (lastGroup.type === CAPTURING || lastGroup.type === NEW_CAPTURING)) {
+						// Shove in the new group's (, increment num/start in from [lastGroup.start .. end]
+						result.splice(lastGroup.start, 0, "(");
+						result.push(op);
+						result.push(")");
+						var newGroup = { start: lastGroup.start, end: result.length-1, type: NEW_CAPTURING, num: lastGroup.num };
+						for (var k=0; k < newGroups.length; k++) {
+							group = newGroups[k];
+							if (group.type === CAPTURING || group.type === NEW_CAPTURING) {
+								if (group.start >= lastGroup.start && group.end <= prevIndex) {
+									group.start += 1;
+									group.end += 1;
+									group.num = group.num + 1;
+									if (group.type === CAPTURING) {
+										old2New[group.oldNum] = group.num;
+									}
+								}
+							}
+						}
+						newGroups.push(newGroup);
+						newGroupNumber++;
+						break;
+					} else {
+						// Fallthrough to default
+					}
+				default:
+					if (chr !== "|" && curGroup !== CAPTURING && curGroup !== NEW_CAPTURING) {
+						// Not in a capturing group, so make a new one to hold this term.
+						// Perf improvement: don't create the new group if we're inside a lookahead, since we don't 
+						// care about them (nothing inside a lookahead actually consumes input so we don't need it)
+						if (lookaheadDepth === 0) {
+							groups.push(NEW_CAPTURING);
+							newGroups.push({ start: i, end: -1, type: NEW_CAPTURING, num: newGroupNumber });
+							result.push("(");
+							consuming[newGroupNumber] = null;
+							newGroupNumber++;
+						}
+					}
+					result.push(chr);
+					if (chr === "\\") {
+						var peek = src.charAt(i+1);
+						// Eat next so following iteration doesn't think it's a real special character
+						result.push(peek);
+						i += 1;
+					}
+					break;
+			}
+		}
+		while (groups.length) {	
+			// Close any remaining new capturing groups
+			groups.pop();
+			result.push(")");
+		}
+		var newRegex = new RegExp(result.join(""));
+		
+		// Update backreferences so they refer to the new group numbers. Use backRefOld2NewMap if provided
+		var subst = {};
+		backRefOld2NewMap = backRefOld2NewMap || old2New;
+		for (var prop in backRefOld2NewMap) {
+			if (backRefOld2NewMap.hasOwnProperty(prop)) {
+				subst[prop] = "\\" + backRefOld2NewMap[prop];
+			}
+		}
+		newRegex = this.getSubstitutedRegex(newRegex, subst, false);
+		
+		return [newRegex, old2New, consuming];
+	},
+	
+	/** @returns {Boolean} True if the captures object assigns scope to a matching group other than "0". */
+	complexCaptures: function(capturesObj) {
+		if (!capturesObj) { return false; }
+		for (var prop in capturesObj) {
+			if (capturesObj.hasOwnProperty(prop)) {
+				if (prop !== "0") {
+					return true;
+				}
+			}
+		}
+		return false;
+	}
+};
+
+	/**
+	 * @private
+	 * @param obj {Object} A JSON-ish object.
+	 * @returns {Object} Deep copy of <code>obj</code>. Does not work on properties that are functions or RegExp instances.
+	 */
+	function clone(obj) {
+		var c;
+		if (obj instanceof Array) {
+			c = new Array(obj.length);
+			for (var i=0; i < obj.length; i++) {
+				c[i] = clone(obj[i]);
+			}
+		} else {
+			c = {};
+			for (var prop in obj) {
+				if (Object.prototype.hasOwnProperty.call(obj, prop)) {
+					var value = obj[prop];
+					if (typeof value === "object" && value !== null) {
+						c[prop] = clone(value);
+					} else {
+						c[prop] = value;
+					}
+				}
+			}
+		}
+		return c;
+	}
+
+	/**
+	 * @name orion.editor.TextMateStyler
+	 * @class A styler that knows how to apply a subset of the TextMate grammar format to style a line.
+	 *
+	 * <h4>Styling from a grammar:</h4>
+	 * <p>Each scope name given in the grammar is converted to an array of CSS class names. For example 
+	 * a region of text with scope <code>keyword.control.php</code> will be assigned the CSS classes<br />
+	 * <code>keyword, keyword-control, keyword-control-php</code></p>
+	 *
+	 * <p>A CSS file can give rules matching any of these class names to provide generic or more specific styling.
+	 * For example,</p>
+	 * <p><code>.keyword { font-color: blue; }</code></p>
+	 * <p>colors all keywords blue, while</p>
+	 * <p><code>.keyword-control-php { font-weight: bold; }</code></p>
+	 * <p>bolds only PHP control keywords.</p>
+	 *
+	 * <p>This is useful when using grammars that adhere to TextMate's
+	 * <a href="http://manual.macromates.com/en/language_grammars.html#naming_conventions">scope name conventions</a>,
+	 * as a single CSS rule can provide consistent styling to similar constructs across different languages.</p>
+	 * 
+	 * <h4>Top-level grammar constructs:</h4>
+	 * <ul><li><code>patterns, repository</code> (with limitations, see "Other Features") are supported.</li>
+	 * <li><code>scopeName, firstLineMatch, foldingStartMarker, foldingStopMarker</code> are <b>not</b> supported.</li>
+	 * <li><code>fileTypes</code> is <b>not</b> supported. When using the Orion service registry, the "orion.edit.highlighter"
+	 * service serves a similar purpose.</li>
+	 * </ul>
+	 *
+	 * <h4>Regular expression constructs:</h4>
+	 * <ul>
+	 * <li><code>match</code> patterns are supported.</li>
+	 * <li><code>begin .. end</code> patterns are supported.</li>
+	 * <li>The "extended" regex forms <code>(?x)</code> and <code>(?x:...)</code> are supported, but <b>only</b> when they 
+	 * apply to the entire regex pattern.</li>
+	 * <li>Matching is done using native JavaScript <code>RegExp</code>s. As a result, many features of the Oniguruma regex
+	 * engine used by TextMate are <b>not</b> supported.
+	 * Unsupported features include:
+	 *   <ul><li>Named captures</li>
+	 *   <li>Setting flags inside subgroups (eg. <code>(?i:a)b</code>)</li>
+	 *   <li>Lookbehind and negative lookbehind</li>
+	 *   <li>Subexpression call</li>
+	 *   <li>etc.</li>
+	 *   </ul>
+	 * </li>
+	 * </ul>
+	 * 
+	 * <h4>Scope-assignment constructs:</h4>
+	 * <ul>
+	 * <li><code>captures, beginCaptures, endCaptures</code> are supported.</li>
+	 * <li><code>name</code> and <code>contentName</code> are supported.</li>
+	 * </ul>
+	 * 
+	 * <h4>Other features:</h4>
+	 * <ul>
+	 * <li><code>applyEndPatternLast</code> is supported.</li>
+	 * <li><code>include</code> is supported, but only when it references a rule in the current grammar's <code>repository</code>.
+	 * Including <code>$self</code>, <code>$base</code>, or <code>rule.from.another.grammar</code> is <b>not</b> supported.</li>
+	 * </ul>
+	 * 
+	 * @description Creates a new TextMateStyler.
+	 * @extends orion.editor.AbstractStyler
+	 * @param {orion.editor.TextView} textView The <code>TextView</code> to provide styling for.
+	 * @param {Object} grammar The TextMate grammar to use for styling the <code>TextView</code>, as a JavaScript object. You can
+	 * produce this object by running a PList-to-JavaScript conversion tool on a TextMate <code>.tmLanguage</code> file.
+	 * @param {Object[]} [externalGrammars] Additional grammar objects that will be used to resolve named rule references.
+	 */
+	function TextMateStyler(textView, grammar, externalGrammars) {
+		this.initialize(textView);
+		// Copy grammar object(s) since we will mutate them
+		this.grammar = clone(grammar);
+		this.externalGrammars = externalGrammars ? clone(externalGrammars) : [];
+		
+		this._styles = {}; /* key: {String} scopeName, value: {String[]} cssClassNames */
+		this._tree = null;
+		this._allGrammars = {}; /* key: {String} scopeName of grammar, value: {Object} grammar */
+		this.preprocess(this.grammar);
+	}
+	TextMateStyler.prototype = /** @lends orion.editor.TextMateStyler.prototype */ {
+		initialize: function(textView) {
+			this.textView = textView;
+			this.textView.stylerOptions = this;
+			var self = this;
+			
+			this._listener = {
+				onModelChanged: function(e) {
+					self.onModelChanged(e);
+				},
+				onDestroy: function(e) {
+					self.onDestroy(e);
+				},
+				onLineStyle: function(e) {
+					self.onLineStyle(e);
+				},
+				onStorage: function(e){
+					self.onStorage(e);
+				}
+			};
+			textView.addEventListener("ModelChanged", this._listener.onModelChanged);
+			textView.addEventListener("Destroy", this._listener.onDestroy);
+			textView.addEventListener("LineStyle", this._listener.onLineStyle);
+			textView.redrawLines();
+		},
+		onDestroy: function(/**eclipse.DestroyEvent*/ e) {
+			this.destroy();
+		},
+		destroy: function() {
+			if (this.textView) {
+				this.textView.removeEventListener("ModelChanged", this._listener.onModelChanged);
+				this.textView.removeEventListener("Destroy", this._listener.onDestroy);
+				this.textView.removeEventListener("LineStyle", this._listener.onLineStyle);
+				this.textView = null;
+			}
+			this.grammar = null;
+			this._styles = null;
+			this._tree = null;
+			this._listener = null;
+		},
+		/** @private */
+		preprocess: function(grammar) {
+			var stack = [grammar];
+			for (; stack.length !== 0; ) {
+				var rule = stack.pop();
+				if (rule._resolvedRule && rule._typedRule) {
+					continue;
+				}
+//					console.debug("Process " + (rule.include || rule.name));
+				
+				// Look up include'd rule, create typed *Rule instance
+				rule._resolvedRule = this._resolve(rule);
+				rule._typedRule = this._createTypedRule(rule);
+				
+				// Convert the scope names to styles and cache them for later
+				this.addStyles(rule.name);
+				this.addStyles(rule.contentName);
+				this.addStylesForCaptures(rule.captures);
+				this.addStylesForCaptures(rule.beginCaptures);
+				this.addStylesForCaptures(rule.endCaptures);
+				
+				if (rule._resolvedRule !== rule) {
+					// Add include target
+					stack.push(rule._resolvedRule);
+				}
+				if (rule.patterns) {
+					// Add subrules
+					for (var i=0; i < rule.patterns.length; i++) {
+						stack.push(rule.patterns[i]);
+					}
+				}
+			}
+		},
+		
+		/**
+		 * @private
+		 * Adds eclipse.Style objects for scope to our _styles cache.
+		 * @param {String} scope A scope name, like "constant.character.php".
+		 */
+		addStyles: function(scope) {
+			if (scope && !this._styles[scope]) {
+				this._styles[scope] = [];
+				var scopeArray = scope.split(".");
+				for (var i = 0; i < scopeArray.length; i++) {
+					this._styles[scope].push(scopeArray.slice(0, i + 1).join("-"));
+				}
+			}
+		},
+		/** @private */
+		addStylesForCaptures: function(/**Object*/ captures) {
+			for (var prop in captures) {
+				if (captures.hasOwnProperty(prop)) {
+					var scope = captures[prop].name;
+					this.addStyles(scope);
+				}
+			}
+		},
+		/**
+		 * A rule that contains subrules ("patterns" in TextMate parlance) but has no "begin" or "end".
+		 * Also handles top level of grammar.
+		 * @private
+		 */
+		ContainerRule: (function() {
+			function ContainerRule(/**Object*/ rule) {
+				this.rule = rule;
+				this.subrules = rule.patterns;
+			}
+			ContainerRule.prototype.valueOf = function() { return "aa"; };
+			return ContainerRule;
+		}()),
+		/**
+		 * A rule that is delimited by "begin" and "end" matches, which may be separated by any number of
+		 * lines. This type of rule may contain subrules, which apply only inside the begin .. end region.
+		 * @private
+		 */
+		BeginEndRule: (function() {
+			function BeginEndRule(/**Object*/ rule) {
+				this.rule = rule;
+				// TODO: the TextMate blog claims that "end" is optional.
+				this.beginRegex = RegexUtil.toRegExp(rule.begin);
+				this.endRegex = RegexUtil.toRegExp(rule.end);
+				this.subrules = rule.patterns || [];
+				
+				this.endRegexHasBackRef = RegexUtil.hasBackReference(this.endRegex);
+				
+				// Deal with non-0 captures
+				var complexCaptures = RegexUtil.complexCaptures(rule.captures);
+				var complexBeginEnd = RegexUtil.complexCaptures(rule.beginCaptures) || RegexUtil.complexCaptures(rule.endCaptures);
+				this.isComplex = complexCaptures || complexBeginEnd;
+				if (this.isComplex) {
+					var bg = RegexUtil.groupify(this.beginRegex);
+					this.beginRegex = bg[0];
+					this.beginOld2New = bg[1];
+					this.beginConsuming = bg[2];
+					
+					var eg = RegexUtil.groupify(this.endRegex, this.beginOld2New /*Update end's backrefs to begin's new group #s*/);
+					this.endRegex = eg[0];
+					this.endOld2New = eg[1];
+					this.endConsuming = eg[2];
+				}
+			}
+			BeginEndRule.prototype.valueOf = function() { return this.beginRegex; };
+			return BeginEndRule;
+		}()),
+		/**
+		 * A rule with a "match" pattern.
+		 * @private
+		 */
+		MatchRule: (function() {
+			function MatchRule(/**Object*/ rule) {
+				this.rule = rule;
+				this.matchRegex = RegexUtil.toRegExp(rule.match);
+				this.isComplex = RegexUtil.complexCaptures(rule.captures);
+				if (this.isComplex) {
+					var mg = RegexUtil.groupify(this.matchRegex);
+					this.matchRegex = mg[0];
+					this.matchOld2New = mg[1];
+					this.matchConsuming = mg[2];
+				}
+			}
+			MatchRule.prototype.valueOf = function() { return this.matchRegex; };
+			return MatchRule;
+		}()),
+		/**
+		 * @param {Object} rule A rule from the grammar.
+		 * @returns {MatchRule|BeginEndRule|ContainerRule}
+		 * @private
+		 */
+		_createTypedRule: function(rule) {
+			if (rule.match) {
+				return new this.MatchRule(rule);
+			} else if (rule.begin) {
+				return new this.BeginEndRule(rule);
+			} else {
+				return new this.ContainerRule(rule);
+			}
+		},
+		/**
+		 * Resolves a rule from the grammar (which may be an include) into the real rule that it points to.
+		 * @private
+		 */
+		_resolve: function(rule) {
+			var resolved = rule;
+			if (rule.include) {
+				if (rule.begin || rule.end || rule.match) {
+					throw new Error("Unexpected regex pattern in \"include\" rule " + rule.include);
+				}
+				var name = rule.include;
+				if (name.charAt(0) === "#") {
+					resolved = this.grammar.repository && this.grammar.repository[name.substring(1)];
+					if (!resolved) { throw new Error("Couldn't find included rule " + name + " in grammar repository"); }
+				} else if (name === "$self") {
+					resolved = this.grammar;
+				} else if (name === "$base") {
+					// $base is only relevant when including rules from foreign grammars
+					throw new Error("Include \"$base\" is not supported"); 
+				} else {
+					resolved = this._allGrammars[name];
+					if (!resolved) {
+						for (var i=0; i < this.externalGrammars.length; i++) {
+							var grammar = this.externalGrammars[i];
+							if (grammar.scopeName === name) {
+								this.preprocess(grammar);
+								this._allGrammars[name] = grammar;
+								resolved = grammar;
+								break;
+							}
+						}
+					}
+				}
+			}
+			return resolved;
+		},
+
+		/** @private */
+		ContainerNode: (function() {
+			function ContainerNode(parent, rule) {
+				this.parent = parent;
+				this.rule = rule;
+				this.children = [];
+				
+				this.start = null;
+				this.end = null;
+			}
+			ContainerNode.prototype.addChild = function(child) {
+				this.children.push(child);
+			};
+			ContainerNode.prototype.valueOf = function() {
+				var r = this.rule;
+				return "ContainerNode { " + (r.include || "") + " " + (r.name || "") + (r.comment || "") + "}";
+			};
+			return ContainerNode;
+		}()),
+		/** @private */
+		BeginEndNode: (function() {
+			function BeginEndNode(parent, rule, beginMatch) {
+				this.parent = parent;
+				this.rule = rule;
+				this.children = [];
+				
+				this.setStart(beginMatch);
+				this.end = null; // will be set eventually during parsing (may be EOF)
+				this.endMatch = null; // may remain null if we never match our "end" pattern
+				
+				// Build a new regex if the "end" regex has backrefs since they refer to matched groups of beginMatch
+				if (rule.endRegexHasBackRef) {
+					this.endRegexSubstituted = RegexUtil.getSubstitutedRegex(rule.endRegex, beginMatch);
+				} else {
+					this.endRegexSubstituted = null;
+				}
+			}
+			BeginEndNode.prototype.addChild = function(child) {
+				this.children.push(child);
+			};
+			/** @return {Number} This node's index in its parent's "children" list */
+			BeginEndNode.prototype.getIndexInParent = function(node) {
+				return this.parent ? this.parent.children.indexOf(this) : -1;
+			};
+			/** @param {RegExp.match} beginMatch */
+			BeginEndNode.prototype.setStart = function(beginMatch) {
+				this.start = beginMatch.index;
+				this.beginMatch = beginMatch;
+			};
+			/** @param {RegExp.match|Number} endMatchOrLastChar */
+			BeginEndNode.prototype.setEnd = function(endMatchOrLastChar) {
+				if (endMatchOrLastChar && typeof(endMatchOrLastChar) === "object") {
+					var endMatch = endMatchOrLastChar;
+					this.endMatch = endMatch;
+					this.end = endMatch.index + endMatch[0].length;
+				} else {
+					var lastChar = endMatchOrLastChar;
+					this.endMatch = null;
+					this.end = lastChar;
+				}
+			};
+			BeginEndNode.prototype.shiftStart = function(amount) {
+				this.start += amount;
+				this.beginMatch.index += amount;
+			};
+			BeginEndNode.prototype.shiftEnd = function(amount) {
+				this.end += amount;
+				if (this.endMatch) { this.endMatch.index += amount; }
+			};
+			BeginEndNode.prototype.valueOf = function() {
+				return "{" + this.rule.beginRegex + " range=" + this.start + ".." + this.end + "}";
+			};
+			return BeginEndNode;
+		}()),
+		/** Pushes rules onto stack such that rules[startFrom] is on top
+		 * @private
+		 */
+		push: function(/**Array*/ stack, /**Array*/ rules) {
+			if (!rules) { return; }
+			for (var i = rules.length; i > 0; ) {
+				stack.push(rules[--i]);
+			}
+		},
+		/** Executes <code>regex</code> on <code>text</code>, and returns the match object with its index 
+		 * offset by the given amount.
+		 * @returns {RegExp.match}
+		 * @private
+		 */
+		exec: function(/**RegExp*/ regex, /**String*/ text, /**Number*/ offset) {
+			var match = regex.exec(text);
+			if (match) { match.index += offset; }
+			regex.lastIndex = 0; // Just in case
+			return match;
+		},
+		/** @returns {Number} The position immediately following the match.
+		 * @private
+		 */
+		afterMatch: function(/**RegExp.match*/ match) {
+			return match.index + match[0].length;
+		},
+		/**
+		 * @returns {RegExp.match} If node is a BeginEndNode and its rule's "end" pattern matches the text.
+		 * @private
+		 */
+		getEndMatch: function(/**Node*/ node, /**String*/ text, /**Number*/ offset) {
+			if (node instanceof this.BeginEndNode) {
+				var rule = node.rule;
+				var endRegex = node.endRegexSubstituted || rule.endRegex;
+				if (!endRegex) { return null; }
+				return this.exec(endRegex, text, offset);
+			}
+			return null;
+		},
+		/** Called once when file is first loaded to build the parse tree. Tree is updated incrementally thereafter 
+		 * as buffer is modified.
+		 * @private
+		 */
+		initialParse: function() {
+			var last = this.textView.getModel().getCharCount();
+			// First time; make parse tree for whole buffer
+			var root = new this.ContainerNode(null, this.grammar._typedRule);
+			this._tree = root;
+			this.parse(this._tree, false, 0);
+		},
+		onModelChanged: function(/**eclipse.ModelChangedEvent*/ e) {
+			var addedCharCount = e.addedCharCount,
+			    addedLineCount = e.addedLineCount,
+			    removedCharCount = e.removedCharCount,
+			    removedLineCount = e.removedLineCount,
+			    start = e.start;
+			if (!this._tree) {
+				this.initialParse();
+			} else {
+				var model = this.textView.getModel();
+				var charCount = model.getCharCount();
+				
+				// For rs, we must rewind to the line preceding the line 'start' is on. We can't rely on start's
+				// line since it may've been changed in a way that would cause a new beginMatch at its lineStart.
+				var rs = model.getLineEnd(model.getLineAtOffset(start) - 1); // may be < 0
+				var fd = this.getFirstDamaged(rs, rs);
+				rs = rs === -1 ? 0 : rs;
+				var stoppedAt;
+				if (fd) {
+					// [rs, re] is the region we need to verify. If we find the structure of the tree
+					// has changed in that area, then we may need to reparse the rest of the file.
+					stoppedAt = this.parse(fd, true, rs, start, addedCharCount, removedCharCount);
+				} else {
+					// FIXME: fd == null ?
+					stoppedAt = charCount;
+				}
+				this.textView.redrawRange(rs, stoppedAt);
+			}
+		},
+		/** @returns {BeginEndNode|ContainerNode} The result of taking the first (smallest "start" value) 
+		 * node overlapping [start,end] and drilling down to get its deepest damaged descendant (if any).
+		 * @private
+		 */
+		getFirstDamaged: function(start, end) {
+			// If start === 0 we actually have to start from the root because there is no position
+			// we can rely on. (First index is damaged)
+			if (start < 0) {
+				return this._tree;
+			}
+			
+			var nodes = [this._tree];
+			var result = null;
+			while (nodes.length) {
+				var n = nodes.pop();
+				if (!n.parent /*n is root*/ || this.isDamaged(n, start, end)) {
+					// n is damaged by the edit, so go into its children
+					// Note: If a node is damaged, then some of its descendents MAY be damaged
+					// If a node is undamaged, then ALL of its descendents are undamaged
+					if (n instanceof this.BeginEndNode) {
+						result = n;
+					}
+					// Examine children[0] last
+					for (var i=0; i < n.children.length; i++) {
+						nodes.push(n.children[i]);
+					}
+				}
+			}
+			return result || this._tree;
+		},
+		/** @returns true If <code>n</code> overlaps the interval [start,end].
+		 * @private
+		 */
+		isDamaged: function(/**BeginEndNode*/ n, start, end) {
+			// Note strict > since [2,5] doesn't overlap [5,7]
+			return (n.start <= end && n.end > start);
+		},
+		/**
+		 * Builds tree from some of the buffer content
+		 *
+		 * TODO cleanup params
+		 * @param {BeginEndNode|ContainerNode} origNode The deepest node that overlaps [rs,rs], or the root.
+		 * @param {Boolean} repairing 
+		 * @param {Number} rs See _onModelChanged()
+		 * @param {Number} [editStart] Only used for repairing === true
+		 * @param {Number} [addedCharCount] Only used for repairing === true
+		 * @param {Number} [removedCharCount] Only used for repairing === true
+		 * @returns {Number} The end position that redrawRange should be called for.
+		 * @private
+		 */
+		parse: function(origNode, repairing, rs, editStart, addedCharCount, removedCharCount) {
+			var model = this.textView.getModel();
+			var lastLineStart = model.getLineStart(model.getLineCount() - 1);
+			var eof = model.getCharCount();
+			var initialExpected = this.getInitialExpected(origNode, rs);
+			
+			// re is best-case stopping point; if we detect change to tree, we must continue past it
+			var re = -1;
+			if (repairing) {
+				origNode.repaired = true;
+				origNode.endNeedsUpdate = true;
+				var lastChild = origNode.children[origNode.children.length-1];
+				var delta = addedCharCount - removedCharCount;
+				var lastChildLineEnd = lastChild ? model.getLineEnd(model.getLineAtOffset(lastChild.end + delta)) : -1;
+				var editLineEnd = model.getLineEnd(model.getLineAtOffset(editStart + removedCharCount));
+				re = Math.max(lastChildLineEnd, editLineEnd);
+			}
+			re = (re === -1) ? eof : re;
+			
+			var expected = initialExpected;
+			var node = origNode;
+			var matchedChildOrEnd = false;
+			var pos = rs;
+			var redrawEnd = -1;
+			while (node && (!repairing || (pos < re))) {
+				var matchInfo = this.getNextMatch(model, node, pos);
+				if (!matchInfo) {
+					// Go to next line, if any
+					pos = (pos >= lastLineStart) ? eof : model.getLineStart(model.getLineAtOffset(pos) + 1);
+				}
+				var match = matchInfo && matchInfo.match,
+				    rule = matchInfo && matchInfo.rule,
+				    isSub = matchInfo && matchInfo.isSub,
+				    isEnd = matchInfo && matchInfo.isEnd;
+				if (isSub) {
+					pos = this.afterMatch(match);
+					if (rule instanceof this.BeginEndRule) {
+						matchedChildOrEnd = true;
+						// Matched a child. Did we expect that?
+						if (repairing && rule === expected.rule && node === expected.parent) {
+							// Yes: matched expected child
+							var foundChild = expected;
+							foundChild.setStart(match);
+							// Note: the 'end' position for this node will either be matched, or fixed up by us post-loop
+							foundChild.repaired = true;
+							foundChild.endNeedsUpdate = true;
+							node = foundChild; // descend
+							expected = this.getNextExpected(expected, "begin");
+						} else {
+							if (repairing) {
+								// No: matched unexpected child.
+								this.prune(node, expected);
+								repairing = false;
+							}
+							
+							// Add the new child (will replace 'expected' in node's children list)
+							var subNode = new this.BeginEndNode(node, rule, match);
+							node.addChild(subNode);
+							node = subNode; // descend
+						}
+					} else {
+						// Matched a MatchRule; no changes to tree required
+					}
+				} else if (isEnd || pos === eof) {
+					if (node instanceof this.BeginEndNode) {
+						if (match) {
+							matchedChildOrEnd = true;
+							redrawEnd = Math.max(redrawEnd, node.end); // if end moved up, must still redraw to its old value
+							node.setEnd(match);
+							pos = this.afterMatch(match);
+							// Matched node's end. Did we expect that?
+							if (repairing && node === expected && node.parent === expected.parent) {
+								// Yes: found the expected end of node
+								node.repaired = true;
+								delete node.endNeedsUpdate;
+								expected = this.getNextExpected(expected, "end");
+							} else {
+								if (repairing) {
+									// No: found an unexpected end
+									this.prune(node, expected);
+									repairing = false;
+								}
+							}
+						} else {
+							// Force-ending a BeginEndNode that runs until eof
+							node.setEnd(eof);
+							delete node.endNeedsUpdate;
+						}
+					}
+					node = node.parent; // ascend
+				}
+				
+				if (repairing && pos >= re && !matchedChildOrEnd) {
+					// Reached re without matching any begin/end => initialExpected itself was removed => repair fail
+					this.prune(origNode, initialExpected);
+					repairing = false;
+				}
+			} // end loop
+			// TODO: do this for every node we end?
+			this.removeUnrepairedChildren(origNode, repairing, rs);
+			
+			//console.debug("parsed " + (pos - rs) + " of " + model.getCharCount + "buf");
+			this.cleanup(repairing, origNode, rs, re, eof, addedCharCount, removedCharCount);
+			if (repairing) {
+				return Math.max(redrawEnd, pos);
+			} else {
+				return pos; // where we stopped reparsing
+			}
+		},
+		/** Helper for parse() in the repair case. To be called when ending a node, as any children that
+		 * lie in [rs,node.end] and were not repaired must've been deleted.
+		 * @private
+		 */
+		removeUnrepairedChildren: function(node, repairing, start) {
+			if (repairing) {
+				var children = node.children;
+				var removeFrom = -1;
+				for (var i=0; i < children.length; i++) {
+					var child = children[i];
+					if (!child.repaired && this.isDamaged(child, start, Number.MAX_VALUE /*end doesn't matter*/)) {
+						removeFrom = i;
+						break;
+					}
+				}
+				if (removeFrom !== -1) {
+					node.children.length = removeFrom;
+				}
+			}
+		},
+		/** Helper for parse() in the repair case
+		 * @private
+		 */
+		cleanup: function(repairing, origNode, rs, re, eof, addedCharCount, removedCharCount) {
+			var i, node, maybeRepairedNodes;
+			if (repairing) {
+				// The repair succeeded, so update stale begin/end indices by simple translation.
+				var delta = addedCharCount - removedCharCount;
+				// A repaired node's end can't exceed re, but it may exceed re-delta+1.
+				// TODO: find a way to guarantee disjoint intervals for repaired vs unrepaired, then stop using flag
+				var maybeUnrepairedNodes = this.getIntersecting(re-delta+1, eof);
+				maybeRepairedNodes = this.getIntersecting(rs, re);
+				// Handle unrepaired nodes. They are those intersecting [re-delta+1, eof] that don't have the flag
+				for (i=0; i < maybeUnrepairedNodes.length; i++) {
+					node = maybeUnrepairedNodes[i];
+					if (!node.repaired && node instanceof this.BeginEndNode) {
+						node.shiftEnd(delta);
+						node.shiftStart(delta);
+					}
+				}
+				// Translate 'end' index of repaired node whose 'end' was not matched in loop (>= re)
+				for (i=0; i < maybeRepairedNodes.length; i++) {
+					node = maybeRepairedNodes[i];
+					if (node.repaired && node.endNeedsUpdate) {
+						node.shiftEnd(delta);
+					}
+					delete node.endNeedsUpdate;
+					delete node.repaired;
+				}
+			} else {
+				// Clean up after ourself
+				maybeRepairedNodes = this.getIntersecting(rs, re);
+				for (i=0; i < maybeRepairedNodes.length; i++) {
+					delete maybeRepairedNodes[i].repaired;
+				}
+			}
+		},
+		/**
+		 * @param model {orion.editor.TextModel}
+		 * @param {Node} node
+		 * @param {Number} pos
+		 * @param {Boolean} [matchRulesOnly] Optional, if true only "match" subrules will be considered.
+		 * @returns {Object} A match info object with properties:
+		 * {Boolean} isEnd
+		 * {Boolean} isSub
+		 * {RegExp.match} match
+		 * {(Match|BeginEnd)Rule} rule
+		 * @private
+		 */
+		getNextMatch: function(model, node, pos, matchRulesOnly) {
+			var lineIndex = model.getLineAtOffset(pos);
+			var lineEnd = model.getLineEnd(lineIndex);
+			var line = model.getText(pos, lineEnd);
+
+			var stack = [],
+			    expandedContainers = [],
+			    subMatches = [],
+			    subrules = [];
+			this.push(stack, node.rule.subrules);
+			while (stack.length) {
+				var next = stack.length ? stack.pop() : null;
+				var subrule = next && next._resolvedRule._typedRule;
+				if (subrule instanceof this.ContainerRule && expandedContainers.indexOf(subrule) === -1) {
+					// Expand ContainerRule by pushing its subrules on
+					expandedContainers.push(subrule);
+					this.push(stack, subrule.subrules);
+					continue;
+				}
+				if (subrule && matchRulesOnly && !(subrule.matchRegex)) {
+					continue;
+				}
+				var subMatch = subrule && this.exec(subrule.matchRegex || subrule.beginRegex, line, pos);
+				if (subMatch) {
+					subMatches.push(subMatch);
+					subrules.push(subrule);
+				}
+			}
+
+			var bestSub = Number.MAX_VALUE,
+			    bestSubIndex = -1;
+			for (var i=0; i < subMatches.length; i++) {
+				var match = subMatches[i];
+				if (match.index < bestSub) {
+					bestSub = match.index;
+					bestSubIndex = i;
+				}
+			}
+			
+			if (!matchRulesOnly) {
+				// See if the "end" pattern of the active begin/end node matches.
+				// TODO: The active begin/end node may not be the same as the node that holds the subrules
+				var activeBENode = node;
+				var endMatch = this.getEndMatch(node, line, pos);
+				if (endMatch) {
+					var doEndLast = activeBENode.rule.applyEndPatternLast;
+					var endWins = bestSubIndex === -1 || (endMatch.index < bestSub) || (!doEndLast && endMatch.index === bestSub);
+					if (endWins) {
+						return {isEnd: true, rule: activeBENode.rule, match: endMatch};
+					}
+				}
+			}
+			return bestSubIndex === -1 ? null : {isSub: true, rule: subrules[bestSubIndex], match: subMatches[bestSubIndex]};
+		},
+		/**
+		 * Gets the node corresponding to the first match we expect to see in the repair.
+		 * @param {BeginEndNode|ContainerNode} node The node returned via getFirstDamaged(rs,rs) -- may be the root.
+		 * @param {Number} rs See _onModelChanged()
+		 * Note that because rs is a line end (or 0, a line start), it will intersect a beginMatch or 
+		 * endMatch either at their 0th character, or not at all. (begin/endMatches can't cross lines).
+		 * This is the only time we rely on the start/end values from the pre-change tree. After this 
+		 * we only look at node ordering, never use the old indices.
+		 * @returns {Node}
+		 * @private
+		 */
+		getInitialExpected: function(node, rs) {
+			// TODO: Kind of weird.. maybe ContainerNodes should have start & end set, like BeginEndNodes
+			var i, child;
+			if (node === this._tree) {
+				// get whichever of our children comes after rs
+				for (i=0; i < node.children.length; i++) {
+					child = node.children[i]; // BeginEndNode
+					if (child.start >= rs) {
+						return child;
+					}
+				}
+			} else if (node instanceof this.BeginEndNode) {
+				if (node.endMatch) {
+					// Which comes next after rs: our nodeEnd or one of our children?
+					var nodeEnd = node.endMatch.index;
+					for (i=0; i < node.children.length; i++) {
+						child = node.children[i]; // BeginEndNode
+						if (child.start >= rs) {
+							break;
+						}
+					}
+					if (child && child.start < nodeEnd) {
+						return child; // Expect child as the next match
+					}
+				} else {
+					// No endMatch => node goes until eof => it end should be the next match
+				}
+			}
+			return node; // We expect node to end, so it should be the next match
+		},
+		/**
+		 * Helper for repair() to tell us what kind of event we expect next.
+		 * @param {Node} expected Last value returned by this method.
+		 * @param {String} event "begin" if the last value of expected was matched as "begin",
+		 *  or "end" if it was matched as an end.
+		 * @returns {Node} The next expected node to match, or null.
+		 * @private
+		 */
+		getNextExpected: function(/**Node*/ expected, event) {
+			var node = expected;
+			if (event === "begin") {
+				var child = node.children[0];
+				if (child) {
+					return child;
+				} else {
+					return node;
+				}
+			} else if (event === "end") {
+				var parent = node.parent;
+				if (parent) {
+					var nextSibling = parent.children[parent.children.indexOf(node) + 1];
+					if (nextSibling) {
+						return nextSibling;
+					} else {
+						return parent;
+					}
+				}
+			}
+			return null;
+		},
+		/** Helper for parse() when repairing. Prunes out the unmatched nodes from the tree so we can continue parsing.
+		 * @private
+		 */
+		prune: function(/**BeginEndNode|ContainerNode*/ node, /**Node*/ expected) {
+			var expectedAChild = expected.parent === node;
+			if (expectedAChild) {
+				// Expected child wasn't matched; prune it and all siblings after it
+				node.children.length = expected.getIndexInParent();
+			} else if (node instanceof this.BeginEndNode) {
+				// Expected node to end but it didn't; set its end unknown and we'll match it eventually
+				node.endMatch = null;
+				node.end = null;
+			}
+			// Reparsing from node, so prune the successors outside of node's subtree
+			if (node.parent) {
+				node.parent.children.length = node.getIndexInParent() + 1;
+			}
+		},
+		onLineStyle: function(/**eclipse.LineStyleEvent*/ e) {
+			function byStart(r1, r2) {
+				return r1.start - r2.start;
+			}
+			
+			if (!this._tree) {
+				// In some cases it seems onLineStyle is called before onModelChanged, so we need to parse here
+				this.initialParse();
+			}
+			var lineStart = e.lineStart,
+			    model = this.textView.getModel(),
+			    lineEnd = model.getLineEnd(e.lineIndex);
+			
+			var rs = model.getLineEnd(model.getLineAtOffset(lineStart) - 1); // may be < 0
+			var node = this.getFirstDamaged(rs, rs);
+			
+			var scopes = this.getLineScope(model, node, lineStart, lineEnd);
+			e.ranges = this.toStyleRanges(scopes);
+			// Editor requires StyleRanges must be in ascending order by 'start', or else some will be ignored
+			e.ranges.sort(byStart);
+		},
+		/** Runs parse algorithm on [start, end] in the context of node, assigning scope as we find matches.
+		 * @private
+		 */
+		getLineScope: function(model, node, start, end) {
+			var pos = start;
+			var expected = this.getInitialExpected(node, start);
+			var scopes = [],
+			    gaps = [];
+			while (node && (pos < end)) {
+				var matchInfo = this.getNextMatch(model, node, pos);
+				if (!matchInfo) { 
+					break; // line is over
+				}
+				var match = matchInfo && matchInfo.match,
+				    rule = matchInfo && matchInfo.rule,
+				    isSub = matchInfo && matchInfo.isSub,
+				    isEnd = matchInfo && matchInfo.isEnd;
+				if (match.index !== pos) {
+					// gap [pos..match.index]
+					gaps.push({ start: pos, end: match.index, node: node});
+				}
+				if (isSub) {
+					pos = this.afterMatch(match);
+					if (rule instanceof this.BeginEndRule) {
+						// Matched a "begin", assign its scope and descend into it
+						this.addBeginScope(scopes, match, rule);
+						node = expected; // descend
+						expected = this.getNextExpected(expected, "begin");
+					} else {
+						// Matched a child MatchRule;
+						this.addMatchScope(scopes, match, rule);
+					}
+				} else if (isEnd) {
+					pos = this.afterMatch(match);
+					// Matched and "end", assign its end scope and go up
+					this.addEndScope(scopes, match, rule);
+					expected = this.getNextExpected(expected, "end");
+					node = node.parent; // ascend
+				}
+			}
+			if (pos < end) {
+				gaps.push({ start: pos, end: end, node: node });
+			}
+			var inherited = this.getInheritedLineScope(gaps, start, end);
+			return scopes.concat(inherited);
+		},
+		/** @private */
+		getInheritedLineScope: function(gaps, start, end) {
+			var scopes = [];
+			for (var i=0; i < gaps.length; i++) {
+				var gap = gaps[i];
+				var node = gap.node;
+				while (node) {
+					// if node defines a contentName or name, apply it
+					var rule = node.rule.rule;
+					var name = rule.name,
+					    contentName = rule.contentName;
+					// TODO: if both are given, we don't resolve the conflict. contentName always wins
+					var scope = contentName || name;
+					if (scope) {
+						this.addScopeRange(scopes, gap.start, gap.end, scope);
+						break;
+					}
+					node = node.parent;
+				}
+			}
+			return scopes;
+		},
+		/** @private */
+		addBeginScope: function(scopes, match, typedRule) {
+			var rule = typedRule.rule;
+			this.addCapturesScope(scopes, match, (rule.beginCaptures || rule.captures), typedRule.isComplex, typedRule.beginOld2New, typedRule.beginConsuming);
+		},
+		/** @private */
+		addEndScope: function(scopes, match, typedRule) {
+			var rule = typedRule.rule;
+			this.addCapturesScope(scopes, match, (rule.endCaptures || rule.captures), typedRule.isComplex, typedRule.endOld2New, typedRule.endConsuming);
+		},
+		/** @private */
+		addMatchScope: function(scopes, match, typedRule) {
+			var rule = typedRule.rule,
+			    name = rule.name,
+			    captures = rule.captures;
+			if (captures) {	
+				// captures takes priority over name
+				this.addCapturesScope(scopes, match, captures, typedRule.isComplex, typedRule.matchOld2New, typedRule.matchConsuming);
+			} else {
+				this.addScope(scopes, match, name);
+			}
+		},
+		/** @private */
+		addScope: function(scopes, match, name) {
+			if (!name) { return; }
+			scopes.push({start: match.index, end: this.afterMatch(match), scope: name });
+		},
+		/** @private */
+		addScopeRange: function(scopes, start, end, name) {
+			if (!name) { return; }
+			scopes.push({start: start, end: end, scope: name });
+		},
+		/** @private */
+		addCapturesScope: function(/**Array*/scopes, /*RegExp.match*/ match, /**Object*/captures, /**Boolean*/isComplex, /**Object*/old2New, /**Object*/consuming) {
+			if (!captures) { return; }
+			if (!isComplex) {
+				this.addScope(scopes, match, captures[0] && captures[0].name);
+			} else {
+				// apply scopes captures[1..n] to matching groups [1]..[n] of match
+				
+				// Sum up the lengths of preceding consuming groups to get the start offset for each matched group.
+				var newGroupStarts = {1: 0};
+				var sum = 0;
+				for (var num = 1; match[num] !== undefined; num++) {
+					if (consuming[num] !== undefined) {
+						sum += match[num].length;
+					}
+					if (match[num+1] !== undefined) {
+						newGroupStarts[num + 1] = sum;
+					}
+				}
+				// Map the group numbers referred to in captures object to the new group numbers, and get the actual matched range.
+				var start = match.index;
+				for (var oldGroupNum = 1; captures[oldGroupNum]; oldGroupNum++) {
+					var scope = captures[oldGroupNum].name;
+					var newGroupNum = old2New[oldGroupNum];
+					var groupStart = start + newGroupStarts[newGroupNum];
+					// Not every capturing group defined in regex need match every time the regex is run.
+					// eg. (a)|b matches "b" but group 1 is undefined
+					if (typeof match[newGroupNum] !== "undefined") {
+						var groupEnd = groupStart + match[newGroupNum].length;
+						this.addScopeRange(scopes, groupStart, groupEnd, scope);
+					}
+				}
+			}
+		},
+		/** @returns {Node[]} In depth-first order
+		 * @private
+		 */
+		getIntersecting: function(start, end) {
+			var result = [];
+			var nodes = this._tree ? [this._tree] : [];
+			while (nodes.length) {
+				var n = nodes.pop();
+				var visitChildren = false;
+				if (n instanceof this.ContainerNode) {
+					visitChildren = true;
+				} else if (this.isDamaged(n, start, end)) {
+					visitChildren = true;
+					result.push(n);
+				}
+				if (visitChildren) {
+					var len = n.children.length;
+//					for (var i=len-1; i >= 0; i--) {
+//						nodes.push(n.children[i]);
+//					}
+					for (var i=0; i < len; i++) {
+						nodes.push(n.children[i]);
+					}
+				}
+			}
+			return result.reverse();
+		},
+		/**
+		 * Applies the grammar to obtain the {@link eclipse.StyleRange[]} for the given line.
+		 * @returns {eclipse.StyleRange[]}
+		 * @private
+		 */
+		toStyleRanges: function(/**ScopeRange[]*/ scopeRanges) {
+			var styleRanges = [];
+			for (var i=0; i < scopeRanges.length; i++) {
+				var scopeRange = scopeRanges[i];
+				var classNames = this._styles[scopeRange.scope];
+				if (!classNames) { throw new Error("styles not found for " + scopeRange.scope); }
+				var classNamesString = classNames.join(" ");
+				styleRanges.push({start: scopeRange.start, end: scopeRange.end, style: {styleClass: classNamesString}});
+//				console.debug("{start " + styleRanges[i].start + ", end " + styleRanges[i].end + ", style: " + styleRanges[i].style.styleClass + "}");
+			}
+			return styleRanges;
+		}
+	};
+	
+	return {
+		RegexUtil: RegexUtil,
+		TextMateStyler: TextMateStyler
+	};
+});
+
+/******************************************************************************* 
+ * @license
+ * Copyright (c) 2011, 2012 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials are made 
+ * available under the terms of the Eclipse Public License v1.0 
+ * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
+ * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
+ * 
+ * Contributors: IBM Corporation - initial API and implementation 
+ ******************************************************************************/
+
+/*jslint */
+/*global define */
+
+define("orion/editor/htmlGrammar", [], function() {
+
+	/**
+	 * Provides a grammar that can do some very rough syntax highlighting for HTML.
+	 * @class orion.syntax.HtmlGrammar
+	 */
+	function HtmlGrammar() {
+		/**
+		 * Object containing the grammar rules.
+		 * @public
+		 * @type Object
+		 */
+		return {
+			"scopeName": "source.html",
+			"uuid": "3B5C76FB-EBB5-D930-F40C-047D082CE99B",
+			"patterns": [
+				{
+					"begin": "<!(doctype|DOCTYPE)",
+					"end": ">",
+					"contentName": "entity.name.tag.doctype.html",
+					"beginCaptures": {
+						"0": { "name": "entity.name.tag.doctype.html" }
+					},
+					"endCaptures": {
+						"0": { "name": "entity.name.tag.doctype.html" }
+					}
+				},
+				{
+					"begin": "<!--",
+					"end": "-->",
+					"beginCaptures": {
+						"0": { "name": "punctuation.definition.comment.html" }
+					},
+					"endCaptures": {
+						"0": { "name": "punctuation.definition.comment.html" }
+					},
+					"patterns": [
+						{
+							"match": "--",
+							"name": "invalid.illegal.badcomment.html"
+						}
+					],
+					"contentName": "comment.block.html"
+				},
+				{ // startDelimiter + tagName
+					"match": "<[A-Za-z0-9_\\-:]+(?= ?)",
+					"name": "entity.name.tag.html"
+				},
+				{ "include": "#attrName" },
+				{ "include": "#qString" },
+				{ "include": "#qqString" },
+				{ "include": "#entity" },
+				// TODO attrName, qString, qqString should be applied first while inside a tag
+				{ // startDelimiter + slash + tagName + endDelimiter
+					"match": "</[A-Za-z0-9_\\-:]+>",
+					"name": "entity.name.tag.html"
+				},
+				{ // end delimiter of open tag
+					"match": ">", 
+					"name": "entity.name.tag.html"
+				} ],
+			"repository": {
+				"attrName": { // attribute name
+					"match": "[A-Za-z\\-:]+(?=\\s*=\\s*['\"])",
+					"name": "entity.other.attribute.name.html"
+				},
+				"qqString": { // double quoted string
+					"match": "(\")[^\"]+(\")",
+					"name": "string.quoted.double.html"
+				},
+				"qString": { // single quoted string
+					"match": "(')[^']+(\')",
+					"name": "string.quoted.single.html"
+				},
+				"entity": {
+					"match": "&[A-Za-z0-9]+;",
+					"name": "constant.character.entity.html"
+				}
+			}
+		};
+	}
+
+	return {HtmlGrammar: HtmlGrammar};
+});
+
+/*******************************************************************************
+ * @license
+ * Copyright (c) 2010, 2012 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials are made 
+ * available under the terms of the Eclipse Public License v1.0 
+ * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
+ * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
+ * 
+ * Contributors: IBM Corporation - initial API and implementation
+ *               Alex Lakatos - fix for bug#369781
+ ******************************************************************************/
+
+/*global define */
+
+define("examples/editor/textStyler", [ //$NON-NLS-0$
+	'orion/editor/annotations', //$NON-NLS-0$
+	'orion/editor/keywords' //$NON-NLS-0$
+], function(mAnnotations, mKeywords) {
+
+	var JS_KEYWORDS = mKeywords.JSKeywords;
+
+	var JAVA_KEYWORDS = mKeywords.JAVAKeywords;
+
+	var CSS_KEYWORDS = mKeywords.CSSKeywords;
+
+	// Scanner constants
+	var UNKOWN = 1;
+	var KEYWORD = 2;
+	var NUMBER = 3;
+	var STRING = 4;
+	var MULTILINE_STRING = 5;
+	var SINGLELINE_COMMENT = 6;
+	var MULTILINE_COMMENT = 7;
+	var DOC_COMMENT = 8;
+	var WHITE = 9;
+	var WHITE_TAB = 10;
+	var WHITE_SPACE = 11;
+	var HTML_MARKUP = 12;
+	var DOC_TAG = 13;
+	var TASK_TAG = 14;
+	
+	var BRACKETS = "{}()[]<>"; //$NON-NLS-0$
+
+	// Styles 
+	var singleCommentStyle = {styleClass: "token_singleline_comment"}; //$NON-NLS-0$
+	var multiCommentStyle = {styleClass: "token_multiline_comment"}; //$NON-NLS-0$
+	var docCommentStyle = {styleClass: "token_doc_comment"}; //$NON-NLS-0$
+	var htmlMarkupStyle = {styleClass: "token_doc_html_markup"}; //$NON-NLS-0$
+	var tasktagStyle = {styleClass: "token_task_tag"}; //$NON-NLS-0$
+	var doctagStyle = {styleClass: "token_doc_tag"}; //$NON-NLS-0$
+	var stringStyle = {styleClass: "token_string"}; //$NON-NLS-0$
+	var numberStyle = {styleClass: "token_number"}; //$NON-NLS-0$
+	var keywordStyle = {styleClass: "token_keyword"}; //$NON-NLS-0$
+	var spaceStyle = {styleClass: "token_space"}; //$NON-NLS-0$
+	var tabStyle = {styleClass: "token_tab"}; //$NON-NLS-0$
+	var caretLineStyle = {styleClass: "line_caret"}; //$NON-NLS-0$
+	
+	function Scanner (keywords, whitespacesVisible) {
+		this.keywords = keywords;
+		this.whitespacesVisible = whitespacesVisible;
+		this.setText("");
+	}
+	
+	Scanner.prototype = {
+		getOffset: function() {
+			return this.offset;
+		},
+		getStartOffset: function() {
+			return this.startOffset;
+		},
+		getData: function() {
+			return this.text.substring(this.startOffset, this.offset);
+		},
+		getDataLength: function() {
+			return this.offset - this.startOffset;
+		},
+		_default: function(c) {
+			switch (c) {
+				case 32: // SPACE
+				case 9: // TAB
+					if (this.whitespacesVisible) {
+						return c === 32 ? WHITE_SPACE : WHITE_TAB;
+					}
+					do {
+						c = this._read();
+					} while(c === 32 || c === 9);
+					this._unread(c);
+					return WHITE;
+				case 123: // {
+				case 125: // }
+				case 40: // (
+				case 41: // )
+				case 91: // [
+				case 93: // ]
+				case 60: // <
+				case 62: // >
+					// BRACKETS
+					return c;
+				default:
+					var isCSS = this.isCSS;
+					var off = this.offset - 1;
+					if (!isCSS && 48 <= c && c <= 57) {
+						var floating = false, exponential = false, hex = false, firstC = c;
+						do {
+							c = this._read();
+							if (c === 46 /* dot */ && !floating) {
+								floating = true;
+							} else if (c === 101 /* e */ && !exponential) {
+								floating = exponential = true;
+								c = this._read();
+								if (c !== 45 /* MINUS */) {
+									this._unread(c);
+								}
+							} else if (c === 120 /* x */ && firstC === 48 && (this.offset - off === 2)) {
+								floating = exponential = hex = true;
+							} else if (!(48 <= c && c <= 57 || (hex && ((65 <= c && c <= 70) || (97 <= c && c <= 102))))) { //NUMBER DIGIT or HEX
+								break;
+							}
+						} while(true);
+						this._unread(c);
+						return NUMBER;
+					}
+					if ((97 <= c && c <= 122) || (65 <= c && c <= 90) || c === 95 || (45 /* DASH */ === c && isCSS)) { //LETTER OR UNDERSCORE OR NUMBER
+						do {
+							c = this._read();
+						} while((97 <= c && c <= 122) || (65 <= c && c <= 90) || c === 95 || (48 <= c && c <= 57) || (45 /* DASH */ === c && isCSS));  //LETTER OR UNDERSCORE OR NUMBER
+						this._unread(c);
+						var keywords = this.keywords;
+						if (keywords.length > 0) {
+							var word = this.text.substring(off, this.offset);
+							//TODO slow
+							for (var i=0; i<keywords.length; i++) {
+								if (this.keywords[i] === word) { return KEYWORD; }
+							}
+						}
+					}
+					return UNKOWN;
+			}
+		},
+		_read: function() {
+			if (this.offset < this.text.length) {
+				return this.text.charCodeAt(this.offset++);
+			}
+			return -1;
+		},
+		_unread: function(c) {
+			if (c !== -1) { this.offset--; }
+		},
+		nextToken: function() {
+			this.startOffset = this.offset;
+			while (true) {
+				var c = this._read(), result;
+				switch (c) {
+					case -1: return null;
+					case 47:	// SLASH -> comment
+						c = this._read();
+						if (!this.isCSS) {
+							if (c === 47) { // SLASH -> single line
+								while (true) {
+									c = this._read();
+									if ((c === -1) || (c === 10) || (c === 13)) {
+										this._unread(c);
+										return SINGLELINE_COMMENT;
+									}
+								}
+							}
+						}
+						if (c === 42) { // STAR -> multi line 
+							c = this._read();
+							var token = MULTILINE_COMMENT;
+							if (c === 42) {
+								token = DOC_COMMENT;
+							}
+							while (true) {
+								while (c === 42) {
+									c = this._read();
+									if (c === 47) {
+										return token;
+									}
+								}
+								if (c === -1) {
+									this._unread(c);
+									return token;
+								}
+								c = this._read();
+							}
+						}
+						this._unread(c);
+						return UNKOWN;
+					case 39:	// SINGLE QUOTE -> char const
+						result = STRING;
+						while(true) {
+							c = this._read();
+							switch (c) {
+								case 39:
+									return result;
+								case 13:
+								case 10:
+								case -1:
+									this._unread(c);
+									return result;
+								case 92: // BACKSLASH
+									c = this._read();
+									switch (c) {
+										case 10: result = MULTILINE_STRING; break;
+										case 13:
+											result = MULTILINE_STRING;
+											c = this._read();
+											if (c !== 10) {
+												this._unread(c);
+											}
+											break;
+									}
+									break;
+							}
+						}
+						break;
+					case 34:	// DOUBLE QUOTE -> string
+						result = STRING;
+						while(true) {
+							c = this._read();
+							switch (c) {
+								case 34: // DOUBLE QUOTE
+									return result;
+								case 13:
+								case 10:
+								case -1:
+									this._unread(c);
+									return result;
+								case 92: // BACKSLASH
+									c = this._read();
+									switch (c) {
+										case 10: result = MULTILINE_STRING; break;
+										case 13:
+											result = MULTILINE_STRING;
+											c = this._read();
+											if (c !== 10) {
+												this._unread(c);
+											}
+											break;
+									}
+									break;
+							}
+						}
+						break;
+					default:
+						return this._default(c);
+				}
+			}
+		},
+		setText: function(text) {
+			this.text = text;
+			this.offset = 0;
+			this.startOffset = 0;
+		}
+	};
+	
+	function WhitespaceScanner () {
+		Scanner.call(this, null, true);
+	}
+	WhitespaceScanner.prototype = new Scanner(null);
+	WhitespaceScanner.prototype.nextToken = function() {
+		this.startOffset = this.offset;
+		while (true) {
+			var c = this._read();
+			switch (c) {
+				case -1: return null;
+				case 32: // SPACE
+					return WHITE_SPACE;
+				case 9: // TAB
+					return WHITE_TAB;
+				default:
+					do {
+						c = this._read();
+					} while(!(c === 32 || c === 9 || c === -1));
+					this._unread(c);
+					return UNKOWN;
+			}
+		}
+	};
+	
+	function CommentScanner (whitespacesVisible) {
+		Scanner.call(this, null, whitespacesVisible);
+	}
+	CommentScanner.prototype = new Scanner(null);
+	CommentScanner.prototype.setType = function(type) {
+		this._type = type;
+	};
+	CommentScanner.prototype.nextToken = function() {
+		this.startOffset = this.offset;
+		while (true) {
+			var c = this._read();
+			switch (c) {
+				case -1: return null;
+				case 32: // SPACE
+				case 9: // TAB
+					if (this.whitespacesVisible) {
+						return c === 32 ? WHITE_SPACE : WHITE_TAB;
+					}
+					do {
+						c = this._read();
+					} while(c === 32 || c === 9);
+					this._unread(c);
+					return WHITE;
+				case 60: // <
+					if (this._type === DOC_COMMENT) {
+						do {
+							c = this._read();
+						} while(!(c === 62 || c === -1)); // >
+						if (c === 62) {
+							return HTML_MARKUP;
+						}
+					}
+					return UNKOWN;
+				case 64: // @
+					if (this._type === DOC_COMMENT) {
+						do {
+							c = this._read();
+						} while((97 <= c && c <= 122) || (65 <= c && c <= 90) || c === 95 || (48 <= c && c <= 57));  //LETTER OR UNDERSCORE OR NUMBER
+						this._unread(c);
+						return DOC_TAG;
+					}
+					return UNKOWN;
+				case 84: // T
+					if ((c = this._read()) === 79) { // O
+						if ((c = this._read()) === 68) { // D
+							if ((c = this._read()) === 79) { // O
+								c = this._read();
+								if (!((97 <= c && c <= 122) || (65 <= c && c <= 90) || c === 95 || (48 <= c && c <= 57))) {
+									this._unread(c);
+									return TASK_TAG;
+								}
+								this._unread(c);
+							} else {
+								this._unread(c);
+							}
+						} else {
+							this._unread(c);
+						}
+					} else {
+						this._unread(c);
+					}
+					//FALL THROUGH
+				default:
+					do {
+						c = this._read();
+					} while(!(c === 32 || c === 9 || c === -1 || c === 60 || c === 64 || c === 84));
+					this._unread(c);
+					return UNKOWN;
+			}
+		}
+	};
+	
+	function FirstScanner () {
+		Scanner.call(this, null, false);
+	}
+	FirstScanner.prototype = new Scanner(null);
+	FirstScanner.prototype._default = function(c) {
+		while(true) {
+			c = this._read();
+			switch (c) {
+				case 47: // SLASH
+				case 34: // DOUBLE QUOTE
+				case 39: // SINGLE QUOTE
+				case -1:
+					this._unread(c);
+					return UNKOWN;
+			}
+		}
+	};
+	
+	function TextStyler (view, lang, annotationModel) {
+		this.commentStart = "/*"; //$NON-NLS-0$
+		this.commentEnd = "*/"; //$NON-NLS-0$
+		var keywords = [];
+		switch (lang) {
+			case "java": keywords = JAVA_KEYWORDS; break; //$NON-NLS-0$
+			case "js": keywords = JS_KEYWORDS; break; //$NON-NLS-0$
+			case "css": keywords = CSS_KEYWORDS; break; //$NON-NLS-0$
+		}
+		this.whitespacesVisible = false;
+		this.detectHyperlinks = true;
+		this.highlightCaretLine = false;
+		this.foldingEnabled = true;
+		this.detectTasks = true;
+		this._scanner = new Scanner(keywords, this.whitespacesVisible);
+		this._firstScanner = new FirstScanner();
+		this._commentScanner = new CommentScanner(this.whitespacesVisible);
+		this._whitespaceScanner = new WhitespaceScanner();
+		//TODO these scanners are not the best/correct way to parse CSS
+		if (lang === "css") { //$NON-NLS-0$
+			this._scanner.isCSS = true;
+			this._firstScanner.isCSS = true;
+		}
+		this.view = view;
+		this.annotationModel = annotationModel;
+		this._bracketAnnotations = undefined; 
+		
+		var self = this;
+		this._listener = {
+			onChanged: function(e) {
+				self._onModelChanged(e);
+			},
+			onDestroy: function(e) {
+				self._onDestroy(e);
+			},
+			onLineStyle: function(e) {
+				self._onLineStyle(e);
+			},
+			onMouseDown: function(e) {
+				self._onMouseDown(e);
+			},
+			onSelection: function(e) {
+				self._onSelection(e);
+			}
+		};
+		var model = view.getModel();
+		if (model.getBaseModel) {
+			model = model.getBaseModel();
+		}
+		model.addEventListener("Changed", this._listener.onChanged); //$NON-NLS-0$
+		view.addEventListener("MouseDown", this._listener.onMouseDown); //$NON-NLS-0$
+		view.addEventListener("Selection", this._listener.onSelection); //$NON-NLS-0$
+		view.addEventListener("Destroy", this._listener.onDestroy); //$NON-NLS-0$
+		view.addEventListener("LineStyle", this._listener.onLineStyle); //$NON-NLS-0$
+		this._computeComments ();
+		this._computeFolding();
+		view.redrawLines();
+	}
+	
+	TextStyler.prototype = {
+		destroy: function() {
+			var view = this.view;
+			if (view) {
+				var model = view.getModel();
+				if (model.getBaseModel) {
+					model = model.getBaseModel();
+				}
+				model.removeEventListener("Changed", this._listener.onChanged); //$NON-NLS-0$
+				view.removeEventListener("MouseDown", this._listener.onMouseDown); //$NON-NLS-0$
+				view.removeEventListener("Selection", this._listener.onSelection); //$NON-NLS-0$
+				view.removeEventListener("Destroy", this._listener.onDestroy); //$NON-NLS-0$
+				view.removeEventListener("LineStyle", this._listener.onLineStyle); //$NON-NLS-0$
+				this.view = null;
+			}
+		},
+		setHighlightCaretLine: function(highlight) {
+			this.highlightCaretLine = highlight;
+		},
+		setWhitespacesVisible: function(visible) {
+			this.whitespacesVisible = visible;
+			this._scanner.whitespacesVisible = visible;
+			this._commentScanner.whitespacesVisible = visible;
+		},
+		setDetectHyperlinks: function(enabled) {
+			this.detectHyperlinks = enabled;
+		},
+		setFoldingEnabled: function(enabled) {
+			this.foldingEnabled = enabled;
+		},
+		setDetectTasks: function(enabled) {
+			this.detectTasks = enabled;
+		},
+		_binarySearch: function (array, offset, inclusive, low, high) {
+			var index;
+			if (low === undefined) { low = -1; }
+			if (high === undefined) { high = array.length; }
+			while (high - low > 1) {
+				index = Math.floor((high + low) / 2);
+				if (offset <= array[index].start) {
+					high = index;
+				} else if (inclusive && offset < array[index].end) {
+					high = index;
+					break;
+				} else {
+					low = index;
+				}
+			}
+			return high;
+		},
+		_computeComments: function() {
+			var model = this.view.getModel();
+			if (model.getBaseModel) { model = model.getBaseModel(); }
+			this.comments = this._findComments(model.getText());
+		},
+		_computeFolding: function() {
+			if (!this.foldingEnabled) { return; }
+			var view = this.view;
+			var viewModel = view.getModel();
+			if (!viewModel.getBaseModel) { return; }
+			var annotationModel = this.annotationModel;
+			if (!annotationModel) { return; }
+			annotationModel.removeAnnotations(mAnnotations.AnnotationType.ANNOTATION_FOLDING);
+			var add = [];
+			var baseModel = viewModel.getBaseModel();
+			var comments = this.comments;
+			for (var i=0; i<comments.length; i++) {
+				var comment = comments[i];
+				var annotation = this._createFoldingAnnotation(viewModel, baseModel, comment.start, comment.end);
+				if (annotation) { 
+					add.push(annotation);
+				}
+			}
+			annotationModel.replaceAnnotations(null, add);
+		},
+		_createFoldingAnnotation: function(viewModel, baseModel, start, end) {
+			var startLine = baseModel.getLineAtOffset(start);
+			var endLine = baseModel.getLineAtOffset(end);
+			if (startLine === endLine) {
+				return null;
+			}
+			return new (mAnnotations.AnnotationType.getType(mAnnotations.AnnotationType.ANNOTATION_FOLDING))(start, end, viewModel);
+		},
+		_computeTasks: function(type, commentStart, commentEnd) {
+			if (!this.detectTasks) { return; }
+			var annotationModel = this.annotationModel;
+			if (!annotationModel) { return; }
+			var view = this.view;
+			var viewModel = view.getModel(), baseModel = viewModel;
+			if (viewModel.getBaseModel) { baseModel = viewModel.getBaseModel(); }
+			var annotations = annotationModel.getAnnotations(commentStart, commentEnd);
+			var remove = [];
+			var annotationType = mAnnotations.AnnotationType.ANNOTATION_TASK;
+			while (annotations.hasNext()) {
+				var annotation = annotations.next();
+				if (annotation.type === annotationType) {
+					remove.push(annotation);
+				}
+			}
+			var add = [];
+			var scanner = this._commentScanner;
+			scanner.setText(baseModel.getText(commentStart, commentEnd));
+			var token;
+			while ((token = scanner.nextToken())) {
+				var tokenStart = scanner.getStartOffset() + commentStart;
+				if (token === TASK_TAG) {
+					var end = baseModel.getLineEnd(baseModel.getLineAtOffset(tokenStart));
+					if (type !== SINGLELINE_COMMENT) {
+						end = Math.min(end, commentEnd - this.commentEnd.length);
+					}
+					add.push(mAnnotations.AnnotationType.createAnnotation(annotationType, tokenStart, end, baseModel.getText(tokenStart, end)));
+				}
+			}
+			annotationModel.replaceAnnotations(remove, add);
+		},
+		_getLineStyle: function(lineIndex) {
+			if (this.highlightCaretLine) {
+				var view = this.view;
+				var model = view.getModel();
+				var selection = view.getSelection();
+				if (selection.start === selection.end && model.getLineAtOffset(selection.start) === lineIndex) {
+					return caretLineStyle;
+				}
+			}
+			return null;
+		},
+		_getStyles: function(model, text, start) {
+			if (model.getBaseModel) {
+				start = model.mapOffset(start);
+			}
+			var end = start + text.length;
+			
+			var styles = [];
+			
+			// for any sub range that is not a comment, parse code generating tokens (keywords, numbers, brackets, line comments, etc)
+			var offset = start, comments = this.comments;
+			var startIndex = this._binarySearch(comments, start, true);
+			for (var i = startIndex; i < comments.length; i++) {
+				if (comments[i].start >= end) { break; }
+				var commentStart = comments[i].start;
+				var commentEnd = comments[i].end;
+				if (offset < commentStart) {
+					this._parse(text.substring(offset - start, commentStart - start), offset, styles);
+				}
+				var type = comments[i].type, style;
+				switch (type) {
+					case DOC_COMMENT: style = docCommentStyle; break;
+					case MULTILINE_COMMENT: style = multiCommentStyle; break;
+					case MULTILINE_STRING: style = stringStyle; break;
+				}
+				var s = Math.max(offset, commentStart);
+				var e = Math.min(end, commentEnd);
+				if ((type === DOC_COMMENT || type === MULTILINE_COMMENT) && (this.whitespacesVisible || this.detectHyperlinks)) {
+					this._parseComment(text.substring(s - start, e - start), s, styles, style, type);
+				} else if (type === MULTILINE_STRING && this.whitespacesVisible) {
+					this._parseString(text.substring(s - start, e - start), s, styles, stringStyle);
+				} else {
+					styles.push({start: s, end: e, style: style});
+				}
+				offset = commentEnd;
+			}
+			if (offset < end) {
+				this._parse(text.substring(offset - start, end - start), offset, styles);
+			}
+			if (model.getBaseModel) {
+				for (var j = 0; j < styles.length; j++) {
+					var length = styles[j].end - styles[j].start;
+					styles[j].start = model.mapOffset(styles[j].start, true);
+					styles[j].end = styles[j].start + length;
+				}
+			}
+			return styles;
+		},
+		_parse: function(text, offset, styles) {
+			var scanner = this._scanner;
+			scanner.setText(text);
+			var token;
+			while ((token = scanner.nextToken())) {
+				var tokenStart = scanner.getStartOffset() + offset;
+				var style = null;
+				switch (token) {
+					case KEYWORD: style = keywordStyle; break;
+					case NUMBER: style = numberStyle; break;
+					case MULTILINE_STRING:
+					case STRING:
+						if (this.whitespacesVisible) {
+							this._parseString(scanner.getData(), tokenStart, styles, stringStyle);
+							continue;
+						} else {
+							style = stringStyle;
+						}
+						break;
+					case DOC_COMMENT: 
+						this._parseComment(scanner.getData(), tokenStart, styles, docCommentStyle, token);
+						continue;
+					case SINGLELINE_COMMENT:
+						this._parseComment(scanner.getData(), tokenStart, styles, singleCommentStyle, token);
+						continue;
+					case MULTILINE_COMMENT: 
+						this._parseComment(scanner.getData(), tokenStart, styles, multiCommentStyle, token);
+						continue;
+					case WHITE_TAB:
+						if (this.whitespacesVisible) {
+							style = tabStyle;
+						}
+						break;
+					case WHITE_SPACE:
+						if (this.whitespacesVisible) {
+							style = spaceStyle;
+						}
+						break;
+				}
+				styles.push({start: tokenStart, end: scanner.getOffset() + offset, style: style});
+			}
+		},
+		_parseComment: function(text, offset, styles, s, type) {
+			var scanner = this._commentScanner;
+			scanner.setText(text);
+			scanner.setType(type);
+			var token;
+			while ((token = scanner.nextToken())) {
+				var tokenStart = scanner.getStartOffset() + offset;
+				var style = s;
+				switch (token) {
+					case WHITE_TAB:
+						if (this.whitespacesVisible) {
+							style = tabStyle;
+						}
+						break;
+					case WHITE_SPACE:
+						if (this.whitespacesVisible) {
+							style = spaceStyle;
+						}
+						break;
+					case HTML_MARKUP:
+						style = htmlMarkupStyle;
+						break;
+					case DOC_TAG:
+						style = doctagStyle;
+						break;
+					case TASK_TAG:
+						style = tasktagStyle;
+						break;
+					default:
+						if (this.detectHyperlinks) {
+							style = this._detectHyperlinks(scanner.getData(), tokenStart, styles, style);
+						}
+				}
+				if (style) {
+					styles.push({start: tokenStart, end: scanner.getOffset() + offset, style: style});
+				}
+			}
+		},
+		_parseString: function(text, offset, styles, s) {
+			var scanner = this._whitespaceScanner;
+			scanner.setText(text);
+			var token;
+			while ((token = scanner.nextToken())) {
+				var tokenStart = scanner.getStartOffset() + offset;
+				var style = s;
+				switch (token) {
+					case WHITE_TAB:
+						if (this.whitespacesVisible) {
+							style = tabStyle;
+						}
+						break;
+					case WHITE_SPACE:
+						if (this.whitespacesVisible) {
+							style = spaceStyle;
+						}
+						break;
+				}
+				if (style) {
+					styles.push({start: tokenStart, end: scanner.getOffset() + offset, style: style});
+				}
+			}
+		},
+		_detectHyperlinks: function(text, offset, styles, s) {
+			var href = null, index, linkStyle;
+			if ((index = text.indexOf("://")) > 0) { //$NON-NLS-0$
+				href = text;
+				var start = index;
+				while (start > 0) {
+					var c = href.charCodeAt(start - 1);
+					if (!((97 <= c && c <= 122) || (65 <= c && c <= 90) || 0x2d === c || (48 <= c && c <= 57))) { //LETTER OR DASH OR NUMBER
+						break;
+					}
+					start--;
+				}
+				if (start > 0) {
+					var brackets = "\"\"''(){}[]<>"; //$NON-NLS-0$
+					index = brackets.indexOf(href.substring(start - 1, start));
+					if (index !== -1 && (index & 1) === 0 && (index = href.lastIndexOf(brackets.substring(index + 1, index + 2))) !== -1) {
+						var end = index;
+						linkStyle = this._clone(s);
+						linkStyle.tagName = "a"; //$NON-NLS-0$
+						linkStyle.attributes = {href: href.substring(start, end)};
+						styles.push({start: offset, end: offset + start, style: s});
+						styles.push({start: offset + start, end: offset + end, style: linkStyle});
+						styles.push({start: offset + end, end: offset + text.length, style: s});
+						return null;
+					}
+				}
+			} else if (text.toLowerCase().indexOf("bug#") === 0) { //$NON-NLS-0$
+				href = "https://bugs.eclipse.org/bugs/show_bug.cgi?id=" + parseInt(text.substring(4), 10); //$NON-NLS-0$
+			}
+			if (href) {
+				linkStyle = this._clone(s);
+				linkStyle.tagName = "a"; //$NON-NLS-0$
+				linkStyle.attributes = {href: href};
+				return linkStyle;
+			}
+			return s;
+		},
+		_clone: function(obj) {
+			if (!obj) { return obj; }
+			var newObj = {};
+			for (var p in obj) {
+				if (obj.hasOwnProperty(p)) {
+					var value = obj[p];
+					newObj[p] = value;
+				}
+			}
+			return newObj;
+		},
+		_findComments: function(text, offset) {
+			offset = offset || 0;
+			var scanner = this._firstScanner, token;
+			scanner.setText(text);
+			var result = [];
+			while ((token = scanner.nextToken())) {
+				if (token === MULTILINE_COMMENT || token === DOC_COMMENT || token === MULTILINE_STRING) {
+					result.push({
+						start: scanner.getStartOffset() + offset,
+						end: scanner.getOffset() + offset,
+						type: token
+					});
+				}
+				if (token === SINGLELINE_COMMENT || token === MULTILINE_COMMENT || token === DOC_COMMENT) {
+					//TODO can we avoid this work if edition does not overlap comment?
+					this._computeTasks(token, scanner.getStartOffset() + offset, scanner.getOffset() + offset);
+				}
+			}
+			return result;
+		}, 
+		_findMatchingBracket: function(model, offset) {
+			var brackets = BRACKETS;
+			var bracket = model.getText(offset, offset + 1);
+			var bracketIndex = brackets.indexOf(bracket, 0);
+			if (bracketIndex === -1) { return -1; }
+			var closingBracket;
+			if (bracketIndex & 1) {
+				closingBracket = brackets.substring(bracketIndex - 1, bracketIndex);
+			} else {
+				closingBracket = brackets.substring(bracketIndex + 1, bracketIndex + 2);
+			}
+			var lineIndex = model.getLineAtOffset(offset);
+			var lineText = model.getLine(lineIndex);
+			var lineStart = model.getLineStart(lineIndex);
+			var lineEnd = model.getLineEnd(lineIndex);
+			brackets = this._findBrackets(bracket, closingBracket, lineText, lineStart, lineStart, lineEnd);
+			for (var i=0; i<brackets.length; i++) {
+				var sign = brackets[i] >= 0 ? 1 : -1;
+				if (brackets[i] * sign - 1 === offset) {
+					var level = 1;
+					if (bracketIndex & 1) {
+						i--;
+						for (; i>=0; i--) {
+							sign = brackets[i] >= 0 ? 1 : -1;
+							level += sign;
+							if (level === 0) {
+								return brackets[i] * sign - 1;
+							}
+						}
+						lineIndex -= 1;
+						while (lineIndex >= 0) {
+							lineText = model.getLine(lineIndex);
+							lineStart = model.getLineStart(lineIndex);
+							lineEnd = model.getLineEnd(lineIndex);
+							brackets = this._findBrackets(bracket, closingBracket, lineText, lineStart, lineStart, lineEnd);
+							for (var j=brackets.length - 1; j>=0; j--) {
+								sign = brackets[j] >= 0 ? 1 : -1;
+								level += sign;
+								if (level === 0) {
+									return brackets[j] * sign - 1;
+								}
+							}
+							lineIndex--;
+						}
+					} else {
+						i++;
+						for (; i<brackets.length; i++) {
+							sign = brackets[i] >= 0 ? 1 : -1;
+							level += sign;
+							if (level === 0) {
+								return brackets[i] * sign - 1;
+							}
+						}
+						lineIndex += 1;
+						var lineCount = model.getLineCount ();
+						while (lineIndex < lineCount) {
+							lineText = model.getLine(lineIndex);
+							lineStart = model.getLineStart(lineIndex);
+							lineEnd = model.getLineEnd(lineIndex);
+							brackets = this._findBrackets(bracket, closingBracket, lineText, lineStart, lineStart, lineEnd);
+							for (var k=0; k<brackets.length; k++) {
+								sign = brackets[k] >= 0 ? 1 : -1;
+								level += sign;
+								if (level === 0) {
+									return brackets[k] * sign - 1;
+								}
+							}
+							lineIndex++;
+						}
+					}
+					break;
+				}
+			}
+			return -1;
+		},
+		_findBrackets: function(bracket, closingBracket, text, textOffset, start, end) {
+			var result = [];
+			var bracketToken = bracket.charCodeAt(0);
+			var closingBracketToken = closingBracket.charCodeAt(0);
+			// for any sub range that is not a comment, parse code generating tokens (keywords, numbers, brackets, line comments, etc)
+			var offset = start, scanner = this._scanner, token, comments = this.comments;
+			var startIndex = this._binarySearch(comments, start, true);
+			for (var i = startIndex; i < comments.length; i++) {
+				if (comments[i].start >= end) { break; }
+				var commentStart = comments[i].start;
+				var commentEnd = comments[i].end;
+				if (offset < commentStart) {
+					scanner.setText(text.substring(offset - start, commentStart - start));
+					while ((token = scanner.nextToken())) {
+						if (token === bracketToken) {
+							result.push(scanner.getStartOffset() + offset - start + textOffset + 1);
+						} else if (token === closingBracketToken) {
+							result.push(-(scanner.getStartOffset() + offset - start + textOffset + 1));
+						}
+					}
+				}
+				offset = commentEnd;
+			}
+			if (offset < end) {
+				scanner.setText(text.substring(offset - start, end - start));
+				while ((token = scanner.nextToken())) {
+					if (token === bracketToken) {
+						result.push(scanner.getStartOffset() + offset - start + textOffset + 1);
+					} else if (token === closingBracketToken) {
+						result.push(-(scanner.getStartOffset() + offset - start + textOffset + 1));
+					}
+				}
+			}
+			return result;
+		},
+		_onDestroy: function(e) {
+			this.destroy();
+		},
+		_onLineStyle: function (e) {
+			if (e.textView === this.view) {
+				e.style = this._getLineStyle(e.lineIndex);
+			}
+			e.ranges = this._getStyles(e.textView.getModel(), e.lineText, e.lineStart);
+		},
+		_onSelection: function(e) {
+			var oldSelection = e.oldValue;
+			var newSelection = e.newValue;
+			var view = this.view;
+			var model = view.getModel();
+			var lineIndex;
+			if (this.highlightCaretLine) {
+				var oldLineIndex = model.getLineAtOffset(oldSelection.start);
+				lineIndex = model.getLineAtOffset(newSelection.start);
+				var newEmpty = newSelection.start === newSelection.end;
+				var oldEmpty = oldSelection.start === oldSelection.end;
+				if (!(oldLineIndex === lineIndex && oldEmpty && newEmpty)) {
+					if (oldEmpty) {
+						view.redrawLines(oldLineIndex, oldLineIndex + 1);
+					}
+					if ((oldLineIndex !== lineIndex || !oldEmpty) && newEmpty) {
+						view.redrawLines(lineIndex, lineIndex + 1);
+					}
+				}
+			}
+			if (!this.annotationModel) { return; }
+			var remove = this._bracketAnnotations, add, caret;
+			if (newSelection.start === newSelection.end && (caret = view.getCaretOffset()) > 0) {
+				var mapCaret = caret - 1;
+				if (model.getBaseModel) {
+					mapCaret = model.mapOffset(mapCaret);
+					model = model.getBaseModel();
+				}
+				var bracket = this._findMatchingBracket(model, mapCaret);
+				if (bracket !== -1) {
+					add = [
+						mAnnotations.AnnotationType.createAnnotation(mAnnotations.AnnotationType.ANNOTATION_MATCHING_BRACKET, bracket, bracket + 1),
+						mAnnotations.AnnotationType.createAnnotation(mAnnotations.AnnotationType.ANNOTATION_CURRENT_BRACKET, mapCaret, mapCaret + 1)
+					];
+				}
+			}
+			this._bracketAnnotations = add;
+			this.annotationModel.replaceAnnotations(remove, add);
+		},
+		_onMouseDown: function(e) {
+			if (e.clickCount !== 2) { return; }
+			var view = this.view;
+			var model = view.getModel();
+			var offset = view.getOffsetAtLocation(e.x, e.y);
+			if (offset > 0) {
+				var mapOffset = offset - 1;
+				var baseModel = model;
+				if (model.getBaseModel) {
+					mapOffset = model.mapOffset(mapOffset);
+					baseModel = model.getBaseModel();
+				}
+				var bracket = this._findMatchingBracket(baseModel, mapOffset);
+				if (bracket !== -1) {
+					e.preventDefault();
+					var mapBracket = bracket;
+					if (model.getBaseModel) {
+						mapBracket = model.mapOffset(mapBracket, true);
+					}
+					if (offset > mapBracket) {
+						offset--;
+						mapBracket++;
+					}	
+					view.setSelection(mapBracket, offset);
+				}
+			}
+		},
+		_onModelChanged: function(e) {
+			var start = e.start;
+			var removedCharCount = e.removedCharCount;
+			var addedCharCount = e.addedCharCount;
+			var changeCount = addedCharCount - removedCharCount;
+			var view = this.view;
+			var viewModel = view.getModel();
+			var baseModel = viewModel.getBaseModel ? viewModel.getBaseModel() : viewModel;
+			var end = start + removedCharCount;
+			var charCount = baseModel.getCharCount();
+			var commentCount = this.comments.length;
+			var lineStart = baseModel.getLineStart(baseModel.getLineAtOffset(start));
+			var commentStart = this._binarySearch(this.comments, lineStart, true);
+			var commentEnd = this._binarySearch(this.comments, end, false, commentStart - 1, commentCount);
+			
+			var ts;
+			if (commentStart < commentCount && this.comments[commentStart].start <= lineStart && lineStart < this.comments[commentStart].end) {
+				ts = this.comments[commentStart].start;
+				if (ts > start) { ts += changeCount; }
+			} else {
+				if (commentStart === commentCount && commentCount > 0 && charCount - changeCount === this.comments[commentCount - 1].end) {
+					ts = this.comments[commentCount - 1].start;
+				} else {
+					ts = lineStart;
+				}
+			}
+			var te;
+			if (commentEnd < commentCount) {
+				te = this.comments[commentEnd].end;
+				if (te > start) { te += changeCount; }
+				commentEnd += 1;
+			} else {
+				commentEnd = commentCount;
+				te = charCount;//TODO could it be smaller?
+			}
+			var text = baseModel.getText(ts, te), comment;
+			var newComments = this._findComments(text, ts), i;
+			for (i = commentStart; i < this.comments.length; i++) {
+				comment = this.comments[i];
+				if (comment.start > start) { comment.start += changeCount; }
+				if (comment.start > start) { comment.end += changeCount; }
+			}
+			var redraw = (commentEnd - commentStart) !== newComments.length;
+			if (!redraw) {
+				for (i=0; i<newComments.length; i++) {
+					comment = this.comments[commentStart + i];
+					var newComment = newComments[i];
+					if (comment.start !== newComment.start || comment.end !== newComment.end || comment.type !== newComment.type) {
+						redraw = true;
+						break;
+					} 
+				}
+			}
+			var args = [commentStart, commentEnd - commentStart].concat(newComments);
+			Array.prototype.splice.apply(this.comments, args);
+			if (redraw) {
+				var redrawStart = ts;
+				var redrawEnd = te;
+				if (viewModel !== baseModel) {
+					redrawStart = viewModel.mapOffset(redrawStart, true);
+					redrawEnd = viewModel.mapOffset(redrawEnd, true);
+				}
+				view.redrawRange(redrawStart, redrawEnd);
+			}
+
+			if (this.foldingEnabled && baseModel !== viewModel && this.annotationModel) {
+				var annotationModel = this.annotationModel;
+				var iter = annotationModel.getAnnotations(ts, te);
+				var remove = [], all = [];
+				var annotation;
+				while (iter.hasNext()) {
+					annotation = iter.next();
+					if (annotation.type === mAnnotations.AnnotationType.ANNOTATION_FOLDING) {
+						all.push(annotation);
+						for (i = 0; i < newComments.length; i++) {
+							if (annotation.start === newComments[i].start && annotation.end === newComments[i].end) {
+								break;
+							}
+						}
+						if (i === newComments.length) {
+							remove.push(annotation);
+							annotation.expand();
+						} else {
+							var annotationStart = annotation.start;
+							var annotationEnd = annotation.end;
+							if (annotationStart > start) {
+								annotationStart -= changeCount;
+							}
+							if (annotationEnd > start) {
+								annotationEnd -= changeCount;
+							}
+							if (annotationStart <= start && start < annotationEnd && annotationStart <= end && end < annotationEnd) {
+								var startLine = baseModel.getLineAtOffset(annotation.start);
+								var endLine = baseModel.getLineAtOffset(annotation.end);
+								if (startLine !== endLine) {
+									if (!annotation.expanded) {
+										annotation.expand();
+										annotationModel.modifyAnnotation(annotation);
+									}
+								} else {
+									annotationModel.removeAnnotation(annotation);
+								}
+							}
+						}
+					}
+				}
+				var add = [];
+				for (i = 0; i < newComments.length; i++) {
+					comment = newComments[i];
+					for (var j = 0; j < all.length; j++) {
+						if (all[j].start === comment.start && all[j].end === comment.end) {
+							break;
+						}
+					}
+					if (j === all.length) {
+						annotation = this._createFoldingAnnotation(viewModel, baseModel, comment.start, comment.end);
+						if (annotation) {
+							add.push(annotation);
+						}
+					}
+				}
+				annotationModel.replaceAnnotations(remove, add);
+			}
+		}
+	};
+	
+	return {TextStyler: TextStyler};
+});
+
+/*******************************************************************************
+ * @license
+ * Copyright (c) 2013 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials are made 
+ * available under the terms of the Eclipse Public License v1.0 
+ * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
+ * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+ 
+/*globals define Node */
+
+define('orion/editor/edit', [ //$NON-NLS-0$
+	
+	"orion/editor/textView", //$NON-NLS-0$
+	"orion/editor/textModel", //$NON-NLS-0$
+	"orion/editor/textTheme", //$NON-NLS-0$
+	"orion/editor/projectionTextModel", //$NON-NLS-0$
+	"orion/editor/eventTarget", //$NON-NLS-0$
+	"orion/keyBinding", //$NON-NLS-0$
+	"orion/editor/rulers", //$NON-NLS-0$
+	"orion/editor/annotations", //$NON-NLS-0$
+	"orion/editor/tooltip", //$NON-NLS-0$
+	"orion/editor/undoStack", //$NON-NLS-0$
+	"orion/editor/textDND", //$NON-NLS-0$
+	
+	"orion/editor/editor", //$NON-NLS-0$
+	"orion/editor/editorFeatures", //$NON-NLS-0$
+	
+	"orion/editor/contentAssist", //$NON-NLS-0$
+	"orion/editor/cssContentAssist", //$NON-NLS-0$
+	"orion/editor/htmlContentAssist", //$NON-NLS-0$
+	"orion/editor/jsTemplateContentAssist", //$NON-NLS-0$
+	
+	"orion/editor/AsyncStyler", //$NON-NLS-0$
+	"orion/editor/mirror", //$NON-NLS-0$
+	"orion/editor/textMateStyler", //$NON-NLS-0$
+	"orion/editor/htmlGrammar", //$NON-NLS-0$
+	"examples/editor/textStyler" //$NON-NLS-0$
+], function(mTextView, mTextModel, mTextTheme, mProjModel, mEventTarget, mKeyBinding, mRulers, mAnnotations, mTooltip, mUndoStack, mTextDND, mEditor, mEditorFeatures, mContentAssist, mCSSContentAssist, mHtmlContentAssist, mJSContentAssist, mAsyncStyler, mMirror, mTextMateStyler, mHtmlGrammar, mTextStyler) {
+
+	/**	@private */
+	function getDisplay(window, document, element) {
+		var display;
+		var temp = element;
+		while (temp && temp !== document && display !== "none") { //$NON-NLS-0$
+			if (window.getComputedStyle) {
+				var style = window.getComputedStyle(temp, null);
+				display = style.getPropertyValue("display"); //$NON-NLS-0$
+			} else {
+				display = temp.currentStyle.display;
+			}
+			temp = temp.parentNode;
+		}
+		return display;
+	}
+
+	/**	@private */
+	function getTextFromElement(element) {
+		var firstChild = element.firstChild;
+		if (firstChild && firstChild.tagName === "TEXTAREA") { //$NON-NLS-0$
+			return firstChild.value;
+		}
+		var document = element.ownerDocument;
+		var window = document.defaultView || document.parentWindow;
+		if (!window.getSelection ||
+			(element.childNodes.length === 1 && firstChild.nodeType === Node.TEXT_NODE) ||
+			getDisplay(window, document, element) === "none") //$NON-NLS-0$
+		{
+			return element.innerText || element.textContent;
+		}
+		var newRange = document.createRange();
+		newRange.selectNode(element);
+		var selection = window.getSelection();
+		var oldRanges = [], i;
+		for (i = 0; i < selection.rangeCount; i++) {
+			oldRanges.push(selection.getRangeAt(i));
+		}
+		selection.removeAllRanges();
+		selection.addRange(newRange);
+		var text = selection.toString();
+		selection.removeAllRanges();
+		for (i = 0; i < oldRanges.length; i++) {
+			selection.addRange(oldRanges[i]);
+		}
+		return text;
+	}
+
+	/**	@private */	
+	function optionName(name) {
+		var prefix = "data-editor-"; //$NON-NLS-0$
+		if (name.substring(0, prefix.length) === prefix) {
+			var key = name.substring(prefix.length);
+			key = key.replace(/-([a-z])/ig, function(all, character) {
+				return character.toUpperCase();
+			});
+			return key;
+		}
+		return undefined;
+	}
+	
+	/**	@private */
+	function merge(obj1, obj2) {
+		for (var p in obj2) {
+			if (obj2.hasOwnProperty(p)) {
+				obj1[p] = obj2[p];
+			}
+		}
+	}
+	
+	/**	@private */
+	function mergeOptions(parent, defaultOptions) {
+		var options = {};
+		merge(options, defaultOptions);
+		for (var attr, j = 0, attrs = parent.attributes, l = attrs.length; j < l; j++) {
+			attr = attrs.item(j);
+			var key = optionName(attr.nodeName);
+			if (key) {
+				var value = attr.nodeValue;
+				if (value === "true" || value === "false") { //$NON-NLS-1$ //$NON-NLS-0$
+					value = value === "true"; //$NON-NLS-0$
+				}
+				options[key] = value;
+			}
+		}
+		return options;
+	}
+	
+	/**	@private */
+	function getHeight(node) {
+		var document = node.ownerDocument;
+		var window = document.defaultView || document.parentWindow;
+		var height;
+		if (window.getComputedStyle) {
+			var style = window.getComputedStyle(node, null);
+			height = style.getPropertyValue("height"); //$NON-NLS-0$
+		} else if (node.currentStyle) {
+			height = node.currentStyle.height;
+		}
+		return parseInt(height, 10) || 0;
+	}
+	
+	/**
+	 * @class This object describes the options for <code>edit</code>.
+	 * @name orion.editor.EditOptions
+	 *
+	 * @property {String|DOMElement} parent the parent element for the view, it can be either a DOM element or an ID for a DOM element.
+	 * @property {Boolean} [readonly=false] whether or not the view is read-only.
+	 * @property {Boolean} [fullSelection=true] whether or not the view is in full selection mode.
+	 * @property {Boolean} [tabMode=true] whether or not the tab keypress is consumed by the view or is used for focus traversal.
+	 * @property {Boolean} [expandTab=false] whether or not the tab key inserts white spaces.
+	 * @property {String} [themeClass] the CSS class for the view theming.
+	 * @property {Number} [tabSize=4] The number of spaces in a tab.
+	 * @property {Boolean} [wrapMode=false] whether or not the view wraps lines.
+	 * @property {Function} [statusReporter] a status reporter.
+	 * @property {String} [title=""] the editor title.
+	 * @property {String} [contents=""] the editor contents.
+	 * @property {String} [lang] the styler language. Plain text by default.
+	 * @property {Boolean} [showLinesRuler=true] whether or not the lines ruler is shown.
+	 * @property {Boolean} [showAnnotationRuler=true] whether or not the annotation ruler is shown.
+	 * @property {Boolean} [showOverviewRuler=true] whether or not the overview ruler is shown.
+	 * @property {Boolean} [showFoldingRuler=true] whether or not the folding ruler is shown.
+	 */
+	/**
+	 * Creates an editor instance configured with the given options.
+	 * 
+	 * @param {orion.editor.EditOptions} options the editor options.
+	 */
+	function edit(options) {
+		var parent = options.parent;
+		if (!parent) { parent = "editor"; } //$NON-NLS-0$
+		if (typeof(parent) === "string") { //$NON-NLS-0$
+			parent = (options.document || document).getElementById(parent);
+		}
+		if (!parent) {
+			if (options.className) {
+				var parents = (options.document || document).getElementsByClassName(options.className);
+				if (parents) {
+					options.className = undefined;
+					var editors = [];
+					for (var i = 0; i < parents.length; i++) {
+						options.parent = parents[i];
+						editors.push(edit(options));
+					}
+					return editors;
+				}
+			}
+		}
+		if (!parent) { throw "no parent"; } //$NON-NLS-0$
+		options = mergeOptions(parent, options);
+	
+		if (typeof options.theme === "string") { //$NON-NLS-0$
+			var theme = mTextTheme.TextTheme.getTheme(options.theme);
+			var index = options.theme.lastIndexOf("/"); //$NON-NLS-0$
+			var themeClass = options.theme; 
+			if (index !== -1) {
+				themeClass = themeClass.substring(index + 1);
+			}
+			var extension = ".css"; //$NON-NLS-0$
+			if (themeClass.substring(themeClass.length - extension.length) === extension) {
+				themeClass = themeClass.substring(0, themeClass.length - extension.length);
+			}
+			theme.setThemeClass(themeClass, {href: options.theme});
+			options.theme = theme;
+		}
+		var textViewFactory = function() {
+			return new mTextView.TextView({
+				parent: parent,
+				model: new mProjModel.ProjectionTextModel(new mTextModel.TextModel("")),
+				tabSize: options.tabSize ? options.tabSize : 4,
+				readonly: options.readonly,
+				fullSelection: options.fullSelection,
+				tabMode: options.tabMode,
+				expandTab: options.expandTab,
+				themeClass: options.themeClass,
+				theme: options.theme,
+				wrapMode: options.wrapMode
+			});
+		};
+
+		var contentAssist, contentAssistFactory;
+		if (!options.readonly) {
+			contentAssistFactory = {
+				createContentAssistMode: function(editor) {
+					contentAssist = new mContentAssist.ContentAssist(editor.getTextView());
+					var contentAssistWidget = new mContentAssist.ContentAssistWidget(contentAssist);
+					var result = new mContentAssist.ContentAssistMode(contentAssist, contentAssistWidget);
+					contentAssist.setMode(result);
+					return result;
+				}
+			};
+		}
+	
+		// Canned highlighters for js, java, and css. Grammar-based highlighter for html
+		var syntaxHighlighter = {
+			styler: null, 
+			
+			highlight: function(lang, editor) {
+				if (this.styler) {
+					this.styler.destroy();
+					this.styler = null;
+				}
+				if (lang) {
+					var textView = editor.getTextView();
+					var annotationModel = editor.getAnnotationModel();
+					switch(lang) {
+						case "js": //$NON-NLS-0$
+						case "java": //$NON-NLS-0$
+						case "css": //$NON-NLS-0$
+							this.styler = new mTextStyler.TextStyler(textView, lang, annotationModel);
+							editor.setFoldingRulerVisible(options.showFoldingRuler === undefined || options.showFoldingRuler);
+							break;
+						case "html": //$NON-NLS-0$
+							this.styler = new mTextMateStyler.TextMateStyler(textView, new mHtmlGrammar.HtmlGrammar());
+							break;
+					}
+				}
+			}
+		};
+		
+		var editor = new mEditor.Editor({
+			textViewFactory: textViewFactory,
+			undoStackFactory: new mEditorFeatures.UndoFactory(),
+			annotationFactory: new mEditorFeatures.AnnotationFactory(),
+			lineNumberRulerFactory: new mEditorFeatures.LineNumberRulerFactory(),
+			foldingRulerFactory: new mEditorFeatures.FoldingRulerFactory(),
+			textDNDFactory: new mEditorFeatures.TextDNDFactory(),
+			contentAssistFactory: contentAssistFactory,
+			keyBindingFactory: new mEditorFeatures.KeyBindingsFactory(), 
+			statusReporter: options.statusReporter,
+			domNode: parent
+		});
+		
+		var contents = options.contents;
+		if (contents === undefined) {
+			contents = getTextFromElement(parent); 
+		}
+		if (!contents) { contents=""; }
+		
+		editor.installTextView();
+		editor.setLineNumberRulerVisible(options.showLinesRuler === undefined || options.showLinesRuler);
+		editor.setAnnotationRulerVisible(options.showAnnotationRuler === undefined || options.showFoldingRuler);
+		editor.setOverviewRulerVisible(options.showOverviewRuler === undefined || options.showOverviewRuler);
+		editor.setFoldingRulerVisible(options.showFoldingRuler === undefined || options.showFoldingRuler);
+		editor.setInput(options.title, null, contents);
+		syntaxHighlighter.highlight(options.lang, editor);
+		if (contentAssist) {
+			var cssContentAssistProvider = new mCSSContentAssist.CssContentAssistProvider();
+			var jsTemplateContentAssistProvider = new mJSContentAssist.JSTemplateContentAssistProvider();
+			contentAssist.addEventListener("Activating", function() { //$NON-NLS-0$
+				if (/css$/.test(options.lang)) {
+					contentAssist.setProviders([cssContentAssistProvider]);
+				} else if (/js$/.test(options.lang)) {
+					contentAssist.setProviders([jsTemplateContentAssistProvider]);
+				}
+			});
+		}
+		/* The minimum height of the editor is 50px */
+		if (getHeight(parent) <= 50) {
+			var height = editor.getTextView().computeSize().height;
+			parent.style.height = height + "px"; //$NON-NLS-0$
+		}
+		return editor;
+	}
+
+	var editorNS = this.orion ? this.orion.editor : undefined;
+	if (editorNS) {
+		for (var i = 0; i < arguments.length; i++) {
+			merge(editorNS, arguments[i]);	
+		}
+	}
+	
+	return edit;
+});
+
+var orion = this.orion || (this.orion = {});
+var editor = orion.editor || (orion.editor = {});
+editor.edit = require('orion/editor/edit');
diff --git a/bundles/org.eclipse.e4.tools.orion.css.editor/web/built-editor.min.js b/bundles/org.eclipse.e4.tools.orion.css.editor/web/built-editor.min.js
deleted file mode 100644
index 983f953..0000000
--- a/bundles/org.eclipse.e4.tools.orion.css.editor/web/built-editor.min.js
+++ /dev/null
@@ -1,905 +0,0 @@
-/*
-
- Copyright (c) 2010, 2012 IBM Corporation and others.
- All rights reserved. This program and the accompanying materials are made 
- available under the terms of the Eclipse Public License v1.0 
- (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
- License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
-
- Contributors: IBM Corporation - initial API and implementation
-
- Copyright (c) 2012 IBM Corporation and others.
- All rights reserved. This program and the accompanying materials are made 
- available under the terms of the Eclipse Public License v1.0 
- (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
- License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
-
- Contributors: 
- Felipe Heidrich (IBM Corporation) - initial API and implementation
- Silenio Quarti (IBM Corporation) - initial API and implementation
-
- Copyright (c) 2010, 2012 IBM Corporation and others.
- All rights reserved. This program and the accompanying materials are made 
- available under the terms of the Eclipse Public License v1.0 
- (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
- License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
-
- Contributors: 
- Felipe Heidrich (IBM Corporation) - initial API and implementation
- Silenio Quarti (IBM Corporation) - initial API and implementation
-
- Copyright (c) 2012 IBM Corporation and others.
- All rights reserved. This program and the accompanying materials are made 
- available under the terms of the Eclipse Public License v1.0 
- (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
- License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
-
- Contributors: IBM Corporation - initial API and implementation
-
- Copyright (c) 2010, 2012 IBM Corporation and others.
- All rights reserved. This program and the accompanying materials are made 
- available under the terms of the Eclipse Public License v1.0 
- (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
- License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
-
- Contributors: 
- Felipe Heidrich (IBM Corporation) - initial API and implementation
- Silenio Quarti (IBM Corporation) - initial API and implementation
-
- Copyright (c) 2010, 2013 IBM Corporation and others.
- All rights reserved. This program and the accompanying materials are made 
- available under the terms of the Eclipse Public License v1.0 
- (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
- License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
-
- Contributors: 
- Felipe Heidrich (IBM Corporation) - initial API and implementation
- Silenio Quarti (IBM Corporation) - initial API and implementation
-
- Copyright (c) 2013 IBM Corporation and others.
- All rights reserved. This program and the accompanying materials are made 
- available under the terms of the Eclipse Public License v1.0 
- (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
- License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
-
- Contributors:
- IBM Corporation - initial API and implementation
-
- Copyright (c) 2013 IBM Corporation and others.
- All rights reserved. This program and the accompanying materials are made 
- available under the terms of the Eclipse Public License v1.0 
- (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
- License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
-
- Contributors:
- IBM Corporation - initial API and implementation
-
- Copyright (c) 2010, 2012 IBM Corporation and others.
- All rights reserved. This program and the accompanying materials are made 
- available under the terms of the Eclipse Public License v1.0 
- (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
- License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
-
- Contributors: 
- Felipe Heidrich (IBM Corporation) - initial API and implementation
- Silenio Quarti (IBM Corporation) - initial API and implementation
- Mihai Sucan (Mozilla Foundation) - fix for Bug#334583 Bug#348471 Bug#349485 Bug#350595 Bug#360726 Bug#361180 Bug#362835 Bug#362428 Bug#362286 Bug#354270 Bug#361474 Bug#363945 Bug#366312 Bug#370584
-
- Copyright (c) 2010, 2012 IBM Corporation and others.
- All rights reserved. This program and the accompanying materials are made 
- available under the terms of the Eclipse Public License v1.0 
- (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
- License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
-
- Contributors: 
- Felipe Heidrich (IBM Corporation) - initial API and implementation
- Silenio Quarti (IBM Corporation) - initial API and implementation
-
- Copyright (c) 2010, 2012 IBM Corporation and others.
- All rights reserved. This program and the accompanying materials are made 
- available under the terms of the Eclipse Public License v1.0 
- (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
- License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
-
- Contributors: 
- Felipe Heidrich (IBM Corporation) - initial API and implementation
- Silenio Quarti (IBM Corporation) - initial API and implementation
-
- Copyright (c) 2010, 2012 IBM Corporation and others.
- All rights reserved. This program and the accompanying materials are made 
- available under the terms of the Eclipse Public License v1.0 
- (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
- License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
-
- Contributors: IBM Corporation - initial API and implementation
-
- Copyright (c) 2010, 2012 IBM Corporation and others.
- All rights reserved. This program and the accompanying materials are made 
- available under the terms of the Eclipse Public License v1.0 
- (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
- License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
-
- Contributors: IBM Corporation - initial API and implementation
-
- Copyright (c) 2010, 2012 IBM Corporation and others.
- All rights reserved. This program and the accompanying materials are made 
- available under the terms of the Eclipse Public License v1.0 
- (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
- License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
-
- Contributors: IBM Corporation - initial API and implementation
-
- Copyright (c) 2010, 2012 IBM Corporation and others.
- All rights reserved. This program and the accompanying materials are made 
- available under the terms of the Eclipse Public License v1.0 
- (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
- License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
-
- Contributors: 
- Felipe Heidrich (IBM Corporation) - initial API and implementation
- Silenio Quarti (IBM Corporation) - initial API and implementation
-
- Copyright (c) 2009, 2012 IBM Corporation and others.
- All rights reserved. This program and the accompanying materials are made 
- available under the terms of the Eclipse Public License v1.0 
- (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
- License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
-
- Contributors: IBM Corporation - initial API and implementation
-
- Copyright (c) 2013 IBM Corporation and others.
- All rights reserved. This program and the accompanying materials are made 
- available under the terms of the Eclipse Public License v1.0 
- (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
- License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
-
- Contributors: IBM Corporation - initial API and implementation
-
- Copyright (c) 2013 IBM Corporation and others.
- All rights reserved. This program and the accompanying materials are made 
- available under the terms of the Eclipse Public License v1.0 
- (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
- License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
-
- Contributors:
- IBM Corporation - initial API and implementation
-
- Copyright (c) 2013 IBM Corporation and others.
- All rights reserved. This program and the accompanying materials are made 
- available under the terms of the Eclipse Public License v1.0 
- (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
- License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
-
- Contributors:
- IBM Corporation - initial API and implementation
-
- Copyright (c) 2010, 2012 IBM Corporation and others.
- All rights reserved. This program and the accompanying materials are made 
- available under the terms of the Eclipse Public License v1.0 
- (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
- License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
-
- Contributors: IBM Corporation - initial API and implementation
-
- Copyright (c) 2013 IBM Corporation and others.
- All rights reserved. This program and the accompanying materials are made 
- available under the terms of the Eclipse Public License v1.0 
- (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
- License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
-
- Contributors:
- IBM Corporation - initial API and implementation
-
- Copyright (c) 2013 IBM Corporation and others.
- All rights reserved. This program and the accompanying materials are made 
- available under the terms of the Eclipse Public License v1.0 
- (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
- License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
-
- Contributors:
- IBM Corporation - initial API and implementation
-
- Copyright (c) 2011, 2013 IBM Corporation and others.
- All rights reserved. This program and the accompanying materials are made 
- available under the terms of the Eclipse Public License v1.0 
- (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
- License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
-
- Contributors:
- IBM Corporation - initial API and implementation
-
- Copyright (c) 2012 IBM Corporation and others.
- All rights reserved. This program and the accompanying materials are made 
- available under the terms of the Eclipse Public License v1.0 
- (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
- License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
-
- Contributors: IBM Corporation - initial API and implementation
-
- Copyright (c) 2011, 2012 IBM Corporation and others.
- All rights reserved. This program and the accompanying materials are made 
- available under the terms of the Eclipse Public License v1.0 
- (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
- License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
-
- Contributors:
- IBM Corporation - initial API and implementation
-
- Copyright (c) 2010, 2012 IBM Corporation and others.
- All rights reserved. This program and the accompanying materials are made 
- available under the terms of the Eclipse Public License v1.0 
- (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
- License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
-
- Contributors: IBM Corporation - initial API and implementation
-
- Copyright (c) 2011, 2012 IBM Corporation and others.
- All rights reserved. This program and the accompanying materials are made 
- available under the terms of the Eclipse Public License v1.0 
- (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
- License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
-
- Contributors:
- IBM Corporation - initial API and implementation
-
- Copyright (c) 2011, 2012 IBM Corporation and others.
- All rights reserved. This program and the accompanying materials are made 
- available under the terms of the Eclipse Public License v1.0 
- (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
- License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
-
- Contributors:
- IBM Corporation - initial API and implementation
-
- Copyright (c) 2011, 2012 IBM Corporation and others.
- Copyright (c) 2012 VMware, Inc.
- All rights reserved. This program and the accompanying materials are made 
- available under the terms of the Eclipse Public License v1.0 
- (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
- License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
-
- Contributors:
- IBM Corporation - initial API and implementation
- Andrew Eisenberg - rename to jsTemplateContentAssist.js
-
- Copyright (c) 2011, 2012 IBM Corporation and others.
- All rights reserved. This program and the accompanying materials are made 
- available under the terms of the Eclipse Public License v1.0 
- (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
- License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
-
- Contributors: IBM Corporation - initial API and implementation 
-
- Copyright (c) 2011, 2012 IBM Corporation and others.
- All rights reserved. This program and the accompanying materials are made 
- available under the terms of the Eclipse Public License v1.0 
- (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
- License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
-
- Contributors:
- IBM Corporation - initial API and implementation
-
- Copyright (c) 2011, 2012 IBM Corporation and others.
- All rights reserved. This program and the accompanying materials are made 
- available under the terms of the Eclipse Public License v1.0 
- (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
- License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
-
- Contributors: IBM Corporation - initial API and implementation 
-
- Copyright (c) 2011, 2012 IBM Corporation and others.
- All rights reserved. This program and the accompanying materials are made 
- available under the terms of the Eclipse Public License v1.0 
- (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
- License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
-
- Contributors: IBM Corporation - initial API and implementation 
-
- Copyright (c) 2010, 2012 IBM Corporation and others.
- All rights reserved. This program and the accompanying materials are made 
- available under the terms of the Eclipse Public License v1.0 
- (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
- License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
-
- Contributors: IBM Corporation - initial API and implementation
- Alex Lakatos - fix for bug#369781
-
- Copyright (c) 2013 IBM Corporation and others.
- All rights reserved. This program and the accompanying materials are made 
- available under the terms of the Eclipse Public License v1.0 
- (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
- License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
-
- Contributors:
- IBM Corporation - initial API and implementation
- RequireJS i18n 2.0.2 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved.
- Available via the MIT or new BSD license.
- see: http://github.com/requirejs/i18n for details
-
- Copyright (c) 2011 IBM Corporation and others.
- All rights reserved. This program and the accompanying materials are made 
- available under the terms of the Eclipse Public License v1.0 
- (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
- License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
-
- Contributors:
- IBM Corporation - initial API and implementation
-*/
-var requirejs,require,define;
-(function(k){function p(a,g){var t,c,s,d,i,b,f,m,n,h=g&&g.split("/"),e=l.map,j=e&&e["*"]||{};if(a&&a.charAt(0)===".")if(g){h=h.slice(0,h.length-1);a=h.concat(a.split("/"));for(m=0;m<a.length;m+=1)if(t=a[m],t===".")a.splice(m,1),m-=1;else if(t==="..")if(m===1&&(a[2]===".."||a[0]===".."))break;else m>0&&(a.splice(m-1,2),m-=2);a=a.join("/")}else a.indexOf("./")===0&&(a=a.substring(2));if((h||j)&&e){t=a.split("/");for(m=t.length;m>0;m-=1){c=t.slice(0,m).join("/");if(h)for(n=h.length;n>0;n-=1)if(s=e[h.slice(0,
-n).join("/")])if(s=s[c]){d=s;i=m;break}if(d)break;!b&&j&&j[c]&&(b=j[c],f=m)}!d&&b&&(d=b,i=f);d&&(t.splice(0,i,d),a=t.join("/"))}return a}function o(c,g){return function(){return a.apply(k,v.call(arguments,0).concat([c,g]))}}function e(a){return function(g){return p(g,a)}}function b(a){return function(g){m[a]=g}}function d(a){if(q.call(n,a)){var g=n[a];delete n[a];r[a]=!0;f.apply(k,g)}if(!q.call(m,a)&&!q.call(r,a))throw Error("No "+a);return m[a]}function h(a){var g,t=a?a.indexOf("!"):-1;t>-1&&(g=
-a.substring(0,t),a=a.substring(t+1,a.length));return[g,a]}function j(a){return function(){return l&&l.config&&l.config[a]||{}}}var f,a,c,i,m={},n={},l={},r={},q=Object.prototype.hasOwnProperty,v=[].slice;c=function(a,g){var t,c=h(a),s=c[0],a=c[1];s&&(s=p(s,g),t=d(s));s?a=t&&t.normalize?t.normalize(a,e(g)):p(a,g):(a=p(a,g),c=h(a),s=c[0],a=c[1],s&&(t=d(s)));return{f:s?s+"!"+a:a,n:a,pr:s,p:t}};i={require:function(a){return o(a)},exports:function(a){var g=m[a];return typeof g!=="undefined"?g:m[a]={}},
-module:function(a){return{id:a,uri:"",exports:m[a],config:j(a)}}};f=function(a,g,t,x){var s,f,h,l,e=[],j,x=x||a;if(typeof t==="function"){g=!g.length&&t.length?["require","exports","module"]:g;for(l=0;l<g.length;l+=1)if(h=c(g[l],x),f=h.f,f==="require")e[l]=i.require(a);else if(f==="exports")e[l]=i.exports(a),j=!0;else if(f==="module")s=e[l]=i.module(a);else if(q.call(m,f)||q.call(n,f)||q.call(r,f))e[l]=d(f);else if(h.p)h.p.load(h.n,o(x,!0),b(f),{}),e[l]=m[f];else throw Error(a+" missing "+f);g=t.apply(m[a],
-e);if(a)if(s&&s.exports!==k&&s.exports!==m[a])m[a]=s.exports;else if(g!==k||!j)m[a]=g}else a&&(m[a]=t)};requirejs=require=a=function(b,g,t,x,s){if(typeof b==="string")return i[b]?i[b](g):d(c(b,g).f);else b.splice||(l=b,g.splice?(b=g,g=t,t=null):b=k);g=g||function(){};typeof t==="function"&&(t=x,x=s);x?f(k,b,g,t):setTimeout(function(){f(k,b,g,t)},4);return a};a.config=function(c){l=c;return a};define=function(a,g,t){g.splice||(t=g,g=[]);!q.call(m,a)&&!q.call(n,a)&&(n[a]=[a,g,t])};define.amd={jQuery:!0}})();
-define("almond",function(){});
-(function(){function k(b,d,h,e,f,a){d[b]&&(h.push(b),(d[b]===!0||d[b]===1)&&e.push(f+b+"/"+a))}function p(b,d,h,e,f){d=e+d+"/"+f;require._fileExists(b.toUrl(d+".js"))&&h.push(d)}function o(b,d,h){for(var e in d)d.hasOwnProperty(e)&&(!b.hasOwnProperty(e)||h)?b[e]=d[e]:typeof d[e]==="object"&&o(b[e],d[e],h)}var e=/(^.*(^|\/)nls(\/|$))([^\/]*)\/?([^\/]*)/;define("i18n",["module"],function(b){var d=b.config?b.config():{};return{version:"2.0.1+",load:function(b,j,f,a){a=a||{};if(a.locale)d.locale=a.locale;
-var c=e.exec(b),i=c[1],m=c[4],n=c[5],l=m.split("-"),r=[],q={},v,u="";if(c[5])i=c[1],b=i+n;else{n=c[4];m=d.locale;if(!m)m=d.locale=typeof navigator==="undefined"?"root":(navigator.language||navigator.userLanguage||"root").toLowerCase();l=m.split("-")}if(a.isBuild){r.push(b);p(j,"root",r,i,n);for(v=0;v<l.length;v++)a=l[v],u+=(u?"-":"")+a,p(j,u,r,i,n);j(r,function(){f()})}else j([b],function(g){var a=[],c;k("root",g,a,r,i,n);for(v=0;v<l.length;v++)c=l[v],u+=(u?"-":"")+c,k(u,g,a,r,i,n);j(r,function(){var c,
-d,x;for(c=a.length-1;c>-1&&a[c];c--){x=a[c];d=g[x];if(d===!0||d===1)d=j(i+x+"/"+n);o(q,d)}f(q)})})}}})})();define("orion/editor/i18n",{load:function(k,p,o){p.specified&&p.specified("orion/bootstrap")?p(["orion/i18n!"+k],function(e){o(e)}):o({})}});
-define("orion/editor/nls/root/messages",{multipleAnnotations:"Multiple annotations:",line:"Line: ${0}",breakpoint:"Breakpoint",bookmark:"Bookmark",task:"Task",error:"Error",warning:"Warning",matchingSearch:"Matching Search",currentSearch:"Current Search",currentLine:"Current Line",matchingBracket:"Matching Bracket",currentBracket:"Current Bracket",lineUp:"Line Up",lineDown:"Line Down",lineStart:"Line Start",lineEnd:"Line End",charPrevious:"Previous Character",charNext:"Next Character",pageUp:"Page Up",
-pageDown:"Page Down",scrollPageUp:"Scroll Page Up",scrollPageDown:"Scroll Page Down",scrollLineUp:"Scroll Line Up",scrollLineDown:"Scroll Line Down",wordPrevious:"Previous Word",wordNext:"Next Word",textStart:"Document Start",textEnd:"Document End",scrollTextStart:"Scroll Document Start",scrollTextEnd:"Scroll Document End",centerLine:"Center Line",selectLineUp:"Select Line Up",selectLineDown:"Select Line Down",selectWholeLineUp:" Select Whole Line Up",selectWholeLineDown:"Select Whole Line Down",
-selectLineStart:"Select Line Start",selectLineEnd:"Select Line End",selectCharPrevious:"Select Previous Character",selectCharNext:"Select Next Character",selectPageUp:"Select Page Up",selectPageDown:"Select Page Down",selectWordPrevious:"Select Previous Word",selectWordNext:"Select Next Word",selectTextStart:"Select Document Start",selectTextEnd:"Select Document End",deletePrevious:"Delete Previous Character",deleteNext:"Delete Next Character",deleteWordPrevious:"Delete Previous Word",deleteWordNext:"Delete Next Word",
-deleteLineStart:"Delete Line Start",deleteLineEnd:"Delete Line End",tab:"Insert Tab",enter:"Insert Line Delimiter",enterNoCursor:"Insert Line Delimiter",selectAll:"Select All",copy:"Copy",cut:"Cut",paste:"Paste",toggleWrapMode:"Toggle Wrap Mode",toggleTabMode:"Toggle Tab Mode",toggleOverwriteMode:"Toggle Overwrite Mode",emacs:"Emacs",exchangeMarkPoint:"Exchange Mark and Point",setMarkCommand:"Set Mark",clearMark:"Clear Mark",digitArgument:"Digit Argument ${0}",negativeArgument:"Negative Argument",
-Comment:"Comment","Flat outline":"Flat outline",incrementalFindStr:"Incremental find: ${0}",incrementalFindStrNotFound:"Incremental find: ${0} (not found)",incrementalFindReverseStr:"Reverse Incremental find: ${0}",incrementalFindReverseStrNotFound:"Reverse Incremental find: ${0} (not found)",find:"Find...",undo:"Undo",redo:"Redo",cancelMode:"Cancel Current Mode",findNext:"Find Next Occurrence",findPrevious:"Find Previous Occurrence",incrementalFind:"Incremental Find",incrementalFindReverse:"Incremental Find Reverse",
-indentLines:"Indent Lines",unindentLines:"Unindent Lines",moveLinesUp:"Move Lines Up",moveLinesDown:"Move Lines Down",copyLinesUp:"Copy Lines Up",copyLinesDown:"Copy Lines Down",deleteLines:"Delete Lines",gotoLine:"Goto Line...",gotoLinePrompty:"Goto Line:",nextAnnotation:"Next Annotation",prevAnnotation:"Previous Annotation",expand:"Expand",collapse:"Collapse",expandAll:"Expand All",collapseAll:"Collapse All",lastEdit:"Last Edit Location",toggleLineComment:"Toggle Line Comment",addBlockComment:"Add Block Comment",
-removeBlockComment:"Remove Block Comment",linkedModeEntered:"Linked Mode entered",linkedModeExited:"Linked Mode exited",syntaxError:"Syntax Error",contentAssist:"Content Assist",lineColumn:"Line ${0} : Col ${1}",replaceAll:"Replacing all...",replacedMatches:"Replaced ${0} matches",nothingReplaced:"Nothing replaced",notFound:"Not found"});
-define("orion/editor/nls/messages",["orion/editor/i18n!orion/editor/nls/messages","orion/editor/nls/root/messages"],function(k,p){var o={root:p},e;for(e in k)k.hasOwnProperty(e)&&typeof o[e]==="undefined"&&(o[e]=k[e]);return o});
-define("orion/editor/eventTarget",[],function(){function k(){}k.addMixin=function(p){var o=k.prototype,e;for(e in o)o.hasOwnProperty(e)&&(p[e]=o[e])};k.prototype={addEventListener:function(k,o,e){if(!this._eventTypes)this._eventTypes={};var b=this._eventTypes[k];b||(b=this._eventTypes[k]={level:0,listeners:[]});b.listeners.push({listener:o,useCapture:e})},dispatchEvent:function(k){var o=k.type;this._dispatchEvent("pre"+o,k);this._dispatchEvent(o,k);this._dispatchEvent("post"+o,k)},_dispatchEvent:function(k,
-o){var e=this._eventTypes?this._eventTypes[k]:null;if(e){var b=e.listeners;try{if(e.level++,b)for(var d=0,h=b.length;d<h;d++)if(b[d]){var j=b[d].listener;typeof j==="function"?j.call(this,o):j.handleEvent&&typeof j.handleEvent==="function"&&j.handleEvent(o)}}finally{if(e.level--,e.compact&&e.level===0){for(d=b.length-1;d>=0;d--)b[d]||b.splice(d,1);b.length===0&&delete this._eventTypes[k];e.compact=!1}}}},isListening:function(k){return!this._eventTypes?!1:this._eventTypes[k]!==void 0},removeEventListener:function(k,
-o,e){if(this._eventTypes){var b=this._eventTypes[k];if(b){for(var d=b.listeners,h=0,j=d.length;h<j;h++){var f=d[h];if(f&&f.listener===o&&f.useCapture===e){b.level!==0?(d[h]=null,b.compact=!0):d.splice(h,1);break}}d.length===0&&delete this._eventTypes[k]}}}};return{EventTarget:k}});
-define("orion/util",[],function(){var k=navigator.userAgent,p=parseFloat(k.split("MSIE")[1])||void 0,o=parseFloat(k.split("Firefox/")[1]||k.split("Minefield/")[1])||void 0,e=k.indexOf("Opera")!==-1,b=parseFloat(k.split("Chrome/")[1])||void 0,d=k.indexOf("Safari")!==-1&&!b,h=parseFloat(k.split("WebKit/")[1])||void 0,j=k.indexOf("Android")!==-1,f=k.indexOf("iPad")!==-1,k=k.indexOf("iPhone")!==-1,a=f||k,c=navigator.platform.indexOf("Mac")!==-1,i=navigator.platform.indexOf("Win")!==-1,m=navigator.platform.indexOf("Linux")!==
--1;return{formatMessage:function(a){var c=arguments;return a.replace(/\$\{([^\}]+)\}/g,function(a,d){return c[(d<<0)+1]})},createElement:function(a,c){return a.createElementNS?a.createElementNS("http://www.w3.org/1999/xhtml",c):a.createElement(c)},isIE:p,isFirefox:o,isOpera:e,isChrome:b,isSafari:d,isWebkit:h,isAndroid:j,isIPad:f,isIPhone:k,isIOS:a,isMac:c,isWindows:i,isLinux:m,platformDelimiter:i?"\r\n":"\n"}});
-define("orion/editor/textModel",["orion/editor/eventTarget","orion/util"],function(k,p){function o(e,b){this._lastLineIndex=-1;this._text=[""];this._lineOffsets=[0];this.setText(e);this.setLineDelimiter(b)}o.prototype={find:function(e){if(this._text.length>1)this._text=[this._text.join("")];var b=e.string,d=b,h=e.caseInsensitive;!e.regex&&b&&(d=b.replace(/([\\$\^*\/+?\.\(\)|{}\[\]])/g,"\\$&"),h&&(d=d.replace(/[iI\u0130\u0131]/g,"[Ii\u0130\u0131]")));var j=null,f;if(d){var b=e.reverse,a=e.wrap,c=e.wholeWord,
-i=e.start||0,e=e.end,m=e!==null&&e!==void 0,n="";n.indexOf("g")===-1&&(n+="g");h&&n.indexOf("i")===-1&&(n+="i");c&&(d="\\b"+d+"\\b");var l=this._text[0],r,q,k=0;m&&(h=i<e?i:e,l=l.substring(h,i<e?e:i),k=h);var o=RegExp(d,n);if(b)f=function(){var g=null;for(o.lastIndex=0;;){q=o.lastIndex;r=o.exec(l);if(q===o.lastIndex)return null;if(r){if(!(r.index+k<i)){if(!a||g)break;i=l.length+k}g={start:r.index+k,end:o.lastIndex+k}}else break}if(g)i=g.start;return g};else{if(!m)o.lastIndex=i;f=function(){for(;;){q=
-o.lastIndex;r=o.exec(l);if(q===o.lastIndex)break;if(r)return{start:r.index+k,end:o.lastIndex+k};if(!(q!==0&&a))break}return null}}j=f()}return{next:function(){var g=j;g&&(j=f());return g},hasNext:function(){return j!==null}}},getCharCount:function(){for(var e=0,b=0;b<this._text.length;b++)e+=this._text[b].length;return e},getLine:function(e,b){var d=this.getLineCount();if(!(0<=e&&e<d))return null;var h=this._lineOffsets[e];if(e+1<d){d=this.getText(h,this._lineOffsets[e+1]);if(b)return d;for(var h=
-d.length,j;(j=d.charCodeAt(h-1))===10||j===13;)h--;return d.substring(0,h)}else return this.getText(h)},getLineAtOffset:function(e){var b=this.getCharCount();if(!(0<=e&&e<=b))return-1;var d=this.getLineCount();if(e===b)return d-1;var h,j,f=this._lastLineIndex;if(0<=f&&f<d&&(h=this._lineOffsets[f],j=f+1<d?this._lineOffsets[f+1]:b,h<=e&&e<j))return f;for(var a=d,c=-1;a-c>1;)if(f=Math.floor((a+c)/2),h=this._lineOffsets[f],j=f+1<d?this._lineOffsets[f+1]:b,e<=h)a=f;else if(e<j){a=f;break}else c=f;return this._lastLineIndex=
-a},getLineCount:function(){return this._lineOffsets.length},getLineDelimiter:function(){return this._lineDelimiter},getLineEnd:function(e,b){var d=this.getLineCount();if(!(0<=e&&e<d))return-1;if(e+1<d){d=this._lineOffsets[e+1];if(b)return d;for(var h=this.getText(Math.max(this._lineOffsets[e],d-2),d),j=h.length,f;(f=h.charCodeAt(j-1))===10||f===13;)j--;return d-(h.length-j)}else return this.getCharCount()},getLineStart:function(e){return!(0<=e&&e<this.getLineCount())?-1:this._lineOffsets[e]},getText:function(e,
-b){e===void 0&&(e=0);b===void 0&&(b=this.getCharCount());if(e===b)return"";for(var d=0,h=0,j;h<this._text.length;){j=this._text[h].length;if(e<=d+j)break;d+=j;h++}for(var f=d,a=h;h<this._text.length;){j=this._text[h].length;if(b<=d+j)break;d+=j;h++}if(a===h)return this._text[a].substring(e-f,b-d);f=this._text[a].substring(e-f);d=this._text[h].substring(0,b-d);return f+this._text.slice(a+1,h).join("")+d},onChanging:function(e){return this.dispatchEvent(e)},onChanged:function(e){return this.dispatchEvent(e)},
-setLineDelimiter:function(e,b){e==="auto"&&(e=void 0,this.getLineCount()>1&&(e=this.getText(this.getLineEnd(0),this.getLineEnd(0,!0))));this._lineDelimiter=e?e:p.platformDelimiter;if(b){var d=this.getLineCount();if(d>1){for(var h=Array(d),j=0;j<d;j++)h[j]=this.getLine(j);this.setText(h.join(this._lineDelimiter))}}},setText:function(e,b,d){e===void 0&&(e="");b===void 0&&(b=0);d===void 0&&(d=this.getCharCount());if(!(b===d&&e==="")){for(var h=this.getLineAtOffset(b),j=this.getLineAtOffset(d),f=b,a=
-d-b,c=j-h,i=e.length,m=0,n=this.getLineCount(),l=0,r=0,q=0,k=[];;){l!==-1&&l<=q&&(l=e.indexOf("\r",q));r!==-1&&r<=q&&(r=e.indexOf("\n",q));if(r===-1&&l===-1)break;q=l!==-1&&r!==-1?l+1===r?r+1:(l<r?l:r)+1:l!==-1?l+1:r+1;k.push(b+q);m++}this.onChanging({type:"Changing",text:e,start:f,removedCharCount:a,addedCharCount:i,removedLineCount:c,addedLineCount:m});k.length===0&&(q=this.getLineStart(h),j=j+1<n?this.getLineStart(j+1):this.getCharCount(),b!==q&&(e=this.getText(q,b)+e,b=q),d!==j&&(e+=this.getText(d,
-j),d=j));q=i-a;for(j=h+c+1;j<n;j++)this._lineOffsets[j]+=q;if(k.length<5E4)h=[h+1,c].concat(k),Array.prototype.splice.apply(this._lineOffsets,h);else{q=h+1;this._lineOffsets.splice(q,c);for(n=0;n<k.length;n+=5E4)h=[q,0].concat(k.slice(n,Math.min(k.length,n+5E4))),Array.prototype.splice.apply(this._lineOffsets,h),q+=5E4}for(q=n=0;q<this._text.length;){j=this._text[q].length;if(b<=n+j)break;n+=j;q++}h=n;for(k=q;q<this._text.length;){j=this._text[q].length;if(d<=n+j)break;n+=j;q++}j=this._text[q];b=
-this._text[k].substring(0,b-h);d=j.substring(d-n);k=[k,q-k+1];b&&k.push(b);e&&k.push(e);d&&k.push(d);Array.prototype.splice.apply(this._text,k);if(this._text.length===0)this._text=[""];this.onChanged({type:"Changed",start:f,removedCharCount:a,addedCharCount:i,removedLineCount:c,addedLineCount:m})}}};k.EventTarget.addMixin(o.prototype);return{TextModel:o}});
-define("orion/keyBinding",["orion/util"],function(k){function p(e,b,d,h,j,f){this.type=f||"keydown";this.keyCode=typeof e==="string"&&this.type==="keydown"?e.toUpperCase().charCodeAt(0):e;this.mod1=b!==void 0&&b!==null?b:!1;this.mod2=d!==void 0&&d!==null?d:!1;this.mod3=h!==void 0&&h!==null?h:!1;this.mod4=j!==void 0&&j!==null?j:!1}function o(e){this.keys=e}p.prototype={getKeys:function(){return[this]},match:function(e,b){if(b!==void 0){if(b!==0)return!1}else if(e instanceof Array){if(e.length>1)return!1;
-e=e[0]}return e.type!==this.type?!1:this.keyCode===e.keyCode||this.keyCode===String.fromCharCode(k.isOpera?e.which:e.charCode!==void 0?e.charCode:e.keyCode)?this.mod1!==(k.isMac?e.metaKey:e.ctrlKey)?!1:this.type==="keydown"&&this.mod2!==e.shiftKey?!1:this.mod3!==e.altKey?!1:k.isMac&&this.mod4!==e.ctrlKey?!1:!0:!1},equals:function(e){return!e?!1:this.keyCode!==e.keyCode?!1:this.mod1!==e.mod1?!1:this.mod2!==e.mod2?!1:this.mod3!==e.mod3?!1:this.mod4!==e.mod4?!1:this.type!==e.type?!1:!0}};o.prototype=
-{getKeys:function(){return this.keys.slice(0)},match:function(e,b){var d=this.keys;if(b!==void 0)return b>d.length?!1:d[b].match(e)?b===d.length-1?!0:b+1:!1;else{e instanceof Array||(e=[e]);if(e.length>d.length)return!1;var h;for(h=0;h<e.length;h++)if(!d[h].match(e[h]))return!1;return h===d.length?!0:h}},equals:function(e){if(!e.keys)return!1;if(e.keys.length!==this.keys.length)return!1;for(var b=0;b<e.keys.length;b++)if(!e.keys[b].equals(this.keys[b]))return!1;return!0}};return{KeyBinding:p,KeyStroke:p,
-KeySequence:o}});
-define("orion/editor/keyModes",["orion/keyBinding","orion/util"],function(k,p){function o(b){this._view=b;this._keyBindings=this.createKeyBindings();this._keyBindingIndex=0}function e(){o.call(this)}o.prototype={createKeyBindings:function(){return[]},getKeyBindings:function(b){for(var d=[],h=this._keyBindings,e=0;e<h.length;e++)h[e].actionID===b&&d.push(h[e].keyBinding);return d},getView:function(){return this._view},isActive:function(){return!0},match:function(b){switch(b.keyCode){case 16:case 17:case 18:case 91:return}for(var d=this._keyBindingIndex,
-h=this._matchingKeyBindings||this._keyBindings,e=[],f=0;f<h.length;f++){var a=h[f],c=a.keyBinding.match(b,d);if(c===!0)return this._keyBindingIndex=0,this._matchingKeyBindings=null,a.actionID;else typeof c==="number"&&e.push(a)}if(e.length===0)this._keyBindingIndex=0,this._matchingKeyBindings=null;else return this._keyBindingIndex++,this._matchingKeyBindings=e,"noop"},setKeyBinding:function(b,d){for(var h=this._keyBindings,e=0;e<h.length;e++){var f=h[e];if(f.keyBinding.equals(b)){d?f.actionID=d:f.predefined?
-f.actionID="noop":h.splice(e,1);return}}d&&h.push({keyBinding:b,actionID:d})},setView:function(b){this._view=b}};e.prototype=new o;e.prototype.createKeyBindings=function(){var b=k.KeyBinding,d=[];d.push({actionID:"lineUp",keyBinding:new b(38),predefined:!0});d.push({actionID:"lineDown",keyBinding:new b(40),predefined:!0});d.push({actionID:"charPrevious",keyBinding:new b(37),predefined:!0});d.push({actionID:"charNext",keyBinding:new b(39),predefined:!0});p.isMac?(d.push({actionID:"scrollPageUp",keyBinding:new b(33),
-predefined:!0}),d.push({actionID:"scrollPageDown",keyBinding:new b(34),predefined:!0}),d.push({actionID:"pageUp",keyBinding:new b(33,null,null,!0),predefined:!0}),d.push({actionID:"pageDown",keyBinding:new b(34,null,null,!0),predefined:!0}),d.push({actionID:"lineStart",keyBinding:new b(37,!0),predefined:!0}),d.push({actionID:"lineEnd",keyBinding:new b(39,!0),predefined:!0}),d.push({actionID:"wordPrevious",keyBinding:new b(37,null,null,!0),predefined:!0}),d.push({actionID:"wordNext",keyBinding:new b(39,
-null,null,!0),predefined:!0}),d.push({actionID:"scrollTextStart",keyBinding:new b(36),predefined:!0}),d.push({actionID:"scrollTextEnd",keyBinding:new b(35),predefined:!0}),d.push({actionID:"textStart",keyBinding:new b(38,!0),predefined:!0}),d.push({actionID:"textEnd",keyBinding:new b(40,!0),predefined:!0}),d.push({actionID:"scrollPageUp",keyBinding:new b(38,null,null,null,!0),predefined:!0}),d.push({actionID:"scrollPageDown",keyBinding:new b(40,null,null,null,!0),predefined:!0}),d.push({actionID:"lineStart",
-keyBinding:new b(37,null,null,null,!0),predefined:!0}),d.push({actionID:"lineEnd",keyBinding:new b(39,null,null,null,!0),predefined:!0}),d.push({actionID:"lineStart",keyBinding:new b(38,null,null,!0),predefined:!0}),d.push({actionID:"lineEnd",keyBinding:new b(40,null,null,!0),predefined:!0})):(d.push({actionID:"pageUp",keyBinding:new b(33),predefined:!0}),d.push({actionID:"pageDown",keyBinding:new b(34),predefined:!0}),d.push({actionID:"lineStart",keyBinding:new b(36),predefined:!0}),d.push({actionID:"lineEnd",
-keyBinding:new b(35),predefined:!0}),d.push({actionID:"wordPrevious",keyBinding:new b(37,!0),predefined:!0}),d.push({actionID:"wordNext",keyBinding:new b(39,!0),predefined:!0}),d.push({actionID:"textStart",keyBinding:new b(36,!0),predefined:!0}),d.push({actionID:"textEnd",keyBinding:new b(35,!0),predefined:!0}));p.isFirefox&&p.isLinux&&(d.push({actionID:"lineUp",keyBinding:new b(38,!0),predefined:!0}),d.push({actionID:"lineDown",keyBinding:new b(40,!0),predefined:!0}));p.isWindows&&(d.push({actionID:"scrollLineUp",
-keyBinding:new b(38,!0),predefined:!0}),d.push({actionID:"scrollLineDown",keyBinding:new b(40,!0),predefined:!0}));d.push({actionID:"selectLineUp",keyBinding:new b(38,null,!0),predefined:!0});d.push({actionID:"selectLineDown",keyBinding:new b(40,null,!0),predefined:!0});d.push({actionID:"selectCharPrevious",keyBinding:new b(37,null,!0),predefined:!0});d.push({actionID:"selectCharNext",keyBinding:new b(39,null,!0),predefined:!0});d.push({actionID:"selectPageUp",keyBinding:new b(33,null,!0),predefined:!0});
-d.push({actionID:"selectPageDown",keyBinding:new b(34,null,!0),predefined:!0});p.isMac?(d.push({actionID:"selectLineStart",keyBinding:new b(37,!0,!0),predefined:!0}),d.push({actionID:"selectLineEnd",keyBinding:new b(39,!0,!0),predefined:!0}),d.push({actionID:"selectWordPrevious",keyBinding:new b(37,null,!0,!0),predefined:!0}),d.push({actionID:"selectWordNext",keyBinding:new b(39,null,!0,!0),predefined:!0}),d.push({actionID:"selectTextStart",keyBinding:new b(36,null,!0),predefined:!0}),d.push({actionID:"selectTextEnd",
-keyBinding:new b(35,null,!0),predefined:!0}),d.push({actionID:"selectTextStart",keyBinding:new b(38,!0,!0),predefined:!0}),d.push({actionID:"selectTextEnd",keyBinding:new b(40,!0,!0),predefined:!0}),d.push({actionID:"selectLineStart",keyBinding:new b(37,null,!0,null,!0),predefined:!0}),d.push({actionID:"selectLineEnd",keyBinding:new b(39,null,!0,null,!0),predefined:!0}),d.push({actionID:"selectLineStart",keyBinding:new b(38,null,!0,!0),predefined:!0}),d.push({actionID:"selectLineEnd",keyBinding:new b(40,
-null,!0,!0),predefined:!0})):(p.isLinux&&(d.push({actionID:"selectWholeLineUp",keyBinding:new b(38,!0,!0),predefined:!0}),d.push({actionID:"selectWholeLineDown",keyBinding:new b(40,!0,!0),predefined:!0})),d.push({actionID:"selectLineStart",keyBinding:new b(36,null,!0),predefined:!0}),d.push({actionID:"selectLineEnd",keyBinding:new b(35,null,!0),predefined:!0}),d.push({actionID:"selectWordPrevious",keyBinding:new b(37,!0,!0),predefined:!0}),d.push({actionID:"selectWordNext",keyBinding:new b(39,!0,
-!0),predefined:!0}),d.push({actionID:"selectTextStart",keyBinding:new b(36,!0,!0),predefined:!0}),d.push({actionID:"selectTextEnd",keyBinding:new b(35,!0,!0),predefined:!0}));d.push({actionID:"undo",keyBinding:new k.KeyBinding("z",!0),predefined:!0});p.isMac?d.push({actionID:"redo",keyBinding:new k.KeyBinding("z",!0,!0),predefined:!0}):d.push({actionID:"redo",keyBinding:new k.KeyBinding("y",!0),predefined:!0});d.push({actionID:"deletePrevious",keyBinding:new b(8),predefined:!0});d.push({actionID:"deletePrevious",
-keyBinding:new b(8,null,!0),predefined:!0});d.push({actionID:"deleteNext",keyBinding:new b(46),predefined:!0});d.push({actionID:"deleteWordPrevious",keyBinding:new b(8,!0),predefined:!0});d.push({actionID:"deleteWordPrevious",keyBinding:new b(8,!0,!0),predefined:!0});d.push({actionID:"deleteWordNext",keyBinding:new b(46,!0),predefined:!0});d.push({actionID:"tab",keyBinding:new b(9),predefined:!0});d.push({actionID:"shiftTab",keyBinding:new b(9,null,!0),predefined:!0});d.push({actionID:"enter",keyBinding:new b(13),
-predefined:!0});d.push({actionID:"enter",keyBinding:new b(13,null,!0),predefined:!0});d.push({actionID:"selectAll",keyBinding:new b("a",!0),predefined:!0});d.push({actionID:"toggleTabMode",keyBinding:new b("m",!0),predefined:!0});p.isMac&&(d.push({actionID:"deleteNext",keyBinding:new b(46,null,!0),predefined:!0}),d.push({actionID:"deleteWordPrevious",keyBinding:new b(8,null,null,!0),predefined:!0}),d.push({actionID:"deleteWordNext",keyBinding:new b(46,null,null,!0),predefined:!0}));d.push({actionID:"toggleWrapMode",
-keyBinding:new k.KeyBinding("w",!0,!1,!0)});d.push({actionID:"toggleOverwriteMode",keyBinding:new k.KeyBinding(45)});if(!p.isFirefox){var h=p.isMac&&p.isChrome;d.push({actionID:"noop",keyBinding:new b("u",!h,!1,!1,h),predefined:!0});d.push({actionID:"noop",keyBinding:new b("i",!h,!1,!1,h),predefined:!0});d.push({actionID:"noop",keyBinding:new b("b",!h,!1,!1,h),predefined:!0})}p.isFirefox&&(d.push({actionID:"copy",keyBinding:new b(45,!0),predefined:!0}),d.push({actionID:"paste",keyBinding:new b(45,
-null,!0),predefined:!0}),d.push({actionID:"cut",keyBinding:new b(46,null,!0),predefined:!0}));p.isMac&&(d.push({actionID:"lineStart",keyBinding:new b("a",!1,!1,!1,!0),predefined:!0}),d.push({actionID:"lineEnd",keyBinding:new b("e",!1,!1,!1,!0),predefined:!0}),d.push({actionID:"lineUp",keyBinding:new b("p",!1,!1,!1,!0),predefined:!0}),d.push({actionID:"lineDown",keyBinding:new b("n",!1,!1,!1,!0),predefined:!0}),d.push({actionID:"charPrevious",keyBinding:new b("b",!1,!1,!1,!0),predefined:!0}),d.push({actionID:"charNext",
-keyBinding:new b("f",!1,!1,!1,!0),predefined:!0}),d.push({actionID:"deletePrevious",keyBinding:new b("h",!1,!1,!1,!0),predefined:!0}),d.push({actionID:"deleteNext",keyBinding:new b("d",!1,!1,!1,!0),predefined:!0}),d.push({actionID:"deleteLineEnd",keyBinding:new b("k",!1,!1,!1,!0),predefined:!0}),p.isFirefox?(d.push({actionID:"scrollPageDown",keyBinding:new b("v",!1,!1,!1,!0),predefined:!0}),d.push({actionID:"deleteLineStart",keyBinding:new b("u",!1,!1,!1,!0),predefined:!0}),d.push({actionID:"deleteWordPrevious",
-keyBinding:new b("w",!1,!1,!1,!0),predefined:!0})):(d.push({actionID:"pageDown",keyBinding:new b("v",!1,!1,!1,!0),predefined:!0}),d.push({actionID:"centerLine",keyBinding:new b("l",!1,!1,!1,!0),predefined:!0}),d.push({actionID:"enterNoCursor",keyBinding:new b("o",!1,!1,!1,!0),predefined:!0})));return d};return{KeyMode:o,DefaultKeyMode:e}});
-define("orion/editor/textTheme",["require","orion/editor/eventTarget","orion/util"],function(k,p,o){function e(d){d=d||{};this._document=d.document||document}var b={};e.getTheme=function(d){var d=d||"default",h=b[d];h||(h=b[d]=new e);return h};e.prototype={getThemeClass:function(){return this._themeClass},setThemeClass:function(d,b){var e=this,f=e._themeClass;e._themeClass=d;this._load(d,b,function(){e.onThemeChanged({type:"ThemeChanged",oldValue:f,newValue:e.getThemeClass()})})},onThemeChanged:function(d){return this.dispatchEvent(d)},
-buildStyleSheet:function(d,b){function e(a,c,b){c&&(f.push("."+d+" ."+a+" {"),f.push("\t"+(b?"background-color":"color")+": "+c+";"),f.push("}"))}var f=[];f.push("");f.push("."+d+" {");b.fontFamily&&f.push("\tfont-family: "+b.fontFamily+";");b.fontSize&&f.push("\tfont-size: "+b.fontSize+";");b.fontSize&&f.push("\tcolor: "+b.text+";");f.push("}");f.push("."+d+".textview {");b.background&&f.push("\tbackground-color: "+b.background+";");f.push("}");e("ruler.annotations",b.annotationRuler,!0);e("ruler.lines",
-b.annotationRuler,!0);e("ruler.folding",b.annotationRuler,!0);e("ruler.overview",b.overviewRuler,!0);e("rulerLines",b.lineNumber,!1);e("rulerLines.even",b.lineNumberEven,!1);e("rulerLines.odd",b.lineNumberOdd,!1);e("annotationLine.currentLine",b.currentLine,!0);e("entity-name-tag",b.keyword,!1);e("entity-other-attribute-name",b.attribute,!1);e("string-quoted",b.string,!1);e("line_caret",b.currentLine,!0);e("token_keyword",b.keyword,!1);e("token_string",b.string,!1);e("token_singleline_comment",b.comment,
-!1);e("token_multiline_comment",b.comment,!1);e("token_doc_comment",b.comment,!1);e("token_doc_html_markup",b.comment,!1);return f.join("\n")},_createStyle:function(d,b,e,f){var a=this._document,d="orion-theme-"+d,c=a.getElementById(d);if(c){if(f||c.firstChild.data===b)return;c.removeChild(c.firstChild);c.appendChild(a.createTextNode(b))}else f?(c=o.createElement(a,"link"),c.rel="stylesheet",c.type="text/css",c.href=b,c.addEventListener("load",function(){e()})):(c=o.createElement(a,"style"),c.appendChild(a.createTextNode(b))),
-c.id=d,(a.getElementsByTagName("head")[0]||a.documentElement).appendChild(c);f||e()},_load:function(d,b,e){if(d)if(typeof b==="string")this._createStyle(d,b,e);else if(b=b.href,b.substring(b.length-4)!==".css"&&(b+=".css"),/^\//.test(b)||/[a-zA-Z0-9]+:\/\//i.test(b)||!k.toUrl)this._createStyle(d,b,e,!0);else{var f=this;k(["text!"+b],function(a){f._createStyle(d,a,e,!1)})}else e()}};p.EventTarget.addMixin(e.prototype);return{TextTheme:e}});
-define("orion/editor/textView","i18n!orion/editor/nls/messages,orion/editor/textModel,orion/editor/keyModes,orion/editor/eventTarget,orion/editor/textTheme,orion/util".split(","),function(k,p,o,e,b,d){function h(g,a,c,s){typeof g.addEventListener==="function"?g.addEventListener(a,c,s===!0):g.attachEvent("on"+a,c)}function j(g,a,c){if(c){a.className="";for(var c=a.attributes,s=c.length;s-- >0;)(!d.isIE||d.isIE>=9||d.isIE<9&&c[s].specified)&&a.removeAttribute(c[s].name)}if(g){if(g.styleClass)a.className=
-g.styleClass;if(c=g.style)for(var b in c)c.hasOwnProperty(b)&&(a.style[b]=c[b]);if(g=g.attributes)for(var i in g)g.hasOwnProperty(i)&&a.setAttribute(i,g[i])}}function f(g){return g instanceof Array?g.slice(0):g}function a(g,a){if(!g)return a;if(!a)return g;for(var c in a)a.hasOwnProperty(c)&&(g.hasOwnProperty(c)||(g[c]=a[c]));return g}function c(g,a){if(g===a)return!0;if(g&&!a||!g&&a)return!1;if(g&&g.constructor===String||a&&a.constructor===String)return!1;if(g instanceof Array||a instanceof Array){if(!(g instanceof
-Array&&a instanceof Array))return!1;if(g.length!==a.length)return!1;for(var d=0;d<g.length;d++)if(!c(g[d],a[d]))return!1;return!0}if(!(g instanceof Object)||!(a instanceof Object))return!1;for(d in g)if(g.hasOwnProperty(d)){if(!a.hasOwnProperty(d))return!1;if(!c(g[d],a[d]))return!1}for(d in a)if(!g.hasOwnProperty(d))return!1;return!0}function i(g,a,c){for(var s=0,d=0,b=0,i=g.length;b<i;){s!==-1&&s<=b&&(s=g.indexOf("\r",b));d!==-1&&d<=b&&(d=g.indexOf("\n",b));var f=b,m;if(d===-1&&s===-1){a(g.substring(b));
-break}s!==-1&&d!==-1?s+1===d?(m=s,b=d+1):(m=s<d?s:d,b=(s<d?s:d)+1):s!==-1?(m=s,b=s+1):(m=d,b=d+1);a(g.substring(f,m));c()}}function m(g){var a,c,s,d,b=g.ownerDocument.defaultView||g.ownerDocument.parentWindow;if(b.getComputedStyle)g=b.getComputedStyle(g,null),a=g.getPropertyValue("padding-left"),c=g.getPropertyValue("padding-top"),s=g.getPropertyValue("padding-right"),d=g.getPropertyValue("padding-bottom");else if(g.currentStyle)a=g.currentStyle.paddingLeft,c=g.currentStyle.paddingTop,s=g.currentStyle.paddingRight,
-d=g.currentStyle.paddingBottom;return{left:parseInt(a,10)||0,top:parseInt(c,10)||0,right:parseInt(s,10)||0,bottom:parseInt(d,10)||0}}function n(g){var a,c,s,d,b=g._trim;if(!b){var b=m(g),i=g.ownerDocument.defaultView||g.ownerDocument.parentWindow;if(i.getComputedStyle)d=i.getComputedStyle(g,null),a=d.getPropertyValue("border-left-width"),c=d.getPropertyValue("border-top-width"),s=d.getPropertyValue("border-right-width"),d=d.getPropertyValue("border-bottom-width");else if(g.currentStyle)a=g.currentStyle.borderLeftWidth,
-c=g.currentStyle.borderTopWidth,s=g.currentStyle.borderRightWidth,d=g.currentStyle.borderBottomWidth;a=parseInt(a,10)||0;c=parseInt(c,10)||0;s=parseInt(s,10)||0;d=parseInt(d,10)||0;b.left+=a;b.top+=c;b.right+=s;b.bottom+=d;g._trim=b}return b}function l(g,a,c){this.start=g;this.end=a;this.caret=c}function r(g){this.left=g.left;this.top=g.top;this.right=g.right;this.bottom=g.bottom}function q(g,a,c){this.view=g;this.lineIndex=a;this._lineDiv=c}function v(g){this._init(g||{})}var u=function(){function g(g){this.options=
-g}g.prototype.play=function(){var g=typeof this.options.duration==="number"?this.options.duration:350,a=this.options.easing||this.defaultEasing,c=this.options.onAnimate||function(){},d=this.options.curve[0],b=this.options.curve[1],i=b-d,f=-1,m,n=this;this.interval=this.options.window.setInterval(function(){f=f===-1?(new Date).getTime():f;var l=((new Date).getTime()-f)/g;l<1?(l=a(l),m=d+l*i,c(m)):(c(b),n.stop())},typeof this.options.rate==="number"?this.options.rate:20)};g.prototype.stop=function(){this.options.window.clearInterval(this.interval);
-(this.options.onEnd||function(){})()};g.prototype.defaultEasing=function(g){return Math.sin(g*(Math.PI/2))};return g}();l.prototype={clone:function(){return new l(this.start,this.end,this.caret)},collapse:function(){this.caret?this.end=this.start:this.start=this.end},extend:function(g){this.caret?this.start=g:this.end=g;if(this.start>this.end)g=this.start,this.start=this.end,this.end=g,this.caret=!this.caret},setCaret:function(g){this.end=this.start=g;this.caret=!1},getCaret:function(){return this.caret?
-this.start:this.end},toString:function(){return"start="+this.start+" end="+this.end+(this.caret?" caret is at start":" caret is at end")},isEmpty:function(){return this.start===this.end},equals:function(g){return this.caret===g.caret&&this.start===g.start&&this.end===g.end}};r.prototype={toString:function(){return"{l="+this.left+", t="+this.top+", r="+this.right+", b="+this.bottom+"}"}};q.prototype={create:function(g,a){if(!this._lineDiv){var c=this._lineDiv=this._createLine(g,a,this.lineIndex);c._line=
-this;return c}},_createLine:function(g,a,b){var s=this.view,i=s._model,f=i.getLine(b),m=i.getLineStart(b),n={type:"LineStyle",textView:s,lineIndex:b,lineText:f,lineStart:m};if(f.length<2E3)s.onLineStyle(n);i=a||d.createElement(g.ownerDocument,"div");if(!a||!c(a.viewStyle,n.style)){j(n.style,i,a);if(a)a._trim=null;i.viewStyle=n.style;i.setAttribute("role","presentation")}i.lineIndex=b;b=[];this._createRanges(n.ranges,f,0,f.length,m,{tabOffset:0,ranges:b});f=" ";!s._fullSelection&&d.isIE<9&&(f="\ufeff");
-d.isWebkit&&(f="\u200c");n={text:f,style:s._metrics.largestFontStyle,ignoreChars:1};b.length===0||!b[b.length-1].style||b[b.length-1].style.tagName!=="div"?b.push(n):b.splice(b.length-1,0,n);var l,e,h,r,q,f=s=0,k,o;if(a){if(e=a.modelChangedEvent)e.removedLineCount===0&&e.addedLineCount===0?(o=e.start-m,k=e.addedCharCount-e.removedCharCount):o=-1,a.modelChangedEvent=void 0;e=a.firstChild}for(m=0;m<b.length;m++){n=b[m];r=n.text;s+=r.length;l=n.style;if(e)if(q=e.firstChild.data,h=e.viewStyle,q===r&&
-c(l,h)){f+=q.length;e._rectsCache=void 0;l=e=e.nextSibling;continue}else for(;e;){if(o!==-1){h=s;h>=o&&(h-=k);q=(q=e.firstChild.data)?q.length:0;if(f+q>h)break;f+=q}h=e.nextSibling;i.removeChild(e);e=h}l=this._createSpan(i,r,l,n.ignoreChars);e?i.insertBefore(l,e):i.appendChild(l);if(a)a.lineWidth=void 0}if(a)for(g=l?l.nextSibling:null;g;)h=g.nextSibling,a.removeChild(g),g=h;else g.appendChild(i);return i},_createRanges:function(g,a,d,s,b,i){if(!(d>s)){if(g)for(var f=0;f<g.length;f++){var m=g[f];if(!(m.end<
-b+d)){var n=Math.max(b+d,m.start)-b;if(n>s)break;var l=Math.min(b+s,m.end)-b;if(n<=l){n=Math.max(d,n);l=Math.min(s,l);for(d<n&&this._createRange(a,d,n,null,i);f+1<g.length&&g[f+1].start-b===l&&c(m.style,g[f+1].style);)m=g[f+1],l=Math.min(b+s,m.end)-b,f++;this._createRange(a,n,l,m.style,i);d=l}}}d<s&&this._createRange(a,d,s,null,i)}},_createRange:function(g,a,c,s,d){if(!(a>c)){var b=this.view._customTabSize;if(b&&b!==8)for(var i=g.indexOf("\t",a);i!==-1;){a<i&&(a={text:g.substring(a,i),style:s},d.ranges.push(a),
-d.tabOffset+=a.text.length);a=b-d.tabOffset%b;if(a>0){for(var f="\u00a0",m=1;m<a;m++)f+=" ";a={text:f,style:s,ignoreChars:a-1};d.ranges.push(a);d.tabOffset+=a.text.length}a=i+1;if(a===c)return;i=g.indexOf("\t",a)}a<=c&&(a={text:g.substring(a,c),style:s},d.ranges.push(a),d.tabOffset+=a.text.length)}},_createSpan:function(g,a,c,s){var b=this.view,i="span";c&&c.tagName&&(i=c.tagName.toLowerCase());var f=i==="a";if(f)g.hasLink=!0;f&&!b._linksVisible&&(i="span");f=g.ownerDocument;g=d.createElement(g.ownerDocument,
-i);c&&c.html?(g.innerHTML=c.html,g.ignore=!0):c&&c.node?(g.appendChild(c.node),g.ignore=!0):g.appendChild(f.createTextNode(c&&c.text?c.text:a));j(c,g);if(i==="a"){var m=b._getWindow();h(g,"click",function(g){return b._handleLinkClick(g?g:m.event)},!1)}g.viewStyle=c;if(s)g.ignoreChars=s;return g},_ensureCreated:function(){return this._lineDiv?this._lineDiv:this._createdDiv=this.create(this.view._clientDiv,null)},getBoundingClientRect:function(g,a){var c=this._ensureCreated(),s=this.view;if(g===void 0)return this._getLineBoundingClientRect(c,
-!0);var b=s._model,i=c.ownerDocument,f=this.lineIndex,m=null;if(g<b.getLineEnd(f)){b=b.getLineStart(f);for(f=c.firstChild;f;){if(!f.ignore){var n=f.firstChild,l=n.length;f.ignoreChars&&(l-=f.ignoreChars);if(b+l>g){m=g-b;if(s._isRangeRects)i=i.createRange(),i.setStart(n,m),i.setEnd(n,m+1),m=new r(i.getBoundingClientRect());else if(d.isIE)i=i.body.createTextRange(),i.moveToElementText(f),i.collapse(),i.moveEnd("character",m+1),i.moveStart("character",m),m=new r(i.getBoundingClientRect());else{var e=
-n.data;f.removeChild(n);f.appendChild(i.createTextNode(e.substring(0,m)));var h=d.createElement(i,"span");h.appendChild(i.createTextNode(e.substring(m,m+1)));f.appendChild(h);f.appendChild(i.createTextNode(e.substring(m+1)));m=new r(h.getBoundingClientRect());f.innerHTML="";f.appendChild(n);this._createdDiv||(i=s._getSelection(),(b<=i.start&&i.start<b+l||b<=i.end&&i.end<b+l)&&s._updateDOMSelection())}d.isIE&&(i=c.ownerDocument.defaultView||c.ownerDocument.parentWindow,c=i.screen.logicalXDPI/i.screen.deviceXDPI,
-i=i.screen.logicalYDPI/i.screen.deviceYDPI,m.left*=c,m.right*=c,m.top*=i,m.bottom*=i);break}b+=l}f=f.nextSibling}}c=this.getBoundingClientRect();if(!m)s._wrapMode?(s=this.getClientRects(),m=s[s.length-1],m.left=m.right,m.left+=c.left,m.top+=c.top,m.right+=c.left,m.bottom+=c.top):(m=new r(c),m.left=m.right);if(a||a===void 0)m.left-=c.left,m.top-=c.top,m.right-=c.left,m.bottom-=c.top;return m},_getClientRects:function(g,a){var c,s,d,b;if(!g._rectsCache){c=g.getClientRects();s=Array(c.length);for(b=
-0;b<c.length;b++)d=s[b]=new r(c[b]),d.left-=a.left,d.top-=a.top,d.right-=a.left,d.bottom-=a.top;g._rectsCache=s}c=g._rectsCache;s=[c.length];for(b=0;b<c.length;b++)s[b]=new r(c[b]);return s},getClientRects:function(g){if(!this.view._wrapMode)return[this.getBoundingClientRect()];for(var a=this._ensureCreated(),c=[],s=a.firstChild,d,b=a.getBoundingClientRect();s;){if(!s.ignore)for(var i=this._getClientRects(s,b),a=0;a<i.length;a++){var f=i[a],m;if(f.top!==f.bottom){var n=f.top+(f.bottom-f.top)/2;for(m=
-0;m<c.length;m++)if(d=c[m],d.top<=n&&n<d.bottom)break;if(m===c.length)c.push(f);else{if(f.left<d.left)d.left=f.left;if(f.top<d.top)d.top=f.top;if(f.right>d.right)d.right=f.right;if(f.bottom>d.bottom)d.bottom=f.bottom}}}s=s.nextSibling}return g!==void 0?c[g]:c},_getLineBoundingClientRect:function(g,a){var c=new r(g.getBoundingClientRect());if(!this.view._wrapMode){c.right=c.left;for(var d=g.lastChild;d&&d.ignoreChars===d.firstChild.length;)d=d.previousSibling;if(d)d=d.getBoundingClientRect(),c.right=
-d.right+n(g).right}a&&(d=n(g),c.left+=d.left,c.right-=d.right);return c},getLineCount:function(){return!this.view._wrapMode?1:this.getClientRects().length},getLineIndex:function(g){if(!this.view._wrapMode)return 0;for(var a=this.getClientRects(),g=this.getBoundingClientRect(g),g=g.top+(g.bottom-g.top)/2,c=0;c<a.length;c++)if(a[c].top<=g&&g<a[c].bottom)return c;return a.length-1},getLineStart:function(g){if(!this.view._wrapMode||g===0)return this.view._model.getLineStart(g);var a=this.getClientRects();
-return this.getOffset(a[g].left+1,a[g].top+1)},getOffset:function(g,a){var c=this.view,s=c._model,b=this.lineIndex,i=s.getLineStart(b),f=s.getLineEnd(b);if(i===f)return i;var m=this._ensureCreated(),n=this.getBoundingClientRect(),l,e;if(c._wrapMode){l=this.getClientRects();if(a<l[0].top)a=l[0].top;for(var h=0;h<l.length;h++)if(e=l[h],e.top<=a&&a<e.bottom)break;if(g<e.left)g=e.left;g>e.right&&(g=e.right-1)}else g<0&&(g=0),g>n.right-n.left&&(g=n.right-n.left);var j=m.ownerDocument,h=j.defaultView||
-j.parentWindow,r=d.isIE?h.screen.logicalXDPI/h.screen.deviceXDPI:1,q=d.isIE?h.screen.logicalYDPI/h.screen.deviceYDPI:1,h=i,k=m.firstChild;a:for(;k;){if(!k.ignore){var o=k.firstChild,m=o.length;k.ignoreChars&&(m-=k.ignoreChars);var v,p,u;l=this._getClientRects(k,n);for(var z=0;z<l.length;z++)if(e=l[z],e.left<=g&&g<e.right&&(!c._wrapMode||e.top<=a&&a<e.bottom)){if(d.isIE||c._isRangeRects){for(var j=c._isRangeRects?j.createRange():j.body.createTextRange(),z=m,C=-1;z-C>1;){var D=Math.floor((z+C)/2);l=
-C+1;e=D===m-1&&k.ignoreChars?o.length:D+1;c._isRangeRects?(j.setStart(o,l),j.setEnd(o,e)):(j.moveToElementText(k),j.move("character",l),j.moveEnd("character",e-l));l=j.getClientRects();for(var J=!1,F=0;F<l.length;F++)if(e=l[F],v=e.left*r-n.left,u=e.right*r-n.left,p=e.top*q-n.top,e=e.bottom*q-n.top,v<=g&&g<u&&(!c._wrapMode||p<=a&&a<e)){J=!0;break}J?z=D:C=D}h+=z;l=z;e=z===m-1&&k.ignoreChars?o.length:Math.min(z+1,o.length);c._isRangeRects?(j.setStart(o,l),j.setEnd(o,e)):(j.moveToElementText(k),j.move("character",
-l),j.moveEnd("character",e-l));e=j.getClientRects()[0];v=e.left*r-n.left;u=e.right*r-n.left;c=g>v+(u-v)/2;n=h-i;s=s.getLine(b);b=s.charCodeAt(n);55296<=b&&b<=56319&&c?n<s.length&&(b=s.charCodeAt(n+1),56320<=b&&b<=57343&&(h+=1)):56320<=b&&b<=57343&&!c&&n>0&&(b=s.charCodeAt(n-1),55296<=b&&b<=56319&&(h-=1));c&&h++}else{s=[];for(b=0;b<m;b++)s.push("<span>"),b===m-1?s.push(o.data.substring(b)):s.push(o.data.substring(b,b+1)),s.push("</span>");k.innerHTML=s.join("");for(s=k.firstChild;s;){e=s.getBoundingClientRect();
-v=e.left-n.left;u=e.right-n.left;if(v<=g&&g<u){g>v+(u-v)/2&&h++;break}h++;s=s.nextSibling}if(!this._createdDiv)k.innerHTML="",k.appendChild(o),n=c._getSelection(),(h<=n.start&&n.start<h+m||h<=n.end&&n.end<h+m)&&c._updateDOMSelection()}break a}h+=m}k=k.nextSibling}return Math.min(f,Math.max(i,h))},getNextOffset:function(g,a){if(a.unit==="line"){var c=this.view._model,s=c.getLineAtOffset(g);return a.count>0?c.getLineEnd(s):c.getLineStart(s)}return a.unit==="wordend"||a.unit==="wordWS"||a.unit==="wordendWS"?
-this._getNextOffset_W3C(g,a):d.isIE?this._getNextOffset_IE(g,a):this._getNextOffset_W3C(g,a)},_getNextOffset_W3C:function(g,a){function c(g){return 33<=g&&g<=47||58<=g&&g<=64||91<=g&&g<=94||g===96||123<=g&&g<=126}function d(g){return g===32||g===9}var b=this.view._model,i=b.getLineAtOffset(g),f=b.getLine(i),m=b.getLineStart(i),b=b.getLineEnd(i),i=f.length,n=g-m,l,e=a.count<0?-1:1;if(a.unit==="word"||a.unit==="wordend"||a.unit==="wordWS"||a.unit==="wordendWS")for(var h,j,r;a.count!==0;){if(a.count>
-0){if(n===i)return b;l=f.charCodeAt(n);h=c(l);j=!h&&!d(l);for(n++;n<i;){l=f.charCodeAt(n);if(a.unit!=="wordWS"&&a.unit!=="wordendWS"){r=c(l);if(a.unit==="wordend"){if(!r&&h)break}else if(r&&!h)break;l=!r&&!d(l)}else l=!d(l);if(a.unit==="wordend"||a.unit==="wordendWS"){if(!l&&j)break}else if(l&&!j)break;j=l;h=r;n++}}else{if(n===0)return m;n--;l=f.charCodeAt(n);h=c(l);for(j=!h&&!d(l);0<n;){l=f.charCodeAt(n-1);if(a.unit!=="wordWS"&&a.unit!=="wordendWS"){r=c(l);if(a.unit==="wordend"){if(r&&!h)break}else if(!r&&
-h)break;l=!r&&!d(l)}else l=!d(l);if(a.unit==="wordend"||a.unit==="wordendWS"){if(l&&!j)break}else if(!l&&j)break;j=l;h=r;n--}}a.count-=e}else for(;a.count!==0&&0<=n+e&&n+e<=i;)n+=e,l=f.charCodeAt(n),56320<=l&&l<=57343&&n>0&&(l=f.charCodeAt(n-1),55296<=l&&l<=56319&&(n+=e)),a.count-=e;return m+n},_getNextOffset_IE:function(g,a){for(var c=this._ensureCreated(),d=this.view._model,b=this.lineIndex,i=0,f,m=d.getLineStart(b),n=c.ownerDocument,l=a.count<0?-1:1;a.count!==0;){if(g===d.getLineEnd(b)){for(f=
-c.lastChild;f&&f.ignoreChars;)f=f.previousSibling;if(!f)return m;i=n.body.createTextRange();i.moveToElementText(f);f=i.text.length;i.moveEnd(a.unit,a.count);i=g+i.text.length-f}else if(g===m&&a.count<0)i=m;else for(f=c.firstChild;f;){var e=f.firstChild.length;f.ignoreChars&&(e-=f.ignoreChars);if(m+e>g){i=n.body.createTextRange();g===m&&a.count<0?i.moveToElementText(f.previousSibling):(i.moveToElementText(f),i.collapse(),i.moveEnd("character",g-m));f=i.text.length;i.moveEnd(a.unit,a.count);i=g+i.text.length-
-f;break}m=e+m;f=f.nextSibling}a.count-=l}return i},destroy:function(){var g=this._createdDiv;if(g)g.parentNode.removeChild(g),this._createdDiv=null}};v.prototype={addKeyMode:function(g,a){var c=this._keyModes;a!==void 0?c.splice(a,0,g):c.push(g);g._modeAdded&&g._modeAdded()},addRuler:function(g,a){g.setView(this);var c=this._rulers;if(a!==void 0){var d,b;for(d=0,b=0;d<c.length&&b<a;d++)g.getLocation()===c[d].getLocation()&&b++;c.splice(b,0,g);a=b}else c.push(g);this._createRuler(g,a);this._update()},
-computeSize:function(){var g=0,a=0,c=this._model,b=this._clientDiv;if(!b)return{width:g,height:a};var i=b.style.width;if(d.isWebkit)b.style.width="0x7fffffffpx";for(var c=c.getLineCount(),f=0;f<c;f++){var m=this._getLine(f),n=m.getBoundingClientRect(),g=Math.max(g,n.right-n.left);a+=n.bottom-n.top;m.destroy()}if(d.isWebkit)b.style.width=i;b=this._getViewPadding();g+=b.right+b.left+this._metrics.scrollWidth;a+=b.bottom+b.top+this._metrics.scrollWidth;return{width:g,height:a}},convert:function(g,a,
-c){if(this._clientDiv){var d=this._getScroll(),b=this._getViewPadding(),i=this._viewDiv.getBoundingClientRect();a==="document"&&(g.x!==void 0&&(g.x+=-d.x+i.left+b.left),g.y!==void 0&&(g.y+=-d.y+i.top+b.top));c==="document"&&(g.x!==void 0&&(g.x+=d.x-i.left-b.left),g.y!==void 0&&(g.y+=d.y-i.top-b.top));return g}},destroy:function(){for(var g=0;g<this._rulers.length;g++)this._rulers[g].setView(null);this.rulers=null;this._destroyView();this.onDestroy({type:"Destroy"});this._actions=this._keyModes=this._doubleClickSelection=
-this._selection=this._theme=this._model=this._parent=null},focus:function(){this._clientDiv&&(this._updateDOMSelection(),d.isOpera&&this._clientDiv.blur(),this._clientDiv.focus(),this._updateDOMSelection())},hasFocus:function(){return this._hasFocus},getActionDescription:function(g){if(g=this._actions[g])return g.actionDescription},getActions:function(g){var a=[],c=this._actions,d;for(d in c)c.hasOwnProperty(d)&&(g||!c[d].defaultHandler)&&a.push(d);return a},getBottomIndex:function(g){return!this._clientDiv?
-0:this._getBottomIndex(g)},getBottomPixel:function(){return!this._clientDiv?0:this._getScroll().y+this._getClientHeight()},getCaretOffset:function(){return this._getSelection().getCaret()},getClientArea:function(){if(!this._clientDiv)return{x:0,y:0,width:0,height:0};var g=this._getScroll();return{x:g.x,y:g.y,width:this._getClientWidth(),height:this._getClientHeight()}},getHorizontalPixel:function(){return!this._clientDiv?0:this._getScroll().x},getKeyBindings:function(g){for(var a=[],c=this._keyModes,
-d=0;d<c.length;d++)a=a.concat(c[d].getKeyBindings(g));return a},getKeyModes:function(){return this._keyModes.slice(0)},getLineHeight:function(g){return!this._clientDiv?0:this._getLineHeight(g)},getLineIndex:function(g){return!this._clientDiv?0:this._getLineIndex(g)},getLinePixel:function(g){return!this._clientDiv?0:this._getLinePixel(g)},getLocationAtOffset:function(g){if(!this._clientDiv)return{x:0,y:0};var a=this._model,g=Math.min(Math.max(0,g),a.getCharCount()),a=a.getLineAtOffset(g),c=this._getLine(a),
-g=c.getBoundingClientRect(g);c.destroy();c=g.left;a=this._getLinePixel(a)+g.top;return{x:c,y:a}},getOptions:function(){var g;if(arguments.length===0)g=this._defaultOptions();else if(arguments.length===1){if(g=arguments[0],typeof g==="string")return f(this["_"+g])}else{g={};for(var a in arguments)arguments.hasOwnProperty(a)&&(g[arguments[a]]=void 0)}for(var c in g)g.hasOwnProperty(c)&&(g[c]=f(this["_"+c]));return g},getModel:function(){return this._model},getOffsetAtLocation:function(g,a){if(!this._clientDiv)return 0;
-var c=this._getLineIndex(a),d=this._getLine(c),c=d.getOffset(g,a-this._getLinePixel(c));d.destroy();return c},getRulers:function(){return this._rulers.slice(0)},getSelection:function(){var g=this._getSelection();return{start:g.start,end:g.end}},getText:function(g,a){return this._model.getText(g,a)},getTopIndex:function(g){return!this._clientDiv?0:this._getTopIndex(g)},getTopPixel:function(){return!this._clientDiv?0:this._getScroll().y},invokeAction:function(g,a,c){if(this._clientDiv){if(g=this._actions[g]){if(!a&&
-g.handler&&g.handler(c))return!0;if(g.defaultHandler)return typeof g.defaultHandler(c)==="boolean"}return!1}},isDestroyed:function(){return!this._clientDiv},onContextMenu:function(g){return this.dispatchEvent(g)},onDragStart:function(g){return this.dispatchEvent(g)},onDrag:function(g){return this.dispatchEvent(g)},onDragEnd:function(g){return this.dispatchEvent(g)},onDragEnter:function(g){return this.dispatchEvent(g)},onDragOver:function(g){return this.dispatchEvent(g)},onDragLeave:function(g){return this.dispatchEvent(g)},
-onDrop:function(g){return this.dispatchEvent(g)},onDestroy:function(g){return this.dispatchEvent(g)},onLineStyle:function(g){return this.dispatchEvent(g)},onKeyDown:function(g){return this.dispatchEvent(g)},onKeyPress:function(g){return this.dispatchEvent(g)},onKeyUp:function(g){return this.dispatchEvent(g)},onModelChanged:function(g){return this.dispatchEvent(g)},onModelChanging:function(g){return this.dispatchEvent(g)},onModify:function(g){return this.dispatchEvent(g)},onMouseDown:function(g){return this.dispatchEvent(g)},
-onMouseUp:function(g){return this.dispatchEvent(g)},onMouseMove:function(g){return this.dispatchEvent(g)},onMouseOver:function(g){return this.dispatchEvent(g)},onMouseOut:function(g){return this.dispatchEvent(g)},onSelection:function(g){return this.dispatchEvent(g)},onScroll:function(g){return this.dispatchEvent(g)},onVerify:function(g){return this.dispatchEvent(g)},onFocus:function(g){return this.dispatchEvent(g)},onBlur:function(g){return this.dispatchEvent(g)},redraw:function(){if(!(this._redrawCount>
-0)){var g=this._model.getLineCount();this.redrawRulers(0,g);this.redrawLines(0,g)}},redrawRulers:function(g,a){if(!(this._redrawCount>0))for(var c=this.getRulers(),d=0;d<c.length;d++)this.redrawLines(g,a,c[d])},redrawLines:function(g,a,c){if(!(this._redrawCount>0)&&(g===void 0&&(g=0),a===void 0&&(a=this._model.getLineCount()),g!==a)){var d=this._clientDiv;if(d){if(c)for(d=(c.getLocation()==="left"?this._leftDiv:this._rightDiv).firstChild;d;){if(d._ruler===c)break;d=d.nextSibling}c?d.rulerChanged=
-!0:this._lineHeight&&this._resetLineHeight(g,a);if(!c||c.getOverview()==="page")for(d=d.firstChild;d;){var b=d.lineIndex;if(g<=b&&b<a)d.lineChanged=!0;d=d.nextSibling}if(!c&&!this._wrapMode&&g<=this._maxLineIndex&&this._maxLineIndex<a)this._checkMaxLineIndex=this._maxLineIndex,this._maxLineIndex=-1,this._maxLineWidth=0;this._queueUpdate()}}},redrawRange:function(g,a){if(!(this._redrawCount>0)){var c=this._model;g===void 0&&(g=0);a===void 0&&(a=c.getCharCount());var d=c.getLineAtOffset(g),c=c.getLineAtOffset(Math.max(g,
-a-1))+1;this.redrawLines(d,c)}},removeKeyMode:function(g){for(var a=this._keyModes,c=0;c<a.length;c++)if(a[c]===g){a.splice(c,1);g._modeRemoved&&g._modeRemoved();break}},removeRuler:function(g){for(var a=this._rulers,c=0;c<a.length;c++)if(a[c]===g){a.splice(c,1);g.setView(null);this._destroyRuler(g);this._update();break}},resize:function(){this._clientDiv&&this._handleResize(null)},setAction:function(g,a,c){if(g){var d=this._actions,b=d[g];b||(b=d[g]={});b.handler=a;if(c!==void 0)b.actionDescription=
-c}},setKeyBinding:function(g,a){this._keyModes[0].setKeyBinding(g,a)},setCaretOffset:function(g,a,c){var d=this._model.getCharCount(),g=Math.max(0,Math.min(g,d));this._setSelection(new l(g,g,!1),a===void 0||a,!0,c)},setHorizontalPixel:function(g){this._clientDiv&&(g=Math.max(0,g),this._scrollView(g-this._getScroll().x,0))},setRedraw:function(g){g?--this._redrawCount===0&&this.redraw():this._redrawCount++},setModel:function(g){if(g&&g!==this._model){this._model.removeEventListener("preChanging",this._modelListener.onChanging);
-this._model.removeEventListener("postChanged",this._modelListener.onChanged);var a=this._model.getLineCount(),c=this._model.getCharCount(),d=g.getLineCount(),b=g.getCharCount(),i={type:"ModelChanging",text:g.getText(),start:0,removedCharCount:c,addedCharCount:b,removedLineCount:a,addedLineCount:d};this.onModelChanging(i);this._model=g;i={type:"ModelChanged",start:0,removedCharCount:c,addedCharCount:b,removedLineCount:a,addedLineCount:d};this.onModelChanged(i);this._model.addEventListener("preChanging",
-this._modelListener.onChanging);this._model.addEventListener("postChanged",this._modelListener.onChanged);this._reset();this._update()}},setOptions:function(g){var a=this._defaultOptions(),d;for(d in g)if(g.hasOwnProperty(d)){var b=g[d];if(!c(this["_"+d],b)){var i=a[d]?a[d].update:null;i?i.call(this,b):this["_"+d]=f(b)}}},setSelection:function(g,a,c,d){var b=g>a;if(b)var i=g,g=a,a=i;i=this._model.getCharCount();g=Math.max(0,Math.min(g,i));a=Math.max(0,Math.min(a,i));this._setSelection(new l(g,a,b),
-c===void 0||c,!0,d)},setText:function(g,a,c){var b=a===void 0&&c===void 0;a===void 0&&(a=0);c===void 0&&(c=this._model.getCharCount());if(b)this._variableLineHeight=!1;this._modifyContent({text:g,start:a,end:c,_code:!0},!b);if(b)this._columnX=-1,this._setSelection(new l(0,0,!1),!0),d.isFirefox&&this._fixCaret()},setTopIndex:function(g){this._clientDiv&&this._scrollView(0,this._getLinePixel(Math.max(0,g))-this._getScroll().y)},setTopPixel:function(g){this._clientDiv&&this._scrollView(0,Math.max(0,
-g)-this._getScroll().y)},showSelection:function(){return this._showCaret(!0)},update:function(g,a){this._clientDiv&&(g&&this._updateStyle(),a===void 0||a?this._update():this._queueUpdate())},_handleRootMouseDown:function(g){if(!this._ignoreEvent(g)){if(d.isFirefox&&g.which===1)this._clientDiv.contentEditable=!1,this._ignoreBlur=(this._overlayDiv||this._clientDiv).draggable=!0;var a=this._overlayDiv||this._clientDiv;if(d.isIE<9)a=this._viewDiv;for(var c=g.target?g.target:g.srcElement;c;){if(a===c)return;
-c=c.parentNode}g.preventDefault&&g.preventDefault();g.stopPropagation&&g.stopPropagation();if(!this._isW3CEvents){var b=this;this._getWindow().setTimeout(function(){b._clientDiv.focus()},0)}}},_handleRootMouseUp:function(g){if(!this._ignoreEvent(g)&&d.isFirefox&&g.which===1)this._clientDiv.contentEditable=!0,(this._overlayDiv||this._clientDiv).draggable=!1,this._fixCaret(),this._ignoreBlur=!1},_handleBlur:function(){if(!this._ignoreBlur){this._hasFocus=!1;if(d.isIE<9&&!this._getSelection().isEmpty()){var g=
-this._rootDiv,a=d.createElement(g.ownerDocument,"div");g.appendChild(a);g.removeChild(a)}if(this._cursorDiv)this._cursorDiv.style.display="none";if(this._selDiv1)if(this._selDiv1.style.background="lightgray",this._selDiv2.style.background="lightgray",this._selDiv3.style.background="lightgray",g=this._getWindow(),a=this._selDiv1.ownerDocument,g.getSelection){a=g.getSelection();for(g=a.anchorNode;g;){if(g===this._clientDiv){a.rangeCount>0&&a.removeAllRanges();break}g=g.parentNode}}else if(a.selection){this._ignoreSelect=
-!1;for(g=a.selection.createRange().parentElement();g;){if(g===this._clientDiv){a.selection.empty();break}g=g.parentNode}this._ignoreSelect=!0}if(!this._ignoreFocus)this.onBlur({type:"Blur"})}},_handleContextMenu:function(g){if(!this._ignoreEvent(g)){d.isIE&&this._lastMouseButton===3&&this._updateDOMSelection();var a=!1;this.isListening("ContextMenu")?(a=this._createMouseEvent("ContextMenu",g),a.screenX=g.screenX,a.screenY=g.screenY,this.onContextMenu(a),a=a.defaultPrevented):d.isMac&&d.isFirefox&&
-g.button===0&&(a=!0);if(a)return g.preventDefault&&g.preventDefault(),!1}},_handleCopy:function(g){if(!this._ignoreEvent(g)&&!this._ignoreCopy&&this._doCopy(g))return g.preventDefault&&g.preventDefault(),!1},_handleCut:function(g){if(!this._ignoreEvent(g)&&this._doCut(g))return g.preventDefault&&g.preventDefault(),!1},_handleDataModified:function(g){this._ignoreEvent(g)||this._startIME()},_handleDblclick:function(g){if(!this._ignoreEvent(g)&&(this._lastMouseTime=g.timeStamp?g.timeStamp:(new Date).getTime(),
-this._clickCount!==2))this._clickCount=2,this._handleMouse(g)},_handleDragStart:function(g){if(!this._ignoreEvent(g)){if(d.isFirefox){var a=this;this._getWindow().setTimeout(function(){a._clientDiv.contentEditable=!0;a._clientDiv.draggable=!1;a._ignoreBlur=!1},0)}if(this.isListening("DragStart")&&this._dragOffset!==-1)this._isMouseDown=!1,this.onDragStart(this._createMouseEvent("DragStart",g)),this._dragOffset=-1;else return g.preventDefault&&g.preventDefault(),!1}},_handleDrag:function(g){if(!this._ignoreEvent(g)&&
-this.isListening("Drag"))this.onDrag(this._createMouseEvent("Drag",g))},_handleDragEnd:function(g){if(!this._ignoreEvent(g)){this._dropTarget=!1;this._dragOffset=-1;if(this.isListening("DragEnd"))this.onDragEnd(this._createMouseEvent("DragEnd",g));d.isFirefox&&(this._fixCaret(),g.dataTransfer.dropEffect==="none"&&!g.dataTransfer.mozUserCancelled&&this._fixCaret())}},_handleDragEnter:function(g){if(!this._ignoreEvent(g)){var a=!0;this._dropTarget=!0;this.isListening("DragEnter")&&(a=!1,this.onDragEnter(this._createMouseEvent("DragEnter",
-g)));if(d.isWebkit||a)return g.preventDefault&&g.preventDefault(),!1}},_handleDragOver:function(g){if(!this._ignoreEvent(g)){var a=!0;this.isListening("DragOver")&&(a=!1,this.onDragOver(this._createMouseEvent("DragOver",g)));if(d.isWebkit||a){if(a)g.dataTransfer.dropEffect="none";g.preventDefault&&g.preventDefault();return!1}}},_handleDragLeave:function(g){if(!this._ignoreEvent(g)&&(this._dropTarget=!1,this.isListening("DragLeave")))this.onDragLeave(this._createMouseEvent("DragLeave",g))},_handleDrop:function(g){if(!this._ignoreEvent(g)){this._dropTarget=
-!1;if(this.isListening("Drop"))this.onDrop(this._createMouseEvent("Drop",g));g.preventDefault&&g.preventDefault();return!1}},_handleFocus:function(){this._hasFocus=!0;d.isIOS&&this._lastTouchOffset!==void 0?(this.setCaretOffset(this._lastTouchOffset,!0),this._lastTouchOffset=void 0):this._updateDOMSelection();if(this._cursorDiv)this._cursorDiv.style.display="block";if(this._selDiv1){var g=this._highlightRGB;this._selDiv1.style.background=g;this._selDiv2.style.background=g;this._selDiv3.style.background=
-g}if(!this._ignoreFocus)this.onFocus({type:"Focus"})},_handleKeyDown:function(g){if(!this._ignoreEvent(g)){if(this.isListening("KeyDown")){var a=this._createKeyEvent("KeyDown",g);this.onKeyDown(a);if(a.defaultPrevented){if(d.isFirefox)this._keyDownPrevented=!0;g.preventDefault();return}}a=!1;switch(g.keyCode){case 16:case 17:case 18:case 91:a=!0;break;default:this._setLinksVisible(!1)}if(g.keyCode===229){if(this._readonly)return g.preventDefault&&g.preventDefault(),!1;a=!0;if(d.isSafari&&d.isMac&&
-g.ctrlKey)a=!1,g.keyCode=129;a&&this._startIME()}else a||this._commitIME();if((d.isMac||d.isLinux)&&d.isFirefox<4||d.isOpera)return this._keyDownEvent=g,!0;if(this._doAction(g))return g.preventDefault?(g.preventDefault(),g.stopPropagation()):(g.cancelBubble=!0,g.returnValue=!1,g.keyCode=0),!1}},_handleKeyPress:function(g){if(!this._ignoreEvent(g))if(this._keyDownPrevented)g.preventDefault&&(g.preventDefault(),g.stopPropagation()),this._keyDownPrevented=void 0;else{if(d.isMac&&d.isWebkit&&(63232<=
-g.keyCode&&g.keyCode<=63487||g.keyCode===13||g.keyCode===8))return g.preventDefault&&g.preventDefault(),!1;if(((d.isMac||d.isLinux)&&d.isFirefox<4||d.isOpera)&&this._doAction(this._keyDownEvent))return g.preventDefault&&g.preventDefault(),!1;var a=d.isMac?g.metaKey:g.ctrlKey;if(g.charCode!==void 0&&a)switch(g.charCode){case 99:case 118:case 120:return!0}if(this.isListening("KeyPress")&&(a=this._createKeyEvent("KeyPress",g),this.onKeyPress(a),a.defaultPrevented)){g.preventDefault();return}if(this._doAction(g))return g.preventDefault?
-(g.preventDefault(),g.stopPropagation()):(g.cancelBubble=!0,g.returnValue=!1,g.keyCode=0),!1;a=!1;if(d.isMac){if(g.ctrlKey||g.metaKey)a=!0}else if(d.isFirefox){if(g.ctrlKey||g.altKey)a=!0}else g.ctrlKey^g.altKey&&(a=!0);if(!a&&(a=d.isOpera?g.which:g.charCode!==void 0?g.charCode:g.keyCode,a>31))return this._doContent(String.fromCharCode(a)),g.preventDefault&&g.preventDefault(),!1}},_handleKeyUp:function(g){if(!this._ignoreEvent(g)){if(this.isListening("KeyUp")){var a=this._createKeyEvent("KeyUp",g);
-this.onKeyUp(a);if(a.defaultPrevented){g.preventDefault();return}}(d.isMac?g.metaKey:g.ctrlKey)||this._setLinksVisible(!1);g.keyCode===13&&this._commitIME()}},_handleLinkClick:function(g){if(!(d.isMac?g.metaKey:g.ctrlKey))return g.preventDefault&&g.preventDefault(),!1},_handleMouse:function(g){var a=this._getWindow(),c=!0,b=a;if(d.isIE||d.isFirefox&&!this._overlayDiv)b=this._clientDiv;if(this._overlayDiv){if(this._hasFocus)this._ignoreFocus=!0;var i=this;a.setTimeout(function(){i.focus();i._ignoreFocus=
-!1},0)}this._clickCount===1?(c=this._setSelectionTo(g.clientX,g.clientY,g.shiftKey,!d.isOpera&&this._hasFocus&&this.isListening("DragStart")))&&this._setGrab(b):(this._isW3CEvents&&this._setGrab(b),this._doubleClickSelection=null,this._setSelectionTo(g.clientX,g.clientY,g.shiftKey),this._doubleClickSelection=this._getSelection());return c},_handleMouseDown:function(g){if(!this._ignoreEvent(g)){if(this._linksVisible)if((g.target||g.srcElement).tagName!=="A")this._setLinksVisible(!1);else return;this._commitIME();
-var a=g.which;a||(g.button===4&&(a=2),g.button===2&&(a=3),g.button===1&&(a=1));var c=a!==2&&g.timeStamp?g.timeStamp:(new Date).getTime(),b=c-this._lastMouseTime,i=Math.abs(this._lastMouseX-g.clientX),f=Math.abs(this._lastMouseY-g.clientY),m=this._lastMouseButton===a;this._lastMouseX=g.clientX;this._lastMouseY=g.clientY;this._lastMouseTime=c;this._lastMouseButton=a;if(a===1)this._isMouseDown=!0,m&&b<=this._clickTime&&i<=this._clickDist&&f<=this._clickDist?this._clickCount++:this._clickCount=1;if(this.isListening("MouseDown")&&
-(c=this._createMouseEvent("MouseDown",g),this.onMouseDown(c),c.defaultPrevented)){g.preventDefault();return}if(a===1&&this._handleMouse(g)&&(d.isIE>=9||d.isOpera||d.isChrome||d.isSafari||d.isFirefox&&!this._overlayDiv))this._hasFocus||this.focus(),g.preventDefault();d.isFirefox&&this._lastMouseButton===3&&this._updateDOMSelection()}},_handleMouseOver:function(g){if(!this._ignoreEvent(g)&&!this._animation&&this.isListening("MouseOver"))this.onMouseOver(this._createMouseEvent("MouseOver",g))},_handleMouseOut:function(g){if(!this._ignoreEvent(g)&&
-!this._animation&&this.isListening("MouseOut"))this.onMouseOut(this._createMouseEvent("MouseOut",g))},_handleMouseMove:function(g){if(!this._animation){var a=this._isClientDiv(g);if(this.isListening("MouseMove")&&a)this.onMouseMove(this._createMouseEvent("MouseMove",g));if(!this._dropTarget){var c=this._linksVisible||this._lastMouseMoveX!==g.clientX||this._lastMouseMoveY!==g.clientY;this._lastMouseMoveX=g.clientX;this._lastMouseMoveY=g.clientY;this._setLinksVisible(c&&!this._isMouseDown&&(d.isMac?
-g.metaKey:g.ctrlKey));if(!this._isW3CEvents){if(g.button===0)return this._setGrab(null),!0;if(!this._isMouseDown&&g.button===1&&(this._clickCount&1)!==0&&a)return this._clickCount=2,this._handleMouse(g,this._clickCount)}if(this._isMouseDown&&this._dragOffset===-1){var a=g.clientX,g=g.clientY,b=this._getViewPadding(),i=this._viewDiv.getBoundingClientRect(),f=this._getClientWidth(),m=this._getClientHeight(),c=i.left+b.left,n=i.top+b.top,f=i.left+b.left+f,b=i.top+b.top+m;g<n?this._doAutoScroll("up",
-a,g-n):g>b?this._doAutoScroll("down",a,g-b):a<c&&!this._wrapMode?this._doAutoScroll("left",a-c,g):a>f&&!this._wrapMode?this._doAutoScroll("right",a-f,g):(this._endAutoScroll(),this._setSelectionTo(a,g,!0))}}}},_isClientDiv:function(g){for(var a=this._overlayDiv||this._clientDiv,g=g.target?g.target:g.srcElement;g;){if(a===g)return!0;g=g.parentNode}return!1},_createKeyEvent:function(g,a){return{type:g,event:a,preventDefault:function(){this.defaultPrevented=!0}}},_createMouseEvent:function(g,a){var c=
-this.convert({x:a.clientX,y:a.clientY},"page","document");return{type:g,event:a,clickCount:this._clickCount,x:c.x,y:c.y,preventDefault:function(){this.defaultPrevented=!0}}},_handleMouseUp:function(g){var a=g.which?g.button===0:g.button===1;if(this.isListening("MouseUp")&&(this._isClientDiv(g)||a&&this._isMouseDown))this.onMouseUp(this._createMouseEvent("MouseUp",g));if(!this._linksVisible&&a&&this._isMouseDown){if(this._dragOffset!==-1)a=this._getSelection(),a.extend(this._dragOffset),a.collapse(),
-this._setSelection(a,!0,!0),this._dragOffset=-1;this._isMouseDown=!1;this._endAutoScroll();this._isW3CEvents&&this._setGrab(null);d.isFirefox&&g.preventDefault()}},_handleMouseWheel:function(g){var a=this._getLineHeight(),c=0,b=0;d.isIE||d.isOpera?b=-g.wheelDelta/40*a:d.isFirefox?(a=d.isMac?g.detail*3:Math.max(-256,Math.min(256,g.detail))*a,g.axis===g.HORIZONTAL_AXIS?c=a:b=a):d.isMac?(b=g.timeStamp-this._wheelTimeStamp,this._wheelTimeStamp=g.timeStamp,c=g.wheelDeltaX%120!==0?1:b<40?40/(40-b):40,b=
-g.wheelDeltaY%120!==0?1:b<40?40/(40-b):40,c=Math.ceil(-g.wheelDeltaX/c),-1<c&&c<0&&(c=-1),0<c&&c<1&&(c=1),b=Math.ceil(-g.wheelDeltaY/b),-1<b&&b<0&&(b=-1),0<b&&b<1&&(b=1)):(c=-g.wheelDeltaX,b=-g.wheelDeltaY/120*8*a);if(d.isSafari){for(a=g.target;a&&a.lineIndex===void 0;)a=a.parentNode;this._mouseWheelLine=a}a=this._getScroll();this._scrollView(c,b);c=this._getScroll();if(a.x!==c.x||a.y!==c.y)return g.preventDefault&&g.preventDefault(),!1},_handlePaste:function(g){if(!this._ignoreEvent(g)&&!this._ignorePaste&&
-this._doPaste(g)){if(d.isIE){var a=this;this._ignoreFocus=!0;this._getWindow().setTimeout(function(){a._updateDOMSelection();a._ignoreFocus=!1},0)}g.preventDefault&&g.preventDefault();return!1}},_handleResize:function(){var g=this._parent.clientWidth,a=this._parent.clientHeight;if(this._parentWidth!==g||this._parentHeight!==a)this._parentWidth!==g&&this._wrapMode&&this._resetLineHeight(),this._parentWidth=g,this._parentHeight=a,d.isIE<9?this._queueUpdate():this._update()},_handleRulerEvent:function(g){for(var a=
-g.target?g.target:g.srcElement,c=a.lineIndex;a&&!a._ruler;){if(c===void 0&&a.lineIndex!==void 0)c=a.lineIndex;a=a.parentNode}a=a?a._ruler:null;if(c===void 0&&a&&a.getOverview()==="document"){var c=this._getClientHeight(),d=this._model.getLineCount(),b=this._getViewPadding(),i=this._viewDiv.getBoundingClientRect(),c=Math.floor((g.clientY-i.top-this._metrics.scrollWidth)*d/(c+b.top+b.bottom-2*this._metrics.scrollWidth));0<=c&&c<d||(c=void 0)}if(a)switch(g.type){case "click":if(a.onClick)a.onClick(c,
-g);break;case "dblclick":if(a.onDblClick)a.onDblClick(c,g);break;case "mousemove":if(a.onMouseMove)a.onMouseMove(c,g);break;case "mouseover":if(a.onMouseOver)a.onMouseOver(c,g);break;case "mouseout":if(a.onMouseOut)a.onMouseOut(c,g)}},_handleScroll:function(){var g=this._getScroll(!1),a=this._hScroll,c=this._vScroll;if(a!==g.x||c!==g.y)this._hScroll=g.x,this._vScroll=g.y,this._commitIME(),this._update(c===g.y),this.onScroll({type:"Scroll",oldValue:{x:a,y:c},newValue:g})},_handleSelectStart:function(g){if(this._ignoreSelect)return g&&
-g.preventDefault&&g.preventDefault(),!1},_getModelOffset:function(g,a){if(g){var c;c=g.tagName==="DIV"?g:g.parentNode.parentNode;var d=0,b=c.lineIndex;if(g.tagName!=="DIV")for(c=c.firstChild;c;){var i=c.firstChild;if(i===g){c.ignoreChars&&(d-=c.ignoreChars);d+=a;break}c.ignoreChars&&(d-=c.ignoreChars);d+=i.data.length;c=c.nextSibling}return Math.max(0,d)+this._model.getLineStart(b)}},_updateSelectionFromDOM:function(){var g=this._getWindow().getSelection(),a=this._getModelOffset(g.anchorNode,g.anchorOffset),
-g=this._getModelOffset(g.focusNode,g.focusOffset);a===void 0||g===void 0||this._setSelection(new l(a,g),!1,!1)},_handleSelectionChange:function(){if(this._imeOffset===-1)if(d.isAndroid){var g=this._getWindow();this._selTimer&&g.clearTimeout(this._selTimer);var a=this;this._selTimer=g.setTimeout(function(){if(a._clientDiv)a._selTimer=null,a._updateSelectionFromDOM()},250)}else this._updateSelectionFromDOM()},_handleTextInput:function(g){if(!this._ignoreEvent(g)){this._imeOffset=-1;if(d.isAndroid){for(var a=
-this._getWindow().getSelection(),c=a.anchorNode;c;){if(c.lineIndex!==void 0)break;c=c.parentNode}if(c){var b=this._model,i=c.lineIndex,f=b.getLine(i),m=f,n=0,b=b.getLineStart(i);if(a.rangeCount>0)a.getRangeAt(0).deleteContents(),i=c.ownerDocument.createTextNode(g.data),a.getRangeAt(0).insertNode(i),n=this._getDOMText(c,i),m=n.text,n=n.offset,i.parentNode.removeChild(i);c.lineRemoved=!0;for(c=0;f.charCodeAt(c)===m.charCodeAt(c)&&c<n;)c++;a=f.length-1;for(i=m.length-f.length;f.charCodeAt(a)===m.charCodeAt(a+
-i)&&a+i>=n+g.data.length;)a--;a++;f=m.substring(c,a+i);c+=b;a+=b;this._modifyContent({text:f,start:c,end:a,_ignoreDOMSelection:!0},!0)}}else this._doContent(g.data);g.preventDefault()}},_handleTouchStart:function(g){this._commitIME();var a=this._getWindow();if(this._touchScrollTimer)this._vScrollDiv.style.display="none",this._hScrollDiv.style.display="none",a.clearInterval(this._touchScrollTimer),this._touchScrollTimer=null;var c=g.touches;if(c.length===1){var c=c[0],b=c.clientX,i=c.clientY;this._touchStartX=
-b;this._touchStartY=i;if(d.isAndroid&&(i<c.pageY-a.pageYOffset||b<c.pageX-a.pageXOffset))b=c.pageX-a.pageXOffset,i=c.pageY-a.pageYOffset;a=this.convert({x:b,y:i},"page","document");this._lastTouchOffset=this.getOffsetAtLocation(a.x,a.y);this._touchStartTime=g.timeStamp;this._touching=!0}},_handleTouchMove:function(g){var a=g.touches;if(a.length===1){a=a[0];this._touchCurrentX=a.clientX;this._touchCurrentY=a.clientY;if(!this._touchScrollTimer&&g.timeStamp-this._touchStartTime<200){this._vScrollDiv.style.display=
-"block";if(!this._wrapMode)this._hScrollDiv.style.display="block";var c=this,d=this._getWindow();this._touchScrollTimer=d.setInterval(function(){var g=0,a=0;if(c._touching)g=c._touchStartX-c._touchCurrentX,a=c._touchStartY-c._touchCurrentY,c._touchSpeedX=g/10,c._touchSpeedY=a/10,c._touchStartX=c._touchCurrentX,c._touchStartY=c._touchCurrentY;else if(Math.abs(c._touchSpeedX)<0.1&&Math.abs(c._touchSpeedY)<0.1){c._vScrollDiv.style.display="none";c._hScrollDiv.style.display="none";d.clearInterval(c._touchScrollTimer);
-c._touchScrollTimer=null;return}else g=c._touchSpeedX*10,a=c._touchSpeedY*10,c._touchSpeedX*=0.95,c._touchSpeedY*=0.95;c._scrollView(g,a)},10)}this._touchScrollTimer&&g.preventDefault()}},_handleTouchEnd:function(g){if(g.touches.length===0)this._touching=!1},_doAction:function(g){var a,c,d=this._keyModes;for(c=d.length-1;c>=0;c--)if(a=d[c],typeof a.match==="function"&&(a=a.match(g),a!==void 0))return this.invokeAction(a);return!1},_doBackspace:function(g){var a=this._getSelection();if(a.isEmpty()){var c=
-this._model,d=a.getCaret(),b=c.getLineAtOffset(d);if(!g.count)g.count=1;for(g.count*=-1;g.count!==0;){var i=c.getLineStart(b);if(d===i)b>0&&(g.unit==="character"&&g.count++,b--,a.extend(c.getLineEnd(b)));else{var f=!1;this._expandTab&&g.unit==="character"&&(d-i)%this._tabSize===0&&(f=!/[^ ]/.test(c.getText(i,d)));f?a.extend(d-this._tabSize):(i=this._getLine(b),a.extend(i.getNextOffset(d,g)),i.destroy())}d=a.getCaret()}}this._modifyContent({text:"",start:a.start,end:a.end},!0);return!0},_doContent:function(g){var a=
-this._getSelection();if(this._overwriteMode&&a.isEmpty()){var c=this._model,d=c.getLineAtOffset(a.end);a.end<c.getLineEnd(d)&&(c=this._getLine(d),a.extend(c.getNextOffset(a.getCaret(),{unit:"character",count:1})),c.destroy())}this._modifyContent({text:g,start:a.start,end:a.end,_ignoreDOMSelection:!0},!0)},_doCopy:function(a){var c=this._getSelection();return!c.isEmpty()?this._setClipboardText(this._getBaseText(c.start,c.end),a):!0},_doCursorNext:function(a){if(!a.select&&this._clearSelection("next"))return!0;
-var c=this._model,d=this._getSelection(),b=d.getCaret(),i=c.getLineAtOffset(b);if(!a.count)a.count=1;for(;a.count!==0;){if(b===c.getLineEnd(i))if(i+1<c.getLineCount())a.unit==="character"&&a.count--,i++,d.extend(c.getLineStart(i));else break;else{var f=this._getLine(i);d.extend(f.getNextOffset(b,a));f.destroy()}b=d.getCaret()}a.select||d.collapse();this._setSelection(d,!0);return!0},_doCursorPrevious:function(a){if(!a.select&&this._clearSelection("previous"))return!0;var c=this._model,d=this._getSelection(),
-b=d.getCaret(),i=c.getLineAtOffset(b);if(!a.count)a.count=1;for(a.count*=-1;a.count!==0;){if(b===c.getLineStart(i))if(i>0)a.unit==="character"&&a.count++,i--,d.extend(c.getLineEnd(i));else break;else{var f=this._getLine(i);d.extend(f.getNextOffset(b,a));f.destroy()}b=d.getCaret()}a.select||d.collapse();this._setSelection(d,!0);return!0},_doCut:function(a){var c=this._getSelection();return!c.isEmpty()?(c=this._getBaseText(c.start,c.end),this._doContent(""),this._setClipboardText(c,a)):!0},_doDelete:function(a){var c=
-this._getSelection();if(c.isEmpty()){var d=this._model,b=c.getCaret(),i=d.getLineAtOffset(b);if(!a.count)a.count=1;for(;a.count!==0;){if(b===d.getLineEnd(i))i+1<d.getLineCount()&&(a.unit==="character"&&a.count--,i++,c.extend(d.getLineStart(i)));else{var f=this._getLine(i);c.extend(f.getNextOffset(b,a));f.destroy()}b=c.getCaret()}}this._modifyContent({text:"",start:c.start,end:c.end},!0);return!0},_doEnd:function(a){var c=this._getSelection(),d=this._model,b;if(a.ctrl)c.extend(d.getCharCount()),b=
-function(){};else{var i=c.getCaret(),f=d.getLineAtOffset(i);if(this._wrapMode){var m=this._getLine(f),i=m.getLineIndex(i),i=i===m.getLineCount()-1?d.getLineEnd(f):m.getLineStart(i+1)-1;m.destroy()}else i=d.getLineEnd(f);c.extend(i)}a.select||c.collapse();this._setSelection(c,!0,!0,b);return!0},_doEnter:function(a){var c=this._model,d=this._getSelection();this._doContent(c.getLineDelimiter());if(a&&a.noCursor)d.end=d.start,this._setSelection(d,!0);return!0},_doHome:function(a){var c=this._getSelection(),
-d=this._model,b;if(a.ctrl)c.extend(0),b=function(){};else{var i=c.getCaret(),f=d.getLineAtOffset(i);this._wrapMode?(d=this._getLine(f),i=d.getLineIndex(i),i=d.getLineStart(i),d.destroy()):i=d.getLineStart(f);c.extend(i)}a.select||c.collapse();this._setSelection(c,!0,!0,b);return!0},_doLineDown:function(a){var c=this._model,b=this._getSelection(),i=b.getCaret(),f=c.getLineAtOffset(i),m=this._getLine(f),n=this._columnX,l=1,e=!1;if(n===-1||a.wholeLine||a.select&&d.isIE)n=a.wholeLine?c.getLineEnd(f+1):
-i,n=m.getBoundingClientRect(n).left;(i=m.getLineIndex(i))<m.getLineCount()-1?l=m.getClientRects(i+1).top+1:(i=c.getLineCount()-1,e=f===i,a.count&&a.count>0?f=Math.min(f+a.count,i):f++);i=!1;if(e){if(a.select||d.isMac||d.isLinux)b.extend(c.getCharCount()),i=!0}else m.lineIndex!==f&&(m.destroy(),m=this._getLine(f)),b.extend(m.getOffset(n,l)),i=!0;i&&(a.select||b.collapse(),this._setSelection(b,!0,!0));this._columnX=n;m.destroy();return!0},_doLineUp:function(a){var c=this._model,b=this._getSelection(),
-i=b.getCaret(),f=c.getLineAtOffset(i),m=this._getLine(f),n=this._columnX,l=!1,e;if(n===-1||a.wholeLine||a.select&&d.isIE)n=a.wholeLine?c.getLineStart(f-1):i,n=m.getBoundingClientRect(n).left;(i=m.getLineIndex(i))>0?e=m.getClientRects(i-1).top+1:(l=f===0,l||(a.count&&a.count>0?f=Math.max(f-a.count,0):f--,e=this._getLineHeight(f)-1));i=!1;if(l){if(a.select||d.isMac||d.isLinux)b.extend(0),i=!0}else m.lineIndex!==f&&(m.destroy(),m=this._getLine(f)),b.extend(m.getOffset(n,e)),i=!0;i&&(a.select||b.collapse(),
-this._setSelection(b,!0,!0));this._columnX=n;m.destroy();return!0},_doNoop:function(){return!0},_doPageDown:function(a){var c=this,b=this._model,i=this._getSelection(),f=i.getCaret(),m=b.getLineAtOffset(f),n=b.getLineCount(),l=this._getScroll(),b=this._getClientHeight(),e,h;if(this._lineHeight){e=this._columnX;l=this._getBoundsAtOffset(f);if(e===-1||a.select&&d.isIE)e=l.left;f=this._getLineIndex(l.top+b);h=this._getLine(f);m=this._getLinePixel(f);f=h.getOffset(e,l.top+b-m);b=h.getBoundingClientRect(f);
-h.destroy();i.extend(f);a.select||i.collapse();this._setSelection(i,!0,!0,function(){c._columnX=e},b.top+m-l.top);return!0}if(m<n-1){var j=this._getLineHeight(),r=Math.min(n-m-1,Math.floor(b/j)),r=Math.max(1,r);e=this._columnX;if(e===-1||a.select&&d.isIE)h=this._getLine(m),e=h.getBoundingClientRect(f).left,h.destroy();h=this._getLine(m+r);i.extend(h.getOffset(e,0));h.destroy();a.select||i.collapse();a=n*j;f=l.y+r*j;f+b>a&&(f=a-b);this._setSelection(i,!0,!0,function(){c._columnX=e},f-l.y)}return!0},
-_doPageUp:function(a){var c=this,b=this._model,i=this._getSelection(),f=i.getCaret(),m=b.getLineAtOffset(f),n=this._getScroll(),l=this._getClientHeight(),e;if(this._lineHeight){e=this._columnX;n=this._getBoundsAtOffset(f);if(e===-1||a.select&&d.isIE)e=n.left;f=this._getLineIndex(n.bottom-l);b=this._getLine(f);m=this._getLinePixel(f);f=b.getOffset(e,n.bottom-l-m);l=b.getBoundingClientRect(f);b.destroy();i.extend(f);a.select||i.collapse();this._setSelection(i,!0,!0,function(){c._columnX=e},l.top+m-
-n.top);return!0}if(m>0){var h=this._getLineHeight(),l=Math.max(1,Math.min(m,Math.floor(l/h)));e=this._columnX;if(e===-1||a.select&&d.isIE)b=this._getLine(m),e=b.getBoundingClientRect(f).left,b.destroy();b=this._getLine(m-l);i.extend(b.getOffset(e,this._getLineHeight(m-l)-1));b.destroy();a.select||i.collapse();a=Math.max(0,n.y-l*h);this._setSelection(i,!0,!0,function(){c._columnX=e},a-n.y)}return!0},_doPaste:function(a){var c=this;return this._getClipboardText(a,function(a){a&&(d.isLinux&&c._lastMouseButton===
-2&&(new Date).getTime()-c._lastMouseTime<=c._clickTime&&c._setSelectionTo(c._lastMouseX,c._lastMouseY),c._doContent(a))})!==null},_doScroll:function(a){var c=a.type,d=this._model,b=d.getLineCount(),a=this._getClientHeight(),i=this._getLineHeight();b*=i;var f=this._getScroll().y,m;switch(c){case "textStart":m=0;break;case "textEnd":m=b-a;break;case "pageDown":m=f+a;break;case "pageUp":m=f-a;break;case "lineDown":m=f+i;break;case "lineUp":m=f-i;break;case "centerLine":c=this._getSelection(),m=d.getLineAtOffset(c.start),
-d=(d.getLineAtOffset(c.end)-m+1)*i,m=m*i-a/2+d/2}m!==void 0&&(m=Math.min(Math.max(0,m),b-a),this._scrollViewAnimated(0,m-f,function(){}));return!0},_doSelectAll:function(){var a=this._model,c=this._getSelection();c.setCaret(0);c.extend(a.getCharCount());this._setSelection(c,!1);return!0},_doTab:function(){if(this._tabMode&&!this._readonly){var a="\t";if(this._expandTab)var c=this._model,a=this._getSelection().getCaret(),d=c.getLineAtOffset(a),c=c.getLineStart(d),a=Array(this._tabSize-(a-c)%this._tabSize+
-1).join(" ");this._doContent(a);return!0}},_doShiftTab:function(){return!this._tabMode||this._readonly?void 0:!0},_doOverwriteMode:function(){if(!this._readonly)return this.setOptions({overwriteMode:!this.getOptions("overwriteMode")}),!0},_doTabMode:function(){this._tabMode=!this._tabMode;return!0},_doWrapMode:function(){this.setOptions({wrapMode:!this.getOptions("wrapMode")});return!0},_autoScroll:function(){var a=this._model,c=this._getSelection(),b=this.convert({x:this._autoScrollX,y:this._autoScrollY},
-"page","document"),i=c.getCaret(),f=a.getLineCount(),m=a.getLineAtOffset(i),n;if(this._autoScrollDir==="up"||this._autoScrollDir==="down")i=this._autoScrollY/this._getLineHeight(),i=i<0?Math.floor(i):Math.ceil(i),n=Math.max(0,Math.min(f-1,m+i));else if(this._autoScrollDir==="left"||this._autoScrollDir==="right")n=this._getLineIndex(b.y),m=this._getLine(m),b.x+=m.getBoundingClientRect(i,!1).left,m.destroy();n===0&&(d.isMac||d.isLinux)?c.extend(0):n===f-1&&(d.isMac||d.isLinux)?c.extend(a.getCharCount()):
-(m=this._getLine(n),c.extend(m.getOffset(b.x,b.y-this._getLinePixel(n))),m.destroy());this._setSelection(c,!0)},_autoScrollTimer:function(){this._autoScroll();var a=this;this._autoScrollTimerID=this._getWindow().setTimeout(function(){a._autoScrollTimer()},this._AUTO_SCROLL_RATE)},_calculateLineHeightTimer:function(a){if(this._lineHeight&&!this._calculateLHTimer){var c=this._model.getLineCount(),b=0;if(a){for(var a=0,d=(new Date).getTime(),i=0;b<c;)if(this._lineHeight[b]||(a++,i||(i=b),this._lineHeight[b]=
-this._calculateLineHeight(b)),b++,(new Date).getTime()-d>100)break;this.redrawRulers(0,c);this._queueUpdate()}a=this._getWindow();if(b!==c){var f=this;this._calculateLHTimer=a.setTimeout(function(){f._calculateLHTimer=null;f._calculateLineHeightTimer(!0)},0)}else if(this._calculateLHTimer)a.clearTimeout(this._calculateLHTimer),this._calculateLHTimer=void 0}},_calculateLineHeight:function(a){var a=this._getLine(a),c=a.getBoundingClientRect();a.destroy();return Math.max(1,Math.ceil(c.bottom-c.top))},
-_calculateMetrics:function(){var a=this._clientDiv,c=a.ownerDocument,b=d.createElement(c,"div");b.style.lineHeight="normal";var i={type:"LineStyle",textView:this,0:0,lineText:this._model.getLine(0),lineStart:0};this.onLineStyle(i);j(i.style,b);b.style.position="fixed";b.style.left="-1000px";var f=d.createElement(c,"span");f.appendChild(c.createTextNode(" "));b.appendChild(f);var l=d.createElement(c,"span");l.style.fontStyle="italic";l.appendChild(c.createTextNode(" "));b.appendChild(l);var e=d.createElement(c,
-"span");e.style.fontWeight="bold";e.appendChild(c.createTextNode(" "));b.appendChild(e);i=d.createElement(c,"span");i.style.fontWeight="bold";i.style.fontStyle="italic";i.appendChild(c.createTextNode(" "));b.appendChild(i);a.appendChild(b);var h=b.getBoundingClientRect(),f=f.getBoundingClientRect(),l=l.getBoundingClientRect(),e=e.getBoundingClientRect(),i=i.getBoundingClientRect(),f=f.bottom-f.top,l=l.bottom-l.top,e=e.bottom-e.top,r=i.bottom-i.top,q=0,i=h.bottom-h.top<=0,h=Math.max(1,h.bottom-h.top);
-l>f&&(q=1);e>l&&(q=2);r>e&&(q=3);var k;if(q!==0){k={style:{}};if((q&1)!==0)k.style.fontStyle="italic";if((q&2)!==0)k.style.fontWeight="bold"}f=n(b);a.removeChild(b);l=m(this._viewDiv);b=d.createElement(c,"div");b.style.position="fixed";b.style.left="-1000px";b.style.paddingLeft=l.left+"px";b.style.paddingTop=l.top+"px";b.style.paddingRight=l.right+"px";b.style.paddingBottom=l.bottom+"px";b.style.width="100px";b.style.height="100px";e=d.createElement(c,"div");e.style.width="100%";e.style.height="100%";
-b.appendChild(e);a.appendChild(b);c=b.getBoundingClientRect();l=e.getBoundingClientRect();b.style.overflow="hidden";e.style.height="200px";e=b.clientWidth;b.style.overflow="scroll";r=b.clientWidth;a.removeChild(b);l={left:l.left-c.left,top:l.top-c.top,right:c.right-l.right,bottom:c.bottom-l.bottom};return{lineHeight:h,largestFontStyle:k,lineTrim:f,viewPadding:l,scrollWidth:e-r,invalid:i}},_cancelAnimation:function(){if(this._animation)this._animation.stop(),this._animation=null},_clearSelection:function(a){var c=
-this._getSelection();if(c.isEmpty())return!1;a==="next"?c.start=c.end:c.end=c.start;this._setSelection(c,!0);return!0},_commitIME:function(){if(this._imeOffset!==-1){this._scrollDiv.focus();this._clientDiv.focus();var a=this._model,c=a.getLineAtOffset(this._imeOffset),b=a.getLineStart(c),d=this._getDOMText(this._getLineNode(c)).text,a=a.getLine(c),b=this._imeOffset-b,a=b+d.length-a.length;b!==a&&this._doContent(d.substring(b,a));this._imeOffset=-1}},_createActions:function(){this.addKeyMode(new o.DefaultKeyMode);
-var g=this;this._actions={noop:{defaultHandler:function(){return g._doNoop()}},lineUp:{defaultHandler:function(c){return g._doLineUp(a(c,{select:!1}))},actionDescription:{name:k.lineUp}},lineDown:{defaultHandler:function(c){return g._doLineDown(a(c,{select:!1}))},actionDescription:{name:k.lineDown}},lineStart:{defaultHandler:function(c){return g._doHome(a(c,{select:!1,ctrl:!1}))},actionDescription:{name:k.lineStart}},lineEnd:{defaultHandler:function(c){return g._doEnd(a(c,{select:!1,ctrl:!1}))},actionDescription:{name:k.lineEnd}},
-charPrevious:{defaultHandler:function(c){return g._doCursorPrevious(a(c,{select:!1,unit:"character"}))},actionDescription:{name:k.charPrevious}},charNext:{defaultHandler:function(c){return g._doCursorNext(a(c,{select:!1,unit:"character"}))},actionDescription:{name:k.charNext}},pageUp:{defaultHandler:function(c){return g._doPageUp(a(c,{select:!1}))},actionDescription:{name:k.pageUp}},pageDown:{defaultHandler:function(c){return g._doPageDown(a(c,{select:!1}))},actionDescription:{name:k.pageDown}},scrollPageUp:{defaultHandler:function(c){return g._doScroll(a(c,
-{type:"pageUp"}))},actionDescription:{name:k.scrollPageUp}},scrollPageDown:{defaultHandler:function(c){return g._doScroll(a(c,{type:"pageDown"}))},actionDescription:{name:k.scrollPageDown}},scrollLineUp:{defaultHandler:function(c){return g._doScroll(a(c,{type:"lineUp"}))},actionDescription:{name:k.scrollLineUp}},scrollLineDown:{defaultHandler:function(c){return g._doScroll(a(c,{type:"lineDown"}))},actionDescription:{name:k.scrollLineDown}},wordPrevious:{defaultHandler:function(c){return g._doCursorPrevious(a(c,
-{select:!1,unit:"word"}))},actionDescription:{name:k.wordPrevious}},wordNext:{defaultHandler:function(c){return g._doCursorNext(a(c,{select:!1,unit:"word"}))},actionDescription:{name:k.wordNext}},textStart:{defaultHandler:function(c){return g._doHome(a(c,{select:!1,ctrl:!0}))},actionDescription:{name:k.textStart}},textEnd:{defaultHandler:function(c){return g._doEnd(a(c,{select:!1,ctrl:!0}))},actionDescription:{name:k.textEnd}},scrollTextStart:{defaultHandler:function(c){return g._doScroll(a(c,{type:"textStart"}))},
-actionDescription:{name:k.scrollTextStart}},scrollTextEnd:{defaultHandler:function(c){return g._doScroll(a(c,{type:"textEnd"}))},actionDescription:{name:k.scrollTextEnd}},centerLine:{defaultHandler:function(c){return g._doScroll(a(c,{type:"centerLine"}))},actionDescription:{name:k.centerLine}},selectLineUp:{defaultHandler:function(c){return g._doLineUp(a(c,{select:!0}))},actionDescription:{name:k.selectLineUp}},selectLineDown:{defaultHandler:function(c){return g._doLineDown(a(c,{select:!0}))},actionDescription:{name:k.selectLineDown}},
-selectWholeLineUp:{defaultHandler:function(c){return g._doLineUp(a(c,{select:!0,wholeLine:!0}))},actionDescription:{name:k.selectWholeLineUp}},selectWholeLineDown:{defaultHandler:function(c){return g._doLineDown(a(c,{select:!0,wholeLine:!0}))},actionDescription:{name:k.selectWholeLineDown}},selectLineStart:{defaultHandler:function(c){return g._doHome(a(c,{select:!0,ctrl:!1}))},actionDescription:{name:k.selectLineStart}},selectLineEnd:{defaultHandler:function(c){return g._doEnd(a(c,{select:!0,ctrl:!1}))},
-actionDescription:{name:k.selectLineEnd}},selectCharPrevious:{defaultHandler:function(c){return g._doCursorPrevious(a(c,{select:!0,unit:"character"}))},actionDescription:{name:k.selectCharPrevious}},selectCharNext:{defaultHandler:function(c){return g._doCursorNext(a(c,{select:!0,unit:"character"}))},actionDescription:{name:k.selectCharNext}},selectPageUp:{defaultHandler:function(c){return g._doPageUp(a(c,{select:!0}))},actionDescription:{name:k.selectPageUp}},selectPageDown:{defaultHandler:function(c){return g._doPageDown(a(c,
-{select:!0}))},actionDescription:{name:k.selectPageDown}},selectWordPrevious:{defaultHandler:function(c){return g._doCursorPrevious(a(c,{select:!0,unit:"word"}))},actionDescription:{name:k.selectWordPrevious}},selectWordNext:{defaultHandler:function(c){return g._doCursorNext(a(c,{select:!0,unit:"word"}))},actionDescription:{name:k.selectWordNext}},selectTextStart:{defaultHandler:function(c){return g._doHome(a(c,{select:!0,ctrl:!0}))},actionDescription:{name:k.selectTextStart}},selectTextEnd:{defaultHandler:function(c){return g._doEnd(a(c,
-{select:!0,ctrl:!0}))},actionDescription:{name:k.selectTextEnd}},deletePrevious:{defaultHandler:function(c){return g._doBackspace(a(c,{unit:"character"}))},actionDescription:{name:k.deletePrevious}},deleteNext:{defaultHandler:function(c){return g._doDelete(a(c,{unit:"character"}))},actionDescription:{name:k.deleteNext}},deleteWordPrevious:{defaultHandler:function(c){return g._doBackspace(a(c,{unit:"word"}))},actionDescription:{name:k.deleteWordPrevious}},deleteWordNext:{defaultHandler:function(c){return g._doDelete(a(c,
-{unit:"word"}))},actionDescription:{name:k.deleteWordNext}},deleteLineStart:{defaultHandler:function(c){return g._doBackspace(a(c,{unit:"line"}))},actionDescription:{name:k.deleteLineStart}},deleteLineEnd:{defaultHandler:function(c){return g._doDelete(a(c,{unit:"line"}))},actionDescription:{name:k.deleteLineEnd}},tab:{defaultHandler:function(){return g._doTab()},actionDescription:{name:k.tab}},shiftTab:{defaultHandler:function(){return g._doShiftTab()},actionDescription:{name:k.shiftTab}},enter:{defaultHandler:function(){return g._doEnter()},
-actionDescription:{name:k.enter}},enterNoCursor:{defaultHandler:function(c){return g._doEnter(a(c,{noCursor:!0}))},actionDescription:{name:k.enterNoCursor}},selectAll:{defaultHandler:function(){return g._doSelectAll()},actionDescription:{name:k.selectAll}},copy:{defaultHandler:function(){return g._doCopy()},actionDescription:{name:k.copy}},cut:{defaultHandler:function(){return g._doCut()},actionDescription:{name:k.cut}},paste:{defaultHandler:function(){return g._doPaste()},actionDescription:{name:k.paste}},
-toggleOverwriteMode:{defaultHandler:function(){return g._doOverwriteMode()},actionDescription:{name:k.toggleOverwriteMode}},toggleTabMode:{defaultHandler:function(){return g._doTabMode()},actionDescription:{name:k.toggleTabMode}},toggleWrapMode:{defaultHandler:function(){return g._doWrapMode()},actionDescription:{name:k.toggleWrapMode}}}},_createRuler:function(a,c){if(this._clientDiv){var b=a.getLocation()==="left"?this._leftDiv:this._rightDiv;b.style.display="block";var i=d.createElement(b.ownerDocument,
-"div");i._ruler=a;i.rulerChanged=!0;i.style.position="relative";i.style.cssFloat="left";i.style.styleFloat="left";i.style.borderWidth="0px";i.style.margin="0px";i.style.padding="0px";i.style.outline="none";if(c===void 0||c<0||c>=b.children.length)b.appendChild(i);else{for(var f=b.firstChild;f&&--c>0;)f=f.nextSibling;b.insertBefore(i,f)}}},_createView:function(){if(!this._clientDiv){for(var a=this._parent;a.hasChildNodes();)a.removeChild(a.lastChild);var c=a.ownerDocument,b=d.createElement(c,"div");
-this._rootDiv=b;b.tabIndex=-1;b.style.position="relative";b.style.overflow="hidden";b.style.width="100%";b.style.height="100%";b.style.overflow="hidden";b.style.WebkitTextSizeAdjust="100%";b.setAttribute("role","application");a.appendChild(b);a=d.createElement(c,"div");a.className="textviewLeftRuler";this._leftDiv=a;a.tabIndex=-1;a.style.overflow="hidden";a.style.MozUserSelect="none";a.style.WebkitUserSelect="none";a.style.position="absolute";a.style.top="0px";a.style.bottom="0px";a.style.cursor=
-"default";a.style.display="none";a.setAttribute("aria-hidden","true");b.appendChild(a);a=d.createElement(c,"div");a.className="textviewScroll";this._viewDiv=a;a.tabIndex=-1;a.style.overflow="auto";a.style.position="absolute";a.style.top="0px";a.style.bottom="0px";a.style.borderWidth="0px";a.style.margin="0px";a.style.outline="none";a.style.background="transparent";if(d.isMac&&d.isWebkit)a.style.pointerEvents="none",a.style.zIndex="2";b.appendChild(a);var i=d.createElement(c,"div");i.className="textviewRightRuler";
-this._rightDiv=i;i.tabIndex=-1;i.style.display="none";i.style.overflow="hidden";i.style.MozUserSelect="none";i.style.WebkitUserSelect="none";i.style.position="absolute";i.style.top="0px";i.style.bottom="0px";i.style.cursor="default";i.style.right="0px";i.setAttribute("aria-hidden","true");b.appendChild(i);this._scrollDiv=i=d.createElement(c,"div");i.style.margin="0px";i.style.borderWidth="0px";i.style.padding="0px";a.appendChild(i);if(d.isFirefox)this._clipboardDiv=a=d.createElement(c,"div"),a.style.position=
-"fixed",a.style.whiteSpace="pre",a.style.left="-1000px",b.appendChild(a);if(!d.isIE&&!d.isIOS)this._clipDiv=a=d.createElement(c,"div"),a.style.position="absolute",a.style.overflow="hidden",a.style.margin="0px",a.style.borderWidth="0px",a.style.padding="0px",a.style.background="transparent",b.appendChild(a),this._clipScrollDiv=i=d.createElement(c,"div"),i.style.position="absolute",i.style.height="1px",i.style.top="-1000px",i.style.background="transparent",a.appendChild(i);this._setFullSelection(this._fullSelection,
-!0);a=d.createElement(c,"div");a.className="textviewContent";this._clientDiv=a;a.style.position="absolute";a.style.borderWidth="0px";a.style.margin="0px";a.style.padding="0px";a.style.outline="none";a.style.zIndex="1";a.style.WebkitUserSelect="text";a.setAttribute("spellcheck","false");if(d.isIOS||d.isAndroid)a.style.WebkitTapHighlightColor="transparent";(this._clipDiv||b).appendChild(a);if(d.isIOS||d.isAndroid)this._vScrollDiv=i=d.createElement(c,"div"),i.style.position="absolute",i.style.borderWidth=
-"1px",i.style.borderColor="white",i.style.borderStyle="solid",i.style.borderRadius="4px",i.style.backgroundColor="black",i.style.opacity="0.5",i.style.margin="0px",i.style.padding="0px",i.style.outline="none",i.style.zIndex="3",i.style.width="8px",i.style.display="none",b.appendChild(i),this._hScrollDiv=i=d.createElement(c,"div"),i.style.position="absolute",i.style.borderWidth="1px",i.style.borderColor="white",i.style.borderStyle="solid",i.style.borderRadius="4px",i.style.backgroundColor="black",
-i.style.opacity="0.5",i.style.margin="0px",i.style.padding="0px",i.style.outline="none",i.style.zIndex="3",i.style.height="8px",i.style.display="none",b.appendChild(i);if(d.isFirefox&&!a.setCapture)this._overlayDiv=c=d.createElement(c,"div"),c.style.position=a.style.position,c.style.borderWidth=a.style.borderWidth,c.style.margin=a.style.margin,c.style.padding=a.style.padding,c.style.cursor="text",c.style.zIndex="2",(this._clipDiv||b).appendChild(c);a.contentEditable="true";a.setAttribute("role","textbox");
-a.setAttribute("aria-multiline","true");this._setWrapMode(this._wrapMode,!0);this._setReadOnly(this._readonly);this._setThemeClass(this._themeClass,!0);this._setTabSize(this._tabSize,!0);this._hookEvents();b=this._rulers;for(c=0;c<b.length;c++)this._createRuler(b[c]);this._update()}},_defaultOptions:function(){return{parent:{value:void 0,update:null},model:{value:void 0,update:this.setModel},scrollAnimation:{value:0,update:null},readonly:{value:!1,update:this._setReadOnly},fullSelection:{value:!0,
-update:this._setFullSelection},tabMode:{value:!0,update:null},tabSize:{value:8,update:this._setTabSize},expandTab:{value:!1,update:null},overwriteMode:{value:!1,update:this._setOverwriteMode},blockCursorVisible:{value:!1,update:this._setBlockCursor},wrapMode:{value:!1,update:this._setWrapMode},wrappable:{value:!1,update:null},theme:{value:b.TextTheme.getTheme(),update:this._setTheme},themeClass:{value:void 0,update:this._setThemeClass}}},_destroyRuler:function(a){var c=a.getLocation()==="left"?this._leftDiv:
-this._rightDiv;if(c)for(var b=c.firstChild;b;){if(b._ruler===a){b._ruler=void 0;c.removeChild(b);if(c.children.length===0)c.style.display="none";break}b=b.nextSibling}},_destroyView:function(){if(this._clientDiv){this._setGrab(null);this._unhookEvents();var a=this._getWindow();if(this._autoScrollTimerID)a.clearTimeout(this._autoScrollTimerID),this._autoScrollTimerID=null;if(this._updateTimer)a.clearTimeout(this._updateTimer),this._updateTimer=null;a=this._rootDiv;a.parentNode.removeChild(a);this._hScrollDiv=
-this._vScrollDiv=this._rightDiv=this._leftDiv=this._overlayDiv=this._clientDiv=this._clipScrollDiv=this._clipDiv=this._viewDiv=this._scrollDiv=this._rootDiv=this._clipboardDiv=this._selDiv3=this._selDiv2=this._selDiv1=null}},_doAutoScroll:function(a,c,b){this._autoScrollDir=a;this._autoScrollX=c;this._autoScrollY=b;this._autoScrollTimerID||this._autoScrollTimer()},_endAutoScroll:function(){this._autoScrollTimerID&&this._getWindow().clearTimeout(this._autoScrollTimerID);this._autoScrollTimerID=this._autoScrollDir=
-void 0},_fixCaret:function(){var a=this._clientDiv;if(a){var c=this._hasFocus;this._ignoreFocus=!0;c&&a.blur();a.contentEditable=!1;a.contentEditable=!0;c&&a.focus();this._ignoreFocus=!1}},_getBaseText:function(a,c){var b=this._model;b.getBaseModel&&(a=b.mapOffset(a),c=b.mapOffset(c),b=b.getBaseModel());return b.getText(a,c)},_getBottomIndex:function(a){var c=this._bottomChild;if(a&&this._getClientHeight()>this._getLineHeight()){var a=c.getBoundingClientRect(),b=this._clientDiv.getBoundingClientRect();
-a.bottom>b.bottom&&(c=this._getLinePrevious(c)||c)}return c.lineIndex},_getBoundsAtOffset:function(a){var c=this._getLine(this._model.getLineAtOffset(a)),a=c.getBoundingClientRect(a),b=this._getLinePixel(c.lineIndex);a.top+=b;a.bottom+=b;c.destroy();return a},_getClientHeight:function(){var a=this._getViewPadding();return Math.max(0,this._viewDiv.clientHeight-a.top-a.bottom)},_getClientWidth:function(){var a=this._getViewPadding();return Math.max(0,this._viewDiv.clientWidth-a.left-a.right)},_getClipboardText:function(a,
-c){var b=this._model.getLineDelimiter(),f,m,n=this._getWindow();if(n.clipboardData)return f=[],m=n.clipboardData.getData("Text"),i(m,function(a){f.push(a)},function(){f.push(b)}),m=f.join(""),c&&c(m),m;if(d.isFirefox){this._ignoreFocus=!0;var l=this._clipboardDiv;l.innerHTML="<pre contenteditable=''></pre>";l.firstChild.focus();var e=this,h=function(){var a=e._getTextFromElement(l);l.innerHTML="";f=[];i(a,function(a){f.push(a)},function(){f.push(b)});return f.join("")},j=!1;this._ignorePaste=!0;if(!d.isLinux||
-this._lastMouseButton!==2)try{j=l.ownerDocument.execCommand("paste",!1,null)}catch(r){j=l.childNodes.length>1||l.firstChild&&l.firstChild.childNodes.length>0}this._ignorePaste=!1;if(!j)return a?(n.setTimeout(function(){e.focus();(m=h())&&c&&c(m);e._ignoreFocus=!1},0),null):(this.focus(),this._ignoreFocus=!1,"");this.focus();this._ignoreFocus=!1;(m=h())&&c&&c(m);return m}return a&&a.clipboardData?(f=[],m=a.clipboardData.getData("text/plain"),i(m,function(a){f.push(a)},function(){f.push(b)}),(m=f.join(""))&&
-c&&c(m),m):""},_getDOMText:function(a,c){for(var b=a.firstChild,d="",i=0;b;){var f;if(!b.ignore)if(b.ignoreChars){f=b.lastChild;for(var m=0,n=[],l=-1;f;){for(var e=f.data,h=e.length-1;h>=0;h--){var j=e.substring(h,h+1);m<b.ignoreChars&&(j===" "||j==="\u200c"||j==="\ufeff")?m++:n.push(j==="\u00a0"?"\t":j)}if(c===f)l=n.length;f=f.previousSibling}n=n.reverse().join("");l!==-1&&(i=d.length+n.length-l);d+=n}else for(f=b.firstChild;f;){if(c===f)i=d.length;d+=f.data;f=f.nextSibling}b=b.nextSibling}return{text:d,
-offset:i}},_getTextFromElement:function(a){var c=a.ownerDocument,b=c.defaultView;if(!b.getSelection)return a.innerText||a.textContent;c=c.createRange();c.selectNode(a);var a=b.getSelection(),b=[],d;for(d=0;d<a.rangeCount;d++)b.push(a.getRangeAt(d));this._ignoreSelect=!0;a.removeAllRanges();a.addRange(c);c=a.toString();a.removeAllRanges();for(d=0;d<b.length;d++)a.addRange(b[d]);this._ignoreSelect=!1;return c},_getViewPadding:function(){return this._metrics.viewPadding},_getLine:function(a){var c=this._getLineNode(a);
-return c&&!c.lineChanged&&!c.lineRemoved?c._line:new q(this,a)},_getLineHeight:function(a,c){if(a!==void 0&&this._lineHeight){var b=this._lineHeight[a];if(b)return b;if(c||c===void 0)return this._lineHeight[a]=this._calculateLineHeight(a)}return this._metrics.lineHeight},_getLineNode:function(a){for(var c=this._clientDiv.firstChild;c;){if(a===c.lineIndex)return c;c=c.nextSibling}},_getLineNext:function(a){for(a=a?a.nextSibling:this._clientDiv.firstChild;a&&a.lineIndex===-1;)a=a.nextSibling;return a},
-_getLinePrevious:function(a){for(a=a?a.previousSibling:this._clientDiv.lastChild;a&&a.lineIndex===-1;)a=a.previousSibling;return a},_getLinePixel:function(a){a=Math.min(Math.max(0,a),this._model.getLineCount());if(this._lineHeight){var c=this._getTopIndex(),b=-this._topIndexY+this._getScroll().y;if(a>c)for(;c<a;c++)b+=this._getLineHeight(c);else for(c-=1;c>=a;c--)b-=this._getLineHeight(c);return b}return this._getLineHeight()*a},_getLineIndex:function(a){var c,b=0,d=this._model.getLineCount();if(this._lineHeight){var b=
-this._getTopIndex(),i=-this._topIndexY+this._getScroll().y;if(a!==i)if(a<i)for(;a<i&&b>0;)a+=this._getLineHeight(--b);else for(c=this._getLineHeight(b);a-c>=i&&b<d-1;)a-=c,c=this._getLineHeight(++b)}else c=this._getLineHeight(),b=Math.floor(a/c);return Math.max(0,Math.min(d-1,b))},_getScroll:function(a){(a===void 0||a)&&this._cancelAnimation();a=this._viewDiv;return{x:a.scrollLeft,y:a.scrollTop}},_getSelection:function(){return this._selection.clone()},_getTopIndex:function(a){var c=this._topChild;
-if(a&&this._getClientHeight()>this._getLineHeight()){var a=c.getBoundingClientRect(),b=this._getViewPadding(),d=this._viewDiv.getBoundingClientRect();a.top<d.top+b.top&&(c=this._getLineNext(c)||c)}return c.lineIndex},_hookEvents:function(){var a=this;this._modelListener={onChanging:function(c){a._onModelChanging(c)},onChanged:function(c){a._onModelChanged(c)}};this._model.addEventListener("preChanging",this._modelListener.onChanging);this._model.addEventListener("postChanged",this._modelListener.onChanged);
-this._themeListener={onChanged:function(){a._setThemeClass(a._themeClass)}};this._theme.addEventListener("ThemeChanged",this._themeListener.onChanged);var c=this._handlers=[],b=this._clientDiv,i=this._viewDiv,f=this._rootDiv,m=this._overlayDiv||b,n=b.ownerDocument,l=this._getWindow(),e=d.isIE?n:l;c.push({target:l,type:"resize",handler:function(c){return a._handleResize(c?c:l.event)}});c.push({target:b,type:"blur",handler:function(c){return a._handleBlur(c?c:l.event)}});c.push({target:b,type:"focus",
-handler:function(c){return a._handleFocus(c?c:l.event)}});c.push({target:i,type:"focus",handler:function(){b.focus()}});c.push({target:i,type:"scroll",handler:function(c){return a._handleScroll(c?c:l.event)}});c.push({target:b,type:"textInput",handler:function(c){return a._handleTextInput(c?c:l.event)}});c.push({target:b,type:"keydown",handler:function(c){return a._handleKeyDown(c?c:l.event)}});c.push({target:b,type:"keypress",handler:function(c){return a._handleKeyPress(c?c:l.event)}});c.push({target:b,
-type:"keyup",handler:function(c){return a._handleKeyUp(c?c:l.event)}});c.push({target:b,type:"contextmenu",handler:function(c){return a._handleContextMenu(c?c:l.event)}});c.push({target:b,type:"copy",handler:function(c){return a._handleCopy(c?c:l.event)}});c.push({target:b,type:"cut",handler:function(c){return a._handleCut(c?c:l.event)}});c.push({target:b,type:"paste",handler:function(c){return a._handlePaste(c?c:l.event)}});if(d.isIOS||d.isAndroid)c.push({target:n,type:"selectionchange",handler:function(c){return a._handleSelectionChange(c?
-c:l.event)}}),c.push({target:b,type:"touchstart",handler:function(c){return a._handleTouchStart(c?c:l.event)}}),c.push({target:b,type:"touchmove",handler:function(c){return a._handleTouchMove(c?c:l.event)}}),c.push({target:b,type:"touchend",handler:function(c){return a._handleTouchEnd(c?c:l.event)}});else{c.push({target:b,type:"selectstart",handler:function(c){return a._handleSelectStart(c?c:l.event)}});c.push({target:b,type:"mousedown",handler:function(c){return a._handleMouseDown(c?c:l.event)}});
-c.push({target:b,type:"mouseover",handler:function(c){return a._handleMouseOver(c?c:l.event)}});c.push({target:b,type:"mouseout",handler:function(c){return a._handleMouseOut(c?c:l.event)}});c.push({target:e,type:"mouseup",handler:function(c){return a._handleMouseUp(c?c:l.event)}});c.push({target:e,type:"mousemove",handler:function(c){return a._handleMouseMove(c?c:l.event)}});c.push({target:f,type:"mousedown",handler:function(c){return a._handleRootMouseDown(c?c:l.event)}});c.push({target:f,type:"mouseup",
-handler:function(c){return a._handleRootMouseUp(c?c:l.event)}});c.push({target:m,type:"dragstart",handler:function(c){return a._handleDragStart(c?c:l.event)}});c.push({target:m,type:"drag",handler:function(c){return a._handleDrag(c?c:l.event)}});c.push({target:m,type:"dragend",handler:function(c){return a._handleDragEnd(c?c:l.event)}});c.push({target:m,type:"dragenter",handler:function(c){return a._handleDragEnter(c?c:l.event)}});c.push({target:m,type:"dragover",handler:function(c){return a._handleDragOver(c?
-c:l.event)}});c.push({target:m,type:"dragleave",handler:function(c){return a._handleDragLeave(c?c:l.event)}});c.push({target:m,type:"drop",handler:function(c){return a._handleDrop(c?c:l.event)}});c.push({target:this._clientDiv,type:d.isFirefox?"DOMMouseScroll":"mousewheel",handler:function(c){return a._handleMouseWheel(c?c:l.event)}});if(d.isFirefox&&(!d.isWindows||d.isFirefox>=15))(i=l.MutationObserver||l.MozMutationObserver)?(this._mutationObserver=new i(function(c){a._handleDataModified(c)}),this._mutationObserver.observe(b,
-{subtree:!0,characterData:!0})):c.push({target:this._clientDiv,type:"DOMCharacterDataModified",handler:function(c){return a._handleDataModified(c?c:l.event)}});this._overlayDiv&&(c.push({target:this._overlayDiv,type:"mousedown",handler:function(c){return a._handleMouseDown(c?c:l.event)}}),c.push({target:this._overlayDiv,type:"mouseover",handler:function(c){return a._handleMouseOver(c?c:l.event)}}),c.push({target:this._overlayDiv,type:"mouseout",handler:function(c){return a._handleMouseOut(c?c:l.event)}}),
-c.push({target:this._overlayDiv,type:"contextmenu",handler:function(c){return a._handleContextMenu(c?c:l.event)}}));this._isW3CEvents||c.push({target:this._clientDiv,type:"dblclick",handler:function(c){return a._handleDblclick(c?c:l.event)}})}i=this._leftDiv;f=this._rightDiv;d.isIE&&c.push({target:i,type:"selectstart",handler:function(){return!1}});c.push({target:i,type:d.isFirefox?"DOMMouseScroll":"mousewheel",handler:function(c){return a._handleMouseWheel(c?c:l.event)}});c.push({target:i,type:"click",
-handler:function(c){a._handleRulerEvent(c?c:l.event)}});c.push({target:i,type:"dblclick",handler:function(c){a._handleRulerEvent(c?c:l.event)}});c.push({target:i,type:"mousemove",handler:function(c){a._handleRulerEvent(c?c:l.event)}});c.push({target:i,type:"mouseover",handler:function(c){a._handleRulerEvent(c?c:l.event)}});c.push({target:i,type:"mouseout",handler:function(c){a._handleRulerEvent(c?c:l.event)}});d.isIE&&c.push({target:f,type:"selectstart",handler:function(){return!1}});c.push({target:f,
-type:d.isFirefox?"DOMMouseScroll":"mousewheel",handler:function(c){return a._handleMouseWheel(c?c:l.event)}});c.push({target:f,type:"click",handler:function(c){a._handleRulerEvent(c?c:l.event)}});c.push({target:f,type:"dblclick",handler:function(c){a._handleRulerEvent(c?c:l.event)}});c.push({target:f,type:"mousemove",handler:function(c){a._handleRulerEvent(c?c:l.event)}});c.push({target:f,type:"mouseover",handler:function(c){a._handleRulerEvent(c?c:l.event)}});c.push({target:f,type:"mouseout",handler:function(c){a._handleRulerEvent(c?
-c:l.event)}});for(i=0;i<c.length;i++)f=c[i],h(f.target,f.type,f.handler,f.capture)},_getWindow:function(){return this._parent.ownerDocument.defaultView||this._parent.ownerDocument.parentWindow},_ignoreEvent:function(a){for(a=a.target;a&&a!==this._clientDiv;){if(a.ignore)return!0;a=a.parentNode}return!1},_init:function(a){var c=a.parent;typeof c==="string"&&(c=(a.document||document).getElementById(c));if(!c)throw"no parent";a.parent=c;a.model=a.model||new p.TextModel;var b=this._defaultOptions(),i;
-for(i in b)b.hasOwnProperty(i)&&(this["_"+i]=a[i]!==void 0?a[i]:b[i].value);this._keyModes=[];this._rulers=[];this._selection=new l(0,0,!1);this._linksVisible=!1;this._maxLineWidth=this._redrawCount=0;this._maxLineIndex=-1;this._ignoreSelect=!0;this._hasFocus=this._ignoreFocus=!1;this._dragOffset=this._columnX=-1;this._isRangeRects=(!d.isIE||d.isIE>=9)&&typeof c.ownerDocument.createRange().getBoundingClientRect==="function";this._isW3CEvents=c.addEventListener;this._autoScrollTimerID=this._autoScrollY=
-this._autoScrollX=null;this._AUTO_SCROLL_RATE=50;this._mouseUpClosure=this._moseMoveClosure=this._grabControl=null;this._clickCount=this._lastMouseTime=this._lastMouseY=this._lastMouseX=0;this._clickTime=250;this._clickDist=5;this._isMouseDown=!1;this._doubleClickSelection=null;this._vScroll=this._hScroll=0;this._imeOffset=-1;this._createActions();this._createView()},_modifyContent:function(a,c){if(!this._readonly||a._code)if(a.type="Verify",this.onVerify(a),!(a.text===null||a.text===void 0)){var b=
-this._model;try{if(a._ignoreDOMSelection)this._ignoreDOMSelection=!0;b.setText(a.text,a.start,a.end)}finally{if(a._ignoreDOMSelection)this._ignoreDOMSelection=!1}c&&(b=this._getSelection(),b.setCaret(a.start+a.text.length),this._setSelection(b,!0));this.onModify({type:"Modify"})}},_onModelChanged:function(a){a.type="ModelChanged";this.onModelChanged(a);a.type="Changed";var c=a.start,b=a.addedCharCount,d=a.removedCharCount,i=a.addedLineCount,f=a.removedLineCount,m=this._getSelection();m.end>c&&(m.end>
-c&&m.start<c+d?m.setCaret(c+b):(m.start+=b-d,m.end+=b-d),this._setSelection(m,!1,!1));c=this._model.getLineAtOffset(c);for(b=this._getLineNext();b;){d=b.lineIndex;if(c<=d&&d<=c+f)c===d&&!b.modelChangedEvent&&!b.lineRemoved?(b.modelChangedEvent=a,b.lineChanged=!0):(b.lineRemoved=!0,b.lineChanged=!1,b.modelChangedEvent=null);if(d>c+f)b.lineIndex=d+i-f,b._line.lineIndex=b.lineIndex;b=this._getLineNext(b)}this._lineHeight&&(a=[c,f].concat(Array(i)),Array.prototype.splice.apply(this._lineHeight,a));if(!this._wrapMode&&
-c<=this._maxLineIndex&&this._maxLineIndex<=c+f)this._checkMaxLineIndex=this._maxLineIndex,this._maxLineIndex=-1,this._maxLineWidth=0;this._update()},_onModelChanging:function(a){a.type="ModelChanging";this.onModelChanging(a);a.type="Changing"},_queueUpdate:function(){if(!this._updateTimer&&!this._ignoreQueueUpdate){var a=this;this._updateTimer=this._getWindow().setTimeout(function(){a._updateTimer=null;a._update()},0)}},_resetLineHeight:function(a,c){if(this._wrapMode||this._variableLineHeight){if(a!==
-void 0&&c!==void 0)for(var b=a;b<c;b++)this._lineHeight[b]=void 0;else this._lineHeight=Array(this._model.getLineCount());this._calculateLineHeightTimer()}else this._lineHeight=null},_resetLineWidth:function(){var a=this._clientDiv;if(a)for(a=a.firstChild;a;)a.lineWidth=void 0,a=a.nextSibling},_reset:function(){this._maxLineIndex=-1;this._maxLineWidth=0;this._columnX=-1;this._bottomChild=this._topChild=null;this._topIndexY=0;this._variableLineHeight=!1;this._resetLineHeight();this._setSelection(new l(0,
-0,!1),!1,!1);if(this._viewDiv)this._viewDiv.scrollLeft=0,this._viewDiv.scrollTop=0;var a=this._clientDiv;if(a){for(var c=a.firstChild;c;)c.lineRemoved=!0,c=c.nextSibling;if(d.isFirefox)this._ignoreFocus=!1,(c=this._hasFocus)&&a.blur(),a.contentEditable=!1,a.contentEditable=!0,c&&a.focus(),this._ignoreFocus=!1}},_scrollViewAnimated:function(a,c,b){if(b&&this._scrollAnimation){var d=this;this._animation=new u({window:this._getWindow(),duration:this._scrollAnimation,curve:[c,0],onAnimate:function(a){a=
-c-Math.floor(a);d._scrollView(0,a);c-=a},onEnd:function(){d._animation=null;d._scrollView(a,c);b&&b()}});this._animation.play()}else this._scrollView(a,c),b&&b()},_scrollView:function(a,c){this._ensureCaretVisible=!1;var b=this._viewDiv;a&&(b.scrollLeft+=a);c&&(b.scrollTop+=c)},_setClipboardText:function(a,c){var b,f=this._parent.ownerDocument,m=this._getWindow();if(m.clipboardData)return b=[],i(a,function(a){b.push(a)},function(){b.push(d.platformDelimiter)}),m.clipboardData.setData("Text",b.join(""));
-if(c&&c.clipboardData&&(b=[],i(a,function(a){b.push(a)},function(){b.push(d.platformDelimiter)}),c.clipboardData.setData("text/plain",b.join(""))))return!0;var l=d.createElement(f,"pre");l.style.position="fixed";l.style.left="-1000px";i(a,function(a){l.appendChild(f.createTextNode(a))},function(){l.appendChild(d.createElement(f,"br"))});l.appendChild(f.createTextNode(" "));this._clientDiv.appendChild(l);var n=f.createRange();n.setStart(l.firstChild,0);n.setEndBefore(l.lastChild);var e=m.getSelection();
-e.rangeCount>0&&e.removeAllRanges();e.addRange(n);var h=this,n=function(){l&&l.parentNode===h._clientDiv&&h._clientDiv.removeChild(l);h._updateDOMSelection()},e=!1;this._ignoreCopy=!0;try{e=f.execCommand("copy",!1,null)}catch(j){}this._ignoreCopy=!1;if(!e&&c)return m.setTimeout(n,0),!1;n();return!0},_setDOMSelection:function(a,c,b,i,f){for(var m,l,n,e,h=0,j=a.firstChild,r,q,k=this._model.getLine(a.lineIndex).length;j;){if(!j.ignore){r=j.firstChild;q=r.length;j.ignoreChars&&(q-=j.ignoreChars);if(h+
-q>c||h+q>=k){m=r;l=c-h;j.ignoreChars&&q>0&&l===q&&(l+=j.ignoreChars);break}h+=q}j=j.nextSibling}for(var h=0,j=b.firstChild,o=this._model.getLine(b.lineIndex).length;j;){if(!j.ignore){r=j.firstChild;q=r.length;j.ignoreChars&&(q-=j.ignoreChars);if(q+h>i||h+q>=o){n=r;e=i-h;j.ignoreChars&&q>0&&e===q&&(e+=j.ignoreChars);break}h+=q}j=j.nextSibling}this._setDOMFullSelection(a,c,k,b,i,o);c=this._getWindow();a=this._parent.ownerDocument;if(c.getSelection){c=c.getSelection();a=a.createRange();a.setStart(m,
-l);a.setEnd(n,e);if(this._hasFocus&&(c.anchorNode!==m||c.anchorOffset!==l||c.focusNode!==n||c.focusOffset!==e||c.anchorNode!==n||c.anchorOffset!==e||c.focusNode!==m||c.focusOffset!==l))this._ignoreSelect=!1,c.rangeCount>0&&c.removeAllRanges(),c.addRange(a),this._ignoreSelect=!0;if(this._cursorDiv)f?a.setEnd(m,l):a.setStart(n,e),n=a.getClientRects()[0],e=this._cursorDiv.parentNode,m=e.getBoundingClientRect(),this._cursorDiv.style.top=n.top-m.top+e.scrollTop+"px",this._cursorDiv.style.left=n.left-m.left+
-e.scrollLeft+"px"}else if(a.selection&&this._hasFocus)f=a.body,a=d.createElement(a,"div"),f.appendChild(a),f.removeChild(a),a=f.createTextRange(),a.moveToElementText(m.parentNode),a.moveStart("character",l),m=f.createTextRange(),m.moveToElementText(n.parentNode),m.moveStart("character",e),a.setEndPoint("EndToStart",m),this._ignoreSelect=!1,a.select(),this._ignoreSelect=!0},_setDOMFullSelection:function(a,c,b,d,i){if(this._selDiv1&&(b=this._selDiv1,b.style.width="0px",b.style.height="0px",b=this._selDiv2,
-b.style.width="0px",b.style.height="0px",b=this._selDiv3,b.style.width="0px",b.style.height="0px",!(a===d&&c===i))){var f=this._model,m=this._getViewPadding(),l=this._clientDiv.getBoundingClientRect(),n=this._viewDiv.getBoundingClientRect(),b=n.left+m.left,e=l.right,m=n.top+m.top,h=l.bottom,n=l=0;this._clipDiv?(n=this._clipDiv.getBoundingClientRect(),l=n.left-this._clipDiv.scrollLeft):(n=this._rootDiv.getBoundingClientRect(),l=n.left);n=n.top;this._ignoreDOMSelection=!0;var a=(new q(this,a.lineIndex,
-a)).getBoundingClientRect(f.getLineStart(a.lineIndex)+c,!1),j=a.left,i=(new q(this,d.lineIndex,d)).getBoundingClientRect(f.getLineStart(d.lineIndex)+i,!1),c=i.left;this._ignoreDOMSelection=!1;var f=this._selDiv1,j=Math.min(e,Math.max(b,j)),r=Math.min(h,Math.max(m,a.top)),k=e,d=Math.min(h,Math.max(m,a.bottom));f.style.left=j-l+"px";f.style.top=r-n+"px";f.style.width=Math.max(0,k-j)+"px";f.style.height=Math.max(0,d-r)+"px";if(a.top===i.top)k=Math.min(c,e),f.style.width=Math.max(0,k-j)+"px";else if(a=
-Math.min(h,Math.max(m,i.top)),c=Math.min(e,Math.max(b,c)),m=Math.min(h,Math.max(m,i.bottom)),h=this._selDiv3,h.style.left=b-l+"px",h.style.top=a-n+"px",h.style.width=Math.max(0,c-b)+"px",h.style.height=Math.max(0,m-a)+"px",a-d>0)m=this._selDiv2,m.style.left=b-l+"px",m.style.top=d-n+"px",m.style.width=Math.max(0,e-b)+"px",m.style.height=Math.max(0,a-d)+"px"}},_setGrab:function(a){if(a!==this._grabControl)a?(a.setCapture&&a.setCapture(),this._grabControl=a):(this._grabControl.releaseCapture&&this._grabControl.releaseCapture(),
-this._grabControl=null)},_setLinksVisible:function(a){if(this._linksVisible!==a){this._linksVisible=a;if(d.isIE&&a)this._hadFocus=this._hasFocus;var c=this._clientDiv;c.contentEditable=!a;this._hadFocus&&!a&&c.focus();if(this._overlayDiv)this._overlayDiv.style.zIndex=a?"-1":"1";for(a=this._getLineNext();a;){if(a.hasLink)for(c=a.firstChild;c;)if(c.ignore)c=c.nextSibling;else{var b=c.nextSibling,i=c.viewStyle;i&&i.tagName&&i.tagName.toLowerCase()==="a"&&a.replaceChild(a._line._createSpan(a,c.firstChild.data,
-i),c);c=b}a=this._getLineNext(a)}this._updateDOMSelection()}},_setSelection:function(a,c,b,d,i){if(a){this._columnX=-1;b===void 0&&(b=!0);var f=this._selection;this._selection=a;c!==!1&&this._showCaret(!1,d,c,i);b&&this._updateDOMSelection();if(!f.equals(a))this.onSelection({type:"Selection",oldValue:{start:f.start,end:f.end},newValue:{start:a.start,end:a.end}})}},_setSelectionTo:function(a,c,b,d){var i=this._model,f=this._getSelection(),c=this.convert({x:a,y:c},"page","document"),a=this._getLineIndex(c.y);
-if(this._clickCount===1){i=this._getLine(a);a=i.getOffset(c.x,c.y-this._getLinePixel(a));i.destroy();if(d&&!b&&f.start<=a&&a<f.end)return this._dragOffset=a,!1;f.extend(a);b||f.collapse()}else(this._clickCount&1)===0?(i=this._getLine(a),a=i.getOffset(c.x,c.y-this._getLinePixel(a)),this._doubleClickSelection?a>=this._doubleClickSelection.start?(b=this._doubleClickSelection.start,d=i.getNextOffset(a,{unit:"wordend",count:1})):(b=i.getNextOffset(a,{unit:"word",count:-1}),d=this._doubleClickSelection.end):
-(b=i.getNextOffset(a,{unit:"word",count:-1}),d=i.getNextOffset(b,{unit:"wordend",count:1})),i.destroy()):this._doubleClickSelection?(d=i.getLineAtOffset(this._doubleClickSelection.start),a>=d?(b=i.getLineStart(d),d=i.getLineEnd(a)):(b=i.getLineStart(a),d=i.getLineEnd(d))):(b=i.getLineStart(a),d=i.getLineEnd(a)),f.setCaret(b),f.extend(d);this._setSelection(f,!0,!0);return!0},_setFullSelection:function(a,c){this._fullSelection=a;if(d.isWebkit)this._fullSelection=!0;var b=this._clipDiv||this._rootDiv;
-if(b)if(this._fullSelection){if(!this._selDiv1&&this._fullSelection&&!d.isIOS){var i=b.ownerDocument;this._highlightRGB=d.isWebkit?"transparent":"Highlight";var f=d.createElement(i,"div");this._selDiv1=f;f.style.position="absolute";f.style.borderWidth="0px";f.style.margin="0px";f.style.padding="0px";f.style.outline="none";f.style.background=this._highlightRGB;f.style.width="0px";f.style.height="0px";f.style.zIndex="0";b.appendChild(f);var m=d.createElement(i,"div");this._selDiv2=m;m.style.position=
-"absolute";m.style.borderWidth="0px";m.style.margin="0px";m.style.padding="0px";m.style.outline="none";m.style.background=this._highlightRGB;m.style.width="0px";m.style.height="0px";m.style.zIndex="0";b.appendChild(m);this._selDiv3=i=d.createElement(i,"div");i.style.position="absolute";i.style.borderWidth="0px";i.style.margin="0px";i.style.padding="0px";i.style.outline="none";i.style.background=this._highlightRGB;i.style.width="0px";i.style.height="0px";i.style.zIndex="0";b.appendChild(i);if(d.isFirefox&&
-d.isMac){b=this._getWindow().getComputedStyle(i,null).getPropertyValue("background-color");switch(b){case "rgb(119, 141, 168)":b="rgb(199, 208, 218)";break;case "rgb(127, 127, 127)":b="rgb(198, 198, 198)";break;case "rgb(255, 193, 31)":b="rgb(250, 236, 115)";break;case "rgb(243, 70, 72)":b="rgb(255, 176, 139)";break;case "rgb(255, 138, 34)":b="rgb(255, 209, 129)";break;case "rgb(102, 197, 71)":b="rgb(194, 249, 144)";break;case "rgb(140, 78, 184)":b="rgb(232, 184, 255)";break;default:b="rgb(180, 213, 255)"}this._highlightRGB=
-b;f.style.background=b;m.style.background=b;i.style.background=b}c||this._updateDOMSelection()}}else{if(this._selDiv1)b.removeChild(this._selDiv1),this._selDiv1=null;if(this._selDiv2)b.removeChild(this._selDiv2),this._selDiv2=null;if(this._selDiv3)b.removeChild(this._selDiv3),this._selDiv3=null}},_setBlockCursor:function(a){this._blockCursorVisible=a;this._updateBlockCursorVisible()},_setOverwriteMode:function(a){this._overwriteMode=a;this._updateBlockCursorVisible()},_updateBlockCursorVisible:function(){if(this._blockCursorVisible||
-this._overwriteMode){if(!this._cursorDiv){var a=d.createElement(document,"div");a.className="textviewBlockCursor";this._cursorDiv=a;a.tabIndex=-1;a.style.zIndex="2";a.style.color="transparent";a.style.position="absolute";a.style.pointerEvents="none";a.innerHTML="&nbsp;";this._viewDiv.appendChild(a);this._updateDOMSelection()}}else if(this._cursorDiv)this._cursorDiv.parentNode.removeChild(this._cursorDiv),this._cursorDiv=null},_setReadOnly:function(a){this._readonly=a;this._clientDiv.setAttribute("aria-readonly",
-a?"true":"false")},_setTabSize:function(a,c){this._tabSize=a;this._customTabSize=void 0;var b=this._clientDiv;if(d.isOpera){if(b)b.style.OTabSize=this._tabSize+""}else if(d.isWebkit>=537.1){if(b)b.style.tabSize=this._tabSize+""}else if(d.isFirefox>=4){if(b)b.style.MozTabSize=this._tabSize+""}else if(this._tabSize!==8)this._customTabSize=this._tabSize;c||(this.redrawLines(),this._resetLineWidth())},_setTheme:function(a){this._theme&&this._theme.removeEventListener("ThemeChanged",this._themeListener.onChanged);
-(this._theme=a)&&this._theme.addEventListener("ThemeChanged",this._themeListener.onChanged);this._setThemeClass(this._themeClass)},_setThemeClass:function(a,c){this._themeClass=a;var b="textview",i=this._theme.getThemeClass();i&&(b+=" "+i);this._themeClass&&i!==this._themeClass&&(b+=" "+this._themeClass);this._rootDiv.className=b;this._updateStyle(c)},_setWrapMode:function(a,c){this._wrapMode=a&&this._wrappable;var b=this._clientDiv,i=this._viewDiv;a?(b.style.whiteSpace="pre-wrap",b.style.wordWrap=
-"break-word",i.style.overflowX="hidden",i.style.overflowY="scroll"):(b.style.whiteSpace="pre",b.style.wordWrap="normal",i.style.overflowX="auto",i.style.overflowY="auto");c||(this.redraw(),this._resetLineWidth());this._resetLineHeight()},_showCaret:function(a,c,b,i){if(this._clientDiv){var d=this._model,f=this._getSelection(),m=this._getScroll(),l=f.getCaret(),n=f.start,e=f.end,h=d.getLineAtOffset(e),j=Math.max(Math.max(n,d.getLineStart(h)),e-1),d=this._getClientWidth(),h=this._getClientHeight(),
-r=d/4,q=this._getBoundsAtOffset(l===n?n:j),k=q.left,o=q.right,v=q.top,p=q.bottom;a&&!f.isEmpty()&&(q=this._getBoundsAtOffset(l===e?n:j),q.top===v?l===n?o=k+Math.min(q.right-k,d):k=o-Math.min(o-q.left,d):l===n?p=v+Math.min(q.bottom-v,h):v=p-Math.min(p-q.top,h));a=0;k<m.x&&(a=Math.min(k-m.x,-r));o>m.x+d&&(a=Math.max(o-m.x-d,r));f=0;v<m.y?f=v-m.y:p>m.y+h&&(f=p-m.y-h);i&&(i>0?f>0&&(f=Math.max(f,i)):f<0&&(f=Math.min(f,i)));if(a!==0||f!==0)return f!==0&&typeof b==="number"&&(b<0&&(b=0),b>1&&(b=1),f+=Math.floor(f>
-0?b*h:-b*h)),this._scrollViewAnimated(a,f,c),h!==this._getClientHeight()||d!==this._getClientWidth()?this._showCaret():this._ensureCaretVisible=!0,!0;else c&&c();return!1}},_startIME:function(){if(this._imeOffset===-1){var a=this._getSelection();a.isEmpty()||this._modifyContent({text:"",start:a.start,end:a.end},!0);this._imeOffset=a.start}},_unhookEvents:function(){this._model.removeEventListener("preChanging",this._modelListener.onChanging);this._model.removeEventListener("postChanged",this._modelListener.onChanged);
-this._theme.removeEventListener("ThemeChanged",this._themeListener.onChanged);this._modelListener=null;for(var a=0;a<this._handlers.length;a++){var c=this._handlers[a],b=c.target,i=c.type,c=c.handler;typeof b.removeEventListener==="function"?b.removeEventListener(i,c,!1):b.detachEvent("on"+i,c)}this._handlers=null;if(this._mutationObserver)this._mutationObserver.disconnect(),this._mutationObserver=null},_updateDOMSelection:function(){if(!this._ignoreDOMSelection&&this._clientDiv){var a=this._getSelection(),
-c=this._model,b=c.getLineAtOffset(a.start),i=c.getLineAtOffset(a.end),d=this._getLineNext();if(d){var f=this._getLinePrevious(),m;b<d.lineIndex?(m=d,b=0):b>f.lineIndex?(m=f,b=0):(m=this._getLineNode(b),b=a.start-c.getLineStart(b));i<d.lineIndex?c=0:i>f.lineIndex?(d=f,c=0):(d=this._getLineNode(i),c=a.end-c.getLineStart(i));this._setDOMSelection(m,b,d,c,a.caret)}}},_update:function(a){if(!(this._redrawCount>0)){if(this._updateTimer)this._getWindow().clearTimeout(this._updateTimer),this._updateTimer=
-null,a=!1;var c=this._clientDiv;if(c){if(this._metrics.invalid)this._ignoreQueueUpdate=!0,this._updateStyle(),this._ignoreQueueUpdate=!1;var b=this._model,i=this._getScroll(!1),f=this._getViewPadding(),m=b.getLineCount(),l=this._getLineHeight(),n=this._getClientWidth();if(this._wrapMode)c.style.width=n+"px";var e,h,j,r,k=0,o=0;if(this._lineHeight){for(;o<m;){h=this._getLineHeight(o);if(k+h>i.y)break;k+=h;o++}e=o;h=Math.max(0,e-1);j=b=i.y-k;e>0&&(b+=this._getLineHeight(e-1))}else r=Math.max(0,i.y)/
-l,e=Math.floor(r),h=Math.max(0,e-1),b=Math.round((r-h)*l),j=Math.round((r-e)*l);this._topIndexY=j;r=this._parent;var v=r.clientWidth,p=r.clientHeight;r=this._getClientHeight();if(a){l=0;this._leftDiv&&(e=this._leftDiv.getBoundingClientRect(),l=e.right-e.left);e=n;for(this._wrapMode||(e=Math.max(this._maxLineWidth,e));o<m;)h=this._getLineHeight(o,!1),k+=h,o++;m=k}else{n=this._viewDiv;j=Math.min(e+Math.floor((r+j)/l),m-1);for(var i=Math.min(j+1,m-1),u,w=c.firstChild;w;){u=w.lineIndex;var y=w.nextSibling;
-if(!(h<=u&&u<=i)||w.lineRemoved||w.lineIndex===-1)this._mouseWheelLine===w?(w.style.display="none",w.lineIndex=-1):c.removeChild(w);w=y}var w=this._getLineNext(),y=n.ownerDocument,A=y.createDocumentFragment();for(u=h;u<=i;u++)if(!w||w.lineIndex>u)(new q(this,u)).create(A,null);else{A.firstChild&&(c.insertBefore(A,w),A=y.createDocumentFragment());if(w&&w.lineChanged)w=(new q(this,u)).create(A,w),w.lineChanged=!1;w=this._getLineNext(w)}A.firstChild&&c.insertBefore(A,w);if(d.isWebkit&&!this._wrapMode)c.style.width=
-"0x7fffffffpx";w=this._getLineNext();u=r+b;for(A=!1;w;){h=w.lineWidth;if(h===void 0)if(y=w._line.getBoundingClientRect(),h=w.lineWidth=Math.ceil(y.right-y.left),y=y.bottom-y.top,this._lineHeight)this._lineHeight[w.lineIndex]=Math.ceil(y);else if(l!==0&&y!==0&&l!==y)this._variableLineHeight=!0,this._lineHeight=[],this._lineHeight[w.lineIndex]=Math.ceil(y);if(this._lineHeight&&!A&&(u-=this._lineHeight[w.lineIndex],u<0))j=w.lineIndex,A=!0;if(!this._wrapMode){if(h>=this._maxLineWidth)this._maxLineWidth=
-h,this._maxLineIndex=w.lineIndex;if(this._checkMaxLineIndex===w.lineIndex)this._checkMaxLineIndex=-1}if(w.lineIndex===e)this._topChild=w;if(w.lineIndex===j)this._bottomChild=w;w=this._getLineNext(w)}if(this._checkMaxLineIndex!==-1&&(u=this._checkMaxLineIndex,this._checkMaxLineIndex=-1,0<=u&&u<m)){l=new q(this,u);y=l.getBoundingClientRect();h=y.right-y.left;if(h>=this._maxLineWidth)this._maxLineWidth=h,this._maxLineIndex=u;l.destroy()}for(;o<m;)h=this._getLineHeight(o,o<=j),k+=h,o++;m=k;this._updateRuler(this._leftDiv,
-e,i,p);this._updateRuler(this._rightDiv,e,i,p);l=0;this._leftDiv&&(e=this._leftDiv.getBoundingClientRect(),l=e.right-e.left);i=0;this._rightDiv&&(i=this._rightDiv.getBoundingClientRect(),i=i.right-i.left);n.style.left=l+"px";n.style.right=i+"px";i=this._scrollDiv;i.style.height=m+"px";k=n=this._getClientWidth();this._wrapMode||(k=Math.max(this._maxLineWidth,k));e=k;if((!d.isIE||d.isIE>=9)&&this._maxLineWidth>n)k+=f.right+f.left;i.style.width=k+"px";if(this._clipScrollDiv)this._clipScrollDiv.style.width=
-k+"px";i=this._getScroll(!1)}if(this._vScrollDiv)k=r-8,o=Math.max(15,Math.ceil(Math.min(1,k/(m+f.top+f.bottom))*k)),this._vScrollDiv.style.left=l+n-8+"px",this._vScrollDiv.style.top=Math.floor(Math.max(0,i.y*k/m))+"px",this._vScrollDiv.style.height=o+"px";if(!this._wrapMode&&this._hScrollDiv)k=n-8,o=Math.max(15,Math.ceil(Math.min(1,k/(this._maxLineWidth+f.left+f.right))*k)),this._hScrollDiv.style.left=l+Math.floor(Math.max(0,Math.floor(i.x*k/this._maxLineWidth)))+"px",this._hScrollDiv.style.top=r-
-9+"px",this._hScrollDiv.style.width=o+"px";w=i.x;h=this._clipDiv;k=this._overlayDiv;if(h){h.scrollLeft=w;o=l+f.left;j=f.top;a=n;l=r;w=0;u=-b;if(i.x===0)o-=f.left,a+=f.left,w=f.left;i.x+n===e&&(a+=f.right);i.y===0&&(j-=f.top,l+=f.top,u+=f.top);i.y+r===m&&(l+=f.bottom);h.style.left=o+"px";h.style.top=j+"px";h.style.right=v-a-o+"px";h.style.bottom=p-l-j+"px";c.style.left=w+"px";c.style.top=u+"px";c.style.width=e+"px";c.style.height=r+b+"px";if(k)k.style.left=c.style.left,k.style.top=c.style.top,k.style.width=
-c.style.width,k.style.height=c.style.height}else{o=w;j=b;v=w+n;p=b+r;o===0&&(o-=f.left);j===0&&(j-=f.top);v===e&&(v+=f.right);i.y+r===m&&(p+=f.bottom);c.style.clip="rect("+j+"px,"+v+"px,"+p+"px,"+o+"px)";c.style.left=-w+l+f.left+"px";c.style.width=(d.isWebkit?e:n+w)+"px";if(!a)c.style.top=-b+f.top+"px",c.style.height=r+b+"px";if(k&&(k.style.clip=c.style.clip,k.style.left=c.style.left,k.style.width=c.style.width,!a))k.style.top=c.style.top,k.style.height=c.style.height}this._updateDOMSelection();c=
-this._ensureCaretVisible;this._ensureCaretVisible=!1;r!==this._getClientHeight()&&(this._update(),c&&this._showCaret())}}},_updateRuler:function(a,c,b,i){if(a)for(var f=this._parent.ownerDocument,m=this._getLineHeight(),l=this._getViewPadding(),a=a.firstChild;a;){var n=a._ruler,e=m,h=n.getOverview();h==="page"&&(e+=this._topIndexY);a.style.top=-e+"px";a.style.height=i+e+"px";a.rulerChanged&&j(n.getRulerStyle(),a);var r,q=a.firstChild;q?(r=q,q=q.nextSibling):(r=d.createElement(f,"div"),r.style.visibility=
-"hidden",a.appendChild(r));var k;if(a.rulerChanged&&r){e=-1;if(k=n.getWidestAnnotation())if(j(k.style,r),k.html)r.innerHTML=k.html;r.lineIndex=e;r.style.height=m+l.top+"px"}var o;if(h==="page"){for(n=n.getAnnotations(c,b+1);q;)e=q.lineIndex,k=q.nextSibling,(!(c<=e&&e<=b)||q.lineChanged)&&a.removeChild(q),q=k;q=a.firstChild.nextSibling;o=f.createDocumentFragment();for(e=c;e<=b;e++)if(!q||q.lineIndex>e){r=d.createElement(f,"div");if(k=n[e]){j(k.style,r);if(k.html)r.innerHTML=k.html;r.annotation=k}r.lineIndex=
-e;r.style.height=this._getLineHeight(e)+"px";o.appendChild(r)}else if(o.firstChild&&(a.insertBefore(o,q),o=f.createDocumentFragment()),q)q=q.nextSibling;o.firstChild&&a.insertBefore(o,q)}else{k=this._getClientHeight();e=this._model.getLineCount();q=k+l.top+l.bottom-2*this._metrics.scrollWidth;h=m*e<q?m:q/e;if(a.rulerChanged){for(k=a.childNodes.length;k>1;)a.removeChild(a.lastChild),k--;n=n.getAnnotations(0,e);o=f.createDocumentFragment();for(var v in n)if(e=v>>>0,!(e<0)){r=d.createElement(f,"div");
-k=n[v];j(k.style,r);r.style.position="absolute";r.style.top=this._metrics.scrollWidth+m+Math.floor(e*h)+"px";if(k.html)r.innerHTML=k.html;r.annotation=k;r.lineIndex=e;o.appendChild(r)}a.appendChild(o)}else if(a._oldTrackHeight!==q)for(r=a.firstChild?a.firstChild.nextSibling:null;r;)r.style.top=this._metrics.scrollWidth+m+Math.floor(r.lineIndex*h)+"px",r=r.nextSibling;a._oldTrackHeight=q}a.rulerChanged=!1;a=a.nextSibling}},_updateStyleSheet:function(){var a="";d.isWebkit&&this._metrics.scrollWidth>
-0&&(a+="\n.textview ::-webkit-scrollbar-corner {background: #eeeeee;}");d.isFirefox&&d.isMac&&this._highlightRGB&&this._highlightRGB!=="Highlight"&&(a+="\n.textview ::-moz-selection {background: "+this._highlightRGB+";}");if(a){var c=document.getElementById("_textviewStyle");if(c)c.removeChild(c.firstChild),c.appendChild(document.createTextNode(a));else{c=d.createElement(document,"style");c.id="_textviewStyle";var b=document.getElementsByTagName("head")[0]||document.documentElement;c.appendChild(document.createTextNode(a));
-b.insertBefore(c,b.firstChild)}}},_updateStyle:function(a){if(!a&&d.isIE)this._rootDiv.style.lineHeight="normal";var c=this._metrics=this._calculateMetrics();this._rootDiv.style.lineHeight=d.isIE?c.lineHeight-(c.lineTrim.top+c.lineTrim.bottom)+"px":"normal";this._updateStyleSheet();a||(this.redraw(),this._resetLineWidth())}};e.EventTarget.addMixin(v.prototype);return{TextView:v}});
-define("orion/editor/projectionTextModel",["orion/editor/textModel","orion/editor/eventTarget"],function(k,p){function o(e){this._model=e;this._projections=[]}o.prototype={addProjection:function(e){if(e){var b=this._model,d=this._projections;e._lineIndex=b.getLineAtOffset(e.start);e._lineCount=b.getLineAtOffset(e.end)-e._lineIndex;var h=e.text;h||(h="");e._model=typeof h==="string"?new k.TextModel(h,b.getLineDelimiter()):h;var b=this.mapOffset(e.start,!0),h=e.end-e.start,j=e._lineCount,f=e._model.getCharCount(),
-a=e._model.getLineCount()-1;this.onChanging({type:"Changing",text:e._model.getText(),start:b,removedCharCount:h,addedCharCount:f,removedLineCount:j,addedLineCount:a});var c=this._binarySearch(d,e.start);d.splice(c,0,e);this.onChanged({type:"Changed",start:b,removedCharCount:h,addedCharCount:f,removedLineCount:j,addedLineCount:a})}},getProjections:function(){return this._projections.slice(0)},getBaseModel:function(){return this._model},mapOffset:function(e,b){var d=this._projections,h=0,j,f;if(b){for(j=
-0;j<d.length;j++){f=d[j];if(f.start>e)break;if(f.end>e)return-1;h+=f._model.getCharCount()-(f.end-f.start)}return e+h}for(j=0;j<d.length;j++){f=d[j];if(f.start>e-h)break;var a=f._model.getCharCount();if(f.start+a>e-h)return-1;h+=a-(f.end-f.start)}return e-h},removeProjection:function(e){var b,d=0;for(b=0;b<this._projections.length;b++){var h=this._projections[b];if(h===e){e=h;break}d+=h._model.getCharCount()-(h.end-h.start)}if(b<this._projections.length){var h=this._model,d=e.start+d,j=e.end-e.start,
-f=e._lineCount,a=e._model.getCharCount(),c=e._model.getLineCount()-1;this.onChanging({type:"Changing",text:h.getText(e.start,e.end),start:d,removedCharCount:a,addedCharCount:j,removedLineCount:c,addedLineCount:f});this._projections.splice(b,1);this.onChanged({type:"Changed",start:d,removedCharCount:a,addedCharCount:j,removedLineCount:c,addedLineCount:f})}},_binarySearch:function(e,b){for(var d=e.length,h=-1,j;d-h>1;)j=Math.floor((d+h)/2),b<=e[j].start?d=j:h=j;return d},getCharCount:function(){for(var e=
-this._model.getCharCount(),b=this._projections,d=0;d<b.length;d++){var h=b[d];e+=h._model.getCharCount()-(h.end-h.start)}return e},getLine:function(e,b){if(e<0)return null;var d=this._model,h=this._projections,j=0,f=[],a=0,c,i,m;for(c=0;c<h.length;c++){m=h[c];if(m._lineIndex>=e-j)break;i=m._model.getLineCount()-1;if(m._lineIndex+i>=e-j)if(a=e-(m._lineIndex+j),a<i)return m._model.getLine(a,b);else f.push(m._model.getLine(i));a=m.end;j+=i-m._lineCount}for(a=Math.max(a,d.getLineStart(e-j));c<h.length;c++){m=
-h[c];if(m._lineIndex>e-j)break;f.push(d.getText(a,m.start));i=m._model.getLineCount()-1;if(m._lineIndex+i>e-j)return f.push(m._model.getLine(0,b)),f.join("");f.push(m._model.getText());a=m.end;j+=i-m._lineCount}h=d.getLineEnd(e-j,b);a<h&&f.push(d.getText(a,h));return f.join("")},getLineAtOffset:function(e){for(var b=this._model,d=this._projections,h=0,j=0,f=0;f<d.length;f++){var a=d[f];if(a.start>e-h)break;var c=a._model.getCharCount();if(a.start+c>e-h){d=e-(a.start+h);j+=a._model.getLineAtOffset(d);
-h+=d;break}j+=a._model.getLineCount()-1-a._lineCount;h+=c-(a.end-a.start)}return b.getLineAtOffset(e-h)+j},getLineCount:function(){for(var e=this._projections,b=this._model.getLineCount(),d=0;d<e.length;d++){var h=e[d];b+=h._model.getLineCount()-1-h._lineCount}return b},getLineDelimiter:function(){return this._model.getLineDelimiter()},getLineEnd:function(e,b){if(e<0)return-1;for(var d=this._model,h=this._projections,j=0,f=0,a=0;a<h.length;a++){var c=h[a];if(c._lineIndex>e-j)break;var i=c._model.getLineCount()-
-1;if(c._lineIndex+i>e-j)return c._model.getLineEnd(e-(c._lineIndex+j),b)+c.start+f;f+=c._model.getCharCount()-(c.end-c.start);j+=i-c._lineCount}return d.getLineEnd(e-j,b)+f},getLineStart:function(e){if(e<0)return-1;for(var b=this._model,d=this._projections,h=0,j=0,f=0;f<d.length;f++){var a=d[f];if(a._lineIndex>=e-h)break;var c=a._model.getLineCount()-1;if(a._lineIndex+c>=e-h)return a._model.getLineStart(e-(a._lineIndex+h))+a.start+j;j+=a._model.getCharCount()-(a.end-a.start);h+=c-a._lineCount}return b.getLineStart(e-
-h)+j},getText:function(e,b){e===void 0&&(e=0);var d=this._model,h=this._projections,j=0,f=[],a,c,i;for(a=0;a<h.length;a++){c=h[a];if(c.start>e-j)break;i=c._model.getCharCount();if(c.start+i>e-j)if(b!==void 0&&c.start+i>b-j)return c._model.getText(e-(c.start+j),b-(c.start+j));else f.push(c._model.getText(e-(c.start+j))),e=c.end+j+i-(c.end-c.start);j+=i-(c.end-c.start)}var m=e-j;if(b!==void 0){for(;a<h.length;a++){c=h[a];if(c.start>b-j)break;f.push(d.getText(m,c.start));i=c._model.getCharCount();if(c.start+
-i>b-j)return f.push(c._model.getText(0,b-(c.start+j))),f.join("");f.push(c._model.getText());m=c.end;j+=i-(c.end-c.start)}f.push(d.getText(m,b-j))}else{for(;a<h.length;a++)c=h[a],f.push(d.getText(m,c.start)),f.push(c._model.getText()),m=c.end;f.push(d.getText(m))}return f.join("")},_onChanging:function(e,b,d,h,j,f){for(var a=this._model,c=this._projections,i,m=0,n,l=b+d;i<c.length;i++){d=c[i];if(d.start>b)break;m+=d._model.getCharCount()-(d.end-d.start)}b+=m;for(var r=i;i<c.length;i++){d=c[i];if(d.start>
-l)break;m+=d._model.getCharCount()-(d.end-d.start);n+=d._model.getLineCount()-1-d._lineCount}d=l+m;m=i;this.onChanging(b,d-b,h,j+n,f);c.splice(c,m-r);for(e=e.length-(d-b);i<c.length;i++)d=c[i],d.start+=e,d.end+=e,d._lineIndex=a.getLineAtOffset(d.start)},onChanging:function(e){return this.dispatchEvent(e)},onChanged:function(e){return this.dispatchEvent(e)},setLineDelimiter:function(e){this._model.setLineDelimiter(e)},setText:function(e,b,d){e===void 0&&(e="");b===void 0&&(b=0);var h=b,j=d,f=this._model,
-a=this._projections,c=0,i=0,m,n,l,r,q,k=0;for(m=0;m<a.length;m++){n=a[m];if(n.start>b-c)break;l=n._model.getCharCount();if(n.start+l>b-c)if(d!==void 0&&n.start+l>d-c){n._model.setText(e,b-(n.start+c),d-(n.start+c));return}else k=n._model.getLineCount()-1-n._model.getLineAtOffset(b-(n.start+c)),r={projection:n,start:b-(n.start+c)},b=n.end+c+l-(n.end-n.start);i+=n._model.getLineCount()-1-n._lineCount;c+=l-(n.end-n.start)}b-=c;var o=m,k=f.getLineAtOffset(b)+i-k;if(d!==void 0)for(;m<a.length;m++){n=a[m];
-if(n.start>d-c)break;l=n._model.getCharCount();if(n.start+l>d-c){i+=n._model.getLineAtOffset(d-(n.start+c));l=d-(n.start+c);d=n.end+c;q={projection:n,end:l};break}i+=n._model.getLineCount()-1-n._lineCount;c+=l-(n.end-n.start)}else{for(;m<a.length;m++)n=a[m],i+=n._model.getLineCount()-1-n._lineCount,c+=n._model.getCharCount()-(n.end-n.start);d=j=f.getCharCount()+c}d-=c;n=f.getLineAtOffset(d)+i;j-=h;for(var i=n-k,k=e.length,g=l=n=c=0;;){n!==-1&&n<=g&&(n=e.indexOf("\r",g));l!==-1&&l<=g&&(l=e.indexOf("\n",
-g));if(l===-1&&n===-1)break;g=n!==-1&&l!==-1?n+1===l?l+1:(n<l?n:l)+1:n!==-1?n+1:l+1;c++}this.onChanging({type:"Changing",text:e,start:h,removedCharCount:j,addedCharCount:k,removedLineCount:i,addedLineCount:c});f.setText(e,b,d);if(r)n=r.projection,n._model.setText("",r.start);if(q)n=q.projection,n._model.setText("",0,q.end),n.start=n.end,n._lineCount=0;a.splice(o,m-o);for(e=e.length-(d-b);m<a.length;m++)n=a[m],n.start+=e,n.end+=e,n._lineIndex=f.getLineAtOffset(n.start);this.onChanged({type:"Changed",
-start:h,removedCharCount:j,addedCharCount:k,removedLineCount:i,addedLineCount:c})}};p.EventTarget.addMixin(o.prototype);return{ProjectionTextModel:o}});
-define("orion/editor/annotations",["i18n!orion/editor/nls/messages","orion/editor/eventTarget"],function(k,p){function o(a,c,b){this.start=a;this.end=c;this._projectionModel=b;this.html=this._expandedHTML;this.style=this._expandedStyle;this.expanded=!0}function e(){}function b(a,c){var b=a.lastIndexOf("."),b=a.substring(b+1),d={title:k[b],style:{styleClass:"annotation "+b},html:"<div class='annotationHTML "+b+"'></div>",overviewStyle:{styleClass:"annotationOverview "+b}};c?d.lineStyle={styleClass:"annotationLine "+
-b}:d.rangeStyle={styleClass:"annotationRange "+b};e.registerType(a,d)}function d(){}function h(a){this._annotations=[];var c=this;this._listener={onChanged:function(a){c._onChanged(a)}};this.setTextModel(a)}function j(a,c){this._view=a;this._annotationModel=c;var b=this;this._listener={onDestroy:function(a){b._onDestroy(a)},onLineStyle:function(a){b._onLineStyle(a)},onChanged:function(a){b._onAnnotationModelChanged(a)}};a.addEventListener("Destroy",this._listener.onDestroy);a.addEventListener("postLineStyle",
-this._listener.onLineStyle);c.addEventListener("Changed",this._listener.onChanged)}o.prototype={_expandedHTML:"<div class='annotationHTML expanded'></div>",_expandedStyle:{styleClass:"annotation expanded"},_collapsedHTML:"<div class='annotationHTML collapsed'></div>",_collapsedStyle:{styleClass:"annotation collapsed"},collapse:function(){if(this.expanded){this.expanded=!1;this.html=this._collapsedHTML;this.style=this._collapsedStyle;var a=this._projectionModel,c=a.getBaseModel();this._projection=
-{start:c.getLineStart(c.getLineAtOffset(this.start)+1),end:c.getLineEnd(c.getLineAtOffset(this.end),!0)};a.addProjection(this._projection)}},expand:function(){if(!this.expanded)this.expanded=!0,this.html=this._expandedHTML,this.style=this._expandedStyle,this._projectionModel.removeProjection(this._projection)}};e.ANNOTATION_ERROR="orion.annotation.error";e.ANNOTATION_WARNING="orion.annotation.warning";e.ANNOTATION_TASK="orion.annotation.task";e.ANNOTATION_BREAKPOINT="orion.annotation.breakpoint";
-e.ANNOTATION_BOOKMARK="orion.annotation.bookmark";e.ANNOTATION_FOLDING="orion.annotation.folding";e.ANNOTATION_CURRENT_BRACKET="orion.annotation.currentBracket";e.ANNOTATION_MATCHING_BRACKET="orion.annotation.matchingBracket";e.ANNOTATION_CURRENT_LINE="orion.annotation.currentLine";e.ANNOTATION_CURRENT_SEARCH="orion.annotation.currentSearch";e.ANNOTATION_MATCHING_SEARCH="orion.annotation.matchingSearch";e.ANNOTATION_READ_OCCURRENCE="orion.annotation.readOccurrence";e.ANNOTATION_WRITE_OCCURRENCE="orion.annotation.writeOccurrence";
-e.ANNOTATION_SELECTED_LINKED_GROUP="orion.annotation.selectedLinkedGroup";e.ANNOTATION_CURRENT_LINKED_GROUP="orion.annotation.currentLinkedGroup";e.ANNOTATION_LINKED_GROUP="orion.annotation.linkedGroup";var f={};e.registerType=function(a,c){var b=c;if(typeof b!=="function")b=function(a,c,b){this.start=a;this.end=c;if(b!==void 0)this.title=b},b.prototype=c;b.prototype.type=a;f[a]=b;return a};e.createAnnotation=function(a,c,b,d){return new (this.getType(a))(c,b,d)};e.getType=function(a){return f[a]};
-b(e.ANNOTATION_ERROR);b(e.ANNOTATION_WARNING);b(e.ANNOTATION_TASK);b(e.ANNOTATION_BREAKPOINT);b(e.ANNOTATION_BOOKMARK);b(e.ANNOTATION_CURRENT_BRACKET);b(e.ANNOTATION_MATCHING_BRACKET);b(e.ANNOTATION_CURRENT_SEARCH);b(e.ANNOTATION_MATCHING_SEARCH);b(e.ANNOTATION_READ_OCCURRENCE);b(e.ANNOTATION_WRITE_OCCURRENCE);b(e.ANNOTATION_SELECTED_LINKED_GROUP);b(e.ANNOTATION_CURRENT_LINKED_GROUP);b(e.ANNOTATION_LINKED_GROUP);b(e.ANNOTATION_CURRENT_LINE,!0);e.registerType(e.ANNOTATION_FOLDING,o);d.addMixin=function(a){var c=
-d.prototype,b;for(b in c)c.hasOwnProperty(b)&&(a[b]=c[b])};d.prototype={addAnnotationType:function(a){if(!this._annotationTypes)this._annotationTypes=[];this._annotationTypes.push(a)},getAnnotationTypePriority:function(a){if(this._annotationTypes)for(var c=0;c<this._annotationTypes.length;c++)if(this._annotationTypes[c]===a)return c+1;return 0},getAnnotationsByType:function(a,c,b){a=a.getAnnotations(c,b);for(b=[];a.hasNext();)c=a.next(),this.getAnnotationTypePriority(c.type)!==0&&b.push(c);var d=
-this;b.sort(function(a,c){return d.getAnnotationTypePriority(a.type)-d.getAnnotationTypePriority(c.type)});return b},isAnnotationTypeVisible:function(a){return this.getAnnotationTypePriority(a)!==0},removeAnnotationType:function(a){if(this._annotationTypes)for(var c=0;c<this._annotationTypes.length;c++)if(this._annotationTypes[c]===a){this._annotationTypes.splice(c,1);break}}};h.prototype={addAnnotation:function(a){if(a){var c=this._annotations,b=this._binarySearch(c,a.start);c.splice(b,0,a);this.onChanged({type:"Changed",
-added:[a],removed:[],changed:[]})}},getTextModel:function(){return this._model},getAnnotations:function(a,c){var b=this._annotations,d,f=0,l=function(){for(;f<b.length;){var d=b[f++];if(a===d.start||(a>d.start?a<d.end:d.start<c))return d;if(d.start>=c)break}return null};d=l();return{next:function(){var a=d;a&&(d=l());return a},hasNext:function(){return d!==null}}},modifyAnnotation:function(a){if(a&&!(this._getAnnotationIndex(a)<0))this.onChanged({type:"Changed",added:[],removed:[],changed:[a]})},
-onChanged:function(a){return this.dispatchEvent(a)},removeAnnotations:function(a){var c=this._annotations,b,d;if(a){b=[];for(d=c.length-1;d>=0;d--){var f=c[d];f.type===a&&c.splice(d,1);b.splice(0,0,f)}}else b=c;this.onChanged({type:"Changed",removed:b,added:[],changed:[]})},removeAnnotation:function(a){if(a&&(a=this._getAnnotationIndex(a),!(a<0)))this.onChanged({type:"Changed",removed:this._annotations.splice(a,1),added:[],changed:[]})},replaceAnnotations:function(a,c){var b=this._annotations,d,f,
-l,e=[];if(a)for(d=a.length-1;d>=0;d--)l=a[d],f=this._getAnnotationIndex(l),f<0||(b.splice(f,1),e.splice(0,0,l));c||(c=[]);for(d=0;d<c.length;d++)l=c[d],f=this._binarySearch(b,l.start),b.splice(f,0,l);this.onChanged({type:"Changed",removed:e,added:c,changed:[]})},setTextModel:function(a){this._model&&this._model.removeEventListener("Changed",this._listener.onChanged);(this._model=a)&&this._model.addEventListener("Changed",this._listener.onChanged)},_binarySearch:function(a,c){for(var b=a.length,d=
--1,f;b-d>1;)f=Math.floor((b+d)/2),c<=a[f].start?b=f:d=f;return b},_getAnnotationIndex:function(a){for(var c=this._annotations,b=this._binarySearch(c,a.start);b<c.length&&c[b].start===a.start;){if(c[b]===a)return b;b++}return-1},_onChanged:function(a){var c=a.start,b=a.removedCharCount,d=this._annotations,f=c+b;if(0<d.length){for(var l={type:"Changed",added:[],removed:[],changed:[],textModelChangedEvent:a},a=a.addedCharCount-b,b=0;b<d.length;b++){var e=d[b];e.start>=f?(e.start+=a,e.end+=a,l.changed.push(e)):
-e.end<=c||(e.start<c&&f<e.end?(e.end+=a,l.changed.push(e)):(d.splice(b,1),l.removed.push(e),b--))}if(l.added.length>0||l.removed.length>0||l.changed.length>0)this.onChanged(l)}}};p.EventTarget.addMixin(h.prototype);j.prototype={destroy:function(){var a=this._view;if(a)a.removeEventListener("Destroy",this._listener.onDestroy),a.removeEventListener("LineStyle",this._listener.onLineStyle),this.view=null;(a=this._annotationModel)&&a.removeEventListener("Changed",this._listener.onChanged)},_mergeStyle:function(a,
-c){if(c){a||(a={});a.styleClass&&c.styleClass&&a.styleClass!==c.styleClass?a.styleClass+=" "+c.styleClass:a.styleClass=c.styleClass;var b;if(c.tagName&&!a.tagName)a.tagName=c.tagName;if(c.style){if(!a.style)a.style={};for(b in c.style)a.style[b]||(a.style[b]=c.style[b])}if(c.attributes){if(!a.attributes)a.attributes={};for(b in c.attributes)a.attributes[b]||(a.attributes[b]=c.attributes[b])}}return a},_mergeStyleRanges:function(a,c){a||(a=[]);var b,d;for(d=0;d<a.length&&c;d++){var f=a[d];if(c.end<=
-f.start)break;if(!(c.start>=f.end)){b=this._mergeStyle({},f.style);b=this._mergeStyle(b,c.style);var l=[];l.push(d,1);c.start<f.start&&l.push({start:c.start,end:f.start,style:c.style});c.start>f.start&&l.push({start:f.start,end:c.start,style:f.style});l.push({start:Math.max(f.start,c.start),end:Math.min(f.end,c.end),style:b});c.end<f.end&&l.push({start:c.end,end:f.end,style:f.style});c=c.end>f.end?{start:f.end,end:c.end,style:c.style}:null;Array.prototype.splice.apply(a,l)}}c&&(b=this._mergeStyle({},
-c.style),a.splice(d,0,{start:c.start,end:c.end,style:b}));return a},_onAnnotationModelChanged:function(a){function c(a){for(var c=0;c<a.length;c++)if(d.isAnnotationTypeVisible(a[c].type)){var e=a[c].start,h=a[c].end;f.getBaseModel&&(e=f.mapOffset(e,!0),h=f.mapOffset(h,!0));e!==-1&&h!==-1&&b.redrawRange(e,h)}}if(!a.textModelChangedEvent){var b=this._view;if(b){var d=this,f=b.getModel();c(a.added);c(a.removed);c(a.changed)}}},_onDestroy:function(){this.destroy()},_onLineStyle:function(a){var c=this._annotationModel,
-b=a.textView.getModel(),d=c.getTextModel(),f=a.lineStart,l=a.lineStart+a.lineText.length;d!==b&&(f=b.mapOffset(f),l=b.mapOffset(l));for(c=c.getAnnotations(f,l);c.hasNext();)if(f=c.next(),this.isAnnotationTypeVisible(f.type)){if(f.rangeStyle){var l=f.start,e=f.end;d!==b&&(l=b.mapOffset(l,!0),e=b.mapOffset(e,!0));a.ranges=this._mergeStyleRanges(a.ranges,{start:l,end:e,style:f.rangeStyle})}if(f.lineStyle)a.style=this._mergeStyle({},a.style),a.style=this._mergeStyle(a.style,f.lineStyle)}}};d.addMixin(j.prototype);
-return{FoldingAnnotation:o,AnnotationType:e,AnnotationTypeList:d,AnnotationModel:h,AnnotationStyler:j}});
-define("orion/editor/tooltip",["i18n!orion/editor/nls/messages","orion/editor/textView","orion/editor/textModel","orion/editor/projectionTextModel","orion/util"],function(k,p,o,e,b){function d(b){this._view=b;this._create(b.getOptions("parent").ownerDocument);b.addEventListener("Destroy",this,this.destroy)}d.getTooltip=function(b){if(!b._tooltip)b._tooltip=new d(b);return b._tooltip};d.prototype={_create:function(d){if(!this._tooltipDiv){var e=this._tooltipDiv=b.createElement(d,"div");e.className=
-"textviewTooltip";e.setAttribute("aria-live","assertive");e.setAttribute("aria-atomic","true");var f=this._tooltipContents=b.createElement(d,"div");e.appendChild(f);d.body.appendChild(e);this.hide()}},_getWindow:function(){var b=this._tooltipDiv.ownerDocument;return b.defaultView||b.parentWindow},destroy:function(){if(this._tooltipDiv){this.hide();var b=this._tooltipDiv.parentNode;b&&b.removeChild(this._tooltipDiv);this._tooltipDiv=null}},hide:function(){if(this._contentsView)this._contentsView.destroy(),
-this._contentsView=null;if(this._tooltipContents)this._tooltipContents.innerHTML="";if(this._tooltipDiv)this._tooltipDiv.style.visibility="hidden";var b=this._getWindow();if(this._showTimeout)b.clearTimeout(this._showTimeout),this._showTimeout=null;if(this._hideTimeout)b.clearTimeout(this._hideTimeout),this._hideTimeout=null;if(this._fadeTimeout)b.clearInterval(this._fadeTimeout),this._fadeTimeout=null},isVisible:function(){return this._tooltipDiv&&this._tooltipDiv.style.visibility==="visible"},setTarget:function(b,
-d){if(this.target!==b&&(this._target=b,this.hide(),b)){var f=this;if(d===0)f.show(!0);else{var a=this._getWindow();f._showTimeout=a.setTimeout(function(){f.show(!0)},d?d:500)}}},show:function(b){if(this._target){var d=this._target.getTooltipInfo();if(d){var f=this._tooltipDiv,a=this._tooltipContents;f.style.left=f.style.right=f.style.width=f.style.height=a.style.width=a.style.height="auto";var c=d.contents;c instanceof Array&&(c=this._getAnnotationContents(c));if(typeof c==="string")a.innerHTML=c;
-else if(this._isNode(c))a.appendChild(c);else if(c instanceof e.ProjectionTextModel){var i=this._view,m=i.getOptions();m.wrapMode=!1;m.parent=a;var n=m.themeClass;n?((n=n.replace("tooltip",""))&&(n=" "+n),n="tooltip"+n):n="tooltip";m.themeClass=n;m=this._contentsView=new p.TextView(m);m._clientDiv.contentEditable=!1;m.addEventListener("LineStyle",function(a){i.onLineStyle(a)});m.setModel(c);c=m.computeSize();a.style.width=c.width+"px";a.style.height=c.height+"px";m.resize()}else return;a=f.ownerDocument.documentElement;
-d.anchor==="right"?(c=a.clientWidth-d.x,f.style.right=c+"px"):(c=parseInt(this._getNodeStyle(f,"padding-left","0"),10),c+=parseInt(this._getNodeStyle(f,"border-left-width","0"),10),c=d.x-c,f.style.left=c+"px");f.style.maxWidth=a.clientWidth-c-10+"px";c=parseInt(this._getNodeStyle(f,"padding-top","0"),10);c+=parseInt(this._getNodeStyle(f,"border-top-width","0"),10);c=d.y-c;f.style.top=c+"px";f.style.maxHeight=a.clientHeight-c-10+"px";f.style.opacity="1";f.style.visibility="visible";if(b){var l=this,
-r=this._getWindow();l._hideTimeout=r.setTimeout(function(){var a=parseFloat(l._getNodeStyle(f,"opacity","1"));l._fadeTimeout=r.setInterval(function(){f.style.visibility==="visible"&&a>0?(a-=0.1,f.style.opacity=a):l.hide()},50)},5E3)}}}},_getAnnotationContents:function(b){function d(c){var b=c.title;if(b==="")return null;var f="<div>";c.html&&(f+=c.html+"&nbsp;");if(!b)b=c.end,c=a.getLineStart(a.getLineAtOffset(c.start)),b=a.getLineEnd(a.getLineAtOffset(b),!0),b=a.getText(c,b);b=b.replace(/</g,"&lt;").replace(/>/g,
-"&gt;");f+="<span style='vertical-align:middle;'>"+b+"</span><div>";return f}if(b.length===0)return null;var f=this._view.getModel(),a=f.getBaseModel?f.getBaseModel():f;if(b.length===1)if(f=b[0],f.title!==void 0)return d(f);else{var b=new e.ProjectionTextModel(a),c=a.getLineStart(a.getLineAtOffset(f.start)),i=a.getCharCount();f.end!==i&&b.addProjection({start:f.end,end:i});c>0&&b.addProjection({start:0,end:c});return b}else{c="<div><em>"+k.multipleAnnotations+"</em></div>";for(i=0;i<b.length;i++)f=
-b[i],(f=d(f))&&(c+=f);return c}},_getNodeStyle:function(b,d,f){var a;if(b&&(a=b.style[d],!a))if(b.currentStyle){for(a=0;(a=d.indexOf("-",a))!==-1;)d=d.substring(0,a)+d.substring(a+1,a+2).toUpperCase()+d.substring(a+2);a=b.currentStyle[d]}else a=(b=b.ownerDocument.defaultView.getComputedStyle(b,null))?b.getPropertyValue(d):null;return a||f},_isNode:function(b){return typeof Node==="object"?b instanceof Node:b&&typeof b==="object"&&typeof b.nodeType==="number"&&typeof b.nodeName==="string"}};return{Tooltip:d}});
-define("orion/editor/rulers",["i18n!orion/editor/nls/messages","orion/editor/annotations","orion/editor/tooltip","orion/util"],function(k,p,o,e){function b(a,c,b,d){this._location=c||"left";this._overview=b||"page";this._rulerStyle=d;this._view=null;var f=this;this._listener={onTextModelChanged:function(a){f._onTextModelChanged(a)},onAnnotationModelChanged:function(a){f._onAnnotationModelChanged(a)}};this.setAnnotationModel(a)}function d(a,c,d,f,e){b.call(this,a,c,"page",d);this._oddStyle=f||{style:{backgroundColor:"white"}};
-this._evenStyle=e||{style:{backgroundColor:"white"}};this._numOfDigits=0}function h(a,c,d){b.call(this,a,c,"page",d)}function j(a,c,d){b.call(this,a,c,"document",d)}function f(a,c,b){h.call(this,a,c,b)}b.prototype={getAnnotations:function(a,c){var b=this._annotationModel;if(!b)return[];var d=this._view.getModel(),f=d.getLineStart(a),l=d.getLineEnd(c-1),e=d;d.getBaseModel&&(e=d.getBaseModel(),f=d.mapOffset(f),l=d.mapOffset(l));for(var h=[],b=this.getAnnotationsByType(b,f,l),f=0;f<b.length;f++)for(var l=
-b[f],j=e.getLineAtOffset(l.start),k=e.getLineAtOffset(Math.max(l.start,l.end-1)),g=j;g<=k;g++){var o=g;if(d!==e){o=e.getLineStart(g);o=d.mapOffset(o,!0);if(o===-1)continue;o=d.getLineAtOffset(o)}if(a<=o&&o<c){var p=this._mergeAnnotation(h[o],l,g-j,k-j+1);p&&(h[o]=p)}}if(!this._multiAnnotation&&this._multiAnnotationOverlay)for(var s in h)h[s]._multiple&&(h[s].html+=this._multiAnnotationOverlay.html);return h},getAnnotationModel:function(){return this._annotationModel},getLocation:function(){return this._location},
-getOverview:function(){return this._overview},getRulerStyle:function(){return this._rulerStyle},getView:function(){return this._view},getWidestAnnotation:function(){return null},setAnnotationModel:function(a){this._annotationModel&&this._annotationModel.removEventListener("Changed",this._listener.onAnnotationModelChanged);(this._annotationModel=a)&&this._annotationModel.addEventListener("Changed",this._listener.onAnnotationModelChanged)},setMultiAnnotation:function(a){this._multiAnnotation=a},setMultiAnnotationOverlay:function(a){this._multiAnnotationOverlay=
-a},setView:function(a){this._onTextModelChanged&&this._view&&this._view.removeEventListener("ModelChanged",this._listener.onTextModelChanged);this._view=a;this._onTextModelChanged&&this._view&&this._view.addEventListener("ModelChanged",this._listener.onTextModelChanged)},onClick:function(a){if(a!==void 0){var c=this._view,b=c.getModel(),d=b,f=b.getLineStart(a),l=f,e=this._annotationModel;if(e){l=c.getSelection();c=Math.max(l.start,l.end);l=b.getLineEnd(a,!0);f<=c&&c<b.getLineEnd(a)&&(f=c+1);b.getBaseModel&&
-(f=b.mapOffset(f),l=b.mapOffset(l),d=b.getBaseModel());for(var h,a=e.getAnnotations(f,l);!h&&a.hasNext();)l=a.next(),this.isAnnotationTypeVisible(l.type)&&(h=l);h&&d.getLineAtOffset(h.start)===d.getLineAtOffset(f)?(f=h.start,l=h.end):l=f;b.getBaseModel&&(f=b.mapOffset(f,!0),l=b.mapOffset(l,!0))}(b=o.Tooltip.getTooltip(this._view))&&b.setTarget(null);this._view.setSelection(l,f,1/3,function(){})}},onDblClick:function(){},onMouseMove:function(a,c){var b=o.Tooltip.getTooltip(this._view);if(b&&!(b.isVisible()&&
-this._tooltipLineIndex===a)){this._tooltipLineIndex=a;var d=this;b.setTarget({y:c.clientY,getTooltipInfo:function(){return d._getTooltipInfo(d._tooltipLineIndex,this.y)}})}},onMouseOver:function(a,c){this.onMouseMove(a,c)},onMouseOut:function(){var a=o.Tooltip.getTooltip(this._view);a&&a.setTarget(null)},_getTooltipInfo:function(a,c){if(a!==void 0){var b=this._view,d=b.getModel(),f=this._annotationModel,l=[];if(f){var l=d.getLineStart(a),e=d.getLineEnd(a);d.getBaseModel&&(l=d.mapOffset(l),e=d.mapOffset(e));
-l=this.getAnnotationsByType(f,l,e)}f=this._getTooltipContents(a,l);if(!f)return null;f={contents:f,anchor:this.getLocation()};l=b.getClientArea();l.y=this.getOverview()==="document"?b.convert({y:c},"view","document").y:b.getLocationAtOffset(d.getLineStart(a)).y;b.convert(l,"document","page");f.x=l.x;f.y=l.y;f.anchor==="right"&&(f.x+=l.width);return f}},_getTooltipContents:function(a,c){return c},_onAnnotationModelChanged:function(a){function c(a){for(var c=0;c<a.length;c++)if(f.isAnnotationTypeVisible(a[c].type)){var l=
-a[c].start,e=a[c].end;d.getBaseModel&&(l=d.mapOffset(l,!0),e=d.mapOffset(e,!0));l!==-1&&e!==-1&&b.redrawLines(d.getLineAtOffset(l),d.getLineAtOffset(Math.max(l,e-1))+1,f)}}var b=this._view;if(b){var d=b.getModel(),f=this,l=d.getLineCount();a.textModelChangedEvent?(a=a.textModelChangedEvent.start,d.getBaseModel&&(a=d.mapOffset(a,!0)),a=d.getLineAtOffset(a),b.redrawLines(a,l,f)):(c(a.added),c(a.removed),c(a.changed))}},_mergeAnnotation:function(a,c,b){a||(a={});if(b===0)if(a.html&&c.html){if(c.html!==
-a.html&&!a._multiple&&this._multiAnnotation)a.html=this._multiAnnotation.html;a._multiple=!0}else a.html=c.html;a.style=this._mergeStyle(a.style,c.style);return a},_mergeStyle:function(a,c){if(c){a||(a={});a.styleClass&&c.styleClass&&a.styleClass!==c.styleClass?a.styleClass+=" "+c.styleClass:a.styleClass=c.styleClass;var b;if(c.style){if(!a.style)a.style={};for(b in c.style)a.style[b]||(a.style[b]=c.style[b])}if(c.attributes){if(!a.attributes)a.attributes={};for(b in c.attributes)a.attributes[b]||
-(a.attributes[b]=c.attributes[b])}}return a}};p.AnnotationTypeList.addMixin(b.prototype);d.prototype=new b;d.prototype.getAnnotations=function(a,c){for(var d=b.prototype.getAnnotations.call(this,a,c),f=this._view.getModel(),e=a;e<c;e++){var l=e&1?this._oddStyle:this._evenStyle,h=e;f.getBaseModel&&(h=f.getLineStart(h),h=f.getBaseModel().getLineAtOffset(f.mapOffset(h)));d[e]||(d[e]={});d[e].html=h+1+"";if(!d[e].style)d[e].style=l}return d};d.prototype.getWidestAnnotation=function(){var a=this._view.getModel().getLineCount();
-return this.getAnnotations(a-1,a)[a-1]};d.prototype._onTextModelChanged=function(a){var a=a.start,c=this._view.getModel(),b=((c.getBaseModel?c.getBaseModel().getLineCount():c.getLineCount())+"").length;if(this._numOfDigits!==b)this._numOfDigits=b,this._view.redrawLines(c.getLineAtOffset(a),c.getLineCount(),this)};h.prototype=new b;j.prototype=new b;j.prototype.getRulerStyle=function(){var a={style:{lineHeight:"1px",fontSize:"1px"}};return a=this._mergeStyle(a,this._rulerStyle)};j.prototype._getTooltipContents=
-function(a,c){if(c.length===0){var d=this._view.getModel(),f=a;d.getBaseModel&&(f=d.getLineStart(f),f=d.getBaseModel().getLineAtOffset(d.mapOffset(f)));return e.formatMessage(k.line,f+1)}return b.prototype._getTooltipContents.call(this,a,c)};j.prototype._mergeAnnotation=function(a,c,b,d){if(b===0){if(!a)a={html:"&nbsp;",style:{style:{height:3*d+"px"}}},a.style=this._mergeStyle(a.style,c.overviewStyle);return a}};f.prototype=new h;f.prototype.onClick=function(a){if(a!==void 0){var c=this._annotationModel;
-if(c){var b=this._view.getModel(),d=b.getLineStart(a),a=b.getLineEnd(a,!0);b.getBaseModel&&(d=b.mapOffset(d),a=b.mapOffset(a),b=b.getBaseModel());for(var f,c=c.getAnnotations(d,a);!f&&c.hasNext();)a=c.next(),this.isAnnotationTypeVisible(a.type)&&(f=a);f&&b.getLineAtOffset(f.start)===b.getLineAtOffset(d)&&((b=o.Tooltip.getTooltip(this._view))&&b.setTarget(null),f.expanded?f.collapse():f.expand(),this._annotationModel.modifyAnnotation(f))}}};f.prototype._getTooltipContents=function(a,c){return c.length===
-1&&c[0].expanded?null:h.prototype._getTooltipContents.call(this,a,c)};f.prototype._onAnnotationModelChanged=function(a){function c(a){for(l=0;l<a.length;l++)if(f.isAnnotationTypeVisible(a[l].type)){var c=a[l].start;d.getBaseModel&&(c=d.mapOffset(c,!0));c!==-1&&(j=Math.min(j,d.getLineAtOffset(c)))}}if(a.textModelChangedEvent)h.prototype._onAnnotationModelChanged.call(this,a);else{var b=this._view;if(b){var d=b.getModel(),f=this,l,e=d.getLineCount(),j=e;c(a.added);c(a.removed);c(a.changed);a=b.getRulers();
-for(l=0;l<a.length;l++)b.redrawLines(j,e,a[l])}}};return{Ruler:b,AnnotationRuler:h,LineNumberRuler:d,OverviewRuler:j,FoldingRuler:f}});
-define("orion/editor/undoStack",[],function(){function k(e,b,d){this.offset=e;this.text=b;this.previousText=d}function p(e){this.owner=e;this.changes=[]}function o(e,b){this.view=e;this.size=b!==void 0?b:100;this.reset();var d=e.getModel();d.getBaseModel&&(d=d.getBaseModel());this.model=d;var h=this;this._listener={onChanging:function(b){h._onChanging(b)},onDestroy:function(b){h._onDestroy(b)}};d.addEventListener("Changing",this._listener.onChanging);e.addEventListener("Destroy",this._listener.onDestroy)}
-k.prototype={undo:function(e,b){this._doUndoRedo(this.offset,this.previousText,this.text,e,b);return!0},redo:function(e,b){this._doUndoRedo(this.offset,this.text,this.previousText,e,b);return!0},_doUndoRedo:function(e,b,d,h,j){var f=h.getModel();if(f.mapOffset&&h.annotationModel){var a=f.mapOffset(e,!0);if(a<0)for(var c=h.annotationModel.getAnnotations(e,e+1);c.hasNext();){var i=c.next();if(i.type==="orion.annotation.folding"){i.expand();a=f.mapOffset(e,!0);break}}if(a<0)return;e=a}f.setText(b,e,
-e+d.length);j&&h.setSelection(e,e+b.length)}};p.prototype={add:function(e){this.changes.push(e)},end:function(e){this.endSelection=e.getSelection();this.endCaret=e.getCaretOffset();(e=this.owner)&&e.end&&e.end()},undo:function(e,b){this.changes.length>1&&e.setRedraw(!1);for(var d=this.changes.length-1;d>=0;d--)this.changes[d].undo(e,!1);this.changes.length>1&&e.setRedraw(!0);if(b){var d=this.startSelection.start,h=this.startSelection.end;e.setSelection(this.startCaret?d:h,this.startCaret?h:d)}(d=
-this.owner)&&d.undo&&d.undo();return this.changes.length>0},redo:function(e,b){this.changes.length>1&&e.setRedraw(!1);for(var d=0;d<this.changes.length;d++)this.changes[d].redo(e,!1);this.changes.length>1&&e.setRedraw(!0);if(b){var d=this.endSelection.start,h=this.endSelection.end;e.setSelection(this.endCaret?d:h,this.endCaret?h:d)}(d=this.owner)&&d.redo&&d.redo();return this.changes.length>0},start:function(e){this.startSelection=e.getSelection();this.startCaret=e.getCaretOffset();(e=this.owner)&&
-e.start&&e.start()}};o.prototype={add:function(e){this.compoundChange?this.compoundChange.add(e):(this.stack.splice(this.index,this.stack.length-this.index,e),this.index++,this.stack.length>this.size&&(this.stack.shift(),this.index--,this.cleanIndex--))},markClean:function(){this.endCompoundChange();this._commitUndo();this.cleanIndex=this.index},isClean:function(){return this.cleanIndex===this.getSize().undo},canUndo:function(){return this.getSize().undo>0},canRedo:function(){return this.getSize().redo>
-0},endCompoundChange:function(){this.compoundChange&&this.compoundChange.end(this.view);this.compoundChange=void 0},getSize:function(){var e=this.index,b=this.stack.length;this._undoStart!==void 0&&e++;return{undo:e,redo:b-e}},undo:function(){this._commitUndo();var e;e=!1;this._ignoreUndo=!0;do{if(this.index<=0)break;e=this.stack[--this.index]}while(!(e=e.undo(this.view,!0)));this._ignoreUndo=!1;return e},redo:function(){this._commitUndo();var e;this._ignoreUndo=!0;do{if(this.index>=this.stack.length)break;
-e=this.stack[this.index++]}while(!e.redo(this.view,!0));this._ignoreUndo=!1;return!0},reset:function(){this.index=this.cleanIndex=0;this.stack=[];this._undoStart=void 0;this._undoText="";this._undoType=0;this._ignoreUndo=!1;this._compoundChange=void 0},startCompoundChange:function(e){this._commitUndo();e=new p(e);this.add(e);this.compoundChange=e;this.compoundChange.start(this.view);return this.compoundChange},_commitUndo:function(){if(this._undoStart!==void 0)this._undoType===-1?this.add(new k(this._undoStart,
-"",this._undoText)):this.add(new k(this._undoStart,this._undoText,"")),this._undoStart=void 0,this._undoText="",this._undoType=0;this.endCompoundChange()},_onDestroy:function(){this.model.removeEventListener("Changing",this._listener.onChanging);this.view.removeEventListener("Destroy",this._listener.onDestroy)},_onChanging:function(e){var b=e.text,d=e.start,h=e.removedCharCount,e=e.addedCharCount;if(!this._ignoreUndo){this._undoStart!==void 0&&!(e===1&&h===0&&this._undoType===1&&d===this._undoStart+
-this._undoText.length||e===0&&h===1&&this._undoType===-1&&(d+1===this._undoStart||d===this._undoStart))&&this._commitUndo();if(!this.compoundChange)if(e===1&&h===0){if(this._undoStart===void 0)this._undoStart=d;this._undoText+=b;this._undoType=1;return}else if(e===0&&h===1){b=this._undoText.length>0&&this._undoStart===d;this._undoStart=d;this._undoType=-1;b?this._undoText+=this.model.getText(d,d+h):this._undoText=this.model.getText(d,d+h)+this._undoText;return}this.add(new k(d,b,this.model.getText(d,
-d+h)))}}};return{UndoStack:o}});
-define("orion/editor/textDND",[],function(){function k(k,o){this._view=k;this._undoStack=o;this._dragSelection=null;this._dropOffset=-1;this._dropText=null;var e=this;this._listener={onDragStart:function(b){e._onDragStart(b)},onDragEnd:function(b){e._onDragEnd(b)},onDragEnter:function(b){e._onDragEnter(b)},onDragOver:function(b){e._onDragOver(b)},onDrop:function(b){e._onDrop(b)},onDestroy:function(b){e._onDestroy(b)}};k.addEventListener("DragStart",this._listener.onDragStart);k.addEventListener("DragEnd",
-this._listener.onDragEnd);k.addEventListener("DragEnter",this._listener.onDragEnter);k.addEventListener("DragOver",this._listener.onDragOver);k.addEventListener("Drop",this._listener.onDrop);k.addEventListener("Destroy",this._listener.onDestroy)}k.prototype={destroy:function(){var k=this._view;if(k)k.removeEventListener("DragStart",this._listener.onDragStart),k.removeEventListener("DragEnd",this._listener.onDragEnd),k.removeEventListener("DragEnter",this._listener.onDragEnter),k.removeEventListener("DragOver",
-this._listener.onDragOver),k.removeEventListener("Drop",this._listener.onDrop),k.removeEventListener("Destroy",this._listener.onDestroy),this._view=null},_onDestroy:function(){this.destroy()},_onDragStart:function(k){var o=this._view,e=o.getSelection(),o=o.getModel();if(o.getBaseModel)e.start=o.mapOffset(e.start),e.end=o.mapOffset(e.end),o=o.getBaseModel();if(o=o.getText(e.start,e.end))this._dragSelection=e,k.event.dataTransfer.effectAllowed="copyMove",k.event.dataTransfer.setData("Text",o)},_onDragEnd:function(k){var o=
-this._view;if(this._dragSelection){this._undoStack&&this._undoStack.startCompoundChange();(k=k.event.dataTransfer.dropEffect==="move")&&o.setText("",this._dragSelection.start,this._dragSelection.end);if(this._dropText){var e=this._dropText,b=this._dropOffset;if(k)if(b>=this._dragSelection.end)b-=this._dragSelection.end-this._dragSelection.start;else if(b>=this._dragSelection.start)b=this._dragSelection.start;o.setText(e,b,b);o.setSelection(b,b+e.length);this._dropText=null;this._dropOffset=-1}this._undoStack&&
-this._undoStack.endCompoundChange()}this._dragSelection=null},_onDragEnter:function(k){this._onDragOver(k)},_onDragOver:function(k){var o=k.event.dataTransfer.types;if(o){var e=!this._view.getOptions("readonly");e&&(e=o.contains?o.contains("text/plain"):o.indexOf("text/plain")!==-1);if(!e)k.event.dataTransfer.dropEffect="none"}},_onDrop:function(k){var o=this._view,e=k.event.dataTransfer.getData("Text");if(e)k=o.getOffsetAtLocation(k.x,k.y),this._dragSelection?(this._dropOffset=k,this._dropText=e):
-(o.setText(e,k,k),o.setSelection(k,k+e.length))}};return{TextDND:k}});
-define("orion/editor/editor","i18n!orion/editor/nls/messages,orion/keyBinding,orion/editor/eventTarget,orion/editor/tooltip,orion/editor/annotations,orion/util".split(","),function(k,p,o,e,b,d){function h(b){this._textViewFactory=b.textViewFactory;this._undoStackFactory=b.undoStackFactory;this._textDNDFactory=b.textDNDFactory;this._annotationFactory=b.annotationFactory;this._foldingRulerFactory=b.foldingRulerFactory;this._lineNumberRulerFactory=b.lineNumberRulerFactory;this._contentAssistFactory=
-b.contentAssistFactory;this._keyBindingFactory=b.keyBindingFactory;this._statusReporter=b.statusReporter;this._domNode=b.domNode;this._foldingRuler=this._overviewRuler=this._lineNumberRuler=this._annotationRuler=this._annotationModel=this._annotationStyler=null;this._dirty=!1;this._title=this._contentAssist=null}function j(b){var a=this,c=Array.prototype.slice.call(arguments,1);return c.length?function(){return arguments.length?a.apply(b,c.concat(Array.prototype.slice.call(arguments))):a.apply(b,
-c)}:function(){return arguments.length?a.apply(b,arguments):a.call(b)}}h.prototype={destroy:function(){this.uninstallTextView();this._textViewFactory=this._undoStackFactory=this._textDNDFactory=this._annotationFactory=this._foldingRulerFactory=this._lineNumberRulerFactory=this._contentAssistFactory=this._keyBindingFactory=this._statusReporter=this._domNode=null},getAnnotationModel:function(){return this._annotationModel},getAnnotationRuler:function(){return this._annotationRuler},getAnnotationStyler:function(){return this._annotationStyler},
-getFoldingRuler:function(){return this._foldingRuler},getLineNumberRuler:function(){return this._lineNumberRuler},getModel:function(){var b=this._textView.getModel();b.getBaseModel&&(b=b.getBaseModel());return b},getOverviewRuler:function(){return this._overviewRuler},getTextView:function(){return this._textView},getTitle:function(){return this._title},getKeyModes:function(){return this._textView.getKeyModes()},isDirty:function(){return this._dirty},setAnnotationRulerVisible:function(b){if(this._annotationRulerVisible!==
-b&&(this._annotationRulerVisible=b,this._annotationRuler)){var a=this._textView;b?a.addRuler(this._annotationRuler,0):a.removeRuler(this._annotationRuler)}},setFoldingRulerVisible:function(b){if(this._foldingRulerVisible!==b&&(this._foldingRulerVisible=b,this._foldingRuler)){var a=this._textView;a.getModel().getBaseModel&&(b?a.addRuler(this._foldingRuler,100):a.removeRuler(this._foldingRuler))}},setDirty:function(b){if(this._dirty!==b)this._dirty=b,this.onDirtyChanged({type:"DirtyChanged"})},setLineNumberRulerVisible:function(b){if(this._lineNumberRulerVisible!==
-b&&(this._lineNumberRulerVisible=b,this._lineNumberRuler)){var a=this._textView;b?a.addRuler(this._lineNumberRuler,1):a.removeRuler(this._lineNumberRuler)}},setOverviewRulerVisible:function(b){if(this._overviewRulerVisible!==b&&(this._overviewRulerVisible=b,this._overviewRuler)){var a=this._textView;b?a.addRuler(this._overviewRuler):a.removeRuler(this._overviewRuler)}},mapOffset:function(b,a){var c=this._textView.getModel();c.getBaseModel&&(b=c.mapOffset(b,a));return b},getCaretOffset:function(){return this.mapOffset(this._textView.getCaretOffset())},
-getSelection:function(){var b=this._textView,a=b.getSelection(),b=b.getModel();if(b.getBaseModel)a.start=b.mapOffset(a.start),a.end=b.mapOffset(a.end);return a},getText:function(b,a){var c=this._textView.getModel();c.getBaseModel&&(c=c.getBaseModel());return c.getText(b,a)},_expandOffset:function(d){var a=this._textView.getModel(),c=this._annotationModel;if(c&&a.getBaseModel)for(d=c.getAnnotations(d,d+1);d.hasNext();)a=d.next(),a.type===b.AnnotationType.ANNOTATION_FOLDING&&a.expand&&(a.expand(),c.modifyAnnotation(a))},
-setCaretOffset:function(b,a,c){var d=this._textView,e=d.getModel();e.getBaseModel&&(this._expandOffset(b),b=e.mapOffset(b,!0));d.setCaretOffset(b,a,c)},setText:function(b,a,c){var d=this._textView,e=d.getModel();e.getBaseModel&&(a!==void 0&&(this._expandOffset(a),a=e.mapOffset(a,!0)),c!==void 0&&(this._expandOffset(c),c=e.mapOffset(c,!0)));d.setText(b,a,c)},setFoldingEnabled:function(b){this.setFoldingRulerVisible(b)},setSelection:function(b,a,c,d){var e=this._textView,n=e.getModel();n.getBaseModel&&
-(this._expandOffset(b),this._expandOffset(a),b=n.mapOffset(b,!0),a=n.mapOffset(a,!0));e.setSelection(b,a,c,d)},moveSelection:function(b,a,c,d){var e=this._textView;this.setSelection(b,a||b,1/3,function(){(d===void 0||d)&&e.focus();c&&c()})},checkDirty:function(){this.setDirty(!this._undoStack.isClean())},reportStatus:function(b,a,c){this._statusReporter&&this._statusReporter(b,a,c)},_getTooltipInfo:function(b,a){var c=this._textView,d=this.getAnnotationModel();if(!d)return null;var e=this._annotationStyler;
-if(!e)return null;var n=c.getOffsetAtLocation(b,a);if(n===-1)return null;n=this.mapOffset(n);e=e.getAnnotationsByType(d,n,n+1);d=[];for(n=0;n<e.length;n++)e[n].rangeStyle&&d.push(e[n]);if(d.length===0)return null;c=c.convert({x:b,y:a},"document","page");return{contents:d,anchor:"left",x:c.x+10,y:c.y+20}},_highlightCurrentLine:function(d,a){var c=this._annotationModel;if(c){var i=this._textView.getModel(),e=a?i.getLineAtOffset(a.start):-1,n=i.getLineAtOffset(d.start),l=d.start===d.end,h=!a||a.start===
-a.end,j=i.getLineStart(n),k=i.getLineEnd(n);i.getBaseModel&&(j=i.mapOffset(j),k=i.mapOffset(k));i=this._currentLineAnnotation;if(!(e===n&&h&&l&&i&&i.start===j&&i.end===k)){var e=i?[i]:null,o;l&&(i=b.AnnotationType.createAnnotation(b.AnnotationType.ANNOTATION_CURRENT_LINE,j,k),o=[i]);this._currentLineAnnotation=i;c.replaceAnnotations(e,o)}}},installTextView:function(){this._textView=this._textViewFactory();if(this._undoStackFactory)this._undoStack=this._undoStackFactory.createUndoStack(this);if(this._textDNDFactory)this._textDND=
-this._textDNDFactory.createTextDND(this,this._undoStack);if(this._contentAssistFactory)this._contentAssist=this._contentAssistFactory.createContentAssistMode(this).getContentAssist();var d=this,a=this._textView,c=this;this._listener={onModelChanged:function(){c.checkDirty()},onMouseOver:function(a){c._listener.onMouseMove(a)},onMouseMove:function(b){var d=e.Tooltip.getTooltip(a);if(d&&!(c._listener.lastMouseX===b.event.clientX&&c._listener.lastMouseY===b.event.clientY))c._listener.lastMouseX=b.event.clientX,
-c._listener.lastMouseY=b.event.clientY,d.setTarget({x:b.x,y:b.y,getTooltipInfo:function(){return c._getTooltipInfo(this.x,this.y)}})},onMouseOut:function(b){var d=e.Tooltip.getTooltip(a);if(d&&!(c._listener.lastMouseX===b.event.clientX&&c._listener.lastMouseY===b.event.clientY))c._listener.lastMouseX=b.event.clientX,c._listener.lastMouseY=b.event.clientY,d.setTarget(null)},onScroll:function(){var c=e.Tooltip.getTooltip(a);c&&c.setTarget(null)},onSelection:function(a){c._updateCursorStatus();c._highlightCurrentLine(a.newValue,
-a.oldValue)}};a.addEventListener("ModelChanged",this._listener.onModelChanged);a.addEventListener("Selection",this._listener.onSelection);a.addEventListener("MouseOver",this._listener.onMouseOver);a.addEventListener("MouseOut",this._listener.onMouseOut);a.addEventListener("MouseMove",this._listener.onMouseMove);a.addEventListener("Scroll",this._listener.onScroll);this._keyBindingFactory&&(typeof this._keyBindingFactory==="function"?this._keyBindingFactory(this,this.getKeyModes(),this._undoStack,this._contentAssist):
-this._keyBindingFactory.createKeyBindings(d,this._undoStack,this._contentAssist));var i=function(a){if(a!==void 0&&a!==-1){for(var c=this.getView().getModel(),i=this.getAnnotationModel(),e=d.mapOffset(c.getLineStart(a)),a=d.mapOffset(c.getLineEnd(a)),c=i.getAnnotations(e,a),m=null;c.hasNext();){var g=c.next();if(g.type===b.AnnotationType.ANNOTATION_BOOKMARK){m=g;break}}m?i.removeAnnotation(m):(m=b.AnnotationType.createAnnotation(b.AnnotationType.ANNOTATION_BOOKMARK,e,a),m.title=void 0,i.addAnnotation(m))}};
-if(this._annotationFactory){var m=a.getModel();m.getBaseModel&&(m=m.getBaseModel());if(this._annotationModel=this._annotationFactory.createAnnotationModel(m))if(m=this._annotationStyler=this._annotationFactory.createAnnotationStyler(a,this._annotationModel))m.addAnnotationType(b.AnnotationType.ANNOTATION_CURRENT_SEARCH),m.addAnnotationType(b.AnnotationType.ANNOTATION_MATCHING_SEARCH),m.addAnnotationType(b.AnnotationType.ANNOTATION_ERROR),m.addAnnotationType(b.AnnotationType.ANNOTATION_WARNING),m.addAnnotationType(b.AnnotationType.ANNOTATION_MATCHING_BRACKET),
-m.addAnnotationType(b.AnnotationType.ANNOTATION_CURRENT_BRACKET),m.addAnnotationType(b.AnnotationType.ANNOTATION_CURRENT_LINE),m.addAnnotationType(b.AnnotationType.ANNOTATION_READ_OCCURRENCE),m.addAnnotationType(b.AnnotationType.ANNOTATION_WRITE_OCCURRENCE),m.addAnnotationType(b.AnnotationType.ANNOTATION_SELECTED_LINKED_GROUP),m.addAnnotationType(b.AnnotationType.ANNOTATION_CURRENT_LINKED_GROUP),m.addAnnotationType(b.AnnotationType.ANNOTATION_LINKED_GROUP),m.addAnnotationType("orion.annotation.highlightError");
-a.annotationModel=this._annotationModel;var m=this._annotationFactory.createAnnotationRulers(this._annotationModel),n=this._annotationRuler=m.annotationRuler;if(n)n.onDblClick=i,n.setMultiAnnotationOverlay({html:"<div class='annotationHTML overlay'></div>"}),n.addAnnotationType(b.AnnotationType.ANNOTATION_ERROR),n.addAnnotationType(b.AnnotationType.ANNOTATION_WARNING),n.addAnnotationType(b.AnnotationType.ANNOTATION_TASK),n.addAnnotationType(b.AnnotationType.ANNOTATION_BOOKMARK);this.setAnnotationRulerVisible(!0);
-if(n=this._overviewRuler=m.overviewRuler)n.addAnnotationType(b.AnnotationType.ANNOTATION_CURRENT_SEARCH),n.addAnnotationType(b.AnnotationType.ANNOTATION_MATCHING_SEARCH),n.addAnnotationType(b.AnnotationType.ANNOTATION_ERROR),n.addAnnotationType(b.AnnotationType.ANNOTATION_WARNING),n.addAnnotationType(b.AnnotationType.ANNOTATION_TASK),n.addAnnotationType(b.AnnotationType.ANNOTATION_BOOKMARK),n.addAnnotationType(b.AnnotationType.ANNOTATION_MATCHING_BRACKET),n.addAnnotationType(b.AnnotationType.ANNOTATION_CURRENT_BRACKET),
-n.addAnnotationType(b.AnnotationType.ANNOTATION_CURRENT_LINE),n.addAnnotationType(b.AnnotationType.ANNOTATION_READ_OCCURRENCE),n.addAnnotationType(b.AnnotationType.ANNOTATION_WRITE_OCCURRENCE);this.setOverviewRulerVisible(!0)}if(this._lineNumberRulerFactory)this._lineNumberRuler=this._lineNumberRulerFactory.createLineNumberRuler(this._annotationModel),this._lineNumberRuler.onDblClick=i,this.setLineNumberRulerVisible(!0);if(this._foldingRulerFactory)this._foldingRuler=this._foldingRulerFactory.createFoldingRuler(this._annotationModel),
-this._foldingRuler.addAnnotationType(b.AnnotationType.ANNOTATION_FOLDING),this.setFoldingRulerVisible(!1);this.dispatchEvent({type:"TextViewInstalled",textView:a})},uninstallTextView:function(){var b=this._textView;if(b)b.destroy(),this._textView=this._undoStack=this._textDND=this._contentAssist=this._listener=this._annotationModel=this._annotationStyler=this._annotationRuler=this._overviewRuler=this._lineNumberRuler=this._foldingRuler=this._currentLineAnnotation=this._title=null,this._dirty=!1,this.dispatchEvent({type:"TextViewUninstalled",
-textView:b})},_updateCursorStatus:function(){var b=this.getModel(),a=this.getCaretOffset(),c=b.getLineAtOffset(a),b=b.getLineStart(c);a-=b;for(var b=this.getKeyModes(),i=0;i<b.length;i++){var e=b[i];if(e.isActive()&&e.isStatusActive&&e.isStatusActive())return}this.reportStatus(d.formatMessage(k.lineColumn,c+1,a+1))},showProblems:function(d){var a=this._annotationModel;if(a){for(var c=[],i=[],e=a.getTextModel(),n=a.getAnnotations(0,e.getCharCount()),l;n.hasNext();)l=n.next(),(l.type===b.AnnotationType.ANNOTATION_ERROR||
-l.type===b.AnnotationType.ANNOTATION_WARNING)&&c.push(l);if(d)for(n=0;n<d.length;n++)if(l=d[n]){var h=l.description.replace(/'/g,"&#39;").replace(/"/g,"&#34;"),j=e.getLineStart(l.line-1);l=b.AnnotationType.createAnnotation(l.severity==="error"?b.AnnotationType.ANNOTATION_ERROR:b.AnnotationType.ANNOTATION_WARNING,j+l.start-1,j+l.end,h);i.push(l)}a.replaceAnnotations(c,i)}},showOccurrences:function(d){var a=this._annotationModel;if(a){for(var c=[],i=[],e=a.getTextModel(),h=a.getAnnotations(0,e.getCharCount()),
-l;h.hasNext();)l=h.next(),(l.type===b.AnnotationType.ANNOTATION_READ_OCCURRENCE||l.type===b.AnnotationType.ANNOTATION_WRITE_OCCURRENCE)&&c.push(l);if(d)for(h=0;h<d.length;h++)if(l=d[h]){var j=e.getLineStart(l.line-1);l=b.AnnotationType.createAnnotation(l.readAccess===!0?b.AnnotationType.ANNOTATION_READ_OCCURRENCE:b.AnnotationType.ANNOTATION_WRITE_OCCURRENCE,j+l.start-1,j+l.end,l.description);i.push(l)}a.replaceAnnotations(c,i)}},showSelection:function(b,a,c,d,e){typeof b==="number"?(typeof a!=="number"&&
-(a=b),this.moveSelection(b,a)):typeof c==="number"&&(b=this.getModel().getLineStart(c-1),typeof d==="number"&&(b+=d),typeof e!=="number"&&(e=0),this.moveSelection(b,b+e))},setInput:function(b,a,c,d){this._title=b;this._textView&&(d?(this._undoStack.markClean(),this.checkDirty()):(a?this._textView.setText(a):c!==null&&c!==void 0&&(this._textView.setText(c),this._textView.getModel().setLineDelimiter("auto"),this._highlightCurrentLine(this._textView.getSelection())),this._undoStack.reset(),this.checkDirty(),
-this._textView.focus()));this.onInputChanged({type:"InputChanged",title:b,message:a,contents:c,contentsSaved:d})},onInputChanged:function(b){return this.dispatchEvent(b)},onGotoLine:function(b,a,c){if(this._textView){var d=this.getModel(),b=Math.max(0,Math.min(b,d.getLineCount()-1)),e=d.getLineStart(b),h=0;c===void 0&&(c=0);typeof a==="string"?(b=d.getLine(b).indexOf(a),b!==-1&&(h=b,c=h+a.length)):(h=a,a=d.getLineEnd(b)-e,h=Math.min(h,a),c=Math.min(c,a));this.moveSelection(e+h,e+c)}},onDirtyChanged:function(b){return this.dispatchEvent(b)}};
-o.EventTarget.addMixin(h.prototype);if(!Function.prototype.bind)Function.prototype.bind=j;return{Editor:h}});define("orion/editor/regex",[],function(){return{escape:function(k){return k.replace(/([\\$\^*\/+?\.\(\)|{}\[\]])/g,"\\$&")},parse:function(k){return(k=/^\s*\/(.+)\/([gim]{0,3})\s*$/.exec(k))?{pattern:k[1],flags:k[2]}:null}}});
-define("orion/objects",[],function(){return{mixin:function(k){Array.prototype.slice.call(arguments,1).forEach(function(p){for(var o=Object.keys(p),e=0;e<o.length;e++){var b=o[e];k[b]=p[b]}})}}});
-define("orion/editor/find","i18n!orion/editor/nls/messages,orion/keyBinding,orion/editor/keyModes,orion/editor/annotations,orion/editor/regex,orion/objects,orion/util".split(","),function(k,p,o,e,b,d,h){function j(a){var d=a.getTextView();o.KeyMode.call(this,d);this.editor=a;this._active=!1;this._success=!0;this._ignoreSelection=!1;this._prefix="";d.setAction("incrementalFindCancel",function(){this.setActive(!1);return!0}.bind(this));d.setAction("incrementalFindBackspace",function(){return this._backspace()}.bind(this));
-var f=this;this._listener={onVerify:function(a){var c=f.editor,d=c.getModel(),i=c.mapOffset(a.start),c=c.mapOffset(a.end),d=d.getText(i,c);if((d=f._prefix.match(RegExp("^"+b.escape(d),"i")))&&d.length>0)f._prefix+=a.text,f._success=!0,f._status(),f.find(f._forward,!0),a.text=null},onSelection:function(){f._ignoreSelection||f.setActive(!1)}}}function f(a,b,d){if(a){this._editor=a;this._undoStack=b;this._showAll=!0;this._visible=!1;this._wrap=this._caseInsensitive=!0;this._wholeWord=!1;this._incremental=
-!0;this._regex=!1;this._findAfterReplace=!0;this._reverse=this._hideAfterFind=!1;this._timer=this._end=this._start=void 0;this._lastString="";var f=this;this._listeners={onEditorFocus:function(a){f._removeCurrentAnnotation(a)}};this.setOptions(d)}}var a={};j.prototype=new o.KeyMode;d.mixin(j.prototype,{createKeyBindings:function(){var a=p.KeyBinding,b=[];b.push({actionID:"incrementalFindBackspace",keyBinding:new a(8)});b.push({actionID:"incrementalFindCancel",keyBinding:new a(13)});b.push({actionID:"incrementalFindCancel",
-keyBinding:new a(27)});b.push({actionID:"incrementalFindReverse",keyBinding:new a(38)});b.push({actionID:"incrementalFind",keyBinding:new a(40)});b.push({actionID:"incrementalFindReverse",keyBinding:new a("k",!0,!0)});b.push({actionID:"incrementalFind",keyBinding:new a("k",!0)});return b},find:function(a,b){this._forward=a;if(!this.isActive())return this.setActive(!0),!1;var d=this._prefix;if(d.length===0)return!1;var f=this.editor,e=f.getModel(),e=a?this._success?b?this._start:f.getCaretOffset()+
-1:0:this._success?b?this._start:f.getCaretOffset():e.getCharCount()-1;if(d=f.getModel().find({string:d,start:e,reverse:!a,caseInsensitive:d.toLowerCase()===d}).next()){if(!b)this._start=e;this._ignoreSelection=this._success=!0;f.moveSelection(a?d.start:d.end,a?d.end:d.start);this._ignoreSelection=!1}else this._success=!1;this._status();return!0},isActive:function(){return this._active},isStatusActive:function(){return this.isActive()},setActive:function(a){if(this._active!==a)this._active=a,this._prefix=
-"",this._success=!0,a=this.editor.getTextView(),this._start=this.editor.getCaretOffset(),this.editor.setCaretOffset(this._start),this._active?(a.addEventListener("Verify",this._listener.onVerify),a.addEventListener("Selection",this._listener.onSelection),a.addKeyMode(this)):(a.removeEventListener("Verify",this._listener.onVerify),a.removeEventListener("Selection",this._listener.onSelection),a.removeKeyMode(this)),this._status()},_backspace:function(){var a=this._prefix,a=this._prefix=a.substring(0,
-a.length-1);return a.length===0?(this._ignoreSelection=this._success=!0,this.editor.setCaretOffset(this.editor.getSelection().start),this._ignoreSelection=!1,this._status(),!0):this.find(this._forward,!0)},_status:function(){if(this.isActive()){var a;a=this._forward?this._success?k.incrementalFindStr:k.incrementalFindStrNotFound:this._success?k.incrementalFindReverseStr:k.incrementalFindReverseStrNotFound;a=h.formatMessage(a,this._prefix);this.editor.reportStatus(a,this._success?"":"error")}else this.editor.reportStatus("")}});
-a.IncrementalFind=j;f.prototype={find:function(a,b,d){this.setOptions({reverse:!a});var f=this.getFindString(),e;if(b)f=b.findString||this.getFindString(),e=b.count;a=this.getOptions();this.setOptions(b);b=d?this._startOffset:this.getStartOffset();if((e=this._doFind(f,b,e))&&!d)this._startOffset=e.start;this.setOptions(a);this._hideAfterFind&&this.hide();return e},getStartOffset:function(){return this._start!==void 0?this._start:this._reverse?this._editor.getSelection().start-1:this._editor.getCaretOffset()},
-getFindString:function(){var a=this._editor.getSelection();return this._editor.getText(a.start,a.end)||this._lastString},getOptions:function(){return{showAll:this._showAll,caseInsensitive:this._caseInsensitive,wrap:this._wrap,wholeWord:this._wholeWord,incremental:this._incremental,regex:this._regex,findAfterReplace:this._findAfterReplace,hideAfterFind:this._hideAfterFind,reverse:this._reverse,start:this._start,end:this._end}},getReplaceString:function(){return""},hide:function(){this._visible=!1;
-if(this._savedOptions&&(this.setOptions(this._savedOptions.pop()),this._savedOptions.length===0))this._savedOptions=null;this._removeAllAnnotations();this._editor.getTextView().removeEventListener("Focus",this._listeners.onEditorFocus);this._editor.getTextView().focus()},isVisible:function(){return this._visible},replace:function(){var a=this.getFindString();if(a){var b=this._editor,d=this.getReplaceString(),f=b.getSelection().start;if(b=b.getModel().find({string:a,start:f,reverse:!1,wrap:this._wrap,
-regex:this._regex,wholeWord:this._wholeWord,caseInsensitive:this._caseInsensitive}).next())this.startUndo(),this._doReplace(b.start,b.end,a,d),this.endUndo()}this._findAfterReplace&&a&&this._doFind(a,this.getStartOffset())},replaceAll:function(){var a=this.getFindString();if(a){this._replacingAll=!0;var b=this._editor,d=b.getTextView();b.reportStatus(k.replaceAll);var f=this.getReplaceString(),e=this;window.setTimeout(function(){for(var j=0,q=0;;){var o=e._doFind(a,j);if(!o)break;q++;q===1&&(d.setRedraw(!1),
-e.startUndo());e._doReplace(o.start,o.end,a,f);j=e.getStartOffset()}q>0&&(e.endUndo(),d.setRedraw(!0));j>0?b.reportStatus(h.formatMessage(k.replacedMatches,q)):b.reportStatus(k.nothingReplaced,"error");e._replacingAll=!1},100)}},setOptions:function(a){if(a){if((a.showAll===!0||a.showAll===!1)&&this._showAll!==a.showAll)if(this._showAll=a.showAll,this.isVisible())if(this._showAll)this._markAllOccurrences(!0);else{var b=this._editor.getAnnotationModel();b&&b.removeAnnotations(e.AnnotationType.ANNOTATION_MATCHING_SEARCH)}if(a.caseInsensitive===
-!0||a.caseInsensitive===!1)this._caseInsensitive=a.caseInsensitive;if(a.wrap===!0||a.wrap===!1)this._wrap=a.wrap;if(a.wholeWord===!0||a.wholeWord===!1)this._wholeWord=a.wholeWord;if(a.incremental===!0||a.incremental===!1)this._incremental=a.incremental;if(a.regex===!0||a.regex===!1)this._regex=a.regex;if(a.findAfterReplace===!0||a.findAfterReplace===!1)this._findAfterReplace=a.findAfterReplace;if(a.hideAfterFind===!0||a.hideAfterFind===!1)this._hideAfterFind=a.hideAfterFind;if(a.reverse===!0||a.reverse===
-!1)this._reverse=a.reverse;if(a.hasOwnProperty("start"))this._start=a.start;if(a.hasOwnProperty("end"))this._end=a.end}},show:function(a){this._visible=!0;if(a){if(!this._savedOptions)this._savedOptions=[];this._savedOptions.push(this.getOptions());this.setOptions(a)}this._startOffset=this._editor.getSelection().start;this._editor.getTextView().addEventListener("Focus",this._listeners.onEditorFocus);var b=this;window.setTimeout(function(){b._incremental&&b.find(!0,null,!0)},0)},startUndo:function(){this._undoStack&&
-this._undoStack.startCompoundChange()},endUndo:function(){this._undoStack&&this._undoStack.endCompoundChange()},_doFind:function(a,b,d){var d=d||1,f=this._editor;if(!a)return this._removeAllAnnotations(),null;this._lastString=a;for(var b=f.getModel().find({string:a,start:b,end:this._end,reverse:this._reverse,wrap:this._wrap,regex:this._regex,wholeWord:this._wholeWord,caseInsensitive:this._caseInsensitive}),l,h=0;h<d&&b.hasNext();h++)l=b.next();if(!this._replacingAll){l?this._editor.reportStatus(""):
-this._editor.reportStatus(k.notFound,"error");if(this.isVisible()){d=e.AnnotationType.ANNOTATION_CURRENT_SEARCH;if(b=f.getAnnotationModel())b.removeAnnotations(d),l&&b.addAnnotation(e.AnnotationType.createAnnotation(d,l.start,l.end));if(this._showAll){this._timer&&window.clearTimeout(this._timer);var j=this;this._timer=window.setTimeout(function(){j._markAllOccurrences(!!l,a);j._timer=null},500)}}l&&f.moveSelection(l.start,l.end,null,!1)}return l},_doReplace:function(a,b,d,f){var e=this._editor;if(this._regex&&
-(f=e.getText(a,b).replace(RegExp(d,this._caseInsensitive?"i":""),f),!f))return;e.setText(f,a,b);e.setSelection(a,a+f.length,!0)},_markAllOccurrences:function(a,b){var d=this._editor.getAnnotationModel();if(d){for(var f=e.AnnotationType.ANNOTATION_MATCHING_SEARCH,l=d.getAnnotations(0,d.getTextModel().getCharCount()),h=[],j;l.hasNext();){var k=l.next();k.type===f&&h.push(k)}if(this.isVisible()&&a&&b){l=this._editor.getModel().find({string:b,regex:this._regex,wholeWord:this._wholeWord,caseInsensitive:this._caseInsensitive});
-for(j=[];l.hasNext();)k=l.next(),j.push(e.AnnotationType.createAnnotation(f,k.start,k.end))}d.replaceAnnotations(h,j)}},_removeAllAnnotations:function(){var a=this._editor.getAnnotationModel();a&&(a.removeAnnotations(e.AnnotationType.ANNOTATION_CURRENT_SEARCH),a.removeAnnotations(e.AnnotationType.ANNOTATION_MATCHING_SEARCH))},_removeCurrentAnnotation:function(){var a=this._editor.getAnnotationModel();a&&a.removeAnnotations(e.AnnotationType.ANNOTATION_CURRENT_SEARCH)}};a.Find=f;return a});
-define("orion/editor/actions","i18n!orion/editor/nls/messages,orion/keyBinding,orion/editor/annotations,orion/editor/tooltip,orion/editor/find,orion/util".split(","),function(k,p,o,e,b,d){function h(a,c,d){this.editor=a;this.undoStack=c;this._incrementalFind=new b.IncrementalFind(a);this._find=d?d:new b.Find(a,c);this._lastEditLocation=null;this.init()}function j(a,c,b,d){this.editor=a;this.undoStack=c;this.contentAssist=b;this.linkedMode=d;this.contentAssist&&this.contentAssist.addEventListener("ProposalApplied",
-this.contentAssistProposalApplied.bind(this));this.init()}var f={};h.prototype={init:function(){var a=this.editor.getTextView();this._lastEditListener={onModelChanged:function(a){if(this.editor.isDirty())this._lastEditLocation=a.start+a.addedCharCount}.bind(this)};a.addEventListener("ModelChanged",this._lastEditListener.onModelChanged);a.setAction("undo",function(){return this.undoStack?(this.undoStack.undo(),!0):!1}.bind(this),{name:k.undo});a.setAction("redo",function(){return this.undoStack?(this.undoStack.redo(),
-!0):!1}.bind(this),{name:k.redo});a.setKeyBinding(new p.KeyBinding("f",!0),"find");a.setAction("find",function(){if(this._find){var a=this.editor.getSelection();(a=prompt(k.find,this.editor.getText(a.start,a.end)))&&this._find.find(!0,{findString:a})}}.bind(this),{name:k.find});a.setKeyBinding(new p.KeyBinding("k",!0),"findNext");a.setAction("findNext",function(a){return this._find?(this._find.find(!0,a),!0):!1}.bind(this),{name:k.findNext});a.setKeyBinding(new p.KeyBinding("k",!0,!0),"findPrevious");
-a.setAction("findPrevious",function(a){return this._find?(this._find.find(!1,a),!0):!1}.bind(this),{name:k.findPrevious});a.setKeyBinding(new p.KeyBinding("j",!0),"incrementalFind");a.setAction("incrementalFind",function(){this._incrementalFind&&this._incrementalFind.find(!0);return!0}.bind(this),{name:k.incrementalFind});a.setKeyBinding(new p.KeyBinding("j",!0,!0),"incrementalFindReverse");a.setAction("incrementalFindReverse",function(){this._incrementalFind&&this._incrementalFind.find(!1);return!0}.bind(this),
-{name:k.incrementalFindReverse});a.setAction("tab",function(){return this.indentLines()}.bind(this));a.setAction("shiftTab",function(){return this.unindentLines()}.bind(this),{name:k.unindentLines});a.setKeyBinding(new p.KeyBinding(38,!1,!1,!0),"moveLinesUp");a.setAction("moveLinesUp",function(){return this.moveLinesUp()}.bind(this),{name:k.moveLinesUp});a.setKeyBinding(new p.KeyBinding(40,!1,!1,!0),"moveLinesDown");a.setAction("moveLinesDown",function(){return this.moveLinesDown()}.bind(this),{name:k.moveLinesDown});
-a.setKeyBinding(new p.KeyBinding(38,!0,!1,!0),"copyLinesUp");a.setAction("copyLinesUp",function(){return this.copyLinesUp()}.bind(this),{name:k.copyLinesUp});a.setKeyBinding(new p.KeyBinding(40,!0,!1,!0),"copyLinesDown");a.setAction("copyLinesDown",function(){return this.copyLinesDown()}.bind(this),{name:k.copyLinesDown});a.setKeyBinding(new p.KeyBinding("d",!0,!1,!1),"deleteLines");a.setAction("deleteLines",function(){return this.deleteLines()}.bind(this),{name:k.deleteLines});a.setKeyBinding(new p.KeyBinding("l",
-!d.isMac,!1,!1,d.isMac),"gotoLine");a.setAction("gotoLine",function(){return this.gotoLine()}.bind(this),{name:k.gotoLine});a.setKeyBinding(new p.KeyBinding(190,!0),"nextAnnotation");a.setAction("nextAnnotation",function(){return this.nextAnnotation(!0)}.bind(this),{name:k.nextAnnotation});a.setKeyBinding(new p.KeyBinding(188,!0),"previousAnnotation");a.setAction("previousAnnotation",function(){return this.nextAnnotation(!1)}.bind(this),{name:k.prevAnnotation});a.setKeyBinding(new p.KeyBinding("e",
-!0,!1,!0,!1),"expand");a.setAction("expand",function(){return this.expandAnnotation(!0)}.bind(this),{name:k.expand});a.setKeyBinding(new p.KeyBinding("c",!0,!1,!0,!1),"collapse");a.setAction("collapse",function(){return this.expandAnnotation(!1)}.bind(this),{name:k.collapse});a.setKeyBinding(new p.KeyBinding("e",!0,!0,!0,!1),"expandAll");a.setAction("expandAll",function(){return this.expandAnnotations(!0)}.bind(this),{name:k.expandAll});a.setKeyBinding(new p.KeyBinding("c",!0,!0,!0,!1),"collapseAll");
-a.setAction("collapseAll",function(){return this.expandAnnotations(!1)}.bind(this),{name:k.collapseAll});a.setKeyBinding(new p.KeyBinding("q",!d.isMac,!1,!1,d.isMac),"lastEdit");a.setAction("lastEdit",function(){return this.gotoLastEdit()}.bind(this),{name:k.lastEdit})},copyLinesDown:function(){var a=this.editor;if(a.getTextView().getOptions("readonly"))return!1;var c=a.getModel(),b=a.getSelection(),d=c.getLineAtOffset(b.start),b=c.getLineAtOffset(b.end>b.start?b.end-1:b.end),f=c.getLineStart(d),
-d=c.getLineEnd(b,!0),e=c.getLineCount(),h="",f=c.getText(f,d);b===e-1&&(f=(h=c.getLineDelimiter())+f);a.setText(f,d,d);a.setSelection(d+h.length,d+f.length);return!0},copyLinesUp:function(){var a=this.editor;if(a.getTextView().getOptions("readonly"))return!1;var c=a.getModel(),b=a.getSelection(),d=c.getLineAtOffset(b.start),b=c.getLineAtOffset(b.end>b.start?b.end-1:b.end),d=c.getLineStart(d),f=c.getLineEnd(b,!0),e=c.getLineCount(),h="",f=c.getText(d,f);b===e-1&&(f+=h=c.getLineDelimiter());a.setText(f,
-d,d);a.setSelection(d,d+f.length-h.length);return!0},deleteLines:function(){var a=this.editor;if(a.getTextView().getOptions("readonly"))return!1;var c=a.getSelection(),b=a.getModel(),d=b.getLineAtOffset(c.start),c=b.getLineAtOffset(c.end>c.start?c.end-1:c.end),d=b.getLineStart(d),b=b.getLineEnd(c,!0);a.setText("",d,b);return!0},expandAnnotation:function(a){var c=this.editor,b=c.getAnnotationModel();if(!b)return!0;var d=c.getModel(),f=c.getCaretOffset(),e=d.getLineAtOffset(f),f=d.getLineStart(e),e=
-d.getLineEnd(e,!0);d.getBaseModel&&(f=d.mapOffset(f),e=d.mapOffset(e),d.getBaseModel());for(var h,d=b.getAnnotations(f,e);!h&&d.hasNext();)f=d.next(),f.type===o.AnnotationType.ANNOTATION_FOLDING&&(h=f);h&&a!==h.expanded&&(a?h.expand():(c.setCaretOffset(h.start),h.collapse()),b.modifyAnnotation(h));return!0},expandAnnotations:function(a){var c=this.editor,b=c.getTextView(),d=c.getAnnotationModel();if(!d)return!0;var c=c.getModel(),f=d.getAnnotations(0,c.getCharCount());for(b.setRedraw(!1);f.hasNext();)c=
-f.next(),c.type===o.AnnotationType.ANNOTATION_FOLDING&&a!==c.expanded&&(a?c.expand():c.collapse(),d.modifyAnnotation(c));b.setRedraw(!0);return!0},indentLines:function(){var a=this.editor,c=a.getTextView();if(c.getOptions("readonly"))return!1;if(c.getOptions("tabMode")){var b=a.getModel(),d=a.getSelection(),f=b.getLineAtOffset(d.start),e=b.getLineAtOffset(d.end>d.start?d.end-1:d.end);if(f!==e){var h=[];h.push("");for(var j=f;j<=e;j++)h.push(b.getLine(j,!0));j=b.getLineStart(f);b=b.getLineEnd(e,!0);
-c=c.getOptions("tabSize","expandTab");c=c.expandTab?Array(c.tabSize+1).join(" "):"\t";a.setText(h.join(c),j,b);a.setSelection(j===d.start?d.start:d.start+c.length,d.end+(e-f+1)*c.length);return!0}return!1}},gotoLastEdit:function(){typeof this._lastEditLocation==="number"&&this.editor.showSelection(this._lastEditLocation);return!0},gotoLine:function(){var a=this.editor,c=a.getModel().getLineAtOffset(a.getCaretOffset());if(c=prompt(k.gotoLinePrompty,c+1))c=parseInt(c,10),a.onGotoLine(c-1,0);return!0},
-moveLinesDown:function(){var a=this.editor;if(a.getTextView().getOptions("readonly"))return!1;var c=a.getModel(),b=a.getSelection(),d=c.getLineAtOffset(b.start),f=c.getLineAtOffset(b.end>b.start?b.end-1:b.end),e=c.getLineCount();if(f===e-1)return!0;var d=c.getLineStart(d),b=c.getLineEnd(f,!0),h=c.getLineEnd(f+1,!0)-(b-d),j=0;f!==e-2?c=c.getText(d,b):(f=c.getLineEnd(f),c=c.getText(f,b)+c.getText(d,f),j+=b-f);this.startUndo();a.setText("",d,b);a.setText(c,h,h);a.setSelection(h+j,h+j+c.length);this.endUndo();
-return!0},moveLinesUp:function(){var a=this.editor;if(a.getTextView().getOptions("readonly"))return!1;var c=a.getModel(),b=a.getSelection(),d=c.getLineAtOffset(b.start);if(d===0)return!0;var f=c.getLineAtOffset(b.end>b.start?b.end-1:b.end),e=c.getLineCount(),b=c.getLineStart(d-1),h=c.getLineStart(d),j=c.getLineEnd(f,!0),k=c.getText(h,j),o=0;f===e-1&&(f=c.getLineEnd(d-1),d=c.getLineEnd(d-1,!0),k+=c.getText(f,d),h=f,o=d-f);this.startUndo();a.setText("",h,j);a.setText(k,b,b);a.setSelection(b,b+k.length-
-o);this.endUndo();return!0},nextAnnotation:function(a){var c=this.editor,b=c.getAnnotationModel();if(!b)return!0;for(var d=c.getModel(),f=c.getCaretOffset(),b=b.getAnnotations(a?f:0,a?d.getCharCount():f),l=null;b.hasNext();){var h=b.next();if(a){if(h.start<=f)continue}else if(h.start>=f)continue;switch(h.type){case o.AnnotationType.ANNOTATION_ERROR:case o.AnnotationType.ANNOTATION_WARNING:case o.AnnotationType.ANNOTATION_TASK:case o.AnnotationType.ANNOTATION_BOOKMARK:break;default:continue}l=h;if(a)break}if(l){var j=
-c.getTextView(),k=d.getLineAtOffset(l.start),p=e.Tooltip.getTooltip(j);if(!p)return c.moveSelection(l.start),!0;c.moveSelection(l.start,l.start,function(){p.setTarget({getTooltipInfo:function(){var a=j.convert({x:j.getLocationAtOffset(l.start).x,y:j.getLocationAtOffset(d.getLineStart(k)).y},"document","page");return{contents:[l],x:a.x,y:a.y+Math.floor(j.getLineHeight(k)*1.33)}}},0)})}return!0},unindentLines:function(){var a=this.editor,c=a.getTextView();if(c.getOptions("readonly"))return!1;if(c.getOptions("tabMode")){for(var b=
-a.getModel(),d=a.getSelection(),f=b.getLineAtOffset(d.start),e=b.getLineAtOffset(d.end>d.start?d.end-1:d.end),h=c.getOptions("tabSize"),j=Array(h+1).join(" "),k=[],o=c=0,g=f;g<=e;g++){var p=b.getLine(g,!0);if(b.getLineStart(g)!==b.getLineEnd(g))if(p.indexOf("\t")===0)p=p.substring(1),c++;else if(p.indexOf(j)===0)p=p.substring(h),c+=h;else return!0;g===f&&(o=c);k.push(p)}f=b.getLineStart(f);h=b.getLineEnd(e,!0);b=b.getLineStart(e);a.setText(k.join(""),f,h);e=f===d.start?d.start:d.start-o;d=Math.max(e,
-d.end-c+(d.end===b+1&&d.start!==d.end?1:0));a.setSelection(e,d);return!0}},startUndo:function(){this.undoStack&&this.undoStack.startCompoundChange()},endUndo:function(){this.undoStack&&this.undoStack.endCompoundChange()}};f.TextActions=h;j.prototype={init:function(){var a=this.editor.getTextView();a.setAction("lineStart",function(){return this.lineStart()}.bind(this));a.setAction("enter",function(){return this.autoIndent()}.bind(this));a.setKeyBinding(new p.KeyBinding(191,!0),"toggleLineComment");
-a.setAction("toggleLineComment",function(){return this.toggleLineComment()}.bind(this),{name:k.toggleLineComment});a.setKeyBinding(new p.KeyBinding(191,!0,!d.isMac,!1,d.isMac),"addBlockComment");a.setAction("addBlockComment",function(){return this.addBlockComment()}.bind(this),{name:k.addBlockComment});a.setKeyBinding(new p.KeyBinding(220,!0,!d.isMac,!1,d.isMac),"removeBlockComment");a.setAction("removeBlockComment",function(){return this.removeBlockComment()}.bind(this),{name:k.removeBlockComment})},
-autoIndent:function(){var a=this.editor;if(a.getTextView().getOptions("readonly"))return!1;var c=a.getSelection();if(c.start===c.end){for(var b=a.getModel(),d=b.getLineAtOffset(c.start),f=b.getLine(d,!0),e=b.getLineStart(d),d=0,e=c.start-e,h;d<e&&((h=f.charCodeAt(d))===32||h===9);)d++;if(d>0){for(var j=f.substring(0,d),d=e;d<f.length&&((h=f.charCodeAt(d++))===32||h===9);)c.end++;a.setText(b.getLineDelimiter()+j,c.start,c.end);return!0}}return!1},addBlockComment:function(){var a=this.editor;if(a.getTextView().getOptions("readonly"))return!1;
-var c=a.getModel(),b=a.getSelection(),d=this._findEnclosingComment(c,b.start,b.end);if(d.commentStart!==void 0&&d.commentEnd!==void 0)return!0;c=c.getText(b.start,b.end);if(c.length===0)return!0;var d=c.length,c=c.replace(/\/\*|\*\//g,""),f=c.length;a.setText("/*"+c+"*/",b.start,b.end);a.setSelection(b.start+2,b.end+2+(f-d));return!0},contentAssistProposalApplied:function(a){a=a.data.proposal;if(a.positions&&a.positions.length>0&&this.linkedMode){for(var c=[],b=0;b<a.positions.length;++b)c[b]={positions:[{offset:a.positions[b].offset,
-length:a.positions[b].length}]};this.linkedMode.enterLinkedMode({groups:c,escapePosition:a.escapePosition})}else a.groups&&a.groups.length>0&&this.linkedMode?this.linkedMode.enterLinkedMode({groups:a.groups,escapePosition:a.escapePosition}):a.escapePosition&&this.editor.getTextView().setCaretOffset(a.escapePosition);return!0},_findEnclosingComment:function(a,c,b){var d=a.getLineAtOffset(c),f=a.getLineAtOffset(b),e,h,j,k,o,g;for(e=d;e>=0;e--)if(h=a.getLine(e),j=e===d?c-a.getLineStart(d):h.length,k=
-h.lastIndexOf("/*",j),h=h.lastIndexOf("*/",j),h>k)break;else if(k!==-1){o=a.getLineStart(e)+k;break}for(e=f;e<a.getLineCount();e++)if(h=a.getLine(e),j=e===f?b-a.getLineStart(f):0,k=h.indexOf("/*",j),h=h.indexOf("*/",j),k!==-1&&k<h)break;else if(h!==-1){g=a.getLineStart(e)+h;break}return{commentStart:o,commentEnd:g}},lineStart:function(){for(var a=this.editor,c=a.getModel(),b=a.getCaretOffset(),d=c.getLineAtOffset(b),f=c.getLineStart(d),c=c.getLine(d),d=0;d<c.length;d++){var e=c.charCodeAt(d);if(!(e===
-32||e===9))break}d+=f;return b!==d?(a.setSelection(d,d),!0):!1},removeBlockComment:function(){var a=this.editor;if(a.getTextView().getOptions("readonly"))return!1;var c=a.getModel(),b=a.getSelection(),d=c.getText(b.start,b.end),f,e,h;for(h=0;h<d.length;h++)if(d.substring(h,h+2)==="/*"){f=b.start+h;break}for(;h<d.length;h++)if(d.substring(h,h+2)==="*/"){e=b.start+h;break}if(f!==void 0&&e!==void 0)a.setText(c.getText(f+2,e),f,e+2),a.setSelection(f,e);else{d=this._findEnclosingComment(c,b.start,b.end);
-if(d.commentStart===void 0||d.commentEnd===void 0)return!0;c=c.getText(d.commentStart+2,d.commentEnd);a.setText(c,d.commentStart,d.commentEnd+2);a.setSelection(b.start-2,b.end-2)}return!0},toggleLineComment:function(){var a=this.editor;if(a.getTextView().getOptions("readonly"))return!1;for(var c=a.getModel(),b=a.getSelection(),d=c.getLineAtOffset(b.start),f=c.getLineAtOffset(b.end>b.start?b.end-1:b.end),e=!0,h=[],j,k,o=d;o<=f;o++)if(j=c.getLine(o,!0),h.push(j),!e||(k=j.indexOf("//"))===-1)e=!1;else if(k!==
-0){var g;for(g=0;g<k;g++)if(e=j.charCodeAt(g),!(e===32||e===9))break;e=g===k}o=c.getLineStart(d);g=c.getLineEnd(f,!0);if(e){for(e=0;e<h.length;e++)j=h[e],k=j.indexOf("//"),h[e]=j.substring(0,k)+j.substring(k+2);h=h.join("");j=c.getLineStart(f);c=o===b.start?b.start:b.start-2;b=b.end-2*(f-d+1)+(b.end===j+1?2:0)}else h.splice(0,0,""),h=h.join("//"),c=o===b.start?b.start:b.start+2,b=b.end+2*(f-d+1);a.setText(h,o,g);a.setSelection(c,b);return!0},startUndo:function(){this.undoStack&&this.undoStack.startCompoundChange()},
-endUndo:function(){this.undoStack&&this.undoStack.endCompoundChange()}};f.SourceCodeActions=j;return f});
-define("orion/editor/templates",[],function(){function k(k,e,b){this.prefix=k;this.description=e;this.template=b;this._parse()}function p(k,e){this._keywords=k||[];this._templates=[];this.addTemplates(e||[])}k.prototype={getProposal:function(k,e,b){var k=e-k.length,e={},d,h=b.delimiter!==void 0?b.delimiter:"\n";b.indentation&&(h+=b.indentation);for(var j=b.tab!==void 0?b.tab:"\t",f=0,a=this.variables,c=this.segments,b=[],i=0;i<c.length;i++){var m=c[i],n=a[m];if(n!==void 0)switch(m){case "${tab}":m=
-j;break;case "${delimiter}":m=h;break;case "${cursor}":m="";d=f;break;default:var l=e[m];l||(l=e[m]={data:n.data,positions:[]});m=n.substitution;l.data&&l.data.values&&(m=l.data.values[0]);l.positions.push({offset:k+f,length:m.length})}b.push(m);f+=m.length}var h=[],r;for(r in e)e.hasOwnProperty(r)&&h.push(e[r]);b=b.join("");if(d===void 0)d=b.length;return{proposal:b,description:this.description,groups:h,escapePosition:k+d}},match:function(k){return this.prefix.indexOf(k)===0},_parse:function(){var k=
-this.template,e=[],b={},d,h=0,k=k.replace(/\n/g,"${delimiter}"),k=k.replace(/\t/g,"${tab}");k.replace(/\$\{((?:[^\\}]+|\\.))*\}/g,function(j,f,a){var c=j.substring(2,j.length-1),f=j,i=c,m=null,n=i.indexOf(":");n!==-1&&(i=i.substring(0,n),f="${"+i+"}",m=JSON.parse(c.substring(n+1).replace("\\}","}").trim()));(c=b[f])||(c=b[f]={});c.substitution=i;if(m)c.data=m;(d=k.substring(h,a))&&e.push(d);e.push(f);h=a+j.length;return i});(d=k.substring(h,k.length))&&e.push(d);this.segments=e;this.variables=b}};
-p.prototype={addTemplates:function(o){for(var e=this.getTemplates(),b=0;b<o.length;b++)e.push(new k(o[b].prefix,o[b].description,o[b].template))},computeProposals:function(k,e,b){var d=this.getPrefix(k,e,b),h=[];if(!this.isValid(d,k,e,b))return h;h=h.concat(this.getTemplateProposals(d,e,b));return h=h.concat(this.getKeywordProposals(d))},getKeywords:function(){return this._keywords},getKeywordProposals:function(k){var e=[],b=this.getKeywords();if(b)for(var d=0;d<b.length;d++)b[d].indexOf(k)===0&&
-e.push({proposal:b[d].substring(k.length),description:b[d]});return e},getPrefix:function(k,e,b){return b.prefix},getTemplates:function(){return this._templates},getTemplateProposals:function(k,e,b){for(var d=[],h=this.getTemplates(),j=0;j<h.length;j++){var f=h[j];f.match(k)&&(f=f.getProposal(k,e,b),this.removePrefix(k,f),d.push(f))}return d},removePrefix:function(k,e){if(!(e.overwrite=e.proposal.substring(0,k.length)!==k))e.proposal=e.proposal.substring(k.length)},isValid:function(){return!0}};return{Template:k,
-TemplateContentAssist:p}});
-define("orion/editor/linkedMode","i18n!orion/editor/nls/messages,orion/keyBinding,orion/editor/keyModes,orion/editor/annotations,orion/editor/templates,orion/objects,orion/util".split(","),function(k,p,o,e,b,d){function h(b,a,c){var d=b.getTextView();o.KeyMode.call(this,d);this.editor=b;this.undoStack=a;this.contentAssist=c;this.linkedModeModel=null;d.setAction("linkedModeEnter",function(){this.exitLinkedMode(!0);return!0}.bind(this));d.setAction("linkedModeCancel",function(){this.exitLinkedMode(!1);return!0}.bind(this));
-d.setAction("linkedModeNextGroup",function(){var a=this.linkedModeModel;this.selectLinkedGroup((a.selectedGroupIndex+1)%a.groups.length);return!0}.bind(this));d.setAction("linkedModePreviousGroup",function(){var a=this.linkedModeModel;this.selectLinkedGroup(a.selectedGroupIndex>0?a.selectedGroupIndex-1:a.groups.length-1);return!0}.bind(this));this.linkedModeListener={onActivating:function(){this._groupContentAssistProvider&&(this.contentAssist.setProviders([this._groupContentAssistProvider]),this.contentAssist.setProgress(null))}.bind(this),
-onModelChanged:function(a){if(!this.ignoreVerify){for(var b=this.linkedModeModel,c,d;b;)if(c=this._getPositionChanged(b,a.start,a.start+a.removedCharCount),d=c.position,d===void 0||d.model!==b)this.exitLinkedMode(!1),b=this.linkedModeModel;else break;if(b){b=0;d=a.addedCharCount-a.removedCharCount;c=c.positions;for(var f,e,h=0;h<c.length;++h){e=c[h];f=e.position;var g=f.offset<=a.start&&a.start<=f.offset+f.length;g&&!e.ansestor?(f.offset+=b,f.length+=d,b+=d):(f.offset+=b,e.ansestor&&g&&(f.length+=
-d));if(e.escape)e.model.escapePosition=f.offset}this._updateAnnotations(c)}}}.bind(this),onVerify:function(a){if(!this.ignoreVerify){for(var b=this.linkedModeModel,c,d;b;)if(c=this._getPositionChanged(b,a.start,a.end),d=c.position,d===void 0||d.model!==b)this.exitLinkedMode(!1),b=this.linkedModeModel;else break;if(b){var f=this._compoundChange;f?f.owner.model===b&&f.owner.group===d.group||(this.endUndo(),this.startUndo()):this.startUndo();b.selectedGroupIndex=d.group;var f=0,e=a.text.length-(a.end-
-a.start);c=c.positions;for(var h,g,i=a.start-d.position.offset,j=a.end-d.position.offset,k=0;k<c.length;++k)if(g=c[k],h=g.position,g.oldOffset=h.offset,g.model===b&&g.group===d.group?(h.offset+=f,h.length+=e,f+=e):(h.offset+=f,g.ansestor&&(h.length+=d.count*e)),g.escape)g.model.escapePosition=h.offset;this.ignoreVerify=!0;f=this.editor.getTextView();for(k=c.length-1;k>=0;k--)g=c[k],g.model===b&&g.group===d.group&&f.setText(a.text,g.oldOffset+i,g.oldOffset+j);this.ignoreVerify=!1;a.text=null;this._updateAnnotations(c)}}}.bind(this)}}
-var j={};h.prototype=new o.KeyMode;d.mixin(h.prototype,{createKeyBindings:function(){var b=p.KeyBinding,a=[];a.push({actionID:"linkedModeEnter",keyBinding:new b(13)});a.push({actionID:"linkedModeCancel",keyBinding:new b(27)});a.push({actionID:"linkedModeNextGroup",keyBinding:new b(9)});a.push({actionID:"linkedModePreviousGroup",keyBinding:new b(9,!1,!0)});return a},enterLinkedMode:function(b){if(!this.linkedModeModel){var a=this.editor.getTextView();a.addKeyMode(this);a.addEventListener("Verify",
-this.linkedModeListener.onVerify);a.addEventListener("ModelChanged",this.linkedModeListener.onModelChanged);this.contentAssist.addEventListener("Activating",this.linkedModeListener.onActivating);this.editor.reportStatus(k.linkedModeEntered,null,!0)}this._sortedPositions=null;if(this.linkedModeModel)b.previousModel=this.linkedModeModel,b.parentGroup=this.linkedModeModel.selectedGroupIndex,this.linkedModeModel.nextModel=b;this.linkedModeModel=b;this.selectLinkedGroup(0)},exitLinkedMode:function(b){if(this.isActive()){if(this._compoundChange)this.endUndo(),
-this._compoundChange=null;this._sortedPositions=null;var a=this.linkedModeModel;this.linkedModeModel=a.previousModel;a.parentGroup=a.previousModel=void 0;if(this.linkedModeModel)this.linkedModeModel.nextModel=void 0;if(!this.linkedModeModel){var c=this.editor.getTextView();c.removeKeyMode(this);c.removeEventListener("Verify",this.linkedModeListener.onVerify);c.removeEventListener("ModelChanged",this.linkedModeListener.onModelChanged);var d=this.contentAssist;d.removeEventListener("Activating",this.linkedModeListener.onActivating);
-d.offset=void 0;this.editor.reportStatus(k.linkedModeExited,null,!0);b&&c.setCaretOffset(a.escapePosition,!1)}this.selectLinkedGroup(0)}},startUndo:function(){if(this.undoStack){var b=this,a=this.linkedModeModel;this._compoundChange=this.undoStack.startCompoundChange({model:a,group:a.selectedGroupIndex,end:function(){b._compoundChange=null}})}},endUndo:function(){this.undoStack&&this.undoStack.endCompoundChange()},isActive:function(){return!!this.linkedModeModel},isStatusActive:function(){return!!this.linkedModeModel},
-selectLinkedGroup:function(d){var a=this.linkedModeModel;if(a){a.selectedGroupIndex=d;var d=a.groups[d],c=d.positions[0],e=this.editor.getTextView();e.setSelection(c.offset,c.offset+c.length);if(a=this.contentAssist)if(a.offset=void 0,d.data&&d.data.type==="link"&&d.data.values)(this._groupContentAssistProvider=new b.TemplateContentAssist(d.data.values)).getPrefix=function(){var a=e.getSelection();return a.start===a.end&&(a=e.getCaretOffset(),c.offset<=a&&a<=c.offset+c.length)?e.getText(c.offset,
-a):""},a.offset=c.offset,a.deactivate(),a.activate();else if(this._groupContentAssistProvider)this._groupContentAssistProvider=null,a.deactivate()}this._updateAnnotations()},_getModelPositions:function(b,a,c){for(var d=a.groups,e=0;e<d.length;e++)for(var h=d[e].positions,l=0;l<h.length;l++){var j=h[l];c&&(j={offset:j.offset+c,length:j.length});j={index:l,group:e,count:h.length,model:a,position:j};b.push(j);if(a.nextModel&&a.nextModel.parentGroup===e)j.ansestor=!0,this._getModelPositions(b,a.nextModel,
-(c||0)+h[l].offset-h[0].offset)}},_getSortedPositions:function(b){var a=this._sortedPositions;if(!a){for(a=[];b.previousModel;)b=b.previousModel;for(this._getModelPositions(a,b);b;)b.escapePosition!==void 0&&a.push({escape:!0,model:b,position:{offset:b.escapePosition,length:0}}),b=b.nextModel;a.sort(function(a,b){return a.position.offset-b.position.offset});this._sortedPositions=a}return a},_getPositionChanged:function(b,a,c){for(var d,b=this._getSortedPositions(b),e=b.length-1;e>=0;e--){var h=b[e].position;
-if(h.offset<=a&&c<=h.offset+h.length){d=b[e];break}}return{position:d,positions:b}},_updateAnnotations:function(b){var a=this.editor.getAnnotationModel();if(a){for(var c=[],d=[],h=a.getTextModel(),h=a.getAnnotations(0,h.getCharCount()),j;h.hasNext();)switch(j=h.next(),j.type){case e.AnnotationType.ANNOTATION_LINKED_GROUP:case e.AnnotationType.ANNOTATION_CURRENT_LINKED_GROUP:case e.AnnotationType.ANNOTATION_SELECTED_LINKED_GROUP:c.push(j)}if(h=this.linkedModeModel)for(var b=b||this._getSortedPositions(h),
-l=0;l<b.length;l++)if(j=b[l],j.model===h){var k=e.AnnotationType.ANNOTATION_LINKED_GROUP;j.group===h.selectedGroupIndex&&(k=j.index===0?e.AnnotationType.ANNOTATION_SELECTED_LINKED_GROUP:e.AnnotationType.ANNOTATION_CURRENT_LINKED_GROUP);j=j.position;j=e.AnnotationType.createAnnotation(k,j.offset,j.offset+j.length,"");d.push(j)}a.replaceAnnotations(c,d)}}});j.LinkedMode=h;return j});
-define("orion/editor/factories","orion/editor/actions,orion/editor/undoStack,orion/editor/rulers,orion/editor/annotations,orion/editor/textDND,orion/editor/linkedMode".split(","),function(k,p,o,e,b,d){function h(){}function j(){}function f(){}function a(){}function c(){}function i(){}var m={};h.prototype={createKeyBindings:function(a,b,c,f){var f=new k.TextActions(a,b,f),e=new d.LinkedMode(a,b,c),a=new k.SourceCodeActions(a,b,c,e);return{textActions:f,linkedMode:e,sourceCodeActions:a}}};m.KeyBindingsFactory=
-h;j.prototype={createUndoStack:function(a){a=a.getTextView();return new p.UndoStack(a,200)}};m.UndoFactory=j;f.prototype={createLineNumberRuler:function(a){return new o.LineNumberRuler(a,"left",{styleClass:"ruler lines"},{styleClass:"rulerLines odd"},{styleClass:"rulerLines even"})}};m.LineNumberRulerFactory=f;a.prototype={createFoldingRuler:function(a){return new o.FoldingRuler(a,"left",{styleClass:"ruler folding"})}};m.FoldingRulerFactory=a;c.prototype={createAnnotationModel:function(a){return new e.AnnotationModel(a)},
-createAnnotationStyler:function(a,b){return new e.AnnotationStyler(a,b)},createAnnotationRulers:function(a){var b=new o.AnnotationRuler(a,"left",{styleClass:"ruler annotations"}),a=new o.OverviewRuler(a,"right",{styleClass:"ruler overview"});return{annotationRuler:b,overviewRuler:a}}};m.AnnotationFactory=c;i.prototype={createTextDND:function(a,c){return new b.TextDND(a.getTextView(),c)}};m.TextDNDFactory=i;return m});
-define("orion/editor/editorFeatures",["orion/editor/factories","orion/editor/actions","orion/editor/linkedMode"],function(k,p,o){var e={};e.KeyBindingsFactory=k.KeyBindingsFactory;e.UndoFactory=k.UndoFactory;e.LineNumberRulerFactory=k.LineNumberRulerFactory;e.FoldingRulerFactory=k.FoldingRulerFactory;e.AnnotationFactory=k.AnnotationFactory;e.TextDNDFactory=k.TextDNDFactory;e.TextActions=p.TextActions;e.SourceCodeActions=p.SourceCodeActions;e.LinkedMode=o.LinkedMode;return e});
-(function(k,p){typeof define==="function"&&define.amd?define("orion/Deferred",p):typeof exports==="object"?module.exports=p():(k.orion=k.orion||{},k.orion.Deferred=p())})(this,function(){function k(){for(var a;a=h.shift()||j.shift();)a();f=!1}function p(a,b){(b?j:h).push(a);f||(f=!0,b?setTimeout(k,0):k())}function o(a){return function(){a.apply(null,arguments)}}function e(){}function b(){var a=Error("Cancel");a.name="Cancel";return a}function d(){function a(){for(var a;a=h.shift();){var b=a.deferred,
-d=f==="resolved"?"resolve":"reject";if(typeof a[d]==="function")try{var j=a[d](c);j&&typeof j.then==="function"?(b.cancel=j.cancel||e,j.then(o(b.resolve),o(b.reject),b.progress)):b.resolve(j)}catch(n){b.reject(n)}else b[d](c)}}var c,f,h=[],j=this;this.reject=function(b){f||(f="rejected",c=b,h.length&&p(a));return j.promise};this.resolve=function(b){f||(f="resolved",c=b,h.length&&p(a));return j.promise};this.progress=function(a){f||h.forEach(function(b){b.progress&&b.progress(a)});return j.promise};
-this.cancel=function(){f||j.reject(b())};this.then=function(b,c,e){var b={resolve:b,reject:c,progress:e,deferred:new d},k=b.deferred,o=!1,g=function(){o||(o=!0,p(function(){var a=k.cancel===g?j.cancel:k.cancel;typeof a==="function"&&a()},!0))};k.cancel=g;h.push(b);f&&p(a,!0);return k.promise};this.promise=Object.create(Object.prototype,{then:{value:j.then},cancel:{get:function(){return j.cancel},set:function(a){j.cancel=a}}})}var h=[],j=[],f=!1;d.all=function(a,b){function f(a,b){j||(l[a]=b,--h===
-0&&k.resolve(l))}function e(a,d){if(!j){if(b)try{f(a,b(d));return}catch(g){d=g}k.reject(d)}}var h=a.length,l=[],j=!1,k=new d;k.then(null,function(){j=!0;a.forEach(function(a){a.cancel&&a.cancel()})});h===0?k.resolve(l):a.forEach(function(a,b){a.then(f.bind(null,b),e.bind(null,b))});return k.promise};d.when=function(a,b,f,e){var h;if(!(a&&typeof a.then==="function"))h=new d,h.resolve(a),a=h.promise;return a.then(b,f,e)};return d});
-define("orion/editor/contentAssist","i18n!orion/editor/nls/messages,orion/keyBinding,orion/editor/keyModes,orion/editor/eventTarget,orion/Deferred,orion/objects,orion/util".split(","),function(k,p,o,e,b,d,h){var j,f,a;function c(b){this.textView=b;this.state=j;this.providers=[];var c=this;this.contentAssistListener={onModelChanging:function(b){c.isDeactivatingChange(b)?c.setState(j):c.state===f&&c.setState(a)},onScroll:function(){c.setState(j)},onSelection:function(){var b=c.state;if(b===f||b===a)c.computeProposals(),
-c.setState(a)}};b.setKeyBinding(h.isMac?new p.KeyBinding(" ",!1,!1,!1,!0):new p.KeyBinding(" ",!0),"contentAssist");b.setAction("contentAssist",function(){c.activate();return!0},{name:k.contentAssist})}function i(a,b){var c=a.textView;o.KeyMode.call(this,c);this.contentAssist=a;this.widget=b;this.proposals=[];var d=this;this.contentAssist.addEventListener("ProposalsComputed",function(a){d.proposals=a.data.proposals;d.selectedIndex=d.proposals.length?0:-1});c.setAction("contentAssistApply",function(){return this.enter()}.bind(this));
-c.setAction("contentAssistCancel",function(){return this.cancel()}.bind(this));c.setAction("contentAssistNextProposal",function(){return this.lineDown()}.bind(this));c.setAction("contentAssistPreviousProposal",function(){return this.lineUp()}.bind(this));c.setAction("contentAssistTab",function(){return this.tab()}.bind(this))}function m(a,b){this.contentAssist=a;this.textView=this.contentAssist.getTextView();this.isShowing=this.textViewListenerAdded=!1;var c=this.textView.getOptions("parent").ownerDocument;
-this.parentNode=typeof b==="string"?c.getElementById(b):b;if(!this.parentNode){this.parentNode=h.createElement(c,"div");this.parentNode.className="contentassist";var d=c.getElementsByTagName("body")[0];if(d)d.appendChild(this.parentNode);else throw Error("parentNode is required");}var f=this;this.textViewListener={onMouseDown:function(a){a.event.target.parentElement!==f.parentNode&&f.contentAssist.deactivate()}};this.contentAssist.addEventListener("ProposalsComputed",function(a){f.setProposals(a.data.proposals);
-f.show();if(!f.textViewListenerAdded)f.textView.addEventListener("MouseDown",f.textViewListener.onMouseDown),f.textViewListenerAdded=!0});this.contentAssist.addEventListener("Deactivating",function(){f.setProposals([]);f.hide();if(f.textViewListenerAdded)f.textView.removeEventListener("MouseDown",f.textViewListener.onMouseDown),f.textViewListenerAdded=!1;f.textViewListenerAdded=!1});this.scrollListener=function(){f.isShowing&&f.position()};c.addEventListener&&c.addEventListener("scroll",this.scrollListener)}
-j=1;f=2;a=3;var n={selected:" selected",hr:"proposal-hr",emphasis:"proposal-emphasis",noemphasis:"proposal-noemphasis",dfault:"proposal-default"};c.prototype={apply:function(a){if(!a)return!1;var b=this.textView.getSelection(),c=Math.min(b.start,b.end),b=Math.max(b.start,b.end);a.overwrite&&(c=this.getPrefixStart(c));var d={proposal:a,start:c,end:b};this.setState(j);this.textView.setText(typeof a==="string"?a:a.proposal,c,b);this.dispatchEvent({type:"ProposalApplied",data:d});return!0},activate:function(){this.state===
-j&&this.setState(f)},deactivate:function(){this.setState(j)},getTextView:function(){return this.textView},isActive:function(){return this.state===f||this.state===a},isDeactivatingChange:function(a){var b=a.removedCharCount>0&&a.addedCharCount===0,c=this.textView,c=a.start+1<=c.getModel().getCharCount()&&/^\s*$/.test(c.getText(a.start,a.start+1));return a.removedLineCount>0||a.addedLineCount>0||b&&c},setState:function(a){var b;a===f?(b="Activating",this._mode&&this._mode.setActive(!0)):a===j&&(b="Deactivating",
-this._mode&&this._mode.setActive(!1));b&&this.dispatchEvent({type:b});this.state=a;this.onStateChange(a)},setMode:function(a){this._mode=a},onStateChange:function(a){if(a===j){if(this.listenerAdded)this.textView.removeEventListener("ModelChanging",this.contentAssistListener.onModelChanging),this.textView.removeEventListener("Scroll",this.contentAssistListener.onScroll),this.textView.removeEventListener("Selection",this.contentAssistListener.onSelection),this.listenerAdded=!1}else if(a===f){if(!this.listenerAdded)this.textView.addEventListener("ModelChanging",
-this.contentAssistListener.onModelChanging),this.textView.addEventListener("Scroll",this.contentAssistListener.onScroll),this.textView.addEventListener("Selection",this.contentAssistListener.onSelection),this.listenerAdded=!0;this.computeProposals()}},computeProposals:function(){var a=this;this._computeProposals(this.textView.getCaretOffset()).then(function(b){a.isActive()&&a.dispatchEvent({type:"ProposalsComputed",data:{proposals:b}})})},getPrefixStart:function(a){for(;a>0&&/[A-Za-z0-9_]/.test(this.textView.getText(a-
-1,a));)a--;return a},handleError:function(a){typeof console!=="undefined"&&(console.log("Error retrieving content assist proposals"),console.log(a))},_computeProposals:function(a){for(var c=this.providers,d=this.textView,f=d.getModel(),e=d.getText(),g=f.getLine(f.getLineAtOffset(a)),h=0;h<g.length&&/\s/.test(g.charAt(h));)h++;var h=g.substring(0,h),i=d.getOptions("tabSize","expandTab"),i=i.expandTab?Array(i.tabSize+1).join(" "):"\t",j={line:g,prefix:d.getText(this.getPrefixStart(a),a),selection:d.getSelection(),
-delimiter:f.getLineDelimiter(),tab:i,indentation:h},m=this,c=c.map(function(c){var d=c.computeProposals||c.getProposals,f;try{typeof d==="function"&&(f=m.progress?m.progress.progress(d.apply(c,[e,a,j]),"Generating content assist proposal"):d.apply(c,[e,a,j]))}catch(g){m.handleError(g)}return b.when(f)});return b.all(c,this.handleError).then(function(a){return a.reduce(function(a,b){return b instanceof Array?a.concat(b):a},[])})},setProviders:function(a){this.providers=a.slice(0)},setProgress:function(a){this.progress=
-a}};e.EventTarget.addMixin(c.prototype);i.prototype=new o.KeyMode;d.mixin(i.prototype,{createKeyBindings:function(){var a=p.KeyBinding,b=[];b.push({actionID:"contentAssistApply",keyBinding:new a(13)});b.push({actionID:"contentAssistCancel",keyBinding:new a(27)});b.push({actionID:"contentAssistNextProposal",keyBinding:new a(40)});b.push({actionID:"contentAssistPreviousProposal",keyBinding:new a(38)});b.push({actionID:"contentAssistTab",keyBinding:new a(9)});return b},cancel:function(){this.getContentAssist().deactivate()},
-getContentAssist:function(){return this.contentAssist},isActive:function(){return this.getContentAssist().isActive()},setActive:function(a){a?this.contentAssist.textView.addKeyMode(this):this.contentAssist.textView.removeKeyMode(this)},lineUp:function(){for(var a=this.selectedIndex===0?this.proposals.length-1:this.selectedIndex-1;this.proposals[a].unselectable&&a>0;)a--;this.selectedIndex=a;this.widget&&this.widget.setSelectedIndex(this.selectedIndex);return!0},lineDown:function(){for(var a=this.selectedIndex===
-this.proposals.length-1?0:this.selectedIndex+1;this.proposals[a].unselectable&&a<this.proposals.length-1;)a++;this.selectedIndex=a;this.widget&&this.widget.setSelectedIndex(this.selectedIndex);return!0},enter:function(){return this.contentAssist.apply(this.proposals[this.selectedIndex]||null)},tab:function(){return this.widget?(this.widget.createAccessible(this),this.widget.parentNode.focus(),!0):!1}});m.prototype={onClick:function(a){this.contentAssist.apply(this.getProposal(a.target));this.textView.focus()},
-createDiv:function(a,b,c,d){var f=c.ownerDocument,e=h.createElement(f,"div");e.id="contentoption"+d;e.setAttribute("role","option");a.style==="hr"?a=h.createElement(f,"hr"):(e.className=this.calculateClasses(a.style,b),a=f.createTextNode(this.getDisplayString(a)),b&&this.parentNode.setAttribute("aria-activedescendant",e.id));e.appendChild(a,e);c.appendChild(e)},createAccessible:function(a){this._isAccessible||this.parentNode.addEventListener("keydown",function(b){b.preventDefault();if(b.keyCode===
-27)return a.cancel();else if(b.keyCode===38)return a.lineUp();else if(b.keyCode===40)return a.lineDown();else if(b.keyCode===13)return a.enter();return!1});this._isAccessible=!0},calculateClasses:function(a,b){var c=n[a];if(!c)c=n.dfault;return b?c+n.selected:c},getDisplayString:function(a){return typeof a==="string"?a:a.description&&typeof a.description==="string"?a.description:a.proposal},getProposal:function(a){for(var b=0,c=this.parentNode.firstChild;c!==null;c=c.nextSibling){if(c===a)return this.proposals[b]||
-null;b++}return null},setSelectedIndex:function(a){this.selectedIndex=a;this.selectNode(this.parentNode.childNodes[this.selectedIndex])},selectNode:function(a){for(var b=this.parentNode.childNodes,c=0;c<b.length;c++){var d=b[c],f=d.className.indexOf(n.selected);if(f>=0)d.className=d.className.substring(0,f)+d.className.substring(f+n.selected.length);d===a&&(d.className+=n.selected,this.parentNode.setAttribute("aria-activedescendant",d.id),d.focus(),d.offsetTop<this.parentNode.scrollTop?d.scrollIntoView(!0):
-d.offsetTop+d.offsetHeight>this.parentNode.scrollTop+this.parentNode.clientHeight&&d.scrollIntoView(!1))}},setProposals:function(a){this.proposals=a},show:function(){if(this.proposals.length===0)this.hide();else{this.parentNode.innerHTML="";for(var a=0;a<this.proposals.length;a++)this.createDiv(this.proposals[a],a===0,this.parentNode,a);this.position();this.parentNode.onclick=this.onClick.bind(this);this.isShowing=!0}},hide:function(){this.parentNode.ownerDocument.activeElement===this.parentNode&&
-this.textView.focus();this.parentNode.style.display="none";this.parentNode.onclick=null;this.isShowing=!1},position:function(){var a=this.contentAssist,a=this.textView.getLocationAtOffset(a.offset!==void 0?a.offset:this.textView.getCaretOffset());a.y+=this.textView.getLineHeight();this.textView.convert(a,"document","page");this.parentNode.style.position="fixed";this.parentNode.style.left=a.x+"px";this.parentNode.style.top=a.y+"px";this.parentNode.style.display="block";this.parentNode.scrollTop=0;
-var b=this.parentNode.ownerDocument,c=b.documentElement.clientWidth;if(a.y+this.parentNode.offsetHeight>b.documentElement.clientHeight)this.parentNode.style.top=a.y-this.parentNode.offsetHeight-this.textView.getLineHeight()+"px";if(a.x+this.parentNode.offsetWidth>c)this.parentNode.style.left=c-this.parentNode.offsetWidth+"px"}};return{ContentAssist:c,ContentAssistMode:i,ContentAssistWidget:m}});
-define("orion/editor/keywords",[],function(){return{JSKeywords:"break,case,class,catch,continue,const,debugger,default,delete,do,else,enum,export,extends,false,finally,for,function,if,implements,import,in,instanceof,interface,let,new,null,package,private,protected,public,return,static,super,switch,this,throw,true,try,typeof,undefined,var,void,while,with,yield".split(","),CSSKeywords:"alignment-adjust,alignment-baseline,animation,animation-delay,animation-direction,animation-duration,animation-iteration-count,animation-name,animation-play-state,animation-timing-function,appearance,azimuth,backface-visibility,background,background-attachment,background-clip,background-color,background-image,background-origin,background-position,background-repeat,background-size,baseline-shift,binding,bleed,bookmark-label,bookmark-level,bookmark-state,bookmark-target,border,border-bottom,border-bottom-color,border-bottom-left-radius,border-bottom-right-radius,border-bottom-style,border-bottom-width,border-collapse,border-color,border-image,border-image-outset,border-image-repeat,border-image-slice,border-image-source,border-image-width,border-left,border-left-color,border-left-style,border-left-width,border-radius,border-right,border-right-color,border-right-style,border-right-width,border-spacing,border-style,border-top,border-top-color,border-top-left-radius,border-top-right-radius,border-top-style,border-top-width,border-width,bottom,box-align,box-decoration-break,box-direction,box-flex,box-flex-group,box-lines,box-ordinal-group,box-orient,box-pack,box-shadow,box-sizing,break-after,break-before,break-inside,caption-side,clear,clip,color,color-profile,column-count,column-fill,column-gap,column-rule,column-rule-color,column-rule-style,column-rule-width,column-span,column-width,columns,content,counter-increment,counter-reset,crop,cue,cue-after,cue-before,cursor,direction,display,dominant-baseline,drop-initial-after-adjust,drop-initial-after-align,drop-initial-before-adjust,drop-initial-before-align,drop-initial-size,drop-initial-value,elevation,empty-cells,fit,fit-position,flex-align,flex-flow,flex-inline-pack,flex-order,flex-pack,float,float-offset,font,font-family,font-size,font-size-adjust,font-stretch,font-style,font-variant,font-weight,grid-columns,grid-rows,hanging-punctuation,height,hyphenate-after,hyphenate-before,hyphenate-character,hyphenate-lines,hyphenate-resource,hyphens,icon,image-orientation,image-rendering,image-resolution,inline-box-align,left,letter-spacing,line-height,line-stacking,line-stacking-ruby,line-stacking-shift,line-stacking-strategy,list-style,list-style-image,list-style-position,list-style-type,margin,margin-bottom,margin-left,margin-right,margin-top,mark,mark-after,mark-before,marker-offset,marks,marquee-direction,marquee-loop,marquee-play-count,marquee-speed,marquee-style,max-height,max-width,min-height,min-width,move-to,nav-down,nav-index,nav-left,nav-right,nav-up,opacity,orphans,outline,outline-color,outline-offset,outline-style,outline-width,overflow,overflow-style,overflow-x,overflow-y,padding,padding-bottom,padding-left,padding-right,padding-top,page,page-break-after,page-break-before,page-break-inside,page-policy,pause,pause-after,pause-before,perspective,perspective-origin,phonemes,pitch,pitch-range,play-during,position,presentation-level,punctuation-trim,quotes,rendering-intent,resize,rest,rest-after,rest-before,richness,right,rotation,rotation-point,ruby-align,ruby-overhang,ruby-position,ruby-span,size,speak,speak-header,speak-numeral,speak-punctuation,speech-rate,stress,string-set,table-layout,target,target-name,target-new,target-position,text-align,text-align-last,text-decoration,text-emphasis,text-height,text-indent,text-justify,text-outline,text-shadow,text-transform,text-wrap,top,transform,transform-origin,transform-style,transition,transition-delay,transition-duration,transition-property,transition-timing-function,unicode-bidi,vertical-align,visibility,voice-balance,voice-duration,voice-family,voice-pitch,voice-pitch-range,voice-rate,voice-stress,voice-volume,volume,white-space,white-space-collapse,widows,width,word-break,word-spacing,word-wrap,z-index".split(","),
-JAVAKeywords:"abstract,boolean,break,byte,case,catch,char,class,continue,default,do,double,else,extends,false,final,finally,float,for,if,implements,import,instanceof,int,interface,long,native,new,null,package,private,protected,public,return,short,static,super,switch,synchronized,this,throw,throws,transient,true,try,void,volatile,while".split(",")}});
-define("orion/editor/cssContentAssist",["orion/editor/templates","orion/editor/keywords"],function(k,p){function o(a){return JSON.stringify(a).replace("}","\\}")}function e(){}for(var b={type:"link",values:"visible,hidden,scroll,auto,no-display,no-content".split(",")},d={type:"link",values:"solid,dashed,dotted,double,groove,ridge,inset,outset".split(",")},h={type:"link",values:[]},j=0;j<10;j++)h.values.push(j.toString());for(var f={type:"link",values:"black,white,red,green,blue,magenta,yellow,cyan,grey,darkred,darkgreen,darkblue,darkmagenta,darkcyan,darkyellow,darkgray,lightgray".split(",")},
-a=[{prefix:"rule",description:"rule - class selector rule",template:".${class} {\n\t${cursor}\n}"},{prefix:"rule",description:"rule - id selector rule",template:"#${id} {\n\t${cursor}\n}"},{prefix:"outline",description:"outline - outline style",template:"outline: ${color:"+o(f)+"} ${style:"+o(d)+"} ${width:"+o(h)+"}px;"},{prefix:"background-image",description:"background-image - image style",template:'background-image: url("${uri}");'},{prefix:"url",description:"url - url image",template:'url("${uri}");'},
-{prefix:"rgb",description:"rgb - rgb color",template:"rgb(${red},${green},${blue});"},{prefix:"@",description:"import - import style sheet",template:'@import "${uri}";'}],c=[{prop:"display",values:{type:"link",values:"none,block,box,flex,inline,inline-block,inline-flex,inline-table,list-item,table,table-caption,table-cell,table-column,table-column-group,table-footer-group,table-header-group,table-row,table-row-group,inherit".split(",")}},{prop:"overflow",values:b},{prop:"overflow-x",values:b},{prop:"overflow-y",
-values:b},{prop:"float",values:{type:"link",values:["left","right","none","inherit"]}},{prop:"position",values:{type:"link",values:["absolute","fixed","relative","static","inherit"]}},{prop:"cursor",values:{type:"link",values:"auto,crosshair,default,e-resize,help,move,n-resize,ne-resize,nw-resize,pointer,progress,s-resize,se-resize,sw-resize,text,w-resize,wait,inherit".split(",")}},{prop:"color",values:f},{prop:"border-top-color",values:f},{prop:"border-bottom-color",values:f},{prop:"border-right-color",
-values:f},{prop:"border-left-color",values:f},{prop:"background-color",values:f},{prop:"font-style",values:{type:"link",values:["italic","normal","oblique","inherit"]}},{prop:"font-weight",values:{type:"link",values:"bold,normal,bolder,lighter,100,200,300,400,500,600,700,800,900,inherit".split(",")}},{prop:"white-space",values:{type:"link",values:"pre,pre-line,pre-wrap,nowrap,normal,inherit".split(",")}},{prop:"word-wrap",values:{type:"link",values:["normal","break-word"]}},{prop:"visibility",values:{type:"link",
-values:["hidden","visible","collapse","inherit"]}}],j=0;j<c.length;j++)b=c[j],a.push({prefix:b.prop,description:b.prop+" - "+b.prop+" style",template:b.prop+": ${value:"+o(b.values)+"};"});c="width,height,top,bottom,left,right,min-width,min-height,max-width,max-height,margin,padding,padding-left,padding-right,padding-top,padding-bottom,margin-left,margin-top,margin-bottom,margin-right".split(",");for(j=0;j<c.length;j++)b=c[j],a.push({prefix:b,description:b+" - "+b+" pixel style",template:b+": ${value}px;"});
-c=["padding","margin"];for(j=0;j<c.length;j++)b=c[j],a.push({prefix:b,description:b+" - "+b+" top right bottom left style",template:b+": ${top}px ${left}px ${bottom}px ${right}px;"}),a.push({prefix:b,description:b+" - "+b+" top right,left bottom style",template:b+": ${top}px ${right_left}px ${bottom}px;"}),a.push({prefix:b,description:b+" - "+b+" top,bottom right,left style",template:b+": ${top_bottom}px ${right_left}px"});c=["border","border-top","border-bottom","border-left","border-right"];for(j=
-0;j<c.length;j++)b=c[j],a.push({prefix:b,description:b+" - "+b+" style",template:b+": ${width:"+o(h)+"}px ${style:"+o(d)+"} ${color:"+o(f)+"};"});e.prototype=new k.TemplateContentAssist(p.CSSKeywords,a);e.prototype.getPrefix=function(a,b){for(var c=b;c&&/[A-Za-z\-\@]/.test(a.charAt(c-1));)c--;return c?a.substring(c,b):""};return{CssContentAssistProvider:e}});
-define("orion/editor/htmlContentAssist",["orion/editor/templates"],function(k){function p(){}var o=new k.Template("","Simple HTML document",'<!DOCTYPE html>\n<html lang="en">\n\t<head>\n\t\t<meta charset=utf-8>\n\t\t<title>${title}</title>\n\t</head>\n\t<body>\n\t\t<h1>${header}</h1>\n\t\t<p>\n\t\t\t${cursor}\n\t\t</p>\n\t</body>\n</html>'),e=[{prefix:"<img",description:"<img> - HTML image element",template:'<img src="${cursor}" alt="${Image}"/>'},{prefix:"<a",description:"<a> - HTML anchor element",
-template:'<a href="${cursor}"></a>'},{prefix:"<ul",description:"<ul> - HTML unordered list",template:"<ul>\n\t<li>${cursor}</li>\n</ul>"},{prefix:"<ol",description:"<ol> - HTML ordered list",template:"<ol>\n\t<li>${cursor}</li>\n</ol>"},{prefix:"<dl",description:"<dl> - HTML definition list",template:"<dl>\n\t<dt>${cursor}</dt>\n\t<dd></dd>\n</dl>"},{prefix:"<table",description:"<table> - basic HTML table",template:"<table>\n\t<tr>\n\t\t<td>${cursor}</td>\n\t</tr>\n</table>"}],b,d,h,j,f="abbr,b,button,canvas,cite,command,dd,del,dfn,dt,em,embed,font,h1,h2,h3,h4,h5,h6,i,ins,kbd,label,li,mark,meter,object,option,output,progress,q,rp,rt,samp,small,strong,sub,sup,td,time,title,tt,u,var".split(",");
-for(j=0;j<f.length;j++)b=f[j],h="<"+b+"></"+b+">",d="<"+b+">${cursor}</"+b+">",e.push({prefix:"<"+b,description:h,template:d});f="address,article,aside,audio,bdo,blockquote,body,caption,code,colgroup,datalist,details,div,fieldset,figure,footer,form,head,header,hgroup,iframe,legend,map,menu,nav,noframes,noscript,optgroup,p,pre,ruby,script,section,select,span,style,tbody,textarea,tfoot,th,thead,tr,video".split(",");for(j=0;j<f.length;j++)b=f[j],h="<"+b+"></"+b+">",d="<"+b+">\n\t${cursor}\n</"+b+">",
-e.push({prefix:"<"+b,description:h,template:d});f="area,base,br,col,hr,input,link,meta,param,keygen,source".split(",");for(j=0;j<f.length;j++)b=f[j],d=h="<"+b+"/>",e.push({prefix:"<"+b,description:h,template:d});p.prototype=new k.TemplateContentAssist([],e);p.prototype.getPrefix=function(a,b){for(var d=b;d&&/[A-Za-z<]/.test(a.charAt(d-1));)if(d--,a.charAt(d)==="<")break;return d?a.substring(d,b):""};p.prototype.computeProposals=function(a,b,d){return a.length===0?[o.getProposal("",b,d)]:k.TemplateContentAssist.prototype.computeProposals.call(this,
-a,b,d)};return{HTMLContentAssistProvider:p}});
-define("orion/editor/jsTemplateContentAssist",["orion/editor/templates","orion/editor/keywords"],function(k,p){function o(){}var e=[{prefix:"if",description:"if - if statement",template:"if (${condition}) {\n\t${cursor}\n}"},{prefix:"if",description:"if - if else statement",template:"if (${condition}) {\n\t${cursor}\n} else {\n\t\n}"},{prefix:"for",description:"for - iterate over array",template:"for (var ${i}=0; ${i}<${array}.length; ${i}++) {\n\t${cursor}\n}"},{prefix:"for",description:"for - iterate over array with local var",
-template:"for (var ${i}=0; ${i}<${array}.length; ${i}++) {\n\tvar ${value} = ${array}[${i}];\n\t${cursor}\n}"},{prefix:"for",description:"for..in - iterate over properties of an object",template:"for (var ${property} in ${object}) {\n\tif (${object}.hasOwnProperty(${property})) {\n\t\t${cursor}\n\t}\n}"},{prefix:"while",description:"while - while loop with condition",template:"while (${condition}) {\n\t${cursor}\n}"},{prefix:"do",description:"do - do while loop with condition",template:"do {\n\t${cursor}\n} while (${condition});"},
-{prefix:"switch",description:"switch - switch case statement",template:"switch (${expression}) {\n\tcase ${value1}:\n\t\t${cursor}\n\t\tbreak;\n\tdefault:\n}"},{prefix:"case",description:"case - case statement",template:"case ${value}:\n\t${cursor}\n\tbreak;"},{prefix:"try",description:"try - try..catch statement",template:"try {\n\t${cursor}\n} catch (${err}) {\n}"},{prefix:"try",description:"try - try..catch statement with finally block",template:"try {\n\t${cursor}\n} catch (${err}) {\n} finally {\n}"},
-{prefix:"var",description:"var - variable declaration",template:"var ${name};"},{prefix:"var",description:"var - variable declaration with value",template:"var ${name} = ${value};"},{prefix:"let",description:"let - local scope variable declaration",template:"let ${name};"},{prefix:"let",description:"let - local scope variable declaration with value",template:"let ${name} = ${value};"},{prefix:"return",description:"return - return result",template:"return ${result};"},{prefix:"typeof",description:"typeof - typeof statement",
-template:'typeof ${object} === "${type:'+JSON.stringify({type:"link",values:"undefined,object,boolean,number,string,function,xml".split(",")}).replace("}","\\}")+'}"'},{prefix:"instanceof",description:"instanceof - instanceof statement",template:"${object} instanceof ${type}"},{prefix:"with",description:"with - with statement",template:"with (${object}) {\n\t${cursor}\n}"},{prefix:"function",description:"function - function declaration",template:"function ${name} (${parameter}) {\n\t${cursor}\n}"},
-{prefix:"log",description:"log - console log",template:"console.log(${object});"}];o.prototype=new k.TemplateContentAssist(p.JSKeywords,e);o.prototype.isValid=function(b,d,e){b=e-b.length-1;for(e="";b>=0;)if(e=d[b],e==="\n"||e==="\r")break;else if(/\s/.test(e))b--;else break;return":!@#$^&*.?<>".indexOf(e)===-1};return{JSTemplateContentAssistProvider:o}});
-define("orion/editor/AsyncStyler",["i18n!orion/editor/nls/messages","orion/editor/annotations"],function(k,p){function o(d){return d.getProperty("objectClass").indexOf(b)!==-1&&d.getProperty("type")==="highlighter"}function e(b,d,f){this.initialize(b,d,f);this.lineStyles=[]}var b="orion.edit.highlighter",d=b+" service must be an event emitter";p.AnnotationType.registerType("orion.annotation.highlightError",{title:k.syntaxError,html:"<div class='annotationHTML error'></div>",rangeStyle:{styleClass:"annotationRange error"}});
-e.prototype={initialize:function(d,e,f){this.textView=d;this.serviceRegistry=e;this.annotationModel=f;this.services=[];var a=this;this.listener={onModelChanging:function(b){a.onModelChanging(b)},onModelChanged:function(b){a.onModelChanged(b)},onDestroy:function(b){a.onDestroy(b)},onLineStyle:function(b){a.onLineStyle(b)},onStyleReady:function(b){a.onStyleReady(b)},onServiceAdded:function(b){a.onServiceAdded(b.serviceReference,a.serviceRegistry.getService(b.serviceReference))},onServiceRemoved:function(b){a.onServiceRemoved(b.serviceReference,
-a.serviceRegistry.getService(b.serviceReference))}};d.addEventListener("ModelChanging",this.listener.onModelChanging);d.addEventListener("ModelChanged",this.listener.onModelChanged);d.addEventListener("Destroy",this.listener.onDestroy);d.addEventListener("LineStyle",this.listener.onLineStyle);e.addEventListener("registered",this.listener.onServiceAdded);e.addEventListener("unregistering",this.listener.onServiceRemoved);d=e.getServiceReferences(b);for(f=0;f<d.length;f++){var c=d[f];o(c)&&this.addServiceListener(e.getService(c))}},
-onDestroy:function(){this.destroy()},destroy:function(){if(this.textView)this.textView.removeEventListener("ModelChanging",this.listener.onModelChanging),this.textView.removeEventListener("ModelChanged",this.listener.onModelChanged),this.textView.removeEventListener("Destroy",this.listener.onDestroy),this.textView.removeEventListener("LineStyle",this.listener.onLineStyle),this.textView=null;if(this.services){for(var b=0;b<this.services.length;b++)this.removeServiceListener(this.services[b]);this.services=
-null}if(this.serviceRegistry)this.serviceRegistry.removeEventListener("registered",this.listener.onServiceAdded),this.serviceRegistry.removeEventListener("unregistering",this.listener.onServiceRemoved),this.serviceRegistry=null;this.lineStyles=this.listener=null},onModelChanging:function(b){this.startLine=this.textView.getModel().getLineAtOffset(b.start)},onModelChanged:function(b){var d=this.startLine;(b.addedLineCount||b.removedLineCount)&&Array.prototype.splice.apply(this.lineStyles,[d,b.removedLineCount].concat(this._getEmptyStyle(b.addedLineCount)))},
-onStyleReady:function(b){var d=b.lineStyles||b.style,b=Number.MAX_VALUE,f=-1,a=this.textView.getModel(),c;for(c in d)d.hasOwnProperty(c)&&(this.lineStyles[c]=d[c],b=Math.min(b,c),f=Math.max(f,c));b=Math.max(b,0);f=Math.min(f,a.getLineCount());if(d=this.annotationModel){for(var e=d.getAnnotations(a.getLineStart(b),a.getLineEnd(f)),m=[];e.hasNext();){var k=e.next();k.type==="orion.annotation.highlightError"&&m.push(k)}e=[];for(k=b;k<=f;k++){c=k;var l=this.lineStyles[c],l=l&&l.errors;c=a.getLineStart(c);
-if(l)for(var o=0;o<l.length;o++){var q=l[o];e.push(p.AnnotationType.createAnnotation("orion.annotation.highlightError",q.start+c,q.end+c))}}d.replaceAnnotations(m,e)}this.textView.redrawLines(b,f+1)},onLineStyle:function(b){function d(a,b){for(var f=a.length,e=[],h=0;h<f;h++){var j=a[h];e.push({start:j.start+b,end:j.end+b,style:j.style})}return e}var f=this.lineStyles[b.lineIndex];if(f)if(f.ranges)b.ranges=d(f.ranges,b.lineStart);else if(f.style)b.style=f.style},_getEmptyStyle:function(b){for(var d=
-[],f=0;f<b;f++)d.push(null);return d},setContentType:function(b){this.contentType=b;if(this.services)for(b=0;b<this.services.length;b++){var d=this.services[b];if(d.setContentType){var f=this.serviceRegistry.getService("orion.page.progress");f?f.progress(d.setContentType(this.contentType),"Styling content type: "+this.contentType.id?this.contentType.id:this.contentType):d.setContentType(this.contentType)}}},onServiceAdded:function(b,d){o(b)&&this.addServiceListener(d)},onServiceRemoved:function(b,
-d){this.services.indexOf(d)!==-1&&this.removeServiceListener(d)},addServiceListener:function(b){if(typeof b.addEventListener==="function"){if(b.addEventListener("orion.edit.highlighter.styleReady",this.listener.onStyleReady),this.services.push(b),b.setContentType&&this.contentType){var e=this.serviceRegistry.getService("orion.page.progress");e?e.progress(b.setContentType(this.contentType),"Styling content type: "+this.contentType.id?this.contentType.id:this.contentType):b.setContentType(this.contentType)}}else typeof console!==
-"undefined"&&console.log(Error(d))},removeServiceListener:function(b){typeof b.removeEventListener==="function"?(b.removeEventListener("orion.edit.highlighter.styleReady",this.listener.onStyleReady),b=this.services.indexOf(b),b!==-1&&this.services.splice(b,1)):typeof console!=="undefined"&&console.log(Error(d))}};return e});
-define("orion/editor/mirror",["i18n!orion/editor/nls/messages","orion/editor/eventTarget","orion/editor/annotations"],function(k,p,o){function e(b){this.string=b;this.tokenStart=this.pos=0}function b(){this._modes={};this.mimeModes={};this.options={};this.StringStream=e}function d(b){var a=[],c;for(c in b)Object.prototype.hasOwnProperty.call(b,c)&&a.push(c);return a}function h(b,a,c){c=c||{};this.model=b;this.codeMirror=a;this.isWhitespaceVisible=typeof c.whitespacesVisible==="undefined"?!1:c.whitespacesVisible;
-this.mode=null;this.isModeLoaded=!1;this.lines=[];this.dirtyLines=[];this.startLine=Number.MAX_VALUE;this.endLine=-1;this.timer=null;this.initialize(b)}function j(b,a,c){this.init(b,a,c)}e.prototype={eol:function(){return this.pos>=this.string.length},sol:function(){return this.pos===0},peek:function(){return this.string[this.pos]},next:function(){return this.string[this.pos++]},eat:function(b){var a=this.string[this.pos];return typeof a==="string"&&(a===b||b.test&&b.test(a)||typeof b==="function"&&
-b(a))?this.string[this.pos++]:void 0},eatWhile:function(b){for(var a=!1;this.eat(b)!==void 0;)a=!0;return a},eatSpace:function(){return this.eatWhile(/\s/)},skipToEnd:function(){this.pos=this.string.length},skipTo:function(b){b=this.string.indexOf(b,this.pos);return b!==-1?(this.pos=b,!0):!1},match:function(b,a,c){a=a===!0||typeof a==="undefined";if(typeof b==="string"){var d=c?this.string.toLowerCase():this.string,b=c?b.toLowerCase():b,c=d.indexOf(b,this.pos);if(c!==-1&&a)this.pos=c+b.length;return c!==
--1}else return(b=this.string.substring(this.pos).match(b))&&a&&typeof b[0]==="string"&&(this.pos+=b.index+b[0].length),b},backUp:function(b){this.pos-=b},column:function(){for(var b=0,a=0;a<this.tokenStart;)b+=this.string[a++]==="\t"?4:1;return b},indentation:function(){for(var b=this.string.search(/\S/),a=0,c=0;c<b;)a+=this.string[c++]==="\t"?4:1;return a},current:function(){return this.string.substring(this.tokenStart,this.pos)},advance:function(){this.tokenStart=this.pos}};b.prototype={options:{},
-setOption:function(b,a){this.options[b]=a},getOption:function(b){return this.options[b]},copyState:function(b,a){if(typeof b.copyState==="function")return b.copyState(a);var c={},d;for(d in a){var e=a[d];c[d]=e instanceof Array?e.slice():e}return c},defineMode:function(b,a){this._modes[b]=a},defineMIME:function(b,a){this.mimeModes[b]=a},getMode:function(b,a){var c={},d;typeof a==="string"&&this.mimeModes[a]&&(a=this.mimeModes[a]);typeof a==="object"&&(c=a,d=this._modes[a.name]);d=d||this._modes[a];
-if(typeof d!=="function")throw"Mode not found "+a;return d(b,c)},listModes:function(){return d(this._modes)},listMIMEs:function(){return d(this.mimeModes)},_getModeName:function(b){b=this.mimeModes[b];if(typeof b==="object")b=b.name;return b}};h.prototype={initialize:function(){var b=this;this.listener={onModelChanging:function(a){b._onModelChanging(a)},onModelChanged:function(a){b._onModelChanged(a)},onDestroy:function(a){b._onDestroy(a)}};this.model.addEventListener("Changing",this.listener.onModelChanging);
-this.model.addEventListener("Changed",this.listener.onModelChanged);this.model.addEventListener("Destroy",this.listener.onDestroy)},destroy:function(){this.model&&(this.model.removeEventListener("Changing",this.listener.onModelChanging),this.model.removeEventListener("Changed",this.listener.onModelChanged),this.model.removeEventListener("Destroy",this.listener.onDestroy));this.dirtyLines=this.lines=this.mode=this.codeMirror=this.model=null;clearTimeout(this.timer);this.timer=null},_onModelChanging:function(b){this.startLine=
-this.model.getLineAtOffset(b.start)},_onModelChanged:function(b){this._dbgEvent(b);var a=this.startLine;(b.removedLineCount||b.addedLineCount)&&Array.prototype.splice.apply(this.lines,[a+1,b.removedLineCount].concat(this._newLines(b.addedLineCount)));this.mode&&(b=Math.max(b.addedLineCount,b.removedLineCount),b=a+Math.min(b,500),this.highlight(a,b),this.highlightLater(b+1))},_onDestroy:function(){this.destroy()},setViewportIndex:function(b){this.viewportIndex=b},_dbgEvent:function(){},_dbgStyle:function(){},
-_newLines:function(b){for(var a=[],c=0;c<b;c++)a.push({style:null,eolState:null});return a},setMode:function(b,a){if(b)this.mode=this.codeMirror.getMode(this.codeMirror.options,b),this.lines=this._newLines(this.model.getLineCount()),a&&this.highlight()},highlight:function(b,a,c){if(this.mode){for(var d=this.model.getLineCount(),b=typeof b==="undefined"?0:b,a=typeof a==="undefined"?d-1:Math.min(a,d-1),d=this.mode,e=this.getState(b),h=b;h<=a;h++){var j=this.lines[h];this.highlightLine(h,j,e);j.eolState=
-this.codeMirror.copyState(d,e)}this._expandRange(b,a);if(!c)this.onHighlightDone()}},highlightLater:function(b){this.dirtyLines.push(b);var a=this;this.timer=setTimeout(function(){a._highlightJob()},50)},_highlightJob:function(){for(var b=+new Date+30,a=this.mode.compareStates,c=this.model.getLineCount();this.dirtyLines.length;){var d=this.viewportIndex,e=this.lines[d],d=e&&!e.eolState?d:this.dirtyLines.pop();if(d>=c)break;this._expandRange(d,d);for(var e=this._getResumeLineIndex(d),d=e+1,e=(e=e>=
-0&&this.lines[e].eolState)?this.codeMirror.copyState(this.mode,e):this.mode.startState(),h=0,j=d;j<c;j++){var k=this.lines[j],o=k.eolState,p=this.highlightLine(j,k,e);k.eolState=this.codeMirror.copyState(this.mode,e);p&&this._expandRange(d,j+1);var k=a&&o&&a(o,k.eolState),u=!a&&!p&&h++>3;if(k||u)break;else if(!o||p)h=0;o=j<c||this.dirtyLines.length;if(+new Date>b&&o){this.highlightLater(j+1);this.onHighlightDone();return}}}this.onHighlightDone()},onHighlightDone:function(){this.startLine!==Number.MAX_VALUE&&
-this.endLine!==-1&&this.dispatchEvent({type:"Highlight",start:this.startLine,end:this.endLine});this.startLine=Number.MAX_VALUE;this.endLine=-1},_getResumeLineIndex:function(b){for(var a=this.lines,c=b-1;c>=0;c--)if(a[c].eolState||b-c>40)return c;return-1},getState:function(b){var a=this.mode,c=this.lines,d,e;for(d=b-1;d>=0;d--)if(e=c[d],e.eolState||b-d>40)break;var h=d>=0&&c[d].eolState;if(h){h=this.codeMirror.copyState(a,h);for(d=Math.max(0,d);d<b-1;d++)e=c[d],this.highlightLine(d,e,h),e.eolState=
-this.codeMirror.copyState(a,h);return h}else return a.startState()},highlightLine:function(b,a,c){if(this.mode){var d=this.model;d.getLineStart(b)===d.getLineEnd(b)&&this.mode.blankLine&&this.mode.blankLine(c);for(var h=a.style||[],b=d.getLine(b),b=new e(b),d=!a.style,j=[],l=0;!b.eol();l++){var k=this.mode.token(b,c)||null,o=b.current();this._whitespaceStyle(k,o,b.tokenStart);k=[b.tokenStart,b.pos,k];o=h[l];j.push(k);d=d||!o||o[0]!==k[0]||o[1]!==k[1]||o[2]!==k[2];b.advance()}if(d=d||j.length!==h.length)a.style=
-j.length?j:null;return d}},_whitespaceStyle:function(b,a,c){if(!b&&this.isWhitespaceVisible&&/\s+/.test(a)){for(var b=[],d,e,h=0;h<a.length;h++){var j=a[h];j!==e&&(e&&b.push([c+d,c+h,e==="\t"?"token_tab":"token_space"]),d=h,e=j)}b.push([c+d,c+h,e==="\t"?"token_tab":"token_space"]);return b}return null},_expandRange:function(b,a){this.startLine=Math.min(this.startLine,b);this.endLine=Math.max(this.endLine,a)},toStyleRangesAndErrors:function(b,a){var c=b.style;if(!c)return null;for(var d=[],e=[],h=
-typeof a==="undefined"?0:this.model.getLineStart(a),j=0;j<c.length;j++){var k=c[j],o=!k[2]?null:k[2]==="token_tab"||k[2]==="token_space"?k[2]:"cm-"+k[2];o&&(k={start:h+k[0],end:h+k[1],style:{styleClass:o}},d.push(k),o==="cm-error"&&e.push(k))}return[d,e]},getLineStyle:function(b){return this.lines[b]},getLineStyles:function(){return this.lines}};p.EventTarget.addMixin(h.prototype);o.AnnotationType.registerType("orion.annotation.highlightError",{title:k.syntaxError,html:"<div class='annotationHTML error'></div>",
-rangeStyle:{styleClass:"annotationRange error"}});j.prototype={init:function(b,a,c){this.textView=b;this.annotationModel=c;this.modeApplier=new h(b.getModel(),a);var d=this;this.listener={onLineStyle:function(a){d.onLineStyle(a)},onDestroy:function(a){d.onDestroy(a)},onHighlight:function(a){d.onHighlight(a)}};b.addEventListener("LineStyle",this.listener.onLineStyle);b.addEventListener("Destroy",this.listener.onDestroy);this.modeApplier.addEventListener("Highlight",this.listener.onHighlight)},destroy:function(){this.modeApplier&&
-(this.modeApplier.removeEventListener("Highlight",this.listener.onHighlight),this.modeApplier.destroy());this.textView&&(this.textView.removeEventListener("LineStyle",this.listener.onLineStyle),this.textView.removeEventListener("Destroy",this.listener.onDestroy));this.listener=this.modeApplier=this.annotationModel=this.textView=null},setMode:function(b){this.modeApplier.setMode(b)},onLineStyle:function(b){var a=b.lineIndex,c=this.modeApplier,d=c.getLineStyle(a);if(!d||!d.eolState){var e=this.textView.getModel().getLineCount();
-c.highlight(a,Math.min(a+20,e-1),!0);d=c.getLineStyle(a)}e=this.textView.getModel();if(d){var h=c.toStyleRangesAndErrors(d,a);if(h&&(b.ranges=h[0],b=this.annotationModel)){c=[];d=[];if(h=h[1])for(var j=0;j<h.length;j++){var k=h[j];k.style.styleClass==="cm-error"&&d.push(o.AnnotationType.createAnnotation("orion.annotation.highlightError",k.start,k.end))}for(a=b.getAnnotations(e.getLineStart(a),e.getLineEnd(a));a.hasNext();)e=a.next(),e.type==="orion.annotation.highlightError"&&c.push(e);b.replaceAnnotations(c,
-d)}}},onHighlight:function(b){this.textView.redrawLines(b.start,b.end)},onDestroy:function(){this.destroy()}};return{Mirror:b,ModeApplier:h,CodeMirrorStyler:j}});
-define("orion/editor/textMateStyler",["orion/editor/regex"],function(k){function p(b){var d;if(b instanceof Array){d=Array(b.length);for(var e=0;e<b.length;e++)d[e]=p(b[e])}else for(e in d={},b)if(Object.prototype.hasOwnProperty.call(b,e)){var j=b[e];d[e]=typeof j==="object"&&j!==null?p(j):j}return d}function o(b,d,e){this.initialize(b);this.grammar=p(d);this.externalGrammars=e?p(e):[];this._styles={};this._tree=null;this._allGrammars={};this.preprocess(this.grammar)}var e={unsupported:[{regex:/\(\?[ims\-]:/,
-func:function(){return"option on/off for subexp"}},{regex:/\(\?<([=!])/,func:function(b){return b[1]==="="?"lookbehind":"negative lookbehind"}},{regex:/\(\?>/,func:function(){return"atomic group"}}],toRegExp:function(b){function d(a,b){throw Error('Unsupported regex feature "'+a+'": "'+b[0]+'" at index: '+b.index+" in "+b.input);}var h="",j,b=e.processGlobalFlag("x",b,function(a){for(var b="",d=!1,e=a.length,f=0;f<e;){var h=a.charAt(f);if(!d&&h==="#")for(;f<e&&h!=="\r"&&h!=="\n";)h=a.charAt(++f);
-else if(!d&&/\s/.test(h))for(;f<e&&/\s/.test(h);)h=a.charAt(++f);else h==="\\"?(b+=h,/\s/.test(a.charAt(f+1))||(b+=a.charAt(f+1),f+=1)):(h==="["?d=!0:h==="]"&&(d=!1),b+=h),f+=1}return b}),b=e.processGlobalFlag("i",b,function(a){h+="i";return a});for(j=0;j<this.unsupported.length;j++){var f;(f=this.unsupported[j].regex.exec(b))&&d(this.unsupported[j].func(f),f)}return RegExp(b,h)},processGlobalFlag:function(b,d,e){function j(a,b){for(var d=0,e=a.length,f=-1,h=b;h<e&&f===-1;h++)switch(a.charAt(h)){case "\\":h++;
-break;case "(":d++;break;case ")":d--,d===0&&(f=h)}return f}var f="(?"+b+")",b="(?"+b+":";if(d.substring(0,f.length)===f)return e(d.substring(f.length));else if(d.substring(0,b.length)===b){f=j(d,0);if(f<d.length-1)throw Error("Only a "+b+") group that encloses the entire regex is supported in: "+d);return e(d.substring(b.length,f))}return d},hasBackReference:function(b){return/\\\d+/.test(b.source)},getSubstitutedRegex:function(b,d,e){for(var e=typeof e==="undefined"?!0:!1,b=b.source.split(/(\\\d+)/g),
-j=[],f=0;f<b.length;f++){var a=b[f],c=/\\(\d+)/.exec(a);c?(a=d[c[1]]||"",j.push(e?k.escape(a):a)):j.push(a)}return RegExp(j.join(""))},groupify:function(b,d){for(var e=b.source,j=e.length,f=[],a=0,c=[],i=1,k=1,n=[],l={},o={},q=0;q<j;q++){var p=f[f.length-1],u=e.charAt(q);switch(u){case "(":if(p===4)f.pop(),n.push(")"),c[c.length-1].end=q;var g=q+2<j?e.charAt(q+1)+""+e.charAt(q+2):null;if(g==="?:"||g==="?="||g==="?!"){var t;g==="?:"?t=1:(t=3,a++);f.push(t);c.push({start:q,end:-1,type:t});n.push(u);
-n.push(g);q+=g.length}else f.push(2),c.push({start:q,end:-1,type:2,oldNum:i,num:k}),n.push(u),a===0&&(o[k]=null),l[i]=k,i++,k++;break;case ")":p=f.pop();p===3&&a--;c[c.length-1].end=q;n.push(u);break;case "*":case "+":case "?":case "}":var x=u,s=e.charAt(q-1),g=q-1;if(u==="}"){for(t=q-1;e.charAt(t)!=="{"&&t>=0;t--);s=e.charAt(t-1);g=t-1;x=e.substring(t,q+1)}t=c[c.length-1];if(s===")"&&(t.type===2||t.type===4)){n.splice(t.start,0,"(");n.push(x);n.push(")");u={start:t.start,end:n.length-1,type:4,num:t.num};
-for(s=0;s<c.length;s++)if(p=c[s],(p.type===2||p.type===4)&&p.start>=t.start&&p.end<=g)if(p.start+=1,p.end+=1,p.num+=1,p.type===2)l[p.oldNum]=p.num;c.push(u);k++;break}default:u!=="|"&&p!==2&&p!==4&&a===0&&(f.push(4),c.push({start:q,end:-1,type:4,num:k}),n.push("("),o[k]=null,k++),n.push(u),u==="\\"&&(u=e.charAt(q+1),n.push(u),q+=1)}}for(;f.length;)f.pop(),n.push(")");var e=RegExp(n.join("")),j={},d=d||l,B;for(B in d)d.hasOwnProperty(B)&&(j[B]="\\"+d[B]);e=this.getSubstitutedRegex(e,j,!1);return[e,
-l,o]},complexCaptures:function(b){if(!b)return!1;for(var d in b)if(b.hasOwnProperty(d)&&d!=="0")return!0;return!1}};o.prototype={initialize:function(b){this.textView=b;this.textView.stylerOptions=this;var d=this;this._listener={onModelChanged:function(b){d.onModelChanged(b)},onDestroy:function(b){d.onDestroy(b)},onLineStyle:function(b){d.onLineStyle(b)},onStorage:function(b){d.onStorage(b)}};b.addEventListener("ModelChanged",this._listener.onModelChanged);b.addEventListener("Destroy",this._listener.onDestroy);
-b.addEventListener("LineStyle",this._listener.onLineStyle);b.redrawLines()},onDestroy:function(){this.destroy()},destroy:function(){if(this.textView)this.textView.removeEventListener("ModelChanged",this._listener.onModelChanged),this.textView.removeEventListener("Destroy",this._listener.onDestroy),this.textView.removeEventListener("LineStyle",this._listener.onLineStyle),this.textView=null;this._listener=this._tree=this._styles=this.grammar=null},preprocess:function(b){for(b=[b];b.length!==0;){var d=
-b.pop();if(!d._resolvedRule||!d._typedRule)if(d._resolvedRule=this._resolve(d),d._typedRule=this._createTypedRule(d),this.addStyles(d.name),this.addStyles(d.contentName),this.addStylesForCaptures(d.captures),this.addStylesForCaptures(d.beginCaptures),this.addStylesForCaptures(d.endCaptures),d._resolvedRule!==d&&b.push(d._resolvedRule),d.patterns)for(var e=0;e<d.patterns.length;e++)b.push(d.patterns[e])}},addStyles:function(b){if(b&&!this._styles[b]){this._styles[b]=[];for(var d=b.split("."),e=0;e<
-d.length;e++)this._styles[b].push(d.slice(0,e+1).join("-"))}},addStylesForCaptures:function(b){for(var d in b)b.hasOwnProperty(d)&&this.addStyles(b[d].name)},ContainerRule:function(){function b(b){this.rule=b;this.subrules=b.patterns}b.prototype.valueOf=function(){return"aa"};return b}(),BeginEndRule:function(){function b(b){this.rule=b;this.beginRegex=e.toRegExp(b.begin);this.endRegex=e.toRegExp(b.end);this.subrules=b.patterns||[];this.endRegexHasBackRef=e.hasBackReference(this.endRegex);var h=e.complexCaptures(b.captures),
-b=e.complexCaptures(b.beginCaptures)||e.complexCaptures(b.endCaptures);if(this.isComplex=h||b)h=e.groupify(this.beginRegex),this.beginRegex=h[0],this.beginOld2New=h[1],this.beginConsuming=h[2],h=e.groupify(this.endRegex,this.beginOld2New),this.endRegex=h[0],this.endOld2New=h[1],this.endConsuming=h[2]}b.prototype.valueOf=function(){return this.beginRegex};return b}(),MatchRule:function(){function b(b){this.rule=b;this.matchRegex=e.toRegExp(b.match);if(this.isComplex=e.complexCaptures(b.captures))b=
-e.groupify(this.matchRegex),this.matchRegex=b[0],this.matchOld2New=b[1],this.matchConsuming=b[2]}b.prototype.valueOf=function(){return this.matchRegex};return b}(),_createTypedRule:function(b){return b.match?new this.MatchRule(b):b.begin?new this.BeginEndRule(b):new this.ContainerRule(b)},_resolve:function(b){var d=b;if(b.include){if(b.begin||b.end||b.match)throw Error('Unexpected regex pattern in "include" rule '+b.include);b=b.include;if(b.charAt(0)==="#"){if(d=this.grammar.repository&&this.grammar.repository[b.substring(1)],
-!d)throw Error("Couldn't find included rule "+b+" in grammar repository");}else if(b==="$self")d=this.grammar;else if(b==="$base")throw Error('Include "$base" is not supported');else if(d=this._allGrammars[b],!d)for(var e=0;e<this.externalGrammars.length;e++){var j=this.externalGrammars[e];if(j.scopeName===b){this.preprocess(j);d=this._allGrammars[b]=j;break}}}return d},ContainerNode:function(){function b(b,e){this.parent=b;this.rule=e;this.children=[];this.end=this.start=null}b.prototype.addChild=
-function(b){this.children.push(b)};b.prototype.valueOf=function(){var b=this.rule;return"ContainerNode { "+(b.include||"")+" "+(b.name||"")+(b.comment||"")+"}"};return b}(),BeginEndNode:function(){function b(b,h,j){this.parent=b;this.rule=h;this.children=[];this.setStart(j);this.endMatch=this.end=null;this.endRegexSubstituted=h.endRegexHasBackRef?e.getSubstitutedRegex(h.endRegex,j):null}b.prototype.addChild=function(b){this.children.push(b)};b.prototype.getIndexInParent=function(){return this.parent?
-this.parent.children.indexOf(this):-1};b.prototype.setStart=function(b){this.start=b.index;this.beginMatch=b};b.prototype.setEnd=function(b){b&&typeof b==="object"?(this.endMatch=b,this.end=b.index+b[0].length):(this.endMatch=null,this.end=b)};b.prototype.shiftStart=function(b){this.start+=b;this.beginMatch.index+=b};b.prototype.shiftEnd=function(b){this.end+=b;this.endMatch&&(this.endMatch.index+=b)};b.prototype.valueOf=function(){return"{"+this.rule.beginRegex+" range="+this.start+".."+this.end+
-"}"};return b}(),push:function(b,d){if(d)for(var e=d.length;e>0;)b.push(d[--e])},exec:function(b,d,e){(d=b.exec(d))&&(d.index+=e);b.lastIndex=0;return d},afterMatch:function(b){return b.index+b[0].length},getEndMatch:function(b,d,e){if(b instanceof this.BeginEndNode){var j=b.rule,b=b.endRegexSubstituted||j.endRegex;return!b?null:this.exec(b,d,e)}return null},initialParse:function(){this.textView.getModel().getCharCount();this._tree=new this.ContainerNode(null,this.grammar._typedRule);this.parse(this._tree,
-!1,0)},onModelChanged:function(b){var d=b.addedCharCount,e=b.removedCharCount,b=b.start;if(this._tree){var j=this.textView.getModel(),f=j.getCharCount(),j=j.getLineEnd(j.getLineAtOffset(b)-1),a=this.getFirstDamaged(j,j),j=j===-1?0:j,d=a?this.parse(a,!0,j,b,d,e):f;this.textView.redrawRange(j,d)}else this.initialParse()},getFirstDamaged:function(b,d){if(b<0)return this._tree;for(var e=[this._tree],j=null;e.length;){var f=e.pop();if(!f.parent||this.isDamaged(f,b,d)){f instanceof this.BeginEndNode&&(j=
-f);for(var a=0;a<f.children.length;a++)e.push(f.children[a])}}return j||this._tree},isDamaged:function(b,d,e){return b.start<=e&&b.end>d},parse:function(b,d,e,j,f,a){var c=this.textView.getModel(),i=c.getLineStart(c.getLineCount()-1),k=c.getCharCount(),n=this.getInitialExpected(b,e),l=-1;if(d)b.repaired=!0,b.endNeedsUpdate=!0,l=(l=b.children[b.children.length-1])?c.getLineEnd(c.getLineAtOffset(l.end+(f-a))):-1,j=c.getLineEnd(c.getLineAtOffset(j+a)),l=Math.max(l,j);for(var l=l===-1?k:l,j=n,o=b,p=!1,
-v=e,u=-1;o&&(!d||v<l);){var g=this.getNextMatch(c,o,v);g||(v=v>=i?k:c.getLineStart(c.getLineAtOffset(v)+1));var t=g&&g.match,x=g&&g.rule,s=g&&g.isEnd;if(g&&g.isSub){if(v=this.afterMatch(t),x instanceof this.BeginEndRule)p=!0,d&&x===j.rule&&o===j.parent?(o=j,o.setStart(t),o.repaired=!0,o.endNeedsUpdate=!0,j=this.getNextExpected(j,"begin")):(d&&(this.prune(o,j),d=!1),t=new this.BeginEndNode(o,x,t),o.addChild(t),o=t)}else if(s||v===k){if(o instanceof this.BeginEndNode)t?(p=!0,u=Math.max(u,o.end),o.setEnd(t),
-v=this.afterMatch(t),d&&o===j&&o.parent===j.parent?(o.repaired=!0,delete o.endNeedsUpdate,j=this.getNextExpected(j,"end")):d&&(this.prune(o,j),d=!1)):(o.setEnd(k),delete o.endNeedsUpdate);o=o.parent}d&&v>=l&&!p&&(this.prune(b,n),d=!1)}this.removeUnrepairedChildren(b,d,e);this.cleanup(d,b,e,l,k,f,a);return d?Math.max(u,v):v},removeUnrepairedChildren:function(b,d,e){if(d){for(var d=b.children,j=-1,f=0;f<d.length;f++){var a=d[f];if(!a.repaired&&this.isDamaged(a,e,Number.MAX_VALUE)){j=f;break}}if(j!==
--1)b.children.length=j}},cleanup:function(b,d,e,j,f,a,c){if(b){b=a-c;f=this.getIntersecting(j-b+1,f);d=this.getIntersecting(e,j);for(e=0;e<f.length;e++)j=f[e],!j.repaired&&j instanceof this.BeginEndNode&&(j.shiftEnd(b),j.shiftStart(b));for(e=0;e<d.length;e++)j=d[e],j.repaired&&j.endNeedsUpdate&&j.shiftEnd(b),delete j.endNeedsUpdate,delete j.repaired}else{d=this.getIntersecting(e,j);for(e=0;e<d.length;e++)delete d[e].repaired}},getNextMatch:function(b,d,e,j){var f=b.getLineAtOffset(e),f=b.getLineEnd(f),
-a=b.getText(e,f),c=[],i=[],b=[],f=[];for(this.push(c,d.rule.subrules);c.length;){var k=c.length?c.pop():null,k=k&&k._resolvedRule._typedRule;if(k instanceof this.ContainerRule&&i.indexOf(k)===-1)i.push(k),this.push(c,k.subrules);else if(!k||!j||k.matchRegex){var n=k&&this.exec(k.matchRegex||k.beginRegex,a,e);n&&(b.push(n),f.push(k))}}c=Number.MAX_VALUE;i=-1;for(k=0;k<b.length;k++)if(n=b[k],n.index<c)c=n.index,i=k;if(!j&&(e=this.getEndMatch(d,a,e)))if(j=d.rule.applyEndPatternLast,i===-1||e.index<c||
-!j&&e.index===c)return{isEnd:!0,rule:d.rule,match:e};return i===-1?null:{isSub:!0,rule:f[i],match:b[i]}},getInitialExpected:function(b,d){var e,j;if(b===this._tree)for(e=0;e<b.children.length;e++){if(j=b.children[e],j.start>=d)return j}else if(b instanceof this.BeginEndNode&&b.endMatch){var f=b.endMatch.index;for(e=0;e<b.children.length;e++)if(j=b.children[e],j.start>=d)break;if(j&&j.start<f)return j}return b},getNextExpected:function(b,d){if(d==="begin"){var e=b.children[0];return e?e:b}else if(d===
-"end"&&(e=b.parent)){var j=e.children[e.children.indexOf(b)+1];return j?j:e}return null},prune:function(b,d){if(d.parent===b)b.children.length=d.getIndexInParent();else if(b instanceof this.BeginEndNode)b.endMatch=null,b.end=null;if(b.parent)b.parent.children.length=b.getIndexInParent()+1},onLineStyle:function(b){this._tree||this.initialParse();var d=b.lineStart,e=this.textView.getModel(),j=e.getLineEnd(b.lineIndex),f=e.getLineEnd(e.getLineAtOffset(d)-1),f=this.getFirstDamaged(f,f),d=this.getLineScope(e,
-f,d,j);b.ranges=this.toStyleRanges(d);b.ranges.sort(function(a,b){return a.start-b.start})},getLineScope:function(b,d,e,j){for(var f=e,a=this.getInitialExpected(d,e),c=[],i=[];d&&f<j;){var k=this.getNextMatch(b,d,f);if(!k)break;var n=k&&k.match,l=k&&k.rule,o=k&&k.isSub,k=k&&k.isEnd;n.index!==f&&i.push({start:f,end:n.index,node:d});if(o)f=this.afterMatch(n),l instanceof this.BeginEndRule?(this.addBeginScope(c,n,l),d=a,a=this.getNextExpected(a,"begin")):this.addMatchScope(c,n,l);else if(k)f=this.afterMatch(n),
-this.addEndScope(c,n,l),a=this.getNextExpected(a,"end"),d=d.parent}f<j&&i.push({start:f,end:j,node:d});b=this.getInheritedLineScope(i,e,j);return c.concat(b)},getInheritedLineScope:function(b){for(var d=[],e=0;e<b.length;e++)for(var j=b[e],f=j.node;f;){var a=f.rule.rule,c=a.name;if(a=a.contentName||c){this.addScopeRange(d,j.start,j.end,a);break}f=f.parent}return d},addBeginScope:function(b,d,e){var j=e.rule;this.addCapturesScope(b,d,j.beginCaptures||j.captures,e.isComplex,e.beginOld2New,e.beginConsuming)},
-addEndScope:function(b,d,e){var j=e.rule;this.addCapturesScope(b,d,j.endCaptures||j.captures,e.isComplex,e.endOld2New,e.endConsuming)},addMatchScope:function(b,d,e){var j=e.rule,f=j.name;(j=j.captures)?this.addCapturesScope(b,d,j,e.isComplex,e.matchOld2New,e.matchConsuming):this.addScope(b,d,f)},addScope:function(b,d,e){e&&b.push({start:d.index,end:this.afterMatch(d),scope:e})},addScopeRange:function(b,d,e,j){j&&b.push({start:d,end:e,scope:j})},addCapturesScope:function(b,d,e,j,f,a){if(e)if(j){for(var j=
-{1:0},c=0,i=1;d[i]!==void 0;i++)a[i]!==void 0&&(c+=d[i].length),d[i+1]!==void 0&&(j[i+1]=c);a=d.index;for(c=1;e[c];c++){var i=e[c].name,k=f[c],n=a+j[k];typeof d[k]!=="undefined"&&this.addScopeRange(b,n,n+d[k].length,i)}}else this.addScope(b,d,e[0]&&e[0].name)},getIntersecting:function(b,d){for(var e=[],j=this._tree?[this._tree]:[];j.length;){var f=j.pop(),a=!1;f instanceof this.ContainerNode?a=!0:this.isDamaged(f,b,d)&&(a=!0,e.push(f));if(a)for(var a=f.children.length,c=0;c<a;c++)j.push(f.children[c])}return e.reverse()},
-toStyleRanges:function(b){for(var d=[],e=0;e<b.length;e++){var j=b[e],f=this._styles[j.scope];if(!f)throw Error("styles not found for "+j.scope);f=f.join(" ");d.push({start:j.start,end:j.end,style:{styleClass:f}})}return d}};return{RegexUtil:e,TextMateStyler:o}});
-define("orion/editor/htmlGrammar",[],function(){return{HtmlGrammar:function(){return{scopeName:"source.html",uuid:"3B5C76FB-EBB5-D930-F40C-047D082CE99B",patterns:[{begin:"<!(doctype|DOCTYPE)",end:">",contentName:"entity.name.tag.doctype.html",beginCaptures:{0:{name:"entity.name.tag.doctype.html"}},endCaptures:{0:{name:"entity.name.tag.doctype.html"}}},{begin:"<\!--",end:"--\>",beginCaptures:{0:{name:"punctuation.definition.comment.html"}},endCaptures:{0:{name:"punctuation.definition.comment.html"}},
-patterns:[{match:"--",name:"invalid.illegal.badcomment.html"}],contentName:"comment.block.html"},{match:"<[A-Za-z0-9_\\-:]+(?= ?)",name:"entity.name.tag.html"},{include:"#attrName"},{include:"#qString"},{include:"#qqString"},{include:"#entity"},{match:"</[A-Za-z0-9_\\-:]+>",name:"entity.name.tag.html"},{match:">",name:"entity.name.tag.html"}],repository:{attrName:{match:"[A-Za-z\\-:]+(?=\\s*=\\s*['\"])",name:"entity.other.attribute.name.html"},qqString:{match:'(")[^"]+(")',name:"string.quoted.double.html"},
-qString:{match:"(')[^']+(')",name:"string.quoted.single.html"},entity:{match:"&[A-Za-z0-9]+;",name:"constant.character.entity.html"}}}}}});
-define("examples/editor/textStyler",["orion/editor/annotations","orion/editor/keywords"],function(k,p){function o(a,b){this.keywords=a;this.whitespacesVisible=b;this.setText("")}function e(){o.call(this,null,!0)}function b(a){o.call(this,null,a)}function d(){o.call(this,null,!1)}function h(c,g,h){this.commentStart="/*";this.commentEnd="*/";var i=[];switch(g){case "java":i=f;break;case "js":i=j;break;case "css":i=a}this.whitespacesVisible=!1;this.detectHyperlinks=!0;this.highlightCaretLine=!1;this.detectTasks=
-this.foldingEnabled=!0;this._scanner=new o(i,this.whitespacesVisible);this._firstScanner=new d;this._commentScanner=new b(this.whitespacesVisible);this._whitespaceScanner=new e;if(g==="css")this._scanner.isCSS=!0,this._firstScanner.isCSS=!0;this.view=c;this.annotationModel=h;this._bracketAnnotations=void 0;var k=this;this._listener={onChanged:function(a){k._onModelChanged(a)},onDestroy:function(a){k._onDestroy(a)},onLineStyle:function(a){k._onLineStyle(a)},onMouseDown:function(a){k._onMouseDown(a)},
-onSelection:function(a){k._onSelection(a)}};g=c.getModel();g.getBaseModel&&(g=g.getBaseModel());g.addEventListener("Changed",this._listener.onChanged);c.addEventListener("MouseDown",this._listener.onMouseDown);c.addEventListener("Selection",this._listener.onSelection);c.addEventListener("Destroy",this._listener.onDestroy);c.addEventListener("LineStyle",this._listener.onLineStyle);this._computeComments();this._computeFolding();c.redrawLines()}var j=p.JSKeywords,f=p.JAVAKeywords,a=p.CSSKeywords,c={styleClass:"token_singleline_comment"},
-i={styleClass:"token_multiline_comment"},m={styleClass:"token_doc_comment"},n={styleClass:"token_doc_html_markup"},l={styleClass:"token_task_tag"},r={styleClass:"token_doc_tag"},q={styleClass:"token_string"},v={styleClass:"token_number"},u={styleClass:"token_keyword"},g={styleClass:"token_space"},t={styleClass:"token_tab"},x={styleClass:"line_caret"};o.prototype={getOffset:function(){return this.offset},getStartOffset:function(){return this.startOffset},getData:function(){return this.text.substring(this.startOffset,
-this.offset)},getDataLength:function(){return this.offset-this.startOffset},_default:function(a){switch(a){case 32:case 9:if(this.whitespacesVisible)return a===32?11:10;do a=this._read();while(a===32||a===9);this._unread(a);return 9;case 123:case 125:case 40:case 41:case 91:case 93:case 60:case 62:return a;default:var b=this.isCSS,c=this.offset-1;if(!b&&48<=a&&a<=57){var d=b=!1,e=!1,f=a;do if(a=this._read(),a===46&&!b)b=!0;else if(a===101&&!d)b=d=!0,a=this._read(),a!==45&&this._unread(a);else if(a===
-120&&f===48&&this.offset-c===2)b=d=e=!0;else if(!(48<=a&&a<=57||e&&(65<=a&&a<=70||97<=a&&a<=102)))break;while(1);this._unread(a);return 3}if(97<=a&&a<=122||65<=a&&a<=90||a===95||45===a&&b){do a=this._read();while(97<=a&&a<=122||65<=a&&a<=90||a===95||48<=a&&a<=57||45===a&&b);this._unread(a);a=this.keywords;if(a.length>0){c=this.text.substring(c,this.offset);for(b=0;b<a.length;b++)if(this.keywords[b]===c)return 2}}return 1}},_read:function(){return this.offset<this.text.length?this.text.charCodeAt(this.offset++):
--1},_unread:function(a){a!==-1&&this.offset--},nextToken:function(){for(this.startOffset=this.offset;;){var a=this._read(),b;switch(a){case -1:return null;case 47:a=this._read();if(!this.isCSS&&a===47)for(;;)if(a=this._read(),a===-1||a===10||a===13)return this._unread(a),6;if(a===42){a=this._read();b=7;for(a===42&&(b=8);;){for(;a===42;)if(a=this._read(),a===47)return b;if(a===-1)return this._unread(a),b;a=this._read()}}this._unread(a);return 1;case 39:for(b=4;;)switch(a=this._read(),a){case 39:return b;
-case 13:case 10:case -1:return this._unread(a),b;case 92:switch(a=this._read(),a){case 10:b=5;break;case 13:b=5,a=this._read(),a!==10&&this._unread(a)}}break;case 34:for(b=4;;)switch(a=this._read(),a){case 34:return b;case 13:case 10:case -1:return this._unread(a),b;case 92:switch(a=this._read(),a){case 10:b=5;break;case 13:b=5,a=this._read(),a!==10&&this._unread(a)}}break;default:return this._default(a)}}},setText:function(a){this.text=a;this.startOffset=this.offset=0}};e.prototype=new o(null);e.prototype.nextToken=
-function(){for(this.startOffset=this.offset;;){var a=this._read();switch(a){case -1:return null;case 32:return 11;case 9:return 10;default:do a=this._read();while(!(a===32||a===9||a===-1));this._unread(a);return 1}}};b.prototype=new o(null);b.prototype.setType=function(a){this._type=a};b.prototype.nextToken=function(){for(this.startOffset=this.offset;;){var a=this._read();switch(a){case -1:return null;case 32:case 9:if(this.whitespacesVisible)return a===32?11:10;do a=this._read();while(a===32||a===
-9);this._unread(a);return 9;case 60:if(this._type===8){do a=this._read();while(!(a===62||a===-1));if(a===62)return 12}return 1;case 64:if(this._type===8){do a=this._read();while(97<=a&&a<=122||65<=a&&a<=90||a===95||48<=a&&a<=57);this._unread(a);return 13}return 1;case 84:if((a=this._read())===79)if((a=this._read())===68)if((a=this._read())===79)if(a=this._read(),!(97<=a&&a<=122||65<=a&&a<=90||a===95||48<=a&&a<=57))return this._unread(a),14;this._unread(a);default:do a=this._read();while(!(a===32||
-a===9||a===-1||a===60||a===64||a===84));this._unread(a);return 1}}};d.prototype=new o(null);d.prototype._default=function(a){for(;;)switch(a=this._read(),a){case 47:case 34:case 39:case -1:return this._unread(a),1}};h.prototype={destroy:function(){var a=this.view;if(a){var b=a.getModel();b.getBaseModel&&(b=b.getBaseModel());b.removeEventListener("Changed",this._listener.onChanged);a.removeEventListener("MouseDown",this._listener.onMouseDown);a.removeEventListener("Selection",this._listener.onSelection);
-a.removeEventListener("Destroy",this._listener.onDestroy);a.removeEventListener("LineStyle",this._listener.onLineStyle);this.view=null}},setHighlightCaretLine:function(a){this.highlightCaretLine=a},setWhitespacesVisible:function(a){this.whitespacesVisible=a;this._scanner.whitespacesVisible=a;this._commentScanner.whitespacesVisible=a},setDetectHyperlinks:function(a){this.detectHyperlinks=a},setFoldingEnabled:function(a){this.foldingEnabled=a},setDetectTasks:function(a){this.detectTasks=a},_binarySearch:function(a,
-b,c,d,e){var f;d===void 0&&(d=-1);if(e===void 0)e=a.length;for(;e-d>1;)if(f=Math.floor((e+d)/2),b<=a[f].start)e=f;else if(c&&b<a[f].end){e=f;break}else d=f;return e},_computeComments:function(){var a=this.view.getModel();a.getBaseModel&&(a=a.getBaseModel());this.comments=this._findComments(a.getText())},_computeFolding:function(){if(this.foldingEnabled){var a=this.view.getModel();if(a.getBaseModel){var b=this.annotationModel;if(b){b.removeAnnotations(k.AnnotationType.ANNOTATION_FOLDING);for(var c=
-[],d=a.getBaseModel(),e=this.comments,f=0;f<e.length;f++){var g=e[f];(g=this._createFoldingAnnotation(a,d,g.start,g.end))&&c.push(g)}b.replaceAnnotations(null,c)}}}},_createFoldingAnnotation:function(a,b,c,d){var e=b.getLineAtOffset(c),b=b.getLineAtOffset(d);return e===b?null:new (k.AnnotationType.getType(k.AnnotationType.ANNOTATION_FOLDING))(c,d,a)},_computeTasks:function(a,b,c){if(this.detectTasks){var d=this.annotationModel;if(d){var e=this.view.getModel(),f=e;e.getBaseModel&&(f=e.getBaseModel());
-for(var g=d.getAnnotations(b,c),e=[],h=k.AnnotationType.ANNOTATION_TASK;g.hasNext();){var i=g.next();i.type===h&&e.push(i)}g=[];i=this._commentScanner;i.setText(f.getText(b,c));for(var j;j=i.nextToken();){var l=i.getStartOffset()+b;j===14&&(j=f.getLineEnd(f.getLineAtOffset(l)),a!==6&&(j=Math.min(j,c-this.commentEnd.length)),g.push(k.AnnotationType.createAnnotation(h,l,j,f.getText(l,j))))}d.replaceAnnotations(e,g)}}},_getLineStyle:function(a){if(this.highlightCaretLine){var b=this.view,c=b.getModel(),
-b=b.getSelection();if(b.start===b.end&&c.getLineAtOffset(b.start)===a)return x}return null},_getStyles:function(a,b,c){a.getBaseModel&&(c=a.mapOffset(c));for(var d=c+b.length,e=[],f=c,g=this.comments,h=this._binarySearch(g,c,!0);h<g.length;h++){if(g[h].start>=d)break;var j=g[h].start,k=g[h].end;f<j&&this._parse(b.substring(f-c,j-c),f,e);var l=g[h].type,n;switch(l){case 8:n=m;break;case 7:n=i;break;case 5:n=q}f=Math.max(f,j);j=Math.min(d,k);(l===8||l===7)&&(this.whitespacesVisible||this.detectHyperlinks)?
-this._parseComment(b.substring(f-c,j-c),f,e,n,l):l===5&&this.whitespacesVisible?this._parseString(b.substring(f-c,j-c),f,e,q):e.push({start:f,end:j,style:n});f=k}f<d&&this._parse(b.substring(f-c,d-c),f,e);if(a.getBaseModel)for(b=0;b<e.length;b++)c=e[b].end-e[b].start,e[b].start=a.mapOffset(e[b].start,!0),e[b].end=e[b].start+c;return e},_parse:function(a,b,d){var e=this._scanner;for(e.setText(a);a=e.nextToken();){var f=e.getStartOffset()+b,h=null;switch(a){case 2:h=u;break;case 3:h=v;break;case 5:case 4:if(this.whitespacesVisible){this._parseString(e.getData(),
-f,d,q);continue}else h=q;break;case 8:this._parseComment(e.getData(),f,d,m,a);continue;case 6:this._parseComment(e.getData(),f,d,c,a);continue;case 7:this._parseComment(e.getData(),f,d,i,a);continue;case 10:this.whitespacesVisible&&(h=t);break;case 11:this.whitespacesVisible&&(h=g)}d.push({start:f,end:e.getOffset()+b,style:h})}},_parseComment:function(a,b,c,d,e){var f=this._commentScanner;f.setText(a);for(f.setType(e);a=f.nextToken();){var e=f.getStartOffset()+b,h=d;switch(a){case 10:this.whitespacesVisible&&
-(h=t);break;case 11:this.whitespacesVisible&&(h=g);break;case 12:h=n;break;case 13:h=r;break;case 14:h=l;break;default:this.detectHyperlinks&&(h=this._detectHyperlinks(f.getData(),e,c,h))}h&&c.push({start:e,end:f.getOffset()+b,style:h})}},_parseString:function(a,b,c,d){var e=this._whitespaceScanner;for(e.setText(a);a=e.nextToken();){var f=e.getStartOffset()+b,h=d;switch(a){case 10:this.whitespacesVisible&&(h=t);break;case 11:this.whitespacesVisible&&(h=g)}h&&c.push({start:f,end:e.getOffset()+b,style:h})}},
-_detectHyperlinks:function(a,b,c,d){var e=null,f;if((f=a.indexOf("://"))>0){for(var e=a,g=f;g>0;){f=e.charCodeAt(g-1);if(!(97<=f&&f<=122||65<=f&&f<=90||45===f||48<=f&&f<=57))break;g--}if(g>0&&(f="\"\"''(){}[]<>".indexOf(e.substring(g-1,g)),f!==-1&&(f&1)===0&&(f=e.lastIndexOf("\"\"''(){}[]<>".substring(f+1,f+2)))!==-1)){var h=f;f=this._clone(d);f.tagName="a";f.attributes={href:e.substring(g,h)};c.push({start:b,end:b+g,style:d});c.push({start:b+g,end:b+h,style:f});c.push({start:b+h,end:b+a.length,style:d});
-return null}}else a.toLowerCase().indexOf("bug#")===0&&(e="https://bugs.eclipse.org/bugs/show_bug.cgi?id="+parseInt(a.substring(4),10));return e?(f=this._clone(d),f.tagName="a",f.attributes={href:e},f):d},_clone:function(a){if(!a)return a;var b={},c;for(c in a)a.hasOwnProperty(c)&&(b[c]=a[c]);return b},_findComments:function(a,b){var b=b||0,c=this._firstScanner,d;c.setText(a);for(var e=[];d=c.nextToken();)(d===7||d===8||d===5)&&e.push({start:c.getStartOffset()+b,end:c.getOffset()+b,type:d}),(d===
-6||d===7||d===8)&&this._computeTasks(d,c.getStartOffset()+b,c.getOffset()+b);return e},_findMatchingBracket:function(a,b){var c="{}()[]<>",d=a.getText(b,b+1),e=c.indexOf(d,0);if(e===-1)return-1;var f;f=e&1?c.substring(e-1,e):c.substring(e+1,e+2);for(var g=a.getLineAtOffset(b),c=a.getLine(g),h=a.getLineStart(g),i=a.getLineEnd(g),c=this._findBrackets(d,f,c,h,h,i),i=0;i<c.length;i++)if(h=c[i]>=0?1:-1,c[i]*h-1===b){var j=1;if(e&1){for(i--;i>=0;i--)if(h=c[i]>=0?1:-1,j+=h,j===0)return c[i]*h-1;for(g-=1;g>=
-0;){c=a.getLine(g);h=a.getLineStart(g);i=a.getLineEnd(g);c=this._findBrackets(d,f,c,h,h,i);for(e=c.length-1;e>=0;e--)if(h=c[e]>=0?1:-1,j+=h,j===0)return c[e]*h-1;g--}}else{for(i++;i<c.length;i++)if(h=c[i]>=0?1:-1,j+=h,j===0)return c[i]*h-1;g+=1;for(e=a.getLineCount();g<e;){c=a.getLine(g);h=a.getLineStart(g);i=a.getLineEnd(g);c=this._findBrackets(d,f,c,h,h,i);for(i=0;i<c.length;i++)if(h=c[i]>=0?1:-1,j+=h,j===0)return c[i]*h-1;g++}}break}return-1},_findBrackets:function(a,b,c,d,e,f){for(var g=[],a=
-a.charCodeAt(0),b=b.charCodeAt(0),h=e,i=this._scanner,j,k=this.comments,l=this._binarySearch(k,e,!0);l<k.length;l++){if(k[l].start>=f)break;j=k[l].start;var m=k[l].end;if(h<j)for(i.setText(c.substring(h-e,j-e));j=i.nextToken();)j===a?g.push(i.getStartOffset()+h-e+d+1):j===b&&g.push(-(i.getStartOffset()+h-e+d+1));h=m}if(h<f)for(i.setText(c.substring(h-e,f-e));j=i.nextToken();)j===a?g.push(i.getStartOffset()+h-e+d+1):j===b&&g.push(-(i.getStartOffset()+h-e+d+1));return g},_onDestroy:function(){this.destroy()},
-_onLineStyle:function(a){if(a.textView===this.view)a.style=this._getLineStyle(a.lineIndex);a.ranges=this._getStyles(a.textView.getModel(),a.lineText,a.lineStart)},_onSelection:function(a){var b=a.oldValue,c=a.newValue,d=this.view,a=d.getModel(),e;if(this.highlightCaretLine){var f=a.getLineAtOffset(b.start);e=a.getLineAtOffset(c.start);var g=c.start===c.end,b=b.start===b.end;f===e&&b&&g||(b&&d.redrawLines(f,f+1),(f!==e||!b)&&g&&d.redrawLines(e,e+1))}if(this.annotationModel){var b=this._bracketAnnotations,
-h,i;if(c.start===c.end&&(i=d.getCaretOffset())>0)i-=1,a.getBaseModel&&(i=a.mapOffset(i),a=a.getBaseModel()),a=this._findMatchingBracket(a,i),a!==-1&&(h=[k.AnnotationType.createAnnotation(k.AnnotationType.ANNOTATION_MATCHING_BRACKET,a,a+1),k.AnnotationType.createAnnotation(k.AnnotationType.ANNOTATION_CURRENT_BRACKET,i,i+1)]);this._bracketAnnotations=h;this.annotationModel.replaceAnnotations(b,h)}},_onMouseDown:function(a){if(a.clickCount===2){var b=this.view,c=b.getModel(),d=b.getOffsetAtLocation(a.x,
-a.y);if(d>0){var e=d-1,f=c;c.getBaseModel&&(e=c.mapOffset(e),f=c.getBaseModel());e=this._findMatchingBracket(f,e);e!==-1&&(a.preventDefault(),a=e,c.getBaseModel&&(a=c.mapOffset(a,!0)),d>a&&(d--,a++),b.setSelection(a,d))}}},_onModelChanged:function(a){var b=a.start,c=a.removedCharCount,d=a.addedCharCount-c,e=this.view,a=e.getModel(),f=a.getBaseModel?a.getBaseModel():a,c=b+c,g=f.getCharCount(),h=this.comments.length,i=f.getLineStart(f.getLineAtOffset(b)),j=this._binarySearch(this.comments,i,!0),l=this._binarySearch(this.comments,
-c,!1,j-1,h);j<h&&this.comments[j].start<=i&&i<this.comments[j].end?(i=this.comments[j].start,i>b&&(i+=d)):i=j===h&&h>0&&g-d===this.comments[h-1].end?this.comments[h-1].start:i;var m;l<h?(m=this.comments[l].end,m>b&&(m+=d),l+=1):(l=h,m=g);for(var n,g=this._findComments(f.getText(i,m),i),h=j;h<this.comments.length;h++)n=this.comments[h],n.start>b&&(n.start+=d),n.start>b&&(n.end+=d);var o=l-j!==g.length;if(!o)for(h=0;h<g.length;h++){n=this.comments[j+h];var p=g[h];if(n.start!==p.start||n.end!==p.end||
-n.type!==p.type){o=!0;break}}h=[j,l-j].concat(g);Array.prototype.splice.apply(this.comments,h);o&&(h=i,n=m,a!==f&&(h=a.mapOffset(h,!0),n=a.mapOffset(n,!0)),e.redrawRange(h,n));if(this.foldingEnabled&&f!==a&&this.annotationModel){e=this.annotationModel;j=e.getAnnotations(i,m);i=[];for(m=[];j.hasNext();)if(n=j.next(),n.type===k.AnnotationType.ANNOTATION_FOLDING){m.push(n);for(h=0;h<g.length;h++)if(n.start===g[h].start&&n.end===g[h].end)break;h===g.length?(i.push(n),n.expand()):(h=n.start,l=n.end,h>
-b&&(h-=d),l>b&&(l-=d),h<=b&&b<l&&h<=c&&c<l&&(h=f.getLineAtOffset(n.start),l=f.getLineAtOffset(n.end),h!==l?n.expanded||(n.expand(),e.modifyAnnotation(n)):e.removeAnnotation(n)))}b=[];for(h=0;h<g.length;h++){n=g[h];for(d=0;d<m.length;d++)if(m[d].start===n.start&&m[d].end===n.end)break;d===m.length&&(n=this._createFoldingAnnotation(a,f,n.start,n.end))&&b.push(n)}e.replaceAnnotations(i,b)}}};return{TextStyler:h}});
-define("orion/editor/edit","orion/editor/textView,orion/editor/textModel,orion/editor/textTheme,orion/editor/projectionTextModel,orion/editor/eventTarget,orion/keyBinding,orion/editor/rulers,orion/editor/annotations,orion/editor/tooltip,orion/editor/undoStack,orion/editor/textDND,orion/editor/editor,orion/editor/editorFeatures,orion/editor/contentAssist,orion/editor/cssContentAssist,orion/editor/htmlContentAssist,orion/editor/jsTemplateContentAssist,orion/editor/AsyncStyler,orion/editor/mirror,orion/editor/textMateStyler,orion/editor/htmlGrammar,examples/editor/textStyler".split(","),function(k,
-p,o,e,b,d,h,j,f,a,c,i,m,n,l,r,q,v,u,g,t,x){function s(a){var b=a.firstChild;if(b&&b.tagName==="TEXTAREA")return b.value;var c=a.ownerDocument,d=c.defaultView||c.parentWindow,e;if(!(e=!d.getSelection)){if(!(b=a.childNodes.length===1&&b.nodeType===Node.TEXT_NODE)){for(var f,b=a;b&&b!==c&&f!=="none";)f=d.getComputedStyle?d.getComputedStyle(b,null).getPropertyValue("display"):b.currentStyle.display,b=b.parentNode;b=f==="none"}e=b}if(e)return a.innerText||a.textContent;c=c.createRange();c.selectNode(a);
-a=d.getSelection();d=[];for(f=0;f<a.rangeCount;f++)d.push(a.getRangeAt(f));a.removeAllRanges();a.addRange(c);c=a.toString();a.removeAllRanges();for(f=0;f<d.length;f++)a.addRange(d[f]);return c}function B(a){if(a.substring(0,12)==="data-editor-")return a=a.substring(12),a=a.replace(/-([a-z])/ig,function(a,b){return b.toUpperCase()})}function G(a,b){for(var c in b)b.hasOwnProperty(c)&&(a[c]=b[c])}function K(a,b){var c={};G(c,b);for(var d,e=0,f=a.attributes,g=f.length;e<g;e++){d=f.item(e);var h=B(d.nodeName);
-if(h){d=d.nodeValue;if(d==="true"||d==="false")d=d==="true";c[h]=d}}return c}function L(a){var b=a.ownerDocument,b=b.defaultView||b.parentWindow,c;if(b.getComputedStyle)c=b.getComputedStyle(a,null).getPropertyValue("height");else if(a.currentStyle)c=a.currentStyle.height;return parseInt(c,10)||0}function H(a){var b=a.parent;b||(b="editor");typeof b==="string"&&(b=(a.document||document).getElementById(b));if(!b&&a.className){var c=(a.document||document).getElementsByClassName(a.className);if(c){a.className=
-void 0;for(var d=[],f=0;f<c.length;f++)a.parent=c[f],d.push(H(a));return d}}if(!b)throw"no parent";a=K(b,a);if(typeof a.theme==="string"){var c=o.TextTheme.getTheme(a.theme),f=a.theme.lastIndexOf("/"),h=a.theme;f!==-1&&(h=h.substring(f+1));h.substring(h.length-4)===".css"&&(h=h.substring(0,h.length-4));c.setThemeClass(h,{href:a.theme});a.theme=c}var j;a.readonly||(d={createContentAssistMode:function(a){j=new n.ContentAssist(a.getTextView());a=new n.ContentAssistWidget(j);a=new n.ContentAssistMode(j,
-a);j.setMode(a);return a}});d=new i.Editor({textViewFactory:function(){return new k.TextView({parent:b,model:new e.ProjectionTextModel(new p.TextModel("")),tabSize:a.tabSize?a.tabSize:4,readonly:a.readonly,fullSelection:a.fullSelection,tabMode:a.tabMode,expandTab:a.expandTab,themeClass:a.themeClass,theme:a.theme,wrapMode:a.wrapMode})},undoStackFactory:new m.UndoFactory,annotationFactory:new m.AnnotationFactory,lineNumberRulerFactory:new m.LineNumberRulerFactory,foldingRulerFactory:new m.FoldingRulerFactory,
-textDNDFactory:new m.TextDNDFactory,contentAssistFactory:d,keyBindingFactory:new m.KeyBindingsFactory,statusReporter:a.statusReporter,domNode:b});c=a.contents;c===void 0&&(c=s(b));c||(c="");d.installTextView();d.setLineNumberRulerVisible(a.showLinesRuler===void 0||a.showLinesRuler);d.setAnnotationRulerVisible(a.showAnnotationRuler===void 0||a.showFoldingRuler);d.setOverviewRulerVisible(a.showOverviewRuler===void 0||a.showOverviewRuler);d.setFoldingRulerVisible(a.showFoldingRuler===void 0||a.showFoldingRuler);
-d.setInput(a.title,null,c);({styler:null,highlight:function(b,c){if(this.styler)this.styler.destroy(),this.styler=null;if(b){var d=c.getTextView(),e=c.getAnnotationModel();switch(b){case "js":case "java":case "css":this.styler=new x.TextStyler(d,b,e);c.setFoldingRulerVisible(a.showFoldingRuler===void 0||a.showFoldingRuler);break;case "html":this.styler=new g.TextMateStyler(d,new t.HtmlGrammar)}}}}).highlight(a.lang,d);if(j){var r=new l.CssContentAssistProvider,u=new q.JSTemplateContentAssistProvider;
-j.addEventListener("Activating",function(){/css$/.test(a.lang)?j.setProviders([r]):/js$/.test(a.lang)&&j.setProviders([u])})}if(L(b)<=50)c=d.getTextView().computeSize().height,b.style.height=c+"px";return d}var I=this.orion?this.orion.editor:void 0;if(I)for(var E=0;E<arguments.length;E++)G(I,arguments[E]);return H});var orion=this.orion||(this.orion={}),editor=orion.editor||(orion.editor={});editor.edit=require("orion/editor/edit");
\ No newline at end of file
diff --git a/bundles/org.eclipse.e4.tools.orion.css.editor/web/orion.editor.txt b/bundles/org.eclipse.e4.tools.orion.css.editor/web/orion.editor.txt
index aeda29e..bfe51a0 100644
--- a/bundles/org.eclipse.e4.tools.orion.css.editor/web/orion.editor.txt
+++ b/bundles/org.eclipse.e4.tools.orion.css.editor/web/orion.editor.txt
@@ -1,3 +1,3 @@
-The current built-editor.css and built-editor.min.js 
-are from orion 3.0 build I20130613-2230.
+The current built-editor.css and built-editor.js 
+are from orion release 3.0